Coverage Report

Created: 2026-05-04 15:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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, &parameter);
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, &parameter);
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
}