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.0k
CCV_IS_TENSOR_VIEW36.0k
(b) &&
!36.0k
CCV_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.7k
CCV_IS_TENSOR_VIEW30.7k
(b) &&
!30.7k
CCV_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.1k
CCV_IS_TENSOR_VIEW40.1k
(a) &&
!40.1k
CCV_IS_TENSOR_VIEW40.1k
(b) &&
!40.1k
CCV_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 || 
!1
CCV_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) && 
!25
CCV_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 || 
!2
CCV_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 || 
!2
CCV_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 || 
!2
CCV_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
}