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

627 lines
26 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
******************************************************************************/
//
// implementation of operations for Simple Recurrent Unit: arXiv:1709.02755v2 [cs.CL] 12 Sep 2017
//
// @author Yurii Shyrma, created on 05.12.2017
//
#include <array/NDArrayFactory.h>
#include <helpers/MmulHelper.h>
#include <helpers/PointersManager.h>
#include <ops/declarable/helpers/sru.h>
#include "execution/cuda/LaunchDims.h"
namespace sd {
namespace ops {
namespace helpers {
//////////////////////////////////////////////////////////////////////////
static SD_INLINE NDArray activation(NDArray& arr) {
auto result = NDArray(&arr, false, arr.getContext());
arr.applyTransform(transform::Tanh,&result);
return result;
}
//////////////////////////////////////////////////////////////////////////
static SD_INLINE NDArray sigmoid(NDArray& arr) {
return (const_cast<NDArray&>(arr)).transform(transform::Sigmoid);
}
//////////////////////////////////////////////////////////////////////////
void sruCell(LaunchContext* context, NDArray* x, NDArray* c0, NDArray* w, NDArray* b,
NDArray* h, NDArray* c) {
// x input [bS x inSize], bS - batch size, inSize - number of features
// c0 previous cell state c [bS x inSize], that is at previous time step t-1
// w weights [inSize x 3*inSize]
// b biases [2*inSize]
// h current cell output [bS x inSize], that is at current time step t
// c current cell state [bS x inSize], that is at current time step t
const int inSize = x->sizeAt(1); // inSize - number of features
auto z = mmul(*x, *w); // [bS x 3*inSize]
// forget gate = sigmoid(x*Wf + bf)
NDArray fIn = z({0, 0, inSize, 2 * inSize}) + (*b)({0, inSize});
auto f = sigmoid(fIn);
NDArray rIn = z({0, 0, 2 * inSize, 3 * inSize}) + (*b)({inSize, 2 * inSize});
// reset gate = sigmoid(x*Wr + br)
auto r = sigmoid(rIn);
// ◦ means element-wise product or so called Hadamard product
// current sell state = f◦c0 + (1 - f)◦(x*Wc)
NDArray cAssign = f * (*c0) + (1.f - f) * z({0, 0, 0, inSize});
c->assign(&cAssign);
// *c = f*(*c0 - z({},{0, inSize})) + z({{},{0, inSize}});
// current cell output = r◦activation(c) + (1 - r)◦x
NDArray resultTwo = r * activation(*c) + (1.f - r) * (*x);
h->assign(&resultTwo);
// *h = r * (activation<T>(c) - *x) + *x;
}
//////////////////////////////////////////////////////////////////////////
void sruTimeLoop(LaunchContext* context, NDArray* x, NDArray* c0, NDArray* w, NDArray* b,
NDArray* h, NDArray* c) {
// x input [bS x inSize x time]
// c0 initial cell state (at time step = 0) [bS x inSize],
// w weights, [3*inSize x inSize]
// b biases, [2*inSize]
// h cell outputs [bS x inSize x time]
// c cell states [bS x inSize x time]
auto wT = w->transpose(); // [3*inSize x inSize] -> [inSize x 3*inSize]
const int time = x->sizeAt(2);
NDArray ct_1(*c0);
// loop through time steps
for (int t = 0; t < time; ++t) {
auto xt = (*x)({0, 0, 0, 0, t, t + 1});
auto ht = (*h)({0, 0, 0, 0, t, t + 1});
auto ct = (*c)({0, 0, 0, 0, t, t + 1});
sruCell(context, &xt, &ct_1, &wT, b, &ht, &ct);
ct_1.assign(&ct);
}
delete wT;
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
__global__ static void sruBICuda(const void* vx, const LongType* xShapeInfo, const void* vwi,
const LongType* wiShapeInfo, const void* vb, const LongType* bShapeInfo,
const void* vc0, const LongType* c0ShapeInfo, const void* vmask,
const LongType* maskShapeInfo, void* vht, const LongType* htShapeInfo,
void* vct, const LongType* ctShapeInfo) {
// Inputs:
// x [time, bS, 2*K]
// wi [time, bS, 6*K], wi = mmul(x, weights);
// b [4*K]
// c0 [bS, 2*K]
// mask [bS, 2*K], optional
// Outputs:
// ht [time, bS, 2*K]
// ct [time, bS, 2*K]
// Reinterpret inputs and outputs
const T* x = reinterpret_cast<const T*>(vx);
const T* wi = reinterpret_cast<const T*>(vwi);
const T* b = reinterpret_cast<const T*>(vb);
const T* c0 = reinterpret_cast<const T*>(vc0);
const T* mask = reinterpret_cast<const T*>(vmask);
T* ht = reinterpret_cast<T*>(vht);
T* ct = reinterpret_cast<T*>(vct);
const int rank = 3; // Assuming 3D tensors
// Shared memory for caching shape information and other variables
extern __shared__ unsigned char shmem[];
// Pointers to shared memory segments
LongType* sharedMem = reinterpret_cast<LongType*>(shmem);
// Shared variables
__shared__ LongType shared_time;
__shared__ LongType shared_bS;
__shared__ LongType shared_K;
__shared__ LongType shared_len;
__shared__ LongType shared_totalThreads;
// Cached shape and stride pointers
__shared__ const LongType* shared_xShape;
__shared__ const LongType* shared_wiShape;
__shared__ const LongType* shared_bShape;
__shared__ const LongType* shared_c0Shape;
__shared__ const LongType* shared_maskShape;
__shared__ const LongType* shared_htShape;
__shared__ const LongType* shared_ctShape;
if (threadIdx.x == 0) {
// Cache shape pointers
shared_xShape = shape::shapeOf(xShapeInfo);
shared_wiShape = shape::shapeOf(wiShapeInfo);
shared_bShape = shape::shapeOf(bShapeInfo);
shared_c0Shape = shape::shapeOf(c0ShapeInfo);
shared_maskShape = shape::shapeOf(maskShapeInfo);
shared_htShape = shape::shapeOf(htShapeInfo);
shared_ctShape = shape::shapeOf(ctShapeInfo);
// Cache time, bS, and K
shared_time = shared_xShape[0]; // time
shared_bS = shared_xShape[1]; // batch size (bS)
shared_K = shared_xShape[2] / 2; // Assuming xShapeInfo[2] = 2*K
// Calculate len = 2*K * bS
shared_len = 2 * shared_K * shared_bS;
// Calculate total number of threads across all blocks
shared_totalThreads = gridDim.x * blockDim.x;
}
// Ensure all threads have access to the cached values
__syncthreads();
// Calculate the global thread ID
const LongType tid = blockIdx.x * blockDim.x + threadIdx.x;
// Allocate space in shared memory for coordinates
LongType* coords = sharedMem + threadIdx.x * (rank - 1); // Only last two dimensions {bS, 2*K}
if (tid >= shared_len) return;
// Convert linear index to multi-dimensional coordinates {bS, 2*K}
INDEX2COORDS(tid, rank - 1, shared_xShape, coords); // coords[0] = bS, coords[1] = 2*K
// Calculate necessary offsets
LongType maskOffset = 0, c0Offset = 0, bFOffset = 0, bROffset = 0;
if (vmask != nullptr) {
COORDS2INDEX(rank - 1, shape::stride(maskShapeInfo), coords, maskOffset);
}
COORDS2INDEX(rank - 1, shape::stride(c0ShapeInfo), coords, c0Offset);
COORDS2INDEX(rank - 1, shape::stride(bShapeInfo), coords + 1, bFOffset);
bROffset = bFOffset + 2 * shared_K * shared_bShape[2]; // 2*K*b_stride
// Fetch values
const T maskVal = (vmask != nullptr) ? mask[maskOffset] : static_cast<T>(1);
const T bF = b[bFOffset];
const T bR = b[bROffset];
T c0Val = c0[c0Offset];
// Determine flip condition
const bool flip = coords[1] >= shared_K;
// Initialize coordinates for time iteration
if (flip)
coords[0] = shared_time - 1;
else
coords[0] = 0;
// Calculate offsets for x, ht, ct
LongType xOffset = 0, htOffset = 0, ctOffset = 0;
COORDS2INDEX(rank, shape::stride(xShapeInfo), coords, xOffset);
COORDS2INDEX(rank, shape::stride(htShapeInfo), coords, htOffset);
COORDS2INDEX(rank, shape::stride(ctShapeInfo), coords, ctOffset);
// Adjust coords for wi and gradWi
coords[1] *= 3; // 6*K corresponds to 3 * 2*K
// Calculate wi offsets
LongType wiOffset0 = 0, wiOffset1 = 0, wiOffset2 = 0;
COORDS2INDEX(rank, shape::stride(wiShapeInfo), coords, wiOffset0);
wiOffset1 = wiOffset0 + shared_wiShape[rank]; // Add stride for wi1
wiOffset2 = wiOffset1 + shared_wiShape[rank]; // Add stride for wi2
// Iterate over the time steps
for (LongType t = 0; t < shared_time; ++t) {
// Evaluate sigmoids
T ft = static_cast<T>(1) / (static_cast<T>(1) + math::sd_exp<T, T>(- (wi[wiOffset1] + bF)));
T rt = static_cast<T>(1) / (static_cast<T>(1) + math::sd_exp<T, T>(- (wi[wiOffset2] + bR)));
// Update c0Val and ct
c0Val = (c0Val - wi[wiOffset0]) * ft + wi[wiOffset0];
ct[ctOffset] = c0Val;
// Compute tanh activation
T val = math::sd_tanh<T, T>(c0Val);
// Fetch x value
T xVal = x[xOffset];
// Compute ht
ht[htOffset] = (val * maskVal - xVal) * rt + xVal;
// Update offsets based on flip condition
if (flip) {
xOffset -= shape::stride(xShapeInfo)[0]; // time step stride
htOffset -= shape::stride(htShapeInfo)[0];
ctOffset -= shape::stride(ctShapeInfo)[0];
wiOffset0 -= shape::stride(wiShapeInfo)[0];
wiOffset1 -= shape::stride(wiShapeInfo)[0];
wiOffset2 -= shape::stride(wiShapeInfo)[0];
} else {
xOffset += shape::stride(xShapeInfo)[0]; // time step stride
htOffset += shape::stride(htShapeInfo)[0];
ctOffset += shape::stride(ctShapeInfo)[0];
wiOffset0 += shape::stride(wiShapeInfo)[0];
wiOffset1 += shape::stride(wiShapeInfo)[0];
wiOffset2 += shape::stride(wiShapeInfo)[0];
}
}
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
static void sruBICudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo,
const void* vwi,
const LongType* wiShapeInfo, const void* vb, const LongType* bShapeInfo, const void* vc0,
const LongType* c0ShapeInfo,
const void* vmask, const LongType* maskShapeInfo, void* vht,
const LongType* htShapeInfo, void* vct, const LongType* ctShapeInfo) {
sruBICuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vwi, wiShapeInfo, vb, bShapeInfo,
vc0, c0ShapeInfo, vmask, maskShapeInfo, vht,
htShapeInfo, vct, ctShapeInfo);
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "sruBICuda failed");
}
//////////////////////////////////////////////////////////////////////////
void sruBI(LaunchContext* context, NDArray* x, NDArray* w, NDArray* b, NDArray* c0,
NDArray* mask, NDArray* ht, NDArray* ct) {
// x = x * mask
std::vector<LongType> dims = {1,2};
if (mask) x->applyBroadcast(broadcast::Multiply, &dims, mask, x); // apply mask
// U = x * w
NDArray wi = mmul(*x, *w); // U [time x bS x 6*K]
PointersManager manager(context, "sru_bi");
dim3 sruBiDims2 = sruBiDims(x->sizeAt(1) * x->sizeAt(2),x->rankOf());
NDArray::prepareSpecialUse({ht, ct}, {x, &wi, b, c0, mask});
BUILD_SINGLE_SELECTOR(
x->dataType(), sruBICudaLauncher,
(sruBiDims2.y,sruBiDims2.x, sruBiDims2.z, context->getCudaStream(), x->specialBuffer(), x->specialShapeInfo(),
wi.specialBuffer(), wi.specialShapeInfo(), b->specialBuffer(), b->specialShapeInfo(), c0->specialBuffer(),
c0->specialShapeInfo(), mask ? mask->specialBuffer() : nullptr, mask ? mask->specialShapeInfo() : nullptr,
ht->specialBuffer(), ht->specialShapeInfo(), ct->specialBuffer(), ct->specialShapeInfo()),
SD_FLOAT_TYPES);
NDArray::registerSpecialUse({ht, ct}, {x, &wi, b, c0, mask});
manager.synchronize();
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
__global__ static void sruBIBPCuda(const void* vx, const LongType* xShapeInfo, const void* vwi,
const LongType* wiShapeInfo, const void* vb, const LongType* bShapeInfo,
const void* vc0, const LongType* c0ShapeInfo, const void* vmask,
const LongType* maskShapeInfo, const void* vct, const LongType* ctShapeInfo,
const void* vgradHt, const LongType* gradHtShapeInfo, const void* vgradCt,
const LongType* gradCtShapeInfo, void* vgradI, const LongType* gradIShapeInfo,
void* vgradWi, const LongType* gradWiShapeInfo, void* vgradB,
const LongType* gradBShapeInfo, void* vgradC0, const LongType* gradC0ShapeInfo) {
// Inputs:
// x [time, bS, 2*K]
// wi [time, bS, 6*K], wi = mmul(x, weights);
// b [4*K]
// c0 [bS, 2*K]
// mask [bS, 2*K], optional
// ct [time, bS, 2*K]
// gradHt [time, bS, 2*K]
// gradCt [bS, 2*K]
// Outputs:
// gradI [time, bS, 2*K]
// gradWi [time, 2*K, 6*K]
// gradB [bS, 4*K]
// gradC0 [bS, 2*K]
// Reinterpret inputs and outputs
const T* x = reinterpret_cast<const T*>(vx);
const T* wi = reinterpret_cast<const T*>(vwi);
const T* b = reinterpret_cast<const T*>(vb);
const T* c0 = reinterpret_cast<const T*>(vc0);
const T* mask = reinterpret_cast<const T*>(vmask);
const T* ct = reinterpret_cast<const T*>(vct);
const T* gradHt = reinterpret_cast<const T*>(vgradHt);
const T* gradCt = reinterpret_cast<const T*>(vgradCt);
T* gradI = reinterpret_cast<T*>(vgradI);
T* gradWi = reinterpret_cast<T*>(vgradWi);
T* gradB = reinterpret_cast<T*>(vgradB);
T* gradC0 = reinterpret_cast<T*>(vgradC0);
const int rank = 3; // Assuming 3D tensors
// Shared memory for caching shape information
extern __shared__ unsigned char shmem[];
LongType* sharedMem = reinterpret_cast<LongType*>(shmem);
__shared__ LongType shared_time;
__shared__ LongType shared_K;
__shared__ LongType shared_len;
__shared__ LongType shared_totalThreads;
// Cached shape pointers
__shared__ const LongType* shared_xShape;
__shared__ const LongType* shared_wiShape;
__shared__ const LongType* shared_bShape;
__shared__ const LongType* shared_c0Shape;
__shared__ const LongType* shared_maskShape;
__shared__ const LongType* shared_ctShape;
__shared__ const LongType* shared_gradHtShape;
__shared__ const LongType* shared_gradCtShape;
__shared__ const LongType* shared_gradIShape;
__shared__ const LongType* shared_gradWiShape;
__shared__ const LongType* shared_gradBShape;
__shared__ const LongType* shared_gradC0Shape;
if (threadIdx.x == 0) {
// Cache ranks, shapes, and strides
shared_xShape = shape::shapeOf(xShapeInfo);
shared_wiShape = shape::shapeOf(wiShapeInfo);
shared_bShape = shape::shapeOf(bShapeInfo);
shared_c0Shape = shape::shapeOf(c0ShapeInfo);
shared_maskShape = shape::shapeOf(maskShapeInfo);
shared_ctShape = shape::shapeOf(ctShapeInfo);
shared_gradHtShape = shape::shapeOf(gradHtShapeInfo);
shared_gradCtShape = shape::shapeOf(gradCtShapeInfo);
shared_gradIShape = shape::shapeOf(gradIShapeInfo);
shared_gradWiShape = shape::shapeOf(gradWiShapeInfo);
shared_gradBShape = shape::shapeOf(gradBShapeInfo);
shared_gradC0Shape = shape::shapeOf(gradC0ShapeInfo);
// Cache time and K
shared_time = shared_xShape[0];
shared_K = shared_xShape[2] / 2; // Assuming xShapeInfo[2] = 2*K
// Calculate len = 2*K * bS
LongType bS = shared_xShape[1];
shared_len = 2 * shared_K * bS;
// Total threads across all blocks
shared_totalThreads = gridDim.x * blockDim.x;
}
// Ensure all threads have access to the cached values
__syncthreads();
const LongType tid = blockIdx.x * blockDim.x + threadIdx.x;
// Allocate space in shared memory for coordinates
LongType* coords = sharedMem + threadIdx.x * rank;
if (tid >= shared_len) return;
// Convert linear index to coordinates {bS, 2*K}
INDEX2COORDS(tid, rank - 1, shared_xShape, coords + 1); // Skipping the time dimension
// Calculate necessary offsets
LongType maskOffset = 0, c0Offset = 0, gradCtOffset = 0, gradC0Offset = 0;
LongType bFOffset = 0, bROffset = 0, gradBFOffset = 0, gradBROffset = 0;
if (vmask != nullptr) {
COORDS2INDEX(rank - 1, shape::stride(maskShapeInfo), coords + 1, maskOffset);
}
COORDS2INDEX(rank - 1, shape::stride(c0ShapeInfo), coords + 1, c0Offset);
COORDS2INDEX(rank - 1, shape::stride(gradCtShapeInfo), coords + 1, gradCtOffset);
COORDS2INDEX(rank - 1, shape::stride(gradC0ShapeInfo), coords + 1, gradC0Offset);
COORDS2INDEX(rank - 1, shape::stride(bShapeInfo), coords + 2, bFOffset);
bROffset = bFOffset + 2 * shared_K * shared_bShape[2]; // 2*K*b_stride
gradBFOffset = coords[1] * shared_gradBShape[3] / 2 + coords[2] * shared_gradBShape[4];
gradBROffset = gradBFOffset + shared_gradBShape[3];
const bool flip = coords[2] >= shared_K;
if (flip)
coords[0] = 0;
else
coords[0] = shared_time - 1;
// Calculate offsets for x, ct, gradI, gradHt
LongType xOffset = 0, ctOffset = 0, gradIOffset = 0, gradHtOffset = 0;
COORDS2INDEX(rank, shape::stride(xShapeInfo), coords, xOffset);
COORDS2INDEX(rank, shape::stride(ctShapeInfo), coords, ctOffset);
COORDS2INDEX(rank, shape::stride(gradIShapeInfo), coords, gradIOffset);
COORDS2INDEX(rank, shape::stride(gradHtShapeInfo), coords, gradHtOffset);
// Adjust coords for wi and gradWi
coords[2] *= 3;
LongType gradWiOffset0 = 0, gradWiOffset1 = 0, gradWiOffset2 = 0;
LongType wiOffset0 = 0, wiOffset1 = 0, wiOffset2 = 0;
COORDS2INDEX(rank, shape::stride(gradWiShapeInfo), coords, gradWiOffset0);
gradWiOffset1 = gradWiOffset0 + shared_gradWiShape[rank + 3]; // add last stride
gradWiOffset2 = gradWiOffset1 + shared_gradWiShape[rank + 3]; // add last stride
COORDS2INDEX(rank, shape::stride(wiShapeInfo), coords, wiOffset0);
wiOffset1 = wiOffset0 + shared_wiShape[rank + 3]; // add last stride
wiOffset2 = wiOffset1 + shared_wiShape[rank + 3]; // add last stride
// Fetch values
const T xVal = x[xOffset];
const T maskVal = (vmask != nullptr) ? mask[maskOffset] : static_cast<T>(1);
const T c0Val = c0[c0Offset];
const T bF = b[bFOffset];
const T bR = b[bROffset];
T gradCtVal = gradCt[gradCtOffset];
T gbF = static_cast<T>(0);
T gbR = static_cast<T>(0);
// Iterate over the time steps
for (LongType t = 0; t < shared_time; ++t) {
// Evaluate sigmoids
T ft = static_cast<T>(1) / (static_cast<T>(1) + math::sd_exp<T, T>(- (wi[wiOffset1] + bF)));
T rt = static_cast<T>(1) / (static_cast<T>(1) + math::sd_exp<T, T>(- (wi[wiOffset2] + bR)));
T val = math::sd_tanh<T, T>(ct[ctOffset]);
T prevVal;
if (t < shared_time - 1)
prevVal = ct[ctOffset += (flip ? shared_ctShape[rank + 1] : -shared_ctShape[rank + 1])];
else
prevVal = c0Val;
// Gradient with respect to input
gradI[gradIOffset] = gradHt[gradHtOffset] - gradHt[gradHtOffset] * rt;
// Gradient with respect to rt, wiR, and bR
T grt = gradHt[gradHtOffset] * (val * maskVal - x[xOffset]) * (rt - rt * rt);
gradWi[gradWiOffset2] = grt;
gbR += grt;
// Gradient with respect to state
T gradC0Val = gradHt[gradHtOffset] * maskVal * (rt - rt * val * val) + gradCtVal;
// Gradient with respect to wi0
gradWi[gradWiOffset0] = gradC0Val - gradC0Val * ft;
// Gradient with respect to ft, wi1, and bF
T gft = gradC0Val * (prevVal - wi[wiOffset0]) * (ft - ft * ft);
gradWi[gradWiOffset1] = gft;
gbF += gft;
// Gradient with respect to c_previous
gradCtVal = gradC0Val * ft;
// Update offsets based on flip
if (flip) {
xOffset += shared_xShape[rank + 1]; // first stride, corresponds to time step
gradHtOffset += shared_gradHtShape[rank + 1];
gradIOffset += shared_gradIShape[rank + 1];
wiOffset0 += shared_wiShape[rank + 1];
wiOffset1 += shared_wiShape[rank + 1];
wiOffset2 += shared_wiShape[rank + 1];
gradWiOffset0 += shared_gradWiShape[rank + 1];
gradWiOffset1 += shared_gradWiShape[rank + 1];
gradWiOffset2 += shared_gradWiShape[rank + 1];
}
else {
xOffset -= shared_xShape[rank + 1]; // first stride, corresponds to time step
gradHtOffset -= shared_gradHtShape[rank + 1];
gradIOffset -= shared_gradIShape[rank + 1];
wiOffset0 -= shared_wiShape[rank + 1];
wiOffset1 -= shared_wiShape[rank + 1];
wiOffset2 -= shared_wiShape[rank + 1];
gradWiOffset0 -= shared_gradWiShape[rank + 1];
gradWiOffset1 -= shared_gradWiShape[rank + 1];
gradWiOffset2 -= shared_gradWiShape[rank + 1];
}
}
// Write accumulated gradients to output
gradB[gradBFOffset] = gbF;
gradB[gradBROffset] = gbR;
gradC0[gradC0Offset] = gradCtVal;
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
static void sruBIBPCudaLauncher(
const int blocksPerGrid, const int threadsPerBlock, const int sharedMem, const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo, const void* vwi,
const LongType* wiShapeInfo, const void* vb, const LongType* bShapeInfo, const void* vc0, const LongType* c0ShapeInfo, const void* vmask,
const LongType* maskShapeInfo, const void* vct, const LongType* ctShapeInfo, const void* vgradHt, const LongType* gradHtShapeInfo, const void* vgradCt,
const LongType* gradCtShapeInfo, void* vgradI, const LongType* gradIShapeInfo, void* vgradWi, const LongType* gradWiShapeInfo, void* vgradB,
const LongType* gradBShapeInfo, void* vgradC0, const LongType* gradC0ShapeInfo) {
sruBIBPCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(
vx, xShapeInfo, vwi, wiShapeInfo, vb, bShapeInfo, vc0, c0ShapeInfo, vmask, maskShapeInfo, vct, ctShapeInfo,
vgradHt, gradHtShapeInfo, vgradCt, gradCtShapeInfo, vgradI, gradIShapeInfo, vgradWi, gradWiShapeInfo, vgradB,
gradBShapeInfo, vgradC0, gradC0ShapeInfo);
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "sruBIBPCuda failed");
}
BUILD_SINGLE_TEMPLATE( void sruBIBPCudaLauncher,
(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, const void* vx, const sd::LongType* xShapeInfo, const void* vwi,
const sd::LongType* wiShapeInfo, const void* vb, const sd::LongType* bShapeInfo, const void* vc0,
const sd::LongType* c0ShapeInfo, const void* vmask, const sd::LongType* maskShapeInfo,
const void* vct, const sd::LongType* ctShapeInfo, const void* vgradHt,
const sd::LongType* gradHtShapeInfo, const void* vgradCt, const sd::LongType* gradCtShapeInfo,
void* vgradI, const sd::LongType* gradIShapeInfo, void* vgradWi,
const sd::LongType* gradWiShapeInfo, void* vgradB, const sd::LongType* gradBShapeInfo,
void* vgradC0, const sd::LongType* gradC0ShapeInfo),
SD_FLOAT_TYPES);
//////////////////////////////////////////////////////////////////////////
void sruBIBP(LaunchContext* context, NDArray* x, NDArray* w, NDArray* b, NDArray* c0,
NDArray* ct, NDArray* gradCt, NDArray* gradHt, NDArray* mask, NDArray* gradI,
NDArray* gradW, NDArray* gradB, NDArray* gradC0) {
// x = x * mask
std::vector<LongType> dims = {1, 2};
if (mask) x->applyBroadcast(broadcast::Multiply, &dims, mask, x); // apply mask
// U = x * w
NDArray wi = mmul(*x, *w); // U [time x bS x 6*K]
const int time = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int K = x->sizeAt(2) / 2;
std::vector<sd::LongType> gradBiasShape = {bS, 4 * K};
std::vector<sd::LongType> gradWiShape = {time, bS, 6 * K};
NDArray gradBias(x->ordering(), gradBiasShape, x->dataType(), context);
NDArray gradWi(x->ordering(), gradWiShape, x->dataType(), context);
PointersManager manager(context, "sru_bi_bp");
const int threadsPerBlock = SD_MAX_NUM_THREADS / 4;
const int blocksPerGrid = (x->sizeAt(1) * x->sizeAt(2) + threadsPerBlock - 1) /
threadsPerBlock; // loop through last two dimensions of x array -> bS, 2*K
const int sharedMem = threadsPerBlock * sizeof(LongType) * x->rankOf() + 128;
dim3 sruBiBpDims = sruBiDims(x->sizeAt(1) + x->sizeAt(2),x->rankOf());
NDArray::prepareSpecialUse({gradI, &gradWi, &gradBias, gradC0}, {x, &wi, b, c0, ct, gradCt, gradHt, mask});
BUILD_SINGLE_SELECTOR(
x->dataType(), sruBIBPCudaLauncher,
(sruBiBpDims.y, sruBiBpDims.x,sruBiBpDims.z, context->getCudaStream(), x->specialBuffer(), x->specialShapeInfo(),
wi.specialBuffer(), wi.specialShapeInfo(), b->specialBuffer(), b->specialShapeInfo(), c0->specialBuffer(),
c0->specialShapeInfo(), mask ? mask->specialBuffer() : nullptr, mask ? mask->specialShapeInfo() : nullptr,
ct->specialBuffer(), ct->specialShapeInfo(), gradHt->specialBuffer(), gradHt->specialShapeInfo(),
gradCt->specialBuffer(), gradCt->specialShapeInfo(), gradI->specialBuffer(), gradI->specialShapeInfo(),
gradWi.specialBuffer(), gradWi.specialShapeInfo(), gradBias.specialBuffer(), gradBias.specialShapeInfo(),
gradC0->specialBuffer(), gradC0->specialShapeInfo()),
SD_FLOAT_TYPES);
NDArray::registerSpecialUse({gradI, &gradWi, &gradBias, gradC0}, {x, &wi, b, c0, ct, gradCt, gradHt, mask});
manager.synchronize();
std::vector<LongType> dims2 = {0};
// gradB
gradBias.reduceAlongDimension(reduce::Sum, gradB, &dims2); // [4*K]
// gradW
x->permutei({0, 2, 1}, false, false); // [time, bS, 2*K] -> [time, 2*K, bS]
MmulHelper::mmul(x, &gradWi, gradW, 1., 0.); // [time, 2*K, bS] x [time, bS , 6*K] = [time, 2*K, 6*K]
}
} // namespace helpers
} // namespace ops
} // namespace sd