Coverage Report

Created: 2021-04-12 03:25

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