Removed custom typedef from nbody

Removed custom typedef from nbody

/**
 * Copyright (C) 2010 Erik Rainey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

__kernel void kernel_magnitude(__global float4 *p,
                               __global float *mag)
{
    int i = get_global_id(0);
    // mag = sqrt(x^2+y^2+z^2)
    mag[i] = sqrt(p[i][0]*p[i][0] + p[i][1]*p[i][1] + p[i][2]*p[i][2]);
}

__kernel void kernel_renormalize(__global float4 *p,
                                 __global float *om,
                                 __global float *nm)
{
    int i = get_global_id(0);

    kernel_magnitude(p,om);

    p[i][0] /= om[i];
    p[i][1] /= om[i];
    p[i][2] /= om[i];

    p[i][0] *= nm[i];
    p[i][1] *= nm[i];
    p[i][2] *= nm[i];
}

__kernel void kernel_distance(__global float4 *v1,
                              __global float4 *v2,
                              __global float *d)
{
    int i = get_global_id(0);

    float dx = v2[i][0] - v1[i][0];
    float dy = v2[i][1] - v1[i][1];
    float dz = v2[i][2] - v1[i][2];

    // distance = sqrt(sum(dx^2 + dy^2 + dz^2))
    d[i] = sqrt(dx*dx + dy*dy + dz*dz);
}

__kernel kernel_gravity(__global float Mg,
                        __global float *m,
                        __global float *d,
                        __global float *f_mag)
{
    int i = get_global_id(0); // data fields are 1 dimensional

    // G = gravitational constant = 6.67300  10^-11 m^3 kg^-1 s^-2
    float G = 0.00000000006673;

    // force of gravity = (M1*M2*G)/d^2
    f_mag[i] = (Mg * m[i] * G)/(d[i] * d[i]);
}

__kernel kernel_kinetic(__global float *m,
                        __global float4 *v,
                        __global float4 *ke)
{
    int i = get_global_id(0);

    ke[i][0] = 0.5 * m[i] * v[i][0] * v[i][0];
    ke[i][1] = 0.5 * m[i] * v[i][1] * v[i][1];
    ke[i][2] = 0.5 * m[i] * v[i][2] * v[i][2];
}

__kernel kernel_energy(__global *m,
                       __global *E)
{
    int i = get_global_id(0);
    float c = 299792458; // speed of light in meters/second;
    E[i] = m[i] * c * c;
}

__kernel kernel_nbody(__global float *ms,
					  __global float *m,
                      __global float4 *a,
                      __global float4 *v,
                      __global float4 *p,
                      __global float *t,
                      __global float *d,
                      __global float *g)
{
    int i = get_global_id(0);
    float G = 0.00000000006673;

	// position is updated by velocity and some small quantity of acceration
    p[i][0] += v[i][0]*t[i] + 0.5*t[i]*t[i]*a[i][0];
    p[i][1] += v[i][1]*t[i] + 0.5*t[i]*t[i]*a[i][1];
    p[i][2] += v[i][2]*t[i] + 0.5*t[i]*t[i]*a[i][2];

    // velocity is updated by acceration and friction (which is zero in space)
    v[i][0] += a[i][0]*t[i];
    v[i][1] += a[i][1]*t[i];
    v[i][2] += a[i][2]*t[i];

    // calculate the magnitude of the position with respec to 0,0,0 (this is a shortcut for distance)
    d[i] = sqrt(p[i][0]*p[i][0] + p[i][1]*p[i][1] + p[i][2]*p[i][2]);

    // gravity is based on position and mass.
    g[i] = (ms[i] * m[i] * G)/(d[i]*d[i]);

    // acceleration is updated by changes in gravity, which is based on position
    a[i][0] = -p[i][0]*g[i]/d[i];
    a[i][1] = -p[i][1]*g[i]/d[i];
    a[i][2] = -p[i][2]*g[i]/d[i];
}

/**
 * Copyright (C) 2010 Erik Rainey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

typedef float cl_float4[4];

__kernel void kernel_magnitude(__global cl_float4 *p,
                               __global float *mag)
{
    int i = get_global_id(0);
    // mag = sqrt(x^2+y^2+z^2)
    mag[i] = sqrt(p[i][0]*p[i][0] + p[i][1]*p[i][1] + p[i][2]*p[i][2]);
}

__kernel void kernel_renormalize(__global cl_float4 *p,
                                 __global float *om,
                                 __global float *nm)
{
    int i = get_global_id(0);

    kernel_magnitude(p,om);

    p[i][0] /= om[i];
    p[i][1] /= om[i];
    p[i][2] /= om[i];

    p[i][0] *= nm[i];
    p[i][1] *= nm[i];
    p[i][2] *= nm[i];
}

__kernel void kernel_distance(__global cl_float4 *v1,
                              __global cl_float4 *v2,
                              __global float *d)
{
    int i = get_global_id(0);

    float dx = v2[i][0] - v1[i][0];
    float dy = v2[i][1] - v1[i][1];
    float dz = v2[i][2] - v1[i][2];

    // distance = sqrt(sum(dx^2 + dy^2 + dz^2))
    d[i] = sqrt(dx*dx + dy*dy + dz*dz);
}

__kernel kernel_gravity(__global float Mg,
                        __global float *m,
                        __global float *d,
                        __global float *f_mag)
{
    int i = get_global_id(0); // data fields are 1 dimensional

    // G = gravitational constant = 6.67300  10^-11 m^3 kg^-1 s^-2
    float G = 0.00000000006673;

    // force of gravity = (M1*M2*G)/d^2
    f_mag[i] = (Mg * m[i] * G)/(d[i] * d[i]);
}

__kernel kernel_kinetic(__global float *m,
                        __global cl_float4 *v,
                        __global cl_float4 *ke)
{
    int i = get_global_id(0);

    ke[i][0] = 0.5 * m[i] * v[i][0] * v[i][0];
    ke[i][1] = 0.5 * m[i] * v[i][1] * v[i][1];
    ke[i][2] = 0.5 * m[i] * v[i][2] * v[i][2];
}

__kernel kernel_energy(__global *m,
                       __global *E)
{
    int i = get_global_id(0);
    float c = 299792458; // speed of light in meters/second;
    E[i] = m[i] * c * c;
}

__kernel kernel_nbody(__global float *ms,
					  __global float *m,
                      __global cl_float4 *a,
                      __global cl_float4 *v,
                      __global cl_float4 *p,
                      __global float *t,
                      __global float *d,
                      __global float *g)
{
    int i = get_global_id(0);
    float G = 0.00000000006673;

	// position is updated by velocity and some small quantity of acceration
    p[i][0] += v[i][0]*t[i] + 0.5*t[i]*t[i]*a[i][0];
    p[i][1] += v[i][1]*t[i] + 0.5*t[i]*t[i]*a[i][1];
    p[i][2] += v[i][2]*t[i] + 0.5*t[i]*t[i]*a[i][2];

    // velocity is updated by acceration and friction (which is zero in space)
    v[i][0] += a[i][0]*t[i];
    v[i][1] += a[i][1]*t[i];
    v[i][2] += a[i][2]*t[i];

    // calculate the magnitude of the position with respec to 0,0,0 (this is a shortcut for distance)
    d[i] = sqrt(p[i][0]*p[i][0] + p[i][1]*p[i][1] + p[i][2]*p[i][2]);

    // gravity is based on position and mass.
    g[i] = (ms[i] * m[i] * G)/(d[i]*d[i]);

    // acceleration is updated by changes in gravity, which is based on position
    a[i][0] = -p[i][0]*g[i]/d[i];
    a[i][1] = -p[i][1]*g[i]/d[i];
    a[i][2] = -p[i][2]*g[i]/d[i];
}


whatever Jenkins likes

whatever Jenkins likes

/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2017 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 *******************************************************************************/
#define PPCAT_NX(A, B) A##B
#define PPCAT(A, B) PPCAT_NX(A, B)
#define TWO 2
#define FOUR 4
#define EIGHT 8

#if MIOPEN_USE_FP16 == 1
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
#define _FLOAT half
#endif
#if MIOPEN_USE_FP32 == 1
#define _FLOAT float
#endif

#define _FLOAT2 PPCAT(_FLOAT, TWO)
#define _FLOAT4 PPCAT(_FLOAT, FOUR)
#define _FLOAT8 PPCAT(_FLOAT, EIGHT)

#define UNUSED __attribute__((__unused__))

#define MLO_NRN_GROUP_SZ2 1

#define MLO_NEURON_PASTHRU 0                       // x
#define MLO_NEURON_LOGISTIC MLO_NEURON_PASTHRU + 1 //	1 / (1 + e^-x)	//Sigmoid
#define MLO_NEURON_TANH MLO_NEURON_LOGISTIC + 1    //	a * tanh( b * x)
#define MLO_NEURON_RELU MLO_NEURON_TANH + 1        //	max(0, x)
#define MLO_NEURON_SOFTRELU \
    MLO_NEURON_RELU + 1                        //	log(1 + e^x)   // bonomial normal log likelihood
#define MLO_NEURON_ABS MLO_NEURON_SOFTRELU + 1 //	abs(x)
#define MLO_NEURON_POWER MLO_NEURON_ABS + 1    // (a + b * x ) ^power
//#define MLO_NEURON_BRELU		MLO_NEURON_POWER + 1		//	min(a, max(0, x))
//#define MLO_NEURON_SQUARE		BRELU + 1			//	x^2
//#define MLO_NEURON_SQR			MLO_NEURON_SQUARE + 1		//	sqr(x)
//#define MLO_NEURON_LINEAR		MLO_NEURON_SQR	+ 1			//	a + b * x
#define MLO_NEURON_TOTAL MLO_NEURON_POWER + 1

__attribute__((always_inline)) void
ActivationFunction_PassThru(uint n, _FLOAT* res, const _FLOAT* data)
{
    for(uint i = 0; i < n; i++)
    {
        res[i] = data[i];
    }
}

__attribute__((always_inline)) void
ActivationFunction_ReLU(uint n, _FLOAT* res, const _FLOAT* data, _FLOAT slope)
{
    for(uint i = 0; i < n; ++i)
    {
        res[i] = (data[i] > 0) ? data[i] : data[i] * slope;
    }
}

__attribute__((always_inline)) void
ActivationFunction_BReLU(uint n, _FLOAT* res, const _FLOAT* data, _FLOAT alpha)
{

    for(uint i = 0; i < n; ++i)
    {
        res[i] = fmin(alpha, fmax(data[i], 0));
    }
}

__attribute__((always_inline)) void
ActivationFunction_Sigmoid(uint n, _FLOAT* res, const _FLOAT* data)
{
    for(uint i = 0; i < n; i++)
    {
        // 1/(1 + exp(-x))
        res[i] = (_FLOAT)1.f / ((_FLOAT)1.f + exp(-data[i]));
    }
}

__attribute__((always_inline)) void
ActivationFunction_TanH(int n, _FLOAT* res, const _FLOAT* data, _FLOAT alpha, _FLOAT beta)
{
    for(uint i = 0; i < n; i++)
    {
        // (exp(2x) -1) / (exp(2x) + 1)
        res[i] = alpha * tanh(beta * data[i]);
    }
}
__attribute__((always_inline)) void ActivationFunction_Abs(uint n, _FLOAT* res, const _FLOAT* data)
{
    for(uint i = 0; i < n; i++)
    {
        res[i] = fabs(data[i]);
    }
}

__attribute__((always_inline)) void
ActivationFunction_Square(uint n, _FLOAT* res, const _FLOAT* data)
{
    for(uint i = 0; i < n; i++)
    {

        res[i] = data[i] * data[i];
    }
}

__attribute__((always_inline)) void ActivationFunction_Sqrt(uint n, _FLOAT* res, const _FLOAT* data)
{
    for(uint i = 0; i < n; i++)
    {

        res[i] = sqrt(data[i]);
    }
}

__attribute__((always_inline)) void
ActivationFunction_Linear(uint n, _FLOAT* res, const _FLOAT* data, _FLOAT alpha, _FLOAT beta)
{
    for(uint i = 0; i < n; i++)
    {
        // (exp(2x) -1) / (exp(2x) + 1)
        res[i] = alpha + beta * data[i];
    }
}

__attribute__((always_inline)) void ActivationFunction_Power(
    uint n, _FLOAT* res, const _FLOAT* data, _FLOAT power, _FLOAT alpha, _FLOAT beta)
{
    for(uint i = 0; i < n; i++)
    {
        // (shift + scale * x ) ^power
        _FLOAT arg     = alpha + data[i] * beta;
        _FLOAT run_arg = (arg == (_FLOAT)0) ? (_FLOAT)1 : arg;
        res[i]         = (arg == (_FLOAT)0) ? (_FLOAT)0 : pow(run_arg, power);
    }
}

__attribute__((always_inline)) void ActivationFunction_BNLL(uint n, _FLOAT* res, const _FLOAT* data)

{
    for(uint i = 0; i < n; i++)
    {
        //	log(1 + exp(x))
        res[i] = (data[i] > 0) ? data[i] + log((_FLOAT)1.f + exp(-data[i]))
                               : log((_FLOAT)(1.f) + exp(data[i]));
    }
}

