## Coverage Report

#### Created: 2026-04-03 17:51

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

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

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