## Coverage Report

#### Created: 2026-04-14 19:45

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

    /home/liu/actions-runner/_work/ccv/ccv/lib/nnc/ccv_cnnp_model_addons.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
    7
    // MARK - Add-on Functions
    8
    9
    static int _ccv_cnnp_model_clip_grad_norm_reduce_norm2(const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, ccv_nnc_tensor_t* const* const inputs, const int input_size, ccv_nnc_tensor_t* const* const outputs, const int output_size, ccv_nnc_stream_context_t* const stream_context)
    10
    2
    {
    11
    2
      const int device_id = CCV_TENSOR_GET_DEVICE_ID(inputs[0]->info.type);
    12
    2
      ccv_nnc_tensor_t* const old_norm2 = outputs[1 + device_id * 2];
    13
    2
      ccv_nnc_tensor_t* const norm2 = outputs[1 + device_id * 2 + 1];
    14
    2
      const int tensor_count = ccv_nnc_tensor_count(inputs[0]->info);
    15
    2
      if (tensor_count == 1)
    16
    2
        ccv_nnc_cmd_exec(CMD_MUL_FORWARD(1), hint, flags, TENSOR_LIST(inputs[0], inputs[0]), TENSOR_LIST(norm2), stream_context);
    17
    0
      else {
    18
    0
        ccv_nnc_cmd_exec(CMD_REDUCE_NORM2_FORWARD(), hint, flags, TENSOR_LIST(inputs[0]), TENSOR_LIST(norm2), stream_context);
    19
    0
        ccv_nnc_cmd_exec(CMD_MUL_FORWARD(1), hint, flags, TENSOR_LIST(norm2, norm2), TENSOR_LIST(norm2), stream_context);
    20
    0
      }
    21
    2
      ccv_nnc_cmd_exec(CMD_ADD_FORWARD(1, 1), hint, flags, TENSOR_LIST(old_norm2, norm2), TENSOR_LIST(old_norm2), stream_context);
    22
    2
      return CCV_NNC_EXEC_SUCCESS;
    23
    2
    }
    24
    25
    static ccv_nnc_cmd_vtab_t clip_grad_norm_reduce_norm2_vtab = {
    26
      .exec = _ccv_cnnp_model_clip_grad_norm_reduce_norm2
    27
    };
    28
    29
    static int _ccv_cnnp_model_clip_grad_norm_scatter_norm2(const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, ccv_nnc_tensor_t* const* const inputs, const int input_size, ccv_nnc_tensor_t* const* const outputs, const int output_size, ccv_nnc_stream_context_t* const stream_context)
    30
    2
    {
    31
    2
      const int device_id = CCV_TENSOR_GET_DEVICE_ID(inputs[0]->info.type);
    32
    2
      ccv_nnc_tensor_t* const norm2 = inputs[1 + device_id * 2];
    33
    2
      ccv_nnc_cmd_exec(CMD_MUL_FORWARD(1), hint, flags, TENSOR_LIST(inputs[0], norm2), TENSOR_LIST(outputs[0]), stream_context);
    34
    2
      return CCV_NNC_EXEC_SUCCESS;
    35
    2
    }
    36
    37
    static ccv_nnc_cmd_vtab_t clip_grad_norm_scatter_norm2_vtab = {
    38
      .exec = _ccv_cnnp_model_clip_grad_norm_scatter_norm2
    39
    };
    40
    41
    void ccv_cnnp_model_parameters_clip_grad_norm(ccv_cnnp_model_t* const model, const ccv_cnnp_model_io_t parameters, int norm_type, float max_norm, ccv_nnc_stream_context_t* const stream_context)
    42
    2
    {
    43
    2
      assert(norm_type == 2);
    44
    2
      ccv_cnnp_compiled_data_t* const compiled_data = model->compiled_data;
    45
    2
      assert(compiled_data);
    46
    2
      const int parallel_count = ccv_max(model->parallel_count, 1);
    47
    2
      ccv_nnc_tensor_t* norm2[parallel_count * 2];
    48
    2
      ccv_nnc_tensor_t* max_normt[parallel_count];
    49
    2
      const int stream_type = model->compiled_data->stream_type;
    50
    2
      int i;
    51
    2
      if (stream_type == CCV_STREAM_CONTEXT_GPU)
    52
    0
      {
    53
    0
        for (i = 0; i < parallel_count; i++)
    54
    0
        {
    55
    0
          ccv_nnc_tensor_param_t info = {
    56
    0
            .type = CCV_TENSOR_GPU_MEMORY,
    57
    0
            .format = CCV_TENSOR_FORMAT_NHWC,
    58
    0
            .datatype = CCV_32F,
    59
    0
            .dim = {1},
    60
    0
          };
    61
    0
          CCV_TENSOR_SET_DEVICE_ID(info.type, i);
    62
    0
          norm2[i * 2] = ccv_nnc_tensor_new(ccv_nnc_xpu_alloc(&compiled_data->xpu_alloc, i, stream_context, ccv_nnc_tensor_data_size(info)), info, 0);
    63
    0
          norm2[i * 2 + 1] = ccv_nnc_tensor_new(ccv_nnc_xpu_alloc(&compiled_data->xpu_alloc, i, stream_context, ccv_nnc_tensor_data_size(info)), info, 0);
    64
    0
          max_normt[i] = ccv_nnc_tensor_new(ccv_nnc_xpu_alloc(&compiled_data->xpu_alloc, i, stream_context, ccv_nnc_tensor_data_size(info)), info, 0);
    65
    0
        }
    66
    2
      } else {
    67
    4
        for (i = 0; i < parallel_count; i++2)
    68
    2
        {
    69
    2
          ccv_nnc_tensor_param_t info = {
    70
    2
            .type = CCV_TENSOR_CPU_MEMORY,
    71
    2
            .format = CCV_TENSOR_FORMAT_NHWC,
    72
    2
            .datatype = CCV_32F,
    73
    2
            .dim = {1},
    74
    2
          };
    75
    2
          norm2[i * 2] = ccv_nnc_tensor_new(0, info, 0);
    76
    2
          norm2[i * 2 + 1] = ccv_nnc_tensor_new(0, info, 0);
    77
    2
          max_normt[i] = ccv_nnc_tensor_new(0, info, 0);
    78
    2
        }
    79
    2
      }
    80
      // zero out old norm2.
    81
    2
      if (parallel_count > 1)
    82
    0
      {
    83
    0
        ccv_nnc_stream_context_t* streams[parallel_count];
    84
    0
        ccv_nnc_stream_signal_t* signal;
    85
    0
        if (stream_context)
    86
    0
          signal = ccv_nnc_stream_context_emit_signal_new(stream_context);
    87
    0
        for (i = 0; i < parallel_count; i++)
    88
    0
        {
    89
    0
          const int stream_type = CCV_TENSOR_GET_MEMORY(norm2[i * 2]->info.type) == CCV_TENSOR_GPU_MEMORY ? CCV_STREAM_CONTEXT_GPU : CCV_STREAM_CONTEXT_CPU;
    90
    0
          const int device_id = CCV_TENSOR_GET_DEVICE_ID(norm2[i * 2]->info.type);
    91
    0
          int type = stream_type;
    92
    0
          CCV_STREAM_SET_DEVICE_ID(type, device_id);
    93
    0
          ccv_nnc_stream_context_t* const stream_0 = ccv_cnnp_compiled_data_get_stream(compiled_data, type);
    94
          // Wait signal to finish.
    95
    0
          if (stream_context)
    96
    0
            ccv_nnc_stream_context_wait_signal(stream_0, signal);
    97
    0
          ccv_nnc_cmd_exec(CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, 0, TENSOR_LIST(norm2[i * 2]), stream_0);
    98
    0
          if (stream_context)
    99
    0
          {
    100
    0
            ccv_nnc_stream_signal_t* const signal = ccv_nnc_stream_context_emit_signal_new(stream_0);
    101
    0
            ccv_nnc_stream_context_wait_signal(stream_context, signal);
    102
    0
          }
    103
    0
          streams[i] = stream_0;
    104
    0
        }
    105
        // If this should be blocking, blocking it.
    106
    0
        if (!stream_context)
    107
    0
          for (i = 0; i < parallel_count; i++)
    108
    0
            if (streams[i])
    109
    0
              ccv_nnc_stream_context_wait(streams[i]);
    110
    2
      } else {
    111
    2
        ccv_nnc_cmd_exec(CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, 0, TENSOR_LIST(norm2[0]), stream_context);
    112
    2
      }
    113
      // Gather norm2.
    114
    2
      ccv_nnc_cmd_t reduce_cmd = {
    115
    2
        .cmd = CCV_NNC_CUSTOM_FORWARD,
    116
    2
        .isa = &clip_grad_norm_reduce_norm2_vtab,
    117
    2
      };
    118
    2
      ccv_cnnp_model_parameter_gradients_map(model, parameters, reduce_cmd, ccv_nnc_no_hint, 0, 0, 0, norm2, parallel_count * 2, stream_context);
    119
      // Now compute max(max_norm / norm2, 1.0).
    120
    2
      if (parallel_count > 1)
    121
    0
      {
    122
    0
        ccv_nnc_stream_context_t* streams[parallel_count];
    123
    0
        ccv_nnc_stream_signal_t* signal;
    124
    0
        if (stream_context)
    125
    0
          signal = ccv_nnc_stream_context_emit_signal_new(stream_context);
    126
    0
        for (i = 0; i < parallel_count; i++)
    127
    0
        {
    128
    0
          const int stream_type = CCV_TENSOR_GET_MEMORY(norm2[i * 2]->info.type) == CCV_TENSOR_GPU_MEMORY ? CCV_STREAM_CONTEXT_GPU : CCV_STREAM_CONTEXT_CPU;
    129
    0
          const int device_id = CCV_TENSOR_GET_DEVICE_ID(norm2[i * 2]->info.type);
    130
    0
          int type = stream_type;
    131
    0
          CCV_STREAM_SET_DEVICE_ID(type, device_id);
    132
    0
          ccv_nnc_stream_context_t* const stream_0 = ccv_cnnp_compiled_data_get_stream(compiled_data, type);
    133
          // Wait signal to finish.
    134
    0
          if (stream_context)
    135
    0
            ccv_nnc_stream_context_wait_signal(stream_0, signal);
    136
    0
          ccv_nnc_cmd_exec(CMD_EWSQRT_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_LIST(norm2[i * 2]), TENSOR_LIST(norm2[i * 2]), stream_0);
    137
    0
          ccv_nnc_cmd_exec(CMD_SET_FORWARD(max_norm), ccv_nnc_no_hint, 0, 0, 0, TENSOR_LIST(max_normt[i]), stream_0);
    138
    0
          ccv_nnc_cmd_exec(CMD_EWDIV_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_LIST(max_normt[i], norm2[i * 2]), TENSOR_LIST(norm2[i * 2]), stream_0);
    139
    0
          ccv_nnc_cmd_exec(CMD_CLAMP_FORWARD(NAN, 1), ccv_nnc_no_hint, 0, TENSOR_LIST(norm2[i * 2]), TENSOR_LIST(norm2[i * 2]), stream_0);
    140
    0
          if (stream_context)
    141
    0
          {
    142
    0
            ccv_nnc_stream_signal_t* const signal = ccv_nnc_stream_context_emit_signal_new(stream_0);
    143
    0
            ccv_nnc_stream_context_wait_signal(stream_context, signal);
    144
    0
          }
    145
    0
          streams[i] = stream_0;
    146
    0
        }
    147
        // If this should be blocking, blocking it.
    148
    0
        if (!stream_context)
    149
    0
          for (i = 0; i < parallel_count; i++)
    150
    0
            if (streams[i])
    151
    0
              ccv_nnc_stream_context_wait(streams[i]);
    152
    2
      } else {
    153
    2
        ccv_nnc_cmd_exec(CMD_EWSQRT_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_LIST(norm2[0]), TENSOR_LIST(norm2[0]), stream_context);
    154
    2
        ccv_nnc_cmd_exec(CMD_SET_FORWARD(max_norm), ccv_nnc_no_hint, 0, 0, 0, TENSOR_LIST(max_normt[0]), stream_context);
    155
    2
        ccv_nnc_cmd_exec(CMD_EWDIV_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_LIST(max_normt[0], norm2[0]), TENSOR_LIST(norm2[0]), stream_context);
    156
    2
        ccv_nnc_cmd_exec(CMD_CLAMP_FORWARD(NAN, 1), ccv_nnc_no_hint, 0, TENSOR_LIST(norm2[0]), TENSOR_LIST(norm2[0]), stream_context);
    157
    2
      }
    158
    2
      ccv_nnc_cmd_t scatter_cmd = {
    159
    2
        .cmd = CCV_NNC_CUSTOM_FORWARD,
    160
    2
        .isa = &clip_grad_norm_scatter_norm2_vtab,
    161
    2
      };
    162
    2
      ccv_cnnp_model_parameter_gradients_map(model, parameters, scatter_cmd, ccv_nnc_no_hint, 0, norm2, parallel_count * 2, 0, 0, stream_context);
    163
    2
      if (stream_type == CCV_STREAM_CONTEXT_GPU)
    164
    0
        for (i = 0; i < parallel_count; i++)
    165
    0
        {
    166
    0
          ccv_nnc_xpu_free(&compiled_data->xpu_alloc, norm2[i * 2]->data.u8);
    167
    0
          ccv_nnc_xpu_free(&compiled_data->xpu_alloc, norm2[i * 2 + 1]->data.u8);
    168
    0
          ccv_nnc_xpu_free(&compiled_data->xpu_alloc, max_normt[i]->data.u8);
    169
    0
        }
    170
    4
      for (i = 0; i < parallel_count; i++2)
    171
    2
      {
    172
    2
        ccv_nnc_tensor_free(norm2[i * 2]);
    173
    2
        ccv_nnc_tensor_free(norm2[i * 2 + 1]);
    174
    2
        ccv_nnc_tensor_free(max_normt[i]);
    175
    2
      }
    176
    2
    }
    177
    178
    // MARK - Add-on Functions
    179
    180
    static int _ccv_cnnp_model_isnan(const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, ccv_nnc_tensor_t* const* const inputs, const int input_size, ccv_nnc_tensor_t* const* const outputs, const int output_size, ccv_nnc_stream_context_t* const stream_context)
    181
    0
    {
    182
    0
      const int device_id = CCV_TENSOR_GET_DEVICE_ID(inputs[0]->info.type);
    183
    0
      ccv_nnc_tensor_t* const old_isnanr = outputs[1 + device_id * 2];
    184
    0
      ccv_nnc_tensor_t* const isnanr = outputs[1 + device_id * 2 + 1];
    185
    0
      ccv_nnc_cmd_t reduce_cmd = CMD_REDUCE_ISNAN_FORWARD();
    186
    0
      reduce_cmd.info.reduce.count = ccv_nnc_tensor_nd(inputs[0]->info.dim);
    187
    0
      int i;
    188
    0
      for (i = 0; i < cmd.info.reduce.count; i++)
    189
    0
        reduce_cmd.info.reduce.axis[i] = i;
    190
    0
      ccv_nnc_cmd_exec(reduce_cmd, hint, flags, TENSOR_LIST(inputs[0]), TENSOR_LIST(isnanr), stream_context);
    191
    0
      ccv_nnc_cmd_exec(CMD_EWSUM_FORWARD(), hint, flags, TENSOR_LIST(old_isnanr, isnanr), TENSOR_LIST(old_isnanr), stream_context);
    192
    0
      return CCV_NNC_EXEC_SUCCESS;
    193
    0
    }
    194
    195
    static ccv_nnc_cmd_vtab_t reduce_isnan_vtab = {
    196
      .exec = _ccv_cnnp_model_isnan
    197
    };
    198
    199
    int ccv_cnnp_model_parameter_gradients_isnan(ccv_cnnp_model_t* const model, const ccv_cnnp_model_io_t parameters, ccv_nnc_stream_context_t* const stream_context)
    200
    0
    {
    201
    0
      ccv_cnnp_compiled_data_t* const compiled_data = model->compiled_data;
    202
    0
      assert(compiled_data);
    203
    0
      const int parallel_count = ccv_max(model->parallel_count, 1);
    204
    0
      ccv_nnc_tensor_t* isnanr[parallel_count * 2];
    205
    0
      const int stream_type = model->compiled_data->stream_type;
    206
    0
      int i;
    207
    0
      if (stream_type == CCV_STREAM_CONTEXT_GPU)
    208
    0
      {
    209
    0
        for (i = 0; i < parallel_count; i++)
    210
    0
        {
    211
    0
          ccv_nnc_tensor_param_t info = {
    212
    0
            .type = CCV_TENSOR_GPU_MEMORY,
    213
    0
            .format = CCV_TENSOR_FORMAT_NHWC,
    214
    0
            .datatype = CCV_32S,
    215
    0
            .dim = {1},
    216
    0
          };
    217
    0
          CCV_TENSOR_SET_DEVICE_ID(info.type, i);
    218
    0
          isnanr[i * 2] = ccv_nnc_tensor_new(ccv_nnc_xpu_alloc(&compiled_data->xpu_alloc, i, stream_context, ccv_nnc_tensor_data_size(info)), info, 0);
    219
    0
          isnanr[i * 2 + 1] = ccv_nnc_tensor_new(ccv_nnc_xpu_alloc(&compiled_data->xpu_alloc, i, stream_context, ccv_nnc_tensor_data_size(info)), info, 0);
    220
    0
        }
    221
    0
      } else {
    222
    0
        for (i = 0; i < parallel_count; i++)
    223
    0
        {
    224
    0
          ccv_nnc_tensor_param_t info = {
    225
    0
            .type = CCV_TENSOR_CPU_MEMORY,
    226
    0
            .format = CCV_TENSOR_FORMAT_NHWC,
    227
    0
            .datatype = CCV_32S,
    228
    0
            .dim = {1},
    229
    0
          };
    230
    0
          isnanr[i * 2] = ccv_nnc_tensor_new(0, info, 0);
    231
    0
          isnanr[i * 2 + 1] = ccv_nnc_tensor_new(0, info, 0);
    232
    0
        }
    233
    0
      }
    234
      // zero out old isnanr.
    235
    0
      if (parallel_count > 1)
    236
    0
      {
    237
    0
        ccv_nnc_stream_context_t* streams[parallel_count];
    238
    0
        ccv_nnc_stream_signal_t* signal;
    239
    0
        if (stream_context)
    240
    0
          signal = ccv_nnc_stream_context_emit_signal_new(stream_context);
    241
    0
        for (i = 0; i < parallel_count; i++)
    242
    0
        {
    243
    0
          const int stream_type = CCV_TENSOR_GET_MEMORY(isnanr[i * 2]->info.type) == CCV_TENSOR_GPU_MEMORY ? CCV_STREAM_CONTEXT_GPU : CCV_STREAM_CONTEXT_CPU;
    244
    0
          const int device_id = CCV_TENSOR_GET_DEVICE_ID(isnanr[i * 2]->info.type);
    245
    0
          int type = stream_type;
    246
    0
          CCV_STREAM_SET_DEVICE_ID(type, device_id);
    247
    0
          ccv_nnc_stream_context_t* const stream_0 = ccv_cnnp_compiled_data_get_stream(compiled_data, type);
    248
          // Wait signal to finish.
    249
    0
          if (stream_context)
    250
    0
            ccv_nnc_stream_context_wait_signal(stream_0, signal);
    251
    0
          ccv_nnc_cmd_exec(CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, 0, TENSOR_LIST(isnanr[i * 2]), stream_0);
    252
    0
          if (stream_context)
    253
    0
          {
    254
    0
            ccv_nnc_stream_signal_t* const signal = ccv_nnc_stream_context_emit_signal_new(stream_0);
    255
    0
            ccv_nnc_stream_context_wait_signal(stream_context, signal);
    256
    0
          }
    257
    0
          streams[i] = stream_0;
    258
    0
        }
    259
        // If this should be blocking, blocking it.
    260
    0
        if (!stream_context)
    261
    0
          for (i = 0; i < parallel_count; i++)
    262
    0
            if (streams[i])
    263
    0
              ccv_nnc_stream_context_wait(streams[i]);
    264
    0
      } else
    265
    0
        ccv_nnc_cmd_exec(CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, 0, TENSOR_LIST(isnanr[0]), stream_context);
    266
      // Gather isnanr.
    267
    0
      ccv_nnc_cmd_t reduce_cmd = {
    268
    0
        .cmd = CCV_NNC_CUSTOM_FORWARD,
    269
    0
        .isa = &reduce_isnan_vtab,
    270
    0
      };
    271
    0
      ccv_cnnp_model_parameter_gradients_map(model, parameters, reduce_cmd, ccv_nnc_no_hint, 0, 0, 0, isnanr, parallel_count * 2, stream_context);
    272
    0
      for (i = 0; i < parallel_count; i++)
    273
    0
        ccv_nnc_tensor_free(isnanr[i * 2 + 1]);
    274
    0
      int retval = 0;
    275
    0
      if (stream_type == CCV_TENSOR_GPU_MEMORY)
    276
    0
      {
    277
    0
        ccv_nnc_tensor_param_t info = {
    278
    0
          .type = CCV_TENSOR_CPU_MEMORY,
    279
    0
          .format = CCV_TENSOR_FORMAT_NHWC,
    280
    0
          .datatype = CCV_32S,
    281
    0
          .dim = {1},
    282
    0
        };
    283
    0
        ccv_nnc_tensor_t* checknan = ccv_nnc_tensor_new(0, info, 0);
    284
    0
        for (i = 0; i < parallel_count; i++)
    285
    0
        {
    286
    0
          ccv_nnc_cmd_exec(CMD_DATA_TRANSFER_FORWARD(), ccv_nnc_no_hint, 0, TENSOR_LIST(isnanr[i * 2]), TENSOR_LIST(checknan), 0);
    287
    0
          if (checknan->data.i32[0] > 0)
    288
    0
          {
    289
    0
            retval = 1;
    290
    0
            break;
    291
    0
          }
    292
    0
        }
    293
    0
        ccv_nnc_tensor_free(checknan);
    294
    0
      } else {
    295
    0
        for (i = 0; i < parallel_count; i++)
    296
    0
          if (isnanr[i * 2]->data.i32[0] > 0)
    297
    0
          {
    298
    0
            retval = 1;
    299
    0
            break;
    300
    0
          }
    301
    0
      }
    302
    0
      for (i = 0; i < parallel_count; i++)
    303
    0
        ccv_nnc_tensor_free(isnanr[i * 2]);
    304
    0
      return retval;
    305
    0
    }
    306
    307
    // MARK - Core Layers
    308
    309
    static void _ccv_cnnp_sum_build(ccv_cnnp_model_t* const self, 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)
    310
    64
    {
    311
    64
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sum_build] -\n");
    312
    64
      assert(output_size == 1);
    313
    64
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, ccv_nnc_tensor_symbol_params(graph, inputs[0]), 0);
    314
    64
      ccv_nnc_graph_exec_symbol_new(graph, CMD_EWSUM_FORWARD(), inputs, input_size, outputs, output_size, 0);
    315
    64
    }
    316
    317
    static ccv_cnnp_model_t* _ccv_cnnp_sum_copy(const ccv_cnnp_model_t* const self, void* const context);
    318
    319
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sum_isa = {
    320
      .build = _ccv_cnnp_sum_build,
    321
      .copy = _ccv_cnnp_sum_copy,
    322
    };
    323
    324
    typedef struct {
    325
      ccv_cnnp_model_t super;
    326
      ccv_nnc_tensor_symbol_t output;
    327
    } ccv_cnnp_model_sum_t;
    328
    329
    ccv_cnnp_model_t* ccv_cnnp_sum(const char* const name)
    330
    63
    {
    331
    63
      ccv_cnnp_model_sum_t* const model_sum = (ccv_cnnp_model_sum_t*)cccalloc(1, sizeof(ccv_cnnp_model_sum_t));
    332
    63
      model_sum->super.isa = &ccv_cnnp_sum_isa;
    333
    63
      model_sum->super.input_size = 0;
    334
    63
      model_sum->super.outputs = &model_sum->output;
    335
    63
      model_sum->super.output_size = 1;
    336
    63
      ccv_cnnp_model_copy_name(&model_sum->super, name);
    337
    63
      return (ccv_cnnp_model_t*)model_sum;
    338
    63
    }
    339
    340
    static ccv_cnnp_model_t* _ccv_cnnp_sum_copy(const ccv_cnnp_model_t* const self, void* const context)
    341
    3
    {
    342
    3
      return ccv_cnnp_sum(self->name);
    343
    3
    }
    344
    345
    typedef struct {
    346
      ccv_cnnp_model_t super;
    347
      int axis;
    348
      ccv_nnc_tensor_symbol_t output;
    349
    } ccv_cnnp_model_concat_t;
    350
    351
    static void _ccv_cnnp_concat_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)
    352
    4
    {
    353
    4
      const ccv_cnnp_model_concat_t* const self = (const ccv_cnnp_model_concat_t*)super;
    354
    4
      PRINT(CCV_CLI_VERBOSE, "[cnnp_concat_build] 1. -\n");
    355
    4
      assert(output_size == 1);
    356
    4
      ccv_nnc_tensor_param_t output_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    357
    4
      int i, j;
    358
    4
      if (output_params.dim[0] == 0)
    359
    0
        for (i = 1; i < input_size; i++)
    360
    0
        {
    361
    0
          output_params = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    362
    0
          if (output_params.dim[0] != 0)
    363
    0
            break;
    364
    0
        }
    365
    4
      const int nd = ccv_nnc_tensor_nd(output_params.dim);
    366
    4
      const int axis = self->axis;
    367
    4
      assert(axis < nd);
    368
    4
      output_params.dim[axis] = 0;
    369
    4
      int input_is_contiguous = 1;
    370
    12
      for (i = 0; i < input_size; i++8)
    371
    8
      {
    372
    8
        const ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    373
    8
        const int input_nd = ccv_nnc_tensor_nd(input_params.dim);
    374
    8
        if (input_nd == 0)
    375
    0
        {
    376
    0
          PRINT(CCV_CLI_VERBOSE, "[cnnp_concat_build] %d. input[%d]: -\n", i + 2, i);
    377
    0
          input_is_contiguous = 0;
    378
    0
          continue;
    379
    0
        }
    380
    8
        if (CCV_CLI_OUTPUT_LEVEL_IS(CCV_CLI_VERBOSE))
    381
    0
        {
    382
    0
          PRINT(CCV_CLI_VERBOSE, "[cnnp_concat_build] %d. input[%d]: (%d", i + 2, i, input_params.dim[0]);
    383
    0
          int i;
    384
    0
          for (i = 1; i < CCV_NNC_MAX_DIM_ALLOC && input_params.dim[i] > 0; i++)
    385
    0
            PRINT(CCV_CLI_VERBOSE, ", %d", input_params.dim[i]);
    386
    0
          PRINT(CCV_CLI_VERBOSE, ")\n");
    387
    0
        }
    388
    8
        assert(input_nd == nd);
    389
    16
        for (j = 0; 8j < nd; j++8)
    390
    8
          if (j != axis)
    391
    0
            { assert(input_params.dim[j] == output_params.dim[j]); }
    392
    8
        output_params.dim[axis] += input_params.dim[axis];
    393
    8
      }
    394
    4
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    395
    4
      int ofs[CCV_NNC_MAX_DIM_ALLOC] = {};
    396
    4
      int stride[CCV_NNC_MAX_DIM_ALLOC] = {};
    397
    4
      ccv_nnc_tensor_get_stride(output_params.dim, stride);
    398
    4
      if (input_is_contiguous)
    399
    4
      {
    400
    4
        ccv_nnc_tensor_symbol_t aliases[input_size];
    401
    12
        for (i = 0; i < input_size; i++8)
    402
    8
        {
    403
    8
          const ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    404
    8
          aliases[i] = ccv_nnc_tensor_symbol_alias_new(graph, outputs[0], ofs, stride, input_params, 0);
    405
    8
          ofs[axis] += input_params.dim[axis];
    406
    8
        }
    407
        // Format transform is more flexible.
    408
    4
        ccv_nnc_graph_exec_symbol_new(graph, CMD_FORMAT_TRANSFORM_FORWARD(), inputs, input_size, aliases, input_size, "concat");
    409
    4
      } else {
    410
    0
        ccv_nnc_tensor_symbol_t aliases[input_size];
    411
    0
        for (i = 0; i < input_size; i++)
    412
    0
        {
    413
    0
          const ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    414
    0
          if (input_params.dim[0] == 0)
    415
    0
          {
    416
            // Create a new alias anyway, but not going to use it, in this way, the alias count will match during absorb.
    417
    0
            aliases[i] = ccv_nnc_tensor_symbol_alias_new(graph, outputs[0], ofs, stride, input_params, 0);
    418
    0
            continue;
    419
    0
          }
    420
    0
          aliases[i] = ccv_nnc_tensor_symbol_alias_new(graph, outputs[0], ofs, stride, input_params, 0);
    421
    0
          ofs[axis] += input_params.dim[axis];
    422
    0
        }
    423
        // Format transform is more flexible.
    424
    0
        ccv_nnc_graph_exec_symbol_new(graph, CMD_FORMAT_TRANSFORM_FORWARD(), inputs, input_size, aliases, input_size, "concat");
    425
    0
      }
    426
    4
    }
    427
    428
    static ccv_cnnp_model_t* _ccv_cnnp_concat_copy(const ccv_cnnp_model_t* const self, void* const context);
    429
    430
    static const ccv_cnnp_model_vtab_t ccv_cnnp_concat_isa = {
    431
      .build = _ccv_cnnp_concat_build,
    432
      .copy = _ccv_cnnp_concat_copy,
    433
    };
    434
    435
    ccv_cnnp_model_t* ccv_cnnp_concat(const int axis, const char* const name)
    436
    4
    {
    437
    4
      ccv_cnnp_model_concat_t* const model_concat = (ccv_cnnp_model_concat_t*)cccalloc(1, sizeof(ccv_cnnp_model_concat_t));
    438
    4
      model_concat->super.isa = &ccv_cnnp_concat_isa;
    439
    4
      model_concat->super.input_size = 0;
    440
    4
      model_concat->super.outputs = &model_concat->output;
    441
    4
      model_concat->super.output_size = 1;
    442
    4
      model_concat->axis = axis;
    443
    4
      ccv_cnnp_model_copy_name(&model_concat->super, name);
    444
    4
      return (ccv_cnnp_model_t*)model_concat;
    445
    4
    }
    446
    447
    static ccv_cnnp_model_t* _ccv_cnnp_concat_copy(const ccv_cnnp_model_t* const super, void* const context)
    448
    0
    {
    449
    0
      const ccv_cnnp_model_concat_t* const self = (const ccv_cnnp_model_concat_t*)super;
    450
    0
      return ccv_cnnp_concat(self->axis, self->super.name);
    451
    0
    }
    452
    453
    typedef struct {
    454
      ccv_cnnp_model_t super;
    455
      int axis;
    456
      ccv_nnc_tensor_symbol_t outputs[1];
    457
    } ccv_cnnp_model_chunk_t;
    458
    459
    static void _ccv_cnnp_chunk_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)
    460
    2
    {
    461
    2
      const ccv_cnnp_model_concat_t* const self = (const ccv_cnnp_model_concat_t*)super;
    462
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_chunk_build] 1. axis: %d\n", self->axis);
    463
    2
      assert(input_size == 1);
    464
    2
      const ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    465
    2
      if (CCV_CLI_OUTPUT_LEVEL_IS(CCV_CLI_VERBOSE))
    466
    0
      {
    467
    0
        PRINT(CCV_CLI_VERBOSE, "[cnnp_chunk_build] 2. input: (%d", input_params.dim[0]);
    468
    0
        int i;
    469
    0
        for (i = 1; i < CCV_NNC_MAX_DIM_ALLOC && input_params.dim[i] > 0; i++)
    470
    0
          PRINT(CCV_CLI_VERBOSE, ", %d", input_params.dim[i]);
    471
    0
        PRINT(CCV_CLI_VERBOSE, ")\n");
    472
    0
      }
    473
    2
      ccv_nnc_tensor_param_t output_params = input_params;
    474
    2
      int i;
    475
    2
      const int nd = ccv_nnc_tensor_nd(output_params.dim);
    476
    2
      const int axis = self->axis;
    477
    2
      assert(axis < nd);
    478
    2
      const int n = self->super.output_size;
    479
    2
      assert(n == output_size);
    480
    2
      assert(output_params.dim[axis] % n == 0);
    481
    2
      output_params.dim[axis] = output_params.dim[axis] / n;
    482
    2
      int ofs[CCV_NNC_MAX_DIM_ALLOC] = {};
    483
    2
      int stride[CCV_NNC_MAX_DIM_ALLOC] = {};
    484
    2
      ccv_nnc_tensor_get_stride(input_params.dim, stride);
    485
    2
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    486
    2
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    487
    2
      {
    488
    6
        for (i = 0; i < output_size; i++4)
    489
    4
        {
    490
    4
          outputs[i] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], ofs, stride, output_params, 0);
    491
    4
          ofs[axis] += output_params.dim[axis];
    492
    4
        }
    493
    2
      } else {
    494
        // Otherwise, we need to check if it is permute. For permute, we cannot do alias directly.
    495
        // We need to first materialize the permute and then run reshape on top of it, otherwise it will be wrong.
    496
    0
        int old_stride[CCV_NNC_MAX_DIM_ALLOC];
    497
    0
        ccv_nnc_tensor_symbol_alias_params(graph, inputs[0], 0, old_stride);
    498
        // We identify permute by checking if the stride is not in descending order.
    499
        // This also covered "permute" through reshape, rather than using ccv_cnnp_permute directly.
    500
    0
        int i, no_permute = 1;
    501
    0
        for (i = 1; no_permute && i < nd; i++)
    502
    0
          if (old_stride[i - 1] < old_stride[i])
    503
    0
            no_permute = 0;
    504
    0
        if (no_permute)
    505
    0
        { // Just straightforward reshape if there is no no permute.
    506
    0
          for (i = 0; i < output_size; i++)
    507
    0
          {
    508
    0
            outputs[i] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], ofs, old_stride, output_params, 0);
    509
    0
            ofs[axis] += output_params.dim[axis];
    510
    0
          }
    511
    0
        } else {
    512
          // Otherwise, we first do format transform to plain tensor and then do reshape.
    513
    0
          ccv_nnc_tensor_symbol_t permuted = ccv_nnc_tensor_symbol_new(graph, input_params, 0);
    514
    0
          ccv_nnc_graph_exec_symbol_new(graph, CMD_FORMAT_TRANSFORM_FORWARD(), TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(permuted), "reshape");
    515
    0
          for (i = 0; i < output_size; i++)
    516
    0
          {
    517
    0
            outputs[i] = ccv_nnc_tensor_symbol_alias_new(graph, permuted, ofs, stride, output_params, 0);
    518
    0
            ofs[axis] += output_params.dim[axis];
    519
    0
          }
    520
    0
        }
    521
    0
      }
    522
    2
    }
    523
    524
    static ccv_cnnp_model_t* _ccv_cnnp_chunk_copy(const ccv_cnnp_model_t* const self, void* const context);
    525
    526
    static const ccv_cnnp_model_vtab_t ccv_cnnp_chunk_isa = {
    527
      .build = _ccv_cnnp_chunk_build,
    528
      .copy = _ccv_cnnp_chunk_copy,
    529
    };
    530
    531
    ccv_cnnp_model_t* ccv_cnnp_chunk(const int n, const int axis, const char* const name)
    532
    2
    {
    533
    2
      assert(n >= 1);
    534
    2
      ccv_cnnp_model_chunk_t* const model_chunk = (ccv_cnnp_model_chunk_t*)cccalloc(1, sizeof(ccv_cnnp_model_chunk_t) + sizeof(ccv_nnc_tensor_symbol_t) * (n - 1));
    535
    2
      model_chunk->super.isa = &ccv_cnnp_chunk_isa;
    536
    2
      model_chunk->super.input_size = 1;
    537
    2
      model_chunk->super.outputs = model_chunk->outputs;
    538
    2
      model_chunk->super.output_size = n;
    539
    2
      model_chunk->axis = axis;
    540
    2
      ccv_cnnp_model_copy_name(&model_chunk->super, name);
    541
    2
      return (ccv_cnnp_model_t*)model_chunk;
    542
    2
    }
    543
    544
    static ccv_cnnp_model_t* _ccv_cnnp_chunk_copy(const ccv_cnnp_model_t* const super, void* const context)
    545
    0
    {
    546
    0
      const ccv_cnnp_model_chunk_t* const self = (const ccv_cnnp_model_chunk_t*)super;
    547
    0
      return ccv_cnnp_chunk(self->super.output_size, self->axis, self->super.name);
    548
    0
    }
    549
    550
    typedef struct {
    551
      ccv_cnnp_model_t super;
    552
      ccv_nnc_tensor_symbol_t output;
    553
      int format;
    554
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    555
      int ofs[CCV_NNC_MAX_DIM_ALLOC];
    556
      int stride[CCV_NNC_MAX_DIM_ALLOC];
    557
    } ccv_cnnp_model_reshape_t;
    558
    559
    static void _ccv_cnnp_reshape_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)
    560
    1.06k
    {
    561
    1.06k
      assert(input_size == 1);
    562
    1.06k
      assert(output_size == 1);
    563
    1.06k
      ccv_cnnp_model_reshape_t* const self = (ccv_cnnp_model_reshape_t*)super;
    564
    1.06k
      if (CCV_CLI_OUTPUT_LEVEL_IS(CCV_CLI_VERBOSE))
    565
    0
      {
    566
    0
        PRINT(CCV_CLI_VERBOSE, "[cnnp_reshape_build] 1. dim: (%d", self->dim[0]);
    567
    0
        int i;
    568
    0
        for (i = 1; i < CCV_NNC_MAX_DIM_ALLOC && self->dim[i] > 0; i++)
    569
    0
          PRINT(CCV_CLI_VERBOSE, ", %d", self->dim[i]);
    570
    0
        const int count = i;
    571
    0
        PRINT(CCV_CLI_VERBOSE, "), ofs: (%d", self->ofs[0]);
    572
    0
        for (i = 1; i < count; i++)
    573
    0
          PRINT(CCV_CLI_VERBOSE, ", %d", self->ofs[i]);
    574
    0
        PRINT(CCV_CLI_VERBOSE, "), stride: (%d", self->stride[0]);
    575
    0
        for (i = 1; i < count; i++)
    576
    0
          PRINT(CCV_CLI_VERBOSE, ", %d", self->stride[i]);
    577
    0
        PRINT(CCV_CLI_VERBOSE, ")\n");
    578
    0
      }
    579
    1.06k
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    580
    1.06k
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    581
    1.06k
      memcpy(dim, self->dim, sizeof(dim));
    582
    1.06k
      int i, auto_idx = -1;
    583
    1.06k
      size_t known = 1;
    584
    1.06k
      const size_t tensor_count = ccv_nnc_tensor_count(params);
    585
    2.26k
      for (i = 0; i < CCV_NNC_MAX_DIM_ALLOC && dim[i]; i++1.19k)
    586
    1.19k
        if (dim[i] == -1)
    587
    1
          auto_idx = i;
    588
    1.19k
        else
    589
    1.19k
          known *= dim[i];
    590
    1.06k
      if (auto_idx >= 0)
    591
    1
      {
    592
    1
        assert(known > 0 && tensor_count % known == 0);
    593
    1
        dim[auto_idx] = tensor_count / known;
    594
    1
      }
    595
    1.06k
      if (CCV_CLI_OUTPUT_LEVEL_IS(CCV_CLI_VERBOSE))
    596
    0
      {
    597
    0
        PRINT(CCV_CLI_VERBOSE, "[cnnp_reshape_build] 2. input: (%d", params.dim[0]);
    598
    0
        int i;
    599
    0
        for (i = 1; i < CCV_NNC_MAX_DIM_ALLOC && params.dim[i] > 0; i++)
    600
    0
          PRINT(CCV_CLI_VERBOSE, ", %d", params.dim[i]);
    601
    0
        PRINT(CCV_CLI_VERBOSE, ")\n");
    602
    0
      }
    603
    1.06k
      assert(ccv_nnc_dimension_count(dim) <= ccv_nnc_tensor_count(params));
    604
    1.06k
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    605
    1.06k
      int stride_from_dim[CCV_NNC_MAX_DIM_ALLOC];
    606
    1.06k
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    607
    1.06k
      {
    608
    1.06k
        memcpy(params.dim, dim, sizeof(params.dim));
    609
    1.06k
        int* stride;
    610
    1.06k
        if (self->stride[0] == 0)
    611
    1.06k
        {
    612
    1.06k
          ccv_nnc_tensor_get_stride(dim, stride_from_dim);
    613
    1.06k
          stride = stride_from_dim;
    614
    1.06k
        } else
    615
    5
          stride = self->stride;
    616
    1.06k
        if (self->format > 0)
    617
    5
          params.format = self->format;
    618
    1.06k
        outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], self->ofs, stride, params, 0);
    619
    1.06k
      } else {
    620
        // Otherwise, we need to check if it is permute. For permute, we cannot do alias directly.
    621
        // We need to first materialize the permute and then run reshape on top of it, otherwise it will be wrong.
    622
    1
        int old_stride[CCV_NNC_MAX_DIM_ALLOC];
    623
    1
        ccv_nnc_tensor_symbol_alias_params(graph, inputs[0], 0, old_stride);
    624
        // We identify permute by checking if the stride is not in descending order.
    625
        // This also covered "permute" through reshape, rather than using ccv_cnnp_permute directly.
    626
    1
        const int nd = ccv_nnc_tensor_nd(params.dim);
    627
    1
        const int new_nd = ccv_nnc_tensor_nd(dim);
    628
    1
        int i, no_permute = 1;
    629
        // If the new dim has different nd, or we actually have a stride, we need to check if it is no permute or not.
    630
    1
        if (new_nd != nd || (0self->stride[0] != 00 && memcmp(self->stride, old_stride, sizeof(self->stride))0))
    631
    2
          for (i = 1; 1no_permute && i < nd1; i++1)
    632
    1
            if (old_stride[i - 1] < old_stride[i])
    633
    1
              no_permute = 0;
    634
    1
        if (no_permute)
    635
    0
        { // Just straightforward reshape if there is no no permute.
    636
    0
          memcpy(params.dim, dim, sizeof(params.dim));
    637
    0
          int* stride;
    638
    0
          if (self->stride[0] == 0)
    639
    0
          {
    640
    0
            if (new_nd != nd) // Cannot use old stride.
    641
    0
            {
    642
    0
              ccv_nnc_tensor_get_stride(dim, stride_from_dim);
    643
    0
              stride = stride_from_dim;
    644
    0
            } else
    645
    0
              stride = old_stride;
    646
    0
          } else
    647
    0
            stride = self->stride;
    648
    0
          if (self->format > 0)
    649
    0
            params.format = self->format;
    650
    0
          outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], self->ofs, stride, params, 0);
    651
    1
        } else {
    652
          // Otherwise, we first do format transform to plain tensor and then do reshape.
    653
    1
          ccv_nnc_tensor_symbol_t permuted = ccv_nnc_tensor_symbol_new(graph, params, 0);
    654
    1
          ccv_nnc_graph_exec_symbol_new(graph, CMD_FORMAT_TRANSFORM_FORWARD(), TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(permuted), "reshape");
    655
    1
          memcpy(params.dim, dim, sizeof(params.dim));
    656
    1
          int* stride;
    657
    1
          if (self->stride[0] == 0)
    658
    1
          {
    659
    1
            ccv_nnc_tensor_get_stride(dim, stride_from_dim);
    660
    1
            stride = stride_from_dim;
    661
    1
          } else
    662
    0
            stride = self->stride;
    663
    1
          if (self->format > 0)
    664
    0
            params.format = self->format;
    665
          // And then we create alias against the permuted one.
    666
    1
          outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, permuted, self->ofs, stride, params, 0);
    667
    1
        }
    668
    1
      }
    669
    1.06k
    }
    670
    671
    static ccv_cnnp_model_t* _ccv_cnnp_reshape_copy(const ccv_cnnp_model_t* const super, void* const context);
    672
    673
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reshape_isa = {
    674
      .build = _ccv_cnnp_reshape_build,
    675
      .copy = _ccv_cnnp_reshape_copy,
    676
    };
    677
    678
    ccv_cnnp_model_t* ccv_cnnp_reshape(const int format, const int dim[CCV_NNC_MAX_DIM_ALLOC], const int ofs[CCV_NNC_MAX_DIM_ALLOC], const int stride[CCV_NNC_MAX_DIM_ALLOC], const char* const name)
    679
    1.06k
    {
    680
    1.06k
      ccv_cnnp_model_reshape_t* const model_reshape = (ccv_cnnp_model_reshape_t*)cccalloc(1, sizeof(ccv_cnnp_model_reshape_t));
    681
    1.06k
      model_reshape->super.isa = &ccv_cnnp_reshape_isa;
    682
    1.06k
      model_reshape->super.input_size = 1;
    683
    1.06k
      model_reshape->super.outputs = &model_reshape->output;
    684
    1.06k
      model_reshape->super.output_size = 1;
    685
    1.06k
      ccv_cnnp_model_copy_name(&model_reshape->super, name);
    686
    1.06k
      model_reshape->format = format;
    687
    1.06k
      memcpy(model_reshape->dim, dim, sizeof(model_reshape->dim));
    688
    1.06k
      memcpy(model_reshape->ofs, ofs, sizeof(model_reshape->ofs));
    689
    1.06k
      if (stride[0] != 0)
    690
    5
        memcpy(model_reshape->stride, stride, sizeof(model_reshape->stride));
    691
    1.06k
      return (ccv_cnnp_model_t*)model_reshape;
    692
    1.06k
    }
    693
    694
    static ccv_cnnp_model_t* _ccv_cnnp_reshape_copy(const ccv_cnnp_model_t* const super, void* const context)
    695
    1.00k
    {
    696
    1.00k
      const ccv_cnnp_model_reshape_t* const self = (const ccv_cnnp_model_reshape_t*)super;
    697
    1.00k
      return ccv_cnnp_reshape(self->format, self->dim, self->ofs, self->stride, self->super.name);
    698
    1.00k
    }
    699
    700
    typedef struct {
    701
      ccv_cnnp_model_t super;
    702
      ccv_nnc_tensor_symbol_t output;
    703
      int type;
    704
      int begin[CCV_NNC_MAX_DIM_ALLOC];
    705
      int end[CCV_NNC_MAX_DIM_ALLOC];
    706
    } ccv_cnnp_model_pad_t;
    707
    708
    static void _ccv_cnnp_pad_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)
    709
    1
    {
    710
    1
      assert(input_size == 1);
    711
    1
      assert(output_size == 1);
    712
    1
      ccv_cnnp_model_pad_t* const self = (ccv_cnnp_model_pad_t*)super;
    713
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_pad_build] -\n");
    714
    1
      const ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    715
    1
      const int nd = ccv_nnc_tensor_nd(input_params.dim);
    716
    1
      ccv_nnc_tensor_param_t params = input_params;
    717
    1
      int i;
    718
    5
      for (i = 0 ; i < nd; i++4)
    719
    4
        params.dim[i] += self->begin[i] + self->end[i];
    720
    1
      const ccv_nnc_tensor_symbol_t padded = ccv_nnc_tensor_symbol_new(graph, params, 0);
    721
    1
      ccv_nnc_cmd_t pad = CMD_PAD_FORWARD(self->type, (), ());
    722
    1
      memcpy(pad.info.size.dim, self->begin, sizeof(pad.info.size.dim));
    723
    1
      memcpy(pad.info.pad.end, self->end, sizeof(pad.info.pad.end));
    724
    1
      ccv_nnc_graph_exec_symbol_new(graph, pad, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(padded), "pad");
    725
    1
      outputs[0] = padded;
    726
    1
    }
    727
    728
    static ccv_cnnp_model_t* _ccv_cnnp_pad_copy(const ccv_cnnp_model_t* const super, void* const context);
    729
    730
    static const ccv_cnnp_model_vtab_t ccv_cnnp_pad_isa = {
    731
      .build = _ccv_cnnp_pad_build,
    732
      .copy = _ccv_cnnp_pad_copy,
    733
    };
    734
    735
    ccv_cnnp_model_t* ccv_cnnp_pad(const int type, const int begin[CCV_NNC_MAX_DIM_ALLOC], const int end[CCV_NNC_MAX_DIM_ALLOC], const char* const name)
    736
    1
    {
    737
    1
      ccv_cnnp_model_pad_t* const model_pad = (ccv_cnnp_model_pad_t*)cccalloc(1, sizeof(ccv_cnnp_model_pad_t));
    738
    1
      model_pad->super.isa = &ccv_cnnp_pad_isa;
    739
    1
      model_pad->super.input_size = 1;
    740
    1
      model_pad->super.outputs = &model_pad->output;
    741
    1
      model_pad->super.output_size = 1;
    742
    1
      ccv_cnnp_model_copy_name(&model_pad->super, name);
    743
    1
      model_pad->type = type;
    744
    1
      memcpy(model_pad->begin, begin, sizeof(model_pad->begin));
    745
    1
      memcpy(model_pad->end, end, sizeof(model_pad->end));
    746
    1
      return (ccv_cnnp_model_t*)model_pad;
    747
    1
    }
    748
    749
    static ccv_cnnp_model_t* _ccv_cnnp_pad_copy(const ccv_cnnp_model_t* const super, void* const context)
    750
    0
    {
    751
    0
      const ccv_cnnp_model_pad_t* const self = (const ccv_cnnp_model_pad_t*)super;
    752
    0
      return ccv_cnnp_pad(self->type, self->begin, self->end, self->super.name);
    753
    0
    }
    754
    755
    typedef struct {
    756
      ccv_cnnp_model_t super;
    757
      ccv_nnc_tensor_symbol_t output;
    758
    } ccv_cnnp_model_identity_t;
    759
    760
    static void _ccv_cnnp_identity_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)
    761
    0
    {
    762
    0
      assert(input_size == 1);
    763
    0
      assert(output_size == 1);
    764
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_identity_build] -\n");
    765
    0
      outputs[0] = inputs[0];
    766
    0
    }
    767
    768
    static ccv_cnnp_model_t* _ccv_cnnp_identity_copy(const ccv_cnnp_model_t* const super, void* const context);
    769
    770
    static const ccv_cnnp_model_vtab_t ccv_cnnp_identity_isa = {
    771
      .build = _ccv_cnnp_identity_build,
    772
      .copy = _ccv_cnnp_identity_copy,
    773
    };
    774
    775
    ccv_cnnp_model_t* ccv_cnnp_identity(const char* const name)
    776
    0
    {
    777
    0
      ccv_cnnp_model_identity_t* const model_identity = (ccv_cnnp_model_identity_t*)cccalloc(1, sizeof(ccv_cnnp_model_identity_t));
    778
    0
      model_identity->super.isa = &ccv_cnnp_identity_isa;
    779
    0
      model_identity->super.input_size = 1;
    780
    0
      model_identity->super.outputs = &model_identity->output;
    781
    0
      model_identity->super.output_size = 1;
    782
    0
      ccv_cnnp_model_copy_name(&model_identity->super, name);
    783
    0
      return (ccv_cnnp_model_t*)model_identity;
    784
    0
    }
    785
    786
    static ccv_cnnp_model_t* _ccv_cnnp_identity_copy(const ccv_cnnp_model_t* const super, void* const context)
    787
    0
    {
    788
    0
      const ccv_cnnp_model_identity_t* const self = (const ccv_cnnp_model_identity_t*)super;
    789
    0
      return ccv_cnnp_identity(self->super.name);
    790
    0
    }
    791
    792
    typedef struct {
    793
      ccv_cnnp_model_t super;
    794
      ccv_nnc_tensor_symbol_t output;
    795
      int index[CCV_NNC_MAX_DIM_ALLOC];
    796
    } ccv_cnnp_model_permute_t;
    797
    798
    static void _ccv_cnnp_permute_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)
    799
    1
    {
    800
    1
      assert(input_size == 1);
    801
    1
      assert(output_size == 1);
    802
    1
      ccv_cnnp_model_permute_t* const self = (ccv_cnnp_model_permute_t*)super;
    803
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_permute_build] -\n");
    804
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    805
    1
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    806
    1
      const int nd = ccv_nnc_tensor_nd(params.dim);
    807
    1
      int input_dim[CCV_NNC_MAX_DIM_ALLOC];
    808
    1
      memcpy(input_dim, params.dim, sizeof(params.dim));
    809
    1
      int input_stride[CCV_NNC_MAX_DIM_ALLOC] = {};
    810
    1
      int output_stride[CCV_NNC_MAX_DIM_ALLOC] = {};
    811
    1
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If it is not an alias. Find stride and permute.
    812
    0
      {
    813
    0
        ccv_nnc_tensor_get_stride(input_dim, input_stride);
    814
    0
        int i;
    815
    0
        for (i = 0; i < nd; i++)
    816
    0
        {
    817
    0
          const int idx = self->index[i];
    818
    0
          assert(idx >= 0 && idx < nd);
    819
    0
          params.dim[i] = input_dim[idx];
    820
    0
          output_stride[i] = input_stride[idx];
    821
    0
        }
    822
    0
        outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], ccv_nnc_no_ofs, output_stride, params, 0);
    823
    1
      } else {
    824
        // if it is an alias, we can get the stride from it and use that.
    825
    1
        int input_ofs[CCV_NNC_MAX_DIM_ALLOC];
    826
    1
        ccv_nnc_tensor_symbol_alias_params(graph, inputs[0], input_ofs, input_stride);
    827
    1
        assert(input_stride[0] != 0);
    828
    1
        int output_ofs[CCV_NNC_MAX_DIM_ALLOC] = {};
    829
    1
        int i;
    830
    4
        for (i = 0; i < nd; i++3)
    831
    3
        {
    832
    3
          const int idx = self->index[i];
    833
    3
          assert(idx >= 0 && idx < nd);
    834
    3
          params.dim[i] = input_dim[idx];
    835
    3
          output_stride[i] = input_stride[idx];
    836
    3
          output_ofs[i] = input_ofs[idx];
    837
    3
        }
    838
    1
        outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], output_ofs, output_stride, params, 0);
    839
    1
      }
    840
    1
    }
    841
    842
    static ccv_cnnp_model_t* _ccv_cnnp_permute_copy(const ccv_cnnp_model_t* const super, void* const context);
    843
    844
    static const ccv_cnnp_model_vtab_t ccv_cnnp_permute_isa = {
    845
      .build = _ccv_cnnp_permute_build,
    846
      .copy = _ccv_cnnp_permute_copy,
    847
    };
    848
    849
    ccv_cnnp_model_t* ccv_cnnp_permute(const int index[CCV_NNC_MAX_DIM_ALLOC], const char* const name)
    850
    1
    {
    851
    1
      ccv_cnnp_model_permute_t* const model_permute = (ccv_cnnp_model_permute_t*)cccalloc(1, sizeof(ccv_cnnp_model_permute_t));
    852
    1
      model_permute->super.isa = &ccv_cnnp_permute_isa;
    853
    1
      model_permute->super.input_size = 1;
    854
    1
      model_permute->super.outputs = &model_permute->output;
    855
    1
      model_permute->super.output_size = 1;
    856
    1
      ccv_cnnp_model_copy_name(&model_permute->super, name);
    857
    1
      memcpy(model_permute->index, index, sizeof(model_permute->index));
    858
    1
      return (ccv_cnnp_model_t*)model_permute;
    859
    1
    }
    860
    861
    static ccv_cnnp_model_t* _ccv_cnnp_permute_copy(const ccv_cnnp_model_t* const super, void* const context)
    862
    0
    {
    863
    0
      const ccv_cnnp_model_permute_t* const self = (const ccv_cnnp_model_permute_t*)super;
    864
    0
      return ccv_cnnp_permute(self->index, self->super.name);
    865
    0
    }
    866
    867
    typedef struct {
    868
      ccv_cnnp_model_t super;
    869
      int index;
    870
      ccv_nnc_tensor_symbol_t output;
    871
    } ccv_cnnp_model_extract_t;
    872
    873
    static void _ccv_cnnp_extract_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)
    874
    6
    {
    875
    6
      assert(output_size == 1);
    876
    6
      ccv_cnnp_model_extract_t* const self = (ccv_cnnp_model_extract_t*)super;
    877
    6
      PRINT(CCV_CLI_VERBOSE, "[cnnp_extract_build] index: %d\n", self->index);
    878
    6
      outputs[0] = inputs[self->index];
    879
    6
    }
    880
    881
    static ccv_cnnp_model_t* _ccv_cnnp_extract_copy(const ccv_cnnp_model_t* const self, void* const context);
    882
    883
    static const ccv_cnnp_model_vtab_t ccv_cnnp_extract_isa = {
    884
      .build = _ccv_cnnp_extract_build,
    885
      .copy = _ccv_cnnp_extract_copy,
    886
    };
    887
    888
    ccv_cnnp_model_t* ccv_cnnp_extract(const int index, const char* const name)
    889
    6
    {
    890
    6
      ccv_cnnp_model_extract_t* const model_extract = (ccv_cnnp_model_extract_t*)cccalloc(1, sizeof(ccv_cnnp_model_extract_t));
    891
    6
      model_extract->index = index;
    892
    6
      model_extract->super.isa = &ccv_cnnp_extract_isa;
    893
    6
      model_extract->super.input_size = 0;
    894
    6
      model_extract->super.outputs = &model_extract->output;
    895
    6
      model_extract->super.output_size = 1;
    896
    6
      ccv_cnnp_model_copy_name(&model_extract->super, name);
    897
    6
      return (ccv_cnnp_model_t*)model_extract;
    898
    6
    }
    899
    900
    static ccv_cnnp_model_t* _ccv_cnnp_extract_copy(const ccv_cnnp_model_t* const super, void* const context)
    901
    0
    {
    902
    0
      ccv_cnnp_model_extract_t* const self = (ccv_cnnp_model_extract_t*)super;
    903
    0
      return ccv_cnnp_extract(self->index, self->super.name);
    904
    0
    }
    905
    906
    typedef struct {
    907
      ccv_cnnp_model_t super;
    908
      ccv_nnc_tensor_symbol_t output;
    909
    } ccv_cnnp_model_flatten_t;
    910
    911
    static void _ccv_cnnp_flatten_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)
    912
    10
    {
    913
    10
      PRINT(CCV_CLI_VERBOSE, "[cnnp_flatten_build] -\n");
    914
    10
      assert(input_size == 1);
    915
    10
      assert(output_size == 1);
    916
    10
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    917
    10
      ccv_nnc_tensor_param_t output_params = params;
    918
    10
      memset(output_params.dim, 0, sizeof(output_params.dim));
    919
    10
      output_params.dim[0] = ccv_nnc_tensor_get_n(params);
    920
    10
      assert(output_params.dim[0] > 0);
    921
    10
      output_params.dim[1] = ccv_nnc_tensor_count(params) / output_params.dim[0];
    922
    10
      int stride[CCV_NNC_MAX_DIM_ALLOC] = {};
    923
    10
      ccv_nnc_tensor_get_stride(output_params.dim, stride);
    924
    10
      outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], DIM_ALLOC(), stride, output_params, 0);
    925
    10
    }
    926
    927
    static ccv_cnnp_model_t* _ccv_cnnp_flatten_copy(const ccv_cnnp_model_t* const self, void* const context);
    928
    929
    static const ccv_cnnp_model_vtab_t ccv_cnnp_flatten_isa = {
    930
      .build = _ccv_cnnp_flatten_build,
    931
      .copy = _ccv_cnnp_flatten_copy,
    932
    };
    933
    934
    ccv_cnnp_model_t* ccv_cnnp_flatten(const char* const name)
    935
    12
    {
    936
    12
      ccv_cnnp_model_flatten_t* const model_flatten = (ccv_cnnp_model_flatten_t*)cccalloc(1, sizeof(ccv_cnnp_model_flatten_t));
    937
    12
      model_flatten->super.isa = &ccv_cnnp_flatten_isa;
    938
    12
      model_flatten->super.input_size = 1;
    939
    12
      model_flatten->super.outputs = &model_flatten->output;
    940
    12
      model_flatten->super.output_size = 1;
    941
    12
      ccv_cnnp_model_copy_name(&model_flatten->super, name);
    942
    12
      return (ccv_cnnp_model_t*)model_flatten;
    943
    12
    }
    944
    945
    static ccv_cnnp_model_t* _ccv_cnnp_flatten_copy(const ccv_cnnp_model_t* const self, void* const context)
    946
    2
    {
    947
    2
      return ccv_cnnp_flatten(self->name);
    948
    2
    }
    949
    950
    // MARK - Batch Norm Layer
    951
    952
    typedef struct {
    953
      ccv_cnnp_model_t super;
    954
      ccv_nnc_tensor_symbol_t output;
    955
      ccv_nnc_tensor_symbol_t bias;
    956
      ccv_nnc_tensor_symbol_t scale;
    957
      ccv_nnc_graph_exec_symbol_t batch_norm;
    958
      ccv_nnc_cmd_param_t params;
    959
      ccv_array_t* zero_inits;
    960
      ccv_array_t* retainables;
    961
    } ccv_cnnp_model_batch_norm_t;
    962
    963
    static void _ccv_cnnp_batch_norm_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)
    964
    75
    {
    965
    75
      assert(input_size == 1);
    966
    75
      assert(output_size == 1);
    967
    75
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    968
    75
      PRINT(CCV_CLI_VERBOSE, "[cnnp_batch_norm_build] -\n");
    969
    75
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    970
    75
      const int nd = ccv_nnc_tensor_nd(params.dim);
    971
    75
      ccv_nnc_tensor_param_t bias_params = params;
    972
    75
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    973
      // If the accuracy is not enough, bump it to 32-bit floating point.
    974
    75
      if (bias_params.datatype != CCV_32F && bias_params.datatype != CCV_64F16)
    975
    16
        bias_params.datatype = CCV_32F;
    976
    75
      bias_params.dim[0] = nd > 1 ? ccv_nnc_tensor_get_c(params) : params.dim[0]0;
    977
    75
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, params, 0);
    978
      // Both scale and bias are shared between if this model is reused.
    979
    75
      if (!self->scale.graph)
    980
    75
        self->scale = ccv_nnc_tensor_symbol_new(graph, bias_params, "scale");
    981
    75
      if (!self->bias.graph)
    982
    75
        self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    983
    75
      const ccv_nnc_tensor_symbol_t mean = ccv_nnc_tensor_symbol_new(graph, bias_params, "mean");
    984
    75
      const ccv_nnc_tensor_symbol_t var = ccv_nnc_tensor_symbol_new(graph, bias_params, "var");
    985
      // Otherwise, notice mean, var, saved_mean, saved_inv_std are not reused.
    986
    75
      if (!self->zero_inits)
    987
    75
        self->zero_inits = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_t), 0, 0);
    988
    75
      ccv_array_push(self->zero_inits, &mean);
    989
    75
      ccv_array_push(self->zero_inits, &var);
    990
    75
      const ccv_nnc_tensor_symbol_t out_mean = ccv_nnc_tensor_symbol_new(graph, bias_params, "out_mean");
    991
    75
      const ccv_nnc_tensor_symbol_t out_var = ccv_nnc_tensor_symbol_new(graph, bias_params, "out_var");
    992
    75
      if (!self->retainables)
    993
    75
        self->retainables = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_t), 0, 0);
    994
    75
      ccv_array_push(self->retainables, &out_mean);
    995
    75
      ccv_array_push(self->retainables, &out_var);
    996
    75
      const ccv_nnc_tensor_symbol_t saved_mean = ccv_nnc_tensor_symbol_new(graph, bias_params, "saved_mean");
    997
    75
      const ccv_nnc_tensor_symbol_t saved_inv_std = ccv_nnc_tensor_symbol_new(graph, bias_params, "saved_inv_std");
    998
    75
      const int hw = ccv_nnc_tensor_hw(params, ccv_nnc_tensor_nd(params.dim), CCV_NNC_MAX_DIM);
    999
    75
      ccv_nnc_cmd_param_t batch_norm = self->params;
    1000
    75
      batch_norm.bnorm.count = hw >= 0 ? CCV_NNC_MAX_DIM + 1 : 10;
    1001
    75
      int i;
    1002
    75
      batch_norm.bnorm.axis[0] = (params.format == CCV_TENSOR_FORMAT_CHWN) ? 30 : 0;
    1003
    75
      if (hw >= 0)
    1004
    225
        for (i = 0; 75i < CCV_NNC_MAX_DIM; i++150)
    1005
    150
          batch_norm.bnorm.axis[i + 1] = i + hw;
    1006
    75
      self->params = batch_norm;
    1007
    75
      self->batch_norm = ccv_nnc_graph_exec_symbol_new(graph, ccv_nnc_cmd(CCV_NNC_BATCH_NORM_FORWARD, 0, batch_norm, 0), TENSOR_SYMBOL_LIST(inputs[0], self->scale, self->bias, mean, var), TENSOR_SYMBOL_LIST(output, out_mean, out_var, saved_mean, saved_inv_std), "batch_norm");
    1008
    75
      outputs[0] = output;
    1009
    75
    }
    1010
    1011
    static void _ccv_cnnp_batch_norm_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)
    1012
    24
    {
    1013
    24
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1014
    24
      if (self->scale.graph)
    1015
    24
        initializer(context, CMD_RANDOM_UNIFORM_FORWARD(0, 1), ccv_nnc_no_hint, 0, 0, self->scale);
    1016
    24
      if (self->bias.graph)
    1017
    24
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    1018
    24
      int i;
    1019
    24
      if (self->zero_inits)
    1020
    72
        for (i = 0; 24i < self->zero_inits->rnum; i++48)
    1021
    48
          initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, *(ccv_nnc_tensor_symbol_t*)ccv_array_get(self->zero_inits, i));
    1022
    24
    }
    1023
    1024
    static void _ccv_cnnp_batch_norm_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)
    1025
    75
    {
    1026
    75
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1027
    75
      if (self->scale.graph)
    1028
    75
        add_to_array(parameters, self->scale, is_trainable);
    1029
    75
      if (self->bias.graph)
    1030
    75
        add_to_array(parameters, self->bias, is_trainable);
    1031
    75
    }
    1032
    1033
    static void _ccv_cnnp_batch_norm_add_to_output(ccv_cnnp_model_t* const super, const ccv_cnnp_add_to_array_f add_to_array, void* const outputs)
    1034
    75
    {
    1035
    75
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1036
    75
      int i;
    1037
    75
      if (self->retainables)
    1038
    225
        for (i = 0; 75i < self->retainables->rnum; i++150)
    1039
    150
        {
    1040
    150
          const ccv_nnc_tensor_symbol_t symbol = *(ccv_nnc_tensor_symbol_t*)ccv_array_get(self->retainables, i);
    1041
    150
          add_to_array(outputs, symbol, 0);
    1042
    150
        }
    1043
    75
    }
    1044
    1045
    static void _ccv_cnnp_batch_norm_set_is_test(ccv_cnnp_model_t* const super, const int is_test, const ccv_cnnp_cmd_updater_f updater, void* const context)
    1046
    32
    {
    1047
    32
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1048
    32
      if (self->batch_norm.graph)
    1049
    32
      {
    1050
    32
        self->params.bnorm.is_test = is_test;
    1051
    32
        updater(context, self->batch_norm, ccv_nnc_cmd(CCV_NNC_BATCH_NORM_FORWARD, 0, self->params, 0), ccv_nnc_no_hint);
    1052
    32
      }
    1053
    32
    }
    1054
    1055
    static void _ccv_cnnp_batch_norm_deinit(ccv_cnnp_model_t* const super)
    1056
    83
    {
    1057
    83
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1058
    83
      if (self->zero_inits)
    1059
    75
        ccv_array_free(self->zero_inits);
    1060
    83
      if (self->retainables)
    1061
    75
        ccv_array_free(self->retainables);
    1062
    83
    }
    1063
    1064
    static ccv_cnnp_model_t* _ccv_cnnp_batch_norm_copy(const ccv_cnnp_model_t* const super, void* const context);
    1065
    1066
    static const ccv_cnnp_model_vtab_t ccv_cnnp_batch_norm_isa = {
    1067
      .build = _ccv_cnnp_batch_norm_build,
    1068
      .init_states = _ccv_cnnp_batch_norm_init_states,
    1069
      .add_to_parameter = _ccv_cnnp_batch_norm_add_to_parameter,
    1070
      .add_to_output = _ccv_cnnp_batch_norm_add_to_output,
    1071
      .copy = _ccv_cnnp_batch_norm_copy,
    1072
      .set_is_test = _ccv_cnnp_batch_norm_set_is_test,
    1073
      .deinit = _ccv_cnnp_batch_norm_deinit,
    1074
    };
    1075
    1076
    ccv_cnnp_model_t* ccv_cnnp_batch_norm(const float momentum, const float epsilon, const int is_trainable, const char* const name)
    1077
    83
    {
    1078
    83
      ccv_cnnp_model_batch_norm_t* const model_batch_norm = (ccv_cnnp_model_batch_norm_t*)cccalloc(1, sizeof(ccv_cnnp_model_batch_norm_t));
    1079
    83
      model_batch_norm->super.isa = &ccv_cnnp_batch_norm_isa;
    1080
    83
      model_batch_norm->super.input_size = 1;
    1081
    83
      model_batch_norm->super.outputs = &model_batch_norm->output;
    1082
    83
      model_batch_norm->super.output_size = 1;
    1083
    83
      model_batch_norm->super.is_trainable = is_trainable;
    1084
    83
      ccv_cnnp_model_copy_name(&model_batch_norm->super, name);
    1085
    83
      model_batch_norm->scale.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1086
    83
      model_batch_norm->scale.graph = 0;
    1087
    83
      model_batch_norm->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1088
    83
      model_batch_norm->bias.graph = 0;
    1089
    83
      model_batch_norm->params.bnorm.momentum = momentum;
    1090
    83
      model_batch_norm->params.bnorm.epsilon = epsilon;
    1091
    83
      return (ccv_cnnp_model_t*)model_batch_norm;
    1092
    83
    }
    1093
    1094
    static ccv_cnnp_model_t* _ccv_cnnp_batch_norm_copy(const ccv_cnnp_model_t* const super, void* const context)
    1095
    8
    {
    1096
    8
      const ccv_cnnp_model_batch_norm_t* const self = (const ccv_cnnp_model_batch_norm_t*)super;
    1097
    8
      return ccv_cnnp_batch_norm(self->params.bnorm.momentum, self->params.bnorm.epsilon, self->super.is_trainable, self->super.name);
    1098
    8
    }
    1099
    1100
    // MARK - Convolution Layer
    1101
    1102
    typedef struct {
    1103
      ccv_cnnp_model_t super;
    1104
      ccv_nnc_tensor_symbol_t output;
    1105
      ccv_nnc_tensor_symbol_t weights;
    1106
      ccv_nnc_tensor_symbol_t bias;
    1107
      int groups;
    1108
      int filters;
    1109
      int kdim[CCV_NNC_MAX_DIM_ALLOC];
    1110
      int dilation[CCV_NNC_MAX_DIM_ALLOC];
    1111
      int no_bias;
    1112
      int format;
    1113
      ccv_nnc_hint_t hint;
    1114
    } ccv_cnnp_model_convolution_t;
    1115
    1116
    static void _ccv_cnnp_convolution_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)
    1117
    114
    {
    1118
    114
      ccv_cnnp_model_convolution_t* const self = (ccv_cnnp_model_convolution_t*)super;
    1119
    114
      PRINT(CCV_CLI_VERBOSE, "[cnnp_convolution_build] -\n");
    1120
    114
      assert(input_size == 1);
    1121
    114
      assert(output_size == 1);
    1122
    114
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1123
    114
      int i;
    1124
    114
      const int k_nd = ccv_nnc_tensor_nd(self->kdim);
    1125
    114
      const int nd = k_nd + 2;
    1126
    114
      ccv_nnc_tensor_param_t weights_params = params;
    1127
    114
      if (self->format)
    1128
    0
        weights_params.format = self->format;
    1129
    114
      ccv_nnc_tensor_set_n(&weights_params, self->filters);
    1130
    114
      const int a_nd = ccv_nnc_tensor_nd(params.dim);
    1131
    114
      int c;
    1132
    114
      switch (params.format)
    1133
    114
      {
    1134
    15
        case CCV_TENSOR_FORMAT_NHWC:
    1135
    15
          c = params.dim[a_nd - 1];
    1136
    15
          break;
    1137
    99
        case CCV_TENSOR_FORMAT_NCHW:
    1138
    99
          if (a_nd == k_nd + 1)
    1139
    0
            c = params.dim[0];
    1140
    99
          else
    1141
    99
            c = params.dim[a_nd <= 1 ? 00 : 1];
    1142
    99
          break;
    1143
    0
        case CCV_TENSOR_FORMAT_CHWN:
    1144
    0
          c = params.dim[0];
    1145
    0
          break;
    1146
    114
      }
    1147
    114
      assert(c % self->groups == 0);
    1148
    114
      ccv_nnc_tensor_set_c(&weights_params, nd, c / self->groups);
    1149
    114
      int hw = -1;
    1150
    114
      if (weights_params.format == CCV_TENSOR_FORMAT_NHWC || weights_params.format == CCV_TENSOR_FORMAT_CHWN99)
    1151
    15
        hw = 1;
    1152
    99
      else if (weights_params.format == CCV_TENSOR_FORMAT_NCHW)
    1153
    99
        hw = 2;
    1154
    114
      assert(hw >= 0);
    1155
    342
      for (i = 0; 114i < k_nd; i++228)
    1156
    228
        weights_params.dim[i + hw] = self->kdim[i];
    1157
    114
      if (!self->weights.graph)
    1158
    110
        self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    1159
    114
      assert(self->weights.graph == graph);
    1160
    114
      ccv_nnc_tensor_param_t bias_params = params;
    1161
    114
      if (self->format)
    1162
    0
        bias_params.format = self->format;
    1163
    114
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    1164
    114
      bias_params.dim[0] = self->filters;
    1165
    114
      ccv_nnc_cmd_t cmd = CMD_CONVOLUTION_FORWARD(self->groups, self->filters);
    1166
    342
      for (i = 0; i < k_nd; i++228)
    1167
    228
        cmd.info.size.dim[i] = self->kdim[i];
    1168
    114
      cmd.info.size.dim[k_nd] = c;
    1169
    114
      memcpy(cmd.info.convolution.dilation, self->dilation, sizeof(self->dilation));
    1170
    114
      ccv_nnc_tensor_param_t output_params;
    1171
      // Dilate weight size based on the dilation factor.
    1172
    342
      for (i = 0; i < k_nd; i++228)
    1173
    228
        weights_params.dim[i + hw] = (self->kdim[i] - 1) * ccv_max(self->dilation[i], 1) + 1;
    1174
    114
      ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    1175
    114
          params,
    1176
    114
          weights_params,
    1177
    114
          bias_params,
    1178
    114
        }, 3, self->hint, &output_params, 1);
    1179
    114
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1180
    114
      ccv_nnc_graph_exec_symbol_t convolution;
    1181
    114
      if (self->no_bias)
    1182
    10
        convolution = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], self->weights), TENSOR_SYMBOL_LIST(output), "convolution");
    1183
    104
      else {
    1184
    104
        if (!self->bias.graph)
    1185
    100
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    1186
    104
        convolution = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], self->weights, self->bias), TENSOR_SYMBOL_LIST(output), "convolution");
    1187
    104
      }
    1188
    114
      ccv_nnc_graph_exec_symbol_set_hint(graph, convolution, self->hint);
    1189
    114
      outputs[0] = output;
    1190
    114
    }
    1191
    1192
    static void _ccv_cnnp_convolution_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)
    1193
    36
    {
    1194
    36
      ccv_cnnp_model_convolution_t* const self = (ccv_cnnp_model_convolution_t*)super;
    1195
    36
      const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    1196
    36
      const int n = ccv_max(ccv_nnc_tensor_get_n(weight_params), 1);
    1197
    36
      const int count = ccv_nnc_tensor_count(weight_params);
    1198
    36
      const float std = sqrtf(2) / sqrtf(count / n);
    1199
    36
      const float bound = sqrtf(3) * std;
    1200
    36
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    1201
    36
      if (self->bias.graph)
    1202
    36
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    1203
    36
    }
    1204
    1205
    static void _ccv_cnnp_convolution_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)
    1206
    114
    {
    1207
    114
      ccv_cnnp_model_convolution_t* const self = (ccv_cnnp_model_convolution_t*)super;
    1208
    114
      add_to_array(parameters, self->weights, is_trainable);
    1209
    114
      if (self->bias.graph)
    1210
    104
        add_to_array(parameters, self->bias, is_trainable);
    1211
    114
    }
    1212
    1213
    static ccv_cnnp_model_t* _ccv_cnnp_convolution_copy(const ccv_cnnp_model_t* const super, void* const context);
    1214
    1215
    static const ccv_cnnp_model_vtab_t ccv_cnnp_convolution_isa = {
    1216
      .build = _ccv_cnnp_convolution_build,
    1217
      .init_states = _ccv_cnnp_convolution_init_states,
    1218
      .add_to_parameter = _ccv_cnnp_convolution_add_to_parameter,
    1219
      .copy = _ccv_cnnp_convolution_copy,
    1220
    };
    1221
    1222
    ccv_cnnp_model_t* ccv_cnnp_convolution(const int groups, const int filters, const int kdim[CCV_NNC_MAX_DIM_ALLOC], const int dilation[CCV_NNC_MAX_DIM_ALLOC], const int no_bias, ccv_nnc_hint_t hint, const int format, const int is_trainable, const char* const name)
    1223
    126
    {
    1224
    126
      ccv_cnnp_model_convolution_t* const model_convolution = (ccv_cnnp_model_convolution_t*)cccalloc(1, sizeof(ccv_cnnp_model_convolution_t));
    1225
    126
      model_convolution->super.isa = &ccv_cnnp_convolution_isa;
    1226
    126
      model_convolution->super.input_size = 1;
    1227
    126
      model_convolution->super.outputs = &model_convolution->output;
    1228
    126
      model_convolution->super.output_size = 1;
    1229
    126
      model_convolution->super.is_trainable = is_trainable;
    1230
    126
      ccv_cnnp_model_copy_name(&model_convolution->super, name);
    1231
    126
      model_convolution->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1232
    126
      model_convolution->weights.graph = 0;
    1233
    126
      model_convolution->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1234
    126
      model_convolution->bias.graph = 0;
    1235
    126
      model_convolution->groups = groups;
    1236
    126
      model_convolution->filters = filters;
    1237
    126
      memcpy(model_convolution->kdim, kdim, sizeof(model_convolution->kdim));
    1238
    126
      memcpy(model_convolution->dilation, dilation, sizeof(model_convolution->dilation));
    1239
    126
      model_convolution->no_bias = no_bias;
    1240
    126
      model_convolution->hint = hint;
    1241
    126
      model_convolution->format = format;
    1242
    126
      return (ccv_cnnp_model_t*)model_convolution;
    1243
    126
    }
    1244
    1245
    static ccv_cnnp_model_t* _ccv_cnnp_convolution_copy(const ccv_cnnp_model_t* const super, void* const context)
    1246
    16
    {
    1247
    16
      ccv_cnnp_model_convolution_t* const self = (ccv_cnnp_model_convolution_t*)super;
    1248
    16
      return ccv_cnnp_convolution(self->groups, self->filters, self->kdim, self->dilation, self->no_bias, self->hint, self->format, self->super.is_trainable, self->super.name);
    1249
    16
    }
    1250
    1251
    // MARK - Convolution Transpose Layer
    1252
    1253
    typedef struct {
    1254
      ccv_cnnp_model_t super;
    1255
      ccv_nnc_tensor_symbol_t output;
    1256
      ccv_nnc_tensor_symbol_t weights;
    1257
      ccv_nnc_tensor_symbol_t bias;
    1258
      int groups;
    1259
      int filters;
    1260
      int kdim[CCV_NNC_MAX_DIM_ALLOC];
    1261
      int dilation[CCV_NNC_MAX_DIM_ALLOC];
    1262
      int output_padding;
    1263
      int no_bias;
    1264
      int format;
    1265
      ccv_nnc_hint_t hint;
    1266
    } ccv_cnnp_model_convolution_transpose_t;
    1267
    1268
    static void _ccv_cnnp_convolution_transpose_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)
    1269
    0
    {
    1270
    0
      ccv_cnnp_model_convolution_transpose_t* const self = (ccv_cnnp_model_convolution_transpose_t*)super;
    1271
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_convolution_transpose_build] -\n");
    1272
    0
      assert(input_size == 1);
    1273
    0
      assert(output_size == 1);
    1274
    0
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1275
    0
      int i;
    1276
    0
      const int nd = CCV_NNC_MAX_DIM + 2;
    1277
    0
      ccv_nnc_tensor_param_t weights_params = params;
    1278
    0
      if (self->format)
    1279
    0
        weights_params.format = self->format;
    1280
    0
      const int c = ccv_nnc_tensor_get_c(params);
    1281
    0
      ccv_nnc_tensor_set_n(&weights_params, c);
    1282
    0
      assert(c % self->groups == 0);
    1283
    0
      ccv_nnc_tensor_set_c(&weights_params, nd, self->filters / self->groups);
    1284
    0
      const int hw = ccv_nnc_tensor_hw(weights_params, nd, CCV_NNC_MAX_DIM);
    1285
    0
      assert(hw >= 0);
    1286
    0
      for (i = 0; i < CCV_NNC_MAX_DIM; i++)
    1287
    0
        weights_params.dim[i + hw] = self->kdim[i];
    1288
    0
      if (!self->weights.graph)
    1289
    0
        self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    1290
    0
      assert(self->weights.graph == graph);
    1291
    0
      ccv_nnc_tensor_param_t bias_params = params;
    1292
    0
      if (self->format)
    1293
    0
        bias_params.format = self->format;
    1294
    0
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    1295
    0
      bias_params.dim[0] = self->filters;
    1296
    0
      ccv_nnc_cmd_t cmd = CMD_CONVOLUTION_TRANSPOSE_FORWARD(self->groups, self->filters, self->output_padding);
    1297
    0
      for (i = 0; i < CCV_NNC_MAX_DIM; i++)
    1298
    0
        cmd.info.size.dim[i] = self->kdim[i];
    1299
    0
      cmd.info.size.dim[CCV_NNC_MAX_DIM] = c;
    1300
    0
      memcpy(cmd.info.convolution_transpose.dilation, self->dilation, sizeof(self->dilation));
    1301
    0
      ccv_nnc_tensor_param_t output_params;
    1302
      // Dilate weight size based on the dilation factor.
    1303
    0
      for (i = 0; i < CCV_NNC_MAX_DIM; i++)
    1304
    0
        weights_params.dim[i + hw] = (self->kdim[i] - 1) * ccv_max(self->dilation[i], 1) + 1;
    1305
    0
      ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    1306
    0
          params,
    1307
    0
          weights_params,
    1308
    0
          bias_params,
    1309
    0
        }, 3, self->hint, &output_params, 1);
    1310
    0
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1311
    0
      ccv_nnc_graph_exec_symbol_t convolution_transpose;
    1312
    0
      if (self->no_bias)
    1313
    0
        convolution_transpose = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], self->weights), TENSOR_SYMBOL_LIST(output), "convolution_transpose");
    1314
    0
      else {
    1315
    0
        if (!self->bias.graph)
    1316
    0
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    1317
    0
        convolution_transpose = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], self->weights, self->bias), TENSOR_SYMBOL_LIST(output), "convolution_transpose");
    1318
    0
      }
    1319
    0
      ccv_nnc_graph_exec_symbol_set_hint(graph, convolution_transpose, self->hint);
    1320
    0
      outputs[0] = output;
    1321
    0
    }
    1322
    1323
    static void _ccv_cnnp_convolution_transpose_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)
    1324
    0
    {
    1325
    0
      ccv_cnnp_model_convolution_transpose_t* const self = (ccv_cnnp_model_convolution_transpose_t*)super;
    1326
    0
      const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    1327
    0
      const int n = ccv_max(ccv_nnc_tensor_get_n(weight_params), 1);
    1328
    0
      const int count = ccv_nnc_tensor_count(weight_params);
    1329
    0
      const float std = sqrtf(2) / sqrtf(count / n);
    1330
    0
      const float bound = sqrtf(3) * std;
    1331
    0
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    1332
    0
      if (self->bias.graph)
    1333
    0
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    1334
    0
    }
    1335
    1336
    static void _ccv_cnnp_convolution_transpose_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)
    1337
    0
    {
    1338
    0
      ccv_cnnp_model_convolution_transpose_t* const self = (ccv_cnnp_model_convolution_transpose_t*)super;
    1339
    0
      add_to_array(parameters, self->weights, is_trainable);
    1340
    0
      if (self->bias.graph)
    1341
    0
        add_to_array(parameters, self->bias, is_trainable);
    1342
    0
    }
    1343
    1344
    static ccv_cnnp_model_t* _ccv_cnnp_convolution_transpose_copy(const ccv_cnnp_model_t* const super, void* const context);
    1345
    1346
    static const ccv_cnnp_model_vtab_t ccv_cnnp_convolution_transpose_isa = {
    1347
      .build = _ccv_cnnp_convolution_transpose_build,
    1348
      .init_states = _ccv_cnnp_convolution_transpose_init_states,
    1349
      .add_to_parameter = _ccv_cnnp_convolution_transpose_add_to_parameter,
    1350
      .copy = _ccv_cnnp_convolution_transpose_copy,
    1351
    };
    1352
    1353
    ccv_cnnp_model_t* ccv_cnnp_convolution_transpose(const int groups, const int filters, const int kdim[CCV_NNC_MAX_DIM_ALLOC], const int dilation[CCV_NNC_MAX_DIM_ALLOC], const int output_padding, const int no_bias, ccv_nnc_hint_t hint, const int format, const int is_trainable, const char* const name)
    1354
    0
    {
    1355
    0
      ccv_cnnp_model_convolution_transpose_t* const model_convolution_transpose = (ccv_cnnp_model_convolution_transpose_t*)cccalloc(1, sizeof(ccv_cnnp_model_convolution_transpose_t));
    1356
    0
      model_convolution_transpose->super.isa = &ccv_cnnp_convolution_transpose_isa;
    1357
    0
      model_convolution_transpose->super.input_size = 1;
    1358
    0
      model_convolution_transpose->super.outputs = &model_convolution_transpose->output;
    1359
    0
      model_convolution_transpose->super.output_size = 1;
    1360
    0
      model_convolution_transpose->super.is_trainable = is_trainable;
    1361
    0
      ccv_cnnp_model_copy_name(&model_convolution_transpose->super, name);
    1362
    0
      model_convolution_transpose->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1363
    0
      model_convolution_transpose->weights.graph = 0;
    1364
    0
      model_convolution_transpose->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1365
    0
      model_convolution_transpose->bias.graph = 0;
    1366
    0
      model_convolution_transpose->groups = groups;
    1367
    0
      model_convolution_transpose->filters = filters;
    1368
    0
      memcpy(model_convolution_transpose->kdim, kdim, sizeof(model_convolution_transpose->kdim));
    1369
    0
      memcpy(model_convolution_transpose->dilation, dilation, sizeof(model_convolution_transpose->dilation));
    1370
    0
      model_convolution_transpose->output_padding = output_padding;
    1371
    0
      model_convolution_transpose->no_bias = no_bias;
    1372
    0
      model_convolution_transpose->hint = hint;
    1373
    0
      model_convolution_transpose->format = format;
    1374
    0
      return (ccv_cnnp_model_t*)model_convolution_transpose;
    1375
    0
    }
    1376
    1377
    static ccv_cnnp_model_t* _ccv_cnnp_convolution_transpose_copy(const ccv_cnnp_model_t* const super, void* const context)
    1378
    0
    {
    1379
    0
      ccv_cnnp_model_convolution_transpose_t* const self = (ccv_cnnp_model_convolution_transpose_t*)super;
    1380
    0
      return ccv_cnnp_convolution_transpose(self->groups, self->filters, self->kdim, self->dilation, self->output_padding, self->no_bias, self->hint, self->format, self->super.is_trainable, self->super.name);
    1381
    0
    }
    1382
    1383
    // MARK - Dense Layer
    1384
    1385
    typedef struct {
    1386
      ccv_cnnp_model_t super;
    1387
      ccv_nnc_tensor_symbol_t output;
    1388
      ccv_nnc_tensor_symbol_t weights;
    1389
      ccv_nnc_tensor_symbol_t bias;
    1390
      int count;
    1391
      int no_bias;
    1392
      int flags;
    1393
    } ccv_cnnp_model_dense_t;
    1394
    1395
    static void _ccv_cnnp_dense_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)
    1396
    2.33k
    {
    1397
    2.33k
      ccv_cnnp_model_dense_t* const self = (ccv_cnnp_model_dense_t*)super;
    1398
    2.33k
      PRINT(CCV_CLI_VERBOSE, "[cnnp_dense_build] -\n");
    1399
    2.33k
      assert(input_size == 1);
    1400
    2.33k
      assert(output_size == 1);
    1401
    2.33k
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1402
    2.33k
      ccv_nnc_tensor_param_t weights_params = params;
    1403
    2.33k
      memset(weights_params.dim, 0, sizeof(weights_params.dim));
    1404
    2.33k
      weights_params.dim[0] = self->count;
    1405
    2.33k
      weights_params.dim[1] = params.dim[ccv_nnc_tensor_nd(params.dim) - 1];
    1406
    2.33k
      if (!self->weights.graph)
    1407
    2.31k
        self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    1408
    2.33k
      assert(self->weights.graph == graph);
    1409
    2.33k
      ccv_nnc_tensor_param_t bias_params = params;
    1410
    2.33k
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    1411
    2.33k
      bias_params.dim[0] = self->count;
    1412
    2.33k
      ccv_nnc_cmd_t cmd = {0};
    1413
    2.33k
      cmd.cmd = CCV_NNC_GEMM_FORWARD;
    1414
    2.33k
      cmd.info.blas.a[0] = 1;
    1415
    2.33k
      cmd.info.blas.a[1] = 1;
    1416
    2.33k
      cmd.info.blas.transpose_b[0] = 0;
    1417
    2.33k
      cmd.info.blas.transpose_b[1] = 1;
    1418
    2.33k
      cmd.info.blas.flags = self->flags;
    1419
    2.33k
      ccv_nnc_tensor_param_t output_params;
    1420
    2.33k
      ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    1421
    2.33k
          params,
    1422
    2.33k
          weights_params,
    1423
    2.33k
          bias_params,
    1424
    2.33k
        }, 3, ccv_nnc_no_hint, &output_params, 1);
    1425
    2.33k
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1426
    2.33k
      if (self->no_bias)
    1427
    2.08k
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], self->weights), TENSOR_SYMBOL_LIST(output), "dense");
    1428
    246
      else {
    1429
    246
        if (!self->bias.graph)
    1430
    243
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    1431
    246
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], self->weights, self->bias), TENSOR_SYMBOL_LIST(output), "dense");
    1432
    246
      }
    1433
    2.33k
      outputs[0] = output;
    1434
    2.33k
    }
    1435
    1436
    static void _ccv_cnnp_dense_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)
    1437
    79
    {
    1438
    79
      ccv_cnnp_model_dense_t* const self = (ccv_cnnp_model_dense_t*)super;
    1439
    79
      const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    1440
    79
      const int c = weight_params.dim[1];
    1441
    79
      const float std = sqrtf(2) / sqrtf(c);
    1442
    79
      const float bound = sqrtf(3) * std;
    1443
    79
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    1444
    79
      if (self->bias.graph)
    1445
    33
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    1446
    79
    }
    1447
    1448
    static void _ccv_cnnp_dense_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)
    1449
    2.33k
    {
    1450
    2.33k
      ccv_cnnp_model_dense_t* const self = (ccv_cnnp_model_dense_t*)super;
    1451
    2.33k
      add_to_array(parameters, self->weights, is_trainable);
    1452
    2.33k
      if (self->bias.graph)
    1453
    246
        add_to_array(parameters, self->bias, is_trainable);
    1454
    2.33k
    }
    1455
    1456
    static ccv_cnnp_model_t* _ccv_cnnp_dense_copy(const ccv_cnnp_model_t* const super, void* const context);
    1457
    1458
    static const ccv_cnnp_model_vtab_t ccv_cnnp_dense_isa = {
    1459
      .build = _ccv_cnnp_dense_build,
    1460
      .init_states = _ccv_cnnp_dense_init_states,
    1461
      .add_to_parameter = _ccv_cnnp_dense_add_to_parameter,
    1462
      .copy = _ccv_cnnp_dense_copy,
    1463
    };
    1464
    1465
    ccv_cnnp_model_t* ccv_cnnp_dense(const int count, const int no_bias, const int flags, const int is_trainable, const char* const name)
    1466
    2.31k
    {
    1467
    2.31k
      ccv_cnnp_model_dense_t* const model_dense = (ccv_cnnp_model_dense_t*)cccalloc(1, sizeof(ccv_cnnp_model_dense_t));
    1468
    2.31k
      model_dense->super.isa = &ccv_cnnp_dense_isa;
    1469
    2.31k
      model_dense->super.input_size = 1;
    1470
    2.31k
      model_dense->super.outputs = &model_dense->output;
    1471
    2.31k
      model_dense->super.output_size = 1;
    1472
    2.31k
      model_dense->super.is_trainable = is_trainable;
    1473
    2.31k
      ccv_cnnp_model_copy_name(&model_dense->super, name);
    1474
    2.31k
      model_dense->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1475
    2.31k
      model_dense->weights.graph = 0;
    1476
    2.31k
      model_dense->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1477
    2.31k
      model_dense->bias.graph = 0;
    1478
    2.31k
      model_dense->count = count;
    1479
    2.31k
      model_dense->no_bias = no_bias;
    1480
    2.31k
      model_dense->flags = flags;
    1481
    2.31k
      return (ccv_cnnp_model_t*)model_dense;
    1482
    2.31k
    }
    1483
    1484
    static ccv_cnnp_model_t* _ccv_cnnp_dense_copy(const ccv_cnnp_model_t* const super, void* const context)
    1485
    2.20k
    {
    1486
    2.20k
      const ccv_cnnp_model_dense_t* const self = (const ccv_cnnp_model_dense_t*)super;
    1487
    2.20k
      return ccv_cnnp_dense(self->count, self->no_bias, self->flags, self->super.is_trainable, self->super.name);
    1488
    2.20k
    }
    1489
    1490
    // MARK - Pool Layers
    1491
    1492
    typedef struct {
    1493
      ccv_cnnp_model_t super;
    1494
      ccv_nnc_tensor_symbol_t output;
    1495
      int kdim[CCV_NNC_MAX_DIM_ALLOC];
    1496
      ccv_nnc_hint_t hint;
    1497
    } ccv_cnnp_model_pool_t;
    1498
    1499
    static void _ccv_cnnp_max_pool_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)
    1500
    18
    {
    1501
    18
      ccv_cnnp_model_pool_t* const self = (ccv_cnnp_model_pool_t*)super;
    1502
    18
      PRINT(CCV_CLI_VERBOSE, "[cnnp_max_pool_build] -\n");
    1503
    18
      assert(input_size == 1);
    1504
    18
      assert(output_size == 1);
    1505
    18
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1506
    18
      const int hw = ccv_nnc_tensor_hw(params, ccv_nnc_tensor_nd(params.dim), CCV_NNC_MAX_DIM);
    1507
    18
      ccv_nnc_cmd_t cmd;
    1508
    18
      if (hw >= 0 && self->kdim[0] == 0 && self->kdim[1] == 03)
    1509
    3
        cmd = CMD_MAX_POOL_FORWARD(params.dim[hw], params.dim[hw + 1]);
    1510
    15
      else
    1511
    15
        cmd = CMD_MAX_POOL_FORWARD(self->kdim[0], self->kdim[1]);
    1512
    18
      ccv_nnc_tensor_param_t output_params;
    1513
    18
      ccv_nnc_hint_tensor_auto(cmd, &params, 1, self->hint, &output_params, 1);
    1514
    18
      const ccv_nnc_tensor_symbol_t pool_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1515
    18
      const ccv_nnc_graph_exec_symbol_t exec = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(pool_output), "max_pool");
    1516
    18
      ccv_nnc_graph_exec_symbol_set_hint(graph, exec, self->hint);
    1517
    18
      outputs[0] = pool_output;
    1518
    18
    }
    1519
    1520
    static ccv_cnnp_model_t* _ccv_cnnp_max_pool_copy(const ccv_cnnp_model_t* const super, void* const context);
    1521
    1522
    static const ccv_cnnp_model_vtab_t ccv_cnnp_max_pool_isa = {
    1523
      .build = _ccv_cnnp_max_pool_build,
    1524
      .copy = _ccv_cnnp_max_pool_copy,
    1525
    };
    1526
    1527
    ccv_cnnp_model_t* ccv_cnnp_max_pool(const int kdim[CCV_NNC_MAX_DIM_ALLOC], const ccv_nnc_hint_t hint, const char* const name)
    1528
    24
    {
    1529
    24
      ccv_cnnp_model_pool_t* const model_pool = (ccv_cnnp_model_pool_t*)cccalloc(1, sizeof(ccv_cnnp_model_pool_t));
    1530
    24
      model_pool->super.isa = &ccv_cnnp_max_pool_isa;
    1531
    24
      model_pool->super.input_size = 1;
    1532
    24
      model_pool->super.outputs = &model_pool->output;
    1533
    24
      model_pool->super.output_size = 1;
    1534
    24
      ccv_cnnp_model_copy_name(&model_pool->super, name);
    1535
    24
      memcpy(model_pool->kdim, kdim, sizeof(model_pool->kdim));
    1536
    24
      model_pool->hint = hint;
    1537
    24
      return (ccv_cnnp_model_t*)model_pool;
    1538
    24
    }
    1539
    1540
    static ccv_cnnp_model_t* _ccv_cnnp_max_pool_copy(const ccv_cnnp_model_t* const super, void* const context)
    1541
    6
    {
    1542
    6
      const ccv_cnnp_model_pool_t* const self = (const ccv_cnnp_model_pool_t*)super;
    1543
    6
      return ccv_cnnp_max_pool(self->kdim, self->hint, self->super.name);
    1544
    6
    }
    1545
    1546
    static void _ccv_cnnp_average_pool_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)
    1547
    15
    {
    1548
    15
      ccv_cnnp_model_pool_t* const self = (ccv_cnnp_model_pool_t*)super;
    1549
    15
      PRINT(CCV_CLI_VERBOSE, "[cnnp_average_pool_build] -\n");
    1550
    15
      assert(input_size == 1);
    1551
    15
      assert(output_size == 1);
    1552
    15
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1553
    15
      const int hw = ccv_nnc_tensor_hw(params, ccv_nnc_tensor_nd(params.dim), CCV_NNC_MAX_DIM);
    1554
    15
      ccv_nnc_cmd_t cmd;
    1555
    15
      if (hw >= 0 && self->kdim[0] == 0 && self->kdim[1] == 02)
    1556
    2
        cmd = CMD_AVERAGE_POOL_FORWARD(params.dim[hw], params.dim[hw + 1]);
    1557
    13
      else
    1558
    13
        cmd = CMD_AVERAGE_POOL_FORWARD(self->kdim[0], self->kdim[1]);
    1559
    15
      ccv_nnc_tensor_param_t output_params;
    1560
    15
      ccv_nnc_hint_tensor_auto(cmd, &params, 1, self->hint, &output_params, 1);
    1561
    15
      const ccv_nnc_tensor_symbol_t pool_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1562
    15
      const ccv_nnc_graph_exec_symbol_t exec = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(pool_output), "average_pool");
    1563
    15
      ccv_nnc_graph_exec_symbol_set_hint(graph, exec, self->hint);
    1564
    15
      outputs[0] = pool_output;
    1565
    15
    }
    1566
    1567
    static ccv_cnnp_model_t* _ccv_cnnp_average_pool_copy(const ccv_cnnp_model_t* const super, void* const context);
    1568
    1569
    static const ccv_cnnp_model_vtab_t ccv_cnnp_average_pool_isa = {
    1570
      .build = _ccv_cnnp_average_pool_build,
    1571
      .copy = _ccv_cnnp_average_pool_copy,
    1572
    };
    1573
    1574
    ccv_cnnp_model_t* ccv_cnnp_average_pool(const int kdim[CCV_NNC_MAX_DIM_ALLOC], const ccv_nnc_hint_t hint, const char* const name)
    1575
    17
    {
    1576
    17
      ccv_cnnp_model_pool_t* const model_pool = (ccv_cnnp_model_pool_t*)cccalloc(1, sizeof(ccv_cnnp_model_pool_t));
    1577
    17
      model_pool->super.isa = &ccv_cnnp_average_pool_isa;
    1578
    17
      model_pool->super.input_size = 1;
    1579
    17
      model_pool->super.outputs = &model_pool->output;
    1580
    17
      model_pool->super.output_size = 1;
    1581
    17
      ccv_cnnp_model_copy_name(&model_pool->super, name);
    1582
    17
      memcpy(model_pool->kdim, kdim, sizeof(model_pool->kdim));
    1583
    17
      model_pool->hint = hint;
    1584
    17
      return (ccv_cnnp_model_t*)model_pool;
    1585
    17
    }
    1586
    1587
    static ccv_cnnp_model_t* _ccv_cnnp_average_pool_copy(const ccv_cnnp_model_t* const super, void* const context)
    1588
    2
    {
    1589
    2
      const ccv_cnnp_model_pool_t* const self = (const ccv_cnnp_model_pool_t*)super;
    1590
    2
      return ccv_cnnp_average_pool(self->kdim, self->hint, self->super.name);
    1591
    2
    }
    1592
    1593
    // MARK - RELU Layer
    1594
    1595
    typedef struct {
    1596
      ccv_cnnp_model_t super;
    1597
      ccv_nnc_tensor_symbol_t output;
    1598
    } ccv_cnnp_model_relu_t;
    1599
    1600
    static void _ccv_cnnp_relu_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)
    1601
    103
    {
    1602
    103
      PRINT(CCV_CLI_VERBOSE, "[cnnp_relu_build] -\n");
    1603
    103
      assert(input_size == 1);
    1604
    103
      assert(output_size == 1);
    1605
    103
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1606
    103
      ccv_nnc_tensor_param_t output_params;
    1607
    103
      const ccv_nnc_cmd_t relu = CMD_RELU_FORWARD();
    1608
    103
      ccv_nnc_hint_tensor_auto(relu, (ccv_nnc_tensor_param_t []){
    1609
    103
          params,
    1610
    103
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1611
    103
      const ccv_nnc_tensor_symbol_t relu_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1612
    103
      ccv_nnc_graph_exec_symbol_new(graph, relu, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(relu_output), "relu");
    1613
    103
      outputs[0] = relu_output;
    1614
    103
    }
    1615
    1616
    static ccv_cnnp_model_t* _ccv_cnnp_relu_copy(const ccv_cnnp_model_t* const self, void* const context);
    1617
    1618
    static const ccv_cnnp_model_vtab_t ccv_cnnp_relu_isa = {
    1619
      .build = _ccv_cnnp_relu_build,
    1620
      .copy = _ccv_cnnp_relu_copy,
    1621
    };
    1622
    1623
    ccv_cnnp_model_t* ccv_cnnp_relu(const char* const name)
    1624
    120
    {
    1625
    120
      ccv_cnnp_model_relu_t* const model_relu = (ccv_cnnp_model_relu_t*)cccalloc(1, sizeof(ccv_cnnp_model_relu_t));
    1626
    120
      model_relu->super.isa = &ccv_cnnp_relu_isa;
    1627
    120
      model_relu->super.input_size = 1;
    1628
    120
      model_relu->super.outputs = &model_relu->output;
    1629
    120
      model_relu->super.output_size = 1;
    1630
    120
      ccv_cnnp_model_copy_name(&model_relu->super, name);
    1631
    120
      return (ccv_cnnp_model_t*)model_relu;
    1632
    120
    }
    1633
    1634
    static ccv_cnnp_model_t* _ccv_cnnp_relu_copy(const ccv_cnnp_model_t* const self, void* const context)
    1635
    17
    {
    1636
    17
      return ccv_cnnp_relu(self->name);
    1637
    17
    }
    1638
    1639
    // MARK - Sigmoid Layer
    1640
    1641
    typedef struct {
    1642
      ccv_cnnp_model_t super;
    1643
      ccv_nnc_tensor_symbol_t output;
    1644
    } ccv_cnnp_model_sigmoid_t;
    1645
    1646
    static void _ccv_cnnp_sigmoid_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)
    1647
    5
    {
    1648
    5
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sigmoid_build] -\n");
    1649
    5
      assert(input_size == 1);
    1650
    5
      assert(output_size == 1);
    1651
    5
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1652
    5
      ccv_nnc_tensor_param_t output_params;
    1653
    5
      const ccv_nnc_cmd_t sigmoid = CMD_SIGMOID_FORWARD();
    1654
    5
      ccv_nnc_hint_tensor_auto(sigmoid, (ccv_nnc_tensor_param_t []){
    1655
    5
          params,
    1656
    5
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1657
    5
      const ccv_nnc_tensor_symbol_t sigmoid_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1658
    5
      ccv_nnc_graph_exec_symbol_new(graph, sigmoid, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(sigmoid_output), "sigmoid");
    1659
    5
      outputs[0] = sigmoid_output;
    1660
    5
    }
    1661
    1662
    static ccv_cnnp_model_t* _ccv_cnnp_sigmoid_copy(const ccv_cnnp_model_t* const self, void* const context);
    1663
    1664
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sigmoid_isa = {
    1665
      .build = _ccv_cnnp_sigmoid_build,
    1666
      .copy = _ccv_cnnp_sigmoid_copy,
    1667
    };
    1668
    1669
    ccv_cnnp_model_t* ccv_cnnp_sigmoid(const char* const name)
    1670
    5
    {
    1671
    5
      ccv_cnnp_model_sigmoid_t* const model_sigmoid = (ccv_cnnp_model_sigmoid_t*)cccalloc(1, sizeof(ccv_cnnp_model_sigmoid_t));
    1672
    5
      model_sigmoid->super.isa = &ccv_cnnp_sigmoid_isa;
    1673
    5
      model_sigmoid->super.input_size = 1;
    1674
    5
      model_sigmoid->super.outputs = &model_sigmoid->output;
    1675
    5
      model_sigmoid->super.output_size = 1;
    1676
    5
      ccv_cnnp_model_copy_name(&model_sigmoid->super, name);
    1677
    5
      return (ccv_cnnp_model_t*)model_sigmoid;
    1678
    5
    }
    1679
    1680
    static ccv_cnnp_model_t* _ccv_cnnp_sigmoid_copy(const ccv_cnnp_model_t* const self, void* const context)
    1681
    0
    {
    1682
    0
      return ccv_cnnp_sigmoid(self->name);
    1683
    0
    }
    1684
    1685
    // MARK - Tanh Layer
    1686
    1687
    typedef struct {
    1688
      ccv_cnnp_model_t super;
    1689
      ccv_nnc_tensor_symbol_t output;
    1690
    } ccv_cnnp_model_tanh_t;
    1691
    1692
    static void _ccv_cnnp_tanh_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)
    1693
    0
    {
    1694
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_tanh_build] -\n");
    1695
    0
      assert(input_size == 1);
    1696
    0
      assert(output_size == 1);
    1697
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1698
    0
      ccv_nnc_tensor_param_t output_params;
    1699
    0
      const ccv_nnc_cmd_t tanh = CMD_TANH_FORWARD();
    1700
    0
      ccv_nnc_hint_tensor_auto(tanh, (ccv_nnc_tensor_param_t []){
    1701
    0
          params,
    1702
    0
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1703
    0
      const ccv_nnc_tensor_symbol_t tanh_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1704
    0
      ccv_nnc_graph_exec_symbol_new(graph, tanh, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(tanh_output), "tanh");
    1705
    0
      outputs[0] = tanh_output;
    1706
    0
    }
    1707
    1708
    static ccv_cnnp_model_t* _ccv_cnnp_tanh_copy(const ccv_cnnp_model_t* const self, void* const context);
    1709
    1710
    static const ccv_cnnp_model_vtab_t ccv_cnnp_tanh_isa = {
    1711
      .build = _ccv_cnnp_tanh_build,
    1712
      .copy = _ccv_cnnp_tanh_copy,
    1713
    };
    1714
    1715
    ccv_cnnp_model_t* ccv_cnnp_tanh(const char* const name)
    1716
    0
    {
    1717
    0
      ccv_cnnp_model_tanh_t* const model_tanh = (ccv_cnnp_model_tanh_t*)cccalloc(1, sizeof(ccv_cnnp_model_tanh_t));
    1718
    0
      model_tanh->super.isa = &ccv_cnnp_tanh_isa;
    1719
    0
      model_tanh->super.input_size = 1;
    1720
    0
      model_tanh->super.outputs = &model_tanh->output;
    1721
    0
      model_tanh->super.output_size = 1;
    1722
    0
      ccv_cnnp_model_copy_name(&model_tanh->super, name);
    1723
    0
      return (ccv_cnnp_model_t*)model_tanh;
    1724
    0
    }
    1725
    1726
    static ccv_cnnp_model_t* _ccv_cnnp_tanh_copy(const ccv_cnnp_model_t* const self, void* const context)
    1727
    0
    {
    1728
    0
      return ccv_cnnp_tanh(self->name);
    1729
    0
    }
    1730
    1731
    // MARK - Swish Layer
    1732
    1733
    typedef struct {
    1734
      ccv_cnnp_model_t super;
    1735
      ccv_nnc_tensor_symbol_t output;
    1736
      float beta;
    1737
    } ccv_cnnp_model_swish_t;
    1738
    1739
    static void _ccv_cnnp_swish_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)
    1740
    0
    {
    1741
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_swish_build] -\n");
    1742
    0
      assert(input_size == 1);
    1743
    0
      assert(output_size == 1);
    1744
    0
      ccv_cnnp_model_swish_t* const self = (ccv_cnnp_model_swish_t*)super;
    1745
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1746
    0
      ccv_nnc_tensor_param_t output_params;
    1747
    0
      const ccv_nnc_cmd_t swish = CMD_SWISH_FORWARD(self->beta);
    1748
    0
      ccv_nnc_hint_tensor_auto(swish, (ccv_nnc_tensor_param_t []){
    1749
    0
          params,
    1750
    0
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1751
    0
      const ccv_nnc_tensor_symbol_t swish_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1752
    0
      ccv_nnc_graph_exec_symbol_new(graph, swish, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(swish_output), "swish");
    1753
    0
      outputs[0] = swish_output;
    1754
    0
    }
    1755
    1756
    static ccv_cnnp_model_t* _ccv_cnnp_swish_copy(const ccv_cnnp_model_t* const self, void* const context);
    1757
    1758
    static const ccv_cnnp_model_vtab_t ccv_cnnp_swish_isa = {
    1759
      .build = _ccv_cnnp_swish_build,
    1760
      .copy = _ccv_cnnp_swish_copy,
    1761
    };
    1762
    1763
    ccv_cnnp_model_t* ccv_cnnp_swish(const float beta, const char* const name)
    1764
    0
    {
    1765
    0
      ccv_cnnp_model_swish_t* const model_swish = (ccv_cnnp_model_swish_t*)cccalloc(1, sizeof(ccv_cnnp_model_swish_t));
    1766
    0
      model_swish->super.isa = &ccv_cnnp_swish_isa;
    1767
    0
      model_swish->super.input_size = 1;
    1768
    0
      model_swish->super.outputs = &model_swish->output;
    1769
    0
      model_swish->super.output_size = 1;
    1770
    0
      model_swish->beta = beta;
    1771
    0
      ccv_cnnp_model_copy_name(&model_swish->super, name);
    1772
    0
      return (ccv_cnnp_model_t*)model_swish;
    1773
    0
    }
    1774
    1775
    static ccv_cnnp_model_t* _ccv_cnnp_swish_copy(const ccv_cnnp_model_t* const self, void* const context)
    1776
    0
    {
    1777
    0
      const ccv_cnnp_model_swish_t* const swish = (const ccv_cnnp_model_swish_t*)self;
    1778
    0
      return ccv_cnnp_swish(swish->beta, self->name);
    1779
    0
    }
    1780
    1781
    // MARK - GELU Layer
    1782
    1783
    typedef struct {
    1784
      ccv_cnnp_model_t super;
    1785
      ccv_nnc_tensor_symbol_t output;
    1786
      int tanh;
    1787
    } ccv_cnnp_model_gelu_t;
    1788
    1789
    static void _ccv_cnnp_gelu_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)
    1790
    2
    {
    1791
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_gelu_build] -\n");
    1792
    2
      assert(input_size == 1);
    1793
    2
      assert(output_size == 1);
    1794
    2
      ccv_cnnp_model_gelu_t* const self = (ccv_cnnp_model_gelu_t*)super;
    1795
    2
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1796
    2
      ccv_nnc_tensor_param_t output_params;
    1797
    2
      const ccv_nnc_cmd_t gelu = CMD_GELU_FORWARD(self->tanh);
    1798
    2
      ccv_nnc_hint_tensor_auto(gelu, (ccv_nnc_tensor_param_t []){
    1799
    2
          params,
    1800
    2
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1801
    2
      const ccv_nnc_tensor_symbol_t gelu_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1802
    2
      ccv_nnc_graph_exec_symbol_new(graph, gelu, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(gelu_output), "gelu");
    1803
    2
      outputs[0] = gelu_output;
    1804
    2
    }
    1805
    1806
    static ccv_cnnp_model_t* _ccv_cnnp_gelu_copy(const ccv_cnnp_model_t* const self, void* const context);
    1807
    1808
    static const ccv_cnnp_model_vtab_t ccv_cnnp_gelu_isa = {
    1809
      .build = _ccv_cnnp_gelu_build,
    1810
      .copy = _ccv_cnnp_gelu_copy,
    1811
    };
    1812
    1813
    ccv_cnnp_model_t* ccv_cnnp_gelu(const int tanh, const char* const name)
    1814
    1
    {
    1815
    1
      ccv_cnnp_model_gelu_t* const model_gelu = (ccv_cnnp_model_gelu_t*)cccalloc(1, sizeof(ccv_cnnp_model_gelu_t));
    1816
    1
      model_gelu->super.isa = &ccv_cnnp_gelu_isa;
    1817
    1
      model_gelu->super.input_size = 1;
    1818
    1
      model_gelu->super.outputs = &model_gelu->output;
    1819
    1
      model_gelu->super.output_size = 1;
    1820
    1
      model_gelu->tanh = tanh;
    1821
    1
      ccv_cnnp_model_copy_name(&model_gelu->super, name);
    1822
    1
      return (ccv_cnnp_model_t*)model_gelu;
    1823
    1
    }
    1824
    1825
    static ccv_cnnp_model_t* _ccv_cnnp_gelu_copy(const ccv_cnnp_model_t* const super, void* const context)
    1826
    0
    {
    1827
    0
      ccv_cnnp_model_gelu_t* const self = (ccv_cnnp_model_gelu_t*)super;
    1828
    0
      return ccv_cnnp_gelu(self->tanh, self->super.name);
    1829
    0
    }
    1830
    1831
    // MARK - Leaky ReLU Layer
    1832
    1833
    typedef struct {
    1834
      ccv_cnnp_model_t super;
    1835
      ccv_nnc_tensor_symbol_t output;
    1836
      float negative_slope;
    1837
    } ccv_cnnp_model_leaky_relu_t;
    1838
    1839
    static void _ccv_cnnp_leaky_relu_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)
    1840
    0
    {
    1841
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_leaky_relu_build] -\n");
    1842
    0
      assert(input_size == 1);
    1843
    0
      assert(output_size == 1);
    1844
    0
      ccv_cnnp_model_leaky_relu_t* const self = (ccv_cnnp_model_leaky_relu_t*)super;
    1845
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1846
    0
      ccv_nnc_tensor_param_t output_params;
    1847
    0
      const ccv_nnc_cmd_t leaky_relu = CMD_LEAKY_RELU_FORWARD(self->negative_slope);
    1848
    0
      ccv_nnc_hint_tensor_auto(leaky_relu, (ccv_nnc_tensor_param_t []){
    1849
    0
          params,
    1850
    0
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1851
    0
      const ccv_nnc_tensor_symbol_t leaky_relu_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1852
    0
      ccv_nnc_graph_exec_symbol_new(graph, leaky_relu, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(leaky_relu_output), "leaky_relu");
    1853
    0
      outputs[0] = leaky_relu_output;
    1854
    0
    }
    1855
    1856
    static ccv_cnnp_model_t* _ccv_cnnp_leaky_relu_copy(const ccv_cnnp_model_t* const self, void* const context);
    1857
    1858
    static const ccv_cnnp_model_vtab_t ccv_cnnp_leaky_relu_isa = {
    1859
      .build = _ccv_cnnp_leaky_relu_build,
    1860
      .copy = _ccv_cnnp_leaky_relu_copy,
    1861
    };
    1862
    1863
    ccv_cnnp_model_t* ccv_cnnp_leaky_relu(const float negative_slope, const char* const name)
    1864
    0
    {
    1865
    0
      ccv_cnnp_model_leaky_relu_t* const model_leaky_relu = (ccv_cnnp_model_leaky_relu_t*)cccalloc(1, sizeof(ccv_cnnp_model_leaky_relu_t));
    1866
    0
      model_leaky_relu->super.isa = &ccv_cnnp_leaky_relu_isa;
    1867
    0
      model_leaky_relu->super.input_size = 1;
    1868
    0
      model_leaky_relu->super.outputs = &model_leaky_relu->output;
    1869
    0
      model_leaky_relu->super.output_size = 1;
    1870
    0
      model_leaky_relu->negative_slope = negative_slope;
    1871
    0
      ccv_cnnp_model_copy_name(&model_leaky_relu->super, name);
    1872
    0
      return (ccv_cnnp_model_t*)model_leaky_relu;
    1873
    0
    }
    1874
    1875
    static ccv_cnnp_model_t* _ccv_cnnp_leaky_relu_copy(const ccv_cnnp_model_t* const super, void* const context)
    1876
    0
    {
    1877
    0
      ccv_cnnp_model_leaky_relu_t* const self = (ccv_cnnp_model_leaky_relu_t*)super;
    1878
    0
      return ccv_cnnp_leaky_relu(self->negative_slope, self->super.name);
    1879
    0
    }
    1880
    1881
    // MARK - Softmax Layer
    1882
    1883
    typedef struct {
    1884
      ccv_cnnp_model_t super;
    1885
      ccv_nnc_tensor_symbol_t output;
    1886
    } ccv_cnnp_model_softmax_t;
    1887
    1888
    static void _ccv_cnnp_softmax_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)
    1889
    8
    {
    1890
    8
      PRINT(CCV_CLI_VERBOSE, "[cnnp_softmax_build] -\n");
    1891
    8
      assert(input_size == 1);
    1892
    8
      assert(output_size == 1);
    1893
    8
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1894
    8
      ccv_nnc_tensor_param_t output_params;
    1895
    8
      const ccv_nnc_cmd_t softmax = CMD_SOFTMAX_FORWARD();
    1896
    8
      ccv_nnc_hint_tensor_auto(softmax, (ccv_nnc_tensor_param_t []){
    1897
    8
          params,
    1898
    8
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1899
    8
      const ccv_nnc_tensor_symbol_t softmax_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1900
    8
      ccv_nnc_graph_exec_symbol_new(graph, softmax, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(softmax_output), "softmax");
    1901
    8
      outputs[0] = softmax_output;
    1902
    8
    }
    1903
    1904
    static ccv_cnnp_model_t* _ccv_cnnp_softmax_copy(const ccv_cnnp_model_t* const self, void* const context);
    1905
    1906
    static const ccv_cnnp_model_vtab_t ccv_cnnp_softmax_isa = {
    1907
      .build = _ccv_cnnp_softmax_build,
    1908
      .copy = _ccv_cnnp_softmax_copy,
    1909
    };
    1910
    1911
    ccv_cnnp_model_t* ccv_cnnp_softmax(const char* const name)
    1912
    9
    {
    1913
    9
      ccv_cnnp_model_softmax_t* const model_softmax = (ccv_cnnp_model_softmax_t*)cccalloc(1, sizeof(ccv_cnnp_model_softmax_t));
    1914
    9
      model_softmax->super.isa = &ccv_cnnp_softmax_isa;
    1915
    9
      model_softmax->super.input_size = 1;
    1916
    9
      model_softmax->super.outputs = &model_softmax->output;
    1917
    9
      model_softmax->super.output_size = 1;
    1918
    9
      ccv_cnnp_model_copy_name(&model_softmax->super, name);
    1919
    9
      return (ccv_cnnp_model_t*)model_softmax;
    1920
    9
    }
    1921
    1922
    static ccv_cnnp_model_t* _ccv_cnnp_softmax_copy(const ccv_cnnp_model_t* const self, void* const context)
    1923
    1
    {
    1924
    1
      return ccv_cnnp_softmax(self->name);
    1925
    1
    }
    1926
    1927
    // MARK - Add Layer
    1928
    1929
    typedef struct {
    1930
      ccv_cnnp_model_t super;
    1931
      float p;
    1932
      float q;
    1933
      ccv_nnc_tensor_symbol_t output;
    1934
    } ccv_cnnp_model_add_t;
    1935
    1936
    static void _ccv_cnnp_add_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)
    1937
    0
    {
    1938
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_add_build] -\n");
    1939
    0
      const ccv_cnnp_model_add_t* const self = (const ccv_cnnp_model_add_t*)super;
    1940
    0
      assert(input_size == 2);
    1941
    0
      assert(output_size == 1);
    1942
    0
      ccv_nnc_tensor_param_t input_params[2];
    1943
    0
      int i;
    1944
    0
      for (i = 0; i < 2; i++)
    1945
    0
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    1946
    0
      ccv_nnc_tensor_param_t output_params;
    1947
    0
      const ccv_nnc_cmd_t add = CMD_ADD_FORWARD(self->p, self->q);
    1948
    0
      ccv_nnc_hint_tensor_auto(add, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    1949
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1950
    0
      ccv_nnc_graph_exec_symbol_new(graph, add, inputs, input_size, outputs, output_size, "add");
    1951
    0
    }
    1952
    1953
    static ccv_cnnp_model_t* _ccv_cnnp_add_copy(const ccv_cnnp_model_t* const self, void* const context);
    1954
    1955
    static const ccv_cnnp_model_vtab_t ccv_cnnp_add_isa = {
    1956
      .build = _ccv_cnnp_add_build,
    1957
      .copy = _ccv_cnnp_add_copy,
    1958
    };
    1959
    1960
    ccv_cnnp_model_t* ccv_cnnp_add(const float p, const float q, const char* const name)
    1961
    0
    {
    1962
    0
      ccv_cnnp_model_add_t* const model_add = (ccv_cnnp_model_add_t*)cccalloc(1, sizeof(ccv_cnnp_model_add_t));
    1963
    0
      model_add->super.isa = &ccv_cnnp_add_isa;
    1964
    0
      model_add->super.input_size = 2;
    1965
    0
      model_add->super.outputs = &model_add->output;
    1966
    0
      model_add->super.output_size = 1;
    1967
    0
      model_add->p = p;
    1968
    0
      model_add->q = q;
    1969
    0
      ccv_cnnp_model_copy_name(&model_add->super, name);
    1970
    0
      return (ccv_cnnp_model_t*)model_add;
    1971
    0
    }
    1972
    1973
    static ccv_cnnp_model_t* _ccv_cnnp_add_copy(const ccv_cnnp_model_t* const super, void* const context)
    1974
    0
    {
    1975
    0
      const ccv_cnnp_model_add_t* const self = (const ccv_cnnp_model_add_t*)super;
    1976
    0
      return ccv_cnnp_add(self->p, self->q, self->super.name);
    1977
    0
    }
    1978
    1979
    // MARK - Mul Layer
    1980
    1981
    typedef struct {
    1982
      ccv_cnnp_model_t super;
    1983
      ccv_nnc_tensor_symbol_t output;
    1984
      float p;
    1985
    } ccv_cnnp_model_mul_t;
    1986
    1987
    static void _ccv_cnnp_mul_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)
    1988
    6
    {
    1989
    6
      PRINT(CCV_CLI_VERBOSE, "[cnnp_mul_build] -\n");
    1990
    6
      const ccv_cnnp_model_mul_t* const self = (const ccv_cnnp_model_mul_t*)super;
    1991
    6
      assert(input_size == 2);
    1992
    6
      assert(output_size == 1);
    1993
    6
      ccv_nnc_tensor_param_t input_params[2];
    1994
    6
      int i;
    1995
    18
      for (i = 0; i < 2; i++12)
    1996
    12
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    1997
    6
      ccv_nnc_tensor_param_t output_params;
    1998
    6
      const ccv_nnc_cmd_t mul = CMD_MUL_FORWARD(self->p);
    1999
    6
      ccv_nnc_hint_tensor_auto(mul, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    2000
    6
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2001
    6
      ccv_nnc_graph_exec_symbol_new(graph, mul, inputs, input_size, outputs, output_size, "mul");
    2002
    6
    }
    2003
    2004
    static ccv_cnnp_model_t* _ccv_cnnp_mul_copy(const ccv_cnnp_model_t* const self, void* const context);
    2005
    2006
    static const ccv_cnnp_model_vtab_t ccv_cnnp_mul_isa = {
    2007
      .build = _ccv_cnnp_mul_build,
    2008
      .copy = _ccv_cnnp_mul_copy,
    2009
    };
    2010
    2011
    ccv_cnnp_model_t* ccv_cnnp_mul(const float p, const char* const name)
    2012
    5
    {
    2013
    5
      ccv_cnnp_model_mul_t* const model_mul = (ccv_cnnp_model_mul_t*)cccalloc(1, sizeof(ccv_cnnp_model_mul_t));
    2014
    5
      model_mul->super.isa = &ccv_cnnp_mul_isa;
    2015
    5
      model_mul->super.input_size = 2;
    2016
    5
      model_mul->super.outputs = &model_mul->output;
    2017
    5
      model_mul->super.output_size = 1;
    2018
    5
      model_mul->p = p;
    2019
    5
      ccv_cnnp_model_copy_name(&model_mul->super, name);
    2020
    5
      return (ccv_cnnp_model_t*)model_mul;
    2021
    5
    }
    2022
    2023
    static ccv_cnnp_model_t* _ccv_cnnp_mul_copy(const ccv_cnnp_model_t* const super, void* const context)
    2024
    0
    {
    2025
    0
      const ccv_cnnp_model_mul_t* const self = (const ccv_cnnp_model_mul_t*)super;
    2026
    0
      return ccv_cnnp_mul(self->p, self->super.name);
    2027
    0
    }
    2028
    2029
    // MARK - Scalar Mul Layer
    2030
    2031
    typedef struct {
    2032
      ccv_cnnp_model_t super;
    2033
      ccv_nnc_tensor_symbol_t output;
    2034
      float a;
    2035
    } ccv_cnnp_model_scalar_mul_t;
    2036
    2037
    static void _ccv_cnnp_scalar_mul_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)
    2038
    4
    {
    2039
    4
      PRINT(CCV_CLI_VERBOSE, "[cnnp_scalar_mul_build] -\n");
    2040
    4
      assert(input_size == 1);
    2041
    4
      assert(output_size == 1);
    2042
    4
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2043
    4
      ccv_nnc_tensor_param_t output_params;
    2044
    4
      ccv_cnnp_model_scalar_mul_t* const self = (ccv_cnnp_model_scalar_mul_t*)super;
    2045
    4
      const ccv_nnc_cmd_t scalar_mul = CMD_SCALAR_MUL_FORWARD(self->a);
    2046
    4
      ccv_nnc_hint_tensor_auto(scalar_mul, (ccv_nnc_tensor_param_t []){
    2047
    4
          params,
    2048
    4
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    2049
    4
      const ccv_nnc_tensor_symbol_t scalar_mul_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2050
    4
      ccv_nnc_graph_exec_symbol_new(graph, scalar_mul, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(scalar_mul_output), "scalar_mul");
    2051
    4
      outputs[0] = scalar_mul_output;
    2052
    4
    }
    2053
    2054
    static ccv_cnnp_model_t* _ccv_cnnp_scalar_mul_copy(const ccv_cnnp_model_t* const super, void* const context);
    2055
    2056
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scalar_mul_isa = {
    2057
      .build = _ccv_cnnp_scalar_mul_build,
    2058
      .copy = _ccv_cnnp_scalar_mul_copy,
    2059
    };
    2060
    2061
    ccv_cnnp_model_t* ccv_cnnp_scalar_mul(const float a, const char* const name)
    2062
    4
    {
    2063
    4
      ccv_cnnp_model_scalar_mul_t* const model_scalar_mul = (ccv_cnnp_model_scalar_mul_t*)cccalloc(1, sizeof(ccv_cnnp_model_scalar_mul_t));
    2064
    4
      model_scalar_mul->super.isa = &ccv_cnnp_scalar_mul_isa;
    2065
    4
      model_scalar_mul->super.input_size = 1;
    2066
    4
      model_scalar_mul->super.outputs = &model_scalar_mul->output;
    2067
    4
      model_scalar_mul->super.output_size = 1;
    2068
    4
      model_scalar_mul->a = a;
    2069
    4
      ccv_cnnp_model_copy_name(&model_scalar_mul->super, name);
    2070
    4
      return (ccv_cnnp_model_t*)model_scalar_mul;
    2071
    4
    }
    2072
    2073
    static ccv_cnnp_model_t* _ccv_cnnp_scalar_mul_copy(const ccv_cnnp_model_t* const super, void* const context)
    2074
    0
    {
    2075
    0
      const ccv_cnnp_model_scalar_mul_t* const self = (const ccv_cnnp_model_scalar_mul_t*)super;
    2076
    0
      return ccv_cnnp_scalar_mul(self->a, self->super.name);
    2077
    0
    }
    2078
    2079
    // MARK - Div Layer
    2080
    2081
    typedef struct {
    2082
      ccv_cnnp_model_t super;
    2083
      ccv_nnc_tensor_symbol_t output;
    2084
      int reciprocal;
    2085
    } ccv_cnnp_model_div_t;
    2086
    2087
    static void _ccv_cnnp_div_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)
    2088
    2
    {
    2089
    2
      const ccv_cnnp_model_div_t* const self = (const ccv_cnnp_model_div_t*)super;
    2090
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_div_build] -\n");
    2091
    2
      assert(output_size == 1);
    2092
    2
      ccv_nnc_tensor_param_t input_params[2];
    2093
    2
      int i;
    2094
    2
      ccv_nnc_tensor_param_t output_params;
    2095
    2
      const ccv_nnc_cmd_t div = CMD_EWDIV_FORWARD();
    2096
    2
      if (self->reciprocal)
    2097
    1
      {
    2098
    1
        assert(input_size == 1);
    2099
    1
        input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2100
    1
        input_params[1] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2101
    1
        ccv_nnc_hint_tensor_auto(div, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    2102
    1
        outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2103
    1
        ccv_nnc_graph_exec_symbol_new(graph, div, TENSOR_SYMBOL_LIST(NO_TENSOR_SYMBOL, inputs[0]), outputs, output_size, "div");
    2104
    1
      } else {
    2105
    1
        assert(input_size == 2);
    2106
    3
        for (i = 0; 1i < 2; i++2)
    2107
    2
          input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    2108
    1
        ccv_nnc_hint_tensor_auto(div, input_params, input_size, ccv_nnc_no_hint, &output_params, 1);
    2109
    1
        outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2110
    1
        ccv_nnc_graph_exec_symbol_new(graph, div, inputs, input_size, outputs, output_size, "div");
    2111
    1
      }
    2112
    2
    }
    2113
    2114
    static ccv_cnnp_model_t* _ccv_cnnp_div_copy(const ccv_cnnp_model_t* const self, void* const context);
    2115
    2116
    static const ccv_cnnp_model_vtab_t ccv_cnnp_div_isa = {
    2117
      .build = _ccv_cnnp_div_build,
    2118
      .copy = _ccv_cnnp_div_copy,
    2119
    };
    2120
    2121
    ccv_cnnp_model_t* ccv_cnnp_div(const int reciprocal, const char* const name)
    2122
    2
    {
    2123
    2
      ccv_cnnp_model_div_t* const model_div = (ccv_cnnp_model_div_t*)cccalloc(1, sizeof(ccv_cnnp_model_div_t));
    2124
    2
      model_div->super.isa = &ccv_cnnp_div_isa;
    2125
    2
      model_div->super.input_size = reciprocal ? 11 : 21;
    2126
    2
      model_div->super.outputs = &model_div->output;
    2127
    2
      model_div->super.output_size = 1;
    2128
    2
      model_div->reciprocal = reciprocal;
    2129
    2
      ccv_cnnp_model_copy_name(&model_div->super, name);
    2130
    2
      return (ccv_cnnp_model_t*)model_div;
    2131
    2
    }
    2132
    2133
    static ccv_cnnp_model_t* _ccv_cnnp_div_copy(const ccv_cnnp_model_t* const super, void* const context)
    2134
    0
    {
    2135
    0
      const ccv_cnnp_model_div_t* const self = (const ccv_cnnp_model_div_t*)super;
    2136
    0
      return ccv_cnnp_div(self->reciprocal, self->super.name);
    2137
    0
    }
    2138
    2139
    // MARK - Sqrt Layer
    2140
    2141
    typedef struct {
    2142
      ccv_cnnp_model_t super;
    2143
      ccv_nnc_tensor_symbol_t output;
    2144
    } ccv_cnnp_model_sqrt_t;
    2145
    2146
    static void _ccv_cnnp_sqrt_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)
    2147
    0
    {
    2148
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sqrt_build] -\n");
    2149
    0
      assert(output_size == 1);
    2150
    0
      ccv_nnc_tensor_param_t input_params[1];
    2151
    0
      ccv_nnc_tensor_param_t output_params;
    2152
    0
      const ccv_nnc_cmd_t sqrt = CMD_EWSQRT_FORWARD();
    2153
    0
      assert(input_size == 1);
    2154
    0
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2155
    0
      ccv_nnc_hint_tensor_auto(sqrt, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2156
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2157
    0
      ccv_nnc_graph_exec_symbol_new(graph, sqrt, inputs, 1, outputs, output_size, "sqrt");
    2158
    0
    }
    2159
    2160
    static ccv_cnnp_model_t* _ccv_cnnp_sqrt_copy(const ccv_cnnp_model_t* const self, void* const context);
    2161
    2162
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sqrt_isa = {
    2163
      .build = _ccv_cnnp_sqrt_build,
    2164
      .copy = _ccv_cnnp_sqrt_copy,
    2165
    };
    2166
    2167
    ccv_cnnp_model_t* ccv_cnnp_sqrt(const char* const name)
    2168
    0
    {
    2169
    0
      ccv_cnnp_model_sqrt_t* const model_sqrt = (ccv_cnnp_model_sqrt_t*)cccalloc(1, sizeof(ccv_cnnp_model_sqrt_t));
    2170
    0
      model_sqrt->super.isa = &ccv_cnnp_sqrt_isa;
    2171
    0
      model_sqrt->super.input_size = 1;
    2172
    0
      model_sqrt->super.outputs = &model_sqrt->output;
    2173
    0
      model_sqrt->super.output_size = 1;
    2174
    0
      ccv_cnnp_model_copy_name(&model_sqrt->super, name);
    2175
    0
      return (ccv_cnnp_model_t*)model_sqrt;
    2176
    0
    }
    2177
    2178
    static ccv_cnnp_model_t* _ccv_cnnp_sqrt_copy(const ccv_cnnp_model_t* const super, void* const context)
    2179
    0
    {
    2180
    0
      const ccv_cnnp_model_sqrt_t* const self = (const ccv_cnnp_model_sqrt_t*)super;
    2181
    0
      return ccv_cnnp_sqrt(self->super.name);
    2182
    0
    }
    2183
    2184
    // MARK - Log Layer
    2185
    2186
    typedef struct {
    2187
      ccv_cnnp_model_t super;
    2188
      ccv_nnc_tensor_symbol_t output;
    2189
    } ccv_cnnp_model_log_t;
    2190
    2191
    static void _ccv_cnnp_log_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)
    2192
    1
    {
    2193
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_log_build] -\n");
    2194
    1
      assert(output_size == 1);
    2195
    1
      ccv_nnc_tensor_param_t input_params[1];
    2196
    1
      ccv_nnc_tensor_param_t output_params;
    2197
    1
      const ccv_nnc_cmd_t log = CMD_EWLOG_FORWARD();
    2198
    1
      assert(input_size == 1);
    2199
    1
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2200
    1
      ccv_nnc_hint_tensor_auto(log, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2201
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2202
    1
      ccv_nnc_graph_exec_symbol_new(graph, log, inputs, 1, outputs, output_size, "log");
    2203
    1
    }
    2204
    2205
    static ccv_cnnp_model_t* _ccv_cnnp_log_copy(const ccv_cnnp_model_t* const self, void* const context);
    2206
    2207
    static const ccv_cnnp_model_vtab_t ccv_cnnp_log_isa = {
    2208
      .build = _ccv_cnnp_log_build,
    2209
      .copy = _ccv_cnnp_log_copy,
    2210
    };
    2211
    2212
    ccv_cnnp_model_t* ccv_cnnp_log(const char* const name)
    2213
    1
    {
    2214
    1
      ccv_cnnp_model_log_t* const model_log = (ccv_cnnp_model_log_t*)cccalloc(1, sizeof(ccv_cnnp_model_log_t));
    2215
    1
      model_log->super.isa = &ccv_cnnp_log_isa;
    2216
    1
      model_log->super.input_size = 1;
    2217
    1
      model_log->super.outputs = &model_log->output;
    2218
    1
      model_log->super.output_size = 1;
    2219
    1
      ccv_cnnp_model_copy_name(&model_log->super, name);
    2220
    1
      return (ccv_cnnp_model_t*)model_log;
    2221
    1
    }
    2222
    2223
    static ccv_cnnp_model_t* _ccv_cnnp_log_copy(const ccv_cnnp_model_t* const super, void* const context)
    2224
    0
    {
    2225
    0
      return ccv_cnnp_log(super->name);
    2226
    0
    }
    2227
    2228
    // MARK - Pow Layer
    2229
    2230
    typedef struct {
    2231
      ccv_cnnp_model_t super;
    2232
      ccv_nnc_tensor_symbol_t output;
    2233
      ccv_nnc_cmd_param_t params;
    2234
    } ccv_cnnp_model_pow_t;
    2235
    2236
    static void _ccv_cnnp_pow_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)
    2237
    1
    {
    2238
    1
      ccv_cnnp_model_pow_t* const self = (ccv_cnnp_model_pow_t*)super;
    2239
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_pow_build] -\n");
    2240
    1
      assert(input_size == 1);
    2241
    1
      assert(output_size == 1);
    2242
    1
      ccv_nnc_tensor_param_t input_params[1];
    2243
    1
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2244
    1
      ccv_nnc_tensor_param_t output_params;
    2245
    1
      const ccv_nnc_cmd_t pow = ccv_nnc_cmd(CCV_NNC_EWPOW_FORWARD, 0, self->params, 0);
    2246
    1
      ccv_nnc_hint_tensor_auto(pow, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2247
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2248
    1
      ccv_nnc_graph_exec_symbol_new(graph, pow, inputs, input_size, outputs, output_size, "pow");
    2249
    1
    }
    2250
    2251
    static ccv_cnnp_model_t* _ccv_cnnp_pow_copy(const ccv_cnnp_model_t* const self, void* const context);
    2252
    2253
    static const ccv_cnnp_model_vtab_t ccv_cnnp_pow_isa = {
    2254
      .build = _ccv_cnnp_pow_build,
    2255
      .copy = _ccv_cnnp_pow_copy,
    2256
    };
    2257
    2258
    ccv_cnnp_model_t* ccv_cnnp_pow(const float exponent, const char* const name)
    2259
    1
    {
    2260
    1
      ccv_cnnp_model_pow_t* const model_pow = (ccv_cnnp_model_pow_t*)cccalloc(1, sizeof(ccv_cnnp_model_pow_t));
    2261
    1
      model_pow->super.isa = &ccv_cnnp_pow_isa;
    2262
    1
      model_pow->super.input_size = 1;
    2263
    1
      model_pow->super.outputs = &model_pow->output;
    2264
    1
      model_pow->super.output_size = 1;
    2265
    1
      model_pow->params = (ccv_nnc_cmd_param_t){
    2266
    1
        .size = {
    2267
    1
          .dim = { 1, 1, 1 }
    2268
    1
        },
    2269
    1
        .pow = {
    2270
    1
          .exponent = exponent,
    2271
    1
        },
    2272
    1
      };
    2273
    1
      ccv_cnnp_model_copy_name(&model_pow->super, name);
    2274
    1
      return (ccv_cnnp_model_t*)model_pow;
    2275
    1
    }
    2276
    2277
    static ccv_cnnp_model_t* _ccv_cnnp_pow_copy(const ccv_cnnp_model_t* const super, void* const context)
    2278
    0
    {
    2279
    0
      const ccv_cnnp_model_pow_t* const self = (const ccv_cnnp_model_pow_t*)super;
    2280
    0
      return ccv_cnnp_pow(self->params.pow.exponent, super->name);
    2281
    0
    }
    2282
    2283
    // MARK - Sin Layer
    2284
    2285
    typedef struct {
    2286
      ccv_cnnp_model_t super;
    2287
      ccv_nnc_tensor_symbol_t output;
    2288
    } ccv_cnnp_model_sin_t;
    2289
    2290
    static void _ccv_cnnp_sin_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)
    2291
    1
    {
    2292
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sin_build] -\n");
    2293
    1
      assert(output_size == 1);
    2294
    1
      assert(input_size == 1);
    2295
    1
      ccv_nnc_tensor_param_t input_params[1];
    2296
    1
      ccv_nnc_tensor_param_t output_params;
    2297
    1
      const ccv_nnc_cmd_t sin = CMD_EWSIN_FORWARD();
    2298
    1
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2299
    1
      ccv_nnc_hint_tensor_auto(sin, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2300
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2301
    1
      ccv_nnc_graph_exec_symbol_new(graph, sin, inputs, 1, outputs, output_size, "sin");
    2302
    1
    }
    2303
    2304
    static ccv_cnnp_model_t* _ccv_cnnp_sin_copy(const ccv_cnnp_model_t* const self, void* const context);
    2305
    2306
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sin_isa = {
    2307
      .build = _ccv_cnnp_sin_build,
    2308
      .copy = _ccv_cnnp_sin_copy,
    2309
    };
    2310
    2311
    ccv_cnnp_model_t* ccv_cnnp_sin(const char* const name)
    2312
    1
    {
    2313
    1
      ccv_cnnp_model_sin_t* const model_sin = (ccv_cnnp_model_sin_t*)cccalloc(1, sizeof(ccv_cnnp_model_sin_t));
    2314
    1
      model_sin->super.isa = &ccv_cnnp_sin_isa;
    2315
    1
      model_sin->super.input_size = 1;
    2316
    1
      model_sin->super.outputs = &model_sin->output;
    2317
    1
      model_sin->super.output_size = 1;
    2318
    1
      ccv_cnnp_model_copy_name(&model_sin->super, name);
    2319
    1
      return (ccv_cnnp_model_t*)model_sin;
    2320
    1
    }
    2321
    2322
    static ccv_cnnp_model_t* _ccv_cnnp_sin_copy(const ccv_cnnp_model_t* const super, void* const context)
    2323
    0
    {
    2324
    0
      return ccv_cnnp_sin(super->name);
    2325
    0
    }
    2326
    2327
    // MARK - Cos Layer
    2328
    2329
    typedef struct {
    2330
      ccv_cnnp_model_t super;
    2331
      ccv_nnc_tensor_symbol_t output;
    2332
    } ccv_cnnp_model_cos_t;
    2333
    2334
    static void _ccv_cnnp_cos_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)
    2335
    1
    {
    2336
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_cos_build] -\n");
    2337
    1
      assert(output_size == 1);
    2338
    1
      assert(input_size == 1);
    2339
    1
      ccv_nnc_tensor_param_t input_params[1];
    2340
    1
      ccv_nnc_tensor_param_t output_params;
    2341
    1
      const ccv_nnc_cmd_t cos = CMD_EWCOS_FORWARD();
    2342
    1
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2343
    1
      ccv_nnc_hint_tensor_auto(cos, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2344
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2345
    1
      ccv_nnc_graph_exec_symbol_new(graph, cos, inputs, 1, outputs, output_size, "cos");
    2346
    1
    }
    2347
    2348
    static ccv_cnnp_model_t* _ccv_cnnp_cos_copy(const ccv_cnnp_model_t* const self, void* const context);
    2349
    2350
    static const ccv_cnnp_model_vtab_t ccv_cnnp_cos_isa = {
    2351
      .build = _ccv_cnnp_cos_build,
    2352
      .copy = _ccv_cnnp_cos_copy,
    2353
    };
    2354
    2355
    ccv_cnnp_model_t* ccv_cnnp_cos(const char* const name)
    2356
    1
    {
    2357
    1
      ccv_cnnp_model_cos_t* const model_cos = (ccv_cnnp_model_cos_t*)cccalloc(1, sizeof(ccv_cnnp_model_cos_t));
    2358
    1
      model_cos->super.isa = &ccv_cnnp_cos_isa;
    2359
    1
      model_cos->super.input_size = 1;
    2360
    1
      model_cos->super.outputs = &model_cos->output;
    2361
    1
      model_cos->super.output_size = 1;
    2362
    1
      ccv_cnnp_model_copy_name(&model_cos->super, name);
    2363
    1
      return (ccv_cnnp_model_t*)model_cos;
    2364
    1
    }
    2365
    2366
    static ccv_cnnp_model_t* _ccv_cnnp_cos_copy(const ccv_cnnp_model_t* const super, void* const context)
    2367
    0
    {
    2368
    0
      return ccv_cnnp_cos(super->name);
    2369
    0
    }
    2370
    2371
    // MARK - Cmul Layer
    2372
    2373
    typedef struct {
    2374
      ccv_cnnp_model_t super;
    2375
      ccv_nnc_tensor_symbol_t output;
    2376
    } ccv_cnnp_model_cmul_t;
    2377
    2378
    static void _ccv_cnnp_cmul_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)
    2379
    0
    {
    2380
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_cmul_build] -\n");
    2381
    0
      assert(input_size == 2);
    2382
    0
      assert(output_size == 1);
    2383
    0
      ccv_nnc_tensor_param_t input_params[2];
    2384
    0
      int i;
    2385
    0
      for (i = 0; i < 2; i++)
    2386
    0
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    2387
    0
      ccv_nnc_tensor_param_t output_params;
    2388
    0
      const ccv_nnc_cmd_t mul = CMD_CMUL_FORWARD();
    2389
    0
      ccv_nnc_hint_tensor_auto(mul, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    2390
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2391
    0
      ccv_nnc_graph_exec_symbol_new(graph, mul, inputs, input_size, outputs, output_size, "cmul");
    2392
    0
    }
    2393
    2394
    static ccv_cnnp_model_t* _ccv_cnnp_cmul_copy(const ccv_cnnp_model_t* const self, void* const context);
    2395
    2396
    static const ccv_cnnp_model_vtab_t ccv_cnnp_cmul_isa = {
    2397
      .build = _ccv_cnnp_cmul_build,
    2398
      .copy = _ccv_cnnp_cmul_copy,
    2399
    };
    2400
    2401
    ccv_cnnp_model_t* ccv_cnnp_cmul(const char* const name)
    2402
    0
    {
    2403
    0
      ccv_cnnp_model_cmul_t* const model_cmul = (ccv_cnnp_model_cmul_t*)cccalloc(1, sizeof(ccv_cnnp_model_cmul_t));
    2404
    0
      model_cmul->super.isa = &ccv_cnnp_cmul_isa;
    2405
    0
      model_cmul->super.input_size = 2;
    2406
    0
      model_cmul->super.outputs = &model_cmul->output;
    2407
    0
      model_cmul->super.output_size = 1;
    2408
    0
      ccv_cnnp_model_copy_name(&model_cmul->super, name);
    2409
    0
      return (ccv_cnnp_model_t*)model_cmul;
    2410
    0
    }
    2411
    2412
    static ccv_cnnp_model_t* _ccv_cnnp_cmul_copy(const ccv_cnnp_model_t* const super, void* const context)
    2413
    0
    {
    2414
    0
      return ccv_cnnp_cmul(super->name);
    2415
    0
    }
    2416
    2417
    // MARK - Transpose Layer
    2418
    2419
    typedef struct {
    2420
      ccv_cnnp_model_t super;
    2421
      ccv_nnc_tensor_symbol_t output;
    2422
      int transpose[2];
    2423
    } ccv_cnnp_model_transpose_t;
    2424
    2425
    static void _ccv_cnnp_transpose_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)
    2426
    22
    {
    2427
    22
      ccv_cnnp_model_transpose_t* const self = (ccv_cnnp_model_transpose_t*)super;
    2428
    22
      PRINT(CCV_CLI_VERBOSE, "[cnnp_transpose_build] (%d, %d)\n", self->transpose[0], self->transpose[1]);
    2429
    22
      assert(input_size == 1);
    2430
    22
      assert(output_size == 1);
    2431
    22
      if (self->transpose[0] == self->transpose[1])
    2432
    0
      {
    2433
    0
        outputs[0] = inputs[0];
    2434
    0
        return;
    2435
    0
      }
    2436
    22
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2437
    22
      ccv_nnc_tensor_param_t output_params;
    2438
    22
      const ccv_nnc_cmd_t transpose = CMD_TRANSPOSE_FORWARD(self->transpose[0], self->transpose[1]);
    2439
    22
      ccv_nnc_hint_tensor_auto(transpose, (ccv_nnc_tensor_param_t []){
    2440
    22
          params,
    2441
    22
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    2442
    22
      const ccv_nnc_tensor_symbol_t transpose_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2443
    22
      ccv_nnc_graph_exec_symbol_new(graph, transpose, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(transpose_output), "transpose");
    2444
    22
      outputs[0] = transpose_output;
    2445
    22
    }
    2446
    2447
    static ccv_cnnp_model_t* _ccv_cnnp_transpose_copy(const ccv_cnnp_model_t* const super, void* const context);
    2448
    2449
    static const ccv_cnnp_model_vtab_t ccv_cnnp_transpose_isa = {
    2450
      .build = _ccv_cnnp_transpose_build,
    2451
      .copy = _ccv_cnnp_transpose_copy,
    2452
    };
    2453
    2454
    ccv_cnnp_model_t* ccv_cnnp_transpose(const int axis_a, const int axis_b, const char* const name)
    2455
    22
    {
    2456
    22
      ccv_cnnp_model_transpose_t* const model_transpose = (ccv_cnnp_model_transpose_t*)cccalloc(1, sizeof(ccv_cnnp_model_transpose_t));
    2457
    22
      model_transpose->super.isa = &ccv_cnnp_transpose_isa;
    2458
    22
      model_transpose->super.input_size = 1;
    2459
    22
      model_transpose->super.outputs = &model_transpose->output;
    2460
    22
      model_transpose->super.output_size = 1;
    2461
    22
      model_transpose->transpose[0] = axis_a;
    2462
    22
      model_transpose->transpose[1] = axis_b;
    2463
    22
      ccv_cnnp_model_copy_name(&model_transpose->super, name);
    2464
    22
      return (ccv_cnnp_model_t*)model_transpose;
    2465
    22
    }
    2466
    2467
    static ccv_cnnp_model_t* _ccv_cnnp_transpose_copy(const ccv_cnnp_model_t* const super, void* const context)
    2468
    0
    {
    2469
    0
      const ccv_cnnp_model_transpose_t* const self = (const ccv_cnnp_model_transpose_t*)super;
    2470
    0
      return ccv_cnnp_transpose(self->transpose[0], self->transpose[1], self->super.name);
    2471
    0
    }
    2472
    2473
    // MARK - Layer Norm Layer
    2474
    2475
    typedef struct {
    2476
      ccv_cnnp_model_t super;
    2477
      ccv_nnc_tensor_symbol_t output;
    2478
      ccv_nnc_tensor_symbol_t bias;
    2479
      ccv_nnc_tensor_symbol_t scale;
    2480
      ccv_nnc_cmd_param_t params;
    2481
    } ccv_cnnp_model_layer_norm_t;
    2482
    2483
    static void _ccv_cnnp_layer_norm_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)
    2484
    8
    {
    2485
    8
      PRINT(CCV_CLI_VERBOSE, "[cnnp_layer_norm_build] -\n");
    2486
    8
      assert(input_size == 1);
    2487
    8
      assert(output_size == 1);
    2488
    8
      ccv_cnnp_model_layer_norm_t* const self = (ccv_cnnp_model_layer_norm_t*)super;
    2489
    8
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2490
    8
      ccv_nnc_tensor_param_t bias_params = params;
    2491
    8
      const int nd = ccv_nnc_tensor_nd(params.dim);
    2492
    8
      int i;
    2493
    32
      for (i = 0; i < nd; i++24)
    2494
    24
        bias_params.dim[i] = 1;
    2495
    16
      for (i = 0; i < self->params.lnorm.count; i++8)
    2496
    8
        bias_params.dim[self->params.lnorm.axis[i]] = params.dim[self->params.lnorm.axis[i]];
    2497
    8
      if (self->params.lnorm.elementwise_affine)
    2498
    8
      {
    2499
        // Both scale and bias are shared between if this model is reused.
    2500
    8
        if (!self->scale.graph)
    2501
    8
          self->scale = ccv_nnc_tensor_symbol_new(graph, bias_params, "scale");
    2502
    8
        if (!self->bias.graph)
    2503
    8
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    2504
    8
      }
    2505
    8
      const ccv_nnc_cmd_t layer_norm = ccv_nnc_cmd(CCV_NNC_LAYER_NORM_FORWARD, 0, self->params, 0);
    2506
    8
      ccv_nnc_tensor_param_t output_params[3];
    2507
    8
      if (self->params.lnorm.elementwise_affine)
    2508
    8
        ccv_nnc_hint_tensor_auto(layer_norm, (ccv_nnc_tensor_param_t []){
    2509
    8
            params,
    2510
    8
            bias_params,
    2511
    8
            bias_params,
    2512
    8
          }, 3, ccv_nnc_no_hint, output_params, 3);
    2513
    0
      else
    2514
    0
        ccv_nnc_hint_tensor_auto(layer_norm, (ccv_nnc_tensor_param_t []){
    2515
    0
            params,
    2516
    0
          }, 1, ccv_nnc_no_hint, output_params, 3);
    2517
    8
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    2518
    8
      const ccv_nnc_tensor_symbol_t saved_mean = ccv_nnc_tensor_symbol_new(graph, output_params[1], "saved_mean");
    2519
    8
      const ccv_nnc_tensor_symbol_t saved_inv_std = ccv_nnc_tensor_symbol_new(graph, output_params[2], "saved_inv_std");
    2520
    8
      if (self->params.lnorm.elementwise_affine)
    2521
    8
        ccv_nnc_graph_exec_symbol_new(graph, layer_norm, TENSOR_SYMBOL_LIST(inputs[0], self->scale, self->bias), TENSOR_SYMBOL_LIST(output, saved_mean, saved_inv_std), "layer_norm");
    2522
    0
      else
    2523
    0
        ccv_nnc_graph_exec_symbol_new(graph, layer_norm, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(output, saved_mean, saved_inv_std), "layer_norm");
    2524
    8
      outputs[0] = output;
    2525
    8
    }
    2526
    2527
    static void _ccv_cnnp_layer_norm_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)
    2528
    8
    {
    2529
    8
      ccv_cnnp_model_layer_norm_t* const self = (ccv_cnnp_model_layer_norm_t*)super;
    2530
    8
      if (self->scale.graph)
    2531
    8
        initializer(context, CMD_SET_FORWARD(1), ccv_nnc_no_hint, 0, 0, self->scale);
    2532
    8
      if (self->bias.graph)
    2533
    8
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    2534
    8
    }
    2535
    2536
    static void _ccv_cnnp_layer_norm_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)
    2537
    8
    {
    2538
    8
      ccv_cnnp_model_layer_norm_t* const self = (ccv_cnnp_model_layer_norm_t*)super;
    2539
    8
      if (self->scale.graph)
    2540
    8
        add_to_array(parameters, self->scale, is_trainable);
    2541
    8
      if (self->bias.graph)
    2542
    8
        add_to_array(parameters, self->bias, is_trainable);
    2543
    8
    }
    2544
    2545
    static ccv_cnnp_model_t* _ccv_cnnp_layer_norm_copy(const ccv_cnnp_model_t* const super, void* const context);
    2546
    2547
    static const ccv_cnnp_model_vtab_t ccv_cnnp_layer_norm_isa = {
    2548
      .build = _ccv_cnnp_layer_norm_build,
    2549
      .init_states = _ccv_cnnp_layer_norm_init_states,
    2550
      .add_to_parameter = _ccv_cnnp_layer_norm_add_to_parameter,
    2551
      .copy = _ccv_cnnp_layer_norm_copy,
    2552
    };
    2553
    2554
    ccv_cnnp_model_t* ccv_cnnp_layer_norm(const float epsilon, const int axis[CCV_NNC_MAX_DIM_ALLOC], const int axis_count, const int elementwise_affine, const int is_trainable, const char* const name)
    2555
    8
    {
    2556
    8
      ccv_cnnp_model_layer_norm_t* const model_layer_norm = (ccv_cnnp_model_layer_norm_t*)cccalloc(1, sizeof(ccv_cnnp_model_layer_norm_t));
    2557
    8
      model_layer_norm->super.isa = &ccv_cnnp_layer_norm_isa;
    2558
    8
      model_layer_norm->super.input_size = 1;
    2559
    8
      model_layer_norm->super.outputs = &model_layer_norm->output;
    2560
    8
      model_layer_norm->super.output_size = 1;
    2561
    8
      model_layer_norm->super.is_trainable = is_trainable;
    2562
    8
      ccv_cnnp_model_copy_name(&model_layer_norm->super, name);
    2563
    8
      model_layer_norm->scale.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2564
    8
      model_layer_norm->scale.graph = 0;
    2565
    8
      model_layer_norm->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2566
    8
      model_layer_norm->bias.graph = 0;
    2567
    8
      model_layer_norm->params.lnorm.epsilon = epsilon;
    2568
    8
      model_layer_norm->params.lnorm.count = axis_count;
    2569
    8
      model_layer_norm->params.lnorm.elementwise_affine = elementwise_affine;
    2570
    8
      memcpy(model_layer_norm->params.lnorm.axis, axis, sizeof(int) * axis_count);
    2571
    8
      return (ccv_cnnp_model_t*)model_layer_norm;
    2572
    8
    }
    2573
    2574
    static ccv_cnnp_model_t* _ccv_cnnp_layer_norm_copy(const ccv_cnnp_model_t* const super, void* const context)
    2575
    0
    {
    2576
    0
      const ccv_cnnp_model_layer_norm_t* const self = (const ccv_cnnp_model_layer_norm_t*)super;
    2577
    0
      return ccv_cnnp_layer_norm(self->params.lnorm.epsilon, self->params.lnorm.axis, self->params.lnorm.count, self->params.lnorm.elementwise_affine, self->super.is_trainable, self->super.name);
    2578
    0
    }
    2579
    2580
    // MARK - Group Norm Layer
    2581
    2582
    typedef struct {
    2583
      ccv_cnnp_model_t super;
    2584
      ccv_nnc_tensor_symbol_t output;
    2585
      ccv_nnc_tensor_symbol_t bias;
    2586
      ccv_nnc_tensor_symbol_t scale;
    2587
      ccv_nnc_cmd_param_t params;
    2588
    } ccv_cnnp_model_group_norm_t;
    2589
    2590
    static void _ccv_cnnp_group_norm_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)
    2591
    0
    {
    2592
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_group_norm_build] -\n");
    2593
    0
      assert(input_size == 1);
    2594
    0
      assert(output_size == 1);
    2595
    0
      ccv_cnnp_model_group_norm_t* const self = (ccv_cnnp_model_group_norm_t*)super;
    2596
    0
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2597
    0
      ccv_nnc_tensor_param_t bias_params = params;
    2598
    0
      const int nd = ccv_nnc_tensor_nd(params.dim);
    2599
    0
      int i;
    2600
    0
      for (i = 0; i < nd; i++)
    2601
    0
        bias_params.dim[i] = 1;
    2602
    0
      bias_params.dim[self->params.gnorm.group_axis] = params.dim[self->params.gnorm.group_axis];
    2603
    0
      if (self->params.gnorm.elementwise_affine)
    2604
    0
      {
    2605
        // Both scale and bias are shared between if this model is reused.
    2606
    0
        if (!self->scale.graph)
    2607
    0
          self->scale = ccv_nnc_tensor_symbol_new(graph, bias_params, "scale");
    2608
    0
        if (!self->bias.graph)
    2609
    0
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    2610
    0
      }
    2611
    0
      const ccv_nnc_cmd_t group_norm = ccv_nnc_cmd(CCV_NNC_GROUP_NORM_FORWARD, 0, self->params, 0);
    2612
    0
      ccv_nnc_tensor_param_t output_params[3];
    2613
    0
      if (self->params.gnorm.elementwise_affine)
    2614
    0
        ccv_nnc_hint_tensor_auto(group_norm, (ccv_nnc_tensor_param_t []){
    2615
    0
            params,
    2616
    0
            bias_params,
    2617
    0
            bias_params,
    2618
    0
          }, 3, ccv_nnc_no_hint, output_params, 3);
    2619
    0
      else
    2620
    0
        ccv_nnc_hint_tensor_auto(group_norm, (ccv_nnc_tensor_param_t []){
    2621
    0
            params,
    2622
    0
          }, 1, ccv_nnc_no_hint, output_params, 3);
    2623
    0
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    2624
    0
      const ccv_nnc_tensor_symbol_t saved_mean = ccv_nnc_tensor_symbol_new(graph, output_params[1], "saved_mean");
    2625
    0
      const ccv_nnc_tensor_symbol_t saved_inv_std = ccv_nnc_tensor_symbol_new(graph, output_params[2], "saved_inv_std");
    2626
    0
      if (self->params.gnorm.elementwise_affine)
    2627
    0
        ccv_nnc_graph_exec_symbol_new(graph, group_norm, TENSOR_SYMBOL_LIST(inputs[0], self->scale, self->bias), TENSOR_SYMBOL_LIST(output, saved_mean, saved_inv_std), "group_norm");
    2628
    0
      else
    2629
    0
        ccv_nnc_graph_exec_symbol_new(graph, group_norm, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(output, saved_mean, saved_inv_std), "group_norm");
    2630
    0
      outputs[0] = output;
    2631
    0
    }
    2632
    2633
    static void _ccv_cnnp_group_norm_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)
    2634
    0
    {
    2635
    0
      ccv_cnnp_model_group_norm_t* const self = (ccv_cnnp_model_group_norm_t*)super;
    2636
    0
      if (self->scale.graph)
    2637
    0
        initializer(context, CMD_SET_FORWARD(1), ccv_nnc_no_hint, 0, 0, self->scale);
    2638
    0
      if (self->bias.graph)
    2639
    0
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    2640
    0
    }
    2641
    2642
    static void _ccv_cnnp_group_norm_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)
    2643
    0
    {
    2644
    0
      ccv_cnnp_model_group_norm_t* const self = (ccv_cnnp_model_group_norm_t*)super;
    2645
    0
      if (self->scale.graph)
    2646
    0
        add_to_array(parameters, self->scale, is_trainable);
    2647
    0
      if (self->bias.graph)
    2648
    0
        add_to_array(parameters, self->bias, is_trainable);
    2649
    0
    }
    2650
    2651
    static ccv_cnnp_model_t* _ccv_cnnp_group_norm_copy(const ccv_cnnp_model_t* const super, void* const context);
    2652
    2653
    static const ccv_cnnp_model_vtab_t ccv_cnnp_group_norm_isa = {
    2654
      .build = _ccv_cnnp_group_norm_build,
    2655
      .init_states = _ccv_cnnp_group_norm_init_states,
    2656
      .add_to_parameter = _ccv_cnnp_group_norm_add_to_parameter,
    2657
      .copy = _ccv_cnnp_group_norm_copy,
    2658
    };
    2659
    2660
    ccv_cnnp_model_t* ccv_cnnp_group_norm(const int group_axis, const int groups, const float epsilon, const int reduce_axis[CCV_NNC_MAX_DIM_ALLOC], const int axis_count, const int elementwise_affine, const int is_trainable, const char* const name)
    2661
    0
    {
    2662
    0
      ccv_cnnp_model_group_norm_t* const model_group_norm = (ccv_cnnp_model_group_norm_t*)cccalloc(1, sizeof(ccv_cnnp_model_group_norm_t));
    2663
    0
      model_group_norm->super.isa = &ccv_cnnp_group_norm_isa;
    2664
    0
      model_group_norm->super.input_size = 1;
    2665
    0
      model_group_norm->super.outputs = &model_group_norm->output;
    2666
    0
      model_group_norm->super.output_size = 1;
    2667
    0
      model_group_norm->super.is_trainable = is_trainable;
    2668
    0
      ccv_cnnp_model_copy_name(&model_group_norm->super, name);
    2669
    0
      model_group_norm->scale.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2670
    0
      model_group_norm->scale.graph = 0;
    2671
    0
      model_group_norm->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2672
    0
      model_group_norm->bias.graph = 0;
    2673
    0
      model_group_norm->params.gnorm.group_axis = group_axis;
    2674
    0
      model_group_norm->params.gnorm.groups = groups;
    2675
    0
      model_group_norm->params.gnorm.epsilon = epsilon;
    2676
    0
      model_group_norm->params.gnorm.reduce_count = axis_count;
    2677
    0
      model_group_norm->params.gnorm.elementwise_affine = elementwise_affine;
    2678
    0
      memcpy(model_group_norm->params.gnorm.reduce_axis, reduce_axis, sizeof(int) * axis_count);
    2679
    0
      return (ccv_cnnp_model_t*)model_group_norm;
    2680
    0
    }
    2681
    2682
    static ccv_cnnp_model_t* _ccv_cnnp_group_norm_copy(const ccv_cnnp_model_t* const super, void* const context)
    2683
    0
    {
    2684
    0
      const ccv_cnnp_model_group_norm_t* const self = (const ccv_cnnp_model_group_norm_t*)super;
    2685
    0
      return ccv_cnnp_group_norm(self->params.gnorm.group_axis, self->params.gnorm.groups, self->params.gnorm.epsilon, self->params.gnorm.reduce_axis, self->params.gnorm.reduce_count, self->params.gnorm.elementwise_affine, self->super.is_trainable, self->super.name);
    2686
    0
    }
    2687
    2688
    // MARK - RMSNorm Layer
    2689
    2690
    typedef struct {
    2691
      ccv_cnnp_model_t super;
    2692
      ccv_nnc_tensor_symbol_t output;
    2693
      ccv_nnc_tensor_symbol_t scale;
    2694
      ccv_nnc_cmd_param_t params;
    2695
    } ccv_cnnp_model_rmsnorm_t;
    2696
    2697
    static void _ccv_cnnp_rmsnorm_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)
    2698
    0
    {
    2699
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_rmsnorm_build] -\n");
    2700
    0
      assert(input_size == 1);
    2701
    0
      assert(output_size == 1);
    2702
    0
      ccv_cnnp_model_rmsnorm_t* const self = (ccv_cnnp_model_rmsnorm_t*)super;
    2703
    0
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2704
    0
      ccv_nnc_tensor_param_t scale_params = params;
    2705
    0
      const int nd = ccv_nnc_tensor_nd(params.dim);
    2706
    0
      int i;
    2707
    0
      for (i = 0; i < nd; i++)
    2708
    0
        scale_params.dim[i] = 1;
    2709
    0
      for (i = 0; i < self->params.rmsnorm.count; i++)
    2710
    0
        scale_params.dim[self->params.rmsnorm.axis[i]] = params.dim[self->params.rmsnorm.axis[i]];
    2711
      // Both scale and bias are shared between if this model is reused.
    2712
    0
      if (self->params.rmsnorm.elementwise_affine)
    2713
    0
      {
    2714
    0
        if (!self->scale.graph)
    2715
    0
          self->scale = ccv_nnc_tensor_symbol_new(graph, scale_params, "scale");
    2716
    0
      }
    2717
    0
      const ccv_nnc_cmd_t rmsnorm = ccv_nnc_cmd(CCV_NNC_RMSNORM_FORWARD, 0, self->params, 0);
    2718
    0
      ccv_nnc_tensor_param_t output_params[2];
    2719
    0
      if (self->params.rmsnorm.elementwise_affine)
    2720
    0
        ccv_nnc_hint_tensor_auto(rmsnorm, (ccv_nnc_tensor_param_t []){
    2721
    0
            params,
    2722
    0
            scale_params,
    2723
    0
          }, 2, ccv_nnc_no_hint, output_params, 2);
    2724
    0
      else
    2725
    0
        ccv_nnc_hint_tensor_auto(rmsnorm, (ccv_nnc_tensor_param_t []){
    2726
    0
            params,
    2727
    0
          }, 1, ccv_nnc_no_hint, output_params, 2);
    2728
    0
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    2729
    0
      const ccv_nnc_tensor_symbol_t saved_inv_std = ccv_nnc_tensor_symbol_new(graph, output_params[1], "saved_inv_std");
    2730
    0
      if (self->params.rmsnorm.elementwise_affine)
    2731
    0
        ccv_nnc_graph_exec_symbol_new(graph, rmsnorm, TENSOR_SYMBOL_LIST(inputs[0], self->scale), TENSOR_SYMBOL_LIST(output, saved_inv_std), "rmsnorm");
    2732
    0
      else
    2733
    0
        ccv_nnc_graph_exec_symbol_new(graph, rmsnorm, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(output, saved_inv_std), "rmsnorm");
    2734
    0
      outputs[0] = output;
    2735
    0
    }
    2736
    2737
    static void _ccv_cnnp_rmsnorm_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)
    2738
    0
    {
    2739
    0
      ccv_cnnp_model_rmsnorm_t* const self = (ccv_cnnp_model_rmsnorm_t*)super;
    2740
    0
      if (self->scale.graph)
    2741
    0
        initializer(context, CMD_SET_FORWARD(1), ccv_nnc_no_hint, 0, 0, self->scale);
    2742
    0
    }
    2743
    2744
    static void _ccv_cnnp_rmsnorm_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)
    2745
    0
    {
    2746
    0
      ccv_cnnp_model_rmsnorm_t* const self = (ccv_cnnp_model_rmsnorm_t*)super;
    2747
    0
      if (self->scale.graph)
    2748
    0
        add_to_array(parameters, self->scale, is_trainable);
    2749
    0
    }
    2750
    2751
    static ccv_cnnp_model_t* _ccv_cnnp_rmsnorm_copy(const ccv_cnnp_model_t* const super, void* const context);
    2752
    2753
    static const ccv_cnnp_model_vtab_t ccv_cnnp_rmsnorm_isa = {
    2754
      .build = _ccv_cnnp_rmsnorm_build,
    2755
      .init_states = _ccv_cnnp_rmsnorm_init_states,
    2756
      .add_to_parameter = _ccv_cnnp_rmsnorm_add_to_parameter,
    2757
      .copy = _ccv_cnnp_rmsnorm_copy,
    2758
    };
    2759
    2760
    ccv_cnnp_model_t* ccv_cnnp_rmsnorm(const float epsilon, const int axis[CCV_NNC_MAX_DIM_ALLOC], const int axis_count, const int elementwise_affine, const int is_trainable, const char* const name)
    2761
    0
    {
    2762
    0
      ccv_cnnp_model_rmsnorm_t* const model_rmsnorm = (ccv_cnnp_model_rmsnorm_t*)cccalloc(1, sizeof(ccv_cnnp_model_rmsnorm_t));
    2763
    0
      model_rmsnorm->super.isa = &ccv_cnnp_rmsnorm_isa;
    2764
    0
      model_rmsnorm->super.input_size = 1;
    2765
    0
      model_rmsnorm->super.outputs = &model_rmsnorm->output;
    2766
    0
      model_rmsnorm->super.output_size = 1;
    2767
    0
      model_rmsnorm->super.is_trainable = is_trainable;
    2768
    0
      ccv_cnnp_model_copy_name(&model_rmsnorm->super, name);
    2769
    0
      model_rmsnorm->scale.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2770
    0
      model_rmsnorm->scale.graph = 0;
    2771
    0
      model_rmsnorm->params.rmsnorm.epsilon = epsilon;
    2772
    0
      model_rmsnorm->params.rmsnorm.count = axis_count;
    2773
    0
      model_rmsnorm->params.rmsnorm.elementwise_affine = elementwise_affine;
    2774
    0
      memcpy(model_rmsnorm->params.lnorm.axis, axis, sizeof(int) * axis_count);
    2775
    0
      return (ccv_cnnp_model_t*)model_rmsnorm;
    2776
    0
    }
    2777
    2778
    static ccv_cnnp_model_t* _ccv_cnnp_rmsnorm_copy(const ccv_cnnp_model_t* const super, void* const context)
    2779
    0
    {
    2780
    0
      const ccv_cnnp_model_rmsnorm_t* const self = (const ccv_cnnp_model_rmsnorm_t*)super;
    2781
    0
      return ccv_cnnp_rmsnorm(self->params.rmsnorm.epsilon, self->params.rmsnorm.axis, self->params.rmsnorm.count, self->params.rmsnorm.elementwise_affine, self->super.is_trainable, self->super.name);
    2782
    0
    }
    2783
    2784
    // MARK - Batched Matrix Mul Layer
    2785
    2786
    typedef struct {
    2787
      ccv_cnnp_model_t super;
    2788
      ccv_nnc_tensor_symbol_t output;
    2789
      int transpose_a[2];
    2790
      int transpose_b[2];
    2791
      int flags;
    2792
    } ccv_cnnp_model_matmul_t;
    2793
    2794
    static void _ccv_cnnp_matmul_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)
    2795
    10
    {
    2796
    10
      PRINT(CCV_CLI_VERBOSE, "[cnnp_matmul_build] -\n");
    2797
    10
      assert(input_size == 2);
    2798
    10
      assert(output_size == 1);
    2799
    10
      ccv_cnnp_model_matmul_t* const self = (ccv_cnnp_model_matmul_t*)super;
    2800
    10
      ccv_nnc_tensor_param_t a_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2801
    10
      ccv_nnc_tensor_param_t b_params = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    2802
    10
      ccv_nnc_tensor_param_t output_params;
    2803
    10
      ccv_nnc_cmd_t matmul = CMD_GEMM_FORWARD(self->transpose_a, self->transpose_b);
    2804
    10
      matmul.info.blas.flags = self->flags;
    2805
    10
      ccv_nnc_hint_tensor_auto(matmul, (ccv_nnc_tensor_param_t []){
    2806
    10
          a_params,
    2807
    10
          b_params,
    2808
    10
        }, 2, ccv_nnc_no_hint, &output_params, 1);
    2809
    10
      const ccv_nnc_tensor_symbol_t matmul_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2810
    10
      ccv_nnc_graph_exec_symbol_new(graph, matmul, inputs, input_size, TENSOR_SYMBOL_LIST(matmul_output), "matmul");
    2811
    10
      outputs[0] = matmul_output;
    2812
    10
    }
    2813
    2814
    static ccv_cnnp_model_t* _ccv_cnnp_matmul_copy(const ccv_cnnp_model_t* const super, void* const context);
    2815
    2816
    static const ccv_cnnp_model_vtab_t ccv_cnnp_matmul_isa = {
    2817
      .build = _ccv_cnnp_matmul_build,
    2818
      .copy = _ccv_cnnp_matmul_copy,
    2819
    };
    2820
    2821
    ccv_cnnp_model_t* ccv_cnnp_matmul(const int transpose_a[2], const int transpose_b[2], const int flags, const char* const name)
    2822
    10
    {
    2823
    10
      ccv_cnnp_model_matmul_t* const model_matmul = (ccv_cnnp_model_matmul_t*)cccalloc(1, sizeof(ccv_cnnp_model_matmul_t));
    2824
    10
      model_matmul->super.isa = &ccv_cnnp_matmul_isa;
    2825
    10
      model_matmul->super.input_size = 2;
    2826
    10
      model_matmul->super.outputs = &model_matmul->output;
    2827
    10
      model_matmul->super.output_size = 1;
    2828
    10
      model_matmul->transpose_a[0] = transpose_a[0];
    2829
    10
      model_matmul->transpose_a[1] = transpose_a[1];
    2830
    10
      model_matmul->transpose_b[0] = transpose_b[0];
    2831
    10
      model_matmul->transpose_b[1] = transpose_b[1];
    2832
    10
      model_matmul->flags = flags;
    2833
    10
      ccv_cnnp_model_copy_name(&model_matmul->super, name);
    2834
    10
      return (ccv_cnnp_model_t*)model_matmul;
    2835
    10
    }
    2836
    2837
    static ccv_cnnp_model_t* _ccv_cnnp_matmul_copy(const ccv_cnnp_model_t* const super, void* const context)
    2838
    1
    {
    2839
    1
      const ccv_cnnp_model_matmul_t* const self = (const ccv_cnnp_model_matmul_t*)super;
    2840
    1
      return ccv_cnnp_matmul(self->transpose_a, self->transpose_b, self->flags, self->super.name);
    2841
    1
    }
    2842
    2843
    // MARK - Dropout Layer
    2844
    2845
    typedef struct {
    2846
      ccv_cnnp_model_t super;
    2847
      ccv_nnc_tensor_symbol_t output;
    2848
      ccv_nnc_graph_exec_symbol_t dropout;
    2849
      float p;
    2850
      int entirety;
    2851
    } ccv_cnnp_model_dropout_t;
    2852
    2853
    static void _ccv_cnnp_dropout_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)
    2854
    12
    {
    2855
    12
      PRINT(CCV_CLI_VERBOSE, "[cnnp_dropout_build] -\n");
    2856
    12
      assert(input_size == 1);
    2857
    12
      assert(output_size == 1);
    2858
    12
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2859
    12
      ccv_nnc_tensor_param_t output_params[2];
    2860
    12
      ccv_cnnp_model_dropout_t* const self = (ccv_cnnp_model_dropout_t*)super;
    2861
    12
      const ccv_nnc_cmd_t dropout = CMD_DROPOUT_FORWARD(self->p, self->entirety);
    2862
    12
      ccv_nnc_hint_tensor_auto(dropout, (ccv_nnc_tensor_param_t []){
    2863
    12
          params,
    2864
    12
        }, 1, ccv_nnc_no_hint, output_params, 2);
    2865
    12
      const ccv_nnc_tensor_symbol_t dropout_output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    2866
    12
      const ccv_nnc_tensor_symbol_t mask = ccv_nnc_tensor_symbol_new(graph, output_params[1], "mask");
    2867
    12
      self->dropout = ccv_nnc_graph_exec_symbol_new(graph, dropout, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(dropout_output, mask), "dropout");
    2868
    12
      outputs[0] = dropout_output;
    2869
    12
    }
    2870
    2871
    static void _ccv_cnnp_dropout_set_is_test(ccv_cnnp_model_t* const super, const int is_test, const ccv_cnnp_cmd_updater_f updater, void* const context)
    2872
    24
    {
    2873
    24
      ccv_cnnp_model_dropout_t* const self = (ccv_cnnp_model_dropout_t*)super;
    2874
    24
      if (self->dropout.graph)
    2875
    24
      {
    2876
    24
        if (is_test)
    2877
          // During test, the dropout is not applied. Data transfer is perfect because if these are the same tensor, it will skip.
    2878
    12
          updater(context, self->dropout, CMD_DATA_TRANSFER_FORWARD(), ccv_nnc_no_hint);
    2879
    12
        else
    2880
    12
          updater(context, self->dropout, CMD_DROPOUT_FORWARD(self->p, self->entirety), ccv_nnc_no_hint);
    2881
    24
      }
    2882
    24
    }
    2883
    2884
    static ccv_cnnp_model_t* _ccv_cnnp_dropout_copy(const ccv_cnnp_model_t* const super, void* const context);
    2885
    2886
    static const ccv_cnnp_model_vtab_t ccv_cnnp_dropout_isa = {
    2887
      .build = _ccv_cnnp_dropout_build,
    2888
      .set_is_test = _ccv_cnnp_dropout_set_is_test,
    2889
      .copy = _ccv_cnnp_dropout_copy,
    2890
    };
    2891
    2892
    ccv_cnnp_model_t* ccv_cnnp_dropout(const float p, const int entirety, const char* const name)
    2893
    12
    {
    2894
    12
      ccv_cnnp_model_dropout_t* const model_dropout = (ccv_cnnp_model_dropout_t*)cccalloc(1, sizeof(ccv_cnnp_model_dropout_t));
    2895
    12
      model_dropout->super.isa = &ccv_cnnp_dropout_isa;
    2896
    12
      model_dropout->super.input_size = 1;
    2897
    12
      model_dropout->super.outputs = &model_dropout->output;
    2898
    12
      model_dropout->super.output_size = 1;
    2899
    12
      model_dropout->p = p;
    2900
    12
      model_dropout->entirety = entirety;
    2901
    12
      ccv_cnnp_model_copy_name(&model_dropout->super, name);
    2902
    12
      return (ccv_cnnp_model_t*)model_dropout;
    2903
    12
    }
    2904
    2905
    static ccv_cnnp_model_t* _ccv_cnnp_dropout_copy(const ccv_cnnp_model_t* const super, void* const context)
    2906
    0
    {
    2907
    0
      const ccv_cnnp_model_dropout_t* const self = (const ccv_cnnp_model_dropout_t*)super;
    2908
    0
      return ccv_cnnp_dropout(self->p, self->entirety, self->super.name);
    2909
    0
    }
    2910
    2911
    // MARK - Masked Fill Layer
    2912
    2913
    typedef struct {
    2914
      ccv_cnnp_model_t super;
    2915
      ccv_nnc_tensor_symbol_t output;
    2916
      float eq;
    2917
      float fill;
    2918
    } ccv_cnnp_model_masked_fill_t;
    2919
    2920
    static void _ccv_cnnp_masked_fill_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)
    2921
    4
    {
    2922
    4
      PRINT(CCV_CLI_VERBOSE, "[cnnp_masked_fill_build] -\n");
    2923
    4
      assert(input_size == 2);
    2924
    4
      assert(output_size == 1);
    2925
    4
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2926
    4
      ccv_cnnp_model_masked_fill_t* const self = (ccv_cnnp_model_masked_fill_t*)super;
    2927
    4
      const ccv_nnc_tensor_symbol_t masked_fill_output = ccv_nnc_tensor_symbol_new(graph, params, 0);
    2928
    4
      ccv_nnc_graph_exec_symbol_new(graph, CMD_MASKED_FILL_FORWARD(self->eq, self->fill), TENSOR_SYMBOL_LIST(inputs[0], inputs[1]), TENSOR_SYMBOL_LIST(masked_fill_output), "masked_fill");
    2929
    4
      outputs[0] = masked_fill_output;
    2930
    4
    }
    2931
    2932
    static ccv_cnnp_model_t* _ccv_cnnp_masked_fill_copy(const ccv_cnnp_model_t* const super, void* const context);
    2933
    2934
    static const ccv_cnnp_model_vtab_t ccv_cnnp_masked_fill_isa = {
    2935
      .build = _ccv_cnnp_masked_fill_build,
    2936
      .copy = _ccv_cnnp_masked_fill_copy,
    2937
    };
    2938
    2939
    ccv_cnnp_model_t* ccv_cnnp_masked_fill(const float eq, const float fill, const char* const name)
    2940
    4
    {
    2941
    4
      ccv_cnnp_model_masked_fill_t* const model_masked_fill = (ccv_cnnp_model_masked_fill_t*)cccalloc(1, sizeof(ccv_cnnp_model_masked_fill_t));
    2942
    4
      model_masked_fill->super.isa = &ccv_cnnp_masked_fill_isa;
    2943
    4
      model_masked_fill->super.input_size = 2;
    2944
    4
      model_masked_fill->super.outputs = &model_masked_fill->output;
    2945
    4
      model_masked_fill->super.output_size = 1;
    2946
    4
      model_masked_fill->eq = eq;
    2947
    4
      model_masked_fill->fill = fill;
    2948
    4
      ccv_cnnp_model_copy_name(&model_masked_fill->super, name);
    2949
    4
      return (ccv_cnnp_model_t*)model_masked_fill;
    2950
    4
    }
    2951
    2952
    static ccv_cnnp_model_t* _ccv_cnnp_masked_fill_copy(const ccv_cnnp_model_t* const super, void* const context)
    2953
    0
    {
    2954
    0
      const ccv_cnnp_model_masked_fill_t* const self = (const ccv_cnnp_model_masked_fill_t*)super;
    2955
    0
      return ccv_cnnp_masked_fill(self->eq, self->fill, self->super.name);
    2956
    0
    }
    2957
    2958
    // MARK - Index Select Layer
    2959
    2960
    typedef struct {
    2961
      ccv_cnnp_model_t super;
    2962
      ccv_nnc_tensor_symbol_t output;
    2963
    } ccv_cnnp_model_index_select_t;
    2964
    2965
    static void _ccv_cnnp_index_select_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)
    2966
    2
    {
    2967
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_index_select_build] -\n");
    2968
    2
      assert(input_size == 2);
    2969
    2
      assert(output_size == 1);
    2970
    2
      const ccv_nnc_tensor_param_t vocab_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2971
    2
      const ccv_nnc_tensor_param_t index_params = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    2972
    2
      ccv_nnc_tensor_param_t output_params;
    2973
    2
      const ccv_nnc_cmd_t index_select = CMD_INDEX_SELECT_FORWARD();
    2974
    2
      ccv_nnc_hint_tensor_auto(index_select, (ccv_nnc_tensor_param_t []){
    2975
    2
          vocab_params,
    2976
    2
          index_params,
    2977
    2
        }, 2, ccv_nnc_no_hint, &output_params, 1);
    2978
    2
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2979
    2
      ccv_nnc_graph_exec_symbol_new(graph, index_select, TENSOR_SYMBOL_LIST(inputs[0], inputs[1]), TENSOR_SYMBOL_LIST(output), "index_select");
    2980
    2
      outputs[0] = output;
    2981
    2
    }
    2982
    2983
    static ccv_cnnp_model_t* _ccv_cnnp_index_select_copy(const ccv_cnnp_model_t* const super, void* const context);
    2984
    2985
    static const ccv_cnnp_model_vtab_t ccv_cnnp_index_select_isa = {
    2986
      .build = _ccv_cnnp_index_select_build,
    2987
      .copy = _ccv_cnnp_index_select_copy,
    2988
    };
    2989
    2990
    ccv_cnnp_model_t* ccv_cnnp_index_select(const char* const name)
    2991
    2
    {
    2992
    2
      ccv_cnnp_model_index_select_t* const model_index_select = (ccv_cnnp_model_index_select_t*)cccalloc(1, sizeof(ccv_cnnp_model_index_select_t));
    2993
    2
      model_index_select->super.isa = &ccv_cnnp_index_select_isa;
    2994
    2
      model_index_select->super.input_size = 2;
    2995
    2
      model_index_select->super.outputs = &model_index_select->output;
    2996
    2
      model_index_select->super.output_size = 1;
    2997
    2
      ccv_cnnp_model_copy_name(&model_index_select->super, name);
    2998
    2
      return (ccv_cnnp_model_t*)model_index_select;
    2999
    2
    }
    3000
    3001
    static ccv_cnnp_model_t* _ccv_cnnp_index_select_copy(const ccv_cnnp_model_t* const super, void* const context)
    3002
    0
    {
    3003
    0
      ccv_cnnp_model_index_select_t* const self = (ccv_cnnp_model_index_select_t*)super;
    3004
    0
      return ccv_cnnp_index_select(self->super.name);
    3005
    0
    }
    3006
    3007
    // MARK - Embedding Layer
    3008
    3009
    typedef struct {
    3010
      ccv_cnnp_model_t super;
    3011
      ccv_nnc_tensor_symbol_t output;
    3012
      ccv_nnc_tensor_symbol_t vocab;
    3013
      int datatype;
    3014
      int vocab_size;
    3015
      int embed_size;
    3016
    } ccv_cnnp_model_embedding_t;
    3017
    3018
    static void _ccv_cnnp_embedding_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)
    3019
    1
    {
    3020
    1
      ccv_cnnp_model_embedding_t* const self = (ccv_cnnp_model_embedding_t*)super;
    3021
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_embedding_build] vocab_size: %d, embed_size: %d\n", self->vocab_size, self->embed_size);
    3022
    1
      assert(input_size == 1);
    3023
    1
      assert(output_size == 1);
    3024
    1
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3025
    1
      ccv_nnc_tensor_param_t vocab_params = params;
    3026
    1
      memset(vocab_params.dim, 0, sizeof(vocab_params.dim));
    3027
    1
      vocab_params.datatype = self->datatype;
    3028
    1
      vocab_params.dim[0] = self->vocab_size;
    3029
    1
      vocab_params.dim[1] = self->embed_size;
    3030
    1
      if (!self->vocab.graph)
    3031
    1
        self->vocab = ccv_nnc_tensor_symbol_new(graph, vocab_params, "vocab");
    3032
    1
      assert(self->vocab.graph == graph);
    3033
    1
      ccv_nnc_tensor_param_t output_params;
    3034
    1
      const ccv_nnc_cmd_t embedding = CMD_INDEX_SELECT_FORWARD();
    3035
    1
      ccv_nnc_hint_tensor_auto(embedding, (ccv_nnc_tensor_param_t []){
    3036
    1
          vocab_params,
    3037
    1
          params,
    3038
    1
        }, 2, ccv_nnc_no_hint, &output_params, 1);
    3039
    1
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3040
    1
      ccv_nnc_graph_exec_symbol_new(graph, embedding, TENSOR_SYMBOL_LIST(self->vocab, inputs[0]), TENSOR_SYMBOL_LIST(output), "embedding");
    3041
    1
      outputs[0] = output;
    3042
    1
    }
    3043
    3044
    static void _ccv_cnnp_embedding_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)
    3045
    1
    {
    3046
    1
      ccv_cnnp_model_embedding_t* const self = (ccv_cnnp_model_embedding_t*)super;
    3047
    1
      const float std = sqrtf(2) / sqrtf(self->vocab_size + self->embed_size);
    3048
    1
      const float bound = sqrtf(3) * std;
    3049
    1
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->vocab);
    3050
    1
    }
    3051
    3052
    static void _ccv_cnnp_embedding_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)
    3053
    1
    {
    3054
    1
      ccv_cnnp_model_embedding_t* const self = (ccv_cnnp_model_embedding_t*)super;
    3055
    1
      add_to_array(parameters, self->vocab, is_trainable);
    3056
    1
    }
    3057
    3058
    static ccv_cnnp_model_t* _ccv_cnnp_embedding_copy(const ccv_cnnp_model_t* const super, void* const context);
    3059
    3060
    static const ccv_cnnp_model_vtab_t ccv_cnnp_embedding_isa = {
    3061
      .build = _ccv_cnnp_embedding_build,
    3062
      .init_states = _ccv_cnnp_embedding_init_states,
    3063
      .add_to_parameter = _ccv_cnnp_embedding_add_to_parameter,
    3064
      .copy = _ccv_cnnp_embedding_copy,
    3065
    };
    3066
    3067
    ccv_cnnp_model_t* ccv_cnnp_embedding(const int datatype, const int vocab_size, const int embed_size, const int is_trainable, const char* const name)
    3068
    1
    {
    3069
    1
      ccv_cnnp_model_embedding_t* const model_embedding = (ccv_cnnp_model_embedding_t*)cccalloc(1, sizeof(ccv_cnnp_model_embedding_t));
    3070
    1
      model_embedding->super.isa = &ccv_cnnp_embedding_isa;
    3071
    1
      model_embedding->super.input_size = 1;
    3072
    1
      model_embedding->super.outputs = &model_embedding->output;
    3073
    1
      model_embedding->super.output_size = 1;
    3074
    1
      model_embedding->super.is_trainable = is_trainable;
    3075
    1
      ccv_cnnp_model_copy_name(&model_embedding->super, name);
    3076
    1
      model_embedding->vocab.d = CCV_NNC_NO_TENSOR_SYMBOL;
    3077
    1
      model_embedding->vocab.graph = 0;
    3078
    1
      assert(datatype == CCV_32F || datatype == CCV_16F || datatype == CCV_16BF);
    3079
    1
      model_embedding->datatype = datatype;
    3080
    1
      assert(vocab_size > 0);
    3081
    1
      model_embedding->vocab_size = vocab_size;
    3082
    1
      assert(embed_size > 0);
    3083
    1
      model_embedding->embed_size = embed_size;
    3084
    1
      return (ccv_cnnp_model_t*)model_embedding;
    3085
    1
    }
    3086
    3087
    static ccv_cnnp_model_t* _ccv_cnnp_embedding_copy(const ccv_cnnp_model_t* const super, void* const context)
    3088
    0
    {
    3089
    0
      ccv_cnnp_model_embedding_t* const self = (ccv_cnnp_model_embedding_t*)super;
    3090
    0
      return ccv_cnnp_embedding(self->datatype, self->vocab_size, self->embed_size, self->super.is_trainable, self->super.name);
    3091
    0
    }
    3092
    3093
    // MARK - Pool Layers
    3094
    3095
    typedef struct {
    3096
      ccv_cnnp_model_t super;
    3097
      ccv_nnc_tensor_symbol_t output;
    3098
      int type;
    3099
      float width_scale;
    3100
      float height_scale;
    3101
      int align_corners;
    3102
    } ccv_cnnp_model_upsample_t;
    3103
    3104
    static void _ccv_cnnp_upsample_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)
    3105
    3
    {
    3106
    3
      PRINT(CCV_CLI_VERBOSE, "[cnnp_upsample_build] -\n");
    3107
    3
      assert(input_size == 1);
    3108
    3
      assert(output_size == 1);
    3109
    3
      ccv_cnnp_model_upsample_t* const self = (ccv_cnnp_model_upsample_t*)super;
    3110
    3
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3111
    3
      ccv_nnc_cmd_t cmd = CMD_UPSAMPLE_FORWARD(self->type, self->width_scale, self->height_scale, self->align_corners);
    3112
    3
      ccv_nnc_tensor_param_t output_params;
    3113
    3
      ccv_nnc_hint_tensor_auto(cmd, &params, 1, ccv_nnc_no_hint, &output_params, 1);
    3114
    3
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3115
    3
      ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(output), "upsample");
    3116
    3
      outputs[0] = output;
    3117
    3
    }
    3118
    3119
    static ccv_cnnp_model_t* _ccv_cnnp_upsample_copy(const ccv_cnnp_model_t* const super, void* const context);
    3120
    3121
    static const ccv_cnnp_model_vtab_t ccv_cnnp_upsample_isa = {
    3122
      .build = _ccv_cnnp_upsample_build,
    3123
      .copy = _ccv_cnnp_upsample_copy,
    3124
    };
    3125
    3126
    ccv_cnnp_model_t* ccv_cnnp_upsample(const int type, const float width_scale, const float height_scale, const int align_corners, const char* const name)
    3127
    3
    {
    3128
    3
      ccv_cnnp_model_upsample_t* const model_upsample = (ccv_cnnp_model_upsample_t*)cccalloc(1, sizeof(ccv_cnnp_model_upsample_t));
    3129
    3
      model_upsample->super.isa = &ccv_cnnp_upsample_isa;
    3130
    3
      model_upsample->super.input_size = 1;
    3131
    3
      model_upsample->super.outputs = &model_upsample->output;
    3132
    3
      model_upsample->super.output_size = 1;
    3133
    3
      ccv_cnnp_model_copy_name(&model_upsample->super, name);
    3134
    3
      assert(type == CCV_NNC_UPSAMPLE_NEAREST || type == CCV_NNC_UPSAMPLE_BILINEAR);
    3135
    3
      model_upsample->type = type;
    3136
    3
      model_upsample->width_scale = width_scale;
    3137
    3
      model_upsample->height_scale = height_scale;
    3138
    3
      model_upsample->align_corners = align_corners;
    3139
    3
      return (ccv_cnnp_model_t*)model_upsample;
    3140
    3
    }
    3141
    3142
    static ccv_cnnp_model_t* _ccv_cnnp_upsample_copy(const ccv_cnnp_model_t* const super, void* const context)
    3143
    0
    {
    3144
    0
      const ccv_cnnp_model_upsample_t* const self = (const ccv_cnnp_model_upsample_t*)super;
    3145
    0
      return ccv_cnnp_upsample(self->type, self->width_scale, self->height_scale, self->align_corners, self->super.name);
    3146
    0
    }
    3147
    3148
    // MARK - Reduce Sum Layer
    3149
    3150
    typedef struct {
    3151
      ccv_cnnp_model_t super;
    3152
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3153
      int count;
    3154
      ccv_nnc_tensor_symbol_t output;
    3155
    } ccv_cnnp_model_reduce_sum_t;
    3156
    3157
    static void _ccv_cnnp_reduce_sum_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)
    3158
    1
    {
    3159
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_sum_build] -\n");
    3160
    1
      const ccv_cnnp_model_reduce_sum_t* const self = (const ccv_cnnp_model_reduce_sum_t*)super;
    3161
    1
      assert(input_size == 1);
    3162
    1
      assert(output_size == 1);
    3163
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3164
    1
      ccv_nnc_tensor_param_t output_params;
    3165
    1
      ccv_nnc_cmd_t reduce_sum = CMD_REDUCE_SUM_FORWARD();
    3166
    1
      int i;
    3167
    2
      for (i = 0; i < self->count; i++1)
    3168
    1
        reduce_sum.info.reduce.axis[i] = self->axis[i];
    3169
    1
      reduce_sum.info.reduce.count = self->count;
    3170
    1
      ccv_nnc_hint_tensor_auto(reduce_sum, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3171
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3172
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_sum, inputs, input_size, outputs, output_size, "reduce_sum");
    3173
    1
    }
    3174
    3175
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_sum_copy(const ccv_cnnp_model_t* const self, void* const context);
    3176
    3177
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_sum_isa = {
    3178
      .build = _ccv_cnnp_reduce_sum_build,
    3179
      .copy = _ccv_cnnp_reduce_sum_copy,
    3180
    };
    3181
    3182
    ccv_cnnp_model_t* ccv_cnnp_reduce_sum(const int* const axis, const int axis_count, const char* const name)
    3183
    1
    {
    3184
    1
      ccv_cnnp_model_reduce_sum_t* const model_reduce_sum = (ccv_cnnp_model_reduce_sum_t*)cccalloc(1, sizeof(ccv_cnnp_model_reduce_sum_t));
    3185
    1
      model_reduce_sum->super.isa = &ccv_cnnp_reduce_sum_isa;
    3186
    1
      model_reduce_sum->super.input_size = 1;
    3187
    1
      model_reduce_sum->super.outputs = &model_reduce_sum->output;
    3188
    1
      model_reduce_sum->super.output_size = 1;
    3189
    1
      ccv_cnnp_model_copy_name(&model_reduce_sum->super, name);
    3190
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3191
    1
      int i;
    3192
    2
      for (i = 0; i < axis_count; i++1)
    3193
    1
        model_reduce_sum->axis[i] = axis[i];
    3194
    1
      model_reduce_sum->count = axis_count;
    3195
    1
      return (ccv_cnnp_model_t*)model_reduce_sum;
    3196
    1
    }
    3197
    3198
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_sum_copy(const ccv_cnnp_model_t* const super, void* const context)
    3199
    0
    {
    3200
    0
      const ccv_cnnp_model_reduce_sum_t* const self = (const ccv_cnnp_model_reduce_sum_t*)super;
    3201
    0
      return ccv_cnnp_reduce_sum(self->axis, self->count, self->super.name);
    3202
    0
    }
    3203
    3204
    // MARK - Reduce Mean Layer
    3205
    3206
    typedef struct {
    3207
      ccv_cnnp_model_t super;
    3208
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3209
      int count;
    3210
      ccv_nnc_tensor_symbol_t output;
    3211
    } ccv_cnnp_model_reduce_mean_t;
    3212
    3213
    static void _ccv_cnnp_reduce_mean_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)
    3214
    1
    {
    3215
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_mean_build] -\n");
    3216
    1
      const ccv_cnnp_model_reduce_mean_t* const self = (const ccv_cnnp_model_reduce_mean_t*)super;
    3217
    1
      assert(input_size == 1);
    3218
    1
      assert(output_size == 1);
    3219
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3220
    1
      ccv_nnc_tensor_param_t output_params;
    3221
    1
      ccv_nnc_cmd_t reduce_mean = CMD_REDUCE_MEAN_FORWARD();
    3222
    1
      int i;
    3223
    2
      for (i = 0; i < self->count; i++1)
    3224
    1
        reduce_mean.info.reduce.axis[i] = self->axis[i];
    3225
    1
      reduce_mean.info.reduce.count = self->count;
    3226
    1
      ccv_nnc_hint_tensor_auto(reduce_mean, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3227
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3228
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_mean, inputs, input_size, outputs, output_size, "reduce_mean");
    3229
    1
    }
    3230
    3231
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_mean_copy(const ccv_cnnp_model_t* const self, void* const context);
    3232
    3233
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_mean_isa = {
    3234
      .build = _ccv_cnnp_reduce_mean_build,
    3235
      .copy = _ccv_cnnp_reduce_mean_copy,
    3236
    };
    3237
    3238
    ccv_cnnp_model_t* ccv_cnnp_reduce_mean(const int* const axis, const int axis_count, const char* const name)
    3239
    1
    {
    3240
    1
      ccv_cnnp_model_reduce_mean_t* const model_reduce_mean = (ccv_cnnp_model_reduce_mean_t*)cccalloc(1, sizeof(ccv_cnnp_model_reduce_mean_t));
    3241
    1
      model_reduce_mean->super.isa = &ccv_cnnp_reduce_mean_isa;
    3242
    1
      model_reduce_mean->super.input_size = 1;
    3243
    1
      model_reduce_mean->super.outputs = &model_reduce_mean->output;
    3244
    1
      model_reduce_mean->super.output_size = 1;
    3245
    1
      ccv_cnnp_model_copy_name(&model_reduce_mean->super, name);
    3246
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3247
    1
      int i;
    3248
    2
      for (i = 0; i < axis_count; i++1)
    3249
    1
        model_reduce_mean->axis[i] = axis[i];
    3250
    1
      model_reduce_mean->count = axis_count;
    3251
    1
      return (ccv_cnnp_model_t*)model_reduce_mean;
    3252
    1
    }
    3253
    3254
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_mean_copy(const ccv_cnnp_model_t* const super, void* const context)
    3255
    0
    {
    3256
    0
      const ccv_cnnp_model_reduce_mean_t* const self = (const ccv_cnnp_model_reduce_mean_t*)super;
    3257
    0
      return ccv_cnnp_reduce_mean(self->axis, self->count, self->super.name);
    3258
    0
    }
    3259
    3260
    // MARK - Reduce Max Layer
    3261
    3262
    typedef struct {
    3263
      ccv_cnnp_model_t super;
    3264
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3265
      int count;
    3266
      ccv_nnc_tensor_symbol_t output;
    3267
    } ccv_cnnp_model_reduce_max_t;
    3268
    3269
    static void _ccv_cnnp_reduce_max_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)
    3270
    1
    {
    3271
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_max_build] -\n");
    3272
    1
      const ccv_cnnp_model_reduce_max_t* const self = (const ccv_cnnp_model_reduce_max_t*)super;
    3273
    1
      assert(input_size == 1);
    3274
    1
      assert(output_size == 1);
    3275
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3276
    1
      ccv_nnc_tensor_param_t output_params;
    3277
    1
      ccv_nnc_cmd_t reduce_max = CMD_REDUCE_MAX_FORWARD();
    3278
    1
      int i;
    3279
    2
      for (i = 0; i < self->count; i++1)
    3280
    1
        reduce_max.info.reduce.axis[i] = self->axis[i];
    3281
    1
      reduce_max.info.reduce.count = self->count;
    3282
    1
      ccv_nnc_hint_tensor_auto(reduce_max, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3283
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3284
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_max, inputs, input_size, outputs, output_size, "reduce_max");
    3285
    1
    }
    3286
    3287
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_max_copy(const ccv_cnnp_model_t* const self, void* const context);
    3288
    3289
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_max_isa = {
    3290
      .build = _ccv_cnnp_reduce_max_build,
    3291
      .copy = _ccv_cnnp_reduce_max_copy,
    3292
    };
    3293
    3294
    ccv_cnnp_model_t* ccv_cnnp_reduce_max(const int* const axis, const int axis_count, const char* const name)
    3295
    1
    {
    3296
    1
      ccv_cnnp_model_reduce_max_t* const model_reduce_max = (ccv_cnnp_model_reduce_max_t*)cccalloc(1, sizeof(ccv_cnnp_model_reduce_max_t));
    3297
    1
      model_reduce_max->super.isa = &ccv_cnnp_reduce_max_isa;
    3298
    1
      model_reduce_max->super.input_size = 1;
    3299
    1
      model_reduce_max->super.outputs = &model_reduce_max->output;
    3300
    1
      model_reduce_max->super.output_size = 1;
    3301
    1
      ccv_cnnp_model_copy_name(&model_reduce_max->super, name);
    3302
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3303
    1
      int i;
    3304
    2
      for (i = 0; i < axis_count; i++1)
    3305
    1
        model_reduce_max->axis[i] = axis[i];
    3306
    1
      model_reduce_max->count = axis_count;
    3307
    1
      return (ccv_cnnp_model_t*)model_reduce_max;
    3308
    1
    }
    3309
    3310
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_max_copy(const ccv_cnnp_model_t* const super, void* const context)
    3311
    0
    {
    3312
    0
      const ccv_cnnp_model_reduce_max_t* const self = (const ccv_cnnp_model_reduce_max_t*)super;
    3313
    0
      return ccv_cnnp_reduce_max(self->axis, self->count, self->super.name);
    3314
    0
    }
    3315
    3316
    // MARK - Reduce Min Layer
    3317
    3318
    typedef struct {
    3319
      ccv_cnnp_model_t super;
    3320
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3321
      int count;
    3322
      ccv_nnc_tensor_symbol_t output;
    3323
    } ccv_cnnp_model_reduce_min_t;
    3324
    3325
    static void _ccv_cnnp_reduce_min_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)
    3326
    1
    {
    3327
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_min_build] -\n");
    3328
    1
      const ccv_cnnp_model_reduce_min_t* const self = (const ccv_cnnp_model_reduce_min_t*)super;
    3329
    1
      assert(input_size == 1);
    3330
    1
      assert(output_size == 1);
    3331
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3332
    1
      ccv_nnc_tensor_param_t output_params;
    3333
    1
      ccv_nnc_cmd_t reduce_min = CMD_REDUCE_MIN_FORWARD();
    3334
    1
      int i;
    3335
    2
      for (i = 0; i < self->count; i++1)
    3336
    1
        reduce_min.info.reduce.axis[i] = self->axis[i];
    3337
    1
      reduce_min.info.reduce.count = self->count;
    3338
    1
      ccv_nnc_hint_tensor_auto(reduce_min, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3339
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3340
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_min, inputs, input_size, outputs, output_size, "reduce_min");
    3341
    1
    }
    3342
    3343
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_min_copy(const ccv_cnnp_model_t* const self, void* const context);
    3344
    3345
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_min_isa = {
    3346
      .build = _ccv_cnnp_reduce_min_build,
    3347
      .copy = _ccv_cnnp_reduce_min_copy,
    3348
    };
    3349
    3350
    ccv_cnnp_model_t* ccv_cnnp_reduce_min(const int* const axis, const int axis_count, const char* const name)
    3351
    1
    {
    3352
    1
      ccv_cnnp_model_reduce_min_t* const model_reduce_min = (ccv_cnnp_model_reduce_min_t*)cccalloc(1, sizeof(ccv_cnnp_model_reduce_min_t));
    3353
    1
      model_reduce_min->super.isa = &ccv_cnnp_reduce_min_isa;
    3354
    1
      model_reduce_min->super.input_size = 1;
    3355
    1
      model_reduce_min->super.outputs = &model_reduce_min->output;
    3356
    1
      model_reduce_min->super.output_size = 1;
    3357
    1
      ccv_cnnp_model_copy_name(&model_reduce_min->super, name);
    3358
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3359
    1
      int i;
    3360
    2
      for (i = 0; i < axis_count; i++1)
    3361
    1
        model_reduce_min->axis[i] = axis[i];
    3362
    1
      model_reduce_min->count = axis_count;
    3363
    1
      return (ccv_cnnp_model_t*)model_reduce_min;
    3364
    1
    }
    3365
    3366
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_min_copy(const ccv_cnnp_model_t* const super, void* const context)
    3367
    0
    {
    3368
    0
      const ccv_cnnp_model_reduce_min_t* const self = (const ccv_cnnp_model_reduce_min_t*)super;
    3369
    0
      return ccv_cnnp_reduce_min(self->axis, self->count, self->super.name);
    3370
    0
    }
    3371
    3372
    // MARK - Reduce Norm2 Layer
    3373
    3374
    typedef struct {
    3375
      ccv_cnnp_model_t super;
    3376
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3377
      int count;
    3378
      ccv_nnc_tensor_symbol_t output;
    3379
    } ccv_cnnp_model_reduce_norm2_t;
    3380
    3381
    static void _ccv_cnnp_reduce_norm2_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)
    3382
    1
    {
    3383
    1
      const ccv_cnnp_model_reduce_norm2_t* const self = (const ccv_cnnp_model_reduce_norm2_t*)super;
    3384
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_norm2_build] -\n");
    3385
    1
      assert(input_size == 1);
    3386
    1
      assert(output_size == 1);
    3387
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3388
    1
      ccv_nnc_tensor_param_t output_params;
    3389
    1
      ccv_nnc_cmd_t reduce_norm2 = CMD_REDUCE_NORM2_FORWARD();
    3390
    1
      int i;
    3391
    2
      for (i = 0; i < self->count; i++1)
    3392
    1
        reduce_norm2.info.reduce.axis[i] = self->axis[i];
    3393
    1
      reduce_norm2.info.reduce.count = self->count;
    3394
    1
      ccv_nnc_hint_tensor_auto(reduce_norm2, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3395
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3396
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_norm2, inputs, input_size, outputs, output_size, "reduce_norm2");
    3397
    1
    }
    3398
    3399
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_norm2_copy(const ccv_cnnp_model_t* const self, void* const context);
    3400
    3401
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_norm2_isa = {
    3402
      .build = _ccv_cnnp_reduce_norm2_build,
    3403
      .copy = _ccv_cnnp_reduce_norm2_copy,
    3404
    };
    3405
    3406
    ccv_cnnp_model_t* ccv_cnnp_reduce_norm2(const int* const axis, const int axis_count, const char* const name)
    3407
    1
    {
    3408
    1
      ccv_cnnp_model_reduce_norm2_t* const model_reduce_norm2 = (ccv_cnnp_model_reduce_norm2_t*)cccalloc(1, sizeof(ccv_cnnp_model_reduce_norm2_t));
    3409
    1
      model_reduce_norm2->super.isa = &ccv_cnnp_reduce_norm2_isa;
    3410
    1
      model_reduce_norm2->super.input_size = 1;
    3411
    1
      model_reduce_norm2->super.outputs = &model_reduce_norm2->output;
    3412
    1
      model_reduce_norm2->super.output_size = 1;
    3413
    1
      ccv_cnnp_model_copy_name(&model_reduce_norm2->super, name);
    3414
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3415
    1
      int i;
    3416
    2
      for (i = 0; i < axis_count; i++1)
    3417
    1
        model_reduce_norm2->axis[i] = axis[i];
    3418
    1
      model_reduce_norm2->count = axis_count;
    3419
    1
      return (ccv_cnnp_model_t*)model_reduce_norm2;
    3420
    1
    }
    3421
    3422
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_norm2_copy(const ccv_cnnp_model_t* const super, void* const context)
    3423
    0
    {
    3424
    0
      const ccv_cnnp_model_reduce_norm2_t* const self = (const ccv_cnnp_model_reduce_norm2_t*)super;
    3425
    0
      return ccv_cnnp_reduce_norm2(self->axis, self->count, self->super.name);
    3426
    0
    }
    3427
    3428
    // MARK - Argmax Layer
    3429
    3430
    typedef struct {
    3431
      ccv_cnnp_model_t super;
    3432
      int axis;
    3433
      ccv_nnc_tensor_symbol_t output;
    3434
    } ccv_cnnp_model_argmax_t;
    3435
    3436
    static void _ccv_cnnp_argmax_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)
    3437
    1
    {
    3438
    1
      const ccv_cnnp_model_argmax_t* const self = (const ccv_cnnp_model_argmax_t*)super;
    3439
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_argmax_build] -\n");
    3440
    1
      assert(input_size == 1);
    3441
    1
      assert(output_size == 1);
    3442
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3443
    1
      ccv_nnc_tensor_param_t output_params;
    3444
    1
      ccv_nnc_cmd_t argmax = CMD_ARGMAX_FORWARD();
    3445
    1
      argmax.info.reduce.axis[0] = self->axis;
    3446
    1
      argmax.info.reduce.count = 1;
    3447
    1
      ccv_nnc_hint_tensor_auto(argmax, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3448
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3449
    1
      ccv_nnc_graph_exec_symbol_new(graph, argmax, inputs, input_size, outputs, output_size, "argmax");
    3450
    1
    }
    3451
    3452
    static ccv_cnnp_model_t* _ccv_cnnp_argmax_copy(const ccv_cnnp_model_t* const self, void* const context);
    3453
    3454
    static const ccv_cnnp_model_vtab_t ccv_cnnp_argmax_isa = {
    3455
      .build = _ccv_cnnp_argmax_build,
    3456
      .copy = _ccv_cnnp_argmax_copy,
    3457
    };
    3458
    3459
    ccv_cnnp_model_t* ccv_cnnp_argmax(const int axis, const char* const name)
    3460
    1
    {
    3461
    1
      ccv_cnnp_model_argmax_t* const model_argmax = (ccv_cnnp_model_argmax_t*)cccalloc(1, sizeof(ccv_cnnp_model_argmax_t));
    3462
    1
      model_argmax->super.isa = &ccv_cnnp_argmax_isa;
    3463
    1
      model_argmax->super.input_size = 1;
    3464
    1
      model_argmax->super.outputs = &model_argmax->output;
    3465
    1
      model_argmax->super.output_size = 1;
    3466
    1
      ccv_cnnp_model_copy_name(&model_argmax->super, name);
    3467
    1
      model_argmax->axis = axis;
    3468
    1
      return (ccv_cnnp_model_t*)model_argmax;
    3469
    1
    }
    3470
    3471
    static ccv_cnnp_model_t* _ccv_cnnp_argmax_copy(const ccv_cnnp_model_t* const super, void* const context)
    3472
    0
    {
    3473
    0
      const ccv_cnnp_model_argmax_t* const self = (const ccv_cnnp_model_argmax_t*)super;
    3474
    0
      return ccv_cnnp_argmax(self->axis, self->super.name);
    3475
    0
    }
    3476
    3477
    // MARK - Argmin Layer
    3478
    3479
    typedef struct {
    3480
      ccv_cnnp_model_t super;
    3481
      int axis;
    3482
      ccv_nnc_tensor_symbol_t output;
    3483
    } ccv_cnnp_model_argmin_t;
    3484
    3485
    static void _ccv_cnnp_argmin_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)
    3486
    1
    {
    3487
    1
      const ccv_cnnp_model_argmin_t* const self = (const ccv_cnnp_model_argmin_t*)super;
    3488
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_argmin_build] -\n");
    3489
    1
      assert(input_size == 1);
    3490
    1
      assert(output_size == 1);
    3491
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3492
    1
      ccv_nnc_tensor_param_t output_params;
    3493
    1
      ccv_nnc_cmd_t argmin = CMD_ARGMIN_FORWARD();
    3494
    1
      argmin.info.reduce.axis[0] = self->axis;
    3495
    1
      argmin.info.reduce.count = 1;
    3496
    1
      ccv_nnc_hint_tensor_auto(argmin, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3497
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3498
    1
      ccv_nnc_graph_exec_symbol_new(graph, argmin, inputs, input_size, outputs, output_size, "argmin");
    3499
    1
    }
    3500
    3501
    static ccv_cnnp_model_t* _ccv_cnnp_argmin_copy(const ccv_cnnp_model_t* const self, void* const context);
    3502
    3503
    static const ccv_cnnp_model_vtab_t ccv_cnnp_argmin_isa = {
    3504
      .build = _ccv_cnnp_argmin_build,
    3505
      .copy = _ccv_cnnp_argmin_copy,
    3506
    };
    3507
    3508
    ccv_cnnp_model_t* ccv_cnnp_argmin(const int axis, const char* const name)
    3509
    1
    {
    3510
    1
      ccv_cnnp_model_argmin_t* const model_argmin = (ccv_cnnp_model_argmin_t*)cccalloc(1, sizeof(ccv_cnnp_model_argmin_t));
    3511
    1
      model_argmin->super.isa = &ccv_cnnp_argmin_isa;
    3512
    1
      model_argmin->super.input_size = 1;
    3513
    1
      model_argmin->super.outputs = &model_argmin->output;
    3514
    1
      model_argmin->super.output_size = 1;
    3515
    1
      ccv_cnnp_model_copy_name(&model_argmin->super, name);
    3516
    1
      model_argmin->axis = axis;
    3517
    1
      return (ccv_cnnp_model_t*)model_argmin;
    3518
    1
    }
    3519
    3520
    static ccv_cnnp_model_t* _ccv_cnnp_argmin_copy(const ccv_cnnp_model_t* const super, void* const context)
    3521
    0
    {
    3522
    0
      const ccv_cnnp_model_argmin_t* const self = (const ccv_cnnp_model_argmin_t*)super;
    3523
    0
      return ccv_cnnp_argmin(self->axis, self->super.name);
    3524
    0
    }
    3525
    3526
    // MARK - Min Layer
    3527
    3528
    typedef struct {
    3529
      ccv_cnnp_model_t super;
    3530
      ccv_nnc_tensor_symbol_t output;
    3531
    } ccv_cnnp_model_min_t;
    3532
    3533
    static void _ccv_cnnp_min_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)
    3534
    1
    {
    3535
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_min_build] -\n");
    3536
    1
      assert(input_size == 2);
    3537
    1
      assert(output_size == 1);
    3538
    1
      ccv_nnc_tensor_param_t input_params[2];
    3539
    1
      int i;
    3540
    3
      for (i = 0; i < 2; i++2)
    3541
    2
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    3542
    1
      ccv_nnc_tensor_param_t output_params;
    3543
    1
      const ccv_nnc_cmd_t min = CMD_MIN_FORWARD();
    3544
    1
      ccv_nnc_hint_tensor_auto(min, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    3545
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3546
    1
      ccv_nnc_graph_exec_symbol_new(graph, min, inputs, input_size, outputs, output_size, "min");
    3547
    1
    }
    3548
    3549
    static ccv_cnnp_model_t* _ccv_cnnp_min_copy(const ccv_cnnp_model_t* const self, void* const context);
    3550
    3551
    static const ccv_cnnp_model_vtab_t ccv_cnnp_min_isa = {
    3552
      .build = _ccv_cnnp_min_build,
    3553
      .copy = _ccv_cnnp_min_copy,
    3554
    };
    3555
    3556
    ccv_cnnp_model_t* ccv_cnnp_min(const char* const name)
    3557
    1
    {
    3558
    1
      ccv_cnnp_model_min_t* const model_min = (ccv_cnnp_model_min_t*)cccalloc(1, sizeof(ccv_cnnp_model_min_t));
    3559
    1
      model_min->super.isa = &ccv_cnnp_min_isa;
    3560
    1
      model_min->super.input_size = 2;
    3561
    1
      model_min->super.outputs = &model_min->output;
    3562
    1
      model_min->super.output_size = 1;
    3563
    1
      ccv_cnnp_model_copy_name(&model_min->super, name);
    3564
    1
      return (ccv_cnnp_model_t*)model_min;
    3565
    1
    }
    3566
    3567
    static ccv_cnnp_model_t* _ccv_cnnp_min_copy(const ccv_cnnp_model_t* const super, void* const context)
    3568
    0
    {
    3569
    0
      const ccv_cnnp_model_min_t* const self = (const ccv_cnnp_model_min_t*)super;
    3570
    0
      return ccv_cnnp_min(self->super.name);
    3571
    0
    }
    3572
    3573
    // MARK - Max Layer
    3574
    3575
    typedef struct {
    3576
      ccv_cnnp_model_t super;
    3577
      ccv_nnc_tensor_symbol_t output;
    3578
    } ccv_cnnp_model_max_t;
    3579
    3580
    static void _ccv_cnnp_max_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)
    3581
    1
    {
    3582
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_max_build] -\n");
    3583
    1
      assert(input_size == 2);
    3584
    1
      assert(output_size == 1);
    3585
    1
      ccv_nnc_tensor_param_t input_params[2];
    3586
    1
      int i;
    3587
    3
      for (i = 0; i < 2; i++2)
    3588
    2
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    3589
    1
      ccv_nnc_tensor_param_t output_params;
    3590
    1
      const ccv_nnc_cmd_t max = CMD_MAX_FORWARD();
    3591
    1
      ccv_nnc_hint_tensor_auto(max, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    3592
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3593
    1
      ccv_nnc_graph_exec_symbol_new(graph, max, inputs, input_size, outputs, output_size, "max");
    3594
    1
    }
    3595
    3596
    static ccv_cnnp_model_t* _ccv_cnnp_max_copy(const ccv_cnnp_model_t* const self, void* const context);
    3597
    3598
    static const ccv_cnnp_model_vtab_t ccv_cnnp_max_isa = {
    3599
      .build = _ccv_cnnp_max_build,
    3600
      .copy = _ccv_cnnp_max_copy,
    3601
    };
    3602
    3603
    ccv_cnnp_model_t* ccv_cnnp_max(const char* const name)
    3604
    1
    {
    3605
    1
      ccv_cnnp_model_max_t* const model_max = (ccv_cnnp_model_max_t*)cccalloc(1, sizeof(ccv_cnnp_model_max_t));
    3606
    1
      model_max->super.isa = &ccv_cnnp_max_isa;
    3607
    1
      model_max->super.input_size = 2;
    3608
    1
      model_max->super.outputs = &model_max->output;
    3609
    1
      model_max->super.output_size = 1;
    3610
    1
      ccv_cnnp_model_copy_name(&model_max->super, name);
    3611
    1
      return (ccv_cnnp_model_t*)model_max;
    3612
    1
    }
    3613
    3614
    static ccv_cnnp_model_t* _ccv_cnnp_max_copy(const ccv_cnnp_model_t* const super, void* const context)
    3615
    0
    {
    3616
    0
      const ccv_cnnp_model_max_t* const self = (const ccv_cnnp_model_max_t*)super;
    3617
    0
      return ccv_cnnp_max(self->super.name);
    3618
    0
    }
    3619
    3620
    // MARK - LSTM Layer
    3621
    3622
    typedef struct {
    3623
      ccv_cnnp_model_t super;
    3624
      int masked;
    3625
      ccv_nnc_tensor_symbol_t output;
    3626
      ccv_nnc_tensor_symbol_t weights;
    3627
      ccv_nnc_tensor_symbol_t reserves;
    3628
      ccv_nnc_cmd_param_t params;
    3629
      ccv_nnc_graph_exec_symbol_t lstm;
    3630
    } ccv_cnnp_model_lstm_t;
    3631
    3632
    static int _ccv_cnnp_lstm_weight_dim(int bidirectional, int num_layers, int input_size, int hidden_size, int proj_size, int bias)
    3633
    1
    {
    3634
    1
      const int D = !!bidirectional + 1;
    3635
    1
      if (hidden_size == proj_size)
    3636
    1
        return (num_layers * (bias ? 8 : 00) + (num_layers - 1) * (hidden_size * 4 * D + hidden_size * 4) + input_size * 4 + hidden_size * 4) * D;
    3637
    0
      else
    3638
    0
        return (num_layers * (bias ? 8 : 0) + (num_layers - 1) * (proj_size * 4 * D + proj_size * 4) + (proj_size * 4 + input_size * 4) + num_layers * proj_size) * D;
    3639
    1
    }
    3640
    3641
    static void _ccv_cnnp_lstm_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)
    3642
    1
    {
    3643
    1
      ccv_cnnp_model_lstm_t* const self = (ccv_cnnp_model_lstm_t*)super;
    3644
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_lstm_build] -\n");
    3645
    1
      assert(input_size == self->super.input_size);
    3646
    1
      assert(output_size == 1);
    3647
    1
      const int proj_size = self->params.rnn.proj_size == 0 ? self->params.rnn.hidden_size : self->params.rnn.proj_size0;
    3648
    1
      ccv_nnc_tensor_param_t input_params[5];
    3649
    1
      input_params[0]= ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3650
    1
      if (input_size == 2)
    3651
    1
        input_params[1] = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    3652
    1
      input_params[4] = input_params[0];
    3653
    1
      memset(input_params[4].dim, 0, sizeof(input_params[4].dim));
    3654
    1
      const int x_nd = ccv_nnc_tensor_nd(input_params[0].dim);
    3655
    1
      const int feature_count = input_params[0].dim[x_nd - 1];
    3656
    1
      input_params[4].dim[0] = _ccv_cnnp_lstm_weight_dim(self->params.rnn.bidirectional, self->params.rnn.num_layers, feature_count, self->params.rnn.hidden_size, proj_size, self->params.rnn.bias);
    3657
    1
      input_params[4].dim[1] = self->params.rnn.hidden_size;
    3658
    1
      const ccv_nnc_cmd_t lstm = ccv_nnc_cmd(CCV_NNC_LSTM_FORWARD, 0, self->params, 0);
    3659
    1
      ccv_nnc_tensor_param_t output_params[4];
    3660
    1
      ccv_nnc_hint_tensor_auto(lstm, input_params, 5, ccv_nnc_no_hint, output_params, 4);
    3661
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    3662
    1
      if (!self->weights.graph)
    3663
    1
        self->weights = ccv_nnc_tensor_symbol_new(graph, input_params[4], "weights");
    3664
    1
      if (!self->reserves.graph)
    3665
    1
        self->reserves = ccv_nnc_tensor_symbol_new(graph, output_params[3], "reserves");
    3666
    1
      const ccv_nnc_tensor_symbol_t mask = input_size == 2 ? inputs[1] : NO_TENSOR_SYMBOL0;
    3667
    1
      self->lstm = ccv_nnc_graph_exec_symbol_new(graph, lstm, TENSOR_SYMBOL_LIST(inputs[0], mask, NO_TENSOR_SYMBOL, NO_TENSOR_SYMBOL, self->weights), TENSOR_SYMBOL_LIST(outputs[0], NO_TENSOR_SYMBOL, NO_TENSOR_SYMBOL, self->reserves), "lstm");
    3668
    1
    }
    3669
    3670
    static void _ccv_cnnp_lstm_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)
    3671
    1
    {
    3672
    1
      ccv_cnnp_model_lstm_t* const self = (ccv_cnnp_model_lstm_t*)super;
    3673
    1
      if (self->weights.graph)
    3674
    1
      {
    3675
    1
        const float stdv = 1.0 / sqrt(self->params.rnn.hidden_size);
    3676
    1
        initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-stdv, stdv), ccv_nnc_no_hint, 0, 0, self->weights);
    3677
    1
      }
    3678
    1
    }
    3679
    3680
    static void _ccv_cnnp_lstm_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)
    3681
    1
    {
    3682
    1
      ccv_cnnp_model_lstm_t* const self = (ccv_cnnp_model_lstm_t*)super;
    3683
    1
      if (self->weights.graph)
    3684
    1
        add_to_array(parameters, self->weights, is_trainable);
    3685
    1
    }
    3686
    3687
    static void _ccv_cnnp_lstm_set_is_test(ccv_cnnp_model_t* const super, const int is_test, const ccv_cnnp_cmd_updater_f updater, void* const context)
    3688
    2
    {
    3689
    2
      ccv_cnnp_model_lstm_t* const self = (ccv_cnnp_model_lstm_t*)super;
    3690
    2
      if (self->lstm.graph)
    3691
    2
      {
    3692
    2
        self->params.rnn.is_test = is_test;
    3693
    2
        updater(context, self->lstm, ccv_nnc_cmd(CCV_NNC_LSTM_FORWARD, 0, self->params, 0), ccv_nnc_no_hint);
    3694
    2
      }
    3695
    2
    }
    3696
    3697
    static ccv_cnnp_model_t* _ccv_cnnp_lstm_copy(const ccv_cnnp_model_t* const self, void* const context);
    3698
    3699
    static const ccv_cnnp_model_vtab_t ccv_cnnp_lstm_isa = {
    3700
      .build = _ccv_cnnp_lstm_build,
    3701
      .init_states = _ccv_cnnp_lstm_init_states,
    3702
      .add_to_parameter = _ccv_cnnp_lstm_add_to_parameter,
    3703
      .copy = _ccv_cnnp_lstm_copy,
    3704
      .set_is_test = _ccv_cnnp_lstm_set_is_test,
    3705
    };
    3706
    3707
    ccv_cnnp_model_t* ccv_cnnp_lstm(const int masked, const int hidden_size, const int proj_size, const int num_layers, const int bias, const int batch_first, const int bidirectional, const float dropout, const int is_trainable, const char* const name)
    3708
    1
    {
    3709
    1
      ccv_cnnp_model_lstm_t* const model_lstm = (ccv_cnnp_model_lstm_t*)cccalloc(1, sizeof(ccv_cnnp_model_lstm_t));
    3710
    1
      model_lstm->super.isa = &ccv_cnnp_lstm_isa;
    3711
    1
      model_lstm->super.input_size = masked ? 2 : 10;
    3712
    1
      model_lstm->super.outputs = &model_lstm->output;
    3713
    1
      model_lstm->super.output_size = 1;
    3714
    1
      model_lstm->super.is_trainable = is_trainable;
    3715
    1
      ccv_cnnp_model_copy_name(&model_lstm->super, name);
    3716
    1
      model_lstm->masked = masked;
    3717
    1
      model_lstm->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    3718
    1
      model_lstm->weights.graph = 0;
    3719
    1
      model_lstm->params.rnn.hidden_size = hidden_size;
    3720
    1
      model_lstm->params.rnn.proj_size = proj_size;
    3721
    1
      model_lstm->params.rnn.num_layers = num_layers;
    3722
    1
      model_lstm->params.rnn.bias = bias;
    3723
    1
      model_lstm->params.rnn.batch_first = batch_first;
    3724
    1
      model_lstm->params.rnn.bidirectional = bidirectional;
    3725
    1
      model_lstm->params.rnn.dropout = dropout;
    3726
    1
      return (ccv_cnnp_model_t*)model_lstm;
    3727
    1
    }
    3728
    3729
    static ccv_cnnp_model_t* _ccv_cnnp_lstm_copy(const ccv_cnnp_model_t* const super, void* const context)
    3730
    0
    {
    3731
    0
      const ccv_cnnp_model_lstm_t* const self = (const ccv_cnnp_model_lstm_t*)super;
    3732
    0
      return ccv_cnnp_lstm(self->masked, self->params.rnn.hidden_size, self->params.rnn.proj_size, self->params.rnn.num_layers, self->params.rnn.bias, self->params.rnn.batch_first, self->params.rnn.bidirectional, self->params.rnn.dropout, self->super.is_trainable, self->super.name);
    3733
    0
    }
    3734
    3735
    /// MARK - Datatype conversion layer.
    3736
    3737
    typedef struct {
    3738
      ccv_cnnp_model_t super;
    3739
      ccv_nnc_tensor_symbol_t output;
    3740
      int datatype;
    3741
      int ref_to_last;
    3742
    } ccv_cnnp_model_datatype_conversion_t;
    3743
    3744
    static void _ccv_cnnp_datatype_conversion_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)
    3745
    2
    {
    3746
    2
      ccv_cnnp_model_datatype_conversion_t* const self = (ccv_cnnp_model_datatype_conversion_t*)super;
    3747
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_datatype_conversion_build] -\n");
    3748
    2
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3749
    2
      if (self->ref_to_last)
    3750
    1
      {
    3751
    1
        assert(input_size > 1);
    3752
    1
        const ccv_nnc_tensor_param_t last_params = ccv_nnc_tensor_symbol_params(graph, inputs[input_size - 1]);
    3753
    1
        params.datatype = last_params.datatype;
    3754
    1
      } else
    3755
    1
        params.datatype = self->datatype;
    3756
    2
      assert(output_size == 1);
    3757
    2
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    3758
    2
      ccv_nnc_graph_exec_symbol_new(graph, CMD_DATATYPE_CONVERSION_FORWARD(), inputs, output_size /* intentional */, outputs, output_size, 0);
    3759
    2
    }
    3760
    3761
    static ccv_cnnp_model_t* _ccv_cnnp_datatype_conversion_copy(const ccv_cnnp_model_t* const self, void* const context);
    3762
    3763
    static const ccv_cnnp_model_vtab_t ccv_cnnp_datatype_conversion_isa = {
    3764
      .build = _ccv_cnnp_datatype_conversion_build,
    3765
      .copy = _ccv_cnnp_datatype_conversion_copy,
    3766
    };
    3767
    3768
    ccv_cnnp_model_t* ccv_cnnp_datatype_conversion(const int datatype, const int ref_to_last, const char* const name)
    3769
    2
    {
    3770
    2
      ccv_cnnp_model_datatype_conversion_t* const model_datatype_conversion = (ccv_cnnp_model_datatype_conversion_t*)cccalloc(1, sizeof(ccv_cnnp_model_datatype_conversion_t));
    3771
    2
      model_datatype_conversion->super.isa = &ccv_cnnp_datatype_conversion_isa;
    3772
    2
      model_datatype_conversion->super.input_size = 0;
    3773
    2
      model_datatype_conversion->super.outputs = &model_datatype_conversion->output;
    3774
    2
      model_datatype_conversion->super.output_size = 1;
    3775
    2
      model_datatype_conversion->datatype = datatype;
    3776
    2
      model_datatype_conversion->ref_to_last = ref_to_last;
    3777
    2
      ccv_cnnp_model_copy_name(&model_datatype_conversion->super, name);
    3778
    2
      return (ccv_cnnp_model_t*)model_datatype_conversion;
    3779
    2
    }
    3780
    3781
    static ccv_cnnp_model_t* _ccv_cnnp_datatype_conversion_copy(const ccv_cnnp_model_t* const super, void* const context)
    3782
    0
    {
    3783
    0
      ccv_cnnp_model_datatype_conversion_t* const self = (ccv_cnnp_model_datatype_conversion_t*)super;
    3784
    0
      return ccv_cnnp_datatype_conversion(self->datatype, self->ref_to_last, self->super.name);
    3785
    0
    }
    3786
    3787
    /// MARK - Clamp layer.
    3788
    3789
    typedef struct {
    3790
      ccv_cnnp_model_t super;
    3791
      ccv_nnc_tensor_symbol_t output;
    3792
      float min;
    3793
      float max;
    3794
    } ccv_cnnp_model_clamp_t;
    3795
    3796
    static void _ccv_cnnp_clamp_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)
    3797
    0
    {
    3798
    0
      ccv_cnnp_model_clamp_t* const self = (ccv_cnnp_model_clamp_t*)super;
    3799
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_clamp_build] -\n");
    3800
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3801
    0
      assert(output_size == 1);
    3802
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    3803
    0
      ccv_nnc_graph_exec_symbol_new(graph, CMD_CLAMP_FORWARD(self->min, self->max), inputs, output_size /* intentional */, outputs, output_size, 0);
    3804
    0
    }
    3805
    3806
    static ccv_cnnp_model_t* _ccv_cnnp_clamp_copy(const ccv_cnnp_model_t* const self, void* const context);
    3807
    3808
    static const ccv_cnnp_model_vtab_t ccv_cnnp_clamp_isa = {
    3809
      .build = _ccv_cnnp_clamp_build,
    3810
      .copy = _ccv_cnnp_clamp_copy,
    3811
    };
    3812
    3813
    ccv_cnnp_model_t* ccv_cnnp_clamp(const float min, const float max, const char* const name)
    3814
    0
    {
    3815
    0
      ccv_cnnp_model_clamp_t* const model_clamp = (ccv_cnnp_model_clamp_t*)cccalloc(1, sizeof(ccv_cnnp_model_clamp_t));
    3816
    0
      model_clamp->super.isa = &ccv_cnnp_clamp_isa;
    3817
    0
      model_clamp->super.input_size = 0;
    3818
    0
      model_clamp->super.outputs = &model_clamp->output;
    3819
    0
      model_clamp->super.output_size = 1;
    3820
    0
      model_clamp->min = min;
    3821
    0
      model_clamp->max = max;
    3822
    0
      ccv_cnnp_model_copy_name(&model_clamp->super, name);
    3823
    0
      return (ccv_cnnp_model_t*)model_clamp;
    3824
    0
    }
    3825
    3826
    static ccv_cnnp_model_t* _ccv_cnnp_clamp_copy(const ccv_cnnp_model_t* const super, void* const context)
    3827
    0
    {
    3828
    0
      ccv_cnnp_model_clamp_t* const self = (ccv_cnnp_model_clamp_t*)super;
    3829
    0
      return ccv_cnnp_clamp(self->min, self->max, self->super.name);
    3830
    0
    }
    3831
    3832
    // MARK - Parameter Layer
    3833
    3834
    typedef struct {
    3835
      ccv_cnnp_model_t super;
    3836
      float init_bound;
    3837
      ccv_nnc_tensor_symbol_t weights;
    3838
      ccv_nnc_tensor_param_t weights_params;
    3839
      ccv_nnc_tensor_symbol_t output;
    3840
    } ccv_cnnp_model_parameter_t;
    3841
    3842
    static void _ccv_cnnp_parameter_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)
    3843
    1
    {
    3844
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_parameter_build] -\n");
    3845
    1
      assert(output_size == 1);
    3846
    1
      ccv_cnnp_model_parameter_t* const self = (ccv_cnnp_model_parameter_t*)super;
    3847
    1
      if (!self->weights.graph)
    3848
    1
        self->weights = ccv_nnc_tensor_symbol_new(graph, self->weights_params, "weights");
    3849
    1
      assert(self->weights.graph == graph);
    3850
    1
      outputs[0] = self->weights;
    3851
    1
    }
    3852
    3853
    static void _ccv_cnnp_parameter_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)
    3854
    0
    {
    3855
    0
      ccv_cnnp_model_parameter_t* const self = (ccv_cnnp_model_parameter_t*)super;
    3856
    0
      if (self->init_bound > 0)
    3857
    0
        initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-self->init_bound, self->init_bound), ccv_nnc_no_hint, 0, 0, self->weights);
    3858
    0
      else
    3859
    0
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->weights);
    3860
    0
    }
    3861
    3862
    static void _ccv_cnnp_parameter_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)
    3863
    1
    {
    3864
    1
      ccv_cnnp_model_parameter_t* const self = (ccv_cnnp_model_parameter_t*)super;
    3865
    1
      add_to_array(parameters, self->weights, is_trainable);
    3866
    1
    }
    3867
    3868
    static ccv_cnnp_model_t* _ccv_cnnp_parameter_copy(const ccv_cnnp_model_t* const super, void* const context);
    3869
    3870
    static const ccv_cnnp_model_vtab_t ccv_cnnp_parameter_isa = {
    3871
      .build = _ccv_cnnp_parameter_build,
    3872
      .init_states = _ccv_cnnp_parameter_init_states,
    3873
      .add_to_parameter = _ccv_cnnp_parameter_add_to_parameter,
    3874
      .copy = _ccv_cnnp_parameter_copy,
    3875
    };
    3876
    3877
    ccv_cnnp_model_t* ccv_cnnp_parameter(const ccv_nnc_tensor_param_t params, const float init_bound, const int is_trainable, const char* const name)
    3878
    1
    {
    3879
    1
      ccv_cnnp_model_parameter_t* const model_parameter = (ccv_cnnp_model_parameter_t*)cccalloc(1, sizeof(ccv_cnnp_model_parameter_t));
    3880
    1
      model_parameter->super.isa = &ccv_cnnp_parameter_isa;
    3881
    1
      model_parameter->super.input_size = 0;
    3882
    1
      model_parameter->super.outputs = &model_parameter->output;
    3883
    1
      model_parameter->super.output_size = 1;
    3884
    1
      model_parameter->super.is_trainable = is_trainable;
    3885
    1
      ccv_cnnp_model_copy_name(&model_parameter->super, name);
    3886
    1
      model_parameter->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    3887
    1
      model_parameter->weights.graph = 0;
    3888
    1
      model_parameter->weights_params = params;
    3889
    1
      return (ccv_cnnp_model_t*)model_parameter;
    3890
    1
    }
    3891
    3892
    static ccv_cnnp_model_t* _ccv_cnnp_parameter_copy(const ccv_cnnp_model_t* const super, void* const context)
    3893
    0
    {
    3894
    0
      const ccv_cnnp_model_parameter_t* const self = (const ccv_cnnp_model_parameter_t*)super;
    3895
    0
      return ccv_cnnp_parameter(self->weights_params, self->init_bound, self->super.is_trainable, self->super.name);
    3896
    0
    }
    3897
    3898
    // MARK - Scalar Layer
    3899
    3900
    typedef struct {
    3901
      ccv_cnnp_model_t super;
    3902
      int type;
    3903
      int format;
    3904
      int datatype;
    3905
      float value;
    3906
      ccv_nnc_tensor_symbol_t output;
    3907
    } ccv_cnnp_model_scalar_t;
    3908
    3909
    static void _ccv_cnnp_scalar_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)
    3910
    2
    {
    3911
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_scalar_build] -\n");
    3912
    2
      assert(output_size == 1);
    3913
    2
      ccv_cnnp_model_scalar_t* const self = (ccv_cnnp_model_scalar_t*)super;
    3914
    2
      ccv_nnc_tensor_param_t params = {
    3915
    2
        .type = self->type,
    3916
    2
        .format = self->format,
    3917
    2
        .datatype = self->datatype,
    3918
    2
        .dim = {
    3919
    2
          1
    3920
    2
        }
    3921
    2
      };
    3922
    2
      if (input_size > 0)
    3923
    1
      {
    3924
    1
        ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3925
    1
        params.type = input_params.type;
    3926
    1
        params.format = input_params.format;
    3927
    1
        params.datatype = input_params.datatype;
    3928
    1
      }
    3929
    2
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    3930
    2
      ccv_nnc_graph_exec_symbol_new(graph, CMD_SET_FORWARD(self->value), 0, 0, outputs, 1, 0);
    3931
    2
    }
    3932
    3933
    static ccv_cnnp_model_t* _ccv_cnnp_scalar_copy(const ccv_cnnp_model_t* const super, void* const context);
    3934
    3935
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scalar_isa = {
    3936
      .build = _ccv_cnnp_scalar_build,
    3937
      .copy = _ccv_cnnp_scalar_copy,
    3938
    };
    3939
    3940
    ccv_cnnp_model_t* ccv_cnnp_scalar(const int type, const int format, const int datatype, const float value, const char* const name)
    3941
    2
    {
    3942
    2
      ccv_cnnp_model_scalar_t* const model_scalar = (ccv_cnnp_model_scalar_t*)cccalloc(1, sizeof(ccv_cnnp_model_scalar_t));
    3943
    2
      model_scalar->super.isa = &ccv_cnnp_scalar_isa;
    3944
    2
      model_scalar->super.input_size = 0;
    3945
    2
      model_scalar->super.outputs = &model_scalar->output;
    3946
    2
      model_scalar->super.output_size = 1;
    3947
    2
      ccv_cnnp_model_copy_name(&model_scalar->super, name);
    3948
    2
      model_scalar->type = type;
    3949
    2
      model_scalar->format = format;
    3950
    2
      model_scalar->datatype = datatype;
    3951
    2
      model_scalar->value = value;
    3952
    2
      return (ccv_cnnp_model_t*)model_scalar;
    3953
    2
    }
    3954
    3955
    static ccv_cnnp_model_t* _ccv_cnnp_scalar_copy(const ccv_cnnp_model_t* const super, void* const context)
    3956
    0
    {
    3957
    0
      const ccv_cnnp_model_scalar_t* const self = (const ccv_cnnp_model_scalar_t*)super;
    3958
    0
      return ccv_cnnp_scalar(self->type, self->format, self->datatype, self->value, self->super.name);
    3959
    0
    }
    3960
    3961
    // MARK - Variable Layer
    3962
    3963
    typedef struct {
    3964
      ccv_cnnp_model_t super;
    3965
      ccv_nnc_tensor_param_t params;
    3966
      ccv_nnc_tensor_symbol_t output;
    3967
    } ccv_cnnp_model_variable_t;
    3968
    3969
    static void _ccv_cnnp_variable_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)
    3970
    1
    {
    3971
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_variable_build] -\n");
    3972
    1
      assert(input_size == 0);
    3973
    1
      assert(output_size == 1);
    3974
    1
      ccv_cnnp_model_variable_t* const self = (ccv_cnnp_model_variable_t*)super;
    3975
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, self->params, 0);
    3976
    1
    }
    3977
    3978
    static ccv_cnnp_model_t* _ccv_cnnp_variable_copy(const ccv_cnnp_model_t* const super, void* const context);
    3979
    3980
    static const ccv_cnnp_model_vtab_t ccv_cnnp_variable_isa = {
    3981
      .build = _ccv_cnnp_variable_build,
    3982
      .copy = _ccv_cnnp_variable_copy,
    3983
    };
    3984
    3985
    ccv_cnnp_model_t* ccv_cnnp_variable(const ccv_nnc_tensor_param_t params, const char* const name)
    3986
    1
    {
    3987
    1
      ccv_cnnp_model_variable_t* const model_variable = (ccv_cnnp_model_variable_t*)cccalloc(1, sizeof(ccv_cnnp_model_variable_t));
    3988
    1
      model_variable->super.isa = &ccv_cnnp_variable_isa;
    3989
    1
      model_variable->super.input_size = 0;
    3990
    1
      model_variable->super.outputs = &model_variable->output;
    3991
    1
      model_variable->super.output_size = 1;
    3992
    1
      ccv_cnnp_model_copy_name(&model_variable->super, name);
    3993
    1
      model_variable->params = params;
    3994
    1
      return (ccv_cnnp_model_t*)model_variable;
    3995
    1
    }
    3996
    3997
    static ccv_cnnp_model_t* _ccv_cnnp_variable_copy(const ccv_cnnp_model_t* const super, void* const context)
    3998
    0
    {
    3999
    0
      const ccv_cnnp_model_variable_t* const self = (const ccv_cnnp_model_variable_t*)super;
    4000
    0
      return ccv_cnnp_variable(self->params, self->super.name);
    4001
    0
    }
    4002
    4003
    // MARK - Move Layer
    4004
    4005
    typedef struct {
    4006
      ccv_cnnp_model_t super;
    4007
      ccv_nnc_tensor_symbol_t output;
    4008
    } ccv_cnnp_model_move_t;
    4009
    4010
    static void _ccv_cnnp_move_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)
    4011
    3
    {
    4012
    3
      PRINT(CCV_CLI_VERBOSE, "[cnnp_move_build] -\n");
    4013
    3
      assert(input_size == 2);
    4014
    3
      assert(output_size == 1);
    4015
    3
      outputs[0] = inputs[1];
    4016
    3
      ccv_nnc_graph_exec_symbol_new(graph, CMD_FORMAT_TRANSFORM_FORWARD(), inputs, 1, outputs, 1, "move");
    4017
    3
    }
    4018
    4019
    static ccv_cnnp_model_t* _ccv_cnnp_move_copy(const ccv_cnnp_model_t* const super, void* const context);
    4020
    4021
    static const ccv_cnnp_model_vtab_t ccv_cnnp_move_isa = {
    4022
      .build = _ccv_cnnp_move_build,
    4023
      .copy = _ccv_cnnp_move_copy,
    4024
    };
    4025
    4026
    ccv_cnnp_model_t* ccv_cnnp_move(const char* const name)
    4027
    3
    {
    4028
    3
      ccv_cnnp_model_move_t* const model_move = (ccv_cnnp_model_move_t*)cccalloc(1, sizeof(ccv_cnnp_model_move_t));
    4029
    3
      model_move->super.isa = &ccv_cnnp_move_isa;
    4030
    3
      model_move->super.input_size = 2;
    4031
    3
      model_move->super.outputs = &model_move->output;
    4032
    3
      model_move->super.output_size = 1;
    4033
    3
      ccv_cnnp_model_copy_name(&model_move->super, name);
    4034
    3
      return (ccv_cnnp_model_t*)model_move;
    4035
    3
    }
    4036
    4037
    static ccv_cnnp_model_t* _ccv_cnnp_move_copy(const ccv_cnnp_model_t* const super, void* const context)
    4038
    0
    {
    4039
    0
      const ccv_cnnp_model_move_t* const self = (const ccv_cnnp_model_move_t*)super;
    4040
    0
      return ccv_cnnp_move(self->super.name);
    4041
    0
    }
    4042
    4043
    // MARK - "Making" Contiguous Layer
    4044
    4045
    typedef struct {
    4046
      ccv_cnnp_model_t super;
    4047
      ccv_nnc_tensor_symbol_t output;
    4048
    } ccv_cnnp_model_contiguous_t;
    4049
    4050
    static void _ccv_cnnp_contiguous_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)
    4051
    5
    {
    4052
    5
      PRINT(CCV_CLI_VERBOSE, "[cnnp_contiguous_build] -\n");
    4053
    5
      assert(input_size == 1);
    4054
    5
      assert(output_size == 1);
    4055
    5
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4056
    5
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    4057
    5
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    4058
    0
      {
    4059
    0
        outputs[0] = inputs[0];
    4060
    0
        return;
    4061
    0
      }
    4062
      // Otherwise, we need to check its stride to know if it is contiguous.
    4063
    5
      int old_stride[CCV_NNC_MAX_DIM_ALLOC];
    4064
    5
      ccv_nnc_tensor_symbol_alias_params(graph, inputs[0], 0, old_stride);
    4065
      // We identify permute by checking if the stride is not in descending order.
    4066
      // This also covered "permute" through reshape, rather than using ccv_cnnp_permute directly.
    4067
    5
      if (ccv_nnc_is_tensor_stride_packed(old_stride, params.dim))
    4068
    2
      {
    4069
    2
        outputs[0] = inputs[0];
    4070
    2
        return;
    4071
    2
      }
    4072
    3
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4073
    3
      ccv_nnc_graph_exec_symbol_t make_contiguous = ccv_nnc_graph_exec_symbol_new(graph, CMD_FORMAT_TRANSFORM_FORWARD(), inputs, 1, outputs, 1, "contiguous");
    4074
    3
      ccv_nnc_graph_exec_symbol_set_flags(graph, make_contiguous, CCV_NNC_GRAPH_EXEC_DISABLE_OPT);
    4075
    3
    }
    4076
    4077
    static ccv_cnnp_model_t* _ccv_cnnp_contiguous_copy(const ccv_cnnp_model_t* const super, void* const context);
    4078
    4079
    static const ccv_cnnp_model_vtab_t ccv_cnnp_contiguous_isa = {
    4080
      .build = _ccv_cnnp_contiguous_build,
    4081
      .copy = _ccv_cnnp_contiguous_copy,
    4082
    };
    4083
    4084
    ccv_cnnp_model_t* ccv_cnnp_contiguous(const char* const name)
    4085
    5
    {
    4086
    5
      ccv_cnnp_model_contiguous_t* const model_contiguous = (ccv_cnnp_model_contiguous_t*)cccalloc(1, sizeof(ccv_cnnp_model_contiguous_t));
    4087
    5
      model_contiguous->super.isa = &ccv_cnnp_contiguous_isa;
    4088
    5
      model_contiguous->super.input_size = 1;
    4089
    5
      model_contiguous->super.outputs = &model_contiguous->output;
    4090
    5
      model_contiguous->super.output_size = 1;
    4091
    5
      ccv_cnnp_model_copy_name(&model_contiguous->super, name);
    4092
    5
      return (ccv_cnnp_model_t*)model_contiguous;
    4093
    5
    }
    4094
    4095
    static ccv_cnnp_model_t* _ccv_cnnp_contiguous_copy(const ccv_cnnp_model_t* const super, void* const context)
    4096
    0
    {
    4097
    0
      const ccv_cnnp_model_contiguous_t* const self = (const ccv_cnnp_model_contiguous_t*)super;
    4098
    0
      return ccv_cnnp_contiguous(self->super.name);
    4099
    0
    }
    4100
    4101
    // MARK - "Making" Copy Layer
    4102
    4103
    typedef struct {
    4104
      ccv_cnnp_model_t super;
    4105
      ccv_nnc_tensor_symbol_t output;
    4106
    } ccv_cnnp_model_copy_t;
    4107
    4108
    static void _ccv_cnnp_copy_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)
    4109
    0
    {
    4110
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_copy_build] -\n");
    4111
    0
      assert(input_size == 1);
    4112
    0
      assert(output_size == 1);
    4113
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4114
    0
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    4115
    0
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    4116
    0
      {
    4117
    0
        outputs[0] = inputs[0];
    4118
    0
        return;
    4119
    0
      }
    4120
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4121
    0
      ccv_nnc_graph_exec_symbol_t make_contiguous = ccv_nnc_graph_exec_symbol_new(graph, CMD_FORMAT_TRANSFORM_FORWARD(), inputs, 1, outputs, 1, "contiguous");
    4122
    0
      ccv_nnc_graph_exec_symbol_set_flags(graph, make_contiguous, CCV_NNC_GRAPH_EXEC_DISABLE_OPT);
    4123
    0
    }
    4124
    4125
    static ccv_cnnp_model_t* _ccv_cnnp_copy_copy(const ccv_cnnp_model_t* const super, void* const context);
    4126
    4127
    static const ccv_cnnp_model_vtab_t ccv_cnnp_copy_isa = {
    4128
      .build = _ccv_cnnp_copy_build,
    4129
      .copy = _ccv_cnnp_copy_copy,
    4130
    };
    4131
    4132
    ccv_cnnp_model_t* ccv_cnnp_copy(const char* const name)
    4133
    0
    {
    4134
    0
      ccv_cnnp_model_copy_t* const model_copy = (ccv_cnnp_model_copy_t*)cccalloc(1, sizeof(ccv_cnnp_model_copy_t));
    4135
    0
      model_copy->super.isa = &ccv_cnnp_copy_isa;
    4136
    0
      model_copy->super.input_size = 1;
    4137
    0
      model_copy->super.outputs = &model_copy->output;
    4138
    0
      model_copy->super.output_size = 1;
    4139
    0
      ccv_cnnp_model_copy_name(&model_copy->super, name);
    4140
    0
      return (ccv_cnnp_model_t*)model_copy;
    4141
    0
    }
    4142
    4143
    static ccv_cnnp_model_t* _ccv_cnnp_copy_copy(const ccv_cnnp_model_t* const super, void* const context)
    4144
    0
    {
    4145
    0
      const ccv_cnnp_model_copy_t* const self = (const ccv_cnnp_model_copy_t*)super;
    4146
    0
      return ccv_cnnp_copy(self->super.name);
    4147
    0
    }
    4148
    4149
    // MARK - Scaled-Dot Product Attention Layer
    4150
    4151
    typedef struct {
    4152
      ccv_cnnp_model_t super;
    4153
      ccv_nnc_tensor_symbol_t output;
    4154
      ccv_nnc_tensor_symbol_t weights;
    4155
      ccv_nnc_tensor_symbol_t bias;
    4156
      float scale;
    4157
      int is_causal;
    4158
      int has_attn_mask;
    4159
      int flags;
    4160
      int fused_unify_head_weights;
    4161
      int no_bias;
    4162
    } ccv_cnnp_model_scaled_dot_product_attention_t;
    4163
    4164
    static void _ccv_cnnp_scaled_dot_product_attention_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)
    4165
    3
    {
    4166
    3
      PRINT(CCV_CLI_VERBOSE, "[cnnp_scaled_dot_product_attention_build] -\n");
    4167
    3
      assert(input_size == 3 || input_size == 4);
    4168
    3
      assert(output_size == 1);
    4169
    3
      ccv_cnnp_model_scaled_dot_product_attention_t* const self = (ccv_cnnp_model_scaled_dot_product_attention_t*)super;
    4170
    3
      const ccv_nnc_tensor_param_t q_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4171
    3
      const ccv_nnc_tensor_param_t k_params = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    4172
    3
      const ccv_nnc_tensor_param_t v_params = ccv_nnc_tensor_symbol_params(graph, inputs[2]);
    4173
    3
      const int v_nd = ccv_nnc_tensor_nd(v_params.dim);
    4174
    3
      assert(v_nd == 3 || v_nd == 4);
    4175
    3
      const int hEv = (v_nd == 3 ? 10 : v_params.dim[2]) * v_params.dim[v_nd - 1];
    4176
    3
      ccv_nnc_tensor_param_t weights_params = q_params;
    4177
    3
      memset(weights_params.dim, 0, sizeof(weights_params.dim));
    4178
    3
      weights_params.dim[0] = hEv;
    4179
    3
      weights_params.dim[1] = hEv;
    4180
    3
      ccv_nnc_tensor_param_t bias_params = q_params;
    4181
    3
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    4182
    3
      bias_params.dim[0] = hEv;
    4183
    3
      ccv_nnc_cmd_t cmd = {0};
    4184
    3
      cmd.cmd = CCV_NNC_SCALED_DOT_PRODUCT_ATTENTION_FORWARD;
    4185
    3
      cmd.info.scaled_dot_product_attention.scale = self->scale;
    4186
    3
      cmd.info.scaled_dot_product_attention.is_causal = self->is_causal;
    4187
    3
      cmd.info.scaled_dot_product_attention.flags = self->flags;
    4188
    3
      ccv_nnc_tensor_param_t output_params[3];
    4189
    3
      ccv_nnc_tensor_symbol_t output;
    4190
    3
      ccv_nnc_tensor_symbol_t saved_softmax_lse;
    4191
    3
      ccv_nnc_tensor_symbol_t saved_v_proj = NO_TENSOR_SYMBOL;
    4192
    3
      ccv_nnc_tensor_symbol_t attn_mask = NO_TENSOR_SYMBOL;
    4193
    3
      ccv_nnc_tensor_symbol_t weights = NO_TENSOR_SYMBOL;
    4194
    3
      ccv_nnc_tensor_symbol_t bias = NO_TENSOR_SYMBOL;
    4195
    3
      if (self->has_attn_mask)
    4196
    1
        attn_mask = inputs[3];
    4197
    3
      if (self->fused_unify_head_weights)
    4198
    1
      {
    4199
    1
        if (!self->weights.graph)
    4200
    1
          self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    4201
    1
        weights = self->weights;
    4202
    1
        if (!self->no_bias)
    4203
    1
        {
    4204
    1
          if (!self->bias.graph)
    4205
    1
            self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    4206
    1
          bias = self->bias;
    4207
    1
        }
    4208
    1
        ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    4209
    1
            q_params,
    4210
    1
            k_params,
    4211
    1
            v_params,
    4212
    1
            (ccv_nnc_tensor_param_t){},
    4213
    1
            weights_params,
    4214
    1
            bias_params,
    4215
    1
          }, 6, ccv_nnc_no_hint, output_params, 3);
    4216
    1
        output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    4217
    1
        saved_softmax_lse = ccv_nnc_tensor_symbol_new(graph, output_params[1], 0);
    4218
    1
        saved_v_proj = ccv_nnc_tensor_symbol_new(graph, output_params[2], 0);
    4219
    2
      } else {
    4220
    2
        ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    4221
    2
            q_params,
    4222
    2
            k_params,
    4223
    2
            v_params,
    4224
    2
          }, 3, ccv_nnc_no_hint, output_params, 2);
    4225
    2
        output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    4226
    2
        saved_softmax_lse = ccv_nnc_tensor_symbol_new(graph, output_params[1], 0);
    4227
    2
      }
    4228
    3
      ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], inputs[1], inputs[2], attn_mask, weights, bias), TENSOR_SYMBOL_LIST(output, saved_softmax_lse, saved_v_proj), "scaled_dot_product_attention");
    4229
    3
      outputs[0] = output;
    4230
    3
    }
    4231
    4232
    static void _ccv_cnnp_scaled_dot_product_attention_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)
    4233
    0
    {
    4234
    0
      ccv_cnnp_model_scaled_dot_product_attention_t* const self = (ccv_cnnp_model_scaled_dot_product_attention_t*)super;
    4235
    0
      if (self->weights.graph)
    4236
    0
      {
    4237
    0
        assert(self->fused_unify_head_weights);
    4238
    0
        const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    4239
    0
        const int c = weight_params.dim[1];
    4240
    0
        const float std = sqrtf(2) / sqrtf(c);
    4241
    0
        const float bound = sqrtf(3) * std;
    4242
    0
        initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    4243
    0
        if (self->bias.graph)
    4244
    0
          initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    4245
    0
      }
    4246
    0
    }
    4247
    4248
    static void _ccv_cnnp_scaled_dot_product_attention_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)
    4249
    1
    {
    4250
    1
      ccv_cnnp_model_scaled_dot_product_attention_t* const self = (ccv_cnnp_model_scaled_dot_product_attention_t*)super;
    4251
    1
      if (self->weights.graph)
    4252
    1
      {
    4253
    1
        assert(self->fused_unify_head_weights);
    4254
    1
        add_to_array(parameters, self->weights, is_trainable);
    4255
    1
        if (self->bias.graph)
    4256
    1
          add_to_array(parameters, self->bias, is_trainable);
    4257
    1
      }
    4258
    1
    }
    4259
    4260
    static ccv_cnnp_model_t* _ccv_cnnp_scaled_dot_product_attention_copy(const ccv_cnnp_model_t* const super, void* const context);
    4261
    4262
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scaled_dot_product_attention_isa = {
    4263
      .build = _ccv_cnnp_scaled_dot_product_attention_build,
    4264
      .copy = _ccv_cnnp_scaled_dot_product_attention_copy,
    4265
    };
    4266
    4267
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scaled_dot_product_attention_fused_isa = {
    4268
      .build = _ccv_cnnp_scaled_dot_product_attention_build,
    4269
      .init_states = _ccv_cnnp_scaled_dot_product_attention_init_states,
    4270
      .add_to_parameter = _ccv_cnnp_scaled_dot_product_attention_add_to_parameter,
    4271
      .copy = _ccv_cnnp_scaled_dot_product_attention_copy,
    4272
    };
    4273
    4274
    ccv_cnnp_model_t* ccv_cnnp_scaled_dot_product_attention(const float scale, const int is_causal, const int has_attn_mask, const int flags, const int fused_unify_head_weights, const int no_bias, const int is_trainable, const char* const name)
    4275
    3
    {
    4276
    3
      ccv_cnnp_model_scaled_dot_product_attention_t* const model_scaled_dot_product_attention = (ccv_cnnp_model_scaled_dot_product_attention_t*)cccalloc(1, sizeof(ccv_cnnp_model_scaled_dot_product_attention_t));
    4277
    3
      model_scaled_dot_product_attention->super.isa = fused_unify_head_weights ? &ccv_cnnp_scaled_dot_product_attention_fused_isa1 : &ccv_cnnp_scaled_dot_product_attention_isa2;
    4278
    3
      model_scaled_dot_product_attention->super.input_size = has_attn_mask ? 41 : 32;
    4279
    3
      model_scaled_dot_product_attention->super.outputs = &model_scaled_dot_product_attention->output;
    4280
    3
      model_scaled_dot_product_attention->super.output_size = 1;
    4281
    3
      model_scaled_dot_product_attention->super.is_trainable = is_trainable;
    4282
    3
      ccv_cnnp_model_copy_name(&model_scaled_dot_product_attention->super, name);
    4283
    3
      model_scaled_dot_product_attention->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    4284
    3
      model_scaled_dot_product_attention->weights.graph = 0;
    4285
    3
      model_scaled_dot_product_attention->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    4286
    3
      model_scaled_dot_product_attention->bias.graph = 0;
    4287
    3
      model_scaled_dot_product_attention->scale = scale;
    4288
    3
      model_scaled_dot_product_attention->is_causal = is_causal;
    4289
    3
      model_scaled_dot_product_attention->has_attn_mask = has_attn_mask;
    4290
    3
      model_scaled_dot_product_attention->flags = flags;
    4291
    3
      model_scaled_dot_product_attention->fused_unify_head_weights = fused_unify_head_weights;
    4292
    3
      model_scaled_dot_product_attention->no_bias = no_bias;
    4293
    3
      return (ccv_cnnp_model_t*)model_scaled_dot_product_attention;
    4294
    3
    }
    4295
    4296
    static ccv_cnnp_model_t* _ccv_cnnp_scaled_dot_product_attention_copy(const ccv_cnnp_model_t* const super, void* const context)
    4297
    0
    {
    4298
    0
      const ccv_cnnp_model_scaled_dot_product_attention_t* const self = (const ccv_cnnp_model_scaled_dot_product_attention_t*)super;
    4299
    0
      return ccv_cnnp_scaled_dot_product_attention(self->scale, self->is_causal, self->has_attn_mask, self->flags, self->fused_unify_head_weights, self->no_bias, self->super.is_trainable, self->super.name);
    4300
    0
    }
    4301
    4302
    // MARK - Debug Layer
    4303
    4304
    typedef struct {
    4305
      ccv_cnnp_model_t super;
    4306
      ccv_nnc_tensor_symbol_t output;
    4307
      ccv_cnnp_model_debug_f debugger;
    4308
      ccv_cnnp_model_debug_context_deinit_f debug_deinit;
    4309
      ccv_cnnp_model_debug_context_copy_f debug_copy;
    4310
      void* debug_context;
    4311
    } ccv_cnnp_model_debug_t;
    4312
    4313
    static int _ccv_cnnp_debug_exec(const ccv_nnc_cmd_t cmd, const ccv_nnc_hint_t hint, const int flags, ccv_nnc_tensor_t* const* const inputs, const int input_size, ccv_nnc_tensor_t* const* const outputs, const int output_size, ccv_nnc_stream_context_t* const stream_context)
    4314
    1
    {
    4315
    1
      if (cmd.cmd == CCV_NNC_CUSTOM_BACKWARD)
    4316
    0
      {
    4317
    0
        assert(0 && "don't support debug backward pass yet");
    4318
    0
      }
    4319
    1
      ccv_cnnp_model_debug_t* const self = (ccv_cnnp_model_debug_t*)cmd.data;
    4320
    1
      self->debugger(inputs, input_size, stream_context, self->debug_context);
    4321
    1
      return CCV_NNC_EXEC_SUCCESS;
    4322
    1
    }
    4323
    4324
    static ccv_nnc_cmd_vtab_t ccv_cnnp_debug_exec_isa = {
    4325
      .exec = _ccv_cnnp_debug_exec
    4326
    };
    4327
    4328
    static void _ccv_cnnp_debug_build(ccv_cnnp_model_t* const self, 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)
    4329
    1
    {
    4330
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_debug_build] -\n");
    4331
    1
      assert(input_size >= 1);
    4332
    1
      assert(output_size == 1);
    4333
    1
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    4334
    1
      ccv_nnc_tensor_param_t output_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4335
    1
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    4336
    1
      {
    4337
    1
        int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
    4338
    1
        int stride[CCV_NNC_MAX_DIM_ALLOC];
    4339
    1
        ccv_nnc_tensor_get_stride(output_params.dim, stride);
    4340
    1
        outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], ofs, stride, output_params, 0);
    4341
    1
      } else {
    4342
    0
        int old_ofs[CCV_NNC_MAX_DIM_ALLOC];
    4343
    0
        int old_stride[CCV_NNC_MAX_DIM_ALLOC];
    4344
    0
        ccv_nnc_tensor_symbol_alias_params(graph, inputs[0], old_ofs, old_stride);
    4345
    0
        outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, to, old_ofs, old_stride, output_params, 0);
    4346
    0
      }
    4347
    1
      ccv_nnc_cmd_t cmd = ccv_nnc_cmd(CCV_NNC_CUSTOM_FORWARD, (ccv_nnc_cmd_vtab_t*)&ccv_cnnp_debug_exec_isa, (ccv_nnc_cmd_param_t){}, 0);
    4348
    1
      cmd.data = self;
    4349
    1
      ccv_nnc_graph_exec_symbol_t make_debug = ccv_nnc_graph_exec_symbol_new(graph, cmd, inputs, input_size, outputs, 1, "debug");
    4350
      // Disable any optimizations.
    4351
    1
      ccv_nnc_graph_exec_symbol_set_flags(graph, make_debug, CCV_NNC_GRAPH_EXEC_DISABLE_OPT);
    4352
    1
    }
    4353
    4354
    static void _ccv_cnnp_debug_deinit(ccv_cnnp_model_t* const super)
    4355
    1
    {
    4356
    1
      const ccv_cnnp_model_debug_t* const self = (const ccv_cnnp_model_debug_t*)super;
    4357
    1
      if (self->debug_deinit && self->debug_context0)
    4358
    0
        self->debug_deinit(self->debug_context);
    4359
    1
    }
    4360
    4361
    static ccv_cnnp_model_t* _ccv_cnnp_debug_copy(const ccv_cnnp_model_t* const super, void* const context);
    4362
    4363
    static const ccv_cnnp_model_vtab_t ccv_cnnp_debug_isa = {
    4364
      .build = _ccv_cnnp_debug_build,
    4365
      .deinit = _ccv_cnnp_debug_deinit,
    4366
      .copy = _ccv_cnnp_debug_copy,
    4367
    };
    4368
    4369
    ccv_cnnp_model_t* ccv_cnnp_debug(ccv_cnnp_model_debug_f func, void* const context, ccv_cnnp_model_debug_context_deinit_f deinit, ccv_cnnp_model_debug_context_copy_f copy, const char* const name)
    4370
    1
    {
    4371
    1
      ccv_cnnp_model_debug_t* const model_debug = (ccv_cnnp_model_debug_t*)cccalloc(1, sizeof(ccv_cnnp_model_debug_t));
    4372
    1
      model_debug->super.isa = &ccv_cnnp_debug_isa;
    4373
    1
      model_debug->super.input_size = 0;
    4374
    1
      model_debug->super.outputs = &model_debug->output;
    4375
    1
      model_debug->super.output_size = 1;
    4376
    1
      model_debug->debugger = func;
    4377
    1
      model_debug->debug_context = context;
    4378
    1
      model_debug->debug_deinit = deinit;
    4379
    1
      model_debug->debug_copy = copy;
    4380
    1
      ccv_cnnp_model_copy_name(&model_debug->super, name);
    4381
    1
      return (ccv_cnnp_model_t*)model_debug;
    4382
    1
    }
    4383
    4384
    static ccv_cnnp_model_t* _ccv_cnnp_debug_copy(const ccv_cnnp_model_t* const super, void* const context)
    4385
    0
    {
    4386
    0
      const ccv_cnnp_model_debug_t* const self = (const ccv_cnnp_model_debug_t*)super;
    4387
    0
      void* debug_context = self->debug_context;
    4388
    0
      if (self->debug_copy && self->debug_context)
    4389
    0
        debug_context = self->debug_copy(self->debug_context);
    4390
    0
      return ccv_cnnp_debug(self->debugger, debug_context, self->debug_deinit, self->debug_copy, self->super.name);
    4391
    0
    }
    4392
    4393
    /// MARK - Sort layer.
    4394
    4395
    typedef struct {
    4396
      ccv_cnnp_model_t super;
    4397
      ccv_nnc_tensor_symbol_t outputs[2];
    4398
      int along_axis;
    4399
      int descending;
    4400
    } ccv_cnnp_model_sort_t;
    4401
    4402
    static void _ccv_cnnp_sort_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)
    4403
    1
    {
    4404
    1
      ccv_cnnp_model_sort_t* const self = (ccv_cnnp_model_sort_t*)super;
    4405
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sort_build] - along_axis: %d, descending: %d\n", self->along_axis, self->descending);
    4406
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4407
    1
      assert(output_size == 2);
    4408
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4409
    1
      params.datatype = CCV_32S;
    4410
    1
      outputs[1] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4411
    1
      ccv_nnc_graph_exec_symbol_new(graph, CMD_SORT_FORWARD(self->along_axis, self->descending), inputs, input_size, outputs, output_size, "sort");
    4412
    1
    }
    4413
    4414
    static ccv_cnnp_model_t* _ccv_cnnp_sort_copy(const ccv_cnnp_model_t* const self, void* const context);
    4415
    4416
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sort_isa = {
    4417
      .build = _ccv_cnnp_sort_build,
    4418
      .copy = _ccv_cnnp_sort_copy,
    4419
    };
    4420
    4421
    ccv_cnnp_model_t* ccv_cnnp_sort(const int along_axis, const int descending, const char* const name)
    4422
    1
    {
    4423
    1
      ccv_cnnp_model_sort_t* const model_sort = (ccv_cnnp_model_sort_t*)cccalloc(1, sizeof(ccv_cnnp_model_sort_t));
    4424
    1
      model_sort->super.isa = &ccv_cnnp_sort_isa;
    4425
    1
      model_sort->super.input_size = 0;
    4426
    1
      model_sort->super.outputs = model_sort->outputs;
    4427
    1
      model_sort->super.output_size = 2;
    4428
    1
      model_sort->along_axis = along_axis;
    4429
    1
      model_sort->descending = descending;
    4430
    1
      ccv_cnnp_model_copy_name(&model_sort->super, name);
    4431
    1
      return (ccv_cnnp_model_t*)model_sort;
    4432
    1
    }
    4433
    4434
    static ccv_cnnp_model_t* _ccv_cnnp_sort_copy(const ccv_cnnp_model_t* const super, void* const context)
    4435
    0
    {
    4436
    0
      ccv_cnnp_model_sort_t* const self = (ccv_cnnp_model_sort_t*)super;
    4437
    0
      return ccv_cnnp_sort(self->along_axis, self->descending, self->super.name);
    4438
    0
    }
    4439
    4440
    /// MARK - Partition layer.
    4441
    4442
    typedef struct {
    4443
      ccv_cnnp_model_t super;
    4444
      ccv_nnc_tensor_symbol_t outputs[2];
    4445
      int kth;
    4446
      int along_axis;
    4447
      int descending;
    4448
    } ccv_cnnp_model_partition_t;
    4449
    4450
    static void _ccv_cnnp_partition_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)
    4451
    1
    {
    4452
    1
      ccv_cnnp_model_partition_t* const self = (ccv_cnnp_model_partition_t*)super;
    4453
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_partition_build] - kth: %d, along_axis: %d, descending: %d\n", self->kth, self->along_axis, self->descending);
    4454
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4455
    1
      assert(output_size == 2);
    4456
    1
      if (self->kth > 0)
    4457
    1
        params.dim[self->along_axis] = self->kth;
    4458
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4459
    1
      params.datatype = CCV_32S;
    4460
    1
      outputs[1] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4461
    1
      ccv_nnc_graph_exec_symbol_new(graph, CMD_PARTITION_FORWARD(self->kth, self->along_axis, self->descending), inputs, input_size, outputs, output_size, "partition");
    4462
    1
    }
    4463
    4464
    static ccv_cnnp_model_t* _ccv_cnnp_partition_copy(const ccv_cnnp_model_t* const self, void* const context);
    4465
    4466
    static const ccv_cnnp_model_vtab_t ccv_cnnp_partition_isa = {
    4467
      .build = _ccv_cnnp_partition_build,
    4468
      .copy = _ccv_cnnp_partition_copy,
    4469
    };
    4470
    4471
    ccv_cnnp_model_t* ccv_cnnp_partition(const int kth, const int along_axis, const int descending, const char* const name)
    4472
    1
    {
    4473
    1
      ccv_cnnp_model_partition_t* const model_partition = (ccv_cnnp_model_partition_t*)cccalloc(1, sizeof(ccv_cnnp_model_partition_t));
    4474
    1
      model_partition->super.isa = &ccv_cnnp_partition_isa;
    4475
    1
      model_partition->super.input_size = 0;
    4476
    1
      model_partition->super.outputs = model_partition->outputs;
    4477
    1
      model_partition->super.output_size = 2;
    4478
    1
      model_partition->kth = kth;
    4479
    1
      model_partition->along_axis = along_axis;
    4480
    1
      model_partition->descending = descending;
    4481
    1
      ccv_cnnp_model_copy_name(&model_partition->super, name);
    4482
    1
      return (ccv_cnnp_model_t*)model_partition;
    4483
    1
    }
    4484
    4485
    static ccv_cnnp_model_t* _ccv_cnnp_partition_copy(const ccv_cnnp_model_t* const super, void* const context)
    4486
    0
    {
    4487
    0
      ccv_cnnp_model_partition_t* const self = (ccv_cnnp_model_partition_t*)super;
    4488
    0
      return ccv_cnnp_partition(self->kth, self->along_axis, self->descending, self->super.name);
    4489
    0
    }
    4490
    4491
    /// MARK - Unique consecutive layer.
    4492
    4493
    typedef struct {
    4494
      ccv_cnnp_model_t super;
    4495
      ccv_nnc_tensor_symbol_t outputs[2];
    4496
      int bincount;
    4497
    } ccv_cnnp_model_unique_consecutive_t;
    4498
    4499
    static void _ccv_cnnp_unique_consecutive_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)
    4500
    1
    {
    4501
    1
      ccv_cnnp_model_unique_consecutive_t* const self = (ccv_cnnp_model_unique_consecutive_t*)super;
    4502
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_unique_consecutive_build] - bincount: %d\n", self->bincount);
    4503
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4504
    1
      assert(output_size == 2);
    4505
    1
      if (self->bincount > 0)
    4506
    1
        params.dim[0] = ccv_min(params.dim[0], self->bincount);
    4507
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4508
    1
      params.datatype = CCV_32S;
    4509
    1
      outputs[1] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4510
    1
      ccv_nnc_graph_exec_symbol_new(graph, CMD_UNIQUE_CONSECUTIVE_FORWARD(self->bincount), inputs, input_size, outputs, output_size, "unique_consecutive");
    4511
    1
    }
    4512
    4513
    static ccv_cnnp_model_t* _ccv_cnnp_unique_consecutive_copy(const ccv_cnnp_model_t* const self, void* const context);
    4514
    4515
    static const ccv_cnnp_model_vtab_t ccv_cnnp_unique_consecutive_isa = {
    4516
      .build = _ccv_cnnp_unique_consecutive_build,
    4517
      .copy = _ccv_cnnp_unique_consecutive_copy,
    4518
    };
    4519
    4520
    ccv_cnnp_model_t* ccv_cnnp_unique_consecutive(const int bincount, const char* const name)
    4521
    1
    {
    4522
    1
      ccv_cnnp_model_unique_consecutive_t* const model_unique_consecutive = (ccv_cnnp_model_unique_consecutive_t*)cccalloc(1, sizeof(ccv_cnnp_model_unique_consecutive_t));
    4523
    1
      model_unique_consecutive->super.isa = &ccv_cnnp_unique_consecutive_isa;
    4524
    1
      model_unique_consecutive->super.input_size = 0;
    4525
    1
      model_unique_consecutive->super.outputs = model_unique_consecutive->outputs;
    4526
    1
      model_unique_consecutive->super.output_size = 2;
    4527
    1
      model_unique_consecutive->bincount = bincount;
    4528
    1
      ccv_cnnp_model_copy_name(&model_unique_consecutive->super, name);
    4529
    1
      return (ccv_cnnp_model_t*)model_unique_consecutive;
    4530
    1
    }
    4531
    4532
    static ccv_cnnp_model_t* _ccv_cnnp_unique_consecutive_copy(const ccv_cnnp_model_t* const super, void* const context)
    4533
    0
    {
    4534
    0
      ccv_cnnp_model_unique_consecutive_t* const self = (ccv_cnnp_model_unique_consecutive_t*)super;
    4535
    0
      return ccv_cnnp_unique_consecutive(self->bincount, self->super.name);
    4536
    0
    }
    4537
    4538
    /// MARK - Scatter add layer.
    4539
    4540
    typedef struct {
    4541
      ccv_cnnp_model_t super;
    4542
      ccv_nnc_tensor_symbol_t output;
    4543
      int bincount;
    4544
    } ccv_cnnp_model_scatter_add_t;
    4545
    4546
    static void _ccv_cnnp_scatter_add_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)
    4547
    1
    {
    4548
    1
      ccv_cnnp_model_scatter_add_t* const self = (ccv_cnnp_model_scatter_add_t*)super;
    4549
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_scatter_add_build] - bincount: %d\n", self->bincount);
    4550
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4551
    1
      assert(output_size == 1);
    4552
    1
      assert(self->bincount > 0);
    4553
    1
      params.dim[0] = self->bincount;
    4554
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4555
    1
      ccv_nnc_graph_exec_symbol_new(graph, CMD_SCATTER_ADD_FORWARD(self->bincount), inputs, input_size, outputs, output_size, "scatter_add");
    4556
    1
    }
    4557
    4558
    static ccv_cnnp_model_t* _ccv_cnnp_scatter_add_copy(const ccv_cnnp_model_t* const self, void* const context);
    4559
    4560
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scatter_add_isa = {
    4561
      .build = _ccv_cnnp_scatter_add_build,
    4562
      .copy = _ccv_cnnp_scatter_add_copy,
    4563
    };
    4564
    4565
    ccv_cnnp_model_t* ccv_cnnp_scatter_add(const int bincount, const char* const name)
    4566
    1
    {
    4567
    1
      assert(bincount > 0);
    4568
    1
      ccv_cnnp_model_scatter_add_t* const model_scatter_add = (ccv_cnnp_model_scatter_add_t*)cccalloc(1, sizeof(ccv_cnnp_model_scatter_add_t));
    4569
    1
      model_scatter_add->super.isa = &ccv_cnnp_scatter_add_isa;
    4570
    1
      model_scatter_add->super.input_size = 0;
    4571
    1
      model_scatter_add->super.outputs = &model_scatter_add->output;
    4572
    1
      model_scatter_add->super.output_size = 1;
    4573
    1
      model_scatter_add->bincount = bincount;
    4574
    1
      ccv_cnnp_model_copy_name(&model_scatter_add->super, name);
    4575
    1
      return (ccv_cnnp_model_t*)model_scatter_add;
    4576
    1
    }
    4577
    4578
    static ccv_cnnp_model_t* _ccv_cnnp_scatter_add_copy(const ccv_cnnp_model_t* const super, void* const context)
    4579
    0
    {
    4580
    0
      ccv_cnnp_model_scatter_add_t* const self = (ccv_cnnp_model_scatter_add_t*)super;
    4581
    0
      return ccv_cnnp_scatter_add(self->bincount, self->super.name);
    4582
    0
    }
    4583
    4584
    // MARK - Segmented Dense Layer
    4585
    4586
    typedef struct {
    4587
      ccv_cnnp_model_t super;
    4588
      ccv_nnc_tensor_symbol_t output;
    4589
      ccv_nnc_tensor_symbol_t weights;
    4590
      ccv_nnc_tensor_symbol_t bias;
    4591
      int segments;
    4592
      int count;
    4593
      int no_bias;
    4594
      int flags;
    4595
    } ccv_cnnp_model_segmented_dense_t;
    4596
    4597
    static void _ccv_cnnp_segmented_dense_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)
    4598
    2
    {
    4599
    2
      ccv_cnnp_model_segmented_dense_t* const self = (ccv_cnnp_model_segmented_dense_t*)super;
    4600
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_segmented_dense_build] -\n");
    4601
    2
      assert(input_size == 3);
    4602
    2
      assert(output_size == 1);
    4603
    2
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4604
    2
      const ccv_nnc_tensor_param_t indices_params = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    4605
    2
      const ccv_nnc_tensor_param_t counts_params = ccv_nnc_tensor_symbol_params(graph, inputs[2]);
    4606
    2
      ccv_nnc_tensor_param_t weights_params = params;
    4607
    2
      memset(weights_params.dim, 0, sizeof(weights_params.dim));
    4608
    2
      weights_params.dim[0] = self->segments;
    4609
    2
      weights_params.dim[1] = self->count;
    4610
    2
      weights_params.dim[2] = params.dim[ccv_nnc_tensor_nd(params.dim) - 1];
    4611
    2
      if (!self->weights.graph)
    4612
    2
        self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    4613
    2
      assert(self->weights.graph == graph);
    4614
    2
      ccv_nnc_tensor_param_t bias_params = params;
    4615
    2
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    4616
    2
      bias_params.dim[0] = self->segments;
    4617
    2
      bias_params.dim[1] = self->count;
    4618
    2
      ccv_nnc_cmd_t cmd = {0};
    4619
    2
      cmd.cmd = CCV_NNC_SEGMENTED_GEMM_FORWARD;
    4620
    2
      cmd.info.blas.a[0] = 1;
    4621
    2
      cmd.info.blas.a[1] = 1;
    4622
    2
      cmd.info.blas.transpose_b[0] = 1;
    4623
    2
      cmd.info.blas.transpose_b[1] = 2;
    4624
    2
      cmd.info.blas.flags = self->flags;
    4625
    2
      ccv_nnc_tensor_param_t output_params;
    4626
    2
      ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    4627
    2
          params, indices_params, counts_params,
    4628
    2
          weights_params,
    4629
    2
          bias_params,
    4630
    2
        }, 5, ccv_nnc_no_hint, &output_params, 1);
    4631
    2
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    4632
    2
      if (self->no_bias)
    4633
    1
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], inputs[1], inputs[2], self->weights), TENSOR_SYMBOL_LIST(output), "segmented_dense");
    4634
    1
      else {
    4635
    1
        if (!self->bias.graph)
    4636
    1
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    4637
    1
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], inputs[1], inputs[2], self->weights, self->bias), TENSOR_SYMBOL_LIST(output), "segmented_dense");
    4638
    1
      }
    4639
    2
      outputs[0] = output;
    4640
    2
    }
    4641
    4642
    static void _ccv_cnnp_segmented_dense_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)
    4643
    0
    {
    4644
    0
      ccv_cnnp_model_segmented_dense_t* const self = (ccv_cnnp_model_segmented_dense_t*)super;
    4645
    0
      const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    4646
    0
      const int c = weight_params.dim[1];
    4647
    0
      const float std = sqrtf(2) / sqrtf(c);
    4648
    0
      const float bound = sqrtf(3) * std;
    4649
    0
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    4650
    0
      if (self->bias.graph)
    4651
    0
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    4652
    0
    }
    4653
    4654
    static void _ccv_cnnp_segmented_dense_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)
    4655
    2
    {
    4656
    2
      ccv_cnnp_model_segmented_dense_t* const self = (ccv_cnnp_model_segmented_dense_t*)super;
    4657
    2
      add_to_array(parameters, self->weights, is_trainable);
    4658
    2
      if (self->bias.graph)
    4659
    1
        add_to_array(parameters, self->bias, is_trainable);
    4660
    2
    }
    4661
    4662
    static ccv_cnnp_model_t* _ccv_cnnp_segmented_dense_copy(const ccv_cnnp_model_t* const super, void* const context);
    4663
    4664
    static const ccv_cnnp_model_vtab_t ccv_cnnp_segmented_dense_isa = {
    4665
      .build = _ccv_cnnp_segmented_dense_build,
    4666
      .init_states = _ccv_cnnp_segmented_dense_init_states,
    4667
      .add_to_parameter = _ccv_cnnp_segmented_dense_add_to_parameter,
    4668
      .copy = _ccv_cnnp_segmented_dense_copy,
    4669
    };
    4670
    4671
    ccv_cnnp_model_t* ccv_cnnp_segmented_dense(const int segments, const int count, const int no_bias, const int flags, const int is_trainable, const char* const name)
    4672
    2
    {
    4673
    2
      ccv_cnnp_model_segmented_dense_t* const model_segmented_dense = (ccv_cnnp_model_segmented_dense_t*)cccalloc(1, sizeof(ccv_cnnp_model_segmented_dense_t));
    4674
    2
      model_segmented_dense->super.isa = &ccv_cnnp_segmented_dense_isa;
    4675
    2
      model_segmented_dense->super.input_size = 3;
    4676
    2
      model_segmented_dense->super.outputs = &model_segmented_dense->output;
    4677
    2
      model_segmented_dense->super.output_size = 1;
    4678
    2
      model_segmented_dense->super.is_trainable = is_trainable;
    4679
    2
      ccv_cnnp_model_copy_name(&model_segmented_dense->super, name);
    4680
    2
      model_segmented_dense->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    4681
    2
      model_segmented_dense->weights.graph = 0;
    4682
    2
      model_segmented_dense->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    4683
    2
      model_segmented_dense->bias.graph = 0;
    4684
    2
      model_segmented_dense->segments = segments;
    4685
    2
      model_segmented_dense->count = count;
    4686
    2
      model_segmented_dense->no_bias = no_bias;
    4687
    2
      model_segmented_dense->flags = flags;
    4688
    2
      return (ccv_cnnp_model_t*)model_segmented_dense;
    4689
    2
    }
    4690
    4691
    static ccv_cnnp_model_t* _ccv_cnnp_segmented_dense_copy(const ccv_cnnp_model_t* const super, void* const context)
    4692
    0
    {
    4693
    0
      const ccv_cnnp_model_segmented_dense_t* const self = (const ccv_cnnp_model_segmented_dense_t*)super;
    4694
    0
      return ccv_cnnp_segmented_dense(self->segments, self->count, self->no_bias, self->flags, self->super.is_trainable, self->super.name);
    4695
    0
    }