void ActivationFunction(
    uint n, _FLOAT* res, const _FLOAT* data, _FLOAT power, _FLOAT alpha, _FLOAT beta)
{
    (void)power;
    (void)alpha;
    (void)beta;
#if MLO_NRN_OP_ID == MLO_NEURON_PASTHRU

    ActivationFunction_PassThru(n, res, data);

#elif MLO_NRN_OP_ID == MLO_NEURON_LOGISTIC
    // 1/(1 + exp(-x))
    ActivationFunction_Sigmoid(n, res, data);

#elif MLO_NRN_OP_ID == MLO_NEURON_TANH
    // (exp(2x) -1) / (exp(2x) + 1)
    ActivationFunction_TanH(n, res, data, alpha, beta);

#elif MLO_NRN_OP_ID == MLO_NEURON_RELU
    ActivationFunction_ReLU(n, res, data, alpha);

//#elif	MLO_NRN_OP_ID==MLO_NEURON_BRELU
//	ActivationFunction_BReLU(n, res, data, alpha);

#elif MLO_NRN_OP_ID == MLO_NEURON_SOFTRELU
    //	log(1 + exp(x))
    ActivationFunction_BNLL(n, res, data);
#elif MLO_NRN_OP_ID == MLO_NEURON_ABS
    ActivationFunction_Abs(n, res, data);

//#elif	MLO_NRN_OP_ID==MLO_NEURON_SQUARE
//	ActivationFunction_Square(res, data);

//#elif	MLO_NRN_OP_ID==MLO_NEURON_SQR
//	ActivationFunction_Sqrt(n, res, data);

#elif MLO_NRN_OP_ID == MLO_NEURON_POWER
    // (shift + scale * x ) ^power

    ActivationFunction_Power(n, res, data, power, alpha, beta);

#endif
}

/******************************************************************************/
/*                                  DIFF                                      */
/******************************************************************************/

static __constant _FLOAT kBNLL_THRESHOLD = (_FLOAT)50.;

__attribute__((always_inline)) void ActivationFunction_ReLU_Diff(uint n,
                                                                 _FLOAT* bot_diff,
                                                                 const _FLOAT* top_diff,
                                                                 const _FLOAT* bot_data,
                                                                 UNUSED _FLOAT negative_slope)
{

    for(uint i = 0; i < n; ++i)
    {
        bot_diff[i] = top_diff[i] * (bot_data[i] > 0);
    }
}

__attribute__((always_inline)) void ActivationFunction_TanH_Diff(uint n,
                                                                 _FLOAT* bot_diff,
                                                                 const _FLOAT* top_diff,
                                                                 const _FLOAT* top_data)
{
    for(uint i = 0; i < n; i++)
    {
        // (exp(2x) -1) / (exp(2x) + 1)
        _FLOAT tanh_x = top_data[i];
        bot_diff[i]   = top_diff[i] * (1 - tanh_x * tanh_x);
    }
}

__attribute__((always_inline)) void ActivationFunction_Sigmoid_Diff(uint n,
                                                                    _FLOAT* bot_diff,
                                                                    const _FLOAT* top_diff,
                                                                    const _FLOAT* top_data)
{
    for(uint i = 0; i < n; i++)
    {
        // 1/(1 + exp(-x))
        _FLOAT sigmoid_x = top_data[i];
        bot_diff[i]      = top_diff[i] * sigmoid_x * ((_FLOAT)1.f - sigmoid_x);
    }
}

__attribute__((always_inline)) void ActivationFunction_Abs_Diff(uint n,
                                                                _FLOAT* bot_diff,
                                                                const _FLOAT* top_diff,
                                                                const _FLOAT* bot_data)
{
    for(uint i = 0; i < n; i++)
    {
        bot_diff[i] = top_diff[i] * ((bot_data[i] >= 0) ? 1 : -1);
    }
}

// Compute dy/dx = scale * power * (shift + scale * x)^(power - 1)
//               = diff_scale * y / (shift + scale * x)
__attribute__((always_inline)) void ActivationFunction_Power_Diff(uint n,
                                                                  _FLOAT* bot_diff,
                                                                  UNUSED const _FLOAT* top_diff,
                                                                  const _FLOAT* top_data,
                                                                  const _FLOAT* bot_data,
                                                                  _FLOAT diff_scale,
                                                                  UNUSED _FLOAT power,
                                                                  _FLOAT scale,
                                                                  _FLOAT shift)
{

    for(uint i = 0; i < n; i++)
    {
        _FLOAT arg = shift + bot_data[i] * scale;
//		bot_diff[i] = (arg == 0) ? 0 : diff_scale * top_data[i] / arg;
#if MIOPEN_USE_FP16 == 1
        bot_diff[i] = (fabs(arg) < (_FLOAT)0.0001) ? (_FLOAT)0 : diff_scale * top_data[i] / arg;
#endif
#if MIOPEN_USE_FP32 == 1
        bot_diff[i] = (fabs(arg) < (_FLOAT)0.000001) ? (_FLOAT)0 : diff_scale * top_data[i] / arg;
#endif
    }
}

__attribute__((always_inline)) void ActivationFunction_BNLL_Diff(uint n,
                                                                 _FLOAT* bot_diff,
                                                                 const _FLOAT* top_diff,
                                                                 const _FLOAT* bot_data)
{
    for(uint i = 0; i < n; i++)
    {
        //	(log(1 + exp(x)))' = 1/ (1 + exp(-x))
        //		_FLOAT kBNLL_THRESHOLD = (_FLOAT)50.;
        _FLOAT expval = exp(fmin(bot_data[i], kBNLL_THRESHOLD));
        bot_diff[i]   = top_diff[i] * expval / (expval + (_FLOAT)1.f);
    }
}

#ifdef LITE

/**********************************************************************************************
**********************************************************************************************/

// N - batch size
// C - # of maps
// H - map height
// W - map width
// TENS_LEN = (N*C*H*W);
// RD_BLCK = (TENS_LEN%4==0) ? 4 : (TENS_LEN%3==0)? 3 : (TENS_LEN%2==0)? 2 : 1;
// READ_TYPE = (RD_BLCK==4) ? "float4" : (RD_BLCK == 3) ? "float3" : (RD_BLC==2) ? "float2" :
// "float";
// local size = (256, 1, 1)
// global size = ((TENS_LEN/RD_BLCK), 1, 1)

__kernel void MIOpenActiveFwdLite(const __global _FLOAT* bot,
                                  __global _FLOAT* top,
                                  _FLOAT power,
                                  _FLOAT scale,
                                  _FLOAT shift,
                                  const long bot_offset,
                                  const long top_offset)
{
    uint gid0 = get_global_id(0);

    uint index = gid0 * MLO_READ_UNIT;

    _FLOAT data[MLO_READ_UNIT];
    _FLOAT response[MLO_READ_UNIT];

    *((MLO_READ_TYPE*)data) = *((const __global MLO_READ_TYPE*)(bot + bot_offset + index));

    ActivationFunction(MLO_READ_UNIT, response, (const _FLOAT*)data, power, scale, shift);

    *((__global MLO_READ_TYPE*)(top + top_offset + index)) = *((MLO_READ_TYPE*)response);
}

/**********************************************************************************************
**********************************************************************************************/

__kernel void MIOpenActiveFwd2DLite(const __global _FLOAT* bot,
                                    __global _FLOAT* top,
                                    _FLOAT power,
                                    _FLOAT scale,
                                    _FLOAT shift,
                                    const long bot_offset,
                                    const long top_offset,
                                    const uint bot_stride,
                                    const uint top_stride)
{
    uint x_id = get_global_id(0);
    uint y    = get_global_id(1);

    uint bot_index = y * bot_stride + x_id * MLO_READ_UNIT;
    uint top_index = y * top_stride + x_id * MLO_READ_UNIT;

    _FLOAT data[MLO_READ_UNIT];
    _FLOAT response[MLO_READ_UNIT];

    *((MLO_READ_TYPE*)data) = *((const __global MLO_READ_TYPE*)(bot + bot_offset + bot_index));

    ActivationFunction(MLO_READ_UNIT, response, (const _FLOAT*)data, power, scale, shift);

    *((__global MLO_READ_TYPE*)(top + top_offset + top_index)) = *((MLO_READ_TYPE*)response);
}

/**********************************************************************************************
**********************************************************************************************/

__kernel void MIOpenActiveBwdLite(__global _FLOAT* bot_diff,
                                  __global const _FLOAT* top_diff,
                                  __global const _FLOAT* bot,
                                  __global const _FLOAT* top,
                                  _FLOAT diff_scale,
                                  _FLOAT power,
                                  _FLOAT scale,
                                  _FLOAT shift,
                                  const long bot_diff_offset,
                                  const long top_diff_offset,
                                  const long bot_offset,
                                  const long top_offset)
{
    (void)diff_scale;
    (void)power;
    (void)scale;
    (void)shift;

    int gid0 = get_global_id(0);

    int index = gid0 * MLO_READ_UNIT;

    _FLOAT bot_diff_dat[MLO_READ_UNIT];
    _FLOAT top_diff_dat[MLO_READ_UNIT];
    _FLOAT bot_dat[MLO_READ_UNIT];
    _FLOAT top_dat[MLO_READ_UNIT];

    *((MLO_READ_TYPE*)top_diff_dat) =
        *((const __global MLO_READ_TYPE*)(top_diff + top_diff_offset + index));
    *((MLO_READ_TYPE*)bot_dat) = *((const __global MLO_READ_TYPE*)(bot + bot_offset + index));
    *((MLO_READ_TYPE*)top_dat) = *((const __global MLO_READ_TYPE*)(top + top_offset + index));

#if MLO_NRN_OP_ID == MLO_NEURON_RELU
    {
        ActivationFunction_ReLU_Diff(MLO_READ_UNIT,
                                     bot_diff_dat,
                                     (const _FLOAT*)top_diff_dat,
                                     (const _FLOAT*)bot_dat,
                                     scale);
    }
#elif MLO_NRN_OP_ID == MLO_NEURON_LOGISTIC
    // 1/(1 + exp(-x))
    ActivationFunction_Sigmoid_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)top_dat);
#elif MLO_NRN_OP_ID == MLO_NEURON_TANH
    // (exp(2x) -1) / (exp(2x) + 1)

    ActivationFunction_TanH_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)top_dat);

#elif MLO_NRN_OP_ID == MLO_NEURON_ABS

    ActivationFunction_Abs_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)bot_dat);
#elif MLO_NRN_OP_ID == MLO_NEURON_POWER
    // (shift + scale * x ) ^power

    ActivationFunction_Power_Diff(MLO_READ_UNIT,
                                  bot_diff_dat,
                                  (const _FLOAT*)top_diff_dat,
                                  (const _FLOAT*)top_dat,
                                  (const _FLOAT*)bot_dat,
                                  diff_scale,
                                  power,
                                  scale,
                                  shift);

#elif MLO_NRN_OP_ID == MLO_NEURON_SOFTRELU
    //	log(1 + exp(x))
    ActivationFunction_BNLL_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)bot_dat);
#endif

    *((__global MLO_READ_TYPE*)(bot_diff + bot_diff_offset + index)) =
        *((MLO_READ_TYPE*)bot_diff_dat);
}

/**********************************************************************************************
**********************************************************************************************/

__kernel void MIOpenActiveBwd2DLite(__global _FLOAT* bot_diff,
                                    __global const _FLOAT* top_diff,
                                    __global const _FLOAT* bot,
                                    __global const _FLOAT* top,
                                    _FLOAT diff_scale,
                                    _FLOAT power,
                                    _FLOAT scale,
                                    _FLOAT shift,
                                    const long bot_diff_offset,
                                    const long top_diff_offset,
                                    const long bot_offset,
                                    const long top_offset,
                                    const uint bot_diff_stride,
                                    const uint top_diff_stride,
                                    const uint bot_stride,
                                    const uint top_stride)
{
    uint x_id = get_global_id(0);
    uint y    = get_global_id(1);

    uint bot_diff_index = y * bot_diff_stride + x_id * MLO_READ_UNIT;
    uint top_diff_index = y * top_diff_stride + x_id * MLO_READ_UNIT;
    uint bot_index      = y * bot_stride + x_id * MLO_READ_UNIT;
    uint top_index      = y * top_stride + x_id * MLO_READ_UNIT;

    _FLOAT bot_diff_dat[MLO_READ_UNIT];
    _FLOAT top_diff_dat[MLO_READ_UNIT];
    _FLOAT bot_dat[MLO_READ_UNIT];
    _FLOAT top_dat[MLO_READ_UNIT];

    *((MLO_READ_TYPE*)top_diff_dat) =
        *((const __global MLO_READ_TYPE*)(top_diff + top_diff_offset + top_diff_index));
    *((MLO_READ_TYPE*)bot_dat) = *((const __global MLO_READ_TYPE*)(bot + bot_offset + bot_index));
    *((MLO_READ_TYPE*)top_dat) = *((const __global MLO_READ_TYPE*)(top + top_offset + top_index));

#if MLO_NRN_OP_ID == MLO_NEURON_RELU
    {
        ActivationFunction_ReLU_Diff(MLO_READ_UNIT,
                                     bot_diff_dat,
                                     (const _FLOAT*)top_diff_dat,
                                     (const _FLOAT*)bot_dat,
                                     scale);
    }
#elif MLO_NRN_OP_ID == MLO_NEURON_LOGISTIC
    // 1/(1 + exp(-x))
    ActivationFunction_Sigmoid_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)top_dat);
#elif MLO_NRN_OP_ID == MLO_NEURON_TANH
    // (exp(2x) -1) / (exp(2x) + 1)

    ActivationFunction_TanH_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)top_dat);

#elif MLO_NRN_OP_ID == MLO_NEURON_ABS

    ActivationFunction_Abs_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)bot_dat);
