Coverage Report

Created: 2026-04-14 11:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/liu/actions-runner/_work/ccv/ccv/test/unit/nnc/dynamic.graph.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
TEST_CASE("dynamic graph to compute log(19)")
15
{
16
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
17
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
18
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 19;
19
  ccv_nnc_tensor_variable_t b = ccv_nnc_tensor_variable_new(graph);
20
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(b), 0, 0);
21
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, b)->data.f32[0], logf(19), 1e-5, "log(19) result should be equal.");
22
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
23
  ccv_nnc_dynamic_graph_free(graph);
24
}
25
26
TEST_CASE("dynamic graph to compute reciprocal")
27
1
{
28
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
29
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
30
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 19;
31
1
  ccv_nnc_tensor_variable_t b = ccv_nnc_tensor_variable_new(graph);
32
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWDIV_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(0, a), TENSOR_VARIABLE_LIST(b), 0, 0);
33
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, b)->data.f32[0], 1.0 / 19, 1e-5, "1.0 / 19 result should be equal.");
34
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
35
1
  ccv_nnc_dynamic_graph_free(graph);
36
1
}
37
38
TEST_CASE("dynamic graph to compute power and gradients")
39
1
{
40
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
41
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
42
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 2;
43
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph);
44
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPOW_FORWARD(3), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(c), 0, 0);
45
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, c)->data.f32[0], 8, 1e-5, "pow result should be equal.");
46
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
47
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(c), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
48
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], 12, 1e-5, "d(pow(a,3))/da should be 3 * pow(a,2).");
49
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
50
1
  ccv_nnc_dynamic_graph_free(graph);
51
1
}
52
53
TEST_CASE("dynamic graph to compute sin and gradient")
54
1
{
55
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
56
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
57
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 0.5;
58
1
  ccv_nnc_tensor_variable_t b = ccv_nnc_tensor_variable_new(graph);
59
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSIN_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(b), 0, 0);
60
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, b)->data.f32[0], sinf(0.5), 1e-5, "sin result should be equal.");
61
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
62
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(b), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
63
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], cosf(0.5), 1e-5, "d(sin(x))/dx should be cos(x).");
64
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
65
1
  ccv_nnc_dynamic_graph_free(graph);
66
1
}
67
68
TEST_CASE("dynamic graph to compute cos and gradient")
69
1
{
70
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
71
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
72
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 0.5;
73
1
  ccv_nnc_tensor_variable_t b = ccv_nnc_tensor_variable_new(graph);
74
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWCOS_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(b), 0, 0);
75
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, b)->data.f32[0], cosf(0.5), 1e-5, "cos result should be equal.");
76
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
77
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(b), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
78
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], -sinf(0.5), 1e-5, "d(cos(x))/dx should be -sin(x).");
79
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
80
1
  ccv_nnc_dynamic_graph_free(graph);
81
1
}
82
83
TEST_CASE("dynamic graph with alias")
84
1
{
85
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
86
1
  ccv_nnc_tensor_variable_t const a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2));
87
1
  ccv_nnc_tensor_variable_t const a0 = ccv_nnc_tensor_variable_alias_new(graph, a, DIM_ALLOC(1), DIM_ALLOC(), CPU_TENSOR_NHWC(32F, 1));
88
1
  ccv_nnc_tensor_variable_t const a1 = ccv_nnc_tensor_variable_alias_new(graph, a, ccv_nnc_no_ofs, DIM_ALLOC(), CPU_TENSOR_NHWC(32F, 1));
89
1
  ccv_nnc_tensor_t* const b0 = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2), 0);
90
1
  ccv_nnc_tensor_variable_set(graph, a, b0);
91
1
  b0->data.f32[0] = 10;
92
1
  b0->data.f32[1] = 11;
93
1
  ccv_nnc_tensor_t* const b1 = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2), 0);
94
1
  b1->data.f32[0] = 20;
95
1
  b1->data.f32[1] = 21;
96
1
  REQUIRE(CCV_IS_TENSOR_VIEW(ccv_nnc_tensor_from_variable(graph, a0)), "Complex vector is a tensor view");
97
1
  REQUIRE(!CCV_IS_TENSOR_VIEW(ccv_nnc_tensor_from_variable(graph, a1)), "Simple vector is not a tensor view");
98
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a0)->data.f32[0], 11, "should be b0[1]");
99
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a1)->data.f32[0], 10, "should be b0[0]");
100
1
  REQUIRE(!CCV_IS_TENSOR_VIEW(ccv_nnc_tensor_from_variable(graph, a1)), "no need to be a tensor view");
101
1
  ccv_nnc_tensor_variable_set(graph, a, b1);
102
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a0)->data.f32[0], 21, "should be b1[1]");
103
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a1)->data.f32[0], 20, "should be b1[0]");
104
1
  REQUIRE(!CCV_IS_TENSOR_VIEW(ccv_nnc_tensor_from_variable(graph, a1)), "no need to be a tensor view");
105
1
  ccv_nnc_dynamic_graph_free(graph);
106
1
  ccv_nnc_tensor_free(b0);
107
1
  ccv_nnc_tensor_free(b1);
108
1
}
109
110
TEST_CASE("dynamic graph alias an alias")
111
1
{
112
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
113
1
  ccv_nnc_tensor_variable_t const a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2, 3));
114
1
  ccv_nnc_tensor_variable_t const a0 = ccv_nnc_tensor_variable_alias_new(graph, a, DIM_ALLOC(1, 0), DIM_ALLOC(3, 1), CPU_TENSOR_NHWC(32F, 1, 3));
115
1
  ccv_nnc_tensor_variable_t const a1 = ccv_nnc_tensor_variable_alias_new(graph, a0, DIM_ALLOC(1), DIM_ALLOC(1), CPU_TENSOR_NHWC(32F, 2));
116
1
  ccv_nnc_tensor_t* const b0 = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2, 3), 0);
117
1
  ccv_nnc_tensor_variable_set(graph, a, b0);
118
1
  b0->data.f32[0] = 10;
119
1
  b0->data.f32[1] = 11;
120
1
  b0->data.f32[2] = 12;
121
1
  b0->data.f32[3] = 13;
122
1
  b0->data.f32[4] = 14;
123
1
  b0->data.f32[5] = 15;
124
1
  ccv_nnc_tensor_t* const b1 = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2, 3), 0);
125
1
  b1->data.f32[0] = 20;
126
1
  b1->data.f32[1] = 21;
127
1
  b1->data.f32[2] = 22;
128
1
  b1->data.f32[3] = 23;
129
1
  b1->data.f32[4] = 24;
130
1
  b1->data.f32[5] = 25;
131
1
  REQUIRE(CCV_IS_TENSOR_VIEW(ccv_nnc_tensor_from_variable(graph, a0)), "Complex vector is a tensor view");
132
1
  REQUIRE(CCV_IS_TENSOR_VIEW(ccv_nnc_tensor_from_variable(graph, a1)), "Complex vector is a tensor view");
133
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a0)->data.f32[0], 13, "should be b0[1, 0]");
134
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a0)->data.f32[1], 14, "should be b0[1, 1]");
135
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a0)->data.f32[2], 15, "should be b0[1, 2]");
136
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a1)->data.f32[0], 14, "should be b0[1, 1]");
137
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a1)->data.f32[1], 15, "should be b0[1, 2]");
138
1
  ccv_nnc_tensor_variable_set(graph, a, b1);
139
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a0)->data.f32[0], 23, "should be b1[1, 0]");
140
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a0)->data.f32[1], 24, "should be b1[1, 1]");
141
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a0)->data.f32[2], 25, "should be b1[1, 2]");
142
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a1)->data.f32[0], 24, "should be b1[1, 1]");
143
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, a1)->data.f32[1], 25, "should be b1[1, 2]");
144
1
  ccv_nnc_dynamic_graph_free(graph);
145
1
  ccv_nnc_tensor_free(b0);
146
1
  ccv_nnc_tensor_free(b1);
147
1
}
148
149
TEST_CASE("dynamic graph to compute f(x) = x * log(x) + 1.2 * x, f'(x) where x = 19")
150
1
{
151
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
152
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
153
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 19;
154
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
155
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(f), 0, 0);
156
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, f), TENSOR_VARIABLE_LIST(f), 0, 0);
157
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
158
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 1.2;
159
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
160
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(z), 0, 0);
161
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSUM_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(f, z), TENSOR_VARIABLE_LIST(f), 0, 0);
162
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, f)->data.f32[0], 19 * logf(19) + 1.2 * 19, 1e-5, "f(x) = 1.2 * 19 + 19 * log(19)");
163
  // Do gradient computation multiple times.
164
1
  ccv_nnc_tensor_variable_t dx = ccv_nnc_tensor_variable_new(graph);
165
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(dx), 0);
166
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dx)->data.f32[0], logf(19) + 1 + 1.2, 1e-5, "f'(x) = 1.2 + log(19) + 19 * 1 / 19");
167
1
  ccv_nnc_tensor_variable_t dy = ccv_nnc_tensor_variable_new(graph);
168
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(dy), 0);
169
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dy)->data.f32[0], 19, 1e-5, "f'(y) = 19");
170
1
  ccv_nnc_tensor_variable_t dz = ccv_nnc_tensor_variable_new(graph);
171
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(z), TENSOR_VARIABLE_LIST(dz), 0);
172
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dz)->data.f32[0], 1, 1e-5, "f'(z) = 1");
173
1
  ccv_nnc_tensor_variable_free(graph, dy);
174
1
  dy = ccv_nnc_tensor_variable_new(graph);
175
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(dx), 0, 0);
176
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(y, x), TENSOR_VARIABLE_LIST(dy, dx), 0);
177
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dx)->data.f32[0], logf(19) + 1 + 1.2, 1e-5, "f'(x) = 1.2 + log(19) + 19 * 1 / 19");
178
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dy)->data.f32[0], 19, 1e-5, "f'(y) = 19");
179
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
180
1
  ccv_nnc_dynamic_graph_free(graph);
