Coverage Report

Created: 2024-08-18 16:21

/home/liu/actions-runner/_work/ccv/ccv/test/unit/nnc/autograd.vector.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
8
TEST_SETUP()
9
{
10
  ccv_nnc_init();
11
}
12
13
TEST_CASE("autograd with D[y = x + [1 1.5] => x_1 + (y_1 + y_1 ^ 2) + Exp[y_2], x] when x = [0.44 -1.18]")
14
1
{
15
1
  ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
16
1
  ccv_nnc_tensor_symbol_t one = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 2), "[1 1.5]");
17
1
  ccv_nnc_tensor_symbol_t x = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 2), "x");
18
1
  ccv_nnc_tensor_symbol_t y = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 2), "y");
19
1
  int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
20
1
  int stride[CCV_NNC_MAX_DIM_ALLOC] = {0};
21
1
  stride[0] = 1;
22
1
  ccv_nnc_tensor_symbol_t x_1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, x, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "x_1");
23
1
  ccv_nnc_tensor_symbol_t y_1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, y, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "y_1");
24
1
  ofs[0] = 1;
25
1
  ccv_nnc_tensor_symbol_t y_2 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, y, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "y_2");
26
1
  ccv_nnc_tensor_symbol_t w_1 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "w_1");
27
1
  ccv_nnc_tensor_symbol_t u_1 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "u_1");
28
1
  ccv_nnc_tensor_symbol_t u_2 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "u_2");
29
1
  ccv_nnc_tensor_symbol_t v = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "v");
30
1
  ccv_nnc_graph_exec_symbol_t plus = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWSUM_FORWARD(), TENSOR_SYMBOL_LIST(x, one), TENSOR_SYMBOL_LIST(y), "plus");
31
1
  ccv_nnc_graph_exec_symbol_t sqr = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(y_1, y_1), TENSOR_SYMBOL_LIST(w_1), "sqr");
32
1
  ccv_nnc_graph_exec_symbol_t plus_y = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWSUM_FORWARD(), TENSOR_SYMBOL_LIST(w_1, y_1), TENSOR_SYMBOL_LIST(u_1), "plus_y");
33
1
  ccv_nnc_graph_exec_symbol_t exp_ = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWEXP_FORWARD(), TENSOR_SYMBOL_LIST(y_2), TENSOR_SYMBOL_LIST(u_2), "exp");
34
1
  ccv_nnc_graph_exec_symbol_t sum = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWSUM_FORWARD(), TENSOR_SYMBOL_LIST(x_1, u_1, u_2), TENSOR_SYMBOL_LIST(v), "sum");
35
1
  ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(plus, sqr, plus_y, exp_, sum), 0);
36
1
  ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(v), TENSOR_SYMBOL_LIST(x), GRAPH_EXEC_SYMBOL_LIST(plus), GRAPH_EXEC_SYMBOL_LIST(sum));
37
1
  ccv_nnc_graph_t* graph = 0;
38
1
  ccv_nnc_tensor_arena_t* tensor_arena = 0;
39
1
  ccv_nnc_graph_exec_arena_t* graph_exec_arena = 0;
40
1
  ccv_nnc_tensor_symbol_t dx = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, x);
41
1
  ccv_nnc_graph_exec_symbol_t dxc = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, dx);
42
1
  ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params, 0, 0, TENSOR_SYMBOL_LIST(v, dx), GRAPH_EXEC_SYMBOL_LIST(plus), GRAPH_EXEC_SYMBOL_LIST(dxc, sum), &graph, &tensor_arena, &graph_exec_arena);
43
1
  SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
44
1
  GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
45
1
  ccv_nnc_tensor_t* tone = ccv_nnc_tensor_from_symbol(tensor_arena, one);
46
1
  tone->data.f32[0] = 1;
47
1
  tone->data.f32[1] = 1.5;
48
1
  ccv_nnc_tensor_t* tx = ccv_nnc_tensor_from_symbol(tensor_arena, x);
49
1
  tx->data.f32[0] = 0.44;
50
1
  tx->data.f32[1] = -1.18;
51
1
  ccv_nnc_tensor_symbol_t dv = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, v);
52
1
  ccv_nnc_tensor_t* tdv = ccv_nnc_tensor_from_symbol(tensor_arena, dv);
