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

320 lines
13 KiB
Plaintext

/* ******************************************************************************
*
*
* 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