181
1
}
182
183
TEST_CASE("dynamic graph with dense net (extensive use of alias)")
184
1
{
185
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
186
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1, 4));
187
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 0.472;
188
1
  ccv_nnc_tensor_variable_t x1 = ccv_nnc_tensor_variable_alias_new(graph, x, ccv_nnc_no_ofs, DIM_ALLOC(4, 1), CPU_TENSOR_NHWC(32F, 1, 1));
189
1
  ccv_nnc_tensor_variable_t w1 = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1, 1));
190
1
  ccv_nnc_tensor_from_variable(graph, w1)->data.f32[0] = 0.234;
191
1
  ccv_nnc_tensor_variable_t b1 = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
192
1
  ccv_nnc_tensor_from_variable(graph, b1)->data.f32[0] = 0.1;
193
1
  ccv_nnc_tensor_variable_t x11 = ccv_nnc_tensor_variable_alias_new(graph, x, DIM_ALLOC(0, 1), DIM_ALLOC(4, 1), CPU_TENSOR_NHWC(32F, 1, 1));
194
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_GEMM_FORWARD(NO_TRANSPOSE, TRANSPOSE(0, 1)), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x1, w1, b1), TENSOR_VARIABLE_LIST(x11), 0, 0);
195
1
  ccv_nnc_tensor_variable_t x2 = ccv_nnc_tensor_variable_alias_new(graph, x, ccv_nnc_no_ofs, DIM_ALLOC(4, 1), CPU_TENSOR_NHWC(32F, 1, 2));
196
1
  ccv_nnc_tensor_variable_t w2 = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1, 2));
197
1
  ccv_nnc_tensor_from_variable(graph, w2)->data.f32[0] = 0.374;
198
1
  ccv_nnc_tensor_from_variable(graph, w2)->data.f32[1] = 0.886;
199
1
  ccv_nnc_tensor_variable_t b2 = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
200
1
  ccv_nnc_tensor_from_variable(graph, b2)->data.f32[0] = 0.2;
201
1
  ccv_nnc_tensor_variable_t x21 = ccv_nnc_tensor_variable_alias_new(graph, x, DIM_ALLOC(0, 2), DIM_ALLOC(4, 1), CPU_TENSOR_NHWC(32F, 1, 1));
202
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_GEMM_FORWARD(NO_TRANSPOSE, TRANSPOSE(0, 1)), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x2, w2, b2), TENSOR_VARIABLE_LIST(x21), 0, 0);
203
1
  ccv_nnc_tensor_variable_t x3 = ccv_nnc_tensor_variable_alias_new(graph, x, ccv_nnc_no_ofs, DIM_ALLOC(4, 1), CPU_TENSOR_NHWC(32F, 1, 3));
204
1
  ccv_nnc_tensor_variable_t w3 = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1, 3));
205
1
  ccv_nnc_tensor_from_variable(graph, w3)->data.f32[0] = 0.484;
206
1
  ccv_nnc_tensor_from_variable(graph, w3)->data.f32[1] = 0.912;
207
1
  ccv_nnc_tensor_from_variable(graph, w3)->data.f32[2] = 0.235;
208
1
  ccv_nnc_tensor_variable_t b3 = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
209
1
  ccv_nnc_tensor_from_variable(graph, b3)->data.f32[0] = 0.3;
210
1
  ccv_nnc_tensor_variable_t x31 = ccv_nnc_tensor_variable_alias_new(graph, x, DIM_ALLOC(0, 3), DIM_ALLOC(4, 1), CPU_TENSOR_NHWC(32F, 1, 1));
211
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_GEMM_FORWARD(NO_TRANSPOSE, TRANSPOSE(0, 1)), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x3, w3, b3), TENSOR_VARIABLE_LIST(x31), 0, 0);
212
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
213
1
  ccv_nnc_tensor_t* xt = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1, 4), 0);
214
1
  xt->data.f32[0] = 0.472;
215
1
  xt->data.f32[1] = xt->data.f32[0] * 0.234 + 0.1;
216
1
  xt->data.f32[2] = xt->data.f32[0] * 0.374 + xt->data.f32[1] * 0.886 + 0.2;
217
1
  xt->data.f32[3] = xt->data.f32[0] * 0.484 + xt->data.f32[1] * 0.912 + xt->data.f32[2] * 0.235 + 0.3;
218
1
  REQUIRE_MATRIX_EQ(ccv_nnc_tensor_from_variable(graph, x), xt, "1x4 matrix should be exactly the same");
219
1
  ccv_nnc_tensor_free(xt);
220
1
  ccv_nnc_tensor_variable_t dw1 = ccv_nnc_tensor_variable_new(graph);
221
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(x), 0, TENSOR_VARIABLE_LIST(w1), TENSOR_VARIABLE_LIST(dw1), 0);
222
1
  REQUIRE_EQ_WITH_TOLERANCE((0.235 * 0.886 + 0.912) * 0.472, ccv_nnc_tensor_from_variable(graph, dw1)->data.f32[0], 1e-5, "the gradient should be equal to a complicated result");
223
1
  ccv_nnc_dynamic_graph_free(graph);
224
1
}
225
226
TEST_CASE("batch norm in dynamic graph (enforce inplace)")
227
1
{
228
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
229
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2, 2, 2, 10));
230
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
231
1
  ccv_nnc_tensor_variable_t scale = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 10));
232
1
  ccv_nnc_tensor_variable_t bias = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 10));
233
1
  ccv_nnc_tensor_variable_t mean = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 10));
234
1
  ccv_nnc_tensor_variable_t var = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 10));
235
1
  ccv_nnc_tensor_variable_t saved_mean = ccv_nnc_tensor_variable_new(graph);
236
1
  ccv_nnc_tensor_variable_t saved_inv_std = ccv_nnc_tensor_variable_new(graph);
237
1
  dsfmt_t dsfmt;
238
1
  int i;
239
1
  dsfmt_init_gen_rand(&dsfmt, 1);
240
81
  for (i = 0; i < 2 * 2 * 2 * 10; 
i++80
)
241
80
    ccv_nnc_tensor_from_variable(graph, x)->data.f32[i] = dsfmt_genrand_open_close(&dsfmt);
242
11
  for (i = 0; i < 10; 
i++10
)
243
10
    ccv_nnc_tensor_from_variable(graph, scale)->data.f32[i] = 1;
244
11
  for (i = 0; i < 10; 
i++10
)
245
10
    ccv_nnc_tensor_from_variable(graph, bias)->data.f32[i] = 0;
246
11
  for (i = 0; i < 10; 
i++10
)
247
10
    ccv_nnc_tensor_from_variable(graph, mean)->data.f32[i] = 0;
248
1
  ccv_nnc_tensor_t* mean_tensor_ptr = ccv_nnc_tensor_from_variable(graph, mean);
249
11
  for (i = 0; i < 10; 
i++10
)
250
10
    ccv_nnc_tensor_from_variable(graph, var)->data.f32[i] = 0;
251
1
  ccv_nnc_tensor_t* var_tensor_ptr = ccv_nnc_tensor_from_variable(graph, var);
252
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_BATCH_NORM_FORWARD(0, 0, 0.9, 0, 1, 2), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, scale, bias, mean, var), TENSOR_VARIABLE_LIST(y, mean, var, saved_mean, saved_inv_std), 0, 0);
253
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
254
1
  REQUIRE(mean_tensor_ptr == ccv_nnc_tensor_from_variable(graph, mean), "enforced inplace, tensor view pointer unchanged");
255
1
  REQUIRE(var_tensor_ptr == ccv_nnc_tensor_from_variable(graph, var), "enforced inplace, tensor view pointer unchanged");
256
1
  ccv_nnc_tensor_t* x_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2, 2, 2, 10), 0);
257
1
  ccv_nnc_tensor_t* y_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 2, 2, 2, 10), 0);
258
1
  ccv_nnc_tensor_t* scale_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 10), 0);
259
1
  ccv_nnc_tensor_t* bias_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 10), 0);
260
1
  ccv_nnc_tensor_t* mean_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 10), 0);
261
1
  ccv_nnc_tensor_t* var_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 10), 0);
262
1
  ccv_nnc_tensor_t* saved_mean_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1, 1, 1, 10), 0);
263
1
  ccv_nnc_tensor_t* saved_inv_std_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1, 1, 1, 10), 0);
264
1
  memcpy(x_tensor->data.f32, ccv_nnc_tensor_from_variable(graph, x)->data.f32, sizeof(float) * 2 * 2 * 2 * 10);
265
11
  for (i = 0; i < 10; 
i++10
)
266
10
    scale_tensor->data.f32[i] = 1;
267
1
  memset(bias_tensor->data.f32, 0, sizeof(float) * 10);
268
1
  memset(mean_tensor->data.f32, 0, sizeof(float) * 10);
269
1
  memset(var_tensor->data.f32, 0, sizeof(float) * 10);
270
1
  ccv_nnc_cmd_exec(CMD_BATCH_NORM_FORWARD(0, 0, 0.9, 0, 1, 2), ccv_nnc_no_hint, 0, TENSOR_LIST(x_tensor, scale_tensor, bias_tensor, mean_tensor, var_tensor), TENSOR_LIST(y_tensor, mean_tensor, var_tensor, saved_mean_tensor, saved_inv_std_tensor), 0);
271
1
  REQUIRE_TENSOR_EQ(y_tensor, ccv_nnc_tensor_from_variable(graph, y), "y should be equal");
272
1
  REQUIRE_TENSOR_EQ(mean_tensor, ccv_nnc_tensor_from_variable(graph, mean), "mean should be equal");