#elif MLO_NRN_OP_ID == MLO_NEURON_POWER
    // (shift + scale * x ) ^power

    ActivationFunction_Power_Diff(MLO_READ_UNIT,
                                  bot_diff_dat,
                                  (const _FLOAT*)top_diff_dat,
                                  (const _FLOAT*)top_dat,
                                  (const _FLOAT*)bot_dat,
                                  diff_scale,
                                  power,
                                  scale,
                                  shift);

#elif MLO_NRN_OP_ID == MLO_NEURON_SOFTRELU
    //	log(1 + exp(x))
    ActivationFunction_BNLL_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)bot_dat);
#endif

    *((__global MLO_READ_TYPE*)(bot_diff + bot_diff_offset + bot_diff_index)) =
        *((MLO_READ_TYPE*)bot_diff_dat);
}

/**************************************************************************************************************/

#else

/***************************************************************************************************************/
__attribute__((reqd_work_group_size(MLO_NRN_GROUP_SZ0, MLO_NRN_GROUP_SZ1, MLO_NRN_GROUP_SZ2)))
__kernel void
MIOpenNeuronFwd(const __global _FLOAT* bot,
                __global _FLOAT* top,
                _FLOAT power,
                _FLOAT scale,
                _FLOAT shift,
                const long xOffset,
                const long yOffset)
{
    int x            = get_global_id(0); // channel x

#if MLO_N_OUT_STRIDE > MLO_OUT_BLOCK_SZ
    int n_out_stride = MLO_N_OUT_STRIDE;
    int c_out        = MLO_C_OUT;
    int h_out        = MLO_H_OUT;
    int w_out        = MLO_W_OUT;
#endif
#if MLO_N_IN_STRIDE > MLO_IN_BLOCK_SZ
    int n_in_stride  = MLO_N_IN_STRIDE;
    int c_in         = MLO_C_IN;
    int h_in         = MLO_H_IN;
    int w_in         = MLO_W_IN;
#endif

    _FLOAT data[MLO_READ_UNIT];
    _FLOAT response[MLO_READ_UNIT];
#if MLO_N_PIXS_OFF > 0
    if(x == MLO_MAP_SZ_ALIGNED - 1)
    {
        int i = 0;
        for(; i < MLO_N_PIXS_OFF; ++i)
        {
#if MLO_N_IN_STRIDE > MLO_IN_BLOCK_SZ
            if(n_in_stride > c_in * h_in * w_in && c_in != 0 && h_in != 0 && w_in != 0)
            {
                int loc, n_loc, c_loc, h_loc, w_loc;
                loc   = x * MLO_READ_UNIT + i;
                n_loc = loc / (MLO_C_IN * MLO_H_IN * MLO_W_IN);
                c_loc = (loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) / (MLO_H_IN * MLO_W_IN);
                h_loc =
                    ((loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) % (MLO_H_IN * MLO_W_IN)) / MLO_W_IN;
                w_loc =
                    ((loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) % (MLO_H_IN * MLO_W_IN)) % MLO_W_IN;

                data[i] = bot[xOffset + n_loc * MLO_N_IN_STRIDE + c_loc * MLO_C_IN_STRIDE +
                              h_loc * MLO_H_IN_STRIDE + w_loc * MLO_W_IN_STRIDE];
            }
            else
#endif
            {
                data[i] = bot[xOffset + x * MLO_READ_UNIT + i];
            }
        }
        for(; i < MLO_READ_UNIT; ++i)
        {
            data[i] = (_FLOAT)1.f;
        }
    }
    else
#endif
    {
        for(int i = 0; i < MLO_READ_UNIT; ++i)
        {
#if MLO_N_IN_STRIDE > MLO_IN_BLOCK_SZ
            if(n_in_stride > c_in * h_in * w_in && c_in != 0 && h_in != 0 && w_in != 0)
            {
                int loc, n_loc, c_loc, h_loc, w_loc;
                loc   = x * MLO_READ_UNIT + i;
                n_loc = loc / (MLO_C_IN * MLO_H_IN * MLO_W_IN);
                c_loc = (loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) / (MLO_H_IN * MLO_W_IN);
                h_loc =
                    ((loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) % (MLO_H_IN * MLO_W_IN)) / MLO_W_IN;
                w_loc =
                    ((loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) % (MLO_H_IN * MLO_W_IN)) % MLO_W_IN;

                data[i] = bot[xOffset + n_loc * MLO_N_IN_STRIDE + c_loc * MLO_C_IN_STRIDE +
                              h_loc * MLO_H_IN_STRIDE + w_loc * MLO_W_IN_STRIDE];
            }
            else
#endif
            {
                data[i] = bot[xOffset + x * MLO_READ_UNIT + i];
            }
        }
    }
    ActivationFunction(MLO_READ_UNIT, response, (const _FLOAT*)data, power, scale, shift);

#if MLO_N_PIXS_OFF > 0
    if(x == MLO_MAP_SZ_ALIGNED - 1)
    {
        int i = 0;
        for(; i < MLO_N_PIXS_OFF; ++i)
        {
#if MLO_N_OUT_STRIDE > MLO_OUT_BLOCK_SZ
            if(n_out_stride > c_out * h_out * w_out && c_out != 0 && h_out != 0 && w_out != 0)
            {
                int loc, n_loc, c_loc, h_loc, w_loc;
                loc   = x * MLO_READ_UNIT + i;
                n_loc = loc / (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT);
                c_loc = (loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) / (MLO_H_OUT * MLO_W_OUT);
                h_loc = ((loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) % (MLO_H_OUT * MLO_W_OUT)) /
                        MLO_W_OUT;
                w_loc = ((loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) % (MLO_H_OUT * MLO_W_OUT)) %
                        MLO_W_OUT;

                top[yOffset + n_loc * MLO_N_OUT_STRIDE + c_loc * MLO_C_OUT_STRIDE +
                    h_loc * MLO_H_OUT_STRIDE + w_loc * MLO_W_OUT_STRIDE] = response[i];
            }
            else
#endif
            {
                top[yOffset + x * MLO_READ_UNIT + i] = response[i];
            }
        }
    }
    else
#endif
    {
        for(int i = 0; i < MLO_READ_UNIT; ++i)
        {
#if MLO_N_OUT_STRIDE > MLO_OUT_BLOCK_SZ
            if(n_out_stride > c_out * h_out * w_out && c_out != 0 && h_out != 0 && w_out != 0)
            {
                int loc, n_loc, c_loc, h_loc, w_loc;
                loc   = x * MLO_READ_UNIT + i;
                n_loc = loc / (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT);
                c_loc = (loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) / (MLO_H_OUT * MLO_W_OUT);
                h_loc = ((loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) % (MLO_H_OUT * MLO_W_OUT)) /
                        MLO_W_OUT;
                w_loc = ((loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) % (MLO_H_OUT * MLO_W_OUT)) %
                        MLO_W_OUT;

                top[yOffset + n_loc * MLO_N_OUT_STRIDE + c_loc * MLO_C_OUT_STRIDE +
                    h_loc * MLO_H_OUT_STRIDE + w_loc * MLO_W_OUT_STRIDE] = response[i];
            }
            else
#endif
            {
                top[yOffset + x * MLO_READ_UNIT + i] = response[i];
            }
        }
    }
}

__attribute__((reqd_work_group_size(MLO_NRN_GROUP_SZ0, MLO_NRN_GROUP_SZ1, MLO_NRN_GROUP_SZ2)))
__kernel void
MIOpenNeuronBwd(__global _FLOAT* bot_diff,
                __global const _FLOAT* top_diff,
                __global const _FLOAT* bot_data,
                __global const _FLOAT* top_data,
                _FLOAT diff_scale,
                _FLOAT power,
                _FLOAT scale,
                _FLOAT shift,
                const long dxOffset,
                const long dyOffset,
                const long xOffset,
                const long yOffset)
{
    (void)diff_scale;
    (void)power;
    (void)scale;
    (void)shift;
    int x             = get_global_id(0); // channel x

#if MLO_N_OUT_STRIDE > MLO_OUT_BLOCK_SZ || MLO_N_DOUT_STRIDE > MLO_DOUT_BLOCK_SZ || \
    MLO_N_IN_STRIDE > MLO_IN_BLOCK_SZ
    int n_out_stride  = MLO_N_OUT_STRIDE;
    int c_out         = MLO_C_OUT;
    int h_out         = MLO_H_OUT;
    int w_out         = MLO_W_OUT;
    int n_dout_stride = MLO_N_DOUT_STRIDE;
    int c_dout        = MLO_C_DOUT;
    int h_dout        = MLO_H_DOUT;
    int w_dout        = MLO_W_DOUT;
    int n_in_stride   = MLO_N_IN_STRIDE;
    int c_in          = MLO_C_IN;
    int h_in          = MLO_H_IN;
    int w_in          = MLO_W_IN;
#endif

#if MLO_N_DIN_STRIDE > MLO_DIN_BLOCK_SZ
    int n_din_stride  = MLO_N_DIN_STRIDE;
    int c_din         = MLO_C_DIN;
    int h_din         = MLO_H_DIN;
    int w_din         = MLO_W_DIN;
#endif

    _FLOAT bot_diff_dat[MLO_READ_UNIT];
    _FLOAT top_diff_dat[MLO_READ_UNIT];
    _FLOAT bot_dat[MLO_READ_UNIT];
    _FLOAT top_dat[MLO_READ_UNIT];
#if MLO_N_PIXS_OFF > 0
    if(x == MLO_MAP_SZ_ALIGNED - 1)
    {
        int i = 0;
        for(; i < MLO_N_PIXS_OFF; ++i)
        {
#if MLO_N_OUT_STRIDE > MLO_OUT_BLOCK_SZ || MLO_N_DOUT_STRIDE > MLO_DOUT_BLOCK_SZ || \
    MLO_N_IN_STRIDE > MLO_IN_BLOCK_SZ
            if((n_out_stride > c_out * h_out * w_out || n_dout_stride > c_dout * h_dout * w_dout ||
                n_in_stride > c_in * h_in * w_in) &&
               c_out != 0 && h_out != 0 && w_out != 0 && c_dout != 0 && h_dout != 0 &&
               w_dout != 0 && c_in != 0 && h_in != 0 && w_in != 0)
            {
                int loc, n_loc_top_diff, c_loc_top_diff, h_loc_top_diff, w_loc_top_diff, n_loc_top,
                    c_loc_top, h_loc_top, w_loc_top, n_loc_bot, c_loc_bot, h_loc_bot, w_loc_bot;
                loc = x * MLO_READ_UNIT + i;

                n_loc_top_diff = loc / (MLO_C_DOUT * MLO_H_DOUT * MLO_W_DOUT);
                c_loc_top_diff =
                    (loc % (MLO_C_DOUT * MLO_H_DOUT * MLO_W_DOUT)) / (MLO_H_DOUT * MLO_W_DOUT);
                h_loc_top_diff =
                    ((loc % (MLO_C_DOUT * MLO_H_DOUT * MLO_W_DOUT)) % (MLO_H_DOUT * MLO_W_DOUT)) /
                    MLO_W_DOUT;
                w_loc_top_diff =
                    ((loc % (MLO_C_DOUT * MLO_H_DOUT * MLO_W_DOUT)) % (MLO_H_DOUT * MLO_W_DOUT)) %
                    MLO_W_DOUT;

                n_loc_top = loc / (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT);
                c_loc_top = (loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) / (MLO_H_OUT * MLO_W_OUT);
                h_loc_top =
                    ((loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) % (MLO_H_OUT * MLO_W_OUT)) /
                    MLO_W_OUT;
                w_loc_top =
                    ((loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) % (MLO_H_OUT * MLO_W_OUT)) %
                    MLO_W_OUT;

                n_loc_bot = loc / (MLO_C_IN * MLO_H_IN * MLO_W_IN);
                c_loc_bot = (loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) / (MLO_H_IN * MLO_W_IN);
                h_loc_bot =
                    ((loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) % (MLO_H_IN * MLO_W_IN)) / MLO_W_IN;
                w_loc_bot =
                    ((loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) % (MLO_H_IN * MLO_W_IN)) % MLO_W_IN;

                top_diff_dat[i] = top_diff[dyOffset + n_loc_top_diff * MLO_N_DOUT_STRIDE +
                                           c_loc_top_diff * MLO_C_DOUT_STRIDE +
                                           h_loc_top_diff * MLO_H_DOUT_STRIDE +
                                           w_loc_top_diff * MLO_W_DOUT_STRIDE];
                bot_dat[i] =
                    bot_data[xOffset + n_loc_bot * MLO_N_IN_STRIDE + c_loc_bot * MLO_C_IN_STRIDE +
                             h_loc_bot * MLO_H_IN_STRIDE + w_loc_bot * MLO_W_IN_STRIDE];
                top_dat[i] =
                    top_data[yOffset + n_loc_top * MLO_N_OUT_STRIDE + c_loc_top * MLO_C_OUT_STRIDE +
                             h_loc_top * MLO_H_OUT_STRIDE + w_loc_top * MLO_W_OUT_STRIDE];
            }
            else
#endif
            {
                top_diff_dat[i] = top_diff[dyOffset + x * MLO_READ_UNIT + i];
                bot_dat[i]      = bot_data[xOffset + x * MLO_READ_UNIT + i];
                top_dat[i]      = top_data[yOffset + x * MLO_READ_UNIT + i];
            }
        }
        for(; i < MLO_READ_UNIT; ++i)
        {
            top_diff_dat[i] = (_FLOAT)1.f;
            bot_dat[i]      = (_FLOAT)1.f;
            top_dat[i]      = (_FLOAT)1.f;
        }
    }
    else
#endif
    {
        for(int i = 0; i < MLO_READ_UNIT; ++i)
        {
#if MLO_N_OUT_STRIDE > MLO_OUT_BLOCK_SZ || MLO_N_DOUT_STRIDE > MLO_DOUT_BLOCK_SZ || \
    MLO_N_IN_STRIDE > MLO_IN_BLOCK_SZ
            if((n_out_stride > c_out * h_out * w_out || n_dout_stride > c_dout * h_dout * w_dout ||
                n_in_stride > c_in * h_in * w_in) &&
               c_out != 0 && h_out != 0 && w_out != 0 && c_dout != 0 && h_dout != 0 &&
               w_dout != 0 && c_in != 0 && h_in != 0 && w_in != 0)
            {
                int loc, n_loc_top_diff, c_loc_top_diff, h_loc_top_diff, w_loc_top_diff, n_loc_top,
                    c_loc_top, h_loc_top, w_loc_top, n_loc_bot, c_loc_bot, h_loc_bot, w_loc_bot;
                loc = x * MLO_READ_UNIT + i;

                n_loc_top_diff = loc / (MLO_C_DOUT * MLO_H_DOUT * MLO_W_DOUT);
                c_loc_top_diff =
                    (loc % (MLO_C_DOUT * MLO_H_DOUT * MLO_W_DOUT)) / (MLO_H_DOUT * MLO_W_DOUT);
                h_loc_top_diff =
                    ((loc % (MLO_C_DOUT * MLO_H_DOUT * MLO_W_DOUT)) % (MLO_H_DOUT * MLO_W_DOUT)) /
                    MLO_W_DOUT;
                w_loc_top_diff =
                    ((loc % (MLO_C_DOUT * MLO_H_DOUT * MLO_W_DOUT)) % (MLO_H_DOUT * MLO_W_DOUT)) %
                    MLO_W_DOUT;

                n_loc_top = loc / (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT);
                c_loc_top = (loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) / (MLO_H_OUT * MLO_W_OUT);
                h_loc_top =
                    ((loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) % (MLO_H_OUT * MLO_W_OUT)) /
                    MLO_W_OUT;
                w_loc_top =
                    ((loc % (MLO_C_OUT * MLO_H_OUT * MLO_W_OUT)) % (MLO_H_OUT * MLO_W_OUT)) %
                    MLO_W_OUT;

                n_loc_bot = loc / (MLO_C_IN * MLO_H_IN * MLO_W_IN);
                c_loc_bot = (loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) / (MLO_H_IN * MLO_W_IN);
                h_loc_bot =
                    ((loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) % (MLO_H_IN * MLO_W_IN)) / MLO_W_IN;
                w_loc_bot =
                    ((loc % (MLO_C_IN * MLO_H_IN * MLO_W_IN)) % (MLO_H_IN * MLO_W_IN)) % MLO_W_IN;

                top_diff_dat[i] = top_diff[dyOffset + n_loc_top_diff * MLO_N_DOUT_STRIDE +
                                           c_loc_top_diff * MLO_C_DOUT_STRIDE +
                                           h_loc_top_diff * MLO_H_DOUT_STRIDE +
                                           w_loc_top_diff * MLO_W_DOUT_STRIDE];
                bot_dat[i] =
                    bot_data[xOffset + n_loc_bot * MLO_N_IN_STRIDE + c_loc_bot * MLO_C_IN_STRIDE +
                             h_loc_bot * MLO_H_IN_STRIDE + w_loc_bot * MLO_W_IN_STRIDE];
                top_dat[i] =
                    top_data[yOffset + n_loc_top * MLO_N_OUT_STRIDE + c_loc_top * MLO_C_OUT_STRIDE +
                             h_loc_top * MLO_H_OUT_STRIDE + w_loc_top * MLO_W_OUT_STRIDE];
            }
            else
#endif
            {
                top_diff_dat[i] = top_diff[dyOffset + x * MLO_READ_UNIT + i];
                bot_dat[i]      = bot_data[xOffset + x * MLO_READ_UNIT + i];
                top_dat[i]      = top_data[yOffset + x * MLO_READ_UNIT + i];
            }
        }
    }

#if MLO_NRN_OP_ID == MLO_NEURON_RELU
    {
        ActivationFunction_ReLU_Diff(MLO_READ_UNIT,
                                     bot_diff_dat,
                                     (const _FLOAT*)top_diff_dat,
                                     (const _FLOAT*)bot_dat,
                                     scale);
    }
#elif MLO_NRN_OP_ID == MLO_NEURON_LOGISTIC
    // 1/(1 + exp(-x))
    ActivationFunction_Sigmoid_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)top_dat);