53
  // Seed the initialization vector.
54
1
  tdv->data.f32[0] = 1;
55
1
  ccv_nnc_graph_run(graph, 0, GRAPH_EXEC_LIST(ccv_nnc_graph_exec_source(graph_exec_arena)), GRAPH_EXEC_LIST(ccv_nnc_graph_exec_destination(graph_exec_arena)), 0, 0);
56
1
  ccv_nnc_tensor_t* tv = ccv_nnc_tensor_from_symbol(tensor_arena, v);
57
1
  ccv_nnc_tensor_t* tdx = ccv_nnc_tensor_from_symbol(tensor_arena, dx);
58
1
  REQUIRE_EQ_WITH_TOLERANCE(tv->data.f32[0], 0.44 + (0.44 + 1 + (0.44 + 1) * (0.44 + 1)) + expf(-1.18 + 1.5), 1e-6, "computed result of y = x + [1 1.5] => x_1 + (y_1 + y_1 ^ 2) + Exp[y_2] should be the same");
59
1
  REQUIRE_EQ_WITH_TOLERANCE(tdx->data.f32[0], 2 + 2 * (0.44 + 1), 1e-6, "computed result of D[y = x + [1 1.5] => x_1 + (y_1 + y_1 ^ 2) + Exp[y_2], x] for x_1 should be the same");
60
1
  REQUIRE_EQ_WITH_TOLERANCE(tdx->data.f32[1], expf(-1.18 + 1.5), 1e-6, "computed result of D[y = x + [1 1.5] => x_1 + (y_1 + y_1 ^ 2) + Exp[y_2], x] for x_2 should be the same");
61
1
  ccv_nnc_symbolic_graph_free(symbolic_graph);
62
1
  ccv_nnc_graph_free(graph);
63
1
  ccv_nnc_tensor_arena_free(tensor_arena);
64
1
  ccv_nnc_graph_exec_arena_free(graph_exec_arena);
65
1
}
66
67
TEST_CASE("autograd with D[y_1 = Log[x_1], y_2 = x_2 ^ 2 => y_1 ^ 2 + y_1 * y_2, x] when x = [0.38 -2.8]")
68
1
{
69
1
  ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
70
1
  ccv_nnc_tensor_symbol_t x = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 2), "x");
71
1
  ccv_nnc_tensor_symbol_t y = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 2), "y");
72
1
  int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
73
1
  int stride[CCV_NNC_MAX_DIM_ALLOC] = {0};
74
1
  stride[0] = 1;
75
1
  ccv_nnc_tensor_symbol_t x_1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, x, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "x_1");
76
1
  ccv_nnc_tensor_symbol_t y_1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, y, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "y_1");
77
1
  ofs[0] = 1;
78
1
  ccv_nnc_tensor_symbol_t x_2 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, x, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "x_2");
79
1
  ccv_nnc_tensor_symbol_t y_2 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, y, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "y_2");
80
1
  ccv_nnc_tensor_symbol_t w = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "w");
81
1
  ccv_nnc_tensor_symbol_t u = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "u");
82
1
  ccv_nnc_tensor_symbol_t v = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "v");
83
1
  ccv_nnc_graph_exec_symbol_t plus = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWLOG_FORWARD(), TENSOR_SYMBOL_LIST(x_1), TENSOR_SYMBOL_LIST(y_1), "log");
84
1
  ccv_nnc_graph_exec_symbol_t x_1_sqr = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(x_2, x_2), TENSOR_SYMBOL_LIST(y_2), "x_1_sqr");
85
1
  ccv_nnc_graph_exec_symbol_t y_1_sqr = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(y_1, y_1), TENSOR_SYMBOL_LIST(w), "y_1_sqr");
86
1
  ccv_nnc_graph_exec_symbol_t prod = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(y_1, y_2), TENSOR_SYMBOL_LIST(u), "prod");
87
1
  ccv_nnc_graph_exec_symbol_t sum = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWSUM_FORWARD(), TENSOR_SYMBOL_LIST(w, u), TENSOR_SYMBOL_LIST(v), "sum");
88
1
  ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(plus, x_1_sqr, y_1_sqr, prod, sum), 0);
