## 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/case_of.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 <3rdparty/dsfmt/dSFMT.h>
    8
    9
    TEST_SETUP()
    10
    {
    11
      ccv_nnc_init();
    12
    }
    13
    14
    static int piecewise_case_of(ccv_nnc_tensor_t* const* const inputs, const int input_size, const void* const data)
    15
    4
    {
    16
    4
      assert(input_size == 1);
    17
    4
      if (inputs[0]->data.f32[0] < 0)
    18
    1
        return 0;
    19
    3
      else if (inputs[0]->data.f32[0] < 1)
    20
    1
        return -1; // Pass through because the computation is essentially x * 1
    21
    2
      else if (inputs[0]->data.f32[0] < 2)
    22
    1
        return 1;
    23
    1
      else
    24
    1
        return 2;
    25
    4
    }
    26
    27
    TEST_CASE("symbolic graph for piece-wise function y = f(x), compute y'")
    28
    1
    {
    29
    1
      ccv_nnc_symbolic_graph_t* const symbolic_graph = ccv_nnc_symbolic_graph_new();
    30
    1
      ccv_nnc_tensor_symbol_t x = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "x");
    31
    1
      ccv_nnc_tensor_symbol_t y = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "y");
    32
    1
      ccv_nnc_graph_exec_symbol_t case_of = ccv_nnc_symbolic_graph_case_of_new(symbolic_graph, CCV_NNC_GRAPH_FORWARD, TENSOR_SYMBOL_LIST(x), TENSOR_SYMBOL_MAP(KV(x, y)), "piece-wise linear");
    33
    1
      ccv_nnc_symbolic_graph_set_case_of_expr(symbolic_graph, case_of, piecewise_case_of, 0);
    34
    1
      ccv_nnc_symbolic_graph_t* const symbolic_graph_0 = ccv_nnc_symbolic_graph_new();
    35
    1
      ccv_nnc_tensor_symbol_t y0 = ccv_nnc_tensor_symbol_new(symbolic_graph_0, CPU_TENSOR_NHWC(32F, 1), "y0");
    36
    1
      ccv_nnc_symbolic_graph_set_case_of(symbolic_graph, case_of, symbolic_graph_0, 0, TENSOR_SYMBOL_MAP(KV(y0, y)));
    37
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph_0, CMD_SET_FORWARD(0), 0, 0, TENSOR_SYMBOL_LIST(y0), "set");
    38
    1
      ccv_nnc_graph_exec_symbol_autogen(symbolic_graph_0, 0, 0, CCV_NNC_AUTOGEN_ALL_EXECS | CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS);
    39
    1
      ccv_nnc_symbolic_graph_t* const symbolic_graph_1 = ccv_nnc_symbolic_graph_new();
    40
    1
      ccv_nnc_tensor_symbol_t y1 = ccv_nnc_tensor_symbol_new(symbolic_graph_1, CPU_TENSOR_NHWC(32F, 1), "y1");
    41
    1
      ccv_nnc_symbolic_graph_set_case_of(symbolic_graph, case_of, symbolic_graph_1, 1, TENSOR_SYMBOL_MAP(KV(y1, y)));
    42
    1
      ccv_nnc_tensor_symbol_t s1 = ccv_nnc_tensor_symbol_new(symbolic_graph_1, CPU_TENSOR_NHWC(32F, 1), "s");
    43
    1
      ccv_nnc_tensor_symbol_t z1 = ccv_nnc_tensor_symbol_new(symbolic_graph_1, CPU_TENSOR_NHWC(32F, 1), "z1");
    44
    1
      ccv_nnc_tensor_symbol_t p1 = ccv_nnc_tensor_symbol_new(symbolic_graph_1, CPU_TENSOR_NHWC(32F, 1), "p");
    45
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph_1, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(x, s1), TENSOR_SYMBOL_LIST(z1), "prod");
    46
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph_1, CMD_EWSUM_FORWARD(), TENSOR_SYMBOL_LIST(z1, p1), TENSOR_SYMBOL_LIST(y1), "sum");
    47
    1
      ccv_nnc_graph_exec_symbol_autogen(symbolic_graph_1, 0, 0, CCV_NNC_AUTOGEN_ALL_EXECS | CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS);
    48
    1
      ccv_nnc_symbolic_graph_t* const symbolic_graph_2 = ccv_nnc_symbolic_graph_new();
    49
    1
      ccv_nnc_tensor_symbol_t y2 = ccv_nnc_tensor_symbol_new(symbolic_graph_2, CPU_TENSOR_NHWC(32F, 1), "y2");
    50
    1
      ccv_nnc_symbolic_graph_set_case_of(symbolic_graph, case_of, symbolic_graph_2, 2, TENSOR_SYMBOL_MAP(KV(y2, y)));
    51
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph_2, CMD_SET_FORWARD(1.5), 0, 0, TENSOR_SYMBOL_LIST(y2), "set");
    52
    1
      ccv_nnc_graph_exec_symbol_autogen(symbolic_graph_2, 0, 0, CCV_NNC_AUTOGEN_ALL_EXECS | CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS);
    53
    1
      ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(y), TENSOR_SYMBOL_LIST(x), GRAPH_EXEC_SYMBOL_LIST(case_of), GRAPH_EXEC_SYMBOL_LIST(case_of));
    54
    1
      SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
    55
    1
      ccv_nnc_graph_t* graph = 0;
    56
    1
      ccv_nnc_tensor_arena_t* tensor_arena = 0;
    57
    1
      ccv_nnc_graph_exec_arena_t* graph_exec_arena = 0;
    58
    1
      const ccv_nnc_tensor_symbol_t dx = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, x);
    59
    1
      ccv_nnc_graph_exec_symbol_t case_of_backward = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, dx);
    60
    1
      ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params, 0, 0, 0, 0, &case_of, 1, &case_of_backward, 1, &graph, &tensor_arena, &graph_exec_arena);
    61
    1
      GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
    62
    1
      ccv_nnc_graph_exec_t source = ccv_nnc_graph_exec_source(graph_exec_arena);
    63
    1
      ccv_nnc_graph_exec_t destination = ccv_nnc_graph_exec_destination(graph_exec_arena);
    64
    1
      const ccv_nnc_tensor_symbol_t dy = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, y);
    65
    1
      ccv_nnc_tensor_t* x_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, x);
    66
    1
      ccv_nnc_tensor_t* dy_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, dy);
    67
    1
      ccv_nnc_tensor_t* s1_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, s1);
    68
    1
      ccv_nnc_tensor_t* p1_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, p1);
    69
    1
      s1_tensor->data.f32[0] = 0.5;
    70
    1
      p1_tensor->data.f32[0] = 0.5;
    71
    1
      x_tensor->data.f32[0] = -1;
    72
    1
      dy_tensor->data.f32[0] = 1;
    73
    1
      ccv_nnc_tensor_tape_t* tape = ccv_nnc_tensor_tape_new();
    74
    1
      ccv_nnc_graph_run(graph, 0, &source, 1, &destination, 1, tape, 0);
    75
    1
      ccv_nnc_tensor_t* dx_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, dx);
    76
    1
      REQUIRE_EQ_WITH_TOLERANCE(dx_tensor->data.f32[0], 0, 1e-5, "in negative region should equal to 0");
    77
    1
      s1_tensor->data.f32[0] = 0.5;
    78
    1
      p1_tensor->data.f32[0] = 0.5;
    79
    1
      x_tensor->data.f32[0] = 0.76;
    80
    1
      dy_tensor->data.f32[0] = 1;
    81
    1
      ccv_nnc_graph_autotune(graph, 0, 0, TRAVERSE_FULL);
    82
    1
      ccv_nnc_graph_run(graph, 0, &source, 1, &destination, 1, tape, 0);
    83
    1
      dx_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, dx);
    84
    1
      REQUIRE_EQ_WITH_TOLERANCE(dx_tensor->data.f32[0], 1, 1e-5, "y = x in (0, 1), y' = 1 (gradient passthrough)");
    85
    1
      s1_tensor->data.f32[0] = 0.5;
    86
    1
      p1_tensor->data.f32[0] = 0.5;
    87
    1
      x_tensor->data.f32[0] = 1.226;
    88
    1
      dy_tensor->data.f32[0] = 1;
    89
    1
      ccv_nnc_graph_run(graph, 0, &source, 1, &destination, 1, tape, 0);
    90
    1
      dx_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, dx);
    91
    1
      REQUIRE_EQ_WITH_TOLERANCE(dx_tensor->data.f32[0], 0.5, 1e-5, "y = (x - 1) * 0.5 + 1 in (1, 2), y' = 0.5");
    92
    1
      s1_tensor->data.f32[0] = 0.5;
    93
    1
      p1_tensor->data.f32[0] = 0.5;
    94
    1
      x_tensor->data.f32[0] = 2.1;
    95
    1
      dy_tensor->data.f32[0] = 1;
    96
    1
      ccv_nnc_graph_run(graph, 0, &source, 1, &destination, 1, tape, 0);
    97
    1
      dx_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, dx);
    98
    1
      REQUIRE_EQ_WITH_TOLERANCE(dx_tensor->data.f32[0], 0, 1e-5, "y = 1.5 if x > 2, y' = 0");
    99
    1
      ccv_nnc_tensor_tape_free(tape);
    100
    1
      ccv_nnc_symbolic_graph_free(symbolic_graph);
    101
    1
      ccv_nnc_graph_exec_arena_free(graph_exec_arena);
    102
    1
      ccv_nnc_tensor_arena_free(tensor_arena);
    103
    1
      ccv_nnc_graph_free(graph);
    104
    1
    }
    105
    106
    #include "case_main.h"