#elif MLO_NRN_OP_ID == MLO_NEURON_TANH
    // (exp(2x) -1) / (exp(2x) + 1)

    ActivationFunction_TanH_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)top_dat);

#elif MLO_NRN_OP_ID == MLO_NEURON_ABS

    ActivationFunction_Abs_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)bot_dat);
#elif MLO_NRN_OP_ID == MLO_NEURON_POWER
    // (shift + scale * x ) ^power

    ActivationFunction_Power_Diff(MLO_READ_UNIT,
                                  bot_diff_dat,
                                  (const _FLOAT*)top_diff_dat,
                                  (const _FLOAT*)top_dat,
                                  (const _FLOAT*)bot_dat,
                                  diff_scale,
                                  power,
                                  scale,
                                  shift);

#elif MLO_NRN_OP_ID == MLO_NEURON_SOFTRELU
    //	log(1 + exp(x))
    ActivationFunction_BNLL_Diff(
        MLO_READ_UNIT, bot_diff_dat, (const _FLOAT*)top_diff_dat, (const _FLOAT*)bot_dat);
#endif

#if MLO_N_PIXS_OFF > 0
    if(x == MLO_MAP_SZ_ALIGNED - 1)
    {
        int i = 0;
        for(; i < MLO_N_PIXS_OFF; ++i)
        {
#if MLO_N_DIN_STRIDE > MLO_DIN_BLOCK_SZ
            if(n_din_stride > c_din * h_din * w_din && c_din != 0 && h_din != 0 && w_din != 0)
            {
                int loc, n_loc_bot_diff, c_loc_bot_diff, h_loc_bot_diff, w_loc_bot_diff;
                loc = x * MLO_READ_UNIT + i;

                n_loc_bot_diff = loc / (MLO_C_DIN * MLO_H_DIN * MLO_W_DIN);
                c_loc_bot_diff =
                    (loc % (MLO_C_DIN * MLO_H_DIN * MLO_W_DIN)) / (MLO_H_DIN * MLO_W_DIN);
                h_loc_bot_diff =
                    ((loc % (MLO_C_DIN * MLO_H_DIN * MLO_W_DIN)) % (MLO_H_DIN * MLO_W_DIN)) /
                    MLO_W_DIN;
                w_loc_bot_diff =
                    ((loc % (MLO_C_DIN * MLO_H_DIN * MLO_W_DIN)) % (MLO_H_DIN * MLO_W_DIN)) %
                    MLO_W_DIN;

                bot_diff[dxOffset + n_loc_bot_diff * MLO_N_DIN_STRIDE +
                         c_loc_bot_diff * MLO_C_DIN_STRIDE + h_loc_bot_diff * MLO_H_DIN_STRIDE +
                         w_loc_bot_diff * MLO_W_DIN_STRIDE] = bot_diff_dat[i];
            }
            else
#endif
            {
                bot_diff[dxOffset + x * MLO_READ_UNIT + i] = bot_diff_dat[i];
            }
        }
    }
    else
#endif
    {
        for(int i = 0; i < MLO_READ_UNIT; ++i)
        {
#if MLO_N_DIN_STRIDE > MLO_DIN_BLOCK_SZ
            if(n_din_stride > c_din * h_din * w_din && c_din != 0 && h_din != 0 && w_din != 0)
            {
                int loc, n_loc_bot_diff, c_loc_bot_diff, h_loc_bot_diff, w_loc_bot_diff;
                loc = x * MLO_READ_UNIT + i;

                n_loc_bot_diff = loc / (MLO_C_DIN * MLO_H_DIN * MLO_W_DIN);
                c_loc_bot_diff =
                    (loc % (MLO_C_DIN * MLO_H_DIN * MLO_W_DIN)) / (MLO_H_DIN * MLO_W_DIN);
                h_loc_bot_diff =
                    ((loc % (MLO_C_DIN * MLO_H_DIN * MLO_W_DIN)) % (MLO_H_DIN * MLO_W_DIN)) /
                    MLO_W_DIN;
                w_loc_bot_diff =
                    ((loc % (MLO_C_DIN * MLO_H_DIN * MLO_W_DIN)) % (MLO_H_DIN * MLO_W_DIN)) %
                    MLO_W_DIN;

                bot_diff[dxOffset + n_loc_bot_diff * MLO_N_DIN_STRIDE +
                         c_loc_bot_diff * MLO_C_DIN_STRIDE + h_loc_bot_diff * MLO_H_DIN_STRIDE +
                         w_loc_bot_diff * MLO_W_DIN_STRIDE] = bot_diff_dat[i];
            }
            else
#endif
            {
                bot_diff[dxOffset + x * MLO_READ_UNIT + i] = bot_diff_dat[i];
            }
        }
    }
}

#endif // #ifdef LITE


store samples in local state array

store samples in local state array

/*  Copyright 2014 Aaron Boxer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "ocl_platform.cl"

/*

BPC Scan Pattern

Xx4 stripes

The stripes are scanned from top to bottom, and the columns within a
stripe are scanned from left to right.

*/

#define BOUNDARY 1
#define STATE_BUFFER_SIZE 1088
#define STATE_BUFFER_STRIDE 34
#define SAMPLE_SHIFT 10

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE  | CLK_FILTER_NEAREST;

void KERNEL run(write_only image2d_t R,
						 write_only image2d_t G,
						 write_only image2d_t B,
						 write_only image2d_t A , const unsigned int  width, const unsigned int height) {

	// Red channel

    //find maximum number of bits in code block
	LOCAL int msbScratch[CODEBLOCKX];
	LOCAL int state[STATE_BUFFER_SIZE];

    // between one and 32 - zero value indicates that this code block is identically zero

	int2 posIn = (int2)(getLocalId(0) + getGlobalId(0)*CODEBLOCKX,  getGlobalId(1)*CODEBLOCKY);
	int maxVal = -2147483647-1;
	int index = BOUNDARY + getLocalId(0);
	for (int i = 0; i < CODEBLOCKY; ++i) {
	    int sample = read_imagei(R, sampler, posIn).x;
		state[index] = sample << SAMPLE_SHIFT;
		maxVal = max(maxVal, sample);
		index += STATE_BUFFER_STRIDE;
		posIn.y++;
	}
	//initialize boundary columns
	if (getLocalId(0) == 0 || getLocalId(0) == CODEBLOCKX-1) {
	    int delta = -1 + (getLocalId(0)/(CODEBLOCKX-1))*2; // -1 or +1
		int index = BOUNDARY + getLocalId(0) + delta;
		 for (int i = 0; i < CODEBLOCKY; ++i) {
		     state[index] = 0;
			 index += STATE_BUFFER_STRIDE;
		 }
	}

	int msbWI = 32 - clz(maxVal);
	msbScratch[getLocalId(0)] =msbWI;
	localMemoryFence();

	if (getLocalId(0) == 0) {
	    int4 mx = (int4)(msbWI);
		/*
		for(int i=0; i < CODEBLOCKX; i+=4) {
		    int4 temp = mx;
			mx = max(temp,(int4)(msbScratch[i],msbScratch[i+1],msbScratch[i+2],msbScratch[i+3]));
		}
		msbWI = mx.x;
		msbWI = max(msbWI, mx.y);
		msbWI = max(msbWI, mx.z);
		msbWI = max(msbWI, mx.w);
		*/
		msbScratch[0] = 8;
	}

	localMemoryFence();
}

/*  Copyright 2014 Aaron Boxer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "ocl_platform.cl"

/*

BPC Scan Pattern

Xx4 stripes

The stripes are scanned from top to bottom, and the columns within a
stripe are scanned from left to right.

*/

#define BOUNDARY 1

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE  | CLK_FILTER_NEAREST;

void KERNEL run(write_only image2d_t R,
						 write_only image2d_t G,
						 write_only image2d_t B,
						 write_only image2d_t A , const unsigned int  width, const unsigned int height) {

	// Red channel

    //find maximum number of bits in code block
	LOCAL char msbScratch[CODEBLOCKX];

    // between one and 32 - zero value indicates that this code block is identically zero

	int2 posIn = (int2)(getLocalId(0) + getGlobalId(0)*CODEBLOCKX,  getGlobalId(1)*CODEBLOCKY);
	int maxVal = -2147483647-1;
	for (int i = 0; i < CODEBLOCKY; ++i) {
		maxVal = max(maxVal, read_imagei(R, sampler, posIn).x);
		posIn.y++;
	}

	char msbWI = 32 - clz(maxVal);
	msbScratch[getLocalId(0)] =msbWI;
	localMemoryFence();

	//group by twos
	if ( (getLocalId(0)&1) == 0) {
		msbWI = max(msbWI, msbScratch[getLocalId(0)+1]);
	}
	localMemoryFence();

	//group by fours
	if ( (getLocalId(0)&3) == 0) {
		msbWI = max(msbWI, msbScratch[getLocalId(0)+2]);
	}
	localMemoryFence();

	//group by eights
	if ( (getLocalId(0)&7) == 0) {
		msbWI = max(msbWI, msbScratch[getLocalId(0)+4]);
	}
	localMemoryFence();

	//group by 16ths
	if ( (getLocalId(0)&15) == 0) {
		msbWI = max(msbWI, msbScratch[getLocalId(0)+8]);
	}
	localMemoryFence();

	if (getLocalId(0) == 0) {
		msbScratch[0] = max(msbWI, msbScratch[16]);  //crashes here with access violation while reading location .....
	}
	localMemoryFence();

}


