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