89
1
  ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(v), TENSOR_SYMBOL_LIST(x), GRAPH_EXEC_SYMBOL_LIST(plus, x_1_sqr), GRAPH_EXEC_SYMBOL_LIST(sum));
90
1
  ccv_nnc_graph_t* graph = 0;
91
1
  ccv_nnc_tensor_arena_t* tensor_arena = 0;
92
1
  ccv_nnc_graph_exec_arena_t* graph_exec_arena = 0;
93
1
  ccv_nnc_tensor_symbol_t dx = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, x);
94
1
  ccv_nnc_graph_exec_symbol_t dxc = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, dx);
95
1
  ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params, 0, 0, TENSOR_SYMBOL_LIST(v, dx), GRAPH_EXEC_SYMBOL_LIST(plus, x_1_sqr), GRAPH_EXEC_SYMBOL_LIST(dxc, sum), &graph, &tensor_arena, &graph_exec_arena);
96
1
  SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
97
1
  GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
98
1
  ccv_nnc_tensor_t* tx = ccv_nnc_tensor_from_symbol(tensor_arena, x);
99
1
  tx->data.f32[0] = 0.38;
100
1
  tx->data.f32[1] = -2.8;
101
1
  ccv_nnc_tensor_symbol_t dv = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, v);
102
1
  ccv_nnc_tensor_t* tdv = ccv_nnc_tensor_from_symbol(tensor_arena, dv);
103
  // Seed the initialization vector.
104
1
  tdv->data.f32[0] = 1;
105
1
  ccv_nnc_graph_run(graph, 0, GRAPH_EXEC_LIST(ccv_nnc_graph_exec_source(graph_exec_arena)), GRAPH_EXEC_LIST(ccv_nnc_graph_exec_destination(graph_exec_arena)), 0, 0);
106
1
  ccv_nnc_tensor_t* tv = ccv_nnc_tensor_from_symbol(tensor_arena, v);
107
1
  ccv_nnc_tensor_t* tdx = ccv_nnc_tensor_from_symbol(tensor_arena, dx);
108
1
  REQUIRE_EQ_WITH_TOLERANCE(tv->data.f32[0], logf(0.38) * logf(0.38) + logf(0.38) * (-2.8 * -2.8), 1e-5, "computed result of y_1 = Log[x_1], y_2 = x_2 ^ 2 => y_1 ^ 2 + y_1 * y_2 should be the same");
109
1
  REQUIRE_EQ_WITH_TOLERANCE(tdx->data.f32[0], 2 * logf(0.38) / 0.38 + (-2.8 * -2.8) / 0.38, 1e-5, "computed result of D[y_1 = Log[x_1], y_2 = x_2 ^ 2 => y_1 ^ 2 + y_1 * y_2, x] for x_1 should be the same");
110
1
  REQUIRE_EQ_WITH_TOLERANCE(tdx->data.f32[1], 2 * -2.8 * logf(0.38), 1e-5, "computed result of D[y_1 = Log[x_1], y_2 = x_2 ^ 2 => y_1 ^ 2 + y_1 * y_2, x] for x_2 should be the same");
111
1
  ccv_nnc_symbolic_graph_free(symbolic_graph);
112
1
  ccv_nnc_graph_free(graph);
113
1
  ccv_nnc_tensor_arena_free(tensor_arena);
114
1
  ccv_nnc_graph_exec_arena_free(graph_exec_arena);
115
1
}
116
117
TEST_CASE("autograd with D[y_1 = Log[x_1] => y_1 ^ 2 + y_1, x] when x = [0.21 -13.22]")
118
1
{
119
1
  ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
120
1
  ccv_nnc_tensor_symbol_t x = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 2), "x");
121
1
  ccv_nnc_tensor_symbol_t y = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 2), "y");
122
1
  int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
123
1
  int stride[CCV_NNC_MAX_DIM_ALLOC] = {0};
124
1
  stride[0] = 1;
125
1
  ccv_nnc_tensor_symbol_t x_1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, x, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "x_1");
126
1
  ccv_nnc_tensor_symbol_t y_1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, y, ofs, stride, CPU_TENSOR_NHWC(32F, 1), "y_1");
127
1
  ccv_nnc_tensor_symbol_t w = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "w");
128
1
  ccv_nnc_tensor_symbol_t v = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "v");
