Coverage Report

Created: 2025-05-05 12:03

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.78k
#define CCV_CNNP_IS_MODEL_INPUT(x) ((x)->isa == &ccv_cnnp_input_isa)
13
14
2.87k
#define CCV_CNNP_IS_MODEL_PARAMETER(x) ((x)->param_ref != 0 || 
(x)->param_sel != 02.87k
)
15
16
typedef struct {
17
  ccv_cnnp_model_t super;
18
  int sequence_size;
19
  ccv_cnnp_model_t* sequence[1];
20
} ccv_cnnp_sequential_model_t;
21
22
static void _ccv_cnnp_sequential_model_deinit(ccv_cnnp_model_t* const super)
23
1.10k
{
24
1.10k
  ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super;
25
1.10k
  int i, j = 0;
26
3.48k
  for (i = 0; i < self->sequence_size; 
i++2.38k
)
27
2.38k
  {
28
2.38k
    ccv_cnnp_model_t* const model = self->sequence[i];
29
2.38k
    if (model->deinit_state)
30
8
      continue;
31
2.37k
    ccv_cnnp_model_deinit(model);
32
2.37k
    self->sequence[j++] = model;
33
2.37k
  }
34
1.10k
  self->sequence_size = j;
35
1.10k
}
36
37
static void _ccv_cnnp_sequential_model_dealloc(ccv_cnnp_model_t* const super)
38
1.10k
{
39
1.10k
  ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super;
40
1.10k
  int i;
41
3.48k
  for (i = 0; i < self->sequence_size; 
i++2.37k
)
42
2.37k
    ccv_cnnp_model_free(self->sequence[i]);
43
1.10k
}
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.09k
{
47
1.09k
  ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super;
48
1.09k
  PRINT(CCV_CLI_VERBOSE, "[cnnp_sequential_model_build] 1. %p, sequence_size: %d\n", self, self->sequence_size);
49
1.09k
  ccv_cnnp_model_t* const sub_model = self->sequence[0];
50
  // Go through each sub model to build the graph.
51
1.09k
  ccv_nnc_tensor_symbol_t input;
52
1.09k
  sub_model->data = self->super.data;
53
1.09k
  ccv_cnnp_model_build(sub_model, graph, inputs, input_size, &input, 1);
54
1.09k
  sub_model->data = 0;
55
1.09k
  int i;
56
2.33k
  for (i = 1; i < self->sequence_size; 
i++1.23k
)
57
1.23k
  {
58
1.23k
    ccv_nnc_tensor_symbol_t output;
59
1.23k
    ccv_cnnp_model_t* const sub_model = self->sequence[i];
60
    // Go through each sub model to build the graph.
61
1.23k
    sub_model->data = self->super.data;
62
1.23k
    ccv_cnnp_model_build(sub_model, graph, &input, 1, &output, 1);
63
1.23k
    sub_model->data = 0;
64
1.23k
    input = output;
65
1.23k
  }
66
1.09k
  outputs[0] = input;
67
1.09k
  PRINT(CCV_CLI_VERBOSE, "[cnnp_sequential_model_build] 2. %p\n", self);
68
1.09k
}
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
43
{
72
43
  ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super;
73
43
  int i;
74
213
  for (i = 0; i < self->sequence_size; 
i++170
)
75
170
    ccv_cnnp_model_init_states(self->sequence[i], graph, initializer, context);
76
43
}
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
67
{
80
67
  ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super;
81
67
  int i;
82
370
  for (i = 0; i < self->sequence_size; 
i++303
)
83
303
    ccv_cnnp_model_set_is_test(self->sequence[i], is_test, updater, context);
84
67
}
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.66k
{
90
2.66k
  ccv_cnnp_sequential_model_t* const self = (ccv_cnnp_sequential_model_t*)super;
91
2.66k
  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.66k
}
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
97
{
148
97
  assert(model_size > 0);
149
97
  ccv_cnnp_sequential_model_t* const sequential_model = (ccv_cnnp_sequential_model_t*)cccalloc(1, sizeof(ccv_cnnp_sequential_model_t) + sizeof(ccv_cnnp_model_t*) * (model_size - 1) + sizeof(ccv_nnc_tensor_symbol_t));
150
97
  sequential_model->super.isa = &ccv_cnnp_sequential_model_isa;
151
97
  sequential_model->super.input_size = models[0]->input_size;
152
97
  sequential_model->super.outputs = (ccv_nnc_tensor_symbol_t*)(sequential_model->sequence + model_size);
153
97
  sequential_model->super.output_size = 1;
154
97
  sequential_model->super.is_trainable = is_trainable;
155
97
  ccv_cnnp_model_copy_name(&sequential_model->super, name);
156
97
  sequential_model->sequence_size = model_size;
157
97
  memcpy(sequential_model->sequence, models, sizeof(ccv_cnnp_model_t*) * model_size);
158
97
  return (ccv_cnnp_model_t*)sequential_model;
159
97
}
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
101
{
173
101
  ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super;
174
101
  int i, j = 0, k;
175
845
  for (i = 0; i < self->sequence_size; 
i++744
)
176
744
  {
177
744
    ccv_cnnp_model_t* const model = self->sequence[i]->model;
178
744
    if (!model || 
model->deinit_state735
)
179
9
      continue;
180
735
    self->sequence[j++] = (ccv_cnnp_model_io_t)model;
181
    // Go through all their IO to remove itself as model.
182
735
    assert(model->io);
183
1.51k
    
for (k = 0; 735
k < model->io->rnum;
k++776
)
184
776
    {
185
776
      ccv_cnnp_model_io_t model_io = *(ccv_cnnp_model_io_t*)ccv_array_get(model->io, k);
186
776
      model_io->model = 0;
187
776
    }
188
735
  }
189
836
  
for (i = 0; 101
i < j;
i++735
)
190
735
    ccv_cnnp_model_deinit((ccv_cnnp_model_t*)self->sequence[i]);
191
101
  self->sequence_size = j;
192
101
}
193
194
static void _ccv_cnnp_functional_model_dealloc(ccv_cnnp_model_t* const super)
195
101
{
196
101
  ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super;
197
101
  int i;
198
836
  for (i = 0; i < self->sequence_size; 
i++735
)
199
735
    ccv_cnnp_model_free((ccv_cnnp_model_t*)self->sequence[i]);
200
101
}
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
99
{
220
99
  ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super;
221
99
  PRINT(CCV_CLI_VERBOSE, "[cnnp_functional_model_build] 1. %p, input_size: %d, output_size: %d\n", self, input_size, output_size);
222
99
  assert(self->super.input_size == input_size);
223
99
  assert(self->super.output_size == output_size);
224
99
  int i, j, k;
225
259
  for (i = 0; i < self->super.input_size; 
i++160
)
226
160
    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
99
  ccv_array_t* input_symbols = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_t), 1, 0);