273
1
  REQUIRE_TENSOR_EQ(var_tensor, ccv_nnc_tensor_from_variable(graph, var), "var should be equal");
274
1
  REQUIRE_TENSOR_EQ(saved_mean_tensor, ccv_nnc_tensor_from_variable(graph, saved_mean), "saved_mean should be equal");
275
1
  REQUIRE_TENSOR_EQ(saved_inv_std_tensor, ccv_nnc_tensor_from_variable(graph, saved_inv_std), "saved_inv_std should be equal");
276
1
  ccv_nnc_dynamic_graph_free(graph);
277
1
  ccv_nnc_tensor_free(x_tensor);
278
1
  ccv_nnc_tensor_free(y_tensor);
279
1
  ccv_nnc_tensor_free(scale_tensor);
280
1
  ccv_nnc_tensor_free(bias_tensor);
281
1
  ccv_nnc_tensor_free(mean_tensor);
282
1
  ccv_nnc_tensor_free(var_tensor);
283
1
  ccv_nnc_tensor_free(saved_mean_tensor);
284
1
  ccv_nnc_tensor_free(saved_inv_std_tensor);
285
1
}
286
287
TEST_CASE("empty inputs / outputs for dynamic graph")
288
1
{
289
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
290
1
  ccv_nnc_tensor_variable_t df = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
291
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
292
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
293
1
  ccv_nnc_tensor_from_variable(graph, df)->data.f32[0] = 1;
294
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
295
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWDIV_BACKWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(df, 0, x), TENSOR_VARIABLE_LIST(y, 0), 0, 0);
296
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
297
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], 1. / 10, 1e-5, "div backward should equal to 1 / 10");
298
1
  ccv_nnc_dynamic_graph_free(graph);
299
1
}
300
301
TEST_CASE("long dynamic graph with unused variables freed")
302
1
{
303
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
304
1
  int i;
305
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
306
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
307
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 32;
308
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 0.5;
309
11
  for (i = 0; i < 10; 
i++10
)
310
10
  {
311
10
    ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
312
10
    if (i < 7)
313
7
      ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(z), 0, 0);
314
3
    else {
315
3
      if (i == 7)
316
1
        ccv_nnc_tensor_variable_free(graph, y); // No longer need y.
317
3
      ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, x), TENSOR_VARIABLE_LIST(z), 0, 0);
318
3
    }
319
10
    if (i < 9)
320
9
      ccv_nnc_tensor_variable_free(graph, x);
321
10
    x = z;
322
10
  }
323
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
324
1
  float g = 32;
325
11
  for (i = 0; i < 10; 
i++10
)
326
10
  {
327
10
    if (i < 7)
328
7
      g = g * 0.5;
329
3
    else
330
3
      g = g * g;
331
10
  }
332
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, x)->data.f32[0], g, 1e-5, "x should equal to the computed result");
333
1
  ccv_nnc_dynamic_graph_free(graph);
334
1
}
335
336
TEST_CASE("repeat multiple x * y with y as a constant")
337
1
{
338
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
339
1
  int i;
340
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
341
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_constant_new(graph, CPU_TENSOR_NHWC(32F, 1));
342
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 32;
343
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 0.5;
344
11
  for (i = 0; i < 10; 
i++10
)
345
10
  {
346
10
    ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
347
10
    ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(z), 0, 0);
348
10
    ccv_nnc_tensor_variable_free(graph, x);
349
10
    x = z;
350
10
  }
351
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
352
1
  float g = 32;
353
11
  for (i = 0; i < 10; 
i++10
)
354
10
    g = g * 0.5;
355
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, x)->data.f32[0], g, 1e-5, "x should equal to the computed result");
356
1
  ccv_nnc_dynamic_graph_free(graph);
357
1
}
358
359
static int _ccv_tensor_variable_freed = 0;
360
361
static void _ccv_tensor_variable_hook(ccv_nnc_dynamic_graph_t* const graph, const ccv_nnc_tensor_t* const tensor, void* const context)
362
7
{
363
7
  ++_ccv_tensor_variable_freed;
364
7
}
365
366
TEST_CASE("repeat multiple x * y with y as a constant, compute d(x)")
367
1
{
368
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
369
1
  int i;
370
1
  _ccv_tensor_variable_freed = 0;
371
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
372
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_constant_new(graph, CPU_TENSOR_NHWC(32F, 1));
373
1
  ccv_nnc_tensor_variable_t ox = x;
374
1
  ccv_nnc_tensor_variable_destructor_hook(graph, x, _ccv_tensor_variable_hook, 0);
375
1
  ccv_nnc_tensor_variable_destructor_hook(graph, y, _ccv_tensor_variable_hook, 0);
376
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 32;
377
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 0.9;
378
5
  for (i = 0; i < 4; 
i++4
)
379
4
  {
380
4
    ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
381
4
    ccv_nnc_tensor_variable_destructor_hook(graph, z, _ccv_tensor_variable_hook, 0);
382
4
    ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(z), 0, 0);
383
4
    if (i > 0)
384
3
      ccv_nnc_tensor_variable_free(graph, x);
385
4
    x = z;
386
4
  }
387
1
  REQUIRE_EQ(0, _ccv_tensor_variable_freed, "none of these are freed");
388
1
  ccv_nnc_tensor_variable_t dx = ccv_nnc_tensor_variable_new(graph);
389
1
  ccv_nnc_tensor_variable_destructor_hook(graph, dx, _ccv_tensor_variable_hook, 0);
390
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(x), 0, TENSOR_VARIABLE_LIST(ox), TENSOR_VARIABLE_LIST(dx), 0);
391
1
  ccv_nnc_tensor_variable_free(graph, ox);
392
  // We freed all 4 x (ox, when i = 1, 2, 3).
393
1
  REQUIRE_EQ(4, _ccv_tensor_variable_freed, "all are freed except dx");
394
1
  _ccv_tensor_variable_freed = 0;
395
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
396
1
  float g = 1;
397
5
  for (i = 0; i < 4; 
i++4
)
398
4
    g = g * 0.9;
399
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dx)->data.f32[0], g, 1e-5, "x should equal to the computed result");
400
1
  ccv_nnc_dynamic_graph_free(graph);
401
  // y, dx, and z.
402
1
  REQUIRE_EQ(3, _ccv_tensor_variable_freed, "dx freed");
403
1
}
404
405
TEST_CASE("compute f(x) = x * log(x) + x, f'(x) when x = 10 (and intermediate results all freed)")
406
1
{
407
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
408
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
409
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
410
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
411
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
412
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
413
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(y), 0, 0);
414
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSUM_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(y, x), TENSOR_VARIABLE_LIST(f), 0, 0);
415
1
  ccv_nnc_tensor_variable_t df = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
416
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(1), ccv_nnc_no_hint, 0, 0, 0, TENSOR_VARIABLE_LIST(df), 0, 0);
417
  // x will be accumulated on to itself.
418
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), &df, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(x), 0);
419
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
420
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, x)->data.f32[0], 10 + log(10) + 1 + 1, 1e-5, "dx should equal to the computed result");
421
1
  ccv_nnc_dynamic_graph_free(graph);
422
1
}
423
424
TEST_CASE("dynamic graph with binded value")
425
1
{
426
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
427
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
428
1
  ccv_nnc_tensor_t* const x_tensor = ccv_nnc_tensor_new(0, CPU_TENSOR_NHWC(32F, 1), 0);
429
1
  x_tensor->data.f32[0] = 10;
430
1
  ccv_nnc_tensor_variable_set(graph, x, x_tensor);
431
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
432
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
433
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
434
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], log(10), 1e-5, "dx should equal to the computed result");
435
1
  ccv_nnc_dynamic_graph_free(graph);
436
1
  ccv_nnc_tensor_free(x_tensor);
437
1
}
438
439
TEST_CASE("dynamic graph to evaluate cnnp model")
440
1
{
441
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
442
1
  ccv_cnnp_model_t* const linear = ccv_cnnp_dense(1, 0, 0, 1, 0);
443
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_constant_new(graph, CPU_TENSOR_NHWC(32F, 1));
444
1
  ccv_nnc_tensor_from_variable(graph, z)->data.f32[0] = 5;
445
1
  int i;
446
101
  for (i = 0; i < 100; 
i++100
)
447
100
  {
448
100
    ccv_nnc_tensor_variable_t x;
449
100
    if (i % 2 == 1)
450
50
    {
451
50
      x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2, 1));
452
50
      ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
453
50
      ccv_nnc_tensor_from_variable(graph, x)->data.f32[1] = 10;
454
50
    } else {
455
50
      x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
456
50
      ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
457
50
    }
458
100
    ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
459
100
    ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
460
100
    ccv_nnc_dynamic_graph_evaluate(graph, linear, 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(y), 0, 0);
461
100
    ccv_nnc_dynamic_graph_exec(graph, CMD_ADD_FORWARD(1, -1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(y, z), TENSOR_VARIABLE_LIST(y), 0, 0);
462
100
    ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
463
100
    ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(y, y), TENSOR_VARIABLE_LIST(f), 0, 0);
464
100
    ccv_nnc_tensor_variable_t dx = ccv_nnc_tensor_variable_new(graph);
465
100
    ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(dx), 0);
466
100
    ccv_cnnp_model_set_minimizer(linear, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), 0, 0, 0);
467
100
    ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(), 0, 0, 0);
468
100
    ccv_nnc_tensor_variable_free(graph, x);
469
100
    ccv_nnc_tensor_variable_free(graph, y);
470
100
    ccv_nnc_tensor_variable_free(graph, f);
471
100
    ccv_nnc_tensor_variable_free(graph, dx);
472
100
  }
473
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
474
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
475
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
476
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
477
1
  ccv_nnc_dynamic_graph_evaluate(graph, linear, 1, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(y), 0, 0);