migrate prop4 to use function...

migrate prop4 to use function...

// Copyright Hugh Perkins 2014, 2015 hughperkins at gmail
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

// expected defines:
// one of: [ TANH | RELU | LINEAR ]
// BIASED (or not)

#ifdef TANH
    #define ACTIVATION_FUNCTION(output) (tanh(output))
#elif defined SCALEDTANH
    #define ACTIVATION_FUNCTION(output) ( 1.7159f * tanh( 0.66667f * output))
#elif SIGMOID
    #define ACTIVATION_FUNCTION(output) (1.0f / (1 + exp(-output)))
#elif defined RELU
    #define ACTIVATION_FUNCTION(output) (output> 0 ? output : 0)
#elif defined LINEAR
    #define ACTIVATION_FUNCTION(output) (output)
#endif

void copyLocal( local float *target, global float const *source, int N ) {
    int numLoops = ( N + get_local_size(0) - 1 ) / get_local_size(0);
    for( int loop = 0; loop < numLoops; loop++ ) {
        int offset = loop * get_local_size(0) + get_local_id(0);
        if( offset < N ) {
            target[offset] = source[offset];
        }
    }
}

#ifdef gOutputBoardSize // for previous tests that dont define it
#ifdef ACTIVATION_FUNCTION // protect against not defined
// workgroup id organized like: [n][filterid]
// local id organized like: [outrow][outcol]
// each thread iterates over: [upstreamplane][filterrow][filtercol]
// number workgroups = 32
// one filter plane takes up 5 * 5 * 4 = 100 bytes
// one filter cube (corresponding to one outplane) = 5*5 * 32 * 4 = 3.2KB (ok)
// all filter cubes = 3.2KB * 32 = 102KB (too big)
// results are organized like [n][filterid][outrow][outcol]
void kernel propagate_4_by_n_outplane_smallercache( const int batchSize,
      global const float *images, global const float *filters,
        #ifdef BIASED
            global const float*biases,
        #endif
    global float *results,
    local float *_upstreamBoard, local float *_filterCube ) {
    #define globalId ( get_global_id(0) )

    #define localId ( get_local_id(0) )
    #define workgroupId ( get_group_id(0) )
//    const int workgroupSize = get_local_size(0);
    const int effectiveWorkgroupId = workgroupId / gPixelsPerThread;
    const int pixel = workgroupId % gPixelsPerThread;
    const int effectiveLocalId = localId + pixel * gWorkgroupSize;
    const int n = effectiveWorkgroupId / gNumFilters;
    const int outPlane = effectiveWorkgroupId % gNumFilters;

    const int outputRow = effectiveLocalId / gOutputBoardSize;
    const int outputCol = effectiveLocalId % gOutputBoardSize;

    float sum = 0;
    for( int upstreamPlane = 0; upstreamPlane < gInputPlanes; upstreamPlane++ ) {
        barrier(CLK_LOCAL_MEM_FENCE);
        copyLocal( _upstreamBoard, images + ( n * gInputPlanes + upstreamPlane ) * gInputBoardSizeSquared, gInputBoardSizeSquared );
        copyLocal( _filterCube, filters + ( outPlane * gInputPlanes + upstreamPlane ) * gFilterSizeSquared, gFilterSizeSquared );
        barrier(CLK_LOCAL_MEM_FENCE);

        if( effectiveLocalId < gOutputBoardSizeSquared ) {
            for( int u = -gHalfFilterSize; u <= gHalfFilterSize - gEven; u++ ) {
                // trying to reduce register pressure...
                #if gPadZeros == 1
                    #define inputRow ( outputRow + u )
                #else
                    #define inputRow ( outputRow + u + gHalfFilterSize )
                #endif
                int inputboardrowoffset = inputRow * gInputBoardSize;
                int filterrowoffset = (u+gHalfFilterSize) * gFilterSize + gHalfFilterSize;
                bool rowOk = inputRow >= 0 && inputRow < gInputBoardSize;
                for( int v = -gHalfFilterSize; v <= gHalfFilterSize - gEven; v++ ) {
                    #if gPadZeros == 1
                        #define inputCol ( outputCol + v )
                    #else
                        #define inputCol ( outputCol + v + gHalfFilterSize )
                    #endif
                    bool process = rowOk && inputCol >= 0 && inputCol < gInputBoardSize;
                    if( process ) {
                            sum += _upstreamBoard[ inputboardrowoffset + inputCol] * _filterCube[ filterrowoffset + v ];
                    }
                }
            }
        }
    }
    #ifdef BIASED
        sum += biases[outPlane];
    #endif
    // results are organized like [imageid][filterid][row][col]
    #define resultIndex ( ( n * gNumFilters + outPlane ) * gOutputBoardSizeSquared + effectiveLocalId )
    if( localId < gOutputBoardSizeSquared ) {
        results[resultIndex ] = ACTIVATION_FUNCTION(sum);
    }
    // results[resultIndex ] = 123;
    //if( globalId == 0 ) results[0] += 0.000001f + _perPixelSums[0];
}
#endif
#endif

// Copyright Hugh Perkins 2014, 2015 hughperkins at gmail
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

// expected defines:
// one of: [ TANH | RELU | LINEAR ]
// BIASED (or not)

#ifdef TANH
    #define ACTIVATION_FUNCTION(output) (tanh(output))
#elif defined SCALEDTANH
    #define ACTIVATION_FUNCTION(output) ( 1.7159f * tanh( 0.66667f * output))
#elif SIGMOID
    #define ACTIVATION_FUNCTION(output) (1.0f / (1 + exp(-output)))
#elif defined RELU
    #define ACTIVATION_FUNCTION(output) (output> 0 ? output : 0)
#elif defined LINEAR
    #define ACTIVATION_FUNCTION(output) (output)
#endif

#ifdef gOutputBoardSize // for previous tests that dont define it
#ifdef ACTIVATION_FUNCTION // protect against not defined
// workgroup id organized like: [n][filterid]
// local id organized like: [outrow][outcol]
// each thread iterates over: [upstreamplane][filterrow][filtercol]
// number workgroups = 32
// one filter plane takes up 5 * 5 * 4 = 100 bytes
// one filter cube (corresponding to one outplane) = 5*5 * 32 * 4 = 3.2KB (ok)
// all filter cubes = 3.2KB * 32 = 102KB (too big)
// results are organized like [n][filterid][outrow][outcol]
void kernel propagate_4_by_n_outplane_smallercache( const int batchSize,
      global const float *images, global const float *filters,
        #ifdef BIASED
            global const float*biases,
        #endif
    global float *results,
    local float *_upstreamBoard, local float *_filterCube ) {
    #define globalId ( get_global_id(0) )

    #define localId ( get_local_id(0) )
    #define workgroupId ( get_group_id(0) )
//    const int workgroupSize = get_local_size(0);
    const int effectiveWorkgroupId = workgroupId / gPixelsPerThread;
    const int pixel = workgroupId % gPixelsPerThread;
    const int effectiveLocalId = localId + pixel * gWorkgroupSize;
    const int n = effectiveWorkgroupId / gNumFilters;
    const int outPlane = effectiveWorkgroupId % gNumFilters;

    const int outputRow = effectiveLocalId / gOutputBoardSize;
    const int outputCol = effectiveLocalId % gOutputBoardSize;

    float sum = 0;
    for( int upstreamPlane = 0; upstreamPlane < gInputPlanes; upstreamPlane++ ) {
        { // these parentheses are an attempt to reduce register pressure...
            // note to self: probably should make workgroupSize a #define...
            const int numUpstreamsPerThread = ( gInputBoardSizeSquared + gWorkgroupSize - 1 ) / gWorkgroupSize;
            int thisUpstreamBoardOffset = ( n * gInputPlanes + upstreamPlane ) * gInputBoardSizeSquared;
            barrier(CLK_LOCAL_MEM_FENCE);
            for( int i = 0; i < numUpstreamsPerThread; i++ ) {
                int thisOffset = gWorkgroupSize * i + localId;
                if( thisOffset < gInputBoardSizeSquared ) {
                    _upstreamBoard[ thisOffset ] = images[ thisUpstreamBoardOffset + thisOffset ];
                }
            }
        }
        {
            const int numFilterPixelsPerThread = ( gFilterSizeSquared + gWorkgroupSize - 1 ) / gWorkgroupSize;
            const int filterGlobalOffset = ( outPlane * gInputPlanes + upstreamPlane ) * gFilterSizeSquared;
            for( int i = 0; i < numFilterPixelsPerThread; i++ ) {
                int thisOffset = gWorkgroupSize * i + localId;
                if( thisOffset < gFilterSizeSquared ) {
                    _filterCube[thisOffset] = filters[filterGlobalOffset + thisOffset];
                }
            }
        }
        barrier(CLK_LOCAL_MEM_FENCE);

        if( effectiveLocalId < gOutputBoardSizeSquared ) {
            for( int u = -gHalfFilterSize; u <= gHalfFilterSize - gEven; u++ ) {
                // trying to reduce register pressure...
                #if gPadZeros == 1
                    #define inputRow ( outputRow + u )
                #else
                    #define inputRow ( outputRow + u + gHalfFilterSize )
                #endif
                int inputboardrowoffset = inputRow * gInputBoardSize;
                int filterrowoffset = (u+gHalfFilterSize) * gFilterSize + gHalfFilterSize;
                bool rowOk = inputRow >= 0 && inputRow < gInputBoardSize;
                for( int v = -gHalfFilterSize; v <= gHalfFilterSize - gEven; v++ ) {
                    #if gPadZeros == 1
                        #define inputCol ( outputCol + v )
                    #else
                        #define inputCol ( outputCol + v + gHalfFilterSize )
                    #endif
                    bool process = rowOk && inputCol >= 0 && inputCol < gInputBoardSize;
                    if( process ) {
                            sum += _upstreamBoard[ inputboardrowoffset + inputCol] * _filterCube[ filterrowoffset + v ];
                    }
                }
            }
        }
    }
    #ifdef BIASED
        sum += biases[outPlane];
    #endif
    // results are organized like [imageid][filterid][row][col]
    int resultIndex = ( n * gNumFilters + outPlane ) * gOutputBoardSizeSquared + effectiveLocalId;
    if( localId < gOutputBoardSizeSquared ) {
        results[resultIndex ] = ACTIVATION_FUNCTION(sum);
    }
    // results[resultIndex ] = 123;
    //if( globalId == 0 ) results[0] += 0.000001f + _perPixelSums[0];
}
#endif
#endif


Fixed small syntax error

Fixed small syntax error

typedef struct
{
	float2 position;
	float2 velocity;
} mosquito;

typedef mosquito dragonfly;

__kernel void
rule_1 (__global mosquito* _swarm, __global float2* _mass_centre,
    const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 mass_centre = (float2)(0.0f, 0.0f);

	for (unsigned int i = 0; i < _swarm_size; i++)
	{
		if (i == idx) continue;
		mass_centre += _swarm[i].position;
	}

	mass_centre /= (float)(_swarm_size - 1);
	_mass_centre[idx] = mass_centre;
}

__kernel void
rule_2 (__global mosquito* _swarm, __global float2 *_centre,
    const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 centre = (float2)(0.0f, 0.0f);

	for (unsigned int i = 0; i < _swarm_size; i++)
	{
		if (i == idx) continue;
		float2 difference = _swarm[i].position - _swarm[idx].position;
		if (fast_length(difference) < 20.0f)
			centre -= difference;
	}

	_centre[idx] = centre;
}

__kernel void
rule_3 (__global mosquito* _swarm, __global float2 *_velocity,
    const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 velocity = (float2)(0.0f, 0.0f);

	for (unsigned int i = 0; i < _swarm_size; i++)
	{
		if (i == idx) continue;
		velocity += _swarm[i].velocity;
	}
	velocity /= (float)(_swarm_size - 1);
	velocity = _swarm[idx].velocity - velocity;
	velocity /= 2.0f;

	_velocity[idx] = velocity;
}

__kernel void
rule_4 (__global mosquito* _swarm, __global float2 *_border_force,
    const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 top_velocity = (float2)(0.0f, 0.0f);
	float2 bottom_velocity = (float2)(0.0f, 0.0f);
	float2 left_velocity = (float2)(0.0f, 0.0f);
	float2 right_velocity = (float2)(0.0f, 0.0f);

	if (_swarm[idx].position.x == 0.0f
	 || _swarm[idx].position.y == 0.0f
	 || _swarm[idx].position.x == 600.0f
	 || _swarm[idx].position.y == 600.0f)
	{
		_border_force[idx] = (float2)(0.0f, 0.0f);
		return;
	}

	top_velocity.y = fabs(20.0f / _swarm[idx].position.y);
	bottom_velocity.y = -fabs(20.0f / (_swarm[idx].position.y - 600.0f));
	left_velocity.x = fabs(20.0f / _swarm[idx].position.x);
	right_velocity.x = -fabs(20.0f / (_swarm[idx].position.x - 600.0f));

	float2 result = top_velocity + bottom_velocity + left_velocity +
	    right_velocity;
	result /= 0.1f;

	_border_force[idx] = result;
}

__kernel void
rule_5 (__global mosquito* _swarm, __global float2 *_fear,
    __global dragonfly *_predator, const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 result = _swarm[idx].position - _predator->position;
	result /= 60.0f;

	_fear[idx] = result;
}