129
1
  ccv_nnc_graph_exec_symbol_t plus = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWLOG_FORWARD(), TENSOR_SYMBOL_LIST(x_1), TENSOR_SYMBOL_LIST(y_1), "log");
130
1
  ccv_nnc_graph_exec_symbol_t sqr = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(y_1, y_1), TENSOR_SYMBOL_LIST(w), "sqr");
131
1
  ccv_nnc_graph_exec_symbol_t sum = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWSUM_FORWARD(), TENSOR_SYMBOL_LIST(w, y_1), TENSOR_SYMBOL_LIST(v), "sum");
132
1
  ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(plus, sqr, sum), 0);
133
1
  ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(v), TENSOR_SYMBOL_LIST(x), GRAPH_EXEC_SYMBOL_LIST(plus), GRAPH_EXEC_SYMBOL_LIST(sum));
134
1
  ccv_nnc_graph_t* graph = 0;
135
1
  ccv_nnc_tensor_arena_t* tensor_arena = 0;
136
1
  ccv_nnc_graph_exec_arena_t* graph_exec_arena = 0;
137
1
  ccv_nnc_tensor_symbol_t dx = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, x);
138
1
  ccv_nnc_graph_exec_symbol_t dxc = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, dx);
139
1
  ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params, 0, 0, TENSOR_SYMBOL_LIST(v, dx), GRAPH_EXEC_SYMBOL_LIST(plus), GRAPH_EXEC_SYMBOL_LIST(dxc, sum), &graph, &tensor_arena, &graph_exec_arena);
140
1
  SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
141
1
  GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
142
1
  ccv_nnc_tensor_t* tx = ccv_nnc_tensor_from_symbol(tensor_arena, x);
143
1
  tx->data.f32[0] = 0.21;
144
1
  tx->data.f32[1] = -13.22;
145
1
  ccv_nnc_tensor_symbol_t dv = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, v);
146
1
  ccv_nnc_tensor_t* tdv = ccv_nnc_tensor_from_symbol(tensor_arena, dv);
147
  // Seed the initialization vector.
148
1
  tdv->data.f32[0] = 1;
149
1
  ccv_nnc_graph_run(graph, 0, GRAPH_EXEC_LIST(ccv_nnc_graph_exec_source(graph_exec_arena)), GRAPH_EXEC_LIST(ccv_nnc_graph_exec_destination(graph_exec_arena)), 0, 0);
150
1
  ccv_nnc_tensor_t* tv = ccv_nnc_tensor_from_symbol(tensor_arena, v);
151
1
  ccv_nnc_tensor_t* tdx = ccv_nnc_tensor_from_symbol(tensor_arena, dx);
152
1
  REQUIRE_EQ_WITH_TOLERANCE(tv->data.f32[0], logf(0.21) * logf(0.21) + logf(0.21), 1e-6, "computed result of y_1 = Log[x_1] => y_1 ^ 2 + y_1 should be the same");
153
1
  REQUIRE_EQ_WITH_TOLERANCE(tdx->data.f32[0], 2 * logf(0.21) / 0.21 + 1 / 0.21, 1e-6, "computed result of D[y_1 = Log[x_1] => y_1 ^ 2 + y_1, x] for x_1 should be the same");
154
  // Note that the value in tdx->data.f32[1] is undefined.
155
1
  ccv_nnc_symbolic_graph_free(symbolic_graph);
156
1
  ccv_nnc_graph_free(graph);
157
1
  ccv_nnc_tensor_arena_free(tensor_arena);
158
1
  ccv_nnc_graph_exec_arena_free(graph_exec_arena);
159
1
}
160
161
TEST_CASE("autograd with sliced tensors for convolution doesn't require zeros (similar to Inception module)")
162
1
{
163
1
  ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
164
1
  ccv_nnc_tensor_symbol_t image = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 3), "image");
165
1
  ccv_nnc_tensor_symbol_t w = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 128, 3, 3, 3), "w");
166
1
  ccv_nnc_tensor_symbol_t bias = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 128), "bias");
167
1
  ccv_nnc_tensor_symbol_t b = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 128), "b");
168
1
  ccv_nnc_tensor_symbol_t c = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 128), "c");
169
1
  int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
