Coverage Report

Created: 2026-07-12 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/Simd/src/Simd/SimdBaseNeural.cpp
Line
Count
Source
1
/*
2
* Simd Library (http://ermig1979.github.io/Simd).
3
*
4
* Copyright (c) 2011-2024 Yermalayeu Ihar.
5
*
6
* Permission is hereby granted, free of charge, to any person obtaining a copy
7
* of this software and associated documentation files (the "Software"), to deal
8
* in the Software without restriction, including without limitation the rights
9
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
* copies of the Software, and to permit persons to whom the Software is
11
* furnished to do so, subject to the following conditions:
12
*
13
* The above copyright notice and this permission notice shall be included in
14
* all copies or substantial portions of the Software.
15
*
16
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
* SOFTWARE.
23
*/
24
#include "Simd/SimdMemory.h"
25
#include "Simd/SimdPow.h"
26
27
namespace Simd
28
{
29
    namespace Base
30
    {
31
        void NeuralConvert(const uint8_t * src, size_t srcStride, size_t width, size_t height, float * dst, size_t dstStride, int inversion)
32
0
        {
33
0
            const float k = 1.0f / 255.0f;
34
0
            for (size_t row = 0; row < height; ++row)
35
0
            {
36
0
                if (inversion)
37
0
                {
38
0
                    for (size_t col = 0; col < width; ++col)
39
0
                        dst[col] = (255 - src[col])* k;
40
0
                }
41
0
                else
42
0
                {
43
0
                    for (size_t col = 0; col < width; ++col)
44
0
                        dst[col] = src[col] * k;
45
0
                }
46
0
                src += srcStride;
47
0
                dst += dstStride;
48
0
            }
49
0
        }
50
51
        SIMD_INLINE float ProductSum(const float * a, const float * b, size_t aligned, size_t full)
52
0
        {
53
0
            size_t i = 0;
54
0
            float sums[4] = { 0, 0, 0, 0 };
55
0
            for (; i < aligned; i += 4)
56
0
            {
57
0
                sums[0] += a[i + 0] * b[i + 0];
58
0
                sums[1] += a[i + 1] * b[i + 1];
59
0
                sums[2] += a[i + 2] * b[i + 2];
60
0
                sums[3] += a[i + 3] * b[i + 3];
61
0
            }
62
0
            for (; i < full; ++i)
63
0
                sums[0] += a[i] * b[i];
64
0
            return sums[0] + sums[1] + sums[2] + sums[3];
65
0
        }
66
67
        void NeuralProductSum(const float * a, const float * b, size_t size, float * sum)
68
0
        {
69
0
            *sum = ProductSum(a, b, Simd::AlignLo(size, 4), size);
70
0
        }
71
72
        SIMD_INLINE void AddMultiplied(const float * src, size_t aligned, size_t full, float value, float * dst)
73
0
        {
74
0
            size_t i = 0;
75
0
            for (; i < aligned; i += 4)
76
0
            {
77
0
                dst[i + 0] += src[i + 0] * value;
78
0
                dst[i + 1] += src[i + 1] * value;
79
0
                dst[i + 2] += src[i + 2] * value;
80
0
                dst[i + 3] += src[i + 3] * value;
81
0
            }
82
0
            for (; i < full; ++i)
83
0
                dst[i] += src[i] * value;
84
0
        }
85
86
        void NeuralAddVectorMultipliedByValue(const float * src, size_t size, const float * value, float * dst)
87
0
        {
88
0
            AddMultiplied(src, Simd::AlignLo(size, 4), size, *value, dst);
89
0
        }
90
91
        void NeuralAddVector(const float * src, size_t size, float * dst)
92
0
        {
93
0
            size_t aligned = Simd::AlignLo(size, 4);
94
0
            size_t i = 0;
95
0
            for (; i < aligned; i += 4)
96
0
            {
97
0
                dst[i + 0] += src[i + 0];
98
0
                dst[i + 1] += src[i + 1];
99
0
                dst[i + 2] += src[i + 2];
100
0
                dst[i + 3] += src[i + 3];
101
0
            }
102
0
            for (; i < size; ++i)
103
0
                dst[i] += src[i];
104
0
        }
105
106
        void NeuralAddValue(const float * value, float * dst, size_t size)
107
0
        {
108
0
            const float val = value[0];
109
0
            size_t aligned = Simd::AlignLo(size, 4);
110
0
            size_t i = 0;
111
0
            for (; i < aligned; i += 4)
112
0
            {
113
0
                dst[i + 0] += val;
114
0
                dst[i + 1] += val;
115
0
                dst[i + 2] += val;
116
0
                dst[i + 3] += val;
117
0
            }
118
0
            for (; i < size; ++i)
119
0
                dst[i] += val;
120
0
        }
121
122
        void NeuralDerivativeSigmoid(const float * src, size_t size, const float * slope, float * dst)
123
0
        {
124
0
            float s = slope[0];
125
0
            for (size_t i = 0; i < size; ++i)
126
0
                dst[i] *= s*DerivativeSigmoid(src[i]);
127
0
        }
128
129
        void NeuralDerivativeTanh(const float * src, size_t size, const float * slope, float * dst)
130
0
        {
131
0
            float s = slope[0];
132
0
            for (size_t i = 0; i < size; ++i)
133
0
                dst[i] *= s*DerivativeTanh(src[i]);
134
0
        }
135
136
        void NeuralDerivativeRelu(const float * src, size_t size, const float * slope, float * dst)
137
0
        {
138
0
            float s = slope[0];
139
0
            for (size_t i = 0; i < size; ++i)
140
0
                dst[i] *= src[i] > 0 ? 1.0f : s;
141
0
        }
142
143
        void NeuralPow(const float * src, size_t size, const float * exponent, float * dst)
144
0
        {
145
0
            float e = exponent[0];
146
0
            for (size_t i = 0; i < size; ++i)
147
0
                dst[i] = Pow(src[i], e);
148
0
        }
149
150
        void NeuralUpdateWeights(const float * x, size_t size, const float * a, const float * b, float * d, float * w)
151
0
        {
152
0
            float _a = a[0], _b = b[0];
153
0
            size_t alignedSize = Simd::AlignLo(size, 4);
154
0
            size_t i = 0;
155
0
            for (; i < alignedSize; i += 4)
156
0
            {
157
0
                UpdateWeights(x, i + 0, _a, _b, d, w);
158
0
                UpdateWeights(x, i + 1, _a, _b, d, w);
159
0
                UpdateWeights(x, i + 2, _a, _b, d, w);
160
0
                UpdateWeights(x, i + 3, _a, _b, d, w);
161
0
            }
162
0
            for (; i < size; ++i)
163
0
                UpdateWeights(x, i, _a, _b, d, w);
164
0
        }
165
166
        void NeuralAdaptiveGradientUpdate(const float * delta, size_t size, size_t batch, const float * alpha, const float * epsilon, float * gradient, float * weight)
167
0
        {
168
0
            float norm = (float)(1.0 / batch), _alpha = alpha[0], _epsilon = epsilon[0];
169
0
            size_t alignedSize = Simd::AlignLo(size, 4);
170
0
            size_t i = 0;
171
0
            for (; i < alignedSize; i += 4)
172
0
            {
173
0
                AdaptiveGradientUpdate(delta, i + 0, norm, _alpha, _epsilon, gradient, weight);
174
0
                AdaptiveGradientUpdate(delta, i + 1, norm, _alpha, _epsilon, gradient, weight);
175
0
                AdaptiveGradientUpdate(delta, i + 2, norm, _alpha, _epsilon, gradient, weight);
176
0
                AdaptiveGradientUpdate(delta, i + 3, norm, _alpha, _epsilon, gradient, weight);
177
0
            }
178
0
            for (; i < size; ++i)
179
0
                AdaptiveGradientUpdate(delta, i, norm, _alpha, _epsilon, gradient, weight);
180
0
        }
181
182
        SIMD_INLINE float Convolution2(const float * src, const float * weights)
183
0
        {
184
0
            return src[0] * weights[0] + src[1] * weights[1];
185
0
        }
186
187
        SIMD_INLINE float Convolution2x2Forward(const float * src, size_t stride, const float * weights)
188
0
        {
189
0
            return
190
0
                Convolution2(src, weights) +
191
0
                Convolution2(src + stride, weights + 2);
192
0
        }
193
194
        void NeuralAddConvolution2x2Forward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
195
0
        {
196
0
            for (size_t row = 0; row < height; ++row)
197
0
            {
198
0
                for (size_t col = 0; col < width; ++col)
199
0
                    dst[col] += Convolution2x2Forward(src + col, srcStride, weights);
200
0
                src += srcStride;
201
0
                dst += dstStride;
202
0
            }
203
0
        }
204
205
        SIMD_INLINE float Convolution3(const float * src, const float * weights)
206
0
        {
207
0
            return src[0] * weights[0] + src[1] * weights[1] + src[2] * weights[2];
208
0
        }
209
210
        SIMD_INLINE float Convolution3x3Forward(const float * src, size_t stride, const float * weights)
211
0
        {
212
0
            return
213
0
                Convolution3(src, weights) +
214
0
                Convolution3(src + stride, weights + 3) +
215
0
                Convolution3(src + 2 * stride, weights + 6);
216
0
        }
217
218
        void NeuralAddConvolution3x3Forward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
219
0
        {
220
0
            for (size_t row = 0; row < height; ++row)
221
0
            {
222
0
                for (size_t col = 0; col < width; ++col)
223
0
                    dst[col] += Convolution3x3Forward(src + col, srcStride, weights);
224
0
                src += srcStride;
225
0
                dst += dstStride;
226
0
            }
227
0
        }
228
229
        SIMD_INLINE float Convolution4(const float * src, const float * weights)
230
0
        {
231
0
            return src[0] * weights[0] + src[1] * weights[1] + src[2] * weights[2] + src[3] * weights[3];
232
0
        }
233
234
        SIMD_INLINE float Convolution4x4Forward(const float * src, size_t stride, const float * weights)
235
0
        {
236
0
            return
237
0
                Convolution4(src, weights) +
238
0
                Convolution4(src + stride, weights + 4) +
239
0
                Convolution4(src + 2 * stride, weights + 8) +
240
0
                Convolution4(src + 3 * stride, weights + 12);
241
0
        }
242
243
        void NeuralAddConvolution4x4Forward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
244
0
        {
245
0
            for (size_t row = 0; row < height; ++row)
246
0
            {
247
0
                for (size_t col = 0; col < width; ++col)
248
0
                    dst[col] += Convolution4x4Forward(src + col, srcStride, weights);
249
0
                src += srcStride;
250
0
                dst += dstStride;
251
0
            }
252
0
        }
253
254
        SIMD_INLINE float Convolution5(const float * src, const float * weights)
255
0
        {
256
0
            return src[0] * weights[0] + src[1] * weights[1] + src[2] * weights[2] + src[3] * weights[3] + src[4] * weights[4];
257
0
        }
258
259
        SIMD_INLINE float Convolution5x5Forward(const float * src, size_t stride, const float * weights)
260
0
        {
261
0
            return
262
0
                Convolution5(src, weights) +
263
0
                Convolution5(src + stride, weights + 5) +
264
0
                Convolution5(src + 2 * stride, weights + 10) +
265
0
                Convolution5(src + 3 * stride, weights + 15) +
266
0
                Convolution5(src + 4 * stride, weights + 20);
267
0
        }
268
269
        void NeuralAddConvolution5x5Forward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
270
0
        {
271
0
            for (size_t row = 0; row < height; ++row)
272
0
            {
273
0
                for (size_t col = 0; col < width; ++col)
274
0
                    dst[col] += Convolution5x5Forward(src + col, srcStride, weights);
275
0
                src += srcStride;
276
0
                dst += dstStride;
277
0
            }
278
0
        }
279
280
        template <size_t coreX, size_t coreY> SIMD_INLINE void NeuralAddConvolutionBackward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
281
0
        {
282
0
            size_t aligned = Simd::AlignLo(width, 4);
283
0
            for (size_t row = 0; row < height; ++row)
284
0
            {
285
0
                for (size_t dy = 0; dy < coreY; ++dy)
286
0
                {
287
0
                    const float * w = weights + dy * coreX;
288
0
                    float * d = dst + dy*dstStride;
289
0
                    for (size_t dx = 0; dx < coreX; ++dx)
290
0
                        AddMultiplied(src, aligned, width, w[dx], d + dx);
291
0
                }
292
0
                src += srcStride;
293
0
                dst += dstStride;
294
0
            }
295
0
        }
Unexecuted instantiation: void Simd::Base::NeuralAddConvolutionBackward<2ul, 2ul>(float const*, unsigned long, unsigned long, unsigned long, float const*, float*, unsigned long)
Unexecuted instantiation: void Simd::Base::NeuralAddConvolutionBackward<3ul, 3ul>(float const*, unsigned long, unsigned long, unsigned long, float const*, float*, unsigned long)
Unexecuted instantiation: void Simd::Base::NeuralAddConvolutionBackward<4ul, 4ul>(float const*, unsigned long, unsigned long, unsigned long, float const*, float*, unsigned long)
Unexecuted instantiation: void Simd::Base::NeuralAddConvolutionBackward<5ul, 5ul>(float const*, unsigned long, unsigned long, unsigned long, float const*, float*, unsigned long)
296
297
        void NeuralAddConvolution2x2Backward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
298
0
        {
299
0
            NeuralAddConvolutionBackward<2, 2>(src, srcStride, width, height, weights, dst, dstStride);
300
0
        }
301
302
        void NeuralAddConvolution3x3Backward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
303
0
        {
304
0
            NeuralAddConvolutionBackward<3, 3>(src, srcStride, width, height, weights, dst, dstStride);
305
0
        }
306
307
        void NeuralAddConvolution4x4Backward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
308
0
        {
309
0
            NeuralAddConvolutionBackward<4, 4>(src, srcStride, width, height, weights, dst, dstStride);
310
0
        }
311
312
        void NeuralAddConvolution5x5Backward(const float * src, size_t srcStride, size_t width, size_t height, const float * weights, float * dst, size_t dstStride)
313
0
        {
314
0
            NeuralAddConvolutionBackward<5, 5>(src, srcStride, width, height, weights, dst, dstStride);
315
0
        }
316
317
        template <size_t coreX, size_t coreY> SIMD_INLINE void NeuralAddConvolutionSum(const float * src, size_t srcStride, const float * dst, size_t dstStride, size_t width, size_t height, float * sums)
318
0
        {
319
0
            size_t aligned = Simd::AlignLo(width, 4);
320
0
            for (size_t row = 0; row < height; ++row)
321
0
            {
322
0
                for (size_t dy = 0; dy < coreY; ++dy)
323
0
                {
324
0
                    const float * s = src + dy*srcStride;
325
0
                    float * sum = sums + dy * coreX;
326
0
                    for (size_t dx = 0; dx < coreX; ++dx)
327
0
                        sum[dx] += ProductSum(s + dx, dst, aligned, width);
328
0
                }
329
0
                src += srcStride;
330
0
                dst += dstStride;
331
0
            }
332
0
        }
Unexecuted instantiation: void Simd::Base::NeuralAddConvolutionSum<2ul, 2ul>(float const*, unsigned long, float const*, unsigned long, unsigned long, unsigned long, float*)
Unexecuted instantiation: void Simd::Base::NeuralAddConvolutionSum<3ul, 3ul>(float const*, unsigned long, float const*, unsigned long, unsigned long, unsigned long, float*)
Unexecuted instantiation: void Simd::Base::NeuralAddConvolutionSum<4ul, 4ul>(float const*, unsigned long, float const*, unsigned long, unsigned long, unsigned long, float*)
Unexecuted instantiation: void Simd::Base::NeuralAddConvolutionSum<5ul, 5ul>(float const*, unsigned long, float const*, unsigned long, unsigned long, unsigned long, float*)
333
334
        void NeuralAddConvolution2x2Sum(const float * src, size_t srcStride, const float * dst, size_t dstStride, size_t width, size_t height, float * sums)
335
0
        {
336
0
            NeuralAddConvolutionSum<2, 2>(src, srcStride, dst, dstStride, width, height, sums);
337
0
        }
338
339
        void NeuralAddConvolution3x3Sum(const float * src, size_t srcStride, const float * dst, size_t dstStride, size_t width, size_t height, float * sums)
340
0
        {
341
0
            NeuralAddConvolutionSum<3, 3>(src, srcStride, dst, dstStride, width, height, sums);
342
0
        }
343
344
        void NeuralAddConvolution4x4Sum(const float * src, size_t srcStride, const float * dst, size_t dstStride, size_t width, size_t height, float * sums)
345
0
        {
346
0
            NeuralAddConvolutionSum<4, 4>(src, srcStride, dst, dstStride, width, height, sums);
347
0
        }
348
349
        void NeuralAddConvolution5x5Sum(const float * src, size_t srcStride, const float * dst, size_t dstStride, size_t width, size_t height, float * sums)
350
0
        {
351
0
            NeuralAddConvolutionSum<5, 5>(src, srcStride, dst, dstStride, width, height, sums);
352
0
        }
353
354
        SIMD_INLINE float Max2(const float * src)
355
0
        {
356
0
            return Simd::Max(src[0], src[1]);
357
0
        }
358
359
        SIMD_INLINE float Max2x2(const float * src, size_t stride)
360
0
        {
361
0
            return Simd::Max(Max2(src), Max2(src + stride));
362
0
        }
363
364
        SIMD_INLINE float Max3(const float * src)
365
0
        {
366
0
            return Simd::Max(src[0], Simd::Max(src[1], src[2]));
367
0
        }
368
369
        SIMD_INLINE float Max3x3(const float * src, size_t stride)
370
0
        {
371
0
            return Simd::Max(Max3(src), Simd::Max(Max3(src + stride), Max3(src + 2 * stride)));
372
0
        }
373
374
        SIMD_INLINE float Max2x3(const float * src, size_t stride)
375
0
        {
376
0
            return Simd::Max(Max2(src), Simd::Max(Max2(src + stride), Max2(src + 2 * stride)));
377
0
        }
378
379
        SIMD_INLINE float Max3x2(const float * src, size_t stride)
380
0
        {
381
0
            return Simd::Max(Max3(src), Max3(src + stride));
382
0
        }
383
384
        void NeuralPooling1x1Max3x3(const float * src, size_t srcStride, size_t width, size_t height, float * dst, size_t dstStride)
385
0
        {
386
0
            height -= 1;
387
0
            width -= 1;
388
0
            src -= 1;
389
390
0
            dst[0] = Max2x2(src + 1, srcStride);
391
0
            for (size_t col = 1; col < width; ++col)
392
0
                dst[col] = Max3x2(src + col, srcStride);
393
0
            dst[width] = Max2x2(src + width, srcStride);
394
0
            dst += dstStride;
395
396
0
            for (size_t row = 1; row < height; ++row)
397
0
            {
398
0
                dst[0] = Max2x3(src + 1, srcStride);
399
0
                for (size_t col = 1; col < width; ++col)
400
0
                    dst[col] = Max3x3(src + col, srcStride);
401
0
                dst[width] = Max2x3(src + width, srcStride);
402
0
                src += srcStride;
403
0
                dst += dstStride;
404
0
            }
405
406
0
            dst[0] = Max2x2(src + 1, srcStride);
407
0
            for (size_t col = 1; col < width; ++col)
408
0
                dst[col] = Max3x2(src + col, srcStride);
409
0
            dst[width] = Max2x2(src + width, srcStride);
410
0
        }
411
412
        void NeuralPooling2x2Max2x2(const float * src, size_t srcStride, size_t width, size_t height, float * dst, size_t dstStride)
413
0
        {
414
0
            size_t heightEven = Simd::AlignLo(height, 2);
415
0
            size_t widthEven = Simd::AlignLo(width, 2);
416
0
            for (size_t row = 0; row < heightEven; row += 2)
417
0
            {
418
0
                for (size_t col = 0; col < widthEven; col += 2)
419
0
                    dst[col >> 1] = Max2x2(src + col, srcStride);
420
0
                if (width - widthEven)
421
0
                    dst[widthEven >> 1] = Simd::Max(src[widthEven], src[widthEven + srcStride]);
422
0
                src += 2 * srcStride;
423
0
                dst += dstStride;
424
0
            }
425
0
            if (height - heightEven)
426
0
            {
427
0
                for (size_t col = 0; col < widthEven; col += 2)
428
0
                    dst[col >> 1] = Simd::Max(src[col], src[col + 1]);
429
0
                if (width - widthEven)
430
0
                    dst[widthEven >> 1] = src[widthEven];
431
0
            }
432
0
        }
433
434
        void NeuralPooling2x2Max3x3(const float * src, size_t srcStride, size_t width, size_t height, float * dst, size_t dstStride)
435
0
        {
436
0
            height -= 1;
437
0
            width -= 1;
438
0
            size_t heightEven = Simd::AlignLo(height, 2);
439
0
            size_t widthEven = Simd::AlignLo(width, 2);
440
0
            for (size_t row = 0; row < heightEven; row += 2)
441
0
            {
442
0
                for (size_t col = 0; col < widthEven; col += 2)
443
0
                    dst[col >> 1] = Max3x3(src + col, srcStride);
444
0
                if (width - widthEven)
445
0
                    dst[widthEven >> 1] = Max2x3(src + widthEven, srcStride);
446
0
                src += 2 * srcStride;
447
0
                dst += dstStride;
448
0
            }
449
0
            if (height - heightEven)
450
0
            {
451
0
                for (size_t col = 0; col < widthEven; col += 2)
452
0
                    dst[col >> 1] = Max3x2(src + col, srcStride);
453
0
                if (width - widthEven)
454
0
                    dst[widthEven >> 1] = Max2x2(src + widthEven, srcStride);
455
0
            }
456
0
        }
457
458
        SIMD_INLINE bool NeuralConvolutionForwardValid(ptrdiff_t a, ptrdiff_t b)
459
0
        {
460
0
            return size_t(a) < size_t(b);
461
0
        }
462
463
        void NeuralConvolutionForwardConvertN(const float * src, ptrdiff_t srcWidth, ptrdiff_t srcHeight, ptrdiff_t srcDepth, ptrdiff_t kernelX, ptrdiff_t kernelY,
464
            ptrdiff_t padX, ptrdiff_t padY, ptrdiff_t strideX, ptrdiff_t strideY, ptrdiff_t dilationX, ptrdiff_t dilationY, float * dst)
465
0
        {
466
0
            const ptrdiff_t dstHeight = (srcHeight + 2 * padY - (dilationY * (kernelY - 1) + 1)) / strideY + 1;
467
0
            const ptrdiff_t dstWidth = (srcWidth + 2 * padX - (dilationX * (kernelX - 1) + 1)) / strideX + 1;
468
0
            const ptrdiff_t channelSize = srcHeight * srcWidth;
469
0
            for (ptrdiff_t channel = 0; channel < srcDepth; ++channel, src += channelSize)
470
0
            {
471
0
                for (ptrdiff_t kernelRow = 0; kernelRow < kernelY; ++kernelRow)
472
0
                {
473
0
                    for (ptrdiff_t kernelCol = 0; kernelCol < kernelX; ++kernelCol)
474
0
                    {
475
0
                        ptrdiff_t srcRow = kernelRow*dilationY - padY;
476
0
                        for (ptrdiff_t dstRow = 0; dstRow < dstHeight; ++dstRow)
477
0
                        {
478
0
                            if (!NeuralConvolutionForwardValid(srcRow, srcHeight))
479
0
                            {
480
0
                                for (ptrdiff_t dstCol = 0; dstCol < dstWidth; ++dstCol)
481
0
                                    *(dst++) = 0;
482
0
                            }
483
0
                            else
484
0
                            {
485
0
                                ptrdiff_t srcCol = kernelCol*dilationX - padX;
486
0
                                for (ptrdiff_t dstCol = 0; dstCol < dstWidth; ++dstCol)
487
0
                                {
488
0
                                    if (NeuralConvolutionForwardValid(srcCol, srcWidth))
489
0
                                        *(dst++) = src[srcRow*srcWidth + srcCol];
490
0
                                    else
491
0
                                        *(dst++) = 0;
492
0
                                    srcCol += strideX;
493
0
                                }
494
0
                            }
495
0
                            srcRow += strideY;
496
0
                        }
497
0
                    }
498
0
                }
499
0
            }
500
0
        }
501
502
        void NeuralConvolutionForwardConvertT(const float * src, ptrdiff_t srcWidth, ptrdiff_t srcHeight, ptrdiff_t srcDepth, ptrdiff_t kernelX, ptrdiff_t kernelY,
503
            ptrdiff_t padX, ptrdiff_t padY, ptrdiff_t strideX, ptrdiff_t strideY, ptrdiff_t dilationX, ptrdiff_t dilationY, float * dst)
504
0
        {
505
0
            const ptrdiff_t dstHeight = (srcHeight + 2 * padY - (dilationY * (kernelY - 1) + 1)) / strideY + 1;
506
0
            const ptrdiff_t dstWidth = (srcWidth + 2 * padX - (dilationX * (kernelX - 1) + 1)) / strideX + 1;
507
0
            for (ptrdiff_t dstRow = 0; dstRow < dstHeight; ++dstRow)
508
0
            {
509
0
                ptrdiff_t srcRow0 = dstRow*strideY - padY;
510
0
                for (ptrdiff_t dstCol = 0; dstCol < dstWidth; ++dstCol)
511
0
                {
512
0
                    ptrdiff_t srcCol0 = dstCol*strideX - padX;
513
0
                    for (ptrdiff_t channel = 0; channel < srcDepth; ++channel)
514
0
                    {
515
0
                        ptrdiff_t dstChannelOffset = ((dstRow*dstWidth + dstCol)*srcDepth + channel)*kernelY*kernelX;
516
0
                        for (ptrdiff_t kernelRow = 0; kernelRow < kernelY; ++kernelRow)
517
0
                        {
518
0
                            ptrdiff_t srcRow = srcRow0 + kernelRow*dilationY;
519
0
                            for (ptrdiff_t kernelCol = 0; kernelCol < kernelX; ++kernelCol)
520
0
                            {
521
0
                                ptrdiff_t srcCol = srcCol0 + kernelCol*dilationX;
522
0
                                ptrdiff_t dstOffset = dstChannelOffset + kernelRow*kernelX + kernelCol;
523
0
                                if (NeuralConvolutionForwardValid(srcRow, srcHeight) && NeuralConvolutionForwardValid(srcCol, srcWidth))
524
0
                                    dst[dstOffset] = src[(channel*srcHeight + srcRow)*srcWidth + srcCol];
525
0
                                else
526
0
                                    dst[dstOffset] = 0;
527
0
                            }
528
0
                        }
529
0
                    }
530
0
                }
531
0
            }
532
0
        }
533
534
        void NeuralConvolutionForwardGemmNN(size_t M, size_t N, size_t K, const float * a, const float * b, float * c)
535
0
        {
536
0
            for (size_t i = 0; i < M; ++i)
537
0
            {
538
0
                for (size_t k = 0; k < K; ++k)
539
0
                {
540
0
                    float va = a[i*K + k];
541
0
                    const float * pb = b + k*N;
542
0
                    float * pc = c + i*N;
543
0
                    for (size_t j = 0; j < N; ++j)
544
0
                        pc[j] += va*pb[j];
545
0
                }
546
0
            }
547
0
        }
548
549
        void NeuralConvolutionForwardGemmNT(size_t M, size_t N, size_t K, const float * a, const float * b, float * c)
550
0
        {
551
0
            for (size_t i = 0; i < M; ++i)
552
0
            {
553
0
                for (size_t j = 0; j < N; ++j)
554
0
                {
555
0
                    float s = 0;
556
0
                    const float * pa = a + i*K;
557
0
                    const float * pb = b + j*K;
558
0
                    for (size_t k = 0; k < K; ++k)
559
0
                        s += pa[k] * pb[k];
560
0
                    c[i*N + j] += s;
561
0
                }
562
0
            }
563
0
        }
564
565
        void NeuralConvolutionForward(const float * src, size_t srcWidth, size_t srcHeight, size_t srcDepth,
566
            const float * weight, size_t kernelX, size_t kernelY, size_t padX, size_t padY, size_t strideX, size_t strideY, size_t dilationX, size_t dilationY,
567
            void * buffer, size_t * size, float * dst, size_t dstWidth, size_t dstHeight, size_t dstDepth, int add)
568
0
        {
569
0
            assert(dstWidth == (srcWidth + 2 * padX - (dilationX * (kernelX - 1) + 1)) / strideX + 1);
570
0
            assert(dstHeight == (srcHeight + 2 * padY - (dilationY * (kernelY - 1) + 1)) / strideY + 1);
571
572
0
            if (!add)
573
0
                memset(dst, 0, dstWidth*dstHeight*dstDepth * sizeof(float));
574
575
0
            float * temporal = NULL;
576
0
            void * internal = NULL;
577
578
0
            bool transpose = dstWidth*dstHeight <= 1024;// && srcDepth > 128;
579
580
0
            if (kernelX == 1 && kernelY == 1 && !transpose)
581
0
                temporal = (float*)src;
582
0
            else
583
0
            {
584
0
                size_t required = dstWidth*dstHeight*srcDepth*kernelX*kernelY * sizeof(float);
585
0
                if (buffer != AlignHi(buffer, SIMD_ALIGN))
586
0
                    required += SIMD_ALIGN;
587
0
                if (buffer == NULL || size == NULL || *size < required)
588
0
                {
589
0
                    internal = Allocate(required);
590
0
                    if (size)
591
0
                        *size = required;
592
0
                    temporal = (float*)internal;
593
0
                }
594
0
                else
595
0
                    temporal = (float*)AlignHi(buffer, SIMD_ALIGN);
596
597
0
                if (transpose)
598
0
                    NeuralConvolutionForwardConvertT(src, srcWidth, srcHeight, srcDepth, kernelX, kernelY, padX, padY, strideX, strideY, dilationX, dilationY, temporal);
599
0
                else
600
0
                    NeuralConvolutionForwardConvertN(src, srcWidth, srcHeight, srcDepth, kernelX, kernelY, padX, padY, strideX, strideY, dilationX, dilationY, temporal);
601
0
            }
602
603
0
            size_t M = dstDepth, N = dstHeight*dstWidth, K = kernelX*kernelY*srcDepth;
604
0
            if (transpose)
605
0
                NeuralConvolutionForwardGemmNT(M, N, K, weight, temporal, dst);
606
0
            else
607
0
                NeuralConvolutionForwardGemmNN(M, N, K, weight, temporal, dst);
608
609
0
            if (internal)
610
0
                Free(internal);
611
0
        }
612
    }
613
}