Coverage Report

Created: 2025-05-07 17:36

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