## Coverage Report

#### Created: 2026-05-04 15:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/liu/actions-runner/_work/ccv/ccv/test/unit/nnc/while.backward.tests.c

    Line
    Count
    Source
    1
    #include "case.h"
    2
    #include "ccv_case.h"
    3
    #include "ccv_nnc_case.h"
    4
    #include <ccv.h>
    5
    #include <nnc/ccv_nnc.h>
    6
    #include <nnc/ccv_nnc_easy.h>
    7
    #include <nnc/_ccv_nnc_graph.h>
    8
    #include <3rdparty/dsfmt/dSFMT.h>
    9
    10
    TEST_SETUP()
    11
    {
    12
      ccv_nnc_init();
    13
    }
    14
    15
    static int while_4(ccv_nnc_tensor_t* const* const inputs, const int input_size, const void* const data)
    16
    15
    {
    17
    15
      return inputs[0]->data.i64[0] < 4;
    18
    15
    }
    19
    20
    TEST_CASE("graph with a while loop to compute back propagation 0.34 * 1.11 ^ 5")
    21
    1
    {
    22
    1
      ccv_nnc_graph_t* graph = ccv_nnc_graph_new();
    23
    1
      ccv_nnc_tensor_t* y = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0);
    24
    1
      ccv_nnc_tensor_t* x0 = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0);
    25
    1
      ccv_nnc_tensor_t* x = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0);
    26
    1
      x->type |= CCV_TAPE_ALLOC;
    27
    1
      ccv_nnc_tensor_t* z = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0);
    28
    1
      z->type |= CCV_TAPE_ALLOC;
    29
    1
      ccv_nnc_tensor_t* g = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0);
    30
    1
      ccv_nnc_graph_t* while_graph = ccv_nnc_graph_new();
    31
    1
      ccv_nnc_graph_exec_t loop = ccv_nnc_graph_while(graph, CCV_NNC_GRAPH_FORWARD, while_graph);
    32
    1
      ccv_nnc_tensor_multiview_t xx;
    33
    1
      ccv_nnc_tensor_multiview((ccv_nnc_tensor_t*[]){
    34
    1
          x0, z, x
    35
    1
      }, CCV_NNC_MULTIVIEW_K1N, 2, while_graph, &xx);
    36
    1
      xx.type |= CCV_TAPE_ALLOC;
    37
    1
      ccv_nnc_tensor_multiview_t zz;
    38
    1
      ccv_nnc_tensor_multiview((ccv_nnc_tensor_t*[]){
    39
    1
          z, x
    40
    1
      }, CCV_NNC_MULTIVIEW_K0N, 2, while_graph, &zz);
    41
    1
      zz.type |= CCV_TAPE_ALLOC;
    42
    1
      ccv_nnc_graph_exec_t prod0 = ccv_nnc_graph_exec_new(while_graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, TENSOR_LIST(y, (ccv_nnc_tensor_t*)&xx), TENSOR_LIST((ccv_nnc_tensor_t*)&zz));
    43
    1
      ccv_nnc_graph_exec_t noop = ccv_nnc_graph_exec_new(while_graph, CMD_NOOP(), ccv_nnc_no_hint, 0, 0, 0, 0);
    44
    1
      ccv_nnc_graph_exec_concat(while_graph, prod0, noop);
    45
    1
      ccv_nnc_graph_set_sources(while_graph, GRAPH_EXEC_LIST(prod0));
    46
    1
      ccv_nnc_graph_set_destinations(while_graph, GRAPH_EXEC_LIST(noop));
    47
    1
      ccv_nnc_tensor_t count_tensor = ccv_nnc_tensor_for_while_count(while_graph);
    48
    1
      ccv_nnc_graph_set_while_expr(while_graph, while_4, 0, TENSOR_LIST(&count_tensor), GRAPH_EXEC_LIST(noop));
    49
    1
      ccv_nnc_graph_t* while_back_graph = ccv_nnc_graph_new();
    50
    1
      while_back_graph->pair = while_graph;
    51
    1
      ccv_nnc_graph_exec_t back_loop = ccv_nnc_graph_while(graph, CCV_NNC_GRAPH_BACKWARD, while_back_graph);
    52
    1
      ccv_nnc_tensor_t* dx = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0);
    53
    1
      ccv_nnc_graph_exec_t back_prod0 = ccv_nnc_graph_exec_new(while_back_graph, CMD_EWPROD_BACKWARD(), ccv_nnc_no_hint, TENSOR_LIST(g, y, (ccv_nnc_tensor_t*)&xx, (ccv_nnc_tensor_t*)&zz), TENSOR_LIST(dx, g));
    54
    1
      ccv_nnc_graph_exec_t back_noop = ccv_nnc_graph_exec_new(while_back_graph, CMD_NOOP(), ccv_nnc_no_hint, 0, 0, 0, 0);
    55
    1
      ccv_nnc_graph_exec_concat(while_back_graph, back_noop, back_prod0);
    56
    1
      ccv_nnc_graph_set_sources(while_back_graph, GRAPH_EXEC_LIST(back_noop));
    57
    1
      ccv_nnc_graph_set_destinations(while_back_graph, GRAPH_EXEC_LIST(back_prod0));
    58
    1
      ccv_nnc_graph_set_while_expr(while_back_graph, while_4, 0, 0, 0, GRAPH_EXEC_LIST(back_noop));
    59
    1
      ccv_nnc_graph_exec_concat(graph, loop, back_loop);
    60
    1
      GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
    61
    1
      x0->data.f32[0] = 0.34;
    62
    1
      y->data.f32[0] = 1.11;
    63
    1
      g->data.f32[0] = 1;
    64
    1
      ccv_nnc_tensor_tape_t* tape = ccv_nnc_tensor_tape_new();
    65
    1
      ccv_nnc_graph_run(graph, 0, GRAPH_EXEC_LIST(loop), GRAPH_EXEC_LIST(back_loop), tape, 0);
    66
    1
      ccv_nnc_graph_free(graph);
    67
    1
      REQUIRE_EQ_WITH_TOLERANCE(g->data.f32[0], 1.11 * 1.11 * 1.11 * 1.11 * 1.11, 1e-6, "back propagation of 0.34 * 1.11 ^ 5 should be 1.11 ^ 5");
    68
    1
      ccv_nnc_tensor_tape_free(tape);
    69
    1
      ccv_nnc_tensor_multiview_free(xx);
    70
    1
      ccv_nnc_tensor_multiview_free(zz);
    71
    1
      ccv_nnc_tensor_free(x0);
    72
    1
      ccv_nnc_tensor_free(dx);
    73
    1
      ccv_nnc_tensor_free(x);
    74
    1
      ccv_nnc_tensor_free(y);
    75
    1
      ccv_nnc_tensor_free(z);
    76
    1
      ccv_nnc_tensor_free(g);
    77
    1
    }
    78
    79
    TEST_CASE("symbolic graph with a while loop z = log(x * y) (x <- z) 5 times, then u = v * z, compute y'")
    80
    1
    {
    81
    1
      ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
    82
    1
      ccv_nnc_tensor_symbol_t x = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "x");
    83
    1
      ccv_nnc_tensor_symbol_t y = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "y");
    84
    1
      ccv_nnc_symbolic_graph_t* while_graph = ccv_nnc_symbolic_graph_new();
    85
    1
      ccv_nnc_tensor_symbol_t xy = ccv_nnc_tensor_symbol_new(while_graph, CPU_TENSOR_NHWC(32F, 1), "xy");
    86
    1
      ccv_nnc_tensor_symbol_t z = ccv_nnc_tensor_symbol_new(while_graph, CPU_TENSOR_NHWC(32F, 1), "z");
    87
    1
      ccv_nnc_tensor_symbol_t u = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "u");
    88
    1
      ccv_nnc_tensor_symbol_t v = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "v");
    89
    1
      ccv_nnc_symbolic_graph_while(symbolic_graph, CCV_NNC_GRAPH_FORWARD, while_graph, "while0");
    90
    1
      ccv_nnc_graph_exec_symbol_t prod0 = ccv_nnc_graph_exec_symbol_new(while_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(x, y), TENSOR_SYMBOL_LIST(xy), "prod0");
    91
    1
      ccv_nnc_graph_exec_symbol_t log0 = ccv_nnc_graph_exec_symbol_new(while_graph, CMD_EWLOG_FORWARD(), TENSOR_SYMBOL_LIST(xy), TENSOR_SYMBOL_LIST(z), "log0");
    92
    1
      ccv_nnc_graph_exec_symbol_autogen(while_graph, GRAPH_EXEC_SYMBOL_LIST(prod0, log0), 0);
    93
    1
      ccv_nnc_graph_exec_symbol_t noop = ccv_nnc_graph_exec_symbol_new(while_graph, CMD_NOOP(), 0, 0, 0, 0, "noop");
    94
    1
      ccv_nnc_graph_exec_symbol_concat(while_graph, log0, noop);
    95
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(z, v), TENSOR_SYMBOL_LIST(u), "prod1");
    96
    1
      ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, 0, 0, CCV_NNC_AUTOGEN_ALL_EXECS | CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS);
    97
    1
      ccv_nnc_symbolic_graph_set_while_expr(while_graph, while_4, 0, TENSOR_SYMBOL_LIST(ccv_nnc_tensor_symbol_for_while_count(while_graph)), GRAPH_EXEC_SYMBOL_LIST(noop));
    98
    1
      ccv_nnc_symbolic_graph_set_carry_overs(while_graph, TENSOR_SYMBOL_MAP(KV(z, x)));
    99
    1
      ccv_nnc_symbolic_graph_set_sources(while_graph, GRAPH_EXEC_SYMBOL_LIST(prod0));
    100
    1
      ccv_nnc_symbolic_graph_set_destinations(while_graph, GRAPH_EXEC_SYMBOL_LIST(noop));
    101
    1
      ccv_nnc_graph_t* graph = 0;
    102
    1
      ccv_nnc_tensor_arena_t* tensor_arena = 0;
    103
    1
      ccv_nnc_graph_exec_arena_t* graph_exec_arena = 0;
    104
    1
      ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params, 0, 0, 0, 0, ccv_nnc_symbolic_graph_sources(symbolic_graph), ccv_nnc_symbolic_graph_source_size(symbolic_graph), ccv_nnc_symbolic_graph_destinations(symbolic_graph), ccv_nnc_symbolic_graph_destination_size(symbolic_graph), &graph, &tensor_arena, &graph_exec_arena);
    105
    1
      ccv_nnc_tensor_t* x_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, x);
    106
    1
      ccv_nnc_tensor_t* y_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, y);
    107
    1
      ccv_nnc_tensor_t* v_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, v);
    108
    1
      x_tensor->data.f32[0] = 1;
    109
    1
      y_tensor->data.f32[0] = 3.2;
    110
    1
      v_tensor->data.f32[0] = 0.22;
    111
    1
      ccv_nnc_graph_run(graph, 0, TRAVERSE_FULL, 0, 0);
    112
    1
      ccv_nnc_tensor_t* u_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, u);
    113
    1
      float z0 = 1, y0 = 3.2;
    114
    1
      int i;
    115
    6
      for (i = 0; i < 5; i++5)
    116
    5
        z0 = log(z0 * y0);
    117
    1
      z0 = 0.22 * z0;
    118
    1
      REQUIRE_EQ_WITH_TOLERANCE(u_tensor->data.f32[0], z0, 1e-6, "u should match the for loop result");
    119
    1
      ccv_nnc_graph_exec_arena_free(graph_exec_arena);
    120
    1
      ccv_nnc_tensor_arena_free(tensor_arena);
    121
    1
      ccv_nnc_graph_free(graph);
    122
    1
      ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(u), TENSOR_SYMBOL_LIST(y), ccv_nnc_symbolic_graph_sources(symbolic_graph), ccv_nnc_symbolic_graph_source_size(symbolic_graph), ccv_nnc_symbolic_graph_destinations(symbolic_graph), ccv_nnc_symbolic_graph_destination_size(symbolic_graph));
    123
    1
      SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
    124
    1
      ccv_nnc_tensor_symbol_t dy = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, y);
    125
    1
      ccv_nnc_graph_exec_symbol_t dyx = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, dy);
    126
    1
      ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params, 0, 0, 0, 0, ccv_nnc_symbolic_graph_sources(symbolic_graph), ccv_nnc_symbolic_graph_source_size(symbolic_graph), &dyx, 1, &graph, &tensor_arena, &graph_exec_arena);
    127
    1
      GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
    128
    1
      x_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, x);
    129
    1
      y_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, y);
    130
    1
      v_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, v);
    131
    1
      x_tensor->data.f32[0] = 1;
    132
    1
      y_tensor->data.f32[0] = 3.2;
    133
    1
      v_tensor->data.f32[0] = 0.22;
    134
    1
      ccv_nnc_tensor_t* du_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, ccv_nnc_tensor_symbol_for_backward(symbolic_graph, u));
    135
    1
      du_tensor->data.f32[0] = 1;
    136
    1
      ccv_nnc_tensor_tape_t* tape = ccv_nnc_tensor_tape_new();
    137
    1
      ccv_nnc_graph_run(graph, 0, TRAVERSE_FULL, tape, 0);
    138
    1
      ccv_nnc_tensor_t* dy_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, dy);
    139
      // Effectively, we are computing:
    140
      // D[Log[Log[Log[Log[Log[x * y] * y] * y] * y] * y] * v, y]
    141
      // From WolframAlpha: http://www.wolframalpha.com/input/?i=D%5BLog%5BLog%5BLog%5BLog%5BLog%5Bx+*+y%5D+*+y%5D+*+y%5D+*+y%5D+*+y%5D+*+v,+y%5D
    142
    1
      const float dya = 0.22 * (((1 / log(1 * 3.2) + 1) / (log(3.2 * log(1 * 3.2)) * log(3.2 * log(3.2 * log(1 * 3.2)))) + 1 / log(3.2 * log(3.2 * log(1 * 3.2))) + 1) / (3.2 * log(3.2 * log(3.2 * log(3.2 * log(1 * 3.2))))) + 1 / 3.2);
    143
    1
      REQUIRE_EQ_WITH_TOLERANCE(dy_tensor->data.f32[0], dya, 1e-6, "back propagation of this while loop should match WolframAlpha result");
    144
    1
      ccv_nnc_tensor_tape_free(tape);
    145
    1
      ccv_nnc_symbolic_graph_free(symbolic_graph);
    146
    1
      ccv_nnc_graph_exec_arena_free(graph_exec_arena);
    147
    1
      ccv_nnc_tensor_arena_free(tensor_arena);
    148
    1
      ccv_nnc_graph_free(graph);
    149
    1
    }
    150
    151
    #include "case_main.h"