chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 George A. Shulinok <sgazeos@gmail.com>, created on 4/18/2019
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/BarnesHutTsne.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
sd::LongType barnes_row_count(NDArray* rowP, NDArray* colP, sd::LongType N, NDArray& rowCounts) {
|
||||
int* pRowCounts = reinterpret_cast<int*>(rowCounts.buffer());
|
||||
int const* pRows = reinterpret_cast<int const*>(rowP->buffer());
|
||||
int const* pCols = reinterpret_cast<int const*>(colP->buffer());
|
||||
for (sd::LongType n = 0; n < N; n++) {
|
||||
int begin = pRows[n]; //->e<int>(n);
|
||||
int end = pRows[n + 1]; // rowP->e<int>(n + 1);
|
||||
for (int i = begin; i < end; i++) {
|
||||
bool present = false;
|
||||
for (int m = pRows[pCols[i]]; m < pRows[pCols[i] + 1]; m++)
|
||||
if (pCols[m] == n) {
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++pRowCounts[n];
|
||||
|
||||
if (!present) ++pRowCounts[pCols[i]];
|
||||
}
|
||||
}
|
||||
NDArray numElementsArr = rowCounts.sumNumber();
|
||||
auto numElements = numElementsArr.e<sd::LongType>(0);
|
||||
return numElements;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void barnes_symmetrize_(NDArray* rowP, NDArray* colP, NDArray* valP, sd::LongType N,
|
||||
NDArray* outputRows, NDArray* outputCols, NDArray* outputVals, NDArray* rowCounts) {
|
||||
|
||||
int const* pRows = reinterpret_cast<int const*>(rowP->buffer());
|
||||
int* symRowP = reinterpret_cast<int*>(outputRows->buffer());
|
||||
symRowP[0] = 0;
|
||||
for (sd::LongType n = 0; n < N; n++) symRowP[n + 1] = symRowP[n] + rowCounts->e<int>(n);
|
||||
|
||||
int* symColP = reinterpret_cast<int*>(outputCols->buffer());
|
||||
int const* pCols = reinterpret_cast<int const*>(colP->buffer());
|
||||
T const* pVals = reinterpret_cast<T const*>(valP->buffer());
|
||||
T* pOutput = reinterpret_cast<T*>(outputVals->buffer());
|
||||
std::vector<int> offset(N);
|
||||
|
||||
for (sd::LongType n = 0; n < N; n++) {
|
||||
int begin = pRows[n];
|
||||
int bound = pRows[n + 1];
|
||||
|
||||
for (int i = begin; i < bound; i++) {
|
||||
bool present = false;
|
||||
int colPI = pCols[i];
|
||||
int start = pRows[colPI];
|
||||
int end = pRows[colPI + 1];
|
||||
|
||||
for (int m = start; m < end; m++) {
|
||||
if (pCols[m] == n) {
|
||||
present = true;
|
||||
if (n <= colPI) {
|
||||
symColP[symRowP[n] + offset[n]] = colPI;
|
||||
symColP[symRowP[colPI] + offset[colPI]] = n;
|
||||
pOutput[symRowP[n] + offset[n]] = pVals[i] + pVals[m];
|
||||
pOutput[symRowP[colPI] + offset[colPI]] = pVals[i] + pVals[m];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!present) {
|
||||
symColP[symRowP[n] + offset[n]] = colPI;
|
||||
symColP[symRowP[pCols[i]] + offset[colPI]] = n;
|
||||
pOutput[symRowP[n] + offset[n]] = pVals[i];
|
||||
pOutput[symRowP[colPI] + offset[colPI]] = pVals[i];
|
||||
//}
|
||||
}
|
||||
// Update offsets
|
||||
if (!present || (present && n <= colPI)) {
|
||||
++offset[n];
|
||||
|
||||
if (colPI != n) ++offset[colPI];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void barnes_symmetrize(NDArray* rowP, NDArray* colP, NDArray* valP, sd::LongType N,
|
||||
NDArray* outputRows, NDArray* outputCols, NDArray* outputVals, NDArray* rowCounts) {
|
||||
// Divide the result by two
|
||||
BUILD_SINGLE_SELECTOR(valP->dataType(), barnes_symmetrize_,
|
||||
(rowP, colP, valP, N, outputRows, outputCols, outputVals, rowCounts), SD_NUMERIC_TYPES);
|
||||
|
||||
*outputVals /= 2.0;
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void barnes_symmetrize_,
|
||||
(NDArray* rowP, NDArray* colP, NDArray* valP, sd::LongType N,
|
||||
NDArray* outputRows, NDArray* outputCols, NDArray* outputVals, NDArray* rowCounts),
|
||||
SD_NUMERIC_TYPES);
|
||||
|
||||
template <typename T>
|
||||
static void barnes_edge_forces_(NDArray* rowP, NDArray * colP, NDArray * valP, int N,
|
||||
NDArray * data, NDArray* output) {
|
||||
T const* dataP = reinterpret_cast<T const*>(data->buffer());
|
||||
T const* vals = reinterpret_cast<T const*>(valP->buffer());
|
||||
T* outputP = reinterpret_cast<T*>(output->buffer());
|
||||
int colCount = data->columns();
|
||||
|
||||
auto rowSize = sizeof(T) * colCount;
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto n = start; n < stop; n++) {
|
||||
int s = rowP->e<int>(n);
|
||||
int end = rowP->e<int>(n + 1);
|
||||
int shift = n * colCount;
|
||||
for (int i = s; i < end; i++) {
|
||||
T const* thisSlice = dataP + colP->e<int>(i) * colCount;
|
||||
T res = static_cast<T>(1);
|
||||
|
||||
for (int k = 0; k < colCount; k++) {
|
||||
auto tempVal = dataP[shift + k] - thisSlice[k]; // thisSlice[k];
|
||||
res += tempVal * tempVal;
|
||||
}
|
||||
|
||||
res = vals[i] / res;
|
||||
for (int k = 0; k < colCount; k++) outputP[shift + k] += ((dataP[shift + k] - thisSlice[k]) * res);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, N);
|
||||
}
|
||||
|
||||
void barnes_edge_forces(NDArray* rowP, NDArray * colP, NDArray * valP, int N, NDArray* output,
|
||||
NDArray& data) {
|
||||
// Loop over all edges in the graph
|
||||
BUILD_SINGLE_SELECTOR(output->dataType(), barnes_edge_forces_, (rowP, colP, valP, N, &data, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void barnes_edge_forces_,
|
||||
(NDArray* rowP, NDArray * colP, NDArray * valP, int N, NDArray * data,
|
||||
NDArray* output),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
template <typename T>
|
||||
static void barnes_gains_(NDArray* input, NDArray* gradX, NDArray* epsilon, NDArray* output) {
|
||||
auto gainsInternal = LAMBDA_TTT(x, grad, eps) {
|
||||
T res = sd::math::sd_sign<T, T>(grad) != sd::math::sd_sign<T, T>(eps) ? x + T(.2) : x * T(.8);
|
||||
if (res < .01) res = static_cast<T>(.01);
|
||||
return res;
|
||||
});
|
||||
|
||||
input->applyTriplewiseLambda<T>(gradX, epsilon, gainsInternal, output);
|
||||
}
|
||||
|
||||
void barnes_gains(NDArray* input, NDArray* gradX, NDArray* epsilon, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), barnes_gains_, (input, gradX, epsilon, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void barnes_gains_, (NDArray * input, NDArray* gradX, NDArray* epsilon, NDArray* output),
|
||||
SD_NUMERIC_TYPES);
|
||||
|
||||
bool cell_contains(NDArray* corner, NDArray* width, NDArray* point, sd::LongType dimension) {
|
||||
auto cornerMinusWidth = *corner - *width;
|
||||
auto cornerPlusWidth = *corner + *width;
|
||||
|
||||
bool result = true;
|
||||
for (sd::LongType i = 0; i < dimension && result; i++) {
|
||||
if (cornerMinusWidth->e<double>(i) > point->e<double>(i)) result = false;
|
||||
else if (cornerPlusWidth->e<double>(i) < point->e<double>(i)) result = false;
|
||||
}
|
||||
|
||||
delete cornerPlusWidth;
|
||||
delete cornerMinusWidth;
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1 @@
|
||||
This folder contains OpenMP implementations for operations helpers. Basically suited for homogenous x86-like platforms.
|
||||
@@ -0,0 +1,272 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 19.04.2018
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/activations.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void static _softMaxDerivForVector(sd::LaunchContext* context, const void* input, const sd::LongType* inShapeInfo,
|
||||
void* output) {
|
||||
const T* inBuff = reinterpret_cast<const T*>(input);
|
||||
T* outBuff = reinterpret_cast<T*>(output);
|
||||
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
T sum = static_cast<T>(0.);
|
||||
const sd::LongType length = shape::length(inShapeInfo);
|
||||
|
||||
const sd::LongType rank = shape::rank(inShapeInfo);
|
||||
const sd::LongType* shape = shape::shapeOf(inShapeInfo);
|
||||
const sd::LongType* stride = shape::stride(inShapeInfo);
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
LongType offset;
|
||||
|
||||
// Find the maximum value in the vector
|
||||
for (sd::LongType i = 0; i < length; i++) {
|
||||
INDEX2COORDS(i, rank, shape, coords);
|
||||
COORDS2INDEX(rank, stride, coords, offset);
|
||||
max = sd::math::sd_max<T>(max, inBuff[offset]);
|
||||
}
|
||||
|
||||
// Calculate exponentials and sum
|
||||
for (sd::LongType i = 0; i < length; i++) {
|
||||
INDEX2COORDS(i, rank, shape, coords);
|
||||
COORDS2INDEX(rank, stride, coords, offset);
|
||||
outBuff[offset] = sd::math::sd_exp<T, T>(inBuff[offset] - max);
|
||||
sum += outBuff[offset];
|
||||
}
|
||||
|
||||
// Compute softmax derivatives
|
||||
for (sd::LongType i = 0; i < length; i++) {
|
||||
INDEX2COORDS(i, rank, shape, coords);
|
||||
COORDS2INDEX(rank, stride, coords, offset);
|
||||
outBuff[offset] /= sum;
|
||||
outBuff[offset] *= (1.f - outBuff[offset]); // derivative
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void softmaxDerivative(sd::LaunchContext* context, NDArray& input, NDArray& output, const int dimension) {
|
||||
const int rank = input.rankOf();
|
||||
sd::LongType temp;
|
||||
|
||||
if (shape::isCommonVector(input.shapeInfo(), temp)) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), _softMaxDerivForVector,
|
||||
(context, input.buffer(), input.shapeInfo(), output.buffer()), SD_FLOAT_TYPES);
|
||||
} else {
|
||||
std::vector<sd::LongType> dimVec = {dimension};
|
||||
auto maxAlongDim = const_cast<NDArray&>(input).reduceAlongDimension(reduce::Max, &dimVec, true);
|
||||
auto minus = (input - *maxAlongDim);
|
||||
minus->applyTransform(transform::Exp, &output); // output contains exponents temporarily
|
||||
auto sumAlongDim = output.reduceAlongDimension(reduce::Sum, &dimVec, true);
|
||||
output /= *sumAlongDim;
|
||||
auto oneMinus = (1.f - output);
|
||||
output *= *oneMinus; // derivative
|
||||
delete sumAlongDim;
|
||||
delete minus;
|
||||
delete oneMinus;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void logSoftMaxForVector_(void const* input, sd::LongType const* inShapeInfo, void* output,
|
||||
sd::LongType const* outShapeInfo) {
|
||||
auto inBuff = reinterpret_cast<T const*>(input);
|
||||
auto outBuff = reinterpret_cast<T*>(output);
|
||||
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
T sum = static_cast<T>(0);
|
||||
|
||||
auto length = shape::length(inShapeInfo);
|
||||
sd::LongType inRank = shape::rank(inShapeInfo);
|
||||
sd::LongType *inShape = shape::shapeOf(inShapeInfo);
|
||||
sd::LongType *inStrides = shape::stride(inShapeInfo);
|
||||
|
||||
sd::LongType *outShape = shape::shapeOf(outShapeInfo);
|
||||
sd::LongType *outStrides = shape::stride(outShapeInfo);
|
||||
sd::LongType outRank = shape::rank(outShapeInfo);
|
||||
sd::LongType inIndices[length];
|
||||
sd::LongType outIndices[length];
|
||||
PRAGMA_OMP_SIMD
|
||||
for (sd::LongType i2 = 0; i2 < length; i2++) {
|
||||
LongType coords[SD_MAX_RANK];
|
||||
sd::LongType idx2;
|
||||
INDEX2COORDS(i2,inRank, inShape, coords);
|
||||
COORDS2INDEX(inRank, inStrides, coords, idx2);
|
||||
max = sd::math::sd_max<T,T>(max, inBuff[idx2]);
|
||||
inIndices[i2] = idx2;
|
||||
}
|
||||
|
||||
PRAGMA_OMP_SIMD
|
||||
for (sd::LongType i2 = 0; i2 < length; i2++) {
|
||||
LongType coords[SD_MAX_RANK];
|
||||
sd::LongType idx2;
|
||||
INDEX2COORDS(i2,outRank, outShape, coords);
|
||||
COORDS2INDEX(outRank, outStrides, coords, idx2);
|
||||
outBuff[idx2] = sd::math::sd_exp<T, T>(inBuff[inIndices[i2]] - max);
|
||||
sum += outBuff[idx2];
|
||||
}
|
||||
|
||||
PRAGMA_OMP_SIMD
|
||||
for (sd::LongType i = 0; i < length; i++) {
|
||||
outBuff[outIndices[i]] /= sum;
|
||||
outBuff[outIndices[i]] = sd::math::sd_log<T, T>(outBuff[outIndices[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void logSoftMaxForVector(sd::LaunchContext* context, NDArray& input, NDArray& output) {
|
||||
if (!input.isVector() || !output.isVector())
|
||||
THROW_EXCEPTION("ops::helpers::logSoftMaxForVector function input and output arrays must be vectors !");
|
||||
|
||||
auto xType = input.dataType();
|
||||
BUILD_SINGLE_SELECTOR(xType, logSoftMaxForVector_,
|
||||
(input.buffer(), input.shapeInfo(), output.buffer(), output.shapeInfo()), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void prelu(LaunchContext* context, NDArray* input, NDArray* alpha, NDArray* output) {
|
||||
const sd::LongType inputLen = input->lengthOf();
|
||||
const sd::LongType* inputShapeInfo = input->shapeInfo();
|
||||
const sd::LongType* alphaShapeInfo = alpha->shapeInfo();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType i = start; i < stop; i++) {
|
||||
// FIXME: double!
|
||||
double x = input->e<double>(i);
|
||||
if (x < 0.0) {
|
||||
// FIXME: double
|
||||
output->p(i, (x * alpha->e<double>(shape::subArrayIndex(i, inputShapeInfo, alphaShapeInfo))));
|
||||
} else
|
||||
output->p(i, x);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, inputLen);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void preluBP(LaunchContext* context, NDArray* input, NDArray* alpha, NDArray* dLdO, NDArray* dLdI,
|
||||
NDArray* dLdA) {
|
||||
const sd::LongType inputLen = input->lengthOf();
|
||||
const sd::LongType* inputShapeInfo = input->shapeInfo();
|
||||
const sd::LongType* alphaShapeInfo = alpha->shapeInfo();
|
||||
float zero = 0.f;
|
||||
dLdA->assign(zero);
|
||||
|
||||
for (sd::LongType i = 0; i < inputLen; ++i) {
|
||||
// FIXME: double
|
||||
double x = input->e<double>(i);
|
||||
double grO = dLdO->isScalar() ? dLdO->e<double>(0) : dLdO->e<double>(i);
|
||||
if (x < 0.0) {
|
||||
sd::LongType alphaInd = shape::subArrayIndex(i, inputShapeInfo, alphaShapeInfo);
|
||||
dLdI->p(i, grO * alpha->e<double>(alphaInd));
|
||||
double prevVal = dLdA->e<double>(alphaInd);
|
||||
prevVal += (grO * x);
|
||||
dLdA->p(alphaInd, prevVal);
|
||||
} else
|
||||
dLdI->p(i, grO);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkAlphaShapeLen(std::vector<sd::LongType> const& expectedShape, sd::LongType shapeLen) {
|
||||
sd::LongType expectedAlphaLen =
|
||||
std::accumulate(expectedShape.cbegin(), expectedShape.cend(), 1, std::multiplies<sd::LongType>());
|
||||
return expectedAlphaLen == shapeLen;
|
||||
}
|
||||
template <typename T>
|
||||
static void thresholdRelu_(NDArray *input, double threshold, NDArray* output) {
|
||||
auto routine = LAMBDA_T(_x, threshold) { return _x > (T)threshold ? _x : (T)0.f; });
|
||||
input->applyLambda<T>(routine, output);
|
||||
}
|
||||
|
||||
void thresholdRelu(LaunchContext* context, NDArray* input, double threshold, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), thresholdRelu_, (input, threshold, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void thresholdReluDerivative_(sd::LaunchContext* context, NDArray* input, double theta, NDArray* dLdO,
|
||||
NDArray* output) {
|
||||
auto derivative = LAMBDA_TT(_x, grO, theta) {
|
||||
if (_x > theta)
|
||||
return grO;
|
||||
else
|
||||
return static_cast<T>(0);
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda<T>(dLdO, derivative, output);
|
||||
}
|
||||
|
||||
void thresholdReluDerivative(sd::LaunchContext* context, NDArray* input, double threshold, NDArray* dLdO,
|
||||
NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), thresholdReluDerivative_, (context, input, threshold, dLdO, output),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void logSoftmax(LaunchContext* context, NDArray* input, NDArray* output, const int dimension) {
|
||||
const int rank = input->rankOf();
|
||||
|
||||
if (input->isVector()) {
|
||||
if (rank == 1 || input->sizeAt(dimension) != 1) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), logSoftMaxForVector_,
|
||||
(input->buffer(), input->shapeInfo(), output->buffer(), output->shapeInfo()), SD_FLOAT_TYPES);
|
||||
} else
|
||||
*output = 0.;
|
||||
} else {
|
||||
std::vector<sd::LongType> dimVector = {dimension};
|
||||
auto maxAlongDim = input->reduceAlongDimension(reduce::Max, &dimVector, true);
|
||||
auto maxMinusDim = *input - *maxAlongDim;
|
||||
maxMinusDim->applyTransform(transform::Exp, output); // output contains exponents temporarily
|
||||
auto sumAlongDim = output->reduceAlongDimension(reduce::Sum, &dimVector, true);
|
||||
*output /= *sumAlongDim;
|
||||
output->applyTransform(transform::Log, output);
|
||||
delete maxAlongDim;
|
||||
delete maxMinusDim;
|
||||
delete sumAlongDim;
|
||||
}
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void thresholdReluDerivative_,
|
||||
(sd::LaunchContext * context, NDArray* input, double threshold, NDArray* dLdO, NDArray* output),
|
||||
SD_FLOAT_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void logSoftMaxForVector_,
|
||||
(void const* input, sd::LongType const* inShapeInfo, void* output,
|
||||
sd::LongType const* outShapeInfo),
|
||||
SD_FLOAT_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void _softMaxDerivForVector,
|
||||
(sd::LaunchContext * context, const void* input, const sd::LongType* inShapeInfo, void* output),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <ops/declarable/helpers/adjust_hue.h>
|
||||
#if NOT_EXCLUDED(OP_adjust_hue)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void adjustHue_(NDArray *input, NDArray *deltaScalarArr, NDArray *output, const sd::LongType dimC) {
|
||||
const T delta = deltaScalarArr->e<T>(0);
|
||||
const int rank = input->rankOf();
|
||||
|
||||
const T *x = input->bufferAsT<T>();
|
||||
T *z = output->bufferAsT<T>();
|
||||
|
||||
if (dimC == rank - 1 && input->ews() == 1 && output->ews() == 1 && input->ordering() == 'c' &&
|
||||
output->ordering() == 'c') {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
T h, s, v;
|
||||
|
||||
rgbToHsv<T>(x[i], x[i + 1], x[i + 2], h, s, v);
|
||||
|
||||
h += delta;
|
||||
if (h > (T)1)
|
||||
h -= (T)1;
|
||||
else if (h < 0)
|
||||
h += (T)1;
|
||||
|
||||
hsvToRgb<T>(h, s, v, z[i], z[i + 1], z[i + 2]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input->lengthOf(), 3);
|
||||
} else {
|
||||
auto packX = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimC);
|
||||
auto packZ = sd::ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimC);
|
||||
|
||||
const sd::LongType numOfTads = packX->numberOfTads();
|
||||
const sd::LongType xDimCstride = input->stridesOf()[dimC];
|
||||
const sd::LongType zDimCstride = output->stridesOf()[dimC];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
const T *xTad = x + packX->platformOffsets()[i];
|
||||
T *zTad = z + packZ->platformOffsets()[i];
|
||||
|
||||
T h, s, v;
|
||||
|
||||
rgbToHsv<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], h, s, v);
|
||||
|
||||
h += delta;
|
||||
if (h > (T)1)
|
||||
h -= (T)1;
|
||||
else if (h < 0)
|
||||
h += (T)1;
|
||||
|
||||
hsvToRgb<T>(h, s, v, zTad[0], zTad[zDimCstride], zTad[2 * zDimCstride]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
}
|
||||
}
|
||||
|
||||
void adjustHue(sd::LaunchContext *context, NDArray *input, NDArray *deltaScalarArr, NDArray *output,
|
||||
const sd::LongType dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), adjustHue_, (input, deltaScalarArr, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <ops/declarable/helpers/adjust_hue.h>
|
||||
#include <ops/declarable/helpers/adjust_saturation.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void adjustSaturation_(NDArray *input, NDArray *factorScalarArr, NDArray *output, const sd::LongType dimC) {
|
||||
const T factor = factorScalarArr->e<T>(0);
|
||||
const int rank = input->rankOf();
|
||||
|
||||
const T *x = input->bufferAsT<T>();
|
||||
T *z = output->bufferAsT<T>();
|
||||
|
||||
if (dimC == rank - 1 && input->ews() == 1 && output->ews() == 1 && input->ordering() == 'c' &&
|
||||
output->ordering() == 'c') {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
T h, s, v;
|
||||
|
||||
rgbToHsv<T>(x[i], x[i + 1], x[i + 2], h, s, v);
|
||||
|
||||
s *= factor;
|
||||
if (s > 1.f)
|
||||
s = 1.f;
|
||||
else if (s < 0.f)
|
||||
s = 0.f;
|
||||
|
||||
hsvToRgb<T>(h, s, v, z[i], z[i + 1], z[i + 2]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input->lengthOf(), 3);
|
||||
} else {
|
||||
auto packX = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimC);
|
||||
auto packZ = sd::ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimC);
|
||||
|
||||
const sd::LongType numOfTads = packX->numberOfTads();
|
||||
const sd::LongType xDimCstride = input->stridesOf()[dimC];
|
||||
const sd::LongType zDimCstride = output->stridesOf()[dimC];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
const T *xTad = x + packX->platformOffsets()[i];
|
||||
T *zTad = z + packZ->platformOffsets()[i];
|
||||
|
||||
T h, s, v;
|
||||
|
||||
rgbToHsv<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], h, s, v);
|
||||
|
||||
s *= factor;
|
||||
if (s > 1.f)
|
||||
s = 1.f;
|
||||
else if (s < 0.f)
|
||||
s = 0.f;
|
||||
|
||||
hsvToRgb<T>(h, s, v, zTad[0], zTad[zDimCstride], zTad[2 * zDimCstride]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
}
|
||||
}
|
||||
|
||||
void adjustSaturation(sd::LaunchContext *context, NDArray *input, NDArray *factorScalarArr, NDArray *output,
|
||||
const sd::LongType dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), adjustSaturation_, (input, factorScalarArr, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,87 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// CPU implementation of assign helper
|
||||
//
|
||||
|
||||
#include <ops/declarable/helpers/assign.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void assignImpl_(NDArray* source, NDArray* target) {
|
||||
auto xBuffer = source->bufferAsT<X>();
|
||||
auto zBuffer = target->bufferAsT<Z>();
|
||||
|
||||
auto xShapeInfo = source->shapeInfo();
|
||||
auto zShapeInfo = target->shapeInfo();
|
||||
|
||||
const int xRank = shape::rank(xShapeInfo);
|
||||
const int zRank = shape::rank(zShapeInfo);
|
||||
const sd::LongType* xShape = shape::shapeOf(xShapeInfo);
|
||||
const sd::LongType* zShape = shape::shapeOf(zShapeInfo);
|
||||
const sd::LongType* xStride = shape::stride(xShapeInfo);
|
||||
const sd::LongType* zStride = shape::stride(zShapeInfo);
|
||||
const sd::LongType len = target->lengthOf();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
sd::LongType xCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
sd::LongType xOffset, zOffset;
|
||||
|
||||
INDEX2COORDS(i, zRank, zShape, zCoords);
|
||||
INDEX2COORDS(i, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
|
||||
zBuffer[zOffset] = static_cast<Z>(xBuffer[xOffset]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, len);
|
||||
}
|
||||
|
||||
void assign(sd::LaunchContext* context, sd::NDArray* target, sd::NDArray* source) {
|
||||
if (target->lengthOf() != source->lengthOf()) {
|
||||
std::string errorMsg = "assign helper: Source and target arrays must have the same length. ";
|
||||
errorMsg += "Source shape: " + ShapeUtils::shapeAsString(source) + ", ";
|
||||
errorMsg += "Target shape: " + ShapeUtils::shapeAsString(target) + ", ";
|
||||
errorMsg += "Source datatype: " + DataTypeUtils::asString(source->dataType()) + ", ";
|
||||
errorMsg += "Target datatype: " + DataTypeUtils::asString(target->dataType());
|
||||
THROW_EXCEPTION(errorMsg.c_str());
|
||||
}
|
||||
|
||||
NDArray::prepareSpecialUse({target}, {source});
|
||||
|
||||
auto xType = source->dataType();
|
||||
auto zType = target->dataType();
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(xType, zType, assignImpl_, (source, target), SD_COMMON_TYPES, SD_COMMON_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({target}, {source});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,57 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/axis.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
void adjustAxis(sd::LongType rank, NDArray* axisVector, std::vector<LongType>& output) {
|
||||
if(axisVector->isScalar()) {
|
||||
output.resize(1);
|
||||
auto ca = axisVector->e<sd::LongType>(0);
|
||||
if (ca < 0) // shift values on rank for negative vals
|
||||
ca += rank;
|
||||
output[0] = ca;
|
||||
return;
|
||||
}
|
||||
output.resize(axisVector->lengthOf());
|
||||
axisVector->tickReadDevice(); // mark input as read on device
|
||||
axisVector->syncToHost(); // sync to host
|
||||
for (int e = 0; e < axisVector->lengthOf(); e++) {
|
||||
auto ca = axisVector->e<sd::LongType>(e);
|
||||
if (ca < 0) // shift values on rank for negative vals
|
||||
ca += rank;
|
||||
|
||||
output[e] = ca;
|
||||
}
|
||||
}
|
||||
|
||||
void adjustAxis(sd::LongType rank, std::vector<LongType>& axisVector) {
|
||||
for (size_t e = 0; e < axisVector.size(); e++) {
|
||||
auto a = axisVector[e];
|
||||
if (a < 0) axisVector[e] = a + rank;
|
||||
}
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,202 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/BlasHelper.h>
|
||||
#include <ops/declarable/helpers/batched_gemm.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
#include <types/float16.h>
|
||||
#include <indexing/NDIndexUtils.h>
|
||||
#include <ops/declarable/CustomOperations.h>
|
||||
|
||||
#if NOT_EXCLUDED(OP_batched_gemm)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
|
||||
void bgemm(NDArray *a, NDArray *b, NDArray *c, NDArray *alphas, NDArray *betas,
|
||||
int transA, int transB, int M, int N, int K, int lda, int ldb, int ldc,
|
||||
NDArray *all) {
|
||||
NDArray *allIndex = nullptr;
|
||||
if(all != nullptr)
|
||||
allIndex = all;
|
||||
else {
|
||||
NDArray *allLocal = NDIndexUtils::createAll();
|
||||
allIndex = allLocal;
|
||||
}
|
||||
|
||||
int batchSize = a->sizeAt(0);
|
||||
std::vector<NDArray *>inputs;
|
||||
std::vector<NDArray *> bInputs;
|
||||
std::vector<NDArray *> outputs;
|
||||
|
||||
ops::create_view createView;
|
||||
|
||||
//divide by 2: queries and keys
|
||||
for(int i = 0; i < batchSize; i++) {
|
||||
auto point = NDIndexUtils::createPoint(i);
|
||||
auto aSlice = createView.evaluate({a,point,allIndex,allIndex},{},{});
|
||||
auto bSlice = createView.evaluate({b,point,allIndex,allIndex},{},{});
|
||||
auto outSlice = createView.evaluate({c,point,allIndex,allIndex},{},{});
|
||||
inputs.push_back(aSlice.at(0));
|
||||
bInputs.push_back(bSlice.at(0));
|
||||
outputs.push_back(outSlice.at(0));
|
||||
delete point;
|
||||
}
|
||||
|
||||
delete allIndex;
|
||||
|
||||
|
||||
|
||||
bgemm(inputs, bInputs,outputs,alphas,betas,transA,transB,M,N,K,lda,ldb,ldc);
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void bgemm_( std::vector<NDArray *> &vA, std::vector<NDArray *> &vB, std::vector<NDArray *> &vC,
|
||||
NDArray *alphas, NDArray *betas, int transA, int transB, int M, int N, int K,
|
||||
int lda, int ldb, int ldc) {
|
||||
int batchSize = vA.size();
|
||||
|
||||
|
||||
|
||||
// Use batched BLAS only when: 1) batched GEMM is available AND 2) BLAS is enabled
|
||||
// Previously used || which incorrectly entered BLAS path when BLAS was disabled
|
||||
if (BlasHelper::getInstance().hasBatchedGEMM<T>() && Environment::getInstance().isEnableBlas()) {
|
||||
auto arr = vA.at(0);
|
||||
CBLAS_TRANSPOSE *tA, *tB;
|
||||
int *tM, *tN, *tK, *tldA, *tldB, *tldC, *tsize;
|
||||
// mkl requires mnk etc as arrays, cuda doesn't
|
||||
ALLOCATE(tA, arr->getContext()->getWorkspace(), batchSize, CBLAS_TRANSPOSE);
|
||||
ALLOCATE(tB, arr->getContext()->getWorkspace(), batchSize, CBLAS_TRANSPOSE);
|
||||
ALLOCATE(tM, arr->getContext()->getWorkspace(), batchSize, int);
|
||||
ALLOCATE(tN, arr->getContext()->getWorkspace(), batchSize, int);
|
||||
ALLOCATE(tK, arr->getContext()->getWorkspace(), batchSize, int);
|
||||
ALLOCATE(tldA, arr->getContext()->getWorkspace(), batchSize, int);
|
||||
ALLOCATE(tldB, arr->getContext()->getWorkspace(), batchSize, int);
|
||||
ALLOCATE(tldC, arr->getContext()->getWorkspace(), batchSize, int);
|
||||
ALLOCATE(tsize, arr->getContext()->getWorkspace(), batchSize, int);
|
||||
|
||||
shape::fill(tA, (CBLAS_TRANSPOSE)transA, batchSize);
|
||||
shape::fill(tB, (CBLAS_TRANSPOSE)transB, batchSize);
|
||||
|
||||
shape::fill(tM, M, batchSize);
|
||||
shape::fill(tN, N, batchSize);
|
||||
shape::fill(tK, K, batchSize);
|
||||
shape::fill(tldA, lda, batchSize);
|
||||
shape::fill(tldB, ldb, batchSize);
|
||||
shape::fill(tldC, ldc, batchSize);
|
||||
shape::fill(tsize, 1, batchSize);
|
||||
|
||||
std::vector<T *> buffersA;
|
||||
std::vector<T *> buffersB;
|
||||
std::vector<T *> buffersC;
|
||||
|
||||
|
||||
|
||||
for (int e = 0; e < batchSize; e++) {
|
||||
buffersA.push_back(reinterpret_cast<T *>(vA[e]->buffer()));
|
||||
buffersB.push_back(reinterpret_cast<T *>(vB[e]->buffer()));
|
||||
buffersC.push_back(reinterpret_cast<T *>(vC[e]->buffer()));
|
||||
}
|
||||
|
||||
// Acquire BLAS lock to prevent OpenBLAS TLS corruption and race conditions
|
||||
auto blasLock = BlasHelper::getInstance().lockBlas();
|
||||
|
||||
// Inside BLAS block, only check type - BLAS enablement was already verified in outer condition
|
||||
if (std::is_same<T, double>::value) {
|
||||
BlasHelper::getInstance().dgemmBatched()(CblasColMajor, tA, tB, tM, tN, tK, (double *)alphas->buffer(),
|
||||
(double **)buffersA.data(), tldA, (double **)buffersB.data(), tldB,
|
||||
(double *)betas->buffer(), (double **)buffersC.data(), tldC, vA.size(),
|
||||
tsize);
|
||||
} else if (std::is_same<T, float>::value) {
|
||||
BlasHelper::getInstance().sgemmBatched()(
|
||||
CblasColMajor, tA, tB, tM, tN, tK, (float *)alphas->buffer(), (float **)buffersA.data(), tldA,
|
||||
(float **)buffersB.data(), tldB, (float *)betas->buffer(), (float **)buffersC.data(), tldC, vA.size(), tsize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// release temporary arrays
|
||||
RELEASE(tA, arr->getContext()->getWorkspace());
|
||||
RELEASE(tB, arr->getContext()->getWorkspace());
|
||||
RELEASE(tM, arr->getContext()->getWorkspace());
|
||||
RELEASE(tN, arr->getContext()->getWorkspace());
|
||||
RELEASE(tK, arr->getContext()->getWorkspace());
|
||||
RELEASE(tldA, arr->getContext()->getWorkspace());
|
||||
RELEASE(tldB, arr->getContext()->getWorkspace());
|
||||
RELEASE(tldC, arr->getContext()->getWorkspace());
|
||||
RELEASE(tsize, arr->getContext()->getWorkspace());
|
||||
} else {
|
||||
|
||||
CBLAS_TRANSPOSE tA = (CBLAS_TRANSPOSE)transA;
|
||||
CBLAS_TRANSPOSE tB = (CBLAS_TRANSPOSE)transB;
|
||||
int vaSize = vA.size();
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto p = start; p < stop; p++) {
|
||||
auto A = reinterpret_cast<T *>(vA.at(p)->buffer());
|
||||
auto B = reinterpret_cast<T *>(vB.at(p)->buffer());
|
||||
auto C = reinterpret_cast<T *>(vC.at(p)->buffer());
|
||||
// Handle scalar, single-element, or empty arrays (use defaults for empty)
|
||||
auto alpha = (alphas->isScalar() || alphas->lengthOf() <= 1)
|
||||
? (alphas->lengthOf() > 0 ? alphas->e<T>(0) : static_cast<T>(1))
|
||||
: alphas->e<T>(p);
|
||||
auto beta = (betas->isScalar() || betas->lengthOf() <= 1)
|
||||
? (betas->lengthOf() > 0 ? betas->e<T>(0) : static_cast<T>(0))
|
||||
: betas->e<T>(p);
|
||||
for (int m = 0; m < M; m++) {
|
||||
for (int n = 0; n < N; n++) {
|
||||
T c_mnp = static_cast<T>(0);
|
||||
PRAGMA_OMP_SIMD
|
||||
for (int k = 0; k < K; k++) {
|
||||
c_mnp += A[tA == CblasNoTrans ? (m + k * lda) : (m * lda + k)] *
|
||||
B[tB == CblasNoTrans ? (k + n * ldb) : (k * ldb + n)];
|
||||
}
|
||||
C[m + n * ldc] = alpha * c_mnp + beta * C[m + n * ldc];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, vaSize);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void bgemm( std::vector<NDArray *> &vA, std::vector<NDArray *> &vB, std::vector<NDArray *> &vC,
|
||||
NDArray *alphas, NDArray *betas, int transA, int transB, int M, int N, int K, int lda,
|
||||
int ldb, int ldc) {
|
||||
auto xType = vA.at(0)->dataType();
|
||||
BUILD_SINGLE_SELECTOR(xType, bgemm_, (vA, vB, vC, alphas, betas, transA, transB, M, N, K, lda, ldb, ldc),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void bgemm_,
|
||||
( std::vector<NDArray *> &vA, std::vector<NDArray *> &vB, std::vector<NDArray *> &vC,
|
||||
NDArray *alphas, NDArray *betas, int transA, int transB, int M, int N, int K,
|
||||
int lda, int ldb, int ldc),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,256 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/OmpLaunchHelper.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/batchnorm.h>
|
||||
|
||||
#if NOT_EXCLUDED(OP_batchnorm)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void batchnorm_(NDArray* input, NDArray* mean, NDArray* variance, NDArray* gamma,
|
||||
NDArray* beta, NDArray* output, const std::vector<LongType>& axes, const double epsilon) {
|
||||
// formula: output = gamma * ((input - mean) / sqrt(variance + epsilon)) + beta
|
||||
|
||||
const T* x = input->bufferAsT<T>();
|
||||
T* z = output->bufferAsT<T>();
|
||||
const T* m = mean->bufferAsT<T>();
|
||||
const T* v = variance->bufferAsT<T>();
|
||||
const T* g = gamma == nullptr ? nullptr : gamma->bufferAsT<T>();
|
||||
const T* b = beta == nullptr ? nullptr : beta->bufferAsT<T>();
|
||||
|
||||
const bool xzSameOffset = shape::haveSameShapeAndStrides(input->shapeInfo(), output->shapeInfo());
|
||||
|
||||
bool paramSameOffset = shape::haveSameShapeAndStrides(mean->shapeInfo(), variance->shapeInfo());
|
||||
if (paramSameOffset && gamma != nullptr)
|
||||
paramSameOffset &= shape::haveSameShapeAndStrides(mean->shapeInfo(), gamma->shapeInfo());
|
||||
if (paramSameOffset && beta != nullptr)
|
||||
paramSameOffset &= shape::haveSameShapeAndStrides(mean->shapeInfo(), beta->shapeInfo());
|
||||
|
||||
const sd::LongType lenBig = input->lengthOf();
|
||||
const sd::LongType lenSmall = mean->lengthOf();
|
||||
|
||||
const sd::LongType steps = lenBig / lenSmall;
|
||||
std::vector<sd::LongType> *dimsToExclude = ShapeUtils::evalDimsToExclude(input->rankOf(), axes.size(),axes.data());
|
||||
|
||||
OmpLaunchHelper info(lenBig, lenSmall);
|
||||
|
||||
auto func = PRAGMA_THREADS_DO {
|
||||
sd::LongType* xOffsets = new sd::LongType[steps];
|
||||
sd::LongType* zOffsets = xzSameOffset ? xOffsets : new sd::LongType[steps];
|
||||
sd::LongType * auxBuff = new sd::LongType [2 * input->rankOf()];
|
||||
|
||||
sd::LongType meanRank = shape::rank(mean->shapeInfo());
|
||||
sd::LongType varianceRank = shape::rank(variance->shapeInfo());
|
||||
sd::LongType gammaRank = gamma == nullptr ? 0 : shape::rank(gamma->shapeInfo());
|
||||
sd::LongType betaRank = beta == nullptr ? 0 : shape::rank(beta->shapeInfo());
|
||||
sd::LongType *meanShape = shape::shapeOf(mean->shapeInfo());
|
||||
sd::LongType *varianceShape = shape::shapeOf(variance->shapeInfo());
|
||||
sd::LongType *gammaShape = gamma == nullptr ? nullptr : shape::shapeOf(gamma->shapeInfo());
|
||||
sd::LongType *betaShape = beta == nullptr ? nullptr : shape::shapeOf(beta->shapeInfo());
|
||||
sd::LongType *meanStride = shape::stride(mean->shapeInfo());
|
||||
sd::LongType *varianceStride = shape::stride(variance->shapeInfo());
|
||||
sd::LongType *gammaStride = gamma == nullptr ? nullptr : shape::stride(gamma->shapeInfo());
|
||||
sd::LongType *betaStride = beta == nullptr ? nullptr : shape::stride(beta->shapeInfo());
|
||||
|
||||
|
||||
for (sd::LongType j = 0; j < lenSmall; ++j) {
|
||||
const bool isOwner = (j < info._numThreads) ? thread_id == j : thread_id == (j % info._numThreads);
|
||||
|
||||
if (!isOwner) continue;
|
||||
|
||||
LongType meanCoords[SD_MAX_RANK];
|
||||
LongType varCoords[SD_MAX_RANK];
|
||||
LongType gammaCoords[SD_MAX_RANK];
|
||||
LongType betaCoords[SD_MAX_RANK];
|
||||
LongType meanOffset;
|
||||
LongType varOffset;
|
||||
LongType gammaOffset;
|
||||
LongType betaOffset;
|
||||
|
||||
INDEX2COORDS(j, meanRank, meanShape, meanCoords);
|
||||
COORDS2INDEX(meanRank, meanStride, meanCoords, meanOffset);
|
||||
varOffset = paramSameOffset ? meanOffset : 0;
|
||||
if (!paramSameOffset) {
|
||||
INDEX2COORDS(j, varianceRank, varianceShape, varCoords);
|
||||
COORDS2INDEX(varianceRank, varianceStride, varCoords, varOffset);
|
||||
}
|
||||
|
||||
const auto meanVal = m[meanOffset];
|
||||
auto sigmaInvGam = static_cast<T>(1) / sd::math::sd_sqrt<T, T>(v[varOffset] + epsilon);
|
||||
|
||||
if (g != nullptr) {
|
||||
gammaOffset = paramSameOffset ? meanOffset : 0;
|
||||
if (!paramSameOffset) {
|
||||
INDEX2COORDS(j, gammaRank, gammaShape, gammaCoords);
|
||||
COORDS2INDEX(gammaRank, gammaStride, gammaCoords, gammaOffset);
|
||||
}
|
||||
sigmaInvGam *= g[gammaOffset];
|
||||
}
|
||||
|
||||
T betaVal = static_cast<T>(0);
|
||||
if (b != nullptr) {
|
||||
betaOffset = paramSameOffset ? meanOffset : 0;
|
||||
if (!paramSameOffset) {
|
||||
INDEX2COORDS(j, betaRank, betaShape, betaCoords);
|
||||
COORDS2INDEX(betaRank, betaStride, betaCoords, betaOffset);
|
||||
}
|
||||
betaVal = b[betaOffset];
|
||||
}
|
||||
|
||||
// calculate offsets for input and output
|
||||
shape::outerArrayOffsets(xOffsets, j, input->shapeInfo(), mean->shapeInfo(), auxBuff, dimsToExclude->data());
|
||||
if (!xzSameOffset)
|
||||
shape::outerArrayOffsets(zOffsets, j, output->shapeInfo(), mean->shapeInfo(), auxBuff, dimsToExclude->data());
|
||||
|
||||
PRAGMA_OMP_SIMD
|
||||
for (sd::LongType i = 0; i < steps; ++i) z[zOffsets[i]] = (x[xOffsets[i]] - meanVal) * sigmaInvGam + betaVal;
|
||||
}
|
||||
|
||||
delete[] auxBuff;
|
||||
delete[] xOffsets;
|
||||
if (!xzSameOffset) delete[] zOffsets;
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_do(func, info._numThreads);
|
||||
|
||||
delete dimsToExclude;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void batchnorm2_(NDArray* input, NDArray* mean, NDArray* variance, NDArray* gamma,
|
||||
NDArray* beta, NDArray* output, const std::vector<int>& axes, const double epsilon) {
|
||||
// formula: output = gamma * ((input - mean) / sqrt(variance + epsilon)) + beta
|
||||
|
||||
const auto x = input->bufferAsT<T>();
|
||||
auto z = output->bufferAsT<T>();
|
||||
const auto m = mean->bufferAsT<T>();
|
||||
const auto v = variance->bufferAsT<T>();
|
||||
const auto g = gamma == nullptr ? nullptr : gamma->bufferAsT<T>();
|
||||
const auto b = beta == nullptr ? nullptr : beta->bufferAsT<T>();
|
||||
|
||||
// xRank == zRank, minRank = meanRank = varianceRank = gammaRank = betaRank
|
||||
const sd::LongType xRank = input->rankOf();
|
||||
const sd::LongType minRank = mean->rankOf();
|
||||
const sd::LongType numAxes = axes.size();
|
||||
|
||||
const bool xzSameOffset = shape::haveSameShapeAndStrides(input->shapeInfo(), output->shapeInfo());
|
||||
|
||||
bool paramSameOffset = shape::haveSameShapeAndStrides(mean->shapeInfo(), variance->shapeInfo());
|
||||
if (paramSameOffset && gamma != nullptr)
|
||||
paramSameOffset &= shape::haveSameShapeAndStrides(mean->shapeInfo(), gamma->shapeInfo());
|
||||
if (paramSameOffset && beta != nullptr)
|
||||
paramSameOffset &= shape::haveSameShapeAndStrides(mean->shapeInfo(), beta->shapeInfo());
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
sd::LongType xzCoords[SD_MAX_RANK], minCoords[SD_MAX_RANK];
|
||||
|
||||
for (sd::LongType i = 0, j = 0; i < xRank; ++i)
|
||||
if (j < numAxes && i != axes[j])
|
||||
minCoords[i] = 0;
|
||||
else
|
||||
++j;
|
||||
|
||||
sd::LongType *inputShape = input->shapeOf();
|
||||
sd::LongType *inputStride = input->stridesOf();
|
||||
|
||||
sd::LongType *outputShape = output->shapeOf();
|
||||
sd::LongType *outputStride = output->stridesOf();
|
||||
|
||||
sd::LongType *meanShape = mean->shapeOf();
|
||||
sd::LongType *varianceShape = variance->shapeOf();
|
||||
sd::LongType *gammaShape = gamma == nullptr ? nullptr : gamma->shapeOf();
|
||||
sd::LongType *betaShape = beta == nullptr ? nullptr : beta->shapeOf();
|
||||
|
||||
sd::LongType *meanStride = mean->stridesOf();
|
||||
sd::LongType *varianceStride = variance->stridesOf();
|
||||
|
||||
sd::LongType *gammaStride = gamma == nullptr ? nullptr : gamma->stridesOf();
|
||||
sd::LongType *betaStride = beta == nullptr ? nullptr : beta->stridesOf();
|
||||
for (sd::LongType i = start; i < stop; i++) {
|
||||
INDEX2COORDS(i, xRank, inputShape, xzCoords);
|
||||
|
||||
sd::LongType xOffset;
|
||||
COORDS2INDEX(xRank, inputStride, xzCoords, xOffset);
|
||||
sd::LongType zOffset = xzSameOffset ? xOffset : 0;
|
||||
if (!xzSameOffset) {
|
||||
COORDS2INDEX(xRank,outputStride, xzCoords, zOffset);
|
||||
}
|
||||
|
||||
if (minRank == xRank) {
|
||||
for (sd::LongType j = 0; j < numAxes; ++j) minCoords[axes[j]] = xzCoords[axes[j]];
|
||||
} else // minRank = numAxes = 1 in this case
|
||||
minCoords[0] = xzCoords[axes[0]];
|
||||
|
||||
sd::LongType meanOffset, varianceOffset;
|
||||
COORDS2INDEX(minRank, meanStride, minCoords, meanOffset);
|
||||
varianceOffset = paramSameOffset ? meanOffset : 0;
|
||||
if (!paramSameOffset) {
|
||||
COORDS2INDEX(minRank, varianceStride, minCoords, varianceOffset);
|
||||
}
|
||||
|
||||
T sigmaInvGam = 1. / sd::math::sd_sqrt<T, T>(v[varianceOffset] + epsilon);
|
||||
|
||||
if (g != nullptr) {
|
||||
sd::LongType gammaOffset = paramSameOffset ? meanOffset : 0;
|
||||
if (!paramSameOffset) {
|
||||
COORDS2INDEX(minRank,gammaStride, minCoords, gammaOffset);
|
||||
}
|
||||
sigmaInvGam *= g[gammaOffset];
|
||||
}
|
||||
|
||||
z[zOffset] = (x[xOffset] - m[meanOffset]) * sigmaInvGam;
|
||||
|
||||
if (b != nullptr) {
|
||||
sd::LongType betaOffset = paramSameOffset ? meanOffset : 0;
|
||||
if (!paramSameOffset) {
|
||||
COORDS2INDEX(minRank,betaStride, minCoords, betaOffset);
|
||||
}
|
||||
z[zOffset] += b[betaOffset];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input->lengthOf());
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void batchnorm(NDArray* input, NDArray* mean, NDArray* variance, NDArray* gamma,
|
||||
NDArray* beta, NDArray* output, const std::vector<LongType>& axes, const double epsilon) {
|
||||
// batchnorm2_ is still slower ?
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), batchnorm_, (input, mean, variance, gamma, beta, output, axes, epsilon),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void batchnorm_,
|
||||
(NDArray* input, NDArray* mean, NDArray* variance, NDArray* gamma,
|
||||
NDArray* beta, NDArray* output, const std::vector<sd::LongType>& axes, const double epsilon),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,126 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by Yurii Shyrma on 11.12.2017
|
||||
//
|
||||
#include <array/DataTypeUtils.h>
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/betaInc.h>
|
||||
|
||||
#include <cmath>
|
||||
#if NOT_EXCLUDED(OP_betainc)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// modified Lentz’s algorithm for continued fractions,
|
||||
// reference: Lentz, W.J. 1976, “Generating Bessel Functions in Mie Scattering Calculations Using Continued Fractions”
|
||||
|
||||
template <typename T>
|
||||
static T continuedFraction(const T a, const T b, const T x) {
|
||||
const T min = DataTypeUtils::min_positive<T>() / static_cast<T>(DataTypeUtils::eps<T>());
|
||||
const T aPlusb = a + b;
|
||||
T val, aPlus2i;
|
||||
|
||||
T t2 = static_cast<T>(1);
|
||||
T t1 = static_cast<T>(1) - aPlusb * x / (a + static_cast<T>(1));
|
||||
if (math::sd_abs<T,T>(t1) < min) t1 = min;
|
||||
t1 = static_cast<T>(1) / t1;
|
||||
T result = t1;
|
||||
|
||||
for (sd::LongType i = 1; i <= maxIter; ++i) {
|
||||
aPlus2i = a + static_cast<T>(2 * i);
|
||||
val = i * (b - i) * x / ((aPlus2i - static_cast<T>(1)) * aPlus2i);
|
||||
// t1
|
||||
t1 = static_cast<T>(1) + val * t1;
|
||||
if (math::sd_abs<T,T>(t1) < min) t1 = min;
|
||||
t1 = static_cast<T>(1) / t1;
|
||||
// t2
|
||||
t2 = static_cast<T>(1) + val / t2;
|
||||
if (math::sd_abs<T,T>(t2) < min) t2 = min;
|
||||
// result
|
||||
result *= t2 * t1;
|
||||
val = -(a + i) * (aPlusb + i) * x / ((aPlus2i + static_cast<T>(1)) * aPlus2i);
|
||||
// t1
|
||||
t1 = static_cast<T>(1) + val * t1;
|
||||
if (math::sd_abs<T,T>(t1) < min) t1 = min;
|
||||
t1 = static_cast<T>(1) / t1;
|
||||
// t2
|
||||
t2 = static_cast<T>(1) + val / t2;
|
||||
if (math::sd_abs<T,T>(t2) < min) t2 = min;
|
||||
// result
|
||||
val = t2 * t1;
|
||||
result *= val;
|
||||
|
||||
// condition to stop loop
|
||||
if (math::sd_abs<T,T>(val - static_cast<T>(1)) <= DataTypeUtils::eps<T>()) return result;
|
||||
}
|
||||
|
||||
return DataTypeUtils::infOrMax<T>(); // no convergence, more iterations is required, return infinity
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// evaluates incomplete beta function for positive a and b, and x between 0 and 1.
|
||||
template <typename T>
|
||||
static T betaIncCore(T a, T b, T x) {
|
||||
// t^{n-1} * (1 - t)^{n-1} is symmetric function with respect to x = 0.5
|
||||
if (a == b && x == static_cast<T>(0.5)) return static_cast<T>(0.5);
|
||||
|
||||
if (x == static_cast<T>(0) || x == static_cast<T>(1)) return x;
|
||||
|
||||
const T gammaPart = static_cast<T>(lgamma(a) + lgamma(b) - lgamma(a + b));
|
||||
const T front = math::sd_exp<T, T>(math::sd_log<T, T>(x) * a + math::sd_log<T, T>(1.f - x) * b - gammaPart);
|
||||
|
||||
if (x <= (a + static_cast<T>(1)) / (a + b + static_cast<T>(2)))
|
||||
return front * continuedFraction<T>(a, b, x) / a;
|
||||
else // symmetry relation
|
||||
return static_cast<T>(1) - front * continuedFraction<T>(b, a, static_cast<T>(1) - x) / b;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void betaIncForArray(sd::LaunchContext* context, NDArray& a, NDArray& b, NDArray& x,
|
||||
NDArray& output) {
|
||||
int xLen = x.lengthOf();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) output.r<T>(i) = betaIncCore<T>(a.t<T>(i), b.t<T>(i), x.t<T>(i));
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, xLen);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// overload betaInc for arrays, shapes of a, b and x must be the same !!!
|
||||
void betaInc(sd::LaunchContext* context, NDArray& a, NDArray& b, NDArray& x, NDArray& output) {
|
||||
auto xType = a.dataType();
|
||||
BUILD_SINGLE_SELECTOR(xType, betaIncForArray, (context, a, b, x, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void betaIncForArray,
|
||||
(sd::LaunchContext * context, NDArray& a, NDArray& b, NDArray& x,
|
||||
NDArray& output),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,234 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com)
|
||||
// @author sgazeos@gmail.com
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#if NOT_EXCLUDED(OP_clip)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void clipByNorm(LaunchContext* context, NDArray* input, NDArray* output, const std::vector<LongType>& dimensions,
|
||||
NDArray* clipNorm, const bool isInplace, const bool useAverage) {
|
||||
NDArray* z = nullptr;
|
||||
|
||||
if (isInplace) {
|
||||
z = input;
|
||||
} else {
|
||||
output->assign(input);
|
||||
z = output;
|
||||
}
|
||||
|
||||
if (dimensions.empty()) {
|
||||
std::vector<sd::LongType> emptyVec = {};
|
||||
|
||||
NDArray *norm2Result = z->reduceAlongDimension(reduce::Norm2, &emptyVec);
|
||||
if (useAverage) {
|
||||
NDArray *divResult = (*norm2Result) / z->lengthOf();
|
||||
if (divResult->e<float>(0) > clipNorm->e<float>(0)) {
|
||||
NDArray *clipDivResult = (*clipNorm) / (*divResult);
|
||||
*z *= (*clipDivResult);
|
||||
delete clipDivResult;
|
||||
}
|
||||
delete divResult;
|
||||
} else {
|
||||
if (norm2Result->e<float>(0) > clipNorm->e<float>(0)) {
|
||||
NDArray *clipDivResult = (*clipNorm) / (*norm2Result);
|
||||
*z *= (*clipDivResult);
|
||||
delete clipDivResult;
|
||||
}
|
||||
}
|
||||
delete norm2Result;
|
||||
} else {
|
||||
auto listOfSubArrs = z->allTensorsAlongDimension(dimensions);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
std::vector<sd::LongType> emptyVec = {};
|
||||
NDArray *norm2Result = listOfSubArrs.at(i)->reduceAlongDimension(reduce::Norm2, &emptyVec);
|
||||
if (useAverage) {
|
||||
NDArray *divResult = (*norm2Result) / listOfSubArrs.at(i)->lengthOf();
|
||||
if (divResult->e<float>(0) > clipNorm->e<float>(0)) {
|
||||
NDArray *clipDivResult = (*clipNorm) / (*divResult);
|
||||
*listOfSubArrs.at(i) *= (*clipDivResult);
|
||||
delete clipDivResult;
|
||||
}
|
||||
delete divResult;
|
||||
} else {
|
||||
if (norm2Result->e<float>(0) > clipNorm->e<float>(0)) {
|
||||
NDArray *clipDivResult = (*clipNorm) / (*norm2Result);
|
||||
*listOfSubArrs.at(i) *= (*clipDivResult);
|
||||
delete clipDivResult;
|
||||
}
|
||||
}
|
||||
delete norm2Result;
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func, 0, listOfSubArrs.size());
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void clipByNormBp_(NDArray *input, NDArray *gradO, NDArray *gradI,
|
||||
const std::vector<LongType>& dimensions, NDArray *clipNorm, const bool useAverage) {
|
||||
const int rank = input->rankOf();
|
||||
|
||||
auto *norm2Ptr = input->reduceAlongDimension(reduce::Norm2, &dimensions);
|
||||
auto norm2 = *norm2Ptr;
|
||||
auto *sumsPtr = input->reduceAlongDimension(reduce::Sum, &dimensions);
|
||||
auto sums = *sumsPtr;
|
||||
|
||||
if (norm2.lengthOf() == 1) {
|
||||
const T norm = useAverage ? norm2.e<T>(0) / input->lengthOf() : norm2.e<T>(0);
|
||||
|
||||
auto clipVal = clipNorm->e<T>(0);
|
||||
|
||||
if (norm > clipVal) {
|
||||
const T sum = sums.e<T>(0); // reduce to scalar
|
||||
const T factor1 = clipVal / norm;
|
||||
const T factor2 = static_cast<T>(1.f) / (norm * norm); // 1 / (norm*norm*norm)
|
||||
|
||||
auto lambda = LAMBDA_TT(x, y, sum, factor1, factor2) {
|
||||
return factor1 * y * (static_cast<T>(1.f) - factor2 * x * sum);
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda<T>(gradO, lambda, gradI);
|
||||
} else
|
||||
gradI->assign(gradO);
|
||||
} else {
|
||||
auto gradISubArrs = gradI->allTensorsAlongDimension({dimensions});
|
||||
auto gradOSubArrs = gradO->allTensorsAlongDimension({dimensions});
|
||||
auto inputSubArrs = input->allTensorsAlongDimension({dimensions});
|
||||
|
||||
auto clipVal = clipNorm->e<T>(0);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto gradOSubArr = gradOSubArrs.at(i);
|
||||
auto gradISubArr = gradISubArrs.at(i);
|
||||
|
||||
const T norm = useAverage ? norm2.e<T>(i) / gradISubArr->lengthOf() : norm2.e<T>(i);
|
||||
|
||||
if (norm > clipVal) {
|
||||
auto inputSubArr = inputSubArrs.at(i);
|
||||
|
||||
const T sum = sums.e<T>(i); // reduce to scalar
|
||||
const T factor1 = clipVal / norm;
|
||||
const T factor2 = static_cast<T>(1.f) / (norm * norm); // 1 / (norm*norm*norm)
|
||||
|
||||
auto lambda = LAMBDA_TT(x, y, sum, factor1, factor2) {
|
||||
return factor1 * y * (static_cast<T>(1.f) - factor2 * x * sum);
|
||||
});
|
||||
|
||||
inputSubArr->applyPairwiseLambda<T>(gradOSubArr, lambda, gradISubArr);
|
||||
} else
|
||||
gradISubArr->assign(gradOSubArr);
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func, 0, gradISubArrs.size());
|
||||
}
|
||||
|
||||
delete norm2Ptr;
|
||||
delete sumsPtr;
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE(void clipByNormBp_,
|
||||
(NDArray *input, NDArray *gradO, NDArray *gradI, const std::vector<sd::LongType>& dimensions,
|
||||
NDArray *clipNorm, const bool useAverage),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void clipByNormBp(sd::LaunchContext* context, NDArray *input, NDArray *gradO, NDArray *gradI,
|
||||
const std::vector<LongType>& dimensions, NDArray* clipNorm, const bool useAverage) {
|
||||
BUILD_SINGLE_SELECTOR(gradI->dataType(), clipByNormBp_, (input, gradO, gradI, dimensions, clipNorm, useAverage),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void clipByGlobalNorm_(std::vector<NDArray*>& inputs, double clipNorm, sd::memory::Workspace* workspace,
|
||||
std::vector<NDArray*>& outputs, bool isInplace) {
|
||||
T globalNorm = static_cast<T>(0);
|
||||
for (size_t i = 0; i < inputs.size(); i++) {
|
||||
auto input = inputs[i];
|
||||
auto* l2norm = input->reduceNumber(reduce::Norm2);
|
||||
T normVal = l2norm->t<T>(0);
|
||||
globalNorm += normVal * normVal;
|
||||
delete l2norm;
|
||||
}
|
||||
|
||||
auto normS = sd::math::sd_sqrt<T, T>(globalNorm);
|
||||
outputs[inputs.size()]->p(0, normS);
|
||||
|
||||
const T factor = clipNorm / normS;
|
||||
|
||||
for (size_t e = 0; e < inputs.size(); e++) {
|
||||
// all-reduce
|
||||
auto input = inputs[e];
|
||||
auto output = outputs[e];
|
||||
|
||||
if (normS <= clipNorm) {
|
||||
output->assign(input);
|
||||
} else {
|
||||
auto lambda = LAMBDA_T(_x, factor) { return _x * factor; });
|
||||
input->applyLambda<T>(lambda, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
void clipByGlobalNorm(LaunchContext* context, std::vector<NDArray*>& inputs, double clipNorm,
|
||||
memory::Workspace* workspace, std::vector<NDArray*>& outputs, bool isInplace) {
|
||||
BUILD_SINGLE_SELECTOR(outputs[0]->dataType(), clipByGlobalNorm_, (inputs, clipNorm, workspace, outputs, isInplace),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void clipByGlobalNorm_,
|
||||
(std::vector<NDArray*> & inputs, double clipNorm, sd::memory::Workspace* workspace,
|
||||
std::vector<NDArray*>& outputs, bool isInplace),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
template <typename T>
|
||||
static void clipByValue_(NDArray* input, double leftBound, double rightBound, NDArray* output) {
|
||||
auto routine = LAMBDA_T(_x, leftBound, rightBound) {
|
||||
if (_x > rightBound) return static_cast<T>(rightBound);
|
||||
if (_x < leftBound) return static_cast<T>(leftBound);
|
||||
return _x;
|
||||
});
|
||||
|
||||
input->applyLambda<T>(routine, output);
|
||||
}
|
||||
|
||||
void clipByValue(LaunchContext* context, NDArray* input, double leftBound, double rightBound, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), clipByValue_, (input, leftBound, rightBound, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void clipByValue_,
|
||||
(NDArray * input, double leftBound, double rightBound, NDArray* output);
|
||||
, SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by raver119 on 30.11.17.
|
||||
//
|
||||
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/col2im.h>
|
||||
#if NOT_EXCLUDED(OP_col2im)
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
// [bS, iC, kH, kW, oH, oW] is de-convoluted to [bS, iC, iH, iW]
|
||||
template <typename T>
|
||||
static void col2im_(sd::LaunchContext& context, NDArray* input, NDArray* output, const LongType sH, const LongType sW,
|
||||
const LongType pH, const LongType pW, const LongType iH, const LongType iW, const LongType dH, const LongType dW) {
|
||||
if(input->rankOf() != 6) {
|
||||
THROW_EXCEPTION("ops::helpers::col2im: input array must have rank = 6");
|
||||
}
|
||||
|
||||
if(output->rankOf() != 4) {
|
||||
THROW_EXCEPTION("ops::helpers::col2im: output array must have rank = 4");
|
||||
}
|
||||
|
||||
auto colBuff = input->bufferAsT<T>();
|
||||
auto imBuff = output->bufferAsT<T>();
|
||||
auto colShapeBuffer = input->shapeInfo();
|
||||
auto imShapeBuffer = output->shapeInfo();
|
||||
auto colShape = shape::shapeOf(colShapeBuffer);
|
||||
auto colStride = shape::stride(colShapeBuffer);
|
||||
auto imShape = shape::shapeOf(imShapeBuffer);
|
||||
auto imStride = shape::stride(imShapeBuffer);
|
||||
|
||||
const LongType bS = imShape[0];
|
||||
const LongType iC = imShape[1];
|
||||
const LongType kH = colShape[2];
|
||||
const LongType kW = colShape[3];
|
||||
const LongType oH = colShape[4];
|
||||
const LongType oW = colShape[5];
|
||||
const sd::LongType colStride0 = colStride[0];
|
||||
const sd::LongType colStride1 = colStride[1];
|
||||
const sd::LongType colStride2 = colStride[2];
|
||||
const sd::LongType colStride3 = colStride[3];
|
||||
const sd::LongType colStride4 = colStride[4];
|
||||
const sd::LongType colStride5 = colStride[5];
|
||||
const sd::LongType imStride0 = imStride[0];
|
||||
const sd::LongType imStride1 = imStride[1];
|
||||
const sd::LongType imStride2 = imStride[2];
|
||||
const sd::LongType imStride3 = imStride[3];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto b = start; b < stop; b++) {
|
||||
LongType im0Offset = b * imStride0;
|
||||
LongType col4Offset = b * colStride0;
|
||||
for (int colH = 0; colH < oH; ++colH) {
|
||||
LongType col5Offset = col4Offset + colH * colStride4;
|
||||
for (int colW = 0; colW < oW; ++colW) {
|
||||
LongType col1Offset = col5Offset + colW * colStride5;
|
||||
LongType im1Offset = im0Offset;
|
||||
for (int c = 0; c < iC; ++c) {
|
||||
int imRow = (-pH + colH * sH);
|
||||
LongType col2Offset = col1Offset + c * colStride1;
|
||||
LongType im2Offset = im1Offset + c * imStride1 + imRow * imStride2;
|
||||
for (int kRow = 0; kRow < kH; ++kRow) {
|
||||
int imCol = -pW + colW * sW;
|
||||
LongType col3Offset = col2Offset + kRow * colStride2;
|
||||
LongType im3Offset = im2Offset + kRow * dH * imStride2 + imCol * imStride3;
|
||||
for (int kCol = 0; kCol < kW; ++kCol) {
|
||||
if (static_cast<unsigned>(imRow) < static_cast<unsigned>(iH) &&
|
||||
static_cast<unsigned>(imCol) < static_cast<unsigned>(iW)) {
|
||||
imBuff[im3Offset] += colBuff[col3Offset];
|
||||
}
|
||||
col3Offset += colStride3;
|
||||
imCol += dW;
|
||||
im3Offset += dW * imStride3;
|
||||
}
|
||||
imRow += dH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, bS);
|
||||
}
|
||||
void col2im(LaunchContext& context, NDArray* input, NDArray* output, const LongType sH, const LongType sW, const LongType pH,
|
||||
const LongType pW, const LongType iH, const LongType iW, const LongType dH, const LongType dW) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), col2im_, (context, input, output, sH, sW, pH, pW, iH, iW, dH, dW),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#if NOT_EXCLUDED(OP_compare_and_bitpack)
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename X>
|
||||
uint8_t pack(const X* buff, const X& threshold) {
|
||||
uint8_t res;
|
||||
res = (buff[0] > threshold) << 7;
|
||||
res = res | ((buff[1] > threshold) << 6);
|
||||
res = res | ((buff[2] > threshold) << 5);
|
||||
res = res | ((buff[3] > threshold) << 4);
|
||||
res = res | ((buff[4] > threshold) << 3);
|
||||
res = res | ((buff[5] > threshold) << 2);
|
||||
res = res | ((buff[6] > threshold) << 1);
|
||||
res = res | (buff[7] > threshold);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint8_t pack<bool>(const bool* buff, const bool& threshold) {
|
||||
// ignore threshold
|
||||
uint8_t res;
|
||||
res = buff[0] << 7;
|
||||
res = res | (buff[1] << 6);
|
||||
res = res | (buff[2] << 5);
|
||||
res = res | (buff[3] << 4);
|
||||
res = res | (buff[4] << 3);
|
||||
res = res | (buff[5] << 2);
|
||||
res = res | (buff[6] << 1);
|
||||
res = res | buff[7];
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename X>
|
||||
uint8_t pack(const X* buff, int stride, const X& threshold) {
|
||||
uint8_t res;
|
||||
res = (buff[0] > threshold) << 7;
|
||||
res = res | ((buff[1 * stride] > threshold) << 6);
|
||||
res = res | ((buff[2 * stride] > threshold) << 5);
|
||||
res = res | ((buff[3 * stride] > threshold) << 4);
|
||||
res = res | ((buff[4 * stride] > threshold) << 3);
|
||||
res = res | ((buff[5 * stride] > threshold) << 2);
|
||||
res = res | ((buff[6 * stride] > threshold) << 1);
|
||||
res = res | (buff[7 * stride] > threshold);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint8_t pack<bool>(const bool* buff, int stride, const bool& threshold) {
|
||||
// ignore threshold
|
||||
uint8_t res;
|
||||
res = buff[0] << 7;
|
||||
res = res | (buff[1 * stride] << 6);
|
||||
res = res | (buff[2 * stride] << 5);
|
||||
res = res | (buff[3 * stride] << 4);
|
||||
res = res | (buff[4 * stride] << 3);
|
||||
res = res | (buff[5 * stride] << 2);
|
||||
res = res | (buff[6 * stride] << 1);
|
||||
res = res | buff[7 * stride];
|
||||
return res;
|
||||
}
|
||||
template <typename X>
|
||||
void compareAndBitpack_(NDArray& input, NDArray& thresholdScalar, NDArray& output) {
|
||||
auto rank = input.rankOf();
|
||||
X threshold = thresholdScalar.e<X>(0);
|
||||
auto buff = input.bufferAsT<X>();
|
||||
uint8_t* outBuff = output.bufferAsT<uint8_t>();
|
||||
if (input.ordering() == 'c' && output.ordering() == 'c' && input.ews() == 1 && output.ews() == 1) {
|
||||
FUNC_1D func = [buff, outBuff, threshold](uint64_t thread_id, int64_t start, int64_t stop,
|
||||
int64_t increment) -> void {
|
||||
auto outBuffPart = outBuff + start;
|
||||
auto buffPart = buff + start * 8;
|
||||
auto len = stop - start;
|
||||
// run
|
||||
for (auto i = 0; i < len; i++) {
|
||||
outBuffPart[i] = pack<X>(&(buffPart[8 * i]), threshold);
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, output.lengthOf(), 1);
|
||||
|
||||
} else {
|
||||
auto inShapes = input.shapeOf();
|
||||
auto outShapes = output.shapeOf();
|
||||
auto inStrides = input.stridesOf();
|
||||
auto outStrides = output.stridesOf();
|
||||
|
||||
if (rank == 1) {
|
||||
auto inLastStride = inStrides[rank - 1];
|
||||
auto outLastStride = outStrides[rank - 1];
|
||||
FUNC_1D func = [buff, outBuff, inLastStride, outLastStride, threshold](uint64_t thread_id, int64_t start,
|
||||
int64_t stop, int64_t increment) -> void {
|
||||
auto buffPart = buff + start * 8 * inLastStride;
|
||||
auto outBuffPart = outBuff + start * outLastStride;
|
||||
auto len = stop - start;
|
||||
// run
|
||||
for (auto i = 0; i < len; i++) {
|
||||
*outBuffPart = pack<X>(buffPart, inLastStride, threshold);
|
||||
buffPart += 8 * inLastStride;
|
||||
outBuffPart += outLastStride;
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, output.lengthOf(), 1);
|
||||
} else {
|
||||
// if output shape is {n1, n2, n3} then input shape is { n1. n2, n3 * 8}
|
||||
// therefore we can split input shape {n1, n2, n3 , 8} and correct its stride
|
||||
// as we do not need last shape info. lets just extend and correct its stride
|
||||
sd::LongType extendedStrides[SD_MAX_RANK];
|
||||
for (int i = 0; i < rank; i++) {
|
||||
extendedStrides[i] = inStrides[i];
|
||||
}
|
||||
// lets correct new stride
|
||||
extendedStrides[rank - 1] = 8 * inStrides[rank - 1];
|
||||
extendedStrides[rank] = inStrides[rank - 1];
|
||||
// general case. its slow. we can improve it for special case later
|
||||
// generic case that could be further improved. for now its slow
|
||||
FUNC_1D func = [rank, buff, outBuff, outShapes, extendedStrides, outStrides, threshold](
|
||||
uint64_t thread_id, int64_t start, int64_t stop, int64_t increment) -> void {
|
||||
sd::LongType coords[SD_MAX_RANK] = {};
|
||||
sd::LongType* ptr_coords = (sd::LongType*)&coords;
|
||||
sd::LongType len = (stop - start);
|
||||
// its extended as {rank+1} so extendedStrides[rank] is valid
|
||||
auto innermostStride = extendedStrides[rank];
|
||||
INDEX2COORDS(start, rank, outShapes, ptr_coords);
|
||||
// here last dimension will not be in coords. this way output shape and input shapes are equal
|
||||
sd::LongType inOffset, outOffset;
|
||||
COORDS2INDEX(rank + 1, extendedStrides, ptr_coords, inOffset);
|
||||
COORDS2INDEX(rank, outStrides, ptr_coords, outOffset);
|
||||
for (sd::LongType k = 0; k < len; k++) {
|
||||
auto buffPart = &(buff[inOffset]);
|
||||
auto outBuffPart = &(outBuff[outOffset]);
|
||||
*outBuffPart = pack<X>(buffPart, innermostStride, threshold);
|
||||
inOffset += extendedStrides[rank];
|
||||
outOffset += outStrides[rank - 1];
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, output.lengthOf(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
void compareAndBitpack(sd::graph::Context& block, NDArray& input, NDArray& threshold, NDArray& output) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), compareAndBitpack_, (input, threshold, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void compareAndBitpack_,
|
||||
(NDArray& input, NDArray& threshold, NDArray& output), SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/compare_elem.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static void _compare_elem(NDArray* input, bool isStrictlyIncreasing, bool& output) {
|
||||
auto length = shape::length(input->shapeInfo());
|
||||
|
||||
int elementsPerThread = length / ELEMENT_THRESHOLD;
|
||||
int num_threads = sd::math::sd_max<int>(1, elementsPerThread);
|
||||
num_threads = sd::math::sd_min<int>(num_threads, omp_get_max_threads());
|
||||
sd::LongType sumt = 0;
|
||||
|
||||
if (isStrictlyIncreasing) {
|
||||
auto func = PRAGMA_REDUCE_LONG {
|
||||
sd::LongType sum = 0;
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto val0 = input->t<T>(i);
|
||||
auto val1 = input->t<T>(i + 1);
|
||||
sum += val0 >= val1 ? -1 : 0;
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
sumt = samediff::Threads::parallel_long(func, LAMBDA_SUML, 0, length - 1);
|
||||
} else {
|
||||
auto func = PRAGMA_REDUCE_LONG {
|
||||
sd::LongType sum = 0;
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto val0 = input->t<T>(i);
|
||||
auto val1 = input->t<T>(i + 1);
|
||||
sum += val0 > val1 ? -1 : 0;
|
||||
}
|
||||
|
||||
return sum;
|
||||
};
|
||||
sumt = samediff::Threads::parallel_long(func, LAMBDA_SUML, 0, length - 1);
|
||||
}
|
||||
|
||||
output = (sumt > -1);
|
||||
}
|
||||
|
||||
void compare_elem(sd::LaunchContext* context, NDArray* input, bool isStrictlyIncreasing, bool& output) {
|
||||
auto xType = input->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, _compare_elem, (input, isStrictlyIncreasing, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _compare_elem, (NDArray * A, bool isStrictlyIncreasing, bool& output);
|
||||
, SD_COMMON_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,37 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <ops/declarable/helpers/cpu/indexReductions.hpp>
|
||||
#include <system/op_boilerplate.h>
|
||||
#if NOT_EXCLUDED(OP_argamax)
|
||||
|
||||
|
||||
#cmakedefine SD_COMMON_TYPES_GEN
|
||||
|
||||
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
BUILD_DOUBLE_TEMPLATE( SD_LIB_HIDDEN void argAbsMax_, (NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_INDEXING_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
//
|
||||
#cmakedefine SD_COMMON_TYPES_GEN
|
||||
#include <ops/declarable/helpers/cpu/indexReductions.hpp>
|
||||
#include <system/op_boilerplate.h>
|
||||
#include <system/selective_rendering.h>
|
||||
#if NOT_EXCLUDED(OP_argamin)
|
||||
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
BUILD_DOUBLE_TEMPLATE( SD_LIB_HIDDEN void argAbsMin_, (NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_INDEXING_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
//
|
||||
#cmakedefine SD_COMMON_TYPES_GEN
|
||||
#include <ops/declarable/helpers/cpu/indexReductions.hpp>
|
||||
#include <system/op_boilerplate.h>
|
||||
#if NOT_EXCLUDED(OP_argmax)
|
||||
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
BUILD_DOUBLE_TEMPLATE( SD_LIB_HIDDEN void argMax_, (NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_INDEXING_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
//
|
||||
|
||||
#cmakedefine SD_COMMON_TYPES_GEN
|
||||
#include <ops/declarable/helpers/cpu/indexReductions.hpp>
|
||||
#include <system/op_boilerplate.h>
|
||||
#if NOT_EXCLUDED(OP_argmin)
|
||||
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
BUILD_DOUBLE_TEMPLATE( SD_LIB_HIDDEN void argMin_, (NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_INDEXING_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 sgazeos@gmail.com
|
||||
//
|
||||
|
||||
#include <ops/declarable/helpers/crop_and_resize.h>
|
||||
#include <ops/declarable/helpers/cpu/crop_and_resize.hpp>
|
||||
#include <system/op_boilerplate.h>
|
||||
#if NOT_EXCLUDED(OP_crop_and_resize)
|
||||
|
||||
#cmakedefine SD_COMMON_TYPES_GEN
|
||||
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
BUILD_TRIPLE_TEMPLATE(template SD_LIB_HIDDEN void cropAndResizeFunctor_, (NDArray *images, NDArray *boxes, NDArray *indices, NDArray *cropSize, int method, double extrapolationVal, NDArray *crops), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_FLOAT_TYPES, SD_INTEGER_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <system/op_boilerplate.h>
|
||||
#if NOT_EXCLUDED(OP_standard_deviation)
|
||||
//inform Cmake that each of LIBND4J_TYPE will be generated and used in separate cpp files.
|
||||
#cmakedefine SD_COMMON_TYPES_GEN
|
||||
#include <ops/declarable/helpers/cpu/summaryReductions.hpp>
|
||||
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
BUILD_DOUBLE_TEMPLATE( SD_LIB_HIDDEN void standardDeviation_, (NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions, bool biasCorrected), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_FLOAT_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
//
|
||||
|
||||
//inform Cmake that each of LIBND4J_TYPE will be generated and used in separate cpp files.
|
||||
#cmakedefine SD_COMMON_TYPES_GEN
|
||||
#include <ops/declarable/helpers/cpu/summaryReductions.hpp>
|
||||
#include <system/op_boilerplate.h>
|
||||
#if NOT_EXCLUDED(OP_variance)
|
||||
|
||||
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
BUILD_DOUBLE_TEMPLATE( SD_LIB_HIDDEN void variance_, (NDArray& input, NDArray& output, const std::vector<sd::LongType>& dimensions, bool biasCorrected), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_FLOAT_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
|
||||
//
|
||||
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
#include <ops/specials.h>
|
||||
#include <system/selective_rendering.h>
|
||||
#if NOT_EXCLUDED(OP_concat)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void concat_(const std::vector<NDArray*>& inArrs, NDArray& output, const int axis) {
|
||||
sd::SpecialMethods<T>::concatCpuGeneric(inArrs, output, axis);
|
||||
}
|
||||
|
||||
void concat(sd::LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output, const int axis) {
|
||||
auto outputTYpe = output.dataType();
|
||||
BUILD_SINGLE_SELECTOR(output.dataType(), concat_, (inArrs, output, axis), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void concat_,
|
||||
(const std::vector<NDArray*>& inArrs, NDArray& output, const int axis), SD_COMMON_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 GS <sgazeos@gmail.com>
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/confusion.h>
|
||||
#if NOT_EXCLUDED(OP_confusion_matrix)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void _confusionFunctor(NDArray* labels, NDArray* predictions, NDArray* weights, NDArray* output) {
|
||||
ResultSet arrs = output->allTensorsAlongDimension({1});
|
||||
int lLen = labels->lengthOf();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType j = start; j < stop; j++) {
|
||||
auto label = labels->e<sd::LongType>(j);
|
||||
auto pred = predictions->e<sd::LongType>(j);
|
||||
T value = (weights == nullptr ? (T)1.0f : weights->e<T>(j));
|
||||
T curr = arrs.at(label)->e<T>(pred);
|
||||
arrs.at(label)->p<T>(pred, curr + value);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, lLen);
|
||||
}
|
||||
|
||||
void confusionFunctor(sd::LaunchContext* context, NDArray* labels, NDArray* predictions, NDArray* weights,
|
||||
NDArray* output) {
|
||||
auto xType = output->dataType(); // weights can be null
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, _confusionFunctor, (labels, predictions, weights, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _confusionFunctor,
|
||||
(NDArray * labels, NDArray* predictions, NDArray* weights, NDArray* output);
|
||||
, SD_NUMERIC_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// [bS, iC, kD, kH, kW, oD, oH, oW] is de-convoluted to [bS, iC, iD, iH, iW]
|
||||
template <typename T>
|
||||
static void col2vol_(NDArray& columns, NDArray& volume, const LongType sD, const LongType sH, const LongType sW, const LongType pD,
|
||||
const LongType pH, const LongType pW, const LongType dD, const LongType dH, const LongType dW) {
|
||||
// initial zeroing of volume content
|
||||
volume.nullify();
|
||||
|
||||
const LongType bS = volume.sizeAt(0);
|
||||
const LongType iC = volume.sizeAt(1);
|
||||
const LongType iD = volume.sizeAt(2);
|
||||
const LongType iH = volume.sizeAt(3);
|
||||
const LongType iW = volume.sizeAt(4);
|
||||
const LongType kD = columns.sizeAt(2);
|
||||
const LongType kH = columns.sizeAt(3);
|
||||
const LongType kW = columns.sizeAt(4);
|
||||
const LongType oD = columns.sizeAt(5);
|
||||
const LongType oH = columns.sizeAt(6);
|
||||
const LongType oW = columns.sizeAt(7);
|
||||
const sd::LongType colStride0 = columns.stridesOf()[0];
|
||||
const sd::LongType colStride1 = columns.stridesOf()[1];
|
||||
const sd::LongType colStride2 = columns.stridesOf()[2];
|
||||
const sd::LongType colStride3 = columns.stridesOf()[3];
|
||||
const sd::LongType colStride4 = columns.stridesOf()[4];
|
||||
const sd::LongType colStride5 = columns.stridesOf()[5];
|
||||
const sd::LongType colStride6 = columns.stridesOf()[6];
|
||||
const sd::LongType colStride7 = columns.stridesOf()[7];
|
||||
const sd::LongType volStride0 = volume.stridesOf()[0];
|
||||
const sd::LongType volStride1 = volume.stridesOf()[1];
|
||||
const sd::LongType volStride2 = volume.stridesOf()[2];
|
||||
const sd::LongType volStride3 = volume.stridesOf()[3];
|
||||
const sd::LongType volStride4 = volume.stridesOf()[4];
|
||||
|
||||
T* volBuff = volume.bufferAsT<T>();
|
||||
T* colBuff = const_cast<NDArray&>(columns).bufferAsT<T>();
|
||||
|
||||
if (volume.ordering() == 'c' && columns.ordering() == 'c' && shape::strideDescendingCAscendingF(volume.shapeInfo()) &&
|
||||
shape::strideDescendingCAscendingF(columns.shapeInfo())) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
T *col, *vol;
|
||||
sd::LongType volDep, volRow, volCol;
|
||||
|
||||
for (sd::LongType b = start; b < stop; b++) {
|
||||
for (sd::LongType c = 0; c < iC; c++) {
|
||||
for (sd::LongType kDep = 0; kDep < kD; ++kDep) {
|
||||
for (sd::LongType kRow = 0; kRow < kH; ++kRow) {
|
||||
for (sd::LongType kCol = 0; kCol < kW; ++kCol) {
|
||||
for (sd::LongType colD = 0; colD < oD; ++colD) {
|
||||
for (sd::LongType colH = 0; colH < oH; ++colH) {
|
||||
for (sd::LongType colW = 0; colW < oW; ++colW) {
|
||||
volDep = (-pD + kDep * dD) + colD * sD;
|
||||
volRow = (-pH + kRow * dH) + colH * sH;
|
||||
volCol = (-pW + kCol * dW) + colW * sW;
|
||||
|
||||
if (volDep >= 0 && volDep < iD &&
|
||||
volRow >= 0 && volRow < iH &&
|
||||
volCol >= 0 && volCol < iW) {
|
||||
|
||||
auto colIndex = b * colStride0 + c * colStride1 + kDep * colStride2 + kRow * colStride3 +
|
||||
kCol * colStride4 + colD * colStride5 + colH * colStride6 + colW * colStride7;
|
||||
auto volIndex = b * volStride0 + c * volStride1 + volDep * volStride2 + volRow * volStride3 +
|
||||
volCol * volStride4;
|
||||
|
||||
|
||||
|
||||
col = colBuff + colIndex;
|
||||
vol = volBuff + volIndex;
|
||||
*vol += *col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, bS);
|
||||
|
||||
} else {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
T *col, *vol;
|
||||
sd::LongType volDep, volRow, volCol;
|
||||
|
||||
for (sd::LongType b = start; b < stop; b++) {
|
||||
for (sd::LongType colD = 0; colD < oD; colD++) {
|
||||
for (sd::LongType colH = 0; colH < oH; ++colH) {
|
||||
for (sd::LongType colW = 0; colW < oW; ++colW) {
|
||||
for (sd::LongType c = 0; c < iC; ++c) {
|
||||
for (sd::LongType kDep = 0; kDep < kD; ++kDep) {
|
||||
for (sd::LongType kRow = 0; kRow < kH; ++kRow) {
|
||||
for (sd::LongType kCol = 0; kCol < kW; ++kCol) {
|
||||
volDep = (-pD + kDep * dD) + colD * sD;
|
||||
volRow = (-pH + kRow * dH) + colH * sH;
|
||||
volCol = (-pW + kCol * dW) + colW * sW;
|
||||
|
||||
if (volDep >= 0 && volDep < iD &&
|
||||
volRow >= 0 && volRow < iH &&
|
||||
volCol >= 0 && volCol < iW) {
|
||||
|
||||
auto colIndex = b * colStride0 + c * colStride1 + kDep * colStride2 + kRow * colStride3 +
|
||||
kCol * colStride4 + colD * colStride5 + colH * colStride6 + colW * colStride7;
|
||||
auto volIndex = b * volStride0 + c * volStride1 + volDep * volStride2 + volRow * volStride3 +
|
||||
volCol * volStride4;
|
||||
|
||||
col = colBuff + colIndex;
|
||||
vol = volBuff + volIndex;
|
||||
*vol += *col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, bS);
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionUtils::col2vol(sd::graph::Context& block, NDArray& columns, NDArray& volume, const LongType sD,
|
||||
const LongType sH, const LongType sW, const LongType pD, const LongType pH, const LongType pW, const LongType dD,
|
||||
const LongType dH, const LongType dW) {
|
||||
BUILD_SINGLE_SELECTOR(volume.dataType(), col2vol_, (columns, volume, sD, sH, sW, pD, pH, pW, dD, dH, dW),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,153 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/MmulHelper.h>
|
||||
#include <ops/declarable/helpers/addBias.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <ops/declarable/helpers/im2col.h>
|
||||
|
||||
#if NOT_EXCLUDED(OP_col2im) && NOT_EXCLUDED(OP_im2col)
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void conv2d_(sd::graph::Context& block, NDArray* input, NDArray* weights, NDArray* bias,
|
||||
NDArray* output, const LongType kH, const LongType kW, const LongType sH, const LongType sW, LongType pH, LongType pW,
|
||||
const LongType dH, const LongType dW, const int paddingMode, const int isNCHW, const int wFormat) {
|
||||
|
||||
// input [bS, iH, iW, iC] (NHWC) or [bS, iC, iH, iW] (NCHW)
|
||||
// weights [kH, kW, iC, oC], [oC, iC, kH, kW], [oC, kH, kW, iC]
|
||||
// bias [oC]
|
||||
// output [bS, oH, oW, oC] (NHWC) or [bS, oC, oH, oW] (NCHW)
|
||||
|
||||
LongType bS = input->sizeAt(0);
|
||||
LongType iC = ConvolutionUtils::inChannels(weights->shapeInfo(), wFormat);
|
||||
LongType oC = ConvolutionUtils::outChannels(weights->shapeInfo(), wFormat);
|
||||
LongType iH = ConvolutionUtils::inputHeight(input->shapeInfo(), isNCHW);
|
||||
LongType iW = ConvolutionUtils::inputWidth(input->shapeInfo(), isNCHW);
|
||||
LongType oH = ConvolutionUtils::calcOutDimConv(iH, kH, sH, pH, dH, paddingMode);
|
||||
LongType oW = ConvolutionUtils::calcOutDimConv(iW, kW, sW, pW, dW, paddingMode);
|
||||
std::vector<LongType> wAxes;
|
||||
if (0 == wFormat)
|
||||
wAxes = {0, 1, 2};
|
||||
else if (1 == wFormat)
|
||||
wAxes = {2, 3, 1};
|
||||
else
|
||||
wAxes = {1, 2, 3};
|
||||
|
||||
|
||||
std::vector<sd::LongType> colShape = {bS, oH, oW, kH, kW, iC};
|
||||
std::vector<sd::LongType> perm = {0, 3, 4, 5, 1, 2};
|
||||
NDArray *col = new NDArray('c', colShape, input->dataType(), input->getContext());
|
||||
NDArray *colPFrom = col->permute(perm, false, false);
|
||||
NDArray *colP = new NDArray(colPFrom); // {bS, iC, kH, kW, oH, oW}
|
||||
std::vector<sd::LongType> mmulResultShape = {bS * oH * oW, oC};
|
||||
NDArray mmulResult('f', mmulResultShape, output->dataType(), output->getContext());
|
||||
std::vector<LongType> permuteForOutput = {0, 3, 1, 2};
|
||||
|
||||
//----- calculation of output -----//
|
||||
auto ctx = block.launchContext();
|
||||
|
||||
NDArray *inputNchw = nullptr; // Track NHWC permutation for cleanup
|
||||
NDArray *zeroVal = NDArrayFactory::create(0.f, input->getContext());
|
||||
if (isNCHW) {
|
||||
helpers::im2col(*ctx, *input, *colP, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
*zeroVal);
|
||||
} else {
|
||||
std::vector<sd::LongType> permute = {0, 3, 1, 2};
|
||||
// For NHWC, we need to permute the input to NCHW before im2col
|
||||
inputNchw = input->permute(permute, false,false);
|
||||
helpers::im2col(*ctx, *inputNchw, *colP, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
*zeroVal);
|
||||
}
|
||||
|
||||
delete zeroVal;
|
||||
delete colPFrom; // View wrapper from permute - no longer needed
|
||||
delete col; // Original col array - no longer needed
|
||||
block.pushIntermediateResult(colP);
|
||||
|
||||
std::vector<sd::LongType> shape = {bS * oH * oW, kH * kW * iC};
|
||||
NDArray *colReshaped = colP->reshape('c', shape, false);
|
||||
std::vector<sd::LongType> perm2 = {3,2,1,0};
|
||||
|
||||
NDArray *weightsPermuted = weights->permute(perm2, false, false);
|
||||
|
||||
std::vector<sd::LongType> wShape = {iC * kH * kW, oC};
|
||||
NDArray *reshapedW = weightsPermuted->reshape('f',wShape, false);
|
||||
NDArray *colpPReshapedAddr = colReshaped;
|
||||
|
||||
NDArray *reshapedWAddr = reshapedW;
|
||||
MmulHelper::matmul(colpPReshapedAddr, reshapedWAddr, &mmulResult, false, false, 1.0, 0.0);
|
||||
|
||||
// Clean up after matmul
|
||||
delete colReshaped;
|
||||
delete weightsPermuted;
|
||||
delete reshapedW;
|
||||
|
||||
std::vector<sd::LongType>lastShape = {oH,oW,bS,oC};
|
||||
NDArray *reshaped = mmulResult.reshape('f', lastShape, false);
|
||||
std::vector<sd::LongType> permute2 = {2,3,1,0};
|
||||
NDArray *permuted = reshaped->permute(permute2, false, false);
|
||||
|
||||
// Clean up reshaped after permute
|
||||
delete reshaped;
|
||||
|
||||
// Reshape and copy result to output
|
||||
if (isNCHW) {
|
||||
output->assign(permuted);
|
||||
delete permuted;
|
||||
} else {
|
||||
std::vector<sd::LongType> perm3 = {0,2,3,1};
|
||||
NDArray *oldPermuted = permuted; // Save old pointer before reassignment
|
||||
permuted = permuted->permute(perm3, false, false);
|
||||
output->assign(permuted);
|
||||
delete oldPermuted; // Delete the first permutation
|
||||
delete permuted; // Delete the second permutation
|
||||
}
|
||||
|
||||
// Clean up NHWC permutation if it was created
|
||||
if (inputNchw != nullptr) {
|
||||
delete inputNchw;
|
||||
}
|
||||
|
||||
//----- add biases if required -----//
|
||||
if (bias) {
|
||||
helpers::addBias(block, *output, *bias, *output, isNCHW);
|
||||
}
|
||||
|
||||
}
|
||||
void ConvolutionUtils::conv2d(sd::graph::Context& block, NDArray* input, NDArray* weights,
|
||||
NDArray* bias, NDArray* output, const LongType kH, const LongType kW, const LongType sH,
|
||||
const LongType sW, LongType pH, LongType pW, const LongType dH, const LongType dW, const int paddingMode,
|
||||
const int isNCHW, const int wFormat) {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
input->dataType(), conv2d_,
|
||||
(block, input, weights, bias, output, kH, kW, sH, sW, pH, pW, dH, dW, paddingMode, isNCHW, wFormat),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,176 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/MmulHelper.h>
|
||||
#include <ops/declarable/helpers/addBias.h>
|
||||
#include <ops/declarable/helpers/col2im.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <ops/declarable/helpers/im2col.h>
|
||||
|
||||
#include "helpers/ShapeUtils.h"
|
||||
#if NOT_EXCLUDED(OP_col2im) && NOT_EXCLUDED(OP_im2col)
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template <typename X, typename Y>
|
||||
static void conv2dBP_(sd::graph::Context& block, NDArray* input, NDArray* weights, NDArray* bias,
|
||||
NDArray* gradO, NDArray* gradI, NDArray* gradW, NDArray* gradB, const LongType kH, const LongType kW,
|
||||
const LongType sH, const LongType sW, LongType pH, LongType pW, const LongType dH, const LongType dW,
|
||||
const int paddingMode, const int isNCHW, const int wFormat) {
|
||||
|
||||
// input [bS, iH, iW, iC] (NHWC) or [bS, iC, iH, iW] (NCHW)
|
||||
// weights [kH, kW, iC, oC], [oC, iC, kH, kW], [oC, kH, kW, iC]
|
||||
// bias [oC]
|
||||
// gradO [bS, oH, oW, oC] (NHWC) or [bS, oC, oH, oW] (NCHW), epsilon_next
|
||||
// gradI [bS, iH, iW, iC] (NHWC) or [bS, iC, iH, iW] (NCHW), epsilon
|
||||
// gradW [kH, kW, iC, oC], [oC, iC, kH, kW], [oC, kH, kW, iC]
|
||||
// gradB [oC]
|
||||
|
||||
const LongType bS = input->sizeAt(0); // batch size
|
||||
const LongType iC = isNCHW ? input->sizeAt(1) : input->sizeAt(3); // input channels
|
||||
const LongType iH = isNCHW ? input->sizeAt(2) : input->sizeAt(1); // input height
|
||||
const LongType iW = isNCHW ? input->sizeAt(3) : input->sizeAt(2); // input width
|
||||
|
||||
const LongType oC = isNCHW ? gradO->sizeAt(1) : gradO->sizeAt(3); // output channels
|
||||
const LongType oH = isNCHW ? gradO->sizeAt(2) : gradO->sizeAt(1); // output height
|
||||
const LongType oW = isNCHW ? gradO->sizeAt(3) : gradO->sizeAt(2); // output width
|
||||
NDArray *inputPermuted, *gradOPermuted, *gradIPermuted;
|
||||
if (!isNCHW) {
|
||||
std::vector<sd::LongType> permute = {0, 3, 1, 2};
|
||||
inputPermuted = input->permute(permute, false, false); // [bS, iH, iW, iC] -> [bS, iC, iH, iW]
|
||||
gradOPermuted = gradO->permute(permute, false, false); // [bS, oH, oW, oC] -> [bS, oC, oH, oW]
|
||||
gradIPermuted = gradI->permute(permute, false, false); // [bS, iH, iW, iC] -> [bS, iC, iH, iW]
|
||||
} else {
|
||||
inputPermuted = input;
|
||||
gradOPermuted = gradO;
|
||||
gradIPermuted = gradI;
|
||||
}
|
||||
|
||||
std::vector<sd::LongType> gradOShape = {oC, bS * oH * oW};
|
||||
// Reshape gradO to 2D: [oC, bS * oH * oW]
|
||||
NDArray *gradO2d = gradOPermuted->reshape(gradOPermuted->ordering(), gradOShape,false);
|
||||
|
||||
// Perform im2col
|
||||
NDArray* columns;
|
||||
if (block.hasIntermediateResults()) {
|
||||
columns = block.intermediateResult(0);
|
||||
if (columns->rankOf() < 6) {
|
||||
columns->reshapei({bS, iC, kH, kW, oH, oW});
|
||||
}
|
||||
} else {
|
||||
std::vector<sd::LongType> colShape = {bS, iC, kH, kW, oH, oW};
|
||||
columns = new NDArray(inputPermuted->ordering(), colShape, inputPermuted->dataType(), inputPermuted->getContext());
|
||||
auto ctx = block.launchContext();
|
||||
NDArray *zeroVal = NDArrayFactory::create<double>(0., inputPermuted->getContext());
|
||||
helpers::im2col(*ctx, *inputPermuted, *columns, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
*zeroVal);
|
||||
delete zeroVal;
|
||||
}
|
||||
|
||||
// Calculate gradW
|
||||
if (gradW) {
|
||||
std::vector<sd::LongType> colShape = {bS * oH * oW, iC * kH * kW};
|
||||
std::vector<sd::LongType> wShape = {oC, iC * kH * kW};
|
||||
NDArray *columns2d = columns->reshape('c',colShape,false);
|
||||
std::vector<sd::LongType> permute = {1,0};
|
||||
NDArray *gradW2d = gradW->reshape('f', wShape, false)->permute(permute, false, false);
|
||||
|
||||
MmulHelper::matmul( columns2d,gradO2d, gradW2d, true, true, 1.0, 0.0, gradW2d);
|
||||
gradW->assign(gradW2d);
|
||||
delete columns2d;
|
||||
|
||||
}
|
||||
|
||||
// Calculate gradB
|
||||
if (gradB) {
|
||||
std::vector<LongType> axes = {1}; // Sum over bS, oH, oW
|
||||
gradO2d->reduceAlongDimension(reduce::Sum, gradB, &axes);
|
||||
}
|
||||
|
||||
// Calculate gradI
|
||||
NDArray *weights2d;
|
||||
if (wFormat == 0) {
|
||||
std::vector<sd::LongType> perm = {3,2,1,0};
|
||||
std::vector<sd::LongType> wShape = {iC * kH * kW,oC};
|
||||
weights2d = weights->permute(perm, false, false)->reshape('f', wShape);
|
||||
} else if (wFormat == 1) {
|
||||
std::vector<sd::LongType> wShape2 = {iC * kH * kW,oC};
|
||||
weights2d = weights->reshape('f', wShape2);
|
||||
} else {
|
||||
std::vector<sd::LongType> wPermute = {0,2,3,1};
|
||||
std::vector<sd::LongType> weights2dShape = {iC * kH * kW,oC};
|
||||
weights2d = weights->permute(wPermute, false, false)->reshape('f', weights2dShape);
|
||||
}
|
||||
|
||||
std::vector<sd::LongType> columns2dShape = {iC * kH * kW, bS * oH * oW};
|
||||
NDArray columns2d('c', columns2dShape, columns->dataType(), columns->getContext());
|
||||
|
||||
|
||||
MmulHelper::matmul(weights2d, gradO2d, &columns2d, false, false, 1.0, 0.0);
|
||||
delete weights2d;
|
||||
//Calculate epsilonNext by doing im2col reduction.
|
||||
//Current col2im implementation expects input with order: [miniBatch,channels,kH,kW,outH,outW]
|
||||
//currently have [kH,kW,inDepth,outW,outH,miniBatch] -> permute first
|
||||
auto eps6d = columns2d.newShapeNoCopy({kH, kW,iC, oW, oH, bS }, 'f');
|
||||
std::vector<sd::LongType> epsPermute = {5,2,1,0,4,3};
|
||||
auto permuted = eps6d->permute(epsPermute, false, false);
|
||||
|
||||
// Perform col2im
|
||||
auto ctx = block.launchContext();
|
||||
helpers::col2im(*ctx, permuted, gradIPermuted, sH, sW, pH, pW, iH, iW, dH, dW);
|
||||
// Handle NHWC format if necessary
|
||||
if (!isNCHW) {
|
||||
std::vector<sd::LongType> perm = {0,2,3,1};
|
||||
gradI->assign(gradIPermuted->permute(perm, false, false)); // [bS, iC, iH, iW] -> [bS, iH, iW, iC]
|
||||
}
|
||||
|
||||
delete gradO2d;
|
||||
// Clean up
|
||||
if (!isNCHW) {
|
||||
delete inputPermuted;
|
||||
delete gradOPermuted;
|
||||
delete gradIPermuted;
|
||||
}
|
||||
if (!block.hasIntermediateResults()) {
|
||||
delete columns;
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionUtils::conv2dBP(sd::graph::Context& block, NDArray* input, NDArray* weights,
|
||||
NDArray* bias, NDArray* gradO, NDArray* gradI, NDArray* gradW,
|
||||
NDArray* gradB, const LongType kH, const LongType kW, const LongType sH, const LongType sW, LongType pH, LongType pW,
|
||||
const LongType dH, const LongType dW, const int paddingMode, const int isNCHW,
|
||||
const int wFormat) {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(input->dataType(), conv2dBP_,
|
||||
(block, input, weights, bias, gradO, gradI, gradW, gradB, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
paddingMode, isNCHW, wFormat),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/MmulHelper.h>
|
||||
#include <ops/declarable/helpers/addBias.h>
|
||||
#include <ops/declarable/helpers/col2im.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <ops/declarable/helpers/im2col.h>
|
||||
#if NOT_EXCLUDED(OP_col2im) && NOT_EXCLUDED(OP_im2col)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void depthwiseConv2d_(sd::graph::Context& block, NDArray* input, NDArray* weights,
|
||||
NDArray* bias, NDArray* output, const LongType kH, const LongType kW, const LongType sH,
|
||||
const LongType sW, LongType pH, LongType pW, const LongType dH, const LongType dW, const int paddingMode,
|
||||
const int isNCHW, const int wFormat) {
|
||||
// input [bS, iH, iW, iC] (NHWC) or [bS, iC, iH, iW] (NCHW)
|
||||
// weights [kH, kW, iC, mC], [mC, iC, kH, kW], [mC, kH, kW, iC]
|
||||
// bias [oC] = iC*mC
|
||||
// output [bS, oH, oW, iC*mC] (NHWC) or [bS, iC*mC, oH, oW] (NCHW)
|
||||
|
||||
// kH filter(kernel) height
|
||||
// kW filter(kernel) width
|
||||
// sH strides height
|
||||
// sW strides width
|
||||
// pH paddings height
|
||||
// pW paddings width
|
||||
// dH dilations height
|
||||
// dW dilations width
|
||||
// paddingMode 0-VALID, 1-SAME
|
||||
// isNCHW 0-NCHW, 1-NHWC
|
||||
|
||||
LongType bS, iC, iH, iW, mC, oC, oH, oW; // batch size, input channels, input height/width, channels multiplier(oC =
|
||||
// iC*mC), output channels, output height/width
|
||||
LongType indIOioC, indIiH, indWmC, indWiC, indWkH, indOoH; // corresponding indexes
|
||||
ConvolutionUtils::getSizesAndIndexesConv2d(isNCHW, wFormat, *input, *output, bS, iC, iH, iW, oC, oH, oW, indIOioC,
|
||||
indIiH, indWiC, indWmC, indWkH, indOoH);
|
||||
mC = weights->sizeAt(indWmC); // channels multiplier
|
||||
|
||||
std::vector<std::vector<sd::LongType>> modifColumns = {
|
||||
{1, 0, 4, 5, 2, 3},
|
||||
{iC, bS * oH * oW, kH * kW}}; // [bS,iC,kH,kW,oH,oW] -> [iC,bS,oH,oW,kH,kW] -> [iC,bS*oH*oW,kH*kW]
|
||||
std::vector<std::vector<sd::LongType>> modifOutput, modifWeights;
|
||||
std::vector<sd::LongType> outReShape;
|
||||
|
||||
if (!isNCHW) {
|
||||
outReShape = {bS, oH, oW, iC, mC}; // [bS,oH,oW,iC*mC] -> [bS,oH,oW,iC,mC]
|
||||
modifOutput = {{3, 0, 1, 2, 4},
|
||||
{iC, bS * oH * oW, mC}}; // [bS,oH,oW,iC,mC] -> [iC,bS,oH,oW,mC] -> [iC,bS*oH*oW,mC]
|
||||
std::vector<sd::LongType> perm = {0, 3, 1, 2}; // [bS,iH,iW,iC] -> [bS,iC,iH,iW]
|
||||
input = input->permute(perm, false, false); // [bS,iH,iW,iC] -> [bS,iC,iH,iW]
|
||||
} else {
|
||||
outReShape = {bS, iC, mC, oH, oW}; // [bS,iC*mC,oH,oW] -> [bS,iC,mC,oH,oW]
|
||||
modifOutput = {{1, 0, 3, 4, 2},
|
||||
{iC, bS * oH * oW, mC}}; // [bS,iC,mC,oH,oW] -> [iC,bS,oH,oW,mC] -> [iC,bS*oH*oW,mC]
|
||||
}
|
||||
|
||||
if (0 == wFormat)
|
||||
modifWeights = {{2, 0, 1, 3}, {iC, kH * kW, mC}};
|
||||
else if (1 == wFormat)
|
||||
modifWeights = {{1, 2, 3, 0}, {iC, kH * kW, mC}};
|
||||
else
|
||||
modifWeights = {{3, 1, 2, 0}, {iC, kH * kW, mC}};
|
||||
|
||||
if (paddingMode == 1) // SAME
|
||||
ConvolutionUtils::calcPadding2D(pH, pW, oH, oW, iH, iW, kH, kW, sH, sW, dH, dW);
|
||||
|
||||
std::vector<sd::LongType> colShape = {bS, iC, kH, kW, oH, oW};
|
||||
NDArray columns(input->ordering(),colShape, input->dataType(), input->getContext());
|
||||
NDArray *outputReshaped = output->reshape(output->ordering(), outReShape, false);
|
||||
NDArray *zero = NDArrayFactory::create(0.f, input->getContext());
|
||||
helpers::im2col(
|
||||
*output->getContext(), *input, columns, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
*zero); // [bS, iC, iH, iW] is convoluted to [bS, iC, kH, kW, oH, oW]
|
||||
MmulHelper::tensorDot(&columns, weights, outputReshaped, modifColumns, modifWeights,
|
||||
modifOutput); // [iC, bS*oH*oW, kW*kH] x [iC, kH*kW, mC] = [iC, bS*oH*oW, mC]
|
||||
delete zero;
|
||||
|
||||
if (bias)
|
||||
helpers::addBias(block, *output, *bias, *output, isNCHW);
|
||||
|
||||
delete outputReshaped;
|
||||
if (!isNCHW) delete input;
|
||||
}
|
||||
|
||||
void ConvolutionUtils::depthwiseConv2d(sd::graph::Context& block, NDArray* input, NDArray* weights,
|
||||
NDArray* bias, NDArray* output, const LongType kH, const LongType kW, const LongType sH,
|
||||
const LongType sW, LongType pH, LongType pW, const LongType dH, const LongType dW, const int paddingMode,
|
||||
const int isNCHW, const int wFormat) {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
input->dataType(), depthwiseConv2d_,
|
||||
(block, input, weights, bias, output, kH, kW, sH, sW, pH, pW, dH, dW, paddingMode, isNCHW, wFormat),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/MmulHelper.h>
|
||||
#include <ops/declarable/helpers/col2im.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <ops/declarable/helpers/im2col.h>
|
||||
#if NOT_EXCLUDED(OP_col2im) && NOT_EXCLUDED(OP_im2col)
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void depthwiseConv2dBP_(NDArray* input, NDArray* weights, NDArray* bias, NDArray* gradO,
|
||||
NDArray* gradI, NDArray* gradW, NDArray* gradB, const LongType kH, const LongType kW, const LongType sH,
|
||||
const LongType sW, LongType pH, LongType pW, const LongType dH, const LongType dW, const int paddingMode,
|
||||
const int isNCHW, const int wFormat) {
|
||||
// input [bS, iH, iW, iC] (NDHWC) or [bS, iC, iH, iW] (NCDHW)
|
||||
// weights [kH, kW, iC, mC], [mC, iC, kH, kW], [mC, kH, kW, iC]
|
||||
// bias [oC] = [iC*mC]
|
||||
// gradO [bS, oH, oW, oC] (NDHWC) or [bS, oC, oH, oW] (NCDHW), epsilon_next
|
||||
// gradI [bS, iH, iW, iC] (NDHWC) or [bS, iC, iH, iW] (NCDHW), epsilon
|
||||
// gradW [kH, kW, iC, mC], [mC, iC, kH, kW], [mC, kH, kW, iC]
|
||||
// gradB [oC]
|
||||
|
||||
// kH filter(kernel) height
|
||||
// kW filter(kernel) width
|
||||
// sH strides height
|
||||
// sW strides width
|
||||
// pH paddings height
|
||||
// pW paddings width
|
||||
// dH dilations height
|
||||
// dW dilations width
|
||||
// paddingMode 0-VALID, 1-SAME
|
||||
// isNCHW 0-NHWC, 1-NCHW
|
||||
|
||||
LongType bS, iC, iH, iW, mC, oC, oH, oW; // batch size, input channels, input height/width, channels multiplier(oC =
|
||||
// iC*mC), output channels, output height/width
|
||||
LongType indIOioC, indIiH, indWmC, indWiC, indWkH, indOoH; // corresponding indexes
|
||||
ConvolutionUtils::getSizesAndIndexesConv2d(isNCHW, wFormat, *input, *gradO, bS, iC, iH, iW, oC, oH, oW, indIOioC,
|
||||
indIiH, indWiC, indWmC, indWkH, indOoH);
|
||||
mC = weights->sizeAt(indWmC); // channels multiplier
|
||||
|
||||
std::vector<std::vector<sd::LongType>> modifColumns = {
|
||||
{1, 2, 3, 0, 4, 5}, {iC, kH * kW, bS * oH * oW}}; // [bS,iC,kH,kW,oH,oW] -> [iC, kH*kW, bS*oH*oW]
|
||||
std::vector<std::vector<sd::LongType>> modifGradO1, modifGradO2, modifWeights;
|
||||
std::vector<sd::LongType> gradOreShape;
|
||||
|
||||
if (!isNCHW) {
|
||||
gradOreShape = {bS, oH, oW, iC, mC}; // [bS,oH,oW,iC*mC] -> [bS,oH,oW,iC,mC]
|
||||
modifGradO1 = {{3, 0, 1, 2, 4},
|
||||
{iC, bS * oH * oW, mC}}; // [bS,oH,oW,iC,mC] -> [iC,bS,oH,oW,mC] -> [iC,bS*oH*oW,mC]
|
||||
modifGradO2 = {{3, 0, 1, 2}, {iC, mC, bS * oH * oW}}; // [bS,oH,oW,iC*mC] -> [iC*mC,bS,oH,oW] -> [iC,mC,bS*oH*oW]
|
||||
std::vector<sd::LongType> perm = {0,3,1,2};
|
||||
input = input->permute(perm, false, false); // [bS,iH,iW,iC] -> [bS,iC,iH,iW]
|
||||
gradI = gradI->permute(perm, false, false); // [bS,iH,iW,iC] -> [bS,iC,iH,iW]
|
||||
} else {
|
||||
gradOreShape = {bS, iC, mC, oH, oW}; // [bS,iC*mC,oH,oW] -> [bS,iC,mC,oH,oW]
|
||||
modifGradO1 = {{1, 0, 3, 4, 2},
|
||||
{iC, bS * oH * oW, mC}}; // [bS,iC,mC,oH,oW] -> [iC,bS,oH,oW,mC] -> [iC,bS*oH*oW,mC]
|
||||
modifGradO2 = {{1, 0, 2, 3}, {iC, mC, bS * oH * oW}}; // [bS,iC*mC,oH,oW] -> [iC*mC,bS,oH,oW] -> [iC,mC,bS*oH*oW]
|
||||
}
|
||||
|
||||
if (0 == wFormat)
|
||||
modifWeights = {{2, 0, 1, 3}, {iC, kH * kW, mC}};
|
||||
else if (1 == wFormat)
|
||||
modifWeights = {{1, 2, 3, 0}, {iC, kH * kW, mC}};
|
||||
else
|
||||
modifWeights = {{3, 1, 2, 0}, {iC, kH * kW, mC}};
|
||||
|
||||
if (paddingMode == 1) // SAME
|
||||
ConvolutionUtils::calcPadding2D(pH, pW, oH, oW, iH, iW, kH, kW, sH, sW, dH, dW);
|
||||
|
||||
std::vector<LongType> colShape = {bS, iC, kH, kW, oH, oW};
|
||||
NDArray columns(input->ordering(), colShape, input->dataType(), input->getContext());
|
||||
NDArray *gradOreshaped = gradO->reshape(gradO->ordering(), gradOreShape);
|
||||
|
||||
// ----- calculation of gradW and gradB ----- //
|
||||
NDArray *zero = NDArrayFactory::create(0.f, input->getContext());
|
||||
helpers::im2col(
|
||||
*input->getContext(), *input, columns, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
*zero); // [bS, iC, iH, iW] is convoluted to [bS, iC, kH, kW, oH, oW]
|
||||
sd::MmulHelper::tensorDot(&columns, gradOreshaped, gradW, modifColumns, modifGradO1,
|
||||
modifWeights); // [iC, kW*kH, bS*oH*oW] x [iC, bS*oH*oW, mC] = [iC, kH*kW, mC]
|
||||
|
||||
delete zero;
|
||||
// ----- calculation of gradB ----- //
|
||||
if (gradB) {
|
||||
NDArray* gradBR = gradB;
|
||||
std::vector<LongType> shape = {gradB->lengthOf()};
|
||||
if (gradB->rankOf() == 2) gradBR =gradB->reshape(gradB->ordering(), shape, false);
|
||||
std::vector<sd::LongType> axes = {0, indOoH, indOoH + 1};
|
||||
gradO->reduceAlongDimension(reduce::Sum, gradBR, &axes); // sum over bS, oH, oW
|
||||
|
||||
if (gradBR != gradB) delete gradBR;
|
||||
}
|
||||
|
||||
//----- calculation of gradI -----//
|
||||
sd::MmulHelper::tensorDot(weights, gradO, &columns, modifWeights, modifGradO2,
|
||||
modifColumns); // [iC, kH*kW, mC] x [iC, mC, bS*oH*oW] = [iC, kW*kH, bS*oH*oW]
|
||||
helpers::col2im(*input->getContext(), &columns, gradI, sH, sW, pH, pW, iH, iW, dH,
|
||||
dW); // [bS, iC, kH, kW, oH, oW] is de-convoluted to [bS, iC, iH, iW]
|
||||
|
||||
if (!isNCHW) {
|
||||
delete input;
|
||||
delete gradI;
|
||||
}
|
||||
|
||||
delete gradOreshaped;
|
||||
}
|
||||
|
||||
void ConvolutionUtils::depthwiseConv2dBP(graph::Context& block, NDArray* input, NDArray* weights,
|
||||
NDArray* bias, NDArray* gradO, NDArray* gradI, NDArray* gradW,
|
||||
NDArray* gradB, const LongType kH, const LongType kW, const LongType sH, const LongType sW, LongType pH,
|
||||
LongType pW, const LongType dH, const LongType dW, const int paddingMode, const int isNCHW,
|
||||
const int wFormat) {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
input->dataType(), depthwiseConv2dBP_,
|
||||
(input, weights, bias, gradO, gradI, gradW, gradB, kH, kW, sH, sW, pH, pW, dH, dW, paddingMode, isNCHW, wFormat),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,236 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <stdexcept>
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void pooling2d_(sd::graph::Context& block, NDArray& input, NDArray& output, const LongType kH, const LongType kW,
|
||||
const LongType sH, const LongType sW, const LongType pH, const LongType pW, const LongType dH, const LongType dW,
|
||||
const int poolingMode, const int extraParam0) {
|
||||
// Cache shape information
|
||||
const auto inShapeInfo = input.shapeInfo();
|
||||
const auto outShapeInfo = output.shapeInfo();
|
||||
|
||||
// Cache input dimensions
|
||||
const auto* inShape = shape::shapeOf(inShapeInfo);
|
||||
const LongType bS = inShape[0];
|
||||
const LongType iC = inShape[1];
|
||||
const LongType iH = inShape[2];
|
||||
const LongType iW = inShape[3];
|
||||
|
||||
// Cache output dimensions
|
||||
const auto* outShape = shape::shapeOf(outShapeInfo);
|
||||
const LongType oH = outShape[2];
|
||||
const LongType oW = outShape[3];
|
||||
|
||||
// Cache strides
|
||||
const auto* inStride = shape::stride(inShapeInfo);
|
||||
const auto* outStride = shape::stride(outShapeInfo);
|
||||
|
||||
const sd::LongType iStride0 = inStride[0];
|
||||
const sd::LongType iStride1 = inStride[1];
|
||||
const sd::LongType iStride2 = inStride[2];
|
||||
const sd::LongType iStride3 = inStride[3];
|
||||
const sd::LongType oStride0 = outStride[0];
|
||||
const sd::LongType oStride1 = outStride[1];
|
||||
const sd::LongType oStride2 = outStride[2];
|
||||
const sd::LongType oStride3 = outStride[3];
|
||||
|
||||
T* out = output.bufferAsT<T>();
|
||||
T* in = const_cast<NDArray&>(input).bufferAsT<T>();
|
||||
|
||||
const int kHEff = kH + (kH - 1) * (dH - 1);
|
||||
const int kWEff = kW + (kW - 1) * (dW - 1);
|
||||
|
||||
const sd::LongType iStep2 = dH * iStride2;
|
||||
const sd::LongType iStep3 = dW * iStride3;
|
||||
const int kProd = kH * kW;
|
||||
|
||||
if (poolingMode == 0) { // max
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType hstart, wstart, hend, wend;
|
||||
T* pIn;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0)
|
||||
hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0)
|
||||
wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (hend > iH)
|
||||
hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW)
|
||||
wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
hstart *= iStride2;
|
||||
hend *= iStride2;
|
||||
wstart *= iStride3;
|
||||
wend *= iStride3;
|
||||
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep2)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep3) {
|
||||
T val = pIn[kh + kw];
|
||||
if (val > max) max = val;
|
||||
}
|
||||
out[b * oStride0 + c * oStride1 + oh * oStride2 + ow * oStride3] = max;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
}
|
||||
/*************************************************************************/
|
||||
else if (poolingMode == 1) { // avg
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType hstart, wstart, hend, wend;
|
||||
T* pIn;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0)
|
||||
hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0)
|
||||
wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (hend > iH)
|
||||
hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW)
|
||||
wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
hstart *= iStride2;
|
||||
hend *= iStride2;
|
||||
wstart *= iStride3;
|
||||
wend *= iStride3;
|
||||
|
||||
T sum = static_cast<T>(0.f);
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep2)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep3)
|
||||
sum += pIn[kh + kw];
|
||||
|
||||
if (extraParam0 == 0) { // Exclude padding
|
||||
int a = (hend - hstart) / iStep2 + ((hend - hstart) % iStep2 == 0 ? 0 : 1);
|
||||
int r = (wend - wstart) / iStep3 + ((wend - wstart) % iStep3 == 0 ? 0 : 1);
|
||||
sum /= static_cast<T>(a * r); // Accounts for dilation
|
||||
} else if (extraParam0 == 1) // Include padding
|
||||
sum /= kProd;
|
||||
|
||||
out[b * oStride0 + c * oStride1 + oh * oStride2 + ow * oStride3] = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
}
|
||||
/*************************************************************************/
|
||||
else if (poolingMode == 2) { // pnorm
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType hstart, wstart, hend, wend;
|
||||
T* pIn;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0)
|
||||
hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0)
|
||||
wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (hend > iH)
|
||||
hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW)
|
||||
wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
hstart *= iStride2;
|
||||
hend *= iStride2;
|
||||
wstart *= iStride3;
|
||||
wend *= iStride3;
|
||||
|
||||
T sum = static_cast<T>(0.f);
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep2)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep3)
|
||||
sum += sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(pIn[kh + kw]), static_cast<T>(extraParam0));
|
||||
|
||||
sum = sd::math::sd_pow<T, T, T>(sum, static_cast<T>((T)1.f) / extraParam0);
|
||||
|
||||
out[b * oStride0 + c * oStride1 + oh * oStride2 + ow * oStride3] = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
} else {
|
||||
char errorMsg[512];
|
||||
snprintf(errorMsg, sizeof(errorMsg),
|
||||
"ConvolutionUtils::pooling2d: pooling mode argument can take three values only: 0, 1, 2, but got %i instead!",
|
||||
poolingMode);
|
||||
THROW_EXCEPTION(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionUtils::pooling2d(sd::graph::Context& block, NDArray& input, NDArray& output, const LongType kH,
|
||||
const LongType kW, const LongType sH, const LongType sW, const LongType pH, const LongType pW, const LongType dH,
|
||||
const LongType dW, const PoolingType poolingMode, const int extraParam0) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), pooling2d_,
|
||||
(block, input, output, kH, kW, sH, sW, pH, pW, dH, dW, poolingMode, extraParam0),
|
||||
SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,317 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void pooling2dBP_(sd::graph::Context& block, NDArray& input, NDArray& gradO, NDArray& gradI,
|
||||
const LongType kH, const LongType kW, const LongType sH, const LongType sW, const LongType pH, const LongType pW,
|
||||
const LongType dH, const LongType dW, const int poolingMode, const int extraParam0) {
|
||||
// input [bS, iC, iH, iW]
|
||||
// gradI [bS, iC, iH, iW] -> gradI is output in this function
|
||||
// gradO [bS, iC, oH, oW]
|
||||
|
||||
// initial zeroing of gradI
|
||||
gradI.nullify();
|
||||
|
||||
T* in = const_cast<NDArray&>(input).bufferAsT<T>();
|
||||
T* gO = const_cast<NDArray&>(gradO).bufferAsT<T>();
|
||||
T* gI = gradI.bufferAsT<T>();
|
||||
|
||||
const int kHEff = kH + (kH - 1) * (dH - 1);
|
||||
const int kWEff = kW + (kW - 1) * (dW - 1);
|
||||
|
||||
const int bS = gradI.sizeAt(0);
|
||||
const int iC = gradI.sizeAt(1);
|
||||
const int iH = gradI.sizeAt(2);
|
||||
const int iW = gradI.sizeAt(3);
|
||||
const int oC = gradO.sizeAt(1);
|
||||
const int oH = gradO.sizeAt(2);
|
||||
const int oW = gradO.sizeAt(3);
|
||||
|
||||
// sd_debug("MKL-DNN is not used for pooling2d_bp!\n", 0);
|
||||
|
||||
const sd::LongType iStride0 = input.stridesOf()[0];
|
||||
const sd::LongType iStride1 = input.stridesOf()[1];
|
||||
const sd::LongType iStride2 = input.stridesOf()[2];
|
||||
const sd::LongType iStride3 = input.stridesOf()[3];
|
||||
const sd::LongType gIStride0 = gradI.stridesOf()[0];
|
||||
const sd::LongType gIStride1 = gradI.stridesOf()[1];
|
||||
const sd::LongType gIStride2 = gradI.stridesOf()[2];
|
||||
const sd::LongType gIStride3 = gradI.stridesOf()[3];
|
||||
const sd::LongType oStride0 = gradO.stridesOf()[0];
|
||||
const sd::LongType oStride1 = gradO.stridesOf()[1];
|
||||
const sd::LongType oStride2 = gradO.stridesOf()[2];
|
||||
const sd::LongType oStride3 = gradO.stridesOf()[3];
|
||||
const sd::LongType iStep2 = dH * iStride2;
|
||||
const sd::LongType iStep3 = dW * iStride3;
|
||||
const sd::LongType gIStep2 = dH * gIStride2;
|
||||
const sd::LongType gIStep3 = dW * gIStride3;
|
||||
const int kProd = kH * kW;
|
||||
|
||||
const bool sameStrides =
|
||||
iStride0 == gIStride0 && iStride1 == gIStride1 && iStride2 == gIStride2 && iStride3 == gIStride3;
|
||||
|
||||
if (poolingMode == 0) { // max
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType hstart, wstart, hend, wend, maxKH, maxKW;
|
||||
T sum, valO, *pIn, *pgI;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0)
|
||||
hstart +=
|
||||
dH * ((-hstart + dH - 1) /
|
||||
dH);
|
||||
if (wstart < 0)
|
||||
wstart +=
|
||||
dW * ((-wstart + dW - 1) /
|
||||
dW);
|
||||
if (hend > iH)
|
||||
hend -=
|
||||
dH * ((hend - iH + dH - 1) /
|
||||
dH);
|
||||
if (wend > iW)
|
||||
wend -=
|
||||
dW * ((wend - iW + dW - 1) /
|
||||
dW);
|
||||
|
||||
sum = -DataTypeUtils::max<T>();
|
||||
valO = gO[b * oStride0 + c * oStride1 + oh * oStride2 + ow * oStride3];
|
||||
|
||||
if (sameStrides) {
|
||||
hstart *= iStride2;
|
||||
hend *= iStride2;
|
||||
wstart *= iStride3;
|
||||
wend *= iStride3;
|
||||
|
||||
// we set these to default values
|
||||
maxKH = hstart;
|
||||
maxKW = wstart;
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep2)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep3) {
|
||||
T valIn = pIn[kh + kw];
|
||||
if (valIn > sum) {
|
||||
sum = valIn;
|
||||
maxKH = kh;
|
||||
maxKW = kw;
|
||||
}
|
||||
}
|
||||
gI[pIn - in + maxKH + maxKW] += valO;
|
||||
} else {
|
||||
// we set these to default values
|
||||
maxKH = hstart;
|
||||
maxKW = wstart;
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += dH)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += dW) {
|
||||
T valIn = pIn[kh * iStride2 + kw * iStride3];
|
||||
if (valIn > sum) {
|
||||
sum = valIn;
|
||||
maxKH = kh;
|
||||
maxKW = kw;
|
||||
}
|
||||
}
|
||||
|
||||
gI[b * gIStride0 + c * gIStride1 + maxKH * gIStride2 + maxKW * gIStride3] += valO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
}
|
||||
/*************************************************************************/
|
||||
else if (poolingMode == 1) { // avg
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType hstart, wstart, hend, wend, maxKH, maxKW;
|
||||
T sum, valO, *pIn, *pgI;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pgI = gI + b * gIStride0 + c * gIStride1;
|
||||
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0)
|
||||
hstart +=
|
||||
dH * ((-hstart + dH - 1) /
|
||||
dH); // (sd::LongType)sd::math::sd_ceil<T,T>(static_cast<T>(-hstart) / static_cast<T>(dH));
|
||||
if (wstart < 0)
|
||||
wstart +=
|
||||
dW * ((-wstart + dW - 1) /
|
||||
dW); //(sd::LongType)sd::math::sd_ceil<T,T>(static_cast<T>(-wstart) / static_cast<T>(dW));
|
||||
if (hend > iH)
|
||||
hend -=
|
||||
dH * ((hend - iH + dH - 1) /
|
||||
dH); //(sd::LongType)sd::math::sd_ceil<T,T>(static_cast<T>(hend-iH) / static_cast<T>(dH));
|
||||
if (wend > iW)
|
||||
wend -=
|
||||
dW * ((wend - iW + dW - 1) /
|
||||
dW); //(sd::LongType)sd::math::sd_ceil<T,T>(static_cast<T>(wend-iW) / static_cast<T>(dW));
|
||||
|
||||
hstart *= gIStride2;
|
||||
hend *= gIStride2;
|
||||
wstart *= gIStride3;
|
||||
wend *= gIStride3;
|
||||
|
||||
valO = gO[b * oStride0 + c * oStride1 + oh * oStride2 + ow * oStride3];
|
||||
|
||||
if ((int)extraParam0 == 0) // Exclude padding
|
||||
valO /=
|
||||
static_cast<T>(sd::math::sd_ceil<double, T>(static_cast<double>(hend - hstart) /
|
||||
static_cast<double>(gIStep2))) *
|
||||
static_cast<T>(sd::math::sd_ceil<double, T>(
|
||||
static_cast<double>(wend - wstart) / static_cast<double>(gIStep3))); // Accounts for dilation
|
||||
else if ((int)extraParam0 == 1) // Include padding
|
||||
valO /= kProd;
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += gIStep2)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += gIStep3) pgI[kh + kw] += valO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
}
|
||||
/*************************************************************************/
|
||||
else if (poolingMode == 2) { // pnorm
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType hstart, wstart, hend, wend, maxKH, maxKW;
|
||||
T sum, valO, *pIn, *pgI;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
pgI = sameStrides ? gI + (pIn - in) : gI + b * gIStride0 + c * gIStride1;
|
||||
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0)
|
||||
hstart +=
|
||||
dH * ((-hstart + dH - 1) /
|
||||
dH); // (sd::LongType)sd::math::sd_ceil<T,T>(static_cast<T>(-hstart) / static_cast<T>(dH));
|
||||
if (wstart < 0)
|
||||
wstart +=
|
||||
dW * ((-wstart + dW - 1) /
|
||||
dW); //(sd::LongType)sd::math::sd_ceil<T,T>(static_cast<T>(-wstart) / static_cast<T>(dW));
|
||||
if (hend > iH)
|
||||
hend -=
|
||||
dH * ((hend - iH + dH - 1) /
|
||||
dH); //(sd::LongType)sd::math::sd_ceil<T,T>(static_cast<T>(hend-iH) / static_cast<T>(dH));
|
||||
if (wend > iW)
|
||||
wend -=
|
||||
dW * ((wend - iW + dW - 1) /
|
||||
dW); //(sd::LongType)sd::math::sd_ceil<T,T>(static_cast<T>(wend-iW) / static_cast<T>(dW));
|
||||
|
||||
sum = static_cast<T>(0.f);
|
||||
valO = gO[b * oStride0 + c * oStride1 + oh * oStride2 + ow * oStride3];
|
||||
|
||||
if (sameStrides) {
|
||||
hstart *= iStride2;
|
||||
hend *= iStride2;
|
||||
wstart *= iStride3;
|
||||
wend *= iStride3;
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep2)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep3)
|
||||
sum += sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(pIn[kh + kw]), static_cast<T>(extraParam0));
|
||||
|
||||
valO *= sd::math::sd_pow<T, T, T>(sum, ((T)1. - extraParam0) / extraParam0);
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep2)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep3)
|
||||
pgI[kh + kw] += valO *
|
||||
sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(pIn[kh + kw]), static_cast<T>(extraParam0) - 1.f) *
|
||||
sd::math::sd_sgn<T, T>(pIn[kh + kw]);
|
||||
} else {
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += dH)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += dW)
|
||||
sum +=
|
||||
sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(pIn[kh * iStride2 + kw * iStride3]), static_cast<T>(extraParam0));
|
||||
|
||||
valO *= sd::math::sd_pow<T, T, T>(sum, ((T)1. - extraParam0) / extraParam0);
|
||||
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += dH) {
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += dW) {
|
||||
const auto inVal = pIn[kh * iStride2 + kw * iStride3];
|
||||
pgI[kh * gIStride2 + kw * gIStride3] +=
|
||||
valO * sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(inVal), static_cast<T>(extraParam0) - static_cast<T>(1.f)) *
|
||||
sd::math::sd_sgn<T, T>(inVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
} else {
|
||||
sd_printf(
|
||||
"ConvolutionUtils::pooling2dBP: pooling mode argument can take three values only: 0, 1, 2, but got %i instead "
|
||||
"!\n",
|
||||
poolingMode);
|
||||
THROW_EXCEPTION("Incorrect pooling2dBP mode");
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionUtils::pooling2dBP(sd::graph::Context& block, NDArray& input, NDArray& gradO,
|
||||
NDArray& gradI, const LongType kH, const LongType kW, const LongType sH, const LongType sW, const LongType pH,
|
||||
const LongType pW, const LongType dH, const LongType dW, const int poolingMode,
|
||||
const int extraParam0) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), pooling2dBP_,
|
||||
(block, input, gradO, gradI, kH, kW, sH, sW, pH, pW, dH, dW, poolingMode, extraParam0),
|
||||
SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,251 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void pooling3d_(sd::graph::Context& block, NDArray& input, NDArray& output, const LongType kD, const LongType kH,
|
||||
const LongType kW, const LongType sD, const LongType sH, const LongType sW, const LongType pD, const LongType pH, const LongType pW,
|
||||
const LongType dD, const LongType dH, const LongType dW, const LongType poolingMode, const int extraParam0) {
|
||||
// input is [bS, iC, iD, iH, iW]
|
||||
// output is [bS, iC, oD, oH, oW]
|
||||
T* out = output.bufferAsT<T>();
|
||||
T* in = const_cast<NDArray&>(input).bufferAsT<T>();
|
||||
|
||||
const int kDEff = kD + (kD - 1) * (dD - 1);
|
||||
const int kHEff = kH + (kH - 1) * (dH - 1);
|
||||
const int kWEff = kW + (kW - 1) * (dW - 1);
|
||||
|
||||
const int bS = input.sizeAt(0);
|
||||
const int iC = input.sizeAt(1);
|
||||
const int iD = input.sizeAt(2);
|
||||
const int iH = input.sizeAt(3);
|
||||
const int iW = input.sizeAt(4);
|
||||
const int oC = output.sizeAt(1);
|
||||
const int oD = output.sizeAt(2);
|
||||
const int oH = output.sizeAt(3);
|
||||
const int oW = output.sizeAt(4);
|
||||
|
||||
sd_debug("MKL-DNN is not used for pooling3d!\n", 0);
|
||||
|
||||
const sd::LongType iStride0 = input.stridesOf()[0];
|
||||
const sd::LongType iStride1 = input.stridesOf()[1];
|
||||
const sd::LongType iStride2 = input.stridesOf()[2];
|
||||
const sd::LongType iStride3 = input.stridesOf()[3];
|
||||
const sd::LongType iStride4 = input.stridesOf()[4];
|
||||
const sd::LongType oStride0 = output.stridesOf()[0];
|
||||
const sd::LongType oStride1 = output.stridesOf()[1];
|
||||
const sd::LongType oStride2 = output.stridesOf()[2];
|
||||
const sd::LongType oStride3 = output.stridesOf()[3];
|
||||
const sd::LongType oStride4 = output.stridesOf()[4];
|
||||
const sd::LongType iStep2 = dD * iStride2;
|
||||
const sd::LongType iStep3 = dH * iStride3;
|
||||
const sd::LongType iStep4 = dW * iStride4;
|
||||
const int kProd = kD * kH * kW;
|
||||
|
||||
if (poolingMode == 0) { // max
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
sd::LongType dstart, hstart, wstart, dend, hend, wend;
|
||||
T sum, *pIn;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int od = start_z; od < stop_z; od += inc_z) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
|
||||
dstart = od * sD - pD;
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
dend = dstart + kDEff;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (dstart < 0) dstart += dD * ((-dstart + dD - 1) / dD);
|
||||
if (hstart < 0) hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0) wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (dend > iD) dend -= dD * ((dend - iD + dD - 1) / dD);
|
||||
if (hend > iH) hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW) wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
dstart *= iStride2;
|
||||
dend *= iStride2;
|
||||
hstart *= iStride3;
|
||||
hend *= iStride3;
|
||||
wstart *= iStride4;
|
||||
wend *= iStride4;
|
||||
|
||||
sum = -DataTypeUtils::max<T>();
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += iStep2)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep3)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep4) {
|
||||
T val = pIn[kd + kh + kw];
|
||||
if (val > sum) sum = val;
|
||||
}
|
||||
|
||||
out[b * oStride0 + c * oStride1 + od * oStride2 + oh * oStride3 + ow * oStride4] = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1, 0, oD, 1);
|
||||
}
|
||||
/*************************************************************************/
|
||||
else if (poolingMode == 1) { // avg
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
sd::LongType dstart, hstart, wstart, dend, hend, wend;
|
||||
T sum, *pIn;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int od = start_z; od < stop_z; od += inc_z) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
|
||||
dstart = od * sD - pD;
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
dend = dstart + kDEff;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (dstart < 0) dstart += dD * ((-dstart + dD - 1) / dD);
|
||||
if (hstart < 0) hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0) wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (dend > iD) dend -= dD * ((dend - iD + dD - 1) / dD);
|
||||
if (hend > iH) hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW) wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
dstart *= iStride2;
|
||||
dend *= iStride2;
|
||||
hstart *= iStride3;
|
||||
hend *= iStride3;
|
||||
wstart *= iStride4;
|
||||
wend *= iStride4;
|
||||
|
||||
sum = static_cast<T>(0.);
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += iStep2)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep3)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep4) sum += pIn[kd + kh + kw];
|
||||
|
||||
if (extraParam0 == 0) // Exclude padding
|
||||
sum /=
|
||||
sd::math::sd_ceil<double, T>(static_cast<double>(dend - dstart) / static_cast<double>(iStep2)) *
|
||||
sd::math::sd_ceil<double, T>(static_cast<double>(hend - hstart) / static_cast<double>(iStep3)) *
|
||||
sd::math::sd_ceil<double, T>(static_cast<double>(wend - wstart) /
|
||||
static_cast<double>(iStep4)); // Accounts for dilation
|
||||
else if (extraParam0 == 1) // Include padding
|
||||
sum /= kProd;
|
||||
|
||||
out[b * oStride0 + c * oStride1 + od * oStride2 + oh * oStride3 + ow * oStride4] = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1, 0, oD, 1);
|
||||
}
|
||||
/*************************************************************************/
|
||||
else if (poolingMode == 2) { // pnorm
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
sd::LongType dstart, hstart, wstart, dend, hend, wend;
|
||||
T sum, *pIn;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int od = start_z; od < stop_z; od += inc_z) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
|
||||
dstart = od * sD - pD;
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
dend = dstart + kDEff;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (dstart < 0) dstart += dD * ((-dstart + dD - 1) / dD);
|
||||
if (hstart < 0) hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0) wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (dend > iD) dend -= dD * ((dend - iD + dD - 1) / dD);
|
||||
if (hend > iH) hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW) wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
dstart *= iStride2;
|
||||
dend *= iStride2;
|
||||
hstart *= iStride3;
|
||||
hend *= iStride3;
|
||||
wstart *= iStride4;
|
||||
wend *= iStride4;
|
||||
|
||||
sum = static_cast<T>(0.);
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += iStep2)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep3)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep4)
|
||||
sum += sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(pIn[kd + kh + kw]), static_cast<T>(extraParam0));
|
||||
|
||||
sum = sd::math::sd_pow<T, T, T>(sum, (T)1.f / extraParam0);
|
||||
|
||||
out[b * oStride0 + c * oStride1 + od * oStride2 + oh * oStride3 + ow * oStride4] = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1, 0, oD, 1);
|
||||
} else {
|
||||
sd_printf(
|
||||
"ConvolutionUtils::pooling3d: pooling mode argument can take three values only: 0, 1, 2, but got %i instead "
|
||||
"!\n",
|
||||
poolingMode);
|
||||
THROW_EXCEPTION("Incorrect poooling3d mode");
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionUtils::pooling3d(sd::graph::Context& block, NDArray& input, NDArray& output, const LongType kD,
|
||||
const LongType kH, const LongType kW, const LongType sD, const LongType sH, const LongType sW, const LongType pD,
|
||||
const LongType pH, const LongType pW, const LongType dD, const LongType dH, const LongType dW,
|
||||
const int poolingMode, const int extraParam0) {
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input.dataType(), pooling3d_,
|
||||
(block, input, output, kD, kH, kW, sD, sH, sW, pD, pH, pW, dD, dH, dW, poolingMode, extraParam0), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,325 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void pooling3dBP_(sd::graph::Context& block, NDArray& input, NDArray& gradO, NDArray& gradI,
|
||||
const LongType kD, const LongType kH, const LongType kW, const LongType sD, const LongType sH, const LongType sW,
|
||||
const LongType pD, const LongType pH, const LongType pW, const LongType dD, const LongType dH, const LongType dW,
|
||||
const int poolingMode, const int extraParam0) {
|
||||
// input [bS, iC, iD, iH, iW]
|
||||
// gradI [bS, iC, iD, iH, iW] -> gradI is output in this function
|
||||
// gradO [bS, iC, oD, oH, oW]
|
||||
|
||||
// initial zeroing of gradI
|
||||
gradI.nullify();
|
||||
|
||||
T* in = const_cast<NDArray&>(input).bufferAsT<T>();
|
||||
T* gO = const_cast<NDArray&>(gradO).bufferAsT<T>();
|
||||
T* gI = gradI.bufferAsT<T>();
|
||||
|
||||
const int kDEff = kD + (kD - 1) * (dD - 1);
|
||||
const int kHEff = kH + (kH - 1) * (dH - 1);
|
||||
const int kWEff = kW + (kW - 1) * (dW - 1);
|
||||
|
||||
const int bS = gradI.sizeAt(0);
|
||||
const int iC = gradI.sizeAt(1);
|
||||
const int iD = gradI.sizeAt(2);
|
||||
const int iH = gradI.sizeAt(3);
|
||||
const int iW = gradI.sizeAt(4);
|
||||
const int oC = gradO.sizeAt(1);
|
||||
const int oD = gradO.sizeAt(2);
|
||||
const int oH = gradO.sizeAt(3);
|
||||
const int oW = gradO.sizeAt(4);
|
||||
|
||||
sd_debug("MKL-DNN is not used for pooling3d_bp!\n", 0);
|
||||
|
||||
const sd::LongType iStride0 = input.stridesOf()[0];
|
||||
const sd::LongType iStride1 = input.stridesOf()[1];
|
||||
const sd::LongType iStride2 = input.stridesOf()[2];
|
||||
const sd::LongType iStride3 = input.stridesOf()[3];
|
||||
const sd::LongType iStride4 = input.stridesOf()[4];
|
||||
const sd::LongType gIStride0 = gradI.stridesOf()[0];
|
||||
const sd::LongType gIStride1 = gradI.stridesOf()[1];
|
||||
const sd::LongType gIStride2 = gradI.stridesOf()[2];
|
||||
const sd::LongType gIStride3 = gradI.stridesOf()[3];
|
||||
const sd::LongType gIStride4 = gradI.stridesOf()[4];
|
||||
const sd::LongType oStride0 = gradO.stridesOf()[0];
|
||||
const sd::LongType oStride1 = gradO.stridesOf()[1];
|
||||
const sd::LongType oStride2 = gradO.stridesOf()[2];
|
||||
const sd::LongType oStride3 = gradO.stridesOf()[3];
|
||||
const sd::LongType oStride4 = gradO.stridesOf()[4];
|
||||
const sd::LongType iStep2 = dD * iStride2;
|
||||
const sd::LongType iStep3 = dH * iStride3;
|
||||
const sd::LongType iStep4 = dW * iStride4;
|
||||
const sd::LongType gIStep2 = dD * gIStride2;
|
||||
const sd::LongType gIStep3 = dH * gIStride3;
|
||||
const sd::LongType gIStep4 = dW * gIStride4;
|
||||
const int kProd = kD * kH * kW;
|
||||
|
||||
const bool sameStrides = iStride0 == gIStride0 && iStride1 == gIStride1 && iStride2 == gIStride2 &&
|
||||
iStride3 == gIStride3 && iStride4 == gIStride4;
|
||||
|
||||
if (poolingMode == 0) { // max
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType dstart, hstart, wstart, dend, hend, wend, maxKD, maxKH, maxKW;
|
||||
T sum, valO, *pIn, *pgI;
|
||||
|
||||
for (int b = start_x; b < stop_x; b++) {
|
||||
for (int c = start_y; c < stop_y; c++) {
|
||||
for (int od = 0; od < oD; od++) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
|
||||
dstart = od * sD - pD;
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
dend = dstart + kDEff;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (dstart < 0) dstart += dD * ((-dstart + dD - 1) / dD);
|
||||
if (hstart < 0) hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0) wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (dend > iD) dend -= dD * ((dend - iD + dD - 1) / dD);
|
||||
if (hend > iH) hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW) wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
sum = -DataTypeUtils::max<T>();
|
||||
valO = gO[b * oStride0 + c * oStride1 + od * oStride2 + oh * oStride3 + ow * oStride4];
|
||||
|
||||
if (sameStrides) {
|
||||
dstart *= iStride2;
|
||||
dend *= iStride2;
|
||||
hstart *= iStride3;
|
||||
hend *= iStride3;
|
||||
wstart *= iStride4;
|
||||
wend *= iStride4;
|
||||
|
||||
maxKD = dstart;
|
||||
maxKH = hstart;
|
||||
maxKW = wstart;
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += iStep2)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep3)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep4) {
|
||||
T valIn = pIn[kd + kh + kw];
|
||||
if (valIn > sum) {
|
||||
sum = valIn;
|
||||
maxKD = kd;
|
||||
maxKH = kh;
|
||||
maxKW = kw;
|
||||
}
|
||||
}
|
||||
gI[pIn - in + maxKD + maxKH + maxKW] += valO;
|
||||
} else {
|
||||
// we set these to default values
|
||||
maxKH = hstart;
|
||||
maxKW = wstart;
|
||||
maxKD = dstart;
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += dD)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += dH)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += dW) {
|
||||
T valIn = pIn[kd * iStride2 + kh * iStride3 + kw * iStride4];
|
||||
if (valIn > sum) {
|
||||
sum = valIn;
|
||||
maxKD = kd;
|
||||
maxKH = kh;
|
||||
maxKW = kw;
|
||||
}
|
||||
}
|
||||
|
||||
gI[b * gIStride0 + c * gIStride1 + maxKD * gIStride2 + maxKH * gIStride3 + maxKW * gIStride4] += valO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
}
|
||||
/*************************************************************************/
|
||||
else if (poolingMode == 1) { // avg
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType dstart, hstart, wstart, dend, hend, wend, maxKD, maxKH, maxKW;
|
||||
T sum, valO, *pIn, *pgI;
|
||||
|
||||
for (int b = start_x; b < stop_x; b++) {
|
||||
for (int c = start_y; c < stop_y; c++) {
|
||||
for (int od = 0; od < oD; od++) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pgI = gI + b * gIStride0 + c * gIStride1;
|
||||
|
||||
dstart = od * sD - pD;
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
dend = dstart + kDEff;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (dstart < 0) dstart += dD * ((-dstart + dD - 1) / dD);
|
||||
if (hstart < 0) hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0) wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (dend > iD) dend -= dD * ((dend - iD + dD - 1) / dD);
|
||||
if (hend > iH) hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW) wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
dstart *= gIStride2;
|
||||
dend *= gIStride2;
|
||||
hstart *= gIStride3;
|
||||
hend *= gIStride3;
|
||||
wstart *= gIStride4;
|
||||
wend *= gIStride4;
|
||||
|
||||
valO = gO[b * oStride0 + c * oStride1 + od * oStride2 + oh * oStride3 + ow * oStride4];
|
||||
|
||||
if (extraParam0 == 0) // Exclude padding
|
||||
valO /=
|
||||
sd::math::sd_ceil<double, T>(static_cast<double>(dend - dstart) / static_cast<double>(gIStep2)) *
|
||||
sd::math::sd_ceil<double, T>(static_cast<double>(hend - hstart) / static_cast<double>(gIStep3)) *
|
||||
sd::math::sd_ceil<double, T>(static_cast<double>(wend - wstart) /
|
||||
static_cast<double>(gIStep4)); // Accounts for dilation
|
||||
else if (extraParam0 == 1) // Include padding
|
||||
valO /= kProd;
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += gIStep2)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += gIStep3)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += gIStep4) pgI[kd + kh + kw] += valO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
}
|
||||
/*************************************************************************/
|
||||
else if (poolingMode == 2) { // pnorm
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType dstart, hstart, wstart, dend, hend, wend, maxKD, maxKH, maxKW;
|
||||
T sum, valO, *pIn, *pgI;
|
||||
|
||||
for (int b = start_x; b < stop_x; b++) {
|
||||
for (int c = start_y; c < stop_y; c++) {
|
||||
for (int od = 0; od < oD; od++) {
|
||||
for (int oh = 0; oh < oH; ++oh) {
|
||||
for (int ow = 0; ow < oW; ++ow) {
|
||||
pIn = in + b * iStride0 + c * iStride1;
|
||||
pgI = gI + (pIn - in);
|
||||
|
||||
dstart = od * sD - pD;
|
||||
hstart = oh * sH - pH;
|
||||
wstart = ow * sW - pW;
|
||||
dend = dstart + kDEff;
|
||||
hend = hstart + kHEff;
|
||||
wend = wstart + kWEff;
|
||||
|
||||
if (dstart < 0) dstart += dD * ((-dstart + dD - 1) / dD);
|
||||
if (hstart < 0) hstart += dH * ((-hstart + dH - 1) / dH);
|
||||
if (wstart < 0) wstart += dW * ((-wstart + dW - 1) / dW);
|
||||
if (dend > iD) dend -= dD * ((dend - iD + dD - 1) / dD);
|
||||
if (hend > iH) hend -= dH * ((hend - iH + dH - 1) / dH);
|
||||
if (wend > iW) wend -= dW * ((wend - iW + dW - 1) / dW);
|
||||
|
||||
sum = static_cast<T>(0.);
|
||||
valO = gO[b * oStride0 + c * oStride1 + od * oStride2 + oh * oStride3 + ow * oStride4];
|
||||
|
||||
if (sameStrides) {
|
||||
dstart *= iStride2;
|
||||
dend *= iStride2;
|
||||
hstart *= iStride3;
|
||||
hend *= iStride3;
|
||||
wstart *= iStride4;
|
||||
wend *= iStride4;
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += iStep2)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep3)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep4)
|
||||
sum += sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(pIn[kd + kh + kw]), static_cast<T>(extraParam0));
|
||||
|
||||
valO *= sd::math::sd_pow<T, T, T>(sum, ((T)1.f - extraParam0) / extraParam0);
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += iStep2)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += iStep3)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += iStep4)
|
||||
pgI[kd + kh + kw] +=
|
||||
valO *
|
||||
sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(pIn[kd + kh + kw]), extraParam0 - (T)1.f) *
|
||||
sd::math::sd_sgn<T, T>(pIn[kd + kh + kw]);
|
||||
} else {
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += dD)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += dH)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += dW)
|
||||
sum += sd::math::sd_pow<T, T, T>(
|
||||
sd::math::sd_abs<T,T>(pIn[kd * iStride2 + kh * iStride3 + kw * iStride4]), static_cast<T>(extraParam0));
|
||||
|
||||
valO *= sd::math::sd_pow<T, T, T>(sum, ((T)1.f - extraParam0) / extraParam0);
|
||||
|
||||
for (sd::LongType kd = dstart; kd < dend; kd += dD)
|
||||
for (sd::LongType kh = hstart; kh < hend; kh += dH)
|
||||
for (sd::LongType kw = wstart; kw < wend; kw += dW) {
|
||||
const auto inVal = pIn[kD * iStride2 + kh * iStride3 + kw * iStride4];
|
||||
pgI[kd * gIStride2 + kh * gIStride3 + kw * gIStride4] +=
|
||||
valO * sd::math::sd_pow<T, T, T>(sd::math::sd_abs<T,T>(inVal), static_cast<T>(extraParam0) - 1.f) *
|
||||
sd::math::sd_sgn<T, T>(inVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1);
|
||||
} else {
|
||||
char errorMsg[512];
|
||||
snprintf(errorMsg, sizeof(errorMsg),
|
||||
"ConvolutionUtils::pooling3dBP: pooling mode argument can take three values only: 0, 1, 2, but got %i instead!",
|
||||
poolingMode);
|
||||
THROW_EXCEPTION(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionUtils::pooling3dBP(sd::graph::Context& block, NDArray& input, NDArray& gradO,
|
||||
NDArray& gradI, const LongType kD, const LongType kH, const LongType kW, const LongType sD, const LongType sH,
|
||||
const LongType sW, const LongType pD, const LongType pH, const LongType pW, const LongType dD, const LongType dH,
|
||||
const LongType dW, const int poolingMode, const int extraParam0) {
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input.dataType(), pooling3dBP_,
|
||||
(block, input, gradO, gradI, kD, kH, kW, sD, sH, sW, pD, pH, pW, dD, dH, dW, poolingMode, extraParam0),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,88 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#if NOT_EXCLUDED(OP_col2im) && NOT_EXCLUDED(OP_im2col)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void sconv2d_(graph::Context& block, NDArray* input, NDArray* weightsDepth,
|
||||
NDArray* weightsPoint, NDArray* bias, NDArray* output, const LongType kH, const LongType kW,
|
||||
const LongType sH, const LongType sW, LongType pH, LongType pW, const LongType dH, const LongType dW, const int paddingMode,
|
||||
const int isNCHW, const int wFormat) {
|
||||
// input [bS, iH, iW, iC] (NHWC) or [bS, iC, iH, iW] (NCHW)
|
||||
// weightsDepth [kH, kW, iC, mC], [mC, iC, kH, kW], [mC, kH, kW, iC]
|
||||
// weightsPoint [1, 1, iC*mC, oC], [oC, iC*mC, 1, 1], [oC, 1, 1, iC*mC]
|
||||
// bias [oC], oC = iC*mC if weightsPoint=nullptr
|
||||
// output is [bS, oH, oW, oC] (NHWC) or [bS, oC, oH, oW] (NCHW)
|
||||
|
||||
// kH filter(kernel) height
|
||||
// kW filter(kernel) width
|
||||
// sH strides height
|
||||
// sW strides width
|
||||
// pH paddings height
|
||||
// pW paddings width
|
||||
// dH dilations height
|
||||
// dW dilations width
|
||||
// paddingMode 0-VALID, 1-SAME
|
||||
// isNCHW 1-NCHW, 0-NHWC
|
||||
|
||||
LongType bS, iC, iH, iW, mC, oC, oH,
|
||||
oW; // batch size, input channels, input height/width, channels multiplier, output channels, output height/width
|
||||
LongType indIOioC, indIiH, indWmC, indWiC, indWkH, indOoH; // corresponding indexes
|
||||
ConvolutionUtils::getSizesAndIndexesConv2d(isNCHW, wFormat, *input, *output, bS, iC, iH, iW, oC, oH, oW, indIOioC,
|
||||
indIiH, indWiC, indWmC, indWkH, indOoH);
|
||||
mC = weightsDepth->sizeAt(indWmC); // channels multiplier
|
||||
|
||||
NDArray* outputDepth = output;
|
||||
if (weightsPoint) { // if pointwise convolution is expected
|
||||
std::vector<sd::LongType> shape3 =
|
||||
!isNCHW ? std::vector<sd::LongType>({bS, oH, oW, iC * mC}) : std::vector<sd::LongType>({bS, iC * mC, oH, oW});
|
||||
outputDepth = new NDArray(output->ordering(), shape3, input->dataType(), input->getContext());
|
||||
}
|
||||
// ----- perform depthwise convolution (if weightsPoint is absent then oC = iC*mC) ----- //
|
||||
ConvolutionUtils::depthwiseConv2d(block, input, weightsDepth, weightsPoint ? nullptr : bias, outputDepth, kH, kW, sH,
|
||||
sW, pH, pW, dH, dW, paddingMode, isNCHW, wFormat);
|
||||
|
||||
// ----- perform pointwise convolution (oH = iH, oW = iW) ----- //
|
||||
if (weightsPoint) {
|
||||
ConvolutionUtils::conv2d(block, outputDepth, weightsPoint, bias, output, 1, 1, 1, 1, 0, 0, 1, 1, paddingMode,
|
||||
isNCHW, wFormat); // in this case oH=iH, oW=iW
|
||||
delete outputDepth;
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionUtils::sconv2d(graph::Context& block, NDArray* input, NDArray* weightsDepth,
|
||||
NDArray* weightsPoint, NDArray* bias, NDArray* output, const LongType kH,
|
||||
const LongType kW, const LongType sH, const LongType sW, LongType pH, LongType pW, const LongType dH, const LongType dW,
|
||||
const int paddingMode, const int isNCHW, const int wFormat) {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(input->dataType(), sconv2d_,
|
||||
(block, input, weightsDepth, weightsPoint, bias, output, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
paddingMode, isNCHW, wFormat),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void upsampling2d_(NDArray& input, NDArray& output, const LongType factorH, const LongType factorW,
|
||||
const bool isNCHW) {
|
||||
// input has shape [bS, iC, iH, iW] (NCHW) or [bS, iH, iW, iC] (NHWC)
|
||||
// output has shape [bS, iC, factorH*iH, factorW*iW ] (NCHW) or [bS, factorH*iH, factorW*iW, iC] (NHWC)
|
||||
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
|
||||
const sd::LongType dimIH = isNCHW ? 2 : 1;
|
||||
const sd::LongType dimIC = isNCHW ? 1 : 3;
|
||||
|
||||
const sd::LongType bS = input.sizeAt(0);
|
||||
const sd::LongType iC = input.sizeAt(dimIC);
|
||||
const sd::LongType oH = output.sizeAt(dimIH);
|
||||
const sd::LongType oW = output.sizeAt(dimIH + 1);
|
||||
|
||||
const sd::LongType xStride0 = input.stridesOf()[0];
|
||||
const sd::LongType xStride1 = input.stridesOf()[dimIC];
|
||||
const sd::LongType xStride2 = input.stridesOf()[dimIH];
|
||||
const sd::LongType xStride3 = input.stridesOf()[dimIH + 1];
|
||||
|
||||
const sd::LongType zStride0 = output.stridesOf()[0];
|
||||
const sd::LongType zStride1 = output.stridesOf()[dimIC];
|
||||
const sd::LongType zStride2 = output.stridesOf()[dimIH];
|
||||
const sd::LongType zStride3 = output.stridesOf()[dimIH + 1];
|
||||
|
||||
// loop through output array
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
sd::LongType xCoord2, xCoord3;
|
||||
for (sd::LongType b = start_x; b < stop_x; b += inc_x) {
|
||||
for (sd::LongType c = start_y; c < stop_y; c += inc_y) {
|
||||
for (sd::LongType h = start_z; h < stop_z; h += inc_z) {
|
||||
for (sd::LongType w = 0; w < oW; ++w) {
|
||||
xCoord2 = h / factorH;
|
||||
xCoord3 = w / factorW;
|
||||
|
||||
z[b * zStride0 + c * zStride1 + h * zStride2 + w * zStride3] =
|
||||
x[b * xStride0 + c * xStride1 + xCoord2 * xStride2 + xCoord3 * xStride3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1, 0, oH, 1);
|
||||
}
|
||||
|
||||
void ConvolutionUtils::upsampling2d(sd::graph::Context& block, NDArray& input, NDArray& output, const LongType factorH,
|
||||
const LongType factorW, const bool isNCHW) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), upsampling2d_, (input, output, factorH, factorW, isNCHW), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,86 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void upsampling2dBP_(NDArray& gradO, NDArray& gradI, const bool isNCHW) {
|
||||
// gradO has shape [bS, iC, factorH*iH, factorW*iW ] (NCHW) or [bS, factorH*iH, factorW*iW, iC] (NHWC)
|
||||
// gradI has shape [bS, iC, iH, iW] (NCHW) or [bS, iH, iW, iC] (NHWC)
|
||||
|
||||
const T* x = gradO.bufferAsT<T>();
|
||||
T* z = gradI.bufferAsT<T>();
|
||||
|
||||
const sd::LongType dimIH = isNCHW ? 2 : 1;
|
||||
const sd::LongType dimIC = isNCHW ? 1 : 3;
|
||||
|
||||
const sd::LongType bS = gradI.sizeAt(0);
|
||||
const sd::LongType iC = gradI.sizeAt(dimIC);
|
||||
const sd::LongType iH = gradI.sizeAt(dimIH);
|
||||
const sd::LongType iW = gradI.sizeAt(dimIH + 1);
|
||||
|
||||
const sd::LongType factorH = gradO.sizeAt(dimIH) / iH;
|
||||
const sd::LongType factorW = gradO.sizeAt(dimIH + 1) / iW;
|
||||
|
||||
const sd::LongType xStride0 = gradO.stridesOf()[0];
|
||||
const sd::LongType xStride1 = gradO.stridesOf()[dimIC];
|
||||
const sd::LongType xStride2 = gradO.stridesOf()[dimIH];
|
||||
const sd::LongType xStride3 = gradO.stridesOf()[dimIH + 1];
|
||||
|
||||
const sd::LongType zStride0 = gradI.stridesOf()[0];
|
||||
const sd::LongType zStride1 = gradI.stridesOf()[dimIC];
|
||||
const sd::LongType zStride2 = gradI.stridesOf()[dimIH];
|
||||
const sd::LongType zStride3 = gradI.stridesOf()[dimIH + 1];
|
||||
|
||||
// loop through output array
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
for (sd::LongType b = start_x; b < stop_x; b += inc_x) {
|
||||
for (sd::LongType c = start_y; c < stop_y; c += inc_y) {
|
||||
for (sd::LongType h = start_z; h < stop_z; h += inc_z) {
|
||||
for (sd::LongType w = 0; w < iW; ++w) {
|
||||
const auto zOffset = b * zStride0 + c * zStride1 + h * zStride2 + w * zStride3;
|
||||
|
||||
z[zOffset] = 0;
|
||||
|
||||
for (sd::LongType xh = h * factorH; xh < h * factorH + factorH; ++xh)
|
||||
for (sd::LongType xw = w * factorW; xw < w * factorW + factorW; ++xw)
|
||||
z[zOffset] += x[b * xStride0 + c * xStride1 + xh * xStride2 + xw * xStride3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1, 0, iH, 1);
|
||||
}
|
||||
|
||||
void ConvolutionUtils::upsampling2dBP(sd::graph::Context& block, NDArray& gradO, NDArray& gradI,
|
||||
const bool isNCHW) {
|
||||
BUILD_SINGLE_SELECTOR(gradO.dataType(), upsampling2dBP_, (gradO, gradI, isNCHW), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,92 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void upsampling3d_(NDArray& input, NDArray& output, const LongType factorD, const LongType factorH,
|
||||
const LongType factorW, const bool isNCDHW) {
|
||||
// input has shape [bS, iC, iD, iH, iW] (NCDHW) or [bS, iD, iH, iW, iC] (NDHWC)
|
||||
// output has shape [bS, iC, factorD*iD, factorH*iH, factorW*iW ] (NCDHW) or [bS, factorD*iD, factorH*iH, factorW*iW,
|
||||
// iC] (NDHWC)
|
||||
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
|
||||
const sd::LongType dimID = isNCDHW ? 2 : 1;
|
||||
const sd::LongType dimIC = isNCDHW ? 1 : 4;
|
||||
|
||||
const sd::LongType bS = input.sizeAt(0);
|
||||
const sd::LongType iC = input.sizeAt(dimIC);
|
||||
const sd::LongType oD = output.sizeAt(dimID);
|
||||
const sd::LongType oH = output.sizeAt(dimID + 1);
|
||||
const sd::LongType oW = output.sizeAt(dimID + 2);
|
||||
|
||||
const sd::LongType xStride0 = input.stridesOf()[0];
|
||||
const sd::LongType xStride1 = input.stridesOf()[dimIC];
|
||||
const sd::LongType xStride2 = input.stridesOf()[dimID];
|
||||
const sd::LongType xStride3 = input.stridesOf()[dimID + 1];
|
||||
const sd::LongType xStride4 = input.stridesOf()[dimID + 2];
|
||||
|
||||
const sd::LongType zStride0 = output.stridesOf()[0];
|
||||
const sd::LongType zStride1 = output.stridesOf()[dimIC];
|
||||
const sd::LongType zStride2 = output.stridesOf()[dimID];
|
||||
const sd::LongType zStride3 = output.stridesOf()[dimID + 1];
|
||||
const sd::LongType zStride4 = output.stridesOf()[dimID + 2];
|
||||
|
||||
// loop through output array
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
sd::LongType xCoord2, xCoord3, xCoord4;
|
||||
|
||||
for (sd::LongType b = start_x; b < stop_x; b += inc_x) {
|
||||
for (sd::LongType c = start_y; c < stop_y; c += inc_y) {
|
||||
for (sd::LongType d = start_z; d < stop_z; d += inc_z) {
|
||||
for (sd::LongType h = 0; h < oH; ++h) {
|
||||
for (sd::LongType w = 0; w < oW; ++w) {
|
||||
xCoord2 = d / factorD;
|
||||
xCoord3 = h / factorH;
|
||||
xCoord4 = w / factorW;
|
||||
|
||||
z[b * zStride0 + c * zStride1 + d * zStride2 + h * zStride3 + w * zStride4] =
|
||||
x[b * xStride0 + c * xStride1 + xCoord2 * xStride2 + xCoord3 * xStride3 + xCoord4 * xStride4];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1, 0, oD, 1);
|
||||
}
|
||||
|
||||
void ConvolutionUtils::upsampling3d(sd::graph::Context& block, NDArray& input, NDArray& output, const LongType factorD,
|
||||
const LongType factorH, const LongType factorW, const bool isNCDHW) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), upsampling3d_, (input, output, factorD, factorH, factorW, isNCDHW),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,94 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void upsampling3dBP_(NDArray& gradO, NDArray& gradI, const bool isNCDHW) {
|
||||
// input has shape [bS, iC, iD, iH, iW] (NCDHW) or [bS, iD, iH, iW, iC] (NDHWC)
|
||||
// output has shape [bS, iC, factorD*iD, factorH*iH, factorW*iW ] (NCDHW) or [bS, factorD*iD, factorH*iH, factorW*iW,
|
||||
// iC] (NDHWC)
|
||||
|
||||
const T* x = gradO.bufferAsT<T>();
|
||||
T* z = gradI.bufferAsT<T>();
|
||||
|
||||
const sd::LongType dimID = isNCDHW ? 2 : 1;
|
||||
const sd::LongType dimIC = isNCDHW ? 1 : 4;
|
||||
|
||||
const sd::LongType bS = gradI.sizeAt(0);
|
||||
const sd::LongType iC = gradI.sizeAt(dimIC);
|
||||
const sd::LongType iD = gradI.sizeAt(dimID);
|
||||
const sd::LongType iH = gradI.sizeAt(dimID + 1);
|
||||
const sd::LongType iW = gradI.sizeAt(dimID + 2);
|
||||
|
||||
const sd::LongType factorD = gradO.sizeAt(dimID) / iD;
|
||||
const sd::LongType factorH = gradO.sizeAt(dimID + 1) / iH;
|
||||
const sd::LongType factorW = gradO.sizeAt(dimID + 2) / iW;
|
||||
|
||||
const sd::LongType xStride0 = gradO.stridesOf()[0];
|
||||
const sd::LongType xStride1 = gradO.stridesOf()[dimIC];
|
||||
const sd::LongType xStride2 = gradO.stridesOf()[dimID];
|
||||
const sd::LongType xStride3 = gradO.stridesOf()[dimID + 1];
|
||||
const sd::LongType xStride4 = gradO.stridesOf()[dimID + 2];
|
||||
|
||||
const sd::LongType zStride0 = gradI.stridesOf()[0];
|
||||
const sd::LongType zStride1 = gradI.stridesOf()[dimIC];
|
||||
const sd::LongType zStride2 = gradI.stridesOf()[dimID];
|
||||
const sd::LongType zStride3 = gradI.stridesOf()[dimID + 1];
|
||||
const sd::LongType zStride4 = gradI.stridesOf()[dimID + 2];
|
||||
|
||||
// loop through output array
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
for (sd::LongType b = start_x; b < stop_x; b += inc_x) {
|
||||
for (sd::LongType c = start_y; c < stop_y; c += inc_y) {
|
||||
for (sd::LongType d = start_z; d < stop_z; d += inc_z) {
|
||||
for (sd::LongType h = 0; h < iH; ++h) {
|
||||
for (sd::LongType w = 0; w < iW; ++w) {
|
||||
const auto zOffset = b * zStride0 + c * zStride1 + d * zStride2 + h * zStride3 + w * zStride4;
|
||||
|
||||
z[zOffset] = 0;
|
||||
|
||||
for (sd::LongType xd = d * factorD; xd < d * factorD + factorD; ++xd)
|
||||
for (sd::LongType xh = h * factorH; xh < h * factorH + factorH; ++xh)
|
||||
for (sd::LongType xw = w * factorW; xw < w * factorW + factorW; ++xw)
|
||||
z[zOffset] += x[b * xStride0 + c * xStride1 + xd * xStride2 + xh * xStride3 + xw * xStride4];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1, 0, iD, 1);
|
||||
}
|
||||
|
||||
void ConvolutionUtils::upsampling3dBP(sd::graph::Context& block, NDArray& gradO, NDArray& gradI,
|
||||
const bool isNCHW) {
|
||||
BUILD_SINGLE_SELECTOR(gradO.dataType(), upsampling3dBP_, (gradO, gradI, isNCHW), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,152 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// [bS, iC, iD, iH, iW] is convoluted to [bS, iC, kD, kH, kW, oD, oH, oW]
|
||||
template <typename T>
|
||||
static void vol2col_(NDArray* volume, NDArray* columns, const int sD, const int sH, const int sW, const int pD,
|
||||
const int pH, const int pW, const int dD, const int dH, const int dW) {
|
||||
const int bS = volume->sizeAt(0);
|
||||
const int iC = volume->sizeAt(1);
|
||||
const int iD = volume->sizeAt(2);
|
||||
const int iH = volume->sizeAt(3);
|
||||
const int iW = volume->sizeAt(4);
|
||||
const int kD = columns->sizeAt(2);
|
||||
const int kH = columns->sizeAt(3);
|
||||
const int kW = columns->sizeAt(4);
|
||||
const int oD = columns->sizeAt(5);
|
||||
const int oH = columns->sizeAt(6);
|
||||
const int oW = columns->sizeAt(7);
|
||||
const int colStride0 = columns->stridesOf()[0];
|
||||
const int colStride1 = columns->stridesOf()[1];
|
||||
const int colStride2 = columns->stridesOf()[2];
|
||||
const int colStride3 = columns->stridesOf()[3];
|
||||
const int colStride4 = columns->stridesOf()[4];
|
||||
const int colStride5 = columns->stridesOf()[5];
|
||||
const int colStride6 = columns->stridesOf()[6];
|
||||
const int colStride7 = columns->stridesOf()[7];
|
||||
const int volStride0 = volume->stridesOf()[0];
|
||||
const int volStride1 = volume->stridesOf()[1];
|
||||
const int volStride2 = volume->stridesOf()[2];
|
||||
const int volStride3 = volume->stridesOf()[3];
|
||||
const int volStride4 = volume->stridesOf()[4];
|
||||
T* colBuff = columns->bufferAsT<T>();
|
||||
T* volBuff = volume->bufferAsT<T>();
|
||||
|
||||
if (volume->ordering() == 'c' && columns->ordering() == 'c' && shape::strideDescendingCAscendingF(volume->shapeInfo()) &&
|
||||
shape::strideDescendingCAscendingF(columns->shapeInfo())) {
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
T *col, *vol;
|
||||
int volDep, volRow, volCol;
|
||||
|
||||
for (int b = start_x; b < stop_x; b += inc_x) {
|
||||
for (int c = start_y; c < stop_y; c += inc_y) {
|
||||
for (int kDep = start_z; kDep < stop_z; kDep += inc_z) {
|
||||
for (int kRow = 0; kRow < kH; ++kRow) {
|
||||
for (int kCol = 0; kCol < kW; ++kCol) {
|
||||
for (int colD = 0; colD < oD; ++colD) {
|
||||
for (int colH = 0; colH < oH; ++colH) {
|
||||
for (int colW = 0; colW < oW; ++colW) {
|
||||
volDep = (-pD + kDep * dD) + colD * sD;
|
||||
volRow = (-pH + kRow * dH) + colH * sH;
|
||||
volCol = (-pW + kCol * dW) + colW * sW;
|
||||
|
||||
col = colBuff + b * colStride0 + c * colStride1 + kDep * colStride2 + kRow * colStride3 +
|
||||
kCol * colStride4 + colD * colStride5 + colH * colStride6 + colW * colStride7;
|
||||
|
||||
if (static_cast<unsigned>(volDep) >= static_cast<unsigned>(iD) ||
|
||||
static_cast<unsigned>(volRow) >= static_cast<unsigned>(iH) ||
|
||||
static_cast<unsigned>(volCol) >= static_cast<unsigned>(iW))
|
||||
*col = static_cast<T>(0.);
|
||||
else {
|
||||
vol = volBuff + b * volStride0 + c * volStride1 + volDep * volStride2 + volRow * volStride3 +
|
||||
volCol * volStride4;
|
||||
*col = static_cast<T>(*vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, iC, 1, 0, kD, 1);
|
||||
|
||||
} else {
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
T *col, *vol;
|
||||
int volDep, volRow, volCol;
|
||||
|
||||
for (int b = start_x; b < stop_x; b++) {
|
||||
for (int colD = start_y; colD < stop_y; colD++) {
|
||||
for (int colH = 0; colH < oH; ++colH) {
|
||||
for (int colW = 0; colW < oW; ++colW) {
|
||||
for (int c = 0; c < iC; ++c) {
|
||||
for (int kDep = 0; kDep < kD; ++kDep) {
|
||||
for (int kRow = 0; kRow < kH; ++kRow) {
|
||||
for (int kCol = 0; kCol < kW; ++kCol) {
|
||||
volDep = (-pD + kDep * dD) + colD * sD;
|
||||
volRow = (-pH + kRow * dH) + colH * sH;
|
||||
volCol = (-pW + kCol * dW) + colW * sW;
|
||||
|
||||
col = colBuff + b * colStride0 + c * colStride1 + kDep * colStride2 + kRow * colStride3 +
|
||||
kCol * colStride4 + colD * colStride5 + colH * colStride6 + colW * colStride7;
|
||||
if (static_cast<unsigned>(volDep) >= static_cast<unsigned>(iD) ||
|
||||
static_cast<unsigned>(volRow) >= static_cast<unsigned>(iH) ||
|
||||
static_cast<unsigned>(volCol) >= static_cast<unsigned>(iW))
|
||||
*col = static_cast<T>(0.0);
|
||||
else {
|
||||
vol = volBuff + b * volStride0 + c * volStride1 + volDep * volStride2 + volRow * volStride3 +
|
||||
volCol * volStride4;
|
||||
*col = static_cast<T>(*vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, oD, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolutionUtils::vol2col(graph::Context& block, NDArray* vol, NDArray* col, const LongType sD, const LongType sH,
|
||||
const LongType sW, const LongType pD, const LongType pH, const LongType pW,
|
||||
const LongType dD, const LongType dH, const LongType dW) {
|
||||
BUILD_SINGLE_SELECTOR(vol->dataType(), vol2col_, (vol, col, sD, sH, sW, pD, pH, pW, dD, dH, dW),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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
|
||||
* *****************************************************************************
|
||||
*/
|
||||
/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
|
||||
//
|
||||
// @author sgazeos@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include "crop_and_resize.hpp"
|
||||
#include <system/selective_rendering.h>
|
||||
#if NOT_EXCLUDED(OP_crop_and_resize)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
// crop and resize helper functor:
|
||||
// \@param context - launch context for operation
|
||||
// \@param images - batch of images (4D tensor) with shape {batch, width, height, channels} with given type
|
||||
// \@param boxes - float boxes for crop
|
||||
// \@param indices - integer boxes indices for crop
|
||||
// \@param cropSize - integer size (newWidth, newHeight)
|
||||
// \@param method - one of bilinear (0) or nearest neighbour (1) interpolation algorithm
|
||||
// \@param extrapolationVal - radix to increase/decrease image
|
||||
// \@param crops - output image batch (4D with given type)
|
||||
//
|
||||
|
||||
void cropAndResizeFunctor(sd::LaunchContext *context, NDArray *images, NDArray *boxes,
|
||||
NDArray *indices, NDArray *cropSize, int method, double extrapolationVal,
|
||||
NDArray *crops) {
|
||||
auto imagesDType = images->dataType();
|
||||
auto boxesDType = boxes->dataType();
|
||||
auto indicesDType = indices->dataType();
|
||||
BUILD_TRIPLE_SELECTOR(images->dataType(), boxes->dataType(), indices->dataType(), cropAndResizeFunctor_,
|
||||
(context,images, boxes, indices, cropSize, method, extrapolationVal, crops), SD_NUMERIC_TYPES,
|
||||
SD_FLOAT_TYPES, SD_INTEGER_TYPES);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/crop_and_resize.h>
|
||||
#if NOT_EXCLUDED(OP_crop_and_resize)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cropAndResizeFunctor main algorithm
|
||||
// context - launch context
|
||||
// images - batch of images (4D tensor - [batch, width, height, pixels])
|
||||
// boxes - 2D tensor with boxes for crop
|
||||
// indices - 2D int tensor with indices of boxes to crop
|
||||
// cropSize - 2D int tensor with crop box sizes
|
||||
// method - (one of 0 - bilinear, 1 - nearest)
|
||||
// extrapolationVal - double value of extrapolation
|
||||
// crops - output (4D tensor - [batch, outWidth, outHeight, pixels])
|
||||
//
|
||||
template <typename T, typename Z, typename I>
|
||||
SD_LIB_EXPORT void cropAndResizeFunctor_(LaunchContext* context, NDArray * images, NDArray * boxes,
|
||||
NDArray * indices, NDArray * cropSize, int method, double extrapolationVal,
|
||||
NDArray* crops) {
|
||||
const int batchSize = images->sizeAt(0);
|
||||
const int imageHeight = images->sizeAt(1);
|
||||
const int imageWidth = images->sizeAt(2);
|
||||
|
||||
const int numBoxes = crops->sizeAt(0);
|
||||
const int cropHeight = crops->sizeAt(1);
|
||||
const int cropWidth = crops->sizeAt(2);
|
||||
const int depth = crops->sizeAt(3);
|
||||
|
||||
for (auto b = 0; b < numBoxes; ++b) {
|
||||
Z y1 = static_cast<Z>(boxes->t<Z>(b, 0));
|
||||
Z x1 = static_cast<Z>(boxes->t<Z>(b, 1));
|
||||
Z y2 = static_cast<Z>(boxes->t<Z>(b, 2));
|
||||
Z x2 = static_cast<Z>(boxes->t<Z>(b, 3));
|
||||
|
||||
int bIn = indices->e<I>(b);
|
||||
if (bIn >= batchSize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Z heightScale = (cropHeight > 1)
|
||||
? Z((y2 - y1) * (imageHeight - 1) / (cropHeight - 1))
|
||||
: Z(0);
|
||||
Z widthScale = (cropWidth > 1)
|
||||
? Z((x2 - x1) * (imageWidth - 1) / (cropWidth - 1))
|
||||
: Z(0);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto y = start; y < stop; y++) {
|
||||
const float inY =
|
||||
(cropHeight > 1) ? y1 * (imageHeight - 1) + y * heightScale : 0.5 * (y1 + y2) * (imageHeight - 1);
|
||||
|
||||
if (inY < 0 || inY > imageHeight - 1) {
|
||||
for (auto x = 0; x < cropWidth; ++x) {
|
||||
for (auto d = 0; d < depth; ++d) {
|
||||
crops->p(b, y, x, d, extrapolationVal);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (method == 0 /* bilinear */) {
|
||||
const int topYIndex = sd::math::p_floor(inY);
|
||||
const int bottomYIndex = sd::math::p_ceil(inY);
|
||||
const float y_lerp = inY - topYIndex;
|
||||
|
||||
for (auto x = 0; x < cropWidth; ++x) {
|
||||
const float in_x =
|
||||
(cropWidth > 1) ? x1 * (imageWidth - 1) + x * widthScale : 0.5 * (x1 + x2) * (imageWidth - 1);
|
||||
|
||||
if (in_x < 0 || in_x > imageWidth - 1) {
|
||||
for (auto d = 0; d < depth; ++d) {
|
||||
crops->p(b, y, x, d, extrapolationVal);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
int left_x_index = math::p_floor(in_x);
|
||||
int right_x_index = math::p_ceil(in_x);
|
||||
T x_lerp = static_cast<T>(in_x - left_x_index);
|
||||
|
||||
for (auto d = 0; d < depth; ++d) {
|
||||
const T topLeft(images->e<T>(bIn, topYIndex, left_x_index, d));
|
||||
const T topRight(images->e<T>(bIn, topYIndex, right_x_index, d));
|
||||
const T bottomLeft(images->e<T>(bIn, bottomYIndex, left_x_index, d));
|
||||
const T bottomRight(images->e<T>(bIn, bottomYIndex, right_x_index, d));
|
||||
const T top = topLeft + (topRight - topLeft) * x_lerp;
|
||||
const T bottom = bottomLeft + (bottomRight - bottomLeft) * x_lerp;
|
||||
crops->p(b, y, x, d, top + (bottom - top) * y_lerp);
|
||||
}
|
||||
}
|
||||
} else { // method is "nearest neighbor"
|
||||
for (auto x = 0; x < cropWidth; ++x) {
|
||||
const float inX =
|
||||
(cropWidth > 1) ? x1 * (imageWidth - 1) + x * widthScale : 0.5 * (x1 + x2) * (imageWidth - 1);
|
||||
|
||||
if (inX < 0 || inX > imageWidth - 1) {
|
||||
for (auto d = 0; d < depth; ++d) {
|
||||
crops->p(b, y, x, d, extrapolationVal);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const int closestXIndex = roundf(inX);
|
||||
const int closestYIndex = roundf(inY);
|
||||
for (auto d = 0; d < depth; ++d) {
|
||||
crops->p(b, y, x, d, images->e<T>(bIn, closestYIndex, closestXIndex, d));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, cropHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
|
||||
|
||||
BUILD_TRIPLE_TEMPLATE(void sd::ops::helpers::cropAndResizeFunctor_,
|
||||
(sd::LaunchContext * context, NDArray * images, NDArray * boxes, NDArray * indices,
|
||||
NDArray * cropSize, int method, double extrapolationVal, NDArray* crops),
|
||||
SD_NUMERIC_TYPES, SD_FLOAT_TYPES, SD_INTEGER_TYPES);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 GS (sgazeos@gmail.com), created on 10/1/2018
|
||||
//
|
||||
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/CustomOperations.h>
|
||||
#include <ops/declarable/helpers/cross.h>
|
||||
#if NOT_EXCLUDED(OP_cross)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
void crossBatched(sd::LaunchContext *context, NDArray *a, NDArray *b, NDArray *o) {
|
||||
std::vector<sd::LongType> shape2= {-1,3};
|
||||
auto _a = a->reshape(a->ordering(), shape2);
|
||||
auto _b = b->reshape(b->ordering(), shape2);
|
||||
auto _o = o->reshape(o->ordering(), shape2, false);
|
||||
|
||||
auto tadsA = _a->allTensorsAlongDimension({1});
|
||||
auto tadsB = _b->allTensorsAlongDimension({1});
|
||||
auto tadsO = _o->allTensorsAlongDimension({1});
|
||||
|
||||
int tads = tadsA.size();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
auto a_ = tadsA.at(e);
|
||||
auto b_ = tadsB.at(e);
|
||||
auto o_ = tadsO.at(e);
|
||||
|
||||
helpers::cross(context, a_, b_, o_);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, tads);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,433 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Deeplearning4j Contributors
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 <ops/declarable/helpers/ctc.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <system/selective_rendering.h>
|
||||
#if NOT_EXCLUDED(OP_ctc_loss)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <bool IsLogPStrided = false, bool IsLblStrided = false, typename Type, typename IndexType>
|
||||
Type forward(Type *alphaPtr, const sd::LongType &incA, const Type *logP, const sd::LongType &incP, const IndexType *lbl,
|
||||
const sd::LongType &lenSB, const sd::LongType &lenT, const int &blankIndex, int elwiseP = 1,
|
||||
int elwiseS = 1) {
|
||||
Type negInf = negative_infinity<Type>();
|
||||
// initialize alphas at t=0
|
||||
alphaPtr[0] = element<IsLogPStrided>(logP, blankIndex, elwiseP);
|
||||
// alphaPtr[1] =logP[lbl[0]];
|
||||
alphaPtr[1] = element<IsLogPStrided>(logP, *lbl, elwiseP);
|
||||
// the rest initialization was skipped
|
||||
// as its assumed the array already were initialized with negative infinity
|
||||
// move to the next frame
|
||||
Type *alphaPrevPtr = alphaPtr;
|
||||
alphaPtr += incA;
|
||||
logP += incP;
|
||||
|
||||
auto startX = lenSB - 2 * lenT;
|
||||
// process the rest
|
||||
for (auto t = 1; t < lenT; t++) {
|
||||
// start = max(0,L-2*(T-t))
|
||||
auto s = startX + 2 * t;
|
||||
s = s > 0 ? s : 0;
|
||||
for (; s < lenSB; s++) {
|
||||
auto ind = s / 2; // our real index
|
||||
// we force blanks for even indexes
|
||||
// strided version of lbl[ind] => element<IsLblStrided>(lbl, ind, elwiseS)
|
||||
auto currentInd = (s % 2 == 0) ? blankIndex : element<IsLblStrided>(lbl, ind, elwiseS);
|
||||
// {t-1,s}
|
||||
Type alphaS = alphaPrevPtr[s];
|
||||
Type alphaS_1 = s > 0 ? alphaPrevPtr[s - 1] : negInf;
|
||||
// logP[currentInd] or logP[currentInd*elwiseP]
|
||||
auto currentProb = element<IsLogPStrided>(logP, currentInd, elwiseP);
|
||||
// if blank or the same as previous
|
||||
if (s > 1 && currentInd != blankIndex && currentInd != element<IsLblStrided>(lbl, ind - 1, elwiseS)) {
|
||||
Type alphaS_2 = alphaPrevPtr[s - 2];
|
||||
alphaPtr[s] = log_sum_exp(alphaS, alphaS_1, alphaS_2) + currentProb;
|
||||
} else {
|
||||
alphaPtr[s] = log_sum_exp(alphaS, alphaS_1) + currentProb;
|
||||
}
|
||||
}
|
||||
|
||||
// store t-1 alpha Ptr
|
||||
alphaPrevPtr = alphaPtr;
|
||||
logP += incP;
|
||||
alphaPtr += incA;
|
||||
}
|
||||
auto logP0 = alphaPrevPtr[lenSB - 1];
|
||||
auto logP1 = alphaPrevPtr[lenSB - 2];
|
||||
return -log_sum_exp(logP0, logP1);
|
||||
}
|
||||
|
||||
//#undef CALCULATE_ALL_IN_ONE_FRAME_LOOP
|
||||
|
||||
template <bool IsLogPStrided = false, bool IsLblStrided = false, bool isGradStrided = false, typename Type,
|
||||
typename IndexType = int>
|
||||
void backwardAndGrad(Type forwardLogLoss, Type *alphaPtr, Type *bettaPtr, int incA, const Type *logP, int incP,
|
||||
Type *gradPtr, int incG, const IndexType *lbl, const sd::LongType &lenS, const sd::LongType &lenT,
|
||||
const sd::LongType &lenK, const int &blankIndex, int elwiseP = 1, int elwiseS = 1,
|
||||
int elwiseG = 1) {
|
||||
Type negInf = negative_infinity<Type>();
|
||||
sd::LongType lenSB = 2 * lenS + 1;
|
||||
auto origBetta = bettaPtr;
|
||||
auto origLogP = logP;
|
||||
// move to the last frame
|
||||
bettaPtr += (lenT - 1) * incA;
|
||||
logP += (lenT - 1) * incP;
|
||||
|
||||
// initialize bettas at t=lenT
|
||||
bettaPtr[lenSB - 1] = element<IsLogPStrided>(logP, blankIndex, elwiseP);
|
||||
auto lblIndex = element<IsLblStrided>(lbl, lenS - 1, elwiseS);
|
||||
bettaPtr[lenSB - 2] = element<IsLogPStrided>(logP, lblIndex, elwiseP); // logP[lbl[lenS - 1]];
|
||||
|
||||
#if defined(CALCULATE_ALL_IN_ONE_FRAME_LOOP)
|
||||
// move to the last
|
||||
gradPtr += (lenT - 1) * incG;
|
||||
alphaPtr += (lenT - 1) * incA;
|
||||
for (auto s = lenSB - 1; s >= 0; s--) {
|
||||
auto ind = s / 2; // our real index
|
||||
// we forced blanks for even indexes
|
||||
auto currentInd = (s % 2 == 0) ? blankIndex : element<IsLblStrided>(lbl, ind, elwiseS);
|
||||
// alpha(s)*betta(s) in log scale but still store in alpha to save memory
|
||||
auto alphaBettaS = alphaPtr[s] + bettaPtr[s];
|
||||
|
||||
// sum (alpha(s)*betta(s) ) over real indexes
|
||||
auto ¤tGrad = element<isGradStrided>(gradPtr, currentInd, elwiseG); // gradPtr[currentInd];
|
||||
if (currentGrad == negInf) {
|
||||
currentGrad = alphaBettaS;
|
||||
} else {
|
||||
Type cMax = std::max(currentGrad, alphaBettaS);
|
||||
currentGrad = std::log(std::exp(currentGrad - cMax) + std::exp(alphaBettaS - cMax)) + cMax;
|
||||
}
|
||||
}
|
||||
for (int k = 0; k < lenK; k++) {
|
||||
// compute the rest grad
|
||||
|
||||
// prob(t,k) - grad(k) / ((prob(t,k)*Z) )
|
||||
|
||||
// p2= grad(k) / (prob(t,k)*Z )
|
||||
// in logscale . plus we have Z as -logLoss
|
||||
// auto p2 = std::exp(gradPtr[k] + forwardLogLoss - logP[k]);
|
||||
// gradPtr[k] = std::exp(logP[k]) - p2;
|
||||
auto currentProb = element<IsLogPStrided>(logP, k, elwiseP);
|
||||
auto ¤tGrad = element<isGradStrided>(gradPtr, k, elwiseG);
|
||||
auto p2 = std::exp(currentGrad + forwardLogLoss - currentProb);
|
||||
currentGrad = std::exp(currentProb) - p2;
|
||||
}
|
||||
gradPtr -= incG;
|
||||
alphaPtr -= incA;
|
||||
#endif
|
||||
|
||||
auto bettaPrevPtr = bettaPtr;
|
||||
bettaPtr -= incA;
|
||||
logP -= incP;
|
||||
// process the rest
|
||||
for (auto t = lenT - 2; t >= 0; t--) {
|
||||
#if defined(CALCULATE_ALL_IN_ONE_FRAME_LOOP)
|
||||
auto end = lenSB - 1;
|
||||
#else
|
||||
auto end = std::min(2 * t + 2, lenSB - 1);
|
||||
#endif
|
||||
for (auto s = end; s >= 0; s--) {
|
||||
auto ind = s / 2; // our real index
|
||||
// we forced blanks for even indexes
|
||||
auto currentInd = (s % 2 == 0) ? blankIndex : element<IsLblStrided>(lbl, ind, elwiseS); // lbl[ind];
|
||||
// {t-1,s}
|
||||
Type bettaS = bettaPrevPtr[s];
|
||||
Type bettaS_1 = s < lenSB - 1 ? bettaPrevPtr[s + 1] : negInf;
|
||||
// logP[currentInd]
|
||||
auto currentProb = element<IsLogPStrided>(logP, currentInd, elwiseP);
|
||||
// if blank or the same as previous
|
||||
if (s < lenSB - 2 && currentInd != blankIndex && currentInd != element<IsLblStrided>(lbl, ind + 1, elwiseS)) {
|
||||
Type bettaS_2 = bettaPrevPtr[s + 2];
|
||||
bettaPtr[s] = log_sum_exp(bettaS, bettaS_1, bettaS_2) + currentProb;
|
||||
} else {
|
||||
bettaPtr[s] = log_sum_exp(bettaS, bettaS_1) + currentProb;
|
||||
}
|
||||
|
||||
#if defined(CALCULATE_ALL_IN_ONE_FRAME_LOOP)
|
||||
// alpha(s)*betta(s) in log scale but still store in alpha to save memory
|
||||
auto alphaBettaS = alphaPtr[s] + bettaPtr[s];
|
||||
|
||||
// sum (alpha(s)*betta(s) ) over real indexes
|
||||
auto ¤tGrad = element<isGradStrided>(gradPtr, currentInd, elwiseG); // gradPtr[currentInd];
|
||||
if (currentGrad == negInf) {
|
||||
currentGrad = alphaBettaS;
|
||||
} else {
|
||||
Type cMax = std::max(currentGrad, alphaBettaS);
|
||||
currentGrad = std::log(std::exp(currentGrad - cMax) + std::exp(alphaBettaS - cMax)) + cMax;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CALCULATE_ALL_IN_ONE_FRAME_LOOP)
|
||||
for (int k = 0; k < lenK; k++) {
|
||||
// compute the rest grad
|
||||
|
||||
// prob(t,k) - grad(k) / ((prob(t,k)*Z) )
|
||||
|
||||
// p2= grad(k) / (prob(t,k)*Z )
|
||||
// in logscale . plus we have Z as -logLoss
|
||||
// auto p2 = std::exp(gradPtr[k] + forwardLogLoss - logP[k]);
|
||||
// gradPtr[k] = std::exp(logP[k]) - p2;
|
||||
auto currentProb = element<IsLogPStrided>(logP, k, elwiseP);
|
||||
auto ¤tGrad = element<isGradStrided>(gradPtr, k, elwiseG);
|
||||
auto p2 = std::exp(currentGrad + forwardLogLoss - currentProb);
|
||||
currentGrad = std::exp(currentProb) - p2;
|
||||
}
|
||||
alphaPtr -= incA;
|
||||
gradPtr -= incG;
|
||||
#endif
|
||||
|
||||
bettaPrevPtr = bettaPtr;
|
||||
bettaPtr -= incA;
|
||||
logP -= incP;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(CALCULATE_ALL_IN_ONE_FRAME_LOOP)
|
||||
// alpha*betta
|
||||
bettaPtr = origBetta;
|
||||
logP = origLogP;
|
||||
|
||||
for (int t = 0; t < lenT; t++) {
|
||||
for (int s = 0; s < lenSB; s++) {
|
||||
auto ind = s / 2; // our real index
|
||||
// we forced blanks for even indexes
|
||||
auto currentInd = (s % 2 == 0) ? blankIndex : element<IsLblStrided>(lbl, ind, elwiseS); // lbl[ind];
|
||||
// alpha(s)*betta(s) in log scale but still store in alpha to save memory
|
||||
auto alphaBettaS = alphaPtr[s] + bettaPtr[s];
|
||||
|
||||
// sum (alpha(s)*betta(s) ) over real indexes
|
||||
auto ¤tGrad = element<isGradStrided>(gradPtr, currentInd, elwiseG); // gradPtr[currentInd];
|
||||
if (currentGrad == negInf) {
|
||||
currentGrad = alphaBettaS;
|
||||
} else {
|
||||
currentGrad = log_sum_exp(currentGrad, alphaBettaS);
|
||||
}
|
||||
// alphaPtr[s] = alphaBettaS;
|
||||
}
|
||||
|
||||
PRAGMA_OMP_SIMD
|
||||
for (int k = 0; k < lenK; k++) {
|
||||
// compute the rest grad
|
||||
|
||||
// prob(t,k) - grad(k) / ((prob(t,k)*Z) )
|
||||
|
||||
// p2= grad(k) / (prob(t,k)*Z )
|
||||
// in logscale . plus we have Z as -logLoss
|
||||
// auto p2 = std::exp(gradPtr[k] + forwardLogLoss - logP[k]);
|
||||
// gradPtr[k] = std::exp(logP[k]) - p2;
|
||||
auto currentProb = element<IsLogPStrided>(logP, k, elwiseP);
|
||||
auto ¤tGrad = element<isGradStrided>(gradPtr, k, elwiseG);
|
||||
auto p2 = std::exp(currentGrad + forwardLogLoss - currentProb);
|
||||
currentGrad = std::exp(currentProb) - p2;
|
||||
}
|
||||
|
||||
gradPtr += incG;
|
||||
bettaPtr += incA;
|
||||
alphaPtr += incA;
|
||||
logP += incP;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates ctc loss and fills gradients
|
||||
* @param logP logits matrix(lenT,lenK) pointer (log soft max input of rnn)
|
||||
* @param incP stride of logits for the next time frame
|
||||
* @param gradPtr gradient for output
|
||||
* @param incG stride of the gradient for the next time frame
|
||||
* @param lbl target label
|
||||
* @param lenT frame length
|
||||
* @param lenK class length
|
||||
* @param lenS target label length
|
||||
* @param blankIndex index of the blank label in logit class
|
||||
*/
|
||||
template <bool IsLogPStrided = true, bool IsLblStrided = true, bool IsGradStrided = true, typename Type,
|
||||
typename IndexType>
|
||||
Type unitLossAndGrad(const Type *logP, int incP, Type *gradPtr, int incG, const IndexType *lbl, int lenT, int lenK,
|
||||
int lenS, int blankIndex, int elwiseP = 1, int elwiseS = 1, int elwiseG = 1) {
|
||||
auto lenSB = 2 * lenS + 1;
|
||||
// create temp Array for holding bettaArr [lenT,lenSB]
|
||||
// create temp Array for holding alphaArr [lenT,lenSB]
|
||||
int bufferC = gradPtr ? 2 : 1;
|
||||
NDArray *bufferArr = NDArrayFactory::create<Type>('c', {bufferC, lenT, lenSB});
|
||||
auto bufferPtr = bufferArr->bufferAsT<Type>();
|
||||
auto incA = bufferArr->stridesOf()[1];
|
||||
auto bettaBufferPtr = bufferPtr + bufferArr->stridesOf()[0];
|
||||
Type negInf = negative_infinity<Type>();
|
||||
|
||||
if (gradPtr) {
|
||||
if (elwiseG == 1) {
|
||||
PRAGMA_OMP_SIMD
|
||||
for (int i = 0; i < lenK * lenT; i++) {
|
||||
gradPtr[i] = negInf;
|
||||
}
|
||||
} else {
|
||||
auto tempPtr = gradPtr;
|
||||
for (int i = 0; i < lenT; i++) {
|
||||
for (int j = 0; j < lenK; j++) element<false>(tempPtr, j, elwiseG) = negInf;
|
||||
tempPtr += incG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set all vals to neginf
|
||||
PRAGMA_OMP_SIMD
|
||||
for (int i = 0; i < bufferC * lenSB * lenT; i++) {
|
||||
bufferPtr[i] = negInf;
|
||||
}
|
||||
|
||||
// forward
|
||||
Type logLoss =
|
||||
forward<IsLogPStrided, IsLblStrided>(bufferPtr, incA, logP, incP, lbl, lenSB, lenT, blankIndex, elwiseP, elwiseS);
|
||||
// backward and gradient if gradptr supplied
|
||||
if (gradPtr)
|
||||
backwardAndGrad<IsLogPStrided, IsLblStrided, IsGradStrided>(logLoss, bufferPtr, bettaBufferPtr, incA, logP, incP,
|
||||
gradPtr, incG, lbl, lenS, lenT, lenK, blankIndex,
|
||||
elwiseP, elwiseS, elwiseG);
|
||||
|
||||
delete bufferArr;
|
||||
return logLoss;
|
||||
}
|
||||
|
||||
template <typename Type, typename IndexType>
|
||||
void ctc_loss_(NDArray&logits, NDArray&targetLabels, NDArray&logitsLengths,
|
||||
NDArray&targetLabelLengths, NDArray &logLosses, NDArray &gradients, int blankIndex) {
|
||||
// lenT - input length of T
|
||||
// lenS - lenght of sequence
|
||||
// lenSB - length with blanks
|
||||
auto lenBatch = logits.shapeOf()[0];
|
||||
|
||||
auto maxLenT = logits.shapeOf()[1];
|
||||
auto lenK = logits.shapeOf()[2];
|
||||
auto maxLenS = targetLabels.shapeOf()[1];
|
||||
|
||||
// get probability buffer and targetLabels buffer
|
||||
auto logP = logits.bufferAsT<Type>();
|
||||
auto lblPtr = targetLabels.bufferAsT<IndexType>();
|
||||
|
||||
auto lenTPtr = logitsLengths.bufferAsT<IndexType>();
|
||||
auto lenSPtr = targetLabelLengths.bufferAsT<IndexType>();
|
||||
|
||||
auto batchLbl = targetLabels.stridesOf()[0];
|
||||
auto batchP = logits.stridesOf()[0];
|
||||
auto incP = logits.stridesOf()[1];
|
||||
|
||||
auto elwiseSLen = targetLabelLengths.stridesOf()[0];
|
||||
auto elwiseT = logitsLengths.stridesOf()[0];
|
||||
auto elwiseS = targetLabels.stridesOf()[1];
|
||||
auto elwiseP = logits.stridesOf()[2];
|
||||
|
||||
int elwiseLL = 0;
|
||||
Type *logLossPtr = nullptr;
|
||||
if (!logLosses.isEmpty()) {
|
||||
elwiseLL = logLosses.stridesOf()[0];
|
||||
logLossPtr = logLosses.bufferAsT<Type>();
|
||||
}
|
||||
// defaulting blankIndex to the last class if its incorrect or -1
|
||||
if (blankIndex > maxLenS || blankIndex < 0) blankIndex = maxLenS - 1;
|
||||
auto func = [logP, batchP, incP, elwiseP, lenK, lenTPtr, lenSPtr, logLossPtr, lblPtr, maxLenT, maxLenS, batchLbl,
|
||||
blankIndex, elwiseT, elwiseLL, elwiseSLen, elwiseS,
|
||||
&gradients](uint64_t thread_id, int64_t start, int64_t stop, int64_t increment) -> void {
|
||||
Type *gradPtr = nullptr;
|
||||
Type resultLoss;
|
||||
int batchG, incG, elwiseG;
|
||||
if (!gradients.isEmpty()) {
|
||||
batchG = gradients.stridesOf()[0];
|
||||
incG = gradients.stridesOf()[1];
|
||||
elwiseG = gradients.stridesOf()[2];
|
||||
gradPtr = gradients.bufferAsT<Type>() + start * batchG;
|
||||
} else {
|
||||
elwiseG = 1;
|
||||
}
|
||||
auto logPtr = logP + start * batchP;
|
||||
auto tempLblPtr = lblPtr + start * batchLbl;
|
||||
|
||||
if (elwiseP == 1 && elwiseS == 1 && elwiseG == 1) {
|
||||
// choose ews one
|
||||
for (int batchIndex = start; batchIndex < stop; batchIndex += increment) {
|
||||
auto lenT = lenTPtr[batchIndex * elwiseT];
|
||||
auto lenS = lenSPtr[batchIndex * elwiseSLen];
|
||||
lenT = lenT > maxLenT ? maxLenT : lenT;
|
||||
lenS = lenS > maxLenS ? maxLenS : lenS;
|
||||
if (lenS <= 0 || lenT <= 0) {
|
||||
resultLoss = negative_infinity<Type>();
|
||||
} else {
|
||||
if (lenS > lenT) lenS = lenT;
|
||||
resultLoss = unitLossAndGrad<false, false, false, Type, IndexType>(logPtr, incP, gradPtr, incG, tempLblPtr,
|
||||
lenT, lenK, lenS, blankIndex);
|
||||
}
|
||||
if (gradPtr) gradPtr += batchG;
|
||||
if (logLossPtr) logLossPtr[batchIndex * elwiseLL] = resultLoss;
|
||||
logPtr += batchP;
|
||||
tempLblPtr += batchLbl;
|
||||
}
|
||||
} else {
|
||||
// slow strided case for all 3
|
||||
for (int batchIndex = start; batchIndex < stop; batchIndex += increment) {
|
||||
auto lenT = lenTPtr[batchIndex * elwiseT];
|
||||
auto lenS = lenSPtr[batchIndex * elwiseSLen];
|
||||
lenT = lenT > maxLenT ? maxLenT : lenT;
|
||||
lenS = lenS > maxLenS ? maxLenS : lenS;
|
||||
if (lenS <= 0 || lenT <= 0) {
|
||||
resultLoss = negative_infinity<Type>();
|
||||
} else {
|
||||
if (lenS > lenT) lenS = lenT;
|
||||
resultLoss = unitLossAndGrad<true, true, true, Type, IndexType>(
|
||||
logPtr, incP, gradPtr, incG, tempLblPtr, lenT, lenK, lenS, blankIndex, elwiseP, elwiseS, elwiseG);
|
||||
}
|
||||
if (gradPtr) gradPtr += batchG;
|
||||
if (logLossPtr) logLossPtr[batchIndex * elwiseLL] = resultLoss;
|
||||
logPtr += batchP;
|
||||
tempLblPtr += batchLbl;
|
||||
}
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, lenBatch, 1);
|
||||
}
|
||||
|
||||
void ctcLoss(graph::Context &block, NDArray&logits, NDArray&targetLabels, NDArray&logitsLengths,
|
||||
NDArray&targetLabelLengths, NDArray &logLosses, NDArray &gradients, int blankIndex) {
|
||||
auto logitsDType = logits.dataType();
|
||||
auto targetLabelsDType = targetLabels.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(logits.dataType(), targetLabels.dataType(), ctc_loss_,
|
||||
(logits, targetLabels, logitsLengths, targetLabelLengths, logLosses, gradients, blankIndex),
|
||||
SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
BUILD_DOUBLE_TEMPLATE( void ctc_loss_,
|
||||
(NDArray&logits, NDArray&targetLabels, NDArray&logitsLengths,
|
||||
NDArray&targetLabelLengths, NDArray &logLosses, NDArray &gradients, int blankIndex),
|
||||
SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/d_t_s.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void __depthToSpace(NDArray&input, NDArray *output, int block_size, bool isNHWC) {
|
||||
T const *input_ptr = reinterpret_cast<T const *>(input.buffer());
|
||||
T *output_ptr = reinterpret_cast<T *>(output->buffer());
|
||||
|
||||
const int batch_size = input.sizeAt(0);
|
||||
const int input_depth = isNHWC ? input.sizeAt(3) : input.sizeAt(1);
|
||||
const int input_height = isNHWC ? input.sizeAt(1) : input.sizeAt(2);
|
||||
const int input_width = isNHWC ? input.sizeAt(2) : input.sizeAt(3);
|
||||
|
||||
const int output_depth = isNHWC ? output->sizeAt(3) : output->sizeAt(1);
|
||||
const int output_height = isNHWC ? output->sizeAt(1) : output->sizeAt(2);
|
||||
const int output_width = isNHWC ? output->sizeAt(2) : output->sizeAt(3);
|
||||
|
||||
const int input_area = input_width * input_height;
|
||||
const int input_depth_by_input_area = input_depth * input_area;
|
||||
const int output_depth_by_input_height = output_depth * input_height;
|
||||
|
||||
if (isNHWC) {
|
||||
const int total_count = batch_size * output_height * output_width * output_depth;
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto out_idx = start; out_idx < stop; out_idx++) {
|
||||
const int d = out_idx % output_depth;
|
||||
const int out_idx2 = out_idx / output_depth;
|
||||
const int w = out_idx2 % output_width;
|
||||
const int out_idx3 = out_idx2 / output_width;
|
||||
const int h = out_idx3 % output_height;
|
||||
const int b = out_idx3 / output_height;
|
||||
|
||||
const int in_h = h / block_size;
|
||||
const int offset_h = h % block_size;
|
||||
const int in_w = w / block_size;
|
||||
const int offset_w = w % block_size;
|
||||
const int offset_d = (offset_h * block_size + offset_w) * output_depth;
|
||||
const int in_d = d + offset_d;
|
||||
const int inp_idx = in_d + input_depth * (in_w + input_width * (in_h + input_height * b));
|
||||
(output_ptr + out_idx)[0] = (input_ptr + inp_idx)[0];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, total_count);
|
||||
} else {
|
||||
const int total_count = batch_size * input_depth_by_input_area;
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (int input_idx = start; input_idx < stop; input_idx++) {
|
||||
const int n_bY_bX_oC_iY = input_idx / input_width;
|
||||
const int iX = input_idx - n_bY_bX_oC_iY * input_width;
|
||||
|
||||
const int n_bY_bX = n_bY_bX_oC_iY / output_depth_by_input_height;
|
||||
const int oC_iY = n_bY_bX_oC_iY - n_bY_bX * output_depth_by_input_height;
|
||||
|
||||
const int n_bY = n_bY_bX / block_size;
|
||||
const int bX = n_bY_bX - n_bY * block_size;
|
||||
|
||||
const int n = n_bY / block_size;
|
||||
const int bY = n_bY - n * block_size;
|
||||
|
||||
const int output_idx =
|
||||
bX + block_size * (iX + input_width * (bY + block_size * (oC_iY + n * output_depth_by_input_height)));
|
||||
|
||||
(output_ptr + output_idx)[0] = (input_ptr + input_idx)[0];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, total_count);
|
||||
}
|
||||
}
|
||||
|
||||
void _depthToSpace(sd::LaunchContext *context, NDArray&input, NDArray *output, int block_size, bool isNHWC) {
|
||||
auto xType = input.dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, __depthToSpace, (input, output, block_size, isNHWC), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void __depthToSpace,
|
||||
(NDArray&input, NDArray *output, int block_size, bool isNHWC);
|
||||
, SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/gammaMathFunc.h>
|
||||
#if NOT_EXCLUDED(OP_digamma)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// calculate digamma function for array elements
|
||||
template <typename T>
|
||||
static void diGamma_(NDArray& x, NDArray& z) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) z.p(i, diGammaScalar<T>(x.e<T>(i)));
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, x.lengthOf());
|
||||
}
|
||||
|
||||
void diGamma(sd::LaunchContext* context, NDArray& x, NDArray& z) {
|
||||
BUILD_SINGLE_SELECTOR(x.dataType(), diGamma_, (x, z), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void diGamma_, (NDArray& x, NDArray& z), SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by GS <sgazeos@gmail.com> on 4/6/2018.
|
||||
//
|
||||
#include <array/ResultSet.h>
|
||||
#include <ops/declarable/helpers/diag.h>
|
||||
#if NOT_EXCLUDED(OP_diag)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Returns a batched matrix tensor with new batched diagonal values.
|
||||
// for detailed explanations please take a look on web page:
|
||||
// https://www.tensorflow.org/api_docs/python/tf/matrix_set_diag
|
||||
template <typename T>
|
||||
static void _diagFunctor(NDArray* input, NDArray* output) {
|
||||
const int inLength = input->isScalar() ? 1 : input->lengthOf();
|
||||
|
||||
for (int i = 0; i < inLength; ++i) output->p<T>(i * (inLength + 1), (*input).e<T>(i));
|
||||
}
|
||||
|
||||
void diagFunctor(sd::LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
auto xType = input->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, _diagFunctor, (input, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _diagFunctor, (NDArray* input, NDArray* output);, SD_COMMON_TYPES);
|
||||
|
||||
void diagPartFunctor(sd::LaunchContext* context, NDArray * input, NDArray* output) {
|
||||
const int outLen = output->lengthOf();
|
||||
const int inLen = input->lengthOf();
|
||||
int i(0), j(0);
|
||||
while (j < outLen) {
|
||||
auto currE = input->e(i);
|
||||
output->p(j, &currE);
|
||||
i += outLen + 1;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
//
|
||||
// @autkhor raver119@gmail.com
|
||||
//
|
||||
#include <array/DataTypeUtils.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/dilation2d.h>
|
||||
#if NOT_EXCLUDED(OP_dilation2d)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void dilation2d_(NDArray* input, NDArray* weights, NDArray* output, const sd::LongType sH, const sd::LongType sW, const sd::LongType pH,
|
||||
const sd::LongType pW, const sd::LongType dH, const sd::LongType dW) {
|
||||
// input [bS, iH, iW, iC]
|
||||
// weights [kH, kW, iC]
|
||||
// output [bS, oH, oW, iC]
|
||||
|
||||
const X* x = input->bufferAsT<X>();
|
||||
const X* y = weights->bufferAsT<X>();
|
||||
Z* z = output->bufferAsT<Z>();
|
||||
|
||||
const sd::LongType* xShapeInfo = input->shapeInfo();
|
||||
const sd::LongType* yShapeInfo = weights->shapeInfo();
|
||||
const sd::LongType* zShapeInfo = output->shapeInfo();
|
||||
|
||||
const sd::LongType bS = input->sizeAt(0);
|
||||
const sd::LongType iH = input->sizeAt(1);
|
||||
const sd::LongType iW = input->sizeAt(2);
|
||||
const sd::LongType iC = input->sizeAt(3);
|
||||
|
||||
const sd::LongType kH = weights->sizeAt(0);
|
||||
const sd::LongType kW = weights->sizeAt(1);
|
||||
|
||||
const sd::LongType oH = output->sizeAt(1);
|
||||
const sd::LongType oW = output->sizeAt(2);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
for (auto b = start_x; b < stop_x; b += inc_x) {
|
||||
for (auto oh = start_y; oh < stop_y; oh += inc_y) {
|
||||
for (sd::LongType ow = 0; ow < oW; ++ow) {
|
||||
for (sd::LongType c = 0; c < iC; ++c) {
|
||||
X max = -DataTypeUtils::max<X>();
|
||||
|
||||
for (sd::LongType kh = 0; kh < kH; ++kh) {
|
||||
const int ih = oh * sH - pH + kh * dH;
|
||||
if (ih < 0 || ih >= iH) continue;
|
||||
|
||||
for (sd::LongType kw = 0; kw < kW; ++kw) {
|
||||
const int iw = ow * sW - pW + kw * dW;
|
||||
if (iw < 0 || iw >= iW) continue;
|
||||
|
||||
sd::LongType xCoords[4] = {static_cast<sd::LongType>(b), static_cast<sd::LongType>(ih),
|
||||
static_cast<sd::LongType>(iw), c};
|
||||
sd::LongType yCoords[3] = {kh, kw, c};
|
||||
|
||||
sd::LongType xOffset;
|
||||
COORDS2INDEX(shape::rank(xShapeInfo), shape::stride(xShapeInfo), xCoords, xOffset);
|
||||
|
||||
sd::LongType yOffset;
|
||||
COORDS2INDEX(shape::rank(yShapeInfo), shape::stride(yShapeInfo), yCoords, yOffset);
|
||||
|
||||
const X val = x[xOffset] + y[yOffset];
|
||||
if (val > max) max = val;
|
||||
}
|
||||
}
|
||||
|
||||
sd::LongType zCoords[4] = {static_cast<sd::LongType>(b), static_cast<sd::LongType>(oh), ow, c};
|
||||
sd::LongType zOffset;
|
||||
COORDS2INDEX(shape::rank(zShapeInfo), shape::stride(zShapeInfo), zCoords, zOffset);
|
||||
|
||||
z[zOffset] = static_cast<Z>(max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, oH, 1);
|
||||
}
|
||||
|
||||
|
||||
void dilation2d(sd::LaunchContext* context, NDArray* input, NDArray* weights, NDArray* output, const sd::LongType sH,
|
||||
const sd::LongType sW, const sd::LongType pH, const sd::LongType pW, const sd::LongType dH, const sd::LongType dW) {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(input->dataType(), dilation2d_, (input, weights, output, sH, sW, pH, pW, dH, dW),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,189 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <legacy/NativeOps.h>
|
||||
#include <ops/declarable/helpers/dropout.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#if NOT_EXCLUDED(OP_dropout)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void dropoutSimple(NDArray* input, NDArray* output, double probValue, int seed, NDArray* mask) {
|
||||
sd::graph::RandomGenerator nodeRng(3019L, seed);
|
||||
int inLen = input->lengthOf();
|
||||
std::vector<sd::LongType> inShape = {inLen};
|
||||
std::vector<sd::LongType> outShape = {output->lengthOf()};
|
||||
auto flattenedInput = input->reshape('c',inShape,false);
|
||||
auto flattenedOutput = output->reshape('c',outShape,false);
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
float val = nodeRng.relativeT<T>(e, T(0.f), T(1.f));
|
||||
//dropout mask might not be the same length
|
||||
if (mask != nullptr && e < mask->lengthOf()) mask->p<T>(e, static_cast<T>(val));
|
||||
if (val < probValue) flattenedOutput->p<T>(e, flattenedInput->e<T>(e));
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, inLen);
|
||||
|
||||
delete flattenedInput;
|
||||
delete flattenedOutput;
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void dropoutSimple, (NDArray* input, NDArray* output, double probValue, int seed,NDArray *mask),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
template <typename T>
|
||||
sd::Status dropOutFunctor_(graph::Context& context, NDArray* input, NDArray* output, NDArray* reduceShape, int seed,
|
||||
double probValue, NDArray* mask) {
|
||||
|
||||
if (reduceShape == nullptr) {
|
||||
dropoutSimple<T>(input, output, probValue, seed, mask);
|
||||
} else {
|
||||
REQUIRE_TRUE(reduceShape->lengthOf() <= input->rankOf(), 0, "dropout: Noise shape should be fittable to input");
|
||||
|
||||
std::vector<sd::LongType> dims(reduceShape->lengthOf());
|
||||
|
||||
bool fit = true;
|
||||
for (size_t i = 0; i < dims.size(); i++) {
|
||||
if (fit) {
|
||||
dims[i] = reduceShape->e<sd::LongType>(i);
|
||||
for (int e = 0; e < input->rankOf(); ++e)
|
||||
if (fit)
|
||||
if (input->sizeAt(e) % dims[i]) {
|
||||
fit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check dims to fit input
|
||||
REQUIRE_TRUE(fit, 0, "dropout: Noise shape should fit to input rank.");
|
||||
std::unique_ptr<NDArray> chunk(new NDArray('c', dims, output->dataType(), output->getContext()));
|
||||
float assign = 1.f;
|
||||
chunk->assign(assign);
|
||||
dropoutSimple<T>(chunk.get(), chunk.get(), probValue, seed, nullptr);
|
||||
// broadcast chunk to full matrix
|
||||
mask->assign(assign);
|
||||
|
||||
*mask += *chunk;
|
||||
NDArray *assign5 = *input * *mask;
|
||||
output->assign(assign5);
|
||||
delete assign5;
|
||||
}
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status dropOutFunctor(graph::Context& context, NDArray* input, NDArray* output, NDArray* reduceShape, int seed,
|
||||
double probValue, NDArray* mask) {
|
||||
auto xType = input->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, return dropOutFunctor_, (context, input, output, reduceShape, seed, probValue,mask),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status dropOutFunctor_, (graph::Context & context, NDArray* input, NDArray* output,
|
||||
NDArray* reduceShape, int seed, double probValue,NDArray *mask);
|
||||
, SD_FLOAT_TYPES);
|
||||
|
||||
/////////////////////////////////// backprpopagations ///////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static Status dropOutFunctorBP_(graph::Context& context, NDArray* input, NDArray* gradOut, NDArray* output,
|
||||
NDArray* reduceShape, int seed, double probValue, NDArray* mask) {
|
||||
auto mask2 = *gradOut * *mask;
|
||||
*output = *mask2;
|
||||
delete mask2;
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static Status alphaDropOutFunctor_(graph::Context& context, NDArray* input, NDArray* output, NDArray* reduceShape,
|
||||
int seed, double probValue, double alpha, double alpha1, double beta,
|
||||
NDArray* mask) {
|
||||
|
||||
sd::graph::RandomGenerator nodeRng(3019L, seed);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
float randVal = nodeRng.relativeT(e, T(0.f), T(1.f));
|
||||
float xVal = input->e<float>(e);
|
||||
float maskVal = randVal >= probValue ? alpha * beta + alpha1 : alpha * 1 + alpha1;
|
||||
mask->p<float>(e, maskVal);
|
||||
output->p<float>(e, randVal >= probValue ? alpha * beta + alpha1 : alpha * xVal + alpha1);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input->lengthOf());
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sd::Status alphaDropOutFunctorBP_(graph::Context& context, NDArray* input, NDArray* gradOut, NDArray* output,
|
||||
NDArray* reduceShape, int seed, double probValue, double alpha, double alpha1,
|
||||
double beta, NDArray* mask) {
|
||||
|
||||
auto mask2 = *gradOut * *mask;
|
||||
*output *= *mask2;
|
||||
delete mask2;
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status dropOutFunctorBP(graph::Context& context, NDArray* input, NDArray* gradOut, NDArray* output,
|
||||
NDArray* reduceShape, int seed, double probValue, NDArray* mask) {
|
||||
BUILD_SINGLE_SELECTOR(context.dataType(), return dropOutFunctorBP_,
|
||||
(context, input, gradOut, output, reduceShape, seed, probValue,mask), SD_FLOAT_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status dropOutFunctorBP_,
|
||||
(::Context & context, NDArray* input, NDArray* gradOut, NDArray* output,
|
||||
NDArray* reduceShape, int seed, double probValue,NDArray* mask),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
sd::Status alphaDropOutFunctor(graph::Context& context, NDArray* input, NDArray* output, NDArray* reduceShape, int seed,
|
||||
double probValue, double alpha, double alpha1, double beta, NDArray* mask) {
|
||||
BUILD_SINGLE_SELECTOR(context.dataType(), return alphaDropOutFunctor_,
|
||||
(context, input, output, reduceShape, seed, probValue, alpha, alpha1, beta,mask), SD_FLOAT_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status alphaDropOutFunctor_,
|
||||
(graph::Context & context, NDArray* input, NDArray* output, NDArray* reduceShape, int seed,
|
||||
double probValue, double alpha, double alpha1, double beta,NDArray* mask),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
sd::Status alphaDropOutFunctorBP(graph::Context& context, NDArray* input, NDArray* gradOut, NDArray* output,
|
||||
NDArray* reduceShape, int seed, double probValue, double alpha, double alpha1,
|
||||
double beta, NDArray* mask) {
|
||||
BUILD_SINGLE_SELECTOR(context.dataType(), return alphaDropOutFunctorBP_,
|
||||
(context, input, gradOut, output, reduceShape, seed, probValue, alpha, alpha1, beta,mask),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status alphaDropOutFunctorBP_,
|
||||
(graph::Context & context, NDArray* input, NDArray* gradOut, NDArray* output,
|
||||
NDArray* reduceShape, int seed, double probValue, double alpha, double alpha1, double beta,NDArray *mask),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,230 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by george on 05.04.18.
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/dynamic.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void _dynamicPartitionFunctor(NDArray * input, NDArray * indices, std::vector<NDArray*>& outputList) {
|
||||
std::vector<std::pair<NDArray*, sd::LongType>> outputs(outputList.size());
|
||||
int sourceDimsLen = input->rankOf() - indices->rankOf();
|
||||
if (sourceDimsLen) {
|
||||
std::vector<sd::LongType> sourceDims(sourceDimsLen);
|
||||
|
||||
for (sd::LongType i = sourceDimsLen; i > 0; i--) sourceDims[sourceDimsLen - i] = input->rankOf() - i;
|
||||
|
||||
ResultSet listOfTensors = input->allTensorsAlongDimension(sourceDims);
|
||||
|
||||
sd::LongType outSize = outputList.size();
|
||||
|
||||
for (sd::LongType i = 0; i < outSize; i++) {
|
||||
outputs[i].first = outputList[i];
|
||||
std::vector<sd::LongType > outDims(outputs[i].first->rankOf() - 1);
|
||||
|
||||
sd::LongType r = outputs[i].first->rankOf();
|
||||
|
||||
for (sd::LongType k = 1; k < r; k++) outDims[k - 1] = k;
|
||||
|
||||
ResultSet listOutForCurrent = outputs[i].first->allTensorsAlongDimension(outDims);
|
||||
|
||||
outputs[i].second = 0;
|
||||
|
||||
for (sd::LongType e = 0; e < indices->lengthOf(); ++e)
|
||||
if ((*indices).e<sd::LongType>(e) == i) {
|
||||
listOutForCurrent.at(outputs[i].second++)->assign(listOfTensors.at(e));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
sd::LongType outSize = outputList.size();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
outputs[i].first = outputList[i];
|
||||
outputs[i].second = 0;
|
||||
for (sd::LongType e = 0; e < indices->lengthOf(); ++e)
|
||||
if (indices->e<sd::LongType>(e) == i) outputs[i].first->p(outputs[i].second++, input->e<T>(e));
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, outSize);
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static sd::Status _dynamicStitchFunctor(std::vector<NDArray*> const& inputs, std::vector<NDArray*> const& indices,
|
||||
NDArray* output) {
|
||||
sd::LongType numOfData = inputs.size();
|
||||
|
||||
if (output->isVector()) {
|
||||
for (sd::LongType e = 0; e < numOfData; e++) {
|
||||
auto data = inputs[e];
|
||||
auto index = indices[e];
|
||||
for (sd::LongType i = 0; i < index->lengthOf(); i++) {
|
||||
sd::LongType pos = index->e<sd::LongType>(i);
|
||||
if (pos < 0) {
|
||||
sd_printf("dynamic_stitch: Index value should be non-negative. But %i was given", pos);
|
||||
return sd::Status::VALIDATION;
|
||||
}
|
||||
if (pos >= output->lengthOf()) {
|
||||
sd_printf("dynamic_stitch: Index should be less than %i. But %i was given", output->lengthOf(), pos);
|
||||
return sd::Status::VALIDATION;
|
||||
}
|
||||
output->p<T>(pos, data->e<T>(i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::vector<sd::LongType > restDims(output->rankOf() - 1);
|
||||
for (auto i = restDims.size(); i > 0; i--) restDims[restDims.size() - i] = output->rankOf() - i;
|
||||
|
||||
ResultSet listOfOutTensors = output->allTensorsAlongDimension(restDims);
|
||||
for (int e = 0; e < numOfData; e++) {
|
||||
auto data = inputs[e];
|
||||
auto index = indices[e];
|
||||
std::vector<sd::LongType > sourceDims(data->rankOf() - index->rankOf());
|
||||
for (auto i = sourceDims.size(); i > 0; i--) sourceDims[sourceDims.size() - i] = data->rankOf() - i;
|
||||
|
||||
ResultSet listOfTensors = data->allTensorsAlongDimension(sourceDims);
|
||||
|
||||
for (sd::LongType i = 0; i < index->lengthOf(); i++) {
|
||||
auto pos = index->e<sd::LongType>(i);
|
||||
if (pos < 0) {
|
||||
sd_printf("dynamic_stitch: Index value should be non-negative. But %i was given", pos);
|
||||
return sd::Status::VALIDATION;
|
||||
}
|
||||
if (pos >= output->lengthOf()) {
|
||||
sd_printf("dynamic_stitch: Index should be less than %i. But %i was given", output->lengthOf(), pos);
|
||||
return sd::Status::VALIDATION;
|
||||
}
|
||||
|
||||
listOfOutTensors.at(pos)->assign(listOfTensors.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void _dynamicPartitionFunctorBP(NDArray * input, NDArray * indices,
|
||||
std::vector<NDArray*> const& inputGradientList,
|
||||
std::vector<NDArray*>& outputList) {
|
||||
std::vector<std::pair<NDArray*, sd::LongType>> outputs(inputGradientList.size());
|
||||
|
||||
int sourceDimsLen = input->rankOf() - indices->rankOf();
|
||||
if (sourceDimsLen) { // multidimensional case
|
||||
std::vector<sd::LongType > sourceDims(sourceDimsLen);
|
||||
|
||||
for (sd::LongType i = sourceDimsLen; i > 0; i--) sourceDims[sourceDimsLen - i] = input->rankOf() - i;
|
||||
|
||||
ResultSet listOfTensors = outputList[0]->allTensorsAlongDimension(sourceDims);
|
||||
|
||||
for (size_t i = 0; i < inputGradientList.size(); i++) {
|
||||
outputs[i].first = inputGradientList[i];
|
||||
if (outputs[i].first->rankOf() < 1) continue; // skip empty gradient outs
|
||||
std::vector<sd::LongType > outDims(outputs[i].first->rankOf() - 1);
|
||||
|
||||
for (int k = 1; k < outputs[i].first->rankOf(); k++) outDims[k - 1] = k;
|
||||
|
||||
ResultSet listOutForCurrent = outputs[i].first->allTensorsAlongDimension(outDims);
|
||||
|
||||
outputs[i].second = 0;
|
||||
|
||||
for (sd::LongType e = 0; e < indices->lengthOf(); ++e)
|
||||
if (indices->e<sd::LongType>(e) == static_cast<sd::LongType>(i)) listOfTensors.at(e)->assign(listOutForCurrent.at(outputs[i].second++));
|
||||
}
|
||||
} else { // one-dimensional case
|
||||
auto output = outputList[0];
|
||||
unsigned int gradsSize = inputGradientList.size();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
outputs[i].first = inputGradientList[i];
|
||||
outputs[i].second = 0;
|
||||
for (sd::LongType e = 0; e < indices->lengthOf(); ++e)
|
||||
if (indices->e<sd::LongType>(e) == i) output->p<T>(e, outputs[i].first->e<T>(outputs[i].second++));
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, gradsSize);
|
||||
}
|
||||
|
||||
outputList[1]->assign(indices);
|
||||
}
|
||||
|
||||
void dynamicPartitionFunctor(sd::LaunchContext* context, NDArray * input, NDArray * indices,
|
||||
std::vector<NDArray*>& outputList) {
|
||||
auto xType = input->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, _dynamicPartitionFunctor, (input, indices, outputList), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static sd::Status _dynamicStitchFunctorBP(std::vector<NDArray*> const& inputs, std::vector<NDArray*> const& indices,
|
||||
NDArray * gradInput, std::vector<NDArray*>& outputList) {
|
||||
THROW_EXCEPTION("Not implemented yet");
|
||||
}
|
||||
|
||||
sd::Status dynamicStitchFunctor(sd::LaunchContext* context, std::vector<NDArray*> const& inputs,
|
||||
std::vector<NDArray*> const& indices, NDArray* output) {
|
||||
auto xType = inputs.at(0)->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, return _dynamicStitchFunctor, (inputs, indices, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
sd::Status dynamicStitchFunctorBP(sd::LaunchContext* context, std::vector<NDArray*> const& inputs,
|
||||
std::vector<NDArray*> const& indices, NDArray * gradInput,
|
||||
std::vector<NDArray*>& outputList) {
|
||||
auto xType = inputs.at(0)->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, return _dynamicStitchFunctorBP, (inputs, indices, gradInput, outputList),
|
||||
SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
void dynamicPartitionFunctorBP(sd::LaunchContext* context, NDArray * input, NDArray * indices,
|
||||
std::vector<NDArray*> const& inputGradientList, std::vector<NDArray*>& outputList) {
|
||||
auto xType = input->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, _dynamicPartitionFunctorBP, (input, indices, inputGradientList, outputList),
|
||||
SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _dynamicPartitionFunctorBP,
|
||||
(NDArray * input, NDArray * indices, std::vector<NDArray*> const& inputGradientList,
|
||||
std::vector<NDArray*>& outputList);
|
||||
, SD_COMMON_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status _dynamicStitchFunctorBP,
|
||||
(std::vector<NDArray*> const& inputs, std::vector<NDArray*> const& indices,
|
||||
NDArray * gradInput, std::vector<NDArray*>& outputList);
|
||||
, SD_COMMON_TYPES);
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _dynamicPartitionFunctor,
|
||||
(NDArray * input, NDArray * indices, std::vector<NDArray*>& outputList);
|
||||
, SD_COMMON_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status _dynamicStitchFunctor,
|
||||
(std::vector<NDArray*> const& inputs, std::vector<NDArray*> const& indices, NDArray* output);
|
||||
, SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,106 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/axis.h>
|
||||
#if NOT_EXCLUDED(OP_extract_patches)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void _extractPatches(NDArray* images, NDArray* output, int sizeRow, int sizeCol, int strideRow, int strideCol,
|
||||
int rateRow, int rateCol, bool theSame) {
|
||||
std::vector<sd::LongType> restDims({1, 2, 3}); // the first and the last dims
|
||||
ResultSet listOfMatricies = images->allTensorsAlongDimension(restDims);
|
||||
ResultSet listOfOutputs = output->allTensorsAlongDimension(restDims);
|
||||
// 3D matricies - 2D matricies of vectors (if last dim is greater than 1)
|
||||
// int e = 0;
|
||||
const int ksizeRowsEffective = sizeRow + (sizeRow - 1) * (rateRow - 1);
|
||||
const int ksizeColsEffective = sizeCol + (sizeCol - 1) * (rateCol - 1);
|
||||
const int ksize = ksizeRowsEffective * ksizeColsEffective;
|
||||
int batchCount = listOfMatricies.size(); // lengthOf() / ksize;
|
||||
sd::LongType lastDim = images->sizeAt(3);
|
||||
sd::LongType outLastDim = output->sizeAt(3);
|
||||
sd::LongType rowDim = images->sizeAt(1);
|
||||
sd::LongType colDim = images->sizeAt(2);
|
||||
sd::LongType outRowDim = output->sizeAt(1);
|
||||
sd::LongType outColDim = output->sizeAt(2);
|
||||
auto rowCast = 1; //(sizeRow - 1)*rateRow < outRowDim/sizeRow ?0:1;///(ksize * lastDim > rowDim * ksizeColsEffective
|
||||
//+ lastDim?1:0);
|
||||
auto colCast = 1; // colDim / ksizeColsEffective +2 <= sizeCol?0:1;//(ksize * lastDim > ksizeRowsEffective * colDim +
|
||||
// lastDim?1:0);
|
||||
if (sizeRow * rateRow < 3) rowCast = 0;
|
||||
if (sizeCol * rateCol < 3) colCast = 0;
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto batch = 0; batch < stop; batch++) {
|
||||
auto patch = listOfMatricies.at(batch);
|
||||
auto outMatrix = listOfOutputs.at(batch);
|
||||
|
||||
for (sd::LongType i = 0; i < outRowDim; i++) {
|
||||
for (sd::LongType j = 0; j < outColDim; j++) {
|
||||
sd::LongType pos = 0;
|
||||
// for (sd::LongType k = 0; k < outputLastDim; k++) {
|
||||
auto rowStart = i * strideRow - (theSame ? rowCast : 0);
|
||||
auto colStart = j * strideCol - (theSame ? colCast : 0);
|
||||
auto rowEnd = rowStart + sizeRow * rateRow;
|
||||
auto colEnd = colStart + sizeCol * rateCol;
|
||||
if (!theSame) {
|
||||
rowEnd = math::sd_min(rowStart + sizeRow * rateRow, rowDim);
|
||||
colEnd = math::sd_min(colStart + sizeCol * rateCol, colDim);
|
||||
}
|
||||
// auto pixel = 0LL;
|
||||
for (auto row = rowStart; row < rowEnd; row += rateRow)
|
||||
for (auto col = colStart; col < colEnd; col += rateCol)
|
||||
for (auto pixel = 0; pixel < lastDim; pixel++) {
|
||||
bool setUp = (theSame && row >= 0 && col >= 0 && row < rowDim && col < colDim) || (!theSame);
|
||||
if (setUp) {
|
||||
outMatrix->r<T>(i, j, pos) = patch->e<T>(row, col, pixel);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, batchCount);
|
||||
}
|
||||
|
||||
void extractPatches(sd::LaunchContext* context, NDArray* images, NDArray* output, int sizeRow, int sizeCol,
|
||||
int stradeRow, int stradeCol, int rateRow, int rateCol, bool theSame) {
|
||||
auto xType = images->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, _extractPatches,
|
||||
(images, output, sizeRow, sizeCol, stradeRow, stradeCol, rateRow, rateCol, theSame),
|
||||
SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _extractPatches,
|
||||
(NDArray * input, NDArray* output, int sizeRow, int sizeCol, int stradeRow, int stradeCol,
|
||||
int rateRow, int rateCol, bool theSame),
|
||||
SD_NUMERIC_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
|
||||
//
|
||||
|
||||
#include <helpers/Loops.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
#if NOT_EXCLUDED(OP_eye)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void eye(sd::LaunchContext* context, NDArray& output) {
|
||||
const int rank = output.rankOf();
|
||||
auto arrs = output.allTensorsAlongDimension({rank - 2, rank - 1});
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) arrs.at(i)->setIdentity();
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, arrs.size());
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <ops/declarable/helpers/fake_quantization.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//
|
||||
// nudge - nudged min max over scale
|
||||
// scale = (Max - Min) / (quantMax - quantMin)
|
||||
// quantMin = 0 or 1, quantMax = 2^b - 1 == (1 << b) - 1
|
||||
//
|
||||
template <typename T>
|
||||
static void nudge(T min, T max, int quantMin, int quantMax, T* scale, T* nudgedMin, T* nudgedMax) {
|
||||
// floating point instead integers
|
||||
T quantMaxF = static_cast<T>(quantMax);
|
||||
T quantMinF = static_cast<T>(quantMin);
|
||||
// compute scale
|
||||
*scale = (max - min) / (quantMaxF - quantMinF);
|
||||
// compute left bound point
|
||||
auto zeroPointFromMin = quantMinF - min / *scale;
|
||||
// bound zero point to conform with range [0 or 1, 2^b - 1]
|
||||
uint16_t const nudged_zero_point = [zeroPointFromMin, quantMin, quantMax, quantMaxF, quantMinF] {
|
||||
if (zeroPointFromMin < quantMinF) {
|
||||
return static_cast<uint16_t>(quantMin);
|
||||
}
|
||||
if (zeroPointFromMin > quantMaxF) {
|
||||
return static_cast<uint16_t>(quantMax);
|
||||
}
|
||||
return (uint16_t)sd::math::sd_round<T, int>(zeroPointFromMin);
|
||||
}();
|
||||
// compute nudged min and max with computed nudged zero point
|
||||
*nudgedMin = (quantMinF - nudged_zero_point) * (*scale);
|
||||
*nudgedMax = (quantMaxF - nudged_zero_point) * (*scale);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void fakeQuantWithMinMaxVarsPerChannel_(NDArray* input, NDArray* min, NDArray* max, int numBits, bool narrowed,
|
||||
NDArray* output) {
|
||||
int lowIntBound = narrowed ? 1 : 0; // 0 or 1
|
||||
int upperIntBound = (1 << numBits) - 1; // 2^b - 1
|
||||
auto channels = input->sizeAt(-1); // last dimension
|
||||
|
||||
PRAGMA_OMP_PARALLEL_FOR
|
||||
for (auto i = 0; i < channels; i++) {
|
||||
T scale, nudged_min, nudged_max;
|
||||
// nudge min and max first, with scale computing
|
||||
nudge<T>(min->t<T>(i), max->t<T>(i), lowIntBound, upperIntBound, &scale, &nudged_min, &nudged_max);
|
||||
// slide using last dimension and process all for given channel
|
||||
for (auto e = 0; e < input->lengthOf(); e += channels) {
|
||||
T val = input->t<T>(e + i);
|
||||
if (val <= nudged_min)
|
||||
val = nudged_min;
|
||||
else if (val >= nudged_max)
|
||||
val = nudged_max;
|
||||
// quantization itself
|
||||
output->r<T>(e + i) = math::sd_floor<T, T>((val - nudged_min) / scale + T(0.5)) * scale + nudged_min;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// const auto clamped = inputs.cwiseMin(nudged_max).cwiseMax(nudged_min);
|
||||
// const auto clamped_shifted = clamped - nudged_min;
|
||||
// outputs.device(d) = (clamped_shifted / nudged_scale_repl + 0.5f).floor() *
|
||||
// nudged_scale_repl +
|
||||
// nudged_min;
|
||||
//
|
||||
template <typename T>
|
||||
void fakeQuantWithMinMaxVars_(NDArray* input, NDArray* min, NDArray* max, int numBits, bool narrowed, NDArray* output) {
|
||||
int lowIntBound = narrowed ? 1 : 0;
|
||||
int upperIntBound = (1 << numBits) - 1;
|
||||
|
||||
T nudgedMin, nudgedMax, scale;
|
||||
// nudge with given min and max and compute scale and nudged min and max
|
||||
nudge<T>(min->t<T>(0), max->t<T>(0), lowIntBound, upperIntBound, &scale, &nudgedMin, &nudgedMax);
|
||||
// quantization as one
|
||||
auto fakeQuantizationWithMinMax = LAMBDA_T(x, nudgedMin, nudgedMax, scale) {
|
||||
T val = x; // boundign value between nudged min and max
|
||||
if (val < nudgedMin) {
|
||||
val = nudgedMin;
|
||||
} else if (val > nudgedMax)
|
||||
val = nudgedMax;
|
||||
// converse value with scale and shifted with nudged min
|
||||
val -= nudgedMin;
|
||||
return (sd::math::sd_floor<T, T>(val / scale + T(0.5f)) * scale + nudgedMin);
|
||||
});
|
||||
|
||||
input->applyLambda<T>(fakeQuantizationWithMinMax, output);
|
||||
}
|
||||
|
||||
void fakeQuantWithMinMaxVars(NDArray* input, NDArray* min, NDArray* max, int numBits, bool narrowed, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), fakeQuantWithMinMaxVars_, (input, min, max, numBits, narrowed, output),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
void fakeQuantWithMinMaxVarsPerChannel(LaunchContext* context, NDArray* input, NDArray* min, NDArray* max, int numBits,
|
||||
bool narrowed, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), fakeQuantWithMinMaxVarsPerChannel_,
|
||||
(input, min, max, numBits, narrowed, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void fakeQuantWithMinMaxVars_,
|
||||
(NDArray * input, NDArray* min, NDArray* max, int numBits, bool narrowed, NDArray* output),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,71 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/flatten.h>
|
||||
#if NOT_EXCLUDED(OP_flatten)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void flatten_(std::vector<NDArray *> &inputs, NDArray *output, const char order) {
|
||||
int numArrays = inputs.size();
|
||||
std::vector<sd::LongType> offsets(numArrays);
|
||||
sd::LongType cOffset = 0;
|
||||
|
||||
// calculating offsets in output
|
||||
for (int e = 0; e < numArrays; e++) {
|
||||
offsets[e] = cOffset;
|
||||
cOffset += inputs[e]->lengthOf();
|
||||
}
|
||||
|
||||
// actually transferring data
|
||||
for (sd::LongType e = 0; e < numArrays; e++) {
|
||||
auto z = reinterpret_cast<T *>(output->bufferWithOffset(offsets[e]));
|
||||
auto xBuffer = inputs[e]->bufferAsT<T>();
|
||||
auto xShapeInfo = inputs[e]->shapeInfo();
|
||||
|
||||
// Cache shape-related values outside the inner loop
|
||||
const int xRank = shape::rank(xShapeInfo);
|
||||
const sd::LongType* xShape = shape::shapeOf(xShapeInfo);
|
||||
const sd::LongType* xStride = shape::stride(xShapeInfo);
|
||||
const sd::LongType xLength = inputs[e]->lengthOf();
|
||||
|
||||
for (sd::LongType i = 0; i < xLength; i++) {
|
||||
sd::LongType xOffset;
|
||||
sd::LongType xCoords[SD_MAX_RANK];
|
||||
|
||||
// Use cached shape values for coordinate transforms
|
||||
INDEX2COORDS(i, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
|
||||
z[i] = xBuffer[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
void flatten(sd::LaunchContext *context, std::vector<NDArray *> &inputs, NDArray *output, char order) {
|
||||
BUILD_SINGLE_SELECTOR(output->dataType(), flatten_, (inputs, output, order), SD_COMMON_TYPES);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,355 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 07.03.2019
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/gather.h>
|
||||
#include <legacy/NativeOpExecutioner.h>
|
||||
|
||||
#include <numeric>
|
||||
#if NOT_EXCLUDED(OP_gather)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void gather(sd::LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output,
|
||||
const std::vector<LongType>& intArgs) {
|
||||
sd::LongType axis = intArgs.size() > 0 ? intArgs[0] : 0;
|
||||
const sd::LongType inputRank = input->rankOf();
|
||||
if (axis < 0) axis += inputRank;
|
||||
|
||||
const sd::LongType numOfIntArgs = intArgs.size();
|
||||
|
||||
// Special handling for 1D input with axis=0
|
||||
// This handles cases like gathering from shape arrays where we want flat indexing
|
||||
bool is1DFlatGather = (inputRank == 1 && axis == 0);
|
||||
|
||||
if (indices != nullptr) {
|
||||
// Validate indices
|
||||
for (sd::LongType i = 0; i < indices->lengthOf(); ++i) {
|
||||
auto idx = indices->e<sd::LongType>(i);
|
||||
|
||||
if (is1DFlatGather) {
|
||||
// For 1D arrays with axis=0, treat as flat array access
|
||||
if (idx >= input->lengthOf() || idx < 0) {
|
||||
std::string error = "helpers::gather function: invalid flat index ";
|
||||
error += std::to_string(idx);
|
||||
error += " at position ";
|
||||
error += std::to_string(i);
|
||||
error += ". Input is 1D with length ";
|
||||
error += std::to_string(input->lengthOf());
|
||||
error += ", valid range is [0, ";
|
||||
error += std::to_string(input->lengthOf() - 1);
|
||||
error += "]";
|
||||
THROW_EXCEPTION(error.c_str());
|
||||
}
|
||||
} else {
|
||||
// Standard axis-based validation
|
||||
if (idx >= input->sizeAt(axis) || idx < 0) {
|
||||
std::string error = "helpers::gather function: invalid index ";
|
||||
error += std::to_string(idx);
|
||||
error += " at position ";
|
||||
error += std::to_string(i);
|
||||
error += ". Input shape ";
|
||||
error += ShapeUtils::shapeAsString(input->shapeInfo());
|
||||
error += ", axis ";
|
||||
error += std::to_string(axis);
|
||||
error += ", valid range is [0, ";
|
||||
error += std::to_string(input->sizeAt(axis) - 1);
|
||||
error += "]";
|
||||
THROW_EXCEPTION(error.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is1DFlatGather) {
|
||||
// Special case: 1D input with axis=0 - treat as flat array gather
|
||||
// This handles gathering from shape arrays like [1, 512] -> gather index 1 -> get 512
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto idx = indices->e<sd::LongType>(i);
|
||||
auto value = input->e<double>(idx); // Get value at flat index
|
||||
output->p(i, value); // Put in output at position i
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, indices->lengthOf());
|
||||
|
||||
} else {
|
||||
// Standard gather implementation
|
||||
//
|
||||
// For gather with axis=A on input shape [..., dimA, ...] and indices shape [I1, I2, ...]:
|
||||
// - Output shape is: input[0:A] + indices_shape + input[A+1:]
|
||||
// - Input TADs: iterate along axis A, each TAD has shape input[A+1:]
|
||||
// - Output TADs: iterate along indices dimensions, each TAD has same shape as input TAD
|
||||
//
|
||||
// tadForDimensions takes dimensions to KEEP in each TAD (not to exclude)
|
||||
// It then internally calls evalDimsToExclude to find which dims to iterate over
|
||||
|
||||
std::vector<sd::LongType> axesVec = {axis};
|
||||
auto dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1, axesVec.data());
|
||||
|
||||
// For output TADs, we want the same shape as input TADs
|
||||
// Input TAD shape = all dims except axis
|
||||
// Output shape = input[0:axis] + indices_shape + input[axis+1:]
|
||||
// Output TAD dims should be: dims 0 to axis-1, then dims axis+indicesRank to end
|
||||
// This gives TAD shape matching input's TAD shape
|
||||
std::vector<sd::LongType> outputTadDims;
|
||||
sd::LongType indicesRank = indices->rankOf();
|
||||
|
||||
// Add dimensions before the indices dimensions (0 to axis-1)
|
||||
for (sd::LongType d = 0; d < axis; d++) {
|
||||
outputTadDims.push_back(d);
|
||||
}
|
||||
// Add dimensions after the indices dimensions (axis+indicesRank to outputRank-1)
|
||||
for (sd::LongType d = axis + indicesRank; d < output->rankOf(); d++) {
|
||||
outputTadDims.push_back(d);
|
||||
}
|
||||
|
||||
// If outputTadDims is empty, it means each TAD is a scalar - handle this case
|
||||
// by using the same approach as input (which would also have empty TAD dims)
|
||||
|
||||
// Get TAD packs - these are cached and should not be deleted
|
||||
auto tadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto tadPackOut = sd::ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), &outputTadDims);
|
||||
|
||||
// Validate TAD packs before use
|
||||
if (tadPack == nullptr || tadPackOut == nullptr) {
|
||||
if (dimensions) delete dimensions;
|
||||
THROW_EXCEPTION("gather: Failed to create TAD packs");
|
||||
}
|
||||
|
||||
// Now safe to delete dimensions as TAD helper has made internal copy
|
||||
delete dimensions;
|
||||
|
||||
auto tadShapeInfo = tadPack->primaryShapeInfo();
|
||||
auto tadOffsets = tadPack->primaryOffsets();
|
||||
auto tadShapeInfoOut = tadPackOut->primaryShapeInfo();
|
||||
auto tadOffsetsOut = tadPackOut->primaryOffsets();
|
||||
|
||||
// Validate that input and output TAD shapes match
|
||||
auto inputTadLength = shape::length(tadShapeInfo);
|
||||
auto outputTadLength = shape::length(tadShapeInfoOut);
|
||||
if (inputTadLength != outputTadLength) {
|
||||
std::string error = "gather: TAD shape mismatch - input TAD length ";
|
||||
error += std::to_string(inputTadLength);
|
||||
error += " != output TAD length ";
|
||||
error += std::to_string(outputTadLength);
|
||||
error += ". Input shape: ";
|
||||
error += ShapeUtils::shapeAsString(input->shapeInfo());
|
||||
error += ", Output shape: ";
|
||||
error += ShapeUtils::shapeAsString(output->shapeInfo());
|
||||
error += ", Indices shape: ";
|
||||
error += ShapeUtils::shapeAsString(indices->shapeInfo());
|
||||
error += ", axis: ";
|
||||
error += std::to_string(axis);
|
||||
THROW_EXCEPTION(error.c_str());
|
||||
}
|
||||
|
||||
auto tadShapeInfoCast = const_cast<sd::LongType *>(tadShapeInfo);
|
||||
auto tadShapeInfoOutCast = const_cast<sd::LongType *>(tadShapeInfoOut);
|
||||
|
||||
// Calculate the number of gather operations (equal to indices length)
|
||||
const sd::LongType numGatherOps = indices->lengthOf();
|
||||
|
||||
// Validate bounds before parallel execution
|
||||
if (numGatherOps > tadPackOut->numberOfTads()) {
|
||||
std::string error = "gather: indices length ";
|
||||
error += std::to_string(numGatherOps);
|
||||
error += " exceeds output TAD count ";
|
||||
error += std::to_string(tadPackOut->numberOfTads());
|
||||
THROW_EXCEPTION(error.c_str());
|
||||
}
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto idx = indices->e<sd::LongType>(i);
|
||||
|
||||
// Bounds check for input TAD access
|
||||
if (idx >= tadPack->numberOfTads() || idx < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bounds check for output TAD access
|
||||
if (i >= tadPackOut->numberOfTads()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto offsetIn = tadOffsets[idx];
|
||||
auto offsetOut = tadOffsetsOut[i];
|
||||
|
||||
NativeOpExecutioner::execTransformAny(input->getContext(),
|
||||
transform::Assign,
|
||||
input->bufferWithOffset(offsetIn), tadShapeInfoCast,
|
||||
nullptr, nullptr,
|
||||
output->bufferWithOffset(offsetOut), tadShapeInfoOutCast,
|
||||
nullptr, nullptr,
|
||||
nullptr, false);
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func, 0, numGatherOps);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Integer arguments case
|
||||
for (int i = 1; i < numOfIntArgs; ++i) {
|
||||
if (is1DFlatGather) {
|
||||
// For 1D arrays with axis=0, validate against total length
|
||||
if (intArgs[i] >= input->lengthOf() || intArgs[i] < 0) {
|
||||
std::string error = "helpers::gather function: invalid flat index ";
|
||||
error += std::to_string(intArgs[i]);
|
||||
error += " at position ";
|
||||
error += std::to_string(i-1);
|
||||
error += ". Input is 1D with length ";
|
||||
error += std::to_string(input->lengthOf());
|
||||
error += ", valid range is [0, ";
|
||||
error += std::to_string(input->lengthOf() - 1);
|
||||
error += "]";
|
||||
THROW_EXCEPTION(error.c_str());
|
||||
}
|
||||
} else {
|
||||
// Standard validation
|
||||
if (intArgs[i] >= input->sizeAt(axis) || intArgs[i] < 0) {
|
||||
std::string error = "helpers::gather function: invalid index ";
|
||||
error += std::to_string(intArgs[i]);
|
||||
error += " at position ";
|
||||
error += std::to_string(i-1);
|
||||
error += ". Input shape ";
|
||||
error += ShapeUtils::shapeAsString(input->shapeInfo());
|
||||
error += ", axis ";
|
||||
error += std::to_string(axis);
|
||||
error += ", valid range is [0, ";
|
||||
error += std::to_string(input->sizeAt(axis) - 1);
|
||||
error += "]";
|
||||
THROW_EXCEPTION(error.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numOfIntArgs == 2) {
|
||||
if (is1DFlatGather) {
|
||||
// For 1D flat gather with single index
|
||||
auto value = input->e<double>(intArgs[1]);
|
||||
output->assign(value);
|
||||
} else {
|
||||
// Standard single index gather
|
||||
NDArray *copy = (*input)(intArgs[1], {axis});
|
||||
output->assign(copy);
|
||||
delete copy;
|
||||
}
|
||||
} else {
|
||||
if (is1DFlatGather) {
|
||||
// Multiple indices for 1D flat gather
|
||||
for (int i = 1; i < numOfIntArgs; ++i) {
|
||||
auto idx = intArgs[i];
|
||||
auto value = input->e<double>(idx);
|
||||
output->p(i - 1, value);
|
||||
}
|
||||
} else {
|
||||
// Standard multiple indices gather
|
||||
// Use the same dimension calculation for input and output TADs
|
||||
std::vector<sd::LongType> axesVec = {axis};
|
||||
auto dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1, axesVec.data());
|
||||
|
||||
// Get TAD packs - these are cached and should not be deleted
|
||||
auto tadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto tadPackOut = sd::ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
|
||||
// Validate TAD packs before use
|
||||
if (tadPack == nullptr || tadPackOut == nullptr) {
|
||||
if (dimensions) delete dimensions;
|
||||
THROW_EXCEPTION("gather: Failed to create TAD packs");
|
||||
}
|
||||
|
||||
// Now safe to delete dimensions as TAD helper has made internal copy
|
||||
delete dimensions;
|
||||
|
||||
auto tadShapeInfo = tadPack->primaryShapeInfo();
|
||||
auto tadOffsets = tadPack->primaryOffsets();
|
||||
auto tadShapeInfoOut = tadPackOut->primaryShapeInfo();
|
||||
auto tadOffsetsOut = tadPackOut->primaryOffsets();
|
||||
|
||||
// Validate that input and output TAD shapes match
|
||||
auto inputTadLength = shape::length(tadShapeInfo);
|
||||
auto outputTadLength = shape::length(tadShapeInfoOut);
|
||||
if (inputTadLength != outputTadLength) {
|
||||
std::string error = "gather: TAD shape mismatch - input TAD length ";
|
||||
error += std::to_string(inputTadLength);
|
||||
error += " != output TAD length ";
|
||||
error += std::to_string(outputTadLength);
|
||||
error += ". Input shape: ";
|
||||
error += ShapeUtils::shapeAsString(input->shapeInfo());
|
||||
error += ", Output shape: ";
|
||||
error += ShapeUtils::shapeAsString(output->shapeInfo());
|
||||
error += ", axis: ";
|
||||
error += std::to_string(axis);
|
||||
THROW_EXCEPTION(error.c_str());
|
||||
}
|
||||
|
||||
// Number of gather operations (number of indices provided as int args)
|
||||
const sd::LongType numGatherOps = numOfIntArgs - 1;
|
||||
|
||||
// Validate bounds before parallel execution
|
||||
if (numGatherOps > tadPackOut->numberOfTads()) {
|
||||
std::string error = "gather: number of indices ";
|
||||
error += std::to_string(numGatherOps);
|
||||
error += " exceeds output TAD count ";
|
||||
error += std::to_string(tadPackOut->numberOfTads());
|
||||
THROW_EXCEPTION(error.c_str());
|
||||
}
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto idx = intArgs[i + 1];
|
||||
|
||||
// Bounds check for input TAD access
|
||||
if (idx >= tadPack->numberOfTads() || idx < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bounds check for output TAD access
|
||||
if (i >= tadPackOut->numberOfTads()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto offsetIn = tadOffsets[idx];
|
||||
auto offsetOut = tadOffsetsOut[i];
|
||||
|
||||
NativeOpExecutioner::execTransformAny(input->getContext(),
|
||||
transform::Assign,
|
||||
input->bufferWithOffset(offsetIn), const_cast<sd::LongType*>(tadShapeInfo),
|
||||
nullptr, nullptr,
|
||||
output->bufferWithOffset(offsetOut), const_cast<sd::LongType*>(tadShapeInfoOut),
|
||||
nullptr, nullptr,
|
||||
nullptr, false);
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func, 0, numGatherOps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,203 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
|
||||
//
|
||||
|
||||
#include <helpers/Loops.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <system/selective_rendering.h>
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void gatherND_(NDArray& input, NDArray& indices, NDArray& output) {
|
||||
const X* x = reinterpret_cast<X*>(input.buffer());
|
||||
const Y* y = reinterpret_cast<Y*>(indices.buffer());
|
||||
X* z = reinterpret_cast<X*>(output.buffer());
|
||||
|
||||
const sd::LongType xRank = input.rankOf();
|
||||
const sd::LongType yRank = indices.rankOf();
|
||||
const sd::LongType zRank = output.rankOf();
|
||||
const sd::LongType maxRank = sd::math::sd_max<sd::LongType>(yRank, sd::math::sd_max<sd::LongType>(xRank, zRank));
|
||||
|
||||
const sd::LongType zLen = output.lengthOf();
|
||||
|
||||
const sd::LongType yLastDim = indices.sizeAt(-1);
|
||||
|
||||
const int diff = zRank - xRank;
|
||||
const bool bEqual = yLastDim == xRank;
|
||||
|
||||
sd::LongType outputRank = output.rankOf();
|
||||
sd::LongType* outputShape = shape::shapeOf(output.shapeInfo());
|
||||
sd::LongType* outputStride = shape::stride(output.shapeInfo());
|
||||
sd::LongType indicesRank = indices.rankOf();
|
||||
sd::LongType* indicesShape = shape::shapeOf(indices.shapeInfo());
|
||||
sd::LongType* indicesStride = shape::stride(indices.shapeInfo());
|
||||
|
||||
sd::LongType inputRank = input.rankOf();
|
||||
sd::LongType* inputShape = shape::shapeOf(input.shapeInfo());
|
||||
sd::LongType* inputStride = shape::stride(input.shapeInfo());
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
sd::LongType xCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK], temp;
|
||||
|
||||
for (sd::LongType i = start; i < stop; i++) {
|
||||
INDEX2COORDS(i, outputRank, outputShape, zCoords);
|
||||
|
||||
sd::LongType zOffset;
|
||||
COORDS2INDEX(outputRank, outputStride, zCoords, zOffset);
|
||||
|
||||
temp = zCoords[yRank - 1];
|
||||
zCoords[yRank - 1] = 0;
|
||||
|
||||
sd::LongType yOffset;
|
||||
COORDS2INDEX(indicesRank, indicesStride, zCoords, yOffset);
|
||||
|
||||
zCoords[yRank - 1] = temp;
|
||||
|
||||
if (bEqual)
|
||||
memcpy(xCoords, zCoords, zRank * sizeof(sd::LongType));
|
||||
else if (diff >= 0)
|
||||
memcpy(xCoords, zCoords + diff, xRank * sizeof(sd::LongType));
|
||||
else
|
||||
memcpy(xCoords - diff, zCoords, zRank * sizeof(sd::LongType));
|
||||
|
||||
for (sd::LongType j = 0; j < yLastDim; ++j)
|
||||
xCoords[j] = y[yOffset + j * indicesStride[yRank - 1]]; // last stride
|
||||
|
||||
sd::LongType xOffset;
|
||||
COORDS2INDEX(inputRank, inputStride, xCoords, xOffset);
|
||||
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, zLen);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void gatherND(sd::LaunchContext* context, NDArray& input, NDArray& indices, NDArray& output) {
|
||||
auto inputDType = input.dataType();
|
||||
auto indicesDType = indices.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), indices.dataType(), gatherND_, (input, indices, output), SD_COMMON_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void gather_(NDArray* input, NDArray* indices, NDArray* output, const std::vector<int>& intArgs) {
|
||||
int axis = intArgs.size() > 0 ? intArgs[0] : 0;
|
||||
const int inputRank = input->rankOf();
|
||||
if (axis < 0) axis += inputRank;
|
||||
|
||||
const int numOfIntArgs = intArgs.size();
|
||||
|
||||
if (indices != nullptr) {
|
||||
for (sd::LongType i = 0; i < indices->lengthOf(); ++i)
|
||||
if (indices->e<sd::LongType>(i) >= input->sizeAt(axis))
|
||||
THROW_EXCEPTION(
|
||||
"helpers::gather function: indices array contains wrong elements, each element must be smaller than "
|
||||
"corresponding dimension of input array !");
|
||||
|
||||
// first case: indices consist of only one scalar
|
||||
if (indices->isScalar()) {
|
||||
if (input->rankOf() <= 1) {
|
||||
// For scalar indices, rank 0 or 1 input: can't do tensor along dimension 0 as this is whole array... instead,
|
||||
// we want to get a scalar
|
||||
auto idx = indices->e<sd::LongType>(0);
|
||||
auto scalarNDArray = input->e(idx);
|
||||
output->assign(&scalarNDArray);
|
||||
} else {
|
||||
// tadForDimensions expects the dimensions to create TADs along,
|
||||
// NOT the dimensions to exclude
|
||||
std::vector<sd::LongType> axesVec = {axis};
|
||||
// Pass the axis directly - TadCalculator will handle the exclusion internally
|
||||
auto tadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), &axesVec);
|
||||
|
||||
auto tadArr = NDArray(reinterpret_cast<void*>(reinterpret_cast<T*>(input->buffer()) +
|
||||
tadPack->primaryOffsets()[indices->e<sd::LongType>(0)]),
|
||||
tadPack->primaryShapeInfo(), output->getContext(), 0, 0);
|
||||
output->assign(&tadArr);
|
||||
}
|
||||
} else if (input->rankOf() == 1 && indices->isVector()) {
|
||||
// special case
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) output->p(e, input->e<T>(indices->e<sd::LongType>(e)));
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, indices->lengthOf());
|
||||
} else {
|
||||
std::vector<sd::LongType> dimsOut(indices->rankOf());
|
||||
std::iota(dimsOut.begin(), dimsOut.end(), axis); // fill with axis, axis+1, ... indices->rankOf()-1
|
||||
const sd::LongType numOfSubArrs = ShapeUtils::getNumOfSubArrs(output->shapeInfo(), dimsOut);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
NDArray *subArrOut = (*output)(i, dimsOut);
|
||||
NDArray *subArrIn = (*input)(indices->e<sd::LongType>(i), {axis});
|
||||
subArrOut->assign(subArrIn);
|
||||
delete subArrOut;
|
||||
delete subArrIn;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfSubArrs);
|
||||
}
|
||||
} else {
|
||||
for (int i = 1; i < numOfIntArgs; ++i)
|
||||
if (intArgs[i] >= input->sizeAt(axis))
|
||||
THROW_EXCEPTION(
|
||||
"helpers::gather function: some of input indexes is larger than corresponding shape of input array !");
|
||||
|
||||
// we only allow scalar/vector case here
|
||||
if (numOfIntArgs == 2) { // scalar case
|
||||
NDArray *view = (*input)(intArgs[1], {axis});
|
||||
output->assign(view);
|
||||
delete view;
|
||||
} else { // vector case
|
||||
const sd::LongType numOfSubArrs = ShapeUtils::getNumOfSubArrs(output->shapeInfo(), {axis});
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
NDArray *subArrOut = (*output)(i, {axis});
|
||||
NDArray *subArrIn = (*input)(intArgs[i + 1], {axis});
|
||||
subArrOut->assign(subArrIn);
|
||||
delete subArrIn;
|
||||
delete subArrOut;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfSubArrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gather(NDArray* input, NDArray* indices, NDArray* output, const std::vector<int>& intArgs) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), gather_, (input, indices, output, intArgs), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,42 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/axis.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static void applyGradientDescent_(NDArray* input, NDArray* step, double weight, NDArray* output) {
|
||||
auto lambda = LAMBDA_TT(_x, _y, weight) { return _x - (_y * weight); });
|
||||
|
||||
input->applyPairwiseLambda<T>(step, lambda, output);
|
||||
}
|
||||
|
||||
void applyGradientDescent(sd::LaunchContext* context, NDArray* input, NDArray* step, double weight, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), applyGradientDescent_, (input, step, weight, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void applyGradientDescent_,
|
||||
(NDArray * input, NDArray* step, double weight, NDArray* output), SD_FLOAT_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,103 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/hamming.h>
|
||||
#include <ops/declarable/helpers/helpers.h>
|
||||
#include <system/selective_rendering.h>
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
static sd::LongType hamming_distance(unsigned long long x, unsigned long long y) {
|
||||
sd::LongType dist = 0;
|
||||
|
||||
for (unsigned long long val = x ^ y; val > 0; val /= 2) {
|
||||
if (val & 1) dist++;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void _hamming(LaunchContext *context, NDArray &x, NDArray &y, NDArray &z) {
|
||||
auto xEws = x.ews();
|
||||
auto yEws = y.ews();
|
||||
|
||||
auto xBuffer = x.bufferAsT<X>();
|
||||
auto yBuffer = y.bufferAsT<X>();
|
||||
|
||||
sd::LongType distance = 0;
|
||||
auto lengthOf = x.lengthOf();
|
||||
int maxThreads = sd::math::sd_min<int>(256, omp_get_max_threads());
|
||||
sd::LongType intermediate[256];
|
||||
|
||||
// nullify temp values
|
||||
for (int e = 0; e < maxThreads; e++) intermediate[e] = 0;
|
||||
|
||||
if (xEws == 1 && yEws == 1 && x.ordering() == y.ordering()) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
auto _x = static_cast<unsigned long long>(xBuffer[e]);
|
||||
auto _y = static_cast<unsigned long long>(yBuffer[e]);
|
||||
|
||||
intermediate[thread_id] += hamming_distance(_x, _y);
|
||||
}
|
||||
};
|
||||
|
||||
maxThreads = samediff::Threads::parallel_for(func, 0, lengthOf);
|
||||
} else if (xEws > 1 && yEws > 1 && x.ordering() == y.ordering()) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
auto _x = static_cast<unsigned long long>(xBuffer[e * xEws]);
|
||||
auto _y = static_cast<unsigned long long>(yBuffer[e * yEws]);
|
||||
|
||||
intermediate[thread_id] += hamming_distance(_x, _y);
|
||||
}
|
||||
};
|
||||
|
||||
maxThreads = samediff::Threads::parallel_for(func, 0, lengthOf);
|
||||
} else {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
auto _x = static_cast<unsigned long long>(x.e<sd::LongType>(e));
|
||||
auto _y = static_cast<unsigned long long>(y.e<sd::LongType>(e));
|
||||
|
||||
intermediate[thread_id] += hamming_distance(_x, _y);
|
||||
}
|
||||
};
|
||||
|
||||
maxThreads = samediff::Threads::parallel_for(func, 0, lengthOf);
|
||||
}
|
||||
|
||||
// accumulate intermediate variables into output array
|
||||
for (int e = 0; e < maxThreads; e++) distance += intermediate[e];
|
||||
|
||||
z.p(0, distance);
|
||||
}
|
||||
|
||||
void hamming(LaunchContext *context, NDArray &x, NDArray &y, NDArray &output) {
|
||||
auto xDType = x.dataType();
|
||||
auto outputDType = output.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(x.dataType(), output.dataType(), _hamming, (context, x, y, output), SD_INTEGER_TYPES, SD_INTEGER_TYPES);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,107 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/hashcode.h>
|
||||
#if NOT_EXCLUDED(OP_hashcode)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static void hashCode_(LaunchContext *context, NDArray &array, NDArray &result) {
|
||||
sd::LongType blockSize = 32;
|
||||
auto length = array.lengthOf();
|
||||
int numBlocks = length / blockSize + ((length % blockSize == 0) ? 0 : 1);
|
||||
auto tempA = NDArrayFactory::create<sd::LongType>('c', {numBlocks}, context);
|
||||
auto tempB = NDArrayFactory::create<sd::LongType>('c', {numBlocks / blockSize + 1}, context);
|
||||
|
||||
auto buffer = array.bufferAsT<T>();
|
||||
auto tempBufferA = tempA->bufferAsT<sd::LongType>();
|
||||
auto tempBufferB = tempB->bufferAsT<sd::LongType>();
|
||||
|
||||
// default buffer is the first one, because it might be the last one in case of small arrays (< blockSize)
|
||||
auto tempBuffer = tempBufferA;
|
||||
auto tempResult = tempBufferB;
|
||||
|
||||
// we divide array into 32 element chunks, and store intermediate results once
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto b = start; b < stop; b++) {
|
||||
auto blockBuffer = buffer + b * numBlocks;
|
||||
|
||||
sd::LongType r = 1;
|
||||
for (sd::LongType e = 0; e < blockSize && e + (b * numBlocks) < length; e++) {
|
||||
auto v = longBytes<T>(blockBuffer[e]);
|
||||
r = 31 * r + v;
|
||||
}
|
||||
|
||||
tempBuffer[b] = r;
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func, 0, numBlocks);
|
||||
|
||||
// we replace pointer with intermediate one, and repeat only one chunk left
|
||||
int iterationCount = 0;
|
||||
while (numBlocks > 1) {
|
||||
int lastLength = numBlocks;
|
||||
numBlocks = lastLength / blockSize + ((lastLength % blockSize == 0) ? 0 : 1);
|
||||
|
||||
auto func2 = PRAGMA_THREADS_FOR {
|
||||
for (auto b = start; b < stop; b++) {
|
||||
auto blockBuffer = tempBuffer + b * numBlocks;
|
||||
|
||||
sd::LongType r = 1;
|
||||
for (sd::LongType e = 0; e < blockSize && e + (b * numBlocks) < lastLength; e++) {
|
||||
auto v = longBytes<T>(static_cast<T>(blockBuffer[e]));
|
||||
r = 31 * r + v;
|
||||
}
|
||||
|
||||
tempResult[b] = r;
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func2, 0, numBlocks);
|
||||
|
||||
iterationCount++;
|
||||
// swapping buffers
|
||||
if (iterationCount % 2 == 0) {
|
||||
tempBuffer = tempBufferA;
|
||||
tempResult = tempBufferB;
|
||||
} else {
|
||||
tempBuffer = tempBufferB;
|
||||
tempResult = tempBufferA;
|
||||
}
|
||||
}
|
||||
|
||||
if (length <= blockSize)
|
||||
result.p(0, tempBufferA[0]);
|
||||
else
|
||||
result.p(0, tempResult[0]);
|
||||
|
||||
delete tempA;
|
||||
delete tempB;
|
||||
}
|
||||
|
||||
void hashCode(LaunchContext *context, NDArray &array, NDArray &result) {
|
||||
BUILD_SINGLE_SELECTOR(array.dataType(), hashCode_, (context, array, result), SD_COMMON_TYPES);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/histogram.h>
|
||||
#include <system/selective_rendering.h>
|
||||
#if NOT_EXCLUDED(OP_histogram)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename X, typename Z>
|
||||
static void histogram_(void const *xBuffer, sd::LongType const *xShapeInfo, void *zBuffer,
|
||||
sd::LongType const *zShapeInfo, sd::LongType numBins, double min_val, double max_val) {
|
||||
auto dx = reinterpret_cast<X const *>(xBuffer);
|
||||
auto result = reinterpret_cast<Z *>(zBuffer);
|
||||
|
||||
int length = shape::length(xShapeInfo);
|
||||
|
||||
X binSize = static_cast<X>((max_val - min_val) / (numBins));
|
||||
|
||||
// FIXME: this op should be parallelized
|
||||
{
|
||||
int *bins = new int[numBins];
|
||||
std::memset(bins, 0, sizeof(int) * numBins);
|
||||
|
||||
PRAGMA_OMP_SIMD
|
||||
for (int x = 0; x < length; x++) {
|
||||
int idx = (int)((dx[x] - min_val) / binSize);
|
||||
if (idx < 0)
|
||||
idx = 0;
|
||||
else if (idx >= numBins)
|
||||
idx = numBins - 1;
|
||||
|
||||
bins[idx]++;
|
||||
}
|
||||
|
||||
PRAGMA_OMP_SIMD
|
||||
for (sd::LongType x = 0; x < numBins; x++) {
|
||||
result[x] += bins[x];
|
||||
}
|
||||
|
||||
delete[] bins;
|
||||
}
|
||||
}
|
||||
|
||||
void histogramHelper(sd::LaunchContext *context, NDArray &input, NDArray &output) {
|
||||
sd::LongType numBins = output.lengthOf();
|
||||
auto min = input.reduceNumber(reduce::SameOps::Min);
|
||||
auto max = input.reduceNumber(reduce::SameOps::Max);
|
||||
double min_val = min->e<double>(0);
|
||||
double max_val = max->e<double>(0);
|
||||
auto inputDType = input.dataType();
|
||||
auto outputDType = output.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(
|
||||
input.dataType(), output.dataType(), histogram_,
|
||||
(input.buffer(), input.shapeInfo(), output.buffer(), output.shapeInfo(), numBins, min_val, max_val),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
|
||||
delete min;
|
||||
delete max;
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 31.08.2018
|
||||
//
|
||||
#include <ops/declarable/helpers/histogramFixedWidth.h>
|
||||
#if NOT_EXCLUDED(OP_histogram_fixed_width)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
void histogramFixedWidth_(NDArray& input, NDArray& range, NDArray& output) {
|
||||
const int nbins = output.lengthOf();
|
||||
|
||||
// firstly initialize output with zeros
|
||||
output.nullify();
|
||||
|
||||
const T leftEdge = static_cast<T>(range.e<double>(0));
|
||||
const T rightEdge = static_cast<T>(range.e<double>(1));
|
||||
|
||||
const T binWidth = (rightEdge - leftEdge) / nbins;
|
||||
const T secondEdge = leftEdge + binWidth;
|
||||
const T lastButOneEdge = rightEdge - binWidth;
|
||||
|
||||
sd::LongType inputLength = input.lengthOf();
|
||||
|
||||
// FIXME: make this one parallel without CRITICAL section
|
||||
for (sd::LongType i = 0; i < inputLength; ++i) {
|
||||
const T value = input.e<T>(i);
|
||||
|
||||
if (value < secondEdge) {
|
||||
output.p<sd::LongType>(0, output.e<sd::LongType>(0) + 1);
|
||||
} else if (value >= lastButOneEdge) {
|
||||
output.p<sd::LongType>(nbins - 1, output.e<sd::LongType>(nbins - 1) + 1);
|
||||
} else {
|
||||
sd::LongType currInd = static_cast<sd::LongType>((value - leftEdge) / binWidth);
|
||||
output.p<sd::LongType>(currInd, output.e<sd::LongType>(currInd) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void histogramFixedWidth(sd::LaunchContext* context, NDArray& input, NDArray& range, NDArray& output) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), histogramFixedWidth_, (input, range, output), SD_COMMON_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void histogramFixedWidth_, (NDArray& input, NDArray& range, NDArray& output),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 19.09.2018
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/im2col.h>
|
||||
#if NOT_EXCLUDED(OP_im2col)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void im2col_(sd::LaunchContext& context, NDArray& input, NDArray& output, const LongType kH,
|
||||
const LongType kW, const LongType sH, const LongType sW, const LongType pH, const LongType pW,
|
||||
const LongType dH, const LongType dW, NDArray& arrZeroPadVal) {
|
||||
// input [bS, iC, iH, iW] is convoluted to output [bS, iC, kH, kW, oH, oW]
|
||||
if(input.rankOf() != 4) {
|
||||
THROW_EXCEPTION("ops::helpers::im2col: input array must have rank = 4");
|
||||
}
|
||||
|
||||
if(output.rankOf() != 6) {
|
||||
THROW_EXCEPTION("ops::helpers::im2col: output array must have rank = 6");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
auto imBuff = static_cast<T const*>(input.buffer());
|
||||
auto colBuff = static_cast<T*>(output.buffer());
|
||||
auto imShapeBuffer = input.shapeInfo();
|
||||
auto colShapeBuffer = output.shapeInfo();
|
||||
auto colShape = shape::shapeOf(colShapeBuffer);
|
||||
auto colStride = shape::stride(colShapeBuffer);
|
||||
auto imShape = shape::shapeOf(imShapeBuffer);
|
||||
auto imStride = shape::stride(imShapeBuffer);
|
||||
|
||||
const T zeroPadVal = arrZeroPadVal.e<T>(0);
|
||||
|
||||
const LongType bS = imShape[0];
|
||||
const LongType iC = imShape[1];
|
||||
const LongType iH = imShape[2];
|
||||
const LongType iW = imShape[3];
|
||||
const LongType oH = colShape[4];
|
||||
const LongType oW = colShape[5];
|
||||
const sd::LongType colStride0 = colStride[0];
|
||||
const sd::LongType colStride1 = colStride[1];
|
||||
const sd::LongType colStride2 = colStride[2];
|
||||
const sd::LongType colStride3 = colStride[3];
|
||||
const sd::LongType colStride4 = colStride[4];
|
||||
const sd::LongType colStride5 = colStride[5];
|
||||
const sd::LongType imStride0 = imStride[0];
|
||||
const sd::LongType imStride1 = imStride[1];
|
||||
const sd::LongType imStride2 = imStride[2];
|
||||
const sd::LongType imStride3 = imStride[3];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
sd::LongType imRow, imCol, colIndex, imIndex;
|
||||
|
||||
for (auto b = start_x; b < stop_x; b += inc_x) {
|
||||
for (auto colH = start_y; colH < stop_y; colH += inc_y) {
|
||||
for (sd::LongType colW = 0; colW < oW; ++colW) {
|
||||
for (sd::LongType c = 0; c < iC; ++c) {
|
||||
for (sd::LongType kRow = 0; kRow < kH; ++kRow) {
|
||||
for (sd::LongType kCol = 0; kCol < kW; ++kCol) {
|
||||
imRow = (-pH + kRow * dH) + colH * sH;
|
||||
imCol = (-pW + kCol * dW) + colW * sW;
|
||||
|
||||
colIndex = b * colStride0 + c * colStride1 + kRow * colStride2 + kCol * colStride3 +
|
||||
colH * colStride4 + colW * colStride5;
|
||||
|
||||
if (static_cast<LongType>(imRow) >= static_cast<LongType>(iH) ||
|
||||
static_cast<LongType>(imRow) < 0 ||
|
||||
static_cast<LongType>(imCol) >= static_cast<LongType>(iW) ||
|
||||
static_cast<LongType>(imCol) < 0) {
|
||||
if (colIndex < output.lengthOf()) {
|
||||
colBuff[colIndex] = zeroPadVal;
|
||||
}
|
||||
} else {
|
||||
imIndex = b * imStride0 + c * imStride1 + imRow * imStride2 + imCol * imStride3;
|
||||
if (colIndex < output.lengthOf() && imIndex < input.lengthOf()) {
|
||||
colBuff[colIndex] = static_cast<T>(imBuff[imIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, oH, 1);
|
||||
}
|
||||
|
||||
void im2col(sd::LaunchContext& context, NDArray& im, NDArray& col, const LongType kH, const LongType kW,
|
||||
const LongType sH, const LongType sW, const LongType pH, const LongType pW, const LongType dH,
|
||||
const LongType dW, NDArray& arrZeroPadVal) {
|
||||
|
||||
BUILD_SINGLE_SELECTOR(im.dataType(), im2col_, (context, im, col, kH, kW, sH, sW, pH, pW, dH, dW, arrZeroPadVal),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
|
||||
//
|
||||
// @author sgazeos@gmail.com
|
||||
//
|
||||
#include <array/NDArray.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
typedef std::vector<std::vector<float>> ColorTable_t;
|
||||
static ColorTable_t DefaultColorTable(int depth) {
|
||||
std::vector<std::vector<float>> colorTable;
|
||||
colorTable.emplace_back(std::vector<float>({1, 1, 0, 1})); // 0: yellow
|
||||
colorTable.emplace_back(std::vector<float>({0, 0, 1, 1})); // 1: blue
|
||||
colorTable.emplace_back(std::vector<float>({1, 0, 0, 1})); // 2: red
|
||||
colorTable.emplace_back(std::vector<float>({0, 1, 0, 1})); // 3: lime
|
||||
colorTable.emplace_back(std::vector<float>({0.5, 0, 0.5, 1})); // 4: purple
|
||||
colorTable.emplace_back(std::vector<float>({0.5, 0.5, 0, 1})); // 5: olive
|
||||
colorTable.emplace_back(std::vector<float>({0.5, 0, 0, 1})); // 6: maroon
|
||||
colorTable.emplace_back(std::vector<float>({0, 0, 0.5, 1})); // 7: navy blue
|
||||
colorTable.emplace_back(std::vector<float>({0, 1, 1, 1})); // 8: aqua
|
||||
colorTable.emplace_back(std::vector<float>({1, 0, 1, 1})); // 9: fuchsia
|
||||
|
||||
if (depth == 1) {
|
||||
for (size_t i = 0; i < colorTable.size(); i++) {
|
||||
colorTable[i][0] = 1;
|
||||
}
|
||||
}
|
||||
return colorTable;
|
||||
}
|
||||
|
||||
void drawBoundingBoxesFunctor(sd::LaunchContext* context, NDArray* images, NDArray* boxes, NDArray* colors,
|
||||
NDArray* output) {
|
||||
// images - batch of 3D images with BW (last dim = 1), RGB (last dim = 3) or RGBA (last dim = 4) channel set
|
||||
// boxes - batch of 2D bounds with last dim (y_start, x_start, y_end, x_end) to compute i and j as
|
||||
// floor((height - 1 ) * y_start) => rowStart, floor((height - 1) * y_end) => rowEnd
|
||||
// floor((width - 1 ) * x_start) => colStart, floor((width - 1) * x_end) => colEnd
|
||||
// height = images->sizeAt(1), width = images->sizeAt(2)
|
||||
// colors - colors for each box given
|
||||
// set up color for each box as frame
|
||||
auto batchSize = images->sizeAt(0);
|
||||
auto boxSize = boxes->sizeAt(0);
|
||||
auto height = images->sizeAt(1);
|
||||
auto width = images->sizeAt(2);
|
||||
auto channels = images->sizeAt(3);
|
||||
|
||||
output->assign(images); // fill up all output with input images, then fill up boxes
|
||||
ColorTable_t colorTable;
|
||||
if (colors) {
|
||||
for (auto i = 0; i < colors->sizeAt(0); i++) {
|
||||
std::vector<float> colorValue(4);
|
||||
for (auto j = 0; j < 4; j++) {
|
||||
colorValue[j] = j < colors->sizeAt(1) ? colors->e<float>(i, j) : 1.f;
|
||||
}
|
||||
colorTable.emplace_back(colorValue);
|
||||
}
|
||||
}
|
||||
if (colorTable.empty()) colorTable = DefaultColorTable(channels);
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto batch = start; batch < stop; ++batch) { // loop by batch
|
||||
const sd::LongType numBoxes = boxes->sizeAt(1);
|
||||
for (auto boxIndex = 0; boxIndex < numBoxes; ++boxIndex) {
|
||||
auto colorIndex = boxIndex % colorTable.size();
|
||||
auto rowStart = sd::LongType((height - 1) * boxes->t<float>(batch, boxIndex, 0));
|
||||
auto rowStartBound = sd::math::sd_max(sd::LongType(0), rowStart);
|
||||
auto rowEnd = sd::LongType((height - 1) * boxes->t<float>(batch, boxIndex, 2));
|
||||
auto rowEndBound = sd::math::sd_min(sd::LongType(height - 1), rowEnd);
|
||||
auto colStart = sd::LongType((width - 1) * boxes->t<float>(batch, boxIndex, 1));
|
||||
auto colStartBound = sd::math::sd_max(sd::LongType(0), colStart);
|
||||
auto colEnd = sd::LongType((width - 1) * boxes->t<float>(batch, boxIndex, 3));
|
||||
auto colEndBound = sd::math::sd_min(sd::LongType(width - 1), colEnd);
|
||||
|
||||
if (rowStart > rowEnd || colStart > colEnd) {
|
||||
sd_debug(
|
||||
"helpers::drawBoundingBoxesFunctor: Bounding box (%lld, %lld, %lld, %lld) is inverted "
|
||||
"and will not be drawn\n",
|
||||
rowStart, colStart, rowEnd, colEnd);
|
||||
continue;
|
||||
}
|
||||
if (rowStart >= height || rowEnd < 0 || colStart >= width || colEnd < 0) {
|
||||
sd_debug(
|
||||
"helpers::drawBoundingBoxesFunctor: Bounding box (%lld, %lld, %lld, %lld) is completely "
|
||||
"outside the image and not be drawn\n ",
|
||||
rowStart, colStart, rowEnd, colEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw upper line
|
||||
if (rowStart >= 0) {
|
||||
for (auto j = colStartBound; j <= colEndBound; ++j)
|
||||
for (auto c = 0; c < channels; c++) {
|
||||
output->p(batch, rowStart, j, c, colorTable[colorIndex][c]);
|
||||
}
|
||||
}
|
||||
// Draw bottom line.
|
||||
if (rowEnd < height) {
|
||||
for (auto j = colStartBound; j <= colEndBound; ++j)
|
||||
for (auto c = 0; c < channels; c++) {
|
||||
output->p(batch, rowEnd, j, c, colorTable[colorIndex][c]);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw left line.
|
||||
if (colStart >= 0) {
|
||||
for (auto i = rowStartBound; i <= rowEndBound; ++i)
|
||||
for (auto c = 0; c < channels; c++) {
|
||||
output->p(batch, i, colStart, c, colorTable[colorIndex][c]);
|
||||
}
|
||||
}
|
||||
// Draw right line.
|
||||
if (colEnd < width) {
|
||||
for (auto i = rowStartBound; i <= rowEndBound; ++i)
|
||||
for (auto c = 0; c < channels; c++) {
|
||||
output->p(batch, i, colEnd, c, colorTable[colorIndex][c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func, 0, batchSize);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,926 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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
|
||||
* *****************************************************************************
|
||||
*/
|
||||
/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
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.
|
||||
==============================================================================*/
|
||||
|
||||
//
|
||||
// @author sgazeos@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/headers/parity_ops.h>
|
||||
#include <ops/declarable/helpers/image_resize.h>
|
||||
|
||||
#include "../cross.h"
|
||||
#include <system/selective_rendering.h>
|
||||
#if NOT_EXCLUDED(OP_image_resize)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <class Scaler>
|
||||
static inline void computeInterpolationWeights(const Scaler scaler, sd::LongType outSize, sd::LongType inSize,
|
||||
double scale, BilinearInterpolationData* interpolationData) {
|
||||
interpolationData[outSize].bottomIndex = 0;
|
||||
interpolationData[outSize].topIndex = 0;
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto k = start; k < stop; k++) {
|
||||
auto i = (outSize - k - 1);
|
||||
double const in = scaler(i, scale);
|
||||
double const in_f = sd::math::sd_floor<double, double>(in);
|
||||
double const in_c = sd::math::sd_ceil<double, double>(in);
|
||||
interpolationData[i].bottomIndex =
|
||||
sd::math::sd_max(static_cast<sd::LongType>(in_f), (sd::LongType)0LL); // static_cast<sd::LongType>(in);
|
||||
interpolationData[i].topIndex = sd::math::sd_min(static_cast<sd::LongType>(in_c), inSize - 1);
|
||||
interpolationData[i].interpolarValue = in - in_f;
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, outSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bilinear interpolation from the appropriate 4 float points
|
||||
* and the linear interpolation weights.
|
||||
*/
|
||||
// static void
|
||||
// resizeImage(NDArray *images, sd::LongType batchSize, sd::LongType inHeight, sd::LongType inWidth,
|
||||
// sd::LongType outHeight,
|
||||
// sd::LongType outWidth, sd::LongType channels,
|
||||
// std::vector<BilinearInterpolationData> const& xs,
|
||||
// std::vector<BilinearInterpolationData> const& ys,
|
||||
// NDArray *output);
|
||||
|
||||
template <typename T, typename Z>
|
||||
static void resizeImage_(T const* pInputBuf, sd::LongType batchSize, sd::LongType inHeight, sd::LongType inWidth,
|
||||
sd::LongType outHeight, sd::LongType outWidth, sd::LongType channels,
|
||||
std::vector<BilinearInterpolationData> const& xs,
|
||||
std::vector<BilinearInterpolationData> const& ys, Z* pOutputBuf) {
|
||||
sd::LongType inRowSize = inWidth * channels;
|
||||
sd::LongType inBatchNumValues = inHeight * inRowSize;
|
||||
sd::LongType outRowSize = outWidth * channels;
|
||||
|
||||
BilinearInterpolationData const* xsPtr = xs.data();
|
||||
|
||||
auto computeBilinear = [](double topLeft, double topRight, double bottomLeft, double bottomRight, double xVal,
|
||||
double yVal) {
|
||||
double top = topLeft + (topRight - topLeft) * xVal;
|
||||
double bottom = bottomLeft + (bottomRight - bottomLeft) * xVal;
|
||||
return top + (bottom - top) * yVal;
|
||||
};
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto batch = start; batch < stop; ++batch) {
|
||||
auto pInput = pInputBuf + batch * inBatchNumValues;
|
||||
for (sd::LongType y = 0; y < outHeight; ++y) {
|
||||
auto pOutput = pOutputBuf + (batch * outHeight + y) * outRowSize;
|
||||
const T* ysInputLowerPtr = pInput + ys[y].bottomIndex * inRowSize;
|
||||
const T* ysInputUpperPtr = pInput + ys[y].topIndex * inRowSize;
|
||||
double yVal = ys[y].interpolarValue;
|
||||
for (sd::LongType x = 0; x < outWidth; ++x) {
|
||||
auto xsBottom = xsPtr[x].bottomIndex;
|
||||
auto xsTop = xsPtr[x].topIndex;
|
||||
auto xVal = xsPtr[x].interpolarValue;
|
||||
for (sd::LongType c = 0; c < channels; ++c) {
|
||||
double topLeft(ysInputLowerPtr[xsBottom + c]);
|
||||
double topRight(ysInputLowerPtr[xsTop + c]);
|
||||
double bottomLeft(ysInputUpperPtr[xsBottom + c]);
|
||||
double bottomRight(ysInputUpperPtr[xsTop + c]);
|
||||
pOutput[x * channels + c] = computeBilinear(topLeft, topRight, bottomLeft, bottomRight, xVal, yVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func, 0, batchSize);
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static sd::Status resizeBilinearFunctor_(NDArray * images, int const width, int const height,
|
||||
bool const alignCorners, bool const halfPixelCenter, NDArray* output) {
|
||||
ImageResizerState st(alignCorners, halfPixelCenter);
|
||||
st.validateAndCalculateOutputSize(images, width, height);
|
||||
|
||||
const sd::LongType batchSize = images->sizeAt(0);
|
||||
const sd::LongType inHeight = images->sizeAt(1);
|
||||
const sd::LongType inWidth = images->sizeAt(2);
|
||||
const sd::LongType channels = images->sizeAt(3);
|
||||
|
||||
const sd::LongType outHeight = output->sizeAt(1);
|
||||
const sd::LongType outWidth = output->sizeAt(2);
|
||||
|
||||
// Handle no-op resizes efficiently.
|
||||
if (outHeight == inHeight && outWidth == inWidth) {
|
||||
output->assign(images);
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
std::vector<BilinearInterpolationData> ys(outHeight + 1);
|
||||
std::vector<BilinearInterpolationData> xs(outWidth + 1);
|
||||
if (halfPixelCenter) {
|
||||
computeInterpolationWeights(HalfPixelScaler(), outHeight, inHeight, st.heightScale, ys.data());
|
||||
computeInterpolationWeights(HalfPixelScaler(), outWidth, inWidth, st.widthScale, xs.data());
|
||||
|
||||
} else {
|
||||
// Compute the cached interpolation weights on the x and y dimensions.
|
||||
computeInterpolationWeights(LegacyScaler(), outHeight, inHeight, st.heightScale, ys.data());
|
||||
computeInterpolationWeights(LegacyScaler(), outWidth, inWidth, st.widthScale, xs.data());
|
||||
}
|
||||
int xsSize = xs.size();
|
||||
// Scale x interpolation weights to avoid a multiplication during iteration.
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
xs[i].bottomIndex *= channels;
|
||||
xs[i].topIndex *= channels;
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, xsSize);
|
||||
|
||||
resizeImage_<X, Z>(images->getDataBuffer()->primaryAsT<X>(), batchSize, inHeight, inWidth, outHeight, outWidth,
|
||||
channels, xs, ys, output->dataBuffer()->primaryAsT<Z>());
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
template <class Scaler, typename T>
|
||||
void resizeNeighborImpl(ImageResizerState const& st, NDArray * images, NearestMode nearestMode, NDArray* output) {
|
||||
const sd::LongType batchSize = st.batchSize;
|
||||
const sd::LongType inHeight = st.inHeight;
|
||||
const sd::LongType inWidth = st.inWidth;
|
||||
const sd::LongType channels = st.channels;
|
||||
|
||||
const sd::LongType outHeight = st.outHeight;
|
||||
const sd::LongType outWidth = st.outWidth;
|
||||
Scaler scaler;
|
||||
constexpr bool halfPixelCenter =
|
||||
std::is_same<Scaler, HalfPixelScaler>::value || std::is_same<Scaler, HalfPixelScalerNN>::value;
|
||||
|
||||
float (*modeFunc)(float);
|
||||
switch (nearestMode) {
|
||||
case NearestMode::FLOOR:
|
||||
modeFunc = [](float x){return sd::math::p_floor<float>(x);};
|
||||
break;
|
||||
case NearestMode::ROUND_PREFER_FLOOR:
|
||||
modeFunc = [](float x){return sd::math::p_round_prefer_floor<float>(x);};
|
||||
break;
|
||||
case NearestMode::ROUND_PREFER_CEIL:
|
||||
modeFunc = [](float x){return sd::math::p_round_prefer_ceil<float>(x);};
|
||||
break;
|
||||
case NearestMode::CEIL:
|
||||
modeFunc = [](float x){return sd::math::p_ceil<float>(x);};
|
||||
break;
|
||||
default:
|
||||
modeFunc = [](float x){return sd::math::p_floor<float>(x);};
|
||||
}
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
for (auto b = start_x; b < stop_x; b += inc_x) {
|
||||
for (auto y = start_y; y < stop_y; y += inc_y) {
|
||||
auto posY = static_cast<sd::LongType>(modeFunc(scaler(y, st.heightScale)));
|
||||
sd::LongType inY = sd::math::sd_min(posY, inHeight - 1);
|
||||
if (halfPixelCenter) {
|
||||
inY = sd::math::sd_max(0LL, inY);
|
||||
}
|
||||
for (sd::LongType x = 0; x < outWidth; ++x) {
|
||||
auto posX = static_cast<sd::LongType>(modeFunc(scaler(x, st.widthScale)));
|
||||
sd::LongType inX = sd::math::sd_min(posX, inWidth - 1);
|
||||
if (halfPixelCenter) {
|
||||
inX = sd::math::sd_max(0LL, inX);
|
||||
}
|
||||
// copy pixel over all channels
|
||||
for (sd::LongType e = 0; e < channels; e++) output->r<T>(b, y, x, e) = images->t<T>(b, inY, inX, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, batchSize, 1, 0, outHeight, 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sd::Status resizeNeighborFunctor_(NDArray * images, int const width, int const height,
|
||||
CoordinateTransformationMode coorMode, NearestMode nearestMode, bool alignCorner,
|
||||
NDArray* output) {
|
||||
ImageResizerState st(alignCorner, (coorMode == HALF_PIXEL_NN));
|
||||
st.validateAndCalculateOutputSize(images, width, height);
|
||||
|
||||
// Handle no-op resizes efficiently.
|
||||
if (output->sizeAt(1) == images->sizeAt(1) && output->sizeAt(2) == images->sizeAt(2)) {
|
||||
output->assign(images);
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
switch (coorMode) {
|
||||
case ASYMMETRIC:
|
||||
resizeNeighborImpl<LegacyScaler, T>(st, images, nearestMode, output);
|
||||
break;
|
||||
case HALF_PIXEL:
|
||||
resizeNeighborImpl<HalfPixelScaler, T>(st, images, nearestMode, output);
|
||||
break;
|
||||
case HALF_PIXEL_NN:
|
||||
resizeNeighborImpl<HalfPixelScalerNN, T>(st, images, nearestMode, output);
|
||||
break;
|
||||
default:
|
||||
resizeNeighborImpl<HalfPixelScaler, T>(st, images, nearestMode, output);
|
||||
break;
|
||||
};
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
sd::Status resizeBilinearFunctor(sd::LaunchContext* context, NDArray * images, int const width, int const height,
|
||||
bool const alignCorners, bool const halfPixelCenter, NDArray* output) {
|
||||
|
||||
auto imagesDtype = images->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(images->dataType(), output->dataType(), return resizeBilinearFunctor_,
|
||||
(images, width, height, alignCorners, halfPixelCenter, output), SD_NUMERIC_TYPES,
|
||||
SD_FLOAT_TYPES);
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status resizeNeighborFunctor(sd::LaunchContext* context, NDArray * images, int const width, int const height,
|
||||
CoordinateTransformationMode coorMode, NearestMode nearestMode, bool alignCorner,
|
||||
NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(images->dataType(), return resizeNeighborFunctor_,
|
||||
(images, width, height, coorMode, nearestMode, alignCorner, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
// Bicubic interpolation
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
template <typename T>
|
||||
std::unique_ptr<T[]> initCoeffsTable(const double a) {
|
||||
// Allocate and initialize coefficients table using Bicubic
|
||||
// convolution algorithm.
|
||||
// https://en.wikipedia.org/wiki/Bicubic_interpolation
|
||||
KeysCubicKernelFunc<T> kernel(static_cast<T>(a));
|
||||
std::unique_ptr<T[]> coeffsTableUniq(new T[(kTableSize + 1) * 2]);
|
||||
T* coeffsTable = coeffsTableUniq.get();
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i <= stop; ++i) {
|
||||
float x = i * 1.0 / kTableSize;
|
||||
coeffsTable[i * 2] = kernel.calc_less1pt0(x);
|
||||
x += 1.0;
|
||||
coeffsTable[i * 2 + 1] = kernel.calc_less2pt0(x);
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, kTableSize);
|
||||
return coeffsTableUniq;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sd::Status resizeBicubicFunctor_(sd::LaunchContext* context, NDArray * image, int width, int height,
|
||||
bool preserveAspectRatio, bool antialias, NDArray* output) {
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status resizeBicubicFunctor(sd::LaunchContext* context, NDArray * image, int width, int height,
|
||||
bool preserveAspectRatio, bool antialias, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(image->dataType(), return resizeBicubicFunctor_,
|
||||
(context, image, width, height, preserveAspectRatio, antialias, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
template <typename Scaler>
|
||||
static void computeXWeightsAndIndices(const ImageResizerState& resizer_state, const float* coeffs_table,
|
||||
std::vector<WeightsAndIndices>* x_wais, bool exclude_outside) {
|
||||
CachedInterpolationCalculator calc;
|
||||
for (auto x = 0; x < resizer_state.outWidth; ++x) {
|
||||
WeightsAndIndices& x_wai = (*x_wais)[x];
|
||||
;
|
||||
getWeightsAndIndices<Scaler>(coeffs_table, resizer_state.widthScale, x, resizer_state.inWidth, &x_wai,
|
||||
exclude_outside);
|
||||
x_wai._advance = calc.Advance(x_wai._index0, x_wai._index1, x_wai._index2, x_wai._index3);
|
||||
(*x_wais)[x]._index0 *= resizer_state.wStride;
|
||||
(*x_wais)[x]._index1 *= resizer_state.wStride;
|
||||
(*x_wais)[x]._index2 *= resizer_state.wStride;
|
||||
(*x_wais)[x]._index3 *= resizer_state.wStride;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename F, typename Scaler>
|
||||
static void bicubicInterpolateWithCaching(NDArray * image, ImageResizerState const& resizerState,
|
||||
const double coefficient, bool exclude_outside, NDArray* output) {
|
||||
std::vector<WeightsAndIndices> xWais(resizerState.outWidth);
|
||||
auto coeffs_table_uniq = initCoeffsTable<float>(coefficient);
|
||||
float* coeffs_table = coeffs_table_uniq.get();
|
||||
|
||||
computeXWeightsAndIndices<Scaler>(resizerState, coeffs_table, &xWais, exclude_outside);
|
||||
|
||||
const auto numChannels = resizerState.channels;
|
||||
const auto batchNum = resizerState.batchSize;
|
||||
const auto outHeight = resizerState.outHeight;
|
||||
const auto outWidth = resizerState.outWidth;
|
||||
const auto batchStride = image->strideAt(0);
|
||||
const auto hStride = image->strideAt(1);
|
||||
const auto cStride = image->strideAt(3);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
const T* inputPtr = image->getDataBuffer()->primaryAsT<T>();
|
||||
F* pOutputY = output->dataBuffer()->primaryAsT<F>(); // output is float anyway
|
||||
std::vector<float> cachedValue(numChannels == 3 ? 0 : 4 * numChannels, 0);
|
||||
for (auto b = start; b < stop; ++b) {
|
||||
auto pInput = inputPtr + b * batchStride;
|
||||
for (sd::LongType y = 0; y < outHeight; ++y) {
|
||||
auto pOutput = &pOutputY[(b * outHeight + y) * outWidth * numChannels];
|
||||
|
||||
WeightsAndIndices yWai;
|
||||
getWeightsAndIndices<Scaler>(coeffs_table, resizerState.heightScale, y, resizerState.inHeight, &yWai,
|
||||
exclude_outside);
|
||||
// Make pointers represent offsets of data in inputBPtr.
|
||||
const T* y_ptr_0 = pInput + yWai._index0 * hStride;
|
||||
const T* y_ptr_1 = pInput + yWai._index1 * hStride;
|
||||
const T* y_ptr_2 = pInput + yWai._index2 * hStride;
|
||||
const T* y_ptr_3 = pInput + yWai._index3 * hStride;
|
||||
|
||||
if (numChannels == 3) {
|
||||
// Manually unroll case of 3 channels.
|
||||
F cached_value_0[4] = {0};
|
||||
F cached_value_1[4] = {0};
|
||||
F cached_value_2[4] = {0};
|
||||
for (sd::LongType x = 0; x < resizerState.outWidth; ++x) {
|
||||
const WeightsAndIndices& xWai = xWais[x];
|
||||
// Shift values in cached_value_* to fill first '_advance' values.
|
||||
switch (xWai._advance) {
|
||||
case 3:
|
||||
cached_value_0[0] = cached_value_0[1];
|
||||
cached_value_0[1] = cached_value_0[2];
|
||||
cached_value_0[2] = cached_value_0[3];
|
||||
cached_value_1[0] = cached_value_1[1];
|
||||
cached_value_1[1] = cached_value_1[2];
|
||||
cached_value_1[2] = cached_value_1[3];
|
||||
cached_value_2[0] = cached_value_2[1];
|
||||
cached_value_2[1] = cached_value_2[2];
|
||||
cached_value_2[2] = cached_value_2[3];
|
||||
break;
|
||||
case 2:
|
||||
cached_value_0[0] = cached_value_0[2];
|
||||
cached_value_0[1] = cached_value_0[3];
|
||||
cached_value_1[0] = cached_value_1[2];
|
||||
cached_value_1[1] = cached_value_1[3];
|
||||
cached_value_2[0] = cached_value_2[2];
|
||||
cached_value_2[1] = cached_value_2[3];
|
||||
break;
|
||||
case 1: {
|
||||
cached_value_0[0] = cached_value_0[3];
|
||||
cached_value_1[0] = cached_value_1[3];
|
||||
cached_value_2[0] = cached_value_2[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the remaining '4-_advance' values by computing.
|
||||
switch (xWai._advance) {
|
||||
case 0:
|
||||
cached_value_0[0] = computeYInterpolation(0, 0, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
cached_value_1[0] = computeYInterpolation(0, cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
cached_value_2[0] =
|
||||
computeYInterpolation(0, 2 * cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
|
||||
case 1:
|
||||
cached_value_0[1] = computeYInterpolation(1, 0, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
cached_value_1[1] = computeYInterpolation(1, cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
cached_value_2[1] =
|
||||
computeYInterpolation(1, 2 * cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
|
||||
case 2:
|
||||
cached_value_0[2] = computeYInterpolation(2, 0, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
cached_value_1[2] = computeYInterpolation(2, cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
cached_value_2[2] =
|
||||
computeYInterpolation(2, 2 * cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
|
||||
case 3:
|
||||
cached_value_0[3] = computeYInterpolation(3, 0, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
cached_value_1[3] = computeYInterpolation(3, cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
cached_value_2[3] =
|
||||
computeYInterpolation(3, 2 * cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
break;
|
||||
}
|
||||
pOutput[x * numChannels + 0] =
|
||||
compute(cached_value_0, xWai._weight0, xWai._weight1, xWai._weight2, xWai._weight3);
|
||||
pOutput[x * numChannels + 1] =
|
||||
compute(cached_value_1, xWai._weight0, xWai._weight1, xWai._weight2, xWai._weight3);
|
||||
pOutput[x * numChannels + 2] =
|
||||
compute(cached_value_2, xWai._weight0, xWai._weight1, xWai._weight2, xWai._weight3);
|
||||
}
|
||||
} else {
|
||||
for (sd::LongType x = 0; x < resizerState.outWidth; ++x) {
|
||||
const WeightsAndIndices& xWai = xWais[x];
|
||||
// Shift values in cachedValue to fill first '_advance' values.
|
||||
switch (xWai._advance) {
|
||||
case 3:
|
||||
for (auto c = 0; c < numChannels; ++c) {
|
||||
cachedValue[4 * c + 0] = cachedValue[4 * c + 1];
|
||||
cachedValue[4 * c + 1] = cachedValue[4 * c + 2];
|
||||
cachedValue[4 * c + 2] = cachedValue[4 * c + 3];
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (auto c = 0; c < numChannels; ++c) {
|
||||
cachedValue[4 * c + 0] = cachedValue[4 * c + 2];
|
||||
cachedValue[4 * c + 1] = cachedValue[4 * c + 3];
|
||||
}
|
||||
break;
|
||||
case 1: {
|
||||
for (auto c = 0; c < numChannels; ++c) {
|
||||
cachedValue[4 * c + 0] = cachedValue[4 * c + 3];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the remaining '4-_advance' values by computing.
|
||||
switch (xWai._advance) {
|
||||
case 0:
|
||||
for (auto c = 0; c < numChannels; ++c) {
|
||||
cachedValue[4 * c + 0] =
|
||||
computeYInterpolation(0, c * cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
}
|
||||
case 1:
|
||||
for (auto c = 0; c < numChannels; ++c) {
|
||||
cachedValue[4 * c + 1] =
|
||||
computeYInterpolation(1, c * cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
}
|
||||
case 2:
|
||||
for (auto c = 0; c < numChannels; ++c) {
|
||||
cachedValue[4 * c + 2] =
|
||||
computeYInterpolation(2, c * cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
}
|
||||
case 3:
|
||||
for (auto c = 0; c < numChannels; ++c) {
|
||||
cachedValue[4 * c + 3] =
|
||||
computeYInterpolation(3, c * cStride, yWai, y_ptr_0, y_ptr_1, y_ptr_2, y_ptr_3, xWai);
|
||||
}
|
||||
break;
|
||||
}
|
||||
for (auto c = 0; c < numChannels; ++c) {
|
||||
pOutput[x * numChannels + c] =
|
||||
(F)compute(&cachedValue[4 * c], xWai._weight0, xWai._weight1, xWai._weight2, xWai._weight3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(func, 0, batchNum);
|
||||
}
|
||||
|
||||
// simplified bicubic resize without antialiasing
|
||||
//
|
||||
template <typename T>
|
||||
sd::Status resizeBicubicFunctorA_(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const alignCorners, CoordinateTransformationMode coorMode, bool exclude_outside,
|
||||
double coefficient, NDArray* output) {
|
||||
ImageResizerState st(alignCorners, coorMode == HALF_PIXEL); // align_corners, half_pixel_align
|
||||
auto res = st.validateAndCreateOutput(image, width, height);
|
||||
if (res == sd::Status::OK) {
|
||||
switch (coorMode) {
|
||||
case ASYMMETRIC:
|
||||
bicubicInterpolateWithCaching<T, float, LegacyScaler>(image, st, coefficient, exclude_outside, output);
|
||||
break;
|
||||
case HALF_PIXEL:
|
||||
bicubicInterpolateWithCaching<T, float, HalfPixelScaler>(image, st, coefficient, exclude_outside, output);
|
||||
break;
|
||||
case HALF_PIXEL_NN:
|
||||
bicubicInterpolateWithCaching<T, float, HalfPixelScalerNN>(image, st, coefficient, exclude_outside, output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
sd::Status resizeBicubicFunctorA(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const alignCorners, CoordinateTransformationMode coorMode, bool exclude_outside,
|
||||
double coefficient, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(image->dataType(), return resizeBicubicFunctorA_,
|
||||
(context, image, width, height, alignCorners, coorMode, exclude_outside, coefficient, output),
|
||||
SD_NUMERIC_TYPES);
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
template <typename T>
|
||||
static void resizeArea(ImageResizerState const& st, std::vector<CachedInterpolation> const& caches,
|
||||
NDArray * input, NDArray* output) {
|
||||
T const* inputPtr = input->bufferAsT<T>();
|
||||
float scale = 1.f / (st.heightScale * st.widthScale);
|
||||
auto outputPtr = output->bufferAsT<float>(); // output is always float. TO DO: provide another float types also with
|
||||
// template <typename X, typename Z> declaration
|
||||
|
||||
auto batchProcess = PRAGMA_THREADS_FOR {
|
||||
for (auto batch = start; batch < stop; batch++) {
|
||||
for (auto y = 0; y < st.outHeight; ++y) {
|
||||
const float inY = y * st.heightScale;
|
||||
const float inY1 = (y + 1) * st.heightScale;
|
||||
// The start and end height indices of all the cells that could
|
||||
// contribute to the target cell.
|
||||
const sd::LongType yStart = math::sd_floor<float, sd::LongType>(inY);
|
||||
const sd::LongType yEnd = math::sd_ceil<float, sd::LongType>(inY1);
|
||||
|
||||
std::vector<ScaleCache<T>> yCaches;
|
||||
auto cacheLen = yEnd - yStart;
|
||||
if (cacheLen) {
|
||||
yCaches.resize(cacheLen);
|
||||
};
|
||||
ScaleCache<T>* yCachesPtr = yCaches.data();
|
||||
sd::LongType yCachesSize = yCaches.size();
|
||||
for (auto i = yStart, k = sd::LongType(0); i < yEnd; ++i, ++k) {
|
||||
ScaleCache<T> scaleCache;
|
||||
if (i < inY) {
|
||||
scaleCache.yScale = (i + 1 > inY1 ? st.heightScale : i + 1 - inY);
|
||||
} else {
|
||||
scaleCache.yScale = (i + 1 > inY1 ? inY1 - i : 1.0);
|
||||
}
|
||||
scaleCache.yPtr = inputPtr + (batch * st.bStride + bound(i, st.inHeight) * st.hStride);
|
||||
yCaches[k] = scaleCache;
|
||||
}
|
||||
float* output = outputPtr + (batch * st.outHeight + y) * st.channels * st.outWidth;
|
||||
|
||||
if (st.channels == 3) {
|
||||
for (sd::LongType x = 0; x < st.outWidth; ++x) {
|
||||
const CachedInterpolation& xCache = caches[x];
|
||||
computePatchSumOf3Channels<T>(scale, st, yCachesPtr, yCachesSize, xCache, output);
|
||||
output += st.channels;
|
||||
}
|
||||
} else {
|
||||
for (sd::LongType x = 0; x < st.outWidth; ++x) {
|
||||
const CachedInterpolation& xCache = caches[x];
|
||||
computePatchSum<T>(scale, st, yCachesPtr, yCachesSize, xCache, output);
|
||||
output += st.channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(batchProcess, 0, st.batchSize, 1);
|
||||
}
|
||||
|
||||
template <typename X>
|
||||
sd::Status resizeAreaFunctor_(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const alignCorners, NDArray* output) {
|
||||
ImageResizerState st(alignCorners, false); // Create resize info
|
||||
auto res = st.validateAndCalculateOutputSize(image, width, height);
|
||||
if (Status::OK == res) {
|
||||
std::vector<CachedInterpolation> xCached(st.outWidth);
|
||||
auto cachingProcedure = PRAGMA_THREADS_FOR {
|
||||
for (auto x = start; x < stop; x++) {
|
||||
auto& xCache = xCached[x];
|
||||
const float inX = x * st.widthScale;
|
||||
const float inX1 = (x + 1) * st.widthScale;
|
||||
|
||||
sd::LongType v = math::sd_floor<float, sd::LongType>(inX);
|
||||
xCache.start = v;
|
||||
xCache.startScale = v < inX ? (v + 1 > inX1 ? st.widthScale : v + 1 - inX) : (v + 1 > inX1 ? inX1 - v : 1.f);
|
||||
v = math::sd_ceil<float, sd::LongType>(inX1);
|
||||
xCache.end = v--;
|
||||
xCache.endMinusOneScale =
|
||||
v < inX ? (v + 1 > inX1 ? st.widthScale : v + 1 - inX) : (v + 1 > inX1 ? inX1 - v : 1.f);
|
||||
xCache.needsBounding =
|
||||
bound(xCache.start, st.inWidth) != xCache.start || bound(xCache.end - 1, st.inWidth) != (xCache.end - 1);
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(cachingProcedure, 0, xCached.size(), 1);
|
||||
|
||||
resizeArea<X>(st, xCached, image, output);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
sd::Status resizeAreaFunctor(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const alignCorners, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(image->dataType(), return resizeAreaFunctor_,
|
||||
(context, image, width, height, alignCorners, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
static sd::Status computeSpans(IKernelFunc<float>* kernel, sd::LongType const outSize, sd::LongType const inSize,
|
||||
float const scale, float const translate, bool const antialias, Spans& spans) {
|
||||
// When sampling, we need the inverse scale and translation, to map from an
|
||||
// output to an input pixel.
|
||||
float const invScale = 1.f / scale;
|
||||
float const invTranslate = -invScale * translate;
|
||||
// When downsampling the kernel should be scaled since we want to low pass
|
||||
// filter and interpolate, but when upsampling it should not be since we only
|
||||
// want to interpolate.
|
||||
float const kernelScale = antialias ? math::sd_max(invScale, 1.f) : 1.f;
|
||||
spans._spanSize =
|
||||
math::sd_min(2 * static_cast<int>(std::ceil(kernel->radius() * kernelScale)) + 1, static_cast<int>(inSize));
|
||||
auto starts = NDArrayFactory::create<int>('c', {outSize});
|
||||
auto weights = NDArrayFactory::create<float>('c', {outSize, spans._spanSize});
|
||||
spans._starts = starts;
|
||||
spans._weights = weights;
|
||||
|
||||
auto startsVec = spans._starts->bufferAsT<int>();
|
||||
auto weightsVector = spans._weights->bufferAsT<float>();
|
||||
spans._weights->nullify();
|
||||
|
||||
const float invKernelScale = 1.f / kernelScale;
|
||||
int maxSpanSize = 0;
|
||||
std::vector<float> tempWeights;
|
||||
|
||||
// return value if within bounds or bounds otherwise
|
||||
auto boundsAmp = [](sd::LongType const low, sd::LongType const high, sd::LongType const value) {
|
||||
if (high < value) return high;
|
||||
if (value < low) return low;
|
||||
return value;
|
||||
};
|
||||
|
||||
for (auto x = 0LL; x < outSize; ++x) {
|
||||
const float columnFloat = x + 0.5f;
|
||||
const float sampleFloat = columnFloat * invScale + invTranslate;
|
||||
|
||||
// Don't sample when the sampling location is outside the source image.
|
||||
if (sampleFloat < 0 || sampleFloat > inSize) {
|
||||
// Add an empty span.
|
||||
startsVec[x] = 0;
|
||||
continue;
|
||||
}
|
||||
sd::LongType spanStart = math::sd_ceil<float, float>(sampleFloat - kernel->radius() * kernelScale - 0.5f);
|
||||
sd::LongType spanEnd = math::sd_floor<float, float>(sampleFloat + kernel->radius() * kernelScale - 0.5f);
|
||||
spanStart = boundsAmp(0LL, inSize - 1, spanStart);
|
||||
spanEnd = boundsAmp(0LL, inSize - 1, spanEnd) + 1;
|
||||
int const spanSize = spanEnd - spanStart;
|
||||
if (spanSize > spans._spanSize) {
|
||||
return Logger::logStatusMsg(
|
||||
Status::BAD_INPUT,
|
||||
"Span is too large: "); // + spanSize + " vs " + spans._spanSize);//, spanSize, spans._spanSize));
|
||||
}
|
||||
float totalWeightSum = 0.f;
|
||||
tempWeights.clear();
|
||||
for (int source = spanStart; source < spanEnd; ++source) {
|
||||
float kernelPos = static_cast<float>(source) + 0.5f - sampleFloat;
|
||||
float weight = (*kernel)(kernelPos * invKernelScale);
|
||||
totalWeightSum += weight;
|
||||
tempWeights.push_back(weight);
|
||||
}
|
||||
maxSpanSize = std::max(maxSpanSize, spanSize);
|
||||
if (math::sd_abs<float,float>(totalWeightSum) >= 1000.f * DataTypeUtils::min_positive<float>()) { //
|
||||
auto totalWeightSumInverted = 1.0f / totalWeightSum;
|
||||
auto outIndex = spans._spanSize * x;
|
||||
for (auto weight : tempWeights) {
|
||||
weightsVector[outIndex] = weight * totalWeightSumInverted;
|
||||
++outIndex;
|
||||
}
|
||||
}
|
||||
startsVec[x] = spanStart;
|
||||
}
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void gatherSpans(int const rowSpanSize, NDArray& rowStarts, NDArray& rowWeights,
|
||||
int const colSpanSize, NDArray& columnStarts, NDArray& columnWeights,
|
||||
NDArray * images, NDArray& intermediate, NDArray* output) {
|
||||
auto batchSize = images->sizeAt(0);
|
||||
auto inputHeight = images->sizeAt(1);
|
||||
auto inputWidth = images->sizeAt(2);
|
||||
auto channels = images->sizeAt(3);
|
||||
|
||||
auto outputHeight = output->sizeAt(1);
|
||||
auto outputWidth = output->sizeAt(2);
|
||||
|
||||
auto inputPixPerBatch = images->strideAt(0);
|
||||
auto intermediatePixPerBatch = inputWidth * outputHeight * channels;
|
||||
auto outputPixPerBatch = outputWidth * outputHeight * channels;
|
||||
Z* intermediatePtr = intermediate.bufferAsT<Z>();
|
||||
bool inputEws1 = images->ews() == 1;
|
||||
auto inRowStride = images->strideAt(1);
|
||||
auto wStride = images->strideAt(2);
|
||||
auto cStride = images->strideAt(3);
|
||||
const X* imagePtr = images->bufferAsT<X>();
|
||||
Z* outPtr = output->bufferAsT<Z>();
|
||||
for (int b = 0; b < batchSize;
|
||||
++b, imagePtr += inputPixPerBatch, intermediatePtr += intermediatePixPerBatch, outPtr += outputPixPerBatch) {
|
||||
gatherRows<X, Z>(rowSpanSize, rowStarts.bufferAsT<int>(), rowWeights.bufferAsT<Z>(), imagePtr, inputHeight,
|
||||
inputWidth, outputHeight, inputWidth, channels, intermediatePtr, inputEws1, inRowStride, wStride,
|
||||
cStride);
|
||||
gatherColumns<Z>(colSpanSize, columnStarts.bufferAsT<int>(), columnWeights.bufferAsT<Z>(), intermediatePtr,
|
||||
outputHeight, inputWidth, outputHeight, outputWidth, channels, outPtr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static sd::Status resizeKernel(IKernelFunc<float>* transformationKernel, NDArray * input, sd::LongType outWidth,
|
||||
sd::LongType outHeight, bool antialias, NDArray* output) {
|
||||
sd::LongType const batchSize = input->sizeAt(0);
|
||||
sd::LongType const inputHeight = input->sizeAt(1);
|
||||
sd::LongType const inputWidth = input->sizeAt(2);
|
||||
sd::LongType const channels = input->sizeAt(3);
|
||||
|
||||
Z rowScale = Z(outHeight) / Z(inputHeight);
|
||||
Z columnScale = Z(outWidth) / Z(inputWidth);
|
||||
|
||||
// Return if the output is empty.
|
||||
if (output->lengthOf() == 0) return sd::Status::OK;
|
||||
|
||||
Spans colSpans;
|
||||
|
||||
auto res = computeSpans(transformationKernel, outWidth, inputWidth, columnScale, 0.f, antialias, colSpans);
|
||||
if (res != sd::Status::OK) return res;
|
||||
Spans rowSpans;
|
||||
res = computeSpans(transformationKernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans);
|
||||
|
||||
NDArray *intermediate = NDArrayFactory::create<Z>('c', {batchSize, outHeight, inputWidth, channels});
|
||||
|
||||
// const functor::Spans& const_row_spans = row_spans;
|
||||
// typename TTypes<int32, 1>::ConstTensor row_starts(
|
||||
// const_row_spans.starts.tensor<int32, 1>());
|
||||
auto rowStarts = *rowSpans._starts; // shape {outWidth}
|
||||
auto rowWeights = *rowSpans._weights; // shape {outWidth, numSpans}
|
||||
auto columnStarts = *colSpans._starts; // shape {outHeights}
|
||||
auto columnWeights = *colSpans._weights; // shape {outHeights, numSpans}
|
||||
|
||||
gatherSpans<X, Z>(rowSpans._spanSize, rowStarts, rowWeights, colSpans._spanSize, columnStarts, columnWeights, input,
|
||||
*intermediate, output);
|
||||
delete intermediate;
|
||||
return res;
|
||||
}
|
||||
#if defined(HAS_FLOAT32)
|
||||
static sd::Status resizeBilinear(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
auto kernel = std::unique_ptr<IKernelFunc<float>>(new TriangleKernelFunc());
|
||||
auto imageDType = image->dataType();
|
||||
auto outputDtype = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(kernel.get(), image, (sd::LongType)width, (sd::LongType)height, antialias, output),
|
||||
SD_NUMERIC_TYPES, SKIP_FIRST_COMMA(TTYPE_FLOAT32));
|
||||
return Logger::logStatusMsg(Status::VALIDATION, "helpers::resizeBilinear: Unknown error occured.");
|
||||
}
|
||||
|
||||
static sd::Status resizeBicubicA(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
CoordinateTransformationMode coorMode, bool exclude_outside, double coefficient,
|
||||
NDArray* output) {
|
||||
constexpr bool alignCorners = false;
|
||||
return resizeBicubicFunctorA(context, image, width, height, alignCorners, coorMode, exclude_outside, coefficient,
|
||||
output);
|
||||
}
|
||||
|
||||
static sd::Status resizeBicubicAntialias(sd::LaunchContext* context, NDArray * image, int const width,
|
||||
int const height, bool const antialias, double coefficient, NDArray* output) {
|
||||
// coorMode is HALF_PIXEL exlude_outside is True
|
||||
auto kernel = std::unique_ptr<IKernelFunc<float>>(new KeysCubicKernelFunc<float>(coefficient));
|
||||
auto imageDType = image->dataType();
|
||||
auto outputDtype = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(kernel.get(), image, (sd::LongType)width, (sd::LongType)height, antialias, output),
|
||||
SD_NUMERIC_TYPES, SKIP_FIRST_COMMA(TTYPE_FLOAT32));
|
||||
return sd::Status::OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static sd::Status resizeArea(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
return resizeAreaFunctor(context, image, width, height, false, output);
|
||||
}
|
||||
#if defined(HAS_FLOAT32)
|
||||
static sd::Status resizeLanczos3(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
auto kernel = std::unique_ptr<IKernelFunc<float>>(new LanczosKernelFunc(3.f));
|
||||
auto imageDType = image->dataType();
|
||||
auto outputDtype = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(kernel.get(), image, (sd::LongType)width, (sd::LongType)height, antialias, output),
|
||||
SD_NUMERIC_TYPES, SKIP_FIRST_COMMA(TTYPE_FLOAT32));
|
||||
return Logger::logStatusMsg(Status::VALIDATION, "helpers::resizeLanczos3: Unknown error occured.");
|
||||
}
|
||||
|
||||
static sd::Status resizeLanczos5(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
auto kernel = std::unique_ptr<IKernelFunc<float>>(new LanczosKernelFunc(5.f));
|
||||
auto imageDType = image->dataType();
|
||||
auto outputDtype = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(kernel.get(), image, (sd::LongType)width, (sd::LongType)height, antialias, output),
|
||||
SD_NUMERIC_TYPES, SKIP_FIRST_COMMA(TTYPE_FLOAT32));
|
||||
return Logger::logStatusMsg(Status::VALIDATION, "helpers::resizeLanczos5: Unknown error occured.");
|
||||
}
|
||||
|
||||
static sd::Status resizeGaussian(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
auto kernel = std::unique_ptr<IKernelFunc<float>>(new GaussianKernelFunc());
|
||||
auto imageDType = image->dataType();
|
||||
auto outputDtype = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(kernel.get(), image, (sd::LongType)width, (sd::LongType)height, antialias, output),
|
||||
SD_NUMERIC_TYPES, SKIP_FIRST_COMMA(TTYPE_FLOAT32));
|
||||
return Logger::logStatusMsg(Status::VALIDATION, "helpers::resizeGaussian: Unknown error occured.");
|
||||
}
|
||||
|
||||
static sd::Status resizeMitchellcubic(sd::LaunchContext* context, NDArray * image, int const width,
|
||||
int const height, bool const antialias, NDArray* output) {
|
||||
auto kernel = std::unique_ptr<IKernelFunc<float>>(new MitchellCubicKernelFunc());
|
||||
auto imageDType = image->dataType();
|
||||
auto outputDtype = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(kernel.get(), image, (sd::LongType)width, (sd::LongType)height, antialias, output),
|
||||
SD_NUMERIC_TYPES, SKIP_FIRST_COMMA(TTYPE_FLOAT32));
|
||||
return Logger::logStatusMsg(Status::VALIDATION, "helpers::ResizeMitchellcubic: Unknown error occured.");
|
||||
}
|
||||
#endif
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
sd::Status resizeImagesFunctor(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
ImageResizeMethods method, bool alignCorners, NDArray* output) {
|
||||
switch (method) {
|
||||
case kResizeBilinear:
|
||||
return resizeBilinearFunctor(context, image, width, height, alignCorners, false, output);
|
||||
case kResizeNearest:
|
||||
return resizeNeighborFunctor(context, image, width, height, CoordinateTransformationMode::ASYMMETRIC,
|
||||
alignCorners ? NearestMode::ROUND_PREFER_CEIL : NearestMode::FLOOR, alignCorners,
|
||||
output);
|
||||
case kResizeBicubic:
|
||||
return resizeBicubicFunctor(context, image, width, height, alignCorners, false, output);
|
||||
case kResizeArea:
|
||||
return resizeAreaFunctor(context, image, width, height, alignCorners, output);
|
||||
case kResizeGaussian:
|
||||
break;
|
||||
case kResizeLanczos3:
|
||||
break;
|
||||
case kResizeLanczos5:
|
||||
break;
|
||||
case kResizeMitchellcubic:
|
||||
break;
|
||||
}
|
||||
sd_printf("helper::resizeImagesFunctor: Wrong resize method %i\n", (int)method);
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT, "helper::resizeImagesFunctor: Wrong resize method");
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------------------------ //
|
||||
sd::Status resizeFunctor(sd::LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
ImageResizeMethods method, CoordinateTransformationMode coorMode, bool exclude_outside,
|
||||
NearestMode nearestMode, double coefficient, bool antialias, NDArray* output) {
|
||||
switch (method) {
|
||||
case kResizeNearest:
|
||||
return resizeNeighborFunctor(context, image, width, height, coorMode, nearestMode, false, output);
|
||||
case kResizeArea:
|
||||
return resizeArea(context, image, width, height, antialias, output);
|
||||
#if defined(HAS_FLOAT32)
|
||||
case kResizeBilinear:
|
||||
return resizeBilinear(context, image, width, height, antialias, output);
|
||||
case kResizeBicubic: {
|
||||
// if antialias then coorMode is HALF_PIXEL and exlude_outside is true
|
||||
if (antialias) {
|
||||
return resizeBicubicAntialias(context, image, width, height, antialias, coefficient, output);
|
||||
} else {
|
||||
// use modified v1
|
||||
return resizeBicubicA(context, image, width, height, coorMode, exclude_outside, coefficient, output);
|
||||
}
|
||||
}
|
||||
case kResizeLanczos3:
|
||||
return resizeLanczos3(context, image, width, height, antialias, output);
|
||||
case kResizeLanczos5:
|
||||
return resizeLanczos5(context, image, width, height, antialias, output);
|
||||
case kResizeGaussian:
|
||||
return resizeGaussian(context, image, width, height, antialias, output);
|
||||
case kResizeMitchellcubic:
|
||||
return resizeMitchellcubic(context, image, width, height, antialias, output);
|
||||
#else
|
||||
case kResizeBilinear:
|
||||
case kResizeBicubic:
|
||||
case kResizeLanczos3:
|
||||
case kResizeLanczos5:
|
||||
case kResizeGaussian:
|
||||
case kResizeMitchellcubic: {
|
||||
sd_printf("helper::resizeFunctor: only float type is supported by this resize method %i\n", (int)method);
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT, "helper::resizeFunctor: only float type supported");
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
sd_printf("helper::resizeFunctor: Wrong resize method %i\n", (int)method);
|
||||
return Logger::logStatusMsg(Status::BAD_INPUT, "helper::resizeFunctor: Wrong resize method");
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,272 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <ops/declarable/helpers/image_suppression.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <queue>
|
||||
#include <system/selective_rendering.h>
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void nonMaxSuppressionV2_(NDArray* boxes, NDArray* scales, int maxSize, double overlapThreshold,
|
||||
double scoreThreshold, NDArray* output) {
|
||||
std::vector<int> indices(scales->lengthOf());
|
||||
std::iota(indices.begin(), indices.end(), 0);
|
||||
auto actualIndicesCount = indices.size();
|
||||
for (auto e = 0; e < scales->lengthOf(); e++) {
|
||||
if (scales->e<float>(e) < (float)scoreThreshold) {
|
||||
indices[e] = -1;
|
||||
actualIndicesCount--;
|
||||
}
|
||||
}
|
||||
std::sort(indices.begin(), indices.end(),
|
||||
[scales](int i, int j) { return i >= 0 && j >= 0 ? scales->e<T>(i) > scales->e<T>(j) : (i > j); });
|
||||
|
||||
std::vector<int> selectedIndices(output->lengthOf(), 0);
|
||||
auto needToSuppressWithThreshold = [](NDArray& boxes, int previousIndex, int nextIndex, T threshold) -> bool {
|
||||
if (previousIndex < 0 || nextIndex < 0) return true;
|
||||
T minYPrev = sd::math::sd_min(boxes.t<T>(previousIndex, 0), boxes.t<T>(previousIndex, 2));
|
||||
T minXPrev = sd::math::sd_min(boxes.t<T>(previousIndex, 1), boxes.t<T>(previousIndex, 3));
|
||||
T maxYPrev = sd::math::sd_max(boxes.t<T>(previousIndex, 0), boxes.t<T>(previousIndex, 2));
|
||||
T maxXPrev = sd::math::sd_max(boxes.t<T>(previousIndex, 1), boxes.t<T>(previousIndex, 3));
|
||||
T minYNext = sd::math::sd_min(boxes.t<T>(nextIndex, 0), boxes.t<T>(nextIndex, 2));
|
||||
T minXNext = sd::math::sd_min(boxes.t<T>(nextIndex, 1), boxes.t<T>(nextIndex, 3));
|
||||
T maxYNext = sd::math::sd_max(boxes.t<T>(nextIndex, 0), boxes.t<T>(nextIndex, 2));
|
||||
T maxXNext = sd::math::sd_max(boxes.t<T>(nextIndex, 1), boxes.t<T>(nextIndex, 3));
|
||||
T areaPrev = (maxYPrev - minYPrev) * (maxXPrev - minXPrev);
|
||||
T areaNext = (maxYNext - minYNext) * (maxXNext - minXNext);
|
||||
|
||||
if (areaNext <= T(0.f) || areaPrev <= T(0.f)) return false;
|
||||
|
||||
T minIntersectionY = sd::math::sd_max(minYPrev, minYNext);
|
||||
T minIntersectionX = sd::math::sd_max(minXPrev, minXNext);
|
||||
T maxIntersectionY = sd::math::sd_min(maxYPrev, maxYNext);
|
||||
T maxIntersectionX = sd::math::sd_min(maxXPrev, maxXNext);
|
||||
T intersectionArea = sd::math::sd_max(T(maxIntersectionY - minIntersectionY), T(0.0f)) *
|
||||
sd::math::sd_max(T(maxIntersectionX - minIntersectionX), T(0.0f));
|
||||
T intersectionValue = intersectionArea / (areaPrev + areaNext - intersectionArea);
|
||||
return intersectionValue > threshold;
|
||||
};
|
||||
// int numSelected = 0;
|
||||
int numBoxes = actualIndicesCount; // boxes->sizeAt(0);
|
||||
int numSelected = 0;
|
||||
|
||||
for (int i = 0; i < numBoxes; ++i) {
|
||||
bool shouldSelect = numSelected < output->lengthOf();
|
||||
|
||||
// FIXME: add parallelism here
|
||||
for (int j = numSelected - 1; j >= 0; --j) {
|
||||
if (shouldSelect)
|
||||
if (needToSuppressWithThreshold(*boxes, indices[i], indices[selectedIndices[j]], T(overlapThreshold))) {
|
||||
shouldSelect = false;
|
||||
}
|
||||
}
|
||||
if (shouldSelect) {
|
||||
output->p(numSelected, indices[i]);
|
||||
selectedIndices[numSelected++] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Return intersection-over-union overlap between boxes i and j
|
||||
template <typename T>
|
||||
static inline T similirityV3_(NDArray& boxes, sd::LongType i, sd::LongType j) {
|
||||
const T zero = static_cast<T>(0.f);
|
||||
const T yminI = math::sd_min(boxes.t<T>(i, 0), boxes.t<T>(i, 2));
|
||||
const T xminI = math::sd_min(boxes.t<T>(i, 1), boxes.t<T>(i, 3));
|
||||
const T ymaxI = math::sd_max(boxes.t<T>(i, 0), boxes.t<T>(i, 2));
|
||||
const T xmaxI = math::sd_max(boxes.t<T>(i, 1), boxes.t<T>(i, 3));
|
||||
const T yminJ = math::sd_min(boxes.t<T>(j, 0), boxes.t<T>(j, 2));
|
||||
const T xminJ = math::sd_min(boxes.t<T>(j, 1), boxes.t<T>(j, 3));
|
||||
const T ymaxJ = math::sd_max(boxes.t<T>(j, 0), boxes.t<T>(j, 2));
|
||||
const T xmaxJ = math::sd_max(boxes.t<T>(j, 1), boxes.t<T>(j, 3));
|
||||
const T areaI = (ymaxI - yminI) * (xmaxI - xminI);
|
||||
const T areaJ = (ymaxJ - yminJ) * (xmaxJ - xminJ);
|
||||
if (areaI <= zero || areaJ <= zero) {
|
||||
return zero;
|
||||
}
|
||||
const T intersectionYmin = math::sd_max(yminI, yminJ);
|
||||
const T intersectionXmin = math::sd_max(xminI, xminJ);
|
||||
const T intersectionYmax = math::sd_min(ymaxI, ymaxJ);
|
||||
const T intersectionXmax = math::sd_min(xmaxI, xmaxJ);
|
||||
const T intersectionY = intersectionYmax - intersectionYmin;
|
||||
const T intersectionX = intersectionXmax - intersectionXmin;
|
||||
const T intersectionArea = math::sd_max(intersectionY, zero) * math::sd_max(intersectionX, zero);
|
||||
return intersectionArea / (areaI + areaJ - intersectionArea);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T similarityOverlaps_(NDArray& boxes, sd::LongType i, sd::LongType j) {
|
||||
return boxes.t<T>(i, j);
|
||||
}
|
||||
|
||||
typedef NDArray (*SimilarityFunc)(NDArray& boxes, sd::LongType i, sd::LongType j);
|
||||
|
||||
static NDArray similiratyOverlaps(NDArray& boxes, sd::LongType i, sd::LongType j) {
|
||||
NDArray res(boxes.dataType(), boxes.getContext());
|
||||
BUILD_SINGLE_SELECTOR(boxes.dataType(), res = similarityOverlaps_, (boxes, i, j), SD_FLOAT_TYPES);
|
||||
return res;
|
||||
}
|
||||
|
||||
static NDArray similarityV3(NDArray& boxes, sd::LongType i, sd::LongType j) {
|
||||
NDArray res(boxes.dataType(), boxes.getContext());
|
||||
BUILD_SINGLE_SELECTOR(boxes.dataType(), res = similirityV3_, (boxes, i, j), SD_FLOAT_TYPES);
|
||||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, typename I>
|
||||
static sd::LongType nonMaxSuppressionGeneric_(sd::LaunchContext* context, NDArray* boxes, NDArray* scores,
|
||||
int outputSize, float overlapThreshold, float scoreThreshold,
|
||||
NDArray* output, SimilarityFunc f) {
|
||||
auto numBoxes = boxes->sizeAt(0);
|
||||
T* scoresData = scores->dataBuffer()->primaryAsT<T>();
|
||||
|
||||
// Data structure for a selection candidate in NMS.
|
||||
struct Candidate {
|
||||
int _boxIndex;
|
||||
T _score;
|
||||
int _suppressBeginIndex;
|
||||
};
|
||||
|
||||
auto cmp = [](const Candidate& bsI, const Candidate& bsJ) -> bool {
|
||||
return ((bsI._score == bsJ._score) && (bsI._boxIndex > bsJ._boxIndex)) || (bsI._score < bsJ._score);
|
||||
};
|
||||
|
||||
std::priority_queue<Candidate, std::deque<Candidate>, decltype(cmp)> candidatePriorityQueue(cmp);
|
||||
for (auto i = 0; i < scores->lengthOf(); ++i) {
|
||||
if ((float)scoresData[i] > (float)scoreThreshold) {
|
||||
candidatePriorityQueue.emplace(Candidate({i, scoresData[i], 0}));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<I> selected;
|
||||
T similarity, originalScore;
|
||||
Candidate nextCandidate;
|
||||
|
||||
while (selected.size() < static_cast<size_t>(outputSize) && !candidatePriorityQueue.empty()) {
|
||||
nextCandidate = candidatePriorityQueue.top();
|
||||
originalScore = nextCandidate._score;
|
||||
candidatePriorityQueue.pop();
|
||||
|
||||
// Overlapping boxes are likely to have similar scores, therefore we
|
||||
// iterate through the previously selected boxes backwards in order to
|
||||
// see if `nextCandidate` should be suppressed. We also enforce a property
|
||||
// that a candidate can be suppressed by another candidate no more than
|
||||
// once via `suppress_begin_index` which tracks which previously selected
|
||||
// boxes have already been compared against next_candidate prior to a given
|
||||
// iteration. These previous selected boxes are then skipped over in the
|
||||
// following loop.
|
||||
bool shouldHardSuppress = false;
|
||||
for (int j = static_cast<int>(selected.size()) - 1; j >= nextCandidate._suppressBeginIndex; --j) {
|
||||
auto similarityA =
|
||||
f(*boxes, nextCandidate._boxIndex, selected[j]); // boxes->t<T>(nextCandidate._boxIndex, selected[j]);
|
||||
similarity = similarityA.template t<T>(0);
|
||||
nextCandidate._score *= T(similarity <= overlapThreshold ? 1.0 : 0.); // suppressWeightFunc(similarity);
|
||||
|
||||
// First decide whether to perform hard suppression
|
||||
if ((float)similarity >= static_cast<float>(overlapThreshold)) {
|
||||
shouldHardSuppress = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If next_candidate survives hard suppression, apply soft suppression
|
||||
if ((float)nextCandidate._score <= (float)scoreThreshold) break;
|
||||
}
|
||||
// If `nextCandidate._score` has not dropped below `scoreThreshold`
|
||||
// by this point, then we know that we went through all of the previous
|
||||
// selections and can safely update `suppress_begin_index` to
|
||||
// `selected.size()`. If on the other hand `next_candidate.score`
|
||||
// *has* dropped below the score threshold, then since `suppressWeight`
|
||||
// always returns values in [0, 1], further suppression by items that were
|
||||
// not covered in the above for loop would not have caused the algorithm
|
||||
// to select this item. We thus do the same update to
|
||||
// `suppressBeginIndex`, but really, this element will not be added back
|
||||
// into the priority queue in the following.
|
||||
nextCandidate._suppressBeginIndex = selected.size();
|
||||
|
||||
if (!shouldHardSuppress) {
|
||||
if (nextCandidate._score == originalScore) {
|
||||
// Suppression has not occurred, so select next_candidate
|
||||
selected.push_back(nextCandidate._boxIndex);
|
||||
}
|
||||
if ((float)nextCandidate._score > (float)scoreThreshold) {
|
||||
// Soft suppression has occurred and current score is still greater than
|
||||
// score_threshold; add next_candidate back onto priority queue.
|
||||
candidatePriorityQueue.push(nextCandidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (output) {
|
||||
DataBuffer buf(selected.data(), selected.size() * sizeof(I), DataTypeUtils::fromT<I>());
|
||||
output->dataBuffer()->copyBufferFrom(buf, buf.getLenInBytes());
|
||||
}
|
||||
|
||||
return (sd::LongType)selected.size();
|
||||
}
|
||||
|
||||
sd::LongType nonMaxSuppressionGeneric(sd::LaunchContext* context, NDArray* boxes, NDArray* scores, int maxSize,
|
||||
double overlapThreshold, double scoreThreshold, NDArray* output) {
|
||||
auto boxesDType = boxes->dataType();
|
||||
auto outputDType = output == nullptr ? DataType::INT32 : output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(boxes->dataType(), output == nullptr ? DataType::INT32 : output->dataType(),
|
||||
return nonMaxSuppressionGeneric_,
|
||||
(context, boxes, scores, maxSize, overlapThreshold, scoreThreshold, output, similiratyOverlaps),
|
||||
SD_FLOAT_TYPES, SD_INTEGER_TYPES);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sd::LongType nonMaxSuppressionV3(sd::LaunchContext* context, NDArray* boxes, NDArray* scores, int maxSize,
|
||||
double overlapThreshold, double scoreThreshold, NDArray* output) {
|
||||
auto boxesDType = boxes->dataType();
|
||||
auto outputDType = output == nullptr ? DataType::INT32 : output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(boxes->dataType(), output == nullptr ? DataType::INT32 : output->dataType(),
|
||||
return nonMaxSuppressionGeneric_,
|
||||
(context, boxes, scores, maxSize, overlapThreshold, scoreThreshold, output, similarityV3),
|
||||
SD_FLOAT_TYPES, SD_INTEGER_TYPES);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BUILD_DOUBLE_TEMPLATE( sd::LongType nonMaxSuppressionGeneric_,
|
||||
(sd::LaunchContext * context, NDArray* boxes, NDArray* scores, int maxSize,
|
||||
float overlapThreshold, float scoreThreshold, NDArray* output, SimilarityFunc SimilarityFunc),
|
||||
SD_FLOAT_TYPES, SD_INTEGER_TYPES);
|
||||
|
||||
void nonMaxSuppression(sd::LaunchContext* context, NDArray* boxes, NDArray* scales, int maxSize,
|
||||
double overlapThreshold, double scoreThreshold, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(boxes->dataType(), nonMaxSuppressionV2_,
|
||||
(boxes, scales, maxSize, overlapThreshold, scoreThreshold, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void nonMaxSuppressionV2_,
|
||||
(NDArray * boxes, NDArray* scales, int maxSize, double overlapThreshold, double scoreThreshold,
|
||||
NDArray* output),
|
||||
SD_NUMERIC_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,326 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Oleh Semeniv (oleg.semeniv@gmail.com)
|
||||
// @author AbdelRauf (rauf@konduit.ai)
|
||||
//
|
||||
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <ops/declarable/helpers/adjust_hue.h>
|
||||
#include <ops/declarable/helpers/imagesHelpers.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void rgbToGrs_(NDArray& input, NDArray& output, const int dimC) {
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
const int rank = input.rankOf();
|
||||
|
||||
if (dimC == rank - 1 && 'c' == input.ordering() && 1 == input.ews() && 'c' == output.ordering() &&
|
||||
1 == output.ews()) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
const auto xStep = i * 3;
|
||||
z[i] = 0.2989f * x[xStep] + 0.5870f * x[xStep + 1] + 0.1140f * x[xStep + 2];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, output.lengthOf(), 1);
|
||||
return;
|
||||
}
|
||||
|
||||
sd::LongType *outputShape = shape::shapeOf(output.shapeInfo());
|
||||
sd::LongType *outputStride = shape::stride(output.shapeOf());
|
||||
sd::LongType *inputStride = shape::stride(input.shapeInfo());
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
for (auto i = start; i < stop; i++) {
|
||||
INDEX2COORDS(i, rank,outputShape, coords);
|
||||
sd::LongType zOffset, xOffset0, xOffset1, xOffset2;
|
||||
COORDS2INDEX(rank, outputStride, coords, zOffset);
|
||||
COORDS2INDEX(rank, inputStride, coords, xOffset0);
|
||||
coords[dimC]++;
|
||||
COORDS2INDEX(rank,inputStride, coords, xOffset1);
|
||||
coords[dimC]++;
|
||||
COORDS2INDEX(rank, inputStride, coords, xOffset2);
|
||||
z[zOffset] = 0.2989f * x[xOffset0] + 0.5870f * x[xOffset1] + 0.1140f * x[xOffset2];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, output.lengthOf(), 1);
|
||||
return;
|
||||
}
|
||||
|
||||
void transformRgbGrs(sd::LaunchContext* context, NDArray& input, NDArray& output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), rgbToGrs_, (input, output, dimC), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SD_INLINE static void tripleTransformer(NDArray* input, NDArray* output, const int dimC, T (&tr)[3][3]) {
|
||||
const int rank = input->rankOf();
|
||||
|
||||
const T* x = input->bufferAsT<T>();
|
||||
T* z = output->bufferAsT<T>();
|
||||
// TODO: Use tensordot or other optimizied helpers to see if we can get better performance.
|
||||
|
||||
if (dimC == rank - 1 && input->ews() == 1 && output->ews() == 1 && input->ordering() == 'c' &&
|
||||
output->ordering() == 'c') {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
// simple M*v //tr.T*v.T // v * tr //rule: (AB)' =B'A'
|
||||
// v.shape (1,3) row vector
|
||||
T x0, x1, x2;
|
||||
x0 = x[i]; // just additional hint
|
||||
x1 = x[i + 1];
|
||||
x2 = x[i + 2];
|
||||
z[i] = x0 * tr[0][0] + x1 * tr[1][0] + x2 * tr[2][0];
|
||||
z[i + 1] = x0 * tr[0][1] + x1 * tr[1][1] + x2 * tr[2][1];
|
||||
z[i + 2] = x0 * tr[0][2] + x1 * tr[1][2] + x2 * tr[2][2];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input->lengthOf(), 3);
|
||||
} else {
|
||||
auto packX = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimC);
|
||||
auto packZ = sd::ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimC);
|
||||
|
||||
const sd::LongType numOfTads = packX->numberOfTads();
|
||||
const sd::LongType xDimCstride = input->stridesOf()[dimC];
|
||||
const sd::LongType zDimCstride = output->stridesOf()[dimC];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
const T* xTad = x + packX->platformOffsets()[i];
|
||||
T* zTad = z + packZ->platformOffsets()[i];
|
||||
// simple M*v //tr.T*v
|
||||
T x0, x1, x2;
|
||||
x0 = xTad[0];
|
||||
x1 = xTad[xDimCstride];
|
||||
x2 = xTad[2 * xDimCstride];
|
||||
zTad[0] = x0 * tr[0][0] + x1 * tr[1][0] + x2 * tr[2][0];
|
||||
zTad[zDimCstride] = x0 * tr[0][1] + x1 * tr[1][1] + x2 * tr[2][1];
|
||||
zTad[2 * zDimCstride] = x0 * tr[0][2] + x1 * tr[1][2] + x2 * tr[2][2];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SD_INLINE static void rgbYiq(NDArray* input, NDArray* output, const int dimC) {
|
||||
T arr[3][3] = {{(T)0.299, (T)0.59590059, (T)0.2115},
|
||||
{(T)0.587, (T)-0.27455667, (T)-0.52273617},
|
||||
{(T)0.114, (T)-0.32134392, (T)0.31119955}};
|
||||
return tripleTransformer<T>(input, output, dimC, arr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SD_INLINE static void yiqRgb(NDArray* input, NDArray* output, const int dimC) {
|
||||
// TODO: this operation does not use the clamp operation, so there is a possibility being out of range.
|
||||
// Justify that it will not be out of range for images data
|
||||
T arr[3][3] = {{(T)1, (T)1, (T)1},
|
||||
{(T)0.95598634, (T)-0.27201283, (T)-1.10674021},
|
||||
{(T)0.6208248, (T)-0.64720424, (T)1.70423049}};
|
||||
return tripleTransformer<T>(input, output, dimC, arr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SD_INLINE static void hsvRgb(NDArray* input, NDArray* output, const int dimC) {
|
||||
const int rank = input->rankOf();
|
||||
|
||||
const T* x = input->bufferAsT<T>();
|
||||
T* z = output->bufferAsT<T>();
|
||||
|
||||
if (dimC == rank - 1 && input->ews() == 1 && output->ews() == 1 && input->ordering() == 'c' &&
|
||||
output->ordering() == 'c') {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
sd::ops::helpers::hsvToRgb<T>(x[i], x[i + 1], x[i + 2], z[i], z[i + 1], z[i + 2]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input->lengthOf(), 3);
|
||||
} else {
|
||||
auto packX = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimC);
|
||||
auto packZ = sd::ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimC);
|
||||
|
||||
const sd::LongType numOfTads = packX->numberOfTads();
|
||||
const sd::LongType xDimCstride = input->stridesOf()[dimC];
|
||||
const sd::LongType zDimCstride = output->stridesOf()[dimC];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
const T* xTad = x + packX->platformOffsets()[i];
|
||||
T* zTad = z + packZ->platformOffsets()[i];
|
||||
sd::ops::helpers::hsvToRgb<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], zTad[0], zTad[zDimCstride],
|
||||
zTad[2 * zDimCstride]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SD_INLINE static void rgbHsv(NDArray* input, NDArray* output, const int dimC) {
|
||||
const int rank = input->rankOf();
|
||||
|
||||
const T* x = input->bufferAsT<T>();
|
||||
T* z = output->bufferAsT<T>();
|
||||
|
||||
if (dimC == rank - 1 && input->ews() == 1 && output->ews() == 1 && input->ordering() == 'c' &&
|
||||
output->ordering() == 'c') {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
sd::ops::helpers::rgbToHsv<T>(x[i], x[i + 1], x[i + 2], z[i], z[i + 1], z[i + 2]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input->lengthOf(), 3);
|
||||
} else {
|
||||
auto packX = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimC);
|
||||
auto packZ = sd::ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimC);
|
||||
|
||||
const sd::LongType numOfTads = packX->numberOfTads();
|
||||
const sd::LongType xDimCstride = input->stridesOf()[dimC];
|
||||
const sd::LongType zDimCstride = output->stridesOf()[dimC];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
const T* xTad = x + packX->platformOffsets()[i];
|
||||
T* zTad = z + packZ->platformOffsets()[i];
|
||||
sd::ops::helpers::rgbToHsv<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], zTad[0], zTad[zDimCstride],
|
||||
zTad[2 * zDimCstride]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SD_INLINE static void rgbYuv_(NDArray& input, NDArray& output, const int dimC) {
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
const int rank = input.rankOf();
|
||||
bool bSimple = (dimC == rank - 1 && 'c' == input.ordering() && 1 == input.ews() && 'c' == output.ordering() &&
|
||||
1 == output.ews());
|
||||
|
||||
if (bSimple) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
sd::ops::helpers::rgbYuv<T>(x[i], x[i + 1], x[i + 2], z[i], z[i + 1], z[i + 2]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input.lengthOf(), 3);
|
||||
return;
|
||||
}
|
||||
|
||||
auto packX = sd::ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), dimC);
|
||||
auto packZ = sd::ConstantTadHelper::getInstance().tadForDimensions(output.shapeInfo(), dimC);
|
||||
|
||||
const sd::LongType numOfTads = packX->numberOfTads();
|
||||
const sd::LongType xDimCstride = input.stridesOf()[dimC];
|
||||
const sd::LongType zDimCstride = output.stridesOf()[dimC];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
const T* xTad = x + packX->platformOffsets()[i];
|
||||
T* zTad = z + packZ->platformOffsets()[i];
|
||||
sd::ops::helpers::rgbYuv<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], zTad[0], zTad[zDimCstride],
|
||||
zTad[2 * zDimCstride]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SD_INLINE static void yuvRgb_(NDArray& input, NDArray& output, const int dimC) {
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
const int rank = input.rankOf();
|
||||
bool bSimple = (dimC == rank - 1 && 'c' == input.ordering() && 1 == input.ews() && 'c' == output.ordering() &&
|
||||
1 == output.ews());
|
||||
|
||||
if (bSimple) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
sd::ops::helpers::yuvRgb<T>(x[i], x[i + 1], x[i + 2], z[i], z[i + 1], z[i + 2]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, input.lengthOf(), 3);
|
||||
return;
|
||||
}
|
||||
|
||||
auto packX = sd::ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), dimC);
|
||||
auto packZ = sd::ConstantTadHelper::getInstance().tadForDimensions(output.shapeInfo(), dimC);
|
||||
|
||||
const sd::LongType numOfTads = packX->numberOfTads();
|
||||
const sd::LongType xDimCstride = input.stridesOf()[dimC];
|
||||
const sd::LongType zDimCstride = output.stridesOf()[dimC];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) {
|
||||
const T* xTad = x + packX->platformOffsets()[i];
|
||||
T* zTad = z + packZ->platformOffsets()[i];
|
||||
sd::ops::helpers::yuvRgb<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], zTad[0], zTad[zDimCstride],
|
||||
zTad[2 * zDimCstride]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
return;
|
||||
}
|
||||
|
||||
void transformRgbYuv(sd::LaunchContext* context, NDArray& input, NDArray& output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), rgbYuv_, (input, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
void transformYuvRgb(sd::LaunchContext* context, NDArray& input, NDArray& output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), yuvRgb_, (input, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
void transformHsvRgb(sd::LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), hsvRgb, (input, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
void transformRgbHsv(sd::LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), rgbHsv, (input, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
void transformYiqRgb(sd::LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), yiqRgb, (input, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
void transformRgbYiq(sd::LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), rgbYiq, (input, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,70 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <ops/declarable/helpers/reductions.h>
|
||||
#include <system/selective_rendering.h>
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
void argMax_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions);
|
||||
|
||||
template <typename X, typename Z>
|
||||
void argMin_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions);
|
||||
|
||||
template <typename X, typename Z>
|
||||
void argAbsMax_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions);
|
||||
|
||||
template <typename X, typename Z>
|
||||
void argAbsMin_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void argMax(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
auto inputDType = input.dataType();
|
||||
auto outputDType = output.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), output.dataType(), argMax_, (input, output, dimensions), SD_COMMON_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
void argMin(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
auto inputDType = input.dataType();
|
||||
auto outputDType = output.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), output.dataType(), argMin_, (input, output, dimensions), SD_COMMON_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
void argAbsMax(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
auto inputDType = input.dataType();
|
||||
auto outputDType = output.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), output.dataType(), argAbsMax_, (input, output, dimensions), SD_COMMON_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
void argAbsMin(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
auto inputDType = input.dataType();
|
||||
auto outputDType = output.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), output.dataType(), argAbsMin_, (input, output, dimensions), SD_COMMON_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,894 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <ops/declarable/helpers/reductions.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#if 1
|
||||
#define LOG_CALLS(X)
|
||||
#else
|
||||
|
||||
#define LOG_CALLS(X) sd_printf("___%s_________%d+\n", __PRETTY_FUNCTION__, X);
|
||||
#endif
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
constexpr int threadingThreshold = 4096;
|
||||
template <typename X, typename Z, typename ReductionOp>
|
||||
static SD_INLINE void indexInnerReductionRank1(const X* buffer, X& current, Z& argCurrent,
|
||||
const sd::LongType& loopCount) {
|
||||
argCurrent = 0;
|
||||
current = buffer[0];
|
||||
LOG_CALLS(0)
|
||||
sd::LongType j_offset = 0;
|
||||
for (Z j = 0; j < loopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, buffer[j], j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp>
|
||||
static SD_INLINE void indexInnerReductionRank1(const X* buffer, X& current, Z& argCurrent,
|
||||
const sd::LongType& loopCount, const sd::LongType& inner_stride) {
|
||||
argCurrent = 0;
|
||||
current = buffer[0];
|
||||
LOG_CALLS(0)
|
||||
sd::LongType j_offset = 0;
|
||||
for (Z j = 0; j < loopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, buffer[j_offset], j);
|
||||
j_offset += inner_stride;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, size_t constRank, bool LastIndexFaster = true>
|
||||
static SD_INLINE void indexInnerReductionConstRank(const X* buffer, X& current, Z& argCurrent,
|
||||
const sd::LongType* bases, const sd::LongType* strides,
|
||||
const sd::LongType outerLoopCount,
|
||||
const sd::LongType& innerLoopCount) {
|
||||
// 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);
|
||||
Z startIndex = 0;
|
||||
argCurrent = 0;
|
||||
current = buffer[offset];
|
||||
LOG_CALLS(0)
|
||||
for (Z i = 0; i < outerLoopCount; i++) {
|
||||
const X* inner_buffer = &(buffer[offset]);
|
||||
// typename std::make_signed<Z>::type iArgMax = -1;
|
||||
for (Z j = 0; j < innerLoopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, inner_buffer[j], j + startIndex);
|
||||
}
|
||||
// we skip 1
|
||||
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
|
||||
startIndex += innerLoopCount;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, size_t constRank, bool LastIndexFaster = true>
|
||||
static SD_INLINE void indexInnerReductionConstRank(const X* buffer, X& current, Z& argCurrent,
|
||||
const sd::LongType* bases, const sd::LongType* strides,
|
||||
const sd::LongType outerLoopCount,
|
||||
const sd::LongType& innerLoopCount,
|
||||
const sd::LongType& inner_stride) {
|
||||
// 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);
|
||||
Z startIndex = 0;
|
||||
argCurrent = 0;
|
||||
current = buffer[offset];
|
||||
LOG_CALLS(0)
|
||||
for (Z i = 0; i < outerLoopCount; i++) {
|
||||
const X* inner_buffer = &(buffer[offset]);
|
||||
for (Z j = 0; j < innerLoopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, *inner_buffer, j + startIndex);
|
||||
inner_buffer += inner_stride;
|
||||
}
|
||||
// we alreaddy skiped
|
||||
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
|
||||
startIndex += innerLoopCount;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, bool LastIndexFaster = true>
|
||||
static SD_INLINE void indexInnerReduction(const LongType& rank, const X* buffer, X& current, Z& argCurrent,
|
||||
const sd::LongType* bases, const sd::LongType* strides,
|
||||
const sd::LongType& outerLoopStart, const sd::LongType& outerLoopStop,
|
||||
const sd::LongType& innerLoopCount) {
|
||||
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);
|
||||
}
|
||||
Z startIndex = outerLoopStart * innerLoopCount;
|
||||
argCurrent = startIndex;
|
||||
current = buffer[offset];
|
||||
LOG_CALLS(0)
|
||||
for (Z i = 0; i < outerLoopCount; i++) {
|
||||
const X* inner_buffer = &(buffer[offset]);
|
||||
for (Z j = 0; j < innerLoopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, inner_buffer[j], j + startIndex);
|
||||
}
|
||||
offset = inc_coords<true>(bases, strides, ptr_coords, offset, rank, 1);
|
||||
startIndex += innerLoopCount;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, bool LastIndexFaster = true>
|
||||
static SD_INLINE void indexInnerReduction(const int& rank, const X* buffer, X& current, Z& argCurrent,
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
Z startIndex = outerLoopStart * innerLoopCount;
|
||||
argCurrent = startIndex;
|
||||
current = buffer[offset];
|
||||
LOG_CALLS(0)
|
||||
for (Z i = 0; i < outerLoopCount; i++) {
|
||||
const X* inner_buffer = &(buffer[offset]);
|
||||
for (Z j = 0; j < innerLoopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, inner_buffer[j * inner_stride], startIndex + j);
|
||||
}
|
||||
offset = inc_coords<true>(bases, strides, ptr_coords, offset, rank, 1);
|
||||
startIndex += innerLoopCount;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp>
|
||||
static SD_INLINE void indexInnerReductionRank1Block4WithMerge(const X* buffer, X& current, Z& argCurrent,
|
||||
const sd::LongType& loopCount) {
|
||||
argCurrent = 0;
|
||||
current = buffer[0];
|
||||
LOG_CALLS(0)
|
||||
sd::LongType loopCount4 = loopCount / 4;
|
||||
sd::LongType loopCountEnd = loopCount4 + (loopCount & 3);
|
||||
const X* buffer1 = buffer + 1 * loopCount4;
|
||||
const X* buffer2 = buffer1 + 1 * loopCount4;
|
||||
const X* buffer3 = buffer2 + 1 * loopCount4;
|
||||
X current1 = *buffer1;
|
||||
X current2 = *buffer2;
|
||||
X current3 = *buffer3;
|
||||
Z argCurrent1 = 0;
|
||||
Z argCurrent2 = 0;
|
||||
Z argCurrent3 = 0;
|
||||
for (Z j = 0; j < loopCount4; j++) {
|
||||
ReductionOp::update(current, argCurrent, buffer[j], j);
|
||||
ReductionOp::update(current1, argCurrent1, buffer1[j], j);
|
||||
ReductionOp::update(current2, argCurrent2, buffer2[j], j);
|
||||
ReductionOp::update(current3, argCurrent3, buffer3[j], j);
|
||||
}
|
||||
// tail
|
||||
for (Z j = loopCount4; j < loopCountEnd; j++) {
|
||||
ReductionOp::update(current3, argCurrent3, buffer3[j], j);
|
||||
}
|
||||
// merge
|
||||
argCurrent1 += loopCount4;
|
||||
argCurrent2 += 2 * loopCount4;
|
||||
argCurrent3 += 3 * loopCount4;
|
||||
ReductionOp::update(current, argCurrent, current1, argCurrent1);
|
||||
ReductionOp::update(current, argCurrent, current2, argCurrent2);
|
||||
ReductionOp::update(current, argCurrent, current3, argCurrent3);
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp>
|
||||
static SD_INLINE void indexInnerReductionRank1Block4WithMerge(const X* buffer, X& current, Z& argCurrent,
|
||||
const sd::LongType& loopCount,
|
||||
const sd::LongType& inner_stride) {
|
||||
argCurrent = 0;
|
||||
current = buffer[0];
|
||||
LOG_CALLS(0)
|
||||
sd::LongType loopCount4 = loopCount / 4;
|
||||
sd::LongType loopCountEnd = loopCount4 + (loopCount & 3);
|
||||
const X* buffer1 = buffer + inner_stride * loopCount4;
|
||||
const X* buffer2 = buffer1 + inner_stride * loopCount4;
|
||||
const X* buffer3 = buffer2 + inner_stride * loopCount4;
|
||||
X current1 = *buffer1;
|
||||
X current2 = *buffer2;
|
||||
X current3 = *buffer3;
|
||||
Z argCurrent1 = 0;
|
||||
Z argCurrent2 = 0;
|
||||
Z argCurrent3 = 0;
|
||||
sd::LongType j_offset = 0;
|
||||
for (Z j = 0; j < loopCount4; j++) {
|
||||
ReductionOp::update(current, argCurrent, buffer[j_offset], j);
|
||||
ReductionOp::update(current1, argCurrent1, buffer1[j_offset], j);
|
||||
ReductionOp::update(current2, argCurrent2, buffer2[j_offset], j);
|
||||
ReductionOp::update(current3, argCurrent3, buffer3[j_offset], j);
|
||||
j_offset += inner_stride;
|
||||
}
|
||||
// tail
|
||||
for (Z j = loopCount4; j < loopCountEnd; j++) {
|
||||
ReductionOp::update(current3, argCurrent3, buffer3[j_offset], j);
|
||||
j_offset += inner_stride;
|
||||
}
|
||||
// merge
|
||||
argCurrent1 += loopCount4;
|
||||
argCurrent2 += 2 * loopCount4;
|
||||
argCurrent3 += 3 * loopCount4;
|
||||
ReductionOp::update(current, argCurrent, current1, argCurrent1);
|
||||
ReductionOp::update(current, argCurrent, current2, argCurrent2);
|
||||
ReductionOp::update(current, argCurrent, current3, argCurrent3);
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp>
|
||||
static SD_INLINE void indexInnerReductionRank1Block4(const X* buffer, const X* buffer1, const X* buffer2,
|
||||
const X* buffer3, Z* output, Z* output1, Z* output2, Z* output3,
|
||||
const sd::LongType& loopCount) {
|
||||
LOG_CALLS(0)
|
||||
Z argCurrent = 0;
|
||||
Z argCurrent1 = 0;
|
||||
Z argCurrent2 = 0;
|
||||
Z argCurrent3 = 0;
|
||||
X current = buffer[0];
|
||||
X current1 = buffer1[0];
|
||||
X current2 = buffer2[0];
|
||||
X current3 = buffer3[0];
|
||||
for (Z j = 0; j < loopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, buffer[j], j);
|
||||
ReductionOp::update(current1, argCurrent1, buffer1[j], j);
|
||||
ReductionOp::update(current2, argCurrent2, buffer2[j], j);
|
||||
ReductionOp::update(current3, argCurrent3, buffer3[j], j);
|
||||
}
|
||||
*output = argCurrent;
|
||||
*output1 = argCurrent1;
|
||||
*output2 = argCurrent2;
|
||||
*output3 = argCurrent3;
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp>
|
||||
static SD_INLINE void indexInnerReductionRank1Block4(const X* buffer, const X* buffer1, const X* buffer2,
|
||||
const X* buffer3, Z* output, Z* output1, Z* output2, Z* output3,
|
||||
const sd::LongType& loopCount, const sd::LongType& inner_stride) {
|
||||
LOG_CALLS(0)
|
||||
Z argCurrent = 0;
|
||||
Z argCurrent1 = 0;
|
||||
Z argCurrent2 = 0;
|
||||
Z argCurrent3 = 0;
|
||||
X current = buffer[0];
|
||||
X current1 = buffer1[0];
|
||||
X current2 = buffer2[0];
|
||||
X current3 = buffer3[0];
|
||||
sd::LongType j_offset = 0;
|
||||
for (Z j = 0; j < loopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, buffer[j_offset], j);
|
||||
ReductionOp::update(current1, argCurrent1, buffer1[j_offset], j);
|
||||
ReductionOp::update(current2, argCurrent2, buffer2[j_offset], j);
|
||||
ReductionOp::update(current3, argCurrent3, buffer3[j_offset], j);
|
||||
j_offset += inner_stride;
|
||||
}
|
||||
*output = argCurrent;
|
||||
*output1 = argCurrent1;
|
||||
*output2 = argCurrent2;
|
||||
*output3 = argCurrent3;
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, size_t constRank, bool LastIndexFaster = true>
|
||||
static SD_INLINE void indexInnerReductionConstRankBlock4(const X* buffer, const X* buffer1, const X* buffer2,
|
||||
const X* buffer3, Z* output, Z* output1, Z* output2,
|
||||
Z* output3, const sd::LongType* bases,
|
||||
const sd::LongType* strides,
|
||||
const sd::LongType& outerLoopCount,
|
||||
const sd::LongType& innerLoopCount) {
|
||||
LOG_CALLS(0)
|
||||
// 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);
|
||||
Z startIndex = 0;
|
||||
Z argCurrent = 0;
|
||||
Z argCurrent1 = 0;
|
||||
Z argCurrent2 = 0;
|
||||
Z argCurrent3 = 0;
|
||||
X current = buffer[0];
|
||||
X current1 = buffer1[0];
|
||||
X current2 = buffer2[0];
|
||||
X current3 = buffer3[0];
|
||||
// LOG_CALLS(0)
|
||||
for (Z i = 0; i < outerLoopCount; i++) {
|
||||
const X* inner_buffer = &(buffer[offset]);
|
||||
const X* inner_buffer1 = &(buffer1[offset]);
|
||||
const X* inner_buffer2 = &(buffer2[offset]);
|
||||
const X* inner_buffer3 = &(buffer3[offset]);
|
||||
// typename std::make_signed<Z>::type iArgMax = -1;
|
||||
for (Z j = 0; j < innerLoopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, inner_buffer[j], j + startIndex);
|
||||
ReductionOp::update(current1, argCurrent1, inner_buffer1[j], j + startIndex);
|
||||
ReductionOp::update(current2, argCurrent2, inner_buffer2[j], j + startIndex);
|
||||
ReductionOp::update(current3, argCurrent3, inner_buffer3[j], j + startIndex);
|
||||
}
|
||||
// we skip 1
|
||||
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
|
||||
startIndex += innerLoopCount;
|
||||
}
|
||||
*output = argCurrent;
|
||||
*output1 = argCurrent1;
|
||||
*output2 = argCurrent2;
|
||||
*output3 = argCurrent3;
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, size_t constRank, bool LastIndexFaster = true>
|
||||
static SD_INLINE void indexInnerReductionConstRankBlock4(
|
||||
const X* buffer, const X* buffer1, const X* buffer2, const X* buffer3, Z* output, 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) {
|
||||
LOG_CALLS(0)
|
||||
// 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);
|
||||
Z startIndex = 0;
|
||||
Z argCurrent = 0;
|
||||
Z argCurrent1 = 0;
|
||||
Z argCurrent2 = 0;
|
||||
Z argCurrent3 = 0;
|
||||
X current = buffer[0];
|
||||
X current1 = buffer1[0];
|
||||
X current2 = buffer2[0];
|
||||
X current3 = buffer3[0];
|
||||
// LOG_CALLS(0)
|
||||
for (Z i = 0; i < outerLoopCount; i++) {
|
||||
const X* inner_buffer = &(buffer[offset]);
|
||||
const X* inner_buffer1 = &(buffer1[offset]);
|
||||
const X* inner_buffer2 = &(buffer2[offset]);
|
||||
const X* inner_buffer3 = &(buffer3[offset]);
|
||||
// typename std::make_signed<Z>::type iArgMax = -1;
|
||||
sd::LongType inner_offset = 0;
|
||||
for (Z j = 0; j < innerLoopCount; j++) {
|
||||
ReductionOp::update(current, argCurrent, inner_buffer[inner_offset], j + startIndex);
|
||||
ReductionOp::update(current1, argCurrent1, inner_buffer1[inner_offset], j + startIndex);
|
||||
ReductionOp::update(current2, argCurrent2, inner_buffer2[inner_offset], j + startIndex);
|
||||
ReductionOp::update(current3, argCurrent3, inner_buffer3[inner_offset], j + startIndex);
|
||||
inner_offset += inner_stride;
|
||||
}
|
||||
// we skip 1
|
||||
offset = sd::inc_coords<updated_rank, 0, LastIndexFaster>(cst, offset);
|
||||
startIndex += innerLoopCount;
|
||||
}
|
||||
*output = argCurrent;
|
||||
*output1 = argCurrent1;
|
||||
*output2 = argCurrent2;
|
||||
*output3 = argCurrent3;
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, bool LastIndexFaster = true>
|
||||
static void argIndexCase1Scalar(const int& second_rank, const sd::LongType* inner_bases,
|
||||
const sd::LongType* inner_strides, const X* bufferX, Z* outputZ) {
|
||||
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 {
|
||||
inner_total = getLength<LastIndexFaster>(inner_bases, second_rank, 1, inner_last);
|
||||
if (inner_total * inner_last < threadingThreshold) {
|
||||
maxThreads = 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<X[]> maxValues(new X[maxThreads]);
|
||||
std::unique_ptr<Z[]> maxIndices(new Z[maxThreads]);
|
||||
X* ptrMaxValues = maxValues.get();
|
||||
Z* ptrMaxIndices = maxIndices.get();
|
||||
auto func = [ptrMaxValues, ptrMaxIndices, 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 (inner_stride == 1) {
|
||||
indexInnerReductionRank1Block4WithMerge<X, Z, ReductionOp>(&(bufferX[start]), current, argCurrent, loopTotal);
|
||||
} else {
|
||||
indexInnerReductionRank1Block4WithMerge<X, Z, ReductionOp>(&(bufferX[start * inner_stride]), current,
|
||||
argCurrent, loopTotal, inner_stride);
|
||||
}
|
||||
ptrMaxIndices[thread_id] = argCurrent + start;
|
||||
} else {
|
||||
if (inner_stride == 1) {
|
||||
indexInnerReduction<X, Z, ReductionOp, LastIndexFaster>(second_rank, bufferX, current, argCurrent, inner_bases,
|
||||
inner_strides, start, stop, inner_last, inner_stride);
|
||||
} else {
|
||||
indexInnerReduction<X, Z, ReductionOp, LastIndexFaster>(second_rank, bufferX, current, argCurrent, inner_bases,
|
||||
inner_strides, start, stop, inner_last, inner_stride);
|
||||
}
|
||||
ptrMaxIndices[thread_id] = argCurrent;
|
||||
}
|
||||
ptrMaxValues[thread_id] = current;
|
||||
};
|
||||
#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
|
||||
Z arg = 0;
|
||||
X current = ptrMaxValues[0];
|
||||
|
||||
for (Z i = 1; i < Count; i++) {
|
||||
ReductionOp::update(current, arg, ptrMaxValues[i], i);
|
||||
}
|
||||
|
||||
*outputZ = ptrMaxIndices[arg];
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, typename Movement, bool LastIndexFaster = true>
|
||||
static void argReductionInnerCases(Movement& movement, sd::LongType loopTotal, const int& second_rank,
|
||||
const sd::LongType* inner_bases, const sd::LongType* inner_strides, const X* bufferX,
|
||||
Z* outputZ) {
|
||||
sd::LongType inner_stride = true /*LastIndexFaster*/ ? inner_strides[second_rank - 1] : inner_strides[0];
|
||||
|
||||
sd::LongType loopTotal_K = loopTotal / 4;
|
||||
sd::LongType loopTotal_Tail = loopTotal & 3;
|
||||
if (inner_stride == 1) {
|
||||
if (second_rank == 1) {
|
||||
LOG_CALLS(0)
|
||||
sd::LongType inner_total = getLength<true>(inner_bases, second_rank);
|
||||
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();
|
||||
indexInnerReductionRank1Block4<X, Z, ReductionOp>(buffer0, buffer1, buffer2, buffer3, output0, output1, output2,
|
||||
output3, inner_total);
|
||||
}
|
||||
if (inner_total >= 2048) {
|
||||
for (sd::LongType i = 0; i < loopTotal_Tail; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReductionRank1Block4WithMerge<X, Z, ReductionOp>(buffer0, current, outputZ[movement.Second()],
|
||||
inner_total);
|
||||
movement.increment();
|
||||
}
|
||||
} else {
|
||||
for (sd::LongType i = 0; i < loopTotal_Tail; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReductionRank1<X, Z, ReductionOp>(buffer0, current, outputZ[movement.Second()], inner_total);
|
||||
movement.increment();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
sd::LongType inner_last;
|
||||
sd::LongType inner_loop = getLength<true>(inner_bases, second_rank, 1, inner_last);
|
||||
if (second_rank == 2) {
|
||||
LOG_CALLS(1)
|
||||
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();
|
||||
indexInnerReductionConstRankBlock4<X, Z, ReductionOp, 2>(buffer0, buffer1, buffer2, buffer3, output0, output1,
|
||||
output2, output3, inner_bases, inner_strides,
|
||||
inner_loop, inner_last);
|
||||
}
|
||||
for (sd::LongType i = 0; i < loopTotal_Tail; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReductionConstRank<X, Z, ReductionOp, 2>(buffer0, current, outputZ[movement.Second()], inner_bases,
|
||||
inner_strides, inner_loop, inner_last);
|
||||
movement.increment();
|
||||
}
|
||||
|
||||
} else if (second_rank == 3) {
|
||||
LOG_CALLS(2)
|
||||
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();
|
||||
indexInnerReductionConstRankBlock4<X, Z, ReductionOp, 3>(buffer0, buffer1, buffer2, buffer3, output0, output1,
|
||||
output2, output3, inner_bases, inner_strides,
|
||||
inner_loop, inner_last);
|
||||
}
|
||||
for (sd::LongType i = 0; i < loopTotal_Tail; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReductionConstRank<X, Z, ReductionOp, 3>(buffer0, current, outputZ[movement.Second()], inner_bases,
|
||||
inner_strides, inner_loop, inner_last);
|
||||
movement.increment();
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_CALLS(3)
|
||||
for (sd::LongType i = 0; i < loopTotal; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReduction<X, Z, ReductionOp>(second_rank, buffer0, current, outputZ[movement.Second()], inner_bases,
|
||||
inner_strides, 0, inner_loop, inner_last);
|
||||
movement.increment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (second_rank == 1) {
|
||||
LOG_CALLS(10)
|
||||
sd::LongType inner_total = getLength<true>(inner_bases, second_rank);
|
||||
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();
|
||||
indexInnerReductionRank1Block4<X, Z, ReductionOp>(buffer0, buffer1, buffer2, buffer3, output0, output1, output2,
|
||||
output3, inner_total, inner_stride);
|
||||
}
|
||||
if (inner_total >= 2048) {
|
||||
for (sd::LongType i = 0; i < loopTotal_Tail; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReductionRank1Block4WithMerge<X, Z, ReductionOp>(buffer0, current, outputZ[movement.Second()],
|
||||
inner_total, inner_stride);
|
||||
movement.increment();
|
||||
}
|
||||
} else {
|
||||
for (sd::LongType i = 0; i < loopTotal_Tail; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReductionRank1<X, Z, ReductionOp>(buffer0, current, outputZ[movement.Second()], inner_total,
|
||||
inner_stride);
|
||||
movement.increment();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
sd::LongType inner_last;
|
||||
sd::LongType inner_loop = getLength<true>(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();
|
||||
indexInnerReductionConstRankBlock4<X, Z, ReductionOp, 2>(buffer0, buffer1, buffer2, buffer3, output0, output1,
|
||||
output2, output3, inner_bases, inner_strides,
|
||||
inner_loop, inner_last, inner_stride);
|
||||
}
|
||||
for (sd::LongType i = 0; i < loopTotal_Tail; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReductionConstRank<X, Z, ReductionOp, 2>(buffer0, current, outputZ[movement.Second()], inner_bases,
|
||||
inner_strides, inner_loop, inner_last, inner_stride);
|
||||
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();
|
||||
indexInnerReductionConstRankBlock4<X, Z, ReductionOp, 3>(buffer0, buffer1, buffer2, buffer3, output0, output1,
|
||||
output2, output3, inner_bases, inner_strides,
|
||||
inner_loop, inner_last, inner_stride);
|
||||
}
|
||||
for (sd::LongType i = 0; i < loopTotal_Tail; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReductionConstRank<X, Z, ReductionOp, 3>(buffer0, current, outputZ[movement.Second()], inner_bases,
|
||||
inner_strides, inner_loop, inner_last, inner_stride);
|
||||
movement.increment();
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_CALLS(13)
|
||||
for (sd::LongType i = 0; i < loopTotal; i++) {
|
||||
X current;
|
||||
const X* buffer0 = &(bufferX[movement.First()]);
|
||||
indexInnerReduction<X, Z, ReductionOp>(second_rank, buffer0, current, outputZ[movement.Second()], inner_bases,
|
||||
inner_strides, 0, inner_loop, inner_last, inner_stride);
|
||||
movement.increment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z, typename ReductionOp, bool LastIndexFaster = true>
|
||||
static void argIndexCaseNonScalar(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) {
|
||||
sd::LongType total = getLength<LastIndexFaster>(outer_bases, first_rank);
|
||||
sd::LongType inner_stride = true /*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](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);
|
||||
argReductionInnerCases<X, Z, ReductionOp>(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX,
|
||||
outputZ);
|
||||
} else {
|
||||
ZipGenericCoordsRank1BothStrideN movement;
|
||||
movement.init(nullptr, &stride, &output_stride, 0, start);
|
||||
argReductionInnerCases<X, Z, ReductionOp>(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX,
|
||||
outputZ);
|
||||
}
|
||||
|
||||
} 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);
|
||||
argReductionInnerCases<X, Z, ReductionOp>(movement, loopTotal, second_rank, inner_bases, inner_strides,
|
||||
bufferX, outputZ);
|
||||
|
||||
} else {
|
||||
ZipGenericCoordsConstMovementSecondStrideN<2, LastIndexFaster> movement;
|
||||
movement.init(outer_bases, outer_strides, &output_stride, first_rank, start);
|
||||
argReductionInnerCases<X, Z, ReductionOp>(movement, loopTotal, second_rank, inner_bases, inner_strides,
|
||||
bufferX, outputZ);
|
||||
}
|
||||
} else if (first_rank == 3) {
|
||||
if (output_stride == 1) {
|
||||
ZipGenericCoordsConstMovementSecondStride1<3, LastIndexFaster> movement;
|
||||
movement.init(outer_bases, outer_strides, nullptr, first_rank, start);
|
||||
argReductionInnerCases<X, Z, ReductionOp>(movement, loopTotal, second_rank, inner_bases, inner_strides,
|
||||
bufferX, outputZ);
|
||||
|
||||
} else {
|
||||
ZipGenericCoordsConstMovementSecondStrideN<3, LastIndexFaster> movement;
|
||||
movement.init(outer_bases, outer_strides, &output_stride, first_rank, start);
|
||||
argReductionInnerCases<X, Z, ReductionOp>(movement, loopTotal, second_rank, inner_bases, inner_strides,
|
||||
bufferX, outputZ);
|
||||
}
|
||||
} else {
|
||||
ZipGenericCoordsMovementSecondStrideN<LastIndexFaster> movement;
|
||||
movement.init(outer_bases, outer_strides, &output_stride, first_rank, start);
|
||||
|
||||
argReductionInnerCases<X, Z, ReductionOp>(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX,
|
||||
outputZ);
|
||||
}
|
||||
|
||||
} else {
|
||||
ZipGenericCoordsMovement<LastIndexFaster> movement;
|
||||
movement.init(outer_bases, outer_strides, output_strides, first_rank, start);
|
||||
|
||||
argReductionInnerCases<X, Z, ReductionOp>(movement, loopTotal, second_rank, inner_bases, inner_strides, bufferX,
|
||||
outputZ);
|
||||
}
|
||||
};
|
||||
#if 0
|
||||
func(0, 0, total, 1);
|
||||
#else
|
||||
//
|
||||
uint32_t numThreads = sd::Environment::getInstance().maxMasterThreads();
|
||||
sd::LongType inner_total = getLength<true>(inner_bases, second_rank);
|
||||
if (total * inner_total <= threadingThreshold) {
|
||||
numThreads = 1;
|
||||
} else {
|
||||
if (inner_stride > outer_stride && total <= 256) {
|
||||
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 ReductionOp>
|
||||
SD_LIB_HIDDEN void argIndex_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
char input_order = input.ordering();
|
||||
bool try_squash_outer = (input_order == output.ordering()) && output.ews() != 0;
|
||||
const sd::LongType* input_shapeInfo = input.shapeInfo();
|
||||
const sd::LongType* 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, input_order == 'c');
|
||||
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) {
|
||||
argIndexCase1Scalar<X, Z, ReductionOp>(second_rank, inner_bases, inner_strides, bufferX, outputZ);
|
||||
} else {
|
||||
argIndexCaseNonScalar<X, Z, ReductionOp>(first_rank, output_rank, try_squash_outer, second_rank, outer_bases,
|
||||
outer_strides, output_strides, output_stride, inner_bases, inner_strides,
|
||||
bufferX, outputZ);
|
||||
}
|
||||
} else {
|
||||
if (first_rank == 0) {
|
||||
LOG_CALLS(0);
|
||||
if (second_rank == 1) {
|
||||
argIndexCase1Scalar<X, Z, ReductionOp, false>(second_rank, inner_bases, inner_strides, bufferX, outputZ);
|
||||
} else {
|
||||
argIndexCase1Scalar<X, Z, ReductionOp, true>(second_rank, inner_bases, inner_strides, bufferX, outputZ);
|
||||
}
|
||||
} else {
|
||||
LOG_CALLS(1);
|
||||
argIndexCaseNonScalar<X, Z, ReductionOp, false>(first_rank, output_rank, try_squash_outer, second_rank,
|
||||
outer_bases, outer_strides, output_strides, output_stride,
|
||||
inner_bases, inner_strides, bufferX, outputZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
struct IndexMax {
|
||||
static SD_INLINE void update(X& current, Z& currentIndex, const X& candidate, const Z& candidateIndex) {
|
||||
if (candidate > current) {
|
||||
current = candidate;
|
||||
currentIndex = candidateIndex;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename X, typename Z>
|
||||
struct IndexMin {
|
||||
static SD_INLINE void update(X& current, Z& currentIndex, const X& candidate, const Z& candidateIndex) {
|
||||
if (candidate < current) {
|
||||
current = candidate;
|
||||
currentIndex = candidateIndex;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename X, typename Z>
|
||||
struct IndexAbsMax {
|
||||
static SD_INLINE void update(X& current, Z& currentIndex, const X& candidate, const Z& candidateIndex) {
|
||||
auto absCandidate = sd::math::sd_abs<X,X>(candidate);
|
||||
if (absCandidate > current) {
|
||||
current = absCandidate;
|
||||
currentIndex = candidateIndex;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename X, typename Z>
|
||||
struct IndexAbsMin {
|
||||
static SD_INLINE void update(X& current, Z& currentIndex, const X& candidate, const Z& candidateIndex) {
|
||||
auto absCandidate = sd::math::sd_abs<X,X>(candidate);
|
||||
if (absCandidate < current) {
|
||||
current = absCandidate;
|
||||
currentIndex = candidateIndex;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
SD_LIB_HIDDEN void argMax_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
return argIndex_<X, Z, IndexMax<X, Z>>(input, output, dimensions);
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
SD_LIB_HIDDEN void argMin_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
return argIndex_<X, Z, IndexMin<X, Z>>(input, output, dimensions);
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
SD_LIB_HIDDEN void argAbsMax_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
return argIndex_<X, Z, IndexAbsMax<X, Z>>(input, output, dimensions);
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
SD_LIB_HIDDEN void argAbsMin_(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
return argIndex_<X, Z, IndexAbsMin<X, Z>>(input, output, dimensions);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,52 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
|
||||
//
|
||||
|
||||
#include <helpers/Loops.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
#if NOT_EXCLUDED(OP_invert_permutation)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void invertPermutation(sd::LaunchContext* context, NDArray& input, NDArray& output) {
|
||||
std::set<int> uniqueElems;
|
||||
const int length = input.lengthOf();
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
int elem = input.e<int>(i);
|
||||
|
||||
if (!uniqueElems.insert(elem).second) // this operation forbids us to use #pragma omp
|
||||
THROW_EXCEPTION("helpers::invertPermutation function: input array contains duplicates !");
|
||||
|
||||
if (elem < 0 || elem > length - 1)
|
||||
THROW_EXCEPTION(
|
||||
"helpers::invertPermutation function: element of input array is out of range (0, length-1) !");
|
||||
|
||||
output.p<int>(elem, i);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma, created on 21.09.2018
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
// CPU implementation of ismax helper
|
||||
//
|
||||
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <ops/declarable/helpers/ismax.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void ismax_(LaunchContext* context, NDArray* input, NDArray* output,
|
||||
const std::vector<LongType>& dimensions) {
|
||||
// Initialize output to zeros
|
||||
output->nullify();
|
||||
|
||||
if (dimensions.size() == 0 || (dimensions.size() == 1 && dimensions[0] == sd::DataTypeUtils::max<int>())) {
|
||||
// Scalar case - find the single maximum in the entire array
|
||||
auto indexMax = input->applyIndexReduce(indexreduce::IndexMax, &dimensions);
|
||||
auto targetIdx = indexMax->e<LongType>(0);
|
||||
|
||||
// Set the maximum position to 1
|
||||
output->p(targetIdx, static_cast<T>(1));
|
||||
|
||||
delete indexMax;
|
||||
} else {
|
||||
// Dimensional case - find maximum along specified dimensions
|
||||
std::vector<LongType> copy(dimensions);
|
||||
|
||||
// Get the indices of maximum values along the specified dimensions
|
||||
auto indexMaxArr = input->applyIndexReduce(indexreduce::IndexMax, &dimensions);
|
||||
|
||||
// Get TAD information for the output
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), copy.data(), copy.size());
|
||||
auto zTadShapeInfo = packZ->primaryShapeInfo();
|
||||
auto zTadOffsets = packZ->primaryOffsets();
|
||||
|
||||
auto numTads = packZ->numberOfTads();
|
||||
auto tadLen = shape::length(zTadShapeInfo);
|
||||
|
||||
auto zBuffer = output->bufferAsT<T>();
|
||||
|
||||
// For each TAD, set the maximum index position to 1
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto t = start; t < stop; t++) {
|
||||
auto zTadOffset = zTadOffsets[t];
|
||||
auto maxIdx = indexMaxArr->e<LongType>(t);
|
||||
|
||||
// Calculate the actual offset within this TAD
|
||||
if (maxIdx >= 0 && maxIdx < tadLen) {
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
sd::LongType zOffset;
|
||||
|
||||
const int tadRank = shape::rank(zTadShapeInfo);
|
||||
const sd::LongType* tadShape = shape::shapeOf(zTadShapeInfo);
|
||||
const sd::LongType* tadStride = shape::stride(zTadShapeInfo);
|
||||
|
||||
INDEX2COORDS(maxIdx, tadRank, tadShape, coords);
|
||||
COORDS2INDEX(tadRank, tadStride, coords, zOffset);
|
||||
|
||||
zBuffer[zTadOffset + zOffset] = static_cast<T>(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, numTads);
|
||||
|
||||
delete indexMaxArr;
|
||||
}
|
||||
}
|
||||
|
||||
void ismax(LaunchContext* context, NDArray* input, NDArray* output, const std::vector<LongType>& dimensions) {
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), ismax_, (context, input, output, dimensions), SD_COMMON_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE(void ismax_,
|
||||
(sd::LaunchContext* context, NDArray* input, NDArray* output,
|
||||
const std::vector<sd::LongType>& dimensions),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,347 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 GS <sgazeos@gmail.com>
|
||||
//
|
||||
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <ops/declarable/helpers/legacy_helpers.h>
|
||||
#include <ops/ops.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static void reluDerivative__(NDArray* theFirst, NDArray* theSecond) {
|
||||
auto functor = LAMBDA_TT(x, y) { return x > (T)0.f ? y : T(0.f); });
|
||||
|
||||
theFirst->applyPairwiseLambda<T>(theSecond, functor, theFirst);
|
||||
}
|
||||
|
||||
void reluDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), reluDerivative__, (theFirst, theSecond), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void reluDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
T zero = (T)0.f;
|
||||
auto functor = LAMBDA_TT(x, y, zero) { return x > zero ? y : zero; });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
|
||||
}
|
||||
|
||||
void reluDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), reluDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void relu6Derivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return x > (T)0.f && x < (T)6.f ? y : T(0.f); });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void relu6Derivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), relu6Derivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void leakyReluDerivative_(NDArray* input, NDArray* epsilon, NDArray* output, const float alpha) {
|
||||
const T alphaT = static_cast<T>(alpha);
|
||||
|
||||
auto functor = LAMBDA_TT(x, y, alphaT) { return x < 0 ? alphaT * y : y; });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void leakyReluDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput,
|
||||
const float alpha) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), leakyReluDerivative_, (theFirst, theSecond, theOutput, alpha),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void eluDerivative_(NDArray* input, NDArray* epsilon, NDArray* output, const float alpha) {
|
||||
const T alphaT = static_cast<T>(alpha);
|
||||
|
||||
auto functor = LAMBDA_TT(x, y, alphaT) { return y * sd::math::sd_eluderivative<T, T>(x, alphaT); });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void eluDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput,
|
||||
const float alpha) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), eluDerivative_, (theFirst, theSecond, theOutput, alpha), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void seluDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return y * simdOps::SELUDerivative<T>::op(x, nullptr); });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void seluDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), seluDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void cubeDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return y * (3 * x * x); });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void cubeDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), cubeDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
// return (x >= X(0.f) ? y: -y);
|
||||
template <typename T>
|
||||
static void reduceNorm1_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return x > T(0.f) ? y : -y; });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void reduceNorm1(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), reduceNorm1_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void sigmCrossEntropy_(NDArray* logits, NDArray* labels, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
return sd::math::sd_max<T>(x, (T)0.f) - x * y +
|
||||
sd::math::sd_log<T, T>((T)1.f + sd::math::sd_exp<T, T>(-sd::math::sd_abs<T,T>(x)));
|
||||
});
|
||||
|
||||
logits->applyPairwiseLambda<T>(labels, functor, output);
|
||||
}
|
||||
|
||||
void sigmCrossEntropy(sd::LaunchContext* context, NDArray* logits, NDArray* labels, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(logits->dataType(), sigmCrossEntropy_, (logits, labels, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void sigmCrossEntropyGrad_(NDArray* logits, NDArray* labels, NDArray* output) {
|
||||
// 1 - labels - 1 / (1 + exp(logits))
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
if (x <= 0) return static_cast<T>(1.) - y - static_cast<T>(1.) / (static_cast<T>(1.) + sd::math::sd_exp<T, T>(x));
|
||||
auto e = sd::math::sd_exp<T, T>(-x);
|
||||
return static_cast<T>(1.) - y - e / (static_cast<T>(1.) + e);
|
||||
});
|
||||
|
||||
logits->applyPairwiseLambda<T>(labels, functor, output);
|
||||
}
|
||||
|
||||
void sigmCrossEntropyGrad(sd::LaunchContext* context, NDArray* logits, NDArray* labels, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(logits->dataType(), sigmCrossEntropyGrad_, (logits, labels, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void tanhDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T th = sd::math::sd_tanh<T, T>(x);
|
||||
return y * ((T)1.0f - (th * th));
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void tanhDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), tanhDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void hardTanhDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
return y * simdOps::HardTanhDerivative<T>::op(x, nullptr);
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor,output);
|
||||
}
|
||||
|
||||
void hardTanhDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), hardTanhDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void rationalTanhDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return y * simdOps::RationalTanhDerivative<T>::op(x, nullptr); });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void rationalTanhDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), rationalTanhDerivative_, (theFirst, theSecond, theOutput),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void rectifiedTanhDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return x > (T)0.0f ? y * (sd::math::sd_tanhderivative<T, T>(x)) : (T)0.0f; });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void rectifiedTanhDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), rectifiedTanhDerivative_, (theFirst, theSecond, theOutput),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
static void softSignDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T ss = (T)1.f + sd::math::sd_abs<T,T>(x);
|
||||
return y * ((T)1.0f / (ss * ss));
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void softSignDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), softSignDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void softPlusDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T p = sd::math::sd_pow<T, T, T>(static_cast<T>(M_E), x);
|
||||
return y * (p / (p + 1.));
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void softPlusDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), softPlusDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
///
|
||||
/// \param theFirst
|
||||
/// \param theSecond
|
||||
/// \param theOutput
|
||||
template <typename T>
|
||||
static void sigmoidDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T s = sd::math::sd_sigmoid<T, T>(x);
|
||||
return y * (s * ((T)1.0f - s));
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void sigmoidDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), sigmoidDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void hardSigmoidDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return y * simdOps::HardSigmoidDerivative<T>::op(x, nullptr); });
|
||||
|
||||
input->applyPairwiseLambda<T>(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void hardSigmoidDerivative(sd::LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), hardSigmoidDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void logSumExp_(NDArray* input, NDArray* axis, NDArray* output) {
|
||||
// reduce along axis with
|
||||
NDArray *tempInput = input->dup();
|
||||
input->applyTransform(transform::Exp, tempInput);
|
||||
std::vector<sd::LongType> axisVector;
|
||||
if (axis != nullptr) {
|
||||
axisVector.resize(axis->lengthOf());
|
||||
for (size_t i = 0; i < axisVector.size(); ++i) axisVector[i] = axis->e<sd::LongType>(i);
|
||||
}
|
||||
tempInput->reduceAlongDimension(reduce::Sum, output, &axisVector);
|
||||
output->applyTransform(transform::Log, output);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void logSumExp_(NDArray* input, NDArray* subtrah, NDArray* axis, NDArray* output) {
|
||||
// reduce along axis with
|
||||
NDArray *tempInput = input->dup();
|
||||
input->applyPairwiseTransform(pairwise::Subtract, subtrah, tempInput);
|
||||
tempInput->applyTransform(transform::Exp, tempInput);
|
||||
|
||||
std::vector<sd::LongType> axisVector;
|
||||
if (axis != nullptr) {
|
||||
axisVector.resize(axis->lengthOf());
|
||||
for (size_t i = 0; i < axisVector.size(); ++i) axisVector[i] = axis->e<sd::LongType>(i);
|
||||
}
|
||||
tempInput->reduceAlongDimension(reduce::Sum, output, &axisVector);
|
||||
output->applyTransform(transform::Log, output);
|
||||
}
|
||||
|
||||
void logSumExp(sd::LaunchContext* context, NDArray* input, NDArray* axis, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), logSumExp_, (input, axis, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
void logSumExp(sd::LaunchContext* context, NDArray* input, NDArray* subtrah, NDArray* axis, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), logSumExp_, (input, subtrah, axis, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void weightedCrossEntropyWithLogitsFunctor_(NDArray * targets, NDArray * input, NDArray * weights,
|
||||
NDArray* output) {
|
||||
T posWeight = weights->e<T>(0);
|
||||
|
||||
auto mainRoutineT1 = LAMBDA_TT(_x, _z, posWeight) {
|
||||
T targetWeight = (1. + (posWeight - (T)1.f) * _z);
|
||||
return (1. - _z) * _x +
|
||||
targetWeight * (sd::math::sd_log<T, T>((T)1.f + sd::math::sd_exp<T, T>(-sd::math::sd_abs<T,T>(_x))) +
|
||||
sd::math::sd_max(-_x, T(0.f)));
|
||||
});
|
||||
|
||||
auto mainRoutineT2 = LAMBDA_TTT(_x, _z, _w) {
|
||||
return (((T)1.0 - _z) * _x) + _w * (sd::math::sd_log<T, T>(T(1.) + sd::math::sd_exp<T, T>(-sd::math::sd_abs<T,T>(_x))) +
|
||||
sd::math::sd_max(-_x, T(0.f)));
|
||||
});
|
||||
|
||||
if (weights->isScalar()) {
|
||||
input->applyPairwiseLambda<T>(targets, mainRoutineT1, output);
|
||||
} else {
|
||||
weights->applyScalar(scalar::Add, -1.f, weights);
|
||||
auto add = (*targets * *targets);
|
||||
auto addOne = (*add) + T(1.f);
|
||||
*targets = *addOne;
|
||||
delete addOne;
|
||||
delete add;
|
||||
input->applyTriplewiseLambda<T>(targets, targets,mainRoutineT2, output);
|
||||
}
|
||||
}
|
||||
|
||||
void weightedCrossEntropyWithLogitsFunctor(sd::LaunchContext* context, NDArray * targets, NDArray * input,
|
||||
NDArray * weights, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(targets->dataType(), weightedCrossEntropyWithLogitsFunctor_, (targets, input, weights, output),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 George A. Shulinok <sgazeos@gmail.com>
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/lgamma.h>
|
||||
#if NOT_EXCLUDED(OP_lgamma)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// calculate digamma function for array elements
|
||||
template <typename T>
|
||||
static void lgamma_(NDArray* x, NDArray* z) {
|
||||
auto lgammaProc = LAMBDA_T(x_) {
|
||||
auto output = math::sd_lgamma<T,T>(x_);
|
||||
return output;
|
||||
});
|
||||
|
||||
x->applyLambda<T>(lgammaProc, z);
|
||||
}
|
||||
|
||||
void lgamma(sd::LaunchContext* context, NDArray* x, NDArray* z) {
|
||||
BUILD_SINGLE_SELECTOR(x->dataType(), lgamma_, (x, z), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void lgamma_, (NDArray * x, NDArray* z), SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,325 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <ops/declarable/helpers/lrn.h>
|
||||
#if NOT_EXCLUDED(OP_lrn)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static sd::Status lrnFunctor_(sd::graph::Context& block, NDArray* input, NDArray* output, int depth, float bias,
|
||||
float alpha, float beta) {
|
||||
sd_debug("MKL-DNN is not used for lrn!\n", 0);
|
||||
|
||||
const int rank = input->rankOf();
|
||||
|
||||
auto inTadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(),
|
||||
rank - 1);
|
||||
std::shared_ptr<TadPack> outTadPack;
|
||||
|
||||
if (shape::haveSameShapeAndStrides(input->shapeInfo(), output->shapeInfo()))
|
||||
outTadPack = inTadPack;
|
||||
else
|
||||
outTadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(),
|
||||
rank - 1);
|
||||
|
||||
const sd::LongType numOfTads = inTadPack->numberOfTads();
|
||||
const sd::LongType tadLen = input->sizeAt(-1);
|
||||
|
||||
const sd::LongType* inTadOffsets = inTadPack->primaryOffsets();
|
||||
const sd::LongType* outTadOffsets = outTadPack->primaryOffsets();
|
||||
|
||||
const sd::LongType inTadEws = shape::elementWiseStride(inTadPack->primaryShapeInfo());
|
||||
const sd::LongType outTadEws = shape::elementWiseStride(outTadPack->primaryShapeInfo());
|
||||
|
||||
const T* inBuff = reinterpret_cast<T*>(input->buffer());
|
||||
T* outBuff = reinterpret_cast<T*>(output->buffer());
|
||||
|
||||
const T tbias = static_cast<T>(bias);
|
||||
const T tbeta = static_cast<T>(beta);
|
||||
|
||||
if (inTadEws == 1 && outTadEws == 1) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
const T* x = inBuff + inTadOffsets[i];
|
||||
T* y = outBuff + outTadOffsets[i];
|
||||
|
||||
T prev = static_cast<T>(0);
|
||||
|
||||
// calculate squared sum of elements per each j-th element range [j - depth, j + depth + 1]
|
||||
// we store each squared sum in corresponding element of y array
|
||||
for (sd::LongType j = 0; j < tadLen; ++j) {
|
||||
const sd::LongType begin = sd::math::sd_max<int>(0, j - depth);
|
||||
const sd::LongType last = depth + j + 1;
|
||||
const sd::LongType end = sd::math::sd_min<int>(last, tadLen);
|
||||
|
||||
if (j == 0) {
|
||||
for (sd::LongType s = begin; s < end; ++s) prev = prev + x[s] * x[s];
|
||||
y[j] = prev;
|
||||
} else if (begin == 0 && last <= tadLen)
|
||||
y[j] = prev + x[end - 1] * x[end - 1];
|
||||
else if (begin > 0 && last <= tadLen)
|
||||
y[j] = prev + x[end - 1] * x[end - 1] - x[begin - 1] * x[begin - 1];
|
||||
else if (begin > 0 && last > tadLen)
|
||||
y[j] = prev - x[begin - 1] * x[begin - 1];
|
||||
else
|
||||
y[j] = prev;
|
||||
|
||||
if (j != 0) prev = y[j];
|
||||
|
||||
y[j] = x[j] / sd::math::sd_pow<T, T, T>(tbias + alpha * prev, tbeta);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
} else {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType i = 0; i < numOfTads; ++i) {
|
||||
const T* x = inBuff + inTadOffsets[i];
|
||||
T* y = outBuff + outTadOffsets[i];
|
||||
|
||||
T prev = static_cast<T>(0);
|
||||
|
||||
// calculate squared sum of elements per each j-th element range [j - depth, j + depth + 1]
|
||||
// we store each squared sum in corresponding element of y array
|
||||
for (sd::LongType j = 0; j < tadLen; ++j) {
|
||||
const sd::LongType begin = sd::math::sd_max<int>(0, j - depth);
|
||||
const sd::LongType last = depth + j + 1;
|
||||
const sd::LongType end = sd::math::sd_min<int>(last, tadLen);
|
||||
|
||||
if (j == 0) {
|
||||
for (sd::LongType s = begin; s < end; ++s) prev = prev + x[s * inTadEws] * x[s * inTadEws];
|
||||
y[j * outTadEws] = prev;
|
||||
} else if (begin == 0 && last <= tadLen)
|
||||
y[j * outTadEws] = prev + x[(end - 1) * inTadEws] * x[(end - 1) * inTadEws];
|
||||
else if (begin > 0 && last <= tadLen)
|
||||
y[j * outTadEws] = prev + x[(end - 1) * inTadEws] * x[(end - 1) * inTadEws] -
|
||||
x[(begin - 1) * inTadEws] * x[(begin - 1) * inTadEws];
|
||||
else if (begin > 0 && last > tadLen)
|
||||
y[j * outTadEws] = prev - x[(begin - 1) * inTadEws] * x[(begin - 1) * inTadEws];
|
||||
else
|
||||
y[j * outTadEws] = prev;
|
||||
|
||||
if (j != 0) prev = y[j * outTadEws];
|
||||
|
||||
y[j * outTadEws] = x[j * inTadEws] / sd::math::sd_pow<T, T, T>(tbias + alpha * prev, tbeta);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
}
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status lrnFunctor_,
|
||||
(sd::graph::Context & block, NDArray* input, NDArray* output, int depth, float bias, float alpha,
|
||||
float beta),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
sd::Status lrnFunctor(sd::graph::Context& block, NDArray* input, NDArray* output, int depth, double bias, double alpha,
|
||||
double beta) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return lrnFunctor_, (block, input, output, depth, bias, alpha, beta),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void lrnBP_(NDArray& input, NDArray& gradO, NDArray& gradI, const int depth, const float bias,
|
||||
const float alpha, const float beta) {
|
||||
const int rank = input.rankOf();
|
||||
|
||||
auto inTadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(),
|
||||
rank - 1);
|
||||
std::shared_ptr<TadPack> gradITadPack;
|
||||
|
||||
if (shape::haveSameShapeAndStrides(input.shapeInfo(), gradI.shapeInfo()))
|
||||
gradITadPack = inTadPack;
|
||||
else
|
||||
gradITadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(gradI.shapeInfo(),
|
||||
rank - 1);
|
||||
|
||||
const sd::LongType numOfTads = inTadPack->numberOfTads();
|
||||
const sd::LongType tadLen = input.sizeAt(-1);
|
||||
|
||||
const sd::LongType* inTadOffsets = inTadPack->primaryOffsets();
|
||||
const sd::LongType* gradITadOffsets = gradITadPack->primaryOffsets();
|
||||
|
||||
const sd::LongType inTadEws = shape::elementWiseStride(inTadPack->primaryShapeInfo());
|
||||
const sd::LongType gradITadEws = shape::elementWiseStride(gradITadPack->primaryShapeInfo());
|
||||
|
||||
const X* inBuff = reinterpret_cast<X const*>(input.buffer());
|
||||
Y* gradIBuff = reinterpret_cast<Y*>(gradI.buffer());
|
||||
|
||||
const Y tbias = static_cast<Y>(bias);
|
||||
const Y tbeta = static_cast<Y>(beta);
|
||||
const Y talpha = static_cast<Y>(alpha);
|
||||
const Y coeff = talpha * tbeta;
|
||||
|
||||
if (inTadEws == 1 && gradITadEws == 1) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
const X* x = inBuff + inTadOffsets[i];
|
||||
Y* y = gradIBuff + gradITadOffsets[i];
|
||||
|
||||
// this loop calculates squared sum of elements per each j-th element range [j - depth, j + depth + 1]
|
||||
// we store each squared sum in corresponding element of y array
|
||||
for (sd::LongType j = 0; j < tadLen; ++j) {
|
||||
const sd::LongType begin = sd::math::sd_max<int>(0, j - depth);
|
||||
const sd::LongType last = depth + j + 1;
|
||||
const sd::LongType end = sd::math::sd_min<int>(last, tadLen);
|
||||
|
||||
if (j == 0) {
|
||||
y[0] = 0;
|
||||
for (sd::LongType s = begin; s < end; ++s) y[0] = y[0] + x[s] * x[s];
|
||||
} else if (begin == 0 && last <= tadLen)
|
||||
y[j] = y[j - 1] + x[end - 1] * x[end - 1];
|
||||
else if (begin > 0 && last <= tadLen)
|
||||
y[j] = y[j - 1] + x[end - 1] * x[end - 1] - x[begin - 1] * x[begin - 1];
|
||||
else if (begin > 0 && last > tadLen)
|
||||
y[j] = y[j - 1] - x[begin - 1] * x[begin - 1];
|
||||
else
|
||||
y[j] = y[j - 1];
|
||||
}
|
||||
|
||||
Y* factor = new Y[tadLen];
|
||||
|
||||
Y prev = static_cast<Y>(0);
|
||||
// second loop calculates derivatives using information gained in first loop above
|
||||
for (sd::LongType j = 0; j < tadLen; ++j) {
|
||||
const sd::LongType begin = sd::math::sd_max<int>(0, j - depth);
|
||||
const sd::LongType last = depth + j + 1;
|
||||
const sd::LongType end = sd::math::sd_min<int>(last, tadLen);
|
||||
|
||||
Y init = tbias + talpha * y[j];
|
||||
|
||||
if (j == 0) {
|
||||
for (sd::LongType s = begin; s < end; ++s) {
|
||||
factor[s] = sd::math::sd_pow<Y, Y, Y>(tbias + talpha * y[s], -tbeta - 1);
|
||||
prev = prev + x[s] * factor[s];
|
||||
}
|
||||
y[0] = prev;
|
||||
} else if (begin == 0 && last <= tadLen) {
|
||||
factor[end - 1] = sd::math::sd_pow<Y, Y, Y>(tbias + talpha * y[end - 1], -tbeta - 1);
|
||||
y[j] = prev + x[end - 1] * factor[end - 1];
|
||||
} else if (begin > 0 && last <= tadLen) {
|
||||
factor[end - 1] = sd::math::sd_pow<Y, Y, Y>(tbias + talpha * y[end - 1], -tbeta - 1);
|
||||
y[j] = prev + x[end - 1] * factor[end - 1] - x[begin - 1] * factor[begin - 1];
|
||||
} else if (begin > 0 && last > tadLen)
|
||||
y[j] = prev - x[begin - 1] * factor[begin - 1];
|
||||
else
|
||||
y[j] = prev;
|
||||
|
||||
if (j != 0) prev = y[j];
|
||||
|
||||
y[j] = factor[j] * init - 2 * x[j] * coeff * prev;
|
||||
}
|
||||
|
||||
delete[] factor;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
} else {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
const X* x = inBuff + inTadOffsets[i];
|
||||
Y* y = gradIBuff + gradITadOffsets[i];
|
||||
|
||||
// this loop calculates squared sum of elements per each j-th element range [j - depth, j + depth + 1]
|
||||
// we store each squared sum in corresponding element of y array
|
||||
for (sd::LongType j = 0; j < tadLen; ++j) {
|
||||
const sd::LongType begin = sd::math::sd_max<int>(0, j - depth);
|
||||
const sd::LongType last = depth + j + 1;
|
||||
const sd::LongType end = sd::math::sd_min<int>(last, tadLen);
|
||||
|
||||
if (j == 0) {
|
||||
y[0] = 0;
|
||||
for (sd::LongType s = begin; s < end; ++s) y[0] = y[0] + x[s * inTadEws] * x[s * inTadEws];
|
||||
} else if (begin == 0 && last <= tadLen)
|
||||
y[j * gradITadEws] = y[(j - 1) * gradITadEws] + x[(end - 1) * inTadEws] * x[(end - 1) * inTadEws];
|
||||
else if (begin > 0 && last <= tadLen)
|
||||
y[j * gradITadEws] = y[(j - 1) * gradITadEws] + x[(end - 1) * inTadEws] * x[(end - 1) * inTadEws] -
|
||||
x[(begin - 1) * inTadEws] * x[(begin - 1) * inTadEws];
|
||||
else if (begin > 0 && last > tadLen)
|
||||
y[j * gradITadEws] = y[(j - 1) * gradITadEws] - x[(begin - 1) * inTadEws] * x[(begin - 1) * inTadEws];
|
||||
else
|
||||
y[j * gradITadEws] = y[(j - 1) * gradITadEws];
|
||||
}
|
||||
|
||||
Y* factor = new Y[tadLen];
|
||||
|
||||
Y prev = static_cast<Y>(0);
|
||||
// second loop calculates derivatives using information gained in first loop above
|
||||
for (sd::LongType j = 0; j < tadLen; ++j) {
|
||||
const sd::LongType begin = sd::math::sd_max<int>(0, j - depth);
|
||||
const sd::LongType last = depth + j + 1;
|
||||
const sd::LongType end = sd::math::sd_min<int>(last, tadLen);
|
||||
|
||||
Y init = tbias + talpha * y[j * gradITadEws];
|
||||
|
||||
if (j == 0) {
|
||||
for (sd::LongType s = begin; s < end; ++s) {
|
||||
factor[s] = sd::math::sd_pow<Y, Y, Y>(tbias + talpha * y[s * gradITadEws], -tbeta - 1);
|
||||
prev = prev + x[s * inTadEws] * factor[s];
|
||||
}
|
||||
y[0] = prev;
|
||||
} else if (begin == 0 && last <= tadLen) {
|
||||
factor[end - 1] = sd::math::sd_pow<Y, Y, Y>(tbias + talpha * y[(end - 1) * gradITadEws], -tbeta - 1);
|
||||
y[j * gradITadEws] = prev + x[(end - 1) * inTadEws] * factor[end - 1];
|
||||
} else if (begin > 0 && last <= tadLen) {
|
||||
factor[end - 1] = sd::math::sd_pow<Y, Y, Y>(tbias + talpha * y[(end - 1) * gradITadEws], -tbeta - 1);
|
||||
y[j * gradITadEws] =
|
||||
prev + x[(end - 1) * inTadEws] * factor[end - 1] - x[(begin - 1) * inTadEws] * factor[begin - 1];
|
||||
} else if (begin > 0 && last > tadLen)
|
||||
y[j * gradITadEws] = prev - x[(begin - 1) * inTadEws] * factor[begin - 1];
|
||||
else
|
||||
y[j * gradITadEws] = prev;
|
||||
|
||||
if (j != 0) prev = y[j * gradITadEws];
|
||||
|
||||
y[j * gradITadEws] = factor[j] * init - 2 * x[j * inTadEws] * coeff * prev;
|
||||
}
|
||||
|
||||
delete[] factor;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, numOfTads);
|
||||
}
|
||||
gradI *= gradO;
|
||||
}
|
||||
|
||||
void lrnBP(sd::graph::Context& block, NDArray& input, NDArray& gradO, NDArray& gradI, const int depth,
|
||||
const float bias, const float alpha, const float beta) {
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), gradO.dataType(), lrnBP_, (input, gradO, gradI, depth, bias, alpha, beta),
|
||||
SD_FLOAT_TYPES, SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 Yurii Shyrma, created on 14.02.2018
|
||||
//
|
||||
|
||||
// implementation of operation for LSTM cell with peep hole connections:
|
||||
// http://www.bioinf.jku.at/publications/older/2604.pdf
|
||||
// S. Hochreiter and J. Schmidhuber. "Long Short-Term Memory". Neural Computation, 9(8):1735-1780, 1997.
|
||||
// and
|
||||
// https://research.google.com/pubs/archive/43905.pdf
|
||||
// Hasim Sak, Andrew Senior, and Francoise Beaufays. "Long short-term memory recurrent neural network architectures for
|
||||
// large scale acoustic modeling." INTERSPEECH, 2014.
|
||||
|
||||
#include <array/NDArrayList.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <graph/VariableSpace.h>
|
||||
#include <helpers/MmulHelper.h>
|
||||
#include <ops/declarable/CustomOperations.h>
|
||||
#include <ops/declarable/helpers/legacy_helpers.h>
|
||||
#include <ops/declarable/helpers/lstm.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <iterator>
|
||||
# if NOT_EXCLUDED(OP_concat) && NOT_EXCLUDED(OP_lstm_cell) && NOT_EXCLUDED(OP_sigmoid)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void lstmCell(sd::LaunchContext* context, NDArray* xt, NDArray* ht_1, NDArray* ct_1,
|
||||
NDArray* Wx, NDArray* Wh, NDArray* Wc, NDArray* Wp, NDArray* b, NDArray* ht,
|
||||
NDArray* ct, const std::vector<double>& params) {
|
||||
// xt input [bS x nIn]
|
||||
// ht_1 previous cell output [bS x numProj], that is at previous time step t-1, in case of projection=false ->
|
||||
// numProj=nOut!!! ct_1 previous cell state [bS x nOut], that is at previous time step t-1
|
||||
|
||||
// Wx input-to-hidden weights, [nIn x 4*nOut]
|
||||
// Wh hidden-to-hidden weights, [numProj x 4*nOut]
|
||||
// Wc diagonal weights for peephole connections [3*nOut]
|
||||
// Wp projection weights [nOut x numProj]
|
||||
// b biases, [4*nOut]
|
||||
|
||||
// ht current cell output [bS x numProj], that is at current time step t
|
||||
// ct current cell state [bS x nOut], that is at current time step t
|
||||
|
||||
const bool peephole = (bool)params[0]; // if true, provide peephole connections
|
||||
const bool projection =
|
||||
(bool)params[1]; // if true, then projection is performed, if false then numProj==nOut is mandatory!!!!
|
||||
double clippingCellValue =
|
||||
params[2]; // clipping value for ct, if it is not equal to zero, then cell state is clipped
|
||||
double clippingProjValue =
|
||||
params[3]; // clipping value for projected ht, if it is not equal to zero, then projected cell output is clipped
|
||||
const double forgetBias = params[4];
|
||||
|
||||
const int bS = xt->sizeAt(0);
|
||||
const int nIn = xt->sizeAt(1);
|
||||
const int numProj = ht_1->sizeAt(1);
|
||||
const int nOut = ct_1->sizeAt(1);
|
||||
|
||||
NDArray *mmulXt = mmul(*xt, *Wx);
|
||||
NDArray *mmulHt = mmul(*ht_1, *Wh);
|
||||
NDArray *addMmuls = (*mmulXt) + (*mmulHt);
|
||||
NDArray *z = (*addMmuls) + (*b); // [bS x 4*nOut] + [bS x 4*nOut] + [1 x 4*nOut] = [bS x 4*nOut]
|
||||
delete mmulXt;
|
||||
delete mmulHt;
|
||||
delete addMmuls;
|
||||
|
||||
NDArray *zit = (*z)({0, 0, 0, nOut}); // z for input gate, = mmul(Wxi,xt) + mmul(Whi,ht_1) + bi = [bS x nOut]
|
||||
NDArray *zft = (*z)({0, 0, nOut, 2 * nOut}); // z for forget gate, = mmul(Wxf,xt) + mmul(Whf,ht_1) + bf = [bS x nOut]
|
||||
NDArray *zct = (*z)({0, 0, 2 * nOut, 3 * nOut}); // z for cell state, = mmul(Wxc,xt) + mmul(Whc,ht_1) + bc = [bS x nOut]
|
||||
NDArray *zot = (*z)({0, 0, 3 * nOut, 4 * nOut}); // z for output gate, = mmul(Wxo,xt) + mmul(Who,ht_1) + bo = [bS x nOut]
|
||||
|
||||
if (peephole) { // add peephole connections: z + ct_1*Wc
|
||||
NDArray *wcFirst = (*Wc)({0, nOut});
|
||||
NDArray *wcSecond = (*Wc)({nOut, 2 * nOut});
|
||||
NDArray *peepholeFirst = (*ct_1) * (*wcFirst);
|
||||
NDArray *peepholeSecond = (*ct_1) * (*wcSecond);
|
||||
*zit += (*peepholeFirst); // add peephole connections to input gate
|
||||
*zft += (*peepholeSecond); // add peephole connections to forget gate
|
||||
delete peepholeFirst;
|
||||
delete peepholeSecond;
|
||||
delete wcFirst;
|
||||
delete wcSecond;
|
||||
}
|
||||
|
||||
// current sell state = ft*ct_1 + it*tanh(mmul(Wxc,xt) + mmul(Whc,ht_1) + bc
|
||||
NDArray *zftPlusBias = (*zft) + forgetBias;
|
||||
NDArray sigmoidZft = sigmoid(*zftPlusBias);
|
||||
NDArray sigmoidZit = sigmoid(*zit);
|
||||
NDArray tanhZct = tanh(*zct);
|
||||
NDArray *sigmoidZftMulCt1 = sigmoidZft * (*ct_1);
|
||||
NDArray *sigmoidZitMulTanhZct = sigmoidZit * tanhZct;
|
||||
NDArray *sigmoidOut = (*sigmoidZftMulCt1) + (*sigmoidZitMulTanhZct);
|
||||
ct->assign(sigmoidOut);
|
||||
delete zftPlusBias;
|
||||
delete sigmoidZftMulCt1;
|
||||
delete sigmoidZitMulTanhZct;
|
||||
delete sigmoidOut;
|
||||
|
||||
// if clipping value is provided then cell state is clipped by this value prior to the cell output activation
|
||||
if (clippingCellValue > 0.0) ct->applyScalar(scalar::LstmClip, clippingCellValue, ct);
|
||||
|
||||
if (peephole) {
|
||||
NDArray *wcThird = (*Wc)({{2 * nOut, 3 * nOut}});
|
||||
NDArray *peepholeThird = (*ct) * (*wcThird);
|
||||
*zot += (*peepholeThird); // add peephole connections to output gate zot + ct*Wc
|
||||
delete peepholeThird;
|
||||
delete wcThird;
|
||||
}
|
||||
|
||||
// current cell output = ot*tanh(ct)
|
||||
NDArray sigmoidZot = sigmoid(*zot);
|
||||
NDArray tanhCt = tanh(*ct);
|
||||
NDArray *htNoPeepHole = sigmoidZot * tanhCt; // = [bS x nOut]
|
||||
|
||||
// apply projection
|
||||
if (projection) {
|
||||
NDArray *assign = mmul(*htNoPeepHole, *Wp);
|
||||
ht->assign(assign); // [bS x nOut] * [ nOut x numProj] = [bS x numProj]
|
||||
delete assign;
|
||||
// if clipping projection is provided then projected cell output state is clipped by this value
|
||||
if (clippingProjValue != 0.) ht->applyScalar(scalar::LstmClip, clippingProjValue, ht);
|
||||
} else
|
||||
ht->assign(htNoPeepHole);
|
||||
|
||||
delete htNoPeepHole;
|
||||
delete z;
|
||||
delete zit;
|
||||
delete zft;
|
||||
delete zct;
|
||||
delete zot;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void fusedTanh(NDArray* z, NDArray* i, NDArray* c, NDArray* cLast, NDArray* f, NDArray* h) {
|
||||
// cell state = blockInput .* inputGate + prevCellState .* forgetGate
|
||||
auto uLen = static_cast<sd::LongType>(z->lengthOf());
|
||||
auto c_ = c->bufferAsT<T>();
|
||||
auto z_ = z->bufferAsT<T>();
|
||||
auto i_ = i->bufferAsT<T>();
|
||||
auto f_ = f->bufferAsT<T>();
|
||||
auto cLast_ = cLast->bufferAsT<T>();
|
||||
auto h_ = h->bufferAsT<T>();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
c_[e] = z_[e] * i_[e] + (f_[e] * cLast_[e]);
|
||||
h_[e] = sd::math::sd_tanh<T, T>(c_[e]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, uLen);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void lstmBlockCell(NDArray* xt, NDArray* cLast, NDArray* yLast, NDArray* W, NDArray* Wci,
|
||||
NDArray* Wcf, NDArray* Wco, NDArray* b, NDArray* i, NDArray* c, NDArray* f,
|
||||
NDArray* o, NDArray* z, NDArray* h, NDArray* y, const std::vector<double>& params) {
|
||||
/* Input arrays:
|
||||
* 0: xt - input [bS, nIn] at time t
|
||||
* 1: cLast (cs_prev) - previous cell state [bS, nOut], time t-1
|
||||
* 2: yLast (h_prev) - previous output [bS, nOut], time t-1
|
||||
* 3: W - Weights - concatenated (input-to-hidden, hidden-to-hidden weights) weights, [(nIn+nOut),
|
||||
* 4*nOut] 4: Wci - weights - cell peephole (t-1) connections to input modulation gate, [nOut] 5: Wcf -
|
||||
* weights - cell peephole (t-1) connections to forget gate, [nOut] 6: Wco - weights - cell peephole (t)
|
||||
* connections to output gate, [nOut] 7: b - biases, [4*nOut]
|
||||
*
|
||||
* Input integer arguments:
|
||||
* 0: if not zero, provide peephole connections
|
||||
*
|
||||
* Input float arguments:
|
||||
* 0: the bias added to forget gates in order to reduce the scale of forgetting in the beginning of the training
|
||||
* 1: clipping value for cell state, if it is not equal to zero, then cell state is clipped
|
||||
*
|
||||
* Output arrays:
|
||||
* 0: i - Input modulation gate activations [bS, nOut]
|
||||
* 1: c (cs) - Cell state (pre tanh) [bs, nOut] (cs)
|
||||
* 2: f - Output - forget gate activations [bs, nOut]
|
||||
* 3: o - Output - output gate activations [bs, nOut]
|
||||
* 4: z (ci) - Output - block input [bs, nOut]
|
||||
* 5: h (co) - Cell state, post tanh [bs, nOut]
|
||||
* 6: y (h) - Current cell output [bS, nOut], time t
|
||||
*/
|
||||
const bool peephole = (bool)params[0]; // if true, provide peephole connections
|
||||
const double forgetBias = params[1];
|
||||
const double clippingCellValue =
|
||||
params[2]; // clipping value for ct, if it is not equal to zero, then cell state is clipped
|
||||
|
||||
const int bS = xt->sizeAt(0);
|
||||
const int nIn = xt->sizeAt(1);
|
||||
const int nOut = cLast->sizeAt(1);
|
||||
|
||||
std::vector<sd::LongType> cOutShape = {xt->sizeAt(0),xt->sizeAt(1), xt->sizeAt(1) + yLast->sizeAt(1)};
|
||||
// Concat inputs: [xt, yt-1]: concat([bs,nIn],[bs,nOut]) -> [bs, (nIn+nOut)]
|
||||
NDArray concatOut(xt->ordering(), cOutShape, xt->dataType(),
|
||||
xt->getContext());
|
||||
helpers::concat(xt->getContext(), {const_cast<NDArray*>(xt), const_cast<NDArray*>(yLast)}, concatOut, 1);
|
||||
|
||||
NDArray *m = mmul(concatOut, *W); // mmul: [bs, (nIn+nOut)] * [(nIn+nOut), 4*nOut] = [bs, 4*nOut]
|
||||
*m += (*b); // addiRowVector
|
||||
|
||||
// Note: weights are ordered [inputGate, blockInput, forgetGate, outputGate] to match TF (TF code comments state
|
||||
// [i,f,z/ci,o] but behaviour is [i,z,f,o])
|
||||
NDArray *zi = (*m)({0, 0, 0, nOut}); // z for input modulation gate, [bS, nOut]
|
||||
NDArray *zz = (*m)({0, 0, nOut, 2 * nOut}); // z for block input, [bS, nOut]
|
||||
NDArray *zf = (*m)({0, 0, 2 * nOut, 3 * nOut}); // z for forget gate, [bS, nOut]
|
||||
NDArray *zo = (*m)({0, 0, 3 * nOut, 4 * nOut}); // z for output gate, [bS, nOut]
|
||||
|
||||
if (peephole) { // add peephole connections: z + ct_1*Wc
|
||||
NDArray *peepholeI = (*cLast) * (*Wci);
|
||||
NDArray *peepholeF = (*cLast) * (*Wcf);
|
||||
*zi += (*peepholeI); // add peephole connections to input gate
|
||||
*zf += (*peepholeF); // add peephole connections to forget gate
|
||||
delete peepholeI;
|
||||
delete peepholeF;
|
||||
}
|
||||
|
||||
// current sell state = ft*cLast + it*tanh(mmul(Wxc,xt) + mmul(Whc,ht_1) + bc
|
||||
if (forgetBias != 0.0) *zf += forgetBias;
|
||||
|
||||
PRAGMA_OMP_PARALLEL
|
||||
PRAGMA_OMP_SINGLE {
|
||||
PRAGMA_OMP_TASK
|
||||
zz->applyTransform(transform::Tanh, z); // z = tanh(zz)
|
||||
|
||||
PRAGMA_OMP_TASK
|
||||
zi->applyTransform(transform::Sigmoid, i); // i = sigmoid(zi)
|
||||
|
||||
PRAGMA_OMP_TASK
|
||||
zf->applyTransform(transform::Sigmoid, f); // f = sigmoid(zf);
|
||||
}
|
||||
|
||||
if (z->ews() == 1 && i->ews() == 1 && c->ews() == 1 && cLast->ews() == 1 && f->ews() == 1 && h->ews() == 1 &&
|
||||
z->ordering() == i->ordering() && z->ordering() == c->ordering() && z->ordering() == cLast->ordering() &&
|
||||
z->ordering() == f->ordering() && z->ordering() == h->ordering()) {
|
||||
// cell state = blockInput .* inputGate + prevCellState .* forgetGate
|
||||
BUILD_SINGLE_SELECTOR(z->dataType(), fusedTanh, (z, i, c, cLast, f, h), SD_FLOAT_TYPES);
|
||||
} else {
|
||||
// cell state = blockInput .* inputGate + prevCellState .* forgetGate
|
||||
z->applyPairwiseTransform(pairwise::Multiply, i, c); // c = z * i
|
||||
NDArray *temp = (*f) * (*cLast);
|
||||
*c += (*temp); // c = (i * z) + (zf * (*cLast))
|
||||
delete temp;
|
||||
c->applyTransform(transform::Tanh, h); // h = tanh(c)
|
||||
}
|
||||
|
||||
// if clipping value is provided then cell state is clipped by this value prior to the cell output activation
|
||||
if (clippingCellValue > 0.0) c->applyScalar(scalar::LstmClip, clippingCellValue, c);
|
||||
|
||||
// add peephole connections to output gate zot + ct*Wc
|
||||
if (peephole) {
|
||||
NDArray *prod = (*c) * (*Wco);
|
||||
*zo += (*prod);
|
||||
delete prod;
|
||||
}
|
||||
|
||||
zo->applyTransform(transform::Sigmoid, o); // o = sigmoid(zo)
|
||||
|
||||
// current cell output = ot*tanh(ct)
|
||||
c->applyTransform(transform::Tanh, h); // h = tanh(c)
|
||||
o->applyPairwiseTransform(pairwise::Multiply, h, y); // y = o * h
|
||||
|
||||
delete m;
|
||||
delete zi;
|
||||
delete zz;
|
||||
delete zf;
|
||||
delete zo;
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 GS <sgazeos@gmail.com>
|
||||
//
|
||||
#include <array/NDArray.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/MmulHelper.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/lstsq.h>
|
||||
#include <ops/declarable/helpers/lup.h>
|
||||
#include <ops/declarable/helpers/qr.h>
|
||||
#include <ops/declarable/helpers/triangular_solve.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
#if NOT_EXCLUDED(OP_lstsq)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void fillRegularizer(NDArray* ioMatrix, double const value) {
|
||||
auto lastDims = ioMatrix->allTensorsAlongDimension({-2, -1});
|
||||
auto rows = ioMatrix->sizeAt(-2);
|
||||
|
||||
for (auto x = 0; x < lastDims.size(); x++) {
|
||||
for (auto r = 0; r < rows; r++) {
|
||||
lastDims[x]->r<T>(r, r) = (T)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sd::Status leastSquaresSolveFunctor_(sd::LaunchContext* context, NDArray* leftInput, NDArray* rightInput,
|
||||
double const l2Regularizer, bool const fast, NDArray* output) {
|
||||
NDArray::preparePrimaryUse({output}, {leftInput, rightInput});
|
||||
if (fast) { // Cholesky decomposition approach
|
||||
// Equation for solve A^T * Ax = A^T * b, so
|
||||
// 1. Computing A2:
|
||||
auto tAtShape = ShapeUtils::evalShapeForMatmul(leftInput->shapeInfo(), leftInput->shapeInfo(), true, false);
|
||||
// tAtShape[tAtShape.size() - 2] = output->sizeAt(-2);
|
||||
NDArray leftOutput('c', tAtShape, output->dataType(), context);
|
||||
MmulHelper::matmul(leftInput, leftInput, &leftOutput, true, false, 0, 0, &leftOutput); // Computing A2 = A^T * A
|
||||
// 2. Computing B' = A^T * b
|
||||
auto rightOutput = output->ulike();
|
||||
|
||||
MmulHelper::matmul(leftInput, rightInput, rightOutput, true, false, 0, 0, rightOutput); // Computing B' = A^T * b
|
||||
// 3. due l2Regularizer = 0, skip regularization ( indeed A' = A2 - l2Regularizer * I)
|
||||
auto regularizer = leftOutput.ulike();
|
||||
fillRegularizer<T>(regularizer, l2Regularizer);
|
||||
leftOutput += *regularizer;
|
||||
// 4. Cholesky decomposition -- output matrix is square and lower triangular
|
||||
// auto leftOutputT = leftOutput.ulike();
|
||||
auto status = helpers::cholesky(context, &leftOutput, &leftOutput, true); // inplace decomposition
|
||||
if (status != sd::Status::OK) return status;
|
||||
// alternate moment: inverse lower triangular matrix to solve equation A'x = b' => L^Tx = L^-1 * b'
|
||||
// solve one upper triangular system (to avoid float problems)
|
||||
|
||||
// 5. Solve two triangular systems:
|
||||
auto rightB = rightOutput->ulike();
|
||||
helpers::triangularSolveFunctor(context, &leftOutput, rightOutput, true, false, rightB);
|
||||
helpers::adjointMatrix(context, &leftOutput, true, &leftOutput);
|
||||
helpers::triangularSolveFunctor(context, &leftOutput, rightB, false, false, output);
|
||||
// All done
|
||||
} else { // QR decomposition approach
|
||||
// Equation for solve Rx = Q^T * b, where A = Q * R, where Q - orthogonal matrix, and R - upper triangular
|
||||
// 1. QR decomposition
|
||||
std::vector<sd::LongType> *qShapePtr = leftInput->getShapeAsVector();
|
||||
std::vector<sd::LongType> qShape = *qShapePtr;
|
||||
delete qShapePtr;
|
||||
|
||||
std::vector<sd::LongType> *rShapePtr = leftInput->getShapeAsVector();
|
||||
std::vector<sd::LongType> rShape = *rShapePtr;
|
||||
delete rShapePtr;
|
||||
|
||||
qShape[leftInput->rankOf() - 1] = leftInput->sizeAt(-2);
|
||||
|
||||
NDArray Q(leftInput->ordering(), qShape, leftInput->dataType(), context); // = leftInput->ulike();
|
||||
NDArray R(leftInput->ordering(), rShape, leftInput->dataType(), context); // = rightInput->ulike();
|
||||
helpers::qr(context, leftInput, &Q, &R, true);
|
||||
// 2. b` = Q^t * b:
|
||||
auto rightOutput = rightInput->ulike();
|
||||
MmulHelper::matmul(&Q, rightInput, rightOutput, true, false, 0, 0, rightOutput);
|
||||
// 3. Solve triangular system
|
||||
helpers::triangularSolveFunctor(context, &R, rightOutput, false, false, output);
|
||||
}
|
||||
NDArray::registerPrimaryUse({output}, {leftInput, rightInput});
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status leastSquaresSolveFunctor(sd::LaunchContext* context, NDArray* leftInput, NDArray* rightInput,
|
||||
double const l2Regularizer, bool const fast, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(leftInput->dataType(), return leastSquaresSolveFunctor_,
|
||||
(context, leftInput, rightInput, l2Regularizer, fast, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,634 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/MmulHelper.h>
|
||||
#include <ops/declarable/helpers/top_k.h>
|
||||
#if NOT_EXCLUDED(OP_lup)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void swapRows_(NDArray* matrix, sd::LongType theFirst, sd::LongType theSecond) {
|
||||
if (theFirst != theSecond)
|
||||
for (sd::LongType i = 0; i < matrix->columns(); i++) {
|
||||
math::sd_swap(matrix->r<T>(theFirst, i), matrix->r<T>(theSecond, i));
|
||||
}
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void swapRows_, (NDArray * matrix, sd::LongType theFirst, sd::LongType theSecond), SD_FLOAT_TYPES);
|
||||
|
||||
template <typename T>
|
||||
static void swapRows(T* matrixBuf, sd::LongType const* matrixShape, sd::LongType theFirst, sd::LongType theSecond) {
|
||||
if (theFirst != theSecond) {
|
||||
auto n = shape::sizeAt(matrixShape, static_cast<sd::LongType>(-1));
|
||||
|
||||
auto loop = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
sd::LongType theFirstPos[] = {theFirst, i};
|
||||
sd::LongType theSecondPos[] = {theSecond, i};
|
||||
|
||||
sd::LongType theFirstIndex;
|
||||
COORDS2INDEX(shape::rank(matrixShape), shape::stride(matrixShape), theFirstPos, theFirstIndex);
|
||||
|
||||
sd::LongType theSecondIndex;
|
||||
COORDS2INDEX(shape::rank(matrixShape), shape::stride(matrixShape), theSecondPos, theSecondIndex);
|
||||
|
||||
math::sd_swap(matrixBuf[theFirstIndex], matrixBuf[theSecondIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(loop, 0, n, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void swapRows(NDArray* matrix, sd::LongType theFirst, sd::LongType theSecond) {
|
||||
BUILD_SINGLE_SELECTOR(matrix->dataType(), swapRows_, (matrix, theFirst, theSecond), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void invertLowerMatrix_(NDArray* inputMatrix, NDArray* invertedMatrix) {
|
||||
sd::LongType n = inputMatrix->rows();
|
||||
invertedMatrix->setIdentity();
|
||||
|
||||
if (inputMatrix->isIdentityMatrix()) return;
|
||||
|
||||
auto invertDiagonals = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType i = start; i < stop; i += increment) invertedMatrix->r<T>(i, i) /= inputMatrix->t<T>(i, i);
|
||||
};
|
||||
|
||||
auto invertSubDiagonals = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType i = start; i < stop; i += increment)
|
||||
invertedMatrix->r<T>(i, i - 1) -=
|
||||
(inputMatrix->t<T>(i, i - 1) * invertedMatrix->t<T>(i - 1, i - 1) / inputMatrix->t<T>(i, i));
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(invertDiagonals, 0, n, 1);
|
||||
samediff::Threads::parallel_for(invertSubDiagonals, 1, n, 1);
|
||||
|
||||
for (sd::LongType i = 1; i < n; i++) {
|
||||
for (sd::LongType j = 0; j < i - 1; j++)
|
||||
for (sd::LongType k = 0; k < i; k++)
|
||||
invertedMatrix->r<T>(i, j) -=
|
||||
((invertedMatrix->t<T>(k, j) * inputMatrix->t<T>(i, k) / inputMatrix->t<T>(i, i)));
|
||||
}
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void invertLowerMatrix_, (NDArray * inputMatrix, NDArray* invertedMatrix);
|
||||
, SD_FLOAT_TYPES);
|
||||
|
||||
void invertLowerMatrix(NDArray* inputMatrix, NDArray* invertedMatrix) {
|
||||
BUILD_SINGLE_SELECTOR(inputMatrix->dataType(), invertLowerMatrix_, (inputMatrix, invertedMatrix), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void _invertUpperMatrix(NDArray* inputMatrix, NDArray* invertedMatrix) {
|
||||
sd::LongType n = inputMatrix->rows();
|
||||
invertedMatrix->setIdentity();
|
||||
|
||||
if (inputMatrix->isIdentityMatrix()) { // the inverse for I is I
|
||||
return;
|
||||
}
|
||||
|
||||
auto invertDiagonals = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment) invertedMatrix->r<T>(i, i) /= inputMatrix->t<T>(i, i);
|
||||
};
|
||||
|
||||
// PRAGMA_OMP_PARALLEL_FOR_IF(n > Environment::getInstance().elementwiseThreshold())
|
||||
auto invertUpDiagonals = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i += increment)
|
||||
invertedMatrix->r<T>(i, i + 1) -=
|
||||
(inputMatrix->t<T>(i, i + 1) * invertedMatrix->t<T>(i + 1, i + 1) / inputMatrix->t<T>(i, i));
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(invertDiagonals, 0, n, 1);
|
||||
samediff::Threads::parallel_for(invertUpDiagonals, 0, n - 1, 1);
|
||||
|
||||
for (auto i = n - 2; i >= 0; i--) {
|
||||
for (auto j = i + 2; j < n; j++)
|
||||
for (auto k = i; k < n; k++)
|
||||
invertedMatrix->r<T>(i, j) -=
|
||||
((invertedMatrix->t<T>(k, j) * inputMatrix->t<T>(i, k) / inputMatrix->t<T>(i, i)));
|
||||
}
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _invertUpperMatrix, (NDArray * inputMatrix, NDArray* invertedMatrix);
|
||||
, SD_FLOAT_TYPES);
|
||||
|
||||
void invertUpperMatrix(NDArray* inputMatrix, NDArray* invertedMatrix) {
|
||||
BUILD_SINGLE_SELECTOR(inputMatrix->dataType(), _invertUpperMatrix, (inputMatrix, invertedMatrix), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static NDArray lup_(LaunchContext* context, NDArray* input, NDArray* compound, NDArray* permutation) {
|
||||
const sd::LongType rowNum = input->rows();
|
||||
const sd::LongType columnNum = input->columns();
|
||||
|
||||
// FIXED: Use stack allocation instead of heap to avoid memory leak
|
||||
NDArray determinant(DataTypeUtils::fromT<T>(), context, true); // scalar initialized to 0
|
||||
determinant.p<T>(0, static_cast<T>(1.f)); // set value to 1
|
||||
NDArray compoundMatrix = *input; // copy
|
||||
NDArray permutationMatrix(input, false, context); // has same shape as input and contiguous strides
|
||||
permutationMatrix.setIdentity();
|
||||
|
||||
T pivotValue; // = T(0.0);
|
||||
sd::LongType pivot; // = -1;
|
||||
sd::LongType swapCount = 0;
|
||||
|
||||
for (sd::LongType i = 0; i < rowNum; i++) {
|
||||
pivotValue = T(0.0);
|
||||
pivot = -1;
|
||||
for (sd::LongType rowCounter = i; rowCounter < rowNum; rowCounter++) {
|
||||
if (sd::math::sd_abs<T,T>(compoundMatrix.t<T>(rowCounter, i)) > pivotValue) {
|
||||
pivotValue = sd::math::sd_abs<T,T>(compoundMatrix.t<T>(rowCounter, i));
|
||||
pivot = rowCounter;
|
||||
}
|
||||
}
|
||||
|
||||
if (pivotValue > DataTypeUtils::min_positive<T>()) {
|
||||
swapRows(&compoundMatrix, pivot, i);
|
||||
swapRows(&permutationMatrix, pivot, i);
|
||||
if (pivot != i) swapCount++;
|
||||
|
||||
for (sd::LongType j = i + 1; j < rowNum; j++) {
|
||||
compoundMatrix.r<T>(j, i) /= compoundMatrix.t<T>(i, i);
|
||||
for (sd::LongType k = i + 1; k < rowNum; k++) {
|
||||
compoundMatrix.r<T>(j, k) -= compoundMatrix.t<T>(j, i) * compoundMatrix.t<T>(i, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (sd::LongType e = 0; e < rowNum; e++) {
|
||||
determinant.p<T>(0, determinant.e<T>(0) * compoundMatrix.e<T>(e, e));
|
||||
}
|
||||
if (swapCount % 2) {
|
||||
determinant.p<T>(0, -determinant.e<T>(0));
|
||||
}
|
||||
if (compound != nullptr) compound->assign(&compoundMatrix);
|
||||
if (permutation != nullptr) {
|
||||
auto permutaionVector = NDArrayFactory::create('c', {rowNum}, DataTypeUtils::fromT<I>(), input->getContext());
|
||||
for (auto i = 0; i < rowNum; i++) {
|
||||
for (auto j = 0; j < columnNum; j++) {
|
||||
if (permutationMatrix.t<T>(i, j) != 0) {
|
||||
permutaionVector->template r<I>(i) = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (permutationMatrix.isSameShape(permutation))
|
||||
permutation->assign(&permutationMatrix);
|
||||
else if (permutation->isSameShape(permutaionVector)) {
|
||||
permutation->assign(permutaionVector);
|
||||
}
|
||||
}
|
||||
return determinant; // FIXED: Return stack-allocated object instead of dereferencing pointer
|
||||
}
|
||||
|
||||
BUILD_DOUBLE_TEMPLATE( NDArray lup_,
|
||||
(LaunchContext * context, NDArray* input, NDArray* output, NDArray* permutation), SD_FLOAT_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
/*
|
||||
* lu decomposition with naive algorithm with partial pivoting
|
||||
* */
|
||||
template <typename T, typename I>
|
||||
static I argmaxCol(I column, T* compoundBuffer, sd::LongType const* compoundShape) {
|
||||
auto rowNum = shape::sizeAt(compoundShape, static_cast<sd::LongType>(0));
|
||||
sd::LongType xInitial[] = {column, column};
|
||||
sd::LongType xInitialIndex;
|
||||
COORDS2INDEX(shape::rank(compoundShape), shape::stride(compoundShape), xInitial, xInitialIndex);
|
||||
auto maxValue = T(0);
|
||||
auto result = -1;
|
||||
auto start = column;
|
||||
auto stop = rowNum;
|
||||
auto increment = 1;
|
||||
for (auto rowCounter = start; rowCounter < stop; rowCounter++) {
|
||||
sd::LongType xPos[] = {rowCounter, column};
|
||||
sd::LongType xIndex;
|
||||
COORDS2INDEX(shape::rank(compoundShape), shape::stride(compoundShape), xPos, xIndex);
|
||||
if (sd::math::sd_abs<T,T>(compoundBuffer[xIndex]) > maxValue) {
|
||||
maxValue = sd::math::sd_max(maxValue, sd::math::sd_abs<T,T>(compoundBuffer[xIndex]));
|
||||
result = rowCounter;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void processColumns(sd::LongType currentRow, sd::LongType rowNum, T* compoundBuf, sd::LongType const* compoundShape) {
|
||||
sd::LongType xDiag[] = {currentRow, currentRow};
|
||||
sd::LongType diagIndex;
|
||||
COORDS2INDEX(shape::rank(compoundShape), shape::stride(compoundShape), xDiag, diagIndex);
|
||||
auto loop = PRAGMA_THREADS_FOR {
|
||||
for (auto j = start; j < stop; j++) {
|
||||
sd::LongType xRow[] = {j, currentRow};
|
||||
sd::LongType rowIndex;
|
||||
COORDS2INDEX(shape::rank(compoundShape), shape::stride(compoundShape), xRow, rowIndex);
|
||||
compoundBuf[rowIndex] /= compoundBuf[diagIndex]; // output->t<T>(i, i);
|
||||
for (sd::LongType k = currentRow + 1; k < rowNum; k++) {
|
||||
sd::LongType yRow[] = {j, k};
|
||||
sd::LongType yCol[] = {currentRow, k};
|
||||
sd::LongType rowIndexY, colIndex;
|
||||
COORDS2INDEX(shape::rank(compoundShape), shape::stride(compoundShape), yRow, rowIndexY);
|
||||
COORDS2INDEX(shape::rank(compoundShape), shape::stride(compoundShape), yCol, colIndex);
|
||||
compoundBuf[rowIndexY] -= compoundBuf[rowIndex] * compoundBuf[colIndex];
|
||||
}
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_tad(loop, currentRow + 1, rowNum, 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void doolitleLU(LaunchContext* context, NDArray* compound, sd::LongType rowNum) {
|
||||
auto input = compound->dup();
|
||||
compound->nullify();
|
||||
|
||||
// Decomposing matrix into Upper and Lower
|
||||
// triangular matrix
|
||||
for (auto i = 0; i < rowNum; i++) {
|
||||
// Upper Triangular
|
||||
for (auto k = i; k < rowNum; k++) {
|
||||
// Summation of L(i, j) * U(j, k)
|
||||
sd::LongType sum = 0;
|
||||
for (sd::LongType j = 0; j < i; j++) sum += compound->t<T>(i, j) * compound->t<T>(j, k);
|
||||
|
||||
// Evaluating U(i, k)
|
||||
compound->r<T>(i, k) = input->t<T>(i, k) - sum;
|
||||
}
|
||||
|
||||
// Lower Triangular
|
||||
for (sd::LongType k = i + 1; k < rowNum; k++) {
|
||||
// Summation of L(k, j) * U(j, i)
|
||||
sd::LongType sum = 0;
|
||||
for (sd::LongType j = 0; j < i; j++) sum += compound->t<T>(k, j) * compound->t<T>(j, i);
|
||||
|
||||
// Evaluating L(k, i)
|
||||
compound->r<T>(k, i) = (input->t<T>(k, i) - sum) / compound->t<T>(i, i);
|
||||
}
|
||||
}
|
||||
delete input; // Clean up duped array
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static void luNN_(LaunchContext* context, NDArray* compound, NDArray* permutation, sd::LongType rowNum) {
|
||||
if (permutation) { // LUP algorithm
|
||||
// Initialize permutation array
|
||||
permutation->linspace(0);
|
||||
|
||||
// Cache all buffers and shape data upfront
|
||||
auto permutationBuf = permutation->bufferAsT<I>();
|
||||
auto compoundBuf = compound->bufferAsT<T>();
|
||||
auto compoundShape = compound->shapeInfo();
|
||||
auto permutationShape = permutation->shapeInfo();
|
||||
|
||||
// Cache shape-related values outside the main loop
|
||||
const int permRank = shape::rank(permutationShape);
|
||||
const sd::LongType* permShape = shape::shapeOf(permutationShape);
|
||||
const sd::LongType* permStride = shape::stride(permutationShape);
|
||||
|
||||
// Main LU decomposition loop
|
||||
for (sd::LongType i = 0; i < rowNum - 1; i++) {
|
||||
auto pivotIndex = argmaxCol(i, compoundBuf, compoundShape);
|
||||
if (pivotIndex < 0) {
|
||||
THROW_EXCEPTION("helpers::luNN_: input matrix is singular.");
|
||||
}
|
||||
|
||||
// Use cached shape values for coordinate transforms
|
||||
sd::LongType firstIndexCoords[SD_MAX_RANK];
|
||||
sd::LongType secondIndexCoords[SD_MAX_RANK];
|
||||
sd::LongType firstIndex;
|
||||
sd::LongType secondIndex;
|
||||
|
||||
// Transform coordinates using cached shape data
|
||||
INDEX2COORDS(i, permRank, permShape, firstIndexCoords);
|
||||
COORDS2INDEX(permRank, permStride, firstIndexCoords, firstIndex);
|
||||
|
||||
INDEX2COORDS(pivotIndex, permRank, permShape, secondIndexCoords);
|
||||
COORDS2INDEX(permRank, permStride, secondIndexCoords, secondIndex);
|
||||
|
||||
// Perform the swaps
|
||||
math::sd_swap(permutationBuf[firstIndex], permutationBuf[secondIndex]);
|
||||
swapRows(compoundBuf, compoundShape, i, pivotIndex);
|
||||
|
||||
// Process remaining columns
|
||||
processColumns(i, rowNum, compoundBuf, compoundShape);
|
||||
}
|
||||
} else { // Doolitle algorithm with LU decomposition
|
||||
doolitleLU<T>(context, compound, rowNum);
|
||||
}
|
||||
}
|
||||
template <typename T, typename I>
|
||||
static void lu_(LaunchContext* context, NDArray* input, NDArray* output, NDArray* permutationVectors) {
|
||||
auto n = input->sizeAt(-1);
|
||||
|
||||
output->assign(input); // fill up output tensor with zeros
|
||||
ResultSet outputs = output->allTensorsAlongDimension({-2, -1});
|
||||
ResultSet permutations;
|
||||
if (permutationVectors) permutations = permutationVectors->allTensorsAlongDimension({-1});
|
||||
|
||||
auto loop = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
luNN_<T, I>(context, outputs.at(i), permutationVectors ? permutations.at(i) : nullptr, n);
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(loop, 0, outputs.size(), 1);
|
||||
}
|
||||
|
||||
void lu(LaunchContext* context, NDArray* input, NDArray* output, NDArray* permutation) {
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), permutation ? permutation->dataType() : DataType::INT32, lu_,
|
||||
(context, input, output, permutation), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
static sd::Status determinant_(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
sd::LongType n = input->sizeAt(-1);
|
||||
sd::LongType n2 = n * n;
|
||||
|
||||
auto matrix =
|
||||
NDArrayFactory::create(input->ordering(), {n, n}, input->dataType(), context); //, block.getWorkspace());
|
||||
|
||||
for (sd::LongType e = 0; e < output->lengthOf(); e++) {
|
||||
for (sd::LongType k = e * n2, row = 0; k < (e + 1) * n2; ++k, ++row) matrix->p(row, input->e<T>(k));
|
||||
auto ret = lup_<T, sd::LongType>(context, matrix, (NDArray*)nullptr, (NDArray*)nullptr);
|
||||
output->p(e, &ret);
|
||||
}
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status determinant(sd::LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return determinant_, (context, input, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sd::Status logAbsDeterminant_(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
sd::LongType n = input->sizeAt(-1);
|
||||
sd::LongType n2 = n * n;
|
||||
|
||||
NDArray *matrix =
|
||||
NDArrayFactory::create(input->ordering(), {n, n}, input->dataType(), context); //, block.getWorkspace());
|
||||
for (sd::LongType e = 0; e < output->lengthOf(); e++) {
|
||||
for (sd::LongType k = e * n2, row = 0; k < (e + 1) * n2; ++k, ++row) {
|
||||
matrix->p(row, input->e<T>(k));
|
||||
}
|
||||
NDArray det = lup_<T, sd::LongType>(context, matrix, (NDArray*)nullptr, (NDArray*)nullptr);
|
||||
if (det.e<T>(0) != 0.f) output->p(e, sd::math::sd_log<T, T>(sd::math::sd_abs<T,T>(det.t<T>(0))));
|
||||
}
|
||||
|
||||
delete matrix;
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status logAbsDeterminant(sd::LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return logAbsDeterminant_, (context, input, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static sd::Status inverse_(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
auto n = input->sizeAt(-1);
|
||||
auto n2 = n * n;
|
||||
auto totalCount = output->lengthOf() / n2;
|
||||
float zerof = 0.f;
|
||||
output->assign(zerof); // fill up output tensor with zeros
|
||||
auto matrix = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
auto compound = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
auto permutation = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
auto lowerMatrix = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
auto upperMatrix = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
float zero = 0.f;
|
||||
for (sd::LongType e = 0; e < totalCount; e++) {
|
||||
if (e) matrix->assign(zero);
|
||||
|
||||
for (sd::LongType k = e * n2, row = 0; k < (e + 1) * n2; k++) {
|
||||
matrix->p(row++, input->e<T>(k));
|
||||
}
|
||||
T det = lup_<T, sd::LongType>(context, matrix, compound, permutation).template e<T>(0);
|
||||
|
||||
// FIXME: and how this is going to work on float16?
|
||||
if (sd::math::sd_abs<T,T>(det) < T(0.000001)) {
|
||||
sd_printf("matrix_inverse: The matrix %i has no inverse due determinant is %lf. Quiting...\n", e, det);
|
||||
return sd::Status::VALIDATION;
|
||||
}
|
||||
lowerMatrix->setIdentity(); // set up U to identity matrix
|
||||
for (sd::LongType k = 1; k < n; k++) { // and then put all values under main diagonal on to it
|
||||
for (sd::LongType j = 0; j < k; j++) lowerMatrix->template r<T>(k, j) = compound->template t<T>(k, j);
|
||||
}
|
||||
upperMatrix->setIdentity(); // set up U to identity matrix
|
||||
for (sd::LongType k = 0; k < n; k++) { // and then put all values under main diagonal on to it
|
||||
for (sd::LongType j = k; j < n; j++) upperMatrix->template r<T>(k, j) = compound->template t<T>(k, j);
|
||||
}
|
||||
invertUpperMatrix(upperMatrix, matrix);
|
||||
|
||||
invertLowerMatrix(lowerMatrix, upperMatrix);
|
||||
|
||||
sd::MmulHelper::mmul(matrix, upperMatrix, compound, 1.0, 0.0);
|
||||
sd::MmulHelper::mmul(compound, permutation, matrix, 1.0, 0.0);
|
||||
for (sd::LongType k = e * n2, row = 0; k < (e + 1) * n2; k++) {
|
||||
output->r<T>(k) = matrix->template t<T>(row++);
|
||||
}
|
||||
}
|
||||
|
||||
delete matrix;
|
||||
delete compound;
|
||||
delete upperMatrix;
|
||||
delete lowerMatrix;
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static sd::Status lowerInverse_(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
auto n = input->sizeAt(-1);
|
||||
auto n2 = n * n;
|
||||
auto totalCount = output->lengthOf() / n2;
|
||||
float zero = 0.f;
|
||||
output->assign(zero); // fill up output tensor with zeros
|
||||
auto matrix = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
auto compound = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
auto permutation = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
auto lowerMatrix = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
auto upperMatrix = NDArrayFactory::create('c', {n, n}, DataTypeUtils::fromT<T>(), context);
|
||||
for (sd::LongType e = 0; e < totalCount; e++) {
|
||||
if (e) matrix->assign(zero);
|
||||
|
||||
for (sd::LongType k = e * n2, row = 0; k < (e + 1) * n2; k++) {
|
||||
matrix->p(row++, input->e<T>(k));
|
||||
}
|
||||
T det = T(1.f);
|
||||
for (auto i = 0; i < n; i++) {
|
||||
det *= matrix->template t<T>(i, i);
|
||||
}
|
||||
|
||||
// FIXME: a->d how this is going to work on float16?
|
||||
if (sd::math::sd_abs<T,T>(det) < T(0.000001)) {
|
||||
sd_printf("matrix_inverse: The matrix %i has no inverse due determinant is %lf. Quitting...\n", e, det);
|
||||
return sd::Status::VALIDATION;
|
||||
}
|
||||
lowerMatrix->nullify();
|
||||
invertLowerMatrix(matrix, lowerMatrix);
|
||||
|
||||
for (sd::LongType k = e * n2, row = 0; k < (e + 1) * n2; k++) {
|
||||
output->r<T>(k) = lowerMatrix->template t<T>(row++);
|
||||
}
|
||||
}
|
||||
|
||||
delete matrix;
|
||||
delete lowerMatrix;
|
||||
delete compound;
|
||||
delete permutation;
|
||||
delete upperMatrix;
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static sd::Status upperInverse_(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
auto n = input->sizeAt(-1);
|
||||
auto n2 = n * n;
|
||||
|
||||
output->nullify(); // fill up output tensor with zeros
|
||||
auto inputPart = input->allTensorsAlongDimension({-2, -1});
|
||||
auto outputPart = output->allTensorsAlongDimension({-2, -1});
|
||||
auto totalCount = outputPart.size();
|
||||
for (sd::LongType e = 0; e < totalCount; e++) {
|
||||
invertUpperMatrix(inputPart.at(e), outputPart.at(e));
|
||||
}
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status inverse(sd::LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return inverse_, (context, input, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
sd::Status lowerInverseFunctor(sd::LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return lowerInverse_, (context, input, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
sd::Status upperInverseFunctor(sd::LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return upperInverse_, (context, input, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool checkCholeskyInput_(sd::LaunchContext* context, NDArray * input) {
|
||||
ResultSet lastMatrixList = input->allTensorsAlongDimension({input->rankOf() - 2, input->rankOf() - 1});
|
||||
for (sd::LongType i = 0; i < lastMatrixList.size(); i++) {
|
||||
auto thisMatrix = lastMatrixList.at(i);
|
||||
// check for symmetric
|
||||
for (sd::LongType r = 0; r < thisMatrix->rows(); r++)
|
||||
for (sd::LongType c = 0; c < thisMatrix->columns(); c++)
|
||||
if (sd::math::sd_abs<T,T>(thisMatrix->e<T>(r, c) - lastMatrixList.at(i)->e<T>(c, r)) >
|
||||
DataTypeUtils::min_positive<T>())
|
||||
return false;
|
||||
|
||||
NDArray *output = NDArrayFactory::create<T>(static_cast<T>(0.), context);
|
||||
if (sd::Status::OK != determinant(context, thisMatrix, output)) return false;
|
||||
if (output->e<T>(0) <= T(0)) return 0;
|
||||
NDArray reversedMatrix(*thisMatrix);
|
||||
if (sd::Status::OK != inverse(context, thisMatrix, &reversedMatrix)) return false;
|
||||
if (sd::Status::OK != determinant(context, &reversedMatrix, output)) return false;
|
||||
if (output->e<T>(0) <= T(0)) return 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool checkCholeskyInput(sd::LaunchContext* context, NDArray * input) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return checkCholeskyInput_, (context, input), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sd::Status cholesky_(LaunchContext* context, NDArray* input, NDArray* output, bool inplace) {
|
||||
auto n = input->sizeAt(-1);
|
||||
auto n2 = n * n;
|
||||
auto totalCount = output->lengthOf() / n2;
|
||||
float zero = 0.f;
|
||||
if (!inplace) output->assign(zero); // fill up output tensor with zeros only inplace=false
|
||||
|
||||
std::vector<sd::LongType> shape = {n,n};
|
||||
std::unique_ptr<NDArray> matrix(
|
||||
NDArrayFactory::create_('c', shape, input->dataType(), context)); //, block.getWorkspace());
|
||||
std::unique_ptr<NDArray> lowerMatrix(NDArrayFactory::create_('c',shape, input->dataType(), context));
|
||||
|
||||
for (sd::LongType e = 0; e < totalCount; e++) {
|
||||
// fill up matrix
|
||||
for (sd::LongType k = e * n2, l = 0; k < (e + 1) * n2; k++) {
|
||||
matrix->p(l++, input->e<T>(k));
|
||||
}
|
||||
// if (e) // from the second loop need to zero matrix
|
||||
lowerMatrix->assign(zero);
|
||||
|
||||
for (sd::LongType col = 0; col < n; col++) {
|
||||
for (sd::LongType row = 0; row < col; row++) {
|
||||
T rowSum = static_cast<T>(0);
|
||||
for (sd::LongType k = 0; k < row; ++k) rowSum += (lowerMatrix->e<T>(col, k) * lowerMatrix->e<T>(row, k));
|
||||
lowerMatrix->p(col, row, (matrix->e<T>(row, col) - rowSum) / lowerMatrix->e<T>(row, row));
|
||||
}
|
||||
T diagonalSum = static_cast<T>(0);
|
||||
for (sd::LongType k = 0; k < col; ++k) diagonalSum += lowerMatrix->e<T>(col, k) * lowerMatrix->e<T>(col, k);
|
||||
lowerMatrix->p(col, col, sd::math::sd_sqrt<T, T>(matrix->e<T>(col, col) - diagonalSum));
|
||||
}
|
||||
for (sd::LongType k = e * n2, l = 0; k < (e + 1) * n2; k++) {
|
||||
output->p(k, lowerMatrix->e<T>(l++));
|
||||
}
|
||||
}
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status cholesky(sd::LaunchContext* context, NDArray* input, NDArray* output, bool inplace) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return cholesky_, (context, input, output, inplace), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sd::Status logdetFunctor_(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
auto tempOutput = input->dup();
|
||||
auto res = cholesky_<T>(context, input, tempOutput, false);
|
||||
if (res != sd::Status::OK) return res;
|
||||
auto n = input->sizeAt(-1);
|
||||
auto totalCount = output->lengthOf();
|
||||
std::vector<T> d(n);
|
||||
ResultSet matrices = tempOutput->allTensorsAlongDimension({input->rankOf() - 2, input->rankOf() - 1});
|
||||
|
||||
for (sd::LongType e = 0; e < totalCount; e++) {
|
||||
for (sd::LongType i = 0; i < n; ++i)
|
||||
output->r<T>(e) += sd::math::sd_log<T, T>(sd::math::sd_pow<T, T, T>(matrices.at(e)->t<T>(i, i), T(2)));
|
||||
}
|
||||
delete tempOutput; // Clean up duped array
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status logdetFunctor(sd::LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return logdetFunctor_, (context, input, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
sd::Status lup(sd::LaunchContext* context, NDArray* input, NDArray* compound, NDArray* permutation) {
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), permutation->dataType(), lup_, (context, input, compound, permutation),
|
||||
SD_FLOAT_NATIVE, SD_INDEXING_TYPES);
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <array/ResultSet.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/matrixSetDiag.h>
|
||||
#if NOT_EXCLUDED(OP_matrix_set_diag)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void matrixSetDiag_(NDArray& input, NDArray& diagonal, NDArray& output, const bool zeroPad) {
|
||||
// input and output are the same array (x == z) when zeroPad = true
|
||||
// xRank = zRank, xRank = yRank + 1
|
||||
// xLen = zLen
|
||||
|
||||
const T* x = input.bufferAsT<T>();
|
||||
const T* y = diagonal.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
|
||||
// Cache all shape information upfront
|
||||
const sd::LongType* xShapeInfo = input.shapeInfo();
|
||||
const sd::LongType* yShapeInfo = diagonal.shapeInfo();
|
||||
const sd::LongType* zShapeInfo = output.shapeInfo();
|
||||
|
||||
// Cache shape-related values
|
||||
const int xRank = input.rankOf();
|
||||
const auto xLen = input.lengthOf();
|
||||
|
||||
// Cache shape and stride pointers
|
||||
const sd::LongType* xShape = shape::shapeOf(xShapeInfo);
|
||||
const sd::LongType* xStride = shape::stride(xShapeInfo);
|
||||
const sd::LongType* yStride = shape::stride(yShapeInfo);
|
||||
const sd::LongType* zStride = shape::stride(zShapeInfo);
|
||||
|
||||
// Check if input and output have same offsets
|
||||
const bool areSameOffsets = shape::haveSameShapeAndStrides(xShapeInfo, zShapeInfo);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
// Pre-allocate coords array outside the loop
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
|
||||
for (sd::LongType i = 0; i < xLen; ++i) {
|
||||
// Use cached shape data for coordinate transforms
|
||||
INDEX2COORDS(i, xRank, xShape, coords);
|
||||
|
||||
sd::LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStride, coords, xOffset);
|
||||
|
||||
sd::LongType zOffset;
|
||||
if (areSameOffsets) {
|
||||
zOffset = xOffset;
|
||||
} else {
|
||||
COORDS2INDEX(xRank, zStride, coords, zOffset);
|
||||
}
|
||||
|
||||
// Check diagonal condition using cached rank
|
||||
if (coords[xRank - 2] == coords[xRank - 1]) {
|
||||
sd::LongType yOffset;
|
||||
COORDS2INDEX(xRank - 1, yStride, coords, yOffset);
|
||||
z[zOffset] = y[yOffset];
|
||||
} else {
|
||||
z[zOffset] = zeroPad ? static_cast<T>(0) : x[xOffset];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, xLen);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void matrixSetDiag(sd::LaunchContext* context, NDArray& input, NDArray& diagonal, NDArray& output,
|
||||
const bool zeroPad) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), matrixSetDiag_, (input, diagonal, output, zeroPad), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 George A. Shulinok <sgazeos@gmail.com>
|
||||
//
|
||||
#include <ops/declarable/helpers/matrix_band.h>
|
||||
#if NOT_EXCLUDED(OP_matrix_band)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
void matrixBandPart_(NDArray* input, NDArray* output, sd::LongType lowerBand, sd::LongType upperBand) {
|
||||
// TO DO: retrieve all 2D submatrices with last dimensions and process them with given bands
|
||||
sd::LongType M = input->sizeAt(-2);
|
||||
sd::LongType N = input->sizeAt(-1);
|
||||
sd::LongType lastDim = input->rankOf() - 1;
|
||||
sd::LongType preLastDim = input->rankOf() - 2;
|
||||
ResultSet listOut = output->allTensorsAlongDimension({preLastDim, lastDim});
|
||||
ResultSet listDiag = input->allTensorsAlongDimension({preLastDim, lastDim});
|
||||
for (sd::LongType e = 0; e < static_cast<sd::LongType>(listOut.size()); ++e) {
|
||||
NDArray* inputMatrix = listDiag.at(e);
|
||||
NDArray* outputMatrix = listOut.at(e);
|
||||
if (outputMatrix != inputMatrix) // if not inplace
|
||||
outputMatrix->assign(inputMatrix);
|
||||
if (lowerBand >= 0) {
|
||||
for (sd::LongType row = 0; row < inputMatrix->rows(); ++row) {
|
||||
for (sd::LongType col = 0; col < row; ++col) {
|
||||
if ((row - col) > lowerBand) outputMatrix->p(row, col, 0.);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (upperBand >= 0) {
|
||||
for (sd::LongType col = 0; col < inputMatrix->columns(); ++col) {
|
||||
for (sd::LongType row = 0; row < col; ++row) {
|
||||
if ((col - row) > upperBand) outputMatrix->p(row, col, 0.);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void matrixBandPart(sd::LaunchContext* context, NDArray* input, NDArray* output, sd::LongType lowerBand,
|
||||
sd::LongType upperBand) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), matrixBandPart_, (input, output, lowerBand, upperBand), SD_FLOAT_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void matrixBandPart_,
|
||||
(NDArray * input, NDArray* output, sd::LongType lowerBand, sd::LongType upperBand),
|
||||
SD_FLOAT_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by GS <sgazeos@gmail.com> on 3/21/2018.
|
||||
//
|
||||
#include <array/ResultSet.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/matrix_diag_part.h>
|
||||
#if NOT_EXCLUDED(OP_matrix_diag_part)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Returns a batched matrix tensor with new batched diagonal values.
|
||||
// for detailed explanations please take a look on web page:
|
||||
// https://www.tensorflow.org/api_docs/python/tf/matrix_set_diag
|
||||
template <typename T>
|
||||
static sd::Status _matrixDiagPart(NDArray* input, NDArray* output) {
|
||||
auto listOut = output->allTensorsAlongDimension({output->rankOf() - 1});
|
||||
auto listDiag = input->allTensorsAlongDimension({input->rankOf() - 2, input->rankOf() - 1});
|
||||
|
||||
if (listOut.size() != listDiag.size()) {
|
||||
sd_printf("matrix_diag_part: Input matrix has wrong shape.", "");
|
||||
return sd::Status::VALIDATION;
|
||||
}
|
||||
sd::LongType lastDimension = sd::math::sd_min(input->sizeAt(-2), input->sizeAt(-1));
|
||||
// TODO: tune this properly
|
||||
sd::LongType lO = listOut.size();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType i = start; i < stop; i++)
|
||||
for (sd::LongType j = 0; j < lastDimension; ++j) listOut.at(i)->p(j, listDiag.at(i)->e<T>(j, j));
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, lO);
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status matrixDiagPart(sd::LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return _matrixDiagPart, (input, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status _matrixDiagPart, (NDArray* input, NDArray* output), SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <ops/declarable/helpers/max_pooling.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void maxPoolingFunctor_(sd::graph::Context& block, NDArray* input, NDArray* values,
|
||||
const std::vector<LongType>& params, NDArray* indices) {
|
||||
LongType kY = params[0];
|
||||
LongType kX = params[1];
|
||||
|
||||
LongType sY = params[2];
|
||||
LongType sX = params[3];
|
||||
|
||||
sd::LongType pY = params[4];
|
||||
sd::LongType pX = params[5];
|
||||
|
||||
LongType dY = params[6];
|
||||
LongType dX = params[7];
|
||||
|
||||
LongType oY = 0;
|
||||
LongType oX = 0;
|
||||
|
||||
const LongType bSize = input->sizeAt(0);
|
||||
const LongType inD = input->sizeAt(1);
|
||||
const LongType inY = input->sizeAt(2);
|
||||
const LongType inX = input->sizeAt(3);
|
||||
|
||||
const bool isSameMode = params[8] != 0;
|
||||
|
||||
ConvolutionUtils::calcOutSizePool2D(oY, oX, kY, kX, sY, sX, pY, pX, dY, dX, inY, inX, isSameMode);
|
||||
|
||||
if (isSameMode)
|
||||
ConvolutionUtils::calcPadding2D(pY, pX, oY, oX, inY, inX, params[0], params[1], params[2], params[3], params[6],
|
||||
params[7]);
|
||||
|
||||
// 0,1 - kernel Height/Width; 2,3 - stride Height/Width; 4,5 - pad Height/Width; 6,7 - dilation Height/Width; 8 -
|
||||
// poolingMode; 9 - divisor;
|
||||
ConvolutionUtils::pooling2d(block, *input, *values, kY, kX, sY, sX, pY, pX, dY, dX, PoolingType::MAX_POOL, 1);
|
||||
|
||||
if (nullptr != indices) {
|
||||
// for max_pool_with_argmax
|
||||
int total = input->lengthOf();
|
||||
int part = total / bSize;
|
||||
|
||||
for (int k = 0; k < total;)
|
||||
for (int i = 0; i < part; i++) {
|
||||
indices->p(k++, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void maxPoolingFunctor(sd::LaunchContext* context, sd::graph::Context& block, NDArray* input, NDArray* values,
|
||||
const std::vector<LongType>& params, NDArray* indices) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), maxPoolingFunctor_, (block, input, values, params, indices),
|
||||
SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
|
||||
// @author Oleh Semeniv (oleg.semeniv@gmail.com)
|
||||
//
|
||||
#include <helpers/Loops.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
#if NOT_EXCLUDED(OP_merge)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static void mergeMaxIndex_(const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
const sd::LongType numArgs = inArrs.size();
|
||||
auto x = inArrs[0];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
X max = -DataTypeUtils::max<X>();
|
||||
Z idx = static_cast<Z>(0);
|
||||
|
||||
for (sd::LongType i = 0; i < numArgs; i++) {
|
||||
X v = inArrs[i]->t<X>(e);
|
||||
if (v > max) {
|
||||
max = v;
|
||||
idx = static_cast<Z>(i);
|
||||
}
|
||||
}
|
||||
|
||||
output.r<Z>(e) = static_cast<Z>(idx);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, x->lengthOf());
|
||||
}
|
||||
|
||||
void mergeMaxIndex(sd::LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
BUILD_DOUBLE_SELECTOR(inArrs[0]->dataType(), output.dataType(), mergeMaxIndex_, (inArrs, output), SD_NUMERIC_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void mergeMax_(const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
const sd::LongType numArgs = inArrs.size();
|
||||
auto x = inArrs[0];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
for (sd::LongType i = 0; i < numArgs; i++) {
|
||||
T v = inArrs[i]->e<T>(e);
|
||||
if (v > max) max = v;
|
||||
}
|
||||
output.p(e, max);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, x->lengthOf());
|
||||
}
|
||||
|
||||
void mergeMax(sd::LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
BUILD_SINGLE_SELECTOR(output.dataType(), mergeMax_, (inArrs, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void mergeMaxBp_(const std::vector<NDArray*>& inArrs, std::vector<NDArray*>& outArrs) {
|
||||
// outArrs.size() == inArrs.size() - 1
|
||||
const sd::LongType numArgs = outArrs.size();
|
||||
// last array is gradient
|
||||
const auto gradient = inArrs[numArgs]->bufferAsT<T>();
|
||||
auto length = inArrs[numArgs]->lengthOf();
|
||||
|
||||
auto gradShape = inArrs[numArgs]->shapeInfo();
|
||||
std::vector<bool> vbSameShaepeAndStrides(numArgs);
|
||||
std::vector<sd::LongType*> vShapePtrs(numArgs);
|
||||
std::vector<sd::LongType*> vStridePtrs(numArgs);
|
||||
std::vector<sd::LongType> vRanks(numArgs);
|
||||
for (int i = 0; i < numArgs; ++i) {
|
||||
vbSameShaepeAndStrides[i] = shape::haveSameShapeAndStrides(gradShape, inArrs[i]->shapeInfo());
|
||||
vShapePtrs[i] = shape::shapeOf(inArrs[i]->shapeInfo());
|
||||
vStridePtrs[i] = shape::stride(inArrs[i]->shapeInfo());
|
||||
vRanks[i] = shape::rank(inArrs[i]->shapeInfo());
|
||||
}
|
||||
|
||||
|
||||
std::vector<sd::LongType *> outShapePtrs(numArgs);
|
||||
std::vector<sd::LongType *> outStridePtrs(numArgs);
|
||||
std::vector<sd::LongType> outRanks(numArgs);
|
||||
for (int i = 0; i < numArgs; ++i) {
|
||||
outShapePtrs[i] = shape::shapeOf(outArrs[i]->shapeInfo());
|
||||
outStridePtrs[i] = shape::stride(outArrs[i]->shapeInfo());
|
||||
outRanks[i] = shape::rank(outArrs[i]->shapeInfo());
|
||||
}
|
||||
|
||||
sd::LongType gradRank = shape::rank(gradShape);
|
||||
sd::LongType *gradShapeOf = shape::shapeOf(gradShape);
|
||||
sd::LongType *gradStride = shape::stride(gradShape);
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
for (auto e = start; e < stop; e++) {
|
||||
INDEX2COORDS(e, gradRank, gradShapeOf, coords);
|
||||
|
||||
sd::LongType gradOffset;
|
||||
COORDS2INDEX(gradRank,gradStride, coords, gradOffset);
|
||||
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
sd::LongType nMaxIndex = 0;
|
||||
|
||||
for (sd::LongType i = 0; i < numArgs; i++) {
|
||||
sd::LongType xOffset;
|
||||
if (vbSameShaepeAndStrides[i]) {
|
||||
xOffset = gradOffset;
|
||||
} else {
|
||||
COORDS2INDEX(vRanks[i],vStridePtrs[i], coords, xOffset);
|
||||
}
|
||||
const T* v = inArrs[i]->bufferAsT<T>();
|
||||
if (v[xOffset] > max) {
|
||||
max = v[xOffset];
|
||||
nMaxIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
sd::LongType zOffset;
|
||||
if (vbSameShaepeAndStrides[nMaxIndex]) {
|
||||
zOffset = gradOffset;
|
||||
} else {
|
||||
COORDS2INDEX(outRanks[nMaxIndex],outStridePtrs[nMaxIndex], coords, zOffset);
|
||||
}
|
||||
|
||||
T* z = outArrs[nMaxIndex]->bufferAsT<T>();
|
||||
z[zOffset] = gradient[gradOffset];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, length);
|
||||
return;
|
||||
}
|
||||
|
||||
void mergeMaxBp(sd::LaunchContext* context, const std::vector<NDArray*>& inArrs, std::vector<NDArray*>& outArrs) {
|
||||
BUILD_SINGLE_SELECTOR(outArrs[0]->dataType(), mergeMaxBp_, (inArrs, outArrs), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void mergeAvg_(const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
const sd::LongType numArgs = inArrs.size();
|
||||
const T factor = static_cast<T>(1.f / numArgs);
|
||||
auto x = inArrs[0];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
T sum = static_cast<T>(0);
|
||||
for (sd::LongType i = 0; i < numArgs; i++) {
|
||||
T v = inArrs[i]->e<T>(e);
|
||||
sum += v;
|
||||
}
|
||||
output.p<T>(e, sum * factor);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, x->lengthOf());
|
||||
}
|
||||
|
||||
void mergeAvg(sd::LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
BUILD_SINGLE_SELECTOR(output.dataType(), mergeAvg_, (inArrs, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void mergeAvgBp_(NDArray& gradient, std::vector<NDArray*>& outArrs) {
|
||||
const sd::LongType numArgs = outArrs.size();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
T v = gradient.e<T>(e) / numArgs;
|
||||
|
||||
for (sd::LongType i = 0; i < numArgs; i++) {
|
||||
outArrs[i]->p<T>(e, v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, gradient.lengthOf());
|
||||
}
|
||||
|
||||
void mergeAvgBp(sd::LaunchContext* context, NDArray& gradient, std::vector<NDArray*>& outArrs) {
|
||||
BUILD_SINGLE_SELECTOR(gradient.dataType(), mergeAvgBp_, (gradient, outArrs), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void mergeAdd_(const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
const sd::LongType numArgs = inArrs.size();
|
||||
auto x = inArrs[0];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
T sum = (T)0.f;
|
||||
for (sd::LongType i = 0; i < numArgs; i++) sum += inArrs[i]->e<T>(e);
|
||||
|
||||
output.p(e, sum);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, x->lengthOf());
|
||||
}
|
||||
void mergeAdd(sd::LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
BUILD_SINGLE_SELECTOR(output.dataType(), mergeAdd_, (inArrs, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void mergeAddBp_(NDArray& gradient, std::vector<NDArray*>& outArrs) {
|
||||
const sd::LongType numArgs = outArrs.size();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
T v = gradient.e<T>(e);
|
||||
|
||||
for (sd::LongType i = 0; i < numArgs; i++) {
|
||||
outArrs[i]->p<T>(e, v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, gradient.lengthOf());
|
||||
}
|
||||
|
||||
void mergeAddBp(sd::LaunchContext* context, NDArray& gradient, std::vector<NDArray*>& outArrs) {
|
||||
BUILD_SINGLE_SELECTOR(gradient.dataType(), mergeAddBp_, (gradient, outArrs), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 18.04.2018
|
||||
//
|
||||
|
||||
#include <array/ResultSet.h>
|
||||
#include <ops/declarable/helpers/meshgrid.h>
|
||||
|
||||
#include <numeric>
|
||||
#if NOT_EXCLUDED(OP_meshgrid)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void meshgrid(sd::LaunchContext* context, const std::vector<NDArray*>& inArrs, const std::vector<NDArray*>& outArrs,
|
||||
const bool swapFirst2Dims) {
|
||||
const int rank = inArrs.size();
|
||||
int inIndices[SD_MAX_RANK];
|
||||
std::iota(inIndices, inIndices + rank, 0);
|
||||
if (swapFirst2Dims && rank > 1) {
|
||||
inIndices[0] = 1;
|
||||
inIndices[1] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rank; ++i) {
|
||||
auto list = outArrs[i]->allTensorsAlongDimension({inIndices[i]});
|
||||
for (int j = 0; j < list.size(); ++j) list.at(j)->assign(inArrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <array/NDArray.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/minimax.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void minimumBPFunctor_(LaunchContext* context, NDArray* x, NDArray* y, NDArray* epsNext, NDArray* gradX, NDArray* gradY) {
|
||||
auto lambdaX = LAMBDA_TTT(_e, _x, _y) { return _x <= _y ? _e : (T)0.; });
|
||||
|
||||
auto lambdaY = LAMBDA_TTT(_e, _x, _y) { return _x >= _y ? _e : (T)0.; });
|
||||
|
||||
if (x->isSameShape(y)) {
|
||||
// PWT case case
|
||||
|
||||
// X gradient
|
||||
epsNext->applyTriplewiseLambda<T>(x, y, lambdaX, gradX);
|
||||
|
||||
// Y gradient
|
||||
epsNext->applyTriplewiseLambda<T>(x, y, lambdaY, gradY);
|
||||
|
||||
} else if (y->isScalar()) {
|
||||
T s = y->e<T>(0);
|
||||
auto lambdaS = LAMBDA_TT(_e, _x, s) { return _x <= s ? _e : (T)0.; });
|
||||
float zero = 0.0f;
|
||||
// scalar case
|
||||
auto tmp = epsNext->reduceNumber(reduce::Sum);
|
||||
if (x <= y)
|
||||
gradY->assign(tmp);
|
||||
else
|
||||
gradY->assign(zero);
|
||||
|
||||
epsNext->applyPairwiseLambda<T>(x, lambdaS, gradX);
|
||||
delete tmp;
|
||||
} else {
|
||||
// broadcast case
|
||||
|
||||
// in this case we want to boost our X and Y shapes to the size of FF pass output (or epsNext, which has the same
|
||||
// shape)
|
||||
auto preX = x->dup();
|
||||
auto preY = y->dup();
|
||||
|
||||
auto targetShape = epsNext->getShapeAsVector();
|
||||
|
||||
preX->tileToShape(*targetShape, *preX);
|
||||
preY->tileToShape(*targetShape, *preY);
|
||||
|
||||
epsNext->applyTriplewiseLambda<T>(preX, preY, lambdaX, preX);
|
||||
epsNext->applyTriplewiseLambda<T>(preX, preY, lambdaY, preY);
|
||||
|
||||
auto axisX = ShapeUtils::evalBroadcastBackwardAxis(x->shapeInfo(), epsNext->shapeInfo());
|
||||
auto axisY = ShapeUtils::evalBroadcastBackwardAxis(y->shapeInfo(), epsNext->shapeInfo());
|
||||
|
||||
if (axisX.size() > 0) {
|
||||
auto sum = preX->reduceAlongDimension(reduce::Sum, &axisX);
|
||||
gradX->assign(sum);
|
||||
} else
|
||||
gradX->assign(preX);
|
||||
|
||||
if (axisY.size() > 0) {
|
||||
auto sum = preY->reduceAlongDimension(reduce::Sum, &axisY);
|
||||
gradY->assign(sum);
|
||||
delete sum;
|
||||
} else
|
||||
gradY->assign(preY);
|
||||
|
||||
delete targetShape;
|
||||
delete preX; // Clean up duped array
|
||||
delete preY; // Clean up duped array
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void maximumBPFunctor_(LaunchContext* context, NDArray* x, NDArray* y, NDArray* epsNext, NDArray* gradX, NDArray* gradY) {
|
||||
auto lambdaX = LAMBDA_TTT(_e, _x, _y) { return _x >= _y ? _e : (T)0.; });
|
||||
|
||||
auto lambdaY = LAMBDA_TTT(_e, _x, _y) { return _x <= _y ? _e : (T)0.; });
|
||||
|
||||
if (x->isSameShape(y)) {
|
||||
// PWT case case
|
||||
|
||||
// X gradient
|
||||
epsNext->applyTriplewiseLambda<T>(x, y, lambdaX, gradX);
|
||||
|
||||
// Y gradient
|
||||
epsNext->applyTriplewiseLambda<T>(x, y, lambdaY, gradY);
|
||||
|
||||
} else if (y->isScalar()) {
|
||||
T s = y->e<T>(0);
|
||||
auto lambdaS = LAMBDA_TT(_e, _x, s) { return _x >= s ? _e : (T)0.; });
|
||||
|
||||
// scalar case
|
||||
auto tmp = epsNext->reduceNumber(reduce::Sum);
|
||||
float zero = 0.0f;
|
||||
if (x <= y)
|
||||
gradY->assign(tmp);
|
||||
else
|
||||
gradY->assign(zero);
|
||||
|
||||
delete tmp;
|
||||
epsNext->applyPairwiseLambda<T>(x, lambdaS, gradX);
|
||||
} else {
|
||||
// broadcast case
|
||||
|
||||
// in this case we want to boost our X and Y shapes to the size of FF pass output (or epsNext, which has the same
|
||||
// shape)
|
||||
auto preX = x->dup();
|
||||
auto preY = y->dup();
|
||||
|
||||
auto targetShape = epsNext->getShapeAsVector();
|
||||
|
||||
preX->tileToShape(*targetShape, *preX);
|
||||
preY->tileToShape(*targetShape, *preY);
|
||||
|
||||
epsNext->applyTriplewiseLambda<T>(preX, preY, lambdaX, preX);
|
||||
epsNext->applyTriplewiseLambda<T>(preX, preY, lambdaY, preY);
|
||||
|
||||
auto axisX = ShapeUtils::evalBroadcastBackwardAxis(x->shapeInfo(), epsNext->shapeInfo());
|
||||
auto axisY = ShapeUtils::evalBroadcastBackwardAxis(y->shapeInfo(), epsNext->shapeInfo());
|
||||
|
||||
if (axisX.size() > 0) {
|
||||
auto sum = preX->reduceAlongDimension(reduce::Sum, &axisX);
|
||||
gradX->assign(sum);
|
||||
delete sum;
|
||||
} else
|
||||
gradX->assign(preX);
|
||||
|
||||
if (axisY.size() > 0) {
|
||||
auto sum = preY->reduceAlongDimension(reduce::Sum, &axisY);
|
||||
gradY->assign(sum);
|
||||
delete sum;
|
||||
} else
|
||||
gradY->assign(preY);
|
||||
|
||||
delete preX; // Clean up duped array
|
||||
delete preY; // Clean up duped array
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void minimumBPFunctor(LaunchContext* context, NDArray* x, NDArray* y, NDArray* epsNext, NDArray* gradX,
|
||||
NDArray* gradY) {
|
||||
BUILD_SINGLE_SELECTOR(x->dataType(), minimumBPFunctor_, (context, x, y, epsNext, gradX, gradY), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
void maximumBPFunctor(LaunchContext* context, NDArray* x, NDArray* y, NDArray* epsNext, NDArray* gradX,
|
||||
NDArray* gradY) {
|
||||
BUILD_SINGLE_SELECTOR(x->dataType(), maximumBPFunctor_, (context, x, y, epsNext, gradX, gradY), SD_NUMERIC_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void minimumBPFunctor_,
|
||||
(LaunchContext* context, NDArray* x, NDArray* y, NDArray* epsNext, NDArray* gradX, NDArray* gradY), SD_NUMERIC_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void maximumBPFunctor_,
|
||||
(LaunchContext* context, NDArray* x, NDArray* y, NDArray* epsNext, NDArray* gradX, NDArray* gradY), SD_NUMERIC_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,72 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/nth_element.h>
|
||||
#include <system/selective_rendering.h>
|
||||
#include "ops/specials.h"
|
||||
#if NOT_EXCLUDED(OP_nth_element)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
void nthElementFunctor_(NDArray* input, sd::LongType n, NDArray* output, bool reverse) {
|
||||
NDArray sortedVals(*input);
|
||||
if (input->isVector()) {
|
||||
SpecialMethods<T>::sortGeneric(input, reverse);
|
||||
output->p(0, input->e<T>(n));
|
||||
} else { // rank greater than 1
|
||||
std::vector<sd::LongType> lastDims(
|
||||
{input->rankOf() - 1});
|
||||
SpecialMethods<T>::sortTadGeneric(&sortedVals, lastDims.data(), lastDims.size(),
|
||||
reverse);
|
||||
|
||||
ResultSet rows = sortedVals.allTensorsAlongDimension(lastDims);
|
||||
sd::LongType oL = output->lengthOf();
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto e = start; e < stop; e++) {
|
||||
auto row = rows.at(e);
|
||||
output->p(e, row->e<T>(n));
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, oL);
|
||||
}
|
||||
}
|
||||
|
||||
void nthElementFunctor(sd::LaunchContext* launchContext, NDArray* input, sd::LongType n, NDArray* output,
|
||||
bool reverse) {
|
||||
|
||||
auto inputDType = input->dataType();
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), nthElementFunctor_, (input, n, output, reverse), SD_NUMERIC_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void nthElementFunctor_,
|
||||
(NDArray * input, sd::LongType n, NDArray* output, bool reverse), SD_NUMERIC_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,97 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 30.05.2019
|
||||
//
|
||||
// CPU implementation of one_hot helper
|
||||
//
|
||||
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/one_hot.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void onehotImpl_(NDArray* indices, NDArray* output,
|
||||
const LongType axis, const LongType depth,
|
||||
const double on, const double off) {
|
||||
auto xBuffer = indices->bufferAsT<X>();
|
||||
auto zBuffer = output->bufferAsT<Z>();
|
||||
|
||||
auto xShapeInfo = indices->shapeInfo();
|
||||
auto zShapeInfo = output->shapeInfo();
|
||||
|
||||
const int xRank = shape::rank(xShapeInfo);
|
||||
const int zRank = shape::rank(zShapeInfo);
|
||||
const sd::LongType* xShape = shape::shapeOf(xShapeInfo);
|
||||
const sd::LongType* zShape = shape::shapeOf(zShapeInfo);
|
||||
const sd::LongType* xStride = shape::stride(xShapeInfo);
|
||||
const sd::LongType* zStride = shape::stride(zShapeInfo);
|
||||
const sd::LongType zLen = output->lengthOf();
|
||||
|
||||
const Z onVal = static_cast<Z>(on);
|
||||
const Z offVal = static_cast<Z>(off);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
sd::LongType coord[SD_MAX_RANK];
|
||||
|
||||
// Compute output coordinate and offset
|
||||
INDEX2COORDS(i, zRank, zShape, coord);
|
||||
sd::LongType zOffset;
|
||||
COORDS2INDEX(zRank, zStride, coord, zOffset);
|
||||
|
||||
// Extract depth coordinate and shift axis
|
||||
const auto depthCoord = coord[axis];
|
||||
for (int j = axis; j < zRank - 1; ++j) {
|
||||
coord[j] = coord[j + 1];
|
||||
}
|
||||
|
||||
// Compute input offset
|
||||
sd::LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStride, coord, xOffset);
|
||||
|
||||
// Check if the depth matches the index
|
||||
const LongType idx = static_cast<LongType>(xBuffer[xOffset]);
|
||||
zBuffer[zOffset] = (depthCoord == idx) ? onVal : offVal;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, zLen);
|
||||
}
|
||||
|
||||
void onehot(const LaunchContext* context, NDArray* indices, NDArray* output,
|
||||
const LongType axis, const LongType depth, const double on, const double off) {
|
||||
const auto xType = indices->dataType();
|
||||
const auto zType = output->dataType();
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {indices});
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(xType, zType, onehotImpl_, (indices, output, axis, depth, on, off),
|
||||
SD_COMMON_TYPES, SD_COMMON_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({output}, {indices});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,293 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
|
||||
//
|
||||
|
||||
#include <helpers/Loops.h>
|
||||
#include <helpers/LoopsCoordsHelper.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
#include <system/Environment.h>
|
||||
|
||||
#include <type_traits>
|
||||
#if NOT_EXCLUDED(OP_pad)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T, size_t constRank>
|
||||
static void copy_core_rank(const T* x, T* coreZ, const sd::LongType* xShapes, const sd::LongType* xStrides,
|
||||
const sd::LongType* zStrides, int start, int stop) {
|
||||
static_assert(constRank > 1, "implement rank 1 directly");
|
||||
size_t loop_count = (stop - start);
|
||||
sd::ZipCoordsState<constRank - 1> cst;
|
||||
sd::zip_size_t offset = sd::init_coords<constRank - 1>(cst, start, xShapes, xStrides, zStrides);
|
||||
auto lastStrideX = xStrides[constRank - 1];
|
||||
auto lastStrideZ = zStrides[constRank - 1];
|
||||
auto inputLastSize = xShapes[constRank - 1];
|
||||
if (lastStrideZ == 1 && lastStrideX == 1) {
|
||||
for (auto k = 0; k < (stop - start); k++) {
|
||||
auto xPtr = &(x[offset.first]);
|
||||
auto zPtr = &(coreZ[offset.second]);
|
||||
for (int i = 0; i < inputLastSize; i++) {
|
||||
zPtr[i] = xPtr[i];
|
||||
}
|
||||
offset = sd::inc_coords<constRank - 1>(cst, offset);
|
||||
}
|
||||
} else {
|
||||
for (size_t k = 0; k < loop_count; k++) {
|
||||
auto xPtr = &(x[offset.first]);
|
||||
auto zPtr = &(coreZ[offset.second]);
|
||||
for (int i = 0; i < inputLastSize; i++) {
|
||||
zPtr[i * lastStrideZ] = xPtr[i * lastStrideX];
|
||||
}
|
||||
offset = sd::inc_coords<constRank - 1>(cst, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void copy_core_generic(int rank, const T* x, T* coreZ, const sd::LongType* xShapes, const sd::LongType* xStrides,
|
||||
const sd::LongType* zStrides, int start, int stop) {
|
||||
auto lastStrideX = xStrides[rank - 1];
|
||||
auto lastStrideZ = zStrides[rank - 1];
|
||||
auto inputLastSize = xShapes[rank - 1];
|
||||
sd::LongType coords[SD_MAX_RANK] = {};
|
||||
sd::LongType* ptrCoords = (sd::LongType*)&coords;
|
||||
|
||||
zip_size_t offset = {};
|
||||
if (rank > 1) {
|
||||
INDEX2COORDS(start, rank - 1, xShapes, ptrCoords);
|
||||
COORDS2INDEX(rank - 1, xStrides, ptrCoords, offset.first);
|
||||
COORDS2INDEX(rank - 1, zStrides, ptrCoords, offset.second);
|
||||
}
|
||||
if (lastStrideZ == 1 && lastStrideX == 1) {
|
||||
for (auto k = 0; k < (stop - start); k++) {
|
||||
auto xPtr = &(x[offset.first]);
|
||||
auto zPtr = &(coreZ[offset.second]);
|
||||
for (int i = 0; i < inputLastSize; i++) {
|
||||
zPtr[i] = xPtr[i];
|
||||
}
|
||||
offset = inc_coords(xShapes, xStrides, zStrides, ptrCoords, offset, rank - 1);
|
||||
}
|
||||
} else {
|
||||
for (auto k = 0; k < (stop - start); k++) {
|
||||
auto xPtr = &(x[offset.first]);
|
||||
auto zPtr = &(coreZ[offset.second]);
|
||||
for (int i = 0; i < inputLastSize; i++) {
|
||||
zPtr[i * lastStrideZ] = xPtr[i * lastStrideX];
|
||||
}
|
||||
offset = inc_coords(xShapes, xStrides, zStrides, ptrCoords, offset, rank - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void pad_(const int mode, NDArray& input, NDArray& paddings, NDArray& output, NDArray& padValue) {
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
|
||||
const sd::LongType* xShape = input.shapeOf();
|
||||
const sd::LongType* zShape = output.shapeOf();
|
||||
|
||||
const int rank = input.rankOf(); // both input and output have the same rank
|
||||
const int rankMinusOne = rank - 1;
|
||||
const auto zLen = output.lengthOf();
|
||||
|
||||
if (mode == 0) { // CONSTANT case
|
||||
|
||||
T padVal = padValue.e<T>(0);
|
||||
|
||||
auto xShapes = input.shapeOf();
|
||||
auto outShapes = output.shapeOf();
|
||||
auto xStrides = input.stridesOf();
|
||||
auto zStrides = output.stridesOf();
|
||||
sd::LongType paddingOffsetCoords[SD_MAX_RANK] = {};
|
||||
sd::LongType* ptrPaddingCoords = (sd::LongType*)&paddingOffsetCoords;
|
||||
bool all_paddings_zero = true;
|
||||
for (int j = 0; j < rank; j++) {
|
||||
auto p0 = paddings.e<sd::LongType>(j, 0);
|
||||
auto p1 = paddings.e<sd::LongType>(j, 1);
|
||||
paddingOffsetCoords[j] = p0;
|
||||
|
||||
all_paddings_zero = all_paddings_zero && (p0 == 0) && (p1 == 0);
|
||||
}
|
||||
|
||||
sd::LongType paddingOffset;
|
||||
COORDS2INDEX(rank, zStrides, ptrPaddingCoords, paddingOffset);
|
||||
|
||||
auto inputLastSize = xShapes[rank - 1];
|
||||
|
||||
// fill everything with padding Value
|
||||
if (!all_paddings_zero) output.assign(padVal, true);
|
||||
|
||||
// fill the core from input
|
||||
auto coreZ = &(z[paddingOffset]);
|
||||
// iterate over core
|
||||
auto len = input.lengthOf() / inputLastSize;
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
if (rank == 3) {
|
||||
copy_core_rank<T, 3>(x, coreZ, xShapes, xStrides, zStrides, start, stop);
|
||||
} else if (rank == 4) {
|
||||
copy_core_rank<T, 4>(x, coreZ, xShapes, xStrides, zStrides, start, stop);
|
||||
} else if (rank == 5) {
|
||||
copy_core_rank<T, 5>(x, coreZ, xShapes, xStrides, zStrides, start, stop);
|
||||
} else {
|
||||
copy_core_generic(rank, x, coreZ, xShapes, xStrides, zStrides, start, stop);
|
||||
}
|
||||
};
|
||||
// fixed restriction for smaller inputs
|
||||
auto numThreads = (zLen > 64 || inputLastSize > 4096) ? sd::Environment::getInstance().maxMasterThreads() : 1;
|
||||
samediff::Threads::parallel_tad(func, 0, len, 1, numThreads);
|
||||
|
||||
} else { // REFLECT and SYMMETRIC cases
|
||||
|
||||
const sd::LongType shift1 = mode == 1 ? 0 : 1; // REFLECT : SYMMETRIC
|
||||
const sd::LongType shift2 = mode == 1 ? 2 : 1; // REFLECT : SYMMETRIC
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
sd::LongType zCoords[SD_MAX_RANK], xCoords[SD_MAX_RANK];
|
||||
|
||||
for (auto i = start; i < stop; i++) {
|
||||
INDEX2COORDS(i, rank, shape::shapeOf(output.shapeInfo()), zCoords);
|
||||
sd::LongType zOffset;
|
||||
COORDS2INDEX(rank, shape::stride(output.shapeInfo()), zCoords, zOffset);
|
||||
|
||||
memcpy(xCoords, zCoords, rank * sizeof(sd::LongType));
|
||||
|
||||
for (int j = rankMinusOne; j >= 0; --j) {
|
||||
if (xShape[j] == zShape[j]) continue;
|
||||
|
||||
xCoords[j] =
|
||||
zCoords[j] - paddings.e<sd::LongType>(j, 0); // are ready to fill middle (within input dimension range)
|
||||
|
||||
if (xCoords[j] < 0)
|
||||
xCoords[j] = -xCoords[j] - shift1; // means fill from left
|
||||
else if (xCoords[j] >= xShape[j])
|
||||
xCoords[j] = 2 * xShape[j] - xCoords[j] - shift2; // means fill from right
|
||||
}
|
||||
|
||||
sd::LongType xOffset;
|
||||
COORDS2INDEX(rank, shape::stride(input.shapeInfo()), xCoords, xOffset);
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, zLen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void pad(sd::LaunchContext* context, const int mode, NDArray& input, NDArray& paddings, NDArray& output,
|
||||
NDArray& padValue) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), pad_, (mode, input, paddings, output, padValue), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void mirrorPad_(NDArray& input, NDArray& paddings, NDArray& output, const int mode) {
|
||||
// mode: 0 - REFLECT, else - SYMMETRIC
|
||||
const int reflBorder = (bool)mode ? 1 : 0;
|
||||
const int rank = input.rankOf();
|
||||
const sd::LongType outLen = output.lengthOf();
|
||||
|
||||
// Cache shape information
|
||||
const sd::LongType* inShapeInfo = input.shapeInfo();
|
||||
const sd::LongType* outShapeInfo = output.shapeInfo();
|
||||
const sd::LongType* inShape = shape::shapeOf(inShapeInfo);
|
||||
const sd::LongType* outShape = shape::shapeOf(outShapeInfo);
|
||||
const sd::LongType* inStride = shape::stride(inShapeInfo);
|
||||
const sd::LongType* outStride = shape::stride(outShapeInfo);
|
||||
|
||||
// Cache buffers
|
||||
T* outBuf = reinterpret_cast<T*>(output.buffer());
|
||||
const T* inBuf = reinterpret_cast<T const*>(input.buffer());
|
||||
|
||||
if (input.isScalar() || input.isVector()) {
|
||||
const sd::LongType inLen = input.isScalar() ? 1 : input.lengthOf();
|
||||
const auto leftSide = paddings.e<sd::LongType>(0);
|
||||
const auto leftSideCorrected = leftSide - reflBorder;
|
||||
const sd::LongType len = 2 * (inLen - 1) + leftSide + reflBorder;
|
||||
|
||||
for (int i = 0; i < outLen; ++i) {
|
||||
if (i < leftSide) // left side
|
||||
output.p(i, input.e<T>(leftSideCorrected - i));
|
||||
else if (i >= leftSide && i < leftSide + inLen) // middle
|
||||
output.p(i, input.e<T>(i - leftSide));
|
||||
else // right side
|
||||
output.p(i, input.e<T>(len - i));
|
||||
}
|
||||
} else {
|
||||
// Cache input sizes
|
||||
std::vector<sd::LongType> inSizes(rank);
|
||||
std::vector<sd::LongType> leftSides(rank);
|
||||
std::vector<sd::LongType> leftSidesCorrected(rank);
|
||||
std::vector<sd::LongType> lens(rank);
|
||||
|
||||
// Pre-calculate size-related values for each dimension
|
||||
for (int j = 0; j < rank; ++j) {
|
||||
inSizes[j] = input.sizeAt(j);
|
||||
leftSides[j] = paddings.e<T>(j, 0);
|
||||
leftSidesCorrected[j] = leftSides[j] - reflBorder;
|
||||
lens[j] = 2 * (inSizes[j] - 1) + leftSides[j] + reflBorder;
|
||||
}
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
// Pre-allocate coordinate arrays
|
||||
sd::LongType inIdx[SD_MAX_RANK], outIdx[SD_MAX_RANK];
|
||||
|
||||
for (sd::LongType i = start; i < stop; i++) {
|
||||
INDEX2COORDS(i, rank, outShape, outIdx);
|
||||
|
||||
for (int j = 0; j < rank; ++j) {
|
||||
if (outIdx[j] < leftSides[j]) // left side
|
||||
inIdx[j] = leftSidesCorrected[j] - outIdx[j];
|
||||
else if (outIdx[j] >= leftSides[j] && outIdx[j] < leftSides[j] + inSizes[j]) // middle
|
||||
inIdx[j] = outIdx[j] - leftSides[j];
|
||||
else // right side
|
||||
inIdx[j] = lens[j] - outIdx[j];
|
||||
}
|
||||
|
||||
sd::LongType outOffset, inOffset;
|
||||
COORDS2INDEX(rank, outStride, outIdx, outOffset);
|
||||
COORDS2INDEX(rank, inStride, inIdx, inOffset);
|
||||
outBuf[outOffset] = inBuf[inOffset];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, outLen);
|
||||
}
|
||||
}
|
||||
void mirrorPad(sd::LaunchContext* context, NDArray& input, NDArray& paddings, NDArray& output,
|
||||
const int mode) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), mirrorPad_, (input, paddings, output, mode), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void mirrorPad_,
|
||||
(NDArray& input, NDArray& paddings, NDArray& output, const int mode),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 17.05.2018
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <ops/declarable/helpers/percentile.h>
|
||||
#if NOT_EXCLUDED(OP_percentile)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void _percentile(NDArray& input, NDArray& output, std::vector<LongType>& axises, const float q,
|
||||
const int interpolation) {
|
||||
const int inputRank = input.rankOf();
|
||||
|
||||
if (axises.empty())
|
||||
for (int i = 0; i < inputRank; ++i) axises.push_back(i);
|
||||
else
|
||||
shape::checkDimensions(inputRank, &axises); // check, sort dimensions and remove duplicates if they are present
|
||||
|
||||
auto listOfSubArrs = input.allTensorsAlongDimension(axises);
|
||||
|
||||
std::vector<sd::LongType> shapeOfSubArr(listOfSubArrs.at(0)->rankOf());
|
||||
for (size_t i = 0; i < shapeOfSubArr.size(); ++i) shapeOfSubArr[i] = listOfSubArrs.at(0)->shapeOf()[i];
|
||||
|
||||
auto flattenedArr = NDArrayFactory::create('c', shapeOfSubArr, input.dataType(), input.getContext());
|
||||
const int len = flattenedArr->lengthOf();
|
||||
|
||||
const float fraction = 1.f - q / 100.;
|
||||
sd::LongType position = 0;
|
||||
|
||||
switch (interpolation) {
|
||||
case 0: // lower
|
||||
position = static_cast<sd::LongType>(math::sd_ceil<float, T>((len - 1) * fraction));
|
||||
break;
|
||||
case 1: // higher
|
||||
position = static_cast<sd::LongType>(math::sd_floor<float, T>((len - 1) * fraction));
|
||||
break;
|
||||
case 2: // nearest
|
||||
position = static_cast<sd::LongType>(math::sd_round<float, T>((len - 1) * fraction));
|
||||
break;
|
||||
}
|
||||
position = len - position - 1;
|
||||
|
||||
// FIXME: our sort impl should be used instead, so this operation might be implemented as generic
|
||||
// FIXME: parallelism !
|
||||
for (int i = 0; i < listOfSubArrs.size(); ++i) {
|
||||
auto buff = reinterpret_cast<T*>(flattenedArr->buffer());
|
||||
flattenedArr->assign(listOfSubArrs.at(i));
|
||||
std::sort(buff, buff + len);
|
||||
output.p(i, flattenedArr->e<T>(position));
|
||||
}
|
||||
|
||||
delete flattenedArr;
|
||||
}
|
||||
|
||||
void percentile(sd::LaunchContext* context, NDArray& input, NDArray& output, std::vector<LongType>& axises,
|
||||
const float q, const int interpolation) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), _percentile, (input, output, axises, q, interpolation), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _percentile,
|
||||
(NDArray& input, NDArray& output, std::vector<LongType>& axises, const float q,
|
||||
const int interpolation),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by Yurii Shyrma on 12.12.2017
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/gammaMathFunc.h>
|
||||
#include <ops/declarable/helpers/zeta.h>
|
||||
#if NOT_EXCLUDED(OP_polygamma)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// calculate factorial
|
||||
template <typename T>
|
||||
static SD_INLINE T getFactorial(const int n) {
|
||||
if (n < 0) THROW_EXCEPTION("factorial is not defined for negative number !");
|
||||
|
||||
if (n == 0 || n == 1) return (T)1.f;
|
||||
|
||||
T result = (T)1.f;
|
||||
|
||||
for (int i = 2; i <= n; ++i) result *= i;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// implementation is based on serial representation written in terms of the Hurwitz zeta function as polygamma =
|
||||
// (-1)^{n+1} * n! * zeta(n+1, x)
|
||||
template <typename T>
|
||||
static SD_INLINE T polyGammaScalar(sd::LaunchContext* context, const int n, const T x) {
|
||||
int sign = (n + 1) % 2 ? -1 : 1;
|
||||
T zeta = zetaScalar<T>(T(n + 1), x);
|
||||
return T(sign) * getFactorial<T>(n) * zeta;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// calculate polygamma function for arrays
|
||||
template <typename T>
|
||||
static void polyGamma_(sd::LaunchContext* context, NDArray& n, NDArray& x, NDArray& output) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
const T order = n.e<T>(i);
|
||||
if (order !=
|
||||
static_cast<int>(order)) // if order has fractional part then do not perform calculations and return NAN
|
||||
output.p(i, std::numeric_limits<T>::quiet_NaN());
|
||||
else if (order == 0) // polygamma function of zero order is digamma function
|
||||
output.p(i, diGammaScalar<T>(x.e<T>(i)));
|
||||
else
|
||||
output.p(i, polyGammaScalar<T>(context, order, x.e<T>(i)));
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, x.lengthOf());
|
||||
}
|
||||
|
||||
void polyGamma(sd::LaunchContext* context, NDArray& n, NDArray& x, NDArray& output) {
|
||||
BUILD_SINGLE_SELECTOR(x.dataType(), polyGamma_, (context, n, x, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void polyGamma_,
|
||||
(sd::LaunchContext * context, NDArray& n, NDArray& x, NDArray& output),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
|
||||
#include <helpers/shape.h>
|
||||
#include <ops/declarable/helpers/prefix.h>
|
||||
#include <ops/ops.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static void prefix_(scalar::Ops op, const void* vx, sd::LongType const* xShapeInfo, void* vz,
|
||||
sd::LongType const* zShapeInfo, bool exclusive, bool reverse) {
|
||||
const auto x = reinterpret_cast<const T*>(vx);
|
||||
auto z = reinterpret_cast<T*>(vz);
|
||||
auto length = shape::length(xShapeInfo);
|
||||
|
||||
T prevSum = op == scalar::Add ? (T)0 : (T)1;
|
||||
T sum = prevSum;
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
LongType zOffset;
|
||||
|
||||
sd::LongType xRank = shape::rank(xShapeInfo);
|
||||
sd::LongType zRank = shape::rank(zShapeInfo);
|
||||
sd::LongType *xShape = shape::shapeOf(xShapeInfo);
|
||||
sd::LongType *xStride = shape::stride(xShapeInfo);
|
||||
sd::LongType *zShape = shape::shapeOf(zShapeInfo);
|
||||
sd::LongType *zStride = shape::stride(zShapeInfo);
|
||||
|
||||
|
||||
if (reverse) {
|
||||
for (sd::LongType e = length - 1; e >= 0; --e) {
|
||||
INDEX2COORDS(e, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
INDEX2COORDS(e, zRank, zShape, zCoords);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
|
||||
sum = op == scalar::Add ? simdOps::Add<T, T, T>::op(sum, x[xOffset])
|
||||
: simdOps::Multiply<T, T, T>::op(sum, x[xOffset]);
|
||||
|
||||
if (!exclusive) prevSum = sum;
|
||||
|
||||
z[zOffset] = prevSum;
|
||||
prevSum = sum;
|
||||
}
|
||||
} else {
|
||||
for (sd::LongType e = 0; e < length; e++) {
|
||||
INDEX2COORDS(e, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
INDEX2COORDS(e, zRank, zShape, zCoords);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
|
||||
sum = op == scalar::Add ? simdOps::Add<T, T, T>::op(sum, x[xOffset])
|
||||
: simdOps::Multiply<T, T, T>::op(sum, x[xOffset]);
|
||||
|
||||
if (!exclusive) prevSum = sum;
|
||||
|
||||
z[zOffset] = prevSum;
|
||||
prevSum = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void prefix_(scalar::Ops op, NDArray* x, NDArray* z, const std::vector<LongType>& dims, bool exclusive,
|
||||
bool reverse) {
|
||||
auto xTads = x->allTensorsAlongDimension(dims);
|
||||
auto zTads = z->allTensorsAlongDimension(dims);
|
||||
auto t = xTads.size();
|
||||
|
||||
for (int e = 0; e < t; e++) {
|
||||
auto tx = xTads.at(e);
|
||||
auto tz = zTads.at(e);
|
||||
|
||||
prefix_<T>(op, tx->buffer(), tx->shapeInfo(), tz->buffer(), tz->shapeInfo(), exclusive, reverse);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static void prefix_(scalar::Ops op, NDArray* x, NDArray* z, bool exclusive, bool reverse) {
|
||||
prefix_<T>(op, x->buffer(), x->shapeInfo(), z->buffer(), z->shapeInfo(), exclusive, reverse);
|
||||
};
|
||||
|
||||
void prefix(sd::LaunchContext* context, scalar::Ops op, NDArray* x, NDArray* z, bool exclusive, bool reverse) {
|
||||
BUILD_SINGLE_SELECTOR(x->dataType(), prefix_, (op, x, z, exclusive, reverse), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
void prefix(sd::LaunchContext* context, scalar::Ops op, NDArray* x, NDArray* z, const std::vector<sd::LongType>& dims,
|
||||
bool exclusive, bool reverse) {
|
||||
BUILD_SINGLE_SELECTOR(x->dataType(), prefix_, (op, x, z, dims, exclusive, reverse), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void prefix_,
|
||||
(scalar::Ops op, const void* vx, sd::LongType const* xShapeInfo, void* vz,
|
||||
sd::LongType const* zShapeInfo, bool exclusive, bool reverse),
|
||||
SD_NUMERIC_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void prefix_,
|
||||
(scalar::Ops op, NDArray* x, NDArray* z, const std::vector<sd::LongType>& dims, bool exclusive,
|
||||
bool reverse),
|
||||
SD_NUMERIC_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void prefix_,
|
||||
(scalar::Ops op, NDArray* x, NDArray* z, bool exclusive, bool reverse), SD_NUMERIC_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,32 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/print_variable.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
void print_special(LaunchContext &ctx, NDArray&array, const std::string &message) {
|
||||
array.printIndexedBuffer(message.c_str());
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 George A. Shulinok <sgazeos@gmail.com>
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/MmulHelper.h>
|
||||
#include <ops/declarable/helpers/qr.h>
|
||||
#if NOT_EXCLUDED(OP_qr)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
NDArray matrixMinor(NDArray& in, sd::LongType col) {
|
||||
NDArray* m = in.ulike();
|
||||
m->setIdentity();
|
||||
auto mRef = *m;
|
||||
auto view = mRef({col, m->rows(), col, m->columns()});
|
||||
auto inView = in({col, m->rows(), col, m->columns()});
|
||||
view->assign(inView);
|
||||
delete view;
|
||||
delete inView;
|
||||
delete m;
|
||||
|
||||
return mRef;
|
||||
}
|
||||
|
||||
/* m = I - v v^T */
|
||||
template <typename T>
|
||||
NDArray vmul(NDArray& v, int n) {
|
||||
std::vector<sd::LongType> nShape = {n,n};
|
||||
NDArray res('c', nShape, v.dataType(), v.getContext()); // x = matrix_new(n, n);
|
||||
T const* vBuf = v.getDataBuffer()->primaryAsT<T>();
|
||||
T* resBuf = res.dataBuffer()->primaryAsT<T>();
|
||||
auto interloop = PRAGMA_THREADS_FOR_2D {
|
||||
for (auto i = start_x; i < n; i += inc_x)
|
||||
for (auto j = start_y; j < n; j += inc_y) resBuf[i * n + j] = -2 * vBuf[i] * vBuf[j] + (i == j ? T(1) : T(0));
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(interloop, 0, n, 1, 0, n, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void qrSingle(NDArray* matrix, NDArray* Q, NDArray* R, bool const fullMatricies) {
|
||||
sd::LongType M = matrix->sizeAt(-2);
|
||||
sd::LongType N = matrix->sizeAt(-1);
|
||||
auto resQ = fullMatricies ? Q->ulike() : new NDArray(NDArrayFactory::create<T>(matrix->ordering(), {M, M}, Q->getContext()));
|
||||
auto resR = fullMatricies ? R->ulike() : matrix->ulike();
|
||||
std::vector<NDArray*> q(M, nullptr);
|
||||
|
||||
std::vector<sd::LongType> mShape = {M};
|
||||
NDArray z = *matrix;
|
||||
NDArray e('c', mShape, DataTypeUtils::fromT<T>(), Q->getContext()); // two internal buffers and scalar for squared norm
|
||||
|
||||
for (sd::LongType k = 0; k < N && k < M - 1; k++) { // loop for columns, but not further then row number
|
||||
e.nullify();
|
||||
z = matrixMinor<T>(z, k); // minor computing for current column with given matrix z (initally is a input matrix)
|
||||
|
||||
std::vector<sd::LongType> zeroVec = {0};
|
||||
auto currentColumn = z({0, 0, k, k + 1}); // retrieve k column from z to x buffer
|
||||
auto *normPtr = currentColumn->reduceAlongDimension(reduce::Norm2,&zeroVec);
|
||||
NDArray norm = *normPtr;
|
||||
delete normPtr;
|
||||
|
||||
if (matrix->t<T>(k, k) > T(0.f)) { // negate on positive matrix diagonal element
|
||||
NDArray *negNorm = norm * T(-1.f);
|
||||
norm.assign(negNorm);
|
||||
delete negNorm;
|
||||
}
|
||||
|
||||
e.p(k, &norm);
|
||||
NDArray *ePlusColumn = e + (*currentColumn);
|
||||
e.assign(ePlusColumn);
|
||||
delete ePlusColumn;
|
||||
|
||||
auto *normEPtr = e.reduceAlongDimension(reduce::Norm2, &zeroVec);
|
||||
NDArray *eDivNormE = e / (*normEPtr);
|
||||
e.assign(eDivNormE);
|
||||
delete eDivNormE;
|
||||
delete normEPtr;
|
||||
|
||||
q[k] = new NDArray(vmul<T>(e, M));
|
||||
auto qQ = z.ulike();
|
||||
MmulHelper::matmul(q[k], &z, qQ, false, false, 0, 0, qQ);
|
||||
z = std::move(*qQ);
|
||||
|
||||
delete currentColumn;
|
||||
}
|
||||
|
||||
|
||||
resQ->assign(q[0]); //
|
||||
|
||||
for (sd::LongType i = 1; i < N && i < M - 1; i++) {
|
||||
auto tempResQ = resQ;
|
||||
MmulHelper::matmul(q[i], resQ, tempResQ, false, false, 0, 0, tempResQ); // use mmulMxM?
|
||||
resQ = std::move(tempResQ);
|
||||
}
|
||||
MmulHelper::matmul(resQ, matrix, resR, false, false, 0, 0, resR);
|
||||
// resR *= -1.f;
|
||||
resQ->transposei();
|
||||
if (fullMatricies) {
|
||||
Q->assign(resQ);
|
||||
R->assign(resR);
|
||||
} else {
|
||||
auto resQRef = *resQ;
|
||||
auto resRRef = *resR;
|
||||
auto resQView = resQRef({0, 0, 0, N});
|
||||
auto resRView = resRRef({0, N, 0, 0});
|
||||
Q->assign(resQView);
|
||||
R->assign(resRView);
|
||||
delete resQView;
|
||||
delete resRView;
|
||||
}
|
||||
|
||||
// Clean up allocated NDArrays in q vector
|
||||
for (sd::LongType i = 0; i < M; i++) {
|
||||
if (q[i] != nullptr) {
|
||||
delete q[i];
|
||||
}
|
||||
}
|
||||
|
||||
delete resQ;
|
||||
delete resR;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void qr_(NDArray * input, NDArray* outputQ, NDArray* outputR, bool const fullMatricies) {
|
||||
sd::LongType lastDim = input->rankOf() - 1;
|
||||
sd::LongType preLastDim = input->rankOf() - 2;
|
||||
ResultSet listOutQ(outputQ->allTensorsAlongDimension({(int)preLastDim, (int)lastDim}));
|
||||
ResultSet listOutR(outputR->allTensorsAlongDimension({(int)preLastDim, (int)lastDim}));
|
||||
ResultSet listInput(input->allTensorsAlongDimension({(int)preLastDim, (int)lastDim}));
|
||||
auto batching = PRAGMA_THREADS_FOR {
|
||||
for (auto batch = start; batch < stop; batch++) {
|
||||
// qr here
|
||||
qrSingle<T>(listInput.at(batch), listOutQ.at(batch), listOutR.at(batch), fullMatricies);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(batching, 0, listOutQ.size(), 1);
|
||||
}
|
||||
|
||||
void qr(sd::LaunchContext* context, NDArray * input, NDArray* outputQ, NDArray* outputR,
|
||||
bool const fullMatricies) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), qr_, (input, outputQ, outputR, fullMatricies), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,297 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/random.h>
|
||||
#include <memory>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/RandomLauncher.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#if NOT_EXCLUDED(OP_random)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
/**
|
||||
* gammaLess - compute gamma distributed value for shapes (alpha) from 0 to 1
|
||||
* @tparam T - any float types are acceptable
|
||||
* @param rng - random generator for uniformly vals
|
||||
* @param alpha - shape of distribution
|
||||
* @param beta - scale of distributed values
|
||||
* @return gamma distributed value
|
||||
*/
|
||||
template <typename T>
|
||||
T gammaLess(graph::RandomGenerator& rng, T const alpha, T const beta) {
|
||||
auto d = T(1.0334f) - T(0.0766f) * math::p_exp(T(2.2942f) * alpha);
|
||||
auto a = math::p_pow(T(2.f), alpha) * math::p_pow<T>(T(1.f) - math::p_exp(-d * T(0.5f)), alpha);
|
||||
auto b = alpha * math::p_pow(d, alpha - T(1.f)) * exp(-d);
|
||||
auto c = a + b;
|
||||
T rawX;
|
||||
static sd::LongType index = 0;
|
||||
const T underAlpha = T(1.f) / alpha;
|
||||
const T powerAlpha = math::p_pow<T>(T(2.f), alpha - T(1.f));
|
||||
|
||||
for (;;) {
|
||||
auto u = rng.relativeT<T>(index++, T(0.f), T(1.f));
|
||||
|
||||
if (u <= a / c)
|
||||
rawX = -T(2.f) * math::p_log(T(1.f) - T(0.5f) * math::p_pow(T(c * u), underAlpha));
|
||||
else
|
||||
rawX = -math::p_log(c * (T(1.f) - u) / (alpha * math::p_pow(d, alpha - T(1.f))));
|
||||
|
||||
T v = static_cast<T>(rng.relativeT(index++, 0.f, 1.f));
|
||||
if (rawX <= d) {
|
||||
auto testVal = (math::p_pow(rawX, alpha - 1.f) * math::p_exp(-T(0.5f) * rawX)) /
|
||||
(powerAlpha * math::p_pow(T(1.f) - math::p_exp(-T(0.5f) * rawX), alpha - T(1.f)));
|
||||
if (testVal < v) continue;
|
||||
break;
|
||||
} else {
|
||||
if (v <= math::p_pow<T>(d / rawX, T(1.f) - alpha)) break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return rawX / beta;
|
||||
}
|
||||
|
||||
/**
|
||||
* gammaGreat - generate gamma distributed value for shape (alpha) greater then 1
|
||||
* @tparam T - given type (any float type is accepted.)
|
||||
* @param rng - random generator
|
||||
* @param alpha - shape of the gamma distribution (alpha)
|
||||
* @param beta - scale of the gamma distribution (beta)
|
||||
* @return - gamma distributed value with given params
|
||||
*/
|
||||
template <typename T>
|
||||
T gammaGreat(graph::RandomGenerator& rng, T const alpha, T const beta) {
|
||||
auto decreasedAlpha = alpha - T(1.f / 3.f);
|
||||
auto c = T(1.) / math::p_sqrt(T(9.f) * decreasedAlpha);
|
||||
static sd::LongType index = 0;
|
||||
T x;
|
||||
auto normalDistributed = [](graph::RandomGenerator& rng, sd::LongType& index) {
|
||||
auto v1 = rng.relativeT(index++, T(0.f), T(1.f));
|
||||
auto v2 = rng.relativeT(index++, T(0.f), T(1.f));
|
||||
|
||||
return math::p_cos(T(2.f * 3.141592f) * v2) * math::p_sqrt(T(-2.f) * math::p_log(v1));
|
||||
};
|
||||
|
||||
float normalizedVar;
|
||||
for (;;) {
|
||||
do {
|
||||
x = normalDistributed(rng, index);
|
||||
normalizedVar = T(1.f) + c * x;
|
||||
} while (normalizedVar < T(0.f));
|
||||
normalizedVar = normalizedVar * normalizedVar * normalizedVar; // v * v * v;
|
||||
|
||||
auto u = rng.relativeT<T>(index++, T(0.f), T(1.f));
|
||||
if (u < T(1.f) - T(.0331f) * (x * x) * (x * x)) break;
|
||||
if (log(u) < 0.5f * x * x + decreasedAlpha * (1. - normalizedVar + math::p_log(normalizedVar))) break;
|
||||
}
|
||||
return (decreasedAlpha * normalizedVar / beta);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void fillRandomGamma_(LaunchContext* context, graph::RandomGenerator& rng, NDArray* alpha, NDArray* beta,
|
||||
NDArray* output) {
|
||||
auto broadcasted = alpha->shapeInfo();
|
||||
if (beta != nullptr) {
|
||||
sd::LongType* broadcastedShape = nullptr;
|
||||
ShapeUtils::evalBroadcastShapeInfo(alpha->shapeInfo(), beta->shapeInfo(), true, broadcastedShape, context->getWorkspace());
|
||||
broadcasted = broadcastedShape;
|
||||
}
|
||||
|
||||
auto step = shape::length(broadcasted);
|
||||
auto shift = output->lengthOf() / step;
|
||||
|
||||
auto copyAlpha = alpha;
|
||||
auto copyBeta = beta;
|
||||
if (beta != nullptr) {
|
||||
NDArray alphaBroadcasted(broadcasted, alpha->dataType(), false, context);
|
||||
NDArray betaBroadcasted(broadcasted, beta->dataType(), false, context);
|
||||
|
||||
copyAlpha = alphaBroadcasted.applyTrueBroadcast(BroadcastOpsTuple::Assign(), alpha);
|
||||
copyBeta = betaBroadcasted.applyTrueBroadcast(BroadcastOpsTuple::Assign(), beta);
|
||||
}
|
||||
bool directOutput = output->ews() == 1 && output->ordering() == 'c';
|
||||
T* outputBuf = output->dataBuffer()->primaryAsT<T>();
|
||||
|
||||
PRAGMA_OMP_PARALLEL_FOR
|
||||
for (sd::LongType k = 0; k < shift; k++) {
|
||||
auto pos = k * step;
|
||||
for (sd::LongType e = 0; e < step; e++)
|
||||
if (directOutput) {
|
||||
outputBuf[pos + e] = copyAlpha->t<T>(e) <= 1
|
||||
? gammaLess(rng, copyAlpha->t<T>(e), beta ? copyBeta->t<T>(e) : T(1.f))
|
||||
: gammaGreat(rng, copyAlpha->t<T>(e), beta ? copyBeta->t<T>(e) : T(1.f));
|
||||
} else {
|
||||
output->r<T>(pos + e) = copyAlpha->t<T>(e) <= 1
|
||||
? gammaLess(rng, copyAlpha->t<T>(e), beta ? copyBeta->t<T>(e) : T(1.f))
|
||||
: gammaGreat(rng, copyAlpha->t<T>(e), beta ? copyBeta->t<T>(e) : T(1.f));
|
||||
}
|
||||
}
|
||||
|
||||
if (beta != nullptr) {
|
||||
delete copyAlpha;
|
||||
delete copyBeta;
|
||||
}
|
||||
}
|
||||
|
||||
void fillRandomGamma(LaunchContext* context, graph::RandomGenerator& rng, NDArray* alpha, NDArray* beta,
|
||||
NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(output->dataType(), fillRandomGamma_, (context, rng, alpha, beta, output), SD_FLOAT_NATIVE);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void fillRandomGamma_,
|
||||
(LaunchContext * context, graph::RandomGenerator& rng, NDArray* alpha, NDArray* beta,
|
||||
NDArray* output),
|
||||
SD_FLOAT_NATIVE);
|
||||
|
||||
/*
|
||||
* algorithm Poisson generator based upon the inversion by sequential search:[48]:505
|
||||
init:
|
||||
Let x ← 0, p ← e−λ, s ← p.
|
||||
Generate uniform random number u in [0,1].
|
||||
while u > s do:
|
||||
x ← x + 1.
|
||||
p ← p * λ / x.
|
||||
s ← s + p.
|
||||
return x.
|
||||
* */
|
||||
template <typename T, typename Z>
|
||||
void fillRandomPoisson_(LaunchContext* context, graph::RandomGenerator& rng, NDArray* lambda, NDArray* output) {
|
||||
auto shift = output->lengthOf() / lambda->lengthOf();
|
||||
auto step = lambda->lengthOf();
|
||||
T* lambdaBuf = lambda->dataBuffer()->primaryAsT<T>();
|
||||
Z* outputBuf = output->dataBuffer()->primaryAsT<Z>();
|
||||
bool directLa = lambda->ews() == 1 && lambda->ordering() == 'c';
|
||||
bool directOut = output->ews() == 1 && output->ordering() == 'c';
|
||||
PRAGMA_OMP_PARALLEL_FOR
|
||||
for (sd::LongType k = 0; k < shift; k++) {
|
||||
auto pos = k * step;
|
||||
auto u = rng.relativeT<T>(k, static_cast<T>(0.), static_cast<T>(1.));
|
||||
for (sd::LongType e = 0; e < step; e++) {
|
||||
auto p = math::sd_exp<T, T>(-lambda->t<T>(e));
|
||||
auto s = p;
|
||||
auto x = Z(0.f);
|
||||
while (u > s) {
|
||||
x += 1.f;
|
||||
p *= static_cast<T>(directLa ? lambdaBuf[e] / x : lambda->t<T>(e) / x);
|
||||
s += p;
|
||||
}
|
||||
if (directOut)
|
||||
outputBuf[pos + e] = x;
|
||||
else
|
||||
output->r<Z>(pos + e) = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fillRandomPoisson(LaunchContext* context, graph::RandomGenerator& rng, NDArray* lambda, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(lambda->dataType(), output->dataType(), fillRandomPoisson_, (context, rng, lambda, output),
|
||||
SD_FLOAT_TYPES, SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_DOUBLE_TEMPLATE( void fillRandomPoisson_,
|
||||
(LaunchContext * context, graph::RandomGenerator& rng, NDArray* lambda, NDArray* output),
|
||||
SD_FLOAT_TYPES, SD_FLOAT_TYPES);
|
||||
|
||||
template <typename T>
|
||||
void fillRandomUniform_(LaunchContext* context, graph::RandomGenerator& rng, NDArray* min, NDArray* max,
|
||||
NDArray* output) {
|
||||
T minVal = T(0);
|
||||
T maxVal = DataTypeUtils::max<T>();
|
||||
if (min) minVal = min->t<T>(0);
|
||||
if (max) maxVal = max->t<T>(0);
|
||||
|
||||
if (output->isR())
|
||||
RandomLauncher::fillUniform(context, rng, output, minVal, maxVal);
|
||||
else {
|
||||
PRAGMA_OMP_PARALLEL_FOR
|
||||
for (sd::LongType i = 0; i < output->lengthOf(); i++) {
|
||||
output->r<T>(i) = rng.relativeT<T>(i, minVal, maxVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fillRandomUniform(LaunchContext* context, graph::RandomGenerator& rng, NDArray* min, NDArray* max,
|
||||
NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(output->dataType(), fillRandomUniform_, (context, rng, min, max, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
// used https://en.wikipedia.org/wiki/Categorical_distribution
|
||||
// methods: gumbel trick + softmax + argmax
|
||||
template <typename Tx, typename Tz>
|
||||
void fillRandomMultiNomial_(LaunchContext* context, graph::RandomGenerator& rng, NDArray& input, NDArray& output,
|
||||
const sd::LongType numOfSamples, const int dimC) {
|
||||
const Tx* x = input.bufferAsT<Tx>();
|
||||
Tz* z = output.bufferAsT<Tz>();
|
||||
|
||||
Tx minVal = DataTypeUtils::min_positive<Tx>();
|
||||
Tx maxVal = static_cast<Tx>(1.0);
|
||||
|
||||
auto dimA = (0 == dimC) ? 1 : 0;
|
||||
const sd::LongType batchValue = output.sizeAt(dimC);
|
||||
const sd::LongType numOfClassX = input.sizeAt(dimA);
|
||||
|
||||
const sd::LongType zDimAstride = output.stridesOf()[dimA];
|
||||
const sd::LongType xDimAstride = input.stridesOf()[dimA];
|
||||
const sd::LongType zDimCstride = output.stridesOf()[dimC];
|
||||
const sd::LongType xDimCstride = input.stridesOf()[dimC];
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
for (auto nBatchIndex = start_x; nBatchIndex < stop_x; nBatchIndex += inc_x) {
|
||||
for (auto nSampleIndexInBatch = start_y; nSampleIndexInBatch < stop_y; nSampleIndexInBatch += inc_y) {
|
||||
const Tx* xTad = x + (nBatchIndex * xDimCstride);
|
||||
Tz* zTad = z + (nBatchIndex * zDimCstride);
|
||||
Tz& arg = zTad[nSampleIndexInBatch * zDimAstride];
|
||||
Tx Max = -minVal;
|
||||
|
||||
auto nSamplesPerBatch = nBatchIndex * numOfClassX * numOfSamples;
|
||||
auto nClassesPerSample = nSampleIndexInBatch * numOfClassX;
|
||||
for (sd::LongType nClass = 0; nClass < numOfClassX; nClass += 1) {
|
||||
auto nIndex = nSamplesPerBatch + nClassesPerSample + nClass;
|
||||
auto unifornLog =
|
||||
sd::math::sd_log<Tx, Tx>(-sd::math::sd_log<Tx, Tx>(rng.relativeT<Tx>(nIndex, minVal, maxVal)));
|
||||
Tx tValue = (xTad[nClass * xDimAstride] - unifornLog);
|
||||
if (tValue > Max) {
|
||||
Max = tValue;
|
||||
arg = nClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, batchValue, 1, 0, numOfSamples, 1);
|
||||
rng.rewindH(output.lengthOf() * numOfClassX);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void fillRandomMultiNomial(LaunchContext* context, graph::RandomGenerator& rng, NDArray& input, NDArray& output,
|
||||
const sd::LongType numOfSamples, const int dimC) {
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), output.dataType(), fillRandomMultiNomial_,
|
||||
(context, rng, input, output, numOfSamples, dimC), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
|
||||
// implementation is based on following article:
|
||||
// "MergeShuffle: A Very Fast, Parallel Random Permutation Algorithm", https://arxiv.org/abs/1508.03167
|
||||
|
||||
#include <graph/RandomGenerator.h>
|
||||
#include <helpers/Loops.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
#if NOT_EXCLUDED(OP_random_shuffle)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Fisher-Yates shuffle
|
||||
template <typename T>
|
||||
static void fisherYates(sd::graph::RandomGenerator& rng, T* buff, const sd::LongType& len, const sd::LongType& ews,
|
||||
sd::LongType ind) {
|
||||
for (sd::LongType i = len - 1; i > 0; --i) {
|
||||
const sd::LongType j = rng.relativeLong(ind++) % (i + 1);
|
||||
if (i != j) math::sd_swap<T>(buff[i * ews], buff[j * ews]);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// mutual shuffle of two adjacent already shuffled ranges with length len1 and (totLen - len1) correspondingly
|
||||
template <typename T>
|
||||
static void mergeShuffle(sd::graph::RandomGenerator& rng, T* buff, const sd::LongType& len1, const sd::LongType& totLen,
|
||||
const sd::LongType& ews, sd::LongType ind) {
|
||||
sd::LongType beg = 0; // beginning
|
||||
sd::LongType mid = len1; // middle
|
||||
|
||||
while (true) {
|
||||
if (rng.relativeLong(ind++) % 2) {
|
||||
if (mid == totLen) break;
|
||||
math::sd_swap<T>(buff[ews * beg], buff[ews * mid++]);
|
||||
} else {
|
||||
if (beg == mid) break;
|
||||
}
|
||||
++beg;
|
||||
}
|
||||
|
||||
// fisherYates
|
||||
while (beg < totLen) {
|
||||
const sd::LongType j = rng.relativeLong(ind++) % (beg + 1);
|
||||
if (beg != j) math::sd_swap<T>(buff[ews * beg], buff[ews * j]);
|
||||
++beg;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void randomShuffle_(NDArray& input, NDArray& output, sd::graph::RandomGenerator& rng, const bool isInplace) {
|
||||
const int firstDim = input.sizeAt(0);
|
||||
sd::LongType temp;
|
||||
|
||||
if (input.lengthOf() == 1 || firstDim == 1) {
|
||||
if (!isInplace) output.assign(&input);
|
||||
} else if (shape::isCommonVector(input.shapeInfo(), temp)) {
|
||||
NDArray* arr = &input;
|
||||
|
||||
if (!isInplace) {
|
||||
output.assign(&input);
|
||||
arr = &output;
|
||||
}
|
||||
|
||||
const sd::LongType ews = arr->ews();
|
||||
|
||||
const sd::LongType len = arr->lengthOf();
|
||||
const sd::LongType threshold = 1 << 22; // this number was deduced from diagram in article
|
||||
|
||||
int power = 0;
|
||||
while ((len >> power) > threshold) ++power;
|
||||
|
||||
const sd::LongType numChunks = 1 << power;
|
||||
|
||||
auto funcFisherYates = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; ++i) {
|
||||
sd::LongType offset = (len * i) >> power;
|
||||
sd::LongType currLen = ((len * (i + 1)) >> power) - offset;
|
||||
fisherYates<T>(rng, arr->bufferAsT<T>() + offset * ews, currLen, ews, offset);
|
||||
}
|
||||
};
|
||||
|
||||
auto funcMerge = PRAGMA_THREADS_FOR {
|
||||
for (int64_t i = start, k = 1; i < stop; i += increment, ++k) {
|
||||
sd::LongType offset = len * i >> power;
|
||||
sd::LongType len1 = (len * (i + increment / 2) >> power) - offset;
|
||||
sd::LongType totLen = (len * (i + increment) >> power) - offset;
|
||||
mergeShuffle<T>(rng, arr->bufferAsT<T>() + offset * ews, len1, totLen, ews, len * k + offset);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(funcFisherYates, 0, numChunks);
|
||||
|
||||
for (int j = 1; j < numChunks; j += j) samediff::Threads::parallel_for(funcMerge, 0, numChunks, 2 * j);
|
||||
|
||||
rng.rewindH((len + 1) * power);
|
||||
} else {
|
||||
std::vector<sd::LongType> zeroDim = {0};
|
||||
auto dimsToExclude = ShapeUtils::evalDimsToExclude(input.rankOf(), 1,zeroDim.data());
|
||||
|
||||
if (isInplace) {
|
||||
auto subArrsList = input.allTensorsAlongDimension(*dimsToExclude);
|
||||
|
||||
// Fisher-Yates shuffle
|
||||
for (int i = firstDim - 1; i > 0; --i) {
|
||||
const int j = rng.relativeInt(i) % (i + 1);
|
||||
if (i != j) subArrsList.at(i)->swapUnsafe(*subArrsList.at(j));
|
||||
}
|
||||
} else {
|
||||
auto subArrsListIn = input.allTensorsAlongDimension(*dimsToExclude);
|
||||
auto subArrsListOut = output.allTensorsAlongDimension(*dimsToExclude);
|
||||
delete dimsToExclude;
|
||||
std::vector<int> indices(firstDim);
|
||||
std::iota(indices.begin(), indices.end(), 0); // 0,1,2,3, ... firstDim-1
|
||||
|
||||
// shuffle indices
|
||||
fisherYates<int>(rng, indices.data(), firstDim, 1, 0);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; ++i) subArrsListOut.at(i)->assign(subArrsListIn.at(indices[i]));
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, firstDim);
|
||||
}
|
||||
|
||||
rng.rewindH(firstDim - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void randomShuffle(sd::LaunchContext* context, NDArray& input, NDArray& output, sd::graph::RandomGenerator& rng,
|
||||
const bool isInplace) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), randomShuffle_, (input, output, rng, isInplace), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/random_crop.h>
|
||||
#include <graph/Context.h>
|
||||
|
||||
#if NOT_EXCLUDED(OP_random_shuffle)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static sd::Status _randomCropFunctor(graph::Context& context, NDArray* input, NDArray* shape, NDArray* output,
|
||||
int seed) {
|
||||
graph::RandomGenerator rngX(context.getRng());
|
||||
// functions::random::RandomFunction<T>::template execTransform<randomOps::UniformDistribution<T>>(rng,
|
||||
// output->buffer(), output->shapeInfo(), std::vector<T>({T(0.), shape->e(last)}).data());
|
||||
// NativeOpExecutioner::execRandom(random::UniformDistribution, rng, output->buffer(), output->shapeInfo(),
|
||||
// std::vector<T>({T(0.), shape->e<T>(last)}).data());
|
||||
sd::LongType last = shape->lengthOf() - 1;
|
||||
|
||||
rngX.setSeed(seed);
|
||||
// functions::random::RandomFunction<T>::template execTransform<randomOps::UniformDistribution<T>>(rng,
|
||||
// output->buffer(), output->shapeInfo(), std::vector<T>({T(0.), shape->getScalar(last)}).data());
|
||||
for (sd::LongType e = 0; e < output->lengthOf(); ++e) {
|
||||
T put = rngX.relativeT<T>(e, static_cast<T>(0), static_cast<T>(shape->e<sd::LongType>(last)));
|
||||
output->p(e, put);
|
||||
}
|
||||
sd::LongType maxIndex = output->argMax();
|
||||
sd::LongType startPos = output->e<sd::LongType>(maxIndex);
|
||||
sd::LongType lastDim = input->sizeAt(-1);
|
||||
sd::LongType pos = 0;
|
||||
sd::LongType width = startPos + shape->e<sd::LongType>(last);
|
||||
if (width >= lastDim) {
|
||||
startPos -= (width - lastDim);
|
||||
width = lastDim;
|
||||
}
|
||||
|
||||
for (sd::LongType i = 0; i < input->lengthOf(); i += lastDim) {
|
||||
for (sd::LongType k = startPos; k < width && pos < output->lengthOf(); k++) {
|
||||
output->p(pos++, input->e<T>(i + k));
|
||||
}
|
||||
}
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
sd::Status randomCropFunctor(graph::Context& context, NDArray* input, NDArray* shape, NDArray* output, int seed) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return _randomCropFunctor, (context, input, shape, output, seed),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status _randomCropFunctor,
|
||||
(graph::Context & context, NDArray* input, NDArray* shape, NDArray* output, int seed),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 27.08.2018
|
||||
//
|
||||
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/range.h>
|
||||
#if NOT_EXCLUDED(OP_range)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// be careful: outVector must have c-order and ews = 1 !!!
|
||||
template <typename T>
|
||||
static void _range(NDArray& start, NDArray& delta, NDArray& outVector) {
|
||||
const sd::LongType len = outVector.lengthOf();
|
||||
|
||||
auto buff = outVector.bufferAsT<T>();
|
||||
auto s = start.e<T>(0);
|
||||
auto d = delta.e<T>(0);
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
buff[i] = s + i * d;
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, len);
|
||||
}
|
||||
|
||||
void range(sd::LaunchContext* context, NDArray& start, NDArray& delta, NDArray& outVector) {
|
||||
BUILD_SINGLE_SELECTOR(outVector.dataType(), _range, (start, delta, outVector), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _range, (NDArray& start, NDArray& delta, NDArray& outVector),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma, created on 16.04.2018
|
||||
//
|
||||
#include <array/ResultSet.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/reverse.h>
|
||||
#if NOT_EXCLUDED(OP_reverse)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
inline void swap(T* arr, sd::LongType from, sd::LongType to) {
|
||||
T tmp = arr[from];
|
||||
arr[from] = arr[to];
|
||||
arr[to] = tmp;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// this legacy op is written by raver119@gmail.com
|
||||
|
||||
template <typename T>
|
||||
static void reverseArray(sd::LaunchContext* context, void const* vinArr, sd::LongType const* inShapeBuffer,
|
||||
void* voutArr, sd::LongType const* outShapeBuffer, int numOfElemsToReverse = 0) {
|
||||
auto inArr = reinterpret_cast<T const*>(vinArr);
|
||||
auto outArr = reinterpret_cast<T*>(voutArr);
|
||||
|
||||
// Cache shape information
|
||||
const auto inRank = shape::rank(inShapeBuffer);
|
||||
const auto outRank = shape::rank(outShapeBuffer);
|
||||
const auto* inShape = shape::shapeOf(inShapeBuffer);
|
||||
const auto* outShape = shape::shapeOf(outShapeBuffer);
|
||||
const auto* inStride = shape::stride(inShapeBuffer);
|
||||
const auto* outStride = shape::stride(outShapeBuffer);
|
||||
|
||||
sd::LongType inLength = shape::length(inShapeBuffer);
|
||||
sd::LongType outLength = shape::length(outShapeBuffer);
|
||||
if (numOfElemsToReverse == 0) numOfElemsToReverse = inLength;
|
||||
sd::LongType sLength = numOfElemsToReverse - 1;
|
||||
|
||||
LongType inCoords[SD_MAX_RANK];
|
||||
LongType outCoords[SD_MAX_RANK];
|
||||
LongType inOffset;
|
||||
LongType outOffset;
|
||||
|
||||
// two step phase here
|
||||
if (inArr == outArr) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType e = start; e < stop; e++) {
|
||||
INDEX2COORDS(e, inRank, inShape, inCoords);
|
||||
COORDS2INDEX(inRank, inStride, inCoords, inOffset);
|
||||
INDEX2COORDS(sLength - e, inRank, inShape, outCoords);
|
||||
COORDS2INDEX(inRank, inStride, outCoords, outOffset);
|
||||
swap(const_cast<T*>(inArr), inOffset, outOffset);
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, numOfElemsToReverse / 2);
|
||||
} else {
|
||||
// single step phase here
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType e = start; e < stop; e++) {
|
||||
INDEX2COORDS(e, inRank, inShape, inCoords);
|
||||
COORDS2INDEX(inRank, inStride, inCoords, inOffset);
|
||||
INDEX2COORDS(sLength - e, outRank, outShape, outCoords);
|
||||
COORDS2INDEX(outRank, outStride, outCoords, outOffset);
|
||||
outArr[outOffset] = inArr[inOffset];
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(func, 0, numOfElemsToReverse);
|
||||
|
||||
if (inLength != numOfElemsToReverse) {
|
||||
auto f2 = PRAGMA_THREADS_FOR {
|
||||
for (sd::LongType e = start; e < stop; e++) {
|
||||
INDEX2COORDS(e, inRank, inShape, inCoords);
|
||||
COORDS2INDEX(inRank, inStride, inCoords, inOffset);
|
||||
INDEX2COORDS(e, outRank, outShape, outCoords);
|
||||
COORDS2INDEX(outRank, outStride, outCoords, outOffset);
|
||||
outArr[outOffset] = inArr[inOffset];
|
||||
}
|
||||
};
|
||||
samediff::Threads::parallel_for(f2, numOfElemsToReverse, inLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void reverseSequence_(sd::LaunchContext* context, NDArray* input, NDArray* seqLengths,
|
||||
NDArray* output, int seqDim, const int batchDim) {
|
||||
int posOfNonUnityDim = -1;
|
||||
if (input->isVector() || shape::isLikeVector(input->shapeInfo(), posOfNonUnityDim)) {
|
||||
if ((seqDim == 0 && input->sizeAt(0) == 1) || (batchDim == posOfNonUnityDim))
|
||||
output->assign(input);
|
||||
else
|
||||
helpers::reverseArray<T>(context, const_cast<NDArray*>(input)->buffer(), const_cast<NDArray*>(input)->shapeInfo(),
|
||||
output->buffer(), output->shapeInfo(), seqLengths->e<int>(0));
|
||||
} else {
|
||||
if (seqDim > batchDim) --seqDim;
|
||||
|
||||
std::vector<sd::LongType> batchDimVec = {batchDim};
|
||||
std::vector<sd::LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,batchDimVec.data());
|
||||
|
||||
auto inSubArrsSet = input->allTensorsAlongDimension(*dimensions);
|
||||
auto outSubArrsSet = output->allTensorsAlongDimension(*dimensions);
|
||||
delete dimensions;
|
||||
|
||||
for (int i = 0; i < inSubArrsSet.size(); ++i) {
|
||||
sd::LongType numOfElemsToReverse = seqLengths->e<sd::LongType>(i);
|
||||
|
||||
if (numOfElemsToReverse == 0 || numOfElemsToReverse == 1) {
|
||||
outSubArrsSet.at(i)->assign(inSubArrsSet.at(i));
|
||||
} else {
|
||||
auto inInnerSet = inSubArrsSet.at(i)->allTensorsAlongDimension({seqDim});
|
||||
auto outInnerSet = outSubArrsSet.at(i)->allTensorsAlongDimension({seqDim});
|
||||
for (int j = 0; j < inInnerSet.size(); ++j)
|
||||
helpers::reverseArray<T>(context, inInnerSet.at(j)->buffer(), inInnerSet.at(j)->shapeInfo(),
|
||||
outInnerSet.at(j)->buffer(), outInnerSet.at(j)->shapeInfo(), numOfElemsToReverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reverseSequence(sd::LaunchContext* context, NDArray* input, NDArray* seqLengths, NDArray* output,
|
||||
int seqDim, const int batchDim) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), reverseSequence_, (context, input, seqLengths, output, seqDim, batchDim),
|
||||
SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void reverse(sd::LaunchContext* context, NDArray* input, NDArray* output, const std::vector<LongType>* intArgs) {
|
||||
auto listOut = output->allTensorsAlongDimension(*intArgs);
|
||||
auto listIn = input->allTensorsAlongDimension(*intArgs);
|
||||
|
||||
NDArray *subArrIn, *subArrOut;
|
||||
|
||||
for (int i = 0; i < listIn.size(); ++i) { // listIn.size() = listOut.size()
|
||||
subArrIn = listIn.at(i);
|
||||
subArrOut = listOut.at(i);
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input->dataType(), helpers::reverseArray,
|
||||
(context, subArrIn->buffer(), subArrIn->shapeInfo(), subArrOut->buffer(), subArrOut->shapeInfo()),
|
||||
SD_COMMON_TYPES);
|
||||
}
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void reverseSequence_,
|
||||
(sd::LaunchContext * context, NDArray* input, NDArray* seqLengths, NDArray* output,
|
||||
int seqDim, const int batchDim),
|
||||
SD_COMMON_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void reverseArray,
|
||||
(sd::LaunchContext * context, void const* inArr, sd::LongType const* inShapeBuffer, void* outArr,
|
||||
sd::LongType const* outShapeBuffer, int numOfElemsToReverse),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,113 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/roll.h>
|
||||
#if NOT_EXCLUDED(OP_roll)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void rollFunctorLinear_(NDArray* input, NDArray* output, int shift, bool inplace) {
|
||||
auto source = input;
|
||||
if (!inplace) output->assign(input);
|
||||
|
||||
int fullLen = source->lengthOf();
|
||||
int actualShift = shift; // % fullLen; // shift already non-negative then
|
||||
if (actualShift < 0) {
|
||||
actualShift -= fullLen * (actualShift / fullLen - 1);
|
||||
} else
|
||||
actualShift %= fullLen;
|
||||
|
||||
if (actualShift) {
|
||||
int shiftCount = fullLen / actualShift - 1;
|
||||
int remainShift = fullLen % actualShift;
|
||||
|
||||
// stage 1) swap last actualShift elements with first ones.
|
||||
for (int e = 0; e < actualShift; ++e) {
|
||||
int sourceIndex = fullLen - actualShift + e;
|
||||
|
||||
auto _e0 = output->e<T>(e);
|
||||
auto _e1 = output->e<T>(sourceIndex);
|
||||
|
||||
output->p<T>(e, _e1);
|
||||
output->p<T>(sourceIndex, _e0);
|
||||
}
|
||||
|
||||
// stage 2) swap swapped actualShift elements with rest remainShiftCount times.
|
||||
for (int count = 1; count < shiftCount; ++count) {
|
||||
for (int e = 0; e < actualShift; ++e) {
|
||||
int destinationIndex = fullLen - (count + 1) * actualShift + e;
|
||||
int sourceIndex = fullLen - count * actualShift + e;
|
||||
|
||||
auto _e0 = output->e<T>(destinationIndex);
|
||||
auto _e1 = output->e<T>(sourceIndex);
|
||||
|
||||
output->p<T>(destinationIndex, _e1);
|
||||
output->p<T>(sourceIndex, _e0);
|
||||
}
|
||||
}
|
||||
|
||||
// stage 3) swap remainder of items.
|
||||
if (remainShift && shiftCount)
|
||||
for (int i = actualShift; i < 2 * actualShift; ++i) {
|
||||
auto _e0 = output->e<T>(i);
|
||||
auto _e1 = output->e<T>(i + remainShift);
|
||||
output->p<T>(i, _e1);
|
||||
output->p<T>(i + remainShift, _e0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rollFunctorFull(sd::LaunchContext* context, NDArray* input, NDArray* output, const std::vector<LongType>& shifts,
|
||||
const std::vector<LongType>& axes, bool inplace) {
|
||||
if (!inplace) output->assign(input);
|
||||
|
||||
auto source = output; // input;
|
||||
for (size_t i = 0; i < axes.size(); i++) {
|
||||
int axe = axes[i];
|
||||
ResultSet listOfTensors = source->allTensorsAlongDimension({axe});
|
||||
ResultSet listOfOutTensors = output->allTensorsAlongDimension({axe});
|
||||
int fullLen = listOfTensors.size();
|
||||
sd_debug("Roll: fullLen at last dimension is %d\n", fullLen);
|
||||
int theShift = shifts[i];
|
||||
if (theShift > 0) {
|
||||
theShift %= fullLen;
|
||||
} else {
|
||||
theShift -= fullLen * (theShift / fullLen - 1);
|
||||
}
|
||||
for (int k = 0; k < fullLen; k++) {
|
||||
rollFunctorLinear(context, listOfTensors.at(k), listOfOutTensors.at(k), theShift, true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void rollFunctorLinear(sd::LaunchContext* context, NDArray* input, NDArray* output, int shift, bool inplace) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), rollFunctorLinear_, (input, output, shift, inplace), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void rollFunctorLinear_, (NDArray * input, NDArray* output, int shift, bool inplace),
|
||||
SD_COMMON_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,408 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com)
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/s_t_b.h>
|
||||
#if NOT_EXCLUDED(OP_space_to_batch)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void batchToSpace_(NDArray& input, NDArray& output, const sd::LongType cropBottom,
|
||||
const sd::LongType cropTop, const sd::LongType cropLeft, const sd::LongType cropRight) {
|
||||
// input [bS, H * blockSize, W * blockSize, iC]
|
||||
// output [bS, H * blockSize - cropBottom - cropTop, W * blockSize - cropLeft - cropRight, iC]
|
||||
|
||||
// if (cropTop = cropBottom = cropRight = cropLeft = 0) shapes are the same
|
||||
// else:
|
||||
// oH -> [cropBottom, iH - cropTop]
|
||||
// oW -> [cropLeft, iH - cropRight]
|
||||
// xLen > zLen
|
||||
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
|
||||
const int rank = 4;
|
||||
|
||||
const sd::LongType* xShapeInfo = input.shapeInfo();
|
||||
const sd::LongType* zShapeInfo = output.shapeInfo();
|
||||
|
||||
const sd::LongType bS = xShapeInfo[1];
|
||||
const sd::LongType iH = xShapeInfo[2];
|
||||
const sd::LongType iW = xShapeInfo[3];
|
||||
const sd::LongType iC = xShapeInfo[4];
|
||||
|
||||
// loop through output array
|
||||
auto func = PRAGMA_THREADS_FOR_3D {
|
||||
for (auto b = start_x; b < stop_x; b += inc_x) {
|
||||
for (auto h = start_y; h < stop_y; h += inc_y) {
|
||||
for (auto w = start_z; w < stop_z; w += inc_z) {
|
||||
for (sd::LongType c = 0; c < iC; ++c) {
|
||||
const sd::LongType xOffset = b * xShapeInfo[5] + h * xShapeInfo[6] + w * xShapeInfo[7] + c * xShapeInfo[8];
|
||||
const sd::LongType zOffset = b * zShapeInfo[5] + (h - cropBottom) * zShapeInfo[6] +
|
||||
(w - cropLeft) * zShapeInfo[7] + c * zShapeInfo[8];
|
||||
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, cropBottom, iH - cropTop, 1, cropLeft, iW - cropRight, 1);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void batchToSpace_,
|
||||
(NDArray& input, NDArray& output, const sd::LongType cropBottom, const sd::LongType cropTop,
|
||||
const sd::LongType cropLeft, const sd::LongType cropRight),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void batchToSpace(sd::LaunchContext* context, NDArray input, NDArray& output, const sd::LongType cropBottom,
|
||||
const sd::LongType cropTop, const sd::LongType cropLeft, const sd::LongType cropRight,
|
||||
const sd::LongType blockSize) {
|
||||
// [bS*blockSize*blockSize, H/blockSize, W/blockSize, iC] is rearranged/permuted to [bS, oH, oW, iC]
|
||||
// oH = H - cropTop - cropBottom
|
||||
// oW = W - cropLeft - cropRight
|
||||
|
||||
std::vector<sd::LongType> shape = {blockSize, blockSize, output.sizeAt(0), input.sizeAt(1), input.sizeAt(2), input.sizeAt(3)};
|
||||
NDArray *inputRearranged0 = input.reshape(
|
||||
input.ordering(),shape);
|
||||
inputRearranged0->permutei({2, 3, 0, 4, 1, 5}, false, false);
|
||||
|
||||
if (input.lengthOf() == output.lengthOf())
|
||||
output.assign(inputRearranged0);
|
||||
else {
|
||||
std::vector<sd::LongType> temp = {output.sizeAt(0), input.sizeAt(1) * blockSize, input.sizeAt(2) * blockSize, input.sizeAt(3)};
|
||||
NDArray *inputRearranged1 = inputRearranged0->reshape(
|
||||
input.ordering(),
|
||||
temp);
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), batchToSpace_,
|
||||
(*inputRearranged1, output, cropBottom, cropTop, cropLeft, cropRight), SD_COMMON_TYPES);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void batchToSpaceND_(NDArray* input, NDArray* crop, NDArray* output,
|
||||
const LongType numOfSpatialDims) {
|
||||
// input [bS, H * blockShape[0], W * blockShape[1], iC]
|
||||
// output [bS, H * blockShape[0] - cropBottom - cropTop, W * blockShape[1] - cropLeft - cropRight, iC]
|
||||
|
||||
// if (cropTop = cropBottom = cropRight = cropLeft = 0) shapes are the same
|
||||
// else:
|
||||
// oH -> [cropBottom, iH - cropTop]
|
||||
// oW -> [cropLeft, iH - cropRight]
|
||||
// xLen >= zLen
|
||||
|
||||
const T* x = input->bufferAsT<T>();
|
||||
T* z = output->bufferAsT<T>();
|
||||
|
||||
const sd::LongType rank = input->rankOf();
|
||||
const sd::LongType zLen = output->lengthOf();
|
||||
|
||||
// loop through input array
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
sd::LongType zCoords[SD_MAX_RANK], xCoords[SD_MAX_RANK];
|
||||
|
||||
for (auto i = start; i < stop; i++) {
|
||||
INDEX2COORDS(i, rank, shape::shapeOf(output->shapeInfo()), zCoords);
|
||||
|
||||
memcpy(xCoords, zCoords, rank * sizeof(sd::LongType));
|
||||
|
||||
// evaluate spatial coordinates for x
|
||||
for (sd::LongType j = 1; j <= numOfSpatialDims; ++j)
|
||||
xCoords[j] += crop->e<sd::LongType>(j - 1, 0); // add crop left
|
||||
|
||||
sd::LongType zOffset, xOffset;
|
||||
COORDS2INDEX(rank, shape::stride(output->shapeInfo()), zCoords, zOffset);
|
||||
COORDS2INDEX(rank, shape::stride(input->shapeInfo()), xCoords, xOffset);
|
||||
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, zLen);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void batchToSpaceND_,
|
||||
(NDArray* input, NDArray* crop, NDArray* output, const sd::LongType numOfSpatialDims),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void batchToSpaceND(sd::LaunchContext* context, NDArray& input, NDArray& blockShape, NDArray& crop,
|
||||
NDArray& output){
|
||||
// 4D example, numOfSpatialDims = 2 - two spatial dimensions
|
||||
// [bS*blockShape[0]*blockShape[1], iH, iW, iC] is rearranged/permuted to [bS, iH*blockShape[0] - cropTop -
|
||||
// cropBottom, iW*blockShape[1] - cropLeft - cropRight, iC]
|
||||
|
||||
const sd::LongType rank = input.rankOf();
|
||||
const sd::LongType numOfSpatialDims = blockShape.sizeAt(0);
|
||||
|
||||
//*** construct reshaping std::vector for first reshape of input array ***//
|
||||
|
||||
std::vector<sd::LongType> temp(numOfSpatialDims + rank);
|
||||
|
||||
sd::LongType i;
|
||||
for (i = 0; i < numOfSpatialDims; ++i) temp[i] = blockShape.e<sd::LongType>(i);
|
||||
temp[i++] = output.sizeAt(0);
|
||||
for (sd::LongType j = 1; j < rank; ++i, ++j) temp[i] = input.sizeAt(j);
|
||||
|
||||
NDArray *inputRearranged0 = input.reshape(input.ordering(), temp);
|
||||
|
||||
//*** construct permuting std::vector for permutation of input array ***//
|
||||
|
||||
temp[0] = numOfSpatialDims;
|
||||
|
||||
for (i = 1; i <= numOfSpatialDims; ++i) {
|
||||
temp[2 * i - 1] = numOfSpatialDims + i;
|
||||
temp[2 * i] = i - 1;
|
||||
}
|
||||
for (i = 2 * numOfSpatialDims + 1; i < static_cast<sd::LongType>(temp.size()); ++i) temp[i] = i;
|
||||
|
||||
inputRearranged0->permutei(temp, false, false);
|
||||
|
||||
if (input.lengthOf() == output.lengthOf()) {
|
||||
output.assign(inputRearranged0);
|
||||
} else {
|
||||
//*** construct reshaping std::vector for second reshape of input array ***//
|
||||
|
||||
temp.resize(rank);
|
||||
|
||||
temp[0] = output.sizeAt(0);
|
||||
|
||||
for (i = 1; i < rank; ++i)
|
||||
temp[i] = (i <= numOfSpatialDims) ? input.sizeAt(i) * blockShape.e<sd::LongType>(i - 1) : input.sizeAt(i);
|
||||
|
||||
NDArray *inputRearranged1 = inputRearranged0->reshape(input.ordering(), temp);
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), batchToSpaceND_, (inputRearranged1, &crop, &output, numOfSpatialDims),
|
||||
SD_COMMON_TYPES);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void spaceToBatch_(NDArray& input, NDArray& output, const sd::LongType padBottom,
|
||||
const sd::LongType padTop, const sd::LongType padLeft, const sd::LongType padRight) {
|
||||
// input [bS, H * blockSize - padBottom - padTop, W * blockSize - padLeft - padRight, iC]
|
||||
// output [bS, H * blockSize, W * blockSize, iC]
|
||||
|
||||
// if (padTop = padBottom = padRight = padLeft = 0) shapes are the same
|
||||
// else:
|
||||
// iH -> [padBottom, oH - padTop]
|
||||
// iW -> [padLeft, oW - padRight]
|
||||
// zLen > xLen
|
||||
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
|
||||
const int rank = 4;
|
||||
|
||||
const sd::LongType* xShapeInfo = input.shapeInfo();
|
||||
const sd::LongType* zShapeInfo = output.shapeInfo();
|
||||
|
||||
const sd::LongType bS = zShapeInfo[1];
|
||||
const sd::LongType oH = zShapeInfo[2];
|
||||
const sd::LongType oW = zShapeInfo[3];
|
||||
const sd::LongType iC = zShapeInfo[4];
|
||||
|
||||
// loop through output array
|
||||
auto func = PRAGMA_THREADS_FOR_2D {
|
||||
for (auto b = start_x; b < stop_x; b += inc_x) {
|
||||
for (auto h = start_y; h < stop_y; h += inc_y) {
|
||||
for (sd::LongType w = 0; w < oW; ++w) {
|
||||
for (sd::LongType c = 0; c < iC; ++c) {
|
||||
const sd::LongType zOffset = b * zShapeInfo[5] + h * zShapeInfo[6] + w * zShapeInfo[7] + c * zShapeInfo[8];
|
||||
|
||||
if (h >= padBottom && h < oH - padTop && w >= padLeft && w < oW - padRight) {
|
||||
const sd::LongType xOffset = b * xShapeInfo[5] + (h - padBottom) * xShapeInfo[6] +
|
||||
(w - padLeft) * xShapeInfo[7] + c * xShapeInfo[8];
|
||||
z[zOffset] = x[xOffset];
|
||||
} else
|
||||
z[zOffset] = 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, bS, 1, 0, oH, 1);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void spaceToBatch_,
|
||||
(NDArray& input, NDArray& output, const sd::LongType padBottom, const sd::LongType padTop,
|
||||
const sd::LongType padLeft, const sd::LongType padRight),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void spaceToBatch(sd::LaunchContext* context, NDArray& input, NDArray& output, const sd::LongType padBottom,
|
||||
const sd::LongType padTop, const sd::LongType padLeft, const sd::LongType padRight,
|
||||
const sd::LongType blockSize) {
|
||||
// [bS, iH, iW, iC] is rearranged/permuted to [bS*blockSize*blockSize, (iH + padBottom + padTop)/blockSize, (iW +
|
||||
// padLeft + padRight)/blockSize, iC]
|
||||
|
||||
std::vector<sd::LongType> shape1 = {blockSize, blockSize, input.sizeAt(0), output.sizeAt(1), output.sizeAt(2), output.sizeAt(3)};
|
||||
NDArray *outputRearranged0 = output.reshape(
|
||||
output.ordering(), shape1,
|
||||
false);
|
||||
outputRearranged0->permutei({2, 3, 0, 4, 1, 5}, false, false);
|
||||
|
||||
if (input.lengthOf() == output.lengthOf()) {
|
||||
outputRearranged0->assign(&input);
|
||||
} else {
|
||||
std::vector<sd::LongType> shape2 = {input.sizeAt(0), output.sizeAt(1) * blockSize, output.sizeAt(2) * blockSize, output.sizeAt(3)};
|
||||
NDArray *outputRearranged1 = outputRearranged0->reshape(
|
||||
output.ordering(),
|
||||
shape2, false);
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), spaceToBatch_,
|
||||
(input, *outputRearranged1, padBottom, padTop, padLeft, padRight), SD_COMMON_TYPES);
|
||||
|
||||
if (output.buffer() != outputRearranged1->buffer()) outputRearranged0->assign(outputRearranged1);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void spaceToBatchND_(NDArray& input, NDArray& padding, NDArray& output,
|
||||
const LongType numOfSpatialDims) {
|
||||
// 4D example
|
||||
// input [bS, H * blockShape[0] - padBottom - padTop, W * blockShape[1] - padLeft - padRight, iC]
|
||||
// output [bS, H * blockShape[0], W * blockShape[1], iC]
|
||||
|
||||
// if (padTop = padBottom = padRight = padLeft = 0) shapes are the same
|
||||
// else:
|
||||
// iH -> [padBottom, oH - padTop]
|
||||
// iW -> [padLeft, oW - padRight]
|
||||
// zLen > xLen
|
||||
|
||||
const T* x = input.bufferAsT<T>();
|
||||
T* z = output.bufferAsT<T>();
|
||||
|
||||
const int rank = input.rankOf();
|
||||
const sd::LongType zLen = output.lengthOf();
|
||||
|
||||
// loop through output array
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
sd::LongType zCoords[SD_MAX_RANK], xCoords[SD_MAX_RANK];
|
||||
|
||||
for (sd::LongType i = start; i < stop; i++) {
|
||||
INDEX2COORDS(i, rank, shape::shapeOf(output.shapeInfo()), zCoords);
|
||||
|
||||
sd::LongType zOffset;
|
||||
COORDS2INDEX(rank, shape::stride(output.shapeInfo()), zCoords, zOffset);
|
||||
|
||||
memcpy(xCoords, zCoords, rank * sizeof(LongType));
|
||||
|
||||
bool within = true;
|
||||
|
||||
for (sd::LongType j = 1; j <= numOfSpatialDims; ++j) {
|
||||
const auto padLeft = padding.e<sd::LongType>(j - 1, 0);
|
||||
const auto padRight = padding.e<sd::LongType>(j - 1, 1);
|
||||
|
||||
within &= zCoords[j] >= padLeft && zCoords[j] < output.sizeAt(j) - padRight;
|
||||
|
||||
if (!within) break;
|
||||
|
||||
xCoords[j] = zCoords[j] - padLeft; // get coordinates for x
|
||||
}
|
||||
|
||||
if (within) {
|
||||
sd::LongType xOffset;
|
||||
COORDS2INDEX(rank, shape::stride(input.shapeInfo()), xCoords, xOffset);
|
||||
z[zOffset] = x[xOffset];
|
||||
} else {
|
||||
z[zOffset] = 0.f;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, zLen);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void spaceToBatchND_,
|
||||
(NDArray& input, NDArray& padding, NDArray& output,
|
||||
const sd::LongType numOfSpatialDims),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void spaceToBatchND(sd::LaunchContext* context, NDArray& input, NDArray& blockShape, NDArray& padding,
|
||||
NDArray& output) {
|
||||
// 4D example with two spatial dimensions
|
||||
// [bS, iH, iW, iC] is rearranged/permuted to [bS*blockShape[0]*blockShape[1], (iH + padBottom +
|
||||
// padTop)/blockShape[0], (iW + padLeft + padRight)/blockShape[1], iC]
|
||||
|
||||
const sd::LongType rank = input.rankOf();
|
||||
|
||||
const sd::LongType numOfSpatialDims = blockShape.sizeAt(0);
|
||||
|
||||
//*** construct reshaping std::vector for first reshape of output array ***//
|
||||
std::vector<sd::LongType> temp(numOfSpatialDims + rank);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < numOfSpatialDims; ++i) temp[i] = blockShape.e<sd::LongType>(i);
|
||||
temp[i++] = input.sizeAt(0);
|
||||
for (int j = 1; j < rank; ++i, ++j) temp[i] = output.sizeAt(j);
|
||||
|
||||
NDArray *outputRearranged0 = output.reshape(output.ordering(), temp, false);
|
||||
|
||||
//*** construct permuting std::vector for permutation of output array ***//
|
||||
|
||||
temp[0] = numOfSpatialDims;
|
||||
|
||||
for (i = 1; i <= numOfSpatialDims; ++i) {
|
||||
temp[2 * i - 1] = numOfSpatialDims + i;
|
||||
temp[2 * i] = i - 1;
|
||||
}
|
||||
for (i = 2 * numOfSpatialDims + 1; i < static_cast<int>(temp.size()); ++i) temp[i] = i;
|
||||
|
||||
outputRearranged0->permutei(temp, false, false);
|
||||
|
||||
// ****** //
|
||||
|
||||
if (input.lengthOf() == output.lengthOf()) {
|
||||
outputRearranged0->assign(&input);
|
||||
} else {
|
||||
//*** construct reshaping std::vector for second reshape of output array ***//
|
||||
temp.resize(rank);
|
||||
|
||||
temp[0] = input.sizeAt(0);
|
||||
|
||||
for (i = 1; i < rank; ++i)
|
||||
temp[i] = (i <= numOfSpatialDims) ? output.sizeAt(i) * blockShape.e<sd::LongType>(i - 1) : output.sizeAt(i);
|
||||
|
||||
NDArray *outputRearranged1 = outputRearranged0->reshape(output.ordering(), temp, false);
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), spaceToBatchND_, (input, padding, *outputRearranged1, numOfSpatialDims),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
if (output.buffer() != outputRearranged1->buffer()) outputRearranged0->assign(outputRearranged1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <ops/declarable/helpers/s_t_d.h>
|
||||
#if NOT_EXCLUDED(OP_space_to_depth)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static void _spaceTodepth_(NDArray&input, NDArray *output, int block_size, bool isNHWC) {
|
||||
auto input_ptr = reinterpret_cast<T const *>(input.buffer());
|
||||
auto output_ptr = reinterpret_cast<T *>(output->buffer());
|
||||
|
||||
const int batch_size = input.sizeAt(0);
|
||||
const int input_depth = isNHWC ? input.sizeAt(3) : input.sizeAt(1);
|
||||
const int input_height = isNHWC ? input.sizeAt(1) : input.sizeAt(2);
|
||||
const int input_width = isNHWC ? input.sizeAt(2) : input.sizeAt(3);
|
||||
|
||||
const int output_depth = isNHWC ? output->sizeAt(3) : output->sizeAt(1);
|
||||
const int output_height = isNHWC ? output->sizeAt(1) : output->sizeAt(2);
|
||||
const int output_width = isNHWC ? output->sizeAt(2) : output->sizeAt(3);
|
||||
|
||||
const int input_depth_by_output_height = input_depth * output_height;
|
||||
|
||||
const int output_area = output_width * output_height;
|
||||
const int output_depth_by_output_area = output_depth * output_area;
|
||||
|
||||
if (isNHWC) {
|
||||
const int total_count = batch_size * input_height * input_width * input_depth;
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto inp_idx = start; inp_idx < stop; inp_idx++) {
|
||||
// inp_idx = d + input_depth * (w + input_width * (h + input_height * b))
|
||||
const int d = inp_idx % input_depth;
|
||||
const int inp_idx2 = inp_idx / input_depth;
|
||||
const int w = inp_idx2 % input_width;
|
||||
const int inp_idx3 = inp_idx2 / input_width;
|
||||
const int h = inp_idx3 % input_height;
|
||||
const int b = inp_idx3 / input_height;
|
||||
|
||||
const int out_h = h / block_size;
|
||||
const int offset_h = h % block_size;
|
||||
const int out_w = w / block_size;
|
||||
const int offset_w = w % block_size;
|
||||
const int offset_d = (offset_h * block_size + offset_w) * input_depth;
|
||||
const int out_d = d + offset_d;
|
||||
|
||||
const int out_idx = out_d + output_depth * (out_w + output_width * (out_h + output_height * b));
|
||||
*(output_ptr + out_idx) = *(input_ptr + inp_idx);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, total_count);
|
||||
} else {
|
||||
const int total_count = batch_size * output_depth_by_output_area;
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto inp_idx = start; inp_idx < stop; inp_idx++) {
|
||||
const int n_iC_oY_bY_oX = inp_idx / block_size;
|
||||
const int bX = inp_idx - n_iC_oY_bY_oX * block_size;
|
||||
|
||||
const int n_iC_oY_bY = n_iC_oY_bY_oX / output_width;
|
||||
const int oX = n_iC_oY_bY_oX - n_iC_oY_bY * output_width;
|
||||
|
||||
const int n_iC_oY = n_iC_oY_bY / block_size;
|
||||
const int bY = n_iC_oY_bY - n_iC_oY * block_size;
|
||||
|
||||
const int n = n_iC_oY / input_depth_by_output_height;
|
||||
const int iC_oY = n_iC_oY - n * input_depth_by_output_height;
|
||||
|
||||
const int output_idx =
|
||||
oX + (((n * block_size + bY) * block_size + bX) * input_depth_by_output_height + iC_oY) * output_width;
|
||||
|
||||
*(output_ptr + output_idx) = *(input_ptr + inp_idx);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, total_count);
|
||||
}
|
||||
}
|
||||
|
||||
void _spaceTodepth(sd::LaunchContext *context, NDArray&input, NDArray *output, int block_size, bool isNHWC) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), _spaceTodepth_, (input, output, block_size, isNHWC), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _spaceTodepth_,
|
||||
(NDArray&input, NDArray *output, int block_size, bool isNHWC), SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,208 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 raver119@gmail.com
|
||||
//
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/scatter.h>
|
||||
|
||||
#include <numeric>
|
||||
#if NOT_EXCLUDED(OP_scatter)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - indices, z - input/output
|
||||
template <typename T>
|
||||
sd::LongType checkIndices_(NDArray& indices, NDArray& output, const int axis) {
|
||||
std::atomic<int64_t> numOfBadIndx{0};
|
||||
|
||||
const auto x = indices.bufferAsT<T>();
|
||||
|
||||
const auto xShapeInfo = indices.shapeInfo();
|
||||
const auto zShapeInfo = output.shapeInfo();
|
||||
|
||||
// Cache shape information
|
||||
const auto xRank = shape::rank(xShapeInfo);
|
||||
const auto* xShape = shape::shapeOf(xShapeInfo);
|
||||
const auto* xStride = shape::stride(xShapeInfo);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
sd::LongType xCoords[SD_MAX_RANK];
|
||||
|
||||
for (auto i = start; i < stop; i++) {
|
||||
INDEX2COORDS(i, xRank, xShape, xCoords);
|
||||
|
||||
sd::LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
|
||||
const sd::LongType currentInd = x[xOffset];
|
||||
|
||||
if (currentInd >= shape::sizeAt(zShapeInfo, axis == -1 ? xCoords[xRank - 1] : axis)) {
|
||||
++numOfBadIndx;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, indices.lengthOf());
|
||||
|
||||
return numOfBadIndx;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
sd::LongType checkIndices(sd::LaunchContext* context, NDArray& indices, NDArray& output, const int axis) {
|
||||
BUILD_SINGLE_SELECTOR(indices.dataType(), return checkIndices_, (indices, output, axis), SD_INTEGER_TYPES);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void scatter(sd::LaunchContext* context, pairwise::Ops op, NDArray& indices, NDArray& updates,
|
||||
NDArray& output, const bool lock) {
|
||||
const int outRank = output.rankOf();
|
||||
const int indRank = indices.rankOf();
|
||||
const int updRank = updates.rankOf();
|
||||
const sd::LongType indLen = indices.lengthOf();
|
||||
|
||||
if (outRank == 1) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
sd::LongType idx = indices.e<sd::LongType>(i);
|
||||
NDArray *out = output({idx, idx + 1});
|
||||
NDArray updateE = updates.e(i);
|
||||
out->applyPairwiseTransform(op, &updateE);
|
||||
delete out;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, indLen, 1, lock ? 1 : sd::Environment::getInstance().maxThreads());
|
||||
} else { // outRank > 1
|
||||
|
||||
int sizeOfDims = indRank;
|
||||
if (outRank == updRank && indices.isVector()) sizeOfDims = 1;
|
||||
|
||||
std::vector<sd::LongType > dimsToExcludeUpd(sizeOfDims);
|
||||
std::iota(dimsToExcludeUpd.begin(), dimsToExcludeUpd.end(), 0);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
NDArray *outSubArr = output(indices.e<sd::LongType>(i), std::vector<sd::LongType >({0}));
|
||||
NDArray *updSubArr = updates(i, dimsToExcludeUpd);
|
||||
outSubArr->applyPairwiseTransform(op, updSubArr);
|
||||
delete outSubArr;
|
||||
delete updSubArr;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, indLen, 1, lock ? 1 : sd::Environment::getInstance().maxThreads());
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void scatterND(sd::LaunchContext* context, pairwise::Ops op, NDArray& indices, NDArray& updates,
|
||||
NDArray& output, const bool lock) {
|
||||
const sd::LongType indLen = indices.lengthOf();
|
||||
const int outRank = output.rankOf();
|
||||
const int indRank = indices.rankOf();
|
||||
const sd::LongType indLastDim = indices.sizeAt(-1);
|
||||
|
||||
if (outRank == 1) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
sd::LongType idx = indices.e<sd::LongType>(i);
|
||||
NDArray *out = output({idx, idx + 1});
|
||||
NDArray updatesE = updates.e(i);
|
||||
ExtraArguments *extraArgs = nullptr;
|
||||
out->applyPairwiseTransform(op, &updatesE, extraArgs);
|
||||
delete out;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, indLen, 1, lock ? 1 : sd::Environment::getInstance().maxThreads());
|
||||
} else {
|
||||
std::vector<sd::LongType> dims = {indRank - 1};
|
||||
std::vector<sd::LongType > *dimsToExcludeInd = ShapeUtils::evalDimsToExclude(indRank, dims.size(),dims.data());
|
||||
std::vector<sd::LongType > dimsToExcludeUpd(indRank - 1);
|
||||
std::iota(dimsToExcludeUpd.begin(), dimsToExcludeUpd.end(), 0);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
std::vector<sd::LongType> idxRangeOut(2 * outRank, 0);
|
||||
|
||||
for (auto i = start; i < stop; i++) {
|
||||
NDArray *indSubArr = indices(i, *dimsToExcludeInd);
|
||||
for (sd::LongType j = 0; j < indLastDim; ++j) {
|
||||
idxRangeOut[2 * j] = indSubArr->e<sd::LongType>(j);
|
||||
idxRangeOut[2 * j + 1] = idxRangeOut[2 * j] + 1;
|
||||
}
|
||||
|
||||
NDArray *outSubArr = output(idxRangeOut);
|
||||
NDArray *updSubArr = updates(i, dimsToExcludeUpd);
|
||||
|
||||
outSubArr->applyPairwiseTransform(op, updSubArr);
|
||||
delete outSubArr;
|
||||
delete indSubArr;
|
||||
delete updSubArr;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, indLen / indLastDim, 1,
|
||||
lock ? 1 : sd::Environment::getInstance().maxThreads());
|
||||
|
||||
delete dimsToExcludeInd;
|
||||
}
|
||||
}
|
||||
|
||||
void scatterForLoss(sd::LaunchContext* context, NDArray& indices, NDArray& updates, NDArray& output,
|
||||
const bool calcGrad) {
|
||||
const sd::LongType indicesLen = indices.lengthOf();
|
||||
std::vector<sd::LongType> dim = {-1};
|
||||
std::vector<sd::LongType > *dimsToExclude = ShapeUtils::evalDimsToExclude(updates.rankOf(), dim.size(),dim.data());
|
||||
|
||||
if (!calcGrad) {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto subArr = updates(i, *dimsToExclude);
|
||||
auto curr = indices.e<sd::LongType>(i);
|
||||
output.p(i, curr);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, indicesLen);
|
||||
|
||||
delete dimsToExclude;
|
||||
} else {
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto subArr = updates(i, *dimsToExclude);
|
||||
auto ind = indices.e<sd::LongType>(i);
|
||||
auto curr = subArr->e<sd::LongType>(ind) - 1.;
|
||||
subArr->p(ind,curr);
|
||||
delete subArr;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, indicesLen);
|
||||
delete dimsToExclude;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
|
||||
//
|
||||
#include <helpers/Loops.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
#if NOT_EXCLUDED(OP_scatter_update)
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void scatterUpdate(sd::LaunchContext* context, NDArray& input, NDArray& updates, const std::vector<LongType>* intArgs) {
|
||||
sd::LongType opCode = (*intArgs)[0];
|
||||
sd::LongType dimSize = (*intArgs)[1];
|
||||
sd::LongType e;
|
||||
sd::LongType limg = 2 + dimSize;
|
||||
std::vector<sd::LongType> tadDimensions(dimSize);
|
||||
for (e = 2; e < limg; e++) tadDimensions[e - 2] = (*intArgs)[e];
|
||||
|
||||
std::vector<sd::LongType> *dimsToExclude = ShapeUtils::evalDimsToExclude(input.rankOf(), tadDimensions.size(),tadDimensions.data());
|
||||
|
||||
// increasing counter to skip numIndices
|
||||
e++;
|
||||
std::vector<sd::LongType> indices;
|
||||
for (; e < static_cast<sd::LongType>(intArgs->size()); e++) indices.push_back((*intArgs)[e]);
|
||||
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto inSubArr = input(indices[i], *dimsToExclude, true);
|
||||
auto updSubArr = updates(i, *dimsToExclude, true);
|
||||
if (inSubArr->lengthOf() != updSubArr->lengthOf()) {
|
||||
delete inSubArr;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (opCode) {
|
||||
case 0:
|
||||
inSubArr->applyPairwiseTransform(pairwise::Add, updSubArr, inSubArr);
|
||||
break;
|
||||
case 1:
|
||||
inSubArr->applyPairwiseTransform(pairwise::Subtract, updSubArr, inSubArr);
|
||||
break;
|
||||
case 2:
|
||||
inSubArr->applyPairwiseTransform(pairwise::Multiply, updSubArr, inSubArr);
|
||||
break;
|
||||
case 3:
|
||||
inSubArr->applyPairwiseTransform(pairwise::Divide, updSubArr, inSubArr);
|
||||
break;
|
||||
case 4:
|
||||
inSubArr->applyPairwiseTransform(pairwise::ReverseSubtract, updSubArr, inSubArr);
|
||||
break;
|
||||
case 5:
|
||||
inSubArr->applyPairwiseTransform(pairwise::ReverseDivide, updSubArr, inSubArr);
|
||||
break;
|
||||
case 6:
|
||||
inSubArr->applyPairwiseTransform(pairwise::CopyPws, updSubArr, inSubArr);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
delete inSubArr;
|
||||
delete updSubArr;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, indices.size());
|
||||
|
||||
|
||||
delete dimsToExclude;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void scatterSimple(sd::LaunchContext* context, const int opId, NDArray& input, NDArray& updates,
|
||||
NDArray& indices, const std::vector<LongType>& dimensions) {
|
||||
// updates and indices have same length
|
||||
const sd::LongType len = indices.lengthOf();
|
||||
|
||||
switch (opId) {
|
||||
case 6: { // copy
|
||||
auto func = PRAGMA_THREADS_FOR {
|
||||
for (auto i = start; i < stop; i++) {
|
||||
auto inSubArr = input(i, dimensions);
|
||||
auto curr = indices.e(i);
|
||||
inSubArr->p(indices.t<sd::LongType>(i), &curr);
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_for(func, 0, len);
|
||||
} break;
|
||||
|
||||
default:
|
||||
THROW_EXCEPTION("helpers::scatterSimple: operation is not implemented for given id !");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user