__kernel void
single_step (__global mosquito* _swarm, __global float2* _rule_1,
    __global float2* _rule_2, __global float2* _rule_3,
    __global float2* _rule_4, __global float2* _rule_5,
    __global mosquito* _new_swarm)
{
	unsigned int idx = get_global_id(0);
	float2 velocity = _rule_1[idx] + _rule_2[idx] + _rule_3[idx] + _rule_4[idx]
	    + _rule_5[idx];
	velocity /= 10000.0f;

	if (fast_length(velocity) > 0.2f)
		velocity = normalize(velocity) * 0.2f;

	_new_swarm[idx].velocity = _swarm[idx].velocity + velocity;
	_new_swarm[idx].position = _swarm[idx].position + _swarm[idx].velocity
	    + velocity;
}

typedef struct
{
	float2 position;
	float2 velocity;
} mosquito;

typedef mosquito dragonfly;

__kernel void
rule_1 (__global mosquito* _swarm, __global float2* _mass_centre,
    const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 mass_centre = (float2)(0.0f, 0.0f);

	for (unsigned int i = 0; i < _swarm_size; i++)
	{
		if (i == idx) continue;
		mass_centre += _swarm[i].position;
	}

	mass_centre /= (float)(_swarm_size - 1);
	_mass_centre[idx] = mass_centre;
}

__kernel void
rule_2 (__global mosquito* _swarm, __global float2 *_centre,
    const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 centre = (float2)(0.0f, 0.0f);

	for (unsigned int i = 0; i < _swarm_size; i++)
	{
		if (i == idx) continue;
		float2 difference = _swarm[i].position - _swarm[idx].position;
		if (fast_length(difference) < 20.0f)
			centre -= difference;
	}

	_centre[idx] = centre;
}

__kernel void
rule_3 (__global mosquito* _swarm, __global float2 *_velocity,
    const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 velocity = (float2)(0.0f, 0.0f);

	for (unsigned int i = 0; i < _swarm_size; i++)
	{
		if (i == idx) continue;
		velocity += _swarm[i].velocity;
	}
	velocity /= (float)(_swarm_size - 1);
	velocity = _swarm[idx].velocity - velocity;
	velocity /= 2.0f;

	_velocity[idx] = velocity;
}

__kernel void
rule_4 (__global mosquito* _swarm, __global float2 *_border_force,
    const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 top_velocity = (float2)(0.0f, 0.0f);
	float2 bottom_velocity = (float2)(0.0f, 0.0f);
	float2 left_velocity = (float2)(0.0f, 0.0f);
	float2 right_velocity = (float2)(0.0f, 0.0f);

	if (_swarm[idx].position.x == 0.0f
	 || _swarm[idx].position.y == 0.0f
	 || _swarm[idx].position.x == 600.0f
	 || _swarm[idx].position.y == 600.0f)
	{
		_border_force[idx] = (float2)(0.0f, 0.0f);
		return;
	}

	top_velocity.y = fabs(20.0f / _swarm[idx].position.y);
	bottom_velocity.y = -fabs(20.0f / (_swarm[idx].position.y - 600.0f));
	left_velocity.x = fabs(20.0f / _swarm[idx].position.x);
	right_velocity.x = -fabs(20.0f / (_swarm[idx].position.x - 600.0f));

	float2 result = top_velocity + bottom_velocity + left_velocity +
	    right_velocity;
	result /= 0.1f;

	_border_force[idx] = result;
}

__kernel void
rule_5 (__global mosquito* _swarm, __global float2 *_fear,
    __global dragonfly *_predator, const unsigned int _swarm_size)
{
	unsigned int idx = get_global_id(0);
	float2 result = _swarm[idx].position - _predator->position;
	result /= 60.0f;

	_fear[idx] = result;
}

__kernel void
single_step (__global mosquito* _swarm, __global float2* _rule_1,
    __global float2* _rule_2, __global float2* _rule_3,
    __global float2* _rule_4, __global float2* _rule_5,
    __global mosquito* _new_swarm)
{
	unsigned int idx = get_global_id(0);
	float2 velocity = _rule_1[idx] + _rule_2[idx] + _rule_3[idx] + _rule_4[idx]
	    + _rule_5[idx];
	velocity /= 10000.0f;

	if (velocity.length() > 0.2f)
		velocity = normalize(velocity) * 0.2f;

	_new_swarm[idx].velocity = _swarm[idx].velocity + velocity;
	_new_swarm[idx].position = _swarm[idx].position + _swarm[idx].velocity
	    + velocity;
}


opencl: experiment

opencl: experiment

__kernel void add(__global const float *a,
                  __global const float *b,
                  __global float *result)
{
  int gid = get_global_id(0);
  result[gid] = a[gid] + b[gid];
}

__kernel void sub(__global const float *a,
                  __global const float *b,
                  __global float *result)
{
  int gid = get_global_id(0);
  result[gid] = a[gid] - b[gid];
}

__kernel void mul(__global const float *a,
                  __global const float *b,
                  __global float *result)
{
  int gid = get_global_id(0);
  result[gid] = a[gid] * b[gid];
}

__kernel void div(__global const float *a,
                  __global const float *b,
                  __global float *result)
{
  int gid = get_global_id(0);
  result[gid] = a[gid] / b[gid];
}


Refine unroll in MIOpenConvBwdWrW_GxL_1x1.cl

/*
 * Copyright (c) 2017 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

#define UNUSED __attribute__((__unused__))

#ifndef MLO_N_PIXS_OFF
#define MLO_N_PIXS_OFF 0
#endif

__attribute__((always_inline))
uint iDiv(uint v, uint d)
{
	uint r = (uint)((float)v * (1.0f / (float)d) + 0.00001f);
	return(r);
}

__attribute__((always_inline))
uint iMod(uint v, uint u, uint d)
{
	uint r = v - mul24((uint)u, (uint)d);
	return(r);
}

inline void ReduceKernel(__local _FLOAT * lcl_blob, __private _FLOAT *weights_accum, uint lcl_id, uint scan_lcl, uint sum_stride, uint unit_len, UNUSED bool debug)
{

	for (uint j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (uint i = 0; i < unit_len; ++i)
			{
				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];

			}

		}
	}
}

static inline void  Kahan_summation(__private _FLOAT * __restrict sum, __private _FLOAT * __restrict c, _FLOAT v)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

/*
	group cooperative read
	read by MLO_READ_UNIT
	handle out of range both horizontally and vertically (by fixed number of veryical reads)

	no guard against number of inputs
*/
__attribute__((always_inline))
void readData(uint n, int gbl_data_off, uint gbl_data_stride, uint map_stride, int map_base,  int map_limit, const __global _FLOAT * __restrict g_data, __private _FLOAT * __restrict p_data, bool last_pixel)
{
	for (uint j = 0; j < n; ++j)
	{
		int gbl_data_off0 = (j*map_stride + map_base < map_limit) ? gbl_data_off + j*map_stride*gbl_data_stride : 0;
		const __global _FLOAT * g_data_p = &g_data[gbl_data_off0];

#if MLO_N_PIXS_OFF > 0

		if (last_pixel)
		{
			for (uint i = 0; i < MLO_N_PIXS_OFF; ++i)
			{

				p_data[j*MLO_READ_UNIT + i] = g_data_p[i];
			}

			for (uint i = MLO_IN_N_PIXS_OFF; i < MLO_READ_UNIT; ++i)
			{
				p_data[j*MLO_READ_UNIT + i] = 0;
			}

		}
		else
#else
		(void)last_pixel;
#endif
		{
			for (uint i = 0; i < MLO_READ_UNIT; ++i)
			{
				p_data[j*MLO_READ_UNIT + i] = g_data_p[i];
			}
		}

	} // for (int j = 0; j < n; ++j)

}

__attribute__((always_inline))
void readDataFlex(uint n, uint gbl_data_off, uint gbl_data_stride, uint map_stride, uint map_base, uint map_limit, const __global _FLOAT * __restrict g_data, __local _FLOAT * __restrict l_data)
{
#ifdef __AMDGCN__
#pragma unroll 2
#endif
	for( uint l = get_local_id(0); l < n*map_stride* MLO_MAP_WK_SZ; l += MLO_GRP_SZ)
	{

		uint r = 0;
		uint k0 = l;
#if MLO_N_LCL_OUT_MAPS > 1
		r = iDiv(l, map_stride* MLO_MAP_WK_SZ);  // maps row
		k0 = iMod(l, r, map_stride* MLO_MAP_WK_SZ);
#endif

#if (MLO_MAP_WK_SZ) &  (MLO_MAP_WK_SZ - 1)

		uint k = iDiv(k0, MLO_MAP_WK_SZ);  // map column
		uint p4 = iMod(k0, k, MLO_MAP_WK_SZ); // pixel block
#else
		uint k = (k0 / MLO_MAP_WK_SZ);  // map column
		uint p4 = (k0 & (MLO_MAP_WK_SZ-1)); // pixel block

#endif

		uint gbl_data_off0 = (r*map_stride + k + map_base < map_limit) ? gbl_data_off + (r*map_stride + k)*gbl_data_stride + p4*MLO_READ_UNIT : 0;
                const __global _FLOAT * g_data_p = &g_data[gbl_data_off0];
		__private _FLOAT p_data[MLO_READ_UNIT];

#if MLO_N_PIXS_OFF > 0

		bool last_pixel = (p4 == MLO_MAP_WK_SZ -1);
		if (last_pixel)
		{
			for (uint i = 0; i < MLO_N_PIXS_OFF; ++i)
			{

				p_data[i] = g_data_p[i];
			}

			for (uint i = MLO_IN_N_PIXS_OFF; i < MLO_READ_UNIT; ++i)
			{
				p_data[i] = 0;
			}

		}
		else
#endif
		{
			for (uint i = 0; i < MLO_READ_UNIT; ++i)
			{
				p_data[i] = g_data_p[i];
			}
		}

		for(uint i = 0; i < MLO_READ_UNIT; ++i)
		{
			l_data[(k*MLO_MAP_WK_SZ + p4)*MLO_READ_UNIT + i] = p_data[i];
		}

	}

}

/*********************************************************************************************************
// wrw algorithm for large filters
// idea:
// read MLO_OUT_STACKS output maps into LDS
// read MLO_IN_STACKS per group, MLO_N_LCL_IN_MAPS per wk_item input maps

// alg

// loop over MLO_OUT_STACKS of output

// convolve with MLO_N_LCL_IN_MAPS per wk-item

// reduce with transform

// write out

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MIOpenCvBwdWrW(
	const __global _FLOAT * __restrict top_df,
	const __global _FLOAT * __restrict bot,
	__global _FLOAT * __restrict weights_df,
	UNUSED _FLOAT padding_val
)
{
	// reduction memory.

	__local _FLOAT lcl_mem[MLO_LCL_MEM_SZ];
#if MLO_MAP_WK_SZ > 8
	__local _FLOAT * red_mem = lcl_mem;
#endif
	__local _FLOAT * proc_mem = lcl_mem;

	uint lcl_id = get_local_id(0);

	uint k_idx = get_group_id(0) * (MLO_OUT_STACKS * MLO_N_LCL_OUT_MAPS); // output map index base

	uint c_idx = get_group_id(1) * (MLO_IN_STACKS * MLO_N_LCL_IN_MAPS); // input map index based

	uint ib = get_group_id(2); // batch id

	uint gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE + ib * MLO_IN_BATCH_STRIDE;
	uint gbl_out_off = k_idx * MLO_OUT_CHANNEL_STRIDE + ib * MLO_OUT_BATCH_STRIDE;

#if MLO_MAP_WK_SZ & (MLO_MAP_WK_SZ - 1)
	// map id inside group
	uint m_idx = iDiv(lcl_id, MLO_MAP_WK_SZ);
	// read pixel inside the map
	uint p4 = iMod(lcl_id, m_idx, MLO_MAP_WK_SZ);
#else
	uint m_idx = lcl_id / MLO_MAP_WK_SZ;
	uint p4 = lcl_id & (MLO_MAP_WK_SZ - 1);
#endif

	bool last_pixel = (p4 == MLO_MAP_WK_SZ - 1);

	gbl_in_off += m_idx * MLO_IN_CHANNEL_STRIDE + p4*MLO_READ_UNIT;

#define MLO_TOP_DAT_SZ (MLO_N_LCL_OUT_MAPS * MLO_READ_UNIT)

	__private _FLOAT top_dat[MLO_TOP_DAT_SZ];

	for (uint i = 0; i < MLO_TOP_DAT_SZ; ++i)
	{
		top_dat[i] = 0;
	}

#define MLO_BOT_DAT_SZ (MLO_N_LCL_IN_MAPS * MLO_READ_UNIT)

	__private _FLOAT bot_dat[MLO_BOT_DAT_SZ];

	for (uint i = 0; i < MLO_BOT_DAT_SZ; ++i)
	{
		bot_dat[i] = 0;
	}

#define MLO_ACCUM_SZ (MLO_N_LCL_OUT_MAPS* MLO_N_LCL_IN_MAPS * MLO_OUT_STACKS)

	__private _FLOAT pvt_accum[MLO_ACCUM_SZ];

	for (uint i = 0; i < MLO_ACCUM_SZ; ++i)
	{
		pvt_accum[i] = 0;
	}

	for (uint i = lcl_id; i < MLO_LCL_MEM_SZ; i += MLO_GRP_SZ)
	{
		lcl_mem[i] = 0;
	}
	// over all batches

	for (uint b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS*MLO_OUT_BATCH_STRIDE
		)
	{

		uint gbl_in_scan_off = gbl_in_off;
		uint gbl_out_scan_off = gbl_out_off;

		// read input maps
		readData(MLO_N_LCL_IN_MAPS, gbl_in_scan_off, MLO_IN_CHANNEL_STRIDE, MLO_IN_STACKS, (m_idx + c_idx), MLO_N_INPUTS, bot, bot_dat, last_pixel);

		// read output maps into LDS
		readDataFlex(MLO_N_LCL_OUT_MAPS, gbl_out_scan_off, MLO_OUT_CHANNEL_STRIDE, MLO_OUT_STACKS, (k_idx), MLO_N_OUTPUTS, top_df, proc_mem);

		for (uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

			/*
			core processing loop
			bot - input
			top - output diff

			do convolution with all available input maps

			*/

			for (uint n = 0; n < MLO_OUT_STACKS; ++n)
			{
				__private _FLOAT pvt_top[MLO_READ_UNIT];
				for (uint i = 0; i < MLO_READ_UNIT; ++i)
				{
					pvt_top[i] = proc_mem[(n*MLO_MAP_WK_SZ + p4)*MLO_READ_UNIT + i]; //top_df[gbl_out_scan_off + (k*MLO_OUT_STACKS + n0)*MLO_IN_CHANNEL_STRIDE + i]; //proc_mem[(n*MLO_MAP_WK_SZ + p4)*MLO_READ_UNIT + i];
				}

				for (uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
				{
					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						pvt_accum[(k*MLO_OUT_STACKS + n)*MLO_N_LCL_IN_MAPS + c]
							+= bot_dat[c*MLO_READ_UNIT + i] * pvt_top[i];
#if 0

						if (k_idx + k*MLO_OUT_STACKS + n == 0 && c_idx + c * MLO_IN_STACKS + m_idx == 16 && m_idx < MLO_IN_STACKS && c_idx + m_idx + c*MLO_IN_STACKS < MLO_N_INPUTS && k_idx + k*MLO_OUT_STACKS + n < MLO_N_OUTPUTS)
						{
							printf("K:s: %d %d %d %d %d %d %d %d %f %f %f %f\n",
								MLO_OUT_STACKS,
								n,
								m_idx,
								gbl_out_scan_off,
								get_group_id(1),
								get_local_id(0),
								k,
								c,
								pvt_accum[(n* MLO_N_LCL_OUT_MAPS + k)*MLO_N_LCL_IN_MAPS + c],
								bot_dat[c*MLO_READ_UNIT + i] * pvt_top[i],
								bot_dat[c*MLO_READ_UNIT + i],
								pvt_top[i]
							);
						}
#endif

					}

				}
			}
		}

	}

	barrier(CLK_LOCAL_MEM_FENCE);