170
1
  int stride[CCV_NNC_MAX_DIM_ALLOC] = {0};
171
1
  stride[0] = 100 * 128;
172
1
  stride[1] = 128;
173
1
  stride[2] = 1;
174
1
  ccv_nnc_tensor_symbol_t b0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b0");
175
1
  ccv_nnc_tensor_symbol_t c0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c0");
176
1
  ofs[2] = 64;
177
1
  ccv_nnc_tensor_symbol_t b1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b1");
178
1
  ccv_nnc_tensor_symbol_t c1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c1");
179
1
  ofs[2] = 0;
180
1
  ofs[0] = 50;
181
1
  ccv_nnc_tensor_symbol_t b2 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b2");
182
1
  ccv_nnc_tensor_symbol_t c2 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c2");
183
1
  ofs[2] = 64;
184
1
  ofs[0] = 50;
185
1
  ccv_nnc_tensor_symbol_t b3 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b3");
186
1
  ccv_nnc_tensor_symbol_t c3 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c3");
187
1
  ccv_nnc_graph_exec_symbol_t conv = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_CONVOLUTION_FORWARD(1, 128, 3, 3, 3), TENSOR_SYMBOL_LIST(image, w, bias), TENSOR_SYMBOL_LIST(b), "conv");
188
1
  ccv_nnc_graph_exec_symbol_t relu0 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b0), TENSOR_SYMBOL_LIST(c0), "relu0");
189
1
  ccv_nnc_graph_exec_symbol_t relu1 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b1), TENSOR_SYMBOL_LIST(c1), "relu1");
190
1
  ccv_nnc_graph_exec_symbol_t relu2 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b2), TENSOR_SYMBOL_LIST(c2), "relu2");
191
1
  ccv_nnc_graph_exec_symbol_t relu3 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b3), TENSOR_SYMBOL_LIST(c3), "relu3");
192
1
  ccv_nnc_tensor_symbol_t d = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1, 1, 128), "d");
193
1
  ccv_nnc_graph_exec_symbol_t pool = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_AVERAGE_POOL_FORWARD(100, 100), TENSOR_SYMBOL_LIST(c), TENSOR_SYMBOL_LIST(d), "pool");
194
1
  ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(conv, relu0, relu1, relu2, relu3, pool), 0);
195
1
  ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(d), TENSOR_SYMBOL_LIST(w, bias, b, c), GRAPH_EXEC_SYMBOL_LIST(conv), GRAPH_EXEC_SYMBOL_LIST(pool));
196
1
  SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
197
1
  ccv_nnc_tensor_symbol_t db = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, b);
198
1
  ccv_nnc_tensor_symbol_t dc = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, c);
199
1
  REQUIRE(!(ccv_nnc_tensor_symbol_flags(symbolic_graph, db) & CCV_NNC_TENSOR_SYMBOL_INIT_ZEROS), "The gradient for b doesn't need to be zero init");
200
1
  REQUIRE(!(ccv_nnc_tensor_symbol_flags(symbolic_graph, dc) & CCV_NNC_TENSOR_SYMBOL_INIT_ZEROS), "The gradient for c doesn't need to be zero init");
201
1
  ccv_nnc_symbolic_graph_free(symbolic_graph);
202
1
}
203
204
TEST_CASE("autograd with sliced tensors for convolution require zeros")
205
1
{
206
1
  ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
207
1
  ccv_nnc_tensor_symbol_t image = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 3, 100, 100, 3), "image");
208
1
  ccv_nnc_tensor_symbol_t w = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 128, 3, 3, 3), "w");
209
1
  ccv_nnc_tensor_symbol_t bias = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 128), "bias");
210
1
  ccv_nnc_tensor_symbol_t b = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 128), "b");
211
1
  ccv_nnc_tensor_symbol_t c = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 128), "c");
212
1
  int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
213
1
  int stride[CCV_NNC_MAX_DIM_ALLOC] = {0};
214
1
  stride[0] = 100 * 128;
215
1
  stride[1] = 128;
216
1
  stride[2] = 1;
217
1
  ccv_nnc_tensor_symbol_t b0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b0");
218
1
  ccv_nnc_tensor_symbol_t c0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c0");
219
1
  ofs[2] = 64;
220
1
  ccv_nnc_tensor_symbol_t b1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b1");