228
99
  ccv_array_t* parameter_indices = 0;
229
99
  khash_t(io_node)* io_node_map = kh_init(io_node);
230
675
  for (i = self->super.input_size; i < self->sequence_size; 
i++576
)
231
576
  {
232
576
    ccv_cnnp_model_t* const sub_model = self->sequence[i]->model;
233
576
    ccv_array_clear(input_symbols);
234
576
    const ccv_array_t* const incomings = self->sequence[i]->incomings;
235
576
    if (incomings)
236
1.29k
      
for (j = 0; 573
j < incomings->rnum;
j++725
)
237
725
      {
238
725
        const ccv_cnnp_model_io_t input = *(ccv_cnnp_model_io_t*)ccv_array_get(incomings, j);
239
725
        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
723
        } else {
263
1.45k
          for (k = 0; k < input->model->output_size; 
k++731
)
264
731
            ccv_array_push(input_symbols, &input->outputs[k]);
265
723
        }
266
725
      }
267
    // Go through each sub model to build the graph.
268
576
    ccv_array_t* nodes;
269
576
    ccv_functional_model_build_node_hook_t hook;
270
576
    const ccv_array_t* const dependencies = self->sequence[i]->dependencies;
271
576
    if ((dependencies && 
dependencies->rnum > 02
) ||
self->sequence[i]->dependents > 0574
)
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
576
    sub_model->data = self->super.data;
283
576
    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
576
    if ((dependencies && 
dependencies->rnum > 02
) ||
self->sequence[i]->dependents > 0574
)
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
576
    sub_model->data = 0;
302
576
  }
303
99
  khiter_t it;
304
107
  for (it = 
kh_begin99
(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
99
  kh_destroy(io_node, io_node_map);
312
99
  ccv_array_free(input_symbols);
313
99
  if (parameter_indices)
314
2
    ccv_array_free(parameter_indices);
315
212
  for (i = 0, k = 0; k < self->model_output_size; 
k++113
)
316
113
  {
317
113
    ccv_cnnp_model_t* const sub_model = self->sequence[self->model_outputs[k]]->model;
318
227
    for (j = 0; j < sub_model->output_size; 
j++114
)
319
114
      outputs[i + j] = self->sequence[self->model_outputs[k]]->outputs[j];
320
113
    i += sub_model->output_size;
321
113
  }
322
99
  assert(i == output_size);
323
99
  PRINT(CCV_CLI_VERBOSE, "[cnnp_functional_model_build] 2. %p\n", self);
324
99
}
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
45
{
328
45
  ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super;
329
45
  int i;
330
355
  for (i = self->super.input_size; i < self->sequence_size; 
i++310
)
331
310
    ccv_cnnp_model_init_states(self->sequence[i]->model, graph, initializer, context);
332
45
}
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
110
{
336
110
  ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super;
337
110
  int i;
338
787
  for (i = self->super.input_size; i < self->sequence_size; 
i++677
)
339
677
    ccv_cnnp_model_set_is_test(self->sequence[i]->model, is_test, updater, context);
340
110
}
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
928
{
344
928
  ccv_cnnp_functional_model_t* const self = (ccv_cnnp_functional_model_t*)super;
345
928
  int i;
346
4.59k
  for (i = self->super.input_size; i < self->sequence_size; 
i++3.66k
)
347
3.66k
    ccv_cnnp_model_add_to_parameter_indices(self->sequence[i]->model, index, parameter_indices);
348
928
}
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
93
{
514
93
  assert(output_size > 0);
515
  // Do topological sort.
516
93
  ccv_array_t* const reverse_top = ccv_array_new(sizeof(ccv_cnnp_model_io_t), output_size, 0);
517
93
  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
200
  for (i = 0; i < output_size; 
i++107
)
522
107
    outputs[i]->visit = 2;
523
200
  for (i = output_size - 1; i >= 0; 
i--107
)
524
107
  {
525
107
    if (outputs[i]->visit == 3) // If we need to remove it, no need to visit.
526
5
      continue;
527
107
    assert
(outputs[i]->visit == 2)102
;
528
102
    ccv_array_clear(reverse_top);
529
102
    ccv_array_push(reverse_top, &outputs[i]);
530
686
    for (j = 0; j < reverse_top->rnum; 
j++584
)
531
584
    {
532
584
      const ccv_cnnp_model_io_t output = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, j);
533
584
      assert(!CCV_CNNP_IS_MODEL_INPUT(output->model));
534
      // If it is input, push it here.
535
584
      if (output->incomings && 
!581
CCV_CNNP_IS_MODEL_PARAMETER581
(output))
536
1.30k
        
for (k = 0; 581
k < output->incomings->rnum;
k++722
)
537
722
        {
538
722
          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
722
          if (CCV_CNNP_IS_MODEL_INPUT(input->model) || 
CCV_CNNP_IS_MODEL_PARAMETER529
(input))
541
194
            continue;
542
528
          if (input->visit == 1 || 
input->visit == 3480
) // Visited, skip.
543
48
            continue;
544
          // If this is an output, we need to remove it from the output array. Otherwise mark it as visited.
545
480
          input->visit = input->visit == 2 ? 
35
:
1475
;
546
480
          ccv_array_push(reverse_top, &input);
547
480
        }
548
      // Similar for dependencies.
549
584
      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
584
    }
563
584
    
for (j = 1; 102
j < reverse_top->rnum;
j++482
)
564
482
    {
565
482
      const ccv_cnnp_model_io_t output = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, j);
566
482
      if (output->visit == 1) // Clean the visit back.
567
477
        output->visit = 0;
568
482
    }
569
102
  }
570
93
  ccv_array_clear(reverse_top);
571
200
  for (i = 0; i < output_size; 
i++107
) // We will assign sequence in reverse order, thus, reverse the reverse top when copying the outputs.
572
107
  {
573
107
    if (outputs[output_size - 1 - i]->visit == 2)
574
102
      ccv_array_push(reverse_top, &outputs[output_size - 1 - i]);
575
107
    assert(outputs[output_size - 1 - i]->visit == 2 || outputs[output_size - 1 - i]->visit == 3);
576
107
    outputs[output_size - 1 - i]->visit = 0; // Clean up all visits.
577
107
  }
578
  // Go from the output, until we meet inputs.
579
93
  uint64_t input_bitmask[((input_size - 1) >> 6) + 1];
580
93
  memset(input_bitmask, 0, sizeof(uint64_t) * (((input_size - 1) >> 6) + 1));
581
93
  int tensor_output_size = 0; // io can be mapped to multiple tensor outputs, therefore, need to compute the exact tensor output size.
582
200
  for (i = 0; i < output_size; 
i++107
)
583
107
    tensor_output_size += outputs[i]->model->output_size;
584
638
  for (i = 0; i < reverse_top->rnum; 
i++545
)
585
545
  {
586
545
    const ccv_cnnp_model_io_t output = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, i);
587
545
    assert(!CCV_CNNP_IS_MODEL_INPUT(output->model));
588
    // If it is input, push it here.
589
545
    if (output->incomings && 
!542
CCV_CNNP_IS_MODEL_PARAMETER542
(output))
590
1.22k
      
for (j = 0; 542
j < output->incomings->rnum;
j++680
)
591
680
      {
592
680
        const ccv_cnnp_model_io_t input = *(ccv_cnnp_model_io_t*)ccv_array_get(output->incomings, j);
593
680
        ++input->visit; // Mark it as visited.
594
680
        if (input->visit != input->outgoings->rnum + input->dependents) // Not all dependencies visited.
595
89
          continue;
596
591
        if (!CCV_CNNP_IS_MODEL_INPUT(input->model) && 
!441
CCV_CNNP_IS_MODEL_PARAMETER441
(input))
597
440
          ccv_array_push(reverse_top, &input);
598
151
        else if (CCV_CNNP_IS_MODEL_INPUT(input->model)) {
599
226
          for (k = 0; k < input_size; 
k++76
)
600
226
            if (input == inputs[k])
601
150
              break;
602
150
          assert(k < input_size);
603
150
          input_bitmask[k >> 6] |= ((uint64_t)1 << (k & 63));
604
150
        }
605
591
      }
606
545
    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
545
  }
624
638
  
for (i = 0; 93
i < reverse_top->rnum;
i++545
)
625
545
  {
626
545
    const ccv_cnnp_model_io_t output = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, i);
627
545
    output->visit = 0; // Clean the visit back.
628
545
  }
629
243
  for (i = 0; i < input_size; 
i++150
)
630
150
    inputs[i]->visit = 0; // Clean the visit back.
631
243
  for (i = 0; i < input_size; 
i++150
)
632
150
    { assert((input_bitmask[i >> 6] & ((uint64_t)1 << (i & 63)))); } // Assuming they all match.
633
93
  const int sequence_size = reverse_top->rnum + input_size;
634
93
  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
93
  functional_model->super.isa = &ccv_cnnp_functional_model_isa;
636
93
  functional_model->super.outputs = (ccv_nnc_tensor_symbol_t*)(functional_model->sequence + sequence_size);
637
93
  functional_model->super.output_size = tensor_output_size;
638
93
  functional_model->super.input_size = input_size;
639
93
  functional_model->super.is_trainable = is_trainable;
640
93
  functional_model->model_output_size = output_size;
641
93
  functional_model->model_outputs = (int*)(functional_model->super.outputs + tensor_output_size);
642
93
  ccv_cnnp_model_copy_name(&functional_model->super, name);
643
93
  functional_model->sequence_size = sequence_size;
644
93
  memcpy(functional_model->sequence, inputs, sizeof(ccv_cnnp_model_io_t) * input_size);
645
638
  for (i = 0; i < reverse_top->rnum; 
i++545
)
646
545
    functional_model->sequence[input_size + i] = *(ccv_cnnp_model_io_t*)ccv_array_get(reverse_top, reverse_top->rnum - 1 - i);
647
200
  for (i = 0; i < output_size; 
i++107
)
648
107
  {
649
129
    for (j = sequence_size - 1; j >= input_size; 
j--22
)
650
129
      if (functional_model->sequence[j] == outputs[i])
651
107
      {
652
107
        functional_model->model_outputs[i] = j;
653
107
        break;
654
107
      }
655
107
  }
656
93
  ccv_array_free(reverse_top);
657
93
  return (ccv_cnnp_model_t*)functional_model;
658
93
}
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
150
{
675
150
  ccv_cnnp_model_t* const input = (ccv_cnnp_model_t*)cccalloc(1, sizeof(ccv_cnnp_model_t) + sizeof(ccv_nnc_tensor_symbol_t));
676
150
  input->isa = &ccv_cnnp_input_isa;
677
150
  input->io = ccv_array_new(sizeof(ccv_cnnp_model_io_t), 1, 0);
678
150
  ccv_cnnp_model_io_t input_io = ccmalloc(sizeof(struct ccv_cnnp_model_io_s) + sizeof(ccv_nnc_tensor_symbol_t));
679
150
  input_io->param_ref = 0;
680
150
  input_io->param_sel = 0;
681
150
  input_io->visit = 0;
682
150
  input_io->incomings = 0;
683
150
  input_io->dependencies = 0;
684
150
  input_io->dependents = 0;
685
150
  input_io->outgoings = 0;
686
150
  input_io->model = input;
687
150
  input_io->outputs = (ccv_nnc_tensor_symbol_t*)(input_io + 1);
688
150
  ccv_array_push(input->io, &input_io);
689
150
  input->outputs = (ccv_nnc_tensor_symbol_t*)(input + 1);
690
150
  input->output_size = 1;
691
150
  return input_io;
692
150
}
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
81
{
814
81
  ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super;
815
81
  PRINT(CCV_CLI_VERBOSE, "[cnnp_cmd_exec_build] -\n");
816
81
  ccv_nnc_tensor_param_t input_params[ccv_max(1, self->input_size)];
817
81
  int i, j;
818
243
  for (i = 0, j = 0; i < self->input_size; 
i++162
)
819
162
    if (self->inputs[i].type == CCV_CNNP_IO)
820
131
    {
821
131
      self->input_symbols[i] = inputs[j++];
822
131
      input_params[i] = ccv_nnc_tensor_symbol_params(graph, self->input_symbols[i]);
823
131
    } else 
if (31
self->inputs[i].type == CCV_CNNP_NO_TENSOR31
) {
824
0
      self->input_symbols[i] = NO_TENSOR_SYMBOL;
825
31
    } else if (!self->input_symbols[i].graph) {
826
      // Otherwise, we only create this symbol if it doesn't exist.
827
22
      const ccv_nnc_tensor_param_t params = self->inputs[i].init_state.info;
828
22
      input_params[i] = params;
829
22
      self->input_symbols[i] = ccv_nnc_tensor_symbol_new(graph, params, 0);
830
22
    }
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
81
  ccv_nnc_tensor_param_t output_params[ccv_max(1, self->output_size)];
834
81
  ccv_nnc_hint_tensor_auto(self->cmd, input_params, self->input_size, self->hint, output_params, self->output_size);
835
162
  for (i = 0, j = 0; i < self->output_size; 
i++81
)
836
81
    if (self->outputs[i] == CCV_CNNP_IO)
837
81
      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
81
  ccv_nnc_graph_exec_symbol_new(graph, self->cmd, self->input_symbols, self->input_size, self->output_symbols, self->output_size, 0);
843
81
}
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
65
{
847
65
  ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super;
848
65
  int i;
849
195
  for (i = 0; i < self->input_size; 
i++130
)
850
130
    if (self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR || 
self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR_AS_TRAINABLE110
)
851
31
      self->inputs[i].init_state.init(self->input_symbols[i], initializer, context, self->inputs[i].init_state.context);
852
65
}
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
81
{
856
81
  ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super;
857
81
  int i;
858
243
  for (i = 0; i < self->input_size; 
i++162
)
859
162
    if (self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR)
860
20
      add_to_array(outputs, self->input_symbols[i], 0); // Push this as retainable because it need to be init.
861
81
}
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
81
{
865
81
  ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super;
866
81
  int i;
867
243
  for (i = 0; i < self->input_size; 
i++162
)
868
162
    if (self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR_AS_TRAINABLE)
869
11
      add_to_array(parameters, self->input_symbols[i], is_trainable); // Push this as parameter.
870
81
}
871
872
static void _ccv_cnnp_cmd_exec_deinit(ccv_cnnp_model_t* const super)
873
72
{
874
72
  ccv_cnnp_model_cmd_exec_t* const self = (ccv_cnnp_model_cmd_exec_t*)super;
875
72
  int i, j;
876
216
  for (i = 0; i < self->input_size; 
i++144
)
877
144
    if ((self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR || 
self->inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR_AS_TRAINABLE133
) &&
878
144
      
self->inputs[i].init_state.context22
)
879
22
    {
880
22
      void* const context = self->inputs[i].init_state.context;
881
22
      if (self->inputs[i].init_state.deinit)
882
9
        self->inputs[i].init_state.deinit(context);
883
22
      self->inputs[i].init_state.init = 0;
884
22
      self->inputs[i].init_state.deinit = 0;
885
22
      self->inputs[i].init_state.context = 0;
886
22
      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
22
    }
894
72
}
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
72
{
909
72
  assert(input_size >= 0);
910
72
  assert(output_size > 0);
911
72
  int i;
912
72
  int io_input_size = 0;
913
216
  for (i = 0; i < input_size; 
i++144
)
914
144
    if (inputs[i].type == CCV_CNNP_IO)
915
122
      ++io_input_size;
916
22
    else {
917
22
      assert(inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR || inputs[i].type == CCV_CNNP_INIT_SHARED_TENSOR_AS_TRAINABLE);
918
22
      assert(inputs[i].init_state.init);
919
22
    }
920
72
  int io_output_size = 0;
921
144
  for (i = 0; i < output_size; 
i++72
)
922
72
    if (outputs[i] == CCV_CNNP_IO)
923
72
      ++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
72
  assert(io_output_size > 0);
928
72
  ccv_cnnp_model_cmd_exec_t* const model_cmd_exec = (ccv_cnnp_model_cmd_exec_t*)cccalloc(1, sizeof(ccv_cnnp_model_cmd_exec_t) + sizeof(ccv_nnc_tensor_symbol_t) * (io_output_size + input_size + output_size) + sizeof(ccv_cnnp_cmd_exec_io_t) * input_size + sizeof(int) * output_size);
929
72
  model_cmd_exec->super.isa = &ccv_cnnp_cmd_exec_isa;
930
72
  model_cmd_exec->super.input_size = io_input_size;
931
72
  model_cmd_exec->super.outputs = (ccv_nnc_tensor_symbol_t*)(model_cmd_exec + 1);
932
72
  model_cmd_exec->super.output_size = io_output_size;
933
72
  model_cmd_exec->super.is_trainable = is_trainable;
934
72
  ccv_cnnp_model_copy_name(&model_cmd_exec->super, name);
935
72
  model_cmd_exec->cmd = cmd;
936
72
  model_cmd_exec->hint = hint;
937
72
  model_cmd_exec->flags = flags;
938
72
  model_cmd_exec->input_size = input_size;
939
72
  model_cmd_exec->input_symbols = model_cmd_exec->super.outputs + io_output_size;
940
72
  model_cmd_exec->output_symbols = model_cmd_exec->input_symbols + input_size;
941
72
  model_cmd_exec->inputs = (ccv_cnnp_cmd_exec_io_t*)(model_cmd_exec->output_symbols + output_size);
942
72
  if (input_size > 0)
943
72
  {
944
72
    memcpy(model_cmd_exec->inputs, inputs, sizeof(ccv_cnnp_cmd_exec_io_t) * input_size);
945
72
    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
72
  }
950
72
  model_cmd_exec->output_size = output_size;
951
72
  model_cmd_exec->outputs = (int*)(model_cmd_exec->inputs + input_size);
952
72
  if (output_size > 0)
953
72
    memcpy(model_cmd_exec->outputs, outputs, sizeof(int) * output_size);
954
72
  return (ccv_cnnp_model_t*)model_cmd_exec;
955
72
}
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
62
{
959
62
  return _ccv_cnnp_cmd_exec(cmd, 0, hint, flags, inputs, input_size, outputs, output_size, is_trainable, name);
960
62
}
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
20
{
970
20
  initializer(initializer_context, CMD_DATA_TRANSFER_FORWARD(), ccv_nnc_no_hint, 0, (ccv_nnc_tensor_t*)context, tensor_symbol);
971
20
}
972
973
ccv_cnnp_cmd_exec_io_init_state_t ccv_cnnp_cmd_exec_io_copy(const ccv_nnc_tensor_t* const tensor)
974
12
{
975
12
  return (ccv_cnnp_cmd_exec_io_init_state_t){
976
12
    .info = tensor->info,
977
12
    .context = (void *)tensor,
978
12
    .init = _ccv_cnnp_cmd_exec_io_copy,
979
12
  };
980
12
}
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
11
{
990
11
  const ccv_cnnp_cmd_exec_io_set_by_t* const set_by = (ccv_cnnp_cmd_exec_io_set_by_t*)context;
991
11
  initializer(initializer_context, set_by->cmd, set_by->hint, set_by->flags, 0, tensor_symbol);
992
11
}
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
8
{
1003
8
  ccv_cnnp_cmd_exec_io_set_by_t* const set_by = (ccv_cnnp_cmd_exec_io_set_by_t*)ccmalloc(sizeof(ccv_cnnp_cmd_exec_io_set_by_t));
1004
8
  set_by->cmd = cmd;
1005
8
  set_by->hint = hint;
1006
8
  set_by->flags = flags;
1007
8
  return (ccv_cnnp_cmd_exec_io_init_state_t){
1008
8
    .info = params,
1009
8
    .context = set_by,
1010
8
    .init = _ccv_cnnp_cmd_exec_io_set_by,
1011
8
    .copy = _ccv_cnnp_cmd_exec_io_set_by_copy,
1012
8
    .deinit = ccfree,
1013
8
  };
1014
8
}