478
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], 5, 1e-2, "linear model should be trained to generate the same value as z");
479
1
  ccv_nnc_tensor_variable_t iy = ccv_nnc_tensor_constant_new(graph, CPU_TENSOR_NHWC(32F, 1));
480
1
  ccv_nnc_tensor_from_variable(graph, iy)->data.f32[0] = log(10);
481
1
  ccv_nnc_tensor_variable_t iz = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
482
1
  ccv_nnc_dynamic_graph_evaluate(graph, linear, 1, TENSOR_VARIABLE_LIST(iy), TENSOR_VARIABLE_LIST(iz), 0, 0);
483
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, iz)->data.f32[0], 5, 1e-2, "linear model should be trained to generate the same value as z");
484
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
485
1
  ccv_nnc_dynamic_graph_free(graph);
486
1
  ccv_cnnp_model_free(linear);
487
1
}
488
489
TEST_CASE("dynamic graph to evaluate cnnp model without any parameters")
490
1
{
491
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
492
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
493
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 1.23;
494
1
  ccv_nnc_tensor_variable_t b = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
495
1
  ccv_nnc_tensor_from_variable(graph, b)->data.f32[0] = 2;
496
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
497
1
  ccv_cnnp_model_t* const mul = ccv_cnnp_mul(1, "mul");
498
1
  ccv_nnc_dynamic_graph_evaluate(graph, mul, 1, TENSOR_VARIABLE_LIST(a, b), TENSOR_VARIABLE_LIST(c), 0, 0);
499
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
500
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(c), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
501
1
  ccv_cnnp_model_set_minimizer(mul, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), 0, 0, 0);
502
1
  ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(), 0, 0, 0);
503
1
  ccv_cnnp_model_free(mul);
504
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, c)->data.f32[0], 2.46, 1e-5, "should be equal");
505
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], 2, 1e-5, "should be equal");
506
1
  ccv_nnc_dynamic_graph_free(graph);
507
1
}
508
509
TEST_CASE("dynamic graph to evaluate cnnp model without any parameters with div")
510
1
{
511
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
512
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
513
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 1.23;
514
1
  ccv_nnc_tensor_variable_t b = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
515
1
  ccv_nnc_tensor_from_variable(graph, b)->data.f32[0] = 2;
516
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
517
1
  ccv_cnnp_model_t* const div = ccv_cnnp_div(0, "div");
518
1
  ccv_nnc_dynamic_graph_evaluate(graph, div, 1, TENSOR_VARIABLE_LIST(a, b), TENSOR_VARIABLE_LIST(c), 0, 0);
519
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
520
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(c), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
521
1
  ccv_cnnp_model_set_minimizer(div, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), 0, 0, 0);
522
1
  ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(), 0, 0, 0);
523
1
  ccv_cnnp_model_free(div);
524
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, c)->data.f32[0], 1.23 / 2, 1e-5, "should be equal");
525
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], 1.0 / 2, 1e-5, "should be equal");
526
1
  ccv_nnc_dynamic_graph_free(graph);
527
1
}
528
529
TEST_CASE("dynamic graph to evaluate cnnp model without any parameters with reciprocal")
530
1
{
531
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
532
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
533
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 1.23;
534
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
535
1
  ccv_cnnp_model_t* const div = ccv_cnnp_div(1, "div");
536
1
  ccv_nnc_dynamic_graph_evaluate(graph, div, 1, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(c), 0, 0);
537
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
538
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(c), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
539
1
  ccv_cnnp_model_set_minimizer(div, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), 0, 0, 0);
540
1
  ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(), 0, 0, 0);
541
1
  ccv_cnnp_model_free(div);
542
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, c)->data.f32[0], 1 / 1.23, 1e-5, "should be equal");
543
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], -1 / (1.23 * 1.23), 1e-5, "should be equal");
544
1
  ccv_nnc_dynamic_graph_free(graph);
545
1
}
546
547
TEST_CASE("dynamic graph to evaluate cnnp model without any parameters with log")
548
1
{
549
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
550
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
551
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 1.23;
552
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
553
1
  ccv_cnnp_model_t* const log = ccv_cnnp_log("log");
554
1
  ccv_nnc_dynamic_graph_evaluate(graph, log, 1, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(c), 0, 0);
555
1
  ccv_cnnp_model_free(log);
556
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, c)->data.f32[0], logf(1.23), 1e-5, "should be equal");
557
1
  ccv_nnc_dynamic_graph_free(graph);
558
1
}
559
560
TEST_CASE("dynamic graph to evaluate cnnp model without any parameters with pow")
561
1
{
562
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
563
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
564
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 2;
565
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
566
1
  ccv_cnnp_model_t* const pow = ccv_cnnp_pow(3, "pow");
567
1
  ccv_nnc_dynamic_graph_evaluate(graph, pow, 1, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(c), 0, 0);
568
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
569
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(c), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
570
1
  ccv_cnnp_model_set_minimizer(pow, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), 0, 0, 0);
571
1
  ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(), 0, 0, 0);
572
1
  ccv_cnnp_model_free(pow);
573
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, c)->data.f32[0], 8, 1e-5, "should be equal");
574
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], 12, 1e-5, "should be equal");
575
1
  ccv_nnc_dynamic_graph_free(graph);
576
1
}
577
578
TEST_CASE("dynamic graph to evaluate cnnp model without any parameters with sin")
579
1
{
580
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
581
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
582
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 0.5;
583
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
584
1
  ccv_cnnp_model_t* const sin = ccv_cnnp_sin("sin");
585
1
  ccv_nnc_dynamic_graph_evaluate(graph, sin, 1, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(c), 0, 0);
586
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
587
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(c), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
588
1
  ccv_cnnp_model_set_minimizer(sin, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), 0, 0, 0);
589
1
  ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(), 0, 0, 0);
590
1
  ccv_cnnp_model_free(sin);
591
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, c)->data.f32[0], sinf(0.5), 1e-5, "should be equal");
592
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], cosf(0.5), 1e-5, "should be equal");
593
1
  ccv_nnc_dynamic_graph_free(graph);
594
1
}
595
596
TEST_CASE("dynamic graph to evaluate cnnp model without any parameters with cos")
597
1
{
598
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
599
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
600
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 0.5;
601
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
602
1
  ccv_cnnp_model_t* const cos = ccv_cnnp_cos("cos");
603
1
  ccv_nnc_dynamic_graph_evaluate(graph, cos, 1, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(c), 0, 0);
604
1
  ccv_nnc_tensor_variable_t da = ccv_nnc_tensor_variable_new(graph);
605
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(c), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(da), 0);
606
1
  ccv_cnnp_model_set_minimizer(cos, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), 0, 0, 0);
607
1
  ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(), 0, 0, 0);
608
1
  ccv_cnnp_model_free(cos);
609
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, c)->data.f32[0], cosf(0.5), 1e-5, "should be equal");
610
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, da)->data.f32[0], -sinf(0.5), 1e-5, "should be equal");
611
1
  ccv_nnc_dynamic_graph_free(graph);
612
1
}
613
614
TEST_CASE("dynamic graph to evaluate cnnp model and simply accumulate gradients")
615
1
{
616
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
617
1
  ccv_cnnp_model_t* const linear = ccv_cnnp_dense(1, 0, 0, 1, 0);
618
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_constant_new(graph, CPU_TENSOR_NHWC(32F, 1));
619
1
  ccv_nnc_tensor_from_variable(graph, z)->data.f32[0] = 5;
620
1
  int i;
621
101
  for (i = 0; i < 100; 
i++100
)
622
100
  {
623
100
    ccv_nnc_tensor_variable_t x;
624
100
    if (i % 2 == 1)
625
50
    {
626
50
      x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2, 1));
627
50
      ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
628
50
      ccv_nnc_tensor_from_variable(graph, x)->data.f32[1] = 10;
629
50
    } else {
630
50
      x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
631
50
      ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
632
50
    }
633
100
    ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
634
100
    ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
635
100
    ccv_nnc_dynamic_graph_evaluate(graph, linear, 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(y), 0, 0);
636
100
    ccv_nnc_dynamic_graph_exec(graph, CMD_ADD_FORWARD(1, -1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(y, z), TENSOR_VARIABLE_LIST(y), 0, 0);
637
100
    ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
638
100
    ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(y, y), TENSOR_VARIABLE_LIST(f), 0, 0);
639
100
    ccv_nnc_tensor_variable_t dx = ccv_nnc_tensor_variable_new(graph);
640
100
    ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(dx), 0);
641
100
    ccv_nnc_tensor_variable_free(graph, x);
642
100
    ccv_nnc_tensor_variable_free(graph, y);
643
100
    ccv_nnc_tensor_variable_free(graph, f);
644
100
    ccv_nnc_tensor_variable_free(graph, dx);
645
100
    if ((i % 2) == 1)
646
50
    {
647
50
      ccv_cnnp_model_set_minimizer(linear, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), 0, 0, 0);
648
50
      ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, 0.01, 1, 0.01, 0, 0), TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(), 0, 0, 0);
649
50
    }
650
100
  }
651
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
652
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
653
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
654
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
655
1
  ccv_nnc_dynamic_graph_evaluate(graph, linear, 1, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(y), 0, 0);
656
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], 5, 1e-2, "linear model should be trained to generate the same value as z");
657
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
658
1
  ccv_nnc_dynamic_graph_free(graph);
659
1
  ccv_cnnp_model_free(linear);
660
1
}
661
662
TEST_CASE("dynamic graph to accumulate gradients cross cnnp models")
663
1
{
664
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
665
1
  ccv_cnnp_model_t* const linear = ccv_cnnp_dense(1, 1, 0, 1, 0);
666
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
667
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(0.2485), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(a), 0, 0);
668
1
  const ccv_nnc_tensor_param_t input = CPU_TENSOR_NHWC(32F, 1);
669
1
  ccv_cnnp_model_compile(linear, &input, 1, CMD_NOOP(), CMD_NOOP());
