## Coverage Report

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

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

    /home/liu/actions-runner/_work/ccv/ccv/lib/nnc/ccv_cnnp_model_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
    6
    {
    461
    6
      const ccv_cnnp_model_concat_t* const self = (const ccv_cnnp_model_concat_t*)super;
    462
    6
      PRINT(CCV_CLI_VERBOSE, "[cnnp_chunk_build] 1. axis: %d\n", self->axis);
    463
    6
      assert(input_size == 1);
    464
    6
      const ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    465
    6
      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
    6
      ccv_nnc_tensor_param_t output_params = input_params;
    474
    6
      int i;
    475
    6
      const int nd = ccv_nnc_tensor_nd(output_params.dim);
    476
    6
      const int axis = self->axis;
    477
    6
      assert(axis < nd);
    478
    6
      const int n = self->super.output_size;
    479
    6
      assert(n == output_size);
    480
    6
      assert(output_params.dim[axis] % n == 0);
    481
    6
      output_params.dim[axis] = output_params.dim[axis] / n;
    482
    6
      int ofs[CCV_NNC_MAX_DIM_ALLOC] = {};
    483
    6
      int stride[CCV_NNC_MAX_DIM_ALLOC] = {};
    484
    6
      ccv_nnc_tensor_get_stride(input_params.dim, stride);
    485
    6
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    486
    6
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    487
    6
      {
    488
    26
        for (i = 0; i < output_size; i++20)
    489
    20
        {
    490
    20
          outputs[i] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], ofs, stride, output_params, 0);
    491
    20
          ofs[axis] += output_params.dim[axis];
    492
    20
        }
    493
    6
      } 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
    6
    }
    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
    6
    {
    533
    6
      assert(n >= 1);
    534
    6
      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
    6
      model_chunk->super.isa = &ccv_cnnp_chunk_isa;
    536
    6
      model_chunk->super.input_size = 1;
    537
    6
      model_chunk->super.outputs = model_chunk->outputs;
    538
    6
      model_chunk->super.output_size = n;
    539
    6
      model_chunk->axis = axis;
    540
    6
      ccv_cnnp_model_copy_name(&model_chunk->super, name);
    541
    6
      return (ccv_cnnp_model_t*)model_chunk;
    542
    6
    }
    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
    22
    {
    875
    22
      assert(output_size == 1);
    876
    22
      ccv_cnnp_model_extract_t* const self = (ccv_cnnp_model_extract_t*)super;
    877
    22
      PRINT(CCV_CLI_VERBOSE, "[cnnp_extract_build] index: %d\n", self->index);
    878
    22
      outputs[0] = inputs[self->index];
    879
    22
    }
    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
    22
    {
    890
    22
      ccv_cnnp_model_extract_t* const model_extract = (ccv_cnnp_model_extract_t*)cccalloc(1, sizeof(ccv_cnnp_model_extract_t));
    891
    22
      model_extract->index = index;
    892
    22
      model_extract->super.isa = &ccv_cnnp_extract_isa;
    893
    22
      model_extract->super.input_size = 0;
    894
    22
      model_extract->super.outputs = &model_extract->output;
    895
    22
      model_extract->super.output_size = 1;
    896
    22
      ccv_cnnp_model_copy_name(&model_extract->super, name);
    897
    22
      return (ccv_cnnp_model_t*)model_extract;
    898
    22
    }
    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 scale = ccv_cnnp_model_get_symbol(super, self->scale);
    984
    75
      const ccv_nnc_tensor_symbol_t bias = ccv_cnnp_model_get_symbol(super, self->bias);
    985
    75
      const ccv_nnc_tensor_symbol_t mean = ccv_nnc_tensor_symbol_new(graph, bias_params, "mean");
    986
    75
      const ccv_nnc_tensor_symbol_t var = ccv_nnc_tensor_symbol_new(graph, bias_params, "var");
    987
      // Otherwise, notice mean, var, saved_mean, saved_inv_std are not reused.
    988
    75
      if (!self->zero_inits)
    989
    75
        self->zero_inits = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_t), 0, 0);
    990
    75
      ccv_array_push(self->zero_inits, &mean);
    991
    75
      ccv_array_push(self->zero_inits, &var);
    992
    75
      const ccv_nnc_tensor_symbol_t out_mean = ccv_nnc_tensor_symbol_new(graph, bias_params, "out_mean");
    993
    75
      const ccv_nnc_tensor_symbol_t out_var = ccv_nnc_tensor_symbol_new(graph, bias_params, "out_var");
    994
    75
      if (!self->retainables)
    995
    75
        self->retainables = ccv_array_new(sizeof(ccv_nnc_tensor_symbol_t), 0, 0);
    996
    75
      ccv_array_push(self->retainables, &out_mean);
    997
    75
      ccv_array_push(self->retainables, &out_var);
    998
    75
      const ccv_nnc_tensor_symbol_t saved_mean = ccv_nnc_tensor_symbol_new(graph, bias_params, "saved_mean");
    999
    75
      const ccv_nnc_tensor_symbol_t saved_inv_std = ccv_nnc_tensor_symbol_new(graph, bias_params, "saved_inv_std");
    1000
    75
      const int hw = ccv_nnc_tensor_hw(params, ccv_nnc_tensor_nd(params.dim), CCV_NNC_MAX_DIM);
    1001
    75
      ccv_nnc_cmd_param_t batch_norm = self->params;
    1002
    75
      batch_norm.bnorm.count = hw >= 0 ? CCV_NNC_MAX_DIM + 1 : 10;
    1003
    75
      int i;
    1004
    75
      batch_norm.bnorm.axis[0] = (params.format == CCV_TENSOR_FORMAT_CHWN) ? 30 : 0;
    1005
    75
      if (hw >= 0)
    1006
    225
        for (i = 0; 75i < CCV_NNC_MAX_DIM; i++150)
    1007
    150
          batch_norm.bnorm.axis[i + 1] = i + hw;
    1008
    75
      self->params = batch_norm;
    1009
    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], scale, bias, mean, var), TENSOR_SYMBOL_LIST(output, out_mean, out_var, saved_mean, saved_inv_std), "batch_norm");
    1010
    75
      outputs[0] = output;
    1011
    75
    }
    1012
    1013
    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)
    1014
    24
    {
    1015
    24
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1016
    24
      if (self->scale.graph)
    1017
    24
        initializer(context, CMD_RANDOM_UNIFORM_FORWARD(0, 1), ccv_nnc_no_hint, 0, 0, self->scale);
    1018
    24
      if (self->bias.graph)
    1019
    24
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    1020
    24
      int i;
    1021
    24
      if (self->zero_inits)
    1022
    72
        for (i = 0; 24i < self->zero_inits->rnum; i++48)
    1023
    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));
    1024
    24
    }
    1025
    1026
    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)
    1027
    75
    {
    1028
    75
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1029
    75
      if (self->scale.graph)
    1030
    75
        add_to_array(parameters, self->scale, is_trainable);
    1031
    75
      if (self->bias.graph)
    1032
    75
        add_to_array(parameters, self->bias, is_trainable);
    1033
    75
    }
    1034
    1035
    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)
    1036
    75
    {
    1037
    75
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1038
    75
      int i;
    1039
    75
      if (self->retainables)
    1040
    225
        for (i = 0; 75i < self->retainables->rnum; i++150)
    1041
    150
        {
    1042
    150
          const ccv_nnc_tensor_symbol_t symbol = *(ccv_nnc_tensor_symbol_t*)ccv_array_get(self->retainables, i);
    1043
    150
          add_to_array(outputs, symbol, 0);
    1044
    150
        }
    1045
    75
    }
    1046
    1047
    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)
    1048
    32
    {
    1049
    32
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1050
    32
      if (self->batch_norm.graph)
    1051
    32
      {
    1052
    32
        self->params.bnorm.is_test = is_test;
    1053
    32
        updater(context, self->batch_norm, ccv_nnc_cmd(CCV_NNC_BATCH_NORM_FORWARD, 0, self->params, 0), ccv_nnc_no_hint);
    1054
    32
      }
    1055
    32
    }
    1056
    1057
    static void _ccv_cnnp_batch_norm_deinit(ccv_cnnp_model_t* const super)
    1058
    83
    {
    1059
    83
      ccv_cnnp_model_batch_norm_t* const self = (ccv_cnnp_model_batch_norm_t*)super;
    1060
    83
      if (self->zero_inits)
    1061
    75
        ccv_array_free(self->zero_inits);
    1062
    83
      if (self->retainables)
    1063
    75
        ccv_array_free(self->retainables);
    1064
    83
    }
    1065
    1066
    static ccv_cnnp_model_t* _ccv_cnnp_batch_norm_copy(const ccv_cnnp_model_t* const super, void* const context);
    1067
    1068
    static const ccv_cnnp_model_vtab_t ccv_cnnp_batch_norm_isa = {
    1069
      .build = _ccv_cnnp_batch_norm_build,
    1070
      .init_states = _ccv_cnnp_batch_norm_init_states,
    1071
      .add_to_parameter = _ccv_cnnp_batch_norm_add_to_parameter,
    1072
      .add_to_output = _ccv_cnnp_batch_norm_add_to_output,
    1073
      .copy = _ccv_cnnp_batch_norm_copy,
    1074
      .set_is_test = _ccv_cnnp_batch_norm_set_is_test,
    1075
      .deinit = _ccv_cnnp_batch_norm_deinit,
    1076
    };
    1077
    1078
    ccv_cnnp_model_t* ccv_cnnp_batch_norm(const float momentum, const float epsilon, const int is_trainable, const char* const name)
    1079
    83
    {
    1080
    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));
    1081
    83
      model_batch_norm->super.isa = &ccv_cnnp_batch_norm_isa;
    1082
    83
      model_batch_norm->super.input_size = 1;
    1083
    83
      model_batch_norm->super.outputs = &model_batch_norm->output;
    1084
    83
      model_batch_norm->super.output_size = 1;
    1085
    83
      model_batch_norm->super.is_trainable = is_trainable;
    1086
    83
      ccv_cnnp_model_copy_name(&model_batch_norm->super, name);
    1087
    83
      model_batch_norm->scale.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1088
    83
      model_batch_norm->scale.graph = 0;
    1089
    83
      model_batch_norm->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1090
    83
      model_batch_norm->bias.graph = 0;
    1091
    83
      model_batch_norm->params.bnorm.momentum = momentum;
    1092
    83
      model_batch_norm->params.bnorm.epsilon = epsilon;
    1093
    83
      return (ccv_cnnp_model_t*)model_batch_norm;
    1094
    83
    }
    1095
    1096
    static ccv_cnnp_model_t* _ccv_cnnp_batch_norm_copy(const ccv_cnnp_model_t* const super, void* const context)
    1097
    8
    {
    1098
    8
      const ccv_cnnp_model_batch_norm_t* const self = (const ccv_cnnp_model_batch_norm_t*)super;
    1099
    8
      return ccv_cnnp_batch_norm(self->params.bnorm.momentum, self->params.bnorm.epsilon, self->super.is_trainable, self->super.name);
    1100
    8
    }
    1101
    1102
    // MARK - Convolution Layer
    1103
    1104
    typedef struct {
    1105
      ccv_cnnp_model_t super;
    1106
      ccv_nnc_tensor_symbol_t output;
    1107
      ccv_nnc_tensor_symbol_t weights;
    1108
      ccv_nnc_tensor_symbol_t bias;
    1109
      int groups;
    1110
      int filters;
    1111
      int kdim[CCV_NNC_MAX_DIM_ALLOC];
    1112
      int dilation[CCV_NNC_MAX_DIM_ALLOC];
    1113
      int no_bias;
    1114
      int format;
    1115
      ccv_nnc_hint_t hint;
    1116
    } ccv_cnnp_model_convolution_t;
    1117
    1118
    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)
    1119
    114
    {
    1120
    114
      ccv_cnnp_model_convolution_t* const self = (ccv_cnnp_model_convolution_t*)super;
    1121
    114
      PRINT(CCV_CLI_VERBOSE, "[cnnp_convolution_build] -\n");
    1122
    114
      assert(input_size == 1);
    1123
    114
      assert(output_size == 1);
    1124
    114
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1125
    114
      int i;
    1126
    114
      const int k_nd = ccv_nnc_tensor_nd(self->kdim);
    1127
    114
      const int nd = k_nd + 2;
    1128
    114
      ccv_nnc_tensor_param_t weights_params = params;
    1129
    114
      if (self->format)
    1130
    0
        weights_params.format = self->format;
    1131
    114
      ccv_nnc_tensor_set_n(&weights_params, self->filters);
    1132
    114
      const int a_nd = ccv_nnc_tensor_nd(params.dim);
    1133
    114
      int c;
    1134
    114
      switch (params.format)
    1135
    114
      {
    1136
    15
        case CCV_TENSOR_FORMAT_NHWC:
    1137
    15
          c = params.dim[a_nd - 1];
    1138
    15
          break;
    1139
    99
        case CCV_TENSOR_FORMAT_NCHW:
    1140
    99
          if (a_nd == k_nd + 1)
    1141
    0
            c = params.dim[0];
    1142
    99
          else
    1143
    99
            c = params.dim[a_nd <= 1 ? 00 : 1];
    1144
    99
          break;
    1145
    0
        case CCV_TENSOR_FORMAT_CHWN:
    1146
    0
          c = params.dim[0];
    1147
    0
          break;
    1148
    114
      }
    1149
    114
      assert(c % self->groups == 0);
    1150
    114
      ccv_nnc_tensor_set_c(&weights_params, nd, c / self->groups);
    1151
    114
      int hw = -1;
    1152
    114
      if (weights_params.format == CCV_TENSOR_FORMAT_NHWC || weights_params.format == CCV_TENSOR_FORMAT_CHWN99)
    1153
    15
        hw = 1;
    1154
    99
      else if (weights_params.format == CCV_TENSOR_FORMAT_NCHW)
    1155
    99
        hw = 2;
    1156
    114
      assert(hw >= 0);
    1157
    342
      for (i = 0; 114i < k_nd; i++228)
    1158
    228
        weights_params.dim[i + hw] = self->kdim[i];
    1159
    114
      if (!self->weights.graph)
    1160
    110
        self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    1161
    114
      assert(self->weights.graph == graph);
    1162
    114
      const ccv_nnc_tensor_symbol_t weights = ccv_cnnp_model_get_symbol(super, self->weights);
    1163
    114
      ccv_nnc_tensor_param_t bias_params = params;
    1164
    114
      if (self->format)
    1165
    0
        bias_params.format = self->format;
    1166
    114
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    1167
    114
      bias_params.dim[0] = self->filters;
    1168
    114
      ccv_nnc_cmd_t cmd = CMD_CONVOLUTION_FORWARD(self->groups, self->filters);
    1169
    342
      for (i = 0; i < k_nd; i++228)
    1170
    228
        cmd.info.size.dim[i] = self->kdim[i];
    1171
    114
      cmd.info.size.dim[k_nd] = c;
    1172
    114
      memcpy(cmd.info.convolution.dilation, self->dilation, sizeof(self->dilation));
    1173
    114
      ccv_nnc_tensor_param_t output_params;
    1174
      // Dilate weight size based on the dilation factor.
    1175
    342
      for (i = 0; i < k_nd; i++228)
    1176
    228
        weights_params.dim[i + hw] = (self->kdim[i] - 1) * ccv_max(self->dilation[i], 1) + 1;
    1177
    114
      ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    1178
    114
          params,
    1179
    114
          weights_params,
    1180
    114
          bias_params,
    1181
    114
        }, 3, self->hint, &output_params, 1);
    1182
    114
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1183
    114
      ccv_nnc_graph_exec_symbol_t convolution;
    1184
    114
      if (self->no_bias)
    1185
    10
        convolution = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], weights), TENSOR_SYMBOL_LIST(output), "convolution");
    1186
    104
      else {
    1187
    104
        if (!self->bias.graph)
    1188
    100
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    1189
    104
        const ccv_nnc_tensor_symbol_t bias = ccv_cnnp_model_get_symbol(super, self->bias);
    1190
    104
        convolution = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], weights, bias), TENSOR_SYMBOL_LIST(output), "convolution");
    1191
    104
      }
    1192
    114
      ccv_nnc_graph_exec_symbol_set_hint(graph, convolution, self->hint);
    1193
    114
      outputs[0] = output;
    1194
    114
    }
    1195
    1196
    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)
    1197
    36
    {
    1198
    36
      ccv_cnnp_model_convolution_t* const self = (ccv_cnnp_model_convolution_t*)super;
    1199
    36
      const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    1200
    36
      const int n = ccv_max(ccv_nnc_tensor_get_n(weight_params), 1);
    1201
    36
      const int count = ccv_nnc_tensor_count(weight_params);
    1202
    36
      const float std = sqrtf(2) / sqrtf(count / n);
    1203
    36
      const float bound = sqrtf(3) * std;
    1204
    36
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    1205
    36
      if (self->bias.graph)
    1206
    36
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    1207
    36
    }
    1208
    1209
    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)
    1210
    114
    {
    1211
    114
      ccv_cnnp_model_convolution_t* const self = (ccv_cnnp_model_convolution_t*)super;
    1212
    114
      add_to_array(parameters, self->weights, is_trainable);
    1213
    114
      if (self->bias.graph)
    1214
    104
        add_to_array(parameters, self->bias, is_trainable);
    1215
    114
    }
    1216
    1217
    static ccv_cnnp_model_t* _ccv_cnnp_convolution_copy(const ccv_cnnp_model_t* const super, void* const context);
    1218
    1219
    static const ccv_cnnp_model_vtab_t ccv_cnnp_convolution_isa = {
    1220
      .build = _ccv_cnnp_convolution_build,
    1221
      .init_states = _ccv_cnnp_convolution_init_states,
    1222
      .add_to_parameter = _ccv_cnnp_convolution_add_to_parameter,
    1223
      .copy = _ccv_cnnp_convolution_copy,
    1224
    };
    1225
    1226
    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)
    1227
    126
    {
    1228
    126
      ccv_cnnp_model_convolution_t* const model_convolution = (ccv_cnnp_model_convolution_t*)cccalloc(1, sizeof(ccv_cnnp_model_convolution_t));
    1229
    126
      model_convolution->super.isa = &ccv_cnnp_convolution_isa;
    1230
    126
      model_convolution->super.input_size = 1;
    1231
    126
      model_convolution->super.outputs = &model_convolution->output;
    1232
    126
      model_convolution->super.output_size = 1;
    1233
    126
      model_convolution->super.is_trainable = is_trainable;
    1234
    126
      ccv_cnnp_model_copy_name(&model_convolution->super, name);
    1235
    126
      model_convolution->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1236
    126
      model_convolution->weights.graph = 0;
    1237
    126
      model_convolution->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1238
    126
      model_convolution->bias.graph = 0;
    1239
    126
      model_convolution->groups = groups;
    1240
    126
      model_convolution->filters = filters;
    1241
    126
      memcpy(model_convolution->kdim, kdim, sizeof(model_convolution->kdim));
    1242
    126
      memcpy(model_convolution->dilation, dilation, sizeof(model_convolution->dilation));
    1243
    126
      model_convolution->no_bias = no_bias;
    1244
    126
      model_convolution->hint = hint;
    1245
    126
      model_convolution->format = format;
    1246
    126
      return (ccv_cnnp_model_t*)model_convolution;
    1247
    126
    }
    1248
    1249
    static ccv_cnnp_model_t* _ccv_cnnp_convolution_copy(const ccv_cnnp_model_t* const super, void* const context)
    1250
    16
    {
    1251
    16
      ccv_cnnp_model_convolution_t* const self = (ccv_cnnp_model_convolution_t*)super;
    1252
    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);
    1253
    16
    }
    1254
    1255
    // MARK - Convolution Transpose Layer
    1256
    1257
    typedef struct {
    1258
      ccv_cnnp_model_t super;
    1259
      ccv_nnc_tensor_symbol_t output;
    1260
      ccv_nnc_tensor_symbol_t weights;
    1261
      ccv_nnc_tensor_symbol_t bias;
    1262
      int groups;
    1263
      int filters;
    1264
      int kdim[CCV_NNC_MAX_DIM_ALLOC];
    1265
      int dilation[CCV_NNC_MAX_DIM_ALLOC];
    1266
      int output_padding;
    1267
      int no_bias;
    1268
      int format;
    1269
      ccv_nnc_hint_t hint;
    1270
    } ccv_cnnp_model_convolution_transpose_t;
    1271
    1272
    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)
    1273
    0
    {
    1274
    0
      ccv_cnnp_model_convolution_transpose_t* const self = (ccv_cnnp_model_convolution_transpose_t*)super;
    1275
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_convolution_transpose_build] -\n");
    1276
    0
      assert(input_size == 1);
    1277
    0
      assert(output_size == 1);
    1278
    0
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1279
    0
      int i;
    1280
    0
      const int nd = CCV_NNC_MAX_DIM + 2;
    1281
    0
      ccv_nnc_tensor_param_t weights_params = params;
    1282
    0
      if (self->format)
    1283
    0
        weights_params.format = self->format;
    1284
    0
      const int c = ccv_nnc_tensor_get_c(params);
    1285
    0
      ccv_nnc_tensor_set_n(&weights_params, c);
    1286
    0
      assert(c % self->groups == 0);
    1287
    0
      ccv_nnc_tensor_set_c(&weights_params, nd, self->filters / self->groups);
    1288
    0
      const int hw = ccv_nnc_tensor_hw(weights_params, nd, CCV_NNC_MAX_DIM);
    1289
    0
      assert(hw >= 0);
    1290
    0
      for (i = 0; i < CCV_NNC_MAX_DIM; i++)
    1291
    0
        weights_params.dim[i + hw] = self->kdim[i];
    1292
    0
      if (!self->weights.graph)
    1293
    0
        self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    1294
    0
      assert(self->weights.graph == graph);
    1295
    0
      const ccv_nnc_tensor_symbol_t weights = ccv_cnnp_model_get_symbol(super, self->weights);
    1296
    0
      ccv_nnc_tensor_param_t bias_params = params;
    1297
    0
      if (self->format)
    1298
    0
        bias_params.format = self->format;
    1299
    0
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    1300
    0
      bias_params.dim[0] = self->filters;
    1301
    0
      ccv_nnc_cmd_t cmd = CMD_CONVOLUTION_TRANSPOSE_FORWARD(self->groups, self->filters, self->output_padding);
    1302
    0
      for (i = 0; i < CCV_NNC_MAX_DIM; i++)
    1303
    0
        cmd.info.size.dim[i] = self->kdim[i];
    1304
    0
      cmd.info.size.dim[CCV_NNC_MAX_DIM] = c;
    1305
    0
      memcpy(cmd.info.convolution_transpose.dilation, self->dilation, sizeof(self->dilation));
    1306
    0
      ccv_nnc_tensor_param_t output_params;
    1307
      // Dilate weight size based on the dilation factor.
    1308
    0
      for (i = 0; i < CCV_NNC_MAX_DIM; i++)
    1309
    0
        weights_params.dim[i + hw] = (self->kdim[i] - 1) * ccv_max(self->dilation[i], 1) + 1;
    1310
    0
      ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    1311
    0
          params,
    1312
    0
          weights_params,
    1313
    0
          bias_params,
    1314
    0
        }, 3, self->hint, &output_params, 1);
    1315
    0
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1316
    0
      ccv_nnc_graph_exec_symbol_t convolution_transpose;
    1317
    0
      if (self->no_bias)
    1318
    0
        convolution_transpose = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], weights), TENSOR_SYMBOL_LIST(output), "convolution_transpose");
    1319
    0
      else {
    1320
    0
        if (!self->bias.graph)
    1321
    0
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    1322
    0
        const ccv_nnc_tensor_symbol_t bias = ccv_cnnp_model_get_symbol(super, self->bias);
    1323
    0
        convolution_transpose = ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], weights, bias), TENSOR_SYMBOL_LIST(output), "convolution_transpose");
    1324
    0
      }
    1325
    0
      ccv_nnc_graph_exec_symbol_set_hint(graph, convolution_transpose, self->hint);
    1326
    0
      outputs[0] = output;
    1327
    0
    }
    1328
    1329
    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)
    1330
    0
    {
    1331
    0
      ccv_cnnp_model_convolution_transpose_t* const self = (ccv_cnnp_model_convolution_transpose_t*)super;
    1332
    0
      const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    1333
    0
      const int n = ccv_max(ccv_nnc_tensor_get_n(weight_params), 1);
    1334
    0
      const int count = ccv_nnc_tensor_count(weight_params);
    1335
    0
      const float std = sqrtf(2) / sqrtf(count / n);
    1336
    0
      const float bound = sqrtf(3) * std;
    1337
    0
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    1338
    0
      if (self->bias.graph)
    1339
    0
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    1340
    0
    }
    1341
    1342
    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)
    1343
    0
    {
    1344
    0
      ccv_cnnp_model_convolution_transpose_t* const self = (ccv_cnnp_model_convolution_transpose_t*)super;
    1345
    0
      add_to_array(parameters, self->weights, is_trainable);
    1346
    0
      if (self->bias.graph)
    1347
    0
        add_to_array(parameters, self->bias, is_trainable);
    1348
    0
    }
    1349
    1350
    static ccv_cnnp_model_t* _ccv_cnnp_convolution_transpose_copy(const ccv_cnnp_model_t* const super, void* const context);
    1351
    1352
    static const ccv_cnnp_model_vtab_t ccv_cnnp_convolution_transpose_isa = {
    1353
      .build = _ccv_cnnp_convolution_transpose_build,
    1354
      .init_states = _ccv_cnnp_convolution_transpose_init_states,
    1355
      .add_to_parameter = _ccv_cnnp_convolution_transpose_add_to_parameter,
    1356
      .copy = _ccv_cnnp_convolution_transpose_copy,
    1357
    };
    1358
    1359
    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)
    1360
    0
    {
    1361
    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));
    1362
    0
      model_convolution_transpose->super.isa = &ccv_cnnp_convolution_transpose_isa;
    1363
    0
      model_convolution_transpose->super.input_size = 1;
    1364
    0
      model_convolution_transpose->super.outputs = &model_convolution_transpose->output;
    1365
    0
      model_convolution_transpose->super.output_size = 1;
    1366
    0
      model_convolution_transpose->super.is_trainable = is_trainable;
    1367
    0
      ccv_cnnp_model_copy_name(&model_convolution_transpose->super, name);
    1368
    0
      model_convolution_transpose->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1369
    0
      model_convolution_transpose->weights.graph = 0;
    1370
    0
      model_convolution_transpose->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1371
    0
      model_convolution_transpose->bias.graph = 0;
    1372
    0
      model_convolution_transpose->groups = groups;
    1373
    0
      model_convolution_transpose->filters = filters;
    1374
    0
      memcpy(model_convolution_transpose->kdim, kdim, sizeof(model_convolution_transpose->kdim));
    1375
    0
      memcpy(model_convolution_transpose->dilation, dilation, sizeof(model_convolution_transpose->dilation));
    1376
    0
      model_convolution_transpose->output_padding = output_padding;
    1377
    0
      model_convolution_transpose->no_bias = no_bias;
    1378
    0
      model_convolution_transpose->hint = hint;
    1379
    0
      model_convolution_transpose->format = format;
    1380
    0
      return (ccv_cnnp_model_t*)model_convolution_transpose;
    1381
    0
    }
    1382
    1383
    static ccv_cnnp_model_t* _ccv_cnnp_convolution_transpose_copy(const ccv_cnnp_model_t* const super, void* const context)
    1384
    0
    {
    1385
    0
      ccv_cnnp_model_convolution_transpose_t* const self = (ccv_cnnp_model_convolution_transpose_t*)super;
    1386
    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);
    1387
    0
    }
    1388
    1389
    // MARK - Dense Layer
    1390
    1391
    typedef struct {
    1392
      ccv_cnnp_model_t super;
    1393
      ccv_nnc_tensor_symbol_t output;
    1394
      ccv_nnc_tensor_symbol_t weights;
    1395
      ccv_nnc_tensor_symbol_t bias;
    1396
      int count;
    1397
      int no_bias;
    1398
      int flags;
    1399
    } ccv_cnnp_model_dense_t;
    1400
    1401
    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)
    1402
    2.33k
    {
    1403
    2.33k
      ccv_cnnp_model_dense_t* const self = (ccv_cnnp_model_dense_t*)super;
    1404
    2.33k
      PRINT(CCV_CLI_VERBOSE, "[cnnp_dense_build] -\n");
    1405
    2.33k
      assert(input_size == 1);
    1406
    2.33k
      assert(output_size == 1);
    1407
    2.33k
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1408
    2.33k
      ccv_nnc_tensor_param_t weights_params = params;
    1409
    2.33k
      memset(weights_params.dim, 0, sizeof(weights_params.dim));
    1410
    2.33k
      weights_params.dim[0] = self->count;
    1411
    2.33k
      weights_params.dim[1] = params.dim[ccv_nnc_tensor_nd(params.dim) - 1];
    1412
    2.33k
      if (!self->weights.graph)
    1413
    2.31k
        self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    1414
    2.33k
      assert(self->weights.graph == graph);
    1415
    2.33k
      const ccv_nnc_tensor_symbol_t weights = ccv_cnnp_model_get_symbol(super, self->weights);
    1416
    2.33k
      ccv_nnc_tensor_param_t bias_params = params;
    1417
    2.33k
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    1418
    2.33k
      bias_params.dim[0] = self->count;
    1419
    2.33k
      ccv_nnc_cmd_t cmd = {0};
    1420
    2.33k
      cmd.cmd = CCV_NNC_GEMM_FORWARD;
    1421
    2.33k
      cmd.info.blas.a[0] = 1;
    1422
    2.33k
      cmd.info.blas.a[1] = 1;
    1423
    2.33k
      cmd.info.blas.transpose_b[0] = 0;
    1424
    2.33k
      cmd.info.blas.transpose_b[1] = 1;
    1425
    2.33k
      cmd.info.blas.flags = self->flags;
    1426
    2.33k
      ccv_nnc_tensor_param_t output_params;
    1427
    2.33k
      ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    1428
    2.33k
          params,
    1429
    2.33k
          weights_params,
    1430
    2.33k
          bias_params,
    1431
    2.33k
        }, 3, ccv_nnc_no_hint, &output_params, 1);
    1432
    2.33k
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1433
    2.33k
      if (self->no_bias)
    1434
    2.08k
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], weights), TENSOR_SYMBOL_LIST(output), "dense");
    1435
    246
      else {
    1436
    246
        if (!self->bias.graph)
    1437
    243
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    1438
    246
        const ccv_nnc_tensor_symbol_t bias = ccv_cnnp_model_get_symbol(super, self->bias);
    1439
    246
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], weights, bias), TENSOR_SYMBOL_LIST(output), "dense");
    1440
    246
      }
    1441
    2.33k
      outputs[0] = output;
    1442
    2.33k
    }
    1443
    1444
    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)
    1445
    79
    {
    1446
    79
      ccv_cnnp_model_dense_t* const self = (ccv_cnnp_model_dense_t*)super;
    1447
    79
      const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    1448
    79
      const int c = weight_params.dim[1];
    1449
    79
      const float std = sqrtf(2) / sqrtf(c);
    1450
    79
      const float bound = sqrtf(3) * std;
    1451
    79
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    1452
    79
      if (self->bias.graph)
    1453
    33
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    1454
    79
    }
    1455
    1456
    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)
    1457
    2.33k
    {
    1458
    2.33k
      ccv_cnnp_model_dense_t* const self = (ccv_cnnp_model_dense_t*)super;
    1459
    2.33k
      add_to_array(parameters, self->weights, is_trainable);
    1460
    2.33k
      if (self->bias.graph)
    1461
    246
        add_to_array(parameters, self->bias, is_trainable);
    1462
    2.33k
    }
    1463
    1464
    static ccv_cnnp_model_t* _ccv_cnnp_dense_copy(const ccv_cnnp_model_t* const super, void* const context);
    1465
    1466
    static const ccv_cnnp_model_vtab_t ccv_cnnp_dense_isa = {
    1467
      .build = _ccv_cnnp_dense_build,
    1468
      .init_states = _ccv_cnnp_dense_init_states,
    1469
      .add_to_parameter = _ccv_cnnp_dense_add_to_parameter,
    1470
      .copy = _ccv_cnnp_dense_copy,
    1471
    };
    1472
    1473
    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)
    1474
    2.31k
    {
    1475
    2.31k
      ccv_cnnp_model_dense_t* const model_dense = (ccv_cnnp_model_dense_t*)cccalloc(1, sizeof(ccv_cnnp_model_dense_t));
    1476
    2.31k
      model_dense->super.isa = &ccv_cnnp_dense_isa;
    1477
    2.31k
      model_dense->super.input_size = 1;
    1478
    2.31k
      model_dense->super.outputs = &model_dense->output;
    1479
    2.31k
      model_dense->super.output_size = 1;
    1480
    2.31k
      model_dense->super.is_trainable = is_trainable;
    1481
    2.31k
      ccv_cnnp_model_copy_name(&model_dense->super, name);
    1482
    2.31k
      model_dense->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1483
    2.31k
      model_dense->weights.graph = 0;
    1484
    2.31k
      model_dense->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    1485
    2.31k
      model_dense->bias.graph = 0;
    1486
    2.31k
      model_dense->count = count;
    1487
    2.31k
      model_dense->no_bias = no_bias;
    1488
    2.31k
      model_dense->flags = flags;
    1489
    2.31k
      return (ccv_cnnp_model_t*)model_dense;
    1490
    2.31k
    }
    1491
    1492
    static ccv_cnnp_model_t* _ccv_cnnp_dense_copy(const ccv_cnnp_model_t* const super, void* const context)
    1493
    2.20k
    {
    1494
    2.20k
      const ccv_cnnp_model_dense_t* const self = (const ccv_cnnp_model_dense_t*)super;
    1495
    2.20k
      return ccv_cnnp_dense(self->count, self->no_bias, self->flags, self->super.is_trainable, self->super.name);
    1496
    2.20k
    }
    1497
    1498
    // MARK - Pool Layers
    1499
    1500
    typedef struct {
    1501
      ccv_cnnp_model_t super;
    1502
      ccv_nnc_tensor_symbol_t output;
    1503
      int kdim[CCV_NNC_MAX_DIM_ALLOC];
    1504
      ccv_nnc_hint_t hint;
    1505
    } ccv_cnnp_model_pool_t;
    1506
    1507
    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)
    1508
    18
    {
    1509
    18
      ccv_cnnp_model_pool_t* const self = (ccv_cnnp_model_pool_t*)super;
    1510
    18
      PRINT(CCV_CLI_VERBOSE, "[cnnp_max_pool_build] -\n");
    1511
    18
      assert(input_size == 1);
    1512
    18
      assert(output_size == 1);
    1513
    18
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1514
    18
      const int hw = ccv_nnc_tensor_hw(params, ccv_nnc_tensor_nd(params.dim), CCV_NNC_MAX_DIM);
    1515
    18
      ccv_nnc_cmd_t cmd;
    1516
    18
      if (hw >= 0 && self->kdim[0] == 0 && self->kdim[1] == 03)
    1517
    3
        cmd = CMD_MAX_POOL_FORWARD(params.dim[hw], params.dim[hw + 1]);
    1518
    15
      else
    1519
    15
        cmd = CMD_MAX_POOL_FORWARD(self->kdim[0], self->kdim[1]);
    1520
    18
      ccv_nnc_tensor_param_t output_params;
    1521
    18
      ccv_nnc_hint_tensor_auto(cmd, &params, 1, self->hint, &output_params, 1);
    1522
    18
      const ccv_nnc_tensor_symbol_t pool_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1523
    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");
    1524
    18
      ccv_nnc_graph_exec_symbol_set_hint(graph, exec, self->hint);
    1525
    18
      outputs[0] = pool_output;
    1526
    18
    }
    1527
    1528
    static ccv_cnnp_model_t* _ccv_cnnp_max_pool_copy(const ccv_cnnp_model_t* const super, void* const context);
    1529
    1530
    static const ccv_cnnp_model_vtab_t ccv_cnnp_max_pool_isa = {
    1531
      .build = _ccv_cnnp_max_pool_build,
    1532
      .copy = _ccv_cnnp_max_pool_copy,
    1533
    };
    1534
    1535
    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)
    1536
    24
    {
    1537
    24
      ccv_cnnp_model_pool_t* const model_pool = (ccv_cnnp_model_pool_t*)cccalloc(1, sizeof(ccv_cnnp_model_pool_t));
    1538
    24
      model_pool->super.isa = &ccv_cnnp_max_pool_isa;
    1539
    24
      model_pool->super.input_size = 1;
    1540
    24
      model_pool->super.outputs = &model_pool->output;
    1541
    24
      model_pool->super.output_size = 1;
    1542
    24
      ccv_cnnp_model_copy_name(&model_pool->super, name);
    1543
    24
      memcpy(model_pool->kdim, kdim, sizeof(model_pool->kdim));
    1544
    24
      model_pool->hint = hint;
    1545
    24
      return (ccv_cnnp_model_t*)model_pool;
    1546
    24
    }
    1547
    1548
    static ccv_cnnp_model_t* _ccv_cnnp_max_pool_copy(const ccv_cnnp_model_t* const super, void* const context)
    1549
    6
    {
    1550
    6
      const ccv_cnnp_model_pool_t* const self = (const ccv_cnnp_model_pool_t*)super;
    1551
    6
      return ccv_cnnp_max_pool(self->kdim, self->hint, self->super.name);
    1552
    6
    }
    1553
    1554
    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)
    1555
    15
    {
    1556
    15
      ccv_cnnp_model_pool_t* const self = (ccv_cnnp_model_pool_t*)super;
    1557
    15
      PRINT(CCV_CLI_VERBOSE, "[cnnp_average_pool_build] -\n");
    1558
    15
      assert(input_size == 1);
    1559
    15
      assert(output_size == 1);
    1560
    15
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1561
    15
      const int hw = ccv_nnc_tensor_hw(params, ccv_nnc_tensor_nd(params.dim), CCV_NNC_MAX_DIM);
    1562
    15
      ccv_nnc_cmd_t cmd;
    1563
    15
      if (hw >= 0 && self->kdim[0] == 0 && self->kdim[1] == 02)
    1564
    2
        cmd = CMD_AVERAGE_POOL_FORWARD(params.dim[hw], params.dim[hw + 1]);
    1565
    13
      else
    1566
    13
        cmd = CMD_AVERAGE_POOL_FORWARD(self->kdim[0], self->kdim[1]);
    1567
    15
      ccv_nnc_tensor_param_t output_params;
    1568
    15
      ccv_nnc_hint_tensor_auto(cmd, &params, 1, self->hint, &output_params, 1);
    1569
    15
      const ccv_nnc_tensor_symbol_t pool_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1570
    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");
    1571
    15
      ccv_nnc_graph_exec_symbol_set_hint(graph, exec, self->hint);
    1572
    15
      outputs[0] = pool_output;
    1573
    15
    }
    1574
    1575
    static ccv_cnnp_model_t* _ccv_cnnp_average_pool_copy(const ccv_cnnp_model_t* const super, void* const context);
    1576
    1577
    static const ccv_cnnp_model_vtab_t ccv_cnnp_average_pool_isa = {
    1578
      .build = _ccv_cnnp_average_pool_build,
    1579
      .copy = _ccv_cnnp_average_pool_copy,
    1580
    };
    1581
    1582
    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)
    1583
    17
    {
    1584
    17
      ccv_cnnp_model_pool_t* const model_pool = (ccv_cnnp_model_pool_t*)cccalloc(1, sizeof(ccv_cnnp_model_pool_t));
    1585
    17
      model_pool->super.isa = &ccv_cnnp_average_pool_isa;
    1586
    17
      model_pool->super.input_size = 1;
    1587
    17
      model_pool->super.outputs = &model_pool->output;
    1588
    17
      model_pool->super.output_size = 1;
    1589
    17
      ccv_cnnp_model_copy_name(&model_pool->super, name);
    1590
    17
      memcpy(model_pool->kdim, kdim, sizeof(model_pool->kdim));
    1591
    17
      model_pool->hint = hint;
    1592
    17
      return (ccv_cnnp_model_t*)model_pool;
    1593
    17
    }
    1594
    1595
    static ccv_cnnp_model_t* _ccv_cnnp_average_pool_copy(const ccv_cnnp_model_t* const super, void* const context)
    1596
    2
    {
    1597
    2
      const ccv_cnnp_model_pool_t* const self = (const ccv_cnnp_model_pool_t*)super;
    1598
    2
      return ccv_cnnp_average_pool(self->kdim, self->hint, self->super.name);
    1599
    2
    }
    1600
    1601
    // MARK - RELU Layer
    1602
    1603
    typedef struct {
    1604
      ccv_cnnp_model_t super;
    1605
      ccv_nnc_tensor_symbol_t output;
    1606
    } ccv_cnnp_model_relu_t;
    1607
    1608
    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)
    1609
    103
    {
    1610
    103
      PRINT(CCV_CLI_VERBOSE, "[cnnp_relu_build] -\n");
    1611
    103
      assert(input_size == 1);
    1612
    103
      assert(output_size == 1);
    1613
    103
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1614
    103
      ccv_nnc_tensor_param_t output_params;
    1615
    103
      const ccv_nnc_cmd_t relu = CMD_RELU_FORWARD();
    1616
    103
      ccv_nnc_hint_tensor_auto(relu, (ccv_nnc_tensor_param_t []){
    1617
    103
          params,
    1618
    103
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1619
    103
      const ccv_nnc_tensor_symbol_t relu_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1620
    103
      ccv_nnc_graph_exec_symbol_new(graph, relu, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(relu_output), "relu");
    1621
    103
      outputs[0] = relu_output;
    1622
    103
    }
    1623
    1624
    static ccv_cnnp_model_t* _ccv_cnnp_relu_copy(const ccv_cnnp_model_t* const self, void* const context);
    1625
    1626
    static const ccv_cnnp_model_vtab_t ccv_cnnp_relu_isa = {
    1627
      .build = _ccv_cnnp_relu_build,
    1628
      .copy = _ccv_cnnp_relu_copy,
    1629
    };
    1630
    1631
    ccv_cnnp_model_t* ccv_cnnp_relu(const char* const name)
    1632
    120
    {
    1633
    120
      ccv_cnnp_model_relu_t* const model_relu = (ccv_cnnp_model_relu_t*)cccalloc(1, sizeof(ccv_cnnp_model_relu_t));
    1634
    120
      model_relu->super.isa = &ccv_cnnp_relu_isa;
    1635
    120
      model_relu->super.input_size = 1;
    1636
    120
      model_relu->super.outputs = &model_relu->output;
    1637
    120
      model_relu->super.output_size = 1;
    1638
    120
      ccv_cnnp_model_copy_name(&model_relu->super, name);
    1639
    120
      return (ccv_cnnp_model_t*)model_relu;
    1640
    120
    }
    1641
    1642
    static ccv_cnnp_model_t* _ccv_cnnp_relu_copy(const ccv_cnnp_model_t* const self, void* const context)
    1643
    17
    {
    1644
    17
      return ccv_cnnp_relu(self->name);
    1645
    17
    }
    1646
    1647
    // MARK - Sigmoid Layer
    1648
    1649
    typedef struct {
    1650
      ccv_cnnp_model_t super;
    1651
      ccv_nnc_tensor_symbol_t output;
    1652
    } ccv_cnnp_model_sigmoid_t;
    1653
    1654
    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)
    1655
    5
    {
    1656
    5
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sigmoid_build] -\n");
    1657
    5
      assert(input_size == 1);
    1658
    5
      assert(output_size == 1);
    1659
    5
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1660
    5
      ccv_nnc_tensor_param_t output_params;
    1661
    5
      const ccv_nnc_cmd_t sigmoid = CMD_SIGMOID_FORWARD();
    1662
    5
      ccv_nnc_hint_tensor_auto(sigmoid, (ccv_nnc_tensor_param_t []){
    1663
    5
          params,
    1664
    5
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1665
    5
      const ccv_nnc_tensor_symbol_t sigmoid_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1666
    5
      ccv_nnc_graph_exec_symbol_new(graph, sigmoid, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(sigmoid_output), "sigmoid");
    1667
    5
      outputs[0] = sigmoid_output;
    1668
    5
    }
    1669
    1670
    static ccv_cnnp_model_t* _ccv_cnnp_sigmoid_copy(const ccv_cnnp_model_t* const self, void* const context);
    1671
    1672
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sigmoid_isa = {
    1673
      .build = _ccv_cnnp_sigmoid_build,
    1674
      .copy = _ccv_cnnp_sigmoid_copy,
    1675
    };
    1676
    1677
    ccv_cnnp_model_t* ccv_cnnp_sigmoid(const char* const name)
    1678
    5
    {
    1679
    5
      ccv_cnnp_model_sigmoid_t* const model_sigmoid = (ccv_cnnp_model_sigmoid_t*)cccalloc(1, sizeof(ccv_cnnp_model_sigmoid_t));
    1680
    5
      model_sigmoid->super.isa = &ccv_cnnp_sigmoid_isa;
    1681
    5
      model_sigmoid->super.input_size = 1;
    1682
    5
      model_sigmoid->super.outputs = &model_sigmoid->output;
    1683
    5
      model_sigmoid->super.output_size = 1;
    1684
    5
      ccv_cnnp_model_copy_name(&model_sigmoid->super, name);
    1685
    5
      return (ccv_cnnp_model_t*)model_sigmoid;
    1686
    5
    }
    1687
    1688
    static ccv_cnnp_model_t* _ccv_cnnp_sigmoid_copy(const ccv_cnnp_model_t* const self, void* const context)
    1689
    0
    {
    1690
    0
      return ccv_cnnp_sigmoid(self->name);
    1691
    0
    }
    1692
    1693
    // MARK - Tanh Layer
    1694
    1695
    typedef struct {
    1696
      ccv_cnnp_model_t super;
    1697
      ccv_nnc_tensor_symbol_t output;
    1698
    } ccv_cnnp_model_tanh_t;
    1699
    1700
    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)
    1701
    0
    {
    1702
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_tanh_build] -\n");
    1703
    0
      assert(input_size == 1);
    1704
    0
      assert(output_size == 1);
    1705
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1706
    0
      ccv_nnc_tensor_param_t output_params;
    1707
    0
      const ccv_nnc_cmd_t tanh = CMD_TANH_FORWARD();
    1708
    0
      ccv_nnc_hint_tensor_auto(tanh, (ccv_nnc_tensor_param_t []){
    1709
    0
          params,
    1710
    0
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1711
    0
      const ccv_nnc_tensor_symbol_t tanh_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1712
    0
      ccv_nnc_graph_exec_symbol_new(graph, tanh, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(tanh_output), "tanh");
    1713
    0
      outputs[0] = tanh_output;
    1714
    0
    }
    1715
    1716
    static ccv_cnnp_model_t* _ccv_cnnp_tanh_copy(const ccv_cnnp_model_t* const self, void* const context);
    1717
    1718
    static const ccv_cnnp_model_vtab_t ccv_cnnp_tanh_isa = {
    1719
      .build = _ccv_cnnp_tanh_build,
    1720
      .copy = _ccv_cnnp_tanh_copy,
    1721
    };
    1722
    1723
    ccv_cnnp_model_t* ccv_cnnp_tanh(const char* const name)
    1724
    0
    {
    1725
    0
      ccv_cnnp_model_tanh_t* const model_tanh = (ccv_cnnp_model_tanh_t*)cccalloc(1, sizeof(ccv_cnnp_model_tanh_t));
    1726
    0
      model_tanh->super.isa = &ccv_cnnp_tanh_isa;
    1727
    0
      model_tanh->super.input_size = 1;
    1728
    0
      model_tanh->super.outputs = &model_tanh->output;
    1729
    0
      model_tanh->super.output_size = 1;
    1730
    0
      ccv_cnnp_model_copy_name(&model_tanh->super, name);
    1731
    0
      return (ccv_cnnp_model_t*)model_tanh;
    1732
    0
    }
    1733
    1734
    static ccv_cnnp_model_t* _ccv_cnnp_tanh_copy(const ccv_cnnp_model_t* const self, void* const context)
    1735
    0
    {
    1736
    0
      return ccv_cnnp_tanh(self->name);
    1737
    0
    }
    1738
    1739
    // MARK - Exp Layer
    1740
    1741
    typedef struct {
    1742
      ccv_cnnp_model_t super;
    1743
      ccv_nnc_tensor_symbol_t output;
    1744
    } ccv_cnnp_model_exp_t;
    1745
    1746
    static void _ccv_cnnp_exp_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)
    1747
    1
    {
    1748
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_exp_build] -\n");
    1749
    1
      assert(input_size == 1);
    1750
    1
      assert(output_size == 1);
    1751
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1752
    1
      ccv_nnc_tensor_param_t output_params;
    1753
    1
      const ccv_nnc_cmd_t exp = CMD_EWEXP_FORWARD();
    1754
    1
      ccv_nnc_hint_tensor_auto(exp, (ccv_nnc_tensor_param_t []){
    1755
    1
          params,
    1756
    1
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1757
    1
      const ccv_nnc_tensor_symbol_t exp_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1758
    1
      ccv_nnc_graph_exec_symbol_new(graph, exp, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(exp_output), "exp");
    1759
    1
      outputs[0] = exp_output;
    1760
    1
    }
    1761
    1762
    static ccv_cnnp_model_t* _ccv_cnnp_exp_copy(const ccv_cnnp_model_t* const self, void* const context);
    1763
    1764
    static const ccv_cnnp_model_vtab_t ccv_cnnp_exp_isa = {
    1765
      .build = _ccv_cnnp_exp_build,
    1766
      .copy = _ccv_cnnp_exp_copy,
    1767
    };
    1768
    1769
    ccv_cnnp_model_t* ccv_cnnp_exp(const char* const name)
    1770
    1
    {
    1771
    1
      ccv_cnnp_model_exp_t* const model_exp = (ccv_cnnp_model_exp_t*)cccalloc(1, sizeof(ccv_cnnp_model_exp_t));
    1772
    1
      model_exp->super.isa = &ccv_cnnp_exp_isa;
    1773
    1
      model_exp->super.input_size = 1;
    1774
    1
      model_exp->super.outputs = &model_exp->output;
    1775
    1
      model_exp->super.output_size = 1;
    1776
    1
      ccv_cnnp_model_copy_name(&model_exp->super, name);
    1777
    1
      return (ccv_cnnp_model_t*)model_exp;
    1778
    1
    }
    1779
    1780
    static ccv_cnnp_model_t* _ccv_cnnp_exp_copy(const ccv_cnnp_model_t* const self, void* const context)
    1781
    0
    {
    1782
    0
      return ccv_cnnp_exp(self->name);
    1783
    0
    }
    1784
    1785
    // MARK - Softplus Layer
    1786
    1787
    typedef struct {
    1788
      ccv_cnnp_model_t super;
    1789
      ccv_nnc_tensor_symbol_t output;
    1790
    } ccv_cnnp_model_softplus_t;
    1791
    1792
    static void _ccv_cnnp_softplus_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)
    1793
    1
    {
    1794
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_softplus_build] -\n");
    1795
    1
      assert(input_size == 1);
    1796
    1
      assert(output_size == 1);
    1797
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1798
    1
      ccv_nnc_tensor_param_t output_params;
    1799
    1
      const ccv_nnc_cmd_t softplus = CMD_EWSOFTPLUS_FORWARD();
    1800
    1
      ccv_nnc_hint_tensor_auto(softplus, (ccv_nnc_tensor_param_t []){
    1801
    1
          params,
    1802
    1
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1803
    1
      const ccv_nnc_tensor_symbol_t softplus_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1804
    1
      ccv_nnc_graph_exec_symbol_new(graph, softplus, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(softplus_output), "softplus");
    1805
    1
      outputs[0] = softplus_output;
    1806
    1
    }
    1807
    1808
    static ccv_cnnp_model_t* _ccv_cnnp_softplus_copy(const ccv_cnnp_model_t* const self, void* const context);
    1809
    1810
    static const ccv_cnnp_model_vtab_t ccv_cnnp_softplus_isa = {
    1811
      .build = _ccv_cnnp_softplus_build,
    1812
      .copy = _ccv_cnnp_softplus_copy,
    1813
    };
    1814
    1815
    ccv_cnnp_model_t* ccv_cnnp_softplus(const char* const name)
    1816
    1
    {
    1817
    1
      ccv_cnnp_model_softplus_t* const model_softplus = (ccv_cnnp_model_softplus_t*)cccalloc(1, sizeof(ccv_cnnp_model_softplus_t));
    1818
    1
      model_softplus->super.isa = &ccv_cnnp_softplus_isa;
    1819
    1
      model_softplus->super.input_size = 1;
    1820
    1
      model_softplus->super.outputs = &model_softplus->output;
    1821
    1
      model_softplus->super.output_size = 1;
    1822
    1
      ccv_cnnp_model_copy_name(&model_softplus->super, name);
    1823
    1
      return (ccv_cnnp_model_t*)model_softplus;
    1824
    1
    }
    1825
    1826
    static ccv_cnnp_model_t* _ccv_cnnp_softplus_copy(const ccv_cnnp_model_t* const self, void* const context)
    1827
    0
    {
    1828
    0
      return ccv_cnnp_softplus(self->name);
    1829
    0
    }
    1830
    1831
    // MARK - Swish Layer
    1832
    1833
    typedef struct {
    1834
      ccv_cnnp_model_t super;
    1835
      ccv_nnc_tensor_symbol_t output;
    1836
      float beta;
    1837
    } ccv_cnnp_model_swish_t;
    1838
    1839
    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)
    1840
    0
    {
    1841
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_swish_build] -\n");
    1842
    0
      assert(input_size == 1);
    1843
    0
      assert(output_size == 1);
    1844
    0
      ccv_cnnp_model_swish_t* const self = (ccv_cnnp_model_swish_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 swish = CMD_SWISH_FORWARD(self->beta);
    1848
    0
      ccv_nnc_hint_tensor_auto(swish, (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 swish_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1852
    0
      ccv_nnc_graph_exec_symbol_new(graph, swish, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(swish_output), "swish");
    1853
    0
      outputs[0] = swish_output;
    1854
    0
    }
    1855
    1856
    static ccv_cnnp_model_t* _ccv_cnnp_swish_copy(const ccv_cnnp_model_t* const self, void* const context);
    1857
    1858
    static const ccv_cnnp_model_vtab_t ccv_cnnp_swish_isa = {
    1859
      .build = _ccv_cnnp_swish_build,
    1860
      .copy = _ccv_cnnp_swish_copy,
    1861
    };
    1862
    1863
    ccv_cnnp_model_t* ccv_cnnp_swish(const float beta, const char* const name)
    1864
    0
    {
    1865
    0
      ccv_cnnp_model_swish_t* const model_swish = (ccv_cnnp_model_swish_t*)cccalloc(1, sizeof(ccv_cnnp_model_swish_t));
    1866
    0
      model_swish->super.isa = &ccv_cnnp_swish_isa;
    1867
    0
      model_swish->super.input_size = 1;
    1868
    0
      model_swish->super.outputs = &model_swish->output;
    1869
    0
      model_swish->super.output_size = 1;
    1870
    0
      model_swish->beta = beta;
    1871
    0
      ccv_cnnp_model_copy_name(&model_swish->super, name);
    1872
    0
      return (ccv_cnnp_model_t*)model_swish;
    1873
    0
    }
    1874
    1875
    static ccv_cnnp_model_t* _ccv_cnnp_swish_copy(const ccv_cnnp_model_t* const self, void* const context)
    1876
    0
    {
    1877
    0
      const ccv_cnnp_model_swish_t* const swish = (const ccv_cnnp_model_swish_t*)self;
    1878
    0
      return ccv_cnnp_swish(swish->beta, self->name);
    1879
    0
    }
    1880
    1881
    // MARK - GELU Layer
    1882
    1883
    typedef struct {
    1884
      ccv_cnnp_model_t super;
    1885
      ccv_nnc_tensor_symbol_t output;
    1886
      int tanh;
    1887
    } ccv_cnnp_model_gelu_t;
    1888
    1889
    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)
    1890
    2
    {
    1891
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_gelu_build] -\n");
    1892
    2
      assert(input_size == 1);
    1893
    2
      assert(output_size == 1);
    1894
    2
      ccv_cnnp_model_gelu_t* const self = (ccv_cnnp_model_gelu_t*)super;
    1895
    2
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1896
    2
      ccv_nnc_tensor_param_t output_params;
    1897
    2
      const ccv_nnc_cmd_t gelu = CMD_GELU_FORWARD(self->tanh);
    1898
    2
      ccv_nnc_hint_tensor_auto(gelu, (ccv_nnc_tensor_param_t []){
    1899
    2
          params,
    1900
    2
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1901
    2
      const ccv_nnc_tensor_symbol_t gelu_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1902
    2
      ccv_nnc_graph_exec_symbol_new(graph, gelu, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(gelu_output), "gelu");
    1903
    2
      outputs[0] = gelu_output;
    1904
    2
    }
    1905
    1906
    static ccv_cnnp_model_t* _ccv_cnnp_gelu_copy(const ccv_cnnp_model_t* const self, void* const context);
    1907
    1908
    static const ccv_cnnp_model_vtab_t ccv_cnnp_gelu_isa = {
    1909
      .build = _ccv_cnnp_gelu_build,
    1910
      .copy = _ccv_cnnp_gelu_copy,
    1911
    };
    1912
    1913
    ccv_cnnp_model_t* ccv_cnnp_gelu(const int tanh, const char* const name)
    1914
    1
    {
    1915
    1
      ccv_cnnp_model_gelu_t* const model_gelu = (ccv_cnnp_model_gelu_t*)cccalloc(1, sizeof(ccv_cnnp_model_gelu_t));
    1916
    1
      model_gelu->super.isa = &ccv_cnnp_gelu_isa;
    1917
    1
      model_gelu->super.input_size = 1;
    1918
    1
      model_gelu->super.outputs = &model_gelu->output;
    1919
    1
      model_gelu->super.output_size = 1;
    1920
    1
      model_gelu->tanh = tanh;
    1921
    1
      ccv_cnnp_model_copy_name(&model_gelu->super, name);
    1922
    1
      return (ccv_cnnp_model_t*)model_gelu;
    1923
    1
    }
    1924
    1925
    static ccv_cnnp_model_t* _ccv_cnnp_gelu_copy(const ccv_cnnp_model_t* const super, void* const context)
    1926
    0
    {
    1927
    0
      ccv_cnnp_model_gelu_t* const self = (ccv_cnnp_model_gelu_t*)super;
    1928
    0
      return ccv_cnnp_gelu(self->tanh, self->super.name);
    1929
    0
    }
    1930
    1931
    // MARK - Leaky ReLU Layer
    1932
    1933
    typedef struct {
    1934
      ccv_cnnp_model_t super;
    1935
      ccv_nnc_tensor_symbol_t output;
    1936
      float negative_slope;
    1937
    } ccv_cnnp_model_leaky_relu_t;
    1938
    1939
    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)
    1940
    0
    {
    1941
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_leaky_relu_build] -\n");
    1942
    0
      assert(input_size == 1);
    1943
    0
      assert(output_size == 1);
    1944
    0
      ccv_cnnp_model_leaky_relu_t* const self = (ccv_cnnp_model_leaky_relu_t*)super;
    1945
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1946
    0
      ccv_nnc_tensor_param_t output_params;
    1947
    0
      const ccv_nnc_cmd_t leaky_relu = CMD_LEAKY_RELU_FORWARD(self->negative_slope);
    1948
    0
      ccv_nnc_hint_tensor_auto(leaky_relu, (ccv_nnc_tensor_param_t []){
    1949
    0
          params,
    1950
    0
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1951
    0
      const ccv_nnc_tensor_symbol_t leaky_relu_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    1952
    0
      ccv_nnc_graph_exec_symbol_new(graph, leaky_relu, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(leaky_relu_output), "leaky_relu");
    1953
    0
      outputs[0] = leaky_relu_output;
    1954
    0
    }
    1955
    1956
    static ccv_cnnp_model_t* _ccv_cnnp_leaky_relu_copy(const ccv_cnnp_model_t* const self, void* const context);
    1957
    1958
    static const ccv_cnnp_model_vtab_t ccv_cnnp_leaky_relu_isa = {
    1959
      .build = _ccv_cnnp_leaky_relu_build,
    1960
      .copy = _ccv_cnnp_leaky_relu_copy,
    1961
    };
    1962
    1963
    ccv_cnnp_model_t* ccv_cnnp_leaky_relu(const float negative_slope, const char* const name)
    1964
    0
    {
    1965
    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));
    1966
    0
      model_leaky_relu->super.isa = &ccv_cnnp_leaky_relu_isa;
    1967
    0
      model_leaky_relu->super.input_size = 1;
    1968
    0
      model_leaky_relu->super.outputs = &model_leaky_relu->output;
    1969
    0
      model_leaky_relu->super.output_size = 1;
    1970
    0
      model_leaky_relu->negative_slope = negative_slope;
    1971
    0
      ccv_cnnp_model_copy_name(&model_leaky_relu->super, name);
    1972
    0
      return (ccv_cnnp_model_t*)model_leaky_relu;
    1973
    0
    }
    1974
    1975
    static ccv_cnnp_model_t* _ccv_cnnp_leaky_relu_copy(const ccv_cnnp_model_t* const super, void* const context)
    1976
    0
    {
    1977
    0
      ccv_cnnp_model_leaky_relu_t* const self = (ccv_cnnp_model_leaky_relu_t*)super;
    1978
    0
      return ccv_cnnp_leaky_relu(self->negative_slope, self->super.name);
    1979
    0
    }
    1980
    1981
    // MARK - Softmax Layer
    1982
    1983
    typedef struct {
    1984
      ccv_cnnp_model_t super;
    1985
      ccv_nnc_tensor_symbol_t output;
    1986
    } ccv_cnnp_model_softmax_t;
    1987
    1988
    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)
    1989
    8
    {
    1990
    8
      PRINT(CCV_CLI_VERBOSE, "[cnnp_softmax_build] -\n");
    1991
    8
      assert(input_size == 1);
    1992
    8
      assert(output_size == 1);
    1993
    8
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    1994
    8
      ccv_nnc_tensor_param_t output_params;
    1995
    8
      const ccv_nnc_cmd_t softmax = CMD_SOFTMAX_FORWARD();
    1996
    8
      ccv_nnc_hint_tensor_auto(softmax, (ccv_nnc_tensor_param_t []){
    1997
    8
          params,
    1998
    8
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    1999
    8
      const ccv_nnc_tensor_symbol_t softmax_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2000
    8
      ccv_nnc_graph_exec_symbol_new(graph, softmax, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(softmax_output), "softmax");
    2001
    8
      outputs[0] = softmax_output;
    2002
    8
    }
    2003
    2004
    static ccv_cnnp_model_t* _ccv_cnnp_softmax_copy(const ccv_cnnp_model_t* const self, void* const context);
    2005
    2006
    static const ccv_cnnp_model_vtab_t ccv_cnnp_softmax_isa = {
    2007
      .build = _ccv_cnnp_softmax_build,
    2008
      .copy = _ccv_cnnp_softmax_copy,
    2009
    };
    2010
    2011
    ccv_cnnp_model_t* ccv_cnnp_softmax(const char* const name)
    2012
    9
    {
    2013
    9
      ccv_cnnp_model_softmax_t* const model_softmax = (ccv_cnnp_model_softmax_t*)cccalloc(1, sizeof(ccv_cnnp_model_softmax_t));
    2014
    9
      model_softmax->super.isa = &ccv_cnnp_softmax_isa;
    2015
    9
      model_softmax->super.input_size = 1;
    2016
    9
      model_softmax->super.outputs = &model_softmax->output;
    2017
    9
      model_softmax->super.output_size = 1;
    2018
    9
      ccv_cnnp_model_copy_name(&model_softmax->super, name);
    2019
    9
      return (ccv_cnnp_model_t*)model_softmax;
    2020
    9
    }
    2021
    2022
    static ccv_cnnp_model_t* _ccv_cnnp_softmax_copy(const ccv_cnnp_model_t* const self, void* const context)
    2023
    1
    {
    2024
    1
      return ccv_cnnp_softmax(self->name);
    2025
    1
    }
    2026
    2027
    // MARK - Add Layer
    2028
    2029
    typedef struct {
    2030
      ccv_cnnp_model_t super;
    2031
      float p;
    2032
      float q;
    2033
      ccv_nnc_tensor_symbol_t output;
    2034
    } ccv_cnnp_model_add_t;
    2035
    2036
    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)
    2037
    0
    {
    2038
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_add_build] -\n");
    2039
    0
      const ccv_cnnp_model_add_t* const self = (const ccv_cnnp_model_add_t*)super;
    2040
    0
      assert(input_size == 2);
    2041
    0
      assert(output_size == 1);
    2042
    0
      ccv_nnc_tensor_param_t input_params[2];
    2043
    0
      int i;
    2044
    0
      for (i = 0; i < 2; i++)
    2045
    0
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    2046
    0
      ccv_nnc_tensor_param_t output_params;
    2047
    0
      const ccv_nnc_cmd_t add = CMD_ADD_FORWARD(self->p, self->q);
    2048
    0
      ccv_nnc_hint_tensor_auto(add, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    2049
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2050
    0
      ccv_nnc_graph_exec_symbol_new(graph, add, inputs, input_size, outputs, output_size, "add");
    2051
    0
    }
    2052
    2053
    static ccv_cnnp_model_t* _ccv_cnnp_add_copy(const ccv_cnnp_model_t* const self, void* const context);
    2054
    2055
    static const ccv_cnnp_model_vtab_t ccv_cnnp_add_isa = {
    2056
      .build = _ccv_cnnp_add_build,
    2057
      .copy = _ccv_cnnp_add_copy,
    2058
    };
    2059
    2060
    ccv_cnnp_model_t* ccv_cnnp_add(const float p, const float q, const char* const name)
    2061
    0
    {
    2062
    0
      ccv_cnnp_model_add_t* const model_add = (ccv_cnnp_model_add_t*)cccalloc(1, sizeof(ccv_cnnp_model_add_t));
    2063
    0
      model_add->super.isa = &ccv_cnnp_add_isa;
    2064
    0
      model_add->super.input_size = 2;
    2065
    0
      model_add->super.outputs = &model_add->output;
    2066
    0
      model_add->super.output_size = 1;
    2067
    0
      model_add->p = p;
    2068
    0
      model_add->q = q;
    2069
    0
      ccv_cnnp_model_copy_name(&model_add->super, name);
    2070
    0
      return (ccv_cnnp_model_t*)model_add;
    2071
    0
    }
    2072
    2073
    static ccv_cnnp_model_t* _ccv_cnnp_add_copy(const ccv_cnnp_model_t* const super, void* const context)
    2074
    0
    {
    2075
    0
      const ccv_cnnp_model_add_t* const self = (const ccv_cnnp_model_add_t*)super;
    2076
    0
      return ccv_cnnp_add(self->p, self->q, self->super.name);
    2077
    0
    }
    2078
    2079
    // MARK - Mul Layer
    2080
    2081
    typedef struct {
    2082
      ccv_cnnp_model_t super;
    2083
      ccv_nnc_tensor_symbol_t output;
    2084
      float p;
    2085
    } ccv_cnnp_model_mul_t;
    2086
    2087
    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)
    2088
    6
    {
    2089
    6
      PRINT(CCV_CLI_VERBOSE, "[cnnp_mul_build] -\n");
    2090
    6
      const ccv_cnnp_model_mul_t* const self = (const ccv_cnnp_model_mul_t*)super;
    2091
    6
      assert(input_size == 2);
    2092
    6
      assert(output_size == 1);
    2093
    6
      ccv_nnc_tensor_param_t input_params[2];
    2094
    6
      int i;
    2095
    18
      for (i = 0; i < 2; i++12)
    2096
    12
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    2097
    6
      ccv_nnc_tensor_param_t output_params;
    2098
    6
      const ccv_nnc_cmd_t mul = CMD_MUL_FORWARD(self->p);
    2099
    6
      ccv_nnc_hint_tensor_auto(mul, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    2100
    6
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2101
    6
      ccv_nnc_graph_exec_symbol_new(graph, mul, inputs, input_size, outputs, output_size, "mul");
    2102
    6
    }
    2103
    2104
    static ccv_cnnp_model_t* _ccv_cnnp_mul_copy(const ccv_cnnp_model_t* const self, void* const context);
    2105
    2106
    static const ccv_cnnp_model_vtab_t ccv_cnnp_mul_isa = {
    2107
      .build = _ccv_cnnp_mul_build,
    2108
      .copy = _ccv_cnnp_mul_copy,
    2109
    };
    2110
    2111
    ccv_cnnp_model_t* ccv_cnnp_mul(const float p, const char* const name)
    2112
    5
    {
    2113
    5
      ccv_cnnp_model_mul_t* const model_mul = (ccv_cnnp_model_mul_t*)cccalloc(1, sizeof(ccv_cnnp_model_mul_t));
    2114
    5
      model_mul->super.isa = &ccv_cnnp_mul_isa;
    2115
    5
      model_mul->super.input_size = 2;
    2116
    5
      model_mul->super.outputs = &model_mul->output;
    2117
    5
      model_mul->super.output_size = 1;
    2118
    5
      model_mul->p = p;
    2119
    5
      ccv_cnnp_model_copy_name(&model_mul->super, name);
    2120
    5
      return (ccv_cnnp_model_t*)model_mul;
    2121
    5
    }
    2122
    2123
    static ccv_cnnp_model_t* _ccv_cnnp_mul_copy(const ccv_cnnp_model_t* const super, void* const context)
    2124
    0
    {
    2125
    0
      const ccv_cnnp_model_mul_t* const self = (const ccv_cnnp_model_mul_t*)super;
    2126
    0
      return ccv_cnnp_mul(self->p, self->super.name);
    2127
    0
    }
    2128
    2129
    // MARK - Scalar Mul Layer
    2130
    2131
    typedef struct {
    2132
      ccv_cnnp_model_t super;
    2133
      ccv_nnc_tensor_symbol_t output;
    2134
      float a;
    2135
    } ccv_cnnp_model_scalar_mul_t;
    2136
    2137
    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)
    2138
    4
    {
    2139
    4
      PRINT(CCV_CLI_VERBOSE, "[cnnp_scalar_mul_build] -\n");
    2140
    4
      assert(input_size == 1);
    2141
    4
      assert(output_size == 1);
    2142
    4
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2143
    4
      ccv_nnc_tensor_param_t output_params;
    2144
    4
      ccv_cnnp_model_scalar_mul_t* const self = (ccv_cnnp_model_scalar_mul_t*)super;
    2145
    4
      const ccv_nnc_cmd_t scalar_mul = CMD_SCALAR_MUL_FORWARD(self->a);
    2146
    4
      ccv_nnc_hint_tensor_auto(scalar_mul, (ccv_nnc_tensor_param_t []){
    2147
    4
          params,
    2148
    4
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    2149
    4
      const ccv_nnc_tensor_symbol_t scalar_mul_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2150
    4
      ccv_nnc_graph_exec_symbol_new(graph, scalar_mul, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(scalar_mul_output), "scalar_mul");
    2151
    4
      outputs[0] = scalar_mul_output;
    2152
    4
    }
    2153
    2154
    static ccv_cnnp_model_t* _ccv_cnnp_scalar_mul_copy(const ccv_cnnp_model_t* const super, void* const context);
    2155
    2156
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scalar_mul_isa = {
    2157
      .build = _ccv_cnnp_scalar_mul_build,
    2158
      .copy = _ccv_cnnp_scalar_mul_copy,
    2159
    };
    2160
    2161
    ccv_cnnp_model_t* ccv_cnnp_scalar_mul(const float a, const char* const name)
    2162
    4
    {
    2163
    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));
    2164
    4
      model_scalar_mul->super.isa = &ccv_cnnp_scalar_mul_isa;
    2165
    4
      model_scalar_mul->super.input_size = 1;
    2166
    4
      model_scalar_mul->super.outputs = &model_scalar_mul->output;
    2167
    4
      model_scalar_mul->super.output_size = 1;
    2168
    4
      model_scalar_mul->a = a;
    2169
    4
      ccv_cnnp_model_copy_name(&model_scalar_mul->super, name);
    2170
    4
      return (ccv_cnnp_model_t*)model_scalar_mul;
    2171
    4
    }
    2172
    2173
    static ccv_cnnp_model_t* _ccv_cnnp_scalar_mul_copy(const ccv_cnnp_model_t* const super, void* const context)
    2174
    0
    {
    2175
    0
      const ccv_cnnp_model_scalar_mul_t* const self = (const ccv_cnnp_model_scalar_mul_t*)super;
    2176
    0
      return ccv_cnnp_scalar_mul(self->a, self->super.name);
    2177
    0
    }
    2178
    2179
    // MARK - Div Layer
    2180
    2181
    typedef struct {
    2182
      ccv_cnnp_model_t super;
    2183
      ccv_nnc_tensor_symbol_t output;
    2184
      int reciprocal;
    2185
    } ccv_cnnp_model_div_t;
    2186
    2187
    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)
    2188
    2
    {
    2189
    2
      const ccv_cnnp_model_div_t* const self = (const ccv_cnnp_model_div_t*)super;
    2190
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_div_build] -\n");
    2191
    2
      assert(output_size == 1);
    2192
    2
      ccv_nnc_tensor_param_t input_params[2];
    2193
    2
      int i;
    2194
    2
      ccv_nnc_tensor_param_t output_params;
    2195
    2
      const ccv_nnc_cmd_t div = CMD_EWDIV_FORWARD();
    2196
    2
      if (self->reciprocal)
    2197
    1
      {
    2198
    1
        assert(input_size == 1);
    2199
    1
        input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2200
    1
        input_params[1] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2201
    1
        ccv_nnc_hint_tensor_auto(div, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    2202
    1
        outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2203
    1
        ccv_nnc_graph_exec_symbol_new(graph, div, TENSOR_SYMBOL_LIST(NO_TENSOR_SYMBOL, inputs[0]), outputs, output_size, "div");
    2204
    1
      } else {
    2205
    1
        assert(input_size == 2);
    2206
    3
        for (i = 0; 1i < 2; i++2)
    2207
    2
          input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    2208
    1
        ccv_nnc_hint_tensor_auto(div, input_params, input_size, ccv_nnc_no_hint, &output_params, 1);
    2209
    1
        outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2210
    1
        ccv_nnc_graph_exec_symbol_new(graph, div, inputs, input_size, outputs, output_size, "div");
    2211
    1
      }
    2212
    2
    }
    2213
    2214
    static ccv_cnnp_model_t* _ccv_cnnp_div_copy(const ccv_cnnp_model_t* const self, void* const context);
    2215
    2216
    static const ccv_cnnp_model_vtab_t ccv_cnnp_div_isa = {
    2217
      .build = _ccv_cnnp_div_build,
    2218
      .copy = _ccv_cnnp_div_copy,
    2219
    };
    2220
    2221
    ccv_cnnp_model_t* ccv_cnnp_div(const int reciprocal, const char* const name)
    2222
    2
    {
    2223
    2
      ccv_cnnp_model_div_t* const model_div = (ccv_cnnp_model_div_t*)cccalloc(1, sizeof(ccv_cnnp_model_div_t));
    2224
    2
      model_div->super.isa = &ccv_cnnp_div_isa;
    2225
    2
      model_div->super.input_size = reciprocal ? 11 : 21;
    2226
    2
      model_div->super.outputs = &model_div->output;
    2227
    2
      model_div->super.output_size = 1;
    2228
    2
      model_div->reciprocal = reciprocal;
    2229
    2
      ccv_cnnp_model_copy_name(&model_div->super, name);
    2230
    2
      return (ccv_cnnp_model_t*)model_div;
    2231
    2
    }
    2232
    2233
    static ccv_cnnp_model_t* _ccv_cnnp_div_copy(const ccv_cnnp_model_t* const super, void* const context)
    2234
    0
    {
    2235
    0
      const ccv_cnnp_model_div_t* const self = (const ccv_cnnp_model_div_t*)super;
    2236
    0
      return ccv_cnnp_div(self->reciprocal, self->super.name);
    2237
    0
    }
    2238
    2239
    // MARK - Sqrt Layer
    2240
    2241
    typedef struct {
    2242
      ccv_cnnp_model_t super;
    2243
      ccv_nnc_tensor_symbol_t output;
    2244
    } ccv_cnnp_model_sqrt_t;
    2245
    2246
    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)
    2247
    0
    {
    2248
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sqrt_build] -\n");
    2249
    0
      assert(output_size == 1);
    2250
    0
      ccv_nnc_tensor_param_t input_params[1];
    2251
    0
      ccv_nnc_tensor_param_t output_params;
    2252
    0
      const ccv_nnc_cmd_t sqrt = CMD_EWSQRT_FORWARD();
    2253
    0
      assert(input_size == 1);
    2254
    0
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2255
    0
      ccv_nnc_hint_tensor_auto(sqrt, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2256
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2257
    0
      ccv_nnc_graph_exec_symbol_new(graph, sqrt, inputs, 1, outputs, output_size, "sqrt");
    2258
    0
    }
    2259
    2260
    static ccv_cnnp_model_t* _ccv_cnnp_sqrt_copy(const ccv_cnnp_model_t* const self, void* const context);
    2261
    2262
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sqrt_isa = {
    2263
      .build = _ccv_cnnp_sqrt_build,
    2264
      .copy = _ccv_cnnp_sqrt_copy,
    2265
    };
    2266
    2267
    ccv_cnnp_model_t* ccv_cnnp_sqrt(const char* const name)
    2268
    0
    {
    2269
    0
      ccv_cnnp_model_sqrt_t* const model_sqrt = (ccv_cnnp_model_sqrt_t*)cccalloc(1, sizeof(ccv_cnnp_model_sqrt_t));
    2270
    0
      model_sqrt->super.isa = &ccv_cnnp_sqrt_isa;
    2271
    0
      model_sqrt->super.input_size = 1;
    2272
    0
      model_sqrt->super.outputs = &model_sqrt->output;
    2273
    0
      model_sqrt->super.output_size = 1;
    2274
    0
      ccv_cnnp_model_copy_name(&model_sqrt->super, name);
    2275
    0
      return (ccv_cnnp_model_t*)model_sqrt;
    2276
    0
    }
    2277
    2278
    static ccv_cnnp_model_t* _ccv_cnnp_sqrt_copy(const ccv_cnnp_model_t* const super, void* const context)
    2279
    0
    {
    2280
    0
      const ccv_cnnp_model_sqrt_t* const self = (const ccv_cnnp_model_sqrt_t*)super;
    2281
    0
      return ccv_cnnp_sqrt(self->super.name);
    2282
    0
    }
    2283
    2284
    // MARK - Log Layer
    2285
    2286
    typedef struct {
    2287
      ccv_cnnp_model_t super;
    2288
      ccv_nnc_tensor_symbol_t output;
    2289
    } ccv_cnnp_model_log_t;
    2290
    2291
    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)
    2292
    1
    {
    2293
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_log_build] -\n");
    2294
    1
      assert(output_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 log = CMD_EWLOG_FORWARD();
    2298
    1
      assert(input_size == 1);
    2299
    1
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2300
    1
      ccv_nnc_hint_tensor_auto(log, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2301
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2302
    1
      ccv_nnc_graph_exec_symbol_new(graph, log, inputs, 1, outputs, output_size, "log");
    2303
    1
    }
    2304
    2305
    static ccv_cnnp_model_t* _ccv_cnnp_log_copy(const ccv_cnnp_model_t* const self, void* const context);
    2306
    2307
    static const ccv_cnnp_model_vtab_t ccv_cnnp_log_isa = {
    2308
      .build = _ccv_cnnp_log_build,
    2309
      .copy = _ccv_cnnp_log_copy,
    2310
    };
    2311
    2312
    ccv_cnnp_model_t* ccv_cnnp_log(const char* const name)
    2313
    1
    {
    2314
    1
      ccv_cnnp_model_log_t* const model_log = (ccv_cnnp_model_log_t*)cccalloc(1, sizeof(ccv_cnnp_model_log_t));
    2315
    1
      model_log->super.isa = &ccv_cnnp_log_isa;
    2316
    1
      model_log->super.input_size = 1;
    2317
    1
      model_log->super.outputs = &model_log->output;
    2318
    1
      model_log->super.output_size = 1;
    2319
    1
      ccv_cnnp_model_copy_name(&model_log->super, name);
    2320
    1
      return (ccv_cnnp_model_t*)model_log;
    2321
    1
    }
    2322
    2323
    static ccv_cnnp_model_t* _ccv_cnnp_log_copy(const ccv_cnnp_model_t* const super, void* const context)
    2324
    0
    {
    2325
    0
      return ccv_cnnp_log(super->name);
    2326
    0
    }
    2327
    2328
    // MARK - Pow Layer
    2329
    2330
    typedef struct {
    2331
      ccv_cnnp_model_t super;
    2332
      ccv_nnc_tensor_symbol_t output;
    2333
      ccv_nnc_cmd_param_t params;
    2334
    } ccv_cnnp_model_pow_t;
    2335
    2336
    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)
    2337
    1
    {
    2338
    1
      ccv_cnnp_model_pow_t* const self = (ccv_cnnp_model_pow_t*)super;
    2339
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_pow_build] -\n");
    2340
    1
      assert(input_size == 1);
    2341
    1
      assert(output_size == 1);
    2342
    1
      ccv_nnc_tensor_param_t input_params[1];
    2343
    1
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2344
    1
      ccv_nnc_tensor_param_t output_params;
    2345
    1
      const ccv_nnc_cmd_t pow = ccv_nnc_cmd(CCV_NNC_EWPOW_FORWARD, 0, self->params, 0);
    2346
    1
      ccv_nnc_hint_tensor_auto(pow, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2347
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2348
    1
      ccv_nnc_graph_exec_symbol_new(graph, pow, inputs, input_size, outputs, output_size, "pow");
    2349
    1
    }
    2350
    2351
    static ccv_cnnp_model_t* _ccv_cnnp_pow_copy(const ccv_cnnp_model_t* const self, void* const context);
    2352
    2353
    static const ccv_cnnp_model_vtab_t ccv_cnnp_pow_isa = {
    2354
      .build = _ccv_cnnp_pow_build,
    2355
      .copy = _ccv_cnnp_pow_copy,
    2356
    };
    2357
    2358
    ccv_cnnp_model_t* ccv_cnnp_pow(const float exponent, const char* const name)
    2359
    1
    {
    2360
    1
      ccv_cnnp_model_pow_t* const model_pow = (ccv_cnnp_model_pow_t*)cccalloc(1, sizeof(ccv_cnnp_model_pow_t));
    2361
    1
      model_pow->super.isa = &ccv_cnnp_pow_isa;
    2362
    1
      model_pow->super.input_size = 1;
    2363
    1
      model_pow->super.outputs = &model_pow->output;
    2364
    1
      model_pow->super.output_size = 1;
    2365
    1
      model_pow->params = (ccv_nnc_cmd_param_t){
    2366
    1
        .size = {
    2367
    1
          .dim = { 1, 1, 1 }
    2368
    1
        },
    2369
    1
        .pow = {
    2370
    1
          .exponent = exponent,
    2371
    1
        },
    2372
    1
      };
    2373
    1
      ccv_cnnp_model_copy_name(&model_pow->super, name);
    2374
    1
      return (ccv_cnnp_model_t*)model_pow;
    2375
    1
    }
    2376
    2377
    static ccv_cnnp_model_t* _ccv_cnnp_pow_copy(const ccv_cnnp_model_t* const super, void* const context)
    2378
    0
    {
    2379
    0
      const ccv_cnnp_model_pow_t* const self = (const ccv_cnnp_model_pow_t*)super;
    2380
    0
      return ccv_cnnp_pow(self->params.pow.exponent, super->name);
    2381
    0
    }
    2382
    2383
    // MARK - Sin Layer
    2384
    2385
    typedef struct {
    2386
      ccv_cnnp_model_t super;
    2387
      ccv_nnc_tensor_symbol_t output;
    2388
    } ccv_cnnp_model_sin_t;
    2389
    2390
    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)
    2391
    1
    {
    2392
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sin_build] -\n");
    2393
    1
      assert(output_size == 1);
    2394
    1
      assert(input_size == 1);
    2395
    1
      ccv_nnc_tensor_param_t input_params[1];
    2396
    1
      ccv_nnc_tensor_param_t output_params;
    2397
    1
      const ccv_nnc_cmd_t sin = CMD_EWSIN_FORWARD();
    2398
    1
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2399
    1
      ccv_nnc_hint_tensor_auto(sin, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2400
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2401
    1
      ccv_nnc_graph_exec_symbol_new(graph, sin, inputs, 1, outputs, output_size, "sin");
    2402
    1
    }
    2403
    2404
    static ccv_cnnp_model_t* _ccv_cnnp_sin_copy(const ccv_cnnp_model_t* const self, void* const context);
    2405
    2406
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sin_isa = {
    2407
      .build = _ccv_cnnp_sin_build,
    2408
      .copy = _ccv_cnnp_sin_copy,
    2409
    };
    2410
    2411
    ccv_cnnp_model_t* ccv_cnnp_sin(const char* const name)
    2412
    1
    {
    2413
    1
      ccv_cnnp_model_sin_t* const model_sin = (ccv_cnnp_model_sin_t*)cccalloc(1, sizeof(ccv_cnnp_model_sin_t));
    2414
    1
      model_sin->super.isa = &ccv_cnnp_sin_isa;
    2415
    1
      model_sin->super.input_size = 1;
    2416
    1
      model_sin->super.outputs = &model_sin->output;
    2417
    1
      model_sin->super.output_size = 1;
    2418
    1
      ccv_cnnp_model_copy_name(&model_sin->super, name);
    2419
    1
      return (ccv_cnnp_model_t*)model_sin;
    2420
    1
    }
    2421
    2422
    static ccv_cnnp_model_t* _ccv_cnnp_sin_copy(const ccv_cnnp_model_t* const super, void* const context)
    2423
    0
    {
    2424
    0
      return ccv_cnnp_sin(super->name);
    2425
    0
    }
    2426
    2427
    // MARK - Cos Layer
    2428
    2429
    typedef struct {
    2430
      ccv_cnnp_model_t super;
    2431
      ccv_nnc_tensor_symbol_t output;
    2432
    } ccv_cnnp_model_cos_t;
    2433
    2434
    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)
    2435
    1
    {
    2436
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_cos_build] -\n");
    2437
    1
      assert(output_size == 1);
    2438
    1
      assert(input_size == 1);
    2439
    1
      ccv_nnc_tensor_param_t input_params[1];
    2440
    1
      ccv_nnc_tensor_param_t output_params;
    2441
    1
      const ccv_nnc_cmd_t cos = CMD_EWCOS_FORWARD();
    2442
    1
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2443
    1
      ccv_nnc_hint_tensor_auto(cos, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2444
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2445
    1
      ccv_nnc_graph_exec_symbol_new(graph, cos, inputs, 1, outputs, output_size, "cos");
    2446
    1
    }
    2447
    2448
    static ccv_cnnp_model_t* _ccv_cnnp_cos_copy(const ccv_cnnp_model_t* const self, void* const context);
    2449
    2450
    static const ccv_cnnp_model_vtab_t ccv_cnnp_cos_isa = {
    2451
      .build = _ccv_cnnp_cos_build,
    2452
      .copy = _ccv_cnnp_cos_copy,
    2453
    };
    2454
    2455
    ccv_cnnp_model_t* ccv_cnnp_cos(const char* const name)
    2456
    1
    {
    2457
    1
      ccv_cnnp_model_cos_t* const model_cos = (ccv_cnnp_model_cos_t*)cccalloc(1, sizeof(ccv_cnnp_model_cos_t));
    2458
    1
      model_cos->super.isa = &ccv_cnnp_cos_isa;
    2459
    1
      model_cos->super.input_size = 1;
    2460
    1
      model_cos->super.outputs = &model_cos->output;
    2461
    1
      model_cos->super.output_size = 1;
    2462
    1
      ccv_cnnp_model_copy_name(&model_cos->super, name);
    2463
    1
      return (ccv_cnnp_model_t*)model_cos;
    2464
    1
    }
    2465
    2466
    static ccv_cnnp_model_t* _ccv_cnnp_cos_copy(const ccv_cnnp_model_t* const super, void* const context)
    2467
    0
    {
    2468
    0
      return ccv_cnnp_cos(super->name);
    2469
    0
    }
    2470
    2471
    // MARK - Rotate Half Layer
    2472
    2473
    typedef struct {
    2474
      ccv_cnnp_model_t super;
    2475
      ccv_nnc_tensor_symbol_t output;
    2476
    } ccv_cnnp_model_rotate_half_t;
    2477
    2478
    static void _ccv_cnnp_rotate_half_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)
    2479
    0
    {
    2480
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_rotate_half_build] -\n");
    2481
    0
      assert(input_size == 1);
    2482
    0
      assert(output_size == 1);
    2483
    0
      ccv_nnc_tensor_param_t input_params[1];
    2484
    0
      ccv_nnc_tensor_param_t output_params;
    2485
    0
      const ccv_nnc_cmd_t rotate_half = CMD_ROTATE_HALF_FORWARD();
    2486
    0
      input_params[0] = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2487
    0
      ccv_nnc_hint_tensor_auto(rotate_half, input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    2488
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2489
    0
      ccv_nnc_graph_exec_symbol_new(graph, rotate_half, inputs, 1, outputs, output_size, "rotate_half");
    2490
    0
    }
    2491
    2492
    static ccv_cnnp_model_t* _ccv_cnnp_rotate_half_copy(const ccv_cnnp_model_t* const self, void* const context);
    2493
    2494
    static const ccv_cnnp_model_vtab_t ccv_cnnp_rotate_half_isa = {
    2495
      .build = _ccv_cnnp_rotate_half_build,
    2496
      .copy = _ccv_cnnp_rotate_half_copy,
    2497
    };
    2498
    2499
    ccv_cnnp_model_t* ccv_cnnp_rotate_half(const char* const name)
    2500
    0
    {
    2501
    0
      ccv_cnnp_model_rotate_half_t* const model_rotate_half = (ccv_cnnp_model_rotate_half_t*)cccalloc(1, sizeof(ccv_cnnp_model_rotate_half_t));
    2502
    0
      model_rotate_half->super.isa = &ccv_cnnp_rotate_half_isa;
    2503
    0
      model_rotate_half->super.input_size = 1;
    2504
    0
      model_rotate_half->super.outputs = &model_rotate_half->output;
    2505
    0
      model_rotate_half->super.output_size = 1;
    2506
    0
      ccv_cnnp_model_copy_name(&model_rotate_half->super, name);
    2507
    0
      return (ccv_cnnp_model_t*)model_rotate_half;
    2508
    0
    }
    2509
    2510
    static ccv_cnnp_model_t* _ccv_cnnp_rotate_half_copy(const ccv_cnnp_model_t* const super, void* const context)
    2511
    0
    {
    2512
    0
      return ccv_cnnp_rotate_half(super->name);
    2513
    0
    }
    2514
    2515
    // MARK - Gated Delta Layer
    2516
    2517
    typedef struct {
    2518
      ccv_cnnp_model_t super;
    2519
      ccv_nnc_tensor_symbol_t outputs[2];
    2520
      int log_decay;
    2521
    } ccv_cnnp_model_gated_delta_t;
    2522
    2523
    static void _ccv_cnnp_gated_delta_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)
    2524
    0
    {
    2525
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_gated_delta_build] -\n");
    2526
    0
      ccv_cnnp_model_gated_delta_t* const self = (ccv_cnnp_model_gated_delta_t*)super;
    2527
    0
      assert(input_size == 6);
    2528
    0
      assert(output_size == 2);
    2529
    0
      ccv_nnc_tensor_param_t input_params[6];
    2530
    0
      int i;
    2531
    0
      for (i = 0; i < 6; i++)
    2532
    0
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    2533
    0
      ccv_nnc_tensor_param_t output_params[2];
    2534
    0
      const ccv_nnc_cmd_t gated_delta = CMD_GATED_DELTA_FORWARD(self->log_decay);
    2535
    0
      ccv_nnc_hint_tensor_auto(gated_delta, input_params, 6, ccv_nnc_no_hint, output_params, 2);
    2536
    0
      for (i = 0; i < 2; i++)
    2537
    0
        outputs[i] = ccv_nnc_tensor_symbol_new(graph, output_params[i], 0);
    2538
    0
      ccv_nnc_graph_exec_symbol_new(graph, gated_delta, inputs, input_size, outputs, output_size, "gated_delta");
    2539
    0
    }
    2540
    2541
    static ccv_cnnp_model_t* _ccv_cnnp_gated_delta_copy(const ccv_cnnp_model_t* const self, void* const context);
    2542
    2543
    static const ccv_cnnp_model_vtab_t ccv_cnnp_gated_delta_isa = {
    2544
      .build = _ccv_cnnp_gated_delta_build,
    2545
      .copy = _ccv_cnnp_gated_delta_copy,
    2546
    };
    2547
    2548
    ccv_cnnp_model_t* ccv_cnnp_gated_delta(const int log_decay, const char* const name)
    2549
    0
    {
    2550
    0
      ccv_cnnp_model_gated_delta_t* const model_gated_delta = (ccv_cnnp_model_gated_delta_t*)cccalloc(1, sizeof(ccv_cnnp_model_gated_delta_t));
    2551
    0
      model_gated_delta->super.isa = &ccv_cnnp_gated_delta_isa;
    2552
    0
      model_gated_delta->super.input_size = 6;
    2553
    0
      model_gated_delta->super.outputs = model_gated_delta->outputs;
    2554
    0
      model_gated_delta->super.output_size = 2;
    2555
    0
      model_gated_delta->log_decay = log_decay;
    2556
    0
      ccv_cnnp_model_copy_name(&model_gated_delta->super, name);
    2557
    0
      return (ccv_cnnp_model_t*)model_gated_delta;
    2558
    0
    }
    2559
    2560
    static ccv_cnnp_model_t* _ccv_cnnp_gated_delta_copy(const ccv_cnnp_model_t* const super, void* const context)
    2561
    0
    {
    2562
    0
      const ccv_cnnp_model_gated_delta_t* const self = (const ccv_cnnp_model_gated_delta_t*)super;
    2563
    0
      return ccv_cnnp_gated_delta(self->log_decay, super->name);
    2564
    0
    }
    2565
    2566
    // MARK - Cmul Layer
    2567
    2568
    typedef struct {
    2569
      ccv_cnnp_model_t super;
    2570
      ccv_nnc_tensor_symbol_t output;
    2571
    } ccv_cnnp_model_cmul_t;
    2572
    2573
    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)
    2574
    0
    {
    2575
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_cmul_build] -\n");
    2576
    0
      assert(input_size == 2);
    2577
    0
      assert(output_size == 1);
    2578
    0
      ccv_nnc_tensor_param_t input_params[2];
    2579
    0
      int i;
    2580
    0
      for (i = 0; i < 2; i++)
    2581
    0
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    2582
    0
      ccv_nnc_tensor_param_t output_params;
    2583
    0
      const ccv_nnc_cmd_t mul = CMD_CMUL_FORWARD();
    2584
    0
      ccv_nnc_hint_tensor_auto(mul, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    2585
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2586
    0
      ccv_nnc_graph_exec_symbol_new(graph, mul, inputs, input_size, outputs, output_size, "cmul");
    2587
    0
    }
    2588
    2589
    static ccv_cnnp_model_t* _ccv_cnnp_cmul_copy(const ccv_cnnp_model_t* const self, void* const context);
    2590
    2591
    static const ccv_cnnp_model_vtab_t ccv_cnnp_cmul_isa = {
    2592
      .build = _ccv_cnnp_cmul_build,
    2593
      .copy = _ccv_cnnp_cmul_copy,
    2594
    };
    2595
    2596
    ccv_cnnp_model_t* ccv_cnnp_cmul(const char* const name)
    2597
    0
    {
    2598
    0
      ccv_cnnp_model_cmul_t* const model_cmul = (ccv_cnnp_model_cmul_t*)cccalloc(1, sizeof(ccv_cnnp_model_cmul_t));
    2599
    0
      model_cmul->super.isa = &ccv_cnnp_cmul_isa;
    2600
    0
      model_cmul->super.input_size = 2;
    2601
    0
      model_cmul->super.outputs = &model_cmul->output;
    2602
    0
      model_cmul->super.output_size = 1;
    2603
    0
      ccv_cnnp_model_copy_name(&model_cmul->super, name);
    2604
    0
      return (ccv_cnnp_model_t*)model_cmul;
    2605
    0
    }
    2606
    2607
    static ccv_cnnp_model_t* _ccv_cnnp_cmul_copy(const ccv_cnnp_model_t* const super, void* const context)
    2608
    0
    {
    2609
    0
      return ccv_cnnp_cmul(super->name);
    2610
    0
    }
    2611
    2612
    // MARK - Transpose Layer
    2613
    2614
    typedef struct {
    2615
      ccv_cnnp_model_t super;
    2616
      ccv_nnc_tensor_symbol_t output;
    2617
      int transpose[2];
    2618
    } ccv_cnnp_model_transpose_t;
    2619
    2620
    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)
    2621
    22
    {
    2622
    22
      ccv_cnnp_model_transpose_t* const self = (ccv_cnnp_model_transpose_t*)super;
    2623
    22
      PRINT(CCV_CLI_VERBOSE, "[cnnp_transpose_build] (%d, %d)\n", self->transpose[0], self->transpose[1]);
    2624
    22
      assert(input_size == 1);
    2625
    22
      assert(output_size == 1);
    2626
    22
      if (self->transpose[0] == self->transpose[1])
    2627
    0
      {
    2628
    0
        outputs[0] = inputs[0];
    2629
    0
        return;
    2630
    0
      }
    2631
    22
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2632
    22
      ccv_nnc_tensor_param_t output_params;
    2633
    22
      const ccv_nnc_cmd_t transpose = CMD_TRANSPOSE_FORWARD(self->transpose[0], self->transpose[1]);
    2634
    22
      ccv_nnc_hint_tensor_auto(transpose, (ccv_nnc_tensor_param_t []){
    2635
    22
          params,
    2636
    22
        }, 1, ccv_nnc_no_hint, &output_params, 1);
    2637
    22
      const ccv_nnc_tensor_symbol_t transpose_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    2638
    22
      ccv_nnc_graph_exec_symbol_new(graph, transpose, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(transpose_output), "transpose");
    2639
    22
      outputs[0] = transpose_output;
    2640
    22
    }
    2641
    2642
    static ccv_cnnp_model_t* _ccv_cnnp_transpose_copy(const ccv_cnnp_model_t* const super, void* const context);
    2643
    2644
    static const ccv_cnnp_model_vtab_t ccv_cnnp_transpose_isa = {
    2645
      .build = _ccv_cnnp_transpose_build,
    2646
      .copy = _ccv_cnnp_transpose_copy,
    2647
    };
    2648
    2649
    ccv_cnnp_model_t* ccv_cnnp_transpose(const int axis_a, const int axis_b, const char* const name)
    2650
    22
    {
    2651
    22
      ccv_cnnp_model_transpose_t* const model_transpose = (ccv_cnnp_model_transpose_t*)cccalloc(1, sizeof(ccv_cnnp_model_transpose_t));
    2652
    22
      model_transpose->super.isa = &ccv_cnnp_transpose_isa;
    2653
    22
      model_transpose->super.input_size = 1;
    2654
    22
      model_transpose->super.outputs = &model_transpose->output;
    2655
    22
      model_transpose->super.output_size = 1;
    2656
    22
      model_transpose->transpose[0] = axis_a;
    2657
    22
      model_transpose->transpose[1] = axis_b;
    2658
    22
      ccv_cnnp_model_copy_name(&model_transpose->super, name);
    2659
    22
      return (ccv_cnnp_model_t*)model_transpose;
    2660
    22
    }
    2661
    2662
    static ccv_cnnp_model_t* _ccv_cnnp_transpose_copy(const ccv_cnnp_model_t* const super, void* const context)
    2663
    0
    {
    2664
    0
      const ccv_cnnp_model_transpose_t* const self = (const ccv_cnnp_model_transpose_t*)super;
    2665
    0
      return ccv_cnnp_transpose(self->transpose[0], self->transpose[1], self->super.name);
    2666
    0
    }
    2667
    2668
    // MARK - Layer Norm Layer
    2669
    2670
    typedef struct {
    2671
      ccv_cnnp_model_t super;
    2672
      ccv_nnc_tensor_symbol_t output;
    2673
      ccv_nnc_tensor_symbol_t bias;
    2674
      ccv_nnc_tensor_symbol_t scale;
    2675
      ccv_nnc_cmd_param_t params;
    2676
    } ccv_cnnp_model_layer_norm_t;
    2677
    2678
    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)
    2679
    12
    {
    2680
    12
      PRINT(CCV_CLI_VERBOSE, "[cnnp_layer_norm_build] -\n");
    2681
    12
      assert(input_size == 1);
    2682
    12
      assert(output_size == 1);
    2683
    12
      ccv_cnnp_model_layer_norm_t* const self = (ccv_cnnp_model_layer_norm_t*)super;
    2684
    12
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2685
    12
      ccv_nnc_tensor_param_t bias_params = params;
    2686
    12
      const int nd = ccv_nnc_tensor_nd(params.dim);
    2687
    12
      int i;
    2688
    44
      for (i = 0; i < nd; i++32)
    2689
    32
        bias_params.dim[i] = 1;
    2690
    24
      for (i = 0; i < self->params.lnorm.count; i++12)
    2691
    12
        bias_params.dim[self->params.lnorm.axis[i]] = params.dim[self->params.lnorm.axis[i]];
    2692
    12
      if (self->params.lnorm.elementwise_affine)
    2693
    12
      {
    2694
        // Both scale and bias are shared between if this model is reused.
    2695
    12
        if (!self->scale.graph)
    2696
    9
          self->scale = ccv_nnc_tensor_symbol_new(graph, bias_params, "scale");
    2697
    12
        if (!self->bias.graph)
    2698
    9
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    2699
    12
      }
    2700
    12
      const ccv_nnc_tensor_symbol_t scale = self->params.lnorm.elementwise_affine ? ccv_cnnp_model_get_symbol(super, self->scale) : NO_TENSOR_SYMBOL0;
    2701
    12
      const ccv_nnc_tensor_symbol_t bias = self->params.lnorm.elementwise_affine ? ccv_cnnp_model_get_symbol(super, self->bias) : NO_TENSOR_SYMBOL0;
    2702
    12
      const ccv_nnc_cmd_t layer_norm = ccv_nnc_cmd(CCV_NNC_LAYER_NORM_FORWARD, 0, self->params, 0);
    2703
    12
      ccv_nnc_tensor_param_t output_params[3];
    2704
    12
      if (self->params.lnorm.elementwise_affine)
    2705
    12
        ccv_nnc_hint_tensor_auto(layer_norm, (ccv_nnc_tensor_param_t []){
    2706
    12
            params,
    2707
    12
            bias_params,
    2708
    12
            bias_params,
    2709
    12
          }, 3, ccv_nnc_no_hint, output_params, 3);
    2710
    0
      else
    2711
    0
        ccv_nnc_hint_tensor_auto(layer_norm, (ccv_nnc_tensor_param_t []){
    2712
    0
            params,
    2713
    0
          }, 1, ccv_nnc_no_hint, output_params, 3);
    2714
    12
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    2715
    12
      const ccv_nnc_tensor_symbol_t saved_mean = ccv_nnc_tensor_symbol_new(graph, output_params[1], "saved_mean");
    2716
    12
      const ccv_nnc_tensor_symbol_t saved_inv_std = ccv_nnc_tensor_symbol_new(graph, output_params[2], "saved_inv_std");
    2717
    12
      if (self->params.lnorm.elementwise_affine)
    2718
    12
        ccv_nnc_graph_exec_symbol_new(graph, layer_norm, TENSOR_SYMBOL_LIST(inputs[0], scale, bias), TENSOR_SYMBOL_LIST(output, saved_mean, saved_inv_std), "layer_norm");
    2719
    0
      else
    2720
    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");
    2721
    12
      outputs[0] = output;
    2722
    12
    }
    2723
    2724
    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)
    2725
    8
    {
    2726
    8
      ccv_cnnp_model_layer_norm_t* const self = (ccv_cnnp_model_layer_norm_t*)super;
    2727
    8
      if (self->scale.graph)
    2728
    8
        initializer(context, CMD_SET_FORWARD(1), ccv_nnc_no_hint, 0, 0, self->scale);
    2729
    8
      if (self->bias.graph)
    2730
    8
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    2731
    8
    }
    2732
    2733
    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)
    2734
    12
    {
    2735
    12
      ccv_cnnp_model_layer_norm_t* const self = (ccv_cnnp_model_layer_norm_t*)super;
    2736
    12
      if (self->scale.graph)
    2737
    12
        add_to_array(parameters, self->scale, is_trainable);
    2738
    12
      if (self->bias.graph)
    2739
    12
        add_to_array(parameters, self->bias, is_trainable);
    2740
    12
    }
    2741
    2742
    static ccv_cnnp_model_t* _ccv_cnnp_layer_norm_copy(const ccv_cnnp_model_t* const super, void* const context);
    2743
    2744
    static const ccv_cnnp_model_vtab_t ccv_cnnp_layer_norm_isa = {
    2745
      .build = _ccv_cnnp_layer_norm_build,
    2746
      .init_states = _ccv_cnnp_layer_norm_init_states,
    2747
      .add_to_parameter = _ccv_cnnp_layer_norm_add_to_parameter,
    2748
      .copy = _ccv_cnnp_layer_norm_copy,
    2749
    };
    2750
    2751
    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)
    2752
    9
    {
    2753
    9
      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));
    2754
    9
      model_layer_norm->super.isa = &ccv_cnnp_layer_norm_isa;
    2755
    9
      model_layer_norm->super.input_size = 1;
    2756
    9
      model_layer_norm->super.outputs = &model_layer_norm->output;
    2757
    9
      model_layer_norm->super.output_size = 1;
    2758
    9
      model_layer_norm->super.is_trainable = is_trainable;
    2759
    9
      ccv_cnnp_model_copy_name(&model_layer_norm->super, name);
    2760
    9
      model_layer_norm->scale.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2761
    9
      model_layer_norm->scale.graph = 0;
    2762
    9
      model_layer_norm->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2763
    9
      model_layer_norm->bias.graph = 0;
    2764
    9
      model_layer_norm->params.lnorm.epsilon = epsilon;
    2765
    9
      model_layer_norm->params.lnorm.count = axis_count;
    2766
    9
      model_layer_norm->params.lnorm.elementwise_affine = elementwise_affine;
    2767
    9
      memcpy(model_layer_norm->params.lnorm.axis, axis, sizeof(int) * axis_count);
    2768
    9
      return (ccv_cnnp_model_t*)model_layer_norm;
    2769
    9
    }
    2770
    2771
    static ccv_cnnp_model_t* _ccv_cnnp_layer_norm_copy(const ccv_cnnp_model_t* const super, void* const context)
    2772
    0
    {
    2773
    0
      const ccv_cnnp_model_layer_norm_t* const self = (const ccv_cnnp_model_layer_norm_t*)super;
    2774
    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);
    2775
    0
    }
    2776
    2777
    // MARK - Group Norm Layer
    2778
    2779
    typedef struct {
    2780
      ccv_cnnp_model_t super;
    2781
      ccv_nnc_tensor_symbol_t output;
    2782
      ccv_nnc_tensor_symbol_t bias;
    2783
      ccv_nnc_tensor_symbol_t scale;
    2784
      ccv_nnc_cmd_param_t params;
    2785
    } ccv_cnnp_model_group_norm_t;
    2786
    2787
    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)
    2788
    0
    {
    2789
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_group_norm_build] -\n");
    2790
    0
      assert(input_size == 1);
    2791
    0
      assert(output_size == 1);
    2792
    0
      ccv_cnnp_model_group_norm_t* const self = (ccv_cnnp_model_group_norm_t*)super;
    2793
    0
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2794
    0
      ccv_nnc_tensor_param_t bias_params = params;
    2795
    0
      const int nd = ccv_nnc_tensor_nd(params.dim);
    2796
    0
      int i;
    2797
    0
      for (i = 0; i < nd; i++)
    2798
    0
        bias_params.dim[i] = 1;
    2799
    0
      bias_params.dim[self->params.gnorm.group_axis] = params.dim[self->params.gnorm.group_axis];
    2800
    0
      if (self->params.gnorm.elementwise_affine)
    2801
    0
      {
    2802
        // Both scale and bias are shared between if this model is reused.
    2803
    0
        if (!self->scale.graph)
    2804
    0
          self->scale = ccv_nnc_tensor_symbol_new(graph, bias_params, "scale");
    2805
    0
        if (!self->bias.graph)
    2806
    0
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    2807
    0
      }
    2808
    0
      const ccv_nnc_tensor_symbol_t scale = self->params.gnorm.elementwise_affine ? ccv_cnnp_model_get_symbol(super, self->scale) : NO_TENSOR_SYMBOL;
    2809
    0
      const ccv_nnc_tensor_symbol_t bias = self->params.gnorm.elementwise_affine ? ccv_cnnp_model_get_symbol(super, self->bias) : NO_TENSOR_SYMBOL;
    2810
    0
      const ccv_nnc_cmd_t group_norm = ccv_nnc_cmd(CCV_NNC_GROUP_NORM_FORWARD, 0, self->params, 0);
    2811
    0
      ccv_nnc_tensor_param_t output_params[3];
    2812
    0
      if (self->params.gnorm.elementwise_affine)
    2813
    0
        ccv_nnc_hint_tensor_auto(group_norm, (ccv_nnc_tensor_param_t []){
    2814
    0
            params,
    2815
    0
            bias_params,
    2816
    0
            bias_params,
    2817
    0
          }, 3, ccv_nnc_no_hint, output_params, 3);
    2818
    0
      else
    2819
    0
        ccv_nnc_hint_tensor_auto(group_norm, (ccv_nnc_tensor_param_t []){
    2820
    0
            params,
    2821
    0
          }, 1, ccv_nnc_no_hint, output_params, 3);
    2822
    0
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    2823
    0
      const ccv_nnc_tensor_symbol_t saved_mean = ccv_nnc_tensor_symbol_new(graph, output_params[1], "saved_mean");
    2824
    0
      const ccv_nnc_tensor_symbol_t saved_inv_std = ccv_nnc_tensor_symbol_new(graph, output_params[2], "saved_inv_std");
    2825
    0
      if (self->params.gnorm.elementwise_affine)
    2826
    0
        ccv_nnc_graph_exec_symbol_new(graph, group_norm, TENSOR_SYMBOL_LIST(inputs[0], scale, bias), TENSOR_SYMBOL_LIST(output, saved_mean, saved_inv_std), "group_norm");
    2827
    0
      else
    2828
    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");
    2829
    0
      outputs[0] = output;
    2830
    0
    }
    2831
    2832
    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)
    2833
    0
    {
    2834
    0
      ccv_cnnp_model_group_norm_t* const self = (ccv_cnnp_model_group_norm_t*)super;
    2835
    0
      if (self->scale.graph)
    2836
    0
        initializer(context, CMD_SET_FORWARD(1), ccv_nnc_no_hint, 0, 0, self->scale);
    2837
    0
      if (self->bias.graph)
    2838
    0
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    2839
    0
    }
    2840
    2841
    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)
    2842
    0
    {
    2843
    0
      ccv_cnnp_model_group_norm_t* const self = (ccv_cnnp_model_group_norm_t*)super;
    2844
    0
      if (self->scale.graph)
    2845
    0
        add_to_array(parameters, self->scale, is_trainable);
    2846
    0
      if (self->bias.graph)
    2847
    0
        add_to_array(parameters, self->bias, is_trainable);
    2848
    0
    }
    2849
    2850
    static ccv_cnnp_model_t* _ccv_cnnp_group_norm_copy(const ccv_cnnp_model_t* const super, void* const context);
    2851
    2852
    static const ccv_cnnp_model_vtab_t ccv_cnnp_group_norm_isa = {
    2853
      .build = _ccv_cnnp_group_norm_build,
    2854
      .init_states = _ccv_cnnp_group_norm_init_states,
    2855
      .add_to_parameter = _ccv_cnnp_group_norm_add_to_parameter,
    2856
      .copy = _ccv_cnnp_group_norm_copy,
    2857
    };
    2858
    2859
    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)
    2860
    0
    {
    2861
    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));
    2862
    0
      model_group_norm->super.isa = &ccv_cnnp_group_norm_isa;
    2863
    0
      model_group_norm->super.input_size = 1;
    2864
    0
      model_group_norm->super.outputs = &model_group_norm->output;
    2865
    0
      model_group_norm->super.output_size = 1;
    2866
    0
      model_group_norm->super.is_trainable = is_trainable;
    2867
    0
      ccv_cnnp_model_copy_name(&model_group_norm->super, name);
    2868
    0
      model_group_norm->scale.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2869
    0
      model_group_norm->scale.graph = 0;
    2870
    0
      model_group_norm->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2871
    0
      model_group_norm->bias.graph = 0;
    2872
    0
      model_group_norm->params.gnorm.group_axis = group_axis;
    2873
    0
      model_group_norm->params.gnorm.groups = groups;
    2874
    0
      model_group_norm->params.gnorm.epsilon = epsilon;
    2875
    0
      model_group_norm->params.gnorm.reduce_count = axis_count;
    2876
    0
      model_group_norm->params.gnorm.elementwise_affine = elementwise_affine;
    2877
    0
      memcpy(model_group_norm->params.gnorm.reduce_axis, reduce_axis, sizeof(int) * axis_count);
    2878
    0
      return (ccv_cnnp_model_t*)model_group_norm;
    2879
    0
    }
    2880
    2881
    static ccv_cnnp_model_t* _ccv_cnnp_group_norm_copy(const ccv_cnnp_model_t* const super, void* const context)
    2882
    0
    {
    2883
    0
      const ccv_cnnp_model_group_norm_t* const self = (const ccv_cnnp_model_group_norm_t*)super;
    2884
    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);
    2885
    0
    }
    2886
    2887
    // MARK - RMSNorm Layer
    2888
    2889
    typedef struct {
    2890
      ccv_cnnp_model_t super;
    2891
      ccv_nnc_tensor_symbol_t output;
    2892
      ccv_nnc_tensor_symbol_t scale;
    2893
      ccv_nnc_cmd_param_t params;
    2894
    } ccv_cnnp_model_rmsnorm_t;
    2895
    2896
    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)
    2897
    0
    {
    2898
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_rmsnorm_build] -\n");
    2899
    0
      assert(input_size == 1);
    2900
    0
      assert(output_size == 1);
    2901
    0
      ccv_cnnp_model_rmsnorm_t* const self = (ccv_cnnp_model_rmsnorm_t*)super;
    2902
    0
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    2903
    0
      ccv_nnc_tensor_param_t scale_params = params;
    2904
    0
      const int nd = ccv_nnc_tensor_nd(params.dim);
    2905
    0
      int i;
    2906
    0
      for (i = 0; i < nd; i++)
    2907
    0
        scale_params.dim[i] = 1;
    2908
    0
      for (i = 0; i < self->params.rmsnorm.count; i++)
    2909
    0
        scale_params.dim[self->params.rmsnorm.axis[i]] = params.dim[self->params.rmsnorm.axis[i]];
    2910
      // Both scale and bias are shared between if this model is reused.
    2911
    0
      if (self->params.rmsnorm.elementwise_affine)
    2912
    0
      {
    2913
    0
        if (!self->scale.graph)
    2914
    0
          self->scale = ccv_nnc_tensor_symbol_new(graph, scale_params, "scale");
    2915
    0
      }
    2916
    0
      const ccv_nnc_tensor_symbol_t scale = self->params.rmsnorm.elementwise_affine ? ccv_cnnp_model_get_symbol(super, self->scale) : NO_TENSOR_SYMBOL;
    2917
    0
      const ccv_nnc_cmd_t rmsnorm = ccv_nnc_cmd(CCV_NNC_RMSNORM_FORWARD, 0, self->params, 0);
    2918
    0
      ccv_nnc_tensor_param_t output_params[2];
    2919
    0
      if (self->params.rmsnorm.elementwise_affine)
    2920
    0
        ccv_nnc_hint_tensor_auto(rmsnorm, (ccv_nnc_tensor_param_t []){
    2921
    0
            params,
    2922
    0
            scale_params,
    2923
    0
          }, 2, ccv_nnc_no_hint, output_params, 2);
    2924
    0
      else
    2925
    0
        ccv_nnc_hint_tensor_auto(rmsnorm, (ccv_nnc_tensor_param_t []){
    2926
    0
            params,
    2927
    0
          }, 1, ccv_nnc_no_hint, output_params, 2);
    2928
    0
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    2929
    0
      const ccv_nnc_tensor_symbol_t saved_inv_std = ccv_nnc_tensor_symbol_new(graph, output_params[1], "saved_inv_std");
    2930
    0
      if (self->params.rmsnorm.elementwise_affine)
    2931
    0
        ccv_nnc_graph_exec_symbol_new(graph, rmsnorm, TENSOR_SYMBOL_LIST(inputs[0], scale), TENSOR_SYMBOL_LIST(output, saved_inv_std), "rmsnorm");
    2932
    0
      else
    2933
    0
        ccv_nnc_graph_exec_symbol_new(graph, rmsnorm, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(output, saved_inv_std), "rmsnorm");
    2934
    0
      outputs[0] = output;
    2935
    0
    }
    2936
    2937
    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)
    2938
    0
    {
    2939
    0
      ccv_cnnp_model_rmsnorm_t* const self = (ccv_cnnp_model_rmsnorm_t*)super;
    2940
    0
      if (self->scale.graph)
    2941
    0
        initializer(context, CMD_SET_FORWARD(1), ccv_nnc_no_hint, 0, 0, self->scale);
    2942
    0
    }
    2943
    2944
    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)
    2945
    0
    {
    2946
    0
      ccv_cnnp_model_rmsnorm_t* const self = (ccv_cnnp_model_rmsnorm_t*)super;
    2947
    0
      if (self->scale.graph)
    2948
    0
        add_to_array(parameters, self->scale, is_trainable);
    2949
    0
    }
    2950
    2951
    static ccv_cnnp_model_t* _ccv_cnnp_rmsnorm_copy(const ccv_cnnp_model_t* const super, void* const context);
    2952
    2953
    static const ccv_cnnp_model_vtab_t ccv_cnnp_rmsnorm_isa = {
    2954
      .build = _ccv_cnnp_rmsnorm_build,
    2955
      .init_states = _ccv_cnnp_rmsnorm_init_states,
    2956
      .add_to_parameter = _ccv_cnnp_rmsnorm_add_to_parameter,
    2957
      .copy = _ccv_cnnp_rmsnorm_copy,
    2958
    };
    2959
    2960
    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)
    2961
    0
    {
    2962
    0
      ccv_cnnp_model_rmsnorm_t* const model_rmsnorm = (ccv_cnnp_model_rmsnorm_t*)cccalloc(1, sizeof(ccv_cnnp_model_rmsnorm_t));
    2963
    0
      model_rmsnorm->super.isa = &ccv_cnnp_rmsnorm_isa;
    2964
    0
      model_rmsnorm->super.input_size = 1;
    2965
    0
      model_rmsnorm->super.outputs = &model_rmsnorm->output;
    2966
    0
      model_rmsnorm->super.output_size = 1;
    2967
    0
      model_rmsnorm->super.is_trainable = is_trainable;
    2968
    0
      ccv_cnnp_model_copy_name(&model_rmsnorm->super, name);
    2969
    0
      model_rmsnorm->scale.d = CCV_NNC_NO_TENSOR_SYMBOL;
    2970
    0
      model_rmsnorm->scale.graph = 0;
    2971
    0
      model_rmsnorm->params.rmsnorm.epsilon = epsilon;
    2972
    0
      model_rmsnorm->params.rmsnorm.count = axis_count;
    2973
    0
      model_rmsnorm->params.rmsnorm.elementwise_affine = elementwise_affine;
    2974
    0
      memcpy(model_rmsnorm->params.lnorm.axis, axis, sizeof(int) * axis_count);
    2975
    0
      return (ccv_cnnp_model_t*)model_rmsnorm;
    2976
    0
    }
    2977
    2978
    static ccv_cnnp_model_t* _ccv_cnnp_rmsnorm_copy(const ccv_cnnp_model_t* const super, void* const context)
    2979
    0
    {
    2980
    0
      const ccv_cnnp_model_rmsnorm_t* const self = (const ccv_cnnp_model_rmsnorm_t*)super;
    2981
    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);
    2982
    0
    }
    2983
    2984
    // MARK - Batched Matrix Mul Layer
    2985
    2986
    typedef struct {
    2987
      ccv_cnnp_model_t super;
    2988
      ccv_nnc_tensor_symbol_t output;
    2989
      int transpose_a[2];
    2990
      int transpose_b[2];
    2991
      int flags;
    2992
    } ccv_cnnp_model_matmul_t;
    2993
    2994
    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)
    2995
    10
    {
    2996
    10
      PRINT(CCV_CLI_VERBOSE, "[cnnp_matmul_build] -\n");
    2997
    10
      assert(input_size == 2);
    2998
    10
      assert(output_size == 1);
    2999
    10
      ccv_cnnp_model_matmul_t* const self = (ccv_cnnp_model_matmul_t*)super;
    3000
    10
      ccv_nnc_tensor_param_t a_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3001
    10
      ccv_nnc_tensor_param_t b_params = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    3002
    10
      ccv_nnc_tensor_param_t output_params;
    3003
    10
      ccv_nnc_cmd_t matmul = CMD_GEMM_FORWARD(self->transpose_a, self->transpose_b);
    3004
    10
      matmul.info.blas.flags = self->flags;
    3005
    10
      ccv_nnc_hint_tensor_auto(matmul, (ccv_nnc_tensor_param_t []){
    3006
    10
          a_params,
    3007
    10
          b_params,
    3008
    10
        }, 2, ccv_nnc_no_hint, &output_params, 1);
    3009
    10
      const ccv_nnc_tensor_symbol_t matmul_output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3010
    10
      ccv_nnc_graph_exec_symbol_new(graph, matmul, inputs, input_size, TENSOR_SYMBOL_LIST(matmul_output), "matmul");
    3011
    10
      outputs[0] = matmul_output;
    3012
    10
    }
    3013
    3014
    static ccv_cnnp_model_t* _ccv_cnnp_matmul_copy(const ccv_cnnp_model_t* const super, void* const context);
    3015
    3016
    static const ccv_cnnp_model_vtab_t ccv_cnnp_matmul_isa = {
    3017
      .build = _ccv_cnnp_matmul_build,
    3018
      .copy = _ccv_cnnp_matmul_copy,
    3019
    };
    3020
    3021
    ccv_cnnp_model_t* ccv_cnnp_matmul(const int transpose_a[2], const int transpose_b[2], const int flags, const char* const name)
    3022
    10
    {
    3023
    10
      ccv_cnnp_model_matmul_t* const model_matmul = (ccv_cnnp_model_matmul_t*)cccalloc(1, sizeof(ccv_cnnp_model_matmul_t));
    3024
    10
      model_matmul->super.isa = &ccv_cnnp_matmul_isa;
    3025
    10
      model_matmul->super.input_size = 2;
    3026
    10
      model_matmul->super.outputs = &model_matmul->output;
    3027
    10
      model_matmul->super.output_size = 1;
    3028
    10
      model_matmul->transpose_a[0] = transpose_a[0];
    3029
    10
      model_matmul->transpose_a[1] = transpose_a[1];
    3030
    10
      model_matmul->transpose_b[0] = transpose_b[0];
    3031
    10
      model_matmul->transpose_b[1] = transpose_b[1];
    3032
    10
      model_matmul->flags = flags;
    3033
    10
      ccv_cnnp_model_copy_name(&model_matmul->super, name);
    3034
    10
      return (ccv_cnnp_model_t*)model_matmul;
    3035
    10
    }
    3036
    3037
    static ccv_cnnp_model_t* _ccv_cnnp_matmul_copy(const ccv_cnnp_model_t* const super, void* const context)
    3038
    1
    {
    3039
    1
      const ccv_cnnp_model_matmul_t* const self = (const ccv_cnnp_model_matmul_t*)super;
    3040
    1
      return ccv_cnnp_matmul(self->transpose_a, self->transpose_b, self->flags, self->super.name);
    3041
    1
    }
    3042
    3043
    // MARK - Dropout Layer
    3044
    3045
    typedef struct {
    3046
      ccv_cnnp_model_t super;
    3047
      ccv_nnc_tensor_symbol_t output;
    3048
      ccv_nnc_graph_exec_symbol_t dropout;
    3049
      float p;
    3050
      int entirety;
    3051
    } ccv_cnnp_model_dropout_t;
    3052
    3053
    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)
    3054
    12
    {
    3055
    12
      PRINT(CCV_CLI_VERBOSE, "[cnnp_dropout_build] -\n");
    3056
    12
      assert(input_size == 1);
    3057
    12
      assert(output_size == 1);
    3058
    12
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3059
    12
      ccv_nnc_tensor_param_t output_params[2];
    3060
    12
      ccv_cnnp_model_dropout_t* const self = (ccv_cnnp_model_dropout_t*)super;
    3061
    12
      const ccv_nnc_cmd_t dropout = CMD_DROPOUT_FORWARD(self->p, self->entirety);
    3062
    12
      ccv_nnc_hint_tensor_auto(dropout, (ccv_nnc_tensor_param_t []){
    3063
    12
          params,
    3064
    12
        }, 1, ccv_nnc_no_hint, output_params, 2);
    3065
    12
      const ccv_nnc_tensor_symbol_t dropout_output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    3066
    12
      const ccv_nnc_tensor_symbol_t mask = ccv_nnc_tensor_symbol_new(graph, output_params[1], "mask");
    3067
    12
      self->dropout = ccv_nnc_graph_exec_symbol_new(graph, dropout, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(dropout_output, mask), "dropout");
    3068
    12
      outputs[0] = dropout_output;
    3069
    12
    }
    3070
    3071
    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)
    3072
    24
    {
    3073
    24
      ccv_cnnp_model_dropout_t* const self = (ccv_cnnp_model_dropout_t*)super;
    3074
    24
      if (self->dropout.graph)
    3075
    24
      {
    3076
    24
        if (is_test)
    3077
          // During test, the dropout is not applied. Data transfer is perfect because if these are the same tensor, it will skip.
    3078
    12
          updater(context, self->dropout, CMD_DATA_TRANSFER_FORWARD(), ccv_nnc_no_hint);
    3079
    12
        else
    3080
    12
          updater(context, self->dropout, CMD_DROPOUT_FORWARD(self->p, self->entirety), ccv_nnc_no_hint);
    3081
    24
      }
    3082
    24
    }
    3083
    3084
    static ccv_cnnp_model_t* _ccv_cnnp_dropout_copy(const ccv_cnnp_model_t* const super, void* const context);
    3085
    3086
    static const ccv_cnnp_model_vtab_t ccv_cnnp_dropout_isa = {
    3087
      .build = _ccv_cnnp_dropout_build,
    3088
      .set_is_test = _ccv_cnnp_dropout_set_is_test,
    3089
      .copy = _ccv_cnnp_dropout_copy,
    3090
    };
    3091
    3092
    ccv_cnnp_model_t* ccv_cnnp_dropout(const float p, const int entirety, const char* const name)
    3093
    12
    {
    3094
    12
      ccv_cnnp_model_dropout_t* const model_dropout = (ccv_cnnp_model_dropout_t*)cccalloc(1, sizeof(ccv_cnnp_model_dropout_t));
    3095
    12
      model_dropout->super.isa = &ccv_cnnp_dropout_isa;
    3096
    12
      model_dropout->super.input_size = 1;
    3097
    12
      model_dropout->super.outputs = &model_dropout->output;
    3098
    12
      model_dropout->super.output_size = 1;
    3099
    12
      model_dropout->p = p;
    3100
    12
      model_dropout->entirety = entirety;
    3101
    12
      ccv_cnnp_model_copy_name(&model_dropout->super, name);
    3102
    12
      return (ccv_cnnp_model_t*)model_dropout;
    3103
    12
    }
    3104
    3105
    static ccv_cnnp_model_t* _ccv_cnnp_dropout_copy(const ccv_cnnp_model_t* const super, void* const context)
    3106
    0
    {
    3107
    0
      const ccv_cnnp_model_dropout_t* const self = (const ccv_cnnp_model_dropout_t*)super;
    3108
    0
      return ccv_cnnp_dropout(self->p, self->entirety, self->super.name);
    3109
    0
    }
    3110
    3111
    // MARK - Masked Fill Layer
    3112
    3113
    typedef struct {
    3114
      ccv_cnnp_model_t super;
    3115
      ccv_nnc_tensor_symbol_t output;
    3116
      float eq;
    3117
      float fill;
    3118
    } ccv_cnnp_model_masked_fill_t;
    3119
    3120
    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)
    3121
    4
    {
    3122
    4
      PRINT(CCV_CLI_VERBOSE, "[cnnp_masked_fill_build] -\n");
    3123
    4
      assert(input_size == 2);
    3124
    4
      assert(output_size == 1);
    3125
    4
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3126
    4
      ccv_cnnp_model_masked_fill_t* const self = (ccv_cnnp_model_masked_fill_t*)super;
    3127
    4
      const ccv_nnc_tensor_symbol_t masked_fill_output = ccv_nnc_tensor_symbol_new(graph, params, 0);
    3128
    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");
    3129
    4
      outputs[0] = masked_fill_output;
    3130
    4
    }
    3131
    3132
    static ccv_cnnp_model_t* _ccv_cnnp_masked_fill_copy(const ccv_cnnp_model_t* const super, void* const context);
    3133
    3134
    static const ccv_cnnp_model_vtab_t ccv_cnnp_masked_fill_isa = {
    3135
      .build = _ccv_cnnp_masked_fill_build,
    3136
      .copy = _ccv_cnnp_masked_fill_copy,
    3137
    };
    3138
    3139
    ccv_cnnp_model_t* ccv_cnnp_masked_fill(const float eq, const float fill, const char* const name)
    3140
    4
    {
    3141
    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));
    3142
    4
      model_masked_fill->super.isa = &ccv_cnnp_masked_fill_isa;
    3143
    4
      model_masked_fill->super.input_size = 2;
    3144
    4
      model_masked_fill->super.outputs = &model_masked_fill->output;
    3145
    4
      model_masked_fill->super.output_size = 1;
    3146
    4
      model_masked_fill->eq = eq;
    3147
    4
      model_masked_fill->fill = fill;
    3148
    4
      ccv_cnnp_model_copy_name(&model_masked_fill->super, name);
    3149
    4
      return (ccv_cnnp_model_t*)model_masked_fill;
    3150
    4
    }
    3151
    3152
    static ccv_cnnp_model_t* _ccv_cnnp_masked_fill_copy(const ccv_cnnp_model_t* const super, void* const context)
    3153
    0
    {
    3154
    0
      const ccv_cnnp_model_masked_fill_t* const self = (const ccv_cnnp_model_masked_fill_t*)super;
    3155
    0
      return ccv_cnnp_masked_fill(self->eq, self->fill, self->super.name);
    3156
    0
    }
    3157
    3158
    // MARK - Index Select Layer
    3159
    3160
    typedef struct {
    3161
      ccv_cnnp_model_t super;
    3162
      ccv_nnc_tensor_symbol_t output;
    3163
    } ccv_cnnp_model_index_select_t;
    3164
    3165
    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)
    3166
    2
    {
    3167
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_index_select_build] -\n");
    3168
    2
      assert(input_size == 2);
    3169
    2
      assert(output_size == 1);
    3170
    2
      const ccv_nnc_tensor_param_t vocab_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3171
    2
      const ccv_nnc_tensor_param_t index_params = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    3172
    2
      ccv_nnc_tensor_param_t output_params;
    3173
    2
      const ccv_nnc_cmd_t index_select = CMD_INDEX_SELECT_FORWARD();
    3174
    2
      ccv_nnc_hint_tensor_auto(index_select, (ccv_nnc_tensor_param_t []){
    3175
    2
          vocab_params,
    3176
    2
          index_params,
    3177
    2
        }, 2, ccv_nnc_no_hint, &output_params, 1);
    3178
    2
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3179
    2
      ccv_nnc_graph_exec_symbol_new(graph, index_select, TENSOR_SYMBOL_LIST(inputs[0], inputs[1]), TENSOR_SYMBOL_LIST(output), "index_select");
    3180
    2
      outputs[0] = output;
    3181
    2
    }
    3182
    3183
    static ccv_cnnp_model_t* _ccv_cnnp_index_select_copy(const ccv_cnnp_model_t* const super, void* const context);
    3184
    3185
    static const ccv_cnnp_model_vtab_t ccv_cnnp_index_select_isa = {
    3186
      .build = _ccv_cnnp_index_select_build,
    3187
      .copy = _ccv_cnnp_index_select_copy,
    3188
    };
    3189
    3190
    ccv_cnnp_model_t* ccv_cnnp_index_select(const char* const name)
    3191
    2
    {
    3192
    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));
    3193
    2
      model_index_select->super.isa = &ccv_cnnp_index_select_isa;
    3194
    2
      model_index_select->super.input_size = 2;
    3195
    2
      model_index_select->super.outputs = &model_index_select->output;
    3196
    2
      model_index_select->super.output_size = 1;
    3197
    2
      ccv_cnnp_model_copy_name(&model_index_select->super, name);
    3198
    2
      return (ccv_cnnp_model_t*)model_index_select;
    3199
    2
    }
    3200
    3201
    static ccv_cnnp_model_t* _ccv_cnnp_index_select_copy(const ccv_cnnp_model_t* const super, void* const context)
    3202
    0
    {
    3203
    0
      ccv_cnnp_model_index_select_t* const self = (ccv_cnnp_model_index_select_t*)super;
    3204
    0
      return ccv_cnnp_index_select(self->super.name);
    3205
    0
    }
    3206
    3207
    // MARK - Embedding Layer
    3208
    3209
    typedef struct {
    3210
      ccv_cnnp_model_t super;
    3211
      ccv_nnc_tensor_symbol_t output;
    3212
      ccv_nnc_tensor_symbol_t vocab;
    3213
      int datatype;
    3214
      int vocab_size;
    3215
      int embed_size;
    3216
    } ccv_cnnp_model_embedding_t;
    3217
    3218
    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)
    3219
    5
    {
    3220
    5
      ccv_cnnp_model_embedding_t* const self = (ccv_cnnp_model_embedding_t*)super;
    3221
    5
      PRINT(CCV_CLI_VERBOSE, "[cnnp_embedding_build] vocab_size: %d, embed_size: %d\n", self->vocab_size, self->embed_size);
    3222
    5
      assert(input_size == 1);
    3223
    5
      assert(output_size == 1);
    3224
    5
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3225
    5
      ccv_nnc_tensor_param_t vocab_params = params;
    3226
    5
      memset(vocab_params.dim, 0, sizeof(vocab_params.dim));
    3227
    5
      vocab_params.datatype = self->datatype;
    3228
    5
      vocab_params.dim[0] = self->vocab_size;
    3229
    5
      vocab_params.dim[1] = self->embed_size;
    3230
    5
      if (!self->vocab.graph)
    3231
    2
        self->vocab = ccv_nnc_tensor_symbol_new(graph, vocab_params, "vocab");
    3232
    5
      assert(self->vocab.graph == graph);
    3233
    5
      const ccv_nnc_tensor_symbol_t vocab = ccv_cnnp_model_get_symbol(super, self->vocab);
    3234
    5
      ccv_nnc_tensor_param_t output_params;
    3235
    5
      const ccv_nnc_cmd_t embedding = CMD_INDEX_SELECT_FORWARD();
    3236
    5
      ccv_nnc_hint_tensor_auto(embedding, (ccv_nnc_tensor_param_t []){
    3237
    5
          vocab_params,
    3238
    5
          params,
    3239
    5
        }, 2, ccv_nnc_no_hint, &output_params, 1);
    3240
    5
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3241
    5
      ccv_nnc_graph_exec_symbol_new(graph, embedding, TENSOR_SYMBOL_LIST(vocab, inputs[0]), TENSOR_SYMBOL_LIST(output), "embedding");
    3242
    5
      outputs[0] = output;
    3243
    5
    }
    3244
    3245
    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)
    3246
    1
    {
    3247
    1
      ccv_cnnp_model_embedding_t* const self = (ccv_cnnp_model_embedding_t*)super;
    3248
    1
      const float std = sqrtf(2) / sqrtf(self->vocab_size + self->embed_size);
    3249
    1
      const float bound = sqrtf(3) * std;
    3250
    1
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->vocab);
    3251
    1
    }
    3252
    3253
    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)
    3254
    5
    {
    3255
    5
      ccv_cnnp_model_embedding_t* const self = (ccv_cnnp_model_embedding_t*)super;
    3256
    5
      add_to_array(parameters, self->vocab, is_trainable);
    3257
    5
    }
    3258
    3259
    static ccv_cnnp_model_t* _ccv_cnnp_embedding_copy(const ccv_cnnp_model_t* const super, void* const context);
    3260
    3261
    static const ccv_cnnp_model_vtab_t ccv_cnnp_embedding_isa = {
    3262
      .build = _ccv_cnnp_embedding_build,
    3263
      .init_states = _ccv_cnnp_embedding_init_states,
    3264
      .add_to_parameter = _ccv_cnnp_embedding_add_to_parameter,
    3265
      .copy = _ccv_cnnp_embedding_copy,
    3266
    };
    3267
    3268
    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)
    3269
    2
    {
    3270
    2
      ccv_cnnp_model_embedding_t* const model_embedding = (ccv_cnnp_model_embedding_t*)cccalloc(1, sizeof(ccv_cnnp_model_embedding_t));
    3271
    2
      model_embedding->super.isa = &ccv_cnnp_embedding_isa;
    3272
    2
      model_embedding->super.input_size = 1;
    3273
    2
      model_embedding->super.outputs = &model_embedding->output;
    3274
    2
      model_embedding->super.output_size = 1;
    3275
    2
      model_embedding->super.is_trainable = is_trainable;
    3276
    2
      ccv_cnnp_model_copy_name(&model_embedding->super, name);
    3277
    2
      model_embedding->vocab.d = CCV_NNC_NO_TENSOR_SYMBOL;
    3278
    2
      model_embedding->vocab.graph = 0;
    3279
    2
      assert(datatype == CCV_32F || datatype == CCV_16F || datatype == CCV_16BF);
    3280
    2
      model_embedding->datatype = datatype;
    3281
    2
      assert(vocab_size > 0);
    3282
    2
      model_embedding->vocab_size = vocab_size;
    3283
    2
      assert(embed_size > 0);
    3284
    2
      model_embedding->embed_size = embed_size;
    3285
    2
      return (ccv_cnnp_model_t*)model_embedding;
    3286
    2
    }
    3287
    3288
    static ccv_cnnp_model_t* _ccv_cnnp_embedding_copy(const ccv_cnnp_model_t* const super, void* const context)
    3289
    0
    {
    3290
    0
      ccv_cnnp_model_embedding_t* const self = (ccv_cnnp_model_embedding_t*)super;
    3291
    0
      return ccv_cnnp_embedding(self->datatype, self->vocab_size, self->embed_size, self->super.is_trainable, self->super.name);
    3292
    0
    }
    3293
    3294
    // MARK - Pool Layers
    3295
    3296
    typedef struct {
    3297
      ccv_cnnp_model_t super;
    3298
      ccv_nnc_tensor_symbol_t output;
    3299
      int type;
    3300
      float width_scale;
    3301
      float height_scale;
    3302
      int align_corners;
    3303
    } ccv_cnnp_model_upsample_t;
    3304
    3305
    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)
    3306
    3
    {
    3307
    3
      PRINT(CCV_CLI_VERBOSE, "[cnnp_upsample_build] -\n");
    3308
    3
      assert(input_size == 1);
    3309
    3
      assert(output_size == 1);
    3310
    3
      ccv_cnnp_model_upsample_t* const self = (ccv_cnnp_model_upsample_t*)super;
    3311
    3
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3312
    3
      ccv_nnc_cmd_t cmd = CMD_UPSAMPLE_FORWARD(self->type, self->width_scale, self->height_scale, self->align_corners);
    3313
    3
      ccv_nnc_tensor_param_t output_params;
    3314
    3
      ccv_nnc_hint_tensor_auto(cmd, &params, 1, ccv_nnc_no_hint, &output_params, 1);
    3315
    3
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3316
    3
      ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0]), TENSOR_SYMBOL_LIST(output), "upsample");
    3317
    3
      outputs[0] = output;
    3318
    3
    }
    3319
    3320
    static ccv_cnnp_model_t* _ccv_cnnp_upsample_copy(const ccv_cnnp_model_t* const super, void* const context);
    3321
    3322
    static const ccv_cnnp_model_vtab_t ccv_cnnp_upsample_isa = {
    3323
      .build = _ccv_cnnp_upsample_build,
    3324
      .copy = _ccv_cnnp_upsample_copy,
    3325
    };
    3326
    3327
    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)
    3328
    3
    {
    3329
    3
      ccv_cnnp_model_upsample_t* const model_upsample = (ccv_cnnp_model_upsample_t*)cccalloc(1, sizeof(ccv_cnnp_model_upsample_t));
    3330
    3
      model_upsample->super.isa = &ccv_cnnp_upsample_isa;
    3331
    3
      model_upsample->super.input_size = 1;
    3332
    3
      model_upsample->super.outputs = &model_upsample->output;
    3333
    3
      model_upsample->super.output_size = 1;
    3334
    3
      ccv_cnnp_model_copy_name(&model_upsample->super, name);
    3335
    3
      assert(type == CCV_NNC_UPSAMPLE_NEAREST || type == CCV_NNC_UPSAMPLE_BILINEAR);
    3336
    3
      model_upsample->type = type;
    3337
    3
      model_upsample->width_scale = width_scale;
    3338
    3
      model_upsample->height_scale = height_scale;
    3339
    3
      model_upsample->align_corners = align_corners;
    3340
    3
      return (ccv_cnnp_model_t*)model_upsample;
    3341
    3
    }
    3342
    3343
    static ccv_cnnp_model_t* _ccv_cnnp_upsample_copy(const ccv_cnnp_model_t* const super, void* const context)
    3344
    0
    {
    3345
    0
      const ccv_cnnp_model_upsample_t* const self = (const ccv_cnnp_model_upsample_t*)super;
    3346
    0
      return ccv_cnnp_upsample(self->type, self->width_scale, self->height_scale, self->align_corners, self->super.name);
    3347
    0
    }
    3348
    3349
    // MARK - Reduce Sum Layer
    3350
    3351
    typedef struct {
    3352
      ccv_cnnp_model_t super;
    3353
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3354
      int count;
    3355
      ccv_nnc_tensor_symbol_t output;
    3356
    } ccv_cnnp_model_reduce_sum_t;
    3357
    3358
    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)
    3359
    1
    {
    3360
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_sum_build] -\n");
    3361
    1
      const ccv_cnnp_model_reduce_sum_t* const self = (const ccv_cnnp_model_reduce_sum_t*)super;
    3362
    1
      assert(input_size == 1);
    3363
    1
      assert(output_size == 1);
    3364
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3365
    1
      ccv_nnc_tensor_param_t output_params;
    3366
    1
      ccv_nnc_cmd_t reduce_sum = CMD_REDUCE_SUM_FORWARD();
    3367
    1
      int i;
    3368
    2
      for (i = 0; i < self->count; i++1)
    3369
    1
        reduce_sum.info.reduce.axis[i] = self->axis[i];
    3370
    1
      reduce_sum.info.reduce.count = self->count;
    3371
    1
      ccv_nnc_hint_tensor_auto(reduce_sum, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3372
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3373
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_sum, inputs, input_size, outputs, output_size, "reduce_sum");
    3374
    1
    }
    3375
    3376
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_sum_copy(const ccv_cnnp_model_t* const self, void* const context);
    3377
    3378
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_sum_isa = {
    3379
      .build = _ccv_cnnp_reduce_sum_build,
    3380
      .copy = _ccv_cnnp_reduce_sum_copy,
    3381
    };
    3382
    3383
    ccv_cnnp_model_t* ccv_cnnp_reduce_sum(const int* const axis, const int axis_count, const char* const name)
    3384
    1
    {
    3385
    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));
    3386
    1
      model_reduce_sum->super.isa = &ccv_cnnp_reduce_sum_isa;
    3387
    1
      model_reduce_sum->super.input_size = 1;
    3388
    1
      model_reduce_sum->super.outputs = &model_reduce_sum->output;
    3389
    1
      model_reduce_sum->super.output_size = 1;
    3390
    1
      ccv_cnnp_model_copy_name(&model_reduce_sum->super, name);
    3391
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3392
    1
      int i;
    3393
    2
      for (i = 0; i < axis_count; i++1)
    3394
    1
        model_reduce_sum->axis[i] = axis[i];
    3395
    1
      model_reduce_sum->count = axis_count;
    3396
    1
      return (ccv_cnnp_model_t*)model_reduce_sum;
    3397
    1
    }
    3398
    3399
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_sum_copy(const ccv_cnnp_model_t* const super, void* const context)
    3400
    0
    {
    3401
    0
      const ccv_cnnp_model_reduce_sum_t* const self = (const ccv_cnnp_model_reduce_sum_t*)super;
    3402
    0
      return ccv_cnnp_reduce_sum(self->axis, self->count, self->super.name);
    3403
    0
    }
    3404
    3405
    // MARK - Reduce Mean Layer
    3406
    3407
    typedef struct {
    3408
      ccv_cnnp_model_t super;
    3409
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3410
      int count;
    3411
      ccv_nnc_tensor_symbol_t output;
    3412
    } ccv_cnnp_model_reduce_mean_t;
    3413
    3414
    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)
    3415
    1
    {
    3416
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_mean_build] -\n");
    3417
    1
      const ccv_cnnp_model_reduce_mean_t* const self = (const ccv_cnnp_model_reduce_mean_t*)super;
    3418
    1
      assert(input_size == 1);
    3419
    1
      assert(output_size == 1);
    3420
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3421
    1
      ccv_nnc_tensor_param_t output_params;
    3422
    1
      ccv_nnc_cmd_t reduce_mean = CMD_REDUCE_MEAN_FORWARD();
    3423
    1
      int i;
    3424
    2
      for (i = 0; i < self->count; i++1)
    3425
    1
        reduce_mean.info.reduce.axis[i] = self->axis[i];
    3426
    1
      reduce_mean.info.reduce.count = self->count;
    3427
    1
      ccv_nnc_hint_tensor_auto(reduce_mean, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3428
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3429
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_mean, inputs, input_size, outputs, output_size, "reduce_mean");
    3430
    1
    }
    3431
    3432
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_mean_copy(const ccv_cnnp_model_t* const self, void* const context);
    3433
    3434
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_mean_isa = {
    3435
      .build = _ccv_cnnp_reduce_mean_build,
    3436
      .copy = _ccv_cnnp_reduce_mean_copy,
    3437
    };
    3438
    3439
    ccv_cnnp_model_t* ccv_cnnp_reduce_mean(const int* const axis, const int axis_count, const char* const name)
    3440
    1
    {
    3441
    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));
    3442
    1
      model_reduce_mean->super.isa = &ccv_cnnp_reduce_mean_isa;
    3443
    1
      model_reduce_mean->super.input_size = 1;
    3444
    1
      model_reduce_mean->super.outputs = &model_reduce_mean->output;
    3445
    1
      model_reduce_mean->super.output_size = 1;
    3446
    1
      ccv_cnnp_model_copy_name(&model_reduce_mean->super, name);
    3447
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3448
    1
      int i;
    3449
    2
      for (i = 0; i < axis_count; i++1)
    3450
    1
        model_reduce_mean->axis[i] = axis[i];
    3451
    1
      model_reduce_mean->count = axis_count;
    3452
    1
      return (ccv_cnnp_model_t*)model_reduce_mean;
    3453
    1
    }
    3454
    3455
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_mean_copy(const ccv_cnnp_model_t* const super, void* const context)
    3456
    0
    {
    3457
    0
      const ccv_cnnp_model_reduce_mean_t* const self = (const ccv_cnnp_model_reduce_mean_t*)super;
    3458
    0
      return ccv_cnnp_reduce_mean(self->axis, self->count, self->super.name);
    3459
    0
    }
    3460
    3461
    // MARK - Reduce Max Layer
    3462
    3463
    typedef struct {
    3464
      ccv_cnnp_model_t super;
    3465
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3466
      int count;
    3467
      ccv_nnc_tensor_symbol_t output;
    3468
    } ccv_cnnp_model_reduce_max_t;
    3469
    3470
    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)
    3471
    1
    {
    3472
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_max_build] -\n");
    3473
    1
      const ccv_cnnp_model_reduce_max_t* const self = (const ccv_cnnp_model_reduce_max_t*)super;
    3474
    1
      assert(input_size == 1);
    3475
    1
      assert(output_size == 1);
    3476
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3477
    1
      ccv_nnc_tensor_param_t output_params;
    3478
    1
      ccv_nnc_cmd_t reduce_max = CMD_REDUCE_MAX_FORWARD();
    3479
    1
      int i;
    3480
    2
      for (i = 0; i < self->count; i++1)
    3481
    1
        reduce_max.info.reduce.axis[i] = self->axis[i];
    3482
    1
      reduce_max.info.reduce.count = self->count;
    3483
    1
      ccv_nnc_hint_tensor_auto(reduce_max, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3484
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3485
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_max, inputs, input_size, outputs, output_size, "reduce_max");
    3486
    1
    }
    3487
    3488
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_max_copy(const ccv_cnnp_model_t* const self, void* const context);
    3489
    3490
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_max_isa = {
    3491
      .build = _ccv_cnnp_reduce_max_build,
    3492
      .copy = _ccv_cnnp_reduce_max_copy,
    3493
    };
    3494
    3495
    ccv_cnnp_model_t* ccv_cnnp_reduce_max(const int* const axis, const int axis_count, const char* const name)
    3496
    1
    {
    3497
    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));
    3498
    1
      model_reduce_max->super.isa = &ccv_cnnp_reduce_max_isa;
    3499
    1
      model_reduce_max->super.input_size = 1;
    3500
    1
      model_reduce_max->super.outputs = &model_reduce_max->output;
    3501
    1
      model_reduce_max->super.output_size = 1;
    3502
    1
      ccv_cnnp_model_copy_name(&model_reduce_max->super, name);
    3503
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3504
    1
      int i;
    3505
    2
      for (i = 0; i < axis_count; i++1)
    3506
    1
        model_reduce_max->axis[i] = axis[i];
    3507
    1
      model_reduce_max->count = axis_count;
    3508
    1
      return (ccv_cnnp_model_t*)model_reduce_max;
    3509
    1
    }
    3510
    3511
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_max_copy(const ccv_cnnp_model_t* const super, void* const context)
    3512
    0
    {
    3513
    0
      const ccv_cnnp_model_reduce_max_t* const self = (const ccv_cnnp_model_reduce_max_t*)super;
    3514
    0
      return ccv_cnnp_reduce_max(self->axis, self->count, self->super.name);
    3515
    0
    }
    3516
    3517
    // MARK - Reduce Min Layer
    3518
    3519
    typedef struct {
    3520
      ccv_cnnp_model_t super;
    3521
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3522
      int count;
    3523
      ccv_nnc_tensor_symbol_t output;
    3524
    } ccv_cnnp_model_reduce_min_t;
    3525
    3526
    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)
    3527
    1
    {
    3528
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_min_build] -\n");
    3529
    1
      const ccv_cnnp_model_reduce_min_t* const self = (const ccv_cnnp_model_reduce_min_t*)super;
    3530
    1
      assert(input_size == 1);
    3531
    1
      assert(output_size == 1);
    3532
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3533
    1
      ccv_nnc_tensor_param_t output_params;
    3534
    1
      ccv_nnc_cmd_t reduce_min = CMD_REDUCE_MIN_FORWARD();
    3535
    1
      int i;
    3536
    2
      for (i = 0; i < self->count; i++1)
    3537
    1
        reduce_min.info.reduce.axis[i] = self->axis[i];
    3538
    1
      reduce_min.info.reduce.count = self->count;
    3539
    1
      ccv_nnc_hint_tensor_auto(reduce_min, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3540
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3541
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_min, inputs, input_size, outputs, output_size, "reduce_min");
    3542
    1
    }
    3543
    3544
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_min_copy(const ccv_cnnp_model_t* const self, void* const context);
    3545
    3546
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_min_isa = {
    3547
      .build = _ccv_cnnp_reduce_min_build,
    3548
      .copy = _ccv_cnnp_reduce_min_copy,
    3549
    };
    3550
    3551
    ccv_cnnp_model_t* ccv_cnnp_reduce_min(const int* const axis, const int axis_count, const char* const name)
    3552
    1
    {
    3553
    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));
    3554
    1
      model_reduce_min->super.isa = &ccv_cnnp_reduce_min_isa;
    3555
    1
      model_reduce_min->super.input_size = 1;
    3556
    1
      model_reduce_min->super.outputs = &model_reduce_min->output;
    3557
    1
      model_reduce_min->super.output_size = 1;
    3558
    1
      ccv_cnnp_model_copy_name(&model_reduce_min->super, name);
    3559
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3560
    1
      int i;
    3561
    2
      for (i = 0; i < axis_count; i++1)
    3562
    1
        model_reduce_min->axis[i] = axis[i];
    3563
    1
      model_reduce_min->count = axis_count;
    3564
    1
      return (ccv_cnnp_model_t*)model_reduce_min;
    3565
    1
    }
    3566
    3567
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_min_copy(const ccv_cnnp_model_t* const super, void* const context)
    3568
    0
    {
    3569
    0
      const ccv_cnnp_model_reduce_min_t* const self = (const ccv_cnnp_model_reduce_min_t*)super;
    3570
    0
      return ccv_cnnp_reduce_min(self->axis, self->count, self->super.name);
    3571
    0
    }
    3572
    3573
    // MARK - Reduce Norm2 Layer
    3574
    3575
    typedef struct {
    3576
      ccv_cnnp_model_t super;
    3577
      int axis[CCV_NNC_MAX_DIM_ALLOC];
    3578
      int count;
    3579
      ccv_nnc_tensor_symbol_t output;
    3580
    } ccv_cnnp_model_reduce_norm2_t;
    3581
    3582
    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)
    3583
    1
    {
    3584
    1
      const ccv_cnnp_model_reduce_norm2_t* const self = (const ccv_cnnp_model_reduce_norm2_t*)super;
    3585
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_reduce_norm2_build] -\n");
    3586
    1
      assert(input_size == 1);
    3587
    1
      assert(output_size == 1);
    3588
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3589
    1
      ccv_nnc_tensor_param_t output_params;
    3590
    1
      ccv_nnc_cmd_t reduce_norm2 = CMD_REDUCE_NORM2_FORWARD();
    3591
    1
      int i;
    3592
    2
      for (i = 0; i < self->count; i++1)
    3593
    1
        reduce_norm2.info.reduce.axis[i] = self->axis[i];
    3594
    1
      reduce_norm2.info.reduce.count = self->count;
    3595
    1
      ccv_nnc_hint_tensor_auto(reduce_norm2, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3596
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3597
    1
      ccv_nnc_graph_exec_symbol_new(graph, reduce_norm2, inputs, input_size, outputs, output_size, "reduce_norm2");
    3598
    1
    }
    3599
    3600
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_norm2_copy(const ccv_cnnp_model_t* const self, void* const context);
    3601
    3602
    static const ccv_cnnp_model_vtab_t ccv_cnnp_reduce_norm2_isa = {
    3603
      .build = _ccv_cnnp_reduce_norm2_build,
    3604
      .copy = _ccv_cnnp_reduce_norm2_copy,
    3605
    };
    3606
    3607
    ccv_cnnp_model_t* ccv_cnnp_reduce_norm2(const int* const axis, const int axis_count, const char* const name)
    3608
    1
    {
    3609
    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));
    3610
    1
      model_reduce_norm2->super.isa = &ccv_cnnp_reduce_norm2_isa;
    3611
    1
      model_reduce_norm2->super.input_size = 1;
    3612
    1
      model_reduce_norm2->super.outputs = &model_reduce_norm2->output;
    3613
    1
      model_reduce_norm2->super.output_size = 1;
    3614
    1
      ccv_cnnp_model_copy_name(&model_reduce_norm2->super, name);
    3615
    1
      assert(axis_count <= CCV_NNC_MAX_DIM_ALLOC);
    3616
    1
      int i;
    3617
    2
      for (i = 0; i < axis_count; i++1)
    3618
    1
        model_reduce_norm2->axis[i] = axis[i];
    3619
    1
      model_reduce_norm2->count = axis_count;
    3620
    1
      return (ccv_cnnp_model_t*)model_reduce_norm2;
    3621
    1
    }
    3622
    3623
    static ccv_cnnp_model_t* _ccv_cnnp_reduce_norm2_copy(const ccv_cnnp_model_t* const super, void* const context)
    3624
    0
    {
    3625
    0
      const ccv_cnnp_model_reduce_norm2_t* const self = (const ccv_cnnp_model_reduce_norm2_t*)super;
    3626
    0
      return ccv_cnnp_reduce_norm2(self->axis, self->count, self->super.name);
    3627
    0
    }
    3628
    3629
    // MARK - Argmax Layer
    3630
    3631
    typedef struct {
    3632
      ccv_cnnp_model_t super;
    3633
      int axis;
    3634
      ccv_nnc_tensor_symbol_t output;
    3635
    } ccv_cnnp_model_argmax_t;
    3636
    3637
    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)
    3638
    1
    {
    3639
    1
      const ccv_cnnp_model_argmax_t* const self = (const ccv_cnnp_model_argmax_t*)super;
    3640
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_argmax_build] -\n");
    3641
    1
      assert(input_size == 1);
    3642
    1
      assert(output_size == 1);
    3643
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3644
    1
      ccv_nnc_tensor_param_t output_params;
    3645
    1
      ccv_nnc_cmd_t argmax = CMD_ARGMAX_FORWARD();
    3646
    1
      argmax.info.reduce.axis[0] = self->axis;
    3647
    1
      argmax.info.reduce.count = 1;
    3648
    1
      ccv_nnc_hint_tensor_auto(argmax, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3649
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3650
    1
      ccv_nnc_graph_exec_symbol_new(graph, argmax, inputs, input_size, outputs, output_size, "argmax");
    3651
    1
    }
    3652
    3653
    static ccv_cnnp_model_t* _ccv_cnnp_argmax_copy(const ccv_cnnp_model_t* const self, void* const context);
    3654
    3655
    static const ccv_cnnp_model_vtab_t ccv_cnnp_argmax_isa = {
    3656
      .build = _ccv_cnnp_argmax_build,
    3657
      .copy = _ccv_cnnp_argmax_copy,
    3658
    };
    3659
    3660
    ccv_cnnp_model_t* ccv_cnnp_argmax(const int axis, const char* const name)
    3661
    1
    {
    3662
    1
      ccv_cnnp_model_argmax_t* const model_argmax = (ccv_cnnp_model_argmax_t*)cccalloc(1, sizeof(ccv_cnnp_model_argmax_t));
    3663
    1
      model_argmax->super.isa = &ccv_cnnp_argmax_isa;
    3664
    1
      model_argmax->super.input_size = 1;
    3665
    1
      model_argmax->super.outputs = &model_argmax->output;
    3666
    1
      model_argmax->super.output_size = 1;
    3667
    1
      ccv_cnnp_model_copy_name(&model_argmax->super, name);
    3668
    1
      model_argmax->axis = axis;
    3669
    1
      return (ccv_cnnp_model_t*)model_argmax;
    3670
    1
    }
    3671
    3672
    static ccv_cnnp_model_t* _ccv_cnnp_argmax_copy(const ccv_cnnp_model_t* const super, void* const context)
    3673
    0
    {
    3674
    0
      const ccv_cnnp_model_argmax_t* const self = (const ccv_cnnp_model_argmax_t*)super;
    3675
    0
      return ccv_cnnp_argmax(self->axis, self->super.name);
    3676
    0
    }
    3677
    3678
    // MARK - Argmin Layer
    3679
    3680
    typedef struct {
    3681
      ccv_cnnp_model_t super;
    3682
      int axis;
    3683
      ccv_nnc_tensor_symbol_t output;
    3684
    } ccv_cnnp_model_argmin_t;
    3685
    3686
    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)
    3687
    1
    {
    3688
    1
      const ccv_cnnp_model_argmin_t* const self = (const ccv_cnnp_model_argmin_t*)super;
    3689
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_argmin_build] -\n");
    3690
    1
      assert(input_size == 1);
    3691
    1
      assert(output_size == 1);
    3692
    1
      ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3693
    1
      ccv_nnc_tensor_param_t output_params;
    3694
    1
      ccv_nnc_cmd_t argmin = CMD_ARGMIN_FORWARD();
    3695
    1
      argmin.info.reduce.axis[0] = self->axis;
    3696
    1
      argmin.info.reduce.count = 1;
    3697
    1
      ccv_nnc_hint_tensor_auto(argmin, &input_params, 1, ccv_nnc_no_hint, &output_params, 1);
    3698
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3699
    1
      ccv_nnc_graph_exec_symbol_new(graph, argmin, inputs, input_size, outputs, output_size, "argmin");
    3700
    1
    }
    3701
    3702
    static ccv_cnnp_model_t* _ccv_cnnp_argmin_copy(const ccv_cnnp_model_t* const self, void* const context);
    3703
    3704
    static const ccv_cnnp_model_vtab_t ccv_cnnp_argmin_isa = {
    3705
      .build = _ccv_cnnp_argmin_build,
    3706
      .copy = _ccv_cnnp_argmin_copy,
    3707
    };
    3708
    3709
    ccv_cnnp_model_t* ccv_cnnp_argmin(const int axis, const char* const name)
    3710
    1
    {
    3711
    1
      ccv_cnnp_model_argmin_t* const model_argmin = (ccv_cnnp_model_argmin_t*)cccalloc(1, sizeof(ccv_cnnp_model_argmin_t));
    3712
    1
      model_argmin->super.isa = &ccv_cnnp_argmin_isa;
    3713
    1
      model_argmin->super.input_size = 1;
    3714
    1
      model_argmin->super.outputs = &model_argmin->output;
    3715
    1
      model_argmin->super.output_size = 1;
    3716
    1
      ccv_cnnp_model_copy_name(&model_argmin->super, name);
    3717
    1
      model_argmin->axis = axis;
    3718
    1
      return (ccv_cnnp_model_t*)model_argmin;
    3719
    1
    }
    3720
    3721
    static ccv_cnnp_model_t* _ccv_cnnp_argmin_copy(const ccv_cnnp_model_t* const super, void* const context)
    3722
    0
    {
    3723
    0
      const ccv_cnnp_model_argmin_t* const self = (const ccv_cnnp_model_argmin_t*)super;
    3724
    0
      return ccv_cnnp_argmin(self->axis, self->super.name);
    3725
    0
    }
    3726
    3727
    // MARK - Min Layer
    3728
    3729
    typedef struct {
    3730
      ccv_cnnp_model_t super;
    3731
      ccv_nnc_tensor_symbol_t output;
    3732
    } ccv_cnnp_model_min_t;
    3733
    3734
    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)
    3735
    1
    {
    3736
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_min_build] -\n");
    3737
    1
      assert(input_size == 2);
    3738
    1
      assert(output_size == 1);
    3739
    1
      ccv_nnc_tensor_param_t input_params[2];
    3740
    1
      int i;
    3741
    3
      for (i = 0; i < 2; i++2)
    3742
    2
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    3743
    1
      ccv_nnc_tensor_param_t output_params;
    3744
    1
      const ccv_nnc_cmd_t min = CMD_MIN_FORWARD();
    3745
    1
      ccv_nnc_hint_tensor_auto(min, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    3746
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3747
    1
      ccv_nnc_graph_exec_symbol_new(graph, min, inputs, input_size, outputs, output_size, "min");
    3748
    1
    }
    3749
    3750
    static ccv_cnnp_model_t* _ccv_cnnp_min_copy(const ccv_cnnp_model_t* const self, void* const context);
    3751
    3752
    static const ccv_cnnp_model_vtab_t ccv_cnnp_min_isa = {
    3753
      .build = _ccv_cnnp_min_build,
    3754
      .copy = _ccv_cnnp_min_copy,
    3755
    };
    3756
    3757
    ccv_cnnp_model_t* ccv_cnnp_min(const char* const name)
    3758
    1
    {
    3759
    1
      ccv_cnnp_model_min_t* const model_min = (ccv_cnnp_model_min_t*)cccalloc(1, sizeof(ccv_cnnp_model_min_t));
    3760
    1
      model_min->super.isa = &ccv_cnnp_min_isa;
    3761
    1
      model_min->super.input_size = 2;
    3762
    1
      model_min->super.outputs = &model_min->output;
    3763
    1
      model_min->super.output_size = 1;
    3764
    1
      ccv_cnnp_model_copy_name(&model_min->super, name);
    3765
    1
      return (ccv_cnnp_model_t*)model_min;
    3766
    1
    }
    3767
    3768
    static ccv_cnnp_model_t* _ccv_cnnp_min_copy(const ccv_cnnp_model_t* const super, void* const context)
    3769
    0
    {
    3770
    0
      const ccv_cnnp_model_min_t* const self = (const ccv_cnnp_model_min_t*)super;
    3771
    0
      return ccv_cnnp_min(self->super.name);
    3772
    0
    }
    3773
    3774
    // MARK - Max Layer
    3775
    3776
    typedef struct {
    3777
      ccv_cnnp_model_t super;
    3778
      ccv_nnc_tensor_symbol_t output;
    3779
    } ccv_cnnp_model_max_t;
    3780
    3781
    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)
    3782
    1
    {
    3783
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_max_build] -\n");
    3784
    1
      assert(input_size == 2);
    3785
    1
      assert(output_size == 1);
    3786
    1
      ccv_nnc_tensor_param_t input_params[2];
    3787
    1
      int i;
    3788
    3
      for (i = 0; i < 2; i++2)
    3789
    2
        input_params[i] = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    3790
    1
      ccv_nnc_tensor_param_t output_params;
    3791
    1
      const ccv_nnc_cmd_t max = CMD_MAX_FORWARD();
    3792
    1
      ccv_nnc_hint_tensor_auto(max, input_params, 2, ccv_nnc_no_hint, &output_params, 1);
    3793
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    3794
    1
      ccv_nnc_graph_exec_symbol_new(graph, max, inputs, input_size, outputs, output_size, "max");
    3795
    1
    }
    3796
    3797
    static ccv_cnnp_model_t* _ccv_cnnp_max_copy(const ccv_cnnp_model_t* const self, void* const context);
    3798
    3799
    static const ccv_cnnp_model_vtab_t ccv_cnnp_max_isa = {
    3800
      .build = _ccv_cnnp_max_build,
    3801
      .copy = _ccv_cnnp_max_copy,
    3802
    };
    3803
    3804
    ccv_cnnp_model_t* ccv_cnnp_max(const char* const name)
    3805
    1
    {
    3806
    1
      ccv_cnnp_model_max_t* const model_max = (ccv_cnnp_model_max_t*)cccalloc(1, sizeof(ccv_cnnp_model_max_t));
    3807
    1
      model_max->super.isa = &ccv_cnnp_max_isa;
    3808
    1
      model_max->super.input_size = 2;
    3809
    1
      model_max->super.outputs = &model_max->output;
    3810
    1
      model_max->super.output_size = 1;
    3811
    1
      ccv_cnnp_model_copy_name(&model_max->super, name);
    3812
    1
      return (ccv_cnnp_model_t*)model_max;
    3813
    1
    }
    3814
    3815
    static ccv_cnnp_model_t* _ccv_cnnp_max_copy(const ccv_cnnp_model_t* const super, void* const context)
    3816
    0
    {
    3817
    0
      const ccv_cnnp_model_max_t* const self = (const ccv_cnnp_model_max_t*)super;
    3818
    0
      return ccv_cnnp_max(self->super.name);
    3819
    0
    }
    3820
    3821
    // MARK - LSTM Layer
    3822
    3823
    typedef struct {
    3824
      ccv_cnnp_model_t super;
    3825
      int masked;
    3826
      ccv_nnc_tensor_symbol_t output;
    3827
      ccv_nnc_tensor_symbol_t weights;
    3828
      ccv_nnc_tensor_symbol_t reserves;
    3829
      ccv_nnc_cmd_param_t params;
    3830
      ccv_nnc_graph_exec_symbol_t lstm;
    3831
    } ccv_cnnp_model_lstm_t;
    3832
    3833
    static int _ccv_cnnp_lstm_weight_dim(int bidirectional, int num_layers, int input_size, int hidden_size, int proj_size, int bias)
    3834
    1
    {
    3835
    1
      const int D = !!bidirectional + 1;
    3836
    1
      if (hidden_size == proj_size)
    3837
    1
        return (num_layers * (bias ? 8 : 00) + (num_layers - 1) * (hidden_size * 4 * D + hidden_size * 4) + input_size * 4 + hidden_size * 4) * D;
    3838
    0
      else
    3839
    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;
    3840
    1
    }
    3841
    3842
    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)
    3843
    1
    {
    3844
    1
      ccv_cnnp_model_lstm_t* const self = (ccv_cnnp_model_lstm_t*)super;
    3845
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_lstm_build] -\n");
    3846
    1
      assert(input_size == self->super.input_size);
    3847
    1
      assert(output_size == 1);
    3848
    1
      const int proj_size = self->params.rnn.proj_size == 0 ? self->params.rnn.hidden_size : self->params.rnn.proj_size0;
    3849
    1
      ccv_nnc_tensor_param_t input_params[5];
    3850
    1
      input_params[0]= ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3851
    1
      if (input_size == 2)
    3852
    1
        input_params[1] = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    3853
    1
      input_params[4] = input_params[0];
    3854
    1
      memset(input_params[4].dim, 0, sizeof(input_params[4].dim));
    3855
    1
      const int x_nd = ccv_nnc_tensor_nd(input_params[0].dim);
    3856
    1
      const int feature_count = input_params[0].dim[x_nd - 1];
    3857
    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);
    3858
    1
      input_params[4].dim[1] = self->params.rnn.hidden_size;
    3859
    1
      const ccv_nnc_cmd_t lstm = ccv_nnc_cmd(CCV_NNC_LSTM_FORWARD, 0, self->params, 0);
    3860
    1
      ccv_nnc_tensor_param_t output_params[4];
    3861
    1
      ccv_nnc_hint_tensor_auto(lstm, input_params, 5, ccv_nnc_no_hint, output_params, 4);
    3862
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    3863
    1
      if (!self->weights.graph)
    3864
    1
        self->weights = ccv_nnc_tensor_symbol_new(graph, input_params[4], "weights");
    3865
    1
      assert(self->weights.graph == graph);
    3866
    1
      const ccv_nnc_tensor_symbol_t weights = ccv_cnnp_model_get_symbol(super, self->weights);
    3867
    1
      if (!self->reserves.graph)
    3868
    1
        self->reserves = ccv_nnc_tensor_symbol_new(graph, output_params[3], "reserves");
    3869
    1
      assert(self->reserves.graph == graph);
    3870
    1
      const ccv_nnc_tensor_symbol_t reserves = ccv_cnnp_model_get_symbol(super, self->reserves);
    3871
    1
      const ccv_nnc_tensor_symbol_t mask = input_size == 2 ? inputs[1] : NO_TENSOR_SYMBOL0;
    3872
    1
      self->lstm = ccv_nnc_graph_exec_symbol_new(graph, lstm, TENSOR_SYMBOL_LIST(inputs[0], mask, NO_TENSOR_SYMBOL, NO_TENSOR_SYMBOL, weights), TENSOR_SYMBOL_LIST(outputs[0], NO_TENSOR_SYMBOL, NO_TENSOR_SYMBOL, reserves), "lstm");
    3873
    1
    }
    3874
    3875
    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)
    3876
    1
    {
    3877
    1
      ccv_cnnp_model_lstm_t* const self = (ccv_cnnp_model_lstm_t*)super;
    3878
    1
      if (self->weights.graph)
    3879
    1
      {
    3880
    1
        const float stdv = 1.0 / sqrt(self->params.rnn.hidden_size);
    3881
    1
        initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-stdv, stdv), ccv_nnc_no_hint, 0, 0, self->weights);
    3882
    1
      }
    3883
    1
    }
    3884
    3885
    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)
    3886
    1
    {
    3887
    1
      ccv_cnnp_model_lstm_t* const self = (ccv_cnnp_model_lstm_t*)super;
    3888
    1
      if (self->weights.graph)
    3889
    1
        add_to_array(parameters, self->weights, is_trainable);
    3890
    1
    }
    3891
    3892
    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)
    3893
    2
    {
    3894
    2
      ccv_cnnp_model_lstm_t* const self = (ccv_cnnp_model_lstm_t*)super;
    3895
    2
      if (self->lstm.graph)
    3896
    2
      {
    3897
    2
        self->params.rnn.is_test = is_test;
    3898
    2
        updater(context, self->lstm, ccv_nnc_cmd(CCV_NNC_LSTM_FORWARD, 0, self->params, 0), ccv_nnc_no_hint);
    3899
    2
      }
    3900
    2
    }
    3901
    3902
    static ccv_cnnp_model_t* _ccv_cnnp_lstm_copy(const ccv_cnnp_model_t* const self, void* const context);
    3903
    3904
    static const ccv_cnnp_model_vtab_t ccv_cnnp_lstm_isa = {
    3905
      .build = _ccv_cnnp_lstm_build,
    3906
      .init_states = _ccv_cnnp_lstm_init_states,
    3907
      .add_to_parameter = _ccv_cnnp_lstm_add_to_parameter,
    3908
      .copy = _ccv_cnnp_lstm_copy,
    3909
      .set_is_test = _ccv_cnnp_lstm_set_is_test,
    3910
    };
    3911
    3912
    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)
    3913
    1
    {
    3914
    1
      ccv_cnnp_model_lstm_t* const model_lstm = (ccv_cnnp_model_lstm_t*)cccalloc(1, sizeof(ccv_cnnp_model_lstm_t));
    3915
    1
      model_lstm->super.isa = &ccv_cnnp_lstm_isa;
    3916
    1
      model_lstm->super.input_size = masked ? 2 : 10;
    3917
    1
      model_lstm->super.outputs = &model_lstm->output;
    3918
    1
      model_lstm->super.output_size = 1;
    3919
    1
      model_lstm->super.is_trainable = is_trainable;
    3920
    1
      ccv_cnnp_model_copy_name(&model_lstm->super, name);
    3921
    1
      model_lstm->masked = masked;
    3922
    1
      model_lstm->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    3923
    1
      model_lstm->weights.graph = 0;
    3924
    1
      model_lstm->params.rnn.hidden_size = hidden_size;
    3925
    1
      model_lstm->params.rnn.proj_size = proj_size;
    3926
    1
      model_lstm->params.rnn.num_layers = num_layers;
    3927
    1
      model_lstm->params.rnn.bias = bias;
    3928
    1
      model_lstm->params.rnn.batch_first = batch_first;
    3929
    1
      model_lstm->params.rnn.bidirectional = bidirectional;
    3930
    1
      model_lstm->params.rnn.dropout = dropout;
    3931
    1
      return (ccv_cnnp_model_t*)model_lstm;
    3932
    1
    }
    3933
    3934
    static ccv_cnnp_model_t* _ccv_cnnp_lstm_copy(const ccv_cnnp_model_t* const super, void* const context)
    3935
    0
    {
    3936
    0
      const ccv_cnnp_model_lstm_t* const self = (const ccv_cnnp_model_lstm_t*)super;
    3937
    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);
    3938
    0
    }
    3939
    3940
    /// MARK - Datatype conversion layer.
    3941
    3942
    typedef struct {
    3943
      ccv_cnnp_model_t super;
    3944
      ccv_nnc_tensor_symbol_t output;
    3945
      int datatype;
    3946
      int ref_to_last;
    3947
    } ccv_cnnp_model_datatype_conversion_t;
    3948
    3949
    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)
    3950
    2
    {
    3951
    2
      ccv_cnnp_model_datatype_conversion_t* const self = (ccv_cnnp_model_datatype_conversion_t*)super;
    3952
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_datatype_conversion_build] -\n");
    3953
    2
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    3954
    2
      if (self->ref_to_last)
    3955
    1
      {
    3956
    1
        assert(input_size > 1);
    3957
    1
        const ccv_nnc_tensor_param_t last_params = ccv_nnc_tensor_symbol_params(graph, inputs[input_size - 1]);
    3958
    1
        params.datatype = last_params.datatype;
    3959
    1
      } else
    3960
    1
        params.datatype = self->datatype;
    3961
    2
      assert(output_size == 1);
    3962
    2
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    3963
    2
      ccv_nnc_graph_exec_symbol_new(graph, CMD_DATATYPE_CONVERSION_FORWARD(), inputs, output_size /* intentional */, outputs, output_size, 0);
    3964
    2
    }
    3965
    3966
    static ccv_cnnp_model_t* _ccv_cnnp_datatype_conversion_copy(const ccv_cnnp_model_t* const self, void* const context);
    3967
    3968
    static const ccv_cnnp_model_vtab_t ccv_cnnp_datatype_conversion_isa = {
    3969
      .build = _ccv_cnnp_datatype_conversion_build,
    3970
      .copy = _ccv_cnnp_datatype_conversion_copy,
    3971
    };
    3972
    3973
    ccv_cnnp_model_t* ccv_cnnp_datatype_conversion(const int datatype, const int ref_to_last, const char* const name)
    3974
    2
    {
    3975
    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));
    3976
    2
      model_datatype_conversion->super.isa = &ccv_cnnp_datatype_conversion_isa;
    3977
    2
      model_datatype_conversion->super.input_size = 0;
    3978
    2
      model_datatype_conversion->super.outputs = &model_datatype_conversion->output;
    3979
    2
      model_datatype_conversion->super.output_size = 1;
    3980
    2
      model_datatype_conversion->datatype = datatype;
    3981
    2
      model_datatype_conversion->ref_to_last = ref_to_last;
    3982
    2
      ccv_cnnp_model_copy_name(&model_datatype_conversion->super, name);
    3983
    2
      return (ccv_cnnp_model_t*)model_datatype_conversion;
    3984
    2
    }
    3985
    3986
    static ccv_cnnp_model_t* _ccv_cnnp_datatype_conversion_copy(const ccv_cnnp_model_t* const super, void* const context)
    3987
    0
    {
    3988
    0
      ccv_cnnp_model_datatype_conversion_t* const self = (ccv_cnnp_model_datatype_conversion_t*)super;
    3989
    0
      return ccv_cnnp_datatype_conversion(self->datatype, self->ref_to_last, self->super.name);
    3990
    0
    }
    3991
    3992
    /// MARK - Clamp layer.
    3993
    3994
    typedef struct {
    3995
      ccv_cnnp_model_t super;
    3996
      ccv_nnc_tensor_symbol_t output;
    3997
      float min;
    3998
      float max;
    3999
    } ccv_cnnp_model_clamp_t;
    4000
    4001
    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)
    4002
    0
    {
    4003
    0
      ccv_cnnp_model_clamp_t* const self = (ccv_cnnp_model_clamp_t*)super;
    4004
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_clamp_build] -\n");
    4005
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4006
    0
      assert(output_size == 1);
    4007
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4008
    0
      ccv_nnc_graph_exec_symbol_new(graph, CMD_CLAMP_FORWARD(self->min, self->max), inputs, output_size /* intentional */, outputs, output_size, 0);
    4009
    0
    }
    4010
    4011
    static ccv_cnnp_model_t* _ccv_cnnp_clamp_copy(const ccv_cnnp_model_t* const self, void* const context);
    4012
    4013
    static const ccv_cnnp_model_vtab_t ccv_cnnp_clamp_isa = {
    4014
      .build = _ccv_cnnp_clamp_build,
    4015
      .copy = _ccv_cnnp_clamp_copy,
    4016
    };
    4017
    4018
    ccv_cnnp_model_t* ccv_cnnp_clamp(const float min, const float max, const char* const name)
    4019
    0
    {
    4020
    0
      ccv_cnnp_model_clamp_t* const model_clamp = (ccv_cnnp_model_clamp_t*)cccalloc(1, sizeof(ccv_cnnp_model_clamp_t));
    4021
    0
      model_clamp->super.isa = &ccv_cnnp_clamp_isa;
    4022
    0
      model_clamp->super.input_size = 0;
    4023
    0
      model_clamp->super.outputs = &model_clamp->output;
    4024
    0
      model_clamp->super.output_size = 1;
    4025
    0
      model_clamp->min = min;
    4026
    0
      model_clamp->max = max;
    4027
    0
      ccv_cnnp_model_copy_name(&model_clamp->super, name);
    4028
    0
      return (ccv_cnnp_model_t*)model_clamp;
    4029
    0
    }
    4030
    4031
    static ccv_cnnp_model_t* _ccv_cnnp_clamp_copy(const ccv_cnnp_model_t* const super, void* const context)
    4032
    0
    {
    4033
    0
      ccv_cnnp_model_clamp_t* const self = (ccv_cnnp_model_clamp_t*)super;
    4034
    0
      return ccv_cnnp_clamp(self->min, self->max, self->super.name);
    4035
    0
    }
    4036
    4037
    // MARK - Parameter Layer
    4038
    4039
    typedef struct {
    4040
      ccv_cnnp_model_t super;
    4041
      float init_bound;
    4042
      ccv_nnc_tensor_symbol_t weights;
    4043
      ccv_nnc_tensor_param_t weights_params;
    4044
      ccv_nnc_tensor_symbol_t output;
    4045
    } ccv_cnnp_model_parameter_t;
    4046
    4047
    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)
    4048
    1
    {
    4049
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_parameter_build] -\n");
    4050
    1
      assert(output_size == 1);
    4051
    1
      ccv_cnnp_model_parameter_t* const self = (ccv_cnnp_model_parameter_t*)super;
    4052
    1
      if (!self->weights.graph)
    4053
    1
        self->weights = ccv_nnc_tensor_symbol_new(graph, self->weights_params, "weights");
    4054
    1
      assert(self->weights.graph == graph);
    4055
    1
      outputs[0] = ccv_cnnp_model_get_symbol(super, self->weights);
    4056
    1
    }
    4057
    4058
    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)
    4059
    0
    {
    4060
    0
      ccv_cnnp_model_parameter_t* const self = (ccv_cnnp_model_parameter_t*)super;
    4061
    0
      if (self->init_bound > 0)
    4062
    0
        initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-self->init_bound, self->init_bound), ccv_nnc_no_hint, 0, 0, self->weights);
    4063
    0
      else
    4064
    0
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->weights);
    4065
    0
    }
    4066
    4067
    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)
    4068
    1
    {
    4069
    1
      ccv_cnnp_model_parameter_t* const self = (ccv_cnnp_model_parameter_t*)super;
    4070
    1
      add_to_array(parameters, self->weights, is_trainable);
    4071
    1
    }
    4072
    4073
    static ccv_cnnp_model_t* _ccv_cnnp_parameter_copy(const ccv_cnnp_model_t* const super, void* const context);
    4074
    4075
    static const ccv_cnnp_model_vtab_t ccv_cnnp_parameter_isa = {
    4076
      .build = _ccv_cnnp_parameter_build,
    4077
      .init_states = _ccv_cnnp_parameter_init_states,
    4078
      .add_to_parameter = _ccv_cnnp_parameter_add_to_parameter,
    4079
      .copy = _ccv_cnnp_parameter_copy,
    4080
    };
    4081
    4082
    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)
    4083
    1
    {
    4084
    1
      ccv_cnnp_model_parameter_t* const model_parameter = (ccv_cnnp_model_parameter_t*)cccalloc(1, sizeof(ccv_cnnp_model_parameter_t));
    4085
    1
      model_parameter->super.isa = &ccv_cnnp_parameter_isa;
    4086
    1
      model_parameter->super.input_size = 0;
    4087
    1
      model_parameter->super.outputs = &model_parameter->output;
    4088
    1
      model_parameter->super.output_size = 1;
    4089
    1
      model_parameter->super.is_trainable = is_trainable;
    4090
    1
      ccv_cnnp_model_copy_name(&model_parameter->super, name);
    4091
    1
      model_parameter->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    4092
    1
      model_parameter->weights.graph = 0;
    4093
    1
      model_parameter->weights_params = params;
    4094
    1
      return (ccv_cnnp_model_t*)model_parameter;
    4095
    1
    }
    4096
    4097
    static ccv_cnnp_model_t* _ccv_cnnp_parameter_copy(const ccv_cnnp_model_t* const super, void* const context)
    4098
    0
    {
    4099
    0
      const ccv_cnnp_model_parameter_t* const self = (const ccv_cnnp_model_parameter_t*)super;
    4100
    0
      return ccv_cnnp_parameter(self->weights_params, self->init_bound, self->super.is_trainable, self->super.name);
    4101
    0
    }
    4102
    4103
    // MARK - Scalar Layer
    4104
    4105
    typedef struct {
    4106
      ccv_cnnp_model_t super;
    4107
      int type;
    4108
      int format;
    4109
      int datatype;
    4110
      float value;
    4111
      ccv_nnc_tensor_symbol_t output;
    4112
    } ccv_cnnp_model_scalar_t;
    4113
    4114
    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)
    4115
    2
    {
    4116
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_scalar_build] -\n");
    4117
    2
      assert(output_size == 1);
    4118
    2
      ccv_cnnp_model_scalar_t* const self = (ccv_cnnp_model_scalar_t*)super;
    4119
    2
      ccv_nnc_tensor_param_t params = {
    4120
    2
        .type = self->type,
    4121
    2
        .format = self->format,
    4122
    2
        .datatype = self->datatype,
    4123
    2
        .dim = {
    4124
    2
          1
    4125
    2
        }
    4126
    2
      };
    4127
    2
      if (input_size > 0)
    4128
    1
      {
    4129
    1
        ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4130
    1
        params.type = input_params.type;
    4131
    1
        params.format = input_params.format;
    4132
    1
        params.datatype = input_params.datatype;
    4133
    1
      }
    4134
    2
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4135
    2
      ccv_nnc_graph_exec_symbol_new(graph, CMD_SET_FORWARD(self->value), 0, 0, outputs, 1, 0);
    4136
    2
    }
    4137
    4138
    static ccv_cnnp_model_t* _ccv_cnnp_scalar_copy(const ccv_cnnp_model_t* const super, void* const context);
    4139
    4140
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scalar_isa = {
    4141
      .build = _ccv_cnnp_scalar_build,
    4142
      .copy = _ccv_cnnp_scalar_copy,
    4143
    };
    4144
    4145
    ccv_cnnp_model_t* ccv_cnnp_scalar(const int type, const int format, const int datatype, const float value, const char* const name)
    4146
    2
    {
    4147
    2
      ccv_cnnp_model_scalar_t* const model_scalar = (ccv_cnnp_model_scalar_t*)cccalloc(1, sizeof(ccv_cnnp_model_scalar_t));
    4148
    2
      model_scalar->super.isa = &ccv_cnnp_scalar_isa;
    4149
    2
      model_scalar->super.input_size = 0;
    4150
    2
      model_scalar->super.outputs = &model_scalar->output;
    4151
    2
      model_scalar->super.output_size = 1;
    4152
    2
      ccv_cnnp_model_copy_name(&model_scalar->super, name);
    4153
    2
      model_scalar->type = type;
    4154
    2
      model_scalar->format = format;
    4155
    2
      model_scalar->datatype = datatype;
    4156
    2
      model_scalar->value = value;
    4157
    2
      return (ccv_cnnp_model_t*)model_scalar;
    4158
    2
    }
    4159
    4160
    static ccv_cnnp_model_t* _ccv_cnnp_scalar_copy(const ccv_cnnp_model_t* const super, void* const context)
    4161
    0
    {
    4162
    0
      const ccv_cnnp_model_scalar_t* const self = (const ccv_cnnp_model_scalar_t*)super;
    4163
    0
      return ccv_cnnp_scalar(self->type, self->format, self->datatype, self->value, self->super.name);
    4164
    0
    }
    4165
    4166
    // MARK - Variable Layer
    4167
    4168
    typedef struct {
    4169
      ccv_cnnp_model_t super;
    4170
      ccv_nnc_tensor_param_t params;
    4171
      ccv_nnc_tensor_symbol_t output;
    4172
    } ccv_cnnp_model_variable_t;
    4173
    4174
    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)
    4175
    1
    {
    4176
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_variable_build] -\n");
    4177
    1
      assert(input_size == 0);
    4178
    1
      assert(output_size == 1);
    4179
    1
      ccv_cnnp_model_variable_t* const self = (ccv_cnnp_model_variable_t*)super;
    4180
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, self->params, 0);
    4181
    1
    }
    4182
    4183
    static ccv_cnnp_model_t* _ccv_cnnp_variable_copy(const ccv_cnnp_model_t* const super, void* const context);
    4184
    4185
    static const ccv_cnnp_model_vtab_t ccv_cnnp_variable_isa = {
    4186
      .build = _ccv_cnnp_variable_build,
    4187
      .copy = _ccv_cnnp_variable_copy,
    4188
    };
    4189
    4190
    ccv_cnnp_model_t* ccv_cnnp_variable(const ccv_nnc_tensor_param_t params, const char* const name)
    4191
    1
    {
    4192
    1
      ccv_cnnp_model_variable_t* const model_variable = (ccv_cnnp_model_variable_t*)cccalloc(1, sizeof(ccv_cnnp_model_variable_t));
    4193
    1
      model_variable->super.isa = &ccv_cnnp_variable_isa;
    4194
    1
      model_variable->super.input_size = 0;
    4195
    1
      model_variable->super.outputs = &model_variable->output;
    4196
    1
      model_variable->super.output_size = 1;
    4197
    1
      ccv_cnnp_model_copy_name(&model_variable->super, name);
    4198
    1
      model_variable->params = params;
    4199
    1
      return (ccv_cnnp_model_t*)model_variable;
    4200
    1
    }
    4201
    4202
    static ccv_cnnp_model_t* _ccv_cnnp_variable_copy(const ccv_cnnp_model_t* const super, void* const context)
    4203
    0
    {
    4204
    0
      const ccv_cnnp_model_variable_t* const self = (const ccv_cnnp_model_variable_t*)super;
    4205
    0
      return ccv_cnnp_variable(self->params, self->super.name);
    4206
    0
    }
    4207
    4208
    // MARK - Send Layer
    4209
    4210
    typedef struct {
    4211
      ccv_cnnp_model_t super;
    4212
      int device_id;
    4213
      ccv_nnc_tensor_symbol_t output;
    4214
    } ccv_cnnp_model_send_t;
    4215
    4216
    static void _ccv_cnnp_send_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)
    4217
    16
    {
    4218
    16
      ccv_cnnp_model_send_t* const self = (ccv_cnnp_model_send_t*)super;
    4219
    16
      PRINT(CCV_CLI_VERBOSE, "[cnnp_send_build] - device_id: %d\n", self->device_id);
    4220
    16
      assert(input_size == 1);
    4221
    16
      assert(output_size == 1);
    4222
    16
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4223
    16
      params.type = (params.type & ~0x3) | CCV_TENSOR_GPU_MEMORY;
    4224
    16
      CCV_TENSOR_SET_DEVICE_ID(params.type, self->device_id);
    4225
    16
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4226
    16
      ccv_nnc_graph_exec_symbol_new(graph, CMD_DATA_TRANSFER_FORWARD(), inputs, 1, outputs, 1, "send");
    4227
    16
    }
    4228
    4229
    static ccv_cnnp_model_t* _ccv_cnnp_send_copy(const ccv_cnnp_model_t* const super, void* const context);
    4230
    4231
    static const ccv_cnnp_model_vtab_t ccv_cnnp_send_isa = {
    4232
      .build = _ccv_cnnp_send_build,
    4233
      .copy = _ccv_cnnp_send_copy,
    4234
    };
    4235
    4236
    ccv_cnnp_model_t* ccv_cnnp_send(const int device_id, const char* const name)
    4237
    16
    {
    4238
    16
      assert(device_id >= 0);
    4239
    16
      ccv_cnnp_model_send_t* const model_send = (ccv_cnnp_model_send_t*)cccalloc(1, sizeof(ccv_cnnp_model_send_t));
    4240
    16
      model_send->super.isa = &ccv_cnnp_send_isa;
    4241
    16
      model_send->super.input_size = 1;
    4242
    16
      model_send->super.outputs = &model_send->output;
    4243
    16
      model_send->super.output_size = 1;
    4244
    16
      ccv_cnnp_model_copy_name(&model_send->super, name);
    4245
    16
      model_send->device_id = device_id;
    4246
    16
      return (ccv_cnnp_model_t*)model_send;
    4247
    16
    }
    4248
    4249
    static ccv_cnnp_model_t* _ccv_cnnp_send_copy(const ccv_cnnp_model_t* const super, void* const context)
    4250
    0
    {
    4251
    0
      const ccv_cnnp_model_send_t* const self = (const ccv_cnnp_model_send_t*)super;
    4252
    0
      return ccv_cnnp_send(self->device_id, self->super.name);
    4253
    0
    }
    4254
    4255
    // MARK - Move Layer
    4256
    4257
    typedef struct {
    4258
      ccv_cnnp_model_t super;
    4259
      ccv_nnc_tensor_symbol_t output;
    4260
    } ccv_cnnp_model_move_t;
    4261
    4262
    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)
    4263
    3
    {
    4264
    3
      PRINT(CCV_CLI_VERBOSE, "[cnnp_move_build] -\n");
    4265
    3
      assert(input_size == 2);
    4266
    3
      assert(output_size == 1);
    4267
    3
      outputs[0] = inputs[1];
    4268
    3
      ccv_nnc_graph_exec_symbol_new(graph, CMD_FORMAT_TRANSFORM_FORWARD(), inputs, 1, outputs, 1, "move");
    4269
    3
    }
    4270
    4271
    static ccv_cnnp_model_t* _ccv_cnnp_move_copy(const ccv_cnnp_model_t* const super, void* const context);
    4272
    4273
    static const ccv_cnnp_model_vtab_t ccv_cnnp_move_isa = {
    4274
      .build = _ccv_cnnp_move_build,
    4275
      .copy = _ccv_cnnp_move_copy,
    4276
    };
    4277
    4278
    ccv_cnnp_model_t* ccv_cnnp_move(const char* const name)
    4279
    3
    {
    4280
    3
      ccv_cnnp_model_move_t* const model_move = (ccv_cnnp_model_move_t*)cccalloc(1, sizeof(ccv_cnnp_model_move_t));
    4281
    3
      model_move->super.isa = &ccv_cnnp_move_isa;
    4282
    3
      model_move->super.input_size = 2;
    4283
    3
      model_move->super.outputs = &model_move->output;
    4284
    3
      model_move->super.output_size = 1;
    4285
    3
      ccv_cnnp_model_copy_name(&model_move->super, name);
    4286
    3
      return (ccv_cnnp_model_t*)model_move;
    4287
    3
    }
    4288
    4289
    static ccv_cnnp_model_t* _ccv_cnnp_move_copy(const ccv_cnnp_model_t* const super, void* const context)
    4290
    0
    {
    4291
    0
      const ccv_cnnp_model_move_t* const self = (const ccv_cnnp_model_move_t*)super;
    4292
    0
      return ccv_cnnp_move(self->super.name);
    4293
    0
    }
    4294
    4295
    // MARK - "Making" Contiguous Layer
    4296
    4297
    typedef struct {
    4298
      ccv_cnnp_model_t super;
    4299
      ccv_nnc_tensor_symbol_t output;
    4300
    } ccv_cnnp_model_contiguous_t;
    4301
    4302
    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)
    4303
    5
    {
    4304
    5
      PRINT(CCV_CLI_VERBOSE, "[cnnp_contiguous_build] -\n");
    4305
    5
      assert(input_size == 1);
    4306
    5
      assert(output_size == 1);
    4307
    5
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4308
    5
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    4309
    5
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    4310
    0
      {
    4311
    0
        outputs[0] = inputs[0];
    4312
    0
        return;
    4313
    0
      }
    4314
      // Otherwise, we need to check its stride to know if it is contiguous.
    4315
    5
      int old_stride[CCV_NNC_MAX_DIM_ALLOC];
    4316
    5
      ccv_nnc_tensor_symbol_alias_params(graph, inputs[0], 0, old_stride);
    4317
      // We identify permute by checking if the stride is not in descending order.
    4318
      // This also covered "permute" through reshape, rather than using ccv_cnnp_permute directly.
    4319
    5
      if (ccv_nnc_is_tensor_stride_packed(old_stride, params.dim))
    4320
    2
      {
    4321
    2
        outputs[0] = inputs[0];
    4322
    2
        return;
    4323
    2
      }
    4324
    3
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4325
    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");
    4326
    3
      ccv_nnc_graph_exec_symbol_set_flags(graph, make_contiguous, CCV_NNC_GRAPH_EXEC_DISABLE_OPT);
    4327
    3
    }
    4328
    4329
    static ccv_cnnp_model_t* _ccv_cnnp_contiguous_copy(const ccv_cnnp_model_t* const super, void* const context);
    4330
    4331
    static const ccv_cnnp_model_vtab_t ccv_cnnp_contiguous_isa = {
    4332
      .build = _ccv_cnnp_contiguous_build,
    4333
      .copy = _ccv_cnnp_contiguous_copy,
    4334
    };
    4335
    4336
    ccv_cnnp_model_t* ccv_cnnp_contiguous(const char* const name)
    4337
    5
    {
    4338
    5
      ccv_cnnp_model_contiguous_t* const model_contiguous = (ccv_cnnp_model_contiguous_t*)cccalloc(1, sizeof(ccv_cnnp_model_contiguous_t));
    4339
    5
      model_contiguous->super.isa = &ccv_cnnp_contiguous_isa;
    4340
    5
      model_contiguous->super.input_size = 1;
    4341
    5
      model_contiguous->super.outputs = &model_contiguous->output;
    4342
    5
      model_contiguous->super.output_size = 1;
    4343
    5
      ccv_cnnp_model_copy_name(&model_contiguous->super, name);
    4344
    5
      return (ccv_cnnp_model_t*)model_contiguous;
    4345
    5
    }
    4346
    4347
    static ccv_cnnp_model_t* _ccv_cnnp_contiguous_copy(const ccv_cnnp_model_t* const super, void* const context)
    4348
    0
    {
    4349
    0
      const ccv_cnnp_model_contiguous_t* const self = (const ccv_cnnp_model_contiguous_t*)super;
    4350
    0
      return ccv_cnnp_contiguous(self->super.name);
    4351
    0
    }
    4352
    4353
    // MARK - "Making" Copy Layer
    4354
    4355
    typedef struct {
    4356
      ccv_cnnp_model_t super;
    4357
      ccv_nnc_tensor_symbol_t output;
    4358
    } ccv_cnnp_model_copy_t;
    4359
    4360
    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)
    4361
    0
    {
    4362
    0
      PRINT(CCV_CLI_VERBOSE, "[cnnp_copy_build] -\n");
    4363
    0
      assert(input_size == 1);
    4364
    0
      assert(output_size == 1);
    4365
    0
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4366
    0
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    4367
    0
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    4368
    0
      {
    4369
    0
        outputs[0] = inputs[0];
    4370
    0
        return;
    4371
    0
      }
    4372
    0
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4373
    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");
    4374
    0
      ccv_nnc_graph_exec_symbol_set_flags(graph, make_contiguous, CCV_NNC_GRAPH_EXEC_DISABLE_OPT);
    4375
    0
    }
    4376
    4377
    static ccv_cnnp_model_t* _ccv_cnnp_copy_copy(const ccv_cnnp_model_t* const super, void* const context);
    4378
    4379
    static const ccv_cnnp_model_vtab_t ccv_cnnp_copy_isa = {
    4380
      .build = _ccv_cnnp_copy_build,
    4381
      .copy = _ccv_cnnp_copy_copy,
    4382
    };
    4383
    4384
    ccv_cnnp_model_t* ccv_cnnp_copy(const char* const name)
    4385
    0
    {
    4386
    0
      ccv_cnnp_model_copy_t* const model_copy = (ccv_cnnp_model_copy_t*)cccalloc(1, sizeof(ccv_cnnp_model_copy_t));
    4387
    0
      model_copy->super.isa = &ccv_cnnp_copy_isa;
    4388
    0
      model_copy->super.input_size = 1;
    4389
    0
      model_copy->super.outputs = &model_copy->output;
    4390
    0
      model_copy->super.output_size = 1;
    4391
    0
      ccv_cnnp_model_copy_name(&model_copy->super, name);
    4392
    0
      return (ccv_cnnp_model_t*)model_copy;
    4393
    0
    }
    4394
    4395
    static ccv_cnnp_model_t* _ccv_cnnp_copy_copy(const ccv_cnnp_model_t* const super, void* const context)
    4396
    0
    {
    4397
    0
      const ccv_cnnp_model_copy_t* const self = (const ccv_cnnp_model_copy_t*)super;
    4398
    0
      return ccv_cnnp_copy(self->super.name);
    4399
    0
    }
    4400
    4401
    // MARK - All-To-All Layer
    4402
    4403
    typedef struct {
    4404
      ccv_cnnp_model_t super;
    4405
      int axis;
    4406
      ccv_nnc_tensor_symbol_t outputs[1];
    4407
    } ccv_cnnp_model_all_to_all_t;
    4408
    4409
    static void _ccv_cnnp_all_to_all_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)
    4410
    2
    {
    4411
    2
      ccv_cnnp_model_all_to_all_t* const self = (ccv_cnnp_model_all_to_all_t*)super;
    4412
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_all_to_all_build] - axis: %d\n", self->axis);
    4413
    2
      assert(input_size == self->super.input_size);
    4414
    2
      assert(output_size == self->super.output_size);
    4415
    2
      assert(input_size == output_size);
    4416
    2
      assert(input_size > 0);
    4417
    2
      const ccv_nnc_tensor_param_t input_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4418
    2
      const int nd = ccv_nnc_tensor_nd(input_params.dim);
    4419
    2
      assert(self->axis >= 0 && self->axis < nd);
    4420
    2
      assert(input_params.dim[self->axis] % input_size == 0);
    4421
    2
      int i;
    4422
    10
      for (i = 0; i < input_size; i++8)
    4423
    8
      {
    4424
    8
        const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[i]);
    4425
    8
        assert(params.format == input_params.format);
    4426
    8
        assert(params.datatype == input_params.datatype);
    4427
    8
        assert(CCV_TENSOR_GET_MEMORY(params.type) == CCV_TENSOR_GET_MEMORY(input_params.type));
    4428
    8
        assert(memcmp(params.dim, input_params.dim, sizeof(input_params.dim)) == 0);
    4429
    8
        outputs[i] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4430
    8
      }
    4431
    2
      ccv_nnc_graph_exec_symbol_new(graph, CMD_COMM_ALL_TO_ALL_FORWARD(self->axis), inputs, input_size, outputs, output_size, "all_to_all");
    4432
    2
    }
    4433
    4434
    static ccv_cnnp_model_t* _ccv_cnnp_all_to_all_copy(const ccv_cnnp_model_t* const super, void* const context);
    4435
    4436
    static const ccv_cnnp_model_vtab_t ccv_cnnp_all_to_all_isa = {
    4437
      .build = _ccv_cnnp_all_to_all_build,
    4438
      .copy = _ccv_cnnp_all_to_all_copy,
    4439
    };
    4440
    4441
    ccv_cnnp_model_t* ccv_cnnp_all_to_all(const int count, const int axis, const char* const name)
    4442
    2
    {
    4443
    2
      assert(count > 0);
    4444
    2
      assert(axis >= 0);
    4445
    2
      ccv_cnnp_model_all_to_all_t* const model_all_to_all = (ccv_cnnp_model_all_to_all_t*)cccalloc(1, sizeof(ccv_cnnp_model_all_to_all_t) + sizeof(ccv_nnc_tensor_symbol_t) * (count - 1));
    4446
    2
      model_all_to_all->super.isa = &ccv_cnnp_all_to_all_isa;
    4447
    2
      model_all_to_all->super.input_size = count;
    4448
    2
      model_all_to_all->super.outputs = model_all_to_all->outputs;
    4449
    2
      model_all_to_all->super.output_size = count;
    4450
    2
      model_all_to_all->axis = axis;
    4451
    2
      ccv_cnnp_model_copy_name(&model_all_to_all->super, name);
    4452
    2
      return (ccv_cnnp_model_t*)model_all_to_all;
    4453
    2
    }
    4454
    4455
    static ccv_cnnp_model_t* _ccv_cnnp_all_to_all_copy(const ccv_cnnp_model_t* const super, void* const context)
    4456
    0
    {
    4457
    0
      const ccv_cnnp_model_all_to_all_t* const self = (const ccv_cnnp_model_all_to_all_t*)super;
    4458
    0
      return ccv_cnnp_all_to_all(self->super.output_size, self->axis, self->super.name);
    4459
    0
    }
    4460
    4461
    // MARK - Scaled-Dot Product Attention Layer
    4462
    4463
    typedef struct {
    4464
      ccv_cnnp_model_t super;
    4465
      ccv_nnc_tensor_symbol_t output;
    4466
      ccv_nnc_tensor_symbol_t weights;
    4467
      ccv_nnc_tensor_symbol_t bias;
    4468
      float scale;
    4469
      int is_causal;
    4470
      int has_attn_mask;
    4471
      int is_varlen;
    4472
      int max_seqlen_q;
    4473
      int max_seqlen_kv;
    4474
      int flags;
    4475
      int fused_unify_head_weights;
    4476
      int no_bias;
    4477
    } ccv_cnnp_model_scaled_dot_product_attention_t;
    4478
    4479
    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)
    4480
    4
    {
    4481
    4
      PRINT(CCV_CLI_VERBOSE, "[cnnp_scaled_dot_product_attention_build] -\n");
    4482
    4
      assert(output_size == 1);
    4483
    4
      ccv_cnnp_model_scaled_dot_product_attention_t* const self = (ccv_cnnp_model_scaled_dot_product_attention_t*)super;
    4484
    4
      assert(input_size == (self->is_varlen ? 5 : (self->has_attn_mask ? 4 : 3)));
    4485
    4
      assert(!self->is_varlen || !self->has_attn_mask);
    4486
    4
      assert(!self->is_varlen || !self->fused_unify_head_weights);
    4487
    4
      const ccv_nnc_tensor_param_t q_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4488
    4
      const ccv_nnc_tensor_param_t k_params = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    4489
    4
      const ccv_nnc_tensor_param_t v_params = ccv_nnc_tensor_symbol_params(graph, inputs[2]);
    4490
    4
      const ccv_nnc_tensor_param_t q_seq_offsets_params = self->is_varlen ? ccv_nnc_tensor_symbol_params(graph, inputs[3])1 : (ccv_nnc_tensor_param_t){}3;
    4491
    4
      const ccv_nnc_tensor_param_t kv_seq_offsets_params = self->is_varlen ? ccv_nnc_tensor_symbol_params(graph, inputs[4])1 : (ccv_nnc_tensor_param_t){}3;
    4492
    4
      const int v_nd = ccv_nnc_tensor_nd(v_params.dim);
    4493
    4
      assert(v_nd == 3 || v_nd == 4);
    4494
    4
      const int hEv = (v_nd == 3 ? 10 : v_params.dim[2]) * v_params.dim[v_nd - 1];
    4495
    4
      ccv_nnc_tensor_param_t weights_params = q_params;
    4496
    4
      memset(weights_params.dim, 0, sizeof(weights_params.dim));
    4497
    4
      weights_params.dim[0] = hEv;
    4498
    4
      weights_params.dim[1] = hEv;
    4499
    4
      ccv_nnc_tensor_param_t bias_params = q_params;
    4500
    4
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    4501
    4
      bias_params.dim[0] = hEv;
    4502
    4
      ccv_nnc_cmd_t cmd = {0};
    4503
    4
      cmd.cmd = CCV_NNC_SCALED_DOT_PRODUCT_ATTENTION_FORWARD;
    4504
    4
      cmd.info.scaled_dot_product_attention.scale = self->scale;
    4505
    4
      cmd.info.scaled_dot_product_attention.is_causal = self->is_causal;
    4506
    4
      cmd.info.scaled_dot_product_attention.is_varlen = self->is_varlen;
    4507
    4
      cmd.info.scaled_dot_product_attention.max_seqlen_q = self->max_seqlen_q;
    4508
    4
      cmd.info.scaled_dot_product_attention.max_seqlen_kv = self->max_seqlen_kv;
    4509
    4
      cmd.info.scaled_dot_product_attention.flags = self->flags;
    4510
    4
      ccv_nnc_tensor_param_t output_params[3];
    4511
    4
      ccv_nnc_tensor_symbol_t output;
    4512
    4
      ccv_nnc_tensor_symbol_t saved_softmax_lse;
    4513
    4
      ccv_nnc_tensor_symbol_t saved_v_proj = NO_TENSOR_SYMBOL;
    4514
    4
      ccv_nnc_tensor_symbol_t attn_mask = NO_TENSOR_SYMBOL;
    4515
    4
      ccv_nnc_tensor_symbol_t weights = NO_TENSOR_SYMBOL;
    4516
    4
      ccv_nnc_tensor_symbol_t bias = NO_TENSOR_SYMBOL;
    4517
    4
      if (self->has_attn_mask)
    4518
    1
        attn_mask = inputs[3];
    4519
    4
      if (self->is_varlen)
    4520
    1
      {
    4521
    1
        ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    4522
    1
            q_params,
    4523
    1
            k_params,
    4524
    1
            v_params,
    4525
    1
            (ccv_nnc_tensor_param_t){},
    4526
    1
            (ccv_nnc_tensor_param_t){},
    4527
    1
            (ccv_nnc_tensor_param_t){},
    4528
    1
            q_seq_offsets_params,
    4529
    1
            kv_seq_offsets_params,
    4530
    1
          }, 8, ccv_nnc_no_hint, output_params, 2);
    4531
    1
        output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    4532
    1
        saved_softmax_lse = ccv_nnc_tensor_symbol_new(graph, output_params[1], 0);
    4533
    1
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], inputs[1], inputs[2], NO_TENSOR_SYMBOL, NO_TENSOR_SYMBOL, NO_TENSOR_SYMBOL, inputs[3], inputs[4]), TENSOR_SYMBOL_LIST(output, saved_softmax_lse, saved_v_proj), "scaled_dot_product_attention");
    4534
    3
      } else if (self->fused_unify_head_weights)
    4535
    1
      {
    4536
    1
        if (!self->weights.graph)
    4537
    1
          self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    4538
    1
        assert(self->weights.graph == graph);
    4539
    1
        weights = ccv_cnnp_model_get_symbol(super, self->weights);
    4540
    1
        if (!self->no_bias)
    4541
    1
        {
    4542
    1
          if (!self->bias.graph)
    4543
    1
            self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    4544
    1
          assert(self->bias.graph == graph);
    4545
    1
          bias = ccv_cnnp_model_get_symbol(super, self->bias);
    4546
    1
        }
    4547
    1
        ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    4548
    1
            q_params,
    4549
    1
            k_params,
    4550
    1
            v_params,
    4551
    1
            (ccv_nnc_tensor_param_t){},
    4552
    1
            weights_params,
    4553
    1
            bias_params,
    4554
    1
          }, 6, ccv_nnc_no_hint, output_params, 3);
    4555
    1
        output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    4556
    1
        saved_softmax_lse = ccv_nnc_tensor_symbol_new(graph, output_params[1], 0);
    4557
    1
        saved_v_proj = ccv_nnc_tensor_symbol_new(graph, output_params[2], 0);
    4558
    1
        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");
    4559
    2
      } else {
    4560
    2
        ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    4561
    2
            q_params,
    4562
    2
            k_params,
    4563
    2
            v_params,
    4564
    2
          }, 3, ccv_nnc_no_hint, output_params, 2);
    4565
    2
        output = ccv_nnc_tensor_symbol_new(graph, output_params[0], 0);
    4566
    2
        saved_softmax_lse = ccv_nnc_tensor_symbol_new(graph, output_params[1], 0);
    4567
    2
        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");
    4568
    2
      }
    4569
    4
      outputs[0] = output;
    4570
    4
    }
    4571
    4572
    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)
    4573
    0
    {
    4574
    0
      ccv_cnnp_model_scaled_dot_product_attention_t* const self = (ccv_cnnp_model_scaled_dot_product_attention_t*)super;
    4575
    0
      if (self->weights.graph)
    4576
    0
      {
    4577
    0
        assert(self->fused_unify_head_weights);
    4578
    0
        const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    4579
    0
        const int c = weight_params.dim[1];
    4580
    0
        const float std = sqrtf(2) / sqrtf(c);
    4581
    0
        const float bound = sqrtf(3) * std;
    4582
    0
        initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    4583
    0
        if (self->bias.graph)
    4584
    0
          initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    4585
    0
      }
    4586
    0
    }
    4587
    4588
    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)
    4589
    1
    {
    4590
    1
      ccv_cnnp_model_scaled_dot_product_attention_t* const self = (ccv_cnnp_model_scaled_dot_product_attention_t*)super;
    4591
    1
      if (self->weights.graph)
    4592
    1
      {
    4593
    1
        assert(self->fused_unify_head_weights);
    4594
    1
        add_to_array(parameters, self->weights, is_trainable);
    4595
    1
        if (self->bias.graph)
    4596
    1
          add_to_array(parameters, self->bias, is_trainable);
    4597
    1
      }
    4598
    1
    }
    4599
    4600
    static ccv_cnnp_model_t* _ccv_cnnp_scaled_dot_product_attention_copy(const ccv_cnnp_model_t* const super, void* const context);
    4601
    4602
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scaled_dot_product_attention_isa = {
    4603
      .build = _ccv_cnnp_scaled_dot_product_attention_build,
    4604
      .copy = _ccv_cnnp_scaled_dot_product_attention_copy,
    4605
    };
    4606
    4607
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scaled_dot_product_attention_fused_isa = {
    4608
      .build = _ccv_cnnp_scaled_dot_product_attention_build,
    4609
      .init_states = _ccv_cnnp_scaled_dot_product_attention_init_states,
    4610
      .add_to_parameter = _ccv_cnnp_scaled_dot_product_attention_add_to_parameter,
    4611
      .copy = _ccv_cnnp_scaled_dot_product_attention_copy,
    4612
    };
    4613
    4614
    ccv_cnnp_model_t* ccv_cnnp_scaled_dot_product_attention(const float scale, const int is_causal, const int has_attn_mask, const int is_varlen, const int max_seqlen_q, const int max_seqlen_kv, const int flags, const int fused_unify_head_weights, const int no_bias, const int is_trainable, const char* const name)
    4615
    4
    {
    4616
    4
      assert(!is_varlen || !has_attn_mask);
    4617
    4
      assert(!is_varlen || !fused_unify_head_weights);
    4618
    4
      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));
    4619
    4
      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_isa3;
    4620
    4
      model_scaled_dot_product_attention->super.input_size = is_varlen ? 51 : (3has_attn_mask3 ? 41 : 32);
    4621
    4
      model_scaled_dot_product_attention->super.outputs = &model_scaled_dot_product_attention->output;
    4622
    4
      model_scaled_dot_product_attention->super.output_size = 1;
    4623
    4
      model_scaled_dot_product_attention->super.is_trainable = is_trainable;
    4624
    4
      ccv_cnnp_model_copy_name(&model_scaled_dot_product_attention->super, name);
    4625
    4
      model_scaled_dot_product_attention->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    4626
    4
      model_scaled_dot_product_attention->weights.graph = 0;
    4627
    4
      model_scaled_dot_product_attention->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    4628
    4
      model_scaled_dot_product_attention->bias.graph = 0;
    4629
    4
      model_scaled_dot_product_attention->scale = scale;
    4630
    4
      model_scaled_dot_product_attention->is_causal = is_causal;
    4631
    4
      model_scaled_dot_product_attention->has_attn_mask = has_attn_mask;
    4632
    4
      model_scaled_dot_product_attention->is_varlen = is_varlen;
    4633
    4
      model_scaled_dot_product_attention->max_seqlen_q = max_seqlen_q;
    4634
    4
      model_scaled_dot_product_attention->max_seqlen_kv = max_seqlen_kv;
    4635
    4
      model_scaled_dot_product_attention->flags = flags;
    4636
    4
      model_scaled_dot_product_attention->fused_unify_head_weights = fused_unify_head_weights;
    4637
    4
      model_scaled_dot_product_attention->no_bias = no_bias;
    4638
    4
      return (ccv_cnnp_model_t*)model_scaled_dot_product_attention;
    4639
    4
    }
    4640
    4641
    static ccv_cnnp_model_t* _ccv_cnnp_scaled_dot_product_attention_copy(const ccv_cnnp_model_t* const super, void* const context)
    4642
    0
    {
    4643
    0
      const ccv_cnnp_model_scaled_dot_product_attention_t* const self = (const ccv_cnnp_model_scaled_dot_product_attention_t*)super;
    4644
    0
      return ccv_cnnp_scaled_dot_product_attention(self->scale, self->is_causal, self->has_attn_mask, self->is_varlen, self->max_seqlen_q, self->max_seqlen_kv, self->flags, self->fused_unify_head_weights, self->no_bias, self->super.is_trainable, self->super.name);
    4645
    0
    }
    4646
    4647
    // MARK - Debug Layer
    4648
    4649
    typedef struct {
    4650
      ccv_cnnp_model_t super;
    4651
      ccv_nnc_tensor_symbol_t output;
    4652
      ccv_cnnp_model_debug_f debugger;
    4653
      ccv_cnnp_model_debug_context_deinit_f debug_deinit;
    4654
      ccv_cnnp_model_debug_context_copy_f debug_copy;
    4655
      void* debug_context;
    4656
    } ccv_cnnp_model_debug_t;
    4657
    4658
    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)
    4659
    1
    {
    4660
    1
      if (cmd.cmd == CCV_NNC_CUSTOM_BACKWARD)
    4661
    0
      {
    4662
    0
        assert(0 && "don't support debug backward pass yet");
    4663
    0
      }
    4664
    1
      ccv_cnnp_model_debug_t* const self = (ccv_cnnp_model_debug_t*)cmd.data;
    4665
    1
      self->debugger(inputs, input_size, stream_context, self->debug_context);
    4666
    1
      return CCV_NNC_EXEC_SUCCESS;
    4667
    1
    }
    4668
    4669
    static ccv_nnc_cmd_vtab_t ccv_cnnp_debug_exec_isa = {
    4670
      .exec = _ccv_cnnp_debug_exec
    4671
    };
    4672
    4673
    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)
    4674
    1
    {
    4675
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_debug_build] -\n");
    4676
    1
      assert(input_size >= 1);
    4677
    1
      assert(output_size == 1);
    4678
    1
      ccv_nnc_tensor_symbol_t to = ccv_nnc_tensor_symbol_alias_to(graph, inputs[0]);
    4679
    1
      ccv_nnc_tensor_param_t output_params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4680
    1
      if (to.d == CCV_NNC_NO_TENSOR_SYMBOL) // If we are not reshape an alias, it is straightforward.
    4681
    1
      {
    4682
    1
        int ofs[CCV_NNC_MAX_DIM_ALLOC] = {0};
    4683
    1
        int stride[CCV_NNC_MAX_DIM_ALLOC];
    4684
    1
        ccv_nnc_tensor_get_stride(output_params.dim, stride);
    4685
    1
        outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, inputs[0], ofs, stride, output_params, 0);
    4686
    1
      } else {
    4687
    0
        int old_ofs[CCV_NNC_MAX_DIM_ALLOC];
    4688
    0
        int old_stride[CCV_NNC_MAX_DIM_ALLOC];
    4689
    0
        ccv_nnc_tensor_symbol_alias_params(graph, inputs[0], old_ofs, old_stride);
    4690
    0
        outputs[0] = ccv_nnc_tensor_symbol_alias_new(graph, to, old_ofs, old_stride, output_params, 0);
    4691
    0
      }
    4692
    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);
    4693
    1
      cmd.data = self;
    4694
    1
      ccv_nnc_graph_exec_symbol_t make_debug = ccv_nnc_graph_exec_symbol_new(graph, cmd, inputs, input_size, outputs, 1, "debug");
    4695
      // Disable any optimizations.
    4696
    1
      ccv_nnc_graph_exec_symbol_set_flags(graph, make_debug, CCV_NNC_GRAPH_EXEC_DISABLE_OPT);
    4697
    1
    }
    4698
    4699
    static void _ccv_cnnp_debug_deinit(ccv_cnnp_model_t* const super)
    4700
    1
    {
    4701
    1
      const ccv_cnnp_model_debug_t* const self = (const ccv_cnnp_model_debug_t*)super;
    4702
    1
      if (self->debug_deinit && self->debug_context0)
    4703
    0
        self->debug_deinit(self->debug_context);
    4704
    1
    }
    4705
    4706
    static ccv_cnnp_model_t* _ccv_cnnp_debug_copy(const ccv_cnnp_model_t* const super, void* const context);
    4707
    4708
    static const ccv_cnnp_model_vtab_t ccv_cnnp_debug_isa = {
    4709
      .build = _ccv_cnnp_debug_build,
    4710
      .deinit = _ccv_cnnp_debug_deinit,
    4711
      .copy = _ccv_cnnp_debug_copy,
    4712
    };
    4713
    4714
    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)
    4715
    1
    {
    4716
    1
      ccv_cnnp_model_debug_t* const model_debug = (ccv_cnnp_model_debug_t*)cccalloc(1, sizeof(ccv_cnnp_model_debug_t));
    4717
    1
      model_debug->super.isa = &ccv_cnnp_debug_isa;
    4718
    1
      model_debug->super.input_size = 0;
    4719
    1
      model_debug->super.outputs = &model_debug->output;
    4720
    1
      model_debug->super.output_size = 1;
    4721
    1
      model_debug->debugger = func;
    4722
    1
      model_debug->debug_context = context;
    4723
    1
      model_debug->debug_deinit = deinit;
    4724
    1
      model_debug->debug_copy = copy;
    4725
    1
      ccv_cnnp_model_copy_name(&model_debug->super, name);
    4726
    1
      return (ccv_cnnp_model_t*)model_debug;
    4727
    1
    }
    4728
    4729
    static ccv_cnnp_model_t* _ccv_cnnp_debug_copy(const ccv_cnnp_model_t* const super, void* const context)
    4730
    0
    {
    4731
    0
      const ccv_cnnp_model_debug_t* const self = (const ccv_cnnp_model_debug_t*)super;
    4732
    0
      void* debug_context = self->debug_context;
    4733
    0
      if (self->debug_copy && self->debug_context)
    4734
    0
        debug_context = self->debug_copy(self->debug_context);
    4735
    0
      return ccv_cnnp_debug(self->debugger, debug_context, self->debug_deinit, self->debug_copy, self->super.name);
    4736
    0
    }
    4737
    4738
    /// MARK - Sort layer.
    4739
    4740
    typedef struct {
    4741
      ccv_cnnp_model_t super;
    4742
      ccv_nnc_tensor_symbol_t outputs[2];
    4743
      int along_axis;
    4744
      int descending;
    4745
    } ccv_cnnp_model_sort_t;
    4746
    4747
    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)
    4748
    1
    {
    4749
    1
      ccv_cnnp_model_sort_t* const self = (ccv_cnnp_model_sort_t*)super;
    4750
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_sort_build] - along_axis: %d, descending: %d\n", self->along_axis, self->descending);
    4751
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4752
    1
      assert(output_size == 2);
    4753
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4754
    1
      params.datatype = CCV_32S;
    4755
    1
      outputs[1] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4756
    1
      ccv_nnc_graph_exec_symbol_new(graph, CMD_SORT_FORWARD(self->along_axis, self->descending), inputs, input_size, outputs, output_size, "sort");
    4757
    1
    }
    4758
    4759
    static ccv_cnnp_model_t* _ccv_cnnp_sort_copy(const ccv_cnnp_model_t* const self, void* const context);
    4760
    4761
    static const ccv_cnnp_model_vtab_t ccv_cnnp_sort_isa = {
    4762
      .build = _ccv_cnnp_sort_build,
    4763
      .copy = _ccv_cnnp_sort_copy,
    4764
    };
    4765
    4766
    ccv_cnnp_model_t* ccv_cnnp_sort(const int along_axis, const int descending, const char* const name)
    4767
    1
    {
    4768
    1
      ccv_cnnp_model_sort_t* const model_sort = (ccv_cnnp_model_sort_t*)cccalloc(1, sizeof(ccv_cnnp_model_sort_t));
    4769
    1
      model_sort->super.isa = &ccv_cnnp_sort_isa;
    4770
    1
      model_sort->super.input_size = 0;
    4771
    1
      model_sort->super.outputs = model_sort->outputs;
    4772
    1
      model_sort->super.output_size = 2;
    4773
    1
      model_sort->along_axis = along_axis;
    4774
    1
      model_sort->descending = descending;
    4775
    1
      ccv_cnnp_model_copy_name(&model_sort->super, name);
    4776
    1
      return (ccv_cnnp_model_t*)model_sort;
    4777
    1
    }
    4778
    4779
    static ccv_cnnp_model_t* _ccv_cnnp_sort_copy(const ccv_cnnp_model_t* const super, void* const context)
    4780
    0
    {
    4781
    0
      ccv_cnnp_model_sort_t* const self = (ccv_cnnp_model_sort_t*)super;
    4782
    0
      return ccv_cnnp_sort(self->along_axis, self->descending, self->super.name);
    4783
    0
    }
    4784
    4785
    /// MARK - Partition layer.
    4786
    4787
    typedef struct {
    4788
      ccv_cnnp_model_t super;
    4789
      ccv_nnc_tensor_symbol_t outputs[2];
    4790
      int kth;
    4791
      int along_axis;
    4792
      int descending;
    4793
    } ccv_cnnp_model_partition_t;
    4794
    4795
    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)
    4796
    1
    {
    4797
    1
      ccv_cnnp_model_partition_t* const self = (ccv_cnnp_model_partition_t*)super;
    4798
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_partition_build] - kth: %d, along_axis: %d, descending: %d\n", self->kth, self->along_axis, self->descending);
    4799
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4800
    1
      assert(output_size == 2);
    4801
    1
      if (self->kth > 0)
    4802
    1
        params.dim[self->along_axis] = self->kth;
    4803
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4804
    1
      params.datatype = CCV_32S;
    4805
    1
      outputs[1] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4806
    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");
    4807
    1
    }
    4808
    4809
    static ccv_cnnp_model_t* _ccv_cnnp_partition_copy(const ccv_cnnp_model_t* const self, void* const context);
    4810
    4811
    static const ccv_cnnp_model_vtab_t ccv_cnnp_partition_isa = {
    4812
      .build = _ccv_cnnp_partition_build,
    4813
      .copy = _ccv_cnnp_partition_copy,
    4814
    };
    4815
    4816
    ccv_cnnp_model_t* ccv_cnnp_partition(const int kth, const int along_axis, const int descending, const char* const name)
    4817
    1
    {
    4818
    1
      ccv_cnnp_model_partition_t* const model_partition = (ccv_cnnp_model_partition_t*)cccalloc(1, sizeof(ccv_cnnp_model_partition_t));
    4819
    1
      model_partition->super.isa = &ccv_cnnp_partition_isa;
    4820
    1
      model_partition->super.input_size = 0;
    4821
    1
      model_partition->super.outputs = model_partition->outputs;
    4822
    1
      model_partition->super.output_size = 2;
    4823
    1
      model_partition->kth = kth;
    4824
    1
      model_partition->along_axis = along_axis;
    4825
    1
      model_partition->descending = descending;
    4826
    1
      ccv_cnnp_model_copy_name(&model_partition->super, name);
    4827
    1
      return (ccv_cnnp_model_t*)model_partition;
    4828
    1
    }
    4829
    4830
    static ccv_cnnp_model_t* _ccv_cnnp_partition_copy(const ccv_cnnp_model_t* const super, void* const context)
    4831
    0
    {
    4832
    0
      ccv_cnnp_model_partition_t* const self = (ccv_cnnp_model_partition_t*)super;
    4833
    0
      return ccv_cnnp_partition(self->kth, self->along_axis, self->descending, self->super.name);
    4834
    0
    }
    4835
    4836
    /// MARK - Unique consecutive layer.
    4837
    4838
    typedef struct {
    4839
      ccv_cnnp_model_t super;
    4840
      ccv_nnc_tensor_symbol_t outputs[2];
    4841
      int bincount;
    4842
    } ccv_cnnp_model_unique_consecutive_t;
    4843
    4844
    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)
    4845
    1
    {
    4846
    1
      ccv_cnnp_model_unique_consecutive_t* const self = (ccv_cnnp_model_unique_consecutive_t*)super;
    4847
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_unique_consecutive_build] - bincount: %d\n", self->bincount);
    4848
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4849
    1
      assert(output_size == 2);
    4850
    1
      if (self->bincount > 0)
    4851
    1
        params.dim[0] = ccv_min(params.dim[0], self->bincount);
    4852
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4853
    1
      params.datatype = CCV_32S;
    4854
    1
      outputs[1] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4855
    1
      ccv_nnc_graph_exec_symbol_new(graph, CMD_UNIQUE_CONSECUTIVE_FORWARD(self->bincount), inputs, input_size, outputs, output_size, "unique_consecutive");
    4856
    1
    }
    4857
    4858
    static ccv_cnnp_model_t* _ccv_cnnp_unique_consecutive_copy(const ccv_cnnp_model_t* const self, void* const context);
    4859
    4860
    static const ccv_cnnp_model_vtab_t ccv_cnnp_unique_consecutive_isa = {
    4861
      .build = _ccv_cnnp_unique_consecutive_build,
    4862
      .copy = _ccv_cnnp_unique_consecutive_copy,
    4863
    };
    4864
    4865
    ccv_cnnp_model_t* ccv_cnnp_unique_consecutive(const int bincount, const char* const name)
    4866
    1
    {
    4867
    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));
    4868
    1
      model_unique_consecutive->super.isa = &ccv_cnnp_unique_consecutive_isa;
    4869
    1
      model_unique_consecutive->super.input_size = 0;
    4870
    1
      model_unique_consecutive->super.outputs = model_unique_consecutive->outputs;
    4871
    1
      model_unique_consecutive->super.output_size = 2;
    4872
    1
      model_unique_consecutive->bincount = bincount;
    4873
    1
      ccv_cnnp_model_copy_name(&model_unique_consecutive->super, name);
    4874
    1
      return (ccv_cnnp_model_t*)model_unique_consecutive;
    4875
    1
    }
    4876
    4877
    static ccv_cnnp_model_t* _ccv_cnnp_unique_consecutive_copy(const ccv_cnnp_model_t* const super, void* const context)
    4878
    0
    {
    4879
    0
      ccv_cnnp_model_unique_consecutive_t* const self = (ccv_cnnp_model_unique_consecutive_t*)super;
    4880
    0
      return ccv_cnnp_unique_consecutive(self->bincount, self->super.name);
    4881
    0
    }
    4882
    4883
    /// MARK - Scatter add layer.
    4884
    4885
    typedef struct {
    4886
      ccv_cnnp_model_t super;
    4887
      ccv_nnc_tensor_symbol_t output;
    4888
      int bincount;
    4889
    } ccv_cnnp_model_scatter_add_t;
    4890
    4891
    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)
    4892
    1
    {
    4893
    1
      ccv_cnnp_model_scatter_add_t* const self = (ccv_cnnp_model_scatter_add_t*)super;
    4894
    1
      PRINT(CCV_CLI_VERBOSE, "[cnnp_scatter_add_build] - bincount: %d\n", self->bincount);
    4895
    1
      ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4896
    1
      assert(output_size == 1);
    4897
    1
      assert(self->bincount > 0);
    4898
    1
      params.dim[0] = self->bincount;
    4899
    1
      outputs[0] = ccv_nnc_tensor_symbol_new(graph, params, 0);
    4900
    1
      ccv_nnc_graph_exec_symbol_new(graph, CMD_SCATTER_ADD_FORWARD(self->bincount), inputs, input_size, outputs, output_size, "scatter_add");
    4901
    1
    }
    4902
    4903
    static ccv_cnnp_model_t* _ccv_cnnp_scatter_add_copy(const ccv_cnnp_model_t* const self, void* const context);
    4904
    4905
    static const ccv_cnnp_model_vtab_t ccv_cnnp_scatter_add_isa = {
    4906
      .build = _ccv_cnnp_scatter_add_build,
    4907
      .copy = _ccv_cnnp_scatter_add_copy,
    4908
    };
    4909
    4910
    ccv_cnnp_model_t* ccv_cnnp_scatter_add(const int bincount, const char* const name)
    4911
    1
    {
    4912
    1
      assert(bincount > 0);
    4913
    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));
    4914
    1
      model_scatter_add->super.isa = &ccv_cnnp_scatter_add_isa;
    4915
    1
      model_scatter_add->super.input_size = 0;
    4916
    1
      model_scatter_add->super.outputs = &model_scatter_add->output;
    4917
    1
      model_scatter_add->super.output_size = 1;
    4918
    1
      model_scatter_add->bincount = bincount;
    4919
    1
      ccv_cnnp_model_copy_name(&model_scatter_add->super, name);
    4920
    1
      return (ccv_cnnp_model_t*)model_scatter_add;
    4921
    1
    }
    4922
    4923
    static ccv_cnnp_model_t* _ccv_cnnp_scatter_add_copy(const ccv_cnnp_model_t* const super, void* const context)
    4924
    0
    {
    4925
    0
      ccv_cnnp_model_scatter_add_t* const self = (ccv_cnnp_model_scatter_add_t*)super;
    4926
    0
      return ccv_cnnp_scatter_add(self->bincount, self->super.name);
    4927
    0
    }
    4928
    4929
    // MARK - Segmented Dense Layer
    4930
    4931
    typedef struct {
    4932
      ccv_cnnp_model_t super;
    4933
      ccv_nnc_tensor_symbol_t output;
    4934
      ccv_nnc_tensor_symbol_t weights;
    4935
      ccv_nnc_tensor_symbol_t bias;
    4936
      int segments;
    4937
      int count;
    4938
      int no_bias;
    4939
      int flags;
    4940
    } ccv_cnnp_model_segmented_dense_t;
    4941
    4942
    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)
    4943
    2
    {
    4944
    2
      ccv_cnnp_model_segmented_dense_t* const self = (ccv_cnnp_model_segmented_dense_t*)super;
    4945
    2
      PRINT(CCV_CLI_VERBOSE, "[cnnp_segmented_dense_build] -\n");
    4946
    2
      assert(input_size == 3);
    4947
    2
      assert(output_size == 1);
    4948
    2
      const ccv_nnc_tensor_param_t params = ccv_nnc_tensor_symbol_params(graph, inputs[0]);
    4949
    2
      const ccv_nnc_tensor_param_t indices_params = ccv_nnc_tensor_symbol_params(graph, inputs[1]);
    4950
    2
      const ccv_nnc_tensor_param_t counts_params = ccv_nnc_tensor_symbol_params(graph, inputs[2]);
    4951
    2
      ccv_nnc_tensor_param_t weights_params = params;
    4952
    2
      memset(weights_params.dim, 0, sizeof(weights_params.dim));
    4953
    2
      weights_params.dim[0] = self->segments;
    4954
    2
      weights_params.dim[1] = self->count;
    4955
    2
      weights_params.dim[2] = params.dim[ccv_nnc_tensor_nd(params.dim) - 1];
    4956
    2
      if (!self->weights.graph)
    4957
    2
        self->weights = ccv_nnc_tensor_symbol_new(graph, weights_params, "weights");
    4958
    2
      assert(self->weights.graph == graph);
    4959
    2
      const ccv_nnc_tensor_symbol_t weights = ccv_cnnp_model_get_symbol(super, self->weights);
    4960
    2
      ccv_nnc_tensor_param_t bias_params = params;
    4961
    2
      memset(bias_params.dim, 0, sizeof(bias_params.dim));
    4962
    2
      bias_params.dim[0] = self->segments;
    4963
    2
      bias_params.dim[1] = self->count;
    4964
    2
      ccv_nnc_cmd_t cmd = {0};
    4965
    2
      cmd.cmd = CCV_NNC_SEGMENTED_GEMM_FORWARD;
    4966
    2
      cmd.info.blas.a[0] = 1;
    4967
    2
      cmd.info.blas.a[1] = 1;
    4968
    2
      cmd.info.blas.transpose_b[0] = 1;
    4969
    2
      cmd.info.blas.transpose_b[1] = 2;
    4970
    2
      cmd.info.blas.flags = self->flags;
    4971
    2
      ccv_nnc_tensor_param_t output_params;
    4972
    2
      ccv_nnc_hint_tensor_auto(cmd, (ccv_nnc_tensor_param_t []){
    4973
    2
          params, indices_params, counts_params,
    4974
    2
          weights_params,
    4975
    2
          bias_params,
    4976
    2
        }, 5, ccv_nnc_no_hint, &output_params, 1);
    4977
    2
      const ccv_nnc_tensor_symbol_t output = ccv_nnc_tensor_symbol_new(graph, output_params, 0);
    4978
    2
      if (self->no_bias)
    4979
    1
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], inputs[1], inputs[2], weights), TENSOR_SYMBOL_LIST(output), "segmented_dense");
    4980
    1
      else {
    4981
    1
        if (!self->bias.graph)
    4982
    1
          self->bias = ccv_nnc_tensor_symbol_new(graph, bias_params, "bias");
    4983
    1
        const ccv_nnc_tensor_symbol_t bias = ccv_cnnp_model_get_symbol(super, self->bias);
    4984
    1
        ccv_nnc_graph_exec_symbol_new(graph, cmd, TENSOR_SYMBOL_LIST(inputs[0], inputs[1], inputs[2], weights, bias), TENSOR_SYMBOL_LIST(output), "segmented_dense");
    4985
    1
      }
    4986
    2
      outputs[0] = output;
    4987
    2
    }
    4988
    4989
    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)
    4990
    0
    {
    4991
    0
      ccv_cnnp_model_segmented_dense_t* const self = (ccv_cnnp_model_segmented_dense_t*)super;
    4992
    0
      const ccv_nnc_tensor_param_t weight_params = ccv_nnc_tensor_symbol_params(graph, self->weights);
    4993
    0
      const int c = weight_params.dim[1];
    4994
    0
      const float std = sqrtf(2) / sqrtf(c);
    4995
    0
      const float bound = sqrtf(3) * std;
    4996
    0
      initializer(context, CMD_RANDOM_UNIFORM_FORWARD(-bound, bound), ccv_nnc_no_hint, 0, 0, self->weights);
    4997
    0
      if (self->bias.graph)
    4998
    0
        initializer(context, CMD_SET_FORWARD(0), ccv_nnc_no_hint, 0, 0, self->bias);
    4999
    0
    }
    5000
    5001
    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)
    5002
    2
    {
    5003
    2
      ccv_cnnp_model_segmented_dense_t* const self = (ccv_cnnp_model_segmented_dense_t*)super;
    5004
    2
      add_to_array(parameters, self->weights, is_trainable);
    5005
    2
      if (self->bias.graph)
    5006
    1
        add_to_array(parameters, self->bias, is_trainable);
    5007
    2
    }
    5008
    5009
    static ccv_cnnp_model_t* _ccv_cnnp_segmented_dense_copy(const ccv_cnnp_model_t* const super, void* const context);
    5010
    5011
    static const ccv_cnnp_model_vtab_t ccv_cnnp_segmented_dense_isa = {
    5012
      .build = _ccv_cnnp_segmented_dense_build,
    5013
      .init_states = _ccv_cnnp_segmented_dense_init_states,
    5014
      .add_to_parameter = _ccv_cnnp_segmented_dense_add_to_parameter,
    5015
      .copy = _ccv_cnnp_segmented_dense_copy,
    5016
    };
    5017
    5018
    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)
    5019
    2
    {
    5020
    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));
    5021
    2
      model_segmented_dense->super.isa = &ccv_cnnp_segmented_dense_isa;
    5022
    2
      model_segmented_dense->super.input_size = 3;
    5023
    2
      model_segmented_dense->super.outputs = &model_segmented_dense->output;
    5024
    2
      model_segmented_dense->super.output_size = 1;
    5025
    2
      model_segmented_dense->super.is_trainable = is_trainable;
    5026
    2
      ccv_cnnp_model_copy_name(&model_segmented_dense->super, name);
    5027
    2
      model_segmented_dense->weights.d = CCV_NNC_NO_TENSOR_SYMBOL;
    5028
    2
      model_segmented_dense->weights.graph = 0;
    5029
    2
      model_segmented_dense->bias.d = CCV_NNC_NO_TENSOR_SYMBOL;
    5030
    2
      model_segmented_dense->bias.graph = 0;
    5031
    2
      model_segmented_dense->segments = segments;
    5032
    2
      model_segmented_dense->count = count;
    5033
    2
      model_segmented_dense->no_bias = no_bias;
    5034
    2
      model_segmented_dense->flags = flags;
    5035
    2
      return (ccv_cnnp_model_t*)model_segmented_dense;
    5036
    2
    }
    5037
    5038
    static ccv_cnnp_model_t* _ccv_cnnp_segmented_dense_copy(const ccv_cnnp_model_t* const super, void* const context)
    5039
    0
    {
    5040
    0
      const ccv_cnnp_model_segmented_dense_t* const self = (const ccv_cnnp_model_segmented_dense_t*)super;
    5041
    0
      return ccv_cnnp_segmented_dense(self->segments, self->count, self->no_bias, self->flags, self->super.is_trainable, self->super.name);
    5042
    0
    }