#if MLO_MAP_WK_SZ > 8
// transpose
	uint red_base_off = m_idx * MLO_MAP_WK_SZ*MLO_ACCUM_SZ;
	for (uint l = 0; l < MLO_ACCUM_SZ; ++l)
	{
		// write data
		red_mem[red_base_off + p4* MLO_ACCUM_SZ + l] = pvt_accum[l];

	}
	barrier(CLK_LOCAL_MEM_FENCE);

	// do final summation

	__private _FLOAT final_sum[1] = { 0 };

#if MLO_ACCUM_SZ & (MLO_ACCUM_SZ-1)
	uint new_m_idx = iDiv(lcl_id, MLO_ACCUM_SZ);
	uint new_p4 = iMod(lcl_id, new_m_idx, MLO_ACCUM_SZ);
#else
	uint new_m_idx = (lcl_id / MLO_ACCUM_SZ);
	uint new_p4 = (lcl_id & (MLO_ACCUM_SZ-1));

#endif

	uint new_red_base_off = new_m_idx * MLO_MAP_WK_SZ*MLO_ACCUM_SZ;

	for (uint s = 0; s < MLO_MAP_WK_SZ; ++s)
	{
		final_sum[0] += red_mem[new_red_base_off + s*MLO_ACCUM_SZ + new_p4];
	}

	// write out
	// inputs are outputs
	uint wei_df_off = ((ib * MLO_N_OUTPUTS + k_idx) * (uint)MLO_WEI_BATCH_STRIDE) + ((c_idx + new_m_idx) * MLO_WEI_CHANNEL_STRIDE);

#if MLO_N_LCL_IN_MAPS & (MLO_N_LCL_IN_MAPS-1)

	uint n = iDiv(new_p4, MLO_N_LCL_IN_MAPS);
	uint c = iMod(new_p4, n, MLO_N_LCL_IN_MAPS);
#else

	uint n = (new_p4 / MLO_N_LCL_IN_MAPS);
	uint c = (new_p4 & (MLO_N_LCL_IN_MAPS-1));
#endif

	uint k = 0;

	if (new_m_idx < MLO_IN_STACKS &&k_idx + k*MLO_OUT_STACKS + n < MLO_N_OUTPUTS && c_idx + new_m_idx + c*MLO_IN_STACKS < MLO_N_INPUTS)
	{
		weights_df[wei_df_off + (k*MLO_OUT_STACKS + n)*MLO_WEI_BATCH_STRIDE + c*MLO_IN_STACKS*MLO_WEI_CHANNEL_STRIDE] = final_sum[0];
	}
#else

#if 0 //MLO_IN_WIDTH > 24
// logar reduction
	for (uint i = lcl_id; i < MLO_LCL_MEM_SZ; i += MLO_GRP_SZ)
	{
		red_mem[i] = 0;
	}

	int red_base_off = (m_idx >= MLO_OUT_STACKS) ? MLO_LCL_MEM_SZ : m_idx * MLO_POW2_MAP_WK_SZ;
	// final summation over each filter row
	for (uint l = 0; l < MLO_ACCUM_SZ; ++l)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		// write data
		red_mem[red_base_off + p4] = pvt_accum[l];

		// barrier inside
		ReduceKernel(&red_mem[red_base_off], &pvt_accum[l], p4, p4, MLO_POW2_MAP_WK_SZ, 1, false);

	}

	barrier(CLK_LOCAL_MEM_FENCE);
#else
	// direct reduction
	for (uint l = 0; l < MLO_ACCUM_SZ; ++l)
	{
		lcl_mem[lcl_id] = pvt_accum[l];
		barrier(CLK_LOCAL_MEM_FENCE);
		if (p4 == 0)
		{
			for (uint i = 1; i < MLO_MAP_WK_SZ; ++i)
			{
				pvt_accum[l] += lcl_mem[lcl_id + i];
			}

		}
		barrier(CLK_LOCAL_MEM_FENCE);
	}

#endif

	// write out
	// inputs are outputs
	uint wei_df_off = ((ib * MLO_N_OUTPUTS + k_idx) * (int)MLO_WEI_BATCH_STRIDE) + ((c_idx + m_idx) * MLO_WEI_CHANNEL_STRIDE);

	if (p4 == 0)
	{
		for (uint n = 0; n < MLO_OUT_STACKS  ; ++n)
		{
			for (uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
			{
				for (uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
				{
					if (m_idx < MLO_IN_STACKS && k_idx + k*MLO_OUT_STACKS + n < MLO_N_OUTPUTS && c_idx + m_idx + c*MLO_IN_STACKS < MLO_N_INPUTS)
					{
						weights_df[wei_df_off + (k*MLO_OUT_STACKS + n)*MLO_WEI_BATCH_STRIDE + c*MLO_IN_STACKS*MLO_WEI_CHANNEL_STRIDE] = pvt_accum[(k*MLO_OUT_STACKS + n)*MLO_N_LCL_IN_MAPS + c];
					}

				}

			}
		}
	}

#endif
}

// final reduction kernel
// add filters over batches
__attribute__((reqd_work_group_size(MLO_UT_GRP_SZ0, 1, 1)))
__kernel void MIOpenCvBwdWrW_rdc(
	const __global _FLOAT * __restrict weight_df_tmp,
	__global _FLOAT * __restrict weights_df
)
{
	uint gbl_id = get_global_id(0);
	uint wei_idx0 = gbl_id * MLO_UT_READ_UNIT;

#if MLO_WEI_CHANNEL_STRIDE & (MLO_WEI_CHANNEL_STRIDE - 1)
	uint wei_blk_idx = iDiv(wei_idx0, MLO_WEI_CHANNEL_STRIDE);
	uint wei_idx = iMod(wei_idx0, wei_blk_idx, MLO_WEI_CHANNEL_STRIDE);
#else
	uint wei_blk_idx = wei_idx0 / MLO_WEI_CHANNEL_STRIDE;
	uint wei_idx = wei_idx0 & (MLO_WEI_CHANNEL_STRIDE - 1);
#endif

	_FLOAT pvt_accum_wei[MLO_UT_READ_UNIT];
	for (uint i = 0; i < MLO_UT_READ_UNIT; ++i)
	{
		pvt_accum_wei[i] = 0;
	}

	uint batch_loop = (MLO_BATCH_SZ + (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS) - 1) / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS);
	for (uint i = 0; i < batch_loop; ++i)
	{
		for (uint j = 0; j < MLO_UT_READ_UNIT; ++j)
		{
			pvt_accum_wei[j]
				+= weight_df_tmp[(wei_blk_idx * MLO_WEI_CHANNEL_STRIDE + i* MLO_N_OUTPUTS*MLO_WEI_BATCH_STRIDE) + wei_idx + j];
		}
	}

	for (uint j = 0; j < MLO_UT_READ_UNIT; ++j)
	{
		weights_df[wei_idx0 + j] = pvt_accum_wei[j];
	}

}

/*
 * Copyright (c) 2017 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

#define UNUSED __attribute__((__unused__))

#ifndef MLO_N_PIXS_OFF
#define MLO_N_PIXS_OFF 0
#endif

__attribute__((always_inline))
uint iDiv(uint v, uint d)
{
	uint r = (uint)((float)v * (1.0f / (float)d) + 0.00001f);
	return(r);
}

__attribute__((always_inline))
uint iMod(uint v, uint u, uint d)
{
	uint r = v - mul24((uint)u, (uint)d);
	return(r);
}

inline void ReduceKernel(__local _FLOAT * lcl_blob, __private _FLOAT *weights_accum, uint lcl_id, uint scan_lcl, uint sum_stride, uint unit_len, UNUSED bool debug)
{

	for (uint j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (uint i = 0; i < unit_len; ++i)
			{
				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];

			}

		}
	}
}

static inline void  Kahan_summation(__private _FLOAT * __restrict sum, __private _FLOAT * __restrict c, _FLOAT v)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

/*
	group cooperative read
	read by MLO_READ_UNIT
	handle out of range both horizontally and vertically (by fixed number of veryical reads)

	no guard against number of inputs
*/
__attribute__((always_inline))
void readData(uint n, int gbl_data_off, uint gbl_data_stride, uint map_stride, int map_base,  int map_limit, const __global _FLOAT * __restrict g_data, __private _FLOAT * __restrict p_data, bool last_pixel)
{
	for (uint j = 0; j < n; ++j)
	{
		int gbl_data_off0 = (j*map_stride + map_base < map_limit) ? gbl_data_off + j*map_stride*gbl_data_stride : 0;
		const __global _FLOAT * g_data_p = &g_data[gbl_data_off0];

#if MLO_N_PIXS_OFF > 0

		if (last_pixel)
		{
			for (uint i = 0; i < MLO_N_PIXS_OFF; ++i)
			{

				p_data[j*MLO_READ_UNIT + i] = g_data_p[i];
			}

			for (uint i = MLO_IN_N_PIXS_OFF; i < MLO_READ_UNIT; ++i)
			{
				p_data[j*MLO_READ_UNIT + i] = 0;
			}

		}
		else
#else
		(void)last_pixel;
#endif
		{
			for (uint i = 0; i < MLO_READ_UNIT; ++i)
			{
				p_data[j*MLO_READ_UNIT + i] = g_data_p[i];
			}
		}

	} // for (int j = 0; j < n; ++j)

}

__attribute__((always_inline))
void readDataFlex(uint n, uint gbl_data_off, uint gbl_data_stride, uint map_stride, uint map_base, uint map_limit, const __global _FLOAT * __restrict g_data, __local _FLOAT * __restrict l_data)
{

	for( uint l = get_local_id(0); l < n*map_stride* MLO_MAP_WK_SZ; l += MLO_GRP_SZ)
	{

		uint r = 0;
		uint k0 = l;
#if MLO_N_LCL_OUT_MAPS > 1
		r = iDiv(l, map_stride* MLO_MAP_WK_SZ);  // maps row
		k0 = iMod(l, r, map_stride* MLO_MAP_WK_SZ);
#endif

#if (MLO_MAP_WK_SZ) &  (MLO_MAP_WK_SZ - 1)

		uint k = iDiv(k0, MLO_MAP_WK_SZ);  // map column
		uint p4 = iMod(k0, k, MLO_MAP_WK_SZ); // pixel block
#else
		uint k = (k0 / MLO_MAP_WK_SZ);  // map column
		uint p4 = (k0 & (MLO_MAP_WK_SZ-1)); // pixel block

#endif

		uint gbl_data_off0 = (r*map_stride + k + map_base < map_limit) ? gbl_data_off + (r*map_stride + k)*gbl_data_stride + p4*MLO_READ_UNIT : 0;
                const __global _FLOAT * g_data_p = &g_data[gbl_data_off0];
		__private _FLOAT p_data[MLO_READ_UNIT];

#if MLO_N_PIXS_OFF > 0

		bool last_pixel = (p4 == MLO_MAP_WK_SZ -1);
		if (last_pixel)
		{
			for (uint i = 0; i < MLO_N_PIXS_OFF; ++i)
			{

				p_data[i] = g_data_p[i];
			}

			for (uint i = MLO_IN_N_PIXS_OFF; i < MLO_READ_UNIT; ++i)
			{
				p_data[i] = 0;
			}

		}
		else
#endif
		{
			for (uint i = 0; i < MLO_READ_UNIT; ++i)
			{
				p_data[i] = g_data_p[i];
			}
		}

		for(uint i = 0; i < MLO_READ_UNIT; ++i)
		{
			l_data[(k*MLO_MAP_WK_SZ + p4)*MLO_READ_UNIT + i] = p_data[i];
		}

	}

}

