## 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/cmd/ew/ccv_nnc_ew_cpu_ref.c

    Line
    Count
    Source
    1
    #include "ccv.h"
    2
    #include "ccv_internal.h"
    3
    #include "nnc/ccv_nnc.h"
    4
    #include "nnc/ccv_nnc_easy.h"
    5
    #include "nnc/ccv_nnc_internal.h"
    6
    #ifdef USE_OPENMP
    7
    #include <omp.h>
    8
    #endif
    9
    #ifdef USE_DISPATCH
    10
    #include <dispatch/dispatch.h>
    11
    #endif
    12
    13
    #include "../_ccv_nnc_cpu_ref.h"
    14
    15
    void _ccv_nnc_ewsum_forw_cpu_ref_f32(ccv_nnc_tensor_view_t* const* const inputs, const int input_size, ccv_nnc_tensor_view_t* const* const outputs, const int output_size)
    16
    36.0k
    {
    17
    36.0k
      if (input_size == 1 && output_size == 10)
    18
    0
      {
    19
    0
        _ccv_nnc_tensor_transfer_cpu_ref_f32(inputs[0], outputs[0]);
    20
    0
        return;
    21
    0
      }
    22
      // Assuming this is float 32.
    23
    36.0k
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    24
    36.0k
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    25
    36.0k
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    26
    36.0k
      int cstride[CCV_NNC_MAX_DIM_ALLOC];
    27
    36.0k
      int x, z;
    28
    36.0k
      int k = 0;
    29
      // Bad, I promised this can be inplace operation. Need to first find out if there are share the same pointer first.
    30
    72.1k
      for (z = 1; z < input_size; z++36.0k)
    31
    36.0k
      {
    32
    36.0k
        ccv_nnc_tensor_view_t* c = outputs[0];
    33
    36.0k
        ccv_nnc_tensor_view_t* a = inputs[z];
    34
    36.0k
        if (c->data.f32 == a->data.f32)
    35
    10
        {
    36
    10
          k = z;
    37
    10
          break;
    38
    10
        }
    39
    36.0k
      }
    40
    72.1k
      for (z = 0; z < input_size - 1; z++36.0k)
    41
    36.0k
      {
    42
    36.0k
        ccv_nnc_tensor_view_t* c = outputs[0];
    43
    36.0k
        ccv_nnc_tensor_view_t* a = z > 0 ? c28 : inputs[k]36.0k;
    44
    36.0k
        ccv_nnc_tensor_view_t* b = z >= k ? inputs[z + 1]36.0k : inputs[z]10;
    45
    36.0k
        assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    46
    36.0k
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    47
    36.0k
        assert(ccv_nnc_tensor_nd(c->info.dim) <= CCV_NNC_MAX_DIM + 2);
    48
    36.0k
        ccv_nnc_tensor_view_get_dim(a, dim);
    49
    36.0k
        assert(ccv_nnc_tensor_view_check_dim(b, dim));
    50
    36.0k
        assert(ccv_nnc_tensor_view_check_dim(c, dim));
    51
    36.0k
        if (!CCV_IS_TENSOR_VIEW(a) && !36.0kCCV_IS_TENSOR_VIEW36.0k(b) && !36.0kCCV_IS_TENSOR_VIEW36.0k(c))
    52
    36.0k
        {
    53
          // Super optimal case, just do one for-loop for sum.
    54
    36.0k
          const int tensor_count = ccv_nnc_tensor_count(a->info);
    55
    15.5M
          for (x = 0; x < tensor_count; x++15.4M)
    56
    15.4M
            c->data.f32[x] = a->data.f32[x] + b->data.f32[x];
    57
    36.0k
          continue;
    58
    36.0k
        }
    59
    36.0k
        assert(CCV_NNC_MAX_DIM == 2)3; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    60
    3
        ccv_nnc_tensor_view_get_stride(a, astride);
    61
    3
        ccv_nnc_tensor_view_get_stride(b, bstride);
    62
    3
        ccv_nnc_tensor_view_get_stride(c, cstride);
    63
    3
        int i[CCV_NNC_MAX_DIM + 2];
    64
    3
        float* const ap = a->data.f32;
    65
    3
        float* const bp = b->data.f32;
    66
    3
        float* const cp = c->data.f32;
    67
    3
        const int count = dim[2] * dim[3];
    68
    3
        if (astride[2] == dim[3] && bstride[2] == dim[3] && cstride[2] == dim[3] && astride[3] == 1 && bstride[3] == 1 && cstride[3] == 1)
    69
    3
        {
    70
          // Special casing if the ainc[3] is the same as dim[3] (do memcpy for the last two dim)
    71
    6
          for (i[0] = 0; i[0] < dim[0]; i[0]++3)
    72
    3
          {
    73
    3
            float* ap0 = ap + i[0] * astride[0];
    74
    3
            float* bp0 = bp + i[0] * bstride[0];
    75
    3
            float* cp0 = cp + i[0] * cstride[0];
    76
    6
            for (i[1] = 0; i[1] < dim[1]; i[1]++3)
    77
    3
            {
    78
    6
              for (x = 0; x < count; x++3)
    79
    3
                cp0[x] = ap0[x] + bp0[x];
    80
    3
              ap0 += astride[1];
    81
    3
              bp0 += bstride[1];
    82
    3
              cp0 += cstride[1];
    83
    3
            }
    84
    3
          }
    85
    3
          continue;
    86
    3
        }
    87
        // Non-optimal case, need to do skip copy.
    88
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    89
    0
        {
    90
    0
          float* const ap0 = ap + i[0] * astride[0];
    91
    0
          float* const bp0 = bp + i[0] * bstride[0];
    92
    0
          float* const cp0 = cp + i[0] * cstride[0];
    93
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    94
    0
          {
    95
    0
            float* ap1 = ap0 + i[1] * astride[1];
    96
    0
            float* bp1 = bp0 + i[1] * bstride[1];
    97
    0
            float* cp1 = cp0 + i[1] * cstride[1];
    98
    0
            for (i[2] = 0; i[2] < dim[2]; i[2]++)
    99
    0
            {
    100
    0
              for (x = 0; x < dim[3]; x++)
    101
    0
                cp1[x * cstride[3]] = ap1[x * astride[3]] + bp1[x * bstride[3]];
    102
    0
              ap1 += astride[2];
    103
    0
              bp1 += bstride[2];
    104
    0
              cp1 += cstride[2];
    105
    0
            }
    106
    0
          }
    107
    0
        }
    108
    0
      }
    109
    36.0k
    }
    110
    111
    void _ccv_nnc_ewsum_forw_cpu_ref_i32(ccv_nnc_tensor_view_t* const* const inputs, const int input_size, ccv_nnc_tensor_view_t* const* const outputs, const int output_size)
    112
    0
    {
    113
    0
      if (input_size == 1 && output_size == 1)
    114
    0
      {
    115
    0
        _ccv_nnc_tensor_transfer_cpu_ref_f32(inputs[0], outputs[0]);
    116
    0
        return;
    117
    0
      }
    118
      // Assuming this is float 32.
    119
    0
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    120
    0
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    121
    0
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    122
    0
      int cstride[CCV_NNC_MAX_DIM_ALLOC];
    123
    0
      int x, z;
    124
    0
      int k = 0;
    125
      // Bad, I promised this can be inplace operation. Need to first find out if there are share the same pointer first.
    126
    0
      for (z = 1; z < input_size; z++)
    127
    0
      {
    128
    0
        ccv_nnc_tensor_view_t* c = outputs[0];
    129
    0
        ccv_nnc_tensor_view_t* a = inputs[z];
    130
    0
        if (c->data.f32 == a->data.f32)
    131
    0
        {
    132
    0
          k = z;
    133
    0
          break;
    134
    0
        }
    135
    0
      }
    136
    0
      for (z = 0; z < input_size - 1; z++)
    137
    0
      {
    138
    0
        ccv_nnc_tensor_view_t* c = outputs[0];
    139
    0
        ccv_nnc_tensor_view_t* a = z > 0 ? c : inputs[k];
    140
    0
        ccv_nnc_tensor_view_t* b = z >= k ? inputs[z + 1] : inputs[z];
    141
    0
        assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    142
    0
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    143
    0
        assert(ccv_nnc_tensor_nd(c->info.dim) <= CCV_NNC_MAX_DIM + 2);
    144
    0
        ccv_nnc_tensor_view_get_dim(a, dim);
    145
    0
        assert(ccv_nnc_tensor_view_check_dim(b, dim));
    146
    0
        assert(ccv_nnc_tensor_view_check_dim(c, dim));
    147
    0
        if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b) && !CCV_IS_TENSOR_VIEW(c))
    148
    0
        {
    149
          // Super optimal case, just do one for-loop for sum.
    150
    0
          const int tensor_count = ccv_nnc_tensor_count(a->info);
    151
    0
          for (x = 0; x < tensor_count; x++)
    152
    0
            c->data.f32[x] = a->data.f32[x] + b->data.f32[x];
    153
    0
          continue;
    154
    0
        }
    155
    0
        assert(CCV_NNC_MAX_DIM == 2); // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    156
    0
        ccv_nnc_tensor_view_get_stride(a, astride);
    157
    0
        ccv_nnc_tensor_view_get_stride(b, bstride);
    158
    0
        ccv_nnc_tensor_view_get_stride(c, cstride);
    159
    0
        int i[CCV_NNC_MAX_DIM + 2];
    160
    0
        int* const ap = a->data.i32;
    161
    0
        int* const bp = b->data.i32;
    162
    0
        int* const cp = c->data.i32;
    163
    0
        const int count = dim[2] * dim[3];
    164
    0
        if (astride[2] == dim[3] && bstride[2] == dim[3] && cstride[2] == dim[3] && astride[3] == 1 && bstride[3] == 1 && cstride[3] == 1)
    165
    0
        {
    166
          // Special casing if the ainc[3] is the same as dim[3] (do memcpy for the last two dim)
    167
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    168
    0
          {
    169
    0
            int* ap0 = ap + i[0] * astride[0];
    170
    0
            int* bp0 = bp + i[0] * bstride[0];
    171
    0
            int* cp0 = cp + i[0] * cstride[0];
    172
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    173
    0
            {
    174
    0
              for (x = 0; x < count; x++)
    175
    0
                cp0[x] = ap0[x] + bp0[x];
    176
    0
              ap0 += astride[1];
    177
    0
              bp0 += bstride[1];
    178
    0
              cp0 += cstride[1];
    179
    0
            }
    180
    0
          }
    181
    0
          continue;
    182
    0
        }
    183
        // Non-optimal case, need to do skip copy.
    184
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    185
    0
        {
    186
    0
          int* const ap0 = ap + i[0] * astride[0];
    187
    0
          int* const bp0 = bp + i[0] * bstride[0];
    188
    0
          int* const cp0 = cp + i[0] * cstride[0];
    189
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    190
    0
          {
    191
    0
            int* ap1 = ap0 + i[1] * astride[1];
    192
    0
            int* bp1 = bp0 + i[1] * bstride[1];
    193
    0
            int* cp1 = cp0 + i[1] * cstride[1];
    194
    0
            for (i[2] = 0; i[2] < dim[2]; i[2]++)
    195
    0
            {
    196
    0
              for (x = 0; x < dim[3]; x++)
    197
    0
                cp1[x * cstride[3]] = ap1[x * astride[3]] + bp1[x * bstride[3]];
    198
    0
              ap1 += astride[2];
    199
    0
              bp1 += bstride[2];
    200
    0
              cp1 += cstride[2];
    201
    0
            }
    202
    0
          }
    203
    0
        }
    204
    0
      }
    205
    0
    }
    206
    207
    static int _ccv_nnc_ewsum_forw(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)
    208
    36.0k
    {
    209
    36.0k
      if (outputs[0]->info.datatype == CCV_32S)
    210
    0
        _ccv_nnc_ewsum_forw_cpu_ref_i32((ccv_nnc_tensor_view_t**)inputs, input_size, (ccv_nnc_tensor_view_t**)outputs, output_size);
    211
    36.0k
      else
    212
    36.0k
        _ccv_nnc_ewsum_forw_cpu_ref_f32((ccv_nnc_tensor_view_t**)inputs, input_size, (ccv_nnc_tensor_view_t**)outputs, output_size);
    213
    36.0k
      return CCV_NNC_EXEC_SUCCESS;
    214
    36.0k
    }
    215
    216
    static int _ccv_nnc_ewsum_back(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)
    217
    7.94k
    {
    218
      // D[x + y + z, x] = 1
    219
    7.94k
      int i;
    220
    7.94k
      if (inputs[0] == 0)
    221
    0
      {
    222
        // Set them to 1.
    223
    0
        for (i = 0; i < output_size; i++)
    224
    0
          if (outputs[i])
    225
    0
            _ccv_nnc_tensor_set_cpu_ref_f32((ccv_nnc_tensor_view_t*)outputs[i], 1);
    226
    7.94k
      } else {
    227
        // Copy over the gradient (If they are not pointing to the same tensor already).
    228
    23.8k
        for (i = 0; i < output_size; i++15.8k)
    229
    15.8k
          if (outputs[i] && inputs[0]->data.f32 != outputs[i]->data.f32)
    230
    7.72k
            _ccv_nnc_tensor_transfer_cpu_ref_f32((ccv_nnc_tensor_view_t*)inputs[0], (ccv_nnc_tensor_view_t*)outputs[i]);
    231
    7.94k
      }
    232
    7.94k
      return CCV_NNC_EXEC_SUCCESS;
    233
    7.94k
    }
    234
    235
    void _ccv_nnc_ewprod_forw_cpu_ref(ccv_nnc_tensor_view_t* const* const inputs, const int input_size, ccv_nnc_tensor_view_t* const* const outputs, const int output_size)
    236
    30.7k
    {
    237
    30.7k
      if (input_size == 1 && output_size == 10)
    238
    0
      {
    239
    0
        _ccv_nnc_tensor_transfer_cpu_ref_f32(inputs[0], outputs[0]);
    240
    0
        return;
    241
    0
      }
    242
      // Assuming this is float 32.
    243
    30.7k
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    244
    30.7k
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    245
    30.7k
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    246
    30.7k
      int cstride[CCV_NNC_MAX_DIM_ALLOC];
    247
    30.7k
      int x, z;
    248
    30.7k
      int k = 0;
    249
      // Bad, I promised this can be inplace operation. Need to first find out if there are share the same pointer first.
    250
    61.3k
      for (z = 1; z < input_size; z++30.6k)
    251
    30.7k
      {
    252
    30.7k
        ccv_nnc_tensor_view_t* c = outputs[0];
    253
    30.7k
        ccv_nnc_tensor_view_t* a = inputs[z];
    254
    30.7k
        if (c->data.f32 == a->data.f32)
    255
    12
        {
    256
    12
          k = z;
    257
    12
          break;
    258
    12
        }
    259
    30.7k
      }
    260
    61.4k
      for (z = 0; z < input_size - 1; z++30.7k)
    261
    30.7k
      {
    262
    30.7k
        ccv_nnc_tensor_view_t* c = outputs[0];
    263
    30.7k
        ccv_nnc_tensor_view_t* a = z > 0 ? c0 : inputs[k];
    264
    30.7k
        ccv_nnc_tensor_view_t* b = z >= k ? inputs[z + 1]30.6k : inputs[z]12;
    265
    30.7k
        assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    266
    30.7k
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    267
    30.7k
        assert(ccv_nnc_tensor_nd(c->info.dim) <= CCV_NNC_MAX_DIM + 2);
    268
    30.7k
        ccv_nnc_tensor_view_get_dim(a, dim);
    269
    30.7k
        assert(ccv_nnc_tensor_view_check_dim(b, dim));
    270
    30.7k
        assert(ccv_nnc_tensor_view_check_dim(c, dim));
    271
    30.7k
        if (!CCV_IS_TENSOR_VIEW(a) && !30.7kCCV_IS_TENSOR_VIEW30.7k(b) && !30.7kCCV_IS_TENSOR_VIEW30.7k(c))
    272
    30.7k
        {
    273
          // Super optimal case, just do one for-loop for sum.
    274
    30.7k
          const int tensor_count = ccv_nnc_tensor_count(a->info);
    275
    104k
          for (x = 0; x < tensor_count; x++73.5k)
    276
    73.5k
            c->data.f32[x] = a->data.f32[x] * b->data.f32[x];
    277
    30.7k
          continue;
    278
    30.7k
        }
    279
    30.7k
        assert(CCV_NNC_MAX_DIM == 2)3; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    280
    3
        ccv_nnc_tensor_view_get_stride(a, astride);
    281
    3
        ccv_nnc_tensor_view_get_stride(b, bstride);
    282
    3
        ccv_nnc_tensor_view_get_stride(c, cstride);
    283
    3
        int i[CCV_NNC_MAX_DIM + 2];
    284
    3
        float* const ap = a->data.f32;
    285
    3
        float* const bp = b->data.f32;
    286
    3
        float* const cp = c->data.f32;
    287
    3
        const int count = dim[2] * dim[3];
    288
    3
        if (astride[2] == dim[3] && bstride[2] == dim[3] && cstride[2] == dim[3])
    289
    3
        {
    290
          // Special casing if the ainc[3] is the same as dim[3]
    291
    6
          for (i[0] = 0; i[0] < dim[0]; i[0]++3)
    292
    3
          {
    293
    3
            float* ap0 = ap + i[0] * astride[0];
    294
    3
            float* bp0 = bp + i[0] * bstride[0];
    295
    3
            float* cp0 = cp + i[0] * cstride[0];
    296
    6
            for (i[1] = 0; i[1] < dim[1]; i[1]++3)
    297
    3
            {
    298
    6
              for (x = 0; x < count; x++3)
    299
    3
                cp0[x] = ap0[x] * bp0[x];
    300
    3
              ap0 += astride[1];
    301
    3
              bp0 += bstride[1];
    302
    3
              cp0 += cstride[1];
    303
    3
            }
    304
    3
          }
    305
    3
          continue;
    306
    3
        }
    307
        // Non-optimal case, need to do skip copy.
    308
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    309
    0
        {
    310
    0
          float* const ap0 = ap + i[0] * astride[0];
    311
    0
          float* const bp0 = bp + i[0] * bstride[0];
    312
    0
          float* const cp0 = cp + i[0] * cstride[0];
    313
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    314
    0
          {
    315
    0
            float* ap1 = ap0 + i[1] * astride[1];
    316
    0
            float* bp1 = bp0 + i[1] * bstride[1];
    317
    0
            float* cp1 = cp0 + i[1] * cstride[1];
    318
    0
            for (i[2] = 0; i[2] < dim[2]; i[2]++)
    319
    0
            {
    320
    0
              for (x = 0; x < dim[3]; x++)
    321
    0
                cp1[x] = ap1[x] * bp1[x];
    322
    0
              ap1 += astride[2];
    323
    0
              bp1 += bstride[2];
    324
    0
              cp1 += cstride[2];
    325
    0
            }
    326
    0
          }
    327
    0
        }
    328
    0
      }
    329
    30.7k
    }
    330
    331
    static int _ccv_nnc_ewprod_forw(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)
    332
    20.2k
    {
    333
    20.2k
      _ccv_nnc_ewprod_forw_cpu_ref((ccv_nnc_tensor_view_t**)inputs, input_size, (ccv_nnc_tensor_view_t**)outputs, output_size);
    334
    20.2k
      return CCV_NNC_EXEC_SUCCESS;
    335
    20.2k
    }
    336
    337
    static int _ccv_nnc_ewprod_back(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)
    338
    20.0k
    {
    339
      // D[x * y * z, x] = y * z
    340
      // Assuming this is float 32.
    341
    20.0k
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    342
    20.0k
      int gstride[CCV_NNC_MAX_DIM_ALLOC];
    343
    20.0k
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    344
    20.0k
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    345
    20.0k
      int hstride[CCV_NNC_MAX_DIM_ALLOC];
    346
    20.0k
      int x, z;
    347
    20.0k
      ccv_nnc_tensor_view_t* g = (ccv_nnc_tensor_view_t*)inputs[0];
    348
    20.0k
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)inputs[output_size + 1];
    349
    20.0k
      if (g == 0)
    350
    0
      {
    351
    0
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    352
    0
        ccv_nnc_tensor_view_get_dim(b, dim);
    353
    0
        ccv_nnc_tensor_view_get_stride(b, bstride);
    354
    0
        for (z = 0; z < output_size; z++)
    355
    0
        {
    356
    0
          ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[z + 1];
    357
    0
          ccv_nnc_tensor_view_t* h = (ccv_nnc_tensor_view_t*)outputs[z];
    358
    0
          assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    359
    0
          assert(ccv_nnc_tensor_nd(h->info.dim) <= CCV_NNC_MAX_DIM + 2);
    360
    0
          assert(ccv_nnc_tensor_view_check_dim(a, dim));
    361
    0
          assert(ccv_nnc_tensor_view_check_dim(h, dim));
    362
    0
          ccv_nnc_tensor_view_get_stride(a, astride);
    363
    0
          ccv_nnc_tensor_view_get_stride(h, hstride);
    364
    0
          if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b) && !CCV_IS_TENSOR_VIEW(h))
    365
    0
          {
    366
            // Super optimal case, just do one for-loop for sum.
    367
    0
            const int tensor_count = ccv_nnc_tensor_count(b->info);
    368
    0
            for (x = 0; x < tensor_count; x++)
    369
    0
              h->data.f32[x] = b->data.f32[x] / a->data.f32[x];
    370
    0
            continue;
    371
    0
          }
    372
    0
          assert(CCV_NNC_MAX_DIM == 2); // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    373
    0
          int i[CCV_NNC_MAX_DIM + 2];
    374
    0
          float* const ap = a->data.f32;
    375
    0
          float* const bp = b->data.f32;
    376
    0
          float* const hp = h->data.f32;
    377
    0
          const int count = dim[2] * dim[3];
    378
    0
          if (astride[2] == dim[3] && bstride[2] == dim[3] && hstride[2] == dim[3])
    379
    0
          {
    380
            // Special casing if the ainc[3] is the same as dim[3]
    381
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    382
    0
            {
    383
    0
              float* ap0 = ap + i[0] * astride[0];
    384
    0
              float* bp0 = bp + i[0] * bstride[0];
    385
    0
              float* hp0 = hp + i[0] * hstride[0];
    386
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    387
    0
              {
    388
    0
                for (x = 0; x < count; x++)
    389
    0
                  hp0[x] = bp0[x] / ap0[x];
    390
    0
                ap0 += astride[1];
    391
    0
                bp0 += bstride[1];
    392
    0
                hp0 += hstride[1];
    393
    0
              }
    394
    0
            }
    395
    0
            continue;
    396
    0
          }
    397
          // Non-optimal case, need to do skip copy.
    398
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    399
    0
          {
    400
    0
            float* const ap0 = ap + i[0] * astride[0];
    401
    0
            float* const bp0 = bp + i[0] * bstride[0];
    402
    0
            float* const hp0 = hp + i[0] * hstride[0];
    403
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    404
    0
            {
    405
    0
              float* ap1 = ap0 + i[1] * astride[1];
    406
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    407
    0
              float* hp1 = hp0 + i[1] * hstride[1];
    408
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    409
    0
              {
    410
    0
                for (x = 0; x < dim[3]; x++)
    411
    0
                  hp1[x] = bp1[x] / ap1[x];
    412
    0
                ap1 += astride[2];
    413
    0
                bp1 += bstride[2];
    414
    0
                hp1 += hstride[2];
    415
    0
              }
    416
    0
            }
    417
    0
          }
    418
    0
        }
    419
    20.0k
      } else {
    420
    20.0k
        assert(ccv_nnc_tensor_nd(g->info.dim) <= CCV_NNC_MAX_DIM + 2);
    421
    20.0k
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    422
    20.0k
        ccv_nnc_tensor_view_get_dim(b, dim);
    423
    20.0k
        assert(ccv_nnc_tensor_view_check_dim(g, dim));
    424
    20.0k
        ccv_nnc_tensor_view_get_stride(b, bstride);
    425
    20.0k
        ccv_nnc_tensor_view_get_stride(g, gstride);
    426
    60.2k
        for (z = 0; z < output_size; z++40.1k)
    427
    40.1k
        {
    428
    40.1k
          ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[z + 1];
    429
    40.1k
          ccv_nnc_tensor_view_t* h = (ccv_nnc_tensor_view_t*)outputs[z];
    430
    40.1k
          assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    431
    40.1k
          assert(ccv_nnc_tensor_nd(h->info.dim) <= CCV_NNC_MAX_DIM + 2);
    432
    40.1k
          assert(ccv_nnc_tensor_view_check_dim(a, dim));
    433
    40.1k
          assert(ccv_nnc_tensor_view_check_dim(h, dim));
    434
    40.1k
          ccv_nnc_tensor_view_get_stride(a, astride);
    435
    40.1k
          ccv_nnc_tensor_view_get_stride(h, hstride);
    436
    40.1k
          if (!CCV_IS_TENSOR_VIEW(g) && !40.1kCCV_IS_TENSOR_VIEW40.1k(a) && !40.1kCCV_IS_TENSOR_VIEW40.1k(b) && !40.1kCCV_IS_TENSOR_VIEW40.1k(h))
    437
    40.1k
          {
    438
            // Super optimal case, just do one for-loop for sum.
    439
    40.1k
            const int tensor_count = ccv_nnc_tensor_count(g->info);
    440
    132k
            for (x = 0; x < tensor_count; x++91.8k)
    441
    91.8k
              h->data.f32[x] = g->data.f32[x] * b->data.f32[x] / a->data.f32[x];
    442
    40.1k
            continue;
    443
    40.1k
          }
    444
    40.1k
          assert(CCV_NNC_MAX_DIM == 2)3; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    445
    3
          int i[CCV_NNC_MAX_DIM + 2];
    446
    3
          float* const gp = g->data.f32;
    447
    3
          float* const ap = a->data.f32;
    448
    3
          float* const bp = b->data.f32;
    449
    3
          float* const hp = h->data.f32;
    450
    3
          const int count = dim[2] * dim[3];
    451
    3
          if (gstride[2] == dim[3] && astride[2] == dim[3] && bstride[2] == dim[3] && hstride[2] == dim[3])
    452
    3
          {
    453
            // Special casing if the ainc[3] is the same as dim[3]
    454
    6
            for (i[0] = 0; i[0] < dim[0]; i[0]++3)
    455
    3
            {
    456
    3
              float* gp0 = gp + i[0] * gstride[0];
    457
    3
              float* ap0 = ap + i[0] * astride[0];
    458
    3
              float* bp0 = bp + i[0] * bstride[0];
    459
    3
              float* hp0 = hp + i[0] * hstride[0];
    460
    6
              for (i[1] = 0; i[1] < dim[1]; i[1]++3)
    461
    3
              {
    462
    6
                for (x = 0; x < count; x++3)
    463
    3
                  hp0[x] = gp0[x] * bp0[x] / ap0[x];
    464
    3
                gp0 += gstride[1];
    465
    3
                ap0 += astride[1];
    466
    3
                bp0 += bstride[1];
    467
    3
                hp0 += hstride[1];
    468
    3
              }
    469
    3
            }
    470
    3
            continue;
    471
    3
          }
    472
          // Non-optimal case, need to do skip copy.
    473
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    474
    0
          {
    475
    0
            float* const gp0 = gp + i[0] * gstride[0];
    476
    0
            float* const ap0 = ap + i[0] * astride[0];
    477
    0
            float* const bp0 = bp + i[0] * bstride[0];
    478
    0
            float* const hp0 = hp + i[0] * hstride[0];
    479
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    480
    0
            {
    481
    0
              float* gp1 = gp0 + i[1] * gstride[1];
    482
    0
              float* ap1 = ap0 + i[1] * astride[1];
    483
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    484
    0
              float* hp1 = hp0 + i[1] * hstride[1];
    485
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    486
    0
              {
    487
    0
                for (x = 0; x < dim[3]; x++)
    488
    0
                  hp1[x] = gp1[x] * bp1[x] / ap1[x];
    489
    0
                gp1 += gstride[2];
    490
    0
                ap1 += astride[2];
    491
    0
                bp1 += bstride[2];
    492
    0
                hp1 += hstride[2];
    493
    0
              }
    494
    0
            }
    495
    0
          }
    496
    0
        }
    497
    20.0k
      }
    498
    20.0k
      return CCV_NNC_EXEC_SUCCESS;
    499
    20.0k
    }
    500
    501
    static void _ccv_nnc_ewdiv_forw_cpu_ref(const float p, ccv_nnc_tensor_view_t* const a, ccv_nnc_tensor_view_t* const b, ccv_nnc_tensor_view_t* const c)
    502
    261
    {
    503
      // Assuming this is float 32.
    504
    261
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    505
    261
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    506
    261
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    507
    261
      int cstride[CCV_NNC_MAX_DIM_ALLOC];
    508
    261
      if (a == 0) // Take 0 as all ones tensor.
    509
    21
      {
    510
    21
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    511
    21
        assert(ccv_nnc_tensor_nd(c->info.dim) <= CCV_NNC_MAX_DIM + 2);
    512
    21
        ccv_nnc_tensor_view_get_dim(b, dim);
    513
    21
        assert(ccv_nnc_tensor_view_check_dim(c, dim));
    514
    21
        int x;
    515
    21
        if (!CCV_IS_TENSOR_VIEW(b) && !CCV_IS_TENSOR_VIEW(c))
    516
    21
        {
    517
          // Super optimal case, just do one for-loop for sum.
    518
    21
          const int tensor_count = ccv_nnc_tensor_count(b->info);
    519
    2.00k
          for (x = 0; x < tensor_count; x++1.98k)
    520
    1.98k
            c->data.f32[x] = p / b->data.f32[x];
    521
    21
          return;
    522
    21
        }
    523
    21
        assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    524
    0
        ccv_nnc_tensor_view_get_stride(b, bstride);
    525
    0
        ccv_nnc_tensor_view_get_stride(c, cstride);
    526
    0
        int i[CCV_NNC_MAX_DIM + 2];
    527
    0
        float* const bp = b->data.f32;
    528
    0
        float* const cp = c->data.f32;
    529
    0
        const int count = dim[2] * dim[3];
    530
    0
        if (bstride[2] == dim[3] && cstride[2] == dim[3])
    531
    0
        {
    532
          // Special casing if the ainc[3] is the same as dim[3]
    533
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    534
    0
          {
    535
    0
            float* bp0 = bp + i[0] * bstride[0];
    536
    0
            float* cp0 = cp + i[0] * cstride[0];
    537
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    538
    0
            {
    539
    0
              for (x = 0; x < count; x++)
    540
    0
                cp0[x] = p / bp0[x];
    541
    0
              bp0 += bstride[1];
    542
    0
              cp0 += cstride[1];
    543
    0
            }
    544
    0
          }
    545
    0
          return;
    546
    0
        }
    547
        // Non-optimal case, need to do skip copy.
    548
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    549
    0
        {
    550
    0
          float* const bp0 = bp + i[0] * bstride[0];
    551
    0
          float* const cp0 = cp + i[0] * cstride[0];
    552
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    553
    0
          {
    554
    0
            float* bp1 = bp0 + i[1] * bstride[1];
    555
    0
            float* cp1 = cp0 + i[1] * cstride[1];
    556
    0
            for (i[2] = 0; i[2] < dim[2]; i[2]++)
    557
    0
            {
    558
    0
              for (x = 0; x < dim[3]; x++)
    559
    0
                cp1[x] = p / bp1[x];
    560
    0
              bp1 += bstride[2];
    561
    0
              cp1 += cstride[2];
    562
    0
            }
    563
    0
          }
    564
    0
        }
    565
    240
      } else {
    566
    240
        assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    567
    240
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    568
    240
        assert(ccv_nnc_tensor_nd(c->info.dim) <= CCV_NNC_MAX_DIM + 2);
    569
    240
        ccv_nnc_tensor_view_get_dim(a, dim);
    570
    240
        assert(ccv_nnc_tensor_view_check_dim(b, dim));
    571
    240
        assert(ccv_nnc_tensor_view_check_dim(c, dim));
    572
    240
        int x;
    573
    240
        if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b) && !CCV_IS_TENSOR_VIEW(c))
    574
    240
        {
    575
          // Super optimal case, just do one for-loop for sum.
    576
    240
          const int tensor_count = ccv_nnc_tensor_count(a->info);
    577
    7.05k
          for (x = 0; x < tensor_count; x++6.81k)
    578
    6.81k
            c->data.f32[x] = p * a->data.f32[x] / b->data.f32[x];
    579
    240
          return;
    580
    240
        }
    581
    240
        assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    582
    0
        ccv_nnc_tensor_view_get_stride(a, astride);
    583
    0
        ccv_nnc_tensor_view_get_stride(b, bstride);
    584
    0
        ccv_nnc_tensor_view_get_stride(c, cstride);
    585
    0
        int i[CCV_NNC_MAX_DIM + 2];
    586
    0
        float* const ap = a->data.f32;
    587
    0
        float* const bp = b->data.f32;
    588
    0
        float* const cp = c->data.f32;
    589
    0
        const int count = dim[2] * dim[3];
    590
    0
        if (astride[2] == dim[3] && bstride[2] == dim[3] && cstride[2] == dim[3])
    591
    0
        {
    592
          // Special casing if the ainc[3] is the same as dim[3]
    593
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    594
    0
          {
    595
    0
            float* ap0 = ap + i[0] * astride[0];
    596
    0
            float* bp0 = bp + i[0] * bstride[0];
    597
    0
            float* cp0 = cp + i[0] * cstride[0];
    598
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    599
    0
            {
    600
    0
              for (x = 0; x < count; x++)
    601
    0
                cp0[x] = p * ap0[x] / bp0[x];
    602
    0
              ap0 += astride[1];
    603
    0
              bp0 += bstride[1];
    604
    0
              cp0 += cstride[1];
    605
    0
            }
    606
    0
          }
    607
    0
          return;
    608
    0
        }
    609
        // Non-optimal case, need to do skip copy.
    610
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    611
    0
        {
    612
    0
          float* const ap0 = ap + i[0] * astride[0];
    613
    0
          float* const bp0 = bp + i[0] * bstride[0];
    614
    0
          float* const cp0 = cp + i[0] * cstride[0];
    615
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    616
    0
          {
    617
    0
            float* ap1 = ap0 + i[1] * astride[1];
    618
    0
            float* bp1 = bp0 + i[1] * bstride[1];
    619
    0
            float* cp1 = cp0 + i[1] * cstride[1];
    620
    0
            for (i[2] = 0; i[2] < dim[2]; i[2]++)
    621
    0
            {
    622
    0
              for (x = 0; x < dim[3]; x++)
    623
    0
                cp1[x] = p * ap1[x] / bp1[x];
    624
    0
              ap1 += astride[2];
    625
    0
              bp1 += bstride[2];
    626
    0
              cp1 += cstride[2];
    627
    0
            }
    628
    0
          }
    629
    0
        }
    630
    0
      }
    631
    261
    }
    632
    633
    static int _ccv_nnc_ewdiv_forw(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)
    634
    32
    {
    635
    32
      _ccv_nnc_ewdiv_forw_cpu_ref(1, (ccv_nnc_tensor_view_t*)inputs[0], (ccv_nnc_tensor_view_t*)inputs[1], (ccv_nnc_tensor_view_t*)outputs[0]);
    636
    32
      return CCV_NNC_EXEC_SUCCESS;
    637
    32
    }
    638
    639
    static int _ccv_nnc_ewdiv_back(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)
    640
    17
    {
    641
      // D[x / y, x] = 1 / y, D[x / y, y] = -x / y^2
    642
    17
      if (output_size == 1 || outputs[1] == 016)
    643
    2
      {
    644
        // When we only need D[x / y, x]
    645
    2
        _ccv_nnc_ewdiv_forw_cpu_ref(1, (ccv_nnc_tensor_view_t*)inputs[0], (ccv_nnc_tensor_view_t*)inputs[2], (ccv_nnc_tensor_view_t*)outputs[0]);
    646
    2
        return CCV_NNC_EXEC_SUCCESS;
    647
    2
      }
    648
    15
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    649
    15
      int gstride[CCV_NNC_MAX_DIM_ALLOC];
    650
    15
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    651
    15
      int cstride[CCV_NNC_MAX_DIM_ALLOC];
    652
    15
      int hastride[CCV_NNC_MAX_DIM_ALLOC];
    653
    15
      int hbstride[CCV_NNC_MAX_DIM_ALLOC];
    654
    15
      ccv_nnc_tensor_view_t* g = (ccv_nnc_tensor_view_t*)inputs[0];
    655
    15
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)inputs[2];
    656
    15
      ccv_nnc_tensor_view_t* c = (ccv_nnc_tensor_view_t*)inputs[3];
    657
    15
      ccv_nnc_tensor_view_t* ha = (ccv_nnc_tensor_view_t*)outputs[0];
    658
    15
      ccv_nnc_tensor_view_t* hb = (ccv_nnc_tensor_view_t*)outputs[1];
    659
    15
      if (g == 0)
    660
    0
      {
    661
    0
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    662
    0
        assert(ccv_nnc_tensor_nd(c->info.dim) <= CCV_NNC_MAX_DIM + 2);
    663
    0
        assert(ccv_nnc_tensor_nd(hb->info.dim) <= CCV_NNC_MAX_DIM + 2);
    664
    0
        ccv_nnc_tensor_view_get_dim(b, dim);
    665
    0
        assert(ccv_nnc_tensor_view_check_dim(c, dim));
    666
    0
        assert(ccv_nnc_tensor_view_check_dim(hb, dim));
    667
    0
        if (ha)
    668
    0
        {
    669
    0
          assert(ccv_nnc_tensor_nd(ha->info.dim) <= CCV_NNC_MAX_DIM + 2);
    670
    0
          assert(ccv_nnc_tensor_view_check_dim(ha, dim));
    671
    0
        }
    672
    0
        int x;
    673
    0
        if (!CCV_IS_TENSOR_VIEW(b) && !CCV_IS_TENSOR_VIEW(c) && (ha == 0 || !CCV_IS_TENSOR_VIEW(ha)) && !CCV_IS_TENSOR_VIEW(hb))
    674
    0
        {
    675
          // Super optimal case, just do one for-loop for sum.
    676
    0
          const int tensor_count = ccv_nnc_tensor_count(b->info);
    677
    0
          if (ha == 0)
    678
    0
          {
    679
    0
            for (x = 0; x < tensor_count; x++)
    680
    0
            {
    681
    0
              const float v = 1 / b->data.f32[x];
    682
    0
              hb->data.f32[x] = -c->data.f32[x] * v;
    683
    0
            }
    684
    0
          } else {
    685
    0
            for (x = 0; x < tensor_count; x++)
    686
    0
            {
    687
    0
              const float v = 1 / b->data.f32[x];
    688
    0
              ha->data.f32[x] = v;
    689
    0
              hb->data.f32[x] = -c->data.f32[x] * v;
    690
    0
            }
    691
    0
          }
    692
    0
          return CCV_NNC_EXEC_SUCCESS;
    693
    0
        }
    694
    0
        assert(CCV_NNC_MAX_DIM == 2); // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    695
    0
        ccv_nnc_tensor_view_get_stride(b, bstride);
    696
    0
        ccv_nnc_tensor_view_get_stride(c, cstride);
    697
    0
        ccv_nnc_tensor_view_get_stride(hb, hbstride);
    698
    0
        int i[CCV_NNC_MAX_DIM + 2];
    699
    0
        float* const bp = b->data.f32;
    700
    0
        float* const cp = c->data.f32;
    701
    0
        float* const hbp = hb->data.f32;
    702
    0
        const int count = dim[2] * dim[3];
    703
    0
        if (ha == 0)
    704
    0
        {
    705
    0
          if (bstride[2] == dim[3] && cstride[2] == dim[3] && hbstride[2] == dim[3])
    706
    0
          {
    707
            // Special casing if the ainc[3] is the same as dim[3]
    708
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    709
    0
            {
    710
    0
              float* bp0 = bp + i[0] * bstride[0];
    711
    0
              float* cp0 = cp + i[0] * cstride[0];
    712
    0
              float* hbp0 = hbp + i[0] * hbstride[0];
    713
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    714
    0
              {
    715
    0
                for (x = 0; x < count; x++)
    716
    0
                {
    717
    0
                  const float v = 1 / bp0[x];
    718
    0
                  hbp0[x] = -cp0[x] * v;
    719
    0
                }
    720
    0
                bp0 += bstride[1];
    721
    0
                cp0 += cstride[1];
    722
    0
                hbp0 += hbstride[1];
    723
    0
              }
    724
    0
            }
    725
    0
            return CCV_NNC_EXEC_SUCCESS;
    726
    0
          }
    727
          // Non-optimal case, need to do skip copy.
    728
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    729
    0
          {
    730
    0
            float* const bp0 = bp + i[0] * bstride[0];
    731
    0
            float* const cp0 = cp + i[0] * cstride[0];
    732
    0
            float* const hbp0 = hbp + i[0] * hbstride[0];
    733
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    734
    0
            {
    735
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    736
    0
              float* cp1 = cp0 + i[1] * cstride[1];
    737
    0
              float* hbp1 = hbp0 + i[1] * hbstride[1];
    738
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    739
    0
              {
    740
    0
                for (x = 0; x < dim[3]; x++)
    741
    0
                {
    742
    0
                  const float v = 1 / bp1[x];
    743
    0
                  hbp1[x] = -cp1[x] * v;
    744
    0
                }
    745
    0
                bp1 += bstride[2];
    746
    0
                cp1 += cstride[2];
    747
    0
                hbp1 += hbstride[2];
    748
    0
              }
    749
    0
            }
    750
    0
          }
    751
    0
        } else {
    752
    0
          float* const hap = ha->data.f32;
    753
    0
          ccv_nnc_tensor_view_get_stride(ha, hastride);
    754
    0
          if (bstride[2] == dim[3] && cstride[2] == dim[3] && hastride[2] == dim[3] && hbstride[2] == dim[3])
    755
    0
          {
    756
            // Special casing if the ainc[3] is the same as dim[3]
    757
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    758
    0
            {
    759
    0
              float* bp0 = bp + i[0] * bstride[0];
    760
    0
              float* cp0 = cp + i[0] * cstride[0];
    761
    0
              float* hap0 = hap + i[0] * hastride[0];
    762
    0
              float* hbp0 = hbp + i[0] * hbstride[0];
    763
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    764
    0
              {
    765
    0
                for (x = 0; x < count; x++)
    766
    0
                {
    767
    0
                  const float v = 1 / bp0[x];
    768
    0
                  hap0[x] = v;
    769
    0
                  hbp0[x] = -cp0[x] * v;
    770
    0
                }
    771
    0
                bp0 += bstride[1];
    772
    0
                cp0 += cstride[1];
    773
    0
                hap0 += hastride[1];
    774
    0
                hbp0 += hbstride[1];
    775
    0
              }
    776
    0
            }
    777
    0
            return CCV_NNC_EXEC_SUCCESS;
    778
    0
          }
    779
          // Non-optimal case, need to do skip copy.
    780
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    781
    0
          {
    782
    0
            float* const bp0 = bp + i[0] * bstride[0];
    783
    0
            float* const cp0 = cp + i[0] * cstride[0];
    784
    0
            float* const hap0 = hap + i[0] * hastride[0];
    785
    0
            float* const hbp0 = hbp + i[0] * hbstride[0];
    786
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    787
    0
            {
    788
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    789
    0
              float* cp1 = cp0 + i[1] * cstride[1];
    790
    0
              float* hap1 = hap0 + i[1] * hastride[1];
    791
    0
              float* hbp1 = hbp0 + i[1] * hbstride[1];
    792
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    793
    0
              {
    794
    0
                for (x = 0; x < dim[3]; x++)
    795
    0
                {
    796
    0
                  const float v = 1 / bp1[x];
    797
    0
                  hap1[x] = v;
    798
    0
                  hbp1[x] = -cp1[x] * v;
    799
    0
                }
    800
    0
                bp1 += bstride[2];
    801
    0
                cp1 += cstride[2];
    802
    0
                hap1 += hastride[2];
    803
    0
                hbp1 += hbstride[2];
    804
    0
              }
    805
    0
            }
    806
    0
          }
    807
    0
        }
    808
    15
      } else {
    809
    15
        assert(ccv_nnc_tensor_nd(g->info.dim) <= CCV_NNC_MAX_DIM + 2);
    810
    15
        assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    811
    15
        assert(ccv_nnc_tensor_nd(c->info.dim) <= CCV_NNC_MAX_DIM + 2);
    812
    15
        assert(ccv_nnc_tensor_nd(hb->info.dim) <= CCV_NNC_MAX_DIM + 2);
    813
    15
        ccv_nnc_tensor_view_get_dim(b, dim);
    814
    15
        assert(ccv_nnc_tensor_view_check_dim(g, dim));
    815
    15
        assert(ccv_nnc_tensor_view_check_dim(c, dim));
    816
    15
        assert(ccv_nnc_tensor_view_check_dim(hb, dim));
    817
    15
        if (ha)
    818
    1
        {
    819
    1
          assert(ccv_nnc_tensor_nd(ha->info.dim) <= CCV_NNC_MAX_DIM + 2);
    820
    1
          assert(ccv_nnc_tensor_view_check_dim(ha, dim));
    821
    1
        }
    822
    15
        int x;
    823
    15
        if (!CCV_IS_TENSOR_VIEW(g) && !CCV_IS_TENSOR_VIEW(b) && !CCV_IS_TENSOR_VIEW(c) && (ha == 0 || !1CCV_IS_TENSOR_VIEW1(ha)) && !CCV_IS_TENSOR_VIEW(hb))
    824
    15
        {
    825
          // Super optimal case, just do one for-loop for sum.
    826
    15
          const int tensor_count = ccv_nnc_tensor_count(g->info);
    827
    15
          if (ha == 0)
    828
    14
          {
    829
    1.50k
            for (x = 0; x < tensor_count; x++1.49k)
    830
    1.49k
            {
    831
    1.49k
              const float v = g->data.f32[x] / b->data.f32[x];
    832
    1.49k
              hb->data.f32[x] = -c->data.f32[x] * v;
    833
    1.49k
            }
    834
    14
          } else {
    835
    2
            for (x = 0; x < tensor_count; x++1)
    836
    1
            {
    837
    1
              const float v = g->data.f32[x] / b->data.f32[x];
    838
    1
              ha->data.f32[x] = v;
    839
    1
              hb->data.f32[x] = -c->data.f32[x] * v;
    840
    1
            }
    841
    1
          }
    842
    15
          return CCV_NNC_EXEC_SUCCESS;
    843
    15
        }
    844
    15
        assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    845
    0
        ccv_nnc_tensor_view_get_stride(g, gstride);
    846
    0
        ccv_nnc_tensor_view_get_stride(b, bstride);
    847
    0
        ccv_nnc_tensor_view_get_stride(c, cstride);
    848
    0
        ccv_nnc_tensor_view_get_stride(hb, hbstride);
    849
    0
        int i[CCV_NNC_MAX_DIM + 2];
    850
    0
        float* const gp = g->data.f32;
    851
    0
        float* const bp = b->data.f32;
    852
    0
        float* const cp = c->data.f32;
    853
    0
        float* const hbp = hb->data.f32;
    854
    0
        const int count = dim[2] * dim[3];
    855
    0
        if (ha == 0)
    856
    0
        {
    857
    0
          if (gstride[2] == dim[3] && bstride[2] == dim[3] && cstride[2] == dim[3] && hbstride[2] == dim[3])
    858
    0
          {
    859
            // Special casing if the ainc[3] is the same as dim[3]
    860
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    861
    0
            {
    862
    0
              float* gp0 = gp + i[0] * gstride[0];
    863
    0
              float* bp0 = bp + i[0] * bstride[0];
    864
    0
              float* cp0 = cp + i[0] * cstride[0];
    865
    0
              float* hbp0 = hbp + i[0] * hbstride[0];
    866
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    867
    0
              {
    868
    0
                for (x = 0; x < count; x++)
    869
    0
                {
    870
    0
                  const float v = gp0[x] / bp0[x];
    871
    0
                  hbp0[x] = -cp0[x] * v;
    872
    0
                }
    873
    0
                gp0 += gstride[1];
    874
    0
                bp0 += bstride[1];
    875
    0
                cp0 += cstride[1];
    876
    0
                hbp0 += hbstride[1];
    877
    0
              }
    878
    0
            }
    879
    0
            return CCV_NNC_EXEC_SUCCESS;
    880
    0
          }
    881
          // Non-optimal case, need to do skip copy.
    882
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    883
    0
          {
    884
    0
            float* const gp0 = gp + i[0] * gstride[0];
    885
    0
            float* const bp0 = bp + i[0] * bstride[0];
    886
    0
            float* const cp0 = cp + i[0] * cstride[0];
    887
    0
            float* const hbp0 = hbp + i[0] * hbstride[0];
    888
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    889
    0
            {
    890
    0
              float* gp1 = gp0 + i[1] * gstride[1];
    891
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    892
    0
              float* cp1 = cp0 + i[1] * cstride[1];
    893
    0
              float* hbp1 = hbp0 + i[1] * hbstride[1];
    894
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    895
    0
              {
    896
    0
                for (x = 0; x < dim[3]; x++)
    897
    0
                {
    898
    0
                  const float v = gp1[x] / bp1[x];
    899
    0
                  hbp1[x] = -cp1[x] * v;
    900
    0
                }
    901
    0
                gp1 += gstride[2];
    902
    0
                bp1 += bstride[2];
    903
    0
                cp1 += cstride[2];
    904
    0
                hbp1 += hbstride[2];
    905
    0
              }
    906
    0
            }
    907
    0
          }
    908
    0
        } else {
    909
    0
          ccv_nnc_tensor_view_get_stride(ha, hastride);
    910
    0
          float* const hap = ha->data.f32;
    911
    0
          if (gstride[2] == dim[3] && bstride[2] == dim[3] && cstride[2] == dim[3] && hastride[2] == dim[3] && hbstride[2] == dim[3])
    912
    0
          {
    913
            // Special casing if the ainc[3] is the same as dim[3]
    914
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    915
    0
            {
    916
    0
              float* gp0 = gp + i[0] * gstride[0];
    917
    0
              float* bp0 = bp + i[0] * bstride[0];
    918
    0
              float* cp0 = cp + i[0] * cstride[0];
    919
    0
              float* hap0 = hap + i[0] * hastride[0];
    920
    0
              float* hbp0 = hbp + i[0] * hbstride[0];
    921
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    922
    0
              {
    923
    0
                for (x = 0; x < count; x++)
    924
    0
                {
    925
    0
                  const float v = gp0[x] / bp0[x];
    926
    0
                  hap0[x] = v;
    927
    0
                  hbp0[x] = -cp0[x] * v;
    928
    0
                }
    929
    0
                gp0 += gstride[1];
    930
    0
                bp0 += bstride[1];
    931
    0
                cp0 += cstride[1];
    932
    0
                hap0 += hastride[1];
    933
    0
                hbp0 += hbstride[1];
    934
    0
              }
    935
    0
            }
    936
    0
            return CCV_NNC_EXEC_SUCCESS;
    937
    0
          }
    938
          // Non-optimal case, need to do skip copy.
    939
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    940
    0
          {
    941
    0
            float* const gp0 = gp + i[0] * gstride[0];
    942
    0
            float* const bp0 = bp + i[0] * bstride[0];
    943
    0
            float* const cp0 = cp + i[0] * cstride[0];
    944
    0
            float* const hap0 = hap + i[0] * hastride[0];
    945
    0
            float* const hbp0 = hbp + i[0] * hbstride[0];
    946
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    947
    0
            {
    948
    0
              float* gp1 = gp0 + i[1] * gstride[1];
    949
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    950
    0
              float* cp1 = cp0 + i[1] * cstride[1];
    951
    0
              float* hap1 = hap0 + i[1] * hastride[1];
    952
    0
              float* hbp1 = hbp0 + i[1] * hbstride[1];
    953
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    954
    0
              {
    955
    0
                for (x = 0; x < dim[3]; x++)
    956
    0
                {
    957
    0
                  const float v = gp1[x] / bp1[x];
    958
    0
                  hap1[x] = v;
    959
    0
                  hbp1[x] = -cp1[x] * v;
    960
    0
                }
    961
    0
                gp1 += gstride[2];
    962
    0
                bp1 += bstride[2];
    963
    0
                cp1 += cstride[2];
    964
    0
                hap1 += hastride[2];
    965
    0
                hbp1 += hbstride[2];
    966
    0
              }
    967
    0
            }
    968
    0
          }
    969
    0
        }
    970
    0
      }
    971
    0
      return CCV_NNC_EXEC_SUCCESS;
    972
    15
    }
    973
    974
    static int _ccv_nnc_ewexp_forw(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)
    975
    26
    {
    976
      // Assuming this is float 32.
    977
    26
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    978
    26
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    979
    26
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    980
    26
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[0];
    981
    26
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    982
    26
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    983
    26
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    984
    26
      ccv_nnc_tensor_view_get_dim(a, dim);
    985
    26
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    986
    26
      int x;
    987
    26
      if (!CCV_IS_TENSOR_VIEW(a) && !25CCV_IS_TENSOR_VIEW25(b))
    988
    25
      {
    989
        // Super optimal case, just do one for-loop for sum.
    990
    25
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    991
    5.10k
        for (x = 0; x < tensor_count; x++5.07k)
    992
    5.07k
          b->data.f32[x] = exp(a->data.f32[x]);
    993
    25
        return CCV_NNC_EXEC_SUCCESS;
    994
    25
      }
    995
    26
      assert(CCV_NNC_MAX_DIM == 2)1; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    996
    1
      ccv_nnc_tensor_view_get_stride(a, astride);
    997
    1
      ccv_nnc_tensor_view_get_stride(b, bstride);
    998
    1
      int i[CCV_NNC_MAX_DIM + 2];
    999
    1
      float* const ap = a->data.f32;
    1000
    1
      float* const bp = b->data.f32;
    1001
    1
      const int count = dim[2] * dim[3];
    1002
    1
      if (astride[2] == dim[3] && bstride[2] == dim[3])
    1003
    1
      {
    1004
        // Special casing if the ainc[3] is the same as dim[3]
    1005
    2
        for (i[0] = 0; i[0] < dim[0]; i[0]++1)
    1006
    1
        {
    1007
    1
          float* ap0 = ap + i[0] * astride[0];
    1008
    1
          float* bp0 = bp + i[0] * bstride[0];
    1009
    2
          for (i[1] = 0; i[1] < dim[1]; i[1]++1)
    1010
    1
          {
    1011
    2
            for (x = 0; x < count; x++1)
    1012
    1
              bp0[x] = exp(ap0[x]);
    1013
    1
            ap0 += astride[1];
    1014
    1
            bp0 += bstride[1];
    1015
    1
          }
    1016
    1
        }
    1017
    1
        return CCV_NNC_EXEC_SUCCESS;
    1018
    1
      }
    1019
      // Non-optimal case, need to do skip copy.
    1020
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1021
    0
      {
    1022
    0
        float* const ap0 = ap + i[0] * astride[0];
    1023
    0
        float* const bp0 = bp + i[0] * bstride[0];
    1024
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1025
    0
        {
    1026
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1027
    0
          float* bp1 = bp0 + i[1] * bstride[1];
    1028
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1029
    0
          {
    1030
    0
            for (x = 0; x < dim[3]; x++)
    1031
    0
              bp1[x] = exp(ap1[x]);
    1032
    0
            ap1 += astride[2];
    1033
    0
            bp1 += bstride[2];
    1034
    0
          }
    1035
    0
        }
    1036
    0
      }
    1037
    0
      return CCV_NNC_EXEC_SUCCESS;
    1038
    1
    }
    1039
    1040
    static int _ccv_nnc_ewexp_back(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)
    1041
    12
    {
    1042
      // D[Exp[x], x] = Exp[x]
    1043
    12
      if (inputs[0] == 0)
    1044
    0
        _ccv_nnc_tensor_transfer_cpu_ref_f32((ccv_nnc_tensor_view_t*)inputs[2], (ccv_nnc_tensor_view_t*)outputs[0]);
    1045
    12
      else
    1046
    12
        _ccv_nnc_ewprod_forw_cpu_ref((ccv_nnc_tensor_view_t*[]){
    1047
    12
          (ccv_nnc_tensor_view_t*)inputs[0], (ccv_nnc_tensor_view_t*)inputs[2]
    1048
    12
        }, 2, (ccv_nnc_tensor_view_t**)outputs, output_size);
    1049
    12
      return CCV_NNC_EXEC_SUCCESS;
    1050
    12
    }
    1051
    1052
    static inline float _ccv_nnc_softplusf(const float x)
    1053
    4.00k
    {
    1054
    4.00k
      return (x > 0) ? x + log1pf(expf(-x))2.03k : log1pf(expf(x))1.96k;
    1055
    4.00k
    }
    1056
    1057
    static inline float _ccv_nnc_softplus_sigmoidf(const float x)
    1058
    3.00k
    {
    1059
    3.00k
      if (x >= 0)
    1060
    1.52k
      {
    1061
    1.52k
        const float z = expf(-x);
    1062
    1.52k
        return 1. / (1. + z);
    1063
    1.52k
      }
    1064
    1.47k
      const float z = expf(x);
    1065
    1.47k
      return z / (1. + z);
    1066
    3.00k
    }
    1067
    1068
    static int _ccv_nnc_ewsoftplus_forw(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)
    1069
    5
    {
    1070
      // Assuming this is float 32.
    1071
    5
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1072
    5
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1073
    5
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    1074
    5
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[0];
    1075
    5
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    1076
    5
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1077
    5
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1078
    5
      ccv_nnc_tensor_view_get_dim(a, dim);
    1079
    5
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    1080
    5
      int x;
    1081
    5
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b))
    1082
    5
      {
    1083
    5
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1084
    4.00k
        for (x = 0; x < tensor_count; x++4.00k)
    1085
    4.00k
          b->data.f32[x] = _ccv_nnc_softplusf(a->data.f32[x]);
    1086
    5
        return CCV_NNC_EXEC_SUCCESS;
    1087
    5
      }
    1088
    5
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1089
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1090
    0
      ccv_nnc_tensor_view_get_stride(b, bstride);
    1091
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1092
    0
      float* const ap = a->data.f32;
    1093
    0
      float* const bp = b->data.f32;
    1094
    0
      const int count = dim[2] * dim[3];
    1095
    0
      if (astride[2] == dim[3] && bstride[2] == dim[3])
    1096
    0
      {
    1097
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1098
    0
        {
    1099
    0
          float* ap0 = ap + i[0] * astride[0];
    1100
    0
          float* bp0 = bp + i[0] * bstride[0];
    1101
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1102
    0
          {
    1103
    0
            for (x = 0; x < count; x++)
    1104
    0
              bp0[x] = _ccv_nnc_softplusf(ap0[x]);
    1105
    0
            ap0 += astride[1];
    1106
    0
            bp0 += bstride[1];
    1107
    0
          }
    1108
    0
        }
    1109
    0
        return CCV_NNC_EXEC_SUCCESS;
    1110
    0
      }
    1111
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1112
    0
      {
    1113
    0
        float* const ap0 = ap + i[0] * astride[0];
    1114
    0
        float* const bp0 = bp + i[0] * bstride[0];
    1115
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1116
    0
        {
    1117
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1118
    0
          float* bp1 = bp0 + i[1] * bstride[1];
    1119
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1120
    0
          {
    1121
    0
            for (x = 0; x < dim[3]; x++)
    1122
    0
              bp1[x] = _ccv_nnc_softplusf(ap1[x]);
    1123
    0
            ap1 += astride[2];
    1124
    0
            bp1 += bstride[2];
    1125
    0
          }
    1126
    0
        }
    1127
    0
      }
    1128
    0
      return CCV_NNC_EXEC_SUCCESS;
    1129
    0
    }
    1130
    1131
    static int _ccv_nnc_ewsoftplus_back(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)
    1132
    3
    {
    1133
      // D[Softplus[x], x] = Sigmoid[x]
    1134
    3
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1135
    3
      int gstride[CCV_NNC_MAX_DIM_ALLOC];
    1136
    3
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1137
    3
      int hstride[CCV_NNC_MAX_DIM_ALLOC];
    1138
    3
      ccv_nnc_tensor_view_t* g = (ccv_nnc_tensor_view_t*)inputs[0];
    1139
    3
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[1];
    1140
    3
      ccv_nnc_tensor_view_t* h = (ccv_nnc_tensor_view_t*)outputs[0];
    1141
    3
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1142
    3
      assert(ccv_nnc_tensor_nd(h->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1143
    3
      ccv_nnc_tensor_view_get_dim(a, dim);
    1144
    3
      assert(ccv_nnc_tensor_view_check_dim(h, dim));
    1145
    3
      if (g)
    1146
    3
      {
    1147
    3
        assert(ccv_nnc_tensor_nd(g->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1148
    3
        assert(ccv_nnc_tensor_view_check_dim(g, dim));
    1149
    3
      }
    1150
    3
      int x;
    1151
    3
      if ((!g || !CCV_IS_TENSOR_VIEW(g)) && !CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(h))
    1152
    3
      {
    1153
    3
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1154
    3
        if (g)
    1155
    3
        {
    1156
    3.00k
          for (x = 0; x < tensor_count; x++3.00k)
    1157
    3.00k
            h->data.f32[x] = g->data.f32[x] * _ccv_nnc_softplus_sigmoidf(a->data.f32[x]);
    1158
    3
        } else {
    1159
    0
          for (x = 0; x < tensor_count; x++)
    1160
    0
            h->data.f32[x] = _ccv_nnc_softplus_sigmoidf(a->data.f32[x]);
    1161
    0
        }
    1162
    3
        return CCV_NNC_EXEC_SUCCESS;
    1163
    3
      }
    1164
    3
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1165
    0
      if (g)
    1166
    0
        ccv_nnc_tensor_view_get_stride(g, gstride);
    1167
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1168
    0
      ccv_nnc_tensor_view_get_stride(h, hstride);
    1169
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1170
    0
      float* const gp = g ? g->data.f32 : 0;
    1171
    0
      float* const ap = a->data.f32;
    1172
    0
      float* const hp = h->data.f32;
    1173
    0
      const int count = dim[2] * dim[3];
    1174
    0
      if ((!g || gstride[2] == dim[3]) && astride[2] == dim[3] && hstride[2] == dim[3])
    1175
    0
      {
    1176
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1177
    0
        {
    1178
    0
          float* gp0 = g ? gp + i[0] * gstride[0] : 0;
    1179
    0
          float* ap0 = ap + i[0] * astride[0];
    1180
    0
          float* hp0 = hp + i[0] * hstride[0];
    1181
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1182
    0
          {
    1183
    0
            if (g)
    1184
    0
            {
    1185
    0
              for (x = 0; x < count; x++)
    1186
    0
                hp0[x] = gp0[x] * _ccv_nnc_softplus_sigmoidf(ap0[x]);
    1187
    0
              gp0 += gstride[1];
    1188
    0
            } else {
    1189
    0
              for (x = 0; x < count; x++)
    1190
    0
                hp0[x] = _ccv_nnc_softplus_sigmoidf(ap0[x]);
    1191
    0
            }
    1192
    0
            ap0 += astride[1];
    1193
    0
            hp0 += hstride[1];
    1194
    0
          }
    1195
    0
        }
    1196
    0
        return CCV_NNC_EXEC_SUCCESS;
    1197
    0
      }
    1198
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1199
    0
      {
    1200
    0
        float* const gp0 = g ? gp + i[0] * gstride[0] : 0;
    1201
    0
        float* const ap0 = ap + i[0] * astride[0];
    1202
    0
        float* const hp0 = hp + i[0] * hstride[0];
    1203
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1204
    0
        {
    1205
    0
          float* gp1 = g ? gp0 + i[1] * gstride[1] : 0;
    1206
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1207
    0
          float* hp1 = hp0 + i[1] * hstride[1];
    1208
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1209
    0
          {
    1210
    0
            if (g)
    1211
    0
            {
    1212
    0
              for (x = 0; x < dim[3]; x++)
    1213
    0
                hp1[x] = gp1[x] * _ccv_nnc_softplus_sigmoidf(ap1[x]);
    1214
    0
              gp1 += gstride[2];
    1215
    0
            } else {
    1216
    0
              for (x = 0; x < dim[3]; x++)
    1217
    0
                hp1[x] = _ccv_nnc_softplus_sigmoidf(ap1[x]);
    1218
    0
            }
    1219
    0
            ap1 += astride[2];
    1220
    0
            hp1 += hstride[2];
    1221
    0
          }
    1222
    0
        }
    1223
    0
      }
    1224
    0
      return CCV_NNC_EXEC_SUCCESS;
    1225
    0
    }
    1226
    1227
    static void _ccv_nnc_ewpow_forw_cpu_ref(ccv_nnc_tensor_view_t* const a, const float exp, ccv_nnc_tensor_view_t* const c)
    1228
    4
    {
    1229
      // Assuming this is float 32.
    1230
    4
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1231
    4
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1232
    4
      int cstride[CCV_NNC_MAX_DIM_ALLOC];
    1233
    4
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1234
    4
      assert(ccv_nnc_tensor_nd(c->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1235
    4
      ccv_nnc_tensor_view_get_dim(a, dim);
    1236
    4
      assert(ccv_nnc_tensor_view_check_dim(c, dim));
    1237
    4
      int x;
    1238
    4
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(c))
    1239
    4
      {
    1240
    4
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1241
    2.00k
        for (x = 0; x < tensor_count; x++2.00k)
    1242
    2.00k
          c->data.f32[x] = powf(a->data.f32[x], exp);
    1243
    4
        return;
    1244
    4
      }
    1245
    4
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1246
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1247
    0
      ccv_nnc_tensor_view_get_stride(c, cstride);
    1248
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1249
    0
      float* const ap = a->data.f32;
    1250
    0
      float* const cp = c->data.f32;
    1251
    0
      const int count = dim[2] * dim[3];
    1252
    0
      if (astride[2] == dim[3] && cstride[2] == dim[3])
    1253
    0
      {
    1254
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1255
    0
        {
    1256
    0
          float* ap0 = ap + i[0] * astride[0];
    1257
    0
          float* cp0 = cp + i[0] * cstride[0];
    1258
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1259
    0
          {
    1260
    0
            for (x = 0; x < count; x++)
    1261
    0
              cp0[x] = powf(ap0[x], exp);
    1262
    0
            ap0 += astride[1];
    1263
    0
            cp0 += cstride[1];
    1264
    0
          }
    1265
    0
        }
    1266
    0
        return;
    1267
    0
      }
    1268
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1269
    0
      {
    1270
    0
        float* const ap0 = ap + i[0] * astride[0];
    1271
    0
        float* const cp0 = cp + i[0] * cstride[0];
    1272
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1273
    0
        {
    1274
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1275
    0
          float* cp1 = cp0 + i[1] * cstride[1];
    1276
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1277
    0
          {
    1278
    0
            for (x = 0; x < dim[3]; x++)
    1279
    0
              cp1[x] = powf(ap1[x], exp);
    1280
    0
            ap1 += astride[2];
    1281
    0
            cp1 += cstride[2];
    1282
    0
          }
    1283
    0
        }
    1284
    0
      }
    1285
    0
    }
    1286
    1287
    static int _ccv_nnc_ewpow_forw(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)
    1288
    4
    {
    1289
    4
      _ccv_nnc_ewpow_forw_cpu_ref((ccv_nnc_tensor_view_t*)inputs[0], cmd.info.pow.exponent, (ccv_nnc_tensor_view_t*)outputs[0]);
    1290
    4
      return CCV_NNC_EXEC_SUCCESS;
    1291
    4
    }
    1292
    1293
    static void _ccv_nnc_ewpow_back_da_cpu_ref(ccv_nnc_tensor_view_t* const g, ccv_nnc_tensor_view_t* const a, const float exp, ccv_nnc_tensor_view_t* const h)
    1294
    3
    {
    1295
      // D[pow(a, exp), a] = exp * pow(a, exp - 1)
    1296
    3
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1297
    3
      int gstride[CCV_NNC_MAX_DIM_ALLOC];
    1298
    3
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1299
    3
      int hstride[CCV_NNC_MAX_DIM_ALLOC];
    1300
    3
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1301
    3
      assert(ccv_nnc_tensor_nd(h->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1302
    3
      ccv_nnc_tensor_view_get_dim(a, dim);
    1303
    3
      assert(ccv_nnc_tensor_view_check_dim(h, dim));
    1304
    3
      if (g)
    1305
    2
      {
    1306
    2
        assert(ccv_nnc_tensor_nd(g->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1307
    2
        assert(ccv_nnc_tensor_view_check_dim(g, dim));
    1308
    2
      }
    1309
    3
      int x;
    1310
    3
      if ((!g || !2CCV_IS_TENSOR_VIEW2(g)) && !CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(h))
    1311
    3
      {
    1312
    3
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1313
    3
        if (g)
    1314
    2
        {
    1315
    1.00k
          for (x = 0; x < tensor_count; x++1.00k)
    1316
    1.00k
            h->data.f32[x] = g->data.f32[x] * exp * powf(a->data.f32[x], exp - 1);
    1317
    2
        } else {
    1318
    2
          for (x = 0; x < tensor_count; x++1)
    1319
    1
            h->data.f32[x] = exp * powf(a->data.f32[x], exp - 1);
    1320
    1
        }
    1321
    3
        return;
    1322
    3
      }
    1323
    3
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1324
    0
      if (g)
    1325
    0
        ccv_nnc_tensor_view_get_stride(g, gstride);
    1326
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1327
    0
      ccv_nnc_tensor_view_get_stride(h, hstride);
    1328
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1329
    0
      float* const gp = g ? g->data.f32 : 0;
    1330
    0
      float* const ap = a->data.f32;
    1331
    0
      float* const hp = h->data.f32;
    1332
    0
      const int count = dim[2] * dim[3];
    1333
    0
      if ((!g || gstride[2] == dim[3]) && astride[2] == dim[3] && hstride[2] == dim[3])
    1334
    0
      {
    1335
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1336
    0
        {
    1337
    0
          float* gp0 = g ? gp + i[0] * gstride[0] : 0;
    1338
    0
          float* ap0 = ap + i[0] * astride[0];
    1339
    0
          float* hp0 = hp + i[0] * hstride[0];
    1340
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1341
    0
          {
    1342
    0
            if (g)
    1343
    0
            {
    1344
    0
              for (x = 0; x < count; x++)
    1345
    0
                hp0[x] = gp0[x] * exp * powf(ap0[x], exp - 1);
    1346
    0
              gp0 += gstride[1];
    1347
    0
            } else {
    1348
    0
              for (x = 0; x < count; x++)
    1349
    0
                hp0[x] = exp * powf(ap0[x], exp - 1);
    1350
    0
            }
    1351
    0
            ap0 += astride[1];
    1352
    0
            hp0 += hstride[1];
    1353
    0
          }
    1354
    0
        }
    1355
    0
        return;
    1356
    0
      }
    1357
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1358
    0
      {
    1359
    0
        float* const gp0 = g ? gp + i[0] * gstride[0] : 0;
    1360
    0
        float* const ap0 = ap + i[0] * astride[0];
    1361
    0
        float* const hp0 = hp + i[0] * hstride[0];
    1362
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1363
    0
        {
    1364
    0
          float* gp1 = g ? gp0 + i[1] * gstride[1] : 0;
    1365
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1366
    0
          float* hp1 = hp0 + i[1] * hstride[1];
    1367
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1368
    0
          {
    1369
    0
            if (g)
    1370
    0
            {
    1371
    0
              for (x = 0; x < dim[3]; x++)
    1372
    0
                hp1[x] = gp1[x] * exp * powf(ap1[x], exp - 1);
    1373
    0
              gp1 += gstride[2];
    1374
    0
            } else {
    1375
    0
              for (x = 0; x < dim[3]; x++)
    1376
    0
                hp1[x] = exp * powf(ap1[x], exp - 1);
    1377
    0
            }
    1378
    0
            ap1 += astride[2];
    1379
    0
            hp1 += hstride[2];
    1380
    0
          }
    1381
    0
        }
    1382
    0
      }
    1383
    0
    }
    1384
    1385
    static int _ccv_nnc_ewpow_back(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)
    1386
    3
    {
    1387
    3
      ccv_nnc_tensor_view_t* const g = (ccv_nnc_tensor_view_t*)inputs[0];
    1388
    3
      ccv_nnc_tensor_view_t* const a = (ccv_nnc_tensor_view_t*)inputs[1];
    1389
    3
      if (output_size > 0 && outputs[0])
    1390
    3
        _ccv_nnc_ewpow_back_da_cpu_ref(g, a, cmd.info.pow.exponent, (ccv_nnc_tensor_view_t*)outputs[0]);
    1391
    3
      return CCV_NNC_EXEC_SUCCESS;
    1392
    3
    }
    1393
    1394
    static int _ccv_nnc_ewlog_forw(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)
    1395
    261
    {
    1396
      // Assuming this is float 32.
    1397
    261
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1398
    261
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1399
    261
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    1400
    261
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[0];
    1401
    261
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    1402
    261
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1403
    261
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1404
    261
      ccv_nnc_tensor_view_get_dim(a, dim);
    1405
    261
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    1406
    261
      int x;
    1407
    261
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b))
    1408
    261
      {
    1409
        // Super optimal case, just do one for-loop for sum.
    1410
    261
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1411
    3.57k
        for (x = 0; x < tensor_count; x++3.31k)
    1412
    3.31k
          b->data.f32[x] = log(a->data.f32[x]);
    1413
    261
        return CCV_NNC_EXEC_SUCCESS;
    1414
    261
      }
    1415
    261
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1416
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1417
    0
      ccv_nnc_tensor_view_get_stride(b, bstride);
    1418
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1419
    0
      float* const ap = a->data.f32;
    1420
    0
      float* const bp = b->data.f32;
    1421
    0
      const int count = dim[2] * dim[3];
    1422
    0
      if (astride[2] == dim[3] && bstride[2] == dim[3])
    1423
    0
      {
    1424
        // Special casing if the ainc[3] is the same as dim[3]
    1425
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1426
    0
        {
    1427
    0
          float* ap0 = ap + i[0] * astride[0];
    1428
    0
          float* bp0 = bp + i[0] * bstride[0];
    1429
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1430
    0
          {
    1431
    0
            for (x = 0; x < count; x++)
    1432
    0
              bp0[x] = log(ap0[x]);
    1433
    0
            ap0 += astride[1];
    1434
    0
            bp0 += bstride[1];
    1435
    0
          }
    1436
    0
        }
    1437
    0
        return CCV_NNC_EXEC_SUCCESS;
    1438
    0
      }
    1439
      // Non-optimal case, need to do skip copy.
    1440
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1441
    0
      {
    1442
    0
        float* const ap0 = ap + i[0] * astride[0];
    1443
    0
        float* const bp0 = bp + i[0] * bstride[0];
    1444
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1445
    0
        {
    1446
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1447
    0
          float* bp1 = bp0 + i[1] * bstride[1];
    1448
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1449
    0
          {
    1450
    0
            for (x = 0; x < dim[3]; x++)
    1451
    0
              bp1[x] = log(ap1[x]);
    1452
    0
            ap1 += astride[2];
    1453
    0
            bp1 += bstride[2];
    1454
    0
          }
    1455
    0
        }
    1456
    0
      }
    1457
    0
      return CCV_NNC_EXEC_SUCCESS;
    1458
    0
    }
    1459
    1460
    static int _ccv_nnc_ewlog_back(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)
    1461
    225
    {
    1462
      // D[Log[x], x] = 1 / x
    1463
    225
      _ccv_nnc_ewdiv_forw_cpu_ref(1, (ccv_nnc_tensor_view_t*)inputs[0], (ccv_nnc_tensor_view_t*)inputs[1], (ccv_nnc_tensor_view_t*)outputs[0]);
    1464
    225
      return CCV_NNC_EXEC_SUCCESS;
    1465
    225
    }
    1466
    1467
    static int _ccv_nnc_ewsqrt_forw(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)
    1468
    5
    {
    1469
      // Assuming this is float 32.
    1470
    5
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1471
    5
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1472
    5
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    1473
    5
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[0];
    1474
    5
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    1475
    5
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1476
    5
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1477
    5
      ccv_nnc_tensor_view_get_dim(a, dim);
    1478
    5
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    1479
    5
      int x;
    1480
    5
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b))
    1481
    5
      {
    1482
        // Super optimal case, just do one for-loop for sum.
    1483
    5
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1484
    2.01k
        for (x = 0; x < tensor_count; x++2.01k)
    1485
    2.01k
          b->data.f32[x] = sqrt(a->data.f32[x]);
    1486
    5
        return CCV_NNC_EXEC_SUCCESS;
    1487
    5
      }
    1488
    5
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1489
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1490
    0
      ccv_nnc_tensor_view_get_stride(b, bstride);
    1491
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1492
    0
      float* const ap = a->data.f32;
    1493
    0
      float* const bp = b->data.f32;
    1494
    0
      const int count = dim[2] * dim[3];
    1495
    0
      if (astride[2] == dim[3] && bstride[2] == dim[3])
    1496
    0
      {
    1497
        // Special casing if the ainc[3] is the same as dim[3]
    1498
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1499
    0
        {
    1500
    0
          float* ap0 = ap + i[0] * astride[0];
    1501
    0
          float* bp0 = bp + i[0] * bstride[0];
    1502
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1503
    0
          {
    1504
    0
            for (x = 0; x < count; x++)
    1505
    0
              bp0[x] = sqrt(ap0[x]);
    1506
    0
            ap0 += astride[1];
    1507
    0
            bp0 += bstride[1];
    1508
    0
          }
    1509
    0
        }
    1510
    0
        return CCV_NNC_EXEC_SUCCESS;
    1511
    0
      }
    1512
      // Non-optimal case, need to do skip copy.
    1513
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1514
    0
      {
    1515
    0
        float* const ap0 = ap + i[0] * astride[0];
    1516
    0
        float* const bp0 = bp + i[0] * bstride[0];
    1517
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1518
    0
        {
    1519
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1520
    0
          float* bp1 = bp0 + i[1] * bstride[1];
    1521
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1522
    0
          {
    1523
    0
            for (x = 0; x < dim[3]; x++)
    1524
    0
              bp1[x] = sqrt(ap1[x]);
    1525
    0
            ap1 += astride[2];
    1526
    0
            bp1 += bstride[2];
    1527
    0
          }
    1528
    0
        }
    1529
    0
      }
    1530
    0
      return CCV_NNC_EXEC_SUCCESS;
    1531
    0
    }
    1532
    1533
    static int _ccv_nnc_ewsqrt_back(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)
    1534
    2
    {
    1535
      // D[Sqrt[x], x] = 0.5 / Sqrt[x]
    1536
    2
      _ccv_nnc_ewdiv_forw_cpu_ref(0.5, (ccv_nnc_tensor_view_t*)inputs[0], (ccv_nnc_tensor_view_t*)inputs[2], (ccv_nnc_tensor_view_t*)outputs[0]);
    1537
    2
      return CCV_NNC_EXEC_SUCCESS;
    1538
    2
    }
    1539
    1540
    static int _ccv_nnc_ewsin_forw(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)
    1541
    4
    {
    1542
      // Assuming this is float 32.
    1543
    4
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1544
    4
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1545
    4
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    1546
    4
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[0];
    1547
    4
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    1548
    4
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1549
    4
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1550
    4
      ccv_nnc_tensor_view_get_dim(a, dim);
    1551
    4
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    1552
    4
      int x;
    1553
    4
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b))
    1554
    4
      {
    1555
    4
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1556
    2.00k
        for (x = 0; x < tensor_count; x++2.00k)
    1557
    2.00k
          b->data.f32[x] = sinf(a->data.f32[x]);
    1558
    4
        return CCV_NNC_EXEC_SUCCESS;
    1559
    4
      }
    1560
    4
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1561
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1562
    0
      ccv_nnc_tensor_view_get_stride(b, bstride);
    1563
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1564
    0
      float* const ap = a->data.f32;
    1565
    0
      float* const bp = b->data.f32;
    1566
    0
      const int count = dim[2] * dim[3];
    1567
    0
      if (astride[2] == dim[3] && bstride[2] == dim[3])
    1568
    0
      {
    1569
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1570
    0
        {
    1571
    0
          float* ap0 = ap + i[0] * astride[0];
    1572
    0
          float* bp0 = bp + i[0] * bstride[0];
    1573
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1574
    0
          {
    1575
    0
            for (x = 0; x < count; x++)
    1576
    0
              bp0[x] = sinf(ap0[x]);
    1577
    0
            ap0 += astride[1];
    1578
    0
            bp0 += bstride[1];
    1579
    0
          }
    1580
    0
        }
    1581
    0
        return CCV_NNC_EXEC_SUCCESS;
    1582
    0
      }
    1583
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1584
    0
      {
    1585
    0
        float* const ap0 = ap + i[0] * astride[0];
    1586
    0
        float* const bp0 = bp + i[0] * bstride[0];
    1587
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1588
    0
        {
    1589
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1590
    0
          float* bp1 = bp0 + i[1] * bstride[1];
    1591
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1592
    0
          {
    1593
    0
            for (x = 0; x < dim[3]; x++)
    1594
    0
              bp1[x] = sinf(ap1[x]);
    1595
    0
            ap1 += astride[2];
    1596
    0
            bp1 += bstride[2];
    1597
    0
          }
    1598
    0
        }
    1599
    0
      }
    1600
    0
      return CCV_NNC_EXEC_SUCCESS;
    1601
    0
    }
    1602
    1603
    static int _ccv_nnc_ewsin_back(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)
    1604
    3
    {
    1605
      // D[Sin[x], x] = Cos[x]
    1606
    3
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1607
    3
      int gstride[CCV_NNC_MAX_DIM_ALLOC];
    1608
    3
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1609
    3
      int hstride[CCV_NNC_MAX_DIM_ALLOC];
    1610
    3
      ccv_nnc_tensor_view_t* g = (ccv_nnc_tensor_view_t*)inputs[0];
    1611
    3
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[1];
    1612
    3
      ccv_nnc_tensor_view_t* h = (ccv_nnc_tensor_view_t*)outputs[0];
    1613
    3
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1614
    3
      assert(ccv_nnc_tensor_nd(h->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1615
    3
      ccv_nnc_tensor_view_get_dim(a, dim);
    1616
    3
      assert(ccv_nnc_tensor_view_check_dim(h, dim));
    1617
    3
      if (g)
    1618
    2
      {
    1619
    2
        assert(ccv_nnc_tensor_nd(g->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1620
    2
        assert(ccv_nnc_tensor_view_check_dim(g, dim));
    1621
    2
      }
    1622
    3
      int x;
    1623
    3
      if ((!g || !2CCV_IS_TENSOR_VIEW2(g)) && !CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(h))
    1624
    3
      {
    1625
    3
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1626
    3
        if (g)
    1627
    2
        {
    1628
    1.00k
          for (x = 0; x < tensor_count; x++1.00k)
    1629
    1.00k
            h->data.f32[x] = g->data.f32[x] * cosf(a->data.f32[x]);
    1630
    2
        } else {
    1631
    2
          for (x = 0; x < tensor_count; x++1)
    1632
    1
            h->data.f32[x] = cosf(a->data.f32[x]);
    1633
    1
        }
    1634
    3
        return CCV_NNC_EXEC_SUCCESS;
    1635
    3
      }
    1636
    3
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1637
    0
      if (g)
    1638
    0
        ccv_nnc_tensor_view_get_stride(g, gstride);
    1639
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1640
    0
      ccv_nnc_tensor_view_get_stride(h, hstride);
    1641
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1642
    0
      float* const gp = g ? g->data.f32 : 0;
    1643
    0
      float* const ap = a->data.f32;
    1644
    0
      float* const hp = h->data.f32;
    1645
    0
      const int count = dim[2] * dim[3];
    1646
    0
      if ((!g || gstride[2] == dim[3]) && astride[2] == dim[3] && hstride[2] == dim[3])
    1647
    0
      {
    1648
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1649
    0
        {
    1650
    0
          float* gp0 = g ? gp + i[0] * gstride[0] : 0;
    1651
    0
          float* ap0 = ap + i[0] * astride[0];
    1652
    0
          float* hp0 = hp + i[0] * hstride[0];
    1653
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1654
    0
          {
    1655
    0
            if (g)
    1656
    0
            {
    1657
    0
              for (x = 0; x < count; x++)
    1658
    0
                hp0[x] = gp0[x] * cosf(ap0[x]);
    1659
    0
              gp0 += gstride[1];
    1660
    0
            } else {
    1661
    0
              for (x = 0; x < count; x++)
    1662
    0
                hp0[x] = cosf(ap0[x]);
    1663
    0
            }
    1664
    0
            ap0 += astride[1];
    1665
    0
            hp0 += hstride[1];
    1666
    0
          }
    1667
    0
        }
    1668
    0
        return CCV_NNC_EXEC_SUCCESS;
    1669
    0
      }
    1670
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1671
    0
      {
    1672
    0
        float* const gp0 = g ? gp + i[0] * gstride[0] : 0;
    1673
    0
        float* const ap0 = ap + i[0] * astride[0];
    1674
    0
        float* const hp0 = hp + i[0] * hstride[0];
    1675
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1676
    0
        {
    1677
    0
          float* gp1 = g ? gp0 + i[1] * gstride[1] : 0;
    1678
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1679
    0
          float* hp1 = hp0 + i[1] * hstride[1];
    1680
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1681
    0
          {
    1682
    0
            if (g)
    1683
    0
            {
    1684
    0
              for (x = 0; x < dim[3]; x++)
    1685
    0
                hp1[x] = gp1[x] * cosf(ap1[x]);
    1686
    0
              gp1 += gstride[2];
    1687
    0
            } else {
    1688
    0
              for (x = 0; x < dim[3]; x++)
    1689
    0
                hp1[x] = cosf(ap1[x]);
    1690
    0
            }
    1691
    0
            ap1 += astride[2];
    1692
    0
            hp1 += hstride[2];
    1693
    0
          }
    1694
    0
        }
    1695
    0
      }
    1696
    0
      return CCV_NNC_EXEC_SUCCESS;
    1697
    0
    }
    1698
    1699
    static int _ccv_nnc_ewcos_forw(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)
    1700
    4
    {
    1701
      // Assuming this is float 32.
    1702
    4
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1703
    4
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1704
    4
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    1705
    4
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[0];
    1706
    4
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    1707
    4
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1708
    4
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1709
    4
      ccv_nnc_tensor_view_get_dim(a, dim);
    1710
    4
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    1711
    4
      int x;
    1712
    4
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b))
    1713
    4
      {
    1714
    4
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1715
    2.00k
        for (x = 0; x < tensor_count; x++2.00k)
    1716
    2.00k
          b->data.f32[x] = cosf(a->data.f32[x]);
    1717
    4
        return CCV_NNC_EXEC_SUCCESS;
    1718
    4
      }
    1719
    4
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1720
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1721
    0
      ccv_nnc_tensor_view_get_stride(b, bstride);
    1722
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1723
    0
      float* const ap = a->data.f32;
    1724
    0
      float* const bp = b->data.f32;
    1725
    0
      const int count = dim[2] * dim[3];
    1726
    0
      if (astride[2] == dim[3] && bstride[2] == dim[3])
    1727
    0
      {
    1728
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1729
    0
        {
    1730
    0
          float* ap0 = ap + i[0] * astride[0];
    1731
    0
          float* bp0 = bp + i[0] * bstride[0];
    1732
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1733
    0
          {
    1734
    0
            for (x = 0; x < count; x++)
    1735
    0
              bp0[x] = cosf(ap0[x]);
    1736
    0
            ap0 += astride[1];
    1737
    0
            bp0 += bstride[1];
    1738
    0
          }
    1739
    0
        }
    1740
    0
        return CCV_NNC_EXEC_SUCCESS;
    1741
    0
      }
    1742
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1743
    0
      {
    1744
    0
        float* const ap0 = ap + i[0] * astride[0];
    1745
    0
        float* const bp0 = bp + i[0] * bstride[0];
    1746
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1747
    0
        {
    1748
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1749
    0
          float* bp1 = bp0 + i[1] * bstride[1];
    1750
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1751
    0
          {
    1752
    0
            for (x = 0; x < dim[3]; x++)
    1753
    0
              bp1[x] = cosf(ap1[x]);
    1754
    0
            ap1 += astride[2];
    1755
    0
            bp1 += bstride[2];
    1756
    0
          }
    1757
    0
        }
    1758
    0
      }
    1759
    0
      return CCV_NNC_EXEC_SUCCESS;
    1760
    0
    }
    1761
    1762
    static int _ccv_nnc_ewcos_back(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)
    1763
    3
    {
    1764
      // D[Cos[x], x] = -Sin[x]
    1765
    3
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1766
    3
      int gstride[CCV_NNC_MAX_DIM_ALLOC];
    1767
    3
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1768
    3
      int hstride[CCV_NNC_MAX_DIM_ALLOC];
    1769
    3
      ccv_nnc_tensor_view_t* g = (ccv_nnc_tensor_view_t*)inputs[0];
    1770
    3
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[1];
    1771
    3
      ccv_nnc_tensor_view_t* h = (ccv_nnc_tensor_view_t*)outputs[0];
    1772
    3
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1773
    3
      assert(ccv_nnc_tensor_nd(h->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1774
    3
      ccv_nnc_tensor_view_get_dim(a, dim);
    1775
    3
      assert(ccv_nnc_tensor_view_check_dim(h, dim));
    1776
    3
      if (g)
    1777
    2
      {
    1778
    2
        assert(ccv_nnc_tensor_nd(g->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1779
    2
        assert(ccv_nnc_tensor_view_check_dim(g, dim));
    1780
    2
      }
    1781
    3
      int x;
    1782
    3
      if ((!g || !2CCV_IS_TENSOR_VIEW2(g)) && !CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(h))
    1783
    3
      {
    1784
    3
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1785
    3
        if (g)
    1786
    2
        {
    1787
    1.00k
          for (x = 0; x < tensor_count; x++1.00k)
    1788
    1.00k
            h->data.f32[x] = -g->data.f32[x] * sinf(a->data.f32[x]);
    1789
    2
        } else {
    1790
    2
          for (x = 0; x < tensor_count; x++1)
    1791
    1
            h->data.f32[x] = -sinf(a->data.f32[x]);
    1792
    1
        }
    1793
    3
        return CCV_NNC_EXEC_SUCCESS;
    1794
    3
      }
    1795
    3
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1796
    0
      if (g)
    1797
    0
        ccv_nnc_tensor_view_get_stride(g, gstride);
    1798
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1799
    0
      ccv_nnc_tensor_view_get_stride(h, hstride);
    1800
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1801
    0
      float* const gp = g ? g->data.f32 : 0;
    1802
    0
      float* const ap = a->data.f32;
    1803
    0
      float* const hp = h->data.f32;
    1804
    0
      const int count = dim[2] * dim[3];
    1805
    0
      if ((!g || gstride[2] == dim[3]) && astride[2] == dim[3] && hstride[2] == dim[3])
    1806
    0
      {
    1807
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1808
    0
        {
    1809
    0
          float* gp0 = g ? gp + i[0] * gstride[0] : 0;
    1810
    0
          float* ap0 = ap + i[0] * astride[0];
    1811
    0
          float* hp0 = hp + i[0] * hstride[0];
    1812
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1813
    0
          {
    1814
    0
            if (g)
    1815
    0
            {
    1816
    0
              for (x = 0; x < count; x++)
    1817
    0
                hp0[x] = -gp0[x] * sinf(ap0[x]);
    1818
    0
              gp0 += gstride[1];
    1819
    0
            } else {
    1820
    0
              for (x = 0; x < count; x++)
    1821
    0
                hp0[x] = -sinf(ap0[x]);
    1822
    0
            }
    1823
    0
            ap0 += astride[1];
    1824
    0
            hp0 += hstride[1];
    1825
    0
          }
    1826
    0
        }
    1827
    0
        return CCV_NNC_EXEC_SUCCESS;
    1828
    0
      }
    1829
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1830
    0
      {
    1831
    0
        float* const gp0 = g ? gp + i[0] * gstride[0] : 0;
    1832
    0
        float* const ap0 = ap + i[0] * astride[0];
    1833
    0
        float* const hp0 = hp + i[0] * hstride[0];
    1834
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1835
    0
        {
    1836
    0
          float* gp1 = g ? gp0 + i[1] * gstride[1] : 0;
    1837
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1838
    0
          float* hp1 = hp0 + i[1] * hstride[1];
    1839
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1840
    0
          {
    1841
    0
            if (g)
    1842
    0
            {
    1843
    0
              for (x = 0; x < dim[3]; x++)
    1844
    0
                hp1[x] = -gp1[x] * sinf(ap1[x]);
    1845
    0
              gp1 += gstride[2];
    1846
    0
            } else {
    1847
    0
              for (x = 0; x < dim[3]; x++)
    1848
    0
                hp1[x] = -sinf(ap1[x]);
    1849
    0
            }
    1850
    0
            ap1 += astride[2];
    1851
    0
            hp1 += hstride[2];
    1852
    0
          }
    1853
    0
        }
    1854
    0
      }
    1855
    0
      return CCV_NNC_EXEC_SUCCESS;
    1856
    0
    }
    1857
    1858
    static int _ccv_nnc_ewabs_forw(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)
    1859
    2
    {
    1860
      // Assuming this is float 32.
    1861
    2
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1862
    2
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1863
    2
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    1864
    2
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[0];
    1865
    2
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    1866
    2
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1867
    2
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1868
    2
      ccv_nnc_tensor_view_get_dim(a, dim);
    1869
    2
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    1870
    2
      int x;
    1871
    2
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b))
    1872
    2
      {
    1873
        // Super optimal case, just do one for-loop for sum.
    1874
    2
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1875
    2.00k
        for (x = 0; x < tensor_count; x++2.00k)
    1876
    2.00k
          b->data.f32[x] = fabs(a->data.f32[x]);
    1877
    2
        return CCV_NNC_EXEC_SUCCESS;
    1878
    2
      }
    1879
    2
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1880
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1881
    0
      ccv_nnc_tensor_view_get_stride(b, bstride);
    1882
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1883
    0
      float* const ap = a->data.f32;
    1884
    0
      float* const bp = b->data.f32;
    1885
    0
      const int count = dim[2] * dim[3];
    1886
    0
      if (astride[2] == dim[3] && bstride[2] == dim[3])
    1887
    0
      {
    1888
        // Special casing if the ainc[3] is the same as dim[3]
    1889
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1890
    0
        {
    1891
    0
          float* ap0 = ap + i[0] * astride[0];
    1892
    0
          float* bp0 = bp + i[0] * bstride[0];
    1893
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1894
    0
          {
    1895
    0
            for (x = 0; x < count; x++)
    1896
    0
              bp0[x] = fabs(ap0[x]);
    1897
    0
            ap0 += astride[1];
    1898
    0
            bp0 += bstride[1];
    1899
    0
          }
    1900
    0
        }
    1901
    0
        return CCV_NNC_EXEC_SUCCESS;
    1902
    0
      }
    1903
      // Non-optimal case, need to do skip copy.
    1904
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1905
    0
      {
    1906
    0
        float* const ap0 = ap + i[0] * astride[0];
    1907
    0
        float* const bp0 = bp + i[0] * bstride[0];
    1908
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1909
    0
        {
    1910
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1911
    0
          float* bp1 = bp0 + i[1] * bstride[1];
    1912
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1913
    0
          {
    1914
    0
            for (x = 0; x < dim[3]; x++)
    1915
    0
              bp1[x] = fabs(ap1[x]);
    1916
    0
            ap1 += astride[2];
    1917
    0
            bp1 += bstride[2];
    1918
    0
          }
    1919
    0
        }
    1920
    0
      }
    1921
    0
      return CCV_NNC_EXEC_SUCCESS;
    1922
    0
    }
    1923
    1924
    static int _ccv_nnc_ewabs_back(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)
    1925
    1
    {
    1926
      // Assuming this is float 32.
    1927
    1
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    1928
    1
      int gstride[CCV_NNC_MAX_DIM_ALLOC];
    1929
    1
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    1930
    1
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    1931
    1
      ccv_nnc_tensor_view_t* g = (ccv_nnc_tensor_view_t*)inputs[0];
    1932
    1
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[1];
    1933
    1
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    1934
    1
      assert(ccv_nnc_tensor_nd(g->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1935
    1
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1936
    1
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    1937
    1
      ccv_nnc_tensor_view_get_dim(a, dim);
    1938
    1
      assert(ccv_nnc_tensor_view_check_dim(g, dim));
    1939
    1
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    1940
    1
      int x;
    1941
    1
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b) && !CCV_IS_TENSOR_VIEW(g))
    1942
    1
      {
    1943
        // Super optimal case, just do one for-loop for sum.
    1944
    1
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    1945
    1.00k
        for (x = 0; x < tensor_count; x++1.00k)
    1946
    1.00k
          b->data.f32[x] = a->data.f32[x] >= 0 ? g->data.f32[x] : -g->data.f32[x]0;
    1947
    1
        return CCV_NNC_EXEC_SUCCESS;
    1948
    1
      }
    1949
    1
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    1950
    0
      ccv_nnc_tensor_view_get_stride(g, astride);
    1951
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    1952
    0
      ccv_nnc_tensor_view_get_stride(b, bstride);
    1953
    0
      int i[CCV_NNC_MAX_DIM + 2];
    1954
    0
      float* const gp = g->data.f32;
    1955
    0
      float* const ap = a->data.f32;
    1956
    0
      float* const bp = b->data.f32;
    1957
    0
      const int count = dim[2] * dim[3];
    1958
    0
      if (astride[2] == dim[3] && bstride[2] == dim[3])
    1959
    0
      {
    1960
        // Special casing if the ainc[3] is the same as dim[3]
    1961
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1962
    0
        {
    1963
    0
          float* gp0 = gp + i[0] * gstride[0];
    1964
    0
          float* ap0 = ap + i[0] * astride[0];
    1965
    0
          float* bp0 = bp + i[0] * bstride[0];
    1966
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1967
    0
          {
    1968
    0
            for (x = 0; x < count; x++)
    1969
    0
              bp0[x] = ap0[x] >= 0 ? gp0[x] : -gp0[x];
    1970
    0
            gp0 += gstride[1];
    1971
    0
            ap0 += astride[1];
    1972
    0
            bp0 += bstride[1];
    1973
    0
          }
    1974
    0
        }
    1975
    0
        return CCV_NNC_EXEC_SUCCESS;
    1976
    0
      }
    1977
      // Non-optimal case, need to do skip copy.
    1978
    0
      for (i[0] = 0; i[0] < dim[0]; i[0]++)
    1979
    0
      {
    1980
    0
        float* const gp0 = gp + i[0] * gstride[0];
    1981
    0
        float* const ap0 = ap + i[0] * astride[0];
    1982
    0
        float* const bp0 = bp + i[0] * bstride[0];
    1983
    0
        for (i[1] = 0; i[1] < dim[1]; i[1]++)
    1984
    0
        {
    1985
    0
          float* gp1 = gp0 + i[1] * gstride[1];
    1986
    0
          float* ap1 = ap0 + i[1] * astride[1];
    1987
    0
          float* bp1 = bp0 + i[1] * bstride[1];
    1988
    0
          for (i[2] = 0; i[2] < dim[2]; i[2]++)
    1989
    0
          {
    1990
    0
            for (x = 0; x < dim[3]; x++)
    1991
    0
              bp1[x] = ap1[x] >= 0 ? gp1[x] : -gp1[x];
    1992
    0
            gp1 += gstride[2];
    1993
    0
            ap1 += astride[2];
    1994
    0
            bp1 += bstride[2];
    1995
    0
          }
    1996
    0
        }
    1997
    0
      }
    1998
    0
      return CCV_NNC_EXEC_SUCCESS;
    1999
    0
    }
    2000
    2001
    static int _ccv_nnc_clamp_forw(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)
    2002
    8
    {
    2003
      // Assuming this is float 32.
    2004
    8
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    2005
    8
      int astride[CCV_NNC_MAX_DIM_ALLOC];
    2006
    8
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    2007
    8
      ccv_nnc_tensor_view_t* a = (ccv_nnc_tensor_view_t*)inputs[0];
    2008
    8
      ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)outputs[0];
    2009
    8
      assert(ccv_nnc_tensor_nd(a->info.dim) <= CCV_NNC_MAX_DIM + 2);
    2010
    8
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    2011
    8
      ccv_nnc_tensor_view_get_dim(a, dim);
    2012
    8
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    2013
    8
      int x;
    2014
    8
      const float min = cmd.info.clamp.min;
    2015
    8
      const float max = cmd.info.clamp.max;
    2016
    8
      assert(!isnan(min) || !isnan(max));
    2017
    8
      if (!CCV_IS_TENSOR_VIEW(a) && !CCV_IS_TENSOR_VIEW(b))
    2018
    8
      {
    2019
        // Super optimal case, just do one for-loop for sum.
    2020
    8
        const int tensor_count = ccv_nnc_tensor_count(a->info);
    2021
    8
        if (isnan(min))
    2022
    4
        {
    2023
    2.00k
          for (x = 0; x < tensor_count; x++2.00k)
    2024
    2.00k
            b->data.f32[x] = ccv_min(a->data.f32[x], max);
    2025
    4
        } else if (isnan(max)) {
    2026
    2.00k
          for (x = 0; x < tensor_count; x++2.00k)
    2027
    2.00k
            b->data.f32[x] = ccv_max(a->data.f32[x], min);
    2028
    2
        } else {
    2029
    2.00k
          for (x = 0; x < tensor_count; x++2.00k)
    2030
    2.00k
            b->data.f32[x] = ccv_clamp(a->data.f32[x], min, max);
    2031
    2
        }
    2032
    8
        return CCV_NNC_EXEC_SUCCESS;
    2033
    8
      }
    2034
    8
      assert(CCV_NNC_MAX_DIM == 2)0; // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    2035
    0
      ccv_nnc_tensor_view_get_stride(a, astride);
    2036
    0
      ccv_nnc_tensor_view_get_stride(b, bstride);
    2037
    0
      int i[CCV_NNC_MAX_DIM + 2];
    2038
    0
      float* const ap = a->data.f32;
    2039
    0
      float* const bp = b->data.f32;
    2040
    0
      const int count = dim[2] * dim[3];
    2041
    0
      if (isnan(min))
    2042
    0
      {
    2043
    0
        if (astride[2] == dim[3] && bstride[2] == dim[3])
    2044
    0
        {
    2045
          // Special casing if the ainc[3] is the same as dim[3]
    2046
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2047
    0
          {
    2048
    0
            float* ap0 = ap + i[0] * astride[0];
    2049
    0
            float* bp0 = bp + i[0] * bstride[0];
    2050
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2051
    0
            {
    2052
    0
              for (x = 0; x < count; x++)
    2053
    0
                bp0[x] = ccv_min(ap0[x], max);
    2054
    0
              ap0 += astride[1];
    2055
    0
              bp0 += bstride[1];
    2056
    0
            }
    2057
    0
          }
    2058
    0
          return CCV_NNC_EXEC_SUCCESS;
    2059
    0
        }
    2060
        // Non-optimal case, need to do skip copy.
    2061
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2062
    0
        {
    2063
    0
          float* const ap0 = ap + i[0] * astride[0];
    2064
    0
          float* const bp0 = bp + i[0] * bstride[0];
    2065
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2066
    0
          {
    2067
    0
            float* ap1 = ap0 + i[1] * astride[1];
    2068
    0
            float* bp1 = bp0 + i[1] * bstride[1];
    2069
    0
            for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2070
    0
            {
    2071
    0
              for (x = 0; x < dim[3]; x++)
    2072
    0
                bp1[x] = ccv_min(ap1[x], max);
    2073
    0
              ap1 += astride[2];
    2074
    0
              bp1 += bstride[2];
    2075
    0
            }
    2076
    0
          }
    2077
    0
        }
    2078
    0
      } else if (isnan(max)) {
    2079
    0
        if (astride[2] == dim[3] && bstride[2] == dim[3])
    2080
    0
        {
    2081
          // Special casing if the ainc[3] is the same as dim[3]
    2082
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2083
    0
          {
    2084
    0
            float* ap0 = ap + i[0] * astride[0];
    2085
    0
            float* bp0 = bp + i[0] * bstride[0];
    2086
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2087
    0
            {
    2088
    0
              for (x = 0; x < count; x++)
    2089
    0
                bp0[x] = ccv_max(ap0[x], min);
    2090
    0
              ap0 += astride[1];
    2091
    0
              bp0 += bstride[1];
    2092
    0
            }
    2093
    0
          }
    2094
    0
          return CCV_NNC_EXEC_SUCCESS;
    2095
    0
        }
    2096
        // Non-optimal case, need to do skip copy.
    2097
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2098
    0
        {
    2099
    0
          float* const ap0 = ap + i[0] * astride[0];
    2100
    0
          float* const bp0 = bp + i[0] * bstride[0];
    2101
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2102
    0
          {
    2103
    0
            float* ap1 = ap0 + i[1] * astride[1];
    2104
    0
            float* bp1 = bp0 + i[1] * bstride[1];
    2105
    0
            for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2106
    0
            {
    2107
    0
              for (x = 0; x < dim[3]; x++)
    2108
    0
                bp1[x] = ccv_max(ap1[x], min);
    2109
    0
              ap1 += astride[2];
    2110
    0
              bp1 += bstride[2];
    2111
    0
            }
    2112
    0
          }
    2113
    0
        }
    2114
    0
      } else {
    2115
    0
        if (astride[2] == dim[3] && bstride[2] == dim[3])
    2116
    0
        {
    2117
          // Special casing if the ainc[3] is the same as dim[3]
    2118
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2119
    0
          {
    2120
    0
            float* ap0 = ap + i[0] * astride[0];
    2121
    0
            float* bp0 = bp + i[0] * bstride[0];
    2122
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2123
    0
            {
    2124
    0
              for (x = 0; x < count; x++)
    2125
    0
                bp0[x] = ccv_clamp(ap0[x], min, max);
    2126
    0
              ap0 += astride[1];
    2127
    0
              bp0 += bstride[1];
    2128
    0
            }
    2129
    0
          }
    2130
    0
          return CCV_NNC_EXEC_SUCCESS;
    2131
    0
        }
    2132
        // Non-optimal case, need to do skip copy.
    2133
    0
        for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2134
    0
        {
    2135
    0
          float* const ap0 = ap + i[0] * astride[0];
    2136
    0
          float* const bp0 = bp + i[0] * bstride[0];
    2137
    0
          for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2138
    0
          {
    2139
    0
            float* ap1 = ap0 + i[1] * astride[1];
    2140
    0
            float* bp1 = bp0 + i[1] * bstride[1];
    2141
    0
            for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2142
    0
            {
    2143
    0
              for (x = 0; x < dim[3]; x++)
    2144
    0
                bp1[x] = ccv_clamp(ap1[x], min, max);
    2145
    0
              ap1 += astride[2];
    2146
    0
              bp1 += bstride[2];
    2147
    0
            }
    2148
    0
          }
    2149
    0
        }
    2150
    0
      }
    2151
    0
      return CCV_NNC_EXEC_SUCCESS;
    2152
    0
    }
    2153
    2154
    static int _ccv_nnc_clamp_back(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)
    2155
    3
    {
    2156
    3
      assert(input_size == 3);
    2157
    3
      const ccv_nnc_tensor_view_t* g = (ccv_nnc_tensor_view_t*)inputs[0]; // gradient
    2158
    3
      const ccv_nnc_tensor_view_t* b = (ccv_nnc_tensor_view_t*)inputs[2];
    2159
    3
      assert(output_size == 1);
    2160
    3
      ccv_nnc_tensor_view_t* h = (ccv_nnc_tensor_view_t*)outputs[0];
    2161
      // Assuming this is float 32.
    2162
    3
      int dim[CCV_NNC_MAX_DIM_ALLOC];
    2163
    3
      int hstride[CCV_NNC_MAX_DIM_ALLOC];
    2164
    3
      int bstride[CCV_NNC_MAX_DIM_ALLOC];
    2165
    3
      assert(ccv_nnc_tensor_nd(h->info.dim) <= CCV_NNC_MAX_DIM + 2);
    2166
    3
      assert(ccv_nnc_tensor_nd(b->info.dim) <= CCV_NNC_MAX_DIM + 2);
    2167
    3
      ccv_nnc_tensor_view_get_dim(g, dim);
    2168
    3
      ccv_nnc_tensor_view_get_dim(h, dim);
    2169
    3
      assert(ccv_nnc_tensor_view_check_dim(b, dim));
    2170
    3
      int x;
    2171
    3
      const float min = cmd.info.clamp.min;
    2172
    3
      const float max = cmd.info.clamp.max;
    2173
    3
      assert(!isnan(min) || !isnan(max));
    2174
    3
      if (g)
    2175
    3
      {
    2176
    3
        if (!CCV_IS_TENSOR_VIEW(g) && !CCV_IS_TENSOR_VIEW(h) && !CCV_IS_TENSOR_VIEW(b))
    2177
    3
        {
    2178
          // Super optimal case, just do one for-loop for sum.
    2179
    3
          const int tensor_count = ccv_nnc_tensor_count(g->info);
    2180
    3
          if (isnan(min))
    2181
    1
          {
    2182
    1.00k
            for (x = 0; x < tensor_count; x++1.00k)
    2183
    1.00k
              h->data.f32[x] = b->data.f32[x] >= max ? 0509 : g->data.f32[x]491;
    2184
    2
          } else if (isnan(max)) {
    2185
    1.00k
            for (x = 0; x < tensor_count; x++1.00k)
    2186
    1.00k
              h->data.f32[x] = b->data.f32[x] <= min ? 00 : g->data.f32[x];
    2187
    1
          } else {
    2188
    1.00k
            for (x = 0; x < tensor_count; x++1.00k)
    2189
    1.00k
              h->data.f32[x] = (b->data.f32[x] >= max || b->data.f32[x] <= min491) ? 0509 : g->data.f32[x]491;
    2190
    1
          }
    2191
    3
          return CCV_NNC_EXEC_SUCCESS;
    2192
    3
        }
    2193
    0
        int gstride[CCV_NNC_MAX_DIM_ALLOC];
    2194
    0
        assert(ccv_nnc_tensor_nd(g->info.dim) <= CCV_NNC_MAX_DIM + 2);
    2195
    0
        assert(CCV_NNC_MAX_DIM == 2); // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    2196
    0
        ccv_nnc_tensor_view_get_stride(g, gstride);
    2197
    0
        ccv_nnc_tensor_view_get_stride(b, bstride);
    2198
    0
        ccv_nnc_tensor_view_get_stride(h, hstride);
    2199
    0
        int i[CCV_NNC_MAX_DIM + 2];
    2200
    0
        float* const gp = g->data.f32;
    2201
    0
        float* const bp = b->data.f32;
    2202
    0
        float* const hp = h->data.f32;
    2203
    0
        const int count = dim[2] * dim[3];
    2204
    0
        const float min = cmd.info.clamp.min;
    2205
    0
        const float max = cmd.info.clamp.max;
    2206
    0
        assert(!isnan(min) || !isnan(max));
    2207
    0
        if (isnan(min))
    2208
    0
        {
    2209
    0
          if (gstride[2] == dim[3] && bstride[2] == dim[3] && hstride[2] == dim[3])
    2210
    0
          {
    2211
            // Special casing if the ginc[3] is the same as dim[3]
    2212
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2213
    0
            {
    2214
    0
              float* gp0 = gp + i[0] * gstride[0];
    2215
    0
              float* bp0 = bp + i[0] * bstride[0];
    2216
    0
              float* hp0 = hp + i[0] * hstride[0];
    2217
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2218
    0
              {
    2219
    0
                for (x = 0; x < count; x++)
    2220
    0
                  hp0[x] = bp0[x] >= max ? 0 : gp0[x];
    2221
    0
                gp0 += gstride[1];
    2222
    0
                bp0 += bstride[1];
    2223
    0
                hp0 += hstride[1];
    2224
    0
              }
    2225
    0
            }
    2226
    0
            return CCV_NNC_EXEC_SUCCESS;
    2227
    0
          }
    2228
          // Non-optimal case, need to do skip copy.
    2229
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2230
    0
          {
    2231
    0
            float* const gp0 = gp + i[0] * gstride[0];
    2232
    0
            float* const bp0 = bp + i[0] * bstride[0];
    2233
    0
            float* const hp0 = hp + i[0] * hstride[0];
    2234
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2235
    0
            {
    2236
    0
              float* gp1 = gp0 + i[1] * gstride[1];
    2237
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    2238
    0
              float* hp1 = hp0 + i[1] * hstride[1];
    2239
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2240
    0
              {
    2241
    0
                for (x = 0; x < dim[3]; x++)
    2242
    0
                  hp1[x] = bp1[x] >= max ? 0 : gp1[x];
    2243
    0
                gp1 += gstride[2];
    2244
    0
                bp1 += bstride[2];
    2245
    0
                hp1 += hstride[2];
    2246
    0
              }
    2247
    0
            }
    2248
    0
          }
    2249
    0
        } else if (isnan(max)) {
    2250
    0
          if (gstride[2] == dim[3] && bstride[2] == dim[3] && hstride[2] == dim[3])
    2251
    0
          {
    2252
            // Special casing if the ginc[3] is the same as dim[3]
    2253
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2254
    0
            {
    2255
    0
              float* gp0 = gp + i[0] * gstride[0];
    2256
    0
              float* bp0 = bp + i[0] * bstride[0];
    2257
    0
              float* hp0 = hp + i[0] * hstride[0];
    2258
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2259
    0
              {
    2260
    0
                for (x = 0; x < count; x++)
    2261
    0
                  hp0[x] = bp0[x] <= min ? 0 : gp0[x];
    2262
    0
                gp0 += gstride[1];
    2263
    0
                bp0 += bstride[1];
    2264
    0
                hp0 += hstride[1];
    2265
    0
              }
    2266
    0
            }
    2267
    0
            return CCV_NNC_EXEC_SUCCESS;
    2268
    0
          }
    2269
          // Non-optimal case, need to do skip copy.
    2270
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2271
    0
          {
    2272
    0
            float* const gp0 = gp + i[0] * gstride[0];
    2273
    0
            float* const bp0 = bp + i[0] * bstride[0];
    2274
    0
            float* const hp0 = hp + i[0] * hstride[0];
    2275
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2276
    0
            {
    2277
    0
              float* gp1 = gp0 + i[1] * gstride[1];
    2278
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    2279
    0
              float* hp1 = hp0 + i[1] * hstride[1];
    2280
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2281
    0
              {
    2282
    0
                for (x = 0; x < dim[3]; x++)
    2283
    0
                  hp1[x] = bp1[x] <= min ? 0 : gp1[x];
    2284
    0
                gp1 += gstride[2];
    2285
    0
                bp1 += bstride[2];
    2286
    0
                hp1 += hstride[2];
    2287
    0
              }
    2288
    0
            }
    2289
    0
          }
    2290
    0
        } else {
    2291
    0
          if (gstride[2] == dim[3] && bstride[2] == dim[3] && hstride[2] == dim[3])
    2292
    0
          {
    2293
            // Special casing if the ginc[3] is the same as dim[3]
    2294
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2295
    0
            {
    2296
    0
              float* gp0 = gp + i[0] * gstride[0];
    2297
    0
              float* bp0 = bp + i[0] * bstride[0];
    2298
    0
              float* hp0 = hp + i[0] * hstride[0];
    2299
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2300
    0
              {
    2301
    0
                for (x = 0; x < count; x++)
    2302
    0
                  hp0[x] = (bp0[x] >= max || bp0[x] <= min) ? 0 : gp0[x];
    2303
    0
                gp0 += gstride[1];
    2304
    0
                bp0 += bstride[1];
    2305
    0
                hp0 += hstride[1];
    2306
    0
              }
    2307
    0
            }
    2308
    0
            return CCV_NNC_EXEC_SUCCESS;
    2309
    0
          }
    2310
          // Non-optimal case, need to do skip copy.
    2311
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2312
    0
          {
    2313
    0
            float* const gp0 = gp + i[0] * gstride[0];
    2314
    0
            float* const bp0 = bp + i[0] * bstride[0];
    2315
    0
            float* const hp0 = hp + i[0] * hstride[0];
    2316
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2317
    0
            {
    2318
    0
              float* gp1 = gp0 + i[1] * gstride[1];
    2319
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    2320
    0
              float* hp1 = hp0 + i[1] * hstride[1];
    2321
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2322
    0
              {
    2323
    0
                for (x = 0; x < dim[3]; x++)
    2324
    0
                  hp1[x] = (bp1[x] >= max || bp1[x] <= min) ? 0 : gp1[x];
    2325
    0
                gp1 += gstride[2];
    2326
    0
                bp1 += bstride[2];
    2327
    0
                hp1 += hstride[2];
    2328
    0
              }
    2329
    0
            }
    2330
    0
          }
    2331
    0
        }
    2332
    0
      } else {
    2333
    0
        if (!CCV_IS_TENSOR_VIEW(h) && !CCV_IS_TENSOR_VIEW(b))
    2334
    0
        {
    2335
          // Super optimal case, just do one for-loop for sum.
    2336
    0
          const int tensor_count = ccv_nnc_tensor_count(h->info);
    2337
    0
          if (isnan(min))
    2338
    0
          {
    2339
    0
            for (x = 0; x < tensor_count; x++)
    2340
    0
              h->data.f32[x] = b->data.f32[x] >= max ? 0 : 1;
    2341
    0
          } else if (isnan(max)) {
    2342
    0
            for (x = 0; x < tensor_count; x++)
    2343
    0
              h->data.f32[x] = b->data.f32[x] <= min ? 0 : 1;
    2344
    0
          } else {
    2345
    0
            for (x = 0; x < tensor_count; x++)
    2346
    0
              h->data.f32[x] = (b->data.f32[x] >= max || b->data.f32[x] <= min) ? 0 : 1;
    2347
    0
          }
    2348
    0
          return CCV_NNC_EXEC_SUCCESS;
    2349
    0
        }
    2350
    0
        assert(CCV_NNC_MAX_DIM == 2); // Need to change this logic for CCV_NNC_MAX_DIM == other number.
    2351
    0
        ccv_nnc_tensor_view_get_stride(b, bstride);
    2352
    0
        ccv_nnc_tensor_view_get_stride(h, hstride);
    2353
    0
        int i[CCV_NNC_MAX_DIM + 2];
    2354
    0
        float* const bp = b->data.f32;
    2355
    0
        float* const hp = h->data.f32;
    2356
    0
        const int count = dim[2] * dim[3];
    2357
    0
        const float min = cmd.info.clamp.min;
    2358
    0
        const float max = cmd.info.clamp.max;
    2359
    0
        assert(!isnan(min) || !isnan(max));
    2360
    0
        if (isnan(min))
    2361
    0
        {
    2362
    0
          if (bstride[2] == dim[3] && hstride[2] == dim[3])
    2363
    0
          {
    2364
            // Special casing if the binc[3] is the same as dim[3]
    2365
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2366
    0
            {
    2367
    0
              float* bp0 = bp + i[0] * bstride[0];
    2368
    0
              float* hp0 = hp + i[0] * hstride[0];
    2369
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2370
    0
              {
    2371
    0
                for (x = 0; x < count; x++)
    2372
    0
                  hp0[x] = bp0[x] >= max ? 0 : 1;
    2373
    0
                bp0 += bstride[1];
    2374
    0
                hp0 += hstride[1];
    2375
    0
              }
    2376
    0
            }
    2377
    0
            return CCV_NNC_EXEC_SUCCESS;
    2378
    0
          }
    2379
          // Non-optimal case, need to do skip copy.
    2380
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2381
    0
          {
    2382
    0
            float* const bp0 = bp + i[0] * bstride[0];
    2383
    0
            float* const hp0 = hp + i[0] * hstride[0];
    2384
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2385
    0
            {
    2386
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    2387
    0
              float* hp1 = hp0 + i[1] * hstride[1];
    2388
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2389
    0
              {
    2390
    0
                for (x = 0; x < dim[3]; x++)
    2391
    0
                  hp1[x] = bp1[x] >= max ? 0 : 1;
    2392
    0
                bp1 += bstride[2];
    2393
    0
                hp1 += hstride[2];
    2394
    0
              }
    2395
    0
            }
    2396
    0
          }
    2397
    0
        } else if (isnan(max)) {
    2398
    0
          if (bstride[2] == dim[3] && hstride[2] == dim[3])
    2399
    0
          {
    2400
            // Special casing if the binc[3] is the same as dim[3]
    2401
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2402
    0
            {
    2403
    0
              float* bp0 = bp + i[0] * bstride[0];
    2404
    0
              float* hp0 = hp + i[0] * hstride[0];
    2405
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2406
    0
              {
    2407
    0
                for (x = 0; x < count; x++)
    2408
    0
                  hp0[x] = bp0[x] <= min ? 0 : 1;
    2409
    0
                bp0 += bstride[1];
    2410
    0
                hp0 += hstride[1];
    2411
    0
              }
    2412
    0
            }
    2413
    0
            return CCV_NNC_EXEC_SUCCESS;
    2414
    0
          }
    2415
          // Non-optimal case, need to do skip copy.
    2416
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2417
    0
          {
    2418
    0
            float* const bp0 = bp + i[0] * bstride[0];
    2419
    0
            float* const hp0 = hp + i[0] * hstride[0];
    2420
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2421
    0
            {
    2422
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    2423
    0
              float* hp1 = hp0 + i[1] * hstride[1];
    2424
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2425
    0
              {
    2426
    0
                for (x = 0; x < dim[3]; x++)
    2427
    0
                  hp1[x] = bp1[x] <= min ? 0 : 1;
    2428
    0
                bp1 += bstride[2];
    2429
    0
                hp1 += hstride[2];
    2430
    0
              }
    2431
    0
            }
    2432
    0
          }
    2433
    0
        } else {
    2434
    0
          if (bstride[2] == dim[3] && hstride[2] == dim[3])
    2435
    0
          {
    2436
            // Special casing if the binc[3] is the same as dim[3]
    2437
    0
            for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2438
    0
            {
    2439
    0
              float* bp0 = bp + i[0] * bstride[0];
    2440
    0
              float* hp0 = hp + i[0] * hstride[0];
    2441
    0
              for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2442
    0
              {
    2443
    0
                for (x = 0; x < count; x++)
    2444
    0
                  hp0[x] = (bp0[x] >= max || bp0[x] <= min) ? 0 : 1;
    2445
    0
                bp0 += bstride[1];
    2446
    0
                hp0 += hstride[1];
    2447
    0
              }
    2448
    0
            }
    2449
    0
            return CCV_NNC_EXEC_SUCCESS;
    2450
    0
          }
    2451
          // Non-optimal case, need to do skip copy.
    2452
    0
          for (i[0] = 0; i[0] < dim[0]; i[0]++)
    2453
    0
          {
    2454
    0
            float* const bp0 = bp + i[0] * bstride[0];
    2455
    0
            float* const hp0 = hp + i[0] * hstride[0];
    2456
    0
            for (i[1] = 0; i[1] < dim[1]; i[1]++)
    2457
    0
            {
    2458
    0
              float* bp1 = bp0 + i[1] * bstride[1];
    2459
    0
              float* hp1 = hp0 + i[1] * hstride[1];
    2460
    0
              for (i[2] = 0; i[2] < dim[2]; i[2]++)
    2461
    0
              {
    2462
    0
                for (x = 0; x < dim[3]; x++)
    2463
    0
                  hp1[x] = (bp1[x] >= max || bp1[x] <= min) ? 0 : 1;
    2464
    0
                bp1 += bstride[2];
    2465
    0
                hp1 += hstride[2];
    2466
    0
              }
    2467
    0
            }
    2468
    0
          }
    2469
    0
        }
    2470
    0
      }
    2471
    0
      return CCV_NNC_EXEC_SUCCESS;
    2472
    3
    }
    2473
    2474
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWSUM_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2475
    1
    {
    2476
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2477
    1
      registry->tensor_datatypes = CCV_32F;
    2478
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2479
    1
      registry->algorithms = 1;
    2480
    1
      registry->exec = _ccv_nnc_ewsum_forw;
    2481
    1
    }
    2482
    2483
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWSUM_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2484
    1
    {
    2485
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2486
    1
      registry->tensor_datatypes = CCV_32F;
    2487
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2488
    1
      registry->algorithms = 1;
    2489
    1
      registry->exec = _ccv_nnc_ewsum_back;
    2490
    1
    }
    2491
    2492
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWPROD_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2493
    1
    {
    2494
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2495
    1
      registry->tensor_datatypes = CCV_32F;
    2496
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2497
    1
      registry->algorithms = 1;
    2498
    1
      registry->exec = _ccv_nnc_ewprod_forw;
    2499
    1
    }
    2500
    2501
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWPROD_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2502
    1
    {
    2503
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2504
    1
      registry->tensor_datatypes = CCV_32F;
    2505
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2506
    1
      registry->algorithms = 1;
    2507
    1
      registry->exec = _ccv_nnc_ewprod_back;
    2508
    1
    }
    2509
    2510
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWDIV_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2511
    1
    {
    2512
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2513
    1
      registry->tensor_datatypes = CCV_32F;
    2514
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2515
    1
      registry->algorithms = 1;
    2516
    1
      registry->exec = _ccv_nnc_ewdiv_forw;
    2517
    1
    }
    2518
    2519
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWDIV_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2520
    1
    {
    2521
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2522
    1
      registry->tensor_datatypes = CCV_32F;
    2523
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2524
    1
      registry->algorithms = 1;
    2525
    1
      registry->exec = _ccv_nnc_ewdiv_back;
    2526
    1
    }
    2527
    2528
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWEXP_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2529
    1
    {
    2530
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2531
    1
      registry->tensor_datatypes = CCV_32F;
    2532
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2533
    1
      registry->algorithms = 1;
    2534
    1
      registry->exec = _ccv_nnc_ewexp_forw;
    2535
    1
    }
    2536
    2537
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWEXP_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2538
    1
    {
    2539
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2540
    1
      registry->tensor_datatypes = CCV_32F;
    2541
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2542
    1
      registry->algorithms = 1;
    2543
    1
      registry->exec = _ccv_nnc_ewexp_back;
    2544
    1
    }
    2545
    2546
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWSOFTPLUS_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2547
    1
    {
    2548
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2549
    1
      registry->tensor_datatypes = CCV_32F;
    2550
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2551
    1
      registry->algorithms = 1;
    2552
    1
      registry->exec = _ccv_nnc_ewsoftplus_forw;
    2553
    1
    }
    2554
    2555
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWSOFTPLUS_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2556
    1
    {
    2557
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2558
    1
      registry->tensor_datatypes = CCV_32F;
    2559
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2560
    1
      registry->algorithms = 1;
    2561
    1
      registry->exec = _ccv_nnc_ewsoftplus_back;
    2562
    1
    }
    2563
    2564
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWPOW_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2565
    1
    {
    2566
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2567
    1
      registry->tensor_datatypes = CCV_32F;
    2568
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2569
    1
      registry->algorithms = 1;
    2570
    1
      registry->exec = _ccv_nnc_ewpow_forw;
    2571
    1
    }
    2572
    2573
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWPOW_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2574
    1
    {
    2575
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2576
    1
      registry->tensor_datatypes = CCV_32F;
    2577
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2578
    1
      registry->algorithms = 1;
    2579
    1
      registry->exec = _ccv_nnc_ewpow_back;
    2580
    1
    }
    2581
    2582
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWLOG_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2583
    1
    {
    2584
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2585
    1
      registry->tensor_datatypes = CCV_32F;
    2586
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2587
    1
      registry->algorithms = 1;
    2588
    1
      registry->exec = _ccv_nnc_ewlog_forw;
    2589
    1
    }
    2590
    2591
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWLOG_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2592
    1
    {
    2593
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2594
    1
      registry->tensor_datatypes = CCV_32F;
    2595
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2596
    1
      registry->algorithms = 1;
    2597
    1
      registry->exec = _ccv_nnc_ewlog_back;
    2598
    1
    }
    2599
    2600
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWSQRT_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2601
    1
    {
    2602
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2603
    1
      registry->tensor_datatypes = CCV_32F;
    2604
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2605
    1
      registry->algorithms = 1;
    2606
    1
      registry->exec = _ccv_nnc_ewsqrt_forw;
    2607
    1
    }
    2608
    2609
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWSQRT_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2610
    1
    {
    2611
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2612
    1
      registry->tensor_datatypes = CCV_32F;
    2613
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2614
    1
      registry->algorithms = 1;
    2615
    1
      registry->exec = _ccv_nnc_ewsqrt_back;
    2616
    1
    }
    2617
    2618
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWSIN_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2619
    1
    {
    2620
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2621
    1
      registry->tensor_datatypes = CCV_32F;
    2622
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2623
    1
      registry->algorithms = 1;
    2624
    1
      registry->exec = _ccv_nnc_ewsin_forw;
    2625
    1
    }
    2626
    2627
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWSIN_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2628
    1
    {
    2629
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2630
    1
      registry->tensor_datatypes = CCV_32F;
    2631
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2632
    1
      registry->algorithms = 1;
    2633
    1
      registry->exec = _ccv_nnc_ewsin_back;
    2634
    1
    }
    2635
    2636
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWCOS_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2637
    1
    {
    2638
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2639
    1
      registry->tensor_datatypes = CCV_32F;
    2640
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2641
    1
      registry->algorithms = 1;
    2642
    1
      registry->exec = _ccv_nnc_ewcos_forw;
    2643
    1
    }
    2644
    2645
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWCOS_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2646
    1
    {
    2647
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2648
    1
      registry->tensor_datatypes = CCV_32F;
    2649
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2650
    1
      registry->algorithms = 1;
    2651
    1
      registry->exec = _ccv_nnc_ewcos_back;
    2652
    1
    }
    2653
    2654
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWABS_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2655
    1
    {
    2656
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2657
    1
      registry->tensor_datatypes = CCV_32F;
    2658
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2659
    1
      registry->algorithms = 1;
    2660
    1
      registry->exec = _ccv_nnc_ewabs_forw;
    2661
    1
    }
    2662
    2663
    REGISTER_COMMAND_BACKEND(CCV_NNC_EWABS_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2664
    1
    {
    2665
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2666
    1
      registry->tensor_datatypes = CCV_32F;
    2667
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2668
    1
      registry->algorithms = 1;
    2669
    1
      registry->exec = _ccv_nnc_ewabs_back;
    2670
    1
    }
    2671
    2672
    REGISTER_COMMAND_BACKEND(CCV_NNC_CLAMP_FORWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2673
    1
    {
    2674
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2675
    1
      registry->tensor_datatypes = CCV_32F;
    2676
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2677
    1
      registry->algorithms = 1;
    2678
    1
      registry->exec = _ccv_nnc_clamp_forw;
    2679
    1
    }
    2680
    2681
    REGISTER_COMMAND_BACKEND(CCV_NNC_CLAMP_BACKWARD, CCV_NNC_BACKEND_CPU_REF)(ccv_nnc_cmd_backend_registry_t* const registry)
    2682
    1
    {
    2683
    1
      registry->tensor_formats = CCV_TENSOR_FORMAT_NHWC | CCV_TENSOR_FORMAT_NCHW | CCV_TENSOR_FORMAT_CHWN;
    2684
    1
      registry->tensor_datatypes = CCV_32F;
    2685
    1
      registry->tensor_memory = CCV_TENSOR_CPU_MEMORY;
    2686
    1
      registry->algorithms = 1;
    2687
    1
      registry->exec = _ccv_nnc_clamp_back;
    2688
    1
    }