chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,291 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <ops/declarable/helpers/BarnesHutTsne.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// count rows kernel - count input pRows and pCols and put result onto pRowCounts
|
||||
// pRowCounts - array of ints, with length N
|
||||
// pRows - array of ints with length N, vals from 0 to N-1
|
||||
// pCols - array of ints with length < N and vals between 0 and max(pRows)
|
||||
//
|
||||
static SD_KERNEL void countRowsKernel(int* pRowCounts, int const* pRows, int const* pCols, LongType N) {
|
||||
auto start = blockIdx.x * blockDim.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
for (int n = threadIdx.x + start; n < N; n += step) {
|
||||
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;
|
||||
// loop between near pRows
|
||||
for (int m = pRows[pCols[i]]; m < pRows[pCols[i] + 1]; m++)
|
||||
if (pCols[m] == n) { // mark index as existed with columns array
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
|
||||
atomicAdd(&pRowCounts[n], 1);
|
||||
|
||||
if (!present) // increment row counter for given index
|
||||
atomicAdd(&pRowCounts[pCols[i]], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// row counter caller
|
||||
LongType barnes_row_count(NDArray* rowP, NDArray* colP, LongType N, NDArray& rowCounts) {
|
||||
int* pRowCounts = reinterpret_cast<int*>(rowCounts.specialBuffer());
|
||||
int const* pRows = reinterpret_cast<int const*>(rowP->specialBuffer());
|
||||
int const* pCols = reinterpret_cast<int const*>(colP->specialBuffer());
|
||||
auto stream = rowCounts.getContext()->getCudaStream();
|
||||
countRowsKernel<<<1, 1, 128, *stream>>>(pRowCounts, pRows, pCols, N);
|
||||
sd::DebugHelper::checkErrorCode(stream, "countRows failed");
|
||||
|
||||
NDArray numElementsArr = rowCounts.sumNumber();
|
||||
auto numElements = numElementsArr.e<LongType>(0);
|
||||
return numElements;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// extend symRowP with pRowCounts array vals
|
||||
// pRowCounts - int array with length N
|
||||
// symRowP - int array with length N+1
|
||||
// N - given array length
|
||||
//
|
||||
static SD_KERNEL void fillUpsymRow(int const* pRowCounts, int* symRowP, int N) {
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
for (int n = start; n < N + 1; n += step) { // to avoid race condition use shift only for given index
|
||||
symRowP[n] = 0;
|
||||
for (int i = 0; i < n; i++) atomicAdd(&symRowP[n], pRowCounts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// symmetrize routine kernel
|
||||
// pRows - rows buffer (ints)
|
||||
// pCols - column buffer (ints) with vals between 0 and max(pRows)
|
||||
// pVals - values vector (floats)
|
||||
// symRowP - ints, shifted pRows
|
||||
// symColP - ints, shifted pCols,
|
||||
// offset - ints, shitfs
|
||||
// pOutput - result matrix (floats)
|
||||
// N - pRows length
|
||||
//
|
||||
template <typename T>
|
||||
static SD_KERNEL void symmetrizeKernel(int const* pRows, int const* pCols, T const* pVals, int* symRowP, int* symColP,
|
||||
int* offset, T* pOutput, int N) {
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
for (int n = start; n < N; n += step) {
|
||||
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 (colP[i], n) is not present, there is no addition involved
|
||||
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)) {
|
||||
atomicAdd(&offset[n], 1);
|
||||
|
||||
if (colPI != n) atomicAdd(&offset[colPI], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// symmetrize algorithm itself
|
||||
//
|
||||
template <typename T>
|
||||
static void barnes_symmetrize_(NDArray* rowP, NDArray* colP, NDArray* valP, LongType N,
|
||||
NDArray* outputRows, NDArray* outputCols, NDArray* outputVals, NDArray* rowCounts) {
|
||||
int const* pRows = reinterpret_cast<int const*>(rowP->specialBuffer());
|
||||
int* symRowP = reinterpret_cast<int*>(outputRows->specialBuffer());
|
||||
int* pRowCounts = reinterpret_cast<int*>(rowCounts->specialBuffer());
|
||||
auto stream = outputCols->getContext()->getCudaStream();
|
||||
// fill up syRowP array
|
||||
fillUpsymRow<<<1, N, 128, *stream>>>(pRowCounts, symRowP, N);
|
||||
sd::DebugHelper::checkErrorCode(stream, "fillUpsymRow failed");
|
||||
|
||||
outputRows->syncToHost();
|
||||
int* symColP = reinterpret_cast<int*>(outputCols->specialBuffer());
|
||||
int const* pCols = reinterpret_cast<int const*>(colP->specialBuffer());
|
||||
T const* pVals = reinterpret_cast<T const*>(valP->specialBuffer());
|
||||
T* pOutput = reinterpret_cast<T*>(outputVals->specialBuffer());
|
||||
auto offsetArr = NDArrayFactory::create<int>('c', {N});
|
||||
int* offset = reinterpret_cast<int*>(offsetArr->specialBuffer());
|
||||
// symmetrize itself
|
||||
symmetrizeKernel<T><<<1, 1, 1024, *stream>>>(pRows, pCols, pVals, symRowP, symColP, offset, pOutput, N);
|
||||
sd::DebugHelper::checkErrorCode(stream, "symmetrizeKernel failed");
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// symmetrize caller and adoption
|
||||
//
|
||||
void barnes_symmetrize(NDArray* rowP, NDArray* colP, NDArray* valP, LongType N,
|
||||
NDArray* outputRows, NDArray* outputCols, NDArray* outputVals, NDArray* rowCounts) {
|
||||
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);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// edge forces implementation
|
||||
//
|
||||
template <typename T>
|
||||
static SD_KERNEL void edgeForcesKernel(int const* pRows, int const* pCols, T const* dataP, T const* vals, T* outputP,
|
||||
int N, int colCount, int rowSize) {
|
||||
// std::vector<T> buffer(colCount);
|
||||
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
for (int n = start; n < N; n += step) {
|
||||
int start = pRows[n];
|
||||
int end = pRows[n + 1];
|
||||
int shift = n * colCount;
|
||||
for (int i = start; i < end; i++) {
|
||||
T const* thisSlice = dataP + pCols[i] * colCount;
|
||||
T res = static_cast<T>(1);
|
||||
|
||||
for (int k = 0; k < colCount; k++) {
|
||||
auto valTemp = dataP[shift + k] - thisSlice[k]; // thisSlice[k];
|
||||
res += valTemp * valTemp; // (dataP[shift + k] * dataP[shift + k] - 2 * dataP[shift + k] * thisSlice[k] +
|
||||
// thisSlice[k] * thisSlice[k])
|
||||
}
|
||||
res = vals[i] / res;
|
||||
for (int k = 0; k < colCount; k++)
|
||||
math::atomics::sd_atomicAdd(&outputP[shift + k], T((dataP[shift + k] - thisSlice[k]) * res));
|
||||
}
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// edge forces algorithm
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
static void barnes_edge_forces_(NDArray* rowP, NDArray * colP, NDArray * valP, int N,
|
||||
NDArray * data, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {data, rowP, colP, valP, valP});
|
||||
T const* dataP = reinterpret_cast<T const*>(data->specialBuffer());
|
||||
T const* vals = reinterpret_cast<T const*>(valP->specialBuffer());
|
||||
T* outputP = reinterpret_cast<T*>(output->specialBuffer());
|
||||
int const* pRows = reinterpret_cast<int const*>(rowP->specialBuffer());
|
||||
int const* pCols = reinterpret_cast<int const*>(colP->specialBuffer());
|
||||
int colCount = data->columns();
|
||||
// auto shift = 0;
|
||||
auto rowSize = sizeof(T) * colCount;
|
||||
auto stream = output->getContext()->getCudaStream();
|
||||
edgeForcesKernel<T><<<1, 128, 1024, *stream>>>(pRows, pCols, dataP, vals, outputP, N, colCount, rowSize);
|
||||
sd::DebugHelper::checkErrorCode(stream, "edgeForces failed");
|
||||
|
||||
NDArray::registerSpecialUse({output}, {rowP, colP, valP, data});
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// edge forces caller
|
||||
//
|
||||
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);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// gains - run a function T((x + 2.) * sd::math::sd_sign<T,T>(grad) != sd::math::sd_sign<T,T>(eps)) + T(x * 0.8 *
|
||||
// sd::math::sd_sign<T,T>(grad) != sd::math::sd_sign<T,T>(eps)); for all members in input and put all in output
|
||||
//
|
||||
template <typename T>
|
||||
void barnes_gains_(NDArray* input, NDArray* gradX, NDArray* epsilon, NDArray* output) {
|
||||
auto gainsInternal = LAMBDA_TTT(x, grad, eps) {
|
||||
T res = math::sd_sign<T, T>(grad) != math::sd_sign<T, T>(eps) ? x + T(.2) : x * T(.8);
|
||||
if (res < .01) res = .01;
|
||||
return res;
|
||||
});
|
||||
|
||||
input->applyTriplewiseLambda(gradX, epsilon, gainsInternal, output);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// gains caller
|
||||
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);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cell contains - check cells for given point
|
||||
//
|
||||
bool cell_contains(NDArray* corner, NDArray* width, NDArray* point, LongType dimension) {
|
||||
auto cornerMinusWidth = *corner - *width;
|
||||
auto cornerPlusWidth = *corner + *width;
|
||||
// executes on host side, so sync all to host memory
|
||||
cornerMinusWidth.syncToHost();
|
||||
cornerPlusWidth.syncToHost();
|
||||
for (LongType i = 0; i < dimension; i++) {
|
||||
if (cornerMinusWidth.e<double>(i) > point->e<double>(i)) return false;
|
||||
if (cornerPlusWidth.e<double>(i) < point->e<double>(i)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,740 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/activations.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
void SD_KERNEL preluCuda(const void *vx, const LongType *xShapeInfo, const void *vy, const LongType *yShapeInfo,
|
||||
void *vz) {
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
const auto y = reinterpret_cast<const Y *>(vy);
|
||||
auto z = reinterpret_cast<X *>(vz);
|
||||
|
||||
__shared__ LongType xzLen;
|
||||
__shared__ int xzRank, yRank;
|
||||
__shared__ const LongType *xzShape;
|
||||
__shared__ const LongType *xzStride;
|
||||
__shared__ const LongType *yShape;
|
||||
__shared__ const LongType *yStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xzLen = shape::length(xShapeInfo);
|
||||
xzRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
xzShape = shape::shapeOf(xShapeInfo);
|
||||
xzStride = shape::stride(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
for (int i = tid; i < xzLen; i += blockDim.x * gridDim.x) {
|
||||
INDEX2COORDS(i, xzRank, xzShape, coords);
|
||||
|
||||
LongType xzOffset;
|
||||
COORDS2INDEX(xzRank, xzStride, coords, xzOffset);
|
||||
const auto xVal = x[xzOffset];
|
||||
|
||||
if (xVal < 0) {
|
||||
for (LongType j = 0; j < yRank; ++j)
|
||||
if (yShapeInfo[j + 1] == 1) coords[j + 1] = 0;
|
||||
|
||||
LongType yOffset;
|
||||
COORDS2INDEX(yRank, yStride, coords + 1, yOffset);
|
||||
z[xzOffset] = xVal * y[yOffset];
|
||||
} else {
|
||||
z[xzOffset] = xVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
void preluCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo, const void *vy,
|
||||
const LongType *yShapeInfo, void *vz) {
|
||||
preluCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz);
|
||||
sd::DebugHelper::checkGlobalErrorCode("prelu failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void prelu(LaunchContext *context, NDArray *input, NDArray *alpha, NDArray *output) {
|
||||
PointersManager manager(context, "prelu");
|
||||
|
||||
dim3 launchDims = getLaunchDims("prelu");
|
||||
|
||||
const auto xType = input->dataType();
|
||||
const auto yType = alpha->dataType();
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {&input, &alpha});
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
xType, preluCudaLauncher,
|
||||
(launchDims.x, launchDims.y, launchDims.z, context->getCudaStream(), input->specialBuffer(),
|
||||
input->specialShapeInfo(), alpha->specialBuffer(), alpha->specialShapeInfo(), output->specialBuffer()),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {&input, &alpha});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
void SD_KERNEL preluBPCuda(const void *vIn, const LongType *inShapeInfo, const void *vAlpha,
|
||||
const LongType *alphaShapeInfo, const void *vdLdO, const LongType *dLdOShapeInfo,
|
||||
void *vdLdI, const LongType *dLdIShapeInfo, void *vdLdA,
|
||||
const LongType *dLdAShapeInfo) {
|
||||
const auto in = reinterpret_cast<const X *>(vIn);
|
||||
const auto alpha = reinterpret_cast<const Y *>(vAlpha);
|
||||
const auto dLdO = reinterpret_cast<const Y *>(vdLdO);
|
||||
auto dLdI = reinterpret_cast<Y *>(vdLdI);
|
||||
auto dLdA = reinterpret_cast<Y *>(vdLdA);
|
||||
|
||||
__shared__ LongType inLen, totalThreads;
|
||||
__shared__ int inRank, alphaRank;
|
||||
__shared__ const LongType *inShape;
|
||||
__shared__ const LongType *inStride;
|
||||
__shared__ const LongType *dLdOStride;
|
||||
__shared__ const LongType *dLdIStride;
|
||||
__shared__ const LongType *alphaStride;
|
||||
__shared__ const LongType *dLdAStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
inLen = shape::length(inShapeInfo);
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
inRank = shape::rank(inShapeInfo);
|
||||
alphaRank = shape::rank(alphaShapeInfo);
|
||||
|
||||
// Cache shapes and strides
|
||||
inShape = shape::shapeOf(inShapeInfo);
|
||||
inStride = shape::stride(inShapeInfo);
|
||||
dLdOStride = shape::stride(dLdOShapeInfo);
|
||||
dLdIStride = shape::stride(dLdIShapeInfo);
|
||||
alphaStride = shape::stride(alphaShapeInfo);
|
||||
dLdAStride = shape::stride(dLdAShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
for (int i = tid; i < inLen; i += totalThreads) {
|
||||
INDEX2COORDS(i, inRank, inShape, coords);
|
||||
|
||||
LongType inOffset, dLdOOffset, dLdIOffset;
|
||||
COORDS2INDEX(inRank, inStride, coords, inOffset);
|
||||
COORDS2INDEX(inRank, dLdOStride, coords, dLdOOffset);
|
||||
COORDS2INDEX(inRank, dLdIStride, coords, dLdIOffset);
|
||||
|
||||
const auto xVal = in[inOffset];
|
||||
const auto grO = dLdO[dLdOOffset];
|
||||
|
||||
if (xVal < 0) {
|
||||
for (LongType j = 0; j < alphaRank; ++j)
|
||||
if (alphaShapeInfo[j + 1] == 1) coords[j + 1] = 0;
|
||||
|
||||
LongType alphaOffset, dLdAOffset;
|
||||
COORDS2INDEX(alphaRank, alphaStride, coords + 1, alphaOffset);
|
||||
COORDS2INDEX(alphaRank, dLdAStride, coords + 1, dLdAOffset);
|
||||
|
||||
dLdI[dLdIOffset] = grO * alpha[alphaOffset];
|
||||
|
||||
math::atomics::sd_atomicAdd<Y>(&dLdA[dLdAOffset], static_cast<Y>(grO * xVal));
|
||||
} else {
|
||||
dLdI[dLdIOffset] = grO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
void SD_HOST preluBPCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const void *vIn, const LongType *inShapeInfo,
|
||||
const void *vAlpha, const LongType *alphaShapeInfo, const void *vdLdO,
|
||||
const LongType *dLdOShapeInfo, void *vdLdI, const LongType *dLdIShapeInfo,
|
||||
void *vdLdA, const LongType *dLdAShapeInfo) {
|
||||
preluBPCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(
|
||||
vIn, inShapeInfo, vAlpha, alphaShapeInfo, vdLdO, dLdOShapeInfo, vdLdI, dLdIShapeInfo, vdLdA, dLdAShapeInfo);
|
||||
sd::DebugHelper::checkGlobalErrorCode("prelu bp failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void preluBP(LaunchContext *context, NDArray *input, NDArray *alpha, NDArray *dLdO, NDArray *dLdI,
|
||||
NDArray *dLdA) {
|
||||
dLdA->nullify();
|
||||
|
||||
PointersManager manager(context, "preluBP");
|
||||
|
||||
dim3 launchDims = getLaunchDims("prelu");
|
||||
|
||||
const auto xType = input->dataType();
|
||||
const auto zType = alpha->dataType();
|
||||
|
||||
NDArray::prepareSpecialUse({dLdI, dLdA}, {input, alpha, dLdO});
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
xType, preluBPCudaLauncher,
|
||||
(launchDims.x, launchDims.y, launchDims.z, context->getCudaStream(), input->specialBuffer(),
|
||||
input->specialShapeInfo(), alpha->specialBuffer(), alpha->specialShapeInfo(), dLdO->specialBuffer(),
|
||||
dLdO->specialShapeInfo(), dLdI->specialBuffer(), dLdI->specialShapeInfo(), dLdA->specialBuffer(),
|
||||
dLdA->specialShapeInfo()),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&dLdI, &dLdA}, {input, alpha, dLdO});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_DEVICE void softMaxForVectorCuda(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo) {
|
||||
auto inBuff = reinterpret_cast<const T *>(vx);
|
||||
auto outBuff = reinterpret_cast<T *>(vz);
|
||||
|
||||
__shared__ T shmemMax;
|
||||
__shared__ T shmemSum;
|
||||
__shared__ LongType tadLen;
|
||||
__shared__ int xRank;
|
||||
__shared__ int zRank;
|
||||
__shared__ const LongType *xShape;
|
||||
__shared__ const LongType *xStride;
|
||||
__shared__ const LongType *zShape;
|
||||
__shared__ const LongType *zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
tadLen = shape::length(xShapeInfo);
|
||||
shmemMax = -DataTypeUtils::max<T>();
|
||||
shmemSum = 0.f;
|
||||
|
||||
// Cache ranks
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
// Cache shapes and strides
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
T sum = static_cast<T>(0.f);
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
|
||||
// Calculate max using cached values
|
||||
for (LongType j = 0; j < tadLen; ++j) {
|
||||
INDEX2COORDS(j, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
max = math::sd_max<T>(max, inBuff[xOffset]);
|
||||
}
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
|
||||
// Calculate exp(x - max) and sum using cached values
|
||||
for (LongType j = 0; j < tadLen; ++j) {
|
||||
INDEX2COORDS(j, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
T temp = math::sd_exp<T, T>(inBuff[xOffset] - max);
|
||||
INDEX2COORDS(j, zRank, zShape, zCoords);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
outBuff[zOffset] = temp;
|
||||
sum += temp;
|
||||
}
|
||||
|
||||
// Final division step using cached values
|
||||
for (LongType j = 0; j < tadLen; ++j) {
|
||||
INDEX2COORDS(j, zRank, zShape, zCoords);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
outBuff[zOffset] /= sum;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SD_KERNEL softMaxForVectorCudaGlobal(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, LongType numOfSubArrs) {
|
||||
softMaxForVectorCuda<T>(vx, xShapeInfo, vz, zShapeInfo);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void softMaxForVectorCudaLauncher(const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, LongType numTads) {
|
||||
|
||||
softMaxForVectorCudaGlobal<T><<<1, SD_CUDA_BLOCK_SIZE, 1024, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, numTads);
|
||||
sd::DebugHelper::checkGlobalErrorCode("softmax failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
SD_KERNEL void softmaxEws1Kernel(const T *input, const LongType *inputOffsets, T *output,
|
||||
const LongType *outputOffsets,
|
||||
LongType numOfSubArrs, LongType tadLen) {
|
||||
int i = blockIdx.x; // Each block handles one TAD
|
||||
|
||||
if (i >= numOfSubArrs) return; // Out-of-bounds check for TADs
|
||||
|
||||
auto inBuff = input + inputOffsets[i];
|
||||
auto outBuff = output + outputOffsets[i];
|
||||
|
||||
__shared__ T shmemMax;
|
||||
__shared__ T shmemSum;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
shmemMax = -DataTypeUtils::max<T>();
|
||||
shmemSum = 0.f;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
|
||||
// Calculate max
|
||||
for (LongType j = threadIdx.x; j < tadLen; j+= gridDim.x) {
|
||||
math::atomics::sd_atomicMax(&shmemMax, inBuff[j]);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Calculate exp(x - max) and sum
|
||||
for (LongType j = threadIdx.x; j < tadLen; j += gridDim.x) {
|
||||
T temp = math::sd_exp<T, T>(inBuff[j] - shmemMax);
|
||||
outBuff[j] = temp;
|
||||
math::atomics::sd_atomicAdd(&shmemSum, temp);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Final division step
|
||||
for (LongType j = threadIdx.x; j < tadLen; j += blockDim.x) {
|
||||
outBuff[j] /= shmemSum;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
template <typename T>
|
||||
SD_KERNEL static void softMaxCuda(const void *vx, const LongType *xTadShapeInfo, const LongType *xOffsets,
|
||||
void *vz, const LongType *zTadShapeInfo, const LongType *zOffsets, LongType numTads) {
|
||||
int i = blockIdx.x;
|
||||
if(i >= numTads) return;
|
||||
|
||||
const auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
const auto *xTad = x + xOffsets[blockIdx.x];
|
||||
auto *zTad = z + zOffsets[blockIdx.x];
|
||||
softMaxForVectorCuda<T>(xTad, xTadShapeInfo, zTad, zTadShapeInfo);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
static void softMaxEws1CudaLauncher(const int blocksPerGrid,
|
||||
const int threadsPerBlock,
|
||||
const int sharedMem,
|
||||
const cudaStream_t *stream,
|
||||
const void *vx, const LongType *xOffsets, void *vz,
|
||||
const LongType *zOffsets, LongType numTads, LongType tadLength) {
|
||||
|
||||
|
||||
|
||||
auto reCastInputs = reinterpret_cast<const T *>(vx);
|
||||
auto reCastOutputs = reinterpret_cast<T *>(vz);
|
||||
softmaxEws1Kernel<T>
|
||||
<<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(reCastInputs,
|
||||
xOffsets,
|
||||
reCastOutputs,
|
||||
zOffsets,
|
||||
numTads,
|
||||
tadLength);
|
||||
sd::DebugHelper::checkGlobalErrorCode("softmaxews failed");
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void softMaxCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const void *vx, const LongType *xTadShapeInfo,
|
||||
const LongType *xOffsets, void *vz, const LongType *zTadShapeInfo,
|
||||
const LongType *zOffsets, LongType numTads) {
|
||||
|
||||
|
||||
softMaxCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xTadShapeInfo, xOffsets, vz, zTadShapeInfo,
|
||||
zOffsets ,numTads);
|
||||
sd::DebugHelper::checkGlobalErrorCode("softmax failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void softmax(LaunchContext *context, NDArray *input, NDArray *output, const int dimension) {
|
||||
const int rank = input->rankOf();
|
||||
|
||||
PointersManager manager(context, "helpers::softmax");
|
||||
|
||||
if (input->isVector()) {
|
||||
if (rank == 1 || input->sizeAt(dimension) != 1) {
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), softMaxForVectorCudaLauncher,
|
||||
(context->getCudaStream(), input->specialBuffer(), input->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(),1),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
} else
|
||||
*output = 1.;
|
||||
} else {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), {dimension});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), {dimension});
|
||||
|
||||
dim3 softmaxDims = getSoftmaxDims(packZ->numberOfTads());
|
||||
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), softMaxCudaLauncher,
|
||||
(softmaxDims.x, softmaxDims.y,
|
||||
softmaxDims.z,
|
||||
context->getCudaStream(),
|
||||
input->specialBuffer(),
|
||||
packX->specialShapeInfo(),
|
||||
packX->specialOffsets(), output->specialBuffer(),
|
||||
packZ->specialShapeInfo(),
|
||||
packZ->specialOffsets(),packX->numberOfTads()),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
|
||||
}
|
||||
|
||||
manager.synchronize();
|
||||
|
||||
output->tickWriteDevice();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void SD_KERNEL logSoftMaxForVectorCuda(const void *vx, const LongType *xzShapeInfo, void *vz) {
|
||||
// logic of this kernel is based on assumption gridDim = 1
|
||||
|
||||
const auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
__shared__ LongType len;
|
||||
__shared__ int numOfIters;
|
||||
__shared__ int xzRank;
|
||||
__shared__ const LongType *xzShape;
|
||||
__shared__ const LongType *xzStride;
|
||||
__shared__ T shmem[SD_CUDA_BLOCK_SIZE];
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
len = shape::length(xzShapeInfo);
|
||||
numOfIters = (len + blockDim.x - 1) / blockDim.x; // ceil (len / blockDim.x)
|
||||
|
||||
// Cache rank, shape and stride information
|
||||
xzRank = shape::rank(xzShapeInfo);
|
||||
xzShape = shape::shapeOf(xzShapeInfo);
|
||||
xzStride = shape::stride(xzShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
T temp = -DataTypeUtils::max<T>(); // set start value to compare with at first iteration, FIXME: what if T is unsigned ??
|
||||
|
||||
// ************ evaluate max element in input array x ************ //
|
||||
for (int i = 0; i < numOfIters; ++i) {
|
||||
const LongType elemIdx = i * blockDim.x + threadIdx.x;
|
||||
if (elemIdx < len) {
|
||||
LongType offset;
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
INDEX2COORDS(elemIdx, xzRank, xzShape, coords);
|
||||
COORDS2INDEX(xzRank, xzStride, coords, offset);
|
||||
shmem[threadIdx.x] = (threadIdx.x != 0) ? x[offset] : math::sd_max<T>(x[offset], temp); // take into account max element evaluated on previous iteration and stored in temp
|
||||
} else {
|
||||
shmem[threadIdx.x] = -DataTypeUtils::max<T>(); // FIXME: what if T is unsigned ??
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
for (int s = blockDim.x / 2; s > 0; s /= 2) {
|
||||
if (threadIdx.x < s) shmem[threadIdx.x] = math::sd_max<T>(shmem[threadIdx.x], shmem[threadIdx.x + s]);
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
temp = shmem[0]; // save max value calculated at current iteration
|
||||
}
|
||||
|
||||
const T max = temp;
|
||||
temp = 0;
|
||||
|
||||
// ************ evaluate value of exp(x[offset] - max) per each element, store it to shared memory shmem ************
|
||||
// at the same time evaluate sum of exponents, sum will be stored in shmem[0]
|
||||
for (int i = 0; i < numOfIters; ++i) {
|
||||
const LongType elemIdx = i * blockDim.x + threadIdx.x;
|
||||
if (elemIdx < len) {
|
||||
LongType offset;
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
INDEX2COORDS(elemIdx, xzRank, xzShape, coords);
|
||||
COORDS2INDEX(xzRank, xzStride, coords, offset);
|
||||
z[offset] = math::sd_exp<T, T>(x[offset] - max);
|
||||
shmem[threadIdx.x] = (threadIdx.x != 0) ? z[offset] : (z[offset] + temp); // take into account sum element evaluated on previous iteration and stored in temp
|
||||
} else {
|
||||
shmem[threadIdx.x] = 0;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
for (int s = blockDim.x / 2; s > 0; s /= 2) {
|
||||
if (threadIdx.x < s) shmem[threadIdx.x] += shmem[threadIdx.x + s];
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
temp = shmem[0]; // save sum calculated at current iteration
|
||||
}
|
||||
|
||||
// ************ evaluate log(z[offset] / sum) ************ //
|
||||
for (int i = 0; i < numOfIters; ++i) {
|
||||
const LongType elemIdx = i * blockDim.x + threadIdx.x;
|
||||
if (elemIdx < len) { // Added bounds check that was missing in original
|
||||
LongType offset;
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
INDEX2COORDS(elemIdx, xzRank, xzShape, coords);
|
||||
COORDS2INDEX(xzRank, xzStride, coords, offset);
|
||||
z[offset] = math::sd_log<T, T>(z[offset] / shmem[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void logSoftMaxForVectorCudaLauncher(const cudaStream_t *stream, const void *vx, const LongType *xzShapeInfo,
|
||||
void *vz) {
|
||||
dim3 launchDims = getLaunchDims("softmax");
|
||||
logSoftMaxForVectorCuda<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(vx, xzShapeInfo, vz);
|
||||
sd::DebugHelper::checkGlobalErrorCode("logsoftmax failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void logSoftmax(LaunchContext *context, NDArray *input, NDArray *output, const int dimension) {
|
||||
if (!input->isActualOnDeviceSide()) input->syncToDevice();
|
||||
const int rank = input->rankOf();
|
||||
|
||||
if (input->isVector()) {
|
||||
if (rank == 1 || input->sizeAt(dimension) != 1) {
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input->dataType(), logSoftMaxForVectorCudaLauncher,
|
||||
(context->getCudaStream(), input->specialBuffer(), input->specialShapeInfo(), output->specialBuffer()),
|
||||
SD_FLOAT_TYPES);
|
||||
input->tickReadDevice();
|
||||
} else
|
||||
*output = 0.;
|
||||
} else {
|
||||
std::vector<LongType> dim = {static_cast<LongType>(dimension)};
|
||||
auto maxAlongDim = const_cast<NDArray *>(input)->reduceAlongDimension(reduce::Max, &dim, true);
|
||||
auto inputMinusMax = *input - maxAlongDim;
|
||||
inputMinusMax.applyTransform(transform::Exp, output); // output contains exponents temporarily
|
||||
auto sumAlongDim = output->reduceAlongDimension(reduce::Sum, &dim, true);
|
||||
*output /= sumAlongDim;
|
||||
output->applyTransform(transform::Log, output);
|
||||
input->tickReadDevice();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "helpers::logSoftmax");
|
||||
manager.synchronize();
|
||||
|
||||
output->tickWriteDevice();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void SD_KERNEL softMaxDerivForVectorCuda(const void *vx, const LongType *xzShapeInfo, void *vz) {
|
||||
// logic of this kernel is based on assumption gridDim = 1
|
||||
|
||||
const auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
__shared__ LongType len;
|
||||
__shared__ int numOfIters;
|
||||
__shared__ int xzRank;
|
||||
__shared__ const LongType *xzShape;
|
||||
__shared__ const LongType *xzStride;
|
||||
__shared__ T shmem[SD_CUDA_BLOCK_SIZE];
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
len = shape::length(xzShapeInfo);
|
||||
numOfIters = (len + blockDim.x - 1) / blockDim.x; // ceil (len / blockDim.x)
|
||||
|
||||
// Cache rank, shape and stride information
|
||||
xzRank = shape::rank(xzShapeInfo);
|
||||
xzShape = shape::shapeOf(xzShapeInfo);
|
||||
xzStride = shape::stride(xzShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
T temp = -DataTypeUtils::max<T>(); // set start value to compare with at first iteration, FIXME: what if T is unsigned ??
|
||||
|
||||
// ************ evaluate max element in input array x ************ //
|
||||
for (int i = 0; i < numOfIters; ++i) {
|
||||
const LongType elemIdx = i * blockDim.x + threadIdx.x;
|
||||
if (elemIdx < len) {
|
||||
LongType offset;
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
INDEX2COORDS(elemIdx, xzRank, xzShape, coords);
|
||||
COORDS2INDEX(xzRank, xzStride, coords, offset);
|
||||
shmem[threadIdx.x] = (threadIdx.x != 0) ? x[offset] : math::sd_max<T>(x[offset], temp); // take into account max element evaluated on previous iteration and stored in temp
|
||||
} else {
|
||||
shmem[threadIdx.x] = -DataTypeUtils::max<T>(); // FIXME: what if T is unsigned ??
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
for (int s = blockDim.x / 2; s > 0; s /= 2) {
|
||||
if (threadIdx.x < s) shmem[threadIdx.x] = math::sd_max<T>(shmem[threadIdx.x], shmem[threadIdx.x + s]);
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
temp = shmem[0]; // save max value calculated at current iteration
|
||||
}
|
||||
|
||||
const T max = temp;
|
||||
temp = 0;
|
||||
|
||||
// ************ evaluate value of exp(x[offset] - max) per each element, store it to shared memory shmem ************
|
||||
// at the same evaluate sum of exponents, sum will be stored in shmem[0]
|
||||
for (int i = 0; i < numOfIters; ++i) {
|
||||
const LongType elemIdx = i * blockDim.x + threadIdx.x;
|
||||
if (elemIdx < len) {
|
||||
LongType offset;
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
INDEX2COORDS(elemIdx, xzRank, xzShape, coords);
|
||||
COORDS2INDEX(xzRank, xzStride, coords, offset);
|
||||
z[offset] = math::sd_exp<T, T>(x[offset] - max);
|
||||
shmem[threadIdx.x] = (threadIdx.x != 0) ? z[offset] : (z[offset] + temp); // take into account sum element evaluated on previous iteration and stored in temp
|
||||
} else {
|
||||
shmem[threadIdx.x] = 0;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
for (int s = blockDim.x / 2; s > 0; s /= 2) {
|
||||
if (threadIdx.x < s) shmem[threadIdx.x] += shmem[threadIdx.x + s];
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
temp = shmem[0]; // save sum calculated at current iteration
|
||||
}
|
||||
|
||||
// ************ evaluate (z[offset] / sum) and derivative z[offset] = z[offset] * (1 - z[offset]) ************ //
|
||||
for (int i = 0; i < numOfIters; ++i) {
|
||||
const LongType elemIdx = i * blockDim.x + threadIdx.x;
|
||||
if (elemIdx >= len) continue;
|
||||
|
||||
LongType offset;
|
||||
sd::LongType coords[SD_MAX_RANK];
|
||||
INDEX2COORDS(elemIdx, xzRank, xzShape, coords);
|
||||
COORDS2INDEX(xzRank, xzStride, coords, offset);
|
||||
z[offset] /= shmem[0];
|
||||
z[offset] *= (1.f - z[offset]); // derivative
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void softMaxDerivForVectorCudaLauncher(const cudaStream_t *stream, const void *vx, const LongType *xzShapeInfo,
|
||||
void *vz) {
|
||||
dim3 launchDims = getLaunchDims("softmax");
|
||||
|
||||
softMaxDerivForVectorCuda<T><<<launchDims.x,launchDims.y, launchDims.z, *stream>>>(vx, xzShapeInfo, vz);
|
||||
sd::DebugHelper::checkGlobalErrorCode("softmax derivative failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void softmaxDerivative(LaunchContext *context, NDArray *input, NDArray *output, const int dimension) {
|
||||
if (!input->isActualOnDeviceSide()) input->syncToDevice();
|
||||
const int rank = input->rankOf();
|
||||
LongType temp;
|
||||
|
||||
if (shape::isCommonVector(input->shapeInfo(), temp)) {
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input->dataType(), softMaxDerivForVectorCudaLauncher,
|
||||
(context->getCudaStream(), input->specialBuffer(), input->specialShapeInfo(), output->specialBuffer()),
|
||||
SD_FLOAT_TYPES);
|
||||
input->tickReadDevice();
|
||||
} else {
|
||||
std::vector<LongType> dim = {static_cast<LongType>(dimension)};
|
||||
auto maxAlongDim = const_cast<NDArray *>(input)->reduceAlongDimension(reduce::Max, &dim, true);
|
||||
auto inputMinusMax = *input - maxAlongDim;
|
||||
inputMinusMax.applyTransform(transform::Exp, output); // output contains exponents temporarily
|
||||
auto sumAlongDim = output->reduceAlongDimension(reduce::Sum, &dim, true);
|
||||
*output /= sumAlongDim;
|
||||
*output *= (1.f - *output); // derivative
|
||||
input->tickReadDevice();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "helpers::softmaxDerivative");
|
||||
manager.synchronize();
|
||||
|
||||
output->tickWriteDevice();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void thresholdRelu_(NDArray *input, double threshold, NDArray *output) {
|
||||
auto routine = LAMBDA_T(_x, threshold) { return _x > (T)threshold ? _x : (T)0.f; });
|
||||
input->applyLambda(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>
|
||||
void thresholdReluDerivative_(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(dLdO, derivative, output);
|
||||
}
|
||||
|
||||
void thresholdReluDerivative(LaunchContext *context, NDArray *input, double threshold, NDArray *dLdO,
|
||||
NDArray *output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), thresholdReluDerivative_, (input, threshold, dLdO, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,157 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/addBias.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void addBiasCuda(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const bool isNCHW) {
|
||||
// bias [oC]
|
||||
|
||||
// if(input_rank == 4)
|
||||
// input and output have same shapes: [bS, oH, oW, oC] (NHWC) or [bS, oC, oH, oW] (NCHW)
|
||||
// if(input_rank == 5)
|
||||
// input and output have same shapes: [bS, oD, oH, oW, oC] (NHWC) or [bS, oD, oC, oH, oW] (NCHW)
|
||||
|
||||
const X* x = reinterpret_cast<const X*>(vx);
|
||||
const Y* y = reinterpret_cast<const Y*>(vy);
|
||||
X* z = reinterpret_cast<X*>(vz);
|
||||
|
||||
__shared__ LongType rank, channelPosition, posOfNonUnityDim;
|
||||
__shared__ LongType len, *sharedMem;
|
||||
__shared__ bool xzSameOffsets, xzAreSame;
|
||||
__shared__ const LongType *xShape;
|
||||
__shared__ const LongType *xStride;
|
||||
__shared__ const LongType *zStride;
|
||||
__shared__ const LongType *yStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
rank = shape::rank(xShapeInfo); // xRank == zRank
|
||||
xzSameOffsets = shape::haveSameShapeAndStrides(xShapeInfo, zShapeInfo);
|
||||
len = shape::length(xShapeInfo);
|
||||
channelPosition = isNCHW ? 1 : rank - 1; // second or last
|
||||
xzAreSame = x == z;
|
||||
|
||||
// Cache shapes and strides
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
|
||||
shape::isCommonVector(yShapeInfo, posOfNonUnityDim);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < len; i += blockDim.x * gridDim.x) {
|
||||
INDEX2COORDS(i, rank, xShape, coords);
|
||||
|
||||
LongType xOffsets;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffsets);
|
||||
LongType zOffsets;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffsets);
|
||||
LongType yOffsets = coords[channelPosition] * yStride[posOfNonUnityDim];
|
||||
|
||||
if (xzAreSame)
|
||||
z[zOffsets] += static_cast<X>(y[yOffsets]);
|
||||
else
|
||||
z[zOffsets] = static_cast<X>(x[xOffsets]) + static_cast<X>(y[yOffsets]);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void addBiasCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const bool isNCHW) {
|
||||
addBiasCuda<X, Y>
|
||||
<<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo, isNCHW);
|
||||
sd::DebugHelper::checkGlobalErrorCode("addbias failed");
|
||||
|
||||
}
|
||||
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void addBias2DCuda(const void* vx, const void* vy, void* vz, uint32_t blocks, uint32_t length) {
|
||||
auto y = reinterpret_cast<const Y*>(vy);
|
||||
|
||||
for (uint32_t b = blockIdx.x; b < blocks; b += gridDim.x) {
|
||||
auto x = reinterpret_cast<const X*>(vx) + length * b;
|
||||
auto z = reinterpret_cast<X*>(vz) + length * b;
|
||||
|
||||
for (uint32_t e = threadIdx.x; e < length; e += blockDim.x) {
|
||||
z[e] = x[e] + y[e];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Y>
|
||||
static void addBias2DCudaLauncher(const cudaStream_t* stream, const void* vx, const void* vy, void* vz, uint32_t blocks,
|
||||
uint32_t length) {
|
||||
dim3 dims = getAddBiasDims(2, 2);
|
||||
|
||||
addBias2DCuda<X, Y><<<dims.x, dims.y, dims.z, *stream>>>(vx, vy, vz, blocks, length);
|
||||
sd::DebugHelper::checkGlobalErrorCode("addbias 2d failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void addBias(graph::Context& block, NDArray& input, NDArray& bias, NDArray& output, const bool isNCHW) {
|
||||
PointersManager manager(block.launchContext(), "addBias");
|
||||
NDArray::prepareSpecialUse({&output}, {&input, &bias});
|
||||
|
||||
if (input.rankOf() == 2 && bias.rankOf() == 1 && input.ordering() == 'c' && output.ordering() == 'c' &&
|
||||
input.sizeAt(1) == bias.sizeAt(0)) {
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), bias.dataType(), addBias2DCudaLauncher,
|
||||
(block.launchContext()->getCudaStream(), input.specialBuffer(), bias.specialBuffer(),
|
||||
output.specialBuffer(), input.sizeAt(0), bias.sizeAt(0)),
|
||||
SD_FLOAT_TYPES, SD_FLOAT_TYPES);
|
||||
} else {
|
||||
// default case
|
||||
dim3 dims = getAddBiasDims(input.rankOf(), input.rankOf());
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), bias.dataType(), addBiasCudaLauncher,
|
||||
(dims.x, dims.y, dims.z, block.launchContext()->getCudaStream(),
|
||||
input.specialBuffer(), input.specialShapeInfo(), bias.specialBuffer(),
|
||||
bias.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), isNCHW),
|
||||
SD_FLOAT_TYPES, SD_FLOAT_TYPES);
|
||||
}
|
||||
NDArray::registerSpecialUse({&output}, {&input, &bias});
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 raver119@gmail.com
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/adjust_hue.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void SD_KERNEL adjustHueCuda(const void* vx, const LongType* xShapeInfo, const LongType* xTadOffsets,
|
||||
void* vz, const LongType* zShapeInfo, const LongType* zTadOffsets,
|
||||
const LongType numOfTads, const T delta, const LongType dimC) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int rank;
|
||||
__shared__ LongType xDimCstride, zDimCstride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(xShapeInfo);
|
||||
xDimCstride = shape::stride(xShapeInfo)[dimC];
|
||||
zDimCstride = shape::stride(zShapeInfo)[dimC];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < numOfTads; i += gridDim.x * blockDim.x) {
|
||||
const T* xTad = x + xTadOffsets[i];
|
||||
T* zTad = z + zTadOffsets[i];
|
||||
|
||||
T h, s, v;
|
||||
|
||||
rgbToHsv<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], h, s, v);
|
||||
|
||||
h += delta;
|
||||
if (h > 1)
|
||||
h -= 1;
|
||||
else if (h < 0)
|
||||
h += 1;
|
||||
|
||||
hsvToRgb<T>(h, s, v, zTad[0], zTad[zDimCstride], zTad[2 * zDimCstride]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_HOST void adjustHueCudaLauncher(const int blocksPerGrid, const int threadsPerBlock,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const LongType* xTadOffsets, void* vz, const LongType* zShapeInfo,
|
||||
const LongType* zTadOffsets, const LongType numOfTads,
|
||||
NDArray* deltaScalarArr, const LongType dimC) {
|
||||
adjustHueCuda<T><<<blocksPerGrid, threadsPerBlock, 512, *stream>>>(
|
||||
vx, xShapeInfo, xTadOffsets, vz, zShapeInfo, zTadOffsets, numOfTads, deltaScalarArr->e<T>(0), dimC);
|
||||
sd::DebugHelper::checkGlobalErrorCode("sadjustHue failed");
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void adjustHue(LaunchContext* context, NDArray* input, NDArray* deltaScalarArr, NDArray* output,
|
||||
const LongType dimC) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), {dimC});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), {dimC});
|
||||
|
||||
const LongType numOfTads = packX->numberOfTads();
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (numOfTads + threadsPerBlock - 1) / threadsPerBlock;
|
||||
|
||||
dim3 adjustDims = getAdjustDims(numOfTads);
|
||||
PointersManager manager(context, "adjustHue");
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input, deltaScalarArr});
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), adjustHueCudaLauncher,
|
||||
(adjustDims.x, adjustDims.y, context->getCudaStream(), input->specialBuffer(),
|
||||
input->specialShapeInfo(), packX->platformOffsets(), output->specialBuffer(),
|
||||
output->specialShapeInfo(), packZ->platformOffsets(), numOfTads, deltaScalarArr, dimC),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, deltaScalarArr});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,116 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/adjust_hue.h>
|
||||
#include <ops/declarable/helpers/adjust_saturation.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void SD_KERNEL adjustSaturationCuda(const void* vx, const LongType* xShapeInfo,
|
||||
const LongType* xTadOffsets, void* vz, const LongType* zShapeInfo,
|
||||
const LongType* zTadOffsets, const LongType numOfTads,
|
||||
const T factor, const LongType dimC) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType rank;
|
||||
__shared__ LongType xDimCstride, zDimCstride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(xShapeInfo);
|
||||
xDimCstride = shape::stride(xShapeInfo)[dimC];
|
||||
zDimCstride = shape::stride(zShapeInfo)[dimC];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < numOfTads; i += gridDim.x * blockDim.x) {
|
||||
const T* xTad = x + xTadOffsets[i];
|
||||
T* zTad = z + zTadOffsets[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]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_HOST void adjustSaturationCudaLauncher(const int blocksPerGrid, const int threadsPerBlock,
|
||||
const cudaStream_t* stream, const void* vx,
|
||||
const LongType* xShapeInfo, const LongType* xTadOffsets,
|
||||
void* vz, const LongType* zShapeInfo,
|
||||
const LongType* zTadOffsets, const LongType numOfTads,
|
||||
NDArray* factorScalarArr, const LongType dimC) {
|
||||
adjustSaturationCuda<T><<<blocksPerGrid, threadsPerBlock, 256, *stream>>>(
|
||||
vx, xShapeInfo, xTadOffsets, vz, zShapeInfo, zTadOffsets, numOfTads, factorScalarArr->e<T>(0), dimC);
|
||||
sd::DebugHelper::checkGlobalErrorCode("adjustSaturation failed");
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void adjustSaturation(LaunchContext* context, NDArray* input, NDArray* factorScalarArr, NDArray* output,
|
||||
const LongType dimC) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), {dimC});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), {dimC});
|
||||
|
||||
const LongType numOfTads = packX->numberOfTads();
|
||||
|
||||
dim3 adjustDims = getAdjustDims(numOfTads);
|
||||
|
||||
PointersManager manager(context, "adjustSaturation");
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input, factorScalarArr});
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), adjustSaturationCudaLauncher,
|
||||
(adjustDims.x,adjustDims.y, context->getCudaStream(), input->specialBuffer(),
|
||||
input->specialShapeInfo(), packX->platformOffsets(), output->specialBuffer(),
|
||||
output->specialShapeInfo(), packZ->platformOffsets(), numOfTads, factorScalarArr, dimC),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, factorScalarArr});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,112 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/assign.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
#include "helpers/ShapeUtils.h"
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename X, typename Z>
|
||||
SD_KERNEL static void assignKernel(const void* vx, const LongType* xShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const LongType xOffset, const LongType zOffset) {
|
||||
const auto x = reinterpret_cast<const X*>(vx);
|
||||
auto z = reinterpret_cast<Z*>(vz);
|
||||
|
||||
__shared__ LongType len, totalThreads;
|
||||
__shared__ int rank;
|
||||
__shared__ const LongType *xShape;
|
||||
__shared__ const LongType *zShape;
|
||||
__shared__ const LongType *xStride;
|
||||
__shared__ const LongType *zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
len = shape::length(zShapeInfo);
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
rank = shape::rank(zShapeInfo);
|
||||
|
||||
// Cache shapes and strides
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
LongType xCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
|
||||
for (LongType i = tid; i < len; i += totalThreads) {
|
||||
INDEX2COORDS(i, rank, zShape, zCoords);
|
||||
INDEX2COORDS(i, rank, xShape, xCoords);
|
||||
|
||||
LongType xIndex, zIndex;
|
||||
COORDS2INDEX(rank, xStride, xCoords, xIndex);
|
||||
COORDS2INDEX(rank, zStride, zCoords, zIndex);
|
||||
|
||||
z[zIndex] = static_cast<Z>(x[xIndex]);
|
||||
}
|
||||
}
|
||||
template <typename X, typename Z>
|
||||
SD_HOST static void assignCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const LongType xOffset, const LongType zOffset) {
|
||||
assignKernel<X, Z><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, xOffset, zOffset);
|
||||
DebugHelper::checkGlobalErrorCode("assignKernel(...) failed");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
dim3 launchDims = traceDims(target->lengthOf());
|
||||
|
||||
PointersManager manager(context, "helpers::assign");
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(xType, zType, assignCudaLauncher,
|
||||
(launchDims.x, launchDims.y, launchDims.z, context->getCudaStream(),
|
||||
source->specialBuffer(), source->specialShapeInfo(),
|
||||
target->specialBuffer(), target->specialShapeInfo(),
|
||||
source->offset(), target->offset()),
|
||||
SD_COMMON_TYPES, SD_COMMON_TYPES);
|
||||
|
||||
manager.synchronize();
|
||||
NDArray::registerSpecialUse({target}, {source});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,60 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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(LongType rank, NDArray* axisVector, std::vector<LongType>& output) {
|
||||
if(axisVector->isScalar()) {
|
||||
output.resize(1);
|
||||
auto ca = axisVector->e<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<LongType>(e);
|
||||
if (ca < 0) // shift values on rank for negative vals
|
||||
ca += rank;
|
||||
|
||||
output[e] = ca;
|
||||
}
|
||||
}
|
||||
|
||||
void adjustAxis(LongType rank, std::vector<LongType>& axisVector) {
|
||||
for (int e = 0; e < axisVector.size(); e++) {
|
||||
auto a = axisVector[e];
|
||||
if (a < 0) // shift vals on rank for negative vals
|
||||
axisVector[e] = a + rank;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <cublas_v2.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/batched_gemm.h>
|
||||
#include <ops/specials_cuda.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
#include <types/float16.h>
|
||||
|
||||
#include <indexing/NDIndexUtils.h>
|
||||
#include <ops/declarable/CustomOperations.h>
|
||||
|
||||
|
||||
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();
|
||||
all = &allLocal;
|
||||
}
|
||||
|
||||
|
||||
int batchSize = a->sizeAt(0) / 2;
|
||||
std::vector<NDArray *>inputs;
|
||||
std::vector<NDArray *> keyInputs;
|
||||
std::vector<NDArray *> outputs;
|
||||
|
||||
create_view createView;
|
||||
|
||||
//add alpha and beta before the batch gemm, this just needs to be broadcasted
|
||||
inputs.push_back(alphas);
|
||||
inputs.push_back(betas);
|
||||
|
||||
//divide by 2: queries and keys
|
||||
for(int i = 0; i < batchSize; i++) {
|
||||
auto point = NDIndexUtils::createPoint(i);
|
||||
auto aSlice = createView.evaluate({a,&point,all,all},{},{});
|
||||
auto bSlice = createView.evaluate({b,&point,all,all},{},{});
|
||||
auto outSlice = createView.evaluate({c,&point,all,all},{},{});
|
||||
inputs.push_back(aSlice.at(0));
|
||||
keyInputs.push_back(bSlice.at(0));
|
||||
outputs.push_back(outSlice.at(0));
|
||||
}
|
||||
|
||||
bgemm(inputs,keyInputs,outputs,alphas,betas,transA,transB,M,N,K,lda,ldb,ldc);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// bsxMXK x bSxKxN = bSxMxN
|
||||
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) {
|
||||
const auto bS = vA.size(); // batch size
|
||||
|
||||
std::vector<NDArray*> pA(bS), pB(bS), pC(bS);
|
||||
|
||||
std::vector<NDArray*> toDelete;
|
||||
|
||||
for (int i = 0; i < bS; ++i) {
|
||||
pA[i] = vA[i]->dup('f'); // dup() already returns NDArray*
|
||||
toDelete.emplace_back(pA[i]);
|
||||
|
||||
|
||||
pB[i] = vB[i]->dup('f'); // dup() already returns NDArray*
|
||||
toDelete.emplace_back(pB[i]);
|
||||
|
||||
pC[i] = vC[i]->dup('f'); // dup() already returns NDArray*
|
||||
toDelete.emplace_back(pC[i]);
|
||||
|
||||
|
||||
if (pC[i]->ordering() != 'f') {
|
||||
auto temp = pA[i];
|
||||
std::vector<sd::LongType> permute = {1,0};
|
||||
pA[i] = pB[i]->permute(permute, false, false); // permute() already returns NDArray*
|
||||
pB[i] = temp->permute(permute, false, false);
|
||||
pC[i] = pC[i]->permute(permute, false, false);
|
||||
toDelete.push_back(pA[i]);
|
||||
toDelete.push_back(pB[i]);
|
||||
toDelete.push_back(pC[i]);
|
||||
M = pA[i]->sizeAt(0);
|
||||
K = pA[i]->sizeAt(1);
|
||||
N = pB[i]->sizeAt(1);
|
||||
}
|
||||
|
||||
NDArray::prepareSpecialUse({pC[i]}, {pA[i], pB[i]});
|
||||
NDArray::registerSpecialUse({pC[i]}, {pA[i], pB[i]});
|
||||
}
|
||||
|
||||
NDArray::prepareSpecialUse({}, {alphas, betas});
|
||||
NDArray::registerSpecialUse({}, {alphas, betas});
|
||||
|
||||
std::vector<void*> pAbuffs(bS), pBbuffs(bS), pCbuffs(bS);
|
||||
for (int i = 0; i < bS; ++i) {
|
||||
pAbuffs[i] = pA[i]->specialBuffer();
|
||||
pBbuffs[i] = pB[i]->specialBuffer();
|
||||
pCbuffs[i] = pC[i]->specialBuffer();
|
||||
}
|
||||
|
||||
LaunchContext * context = vA[0]->getContext();
|
||||
PointersManager manager(context, "helpers::bgemm cuda");
|
||||
|
||||
const void** aBuffers = reinterpret_cast<const void**>(manager.replicatePointer(pAbuffs.data(), bS * sizeof(void*)));
|
||||
const void** bBuffers = reinterpret_cast<const void**>(manager.replicatePointer(pBbuffs.data(), bS * sizeof(void*)));
|
||||
void** cBuffers = reinterpret_cast<void**>(manager.replicatePointer(pCbuffs.data(), bS * sizeof(void*)));
|
||||
|
||||
|
||||
|
||||
const cublasOperation_t transAblas = transA == 112 ? CUBLAS_OP_T : CUBLAS_OP_N;
|
||||
const cublasOperation_t transBblas = transB == 112 ? CUBLAS_OP_T : CUBLAS_OP_N;
|
||||
|
||||
if(M < 0) THROW_EXCEPTION("M < 0");
|
||||
if(N < 0) THROW_EXCEPTION("N < 0");
|
||||
if(K < 0) THROW_EXCEPTION("K < 0");
|
||||
|
||||
|
||||
const auto aType = pA[0]->dataType();
|
||||
const auto bType = pB[0]->dataType();
|
||||
const auto cType = pC[0]->dataType();
|
||||
|
||||
std::lock_guard<std::mutex> lock(*LaunchContext::deviceMutex());
|
||||
|
||||
auto handle = reinterpret_cast<cublasHandle_t*>(context->getCublasHandle());
|
||||
auto stream = context->getCudaStream();
|
||||
|
||||
auto status = cublasSetStream_v2(*handle, *stream);
|
||||
|
||||
if (status != CUBLAS_STATUS_SUCCESS) throw cuda_exception::build("MmulHelper::mmulMxM cuda set stream failed ! Please double check the passed in handle.", status);
|
||||
|
||||
const bool AB(aType == bType), AC(aType == cType), ABC(AB && AC);
|
||||
|
||||
// choose appropriate cuda gemm api depending on data types
|
||||
// Handle empty arrays by using default values (alpha=1.0, beta=0.0)
|
||||
if (ABC && aType == DOUBLE) {
|
||||
double alpha = alphas->lengthOf() > 0 ? alphas->e<double>(0) : 1.0;
|
||||
double beta = betas->lengthOf() > 0 ? betas->e<double>(0) : 0.0;
|
||||
status = cublasDgemmBatched(*handle, transAblas, transBblas, M, N, K, &alpha, (const double**)aBuffers, lda,
|
||||
(const double**)bBuffers, ldb, &beta, (double**)cBuffers, ldc, bS);
|
||||
} else if (ABC && aType == FLOAT32) {
|
||||
float alpha = alphas->lengthOf() > 0 ? alphas->e<float>(0) : 1.0f;
|
||||
float beta = betas->lengthOf() > 0 ? betas->e<float>(0) : 0.0f;
|
||||
status = cublasSgemmBatched(*handle, transAblas, transBblas, M, N, K, &alpha, (const float**)aBuffers, lda,
|
||||
(const float**)bBuffers, ldb, &beta, (float**)cBuffers, ldc, bS);
|
||||
} else if (ABC && aType == HALF) {
|
||||
__half alpha = alphas->lengthOf() > 0 ? alphas->e<float>(0) : 1.0f;
|
||||
__half beta = betas->lengthOf() > 0 ? betas->e<float>(0) : 0.0f;
|
||||
status = cublasHgemmBatched(*handle, transAblas, transBblas, M, N, K, &alpha, (const __half**)aBuffers, lda,
|
||||
(const __half**)bBuffers, ldb, &beta, (__half**)cBuffers, ldc, bS);
|
||||
} else if (AB && aType == INT8 && cType == FLOAT32) {
|
||||
float alpha = alphas->lengthOf() > 0 ? alphas->e<float>(0) : 1.0f;
|
||||
float beta = betas->lengthOf() > 0 ? betas->e<float>(0) : 0.0f;
|
||||
status = cublasGemmBatchedEx(*handle, transAblas, transBblas, M, N, K, &alpha, aBuffers, CUDA_R_8I, lda, bBuffers,
|
||||
CUDA_R_8I, ldb, &beta, cBuffers, CUDA_R_32F, ldc, bS, CUDA_R_32F, CUBLAS_GEMM_DEFAULT);
|
||||
} else if (AB && aType == HALF && cType == FLOAT32) {
|
||||
float alpha = alphas->lengthOf() > 0 ? alphas->e<float>(0) : 1.0f;
|
||||
float beta = betas->lengthOf() > 0 ? betas->e<float>(0) : 0.0f;
|
||||
status =
|
||||
cublasGemmBatchedEx(*handle, transAblas, transBblas, M, N, K, &alpha, aBuffers, CUDA_R_16F, lda, bBuffers,
|
||||
CUDA_R_16F, ldb, &beta, cBuffers, CUDA_R_32F, ldc, bS, CUDA_R_32F, CUBLAS_GEMM_DEFAULT);
|
||||
} else
|
||||
THROW_EXCEPTION("batched gemm cuda: this mode is not implemented yet !");
|
||||
|
||||
if (status != CUBLAS_STATUS_SUCCESS) {
|
||||
throw cuda_exception::build("MmulHelper::mmulMxM cuda execution failed !", status);
|
||||
}
|
||||
|
||||
auto cudaResult = cudaStreamSynchronize(*stream);
|
||||
if (cudaResult != 0) {
|
||||
throw cuda_exception::build("MmulHelper::mmulMxM cuda stream synchronize failed !", cudaResult);
|
||||
}
|
||||
|
||||
for (int i = 0; i < bS; ++i)
|
||||
vC[i]->assign(pC[i]);
|
||||
|
||||
for (int i = toDelete.size() - 1; i >= 0; --i) delete toDelete[i];
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,151 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 25.02.2018
|
||||
//
|
||||
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/OmpLaunchHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/batchnorm.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void batchnormCuda2(const void* vx, const LongType* xShapeInfo, const void* vMean,
|
||||
const LongType* meanShapeInfo, const void* vVariance,
|
||||
const LongType* varianceShapeInfo, const void* vGamma,
|
||||
const LongType* gammaShapeInfo, const void* vBeta,
|
||||
const LongType* betaShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const int numDims, const LongType* dims, const T epsilon) {
|
||||
const auto x = reinterpret_cast<const T*>(vx);
|
||||
auto z = reinterpret_cast<T*>(vz);
|
||||
const auto mean = reinterpret_cast<const T*>(vMean);
|
||||
const auto variance = reinterpret_cast<const T*>(vVariance);
|
||||
const auto gamma = reinterpret_cast<const T*>(vGamma);
|
||||
const auto beta = reinterpret_cast<const T*>(vBeta);
|
||||
|
||||
__shared__ int xRank, minRank; // xRank == zRank, minRank = meanRank = varianceRank = gammaRank = betaRank
|
||||
__shared__ LongType xLen, totalThreads; // xLen = zLen
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
xLen = shape::length(xShapeInfo);
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
minRank = shape::rank(meanShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < xLen; i += totalThreads) {
|
||||
INDEX2COORDS(i, xRank, shape::shapeOf(xShapeInfo), coords);
|
||||
|
||||
LongType xOffset, zOffset;
|
||||
COORDS2INDEX(xRank, shape::stride(xShapeInfo), coords, xOffset);
|
||||
COORDS2INDEX(xRank, shape::stride(zShapeInfo), coords, zOffset);
|
||||
|
||||
if (minRank == xRank) {
|
||||
for (LongType i = 0, j = 0; i < xRank; ++i) {
|
||||
if (j < numDims && i != dims[j])
|
||||
coords[i] = 0;
|
||||
else
|
||||
++j;
|
||||
}
|
||||
} else // minRank = numDims = 1 in this case
|
||||
coords[0] = coords[dims[0]];
|
||||
|
||||
LongType meanOffset, varianceOffset;
|
||||
COORDS2INDEX(minRank, shape::stride(meanShapeInfo), coords, meanOffset);
|
||||
COORDS2INDEX(minRank, shape::stride(varianceShapeInfo), coords, varianceOffset);
|
||||
|
||||
T sigmaInvGam = 1. / math::sd_sqrt<T, T>(variance[varianceOffset] + epsilon);
|
||||
|
||||
if (gamma != nullptr) {
|
||||
LongType gammaOffset;
|
||||
COORDS2INDEX(minRank, shape::stride(gammaShapeInfo), coords, gammaOffset);
|
||||
sigmaInvGam *= gamma[gammaOffset];
|
||||
}
|
||||
|
||||
z[zOffset] = (x[xOffset] - mean[meanOffset]) * sigmaInvGam;
|
||||
|
||||
if (beta != nullptr) {
|
||||
LongType betaOffset;
|
||||
COORDS2INDEX(minRank, shape::stride(betaShapeInfo), coords, betaOffset);
|
||||
z[zOffset] += beta[betaOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_HOST static void batchnormCudaLauncher2(const int blocksPerGrid, const int threadsPerBlock,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vMean, const LongType* meanShapeInfo, const void* vVariance,
|
||||
const LongType* varianceShapeInfo, const void* vGamma,
|
||||
const LongType* gammaShapeInfo, const void* vBeta,
|
||||
const LongType* betaShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const int numDims, const LongType* dims, const double epsilon) {
|
||||
batchnormCuda2<T><<<blocksPerGrid, threadsPerBlock, 512, *stream>>>(
|
||||
vx, xShapeInfo, vMean, meanShapeInfo, vVariance, varianceShapeInfo, vGamma, gammaShapeInfo, vBeta, betaShapeInfo,
|
||||
vz, zShapeInfo, numDims, dims, static_cast<T>(epsilon));
|
||||
sd::DebugHelper::checkGlobalErrorCode("batchNormCuda2 failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void batchnorm(NDArray* input, NDArray* mean, NDArray* variance, NDArray* gamma,
|
||||
NDArray* beta, NDArray* output, const std::vector<LongType>& axes, const double epsilon) {
|
||||
|
||||
dim3 batchNormDims = getBatchNormDims(input->lengthOf());
|
||||
PointersManager manager(input->getContext(), "batchnorm");
|
||||
|
||||
const LongType* dims = reinterpret_cast<LongType*>(manager.replicatePointer(axes.data(), axes.size() * sizeof(LongType)));
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input, mean, variance, gamma, beta});
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), batchnormCudaLauncher2,
|
||||
(batchNormDims.x, batchNormDims.y, input->getContext()->getCudaStream(), input->specialBuffer(),
|
||||
input->specialShapeInfo(), mean->specialBuffer(), mean->specialShapeInfo(),
|
||||
variance->specialBuffer(), variance->specialShapeInfo(),
|
||||
gamma ? gamma->specialBuffer() : nullptr, gamma ? gamma->specialShapeInfo() : nullptr,
|
||||
beta ? beta->specialBuffer() : nullptr, beta ? beta->specialShapeInfo() : nullptr,
|
||||
output->specialBuffer(), output->specialShapeInfo(), axes.size(), dims, epsilon),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, mean, variance, gamma, beta});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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/DataTypeUtils.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/betaInc.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
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>
|
||||
SD_DEVICE T continuedFractionCuda(const T a, const T b, const T x) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
T* coeffs = reinterpret_cast<T*>(shmem);
|
||||
|
||||
const T min = DataTypeUtils::min<T>() / DataTypeUtils::eps<T>();
|
||||
const T aPlusb = a + b;
|
||||
T val, aPlus2i;
|
||||
|
||||
T t2 = coeffs[1];
|
||||
T t1 = coeffs[0];
|
||||
if (math::sd_abs<T,T>(t1) < min) t1 = min;
|
||||
t1 = static_cast<T>(1) / t1;
|
||||
T result = t1;
|
||||
|
||||
for (LongType i = 1; i <= maxIter; ++i) {
|
||||
const LongType i2 = 2 * i;
|
||||
aPlus2i = a + static_cast<T>(i2);
|
||||
|
||||
// t1
|
||||
t1 = static_cast<T>(1) + coeffs[i2] * t1;
|
||||
if (math::sd_abs<T,T>(t1) < min) t1 = min;
|
||||
t1 = static_cast<T>(1) / t1;
|
||||
// t2
|
||||
t2 = static_cast<T>(1) + coeffs[i2] / t2;
|
||||
if (math::sd_abs<T,T>(t2) < min) t2 = min;
|
||||
// result
|
||||
result *= t2 * t1;
|
||||
// t1
|
||||
t1 = static_cast<T>(1) + coeffs[i2 + 1] * t1;
|
||||
if (math::sd_abs<T,T>(t1) < min) t1 = min;
|
||||
t1 = static_cast<T>(1) / t1;
|
||||
// t2
|
||||
t2 = static_cast<T>(1) + coeffs[i2 + 1] / 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
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL void betaIncForArrayCuda(const void* va, const LongType* aShapeInfo, const void* vb,
|
||||
const LongType* bShapeInfo, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
T* sharedMem = reinterpret_cast<T*>(shmem);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType aLen, bLen, xLen, zLen, aOffset, bOffset, xOffset, zOffset;
|
||||
__shared__ int aRank, bRank, xRank, zRank;
|
||||
__shared__ const LongType *aShape, *bShape, *xShape, *zShape;
|
||||
__shared__ const LongType *aStride, *bStride, *xStride, *zStride;
|
||||
__shared__ T a, b, x;
|
||||
__shared__ bool symmCond;
|
||||
|
||||
const LongType j = blockIdx.x; // one block per each element
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache lengths
|
||||
aLen = shape::length(aShapeInfo);
|
||||
bLen = shape::length(bShapeInfo);
|
||||
xLen = shape::length(xShapeInfo);
|
||||
zLen = shape::length(zShapeInfo);
|
||||
|
||||
// Cache ranks
|
||||
aRank = shape::rank(aShapeInfo);
|
||||
bRank = shape::rank(bShapeInfo);
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
// Cache shapes
|
||||
aShape = shape::shapeOf(aShapeInfo);
|
||||
bShape = shape::shapeOf(bShapeInfo);
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
|
||||
// Cache strides
|
||||
aStride = shape::stride(aShapeInfo);
|
||||
bStride = shape::stride(bShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
|
||||
LongType aCoords[SD_MAX_RANK];
|
||||
LongType bCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
|
||||
INDEX2COORDS(j, aRank, aShape, aCoords);
|
||||
COORDS2INDEX(aRank, aStride, aCoords, aOffset);
|
||||
INDEX2COORDS(j, bRank, bShape, bCoords);
|
||||
COORDS2INDEX(bRank, bStride, bCoords, bOffset);
|
||||
INDEX2COORDS(j, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
INDEX2COORDS(j, zRank, zShape, zCoords);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
|
||||
if (aOffset >= aLen || bOffset >= bLen || xOffset >= xLen || zOffset >= zLen)
|
||||
return;
|
||||
|
||||
a = *(reinterpret_cast<const T*>(va) + aOffset);
|
||||
b = *(reinterpret_cast<const T*>(vb) + bOffset);
|
||||
x = *(reinterpret_cast<const T*>(vx) + xOffset);
|
||||
symmCond = x > (a + T(1)) / (a + b + T(2));
|
||||
|
||||
if (symmCond) { // swap a and b, x = 1 - x
|
||||
T temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
x = T(1) - x;
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// t^{n-1} * (1 - t)^{n-1} is symmetric function with respect to x = 0.5
|
||||
if (zOffset < zLen && a == b && x == T(0.5)) {
|
||||
z[zOffset] = T(0.5);
|
||||
return;
|
||||
}
|
||||
|
||||
if (zOffset < zLen && (x == T(0) || x == T(1))) {
|
||||
if (symmCond) {
|
||||
z[zOffset] = T(1) - x;
|
||||
} else {
|
||||
z[zOffset] = x;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate two coefficients per thread
|
||||
if (threadIdx.x != 0) {
|
||||
const int i = threadIdx.x;
|
||||
const T aPlus2i = a + T(2) * T(i);
|
||||
sharedMem[2 * i] = T(i) * (b - T(i)) * x / ((aPlus2i - T(1)) * aPlus2i);
|
||||
sharedMem[2 * i + 1] = -(a + T(i)) * (a + b + T(i)) * x / ((aPlus2i + T(1)) * aPlus2i);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
const T gammaPart = static_cast<T>(lgamma(a)) + static_cast<T>(lgamma(b)) - static_cast<T>(lgamma(a + b));
|
||||
const T front = math::sd_exp<T, T>(math::sd_log<T, T>(x) * a + math::sd_log<T, T>(T(1) - x) * b - gammaPart);
|
||||
|
||||
sharedMem[0] = T(1) - (a + b) * x / (a + T(1));
|
||||
sharedMem[1] = T(1);
|
||||
|
||||
z[zOffset] = front * continuedFractionCuda(a, b, x) / a;
|
||||
|
||||
if (symmCond) { // symmetry relation
|
||||
z[zOffset] = T(1) - z[zOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void betaIncForArrayCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* va, const LongType* aShapeInfo,
|
||||
const void* vb, const LongType* bShapeInfo, const void* vx,
|
||||
const LongType* xShapeInfo, void* vz, const LongType* zShapeInfo) {
|
||||
betaIncForArrayCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(va, aShapeInfo, vb, bShapeInfo, vx,
|
||||
xShapeInfo, vz, zShapeInfo);
|
||||
sd::DebugHelper::checkGlobalErrorCode("betaInc failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// overload betaInc for arrays, shapes of a, b and x must be the same !!!
|
||||
void betaInc(LaunchContext* context, NDArray& a, NDArray& b, NDArray& x, NDArray& output) {
|
||||
dim3 launchDims = getBetaInc(maxIter,output.lengthOf(),output.sizeOfT());
|
||||
|
||||
const auto xType = x.dataType();
|
||||
|
||||
PointersManager manager(context, "betaInc");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&a, &b, &x});
|
||||
BUILD_SINGLE_SELECTOR(xType, betaIncForArrayCudaLauncher,
|
||||
(launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), a.specialBuffer(),
|
||||
a.specialShapeInfo(), b.specialBuffer(), b.specialShapeInfo(), x.specialBuffer(),
|
||||
x.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo()),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&a, &b, &x});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,428 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void clipByNormCuda(const void* vClipNorm, const void* vNorm, const LongType* normShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const LongType* dimensions,
|
||||
const LongType dimsLen,
|
||||
const bool useAverage) {
|
||||
const T clipNorm = *reinterpret_cast<const T*>(vClipNorm);
|
||||
const T* norm = reinterpret_cast<const T*>(vNorm);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType zLen, tadLen, totalThreads;
|
||||
__shared__ int zRank, normRank;
|
||||
__shared__ const LongType *zShape;
|
||||
__shared__ const LongType *zStride;
|
||||
__shared__ const LongType *normStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
zLen = shape::length(zShapeInfo);
|
||||
tadLen = zLen / shape::length(normShapeInfo);
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
// Cache ranks
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
normRank = shape::rank(normShapeInfo);
|
||||
|
||||
// Cache shapes and strides
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
normStride = shape::stride(normShapeInfo);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
LongType zCoords[SD_MAX_RANK], normCoords[SD_MAX_RANK];
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < zLen; i += totalThreads) {
|
||||
INDEX2COORDS(i, zRank, zShape, zCoords);
|
||||
|
||||
// deduce norm coords
|
||||
for (int j = 0; j < dimsLen; ++j) normCoords[j] = zCoords[dimensions[j]];
|
||||
|
||||
LongType normOffset, zOffset;
|
||||
COORDS2INDEX(normRank, normStride, normCoords, normOffset);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
|
||||
const T actualNorm = useAverage ? static_cast<T>(norm[normOffset]) / static_cast<T>(tadLen) : static_cast<T>(norm[normOffset]);
|
||||
|
||||
if (actualNorm > clipNorm) z[zOffset] *= static_cast<T>(clipNorm) / static_cast<T>(actualNorm);
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_HOST static void clipByNormCudaLauncher(const int blocksPerGrid, const int threadsPerBlock,
|
||||
const cudaStream_t* stream, const void* vClipNorm, const void* vNorm,
|
||||
const LongType* normShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const LongType* dimensions, const LongType dimsLen, const bool useAverage) {
|
||||
clipByNormCuda<T><<<blocksPerGrid, threadsPerBlock, 512, *stream>>>(vClipNorm, vNorm, normShapeInfo, vz, zShapeInfo,
|
||||
dimensions, dimsLen, useAverage);
|
||||
sd::DebugHelper::checkGlobalErrorCode("clipByNorm failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void clipByNorm(LaunchContext* context, NDArray* input, NDArray* output, const std::vector<LongType>& dims,
|
||||
NDArray* clipNorm, const bool isInplace, const bool useAverage) {
|
||||
NDArray* z = nullptr;
|
||||
|
||||
if (isInplace) {
|
||||
z = input;
|
||||
} else {
|
||||
output->assign(input);
|
||||
z = output;
|
||||
}
|
||||
|
||||
if (dims.empty()) {
|
||||
std::vector<LongType> empty;
|
||||
NDArray actualNorm = useAverage ? z->reduceAlongDimension(reduce::Norm2, &empty) / z->lengthOf()
|
||||
: z->reduceAlongDimension(reduce::Norm2, &empty);
|
||||
|
||||
if (actualNorm.e<float>(0) > clipNorm->e<float>(0)) *z *= *clipNorm / actualNorm;
|
||||
} else {
|
||||
NDArray actualNorms = z->reduceAlongDimension(reduce::Norm2, &dims);
|
||||
|
||||
std::vector<LongType> *dimsToExclude = ShapeUtils::evalDimsToExclude(z->rankOf(), dims.size(),dims.data());
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (z->lengthOf() + threadsPerBlock - 1) / threadsPerBlock;
|
||||
|
||||
PointersManager manager(context, "clipByNorm");
|
||||
|
||||
const LongType* dimensions = reinterpret_cast<const LongType*>(
|
||||
manager.replicatePointer(dimsToExclude->data(), dimsToExclude->size() * sizeof(LongType)));
|
||||
|
||||
NDArray::prepareSpecialUse({z}, {z, &actualNorms, clipNorm});
|
||||
|
||||
|
||||
BUILD_SINGLE_SELECTOR(z->dataType(), clipByNormCudaLauncher,
|
||||
(blocksPerGrid,
|
||||
threadsPerBlock,
|
||||
context->getCudaStream(),
|
||||
clipNorm->specialBuffer(),
|
||||
actualNorms.specialBuffer(),
|
||||
actualNorms.specialShapeInfo(),
|
||||
z->specialBuffer(),
|
||||
z->specialShapeInfo(),
|
||||
dimensions,
|
||||
dimsToExclude->size(),
|
||||
useAverage),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({z}, {z, &actualNorms, clipNorm});
|
||||
|
||||
manager.synchronize();
|
||||
delete dimsToExclude;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void clipByNormBpCuda(const void* vClipNorm, const void* vx, const LongType* xShapeInfo, // input
|
||||
const void* vy, const LongType* yShapeInfo, // gradO
|
||||
const void* vNorm, const LongType* normShapeInfo, const void* vSum,
|
||||
const LongType* sumShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, // gradI
|
||||
const LongType* dimensions, const LongType dimsLen, const bool useAverage) {
|
||||
const T clipNorm = *reinterpret_cast<const T*>(vClipNorm);
|
||||
const T* norm = reinterpret_cast<const T*>(vNorm);
|
||||
const T* sum = reinterpret_cast<const T*>(vSum);
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
const T* y = reinterpret_cast<const T*>(vy);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType zLen, tadLen, totalThreads;
|
||||
__shared__ bool sameOffsets;
|
||||
__shared__ int zRank, yRank, normRank, sumRank, xRank;
|
||||
__shared__ const LongType *zShape;
|
||||
__shared__ const LongType *zStride;
|
||||
__shared__ const LongType *yStride;
|
||||
__shared__ const LongType *normStride;
|
||||
__shared__ const LongType *sumStride;
|
||||
__shared__ const LongType *xStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
zLen = shape::length(zShapeInfo);
|
||||
tadLen = zLen / shape::length(normShapeInfo);
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
sameOffsets = shape::haveSameShapeAndStrides(xShapeInfo, yShapeInfo, zShapeInfo);
|
||||
|
||||
// Cache ranks
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
normRank = shape::rank(normShapeInfo);
|
||||
sumRank = shape::rank(sumShapeInfo);
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
|
||||
// Cache shapes and strides
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
normStride = shape::stride(normShapeInfo);
|
||||
sumStride = shape::stride(sumShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
LongType zCoords[SD_MAX_RANK], normCoords[SD_MAX_RANK];
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < zLen; i += totalThreads) {
|
||||
INDEX2COORDS(i, zRank, zShape, zCoords);
|
||||
|
||||
LongType zOffset, yOffset;
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
if(sameOffsets) {
|
||||
yOffset = zOffset;
|
||||
} else {
|
||||
COORDS2INDEX(yRank, yStride, zCoords, yOffset);
|
||||
}
|
||||
|
||||
// deduce norm coords
|
||||
for (int j = 0; j < dimsLen; ++j) normCoords[j] = zCoords[dimensions[j]];
|
||||
|
||||
LongType normOffset;
|
||||
COORDS2INDEX(normRank, normStride, normCoords, normOffset);
|
||||
|
||||
const T actualNorm = useAverage ? norm[normOffset] / tadLen : norm[normOffset];
|
||||
|
||||
if (actualNorm > clipNorm) {
|
||||
LongType sumOffset, xOffset;
|
||||
COORDS2INDEX(sumRank, sumStride, normCoords, sumOffset);
|
||||
if(sameOffsets) {
|
||||
xOffset = zOffset;
|
||||
} else {
|
||||
COORDS2INDEX(xRank, xStride, zCoords, xOffset);
|
||||
}
|
||||
|
||||
const T sumVal = sum[sumOffset];
|
||||
z[zOffset] = (clipNorm / actualNorm) * y[yOffset] *
|
||||
(static_cast<T>(1.f) - (x[xOffset] * sumVal) / (actualNorm * actualNorm));
|
||||
} else {
|
||||
z[zOffset] = y[yOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void clipByNormBp_(LaunchContext* context, NDArray* input, NDArray* gradO, NDArray* gradI,
|
||||
const std::vector<LongType>& dims, NDArray* clipNorm, const bool useAverage) {
|
||||
const int rank = input->rankOf();
|
||||
|
||||
auto actualNorms = input->reduceAlongDimension(reduce::Norm2, &dims);
|
||||
|
||||
if (actualNorms.lengthOf() == 1) {
|
||||
const T norm = useAverage ? actualNorms.e<T>(0) / static_cast<T>(input->lengthOf()) : actualNorms.e<T>(0);
|
||||
|
||||
auto clipVal = clipNorm->e<T>(0);
|
||||
|
||||
if (norm > clipVal) {
|
||||
const T sum = input->reduceNumber(reduce::Sum).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(gradO, lambda, gradI);
|
||||
const_cast<NDArray*>(input)->applyPairwiseLambda(const_cast<NDArray*>(gradO), lambda, gradI);
|
||||
} else
|
||||
gradI->assign(gradO);
|
||||
} else {
|
||||
NDArray actualNorms = input->reduceAlongDimension(reduce::Norm2, &dims);
|
||||
NDArray sums = input->reduceAlongDimension(reduce::Sum, &dims);
|
||||
|
||||
std::vector<LongType> *dimsToExclude = ShapeUtils::evalDimsToExclude(gradI->rankOf(), dims.size(),dims.data());
|
||||
|
||||
|
||||
dim3 launchDims = clipDims(gradI->lengthOf());
|
||||
PointersManager manager(context, "clipByNormBp");
|
||||
|
||||
const LongType* dimensions = reinterpret_cast<const LongType*>(
|
||||
manager.replicatePointer(dimsToExclude->data(), dimsToExclude->size() * sizeof(LongType)));
|
||||
|
||||
NDArray::prepareSpecialUse({gradI}, {&actualNorms, &sums, clipNorm, input, gradO});
|
||||
clipByNormBpCuda<T><<<launchDims.y, launchDims.x,launchDims.z, *context->getCudaStream()>>>(
|
||||
clipNorm->specialBuffer(), input->specialBuffer(), input->specialShapeInfo(), gradO->specialBuffer(),
|
||||
gradO->specialShapeInfo(), actualNorms.specialBuffer(), actualNorms.specialShapeInfo(), sums.specialBuffer(),
|
||||
sums.specialShapeInfo(), gradI->specialBuffer(), gradI->specialShapeInfo(), dimensions, (LongType)dimsToExclude->size(),
|
||||
useAverage);
|
||||
sd::DebugHelper::checkGlobalErrorCode("clipByNorm failed");
|
||||
|
||||
NDArray::registerSpecialUse({gradI}, {&actualNorms, &sums, clipNorm, input, gradO});
|
||||
|
||||
manager.synchronize();
|
||||
delete dimsToExclude;
|
||||
}
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void clipByNormBp_,
|
||||
(sd::LaunchContext * context, NDArray* input, NDArray* gradO, NDArray* gradI,
|
||||
const std::vector<sd::LongType>& dimensions, NDArray* clipNorm, const bool useAverage),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void clipByNormBp(LaunchContext* context, NDArray* input, NDArray* gradO, NDArray* gradI,
|
||||
const std::vector<LongType>& dimensions, NDArray* clipNorm, const bool useAverage) {
|
||||
NDArray casted = clipNorm->cast(input->dataType());
|
||||
BUILD_SINGLE_SELECTOR(gradI->dataType(), clipByNormBp_,
|
||||
(context, &casted, gradO, gradI, dimensions, clipNorm, useAverage), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void clipByGlobalNorm_(LaunchContext* context, std::vector<NDArray*>& inputs, double clipNorm,
|
||||
memory::Workspace* workspace, std::vector<NDArray*>& outputs, bool isInplace) {
|
||||
T globalNorm = static_cast<T>(0.f);
|
||||
|
||||
for (auto i = 0; i < inputs.size(); i++) {
|
||||
auto input = inputs[i];
|
||||
auto l2norm = input->reduceNumber(reduce::Norm2);
|
||||
globalNorm += l2norm.e<T>(0) * l2norm.e<T>(0);
|
||||
}
|
||||
|
||||
globalNorm = math::sd_sqrt<T,T>(globalNorm);
|
||||
outputs[inputs.size()]->p(0, globalNorm);
|
||||
const T factor = static_cast<T>(clipNorm) / globalNorm;
|
||||
|
||||
for (size_t e = 0; e < inputs.size(); e++) {
|
||||
// all-reduce
|
||||
auto input = inputs[e];
|
||||
auto output = outputs[e];
|
||||
|
||||
if (static_cast<double>(globalNorm) <= clipNorm) {
|
||||
output->assign(input);
|
||||
} else {
|
||||
auto lambda = LAMBDA_T(_x, factor) { return _x * factor; });
|
||||
input->applyLambda(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_,
|
||||
(context, inputs, clipNorm, workspace, outputs, isInplace), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void clipByGlobalNorm_,
|
||||
(sd::LaunchContext * context, std::vector<NDArray*> & inputs, double clipNorm,
|
||||
sd::memory::Workspace* workspace, std::vector<NDArray*>& outputs, bool isInplace),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
template <typename T>
|
||||
static void SD_KERNEL clipByValueKernel(void* input, const LongType* inputShape, void* output,
|
||||
const LongType* outputShape, double leftBound, double rightBound) {
|
||||
__shared__ T* outputBuf;
|
||||
__shared__ T* inputBuf;
|
||||
__shared__ LongType length;
|
||||
__shared__ LongType inputRank;
|
||||
__shared__ LongType outputRank;
|
||||
__shared__ LongType* inputShapePtr;
|
||||
__shared__ LongType* outputShapePtr;
|
||||
__shared__ LongType* inputStridePtr;
|
||||
__shared__ LongType* outputStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
outputBuf = reinterpret_cast<T*>(output);
|
||||
inputBuf = reinterpret_cast<T*>(input);
|
||||
length = shape::length(inputShape);
|
||||
|
||||
// Cache shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
for (LongType e = tid; e < length; e += step) {
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType inputOffset;
|
||||
LongType outputOffset;
|
||||
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, inputOffset);
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, outputOffset);
|
||||
|
||||
if (inputBuf[inputOffset] > rightBound)
|
||||
outputBuf[outputOffset] = (T)rightBound;
|
||||
else if (inputBuf[inputOffset] < leftBound)
|
||||
outputBuf[outputOffset] = (T)leftBound;
|
||||
else
|
||||
outputBuf[outputOffset] = inputBuf[inputOffset];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void clipByValue_(LaunchContext* context, NDArray* input, double leftBound, double rightBound,
|
||||
NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
if (!input->isActualOnDeviceSide()) input->syncToDevice();
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
dim3 launchDims = getLaunchDims("clip");
|
||||
clipByValueKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(input->specialBuffer(), input->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), leftBound,
|
||||
rightBound);
|
||||
sd::DebugHelper::checkGlobalErrorCode("clipByValue failed");
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
|
||||
void clipByValue(LaunchContext* context, NDArray* input, double leftBound, double rightBound, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), clipByValue_, (context, input, leftBound, rightBound, output),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void clipByValue_, (sd::LaunchContext * context, NDArray* input, double leftBound,
|
||||
double rightBound, NDArray* output);
|
||||
, SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,141 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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, created on 30.11.17.
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/col2im.h>
|
||||
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// columns [bS, iC, kH, kW, oH, oW] to be de-convoluted to image [bS, iC, iH, iW]
|
||||
template <typename T>
|
||||
static SD_KERNEL void col2imCuda(const void* columns, const LongType* colShapeInfo, void* image,
|
||||
const LongType* imShapeInfo, const LongType sH, const LongType sW, const LongType pH,
|
||||
const LongType pW, const LongType dH, const LongType dW) {
|
||||
const T* col = reinterpret_cast<const T*>(columns);
|
||||
T* im = reinterpret_cast<T*>(image);
|
||||
|
||||
__shared__ LongType kH, kW, oH, oW;
|
||||
__shared__ LongType imLen;
|
||||
__shared__ LongType imRank;
|
||||
__shared__ LongType colRank;
|
||||
__shared__ LongType* imShape;
|
||||
__shared__ LongType* colShape;
|
||||
__shared__ LongType* imStride;
|
||||
__shared__ LongType* colStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
kH = dH * (colShapeInfo[3] - 1) + 1;
|
||||
kW = dW * (colShapeInfo[4] - 1) + 1;
|
||||
oH = colShapeInfo[5];
|
||||
oW = colShapeInfo[6];
|
||||
imLen = shape::length(imShapeInfo);
|
||||
|
||||
// Cache shape information
|
||||
imRank = shape::rank(imShapeInfo);
|
||||
colRank = shape::rank(colShapeInfo);
|
||||
imShape = shape::shapeOf(imShapeInfo);
|
||||
colShape = shape::shapeOf(colShapeInfo);
|
||||
imStride = shape::stride(imShapeInfo);
|
||||
colStride = shape::stride(colShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < imLen; i += gridDim.x * blockDim.x) {
|
||||
INDEX2COORDS(i, imRank, imShape, coords);
|
||||
|
||||
LongType imOffset;
|
||||
COORDS2INDEX(imRank, imStride, coords, imOffset);
|
||||
|
||||
const auto bSiCoffset = coords[0] * colShape[7] + coords[1] * colShape[8];
|
||||
|
||||
const LongType imH = coords[2] + pH;
|
||||
const LongType imW = coords[3] + pW;
|
||||
|
||||
const LongType colHstart = (imH < kH) ? 0 : (imH - kH) / sH + 1;
|
||||
const LongType colWstart = (imW < kW) ? 0 : (imW - kW) / sW + 1;
|
||||
|
||||
const LongType colHend = sd::math::sd_min<LongType>(imH / sH + 1, oH);
|
||||
const LongType colWend = sd::math::sd_min<LongType>(imW / sW + 1, oW);
|
||||
|
||||
T val = static_cast<T>(0);
|
||||
|
||||
for (coords[4] = colHstart; coords[4] < colHend; ++coords[4]) {
|
||||
coords[2] = imH - coords[4] * sH;
|
||||
if (coords[2] % dH != 0) continue;
|
||||
|
||||
for (coords[5] = colWstart; coords[5] < colWend; ++coords[5]) {
|
||||
coords[3] = imW - coords[5] * sW;
|
||||
if (coords[3] % dW != 0) continue;
|
||||
|
||||
LongType colOffset;
|
||||
COORDS2INDEX(colRank, colStride, coords, colOffset);
|
||||
|
||||
val += col[bSiCoffset + colOffset];
|
||||
}
|
||||
}
|
||||
im[imOffset] = val;
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void col2imCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* columns, const LongType* colShapeInfo,
|
||||
void* image, const LongType* imShapeInfo, const LongType sH, const LongType sW, const LongType pH,
|
||||
const LongType pW, const LongType dH, const LongType dW) {
|
||||
col2imCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(columns, colShapeInfo, image, imShapeInfo, sH,
|
||||
sW, pH, pW, dH, dW);
|
||||
DebugHelper::checkGlobalErrorCode( "col2im(...) failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
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){
|
||||
PointersManager manager(&context, "col2im");
|
||||
dim3 dims = getCol2imLaunchParams(*input,*output);
|
||||
|
||||
NDArray::prepareSpecialUse({input}, {output});
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), col2imCudaLauncher,
|
||||
(dims.x, dims.y, dims.z, context.getCudaStream(), output->specialBuffer(),
|
||||
output->specialShapeInfo(), input->specialBuffer(), input->specialShapeInfo(), sH, sW, pH, pW, dH, dW),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({input}, {output});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/LoopsCoordsHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/adjust_hue.h>
|
||||
#include <ops/declarable/helpers/imagesHelpers.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename X>
|
||||
SD_HOST_DEVICE static 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 <>
|
||||
SD_HOST_DEVICE 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>
|
||||
SD_HOST_DEVICE static 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 <>
|
||||
SD_HOST_DEVICE 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 T>
|
||||
static void SD_KERNEL cmpBitpack(const void* vx, void* vz, int rank, int len, const LongType* xStridesExtended,
|
||||
const LongType* outPutShapeInfo, T threshold) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
uint8_t* z = reinterpret_cast<uint8_t*>(vz);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto shapes = shape::shapeOf(outPutShapeInfo);
|
||||
auto zStrides = shape::stride(outPutShapeInfo);
|
||||
LongType coords[SD_MAX_RANK] = {};
|
||||
LongType* ptr_coords = (LongType*)&coords;
|
||||
// its extended as {rank+1} so xStridesExtended[rank] is valid
|
||||
auto inLastStride = xStridesExtended[rank];
|
||||
|
||||
for (auto k = tid; k < len; k += gridDim.x * blockDim.x) {
|
||||
INDEX2COORDS(k, rank, shapes, ptr_coords);
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStridesExtended, ptr_coords, xOffset);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStrides, ptr_coords, zOffset);
|
||||
|
||||
auto buffPart = &(x[xOffset]);
|
||||
auto outBuffPart = &(z[zOffset]);
|
||||
*outBuffPart = pack<T>(buffPart, inLastStride, threshold);
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void SD_KERNEL cmpBitpackEws(const void* vx, void* vz, int len, const LongType xStride,
|
||||
const LongType yStride, T threshold) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
uint8_t* z = reinterpret_cast<uint8_t*>(vz);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (xStride == 1) {
|
||||
for (auto k = tid; k < len; k += gridDim.x * blockDim.x) {
|
||||
auto buffPart = &(x[k * 8]);
|
||||
auto outBuffPart = &(z[k * yStride]);
|
||||
*outBuffPart = pack<T>(buffPart, threshold);
|
||||
}
|
||||
} else {
|
||||
for (auto k = tid; k < len; k += gridDim.x * blockDim.x) {
|
||||
auto buffPart = &(x[k * 8 * xStride]);
|
||||
auto outBuffPart = &(z[k * yStride]);
|
||||
*outBuffPart = pack<T>(buffPart, xStride, threshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_HOST void cmpBitpackCudaLauncher(graph::Context& block, NDArray& input,
|
||||
|
||||
NDArray& thresholdScalar, NDArray& output) {
|
||||
T threshold = thresholdScalar.e<T>(0);
|
||||
|
||||
auto inStrides = input.stridesOf();
|
||||
auto rank = output.rankOf();
|
||||
|
||||
// threadblock size
|
||||
// grid size
|
||||
auto stream = block.launchContext()->getCudaStream();
|
||||
dim3 compareAndBitpackDims = getCompareAndBitpackDims(output.lengthOf());
|
||||
PointersManager manager(block.launchContext(), "compare_and_bitpack");
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
if (input.ordering() == 'c' && output.ordering() == 'c') {
|
||||
cmpBitpackEws<T><<<compareAndBitpackDims.y, compareAndBitpackDims.x,compareAndBitpackDims.z>>>(input.specialBuffer(), output.specialBuffer(),
|
||||
output.lengthOf(), inStrides[rank - 1],
|
||||
output.stridesOf()[rank - 1], threshold);
|
||||
sd::DebugHelper::checkGlobalErrorCode("cmpBitpackEws failed");
|
||||
|
||||
} 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
|
||||
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];
|
||||
|
||||
auto strideSize = (rank + 1) * sizeof(LongType);
|
||||
LongType* extendedStridesDevPtr =
|
||||
reinterpret_cast<LongType*>(manager.replicatePointer(extendedStrides, strideSize));
|
||||
cmpBitpack<T><<<compareAndBitpackDims.y, compareAndBitpackDims.x,compareAndBitpackDims.z>>>(input.specialBuffer(), output.specialBuffer(), rank,
|
||||
output.lengthOf(), extendedStridesDevPtr,
|
||||
output.specialShapeInfo(), threshold);
|
||||
sd::DebugHelper::checkGlobalErrorCode("compareAndBitpackDims failed");
|
||||
|
||||
}
|
||||
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
void compareAndBitpack(graph::Context& block, NDArray& input, NDArray& threshold, NDArray& output) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), cmpBitpackCudaLauncher, (block, input, threshold, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,154 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <ops/declarable/helpers/compare_elem.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static SD_KERNEL void comparator(void *vx, const LongType *xShapeInfo, LongType length, const bool isStrict,
|
||||
void *reductionBuffer, bool *z) {
|
||||
auto x = reinterpret_cast<T *>(vx);
|
||||
auto reduction = reinterpret_cast<uint32_t *>(reductionBuffer);
|
||||
|
||||
extern __shared__ uint32_t shared[];
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
// Cache shape information in shared memory
|
||||
__shared__ LongType xRank;
|
||||
__shared__ LongType *xShape;
|
||||
__shared__ LongType *xStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
shared[threadIdx.x] = 0;
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset0;
|
||||
LongType xOffset1;
|
||||
|
||||
// each thread will compare 2 elements: E and E+1
|
||||
for (int e = tid; e < length - 1; e += blockDim.x * gridDim.x) {
|
||||
INDEX2COORDS(e, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset0);
|
||||
INDEX2COORDS(e + 1, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset1);
|
||||
|
||||
auto val0 = x[xOffset0];
|
||||
auto val1 = x[xOffset1];
|
||||
|
||||
bool v = false;
|
||||
if (isStrict)
|
||||
v = val1 > val0;
|
||||
else
|
||||
v = val1 >= val0;
|
||||
|
||||
// store comparison result in shared memory
|
||||
shared[threadIdx.x] += v ? 0 : 1;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// aggregate sums in shared memory
|
||||
for (LongType activeThreads = blockDim.x / 2; activeThreads > 0; activeThreads /= 2) {
|
||||
if (threadIdx.x < activeThreads) shared[threadIdx.x] += shared[threadIdx.x + activeThreads];
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// store over the grid if we have more than 1 block
|
||||
if (gridDim.x > 1) {
|
||||
auto tc = reinterpret_cast<unsigned int *>(reductionBuffer);
|
||||
__shared__ bool amLast;
|
||||
|
||||
tid = threadIdx.x;
|
||||
if (threadIdx.x == 0) reduction[blockIdx.x] = shared[0];
|
||||
|
||||
__threadfence();
|
||||
__syncthreads();
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
unsigned int ticket = atomicInc(&tc[16384], gridDim.x);
|
||||
amLast = (ticket == gridDim.x - 1);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (amLast) {
|
||||
tc[16384] = 0;
|
||||
shared[threadIdx.x] = 0;
|
||||
|
||||
for (int i = threadIdx.x; i < gridDim.x; i += blockDim.x) shared[threadIdx.x] += reduction[i];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
for (LongType activeThreads = blockDim.x / 2; activeThreads > 0; activeThreads /= 2) {
|
||||
if (threadIdx.x < activeThreads) shared[threadIdx.x] += shared[threadIdx.x + activeThreads];
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
z[0] = shared[0] == 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if we have only 1 block, we just store results right away
|
||||
if (threadIdx.x == 0) {
|
||||
auto tc = reinterpret_cast<unsigned int *>(reductionBuffer);
|
||||
tc[16384] = 0;
|
||||
z[0] = shared[0] == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void _compare_elem(LaunchContext *context, NDArray *input, bool isStrictlyIncreasing, bool &output) {
|
||||
auto z = NDArrayFactory::create<bool>(false, context);
|
||||
|
||||
dim3 compareElemDims = getCompareElem(input->lengthOf());
|
||||
comparator<T><<<compareElemDims.x,compareElemDims.y,compareElemDims.z, *context->getCudaStream()>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), input->lengthOf(), isStrictlyIncreasing,
|
||||
context->getReductionPointer(), reinterpret_cast<bool *>(z.specialBuffer()));
|
||||
|
||||
z->tickWriteDevice();
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(), "is_strictly_increasing");
|
||||
|
||||
output = z->e<bool>(0);
|
||||
}
|
||||
|
||||
void compare_elem(LaunchContext *context, NDArray *input, bool isStrictlyIncreasing, bool &output) {
|
||||
auto xType = input->dataType();
|
||||
input->syncToDevice();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, _compare_elem, (context, input, isStrictlyIncreasing, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _compare_elem,
|
||||
(sd::LaunchContext * context, NDArray *A, bool isStrictlyIncreasing, bool &output);
|
||||
, SD_COMMON_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,169 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
|
||||
template <typename T>
|
||||
SD_KERNEL static void concatCuda(void* pVx, void* pxShapeInfo, void* vz, const sd::LongType* zShapeInfo,
|
||||
const int axis) {
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType zLen, totalThreads;
|
||||
__shared__ LongType zRank;
|
||||
__shared__ LongType* zShape;
|
||||
__shared__ LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
zLen = shape::length(zShapeInfo);
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
// Cache shape information
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
for (LongType i = tid; i < zLen; i += totalThreads) {
|
||||
INDEX2COORDS(i, zRank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(zRank, zStride, coords, zOffset);
|
||||
|
||||
int inArrIdx = 0;
|
||||
LongType* xShapeInfo = reinterpret_cast<sd::LongType**>(pxShapeInfo)[inArrIdx];
|
||||
|
||||
// Cache the input array's shape information for the current iteration
|
||||
LongType xRank = shape::rank(xShapeInfo);
|
||||
LongType* xStride = shape::stride(xShapeInfo);
|
||||
|
||||
while (coords[axis] >= xShapeInfo[axis + 1]) {
|
||||
coords[axis] -= xShapeInfo[axis + 1];
|
||||
xShapeInfo = reinterpret_cast<sd::LongType**>(pxShapeInfo)[++inArrIdx];
|
||||
// Update shape information for new input array
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
}
|
||||
|
||||
const auto* x = reinterpret_cast<T*>(reinterpret_cast<void**>(pVx)[inArrIdx]);
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStride, coords, xOffset);
|
||||
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_HOST static void concatCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, void* pVx, void* pxShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const int axis) {
|
||||
concatCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(pVx, pxShapeInfo, vz, zShapeInfo, axis);
|
||||
DebugHelper::checkGlobalErrorCode("concat general case failed(...) failed");
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void concat(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output, const int axis) {
|
||||
const int numInArrs = inArrs.size();
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, inArrs);
|
||||
|
||||
bool luckCase1 = false;
|
||||
|
||||
// prepare arrays of pointers on buffers and shapes
|
||||
std::vector<const void*> hInBuffers(numInArrs);
|
||||
std::vector<const LongType*> hInShapeInfo(numInArrs);
|
||||
std::vector <int> lenPerArray(numInArrs);
|
||||
for (int i = 0; i < numInArrs; i++) {
|
||||
hInBuffers[i] = inArrs[i]->specialBuffer();
|
||||
hInShapeInfo[i] = inArrs[i]->specialShapeInfo();
|
||||
lenPerArray[i] = inArrs[i]->isEmpty() ? 0 : inArrs[i]->isScalar() ? 1 : inArrs[i]->lengthOf();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "helpers::concat");
|
||||
|
||||
void* dInBuffers = manager.replicatePointer(hInBuffers.data(), hInBuffers.size() * sizeof(void*));
|
||||
|
||||
dim3 dims = getConcat(output.lengthOf());
|
||||
|
||||
if (luckCase1) { // for example {1,10} + {2,10} + {3,10} = {6, 10} order c; or {10,1} + {10,2} + {10,3} = {10, 6}
|
||||
void* z = static_cast<int8_t*>(output.specialBuffer());
|
||||
|
||||
for (sd::LongType i = 0; i < numInArrs; ++i) {
|
||||
const auto sizeofT = output.sizeOfT();
|
||||
const auto memAmountToCopy = inArrs[i]->lengthOf() * sizeofT;
|
||||
cudaMemcpyAsync(z, reinterpret_cast<const int8_t*>(inArrs[i]->specialBuffer()), memAmountToCopy,
|
||||
cudaMemcpyDeviceToDevice, *context->getCudaStream());
|
||||
z = static_cast<int8_t*>(z) + memAmountToCopy;
|
||||
}
|
||||
|
||||
if (cudaStreamSynchronize(*context->getCudaStream()) != 0)
|
||||
THROW_EXCEPTION("concat cuda: luckCase1 failed!");
|
||||
|
||||
for (int i = 0; i < numInArrs; ++i) inArrs[i]->tickReadDevice();
|
||||
output.tickWriteDevice();
|
||||
manager.synchronize();
|
||||
output.syncToHost();
|
||||
return;
|
||||
}
|
||||
|
||||
void* dInShapeInfo = manager.replicatePointer(hInShapeInfo.data(), hInShapeInfo.size() * sizeof(LongType*));
|
||||
|
||||
BUILD_SINGLE_SELECTOR(inArrs[0]->dataType(), concatCudaLauncher,
|
||||
(dims.x, dims.y, dims.z, context->getCudaStream(), dInBuffers, dInShapeInfo,
|
||||
output.specialBuffer(), output.specialShapeInfo(), axis),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
manager.synchronize();
|
||||
manager.synchronize();
|
||||
output.syncToHost();
|
||||
NDArray::registerSpecialUse({&output}, inArrs);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,159 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
|
||||
#include <ops/declarable/helpers/confusion.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
SD_KERNEL static void copyBuffers(LongType* destination, void const* source, LongType bufferLength) {
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
const T * sourceCast = reinterpret_cast<T const*>(source);
|
||||
for (int t = tid; t < bufferLength; t += step) {
|
||||
destination[t] = static_cast<LongType>(sourceCast[t]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
SD_KERNEL static void confusionFunctorKernel(LongType* labelsBuffer, LongType* predictionBuffer, LongType bufferLength, void const* weightsBuffer, void* outputBuffer,
|
||||
const LongType* tadShape, const LongType* tadOffsets) {
|
||||
__shared__ int arrIdx, blocksPerArr;
|
||||
__shared__ T* z;
|
||||
__shared__ T const* w;
|
||||
__shared__ LongType *zShapeInfo, *xShapeInfo, arrLen;
|
||||
__shared__ LongType tadRank;
|
||||
__shared__ LongType* tadShapePtr;
|
||||
__shared__ LongType* tadStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
z = reinterpret_cast<T*>(outputBuffer);
|
||||
w = reinterpret_cast<T const*>(weightsBuffer);
|
||||
arrLen = shape::length(tadShape);
|
||||
|
||||
// Cache shape information
|
||||
tadRank = shape::rank(tadShape);
|
||||
tadShapePtr = shape::shapeOf(tadShape);
|
||||
tadStridePtr = shape::stride(tadShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
LongType predCoords[SD_MAX_RANK];
|
||||
LongType predOffset;
|
||||
|
||||
for (int t = tid; t < bufferLength; t += step) {
|
||||
auto label = labelsBuffer[t];
|
||||
auto pred = predictionBuffer[t];
|
||||
auto tZ = z + tadOffsets[label];
|
||||
T val = (weightsBuffer == nullptr ? (T)1.0f : w[t]);
|
||||
|
||||
INDEX2COORDS(pred, tadRank, tadShapePtr, predCoords);
|
||||
COORDS2INDEX(tadRank, tadStridePtr, predCoords, predOffset);
|
||||
tZ[predOffset] = val;
|
||||
}
|
||||
}
|
||||
template <typename X, typename Z>
|
||||
void _confusionFunctor(LaunchContext* context, NDArray* labels, NDArray* predictions, NDArray* weights,
|
||||
NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
|
||||
auto pack = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), 1);
|
||||
PointersManager manager(context, "helpers::confusion");
|
||||
predictions->syncToDevice();
|
||||
LongType* labelsLongBuffer = labels->dataType() == INT64 ? (LongType*)labels->specialBuffer() : nullptr;
|
||||
LongType* predictionLongBuffer =
|
||||
predictions->dataType() == INT64 ? (LongType*)predictions->specialBuffer() : nullptr;
|
||||
|
||||
|
||||
|
||||
dim3 conf = getLaunchDims("confusion_matrix");
|
||||
if (labelsLongBuffer == nullptr) {
|
||||
auto err = cudaMalloc(&labelsLongBuffer, labels->lengthOf() * sizeof(LongType));
|
||||
if (err != 0) throw cuda_exception::build("Cannot allocate memory for labels long buffer", err);
|
||||
// copy with type conversion
|
||||
copyBuffers<X><<<conf.x, conf.y, conf.z, *stream>>>(labelsLongBuffer, labels->specialBuffer(), labels->lengthOf());
|
||||
sd::DebugHelper::checkGlobalErrorCode("copyBuffers failed");
|
||||
|
||||
}
|
||||
|
||||
if (predictionLongBuffer == nullptr) {
|
||||
auto err = cudaMalloc(&predictionLongBuffer, predictions->lengthOf() * sizeof(LongType));
|
||||
if (err != 0) throw cuda_exception::build("Cannot allocate memory for predictions long buffer", err);
|
||||
// copy with type conversion
|
||||
copyBuffers<X>
|
||||
<<<256, 512, 1024, *stream>>>(predictionLongBuffer, predictions->specialBuffer(), predictions->lengthOf());
|
||||
sd::DebugHelper::checkGlobalErrorCode("copyBuffers failed");
|
||||
|
||||
}
|
||||
|
||||
manager.synchronize();
|
||||
|
||||
|
||||
|
||||
auto bufferLength = labels->lengthOf();
|
||||
dim3 launchDims = getLaunchDims("confusionMatrix");
|
||||
confusionFunctorKernel<Z><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
labelsLongBuffer, predictionLongBuffer, bufferLength, weights != nullptr ? weights->specialBuffer() : nullptr,
|
||||
output->specialBuffer(), pack->specialShapeInfo(), pack->specialOffsets());
|
||||
sd::DebugHelper::checkGlobalErrorCode("confusionFunctorKernel failed");
|
||||
|
||||
manager.synchronize();
|
||||
|
||||
if (predictionLongBuffer != predictions->specialBuffer()) {
|
||||
cudaError_t err = cudaFree(predictionLongBuffer);
|
||||
if (err != 0) throw cuda_exception::build("Cannot deallocate memory for predictions long buffer", err);
|
||||
}
|
||||
|
||||
if (labelsLongBuffer != labels->specialBuffer()) {
|
||||
cudaError_t err = cudaFree(labelsLongBuffer);
|
||||
if (err != 0) throw cuda_exception::build("Cannot deallocate memory for labels long buffer", err);
|
||||
}
|
||||
}
|
||||
|
||||
void confusionFunctor(LaunchContext* context, NDArray* labels, NDArray* predictions, NDArray* weights,
|
||||
NDArray* output) {
|
||||
auto xType = predictions->dataType();
|
||||
auto zType = output->dataType(); // weights can be null
|
||||
NDArray::prepareSpecialUse({output}, {labels, predictions, weights});
|
||||
BUILD_DOUBLE_SELECTOR(xType, zType, _confusionFunctor, (context, labels, predictions, weights, output),
|
||||
SD_INDEXING_TYPES, SD_NUMERIC_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {labels, predictions, weights});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <helpers/PointersManager.h>
|
||||
#include <math/templatemath.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include <helpers/DebugHelper.h>
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// columns [bS, iC, kD, kH, kW, oD, oH, oW] to be de-convoluted to volume [bS, iC, iD, iH, iW]
|
||||
template <typename T>
|
||||
static SD_KERNEL void col2volCuda(const void* columns, const LongType* colShapeInfo, void* volume,
|
||||
const LongType* volShapeInfo, 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 T* col = reinterpret_cast<const T*>(columns);
|
||||
T* vol = reinterpret_cast<T*>(volume);
|
||||
|
||||
__shared__ LongType kD, kH, kW, oD, oH, oW, *sharedMem;
|
||||
__shared__ LongType volLen;
|
||||
__shared__ LongType volRank;
|
||||
__shared__ LongType* volShape;
|
||||
__shared__ LongType* volStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
oD = colShapeInfo[6];
|
||||
oH = colShapeInfo[7];
|
||||
oW = colShapeInfo[8];
|
||||
|
||||
kD = dD * (colShapeInfo[3] - 1) + 1;
|
||||
kH = dH * (colShapeInfo[4] - 1) + 1;
|
||||
kW = dW * (colShapeInfo[5] - 1) + 1;
|
||||
|
||||
volLen = shape::length(volShapeInfo);
|
||||
|
||||
// Cache shape information
|
||||
volRank = shape::rank(volShapeInfo);
|
||||
volShape = shape::shapeOf(volShapeInfo);
|
||||
volStride = shape::stride(volShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * 8;
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < volLen; i += gridDim.x * blockDim.x) {
|
||||
INDEX2COORDS(i, volRank, volShape, coords);
|
||||
|
||||
sd::LongType volOffset;
|
||||
COORDS2INDEX(volRank, volStride, coords, volOffset);
|
||||
|
||||
const auto bSiCoffset = coords[0] * colShapeInfo[9] + coords[1] * colShapeInfo[10];
|
||||
|
||||
const LongType imD = coords[2] + pD;
|
||||
const LongType imH = coords[3] + pH;
|
||||
const LongType imW = coords[4] + pW;
|
||||
|
||||
const LongType colDstart = (imD < kD) ? 0 : (imD - kD) / sD + 1;
|
||||
const LongType colHstart = (imH < kH) ? 0 : (imH - kH) / sH + 1;
|
||||
const LongType colWstart = (imW < kW) ? 0 : (imW - kW) / sW + 1;
|
||||
|
||||
const LongType colDend = sd::math::sd_min<LongType>(imD / sD + 1, oD);
|
||||
const LongType colHend = sd::math::sd_min<LongType>(imH / sH + 1, oH);
|
||||
const LongType colWend = sd::math::sd_min<LongType>(imW / sW + 1, oW);
|
||||
|
||||
T val = static_cast<T>(0);
|
||||
|
||||
for (LongType colD = colDstart; colD < colDend; ++colD) {
|
||||
coords[2] = imD - colD * sD;
|
||||
if (coords[2] % dD != 0) continue;
|
||||
|
||||
for (LongType colH = colHstart; colH < colHend; ++colH) {
|
||||
coords[3] = imH - colH * sH;
|
||||
if (coords[3] % dH != 0) continue;
|
||||
|
||||
for (LongType colW = colWstart; colW < colWend; ++colW) {
|
||||
coords[4] = imW - colW * sW;
|
||||
if (coords[4] % dW != 0) continue;
|
||||
|
||||
val += col[bSiCoffset + (coords[2] / dD) * colShapeInfo[11] + (coords[3] / dH) * colShapeInfo[12] +
|
||||
(coords[4] / dW) * colShapeInfo[13] + colD * colShapeInfo[14] + colH * colShapeInfo[15] +
|
||||
colW * colShapeInfo[16]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vol[volOffset] = val;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void col2volCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* columns, const LongType* colShapeInfo,
|
||||
void* volume, const LongType* volShapeInfo, 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) {
|
||||
col2volCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(columns, colShapeInfo, volume, volShapeInfo,
|
||||
sD, sH, sW, pD, pH, pW, dD, dH, dW);
|
||||
DebugHelper::checkGlobalErrorCode( "col2vol(...) failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::col2vol(graph::Context& block, NDArray& col, NDArray& vol, 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) {
|
||||
PointersManager manager(block.launchContext(), "col2vol");
|
||||
|
||||
|
||||
dim3 col2VolDims = getCol2VolDims(vol.lengthOf(), col.rankOf());
|
||||
|
||||
NDArray::prepareSpecialUse({&vol}, {&col});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
vol.dataType(), col2volCudaLauncher,
|
||||
(col2VolDims.x, col2VolDims.y, col2VolDims.z, block.launchContext()->getCudaStream(), col.specialBuffer(),
|
||||
col.specialShapeInfo(), vol.specialBuffer(), vol.specialShapeInfo(), sD, sH, sW, pD, pH, pW, dD, dH, dW),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&vol}, {&col});
|
||||
|
||||
manager.synchronize();
|
||||
DebugHelper::checkGlobalErrorCode( "col2vol(...) failed");
|
||||
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <helpers/MmulHelper.h>
|
||||
#include <helpers/PointersManager.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>
|
||||
|
||||
|
||||
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, iC, kH, kW, oH, oW};
|
||||
NDArray *col = new NDArray('c', colShape, input->dataType(), input->getContext());
|
||||
std::vector<LongType> colPermute = {0, 3, 4, 5, 1, 2}; // {bS, iC, kH, kW, oH, oW}
|
||||
NDArray *colP = col->permute(colPermute, false, false); // permute() already returns NDArray*
|
||||
std::vector<sd::LongType> mmulResShape = {bS * oH * oW, oC};
|
||||
NDArray mmulResult('f', mmulResShape, output->dataType(), output->getContext());
|
||||
|
||||
std::vector<LongType> permuteForOutput = {0, 3, 1, 2};
|
||||
|
||||
//----- calculation of output -----//
|
||||
auto ctx = block.launchContext();
|
||||
|
||||
|
||||
NDArray zero = NDArrayFactory::create(0.f, input->getContext());
|
||||
if (isNCHW) {
|
||||
helpers::im2col(*ctx, *input, *colP, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
zero);
|
||||
} else {
|
||||
std::vector<sd::LongType> permute = {0, 3, 1, 2};
|
||||
// For NHWC, we need to permute the input to NCHW before im2col
|
||||
NDArray* inputNchw = input->permute(permute, 0, false); // permute() already returns NDArray*
|
||||
helpers::im2col(*ctx, *inputNchw, *colP, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
zero);
|
||||
delete inputNchw; // Clean up permuted array
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<sd::LongType> permute = {0, 3, 4, 5, 1, 2};
|
||||
block.pushIntermediateResult(col);
|
||||
|
||||
std::vector<sd::LongType> shape = {bS * oH * oW, kW * kH * iC};
|
||||
auto im2colReshape = col->reshape('c', shape, true);
|
||||
|
||||
auto weightsPermuted = weights->permute(permuteForOutput, 0, false);
|
||||
std::vector<LongType> weightShape = {iC * kH * kW, oC};
|
||||
auto reshapedW = weightsPermuted.reshape('f', weightShape, false);
|
||||
MmulHelper::matmul(&im2colReshape, &reshapedW, &mmulResult, false, false, 1.0, 0.0);
|
||||
|
||||
|
||||
std::vector<LongType> mmulResultShape = {oH, oW, bS, oC};
|
||||
auto reshaped = mmulResult.reshape('f', mmulResultShape, false);
|
||||
std::vector<sd::LongType> permutedShape = {2, 3, 1,0};
|
||||
auto permuted = reshaped.permute(permutedShape, 0, false);
|
||||
|
||||
// Reshape and copy result to output
|
||||
if (isNCHW) {
|
||||
output->assign(&permuted);
|
||||
} else {
|
||||
std::vector<sd::LongType> otherPermute = {0,2,3,1};
|
||||
permuted = permuted.permute(otherPermute, 0, false);
|
||||
output->assign(&permuted);
|
||||
}
|
||||
|
||||
//----- 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
|
||||
@@ -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)
|
||||
//
|
||||
#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); // permute() already returns NDArray*
|
||||
gradOPermuted = gradO->permute(permute,false,false);
|
||||
gradIPermuted = gradI->permute(permute,false,false);
|
||||
} else {
|
||||
inputPermuted = input;
|
||||
gradOPermuted = gradO;
|
||||
gradIPermuted = gradI;
|
||||
}
|
||||
|
||||
std::vector<sd::LongType> reshape = {oC,bS * oH * oW};
|
||||
// Reshape gradO to 2D: [oC, bS * oH * oW]
|
||||
NDArray gradO2d = gradOPermuted->reshape(gradOPermuted->ordering(), reshape,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 zero = NDArrayFactory::create<double>(0., inputPermuted->getContext());
|
||||
helpers::im2col(*ctx, *inputPermuted, *columns, kH, kW, sH, sW, pH, pW, dH, dW,
|
||||
zero);
|
||||
}
|
||||
|
||||
// Calculate gradW
|
||||
if (gradW) {
|
||||
std::vector<sd::LongType> columnsShape = {bS * oH * oW, iC * kH * kW};
|
||||
std::vector<sd::LongType> wShape = {oC, iC * kH * kW};
|
||||
NDArray columns2d = columns->reshape('c', columnsShape,false);
|
||||
std::vector<sd::LongType > permu = {1,0};
|
||||
NDArray &gradW2d = gradW->reshape('f', wShape,false).permute(permu,false,false);
|
||||
|
||||
MmulHelper::matmul( &columns2d,&gradO2d, &gradW2d, true, true, 1.0, 0.0, &gradW2d);
|
||||
gradW->assign(&gradW2d);
|
||||
|
||||
}
|
||||
|
||||
// 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> permute = {3,2,1,0};
|
||||
std::vector<sd::LongType> shape2 = {iC * kH * kW, oC};
|
||||
weights2d = weights->permute(permute,false,false).reshape('f', shape2);
|
||||
} else if (wFormat == 1) {
|
||||
std::vector<sd::LongType> shape2 = {iC * kH * kW, oC};
|
||||
weights2d = weights->reshape('f', shape2);
|
||||
} else {
|
||||
std::vector<sd::LongType> shape2 = {iC * kH * kW, oC};
|
||||
std::vector<sd::LongType> permute3 = {0, 2, 3, 1};
|
||||
weights2d = weights->permute(permute3,false,false).reshape('f', shape2);
|
||||
}
|
||||
|
||||
std::vector<sd::LongType> colShape2 = {iC * kH * kW, bS * oH * oW};
|
||||
NDArray columns2d('c', colShape2, columns->dataType(), columns->getContext());
|
||||
|
||||
|
||||
MmulHelper::matmul(&weights2d, &gradO2d, &columns2d, false, false, 1.0, 0.0);
|
||||
//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> epsPerm = {5,2,1,0,4,3};
|
||||
auto permuted = eps6d->permute(epsPerm,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> permute = {0, 2,3,1};
|
||||
auto permuted = gradIPermuted->permute(permute,false,false); // [bS, iC, iH, iW] -> [bS, iH, iW, iC]
|
||||
gradI->assign(&permuted); // [bS, iC, iH, iW] -> [bS, iH, iW, iC]
|
||||
}
|
||||
|
||||
// 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,122 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <helpers/MmulHelper.h>
|
||||
#include <helpers/PointersManager.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>
|
||||
|
||||
|
||||
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> permuteVec = {0, 3, 1, 2};
|
||||
input = input->permute(permuteVec, false, false); // permute() already returns NDArray*
|
||||
} 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]
|
||||
|
||||
if (bias)
|
||||
helpers::addBias(block, *output, *bias, *output, isNCHW);
|
||||
|
||||
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
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <helpers/MmulHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/col2im.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <ops/declarable/helpers/im2col.h>
|
||||
|
||||
|
||||
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<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<LongType>> modifGradO1, modifGradO2, modifWeights;
|
||||
std::vector<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> permuteVec = {0, 3, 1, 2};
|
||||
input = input->permute(permuteVec, false, false); // permute() already returns NDArray*
|
||||
gradI = gradI->permute(permuteVec, false, false);
|
||||
} 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<sd::LongType> colShape = {bS, iC, kH, kW, oH, oW};
|
||||
NDArray columns(input->ordering(), colShape, input->dataType(), input->getContext());
|
||||
NDArray gradOreshaped = gradO->reshape(gradO->ordering(), gradOreShape,false);
|
||||
|
||||
// ----- 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]
|
||||
MmulHelper::tensorDot(&columns, &gradOreshaped, gradW, modifColumns, modifGradO1,
|
||||
modifWeights); // [iC, kW*kH, bS*oH*oW] x [iC, bS*oH*oW, mC] = [iC, kH*kW, mC]
|
||||
|
||||
// ----- calculation of gradB ----- //
|
||||
if (gradB) {
|
||||
NDArray* gradBR = gradB;
|
||||
if (gradB->rankOf() == 2) {
|
||||
std::vector<sd::LongType> lenShape = {gradB->lengthOf()};
|
||||
gradBR = gradB->reshape(gradB->ordering(), lenShape, false);
|
||||
}
|
||||
std::vector<LongType> dims = {0, indOoH, indOoH + 1};
|
||||
gradO->reduceAlongDimension(reduce::Sum, gradBR,&dims, false); // sum over bS, oH, oW
|
||||
if (gradBR != gradB) delete gradBR;
|
||||
}
|
||||
|
||||
//----- calculation of gradI -----//
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
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
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <exceptions/cuda_exception.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <math/templatemath.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static SD_KERNEL void avgPooling2dCuda(const void *vx, const LongType *xShapeInfo, void *vz, const LongType *zShapeInfo,
|
||||
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 extraParam0) {
|
||||
// input is [bS, iC, iH, iW]
|
||||
// output is [bS, iC, oH, oW]
|
||||
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
auto z = reinterpret_cast<Z *>(vz);
|
||||
|
||||
__shared__ LongType bS, iC, oH, oW, iH, iW, strideB, strideC, strideY, strideX, strideOB, strideOC, strideOY,
|
||||
strideOX, length, kHEff, kWEff;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
bS = shape::sizeAt(xShapeInfo, 0);
|
||||
iC = shape::sizeAt(xShapeInfo, 1);
|
||||
oH = shape::sizeAt(zShapeInfo, 2);
|
||||
oW = shape::sizeAt(zShapeInfo, 3);
|
||||
iH = shape::sizeAt(xShapeInfo, 2);
|
||||
iW = shape::sizeAt(xShapeInfo, 3);
|
||||
|
||||
strideB = shape::stride(xShapeInfo)[0];
|
||||
strideC = shape::stride(xShapeInfo)[1];
|
||||
strideY = shape::stride(xShapeInfo)[2];
|
||||
strideX = shape::stride(xShapeInfo)[3];
|
||||
|
||||
strideOB = shape::stride(zShapeInfo)[0];
|
||||
strideOC = shape::stride(zShapeInfo)[1];
|
||||
strideOY = shape::stride(zShapeInfo)[2];
|
||||
strideOX = shape::stride(zShapeInfo)[3];
|
||||
|
||||
length = shape::length(zShapeInfo);
|
||||
|
||||
// Replace kernel H/W with *effective* kernel H/W accounting for dilation
|
||||
kHEff = kH + (kH - 1) * (dH - 1);
|
||||
kWEff = kW + (kW - 1) * (dW - 1);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (int index = tid; index < length; index += blockDim.x * gridDim.x) {
|
||||
const LongType pw = index % oW;
|
||||
const LongType ph = (index / oW) % oH;
|
||||
const LongType c = (index / oW / oH) % iC;
|
||||
const LongType n = index / oW / oH / iC;
|
||||
|
||||
LongType hstart = sH * ph - pH;
|
||||
LongType wstart = sW * pw - pW;
|
||||
LongType hend = hstart + kHEff;
|
||||
LongType wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(-hstart) / static_cast<double>(dH));
|
||||
hstart += f * dH;
|
||||
}
|
||||
if (wstart < 0) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(-wstart) / static_cast<double>(dW));
|
||||
wstart += f * dW;
|
||||
}
|
||||
if (hend > iH) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(hend - iH) / static_cast<double>(dH));
|
||||
hend -= f * dH;
|
||||
}
|
||||
if (wend > iW) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(wend - iW) / static_cast<double>(dW));
|
||||
wend -= f * dW;
|
||||
}
|
||||
|
||||
// Accounts for dilation
|
||||
int pool_size = sd::math::sd_ceil<double, int>(static_cast<double>(hend - hstart) / static_cast<double>(dH)) *
|
||||
sd::math::sd_ceil<double, int>(static_cast<double>(wend - wstart) / static_cast<double>(dW));
|
||||
|
||||
Z sum = static_cast<Z>(0.0f);
|
||||
|
||||
const X *inSlice = x + (n * strideB + c * strideC);
|
||||
|
||||
for (int h = hstart; h < hend; h += dH)
|
||||
for (int w = wstart; w < wend; w += dW) sum += static_cast<Z>(inSlice[h * strideY + w * strideX]);
|
||||
|
||||
int divide_factor = pool_size; // Case 0: exclude padding
|
||||
if (extraParam0 == 1) // Case 1: include padding
|
||||
divide_factor = kH * kW;
|
||||
|
||||
z[n * strideOB + c * strideOC + pw * strideOX + ph * strideOY] = sum / static_cast<Z>(divide_factor);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static void avgPooling2dCudaLauncher(LaunchContext &block, const void *vx, const LongType *vxShapeInfo, void *vz,
|
||||
const LongType *vzShapeInfo, 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 extraParam0) {
|
||||
dim3 launchDims = getLaunchDims("avg_pooling");
|
||||
avgPooling2dCuda<X, Z><<<launchDims.y, launchDims.x, launchDims.z, *block.getCudaStream()>>>(
|
||||
vx, vxShapeInfo, vz, vzShapeInfo, kH, kW, sH, sW, pH, pW, dH, dW, extraParam0);
|
||||
DebugHelper::checkErrorCode(block.getCudaStream(), "avgb pooling 2d failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static SD_KERNEL void pnormPooling2dCuda(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, 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 extraParam0) {
|
||||
// input is [bS, iC, iH, iW]
|
||||
// output is [bS, iC, oH, oW]
|
||||
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
auto z = reinterpret_cast<Z *>(vz);
|
||||
|
||||
__shared__ LongType bS, iC, oH, oW, iH, iW, strideB, strideC, strideY, strideX, strideOB, strideOC, strideOY,
|
||||
strideOX, length, kHEff, kWEff;
|
||||
__shared__ bool fOrder;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
bS = shape::sizeAt(xShapeInfo, 0);
|
||||
iC = shape::sizeAt(xShapeInfo, 1);
|
||||
oH = shape::sizeAt(zShapeInfo, 2);
|
||||
oW = shape::sizeAt(zShapeInfo, 3);
|
||||
iH = shape::sizeAt(xShapeInfo, 2);
|
||||
iW = shape::sizeAt(xShapeInfo, 3);
|
||||
|
||||
strideB = shape::stride(xShapeInfo)[0];
|
||||
strideC = shape::stride(xShapeInfo)[1];
|
||||
strideY = shape::stride(xShapeInfo)[2];
|
||||
strideX = shape::stride(xShapeInfo)[3];
|
||||
|
||||
strideOB = shape::stride(zShapeInfo)[0];
|
||||
strideOC = shape::stride(zShapeInfo)[1];
|
||||
strideOY = shape::stride(zShapeInfo)[2];
|
||||
strideOX = shape::stride(zShapeInfo)[3];
|
||||
|
||||
length = shape::length(zShapeInfo);
|
||||
|
||||
// Replace kernel H/W with *effective* kernel H/W accounting for dilation
|
||||
kHEff = kH + (kH - 1) * (dH - 1);
|
||||
kWEff = kW + (kW - 1) * (dW - 1);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (int index = tid; index < length; index += blockDim.x * gridDim.x) {
|
||||
const LongType pw = index % oW;
|
||||
const LongType ph = (index / oW) % oH;
|
||||
const LongType c = (index / oW / oH) % iC;
|
||||
const LongType n = index / oW / oH / iC;
|
||||
|
||||
LongType hstart = sH * ph - pH;
|
||||
LongType wstart = sW * pw - pW;
|
||||
LongType hend = hstart + kHEff;
|
||||
LongType wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(-hstart) / static_cast<double>(dH));
|
||||
hstart += f * dH;
|
||||
}
|
||||
if (wstart < 0) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(-wstart) / static_cast<double>(dW));
|
||||
wstart += f * dW;
|
||||
}
|
||||
if (hend > iH) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(hend - iH) / static_cast<double>(dH));
|
||||
hend -= f * dH;
|
||||
}
|
||||
if (wend > iW) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(wend - iW) / static_cast<double>(dW));
|
||||
wend -= f * dW;
|
||||
}
|
||||
|
||||
Z sum = static_cast<Z>(0.0f);
|
||||
|
||||
const X *inSlice = x + (n * strideB + c * strideC);
|
||||
|
||||
for (int h = hstart; h < hend; h += dH)
|
||||
for (int w = wstart; w < wend; w += dW)
|
||||
sum += math::sd_pow<Z, Z, Z>(static_cast<Z>(math::sd_abs<X,X>(inSlice[h * strideY + w * strideX])), static_cast<Z>(extraParam0));
|
||||
|
||||
z[n * strideOB + c * strideOC + pw * strideOX + ph * strideOY] = math::sd_pow<Z, Z, Z>(sum, static_cast<Z>(1.0f) / static_cast<Z>(extraParam0));
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static void pnormPooling2dCudaLauncher(LaunchContext &block, const void *vx, const LongType *vxShapeInfo, void *vz,
|
||||
const LongType *vzShapeInfo, 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 extraParam0) {
|
||||
dim3 launchDims = getLaunchDims("avg_pooling");
|
||||
pnormPooling2dCuda<X, Z><<<launchDims.y, launchDims.x, launchDims.z, *block.getCudaStream()>>>(
|
||||
vx, vxShapeInfo, vz, vzShapeInfo, kH, kW, sH, sW, pH, pW, dH, dW, extraParam0);
|
||||
DebugHelper::checkErrorCode(block.getCudaStream(), "pnorm pooling 2d failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static SD_KERNEL void maxPooling2dCuda(const void *vx, const LongType *xShapeInfo, void *vz, const LongType *zShapeInfo,
|
||||
const int kH, const LongType kW, const LongType sH, const LongType sW,
|
||||
const LongType pH, const LongType pW, const LongType dH, const LongType dW,
|
||||
const int extraParam0) {
|
||||
// input is [bS, iC, iH, iW]
|
||||
// output is [bS, iC, oH, oW]
|
||||
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
auto z = reinterpret_cast<Z *>(vz);
|
||||
|
||||
__shared__ LongType bS, iC, oH, oW, iH, iW, strideB, strideC, strideY, strideX, strideOB, strideOC, strideOY,
|
||||
strideOX, length, kHEff, kWEff;
|
||||
__shared__ bool fOrder;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
bS = shape::sizeAt(xShapeInfo, 0);
|
||||
iC = shape::sizeAt(xShapeInfo, 1);
|
||||
oH = shape::sizeAt(zShapeInfo, 2);
|
||||
oW = shape::sizeAt(zShapeInfo, 3);
|
||||
iH = shape::sizeAt(xShapeInfo, 2);
|
||||
iW = shape::sizeAt(xShapeInfo, 3);
|
||||
|
||||
strideB = shape::stride(xShapeInfo)[0];
|
||||
strideC = shape::stride(xShapeInfo)[1];
|
||||
strideY = shape::stride(xShapeInfo)[2];
|
||||
strideX = shape::stride(xShapeInfo)[3];
|
||||
|
||||
strideOB = shape::stride(zShapeInfo)[0];
|
||||
strideOC = shape::stride(zShapeInfo)[1];
|
||||
strideOY = shape::stride(zShapeInfo)[2];
|
||||
strideOX = shape::stride(zShapeInfo)[3];
|
||||
|
||||
length = shape::length(zShapeInfo);
|
||||
|
||||
// Replace kernel H/W with *effective* kernel H/W accounting for dilation
|
||||
kHEff = kH + (kH - 1) * (dH - 1);
|
||||
kWEff = kW + (kW - 1) * (dW - 1);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (int index = tid; index < length; index += blockDim.x * gridDim.x) {
|
||||
const LongType pw = index % oW;
|
||||
const LongType ph = (index / oW) % oH;
|
||||
const LongType c = (index / oW / oH) % iC;
|
||||
const LongType n = index / oW / oH / iC;
|
||||
|
||||
LongType hstart = sH * ph - pH;
|
||||
LongType wstart = sW * pw - pW;
|
||||
LongType hend = hstart + kHEff;
|
||||
LongType wend = wstart + kWEff;
|
||||
|
||||
if (hstart < 0) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(-hstart) / static_cast<double>(dH));
|
||||
hstart += f * dH;
|
||||
}
|
||||
if (wstart < 0) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(-wstart) / static_cast<double>(dW));
|
||||
wstart += f * dW;
|
||||
}
|
||||
if (hend > iH) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(hend - iH) / static_cast<double>(dH));
|
||||
hend -= f * dH;
|
||||
}
|
||||
if (wend > iW) {
|
||||
int f = math::sd_ceil<double, int>(static_cast<double>(wend - iW) / static_cast<double>(dW));
|
||||
wend -= f * dW;
|
||||
}
|
||||
// Accounts for dilation
|
||||
int pool_size = sd::math::sd_ceil<double, int>(static_cast<double>(hend - hstart) / static_cast<double>(dH)) *
|
||||
sd::math::sd_ceil<double, int>(static_cast<double>(wend - wstart) / static_cast<double>(dW));
|
||||
|
||||
Z max = -DataTypeUtils::max<Z>();
|
||||
|
||||
const X *inSlice = x + (n * strideB + c * strideC);
|
||||
|
||||
for (int h = hstart; h < hend; h += dH) {
|
||||
for (int w = wstart; w < wend; w += dW) {
|
||||
Z v = static_cast<Z>(inSlice[h * strideY + w * strideX]);
|
||||
if (v > max) max = v;
|
||||
}
|
||||
}
|
||||
|
||||
z[n * strideOB + c * strideOC + pw * strideOX + ph * strideOY] = max;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static void maxPooling2dCudaLauncher(LaunchContext &block, const void *vx, const LongType *vxShapeInfo, void *vz,
|
||||
const LongType *vzShapeInfo, 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 extraParam0, const int rank,
|
||||
const int len) {
|
||||
dim3 poolingDims = getPoolingDims(len, rank);
|
||||
maxPooling2dCuda<X, Z><<<poolingDims.y, poolingDims.x, poolingDims.z, *block.getCudaStream()>>>(
|
||||
vx, vxShapeInfo, vz, vzShapeInfo, kH, kW, sH, sW, pH, pW, dH, dW, extraParam0);
|
||||
DebugHelper::checkErrorCode(block.getCudaStream(), "max pooling 2d failed");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::pooling2d(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) {
|
||||
if (!input.isActualOnDeviceSide()) input.syncToDevice();
|
||||
|
||||
switch (poolingMode) {
|
||||
case MAX_POOL: {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
input.dataType(), maxPooling2dCudaLauncher,
|
||||
(*block.launchContext(), input.specialBuffer(), input.specialShapeInfo(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), kH, kW, sH, sW, pH, pW, dH, dW, extraParam0, output.rankOf(), output.lengthOf()),
|
||||
SD_NUMERIC_TYPES);
|
||||
|
||||
} break;
|
||||
case AVG_POOL: {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
input.dataType(), avgPooling2dCudaLauncher,
|
||||
(*block.launchContext(), input.specialBuffer(), input.specialShapeInfo(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), kH, kW, sH, sW, pH, pW, dH, dW, extraParam0),
|
||||
SD_NUMERIC_TYPES);
|
||||
} break;
|
||||
case PNORM_POOL: {
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
input.dataType(), pnormPooling2dCudaLauncher,
|
||||
(*block.launchContext(), input.specialBuffer(), input.specialShapeInfo(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), kH, kW, sH, sW, pH, pW, dH, dW, extraParam0),
|
||||
SD_FLOAT_TYPES);
|
||||
} break;
|
||||
default:
|
||||
THROW_EXCEPTION("Pooling2D: Unknown PoolingType used");
|
||||
}
|
||||
|
||||
output.tickWriteDevice();
|
||||
input.tickReadDevice();
|
||||
|
||||
auto result = cudaStreamSynchronize(*block.launchContext()->getCudaStream());
|
||||
if (result != 0) throw cuda_exception::build("Pooling2D failed", result);
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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/cuda/LaunchDims.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <math/templatemath.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void pooling2dBPCuda(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
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) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
const T* y = reinterpret_cast<const T*>(vy);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
LongType coord2, coord3;
|
||||
__shared__ LongType rank, kHeff, kWeff, iH, iW, kProd;
|
||||
__shared__ LongType xLen, yLen, *sharedMem;
|
||||
__shared__ LongType* xShape;
|
||||
__shared__ LongType* yShape;
|
||||
__shared__ LongType* zShape;
|
||||
__shared__ LongType* xStride;
|
||||
__shared__ LongType* yStride;
|
||||
__shared__ LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
yLen = shape::length(yShapeInfo);
|
||||
xLen = shape::length(xShapeInfo);
|
||||
rank = 4;
|
||||
|
||||
kHeff = kH + (kH - 1) * (dH - 1);
|
||||
kWeff = kW + (kW - 1) * (dW - 1);
|
||||
|
||||
iH = xShapeInfo[3];
|
||||
iW = xShapeInfo[4];
|
||||
|
||||
kProd = kH * kW;
|
||||
|
||||
// Cache shape information
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto yInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (yInd >= yLen) return;
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
INDEX2COORDS(yInd, rank, yShape, coords);
|
||||
|
||||
LongType yOffset;
|
||||
COORDS2INDEX(rank, yStride, coords, yOffset);
|
||||
|
||||
LongType hstart = coords[2] * sH - pH;
|
||||
LongType wstart = coords[3] * sW - pW;
|
||||
LongType hend = hstart + kHeff;
|
||||
LongType 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);
|
||||
|
||||
switch (poolingMode) {
|
||||
/*** max ***/
|
||||
case 0: {
|
||||
coord2 = hstart;
|
||||
coord3 = wstart;
|
||||
bool out_of_range = false;
|
||||
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
for (coords[2] = hstart; coords[2] < hend; coords[2] += dH) {
|
||||
for (coords[3] = wstart; coords[3] < wend; coords[3] += dW) {
|
||||
LongType offset;
|
||||
COORDS2INDEX(rank, xStride, coords, offset);
|
||||
T val = x[offset];
|
||||
if (val > max) {
|
||||
max = val;
|
||||
coord2 = coords[2];
|
||||
coord3 = coords[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
coords[2] = coord2;
|
||||
coords[3] = coord3;
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
math::atomics::sd_atomicAdd<T>(&z[zOffset], y[yOffset]);
|
||||
} break;
|
||||
|
||||
/*** avg ***/
|
||||
case 1: {
|
||||
T val = y[yOffset];
|
||||
|
||||
if (extraParam0 == 0) // Exclude padding
|
||||
val /= static_cast<T>(math::sd_ceil<double, int>(static_cast<double>(hend - hstart) / static_cast<double>(dH)) *
|
||||
math::sd_ceil<double, int>(static_cast<double>(wend - wstart) / static_cast<double>(dW)));
|
||||
else if (extraParam0 == 1) // Include padding
|
||||
val /= static_cast<T>(kProd);
|
||||
|
||||
for (coords[2] = hstart; coords[2] < hend; coords[2] += dH)
|
||||
for (coords[3] = wstart; coords[3] < wend; coords[3] += dW) {
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
math::atomics::sd_atomicAdd<T>(&z[zOffset], val);
|
||||
}
|
||||
} break;
|
||||
|
||||
/*** pnorm ***/
|
||||
case 2: {
|
||||
T sum = static_cast<T>(0.);
|
||||
T val = y[yOffset];
|
||||
|
||||
for (coords[2] = hstart; coords[2] < hend; coords[2] += dH)
|
||||
for (coords[3] = wstart; coords[3] < wend; coords[3] += dW) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
sum += math::sd_pow<T, T, T>(math::sd_abs<T,T>(x[xOffset]), static_cast<T>(extraParam0));
|
||||
}
|
||||
|
||||
val *= math::sd_pow<T, T, T>(sum, (static_cast<T>(1.0f) - static_cast<T>(extraParam0)) / static_cast<T>(extraParam0));
|
||||
|
||||
for (coords[2] = hstart; coords[2] < hend; coords[2] += dH) {
|
||||
for (coords[3] = wstart; coords[3] < wend; coords[3] += dW) {
|
||||
LongType xOffset, zOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
math::atomics::sd_atomicAdd<T>(
|
||||
&z[zOffset], val * math::sd_pow<T, T, T>(math::sd_abs<T,T>(x[xOffset]), static_cast<T>(extraParam0) - static_cast<T>(1.0f)) *
|
||||
math::sd_sgn<T, T>(x[xOffset]));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void pooling2dBPCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, 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) {
|
||||
pooling2dBPCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(
|
||||
vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo, kH, kW, sH, sW, pH, pW, dH, dW, poolingMode, extraParam0);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"pooling2dBPCudaLauncher failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::pooling2dBP(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) {
|
||||
// initial zeroing of gradI
|
||||
gradI.nullify();
|
||||
|
||||
PointersManager manager(block.launchContext(), "pooling2dBP");
|
||||
|
||||
auto inputBuff = input.specialBuffer();
|
||||
dim3 poolingDims = getPoolingDims(gradO.lengthOf(),gradO.rankOf());
|
||||
|
||||
NDArray::prepareSpecialUse({&gradI}, {&input, &gradO});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input.dataType(), pooling2dBPCudaLauncher,
|
||||
(poolingDims.x, poolingDims.y, poolingDims.z, block.launchContext()->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), gradO.specialBuffer(), gradO.specialShapeInfo(), gradI.specialBuffer(),
|
||||
gradI.specialShapeInfo(), kH, kW, sH, sW, pH, pW, dH, dW, poolingMode, extraParam0),
|
||||
SD_NUMERIC_TYPES);
|
||||
NDArray::registerSpecialUse({&gradI}, {&input, &gradO});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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/cuda/LaunchDims.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <math/templatemath.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void pooling3dCuda(const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const int kD, const int kH, const int kW,
|
||||
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 poolingMode,
|
||||
const int extraParam0) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int rank, kDeff, kHeff, kWeff, iD, iH, iW, kProd;
|
||||
__shared__ LongType zLen, *sharedMem;
|
||||
__shared__ LongType* xShape;
|
||||
__shared__ LongType* zShape;
|
||||
__shared__ LongType* xStride;
|
||||
__shared__ LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
zLen = shape::length(zShapeInfo);
|
||||
rank = 5;
|
||||
|
||||
kDeff = kD + (kD - 1) * (dD - 1);
|
||||
kHeff = kH + (kH - 1) * (dH - 1);
|
||||
kWeff = kW + (kW - 1) * (dW - 1);
|
||||
|
||||
iD = xShapeInfo[3];
|
||||
iH = xShapeInfo[4];
|
||||
iW = xShapeInfo[5];
|
||||
|
||||
kProd = kD * kH * kW;
|
||||
|
||||
// Cache shape information
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto zInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (zInd >= zLen) return;
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
INDEX2COORDS(zInd, rank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
|
||||
int dstart = coords[2] * sD - pD;
|
||||
int hstart = coords[3] * sH - pH;
|
||||
int wstart = coords[4] * sW - pW;
|
||||
int dend = dstart + kDeff;
|
||||
int hend = hstart + kHeff;
|
||||
int 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);
|
||||
|
||||
switch (poolingMode) {
|
||||
/*** max ***/
|
||||
case 0: {
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
for (coords[2] = dstart; coords[2] < dend; coords[2] += dD) {
|
||||
for (coords[3] = hstart; coords[3] < hend; coords[3] += dH) {
|
||||
for (coords[4] = wstart; coords[4] < wend; coords[4] += dW) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
T val = x[xOffset];
|
||||
if (val > max) max = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
z[zOffset] = max;
|
||||
} break;
|
||||
|
||||
/*** avg ***/
|
||||
case 1: {
|
||||
T sum = static_cast<T>(0.);
|
||||
for (coords[2] = dstart; coords[2] < dend; coords[2] += dD)
|
||||
for (coords[3] = hstart; coords[3] < hend; coords[3] += dH)
|
||||
for (coords[4] = wstart; coords[4] < wend; coords[4] += dW) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
sum += x[xOffset];
|
||||
}
|
||||
|
||||
if (extraParam0 == 0) { // Exclude padding
|
||||
LongType a = (dend - dstart) / dD + ((dend - dstart) % dD == 0 ? 0 : 1);
|
||||
LongType b = (hend - hstart) / dH + ((hend - hstart) % dH == 0 ? 0 : 1);
|
||||
LongType c = (wend - wstart) / dW + ((wend - wstart) % dW == 0 ? 0 : 1);
|
||||
sum /= static_cast<T>(a * b * c); // Accounts for dilation
|
||||
} else if (extraParam0 == 1) // Include padding
|
||||
sum /= kProd;
|
||||
|
||||
z[zOffset] = sum;
|
||||
} break;
|
||||
|
||||
/*** pnorm ***/
|
||||
case 2: {
|
||||
T sum = static_cast<T>(0.);
|
||||
for (coords[2] = dstart; coords[2] < dend; coords[2] += dD)
|
||||
for (coords[3] = hstart; coords[3] < hend; coords[3] += dH)
|
||||
for (coords[4] = wstart; coords[4] < wend; coords[4] += dW) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
sum += math::sd_pow<T, T, T>(math::sd_abs<T, T>(x[xOffset]), static_cast<T>(extraParam0));
|
||||
}
|
||||
|
||||
sum = math::sd_pow<T, T, T>(sum, static_cast<T>(1.0f) / static_cast<T>(extraParam0));
|
||||
|
||||
z[zOffset] = sum;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void pooling3dCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const int kD, const int kH, const int kW,
|
||||
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 poolingMode,
|
||||
const int extraParam0) {
|
||||
pooling3dCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(
|
||||
vx, xShapeInfo, vz, zShapeInfo, kD, kH, kW, sD, sH, sW, pD, pH, pW, dD, dH, dW, poolingMode, extraParam0);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"pooling3dBPCudaLauncher failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::pooling3d(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) {
|
||||
PointersManager manager(block.launchContext(), "pooling3d");
|
||||
|
||||
dim3 poolingDims = getPoolingDims(output.lengthOf(),output.rankOf());
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input.dataType(), pooling3dCudaLauncher,
|
||||
(poolingDims.x, poolingDims.y, poolingDims.z, block.launchContext()->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), kD, kH, kW, sD, sH, sW, pD, pH, pW,
|
||||
dD, dH, dW, poolingMode, extraParam0),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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/cuda/LaunchDims.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <math/templatemath.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void pooling3dBPCuda(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const int kD, const int kH, const int kW, 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 poolingMode, const int extraParam0) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
const T* y = reinterpret_cast<const T*>(vy);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
LongType coord2, coord3, coord4;
|
||||
__shared__ int rank, kDeff, kHeff, kWeff, iD, iH, iW, kProd;
|
||||
__shared__ LongType yLen, *sharedMem;
|
||||
__shared__ LongType* xShape;
|
||||
__shared__ LongType* yShape;
|
||||
__shared__ LongType* zShape;
|
||||
__shared__ LongType* xStride;
|
||||
__shared__ LongType* yStride;
|
||||
__shared__ LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
yLen = shape::length(yShapeInfo);
|
||||
rank = 5;
|
||||
|
||||
kDeff = kD + (kD - 1) * (dD - 1);
|
||||
kHeff = kH + (kH - 1) * (dH - 1);
|
||||
kWeff = kW + (kW - 1) * (dW - 1);
|
||||
|
||||
iD = xShapeInfo[3];
|
||||
iH = xShapeInfo[4];
|
||||
iW = xShapeInfo[5];
|
||||
|
||||
kProd = kD * kH * kW;
|
||||
|
||||
// Cache shape information
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto yInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (yInd >= yLen) return;
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
INDEX2COORDS(yInd, rank, yShape, coords);
|
||||
|
||||
LongType yOffset;
|
||||
COORDS2INDEX(rank, yStride, coords, yOffset);
|
||||
|
||||
int dstart = coords[2] * sD - pD;
|
||||
int hstart = coords[3] * sH - pH;
|
||||
int wstart = coords[4] * sW - pW;
|
||||
int dend = dstart + kDeff;
|
||||
int hend = hstart + kHeff;
|
||||
int 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);
|
||||
|
||||
switch (poolingMode) {
|
||||
/*** max ***/
|
||||
case 0: {
|
||||
T max = -DataTypeUtils::max<T>();
|
||||
for (coords[2] = dstart; coords[2] < dend; coords[2] += dD) {
|
||||
for (coords[3] = hstart; coords[3] < hend; coords[3] += dH) {
|
||||
for (coords[4] = wstart; coords[4] < wend; coords[4] += dW) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
T val = x[xOffset];
|
||||
if (val > max) {
|
||||
max = val;
|
||||
coord2 = coords[2];
|
||||
coord3 = coords[3];
|
||||
coord4 = coords[4];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
coords[2] = coord2;
|
||||
coords[3] = coord3;
|
||||
coords[4] = coord4;
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
math::atomics::sd_atomicAdd<T>(&z[zOffset], y[yOffset]);
|
||||
} break;
|
||||
|
||||
/*** avg ***/
|
||||
case 1: {
|
||||
T val = y[yOffset];
|
||||
|
||||
if (extraParam0 == 0) // Exclude padding
|
||||
val /= static_cast<T>(math::sd_ceil<double, int>(static_cast<double>(dend - dstart) / static_cast<double>(dD)) *
|
||||
math::sd_ceil<double, int>(static_cast<double>(hend - hstart) / static_cast<double>(dH)) *
|
||||
math::sd_ceil<double, int>(static_cast<double>(wend - wstart) / static_cast<double>(dW)));
|
||||
else if (extraParam0 == 1) // Include padding
|
||||
val /= static_cast<T>(kProd);
|
||||
|
||||
for (coords[2] = dstart; coords[2] < dend; coords[2] += dD)
|
||||
for (coords[3] = hstart; coords[3] < hend; coords[3] += dH)
|
||||
for (coords[4] = wstart; coords[4] < wend; coords[4] += dW) {
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
math::atomics::sd_atomicAdd<T>(&z[zOffset], val);
|
||||
}
|
||||
} break;
|
||||
|
||||
/*** pnorm ***/
|
||||
case 2: {
|
||||
T sum = static_cast<T>(0.);
|
||||
T val = y[yOffset];
|
||||
|
||||
for (coords[2] = dstart; coords[2] < dend; coords[2] += dD)
|
||||
for (coords[3] = hstart; coords[3] < hend; coords[3] += dH)
|
||||
for (coords[4] = wstart; coords[4] < wend; coords[4] += dW) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
sum += math::sd_pow<T, T, T>(math::sd_abs<T,T>(x[xOffset]), static_cast<T>(extraParam0));
|
||||
}
|
||||
|
||||
val *= math::sd_pow<T, T, T>(sum, (static_cast<T>(1.0f) - static_cast<T>(extraParam0)) / static_cast<T>(extraParam0));
|
||||
|
||||
for (coords[2] = dstart; coords[2] < dend; coords[2] += dD)
|
||||
for (coords[3] = hstart; coords[3] < hend; coords[3] += dH)
|
||||
for (coords[4] = wstart; coords[4] < wend; coords[4] += dW) {
|
||||
LongType xOffset, zOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
math::atomics::sd_atomicAdd<T>(
|
||||
&z[zOffset], val * math::sd_pow<T, T, T>(math::sd_abs<T,T>(x[xOffset]), static_cast<T>(extraParam0) - static_cast<T>(1.0f)) *
|
||||
math::sd_sgn<T, T>(x[xOffset]));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void pooling3dBPCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const int kD, const int kH, const int kW,
|
||||
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 poolingMode,
|
||||
const int extraParam0) {
|
||||
pooling3dBPCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz,
|
||||
zShapeInfo, kD, kH, kW, sD, sH, sW, pD, pH,
|
||||
pW, dD, dH, dW, poolingMode, extraParam0);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"pooling3dBPCudaLauncher failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::pooling3dBP(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) {
|
||||
// initial zeroing of gradI
|
||||
gradI.nullify();
|
||||
|
||||
PointersManager manager(block.launchContext(), "pooling3dBP");
|
||||
dim3 poolingDims = getPoolingDims(gradO.lengthOf(),gradO.rankOf());
|
||||
NDArray::prepareSpecialUse({&gradI}, {&input, &gradO});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input.dataType(), pooling3dBPCudaLauncher,
|
||||
(poolingDims.y, poolingDims.x, poolingDims.z, block.launchContext()->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), gradO.specialBuffer(), gradO.specialShapeInfo(), gradI.specialBuffer(),
|
||||
gradI.specialShapeInfo(), kD, kH, kW, sD, sH, sW, pD, pH, pW, dD, dH, dW, poolingMode, extraParam0),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&gradI}, {&input, &gradO});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace s
|
||||
@@ -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)
|
||||
//
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
|
||||
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> shape1 = {bS, oH, oW, iC * mC};
|
||||
std::vector<sd::LongType> shape2 = {bS, iC * mC, oH, oW};
|
||||
outputDepth = new NDArray(output->ordering(), !isNCHW ? shape1 : shape2, 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
|
||||
@@ -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
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
//
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void upsampling2dCuda(const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const LongType factorH, const LongType factorW,
|
||||
const bool isNCHW) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType rank, dimIH;
|
||||
__shared__ LongType zLen, *sharedMem;
|
||||
__shared__ LongType* xShape;
|
||||
__shared__ LongType* zShape;
|
||||
__shared__ LongType* xStride;
|
||||
__shared__ LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
dimIH = isNCHW ? 2 : 1;
|
||||
zLen = shape::length(zShapeInfo);
|
||||
rank = 4;
|
||||
|
||||
// Cache shape information
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto zInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (zInd >= zLen) return;
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
INDEX2COORDS(zInd, rank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
|
||||
coords[dimIH] /= factorH;
|
||||
coords[dimIH + 1] /= factorW;
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void upsampling2dCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const LongType factorH, const LongType factorW,
|
||||
const bool isNCHW) {
|
||||
upsampling2dCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, factorH,
|
||||
factorW, isNCHW);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"upsampling2dCudaLauncher failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::upsampling2d(graph::Context& block, NDArray& input, NDArray& output, const LongType factorH,
|
||||
const LongType factorW, const bool isNCHW) {
|
||||
PointersManager manager(block.launchContext(), "upsampling2d");
|
||||
|
||||
dim3 getUpSampling = getUpsamplingDims(output.lengthOf(),output.rankOf());
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input.dataType(), upsampling2dCudaLauncher,
|
||||
(getUpSampling.x, getUpSampling.y, getUpSampling.z, block.launchContext()->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), factorH, factorW, isNCHW),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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)
|
||||
//
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void upsampling2dBPCuda(const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const bool isNCHW) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType rank, dimIH;
|
||||
__shared__ LongType factorH, factorW;
|
||||
__shared__ LongType zLen, *sharedMem;
|
||||
__shared__ LongType* xShape;
|
||||
__shared__ LongType* zShape;
|
||||
__shared__ LongType* xStride;
|
||||
__shared__ LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
dimIH = isNCHW ? 2 : 1;
|
||||
zLen = shape::length(zShapeInfo);
|
||||
rank = 4;
|
||||
|
||||
factorH = xShapeInfo[dimIH + 1] / zShapeInfo[dimIH + 1];
|
||||
factorW = xShapeInfo[dimIH + 2] / zShapeInfo[dimIH + 2];
|
||||
|
||||
// Cache shape information
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto zInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (zInd >= zLen) return;
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
INDEX2COORDS(zInd, rank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
|
||||
z[zOffset] = 0;
|
||||
|
||||
const LongType zCoord2 = coords[dimIH] * factorH;
|
||||
const LongType zCoord3 = coords[dimIH + 1] * factorW;
|
||||
|
||||
for (coords[dimIH] = zCoord2; coords[dimIH] < zCoord2 + factorH; ++coords[dimIH])
|
||||
for (coords[dimIH + 1] = zCoord3; coords[dimIH + 1] < zCoord3 + factorW; ++coords[dimIH + 1]) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
z[zOffset] += x[xOffset];
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void upsampling2dBPCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const bool isNCHW) {
|
||||
upsampling2dBPCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, isNCHW);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"upsampling2dBPCuda failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::upsampling2dBP(graph::Context& block, NDArray& gradO, NDArray& gradI,
|
||||
const bool isNCHW) {
|
||||
PointersManager manager(block.launchContext(), "upsampling2d_bp");
|
||||
|
||||
dim3 getUpSampling = getUpsamplingDims(gradI.lengthOf(),gradI.rankOf());
|
||||
|
||||
NDArray::prepareSpecialUse({&gradI}, {&gradO});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
gradI.dataType(), upsampling2dBPCudaLauncher,
|
||||
(getUpSampling.x, getUpSampling.y, getUpSampling.z, block.launchContext()->getCudaStream(), gradO.specialBuffer(),
|
||||
gradO.specialShapeInfo(), gradI.specialBuffer(), gradI.specialShapeInfo(), isNCHW),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&gradI}, {&gradO});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void upsampling3dCuda(const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const int factorD, const int factorH,
|
||||
const int factorW, const bool isNCDHW) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int rank, dimID;
|
||||
__shared__ LongType zLen, *sharedMem;
|
||||
__shared__ LongType* xShape;
|
||||
__shared__ LongType* zShape;
|
||||
__shared__ LongType* xStride;
|
||||
__shared__ LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
dimID = isNCDHW ? 2 : 1;
|
||||
zLen = shape::length(zShapeInfo);
|
||||
rank = 5;
|
||||
|
||||
// Cache shape information
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto zInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (zInd >= zLen) return;
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
INDEX2COORDS(zInd, rank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
|
||||
coords[dimID] /= factorD;
|
||||
coords[dimID + 1] /= factorH;
|
||||
coords[dimID + 2] /= factorW;
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void upsampling3dCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const int factorD, const int factorH,
|
||||
const int factorW, const bool isNCDHW) {
|
||||
upsampling3dCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, factorD,
|
||||
factorH, factorW, isNCDHW);
|
||||
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"upsampling3dCudaLauncher failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::upsampling3d(graph::Context& block, NDArray& input, NDArray& output, const LongType factorD,
|
||||
const LongType factorH, const LongType factorW, const bool isNCDHW) {
|
||||
PointersManager manager(block.launchContext(), "upsampling3d");
|
||||
|
||||
|
||||
dim3 getUpSampling = getUpsamplingDims(output.lengthOf(),output.rankOf());
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input.dataType(), upsampling3dCudaLauncher,
|
||||
(getUpSampling.x, getUpSampling.y, getUpSampling.z, block.launchContext()->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), factorD, factorH, factorW, isNCDHW),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void upsampling3dBPCuda(const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const bool isNCDHW) {
|
||||
// x (gradO) has shape [bS, iC, iD, iH, iW] (NCDHW) or [bS, iD, iH, iW, iC] (NDHWC)
|
||||
// z (gradI) has shape [bS, iC, factorD*iD, factorH*iH, factorW*iW ] (NCDHW) or [bS, factorD*iD, factorH*iH,
|
||||
// factorW*iW, iC] (NDHWC)
|
||||
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int rank, dimID;
|
||||
__shared__ LongType factorD, factorH, factorW;
|
||||
__shared__ LongType zLen;
|
||||
__shared__ LongType *sharedMem;
|
||||
__shared__ LongType *xShape, *zShape;
|
||||
__shared__ LongType *xStride, *zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
// Cache shape and stride pointers
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
|
||||
dimID = isNCDHW ? 2 : 1;
|
||||
zLen = shape::length(zShapeInfo);
|
||||
rank = 5;
|
||||
|
||||
factorD = xShape[dimID + 1] / zShape[dimID + 1];
|
||||
factorH = xShape[dimID + 2] / zShape[dimID + 2];
|
||||
factorW = xShape[dimID + 3] / zShape[dimID + 3];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto zInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (zInd >= zLen) return;
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
INDEX2COORDS(zInd, rank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
|
||||
z[zOffset] = 0;
|
||||
|
||||
const LongType zCoord2 = coords[dimID] * factorD;
|
||||
const LongType zCoord3 = coords[dimID + 1] * factorH;
|
||||
const LongType zCoord4 = coords[dimID + 2] * factorW;
|
||||
|
||||
for (coords[dimID] = zCoord2; coords[dimID] < zCoord2 + factorD; ++coords[dimID])
|
||||
for (coords[dimID + 1] = zCoord3; coords[dimID + 1] < zCoord3 + factorH; ++coords[dimID + 1])
|
||||
for (coords[dimID + 2] = zCoord4; coords[dimID + 2] < zCoord4 + factorW; ++coords[dimID + 2]) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
z[zOffset] += x[xOffset];
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void upsampling3dBPCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const bool isNCDHW) {
|
||||
upsampling3dBPCuda<T>
|
||||
<<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, isNCDHW);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"upsampling3dBPCudaLauncher failed");
|
||||
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void ConvolutionUtils::upsampling3dBP(graph::Context& block, NDArray& gradO, NDArray& gradI,
|
||||
const bool isNCDHW) {
|
||||
PointersManager manager(block.launchContext(), "upsampling3d_bp");
|
||||
|
||||
dim3 getUpSampling = getUpsamplingDims(gradI.lengthOf(),gradI.rankOf());
|
||||
|
||||
NDArray::prepareSpecialUse({&gradI}, {&gradO});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
gradI.dataType(), upsampling3dBPCudaLauncher,
|
||||
(getUpSampling.x, getUpSampling.y, getUpSampling.z, block.launchContext()->getCudaStream(), gradO.specialBuffer(),
|
||||
gradO.specialShapeInfo(), gradI.specialBuffer(), gradI.specialShapeInfo(), isNCDHW),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&gradI}, {&gradO});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// vol [bS, iC, iD, iH, iW] is convoluted to col [bS, iC, kD, kH, kW, oD, oH, oW]
|
||||
template <typename T>
|
||||
static SD_KERNEL void vol2colCuda(const void* volume, const LongType* volShapeInfo, void* columns,
|
||||
const LongType* colShapeInfo, 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 T* vol = reinterpret_cast<const T*>(volume);
|
||||
T* col = reinterpret_cast<T*>(columns);
|
||||
|
||||
__shared__ LongType colRank, volRank, colLen;
|
||||
__shared__ LongType iD, iH, iW;
|
||||
__shared__ const LongType *volShape, *volStride, *colShape, *colStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
volRank = 5;
|
||||
colRank = 8;
|
||||
|
||||
volShape = shape::shapeOf(volShapeInfo);
|
||||
volStride = shape::stride(volShapeInfo);
|
||||
|
||||
colShape = shape::shapeOf(colShapeInfo);
|
||||
colStride = shape::stride(colShapeInfo);
|
||||
|
||||
colLen = shape::length(colShapeInfo);
|
||||
|
||||
iD = volShape[2];
|
||||
iH = volShape[3];
|
||||
iW = volShape[4];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto colInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (colInd >= colLen) return;
|
||||
|
||||
extern __shared__ LongType sharedMem[];
|
||||
auto coords = sharedMem + threadIdx.x * colRank;
|
||||
|
||||
INDEX2COORDS(colInd, colRank, colShape, coords);
|
||||
|
||||
LongType colOffset;
|
||||
COORDS2INDEX(colRank, colStride, coords, colOffset);
|
||||
|
||||
// Compute volumetric indices
|
||||
const auto volDep = -pD + coords[2] * dD + coords[5] * sD;
|
||||
const auto volRow = -pH + coords[3] * dH + coords[6] * sH;
|
||||
const auto volCol = -pW + coords[4] * dW + coords[7] * sW;
|
||||
|
||||
if (volDep < 0 || volDep >= iD || volRow < 0 || volRow >= iH || volCol < 0 || volCol >= iW) {
|
||||
col[colOffset] = static_cast<T>(0.);
|
||||
} else {
|
||||
coords[2] = volDep;
|
||||
coords[3] = volRow;
|
||||
coords[4] = volCol;
|
||||
|
||||
LongType volOffset;
|
||||
COORDS2INDEX(volRank, volStride, coords, volOffset);
|
||||
col[colOffset] = vol[volOffset];
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void vol2colCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* volume, const LongType* volShapeInfo,
|
||||
void* columns, const LongType* colShapeInfo, const int 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) {
|
||||
vol2colCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(volume, volShapeInfo, columns, colShapeInfo,
|
||||
sD, sH, sW, pD, pH, pW, dD, dH, dW);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"vol2colCudaLauncher failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
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) {
|
||||
PointersManager manager(block.launchContext(), "vol2col");
|
||||
|
||||
dim3 vol2ColDims = getVol2ColDims(col->lengthOf(),col->rankOf());
|
||||
|
||||
NDArray::prepareSpecialUse({col}, {vol});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
vol->dataType(), vol2colCudaLauncher,
|
||||
(vol2ColDims.x, vol2ColDims.y, vol2ColDims.z, block.launchContext()->getCudaStream(), vol->specialBuffer(),
|
||||
vol->specialShapeInfo(), col->specialBuffer(), col->specialShapeInfo(), sD, sH, sW, pD, pH, pW, dD, dH, dW),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({col}, {vol});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,135 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 10.06.2019
|
||||
//
|
||||
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/cross.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void crossCuda(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo) {
|
||||
__shared__ const T* x;
|
||||
__shared__ const T* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType rank;
|
||||
__shared__ LongType lenWithoutLastDim, totalThreads;
|
||||
__shared__ const LongType *xShape, *xStride, *yShape, *yStride, *zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<const T*>(vx);
|
||||
y = reinterpret_cast<const T*>(vy);
|
||||
z = reinterpret_cast<T*>(vz);
|
||||
|
||||
rank = shape::rank(xShapeInfo);
|
||||
lenWithoutLastDim = shape::length(xShapeInfo) / xShapeInfo[rank];
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
extern __shared__ LongType sharedMem[];
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < lenWithoutLastDim; i += totalThreads) {
|
||||
// Compute coordinates without the last dimension
|
||||
INDEX2COORDS(i, rank - 1, xShape, coords);
|
||||
coords[rank - 1] = 0;
|
||||
|
||||
LongType xOffset, yOffset, zOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
COORDS2INDEX(rank, yStride, coords, yOffset);
|
||||
|
||||
// Fetch elements for cross product
|
||||
const auto x0 = x[xOffset];
|
||||
const auto y0 = y[yOffset];
|
||||
|
||||
xOffset += xStride[rank - 1];
|
||||
yOffset += yStride[rank - 1];
|
||||
|
||||
const auto x1 = x[xOffset];
|
||||
const auto y1 = y[yOffset];
|
||||
|
||||
xOffset += xStride[rank - 1];
|
||||
yOffset += yStride[rank - 1];
|
||||
|
||||
const auto x2 = x[xOffset];
|
||||
const auto y2 = y[yOffset];
|
||||
|
||||
// Compute offsets for output
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
z[zOffset] = x1 * y2 - x2 * y1;
|
||||
|
||||
zOffset += zStride[rank - 1];
|
||||
z[zOffset] = x2 * y0 - x0 * y2;
|
||||
|
||||
zOffset += zStride[rank - 1];
|
||||
z[zOffset] = x0 * y1 - x1 * y0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
SD_HOST static void crossCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo) {
|
||||
crossCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t*>(stream),"crossCuda failed");
|
||||
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void crossCudaLauncher,
|
||||
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const sd::LongType* xShapeInfo, const void* vy,
|
||||
const sd::LongType* yShapeInfo, void* vz, const sd::LongType* zShapeInfo),
|
||||
SD_NUMERIC_TYPES);
|
||||
|
||||
void crossBatched(LaunchContext* context, NDArray* x, NDArray* y, NDArray* z) {
|
||||
dim3 launchDims = getCross(x->lengthOf(),x->rankOf(),x->sizeAt(-1));
|
||||
PointersManager manager(context, "cross");
|
||||
|
||||
NDArray::prepareSpecialUse({z}, {x, y});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
x->dataType(), crossCudaLauncher,
|
||||
(launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), x->specialBuffer(), x->specialShapeInfo(),
|
||||
y->specialBuffer(), y->specialShapeInfo(), z->specialBuffer(), z->specialShapeInfo()),
|
||||
SD_NUMERIC_TYPES);
|
||||
NDArray::registerSpecialUse({z}, {x, y});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,43 @@
|
||||
/*******************************************************************************
|
||||
* 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>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
void ctcLoss(graph::Context &block, NDArray&logitsInput, NDArray&targetLabels,
|
||||
NDArray&logitsLengths, NDArray&targetLabelLengths, NDArray &logLosses,
|
||||
NDArray &gradients, int blankIndex) {
|
||||
// not imeplemented
|
||||
THROW_EXCEPTION("ctcLoss:: Not implemented yet");
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,116 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <ops/declarable/helpers/d_t_s.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void depthToSpaceKernel(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, const int block_size, const bool isNHWC) {
|
||||
auto input_ptr = reinterpret_cast<const T *>(vx);
|
||||
auto output_ptr = reinterpret_cast<T *>(vz);
|
||||
|
||||
const int batch_size = shape::sizeAt(xShapeInfo, 0);
|
||||
const int input_depth = isNHWC ? shape::sizeAt(xShapeInfo, 3) : shape::sizeAt(xShapeInfo, 1);
|
||||
const int input_height = isNHWC ? shape::sizeAt(xShapeInfo, 1) : shape::sizeAt(xShapeInfo, 2);
|
||||
const int input_width = isNHWC ? shape::sizeAt(xShapeInfo, 2) : shape::sizeAt(xShapeInfo, 3);
|
||||
|
||||
const int output_depth = isNHWC ? shape::sizeAt(zShapeInfo, 3) : shape::sizeAt(zShapeInfo, 1);
|
||||
const int output_height = isNHWC ? shape::sizeAt(zShapeInfo, 1) : shape::sizeAt(zShapeInfo, 2);
|
||||
const int output_width = isNHWC ? shape::sizeAt(zShapeInfo, 2) : shape::sizeAt(zShapeInfo, 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;
|
||||
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (isNHWC) {
|
||||
const int total_count = batch_size * output_height * output_width * output_depth;
|
||||
for (int out_idx = tid; out_idx < total_count; out_idx += blockDim.x * gridDim.x) {
|
||||
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];
|
||||
}
|
||||
} else {
|
||||
const int total_count = batch_size * input_depth_by_input_area;
|
||||
|
||||
for (int input_idx = tid; input_idx < total_count; input_idx += blockDim.x * gridDim.x) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void __depthToSpace(LaunchContext *context, NDArray&input, NDArray *output, int block_size,
|
||||
bool isNHWC) {
|
||||
dim3 launchDims = getLaunchDims("depth_to_space");
|
||||
depthToSpaceKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *context->getCudaStream()>>>(input.specialBuffer(), input.specialShapeInfo(),
|
||||
output->specialBuffer(),
|
||||
output->specialShapeInfo(), block_size, isNHWC);
|
||||
DebugHelper::checkGlobalErrorCode("depthToSpaceKernel failed");
|
||||
|
||||
}
|
||||
|
||||
void _depthToSpace(LaunchContext *context, NDArray&input, NDArray *output, int block_size, bool isNHWC) {
|
||||
auto xType = input.dataType();
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {&input});
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, __depthToSpace, (context, input, output, block_size, isNHWC), SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {&input});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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/NDArrayFactory.h>
|
||||
#include <ops/declarable/helpers/gammaMathFunc.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void diGammaCuda(const void *vx, const LongType *xShapeInfo, void *vz, const LongType *zShapeInfo) {
|
||||
const auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
__shared__ LongType len;
|
||||
__shared__ bool sameOffset;
|
||||
__shared__ LongType xRank, zRank;
|
||||
__shared__ const LongType *xShape, *xStride, *zShape, *zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
len = shape::length(xShapeInfo);
|
||||
sameOffset = shape::haveSameShapeAndStrides(xShapeInfo, zShapeInfo);
|
||||
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
LongType zOffset;
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < len; i += gridDim.x * blockDim.x) {
|
||||
INDEX2COORDS(i, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
|
||||
if (sameOffset) {
|
||||
zOffset = xOffset;
|
||||
} else {
|
||||
INDEX2COORDS(i, zRank, zShape, zCoords);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
}
|
||||
|
||||
z[zOffset] = diGammaScalar<T>(x[xOffset]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void diGammaCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMemory,
|
||||
const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo) {
|
||||
diGammaCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMemory, *stream>>>(vx, xShapeInfo, vz, zShapeInfo);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "crossCuda failed");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void diGamma(LaunchContext *context, NDArray&x, NDArray &z) {
|
||||
dim3 digammaDims2 = digammaDims(z.lengthOf());
|
||||
NDArray::prepareSpecialUse({&z}, {&x});
|
||||
BUILD_SINGLE_SELECTOR(x.dataType(), diGammaCudaLauncher,
|
||||
(digammaDims2.y, digammaDims2.x, digammaDims2.z, context->getCudaStream(), x.specialBuffer(),
|
||||
x.specialShapeInfo(), z.specialBuffer(), z.specialShapeInfo()),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&z}, {&x});
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void diGammaCudaLauncher,
|
||||
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMemory,
|
||||
const cudaStream_t *stream, const void *vx, const sd::LongType *xShapeInfo, void *vz,
|
||||
const sd::LongType *zShapeInfo),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,200 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <execution/cuda/LaunchDims.h>
|
||||
#include <ops/declarable/helpers/diag.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// diag functor cuda kernel
|
||||
// outputBuffer - output tensor buffer
|
||||
// outputShape - output tensor shape
|
||||
// inputBuffer - input tensor buffer - this tensor should be placed on diagonal position of output
|
||||
// inputShape - input tensor shape
|
||||
// inputLength - length for input tensor
|
||||
//
|
||||
template <typename T>
|
||||
static SD_KERNEL void diagFunctorKernel(void* outputBuffer, const LongType* outputShape, void const* inputBuffer,
|
||||
const LongType* inputShape, LongType inputLength) {
|
||||
__shared__ T* z;
|
||||
__shared__ T const* x;
|
||||
__shared__ LongType outputRank, inputRank, outputLength;
|
||||
__shared__ const LongType *outputShapePtr, *outputStridePtr;
|
||||
__shared__ const LongType *inputShapePtr, *inputStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
z = reinterpret_cast<T*>(outputBuffer);
|
||||
x = reinterpret_cast<T const*>(inputBuffer);
|
||||
|
||||
outputRank = shape::rank(outputShape);
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputLength = shape::length(outputShape);
|
||||
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
LongType xOffset;
|
||||
|
||||
for (LongType t = tid; t < inputLength; t += step) {
|
||||
// Compute coordinates and offsets for output
|
||||
INDEX2COORDS(t * (inputLength + 1), outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zOffset);
|
||||
|
||||
// Compute coordinates and offsets for input
|
||||
INDEX2COORDS(t, inputRank, inputShapePtr, xCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xOffset);
|
||||
|
||||
// Assign the value to the diagonal position
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// diag part functor cuda kernel
|
||||
// outputBuffer - output tensor buffer - linear sequence of diagonal values
|
||||
// outputShape - output tensor shape
|
||||
// inputBuffer - input tensor buffer - this tensor should be placed on diagonal position of output
|
||||
// inputShape - input tensor shape
|
||||
// outputLength - given length of output
|
||||
// inputLength - given length for input tensor
|
||||
//
|
||||
template <typename T>
|
||||
static SD_KERNEL void diagPartFunctorKernel(void* outputBuffer, const LongType* outputShape,
|
||||
void const* inputBuffer, const LongType* inputShape, LongType outputLength, LongType inputLength) {
|
||||
__shared__ T* z;
|
||||
__shared__ T const* x;
|
||||
__shared__ LongType outputRank, inputRank;
|
||||
__shared__ const LongType *outputShapePtr, *outputStridePtr;
|
||||
__shared__ const LongType *inputShapePtr, *inputStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
z = reinterpret_cast<T*>(outputBuffer);
|
||||
x = reinterpret_cast<T const*>(inputBuffer);
|
||||
|
||||
outputRank = shape::rank(outputShape);
|
||||
inputRank = shape::rank(inputShape);
|
||||
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
LongType xOffset;
|
||||
LongType i = tid * (outputLength + 1); // position of the diagonal value in the input
|
||||
|
||||
for (LongType t = tid; t < outputLength && i < inputLength; t += step) {
|
||||
// Compute coordinates and offsets for output
|
||||
INDEX2COORDS(t, outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zOffset);
|
||||
|
||||
// Compute coordinates and offsets for input
|
||||
INDEX2COORDS(i, inputRank, inputShapePtr, xCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xOffset);
|
||||
|
||||
// Assign diagonal value
|
||||
z[zOffset] = x[xOffset];
|
||||
|
||||
// Move to the next diagonal value
|
||||
i += outputLength + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// 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(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
auto inputLength = input->isScalar() ? 1 : input->lengthOf();
|
||||
dim3 launchDims = getLaunchDims("diagPart");
|
||||
if (!input->isActualOnDeviceSide()) input->syncToDevice();
|
||||
diagFunctorKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
output->specialBuffer(), output->specialShapeInfo(), input->specialBuffer(), input->specialShapeInfo(),
|
||||
inputLength);
|
||||
DebugHelper::checkErrorCode(stream,"diagFunctorKernel failed");
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// diagFunctor - caller for diag functor processor
|
||||
void diagFunctor(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
auto xType = input->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, _diagFunctor, (context, input, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _diagFunctor, (sd::LaunchContext * context, NDArray* input, NDArray* output);
|
||||
, SD_COMMON_TYPES);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// diagPartFunctor - caller for diag part functor kernel
|
||||
template <typename T>
|
||||
void _diagPartFunctor(LaunchContext* context, NDArray * input, NDArray* output) {
|
||||
const int outLen = output->lengthOf();
|
||||
const int inLen = input->isScalar() ? 1 : input->lengthOf();
|
||||
auto stream = context->getCudaStream();
|
||||
|
||||
dim3 launchDims = getLaunchDims("diagPart");
|
||||
if (!input->isActualOnDeviceSide()) input->syncToDevice();
|
||||
|
||||
diagPartFunctorKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
output->specialBuffer(), output->specialShapeInfo(), input->specialBuffer(), input->specialShapeInfo(), outLen,
|
||||
inLen);
|
||||
DebugHelper::checkErrorCode(stream,"diagFunctorKernel failed");
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// diagPartFunctor - caller for diag part functor processor
|
||||
void diagPartFunctor(LaunchContext* context, NDArray * input, NDArray* output) {
|
||||
auto zType = output->dataType();
|
||||
BUILD_SINGLE_SELECTOR(zType, _diagPartFunctor, (context, input, output), SD_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,143 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/DataTypeUtils.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/dilation2d.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
SD_KERNEL static void dilation2dCuda(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const int sH, const int sW, const int pH, const int pW, const int dH,
|
||||
const int dW) {
|
||||
// x [bS, iH, iW, iC]
|
||||
// y [kH, kW, iC]
|
||||
// z [bS, oH, oW, iC]
|
||||
|
||||
const X* x = reinterpret_cast<const X*>(vx);
|
||||
const X* y = reinterpret_cast<const X*>(vy);
|
||||
Z* z = reinterpret_cast<Z*>(vz);
|
||||
|
||||
__shared__ LongType xRank, yRank, zRank;
|
||||
__shared__ const LongType *xShape, *xStride, *yShape, *yStride, *zShape, *zStride;
|
||||
__shared__ LongType iH, iW, kH, kW, zLen;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
|
||||
zLen = shape::length(zShapeInfo);
|
||||
|
||||
iH = xShape[1];
|
||||
iW = xShape[2];
|
||||
|
||||
kH = yShape[0];
|
||||
kW = yShape[1];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto zInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (zInd >= zLen) return;
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
|
||||
INDEX2COORDS(zInd, zRank, zShape, zCoords);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
|
||||
yCoords[2] = zCoords[3]; // iC coordinate is the same for x, y, and z
|
||||
const auto oh = zCoords[1];
|
||||
const auto ow = zCoords[2];
|
||||
|
||||
X max = -DataTypeUtils::max<X>();
|
||||
|
||||
for (yCoords[0] = 0; yCoords[0] < kH; ++yCoords[0]) {
|
||||
xCoords[1] = oh * sH - pH + yCoords[0] * dH;
|
||||
if (xCoords[1] < 0 || xCoords[1] >= iH) continue;
|
||||
|
||||
for (yCoords[1] = 0; yCoords[1] < kW; ++yCoords[1]) {
|
||||
xCoords[2] = ow * sW - pW + yCoords[1] * dW;
|
||||
if (xCoords[2] < 0 || xCoords[2] >= iW) continue;
|
||||
|
||||
LongType xOffset, yOffset;
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
|
||||
|
||||
const X val = x[xOffset] + y[yOffset];
|
||||
if (val > max) max = val;
|
||||
}
|
||||
}
|
||||
|
||||
z[zOffset] = static_cast<Z>(max);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static void dilation2dCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const LongType sH, const LongType sW, const LongType pH,
|
||||
const LongType pW, const LongType dH, const LongType dW) {
|
||||
dilation2dCuda<X, Z><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz,
|
||||
zShapeInfo, sH, sW, pH, pW, dH, dW);
|
||||
DebugHelper::checkGlobalErrorCode( "dilation2d(...) failed");
|
||||
|
||||
}
|
||||
|
||||
void dilation2d(LaunchContext* context, NDArray* input, NDArray* weights, NDArray* output, const LongType sH,
|
||||
const LongType sW, const LongType pH, const LongType pW, const LongType dH, const LongType dW) {
|
||||
PointersManager manager(context, "dilation2d");
|
||||
dim3 dilation = getDilation(output->lengthOf(),weights->rankOf(),output->rankOf());
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input, weights});
|
||||
BUILD_SINGLE_SELECTOR_TWICE(
|
||||
input->dataType(), dilation2dCudaLauncher,
|
||||
(dilation.y, dilation.x, dilation.z, context->getCudaStream(), input->specialBuffer(),
|
||||
input->specialShapeInfo(), weights->specialBuffer(), weights->specialShapeInfo(), output->specialBuffer(),
|
||||
output->specialShapeInfo(), sH, sW, pH, pW, dH, dW),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, weights});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,393 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <legacy/NativeOps.h>
|
||||
#include <ops/declarable/helpers/dropout.h>
|
||||
#include <helpers/DebugHelper.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void dropoutSimpleKernel(void const* inputBuf, LongType const* inputShape, void* outputBuf,
|
||||
LongType const* outputShape, double probVal, int inLen,
|
||||
RandomGenerator* nodeRng) {
|
||||
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
T const* input = reinterpret_cast<T const*>(inputBuf);
|
||||
T* output = reinterpret_cast<T*>(outputBuf);
|
||||
|
||||
__shared__ LongType inputRank, outputRank;
|
||||
__shared__ const LongType *inputShapePtr, *inputStridePtr;
|
||||
__shared__ const LongType *outputShapePtr, *outputStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
inputRank = shape::rank(inputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
|
||||
outputRank = shape::rank(outputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType inputOffset;
|
||||
LongType outputOffset;
|
||||
|
||||
// Loop through all elements and nullify based on probability
|
||||
for (LongType e = tid; e < inLen; e += step) {
|
||||
T val = nodeRng->relativeT(e, T(0.f), T(1.f));
|
||||
|
||||
// If probability is acceptable, save the scaled value
|
||||
if (double(val) < probVal) {
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, outputOffset);
|
||||
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, inputOffset);
|
||||
|
||||
output[outputOffset] = T(input[inputOffset] / probVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static void dropoutSimple(LaunchContext* context, NDArray * input, NDArray* output, double probValue,
|
||||
int seed) {
|
||||
RandomGenerator nodeRng(3019L, seed);
|
||||
int inLen = input->lengthOf();
|
||||
RandomGenerator* dRandom;
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
|
||||
auto err = cudaMalloc(&dRandom, sizeof(RandomGenerator));
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::dropoutSimple: Cannot allocate device memory for random generator.", err);
|
||||
}
|
||||
err = cudaMemcpy(dRandom, &nodeRng, sizeof(RandomGenerator), cudaMemcpyHostToDevice);
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::dropoutSimple: Cannot set up device memory for random generator.", err);
|
||||
}
|
||||
|
||||
dim3 getDims = getLaunchDims("dropout");
|
||||
dropoutSimpleKernel<T><<<getDims.x, getDims.y, getDims.z, *stream>>>(input->specialBuffer(), input->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), probValue,
|
||||
inLen, dRandom);
|
||||
err = cudaFree(dRandom);
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::dropoutSimple: Cannot deallocate device memory for random generator.", err);
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Status _dropOutFunctor(sd::graph::Context& context, NDArray* input, NDArray* output, NDArray* reduceShape, int seed,
|
||||
double probValue) {
|
||||
if (reduceShape == nullptr) {
|
||||
dropoutSimple<T>(context.launchContext(), input, output, probValue, seed);
|
||||
} else {
|
||||
REQUIRE_TRUE(reduceShape->lengthOf() <= input->rankOf(), 0, "dropout: Noise shape should be fittable to input");
|
||||
|
||||
std::vector<LongType> dims(reduceShape->lengthOf());
|
||||
reduceShape->syncToHost(); // to ensure that follows are actual
|
||||
bool fit = true;
|
||||
|
||||
for (int i = 0; i < dims.size(); i++) {
|
||||
if (fit) {
|
||||
dims[i] = reduceShape->e<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(), context.launchContext()));
|
||||
float one = 1.f;
|
||||
chunk->assign(one);
|
||||
|
||||
dropoutSimple<T>(context.launchContext(), chunk.get(), chunk.get(), probValue, seed);
|
||||
// broadcast chunk to full matrix
|
||||
std::unique_ptr<NDArray> dropOutMultiplier(new NDArray(*input));
|
||||
dropOutMultiplier->assign(one);
|
||||
|
||||
*dropOutMultiplier += *chunk;
|
||||
|
||||
// FIXME: we could do this in one step, aren't we?
|
||||
NDArray ret = *input * *dropOutMultiplier;
|
||||
output->assign(&ret);
|
||||
}
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
Status dropOutFunctor(sd::graph::Context& context, NDArray* input, NDArray* output, NDArray* reduceShape, int seed, double probValue, NDArray* mask) {
|
||||
auto xType = input->dataType();
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
|
||||
BUILD_SINGLE_SELECTOR(xType, return _dropOutFunctor, (context, input, output, reduceShape, seed, probValue),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
|
||||
/////////////////////////////////// backpropagations ///////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void dropoutBPKernel(void* outputBuf, LongType const* outputShape, void* gradOutBuf,
|
||||
LongType const* gradOutShape, double probValue) {
|
||||
__shared__ T* output;
|
||||
__shared__ T* input;
|
||||
__shared__ LongType len;
|
||||
__shared__ LongType outputRank, gradOutRank;
|
||||
__shared__ const LongType *outputShapePtr, *outputStridePtr;
|
||||
__shared__ const LongType *gradOutShapePtr, *gradOutStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
len = shape::length(outputShape);
|
||||
|
||||
output = reinterpret_cast<T*>(outputBuf);
|
||||
input = reinterpret_cast<T*>(gradOutBuf);
|
||||
|
||||
outputRank = shape::rank(outputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
gradOutRank = shape::rank(gradOutShape);
|
||||
gradOutShapePtr = shape::shapeOf(gradOutShape);
|
||||
gradOutStridePtr = shape::stride(gradOutShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType gradOutCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
LongType gradOutOffset;
|
||||
|
||||
for (LongType e = tid; e < len; e += step) {
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, zOffset);
|
||||
|
||||
INDEX2COORDS(e, gradOutRank, gradOutShapePtr, gradOutCoords);
|
||||
COORDS2INDEX(gradOutRank, gradOutStridePtr, gradOutCoords, gradOutOffset);
|
||||
|
||||
// Scale gradients back if the output wasn't zero
|
||||
if (output[zOffset] != T(0.)) {
|
||||
output[zOffset] = T(input[gradOutOffset] / probValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static Status dropOutFunctorBP_(sd::graph::Context& context, NDArray* input, NDArray* gradOut, NDArray* output,
|
||||
NDArray* reduceShape, int seed, double probValue, NDArray* mask) {
|
||||
// we're making additional FF run to see how probabilities played out with given seeds
|
||||
auto res = dropOutFunctor(context, input, output, reduceShape, seed, probValue,mask);
|
||||
auto stream = context.launchContext()->getCudaStream();
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input, gradOut});
|
||||
|
||||
|
||||
if (Status::OK == res) {
|
||||
dim3 launchDims = getLaunchDims("dropout");
|
||||
dropoutBPKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
output->specialBuffer(), output->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
probValue);
|
||||
|
||||
DebugHelper::checkGlobalErrorCode( "dropout_bp(...) failed");
|
||||
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, gradOut});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void alphaDropoutSimpleKernel(void const* inputBuf, LongType const* inputShape, void* outputBuf,
|
||||
LongType const* outputShape, double probValue, double alpha,
|
||||
double alpha1, double beta, int inLen, RandomGenerator* nodeRng) {
|
||||
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
T const* input = reinterpret_cast<T const*>(inputBuf);
|
||||
T* output = reinterpret_cast<T*>(outputBuf);
|
||||
|
||||
__shared__ LongType inputRank, outputRank;
|
||||
__shared__ const LongType *inputShapePtr, *inputStridePtr;
|
||||
__shared__ const LongType *outputShapePtr, *outputStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
inputRank = shape::rank(inputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
|
||||
outputRank = shape::rank(outputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType inputOffset;
|
||||
LongType outputOffset;
|
||||
|
||||
for (auto e = tid; e < inLen; e += step) {
|
||||
T val = nodeRng->relativeT(e, T(0.f), T(1.f));
|
||||
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, inputOffset);
|
||||
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, outputOffset);
|
||||
|
||||
output[outputOffset] = (val >= T(probValue)
|
||||
? T(alpha * beta + alpha1)
|
||||
: T(alpha * static_cast<double>(input[inputOffset]) + alpha1));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void alphaDropoutSimple(LaunchContext* context, NDArray * input, NDArray* output, int seed,
|
||||
double probValue, double alpha, double alpha1, double beta) {
|
||||
RandomGenerator nodeRng(3019L, seed), *dRandom;
|
||||
auto stream = context->getCudaStream();
|
||||
auto err = cudaMalloc(&dRandom, sizeof(RandomGenerator));
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::alphaDropoutSimple: Cannot allocate device memory for random generator.",
|
||||
err);
|
||||
}
|
||||
err = cudaMemcpy(dRandom, &nodeRng, sizeof(RandomGenerator), cudaMemcpyHostToDevice);
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::alphaDropoutSimple: Cannot set up device memory for random generator.", err);
|
||||
}
|
||||
|
||||
dim3 launchDims = getLaunchDims("dropout");
|
||||
alphaDropoutSimpleKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(), probValue,
|
||||
alpha, alpha1, beta, output->lengthOf(), dRandom);
|
||||
|
||||
DebugHelper::checkGlobalErrorCode( "alphaDropoutSimpleKernel(...) failed");
|
||||
|
||||
err = cudaFree(dRandom);
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::alphaDropoutSimple: Cannot deallocate device memory for random generator.",
|
||||
err);
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static Status alphaDropOutFunctor_(sd::graph::Context& context, NDArray* input, NDArray* output, NDArray* reduceShape, int seed, double probValue, double alpha,
|
||||
double alpha1, double beta, NDArray* mask) {
|
||||
if (reduceShape == nullptr) {
|
||||
alphaDropoutSimple<T>(context.launchContext(), input, output, seed, probValue, alpha, alpha1, beta);
|
||||
} else {
|
||||
REQUIRE_TRUE(reduceShape->lengthOf() <= input->rankOf(), 0, "dropout: Noise shape should be fittable to input");
|
||||
|
||||
std::vector<LongType> dims(reduceShape->lengthOf());
|
||||
reduceShape->syncToHost(); // to ensure that follows are actual
|
||||
bool fit = true;
|
||||
|
||||
for (int i = 0; i < dims.size(); i++) {
|
||||
if (fit) {
|
||||
dims[i] = reduceShape->e<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, "alpha_dropout: Noise shape should fit to input rank.");
|
||||
std::unique_ptr<NDArray> chunk(new NDArray('c', dims, output->dataType(), context.launchContext()));
|
||||
float one = 1.f;
|
||||
|
||||
chunk->assign(one);
|
||||
|
||||
alphaDropoutSimple<T>(context.launchContext(), chunk.get(), chunk.get(), seed, probValue, alpha, alpha1, beta);
|
||||
|
||||
// broadcast chunk to full matrix
|
||||
std::unique_ptr<NDArray> dropOutMultiplier(new NDArray(*input));
|
||||
dropOutMultiplier->assign(one);
|
||||
|
||||
*dropOutMultiplier += *chunk;
|
||||
NDArray ret = *input * *dropOutMultiplier;
|
||||
output->assign(&ret);
|
||||
|
||||
}
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Status alphaDropOutFunctorBP_(sd::graph::Context& context, NDArray* input, NDArray* gradOut, NDArray* output, NDArray* reduceShape,
|
||||
int seed, double probValue, double alpha, double alpha1, double beta, NDArray* mask) {
|
||||
auto res = alphaDropOutFunctor(context, input, output, reduceShape, seed, probValue, alpha, alpha1, beta, mask);
|
||||
if (res == Status::OK) {
|
||||
// FIXME: can we make it single-loop?
|
||||
(*output) *= alpha;
|
||||
(*output) *= (*gradOut);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Status dropOutFunctorBP(sd::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);
|
||||
}
|
||||
|
||||
Status alphaDropOutFunctor(sd::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);
|
||||
}
|
||||
|
||||
Status alphaDropOutFunctorBP(sd::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);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,529 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/dynamic.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename X, typename Y>
|
||||
static SD_KERNEL void dynamicPartitionScalarKernel(const void *vx, const LongType *xShapeInfo, const void *vi,
|
||||
const LongType *iShapeInfo, void **vz, LongType **zShapeInfos, const LongType numOutputs) {
|
||||
auto x = reinterpret_cast<const X *>(vx);
|
||||
auto i = reinterpret_cast<const Y *>(vi);
|
||||
|
||||
__shared__ LongType xRank, iRank;
|
||||
__shared__ const LongType *xShape, *xStride;
|
||||
__shared__ const LongType *iShape, *iStride;
|
||||
|
||||
// Shared variables for CUDA kernel
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
iRank = shape::rank(iShapeInfo);
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
iShape = shape::shapeOf(iShapeInfo);
|
||||
iStride = shape::stride(iShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto xLength = shape::length(xShapeInfo);
|
||||
auto iLength = shape::length(iShapeInfo);
|
||||
|
||||
extern __shared__ char shmem[];
|
||||
__shared__ Y *rawIndices;
|
||||
__shared__ Y *trueIndices;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rawIndices = reinterpret_cast<Y *>(shmem);
|
||||
trueIndices = rawIndices + blockDim.x;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Process partitions
|
||||
for (LongType o = blockIdx.x; o < numOutputs; o += gridDim.x) {
|
||||
auto z = reinterpret_cast<X *>(vz[o]);
|
||||
auto zShapeInfo = zShapeInfos[o];
|
||||
|
||||
__shared__ LongType zLength, zRank;
|
||||
__shared__ const LongType *zShape, *zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
zLength = shape::length(zShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Ensure iLimit is a multiple of blockDim.x
|
||||
auto iLimit = (iLength <= blockDim.x) ? blockDim.x : (iLength + (blockDim.x - (iLength % blockDim.x)));
|
||||
int cnt = 0;
|
||||
|
||||
for (LongType e = threadIdx.x; e < iLimit; e += blockDim.x) {
|
||||
if (e < iLength) {
|
||||
LongType iOffset, iCoords[SD_MAX_RANK];
|
||||
INDEX2COORDS(e, iRank, iShape, iCoords);
|
||||
COORDS2INDEX(iRank, iStride, iCoords, iOffset);
|
||||
rawIndices[threadIdx.x] = i[iOffset];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Map updates using prefix-like approach
|
||||
if (threadIdx.x == 0) {
|
||||
for (int f = 0; f < blockDim.x; f++) {
|
||||
if (rawIndices[f] == static_cast<Y>(o))
|
||||
trueIndices[f] = cnt++;
|
||||
else
|
||||
trueIndices[f] = -1;
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Perform actual update
|
||||
if (e < iLength && trueIndices[threadIdx.x] >= 0) {
|
||||
LongType xOffset, xCoords[SD_MAX_RANK];
|
||||
INDEX2COORDS(e, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
z[trueIndices[threadIdx.x]] = x[xOffset];
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Y>
|
||||
static SD_KERNEL void dynamicPartitionTadKernel(const void *vx, const LongType *xTadShapeInfo,
|
||||
const LongType *xTadOffsets, LongType xLength,
|
||||
const void *vindices, const LongType *iShapeInfo, LongType iLength, void **vz,
|
||||
LongType **zTadShapeInfos, LongType **zTadOffsets,
|
||||
LongType numOutputs) {
|
||||
auto x = reinterpret_cast<const X *>(vx);
|
||||
auto indices = reinterpret_cast<const Y *>(vindices);
|
||||
|
||||
// we run things in blocks, 1 partition per block of threads
|
||||
for (int i = blockIdx.x; i < numOutputs; i += gridDim.x) {
|
||||
auto z = reinterpret_cast<X *>(vz[i]);
|
||||
|
||||
// each thread has own counter for partitions
|
||||
int outCnt = 0;
|
||||
|
||||
for (LongType e = 0; e < iLength; e++) {
|
||||
LongType iCoords[SD_MAX_RANK];
|
||||
LongType iOffset;
|
||||
INDEX2COORDS(e, shape::rank(iShapeInfo), shape::shapeOf(iShapeInfo), iCoords);
|
||||
COORDS2INDEX(shape::rank(iShapeInfo), shape::stride(iShapeInfo), iCoords, iOffset);
|
||||
|
||||
if (indices[iOffset] == i) {
|
||||
auto dx = x + xTadOffsets[e];
|
||||
auto dz = z + zTadOffsets[i][outCnt++];
|
||||
|
||||
for (int f = threadIdx.x; f < xLength; f += blockDim.x) {
|
||||
LongType fCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
LongType zOffset;
|
||||
INDEX2COORDS(f, shape::rank(xTadShapeInfo), shape::shapeOf(xTadShapeInfo), fCoords);
|
||||
COORDS2INDEX(shape::rank(xTadShapeInfo), shape::stride(xTadShapeInfo), fCoords, xOffset);
|
||||
INDEX2COORDS(f, shape::rank(zTadShapeInfos[i]), shape::shapeOf(zTadShapeInfos[i]), fCoords);
|
||||
COORDS2INDEX(shape::rank(zTadShapeInfos[i]), shape::stride(zTadShapeInfos[i]), fCoords, zOffset);
|
||||
|
||||
dz[zOffset] = dx[xOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename X, typename Y>
|
||||
static void _dynamicPartitionFunctor(LaunchContext *context, NDArray *input, NDArray *indices,
|
||||
std::vector<NDArray *> &outputList) {
|
||||
std::vector<std::pair<NDArray *, int>> outputs(outputList.size());
|
||||
int sourceDimsLen = input->rankOf() - indices->rankOf();
|
||||
|
||||
unsigned int outSize = outputList.size();
|
||||
|
||||
PointersManager pm(context, "dynamicPartition");
|
||||
|
||||
if (sourceDimsLen) { // non-linear case
|
||||
std::vector<LongType> sourceDims(sourceDimsLen);
|
||||
|
||||
for (int i = sourceDimsLen; i > 0; i--) sourceDims[sourceDimsLen - i] = input->rankOf() - i;
|
||||
// compute tad array for given dimensions
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), &sourceDims);
|
||||
|
||||
std::vector<void *> outBuffers(outSize);
|
||||
std::vector<const LongType *> tadShapes(outSize);
|
||||
std::vector<const LongType *> tadOffsets(outSize);
|
||||
std::vector<LongType> numTads(outSize);
|
||||
// fill up dimensions array for before kernel
|
||||
for (unsigned int i = 0; i < outSize; i++) {
|
||||
outputs[i].first = outputList[i];
|
||||
std::vector<LongType> outDims(outputs[i].first->rankOf() - 1);
|
||||
|
||||
int r = outputs[i].first->rankOf();
|
||||
|
||||
for (int k = 1; k < r; k++) outDims[k - 1] = k;
|
||||
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(outputList.at(i)->shapeInfo(), &outDims);
|
||||
|
||||
outBuffers[i] = outputList.at(i)->specialBuffer();
|
||||
tadShapes[i] = packZ->platformShapeInfo();
|
||||
tadOffsets[i] = packZ->platformOffsets();
|
||||
}
|
||||
|
||||
// we copy pointers to device
|
||||
auto dOutBuffers =
|
||||
reinterpret_cast<void **>(pm.replicatePointer(outBuffers.data(), outBuffers.size() * sizeof(void *)));
|
||||
auto dOutTadShapes = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(tadShapes.data(), tadShapes.size() * sizeof(LongType *)));
|
||||
auto dOutTadOffsets = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(tadOffsets.data(), tadOffsets.size() * sizeof(LongType *)));
|
||||
// run kernel on device
|
||||
dim3 launchDims = getDynamicPartitionDims(256,sizeof(Y));
|
||||
|
||||
dynamicPartitionTadKernel<X, Y><<<launchDims.y,launchDims.x, launchDims.z, *context->getCudaStream()>>>(
|
||||
input->specialBuffer(), packX->platformShapeInfo(), packX->platformOffsets(),
|
||||
shape::length(packX->primaryShapeInfo()), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
indices->lengthOf(), dOutBuffers, dOutTadShapes, dOutTadOffsets, outSize);
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"dynamicPartitionTadKernel failed");
|
||||
|
||||
|
||||
} else { // linear case
|
||||
dim3 launchDims = getDynamicPartitionDims(256,sizeof(Y));
|
||||
std::vector<void *> outBuffers;
|
||||
std::vector<const LongType *> outShapes;
|
||||
|
||||
for (auto v : outputList) {
|
||||
outBuffers.emplace_back(v->specialBuffer());
|
||||
outShapes.emplace_back(v->specialShapeInfo());
|
||||
}
|
||||
|
||||
auto dOutBuffers =
|
||||
reinterpret_cast<void **>(pm.replicatePointer(outBuffers.data(), outBuffers.size() * sizeof(void *)));
|
||||
auto dOutShapes = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(outShapes.data(), outShapes.size() * sizeof(LongType *)));
|
||||
|
||||
dynamicPartitionScalarKernel<X, Y><<<launchDims.y,launchDims.x, launchDims.z, *context->getCudaStream()>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
dOutBuffers, dOutShapes, outSize);
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"dynamicPartitionScalarKernel failed");
|
||||
|
||||
}
|
||||
|
||||
pm.synchronize();
|
||||
}
|
||||
|
||||
template <typename X, typename Y>
|
||||
static SD_KERNEL void dynamicStitchScalarKernel(void **vx, LongType **xShapeInfos, void **vindices,
|
||||
LongType **iShapeInfos, int inputSize, void *vz,
|
||||
const LongType *zShapeInfo, LongType zLength) {
|
||||
__shared__ LongType zRank;
|
||||
__shared__ const LongType *zShapePtr, *zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto z = reinterpret_cast<X *>(vz);
|
||||
|
||||
// Process each input array
|
||||
for (int e = blockIdx.x; e < inputSize; e += gridDim.x) {
|
||||
auto x = reinterpret_cast<X *>(vx[e]);
|
||||
auto indices = reinterpret_cast<Y *>(vindices[e]);
|
||||
|
||||
auto xShapeInfo = xShapeInfos[e];
|
||||
auto iShapeInfo = iShapeInfos[e];
|
||||
|
||||
auto iLength = shape::length(iShapeInfo);
|
||||
|
||||
// Loop over indices in parallel
|
||||
for (int i = threadIdx.x; i < iLength; i += blockDim.x) {
|
||||
LongType iCoords[SD_MAX_RANK], xCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
LongType iOffset, xOffset, zOffset;
|
||||
|
||||
// Compute index for indices array
|
||||
INDEX2COORDS(i, shape::rank(iShapeInfo), shape::shapeOf(iShapeInfo), iCoords);
|
||||
COORDS2INDEX(shape::rank(iShapeInfo), shape::stride(iShapeInfo), iCoords, iOffset);
|
||||
|
||||
auto idx = indices[iOffset];
|
||||
if (idx >= 0 && idx < zLength) {
|
||||
// Compute z offset
|
||||
INDEX2COORDS(idx, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
|
||||
|
||||
// Compute x offset
|
||||
INDEX2COORDS(i, shape::rank(xShapeInfo), shape::shapeOf(xShapeInfo), xCoords);
|
||||
COORDS2INDEX(shape::rank(xShapeInfo), shape::stride(xShapeInfo), xCoords, xOffset);
|
||||
|
||||
// Assign value to z
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename X, typename Y>
|
||||
static SD_KERNEL void dynamicStitchTadKernel(void **vx, LongType **xTadShapeInfos, LongType **xTadOffsets,
|
||||
void **vindices, LongType **iShapeInfos, int inputSize, void *vz,
|
||||
const LongType *zTadShapeInfo, const LongType *zTadOffsets,
|
||||
LongType *numTadsPerInput, LongType numOutputsTad) {
|
||||
__shared__ LongType zRank, zLength, zTadLength;
|
||||
__shared__ const LongType *zShapePtr, *zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
zRank = shape::rank(zTadShapeInfo);
|
||||
zLength = shape::length(zTadShapeInfo);
|
||||
zTadLength = shape::length(zTadShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zTadShapeInfo);
|
||||
zStridePtr = shape::stride(zTadShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto bz = reinterpret_cast<X *>(vz);
|
||||
|
||||
// Process each input array
|
||||
for (int e = threadIdx.x; e < inputSize; e += blockDim.x) {
|
||||
auto indices = reinterpret_cast<Y *>(vindices[e]);
|
||||
auto iShapeInfo = iShapeInfos[e];
|
||||
auto numTads = numTadsPerInput[e];
|
||||
|
||||
if (shape::isEmptyConst(iShapeInfo)) continue;
|
||||
|
||||
auto iLength = shape::length(iShapeInfo);
|
||||
auto xTadShapeInfo = xTadShapeInfos[e];
|
||||
auto xTadLength = shape::length(xTadShapeInfo);
|
||||
auto xShapePtr = shape::shapeOf(xTadShapeInfo);
|
||||
auto xStridePtr = shape::stride(xTadShapeInfo);
|
||||
|
||||
// Process each index in the input
|
||||
for (int i = 0; i < iLength; i++) {
|
||||
LongType iCoords[SD_MAX_RANK], iOffset;
|
||||
INDEX2COORDS(i, shape::rank(iShapeInfo), shape::shapeOf(iShapeInfo), iCoords);
|
||||
COORDS2INDEX(shape::rank(iShapeInfo), shape::stride(iShapeInfo), iCoords, iOffset);
|
||||
|
||||
auto idx = indices[iOffset];
|
||||
|
||||
// Input array offset for current TAD
|
||||
auto x = reinterpret_cast<X *>(vx[e]) + xTadOffsets[e][i];
|
||||
auto zTad = bz + zTadOffsets[idx];
|
||||
|
||||
// Copy data from input to output
|
||||
for (int j = threadIdx.x; j < xTadLength; j += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
LongType xIdx, zIdx;
|
||||
|
||||
INDEX2COORDS(j, shape::rank(xTadShapeInfo), xShapePtr, xCoords);
|
||||
COORDS2INDEX(shape::rank(xTadShapeInfo), xStridePtr, xCoords, xIdx);
|
||||
|
||||
INDEX2COORDS(j, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zIdx);
|
||||
|
||||
if (xIdx < xTadLength && zIdx < zLength) {
|
||||
zTad[zIdx] = x[xIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename X, typename Y>
|
||||
static Status _dynamicStitchFunctor(LaunchContext *context, std::vector<NDArray *> const &inputs,
|
||||
std::vector<NDArray *> const &indices, NDArray *output) {
|
||||
LongType inputSize = inputs.size();
|
||||
|
||||
PointersManager pm(context, "dynamicStitch");
|
||||
|
||||
if (output->isVector()) {
|
||||
std::vector<const void *> inputBuffers(inputSize);
|
||||
std::vector<const LongType *> inputShapes(inputSize);
|
||||
std::vector<const void *> indicesBuffers(inputSize);
|
||||
std::vector<const LongType *> indicesShapes(inputSize);
|
||||
|
||||
for (LongType e = 0; e < inputSize; e++) {
|
||||
inputBuffers[e] = inputs.at(e)->specialBuffer();
|
||||
indicesBuffers[e] = indices.at(e)->specialBuffer();
|
||||
|
||||
inputShapes[e] = inputs.at(e)->specialShapeInfo();
|
||||
indicesShapes[e] = indices.at(e)->specialShapeInfo();
|
||||
}
|
||||
|
||||
// copying pointers to buffers to device
|
||||
auto dInputBuffers =
|
||||
reinterpret_cast<void **>(pm.replicatePointer(inputBuffers.data(), inputSize * sizeof(void *)));
|
||||
auto dIndicesBuffers =
|
||||
reinterpret_cast<void **>(pm.replicatePointer(indicesBuffers.data(), inputSize * sizeof(void *)));
|
||||
auto dInputShapes =
|
||||
reinterpret_cast<LongType **>(pm.replicatePointer(inputShapes.data(), inputSize * sizeof(LongType *)));
|
||||
auto dIndicesShapes = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(indicesShapes.data(), inputSize * sizeof(LongType *)));
|
||||
dim3 launchDims = getLaunchDims("dynamic_stitch_tad");
|
||||
|
||||
dynamicStitchScalarKernel<X, Y><<<launchDims.y, launchDims.x, launchDims.z, *context->getCudaStream()>>>(
|
||||
dInputBuffers, dInputShapes, dIndicesBuffers, dIndicesShapes, inputSize, output->specialBuffer(),
|
||||
output->specialShapeInfo(), output->lengthOf());
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"dynamicStitchScalarKernel failed");
|
||||
|
||||
} else {
|
||||
std::vector<LongType> restDims(output->rankOf() - 1);
|
||||
for (int i = restDims.size(); i > 0; i--) restDims[restDims.size() - i] = output->rankOf() - i;
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), &restDims);
|
||||
|
||||
std::vector<const void *> inputBuffers(inputSize);
|
||||
std::vector<const LongType *> inputTadShapes(inputSize);
|
||||
std::vector<const LongType *> inputTadOffsets(inputSize);
|
||||
|
||||
std::vector<const void *> indicesBuffers(inputSize);
|
||||
std::vector<const LongType *> indicesShapes(inputSize);
|
||||
std::vector<LongType> inputsNumTads(inputSize);
|
||||
|
||||
for (LongType e = 0; e < inputSize; e++) {
|
||||
std::vector<LongType> sourceDims(inputs[e]->rankOf() - indices[e]->rankOf());
|
||||
for (LongType i = sourceDims.size(); i > 0; i--) sourceDims[sourceDims.size() - i] = inputs[e]->rankOf() - i;
|
||||
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(inputs[e]->shapeInfo(), &sourceDims);
|
||||
indicesBuffers[e] = indices[e]->specialBuffer();
|
||||
indicesShapes[e] = indices[e]->specialShapeInfo();
|
||||
inputsNumTads[e] = packX->numberOfTads();
|
||||
inputBuffers[e] = inputs[e]->specialBuffer();
|
||||
inputTadShapes[e] = packX->platformShapeInfo();
|
||||
inputTadOffsets[e] = packX->platformOffsets();
|
||||
}
|
||||
|
||||
// copying pointers to buffers to device
|
||||
auto dInputBuffers =
|
||||
reinterpret_cast<void **>(pm.replicatePointer(inputBuffers.data(), inputSize * sizeof(void *)));
|
||||
auto dInputTadShapes = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(inputTadShapes.data(), inputSize * sizeof(LongType *)));
|
||||
auto dInputTadOffsets = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(inputTadOffsets.data(), inputSize * sizeof(LongType *)));
|
||||
|
||||
auto dIndicesBuffers =
|
||||
reinterpret_cast<void **>(pm.replicatePointer(indicesBuffers.data(), inputSize * sizeof(void *)));
|
||||
auto dIndicesShapes = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(indicesShapes.data(), inputSize * sizeof(LongType *)));
|
||||
|
||||
auto dNumTadsInputs = reinterpret_cast<LongType *>(
|
||||
pm.replicatePointer(inputsNumTads.data(), inputSize * sizeof(LongType *)));
|
||||
|
||||
|
||||
dim3 launchDims = getLaunchDims("dynamic_stitch_tad");
|
||||
dynamicStitchTadKernel<X, Y><<<launchDims.x, launchDims.y, launchDims.z, *context->getCudaStream()>>>(
|
||||
dInputBuffers, dInputTadShapes, dInputTadOffsets, dIndicesBuffers, dIndicesShapes, inputSize,
|
||||
output->specialBuffer(), packZ->platformShapeInfo(), packZ->platformOffsets(),dNumTadsInputs, packZ->numberOfTads());
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"dynamicStitchTadKernel failed");
|
||||
|
||||
}
|
||||
|
||||
pm.synchronize();
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void _dynamicPartitionFunctorBP(NDArray *input, NDArray *indices,
|
||||
std::vector<NDArray *> const &inputGradientList,
|
||||
std::vector<NDArray *> &outputList) {}
|
||||
|
||||
void dynamicPartitionFunctor(LaunchContext *context, NDArray *input, NDArray *indices,
|
||||
std::vector<NDArray *> &outputList) {
|
||||
auto xType = input->dataType();
|
||||
auto yType = indices->dataType();
|
||||
|
||||
NDArray::prepareSpecialUse({}, {indices, input});
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(xType, yType, _dynamicPartitionFunctor, (context, input, indices, outputList), SD_NUMERIC_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({}, {indices, input});
|
||||
|
||||
// TODO: it would be nice to have NDArray::registerSpecialUse signature that accepts something else beyond
|
||||
// initializer_list
|
||||
for (auto v : outputList) {
|
||||
v->tickWriteDevice();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static Status _dynamicStitchFunctorBP(std::vector<NDArray *> const &inputs, std::vector<NDArray *> const &indices,
|
||||
NDArray *gradInput, std::vector<NDArray *> &outputList) {
|
||||
THROW_EXCEPTION("Not implemented yet");
|
||||
}
|
||||
|
||||
Status dynamicStitchFunctor(LaunchContext *context, std::vector<NDArray *> const &inputs,
|
||||
std::vector<NDArray *> const &indices, NDArray *output) {
|
||||
auto xType = inputs.at(0)->dataType();
|
||||
auto yType = indices.at(0)->dataType();
|
||||
|
||||
for (auto v : indices) {
|
||||
v->syncToDevice();
|
||||
v->tickReadDevice();
|
||||
}
|
||||
|
||||
for (auto v : inputs) {
|
||||
v->syncToDevice();
|
||||
v->tickReadDevice();
|
||||
}
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {});
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(xType, yType, _dynamicStitchFunctor, (context, inputs, indices, output), SD_NUMERIC_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({output}, {});
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
Status dynamicStitchFunctorBP(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_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
void dynamicPartitionFunctorBP(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_NUMERIC_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,184 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
|
||||
#include <ops/declarable/helpers/axis.h>
|
||||
#include <execution/Threads.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// extract patches kernel
|
||||
// - theSame - SAME or VALID - output format
|
||||
// - batchCount - batches - the first dimension of input
|
||||
// - sizeRow, sizeCol - rows and cols sizes for batch
|
||||
// - rowDim, colDim - rows and cols dimensions for input patches
|
||||
// - outRowDim, outColDim - rows and cols dimensions for output patches
|
||||
// - strideRow, strideCol - step between input elements with patches
|
||||
// - rateRow, rateCol - counts for input patches
|
||||
// - rowCast, colCast - shifts for output placement (1 or 0)
|
||||
// - lastDim - last dimension of input/output
|
||||
// - input - input tensor buffer
|
||||
// - patchShape - input patch TAD shape
|
||||
// - inputOffsets - input TAD offsets
|
||||
// - output - output tensor buffer
|
||||
// - outTadShape - output TAD shape
|
||||
// - outputOffsets - output TAD offsets
|
||||
//
|
||||
template <typename T>
|
||||
static SD_KERNEL void globalExtractPatchesKernel(bool theSame, int batchCount,
|
||||
int sizeRow, int sizeCol, int rowDim,
|
||||
int colDim, int outRowDim,
|
||||
int outColDim, int strideRow,
|
||||
int strideCol,
|
||||
int rateRow, int rateCol,
|
||||
int rowCast,
|
||||
int colCast, int lastDim,
|
||||
const T* input,
|
||||
const LongType* patchShape,
|
||||
const LongType* inputOffsets,
|
||||
T* output,
|
||||
const LongType* outTadShape,
|
||||
const LongType* outputOffsets) {
|
||||
|
||||
for (auto batch = threadIdx.x; batch < batchCount; batch+= gridDim.x) {
|
||||
auto patch = input + inputOffsets[batch];
|
||||
auto outMatrix = output + outputOffsets[batch];
|
||||
|
||||
for (LongType i = 0; i < outRowDim; i++) {
|
||||
for (LongType j = 0; j < outColDim; j++) {
|
||||
LongType pos = 0;
|
||||
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<T>(rowStart + sizeRow * rateRow, rowDim);
|
||||
colEnd = math::sd_min<T>(colStart + sizeCol * rateCol, colDim);
|
||||
}
|
||||
for (auto row = rowStart; row < rowEnd; row += rateRow)
|
||||
for (auto col = colStart; col < colEnd; col += rateCol)
|
||||
for (auto pixel = 0; pixel < lastDim; pixel++) {
|
||||
LongType zPos[] = {i, j, pos};
|
||||
LongType xPos[] = {row, col, pixel};
|
||||
bool setUp = (theSame && row >= 0 && col >= 0 && row < rowDim && col < colDim) || (!theSame);
|
||||
|
||||
if (setUp) { // VALID or SAME cases
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(shape::rank(outTadShape), shape::stride(outTadShape), zPos, zOffset);
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(shape::rank(patchShape), shape::stride(patchShape), xPos, xOffset);
|
||||
outMatrix[zOffset] = patch[xOffset];
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void _extractPatches(LaunchContext* context, NDArray* images, NDArray* output, int sizeRow, int sizeCol,
|
||||
int strideRow, int strideCol, int rateRow, int rateCol, bool theSame) {
|
||||
std::vector<LongType> restDims({1, 2, 3}); // the first and the last dims
|
||||
ResultSet listOfMatricies = images->allTensorsAlongDimension(restDims);
|
||||
ResultSet listOfOutputs = output->allTensorsAlongDimension(restDims);
|
||||
// 3D matrices - 2D matrices of vectors (if last dim is greater than 1)
|
||||
|
||||
int batchCount = listOfMatricies.size();
|
||||
LongType lastDim = images->sizeAt(3);
|
||||
LongType rowDim = images->sizeAt(1);
|
||||
LongType colDim = images->sizeAt(2);
|
||||
LongType outRowDim = output->sizeAt(1);
|
||||
LongType outColDim = output->sizeAt(2);
|
||||
auto rowCast = 1;
|
||||
auto colCast = 1;
|
||||
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);
|
||||
std::vector<sd::LongType> inShape = {patch->sizeAt(1), patch->sizeAt(2), patch->sizeAt(3)};
|
||||
auto inPatch = patch->rankOf() > 3 && patch->sizeAt(0) == 1 ? patch->reshape('c', inShape, false) : patch;
|
||||
auto outMatrix = listOfOutputs.at(batch);
|
||||
std::vector<sd::LongType> outShape = {outMatrix->sizeAt(1), outMatrix->sizeAt(2), outMatrix->sizeAt(3)};
|
||||
auto outReshape = outMatrix->rankOf() > 3 && outMatrix->sizeAt(0) == 1 ? outMatrix->reshape('c', outShape, false) : outMatrix;
|
||||
for (LongType i = 0; i < outRowDim; i++) {
|
||||
for (LongType j = 0; j < outColDim; j++) {
|
||||
LongType pos = 0;
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
outReshape->p<T>(i,j,pos,inPatch->e<T>(row, col, pixel));
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cleanup reshaped arrays if they were created
|
||||
if (inPatch != patch) delete inPatch;
|
||||
if (outReshape != outMatrix) delete outReshape;
|
||||
}
|
||||
};
|
||||
|
||||
samediff::Threads::parallel_tad(func, 0, batchCount);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void _extractPatches,
|
||||
(sd::LaunchContext * context, NDArray* input, NDArray* output, int sizeRow, int sizeCol,
|
||||
int stradeRow, int stradeCol, int rateRow, int rateCol, bool theSame),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
void extractPatches(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,
|
||||
(context, images, output, sizeRow, sizeCol, stradeRow, stradeCol, rateRow, rateCol, theSame),
|
||||
SD_COMMON_TYPES);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,179 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// fakeQuantWithMinMaxVars_
|
||||
// input - input tensor
|
||||
// min - min scalar tensor
|
||||
// max - max scalar tensor
|
||||
// numBits - (default 16bit)
|
||||
// narrowed - shrink is true
|
||||
// output - output tensor
|
||||
//
|
||||
template <typename T>
|
||||
static SD_HOST_DEVICE void nudge(T min, T max, int quantMin, int quantMax, T* scale, T* nudgedMin, T* nudgedMax) {
|
||||
T quantMaxF = static_cast<T>(quantMax);
|
||||
T quantMinF = static_cast<T>(quantMin);
|
||||
*scale = (max - min) / (quantMaxF - quantMinF);
|
||||
auto zeroPointFromMin = quantMinF - min / *scale;
|
||||
uint16_t const nudgedZeroPoint = [zeroPointFromMin, quantMin, quantMax, quantMaxF, quantMinF] {
|
||||
if (zeroPointFromMin < quantMinF) {
|
||||
return static_cast<uint16_t>(quantMin);
|
||||
}
|
||||
if (zeroPointFromMin > quantMaxF) {
|
||||
return static_cast<uint16_t>(quantMax);
|
||||
}
|
||||
return math::sd_round<T, uint16_t>(zeroPointFromMin);
|
||||
}();
|
||||
*nudgedMax = (quantMaxF - static_cast<T>(nudgedZeroPoint)) * (*scale);
|
||||
*nudgedMin = (quantMinF - static_cast<T>(nudgedZeroPoint)) * (*scale);
|
||||
}
|
||||
|
||||
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;
|
||||
min->syncToHost(); // these are scalars, so nothing much happened
|
||||
max->syncToHost();
|
||||
T scale, nudgedMin, nudgedMax;
|
||||
nudge(min->t<T>(0), max->t<T>(0), lowIntBound, upperIntBound, &scale, &nudgedMin, &nudgedMax);
|
||||
|
||||
auto wiseMinMaxAndSoOn = LAMBDA_T(x, nudgedMin, nudgedMax, scale) {
|
||||
T val = x;
|
||||
if (x < nudgedMin) {
|
||||
val = nudgedMin;
|
||||
} else if (x > nudgedMax) {
|
||||
val = nudgedMax;
|
||||
} else
|
||||
val = x;
|
||||
return (math::sd_floor<T, T>((val - nudgedMin) / scale + T(0.5)) * scale + nudgedMin);
|
||||
});
|
||||
|
||||
input->applyLambda(wiseMinMaxAndSoOn, output);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void fakeQuantWithMinMaxKernel(const T* input, const LongType* inputShape, T* min, T* max,
|
||||
int lowIntBound, int upperIntBound, LongType channels, T* output,
|
||||
const LongType* outputShape, LongType length) {
|
||||
__shared__ LongType inputRank, outputRank;
|
||||
__shared__ const LongType* inputShapePtr;
|
||||
__shared__ const LongType* inputStridePtr;
|
||||
__shared__ const LongType* outputShapePtr;
|
||||
__shared__ const LongType* outputStridePtr;
|
||||
__shared__ LongType blockSize;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
blockSize = length / channels; // Calculate block size based on the last dimension
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType inputOffset;
|
||||
LongType outputOffset;
|
||||
|
||||
// Loop over channels
|
||||
for (auto i = blockIdx.x; i < (int)channels; i += gridDim.x) {
|
||||
T scale, nudgedMin, nudgedMax;
|
||||
|
||||
// Nudge values for quantization
|
||||
nudge(min[i], max[i], lowIntBound, upperIntBound, &scale, &nudgedMin, &nudgedMax);
|
||||
|
||||
// Loop over blocks for quantization
|
||||
for (auto b = threadIdx.x; b < blockSize; b += blockDim.x) {
|
||||
// Compute input coordinates and offset
|
||||
INDEX2COORDS(b * channels + i, inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, inputOffset);
|
||||
|
||||
T val = input[inputOffset];
|
||||
|
||||
// Clamp value within nudged min and max
|
||||
if (val < nudgedMin) {
|
||||
val = nudgedMin;
|
||||
} else if (val > nudgedMax) {
|
||||
val = nudgedMax;
|
||||
}
|
||||
|
||||
// Compute output coordinates and offset
|
||||
INDEX2COORDS(b * channels + i, outputRank, outputShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, outputOffset);
|
||||
|
||||
// Quantize and assign the value to output
|
||||
output[outputOffset] = math::sd_floor<T, T>((val - nudgedMin) / scale + T(0.5f)) * scale + nudgedMin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void fakeQuantWithMinMaxVarsPerChannel_(LaunchContext* context, NDArray* input, NDArray* min, NDArray* max, int numBits,
|
||||
bool narrowed, NDArray* output) {
|
||||
int lowIntBound = narrowed ? 1 : 0;
|
||||
int upperIntBound = (1 << numBits) - 1;
|
||||
auto channels = min->lengthOf();
|
||||
auto length = input->lengthOf();
|
||||
NDArray::prepareSpecialUse({output}, {min, max, input});
|
||||
auto stream = context->getCudaStream();
|
||||
T* inputBuf = input->dataBuffer()->specialAsT<T>();
|
||||
T* outputBuf = output->dataBuffer()->specialAsT<T>();
|
||||
T* minBuf = min->dataBuffer()->specialAsT<T>();
|
||||
T* maxBuf = max->dataBuffer()->specialAsT<T>();
|
||||
dim3 launchDims = getLaunchDims("fake_quantization");
|
||||
fakeQuantWithMinMaxKernel<<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(inputBuf, input->specialShapeInfo(), minBuf, maxBuf,
|
||||
lowIntBound, upperIntBound, channels, outputBuf,
|
||||
output->specialShapeInfo(), length);
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"fakeQuantWithMinMaxKernel failed");
|
||||
|
||||
NDArray::registerSpecialUse({output}, {min, max, input});
|
||||
}
|
||||
|
||||
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_,
|
||||
(context, input, min, max, numBits, narrowed, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,109 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/PointersManager.h>
|
||||
#include <ops/declarable/helpers/flatten.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static void SD_KERNEL flattenKernel(void **xBuffers, LongType **xShapeInfos, LongType *offsets, LongType numInputs, void *zBuffer, const LongType *zShapeInfo, char order) {
|
||||
__shared__ LongType xRank, xLength;
|
||||
__shared__ const LongType *xShapePtr, *xStridePtr;
|
||||
|
||||
int xCoord[SD_MAX_RANK];
|
||||
|
||||
// Each block of threads works on one input array
|
||||
for (LongType e = blockIdx.x; e < numInputs; e += gridDim.x) {
|
||||
auto z = reinterpret_cast<T *>(zBuffer) + offsets[e];
|
||||
|
||||
auto xBuffer = reinterpret_cast<T *>(xBuffers[e]);
|
||||
auto xShapeInfo = xShapeInfos[e];
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xLength = shape::length(xShapeInfo);
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Each element of this input array has its own place within the common output array
|
||||
for (LongType i = threadIdx.x; i < xLength; i += blockDim.x) {
|
||||
LongType xOffset;
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
|
||||
// Compute x coordinates and offset
|
||||
INDEX2COORDS(i, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
|
||||
|
||||
// Write the value from xBuffer to the flattened zBuffer
|
||||
z[i] = xBuffer[xOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void flatten_(LaunchContext *context, std::vector<NDArray *> &inputs, NDArray *output, char order) {
|
||||
PointersManager pm(context, "flatten");
|
||||
|
||||
std::vector<const void *> hdBuffers(inputs.size());
|
||||
std::vector<LongType> hOffsets(inputs.size());
|
||||
std::vector<const LongType *> hdShapes(inputs.size());
|
||||
LongType cOffset = 0;
|
||||
|
||||
// calculating offsets in output
|
||||
for (int e = 0; e < inputs.size(); e++) {
|
||||
hOffsets[e] = cOffset;
|
||||
cOffset += inputs[e]->lengthOf();
|
||||
|
||||
hdBuffers[e] = inputs[e]->specialBuffer();
|
||||
hdShapes[e] = inputs[e]->specialShapeInfo();
|
||||
}
|
||||
|
||||
// copying pointers to device
|
||||
auto dBuffers = (void **)pm.replicatePointer(hdBuffers.data(), inputs.size() * sizeof(void *));
|
||||
auto dShapes = (LongType **)pm.replicatePointer(hdShapes.data(), inputs.size() * sizeof(LongType *));
|
||||
auto dOffsets = (LongType *)pm.replicatePointer(hOffsets.data(), inputs.size() * sizeof(LongType));
|
||||
dim3 launchDims = getLaunchDims("flatten");
|
||||
flattenKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *context->getCudaStream()>>>(
|
||||
dBuffers, dShapes, dOffsets, inputs.size(), output->specialBuffer(), output->specialShapeInfo(), order);
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"flattenKernel failed");
|
||||
|
||||
pm.synchronize();
|
||||
}
|
||||
|
||||
void flatten(LaunchContext *context, std::vector<NDArray *> &inputs, NDArray *output, char order) {
|
||||
// FIXME: we want NDArrayFactory::prepareSpecialUse here eventually
|
||||
const std::vector<NDArray *> v(inputs.begin(), inputs.end());
|
||||
//prepareSpecialUse requires const
|
||||
NDArray::prepareSpecialUse({output}, v, {});
|
||||
BUILD_SINGLE_SELECTOR(output->dataType(), flatten_, (context, inputs, output, order), SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,266 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/gather.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void gatherCudaLinearKernel(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo) {
|
||||
__shared__ const X* x;
|
||||
__shared__ const Y* y;
|
||||
__shared__ X* z;
|
||||
__shared__ LongType xLen, yLen, zLen;
|
||||
__shared__ LongType xRank, yRank, zRank;
|
||||
__shared__ const LongType *xShapePtr, *xStridePtr;
|
||||
__shared__ const LongType *yShapePtr, *yStridePtr;
|
||||
__shared__ const LongType *zShapePtr, *zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<const X*>(vx);
|
||||
z = reinterpret_cast<X*>(vz);
|
||||
y = reinterpret_cast<const Y*>(vy);
|
||||
|
||||
xLen = shape::length(xShapeInfo);
|
||||
yLen = shape::length(yShapeInfo);
|
||||
zLen = shape::length(zShapeInfo);
|
||||
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
|
||||
yShapePtr = shape::shapeOf(yShapeInfo);
|
||||
yStridePtr = shape::stride(yShapeInfo);
|
||||
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
for (LongType j = start; j < zLen; j += step) {
|
||||
LongType zIndex, yIndex, xIndex;
|
||||
LongType zCoords[SD_MAX_RANK], yCoords[SD_MAX_RANK], xCoords[SD_MAX_RANK];
|
||||
|
||||
// Compute z coordinates and offset
|
||||
INDEX2COORDS(j, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zIndex);
|
||||
|
||||
// Compute y coordinates and offset
|
||||
INDEX2COORDS(j, yRank, yShapePtr, yCoords);
|
||||
COORDS2INDEX(yRank, yStridePtr, yCoords, yIndex);
|
||||
|
||||
// Compute x coordinates and offset
|
||||
INDEX2COORDS(y[yIndex], xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xIndex);
|
||||
|
||||
// Assign value to z
|
||||
z[zIndex] = x[xIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void gatherCuda(const int numOfSubArrs, const void* vx, const LongType* xShapeInfo,
|
||||
const LongType* xOffsets, const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const LongType* zOffsets) {
|
||||
const Y* y = reinterpret_cast<const Y*>(vy);
|
||||
|
||||
__shared__ const X* x;
|
||||
__shared__ X* z;
|
||||
__shared__ LongType xLen, yRank, xRank, zRank;
|
||||
__shared__ const LongType *xShapePtr, *xStridePtr, *yShapePtr, *yStridePtr, *zShapePtr, *zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
yShapePtr = shape::shapeOf(yShapeInfo);
|
||||
yStridePtr = shape::stride(yShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (LongType i = blockIdx.x; i < numOfSubArrs; i += gridDim.x) {
|
||||
if (threadIdx.x == 0) {
|
||||
LongType yIndex, xOffset, zOffset;
|
||||
LongType yCoords[SD_MAX_RANK], xCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
|
||||
// Calculate y index
|
||||
INDEX2COORDS(i, yRank, yShapePtr, yCoords);
|
||||
COORDS2INDEX(yRank, yStridePtr, yCoords, yIndex);
|
||||
|
||||
// Calculate x offset
|
||||
INDEX2COORDS(y[yIndex], xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
|
||||
|
||||
// Calculate z offset
|
||||
INDEX2COORDS(i, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
|
||||
|
||||
x = reinterpret_cast<const X*>(vx) + xOffsets[xOffset];
|
||||
z = reinterpret_cast<X*>(vz) + zOffsets[zOffset];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Copy data from x to z
|
||||
for (LongType j = threadIdx.x; j < xLen; j += blockDim.x) {
|
||||
LongType zIndex, xIndex;
|
||||
LongType zCoords[SD_MAX_RANK], xCoords[SD_MAX_RANK];
|
||||
|
||||
// Calculate z index
|
||||
INDEX2COORDS(j, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zIndex);
|
||||
|
||||
// Calculate x index
|
||||
INDEX2COORDS(j, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xIndex);
|
||||
|
||||
// Copy value
|
||||
z[zIndex] = x[xIndex];
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename X, typename Y>
|
||||
SD_HOST static void gatherCudaLinear(const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo) {
|
||||
//note gather linear and gather are different kernels
|
||||
dim3 gatherLinear = getLaunchDims("gather_linear");
|
||||
gatherCudaLinearKernel<X, Y><<<gatherLinear.x, gatherLinear.y, gatherLinear.z, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream),"gatherCudaLinearKernel failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
SD_HOST static void gatherCudaLauncher(const cudaStream_t* stream, const int numOfSubArrs, const void* vx,
|
||||
const LongType* xShapeInfo, const LongType* xOffsets, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const LongType* zOffsets) {
|
||||
dim3 gatherLinear = getGatherLinear(numOfSubArrs);
|
||||
gatherCuda<X, Y><<<gatherLinear.y, gatherLinear.x, gatherLinear.z, *stream>>>(numOfSubArrs, vx, xShapeInfo, xOffsets, vy,
|
||||
yShapeInfo, vz, zShapeInfo, zOffsets);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream),"gatherCudaLauncher failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void gather(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output,
|
||||
const std::vector<LongType>& intArgs) {
|
||||
const LongType inputRank = input->rankOf();
|
||||
const LongType numOfIntArgs = intArgs.size();
|
||||
|
||||
LongType axis = numOfIntArgs > 0 ? intArgs[0] : 0;
|
||||
if (axis < 0) axis += inputRank;
|
||||
|
||||
if (indices == nullptr && numOfIntArgs == 2) { // scalar case
|
||||
NDArray scalar = (*input)(intArgs[1], {axis});
|
||||
output->assign(&scalar);
|
||||
} else if (indices != nullptr && 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<LongType>(0);
|
||||
auto scalarNDArray = input->e(idx);
|
||||
output->assign(&scalarNDArray);
|
||||
} else {
|
||||
NDArray inSubArr = (*input)(indices->e<LongType>(0), {axis});
|
||||
output->assign(&inSubArr);
|
||||
}
|
||||
} else {
|
||||
NDArray* pIndices = const_cast<NDArray*>(indices);
|
||||
if (indices == nullptr) {
|
||||
std::vector<LongType> firstShape = {numOfIntArgs - 1};
|
||||
std::vector<double> data = std::vector<double>(intArgs.begin() + 1, intArgs.end());
|
||||
pIndices = new NDArray(input->ordering(),firstShape,
|
||||
data, INT64, input->getContext());
|
||||
}
|
||||
std::vector<LongType> dimsOut(pIndices->rankOf());
|
||||
std::iota(dimsOut.begin(), dimsOut.end(), axis); // fill with axis, axis+1, ... axis+pIndices->rankOf()-1
|
||||
|
||||
const LongType numOfSubArrs = pIndices->lengthOf();
|
||||
|
||||
LongType *outSubArrShapeInfo(nullptr), *inSubArrShapeInfo(nullptr), *outSubArrOffsets(nullptr),
|
||||
*inSubArrOffsets(nullptr);
|
||||
input->getSubArrShapeAndOffsets({axis}, inSubArrShapeInfo, inSubArrOffsets);
|
||||
output->getSubArrShapeAndOffsets(dimsOut, outSubArrShapeInfo, outSubArrOffsets);
|
||||
if (output->rankOf() > 1) {
|
||||
PointersManager manager(context, "gather");
|
||||
auto xShapeInfo = reinterpret_cast<LongType*>(
|
||||
manager.replicatePointer(inSubArrShapeInfo, shape::shapeInfoByteLength(inSubArrShapeInfo)));
|
||||
auto zShapeInfo = reinterpret_cast<LongType*>(
|
||||
manager.replicatePointer(outSubArrShapeInfo, shape::shapeInfoByteLength(outSubArrShapeInfo)));
|
||||
auto xOffsets = reinterpret_cast<LongType*>(manager.replicatePointer(
|
||||
inSubArrOffsets, (input->lengthOf() / shape::length(inSubArrShapeInfo)) * sizeof(LongType)));
|
||||
auto zOffsets = reinterpret_cast<LongType*>(manager.replicatePointer(
|
||||
outSubArrOffsets, (output->lengthOf() / shape::length(outSubArrShapeInfo)) * sizeof(LongType)));
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input, pIndices});
|
||||
BUILD_DOUBLE_SELECTOR(
|
||||
input->dataType(), pIndices->dataType(), gatherCudaLauncher,
|
||||
(context->getCudaStream(), numOfSubArrs, input->specialBuffer(), xShapeInfo, xOffsets,
|
||||
pIndices->specialBuffer(), pIndices->specialShapeInfo(), output->specialBuffer(), zShapeInfo, zOffsets),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, pIndices});
|
||||
manager.synchronize();
|
||||
} else {
|
||||
NDArray::prepareSpecialUse({output}, {input, pIndices});
|
||||
BUILD_DOUBLE_SELECTOR(
|
||||
input->dataType(), pIndices->dataType(), gatherCudaLinear,
|
||||
(context->getCudaStream(), input->specialBuffer(), input->specialShapeInfo(), pIndices->specialBuffer(),
|
||||
pIndices->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo()),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, pIndices});
|
||||
}
|
||||
|
||||
if (indices == nullptr) delete pIndices;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,159 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - input, y - indices, z - output
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void gatherNDCuda(const void *vx, const LongType *xShapeInfo, const void *vy,
|
||||
const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) {
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
const auto y = reinterpret_cast<const Y *>(vy);
|
||||
auto z = reinterpret_cast<X *>(vz);
|
||||
|
||||
__shared__ int xRank, yRank, zRank, maxRank, yLastDim;
|
||||
__shared__ LongType zLen, totalThreads;
|
||||
__shared__ const LongType *xShapePtr, *xStridePtr;
|
||||
__shared__ const LongType *yShapePtr, *yStridePtr;
|
||||
__shared__ const LongType *zShapePtr, *zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
maxRank = sd::math::sd_max<int>(yRank, sd::math::sd_max<int>(xRank, zRank));
|
||||
|
||||
zLen = shape::length(zShapeInfo);
|
||||
yLastDim = shape::shapeOf(yShapeInfo)[yRank - 1];
|
||||
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
yShapePtr = shape::shapeOf(yShapeInfo);
|
||||
yStridePtr = shape::stride(yShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
extern __shared__ unsigned char shmem[];
|
||||
auto coord = reinterpret_cast<LongType *>(shmem) + threadIdx.x * maxRank;
|
||||
|
||||
LongType *zCoordStart, *xCoordStart;
|
||||
|
||||
if (yLastDim == xRank) {
|
||||
zCoordStart = coord;
|
||||
xCoordStart = coord;
|
||||
} else if (zRank >= xRank) {
|
||||
zCoordStart = coord;
|
||||
xCoordStart = coord + zRank - xRank;
|
||||
} else {
|
||||
zCoordStart = coord + xRank - zRank;
|
||||
xCoordStart = coord;
|
||||
}
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < zLen; i += totalThreads) {
|
||||
// Compute z coordinates and offset
|
||||
INDEX2COORDS(i, zRank, zShapePtr, zCoordStart);
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoordStart, zOffset);
|
||||
|
||||
// Save and modify last y coordinate
|
||||
int coordToRestore = (yLastDim != xRank) ? static_cast<int>(zCoordStart[yRank - 1]) : 0;
|
||||
zCoordStart[yRank - 1] = 0;
|
||||
|
||||
// Compute y offset
|
||||
LongType yOffset;
|
||||
COORDS2INDEX(yRank, yStridePtr, zCoordStart, yOffset);
|
||||
|
||||
// Restore z coordinate
|
||||
if (yLastDim != xRank) zCoordStart[yRank - 1] = coordToRestore;
|
||||
|
||||
// Compute x coordinates
|
||||
for (LongType j = 0; j < yLastDim; ++j) {
|
||||
xCoordStart[j] = y[yOffset + j * yStridePtr[yRank - 1]];
|
||||
}
|
||||
|
||||
// Compute x offset
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoordStart, xOffset);
|
||||
|
||||
// Assign value to z
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void gatherNDCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo,
|
||||
const void *vy, const LongType *yShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo) {
|
||||
gatherNDCuda<X, Y>
|
||||
<<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream),"gatherNDCuda failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void gatherND(LaunchContext *context, NDArray &input, NDArray &indices, NDArray &output) {
|
||||
const int maxRank = sd::math::sd_max<int>(indices.rankOf(), sd::math::sd_max<int>(input.rankOf(), output.rankOf()));
|
||||
|
||||
|
||||
dim3 gatherNdDims = getGatherNd(output.lengthOf(),maxRank);
|
||||
const auto xType = input.dataType();
|
||||
const auto yType = indices.dataType();
|
||||
|
||||
PointersManager manager(context, "gatherND");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input, &indices});
|
||||
BUILD_DOUBLE_SELECTOR(xType, yType, gatherNDCudaLauncher,
|
||||
(gatherNdDims.y, gatherNdDims.x, gatherNdDims.z, context->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), indices.specialBuffer(), indices.specialShapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo()),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input, &indices});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 sgazeos@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/axis.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
void applyGradientDescent_(LaunchContext* context, NDArray* input, NDArray* step, double weight,
|
||||
NDArray* output) {
|
||||
// classic one
|
||||
auto lambda = LAMBDA_TT(_x, _y, weight) { return _x - (_y * weight); });
|
||||
|
||||
input->applyPairwiseLambda(step, lambda, output);
|
||||
}
|
||||
|
||||
void applyGradientDescent(LaunchContext* context, NDArray* input, NDArray* step, double weight, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), applyGradientDescent_, (context, input, step, weight, output),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void applyGradientDescent_,
|
||||
(LaunchContext * context, NDArray* input, NDArray* step, double weight, NDArray* output),
|
||||
SD_FLOAT_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 raver119@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/hamming.h>
|
||||
#include <ops/declarable/helpers/helpers.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename X, typename Z>
|
||||
static SD_KERNEL void _hammingKernel(const void *vx, const LongType *xShapeInfo, const void *vy,
|
||||
const LongType *yShapeInfo, void *vz, void *reductionBuffer, LongType length) {
|
||||
auto x = reinterpret_cast<const X *>(vx);
|
||||
auto y = reinterpret_cast<const X *>(vy);
|
||||
auto z = reinterpret_cast<Z *>(vz);
|
||||
|
||||
__shared__ LongType shared[SD_CUDA_BLOCK_SIZE];
|
||||
__shared__ LongType xRank, yRank;
|
||||
__shared__ const LongType *xShapePtr, *xStridePtr;
|
||||
__shared__ const LongType *yShapePtr, *yStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
yShapePtr = shape::shapeOf(yShapeInfo);
|
||||
yStridePtr = shape::stride(yShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Initialize shared memory for intermediate results
|
||||
shared[threadIdx.x] = 0;
|
||||
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
for (LongType e = tid; e < length; e += blockDim.x * gridDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK], yCoords[SD_MAX_RANK];
|
||||
LongType xOffset, yOffset;
|
||||
|
||||
// Calculate coordinates and offsets using cached values
|
||||
INDEX2COORDS(e, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
|
||||
INDEX2COORDS(e, yRank, yShapePtr, yCoords);
|
||||
COORDS2INDEX(yRank, yStridePtr, yCoords, yOffset);
|
||||
|
||||
auto _x = static_cast<unsigned long long>(x[xOffset]);
|
||||
auto _y = static_cast<unsigned long long>(y[yOffset]);
|
||||
|
||||
// Save intermediate results into shared memory
|
||||
shared[threadIdx.x] += __popcll(_x ^ _y);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Reduction within a block
|
||||
auto numItems = sd::math::sd_min<LongType>(blockDim.x, length);
|
||||
auto floorPow2 = numItems;
|
||||
if (floorPow2 & (floorPow2 - 1)) {
|
||||
while (floorPow2 & (floorPow2 - 1)) floorPow2 &= floorPow2 - 1;
|
||||
|
||||
if (threadIdx.x >= floorPow2)
|
||||
shared[threadIdx.x - floorPow2] += shared[threadIdx.x];
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
for (LongType activeThreads = floorPow2 >> 1; activeThreads; activeThreads >>= 1) {
|
||||
if (threadIdx.x < activeThreads && threadIdx.x + activeThreads < numItems)
|
||||
shared[threadIdx.x] += shared[threadIdx.x + activeThreads];
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Write the final result to global memory
|
||||
if (threadIdx.x == 0 && shared[0] > 0) {
|
||||
math::atomics::sd_atomicAdd<Z>(&z[0], static_cast<Z>(shared[0]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void _hamming(LaunchContext *context, NDArray &x, NDArray &y, NDArray &z) {
|
||||
dim3 launchDims = getLaunchDims("hamming");
|
||||
_hammingKernel<X, Z><<<launchDims.x,launchDims.y, launchDims.z, *context->getCudaStream()>>>(
|
||||
x.specialBuffer(), x.specialShapeInfo(), y.specialBuffer(), y.specialShapeInfo(), z.specialBuffer(), nullptr,
|
||||
x.lengthOf());
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"_hammingKernel failed");
|
||||
|
||||
}
|
||||
|
||||
void hamming(LaunchContext *context, NDArray &x, NDArray &y, NDArray &output) {
|
||||
NDArray::prepareSpecialUse({&output}, {&x, &y});
|
||||
BUILD_DOUBLE_SELECTOR(x.dataType(), output.dataType(), _hamming, (context, x, y, output), SD_INTEGER_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&x, &y});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,137 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/hashcode.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static SD_KERNEL void splitBufferToChuncks(T* buffer, LongType* tempBuffer, LongType numBlocks, LongType blockSize,
|
||||
LongType length) {
|
||||
for (int b = blockIdx.x * blockDim.x + threadIdx.x; b < numBlocks; b += gridDim.x * blockDim.x) {
|
||||
auto blockBuffer = buffer + b * numBlocks;
|
||||
|
||||
LongType r = 1LL;
|
||||
for (int e = 0; e < blockSize && e + (b * numBlocks) < length; e++) {
|
||||
auto v = longBytes<T>(blockBuffer[e]);
|
||||
r = 31LL * r + v;
|
||||
}
|
||||
|
||||
tempBuffer[b] = r;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void internalHash(LongType* tempBuffer, LongType* tempResult, LongType numBlocks, LongType blockSize,
|
||||
LongType lastLength) {
|
||||
for (int b = blockIdx.x * blockDim.x + threadIdx.x; b < numBlocks; b += gridDim.x * blockDim.x) {
|
||||
auto blockBuffer = tempBuffer + b * numBlocks;
|
||||
LongType r = 1LL;
|
||||
|
||||
for (LongType e = 0; e < blockSize && e + (b * numBlocks) < lastLength; e++) {
|
||||
auto v = longBytes<T>(blockBuffer[e]);
|
||||
r = 31LL * r + v;
|
||||
}
|
||||
|
||||
tempResult[b] = r;
|
||||
}
|
||||
}
|
||||
|
||||
static SD_KERNEL void lastStep(LongType* resultBuf, LongType* tempBufferA, LongType* tempResult, LongType length,
|
||||
LongType blockSize) {
|
||||
if (threadIdx.x == 0) {
|
||||
if (length <= blockSize)
|
||||
*resultBuf = *tempBufferA;
|
||||
else
|
||||
*resultBuf = *tempResult;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void hashCode_(LaunchContext* context, NDArray& array, NDArray& result) {
|
||||
auto blockSize = 32;
|
||||
auto stream = context->getCudaStream();
|
||||
array.syncToDevice();
|
||||
|
||||
NDArray::prepareSpecialUse({&result}, {&array});
|
||||
auto length = array.lengthOf();
|
||||
int numBlocks = length / blockSize + ((length % blockSize == 0) ? 0 : 1);
|
||||
auto tempA = NDArrayFactory::create<LongType>('c', {numBlocks}, context);
|
||||
auto tempB = NDArrayFactory::create<LongType>('c', {numBlocks / blockSize + 1}, context);
|
||||
|
||||
auto buffer = reinterpret_cast<T*>(array.specialBuffer()); // bufferAsT<T>();
|
||||
auto tempBufferA = reinterpret_cast<LongType*>(tempA.specialBuffer()); // bufferAsT<sd::LongType>();
|
||||
auto tempBufferB = reinterpret_cast<LongType*>(tempB.specialBuffer()); // bufferAsT<sd::LongType>();
|
||||
|
||||
dim3 launchDims = getHashCodeSplit(length,numBlocks);
|
||||
// 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
|
||||
splitBufferToChuncks<T><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(buffer, tempBuffer, numBlocks, blockSize, length);
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"splitBufferToChuncks failed");
|
||||
|
||||
// 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);
|
||||
|
||||
dim3 internalLaunchDims = getHashCodeInternal(numBlocks);
|
||||
internalHash<LongType>
|
||||
<<<internalLaunchDims.y,internalLaunchDims.x, internalLaunchDims.z, *stream>>>(tempBuffer, tempResult, numBlocks, blockSize, lastLength);
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"internalHash failed");
|
||||
|
||||
iterationCount++;
|
||||
// swapping buffers
|
||||
if (iterationCount % 2 == 0) {
|
||||
tempBuffer = tempBufferA;
|
||||
tempResult = tempBufferB;
|
||||
} else {
|
||||
tempBuffer = tempBufferB;
|
||||
tempResult = tempBufferA;
|
||||
}
|
||||
}
|
||||
|
||||
dim3 lastDims = getLaunchDims("hashcode_last");
|
||||
lastStep<<<lastDims.x, lastDims.y, lastDims.z, *stream>>>(reinterpret_cast<LongType*>(result.specialBuffer()), tempBufferA, tempResult,
|
||||
length, blockSize);
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"lastStep failed");
|
||||
|
||||
delete tempA;
|
||||
delete tempB;
|
||||
NDArray::registerSpecialUse({&result}, {&array});
|
||||
}
|
||||
|
||||
void hashCode(LaunchContext* context, NDArray& array, NDArray& result) {
|
||||
BUILD_SINGLE_SELECTOR(array.dataType(), hashCode_, (context, array, result), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void hashCode_, (LaunchContext * context, NDArray& array, NDArray& result),
|
||||
SD_COMMON_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 raver119@gmail.com
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <ops/declarable/helpers/histogram.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename X, typename Z>
|
||||
static void SD_KERNEL histogramKernel(void *xBuffer, const LongType *xShapeInfo, void *zBuffer,
|
||||
const LongType *zShapeInfo, void *allocationPointer, void *reductionPointer,
|
||||
LongType numBins, X *min_val, X *max_val) {
|
||||
int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto dx = reinterpret_cast<X *>(xBuffer);
|
||||
auto result = reinterpret_cast<Z *>(zBuffer);
|
||||
|
||||
__shared__ Z *bins;
|
||||
__shared__ int length;
|
||||
__shared__ Z *reductor;
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
bins = (Z *)shmem;
|
||||
reductor = ((Z *)allocationPointer) + (numBins * blockIdx.x);
|
||||
|
||||
length = shape::length(xShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
X binSize = X((*max_val - *min_val) / numBins);
|
||||
|
||||
// nullify bins
|
||||
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
|
||||
bins[e] = (Z)0;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (int e = tid; e < length; e += blockDim.x * gridDim.x) {
|
||||
int idx = int((dx[e] - *min_val) / binSize);
|
||||
idx = math::sd_max(idx, 0); // atomicMax(&idx, 0);//atomicMax(&idx, 0);
|
||||
idx = math::sd_min(idx, int(numBins - 1)); // atomicMin(&idx, int(numBins - 1));
|
||||
math::atomics::sd_atomicAdd<Z>(&bins[idx], (Z)1);
|
||||
}
|
||||
__syncthreads();
|
||||
// at this point all bins in shared memory are calculated, so we aggregate them now via threadfence trick
|
||||
|
||||
// transfer shared memory to reduction memory
|
||||
if (gridDim.x > 1) {
|
||||
unsigned int *tc = (unsigned int *)reductionPointer;
|
||||
__shared__ bool amLast;
|
||||
|
||||
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
|
||||
reductor[e] = bins[e];
|
||||
}
|
||||
__threadfence();
|
||||
__syncthreads();
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
unsigned int ticket = atomicInc(&tc[16384], gridDim.x);
|
||||
amLast = (ticket == gridDim.x - 1);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (amLast) {
|
||||
tc[16384] = 0;
|
||||
|
||||
// nullify shared memory for future accumulation
|
||||
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
|
||||
bins[e] = (Z)0;
|
||||
}
|
||||
|
||||
// accumulate reduced bins
|
||||
for (int r = 0; r < gridDim.x; r++) {
|
||||
Z *ptrBuf = ((Z *)allocationPointer) + (r * numBins);
|
||||
|
||||
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
|
||||
math::atomics::sd_atomicAdd(&bins[e], ptrBuf[e]);
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// write them out to Z
|
||||
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
|
||||
result[e] = bins[e];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if there's only 1 block - just write away data
|
||||
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
|
||||
result[e] = bins[e];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void histogram_(LaunchContext *context, void *xBuffer, const LongType *xShapeInfo,
|
||||
const LongType *dxShapeInfo, void *zBuffer, const LongType *zShapeInfo, LongType numBins, void *min_val, void *max_val) {
|
||||
dim3 histogramDims = getHistogramDims(shape::length(xShapeInfo),numBins);
|
||||
int workspaceSize = histogramDims.x * numBins;
|
||||
auto tmp = NDArrayFactory::create<Z>('c', {workspaceSize}, context);
|
||||
|
||||
histogramKernel<X, Z><<<histogramDims.x, histogramDims.y, histogramDims.z, *context->getCudaStream()>>>(
|
||||
xBuffer, dxShapeInfo, zBuffer, zShapeInfo, tmp.specialBuffer(), context->getReductionPointer(), numBins,
|
||||
reinterpret_cast<X *>(min_val), reinterpret_cast<X *>(max_val));
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(),"histogramKernel failed");
|
||||
|
||||
cudaStreamSynchronize(*context->getCudaStream());
|
||||
}
|
||||
|
||||
void histogramHelper(LaunchContext *context, NDArray &input, NDArray &output) {
|
||||
LongType numBins = output.lengthOf();
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
|
||||
auto min_val = input.reduceNumber(reduce::SameOps::Min);
|
||||
auto max_val = input.reduceNumber(reduce::SameOps::Max);
|
||||
BUILD_DOUBLE_SELECTOR(
|
||||
input.dataType(), output.dataType(), histogram_,
|
||||
(context, input.specialBuffer(), input.shapeInfo(), input.specialShapeInfo(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), numBins, min_val.specialBuffer(), max_val.specialBuffer()),
|
||||
SD_COMMON_TYPES, SD_INTEGER_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,136 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/histogramFixedWidth.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
SD_KERNEL static void histogramFixedWidthCuda(const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const X leftEdge, const X rightEdge) {
|
||||
const auto x = reinterpret_cast<const X*>(vx);
|
||||
auto z = reinterpret_cast<Z*>(vz);
|
||||
|
||||
// Shared memory caching
|
||||
__shared__ LongType xLen, zLen, totalThreads, nbins;
|
||||
__shared__ X binWidth, secondEdge, lastButOneEdge;
|
||||
__shared__ LongType xRank, zRank;
|
||||
__shared__ const LongType* xShapePtr;
|
||||
__shared__ const LongType* xStridePtr;
|
||||
__shared__ const LongType* zShapePtr;
|
||||
__shared__ const LongType* zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(xShapeInfo);
|
||||
nbins = shape::length(zShapeInfo); // nbins = zLen
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
binWidth = (rightEdge - leftEdge) / nbins;
|
||||
secondEdge = leftEdge + binWidth;
|
||||
lastButOneEdge = rightEdge - binWidth;
|
||||
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < xLen; i += totalThreads) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
|
||||
// Use cached rank and shape to compute coordinates and offset for x
|
||||
INDEX2COORDS(i, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
|
||||
|
||||
const X value = x[xOffset];
|
||||
|
||||
// Determine the histogram bin index
|
||||
LongType zIndex;
|
||||
if (value < secondEdge)
|
||||
zIndex = 0;
|
||||
else if (value >= lastButOneEdge)
|
||||
zIndex = nbins - 1;
|
||||
else
|
||||
zIndex = static_cast<LongType>((value - leftEdge) / binWidth);
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
|
||||
// Use cached rank and shape to compute coordinates and offset for z
|
||||
INDEX2COORDS(zIndex, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
|
||||
|
||||
// Atomic addition to the histogram bin
|
||||
math::atomics::sd_atomicAdd<Z>(&z[zOffset], 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
SD_HOST static void histogramFixedWidthCudaLauncher(const cudaStream_t* stream, NDArray& input,
|
||||
NDArray& range, NDArray& output) {
|
||||
const X leftEdge = range.e<X>(0);
|
||||
const X rightEdge = range.e<X>(1);
|
||||
|
||||
dim3 launchDims = getLaunchDims("histogram_fixed_width");
|
||||
histogramFixedWidthCuda<X, Z><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(input.specialBuffer(), input.specialShapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo(),
|
||||
leftEdge, rightEdge);
|
||||
DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream),"histogramKernel failed");
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
void histogramFixedWidth(LaunchContext* context, NDArray& input, NDArray& range, NDArray& output) {
|
||||
// firstly initialize output with zeros
|
||||
output.nullify();
|
||||
|
||||
PointersManager manager(context, "histogramFixedWidth");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), output.dataType(), histogramFixedWidthCudaLauncher,
|
||||
(context->getCudaStream(), input, range, output), SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,125 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/im2col.h>
|
||||
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// input [bS, iC, iH, iW] is convoluted to output [bS, iC, kH, kW, oH, oW]
|
||||
template <typename T>
|
||||
SD_KERNEL static void im2colCuda(const void *image, void *columns, const LongType *imShapeInfo,
|
||||
const LongType *colShapeInfo, const LongType sH, const LongType sW, const LongType pH,
|
||||
const LongType pW, const LongType dH, const LongType dW, const double zeroPadValD) {
|
||||
T zeroPadVal = static_cast<T>(zeroPadValD); // Value to use when value is padding
|
||||
const auto im = reinterpret_cast<const T *>(image);
|
||||
auto col = reinterpret_cast<T *>(columns);
|
||||
|
||||
// Shared memory caching
|
||||
__shared__ LongType colLen, imLen, iH, iW;
|
||||
__shared__ LongType imRank, colRank;
|
||||
__shared__ const LongType *imShapePtr, *imStridePtr;
|
||||
__shared__ const LongType *colShapePtr, *colStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
colRank = 6;
|
||||
imRank = 4;
|
||||
|
||||
colLen = shape::length(colShapeInfo);
|
||||
imLen = shape::length(imShapeInfo);
|
||||
|
||||
iH = shape::shapeOf(imShapeInfo)[2];
|
||||
iW = shape::shapeOf(imShapeInfo)[3];
|
||||
|
||||
imShapePtr = shape::shapeOf(imShapeInfo);
|
||||
imStridePtr = shape::stride(imShapeInfo);
|
||||
|
||||
colShapePtr = shape::shapeOf(colShapeInfo);
|
||||
colStridePtr = shape::stride(colShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto colInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (colInd >= colLen) return; // Boundary check for threads
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
// Calculate coordinates and offsets
|
||||
INDEX2COORDS(colInd, colRank, colShapePtr, coords);
|
||||
|
||||
LongType colOffset;
|
||||
COORDS2INDEX(colRank, colStridePtr, coords, colOffset);
|
||||
|
||||
coords[2] = (-pH + coords[2] * dH) + coords[4] * sH; // imH
|
||||
coords[3] = (-pW + coords[3] * dW) + coords[5] * sW; // imW
|
||||
|
||||
// Check bounds and assign appropriate values
|
||||
if (coords[2] >= iH || coords[3] >= iW || coords[2] < 0 || coords[3] < 0) {
|
||||
if (colOffset < colLen)
|
||||
col[colOffset] = zeroPadVal;
|
||||
} else {
|
||||
LongType imOffset;
|
||||
COORDS2INDEX(imRank, imStridePtr, coords, imOffset);
|
||||
if (imOffset < imLen && colOffset < colLen)
|
||||
col[colOffset] = im[imOffset];
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void im2colCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMemory,
|
||||
LaunchContext &context, const void *image, void *columns,
|
||||
const LongType *imShapeInfo, const LongType *colShapeInfo, LongType sH,
|
||||
LongType sW, LongType pH, LongType pW, LongType dH, LongType dW, double zeroPadVal) {
|
||||
im2colCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMemory /* rank of columns = 6 */, *context.getCudaStream()>>>(
|
||||
image, columns, imShapeInfo, colShapeInfo, sH, sW, pH, pW, dH, dW, zeroPadVal);
|
||||
DebugHelper::checkErrorCode(context.getCudaStream(), "im2colCuda(...) failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void im2col(LaunchContext &context, NDArray&image, NDArray &columns, 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) {
|
||||
PointersManager manager(&context, "im2col");
|
||||
|
||||
dim3 im2colDevs = getim2ColLaunchParams(columns);
|
||||
NDArray::prepareSpecialUse({&columns}, {&image});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
columns.dataType(), im2colCudaLauncher,
|
||||
(im2colDevs.x, im2colDevs.y,im2colDevs.z, context, image.specialBuffer(), columns.specialBuffer(),
|
||||
image.specialShapeInfo(), columns.specialShapeInfo(), sH, sW, pH, pW, dH, dW, arrZeroPadVal.e<double>(0)),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&columns}, {&image});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,194 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <system/op_boilerplate.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
typedef NDArray ColorTable_t;
|
||||
static NDArray DefaultColorTable(int depth, LaunchContext* context) {
|
||||
// std::vector<std::vector<float>> colorTable;
|
||||
const LongType kDefaultTableLength = 10;
|
||||
const LongType kDefaultChannelLength = 4;
|
||||
std::vector<sd::LongType> shape = {kDefaultTableLength, kDefaultChannelLength};
|
||||
std::vector<double> table = {
|
||||
1, 1, 0, 1, // yellow
|
||||
0, 0, 1, 1, // 1: blue
|
||||
1, 0, 0, 1, // 2: red
|
||||
0, 1, 0, 1, // 3: lime
|
||||
0.5, 0, 0.5, 1, // 4: purple
|
||||
0.5, 0.5, 0, 1, // 5: olive
|
||||
0.5, 0, 0, 1, // 6: maroon
|
||||
0, 0, 0.5, 1, // 7: navy blue
|
||||
0, 1, 1, 1, // 8: aqua
|
||||
1, 0, 1, 1 // 9: fuchsia
|
||||
};
|
||||
NDArray colorTable('c', shape,
|
||||
table,
|
||||
FLOAT32, context);
|
||||
|
||||
if (depth == 1) {
|
||||
float one = 1.f;
|
||||
colorTable.assign(one); // all to white when black and white colors
|
||||
}
|
||||
return colorTable;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void drawBoundingBoxesKernel(T const* images, const LongType* imagesShape, float const* boxes,
|
||||
const LongType* boxesShape, float const* colorTable,
|
||||
const LongType* colorTableShape, T* output,
|
||||
const LongType* outputShape,
|
||||
LongType batchSize, LongType width, LongType height, LongType channels,
|
||||
LongType boxSize, LongType colorTableLen) {
|
||||
for (auto batch = blockIdx.x; batch < (int)batchSize; batch += gridDim.x) { // loop by batch
|
||||
for (auto boxIndex = 0; boxIndex < boxSize; ++boxIndex) {
|
||||
auto colorIndex = boxIndex % colorTableLen; // colorSet->at(c);
|
||||
LongType indices0[] = {batch, boxIndex, 0};
|
||||
LongType indices1[] = {batch, boxIndex, 1};
|
||||
LongType indices2[] = {batch, boxIndex, 2};
|
||||
LongType indices3[] = {batch, boxIndex, 3};
|
||||
|
||||
LongType rowStartOffset, rowEndOffset, colStartOffset, colEndOffset;
|
||||
COORDS2INDEX(3, shape::stride(boxesShape), indices0, rowStartOffset);
|
||||
COORDS2INDEX(3, shape::stride(boxesShape), indices2, rowEndOffset);
|
||||
COORDS2INDEX(3,shape::stride(boxesShape), indices1, colStartOffset);
|
||||
COORDS2INDEX(3, shape::stride(boxesShape), indices3, colEndOffset);
|
||||
|
||||
auto rowStart = LongType((height - 1) * boxes[rowStartOffset]);
|
||||
auto rowStartBound = math::sd_max(LongType(0), rowStart);
|
||||
auto rowEnd = LongType((height - 1) * boxes[rowEndOffset]);
|
||||
auto rowEndBound = math::sd_min(LongType(height - 1), rowEnd);
|
||||
auto colStart = LongType((width - 1) * boxes[colStartOffset]);
|
||||
auto colStartBound = math::sd_max(LongType(0), colStart);
|
||||
auto colEnd = LongType((width - 1) * boxes[colEndOffset]);
|
||||
auto colEndBound = math::sd_min(LongType(width - 1), colEnd);
|
||||
|
||||
if (rowStart > rowEnd || colStart > colEnd) {
|
||||
continue;
|
||||
}
|
||||
if (rowStart >= height || rowEnd < 0 || colStart >= width || colEnd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw upper line
|
||||
if (rowStart >= 0) {
|
||||
for (auto j = colStartBound + threadIdx.x; j <= colEndBound; j += blockDim.x)
|
||||
for (auto c = 0; c < channels; c++) {
|
||||
LongType zPos[] = {batch, rowStart, j, c};
|
||||
LongType cPos[] = {colorIndex, c};
|
||||
LongType cIndex, zIndex;
|
||||
COORDS2INDEX(2, shape::stride(colorTableShape), cPos, cIndex);
|
||||
COORDS2INDEX(4, shape::stride(outputShape), zPos, zIndex);
|
||||
output[zIndex] = (T)colorTable[cIndex];
|
||||
}
|
||||
}
|
||||
// Draw bottom line.
|
||||
if (rowEnd < height) {
|
||||
for (auto j = colStartBound + threadIdx.x; j <= colEndBound; j += blockDim.x)
|
||||
for (auto c = 0; c < channels; c++) {
|
||||
LongType zPos[] = {batch, rowEnd, j, c};
|
||||
LongType cPos[] = {colorIndex, c};
|
||||
LongType cIndex, zIndex;
|
||||
COORDS2INDEX(2, shape::stride(colorTableShape), cPos, cIndex);
|
||||
COORDS2INDEX(4, shape::stride(outputShape), zPos, zIndex);
|
||||
output[zIndex] = (T)colorTable[cIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// Draw left line.
|
||||
if (colStart >= 0) {
|
||||
for (auto i = rowStartBound + threadIdx.x; i <= rowEndBound; i += blockDim.x)
|
||||
for (auto c = 0; c < channels; c++) {
|
||||
LongType zPos[] = {batch, i, colStart, c};
|
||||
LongType cPos[] = {colorIndex, c};
|
||||
LongType cIndex, zIndex;
|
||||
COORDS2INDEX(2, shape::stride(colorTableShape), cPos, cIndex);
|
||||
COORDS2INDEX(4, shape::stride(outputShape), zPos, zIndex);
|
||||
output[zIndex] = (T)colorTable[cIndex];
|
||||
}
|
||||
}
|
||||
// Draw right line.
|
||||
if (colEnd < width) {
|
||||
for (auto i = rowStartBound + threadIdx.x; i <= rowEndBound; i += blockDim.x)
|
||||
for (auto c = 0; c < channels; c++) {
|
||||
LongType zPos[] = {batch, i, colEnd, c};
|
||||
LongType cPos[] = {colorIndex, c};
|
||||
LongType cIndex, zIndex;
|
||||
COORDS2INDEX(2, shape::stride(colorTableShape), cPos, cIndex);
|
||||
COORDS2INDEX(4, shape::stride(outputShape), zPos, zIndex);
|
||||
output[zIndex] = (T)colorTable[cIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void drawBoundingBoxesH(LaunchContext* context, NDArray * images, NDArray * boxes, NDArray * colors,
|
||||
NDArray* output) {
|
||||
auto batchSize = images->sizeAt(0);
|
||||
auto height = images->sizeAt(1);
|
||||
auto width = images->sizeAt(2);
|
||||
auto channels = images->sizeAt(3);
|
||||
auto stream = context->getCudaStream();
|
||||
auto boxSize = boxes->sizeAt(1);
|
||||
NDArray colorsTable = DefaultColorTable(channels, context);
|
||||
if ((colors != nullptr && colors->lengthOf() > 0)) {
|
||||
colorsTable = *colors;
|
||||
}
|
||||
|
||||
auto imagesBuf = images->getDataBuffer()->specialAsT<T>();
|
||||
auto boxesBuf = boxes->getDataBuffer()->specialAsT<float>(); // boxes should be float32
|
||||
auto colorsTableBuf = colorsTable.getDataBuffer()->specialAsT<float>(); // color table is float32
|
||||
auto outputBuf = output->dataBuffer()->specialAsT<T>();
|
||||
dim3 launchDims = getLaunchDims("draw_bounding_boxes");
|
||||
drawBoundingBoxesKernel<<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
imagesBuf, images->specialShapeInfo(), boxesBuf, boxes->specialShapeInfo(), colorsTableBuf,
|
||||
colorsTable.specialShapeInfo(), outputBuf, output->specialShapeInfo(), batchSize, width, height, channels,
|
||||
boxSize, colorsTable.lengthOf());
|
||||
}
|
||||
|
||||
void drawBoundingBoxesFunctor(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
|
||||
NDArray::prepareSpecialUse({output}, {images, boxes, colors});
|
||||
output->assign(images);
|
||||
BUILD_SINGLE_SELECTOR(output->dataType(), drawBoundingBoxesH, (context, images, boxes, colors, output),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {images, boxes, colors});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,396 @@
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/image_resize.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
static SD_INLINE SD_HOST_DEVICE LongType boundsAmp(LongType const low, LongType const high, LongType const value) {
|
||||
if (high < value) return high;
|
||||
if (value < low) return low;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename TKernelFunc>
|
||||
static SD_KERNEL void computeSpansKernel(TKernelFunc* kernel, int* startsVec, float* weightsVector, LongType outSize,
|
||||
LongType inSize, float kernelScale, int spanSize,
|
||||
float const invScale, float const invTranslate, float invKernelScale,
|
||||
float* tempWeightsBuf) {
|
||||
// return value if within bounds or bounds otherwise
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
__shared__ int maxSpanSize;
|
||||
|
||||
if (threadIdx.x == 0 && blockIdx.x == 0) {
|
||||
maxSpanSize = 0;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto x = tid; x < outSize; x += step) {
|
||||
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;
|
||||
}
|
||||
LongType spanStart = math::sd_ceil<float, float>(sampleFloat - kernel->radius() * kernelScale - 0.5f);
|
||||
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 > spanSize) {
|
||||
return;
|
||||
}
|
||||
float totalWeightSum = 0.f;
|
||||
auto tempWeights = &tempWeightsBuf[x];
|
||||
auto actualWeights = 0;
|
||||
for (int source = spanStart; source < spanEnd; ++source) {
|
||||
float kernelPos = static_cast<float>(source) + 0.5f - sampleFloat;
|
||||
float weight = (*kernel)(kernelPos * invKernelScale);
|
||||
totalWeightSum += weight;
|
||||
tempWeights[actualWeights++] = weight;
|
||||
}
|
||||
maxSpanSize = math::sd_max(maxSpanSize, spanSize);
|
||||
if (math::sd_abs<float,float>(totalWeightSum) >= 1000.f * DataTypeUtils::min<float>()) { //
|
||||
auto totalWeightSumInverted = 1.0f / totalWeightSum;
|
||||
auto outIndex = spanSize * x;
|
||||
for (auto weightIndex = 0; weightIndex < actualWeights; ++weightIndex) {
|
||||
weightsVector[outIndex] = tempWeights[weightIndex] * totalWeightSumInverted;
|
||||
++outIndex;
|
||||
}
|
||||
}
|
||||
startsVec[x] = spanStart;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TKernelFunc>
|
||||
static Status computeSpans(LaunchContext* context, TKernelFunc& kernel, LongType const outSize, 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));
|
||||
spans._starts = NDArrayFactory::create<int>('c', {outSize});
|
||||
spans._starts.syncToHost();
|
||||
spans._weights = NDArrayFactory::create<float>('c', {outSize, spans._spanSize});
|
||||
spans._weights.syncToHost();
|
||||
|
||||
auto startsVec = reinterpret_cast<int*>(spans._starts.buffer());
|
||||
auto weightsVector = reinterpret_cast<float*>(spans._weights.buffer());
|
||||
spans._weights.nullify();
|
||||
|
||||
const float invKernelScale = 1.f / kernelScale;
|
||||
auto stream = context->getCudaStream();
|
||||
auto maxSpanSize = 0;
|
||||
std::vector<float> tempWeights;
|
||||
for (auto x = 0; 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;
|
||||
}
|
||||
LongType spanStart = math::sd_ceil<float, float>(sampleFloat - kernel.radius() * kernelScale - 0.5f);
|
||||
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 = math::sd_max(maxSpanSize, spanSize);
|
||||
if (math::sd_abs<float,float>(totalWeightSum) >= 1000.f * DataTypeUtils::min<float>()) { //
|
||||
auto totalWeightSumInverted = 1.0f / totalWeightSum;
|
||||
auto outIndex = spans._spanSize * x;
|
||||
for (auto weightIndex = 0; weightIndex < tempWeights.size(); ++weightIndex) {
|
||||
weightsVector[outIndex++] = tempWeights[weightIndex] * totalWeightSumInverted;
|
||||
}
|
||||
}
|
||||
startsVec[x] = spanStart;
|
||||
}
|
||||
spans._starts.tickWriteHost();
|
||||
spans._weights.tickWriteHost();
|
||||
spans._starts.syncToDevice();
|
||||
spans._weights.syncToDevice();
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
||||
template <typename X, typename Z>
|
||||
static SD_KERNEL void batchedGatherSpan(LongType outputWidth, LongType outputHeight, int rowSpanSize,
|
||||
int const* rowStartsBuf, Z const* rowWeightBuf, int columnSpanSize,
|
||||
int const* columnStartsBuf, Z const* columnWeightBuf, X const* pImages,
|
||||
const LongType* imageSpecialShapeInfo, Z* pIntermediate, Z* pOutput,
|
||||
LongType outputPixPerBatch) {
|
||||
auto batchSize = shape::sizeAt(imageSpecialShapeInfo, 0);
|
||||
auto inputHeight = shape::sizeAt(imageSpecialShapeInfo, 1);
|
||||
auto inputWidth = shape::sizeAt(imageSpecialShapeInfo, 2);
|
||||
auto channels = shape::sizeAt(imageSpecialShapeInfo, 3);
|
||||
bool inputEws1 = shape::elementWiseStride(imageSpecialShapeInfo) == 1;
|
||||
auto inputPixPerBatch = shape::strideAt(imageSpecialShapeInfo, 0);
|
||||
auto inRowStride = shape::strideAt(imageSpecialShapeInfo, 1);
|
||||
auto wStride = shape::strideAt(imageSpecialShapeInfo, 2);
|
||||
auto cStride = shape::strideAt(imageSpecialShapeInfo, 3);
|
||||
auto intermediatePixPerBatch = inputWidth * outputHeight * channels;
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
for (int b = tid; b < batchSize; b += step) {
|
||||
auto imagePtr = pImages + b * inputPixPerBatch;
|
||||
auto intermediatePtr = pIntermediate + b * intermediatePixPerBatch;
|
||||
auto outputPtr = pOutput + b * outputPixPerBatch;
|
||||
gatherRows<X, Z>(rowSpanSize, rowStartsBuf, rowWeightBuf, imagePtr, inputHeight, inputWidth, outputHeight,
|
||||
inputWidth, channels, intermediatePtr, inputEws1, inRowStride, wStride, cStride);
|
||||
gatherColumns<Z>(columnSpanSize, columnStartsBuf, columnWeightBuf, intermediatePtr, outputHeight, inputWidth,
|
||||
outputHeight, outputWidth, channels, outputPtr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void gatherSpans(LaunchContext* context, int const rowSpanSize, NDArray& rowStarts,
|
||||
NDArray& rowWeights, int const colSpanSize, NDArray& columnStarts,
|
||||
NDArray& columnWeights, NDArray * images, NDArray& intermediate, NDArray* output) {
|
||||
const auto imageSpecialShapeInfo = images->specialShapeInfo();
|
||||
auto outputHeight = output->sizeAt(1);
|
||||
auto outputWidth = output->sizeAt(2);
|
||||
auto channels = images->sizeAt(3);
|
||||
auto outputPixPerBatch = outputWidth * outputHeight * channels;
|
||||
auto intermediatePtr = reinterpret_cast<Z*>(intermediate.specialBuffer());
|
||||
|
||||
auto imagePtr = reinterpret_cast<X const*>(images->specialBuffer());
|
||||
auto outputPtr = reinterpret_cast<Z*>(output->specialBuffer());
|
||||
auto stream = context->getCudaStream();
|
||||
auto rowStartsBuf = reinterpret_cast<int const*>(rowStarts.specialBuffer());
|
||||
auto rowWeightBuf = reinterpret_cast<Z const*>(rowWeights.specialBuffer());
|
||||
auto columnStartsBuf = reinterpret_cast<int const*>(columnStarts.specialBuffer());
|
||||
auto columnWeightBuf = reinterpret_cast<Z const*>(columnWeights.specialBuffer());
|
||||
dim3 launchDims = getLaunchDims("image_resize_v2_gather");
|
||||
batchedGatherSpan<X, Z><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
outputWidth, outputHeight, rowSpanSize, rowStartsBuf, rowWeightBuf, colSpanSize, columnStartsBuf, columnWeightBuf,
|
||||
imagePtr, imageSpecialShapeInfo, intermediatePtr, outputPtr, outputPixPerBatch);
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static Status resizeKernel(LaunchContext* context, ImageResizeMethods method, NDArray * input, LongType outWidth,
|
||||
LongType outHeight, bool antialias, double coefficient,
|
||||
NDArray* output) {
|
||||
LongType const batchSize = input->sizeAt(0);
|
||||
LongType const inputHeight = input->sizeAt(1);
|
||||
LongType const inputWidth = input->sizeAt(2);
|
||||
LongType const channels = input->sizeAt(3);
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
Z rowScale = Z(outHeight) / Z(inputHeight);
|
||||
Z columnScale = Z(outWidth) / Z(inputWidth);
|
||||
|
||||
// Return if the output is empty.
|
||||
if (output->lengthOf() == 0) return Status::OK;
|
||||
|
||||
Spans colSpans;
|
||||
Spans rowSpans;
|
||||
auto res = Status::OK;
|
||||
switch (method) {
|
||||
case kResizeBilinear: {
|
||||
TriangleKernelFunc kernel;
|
||||
res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, colSpans);
|
||||
if (res != Status::OK) return res;
|
||||
res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans);
|
||||
|
||||
} break;
|
||||
case kResizeBicubic: {
|
||||
KeysCubicKernelFunc<float> kernel(static_cast<float>(coefficient));
|
||||
res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, colSpans);
|
||||
if (res != Status::OK) return res;
|
||||
res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans);
|
||||
} break;
|
||||
case kResizeLanczos3: {
|
||||
LanczosKernelFunc kernel(3.f);
|
||||
res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, colSpans);
|
||||
if (res != Status::OK) return res;
|
||||
res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans);
|
||||
|
||||
} break;
|
||||
|
||||
case kResizeLanczos5: {
|
||||
LanczosKernelFunc kernel(5.f);
|
||||
res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, colSpans);
|
||||
if (res != Status::OK) return res;
|
||||
res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans);
|
||||
|
||||
} break;
|
||||
case kResizeGaussian: {
|
||||
GaussianKernelFunc kernel;
|
||||
res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, colSpans);
|
||||
if (res != Status::OK) return res;
|
||||
res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans);
|
||||
|
||||
} break;
|
||||
case kResizeMitchellcubic: {
|
||||
MitchellCubicKernelFunc kernel;
|
||||
res = computeSpans(context, kernel, outWidth, inputWidth, columnScale, 0.f, antialias, colSpans);
|
||||
if (res != Status::OK) return res;
|
||||
res = computeSpans(context, kernel, outHeight, inputHeight, rowScale, 0.f, antialias, rowSpans);
|
||||
|
||||
} break;
|
||||
};
|
||||
|
||||
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>(context, rowSpans._spanSize, rowStarts, rowWeights, colSpans._spanSize, columnStarts, columnWeights,
|
||||
input, intermediate, output);
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
return res;
|
||||
}
|
||||
|
||||
#if defined(HAS_FLOAT32)
|
||||
#define SD_FLOAT_TYPES_FLOAT32 SKIP_FIRST_COMMA(TTYPE_FLOAT32)
|
||||
|
||||
static Status resizeTriangle(LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(context, kResizeBilinear, image, width, height, antialias, 0, output), SD_NUMERIC_TYPES,
|
||||
SD_FLOAT_TYPES_FLOAT32);
|
||||
return Logger::logStatusMsg(Status::VALIDATION,
|
||||
"helpers::resizeTriangle: This resize method is avaliable in future versions");
|
||||
}
|
||||
|
||||
static Status resizeLanczos3(LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(context, kResizeLanczos3, image, width, height, antialias, 0, output), SD_NUMERIC_TYPES,
|
||||
SD_FLOAT_TYPES_FLOAT32);
|
||||
return Logger::logStatusMsg(Status::VALIDATION,
|
||||
"helpers::resizeLanczos3: This resize method is avaliable in future versions");
|
||||
}
|
||||
|
||||
static Status resizeLanczos5(LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(context, kResizeLanczos5, image, width, height, antialias, 0, output), SD_NUMERIC_TYPES,
|
||||
SD_FLOAT_TYPES_FLOAT32);
|
||||
return Logger::logStatusMsg(Status::VALIDATION,
|
||||
"helpers::resizeLanczos5: This resize method is avaliable in future versions");
|
||||
}
|
||||
|
||||
static Status resizeGaussian(LaunchContext* context, NDArray * image, int const width, int const height,
|
||||
bool const antialias, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(context, kResizeGaussian, image, width, height, antialias, 0, output), SD_NUMERIC_TYPES,
|
||||
SD_FLOAT_TYPES_FLOAT32);
|
||||
return Logger::logStatusMsg(Status::VALIDATION,
|
||||
"helpers::resizeGaussian: This resize method is avaliable in future versions");
|
||||
}
|
||||
static Status resizeMitchellcubic(LaunchContext* context, NDArray * image, int const width,
|
||||
int const height, bool const antialias, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(context, kResizeMitchellcubic, image, width, height, antialias, 0, output), SD_NUMERIC_TYPES,
|
||||
SD_FLOAT_TYPES_FLOAT32);
|
||||
return Logger::logStatusMsg(Status::VALIDATION,
|
||||
"helpers::ResizeMitchellcubic: This resize method is avaliable in future versions");
|
||||
}
|
||||
|
||||
static Status resizeBicubicA(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 Status resizeBicubicAntialias(LaunchContext* context, NDArray * image, int const width,
|
||||
int const height, bool const antialias, double coefficient, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(image->dataType(), output->dataType(), return resizeKernel,
|
||||
(context, kResizeBicubic, image, width, height, antialias, coefficient, output),
|
||||
SD_NUMERIC_TYPES, SD_FLOAT_TYPES_FLOAT32);
|
||||
return Logger::logStatusMsg(Status::VALIDATION,
|
||||
"helpers::ResizeMitchellcubic: This resize method is avaliable in future versions");
|
||||
}
|
||||
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Status resizeFunctor(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 resizeAreaFunctor(context, image, width, height, false, output);
|
||||
|
||||
#if defined(HAS_FLOAT32)
|
||||
case kResizeBilinear:
|
||||
return resizeTriangle(context, image, width, height, antialias, 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
#else
|
||||
case kResizeBilinear:
|
||||
case kResizeLanczos3:
|
||||
case kResizeLanczos5:
|
||||
case kResizeGaussian:
|
||||
case kResizeMitchellcubic:
|
||||
case kResizeBicubic: {
|
||||
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
|
||||
default:
|
||||
sd_printf("helper::resizeFunctor: Wrong resize method %i\n", (int)method);
|
||||
THROW_EXCEPTION("helper::resizeFunctor: Wrong resize method.");
|
||||
}
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,551 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <legacy/NativeOps.h>
|
||||
#include <ops/declarable/helpers/image_suppression.h>
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// needToSuppressWithThreshold - predicate for suppression
|
||||
// boxes - boxes tensor buffer
|
||||
// boxesShape boxes tensor shape
|
||||
// previousIndex - index for current pos value
|
||||
// nextIndex - index for neighbor pos value
|
||||
// threshold - threashold value to suppress
|
||||
//
|
||||
// return value: true, if threshold is overcome, false otherwise
|
||||
//
|
||||
template <typename T>
|
||||
static SD_DEVICE bool needToSuppressWithThreshold(T* boxes, LongType const* boxesShape, int previousIndex,
|
||||
int nextIndex, T threshold) {
|
||||
LongType previous0[] = {previousIndex, 0};
|
||||
LongType previous1[] = {previousIndex, 1};
|
||||
LongType previous2[] = {previousIndex, 2};
|
||||
LongType previous3[] = {previousIndex, 3};
|
||||
LongType next0[] = {nextIndex, 0};
|
||||
LongType next1[] = {nextIndex, 1};
|
||||
LongType next2[] = {nextIndex, 2};
|
||||
LongType next3[] = {nextIndex, 3};
|
||||
|
||||
LongType prevOffset0, prevOffset1, prevOffset2, prevOffset3;
|
||||
LongType nextOffset0, nextOffset1, nextOffset2, nextOffset3;
|
||||
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), previous0, prevOffset0);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), previous1, prevOffset1);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), previous2, prevOffset2);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), previous3, prevOffset3);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), next0, nextOffset0);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), next1, nextOffset1);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), next2, nextOffset2);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), next3, nextOffset3);
|
||||
|
||||
// we have rectangle with given max values. Compute vexes of rectangle first
|
||||
|
||||
T minYPrev = math::sd_min(boxes[prevOffset0], boxes[prevOffset2]);
|
||||
T minXPrev = math::sd_min(boxes[prevOffset1], boxes[prevOffset3]);
|
||||
T maxYPrev = math::sd_max(boxes[prevOffset0], boxes[prevOffset2]);
|
||||
T maxXPrev = math::sd_max(boxes[prevOffset1], boxes[prevOffset3]);
|
||||
T minYNext = math::sd_min(boxes[nextOffset0], boxes[nextOffset2]);
|
||||
T minXNext = math::sd_min(boxes[nextOffset1], boxes[nextOffset3]);
|
||||
T maxYNext = math::sd_max(boxes[nextOffset0], boxes[nextOffset2]);
|
||||
T maxXNext = math::sd_max(boxes[nextOffset1], boxes[nextOffset3]);
|
||||
|
||||
// compute areas for comparation
|
||||
T areaPrev = (maxYPrev - minYPrev) * (maxXPrev - minXPrev);
|
||||
T areaNext = (maxYNext - minYNext) * (maxXNext - minXNext);
|
||||
|
||||
// of course, areas should be positive
|
||||
if (areaNext <= T(0.f) || areaPrev <= T(0.f)) return false;
|
||||
|
||||
// compute intersection of rectangles
|
||||
T minIntersectionY = math::sd_max(minYPrev, minYNext);
|
||||
T minIntersectionX = math::sd_max(minXPrev, minXNext);
|
||||
T maxIntersectionY = math::sd_min(maxYPrev, maxYNext);
|
||||
T maxIntersectionX = math::sd_min(maxXPrev, maxXNext);
|
||||
T intersectionArea = math::sd_max(T(maxIntersectionY - minIntersectionY), T(0.0f)) *
|
||||
math::sd_max(T(maxIntersectionX - minIntersectionX), T(0.0f));
|
||||
T intersectionValue = intersectionArea / (areaPrev + areaNext - intersectionArea);
|
||||
// final check
|
||||
return intersectionValue > threshold;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T similirityV3_(NDArray& boxes, LongType i, 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 SD_DEVICE T similirityV3(T* boxes, LongType const* boxesShape, int previousIndex, int nextIndex) {
|
||||
LongType previous0[] = {previousIndex, 0};
|
||||
LongType previous1[] = {previousIndex, 1};
|
||||
LongType previous2[] = {previousIndex, 2};
|
||||
LongType previous3[] = {previousIndex, 3};
|
||||
LongType next0[] = {nextIndex, 0};
|
||||
LongType next1[] = {nextIndex, 1};
|
||||
LongType next2[] = {nextIndex, 2};
|
||||
LongType next3[] = {nextIndex, 3};
|
||||
|
||||
LongType prevOffset0, prevOffset1, prevOffset2, prevOffset3;
|
||||
LongType nextOffset0, nextOffset1, nextOffset2, nextOffset3;
|
||||
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), previous0, prevOffset0);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), previous1, prevOffset1);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), previous2, prevOffset2);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), previous3, prevOffset3);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), next0, nextOffset0);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), next1, nextOffset1);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), next2, nextOffset2);
|
||||
COORDS2INDEX(2, shape::stride(boxesShape), next3, nextOffset3);
|
||||
|
||||
// we have rectangle with given max values. Compute vexes of rectangle first
|
||||
|
||||
T minYPrev = math::sd_min(boxes[prevOffset0], boxes[prevOffset2]);
|
||||
T minXPrev = math::sd_min(boxes[prevOffset1], boxes[prevOffset3]);
|
||||
T maxYPrev = math::sd_max(boxes[prevOffset0], boxes[prevOffset2]);
|
||||
T maxXPrev = math::sd_max(boxes[prevOffset1], boxes[prevOffset3]);
|
||||
T minYNext = math::sd_min(boxes[nextOffset0], boxes[nextOffset2]);
|
||||
T minXNext = math::sd_min(boxes[nextOffset1], boxes[nextOffset3]);
|
||||
T maxYNext = math::sd_max(boxes[nextOffset0], boxes[nextOffset2]);
|
||||
T maxXNext = math::sd_max(boxes[nextOffset1], boxes[nextOffset3]);
|
||||
|
||||
// compute areas for comparator
|
||||
T areaPrev = (maxYPrev - minYPrev) * (maxXPrev - minXPrev);
|
||||
T areaNext = (maxYNext - minYNext) * (maxXNext - minXNext);
|
||||
|
||||
// of course, areas should be positive
|
||||
if (areaNext <= T(0.f) || areaPrev <= T(0.f)) return false;
|
||||
|
||||
// compute intersection of rectangles
|
||||
T minIntersectionY = math::sd_max(minYPrev, minYNext);
|
||||
T minIntersectionX = math::sd_max(minXPrev, minXNext);
|
||||
T maxIntersectionY = math::sd_min(maxYPrev, maxYNext);
|
||||
T maxIntersectionX = math::sd_min(maxXPrev, maxXNext);
|
||||
T intersectionArea = math::sd_max(T(maxIntersectionY - minIntersectionY), T(0.0f)) *
|
||||
math::sd_max(T(maxIntersectionX - minIntersectionX), T(0.0f));
|
||||
T intersectionValue = intersectionArea / (areaPrev + areaNext - intersectionArea);
|
||||
// final check
|
||||
return intersectionValue;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// shouldSelectKernel - compute status for all selected rectangles (boxes)
|
||||
//
|
||||
// we compute boolean flag as shared uint32 and return it on final only for the first thread
|
||||
//
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void shouldSelectKernel(T* boxesBuf, LongType const* boxesShape, I* indexBuf,
|
||||
I* selectedIndicesData, double threshold, int numSelected, int i,
|
||||
bool* shouldSelect) {
|
||||
auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = gridDim.x * blockDim.x;
|
||||
__shared__ unsigned int shouldSelectShared;
|
||||
if (threadIdx.x == 0) {
|
||||
shouldSelectShared = (unsigned int)shouldSelect[0];
|
||||
}
|
||||
__syncthreads();
|
||||
for (int j = numSelected - 1 - tid; j >= 0; j -= step) {
|
||||
if (shouldSelectShared) {
|
||||
if (needToSuppressWithThreshold(boxesBuf, boxesShape, indexBuf[i], indexBuf[selectedIndicesData[j]],
|
||||
T(threshold)))
|
||||
atomicCAS(&shouldSelectShared, 1, 0); // exchange only when need to suppress
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// final move: collect result
|
||||
if (threadIdx.x == 0) {
|
||||
*shouldSelect = shouldSelectShared > 0;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// indices - type depended, indicesLong - type defined (only 64bit integers)
|
||||
//
|
||||
template <typename I>
|
||||
static SD_KERNEL void copyIndices(void* indices, void* indicesLong, LongType len) {
|
||||
I* indexBuf = reinterpret_cast<I*>(indices);
|
||||
LongType* srcBuf = reinterpret_cast<LongType*>(indicesLong);
|
||||
;
|
||||
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
for (auto i = tid; i < len; i += step) indexBuf[i] = (I)srcBuf[i];
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void suppressScores(T* scores, I* indices, LongType length, T scoreThreshold) {
|
||||
auto start = blockIdx.x * blockDim.x;
|
||||
auto step = gridDim.x * blockDim.x;
|
||||
|
||||
for (auto e = start + threadIdx.x; e < (int)length; e += step) {
|
||||
if (scores[e] < scoreThreshold) {
|
||||
scores[e] = scoreThreshold;
|
||||
indices[e] = -1;
|
||||
} else {
|
||||
indices[e] = I(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// nonMaxSuppressionV2 algorithm - given from TF NonMaxSuppressionV2 implementation
|
||||
//
|
||||
template <typename T, typename I>
|
||||
static void nonMaxSuppressionV2_(LaunchContext* context, NDArray* boxes, NDArray* scales, int maxSize,
|
||||
double threshold, double scoreThreshold, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray::prepareSpecialUse({output}, {boxes, scales});
|
||||
std::vector<sd::LongType> shape = {scales->lengthOf()};
|
||||
NDArray indices (NDArrayFactory::create_<I>(
|
||||
'c', shape, context)); // - 1, scales->lengthOf()); //, scales->getContext());
|
||||
|
||||
NDArray scores(*scales);
|
||||
Pointer extras[2] = {nullptr, stream};
|
||||
auto indexBuf = indices.dataBuffer()->specialAsT<I>();
|
||||
auto scoreBuf = scores.dataBuffer()->specialAsT<T>();
|
||||
dim3 launchDims = getLaunchDims("image_suppress_scores");
|
||||
suppressScores<T, I><<<launchDims.x, launchDims.y,launchDims.z, *stream>>>(scoreBuf, indexBuf, scores.lengthOf(), T(scoreThreshold));
|
||||
indices.tickWriteDevice();
|
||||
sortByValue(extras, &indices,
|
||||
&scores,true);
|
||||
indices.tickWriteDevice();
|
||||
NDArray selectedIndices = NDArrayFactory::create<I>('c', {output->lengthOf()}, context);
|
||||
int numSelected = 0;
|
||||
int numBoxes = boxes->sizeAt(0), tt(0);
|
||||
auto boxesBuf = reinterpret_cast<T*>(boxes->specialBuffer());
|
||||
|
||||
auto selectedIndicesData = reinterpret_cast<I*>(selectedIndices.specialBuffer());
|
||||
auto outputBuf = reinterpret_cast<I*>(output->specialBuffer());
|
||||
|
||||
bool* shouldSelectD;
|
||||
auto err = cudaMalloc(&shouldSelectD, sizeof(bool));
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::nonMaxSuppressionV2: Cannot allocate memory for bool flag", err);
|
||||
}
|
||||
for (I i = 0; i < boxes->sizeAt(0); ++i) {
|
||||
bool shouldSelect = numSelected < output->lengthOf();
|
||||
if (shouldSelect) {
|
||||
err = cudaMemcpy(shouldSelectD, &shouldSelect, sizeof(bool), cudaMemcpyHostToDevice);
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::nonMaxSuppressionV2: Cannot set up bool flag to device", err);
|
||||
}
|
||||
|
||||
dim3 selectDims = getLaunchDims("image_suppress_select");
|
||||
shouldSelectKernel<T, I><<<selectDims.y,selectDims.x,selectDims.z, *stream>>>(
|
||||
boxesBuf, boxes->specialShapeInfo(), indexBuf, selectedIndicesData, threshold, numSelected, i, shouldSelectD);
|
||||
err = cudaMemcpy(&shouldSelect, shouldSelectD, sizeof(bool), cudaMemcpyDeviceToHost);
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::nonMaxSuppressionV2: Cannot set up bool flag to host", err);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSelect) {
|
||||
cudaMemcpy(reinterpret_cast<I*>(output->specialBuffer()) + numSelected, indexBuf + i, sizeof(I),
|
||||
cudaMemcpyDeviceToDevice);
|
||||
cudaMemcpy(selectedIndicesData + numSelected, &i, sizeof(I), cudaMemcpyHostToDevice);
|
||||
numSelected++;
|
||||
}
|
||||
}
|
||||
|
||||
err = cudaFree(shouldSelectD);
|
||||
if (err) {
|
||||
throw cuda_exception::build("helpers::nonMaxSuppressionV2: Cannot deallocate memory for bool flag", err);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, typename I>
|
||||
static SD_DEVICE bool checkOverlapBoxes(T* boxes, LongType const* shape, T* scores, I* indices, I* selectedIndices,
|
||||
I* startIndices, I selectedSize, I nextCandidateIndex, T overlapThreshold,
|
||||
T scoreThreshold, bool simple) {
|
||||
bool shouldHardSuppress = false;
|
||||
T& nextCandidateScore = scores[nextCandidateIndex];
|
||||
I selectedIndex = indices[nextCandidateIndex];
|
||||
I finish = startIndices[nextCandidateIndex];
|
||||
|
||||
for (int j = selectedSize; j > finish; --j) {
|
||||
T boxVal;
|
||||
if (simple) {
|
||||
LongType xPos[] = {selectedIndex, selectedIndices[j - 1]};
|
||||
LongType xShift;
|
||||
COORDS2INDEX(shape::rank(shape), shape::stride(shape), xPos, xShift);
|
||||
boxVal = boxes[xShift];
|
||||
} else {
|
||||
boxVal = similirityV3(boxes, shape, selectedIndex, selectedIndices[j - 1]);
|
||||
}
|
||||
if (boxVal > static_cast<T>(overlapThreshold)) nextCandidateScore = static_cast<T>(0.f);
|
||||
|
||||
// First decide whether to perform hard suppression
|
||||
if (boxVal >= overlapThreshold) {
|
||||
shouldHardSuppress = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If nextCandidate survives hard suppression, apply soft suppression
|
||||
if (nextCandidateScore <= static_cast<T>(scoreThreshold)) break;
|
||||
}
|
||||
|
||||
return shouldHardSuppress;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void suppressNonMaxOverlapKernel(T* boxes, LongType const* boxesShape, T* scoresData, I* indices,
|
||||
I* startIndices, LongType length, I maxOutputLen,
|
||||
T overlapThreshold, T scoreThreshold, I* output, LongType const* outputShape, I* outputLength, bool simple) {
|
||||
__shared__ I selectedSize;
|
||||
__shared__ I* tempOutput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
selectedSize = outputLength ? *outputLength : maxOutputLen;
|
||||
extern __shared__ unsigned char shmem[];
|
||||
tempOutput = (I*)shmem;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = blockIdx.x * blockDim.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
for (I nextCandidateIndex = start + threadIdx.x; selectedSize < maxOutputLen && nextCandidateIndex < (I)length;) {
|
||||
auto originalScore = scoresData[nextCandidateIndex];
|
||||
I nextCandidateBoxIndex = indices[nextCandidateIndex];
|
||||
auto selectedSizeMark = selectedSize;
|
||||
|
||||
// skip for cases when index is less than 0 (under score threshold)
|
||||
if (nextCandidateBoxIndex < 0) {
|
||||
nextCandidateIndex += step;
|
||||
continue;
|
||||
}
|
||||
// check for overlaps
|
||||
bool shouldHardSuppress =
|
||||
checkOverlapBoxes(boxes, boxesShape, scoresData, indices, tempOutput, startIndices, selectedSize,
|
||||
nextCandidateIndex, overlapThreshold, scoreThreshold, simple); // false;
|
||||
T nextCandidateScore = scoresData[nextCandidateIndex];
|
||||
|
||||
startIndices[nextCandidateIndex] = selectedSize;
|
||||
if (!shouldHardSuppress) {
|
||||
if (nextCandidateScore == originalScore) {
|
||||
// Suppression has not occurred, so select nextCandidate
|
||||
I currSize = math::atomics::sd_atomicAdd(&selectedSize, (I)1);
|
||||
if (output) {
|
||||
output[currSize] = nextCandidateBoxIndex;
|
||||
}
|
||||
tempOutput[currSize] = nextCandidateBoxIndex;
|
||||
|
||||
}
|
||||
|
||||
if ((float) nextCandidateScore > (float) scoreThreshold) {
|
||||
// Soft suppression has occurred and current score is still greater than
|
||||
// scoreThreshold; add nextCandidate back onto priority queue.
|
||||
continue; // in some cases, this index not 0
|
||||
}
|
||||
}
|
||||
nextCandidateIndex += step;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
printf("selectedSize: %i\n", selectedSize);
|
||||
if (outputLength) *outputLength = selectedSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef NDArray (*SimilarityFunc)(NDArray& boxes, LongType i, LongType j);
|
||||
template <typename T>
|
||||
static inline T similarityOverlaps_(NDArray& boxes, LongType i, LongType j) {
|
||||
return boxes.t<T>(i, j);
|
||||
}
|
||||
|
||||
static NDArray similiratyOverlaps(NDArray& boxes, LongType i, LongType j) {
|
||||
NDArray res(boxes.dataType(), boxes.getContext()); // = NDArrayFactory::create(0.);
|
||||
BUILD_SINGLE_SELECTOR(boxes.dataType(), res = similarityOverlaps_, (boxes, i, j), SD_FLOAT_TYPES);
|
||||
return res;
|
||||
}
|
||||
|
||||
static NDArray similarityV3(NDArray& boxes, LongType i, LongType j) {
|
||||
NDArray res(boxes.dataType(), boxes.getContext()); // = NDArrayFactory::create(0.);
|
||||
BUILD_SINGLE_SELECTOR(boxes.dataType(), res = similirityV3_, (boxes, i, j), SD_FLOAT_TYPES);
|
||||
return res;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, typename I>
|
||||
static LongType nonMaxSuppressionGeneric_(LaunchContext* context, NDArray* boxes, NDArray* scores,
|
||||
int outputSize, float overlapThreshold, float scoreThreshold,
|
||||
NDArray* output, SimilarityFunc f) {
|
||||
auto stream = context->getCudaStream();
|
||||
if (output)
|
||||
NDArray::preparePrimaryUse({output}, {boxes, scores});
|
||||
else {
|
||||
if (!boxes->isActualOnHostSide()) boxes->syncToHost();
|
||||
if (!scores->isActualOnHostSide()) scores->syncToHost();
|
||||
}
|
||||
|
||||
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() < 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 (LongType)selected.size();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void nonMaxSuppression(LaunchContext* context, NDArray* boxes, NDArray* scales, int maxSize, double threshold,
|
||||
double scoreThreshold, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(boxes->dataType(), output->dataType(), nonMaxSuppressionV2_,
|
||||
(context, boxes, scales, maxSize, threshold, scoreThreshold, output), SD_FLOAT_TYPES,
|
||||
SD_INDEXING_TYPES);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LongType nonMaxSuppressionGeneric(LaunchContext* context, NDArray* boxes, NDArray* scales, int maxSize,
|
||||
double threshold, double scoreThreshold, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(boxes->dataType(), output ? output->dataType() : DataType::INT32,
|
||||
return nonMaxSuppressionGeneric_,
|
||||
(context, boxes, scales, maxSize, threshold, scoreThreshold, output, similiratyOverlaps),
|
||||
SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
return boxes->sizeAt(0);
|
||||
}
|
||||
|
||||
LongType nonMaxSuppressionV3(LaunchContext* context, NDArray* boxes, NDArray* scores, int maxSize,
|
||||
double overlapThreshold, double scoreThreshold, NDArray* output) {
|
||||
BUILD_DOUBLE_SELECTOR(boxes->dataType(), output ? output->dataType() : DataType::INT32,
|
||||
return nonMaxSuppressionGeneric_,
|
||||
(context, boxes, scores, maxSize, overlapThreshold, scoreThreshold, output, similarityV3),
|
||||
SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
return boxes->sizeAt(0);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,463 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 Oleh Semeniv (oleg.semeniv@gmail.com)
|
||||
//
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/adjust_hue.h>
|
||||
#include <ops/declarable/helpers/imagesHelpers.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL void rgbToYuvCuda(const void* vx, const LongType* xShapeInfo, const LongType* xTadOffsets, void* vz,
|
||||
const LongType* zShapeInfo, const LongType* zTadOffsets,
|
||||
const LongType numOfTads, const int dimC) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int rank;
|
||||
__shared__ LongType xDimCstride, zDimCstride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(xShapeInfo);
|
||||
xDimCstride = shape::stride(xShapeInfo)[dimC];
|
||||
zDimCstride = shape::stride(zShapeInfo)[dimC];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < numOfTads; i += gridDim.x * blockDim.x) {
|
||||
const T* xTad = x + xTadOffsets[i];
|
||||
T* zTad = z + zTadOffsets[i];
|
||||
|
||||
rgbYuv<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], zTad[0], zTad[zDimCstride], zTad[2 * zDimCstride]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void rgbToYuvCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const cudaStream_t* stream,
|
||||
const void* vx, const LongType* xShapeInfo, const LongType* xTadOffsets, void* vz,
|
||||
const LongType* zShapeInfo, const LongType* zTadOffsets, const LongType numOfTads,
|
||||
const int dimC) {
|
||||
rgbToYuvCuda<T><<<blocksPerGrid, threadsPerBlock, 256, *stream>>>(vx, xShapeInfo, xTadOffsets, vz, zShapeInfo,
|
||||
zTadOffsets, numOfTads, dimC);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "rgbToYuvCudaLauncher failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void transformRgbYuv(LaunchContext* context, NDArray& input, NDArray& output, const int dimC) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), {dimC});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output.shapeInfo(), {dimC});
|
||||
|
||||
const LongType numOfTads = packX->numberOfTads();
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (numOfTads + threadsPerBlock - 1) / threadsPerBlock;
|
||||
|
||||
PointersManager manager(context, "yuv_to_rgb");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), rgbToYuvCudaLauncher,
|
||||
(blocksPerGrid, threadsPerBlock, context->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), packX->platformOffsets(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), packZ->platformOffsets(), numOfTads, dimC),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL void yuvToRgbCuda(const void* vx, const LongType* xShapeInfo, const LongType* xTadOffsets, void* vz,
|
||||
const LongType* zShapeInfo, const LongType* zTadOffsets,
|
||||
const LongType numOfTads, const int dimC) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int rank;
|
||||
__shared__ LongType xDimCstride, zDimCstride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(xShapeInfo);
|
||||
xDimCstride = shape::stride(xShapeInfo)[dimC];
|
||||
zDimCstride = shape::stride(zShapeInfo)[dimC];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < numOfTads; i += gridDim.x * blockDim.x) {
|
||||
const T* xTad = x + xTadOffsets[i];
|
||||
T* zTad = z + zTadOffsets[i];
|
||||
|
||||
yuvRgb<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], zTad[0], zTad[zDimCstride], zTad[2 * zDimCstride]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void yuvToRgbCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const cudaStream_t* stream,
|
||||
const void* vx, const LongType* xShapeInfo, const LongType* xTadOffsets, void* vz,
|
||||
const LongType* zShapeInfo, const LongType* zTadOffsets, const LongType numOfTads,
|
||||
const int dimC) {
|
||||
yuvToRgbCuda<T><<<blocksPerGrid, threadsPerBlock, 256, *stream>>>(vx, xShapeInfo, xTadOffsets, vz, zShapeInfo,
|
||||
zTadOffsets, numOfTads, dimC);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "yuvToRgbCuda failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void transformYuvRgb(LaunchContext* context, NDArray& input, NDArray& output, const int dimC) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), {dimC});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output.shapeInfo(), {dimC});
|
||||
|
||||
const LongType numOfTads = packX->numberOfTads();
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (numOfTads + threadsPerBlock - 1) / threadsPerBlock;
|
||||
|
||||
PointersManager manager(context, "yuv_to_rgb");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), yuvToRgbCudaLauncher,
|
||||
(blocksPerGrid, threadsPerBlock, context->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), packX->platformOffsets(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), packZ->platformOffsets(), numOfTads, dimC),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// for example xShapeInfo = {2,3,4}, zShapeInfo = {2,1,4}
|
||||
template <typename T>
|
||||
SD_KERNEL void rgbToGrsCuda(const void* vx, const LongType* xShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const int dimC) {
|
||||
const auto x = reinterpret_cast<const T*>(vx);
|
||||
auto z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType zLen;
|
||||
__shared__ LongType rank;
|
||||
__shared__ const LongType* xShapePtr;
|
||||
__shared__ const LongType* zShapePtr;
|
||||
__shared__ const LongType* xStridePtr;
|
||||
__shared__ const LongType* zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
zLen = shape::length(zShapeInfo);
|
||||
rank = shape::rank(zShapeInfo);
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
extern __shared__ unsigned char shmem[];
|
||||
auto coords = reinterpret_cast<LongType*>(shmem) + threadIdx.x * rank;
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < zLen; i += gridDim.x * blockDim.x) {
|
||||
// Compute coordinates for the current index
|
||||
INDEX2COORDS(i, rank, zShapePtr, coords);
|
||||
|
||||
// Compute z offset
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStridePtr, coords, zOffset);
|
||||
|
||||
// Compute x offsets for R, G, B channels
|
||||
LongType xOffset0;
|
||||
COORDS2INDEX(rank, xStridePtr, coords, xOffset0);
|
||||
const auto xOffset1 = xOffset0 + xStridePtr[dimC];
|
||||
const auto xOffset2 = xOffset1 + xStridePtr[dimC];
|
||||
|
||||
// Convert RGB to grayscale
|
||||
z[zOffset] = 0.2989f * x[xOffset0] + 0.5870f * x[xOffset1] + 0.1140f * x[xOffset2];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void rgbToGrsCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const int dimC) {
|
||||
rgbToGrsCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, dimC);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "rgbToGrsCuda failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void transformRgbGrs(LaunchContext* context, NDArray& input, NDArray& output, const int dimC) {
|
||||
PointersManager manager(context, "rgbToGrs");
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 4;
|
||||
const int blocksPerGrid = (input.lengthOf() + threadsPerBlock - 1) / threadsPerBlock;
|
||||
const int sharedMem = input.rankOf() * sizeof(LongType) * threadsPerBlock + 128;
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), rgbToGrsCudaLauncher,
|
||||
(blocksPerGrid, threadsPerBlock, sharedMem, context->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), dimC),
|
||||
SD_NUMERIC_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void SD_KERNEL rgbToHsvCuda(const void* vx, const LongType* xShapeInfo, const LongType* xTadOffsets,
|
||||
void* vz, const LongType* zShapeInfo, const LongType* zTadOffsets,
|
||||
const LongType numOfTads, const int dimC) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int rank;
|
||||
__shared__ LongType xDimCstride, zDimCstride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(xShapeInfo);
|
||||
xDimCstride = shape::stride(xShapeInfo)[dimC];
|
||||
zDimCstride = shape::stride(zShapeInfo)[dimC];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < numOfTads; i += gridDim.x * blockDim.x) {
|
||||
const T* xTad = x + xTadOffsets[i];
|
||||
T* zTad = z + zTadOffsets[i];
|
||||
|
||||
rgbToHsv<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], zTad[0], zTad[zDimCstride], zTad[2 * zDimCstride]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void SD_KERNEL hsvToRgbCuda(const void* vx, const LongType* xShapeInfo, const LongType* xTadOffsets,
|
||||
void* vz, const LongType* zShapeInfo, const LongType* zTadOffsets,
|
||||
const LongType numOfTads, const int dimC) {
|
||||
const T* x = reinterpret_cast<const T*>(vx);
|
||||
T* z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int rank;
|
||||
__shared__ LongType xDimCstride, zDimCstride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(xShapeInfo);
|
||||
xDimCstride = shape::stride(xShapeInfo)[dimC];
|
||||
zDimCstride = shape::stride(zShapeInfo)[dimC];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType i = tid; i < numOfTads; i += gridDim.x * blockDim.x) {
|
||||
const T* xTad = x + xTadOffsets[i];
|
||||
T* zTad = z + zTadOffsets[i];
|
||||
|
||||
hsvToRgb<T>(xTad[0], xTad[xDimCstride], xTad[2 * xDimCstride], zTad[0], zTad[zDimCstride], zTad[2 * zDimCstride]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_HOST void hsvToRgbCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const LongType* xTadOffsets, void* vz, const LongType* zShapeInfo,
|
||||
const LongType* zTadOffsets, const LongType numOfTads,
|
||||
const int dimC) {
|
||||
hsvToRgbCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, xTadOffsets, vz, zShapeInfo,
|
||||
zTadOffsets, numOfTads, dimC);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "hsvToRgbCuda failed");
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static SD_HOST void rgbToHsvCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMemory,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const LongType* xTadOffsets, void* vz, const LongType* zShapeInfo,
|
||||
const LongType* zTadOffsets, const LongType numOfTads,
|
||||
const int dimC) {
|
||||
rgbToHsvCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMemory, *stream>>>(vx, xShapeInfo, xTadOffsets, vz, zShapeInfo,
|
||||
zTadOffsets, numOfTads, dimC);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "rgbToHsvCuda failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void transformHsvRgb(LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), {dimC});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), {dimC});
|
||||
|
||||
const LongType numOfTads = packX->numberOfTads();
|
||||
|
||||
dim3 launchDims = imageHelper(numOfTads);
|
||||
PointersManager manager(context, "hsv_to_rgb");
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), hsvToRgbCudaLauncher,
|
||||
(launchDims.y, launchDims.x, launchDims.z,context->getCudaStream(), input->specialBuffer(),
|
||||
input->specialShapeInfo(), packX->platformOffsets(), output->specialBuffer(),
|
||||
output->specialShapeInfo(), packZ->platformOffsets(), numOfTads, dimC),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void transformRgbHsv(LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), {dimC});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), {dimC});
|
||||
|
||||
const LongType numOfTads = packX->numberOfTads();
|
||||
|
||||
dim3 launchDims = imageHelper(numOfTads);
|
||||
|
||||
PointersManager manager(context, "rgb_to_hsv");
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), rgbToHsvCudaLauncher,
|
||||
(launchDims.y, launchDims.x,launchDims.z, context->getCudaStream(), input->specialBuffer(),
|
||||
input->specialShapeInfo(), packX->platformOffsets(), output->specialBuffer(),
|
||||
output->specialShapeInfo(), packZ->platformOffsets(), numOfTads, dimC),
|
||||
SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void tripleTransformerCuda(const void* vx, const LongType* xShapeInfo,
|
||||
const LongType* xTadShapeInfo, const LongType* xOffsets, void* vz,
|
||||
const LongType* zShapeInfo, const LongType* zTadShapeInfo,
|
||||
const LongType* zOffsets, const int dimC, int mode, uint64_t numTads) {
|
||||
const auto x = reinterpret_cast<const T*>(vx);
|
||||
auto z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType zLen, *sharedMem;
|
||||
__shared__ int rank; // xRank == zRank
|
||||
|
||||
float yiqarr[3][3] = {
|
||||
{0.299f, 0.59590059f, 0.2115f}, {0.587f, -0.27455667f, -0.52273617f}, {0.114f, -0.32134392f, 0.31119955f}};
|
||||
|
||||
float rgbarr[3][3] = {
|
||||
{1.f, 1.f, 1.f}, {0.95598634f, -0.27201283f, -1.10674021f}, {0.6208248f, -0.64720424f, 1.70423049f}};
|
||||
|
||||
auto tr = mode == 1 ? yiqarr : rgbarr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
zLen = shape::length(zShapeInfo);
|
||||
rank = shape::rank(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType* coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
if (dimC == (rank - 1) && 'c' == shape::order(xShapeInfo) && 1 == shape::elementWiseStride(xShapeInfo) &&
|
||||
'c' == shape::order(zShapeInfo) && 1 == shape::elementWiseStride(zShapeInfo)) {
|
||||
for (uint64_t f = blockIdx.x * blockDim.x + threadIdx.x; f < zLen / 3; f += gridDim.x * blockDim.x) {
|
||||
auto i = f * 3;
|
||||
|
||||
auto xi0 = x[i];
|
||||
auto xi1 = x[i + 1];
|
||||
auto xi2 = x[i + 2];
|
||||
|
||||
for (int e = 0; e < 3; e++) z[i + e] = xi0 * tr[0][e] + xi1 * tr[1][e] + xi2 * tr[2][e];
|
||||
}
|
||||
} else {
|
||||
// TAD based case
|
||||
const LongType xDimCstride = shape::stride(xShapeInfo)[dimC];
|
||||
const LongType zDimCstride = shape::stride(zShapeInfo)[dimC];
|
||||
|
||||
for (uint64_t i = blockIdx.x * blockDim.x + threadIdx.x; i < numTads; i += blockDim.x * gridDim.x) {
|
||||
const T* xTad = x + xOffsets[i];
|
||||
T* zTad = z + zOffsets[i];
|
||||
|
||||
auto xi0 = xTad[0];
|
||||
auto xi1 = xTad[xDimCstride];
|
||||
auto xi2 = xTad[xDimCstride * 2];
|
||||
|
||||
for (int e = 0; e < 3; e++) zTad[zDimCstride * e] = xi0 * tr[0][e] + xi1 * tr[1][e] + xi2 * tr[2][e];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void rgbYiq(LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimC);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimC);
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
dim3 launchDims = getLaunchDims("image_helpers_triple");
|
||||
tripleTransformerCuda<T><<<launchDims.x,launchDims.y, launchDims.z, *context->getCudaStream()>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), packX->platformShapeInfo(), packX->platformOffsets(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), packZ->platformShapeInfo(), packZ->platformOffsets(), dimC, 1,
|
||||
packZ->numberOfTads());
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "tripleTransformerCuda failed");
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SD_INLINE static void yiqRgb(LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimC);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimC);
|
||||
|
||||
dim3 launchDims = getLaunchDims("image_helpers_triple");
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
tripleTransformerCuda<T><<<launchDims.x, launchDims.y,launchDims.z, *context->getCudaStream()>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), packX->platformShapeInfo(), packX->platformOffsets(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), packZ->platformShapeInfo(), packZ->platformOffsets(), dimC, 2,
|
||||
packZ->numberOfTads());
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "tripleTransformerCuda failed");
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
|
||||
void transformYiqRgb(LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), yiqRgb, (context, input, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
void transformRgbYiq(LaunchContext* context, NDArray* input, NDArray* output, const int dimC) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), rgbYiq, (context, input, output, dimC), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,112 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/ConstantTadHelper.h>
|
||||
#include <legacy/NativeOpExecutioner.h>
|
||||
#include <ops/declarable/helpers/reductions.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void argMax(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
if (output.isScalar()) {
|
||||
NativeOpExecutioner::execIndexReduceScalar(LaunchContext::defaultContext(), indexreduce::Ops::IndexMax,
|
||||
input.buffer(), input.shapeInfo(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), nullptr, output.buffer(), output.shapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo());
|
||||
} else {
|
||||
auto tadPack = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), (LongType*)&dimensions,dimensions.size());
|
||||
|
||||
NativeOpExecutioner::execIndexReduce(LaunchContext::defaultContext(), indexreduce::Ops::IndexMax, input.buffer(),
|
||||
input.shapeInfo(), input.specialBuffer(), input.specialShapeInfo(), nullptr,
|
||||
output.buffer(), output.shapeInfo(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), (LongType*)nullptr, dimensions.size(),
|
||||
tadPack->specialShapeInfo(), tadPack->specialOffsets());
|
||||
}
|
||||
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
}
|
||||
|
||||
void argMin(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
if (output.isScalar()) {
|
||||
NativeOpExecutioner::execIndexReduceScalar(LaunchContext::defaultContext(), indexreduce::Ops::IndexMin,
|
||||
input.buffer(), input.shapeInfo(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), nullptr, output.buffer(), output.shapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo());
|
||||
} else {
|
||||
auto tadPack = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), (LongType)&dimensions);
|
||||
|
||||
NativeOpExecutioner::execIndexReduce(LaunchContext::defaultContext(), indexreduce::Ops::IndexMin, input.buffer(),
|
||||
input.shapeInfo(), input.specialBuffer(), input.specialShapeInfo(), nullptr,
|
||||
output.buffer(), output.shapeInfo(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), (LongType*)nullptr, dimensions.size(),
|
||||
tadPack->specialShapeInfo(), tadPack->specialOffsets());
|
||||
}
|
||||
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
}
|
||||
|
||||
void argAbsMax(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
if (output.isScalar()) {
|
||||
NativeOpExecutioner::execIndexReduceScalar(LaunchContext::defaultContext(), indexreduce::Ops::IndexAbsoluteMax,
|
||||
input.buffer(), input.shapeInfo(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), nullptr, output.buffer(), output.shapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo());
|
||||
} else {
|
||||
auto tadPack = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), (LongType)&dimensions);
|
||||
|
||||
NativeOpExecutioner::execIndexReduce(LaunchContext::defaultContext(), indexreduce::Ops::IndexAbsoluteMax,
|
||||
input.buffer(), input.shapeInfo(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), nullptr, output.buffer(), output.shapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo(), (LongType*)nullptr,
|
||||
dimensions.size(), tadPack->specialShapeInfo(), tadPack->specialOffsets());
|
||||
}
|
||||
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
}
|
||||
|
||||
void argAbsMin(NDArray& input, NDArray& output, const std::vector<LongType>& dimensions) {
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
if (output.isScalar()) {
|
||||
NativeOpExecutioner::execIndexReduceScalar(LaunchContext::defaultContext(), indexreduce::Ops::IndexAbsoluteMin,
|
||||
input.buffer(), input.shapeInfo(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), nullptr, output.buffer(), output.shapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo());
|
||||
} else {
|
||||
auto tadPack = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), (LongType)&dimensions);
|
||||
|
||||
NativeOpExecutioner::execIndexReduce(LaunchContext::defaultContext(), indexreduce::Ops::IndexAbsoluteMin,
|
||||
input.buffer(), input.shapeInfo(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), nullptr, output.buffer(), output.shapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo(), (LongType*)nullptr,
|
||||
dimensions.size(), tadPack->specialShapeInfo(), tadPack->specialOffsets());
|
||||
}
|
||||
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,112 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
//
|
||||
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/DebugHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
|
||||
#include <loops/special_kernels.h>
|
||||
#include <ops/declarable/helpers/ismax.h>
|
||||
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void ismax_(LaunchContext* context, NDArray* input, NDArray* output,
|
||||
const std::vector<LongType>& dimensions) {
|
||||
auto stream = context->getCudaStream();
|
||||
|
||||
auto xRank = input->rankOf();
|
||||
auto zRank = output->rankOf();
|
||||
auto xType = input->dataType();
|
||||
auto zType = output->dataType();
|
||||
input->syncToDevice();
|
||||
LongType* special = nullptr;
|
||||
PointersManager manager(context, "IsMaxHelper");
|
||||
if (dimensions.size() == 0) {
|
||||
/**
|
||||
* In case of vector-input for IsMax, it just turns into IndexReduce call + subsequent filler call
|
||||
*/
|
||||
auto indexMax = input->applyIndexReduce(indexreduce::IndexMax, &dimensions);
|
||||
auto targetIdx = indexMax->e<LongType>(0);
|
||||
|
||||
dim3 launchDims = getLaunchDims("ismaxFill");
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
zType, fillIsMaxGeneric,
|
||||
(launchDims, stream, output->specialBuffer(),
|
||||
const_cast<sd::LongType *>(output->specialShapeInfo()), output->lengthOf(), targetIdx),
|
||||
SD_COMMON_TYPES);
|
||||
manager.synchronize();
|
||||
|
||||
delete indexMax;
|
||||
} else {
|
||||
LongType* hostYShapeInfo = nullptr;
|
||||
LongType* hostTShapeInfo = nullptr;
|
||||
LongType* dimension = nullptr;
|
||||
|
||||
LongType dimensionLength = dimensions.size();
|
||||
std::vector<LongType> copy(dimensions);
|
||||
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), copy.data(), copy.size());
|
||||
|
||||
// we launch legacy IndexMax op, to get indices of max values along dimension
|
||||
auto indexMaxArr = input->applyIndexReduce(indexreduce::IndexMax, &dimensions);
|
||||
|
||||
dim3 launchDims = getLaunchDims("ismax");
|
||||
dimension = (LongType*)manager.replicatePointer(dimensions.data(), dimensions.size() * sizeof(LongType));
|
||||
|
||||
// at this point, all IMax indexes are gathered, and we execute filler
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
zType, fillDimensionalIsMaxGeneric,
|
||||
(launchDims, stream, indexMaxArr->specialBuffer(), output->specialBuffer(),
|
||||
const_cast<sd::LongType *>(output->specialShapeInfo()),
|
||||
const_cast<sd::LongType *>(packZ->specialShapeInfo()),
|
||||
dimension, dimensionLength, const_cast<sd::LongType *>(packZ->specialOffsets())),
|
||||
SD_COMMON_TYPES);
|
||||
manager.synchronize();
|
||||
|
||||
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,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 GS <sgazeos@gmail.com>
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <ops/declarable/helpers/legacy_helpers.h>
|
||||
#include <ops/ops.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
void reluDerivative__(NDArray* theFirst, NDArray* theSecond) {
|
||||
auto functor = LAMBDA_TT(x, y) { return x > (T)0.f ? y : T(0.f); });
|
||||
|
||||
theFirst->applyPairwiseLambda(theSecond, functor,theFirst);
|
||||
}
|
||||
|
||||
void reluDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), reluDerivative__, (theFirst, theSecond), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void reluDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return x > (T)0.f ? y : T(0.f); });
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void reluDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), reluDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void relu6Derivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), relu6Derivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void leakyReluDerivative(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>
|
||||
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 * math::sd_eluderivative<T, T>(x, alphaT); });
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void eluDerivative(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>
|
||||
void seluDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return y * simdOps::SELUDerivative<T>::op(x, nullptr); });
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void seluDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), seluDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,85 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void tanhDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T th = math::sd_tanh<T, T>(x);
|
||||
return y * ((T)1.0f - (th * th));
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void tanhDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), tanhDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void hardTanhDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T th = math::sd_tanh<T, T>(x);
|
||||
return y * simdOps::HardTanhDerivative<T>::op(x, nullptr);
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void hardTanhDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), hardTanhDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void rationalTanhDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return y * simdOps::RationalTanhDerivative<T>::op(x, nullptr); });
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void rationalTanhDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), rationalTanhDerivative_, (theFirst, theSecond, theOutput),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void rectifiedTanhDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return x > (T)0.0f ? y * (math::sd_tanhderivative<T, T>(x)) : (T)0.0f; });
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void rectifiedTanhDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), rectifiedTanhDerivative_, (theFirst, theSecond, theOutput),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 GS <sgazeos@gmail.com>
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <ops/declarable/helpers/legacy_helpers.h>
|
||||
#include <ops/ops.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void cubeDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return y * (3 * x * x); });
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void cubeDerivative(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>
|
||||
void reduceNorm1_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return x > T(0.f) ? y : -y; });
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void reduceNorm1(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), reduceNorm1_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void sigmCrossEntropy_(NDArray* logits, NDArray* labels, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
return math::sd_max<T>(x, (T)0.f) - x * y + math::sd_log<T, T>((T)1.f + math::sd_exp<T, T>(-math::sd_abs<T,T>(x)));
|
||||
});
|
||||
|
||||
logits->applyPairwiseLambda(labels, functor, output);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void sigmCrossEntropy(LaunchContext* context, NDArray* logits, NDArray* labels, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(logits->dataType(), sigmCrossEntropy_, (logits, labels, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
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.) + math::sd_exp<T, T>(x));
|
||||
auto e = math::sd_exp<T, T>(-x);
|
||||
return static_cast<T>(1.) - y - e / (static_cast<T>(1.) + e);
|
||||
});
|
||||
|
||||
logits->applyPairwiseLambda(labels, functor, output);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void sigmCrossEntropyGrad(LaunchContext* context, NDArray* logits, NDArray* labels, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(logits->dataType(), sigmCrossEntropyGrad_, (logits, labels, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void softSignDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T ss = (T)1.f + math::sd_abs<T,T>(x);
|
||||
return y * ((T)1.0f / (ss * ss));
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void softSignDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), softSignDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void softPlusDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T p = math::sd_pow<T, T, T>(static_cast<T>(M_E), x);
|
||||
return y * (p / (p + 1.));
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void softPlusDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), softPlusDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// \param input
|
||||
/// \param epsilon
|
||||
/// \param output
|
||||
template <typename T>
|
||||
void sigmoidDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) {
|
||||
T s = math::sd_sigmoid<T, T>(x);
|
||||
return y * (s * ((T)1.0f - s));
|
||||
});
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void sigmoidDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), sigmoidDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void hardSigmoidDerivative_(NDArray* input, NDArray* epsilon, NDArray* output) {
|
||||
auto functor = LAMBDA_TT(x, y) { return y * simdOps::HardSigmoidDerivative<T>::op(x, nullptr); });
|
||||
|
||||
input->applyPairwiseLambda(epsilon, functor, output);
|
||||
}
|
||||
|
||||
void hardSigmoidDerivative(LaunchContext* context, NDArray* theFirst, NDArray* theSecond, NDArray* theOutput) {
|
||||
BUILD_SINGLE_SELECTOR(theFirst->dataType(), hardSigmoidDerivative_, (theFirst, theSecond, theOutput), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
void logSumExp_(NDArray* input, NDArray* axis, NDArray* output) {
|
||||
// reduce along axis with
|
||||
NDArray *tempInput = input->dup();
|
||||
input->applyTransform(transform::Exp, tempInput);
|
||||
std::vector<LongType> axisVector;
|
||||
if (axis != nullptr) {
|
||||
axisVector.resize(axis->lengthOf());
|
||||
for (size_t i = 0; i < axisVector.size(); ++i) axisVector[i] = axis->e<int>(i);
|
||||
}
|
||||
tempInput.reduceAlongDimension(reduce::Sum, output, &axisVector);
|
||||
output->applyTransform(transform::Log, output);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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<LongType> axisVector;
|
||||
if (axis != nullptr) {
|
||||
axisVector.resize(axis->lengthOf());
|
||||
for (size_t i = 0; i < axisVector.size(); ++i) axisVector[i] = axis->e<int>(i);
|
||||
}
|
||||
tempInput.reduceAlongDimension(reduce::Sum, output, &axisVector);
|
||||
output->applyTransform(transform::Log, output);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void logSumExp(LaunchContext* context, NDArray* input, NDArray* axis, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), logSumExp_, (input, axis, output), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void logSumExp(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>
|
||||
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 * (math::sd_log<T, T>((T)1.f + math::sd_exp<T, T>(-math::sd_abs<T,T>(_x))) +
|
||||
math::sd_max(-_x, T(0.f)));
|
||||
});
|
||||
|
||||
auto mainRoutineT2 = LAMBDA_TTT(_x, _z, _w) {
|
||||
return (((T)1.0 - _z) * _x) + _w * (math::sd_log<T, T>(T(1.) + math::sd_exp<T, T>(-math::sd_abs<T,T>(_x))) + math::sd_max(-_x, T(0.f)));
|
||||
});
|
||||
|
||||
if (weights->isScalar()) {
|
||||
input->applyPairwiseLambda(targets, mainRoutineT1, output);
|
||||
} else {
|
||||
std::unique_ptr<NDArray> targetVector(new NDArray(*weights));
|
||||
targetVector->applyScalar(scalar::Add, -1.f, targetVector.get());
|
||||
|
||||
*targets = (*targetVector * *targets) + T(1.f);
|
||||
input->applyPairwiseLambda(targets, mainRoutineT1, output);
|
||||
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void weightedCrossEntropyWithLogitsFunctor(LaunchContext* context, NDArray * targets, NDArray * input,
|
||||
NDArray * weights, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {targets, input, weights});
|
||||
|
||||
BUILD_SINGLE_SELECTOR(targets->dataType(), weightedCrossEntropyWithLogitsFunctor_, (targets, input, weights, output),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({output}, {targets, input, weights});
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // 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 <ops/declarable/helpers/lgamma.h>
|
||||
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// calculate digamma function for array elements
|
||||
template <typename T>
|
||||
void lgamma_(NDArray* x, NDArray* z) {
|
||||
auto lgammaProc = LAMBDA_T(x_) {
|
||||
return T(DataTypeUtils::fromT<T>() == DOUBLE ? ::lgamma(x_)
|
||||
: ::lgammaf(x_));
|
||||
});
|
||||
|
||||
x->applyLambda(lgammaProc, z);
|
||||
}
|
||||
|
||||
void lgamma(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
|
||||
@@ -0,0 +1,188 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/ConstantTadHelper.h>
|
||||
#include <ops/declarable/helpers/lrn.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void lrnKernel(void* vx, LongType const* xTadShapeInfo, LongType const* xTadOffsets, void* vz,
|
||||
LongType const* zTadShapeInfo, LongType const* zTadOffsets, LongType numTads,
|
||||
LongType tadLength, int depth, double bias, double alpha,
|
||||
double beta) {
|
||||
extern __shared__ char sharedChar[];
|
||||
T* shared = reinterpret_cast<T*>(sharedChar);
|
||||
|
||||
auto xEws = shape::elementWiseStride(xTadShapeInfo);
|
||||
auto zEws = shape::elementWiseStride(zTadShapeInfo);
|
||||
|
||||
auto xOrder = shape::order(xTadShapeInfo);
|
||||
auto zOrder = shape::order(zTadShapeInfo);
|
||||
|
||||
const T tbias = static_cast<T>(bias);
|
||||
const T tbeta = static_cast<T>(beta);
|
||||
const T talpha = static_cast<T>(alpha);
|
||||
|
||||
// one block of threads processes 1 example within batch
|
||||
for (LongType i = blockIdx.x; i < numTads; i += gridDim.x) {
|
||||
auto x = reinterpret_cast<T*>(vx) + xTadOffsets[i];
|
||||
auto z = reinterpret_cast<T*>(vz) + zTadOffsets[i];
|
||||
|
||||
// load everything into shared memory, so we'll operate on shared memory from now on
|
||||
shared[threadIdx.x] = x[threadIdx.x * xEws];
|
||||
__syncthreads();
|
||||
|
||||
const LongType begin = sd::math::sd_max<int>(0, threadIdx.x - depth);
|
||||
const LongType last = depth + threadIdx.x + 1;
|
||||
const LongType end = sd::math::sd_min<int>(last, tadLength);
|
||||
|
||||
T prev = static_cast<T>(0.);
|
||||
for (int s = begin; s < end; s++) prev = prev + shared[s] * shared[s];
|
||||
|
||||
z[threadIdx.x * zEws] = shared[threadIdx.x] / math::sd_pow<T, T, T>(tbias + alpha * prev, tbeta);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static SD_KERNEL void lrnBPKernel(void const* vx, LongType const* xTadShapeInfo, LongType const* xTadOffsets,
|
||||
void* vz,
|
||||
LongType const* zTadShapeInfo, LongType const* zTadOffsets, LongType numTads,
|
||||
LongType tadLength, int depth, double bias, double alpha,
|
||||
double beta) {
|
||||
extern __shared__ char sharedChar[];
|
||||
X* sharedX = reinterpret_cast<X*>(sharedChar);
|
||||
Z* sharedY = reinterpret_cast<Z*>(sharedX + blockDim.x);
|
||||
|
||||
auto xEws = shape::elementWiseStride(xTadShapeInfo);
|
||||
auto zEws = shape::elementWiseStride(zTadShapeInfo);
|
||||
|
||||
auto xOrder = shape::order(xTadShapeInfo);
|
||||
auto zOrder = shape::order(zTadShapeInfo);
|
||||
|
||||
const Z tbias = static_cast<Z>(bias);
|
||||
const Z tbeta = static_cast<Z>(beta);
|
||||
const Z talpha = static_cast<Z>(alpha);
|
||||
const Z coeff = talpha * tbeta;
|
||||
|
||||
for (LongType i = blockIdx.x; i < numTads; i += gridDim.x) {
|
||||
auto x = reinterpret_cast<X const*>(vx) + xTadOffsets[i];
|
||||
auto z = reinterpret_cast<Z*>(vz) + zTadOffsets[i];
|
||||
|
||||
const LongType begin = sd::math::sd_max<int>(0, threadIdx.x - depth);
|
||||
const LongType last = depth + threadIdx.x + 1;
|
||||
const LongType end = sd::math::sd_min<int>(last, tadLength);
|
||||
|
||||
// load everything into shared memory
|
||||
sharedX[threadIdx.x] = x[threadIdx.x * xEws];
|
||||
sharedY[threadIdx.x] = static_cast<Z>(0.f);
|
||||
__syncthreads();
|
||||
|
||||
// we're operating in shared memory
|
||||
for (int s = begin; s < end; s++) sharedY[threadIdx.x] = sharedY[threadIdx.x] + sharedX[s] * sharedX[s];
|
||||
__syncthreads();
|
||||
|
||||
Z factor[1024];
|
||||
Z init = tbias + talpha * sharedY[threadIdx.x];
|
||||
|
||||
Z prev = static_cast<Z>(0.f);
|
||||
for (LongType s = begin; s < end; ++s) {
|
||||
factor[s] = math::sd_pow<Z, Z, Z>(tbias + talpha * sharedY[s], -tbeta - 1);
|
||||
prev = prev + sharedX[s] * factor[s];
|
||||
}
|
||||
|
||||
z[threadIdx.x * zEws] = factor[threadIdx.x] * init - 2 * sharedX[threadIdx.x] * coeff * prev;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename X, typename Z>
|
||||
static void lrnBP_(graph::Context& block, NDArray& input, NDArray& gradO, NDArray& gradI,
|
||||
const int depth, const float bias, const float alpha, const float beta) {
|
||||
auto rank = input.rankOf();
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), {rank - 1});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(gradI.shapeInfo(), {rank - 1});
|
||||
|
||||
const auto tadLength = shape::length(packX->primaryShapeInfo());
|
||||
const int numThreads = tadLength;
|
||||
|
||||
if (tadLength > 1024 || tadLength < 1) THROW_EXCEPTION("LRN: tadLength > 1024 isn't implemented yet");
|
||||
|
||||
dim3 launchDims = lrnDims(tadLength,packX->numberOfTads(),DataTypeUtils::sizeOf(input.dataType()),DataTypeUtils::sizeOf(gradI.dataType()));
|
||||
|
||||
lrnBPKernel<X, Z><<<launchDims.y, launchDims.x, launchDims.z,
|
||||
*block.launchContext()->getCudaStream()>>>(
|
||||
input.specialBuffer(), packX->platformShapeInfo(), packX->platformOffsets(), gradI.specialBuffer(),
|
||||
packZ->platformShapeInfo(), packZ->platformOffsets(), packX->numberOfTads(), tadLength, depth, bias, alpha, beta);
|
||||
|
||||
gradI.tickWriteDevice();
|
||||
gradI *= gradO;
|
||||
}
|
||||
|
||||
void lrnBP(graph::Context& block, NDArray& input, NDArray& gradO, NDArray& gradI, const int depth,
|
||||
const float bias, const float alpha, const float beta) {
|
||||
input.syncToDevice();
|
||||
gradO.syncToDevice();
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), gradO.dataType(), lrnBP_,
|
||||
(block, input, gradO, gradI, depth, bias, alpha, beta), SD_FLOAT_TYPES, SD_FLOAT_TYPES);
|
||||
|
||||
gradI.tickWriteDevice();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void lrnFunctor_(graph::Context& block, NDArray* input, NDArray* output, int depth, double bias, double alpha,
|
||||
double beta) {
|
||||
auto rank = input->rankOf();
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), {rank - 1});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), {rank - 1});
|
||||
|
||||
const auto tadLength = shape::length(packX->primaryShapeInfo());
|
||||
const int numBlocks = sd::math::sd_min<LongType>(1024, packX->numberOfTads());
|
||||
const int numThreads = tadLength;
|
||||
dim3 launchDims = lrnDims(tadLength, packX->numberOfTads(), DataTypeUtils::sizeOf(input->dataType()),
|
||||
DataTypeUtils::sizeOf(input->dataType()));
|
||||
|
||||
if (tadLength > 1024 || tadLength < 1) THROW_EXCEPTION("LRN: tadLength > 1024 isn't implemented yet");
|
||||
|
||||
lrnKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *block.launchContext()->getCudaStream()>>>(
|
||||
input->specialBuffer(), packX->platformShapeInfo(), packX->platformOffsets(), output->specialBuffer(),
|
||||
packZ->platformShapeInfo(), packZ->platformOffsets(), packX->numberOfTads(), tadLength, depth, bias, alpha, beta);
|
||||
}
|
||||
|
||||
Status lrnFunctor(graph::Context& block, NDArray* input, NDArray* output, int depth, double bias, double alpha,
|
||||
double beta) {
|
||||
input->syncToDevice();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), lrnFunctor_, (block, input, output, depth, bias, alpha, beta),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
output->tickWriteDevice();
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,199 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/PointersManager.h>
|
||||
#include <ops/declarable/CustomOperations.h>
|
||||
#include <ops/declarable/helpers/lstm.h>
|
||||
#include <ops/declarable/helpers/lstmBlock.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void lstmCell(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);
|
||||
|
||||
auto z = mmul(*xt, *Wx) + mmul(*ht_1, *Wh) + *b; // [bS x 4*nOut] + [bS x 4*nOut] + [1 x 4*nOut] = [bS x 4*nOut]
|
||||
|
||||
auto zit = z({0, 0, 0, nOut}); // z for input gate, = mmul(Wxi,xt) + mmul(Whi,ht_1) + bi = [bS x nOut]
|
||||
auto zft = z({0, 0, nOut, 2 * nOut}); // z for forget gate, = mmul(Wxf,xt) + mmul(Whf,ht_1) + bf = [bS x nOut]
|
||||
auto zct = z({0, 0, 2 * nOut, 3 * nOut}); // z for cell state, = mmul(Wxc,xt) + mmul(Whc,ht_1) + bc = [bS x nOut]
|
||||
auto 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
|
||||
zit += (*ct_1) * (*Wc)({0, nOut}); // add peephole connections to input gate
|
||||
zft += (*ct_1) * (*Wc)({nOut, 2 * nOut}); // add peephole connections to forget gate
|
||||
}
|
||||
|
||||
// current sell state = ft*ct_1 + it*tanh(mmul(Wxc,xt) + mmul(Whc,ht_1) + bc
|
||||
NDArray zftPlusForgetBias = zft + forgetBias;
|
||||
NDArray toAssign = sigmoid(zftPlusForgetBias) * (*ct_1) + sigmoid(zit) * tanh(zct);
|
||||
ct->assign(&toAssign);
|
||||
|
||||
// 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) zot += (*ct) * (*Wc)({{2 * nOut, 3 * nOut}}); // add peephole connections to output gate zot + ct*Wc
|
||||
|
||||
// current cell output = ot*tanh(ct)
|
||||
auto htNoPeepHole = sigmoid(zot) * tanh(*ct); // = [bS x nOut]
|
||||
|
||||
// apply projection
|
||||
if (projection) {
|
||||
NDArray restultOne = mmul(htNoPeepHole, *Wp);
|
||||
ht->assign(&restultOne); // [bS x nOut] * [ nOut x numProj] = [bS x numProj]
|
||||
// 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);
|
||||
}
|
||||
|
||||
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> shape = {xt->sizeAt(0), xt->sizeAt(1) + yLast->sizeAt(1)};
|
||||
// Concat inputs: [xt, yt-1]: concat([bs,nIn],[bs,nOut]) -> [bs, (nIn+nOut)]
|
||||
NDArray concatOut(xt->ordering(), shape, xt->dataType(),
|
||||
xt->getContext());
|
||||
concat(xt->getContext(), {const_cast<NDArray*>(xt), const_cast<NDArray*>(yLast)}, concatOut, {1});
|
||||
|
||||
auto 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])
|
||||
auto zi = m({0, 0, 0, nOut}); // z for input modulation gate, [bS, nOut]
|
||||
auto zz = m({0, 0, nOut, 2 * nOut}); // z for block input, [bS, nOut]
|
||||
auto zf = m({0, 0, 2 * nOut, 3 * nOut}); // z for forget gate, [bS, nOut]
|
||||
auto zo = m({0, 0, 3 * nOut, 4 * nOut}); // z for output gate, [bS, nOut]
|
||||
|
||||
if (peephole) { // add peephole connections: z + ct_1*Wc
|
||||
zi += (*cLast) * (*Wci); // add peephole connections to input gate
|
||||
zf += (*cLast) * (*Wcf); // add peephole connections to forget gate
|
||||
}
|
||||
|
||||
// current sell state = ft*cLast + it*tanh(mmul(Wxc,xt) + mmul(Whc,ht_1) + bc
|
||||
if (forgetBias != 0.0) zf += forgetBias;
|
||||
|
||||
zz.applyTransform(transform::Tanh, z); // z = tanh(zz)
|
||||
zi.applyTransform(transform::Sigmoid, i); // i = sigmoid(zi)
|
||||
zf.applyTransform(transform::Sigmoid, f); // f = sigmoid(zf);
|
||||
|
||||
// cell state = blockInput .* inputGate + prevCellState .* forgetGate
|
||||
z->applyPairwiseTransform(pairwise::Multiply, i, c); // c = z * i
|
||||
auto temp = (*f) * (*cLast);
|
||||
*c += temp; // c = (i * z) + (zf * (*cLast))
|
||||
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);
|
||||
|
||||
if (peephole) {
|
||||
// add peephole connections to output gate zot + ct*Wc
|
||||
auto prod = *c * (*Wco);
|
||||
zo += 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
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * 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 <helpers/ConstantTadHelper.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>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void fillRegularizerKernel(T* ioMatrixData, const LongType* ioMatrixShape,
|
||||
const LongType* ioMatrixTads, const LongType* ioMatrixOffsets,
|
||||
LongType batchSize, LongType rows, T const value) {
|
||||
for (auto x = blockIdx.x; x < batchSize; x += gridDim.x) {
|
||||
auto z = ioMatrixData + ioMatrixOffsets[x];
|
||||
for (auto r = threadIdx.x; r < rows; r += blockDim.x) {
|
||||
LongType pos[] = {r, r};
|
||||
LongType zIndex;
|
||||
COORDS2INDEX(2, shape::stride(ioMatrixTads), pos, zIndex);
|
||||
z[zIndex] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void fillRegularizer(LaunchContext* context, NDArray* ioMatrix, double const value) {
|
||||
std::vector<LongType> dims = {-2, -1};
|
||||
auto lastDimsTads = ConstantTadHelper::getInstance().tadForDimensions(ioMatrix->shapeInfo(), &dims);
|
||||
auto stream = context->getCudaStream();
|
||||
auto rows = ioMatrix->sizeAt(-2);
|
||||
dim3 launchDims = getLaunchDims("lstsq_reg");
|
||||
fillRegularizerKernel<T><<<launchDims.y,launchDims.x,launchDims.z, *stream>>>(
|
||||
ioMatrix->dataBuffer()->specialAsT<T>(), ioMatrix->specialShapeInfo(), lastDimsTads->specialShapeInfo(),
|
||||
lastDimsTads->specialOffsets(), lastDimsTads->numberOfTads(), rows, (T)value);
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Status leastSquaresSolveFunctor_(LaunchContext* context, NDArray* leftInput, NDArray* rightInput,
|
||||
double const l2Regularizer, bool const fast, NDArray* output) {
|
||||
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(leftInput->ordering(), tAtShape, output->dataType(), context);
|
||||
MmulHelper::matmul(leftInput, leftInput, &leftOutput, true, false,1.0,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,1.0,0.0,rightOutput); // Computing B' = A^T * b
|
||||
// 3. Regularization ( indeed A' = A2 - l2Regularizer * I)
|
||||
if (l2Regularizer != 0.0) {
|
||||
auto regularizer = leftOutput.ulike();
|
||||
regularizer->nullify();
|
||||
fillRegularizer<T>(context, regularizer, (T)l2Regularizer);
|
||||
leftOutput += *regularizer;
|
||||
}
|
||||
|
||||
// 4. Cholesky decomposition -- output matrix is square and lower triangular
|
||||
cholesky(context, &leftOutput, &leftOutput, true); // inplace decomposition
|
||||
// 5. Solve two triangular systems:
|
||||
auto rightB = rightOutput->ulike();
|
||||
rightB->nullify();
|
||||
|
||||
triangularSolveFunctor(context, &leftOutput, rightOutput, true, false, rightB);
|
||||
|
||||
adjointMatrix(context, &leftOutput, true, &leftOutput);
|
||||
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
|
||||
auto* qShapePtr = leftInput->getShapeAsVector();
|
||||
std::vector<LongType> qShape = *qShapePtr;
|
||||
delete qShapePtr;
|
||||
auto* rShapePtr = leftInput->getShapeAsVector();
|
||||
std::vector<LongType> rShape = *rShapePtr;
|
||||
delete rShapePtr;
|
||||
qShape[leftInput->rankOf() - 1] = leftInput->sizeAt(-2);
|
||||
|
||||
NDArray Q(leftInput->ordering(), qShape, leftInput->dataType(), context);
|
||||
NDArray R(leftInput->ordering(), rShape, leftInput->dataType(), context);
|
||||
qr(context, leftInput, &Q, &R, true);
|
||||
// 2. b` = Q^t * b:
|
||||
auto rightOutput = rightInput->ulike();
|
||||
MmulHelper::matmul(&Q, rightInput, rightOutput, true, false,1.0,0.0,rightOutput);
|
||||
// 3. Solve triangular system
|
||||
triangularSolveFunctor(context, &R, rightOutput, false, false, output);
|
||||
}
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
Status leastSquaresSolveFunctor(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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/matrixSetDiag.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void matrixSetDiagCuda(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const bool zeroPad) {
|
||||
// x - input, shape [A,B,C]
|
||||
// y - diagonal, shape [A,B]
|
||||
// z - output, shape [A,B,C]
|
||||
|
||||
const auto x = reinterpret_cast<const T*>(vx);
|
||||
const auto y = reinterpret_cast<const T*>(vy);
|
||||
auto z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ int xRank;
|
||||
__shared__ LongType xLen;
|
||||
__shared__ const LongType *shapeX, *strideX, *strideY, *strideZ;
|
||||
__shared__ bool areSameOffsets;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xLen = shape::length(xShapeInfo);
|
||||
shapeX = shape::shapeOf(xShapeInfo);
|
||||
strideX = shape::stride(xShapeInfo);
|
||||
strideY = shape::stride(yShapeInfo);
|
||||
strideZ = shape::stride(zShapeInfo);
|
||||
areSameOffsets = shape::haveSameShapeAndStrides(xShapeInfo, zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
for (LongType i = tid; i < xLen; i += step) {
|
||||
// Compute coordinates and offsets
|
||||
INDEX2COORDS(i, xRank, shapeX, coords);
|
||||
LongType xOffset, zOffset, yOffset;
|
||||
|
||||
COORDS2INDEX(xRank, strideX, coords, xOffset);
|
||||
zOffset = areSameOffsets ? xOffset : 0;
|
||||
if (!areSameOffsets) {
|
||||
COORDS2INDEX(xRank, strideZ, coords, zOffset);
|
||||
}
|
||||
|
||||
// Check if on the diagonal
|
||||
if (coords[xRank - 2] == coords[xRank - 1]) {
|
||||
COORDS2INDEX(xRank - 1, strideY, coords, yOffset);
|
||||
z[zOffset] = y[yOffset];
|
||||
} else {
|
||||
z[zOffset] = zeroPad ? static_cast<T>(0) : x[xOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void matrixSetDiagCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const bool zeroPad) {
|
||||
matrixSetDiagCuda<T>
|
||||
<<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo, zeroPad);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "matrixSetDiagCuda failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void matrixSetDiag(LaunchContext* context, NDArray& input, NDArray& diagonal, NDArray& output,
|
||||
const bool zeroPad) {
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (input.lengthOf() + threadsPerBlock - 1) / threadsPerBlock;
|
||||
const int sharedMem = threadsPerBlock * sizeof(LongType) * input.rankOf() + 128;
|
||||
|
||||
dim3 launchDims = matrixSetDiagDims(input.lengthOf(),input.rankOf());
|
||||
PointersManager manager(context, "matrixSetDiag");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input, &diagonal});
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), matrixSetDiagCudaLauncher,
|
||||
(launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), diagonal.specialBuffer(), diagonal.specialShapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo(), zeroPad),
|
||||
SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input, &diagonal});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// @author George A. Shulinok <sgazeos@gmail.com>
|
||||
//
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/matrix_band.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// matrix band kernel
|
||||
//
|
||||
// inputBuffer - buffer of input tensor
|
||||
// inputShape - shape of input tensor
|
||||
// outputBuffer - buffer of output tensor
|
||||
// outputShape - shape of output tensor
|
||||
// lowerBand - lower band of matrix
|
||||
// upperBand - upper band of matrix
|
||||
// tadOnlyInputShapeInfo - TAD shape for input
|
||||
// tadInputOffsets - TAD offsets for input
|
||||
// tadOnlyOutputShapeInfo - TAD output shape
|
||||
// tadOutputOffsets - TAD output offsets
|
||||
// numTads - number of subarrays
|
||||
// inputLength - input subarray length
|
||||
//
|
||||
template <typename T>
|
||||
static SD_KERNEL void matrixBandKernel(const void* inputBuffer, const LongType* inputShape, void* outputBuffer,
|
||||
const LongType* outputShape, LongType lowerBand, LongType upperBand,
|
||||
const LongType* tadOnlyInputShapeInfo, const LongType* tadInputOffsets,
|
||||
const LongType* tadOnlyOutputShapeInfo, const LongType* tadOutputOffsets,
|
||||
LongType numTads, LongType inputLength) {
|
||||
int totalThreads = blockDim.x;
|
||||
LongType rows = shape::sizeAt(inputShape, -2);
|
||||
LongType cols = shape::sizeAt(inputShape, -1);
|
||||
auto resetBuffer = reinterpret_cast<T *>(outputBuffer);
|
||||
auto input = reinterpret_cast<T const *>(inputBuffer);
|
||||
|
||||
for (LongType e = blockIdx.x; e < numTads; e += gridDim.x) {
|
||||
auto yOffset = tadInputOffsets[e];
|
||||
auto xOffset = tadOutputOffsets[e];
|
||||
if (outputBuffer != inputBuffer) // if not inplace
|
||||
for(int i = 0; i < inputLength; i++) {
|
||||
resetBuffer[i] = input[i];
|
||||
}
|
||||
for (LongType i = blockIdx.y; i < rows; i += gridDim.y) {
|
||||
for (LongType j = threadIdx.x; j < cols; j += totalThreads) {
|
||||
LongType coords[2] = {i, j};
|
||||
LongType tadOffsetOut, tadOffsetIn;
|
||||
COORDS2INDEX(shape::rank(tadOnlyOutputShapeInfo), shape::stride(tadOnlyOutputShapeInfo), coords, tadOffsetOut);
|
||||
COORDS2INDEX(shape::rank(tadOnlyInputShapeInfo), shape::stride(tadOnlyInputShapeInfo), coords, tadOffsetIn);
|
||||
|
||||
// If not inplace, copy the input to the output
|
||||
*(resetBuffer + xOffset + tadOffsetOut) = *(input + yOffset + tadOffsetIn);
|
||||
|
||||
// Check the lower diagonals
|
||||
if (lowerBand >= 0 && (i - j) > lowerBand)
|
||||
*(resetBuffer + xOffset + tadOffsetOut) = T(0);
|
||||
|
||||
// Check the upper diagonals
|
||||
if (upperBand >= 0 && (j - i) > upperBand)
|
||||
*(resetBuffer + xOffset + tadOffsetOut) = T(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// matrixBandPart_ - main algorithm caller
|
||||
//
|
||||
template <typename T>
|
||||
void matrixBandPart_(LaunchContext* context, NDArray* input, NDArray* output, LongType lowerBand, LongType upperBand) {
|
||||
dim3 launchDims = getLaunchDims("matrixBand");
|
||||
auto stream = context->getCudaStream();
|
||||
|
||||
std::vector<LongType> lastDims({input->rankOf() - 2, input->rankOf() - 1});
|
||||
std::vector<LongType> *dimsToExclude = ShapeUtils::evalDimsToExclude(input->rankOf(), lastDims.size(),lastDims.data());
|
||||
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), &lastDims);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), &lastDims);
|
||||
|
||||
const LongType numTads = packX->numberOfTads();
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
matrixBandKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(), lowerBand,
|
||||
upperBand, packX->specialShapeInfo(), packX->specialOffsets(), packZ->specialShapeInfo(), packZ->specialOffsets(),
|
||||
numTads, input->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "matrixBandKernel failed");
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
|
||||
delete dimsToExclude;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void matrixBandPart(LaunchContext* context, NDArray* input, NDArray* output, LongType lowerBand, LongType upperBand) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), matrixBandPart_, (context, input, output, lowerBand, upperBand),
|
||||
SD_FLOAT_TYPES);
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
// Created by GS <sgazeos@gmail.com> on 3/21/2018.
|
||||
//
|
||||
#include <array/ResultSet.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/matrix_diag_part.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// put diagonals from input batched matrices to output batched vectors
|
||||
template <typename T>
|
||||
static SD_KERNEL void matrixDiagPartKernel(void* inputBuffer, void* outputBuffer, LongType numTads,
|
||||
LongType inputLength, LongType* tadOnlyInputShapeInfo,
|
||||
LongType* tadInputOffsets,
|
||||
LongType* tadOnlyOutputShapeInfo,
|
||||
LongType* tadOutputOffsets) {
|
||||
|
||||
if(blockIdx.x >= numTads)
|
||||
return;
|
||||
auto outputBuffer2 = reinterpret_cast<T*>(outputBuffer);
|
||||
auto inputBuffer2 = reinterpret_cast<T const*>(inputBuffer);
|
||||
|
||||
int totalThreads = blockDim.x;
|
||||
for (LongType i = blockIdx.x; i < numTads; i += gridDim.x) {
|
||||
auto yOffset = tadInputOffsets[i];
|
||||
auto xOffset = tadOutputOffsets[i];
|
||||
for (LongType j = threadIdx.x; j < inputLength; j += totalThreads) {
|
||||
LongType coords[2] = {j, j};
|
||||
LongType tadOffset, indexOffset;
|
||||
COORDS2INDEX(shape::rank(tadOnlyInputShapeInfo), shape::stride(tadOnlyInputShapeInfo), coords, tadOffset);
|
||||
COORDS2INDEX(shape::rank(tadOnlyOutputShapeInfo), shape::stride(tadOnlyOutputShapeInfo), coords, indexOffset);
|
||||
*(reinterpret_cast<T*>(outputBuffer) + xOffset + indexOffset) =
|
||||
*(reinterpret_cast<T const*>(inputBuffer) + yOffset + tadOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// 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 Status _matrixDiagPart(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
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 Status::VALIDATION;
|
||||
}
|
||||
LongType lastDimension = math::sd_min(input->sizeAt(-2), input->sizeAt(-1));
|
||||
|
||||
LongType dims = output->rankOf() - 1;
|
||||
std::vector<LongType> *dimsToExclude = ShapeUtils::evalDimsToExclude(output->rankOf(), 1,&dims);
|
||||
const LongType numTads =
|
||||
ShapeUtils::getNumOfSubArrs(input->shapeInfo(),*dimsToExclude);
|
||||
std::vector<LongType> outputDims({output->rankOf() - 1});
|
||||
std::vector<LongType> inputDims({input->rankOf() - 2, input->rankOf() - 1});
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), &inputDims);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), &outputDims);
|
||||
|
||||
if (!output->isActualOnDeviceSide()) input->syncToDevice();
|
||||
|
||||
if (!input->isActualOnDeviceSide()) input->syncToDevice();
|
||||
|
||||
dim3 launchDims = getLaunchDims("matrixDiag");
|
||||
matrixDiagPartKernel<T><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(),
|
||||
output->specialBuffer(),numTads, lastDimension, const_cast<sd::LongType *>(packX->specialShapeInfo()),
|
||||
const_cast<sd::LongType *>(packX->specialOffsets()),
|
||||
const_cast<sd::LongType *>(packZ->specialShapeInfo()), const_cast<sd::LongType *>(packZ->specialOffsets()));
|
||||
|
||||
sd::DebugHelper::checkErrorCode(stream, "matrixDiagPartKernel failed");
|
||||
|
||||
delete dimsToExclude;
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// caller for _matrixDiagPart
|
||||
//
|
||||
Status matrixDiagPart(LaunchContext* context, NDArray* input, NDArray* output) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), return _matrixDiagPart, (context, input, output), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( sd::Status _matrixDiagPart,
|
||||
(sd::LaunchContext * context, NDArray* input, NDArray* output), SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 raver119@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <ops/declarable/helpers/max_pooling.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename Z>
|
||||
static SD_KERNEL void indicesFiller(void* vz, const LongType* zShapeInfo, LongType part, LongType bSize) {
|
||||
auto z = reinterpret_cast<Z*>(vz);
|
||||
|
||||
__shared__ int rank;
|
||||
__shared__ const LongType *shape, *stride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(zShapeInfo);
|
||||
shape = shape::shapeOf(zShapeInfo);
|
||||
stride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (LongType b = blockIdx.x; b < bSize; b += gridDim.x) {
|
||||
for (LongType e = threadIdx.x; e < part; e += blockDim.x) {
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
|
||||
// Compute coordinates and offset
|
||||
INDEX2COORDS(e + b * part, rank, shape, zCoords);
|
||||
COORDS2INDEX(rank, stride, zCoords, zOffset);
|
||||
|
||||
// Assign the index value
|
||||
z[zOffset] = static_cast<Z>(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Y>
|
||||
static void maxPoolingFunctor_(graph::Context& block, NDArray* input, NDArray* values,
|
||||
std::vector<LongType> const& params, NDArray* indices) {
|
||||
LongType kY = params[0];
|
||||
LongType kX = params[1];
|
||||
|
||||
LongType sY = params[2];
|
||||
LongType sX = params[3];
|
||||
|
||||
LongType pY = params[4];
|
||||
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, MAX_POOL, 1);
|
||||
|
||||
if (nullptr != indices) {
|
||||
// for max_pool_with_argmax
|
||||
auto total = input->lengthOf();
|
||||
auto part = total / bSize;
|
||||
|
||||
indicesFiller<Y><<<256, 256, 1024, *block.launchContext()->getCudaStream()>>>(
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), part, bSize);
|
||||
|
||||
sd::DebugHelper::checkErrorCode(block.launchContext()->getCudaStream(), "indicesFiller failed");
|
||||
}
|
||||
}
|
||||
|
||||
void maxPoolingFunctor(LaunchContext* context, graph::Context& block, NDArray* input, NDArray* values,
|
||||
std::vector<LongType> const& params, NDArray* indices) {
|
||||
NDArray::prepareSpecialUse({values, indices}, {input});
|
||||
auto yType = indices == nullptr ? INT64 : indices->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), yType, maxPoolingFunctor_, (block, input, values, params, indices),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({values, indices}, {input});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,105 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <system/op_boilerplate.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
void maximumBPFunctor_(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(x, y, lambdaX, gradX);
|
||||
|
||||
// Y gradient
|
||||
epsNext->applyTriplewiseLambda(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.f;
|
||||
// scalar case
|
||||
auto tmp = epsNext->reduceNumber(reduce::Sum);
|
||||
if (x <= y)
|
||||
gradY->assign(&tmp);
|
||||
else
|
||||
gradY->assign(zero);
|
||||
|
||||
epsNext->applyPairwiseLambda(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* targetShapePtr = epsNext->getShapeAsVector();
|
||||
std::vector<LongType> targetShape = *targetShapePtr;
|
||||
delete targetShapePtr;
|
||||
|
||||
preX.tileToShape(targetShape, preX);
|
||||
preY.tileToShape(targetShape, preY);
|
||||
|
||||
epsNext->applyTriplewiseLambda(&preX, &preY, lambdaX, &preX);
|
||||
epsNext->applyTriplewiseLambda(&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);
|
||||
} else
|
||||
gradY->assign(&preY);
|
||||
}
|
||||
}
|
||||
|
||||
void maximumBPFunctor(LaunchContext* context, NDArray* x, NDArray* y, NDArray* epsNext, NDArray* gradX,
|
||||
NDArray* gradY) {
|
||||
NDArray::prepareSpecialUse({gradX, gradY}, {x, y, epsNext});
|
||||
|
||||
BUILD_SINGLE_SELECTOR(x->dataType(), maximumBPFunctor_, (x, y, epsNext, gradX, gradY), SD_NUMERIC_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({gradX, gradY}, {x, y, epsNext});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,735 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, typename Z>
|
||||
static SD_KERNEL void mergeMaxIndexCudaLauncher(void** inArrs, void** inShapes, const int numArrays, void* voutput,
|
||||
const LongType* outputShape, LongType length) {
|
||||
auto output = reinterpret_cast<Z*>(voutput);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ int rankOutput;
|
||||
__shared__ const LongType *shapeOutput, *strideOutput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rankOutput = shape::rank(outputShape);
|
||||
shapeOutput = shape::shapeOf(outputShape);
|
||||
strideOutput = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
|
||||
for (LongType e = tid; e < length; e += step) {
|
||||
T mVal = -DataTypeUtils::max<T>();
|
||||
Z mIdx(0);
|
||||
|
||||
// Iterate through all input arrays to find the maximum value and its index
|
||||
for (int i = 0; i < numArrays; ++i) {
|
||||
auto x = reinterpret_cast<const T*>(inArrs[i]);
|
||||
auto xShape = reinterpret_cast<const LongType*>(inShapes[i]);
|
||||
|
||||
__shared__ int rankInput;
|
||||
__shared__ const LongType *shapeInput, *strideInput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rankInput = shape::rank(xShape);
|
||||
shapeInput = shape::shapeOf(xShape);
|
||||
strideInput = shape::stride(xShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
|
||||
// Compute input coordinates and offset
|
||||
INDEX2COORDS(e, rankInput, shapeInput, xCoords);
|
||||
COORDS2INDEX(rankInput, strideInput, xCoords, xOffset);
|
||||
|
||||
// Update maximum value and index
|
||||
const auto val = x[xOffset];
|
||||
if (mVal < val) {
|
||||
mIdx = static_cast<Z>(i);
|
||||
mVal = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute output coordinates and offset
|
||||
LongType outputOffset;
|
||||
INDEX2COORDS(e, rankOutput, shapeOutput, outputCoords);
|
||||
COORDS2INDEX(rankOutput, strideOutput, outputCoords, outputOffset);
|
||||
|
||||
// Store the index of the maximum value in the output
|
||||
output[outputOffset] = mIdx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Z>
|
||||
static void mergeMaxIndex_(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
int nArrSize = static_cast<int>(inArrs.size());
|
||||
std::vector<const void*> inBuffers(nArrSize), inShapes(nArrSize);
|
||||
|
||||
for (int e = 0; e < nArrSize; e++) {
|
||||
inBuffers[e] = inArrs[e]->specialBuffer();
|
||||
inShapes[e] = inArrs[e]->specialShapeInfo();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "mergeMaxIndex");
|
||||
|
||||
auto pInBuffers =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(inBuffers.data(), inBuffers.size() * sizeof(void*)));
|
||||
auto pInShapes = reinterpret_cast<void**>(manager.replicatePointer(inShapes.data(), inShapes.size() * sizeof(void*)));
|
||||
auto length = output.lengthOf();
|
||||
|
||||
dim3 mergeLaunchDims = mergeDims(length);
|
||||
mergeMaxIndexCudaLauncher<T, Z><<<mergeLaunchDims.y, mergeLaunchDims.x, mergeLaunchDims.z, *context->getCudaStream()>>>(
|
||||
pInBuffers, pInShapes, nArrSize, output.specialBuffer(), output.specialShapeInfo(), length);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "mergeMaxIndexCudaLauncher failed");
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
void mergeMaxIndex(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
NDArray::prepareSpecialUse({&output}, inArrs);
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(inArrs[0]->dataType(), output.dataType(), mergeMaxIndex_, (context, inArrs, output),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({&output}, inArrs);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void mergeMaxCudaLauncher(void** inArrs, void** inShapes, const int numArrays, void* voutput,
|
||||
const LongType* outputShape, LongType length) {
|
||||
auto output = reinterpret_cast<T*>(voutput);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ int rankOutput;
|
||||
__shared__ const LongType *shapeOutput, *strideOutput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rankOutput = shape::rank(outputShape);
|
||||
shapeOutput = shape::shapeOf(outputShape);
|
||||
strideOutput = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
|
||||
for (LongType e = tid; e < length; e += step) {
|
||||
T mVal = -DataTypeUtils::max<T>();
|
||||
|
||||
// Iterate through all input arrays to find the maximum value
|
||||
for (int i = 0; i < numArrays; ++i) {
|
||||
auto x = reinterpret_cast<const T*>(inArrs[i]);
|
||||
auto xShape = reinterpret_cast<const LongType*>(inShapes[i]);
|
||||
|
||||
__shared__ int rankInput;
|
||||
__shared__ const LongType *shapeInput, *strideInput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rankInput = shape::rank(xShape);
|
||||
shapeInput = shape::shapeOf(xShape);
|
||||
strideInput = shape::stride(xShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
|
||||
// Compute input coordinates and offset
|
||||
INDEX2COORDS(e, rankInput, shapeInput, xCoords);
|
||||
COORDS2INDEX(rankInput, strideInput, xCoords, xOffset);
|
||||
|
||||
// Update maximum value
|
||||
const auto val = x[xOffset];
|
||||
if (mVal < val) {
|
||||
mVal = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute output coordinates and offset
|
||||
LongType outputOffset;
|
||||
INDEX2COORDS(e, rankOutput, shapeOutput, outputCoords);
|
||||
COORDS2INDEX(rankOutput, strideOutput, outputCoords, outputOffset);
|
||||
|
||||
// Store the maximum value in the output
|
||||
output[outputOffset] = mVal;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void mergeMax_(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
int nArrsSize = static_cast<int>(inArrs.size());
|
||||
|
||||
std::vector<const void*> inBuffers(nArrsSize), inShapes(nArrsSize);
|
||||
|
||||
for (int e = 0; e < nArrsSize; e++) {
|
||||
inBuffers[e] = inArrs[e]->specialBuffer();
|
||||
inShapes[e] = inArrs[e]->specialShapeInfo();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "mergeMax");
|
||||
|
||||
auto pInBuffers =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(inBuffers.data(), inBuffers.size() * sizeof(void*)));
|
||||
auto pInShapes = reinterpret_cast<void**>(manager.replicatePointer(inShapes.data(), inShapes.size() * sizeof(void*)));
|
||||
auto length = output.lengthOf();
|
||||
|
||||
dim3 mergeLaunchDims = mergeDims(length);
|
||||
mergeMaxCudaLauncher<T><<<mergeLaunchDims.y, mergeLaunchDims.x, mergeLaunchDims.z, *context->getCudaStream()>>>(
|
||||
pInBuffers, pInShapes, nArrsSize, output.specialBuffer(), output.specialShapeInfo(), length);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "mergeMaxCudaLauncher failed");
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
void mergeMax(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
NDArray::prepareSpecialUse({&output}, inArrs);
|
||||
|
||||
BUILD_SINGLE_SELECTOR(output.dataType(), mergeMax_, (context, inArrs, output), SD_COMMON_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({&output}, inArrs);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void mergeMaxBpCudaLauncher(void** inArrs, void** inShapes, const void* vgradient,
|
||||
const LongType* gradientShape, const int numArrays, void** outArrs,
|
||||
void** outShapes, LongType length, bool bSameOrderAndEws1) {
|
||||
const auto grad = reinterpret_cast<const T*>(vgradient);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ int gradRank;
|
||||
__shared__ const LongType *gradShape, *gradStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
gradRank = shape::rank(gradientShape);
|
||||
gradShape = shape::shapeOf(gradientShape);
|
||||
gradStride = shape::stride(gradientShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
for (LongType e = tid; e < length; e += step) {
|
||||
T mVal = -DataTypeUtils::max<T>();
|
||||
int nMaxIndex = 0;
|
||||
LongType gradOffset = bSameOrderAndEws1 ? e : 0;
|
||||
|
||||
// Compute gradient offset if not same order and EWS=1
|
||||
if (!bSameOrderAndEws1) {
|
||||
INDEX2COORDS(e, gradRank, gradShape, coords);
|
||||
COORDS2INDEX(gradRank, gradStride, coords, gradOffset);
|
||||
}
|
||||
|
||||
// Find the maximum value and its index across all input arrays
|
||||
for (int i = 0; i < numArrays; ++i) {
|
||||
auto x = reinterpret_cast<T*>(inArrs[i]);
|
||||
LongType xOffset = bSameOrderAndEws1 ? e : 0;
|
||||
|
||||
if (!bSameOrderAndEws1) {
|
||||
auto xShape = reinterpret_cast<const LongType*>(inShapes[i]);
|
||||
COORDS2INDEX(shape::rank(xShape), shape::stride(xShape), coords, xOffset);
|
||||
}
|
||||
|
||||
const auto val = x[xOffset];
|
||||
if (mVal < val) {
|
||||
mVal = val;
|
||||
nMaxIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign gradient to the corresponding output array at the max index
|
||||
auto output = reinterpret_cast<T*>(outArrs[nMaxIndex]);
|
||||
LongType zOffset = bSameOrderAndEws1 ? e : 0;
|
||||
|
||||
if (!bSameOrderAndEws1) {
|
||||
auto outShape = reinterpret_cast<const LongType*>(outShapes[nMaxIndex]);
|
||||
COORDS2INDEX(shape::rank(outShape), shape::stride(outShape), coords, zOffset);
|
||||
}
|
||||
|
||||
output[zOffset] = grad[gradOffset];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static void mergeMaxBp_(LaunchContext* context, const std::vector<NDArray*>& inArrs,
|
||||
std::vector<NDArray*>& outArrs, int nArrSize, bool bSameOrderAndEws1) {
|
||||
std::vector<const void*> inBuffers(nArrSize), inShapes(nArrSize), outBuffers(nArrSize), outShapes(nArrSize);
|
||||
|
||||
for (int e = 0; e < nArrSize; e++) {
|
||||
inBuffers[e] = inArrs[e]->specialBuffer();
|
||||
inShapes[e] = inArrs[e]->specialShapeInfo();
|
||||
outBuffers[e] = outArrs[e]->specialBuffer();
|
||||
outShapes[e] = outArrs[e]->specialShapeInfo();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "mergeMaxBp");
|
||||
|
||||
auto pInBuffers =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(inBuffers.data(), inBuffers.size() * sizeof(void*)));
|
||||
auto pInShapes = reinterpret_cast<void**>(manager.replicatePointer(inShapes.data(), inShapes.size() * sizeof(void*)));
|
||||
|
||||
auto pOutBuffers =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(outBuffers.data(), outBuffers.size() * sizeof(void*)));
|
||||
auto pOutShapes =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(outShapes.data(), outShapes.size() * sizeof(void*)));
|
||||
|
||||
auto length = inArrs[nArrSize]->lengthOf();
|
||||
|
||||
dim3 mergeLaunchDims = mergeDims(length);
|
||||
|
||||
mergeMaxBpCudaLauncher<T><<<mergeLaunchDims.y, mergeLaunchDims.x, mergeLaunchDims.z, *context->getCudaStream()>>>(
|
||||
pInBuffers, pInShapes, inArrs[nArrSize]->specialBuffer(), inArrs[nArrSize]->specialShapeInfo(), nArrSize,
|
||||
pOutBuffers, pOutShapes, length, bSameOrderAndEws1);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "mergeMaxBpCudaLauncher failed");
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
void mergeMaxBp(LaunchContext* context, const std::vector<NDArray*>& inArrs, std::vector<NDArray*>& outArrs) {
|
||||
// not use gradient
|
||||
int nArrSize = static_cast<int>(inArrs.size() - 1);
|
||||
|
||||
const std::vector<NDArray*>& out = reinterpret_cast<const std::vector<NDArray*>&>(outArrs);
|
||||
|
||||
NDArray::prepareSpecialUse(out, inArrs);
|
||||
|
||||
bool bSameOrderAndEws1 = false;
|
||||
auto ordering = inArrs[nArrSize]->ordering();
|
||||
|
||||
|
||||
BUILD_SINGLE_SELECTOR(inArrs[nArrSize]->dataType(), mergeMaxBp_,
|
||||
(context, inArrs, outArrs, nArrSize, bSameOrderAndEws1), SD_COMMON_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse(out, inArrs);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void mergeAvgCudaLauncher(void** inArrs, void** inShapes, const int numArrays, void* voutput,
|
||||
const LongType* outputShape, LongType length) {
|
||||
auto output = reinterpret_cast<T*>(voutput);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ int rankOutput;
|
||||
__shared__ const LongType *shapeOutput, *strideOutput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rankOutput = shape::rank(outputShape);
|
||||
shapeOutput = shape::shapeOf(outputShape);
|
||||
strideOutput = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
|
||||
for (LongType e = tid; e < length; e += step) {
|
||||
T sum = static_cast<T>(0.0);
|
||||
|
||||
// Sum values from all input arrays
|
||||
for (int i = 0; i < numArrays; ++i) {
|
||||
auto x = reinterpret_cast<T*>(inArrs[i]);
|
||||
auto xShape = reinterpret_cast<const LongType*>(inShapes[i]);
|
||||
|
||||
__shared__ int rankInput;
|
||||
__shared__ const LongType *shapeInput, *strideInput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rankInput = shape::rank(xShape);
|
||||
shapeInput = shape::shapeOf(xShape);
|
||||
strideInput = shape::stride(xShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
|
||||
// Compute input coordinates and offset
|
||||
INDEX2COORDS(e, rankInput, shapeInput, xCoords);
|
||||
COORDS2INDEX(rankInput, strideInput, xCoords, xOffset);
|
||||
|
||||
sum += x[xOffset];
|
||||
}
|
||||
|
||||
// Compute output coordinates and offset
|
||||
LongType outputOffset;
|
||||
INDEX2COORDS(e, rankOutput, shapeOutput, outputCoords);
|
||||
COORDS2INDEX(rankOutput, strideOutput, outputCoords, outputOffset);
|
||||
|
||||
// Store the averaged value in the output
|
||||
output[outputOffset] = sum / static_cast<T>(numArrays);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void mergeAvg_(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
std::vector<const void*> inBuffers(inArrs.size()), inShapes(inArrs.size());
|
||||
|
||||
for (int e = 0; e < inArrs.size(); e++) {
|
||||
inBuffers[e] = inArrs[e]->specialBuffer();
|
||||
inShapes[e] = inArrs[e]->specialShapeInfo();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "mergeAvg");
|
||||
|
||||
auto pInBuffers =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(inBuffers.data(), inBuffers.size() * sizeof(void*)));
|
||||
auto pInShapes = reinterpret_cast<void**>(manager.replicatePointer(inShapes.data(), inShapes.size() * sizeof(void*)));
|
||||
auto length = output.lengthOf();
|
||||
|
||||
dim3 mergeLaunchDims = mergeDims(length);
|
||||
|
||||
mergeAvgCudaLauncher<T><<<mergeLaunchDims.y, mergeLaunchDims.x, mergeLaunchDims.z, *context->getCudaStream()>>>(
|
||||
pInBuffers, pInShapes, (int)inArrs.size(), output.specialBuffer(), output.specialShapeInfo(), length);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "mergeAvgCudaLauncher failed");
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
void mergeAvg(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
NDArray::prepareSpecialUse({&output}, inArrs);
|
||||
|
||||
BUILD_SINGLE_SELECTOR(output.dataType(), mergeAvg_, (context, inArrs, output), SD_FLOAT_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({&output}, inArrs);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void mergeAvgBpCudaLauncher(const void* vgradient, const LongType* gradientShape, void** outArrs,
|
||||
void** outShapes, const int numArrays, LongType length,
|
||||
bool bSameOrderAndEws1) {
|
||||
const auto grad = reinterpret_cast<const T*>(vgradient);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ int gradRank;
|
||||
__shared__ const LongType *gradShape, *gradStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
gradRank = shape::rank(gradientShape);
|
||||
gradShape = shape::shapeOf(gradientShape);
|
||||
gradStride = shape::stride(gradientShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
for (LongType e = tid; e < length; e += step) {
|
||||
LongType gradOffset = bSameOrderAndEws1 ? e : 0;
|
||||
|
||||
// Compute gradient offset if not using the same order and EWS=1
|
||||
if (!bSameOrderAndEws1) {
|
||||
INDEX2COORDS(e, gradRank, gradShape, coords);
|
||||
COORDS2INDEX(gradRank, gradStride, coords, gradOffset);
|
||||
}
|
||||
|
||||
// Iterate through each output array and compute the average gradient
|
||||
for (int i = 0; i < numArrays; ++i) {
|
||||
auto output = reinterpret_cast<T*>(outArrs[i]);
|
||||
LongType zOffset = bSameOrderAndEws1 ? e : 0;
|
||||
|
||||
if (!bSameOrderAndEws1) {
|
||||
auto outShape = reinterpret_cast<const LongType*>(outShapes[i]);
|
||||
COORDS2INDEX(shape::rank(outShape), shape::stride(outShape), coords, zOffset);
|
||||
}
|
||||
|
||||
// Assign averaged gradient value to output
|
||||
output[zOffset] = grad[gradOffset] / static_cast<T>(numArrays);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static void mergeAvgBp_(LaunchContext* context, NDArray& gradient, std::vector<NDArray*>& outArrs,
|
||||
bool bSameOrderAndEws1) {
|
||||
int nArrSize = static_cast<int>(outArrs.size());
|
||||
|
||||
std::vector<const void*> outBuffers(nArrSize), outShapes(nArrSize);
|
||||
|
||||
for (int e = 0; e < nArrSize; e++) {
|
||||
outBuffers[e] = outArrs[e]->specialBuffer();
|
||||
outShapes[e] = outArrs[e]->specialShapeInfo();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "mergeAvgBp");
|
||||
|
||||
auto pOutBuffers =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(outBuffers.data(), outBuffers.size() * sizeof(void*)));
|
||||
auto pOutShapes =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(outShapes.data(), outShapes.size() * sizeof(void*)));
|
||||
|
||||
auto length = gradient.lengthOf();
|
||||
|
||||
dim3 mergeLaunchDims = mergeDims(length);
|
||||
|
||||
mergeAvgBpCudaLauncher<T><<<mergeLaunchDims.y, mergeLaunchDims.x,mergeLaunchDims.z, *context->getCudaStream()>>>(
|
||||
gradient.specialBuffer(), gradient.specialShapeInfo(), pOutBuffers, pOutShapes, nArrSize, length,
|
||||
bSameOrderAndEws1);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "mergeAvgBpCudaLauncher failed");
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
void mergeAvgBp(LaunchContext* context, NDArray& gradient, std::vector<NDArray*>& outArrs) {
|
||||
const std::vector<NDArray*>& out = reinterpret_cast<const std::vector<NDArray*>&>(outArrs);
|
||||
|
||||
NDArray::prepareSpecialUse(out, {&gradient});
|
||||
|
||||
bool bSameOrderAndEws1 = false;
|
||||
auto ordering = gradient.ordering();
|
||||
|
||||
for (const auto& v : outArrs) {
|
||||
bSameOrderAndEws1 &= (ordering == v->ordering());
|
||||
}
|
||||
|
||||
BUILD_SINGLE_SELECTOR(gradient.dataType(), mergeAvgBp_, (context, gradient, outArrs, bSameOrderAndEws1),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
NDArray::prepareSpecialUse(out, {&gradient});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void mergeAddCudaLauncher(void** inArrs, void** inShapes, const int numArrays, void* voutput,
|
||||
const LongType* outputShape, LongType length) {
|
||||
auto output = reinterpret_cast<T*>(voutput);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ int rankOutput;
|
||||
__shared__ const LongType *shapeOutput, *strideOutput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rankOutput = shape::rank(outputShape);
|
||||
shapeOutput = shape::shapeOf(outputShape);
|
||||
strideOutput = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
|
||||
for (LongType e = tid; e < length; e += step) {
|
||||
T sum(0.0f);
|
||||
|
||||
// Compute the sum across all input arrays
|
||||
for (int i = 0; i < numArrays; ++i) {
|
||||
auto x = reinterpret_cast<T*>(inArrs[i]);
|
||||
auto xShape = reinterpret_cast<const LongType*>(inShapes[i]);
|
||||
|
||||
__shared__ int rankInput;
|
||||
__shared__ const LongType *shapeInput, *strideInput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rankInput = shape::rank(xShape);
|
||||
shapeInput = shape::shapeOf(xShape);
|
||||
strideInput = shape::stride(xShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
|
||||
// Compute input coordinates and offset
|
||||
INDEX2COORDS(e, rankInput, shapeInput, xCoords);
|
||||
COORDS2INDEX(rankInput, strideInput, xCoords, xOffset);
|
||||
|
||||
sum += x[xOffset];
|
||||
}
|
||||
|
||||
// Compute output coordinates and offset
|
||||
LongType outputOffset;
|
||||
INDEX2COORDS(e, rankOutput, shapeOutput, outputCoords);
|
||||
COORDS2INDEX(rankOutput, strideOutput, outputCoords, outputOffset);
|
||||
|
||||
// Store the computed sum in the output
|
||||
output[outputOffset] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void mergeAdd_(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
int nArrSize = static_cast<int>(inArrs.size());
|
||||
std::vector<const void*> inBuffers(nArrSize), inShapes(nArrSize);
|
||||
|
||||
for (int e = 0; e < nArrSize; e++) {
|
||||
inBuffers[e] = inArrs[e]->specialBuffer();
|
||||
inShapes[e] = inArrs[e]->specialShapeInfo();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "mergeAdd");
|
||||
|
||||
auto pInBuffers =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(inBuffers.data(), inBuffers.size() * sizeof(void*)));
|
||||
auto pInShapes = reinterpret_cast<void**>(manager.replicatePointer(inShapes.data(), inShapes.size() * sizeof(void*)));
|
||||
auto length = output.lengthOf();
|
||||
|
||||
dim3 mergeLaunchDims = mergeDims(length);
|
||||
|
||||
mergeAddCudaLauncher<T><<<mergeLaunchDims.x, mergeLaunchDims.y, mergeLaunchDims.z, *context->getCudaStream()>>>(
|
||||
pInBuffers, pInShapes, nArrSize, output.specialBuffer(), output.specialShapeInfo(), length);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "mergeAddCudaLauncher failed");
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void mergeAdd_,
|
||||
(sd::LaunchContext * context, const std::vector<NDArray*>& inArrs, NDArray& output),
|
||||
SD_NUMERIC_TYPES);
|
||||
|
||||
void mergeAdd(LaunchContext* context, const std::vector<NDArray*>& inArrs, NDArray& output) {
|
||||
NDArray::prepareSpecialUse({&output}, inArrs);
|
||||
|
||||
BUILD_SINGLE_SELECTOR(output.dataType(), mergeAdd_, (context, inArrs, output), SD_NUMERIC_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({&output}, inArrs);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void mergeAddBpCudaLauncher(const void* vgradient, const LongType* gradientShape, void** outArrs,
|
||||
void** outShapes, const int numArrays, LongType length,
|
||||
bool bSameOrderAndEws1) {
|
||||
const auto grad = reinterpret_cast<const T*>(vgradient);
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ int gradRank;
|
||||
__shared__ const LongType *gradShape, *gradStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
gradRank = shape::rank(gradientShape);
|
||||
gradShape = shape::shapeOf(gradientShape);
|
||||
gradStride = shape::stride(gradientShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
|
||||
for (LongType e = tid; e < length; e += step) {
|
||||
LongType gradOffset = bSameOrderAndEws1 ? e : 0;
|
||||
|
||||
// Compute gradient offset if not using same order and EWS=1
|
||||
if (!bSameOrderAndEws1) {
|
||||
INDEX2COORDS(e, gradRank, gradShape, coords);
|
||||
COORDS2INDEX(gradRank, gradStride, coords, gradOffset);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numArrays; ++i) {
|
||||
auto output = reinterpret_cast<T*>(outArrs[i]);
|
||||
LongType zOffset = bSameOrderAndEws1 ? e : 0;
|
||||
|
||||
// Compute output offset if not using same order and EWS=1
|
||||
if (!bSameOrderAndEws1) {
|
||||
auto outShape = reinterpret_cast<const LongType*>(outShapes[i]);
|
||||
COORDS2INDEX(shape::rank(outShape), shape::stride(outShape), coords, zOffset);
|
||||
}
|
||||
|
||||
// Assign gradient value to output
|
||||
output[zOffset] = grad[gradOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static void mergeAddBp_(LaunchContext* context, NDArray& gradient, std::vector<NDArray*>& outArrs,
|
||||
bool bSameOrderAndEws1) {
|
||||
int nArrSize = static_cast<int>(outArrs.size());
|
||||
|
||||
std::vector<const void*> outBuffers(nArrSize), outShapes(nArrSize);
|
||||
|
||||
for (int e = 0; e < nArrSize; e++) {
|
||||
outBuffers[e] = outArrs[e]->specialBuffer();
|
||||
outShapes[e] = outArrs[e]->specialShapeInfo();
|
||||
}
|
||||
|
||||
PointersManager manager(context, "mergeAddBp");
|
||||
|
||||
auto pOutBuffers =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(outBuffers.data(), outBuffers.size() * sizeof(void*)));
|
||||
auto pOutShapes =
|
||||
reinterpret_cast<void**>(manager.replicatePointer(outShapes.data(), outShapes.size() * sizeof(void*)));
|
||||
|
||||
auto length = gradient.lengthOf();
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (length + threadsPerBlock - 1) / threadsPerBlock;
|
||||
|
||||
mergeAddBpCudaLauncher<T><<<blocksPerGrid, threadsPerBlock, 512, *context->getCudaStream()>>>(
|
||||
gradient.specialBuffer(), gradient.specialShapeInfo(), pOutBuffers, pOutShapes, nArrSize, length,
|
||||
bSameOrderAndEws1);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "mergeAddBpCudaLauncher failed");
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
void mergeAddBp(LaunchContext* context, NDArray& gradient, std::vector<NDArray*>& outArrs) {
|
||||
const std::vector<NDArray*>& out = reinterpret_cast<const std::vector<NDArray*>&>(outArrs);
|
||||
NDArray::prepareSpecialUse(out, {&gradient});
|
||||
|
||||
bool bSameOrderAndEws1 = false;
|
||||
auto ordering = gradient.ordering();
|
||||
|
||||
for (const auto& v : outArrs) {
|
||||
bSameOrderAndEws1 &= (ordering == v->ordering());
|
||||
}
|
||||
|
||||
BUILD_SINGLE_SELECTOR(gradient.dataType(), mergeAddBp_, (context, gradient, outArrs, bSameOrderAndEws1),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
NDArray::prepareSpecialUse(out, {&gradient});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,160 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/ResultSet.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/meshgrid.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_DEVICE void assign_(void *vx, LongType *xShapeInfo, void *vz, LongType *zShapeInfo) {
|
||||
auto x = reinterpret_cast<T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
const auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
const auto step = blockDim.x * gridDim.x;
|
||||
|
||||
__shared__ LongType length, rankX, rankZ;
|
||||
__shared__ const LongType *shapeX, *strideX, *shapeZ, *strideZ;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
length = shape::length(xShapeInfo);
|
||||
rankX = shape::rank(xShapeInfo);
|
||||
rankZ = shape::rank(zShapeInfo);
|
||||
shapeX = shape::shapeOf(xShapeInfo);
|
||||
strideX = shape::stride(xShapeInfo);
|
||||
shapeZ = shape::shapeOf(zShapeInfo);
|
||||
strideZ = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
|
||||
for (LongType i = tid; i < length; i += step) {
|
||||
// Compute input coordinates and offset
|
||||
INDEX2COORDS(i, rankX, shapeX, xCoords);
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rankX, strideX, xCoords, xOffset);
|
||||
|
||||
// Compute output coordinates and offset
|
||||
INDEX2COORDS(i, rankZ, shapeZ, zCoords);
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rankZ, strideZ, zCoords, zOffset);
|
||||
|
||||
// Assign value from input to output
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void meshgridKernel(int rank, void **outBuffers, LongType **tadShapes, LongType **tadOffsets,
|
||||
LongType *numTads, void **inBuffers, LongType **inShapes) {
|
||||
// for all arrays
|
||||
for (int i = blockIdx.x; i < rank; i += gridDim.x) {
|
||||
// for all tads in this array
|
||||
for (LongType j = 0; j < numTads[i]; j++) {
|
||||
assign_<T>(inBuffers[i], inShapes[i], reinterpret_cast<T *>(outBuffers[i]) + tadOffsets[i][j], tadShapes[i]);
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void meshgrid_(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;
|
||||
}
|
||||
|
||||
PointersManager pm(context, "meshgrid");
|
||||
std::vector<const void *> hInBuffers(rank);
|
||||
std::vector<void *> hOutBuffers(rank);
|
||||
std::vector<const LongType *> hInShapes(rank);
|
||||
|
||||
std::vector<const LongType *> hOutTadShapes(rank);
|
||||
std::vector<const LongType *> hOutTadOffsets(rank);
|
||||
|
||||
std::vector<LongType> hNumTads(rank);
|
||||
|
||||
for (int i = 0; i < rank; ++i) {
|
||||
hInBuffers[i] = inArrs[i]->specialBuffer();
|
||||
hInShapes[i] = inArrs[i]->specialShapeInfo();
|
||||
|
||||
hOutBuffers[i] = outArrs[i]->specialBuffer();
|
||||
|
||||
auto pack = ConstantTadHelper::getInstance().tadForDimensions(outArrs[i]->shapeInfo(), {inIndices[i]});
|
||||
hOutTadShapes[i] = pack->specialShapeInfo();
|
||||
hOutTadOffsets[i] = pack->specialOffsets();
|
||||
hNumTads[i] = pack->numberOfTads();
|
||||
|
||||
}
|
||||
|
||||
auto dInBuffers =
|
||||
reinterpret_cast<void **>(pm.replicatePointer(hInBuffers.data(), hInBuffers.size() * sizeof(void *)));
|
||||
auto dOutBuffers =
|
||||
reinterpret_cast<void **>(pm.replicatePointer(hOutBuffers.data(), hOutBuffers.size() * sizeof(void *)));
|
||||
|
||||
auto dInShapes = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(hInShapes.data(), hInShapes.size() * sizeof(LongType *)));
|
||||
auto dOutTadShapes = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(hOutTadShapes.data(), hOutTadShapes.size() * sizeof(LongType *)));
|
||||
auto dOutTadOffsets = reinterpret_cast<LongType **>(
|
||||
pm.replicatePointer(hOutTadOffsets.data(), hOutTadOffsets.size() * sizeof(LongType *)));
|
||||
|
||||
auto dNumTads =
|
||||
reinterpret_cast<LongType *>(pm.replicatePointer(hNumTads.data(), hNumTads.size() * sizeof(LongType)));
|
||||
|
||||
dim3 launchDims = getLaunchDims("meshgrid");
|
||||
meshgridKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *context->getCudaStream()>>>(rank, dOutBuffers, dOutTadShapes, dOutTadOffsets,
|
||||
dNumTads, dInBuffers, dInShapes);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "meshgridKernel failed");
|
||||
|
||||
pm.synchronize();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void meshgrid(LaunchContext *context, const std::vector<NDArray *> &inArrs, const std::vector<NDArray *> &outArrs,
|
||||
const bool swapFirst2Dims) {
|
||||
BUILD_SINGLE_SELECTOR(inArrs.at(0)->dataType(), meshgrid_, (context, inArrs, outArrs, swapFirst2Dims),
|
||||
SD_NUMERIC_TYPES);
|
||||
|
||||
for (auto v : outArrs) v->tickWriteDevice();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,108 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
//
|
||||
#ifndef __MIN_I_MAX_H_HELPERS__
|
||||
#define __MIN_I_MAX_H_HELPERS__
|
||||
#include <array/NDArray.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <system/op_boilerplate.h>
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
void minimumBPFunctor_(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(x, y, lambdaX, gradX);
|
||||
|
||||
// Y gradient
|
||||
epsNext->applyTriplewiseLambda(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.f;
|
||||
// scalar case
|
||||
auto tmp = epsNext->reduceNumber(reduce::Sum);
|
||||
if (x <= y)
|
||||
gradY->assign(&tmp);
|
||||
else
|
||||
gradY->assign(zero);
|
||||
|
||||
epsNext->applyPairwiseLambda(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* targetShapePtr = epsNext->getShapeAsVector();
|
||||
std::vector<LongType> targetShape = *targetShapePtr;
|
||||
delete targetShapePtr;
|
||||
|
||||
preX.tileToShape(targetShape, preX);
|
||||
preY.tileToShape(targetShape, preY);
|
||||
|
||||
epsNext->applyTriplewiseLambda(&preX, &preY, lambdaX, &preX);
|
||||
epsNext->applyTriplewiseLambda(&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);
|
||||
} else
|
||||
gradY->assign(&preY);
|
||||
}
|
||||
}
|
||||
|
||||
void minimumBPFunctor(LaunchContext* context, NDArray* x, NDArray* y, NDArray* epsNext, NDArray* gradX,
|
||||
NDArray* gradY) {
|
||||
NDArray::prepareSpecialUse({gradX, gradY}, {x, y, epsNext});
|
||||
|
||||
BUILD_SINGLE_SELECTOR(x->dataType(), minimumBPFunctor_, (x, y, epsNext, gradX, gradY), SD_NUMERIC_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({gradX, gradY}, {x, y, epsNext});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <legacy/NativeOps.h>
|
||||
#include <ops/declarable/helpers/nth_element.h>
|
||||
|
||||
#include "array/NDArrayFactory.h"
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void fillUpElementKernel(void* outputBuffer, const LongType* outputShapeInfo, void* inputBuffer,
|
||||
const LongType* inputShapeInfo, const LongType* pTadShape,
|
||||
const LongType* pTadOffsets, LongType n) {
|
||||
__shared__ LongType bufferLength;
|
||||
__shared__ int rankOutput, rankTad;
|
||||
__shared__ const LongType *shapeOutput, *strideOutput, *shapeTad, *strideTad;
|
||||
|
||||
auto z = reinterpret_cast<T*>(outputBuffer);
|
||||
auto x = reinterpret_cast<T*>(inputBuffer);
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
bufferLength = shape::length(outputShapeInfo);
|
||||
rankOutput = shape::rank(outputShapeInfo);
|
||||
rankTad = shape::rank(pTadShape);
|
||||
shapeOutput = shape::shapeOf(outputShapeInfo);
|
||||
strideOutput = shape::stride(outputShapeInfo);
|
||||
shapeTad = shape::shapeOf(pTadShape);
|
||||
strideTad = shape::stride(pTadShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
|
||||
for (LongType t = tid; t < bufferLength; t += step) {
|
||||
// Compute output coordinates and offset
|
||||
INDEX2COORDS(t, rankOutput, shapeOutput, zCoords);
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rankOutput, strideOutput, zCoords, zOffset);
|
||||
|
||||
// Compute input coordinates and offset
|
||||
INDEX2COORDS(n, rankTad, shapeTad, xCoords);
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rankTad, strideTad, xCoords, xOffset);
|
||||
|
||||
// Access and assign the value
|
||||
z[zOffset] = x[pTadOffsets[t] + xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void nthElementFunctor_(LaunchContext* context, NDArray* input, LongType n, NDArray* output, bool reverse) {
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
NDArray sortedVals(*input);
|
||||
Pointer params[2];
|
||||
params[0] = context;
|
||||
params[1] = context->getCudaStream();
|
||||
// Nth element in sorted sequence : basic algorithm sort and retrieve nth element in sorted
|
||||
if (input->isVector()) {
|
||||
sort(params, &sortedVals, reverse);
|
||||
|
||||
cudaMemcpy(reinterpret_cast<T*>(output->specialBuffer()), reinterpret_cast<T*>(sortedVals.specialBuffer()) + n,
|
||||
sizeof(T), cudaMemcpyDeviceToDevice);
|
||||
} else { // rank greater than 1
|
||||
std::vector<LongType> lastDims(
|
||||
{input->rankOf() - 1});
|
||||
NDArray *dimData = NDArrayFactory::create_<LongType>('c',{2},lastDims, context);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(sortedVals.shapeInfo(), &lastDims);
|
||||
|
||||
auto pTadShape = packX->specialShapeInfo();
|
||||
auto pTadShapeH = packX->primaryShapeInfo();
|
||||
auto pTadOffsets = packX->specialOffsets();
|
||||
sortTad(params, &sortedVals,
|
||||
reinterpret_cast<sd::LongType *>(lastDims.data()),
|
||||
lastDims.size(),
|
||||
const_cast<sd::LongType *>(pTadShape),
|
||||
const_cast<sd::LongType *>(pTadOffsets),
|
||||
reverse);
|
||||
sortedVals.tickWriteDevice();
|
||||
sortedVals.syncToHost();
|
||||
auto stream = context->getCudaStream();
|
||||
dim3 launchDims = getLaunchDims("nth_element_fill");
|
||||
fillUpElementKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(output->specialBuffer(), output->specialShapeInfo(),
|
||||
sortedVals.specialBuffer(), sortedVals.specialShapeInfo(),
|
||||
pTadShape, pTadOffsets, n);
|
||||
sd::DebugHelper::checkErrorCode(stream, "fillUpElementKernel failed");
|
||||
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
void nthElementFunctor(LaunchContext* context, NDArray* input, LongType n, NDArray* output, bool reverse) {
|
||||
auto inputDType = input->dataType();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), nthElementFunctor_, (context, input, n, output, reverse), SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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 Yurii Shyrma (iuriish@yahoo.com), created on 30.05.2019
|
||||
//
|
||||
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/one_hot.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - indices, z - output
|
||||
template <typename X, typename Z>
|
||||
SD_KERNEL static void onehotCuda(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, const LongType axis, const LongType depth,
|
||||
const Z on, const Z off) {
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
auto z = reinterpret_cast<Z *>(vz);
|
||||
|
||||
__shared__ int xRank, zRank;
|
||||
__shared__ LongType zLen, totalThreads;
|
||||
__shared__ const LongType *xShape, *xStride, *zShape, *zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
zLen = shape::length(zShapeInfo);
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
LongType coord[SD_MAX_RANK];
|
||||
|
||||
for (LongType i = tid; i < zLen; i += totalThreads) {
|
||||
// Compute output coordinate and offset
|
||||
INDEX2COORDS(i, zRank, zShape, coord);
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(zRank, zStride, coord, zOffset);
|
||||
|
||||
// Extract depth coordinate and shift axis
|
||||
const auto depthCoord = coord[axis];
|
||||
for (LongType j = axis; j < zRank - 1; ++j) {
|
||||
coord[j] = coord[j + 1];
|
||||
}
|
||||
|
||||
// Compute input offset
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStride, coord, xOffset);
|
||||
|
||||
// Check if the depth matches the index
|
||||
const LongType idx = static_cast<LongType>(x[xOffset]);
|
||||
z[zOffset] = (depthCoord == idx) ? on : off;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void onehotCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, const LongType axis, const LongType depth,
|
||||
const double on, const double off) {
|
||||
onehotCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, axis, depth,
|
||||
static_cast<Y>(on), static_cast<Y>(off));
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "onehotCuda failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
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();
|
||||
|
||||
dim3 oneHotLaunch = oneHotDims(output->lengthOf(),output->rankOf(), sizeof(decltype(*output->shapeInfo())));
|
||||
PointersManager manager(context, "onehot");
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {indices});
|
||||
BUILD_DOUBLE_SELECTOR(
|
||||
xType, zType, onehotCudaLauncher,
|
||||
(oneHotLaunch.y, oneHotLaunch.x, oneHotLaunch.z, context->getCudaStream(), indices->specialBuffer(),
|
||||
indices->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(), axis, depth, on, off),
|
||||
SD_COMMON_TYPES, SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {indices});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,322 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - input, y - paddings, z - output
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void padCuda(const int mode, const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const void* vPadVal) {
|
||||
const X padVal = *reinterpret_cast<const X*>(vPadVal);
|
||||
|
||||
const auto x = reinterpret_cast<const X*>(vx);
|
||||
const auto y = reinterpret_cast<const Y*>(vy);
|
||||
auto z = reinterpret_cast<X*>(vz);
|
||||
|
||||
__shared__ int rank, rankMinusOne;
|
||||
__shared__ LongType zLen, totalThreads;
|
||||
__shared__ const LongType *xShape, *zShape, *xStride, *zStride;
|
||||
__shared__ LongType yStride0, shift1, shift2;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(xShapeInfo);
|
||||
rankMinusOne = rank - 1;
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
yStride0 = shape::stride(yShapeInfo)[0];
|
||||
zLen = shape::length(zShapeInfo);
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
shift1 = (mode == 1) ? 0 : 1; // REFLECT : SYMMETRIC
|
||||
shift2 = (mode == 1) ? 2 : 1; // REFLECT : SYMMETRIC
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = totalThreads;
|
||||
|
||||
LongType xzCoord[SD_MAX_RANK];
|
||||
|
||||
for (LongType i = start; i < zLen; i += step) {
|
||||
// Compute output coordinate and offset
|
||||
INDEX2COORDS(i, rank, zShape, xzCoord);
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, xzCoord, zOffset);
|
||||
|
||||
bool within = true;
|
||||
|
||||
for (int j = rankMinusOne; j >= 0; --j) {
|
||||
if (xShape[j] == zShape[j]) continue;
|
||||
|
||||
LongType leftOffset;
|
||||
LongType leftCoords[] = {yStride0 * j};
|
||||
COORDS2INDEX(1, shape::stride(yShapeInfo), leftCoords, leftOffset);
|
||||
const auto left = y[leftOffset];
|
||||
|
||||
if (xzCoord[j] < left || xzCoord[j] >= left + xShape[j]) {
|
||||
within = false;
|
||||
|
||||
if (mode != 0) { // REFLECT or SYMMETRIC
|
||||
xzCoord[j] = xzCoord[j] - left;
|
||||
|
||||
if (xzCoord[j] < 0) { // Left boundary
|
||||
xzCoord[j] = -xzCoord[j] - shift1;
|
||||
} else if (xzCoord[j] >= xShape[j]) { // Right boundary
|
||||
xzCoord[j] = 2 * xShape[j] - xzCoord[j] - shift2;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
xzCoord[j] -= left;
|
||||
}
|
||||
}
|
||||
|
||||
if (within || mode != 0) {
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, xzCoord, xOffset);
|
||||
z[zOffset] = within ? x[xOffset] : x[xOffset]; // Handles REFLECT or SYMMETRIC
|
||||
} else {
|
||||
z[zOffset] = padVal; // CONSTANT padding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void padCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const int mode, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const void* padVal) {
|
||||
padCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(mode, vx, xShapeInfo, vy, yShapeInfo, vz,
|
||||
zShapeInfo, padVal);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "padCuda failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void pad(LaunchContext* context, const int mode, NDArray& input, NDArray& paddings, NDArray& output,
|
||||
NDArray& padValue) {
|
||||
PointersManager manager(context, "pad");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input, &paddings, &padValue});
|
||||
|
||||
dim3 padLaunch = padDims(output.lengthOf(),output.rankOf());
|
||||
const auto xType = input.dataType();
|
||||
const auto yType = paddings.dataType();
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(
|
||||
xType, yType, padCudaLauncher,
|
||||
(padLaunch.y, padLaunch.x, padLaunch.z, context->getCudaStream(), mode, input.specialBuffer(),
|
||||
input.specialShapeInfo(), paddings.specialBuffer(), paddings.specialShapeInfo(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), padValue.specialBuffer()),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({&output}, {&input, &paddings, &padValue});
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void mirrorPadLinearKernel(void const* vx, const LongType* xShape, void* vz,
|
||||
const LongType* zShape,
|
||||
LongType leftSide, LongType leftSideCorrected, LongType xLen, LongType len,
|
||||
LongType zLen) {
|
||||
__shared__ T const* x;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType rankX, rankZ;
|
||||
__shared__ const LongType* shapeX;
|
||||
__shared__ const LongType* strideX;
|
||||
__shared__ const LongType* shapeZ;
|
||||
__shared__ const LongType* strideZ;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<T const*>(vx);
|
||||
z = reinterpret_cast<T*>(vz);
|
||||
|
||||
rankX = shape::rank(xShape);
|
||||
rankZ = shape::rank(zShape);
|
||||
shapeX = shape::shapeOf(xShape);
|
||||
strideX = shape::stride(xShape);
|
||||
shapeZ = shape::shapeOf(zShape);
|
||||
strideZ = shape::stride(zShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = blockDim.x * gridDim.x;
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xOffset, zOffset;
|
||||
|
||||
for (LongType i = start; i < zLen; i += step) {
|
||||
// Compute coordinates and offset for the output
|
||||
INDEX2COORDS(i, rankZ, shapeZ, zCoords);
|
||||
COORDS2INDEX(rankZ, strideZ, zCoords, zOffset);
|
||||
|
||||
// Adjust input offset based on the mirror padding logic
|
||||
if (i < leftSide) { // Left side
|
||||
const LongType mirrorIndex = leftSideCorrected - i;
|
||||
COORDS2INDEX(rankX, strideX, &mirrorIndex, xOffset);
|
||||
} else if (i < leftSide + xLen) { // Middle section
|
||||
const LongType middleIndex = i - leftSide;
|
||||
COORDS2INDEX(rankX, strideX, &middleIndex, xOffset);
|
||||
} else { // Right side
|
||||
const LongType mirrorIndex = len - i;
|
||||
COORDS2INDEX(rankX, strideX, &mirrorIndex, xOffset);
|
||||
}
|
||||
|
||||
// Assign value from input to output
|
||||
if (zOffset < zLen && xOffset < xLen) {
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename I>
|
||||
static SD_KERNEL void mirrorPadKernel(void const* vx, const LongType* xShape, void* vz, const LongType* zShape,
|
||||
LongType outLen, void const* paddings, const LongType* paddingShape,
|
||||
int reflBorder) {
|
||||
__shared__ F const* x;
|
||||
__shared__ I const* pads;
|
||||
__shared__ F* z;
|
||||
__shared__ LongType rank;
|
||||
__shared__ sd::LongType *zStride;
|
||||
__shared__ sd::LongType *xStride;
|
||||
__shared__ LongType* zShapeArr;
|
||||
__shared__ LongType* xShapeArr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
rank = shape::rank(xShape);
|
||||
zShapeArr = shape::shapeOf(zShape);
|
||||
zStride = shape::stride(zShape);
|
||||
xShapeArr = shape::shapeOf(xShape);
|
||||
xStride = shape::stride(xShape);
|
||||
|
||||
x = reinterpret_cast<F const*>(vx);
|
||||
pads = reinterpret_cast<I const*>(paddings);
|
||||
z = reinterpret_cast<F*>(vz);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto start = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
const auto step = blockDim.x * gridDim.x;
|
||||
|
||||
LongType xzCoord[SD_MAX_RANK];
|
||||
LongType coords[2];
|
||||
|
||||
for (LongType i = start; i < outLen; i += step) {
|
||||
// Calculate output coordinate and offset
|
||||
INDEX2COORDS(i, rank, zShapeArr, xzCoord);
|
||||
LongType outOffset;
|
||||
COORDS2INDEX(rank, zStride, xzCoord, outOffset);
|
||||
|
||||
// Adjust input coordinates based on mirror padding
|
||||
for (LongType j = 0; j < rank; ++j) {
|
||||
const auto inLen = shape::sizeAt(xShape, j);
|
||||
|
||||
coords[0] = j;
|
||||
coords[1] = 0;
|
||||
|
||||
LongType padOffset;
|
||||
COORDS2INDEX(2, shape::stride(paddingShape), coords, padOffset);
|
||||
const auto leftSide = pads[padOffset];
|
||||
const auto leftSideCorrected = leftSide - reflBorder;
|
||||
const auto len = 2 * (inLen - 1) + leftSide + reflBorder;
|
||||
|
||||
if (xzCoord[j] < leftSide) { // Left side
|
||||
xzCoord[j] = leftSideCorrected - xzCoord[j];
|
||||
} else if (xzCoord[j] < leftSide + inLen) { // Middle
|
||||
xzCoord[j] = xzCoord[j] - leftSide;
|
||||
} else if (xzCoord[j] < len) { // Right side
|
||||
xzCoord[j] = len - xzCoord[j];
|
||||
} else { // Beyond the mirrored region
|
||||
xzCoord[j] = xzCoord[j] - len;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate input offset and assign value
|
||||
LongType inOffset;
|
||||
COORDS2INDEX(rank, xStride, xzCoord, inOffset);
|
||||
z[outOffset] = x[inOffset];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename F, typename I>
|
||||
static void mirrorPad_(LaunchContext* context, NDArray& input, NDArray& paddings, NDArray& output,
|
||||
const int mode) {
|
||||
// mode: 0 - REFLECT, else - SYMMETRIC
|
||||
const int reflBorder = (bool)mode ? 1 : 0;
|
||||
const LongType rank = input.rankOf();
|
||||
const LongType outLen = output.lengthOf();
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray::prepareSpecialUse({&output}, {&input, &paddings});
|
||||
|
||||
if (rank <= 1) {
|
||||
const LongType inLen = input.isScalar() ? 1 : input.lengthOf();
|
||||
const auto leftSide = paddings.e<LongType>(0);
|
||||
const auto leftSideCorrected = leftSide - reflBorder;
|
||||
const LongType len = 2 * (inLen - 1) + leftSide + reflBorder;
|
||||
|
||||
dim3 mirrorPadLinearDims2 = mirrorPadLinearDims(len);
|
||||
mirrorPadLinearKernel<F><<<mirrorPadLinearDims2.y, mirrorPadLinearDims2.x, mirrorPadLinearDims2.z, *stream>>>(
|
||||
input.specialBuffer(), input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), leftSide,
|
||||
leftSideCorrected, inLen, len, outLen);
|
||||
DebugHelper::checkErrorCode(stream, "helpers::mirrorPadLinearKernel(...) failed");
|
||||
} else {
|
||||
dim3 mirrorPadDims = mirrorPadTad(output.lengthOf(),input.rankOf());
|
||||
mirrorPadKernel<F, I><<<mirrorPadDims.y, mirrorPadDims.x, mirrorPadDims.z, *stream>>>(
|
||||
input.specialBuffer(), input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), outLen,
|
||||
paddings.specialBuffer(), paddings.specialShapeInfo(), reflBorder);
|
||||
DebugHelper::checkErrorCode(stream, "helpers::mirrorPadKernel(...) failed");
|
||||
}
|
||||
NDArray::registerSpecialUse({&output}, {&input, &paddings});
|
||||
}
|
||||
|
||||
void mirrorPad(LaunchContext* context, NDArray& input, NDArray& paddings, NDArray& output,
|
||||
const int mode) {
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), paddings.dataType(), mirrorPad_, (context, input, paddings, output, mode),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,148 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
// @author raver119@gmail.com
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/DebugHelper.h>
|
||||
#include <ops/declarable/helpers/percentile.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename X>
|
||||
static SD_KERNEL void percentileKernel(void* vx, const LongType* xTadShapeInfo, const LongType* xTadOffsets,
|
||||
const LongType numTads, const LongType tadLength, void* vz,
|
||||
const LongType* zShapeInfo, const LongType zLength,
|
||||
const LongType position) {
|
||||
const auto x = reinterpret_cast<X*>(vx);
|
||||
auto z = reinterpret_cast<X*>(vz);
|
||||
|
||||
__shared__ LongType xRank, zRank;
|
||||
__shared__ const LongType* xShape;
|
||||
__shared__ const LongType* xStride;
|
||||
__shared__ const LongType* zShape;
|
||||
__shared__ const LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xTadShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
xShape = shape::shapeOf(xTadShapeInfo);
|
||||
xStride = shape::stride(xTadShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (LongType t = blockIdx.x; t < numTads; t += gridDim.x) {
|
||||
auto tad = x + xTadOffsets[t];
|
||||
|
||||
// Sort TAD using odd-even transposition sort
|
||||
for (LongType m = 0; m < tadLength; ++m) {
|
||||
for (LongType tid = threadIdx.x; tid < tadLength; tid += blockDim.x) {
|
||||
const auto top = (m % 2 == 0) ? 2 * tid + 1 : 2 * tid + 2;
|
||||
if (top < tadLength) {
|
||||
if (tad[top - 1] > tad[top]) {
|
||||
// Swap values
|
||||
X temp = tad[top - 1];
|
||||
tad[top - 1] = tad[top];
|
||||
tad[top] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Save the final value to the output
|
||||
if (threadIdx.x == 0) {
|
||||
const auto value = tad[position];
|
||||
LongType zOffset;
|
||||
|
||||
COORDS2INDEX(zRank, zStride, &t, zOffset);
|
||||
z[zOffset] = value;
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static void _percentile(LaunchContext* context, NDArray& input, NDArray& output, std::vector<LongType>& axis,
|
||||
const float q, const int interpolation) {
|
||||
const int inputRank = input.rankOf();
|
||||
|
||||
if (axis.empty())
|
||||
for (int i = 0; i < inputRank; ++i) axis.push_back(i);
|
||||
else
|
||||
shape::checkDimensions(inputRank, &axis);
|
||||
|
||||
auto tempArray = input.dup();
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(tempArray.shapeInfo(), &axis);
|
||||
|
||||
auto tadLength = shape::length(packX->primaryShapeInfo());
|
||||
|
||||
const float fraction = 1.f - q / 100.;
|
||||
LongType position = 0;
|
||||
|
||||
switch (interpolation) {
|
||||
case 0: // lower
|
||||
position = static_cast<LongType>(math::sd_ceil<float, T>((tadLength - 1) * fraction));
|
||||
break;
|
||||
case 1: // higher
|
||||
position = static_cast<LongType>(math::sd_floor<float, T>((tadLength - 1) * fraction));
|
||||
break;
|
||||
case 2: // nearest
|
||||
position = static_cast<LongType>(math::sd_round<float, T>((tadLength - 1) * fraction));
|
||||
break;
|
||||
}
|
||||
position = tadLength - position - 1;
|
||||
|
||||
dim3 launchDims = getLaunchDims("percentile");
|
||||
percentileKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *context->getCudaStream()>>>(
|
||||
tempArray.specialBuffer(), packX->platformShapeInfo(), packX->platformOffsets(), packX->numberOfTads(), tadLength,
|
||||
output.specialBuffer(), output.specialShapeInfo(), output.lengthOf(), position);
|
||||
|
||||
DebugHelper::checkErrorCode(context->getCudaStream(), "percentile");
|
||||
}
|
||||
|
||||
void percentile(LaunchContext* context, NDArray& input, NDArray& output, std::vector<LongType>& axises,
|
||||
const float q, const int interpolation) {
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), _percentile, (context, input, output, axises, q, interpolation),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void _percentile,
|
||||
(sd::LaunchContext * context, NDArray& input, NDArray& output, std::vector<sd::LongType>& axises,
|
||||
const float q, const int interpolation),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,125 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 26.04.2019
|
||||
//
|
||||
#include <array/NDArrayFactory.h>
|
||||
#include <ops/declarable/helpers/gammaMathFunc.h>
|
||||
#include <ops/declarable/helpers/zeta.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void polyGammaCuda(const void *vn, const LongType *nShapeInfo, const void *vx,
|
||||
const LongType *xShapeInfo, void *vz, const LongType *zShapeInfo) {
|
||||
const auto n = reinterpret_cast<const T *>(vn);
|
||||
const auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
__shared__ LongType len;
|
||||
__shared__ bool sameOffsetNX, sameOffsetNZ;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
len = shape::length(nShapeInfo);
|
||||
sameOffsetNX = shape::haveSameShapeAndStrides(xShapeInfo, nShapeInfo);
|
||||
sameOffsetNZ = shape::haveSameShapeAndStrides(zShapeInfo, nShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
for (LongType i = tid; i < len; i += totalThreads) {
|
||||
LongType nOffset, xOffset, zOffset;
|
||||
|
||||
// Compute offsets for n
|
||||
nOffset = i * (sameOffsetNX ? 0 : shape::stride(nShapeInfo)[0]);
|
||||
|
||||
// Compute offsets for x
|
||||
if (sameOffsetNX) {
|
||||
xOffset = nOffset;
|
||||
} else {
|
||||
xOffset = i * shape::stride(xShapeInfo)[0];
|
||||
}
|
||||
|
||||
// Compute offsets for z
|
||||
if (sameOffsetNZ) {
|
||||
zOffset = nOffset;
|
||||
} else {
|
||||
zOffset = i * shape::stride(zShapeInfo)[0];
|
||||
}
|
||||
|
||||
const T order = n[nOffset];
|
||||
const int sign = ((static_cast<int>(order) + 1) % 2 == 0) ? 1 : -1;
|
||||
|
||||
if (order != static_cast<int>(order)) {
|
||||
z[zOffset] = DataTypeUtils::nanOrZero<T>();
|
||||
} else if (order == 0) {
|
||||
z[zOffset] = diGammaScalar<T>(x[xOffset]);
|
||||
} else {
|
||||
T factorial = static_cast<T>(1);
|
||||
for (int j = 2; j <= order; ++j) {
|
||||
factorial *= j;
|
||||
}
|
||||
z[zOffset] = sign * factorial * zetaScalar<T>(order + 1, x[xOffset]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void polyGammaCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMemory,
|
||||
const cudaStream_t *stream, const void *vn, const LongType *nShapeInfo,
|
||||
const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo) {
|
||||
polyGammaCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMemory, *stream>>>(vn, nShapeInfo, vx, xShapeInfo, vz, zShapeInfo);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "print_device failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void polyGamma(LaunchContext *context, NDArray&n, NDArray&x, NDArray &z) {
|
||||
NDArray::prepareSpecialUse({&z}, {&n, &x});
|
||||
|
||||
dim3 launchDims = polygammaDims(z.lengthOf());
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
n.dataType(), polyGammaCudaLauncher,
|
||||
(launchDims.y,launchDims.x,launchDims.z, context->getCudaStream(), n.specialBuffer(), n.specialShapeInfo(),
|
||||
x.specialBuffer(), x.specialShapeInfo(), z.specialBuffer(), z.specialShapeInfo()),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({&z}, {&n, &x});
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void polyGammaCudaLauncher,
|
||||
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMemory,const cudaStream_t *stream, const void *vn,
|
||||
const sd::LongType *nShapeInfo, const void *vx, const sd::LongType *xShapeInfo, void *vz,
|
||||
const sd::LongType *zShapeInfo),
|
||||
SD_FLOAT_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,140 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 12.06.2019
|
||||
//
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/prefix.h>
|
||||
#include <ops/ops.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void prefix_(scalar::Ops op, const void* vx, const LongType* xShapeInfo, void* vz, const LongType* zShapeInfo, bool exclusive, bool reverse) {
|
||||
const auto x = reinterpret_cast<const T*>(vx);
|
||||
auto z = reinterpret_cast<T*>(vz);
|
||||
|
||||
const auto length = shape::length(xShapeInfo);
|
||||
const auto rankX = shape::rank(xShapeInfo);
|
||||
const auto rankZ = shape::rank(zShapeInfo);
|
||||
const auto shapeX = shape::shapeOf(xShapeInfo);
|
||||
const auto strideX = shape::stride(xShapeInfo);
|
||||
const auto shapeZ = shape::shapeOf(zShapeInfo);
|
||||
const auto strideZ = shape::stride(zShapeInfo);
|
||||
|
||||
T prevSum = (op == scalar::Add) ? static_cast<T>(0) : static_cast<T>(1);
|
||||
T sum = prevSum;
|
||||
|
||||
LongType coordsX[SD_MAX_RANK];
|
||||
LongType coordsZ[SD_MAX_RANK];
|
||||
|
||||
if (reverse) {
|
||||
for (LongType e = length - 1; e >= 0; --e) {
|
||||
LongType offsetX, offsetZ;
|
||||
|
||||
// Compute input and output offsets
|
||||
INDEX2COORDS(e, rankX, shapeX, coordsX);
|
||||
COORDS2INDEX(rankX, strideX, coordsX, offsetX);
|
||||
INDEX2COORDS(e, rankZ, shapeZ, coordsZ);
|
||||
COORDS2INDEX(rankZ, strideZ, coordsZ, offsetZ);
|
||||
|
||||
// Perform operation
|
||||
sum = (op == scalar::Add) ? simdOps::Add<T, T, T>::op(sum, x[offsetX]) : simdOps::Multiply<T, T, T>::op(sum, x[offsetX]);
|
||||
|
||||
if (!exclusive) prevSum = sum;
|
||||
|
||||
z[offsetZ] = prevSum;
|
||||
prevSum = sum;
|
||||
}
|
||||
} else {
|
||||
for (LongType e = 0; e < length; ++e) {
|
||||
LongType offsetX, offsetZ;
|
||||
|
||||
// Compute input and output offsets
|
||||
INDEX2COORDS(e, rankX, shapeX, coordsX);
|
||||
COORDS2INDEX(rankX, strideX, coordsX, offsetX);
|
||||
INDEX2COORDS(e, rankZ, shapeZ, coordsZ);
|
||||
COORDS2INDEX(rankZ, strideZ, coordsZ, offsetZ);
|
||||
|
||||
// Perform operation
|
||||
sum = (op == scalar::Add) ? simdOps::Add<T, T, T>::op(sum, x[offsetX]) : simdOps::Multiply<T, T, T>::op(sum, x[offsetX]);
|
||||
|
||||
if (!exclusive) prevSum = sum;
|
||||
|
||||
z[offsetZ] = 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) {
|
||||
NDArray::preparePrimaryUse({z}, {x});
|
||||
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);
|
||||
}
|
||||
|
||||
NDArray::registerPrimaryUse({z}, {x});
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
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(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_COMMON_TYPES);
|
||||
}
|
||||
|
||||
void prefix(LaunchContext* context, scalar::Ops op, NDArray* x, NDArray* z, const std::vector<LongType>& dims,
|
||||
bool exclusive, bool reverse) {
|
||||
BUILD_SINGLE_SELECTOR(x->dataType(), prefix_, (op, x, z, dims, exclusive, reverse), SD_COMMON_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_COMMON_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void prefix_,
|
||||
(scalar::Ops op, NDArray* x, NDArray* z, const std::vector<sd::LongType>& dims, bool exclusive,
|
||||
bool reverse),
|
||||
SD_COMMON_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void prefix_,
|
||||
(scalar::Ops op, NDArray* x, NDArray* z, bool exclusive, bool reverse), SD_COMMON_TYPES);
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,85 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/PointersManager.h>
|
||||
#include <ops/declarable/helpers/print_variable.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static SD_KERNEL void print_device(const void *special, const LongType *shapeInfo) {
|
||||
__shared__ LongType length;
|
||||
__shared__ LongType rank;
|
||||
__shared__ const LongType *shape, *stride;
|
||||
const auto x = reinterpret_cast<const T *>(special);
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
length = shape::length(shapeInfo);
|
||||
rank = shape::rank(shapeInfo);
|
||||
shape = shape::shapeOf(shapeInfo);
|
||||
stride = shape::stride(shapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (threadIdx.x == 0 && blockIdx.x == 0) {
|
||||
printf("[");
|
||||
|
||||
LongType coords[SD_MAX_RANK];
|
||||
LongType offset;
|
||||
|
||||
for (LongType e = 0; e < length; e++) {
|
||||
INDEX2COORDS(e, rank, shape, coords);
|
||||
COORDS2INDEX(rank, stride, coords, offset);
|
||||
|
||||
printf("%f", static_cast<float>(x[offset]));
|
||||
if (e < length - 1) printf(", ");
|
||||
}
|
||||
|
||||
printf("]\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static SD_HOST void exec_print_device(LaunchContext &ctx, const void *special, const LongType *shapeInfo) {
|
||||
dim3 launchDims = getLaunchDims("print");
|
||||
print_device<T><<<launchDims.x, launchDims.y, launchDims.z, *ctx.getCudaStream()>>>(special, shapeInfo);
|
||||
sd::DebugHelper::checkErrorCode(ctx.getCudaStream(), "print_device failed");
|
||||
|
||||
}
|
||||
|
||||
void print_special(LaunchContext &ctx, NDArray&array, const std::string &message) {
|
||||
NDArray::prepareSpecialUse({}, {&array});
|
||||
|
||||
PointersManager pm(&ctx, "print_device");
|
||||
BUILD_SINGLE_SELECTOR(array.dataType(), exec_print_device, (ctx, array.specialBuffer(), array.specialShapeInfo()),
|
||||
SD_COMMON_TYPES)
|
||||
pm.synchronize();
|
||||
|
||||
NDArray::registerSpecialUse({}, {&array});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,196 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/MmulHelper.h>
|
||||
#include <ops/declarable/helpers/qr.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void matrixMinorKernel(T* outBuffer, LongType* outShape, T* inBuffer, LongType* inShape,
|
||||
LongType column, LongType rows, LongType columns) {
|
||||
|
||||
for (auto i = blockIdx.x; i < rows; i += gridDim.x)
|
||||
for (auto j = threadIdx.x; j < columns; j += blockDim.x) {
|
||||
LongType pos[] = {i, j};
|
||||
|
||||
LongType zIndex;
|
||||
COORDS2INDEX(shape::rank(outShape), shape::stride(outShape), pos, zIndex);
|
||||
|
||||
LongType xIndex;
|
||||
COORDS2INDEX(shape::rank(inShape), shape::stride(inShape), pos, xIndex);
|
||||
|
||||
if (i < column || j < column) {
|
||||
outBuffer[zIndex] = i != j ? T(0.f) : T(1.f);
|
||||
} else {
|
||||
outBuffer[zIndex] = inBuffer[xIndex]; // m.t<T>(i,j) = in.t<T>(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NDArray matrixMinor(LaunchContext* context, NDArray& in, LongType col) {
|
||||
NDArray *m = in.ulike();
|
||||
m->setIdentity();
|
||||
NDArray view = *m;
|
||||
NDArray assign = in({col, m->rows(), col, m->columns()});
|
||||
view({col, m->rows(), col, m->columns()}).assign(&assign);
|
||||
|
||||
m->tickWriteDevice();
|
||||
return *m;
|
||||
}
|
||||
|
||||
/* m = I - v v^T */
|
||||
template <typename T>
|
||||
static SD_KERNEL void vmulKernel(T* resBuf, const LongType* resShape, T const* vBuff, LongType const* vShape,
|
||||
LongType n) {
|
||||
for (auto i = blockIdx.x; i < n; i += gridDim.x)
|
||||
for (auto j = threadIdx.x; j < n; j += blockDim.x) {
|
||||
LongType posR[] = {i, j};
|
||||
LongType indexR, indexX, indexY;
|
||||
COORDS2INDEX(shape::rank(resShape), shape::stride(resShape), posR, indexR);
|
||||
COORDS2INDEX(1, shape::stride(vShape), &i, indexX);
|
||||
COORDS2INDEX(1, shape::stride(vShape), &j, indexY);
|
||||
|
||||
resBuf[indexR] = T(-2.f) * vBuff[indexX] * vBuff[indexY] + (i != j ? T(0.f) : T(1.f));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NDArray vmul(LaunchContext* context, NDArray& v, int n) {
|
||||
std::vector<LongType> shape = {n, n};
|
||||
NDArray res('c', shape, v.dataType(), context); // x = matrix_new(n, n);
|
||||
|
||||
auto stream = context->getCudaStream();
|
||||
dim3 launchDims = getLaunchDims("qr");
|
||||
vmulKernel<T><<<launchDims.x,launchDims.y, launchDims.z, *stream>>>(res.dataBuffer()->specialAsT<T>(), res.specialShapeInfo(),
|
||||
reinterpret_cast<T const*>(v.specialBuffer()), v.specialShapeInfo(), n);
|
||||
sd::DebugHelper::checkErrorCode(stream, "vmulKernel failed");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool diagonalIsPositive(NDArray* matrix, LongType k) {
|
||||
T hVal;
|
||||
LongType pos[] = {k, k};
|
||||
LongType shift;
|
||||
COORDS2INDEX(shape::rank(matrix->shapeInfo()), shape::stride(matrix->shapeInfo()), pos, shift);
|
||||
cudaMemcpy(&hVal, matrix->specialBuffer(), sizeof(T), cudaMemcpyDeviceToHost);
|
||||
return hVal > T(0.f);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void qrSingle(LaunchContext* context, NDArray* matrix, NDArray* Q, NDArray* R, bool const fullMatrices) {
|
||||
LongType M = matrix->sizeAt(0);
|
||||
LongType N = matrix->sizeAt(1);
|
||||
auto resQ = fullMatrices ? *Q->ulike() : NDArrayFactory::create<T>(matrix->ordering(), {M, M}, Q->getContext());
|
||||
auto resR = fullMatrices ? R->ulike() : matrix->ulike();
|
||||
std::vector<NDArray*> q(M, nullptr);
|
||||
NDArray z = *matrix;
|
||||
std::vector<LongType> shape = {M};
|
||||
|
||||
NDArray e('c', shape, DataTypeUtils::fromT<T>(), context); // two internal buffers and scalar for squared norm
|
||||
for (auto k = 0; k < N && k < M - 1; k++) { // loop for columns, but not further then row number
|
||||
e.nullify();
|
||||
z = matrixMinor<T>(context, z,
|
||||
k); // minor computing for current column with given matrix z (initally is a input matrix)
|
||||
|
||||
auto currentColumn = z({0, 0, k, k + 1}); // retrieve k column from z to x buffer
|
||||
std::vector<LongType> zero = {0};
|
||||
auto norm = currentColumn.reduceAlongDimension(reduce::Norm2, &zero);
|
||||
if (diagonalIsPositive<T>(matrix, k)) // matrix->t<T>(k,k) > T(0.f)) // negate on positive matrix diagonal element
|
||||
norm.applyTransform(transform::Neg, &norm); // *= -1.f;//-norm.t<T>(0);
|
||||
|
||||
e.p(k, &norm); // e - is filled by 0 vector except diagonal element (filled by 1)
|
||||
e += currentColumn; // e[i] = x[i] + a * e[i] for each i from 0 to n - 1
|
||||
auto normE = e.reduceAlongDimension(reduce::Norm2, &zero);
|
||||
e /= normE;
|
||||
q[k] = new NDArray(vmul<T>(context, e, M));
|
||||
auto qQ = z.ulike();
|
||||
MmulHelper::matmul(q[k], &z, qQ, false, false,1.0,0.0,qQ);
|
||||
z = std::move(*qQ);
|
||||
}
|
||||
resQ.assign(q[0]);
|
||||
|
||||
for (int i = 1; i < N && i < M - 1; i++) {
|
||||
auto tempResQ = resQ;
|
||||
MmulHelper::matmul(q[i],&resQ, &tempResQ, false, false,1.0,0.0,&tempResQ);
|
||||
resQ = std::move(tempResQ);
|
||||
}
|
||||
MmulHelper::matmul(&resQ, matrix, resR, false, false,1.0,0.0,resR);
|
||||
// resR *= -1.f;
|
||||
resQ.transposei();
|
||||
|
||||
if (fullMatrices) {
|
||||
Q->assign(&resQ);
|
||||
R->assign(resR);
|
||||
} else {
|
||||
NDArray resRRef = *resR;
|
||||
NDArray qAssign = resQ({0, 0, 0, N});
|
||||
Q->assign(&qAssign);
|
||||
NDArray rAssign = resRRef({0, N, 0, 0});
|
||||
R->assign(&rAssign);
|
||||
}
|
||||
|
||||
// Clean up allocated NDArrays in q vector
|
||||
for (LongType i = 0; i < M; i++) {
|
||||
if (q[i] != nullptr) {
|
||||
delete q[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void qr_(LaunchContext* context, NDArray * input, NDArray* outputQ, NDArray* outputR, bool const fullMatricies) {
|
||||
LongType lastDim = input->rankOf() - 1;
|
||||
LongType preLastDim = input->rankOf() - 2;
|
||||
|
||||
NDArray::prepareSpecialUse({outputQ, outputR}, {input});
|
||||
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 start = 0;
|
||||
auto stop = listInput.size();
|
||||
auto increment = 1;
|
||||
|
||||
for (auto batch = start; batch < stop; batch += increment) {
|
||||
// qr here
|
||||
qrSingle<T>(context, listInput.at(batch), listOutQ.at(batch), listOutR.at(batch), fullMatricies);
|
||||
}
|
||||
NDArray::registerSpecialUse({outputQ, outputR}, {input});
|
||||
}
|
||||
|
||||
void qr(LaunchContext* context, NDArray * input, NDArray* outputQ, NDArray* outputR,
|
||||
bool const fullMatricies) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), qr_, (context, input, outputQ, outputR, fullMatricies), SD_FLOAT_TYPES);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,542 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <graph/Context.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/RandomLauncher.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/random.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
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 U - uniform random generated vals
|
||||
* @param alpha - shape of distribution
|
||||
* @param beta - scale of distributed values
|
||||
* @return gamma distributed value
|
||||
*/
|
||||
template <typename T>
|
||||
T SD_DEVICE gammaLess(T const* U, LongType index, LongType maxLength, 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;
|
||||
auto indexV = index;
|
||||
auto underAlpha = T(1.f) / alpha;
|
||||
auto powerAlpha = math::p_pow<T>(T(2.f), alpha - T(1.f));
|
||||
|
||||
for (;;) {
|
||||
auto u = (indexV < maxLength) ? U[indexV++] : U[0];
|
||||
if (indexV >= maxLength) indexV = 0LL;
|
||||
if (u <= a / c)
|
||||
rawX = -T(2.f) * math::p_log(T(1.f) - T(0.5f) * math::p_pow(c * u, underAlpha));
|
||||
else
|
||||
rawX = -math::p_log(c * (T(1.f) - u) / (alpha * math::p_pow(d, alpha - T(1.f))));
|
||||
|
||||
T v = indexV < maxLength ? U[indexV++] : U[0];
|
||||
if (indexV >= maxLength) indexV = 0LL;
|
||||
// math::atomics::sd_atomicAdd(index, 1LL);
|
||||
|
||||
if (rawX <= d) {
|
||||
auto testVal = (math::p_pow<T>(rawX, alpha - 1.f) * math::p_exp(-T(0.5f) * rawX)) /
|
||||
(powerAlpha * math::p_pow<T>(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 SD_DEVICE gammaGreat(T const* U, LongType index, LongType maxLength, 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);
|
||||
auto indexV = index;
|
||||
T x;
|
||||
auto normalDistributed = [U, maxLength](LongType& index) {
|
||||
auto v1 = index < maxLength ? U[index++] : U[0];
|
||||
if (index >= maxLength) index = 0LL;
|
||||
auto v2 = index < maxLength ? U[index++] : U[0];
|
||||
if (index >= maxLength) index = 0LL;
|
||||
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(indexV);
|
||||
normalizedVar = T(1.f) + c * x;
|
||||
} while (normalizedVar < T(0.f));
|
||||
normalizedVar = normalizedVar * normalizedVar * normalizedVar; // v * v * v;
|
||||
|
||||
auto u = U[indexV++];
|
||||
if (indexV >= maxLength) indexV = 0LL;
|
||||
if (u < T(1.f) - T(.0331f) * (x * x) * (x * x)) break; // return (d * v / b);
|
||||
if (log(u) < 0.5f * x * x + decreasedAlpha * (1. - normalizedVar + math::p_log(normalizedVar))) break;
|
||||
}
|
||||
return (decreasedAlpha * normalizedVar / beta);
|
||||
}
|
||||
|
||||
/*
|
||||
* fillGammaKernel - fill up output with gamma distributed values
|
||||
*
|
||||
* uList - uniformly distributed values set
|
||||
* uLength - length of uList
|
||||
* alpha - alpha param
|
||||
* beta - beta param
|
||||
* output - distributed output.
|
||||
* */
|
||||
template <typename T>
|
||||
static SD_KERNEL void fillGammaKernel(T const* uList, LongType uLength, T const* alpha,
|
||||
const LongType* alphaShape, T const* beta, const LongType* betaShape,
|
||||
T* output, const LongType* outputShape) {
|
||||
__shared__ LongType aLength;
|
||||
__shared__ LongType outLength;
|
||||
__shared__ LongType rankAlpha, rankBeta, rankOutput;
|
||||
__shared__ const LongType *alphaShapeArr, *alphaStride, *betaShapeArr, *betaStride, *outputShapeArr, *outputStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
aLength = shape::length(alphaShape);
|
||||
outLength = shape::length(outputShape) / aLength;
|
||||
|
||||
rankAlpha = shape::rank(alphaShape);
|
||||
alphaShapeArr = shape::shapeOf(alphaShape);
|
||||
alphaStride = shape::stride(alphaShape);
|
||||
|
||||
rankBeta = betaShape ? shape::rank(betaShape) : 0;
|
||||
betaShapeArr = betaShape ? shape::shapeOf(betaShape) : nullptr;
|
||||
betaStride = betaShape ? shape::stride(betaShape) : nullptr;
|
||||
|
||||
rankOutput = shape::rank(outputShape);
|
||||
outputShapeArr = shape::shapeOf(outputShape);
|
||||
outputStride = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (LongType k = blockIdx.x; k < outLength; k += gridDim.x) {
|
||||
const auto pos = k * aLength;
|
||||
|
||||
for (LongType e = threadIdx.x; e < aLength; e += blockDim.x) {
|
||||
LongType aCoords[SD_MAX_RANK], bCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
LongType aIndex, bIndex = -1LL, zIndex;
|
||||
|
||||
// Alpha coordinates and index
|
||||
INDEX2COORDS(e, rankAlpha, alphaShapeArr, aCoords);
|
||||
COORDS2INDEX(rankAlpha, alphaStride, aCoords, aIndex);
|
||||
|
||||
// Beta coordinates and index (if beta is provided)
|
||||
if (betaShape) {
|
||||
INDEX2COORDS(e, rankBeta, betaShapeArr, bCoords);
|
||||
COORDS2INDEX(rankBeta, betaStride, bCoords, bIndex);
|
||||
}
|
||||
|
||||
// Output coordinates and index
|
||||
INDEX2COORDS(e + pos, rankOutput, outputShapeArr, zCoords);
|
||||
COORDS2INDEX(rankOutput, outputStride, zCoords, zIndex);
|
||||
|
||||
// Get beta value or default to 1
|
||||
const auto betaV = beta ? beta[bIndex] : T(1.f);
|
||||
|
||||
// Fill the output with the computed gamma value
|
||||
output[zIndex] = alpha[aIndex] > T(1.f)
|
||||
? gammaGreat(uList, pos, uLength, alpha[aIndex], betaV)
|
||||
: gammaLess(uList, pos, uLength, alpha[aIndex], betaV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void fillRandomGamma_(LaunchContext* context, graph::RandomGenerator& rng, NDArray* alpha, NDArray* beta,
|
||||
NDArray* output) {
|
||||
// To fill up output need to broadcast alpha and beta to the same shape and in
|
||||
LongType* broadcasted = nullptr;
|
||||
if (beta != nullptr)
|
||||
ShapeUtils::evalBroadcastShapeInfo(alpha->shapeInfo(), beta->shapeInfo(), true, broadcasted, context->getWorkspace());
|
||||
else
|
||||
broadcasted = alpha->shapeInfo();
|
||||
auto step = shape::length(broadcasted);
|
||||
auto shift = output->lengthOf() * 4LL; // 2-wise greater case for uniform vals
|
||||
|
||||
auto copyAlpha = alpha;
|
||||
auto copyBeta = beta;
|
||||
if (beta != nullptr) {
|
||||
NDArray alphaBroadcasted(broadcasted, alpha->dataType(), true, context);
|
||||
NDArray betaBroadcasted(broadcasted, beta->dataType(), true, context);
|
||||
|
||||
copyAlpha = new NDArray(alphaBroadcasted.applyTrueBroadcast(BroadcastOpsTuple::Assign(), alpha));
|
||||
copyBeta = new NDArray(betaBroadcasted.applyTrueBroadcast(BroadcastOpsTuple::Assign(), beta));
|
||||
}
|
||||
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray uniform = NDArrayFactory::create<T>('c', {shift}, context);
|
||||
uniform.syncToDevice();
|
||||
// fill up uniform with given length
|
||||
RandomLauncher::fillUniform(context, rng, &uniform, 0.0000000001, 0.9999999999);
|
||||
uniform.syncToDevice();
|
||||
dim3 launchDims = getLaunchDims("random_gamma");
|
||||
fillGammaKernel<T><<<launchDims.x, launchDims.y,launchDims.z, *stream>>>(
|
||||
uniform.dataBuffer()->specialAsT<T>(), shift, copyAlpha->dataBuffer()->specialAsT<T>(),
|
||||
copyAlpha->specialShapeInfo(), beta ? copyBeta->dataBuffer()->specialAsT<T>() : (T const*)nullptr,
|
||||
beta ? copyBeta->specialShapeInfo() : (LongType const*)nullptr, output->dataBuffer()->specialAsT<T>(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "fillGammaKernel failed");
|
||||
|
||||
if (beta != nullptr) {
|
||||
delete copyAlpha;
|
||||
delete copyBeta;
|
||||
}
|
||||
}
|
||||
|
||||
void fillRandomGamma(LaunchContext* context, graph::RandomGenerator& rng, NDArray* alpha, NDArray* beta,
|
||||
NDArray* output) {
|
||||
if (beta)
|
||||
NDArray::prepareSpecialUse({output}, {alpha, beta});
|
||||
else
|
||||
NDArray::prepareSpecialUse({output}, {alpha});
|
||||
BUILD_SINGLE_SELECTOR(output->dataType(), fillRandomGamma_, (context, rng, alpha, beta, output), SD_FLOAT_NATIVE);
|
||||
if (beta)
|
||||
NDArray::registerSpecialUse({output}, {alpha, beta});
|
||||
else
|
||||
NDArray::prepareSpecialUse({output}, {alpha});
|
||||
}
|
||||
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
|
||||
*
|
||||
init:
|
||||
Let x ← 0, p ← e−λ, s ← p.
|
||||
using uniformly random sequence U (u in U) distributed at [0, 1].
|
||||
while u > s do:
|
||||
x ← x + 1.
|
||||
p ← p * λ / x.
|
||||
s ← s + p.
|
||||
return x.
|
||||
* */
|
||||
template <typename T>
|
||||
static SD_KERNEL void fillPoissonKernel(T* uList, LongType uLength, T* lambda, const LongType* lambdaShape,
|
||||
T* output, const LongType* outputShape) {
|
||||
__shared__ LongType lambdaLen;
|
||||
__shared__ LongType rankLambda, rankOutput;
|
||||
__shared__ const LongType *lambdaShapeArr, *lambdaStride, *outputShapeArr, *outputStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
lambdaLen = shape::length(lambdaShape);
|
||||
|
||||
rankLambda = shape::rank(lambdaShape);
|
||||
rankOutput = shape::rank(outputShape);
|
||||
|
||||
lambdaShapeArr = shape::shapeOf(lambdaShape);
|
||||
lambdaStride = shape::stride(lambdaShape);
|
||||
|
||||
outputShapeArr = shape::shapeOf(outputShape);
|
||||
outputStride = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (LongType k = blockIdx.x; k < uLength; k += gridDim.x) {
|
||||
const auto pos = k * lambdaLen;
|
||||
const auto u = uList[k];
|
||||
|
||||
for (LongType e = threadIdx.x; e < lambdaLen; e += blockDim.x) {
|
||||
auto p = math::sd_exp<T, T>(-lambda[e]);
|
||||
auto s = p;
|
||||
auto x = T(0.);
|
||||
|
||||
LongType lCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
LongType lIndex, zIndex;
|
||||
|
||||
// Compute coordinates and indices for lambda
|
||||
INDEX2COORDS(e, rankLambda, lambdaShapeArr, lCoords);
|
||||
COORDS2INDEX(rankLambda, lambdaStride, lCoords, lIndex);
|
||||
|
||||
// Compute coordinates and indices for output
|
||||
INDEX2COORDS(e + pos, rankOutput, outputShapeArr, zCoords);
|
||||
COORDS2INDEX(rankOutput, outputStride, zCoords, zIndex);
|
||||
|
||||
// Compute Poisson distributed value
|
||||
while (u > s) {
|
||||
x += T(1.);
|
||||
p *= lambda[lIndex] / x;
|
||||
s += p;
|
||||
}
|
||||
|
||||
// Assign computed value to output
|
||||
output[zIndex] = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static void fillRandomPoisson_(LaunchContext* context, graph::RandomGenerator& rng, NDArray* lambda, NDArray* output) {
|
||||
auto shift = output->lengthOf() / lambda->lengthOf();
|
||||
std::vector<LongType> shape = {shift};
|
||||
NDArray uniform('c',shape, DOUBLE);
|
||||
PointersManager manager(context, "fillRandomPoisson");
|
||||
auto stream = context->getCudaStream();
|
||||
// fill up uniform with given length
|
||||
NDArray tempOutput = output->cast(DOUBLE);
|
||||
RandomLauncher::fillUniform(context, rng, &uniform, 0., 1.);
|
||||
|
||||
NDArray tempLambda = lambda->cast(DOUBLE);
|
||||
NDArray::prepareSpecialUse({output,&tempOutput}, {lambda,&tempLambda});
|
||||
|
||||
dim3 launchDims = getLaunchDims("random_poisson");
|
||||
fillPoissonKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(uniform.dataBuffer()->specialAsT<T>(), uniform.lengthOf(),
|
||||
tempLambda.dataBuffer()->specialAsT<T>(), tempLambda.specialShapeInfo(),
|
||||
tempOutput.dataBuffer()->specialAsT<T>(), tempOutput.specialShapeInfo());
|
||||
|
||||
sd::DebugHelper::checkErrorCode(stream, "fillPoissonKernel failed");
|
||||
NDArray ret = tempOutput.cast(output->dataType());
|
||||
output->assign(&ret);
|
||||
NDArray::registerSpecialUse({output,&tempOutput}, {lambda,&tempLambda});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
void fillRandomPoisson(LaunchContext* context, graph::RandomGenerator& rng, NDArray* lambda, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {lambda});
|
||||
BUILD_SINGLE_SELECTOR(output->dataType(), fillRandomPoisson_, (context, rng, lambda, output), SD_FLOAT_NATIVE);
|
||||
NDArray::registerSpecialUse({output}, {lambda});
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void fillRandomPoisson_,
|
||||
(LaunchContext * context, graph::RandomGenerator& rng, NDArray* lambda, NDArray* output),
|
||||
SD_FLOAT_NATIVE);
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void fillUniformKernel(graph::RandomGenerator* devRng, T from, T to, T* output,
|
||||
const LongType* outputShape) {
|
||||
const auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = blockDim.x * gridDim.x;
|
||||
|
||||
__shared__ LongType outputLen;
|
||||
__shared__ LongType rank;
|
||||
__shared__ const LongType *shape, *stride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
outputLen = shape::length(outputShape);
|
||||
rank = shape::rank(outputShape);
|
||||
shape = shape::shapeOf(outputShape);
|
||||
stride = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
|
||||
for (LongType i = start; i < outputLen; i += step) {
|
||||
LongType zIndex;
|
||||
|
||||
// Calculate output index
|
||||
INDEX2COORDS(i, rank, shape, zCoords);
|
||||
COORDS2INDEX(rank, stride, zCoords, zIndex);
|
||||
|
||||
// Fill output with a random value in the range [from, to]
|
||||
output[zIndex] = devRng->relativeT<T>(i, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void fillRandomUniform_(LaunchContext* context, graph::RandomGenerator& rng, NDArray* min, NDArray* max,
|
||||
NDArray* output) {
|
||||
T minVal = T(0);
|
||||
T maxVal = DataTypeUtils::infOrMax<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 {
|
||||
auto stream = context->getCudaStream();
|
||||
graph::RandomGenerator* devRng;
|
||||
auto err = cudaMalloc(&devRng, sizeof(graph::RandomGenerator));
|
||||
if (err != 0) {
|
||||
cuda_exception::build("fillRandomUniform_: Cannot allocate device memory for random generator due error", err);
|
||||
}
|
||||
|
||||
err = cudaMemcpy(devRng, &rng, sizeof(graph::RandomGenerator), cudaMemcpyHostToDevice);
|
||||
if (err != 0) {
|
||||
cuda_exception::build("fillRandomUniform_: Cannot copy random generator to device", err);
|
||||
}
|
||||
auto outputBuf = output->dataBuffer()->specialAsT<T>();
|
||||
auto outputShape = output->specialShapeInfo();
|
||||
dim3 launchDims = getLaunchDims("random_uniform");
|
||||
fillUniformKernel<T><<<launchDims.x,launchDims.y, launchDims.z, *stream>>>(devRng, minVal, maxVal, outputBuf, outputShape);
|
||||
sd::DebugHelper::checkErrorCode(stream, "fillUniformKernel failed");
|
||||
|
||||
err = cudaStreamSynchronize(*stream);
|
||||
if (err != 0) {
|
||||
cuda_exception::build("fillRandomUniform_: Cannot successfully finish kernel call", err);
|
||||
}
|
||||
|
||||
err = cudaFree(devRng);
|
||||
if (err != 0) {
|
||||
cuda_exception::build("fillRandomUniform_: Cannot deallocate device memory for random generator", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 X, typename Z>
|
||||
SD_KERNEL static void fillMultiNomialCuda_(graph::RandomGenerator* devRng, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const LongType batchValue,
|
||||
const LongType numOfSamples, const LongType numOfClassX, const LongType dimA, const X minVal,
|
||||
const X maxVal) {
|
||||
const X* x = reinterpret_cast<const X*>(vx);
|
||||
Z* z = reinterpret_cast<Z*>(vz);
|
||||
|
||||
__shared__ LongType xDimAstride, zDimAstride, xDimCstride, zDimCstride, dimC;
|
||||
|
||||
if (0 == threadIdx.x) {
|
||||
dimC = (0 == dimA) ? 1 : 0;
|
||||
zDimAstride = shape::stride(zShapeInfo)[dimA];
|
||||
xDimAstride = shape::stride(xShapeInfo)[dimA];
|
||||
zDimCstride = shape::stride(zShapeInfo)[dimC];
|
||||
xDimCstride = shape::stride(xShapeInfo)[dimC];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
for (LongType index = tid; index < batchValue * numOfSamples; index += gridDim.x * blockDim.x) {
|
||||
LongType nBatchIndex = index / numOfSamples;
|
||||
LongType nSampleIndexInBatch = index - (nBatchIndex * numOfSamples);
|
||||
|
||||
const X* xTad = x + (nBatchIndex * xDimCstride);
|
||||
Z* zTad = z + (nBatchIndex * zDimCstride);
|
||||
Z& arg = zTad[nSampleIndexInBatch * zDimAstride];
|
||||
|
||||
X Max = -minVal;
|
||||
LongType nSamplesPerBatch = nBatchIndex * numOfClassX * numOfSamples;
|
||||
LongType nClassPerSamples = nSampleIndexInBatch * numOfClassX;
|
||||
|
||||
for (LongType nClass = 0; nClass < numOfClassX; nClass++) {
|
||||
LongType nIndex = nSamplesPerBatch + nClassPerSamples + nClass;
|
||||
X tValue = (xTad[nClass * xDimAstride] -
|
||||
math::sd_log<X, X>(-math::sd_log<X, X>(devRng->relativeT<X>(nIndex, minVal, maxVal))));
|
||||
if (tValue > Max) {
|
||||
Max = tValue;
|
||||
arg = nClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
SD_HOST static void fillMultiNomialCudaLauncher(const int blocksPerGrid, const int threadsPerBlock,
|
||||
const cudaStream_t* stream, graph::RandomGenerator* devRng,
|
||||
const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const LongType batchValue,
|
||||
const LongType numOfSamples, const LongType numOfClassX,
|
||||
const LongType dimA) {
|
||||
const X minVal = DataTypeUtils::min<X>();
|
||||
const X maxVal = static_cast<X>(1.0);
|
||||
|
||||
fillMultiNomialCuda_<X, Z><<<blocksPerGrid, threadsPerBlock, 256, *stream>>>(
|
||||
devRng, vx, xShapeInfo, vz, zShapeInfo, batchValue, numOfSamples, numOfClassX, dimA, minVal, maxVal);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "fillMultiNomialCuda_ failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void fillRandomMultiNomial(LaunchContext* context, graph::RandomGenerator& rng, NDArray& input, NDArray& output,
|
||||
const LongType numOfSamples, const int dimC) {
|
||||
LongType dimA = (0 == dimC) ? 1 : 0;
|
||||
|
||||
const LongType batchValue = output.sizeAt(dimC);
|
||||
const LongType numOfClassX = input.sizeAt(dimA);
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (batchValue * numOfSamples + threadsPerBlock - 1) / threadsPerBlock;
|
||||
|
||||
PointersManager manager(context, "fillMultinomial");
|
||||
graph::RandomGenerator* devRng;
|
||||
|
||||
auto err = cudaMalloc(&devRng, sizeof(graph::RandomGenerator));
|
||||
if (err != 0) {
|
||||
cuda_exception::build("fillRandomMultiNomial: Cannot allocate device memory for random generator due error", err);
|
||||
}
|
||||
err = cudaStreamSynchronize(*context->getCudaStream());
|
||||
if (err != 0) {
|
||||
cuda_exception::build("fillRandomMultiNomial: Cannot synchronize stream for random generator due error", err);
|
||||
}
|
||||
err =
|
||||
cudaMemcpyAsync(devRng, &rng, sizeof(graph::RandomGenerator), cudaMemcpyHostToDevice, *context->getCudaStream());
|
||||
if (err != 0) {
|
||||
cuda_exception::build("fillRandomMultiNomial: Cannot copy random generator to device", err);
|
||||
}
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&input});
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), output.dataType(), fillMultiNomialCudaLauncher,
|
||||
(blocksPerGrid, threadsPerBlock, context->getCudaStream(), devRng, input.specialBuffer(),
|
||||
input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), batchValue,
|
||||
numOfSamples, numOfClassX, dimA),
|
||||
SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&input});
|
||||
manager.synchronize();
|
||||
|
||||
err = cudaFree(devRng);
|
||||
if (err != 0) {
|
||||
cuda_exception::build("fillRandomMultiNomial: Cannot deallocate device memory for random generator", err);
|
||||
}
|
||||
rng.rewindH(output.lengthOf() * numOfClassX);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,228 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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)
|
||||
// implemented algorithm is GPU adaptation of algorithm described in following article:
|
||||
// "MergeShuffle: A Very Fast, Parallel Random Permutation Algorithm", https://arxiv.org/abs/1508.03167
|
||||
//
|
||||
#include <array/ResultSet.h>
|
||||
#include <execution/Threads.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static SD_KERNEL void fisherYatesCuda(graph::RandomGenerator* rng, void* vx, const LongType ews,
|
||||
const LongType len, const int power) {
|
||||
T* x = reinterpret_cast<T*>(vx);
|
||||
|
||||
__shared__ T *shmem, temp;
|
||||
__shared__ LongType ind, blockOffset, lenPerBlock;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char sharedMemory[];
|
||||
shmem = reinterpret_cast<T*>(sharedMemory);
|
||||
|
||||
blockOffset = (len * blockIdx.x) >> power;
|
||||
lenPerBlock = ((len * (blockIdx.x + 1)) >> power) - blockOffset;
|
||||
ind = blockOffset;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// copy from global memory to shared memory
|
||||
if (threadIdx.x < lenPerBlock) shmem[threadIdx.x] = x[(blockOffset + threadIdx.x) * ews];
|
||||
__syncthreads();
|
||||
|
||||
// *** apply Fisher-Yates shuffle to lenPerBlock number of elements
|
||||
if (threadIdx.x == 0) {
|
||||
for (LongType i = lenPerBlock - 1; i > 0; --i) {
|
||||
const LongType j = rng->relativeLong(ind++) % (i + 1);
|
||||
if (i != j) {
|
||||
temp = shmem[i];
|
||||
shmem[i] = shmem[j];
|
||||
shmem[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// copy from shared memory to global memory
|
||||
if (threadIdx.x < lenPerBlock) x[(blockOffset + threadIdx.x) * ews] = shmem[threadIdx.x];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void mergeShuffleCuda(graph::RandomGenerator* rng, void* vx, const LongType ews,
|
||||
const LongType len, const int power, const LongType iterNum) {
|
||||
T* x = reinterpret_cast<T*>(vx);
|
||||
|
||||
__shared__ LongType ind, blockOffset, factor, beg, mid, totLen, iterExp;
|
||||
|
||||
// *** apply mergeShuffle algorithm
|
||||
if (threadIdx.x == 0) {
|
||||
factor = blockIdx.x << iterNum;
|
||||
iterExp = 1 << (iterNum - 1);
|
||||
blockOffset = (len * factor) >> power;
|
||||
mid = ((len * (factor + iterExp)) >> power) - blockOffset; // middle
|
||||
totLen = ((len * (factor + 2 * iterExp)) >> power) - blockOffset;
|
||||
ind = iterNum * len + blockOffset;
|
||||
beg = 0; // beginning
|
||||
|
||||
while (true) {
|
||||
if (rng->relativeLong(ind++) % 2) {
|
||||
if (mid == totLen) break;
|
||||
int first = (blockOffset + beg) * ews;
|
||||
int second = blockOffset + mid * ews;
|
||||
if(first >= len || second >= len) {
|
||||
break;
|
||||
}
|
||||
math::sd_swap<T>(x[(blockOffset + beg) * ews], x[(blockOffset + mid++) * ews]);
|
||||
} else {
|
||||
if (beg == mid) break;
|
||||
}
|
||||
++beg;
|
||||
}
|
||||
|
||||
// Fisher-Yates
|
||||
while (beg < totLen) {
|
||||
const LongType e = rng->relativeLong(ind++) % (beg + 1);
|
||||
int first = (blockOffset + beg) * ews;
|
||||
int second = blockOffset + e * ews;
|
||||
if(first >= len || second >= len) {
|
||||
break;
|
||||
}
|
||||
if (beg != e) math::sd_swap<T>(x[(blockOffset + beg) * ews], x[(blockOffset + e) * ews]);
|
||||
++beg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Fisher-Yates shuffle
|
||||
template <typename T>
|
||||
static void fisherYates(graph::RandomGenerator& rng, T* buff, const LongType& len, const LongType& ews, LongType ind) {
|
||||
for (LongType i = len - 1; i > 0; --i) {
|
||||
const LongType j = rng.relativeLong(ind++) % (i + 1);
|
||||
if (i != j) math::sd_swap<T>(buff[i * ews], buff[j * ews]);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void randomShuffle_(LaunchContext* context, NDArray& input, NDArray& output, graph::RandomGenerator& rng,
|
||||
const bool isInplace) {
|
||||
const int firstDim = input.sizeAt(0);
|
||||
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 LongType len = arr->lengthOf();
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS;
|
||||
|
||||
int power = 0;
|
||||
while ((len >> power) > threadsPerBlock) ++power;
|
||||
|
||||
dim3 fisherDims = randomShuffleFisherDims(power,input.sizeOfT());
|
||||
const int blocksPerGrid = fisherDims.y;
|
||||
const int sharedMem = fisherDims.z;
|
||||
|
||||
PointersManager manager(context, "NDArray::randomShuffle cuda");
|
||||
|
||||
graph::RandomGenerator* pRng = reinterpret_cast<graph::RandomGenerator*>(
|
||||
manager.replicatePointer(&rng, sizeof(graph::RandomGenerator)));
|
||||
|
||||
NDArray::prepareSpecialUse({arr}, {arr});
|
||||
|
||||
fisherYatesCuda<T><<<fisherDims.y, fisherDims.x, fisherDims.z, *context->getCudaStream()>>>(
|
||||
pRng, arr->specialBuffer(),0, len, power);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "fisherYatesCuda failed");
|
||||
|
||||
for (LongType j = 1, i = 1; j < blocksPerGrid; j += j, ++i) {
|
||||
dim3 mergeShuffleDims = randomShuffleMergeDims(j, power);
|
||||
mergeShuffleCuda<T><<<mergeShuffleDims.x, mergeShuffleDims.y, mergeShuffleDims.z, *context->getCudaStream()>>>(
|
||||
pRng, arr->specialBuffer(), 0, len, power, i);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "mergeShuffleCuda failed");
|
||||
|
||||
NDArray::registerSpecialUse({arr}, {arr});
|
||||
|
||||
manager.synchronize();
|
||||
|
||||
rng.rewindH((len + 1) * power);
|
||||
}
|
||||
} else {
|
||||
LongType dim = 0;
|
||||
auto dimsToExclude = ShapeUtils::evalDimsToExclude(input.rankOf(),1 ,&dim);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
delete dimsToExclude;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
void randomShuffle(LaunchContext* context, NDArray& input, NDArray& output, graph::RandomGenerator& rng,
|
||||
const bool isInplace) {
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), randomShuffle_, (context, input, output, rng, isInplace), 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 sgazeos@gmail.com
|
||||
//
|
||||
#include <ops/declarable/helpers/random_crop.h>
|
||||
#include <graph/Context.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static Status _randomCropFunctor(graph::Context& context, NDArray* input, NDArray* shape, NDArray* output, int seed) {
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -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 Yurii Shyrma (iuriish@yahoo.com), created on 27.08.2018
|
||||
//
|
||||
|
||||
#include <ops/declarable/helpers/range.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void global_range(void* output, LongType length, T start, T delta) {
|
||||
auto buff = reinterpret_cast<T*>(output);
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
for (LongType i = tid; i < length; i += step) {
|
||||
buff[i] = static_cast<T>(start) + static_cast<T>(i) * static_cast<T>(delta);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// be careful: outVector must have c-order and ews = 1 !!!
|
||||
template <typename T>
|
||||
static void _range(LaunchContext* context, NDArray& start, NDArray& delta, NDArray& outVector) {
|
||||
dim3 launchDims = getLaunchDims("range");
|
||||
global_range<T><<<launchDims.y, launchDims.x, launchDims.z, *context->getCudaStream()>>>(outVector.specialBuffer(), outVector.lengthOf(),
|
||||
start.e<T>(0), delta.e<T>(0));
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "global_range failed");
|
||||
|
||||
}
|
||||
|
||||
void range(LaunchContext* context, NDArray& start, NDArray& delta, NDArray& outVector) {
|
||||
NDArray::prepareSpecialUse({&outVector}, {&start, &delta});
|
||||
BUILD_SINGLE_SELECTOR(outVector.dataType(), _range, (context, start, delta, outVector), SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({&outVector}, {&start, &delta});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,319 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/reverse.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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
|
||||
******************************************************************************/
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void reverseTadKernel(const void* vinput, const LongType* inputShape, void* voutput,
|
||||
const LongType* outputShape, const LongType* inputTadShape,
|
||||
const LongType* inputTadOffsets, const LongType* outputTadShape,
|
||||
const LongType* outputTadOffsets, uint64_t limit,
|
||||
uint64_t numOfElemsToReverse, uint64_t numTads) {
|
||||
auto input = reinterpret_cast<const T*>(vinput);
|
||||
auto output = reinterpret_cast<T*>(voutput);
|
||||
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ LongType tadRankInput, tadRankOutput;
|
||||
__shared__ const LongType *tadShapeInput, *strideInput, *tadShapeOutput, *strideOutput;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
tadRankInput = shape::rank(inputTadShape);
|
||||
tadShapeInput = shape::shapeOf(inputTadShape);
|
||||
strideInput = shape::stride(inputTadShape);
|
||||
|
||||
tadRankOutput = shape::rank(outputTadShape);
|
||||
tadShapeOutput = shape::shapeOf(outputTadShape);
|
||||
strideOutput = shape::stride(outputTadShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto div = numOfElemsToReverse / 2;
|
||||
const auto odd = numOfElemsToReverse % 2 != 0;
|
||||
const auto rlimit = odd ? limit / 2 + 1 : limit / 2;
|
||||
|
||||
// Main loop for element swaps
|
||||
for (uint64_t e = tid; e < rlimit; e += step) {
|
||||
const auto tadId = e / div;
|
||||
if (tadId >= numTads) continue;
|
||||
|
||||
const auto idx = e % div;
|
||||
|
||||
const auto tadInput = input + inputTadOffsets[tadId];
|
||||
const auto tadOutput = output + outputTadOffsets[tadId];
|
||||
|
||||
LongType fCoords[SD_MAX_RANK], lCoords[SD_MAX_RANK];
|
||||
LongType fOffset, lOffset;
|
||||
|
||||
// Input coordinates and offsets
|
||||
INDEX2COORDS(idx, tadRankInput, tadShapeInput, fCoords);
|
||||
COORDS2INDEX(tadRankInput, strideInput, fCoords, fOffset);
|
||||
|
||||
INDEX2COORDS(numOfElemsToReverse - idx - 1, tadRankInput, tadShapeInput, lCoords);
|
||||
COORDS2INDEX(tadRankInput, strideInput, lCoords, lOffset);
|
||||
|
||||
auto v1 = tadInput[fOffset];
|
||||
auto v2 = tadInput[lOffset];
|
||||
|
||||
LongType zfCoords[SD_MAX_RANK], zlCoords[SD_MAX_RANK];
|
||||
LongType zfOffset, zlOffset;
|
||||
|
||||
// Output coordinates and offsets
|
||||
INDEX2COORDS(idx, tadRankOutput, tadShapeOutput, zfCoords);
|
||||
COORDS2INDEX(tadRankOutput, strideOutput, zfCoords, zfOffset);
|
||||
|
||||
INDEX2COORDS(numOfElemsToReverse - idx - 1, tadRankOutput, tadShapeOutput, zlCoords);
|
||||
COORDS2INDEX(tadRankOutput, strideOutput, zlCoords, zlOffset);
|
||||
|
||||
// Store swapped values
|
||||
tadOutput[zfOffset] = v2;
|
||||
tadOutput[zlOffset] = v1;
|
||||
}
|
||||
|
||||
// Handle odd middle element
|
||||
if (odd && threadIdx.x == 0) {
|
||||
for (uint64_t e = blockIdx.x; e < numTads; e += gridDim.x) {
|
||||
const auto tadInput = input + inputTadOffsets[e];
|
||||
const auto tadOutput = output + outputTadOffsets[e];
|
||||
|
||||
LongType xCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
LongType xOffset, zOffset;
|
||||
|
||||
// Coordinates and offsets for the middle element
|
||||
INDEX2COORDS(numOfElemsToReverse / 2, tadRankInput, tadShapeInput, xCoords);
|
||||
COORDS2INDEX(tadRankInput, strideInput, xCoords, xOffset);
|
||||
|
||||
INDEX2COORDS(numOfElemsToReverse / 2, tadRankOutput, tadShapeOutput, zCoords);
|
||||
COORDS2INDEX(tadRankOutput, strideOutput, zCoords, zOffset);
|
||||
|
||||
tadOutput[zOffset] = tadInput[xOffset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static SD_KERNEL void reverseArrayKernel(const void* input, const LongType* inputShape, void* output,
|
||||
const LongType* outputShape, LongType numOfElemsToReverse) {
|
||||
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto step = gridDim.x * blockDim.x;
|
||||
|
||||
__shared__ const T* inputArr;
|
||||
__shared__ T* outputArr;
|
||||
__shared__ LongType rankInput, rankOutput;
|
||||
__shared__ const LongType *inputShapeArr, *inputStride, *outputShapeArr, *outputStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
inputArr = reinterpret_cast<const T*>(input);
|
||||
outputArr = reinterpret_cast<T*>(output);
|
||||
|
||||
rankInput = shape::rank(inputShape);
|
||||
rankOutput = shape::rank(outputShape);
|
||||
|
||||
inputShapeArr = shape::shapeOf(inputShape);
|
||||
inputStride = shape::stride(inputShape);
|
||||
|
||||
outputShapeArr = shape::shapeOf(outputShape);
|
||||
outputStride = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const auto odd = numOfElemsToReverse % 2 != 0;
|
||||
const auto limit = numOfElemsToReverse / 2;
|
||||
|
||||
for (LongType e = tid; e < limit; e += step) {
|
||||
LongType fCoords[SD_MAX_RANK], lCoords[SD_MAX_RANK];
|
||||
LongType fOffset, lOffset;
|
||||
|
||||
// Input indices
|
||||
INDEX2COORDS(e, rankInput, inputShapeArr, fCoords);
|
||||
COORDS2INDEX(rankInput, inputStride, fCoords, fOffset);
|
||||
|
||||
INDEX2COORDS(numOfElemsToReverse - e - 1, rankInput, inputShapeArr, lCoords);
|
||||
COORDS2INDEX(rankInput, inputStride, lCoords, lOffset);
|
||||
|
||||
auto v1 = inputArr[fOffset];
|
||||
auto v2 = inputArr[lOffset];
|
||||
|
||||
LongType zfCoords[SD_MAX_RANK], zlCoords[SD_MAX_RANK];
|
||||
LongType zfOffset, zlOffset;
|
||||
|
||||
// Output indices
|
||||
INDEX2COORDS(e, rankOutput, outputShapeArr, zfCoords);
|
||||
COORDS2INDEX(rankOutput, outputStride, zfCoords, zfOffset);
|
||||
|
||||
INDEX2COORDS(numOfElemsToReverse - e - 1, rankOutput, outputShapeArr, zlCoords);
|
||||
COORDS2INDEX(rankOutput, outputStride, zlCoords, zlOffset);
|
||||
|
||||
outputArr[zfOffset] = v2;
|
||||
outputArr[zlOffset] = v1;
|
||||
}
|
||||
|
||||
// Handle the odd middle element if applicable
|
||||
if (odd && tid == 0) {
|
||||
LongType xCoords[SD_MAX_RANK], zCoords[SD_MAX_RANK];
|
||||
LongType xOffset, zOffset;
|
||||
|
||||
INDEX2COORDS(limit, rankInput, inputShapeArr, xCoords);
|
||||
COORDS2INDEX(rankInput, inputStride, xCoords, xOffset);
|
||||
|
||||
INDEX2COORDS(limit, rankOutput, outputShapeArr, zCoords);
|
||||
COORDS2INDEX(rankOutput, outputStride, zCoords, zOffset);
|
||||
|
||||
outputArr[zOffset] = inputArr[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void reverseTad(LaunchContext* context, NDArray* input, NDArray* output,
|
||||
const LongType* inputTadShape, const LongType* inputTadOffsets,
|
||||
const LongType* outputTadShape, const LongType* outputTadOffsets, uint64_t tadLength) {
|
||||
auto stream = context->getCudaStream();
|
||||
dim3 launchDims = getLaunchDims("reverse");
|
||||
|
||||
reverseTadKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(input->specialBuffer(), input->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), inputTadShape,
|
||||
inputTadOffsets, outputTadShape, outputTadOffsets, input->lengthOf(),
|
||||
tadLength, input->lengthOf() / tadLength);
|
||||
sd::DebugHelper::checkErrorCode(stream, "reverseTadKernel failed");
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void reverseArray(LaunchContext* context, NDArray* input, NDArray* output, LongType numOfElemsToReverse) {
|
||||
auto stream = context->getCudaStream();
|
||||
LongType numOfReverse = numOfElemsToReverse;
|
||||
if (numOfElemsToReverse == 0) numOfReverse = input->lengthOf();
|
||||
dim3 launchDims = getLaunchDims("reverse");
|
||||
|
||||
reverseArrayKernel<T><<<launchDims.y,launchDims.x, launchDims.z, *stream>>>(input->specialBuffer(), input->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), numOfReverse);
|
||||
sd::DebugHelper::checkErrorCode(stream, "reverseArrayKernel failed");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void reverseSequence_(LaunchContext* context, NDArray* input, NDArray* seqLengths,
|
||||
NDArray* output, int seqDim, const int batchDim) {
|
||||
int posOfNonUnityDim = -1;
|
||||
seqLengths->syncToHost();
|
||||
auto stream = context->getCudaStream();
|
||||
dim3 launchDims = getLaunchDims("reverse");
|
||||
if (input->isVector() || shape::isLikeVector(input->shapeInfo(), posOfNonUnityDim) || seqLengths->lengthOf() == 1) {
|
||||
LongType numOfElemsToReverse = seqLengths->e<LongType>(0);
|
||||
if ((seqDim == 0 && input->sizeAt(0) == 1) || (batchDim == posOfNonUnityDim))
|
||||
output->assign(input);
|
||||
else
|
||||
reverseArrayKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(),
|
||||
numOfElemsToReverse);
|
||||
sd::DebugHelper::checkErrorCode(stream, "reverseArrayKernel failed");
|
||||
|
||||
} else {
|
||||
if (seqDim > batchDim) --seqDim;
|
||||
|
||||
std::vector<LongType> dim = {batchDim};
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,dim.data());
|
||||
|
||||
auto inSubArrsSet = input->allTensorsAlongDimension(*dimensions);
|
||||
auto outSubArrsSet = output->allTensorsAlongDimension(*dimensions);
|
||||
|
||||
for (int i = 0; i < inSubArrsSet.size(); ++i) {
|
||||
LongType numOfElemsToReverse = seqLengths->e<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)
|
||||
reverseArray<T>(context, inInnerSet.at(j), outInnerSet.at(j), numOfElemsToReverse);
|
||||
}
|
||||
}
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
}
|
||||
|
||||
void reverseSequence(LaunchContext* context, NDArray* input, NDArray* seqLengths, NDArray* output,
|
||||
int seqDim, const int batchDim) {
|
||||
NDArray::prepareSpecialUse({output}, {input, seqLengths});
|
||||
|
||||
// if op isn't inplace - copy original data into output array
|
||||
if (output->specialBuffer() != input->specialBuffer()) output->assign(input);
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), reverseSequence_, (context, input, seqLengths, output, seqDim, batchDim),
|
||||
SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, seqLengths});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void reverse(LaunchContext* context, NDArray* input, NDArray* output, const std::vector<LongType>* intArgs) {
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), reinterpret_cast<LongType*>(*intArgs->data()),static_cast<sd::LongType>(intArgs->size()));
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), reinterpret_cast<LongType*>(*intArgs->data()),static_cast<sd::LongType>(intArgs->size()));
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
|
||||
if (packX->numberOfTads() == 1) {
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), reverseArray, (context, input, output, 0), SD_COMMON_TYPES);
|
||||
} else {
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input->dataType(), reverseTad,
|
||||
(context, input, output, packX->platformShapeInfo(), packX->platformOffsets(), packZ->platformShapeInfo(),
|
||||
packZ->platformOffsets(), (uint64_t)(input->lengthOf() / packX->numberOfTads())),
|
||||
SD_COMMON_TYPES);
|
||||
}
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,373 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/roll.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename T>
|
||||
static void SD_DEVICE rollKernelLinearStage1Dev(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, LongType fullLength,
|
||||
int actualShift) {
|
||||
auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
// Cache shape information for x buffer
|
||||
__shared__ sd::LongType xRank;
|
||||
__shared__ const sd::LongType* xShapePtr;
|
||||
__shared__ const sd::LongType* xStridePtr;
|
||||
|
||||
// Cache shape information for z buffer
|
||||
__shared__ sd::LongType zRank;
|
||||
__shared__ const sd::LongType* zShapePtr;
|
||||
__shared__ const sd::LongType* zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache x shape information
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
|
||||
// Cache z shape information
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xOffsetA;
|
||||
LongType xOffsetB;
|
||||
LongType zOffsetA;
|
||||
LongType zOffsetB;
|
||||
|
||||
for (LongType i = tid; i < actualShift; i += blockDim.x * gridDim.x) {
|
||||
int sourceIndex = fullLength - actualShift + i;
|
||||
|
||||
INDEX2COORDS(i, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffsetA);
|
||||
INDEX2COORDS(sourceIndex, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffsetB);
|
||||
|
||||
INDEX2COORDS(i, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffsetA);
|
||||
INDEX2COORDS(sourceIndex, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffsetB);
|
||||
|
||||
auto eA = x[xOffsetA];
|
||||
auto eB = x[xOffsetB];
|
||||
|
||||
z[zOffsetA] = eB;
|
||||
z[zOffsetB] = eA;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void SD_KERNEL rollKernelLinearStage1(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, LongType fullLength, int actualShift) {
|
||||
rollKernelLinearStage1Dev<T>(vx, xShapeInfo, vz, zShapeInfo, fullLength, actualShift);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void SD_KERNEL rollKernelLinearStage2(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, LongType fullLength, int actualShift,
|
||||
int shiftCount) {
|
||||
auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
// Cache shape information for x buffer
|
||||
__shared__ sd::LongType xRank;
|
||||
__shared__ const sd::LongType* xShapePtr;
|
||||
__shared__ const sd::LongType* xStridePtr;
|
||||
|
||||
// Cache shape information for z buffer
|
||||
__shared__ sd::LongType zRank;
|
||||
__shared__ const sd::LongType* zShapePtr;
|
||||
__shared__ const sd::LongType* zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache x shape information
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
|
||||
// Cache z shape information
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xOffsetA;
|
||||
LongType xOffsetB;
|
||||
LongType zOffsetA;
|
||||
LongType zOffsetB;
|
||||
|
||||
for (int count = 1; count < shiftCount; ++count) {
|
||||
for (int i = tid; i < actualShift; i += blockDim.x * gridDim.x) {
|
||||
int destinationIndex = fullLength - (count + 1) * actualShift + i;
|
||||
int sourceIndex = fullLength - count * actualShift + i;
|
||||
|
||||
INDEX2COORDS(destinationIndex, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffsetA);
|
||||
INDEX2COORDS(sourceIndex, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffsetB);
|
||||
|
||||
INDEX2COORDS(destinationIndex, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffsetA);
|
||||
INDEX2COORDS(sourceIndex, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffsetB);
|
||||
|
||||
auto eA = x[xOffsetB];
|
||||
auto eB = x[xOffsetA];
|
||||
|
||||
z[zOffsetA] = eA;
|
||||
z[zOffsetB] = eB;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void SD_KERNEL rollKernelLinearStage3(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, LongType fullLength, int actualShift,
|
||||
int remainShift) {
|
||||
auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
// Cache shape information for x buffer
|
||||
__shared__ sd::LongType xRank;
|
||||
__shared__ const sd::LongType* xShapePtr;
|
||||
__shared__ const sd::LongType* xStridePtr;
|
||||
|
||||
// Cache shape information for z buffer
|
||||
__shared__ sd::LongType zRank;
|
||||
__shared__ const sd::LongType* zShapePtr;
|
||||
__shared__ const sd::LongType* zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache x shape information
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xShapePtr = shape::shapeOf(xShapeInfo);
|
||||
xStridePtr = shape::stride(xShapeInfo);
|
||||
|
||||
// Cache z shape information
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
for (int i = tid; i < actualShift; i += blockDim.x * gridDim.x) {
|
||||
int remainIdx = i + actualShift;
|
||||
int sourceIndex = remainIdx + remainShift;
|
||||
|
||||
LongType xCoordsA[SD_MAX_RANK];
|
||||
LongType xCoordsB[SD_MAX_RANK];
|
||||
LongType zCoordsA[SD_MAX_RANK];
|
||||
LongType zCoordsB[SD_MAX_RANK];
|
||||
LongType xOffsetA;
|
||||
LongType xOffsetB;
|
||||
LongType zOffsetA;
|
||||
LongType zOffsetB;
|
||||
|
||||
INDEX2COORDS(remainIdx, xRank, xShapePtr, xCoordsA);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoordsA, xOffsetA);
|
||||
INDEX2COORDS(sourceIndex, xRank, xShapePtr, xCoordsB);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoordsB, xOffsetB);
|
||||
|
||||
INDEX2COORDS(remainIdx, zRank, zShapePtr, zCoordsA);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoordsA, zOffsetA);
|
||||
INDEX2COORDS(sourceIndex, zRank, zShapePtr, zCoordsB);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoordsB, zOffsetB);
|
||||
|
||||
auto eA = x[xOffsetA];
|
||||
auto eB = x[xOffsetB];
|
||||
|
||||
z[zOffsetA] = eB;
|
||||
z[zOffsetB] = eA;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void SD_DEVICE swapTadsKernel(void *vx, void *vz, const LongType *zShapeInfo, LongType tadLength) {
|
||||
auto x = reinterpret_cast<T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
// Cache shape information for z buffer
|
||||
__shared__ sd::LongType zRank;
|
||||
__shared__ const sd::LongType* zShapePtr;
|
||||
__shared__ const sd::LongType* zStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache z shape information
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
zShapePtr = shape::shapeOf(zShapeInfo);
|
||||
zStridePtr = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
for (int e = threadIdx.x; e < tadLength; e += blockDim.x) {
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
|
||||
INDEX2COORDS(e, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
|
||||
|
||||
auto eA = x[zOffset];
|
||||
auto eB = z[zOffset];
|
||||
|
||||
x[zOffset] = eB;
|
||||
z[zOffset] = eA;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void SD_KERNEL rollKernelFullAnyDimensionStage1(const void *vx, const LongType *xTadShapeInfo,
|
||||
const LongType *xTadOffsets, void *vz,
|
||||
const LongType *zTadShapeInfo,
|
||||
const LongType *zTadOffsets, int numTads, LongType tadLength, int dim, LongType sizeAt,
|
||||
int theShift) {
|
||||
auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
for (int e = blockIdx.x + theShift; e < sizeAt - theShift; e += gridDim.x) {
|
||||
int sourceIndex = dim * sizeAt + e - theShift;
|
||||
int targetIndex = dim * sizeAt + e;
|
||||
|
||||
swapTadsKernel<T>(z + xTadOffsets[sourceIndex], z + xTadOffsets[targetIndex], zTadShapeInfo, tadLength);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void SD_KERNEL rollKernelFullAnyDimensionStage2(void *vx, const LongType *xTadShapeInfo,
|
||||
const LongType *xTadOffsets, void *vz,
|
||||
const LongType *zTadShapeInfo,
|
||||
const LongType *zTadOffsets, int numTads, LongType tadLength, int dim, LongType sizeAt,
|
||||
int theShift) {
|
||||
auto x = reinterpret_cast<const T *>(vx);
|
||||
auto z = reinterpret_cast<T *>(vz);
|
||||
|
||||
for (int e = blockIdx.x; e < theShift; e += gridDim.x) {
|
||||
int sourceIndex = dim * sizeAt + sizeAt - theShift + e;
|
||||
int targetIndex = dim * sizeAt + e;
|
||||
|
||||
swapTadsKernel<T>(z + zTadOffsets[sourceIndex], z + zTadOffsets[targetIndex], zTadShapeInfo, tadLength);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void rollFunctorFull_(NDArray *input, NDArray *output, std::vector<LongType> const &shifts,
|
||||
std::vector<LongType> const &axes, bool inplace) {
|
||||
if (!inplace) output->assign(input);
|
||||
|
||||
for (size_t i = 0; i < axes.size(); i++) {
|
||||
int axe = axes[i];
|
||||
ResultSet listOfTensors = input->allTensorsAlongDimension({axe});
|
||||
ResultSet listOfOutTensors = output->allTensorsAlongDimension({axe});
|
||||
int fullLen = listOfTensors.size();
|
||||
int theShift = shifts[i];
|
||||
for (int k = 0; k < fullLen; k++) {
|
||||
rollFunctorLinear(output->getContext(), listOfTensors.at(k), listOfOutTensors.at(k), theShift, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static void rollFunctorLinear_(NDArray *input, NDArray *output, int shift, bool inplace) {
|
||||
if (!inplace) output->assign(input);
|
||||
|
||||
dim3 launchDims = getLaunchDims("roll");
|
||||
auto fullLen = input->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.
|
||||
rollKernelLinearStage1<T><<<launchDims.y, launchDims.x, launchDims.z, *(output->getContext()->getCudaStream())>>>(
|
||||
output->specialBuffer(), output->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(),
|
||||
fullLen, actualShift);
|
||||
sd::DebugHelper::checkErrorCode(output->getContext()->getCudaStream(), "rollKernelLinearStage1 failed");
|
||||
|
||||
// stage 2) swap swapped actualShift elements with rest remainShiftCount times.
|
||||
rollKernelLinearStage2<T><<<launchDims.y, launchDims.x, launchDims.z, *(output->getContext()->getCudaStream())>>>(
|
||||
output->specialBuffer(), output->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(),
|
||||
fullLen, actualShift, shiftCount);
|
||||
sd::DebugHelper::checkErrorCode(output->getContext()->getCudaStream(), "rollKernelLinearStage2 failed");
|
||||
// FIXME: no parallelism here :(
|
||||
// stage 3) swap remainer of items.
|
||||
if (remainShift && shiftCount)
|
||||
rollKernelLinearStage3<T><<<launchDims.y,launchDims.x,launchDims.z, *(output->getContext()->getCudaStream())>>>(
|
||||
output->specialBuffer(), output->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(),
|
||||
fullLen, actualShift, remainShift);
|
||||
sd::DebugHelper::checkErrorCode(output->getContext()->getCudaStream(), "rollKernelLinearStage3 failed");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void rollFunctorFull(LaunchContext *context, NDArray *input, NDArray *output, std::vector<LongType> const &shifts,
|
||||
std::vector<LongType> const &axes, bool inplace) {
|
||||
input->syncToDevice();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), rollFunctorFull_, (input, output, shifts, axes, inplace), SD_COMMON_TYPES);
|
||||
|
||||
output->tickWriteDevice();
|
||||
}
|
||||
|
||||
void rollFunctorLinear(LaunchContext *context, NDArray *input, NDArray *output, int shift, bool inplace) {
|
||||
input->syncToDevice();
|
||||
|
||||
BUILD_SINGLE_SELECTOR(input->dataType(), rollFunctorLinear_, (input, output, shift, inplace), SD_COMMON_TYPES);
|
||||
|
||||
output->tickWriteDevice();
|
||||
}
|
||||
|
||||
BUILD_SINGLE_TEMPLATE( void rollFunctorLinear_, (NDArray * input, NDArray *output, int shift, bool inplace),
|
||||
SD_COMMON_TYPES);
|
||||
BUILD_SINGLE_TEMPLATE( void rollFunctorFull_,
|
||||
(NDArray * input, NDArray *output, std::vector<sd::LongType> const &shifts, std::vector<sd::LongType> const &axes,
|
||||
bool inplace),
|
||||
SD_COMMON_TYPES);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,532 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 19.01.18.
|
||||
// @author Yurii Shyrma (iuriish@yahoo.com)
|
||||
//
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <ops/declarable/helpers/s_t_b.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void batchToSpaceCuda(const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const LongType cropBottom,
|
||||
const LongType cropLeft) {
|
||||
const auto x = reinterpret_cast<const T*>(vx);
|
||||
auto z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType rank, zLen;
|
||||
__shared__ const LongType *xShape, *xStride, *zShape, *zStride;
|
||||
__shared__ LongType* sharedMem;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
rank = shape::rank(zShapeInfo);
|
||||
zLen = shape::length(zShapeInfo);
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType* coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < zLen; i += gridDim.x * blockDim.x) {
|
||||
INDEX2COORDS(i, rank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
|
||||
// Adjust spatial coordinates for cropping
|
||||
coords[1] += cropBottom;
|
||||
coords[2] += cropLeft;
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
|
||||
// Assign the value from input to output
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void batchToSpaceCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const LongType cropBottom,
|
||||
const LongType cropLeft) {
|
||||
batchToSpaceCuda<T>
|
||||
<<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, cropBottom, cropLeft);
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void batchToSpaceCudaLauncher,
|
||||
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const sd::LongType* xShapeInfo, void* vz,
|
||||
const sd::LongType* zShapeInfo, const sd::LongType cropBottom, const sd::LongType cropLeft),
|
||||
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> rearrShape = {blockSize, blockSize, output.sizeAt(0), input.sizeAt(1), input.sizeAt(2), input.sizeAt(3)};
|
||||
NDArray inputRearranged0 = input.reshape(
|
||||
input.ordering(), rearrShape,false);
|
||||
inputRearranged0.permutei({2, 3, 0, 4, 1, 5}, false, false);
|
||||
|
||||
if (input.lengthOf() == output.lengthOf()) {
|
||||
output.assign(&inputRearranged0);
|
||||
} else {
|
||||
std::vector<sd::LongType> outputShape = {output.sizeAt(0), input.sizeAt(1) * blockSize, input.sizeAt(2) * blockSize, input.sizeAt(3)};
|
||||
NDArray inputRearranged1 = inputRearranged0.reshape(
|
||||
input.ordering(),
|
||||
outputShape);
|
||||
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (output.lengthOf() + threadsPerBlock - 1) / threadsPerBlock;
|
||||
const int sharedMem = threadsPerBlock * sizeof(LongType) * output.rankOf() + 128;
|
||||
|
||||
PointersManager manager(context, "batchToSpace");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&inputRearranged1});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
input.dataType(), batchToSpaceCudaLauncher,
|
||||
(blocksPerGrid, threadsPerBlock, sharedMem, context->getCudaStream(), inputRearranged1.specialBuffer(),
|
||||
inputRearranged1.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), cropBottom, cropLeft),
|
||||
SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&inputRearranged1});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void batchToSpaceNDCuda(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const LongType numOfSpatialDims) {
|
||||
// x - input, y - crop, z - output
|
||||
|
||||
const auto x = reinterpret_cast<const X*>(vx);
|
||||
const auto y = reinterpret_cast<const Y*>(vy);
|
||||
auto z = reinterpret_cast<X*>(vz);
|
||||
|
||||
__shared__ LongType rank, zLen;
|
||||
__shared__ const LongType *xShape, *xStride, *zShape, *zStride;
|
||||
__shared__ LongType* sharedMem;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
rank = shape::rank(zShapeInfo);
|
||||
zLen = shape::length(zShapeInfo);
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType* coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < zLen; i += gridDim.x * blockDim.x) {
|
||||
INDEX2COORDS(i, rank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
|
||||
// Adjust spatial coordinates for cropping
|
||||
for (LongType j = 1; j <= numOfSpatialDims; ++j) {
|
||||
const LongType yOffset = (j - 1) * yShapeInfo[3]; // yRank = 2, calculate offset manually
|
||||
coords[j] += y[yOffset]; // Add crop offset (cropLeft for each spatial dimension)
|
||||
}
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
|
||||
z[zOffset] = x[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void batchToSpaceNDCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const LongType numOfSpatialDims) {
|
||||
batchToSpaceNDCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz,
|
||||
zShapeInfo, numOfSpatialDims);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "batchToSpaceNDCuda failed");
|
||||
|
||||
}
|
||||
BUILD_DOUBLE_TEMPLATE( void batchToSpaceNDCudaLauncher,
|
||||
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const sd::LongType* xShapeInfo, const void* vy,
|
||||
const sd::LongType* yShapeInfo, void* vz, const sd::LongType* zShapeInfo,
|
||||
const sd::LongType numOfSpatialDims),
|
||||
SD_COMMON_TYPES, SD_INTEGER_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 LongType rank = input.rankOf();
|
||||
const LongType numOfSpatialDims = blockShape.sizeAt(0);
|
||||
|
||||
//*** construct reshaping std::vector for first reshape of input array ***//
|
||||
|
||||
std::vector<LongType> temp(numOfSpatialDims + rank);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < numOfSpatialDims; ++i) temp[i] = blockShape.e<LongType>(i);
|
||||
temp[i++] = output.sizeAt(0);
|
||||
for (int 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 < temp.size(); ++i) temp[i] = i;
|
||||
|
||||
inputRearranged0.permutei(temp, 0, 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<LongType>(i - 1) : input.sizeAt(i);
|
||||
|
||||
NDArray inputRearranged1 = inputRearranged0.reshape(input.ordering(), temp);
|
||||
|
||||
dim3 launchDims = batchToSpaceNdLaunch(output.lengthOf(),output.rankOf());
|
||||
|
||||
PointersManager manager(context, "batchToSpaceND");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&inputRearranged1, &crop});
|
||||
BUILD_DOUBLE_SELECTOR(
|
||||
input.dataType(), crop.dataType(), batchToSpaceNDCudaLauncher,
|
||||
(launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), inputRearranged1.specialBuffer(),
|
||||
inputRearranged1.specialShapeInfo(), crop.specialBuffer(), crop.specialShapeInfo(), output.specialBuffer(),
|
||||
output.specialShapeInfo(), numOfSpatialDims),
|
||||
SD_COMMON_TYPES, SD_INTEGER_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&inputRearranged1, &crop});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void spaceToBatchCuda(const void* vx, const LongType* xShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const LongType padBottom,
|
||||
const LongType padTop, const LongType padLeft,
|
||||
const 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 auto x = reinterpret_cast<const T*>(vx);
|
||||
auto z = reinterpret_cast<T*>(vz);
|
||||
|
||||
__shared__ LongType rank, *sharedMem;
|
||||
__shared__ LongType zLen;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
rank = shape::rank(zShapeInfo);
|
||||
zLen = shape::length(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType* coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
const LongType i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
if (i >= zLen) return;
|
||||
|
||||
INDEX2COORDS(i, rank, shape::shapeOf(zShapeInfo), coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, shape::stride(zShapeInfo), coords, zOffset);
|
||||
|
||||
if (coords[1] >= padBottom && coords[1] < zShapeInfo[2] - padTop && coords[2] >= padLeft &&
|
||||
coords[2] < zShapeInfo[3] - padRight) {
|
||||
coords[1] -= padBottom;
|
||||
coords[2] -= padLeft;
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, shape::stride(xShapeInfo), coords, xOffset);
|
||||
|
||||
z[zOffset] = x[xOffset];
|
||||
} else
|
||||
z[zOffset] = static_cast<T>(0.f);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
static void spaceToBatchCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo, const LongType padBottom,
|
||||
const LongType padTop, const LongType padLeft, const LongType padRight) {
|
||||
spaceToBatchCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, padBottom,
|
||||
padTop, padLeft, padRight);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "spaceToBatchCudaLauncher failed");
|
||||
|
||||
}
|
||||
BUILD_SINGLE_TEMPLATE( void spaceToBatchCudaLauncher,
|
||||
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const sd::LongType* xShapeInfo, void* vz,
|
||||
const sd::LongType* zShapeInfo, const sd::LongType padBottom, const sd::LongType padTop,
|
||||
const sd::LongType padLeft, const sd::LongType padRight),
|
||||
SD_COMMON_TYPES);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void spaceToBatch(LaunchContext* context, NDArray& input, NDArray& output, const LongType padBottom,
|
||||
const LongType padTop, const LongType padLeft, const LongType padRight,
|
||||
const 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> outputShape = {blockSize, blockSize, input.sizeAt(0), output.sizeAt(1), output.sizeAt(2), input.sizeAt(3)};
|
||||
NDArray outputRearranged0 = output.reshape(
|
||||
output.ordering(), outputShape,
|
||||
false);
|
||||
outputRearranged0.permutei({2, 3, 0, 4, 1, 5}, false, false);
|
||||
|
||||
if (input.lengthOf() == output.lengthOf()) {
|
||||
outputRearranged0.assign(&input);
|
||||
} else {
|
||||
std::vector<sd::LongType> outReArrShape = {input.sizeAt(0), output.sizeAt(1) * blockSize, output.sizeAt(2) * blockSize, input.sizeAt(3)};
|
||||
NDArray outputRearranged1 = outputRearranged0.reshape(
|
||||
output.ordering(),
|
||||
outReArrShape, false);
|
||||
|
||||
|
||||
dim3 launchDims = spaceToBatchLaunch(output.lengthOf(),output.rankOf());
|
||||
|
||||
PointersManager manager(context, "spaceToBatch");
|
||||
|
||||
NDArray::prepareSpecialUse({&outputRearranged1}, {&input});
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), spaceToBatchCudaLauncher,
|
||||
(launchDims.y,launchDims.x,launchDims.z, context->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), outputRearranged1.specialBuffer(),
|
||||
outputRearranged1.specialShapeInfo(), padBottom, padTop, padLeft, padRight),
|
||||
SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({&outputRearranged1}, {&input});
|
||||
|
||||
manager.synchronize();
|
||||
|
||||
if (output.specialBuffer() != outputRearranged1.specialBuffer()) outputRearranged0.assign(&outputRearranged1);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void spaceToBatchNDCuda(const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo,
|
||||
const LongType numOfSpatialDims) {
|
||||
// x - input, y - padding, z - output
|
||||
|
||||
const auto x = reinterpret_cast<const X*>(vx);
|
||||
const auto y = reinterpret_cast<const Y*>(vy);
|
||||
auto z = reinterpret_cast<X*>(vz);
|
||||
|
||||
__shared__ LongType rank, zLen, totalThreads;
|
||||
__shared__ const LongType *xShape, *xStride, *zShape, *zStride;
|
||||
__shared__ LongType *sharedMem;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
sharedMem = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
rank = shape::rank(zShapeInfo);
|
||||
zLen = shape::length(zShapeInfo);
|
||||
totalThreads = gridDim.x * blockDim.x;
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
auto coords = sharedMem + threadIdx.x * rank;
|
||||
|
||||
for (LongType i = blockDim.x * blockIdx.x + threadIdx.x; i < zLen; i += totalThreads) {
|
||||
INDEX2COORDS(i, rank, zShape, coords);
|
||||
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(rank, zStride, coords, zOffset);
|
||||
|
||||
bool within = true;
|
||||
|
||||
for (LongType j = 1; j <= numOfSpatialDims; ++j) {
|
||||
// Manually calculate y offsets for padding
|
||||
const LongType yOffset = (j - 1) * yShapeInfo[3];
|
||||
const LongType padLeft = y[yOffset];
|
||||
const LongType padRight = y[yOffset + yShapeInfo[4]];
|
||||
|
||||
// Check if coordinates are within the valid range
|
||||
within &= (coords[j] >= padLeft && coords[j] < zShape[j] - padRight);
|
||||
|
||||
if (!within) break;
|
||||
|
||||
// Adjust coordinates for x
|
||||
coords[j] -= padLeft;
|
||||
}
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(rank, xStride, coords, xOffset);
|
||||
|
||||
// Assign values to z
|
||||
if (within)
|
||||
z[zOffset] = x[xOffset];
|
||||
else
|
||||
z[zOffset] = static_cast<X>(0.f);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void spaceToBatchNDCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
|
||||
const void* vy, const LongType* yShapeInfo, void* vz,
|
||||
const LongType* zShapeInfo, const LongType numOfSpatialDims) {
|
||||
spaceToBatchNDCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz,
|
||||
zShapeInfo, numOfSpatialDims);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "spaceToBatchNDCuda failed");
|
||||
|
||||
}
|
||||
BUILD_DOUBLE_TEMPLATE( void spaceToBatchNDCudaLauncher,
|
||||
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t* stream, const void* vx, const sd::LongType* xShapeInfo, const void* vy,
|
||||
const sd::LongType* yShapeInfo, void* vz, const sd::LongType* zShapeInfo,
|
||||
const sd::LongType numOfSpatialDims),
|
||||
SD_COMMON_TYPES, SD_INTEGER_TYPES);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void spaceToBatchND(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 LongType rank = input.rankOf();
|
||||
|
||||
const LongType numOfSpatialDims = blockShape.sizeAt(0);
|
||||
|
||||
//*** construct reshaping std::vector for first reshape of output array ***//
|
||||
std::vector<LongType> temp(numOfSpatialDims + rank);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < numOfSpatialDims; ++i) temp[i] = blockShape.e<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 < 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<LongType>(i - 1) : output.sizeAt(i);
|
||||
|
||||
NDArray outputRearranged1 = outputRearranged0.reshape(output.ordering(), temp, false);
|
||||
|
||||
dim3 launchDims = spaceToBatchNdLaunch(output.lengthOf(),output.rankOf());
|
||||
PointersManager manager(context, "spaceToBatchND");
|
||||
|
||||
NDArray::prepareSpecialUse({&outputRearranged1}, {&input, &padding});
|
||||
BUILD_DOUBLE_SELECTOR(input.dataType(), padding.dataType(), spaceToBatchNDCudaLauncher,
|
||||
(launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), input.specialBuffer(),
|
||||
input.specialShapeInfo(), padding.specialBuffer(), padding.specialShapeInfo(),
|
||||
outputRearranged1.specialBuffer(), outputRearranged1.specialShapeInfo(), numOfSpatialDims),
|
||||
SD_COMMON_TYPES, SD_INTEGER_TYPES);
|
||||
NDArray::registerSpecialUse({&outputRearranged1}, {&input, &padding});
|
||||
|
||||
manager.synchronize();
|
||||
|
||||
if (output.specialBuffer() != outputRearranged1.specialBuffer()) outputRearranged0.assign(&outputRearranged1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -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
|
||||
******************************************************************************/
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
#include <ops/declarable/helpers/s_t_d.h>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename T>
|
||||
static SD_KERNEL void spaceToDepthKernel(const void *vx, const LongType *xShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, const int block_size, const bool isNHWC) {
|
||||
auto input_ptr = reinterpret_cast<const T *>(vx);
|
||||
auto output_ptr = reinterpret_cast<T *>(vz);
|
||||
|
||||
const int batch_size = shape::sizeAt(xShapeInfo, 0);
|
||||
const int input_depth = isNHWC ? shape::sizeAt(xShapeInfo, 3) : shape::sizeAt(xShapeInfo, 1);
|
||||
const int input_height = isNHWC ? shape::sizeAt(xShapeInfo, 1) : shape::sizeAt(xShapeInfo, 2);
|
||||
const int input_width = isNHWC ? shape::sizeAt(xShapeInfo, 2) : shape::sizeAt(xShapeInfo, 3);
|
||||
|
||||
const int output_depth = isNHWC ? shape::sizeAt(zShapeInfo, 3) : shape::sizeAt(zShapeInfo, 1);
|
||||
const int output_height = isNHWC ? shape::sizeAt(zShapeInfo, 1) : shape::sizeAt(zShapeInfo, 2);
|
||||
const int output_width = isNHWC ? shape::sizeAt(zShapeInfo, 2) : shape::sizeAt(zShapeInfo, 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;
|
||||
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (isNHWC) {
|
||||
const int total_count = batch_size * input_height * input_width * input_depth;
|
||||
|
||||
for (int inp_idx = tid; inp_idx < total_count; inp_idx += blockDim.x * gridDim.x) {
|
||||
// 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);
|
||||
}
|
||||
} else {
|
||||
const int total_count = batch_size * output_depth_by_output_area;
|
||||
|
||||
for (int inp_idx = tid; inp_idx < total_count; inp_idx += blockDim.x * gridDim.x) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void _spaceTodepth_(LaunchContext *context, NDArray&input, NDArray *output, int block_size,
|
||||
bool isNHWC) {
|
||||
dim3 launchDims = getLaunchDims("space_to_depth");
|
||||
spaceToDepthKernel<T><<<launchDims.y, launchDims.x, launchDims.z, *context->getCudaStream()>>>(input.specialBuffer(), input.specialShapeInfo(),
|
||||
output->specialBuffer(),
|
||||
output->specialShapeInfo(), block_size, isNHWC);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "spaceToDepthKernel failed");
|
||||
|
||||
}
|
||||
|
||||
void _spaceTodepth(LaunchContext *context, NDArray&input, NDArray *output, int block_size, bool isNHWC) {
|
||||
NDArray::prepareSpecialUse({output}, {&input});
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), _spaceTodepth_, (context, input, output, block_size, isNHWC),
|
||||
SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {&input});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,751 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <helpers/ConstantShapeHelper.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/scatter.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
#include "helpers/DebugHelper.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - indices, y - contains number of bad indices, z - input/output
|
||||
template <typename X>
|
||||
SD_KERNEL static void checkIndicesCuda(const void *vx, const LongType *xShapeInfo, LongType *y,
|
||||
const LongType *zShapeInfo, const int axis) {
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
|
||||
__shared__ LongType xRank, xLen, numOfBadIndxPerBlock;
|
||||
__shared__ const LongType *xShape, *xStride, *zShape;
|
||||
__shared__ LongType *coords;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
coords = reinterpret_cast<LongType *>(shmem);
|
||||
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xLen = shape::length(xShapeInfo);
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
|
||||
numOfBadIndxPerBlock = 0;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto xCoords = coords + threadIdx.x * xRank;
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < xLen; i += gridDim.x * blockDim.x) {
|
||||
INDEX2COORDS(i, xRank, xShape, xCoords);
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
|
||||
const LongType currentInd = x[xOffset];
|
||||
|
||||
const LongType limit = shape::sizeAt(zShapeInfo, axis == -1 ? xCoords[xRank - 1] : axis);
|
||||
if (currentInd >= limit) {
|
||||
sd::math::atomics::sd_atomicAdd<LongType>(&numOfBadIndxPerBlock, 1);
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (threadIdx.x == 0 && numOfBadIndxPerBlock != 0) {
|
||||
sd::math::atomics::sd_atomicAdd<LongType>(y, numOfBadIndxPerBlock);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X>
|
||||
static void checkIndicesCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo,
|
||||
LongType *y, const LongType *zShapeInfo, const int axis) {
|
||||
checkIndicesCuda<X><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, y, zShapeInfo, axis);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "checkIndicesCuda failed");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
LongType checkIndices(LaunchContext *context, NDArray&indices, NDArray&output, const int axis) {
|
||||
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
|
||||
const int blocksPerGrid = (indices.lengthOf() + threadsPerBlock - 1) / threadsPerBlock;
|
||||
const int sharedMem = threadsPerBlock * sizeof(LongType) * indices.rankOf() + 256;
|
||||
dim3 scatterDimsIndices = scatterDimsCheckIndices(indices.lengthOf(), indices.rankOf());
|
||||
const auto xType = indices.dataType();
|
||||
|
||||
PointersManager manager(context, "scatterNDcheckIndices");
|
||||
|
||||
// scalar, initial value = 0
|
||||
NDArray numOfBadIndx(INT64, context, true);
|
||||
|
||||
NDArray::prepareSpecialUse({&numOfBadIndx}, {&indices});
|
||||
BUILD_SINGLE_SELECTOR(
|
||||
xType, checkIndicesCudaLauncher,
|
||||
(scatterDimsIndices.y, scatterDimsIndices.x, scatterDimsIndices.z, context->getCudaStream(),
|
||||
indices.specialBuffer(), indices.specialShapeInfo(),
|
||||
reinterpret_cast<sd::LongType *>(numOfBadIndx.specialBuffer()), output.specialShapeInfo(), axis),
|
||||
SD_INTEGER_TYPES);
|
||||
NDArray::registerSpecialUse({&numOfBadIndx}, {&indices});
|
||||
|
||||
manager.synchronize();
|
||||
|
||||
return numOfBadIndx.t<LongType>(0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - indices, y - updates, z - input/output
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void scatterLockCuda(const int opCode, const void *vx, const LongType *xShapeInfo, const void *vy,
|
||||
const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) {
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
const auto y = reinterpret_cast<const Y *>(vy);
|
||||
auto z = reinterpret_cast<Y *>(vz);
|
||||
|
||||
__shared__ LongType xRank, yRank, zRank, xNonUnitDim, yNonUnitDim, zNonUnitDim;
|
||||
__shared__ const LongType *xShape, *yShape, *zShape, *xStride, *yStride, *zStride;
|
||||
__shared__ LongType xLen, zLen;
|
||||
__shared__ bool is1Dcase, xySameStride;
|
||||
__shared__ LongType *coords;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
coords = reinterpret_cast<LongType *>(shmem);
|
||||
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
|
||||
xLen = shape::length(xShapeInfo);
|
||||
zLen = shape::length(zShapeInfo);
|
||||
|
||||
xNonUnitDim = yNonUnitDim = zNonUnitDim = 0;
|
||||
|
||||
is1Dcase = (shape::isCommonVector(zShapeInfo, zNonUnitDim) || shape::isScalar(zShapeInfo)) &&
|
||||
(shape::isCommonVector(yShapeInfo, yNonUnitDim) || shape::isScalar(yShapeInfo)) &&
|
||||
(shape::isCommonVector(xShapeInfo, xNonUnitDim) || shape::isScalar(xShapeInfo));
|
||||
|
||||
if (is1Dcase) xySameStride = xStride[xNonUnitDim] == yStride[yNonUnitDim];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType yOffset, zOffset;
|
||||
LongType zFirstCoord, *yCoords, *zCoords;
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < zLen; i += gridDim.x * blockDim.x) {
|
||||
if (!is1Dcase) {
|
||||
yCoords = coords + threadIdx.x * (yRank + zRank);
|
||||
zCoords = yCoords + yRank;
|
||||
INDEX2COORDS(i, zRank, zShape, zCoords);
|
||||
}
|
||||
|
||||
for (LongType j = 0; j < xLen; ++j) {
|
||||
if (is1Dcase) {
|
||||
yOffset = j * yStride[yNonUnitDim];
|
||||
zFirstCoord = x[xySameStride ? yOffset : j];
|
||||
|
||||
if (i != zFirstCoord) continue;
|
||||
|
||||
zOffset = i * zStride[zNonUnitDim];
|
||||
} else {
|
||||
INDEX2COORDS(j, xRank, xShape, yCoords);
|
||||
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStride, yCoords, xOffset);
|
||||
zFirstCoord = x[xOffset];
|
||||
|
||||
if (zCoords[0] != zFirstCoord) continue;
|
||||
|
||||
for (LongType k = 0; k < yRank - xRank; ++k) yCoords[xRank + k] = zCoords[k + 1];
|
||||
|
||||
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
}
|
||||
|
||||
switch (opCode) {
|
||||
case pairwise::Add:
|
||||
z[zOffset] += y[yOffset];
|
||||
break;
|
||||
case pairwise::Subtract:
|
||||
z[zOffset] -= y[yOffset];
|
||||
break;
|
||||
case pairwise::Multiply:
|
||||
z[zOffset] *= y[yOffset];
|
||||
break;
|
||||
case pairwise::Divide:
|
||||
z[zOffset] /= y[yOffset];
|
||||
break;
|
||||
case pairwise::ReverseSubtract:
|
||||
z[zOffset] = y[yOffset] - z[zOffset];
|
||||
break;
|
||||
case pairwise::ReverseDivide:
|
||||
z[zOffset] = y[yOffset] / z[zOffset];
|
||||
break;
|
||||
case pairwise::CopyPws:
|
||||
z[zOffset] = y[yOffset];
|
||||
break;
|
||||
case pairwise::MaxPairwise:
|
||||
if (z[zOffset] < y[yOffset]) z[zOffset] = y[yOffset];
|
||||
break;
|
||||
case pairwise::MinPairwise:
|
||||
if (z[zOffset] > y[yOffset]) z[zOffset] = y[yOffset];
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - indices, y - updates, z - input/output
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void scatterCuda(const int opCode, const void *vx, const LongType *xShapeInfo, const void *vy,
|
||||
const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) {
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
const auto y = reinterpret_cast<const Y *>(vy);
|
||||
auto z = reinterpret_cast<Y *>(vz);
|
||||
|
||||
__shared__ LongType xRank, yRank, zRank, xNonUnitDim, yNonUnitDim, zNonUnitDim;
|
||||
__shared__ const LongType *xShape, *yShape, *zShape, *xStride, *yStride, *zStride;
|
||||
__shared__ LongType yLen;
|
||||
__shared__ bool is1Dcase, xySameStride;
|
||||
__shared__ LongType *coords;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
coords = reinterpret_cast<LongType *>(shmem);
|
||||
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
|
||||
yLen = shape::length(yShapeInfo);
|
||||
|
||||
xNonUnitDim = yNonUnitDim = zNonUnitDim = 0;
|
||||
|
||||
is1Dcase = (shape::isCommonVector(zShapeInfo, zNonUnitDim) || shape::isScalar(zShapeInfo)) &&
|
||||
(shape::isCommonVector(yShapeInfo, yNonUnitDim) || shape::isScalar(yShapeInfo)) &&
|
||||
(shape::isCommonVector(xShapeInfo, xNonUnitDim) || shape::isScalar(xShapeInfo));
|
||||
|
||||
if (is1Dcase) xySameStride = xStride[xNonUnitDim] == yStride[yNonUnitDim];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType xOffset, yOffset, zOffset;
|
||||
LongType *yCoords, *zCoords;
|
||||
|
||||
if (!is1Dcase) {
|
||||
yCoords = coords + threadIdx.x * (yRank + zRank);
|
||||
zCoords = yCoords + yRank;
|
||||
}
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < yLen; i += gridDim.x * blockDim.x) {
|
||||
if (is1Dcase) {
|
||||
yOffset = i * yStride[yNonUnitDim];
|
||||
zOffset = x[xySameStride ? yOffset : i * xStride[xNonUnitDim]] * zStride[zNonUnitDim];
|
||||
} else {
|
||||
INDEX2COORDS(i, yRank, yShape, yCoords);
|
||||
|
||||
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
|
||||
COORDS2INDEX(xRank, xStride, yCoords, xOffset);
|
||||
|
||||
zCoords[0] = x[xOffset];
|
||||
|
||||
for (LongType j = 0; j < yRank - xRank; ++j) {
|
||||
zCoords[j + 1] = yCoords[xRank + j];
|
||||
}
|
||||
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
}
|
||||
|
||||
switch (opCode) {
|
||||
case pairwise::Add:
|
||||
z[zOffset] += y[yOffset];
|
||||
break;
|
||||
case pairwise::Subtract:
|
||||
z[zOffset] -= y[yOffset];
|
||||
break;
|
||||
case pairwise::Multiply:
|
||||
z[zOffset] *= y[yOffset];
|
||||
break;
|
||||
case pairwise::Divide:
|
||||
z[zOffset] /= y[yOffset];
|
||||
break;
|
||||
case pairwise::ReverseSubtract:
|
||||
z[zOffset] = y[yOffset] - z[zOffset];
|
||||
break;
|
||||
case pairwise::ReverseDivide:
|
||||
z[zOffset] = y[yOffset] / z[zOffset];
|
||||
break;
|
||||
case pairwise::CopyPws:
|
||||
z[zOffset] = y[yOffset];
|
||||
break;
|
||||
case pairwise::MaxPairwise:
|
||||
if (z[zOffset] < y[yOffset]) z[zOffset] = y[yOffset];
|
||||
break;
|
||||
case pairwise::MinPairwise:
|
||||
if (z[zOffset] > y[yOffset]) z[zOffset] = y[yOffset];
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void scatterCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const int opCode, const void *vx,
|
||||
const LongType *xShapeInfo, const void *vy, const LongType *yShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, const bool lock) {
|
||||
if (lock)
|
||||
scatterLockCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(opCode, vx, xShapeInfo, vy,
|
||||
yShapeInfo, vz, zShapeInfo);
|
||||
else
|
||||
scatterCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(opCode, vx, xShapeInfo, vy, yShapeInfo,
|
||||
vz, zShapeInfo);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "scatterLockCuda failed");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void scatter(LaunchContext *context, pairwise::Ops op, NDArray&indices, NDArray&updates, NDArray &output,
|
||||
const bool lock) {
|
||||
const auto xType = indices.dataType();
|
||||
const auto yType = updates.dataType();
|
||||
|
||||
dim3 launchDims = scatterDims(lock ? output.lengthOf() : updates.lengthOf(), updates.rankOf() + output.rankOf());
|
||||
PointersManager manager(context, "scatter");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&updates, &indices});
|
||||
BUILD_DOUBLE_SELECTOR(xType, yType, scatterCudaLauncher,
|
||||
(launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), op,
|
||||
indices.specialBuffer(), indices.specialShapeInfo(), updates.specialBuffer(),
|
||||
updates.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), lock),
|
||||
SD_INDEXING_TYPES, SD_GENERIC_NUMERIC_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&updates, &indices});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - indices, y - updates, z - output
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void scatterNDLockCuda(const int opCode, const void *vx, const LongType *xShapeInfo, const void *vy,
|
||||
const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) {
|
||||
const auto x = reinterpret_cast<const X *>(vx);
|
||||
const auto y = reinterpret_cast<const Y *>(vy);
|
||||
auto z = reinterpret_cast<Y *>(vz);
|
||||
|
||||
__shared__ LongType xRank, yRank, zRank, biggerXYRank, xLastDim, xNonUnitDim, yNonUnitDim, zNonUnitDim;
|
||||
__shared__ const LongType *xShape, *yShape, *zShape, *xStride, *yStride, *zStride;
|
||||
__shared__ LongType zLen, len;
|
||||
__shared__ bool is1Dcase;
|
||||
__shared__ LongType *coords;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
extern __shared__ unsigned char shmem[];
|
||||
coords = reinterpret_cast<LongType *>(shmem);
|
||||
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
xLastDim = shape::sizeAt(xShapeInfo, -1);
|
||||
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
zShape = shape::shapeOf(zShapeInfo);
|
||||
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
zStride = shape::stride(zShapeInfo);
|
||||
|
||||
biggerXYRank = xRank > yRank ? xRank : yRank;
|
||||
|
||||
xNonUnitDim = yNonUnitDim = zNonUnitDim = 0;
|
||||
|
||||
is1Dcase = (shape::isCommonVector(zShapeInfo, zNonUnitDim) || shape::isScalar(zShapeInfo)) &&
|
||||
(shape::isCommonVector(yShapeInfo, yNonUnitDim) || shape::isScalar(yShapeInfo)) &&
|
||||
(shape::isCommonVector(xShapeInfo, xNonUnitDim) || shape::isScalar(xShapeInfo));
|
||||
|
||||
len = is1Dcase ? shape::length(xShapeInfo) : shape::length(xShapeInfo) / xLastDim;
|
||||
zLen = shape::length(zShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType yOffset, zOffset, xOffset;
|
||||
LongType *yCoords, *zCoords;
|
||||
|
||||
if (!is1Dcase) {
|
||||
yCoords = coords + threadIdx.x * (biggerXYRank + zRank);
|
||||
zCoords = yCoords + biggerXYRank;
|
||||
}
|
||||
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < zLen; i += gridDim.x * blockDim.x) {
|
||||
if (!is1Dcase) INDEX2COORDS(i, zRank, zShape, zCoords);
|
||||
|
||||
for (LongType j = 0; j < len; j++) {
|
||||
if (is1Dcase) {
|
||||
if (x[j * xStride[xNonUnitDim]] != i) continue;
|
||||
|
||||
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
} else {
|
||||
INDEX2COORDS(j, xRank - 1, xShape, yCoords);
|
||||
|
||||
yCoords[xRank - 1] = 0;
|
||||
COORDS2INDEX(xRank, xStride, yCoords, xOffset);
|
||||
if (zCoords[0] != x[xOffset]) continue;
|
||||
|
||||
bool matched = true;
|
||||
for (LongType k = 1; k < xLastDim; k++) {
|
||||
yCoords[xRank - 1] = k;
|
||||
COORDS2INDEX(xRank, xStride, yCoords, xOffset);
|
||||
if (zCoords[k] != x[xOffset]) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) continue;
|
||||
|
||||
for (LongType k = xLastDim; k < zRank; ++k) yCoords[yRank - zRank + k] = zCoords[k];
|
||||
|
||||
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
|
||||
COORDS2INDEX(zRank, zStride, zCoords, zOffset);
|
||||
}
|
||||
|
||||
switch (opCode) {
|
||||
case pairwise::Add:
|
||||
z[zOffset] += y[yOffset];
|
||||
break;
|
||||
case pairwise::Subtract:
|
||||
z[zOffset] -= y[yOffset];
|
||||
break;
|
||||
case pairwise::Multiply:
|
||||
z[zOffset] *= y[yOffset];
|
||||
break;
|
||||
case pairwise::Divide:
|
||||
z[zOffset] /= y[yOffset];
|
||||
break;
|
||||
case pairwise::ReverseSubtract:
|
||||
z[zOffset] = y[yOffset] - z[zOffset];
|
||||
break;
|
||||
case pairwise::ReverseDivide:
|
||||
z[zOffset] = y[yOffset] / z[zOffset];
|
||||
break;
|
||||
case pairwise::CopyPws:
|
||||
z[zOffset] = y[yOffset];
|
||||
break;
|
||||
case pairwise::MaxPairwise:
|
||||
if (z[zOffset] < y[yOffset]) z[zOffset] = y[yOffset];
|
||||
break;
|
||||
case pairwise::MinPairwise:
|
||||
if (z[zOffset] > y[yOffset]) z[zOffset] = y[yOffset];
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// x - indices, y - updates, z - output
|
||||
template <typename X, typename Y>
|
||||
SD_KERNEL static void scatterNDCuda(const int opCode, const void* vx, const LongType* xShapeInfo, const void* vy,
|
||||
const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo) {
|
||||
// Cast input and output pointers
|
||||
const auto x = reinterpret_cast<const X*>(vx);
|
||||
const auto y = reinterpret_cast<const Y*>(vy);
|
||||
auto z = reinterpret_cast<Y*>(vz);
|
||||
|
||||
// Shared memory for shape information and flags
|
||||
__shared__ LongType xRank, yRank, zRank, biggerXYRank, xLastDim, xNonUnitDim, yNonUnitDim, zNonUnitDim, yLen;
|
||||
__shared__ bool is1Dcase;
|
||||
|
||||
// Shared memory for coordinates
|
||||
__shared__ LongType* coords;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Dynamically allocated shared memory
|
||||
extern __shared__ unsigned char shmem[];
|
||||
coords = reinterpret_cast<LongType*>(shmem);
|
||||
|
||||
// Initialize shared values
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
zRank = shape::rank(zShapeInfo);
|
||||
xLastDim = shape::sizeAt(xShapeInfo, -1);
|
||||
yLen = shape::length(yShapeInfo);
|
||||
|
||||
biggerXYRank = max(xRank, yRank);
|
||||
|
||||
xNonUnitDim = yNonUnitDim = zNonUnitDim = 0;
|
||||
|
||||
// Check if the operation involves 1D cases
|
||||
is1Dcase = (shape::isCommonVector(zShapeInfo, zNonUnitDim) || shape::isScalar(zShapeInfo)) &&
|
||||
(shape::isCommonVector(yShapeInfo, yNonUnitDim) || shape::isScalar(yShapeInfo)) &&
|
||||
(shape::isCommonVector(xShapeInfo, xNonUnitDim) || shape::isScalar(xShapeInfo));
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Dynamically allocated memory for local coordinates
|
||||
LongType* yCoords = coords + threadIdx.x * (biggerXYRank + zRank);
|
||||
LongType* zCoords = yCoords + biggerXYRank;
|
||||
|
||||
// Process each element in y
|
||||
for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < yLen; i += gridDim.x * blockDim.x) {
|
||||
LongType yOffset, zOffset;
|
||||
|
||||
// Convert linear index to multi-dimensional coordinates for y
|
||||
INDEX2COORDS(i, yRank, shape::shapeOf(yShapeInfo), yCoords);
|
||||
COORDS2INDEX(yRank, shape::stride(yShapeInfo), yCoords, yOffset);
|
||||
|
||||
// Save the last coordinate of y if needed
|
||||
if (yRank >= xRank) {
|
||||
zCoords[xLastDim] = yCoords[xRank - 1];
|
||||
}
|
||||
|
||||
// Map y coordinates to x and z coordinates
|
||||
for (LongType j = 0; j < xLastDim; ++j) {
|
||||
yCoords[xRank - 1] = j;
|
||||
COORDS2INDEX(xRank, shape::stride(xShapeInfo), yCoords, zCoords[j]);
|
||||
}
|
||||
|
||||
// Adjust remaining coordinates for z
|
||||
for (LongType j = xLastDim + 1; j < zRank; ++j) {
|
||||
zCoords[j] = yCoords[yRank - zRank + j];
|
||||
}
|
||||
|
||||
// Compute linear index for z
|
||||
COORDS2INDEX(zRank, shape::stride(zShapeInfo), zCoords, zOffset);
|
||||
|
||||
// Perform the operation based on opCode
|
||||
switch (opCode) {
|
||||
case pairwise::Add:
|
||||
z[zOffset] += y[yOffset];
|
||||
break;
|
||||
case pairwise::Subtract:
|
||||
z[zOffset] -= y[yOffset];
|
||||
break;
|
||||
case pairwise::Multiply:
|
||||
z[zOffset] *= y[yOffset];
|
||||
break;
|
||||
case pairwise::Divide:
|
||||
z[zOffset] /= y[yOffset];
|
||||
break;
|
||||
case pairwise::ReverseSubtract:
|
||||
z[zOffset] = y[yOffset] - z[zOffset];
|
||||
break;
|
||||
case pairwise::ReverseDivide:
|
||||
z[zOffset] = y[yOffset] / z[zOffset];
|
||||
break;
|
||||
case pairwise::CopyPws:
|
||||
z[zOffset] = y[yOffset];
|
||||
break;
|
||||
case pairwise::MaxPairwise:
|
||||
z[zOffset] = max(z[zOffset], y[yOffset]);
|
||||
break;
|
||||
case pairwise::MinPairwise:
|
||||
z[zOffset] = min(z[zOffset], y[yOffset]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Y>
|
||||
static void scatterNDCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const int opCode, const void *vx,
|
||||
const LongType *xShapeInfo, const void *vy, const LongType *yShapeInfo, void *vz,
|
||||
const LongType *zShapeInfo, const bool lock) {
|
||||
if (lock)
|
||||
scatterNDLockCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(opCode, vx, xShapeInfo, vy,
|
||||
yShapeInfo, vz, zShapeInfo);
|
||||
else
|
||||
scatterNDCuda<X, Y><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(opCode, vx, xShapeInfo, vy, yShapeInfo,
|
||||
vz, zShapeInfo);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "scatterNDCuda failed");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void scatterND(LaunchContext *context, pairwise::Ops op, NDArray&indices, NDArray&updates,
|
||||
NDArray &output, const bool lock) {
|
||||
const int xRank = indices.rankOf();
|
||||
const int yRank = updates.rankOf();
|
||||
const int zRank = output.rankOf();
|
||||
|
||||
dim3 launchDims =
|
||||
scatterNdDims(lock ? output.lengthOf() : updates.lengthOf(), ((yRank > xRank ? yRank : xRank) + zRank));
|
||||
const auto xType = indices.dataType();
|
||||
const auto yType = updates.dataType();
|
||||
|
||||
PointersManager manager(context, "scatterND");
|
||||
|
||||
NDArray::prepareSpecialUse({&output}, {&updates, &indices});
|
||||
BUILD_DOUBLE_SELECTOR(xType, yType, scatterNDCudaLauncher,
|
||||
(launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), op,
|
||||
indices.specialBuffer(), indices.specialShapeInfo(), updates.specialBuffer(),
|
||||
updates.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), lock),
|
||||
SD_INDEXING_TYPES, SD_GENERIC_NUMERIC_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&updates, &indices});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
SD_KERNEL void scatterForLossCuda(const void* vx, const LongType* xShapeInfo, void* vy, const LongType* yShapeInfo,
|
||||
void* vz, const LongType* zShapeInfo) {
|
||||
// Cast input and output pointers
|
||||
const auto x = reinterpret_cast<const X*>(vx);
|
||||
auto y = reinterpret_cast<Z*>(vy);
|
||||
auto z = reinterpret_cast<Z*>(vz);
|
||||
|
||||
// Shared memory for shape information and coordinates
|
||||
__shared__ LongType xLen;
|
||||
__shared__ LongType xRank;
|
||||
__shared__ const LongType* xShape;
|
||||
__shared__ const LongType* xStride;
|
||||
__shared__ const LongType* yStride;
|
||||
__shared__ const LongType* zStride;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Initialize shared memory variables
|
||||
xLen = shape::length(xShapeInfo);
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
zStride = zShapeInfo ? shape::stride(zShapeInfo) : nullptr;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Calculate global thread index
|
||||
const LongType xInd = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
// Return if the thread index exceeds the length of x
|
||||
if (xInd >= xLen) return;
|
||||
|
||||
// Dynamically allocated shared memory for coordinates
|
||||
extern __shared__ unsigned char shmem[];
|
||||
auto coords = reinterpret_cast<LongType*>(shmem) + threadIdx.x * (xRank + 1);
|
||||
|
||||
// Convert linear index to coordinates for x
|
||||
INDEX2COORDS(xInd, xRank, xShape, coords);
|
||||
|
||||
// Calculate offset for x
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(xRank, xStride, coords, xOffset);
|
||||
|
||||
// Update the last coordinate with the value from x
|
||||
coords[xRank] = x[xOffset];
|
||||
|
||||
// Calculate offset for y
|
||||
LongType yOffset;
|
||||
COORDS2INDEX(xRank + 1, yStride, coords, yOffset);
|
||||
|
||||
if (z == nullptr) {
|
||||
// Gradient calculation
|
||||
y[yOffset] -= 1.f;
|
||||
} else {
|
||||
// Calculate offset for z
|
||||
LongType zOffset;
|
||||
COORDS2INDEX(xRank + 1, zStride, coords, zOffset);
|
||||
|
||||
// Update z with the value from y
|
||||
z[zOffset] = y[yOffset];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename X, typename Z>
|
||||
static void scatterForLossCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
|
||||
const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo, void *vy,
|
||||
const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) {
|
||||
scatterForLossCuda<X, Z>
|
||||
<<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "scatterUpdateCuda failed");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
void scatterForLoss(LaunchContext *context, NDArray&indices, NDArray &updates, NDArray &output,
|
||||
const bool calcGrad) {
|
||||
// shapes of indices and output must be the same
|
||||
// shape of indices should be the same as updates shape with last dimension excluded, for example if updates is
|
||||
// {a,b,c} then indices should be {a,b}
|
||||
|
||||
PointersManager manager(context, "scatterForLoss");
|
||||
|
||||
dim3 launchDIms = scatterDims(indices.lengthOf(), updates.rankOf());
|
||||
if (calcGrad) {
|
||||
NDArray::prepareSpecialUse({&updates}, {&indices});
|
||||
BUILD_DOUBLE_SELECTOR(
|
||||
indices.dataType(), updates.dataType(), scatterForLossCudaLauncher,
|
||||
(launchDIms.y, launchDIms.x, launchDIms.z, context->getCudaStream(), indices.specialBuffer(),
|
||||
indices.specialShapeInfo(), updates.specialBuffer(), updates.specialShapeInfo(), nullptr, nullptr),
|
||||
SD_INDEXING_TYPES, SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&updates}, {&indices});
|
||||
} else {
|
||||
NDArray::prepareSpecialUse({&output}, {&indices, &updates});
|
||||
BUILD_DOUBLE_SELECTOR(indices.dataType(), updates.dataType(), scatterForLossCudaLauncher,
|
||||
(launchDIms.y, launchDIms.x, launchDIms.z, context->getCudaStream(), indices.specialBuffer(),
|
||||
indices.specialShapeInfo(), updates.specialBuffer(), updates.specialShapeInfo(),
|
||||
output.specialBuffer(), output.specialShapeInfo()),
|
||||
SD_INDEXING_TYPES, SD_FLOAT_TYPES);
|
||||
NDArray::registerSpecialUse({&output}, {&indices, &updates});
|
||||
}
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,143 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
template <typename X, typename Y>
|
||||
static SD_KERNEL void scatterSimpleKernel(void* vx, const LongType* xTadShape, const LongType* xTadOffsets,
|
||||
LongType xLength, LongType numTads, const void* vi,
|
||||
const LongType* iShapeInfo, LongType iLength, const void* vu,
|
||||
const LongType* uShapeInfo, LongType uLength) {
|
||||
// Shared memory caching for shape and stride information
|
||||
__shared__ LongType iRank, xTadRank, uRank;
|
||||
__shared__ const LongType* iShape;
|
||||
__shared__ const LongType* xTadShapePtr;
|
||||
__shared__ const LongType* uShape;
|
||||
__shared__ const LongType* iStride;
|
||||
__shared__ const LongType* xTadStride;
|
||||
__shared__ const LongType* uStride;
|
||||
|
||||
// Initialize shared memory
|
||||
if (threadIdx.x == 0) {
|
||||
iRank = shape::rank(iShapeInfo);
|
||||
xTadRank = shape::rank(xTadShape);
|
||||
uRank = shape::rank(uShapeInfo);
|
||||
|
||||
iShape = shape::shapeOf(iShapeInfo);
|
||||
xTadShapePtr = shape::shapeOf(xTadShape);
|
||||
uShape = shape::shapeOf(uShapeInfo);
|
||||
|
||||
iStride = shape::stride(iShapeInfo);
|
||||
xTadStride = shape::stride(xTadShape);
|
||||
uStride = shape::stride(uShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Cast input pointers
|
||||
const X* u = reinterpret_cast<const X*>(vu);
|
||||
const Y* indices = reinterpret_cast<const Y*>(vi);
|
||||
|
||||
// Calculate thread ID
|
||||
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
// Iterate over the indices
|
||||
for (int i = tid; i < iLength; i += blockDim.x * gridDim.x) {
|
||||
// Offset for `x`
|
||||
auto x = reinterpret_cast<X*>(vx) + xTadOffsets[i];
|
||||
|
||||
// Compute coordinates and offsets for index tensor
|
||||
LongType idxCoords[SD_MAX_RANK];
|
||||
LongType idxOffset;
|
||||
INDEX2COORDS(i, iRank, iShape, idxCoords);
|
||||
COORDS2INDEX(iRank, iStride, idxCoords, idxOffset);
|
||||
auto idx = indices[idxOffset];
|
||||
|
||||
// Compute coordinates and offsets for x
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
INDEX2COORDS(idx, xTadRank, xTadShapePtr, xCoords);
|
||||
COORDS2INDEX(xTadRank, xTadStride, xCoords, xOffset);
|
||||
|
||||
// Compute coordinates and offsets for u
|
||||
LongType uCoords[SD_MAX_RANK];
|
||||
LongType uOffset;
|
||||
INDEX2COORDS(i, uRank, uShape, uCoords);
|
||||
COORDS2INDEX(uRank, uStride, uCoords, uOffset);
|
||||
|
||||
// Perform the scatter update
|
||||
x[xOffset] = u[uOffset];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename X, typename Y>
|
||||
void scatterSimple_(LaunchContext* context, const int opId, NDArray& input, NDArray& updates,
|
||||
NDArray& indices, const std::vector<LongType>& dimensions) {
|
||||
auto dims = ShapeUtils::evalDimsToExclude(input.rankOf(),dimensions.size(),dimensions.data());
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), dims);
|
||||
|
||||
auto xLength = shape::length(packX->primaryShapeInfo());
|
||||
auto iLength = indices.lengthOf();
|
||||
auto uLength = updates.lengthOf();
|
||||
|
||||
dim3 launchDims = getLaunchDims("scatter_simple");
|
||||
scatterSimpleKernel<X, Y><<<launchDims.y, launchDims.x, launchDims.z, *context->getCudaStream()>>>(
|
||||
input.specialBuffer(), packX->platformShapeInfo(), packX->platformOffsets(), xLength, packX->numberOfTads(),
|
||||
indices.specialBuffer(), indices.specialShapeInfo(), iLength, updates.specialBuffer(), updates.specialShapeInfo(),
|
||||
uLength);
|
||||
sd::DebugHelper::checkErrorCode(context->getCudaStream(), "scatterUpdateCuda failed");
|
||||
|
||||
|
||||
}
|
||||
|
||||
void scatterSimple(LaunchContext* context, const int opId, NDArray& input, NDArray& updates,
|
||||
NDArray& indices, const std::vector<LongType>& dimensions) {
|
||||
auto xType = input.dataType();
|
||||
auto yType = indices.dataType();
|
||||
|
||||
if (opId != 6) THROW_EXCEPTION("scatterSimple: only copy op is supported");
|
||||
|
||||
NDArray::prepareSpecialUse({&input}, {&updates, &indices});
|
||||
|
||||
BUILD_DOUBLE_SELECTOR(xType, yType, scatterSimple_, (context, opId, input, updates, indices, dimensions),
|
||||
SD_COMMON_TYPES, SD_INDEXING_TYPES);
|
||||
|
||||
NDArray::registerSpecialUse({&input}, {&updates, &indices});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,169 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <array/NDArrayFactory.h>
|
||||
#include <array/ResultSet.h>
|
||||
#include <exceptions/cuda_exception.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/transforms.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "execution/cuda/LaunchDims.h"
|
||||
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
SD_KERNEL static void scatterUpdateCuda(const int opCode, const int numOfInd, void* vx, const LongType* xShapeInfo,
|
||||
const LongType* xOffsets, void* vy, const LongType* yShapeInfo,
|
||||
const LongType* yOffsets, const LongType* indexes) {
|
||||
// Shared memory caching for shape and pointers
|
||||
__shared__ T *x, *y;
|
||||
__shared__ LongType arrLenX, arrLenY;
|
||||
__shared__ LongType xRank, yRank;
|
||||
__shared__ const LongType* xShape;
|
||||
__shared__ const LongType* yShape;
|
||||
__shared__ const LongType* xStride;
|
||||
__shared__ const LongType* yStride;
|
||||
|
||||
// Initialize shared variables
|
||||
if (threadIdx.x == 0) {
|
||||
xRank = shape::rank(xShapeInfo);
|
||||
yRank = shape::rank(yShapeInfo);
|
||||
xShape = shape::shapeOf(xShapeInfo);
|
||||
yShape = shape::shapeOf(yShapeInfo);
|
||||
xStride = shape::stride(xShapeInfo);
|
||||
yStride = shape::stride(yShapeInfo);
|
||||
arrLenX = shape::length(xShapeInfo);
|
||||
arrLenY = shape::length(yShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Iterate through the number of indices
|
||||
for (int e = 0; e < numOfInd; e++) {
|
||||
const auto xIndex = indexes[e];
|
||||
const bool isOwner = xIndex < gridDim.x ? blockIdx.x == xIndex : blockIdx.x == xIndex % gridDim.x;
|
||||
|
||||
if (!isOwner) continue;
|
||||
|
||||
// Initialize x and y pointers
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<T*>(vx) + xOffsets[xIndex];
|
||||
y = reinterpret_cast<T*>(vy) + yOffsets[e];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Validate array lengths
|
||||
if (arrLenX != arrLenY) return;
|
||||
|
||||
// Process the elements
|
||||
for (LongType i = threadIdx.x; i < arrLenX; i += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType xOffset, yOffset;
|
||||
|
||||
// Compute coordinates and offsets for x and y
|
||||
INDEX2COORDS(i, xRank, xShape, xCoords);
|
||||
COORDS2INDEX(xRank, xStride, xCoords, xOffset);
|
||||
INDEX2COORDS(i, yRank, yShape, yCoords);
|
||||
COORDS2INDEX(yRank, yStride, yCoords, yOffset);
|
||||
|
||||
// Perform the specified operation
|
||||
switch (opCode) {
|
||||
case 0:
|
||||
x[xOffset] += y[yOffset];
|
||||
break;
|
||||
case 1:
|
||||
x[xOffset] -= y[yOffset];
|
||||
break;
|
||||
case 2:
|
||||
x[xOffset] *= y[yOffset];
|
||||
break;
|
||||
case 3:
|
||||
x[xOffset] /= y[yOffset];
|
||||
break;
|
||||
case 4:
|
||||
x[xOffset] = y[yOffset] - x[xOffset];
|
||||
break;
|
||||
case 5:
|
||||
x[xOffset] = y[yOffset] / x[xOffset];
|
||||
break;
|
||||
case 6:
|
||||
x[xOffset] = y[yOffset];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
SD_HOST static void scatterUpdateCudaLauncher(const cudaStream_t* stream, const int opCode, const int numOfInd,
|
||||
void* vx, const LongType* xShapeInfo, const LongType* xOffsets,
|
||||
void* vy, const LongType* yShapeInfo, const LongType* yOffsets,
|
||||
const LongType* indexes) {
|
||||
dim3 launchDims = getLaunchDims("scatter_update");
|
||||
scatterUpdateCuda<T><<<launchDims.y, launchDims.x, SD_MAX_NUM_THREADS, *stream>>>(opCode, numOfInd, vx, xShapeInfo, xOffsets, vy,
|
||||
yShapeInfo, yOffsets, indexes);
|
||||
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "scatterUpdateCuda failed");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void scatterUpdate(LaunchContext* context, NDArray& input, NDArray& updates, const std::vector<LongType>* intArgs) {
|
||||
const int opCode = (*intArgs)[0];
|
||||
const int numOfDims = (*intArgs)[1];
|
||||
const int numOfInd = (*intArgs)[2 + numOfDims];
|
||||
|
||||
std::vector<LongType> tadDimensions(numOfDims);
|
||||
for (int e = 2; e < 2 + numOfDims; e++) tadDimensions[e - 2] = (*intArgs)[e];
|
||||
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input.shapeInfo(), &tadDimensions);
|
||||
auto packY = ConstantTadHelper::getInstance().tadForDimensions(updates.shapeInfo(), &tadDimensions);
|
||||
|
||||
std::vector<LongType> shape = {numOfInd};
|
||||
NDArray indices(const_cast<LongType*>(intArgs->data()) + numOfDims + 3, 'c', shape, INT32, context);
|
||||
|
||||
PointersManager manager(context, "scatterUpdate");
|
||||
|
||||
NDArray::prepareSpecialUse({&input}, {&input, &updates, &indices});
|
||||
BUILD_SINGLE_SELECTOR(input.dataType(), scatterUpdateCudaLauncher,
|
||||
(context->getCudaStream(), opCode, numOfInd, input.specialBuffer(), packX->platformShapeInfo(),
|
||||
packX->platformOffsets(), updates.specialBuffer(), packY->platformShapeInfo(),
|
||||
packY->platformOffsets(), reinterpret_cast<sd::LongType *>(indices.specialBuffer())),
|
||||
SD_COMMON_TYPES);
|
||||
NDArray::registerSpecialUse({&input}, {&input, &updates, &indices});
|
||||
|
||||
manager.synchronize();
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,154 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/segment.h>
|
||||
#include <ops/declarable/helpers/segment_common.h>
|
||||
#include <system/selective_rendering.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Sorted segments ops implementations
|
||||
|
||||
template <typename T, typename I>
|
||||
static bool segmentIndicesValidate_(NDArray* indices, NDArray& aexpected, NDArray& aoutput) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool segmentIndicesValidate(LaunchContext* context, NDArray* indices, NDArray& expected, NDArray& output) {
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output.dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output.dataType(), indices->dataType(), return segmentIndicesValidate_,
|
||||
(indices, expected, output), SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Unsorted segment ops functors implementation
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename I>
|
||||
static SD_KERNEL void unsortedSegmentIndexValidateKernel(const I* indices, const LongType* indicesShape, I expected,
|
||||
I* found) {
|
||||
__shared__ bool onlyTrue;
|
||||
__shared__ LongType len;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
onlyTrue = true;
|
||||
len = shape::length(indicesShape);
|
||||
}
|
||||
__syncthreads();
|
||||
auto start = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
auto step = gridDim.x * blockDim.x;
|
||||
for (LongType e = start; e < len && onlyTrue; e += step) {
|
||||
math::atomics::sd_atomicMax(found, indices[e]);
|
||||
if (expected < *found) onlyTrue = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
static bool unsortedSegmentIndicesValidate_(LaunchContext* context, NDArray* indices, LongType expected,
|
||||
LongType& output) {
|
||||
output = expected;
|
||||
I found = output;
|
||||
I exp = expected;
|
||||
auto stream = context->getCudaStream();
|
||||
I* devFound;
|
||||
cudaMalloc(&devFound, sizeof(I));
|
||||
cudaMemcpy(devFound, &found, sizeof(I), cudaMemcpyHostToDevice);
|
||||
|
||||
dim3 launchDims = segmentValidateIndices(indices->lengthOf());
|
||||
unsortedSegmentIndexValidateKernel<I><<<launchDims.y,launchDims.x, launchDims.z, *stream>>>(
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), indices->specialShapeInfo(), exp, devFound);
|
||||
sd::DebugHelper::checkErrorCode(stream, "unsortedSegmentIndexValidateKernel failed");
|
||||
|
||||
cudaMemcpy(&found, devFound, sizeof(I), cudaMemcpyDeviceToHost);
|
||||
cudaFree(devFound);
|
||||
output = found;
|
||||
return expected == output;
|
||||
}
|
||||
|
||||
bool unsortedSegmentIndicesValidate(LaunchContext* context, NDArray* indices, LongType expected, LongType& output) {
|
||||
BUILD_SINGLE_SELECTOR(indices->dataType(), return unsortedSegmentIndicesValidate_,
|
||||
(context, indices, expected, output), SD_INDEXING_TYPES);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// fill up segments starts and ends - splitted ordered case
|
||||
template <typename I>
|
||||
static SD_KERNEL void fillUpSegmentsKernel(const void* indices, const LongType* indexShape, LongType numClasses,
|
||||
LongType* classesRangesStart, LongType* classesRangesLengths) {
|
||||
__shared__ const I* idxBuf;
|
||||
__shared__ LongType idxLen;
|
||||
__shared__ LongType* result;
|
||||
if (threadIdx.x == 0) {
|
||||
idxBuf = reinterpret_cast<const I*>(indices);
|
||||
idxLen = shape::length(indexShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto tid = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
for (auto j = tid; j < idxLen; j += step) {
|
||||
auto pos = idxBuf[j];
|
||||
math::atomics::sd_atomicMin<LongType>(&classesRangesStart[pos], (LongType)j);
|
||||
math::atomics::sd_atomicAdd<LongType>(&classesRangesLengths[pos], 1);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename I>
|
||||
static void fillUpSegments_(NDArray* indices, LongType numClasses, NDArray& classesRangesBegs,
|
||||
NDArray& classesRangesLens) {
|
||||
dim3 dims = getFillUpSegmentsDims(numClasses, indices->lengthOf());
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
auto stream = classesRangesBegs.getContext()->getCudaStream();
|
||||
fillUpSegmentsKernel<I><<<dims.x, dims.y, dims.z, *stream>>>(indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
numClasses, begins, lengths);
|
||||
sd::DebugHelper::checkErrorCode(stream, "fillUpSegmentsKernel failed");
|
||||
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
void fillUpSegments(NDArray* indices, LongType numClasses, NDArray& classesRangesBegs, NDArray& classesRangesLens) {
|
||||
BUILD_SINGLE_SELECTOR(indices->dataType(), fillUpSegments_,
|
||||
(indices, numClasses, classesRangesBegs, classesRangesLens), SD_INDEXING_TYPES);
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
@@ -0,0 +1,677 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/segment.h>
|
||||
#include <ops/declarable/helpers/segment_common.h>
|
||||
#include <system/selective_rendering.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Segment ops linear kernels
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMaxLinearKernel(void* input, LongType const* inputShape, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* output,
|
||||
LongType const* outputShape) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType xLen, zLen, zIndex;
|
||||
__shared__ T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType threadsPerSegment, start, finish;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
|
||||
auto segment = blockIdx.x;
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
extern __shared__ unsigned char shmem[];
|
||||
val = reinterpret_cast<T*>(shmem);
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
// Cache shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
if (segment < numOfClasses) {
|
||||
LongType segmentCoords[] = {segment};
|
||||
COORDS2INDEX(1, outputStridePtr, segmentCoords, zIndex);
|
||||
start = starts[segment];
|
||||
finish = start + lengths[segment];
|
||||
LongType startCoords[] = {start};
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(1, inputStridePtr, startCoords, xOffset);
|
||||
z[zIndex] = x[xOffset];
|
||||
val[segment] = z[zIndex];
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto e = start + threadIdx.x + 1; e < finish; e += blockDim.x) {
|
||||
LongType eCoords[] = {e};
|
||||
LongType xIndex;
|
||||
COORDS2INDEX(1, inputStridePtr, eCoords, xIndex);
|
||||
math::atomics::sd_atomicMax<T>(&z[zIndex], x[xIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void unsortedSegmentMaxLinearKernel(void* input, LongType const* inputShape, void* indices,
|
||||
LongType const* indicesShape, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* output,
|
||||
LongType const* outputShape) {
|
||||
__shared__ LongType xLen, zLen, zIndex;
|
||||
__shared__ T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ I* y;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank, indicesRank;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
|
||||
auto segment = blockIdx.x;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
y = reinterpret_cast<I*>(indices);
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
// Cache shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
|
||||
LongType segmentCoords[] = {segment};
|
||||
COORDS2INDEX(1, outputStridePtr, segmentCoords, zIndex);
|
||||
if (lengths[segment] > 0) {
|
||||
LongType startCoords[] = {starts[segment]};
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(1, inputStridePtr, startCoords, xOffset);
|
||||
z[zIndex] = x[xOffset];
|
||||
} else {
|
||||
z[zIndex] = -DataTypeUtils::max<T>();
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (lengths[segment] > 0) {
|
||||
for (auto e = threadIdx.x + 1; e < xLen; e += blockDim.x) {
|
||||
LongType eCoords[] = {e};
|
||||
LongType xIndex, yIndex;
|
||||
COORDS2INDEX(1, inputStridePtr, eCoords, xIndex);
|
||||
COORDS2INDEX(1, indicesStridePtr, eCoords, yIndex);
|
||||
if (y[yIndex] == segment) {
|
||||
math::atomics::sd_atomicMax<T>(&z[zIndex], x[xIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMaxTadKernel(void* inputBuf, LongType const* inputShape, LongType const* inputTads,
|
||||
LongType const* inputTadOffsets, I* indices, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* outputBuf,
|
||||
LongType const* outputShape, LongType const* outputTads,
|
||||
LongType const* outputTadOffsets, T filler, LongType indicesLength,
|
||||
LongType numInputTads, LongType numOutputTads) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType len, zIndex, total, zLen;
|
||||
__shared__ T* z;
|
||||
__shared__ int start, finish;
|
||||
__shared__ I segment;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputTadRank, outputTadRank;
|
||||
__shared__ const sd::LongType* inputTadShapePtr;
|
||||
__shared__ const sd::LongType* outputTadShapePtr;
|
||||
__shared__ const sd::LongType* inputTadStridePtr;
|
||||
__shared__ const sd::LongType* outputTadStridePtr;
|
||||
|
||||
if (threadIdx.x == 0 && blockIdx.x < indicesLength) {
|
||||
segment = indices[blockIdx.x];
|
||||
zLen = shape::length(outputShape);
|
||||
auto zOffset = outputTadOffsets[segment];
|
||||
z = reinterpret_cast<T*>(outputBuf) + outputTadOffsets[segment];
|
||||
len = shape::length(inputTads);
|
||||
|
||||
// Cache shape information
|
||||
inputTadRank = shape::rank(inputTads);
|
||||
outputTadRank = shape::rank(outputTads);
|
||||
inputTadShapePtr = shape::shapeOf(inputTads);
|
||||
outputTadShapePtr = shape::shapeOf(outputTads);
|
||||
inputTadStridePtr = shape::stride(inputTads);
|
||||
outputTadStridePtr = shape::stride(outputTads);
|
||||
|
||||
start = starts[segment];
|
||||
finish = start + lengths[segment];
|
||||
total = shape::sizeAt(inputShape, 0);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto idx = blockIdx.x;
|
||||
if (idx < numInputTads) {
|
||||
auto x = reinterpret_cast<T*>(inputBuf) + inputTadOffsets[idx];
|
||||
if (blockIdx.x == start) {
|
||||
for (auto e = threadIdx.x; e < len; e += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xIndex;
|
||||
LongType zIndex;
|
||||
|
||||
INDEX2COORDS(e, inputTadRank, inputTadShapePtr, xCoords);
|
||||
COORDS2INDEX(inputTadRank, inputTadStridePtr, xCoords, xIndex);
|
||||
INDEX2COORDS(e, outputTadRank, outputTadShapePtr, zCoords);
|
||||
COORDS2INDEX(outputTadRank, outputTadStridePtr, zCoords, zIndex);
|
||||
|
||||
math::atomics::sd_atomicMax<T>(&z[zIndex], x[xIndex]);
|
||||
}
|
||||
} else {
|
||||
for (auto e = threadIdx.x; e < len; e += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xIndex;
|
||||
LongType zIndex;
|
||||
|
||||
INDEX2COORDS(e, inputTadRank, inputTadShapePtr, xCoords);
|
||||
COORDS2INDEX(inputTadRank, inputTadStridePtr, xCoords, xIndex);
|
||||
INDEX2COORDS(e, outputTadRank, outputTadShapePtr, zCoords);
|
||||
COORDS2INDEX(outputTadRank, outputTadStridePtr, zCoords, zIndex);
|
||||
|
||||
if (lengths[segment]) math::atomics::sd_atomicMax<T>(&z[zIndex], x[xIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static void segmentMaxFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
T val = -DataTypeUtils::max<T>();
|
||||
output->assign(val);
|
||||
auto stream = context->getCudaStream();
|
||||
indices->syncToHost();
|
||||
LongType numOfClasses = indices->e<LongType>(indices->lengthOf() - 1) + 1;
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
int zero2 = 0;
|
||||
classesRangesLens.assign(zero2);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
fillUpSegments(indices, numOfClasses, classesRangesBegs, classesRangesLens);
|
||||
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, &classesRangesBegs, &classesRangesLens});
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
dim3 launchDims = segmentDims(numOfClasses,input->lengthOf());
|
||||
segmentMaxLinearKernel<T, I><<<launchDims.y,launchDims.x,launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), begins, lengths, numOfClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMaxLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dim3 launchDims = segmentTad(packX->numberOfTads());
|
||||
segmentMaxTadKernel<T, I><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numOfClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets,static_cast<T>(0),
|
||||
indices->lengthOf(),packX->numberOfTads(),packZ->numberOfTads());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMaxTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, &classesRangesBegs, &classesRangesLens});
|
||||
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void segmentMaxFunctor(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), segmentMaxFunctor_, (context, input, indices, output),
|
||||
SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static void unsortedSegmentMaxFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
T val = DataTypeUtils::infOrMax<T>();
|
||||
output->assign(val);
|
||||
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
int zero2 = 0;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero2);
|
||||
|
||||
dim3 dims = getFillUpSegmentsDims(numOfClasses, indices->lengthOf());
|
||||
fillUpSegments(indices, numOfClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
unsortedSegmentMaxLinearKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
begins, lengths, numOfClasses, output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "unsortedSegmentMaxLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dims.x = input->sizeAt(0);
|
||||
T val = -DataTypeUtils::max<T>();
|
||||
output->assign(val);
|
||||
segmentMaxTadKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numOfClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets,static_cast<T>(0),indices->lengthOf(),packX->numberOfTads(),packZ->numberOfTads());
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void unsortedSegmentMaxFunctor(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
output->nullify();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), unsortedSegmentMaxFunctor_,
|
||||
(context, input, indices, numOfClasses, output), SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// segment max
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMaxBPLinearKernel(void* inputBuf, LongType const* inputShape, void* forwardOutput,
|
||||
LongType const* forwardShape, void* eps, LongType const* epsShape, void* indicesBuf, LongType const* indicesShape, void* outputBuf,
|
||||
LongType const* outputShape, LongType indicesLen) {
|
||||
__shared__ T* x;
|
||||
__shared__ T* gradIn;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, gradLen;
|
||||
|
||||
// Cache shape/stride/rank information in shared memory
|
||||
__shared__ sd::LongType xRank, yRank, zRank, gradInRank, gradOutRank;
|
||||
__shared__ const sd::LongType *xShapePtr, *yShapePtr, *zShapePtr, *gradInShapePtr, *gradOutShapePtr;
|
||||
__shared__ const sd::LongType *xStridePtr, *yStridePtr, *zStridePtr, *gradInStridePtr, *gradOutStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
y = reinterpret_cast<I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
gradIn = reinterpret_cast<T*>(forwardOutput);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradLen = shape::length(epsShape);
|
||||
|
||||
// Cache all shape information
|
||||
xRank = shape::rank(inputShape);
|
||||
yRank = shape::rank(indicesShape);
|
||||
zRank = shape::rank(outputShape);
|
||||
gradInRank = shape::rank(forwardShape);
|
||||
gradOutRank = shape::rank(epsShape);
|
||||
|
||||
xShapePtr = shape::shapeOf(inputShape);
|
||||
yShapePtr = shape::shapeOf(indicesShape);
|
||||
zShapePtr = shape::shapeOf(outputShape);
|
||||
gradInShapePtr = shape::shapeOf(forwardShape);
|
||||
gradOutShapePtr = shape::shapeOf(epsShape);
|
||||
|
||||
xStridePtr = shape::stride(inputShape);
|
||||
yStridePtr = shape::stride(indicesShape);
|
||||
zStridePtr = shape::stride(outputShape);
|
||||
gradInStridePtr = shape::stride(forwardShape);
|
||||
gradOutStridePtr = shape::stride(epsShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = gridDim.x * blockDim.x;
|
||||
|
||||
for (auto e = start; e < indicesLen; e += step) {
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType gradICoords[SD_MAX_RANK];
|
||||
LongType gradOCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
LongType xOffset;
|
||||
LongType yOffset;
|
||||
LongType gradOffsetI;
|
||||
LongType gradOffsetO;
|
||||
|
||||
INDEX2COORDS(e, zRank, zShapePtr, zCoords);
|
||||
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
|
||||
INDEX2COORDS(e, xRank, xShapePtr, xCoords);
|
||||
COORDS2INDEX(xRank, xStridePtr, xCoords, xOffset);
|
||||
INDEX2COORDS(e, yRank, yShapePtr, yCoords);
|
||||
COORDS2INDEX(yRank, yStridePtr, yCoords, yOffset);
|
||||
|
||||
auto classIndex = y[yOffset];
|
||||
INDEX2COORDS(classIndex, gradInRank, gradInShapePtr, gradICoords);
|
||||
COORDS2INDEX(gradInRank, gradInStridePtr, gradICoords, gradOffsetI);
|
||||
INDEX2COORDS(classIndex, gradOutRank, gradOutShapePtr, gradOCoords);
|
||||
COORDS2INDEX(gradOutRank, gradOutStridePtr, gradOCoords, gradOffsetO);
|
||||
|
||||
if (math::sd_abs<T,T>(gradIn[gradOffsetI] - x[xOffset]) <= T(1.e-6)) {
|
||||
z[zOffset] = gradOut[gradOffsetO];
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMaxBPTadKernel(void* inputBuf, LongType const* inputShape,
|
||||
void* forwardOutput,
|
||||
LongType const* forwardShape,
|
||||
void* eps, LongType const* epsShape,
|
||||
void* indicesBuf, LongType const* indicesShape,
|
||||
void* outputBuf,
|
||||
LongType const* outputShape, LongType const* inputTadShapeInfo,
|
||||
LongType const* inputOffsets, LongType const* gradInTadShapeInfo,
|
||||
LongType const* gradInOffsets, LongType const* gradOutTadShapeInfo,
|
||||
LongType const* gradOutOffsets, LongType const* outTadShapeInfo,
|
||||
LongType const* outOffsets, LongType indicesLen) {
|
||||
__shared__ T* x;
|
||||
__shared__ I *indices;
|
||||
__shared__ T* gradIn;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, yLen, gradLen, currentLen, gradOutLen, inLen;
|
||||
|
||||
// Cache shape information for all TADs
|
||||
__shared__ sd::LongType inputTadRank;
|
||||
__shared__ const sd::LongType* inputTadShapePtr;
|
||||
__shared__ const sd::LongType* inputTadStridePtr;
|
||||
|
||||
__shared__ sd::LongType gradInTadRank;
|
||||
__shared__ const sd::LongType* gradInTadShapePtr;
|
||||
__shared__ const sd::LongType* gradInTadStridePtr;
|
||||
|
||||
__shared__ sd::LongType gradOutTadRank;
|
||||
__shared__ const sd::LongType* gradOutTadShapePtr;
|
||||
__shared__ const sd::LongType* gradOutTadStridePtr;
|
||||
|
||||
__shared__ sd::LongType outTadRank;
|
||||
__shared__ const sd::LongType* outTadShapePtr;
|
||||
__shared__ const sd::LongType* outTadStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
indices = reinterpret_cast<I*>(indicesBuf);
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
y = reinterpret_cast<I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
yLen = shape::length(indicesShape);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradIn = reinterpret_cast<T*>(forwardOutput);
|
||||
gradLen = shape::length(epsShape);
|
||||
inLen = shape::length(gradInTadShapeInfo);
|
||||
gradOutLen = shape::length(gradOutTadShapeInfo);
|
||||
currentLen = shape::length(inputTadShapeInfo);
|
||||
|
||||
// Cache all TAD shape information
|
||||
inputTadRank = shape::rank(inputTadShapeInfo);
|
||||
inputTadShapePtr = shape::shapeOf(inputTadShapeInfo);
|
||||
inputTadStridePtr = shape::stride(inputTadShapeInfo);
|
||||
|
||||
gradInTadRank = shape::rank(gradInTadShapeInfo);
|
||||
gradInTadShapePtr = shape::shapeOf(gradInTadShapeInfo);
|
||||
gradInTadStridePtr = shape::stride(gradInTadShapeInfo);
|
||||
|
||||
gradOutTadRank = shape::rank(gradOutTadShapeInfo);
|
||||
gradOutTadShapePtr = shape::shapeOf(gradOutTadShapeInfo);
|
||||
gradOutTadStridePtr = shape::stride(gradOutTadShapeInfo);
|
||||
|
||||
outTadRank = shape::rank(outTadShapeInfo);
|
||||
outTadShapePtr = shape::shapeOf(outTadShapeInfo);
|
||||
outTadStridePtr = shape::stride(outTadShapeInfo);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto i = blockIdx.x; i < indicesLen; i += gridDim.x) {
|
||||
I segment = indices[i];
|
||||
T* current = x;
|
||||
T* currentOut = z;
|
||||
auto classNum = segment;
|
||||
auto currentOffset = inputOffsets[i];
|
||||
auto currentOutOffset = outOffsets[i];
|
||||
auto currentGradOutOffset = gradOutOffsets[classNum];
|
||||
auto bPTensorOffset = gradInOffsets[classNum];
|
||||
|
||||
auto gradIn2 = gradIn + bPTensorOffset;
|
||||
auto current2 = current + currentOffset;
|
||||
auto currentGradOut2 = gradOut + currentGradOutOffset;
|
||||
auto currentOut2 = currentOut + currentOutOffset;
|
||||
|
||||
for (auto e = threadIdx.x; e < currentLen; e += blockDim.x) {
|
||||
sd::LongType xCoords[SD_MAX_RANK];
|
||||
sd::LongType gradInCoords[SD_MAX_RANK];
|
||||
sd::LongType gradOutCoords[SD_MAX_RANK];
|
||||
sd::LongType outCoords[SD_MAX_RANK];
|
||||
sd::LongType xIndex;
|
||||
sd::LongType gradInIndex;
|
||||
sd::LongType gradOutIndex;
|
||||
sd::LongType outIndex;
|
||||
|
||||
INDEX2COORDS(e, inputTadRank, inputTadShapePtr, xCoords);
|
||||
COORDS2INDEX(inputTadRank, inputTadStridePtr, xCoords, xIndex);
|
||||
INDEX2COORDS(e, gradInTadRank, gradInTadShapePtr, gradInCoords);
|
||||
COORDS2INDEX(gradInTadRank, gradInTadStridePtr, gradInCoords, gradInIndex);
|
||||
INDEX2COORDS(e, gradOutTadRank, gradOutTadShapePtr, gradOutCoords);
|
||||
COORDS2INDEX(gradOutTadRank, gradOutTadStridePtr, gradOutCoords, gradOutIndex);
|
||||
INDEX2COORDS(e, outTadRank, outTadShapePtr, outCoords);
|
||||
COORDS2INDEX(outTadRank, outTadStridePtr, outCoords, outIndex);
|
||||
|
||||
if (math::sd_abs<T, T>(gradIn2[gradInIndex] - current2[xIndex]) <= T(1.e-6)) {
|
||||
currentOut2[outIndex] = currentGradOut2[gradOutIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
Status segmentMaxFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
// if input is a vector: (as if in doc sample)
|
||||
auto stream = context->getCudaStream();
|
||||
/* NDArray tempRes(gradOut->ordering(), gradOut->getShapeAsVector(), DataTypeUtils::fromT<T>(),
|
||||
context); */
|
||||
auto outShape = gradOut->getShapeAsVector();
|
||||
NDArray tempRes(gradOut->ordering(), outShape, DataTypeUtils::fromT<T>(), context);
|
||||
segmentMaxFunctor_<T, I>(context, input, indices, &tempRes);
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut, &tempRes});
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf();
|
||||
dim3 segmentBpDims2 = segmentBpDims(1 + gradOut->lengthOf(),input->lengthOf());
|
||||
segmentMaxBPLinearKernel<T, I><<<segmentBpDims2.y, segmentBpDims2.x, segmentBpDims2.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMaxBPLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
|
||||
NDArray::preparePrimaryUse({&tempRes}, {&tempRes});
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradIn = ConstantTadHelper::getInstance().tadForDimensions(tempRes.shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
LongType const* inputTadShapeInfo = packX->specialShapeInfo();
|
||||
LongType const* inputTadOffsets = packX->specialOffsets();
|
||||
LongType const* outputTadShapeInfo = packZ->specialShapeInfo();
|
||||
LongType const* outputTadOffsets = packZ->specialOffsets();
|
||||
LongType const* gradInTadShapeInfo = packGradIn->specialShapeInfo();
|
||||
LongType const* gradInTadOffsets = packGradIn->specialOffsets();
|
||||
LongType const* gradOutTadShapeInfo = packGradOut->specialShapeInfo();
|
||||
LongType const* gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
dim3 segmentBpTad2 = segmentBpTad(gradOut->lengthOf(),input->lengthOf());
|
||||
segmentMaxBPTadKernel<T, I><<<segmentBpTad2.x, segmentBpTad2.y, segmentBpTad2.z, *stream>>>(
|
||||
input->specialBuffer(),
|
||||
input->specialShapeInfo(),
|
||||
tempRes.specialBuffer(),
|
||||
tempRes.specialShapeInfo(),
|
||||
|
||||
gradOut->specialBuffer(),
|
||||
gradOut->specialShapeInfo(),
|
||||
|
||||
indices->specialBuffer(),
|
||||
indices->specialShapeInfo(),
|
||||
output->specialBuffer(),
|
||||
output->specialShapeInfo(),
|
||||
inputTadShapeInfo,
|
||||
inputTadOffsets, gradInTadShapeInfo,
|
||||
gradInTadOffsets, gradOutTadShapeInfo,
|
||||
gradOutTadOffsets, outputTadShapeInfo,
|
||||
outputTadOffsets,
|
||||
indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMaxBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut, &tempRes});
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
Status segmentMaxFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return segmentMaxFunctorBP_,
|
||||
(context, input, indices, gradOut, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static Status unsortedSegmentMaxFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices,
|
||||
NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
// if input is a vector: (as if in doc sample)
|
||||
auto stream = context->getCudaStream();
|
||||
auto outShape = gradOut->getShapeAsVector();
|
||||
NDArray tempRes(gradOut->ordering(), outShape, DataTypeUtils::fromT<T>(),
|
||||
context);
|
||||
unsortedSegmentMaxFunctor_<T, I>(context, input, indices, numOfClasses, &tempRes);
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut, &tempRes});
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf(); // indices->e<sd::LongType>(loop_size - 1);
|
||||
segmentMaxBPLinearKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(),indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMaxBPLinearKernel failed");
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradIn = ConstantTadHelper::getInstance().tadForDimensions(tempRes.shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
LongType const* inputTads = packX->specialShapeInfo();
|
||||
LongType const* inputTadOffsets = packX->specialOffsets();
|
||||
LongType const* outputTads = packZ->specialShapeInfo();
|
||||
LongType const* outputTadOffsets = packZ->specialOffsets();
|
||||
LongType const* gradInTads = packGradIn->specialShapeInfo();
|
||||
LongType const* gradInTadOffsets = packGradIn->specialOffsets();
|
||||
LongType const* gradOutTads = packGradOut->specialShapeInfo();
|
||||
LongType const* gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
|
||||
|
||||
segmentMaxBPTadKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(),
|
||||
input->specialShapeInfo(),
|
||||
tempRes.specialBuffer(),
|
||||
tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(),
|
||||
gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), inputTads, inputTadOffsets, gradInTads, gradInTadOffsets,
|
||||
gradOutTads, gradOutTadOffsets, outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMaxBPTadKernel failed");
|
||||
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut, &tempRes});
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
Status unsortedSegmentMaxFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return unsortedSegmentMaxFunctorBP_,
|
||||
(context, input, indices, gradOut, numOfClasses, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,632 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/segment.h>
|
||||
#include <ops/declarable/helpers/segment_common.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
#include <system/selective_rendering.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Segment ops linear kernels
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMeanLinearKernel(void* input, LongType const* inputShape, LongType* indices,
|
||||
LongType* lengths, LongType numOfClasses, void* output,
|
||||
LongType const* outputShape) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType xLen, zLen, zIndex;
|
||||
__shared__ T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType threadsPerSegment, start, finish;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
|
||||
auto segment = blockIdx.x;
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
extern __shared__ unsigned char shmem[];
|
||||
val = reinterpret_cast<T*>(shmem);
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
// Cache shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
if (segment < numOfClasses) {
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
LongType zOffset;
|
||||
|
||||
INDEX2COORDS(segment, outputRank, outputShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, zIndex);
|
||||
start = indices[segment];
|
||||
finish = start + lengths[segment];
|
||||
INDEX2COORDS(start, inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, xOffset);
|
||||
if (lengths[segment] > 0)
|
||||
z[zIndex] = T(x[xOffset] / T(lengths[segment]));
|
||||
else
|
||||
z[zIndex] = 0;
|
||||
val[segment] = z[zIndex];
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto e = start + threadIdx.x + 1; e < finish; e += blockDim.x) {
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, xOffset);
|
||||
math::atomics::sd_atomicAdd(&z[zIndex], T(x[xOffset] / static_cast<T>(lengths[segment])));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void unsortedSegmentMeanLinearKernel(void* input, LongType const* inputShape, void* indices,
|
||||
LongType const* indicesShape, LongType* starts, LongType* lengths,
|
||||
LongType numOfClasses, void* output,
|
||||
LongType const* outputShape) {
|
||||
__shared__ LongType xLen, zLen, zIndex;
|
||||
__shared__ T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ I* y;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank, indicesRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
|
||||
auto segment = blockIdx.x;
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
y = reinterpret_cast<I*>(indices);
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
// Cache shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
LongType zOffset;
|
||||
|
||||
INDEX2COORDS(segment, outputRank, outputShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, zIndex);
|
||||
INDEX2COORDS(starts[segment], inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, xOffset);
|
||||
|
||||
if (lengths[segment] > 0)
|
||||
z[zIndex] = T(x[xOffset] / T(lengths[segment]));
|
||||
else
|
||||
z[zIndex] = 0;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (lengths[segment] > 0) {
|
||||
for (auto e = threadIdx.x; e < xLen; e += blockDim.x) {
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
LongType yIndex;
|
||||
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, xOffset);
|
||||
INDEX2COORDS(e, indicesRank, indicesShapePtr, inputCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, inputCoords, yIndex);
|
||||
|
||||
if (y[yIndex] == segment && e != starts[segment]) {
|
||||
math::atomics::sd_atomicAdd(&z[zIndex], T(x[xOffset] / T(lengths[segment])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMeanTadKernel(void* inputBuf, LongType const* inputShape, LongType const* inputTads,
|
||||
LongType const* inputTadOffsets,
|
||||
I* indices, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* outputBuf,
|
||||
LongType const* outputShape, LongType const* outputTads,
|
||||
LongType const* outputTadOffsets, LongType indicesLen) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType len, zIndex, total;
|
||||
__shared__ T* z;
|
||||
__shared__ int threadsPerSegment, start, finish;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputTadRank, outputTadRank;
|
||||
__shared__ const sd::LongType* inputTadShapePtr;
|
||||
__shared__ const sd::LongType* outputTadShapePtr;
|
||||
__shared__ const sd::LongType* inputTadStridePtr;
|
||||
__shared__ const sd::LongType* outputTadStridePtr;
|
||||
|
||||
if(blockIdx.x >= indicesLen)
|
||||
return;
|
||||
|
||||
auto segment = indices[blockIdx.x];
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
z = reinterpret_cast<T*>(outputBuf) + outputTadOffsets[segment];
|
||||
len = shape::length(inputTads);
|
||||
start = starts[segment];
|
||||
finish = start + lengths[segment];
|
||||
total = shape::sizeAt(inputShape, 0);
|
||||
|
||||
// Cache TAD shape information
|
||||
inputTadRank = shape::rank(inputTads);
|
||||
outputTadRank = shape::rank(outputTads);
|
||||
inputTadShapePtr = shape::shapeOf(inputTads);
|
||||
outputTadShapePtr = shape::shapeOf(outputTads);
|
||||
inputTadStridePtr = shape::stride(inputTads);
|
||||
outputTadStridePtr = shape::stride(outputTads);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto idx = blockIdx.x;
|
||||
if (blockIdx.x <= total) {
|
||||
auto x = reinterpret_cast<T*>(inputBuf) + inputTadOffsets[idx];
|
||||
if (blockIdx.x == start) {
|
||||
for (auto e = threadIdx.x; e < len; e += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xIndex;
|
||||
LongType zIndex;
|
||||
|
||||
INDEX2COORDS(e, inputTadRank, inputTadShapePtr, xCoords);
|
||||
COORDS2INDEX(inputTadRank, inputTadStridePtr, xCoords, xIndex);
|
||||
INDEX2COORDS(e, outputTadRank, outputTadShapePtr, zCoords);
|
||||
COORDS2INDEX(outputTadRank, outputTadStridePtr, zCoords, zIndex);
|
||||
|
||||
math::atomics::sd_atomicAdd(&z[zIndex], T(x[xIndex] / lengths[segment]));
|
||||
}
|
||||
} else {
|
||||
for (auto e = threadIdx.x; e < len; e += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xIndex;
|
||||
LongType zIndex;
|
||||
|
||||
INDEX2COORDS(e, inputTadRank, inputTadShapePtr, xCoords);
|
||||
COORDS2INDEX(inputTadRank, inputTadStridePtr, xCoords, xIndex);
|
||||
INDEX2COORDS(e, outputTadRank, outputTadShapePtr, zCoords);
|
||||
COORDS2INDEX(outputTadRank, outputTadStridePtr, zCoords, zIndex);
|
||||
|
||||
if (lengths[segment]) math::atomics::sd_atomicAdd(&z[zIndex], T(x[xIndex] / lengths[segment]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// segment mean
|
||||
template <typename T, typename I>
|
||||
static void segmentMeanFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
LongType numClasses = indices->e<LongType>(indices->lengthOf() - 1) + 1;
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
|
||||
int zero2 = 0;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero2);
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
fillUpSegments(indices, numClasses, classesRangesBegs, classesRangesLens);
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
dim3 launchDims = segmentDims(numClasses,input->lengthOf());
|
||||
segmentMeanLinearKernel<T, I><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), begins, lengths, numClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMeanLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dim3 launchDims = segmentTad(input->sizeAt(0));
|
||||
segmentMeanTadKernel<T, I><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets,indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMeanTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void segmentMeanFunctor(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
UILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), segmentMeanFunctor_, (context, input, indices, output),
|
||||
SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static void unsortedSegmentMeanFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
|
||||
int zero2 = 0;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero2);
|
||||
dim3 dims = getFillUpSegmentsDims(numOfClasses, indices->lengthOf());
|
||||
fillUpSegments(indices, numOfClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
unsortedSegmentMeanLinearKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
begins, lengths, numOfClasses, output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "unsortedSegmentMeanLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
output->assign(zero);
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
LongType const* inputTads = packX->specialShapeInfo();
|
||||
LongType const* inputTadOffsets = packX->specialOffsets();
|
||||
LongType const* outputTads = packZ->specialShapeInfo();
|
||||
LongType const* outputTadOffsets = packZ->specialOffsets();
|
||||
dims.x = input->sizeAt(0);
|
||||
segmentMeanTadKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numOfClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMeanTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void unsortedSegmentMeanFunctor(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto inputDType = input->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), unsortedSegmentMeanFunctor_,
|
||||
(context, input, indices, numOfClasses, output), SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMeanBPLinearKernel(void* inputBuf, LongType const* inputShape, void* eps,
|
||||
LongType const* epsShape, void* indicesBuf,
|
||||
LongType const* indicesShape, LongType* lengths, void* outputBuf,
|
||||
LongType const* outputShape) {
|
||||
__shared__ T* x;
|
||||
__shared__ T* gradIn;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, gradLen;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank, indicesRank, epsRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* epsShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
__shared__ const sd::LongType* epsStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
y = reinterpret_cast<I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradLen = shape::length(epsShape);
|
||||
|
||||
// Cache all shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
epsRank = shape::rank(epsShape);
|
||||
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
epsShapePtr = shape::shapeOf(epsShape);
|
||||
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
epsStridePtr = shape::stride(epsShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = gridDim.x * blockDim.x;
|
||||
|
||||
for (auto e = start; e < xLen; e += step) {
|
||||
LongType zOffset, xOffset, yOffset, gradOffsetO;
|
||||
sd::LongType zCoords[SD_MAX_RANK], xCoords[SD_MAX_RANK], yCoords[SD_MAX_RANK], gradCoords[SD_MAX_RANK];
|
||||
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zOffset);
|
||||
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, xCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xOffset);
|
||||
|
||||
INDEX2COORDS(e, indicesRank, indicesShapePtr, yCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yOffset);
|
||||
|
||||
auto classIndex = y[yOffset];
|
||||
|
||||
INDEX2COORDS(classIndex, epsRank, epsShapePtr, gradCoords);
|
||||
COORDS2INDEX(epsRank, epsStridePtr, gradCoords, gradOffsetO);
|
||||
|
||||
z[zOffset] = T(gradOut[gradOffsetO] / float(lengths[classIndex]));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMeanBPTadKernel(void* inputBuf, LongType const* inputShape, void* eps,
|
||||
LongType const* epsShape, void* indicesBuf, LongType const* indicesShape,
|
||||
LongType* lengths, void* outputBuf, LongType const* outputShape,
|
||||
LongType const* inputTad, LongType const* inputOffsets,
|
||||
LongType const* gradOutTad, LongType const* gradOutOffsets,
|
||||
LongType const* outTad, LongType const* outOffsets) {
|
||||
__shared__ T* x;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, yLen, gradLen, currentLen;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType outTadRank, gradOutTadRank;
|
||||
__shared__ const sd::LongType* outTadShapePtr;
|
||||
__shared__ const sd::LongType* gradOutTadShapePtr;
|
||||
__shared__ const sd::LongType* outTadStridePtr;
|
||||
__shared__ const sd::LongType* gradOutTadStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
y = reinterpret_cast<I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
yLen = shape::length(indicesShape);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradLen = shape::length(epsShape);
|
||||
currentLen = shape::length(outTad);
|
||||
|
||||
// Cache TAD shape information
|
||||
outTadRank = shape::rank(outTad);
|
||||
gradOutTadRank = shape::rank(gradOutTad);
|
||||
outTadShapePtr = shape::shapeOf(outTad);
|
||||
gradOutTadShapePtr = shape::shapeOf(gradOutTad);
|
||||
outTadStridePtr = shape::stride(outTad);
|
||||
gradOutTadStridePtr = shape::stride(gradOutTad);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto i = blockIdx.x; i < yLen; i += gridDim.x) {
|
||||
auto segment = y[i];
|
||||
T* currentOut = z + outOffsets[i];
|
||||
T* outGrad = gradOut + gradOutOffsets[segment];
|
||||
|
||||
for (auto e = threadIdx.x; e < currentLen; e += blockDim.x) {
|
||||
sd::LongType zCoords[SD_MAX_RANK];
|
||||
sd::LongType gradCoords[SD_MAX_RANK];
|
||||
sd::LongType zIndex;
|
||||
sd::LongType gradIndex;
|
||||
|
||||
INDEX2COORDS(e, outTadRank, outTadShapePtr, zCoords);
|
||||
COORDS2INDEX(outTadRank, outTadStridePtr, zCoords, zIndex);
|
||||
INDEX2COORDS(e, gradOutTadRank, gradOutTadShapePtr, gradCoords);
|
||||
COORDS2INDEX(gradOutTadRank, gradOutTadStridePtr, gradCoords, gradIndex);
|
||||
|
||||
if (lengths[segment] > 0) currentOut[zIndex] = T(outGrad[gradIndex] / float(lengths[segment]));
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// backrop for mean
|
||||
template <typename T, typename I>
|
||||
Status segmentMeanFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto numClasses = indices->e<LongType>(indices->lengthOf() - 1) + 1;
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
sd::LongType zero2 = 0;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(zero2);
|
||||
classesRangesLens.assign(len);
|
||||
fillUpSegments(indices, numClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf(); // indices->e<sd::LongType>(loop_size - 1);
|
||||
dim3 segmentBpDims2 = segmentBpDims(gradOut->lengthOf(),input->lengthOf());
|
||||
segmentMeanBPLinearKernel<T, I><<<segmentBpDims2.y, segmentBpDims2.x, segmentBpDims2.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), lengths, output->specialBuffer(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMeanBPLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
LongType const* inputTads = packX->specialShapeInfo();
|
||||
LongType const* inputTadOffsets = packX->specialOffsets();
|
||||
LongType const* outputTads = packZ->specialShapeInfo();
|
||||
LongType const* outputTadOffsets = packZ->specialOffsets();
|
||||
LongType const* gradOutTads = packGradOut->specialShapeInfo();
|
||||
LongType const* gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
dim3 segmentBpTad2 = segmentBpTad(indices->lengthOf(),input->lengthOf());
|
||||
|
||||
segmentMeanBPTadKernel<T, I><<<segmentBpTad2.y, segmentBpTad2.x, segmentBpTad2.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), lengths, output->specialBuffer(),
|
||||
output->specialShapeInfo(), inputTads, inputTadOffsets, gradOutTads, gradOutTadOffsets, outputTads,
|
||||
outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMeanBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// segmen mean bp main
|
||||
Status segmentMeanFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return segmentMeanFunctorBP_,
|
||||
(context, input, indices, gradOut, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static Status unsortedSegmentMeanFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices,
|
||||
NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto numClasses = indices->e<LongType>(indices->lengthOf() - 1) + 1;
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
|
||||
sd::LongType zero2 = 0;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(zero2);
|
||||
classesRangesLens.assign(len);
|
||||
fillUpSegments(indices, numClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf();
|
||||
dim3 segmentBpDims2 = segmentBpDims(gradOut->lengthOf(),input->lengthOf());
|
||||
segmentMeanBPLinearKernel<T, I><<<segmentBpDims2.y,segmentBpDims2.x,segmentBpDims2.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), lengths, output->specialBuffer(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMeanBPLinearKernel failed");
|
||||
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(),1, &zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
LongType const* inputTads = packX->specialShapeInfo();
|
||||
LongType const* inputTadOffsets = packX->specialOffsets();
|
||||
LongType const* outputTads = packZ->specialShapeInfo();
|
||||
LongType const* outputTadOffsets = packZ->specialOffsets();
|
||||
LongType const* gradOutTads = packGradOut->specialShapeInfo();
|
||||
LongType const* gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
dim3 segmentBpTad2 = segmentBpTad(indices->lengthOf(),input->lengthOf());
|
||||
|
||||
segmentMeanBPTadKernel<T, I><<<segmentBpTad2.y,segmentBpTad2.x, segmentBpTad2.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), lengths, output->specialBuffer(),
|
||||
output->specialShapeInfo(), inputTads, inputTadOffsets, gradOutTads, gradOutTadOffsets, outputTads,
|
||||
outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMeanBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
Status unsortedSegmentMeanFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return unsortedSegmentMeanFunctorBP_,
|
||||
(context, input, indices, gradOut, numOfClasses, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,620 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/segment.h>
|
||||
#include <ops/declarable/helpers/segment_common.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
#include <system/selective_rendering.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Segment ops linear kernels
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMinLinearKernel(const void* input, const LongType* inputShape, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* output,
|
||||
const LongType* outputShape) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType xLen, zLen, zIndex;
|
||||
__shared__ const T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType threadsPerSegment, start, finish;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
|
||||
auto segment = blockIdx.x;
|
||||
if(blockIdx.x >= numOfClasses)
|
||||
return;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
x = reinterpret_cast<const T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
extern __shared__ unsigned char shmem[];
|
||||
val = reinterpret_cast<T*>(shmem);
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
// Cache shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
if (segment < numOfClasses) {
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
INDEX2COORDS(segment, outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zIndex);
|
||||
if(zIndex >= zLen)
|
||||
return;
|
||||
start = starts[segment];
|
||||
finish = start + lengths[segment];
|
||||
LongType startCoords[SD_MAX_RANK];
|
||||
LongType startIndex;
|
||||
INDEX2COORDS(start, inputRank, inputShapePtr, startCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, startCoords, startIndex);
|
||||
z[zIndex] = x[startIndex];
|
||||
val[segment] = z[zIndex];
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto e = start + threadIdx.x + 1; e < finish; e += blockDim.x) {
|
||||
LongType eCoords[SD_MAX_RANK];
|
||||
LongType eIndex;
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, eCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, eCoords, eIndex);
|
||||
if (eIndex >= xLen) return;
|
||||
math::atomics::sd_atomicMin(&z[zIndex], x[eIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void unsortedSegmentMinLinearKernel(const void* input, const LongType* inputShape,
|
||||
const void* indices, const LongType* indicesShape, LongType* starts, LongType* lengths,
|
||||
LongType numOfClasses, void* output,
|
||||
const LongType* outputShape) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType xLen, zLen, segment, zIndex;
|
||||
__shared__ const T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ const I* y;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank, indicesRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
segment = blockIdx.x;
|
||||
x = reinterpret_cast<const T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
y = reinterpret_cast<const I*>(indices);
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
// Cache shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
INDEX2COORDS(segment, outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zIndex);
|
||||
if (lengths[segment] > 0) {
|
||||
LongType startCoords[SD_MAX_RANK];
|
||||
LongType startIndex;
|
||||
INDEX2COORDS(starts[segment], inputRank, inputShapePtr, startCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, startCoords, startIndex);
|
||||
z[zIndex] = x[startIndex];
|
||||
} else {
|
||||
z[zIndex] = DataTypeUtils::max<T>();
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (lengths[segment] > 0) {
|
||||
for (auto e = threadIdx.x + 1; e < xLen; e += blockDim.x) {
|
||||
LongType eCoords[SD_MAX_RANK];
|
||||
LongType eIndex;
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, eCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, eCoords, eIndex);
|
||||
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType yIndex;
|
||||
INDEX2COORDS(e, indicesRank, indicesShapePtr, yCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yIndex);
|
||||
|
||||
if (y[yIndex] == segment) {
|
||||
math::atomics::sd_atomicMin(&z[zIndex], x[eIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMinTadKernel(const void* inputBuf, const LongType* inputShape,
|
||||
const LongType* inputTads, const LongType* inputTadOffsets,
|
||||
I* indices, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* outputBuf, const LongType* outputShape,
|
||||
const LongType* outputTads, const LongType* outputTadOffsets, LongType indicesLen) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType len, zIndex, total;
|
||||
__shared__ T* z;
|
||||
__shared__ int threadsPerSegment, start, finish;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputTadRank, outputTadRank;
|
||||
__shared__ const sd::LongType* inputTadShapePtr;
|
||||
__shared__ const sd::LongType* outputTadShapePtr;
|
||||
__shared__ const sd::LongType* inputTadStridePtr;
|
||||
__shared__ const sd::LongType* outputTadStridePtr;
|
||||
|
||||
if(blockIdx.x >= indicesLen)
|
||||
return;
|
||||
|
||||
auto segment = indices[blockIdx.x];
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
z = reinterpret_cast<T*>(outputBuf) + outputTadOffsets[segment];
|
||||
len = shape::length(inputTads);
|
||||
start = starts[segment];
|
||||
finish = start + lengths[segment];
|
||||
total = shape::sizeAt(inputShape, 0);
|
||||
|
||||
// Cache TAD shape information
|
||||
inputTadRank = shape::rank(inputTads);
|
||||
outputTadRank = shape::rank(outputTads);
|
||||
inputTadShapePtr = shape::shapeOf(inputTads);
|
||||
outputTadShapePtr = shape::shapeOf(outputTads);
|
||||
inputTadStridePtr = shape::stride(inputTads);
|
||||
outputTadStridePtr = shape::stride(outputTads);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto idx = blockIdx.x;
|
||||
if (blockIdx.x <= total) {
|
||||
auto x = reinterpret_cast<const T*>(inputBuf) + inputTadOffsets[idx];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
LongType zOffset;
|
||||
|
||||
for (auto e = threadIdx.x; e < len; e += blockDim.x) {
|
||||
INDEX2COORDS(e, inputTadRank, inputTadShapePtr, xCoords);
|
||||
COORDS2INDEX(inputTadRank, inputTadStridePtr, xCoords, xOffset);
|
||||
INDEX2COORDS(e, outputTadRank, outputTadShapePtr, zCoords);
|
||||
COORDS2INDEX(outputTadRank, outputTadStridePtr, zCoords, zOffset);
|
||||
math::atomics::sd_atomicMin(&z[zOffset], x[xOffset]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// segmen min
|
||||
template <typename T, typename I>
|
||||
static void segmentMinFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
LongType numClasses = indices->e<LongType>(indices->lengthOf() - 1) + 1;
|
||||
auto classesRangesLens = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
auto classesRangesBegs = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
T val = DataTypeUtils::infOrMax<T>();
|
||||
output->assign(val);
|
||||
sd::LongType zero2 = 0;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(zero2);
|
||||
classesRangesLens.assign(len);
|
||||
fillUpSegments(indices, numClasses, classesRangesBegs, classesRangesLens);
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, &classesRangesBegs, &classesRangesLens});
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
dim3 launchDims = segmentDims(numClasses,input->lengthOf());
|
||||
segmentMinLinearKernel<T, I><<<launchDims.y,launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), begins, lengths, numClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMinLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(),1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dim3 launchDims = segmentTad(input->sizeAt(0));
|
||||
segmentMinTadKernel<T, I><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMinTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, &classesRangesBegs, &classesRangesLens});
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void segmentMinFunctor(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
output->nullify();
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), segmentMinFunctor_, (context, input, indices, output),
|
||||
SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static void unsortedSegmentMinFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
T val = DataTypeUtils::infOrMax<T>();
|
||||
sd::LongType len = indices->lengthOf();
|
||||
output->assign(val);
|
||||
sd::LongType zero = 0;
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero);
|
||||
dim3 dims = getFillUpSegmentsDims(numOfClasses, indices->lengthOf());
|
||||
fillUpSegments(indices, numOfClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
unsortedSegmentMinLinearKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
begins, lengths, numOfClasses, output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "unsortedSegmentMinLinearKernel failed");
|
||||
|
||||
} else {
|
||||
T val = DataTypeUtils::max<T>();
|
||||
output->assign(val);
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(),1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dims.x = input->sizeAt(0);
|
||||
segmentMinTadKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numOfClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMinTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void unsortedSegmentMinFunctor(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
output->nullify();
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), unsortedSegmentMinFunctor_,
|
||||
(context, input, indices, numOfClasses, output), SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMinBPLinearKernel(const void* inputBuf, const LongType* inputShape,
|
||||
void* forwardOutput, const LongType* forwardShape, void* eps,
|
||||
const LongType* epsShape, const void* indicesBuf,
|
||||
const LongType* indicesShape, void* outputBuf,
|
||||
const LongType* outputShape) {
|
||||
__shared__ const T* x;
|
||||
__shared__ T* gradIn;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ const I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, gradLen;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank, indicesRank, forwardRank, epsRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* forwardShapePtr;
|
||||
__shared__ const sd::LongType* epsShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
__shared__ const sd::LongType* forwardStridePtr;
|
||||
__shared__ const sd::LongType* epsStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
x = reinterpret_cast<const T*>(inputBuf);
|
||||
y = reinterpret_cast<const I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
gradIn = reinterpret_cast<T*>(forwardOutput);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradLen = shape::length(epsShape);
|
||||
|
||||
// Cache all shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
forwardRank = shape::rank(forwardShape);
|
||||
epsRank = shape::rank(epsShape);
|
||||
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
forwardShapePtr = shape::shapeOf(forwardShape);
|
||||
epsShapePtr = shape::shapeOf(epsShape);
|
||||
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
forwardStridePtr = shape::stride(forwardShape);
|
||||
epsStridePtr = shape::stride(epsShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = gridDim.x * blockDim.x;
|
||||
|
||||
for (auto e = start; e < xLen; e += step) {
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType gradICoords[SD_MAX_RANK];
|
||||
LongType gradOCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
LongType xOffset;
|
||||
LongType yOffset;
|
||||
LongType gradOffsetI;
|
||||
LongType gradOffsetO;
|
||||
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zOffset);
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, xCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xOffset);
|
||||
INDEX2COORDS(e, indicesRank, indicesShapePtr, yCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yOffset);
|
||||
auto classIndex = y[yOffset];
|
||||
INDEX2COORDS(classIndex, forwardRank, forwardShapePtr, gradICoords);
|
||||
COORDS2INDEX(forwardRank, forwardStridePtr, gradICoords, gradOffsetI);
|
||||
INDEX2COORDS(classIndex, epsRank, epsShapePtr, gradOCoords);
|
||||
COORDS2INDEX(epsRank, epsStridePtr, gradOCoords, gradOffsetO);
|
||||
|
||||
if (math::sd_abs<T, T>(gradIn[gradOffsetI] - x[xOffset]) <= T(1.e-6)) {
|
||||
z[zOffset] = gradOut[gradOffsetO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentMinBPTadKernel(const void* inputBuf, const LongType* inputShape, void* forwardOutput,
|
||||
const LongType* forwardShape, void* eps, const LongType* epsShape,
|
||||
const void* indicesBuf, const LongType* indicesShape, void* outputBuf,
|
||||
const LongType* outputShape, const LongType* inputTad,
|
||||
const LongType* inputOffsets, const LongType* gradInTad,
|
||||
const LongType* gradInOffsets, const LongType* gradOutTad,
|
||||
const LongType* gradOutOffsets, const LongType* outTad,
|
||||
const LongType* outOffsets) {
|
||||
__shared__ const T* x;
|
||||
__shared__ T* gradIn;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ const I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, yLen, gradLen, currentLen;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType indicesRank;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
x = reinterpret_cast<const T*>(inputBuf);
|
||||
y = reinterpret_cast<const I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
yLen = shape::length(indicesShape);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradIn = reinterpret_cast<T*>(forwardOutput);
|
||||
gradLen = shape::length(epsShape);
|
||||
currentLen = shape::length(outTad);
|
||||
|
||||
// Cache indices shape information (only needed for segment calculation)
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto i = blockIdx.x; i < yLen; i += gridDim.x) {
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType yIndex;
|
||||
INDEX2COORDS(i, indicesRank, indicesShapePtr, yCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yIndex);
|
||||
auto segment = y[yIndex];
|
||||
auto current = x + inputOffsets[i];
|
||||
auto currentOut = z + outOffsets[i];
|
||||
auto in = gradIn + gradInOffsets[segment];
|
||||
auto outGrad = gradOut + gradOutOffsets[segment];
|
||||
|
||||
for (auto e = threadIdx.x; e < currentLen; e += blockDim.x) {
|
||||
if (math::sd_abs<T,T>(in[e] - current[e]) <= T(1.e-6)) currentOut[e] = outGrad[e];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
Status segmentMinFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
|
||||
// if input is a vector: (as if in doc sample)
|
||||
auto stream = context->getCudaStream();
|
||||
auto outShape = gradOut->getShapeAsVector();
|
||||
NDArray tempRes(gradOut->ordering(), outShape, DataTypeUtils::fromT<T>(),
|
||||
context);
|
||||
segmentMinFunctor_<T, I>(context, input, indices, &tempRes);
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut, &tempRes});
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf();
|
||||
|
||||
segmentMinBPLinearKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMinBPLinearKernel failed");
|
||||
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(),1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradIn = ConstantTadHelper::getInstance().tadForDimensions(tempRes.shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
auto gradInTads = packGradIn->specialShapeInfo();
|
||||
auto gradInTadOffsets = packGradIn->specialOffsets();
|
||||
auto gradOutTads = packGradOut->specialShapeInfo();
|
||||
auto gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
|
||||
segmentMinBPTadKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), inputTads, inputTadOffsets, gradInTads, gradInTadOffsets,
|
||||
gradOutTads, gradOutTadOffsets, outputTads, outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMinBPTadKernel failed");
|
||||
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut, &tempRes});
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// segment min
|
||||
Status segmentMinFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return segmentMinFunctorBP_,
|
||||
(context, input, indices, gradOut, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static Status unsortedSegmentMinFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices,
|
||||
NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
// if input is a vector: (as if in doc sample)
|
||||
auto stream = context->getCudaStream();
|
||||
auto outShape = gradOut->getShapeAsVector();
|
||||
|
||||
NDArray tempRes(gradOut->ordering(), outShape, DataTypeUtils::fromT<T>(),
|
||||
context);
|
||||
unsortedSegmentMinFunctor_<T, I>(context, input, indices, numOfClasses, &tempRes);
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut, &tempRes});
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf();
|
||||
segmentMinBPLinearKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMinBPLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradIn = ConstantTadHelper::getInstance().tadForDimensions(tempRes.shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
auto gradInTads = packGradIn->specialShapeInfo();
|
||||
auto gradInTadOffsets = packGradIn->specialOffsets();
|
||||
auto gradOutTads = packGradOut->specialShapeInfo();
|
||||
auto gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
|
||||
segmentMinBPTadKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), inputTads, inputTadOffsets, gradInTads, gradInTadOffsets,
|
||||
gradOutTads, gradOutTadOffsets, outputTads, outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentMinBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut, &tempRes});
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
Status unsortedSegmentMinFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return unsortedSegmentMinFunctorBP_,
|
||||
(context, input, indices, gradOut, numOfClasses, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,862 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/segment.h>
|
||||
#include <ops/declarable/helpers/segment_common.h>
|
||||
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
#include <system/selective_rendering.h>
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Segment Prod ops linear kernels
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentProdLinearKernel(void* input, LongType const* inputShape, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* output,
|
||||
LongType const* outputShape) {
|
||||
|
||||
// Shared memory for caching shape, stride, and rank information
|
||||
__shared__ LongType inputRank;
|
||||
__shared__ const LongType* inputShapePtr;
|
||||
__shared__ const LongType* inputStridePtr;
|
||||
|
||||
__shared__ LongType outputRank;
|
||||
__shared__ const LongType* outputShapePtr;
|
||||
__shared__ const LongType* outputStridePtr;
|
||||
|
||||
// Shared memory for pointers and lengths initialized by thread 0
|
||||
__shared__ T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen;
|
||||
__shared__ LongType zLen;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache rank, shape, and stride for inputShape
|
||||
inputRank = shape::rank(inputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
|
||||
// Cache rank, shape, and stride for outputShape
|
||||
outputRank = shape::rank(outputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
// Cache lengths
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
// Initialize pointers
|
||||
x = reinterpret_cast<T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Calculate global thread index and step size
|
||||
LongType startIdx = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
LongType step = blockDim.x * gridDim.x;
|
||||
|
||||
// Coordinate arrays
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
|
||||
// Offset variables
|
||||
LongType xIndex;
|
||||
LongType zIndex;
|
||||
|
||||
// Iterate over each class segment assigned to this block
|
||||
for (LongType segment = blockIdx.x; segment < numOfClasses; segment += gridDim.x) {
|
||||
// Convert segment index to coordinates for outputShape
|
||||
INDEX2COORDS(segment, outputRank, outputShapePtr, outputCoords);
|
||||
// Convert coordinates back to linear index for outputShape
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, zIndex);
|
||||
|
||||
// Skip processing if zIndex is out of bounds
|
||||
if (zIndex >= zLen)
|
||||
continue;
|
||||
|
||||
// Retrieve start and finish indices for the current segment
|
||||
auto start = starts[segment];
|
||||
auto finish = start + lengths[segment];
|
||||
|
||||
// Skip processing if the length for the segment is zero
|
||||
if (lengths[segment] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate over elements within the segment, distributing work among threads
|
||||
for (LongType e = startIdx; e < finish; e += step) {
|
||||
// Convert linear index to coordinates for inputShape
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, inputCoords);
|
||||
// Convert coordinates back to linear index for inputShape
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, xIndex);
|
||||
|
||||
// Skip processing if xIndex is out of bounds
|
||||
if (xIndex >= xLen)
|
||||
continue;
|
||||
|
||||
// Perform atomic multiplication on the output buffer
|
||||
math::atomics::sd_atomicMul(&z[zIndex], x[xIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void unsortedSegmentProdLinearKernel(T* input, LongType const* inputShape, I* indices,
|
||||
LongType const* indicesShape, LongType* starts, LongType* lengths,
|
||||
LongType numOfClasses, T* output, LongType const* outputShape) {
|
||||
|
||||
// Shared memory for caching shape, stride, and rank information
|
||||
__shared__ LongType inputRank;
|
||||
__shared__ const LongType* inputShapePtr;
|
||||
__shared__ const LongType* inputStridePtr;
|
||||
|
||||
__shared__ LongType indicesRank;
|
||||
__shared__ const LongType* indicesShapePtr;
|
||||
__shared__ const LongType* indicesStridePtr;
|
||||
|
||||
__shared__ LongType outputRank;
|
||||
__shared__ const LongType* outputShapePtr;
|
||||
__shared__ const LongType* outputStridePtr;
|
||||
|
||||
// Shared memory for pointers and lengths initialized by thread 0
|
||||
__shared__ T* x;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen;
|
||||
__shared__ LongType zLen;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache rank, shape, and stride for inputShape
|
||||
inputRank = shape::rank(inputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
|
||||
// Cache rank, shape, and stride for indicesShape
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
|
||||
// Cache rank, shape, and stride for outputShape
|
||||
outputRank = shape::rank(outputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
// Cache lengths
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
// Initialize pointers
|
||||
x = input;
|
||||
y = indices;
|
||||
z = output;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Calculate global thread index and step size
|
||||
LongType startIdx = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
LongType step = blockDim.x * gridDim.x;
|
||||
|
||||
// Coordinate arrays
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
|
||||
// Offset variables
|
||||
LongType xIndex;
|
||||
LongType yIndex;
|
||||
LongType zIndex;
|
||||
|
||||
for (LongType idx = startIdx; idx < xLen; idx += step) {
|
||||
// Convert linear index to coordinates for inputShape
|
||||
INDEX2COORDS(idx, inputRank, inputShapePtr, xCoords);
|
||||
// Convert coordinates back to linear index for inputShape
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xIndex);
|
||||
|
||||
// Convert linear index to coordinates for indicesShape
|
||||
INDEX2COORDS(idx, indicesRank, indicesShapePtr, yCoords);
|
||||
// Convert coordinates back to linear index for indicesShape
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yIndex);
|
||||
|
||||
// Retrieve the segment index from indices
|
||||
auto segment = y[yIndex];
|
||||
|
||||
// Convert segment index to coordinates for outputShape
|
||||
INDEX2COORDS(segment, outputRank, outputShapePtr, zCoords);
|
||||
// Convert coordinates back to linear index for outputShape
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zIndex);
|
||||
|
||||
// Skip processing if the length for the segment is zero
|
||||
if (lengths[segment] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perform atomic multiplication on the output buffer
|
||||
math::atomics::sd_atomicMul(&z[zIndex], x[xIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// SegmentProd kernel
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentProdTadKernel(void* inputBuf, LongType const* inputShape, LongType const* inputTads,
|
||||
LongType const* inputTadOffsets,
|
||||
I* indices, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* outputBuf,
|
||||
LongType const* outputShape, LongType const* outputTads,
|
||||
LongType const* outputTadOffsets, LongType indicesLen) {
|
||||
|
||||
// Early exit if block index is out of range
|
||||
if (blockIdx.x >= indicesLen)
|
||||
return;
|
||||
|
||||
// Shared memory for caching shape, stride, and rank information
|
||||
__shared__ LongType inputTadRank;
|
||||
__shared__ const LongType* inputTadShapePtr;
|
||||
__shared__ const LongType* inputTadStridePtr;
|
||||
|
||||
__shared__ LongType outputTadRank;
|
||||
__shared__ const LongType* outputTadShapePtr;
|
||||
__shared__ const LongType* outputTadStridePtr;
|
||||
|
||||
__shared__ LongType inputRank;
|
||||
__shared__ const LongType* inputShapePtr;
|
||||
__shared__ const LongType* inputStridePtr;
|
||||
|
||||
__shared__ LongType outputRank;
|
||||
__shared__ const LongType* outputShapePtr;
|
||||
__shared__ const LongType* outputStridePtr;
|
||||
|
||||
// Shared memory for pointers and lengths initialized by thread 0
|
||||
__shared__ T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType len;
|
||||
__shared__ LongType total;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache rank, shape, and stride for inputTads
|
||||
inputTadRank = shape::rank(inputTads);
|
||||
inputTadShapePtr = shape::shapeOf(inputTads);
|
||||
inputTadStridePtr = shape::stride(inputTads);
|
||||
|
||||
// Cache rank, shape, and stride for outputTads
|
||||
outputTadRank = shape::rank(outputTads);
|
||||
outputTadShapePtr = shape::shapeOf(outputTads);
|
||||
outputTadStridePtr = shape::stride(outputTads);
|
||||
|
||||
// Cache rank, shape, and stride for inputShape
|
||||
inputRank = shape::rank(inputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
|
||||
// Cache rank, shape, and stride for outputShape
|
||||
outputRank = shape::rank(outputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
// Cache lengths and total size
|
||||
total = shape::sizeAt(inputShape, 0);
|
||||
len = shape::length(inputTads);
|
||||
|
||||
// Initialize pointers
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Calculate global thread index and step size
|
||||
LongType startIdx = blockIdx.x;
|
||||
LongType step = gridDim.x;
|
||||
|
||||
// Coordinate arrays
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
|
||||
// Offset variables
|
||||
LongType xIndex;
|
||||
LongType zIndex;
|
||||
|
||||
for (auto idx = startIdx; idx < total; idx += step) {
|
||||
// Retrieve the segment index from indices
|
||||
auto segment = indices[idx];
|
||||
|
||||
// Pointers to the current input and output TADs
|
||||
T* current = x + inputTadOffsets[idx];
|
||||
T* currentOut = z + outputTadOffsets[segment];
|
||||
|
||||
// Retrieve start and finish indices for the current segment
|
||||
LongType start = starts[segment];
|
||||
LongType finish = start + lengths[segment];
|
||||
|
||||
// Skip processing if the length for the segment is zero
|
||||
if (lengths[segment] == 0) continue;
|
||||
|
||||
// Iterate over elements within the TAD
|
||||
for (auto e = threadIdx.x; e < len; e += blockDim.x) {
|
||||
// Convert linear index to coordinates for inputTads
|
||||
INDEX2COORDS(e, inputTadRank, inputTadShapePtr, inputCoords);
|
||||
// Convert coordinates back to linear index for inputTads
|
||||
COORDS2INDEX(inputTadRank, inputTadStridePtr, inputCoords, xIndex);
|
||||
|
||||
// Convert linear index to coordinates for outputTads
|
||||
INDEX2COORDS(e, outputTadRank, outputTadShapePtr, outputCoords);
|
||||
// Convert coordinates back to linear index for outputTads
|
||||
COORDS2INDEX(outputTadRank, outputTadStridePtr, outputCoords, zIndex);
|
||||
|
||||
// Perform atomic multiplication on the output buffer
|
||||
math::atomics::sd_atomicMul(¤tOut[zIndex], current[xIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static void segmentProdFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
LongType numClasses = indices->e<LongType>(indices->lengthOf() - 1) + 1;
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
sd::LongType zero = 0;
|
||||
sd::LongType one = 1;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
output->assign(one);
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero);
|
||||
|
||||
fillUpSegments(indices, numClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
dim3 launchDims = segmentDims(indices->lengthOf(),input->lengthOf());
|
||||
segmentProdLinearKernel<T, I><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(input->specialBuffer(), input->specialShapeInfo(), begins,
|
||||
lengths, numClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentProdLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dim3 launchDims = segmentTad(input->lengthOf());
|
||||
segmentProdTadKernel<T, I><<<launchDims.x, launchDims.y, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentProdTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void segmentProdFunctor(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), segmentProdFunctor_, (context, input, indices, output),
|
||||
SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static void unsortedSegmentProdFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
sd::LongType zero = 0;
|
||||
sd::LongType one = 1;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero);
|
||||
dim3 dims = getFillUpSegmentsDims(numOfClasses,indices->lengthOf());
|
||||
fillUpSegments(indices, numOfClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
output->assign(one);
|
||||
|
||||
dim3 launchDims = getLaunchDims("unsorted_segment_prod_2");
|
||||
if (input->isVector()) {
|
||||
unsortedSegmentProdLinearKernel<T, I><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->dataBuffer()->specialAsT<T>(), input->specialShapeInfo(), indices->dataBuffer()->specialAsT<I>(),
|
||||
indices->specialShapeInfo(), begins, lengths, numOfClasses, output->dataBuffer()->specialAsT<T>(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "unsortedSegmentProdLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dims.x = input->sizeAt(0);
|
||||
segmentProdTadKernel<T, I><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numOfClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentProdTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void unsortedSegmentProdFunctor(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), unsortedSegmentProdFunctor_,
|
||||
(context, input, indices, numOfClasses, output), SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentProdBPLinearKernel(void* inputBuf, LongType const* inputShape, void* forwardOutput,
|
||||
LongType const* forwardShape, void* eps, LongType const* epsShape,
|
||||
void* indicesBuf, LongType const* indicesShape, void* outputBuf,
|
||||
LongType const* outputShape) {
|
||||
|
||||
// Shared memory for caching shape, stride, and rank information
|
||||
__shared__ LongType inputRank;
|
||||
__shared__ const LongType* inputShapePtr;
|
||||
__shared__ const LongType* inputStridePtr;
|
||||
|
||||
__shared__ LongType forwardRank;
|
||||
__shared__ const LongType* forwardShapePtr;
|
||||
__shared__ const LongType* forwardStridePtr;
|
||||
|
||||
__shared__ LongType epsRank;
|
||||
__shared__ const LongType* epsShapePtr;
|
||||
__shared__ const LongType* epsStridePtr;
|
||||
|
||||
__shared__ LongType indicesRank;
|
||||
__shared__ const LongType* indicesShapePtr;
|
||||
__shared__ const LongType* indicesStridePtr;
|
||||
|
||||
__shared__ LongType outputRank;
|
||||
__shared__ const LongType* outputShapePtr;
|
||||
__shared__ const LongType* outputStridePtr;
|
||||
|
||||
// Shared memory for pointers and lengths initialized by thread 0
|
||||
__shared__ T* x;
|
||||
__shared__ T* gradIn;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen;
|
||||
__shared__ LongType gradLen;
|
||||
__shared__ LongType currentLen;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache rank, shape, and stride for inputShape
|
||||
inputRank = shape::rank(inputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
|
||||
// Cache rank, shape, and stride for forwardShape
|
||||
forwardRank = shape::rank(forwardShape);
|
||||
forwardShapePtr = shape::shapeOf(forwardShape);
|
||||
forwardStridePtr = shape::stride(forwardShape);
|
||||
|
||||
// Cache rank, shape, and stride for epsShape
|
||||
epsRank = shape::rank(epsShape);
|
||||
epsShapePtr = shape::shapeOf(epsShape);
|
||||
epsStridePtr = shape::stride(epsShape);
|
||||
|
||||
// Cache rank, shape, and stride for indicesShape
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
|
||||
// Cache rank, shape, and stride for outputShape
|
||||
outputRank = shape::rank(outputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
// Initialize pointers and lengths
|
||||
xLen = shape::length(inputShape);
|
||||
gradLen = shape::length(epsShape);
|
||||
currentLen = shape::length(outputShape); // Assuming 'currentLen' corresponds to outputShape
|
||||
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
y = reinterpret_cast<I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
gradIn = reinterpret_cast<T*>(forwardOutput);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Calculate global thread index and step size
|
||||
LongType start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
LongType step = gridDim.x * blockDim.x;
|
||||
|
||||
// Coordinate arrays
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType gradICoords[SD_MAX_RANK];
|
||||
LongType gradOCoords[SD_MAX_RANK];
|
||||
|
||||
// Offset variables
|
||||
LongType xOffset;
|
||||
LongType yOffset;
|
||||
LongType zOffset;
|
||||
LongType gradOffsetI;
|
||||
LongType gradOffsetO;
|
||||
|
||||
for (LongType e = start; e < xLen; e += step) {
|
||||
// Convert linear index to coordinates for inputShape
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, xCoords);
|
||||
// Convert coordinates back to linear index for inputShape
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xOffset);
|
||||
|
||||
// Convert linear index to coordinates for indicesShape
|
||||
INDEX2COORDS(e, indicesRank, indicesShapePtr, yCoords);
|
||||
// Convert coordinates back to linear index for indicesShape
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yOffset);
|
||||
|
||||
// Retrieve the class index from indices
|
||||
auto classIndex = y[yOffset];
|
||||
|
||||
// Convert class index to coordinates for forwardShape
|
||||
INDEX2COORDS(classIndex, forwardRank, forwardShapePtr, gradICoords);
|
||||
// Convert coordinates back to linear index for forwardShape
|
||||
COORDS2INDEX(forwardRank, forwardStridePtr, gradICoords, gradOffsetI);
|
||||
|
||||
// Convert class index to coordinates for epsShape
|
||||
INDEX2COORDS(classIndex, epsRank, epsShapePtr, gradOCoords);
|
||||
// Convert coordinates back to linear index for epsShape
|
||||
COORDS2INDEX(epsRank, epsStridePtr, gradOCoords, gradOffsetO);
|
||||
|
||||
// Convert linear index to coordinates for outputShape
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, zCoords);
|
||||
// Convert coordinates back to linear index for outputShape
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zOffset);
|
||||
|
||||
// Perform the computation: z[zOffset] = gradOut[gradOffsetO] * gradIn[gradOffsetI] / x[xOffset];
|
||||
z[zOffset] = gradOut[gradOffsetO] * gradIn[gradOffsetI] / x[xOffset];
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentProdBPTadKernel(void* inputBuf, LongType const* inputShape, void* forwardOutput,
|
||||
LongType const* forwardShape, void* eps, LongType const* epsShape,
|
||||
void* indicesBuf, LongType const* indicesShape, void* outputBuf,
|
||||
LongType const* outputShape, LongType const* inputTad,
|
||||
LongType const* inputOffsets, LongType const* gradInTad,
|
||||
LongType const* gradInOffsets, LongType const* gradOutTad,
|
||||
LongType const* gradOutOffsets, LongType const* outTad,
|
||||
LongType const* outOffsets) {
|
||||
|
||||
// Shared memory for caching shape, stride, and rank information
|
||||
__shared__ LongType inputRank;
|
||||
__shared__ const LongType* inputShapePtr;
|
||||
__shared__ const LongType* inputStridePtr;
|
||||
|
||||
__shared__ LongType forwardRank;
|
||||
__shared__ const LongType* forwardShapePtr;
|
||||
__shared__ const LongType* forwardStridePtr;
|
||||
|
||||
__shared__ LongType epsRank;
|
||||
__shared__ const LongType* epsShapePtr;
|
||||
__shared__ const LongType* epsStridePtr;
|
||||
|
||||
__shared__ LongType indicesRank;
|
||||
__shared__ const LongType* indicesShapePtr;
|
||||
__shared__ const LongType* indicesStridePtr;
|
||||
|
||||
__shared__ LongType outputRank;
|
||||
__shared__ const LongType* outputShapePtr;
|
||||
__shared__ const LongType* outputStridePtr;
|
||||
|
||||
__shared__ LongType inputTadRank;
|
||||
__shared__ const LongType* inputTadShapePtr;
|
||||
__shared__ const LongType* inputTadStridePtr;
|
||||
|
||||
__shared__ LongType gradInTadRank;
|
||||
__shared__ const LongType* gradInTadShapePtr;
|
||||
__shared__ const LongType* gradInTadStridePtr;
|
||||
|
||||
__shared__ LongType gradOutTadRank;
|
||||
__shared__ const LongType* gradOutTadShapePtr;
|
||||
__shared__ const LongType* gradOutTadStridePtr;
|
||||
|
||||
__shared__ LongType outTadRank;
|
||||
__shared__ const LongType* outTadShapePtr;
|
||||
__shared__ const LongType* outTadStridePtr;
|
||||
|
||||
// Shared memory for pointers and lengths initialized by thread 0
|
||||
__shared__ T* x;
|
||||
__shared__ T* gradIn;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen;
|
||||
__shared__ LongType yLen;
|
||||
__shared__ LongType gradLen;
|
||||
__shared__ LongType currentLen;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
// Cache rank, shape, and stride for inputShape
|
||||
inputRank = shape::rank(inputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
|
||||
// Cache rank, shape, and stride for forwardShape
|
||||
forwardRank = shape::rank(forwardShape);
|
||||
forwardShapePtr = shape::shapeOf(forwardShape);
|
||||
forwardStridePtr = shape::stride(forwardShape);
|
||||
|
||||
// Cache rank, shape, and stride for epsShape
|
||||
epsRank = shape::rank(epsShape);
|
||||
epsShapePtr = shape::shapeOf(epsShape);
|
||||
epsStridePtr = shape::stride(epsShape);
|
||||
|
||||
// Cache rank, shape, and stride for indicesShape
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
|
||||
// Cache rank, shape, and stride for outputShape
|
||||
outputRank = shape::rank(outputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
|
||||
// Cache rank, shape, and stride for inputTad
|
||||
inputTadRank = shape::rank(inputTad);
|
||||
inputTadShapePtr = shape::shapeOf(inputTad);
|
||||
inputTadStridePtr = shape::stride(inputTad);
|
||||
|
||||
// Cache rank, shape, and stride for gradInTad
|
||||
gradInTadRank = shape::rank(gradInTad);
|
||||
gradInTadShapePtr = shape::shapeOf(gradInTad);
|
||||
gradInTadStridePtr = shape::stride(gradInTad);
|
||||
|
||||
// Cache rank, shape, and stride for gradOutTad
|
||||
gradOutTadRank = shape::rank(gradOutTad);
|
||||
gradOutTadShapePtr = shape::shapeOf(gradOutTad);
|
||||
gradOutTadStridePtr = shape::stride(gradOutTad);
|
||||
|
||||
// Cache rank, shape, and stride for outTad
|
||||
outTadRank = shape::rank(outTad);
|
||||
outTadShapePtr = shape::shapeOf(outTad);
|
||||
outTadStridePtr = shape::stride(outTad);
|
||||
|
||||
// Initialize pointers and lengths
|
||||
xLen = shape::length(inputShape);
|
||||
yLen = shape::length(indicesShape);
|
||||
gradLen = shape::length(epsShape);
|
||||
currentLen = shape::length(outTad);
|
||||
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
y = reinterpret_cast<I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradIn = reinterpret_cast<T*>(forwardOutput);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Calculate global thread index and step size
|
||||
LongType startIdx = blockIdx.x;
|
||||
LongType step = gridDim.x;
|
||||
|
||||
// Coordinate arrays
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType yIndex;
|
||||
|
||||
// Iterate over all relevant indices
|
||||
for (auto i = startIdx; i < yLen; i += step) {
|
||||
// Convert linear index to coordinates for indicesShape
|
||||
INDEX2COORDS(i, indicesRank, indicesShapePtr, yCoords);
|
||||
// Convert coordinates back to linear index for indicesShape
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yIndex);
|
||||
|
||||
// Retrieve the segment index from indices
|
||||
auto segment = y[yIndex];
|
||||
|
||||
// Pointers to the current input and output TADs
|
||||
T* current = x + inputOffsets[i];
|
||||
T* currentOut = z + outOffsets[i];
|
||||
|
||||
// Pointers to the corresponding gradIn and gradOut TADs
|
||||
T* in = gradIn + gradInOffsets[segment];
|
||||
T* outGrad = gradOut + gradOutOffsets[segment];
|
||||
|
||||
// Perform element-wise computation within the current TAD
|
||||
for (auto e = threadIdx.x; e < currentLen; e += blockDim.x) {
|
||||
// Compute output: currentOut[e] = outGrad[e] * in[e] / current[e];
|
||||
currentOut[e] = outGrad[e] * in[e] / current[e];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
Status segmentProdFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
auto outShape = gradOut->getShapeAsVector();
|
||||
|
||||
NDArray tempRes(gradOut->ordering(), outShape, DataTypeUtils::fromT<T>(),
|
||||
context); //->shapeInfo(), context);
|
||||
segmentProdFunctor_<T, I>(context, input, indices, &tempRes);
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
if (input->isVector()) {
|
||||
LongType loopSize = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf(); // indices->e<sd::LongType>(loop_size - 1);
|
||||
segmentProdBPLinearKernel<T, I><<<gradOut->lengthOf(), loopSize, 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentProdBPLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(),1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradIn = ConstantTadHelper::getInstance().tadForDimensions(tempRes.shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
auto gradInTads = packGradIn->specialShapeInfo();
|
||||
auto gradInTadOffsets = packGradIn->specialOffsets();
|
||||
auto gradOutTads = packGradOut->specialShapeInfo();
|
||||
auto gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
dim3 segmentBpTad2 = segmentBpTad(gradOut->lengthOf(),input->lengthOf());
|
||||
|
||||
segmentProdBPTadKernel<T, I><<<segmentBpTad2.y,segmentBpTad2.x, segmentBpTad2.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), inputTads, inputTadOffsets, gradInTads, gradInTadOffsets,
|
||||
gradOutTads, gradOutTadOffsets, outputTads, outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentProdBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
Status segmentProdFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return segmentProdFunctorBP_,
|
||||
(context, input, indices, gradOut, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static Status unsortedSegmentProdFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices,
|
||||
NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
auto outShape = gradOut->getShapeAsVector();
|
||||
NDArray tempRes(gradOut->ordering(),outShape, DataTypeUtils::fromT<T>(),
|
||||
context);
|
||||
unsortedSegmentProdFunctor_<T, I>(context, input, indices, numOfClasses, &tempRes);
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
if (input->isVector()) {
|
||||
LongType loopSize = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf();
|
||||
dim3 segmentBpTad2 = segmentBpDims(gradOut->lengthOf(),input->lengthOf());
|
||||
segmentProdBPLinearKernel<T, I><<<segmentBpTad2.y, segmentBpTad2.x,segmentBpTad2.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentProdBPLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradIn = ConstantTadHelper::getInstance().tadForDimensions(tempRes.shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
auto gradInTads = packGradIn->specialShapeInfo();
|
||||
auto gradInTadOffsets = packGradIn->specialOffsets();
|
||||
auto gradOutTads = packGradOut->specialShapeInfo();
|
||||
auto gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
dim3 segmentBpTad2 = segmentBpTad(gradOut->lengthOf(),input->lengthOf());
|
||||
segmentProdBPTadKernel<T, I><<<indices->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), tempRes.specialBuffer(), tempRes.specialShapeInfo(),
|
||||
gradOut->specialBuffer(), gradOut->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
output->specialBuffer(), output->specialShapeInfo(), inputTads, inputTadOffsets, gradInTads, gradInTadOffsets,
|
||||
gradOutTads, gradOutTadOffsets, outputTads, outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentProdBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
Status unsortedSegmentProdFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return unsortedSegmentProdFunctorBP_,
|
||||
(context, input, indices, gradOut, numOfClasses, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,404 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/segment.h>
|
||||
#include <ops/declarable/helpers/segment_common.h>
|
||||
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void unsortedSegmentSqrtNLinearKernel(T* input, LongType const* inputShape, I* indices,
|
||||
LongType const* indicesShape, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, T* output,
|
||||
LongType const* outputShape) {
|
||||
__shared__ LongType xLen, zLen;
|
||||
__shared__ sd::LongType inputRank, outputRank, indicesRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
auto step = blockDim.x * gridDim.x;
|
||||
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yIndex;
|
||||
LongType zIndex;
|
||||
LongType xIndex;
|
||||
|
||||
for (auto idx = start; idx < xLen; idx += step) {
|
||||
INDEX2COORDS(idx, indicesRank, indicesShapePtr, yCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yIndex);
|
||||
auto segment = indices[yIndex];
|
||||
INDEX2COORDS(segment, outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zIndex);
|
||||
if (lengths[segment] == 0) continue;
|
||||
INDEX2COORDS(idx, inputRank, inputShapePtr, xCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xIndex);
|
||||
if (xIndex >= xLen) continue;
|
||||
math::atomics::sd_atomicAdd(&output[zIndex], input[xIndex] / math::sd_sqrt<LongType, T>(lengths[segment]));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentSqrtNTadKernel(T* inputBuf, LongType const* inputShape, LongType const* inputTads,
|
||||
LongType const* inputTadOffsets, I* indices, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* outputBuf,
|
||||
LongType const* outputShape, LongType const* outputTads,
|
||||
LongType const* outputTadOffsets, LongType numIndices) {
|
||||
if(blockIdx.x >= numIndices)
|
||||
return;
|
||||
|
||||
__shared__ LongType len, total;
|
||||
__shared__ sd::LongType inputTadRank, outputTadRank;
|
||||
__shared__ const sd::LongType* inputTadShapePtr;
|
||||
__shared__ const sd::LongType* outputTadShapePtr;
|
||||
__shared__ const sd::LongType* inputTadStridePtr;
|
||||
__shared__ const sd::LongType* outputTadStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
total = shape::sizeAt(inputShape, 0);
|
||||
len = shape::length(inputTads);
|
||||
|
||||
inputTadRank = shape::rank(inputTads);
|
||||
outputTadRank = shape::rank(outputTads);
|
||||
|
||||
inputTadShapePtr = shape::shapeOf(inputTads);
|
||||
outputTadShapePtr = shape::shapeOf(outputTads);
|
||||
|
||||
inputTadStridePtr = shape::stride(inputTads);
|
||||
outputTadStridePtr = shape::stride(outputTads);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType xIndex;
|
||||
LongType zIndex;
|
||||
|
||||
for (auto idx = blockIdx.x; idx < total; idx += gridDim.x) {
|
||||
auto segment = indices[idx];
|
||||
auto x = inputBuf + inputTadOffsets[idx];
|
||||
auto z = reinterpret_cast<T*>(outputBuf) + outputTadOffsets[segment];
|
||||
auto start = starts[segment];
|
||||
auto finish = start + lengths[segment];
|
||||
|
||||
for (auto e = threadIdx.x; e < len; e += blockDim.x) {
|
||||
INDEX2COORDS(e, inputTadRank, inputTadShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputTadRank, inputTadStridePtr, inputCoords, xIndex);
|
||||
INDEX2COORDS(e, outputTadRank, outputTadShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputTadRank, outputTadStridePtr, outputCoords, zIndex);
|
||||
math::atomics::sd_atomicAdd(&z[zIndex], x[xIndex] / math::sd_sqrt<LongType, T>(lengths[segment]));
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static void unsortedSegmentSqrtNFunctor_(LaunchContext* context, NDArray* input, NDArray* indices,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
sd::LongType zero = 0;
|
||||
sd::LongType one = 1;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero);
|
||||
dim3 dims= getLaunchDims("segmentSqrtN");
|
||||
fillUpSegments(indices, numOfClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
output->nullify();
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
unsortedSegmentSqrtNLinearKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->dataBuffer()->specialAsT<T>(), input->specialShapeInfo(), indices->dataBuffer()->specialAsT<I>(),
|
||||
indices->specialShapeInfo(), begins, lengths, numOfClasses, output->dataBuffer()->specialAsT<T>(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "unsortedSegmentSqrtNLinearKernel failed");
|
||||
|
||||
} else {
|
||||
output->nullify();
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dims.x = input->sizeAt(0);
|
||||
segmentSqrtNTadKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->dataBuffer()->specialAsT<T>(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
indices->dataBuffer()->specialAsT<I>(), begins, lengths, numOfClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSqrtNTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void unsortedSegmentSqrtNFunctor(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = input->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), unsortedSegmentSqrtNFunctor_,
|
||||
(context, input, indices, numOfClasses, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentSqrtNBPLinearKernel(void* inputBuf, LongType const* inputShape, void* eps,
|
||||
LongType const* epsShape, void* indicesBuf,
|
||||
LongType const* indicesShape, LongType* lengths, void* outputBuf,
|
||||
LongType const* outputShape) {
|
||||
__shared__ T* x;
|
||||
__shared__ T* gradIn;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, gradLen;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank, indicesRank, epsRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* epsShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
__shared__ const sd::LongType* epsStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
y = reinterpret_cast<I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradLen = shape::length(epsShape);
|
||||
|
||||
// Cache all shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
epsRank = shape::rank(epsShape);
|
||||
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
epsShapePtr = shape::shapeOf(epsShape);
|
||||
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
epsStridePtr = shape::stride(epsShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = gridDim.x * blockDim.x;
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
LongType xOffset;
|
||||
LongType yOffset;
|
||||
LongType gradOffsetO;
|
||||
|
||||
for (auto e = start; e < xLen; e += step) {
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zOffset);
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, xCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xOffset);
|
||||
INDEX2COORDS(e, indicesRank, indicesShapePtr, yCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yOffset);
|
||||
auto classIndex = y[yOffset];
|
||||
INDEX2COORDS(classIndex, epsRank, epsShapePtr, zCoords);
|
||||
COORDS2INDEX(epsRank, epsStridePtr, zCoords, gradOffsetO);
|
||||
|
||||
z[zOffset] = T(gradOut[gradOffsetO] / math::sd_sqrt<LongType, float>(lengths[classIndex]));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentSqrtNBPTadKernel(void* inputBuf, LongType const* inputShape, void* eps,
|
||||
LongType const* epsShape, void* indicesBuf, LongType const* indicesShape,
|
||||
LongType* lengths, void* outputBuf, LongType const* outputShape,
|
||||
LongType const* inputTad, LongType const* inputOffsets,
|
||||
LongType const* gradOutTad, LongType const* gradOutOffsets,
|
||||
LongType const* outTad, LongType const* outOffsets) {
|
||||
__shared__ T* x;
|
||||
__shared__ T* gradOut;
|
||||
__shared__ I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, yLen, gradLen, currentLen;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType outTadRank, gradOutTadRank;
|
||||
__shared__ const sd::LongType* outTadShapePtr;
|
||||
__shared__ const sd::LongType* gradOutTadShapePtr;
|
||||
__shared__ const sd::LongType* outTadStridePtr;
|
||||
__shared__ const sd::LongType* gradOutTadStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
x = reinterpret_cast<T*>(inputBuf);
|
||||
y = reinterpret_cast<I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
yLen = shape::length(indicesShape);
|
||||
gradOut = reinterpret_cast<T*>(eps);
|
||||
gradLen = shape::length(epsShape);
|
||||
currentLen = shape::length(outTad);
|
||||
|
||||
// Cache TAD shape information
|
||||
outTadRank = shape::rank(outTad);
|
||||
gradOutTadRank = shape::rank(gradOutTad);
|
||||
outTadShapePtr = shape::shapeOf(outTad);
|
||||
gradOutTadShapePtr = shape::shapeOf(gradOutTad);
|
||||
outTadStridePtr = shape::stride(outTad);
|
||||
gradOutTadStridePtr = shape::stride(gradOutTad);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto i = blockIdx.x; i < yLen; i += gridDim.x) {
|
||||
auto segment = y[i];
|
||||
T* currentOut = z + outOffsets[i];
|
||||
T* outGrad = gradOut + gradOutOffsets[segment];
|
||||
|
||||
for (auto e = threadIdx.x; e < currentLen; e += blockDim.x) {
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType gradCoords[SD_MAX_RANK];
|
||||
LongType zIndex;
|
||||
LongType gradIndex;
|
||||
|
||||
INDEX2COORDS(e, outTadRank, outTadShapePtr, zCoords);
|
||||
COORDS2INDEX(outTadRank, outTadStridePtr, zCoords, zIndex);
|
||||
INDEX2COORDS(e, gradOutTadRank, gradOutTadShapePtr, gradCoords);
|
||||
COORDS2INDEX(gradOutTadRank, gradOutTadStridePtr, gradCoords, gradIndex);
|
||||
|
||||
if (lengths[segment] > 0)
|
||||
currentOut[zIndex] = T(outGrad[gradIndex] / math::sd_sqrt<LongType, float>(lengths[segment]));
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static Status unsortedSegmentSqrtNFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices,
|
||||
NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto numClasses = indices->e<LongType>(indices->lengthOf() - 1) + 1;
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
sd::LongType zero = 0;
|
||||
sd::LongType one = 1;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero);
|
||||
fillUpSegments(indices, numClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf();
|
||||
segmentSqrtNBPLinearKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), lengths, output->specialBuffer(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSqrtNBPLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
auto gradOutTads = packGradOut->specialShapeInfo();
|
||||
auto gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
dim3 segmentBpTad2 = segmentBpTad(indices->lengthOf(),input->lengthOf());
|
||||
|
||||
segmentSqrtNBPTadKernel<T, I><<<segmentBpTad2.y, segmentBpTad2.x, segmentBpTad2.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), lengths, output->specialBuffer(),
|
||||
output->specialShapeInfo(), inputTads, inputTadOffsets, gradOutTads, gradOutTadOffsets, outputTads,
|
||||
outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSqrtNBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
Status unsortedSegmentSqrtNFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return unsortedSegmentSqrtNFunctorBP_,
|
||||
(context, input, indices, gradOut, numOfClasses, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,508 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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 <exceptions/cuda_exception.h>
|
||||
#include <execution/cuda/LaunchDims.h>
|
||||
#include <helpers/ConstantTadHelper.h>
|
||||
#include <helpers/PointersManager.h>
|
||||
#include <helpers/ShapeUtils.h>
|
||||
|
||||
#include <ops/declarable/helpers/segment.h>
|
||||
#include <ops/declarable/helpers/segment_common.h>
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
#include <system/selective_rendering.h>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Segment ops linear kernels
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentSumLinearKernel(const void* input, const LongType* inputShape, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* output,
|
||||
const LongType* outputShape) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType xLen, zLen, segment, zIndex;
|
||||
__shared__ const T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ int threadsPerSegment, start, finish;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
threadsPerSegment = (gridDim.x + numOfClasses - 1) / numOfClasses;
|
||||
segment = blockIdx.x / threadsPerSegment;
|
||||
x = reinterpret_cast<const T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
if (segment < numOfClasses) {
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
INDEX2COORDS(segment, shape::rank(outputShape), shape::shapeOf(outputShape), zCoords);
|
||||
COORDS2INDEX(shape::rank(outputShape), shape::stride(outputShape), zCoords, zIndex);
|
||||
if(zIndex >= zLen)
|
||||
return;
|
||||
start = starts[segment];
|
||||
finish = start + lengths[segment];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
INDEX2COORDS(start, shape::rank(inputShape), shape::shapeOf(inputShape), xCoords);
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(shape::rank(inputShape), shape::stride(inputShape), xCoords, xOffset);
|
||||
z[zIndex] = x[xOffset];
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto e = start + threadIdx.x + 1; e < finish; e += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
INDEX2COORDS(e, shape::rank(inputShape), shape::shapeOf(inputShape), xCoords);
|
||||
LongType xOffset;
|
||||
COORDS2INDEX(shape::rank(inputShape), shape::stride(inputShape), xCoords, xOffset);
|
||||
if (xOffset >= xLen) return;
|
||||
math::atomics::sd_atomicAdd(&z[zIndex], x[xOffset]);
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void unsortedSegmentSumLinearKernel(const void* input, const LongType* inputShape,
|
||||
const void* indices, const LongType* indicesShape, LongType* starts, LongType* lengths,
|
||||
LongType numOfClasses, void* output,
|
||||
const LongType* outputShape) {
|
||||
__shared__ T* val;
|
||||
__shared__ LongType xLen, zLen, segment, zIndex;
|
||||
__shared__ const T* x;
|
||||
__shared__ T* z;
|
||||
__shared__ const I* y;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
segment = blockIdx.x;
|
||||
x = reinterpret_cast<const T*>(input);
|
||||
z = reinterpret_cast<T*>(output);
|
||||
y = reinterpret_cast<const I*>(indices);
|
||||
xLen = shape::length(inputShape);
|
||||
zLen = shape::length(outputShape);
|
||||
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
INDEX2COORDS(segment, shape::rank(outputShape), shape::shapeOf(outputShape), zCoords);
|
||||
COORDS2INDEX(shape::rank(outputShape), shape::stride(outputShape), zCoords, zIndex);
|
||||
if (lengths[segment] > 0) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType xOffset;
|
||||
INDEX2COORDS(starts[segment], shape::rank(inputShape), shape::shapeOf(inputShape), xCoords);
|
||||
COORDS2INDEX(shape::rank(inputShape), shape::stride(inputShape), xCoords, xOffset);
|
||||
z[zIndex] = x[xOffset];
|
||||
} else {
|
||||
z[zIndex] = 0;
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (lengths[segment] > 0) {
|
||||
for (auto e = threadIdx.x; e < xLen; e += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType xIndex;
|
||||
LongType yIndex;
|
||||
|
||||
INDEX2COORDS(e, shape::rank(inputShape), shape::shapeOf(inputShape), xCoords);
|
||||
COORDS2INDEX(shape::rank(inputShape), shape::stride(inputShape), xCoords, xIndex);
|
||||
INDEX2COORDS(e, shape::rank(indicesShape), shape::shapeOf(indicesShape), yCoords);
|
||||
COORDS2INDEX(shape::rank(indicesShape), shape::stride(indicesShape), yCoords, yIndex);
|
||||
|
||||
if (y[yIndex] == segment && e != starts[segment]) {
|
||||
math::atomics::sd_atomicAdd(&z[zIndex], x[xIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// SegmentSum kernel
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentSumTadKernel(void* inputBuf, const LongType* inputShape,
|
||||
const LongType* inputTads, const LongType* inputTadOffsets,
|
||||
const I* indices, LongType* starts,
|
||||
LongType* lengths, LongType numOfClasses, void* outputBuf, const LongType* outputShape,
|
||||
const LongType* outputTads, const LongType* outputTadOffsets, LongType numIndices) {
|
||||
|
||||
__shared__ LongType len, total;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
total = shape::sizeAt(inputShape, 0);
|
||||
len = shape::length(inputTads);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto idx = blockIdx.x; idx < total; idx += gridDim.x) {
|
||||
auto x = reinterpret_cast<T*>(inputBuf) + inputTadOffsets[idx];
|
||||
auto segment = indices[idx];
|
||||
auto z = reinterpret_cast<T*>(outputBuf) + outputTadOffsets[segment];
|
||||
auto start = starts[segment];
|
||||
auto finish = start + lengths[segment];
|
||||
if (lengths[segment] == 0) continue;
|
||||
for (auto e = threadIdx.x; e < len; e += blockDim.x) {
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xIndex;
|
||||
LongType zIndex;
|
||||
|
||||
INDEX2COORDS(e, shape::rank(inputTads), shape::shapeOf(inputTads), xCoords);
|
||||
COORDS2INDEX(shape::rank(inputTads), shape::stride(inputTads), xCoords, xIndex);
|
||||
INDEX2COORDS(e, shape::rank(outputTads), shape::shapeOf(outputTads), zCoords);
|
||||
COORDS2INDEX(shape::rank(outputTads), shape::stride(outputTads), zCoords, zIndex);
|
||||
|
||||
math::atomics::sd_atomicAdd(&z[zIndex], x[xIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template <typename T, typename I>
|
||||
static void segmentSumFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
LongType numClasses = indices->e<LongType>(indices->lengthOf() - 1) + 1;
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numClasses}, context);
|
||||
sd::LongType zero = 0;
|
||||
sd::LongType one = 1;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero);
|
||||
|
||||
fillUpSegments(indices, numClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
segmentSumLinearKernel<T, I><<<numClasses, input->lengthOf(), numClasses * 32 + 32, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), begins, lengths, numClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSumLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dim3 segmentTadDims = segmentTad(input->sizeAt(0));
|
||||
segmentSumTadKernel<T, I><<<segmentTadDims.y,segmentTadDims.x,segmentTadDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSumTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void segmentSumFunctor(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
output->nullify();
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = input->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), segmentSumFunctor_, (context, input, indices, output),
|
||||
SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
static void unsortedSegmentSumFunctor_(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray classesRangesBegs = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
NDArray classesRangesLens = NDArrayFactory::create<LongType>('c', {numOfClasses}, context);
|
||||
sd::LongType zero = 0;
|
||||
sd::LongType one = 1;
|
||||
sd::LongType len = indices->lengthOf();
|
||||
classesRangesBegs.assign(len);
|
||||
classesRangesLens.assign(zero);
|
||||
dim3 dims = getSegmentSumDims(numOfClasses,indices->lengthOf());
|
||||
fillUpSegments(indices, numOfClasses, classesRangesBegs, classesRangesLens);
|
||||
LongType* begins = reinterpret_cast<LongType*>(classesRangesBegs.specialBuffer());
|
||||
LongType* lengths = reinterpret_cast<LongType*>(classesRangesLens.specialBuffer());
|
||||
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
unsortedSegmentSumLinearKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), indices->specialBuffer(), indices->specialShapeInfo(),
|
||||
begins, lengths, numOfClasses, output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "unsortedSegmentSumLinearKernel failed");
|
||||
|
||||
} else {
|
||||
output->assign(zero);
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(),1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
dim3 dims = segmentTad(input->sizeAt(0));
|
||||
segmentSumTadKernel<T, I><<<dims.x, dims.y, dims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), inputTads, inputTadOffsets,
|
||||
reinterpret_cast<I*>(indices->specialBuffer()), begins, lengths, numOfClasses, output->specialBuffer(),
|
||||
output->specialShapeInfo(), outputTads, outputTadOffsets, indices->lengthOf());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSumTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
dimensions = nullptr;
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
void unsortedSegmentSumFunctor(LaunchContext* context, NDArray* input, NDArray* indices, LongType numOfClasses,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices});
|
||||
output->nullify();
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = input ->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), indices->dataType(), unsortedSegmentSumFunctor_,
|
||||
(context, input, indices, numOfClasses, output), SD_NUMERIC_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Backpropagate ops
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
// Sorted sum backpropagate
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentSumBPLinearKernel(const void* inputBuf, const LongType* inputShape, const void* eps,
|
||||
const LongType* epsShape, const void* indicesBuf,
|
||||
const LongType* indicesShape, void* outputBuf,
|
||||
const LongType* outputShape) {
|
||||
__shared__ LongType xLen, gradLen;
|
||||
__shared__ sd::LongType inputRank, outputRank, indicesRank, epsRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* epsShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
__shared__ const sd::LongType* epsStridePtr;
|
||||
|
||||
auto x = reinterpret_cast<const T*>(inputBuf);
|
||||
auto y = reinterpret_cast<const I*>(indicesBuf);
|
||||
auto z = reinterpret_cast<T*>(outputBuf);
|
||||
auto gradOut = reinterpret_cast<const T*>(eps);
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
gradLen = shape::length(epsShape);
|
||||
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
epsRank = shape::rank(epsShape);
|
||||
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
epsShapePtr = shape::shapeOf(epsShape);
|
||||
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
epsStridePtr = shape::stride(epsShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
auto start = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
auto step = gridDim.x * blockDim.x;
|
||||
|
||||
for (auto e = start; e < xLen; e += step) {
|
||||
LongType zCoords[SD_MAX_RANK];
|
||||
LongType xCoords[SD_MAX_RANK];
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType zOffset;
|
||||
LongType xOffset;
|
||||
LongType yOffset;
|
||||
LongType gradOffsetO;
|
||||
|
||||
INDEX2COORDS(e, outputRank, outputShapePtr, zCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, zCoords, zOffset);
|
||||
INDEX2COORDS(e, inputRank, inputShapePtr, xCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, xCoords, xOffset);
|
||||
INDEX2COORDS(e, indicesRank, indicesShapePtr, yCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yOffset);
|
||||
auto classIndex = y[yOffset];
|
||||
INDEX2COORDS(classIndex, epsRank, epsShapePtr, zCoords);
|
||||
COORDS2INDEX(epsRank, epsStridePtr, zCoords, gradOffsetO);
|
||||
|
||||
z[zOffset] = gradOut[gradOffsetO];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static SD_KERNEL void segmentSumBPTadKernel(const void* inputBuf, const LongType* inputShape, const void* eps,
|
||||
const LongType* epsShape, const void* indicesBuf,
|
||||
const LongType* indicesShape, void* outputBuf,
|
||||
const LongType* outputShape, const LongType* inputTad,
|
||||
const LongType* inputOffsets, const LongType* gradOutTad,
|
||||
const LongType* gradOutOffsets, const LongType* outTad,
|
||||
const LongType* outOffsets) {
|
||||
__shared__ const T* x;
|
||||
__shared__ const T* gradOut;
|
||||
__shared__ const I* y;
|
||||
__shared__ T* z;
|
||||
__shared__ LongType xLen, yLen, gradLen, currentLen;
|
||||
__shared__ sd::LongType indicesRank;
|
||||
__shared__ const sd::LongType* indicesShapePtr;
|
||||
__shared__ const sd::LongType* indicesStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
xLen = shape::length(inputShape);
|
||||
x = reinterpret_cast<const T*>(inputBuf);
|
||||
y = reinterpret_cast<const I*>(indicesBuf);
|
||||
z = reinterpret_cast<T*>(outputBuf);
|
||||
yLen = shape::length(indicesShape);
|
||||
gradOut = reinterpret_cast<const T*>(eps);
|
||||
gradLen = shape::length(epsShape);
|
||||
currentLen = shape::length(outTad);
|
||||
|
||||
indicesRank = shape::rank(indicesShape);
|
||||
indicesShapePtr = shape::shapeOf(indicesShape);
|
||||
indicesStridePtr = shape::stride(indicesShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (auto i = blockIdx.x; i < yLen; i += gridDim.x) {
|
||||
LongType yCoords[SD_MAX_RANK];
|
||||
LongType yIndex;
|
||||
INDEX2COORDS(i, indicesRank, indicesShapePtr, yCoords);
|
||||
COORDS2INDEX(indicesRank, indicesStridePtr, yCoords, yIndex);
|
||||
auto segment = y[yIndex];
|
||||
auto currentOut = z + outOffsets[i];
|
||||
auto outGrad = gradOut + gradOutOffsets[segment];
|
||||
|
||||
for (auto e = threadIdx.x; e < currentLen; e += blockDim.x) {
|
||||
currentOut[e] = outGrad[e];
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
template <typename T, typename I>
|
||||
Status segmentSumFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf();
|
||||
segmentSumBPLinearKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSumBPLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
auto gradOutTads = packGradOut->specialShapeInfo();
|
||||
auto gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
|
||||
segmentSumBPTadKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(),
|
||||
inputTads, inputTadOffsets, gradOutTads, gradOutTadOffsets, outputTads, outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSumBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
Status segmentSumFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return segmentSumFunctorBP_,
|
||||
(context, input, indices, gradOut, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
static Status unsortedSegmentSumFunctorBP_(LaunchContext* context, NDArray* input, NDArray* indices,
|
||||
NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
auto stream = context->getCudaStream();
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
if (input->isVector() || input->isScalar()) {
|
||||
LongType loop_size = input->lengthOf();
|
||||
auto numOfClasses = gradOut->lengthOf();
|
||||
segmentSumBPLinearKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo());
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSumBPLinearKernel failed");
|
||||
|
||||
} else {
|
||||
LongType zero = 0;
|
||||
std::vector<LongType> *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,&zero);
|
||||
auto packX = ConstantTadHelper::getInstance().tadForDimensions(input->shapeInfo(), dimensions);
|
||||
auto packZ = ConstantTadHelper::getInstance().tadForDimensions(output->shapeInfo(), dimensions);
|
||||
auto packGradOut = ConstantTadHelper::getInstance().tadForDimensions(gradOut->shapeInfo(), dimensions);
|
||||
auto inputTads = packX->specialShapeInfo();
|
||||
auto inputTadOffsets = packX->specialOffsets();
|
||||
auto outputTads = packZ->specialShapeInfo();
|
||||
auto outputTadOffsets = packZ->specialOffsets();
|
||||
auto gradOutTads = packGradOut->specialShapeInfo();
|
||||
auto gradOutTadOffsets = packGradOut->specialOffsets();
|
||||
|
||||
segmentSumBPTadKernel<T, I><<<gradOut->lengthOf(), input->lengthOf(), 256, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), gradOut->specialBuffer(), gradOut->specialShapeInfo(),
|
||||
indices->specialBuffer(), indices->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(),
|
||||
inputTads, inputTadOffsets, gradOutTads, gradOutTadOffsets, outputTads, outputTadOffsets);
|
||||
sd::DebugHelper::checkErrorCode(stream, "segmentSumBPTadKernel failed");
|
||||
|
||||
delete dimensions;
|
||||
}
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
return Status::OK;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------------------------- //
|
||||
Status unsortedSegmentSumFunctorBP(LaunchContext* context, NDArray* input, NDArray* indices, NDArray* gradOut,
|
||||
LongType numOfClasses, NDArray* output) {
|
||||
NDArray::prepareSpecialUse({output}, {input, indices, gradOut});
|
||||
auto indicesDType = indices->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(output->dataType(), indices->dataType(), return unsortedSegmentSumFunctorBP_,
|
||||
(context, input, indices, gradOut, numOfClasses, output), SD_FLOAT_TYPES, SD_INDEXING_TYPES);
|
||||
NDArray::registerSpecialUse({output}, {input, indices, gradOut});
|
||||
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,101 @@
|
||||
/* ******************************************************************************
|
||||
*
|
||||
*
|
||||
* 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/cuda/LaunchDims.h>
|
||||
#include <ops/declarable/helpers/sequence_mask.h>
|
||||
|
||||
|
||||
#include "helpers/DebugHelper.h"
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace helpers {
|
||||
|
||||
template <typename I, typename B>
|
||||
static SD_KERNEL void sequenceMaskKernel(const void* inputBuf, const LongType* inputShape, void* outputBuf,
|
||||
const LongType* outputShape, int maxIndex) {
|
||||
__shared__ const I* input;
|
||||
__shared__ B* output;
|
||||
__shared__ LongType inputLen, outputLen;
|
||||
|
||||
// Cache shape information
|
||||
__shared__ sd::LongType inputRank, outputRank;
|
||||
__shared__ const sd::LongType* inputShapePtr;
|
||||
__shared__ const sd::LongType* outputShapePtr;
|
||||
__shared__ const sd::LongType* inputStridePtr;
|
||||
__shared__ const sd::LongType* outputStridePtr;
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
input = reinterpret_cast<const I*>(inputBuf);
|
||||
output = reinterpret_cast<B*>(outputBuf);
|
||||
inputLen = shape::length(inputShape);
|
||||
outputLen = shape::length(outputShape);
|
||||
|
||||
// Cache shape information
|
||||
inputRank = shape::rank(inputShape);
|
||||
outputRank = shape::rank(outputShape);
|
||||
inputShapePtr = shape::shapeOf(inputShape);
|
||||
outputShapePtr = shape::shapeOf(outputShape);
|
||||
inputStridePtr = shape::stride(inputShape);
|
||||
outputStridePtr = shape::stride(outputShape);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
LongType inputCoords[SD_MAX_RANK];
|
||||
LongType outputCoords[SD_MAX_RANK];
|
||||
LongType inputOffset;
|
||||
LongType outputOffset;
|
||||
|
||||
for (auto i = blockIdx.x; i < maxIndex; i += gridDim.x)
|
||||
for (auto k = threadIdx.x; k < inputLen; k += blockDim.x) {
|
||||
INDEX2COORDS(k, inputRank, inputShapePtr, inputCoords);
|
||||
COORDS2INDEX(inputRank, inputStridePtr, inputCoords, inputOffset);
|
||||
if (i < input[inputOffset]) {
|
||||
INDEX2COORDS(k * maxIndex + i, outputRank, outputShapePtr, outputCoords);
|
||||
COORDS2INDEX(outputRank, outputStridePtr, outputCoords, outputOffset);
|
||||
output[outputOffset] = B(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename I, typename B>
|
||||
static void sequenceMask_(LaunchContext* context, NDArray* input, NDArray* output, int maxIndex) {
|
||||
dim3 launchDims = getSequenceMaskLaunchDims(maxIndex,*input);
|
||||
NDArray::prepareSpecialUse({output}, {input});
|
||||
auto stream = context->getCudaStream();
|
||||
sequenceMaskKernel<I, B><<<launchDims.y, launchDims.x, launchDims.z, *stream>>>(
|
||||
input->specialBuffer(), input->specialShapeInfo(), output->specialBuffer(), output->specialShapeInfo(), maxIndex);
|
||||
sd::DebugHelper::checkErrorCode(stream, "sequenceMaskKernel failed");
|
||||
|
||||
NDArray::registerSpecialUse({output}, {input});
|
||||
}
|
||||
|
||||
void sequenceMask(LaunchContext* context, NDArray* input, NDArray* output, int maxIndex) {
|
||||
auto inputDType = input->dataType();
|
||||
auto outputDType = output->dataType();
|
||||
BUILD_DOUBLE_SELECTOR(input->dataType(), output->dataType(), sequenceMask_, (context, input, output, maxIndex),
|
||||
SD_INTEGER_TYPES, SD_COMMON_TYPES_EXTENDED);
|
||||
}
|
||||
|
||||
BUILD_DOUBLE_TEMPLATE( void sequenceMask_,
|
||||
(sd::LaunchContext * context, NDArray* input, NDArray* output, int maxIndex), SD_INTEGER_TYPES,
|
||||
SD_COMMON_TYPES_EXTENDED);
|
||||
} // namespace helpers
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user