670
1
  ccv_cnnp_model_set_parameter(linear, ccv_cnnp_model_parameters(linear, CCV_CNNP_PARAMETER_SELECT_WEIGHT, 0), ccv_nnc_tensor_from_variable(graph, a));
671
1
  ccv_nnc_tensor_variable_t a_grad = ccv_nnc_tensor_variable_new(graph);
672
1
  ccv_nnc_tensor_variable_t saved_aux = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
673
1
  int i;
674
1.00k
  for (i = 0; i < 1000; 
i++1.00k
)
675
1.00k
  {
676
1.00k
    ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
677
1.00k
    ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = i;
678
1.00k
    ccv_nnc_tensor_variable_t t = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
679
1.00k
    ccv_nnc_tensor_from_variable(graph, t)->data.f32[0] = -i;
680
1.00k
    ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
681
1.00k
    ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, a), TENSOR_VARIABLE_LIST(y), 0, 0);
682
1.00k
    ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
683
1.00k
    ccv_nnc_dynamic_graph_evaluate(graph, linear, 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(z), 0, 0);
684
1.00k
    ccv_nnc_tensor_variable_t n = ccv_nnc_tensor_variable_new(graph);
685
1.00k
    ccv_nnc_dynamic_graph_exec(graph, CMD_ADD_FORWARD(1, -1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(z, t), TENSOR_VARIABLE_LIST(n), 0, 0);
686
1.00k
    ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
687
1.00k
    ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(n, n), TENSOR_VARIABLE_LIST(f), 0, 0);
688
1.00k
    ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(a_grad), 0);
689
1.00k
    ccv_nnc_tensor_variable_free(graph, n);
690
1.00k
    ccv_nnc_tensor_variable_free(graph, z);
691
1.00k
    ccv_nnc_tensor_variable_free(graph, y);
692
1.00k
    ccv_nnc_tensor_variable_free(graph, x);
693
1.00k
    ccv_nnc_tensor_variable_free(graph, t);
694
1.00k
    ccv_nnc_tensor_variable_free(graph, f);
695
1.00k
    if (((i + 1) % 5) == 0)
696
200
    {
697
200
      DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
698
200
      REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
699
200
      float lr = 0.001;
700
200
      if (i >= 200)
701
160
        lr = 0.0001;
702
40
      else if (i >= 600)
703
0
        lr = 0.00001;
704
200
      ccv_cnnp_model_set_minimizer(linear, CMD_SGD_FORWARD(0, lr, 0.001, 0, 0, 0), 0, 0, 0);
705
200
      ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, lr, 0.001, 0, 0, 0), TENSOR_VARIABLE_LIST(a_grad), TENSOR_VARIABLE_LIST(a), &saved_aux, 0, 0);
706
200
    }
707
1.00k
  }
708
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
709
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 5;
710
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
711
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, a), TENSOR_VARIABLE_LIST(y), 0, 0);
712
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
713
1
  ccv_nnc_dynamic_graph_evaluate(graph, linear, 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(z), 0, 0);
714
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, z)->data.f32[0], -5, 1e-2, "linear model should be trained to generate the same value as z");
715
1
  ccv_nnc_dynamic_graph_free(graph);
716
1
  ccv_cnnp_model_free(linear);
717
1
}
718
719
TEST_CASE("dynamic graph to accumulate gradients cross cnnp models with aliases")
720
1
{
721
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
722
1
  ccv_cnnp_model_t* const linear = ccv_cnnp_sequential_new(MODEL_LIST(
723
1
    ccv_cnnp_dense(1, 1, 0, 1, 0),
724
1
    ccv_cnnp_reshape(0, DIM_ALLOC(1), DIM_ALLOC(), DIM_ALLOC(), 0),
725
1
  ), 1, 0);
726
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
727
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(0.2485), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(a), 0, 0);
728
1
  const ccv_nnc_tensor_param_t input = CPU_TENSOR_NHWC(32F, 1);
729
1
  ccv_cnnp_model_compile(linear, &input, 1, CMD_NOOP(), CMD_NOOP());
730
1
  ccv_cnnp_model_set_parameter(linear, ccv_cnnp_model_parameters(linear, CCV_CNNP_PARAMETER_SELECT_WEIGHT, 0), ccv_nnc_tensor_from_variable(graph, a));
731
1
  ccv_nnc_tensor_variable_t a_grad = ccv_nnc_tensor_variable_new(graph);
732
1
  ccv_nnc_tensor_variable_t saved_aux = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
733
1
  int i;
734
1.00k
  for (i = 0; i < 1000; 
i++1.00k
)
735
1.00k
  {
736
1.00k
    ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
737
1.00k
    ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = i;
738
1.00k
    ccv_nnc_tensor_variable_t t = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
739
1.00k
    ccv_nnc_tensor_from_variable(graph, t)->data.f32[0] = -i;
740
1.00k
    ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
741
1.00k
    ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, a), TENSOR_VARIABLE_LIST(y), 0, 0);
742
1.00k
    ccv_nnc_tensor_variable_t y_alias = ccv_nnc_tensor_variable_alias_new(graph, y, DIM_ALLOC(), DIM_ALLOC(1), CPU_TENSOR_NHWC(32F, 1));
743
1.00k
    ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
744
1.00k
    ccv_nnc_dynamic_graph_evaluate(graph, linear, 0, TENSOR_VARIABLE_LIST(y_alias), TENSOR_VARIABLE_LIST(z), 0, 0);
745
1.00k
    ccv_nnc_tensor_variable_t n = ccv_nnc_tensor_variable_new(graph);
746
1.00k
    ccv_nnc_dynamic_graph_exec(graph, CMD_ADD_FORWARD(1, -1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(z, t), TENSOR_VARIABLE_LIST(n), 0, 0);
747
1.00k
    ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
748
1.00k
    ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(n, n), TENSOR_VARIABLE_LIST(f), 0, 0);
749
1.00k
    ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(a_grad), 0);
750
1.00k
    ccv_nnc_tensor_variable_free(graph, n);
751
1.00k
    ccv_nnc_tensor_variable_free(graph, z);
752
1.00k
    ccv_nnc_tensor_variable_free(graph, y);
753
1.00k
    ccv_nnc_tensor_variable_free(graph, y_alias);
754
1.00k
    ccv_nnc_tensor_variable_free(graph, x);
755
1.00k
    ccv_nnc_tensor_variable_free(graph, t);
756
1.00k
    ccv_nnc_tensor_variable_free(graph, f);
757
1.00k
    if (((i + 1) % 5) == 0)
758
200
    {
759
200
      REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
760
200
      DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
761
200
      float lr = 0.001;
762
200
      if (i >= 200)
763
160
        lr = 0.0001;
764
40
      else if (i >= 600)
765
0
        lr = 0.00001;
766
200
      ccv_cnnp_model_set_minimizer(linear, CMD_SGD_FORWARD(0, lr, 0.001, 0, 0, 0), 0, 0, 0);
767
200
      ccv_nnc_dynamic_graph_apply_gradients(graph, CMD_SGD_FORWARD(0, lr, 0.001, 0, 0, 0), TENSOR_VARIABLE_LIST(a_grad), TENSOR_VARIABLE_LIST(a), &saved_aux, 0, 0);
768
200
    }
769
1.00k
  }
770
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
771
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 5;
772
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
773
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, a), TENSOR_VARIABLE_LIST(y), 0, 0);
774
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
775
1
  ccv_nnc_dynamic_graph_evaluate(graph, linear, 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(z), 0, 0);
776
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, z)->data.f32[0], -5, 1e-2, "linear model should be trained to generate the same value as z");
777
1
  ccv_nnc_dynamic_graph_free(graph);
778
1
  ccv_cnnp_model_free(linear);
779
1
}
780
781
TEST_CASE("dynamic graph to use cnnp model for permute and reshape")
782
1
{
783
1
  ccv_cnnp_model_t* const sequential = ccv_cnnp_sequential_new(MODEL_LIST(
784
1
    ccv_cnnp_reshape(0, DIM_ALLOC(4, 3, 2), DIM_ALLOC(), DIM_ALLOC(), 0),
785
1
    ccv_cnnp_permute(DIM_ALLOC(2, 0, 1), 0),
786
1
    ccv_cnnp_reshape(0, DIM_ALLOC(2 * 4, 3), DIM_ALLOC(), DIM_ALLOC(), 0),
787
1
  ), 1, 0);
788
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
789
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 4, 3, 2));
790
1
  int i;
791
25
  for (i = 0; i < 4 * 3 * 2; 
i++24
)
792
24
    ccv_nnc_tensor_from_variable(graph, x)->data.f32[i] = i;
793
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
794
1
  ccv_nnc_dynamic_graph_evaluate(graph, sequential, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
795
  // 0, 1,
796
  // 2, 3,
797
  // 4, 5
798
  //
799
  // 6, 7,
800
  // 8, 9,
801
  // 10, 11
802
  //
803
  // 12, 13,
804
  // 14, 15,
805
  // 16, 17
806
  //
807
  // 18, 19,
808
  // 20, 21,
809
  // 22, 23
810
  //
811
  // After permute:
812
  // 0, 2, 4,
813
  // 6, 8, 10,
814
  // 12, 14, 16,
815
  // 18, 20, 22
816
  //
817
  // 1, 3, 5,
818
  // 7, 9, 11,
819
  // 13, 15, 17,
820
  // 19, 21, 23
821
1
  float btp[] = {
822
1
    0, 2, 4,
823
1
    6, 8, 10,
824
1
    12, 14, 16,
825
1
    18, 20, 22,
826
1
    1, 3, 5,
827
1
    7, 9, 11,
828
1
    13, 15, 17,
829
1
    19, 21, 23
830
1
  };
831
1
  ccv_nnc_tensor_t bt = ccv_nnc_tensor(btp, CPU_TENSOR_NHWC(32F, 2 * 4, 3), 0);
832
1
  REQUIRE_TENSOR_EQ(ccv_nnc_tensor_from_variable(graph, y), &bt, "should materialize permute before reshape");
833
1
  ccv_nnc_dynamic_graph_free(graph);
834
1
  ccv_cnnp_model_free(sequential);
835
1
}
836
837
TEST_CASE("dynamic graph to compute f(x) = x * log(x) + 1.2 * x, f'(x) and sum on x = 19, 10")
838
1
{
839
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
840
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
841
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 19;
842
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
843
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(f), 0, 0);
844
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, f), TENSOR_VARIABLE_LIST(f), 0, 0);
845
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
846
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 1.2;
847
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
848
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(z), 0, 0);
849
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSUM_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(f, z), TENSOR_VARIABLE_LIST(f), 0, 0);
850
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, f)->data.f32[0], 19 * logf(19) + 1.2 * 19, 1e-5, "f(x) = 1.2 * 19 + 19 * log(19)");
851
  // Do gradient computation multiple times.
