chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:47:05 +08:00
commit 4f3b7da785
7394 changed files with 2005594 additions and 0 deletions
@@ -0,0 +1,669 @@
/*
* ******************************************************************************
* *
* *
* * 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 15.02.2018, Alex Black
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_gru)
// implementation of gated Recurrent Unit cell
// (cf. https://arxiv.org/abs/1406.1078).
// Kyunghyun Cho, Bart van Merrienboer, Caglar Gulcehre, Dzmitry Bahdanau, Fethi Bougares, Holger Schwenk, Yoshua Bengio
// "Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation"
#include <helpers/MmulHelper.h>
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/gru.h>
#include <ops/declarable/helpers/transforms.h>
namespace sd {
namespace ops {
namespace helpers {
//////////////////////////////////////////////////////////////////////////
void gruCell(sd::LaunchContext* context, NDArray* x, NDArray* hI, NDArray* W, NDArray* Wc,
NDArray* b, NDArray* bc, NDArray* r, NDArray* u, NDArray* c, NDArray* h) {
// Inputs:
// x input [bS, nIn], nIn - input size
// hI previous cell output [bS, nOut], that is at previous time step t-1, nOut - number of units
// W RU weights - [nIn+nOut, 2*nOut] - reset and update gates
// Wc C weights - [nIn+nOut, nOut] - cell gate
// b r and u biases, [2*nOut] - reset and update gates
// bc c biases, [nOut] - cell gate
// Outputs:
// r Reset gate output [bS, nOut]
// u Update gate output [bS, nOut]
// c Cell gate output [bS, nOut]
// h current cell output [bS, nOut]
/***************************************************************************************/
/************************ THIS IS NOT OPTIMIZED CODE ***********************************/
/** however it is more math-friendly and convenient for backprop formulas derivation) **/
const int bS = x->sizeAt(0);
const int nIn = x->sizeAt(1);
const int nOut = hI->sizeAt(1);
NDArray *Wrx = (*W)({0, nIn, 0, nOut}); // [nIn, nOut]
NDArray *Wux = (*W)({0, nIn, nOut, 2 * nOut}); // [nIn, nOut]
NDArray *Wrh = (*W)({nIn, nIn + nOut, 0, nOut}); // [nOut, nOut]
NDArray *Wuh = (*W)({nIn, nIn + nOut, nOut, 2 * nOut}); // [nOut, nOut]
NDArray *Wcx = (*Wc)({0, nIn, 0, 0}); // reset cell weights [nIn, nOut]
NDArray *Wch = (*Wc)({nIn, nIn + nOut, 0, 0}); // updates cell weights [nOut, nOut]
NDArray *br = (*b)({0, nOut}); // [nOut]
NDArray *bu = (*b)({nOut, 2 * nOut}); // [nOut]
// × means matrix multiplication
// * means element-wise product or so called Hadamard product
// r = sigmoid(x × Wrx + hI × Wrh + br)
auto xWrx = mmul(*x, *Wrx);
auto hIWrh = mmul(*hI, *Wrh);
auto* sum1 = *xWrx + *hIWrh;
auto* rAssign = (*sum1) + (*br);
delete sum1;
delete hIWrh;
r->assign(rAssign); // [bS, nIn] × [nIn, nOut] + [bS, nOut] × [nOut, nOut] + [nOut] = [bS, nOut]
delete rAssign;
r->applyTransform(transform::Sigmoid, r);
// u = sigmoid(x × Wux + hI × Wuh + bu)
auto xWux = mmul(*x, *Wux);
auto hIWuh = mmul(*hI, *Wuh);
auto* sum2 = *xWux + *hIWuh;
auto* uAssign = (*sum2) + (*bu);
delete sum2;
delete xWux;
u->assign(uAssign); // [bS, nIn] × [nIn, nOut] + [bS, nOut] × [nOut, nOut] + [nOut] = [bS, nOut]
delete uAssign;
u->applyTransform(transform::Sigmoid, u);
// c = tanh(x × Wcx + (r * hI) × Wch + bc)
auto* rTimesHi = (*r) * (*hI);
auto xWcx = mmul(*x, *Wcx);
auto rTimesHiWch = mmul(*rTimesHi, *Wch);
delete rTimesHi;
auto* sum3 = *xWcx + *rTimesHiWch;
auto* cAssign = (*sum3) + (*bc);
delete sum3;
delete xWcx;
delete rTimesHiWch;
c->assign(cAssign); // [bS, nIn] × [nIn, nOut] + [bS, nOut] × [nOut, nOut] + [nOut] = [bS, nOut]
delete cAssign;
c->applyTransform(transform::Tanh, c);
// h = (1 - u) * c + u * hI
auto* uTimesHi = (*u) * (*hI);
auto* oneMinusU = 1.f - (*u);
auto* oneMinusUTimesC = (*oneMinusU) * (*c);
delete oneMinusU;
auto* hAssign = (*uTimesHi) + (*oneMinusUTimesC);
delete uTimesHi;
delete oneMinusUTimesC;
h->assign(hAssign);
delete hAssign;
delete Wrx;
delete Wux;
delete Wrh;
delete Wuh;
delete Wcx;
delete Wch;
delete br;
delete bu;
}
//////////////////////////////////////////////////////////////////////////
void gruCell(NDArray* x, NDArray* hI, NDArray* Wx, NDArray* Wh, NDArray* b,
NDArray* gates, NDArray* h, bool linearBeforeReset) {
if(linearBeforeReset) {
THROW_EXCEPTION("GRU: Linear before reset not implemented. Please set to false.");
}
// Inputs:
// x input [bS, nIn]
// hI previous cell output [bS, nOut], that is at previous time step t-1
// Wx weights for x - [nIn, 3*nOut]
// Wh weights for h - [nOut, 3*nOut]
// b biases [3*nOut]
// 3*nOut means following sequence: reset, update, cell
// Outputs:
// gates [bS, 3*nOut] = reset gate [bS, nOut] + update gate [bS, nOut] + cell gate [bS, nOut]
// h current cell output [bS, nOut]
// formulas:
// zr = x × Wxr + hI × Whr + br
// zu = x × Wxu + hI × Whu + bu
// r = sigmoid(zr)
// u = sigmoid(zu)
// zc = x × Wxc + (r * hI) × Whc + bc
// c = tanh(zc)
// h = (1-u)*c + u*hI
const int bS = x->sizeAt(0);
const int nIn = x->sizeAt(1);
const int nOut = hI->sizeAt(1);
NDArray *gatesULike = gates->ulike();
NDArray temp = *gatesULike;
MmulHelper::mmul(x, Wx, &temp); // [bS, nIn] × [nIn, 3*nOut] = [bS, 3*nOut]
temp += *b;
MmulHelper::mmul(hI, Wh, gates); // [bS, nOut] × [nOut, 3*nOut] = [bS, 3*nOut]
NDArray *ru = (*gates)({0, 0, 0, 2 * nOut}); // [bS, 2*nOut]
NDArray *r = (*gates)({0, 0, 0, nOut}); // [bS, nOut]
NDArray *u = (*gates)({0, 0, nOut, 2 * nOut}); // [bS, nOut]
NDArray *c = (*gates)({0, 0, 2 * nOut, 3 * nOut}); // [bS, nOut]
NDArray *tempView1 = temp({0, 0, 0, 2 * nOut});
NDArray *tempView2 = temp({0, 0, 2 * nOut, 3 * nOut});
// reset and update gates
*ru += *tempView1;
ru->applyTransform(transform::Sigmoid, ru);
// cell gate
auto* cTimesR = (*c) * (*r);
auto* cAssign = (*cTimesR) + (*tempView2);
delete cTimesR;
c->assign(cAssign);
delete cAssign;
c->applyTransform(transform::Tanh, c);
// h = (1-u)*c + u*hI
auto* uTimesHi = (*u) * (*hI);
auto* oneMinusU = 1.f - (*u);
auto* oneMinusUTimesC = (*oneMinusU) * (*c);
delete oneMinusU;
auto* hAssign = (*uTimesHi) + (*oneMinusUTimesC);
delete uTimesHi;
delete oneMinusUTimesC;
h->assign(hAssign);
delete hAssign;
delete gatesULike;
delete ru;
delete r;
delete u;
delete c;
delete tempView1;
delete tempView2;
}
//////////////////////////////////////////////////////////////////////////
void gruTimeLoop(sd::LaunchContext* context, NDArray* x, NDArray* hI, NDArray* Wx, NDArray* Wh,
NDArray* b, NDArray* h, bool linearBeforeReset) {
// sL means time steps
// x input [sL, bS, nIn]
// hI initial cell output (at time step = 0) [bS, nOut]
// Wx input-to-hidden weights, [nIn, 3*nOut]
// Wh hidden-to-hidden weights, [nOut, 3*nOut]
// b biases, [3*nOut]
// h cell outputs at each time step [sL, bS, nOut]
const int sL = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int nOut = hI->sizeAt(1);
std::vector<LongType> shape = {bS, 3 * nOut};
NDArray gates(h->ordering(), shape, h->dataType(), context);
auto xSet = x->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nIn]
auto hSet = h->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nOut]
// time loop
for (int t = 0; t < sL; ++t) {
gruCell(xSet.at(t), t == 0 ? hI : hSet.at(t - 1), Wx, Wh, b, &gates, hSet.at(t), linearBeforeReset);
}
}
//////////////////////////////////////////////////////////////////////////
void gruCellBp(sd::LaunchContext* context, NDArray* x, NDArray* hLast, NDArray* W, NDArray* Wc,
NDArray* b, NDArray* bc, NDArray* dLdr, NDArray* dLdu, NDArray* dLdc,
NDArray* dLdh, NDArray* dLdx, NDArray* dLdhLast, NDArray* dLdW, NDArray* dLdWc, NDArray* dLdb,
NDArray* dLdbc) {
// Inputs:
// x input [bS, iS]
// hLast previous cell output [bS, nU], that is at previous time step t-1
// W weights - [iS+nU, 2*nU] - reset and update gates
// Wc C weights - [iS+nU, nU] - cell gate
// b r and u biases, [2*nU] - reset and update gates
// bc c biases, [nU] - cell gate
// dLdr gradient wrt reset gate, [bS, nU]
// dLdu gradient wrt update gate, [bS, nU]
// dLdc gradient wrt cell state, [bS, nU]
// dLdh gradient wrt current cell output, [bS, nU]
// Outputs:
// dLdx gradient wrt x, [bS, iS],
// dLdhLast gradient wrt hLast, [bS, nU]
// dLdW gradient wrt W, [iS+nU, 2*nU]
// dLdWc gradient wrt Wc, [iS+nU, nU]
// dLdb gradient wrt bru [2*nU]
// dLdbc gradient wrt bc [nU]
// * means element-wise product or so called Hadamard product
// × means matrix multiplication
/************************************************************************************************/
/******************************* THIS IS NOT OPTIMIZED CODE *************************************/
/*** aim is to have math-readable code in order to keep track of backprop formulas derivation ***/
const int bS = x->sizeAt(0);
const int iS = x->sizeAt(1);
const int nU = hLast->sizeAt(1);
NDArray *xT = x->transpose(); // [iS, bS]
NDArray *hLastT = hLast->transpose(); // [nU, bS]
NDArray *Wrx = (*W)({0, iS, 0, nU}); // [iS, nU]
NDArray *Wux = (*W)({0, iS, nU, 2 * nU}); // [iS, nU]
NDArray *Wrh = (*W)({iS, iS + nU, 0, nU}); // [nU, nU]
NDArray *Wuh = (*W)({iS, iS + nU, nU, 2 * nU}); // [nU, nU]
NDArray *Wcx = (*Wc)({0, iS, 0, 0}); // reset cell weights [iS, nU]
NDArray *Wch = (*Wc)({iS, iS + nU, 0, 0}); // updates cell weights [nU, nU]
NDArray *br = (*b)({0, nU}); // [nU]
NDArray *bu = (*b)({nU, 2 * nU}); // [nU]
NDArray *WrxT = Wrx->transpose(); // [nU, iS]
NDArray *WuxT = Wux->transpose(); // [nU, iS]
NDArray *WrhT = Wrh->transpose(); // [nU, nU]
NDArray *WuhT = Wuh->transpose(); // [nU, nU]
NDArray *WcxT = Wcx->transpose(); // [nU, iS]
NDArray *WchT = Wch->transpose(); // [nU, nU]
NDArray *dLdWrx = (*dLdW)({0, iS, 0, nU}); // [iS, nU]
NDArray *dLdWux = (*dLdW)({0, iS, nU, 2 * nU}); // [iS, nU]
NDArray *dLdWrh = (*dLdW)({iS, iS + nU, 0, nU}); // [nU, nU]
NDArray *dLdWuh = (*dLdW)({iS, iS + nU, nU, 2 * nU}); // [nU, nU]
NDArray *dLdWcx = (*dLdWc)({0, iS, 0, 0}); // [iS, nU]
NDArray *dLdWch = (*dLdWc)({iS, iS + nU, 0, 0}); // [nU, nU]
NDArray *dLdbr = (*dLdb)({0, nU}); // [nU]
NDArray *dLdbu = (*dLdb)({nU, 2 * nU}); // [nU]
// ***** feed forward step ***** //
// r = sigmoid(x × Wrx + hLast × Wrh + br)
auto xWrx = mmul(*x, *Wrx);
auto hLastWrh = mmul(*hLast, *Wrh);
auto* sum1 = *xWrx + *hLastWrh;
auto* rTemp = (*sum1) + (*br);
delete sum1;
NDArray r = *rTemp; // [bS, iS] × [iS, nU] + [bS, nU] × [nU, nU] + [nU] = [bS, nU]
delete rTemp;
r.applyTransform(transform::Sigmoid, &r);
// u = sigmoid(x × Wux + hLast × Wuh + bu)
auto xWux = mmul(*x, *Wux);
auto hLastWuh = mmul(*hLast, *Wuh);
auto* sum2 = *xWux + *hLastWuh;
auto* uTemp = (*sum2) + (*bu);
delete sum2;
NDArray u = *uTemp; // [bS, iS] × [iS, nU] + [bS, nU] × [nU, nU] + [nU] = [bS, nU]
delete uTemp;
delete xWux;
u.applyTransform(transform::Sigmoid, &u);
// c = tanh(x × Wcx + (r * hLast) × Wch + bc)
auto* rTimesHLast2 = r * (*hLast);
auto xWcx = mmul(*x, *Wcx);
auto rTimesHLast2Wch = mmul(*rTimesHLast2, *Wch);
delete rTimesHLast2;
auto* sum3 = *xWcx + *rTimesHLast2Wch;
auto* cTemp = (*sum3) + (*bc);
delete sum3;
delete xWcx;
delete rTimesHLast2Wch;
NDArray c = *cTemp; // [bS, iS] × [iS, nU] + [bS, nU] × [nU, nU] + [nU] = [bS, nU]
delete cTemp;
c.applyTransform(transform::Tanh, &c);
// h = (1 - u) * c + u * hPrev
// ***** back prop step ***** //
auto* hLastMinusC = (*hLast) - c;
auto* oneMinusU = 1.f - u;
auto* dudZu = u * (*oneMinusU);
delete oneMinusU;
auto* oneMinusR = 1.f - r;
auto* drdZr = r * (*oneMinusR);
delete oneMinusR;
auto* cSquared = c * c;
auto* oneMinusCSquared = 1.f - (*cSquared);
delete cSquared;
auto dcdZc = *oneMinusCSquared;
delete oneMinusCSquared;
auto* dLdZc = (*dLdc) * dcdZc;
auto* dLdZu = (*dLdu) * (*dudZu);
delete dudZu;
auto* dLdZr = (*dLdr) * (*drdZr);
delete drdZr;
NDArray *dhdc = 1.f - u; // [bS, nU]
NDArray dhdu = *hLastMinusC; // [bS, nU]
delete hLastMinusC;
// dLdx = dLdZu × WuxT + dLdZc × WcxT + dLdZr × WrxT
auto dLdZuWuxT = mmul(*dLdZu, *WuxT);
auto dLdZcWcxT = mmul(*dLdZc, *WcxT);
auto dLdZrWrxT = mmul(*dLdZr, *WrxT);
auto* temp1 = *dLdZuWuxT + *dLdZcWcxT;
auto* dLdxTemp = (*temp1) + *dLdZrWrxT;
delete temp1;
delete dLdZuWuxT;
delete dLdZcWcxT;
delete dLdZrWrxT;
dLdx->assign(dLdxTemp); // [bS, iS]
delete dLdxTemp;
// dldZTimeR = dLdZc * r
auto* dldZTimeR = (*dLdZc) * r;
// dLdhLast = dLdh * u + dLdZu × WuhT + dldZTimeR × WchT + dLdZr × WrhT
auto* dLdhTimesU = (*dLdh) * u;
auto dLdZuWuhT = mmul(*dLdZu, *WuhT);
auto dldZTimeRWchT = mmul(*dldZTimeR, *WchT);
auto dLdZrWrhT = mmul(*dLdZr, *WrhT);
auto* temp2 = (*dLdhTimesU) + *dLdZuWuhT;
delete dLdhTimesU;
delete dLdZuWuhT;
auto* temp3 = (*temp2) + *dldZTimeRWchT;
delete temp2;
delete dldZTimeRWchT;
auto* dLdhLastTemp = (*temp3) + *dLdZrWrhT;
delete temp3;
delete dLdZrWrhT;
dLdhLast->assign(dLdhLastTemp); // [bS, nU]
delete dLdhLastTemp;
// dLdWrx = xT × dLdZr
auto dLdWrxTemp = mmul(*xT, *dLdZr);
dLdWrx->assign(dLdWrxTemp); // [iS, bS] × [bS, nU] = [iS, nU]
delete dLdWrxTemp;
// dLdWrh = hLastT × dLdZr
auto dLdWrhTemp = mmul(*hLastT, *dLdZr);
dLdWrh->assign(dLdWrhTemp); // [nU, bS] × [bS, nU] = [nU, nU]
delete dLdWrhTemp;
// dLdWux = xT × dLdZu
auto dLdWuxTemp = mmul(*xT, *dLdZu);
dLdWux->assign(dLdWuxTemp); // [iS, bS] × [bS, nU] = [iS, nU]
delete dLdWuxTemp;
// dLdWuh = hLastT × dLdZu
auto dLdWuhTemp = mmul(*hLastT, *dLdZu);
dLdWuh->assign(dLdWuhTemp); // [nU, bS] × [bS, nU] = [nU, nU]
delete dLdWuhTemp;
// dLdWcx = xT × dLdZc
auto dLdWcxTemp = mmul(*xT, *dLdZc);
dLdWcx->assign(dLdWcxTemp); // [iS, bS] × [bS, nU] = [iS, nU]
delete dLdWcxTemp;
// dLdWch = (r * hLast)T × dLdZc
auto* rTimesHLast = r * (*hLast);
NDArray* rTimesHLastT = rTimesHLast->transpose();
delete rTimesHLast;
auto dLdWchTemp = mmul(*rTimesHLastT, *dLdZc);
dLdWch->assign(dLdWchTemp); // [nU, bS] × [bS, nU] = [nU, nU]
delete dLdWchTemp;
// Calculate reduction for bias gradients
std::vector<sd::LongType> zeroVec = {0};
auto* dLdbrTemp = dLdZr->reduceAlongDimension(reduce::Sum, &zeroVec);
dLdbr->assign(dLdbrTemp); // [nU]
delete dLdbrTemp;
auto* dLdbuTemp = dLdZu->reduceAlongDimension(reduce::Sum, &zeroVec);
dLdbu->assign(dLdbuTemp); // [nU]
delete dLdbuTemp;
auto* dLdbcTemp = dLdZc->reduceAlongDimension(reduce::Sum, &zeroVec);
dLdbc->assign(dLdbcTemp); // [nU]
delete dLdbcTemp;
delete dhdc;
delete dLdZc;
delete dLdZu;
delete dLdZr;
delete dldZTimeR;
delete Wrx;
delete Wux;
delete Wrh;
delete Wuh;
delete Wcx;
delete Wch;
delete br;
delete bu;
delete WrxT;
delete WuxT;
delete WrhT;
delete WuhT;
delete WcxT;
delete WchT;
delete dLdWrx;
delete dLdWux;
delete dLdWrh;
delete dLdWuh;
delete dLdWcx;
delete dLdWch;
delete dLdbr;
delete dLdbu;
delete xT;
delete hLastT;
delete rTimesHLastT;
}
//////////////////////////////////////////////////////////////////////////
void gruCellBp(sd::LaunchContext* context, NDArray* x, NDArray* hI, NDArray* Wx, NDArray* Wh,
NDArray* b, NDArray* dLdh, NDArray* gates, NDArray* dLdx, NDArray* dLdhI,
NDArray* dLdWx, NDArray* dLdWh, NDArray* dLdb) {
// Inputs:
// x input [bS, nIn]
// hI previous cell output [bS, nOut], that nIn at previous time step t-1
// Wx input-to-hidden weights - [nIn, 3*nOut]
// Wh hidden-to-hidden weights - [nOut, 3*nOut]
// b biases, [3*nOut] - reset and update gates
// dLdh gradient vs. ff output, [bS, nOut]
// Outputs:
// dLdx gradient vs. x, [bS, nIn],
// dLdhI gradient vs. hI, [bS, nOut]
// dLdWx gradient vs. W, [nIn, 3*nOut]
// dLdWh gradient vs. Wc, [nOut, 3*nOut]
// dLdb gradient vs. b [3*nOut]
// 3*nOut means following sequence: reset, update, cell
// * means element-wise product or so called Hadamard product
// × means matrix multiplication
const int nOut = hI->sizeAt(1);
NDArray *gatesULike = gates->ulike();
NDArray dLdz = *gatesULike; // [bS, 3*nOut]
NDArray *dLdzru = dLdz({0, 0, 0, 2 * nOut}); // [bS, 2*nOut]
NDArray *dLdzr = dLdz({0, 0, 0, nOut}); // [bS, nOut]
NDArray *dLdzu = dLdz({0, 0, nOut, 2 * nOut}); // [bS, nOut]
NDArray *dLdzc = dLdz({0, 0, 2 * nOut, 3 * nOut}); // [bS, nOut]
NDArray *r = (*gates)({0, 0, 0, nOut}); // [bS, nOut]
NDArray *u = (*gates)({0, 0, nOut, 2 * nOut}); // [bS, nOut]
NDArray *c = (*gates)({0, 0, 2 * nOut, 3 * nOut}); // [bS, nOut]
NDArray *WhView = (*Wh)({0, 0, 2 * nOut, 3 * nOut});
NDArray *WhcT = WhView->transpose();
if (dLdh) *dLdhI += *dLdh;
auto* oneMinusU = 1 - (*u); // [bS, nOut]
// dLdzc = dLdhI * (1-u) * (1-c²)
auto* cSquared = (*c) * (*c);
auto* oneMinusCSquared = 1.f - (*cSquared);
delete cSquared;
auto* temp1 = (*dLdhI) * (*oneMinusU);
auto* dLdzcTemp = (*temp1) * (*oneMinusCSquared);
delete temp1;
delete oneMinusCSquared;
dLdzc->assign(dLdzcTemp); // [bS, nOut]
delete dLdzcTemp;
// dLdzu = dLdhI * (hI - c) * u * (1-u)
auto* hIMinusC = (*hI) - (*c);
auto* uTimesOneMinusU = (*u) * (*oneMinusU);
auto* temp2 = (*dLdhI) * (*hIMinusC);
auto* dLdzuTemp = (*temp2) * (*uTimesOneMinusU);
delete temp2;
delete hIMinusC;
delete uTimesOneMinusU;
dLdzu->assign(dLdzuTemp); // [bS, nOut]
delete dLdzuTemp;
delete oneMinusU;
// dLdzr = (dLdzc * hI * r * (1-r)) × WhcT
auto* oneMinusR = 1 - (*r);
auto* rTimesOneMinusR = (*r) * (*oneMinusR);
delete oneMinusR;
auto* temp3 = (*dLdzc) * (*hI);
auto* temp4 = (*temp3) * (*rTimesOneMinusR);
delete temp3;
delete rTimesOneMinusR;
MmulHelper::mmul(temp4, WhcT, dLdzr); // [bS, nOut] x [nOut, nOut] = [bS, nOut]
delete temp4;
// dLdx = dLdz × WxT
NDArray *WxT = Wx->transpose();
MmulHelper::mmul(&dLdz, WxT, dLdx); // [bS, 3*nOut] x [3*nOut, nIn] = [bS, nIn]
// dLdWx += xT × dLdz
NDArray *xT = x->transpose();
auto dLdWxAdd = mmul(*xT, dLdz);
*dLdWx += *dLdWxAdd; // [nIn, bS] x [bS, 3*nOut] = [nIn, 3*nOut]
delete dLdWxAdd;
// dLdb += sum(dLdz, axis=0)
std::vector<sd::LongType> zeroVec = {0};
auto* dLdbAdd = dLdz.reduceAlongDimension(reduce::Sum, &zeroVec);
*dLdb += (*dLdbAdd); // [bS, 3*nOut] -> reduce -> [3*nOut];
delete dLdbAdd;
*dLdzc *= (*r);
// dLdhI = dLdhI * u + dLdz × WhT
NDArray *WhT = Wh->transpose();
auto* dLdhIU = (*dLdhI) * (*u);
auto mmulResult = mmul(dLdz, *WhT);
auto* dLdhIAssign = (*dLdhIU) + *mmulResult;
delete dLdhIU;
delete mmulResult;
dLdhI->assign(dLdhIAssign); // [bS, 3*nOut] x [3*nOut, nOut] = [bS, nOut]
delete dLdhIAssign;
// dLdWh += hIT × dLdz
NDArray *hITranspose = hI->transpose();
auto dLdWhAdd = mmul(*hITranspose, dLdz);
*dLdWh += *dLdWhAdd; // [nOut, bS] x [bS, 3*nOut] = [nOut, 3*nOut]
delete dLdWhAdd;
delete gatesULike;
delete dLdzru;
delete dLdzr;
delete dLdzu;
delete dLdzc;
delete r;
delete u;
delete c;
delete WhView;
delete WhcT;
delete hITranspose;
delete WhT;
delete xT;
delete WxT;
}
//////////////////////////////////////////////////////////////////////////
void gruTimeLoopBp(sd::LaunchContext* context, NDArray* x, NDArray* hI, NDArray* Wx,
NDArray* Wh, NDArray* b, NDArray* dLdh, NDArray* dLdx, NDArray* dLdhI,
NDArray* dLdWx, NDArray* dLdWh, NDArray* dLdb) {
// sL means time steps
// x input [sL, bS, nIn]
// hI initial cell output (at time step = 0) [bS, nOut]
// Wx input-to-hidden weights, [nIn, 3*nOut]
// Wh hidden-to-hidden weights, [nOut, 3*nOut]
// b biases, [3*nOut]
// dLdh gradient vs. ff output, [sL, bS, nOut]
// dLdx gradient vs. x, [sL, bS, nIn],
// dLdhI gradient vs. hI, [bS, nOut]
// dLdWx gradient vs. W, [nIn, 3*nOut]
// dLdWh gradient vs. Wc, [nOut, 3*nOut]
// dLdb gradient vs. b [3*nOut]
const int sL = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int nOut = hI->sizeAt(1);
std::vector<sd::LongType> shape = {bS, 3 * nOut};
std::vector<sd::LongType> hShape = {sL + 1, bS, nOut};
NDArray gates(x->ordering(), shape, dLdh->dataType(), x->getContext());
NDArray h(x->ordering(), hShape, dLdh->dataType(), x->getContext());
auto xSet = x->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nIn]
auto dLdhSet = dLdh->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nOut]
auto hSet = h.allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nOut]
auto gatesSet = gates.allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nOut]
auto dLdxSet = dLdx->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nIn]
hSet.at(0)->assign(hI);
// forward time loop
for (int t = 0; t < sL; ++t) gruCell(xSet.at(t), hSet.at(t), Wx, Wh, b, gatesSet.at(t), hSet.at(t + 1), false);
// backward time loop
for (int t = sL - 1; t >= 0; --t)
gruCellBp(context, xSet.at(t), hSet.at(t), Wx, Wh, b, dLdhSet.at(t), gatesSet.at(t), dLdxSet.at(t), dLdhI, dLdWx,
dLdWh, dLdb);
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif