Coverage Report

Created: 2024-08-18 16:21

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