852
1
  ccv_nnc_tensor_variable_t dx = ccv_nnc_tensor_variable_new(graph);
853
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(dx), 0);
854
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dx)->data.f32[0], logf(19) + 1 + 1.2, 1e-5, "f'(x) = 1.2 + log(19) + 19 * 1 / 19");
855
1
  ccv_nnc_tensor_variable_free(graph, x);
856
1
  x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
857
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
858
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(f), 0, 0);
859
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, f), TENSOR_VARIABLE_LIST(f), 0, 0);
860
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 1.2;
861
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(z), 0, 0);
862
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSUM_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(f, z), TENSOR_VARIABLE_LIST(f), 0, 0);
863
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, f)->data.f32[0], 10 * logf(10) + 1.2 * 10, 1e-5, "f(x) = 1.2 * 10 + 10 * log(10)");
864
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(dx), 0);
865
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dx)->data.f32[0], logf(19) + 1 + 1.2 + logf(10) + 1 + 1.2, 1e-5, "f'(x) = 1.2 + log(19) + 19 * 1 / 19 + 1.2 + log(19) + 19 * 1 / 19");
866
1
  DYNAMIC_GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
867
1
  ccv_nnc_dynamic_graph_free(graph);
868
1
}
869
870
TEST_CASE("dynamic graph to compute f(x) = x * log(x) + y' where y = 1.2 * x and y' is an alias to y")
871
1
{
872
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
873
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
874
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 19;
875
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
876
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(f), 0, 0);
877
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, f), TENSOR_VARIABLE_LIST(f), 0, 0);
878
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
879
1
  ccv_nnc_tensor_from_variable(graph, z)->data.f32[0] = 1.2;
880
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
881
1
  ccv_nnc_tensor_variable_t y_hat = ccv_nnc_tensor_variable_alias_new(graph, y, ccv_nnc_no_ofs, DIM_ALLOC(), CPU_TENSOR_NHWC(32F, 1));
882
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, z), TENSOR_VARIABLE_LIST(y), 0, 0);
883
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSUM_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(f, y_hat), TENSOR_VARIABLE_LIST(f), 0, 0);
884
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, f)->data.f32[0], 19 * logf(19) + 1.2 * 19, 1e-5, "f(x) = 1.2 * 19 + 19 * log(19)");
885
1
  ccv_nnc_tensor_variable_free(graph, y_hat);
886
1
  ccv_nnc_tensor_variable_free(graph, f);
887
1
  ccv_nnc_tensor_variable_free(graph, x);
888
1
  ccv_nnc_tensor_variable_free(graph, z);
889
1
  ccv_nnc_tensor_variable_free(graph, y);
890
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "should have no tensor left");
891
1
  ccv_nnc_dynamic_graph_free(graph);
892
1
}
893
894
TEST_CASE("dynamic graph to compute f(x) = x * log(x) + y' where y'' = 1.2 * x and both y'' and y' are aliases to y")
895
1
{
896
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
897
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
898
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 19;
899
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
900
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(f), 0, 0);
901
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, f), TENSOR_VARIABLE_LIST(f), 0, 0);
902
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
903
1
  ccv_nnc_tensor_from_variable(graph, z)->data.f32[0] = 1.2;
904
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
905
1
  ccv_nnc_tensor_variable_t y_hat = ccv_nnc_tensor_variable_alias_new(graph, y, ccv_nnc_no_ofs, DIM_ALLOC(), CPU_TENSOR_NHWC(32F, 1));
906
1
  ccv_nnc_tensor_variable_t y_double_hat = ccv_nnc_tensor_variable_alias_new(graph, y, ccv_nnc_no_ofs, DIM_ALLOC(), CPU_TENSOR_NHWC(32F, 1));
907
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWPROD_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, z), TENSOR_VARIABLE_LIST(y_double_hat), 0, 0);
908
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSUM_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(f, y_hat), TENSOR_VARIABLE_LIST(f), 0, 0);
909
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, f)->data.f32[0], 19 * logf(19) + 1.2 * 19, 1e-5, "f(x) = 1.2 * 19 + 19 * log(19)");
910
1
  ccv_nnc_tensor_variable_free(graph, y_hat);
911
1
  ccv_nnc_tensor_variable_free(graph, y_double_hat);
912
1
  ccv_nnc_tensor_variable_free(graph, f);
913
1
  ccv_nnc_tensor_variable_free(graph, x);
914
1
  ccv_nnc_tensor_variable_free(graph, z);
915
1
  ccv_nnc_tensor_variable_free(graph, y);
916
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "should have no tensor left");
917
1
  ccv_nnc_dynamic_graph_free(graph);
918
1
}
919
920
TEST_CASE("dynamic graph to compute f(x) = x * y, where y[0] = 10 * z, y[1] = 2 * z, z = [2], x = [10], should free all variables")
921
1
{
922
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
923
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
924
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2));
925
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
926
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(2), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(z), 0, 0);
927
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(10), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(x), 0, 0);
928
1
  ccv_nnc_tensor_variable_t y_1 = ccv_nnc_tensor_variable_alias_new(graph, y, ccv_nnc_no_ofs, DIM_ALLOC(1), CPU_TENSOR_NHWC(32F, 1));
929
1
  ccv_nnc_tensor_variable_t y_2 = ccv_nnc_tensor_variable_alias_new(graph, y, DIM_ALLOC(1), DIM_ALLOC(1), CPU_TENSOR_NHWC(32F, 1));
930
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(10), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(z), TENSOR_VARIABLE_LIST(y_1), 0, 0);
931
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(2), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(z), TENSOR_VARIABLE_LIST(y_2), 0, 0);
932
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
933
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(f), 0, 0);
934
1
  float gt[] = {10 * 2 * 10, 2 * 2 * 10};
935
1
  REQUIRE_ARRAY_EQ_WITH_TOLERANCE(float, ccv_nnc_tensor_from_variable(graph, f)->data.f32, gt, 2, 1e-5, "should be equal");
936
1
  ccv_nnc_tensor_variable_free(graph, z);
937
1
  ccv_nnc_tensor_variable_free(graph, y_1);
938
1
  ccv_nnc_tensor_variable_free(graph, y_2);
939
1
  ccv_nnc_tensor_variable_free(graph, x);
940
1
  ccv_nnc_tensor_variable_free(graph, y);
941
1
  ccv_nnc_tensor_variable_free(graph, f);
942
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
943
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "everything should be freed");
944
1
  ccv_nnc_dynamic_graph_free(graph);
945
1
}
946
947
TEST_CASE("dynamic graph to compute f(x) = x * y, where y[0] = 10 * z, y[1] = 2 * z, z = [2], x = [10], freed y[0], y[1] before use")
948
1
{
949
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
950
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
951
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2));
952
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
953
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(2), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(z), 0, 0);
954
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(10), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(x), 0, 0);
955
1
  ccv_nnc_tensor_variable_t y_1 = ccv_nnc_tensor_variable_alias_new(graph, y, ccv_nnc_no_ofs, DIM_ALLOC(1), CPU_TENSOR_NHWC(32F, 1));
956
1
  ccv_nnc_tensor_variable_t y_2 = ccv_nnc_tensor_variable_alias_new(graph, y, DIM_ALLOC(1), DIM_ALLOC(1), CPU_TENSOR_NHWC(32F, 1));
957
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(10), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(z), TENSOR_VARIABLE_LIST(y_1), 0, 0);
958
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(2), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(z), TENSOR_VARIABLE_LIST(y_2), 0, 0);
959
1
  ccv_nnc_tensor_variable_free(graph, y_1);
960
1
  ccv_nnc_tensor_variable_free(graph, y_2);
961
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
962
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y), TENSOR_VARIABLE_LIST(f), 0, 0);
963
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 3, "should keep 3 ops");
964
1
  float gt[] = {10 * 2 * 10, 2 * 2 * 10};
965
1
  REQUIRE_ARRAY_EQ_WITH_TOLERANCE(float, ccv_nnc_tensor_from_variable(graph, f)->data.f32, gt, 2, 1e-5, "should be equal");
966
1
  ccv_nnc_tensor_variable_free(graph, z);
967
1
  ccv_nnc_tensor_variable_free(graph, x);
968
1
  ccv_nnc_tensor_variable_free(graph, y);
969
1
  ccv_nnc_tensor_variable_free(graph, f);
970
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
971
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "everything should be freed");
972
1
  ccv_nnc_dynamic_graph_free(graph);