221
1
  ccv_nnc_tensor_symbol_t c1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c1");
222
1
  ccv_nnc_graph_exec_symbol_t conv = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_CONVOLUTION_FORWARD(1, 128, 3, 3, 3), TENSOR_SYMBOL_LIST(image, w, bias), TENSOR_SYMBOL_LIST(b), "conv");
223
1
  ccv_nnc_graph_exec_symbol_t relu0 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b0), TENSOR_SYMBOL_LIST(c0), "relu0");
224
1
  ccv_nnc_graph_exec_symbol_t relu1 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b1), TENSOR_SYMBOL_LIST(c1), "relu1");
225
1
  ccv_nnc_tensor_symbol_t d = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1, 1, 128), "d");
226
1
  ccv_nnc_graph_exec_symbol_t pool = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_AVERAGE_POOL_FORWARD(100, 100), TENSOR_SYMBOL_LIST(c), TENSOR_SYMBOL_LIST(d), "pool");
227
1
  ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(conv, relu0, relu1, pool), 0);
228
1
  ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(d), TENSOR_SYMBOL_LIST(w, bias, b, c), GRAPH_EXEC_SYMBOL_LIST(conv), GRAPH_EXEC_SYMBOL_LIST(pool));
229
1
  SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_SHORT_DOT_GRAPH);
230
1
  ccv_nnc_tensor_symbol_t db = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, b);
231
1
  ccv_nnc_tensor_symbol_t dc = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, c);
232
1
  REQUIRE((ccv_nnc_tensor_symbol_flags(symbolic_graph, db) & CCV_NNC_TENSOR_SYMBOL_INIT_ZEROS), "The gradient for b needs to be zero init");
233
1
  REQUIRE(!(ccv_nnc_tensor_symbol_flags(symbolic_graph, dc) & CCV_NNC_TENSOR_SYMBOL_INIT_ZEROS), "The gradient for c doesn't need to be zero init");
234
1
  ccv_nnc_symbolic_graph_free(symbolic_graph);
235
1
}
236
237
TEST_CASE("autograd with sliced tensors for convolution that are over-subscribed")
238
1
{
239
1
  ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
240
1
  ccv_nnc_tensor_symbol_t image = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 3), "image");
241
1
  ccv_nnc_tensor_symbol_t w = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 128, 3, 3, 3), "w");
242
1
  ccv_nnc_tensor_symbol_t bias = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 128), "bias");
243
1
  ccv_nnc_tensor_symbol_t b = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 128), "b");
244
1
  ccv_nnc_tensor_symbol_t c = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 128), "c");
245
1
  int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
246
1
  int stride[CCV_NNC_MAX_DIM_ALLOC] = {0};
247
1
  stride[0] = 100 * 128;
248
1
  stride[1] = 128;
249
1
  stride[2] = 1;
250
1
  ccv_nnc_tensor_symbol_t b0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b0");
251
1
  ccv_nnc_tensor_symbol_t c0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c0");
252
1
  ofs[2] = 32;
253
1
  ccv_nnc_tensor_symbol_t b1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b1");
254
1
  ccv_nnc_tensor_symbol_t c1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c1");
255
1
  ccv_nnc_graph_exec_symbol_t conv = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_CONVOLUTION_FORWARD(1, 128, 3, 3, 3), TENSOR_SYMBOL_LIST(image, w, bias), TENSOR_SYMBOL_LIST(b), "conv");
256
1
  ccv_nnc_graph_exec_symbol_t relu0 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b0), TENSOR_SYMBOL_LIST(c0), "relu0");
257
1
  ccv_nnc_graph_exec_symbol_t relu1 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b1), TENSOR_SYMBOL_LIST(c1), "relu1");
258
1
  ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(conv, relu0, relu1), 0);
259
1
  ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(c), TENSOR_SYMBOL_LIST(w, bias, b), GRAPH_EXEC_SYMBOL_LIST(conv), GRAPH_EXEC_SYMBOL_LIST(relu0, relu1));
260
1
  SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_SHORT_DOT_GRAPH);
261
1
  ccv_nnc_tensor_symbol_t db = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, b);
262
1
  ccv_nnc_graph_exec_symbol_t dbx = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, db);
