## Coverage Report

#### Created: 2026-05-04 15:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/liu/actions-runner/_work/ccv/ccv/lib/nnc/ccv_cnnp_model_core.c

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