/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" |