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

323 lines
12 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 (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