/*********************************************************************************************************
// wrw algorithm for large filters
// idea:
// read MLO_OUT_STACKS output maps into LDS
// read MLO_IN_STACKS per group, MLO_N_LCL_IN_MAPS per wk_item input maps

// alg

// loop over MLO_OUT_STACKS of output

// convolve with MLO_N_LCL_IN_MAPS per wk-item

// reduce with transform

// write out

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MIOpenCvBwdWrW(
	const __global _FLOAT * __restrict top_df,
	const __global _FLOAT * __restrict bot,
	__global _FLOAT * __restrict weights_df,
	UNUSED _FLOAT padding_val
)
{
	// reduction memory.

	__local _FLOAT lcl_mem[MLO_LCL_MEM_SZ];
#if MLO_MAP_WK_SZ > 8
	__local _FLOAT * red_mem = lcl_mem;
#endif
	__local _FLOAT * proc_mem = lcl_mem;

	uint lcl_id = get_local_id(0);

	uint k_idx = get_group_id(0) * (MLO_OUT_STACKS * MLO_N_LCL_OUT_MAPS); // output map index base

	uint c_idx = get_group_id(1) * (MLO_IN_STACKS * MLO_N_LCL_IN_MAPS); // input map index based

	uint ib = get_group_id(2); // batch id

	uint gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE + ib * MLO_IN_BATCH_STRIDE;
	uint gbl_out_off = k_idx * MLO_OUT_CHANNEL_STRIDE + ib * MLO_OUT_BATCH_STRIDE;

#if MLO_MAP_WK_SZ & (MLO_MAP_WK_SZ - 1)
	// map id inside group
	uint m_idx = iDiv(lcl_id, MLO_MAP_WK_SZ);
	// read pixel inside the map
	uint p4 = iMod(lcl_id, m_idx, MLO_MAP_WK_SZ);
#else
	uint m_idx = lcl_id / MLO_MAP_WK_SZ;
	uint p4 = lcl_id & (MLO_MAP_WK_SZ - 1);
#endif

	bool last_pixel = (p4 == MLO_MAP_WK_SZ - 1);

	gbl_in_off += m_idx * MLO_IN_CHANNEL_STRIDE + p4*MLO_READ_UNIT;

#define MLO_TOP_DAT_SZ (MLO_N_LCL_OUT_MAPS * MLO_READ_UNIT)

	__private _FLOAT top_dat[MLO_TOP_DAT_SZ];

	for (uint i = 0; i < MLO_TOP_DAT_SZ; ++i)
	{
		top_dat[i] = 0;
	}

#define MLO_BOT_DAT_SZ (MLO_N_LCL_IN_MAPS * MLO_READ_UNIT)

	__private _FLOAT bot_dat[MLO_BOT_DAT_SZ];

	for (uint i = 0; i < MLO_BOT_DAT_SZ; ++i)
	{
		bot_dat[i] = 0;
	}

#define MLO_ACCUM_SZ (MLO_N_LCL_OUT_MAPS* MLO_N_LCL_IN_MAPS * MLO_OUT_STACKS)

	__private _FLOAT pvt_accum[MLO_ACCUM_SZ];

	for (uint i = 0; i < MLO_ACCUM_SZ; ++i)
	{
		pvt_accum[i] = 0;
	}

	for (uint i = lcl_id; i < MLO_LCL_MEM_SZ; i += MLO_GRP_SZ)
	{
		lcl_mem[i] = 0;
	}
	// over all batches

	for (uint b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS*MLO_OUT_BATCH_STRIDE
		)
	{

		uint gbl_in_scan_off = gbl_in_off;
		uint gbl_out_scan_off = gbl_out_off;

		// read input maps
		readData(MLO_N_LCL_IN_MAPS, gbl_in_scan_off, MLO_IN_CHANNEL_STRIDE, MLO_IN_STACKS, (m_idx + c_idx), MLO_N_INPUTS, bot, bot_dat, last_pixel);

		// read output maps into LDS
		readDataFlex(MLO_N_LCL_OUT_MAPS, gbl_out_scan_off, MLO_OUT_CHANNEL_STRIDE, MLO_OUT_STACKS, (k_idx), MLO_N_OUTPUTS, top_df, proc_mem);

		for (uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

			/*
			core processing loop
			bot - input
			top - output diff

			do convolution with all available input maps

			*/

			for (uint n = 0; n < MLO_OUT_STACKS; ++n)
			{
				__private _FLOAT pvt_top[MLO_READ_UNIT];
				for (uint i = 0; i < MLO_READ_UNIT; ++i)
				{
					pvt_top[i] = proc_mem[(n*MLO_MAP_WK_SZ + p4)*MLO_READ_UNIT + i]; //top_df[gbl_out_scan_off + (k*MLO_OUT_STACKS + n0)*MLO_IN_CHANNEL_STRIDE + i]; //proc_mem[(n*MLO_MAP_WK_SZ + p4)*MLO_READ_UNIT + i];
				}

				for (uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
				{
					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						pvt_accum[(k*MLO_OUT_STACKS + n)*MLO_N_LCL_IN_MAPS + c]
							+= bot_dat[c*MLO_READ_UNIT + i] * pvt_top[i];
#if 0

						if (k_idx + k*MLO_OUT_STACKS + n == 0 && c_idx + c * MLO_IN_STACKS + m_idx == 16 && m_idx < MLO_IN_STACKS && c_idx + m_idx + c*MLO_IN_STACKS < MLO_N_INPUTS && k_idx + k*MLO_OUT_STACKS + n < MLO_N_OUTPUTS)
						{
							printf("K:s: %d %d %d %d %d %d %d %d %f %f %f %f\n",
								MLO_OUT_STACKS,
								n,
								m_idx,
								gbl_out_scan_off,
								get_group_id(1),
								get_local_id(0),
								k,
								c,
								pvt_accum[(n* MLO_N_LCL_OUT_MAPS + k)*MLO_N_LCL_IN_MAPS + c],
								bot_dat[c*MLO_READ_UNIT + i] * pvt_top[i],
								bot_dat[c*MLO_READ_UNIT + i],
								pvt_top[i]
							);
						}
#endif

					}

				}
			}
		}

	}

	barrier(CLK_LOCAL_MEM_FENCE);

#if MLO_MAP_WK_SZ > 8
// transpose
	uint red_base_off = m_idx * MLO_MAP_WK_SZ*MLO_ACCUM_SZ;
	for (uint l = 0; l < MLO_ACCUM_SZ; ++l)
	{
		// write data
		red_mem[red_base_off + p4* MLO_ACCUM_SZ + l] = pvt_accum[l];

	}
	barrier(CLK_LOCAL_MEM_FENCE);

	// do final summation

	__private _FLOAT final_sum[1] = { 0 };

#if MLO_ACCUM_SZ & (MLO_ACCUM_SZ-1)
	uint new_m_idx = iDiv(lcl_id, MLO_ACCUM_SZ);
	uint new_p4 = iMod(lcl_id, new_m_idx, MLO_ACCUM_SZ);
#else
	uint new_m_idx = (lcl_id / MLO_ACCUM_SZ);
	uint new_p4 = (lcl_id & (MLO_ACCUM_SZ-1));

#endif

	uint new_red_base_off = new_m_idx * MLO_MAP_WK_SZ*MLO_ACCUM_SZ;

	for (uint s = 0; s < MLO_MAP_WK_SZ; ++s)
	{
		final_sum[0] += red_mem[new_red_base_off + s*MLO_ACCUM_SZ + new_p4];
	}

	// write out
	// inputs are outputs
	uint wei_df_off = ((ib * MLO_N_OUTPUTS + k_idx) * (uint)MLO_WEI_BATCH_STRIDE) + ((c_idx + new_m_idx) * MLO_WEI_CHANNEL_STRIDE);

#if MLO_N_LCL_IN_MAPS & (MLO_N_LCL_IN_MAPS-1)

	uint n = iDiv(new_p4, MLO_N_LCL_IN_MAPS);
	uint c = iMod(new_p4, n, MLO_N_LCL_IN_MAPS);
#else

	uint n = (new_p4 / MLO_N_LCL_IN_MAPS);
	uint c = (new_p4 & (MLO_N_LCL_IN_MAPS-1));
#endif

	uint k = 0;

	if (new_m_idx < MLO_IN_STACKS &&k_idx + k*MLO_OUT_STACKS + n < MLO_N_OUTPUTS && c_idx + new_m_idx + c*MLO_IN_STACKS < MLO_N_INPUTS)
	{
		weights_df[wei_df_off + (k*MLO_OUT_STACKS + n)*MLO_WEI_BATCH_STRIDE + c*MLO_IN_STACKS*MLO_WEI_CHANNEL_STRIDE] = final_sum[0];
	}
#else

#if 0 //MLO_IN_WIDTH > 24
// logar reduction
	for (uint i = lcl_id; i < MLO_LCL_MEM_SZ; i += MLO_GRP_SZ)
	{
		red_mem[i] = 0;
	}

	int red_base_off = (m_idx >= MLO_OUT_STACKS) ? MLO_LCL_MEM_SZ : m_idx * MLO_POW2_MAP_WK_SZ;
	// final summation over each filter row
	for (uint l = 0; l < MLO_ACCUM_SZ; ++l)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		// write data
		red_mem[red_base_off + p4] = pvt_accum[l];

		// barrier inside
		ReduceKernel(&red_mem[red_base_off], &pvt_accum[l], p4, p4, MLO_POW2_MAP_WK_SZ, 1, false);

	}

	barrier(CLK_LOCAL_MEM_FENCE);
#else
	// direct reduction
	for (uint l = 0; l < MLO_ACCUM_SZ; ++l)
	{
		lcl_mem[lcl_id] = pvt_accum[l];
		barrier(CLK_LOCAL_MEM_FENCE);
		if (p4 == 0)
		{
			for (uint i = 1; i < MLO_MAP_WK_SZ; ++i)
			{
				pvt_accum[l] += lcl_mem[lcl_id + i];
			}

		}
		barrier(CLK_LOCAL_MEM_FENCE);
	}

#endif

	// write out
	// inputs are outputs
	uint wei_df_off = ((ib * MLO_N_OUTPUTS + k_idx) * (int)MLO_WEI_BATCH_STRIDE) + ((c_idx + m_idx) * MLO_WEI_CHANNEL_STRIDE);

	if (p4 == 0)
	{
		for (uint n = 0; n < MLO_OUT_STACKS  ; ++n)
		{
			for (uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
			{
				for (uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
				{
					if (m_idx < MLO_IN_STACKS && k_idx + k*MLO_OUT_STACKS + n < MLO_N_OUTPUTS && c_idx + m_idx + c*MLO_IN_STACKS < MLO_N_INPUTS)
					{
						weights_df[wei_df_off + (k*MLO_OUT_STACKS + n)*MLO_WEI_BATCH_STRIDE + c*MLO_IN_STACKS*MLO_WEI_CHANNEL_STRIDE] = pvt_accum[(k*MLO_OUT_STACKS + n)*MLO_N_LCL_IN_MAPS + c];
					}

				}

			}
		}
	}

#endif
}

// final reduction kernel
// add filters over batches
__attribute__((reqd_work_group_size(MLO_UT_GRP_SZ0, 1, 1)))
__kernel void MIOpenCvBwdWrW_rdc(
	const __global _FLOAT * __restrict weight_df_tmp,
	__global _FLOAT * __restrict weights_df
)
{
	uint gbl_id = get_global_id(0);
	uint wei_idx0 = gbl_id * MLO_UT_READ_UNIT;

#if MLO_WEI_CHANNEL_STRIDE & (MLO_WEI_CHANNEL_STRIDE - 1)
	uint wei_blk_idx = iDiv(wei_idx0, MLO_WEI_CHANNEL_STRIDE);
	uint wei_idx = iMod(wei_idx0, wei_blk_idx, MLO_WEI_CHANNEL_STRIDE);
#else
	uint wei_blk_idx = wei_idx0 / MLO_WEI_CHANNEL_STRIDE;
	uint wei_idx = wei_idx0 & (MLO_WEI_CHANNEL_STRIDE - 1);
#endif

	_FLOAT pvt_accum_wei[MLO_UT_READ_UNIT];
	for (uint i = 0; i < MLO_UT_READ_UNIT; ++i)
	{
		pvt_accum_wei[i] = 0;
	}

	uint batch_loop = (MLO_BATCH_SZ + (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS) - 1) / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS);
	for (uint i = 0; i < batch_loop; ++i)
	{
		for (uint j = 0; j < MLO_UT_READ_UNIT; ++j)
		{
			pvt_accum_wei[j]
				+= weight_df_tmp[(wei_blk_idx * MLO_WEI_CHANNEL_STRIDE + i* MLO_N_OUTPUTS*MLO_WEI_BATCH_STRIDE) + wei_idx + j];
		}
	}

	for (uint j = 0; j < MLO_UT_READ_UNIT; ++j)
	{
		weights_df[wei_idx0 + j] = pvt_accum_wei[j];
	}

}