973
1
}
974
975
TEST_CASE("dynamic graph to compute f(x) = x * y', where y[0, 0] = 2, y[0, 1] = 4, x = [10], y' = y^T, gradient against y")
976
1
{
977
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
978
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
979
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2));
980
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 2;
981
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 4;
982
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SET_FORWARD(10), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(), TENSOR_VARIABLE_LIST(x), 0, 0);
983
1
  ccv_nnc_tensor_variable_t y_t = ccv_nnc_tensor_variable_alias_new(graph, y, ccv_nnc_no_ofs, DIM_ALLOC(), CPU_TENSOR_NHWC(32F, 2, 1));
984
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
985
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x, y_t), TENSOR_VARIABLE_LIST(f), 0, 0);
986
1
  ccv_nnc_tensor_variable_t dy_t = ccv_nnc_tensor_variable_new(graph);
987
1
  ccv_nnc_tensor_variable_free(graph, x); // Cannot free y_t, it has to be freed in lock step with y.
988
1
  ccv_nnc_tensor_variable_free(graph, y);
989
1
  ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f), 0, TENSOR_VARIABLE_LIST(y_t), TENSOR_VARIABLE_LIST(dy_t), 0);
990
1
  float gt[] = {10, 10};
991
1
  REQUIRE_ARRAY_EQ_WITH_TOLERANCE(float, ccv_nnc_tensor_from_variable(graph, dy_t)->data.f32, gt, 2, 1e-5, "should be equal");
992
1
  ccv_nnc_tensor_variable_free(graph, y_t);
993
1
  ccv_nnc_tensor_variable_free(graph, f);
994
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
995
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "everything should be freed");
996
1
  ccv_nnc_dynamic_graph_free(graph);
997
1
}
998
999
TEST_CASE("long chain to autograd, simulate random free of unused tensors due to garbage collection")
1000
1
{
1001
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1002
1
  ccv_nnc_tensor_variable_t x[10];
1003
1
  ccv_nnc_tensor_variable_t f[10];
1004
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1005
1
  ccv_nnc_tensor_variable_t dy[3];
1006
1
  ccv_nnc_tensor_from_variable(graph, y)->data.f32[0] = 1.3;
1007
1
  int i, j;
1008
11
  for (i = 0; i < 10; 
i++10
)
1009
10
  {
1010
10
    x[i] = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1011
10
    ccv_nnc_tensor_from_variable(graph, x[i])->data.f32[0] = i + 1;
1012
10
    f[i] = ccv_nnc_tensor_variable_new(graph);
1013
10
    ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x[i], y), TENSOR_VARIABLE_LIST(f[i]), 0, 0);
1014
10
    if (i >= 5)
1015
5
    {
1016
5
      if (i - 5 < 3)
1017
3
        dy[i - 5] = ccv_nnc_tensor_variable_new(graph);
1018
5
      ccv_nnc_dynamic_graph_backward(graph, TENSOR_VARIABLE_LIST(f[i]), 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(dy[ccv_min(2, i - 5)]), 0);
1019
5
    }
1020
10
    if (i == 7)
1021
6
      
for (j = 0; 1
j < 5;
j++5
)
1022
5
        ccv_nnc_tensor_variable_free(graph, f[j]);
1023
10
  }
1024
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, f[9])->data.f32[0], 1.3 * 10, 1e-5, "should equal to 1.3 * 10");
1025
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, dy[2])->data.f32[0], 8 + 9 + 10, 1e-5, "should equal to sum of the last 3");
1026
11
  for (i = 0; i < 10; 
i++10
)
1027
10
    ccv_nnc_tensor_variable_free(graph, x[i]);
1028
1
  ccv_nnc_dynamic_graph_free(graph);
1029
1
}
1030
1031
TEST_CASE("parallel exec with a stream, focus on uma tensors")
1032
1
{
1033
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1034
1
  ccv_nnc_stream_context_t* const stream = ccv_nnc_stream_context_new(CCV_STREAM_CONTEXT_CPU);
1035
1
  ccv_nnc_tensor_variable_t x[2];
1036
1
  x[0] = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1037
1
  x[1] = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1038
1
  ccv_nnc_tensor_from_variable(graph, x[0])->data.f32[0] = 10;
1039
1
  ccv_nnc_tensor_from_variable(graph, x[1])->data.f32[0] = 9;
1040
1
  ccv_nnc_tensor_variable_t y[2];
1041
1
  y[0] = ccv_nnc_tensor_variable_new(graph);
1042
1
  y[1] = ccv_nnc_tensor_variable_new(graph);
1043
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(0.4), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x[0], x[1]), TENSOR_VARIABLE_LIST(y[0], y[1]), 2, stream);
1044
1
  ccv_nnc_stream_context_wait(stream);
1045
1
  ccv_nnc_stream_context_free(stream);
1046
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y[0])->data.f32[0], 10 * 0.4, 1e-5, "should match the result");
1047
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y[1])->data.f32[0], 9 * 0.4, 1e-5, "should match the result");
1048
1
  ccv_nnc_dynamic_graph_free(graph);
1049
1
}
1050
1051
TEST_CASE("parallel exec with a stream, focus on potential memory issues")
1052
1
{
1053
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1054
1
  ccv_nnc_stream_context_t* const stream = ccv_nnc_stream_context_new(CCV_STREAM_CONTEXT_CPU);
1055
1
  ccv_nnc_tensor_variable_t x[2];
1056
1
  x[0] = ccv_nnc_tensor_variable_new(graph, CPU_NUMA_TENSOR_NHWC(000, 32F, 1));
1057
1
  x[1] = ccv_nnc_tensor_variable_new(graph, CPU_NUMA_TENSOR_NHWC(000, 32F, 1));
1058
1
  ccv_nnc_tensor_from_variable(graph, x[0])->data.f32[0] = 10;
1059
1
  ccv_nnc_tensor_from_variable(graph, x[1])->data.f32[0] = 9;
1060
1
  ccv_nnc_tensor_variable_t y[2];
1061
1
  y[0] = ccv_nnc_tensor_variable_new(graph);
1062
1
  y[1] = ccv_nnc_tensor_variable_new(graph);
1063
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(0.4), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x[0], x[1]), TENSOR_VARIABLE_LIST(y[0], y[1]), 2, stream);
1064
1
  ccv_nnc_stream_context_wait(stream);
1065
1
  ccv_nnc_stream_context_free(stream);
1066
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y[0])->data.f32[0], 10 * 0.4, 1e-5, "should match the result");
1067
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y[1])->data.f32[0], 9 * 0.4, 1e-5, "should match the result");
1068
1
  ccv_nnc_dynamic_graph_free(graph);
1069
1
}
1070
1071
TEST_CASE("query whether a tensor variable depends on another")
1072
1
{
1073
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1074
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1075
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 10;
1076
1
  ccv_nnc_tensor_variable_t b = ccv_nnc_tensor_variable_new(graph);
1077
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(0.5), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(a), TENSOR_VARIABLE_LIST(b), 0, 0);
1078
1
  ccv_nnc_tensor_variable_t c = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1079
1
  ccv_nnc_tensor_from_variable(graph, c)->data.f32[0] = 9;
1080
1
  ccv_nnc_tensor_variable_t d = ccv_nnc_tensor_variable_new(graph);
1081
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(0.4), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(c), TENSOR_VARIABLE_LIST(d), 0, 0);
1082
1
  ccv_nnc_tensor_variable_t e = ccv_nnc_tensor_variable_new(graph);
1083
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(2), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(d), TENSOR_VARIABLE_LIST(e), 0, 0);
1084
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
1085
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSUM_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(a, c), TENSOR_VARIABLE_LIST(f), 0, 0);
1086
1
  ccv_nnc_tensor_variable_t g = ccv_nnc_tensor_variable_new(graph);
1087
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWSUM_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(e, b), TENSOR_VARIABLE_LIST(g), 0, 0);
1088
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, f)->data.f32[0], 10 + 9, 1e-5, "should match the result");
1089
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, g)->data.f32[0], 10 * 0.5 + 9 * 0.4 * 2, 1e-5, "should match the result");
1090
1
  uint64_t bitmask = 1;
1091
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(a, b, f), TENSOR_VARIABLE_LIST(g), &bitmask);
1092
1
  REQUIRE(bitmask == 3, "a and b should be ancestors to g");
1093
1
  bitmask = 8;
1094
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(a, e, c, f), TENSOR_VARIABLE_LIST(g), &bitmask);
1095
1
  REQUIRE(bitmask == 7, "a, e and c should be ancestors to g");
1096
1
  bitmask = 2;
1097
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(a, e, c), TENSOR_VARIABLE_LIST(f), &bitmask);
1098
1
  REQUIRE(bitmask == 5, "a and c should be ancestors to f");
1099
1
  ccv_nnc_dynamic_graph_free(graph);
1100
1
}
1101
1102
TEST_CASE("compute f(x) = 0.5 * log(x), detach log(x) means 0.5 * y should be freed")
1103
1
{
1104
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1105
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1106
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
1107
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
1108
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
1109
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], log(10), 1e-5, "should match the result");
1110
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
1111
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(0.5), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(z), 0, 0);
1112
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, z)->data.f32[0], 0.5 * log(10), 1e-5, "should match the result");
1113
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 2, "nothing should be freed");
1114
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 3, "nothing should be freed");
1115
1
  uint64_t bitmask = 0;
1116
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(z), &bitmask);
1117
1
  REQUIRE(bitmask == 1, "before detach, it should have effect");
1118
1
  ccv_nnc_tensor_variable_detach(graph, y);
1119
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 1, "0.5 * y should be freed");
1120
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 3, "nothing should be freed");
1121
1
  bitmask = 0;
1122
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(z), &bitmask);
1123
1
  REQUIRE(bitmask == 0, "after detach, it should have no effect");
1124
1
  ccv_nnc_tensor_variable_free(graph, x);
1125
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
1126
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 1, "x and y should be freed");
1127
1
  ccv_nnc_tensor_variable_free(graph, y);
1128
1
  ccv_nnc_tensor_variable_free(graph, z);
