Files
2026-07-13 12:47:05 +08:00

1125 lines
51 KiB
C++

/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* 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.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author AbdelRauf
//
#include <execution/ThreadPool.h>
#include <execution/Threads.h>
#include <helpers/LoopsCoordsHelper.h>
#include <math/platformmath.h>
#include <math/templatemath.h>
#include <ops/declarable/helpers/reductions.h>
#include <cmath>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <vector>
#if 1
#define LOG_CALLS(X)
#else
#define LOG_CALLS(X) sd_printf("___%s_________%d+\n", __PRETTY_FUNCTION__, X);
#endif
#define USE_REDUCED_DIV 1
namespace sd {
namespace ops {
namespace helpers {
constexpr int threadingThreshold = 4096 * 2;
constexpr int vectorizationThreshold = 64;
struct DeviationAggregate {
double n;
double mean;
double M2;
};
template <typename X, typename Z, bool Standard = false>
class Deviation {
public:
using aggregate_type = DeviationAggregate;
template <bool S = Standard>
static SD_INLINE typename std::enable_if<S == true, Z>::type getDeviation(const aggregate_type& a,
bool biasCorrected) {
if (a.n <= 1.0) {
return static_cast<Z>(0.0);
}
double ret;
if (biasCorrected) {
ret = a.M2 / (a.n - 1.0);
if (ret < 0.0) ret = a.M2 / a.n;
} else {
ret = a.M2 / a.n;
}
return sd::math::sd_sqrt<double, Z>(ret);
}
template <bool S = Standard>
static SD_INLINE typename std::enable_if<S == false, Z>::type getDeviation(const aggregate_type& a,
bool biasCorrected) {
if (a.n <= 1.0) {
return static_cast<Z>(0.0);
}
double ret;
if (biasCorrected) {
ret = a.M2 / (a.n - 1.0);
if (ret < 0.0) ret = a.M2 / a.n;
} else {
ret = a.M2 / a.n;
}
return static_cast<Z>(ret);
}
template <bool InitializeFromAggregate = false>
static SD_INLINE void updateInnerLoop1b(const X* buffer, sd::LongType length, aggregate_type& a) {
double xn = InitializeFromAggregate ? a.n : 0;
double xmean = InitializeFromAggregate ? a.mean : 0;
double xM2 = InitializeFromAggregate ? a.M2 : 0;
for (sd::LongType i = 0; i < length; i++) {
double n = xn + 1;
double delta = buffer[i] - xmean;
double delta2 = delta * delta;
double delta_n = delta / n;
xmean = xmean + delta_n;
xM2 += delta2 * xn / n;
xn = n;
}
a = {xn, xmean, xM2};
}
template <bool InitializeFromAggregate = false>
static SD_INLINE void updateInnerLoop1b(const X* buffer, sd::LongType length, sd::LongType stride,
aggregate_type& a) {
double xn = InitializeFromAggregate ? a.n : 0;
double xmean = InitializeFromAggregate ? a.mean : 0;
double xM2 = InitializeFromAggregate ? a.M2 : 0;
for (sd::LongType i = 0; i < length; i++) {
double n = xn + 1;
double delta = buffer[i * stride] - xmean;
double delta2 = delta * delta;
double delta_n = delta / n;
xmean = xmean + delta_n;
xM2 += delta2 * xn / n;
xn = n;
}
a = {xn, xmean, xM2};
}
// WARNING: a.n a1.n a2.n a3.n should be equal
template <bool InitializeFromAggregate = false>
static SD_INLINE void updateInnerLoop4b(const X* buffer, const X* buffer1, const X* buffer2, const X* buffer3,
sd::LongType length, aggregate_type& a, aggregate_type& a1,
aggregate_type& a2, aggregate_type& a3) {
double xn, x0mean, x0M2, x1mean, x1M2, x2mean, x2M2, x3mean, x3M2;
xn = InitializeFromAggregate ? a.n : 0;
x0mean = InitializeFromAggregate ? a.mean : 0;
x1mean = InitializeFromAggregate ? a1.mean : 0;
x2mean = InitializeFromAggregate ? a2.mean : 0;
x3mean = InitializeFromAggregate ? a3.mean : 0;
x0M2 = InitializeFromAggregate ? a.M2 : 0;
x1M2 = InitializeFromAggregate ? a1.M2 : 0;
x2M2 = InitializeFromAggregate ? a2.M2 : 0;
x3M2 = InitializeFromAggregate ? a3.M2 : 0;
for (sd::LongType i = 0; i < length; i++) {
double n = xn + 1;
double delta0 = buffer[i] - x0mean;
double delta1 = buffer1[i] - x1mean;
double delta2 = buffer2[i] - x2mean;
double delta3 = buffer3[i] - x3mean;
#if defined(USE_REDUCED_DIV)
double delta_nj0 = delta0 / n;
double delta_nj1 = delta1 / n;
double delta_nj2 = delta2 / n;
double delta_nj3 = delta3 / n;
x0mean = x0mean + delta_nj0;
x1mean = x1mean + delta_nj1;
x2mean = x2mean + delta_nj2;
x3mean = x3mean + delta_nj3;
x0M2 += delta0 * delta_nj0 * xn;
x1M2 += delta1 * delta_nj1 * xn;
x2M2 += delta2 * delta_nj2 * xn;
x3M2 += delta3 * delta_nj3 * xn;
#else
double delta02 = delta0 * delta0;
double delta12 = delta1 * delta1;
double delta22 = delta2 * delta2;
double delta32 = delta3 * delta3;
x0mean = x0mean + delta0 / n;
x1mean = x1mean + delta1 / n;
x2mean = x2mean + delta2 / n;
x3mean = x3mean + delta3 / n;
x0M2 += delta02 * xn / n;
x1M2 += delta12 * xn / n;
x2M2 += delta22 * xn / n;
x3M2 += delta32 * xn / n;
#endif
xn = n;
}
a = {xn, x0mean, x0M2};
a1 = {xn, x1mean, x1M2};
a2 = {xn, x2mean, x2M2};
a3 = {xn, x3mean, x3M2};
}
// WARNING: a.n a1.n a2.n a3.n should be equal
template <bool InitializeFromAggregate = false>
static SD_INLINE void updateInnerLoop4b(const X* buffer, const X* buffer1, const X* buffer2, const X* buffer3,
sd::LongType length, sd::LongType stride, aggregate_type& a,
aggregate_type& a1, aggregate_type& a2, aggregate_type& a3) {
double xn, x0mean, x0M2, x1mean, x1M2, x2mean, x2M2, x3mean, x3M2;
xn = InitializeFromAggregate ? a.n : 0;
x0mean = InitializeFromAggregate ? a.mean : 0;
x1mean = InitializeFromAggregate ? a1.mean : 0;
x2mean = InitializeFromAggregate ? a2.mean : 0;
x3mean = InitializeFromAggregate ? a3.mean : 0;
x0M2 = InitializeFromAggregate ? a.M2 : 0;
x1M2 = InitializeFromAggregate ? a1.M2 : 0;
x2M2 = InitializeFromAggregate ? a2.M2 : 0;
x3M2 = InitializeFromAggregate ? a3.M2 : 0;
for (sd::LongType i = 0; i < length; i++) {
double n = xn + 1;
double delta0 = buffer[i * stride] - x0mean;
double delta1 = buffer1[i * stride] - x1mean;
double delta2 = buffer2[i * stride] - x2mean;
double delta3 = buffer3[i * stride] - x3mean;
#if defined(USE_REDUCED_DIV)
double delta_nj0 = delta0 / n;
double delta_nj1 = delta1 / n;
double delta_nj2 = delta2 / n;
double delta_nj3 = delta3 / n;
x0mean = x0mean + delta_nj0;
x1mean = x1mean + delta_nj1;
x2mean = x2mean + delta_nj2;
x3mean = x3mean + delta_nj3;
x0M2 += delta0 * delta_nj0 * xn;
x1M2 += delta1 * delta_nj1 * xn;
x2M2 += delta2 * delta_nj2 * xn;
x3M2 += delta3 * delta_nj3 * xn;
#else
double delta02 = delta0 * delta0;
double delta12 = delta1 * delta1;
double delta22 = delta2 * delta2;
double delta32 = delta3 * delta3;
x0mean = x0mean + delta0 / n;
x1mean = x1mean + delta1 / n;
x2mean = x2mean + delta2 / n;
x3mean = x3mean + delta3 / n;
x0M2 += delta02 * xn / n;
x1M2 += delta12 * xn / n;
x2M2 += delta22 * xn / n;
x3M2 += delta32 * xn / n;
#endif
xn = n;
}
a = {xn, x0mean, x0M2};
a1 = {xn, x1mean, x1M2};
a2 = {xn, x2mean, x2M2};
a3 = {xn, x3mean, x3M2};
}
// WARNING: length required to to be length/8.
static SD_INLINE void updateInnerLoop1b_vec8(const X* buffer, sd::LongType length_8th, double (&xn)[8],
double (&xmean)[8], double (&xM2)[8]) {
double n[8] = {};
for (int i = 0; i < length_8th; i++) {
const X* bufferX = &(buffer[i * 8]);
#pragma omp simd
for (int j = 0; j < 8; j++) {
n[j] = xn[j] + 1.0;
#if defined(USE_REDUCED_DIV)
double delta = bufferX[j] - xmean[j];
double delta_nj = delta / n[j];
xmean[j] = xmean[j] + delta_nj;
xM2[j] = xM2[j] + delta * delta_nj * xn[j];
#else
double delta = bufferX[j] - xmean[j];
double delta2 = delta * delta;
xmean[j] = xmean[j] + delta / n[j];
xM2[j] = xM2[j] + delta2 * xn[j] / n[j];
#endif
xn[j] = n[j];
}
}
}
// WARNING: length required to to be length/8.
// WARNING: a.n a1.n a2.n a3.n should be equal
static SD_INLINE void updateInnerLoop4b_vec8(const X* buffer, const X* buffer1, const X* buffer2, const X* buffer3,
sd::LongType length_8th, double (&xn)[8], double (&x0mean)[8],
double (&x1mean)[8], double (&x2mean)[8], double (&x3mean)[8],
double (&x0M2)[8], double (&x1M2)[8], double (&x2M2)[8],
double (&x3M2)[8]) {
double n[8] = {};
for (sd::LongType i = 0; i < length_8th; i++) {
const X* bufferX = &(buffer[i * 8]);
const X* buffer1X = &(buffer1[i * 8]);
const X* buffer2X = &(buffer2[i * 8]);
const X* buffer3X = &(buffer3[i * 8]);
#pragma omp simd
for (int j = 0; j < 8; j++) {
n[j] = xn[j] + 1.0;
double delta0 = bufferX[j] - x0mean[j];
double delta1 = buffer1X[j] - x1mean[j];
double delta2 = buffer2X[j] - x2mean[j];
double delta3 = buffer3X[j] - x3mean[j];
double delta0_nj = delta0 / n[j];
double delta1_nj = delta1 / n[j];
double delta2_nj = delta2 / n[j];
double delta3_nj = delta3 / n[j];
x0mean[j] = x0mean[j] + delta0_nj;
x1mean[j] = x1mean[j] + delta1_nj;
x2mean[j] = x2mean[j] + delta2_nj;
x3mean[j] = x3mean[j] + delta3_nj;
x0M2[j] = x0M2[j] + xn[j] * delta0 * delta0_nj;
x1M2[j] = x1M2[j] + xn[j] * delta1 * delta1_nj;
x2M2[j] = x2M2[j] + xn[j] * delta2 * delta2_nj;
x3M2[j] = x3M2[j] + xn[j] * delta3 * delta3_nj;
xn[j] = n[j];
}
}
}
template <size_t constRank, bool LastIndexFaster = true>
static SD_INLINE void reduceConstRankLoop4b(const X* buff, const X* buff1, const X* buff2, const X* buff3, Z* output0,
Z* output1, Z* output2, Z* output3, const sd::LongType* bases,
const sd::LongType* strides, const sd::LongType& outerLoopCount,
const sd::LongType& innerLoopCount, bool biasCorrected) {
// skip 1 from the beginning or end depending the Order
constexpr size_t updated_index = LastIndexFaster ? 0 : 1;
constexpr size_t updated_rank = constRank - 1;
sd::CoordsState<updated_rank - 1> cst;
// we skip 1
size_t offset =
sd::init_coords<updated_rank, 0, LastIndexFaster>(cst, 0, bases + updated_index, strides + updated_index);
aggregate_type agg0 = {};
aggregate_type agg1 = {};
aggregate_type agg2 = {};
aggregate_type agg3 = {};
if (innerLoopCount >= vectorizationThreshold) {
LOG_CALLS(0)
// use vector
const sd::LongType loopCount = innerLoopCount & (-8);
const sd::LongType tail = innerLoopCount & 7;
const auto loopCount_8th = loopCount / 8;
double xn[8] = {};
double x0mean[8] = {};
double x1mean[8] = {};
double x2mean[8] = {};
double x3mean[8] = {};
double x0M2[8] = {};
double x1M2[8] = {};
double x2M2[8] = {};
double x3M2[8] = {};
for (sd::LongType i = 0; i < outerLoopCount; i++) {
const X* buffPtr0 = &(buff[offset]);
const X* buffPtr1 = &(buff1[offset]);
const X* buffPtr2 = &(buff2[offset]);
const X* buffPtr3 = &(buff3[offset]);
updateInnerLoop4b_vec8(buffPtr0, buffPtr1, buffPtr2, buffPtr3, loopCount_8th, xn, x0mean, x1mean, x2mean,
x3mean, x0M2, x1M2, x2M2, x3M2);
if (tail > 0) {
// tails
updateInnerLoop4b<true>(&(buffPtr0[loopCount]), &(buffPtr1[loopCount]), &(buffPtr2[loopCount]),
&(buffPtr3[loopCount]), tail, agg0, agg1, agg2, agg3);
}
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
}
// merge vector and tails
auto merged0 = mergeAggregates(xn[0], x0mean, x0M2);
auto merged1 = mergeAggregates(xn[0], x1mean, x1M2);
auto merged2 = mergeAggregates(xn[0], x2mean, x2M2);
auto merged3 = mergeAggregates(xn[0], x3mean, x3M2);
// tail with merged3
agg0 = mergeAggregates(merged0, agg0);
agg1 = mergeAggregates(merged1, agg1);
agg2 = mergeAggregates(merged2, agg2);
agg3 = mergeAggregates(merged3, agg3);
} else {
LOG_CALLS(1)
for (sd::LongType i = 0; i < outerLoopCount; i++) {
updateInnerLoop4b<true>(&(buff[offset]), &(buff1[offset]), &(buff2[offset]), &(buff3[offset]), innerLoopCount,
agg0, agg1, agg2, agg3);
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
}
}
*output0 = getDeviation(agg0, biasCorrected);
*output1 = getDeviation(agg1, biasCorrected);
*output2 = getDeviation(agg2, biasCorrected);
*output3 = getDeviation(agg3, biasCorrected);
return;
}
template <size_t constRank, bool LastIndexFaster = true>
static SD_INLINE void reduceConstRankLoop4b(const X* buff, const X* buff1, const X* buff2, const X* buff3, Z* output0,
Z* output1, Z* output2, Z* output3, const sd::LongType* bases,
const sd::LongType* strides, const sd::LongType& outerLoopCount,
const sd::LongType& innerLoopCount, const sd::LongType& inner_stride,
bool biasCorrected) {
LOG_CALLS(2)
// skip 1 from the beginning or end depending the Order
constexpr size_t updated_index = LastIndexFaster ? 0 : 1;
constexpr size_t updated_rank = constRank - 1;
sd::CoordsState<updated_rank - 1> cst;
// we skip 1
size_t offset =
sd::init_coords<updated_rank, 0, LastIndexFaster>(cst, 0, bases + updated_index, strides + updated_index);
aggregate_type agg = {0.0, 0.0, 0.0};
aggregate_type agg1 = {0.0, 0.0, 0.0};
aggregate_type agg2 = {0.0, 0.0, 0.0};
aggregate_type agg3 = {0.0, 0.0, 0.0};
// LOG_CALLS(0)
for (sd::LongType i = 0; i < outerLoopCount; i++) {
updateInnerLoop4b<true>(&(buff[offset]), &(buff1[offset]), &(buff2[offset]), &(buff3[offset]), innerLoopCount,
inner_stride, agg, agg1, agg2, agg3);
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
}
*output0 = getDeviation(agg, biasCorrected);
*output1 = getDeviation(agg1, biasCorrected);
*output2 = getDeviation(agg2, biasCorrected);
*output3 = getDeviation(agg3, biasCorrected);
return;
}
template <size_t constRank, bool LastIndexFaster = true>
static SD_INLINE void reduceConstRankLoop1b(const X* buff, Z* output, const sd::LongType* bases,
const sd::LongType* strides, const sd::LongType& outerLoopCount,
const sd::LongType& innerLoopCount, bool biasCorrected) {
// skip 1 from the beginning or end depending the Order
constexpr size_t updated_index = LastIndexFaster ? 0 : 1;
constexpr size_t updated_rank = constRank - 1;
sd::CoordsState<updated_rank - 1> cst;
// we skip 1
size_t offset =
sd::init_coords<updated_rank, 0, LastIndexFaster>(cst, 0, bases + updated_index, strides + updated_index);
aggregate_type agg = {0.0, 0.0, 0.0};
if (innerLoopCount >= vectorizationThreshold) {
LOG_CALLS(0)
// use vector
const sd::LongType loopCount = innerLoopCount & (-8);
const sd::LongType tail = innerLoopCount & 7;
const auto loopCount_8th = loopCount / 8;
double xn[8] = {};
double xmean[8] = {};
double xM2[8] = {};
for (sd::LongType i = 0; i < outerLoopCount; i++) {
const X* buffPtr0 = &(buff[offset]);
updateInnerLoop1b_vec8(buffPtr0, loopCount_8th, xn, xmean, xM2);
if (tail > 0) {
updateInnerLoop1b<true>(&(buffPtr0[loopCount]), tail, agg);
}
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
}
// merge vector between and with the tail agg
auto merged = mergeAggregates(xn[0], xmean, xM2);
agg = mergeAggregates(agg, merged);
} else {
LOG_CALLS(1)
for (sd::LongType i = 0; i < outerLoopCount; i++) {
updateInnerLoop1b<true>(&(buff[offset]), innerLoopCount, agg);
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
}
}
*output = getDeviation(agg, biasCorrected);
return;
}
template <size_t constRank, bool LastIndexFaster = true>
static SD_INLINE void reduceConstRankLoop1b(const X* buff, Z* output, const sd::LongType* bases,
const sd::LongType* strides, const sd::LongType& outerLoopCount,
const sd::LongType& innerLoopCount, const sd::LongType& inner_stride,
bool biasCorrected) {
LOG_CALLS(2)
// skip 1 from the beginning or end depending the Order
constexpr size_t updated_index = LastIndexFaster ? 0 : 1;
constexpr size_t updated_rank = constRank - 1;
sd::CoordsState<updated_rank - 1> cst;
// we skip 1
size_t offset =
sd::init_coords<updated_rank, 0, LastIndexFaster>(cst, 0, bases + updated_index, strides + updated_index);
aggregate_type agg = {0.0, 0.0, 0.0};
// LOG_CALLS(0)
for (sd::LongType i = 0; i < outerLoopCount; i++) {
updateInnerLoop1b<true>(&(buff[offset]), innerLoopCount, inner_stride, agg);
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
}
*output = getDeviation(agg, biasCorrected);
return;
}
template <bool LastIndexFaster = true>
static SD_INLINE void updateGeneralLoop1b(LongType rank, const X* buff, DeviationAggregate& agg, const sd::LongType* bases,
const sd::LongType* strides, const sd::LongType& outerLoopStart,
const sd::LongType& outerLoopStop, const sd::LongType& innerLoopCount) {
agg = {};
size_t offset = 0;
sd::LongType outerLoopCount = outerLoopStop - outerLoopStart;
sd::LongType coords[SD_MAX_RANK] = {};
sd::LongType* ptr_coords = (sd::LongType*)&coords;
if (outerLoopStart > 0) {
INDEX2COORDS(outerLoopStart, rank - 1, bases, ptr_coords);
COORDS2INDEX(rank, strides, ptr_coords, offset);
}
if (innerLoopCount >= vectorizationThreshold) {
LOG_CALLS(88)
// use vector
const sd::LongType loopCount = innerLoopCount & (-8);
const sd::LongType tail = innerLoopCount & 7;
const auto loopCount_8th = loopCount / 8;
double xn[8] = {};
double x0mean[8] = {};
double x0M2[8] = {};
for (sd::LongType i = 0; i < outerLoopCount; i++) {
const X* buffPtr0 = &(buff[offset]);
updateInnerLoop1b_vec8(buffPtr0, loopCount_8th, xn, x0mean, x0M2);
if (tail > 0) {
// tails
updateInnerLoop1b<true>(&(buffPtr0[loopCount]), tail, agg);
}
offset = inc_coords<LastIndexFaster>(bases, strides, ptr_coords, offset, rank, 1);
}
// merge vector and tails
auto merged0 = mergeAggregates(xn[0], x0mean, x0M2);
// tail with merged3
agg = mergeAggregates(merged0, agg);
} else {
LOG_CALLS(89)
for (sd::LongType i = 0; i < outerLoopCount; i++) {
updateInnerLoop1b<true>(&(buff[offset]), innerLoopCount, agg);
offset = inc_coords<LastIndexFaster>(bases, strides, ptr_coords, offset, rank, 1);
}
}
}
template <bool LastIndexFaster = true>
static SD_INLINE void updateGeneralLoop1b(int rank, const X* buff, DeviationAggregate& agg, const sd::LongType* bases,
const sd::LongType* strides, const sd::LongType& outerLoopStart,
const sd::LongType& outerLoopStop, const sd::LongType& innerLoopCount,
const sd::LongType& inner_stride) {
agg = {};
size_t offset = 0;
sd::LongType outerLoopCount = outerLoopStop - outerLoopStart;
sd::LongType coords[SD_MAX_RANK] = {};
sd::LongType* ptr_coords = (sd::LongType*)&coords;
if (outerLoopStart > 0) {
INDEX2COORDS(outerLoopStart, rank - 1, bases, ptr_coords);
COORDS2INDEX(rank, strides, ptr_coords, offset);
}
LOG_CALLS(90)
for (sd::LongType i = 0; i < outerLoopCount; i++) {
updateInnerLoop1b<true>(&(buff[offset]), innerLoopCount, inner_stride, agg);
offset = inc_coords<LastIndexFaster>(bases, strides, ptr_coords, offset, rank, 1);
}
}
static SD_INLINE aggregate_type mergeAggregates(const aggregate_type& x, const aggregate_type& y) {
if ((long)x.n == 0 && (long)y.n > 0)
return y;
else if ((long)x.n > 0 && (long)y.n == 0)
return x;
double n = x.n + y.n;
double delta = y.mean - x.mean;
double delta2 = delta * delta;
double meanD = x.mean + delta * y.n / n;
double M2D = x.M2 + y.M2;
M2D += delta2 * x.n * y.n / n;
return {n, meanD, M2D};
}
static SD_INLINE aggregate_type mergeAggregates(double xn, double (&xmean)[8], double (&xM2)[8]) {
auto arg0 = mergeAggregates(xn, xn, xmean[0], xmean[1], xM2[0], xM2[1]);
auto arg1 = mergeAggregates(xn, xn, xmean[2], xmean[3], xM2[2], xM2[3]);
auto arg2 = mergeAggregates(xn, xn, xmean[4], xmean[5], xM2[4], xM2[5]);
auto arg3 = mergeAggregates(xn, xn, xmean[6], xmean[7], xM2[6], xM2[7]);
auto arg01 = mergeAggregates(arg0, arg1);
auto arg23 = mergeAggregates(arg2, arg3);
return mergeAggregates(arg01, arg23);
}
static SD_INLINE aggregate_type mergeAggregates(double (&xn)[8], double (&xmean)[8], double (&xM2)[8]) {
auto arg0 = mergeAggregates(xn[0], xn[1], xmean[0], xmean[1], xM2[0], xM2[1]);
auto arg1 = mergeAggregates(xn[2], xn[3], xmean[2], xmean[3], xM2[2], xM2[3]);
auto arg2 = mergeAggregates(xn[4], xn[5], xmean[4], xmean[5], xM2[4], xM2[5]);
auto arg3 = mergeAggregates(xn[6], xn[7], xmean[6], xmean[7], xM2[6], xM2[7]);
auto arg01 = mergeAggregates(arg0, arg1);
auto arg23 = mergeAggregates(arg2, arg3);
return mergeAggregates(arg01, arg23);
}
static SD_INLINE aggregate_type mergeAggregates(double xn, double yn, double xmean, double ymean, double xM2,
double yM2) {
double n = xn + yn;
double delta = ymean - xmean;
double delta2 = delta * delta;
double meanD = xmean + delta * yn / n;
double M2D = xM2 + yM2;
M2D += delta2 * xn * yn / n;
return {n, meanD, M2D};
}
};
template <typename X, typename Z, typename DeviationOp, bool LastIndexFaster = true>
static void reductionCase1Scalar(const int& second_rank, const sd::LongType* inner_bases,
const sd::LongType* inner_strides, const X* bufferX, Z* outputZ, bool biasCorrected) {
using AggType = typename DeviationOp::aggregate_type;
sd::LongType inner_total;
sd::LongType inner_last = 0;
int maxThreads = sd::Environment::getInstance().maxMasterThreads();
if (second_rank == 1) {
inner_total = inner_bases[0];
if (inner_total < threadingThreshold) {
maxThreads = 1;
} else {
auto gen = inner_total / threadingThreshold + 1;
maxThreads = gen > maxThreads ? maxThreads : gen;
}
} else {
inner_total = getLength<LastIndexFaster>(inner_bases, second_rank, 1, inner_last);
if (inner_total * inner_last < threadingThreshold) {
maxThreads = 1;
} else {
auto gen = inner_total * inner_last / threadingThreshold + 1;
maxThreads = gen > maxThreads ? maxThreads : gen;
}
}
#define BLOCKX4 1
std::unique_ptr<AggType[]> aggs(new AggType[maxThreads]);
AggType* ptrAggs = aggs.get();
auto func = [ptrAggs, inner_last, second_rank, inner_bases, inner_strides, bufferX](
uint64_t thread_id, int64_t start, int64_t stop, int64_t increment) -> void {
// LOG_CALLS(0)
const sd::LongType inner_stride = LastIndexFaster ? inner_strides[second_rank - 1] : inner_strides[0];
Z argCurrent;
X current;
if (second_rank == 1) {
const sd::LongType loopTotal = stop - start;
#if defined(BLOCKX4)
if (loopTotal >= 2048) {
AggType agg, agg1, agg2, agg3;
if (inner_stride == 1) {
// use vector version
sd::LongType loopTotal4_32 = loopTotal & (-32);
auto loopCount4 = loopTotal4_32 / 4;
auto loopCount4_8th = loopCount4 / 8;
auto tail = (loopTotal & 31);
const X* buffer0 = bufferX + start * 1;
const X* buffer1 = buffer0 + 1 * loopCount4;
const X* buffer2 = buffer1 + 1 * loopCount4;
const X* buffer3 = buffer2 + 1 * loopCount4;
double xn[8] = {};
double x0mean[8] = {};
double x1mean[8] = {};
double x2mean[8] = {};
double x3mean[8] = {};
double x0M2[8] = {};
double x1M2[8] = {};
double x2M2[8] = {};
double x3M2[8] = {};
DeviationOp::updateInnerLoop4b_vec8(buffer0, buffer1, buffer2, buffer3, loopCount4_8th, xn, x0mean, x1mean,
x2mean, x3mean, x0M2, x1M2, x2M2, x3M2);
// merge vectors
agg = DeviationOp::mergeAggregates(xn[0], x0mean, x0M2);
agg1 = DeviationOp::mergeAggregates(xn[0], x1mean, x1M2);
agg2 = DeviationOp::mergeAggregates(xn[0], x2mean, x2M2);
agg3 = DeviationOp::mergeAggregates(xn[0], x3mean, x3M2);
// tail merge to one of the aggs
if (tail > 0) {
DeviationOp::template updateInnerLoop1b<true>(&(buffer3[loopCount4]), tail, agg3);
}
} else {
auto loopCount4 = loopTotal / 4;
auto tail = (loopTotal & 3);
const X* buffer0 = bufferX + start * inner_stride;
const X* buffer1 = buffer0 + inner_stride * loopCount4;
const X* buffer2 = buffer1 + inner_stride * loopCount4;
const X* buffer3 = buffer2 + inner_stride * loopCount4;
DeviationOp::updateInnerLoop4b(buffer0, buffer1, buffer2, buffer3, loopCount4, inner_stride, agg, agg1, agg2,
agg3);
// tail
if (tail > 0) {
DeviationOp::template updateInnerLoop1b<true>(&(buffer3[loopCount4 * inner_stride]), tail, inner_stride,
agg3);
}
}
// merge all
auto merged = DeviationOp::mergeAggregates(agg, agg1);
merged = DeviationOp::mergeAggregates(merged, agg2);
merged = DeviationOp::mergeAggregates(merged, agg3);
ptrAggs[thread_id] = merged;
} else {
#endif
if (inner_stride == 1) {
const X* buffer0 = bufferX + start;
if (loopTotal > vectorizationThreshold) {
auto length8 = loopTotal / 8;
auto bufferTail = buffer0 + (loopTotal & (-8));
auto tail = loopTotal & 7;
double xn[8] = {};
double xmean[8] = {};
double xM2[8] = {};
DeviationOp::updateInnerLoop1b_vec8(buffer0, length8, xn, xmean, xM2);
auto agg = DeviationOp::mergeAggregates(xn[0], xmean, xM2);
// add tail into
if (tail > 0) {
DeviationOp::template updateInnerLoop1b<true>(bufferTail, tail, agg);
}
ptrAggs[thread_id] = agg;
} else {
DeviationOp::updateInnerLoop1b(buffer0, loopTotal, ptrAggs[thread_id]);
}
} else {
DeviationOp::updateInnerLoop1b(&(bufferX[start * inner_stride]), loopTotal, inner_stride, ptrAggs[thread_id]);
}
#if defined(BLOCKX4)
}
#endif
} else {
// just lets do general case
if (inner_stride == 1) {
DeviationOp::template updateGeneralLoop1b<LastIndexFaster>(second_rank, bufferX, ptrAggs[thread_id],
inner_bases, inner_strides, start, stop, inner_last,
inner_stride);
} else {
DeviationOp::template updateGeneralLoop1b<LastIndexFaster>(second_rank, bufferX, ptrAggs[thread_id],
inner_bases, inner_strides, start, stop, inner_last,
inner_stride);
}
}
};
#if 0
int Count = 0;
func(0, 0, inner_total, 1);
#else
int Count = samediff::Threads::parallel_tad(func, 0, inner_total, 1, maxThreads);
#endif
auto current = ptrAggs[0];
for (int i = 1; i < Count; i++) {
current = DeviationOp::mergeAggregates(current, ptrAggs[i]);
}
*outputZ = DeviationOp::getDeviation(current, biasCorrected);
}
template <typename X, typename Z, typename DeviationOp, bool LastIndexFaster = true, typename Movement>
static void reductionCases(Movement& movement, sd::LongType loopTotal, const int& second_rank,
const sd::LongType* inner_bases, const sd::LongType* inner_strides, const X* bufferX,
Z* outputZ, bool biasCorrected) {
using AggType = typename DeviationOp::aggregate_type;
sd::LongType inner_stride = LastIndexFaster ? inner_strides[second_rank - 1] : inner_strides[0];
sd::LongType loopTotal_K = loopTotal / 4;
sd::LongType loopTotalTail = loopTotal & 3;
if (inner_stride == 1) {
if (second_rank == 1) {
LOG_CALLS(0)
sd::LongType inner_total = getLength<LastIndexFaster>(inner_bases, second_rank);
auto loopCount4 = inner_total & (-8);
auto loopCount4_8th = inner_total / 8;
auto tail = inner_total & 7;
bool use_vector = loopCount4_8th > 16;
for (sd::LongType i = 0; i < loopTotal_K; i++) {
AggType agg0, agg1, agg2, agg3;
const X* buff0 = &(bufferX[movement.First()]);
Z* output0 = &(outputZ[movement.Second()]);
movement.increment();
const X* buff1 = &(bufferX[movement.First()]);
Z* output1 = &(outputZ[movement.Second()]);
movement.increment();
const X* buff2 = &(bufferX[movement.First()]);
Z* output2 = &(outputZ[movement.Second()]);
movement.increment();
const X* buff3 = &(bufferX[movement.First()]);
Z* output3 = &(outputZ[movement.Second()]);
movement.increment();
if (use_vector) {
double xn[8] = {};
double x0mean[8] = {};
double x1mean[8] = {};
double x2mean[8] = {};
double x3mean[8] = {};
double x0M2[8] = {};
double x1M2[8] = {};
double x2M2[8] = {};
double x3M2[8] = {};
DeviationOp::updateInnerLoop4b_vec8(buff0, buff1, buff2, buff3, loopCount4_8th, xn, x0mean, x1mean, x2mean,
x3mean, x0M2, x1M2, x2M2, x3M2);
// merge vectors
agg0 = DeviationOp::mergeAggregates(xn[0], x0mean, x0M2);
agg1 = DeviationOp::mergeAggregates(xn[0], x1mean, x1M2);
agg2 = DeviationOp::mergeAggregates(xn[0], x2mean, x2M2);
agg3 = DeviationOp::mergeAggregates(xn[0], x3mean, x3M2);
if (tail > 0) {
// tails into merged , this time for each
DeviationOp::template updateInnerLoop4b<true>(&(buff0[loopCount4]), &(buff1[loopCount4]),
&(buff2[loopCount4]), &(buff3[loopCount4]), tail, agg0, agg1,
agg2, agg3);
}
} else {
DeviationOp::updateInnerLoop4b(buff0, buff1, buff2, buff3, inner_total, agg0, agg1, agg2, agg3);
}
*output0 = DeviationOp::getDeviation(agg0, biasCorrected);
*output1 = DeviationOp::getDeviation(agg1, biasCorrected);
*output2 = DeviationOp::getDeviation(agg2, biasCorrected);
*output3 = DeviationOp::getDeviation(agg3, biasCorrected);
}
for (sd::LongType i = 0; i < loopTotalTail; i++) {
AggType agg0;
const X* buff0 = &(bufferX[movement.First()]);
Z* output0 = &(outputZ[movement.Second()]);
if (use_vector) {
double xn[8] = {};
double x0mean[8] = {};
double x0M2[8] = {};
DeviationOp::updateInnerLoop1b_vec8(buff0, loopCount4_8th, xn, x0mean, x0M2);
// merge vectors
agg0 = DeviationOp::mergeAggregates(xn[0], x0mean, x0M2);
if (tail > 0) {
// tails into merged
DeviationOp::template updateInnerLoop1b<true>(&(buff0[loopCount4]), tail, agg0);
}
} else {
DeviationOp::updateInnerLoop1b(buff0, inner_total, agg0);
}
movement.increment();
*output0 = DeviationOp::getDeviation(agg0, biasCorrected);
}
} else {
sd::LongType inner_last;
sd::LongType inner_loop = getLength<LastIndexFaster>(inner_bases, second_rank, 1, inner_last);
if (second_rank == 2) {
LOG_CALLS(11)
for (sd::LongType i = 0; i < loopTotal_K; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
Z* output0 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer1 = &(bufferX[movement.First()]);
Z* output1 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer2 = &(bufferX[movement.First()]);
Z* output2 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer3 = &(bufferX[movement.First()]);
Z* output3 = &(outputZ[movement.Second()]);
movement.increment();
DeviationOp::template reduceConstRankLoop4b<2, LastIndexFaster>(
buffer0, buffer1, buffer2, buffer3, output0, output1, output2, output3, inner_bases, inner_strides,
inner_loop, inner_last, biasCorrected);
}
for (sd::LongType i = 0; i < loopTotalTail; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
DeviationOp::template reduceConstRankLoop1b<2, LastIndexFaster>(buffer0, &(outputZ[movement.Second()]),
inner_bases, inner_strides, inner_loop,
inner_last, biasCorrected);
movement.increment();
}
} else if (second_rank == 3) {
LOG_CALLS(12)
for (sd::LongType i = 0; i < loopTotal_K; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
Z* output0 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer1 = &(bufferX[movement.First()]);
Z* output1 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer2 = &(bufferX[movement.First()]);
Z* output2 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer3 = &(bufferX[movement.First()]);
Z* output3 = &(outputZ[movement.Second()]);
movement.increment();
DeviationOp::template reduceConstRankLoop4b<3, LastIndexFaster>(
buffer0, buffer1, buffer2, buffer3, output0, output1, output2, output3, inner_bases, inner_strides,
inner_loop, inner_last, biasCorrected);
}
for (sd::LongType i = 0; i < loopTotalTail; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
DeviationOp::template reduceConstRankLoop1b<3, LastIndexFaster>(buffer0, &(outputZ[movement.Second()]),
inner_bases, inner_strides, inner_loop,
inner_last, biasCorrected);
movement.increment();
}
} else {
LOG_CALLS(13)
AggType agg;
for (sd::LongType i = 0; i < loopTotal; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
DeviationOp::template updateGeneralLoop1b<LastIndexFaster>(second_rank, buffer0, agg, inner_bases,
inner_strides, 0, inner_loop, inner_last);
outputZ[movement.Second()] = DeviationOp::getDeviation(agg, biasCorrected);
movement.increment();
}
}
}
} else {
if (second_rank == 1) {
LOG_CALLS(20)
sd::LongType inner_total = getLength<LastIndexFaster>(inner_bases, second_rank);
for (sd::LongType i = 0; i < loopTotal_K; i++) {
AggType agg, agg1, agg2, agg3;
const X* buffer0 = &(bufferX[movement.First()]);
Z* output0 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer1 = &(bufferX[movement.First()]);
Z* output1 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer2 = &(bufferX[movement.First()]);
Z* output2 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer3 = &(bufferX[movement.First()]);
Z* output3 = &(outputZ[movement.Second()]);
movement.increment();
DeviationOp::updateInnerLoop4b(buffer0, buffer1, buffer2, buffer3, inner_total, inner_stride, agg, agg1, agg2,
agg3);
*output0 = DeviationOp::getDeviation(agg, biasCorrected);
*output1 = DeviationOp::getDeviation(agg1, biasCorrected);
*output2 = DeviationOp::getDeviation(agg2, biasCorrected);
*output3 = DeviationOp::getDeviation(agg3, biasCorrected);
}
for (sd::LongType i = 0; i < loopTotalTail; i++) {
AggType agg;
const X* buffer0 = &(bufferX[movement.First()]);
Z* output0 = &(outputZ[movement.Second()]);
DeviationOp::updateInnerLoop1b(buffer0, inner_total, inner_stride, agg);
movement.increment();
*output0 = DeviationOp::getDeviation(agg, biasCorrected);
}
} else {
sd::LongType inner_last;
sd::LongType inner_loop = getLength<LastIndexFaster>(inner_bases, second_rank, 1, inner_last);
if (second_rank == 2) {
LOG_CALLS(21)
for (sd::LongType i = 0; i < loopTotal_K; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
Z* output0 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer1 = &(bufferX[movement.First()]);
Z* output1 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer2 = &(bufferX[movement.First()]);
Z* output2 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer3 = &(bufferX[movement.First()]);
Z* output3 = &(outputZ[movement.Second()]);
movement.increment();
DeviationOp::template reduceConstRankLoop4b<2, LastIndexFaster>(
buffer0, buffer1, buffer2, buffer3, output0, output1, output2, output3, inner_bases, inner_strides,
inner_loop, inner_last, inner_stride, biasCorrected);
}
for (sd::LongType i = 0; i < loopTotalTail; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
DeviationOp::template reduceConstRankLoop1b<2, LastIndexFaster>(buffer0, &(outputZ[movement.Second()]),
inner_bases, inner_strides, inner_loop,
inner_last, inner_stride, biasCorrected);
movement.increment();
}
} else if (second_rank == 3) {
LOG_CALLS(22)
for (sd::LongType i = 0; i < loopTotal_K; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
Z* output0 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer1 = &(bufferX[movement.First()]);
Z* output1 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer2 = &(bufferX[movement.First()]);
Z* output2 = &(outputZ[movement.Second()]);
movement.increment();
const X* buffer3 = &(bufferX[movement.First()]);
Z* output3 = &(outputZ[movement.Second()]);
movement.increment();
DeviationOp::template reduceConstRankLoop4b<3, LastIndexFaster>(
buffer0, buffer1, buffer2, buffer3, output0, output1, output2, output3, inner_bases, inner_strides,
inner_loop, inner_last, inner_stride, biasCorrected);
}
for (sd::LongType i = 0; i < loopTotalTail; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
DeviationOp::template reduceConstRankLoop1b<3, LastIndexFaster>(buffer0, &(outputZ[movement.Second()]),
inner_bases, inner_strides, inner_loop,
inner_last, inner_stride, biasCorrected);
movement.increment();
}
} else {
LOG_CALLS(23)
AggType agg;
for (sd::LongType i = 0; i < loopTotal; i++) {
const X* buffer0 = &(bufferX[movement.First()]);
DeviationOp::template updateGeneralLoop1b<LastIndexFaster>(
second_rank, buffer0, agg, inner_bases, inner_strides, 0, inner_loop, inner_last, inner_stride);
outputZ[movement.Second()] = DeviationOp::getDeviation(agg, biasCorrected);
movement.increment();
}
}
}
}
}
template <typename X, typename Z, typename DeviationOp, bool LastIndexFaster = true>
static void reductionCaseNonScalar(const int& first_rank, const int& output_rank, bool squashed, const int& second_rank,
const sd::LongType*& outer_bases, const sd::LongType* outer_strides,
const sd::LongType* output_strides, const sd::LongType& output_stride,
const sd::LongType*& inner_bases, const sd::LongType* inner_strides,
const X* bufferX, Z* outputZ, bool biasCorrected) {
sd::LongType total = getLength<LastIndexFaster>(outer_bases, first_rank);
sd::LongType inner_stride = LastIndexFaster ? inner_strides[second_rank - 1] : inner_strides[0];
sd::LongType outer_stride = LastIndexFaster ? outer_strides[second_rank - 1] : outer_strides[0];
auto func = [first_rank, output_rank, squashed, outer_bases, outer_strides, output_strides, output_stride,
second_rank, inner_bases, inner_strides, bufferX, outputZ,
biasCorrected](uint64_t thread_id, int64_t start, int64_t stop, int64_t increment) -> void {
sd::LongType loopTotal = stop - start;
sd::LongType stride = LastIndexFaster ? outer_strides[first_rank - 1] : outer_strides[0];
if (first_rank == 1) {
if (stride == 1) {
ZipGenericCoordsRank1Stride1 movement;
movement.init(nullptr, nullptr, nullptr, 0, start);
reductionCases<X, Z, DeviationOp, LastIndexFaster>(movement, loopTotal, second_rank, inner_bases, inner_strides,
bufferX, outputZ, biasCorrected);
} else {
ZipGenericCoordsRank1BothStrideN movement;
movement.init(nullptr, &stride, &output_stride, 0, start);
reductionCases<X, Z, DeviationOp, LastIndexFaster>(movement, loopTotal, second_rank, inner_bases, inner_strides,
bufferX, outputZ, biasCorrected);
}
} else if (squashed && first_rank <= output_rank) {
if (first_rank == 2) {
if (output_stride == 1) {
ZipGenericCoordsConstMovementSecondStride1<2, LastIndexFaster> movement;
movement.init(outer_bases, outer_strides, nullptr, first_rank, start);
reductionCases<X, Z, DeviationOp, LastIndexFaster>(movement, loopTotal, second_rank, inner_bases,
inner_strides, bufferX, outputZ, biasCorrected);
} else {
ZipGenericCoordsConstMovementSecondStrideN<2, LastIndexFaster> movement;
movement.init(outer_bases, outer_strides, &output_stride, first_rank, start);
reductionCases<X, Z, DeviationOp, LastIndexFaster>(movement, loopTotal, second_rank, inner_bases,
inner_strides, bufferX, outputZ, biasCorrected);
}
} else if (first_rank == 3) {
if (output_stride == 1) {
ZipGenericCoordsConstMovementSecondStride1<3, LastIndexFaster> movement;
movement.init(outer_bases, outer_strides, nullptr, first_rank, start);
reductionCases<X, Z, DeviationOp, LastIndexFaster>(movement, loopTotal, second_rank, inner_bases,
inner_strides, bufferX, outputZ, biasCorrected);
} else {
ZipGenericCoordsConstMovementSecondStrideN<3, LastIndexFaster> movement;
movement.init(outer_bases, outer_strides, &output_stride, first_rank, start);
reductionCases<X, Z, DeviationOp, LastIndexFaster>(movement, loopTotal, second_rank, inner_bases,
inner_strides, bufferX, outputZ, biasCorrected);
}
} else {
ZipGenericCoordsMovementSecondStrideN<LastIndexFaster> movement;
movement.init(outer_bases, outer_strides, &output_stride, first_rank, start);
reductionCases<X, Z, DeviationOp, LastIndexFaster>(movement, loopTotal, second_rank, inner_bases, inner_strides,
bufferX, outputZ, biasCorrected);
}
} else {
ZipGenericCoordsMovement<LastIndexFaster> movement;
movement.init(outer_bases, outer_strides, output_strides, first_rank, start);
reductionCases<X, Z, DeviationOp, LastIndexFaster>(movement, loopTotal, second_rank, inner_bases, inner_strides,
bufferX, outputZ, biasCorrected);
}
};
#if 0
func(0, 0, total, 1);
#else
//
uint32_t numThreads = sd::Environment::getInstance().maxMasterThreads();
sd::LongType inner_total = getLength<LastIndexFaster>(inner_bases, second_rank);
if (total * inner_total <= threadingThreshold) {
numThreads = 1;
} else {
if (inner_stride > outer_stride && total <= 1024) {
auto desired = total > 4 ? (total / 4) : 1;
numThreads = numThreads > desired ? desired : numThreads;
}
}
samediff::Threads::parallel_tad(func, 0, total, 1, numThreads);
#endif
}
template <typename X, typename Z, typename DeviationOp>
static void reduction_(NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions, bool biasCorrected) {
char input_order = input.ordering();
bool try_squash_outer = (input_order == output.ordering()) && output.ews() != 0;
auto input_shapeInfo = input.shapeInfo();
auto output_shapeInfo = output.shapeInfo();
const sd::LongType rank = input_shapeInfo[0];
const sd::LongType* input_bases = &(input_shapeInfo[1]);
const sd::LongType* input_strides = &(input_shapeInfo[rank + 1]);
const sd::LongType output_rank = output_shapeInfo[0];
const sd::LongType* output_strides = &(output_shapeInfo[output_rank + 1]);
sd::LongType new_bases[SD_MAX_RANK];
sd::LongType new_strides[SD_MAX_RANK];
sd::LongType first_begin, first_end, second_begin, second_end;
// rePartition into two parts based on the selection
rePartition(input_order, dimensions, rank, input_bases, input_strides, new_bases, new_strides, first_begin, first_end,
second_begin, second_end, try_squash_outer, true);
int first_rank = first_end - first_begin; // the first rank can be 0 for scalar cases
int second_rank = second_end - second_begin;
auto bufferX = input.bufferAsT<X>();
auto outputZ = output.bufferAsT<Z>();
const sd::LongType* outer_bases = &(new_bases[first_begin]);
const sd::LongType* outer_strides = &(new_strides[first_begin]);
const sd::LongType* inner_bases = &(new_bases[second_begin]);
const sd::LongType* inner_strides = &(new_strides[second_begin]);
const sd::LongType output_stride = output.ordering() == 'c' ? output_strides[output_rank - 1] : output_strides[0];
if (input_order == 'c') {
if (first_rank == 0) {
reductionCase1Scalar<X, Z, DeviationOp>(second_rank, inner_bases, inner_strides, bufferX, outputZ, biasCorrected);
} else {
reductionCaseNonScalar<X, Z, DeviationOp>(first_rank, output_rank, try_squash_outer, second_rank, outer_bases,
outer_strides, output_strides, output_stride, inner_bases,
inner_strides, bufferX, outputZ, biasCorrected);
}
} else {
if (first_rank == 0) {
LOG_CALLS(100);
reductionCase1Scalar<X, Z, DeviationOp, false>(second_rank, inner_bases, inner_strides, bufferX, outputZ,
biasCorrected);
} else {
LOG_CALLS(101);
reductionCaseNonScalar<X, Z, DeviationOp, false>(first_rank, output_rank, try_squash_outer, second_rank,
outer_bases, outer_strides, output_strides, output_stride,
inner_bases, inner_strides, bufferX, outputZ, biasCorrected);
}
}
}
template <typename X, typename Z>
SD_LIB_HIDDEN void variance_(NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions,
bool biasCorrected) {
return reduction_<X, Z, Deviation<X, Z>>(input, output, dimensions, biasCorrected);
}
template <typename X, typename Z>
SD_LIB_HIDDEN void standardDeviation_(NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions,
bool biasCorrected) {
return reduction_<X, Z, Deviation<X, Z, true>>(input, output, dimensions, biasCorrected);
}
} // namespace helpers
} // namespace ops
} // namespace sd