263
1
  REQUIRE(ccv_nnc_graph_exec_symbol_cmd(symbolic_graph, dbx).cmd == CCV_NNC_EWSUM_FORWARD, "Since gradient of b is overlapped, it has to be summed up");
264
1
  ccv_nnc_symbolic_graph_free(symbolic_graph);
265
1
}
266
267
TEST_CASE("autograd with sliced tensors for convolution that are over-subscribed with no-op")
268
1
{
269
1
  ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
270
1
  ccv_nnc_tensor_symbol_t image = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 3), "image");
271
1
  ccv_nnc_tensor_symbol_t w = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 128, 3, 3, 3), "w");
272
1
  ccv_nnc_tensor_symbol_t bias = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 128), "bias");
273
1
  ccv_nnc_tensor_symbol_t b = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 128), "b");
274
1
  ccv_nnc_tensor_symbol_t c = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 100, 100, 128), "c");
275
1
  int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
276
1
  int stride[CCV_NNC_MAX_DIM_ALLOC] = {0};
277
1
  stride[0] = 100 * 128;
278
1
  stride[1] = 128;
279
1
  stride[2] = 1;
280
1
  ccv_nnc_tensor_symbol_t b0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b0");
281
1
  ccv_nnc_tensor_symbol_t c0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c0");
282
1
  ofs[2] = 32;
283
1
  ccv_nnc_tensor_symbol_t b1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, b, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "b1");
284
1
  ccv_nnc_tensor_symbol_t c1 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, c, ofs, stride, CPU_TENSOR_NHWC(32F, 50, 100, 64), "c1");
285
1
  ccv_nnc_graph_exec_symbol_t conv = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_CONVOLUTION_FORWARD(1, 128, 3, 3, 3), TENSOR_SYMBOL_LIST(image, w, bias), TENSOR_SYMBOL_LIST(b), "conv");
286
1
  ccv_nnc_graph_exec_symbol_t relu0 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b0), TENSOR_SYMBOL_LIST(c0), "relu0");
287
1
  ccv_nnc_graph_exec_symbol_t relu1 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_RELU_FORWARD(), TENSOR_SYMBOL_LIST(b1), TENSOR_SYMBOL_LIST(c1), "relu1");
288
1
  ccv_nnc_graph_exec_symbol_t noop = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_NOOP(), TENSOR_SYMBOL_LIST(c0, c1), TENSOR_SYMBOL_LIST(c), "noop");
289
1
  ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(conv, relu0, relu1, noop), 0);
290
1
  ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(c), TENSOR_SYMBOL_LIST(w, bias, b), GRAPH_EXEC_SYMBOL_LIST(conv), GRAPH_EXEC_SYMBOL_LIST(noop));
291
1
  SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_SHORT_DOT_GRAPH);
292
1
  ccv_nnc_tensor_symbol_t db = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, b);
293
1
  ccv_nnc_graph_exec_symbol_t dbx = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, db);
294
1
  REQUIRE(ccv_nnc_graph_exec_symbol_cmd(symbolic_graph, dbx).cmd == CCV_NNC_EWSUM_FORWARD, "Since gradient of b is overlapped, it has to be summed up");
295
1
  ccv_nnc_symbolic_graph_free(symbolic_graph);
296
1
}
297
298
typedef struct {
299
  int dobsx;
300
  int dactx;
301
  int has_sum_act;
302
  int has_sum_obs;
303
} autograd_count_t;
304
305
static void _autograd_count(const ccv_nnc_symbolic_graph_t* const graph, const int node, const char* const name, const ccv_nnc_cmd_t cmd, const int flags, const int* const incomings, const int incoming_size, const int* const outgoings, const int outgoing_size, const int* const inputs, const int input_size, const int* const outputs, const int output_size, void* const context)
306
9
{
307
  // Only check sum.
308
9
  if (cmd.cmd != CCV_NNC_EWSUM_FORWARD)
309
8
    return;
310
1
  autograd_count_t* const count = (autograd_count_t*)context;
311
1
  int i;
312
3
  for (i = 0; i < outgoing_size; 
i++2
)
313
2
  {
314
2
    if (outgoings[i] == count->dobsx)
315
1
      count->has_sum_obs = 1;
316
2
    if (outgoings[i] == count->dactx)
317
1
      count->has_sum_act = 1;
318
2
  }
319
1
}
320
321
TEST_CASE("autograd with concatenated tensors to check dependencies done correctly (similar to observation / action concatenation for critics)")
322
1
{
323
1
  ccv_nnc_symbolic_graph_t* symbolic_graph = ccv_nnc_symbolic_graph_new();
324
1
  ccv_nnc_tensor_symbol_t obs = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 64, 4), "obs");
