/home/liu/actions-runner/_work/ccv/ccv/lib/nnc/ccv_cnnp_model_core.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "ccv_nnc.h" |
2 | | #include "ccv_nnc_easy.h" |
3 | | #include "ccv_nnc_internal.h" |
4 | | #include "ccv_internal.h" |
5 | | #include "_ccv_cnnp_model.h" |
6 | | #include "3rdparty/khash/khash.h" |
7 | | |
8 | | // MARK - Baisc Layers |
9 | | |
10 | | static const ccv_cnnp_model_vtab_t ccv_cnnp_input_isa; |
11 | | |
12 | 2.75k | #define CCV_CNNP_IS_MODEL_INPUT(x) ((x)->isa == &ccv_cnnp_input_isa) |
13 | | |
14 | 2.85k | #define CCV_CNNP_IS_MODEL_PARAMETER(x) ((x)->param_ref != 0 || (x)->param_sel != 02.85k ) |
15 | | |
16 | | typedef struct { |
17 | | ccv_cnnp_model_t super; |
18 | | int sequence_size; |
19 | | ccv_cnnp_model_t* sequence[1]; |
20 | | } ccv_cnnp_sequential_model_t; |
21 | | |
22 | | static void _ccv_cnnp_sequential_model_deinit(ccv_cnnp_model_t* const super) |
23 | 1.10k | { |
24 | 1.10k | ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super; |
25 | 1.10k | int i, j; |
26 | 3.48k | for (i = 0; i < self->sequence_size; i++2.38k ) |
27 | 2.38k | { |
28 | 2.38k | ccv_cnnp_model_t* const model = self->sequence[i]; |
29 | 2.38k | if (!model) |
30 | 8 | continue; |
31 | 2.37k | ccv_cnnp_model_free(model); |
32 | 4.19k | for (j = i + 1; j < self->sequence_size; j++1.82k ) |
33 | 1.82k | if (self->sequence[j] == model) |
34 | 8 | self->sequence[j] = 0; |
35 | 2.37k | } |
36 | 1.10k | } |
37 | | |
38 | | static void _ccv_cnnp_sequential_model_build(ccv_cnnp_model_t* const super, ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t* const inputs, const int input_size, ccv_nnc_tensor_symbol_t* const outputs, const int output_size) |
39 | 1.09k | { |
40 | 1.09k | ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super; |
41 | 1.09k | PRINT(CCV_CLI_VERBOSE, "[cnnp_sequential_model_build] 1. %p, sequence_size: %d\n", self, self->sequence_size); |
42 | 1.09k | ccv_cnnp_model_t* const sub_model = self->sequence[0]; |
43 | | // Go through each sub model to build the graph. |
44 | 1.09k | ccv_nnc_tensor_symbol_t input; |
45 | 1.09k | sub_model->data = self->super.data; |
46 | 1.09k | ccv_cnnp_model_build(sub_model, graph, inputs, input_size, &input, 1); |
47 | 1.09k | sub_model->data = 0; |
48 | 1.09k | int i; |
49 | 2.33k | for (i = 1; i < self->sequence_size; i++1.23k ) |
50 | 1.23k | { |
51 | 1.23k | ccv_nnc_tensor_symbol_t output; |
52 | 1.23k | ccv_cnnp_model_t* const sub_model = self->sequence[i]; |
53 | | // Go through each sub model to build the graph. |
54 | 1.23k | sub_model->data = self->super.data; |
55 | 1.23k | ccv_cnnp_model_build(sub_model, graph, &input, 1, &output, 1); |
56 | 1.23k | sub_model->data = 0; |
57 | 1.23k | input = output; |
58 | 1.23k | } |
59 | 1.09k | outputs[0] = input; |
60 | 1.09k | PRINT(CCV_CLI_VERBOSE, "[cnnp_sequential_model_build] 2. %p\n", self); |
61 | 1.09k | } |
62 | | |
63 | | static void _ccv_cnnp_sequential_model_init_states(ccv_cnnp_model_t* const super, ccv_nnc_symbolic_graph_t* const graph, const ccv_cnnp_state_initializer_f initializer, void* const context) |
64 | 43 | { |
65 | 43 | ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super; |
66 | 43 | int i; |
67 | 213 | for (i = 0; i < self->sequence_size; i++170 ) |
68 | 170 | ccv_cnnp_model_init_states(self->sequence[i], graph, initializer, context); |
69 | 43 | } |
70 | | |
71 | | static void _ccv_cnnp_sequential_model_set_is_test(ccv_cnnp_model_t* const super, const int is_test, const ccv_cnnp_cmd_updater_f updater, void* const context) |
72 | 67 | { |
73 | 67 | ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super; |
74 | 67 | int i; |
75 | 370 | for (i = 0; i < self->sequence_size; i++303 ) |
76 | 303 | ccv_cnnp_model_set_is_test(self->sequence[i], is_test, updater, context); |
77 | 67 | } |
78 | | |
79 | | static ccv_cnnp_model_t* _ccv_cnnp_sequential_model_copy(const ccv_cnnp_model_t* const super, void* const context); |
80 | | |
81 | | static void _ccv_cnnp_sequential_model_add_to_parameter_indices(ccv_cnnp_model_t* const super, const int index, ccv_array_t* const parameter_indices) |
82 | 2.66k | { |
83 | 2.66k | ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super; |
84 | 2.66k | int i; |
85 | 11.9k | for (i = 0; i < self->sequence_size; i++9.27k ) |
86 | 9.27k | ccv_cnnp_model_add_to_parameter_indices(self->sequence[i], index, parameter_indices); |
87 | 2.66k | } |
88 | | |
89 | | static void _ccv_cnnp_sequential_model_notify(const ccv_cnnp_model_t* const super, const int tag, void* const payload) |
90 | 0 | { |
91 | 0 | ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super; |
92 | 0 | int i; |
93 | 0 | for (i = 0; i < self->sequence_size; i++) |
94 | 0 | ccv_cnnp_model_notify(self->sequence[i], tag, payload); |
95 | 0 | } |
96 | | |
97 | | static const ccv_cnnp_model_vtab_t ccv_cnnp_sequential_model_isa = { |
98 | | .deinit = _ccv_cnnp_sequential_model_deinit, |
99 | | .build = _ccv_cnnp_sequential_model_build, |
100 | | .init_states = _ccv_cnnp_sequential_model_init_states, |
101 | | .copy = _ccv_cnnp_sequential_model_copy, |
102 | | .set_is_test = _ccv_cnnp_sequential_model_set_is_test, |
103 | | .add_to_parameter_indices = _ccv_cnnp_sequential_model_add_to_parameter_indices, |
104 | | .notify = _ccv_cnnp_sequential_model_notify, |
105 | | }; |
106 | | |
107 | | KHASH_MAP_INIT_INT64(model, ccv_cnnp_model_t*) |
108 | | |
109 | | static ccv_cnnp_model_t* _ccv_cnnp_sequential_model_copy(const ccv_cnnp_model_t* const super, void* const context) |
110 | 1.01k | { |
111 | 1.01k | const ccv_cnnp_sequential_model_t* const self = (const ccv_cnnp_sequential_model_t*)super; |
112 | 1.01k | ccv_cnnp_sequential_model_t* const sequential_model = (ccv_cnnp_sequential_model_t*)cccalloc(1, sizeof(ccv_cnnp_sequential_model_t) + sizeof(ccv_cnnp_model_t*) * (self->sequence_size - 1) + sizeof(ccv_nnc_tensor_symbol_t)); |
113 | 1.01k | sequential_model->super.isa = &ccv_cnnp_sequential_model_isa; |
114 | 1.01k | sequential_model->super.input_size = 1; |
115 | 1.01k | sequential_model->super.outputs = (ccv_nnc_tensor_symbol_t*)(sequential_model->sequence + self->sequence_size); |
116 | 1.01k | sequential_model->super.output_size = 1; |
117 | 1.01k | ccv_cnnp_model_copy_name(&sequential_model->super, self->super.name); |
118 | 1.01k | sequential_model->sequence_size = self->sequence_size; |
119 | 1.01k | int i; |
120 | 1.01k | khash_t(model)* model_map = context ? (khash_t(model)*)context10 : kh_init(model); |
121 | 3.06k | for (i = 0; i < self->sequence_size; i++2.04k ) |
122 | 2.04k | { |
123 | 2.04k | ccv_cnnp_model_t* const sub_model = self->sequence[i]; |
124 | 2.04k | int ret; |
125 | 2.04k | khiter_t k = kh_put(model, model_map, (uint64_t)(uintptr_t)sub_model, &ret); |
126 | 2.04k | ccv_cnnp_model_t* model_copy; |
127 | 2.04k | if (ret != 0) |
128 | 2.04k | model_copy = kh_val(model_map, k) = _ccv_cnnp_model_copy(sub_model, model_map); |
129 | 1 | else |
130 | 1 | model_copy = kh_val(model_map, k); |
131 | 2.04k | sequential_model->sequence[i] = model_copy; |
132 | 2.04k | } |
133 | 1.01k | if (!context) |
134 | 1.01k | kh_destroy(model, model_map); |
135 | 1.01k | return (ccv_cnnp_model_t*)sequential_model; |
136 | 1.01k | } |
137 | | |
138 | | ccv_cnnp_model_t* ccv_cnnp_sequential_new(ccv_cnnp_model_t* const* const models, const int model_size, const int is_trainable, const char* const name) |
139 | 97 | { |
140 | 97 | assert(model_size > 0); |
141 | 97 | ccv_cnnp_sequential_model_t* const sequential_model = (ccv_cnnp_sequential_model_t*)cccalloc(1, sizeof(ccv_cnnp_sequential_model_t) + sizeof(ccv_cnnp_model_t*) * (model_size - 1) + sizeof(ccv_nnc_tensor_symbol_t)); |
142 | 97 | sequential_model->super.isa = &ccv_cnnp_sequential_model_isa; |
143 | 97 | sequential_model->super.input_size = models[0]->input_size; |
144 | 97 | sequential_model->super.outputs = (ccv_nnc_tensor_symbol_t*)(sequential_model->sequence + model_size); |
145 | 97 | sequential_model->super.output_size = 1; |
146 | 97 | sequential_model->super.is_trainable = is_trainable; |
147 | 97 | ccv_cnnp_model_copy_name(&sequential_model->super, name); |
148 | 97 | sequential_model->sequence_size = model_size; |
149 | 97 | memcpy(sequential_model->sequence, models, sizeof(ccv_cnnp_model_t*) * model_size); |
150 | 97 | return (ccv_cnnp_model_t*)sequential_model; |
151 | 97 | } |
152 | | |
153 | | typedef struct { |
154 | | ccv_cnnp_model_t super; |
155 | | // The model's outputs, it is different from super.output_size, as latter is for actual tensor symbols. |
156 | | int model_output_size; |
157 | | // The name is similar to sequential model, but it is just topological sorted models. |
158 | | int sequence_size; |
159 | | int* model_outputs; // Which model, as in sequences, have some outputs. |
160 | | ccv_cnnp_model_io_t sequence[1]; |
161 | | } ccv_cnnp_functional_model_t; |
162 | | |
163 | | static void _ccv_cnnp_functional_model_deinit(ccv_cnnp_model_t* const super) |
164 | 100 | { |
165 | 100 | ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super; |
166 | 100 | int i, j = 0, k; |
167 | 836 | for (i = 0; i < self->sequence_size; i++736 ) |
168 | 736 | { |
169 | 736 | ccv_cnnp_model_t* const model = self->sequence[i]->model; |
170 | 736 | if (!model) |
171 | 9 | continue; |
172 | 727 | self->sequence[j++] = (ccv_cnnp_model_io_t)model; |
173 | | // Go through all their IO to remove itself as model. |
174 | 727 | assert(model->io); |
175 | 1.49k | for (k = 0; 727 k < model->io->rnum; k++768 ) |
176 | 768 | { |
177 | 768 | ccv_cnnp_model_io_t model_io = *(ccv_cnnp_model_io_t*)ccv_array_get(model->io, k); |
178 | 768 | model_io->model = 0; |
179 | 768 | } |
180 | 727 | } |
181 | 827 | for (i = 0; 100 i < j; i++727 ) |
182 | 727 | ccv_cnnp_model_free((ccv_cnnp_model_t*)self->sequence[i]); |
183 | 100 | } |
184 | | |
185 | | KHASH_MAP_INIT_INT64(io_node, ccv_array_t*) |
186 | | |
187 | | typedef struct { |
188 | | ccv_array_t* nodes; |
189 | | ccv_nnc_graph_exec_symbol_new_hook_f previous_func; |
190 | | void* previous_context; |
191 | | } ccv_functional_model_build_node_hook_t; |
192 | | |
193 | | static void _ccv_cnnp_functional_model_build_node_new(void* context, const ccv_nnc_graph_exec_symbol_t symbol, const ccv_nnc_cmd_t cmd, const ccv_nnc_tensor_symbol_t* const inputs, const int input_size, const ccv_nnc_tensor_symbol_t* const outputs, const int output_size, const char* const name) |
194 | 5 | { |
195 | 5 | ccv_functional_model_build_node_hook_t* const hook = (ccv_functional_model_build_node_hook_t*)context; |
196 | 5 | ccv_array_push(hook->nodes, &symbol); |
197 | 5 | if (hook->previous_func) |
198 | 0 | hook->previous_func(hook->previous_context, symbol, cmd, inputs, input_size, outputs, output_size, name); |
199 | 5 | } |
200 | | |
201 | | static void _ccv_cnnp_functional_model_build(ccv_cnnp_model_t* const super, ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t* const inputs, const int input_size, ccv_nnc_tensor_symbol_t* const outputs, const int output_size) |
202 | 98 | { |
203 | 98 | ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super; |
204 | 98 | PRINT(CCV_CLI_VERBOSE, "[cnnp_functional_model_build] 1. %p, input_size: %d, output_size: %d\n", self, input_size, output_size); |
205 | 98 | assert(self->super.input_size == input_size); |
206 | 98 | assert(self->super.output_size == output_size); |
207 | 98 | int i, j, k; |
208 | 254 | for (i = 0; i < self->super.input_size; i++156 ) |
209 | 156 | self->sequence[i]->outputs[0] = self->sequence[i]->model->outputs[0] = inputs[i]; // Assigning the output symbol of input layer to be the input symbol. |
210 | 98 | ccv_array_t* input_symbols = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_t), 1, 0); |
211 | 98 | ccv_array_t* parameter_indices = 0; |
212 | 98 | khash_t(io_node)* io_node_map = kh_init(io_node); |
213 | 670 | for (i = self->super.input_size; i < self->sequence_size; i++572 ) |
214 | 572 | { |
215 | 572 | ccv_cnnp_model_t* const sub_model = self->sequence[i]->model; |
216 | 572 | ccv_array_clear(input_symbols); |
217 | 572 | const ccv_array_t* const incomings = self->sequence[i]->incomings; |
218 | 572 | if (incomings) |
219 | 1.28k | for (j = 0; 569 j < incomings->rnum; j++718 ) |
220 | 718 | { |
221 | 718 | const ccv_cnnp_model_io_t input = *(ccv_cnnp_model_io_t*)ccv_array_get(incomings, j); |
222 | 718 | if (CCV_CNNP_IS_MODEL_PARAMETER(input)) |
223 | 2 | { |
224 | 2 | if (!parameter_indices) |
225 | 2 | parameter_indices = ccv_array_new(sizeof(int), 0, 0); |
226 | 0 | else |
227 | 0 | ccv_array_clear(parameter_indices); |
228 | 2 | const int param_sel = input->param_sel > 0 ? input->param_sel - 1 : input->param_sel0 ; |
229 | 2 | assert(input->param_sel != 0); |
230 | 2 | ccv_cnnp_model_add_to_parameter_indices(input->model, param_sel, parameter_indices); |
231 | 2 | assert(parameter_indices->rnum > 0); |
232 | 2 | const int param_ref = input->param_ref > 0 ? input->param_ref - 1 : input->param_ref0 ; |
233 | 2 | assert(input->param_ref != 0); |
234 | 2 | if (param_ref >= 0) |
235 | 2 | { |
236 | 2 | assert(param_ref < parameter_indices->rnum); |
237 | 2 | const ccv_nnc_tensor_symbol_t parameter = ccv_cnnp_parameter_from_indice(super, *(int*)ccv_array_get(parameter_indices, param_ref)); |
238 | 2 | ccv_array_push(input_symbols, ¶meter); |
239 | 2 | } else // Otherwise, all of them. |
240 | 0 | for (k = 0; k < parameter_indices->rnum; k++) |
241 | 0 | { |
242 | 0 | const ccv_nnc_tensor_symbol_t parameter = ccv_cnnp_parameter_from_indice(super, *(int*)ccv_array_get(parameter_indices, k)); |
243 | 0 | ccv_array_push(input_symbols, ¶meter); |
244 | 0 | } |
245 | 716 | } else { |
246 | 1.44k | for (k = 0; k < input->model->output_size; k++724 ) |
247 | 724 | ccv_array_push(input_symbols, &input->outputs[k]); |
248 | 716 | } |
249 | 718 | } |
250 | | // Go through each sub model to build the graph. |
251 | 572 | ccv_array_t* nodes; |
252 | 572 | ccv_functional_model_build_node_hook_t hook; |
253 | 572 | const ccv_array_t* const dependencies = self->sequence[i]->dependencies; |
254 | 572 | if ((dependencies && dependencies->rnum > 02 ) || self->sequence[i]->dependents > 0570 ) |
255 | 5 | { |
256 | 5 | int ret; |
257 | 5 | khiter_t k = kh_put(io_node, io_node_map, (uint64_t)(uintptr_t)self->sequence[i], &ret); |
258 | 5 | if (ret != 0) |
259 | 5 | nodes = kh_val(io_node_map, k) = ccv_array_new(sizeof(ccv_nnc_graph_exec_symbol_t), 1, 0); |
260 | 0 | else |
261 | 0 | nodes = kh_val(io_node_map, k); |
262 | 5 | hook.nodes = nodes; |
263 | 5 | hook.previous_context = ccv_nnc_graph_exec_symbol_new_hook(graph, _ccv_cnnp_functional_model_build_node_new, &hook, &hook.previous_func); |
264 | 5 | } |
265 | 572 | sub_model->data = self->super.data; |
266 | 572 | ccv_cnnp_model_build(sub_model, graph, (ccv_nnc_tensor_symbol_t*)ccv_array_get(input_symbols, 0), input_symbols->rnum, self->sequence[i]->outputs, sub_model->output_size); |
267 | 572 | if ((dependencies && dependencies->rnum > 02 ) || self->sequence[i]->dependents > 0570 ) |
268 | 5 | { |
269 | 5 | ccv_nnc_graph_exec_symbol_new_hook(graph, hook.previous_func, hook.previous_context, 0); |
270 | 5 | if (dependencies) |
271 | 5 | for (j = 0; 2 j < dependencies->rnum; j++3 ) |
272 | 3 | { |
273 | 3 | const ccv_cnnp_model_io_t dependency = *(ccv_cnnp_model_io_t*)ccv_array_get(dependencies, j); |
274 | 3 | khiter_t k = kh_get(io_node, io_node_map, (uint64_t)(uintptr_t)dependency); |
275 | 3 | if (k == kh_end(io_node_map)) |
276 | 0 | continue; |
277 | 3 | const ccv_array_t* const dependency_nodes = kh_val(io_node_map, k); |
278 | 3 | int x, y; |
279 | 6 | for (y = 0; y < dependency_nodes->rnum; y++3 ) |
280 | 6 | for (x = 0; 3 x < nodes->rnum; x++3 ) |
281 | 3 | ccv_nnc_graph_exec_symbol_concat(graph, *(ccv_nnc_graph_exec_symbol_t*)ccv_array_get(dependency_nodes, y), *(ccv_nnc_graph_exec_symbol_t*)ccv_array_get(nodes, x)); |
282 | 3 | } |
283 | 5 | } |
284 | 572 | sub_model->data = 0; |
285 | 572 | } |
286 | 98 | khiter_t it; |
287 | 106 | for (it = kh_begin98 (io_node_map); it != kh_end(io_node_map); ++it8 ) |
288 | 8 | { |
289 | 8 | if (!kh_exist(io_node_map, it)) |
290 | 3 | continue; |
291 | 5 | ccv_array_t* const nodes = kh_val(io_node_map, it); |
292 | 5 | ccv_array_free(nodes); |
293 | 5 | } |
294 | 98 | kh_destroy(io_node, io_node_map); |
295 | 98 | ccv_array_free(input_symbols); |
296 | 98 | if (parameter_indices) |
297 | 2 | ccv_array_free(parameter_indices); |
298 | 210 | for (i = 0, k = 0; k < self->model_output_size; k++112 ) |
299 | 112 | { |
300 | 112 | ccv_cnnp_model_t* const sub_model = self->sequence[self->model_outputs[k]]->model; |
301 | 225 | for (j = 0; j < sub_model->output_size; j++113 ) |
302 | 113 | outputs[i + j] = self->sequence[self->model_outputs[k]]->outputs[j]; |
303 | 112 | i += sub_model->output_size; |
304 | 112 | } |
305 | 98 | assert(i == output_size); |
306 | 98 | PRINT(CCV_CLI_VERBOSE, "[cnnp_functional_model_build] 2. %p\n", self); |
307 | 98 | } |
308 | | |
309 | | static void _ccv_cnnp_functional_model_init_states(ccv_cnnp_model_t* const super, ccv_nnc_symbolic_graph_t* const graph, const ccv_cnnp_state_initializer_f initializer, void* const context) |
310 | 45 | { |
311 | 45 | ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super; |
312 | 45 | int i; |
313 | 355 | for (i = self->super.input_size; i < self->sequence_size; i++310 ) |
314 | 310 | ccv_cnnp_model_init_states(self->sequence[i]->model, graph, initializer, context); |
315 | 45 | } |
316 | | |
317 | | static void _ccv_cnnp_functional_model_set_is_test(ccv_cnnp_model_t* const super, const int is_test, const ccv_cnnp_cmd_updater_f updater, void* const context) |
318 | 109 | { |
319 | 109 | ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super; |
320 | 109 | int i; |
321 | 782 | for (i = self->super.input_size; i < self->sequence_size; i++673 ) |
322 | 673 | ccv_cnnp_model_set_is_test(self->sequence[i]->model, is_test, updater, context); |
323 | 109 | } |
324 | | |
325 | | static void _ccv_cnnp_functional_model_add_to_parameter_indices(ccv_cnnp_model_t* const super, const int index, ccv_array_t* const parameter_indices) |
326 | 928 | { |
327 | 928 | ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super; |
328 | 928 | int i; |
329 | 4.59k | for (i = self->super.input_size; i < self->sequence_size; i++3.66k ) |
330 | 3.66k | ccv_cnnp_model_add_to_parameter_indices(self->sequence[i]->model, index, parameter_indices); |
331 | 928 | } |
332 | | |
333 | | static void _ccv_cnnp_functional_model_notify(const ccv_cnnp_model_t* const super, const int tag, void* const payload) |
334 | 1 | { |
335 | 1 | ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super; |
336 | 1 | int i; |
337 | 14 | for (i = 0; i < self->sequence_size; i++13 ) |
338 | 13 | { |
339 | 13 | const ccv_cnnp_model_t* const model = self->sequence[i]->model; |
340 | 13 | ccv_cnnp_model_notify(model, tag, payload); |
341 | 13 | } |
342 | 1 | } |
343 | | |
344 | | static ccv_cnnp_model_t* _ccv_cnnp_functional_model_copy(const ccv_cnnp_model_t* const super, void* const context); |
345 | | |
346 | | static const ccv_cnnp_model_vtab_t ccv_cnnp_functional_model_isa = { |
347 | | .deinit = _ccv_cnnp_functional_model_deinit, |
348 | | .build = _ccv_cnnp_functional_model_build, |
349 | | .init_states = _ccv_cnnp_functional_model_init_states, |
350 | | .copy = _ccv_cnnp_functional_model_copy, |
351 | | .set_is_test = _ccv_cnnp_functional_model_set_is_test, |
352 | | .add_to_parameter_indices = _ccv_cnnp_functional_model_add_to_parameter_indices, |
353 | | .notify = _ccv_cnnp_functional_model_notify, |
354 | | }; |
355 | | |
356 | | KHASH_MAP_INIT_INT64(model_io, ccv_cnnp_model_io_t) |
357 | | |
358 | | static ccv_cnnp_model_t* _ccv_cnnp_functional_model_copy(const ccv_cnnp_model_t* const super, void* const context) |
359 | 8 | { |
360 | 8 | const ccv_cnnp_functional_model_t* const self = (const ccv_cnnp_functional_model_t*)super; |
361 | 8 | ccv_cnnp_functional_model_t* const functional_model = (ccv_cnnp_functional_model_t*)cccalloc(1, sizeof(ccv_cnnp_functional_model_t) + sizeof(ccv_cnnp_model_t*) * (self->sequence_size - 1) + sizeof(ccv_nnc_tensor_symbol_t) * self->super.output_size + sizeof(int) * self->model_output_size); |
362 | 8 | functional_model->super.isa = &ccv_cnnp_functional_model_isa; |
363 | 8 | functional_model->super.outputs = (ccv_nnc_tensor_symbol_t*)(functional_model->sequence + self->sequence_size); |
364 | 8 | functional_model->super.output_size = self->super.output_size; |
365 | 8 | functional_model->super.input_size = self->super.input_size; |
366 | 8 | ccv_cnnp_model_copy_name(&functional_model->super, self->super.name); |
367 | 8 | functional_model->sequence_size = self->sequence_size; |
368 | 8 | functional_model->model_output_size = self->model_output_size; |
369 | 8 | functional_model->model_outputs = (int*)(functional_model->super.outputs + functional_model->super.output_size); |
370 | 8 | memcpy(functional_model->model_outputs, self->model_outputs, sizeof(int) * self->model_output_size); |
371 | | // Now the difficult part, copy over the model_io. |
372 | 8 | khash_t(model_io)* model_io_map = kh_init(model_io); |
373 | 8 | khash_t(model)* model_map = context ? (khash_t(model)*)context3 : kh_init(model); |
374 | 8 | int i, j; |
375 | 57 | for (i = 0; i < self->sequence_size; i++49 ) |
376 | 49 | { |
377 | 49 | const ccv_cnnp_model_t* const sub_model = self->sequence[i]->model; |
378 | 49 | int ret; |
379 | 49 | khiter_t k = kh_put(model, model_map, (uint64_t)(uintptr_t)sub_model, &ret); |
380 | 49 | ccv_cnnp_model_t* model_copy; |
381 | 49 | if (ret != 0) |
382 | 49 | model_copy = kh_val(model_map, k) = _ccv_cnnp_model_copy(sub_model, model_map); |
383 | 0 | else |
384 | 0 | model_copy = kh_val(model_map, k); |
385 | 49 | ccv_cnnp_model_io_t model_io = functional_model->sequence[i] = ccmalloc(sizeof(struct ccv_cnnp_model_io_s) + sizeof(ccv_nnc_tensor_symbol_t) * sub_model->output_size); |
386 | 49 | model_io->param_ref = 0; |
387 | 49 | model_io->param_sel = 0; |
388 | 49 | model_io->visit = 0; |
389 | 49 | model_io->model = model_copy; |
390 | 49 | model_io->dependencies = 0; |
391 | 49 | model_io->dependents = 0; |
392 | 49 | model_io->incomings = 0; |
393 | 49 | model_io->outgoings = 0; |
394 | 49 | model_io->outputs = (ccv_nnc_tensor_symbol_t*)(model_io + 1); |
395 | 49 | if (!model_copy->io) |
396 | 49 | model_copy->io = ccv_array_new(sizeof(ccv_cnnp_model_io_t), 1, 0); |
397 | 49 | ccv_array_push(model_copy->io, &model_io); |
398 | 49 | k = kh_put(model_io, model_io_map, (uint64_t)(uintptr_t)self->sequence[i], &ret); |
399 | 49 | kh_val(model_io_map, k) = functional_model->sequence[i]; |
400 | 49 | } |
401 | 45 | for (i = self->super.input_size; i < self->sequence_size; i++37 ) |
402 | 37 | { |
403 | 37 | if (self->sequence[i]->incomings) |
404 | 87 | for (j = 0; 37 j < self->sequence[i]->incomings->rnum; j++50 ) |
405 | 50 | { |
406 | 50 | const ccv_cnnp_model_io_t input = *(ccv_cnnp_model_io_t*)ccv_array_get(self->sequence[i]->incomings, j); |
407 | 50 | if (CCV_CNNP_IS_MODEL_PARAMETER(input)) // I am pretty sure this is not in the model_io_map. |
408 | 1 | { |
409 | 1 | int ret; |
410 | 1 | khiter_t k = kh_put(model_io, model_io_map, (uint64_t)(uintptr_t)input, &ret); |
411 | 1 | if (ret != 0) |
412 | 1 | { |
413 | | // The model may not exist on the map due to wrapping (it is inside another sequential or functional model). |
414 | 1 | khiter_t m = kh_get(model, model_map, (uint64_t)(uintptr_t)input->model); |
415 | 1 | assert(m != kh_end(model_map)); |
416 | 1 | ccv_cnnp_model_t* const model_copy = kh_val(model_map, m); |
417 | 1 | ccv_cnnp_model_io_t model_io = ccmalloc(sizeof(struct ccv_cnnp_model_io_s)); |
418 | 1 | model_io->param_ref = input->param_ref; |
419 | 1 | model_io->param_sel = input->param_sel; |
420 | 1 | model_io->visit = 0; |
421 | 1 | model_io->model = model_copy; |
422 | 1 | model_io->incomings = 0; |
423 | 1 | model_io->dependencies = 0; |
424 | 1 | model_io->dependents = 0; |
425 | 1 | model_io->outgoings = 0; |
426 | 1 | model_io->outputs = 0; |
427 | 1 | if (!model_copy->io) |
428 | 1 | model_copy->io = ccv_array_new(sizeof(ccv_cnnp_model_io_t), 1, 0); |
429 | 1 | ccv_array_push(model_copy->io, &model_io); |
430 | 1 | kh_val(model_io_map, k) = model_io; |
431 | 1 | if (input->outgoings) |
432 | 1 | { |
433 | 1 | model_io->outgoings = ccv_array_new(sizeof(ccv_cnnp_model_io_t), input->outgoings->rnum, 0); |
434 | 1 | int x; |
435 | 2 | for (x = 0; x < input->outgoings->rnum; x++1 ) |
436 | 1 | { |
437 | 1 | khiter_t k = kh_get(model_io, model_io_map, (uint64_t)(uintptr_t)(*(ccv_cnnp_model_io_t*)ccv_array_get(input->outgoings, x))); |
438 | 1 | assert(k != kh_end(model_io_map)); |
439 | 1 | ccv_cnnp_model_io_t outgoing_io = kh_val(model_io_map, k); |
440 | 1 | ccv_array_push(model_io->outgoings, &outgoing_io); |
441 | 1 | } |
442 | 1 | } |
443 | 1 | } |
444 | 1 | } |
445 | 50 | } |
446 | 37 | } |
447 | 8 | if (!context) |
448 | 8 | kh_destroy(model, model_map); |
449 | 57 | for (i = 0; i < self->sequence_size; i++49 ) |
450 | 49 | { |
451 | 49 | const ccv_cnnp_model_io_t model_io = self->sequence[i]; |
452 | 49 | ccv_cnnp_model_io_t model_io_copy = functional_model->sequence[i]; |
453 | 49 | model_io_copy->param_ref = model_io->param_ref; |
454 | 49 | model_io_copy->param_sel = model_io->param_sel; |
455 | 49 | if (model_io->incomings) |
456 | 37 | { |
457 | 37 | model_io_copy->incomings = ccv_array_new(sizeof(ccv_cnnp_model_io_t), model_io->incomings->rnum, 0); |
458 | 87 | for (j = 0; j < model_io->incomings->rnum; j++50 ) |
459 | 50 | { |
460 | 50 | khiter_t k = kh_get(model_io, model_io_map, (uint64_t)(uintptr_t)(*(ccv_cnnp_model_io_t*)ccv_array_get(model_io->incomings, j))); |
461 | 50 | assert(k != kh_end(model_io_map)); |
462 | 50 | ccv_cnnp_model_io_t input_io = kh_val(model_io_map, k); |
463 | 50 | ccv_array_push(model_io_copy->incomings, &input_io); |
464 | 50 | } |
465 | 37 | } |
466 | 49 | if (model_io->dependencies) |
467 | 0 | { |
468 | 0 | model_io_copy->dependencies = ccv_array_new(sizeof(ccv_cnnp_model_io_t), model_io->dependencies->rnum, 0); |
469 | 0 | for (j = 0; j < model_io->dependencies->rnum; j++) |
470 | 0 | { |
471 | 0 | khiter_t k = kh_get(model_io, model_io_map, (uint64_t)(uintptr_t)(*(ccv_cnnp_model_io_t*)ccv_array_get(model_io->dependencies, j))); |
472 | 0 | assert(k != kh_end(model_io_map)); |
473 | 0 | ccv_cnnp_model_io_t input_io = kh_val(model_io_map, k); |
474 | 0 | ccv_array_push(model_io_copy->dependencies, &input_io); |
475 | 0 | } |
476 | 0 | } |
477 | 49 | model_io_copy->dependents = model_io->dependents; |
478 | 49 | if (model_io->outgoings) |
479 | 41 | { |
480 | 41 | model_io_copy->outgoings = ccv_array_new(sizeof(ccv_cnnp_model_io_t), model_io->outgoings->rnum, 0); |
481 | 90 | for (j = 0; j < model_io->outgoings->rnum; j++49 ) |
482 | 49 | { |
483 | 49 | khiter_t k = kh_get(model_io, model_io_map, (uint64_t)(uintptr_t)(*(ccv_cnnp_model_io_t*)ccv_array_get(model_io->outgoings, j))); |
484 | 49 | assert(k != kh_end(model_io_map)); |
485 | 49 | ccv_cnnp_model_io_t outgoing_io = kh_val(model_io_map, k); |
486 | 49 | ccv_array_push(model_io_copy->outgoings, &outgoing_io); |
487 | 49 | } |
488 | 41 | } |
489 | 49 | } |
490 | 8 | kh_destroy(model_io, model_io_map); |
491 | 8 | return (ccv_cnnp_model_t*)functional_model; |
492 | 8 | } |
493 | | |
494 | | ccv_cnnp_model_t* ccv_cnnp_model_new(const ccv_cnnp_model_io_t* const inputs, const int input_size, const ccv_cnnp_model_io_t* const outputs, const int output_size, const int is_trainable, const char* const name) |
495 | 92 | { |
496 | 92 | assert(output_size > 0); |
497 | | // Do topological sort. |
498 | 92 | ccv_array_t* const reverse_top = ccv_array_new(sizeof(ccv_cnnp_model_io_t), output_size, 0); |
499 | 92 | int i, j, k; |
500 | | // Go through output one by one, reverse traversal them, to detect potential overlap (overlap means, for example, |
501 | | // outputs[1] is an incoming node for outputs[0]. Thus, if we reverse them, we may have outputs[0] build before outputs[1], |
502 | | // hence, having issues. |
503 | 198 | for (i = 0; i < output_size; i++106 ) |
504 | 106 | outputs[i]->visit = 2; |
505 | 198 | for (i = output_size - 1; i >= 0; i--106 ) |
506 | 106 | { |
507 | 106 | if (outputs[i]->visit == 3) // If we need to remove it, no need to visit. |
508 | 5 | continue; |
509 | 101 | assert(outputs[i]->visit == 2); |
510 | 101 | ccv_array_clear(reverse_top); |
511 | 101 | ccv_array_push(reverse_top, &outputs[i]); |
512 | 681 | for (j = 0; j < reverse_top->rnum; j++580 ) |
513 | 580 | { |
514 | 580 | const ccv_cnnp_model_io_t output = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, j); |
515 | 580 | assert(!CCV_CNNP_IS_MODEL_INPUT(output->model)); |
516 | | // If it is input, push it here. |
517 | 580 | if (output->incomings && !577 CCV_CNNP_IS_MODEL_PARAMETER577 (output)) |
518 | 1.29k | for (k = 0; 577 k < output->incomings->rnum; k++715 ) |
519 | 715 | { |
520 | 715 | const ccv_cnnp_model_io_t input = *(ccv_cnnp_model_io_t*)ccv_array_get(output->incomings, k); |
521 | | // If it is an input or parameter, skip. |
522 | 715 | if (CCV_CNNP_IS_MODEL_INPUT(input->model) || CCV_CNNP_IS_MODEL_PARAMETER526 (input)) |
523 | 190 | continue; |
524 | 525 | if (input->visit == 1 || input->visit == 3477 ) // Visited, skip. |
525 | 48 | continue; |
526 | | // If this is an output, we need to remove it from the output array. Otherwise mark it as visited. |
527 | 477 | input->visit = input->visit == 2 ? 35 : 1472 ; |
528 | 477 | ccv_array_push(reverse_top, &input); |
529 | 477 | } |
530 | | // Similar for dependencies. |
531 | 580 | if (output->dependencies && !2 CCV_CNNP_IS_MODEL_PARAMETER2 (output)) |
532 | 5 | for (k = 0; 2 k < output->dependencies->rnum; k++3 ) |
533 | 3 | { |
534 | 3 | const ccv_cnnp_model_io_t dependency = *(ccv_cnnp_model_io_t*)ccv_array_get(output->dependencies, k); |
535 | | // If it is an input or parameter, skip. |
536 | 3 | if (CCV_CNNP_IS_MODEL_INPUT(dependency->model) || CCV_CNNP_IS_MODEL_PARAMETER(dependency)) |
537 | 0 | continue; |
538 | 3 | if (dependency->visit == 1 || dependency->visit == 3) // Visited, skip. |
539 | 1 | continue; |
540 | | // If this is an output, we need to remove it from the output array. Otherwise mark it as visited. |
541 | 2 | dependency->visit = dependency->visit == 2 ? 30 : 1; |
542 | 2 | ccv_array_push(reverse_top, &dependency); |
543 | 2 | } |
544 | 580 | } |
545 | 580 | for (j = 1; 101 j < reverse_top->rnum; j++479 ) |
546 | 479 | { |
547 | 479 | const ccv_cnnp_model_io_t output = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, j); |
548 | 479 | if (output->visit == 1) // Clean the visit back. |
549 | 474 | output->visit = 0; |
550 | 479 | } |
551 | 101 | } |
552 | 92 | ccv_array_clear(reverse_top); |
553 | 198 | for (i = 0; i < output_size; i++106 ) // We will assign sequence in reverse order, thus, reverse the reverse top when copying the outputs. |
554 | 106 | { |
555 | 106 | if (outputs[output_size - 1 - i]->visit == 2) |
556 | 101 | ccv_array_push(reverse_top, &outputs[output_size - 1 - i]); |
557 | 106 | assert(outputs[output_size - 1 - i]->visit == 2 || outputs[output_size - 1 - i]->visit == 3); |
558 | 106 | outputs[output_size - 1 - i]->visit = 0; // Clean up all visits. |
559 | 106 | } |
560 | | // Go from the output, until we meet inputs. |
561 | 92 | uint64_t input_bitmask[((input_size - 1) >> 6) + 1]; |
562 | 92 | memset(input_bitmask, 0, sizeof(uint64_t) * (((input_size - 1) >> 6) + 1)); |
563 | 92 | int tensor_output_size = 0; // io can be mapped to multiple tensor outputs, therefore, need to compute the exact tensor output size. |
564 | 198 | for (i = 0; i < output_size; i++106 ) |
565 | 106 | tensor_output_size += outputs[i]->model->output_size; |
566 | 633 | for (i = 0; i < reverse_top->rnum; i++541 ) |
567 | 541 | { |
568 | 541 | const ccv_cnnp_model_io_t output = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, i); |
569 | 541 | assert(!CCV_CNNP_IS_MODEL_INPUT(output->model)); |
570 | | // If it is input, push it here. |
571 | 541 | if (output->incomings && !538 CCV_CNNP_IS_MODEL_PARAMETER538 (output)) |
572 | 1.21k | for (j = 0; 538 j < output->incomings->rnum; j++673 ) |
573 | 673 | { |
574 | 673 | const ccv_cnnp_model_io_t input = *(ccv_cnnp_model_io_t*)ccv_array_get(output->incomings, j); |
575 | 673 | ++input->visit; // Mark it as visited. |
576 | 673 | if (input->visit != input->outgoings->rnum + input->dependents) // Not all dependencies visited. |
577 | 89 | continue; |
578 | 584 | if (!CCV_CNNP_IS_MODEL_INPUT(input->model) && !438 CCV_CNNP_IS_MODEL_PARAMETER438 (input)) |
579 | 437 | ccv_array_push(reverse_top, &input); |
580 | 147 | else if (CCV_CNNP_IS_MODEL_INPUT(input->model)) { |
581 | 216 | for (k = 0; k < input_size; k++70 ) |
582 | 216 | if (input == inputs[k]) |
583 | 146 | break; |
584 | 146 | assert(k < input_size); |
585 | 146 | input_bitmask[k >> 6] |= ((uint64_t)1 << (k & 63)); |
586 | 146 | } |
587 | 584 | } |
588 | 541 | if (output->dependencies && !2 CCV_CNNP_IS_MODEL_PARAMETER2 (output)) |
589 | 5 | for (j = 0; 2 j < output->dependencies->rnum; j++3 ) |
590 | 3 | { |
591 | 3 | const ccv_cnnp_model_io_t dependency = *(ccv_cnnp_model_io_t*)ccv_array_get(output->dependencies, j); |
592 | 3 | ++dependency->visit; // Mark it as visited. |
593 | 3 | if (dependency->visit != (dependency->outgoings ? dependency->outgoings->rnum1 : 02 ) + dependency->dependents) // Not all dependencies visited. |
594 | 0 | continue; |
595 | 3 | if (!CCV_CNNP_IS_MODEL_INPUT(dependency->model) && !CCV_CNNP_IS_MODEL_PARAMETER(dependency)) |
596 | 3 | ccv_array_push(reverse_top, &dependency); |
597 | 0 | else if (CCV_CNNP_IS_MODEL_INPUT(dependency->model)) { |
598 | 0 | for (k = 0; k < input_size; k++) |
599 | 0 | if (dependency == inputs[k]) |
600 | 0 | break; |
601 | 0 | assert(k < input_size); |
602 | 0 | input_bitmask[k >> 6] |= ((uint64_t)1 << (k & 63)); |
603 | 0 | } |
604 | 3 | } |
605 | 541 | } |
606 | 633 | for (i = 0; 92 i < reverse_top->rnum; i++541 ) |
607 | 541 | { |
608 | 541 | const ccv_cnnp_model_io_t output = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, i); |
609 | 541 | output->visit = 0; // Clean the visit back. |
610 | 541 | } |
611 | 238 | for (i = 0; i < input_size; i++146 ) |
612 | 146 | inputs[i]->visit = 0; // Clean the visit back. |
613 | 238 | for (i = 0; i < input_size; i++146 ) |
614 | 146 | { assert((input_bitmask[i >> 6] & ((uint64_t)1 << (i & 63)))); } // Assuming they all match. |
615 | 92 | const int sequence_size = reverse_top->rnum + input_size; |
616 | 92 | ccv_cnnp_functional_model_t* const functional_model = (ccv_cnnp_functional_model_t*)cccalloc(1, sizeof(ccv_cnnp_functional_model_t) + sizeof(ccv_cnnp_model_t*) * (sequence_size - 1) + sizeof(ccv_nnc_tensor_symbol_t) * tensor_output_size + sizeof(int) * output_size); |
617 | 92 | functional_model->super.isa = &ccv_cnnp_functional_model_isa; |
618 | 92 | functional_model->super.outputs = (ccv_nnc_tensor_symbol_t*)(functional_model->sequence + sequence_size); |
619 | 92 | functional_model->super.output_size = tensor_output_size; |
620 | 92 | functional_model->super.input_size = input_size; |
621 | 92 | functional_model->super.is_trainable = is_trainable; |
622 | 92 | functional_model->model_output_size = output_size; |
623 | 92 | functional_model->model_outputs = (int*)(functional_model->super.outputs + tensor_output_size); |
624 | 92 | ccv_cnnp_model_copy_name(&functional_model->super, name); |
625 | 92 | functional_model->sequence_size = sequence_size; |
626 | 92 | memcpy(functional_model->sequence, inputs, sizeof(ccv_cnnp_model_io_t) * input_size); |
627 | 633 | for (i = 0; i < reverse_top->rnum; i++541 ) |
628 | 541 | functional_model->sequence[input_size + i] = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, reverse_top->rnum - 1 - i); |
629 | 198 | for (i = 0; i < output_size; i++106 ) |
630 | 106 | { |
631 | 128 | for (j = sequence_size - 1; j >= input_size; j--22 ) |
632 | 128 | if (functional_model->sequence[j] == outputs[i]) |
633 | 106 | { |
634 | 106 | functional_model->model_outputs[i] = j; |
635 | 106 | break; |
636 | 106 | } |
637 | 106 | } |
638 | 92 | ccv_array_free(reverse_top); |
639 | 92 | return (ccv_cnnp_model_t*)functional_model; |
640 | 92 | } |
641 | | |
642 | | static ccv_cnnp_model_t* _ccv_cnnp_input_copy(const ccv_cnnp_model_t* const self, void* const context) |
643 | 12 | { |
644 | 12 | ccv_cnnp_model_t* const input = (ccv_cnnp_model_t*)cccalloc(1, sizeof(ccv_cnnp_model_t) + sizeof(ccv_nnc_tensor_symbol_t)); |
645 | 12 | input->isa = &ccv_cnnp_input_isa; |
646 | 12 | input->outputs = (ccv_nnc_tensor_symbol_t*)(input + 1); |
647 | 12 | input->output_size = 1; |
648 | 12 | return input; |
649 | 12 | } |
650 | | |
651 | | static const ccv_cnnp_model_vtab_t ccv_cnnp_input_isa = { |
652 | | .copy = _ccv_cnnp_input_copy, |
653 | | }; |
654 | | |
655 | | ccv_cnnp_model_io_t ccv_cnnp_input(void) |
656 | 146 | { |
657 | 146 | ccv_cnnp_model_t* const input = (ccv_cnnp_model_t*)cccalloc(1, sizeof(ccv_cnnp_model_t) + sizeof(ccv_nnc_tensor_symbol_t)); |
658 | 146 | input->isa = &ccv_cnnp_input_isa; |
659 | 146 | input->io = ccv_array_new(sizeof(ccv_cnnp_model_io_t), 1, 0); |
660 | 146 | ccv_cnnp_model_io_t input_io = ccmalloc(sizeof(struct ccv_cnnp_model_io_s) + sizeof(ccv_nnc_tensor_symbol_t)); |
661 | 146 | input_io->param_ref = 0; |
662 | 146 | input_io->param_sel = 0; |
663 | 146 | input_io->visit = 0; |
664 | 146 | input_io->incomings = 0; |
665 | 146 | input_io->dependencies = 0; |
666 | 146 | input_io->dependents = 0; |
667 | 146 | input_io->outgoings = 0; |
668 | 146 | input_io->model = input; |
669 | 146 | input_io->outputs = (ccv_nnc_tensor_symbol_t*)(input_io + 1); |
670 | 146 | ccv_array_push(input->io, &input_io); |
671 | 146 | input->outputs = (ccv_nnc_tensor_symbol_t*)(input + 1); |
672 | 146 | input->output_size = 1; |
673 | 146 | return input_io; |
674 | 146 | } |
675 | | |
676 | | // MARK - Dynamic Layer |
677 | | |
678 | | typedef struct { |
679 | | ccv_cnnp_model_t super; |
680 | | ccv_cnnp_model_dynamic_f func; |
681 | | void* context; |
682 | | ccv_cnnp_model_t* model; |
683 | | } ccv_cnnp_dynamic_model_t; |
684 | | |
685 | | static void _ccv_cnnp_dynamic_model_deinit(ccv_cnnp_model_t* const super) |
686 | 4 | { |
687 | 4 | ccv_cnnp_dynamic_model_t* const self = (ccv_cnnp_dynamic_model_t*)super; |
688 | 4 | if (self->model) |
689 | 4 | ccv_cnnp_model_free(self->model); |
690 | 4 | } |
691 | | |
692 | | static void _ccv_cnnp_dynamic_model_build(ccv_cnnp_model_t* const super, ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t* const inputs, const int input_size, ccv_nnc_tensor_symbol_t* const outputs, const int output_size) |
693 | 4 | { |
694 | 4 | ccv_cnnp_dynamic_model_t* const self = (ccv_cnnp_dynamic_model_t*)super; |
695 | 4 | PRINT(CCV_CLI_VERBOSE, "[cnnp_dynamic_model_build] 1. %p, func: %p\n", self, self->func); |
696 | 4 | if (!self->model) |
697 | 4 | { |
698 | 4 | ccv_nnc_tensor_param_t input_params[input_size]; |
699 | 4 | int i; |
700 | 14 | for (i = 0; i < input_size; i++10 ) |
701 | 10 | input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]); |
702 | 4 | self->model = self->func(input_params, input_size, self->context); |
703 | | // Update to use the settings of the compiled model. |
704 | 4 | self->super.input_size = self->model->input_size; |
705 | 4 | self->super.outputs = self->model->outputs; |
706 | 4 | self->super.output_size = self->model->output_size; |
707 | 4 | } |
708 | 4 | self->model->data = self->super.data; |
709 | 4 | ccv_cnnp_model_build(self->model, graph, inputs, input_size, outputs, output_size); |
710 | 4 | self->model->data = 0; |
711 | 4 | PRINT(CCV_CLI_VERBOSE, "[cnnp_dynamic_model_build] 2. %p\n", self); |
712 | 4 | } |
713 | | |
714 | | static void _ccv_cnnp_dynamic_model_init_states(ccv_cnnp_model_t* const super, ccv_nnc_symbolic_graph_t* const graph, const ccv_cnnp_state_initializer_f initializer, void* const context) |
715 | 3 | { |
716 | 3 | ccv_cnnp_dynamic_model_t* const self = (ccv_cnnp_dynamic_model_t*)super; |
717 | 3 | assert(self->model); |
718 | 3 | ccv_cnnp_model_init_states(self->model, graph, initializer, context); |
719 | 3 | } |
720 | | |
721 | | static void _ccv_cnnp_dynamic_model_set_is_test(ccv_cnnp_model_t* const super, const int is_test, const ccv_cnnp_cmd_updater_f updater, void* const context) |
722 | 6 | { |
723 | 6 | ccv_cnnp_dynamic_model_t* const self = (ccv_cnnp_dynamic_model_t*)super; |
724 | 6 | assert(self->model); |
725 | 6 | ccv_cnnp_model_set_is_test(self->model, is_test, updater, context); |
726 | 6 | } |
727 | | |
728 | | static ccv_cnnp_model_t* _ccv_cnnp_dynamic_model_copy(const ccv_cnnp_model_t* const super, void* const context); |
729 | | |
730 | | static void _ccv_cnnp_dynamic_model_add_to_parameter_indices(ccv_cnnp_model_t* const super, const int index, ccv_array_t* const parameter_indices) |
731 | 0 | { |
732 | 0 | ccv_cnnp_dynamic_model_t* const self = (ccv_cnnp_dynamic_model_t*)super; |
733 | 0 | assert(self->model); |
734 | 0 | ccv_cnnp_model_add_to_parameter_indices(self->model, index, parameter_indices); |
735 | 0 | } |
736 | | |
737 | | static void _ccv_cnnp_dynamic_model_notify(const ccv_cnnp_model_t* const super, const int tag, void* const payload) |
738 | 0 | { |
739 | 0 | ccv_cnnp_dynamic_model_t* const self = (ccv_cnnp_dynamic_model_t*)super; |
740 | 0 | if (self->model) |
741 | 0 | ccv_cnnp_model_notify(self->model, tag, payload); |
742 | 0 | } |
743 | | |
744 | | static const ccv_cnnp_model_vtab_t ccv_cnnp_dynamic_model_isa = { |
745 | | .deinit = _ccv_cnnp_dynamic_model_deinit, |
746 | | .build = _ccv_cnnp_dynamic_model_build, |
747 | | .init_states = _ccv_cnnp_dynamic_model_init_states, |
748 | | .copy = _ccv_cnnp_dynamic_model_copy, |
749 | | .set_is_test = _ccv_cnnp_dynamic_model_set_is_test, |
750 | | .add_to_parameter_indices = _ccv_cnnp_dynamic_model_add_to_parameter_indices, |
751 | | .notify = _ccv_cnnp_dynamic_model_notify, |
752 | | }; |
753 | | |
754 | | ccv_cnnp_model_t* ccv_cnnp_dynamic_new(ccv_cnnp_model_dynamic_f func, void* const context, const char* const name) |
755 | 4 | { |
756 | 4 | ccv_cnnp_dynamic_model_t* const dynamic_model = (ccv_cnnp_dynamic_model_t*)cccalloc(1, sizeof(ccv_cnnp_dynamic_model_t)); |
757 | 4 | dynamic_model->super.isa = &ccv_cnnp_dynamic_model_isa; |
758 | 4 | dynamic_model->super.is_trainable = -1; |
759 | 4 | dynamic_model->func = func; |
760 | 4 | dynamic_model->context = context; |
761 | 4 | ccv_cnnp_model_copy_name(&dynamic_model->super, name); |
762 | 4 | return (ccv_cnnp_model_t*)dynamic_model; |
763 | 4 | } |
764 | | |
765 | | static ccv_cnnp_model_t* _ccv_cnnp_dynamic_model_copy(const ccv_cnnp_model_t* const super, void* const context) |
766 | 0 | { |
767 | 0 | const ccv_cnnp_dynamic_model_t* const self = (const ccv_cnnp_dynamic_model_t*)super; |
768 | 0 | return ccv_cnnp_dynamic_new(self->func, self->context, self->super.name); |
769 | 0 | } |
770 | | |
771 | | // MARK - Command Layer |
772 | | |
773 | | typedef struct { |
774 | | ccv_cnnp_model_t super; |
775 | | ccv_nnc_cmd_t cmd; |
776 | | ccv_nnc_hint_t hint; |
777 | | ccv_nnc_tensor_symbol_t* input_symbols; // This is only valid for INIT_SHARED_TENSOR / INIT_SHARED_TENSOR_AS_TRAINABLE |
778 | | ccv_nnc_tensor_symbol_t* output_symbols; // This is just for the output symbol (in case we need to have no tensor symbol). |
779 | | ccv_cnnp_cmd_exec_io_t* inputs; |
780 | | int flags; |
781 | | int input_size; |
782 | | int* outputs; |
783 | | int output_size; |
784 | | } ccv_cnnp_model_cmd_exec_t; |
785 | | |
786 | | static void _ccv_cnnp_cmd_exec_build(ccv_cnnp_model_t* const super, ccv_nnc_symbolic_graph_t* const graph, const ccv_nnc_tensor_symbol_t* const inputs, const int input_size, ccv_nnc_tensor_symbol_t* const outputs, const int output_size) |
787 | 81 | { |
788 | 81 | ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super; |
789 | 81 | PRINT(CCV_CLI_VERBOSE, "[cnnp_cmd_exec_build] -\n"); |
790 | 81 | ccv_nnc_tensor_param_t input_params[ccv_max(1, self->input_size)]; |
791 | 81 | int i, j; |
792 | 243 | for (i = 0, j = 0; i < self->input_size; i++162 ) |
793 | 162 | if (self->inputs[i].type == CCV_CNNP_IO) |
794 | 131 | { |
795 | 131 | self->input_symbols[i] = inputs[j++]; |
796 | 131 | input_params[i] = ccv_nnc_tensor_symbol_params(graph, self->input_symbols[i]); |
797 | 131 | } else if (31 self->inputs[i].type == CCV_CNNP_NO_TENSOR31 ) { |
798 | 0 | self->input_symbols[i] = NO_TENSOR_SYMBOL; |
799 | 31 | } else if (!self->input_symbols[i].graph) { |
800 | | // Otherwise, we only create this symbol if it doesn't exist. |
801 | 22 | const ccv_nnc_tensor_param_t params = self->inputs[i].init_state.info; |
802 | 22 | input_params[i] = params; |
803 | 22 | self->input_symbols[i] = ccv_nnc_tensor_symbol_new(graph, params, 0); |
804 | 22 | } |
805 | | // We cannot simply mark the outputs as auto, because the subsequent build call may require this output to have params setup. |
806 | | // Infer the parameters here. |
807 | 81 | ccv_nnc_tensor_param_t output_params[ccv_max(1, self->output_size)]; |
808 | 81 | ccv_nnc_hint_tensor_auto(self->cmd, input_params, self->input_size, self->hint, output_params, self->output_size); |
809 | 162 | for (i = 0, j = 0; i < self->output_size; i++81 ) |
810 | 81 | if (self->outputs[i] == CCV_CNNP_IO) |
811 | 81 | self->output_symbols[i] = outputs[j++] = ccv_nnc_tensor_symbol_new(graph, output_params[i], 0); |
812 | 0 | else if (self->outputs[i] == CCV_CNNP_TENSOR_NOT_OUTPUT) |
813 | 0 | self->output_symbols[i] = ccv_nnc_tensor_symbol_new(graph, output_params[i], 0); |
814 | 0 | else |
815 | 0 | self->output_symbols[i] = NO_TENSOR_SYMBOL; |
816 | 81 | ccv_nnc_graph_exec_symbol_new(graph, self->cmd, self->input_symbols, self->input_size, self->output_symbols, self->output_size, 0); |
817 | 81 | } |
818 | | |
819 | | static void _ccv_cnnp_cmd_exec_init_states(ccv_cnnp_model_t* const super, ccv_nnc_symbolic_graph_t* const graph, const ccv_cnnp_state_initializer_f initializer, void* const context) |
820 | 65 | { |
821 | 65 | ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super; |
822 | 65 | int i; |
823 | 195 | for (i = 0; i < self->input_size; i++130 ) |
824 | 130 | if (self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR || self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR_AS_TRAINABLE110 ) |
825 | 31 | self->inputs[i].init_state.init(self->input_symbols[i], initializer, context, self->inputs[i].init_state.context); |
826 | 65 | } |
827 | | |
828 | | static void _ccv_cnnp_cmd_exec_add_to_output(ccv_cnnp_model_t* const super, const ccv_cnnp_add_to_array_f add_to_array, void* const outputs) |
829 | 81 | { |
830 | 81 | ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super; |
831 | 81 | int i; |
832 | 243 | for (i = 0; i < self->input_size; i++162 ) |
833 | 162 | if (self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR) |
834 | 20 | add_to_array(outputs, self->input_symbols[i], 0); // Push this as retainable because it need to be init. |
835 | 81 | } |
836 | | |
837 | | static void _ccv_cnnp_cmd_exec_add_to_parameter(ccv_cnnp_model_t* const super, const ccv_cnnp_add_to_array_f add_to_array, void* const parameters, const int is_trainable) |
838 | 81 | { |
839 | 81 | ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super; |
840 | 81 | int i; |
841 | 243 | for (i = 0; i < self->input_size; i++162 ) |
842 | 162 | if (self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR_AS_TRAINABLE) |
843 | 11 | add_to_array(parameters, self->input_symbols[i], is_trainable); // Push this as parameter. |
844 | 81 | } |
845 | | |
846 | | static void _ccv_cnnp_cmd_exec_deinit(ccv_cnnp_model_t* const super) |
847 | 72 | { |
848 | 72 | ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super; |
849 | 72 | int i, j; |
850 | 216 | for (i = 0; i < self->input_size; i++144 ) |
851 | 144 | if ((self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR || self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR_AS_TRAINABLE133 ) && |
852 | 144 | self->inputs[i].init_state.context22 ) |
853 | 22 | { |
854 | 22 | void* const context = self->inputs[i].init_state.context; |
855 | 22 | if (self->inputs[i].init_state.deinit) |
856 | 9 | self->inputs[i].init_state.deinit(context); |
857 | 22 | self->inputs[i].init_state.init = 0; |
858 | 22 | self->inputs[i].init_state.deinit = 0; |
859 | 22 | self->inputs[i].init_state.context = 0; |
860 | 22 | for (j = i + 1; j < self->input_size; j++0 ) |
861 | 0 | if (self->inputs[j].init_state.context == context) |
862 | 0 | { |
863 | 0 | self->inputs[j].init_state.init = 0; |
864 | 0 | self->inputs[j].init_state.deinit = 0; |
865 | 0 | self->inputs[j].init_state.context = 0; |
866 | 0 | } |
867 | 22 | } |
868 | 72 | } |
869 | | |
870 | | static ccv_cnnp_model_t* _ccv_cnnp_cmd_exec_copy(const ccv_cnnp_model_t* const super, void* const context); |
871 | | |
872 | | static const ccv_cnnp_model_vtab_t ccv_cnnp_cmd_exec_isa = { |
873 | | .build = _ccv_cnnp_cmd_exec_build, |
874 | | .init_states = _ccv_cnnp_cmd_exec_init_states, |
875 | | .add_to_parameter = _ccv_cnnp_cmd_exec_add_to_parameter, |
876 | | .add_to_output = _ccv_cnnp_cmd_exec_add_to_output, |
877 | | .deinit = _ccv_cnnp_cmd_exec_deinit, |
878 | | .copy = _ccv_cnnp_cmd_exec_copy, |
879 | | }; |
880 | | |
881 | | static ccv_cnnp_model_t* _ccv_cnnp_cmd_exec(const ccv_nnc_cmd_t cmd, int copy_io, const ccv_nnc_hint_t hint, const int flags, const ccv_cnnp_cmd_exec_io_t* const inputs, const int input_size, const int* const outputs, const int output_size, const int is_trainable, const char* const name) |
882 | 72 | { |
883 | 72 | assert(input_size >= 0); |
884 | 72 | assert(output_size > 0); |
885 | 72 | int i; |
886 | 72 | int io_input_size = 0; |
887 | 216 | for (i = 0; i < input_size; i++144 ) |
888 | 144 | if (inputs[i].type == CCV_CNNP_IO) |
889 | 122 | ++io_input_size; |
890 | 22 | else { |
891 | 22 | assert(inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR || inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR_AS_TRAINABLE); |
892 | 22 | assert(inputs[i].init_state.init); |
893 | 22 | } |
894 | 72 | int io_output_size = 0; |
895 | 144 | for (i = 0; i < output_size; i++72 ) |
896 | 72 | if (outputs[i] == CCV_CNNP_IO) |
897 | 72 | ++io_output_size; |
898 | 0 | else { |
899 | 0 | assert(outputs[i] == CCV_CNNP_TENSOR_NOT_OUTPUT || outputs[i] == CCV_CNNP_NO_TENSOR); |
900 | 0 | } |
901 | 72 | assert(io_output_size > 0); |
902 | 72 | ccv_cnnp_model_cmd_exec_t* const model_cmd_exec = (ccv_cnnp_model_cmd_exec_t*)cccalloc(1, sizeof(ccv_cnnp_model_cmd_exec_t) + sizeof(ccv_nnc_tensor_symbol_t) * (io_output_size + input_size + output_size) + sizeof(ccv_cnnp_cmd_exec_io_t) * input_size + sizeof(int) * output_size); |
903 | 72 | model_cmd_exec->super.isa = &ccv_cnnp_cmd_exec_isa; |
904 | 72 | model_cmd_exec->super.input_size = io_input_size; |
905 | 72 | model_cmd_exec->super.outputs = (ccv_nnc_tensor_symbol_t*)(model_cmd_exec + 1); |
906 | 72 | model_cmd_exec->super.output_size = io_output_size; |
907 | 72 | model_cmd_exec->super.is_trainable = is_trainable; |
908 | 72 | ccv_cnnp_model_copy_name(&model_cmd_exec->super, name); |
909 | 72 | model_cmd_exec->cmd = cmd; |
910 | 72 | model_cmd_exec->hint = hint; |
911 | 72 | model_cmd_exec->flags = flags; |
912 | 72 | model_cmd_exec->input_size = input_size; |
913 | 72 | model_cmd_exec->input_symbols = model_cmd_exec->super.outputs + io_output_size; |
914 | 72 | model_cmd_exec->output_symbols = model_cmd_exec->input_symbols + input_size; |
915 | 72 | model_cmd_exec->inputs = (ccv_cnnp_cmd_exec_io_t*)(model_cmd_exec->output_symbols + output_size); |
916 | 72 | if (input_size > 0) |
917 | 72 | { |
918 | 72 | memcpy(model_cmd_exec->inputs, inputs, sizeof(ccv_cnnp_cmd_exec_io_t) * input_size); |
919 | 72 | if (copy_io) |
920 | 30 | for (i = 0; 10 i < input_size; i++20 ) |
921 | 20 | if (inputs[i].type != CCV_CNNP_IO && inputs[i].init_state.copy2 ) |
922 | 1 | model_cmd_exec->inputs[i].init_state.context = inputs[i].init_state.copy(inputs[i].init_state.context); |
923 | 72 | } |
924 | 72 | model_cmd_exec->output_size = output_size; |
925 | 72 | model_cmd_exec->outputs = (int*)(model_cmd_exec->inputs + input_size); |
926 | 72 | if (output_size > 0) |
927 | 72 | memcpy(model_cmd_exec->outputs, outputs, sizeof(int) * output_size); |
928 | 72 | return (ccv_cnnp_model_t*)model_cmd_exec; |
929 | 72 | } |
930 | | |
931 | | ccv_cnnp_model_t* ccv_cnnp_cmd_exec(const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, const ccv_cnnp_cmd_exec_io_t* const inputs, const int input_size, const int* const outputs, const int output_size, const int is_trainable, const char* const name) |
932 | 62 | { |
933 | 62 | return _ccv_cnnp_cmd_exec(cmd, 0, hint, flags, inputs, input_size, outputs, output_size, is_trainable, name); |
934 | 62 | } |
935 | | |
936 | | static ccv_cnnp_model_t* _ccv_cnnp_cmd_exec_copy(const ccv_cnnp_model_t* const super, void* const context) |
937 | 10 | { |
938 | 10 | const ccv_cnnp_model_cmd_exec_t* const self = (const ccv_cnnp_model_cmd_exec_t*)super; |
939 | 10 | return _ccv_cnnp_cmd_exec(self->cmd, 1, self->hint, self->flags, self->inputs, self->input_size, self->outputs, self->output_size, self->super.is_trainable, self->super.name); |
940 | 10 | } |
941 | | |
942 | | static void _ccv_cnnp_cmd_exec_io_copy(const ccv_nnc_tensor_symbol_t tensor_symbol, const ccv_cnnp_state_initializer_f initializer, void* const initializer_context, void* const context) |
943 | 20 | { |
944 | 20 | initializer(initializer_context, CMD_DATA_TRANSFER_FORWARD(), ccv_nnc_no_hint, 0, (ccv_nnc_tensor_t*)context, tensor_symbol); |
945 | 20 | } |
946 | | |
947 | | ccv_cnnp_cmd_exec_io_init_state_t ccv_cnnp_cmd_exec_io_copy(const ccv_nnc_tensor_t* const tensor) |
948 | 12 | { |
949 | 12 | return (ccv_cnnp_cmd_exec_io_init_state_t){ |
950 | 12 | .info = tensor->info, |
951 | 12 | .context = (void *)tensor, |
952 | 12 | .init = _ccv_cnnp_cmd_exec_io_copy, |
953 | 12 | }; |
954 | 12 | } |
955 | | |
956 | | typedef struct { |
957 | | ccv_nnc_cmd_t cmd; |
958 | | ccv_nnc_hint_t hint; |
959 | | int flags; |
960 | | } ccv_cnnp_cmd_exec_io_set_by_t; |
961 | | |
962 | | static void _ccv_cnnp_cmd_exec_io_set_by(const ccv_nnc_tensor_symbol_t tensor_symbol, const ccv_cnnp_state_initializer_f initializer, void* const initializer_context, void* const context) |
963 | 11 | { |
964 | 11 | const ccv_cnnp_cmd_exec_io_set_by_t* const set_by = (ccv_cnnp_cmd_exec_io_set_by_t*)context; |
965 | 11 | initializer(initializer_context, set_by->cmd, set_by->hint, set_by->flags, 0, tensor_symbol); |
966 | 11 | } |
967 | | |
968 | | static void* _ccv_cnnp_cmd_exec_io_set_by_copy(void* const context) |
969 | 1 | { |
970 | 1 | ccv_cnnp_cmd_exec_io_set_by_t* const set_by = (ccv_cnnp_cmd_exec_io_set_by_t*)ccmalloc(sizeof(ccv_cnnp_cmd_exec_io_set_by_t)); |
971 | 1 | memcpy(set_by, context, sizeof(ccv_cnnp_cmd_exec_io_set_by_t)); |
972 | 1 | return set_by; |
973 | 1 | } |
974 | | |
975 | | ccv_cnnp_cmd_exec_io_init_state_t ccv_cnnp_cmd_exec_io_set_by(const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, const ccv_nnc_tensor_param_t params) |
976 | 8 | { |
977 | 8 | ccv_cnnp_cmd_exec_io_set_by_t* const set_by = (ccv_cnnp_cmd_exec_io_set_by_t*)ccmalloc(sizeof(ccv_cnnp_cmd_exec_io_set_by_t)); |
978 | 8 | set_by->cmd = cmd; |
979 | 8 | set_by->hint = hint; |
980 | 8 | set_by->flags = flags; |
981 | 8 | return (ccv_cnnp_cmd_exec_io_init_state_t){ |
982 | 8 | .info = params, |
983 | 8 | .context = set_by, |
984 | 8 | .init = _ccv_cnnp_cmd_exec_io_set_by, |
985 | 8 | .copy = _ccv_cnnp_cmd_exec_io_set_by_copy, |
986 | 8 | .deinit = ccfree, |
987 | 8 | }; |
988 | 8 | } |