1129
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "everything should be freed");
1130
1
  ccv_nnc_dynamic_graph_free(graph);
1131
1
}
1132
1133
TEST_CASE("compute f(x) = 0.5 * log(x), detach log(x) after freeing x means all computations on the graph should be freed")
1134
1
{
1135
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1136
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1137
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
1138
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
1139
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
1140
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], log(10), 1e-5, "should match the result");
1141
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
1142
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(0.5), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(z), 0, 0);
1143
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, z)->data.f32[0], 0.5 * log(10), 1e-5, "should match the result");
1144
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 2, "nothing should be freed");
1145
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 3, "nothing should be freed");
1146
  // free x first before detaching y.
1147
1
  ccv_nnc_tensor_variable_free(graph, x);
1148
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 1, "log(x) should be freed");
1149
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 2, "only x should be freed");
1150
1
  ccv_nnc_tensor_variable_detach(graph, y);
1151
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
1152
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 1, "x and y should be freed");
1153
1
  ccv_nnc_tensor_variable_free(graph, y);
1154
1
  ccv_nnc_tensor_variable_free(graph, z);
1155
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "everything should be freed");
1156
1
  ccv_nnc_dynamic_graph_free(graph);
1157
1
}
1158
1159
TEST_CASE("compute f(x) = a * log(x), detach log(x) means nothing changed")
1160
1
{
1161
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1162
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1163
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
1164
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1165
1
  ccv_nnc_tensor_from_variable(graph, a)->data.f32[0] = 5;
1166
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
1167
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
1168
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], log(10), 1e-5, "should match the result");
1169
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
1170
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(a, y), TENSOR_VARIABLE_LIST(z), 0, 0);
1171
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, z)->data.f32[0], 5 * log(10), 1e-5, "should match the result");
1172
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 2, "nothing should be freed");
1173
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 4, "nothing should be freed");
1174
1
  uint64_t bitmask = 0;
1175
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(z), &bitmask);
1176
1
  REQUIRE(bitmask == 1, "before detach, it should have effect");
1177
1
  ccv_nnc_tensor_variable_detach(graph, y);
1178
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 2, "nothing should be freed");
1179
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 4, "nothing should be freed");
1180
1
  bitmask = 0;
1181
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(z), &bitmask);
1182
1
  REQUIRE(bitmask == 0, "after detach, it should have no effect");
1183
1
  ccv_nnc_tensor_variable_free(graph, a);
1184
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 1, "a * y should be freed");
1185
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 3, "a should be freed");
1186
1
  ccv_nnc_tensor_variable_free(graph, x);
1187
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
1188
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 1, "x and y should be freed");
1189
1
  ccv_nnc_tensor_variable_free(graph, y);
1190
1
  ccv_nnc_tensor_variable_free(graph, z);
1191
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "everything should be freed");
1192
1
  ccv_nnc_dynamic_graph_free(graph);
1193
1
}
1194
1195
TEST_CASE("compute f(x) = exp(x) * log(x), detach log(x) means nothing changed")
1196
1
{
1197
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1198
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1199
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
1200
1
  ccv_nnc_tensor_variable_t a = ccv_nnc_tensor_variable_new(graph);
1201
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWEXP_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(a), 0, 0);
1202
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, a)->data.f32[0], exp(10), 1e-2, "should match the result");
1203
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
1204
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
1205
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], log(10), 1e-5, "should match the result");
1206
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
1207
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_MUL_FORWARD(1), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(a, y), TENSOR_VARIABLE_LIST(z), 0, 0);
1208
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, z)->data.f32[0], exp(10) * log(10), 1e-2, "should match the result");
1209
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 3, "nothing should be freed");
1210
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 4, "nothing should be freed");
1211
1
  uint64_t bitmask = 0;
1212
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(z), &bitmask);
1213
1
  REQUIRE(bitmask == 1, "before detach, it should have effect");
1214
1
  ccv_nnc_tensor_variable_detach(graph, y);
1215
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 3, "nothing should be freed");
1216
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 4, "nothing should be freed");
1217
1
  bitmask = 0;
1218
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(z), &bitmask);
1219
1
  REQUIRE(bitmask == 1, "after detach, it should have still effect (through exp(x))");
1220
1
  ccv_nnc_tensor_variable_detach(graph, a);
1221
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 2, "a * y should be freed");
1222
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 4, "nothing should be freed");
1223
1
  bitmask = 0;
1224
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(z), &bitmask);
1225
1
  REQUIRE(bitmask == 0, "after a freed, it should have no effect");
1226
1
  ccv_nnc_tensor_variable_free(graph, a);
1227
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 3, "a should be freed");
1228
1
  ccv_nnc_tensor_variable_free(graph, x);
1229
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "everything should be freed");
1230
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 1, "x and y should be freed");
1231
1
  ccv_nnc_tensor_variable_free(graph, y);
1232
1
  ccv_nnc_tensor_variable_free(graph, z);
1233
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "everything should be freed");
1234
1
  ccv_nnc_dynamic_graph_free(graph);
1235
1
}
1236
1237
TEST_CASE("compute f(x) = exp(0.5 * log(x)), detach log(x) won't free exp(_)")
1238
1
{
1239
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1240
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 1));
1241
1
  ccv_nnc_tensor_from_variable(graph, x)->data.f32[0] = 10;
1242
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
1243
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWLOG_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
1244
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, y)->data.f32[0], log(10), 1e-5, "should match the result");
1245
1
  ccv_nnc_tensor_variable_t z = ccv_nnc_tensor_variable_new(graph);
1246
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_SCALAR_MUL_FORWARD(0.5), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(y), TENSOR_VARIABLE_LIST(z), 0, 0);
1247
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, z)->data.f32[0], 0.5 * log(10), 1e-5, "should match the result");
1248
1
  ccv_nnc_tensor_variable_t f = ccv_nnc_tensor_variable_new(graph);
1249
1
  ccv_nnc_dynamic_graph_exec(graph, CMD_EWEXP_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_VARIABLE_LIST(z), TENSOR_VARIABLE_LIST(f), 0, 0);
1250
1
  REQUIRE_EQ_WITH_TOLERANCE(ccv_nnc_tensor_from_variable(graph, f)->data.f32[0], exp(0.5 * log(10)), 1e-5, "should match the result");
1251
1
  uint64_t bitmask = 0;
1252
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(f), &bitmask);
1253
1
  REQUIRE(bitmask == 1, "before detach, it should have effect");
1254
1
  ccv_nnc_tensor_variable_detach(graph, y);
1255
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 2, "0.5 * y should be freed");
1256
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 4, "nothing should be freed");
1257
1
  bitmask = 0;
1258
1
  ccv_nnc_dynamic_graph_has_effect_to_tensor_variables(graph, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(f), &bitmask);
1259
1
  REQUIRE(bitmask == 0, "after detach, it should have no effect");
1260
1
  ccv_nnc_tensor_variable_free(graph, f);
1261
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 1, "exp(_) should be freed");
1262
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 3, "f should be freed");
1263
1
  ccv_nnc_tensor_variable_free(graph, z);
1264
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 1, "nothing should change");
1265
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 2, "z should be freed");
1266
1
  ccv_nnc_tensor_variable_free(graph, y);
1267
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_GRAPH_EXEC), 0, "log(x) should be freed");
1268
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 1, "y should be freed");
1269
1
  ccv_nnc_tensor_variable_free(graph, x);
1270
1
  REQUIRE_EQ(ccv_nnc_dynamic_graph_bookkeeping_count(graph, CCV_NNC_SYMBOL_TENSOR), 0, "x should be freed");
1271
1
  ccv_nnc_dynamic_graph_free(graph);
1272
1
}
1273
1274
TEST_CASE("cnnp reshape supports -1 dimension")
1275
1
{
1276
1
  ccv_cnnp_model_t* const reshape = ccv_cnnp_reshape(0, DIM_ALLOC(-1, 4), DIM_ALLOC(), DIM_ALLOC(), 0);
1277
1
  ccv_nnc_dynamic_graph_t* const graph = ccv_nnc_dynamic_graph_new();
1278
1
  ccv_nnc_tensor_variable_t x = ccv_nnc_tensor_variable_new(graph, CPU_TENSOR_NHWC(32F, 2, 3, 4));
1279
1
  int i;
1280
25
  for (i = 0; i < 2 * 3 * 4; 
i++24
)
1281
24
    ccv_nnc_tensor_from_variable(graph, x)->data.f32[i] = i;
1282
1
  const ccv_nnc_tensor_param_t input = CPU_TENSOR_NHWC(32F, 2, 3, 4);
1283
1
  ccv_cnnp_model_compile(reshape, &input, 1, CMD_NOOP(), CMD_NOOP());
1284
1
  ccv_nnc_tensor_variable_t y = ccv_nnc_tensor_variable_new(graph);
1285
1
  ccv_nnc_dynamic_graph_evaluate(graph, reshape, 0, TENSOR_VARIABLE_LIST(x), TENSOR_VARIABLE_LIST(y), 0, 0);
1286
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, y)->info.dim[0], 6, "dim inference");
1287
1
  REQUIRE_EQ(ccv_nnc_tensor_from_variable(graph, y)->info.dim[1], 4, "dim inference");
1288
1
  float btp[24];
1289
25
  for (i = 0; i < 24; 
i++24
)
1290
24
    btp[i] = i;
1291
1
  ccv_nnc_tensor_t bt = ccv_nnc_tensor(btp, CPU_TENSOR_NHWC(32F, 6, 4), 0);
1292
1
  REQUIRE_TENSOR_EQ(ccv_nnc_tensor_from_variable(graph, y), &bt, "reshape correctness");
1293
1
  ccv_nnc_dynamic_graph_free(graph);
1294
1
  ccv_cnnp_model_free(reshape);
1295
1
}
1296
1297
#include "case_main.h"