325
1
  ccv_nnc_tensor_symbol_t act = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 64, 1), "act");
326
1
  ccv_nnc_tensor_symbol_t obs_act = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 64, 5), "obs_act");
327
1
  ccv_nnc_tensor_symbol_t w1 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 5, 1), "w1");
328
1
  ccv_nnc_tensor_symbol_t v1 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 64, 1), "v1");
329
1
  ccv_nnc_tensor_symbol_t w2 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 5, 1), "w2");
330
1
  ccv_nnc_tensor_symbol_t v2 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 64, 1), "v2");
331
1
  int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
332
1
  int stride[CCV_NNC_MAX_DIM_ALLOC] = {0};
333
1
  stride[0] = 5;
334
1
  stride[1] = 1;
335
1
  ccv_nnc_tensor_symbol_t obs0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, obs_act, ofs, stride, CPU_TENSOR_NHWC(32F, 64, 4), "obs0");
336
1
  ofs[1] = 4;
337
1
  ccv_nnc_tensor_symbol_t act0 = ccv_nnc_tensor_symbol_alias_new(symbolic_graph, obs_act, ofs, stride, CPU_TENSOR_NHWC(32F, 64, 1), "act0");
338
1
  ccv_nnc_graph_exec_symbol_t obst = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_FORMAT_TRANSFORM_FORWARD(), TENSOR_SYMBOL_LIST(obs), TENSOR_SYMBOL_LIST(obs0), "obst");
339
1
  ccv_nnc_graph_exec_symbol_t actt = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_FORMAT_TRANSFORM_FORWARD(), TENSOR_SYMBOL_LIST(act), TENSOR_SYMBOL_LIST(act0), "actt");
340
1
  ccv_nnc_graph_exec_symbol_t gemm1 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_GEMM_FORWARD(), TENSOR_SYMBOL_LIST(obs_act, w1), TENSOR_SYMBOL_LIST(v1), "gemm1");
341
1
  ccv_nnc_graph_exec_symbol_t gemm2 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_GEMM_FORWARD(), TENSOR_SYMBOL_LIST(obs_act, w2), TENSOR_SYMBOL_LIST(v2), "gemm2");
342
1
  ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(obst, actt, gemm1, gemm2), 0);
343
1
  ccv_nnc_symbolic_graph_backward(symbolic_graph, TENSOR_SYMBOL_LIST(v1, v2), TENSOR_SYMBOL_LIST(w1, w2, obs, act), GRAPH_EXEC_SYMBOL_LIST(obst, actt), GRAPH_EXEC_SYMBOL_LIST(gemm1, gemm2));
344
1
  SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
345
1
  ccv_nnc_tensor_symbol_t dobs = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, obs);
346
1
  ccv_nnc_graph_exec_symbol_t dobsx = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, dobs);
347
1
  ccv_nnc_tensor_symbol_t dact = ccv_nnc_tensor_symbol_for_backward(symbolic_graph, act);
348
1
  ccv_nnc_graph_exec_symbol_t dactx = ccv_nnc_graph_exec_symbol_for_backward(symbolic_graph, dact);
349
1
  autograd_count_t count = {
350
1
    .dobsx = dobsx.d,
351
1
    .dactx = dactx.d,
352
1
    .has_sum_act = 0,
353
1
    .has_sum_obs = 0,
354
1
  };
355
1
  ccv_nnc_symbolic_graph_format(symbolic_graph, GRAPH_EXEC_SYMBOL_LIST(obst, actt), GRAPH_EXEC_SYMBOL_LIST(dobsx, dactx), _autograd_count, &count);
356
1
  REQUIRE(count.has_sum_act && count.has_sum_obs, "both act and obs should be after a sum operation");
357
1
  ccv_nnc_symbolic_graph_free(symbolic_graph);
358
1
}
359
360
#include "case_main.h"