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,295 @@
/* ******************************************************************************
*
*
* 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 05.04.2018
//
#include <ops/declarable/CustomOperations.h>
#if NOT_EXCLUDED(OP_dynamic_bidirectional_rnn)
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(dynamic_bidirectional_rnn, 7, 4, false, 0, 0) {
auto x = INPUT_VARIABLE(0);
// input [time x bS x inSize] or [bS x time x inSize], shape depends on timeMajor parameter
auto WxFW = INPUT_VARIABLE(1); // input-to-hidden weights for forward RNN, [inSize x numUnitsFW]
auto WhFW = INPUT_VARIABLE(2); // hidden-to-hidden weights for forward RNN, [numUnitsFW x numUnitsFW]
auto bFW = INPUT_VARIABLE(3); // biases for forward RNN, [2*numUnitsFW]
auto WxBW = INPUT_VARIABLE(4); // input-to-hidden weights for backward RNN, [inSize x numUnitsBW]
auto WhBW = INPUT_VARIABLE(5); // hidden-to-hidden weights for backward RNN, [numUnitsBW x numUnitsBW]
auto bBW = INPUT_VARIABLE(6); // biases for backward RNN, [2*v]
NDArray* h0FW = nullptr; // initial cell output for forward RNN (at time step = 0) [bS x numUnitsFW]
NDArray* h0BW = nullptr; // initial cell output for backward RNN (at time step = 0) [bS x numUnitsBW]
NDArray* maxTimeStep =
nullptr; // vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
const int timeMajor =
block.getIArguments()->size() > 0 ? INT_ARG(0) : 0; // if non zero then [time, bS, ...], else [bS, time, ...]
switch (block.width()) {
case 8:
maxTimeStep = INPUT_VARIABLE(7);
break;
case 9:
h0FW = INPUT_VARIABLE(7);
h0BW = INPUT_VARIABLE(8);
break;
case 10:
h0FW = INPUT_VARIABLE(7);
h0BW = INPUT_VARIABLE(8);
maxTimeStep = INPUT_VARIABLE(9);
break;
}
auto hFW = OUTPUT_VARIABLE(0); // cell outputs for forward RNN [time x bS x numUnitsFW] or [bS x time x numUnitsFW],
// shape depends on timeMajor parameter
auto hBW = OUTPUT_VARIABLE(1); // cell outputs for backward RNN [time x bS x numUnitsBW] or [bS x time x numUnitsBW],
// shape depends on timeMajor parameter
auto hFWFinal = OUTPUT_VARIABLE(2); // final cell out for forward RNN [bS x numUnitsFW]
auto hBWFinal = OUTPUT_VARIABLE(3); // final cell out for backward RNN [bS x numUnitsBF]
REQUIRE_TRUE(x->rankOf() == 3, 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: input array must have rank = 3, but got %i instead !",
x->rankOf());
REQUIRE_TRUE(WxFW->rankOf() == 2, 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: input-to-hidden weights array (for forward RNN) must have "
"rank = 2, but got %i instead !",
WxFW->rankOf());
REQUIRE_TRUE(WxBW->rankOf() == 2, 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: input-to-hidden weights array (for backward RNN) must have "
"rank = 2, but got %i instead !",
WxBW->rankOf());
const int inRank = x->rankOf();
int time = timeMajor ? x->sizeAt(0) : x->sizeAt(1);
const int bS = timeMajor ? x->sizeAt(1) : x->sizeAt(0);
const int numUnitsFW = WxFW->sizeAt(1);
const int numUnitsBW = WxBW->sizeAt(1);
std::vector<LongType> expectedWhFWshape = {numUnitsFW, numUnitsFW};
std::vector<LongType> expectedWhBWshape = {numUnitsBW, numUnitsBW};
std::vector<LongType> expectedbFWshape = {2 * numUnitsFW};
std::vector<LongType> expectedbBWshape = {2 * numUnitsBW};
REQUIRE_TRUE(WhFW->isSameShape(expectedWhFWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of hidden-to-hidden weights array (for forward "
" RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedWhFWshape).c_str(), ShapeUtils::shapeAsString(WhFW).c_str());
REQUIRE_TRUE(WhBW->isSameShape(expectedWhBWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of hidden-to-hidden weights array (for "
"backward RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedWhBWshape).c_str(), ShapeUtils::shapeAsString(WhBW).c_str());
REQUIRE_TRUE(bFW->isSameShape(expectedbFWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of biases array (for forward RNN), expected "
"is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbFWshape).c_str(), ShapeUtils::shapeAsString(bFW).c_str());
REQUIRE_TRUE(bBW->isSameShape(expectedbBWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of biases array (for backward RNN), expected "
"is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbBWshape).c_str(), ShapeUtils::shapeAsString(bBW).c_str());
if (h0FW) {
std::vector<LongType> expectedh0FWshape = {bS, numUnitsFW};
REQUIRE_TRUE(h0FW->isSameShape(expectedh0FWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of initial cell output array (for forward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0FWshape).c_str(), ShapeUtils::shapeAsString(h0FW).c_str());
}
if (h0BW) {
std::vector<LongType> expectedh0BWshape = {bS, numUnitsBW};
REQUIRE_TRUE(h0BW->isSameShape(expectedh0BWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of initial cell output array (for backward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0BWshape).c_str(), ShapeUtils::shapeAsString(h0BW).c_str());
}
if (maxTimeStep) {
std::vector<LongType> expectedmaxTimeStepshape = {bS};
REQUIRE_TRUE(maxTimeStep->isSameShape(expectedmaxTimeStepshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of maxTimeStep array, expected is [%i], but "
"got %s instead !",
bS, ShapeUtils::shapeAsString(maxTimeStep).c_str());
}
// forward steps
dynamic_rnn dynamicRnn;
auto resultsFW = dynamicRnn.evaluate({x, WxFW, WhFW, bFW, h0FW, maxTimeStep}, {timeMajor});
hFW->assign(resultsFW.at(0)); // [time x bS x numUnitsFW] or [bS x time x numUnitsFW]
hFWFinal->assign(resultsFW.at(1));
auto seqLen = maxTimeStep;
if (seqLen == nullptr) {
// FIXME: which datatype should be used here?
std::vector<sd::LongType> shape = {bS};
seqLen = new NDArray(x->ordering(),shape, INT64, block.launchContext());
seqLen->assign(time); // set each element of seqLen to be equal to time
}
// reverse x
reverse_sequence reverse;
auto resultsIn = timeMajor ? reverse.evaluate({x, seqLen}, {0, 1}) : reverse.evaluate({x, seqLen}, {1, 0});
REQUIRE_TRUE(resultsIn.status() == sd::Status::OK, 0,
"dynamic_bidirectional_rnn: there is a problem with reverse on the sequence.");
auto revInput = resultsIn.at(0);
// backward steps
auto resultsBW = dynamicRnn.evaluate({revInput, WxBW, WhBW, bBW, h0BW, maxTimeStep}, {timeMajor});
auto hBWtemp = resultsBW.at(0); // [time x bS x numUnitsBW] or [ bS x time xnumUnitsBW]
hBWFinal->assign(resultsBW.at(1));
// reverse hBWtemp
auto resultsOut =
timeMajor ? reverse.evaluate({hBWtemp, seqLen}, {0, 1}) : reverse.evaluate({hBWtemp, seqLen}, {1, 0});
hBW->assign(resultsOut.at(0));
if (seqLen != maxTimeStep) delete seqLen;
return Status::OK;
}
DECLARE_TYPES(dynamic_bidirectional_rnn) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(dynamic_bidirectional_rnn) {
auto x =
INPUT_VARIABLE(0); // input [time x bS x inSize] or [bS x time x inSize], shape depends on timeMajor parameter
auto WxFW = INPUT_VARIABLE(1); // input-to-hidden weights for forward RNN, [inSize x numUnitsFW]
auto WhFW = INPUT_VARIABLE(2); // hidden-to-hidden weights for forward RNN, [numUnitsFW x numUnitsFW]
auto bFW = INPUT_VARIABLE(3); // biases for forward RNN, [2*numUnitsFW]
auto WxBW = INPUT_VARIABLE(4); // input-to-hidden weights for backward RNN, [inSize x numUnitsBW]
auto WhBW = INPUT_VARIABLE(5); // hidden-to-hidden weights for backward RNN, [numUnitsBW x numUnitsBW]
auto bBW = INPUT_VARIABLE(6); // biases for backward RNN, [2*numUnitsBW]
NDArray* h0FW = nullptr; // initial cell output for forward RNN (at time step = 0) [bS x numUnitsFW]
NDArray* h0BW = nullptr; // initial cell output for backward RNN (at time step = 0) [bS x numUnitsBW]
NDArray* maxTimeStep =
nullptr; // vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
const int timeMajor =
block.getIArguments()->size() > 0 ? INT_ARG(0) : 0; // if true then [time, bS, ...], else [bS, time, ...]
switch (block.width()) {
case 8:
maxTimeStep = INPUT_VARIABLE(7);
break;
case 9:
h0FW = INPUT_VARIABLE(7);
h0BW = INPUT_VARIABLE(8);
break;
case 10:
h0FW = INPUT_VARIABLE(7);
h0BW = INPUT_VARIABLE(8);
maxTimeStep = INPUT_VARIABLE(9);
break;
}
REQUIRE_TRUE(x->rankOf() == 3, 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: input array must have rank = 3, but got %i instead !",
x->rankOf());
REQUIRE_TRUE(WxFW->rankOf() == 2, 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: input-to-hidden weights array (for forward RNN) must have "
"rank = 2, but got %i instead !",
WxFW->rankOf());
REQUIRE_TRUE(WxBW->rankOf() == 2, 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: input-to-hidden weights array (for backward RNN) must have "
"rank = 2, but got %i instead !",
WxBW->rankOf());
const int inRank = x->rankOf();
const int time = timeMajor ? x->sizeAt(0) : x->sizeAt(1);
const int bS = timeMajor ? x->sizeAt(1) : x->sizeAt(0);
const int numUnitsFW = WxFW->sizeAt(1);
const int numUnitsBW = WxBW->sizeAt(1);
std::vector<LongType> expectedWhFWshape = {numUnitsFW, numUnitsFW};
std::vector<LongType> expectedWhBWshape = {numUnitsBW, numUnitsBW};
std::vector<LongType> expectedbFWshape = {2 * numUnitsFW};
std::vector<LongType> expectedbBWshape = {2 * numUnitsBW};
REQUIRE_TRUE(WhFW->isSameShape(expectedWhFWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of hidden-to-hidden weights array (for forward "
" RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedWhFWshape).c_str(), ShapeUtils::shapeAsString(WhFW).c_str());
REQUIRE_TRUE(WhBW->isSameShape(expectedWhBWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of hidden-to-hidden weights array (for "
"backward RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedWhBWshape).c_str(), ShapeUtils::shapeAsString(WhBW).c_str());
REQUIRE_TRUE(bFW->isSameShape(expectedbFWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of biases array (for forward RNN), expected "
"is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbFWshape).c_str(), ShapeUtils::shapeAsString(bFW).c_str());
REQUIRE_TRUE(bBW->isSameShape(expectedbBWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of biases array (for backward RNN), expected "
"is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbBWshape).c_str(), ShapeUtils::shapeAsString(bBW).c_str());
if (h0FW) {
std::vector<LongType> expectedh0FWshape = {bS, numUnitsFW};
REQUIRE_TRUE(h0FW->isSameShape(expectedh0FWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of initial cell output array (for forward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0FWshape).c_str(), ShapeUtils::shapeAsString(h0FW).c_str());
}
if (h0BW) {
std::vector<LongType> expectedh0BWshape = {bS, numUnitsBW};
REQUIRE_TRUE(h0BW->isSameShape(expectedh0BWshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of initial cell output array (for backward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0BWshape).c_str(), ShapeUtils::shapeAsString(h0BW).c_str());
}
if (maxTimeStep) {
std::vector<LongType> expectedmaxTimeStepshape = {bS};
REQUIRE_TRUE(maxTimeStep->isSameShape(expectedmaxTimeStepshape), 0,
"DYNAMIC_BIDIRECTIONAL_RNN custom operation: wrong shape of maxTimeStep array, expected is [%i], but "
"got %s instead !",
bS, ShapeUtils::shapeAsString(maxTimeStep).c_str());
}
// evaluate output shapeInfos
LongType *hFWShapeInfo(nullptr), *hBWShapeInfo(nullptr), *hFWFinalPrevShapeInfo(nullptr),
*hBWFinalPrevShapeInfo(nullptr);
ALLOCATE(hFWShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank), sd::LongType);
ALLOCATE(hBWShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank), sd::LongType);
ALLOCATE(hFWFinalPrevShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank - 1), sd::LongType);
ALLOCATE(hBWFinalPrevShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank - 1), sd::LongType);
hFWShapeInfo[0] = hBWShapeInfo[0] = inRank;
hFWShapeInfo[1] = hBWShapeInfo[1] = timeMajor ? time : bS;
hFWShapeInfo[2] = hBWShapeInfo[2] = timeMajor ? bS : time;
hFWShapeInfo[3] = numUnitsFW;
hBWShapeInfo[3] = numUnitsBW;
hFWFinalPrevShapeInfo[0] = hBWFinalPrevShapeInfo[0] = inRank - 1;
hFWFinalPrevShapeInfo[1] = hBWFinalPrevShapeInfo[1] = bS;
hFWFinalPrevShapeInfo[2] = numUnitsFW;
hBWFinalPrevShapeInfo[2] = numUnitsBW;
ShapeUtils::updateStridesAndType(hFWShapeInfo, x->shapeInfo(), x->ordering());
ShapeUtils::updateStridesAndType(hBWShapeInfo, x->shapeInfo(), x->ordering());
ShapeUtils::updateStridesAndType(hFWFinalPrevShapeInfo, x->shapeInfo(), x->ordering());
ShapeUtils::updateStridesAndType(hBWFinalPrevShapeInfo, x->shapeInfo(), x->ordering());
return SHAPELIST(CONSTANT(hFWShapeInfo), CONSTANT(hBWShapeInfo), CONSTANT(hFWFinalPrevShapeInfo),
CONSTANT(hBWFinalPrevShapeInfo));
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,202 @@
/* ******************************************************************************
*
*
* 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 05.04.2018
//
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/rnn.h>
#if NOT_EXCLUDED(OP_dynamic_rnn)
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(dynamic_rnn, 4, 2, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // input [time x bS x inSize] or [bS x time x inSize], depends on timeMajor parameter
auto Wx = INPUT_VARIABLE(1); // input-to-hidden weights, [inSize x numUnits]
auto Wh = INPUT_VARIABLE(2); // hidden-to-hidden weights, [numUnits x numUnits]
auto b = INPUT_VARIABLE(3); // biases for, [2*numUnits]
NDArray* h0 = nullptr; // initial cell output (at time step = 0) [bS x numUnits]
NDArray* maxTimeStep =
nullptr; // vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
const int timeMajor =
block.getIArguments()->size() > 0 ? INT_ARG(0) : 0; // if true then [time, bS, ...], else [bS, time, ...]
if (block.width() == 5) {
if ((*INPUT_VARIABLE(4)).rankOf() == 2)
h0 = INPUT_VARIABLE(4);
else
maxTimeStep = INPUT_VARIABLE(4);
} else if (block.width() == 6) {
h0 = INPUT_VARIABLE(4);
maxTimeStep = INPUT_VARIABLE(5);
}
auto h = OUTPUT_VARIABLE(0); // cell outputs [time x bS x numUnits] or [bS x time x numUnits], depends on timeMajor parameter
auto hFinal = OUTPUT_VARIABLE(1); // at the end it will store cell final non-zero output [bS x numUnits]
REQUIRE_TRUE(x->rankOf() == 3, 0,
"DYNAMIC_RNN custom operation: input array x must have rank = 3, but got %i instead !", x->rankOf());
REQUIRE_TRUE(Wx->rankOf() == 2, 0,
"DYNAMIC_RNN custom operation: input-to-hidden weights array must have rank = 2, but got %i instead !",
Wx->rankOf());
const int inRank = x->rankOf();
const int time = timeMajor ? x->sizeAt(0) : x->sizeAt(1);
const int bS = timeMajor ? x->sizeAt(1) : x->sizeAt(0);
const int numUnits = Wx->sizeAt(1);
std::vector<LongType> expectedWhShape = {numUnits, numUnits};
std::vector<LongType> expectedBShape = {2 * numUnits};
REQUIRE_TRUE(Wh->isSameShape(expectedWhShape), 0,
"DYNAMIC_RNN custom operation: wrong shape of hidden-to-hidden weights array, expected is %s, but got "
"%s instead !",
ShapeUtils::shapeAsString(expectedWhShape).c_str(), ShapeUtils::shapeAsString(Wh).c_str());
REQUIRE_TRUE(b->isSameShape(expectedBShape), 0,
"DYNAMIC_RNN custom operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedBShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
if (h0) {
std::vector<LongType> expectedh0Shape = {bS, numUnits};
REQUIRE_TRUE(
h0->isSameShape(expectedh0Shape), 0,
"DYNAMIC_RNN custom operation: wrong shape of initial cell output array, expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0Shape).c_str(), ShapeUtils::shapeAsString(h0).c_str());
}
if (maxTimeStep) {
std::vector<LongType> expectedmaxTimeStepShape = {bS};
REQUIRE_TRUE(maxTimeStep->isSameShape(expectedmaxTimeStepShape), 0,
"DYNAMIC_RNN custom operation: wrong shape of maxTimeStep array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedmaxTimeStepShape).c_str(),
ShapeUtils::shapeAsString(maxTimeStep).c_str());
}
if (timeMajor == false) {
std::vector<sd::LongType> perm = {1, 0, 2};
x = x->permute(perm, false, false); // [bS x time x inSize] -> [time x bS x inSize]
h = h->permute(perm, false, false); // [bS x time x numUnits] -> [time x bS x numUnits]
}
helpers::rnnTimeLoop(block.launchContext(), x, Wx, Wh, b, h0, maxTimeStep, h, hFinal);
if (timeMajor == false) {
delete x;
delete h;
}
return Status::OK;
}
DECLARE_TYPES(dynamic_rnn) {
getOpDescriptor()
->setAllowedInputTypes(0, ANY)
->setAllowedInputTypes(1, {ALL_FLOATS})
->setAllowedInputTypes(2, {ALL_FLOATS})
->setAllowedInputTypes(3, {ALL_FLOATS})
->setAllowedInputTypes(4, {ALL_FLOATS, ALL_INTS})
->setAllowedInputTypes(5, {ALL_FLOATS, ALL_INTS})
->setAllowedOutputTypes(0, {ALL_FLOATS})
->setAllowedOutputTypes(1, {ALL_FLOATS});
}
DECLARE_SHAPE_FN(dynamic_rnn) {
auto xShapeInfo =
inputShape->at(0); // input [time x bS x inSize] or [bS x time x inSize], depends on timeMajor parameter
auto WxShapeInfo = inputShape->at(1); // input-to-hidden weights, [inSize x numUnits]
auto WhShapeInfo = inputShape->at(2); // hidden-to-hidden weights, [numUnits x numUnits]
auto bShapeInfo = inputShape->at(3); // biases for, [2*numUnits]
LongType const* h0ShapeInfo = nullptr; // initial cell output (at time step = 0) [bS x numUnits]
LongType const* maxTimeStepShapeInfo =
nullptr; // vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
const int timeMajor =
block.getIArguments()->size() > 0 ? INT_ARG(0) : 0; // if true then [time, bS, ...], else [bS, time, ...]
if (block.width() == 5) {
if (inputShape->at(4)[0] == 2)
h0ShapeInfo = inputShape->at(4);
else
maxTimeStepShapeInfo = inputShape->at(4);
} else if (block.width() == 6) {
h0ShapeInfo = inputShape->at(4);
maxTimeStepShapeInfo = inputShape->at(5);
}
REQUIRE_TRUE(xShapeInfo[0] == 3, 0,
"DYNAMIC_RNN custom operation: input array x must have rank = 3, but got %i instead !", xShapeInfo[0]);
REQUIRE_TRUE(WxShapeInfo[0] == 2, 0,
"DYNAMIC_RNN custom operation: input-to-hidden weights array must have rank = 2, but got %i instead !",
WxShapeInfo[0]);
const int inRank = xShapeInfo[0];
const int time = timeMajor ? xShapeInfo[1] : xShapeInfo[2];
const int bS = timeMajor ? xShapeInfo[2] : xShapeInfo[1];
const int numUnits = WxShapeInfo[2];
std::vector<LongType> expectedWhShape = {numUnits, numUnits};
std::vector<LongType> expectedBShape = {2 * numUnits};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WhShapeInfo, expectedWhShape), 0,
"DYNAMIC_RNN custom operation: wrong shape of hidden-to-hidden weights array, expected is %s, but got "
"%s instead !",
ShapeUtils::shapeAsString(expectedWhShape).c_str(), ShapeUtils::shapeAsString(WhShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, expectedBShape), 0,
"DYNAMIC_RNN custom operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedBShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
if (h0ShapeInfo) {
std::vector<LongType> expectedh0Shape = {bS, numUnits};
REQUIRE_TRUE(
ShapeUtils::areShapesEqual(h0ShapeInfo, expectedh0Shape), 0,
"DYNAMIC_RNN custom operation: wrong shape of initial cell output array, expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0Shape).c_str(), ShapeUtils::shapeAsString(h0ShapeInfo).c_str());
}
if (maxTimeStepShapeInfo) {
std::vector<LongType> expectedmaxTimeStepShape = {bS};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maxTimeStepShapeInfo, expectedmaxTimeStepShape), 0,
"DYNAMIC_RNN custom operation: wrong shape of maxTimeStep array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedmaxTimeStepShape).c_str(),
ShapeUtils::shapeAsString(maxTimeStepShapeInfo).c_str());
}
// evaluate output shapeInfos
LongType *hShapeInfo(nullptr), *hPrevShapeInfo(nullptr);
ALLOCATE(hShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank), sd::LongType);
ALLOCATE(hPrevShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank - 1), sd::LongType);
hShapeInfo[0] = inRank;
hPrevShapeInfo[0] = inRank - 1;
hShapeInfo[1] = timeMajor ? time : bS;
hShapeInfo[2] = timeMajor ? bS : time;
hPrevShapeInfo[1] = bS;
hShapeInfo[3] = hPrevShapeInfo[2] = numUnits;
ShapeUtils::updateStridesAndType(hShapeInfo, WhShapeInfo, shape::order(xShapeInfo));
ShapeUtils::updateStridesAndType(hPrevShapeInfo, WhShapeInfo, shape::order(xShapeInfo));
return SHAPELIST(CONSTANT(hShapeInfo), CONSTANT(hPrevShapeInfo));
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,219 @@
/*
* ******************************************************************************
* *
* *
* * 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 <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_gru)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/gru.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(gru, 5, 1, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // input [time, bS, nIn]
auto hI = INPUT_VARIABLE(1); // initial cell output (at time step = 0) [bS, nOut]
auto Wx = INPUT_VARIABLE(2); // input-to-hidden weights, [nIn, 3*nOut]
auto Wh = INPUT_VARIABLE(3); // hidden-to-hidden weights, [nOut, 3*nOut]
auto b = INPUT_VARIABLE(4); // biases, [3*nOut]
auto h = OUTPUT_VARIABLE(0); // cell outputs [time, bS, nOut], that is per each time step
auto linearBeforeReset = block.numB() > 0 ? B_ARG(0) : false;
const int bS = x->sizeAt(1);
const int nIn = x->sizeAt(2);
const int nOut = hI->sizeAt(1);
const std::vector<LongType> h0CorrectShape = {bS, nOut};
const std::vector<LongType> wxCorrectShape = {nIn, 3 * nOut};
const std::vector<LongType> whCorrectShape = {nOut, 3 * nOut};
const std::vector<LongType> bCorrectShape = {3 * nOut};
REQUIRE_TRUE(hI->isSameShape(h0CorrectShape), 0,
"GRU operation: wrong shape of previous cell output array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(h0CorrectShape).c_str(), ShapeUtils::shapeAsString(hI).c_str());
REQUIRE_TRUE(Wx->isSameShape(wxCorrectShape), 0,
"GRU operation: wrong shape of input-to-hidden weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wxCorrectShape).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
REQUIRE_TRUE(Wh->isSameShape(whCorrectShape), 0,
"GRU operation: wrong shape of hidden-to-hidden weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(whCorrectShape).c_str(), ShapeUtils::shapeAsString(Wh).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"GRU operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
helpers::gruTimeLoop(block.launchContext(), x, hI, Wx, Wh, b, h, linearBeforeReset);
return Status::OK;
}
//////////////////////////////////////////////////////////////////////////
DECLARE_TYPES(gru) { getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS}); }
//////////////////////////////////////////////////////////////////////////
DECLARE_SHAPE_FN(gru) {
auto x = INPUT_VARIABLE(0); // input [time, bS, nIn]
auto hI = INPUT_VARIABLE(1); // initial cell output (at time step = 0) [bS, nOut]
auto Wx = INPUT_VARIABLE(2); // input-to-hidden weights, [nIn, 3*nOut]
auto Wh = INPUT_VARIABLE(3); // hidden-to-hidden weights, [nOut, 3*nOut]
auto b = INPUT_VARIABLE(4); // biases, [3*nOut]
const int time = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int nIn = x->sizeAt(2);
const int nOut = hI->sizeAt(1);
const std::vector<LongType> h0CorrectShape = {bS, nOut};
const std::vector<LongType> wxCorrectShape = {nIn, 3 * nOut};
const std::vector<LongType> whCorrectShape = {nOut, 3 * nOut};
const std::vector<LongType> bCorrectShape = {3 * nOut};
REQUIRE_TRUE(hI->isSameShape(h0CorrectShape), 0,
"GRU operation: wrong shape of previous cell output array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(h0CorrectShape).c_str(), ShapeUtils::shapeAsString(hI).c_str());
REQUIRE_TRUE(Wx->isSameShape(wxCorrectShape), 0,
"GRU operation: wrong shape of input-to-hidden weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wxCorrectShape).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
REQUIRE_TRUE(Wh->isSameShape(whCorrectShape), 0,
"GRU operation: wrong shape of hidden-to-hidden weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(whCorrectShape).c_str(), ShapeUtils::shapeAsString(Wh).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"GRU operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
auto hShapeInfo =
ConstantShapeHelper::getInstance().createShapeInfo(hI->dataType(), hI->ordering(), {time, bS, nOut});
return SHAPELIST(hShapeInfo);
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(gru_bp, 6, 5, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // input [time, bS, nIn]
auto hI = INPUT_VARIABLE(1); // initial cell output (at time step = 0) [bS, nOut]
auto Wx = INPUT_VARIABLE(2); // input-to-hidden weights, [nIn, 3*nOut]
auto Wh = INPUT_VARIABLE(3); // hidden-to-hidden weights, [nOut, 3*nOut]
auto b = INPUT_VARIABLE(4); // biases, [3*nOut]
auto dLdh = INPUT_VARIABLE(5); // gradient vs. ff output, [time, bS, nOut]
auto dLdx = OUTPUT_VARIABLE(0); // gradient vs. ff input, [time, bS, nIn]
auto dLdhI = OUTPUT_NULLIFIED(1); // gradient vs. initial cell output, [bS, nOut]
auto dLdWx = OUTPUT_NULLIFIED(2); // gradient vs. input-to-hidden weights, [nIn, 3*nOut]
auto dLdWh = OUTPUT_NULLIFIED(3); // gradient vs. hidden-to-hidden weights, [nOut, 3*nOut]
auto dLdb = OUTPUT_NULLIFIED(4); // gradient vs. biases [3*nOut]
const int time = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int nIn = x->sizeAt(2);
const int nOut = hI->sizeAt(1);
const std::vector<LongType> h0CorrectShape = {bS, nOut};
const std::vector<LongType> wxCorrectShape = {nIn, 3 * nOut};
const std::vector<LongType> whCorrectShape = {nOut, 3 * nOut};
const std::vector<LongType> bCorrectShape = {3 * nOut};
const std::vector<LongType> hCorrectShape = {time, bS, nOut};
REQUIRE_TRUE(hI->isSameShape(h0CorrectShape), 0,
"GRU_BP operation: wrong shape of previous cell output array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(h0CorrectShape).c_str(), ShapeUtils::shapeAsString(hI).c_str());
REQUIRE_TRUE(Wx->isSameShape(wxCorrectShape), 0,
"GRU_BP operation: wrong shape of input-to-hidden weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wxCorrectShape).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
REQUIRE_TRUE(Wh->isSameShape(whCorrectShape), 0,
"GRU_BP operation: wrong shape of hidden-to-hidden weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(whCorrectShape).c_str(), ShapeUtils::shapeAsString(Wh).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"GRU_BP operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(dLdh->isSameShape(hCorrectShape), 0,
"GRU_BP operation: wrong shape of gradient vs. ff output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdh).c_str());
helpers::gruTimeLoopBp(block.launchContext(), x, hI, Wx, Wh, b, dLdh, dLdx, dLdhI, dLdWx, dLdWh, dLdb);
return Status::OK;
}
//////////////////////////////////////////////////////////////////////////
DECLARE_TYPES(gru_bp) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
//////////////////////////////////////////////////////////////////////////
DECLARE_SHAPE_FN(gru_bp) {
auto x = INPUT_VARIABLE(0); // input [time, bS, nIn]
auto hI = INPUT_VARIABLE(1); // initial cell output (at time step = 0) [bS, nOut]
auto Wx = INPUT_VARIABLE(2); // input-to-hidden weights, [nIn, 3*nOut]
auto Wh = INPUT_VARIABLE(3); // hidden-to-hidden weights, [nOut, 3*nOut]
auto b = INPUT_VARIABLE(4); // biases, [3*nOut]
auto dLdh = INPUT_VARIABLE(5); // gradient vs. ff output, [time, bS, nOut]
const int time = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int nIn = x->sizeAt(2);
const int nOut = hI->sizeAt(1);
const std::vector<LongType> h0CorrectShape = {bS, nOut};
const std::vector<LongType> wxCorrectShape = {nIn, 3 * nOut};
const std::vector<LongType> whCorrectShape = {nOut, 3 * nOut};
const std::vector<LongType> bCorrectShape = {3 * nOut};
const std::vector<LongType> hCorrectShape = {time, bS, nOut};
REQUIRE_TRUE(hI->isSameShape(h0CorrectShape), 0,
"GRU_BP operation: wrong shape of previous cell output array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(h0CorrectShape).c_str(), ShapeUtils::shapeAsString(hI).c_str());
REQUIRE_TRUE(Wx->isSameShape(wxCorrectShape), 0,
"GRU_BP operation: wrong shape of input-to-hidden weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wxCorrectShape).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
REQUIRE_TRUE(Wh->isSameShape(whCorrectShape), 0,
"GRU_BP operation: wrong shape of hidden-to-hidden weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(whCorrectShape).c_str(), ShapeUtils::shapeAsString(Wh).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"GRU_BP operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(dLdh->isSameShape(hCorrectShape), 0,
"GRU_BP operation: wrong shape of gradient vs. ff output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdh).c_str());
auto dLdxShapeInfo = ConstantShapeHelper::getInstance().createShapeInfo(dLdh->dataType(), x->shapeInfo());
auto dLdhIShapeInfo = ConstantShapeHelper::getInstance().createShapeInfo(dLdh->dataType(), hI->shapeInfo());
auto dLdWxShapeInfo = ConstantShapeHelper::getInstance().createShapeInfo(dLdh->dataType(), Wx->shapeInfo());
auto dLdWhShapeInfo = ConstantShapeHelper::getInstance().createShapeInfo(dLdh->dataType(), Wh->shapeInfo());
auto dLdbShapeInfo = ConstantShapeHelper::getInstance().createShapeInfo(dLdh->dataType(), b->shapeInfo());
return SHAPELIST(dLdxShapeInfo, dLdhIShapeInfo, dLdWxShapeInfo, dLdWhShapeInfo, dLdbShapeInfo);
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,281 @@
/* ******************************************************************************
*
*
* 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 Alex Black
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_gruCell)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/gru.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(gruCell, 6, 4, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // input [bS, nIn], nIn - input size
auto hLast =
INPUT_VARIABLE(1); // previous cell output [bS, nU], that is at previous time step t-1, nU - number of units
auto Wru = INPUT_VARIABLE(2); // RU weights - [nIn+nU, 2*nU] - reset and update gates (input/recurrent weights)
auto Wc = INPUT_VARIABLE(3); // C weights - [nIn+nU, nU] - cell gate (input/recurrent weights)
auto bru = INPUT_VARIABLE(4); // reset and update biases, [2*nU] - reset and update gates
auto bc = INPUT_VARIABLE(5); // cell biases, [nU]
auto r = OUTPUT_VARIABLE(0); // Reset gate output [bS, nU]
auto u = OUTPUT_VARIABLE(1); // Update gate output [bS, nU]
auto c = OUTPUT_VARIABLE(2); // Cell gate output [bS, nU]
auto h = OUTPUT_VARIABLE(3); // current cell output [bS, nU]
REQUIRE_TRUE(x->rankOf() == 2 && hLast->rankOf() == 2, 0,
"gruCell: Input ranks must be 2 for inputs 0 and 1 (x, hLast) - got %i, %i", x->rankOf(),
hLast->rankOf());
const int rank = x->rankOf();
const auto bS = x->sizeAt(0);
const auto nIn = x->sizeAt(1);
const auto nU = hLast->sizeAt(1);
REQUIRE_TRUE(x->sizeAt(0) == hLast->sizeAt(0), 0,
"gruCell: Input minibatch sizes (dimension 0) must be same for x and hLast");
REQUIRE_TRUE(Wru->rankOf() == 2 && Wc->rankOf() == 2, 0,
"gruCell: weight arrays (Wru, Wc) arrays must be 2, got %i and %i", Wru->rankOf(), Wc->rankOf());
REQUIRE_TRUE(Wru->sizeAt(0) == (nIn + nU) && Wc->sizeAt(0) == (nIn + nU), 0,
"gruCell: Weights size(0) must be equal to nIn + nU, got %i", Wru->sizeAt(0));
REQUIRE_TRUE(Wru->sizeAt(1) == (2 * nU), 0,
"gruCell: Weights (reset and update) size(1) must be equal to 2*nU, got %i", Wru->sizeAt(1));
REQUIRE_TRUE(Wc->sizeAt(1) == nU, 0, "gruCell: Weights (cell) size(1) must be equal to nU, got %i", Wc->sizeAt(1));
REQUIRE_TRUE(bru->rankOf() == 1 && bru->sizeAt(0) == (2 * nU), 0,
"gruCell: reset/update biases must be rank 1, size 2*nU");
REQUIRE_TRUE(bc->rankOf() == 1 && bc->sizeAt(0) == nU, 0, "gruCell: cell biases must be rank 1, size nU");
REQUIRE_TRUE(r->rankOf() == 2 && u->rankOf() == 2 && c->rankOf() == 2 && h->rankOf() == 2 && r->sizeAt(0) == bS &&
u->sizeAt(0) == bS && c->sizeAt(0) == bS && h->sizeAt(0) == bS && r->sizeAt(1) == nU &&
u->sizeAt(1) == nU && c->sizeAt(1) == nU && h->sizeAt(1) == nU,
0, "gruCell: Output arrays must all be rank 2 with size(0) == batchSize and size(1) == nU");
helpers::gruCell(block.launchContext(), x, hLast, Wru, Wc, bru, bc, r, u, c, h);
return Status::OK;
}
DECLARE_TYPES(gruCell) {
getOpDescriptor()
->setAllowedInputTypes(0, ANY)
->setAllowedInputTypes(1, {ALL_FLOATS})
->setAllowedInputTypes(2, {ALL_FLOATS})
->setAllowedInputTypes(3, {ALL_FLOATS})
->setAllowedInputTypes(4, {ALL_FLOATS})
->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(gruCell) {
auto x = inputShape->at(0); // input [bS x nIn]
auto hLast = inputShape->at(1); // previous cell output [bS x nU], that is at previous time step t-1
auto Wru = inputShape->at(2); // RU weights - [(nIn+nU), 2*nU] - reset and update gates (input/recurrent weights)
auto Wc = inputShape->at(3); // C weights - [(nIn+nU), nU] - cell gate (input/recurrent weights)
auto bru = inputShape->at(4); // reset and update biases, [2*nU] - reset and update gates
auto bc = inputShape->at(5); // cell biases, [nU]
REQUIRE_TRUE(shape::rank(x) == 2 && shape::rank(hLast) == 2, 0,
"gruCell: Input ranks must be 2 for inputs 0 and 1 (x, hLast) - got %i, %i", shape::rank(x),
shape::rank(hLast));
const int rank = x[0];
const auto bS = x[1];
const auto nIn = x[2];
const auto nU = hLast[2];
REQUIRE_TRUE(x[1] == hLast[1], 0, "gruCell: Input minibatch sizes (dimension 0) must be same for x and hLast");
REQUIRE_TRUE(shape::rank(Wru) == 2 && shape::rank(Wc) == 2, 0,
"gruCell: weight arrays (Wru, Wc) arrays must be 2, got %i and %i", shape::rank(Wru), shape::rank(Wc));
REQUIRE_TRUE(Wru[1] == (nIn + nU) && Wc[1] == (nIn + nU), 0,
"gruCell: Weights size(0) must be equal to nIn + nU, got %i and %i", Wru[1], Wc[1]);
REQUIRE_TRUE(Wru[2] == (2 * nU), 0, "gruCell: Weights (reset and update) size(1) must be equal to 2*nU, got %i",
Wru[2]);
REQUIRE_TRUE(Wc[2] == nU, 0, "gruCell: Weights (cell) size(1) must be equal to nU, got %i", Wc[2]);
REQUIRE_TRUE(shape::rank(bru) == 1 && bru[1] == (2 * nU), 0,
"gruCell: reset/update biases must be rank 1, size 2*nU");
REQUIRE_TRUE(shape::rank(bc) == 1 && bc[1] == nU, 0, "gruCell: cell biases must be rank 1, size nU");
LongType *s0(nullptr);
ALLOCATE(s0, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [bS x nU]
s0[0] = rank;
s0[1] = bS;
s0[2] = nU;
ShapeUtils::updateStridesAndType(s0, x, shape::order(hLast));
auto ts0 = ConstantShapeHelper::getInstance().createFromExisting(s0);
// 4 output shapes, all [bs, nU]
return SHAPELIST(ts0, ts0, ts0, ts0);
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(gruCell_bp, 10, 6, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // input [bS x iS]
auto hi = INPUT_VARIABLE(1); // previous cell output [bS x nU]
auto W = INPUT_VARIABLE(2); // weights, [iS+nU x 2*nU]
auto Wc = INPUT_VARIABLE(3); // c weights, [iS+nU x nU]
auto b = INPUT_VARIABLE(4); // biases, [2*nU]
auto bc = INPUT_VARIABLE(5); // biases, [nU]
auto dLdr = INPUT_VARIABLE(6); // gradient wrt reset gate, [bS, nU]
auto dLdu = INPUT_VARIABLE(7); // gradient wrt update gate, [bS, nU]
auto dLdc = INPUT_VARIABLE(8); // gradient wrt cell state, [bS, nU]
auto dLdh = INPUT_VARIABLE(9); // gradient wrt current cell output, [bS, nU]
auto dLdx = OUTPUT_VARIABLE(0); // gradient wrt x, [bS, iS]
auto dLdhi = OUTPUT_VARIABLE(1); // gradient wrt hi, [bS, nU]
auto dLdW = OUTPUT_VARIABLE(2); // gradient wrt W, [iS+nU x 2*nU]
auto dLdWc = OUTPUT_VARIABLE(3); // gradient wrt Wc, [iS+nU x nU]
auto dLdb = OUTPUT_VARIABLE(4); // gradient wrt biases, [2*nU]
auto dLdbc = OUTPUT_VARIABLE(5); // gradient wrt c biases, [nU]
const LongType bS = x->sizeAt(0);
const LongType iS = x->sizeAt(1);
const LongType nU = hi->sizeAt(1);
REQUIRE_TRUE(x->rankOf() == 2, 0, "GRU_CELL_BP: rank of input array x must be 2, but got %i instead", x->rankOf());
const std::vector<LongType> hiCorrectShape = {bS, nU};
const std::vector<LongType> wCorrectShape = {iS + nU, 2 * nU};
const std::vector<LongType> wcCorrectShape = {iS + nU, nU};
const std::vector<LongType> bCorrectShape = {2 * nU};
const std::vector<LongType> bcCorrectShape = {nU};
REQUIRE_TRUE(hi->isSameShape(hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of previous cell output array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(hi).c_str());
REQUIRE_TRUE(W->isSameShape(wCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(W).c_str());
REQUIRE_TRUE(Wc->isSameShape(wcCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of c weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wcCorrectShape).c_str(), ShapeUtils::shapeAsString(Wc).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(bc->isSameShape(bcCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of c biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bcCorrectShape).c_str(), ShapeUtils::shapeAsString(bc).c_str());
REQUIRE_TRUE(
dLdr->isSameShape(hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of dLdr array (gradient wrt reset gate), expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdr).c_str());
REQUIRE_TRUE(
dLdu->isSameShape(hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of dLdu array (gradient wrt update gate), expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdu).c_str());
REQUIRE_TRUE(
dLdc->isSameShape(hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of dLdc array (gradient wrt cell state), expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdc).c_str());
REQUIRE_TRUE(dLdh->isSameShape(hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of dLdh array (gradient wrt current cell output), expected is %s, but got "
"%s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdh).c_str());
helpers::gruCellBp(block.launchContext(), x, hi, W, Wc, b, bc, dLdr, dLdu, dLdc, dLdh, dLdx, dLdhi, dLdW, dLdWc, dLdb,
dLdbc);
return Status::OK;
}
DECLARE_TYPES(gruCell_bp) {
getOpDescriptor()
->setAllowedInputTypes(0, ANY)
->setAllowedInputTypes(1, {ALL_FLOATS})
->setAllowedInputTypes(2, {ALL_FLOATS})
->setAllowedInputTypes(3, {ALL_FLOATS})
->setAllowedInputTypes(4, {ALL_FLOATS})
->setAllowedInputTypes(5, {ALL_FLOATS})
->setAllowedInputTypes(6, {ALL_FLOATS})
->setAllowedInputTypes(7, {ALL_FLOATS})
->setAllowedInputTypes(8, {ALL_FLOATS})
->setAllowedInputTypes(9, {ALL_FLOATS})
->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(gruCell_bp) {
auto xShapeInfo = inputShape->at(0); // [bS x iS]
auto hiShapeInfo = inputShape->at(1); // [bS x nU]
auto wShapeInfo = inputShape->at(2); // [iS+nU x 2*nU]
auto wcShapeInfo = inputShape->at(3); // [iS+nU x nU]
auto bShapeInfo = inputShape->at(4); // [2*nU]
auto bcShapeInfo = inputShape->at(5); // [nU]
auto dLdrShapeInfo = inputShape->at(6); // [bS, nU]
auto dLduShapeInfo = inputShape->at(7); // [bS, nU]
auto dLdcShapeInfo = inputShape->at(8); // [bS, nU]
auto dLdhShapeInfo = inputShape->at(9); // [bS, nU]
const int rank = xShapeInfo[0]; // = 2
const LongType bS = xShapeInfo[1];
const LongType iS = xShapeInfo[2];
const LongType nU = hiShapeInfo[2];
REQUIRE_TRUE(xShapeInfo[0] == 2, 0, "GRU_CELL_BP: rank of input array x must be 2, but got %i instead",
xShapeInfo[0]);
const std::vector<LongType> hiCorrectShape = {bS, nU};
const std::vector<LongType> wCorrectShape = {iS + nU, 2 * nU};
const std::vector<LongType> wcCorrectShape = {iS + nU, nU};
const std::vector<LongType> bCorrectShape = {2 * nU};
const std::vector<LongType> bcCorrectShape = {nU};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(hiShapeInfo, hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of previous cell output array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(hiShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wShapeInfo, wCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(wShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wcShapeInfo, wcCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of c weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wcCorrectShape).c_str(), ShapeUtils::shapeAsString(wcShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, bCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bcShapeInfo, bcCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of c biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bcCorrectShape).c_str(), ShapeUtils::shapeAsString(bcShapeInfo).c_str());
REQUIRE_TRUE(
ShapeUtils::areShapesEqual(dLdrShapeInfo, hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of dLdr array (gradient wrt reset gate), expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdrShapeInfo).c_str());
REQUIRE_TRUE(
ShapeUtils::areShapesEqual(dLduShapeInfo, hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of dLdu array (gradient wrt update gate), expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(dLduShapeInfo).c_str());
REQUIRE_TRUE(
ShapeUtils::areShapesEqual(dLdcShapeInfo, hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of dLdc array (gradient wrt cell state), expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdcShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(dLdhShapeInfo, hiCorrectShape), 0,
"GRU_CELL_BP op: wrong shape of dLdh array (gradient wrt current cell output), expected is %s, but got "
"%s instead !",
ShapeUtils::shapeAsString(hiCorrectShape).c_str(), ShapeUtils::shapeAsString(dLdhShapeInfo).c_str());
return SHAPELIST(CONSTANT(xShapeInfo), CONSTANT(hiShapeInfo), CONSTANT(wShapeInfo), CONSTANT(wcShapeInfo),
CONSTANT(bShapeInfo), CONSTANT(bcShapeInfo));
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,182 @@
/*
* ******************************************************************************
* *
* *
* * 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 15.02.2018
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_lstm)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/lstm.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(lstm, 8, 2, false, 3, 2) {
auto x = INPUT_VARIABLE(0); // input [time x bS x inSize]
auto h0 = INPUT_VARIABLE(1); // initial cell output (at time step = 0) [bS x numProj], in case of projection=false ->
// numProj == numUnits !!!
auto c0 = INPUT_VARIABLE(2); // initial cell state (at time step = 0) [bS x numUnits],
auto Wx = INPUT_VARIABLE(3); // input-to-hidden weights, [inSize x 4*numUnits]
auto Wh = INPUT_VARIABLE(4); // hidden-to-hidden weights, [numProj x 4*numUnits]
auto Wc = INPUT_VARIABLE(5); // diagonal weights for peephole connections [3*numUnits]
auto Wp = INPUT_VARIABLE(6); // projection weights [numUnits x numProj]
auto b = INPUT_VARIABLE(7); // biases, [4*numUnits]
auto h = OUTPUT_VARIABLE(0); // cell outputs [time x bS x numProj], that is per each time step
auto c = OUTPUT_VARIABLE(1); // cell states [time x bS x numUnits] that is per each time step
const int peephole = INT_ARG(0); // if 1, provide peephole connections
const int projection = INT_ARG(1);
// if 1, then projection is performed, if false then numProj==numUnits is mandatory!!!!
// FIXME: double
const double clippingCellValue = T_ARG(0);
// clipping value for ct, if it is not equal to zero, then cell state is clipped
const double clippingProjValue = T_ARG(1);
// clipping value for projected ht, if it is not equal to zero, then projected cell output is clipped
const double forgetBias = T_ARG(2);
const int rank = x->rankOf();
const int time = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int inSize = x->sizeAt(2);
const int numProj = h0->sizeAt(1);
const int numUnits = c0->sizeAt(1);
// input shapes validation
const std::vector<LongType> correctH0Shape = {bS, numProj};
const std::vector<LongType> correctC0Shape = {bS, numUnits};
const std::vector<LongType> correctWxShape = {inSize, 4 * numUnits};
const std::vector<LongType> correctWhShape = {numProj, 4 * numUnits};
const std::vector<LongType> correctWcShape = {3 * numUnits};
const std::vector<LongType> correctWpShape = {numUnits, numProj};
const std::vector<LongType> correctBShape = {4 * numUnits};
REQUIRE_TRUE(h0->isSameShape(correctH0Shape), 0,
"LSTM operation: wrong shape of initial cell output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctH0Shape).c_str(), ShapeUtils::shapeAsString(h0).c_str());
REQUIRE_TRUE(c0->isSameShape(correctC0Shape), 0,
"LSTM operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctC0Shape).c_str(), ShapeUtils::shapeAsString(c0).c_str());
REQUIRE_TRUE(Wx->isSameShape(correctWxShape), 0,
"LSTM operation: wrong shape of input-to-hidden weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWxShape).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
REQUIRE_TRUE(Wh->isSameShape(correctWhShape), 0,
"LSTM operation: wrong shape of hidden-to-hidden weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWhShape).c_str(), ShapeUtils::shapeAsString(Wh).c_str());
REQUIRE_TRUE(
Wc->isSameShape(correctWcShape), 0,
"LSTM operation: wrong shape of diagonal weights for peephole connections, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWcShape).c_str(), ShapeUtils::shapeAsString(Wc).c_str());
REQUIRE_TRUE(Wp->isSameShape(correctWpShape), 0,
"LSTM operation: wrong shape of projection weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWpShape).c_str(), ShapeUtils::shapeAsString(Wp).c_str());
REQUIRE_TRUE(b->isSameShape(correctBShape), 0,
"LSTM operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctBShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(!(!projection && numUnits != numProj), 0,
"LSTM operation: projection option is switched of, and in this case output dimensionality for the "
"projection matrices (numProj) must be equal to number of units in lstmCell !");
helpers::lstmTimeLoop(block.launchContext(), x, h0, c0, Wx, Wh, Wc, Wp, b, h, c,
{(double)peephole, (double)projection, clippingCellValue, clippingProjValue, forgetBias});
return Status::OK;
}
DECLARE_TYPES(lstm) { getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS}); }
DECLARE_SHAPE_FN(lstm) {
auto xShapeInfo = inputShape->at(0); // input [time x bS x inSize]
auto h0ShapeInfo = inputShape->at(1); // initial cell output (at time step = 0) [bS x numProj], in case of
// projection=false -> numProj == numUnits !!!
auto c0ShapeInfo = inputShape->at(2); // initial cell state (at time step = 0) [bS x numUnits],
auto WxShapeInfo = inputShape->at(3); // input-to-hidden weights, [inSize x 4*numUnits]
auto WhShapeInfo = inputShape->at(4); // hidden-to-hidden weights, [numProj x 4*numUnits]
auto WcShapeInfo = inputShape->at(5); // diagonal weights for peephole connections [3*numUnits]
auto WpShapeInfo = inputShape->at(6); // projection weights [numUnits x numProj]
auto bShapeInfo = inputShape->at(7); // biases, [4*numUnits]
const int rank = xShapeInfo[0];
const int time = xShapeInfo[1];
const int bS = xShapeInfo[2];
const int inSize = xShapeInfo[3];
const int numProj = h0ShapeInfo[2];
const int numUnits = c0ShapeInfo[2];
// input shapes validation
const std::vector<LongType> correctH0Shape = {bS, numProj};
const std::vector<LongType> correctC0Shape = {bS, numUnits};
const std::vector<LongType> correctWxShape = {inSize, 4 * numUnits};
const std::vector<LongType> correctWhShape = {numProj, 4 * numUnits};
const std::vector<LongType> correctWcShape = {3 * numUnits};
const std::vector<LongType> correctWpShape = {numUnits, numProj};
const std::vector<LongType> correctBShape = {4 * numUnits};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(h0ShapeInfo, correctH0Shape), 0,
"LSTM operation: wrong shape of initial cell output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctH0Shape).c_str(), ShapeUtils::shapeAsString(h0ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(c0ShapeInfo, correctC0Shape), 0,
"LSTM operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctC0Shape).c_str(), ShapeUtils::shapeAsString(c0ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WxShapeInfo, correctWxShape), 0,
"LSTM operation: wrong shape of input-to-hidden weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWxShape).c_str(), ShapeUtils::shapeAsString(WxShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WhShapeInfo, correctWhShape), 0,
"LSTM operation: wrong shape of hidden-to-hidden weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWhShape).c_str(), ShapeUtils::shapeAsString(WhShapeInfo).c_str());
REQUIRE_TRUE(
ShapeUtils::areShapesEqual(WcShapeInfo, correctWcShape), 0,
"LSTM operation: wrong shape of diagonal weights for peephole connections, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWcShape).c_str(), ShapeUtils::shapeAsString(WcShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WpShapeInfo, correctWpShape), 0,
"LSTM operation: wrong shape of projection weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWpShape).c_str(), ShapeUtils::shapeAsString(WpShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, correctBShape), 0,
"LSTM operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctBShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
// evaluate output shapeInfos
LongType *hShapeInfo(nullptr), *cShapeInfo(nullptr);
ALLOCATE(hShapeInfo, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [time x bS x numProj]
ALLOCATE(cShapeInfo, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [time x bS x numUnits]
hShapeInfo[0] = cShapeInfo[0] = rank;
hShapeInfo[1] = cShapeInfo[1] = time;
hShapeInfo[2] = cShapeInfo[2] = bS;
hShapeInfo[3] = numProj;
cShapeInfo[3] = numUnits;
ShapeUtils::updateStridesAndType(hShapeInfo, xShapeInfo, shape::order(h0ShapeInfo));
ShapeUtils::updateStridesAndType(cShapeInfo, xShapeInfo, shape::order(c0ShapeInfo));
return SHAPELIST(CONSTANT(hShapeInfo), CONSTANT(cShapeInfo));
}
} // namespace ops
} // namespace sd
#endif
@@ -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
* *****************************************************************************
*/
// lstmBlock: Full LSTM layer in one op
// @author Alex Black
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_lstmBlock)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/lstmBlock.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(lstmBlock, 9, 7, false, 2, 2) {
auto maxTSLength = INPUT_VARIABLE(0);
auto x = INPUT_VARIABLE(1); // input [seqLen, bS, nIn] at time t
auto cLast = INPUT_VARIABLE(2); // previous cell state [bS, nOut], time t-1
auto yLast = INPUT_VARIABLE(3); // previous output [bS, nOut], time t-1
auto W = INPUT_VARIABLE(4);
// Weights - concatenated (input-to-hidden, hidden-to-hidden weights) weights, [(nIn+nOut), 4*nOut]
auto Wci = INPUT_VARIABLE(5); // weights - cell peephole (t-1) connections to input modulation gate, [nOut]
auto Wcf = INPUT_VARIABLE(6); // weights - cell peephole (t-1) connections to forget gate, [nOut]
auto Wco = INPUT_VARIABLE(7); // weights - cell peephole (t) connections to output gate, [nOut]
auto b = INPUT_VARIABLE(8); // biases, [4*nOut]
auto i = OUTPUT_VARIABLE(0); // Output - input modulation gate activations [seqLen, bS, nOut]
auto c = OUTPUT_VARIABLE(1); // Activations, cell state (pre tanh) [seqLen, bs, nOut]
auto f = OUTPUT_VARIABLE(2); // Output - forget gate activations [seqLen, bs, nOut]
auto o = OUTPUT_VARIABLE(3); // Output - output gate activations [seqLen, bs, nOut]
auto z = OUTPUT_VARIABLE(4); // Output - input gate activations [seqLen, bs, nOut]
auto h = OUTPUT_VARIABLE(5); // Cell state, post tanh [seqLen, bs, nOut]
auto y = OUTPUT_VARIABLE(6); // current cell output [seqLen, bS, numProj], time t
const int peephole = INT_ARG(0); // if 1, provide peephole connections
const int dataFormat = INT_ARG(1); // 0=TNS=[seqLen,bS,nIn]; 1=NST=[bS,nIn,seqLen]; 2=NTS=[bS,seqLen,nIn]
const double forgetBias = T_ARG(0);
const double clippingCellValue = T_ARG(1);
// clipping value for ct, if it is not equal to zero, then cell state is clipped
REQUIRE_TRUE(x->rankOf() == 3, 0, "lstmBlock: Input array 1 (x) rank must be got input with rank %i", x->rankOf());
REQUIRE_TRUE(cLast->rankOf() == 2 && yLast->rankOf() == 2, 0,
"lstmBlock: Input ranks must be 2 for inputs 2/3 (cLast, yLast) - got %i, %i", cLast->rankOf(),
yLast->rankOf());
REQUIRE_TRUE(W->rankOf() == 2, 0, "lstmBlock: Weights array rank must be 2");
REQUIRE_TRUE(b->rankOf() == 1, 0, "lstmBlock: Biases must be rank 1");
REQUIRE_TRUE(i->rankOf() == 3 && c->rankOf() == 3 && f->rankOf() == 3 && o->rankOf() == 3 && z->rankOf() == 3 &&
h->rankOf() == 3 && y->rankOf() == 3,
0, "lstmBlock: Output arrays must all be rank 3");
helpers::lstmBlockTimeLoop(maxTSLength, x, cLast, yLast, W, Wci, Wcf, Wco, b, i, c, f, o, z, h, y,
{(double)peephole, forgetBias, clippingCellValue}, dataFormat);
return Status::OK;
}
DECLARE_TYPES(lstmBlock) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(lstmBlock) {
auto x = inputShape->at(1);
auto cLast = inputShape->at(2);
auto yLast = inputShape->at(3);
auto W = inputShape->at(4);
auto b = inputShape->at(8);
REQUIRE_TRUE(shape::rank(x) == 3, 0, "lstmBlock: Input array 1 (x) rank must be got input with rank %i",
shape::rank(x));
REQUIRE_TRUE(shape::rank(cLast) == 2 && shape::rank(yLast) == 2, 0,
"lstmBlock: Input ranks must be 2 for inputs 2/3 (cLast, yLast) - got %i, %i", shape::rank(cLast),
shape::rank(yLast));
REQUIRE_TRUE(shape::rank(W) == 2, 0, "lstmBlock: Weights array rank must be 2");
REQUIRE_TRUE(shape::rank(b) == 1, 0, "lstmBlock: Biases must be rank 1");
const int dataFormat = INT_ARG(1); // 0=TNS=[seqLen,bS,size]; 1=NST=[bS,size,seqLen]; 2=NTS=[bS,seqLen,size]
int bs;
int t;
int nOut = cLast[2]; // rank, bs, nOut, ...]
LongType *s(nullptr);
ALLOCATE(s, block.getWorkspace(), shape::shapeInfoLength(3), sd::LongType); // [time, bS, nOut]
s[0] = 3;
if (dataFormat == 0) {
//[rank, seqLen, bs, nIn, ...]
s[1] = x[1]; // seqLen
s[2] = x[2]; // bS
s[3] = nOut;
} else if (dataFormat == 1) {
//[rank, bs, nIn, seqLen, ...]
s[1] = x[1]; // bS
s[2] = nOut;
s[3] = x[3]; // seqLen
} else {
//[rank, bs, seqLen, nIn, ...]
s[1] = x[1]; // bS
s[2] = x[2]; // seqLen
s[3] = nOut;
}
ShapeUtils::updateStridesAndType(s, x, 'c');
auto s1 = CONSTANT(s);
// 7 outputs, all same shape/type
return SHAPELIST(s1, s1, s1, s1, s1, s1, s1);
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,144 @@
/*
* ******************************************************************************
* *
* *
* * 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 Alex Black
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_lstmBlockCell)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/lstmBlock.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(lstmBlockCell, 8, 7, false, 2, 1) {
// Notation: mostly following https://arxiv.org/pdf/1503.04069.pdf
auto xt = INPUT_VARIABLE(0); // input [bS, inSize] at time t
auto cLast = INPUT_VARIABLE(1); // previous cell state [bS, numUnits], time t-1
auto yLast = INPUT_VARIABLE(2); // previous output [bS, numUnits], time t-1
auto W = INPUT_VARIABLE(3); // Weights - concatenated (input-to-hidden, hidden-to-hidden weights) weights,
// [(inSize+numUnits), 4*numUnits]
auto Wci = INPUT_VARIABLE(4); // weights - cell peephole (t-1) connections to input modulation gate, [numUnits]
auto Wcf = INPUT_VARIABLE(5); // weights - cell peephole (t-1) connections to forget gate, [numUnits]
auto Wco = INPUT_VARIABLE(6); // weights - cell peephole (t) connections to output gate, [numUnits]
auto b = INPUT_VARIABLE(7); // biases, [4*numUnits]
auto i = OUTPUT_VARIABLE(0); // Output - input modulation gate activations [bS, numUnits]
auto c = OUTPUT_VARIABLE(1); // Activations, cell state (pre tanh) [bs, numUnits]
auto f = OUTPUT_VARIABLE(2); // Output - forget gate activations [bs, numUnits]
auto o = OUTPUT_VARIABLE(3); // Output - output gate activations [bs, numUnits]
auto z = OUTPUT_VARIABLE(4); // Output - input gate activations [bs, numUnits]
auto h = OUTPUT_VARIABLE(5); // Cell state, post tanh [bs, numUnits]
auto y = OUTPUT_VARIABLE(6); // current cell output [bS, numProj], time t
const int peephole = INT_ARG(0); // if 1, provide peephole connections
const double forgetBias = T_ARG(0);
const double clippingCellValue = T_ARG(1);
// clipping value for ct, if it is not equal to zero, then cell state is clipped
REQUIRE_TRUE(xt->rankOf() == 2 && cLast->rankOf() == 2 && yLast->rankOf() == 2, 0,
"lstmBlockCell: Input ranks must be 2 for inputs 0/1/2 (x, cLast, outLast) - got %i, %i, %i",
xt->rankOf(), cLast->rankOf(), yLast->rankOf());
const int rank = xt->rankOf();
const int bS = xt->sizeAt(0);
const int inSize = xt->sizeAt(1);
const int numUnits = cLast->sizeAt(1);
REQUIRE_TRUE(xt->sizeAt(0) == yLast->sizeAt(0) && xt->sizeAt(0) == cLast->sizeAt(0), 0,
"lstmBlockCell: Input minibatch sizes (dimension 0) must be same for xt, cLast, yLast");
REQUIRE_TRUE(W->rankOf() == 2, 0, "lstmBlockCell: Weights array rank must be 2");
REQUIRE_TRUE(W->sizeAt(0) == (inSize + numUnits), 0,
"lstmBlockCell: Weights size(0) must be equal to inSize + numUnits, got %i", W->sizeAt(0));
REQUIRE_TRUE(W->sizeAt(1) == (4 * numUnits), 0, "lstmBlockCell: Weights size(1) must be equal to 4*numUnits, got %i",
W->sizeAt(1));
REQUIRE_TRUE(b->rankOf() == 1 && b->sizeAt(0) == (4 * numUnits), 0,
"lstmBlockCell: Biases must be rank 1, size 4*numUnits");
REQUIRE_TRUE(i->rankOf() == 2 && c->rankOf() == 2 && f->rankOf() == 2 && o->rankOf() == 2 && z->rankOf() == 2 &&
h->rankOf() == 2 && y->rankOf() == 2 && i->sizeAt(0) == bS && c->sizeAt(0) == bS &&
f->sizeAt(0) == bS && o->sizeAt(0) == bS && z->sizeAt(0) == bS && h->sizeAt(0) == bS &&
y->sizeAt(0) == bS && i->sizeAt(1) == numUnits && c->sizeAt(1) == numUnits &&
f->sizeAt(1) == numUnits && o->sizeAt(1) == numUnits && z->sizeAt(1) == numUnits &&
h->sizeAt(1) == numUnits && y->sizeAt(1) == numUnits,
0, "lstmBlockCell: Output arrays must all be rank 2 with size(0) == batchSize and size(1) == numUnits");
// calculations
helpers::lstmBlockCell(xt, cLast, yLast, W, Wci, Wcf, Wco, b, i, c, f, o, z, h, y,
{(double)peephole, forgetBias, clippingCellValue});
return Status::OK;
}
DECLARE_TYPES(lstmBlockCell) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(lstmBlockCell) {
auto xt = inputShape->at(0); // input [bS, inSize] at time t
auto cLast = inputShape->at(1); // previous cell state [bS, numUnits], time t-1
auto yLast = inputShape->at(2); // previous output [bS, numUnits], time t-1
auto W = inputShape->at(3); // Weights - concatenated (input-to-hidden, hidden-to-hidden weights) weights,
// [(inSize+numUnits), 4*numUnits]
auto Wci = inputShape->at(4); // weights - cell peephole (t-1) connections to input modulation gate, [numUnits]
auto Wcf = inputShape->at(5); // weights - cell peephole (t-1) connections to forget gate, [numUnits]
auto Wco = inputShape->at(6); // weights - cell peephole (t) connections to output gate, [numUnits]
auto b = inputShape->at(7); // biases, [4*numUnits]
REQUIRE_TRUE(shape::rank(xt) == 2 && shape::rank(cLast) == 2 && shape::rank(yLast) == 2, 0,
"lstmBlockCell: Input ranks must be 2 for inputs 0/1/2 (x, cLast, outLast) - got %i, %i, %i",
shape::rank(xt), shape::rank(cLast), shape::rank(yLast));
const int inSize = xt[2];
const int numUnits = cLast[2]; //[rank, bS, nOut, ...]
REQUIRE_TRUE(xt[1] == yLast[1] && xt[1] == cLast[1], 0,
"lstmBlockCell: Input minibatch sizes (dimension 0) must be same for xt, cLast, yLast");
REQUIRE_TRUE(shape::rank(W) == 2, 0, "lstmBlockCell: Weights array rank must be rank 2, got %i", shape::rank(W));
REQUIRE_TRUE(W[1] == (inSize + numUnits), 0,
"lstmBlockCell: Weights size(0) must be equal to inSize + numUnits, got %i", W[1]);
REQUIRE_TRUE(W[2] == (4 * numUnits), 0, "lstmBlockCell: Weights size(1) must be equal to 4*numUnits, got %i", W[2]);
REQUIRE_TRUE(shape::rank(b) == 1 && b[1] == (4 * numUnits), 0,
"lstmBlockCell: Biases must be rank 1, size 4*numUnits");
// evaluate output shapeInfos
const int bS = xt[1];
LongType *s(nullptr);
ALLOCATE(s, block.getWorkspace(), shape::shapeInfoLength(2), sd::LongType); // [bS, numUnits]
s[0] = 2;
s[1] = bS;
s[2] = numUnits;
ShapeUtils::updateStridesAndType(s, xt, 'c');
auto s1 = CONSTANT(s);
// 7 outputs, all same shape: z, i, f, o, h, c, y
return SHAPELIST(s1, s1, s1, s1, s1, s1, s1);
}
} // namespace ops
} // namespace sd
#endif
@@ -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 Yurii Shyrma, created on 30.11.2017
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_lstmCell)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/lstm.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(lstmCell, 8, 2, false, 3, 2) {
auto xt = INPUT_VARIABLE(0); // input [bS x inSize]
auto ht_1 = INPUT_VARIABLE(1); // previous cell output [bS x numProj], that is at previous time step t-1, in case of
// projection=false -> numProj=numUnits!!!
auto ct_1 = INPUT_VARIABLE(2); // previous cell state [bS x numUnits], that is at previous time step t-1
auto Wx = INPUT_VARIABLE(3); // input-to-hidden weights, [inSize x 4*numUnits]
auto Wh = INPUT_VARIABLE(4); // hidden-to-hidden weights, [numProj x 4*numUnits]
auto Wc = INPUT_VARIABLE(5); // diagonal weights for peephole connections [3*numUnits]
auto Wp = INPUT_VARIABLE(6); // projection weights [numUnits x numProj]
auto b = INPUT_VARIABLE(7); // biases, [4*numUnits]
auto ht = OUTPUT_VARIABLE(0); // current cell output [bS x numProj], that is at current time step t
auto ct = OUTPUT_VARIABLE(1); // current cell state [bS x numUnits], that is at current time step t
const int peephole = INT_ARG(0); // if 1, provide peephole connections
const int projection = INT_ARG(1);
// if 1, then projection is performed, if false then numProj==numUnits is mandatory!!!!
// FIXME: double?
const double clippingCellValue = T_ARG(0);
// clipping value for ct, if it is not equal to zero, then cell state is clipped
const double clippingProjValue = T_ARG(1);
// clipping value for projected ht, if it is not equal to zero, then projected cell output is clipped
const double forgetBias = T_ARG(2);
const int rank = xt->rankOf();
const int bS = xt->sizeAt(0);
const int inSize = xt->sizeAt(1);
const int numProj = ht_1->sizeAt(1);
const int numUnits = ct_1->sizeAt(1);
// input shapes validation
const std::vector<LongType> correctHt_1Shape = {bS, numProj};
const std::vector<LongType> correctCt_1Shape = {bS, numUnits};
const std::vector<LongType> correctWxShape = {inSize, 4 * numUnits};
const std::vector<LongType> correctWhShape = {numProj, 4 * numUnits};
const std::vector<LongType> correctWcShape = {3 * numUnits};
const std::vector<LongType> correctWpShape = {numUnits, numProj};
const std::vector<LongType> correctBShape = {4 * numUnits};
REQUIRE_TRUE(ht_1->isSameShape(correctHt_1Shape), 0,
"LSTMCELL operation: wrong shape of initial cell output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctHt_1Shape).c_str(), ShapeUtils::shapeAsString(ht_1).c_str());
REQUIRE_TRUE(ct_1->isSameShape(correctCt_1Shape), 0,
"LSTMCELL operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctCt_1Shape).c_str(), ShapeUtils::shapeAsString(ct_1).c_str());
REQUIRE_TRUE(Wx->isSameShape(correctWxShape), 0,
"LSTMCELL operation: wrong shape of input-to-hidden weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWxShape).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
REQUIRE_TRUE(Wh->isSameShape(correctWhShape), 0,
"LSTMCELL operation: wrong shape of hidden-to-hidden weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWhShape).c_str(), ShapeUtils::shapeAsString(Wh).c_str());
REQUIRE_TRUE(Wc->isSameShape(correctWcShape), 0,
"LSTMCELL operation: wrong shape of diagonal weights for peephole connections, expected is %s, but got "
"%s instead !",
ShapeUtils::shapeAsString(correctWcShape).c_str(), ShapeUtils::shapeAsString(Wc).c_str());
REQUIRE_TRUE(Wp->isSameShape(correctWpShape), 0,
"LSTMCELL operation: wrong shape of projection weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWpShape).c_str(), ShapeUtils::shapeAsString(Wp).c_str());
REQUIRE_TRUE(b->isSameShape(correctBShape), 0,
"LSTMCELL operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctBShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(!(!projection && numUnits != numProj), 0,
"LSTMCELL operation: projection option is switched of, and in this case output dimensionality for the "
"projection matrices (numProj) must be equal to number of units in lstmCell !");
// calculations
helpers::lstmCell(block.launchContext(), xt, ht_1, ct_1, Wx, Wh, Wc, Wp, b, ht, ct,
{(double)peephole, (double)projection, clippingCellValue, clippingProjValue, forgetBias});
return Status::OK;
}
DECLARE_TYPES(lstmCell) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(lstmCell) {
auto xtShapeInfo = inputShape->at(0); // input [bS x inSize]
auto ht_1ShapeInfo = inputShape->at(1); // previous cell output [bS x numProj], that is at previous time step t-1,
// in case of projection=false -> numProj=numUnits!!!
auto ct_1ShapeInfo = inputShape->at(2); // previous cell state [bS x numUnits], that is at previous time step t-1
auto WxShapeInfo = inputShape->at(3); // input-to-hidden weights, [inSize x 4*numUnits]
auto WhShapeInfo = inputShape->at(4); // hidden-to-hidden weights, [numProj x 4*numUnits]
auto WcShapeInfo = inputShape->at(5); // diagonal weights for peephole connections [3*numUnits]
auto WpShapeInfo = inputShape->at(6); // projection weights [numUnits x numProj]
auto bShapeInfo = inputShape->at(7); // biases, [4*numUnits]
const int rank = shape::rank(xtShapeInfo);
const auto bS = xtShapeInfo[1];
const auto inSize = xtShapeInfo[2];
const auto numProj = ht_1ShapeInfo[2];
const auto numUnits = ct_1ShapeInfo[2];
// input shapes validation
const std::vector<LongType> correctHt_1Shape = {bS, numProj};
const std::vector<LongType> correctCt_1Shape = {bS, numUnits};
const std::vector<LongType> correctWxShape = {inSize, 4 * numUnits};
const std::vector<LongType> correctWhShape = {numProj, 4 * numUnits};
const std::vector<LongType> correctWcShape = {3 * numUnits};
const std::vector<LongType> correctWpShape = {numUnits, numProj};
const std::vector<LongType> correctBShape = {4 * numUnits};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(ht_1ShapeInfo, correctHt_1Shape), 0,
"LSTMCELL operation: wrong shape of initial cell output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctHt_1Shape).c_str(), ShapeUtils::shapeAsString(ht_1ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(ct_1ShapeInfo, correctCt_1Shape), 0,
"LSTMCELL operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctCt_1Shape).c_str(), ShapeUtils::shapeAsString(ct_1ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WxShapeInfo, correctWxShape), 0,
"LSTMCELL operation: wrong shape of input-to-hidden weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWxShape).c_str(), ShapeUtils::shapeAsString(WxShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WhShapeInfo, correctWhShape), 0,
"LSTMCELL operation: wrong shape of hidden-to-hidden weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWhShape).c_str(), ShapeUtils::shapeAsString(WhShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WcShapeInfo, correctWcShape), 0,
"LSTMCELL operation: wrong shape of diagonal weights for peephole connections, expected is %s, but got "
"%s instead !",
ShapeUtils::shapeAsString(correctWcShape).c_str(), ShapeUtils::shapeAsString(WcShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WpShapeInfo, correctWpShape), 0,
"LSTMCELL operation: wrong shape of projection weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWpShape).c_str(), ShapeUtils::shapeAsString(WpShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, correctBShape), 0,
"LSTMCELL operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctBShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
// evaluate output shapeInfos
LongType *hShapeInfo(nullptr), *cShapeInfo(nullptr);
ALLOCATE(hShapeInfo, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [bS x numProj]
ALLOCATE(cShapeInfo, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [bS x numUnits]
hShapeInfo[0] = cShapeInfo[0] = rank;
hShapeInfo[1] = cShapeInfo[1] = bS;
hShapeInfo[2] = numProj;
cShapeInfo[2] = numUnits;
ShapeUtils::updateStridesAndType(hShapeInfo, xtShapeInfo, shape::order(ht_1ShapeInfo));
ShapeUtils::updateStridesAndType(cShapeInfo, xtShapeInfo, shape::order(ct_1ShapeInfo));
auto result = SHAPELIST(ConstantShapeHelper::getInstance().bufferForShapeInfo(hShapeInfo)->primary(),
ConstantShapeHelper::getInstance().bufferForShapeInfo(cShapeInfo)->primary());
RELEASE(hShapeInfo, block.workspace());
RELEASE(cShapeInfo, block.workspace());
return result;
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,967 @@
/*
* ******************************************************************************
* *
* *
* * 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 <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_lstmLayer)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/lstmLayer.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(lstmLayer, 3, 1, false, 1, 5) {
// equations (no peephole connections)
// it = σ(Wxi * xt + Wri * ht-1 + bi)
// ft = σ(Wxf * xt + Wrf * ht-1 + bf)
// c't = tanh(Wxc * xt + Wrc * ht-1 + bc)
// ct = ft ◦ ct-1 + it ◦ c't
// ot = σ(Wxo * xt + Wro * ht-1 + bo)
// ht = ot ◦ tanh(ct)
// equations (peephole connections are present)
// it = σ(Wxi * xt + Wri * ht-1 + Wpi ◦ ct-1 + bi)
// ft = σ(Wxf * xt + Wrf * ht-1 + Wpf ◦ ct-1 + bf)
// c't = tanh(Wxc * xt + Wrc * ht-1 + bc)
// ct = clip(ft ◦ ct-1 + it ◦ c't)
// ot = σ(Wxo * xt + Wro * ht-1 + Wpo ◦ ct + bo)
// ht = ot ◦ tanh(ct)
// notations:
// bS - batch size
// sL - sequence length, number of time steps
// nIn - input size
// nOut - output size (hidden size)
// INPUTS:
// *******
// input x:
// 1) [sL, bS, nIn] when dataFormat == 0
// 2) [bS, sL, nIn] when dataFormat == 1
// 3) [bS, nIn, sL] when dataFormat == 2
// *******
// input weights Wx:
// 1) [nIn, 4*nOut] when directionMode < 2
// 2) [2, nIn, 4*nOut] when directionMode >= 2
// *******
// recurrent weights Wr:
// 1) [nOut, 4*nOut] when directionMode < 2
// 2) [2, nOut, 4*nOut] when directionMode >= 2
// *******
// peephole weights Wp, optional:
// 1) [3*nOut] when directionMode < 2
// 2) [2, 3*nOut] when directionMode >= 2
// *******
// biases b, optional:
// 1) [4*nOut] when directionMode < 2
// 2) [2, 4*nOut] when directionMode >= 2
// *******
// sequence length array seqLen, optional:
// 1) [bS]
// *******
// initial output hI, optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// *******
// initial cell state cI (same shape as in hI), optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// OUTPUTS:
// *******
// output h, optional:
// 1) [sL, bS, nOut] when directionMode <= 2 && dataFormat == 0
// 2) [bS, sL, nOut] when directionMode <= 2 && dataFormat == 1
// 3) [bS, nOut, sL] when directionMode <= 2 && dataFormat == 2
// 4) [sL, bS, 2*nOut] when directionMode == 3 && dataFormat == 0
// 5) [bS, sL, 2*nOut] when directionMode == 3 && dataFormat == 1
// 6) [bS, 2*nOut, sL] when directionMode == 3 && dataFormat == 2
// 7) [sL, 2, bS, nOut] when directionMode == 4 && dataFormat == 3
// *******
// output at last step hL, optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// *******
// cell state at last step cL (same shape as in hL), optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// !!! dimension 4*nOut implies order it, ft, c't, ot
// !!! dimension 3*nOut implies order it, ft, ot
const auto dataFormat = INT_ARG(0); // for unidirectional: 0 = [sL, bS, nIn], 1 = [bS, sL ,nIn], 2 = [bS, nIn, sL],
// for bidirectional: 3 = [sL, bS, nIn] && [sL, 2, bS, nOut] (for ONNX)
const auto directionMode = INT_ARG(1);
// direction: 0 = fwd, 1 = bwd, 2 = bidirectional sum, 3 = bidirectional concat, 4 = bidirectional
// extra output dim (in conjunction with format dataFormat = 3)
// integer numbers corresponding to activations: 0=tanh, 1=relu, 2=sigmoid, 3=affine, 4=leaky relu, 5= thresholded
// relu, 6=scaled tanh, 7=hard sigmoid, 8=ELU, 9=softsign, 10=softplus
const auto gateAct = INT_ARG(2); // activation for input (i), forget (f) and output (o) gates
const auto cellAct = INT_ARG(3); // activation for cell state (c)
const auto outAct = INT_ARG(4); // activation for output (h)
const auto hasBiases = B_ARG(0); // indicates whether biases array is provided
const auto hasSeqLen = B_ARG(1); // indicates whether seqLen array is provided
const auto hasInitH = B_ARG(2); // indicates whether initial output is provided
const auto hasInitC = B_ARG(3); // indicates whether initial cell state is provided
const auto hasPH = B_ARG(4); // indicates whether peephole connections are present
const auto retFullSeq = B_ARG(5); // indicates whether to return whole time sequence h {h_0, h_1, ... , h_sL-1}
const auto retLastH = B_ARG(6); // indicates whether to return output at last time step only
const auto retLastC = B_ARG(7); // indicates whether to return cells state at last time step only
const auto gateActHasAlpha = gateAct == 3 || gateAct == 4 || gateAct == 5 || gateAct == 6 || gateAct == 8;
const auto cellActHasAlpha = cellAct == 3 || cellAct == 4 || cellAct == 5 || cellAct == 6 || cellAct == 8;
const auto outActHasAlpha = outAct == 3 || outAct == 4 || outAct == 5 || outAct == 6 || outAct == 8;
const auto gateActHasBeta = gateAct == 3 || gateAct == 6;
const auto cellActHasBeta = cellAct == 3 || cellAct == 6;
const auto outActHasBeta = outAct == 3 || outAct == 6;
LongType count = 1;
const auto cellClip = T_ARG(0); // cell clipping value, if it = 0 then do not apply clipping
const auto gateAlpha = gateActHasAlpha ? T_ARG(count++) : 0;
const auto gateBeta = gateActHasBeta ? T_ARG(count++) : 0;
const auto cellAlpha = cellActHasAlpha ? T_ARG(count++) : 0;
const auto cellBeta = cellActHasBeta ? T_ARG(count++) : 0;
const auto outAlpha = outActHasAlpha ? T_ARG(count++) : 0;
const auto outBeta = outActHasBeta ? T_ARG(count++) : 0;
const auto x = INPUT_VARIABLE(0); // input
const auto Wx = INPUT_VARIABLE(1); // input weights
const auto Wr = INPUT_VARIABLE(2); // recurrent weights
count = 3;
const auto b = hasBiases ? INPUT_VARIABLE(count++) : nullptr; // biases
const auto seqLen = hasSeqLen ? INPUT_VARIABLE(count++) : nullptr; // seqLen vector
const auto hI = hasInitH ? INPUT_VARIABLE(count++) : nullptr; // initial output
const auto cI = hasInitC ? INPUT_VARIABLE(count++) : nullptr; // initial cell state
const auto Wp = hasPH ? INPUT_VARIABLE(count++) : nullptr; // peephole weights
REQUIRE_TRUE(dataFormat < 3 || (dataFormat == 3 && directionMode == 4), 0,
"LSTM_LAYER operation: if argument dataFormat = 3, then directionMode = 4, but got dataFormat = %i and "
"directionMode = %i instead !",
dataFormat, directionMode);
REQUIRE_TRUE(cellClip >= 0, 0, "LSTM_LAYER operation: cell clipping value should be nonnegative (>=0) !");
REQUIRE_TRUE(retFullSeq || retLastH || retLastC, 0,
"LSTM_LAYER operation: please specify what output arrays to produce !");
count = 0;
auto h = retFullSeq ? OUTPUT_VARIABLE(count++) : nullptr; // output
auto hL = retLastH ? OUTPUT_VARIABLE(count++) : nullptr; // output at last step
auto cL = retLastC ? OUTPUT_VARIABLE(count++) : nullptr; // cell state at last step
// evaluate dimensions
const LongType sL = dataFormat == 3 ? x->sizeAt(0) : x->sizeAt(dataFormat);
const LongType bS = dataFormat == 1 || dataFormat == 2 ? x->sizeAt(0) : x->sizeAt(1);
const LongType nIn = dataFormat == 2 ? x->sizeAt(1) : x->sizeAt(2);
const LongType nOut = Wx->sizeAt(-1) / 4;
// inputs validations
if (directionMode < 2) { // no bidirectional
// Wx validation
if (Wx->rankOf() != 2 || Wx->sizeAt(0) != nIn)
REQUIRE_TRUE(false, 0, "LSTM_LAYER operation: wrong shape of input weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({nIn, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
// Wr validation
if (Wr->rankOf() != 2 || Wr->sizeAt(0) != nOut || Wr->sizeAt(1) != 4 * nOut)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER operation: wrong shape of recurrent weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({nOut, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wr).c_str());
// biases validation
if (b != nullptr && (b->rankOf() != 1 || b->sizeAt(0) != 4 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({4 * nOut}).c_str(), ShapeUtils::shapeAsString(b).c_str());
// initial output validation
if (hI != nullptr && (hI->rankOf() != 2 || hI->sizeAt(0) != bS || hI->sizeAt(1) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER operation: wrong shape of initial output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({bS, nOut}).c_str(), ShapeUtils::shapeAsString(hI).c_str());
// initial cell validation
if (cI != nullptr && (cI->rankOf() != 2 || cI->sizeAt(0) != bS || cI->sizeAt(1) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({bS, nOut}).c_str(), ShapeUtils::shapeAsString(cI).c_str());
// peephole weights validation
if (Wp != nullptr && (Wp->rankOf() != 1 || Wp->sizeAt(0) != 3 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER operation: wrong peephole weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({3 * nOut}).c_str(), ShapeUtils::shapeAsString(Wp).c_str());
} else { // bidirectional
// Wx validation
if (Wx->rankOf() != 3 || Wx->sizeAt(0) != 2 || Wx->sizeAt(1) != nIn)
REQUIRE_TRUE(false, 0, "LSTM_LAYER operation: wrong shape of input weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, nIn, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
// Wr validation
if (Wr->rankOf() != 3 || Wr->sizeAt(0) != 2 || Wr->sizeAt(1) != nOut || Wr->sizeAt(2) != 4 * nOut)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER operation: wrong shape of recurrent weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, nOut, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wr).c_str());
// biases validation
if (b != nullptr && (b->rankOf() != 2 || b->sizeAt(0) != 2 || b->sizeAt(1) != 4 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(b).c_str());
// initial output validation
if (hI != nullptr && (hI->rankOf() != 3 || hI->sizeAt(0) != 2 || hI->sizeAt(1) != bS || hI->sizeAt(2) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER operation: wrong shape of initial output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, bS, nOut}).c_str(), ShapeUtils::shapeAsString(hI).c_str());
// initial cell validation
if (cI != nullptr && (cI->rankOf() != 3 || cI->sizeAt(0) != 2 || cI->sizeAt(1) != bS || cI->sizeAt(2) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, bS, nOut}).c_str(), ShapeUtils::shapeAsString(cI).c_str());
// peephole weights validation
if (Wp != nullptr && (Wp->rankOf() != 2 || Wp->sizeAt(0) != 2 || Wp->sizeAt(1) != 3 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER operation: wrong peephole weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, 3 * nOut}).c_str(), ShapeUtils::shapeAsString(Wp).c_str());
}
std::vector<float> params = {
static_cast<float>(dataFormat), static_cast<float>(directionMode), static_cast<float>(cellClip),
static_cast<float>(gateAct), static_cast<float>(gateAlpha), static_cast<float>(gateBeta),
static_cast<float>(cellAct), static_cast<float>(cellAlpha), static_cast<float>(cellBeta),
static_cast<float>(outAct), static_cast<float>(outAlpha), static_cast<float>(outBeta)};
if (directionMode == 0) { // forward
helpers::lstmLayerTimeLoop(x, Wx, Wr, b, seqLen, hI, cI, Wp, params, true, h, hL, cL);
} else if (directionMode == 1) { // backward
helpers::lstmLayerTimeLoop(x, Wx, Wr, b, seqLen, hI, cI, Wp, params, false, h, hL, cL);
} else { // bidirectional
NDArray *WxFwd = (*Wx)({0, 1, 0, 0, 0, 0});
NDArray *WxBwd = (*Wx)({1, 2, 0, 0, 0, 0});
NDArray *WrFwd = (*Wr)({0, 1, 0, 0, 0, 0});
NDArray *WrBwd = (*Wr)({1, 2, 0, 0, 0, 0});
NDArray *WpFwd(nullptr), *WpBwd(nullptr), *bFwd(nullptr), *bBwd(nullptr), *hIFwd(nullptr), *hIBwd(nullptr),
*cIFwd(nullptr), *cIBwd(nullptr), *hLFwd(nullptr), *hLBwd(nullptr), *cLFwd(nullptr), *cLBwd(nullptr),
*hFwd(nullptr), *hBwd(nullptr);
if (Wp) {
WpFwd = (*Wp)({0, 1, 0, 0});
WpBwd = (*Wp)({1, 2, 0, 0});
}
if (b) {
bFwd = (*b)({0, 1, 0, 0});
bBwd = (*b)({1, 2, 0, 0});
}
if (hI) {
hIFwd = (*hI)({0, 1, 0, 0, 0, 0});
hIBwd = (*hI)({1, 2, 0, 0, 0, 0});
}
if (cI) {
cIFwd =(*cI)({0, 1, 0, 0, 0, 0});
cIBwd = (*cI)({1, 2, 0, 0, 0, 0});
}
if (hL) {
hLFwd = (*hL)({0, 1, 0, 0, 0, 0});
hLBwd = (*hL)({1, 2, 0, 0, 0, 0});
}
if (cL) {
cLFwd = (*cL)({0, 1, 0, 0, 0, 0});
cLBwd = (*cL)({1, 2, 0, 0, 0, 0});
}
if (h) {
if (directionMode == 2) { // sum
hFwd = h;
hBwd = new NDArray(h, false, h->getContext());
} else if (directionMode == 3) { // concat
hFwd = dataFormat <= 1 ? (*h)({0, 0, 0, 0, 0, nOut}) : (*h)({0, 0, 0, nOut, 0, 0});
hBwd = dataFormat <= 1 ? (*h)({0, 0, 0, 0, nOut, 2 * nOut}) : (*h)({0, 0, nOut, 2 * nOut, 0, 0});
} else { // directionMode == 4
hFwd = (*h)({0, 0, 0, 1, 0, 0, 0, 0});
hBwd = (*h)({0, 0, 1, 2, 0, 0, 0, 0});
}
}
// FIXME - following two calls are independent and may run in different streams
helpers::lstmLayerTimeLoop(x, WxFwd, WrFwd, bFwd, seqLen, hIFwd, cIFwd, WpFwd, params, true, hFwd, hLFwd, cLFwd);
helpers::lstmLayerTimeLoop(x, WxBwd, WrBwd, bBwd, seqLen, hIBwd, cIBwd, WpBwd, params, false, hBwd, hLBwd, cLBwd);
if (h && directionMode == 2) *h += *hBwd;
delete WpFwd;
delete WpBwd;
delete bFwd;
delete bBwd;
delete hIFwd;
delete hIBwd;
delete cIFwd;
delete cIBwd;
delete hLFwd;
delete hLBwd;
delete cLFwd;
delete cLBwd;
delete hBwd;
delete WxFwd;
delete WxBwd;
delete WrFwd;
delete WrBwd;
if (hFwd != h) delete hFwd;
}
return Status::OK;
}
DECLARE_TYPES(lstmLayer) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(lstmLayer) {
const auto dataFormat = INT_ARG(0); // for unidirectional: 0 = [sL, bS, nIn], 1 = [bS, sL ,nIn], 2 = [bS, nIn, sL],
// for bidirectional: 3 = [sL, 2, bS, nIn] (for ONNX)
const auto directionMode = INT_ARG(1); // direction: 0 = fwd, 1 = bwd, 2 = bidirectional sum, 3 = bidirectional
// concat, 4 = bidirectional extra output dim
const auto retFullSeq = B_ARG(5); // indicates whether to return whole h {h_0, h_1, ... , h_sL-1}, if true, format
// would be [sL,bS,nOut] (exact shape depends on dataFormat argument)
const auto retLastH = B_ARG(6); // indicates whether to return output at last time step only, in this case shape
// would be [bS, nOut] (exact shape depends on dataFormat argument)
const auto retLastC = B_ARG(7); // indicates whether to return cells state at last time step only, in this case shape
// would be [bS, nOut] (exact shape depends on dataFormat argument)
const auto x = INPUT_VARIABLE(0); // input
const auto Wx = INPUT_VARIABLE(1); // input weights
const auto Wr = INPUT_VARIABLE(2); // recurrent weights
// evaluate dimensions
const LongType sL = dataFormat == 3 ? x->sizeAt(0) : x->sizeAt(dataFormat);
const LongType bS = dataFormat == 1 || dataFormat == 2 ? x->sizeAt(0) : x->sizeAt(1);
const LongType nIn = dataFormat == 2 ? x->sizeAt(1) : x->sizeAt(2);
const LongType nOut = Wx->sizeAt(-1) / 4;
DataType type;
if (x->isR())
type = x->dataType();
else
type = FLOAT32;
auto shapes = SHAPELIST();
// evaluate h shape (output)
if (retFullSeq) {
std::vector<LongType> hShape;
if (directionMode <= 2) { // single direction or bidirectional with sum
if (dataFormat == 0)
hShape = {sL, bS, nOut};
else if (dataFormat == 1)
hShape = {bS, sL, nOut};
else if (dataFormat == 2)
hShape = {bS, nOut, sL};
} else if (directionMode == 3) { // bidirectional with concat
if (dataFormat == 0)
hShape = {sL, bS, 2 * nOut};
else if (dataFormat == 1)
hShape = {bS, sL, 2 * nOut};
else if (dataFormat == 2)
hShape = {bS, 2 * nOut, sL};
} else { // bidirectional with extra output dimension equal to 2
hShape = {sL, 2, bS, nOut};
}
shapes->push_back(ConstantShapeHelper::getInstance().createShapeInfo(type, x->ordering(), hShape));
}
// evaluate hL shape (output at last step)
if (retLastH) {
std::vector<LongType> hLShape;
if (directionMode < 2)
hLShape = {bS, nOut};
else
hLShape = {2, bS, nOut};
shapes->push_back(ConstantShapeHelper::getInstance().createShapeInfo(type, x->ordering(), hLShape));
if (retLastC) // cL and hL have same shapes
shapes->push_back(shapes->at(shapes->size() - 1));
}
// evaluate cL shape (cell state at last step)
if (retLastC && !retLastH) {
std::vector<LongType> cLShape;
if (directionMode < 2)
cLShape = {bS, nOut};
else
cLShape = {2, bS, nOut};
shapes->push_back(ConstantShapeHelper::getInstance().createShapeInfo(type, x->ordering(), cLShape));
}
return shapes;
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(lstmLayer_bp, 4, 1, false, 1, 5) {
// equations (no peephole connections)
// it = σ(Wxi * xt + Wri * ht-1 + bi)
// ft = σ(Wxf * xt + Wrf * ht-1 + bf)
// c't = tanh(Wxc * xt + Wrc * ht-1 + bc)
// ct = ft ◦ ct-1 + it ◦ c't
// ot = σ(Wxo * xt + Wro * ht-1 + bo)
// ht = ot ◦ tanh(ct)
// equations (peephole connections are present)
// it = σ(Wxi * xt + Wri * ht-1 + Wpi ◦ ct-1 + bi)
// ft = σ(Wxf * xt + Wrf * ht-1 + Wpf ◦ ct-1 + bf)
// c't = tanh(Wxc * xt + Wrc * ht-1 + bc)
// ct = clip(ft ◦ ct-1 + it ◦ c't)
// ot = σ(Wxo * xt + Wro * ht-1 + Wpo ◦ ct + bo)
// ht = ot ◦ tanh(ct)
// notations:
// bS - batch size
// sL - sequence length, number of time steps
// nIn - input size
// nOut - output size (hidden size)
// INPUTS:
// *******
// input x:
// 1) [sL, bS, nIn] when dataFormat == 0
// 2) [bS, sL, nIn] when dataFormat == 1
// 3) [bS, nIn, sL] when dataFormat == 2
// *******
// input weights Wx:
// 1) [nIn, 4*nOut] when directionMode < 2
// 2) [2, nIn, 4*nOut] when directionMode >= 2
// *******
// recurrent weights Wr:
// 1) [nOut, 4*nOut] when directionMode < 2
// 2) [2, nOut, 4*nOut] when directionMode >= 2
// *******
// peephole weights Wp, optional:
// 1) [3*nOut] when directionMode < 2
// 2) [2, 3*nOut] when directionMode >= 2
// *******
// biases b, optional:
// 1) [4*nOut] when directionMode < 2
// 2) [2, 4*nOut] when directionMode >= 2
// *******
// sequence length array seqLen, optional:
// 1) [bS]
// *******
// initial output hI, optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// *******
// initial cell state cI (same shape as in hI), optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// *******
// gradient vs. output dLdh, optional:
// 1) [sL, bS, nOut] when directionMode <= 2 && dataFormat == 0
// 2) [bS, sL, nOut] when directionMode <= 2 && dataFormat == 1
// 3) [bS, nOut, sL] when directionMode <= 2 && dataFormat == 2
// 4) [sL, bS, 2*nOut] when directionMode == 3 && dataFormat == 0
// 5) [bS, sL, 2*nOut] when directionMode == 3 && dataFormat == 1
// 6) [bS, 2*nOut, sL] when directionMode == 3 && dataFormat == 2
// 7) [sL, 2, bS, nOut] when directionMode == 4 && dataFormat == 3
// *******
// gradient vs output at last time step dLdhL, optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// *******
// gradient vs cell state at last time step dLdcL(same shape as in dLdhL), optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// OUTPUTS:
// *******
// gradient vs. input dLdx:
// 1) [sL, bS, nIn] when dataFormat == 0
// 2) [bS, sL, nIn] when dataFormat == 1
// 3) [bS, nIn, sL] when dataFormat == 2
// *******
// gradient vs. input weights dLdWx:
// 1) [nIn, 4*nOut] when directionMode < 2
// 2) [2, nIn, 4*nOut] when directionMode >= 2
// *******
// gradient vs. recurrent weights dLdWr:
// 1) [nOut, 4*nOut] when directionMode < 2
// 2) [2, nOut, 4*nOut] when directionMode >= 2
// *******
// gradient vs. peephole weights dLdWp, optional:
// 1) [3*nOut] when directionMode < 2
// 2) [2, 3*nOut] when directionMode >= 2
// *******
// gradient vs. biases dLdb, optional:
// 1) [4*nOut] when directionMode < 2
// 2) [2, 4*nOut] when directionMode >= 2
// gradient vs. sequence length array dLdsL, optional (do not calculate it!!!):
// 1) [bS] always
// *******
// gradient vs. initial output dLdhI, optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// *******
// gradient vs. initial cell state dLdcI (same shape as in dLdhI), optional:
// 1) [bS, nOut] when directionMode < 2
// 2) [2, bS, nOut] when directionMode >= 2
// !!! dimension 4*nOut implies order it, ft, c't, ot
// !!! dimension 3*nOut implies order it, ft, ot
const auto dataFormat = INT_ARG(0); // for unidirectional: 0 = [sL, bS, nIn], 1 = [bS, sL ,nIn], 2 = [bS, nIn, sL],
// for bidirectional: 3 = [sL, bS, nIn] && [sL, 2, bS, nOut] (for ONNX)
const auto directionMode = INT_ARG(1);
// direction: 0 = fwd, 1 = bwd, 2 = bidirectional sum, 3 = bidirectional concat, 4 = bidirectional
// extra output dim (in conjunction with format dataFormat = 3)
// integer numbers corresponding to activations: 0=tanh, 1=relu, 2=sigmoid, 3=affine, 4=leaky relu, 5= thresholded
// relu, 6=scaled tanh, 7=hard sigmoid, 8=ELU, 9=softsign, 10=softplus
const auto gateAct = INT_ARG(2); // activation for input (i), forget (f) and output (o) gates
const auto cellAct = INT_ARG(3); // activation for cell state (c)
const auto outAct = INT_ARG(4); // activation for output (h)
const auto hasBiases = B_ARG(0); // indicates whether biases array is provided
const auto hasSeqLen = B_ARG(1); // indicates whether seqLen array is provided
const auto hasInitH = B_ARG(2); // indicates whether initial output is provided
const auto hasInitC = B_ARG(3); // indicates whether initial cell state is provided
const auto hasPH = B_ARG(4); // indicates whether peephole connections are present
const auto retFullSeq = B_ARG(5); // indicates whether gradient vs. outputs is given for whole time sequence dLdh
// {dLdh_0, dLdh_1, ... , dLdh_sL-1}
const auto retLastH = B_ARG(6); // indicates whether gradient vs. output at last time step (dLdhL) is given
const auto retLastC = B_ARG(7); // indicates whether gradient vs. cell state at last time step (dLdcL) is given
const auto gateActHasAlpha = gateAct == 3 || gateAct == 4 || gateAct == 5 || gateAct == 6 || gateAct == 8;
const auto cellActHasAlpha = cellAct == 3 || cellAct == 4 || cellAct == 5 || cellAct == 6 || cellAct == 8;
const auto outActHasAlpha = outAct == 3 || outAct == 4 || outAct == 5 || outAct == 6 || outAct == 8;
const auto gateActHasBeta = gateAct == 3 || gateAct == 6;
const auto cellActHasBeta = cellAct == 3 || cellAct == 6;
const auto outActHasBeta = outAct == 3 || outAct == 6;
LongType count = 1;
const auto cellClip = T_ARG(0); // cell clipping value, if it = 0 then do not apply clipping
const auto gateAlpha = gateActHasAlpha ? T_ARG(count++) : 0;
const auto gateBeta = gateActHasBeta ? T_ARG(count++) : 0;
const auto cellAlpha = cellActHasAlpha ? T_ARG(count++) : 0;
const auto cellBeta = cellActHasBeta ? T_ARG(count++) : 0;
const auto outAlpha = outActHasAlpha ? T_ARG(count++) : 0;
const auto outBeta = outActHasBeta ? T_ARG(count++) : 0;
REQUIRE_TRUE(dataFormat < 3 || (dataFormat == 3 && directionMode == 4), 0,
"LSTM_LAYER_BP operation: if argument dataFormat = 3, then directionMode = 4, but got dataFormat = %i "
"and directionMode = %i instead !",
dataFormat, directionMode);
REQUIRE_TRUE(cellClip >= 0, 0, "LSTM_LAYER_BP operation: cell clipping value should be nonnegative (>=0) !");
REQUIRE_TRUE(
retFullSeq || retLastH || retLastC, 0,
"LSTM_LAYER_BP operation: please specify at least one of three input gradient arrays: dLdh, dLdhL or dLdcL !");
const auto x = INPUT_VARIABLE(0); // input
const auto Wx = INPUT_VARIABLE(1); // input weights
const auto Wr = INPUT_VARIABLE(2); // recurrent weights
// evaluate dimensions
const LongType sL = dataFormat == 3 ? x->sizeAt(0) : x->sizeAt(dataFormat);
const LongType bS = dataFormat == 1 || dataFormat == 2 ? x->sizeAt(0) : x->sizeAt(1);
const LongType nIn = dataFormat == 2 ? x->sizeAt(1) : x->sizeAt(2);
const LongType nOut = Wx->sizeAt(-1) / 4;
// continue with input
count = 3;
const auto b = hasBiases ? INPUT_VARIABLE(count++) : nullptr; // biases
const auto seqLen = hasSeqLen ? INPUT_VARIABLE(count++) : nullptr; // seqLen vector
const auto hI = hasInitH ? INPUT_VARIABLE(count++) : nullptr; // initial output
const auto cI = hasInitC ? INPUT_VARIABLE(count++) : nullptr; // initial cell state
const auto Wp = hasPH ? INPUT_VARIABLE(count++) : nullptr; // peephole weights
NDArray *dLdh = nullptr;
NDArray *dLdhL = nullptr;
NDArray *dLdcL = nullptr;
std::unique_ptr<NDArray> temp_dLdh, temp_dLdhL, temp_dLdcL;
std::vector<LongType> expdLdhShape;
// gradient vs. output
if (retFullSeq) {
int factor = directionMode <= 2 ? 1 : 2;
if (dataFormat == 0)
expdLdhShape = std::vector<LongType>{sL, bS, factor * nOut};
else if (dataFormat == 1)
expdLdhShape = std::vector<LongType>{bS, sL, factor * nOut};
else if (dataFormat == 2)
expdLdhShape = std::vector<LongType>{bS, factor * nOut, sL};
else
expdLdhShape = std::vector<LongType>{sL, 2, bS, nOut};
dLdh = INPUT_VARIABLE(count++);
if (dLdh->isScalar()) {
temp_dLdh.reset(NDArrayFactory::valueOf(expdLdhShape, *dLdh, x->ordering()));
dLdh = temp_dLdh.get();
}
}
// gradient vs. output at last time step
if (retLastH) {
dLdhL = INPUT_VARIABLE(count++);
if (dLdhL->isScalar()) {
std::vector<sd::LongType> shape = directionMode < 2 ? std::vector<LongType>{bS, nOut} : std::vector<LongType>{2, bS, nOut};
temp_dLdhL.reset(NDArrayFactory::valueOf(
shape, *dLdhL,
x->ordering()));
// refresh
dLdhL = temp_dLdhL.get();
}
}
// gradient vs. cell state at last time step
if (retLastC) {
dLdcL = INPUT_VARIABLE(count++);
if (dLdcL->isScalar()) {
std::vector<sd::LongType> shape = directionMode < 2 ? std::vector<LongType>{bS, nOut} : std::vector<LongType>{2, bS, nOut};
temp_dLdcL.reset(NDArrayFactory::valueOf(
shape, *dLdcL,
x->ordering()));
// refresh
dLdcL = temp_dLdcL.get();
}
}
count = 3;
auto dLdx = OUTPUT_VARIABLE(0); // gradient vs. input
auto dLdWx = OUTPUT_NULLIFIED(1); // gradient vs. input weights
auto dLdWr = OUTPUT_NULLIFIED(2); // gradient vs. recurrent weights
auto dLdb = hasBiases ? OUTPUT_NULLIFIED(count++) : nullptr; // gradient vs. biases
auto dLdsL = hasSeqLen ? INPUT_VARIABLE(count++) : nullptr; // gradient vs. seqLen vector, we don't calculate it !!!
auto dLdhI = hasInitH ? OUTPUT_NULLIFIED(count++) : nullptr; // gradient vs. initial output
auto dLdcI = hasInitC ? OUTPUT_NULLIFIED(count++) : nullptr; // gradient vs. initial cell state
auto dLdWp = hasPH ? OUTPUT_NULLIFIED(count) : nullptr; // gradient vs. peephole weights
// inputs validations
if (directionMode < 2) { // no bidirectional
// Wx validation
if (Wx->rankOf() != 2 || Wx->sizeAt(0) != nIn)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of input weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({nIn, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
// Wr validation
if (Wr->rankOf() != 2 || Wr->sizeAt(0) != nOut || Wr->sizeAt(1) != 4 * nOut)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of recurrent weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({nOut, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wr).c_str());
// biases validation
if (b != nullptr && (b->rankOf() != 1 || b->sizeAt(0) != 4 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER_BP operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({4 * nOut}).c_str(), ShapeUtils::shapeAsString(b).c_str());
// initial output validation
if (hI != nullptr && (hI->rankOf() != 2 || hI->sizeAt(0) != bS || hI->sizeAt(1) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of initial output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({bS, nOut}).c_str(), ShapeUtils::shapeAsString(hI).c_str());
// initial cell validation
if (cI != nullptr && (cI->rankOf() != 2 || cI->sizeAt(0) != bS || cI->sizeAt(1) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({bS, nOut}).c_str(), ShapeUtils::shapeAsString(cI).c_str());
// peephole weights validation
if (Wp != nullptr && (Wp->rankOf() != 1 || Wp->sizeAt(0) != 3 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER_BP operation: wrong peephole weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({3 * nOut}).c_str(), ShapeUtils::shapeAsString(Wp).c_str());
// gradient vs. output at last time step validation
if (dLdhL != nullptr && (dLdhL->rankOf() != 2 || dLdhL->sizeAt(0) != bS || dLdhL->sizeAt(1) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of gradient vs. output at last time step, expected is %s, but "
"got %s instead !",
ShapeUtils::shapeAsString({bS, nOut}).c_str(), ShapeUtils::shapeAsString(dLdhL).c_str());
// gradient vs. cell state at last time step validation
if (dLdcL != nullptr && (dLdcL->rankOf() != 2 || dLdcL->sizeAt(0) != bS || dLdcL->sizeAt(1) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of gradient vs. cell state at last time, expected is %s, but "
"got %s instead !",
ShapeUtils::shapeAsString({bS, nOut}).c_str(), ShapeUtils::shapeAsString(dLdcL).c_str());
} else { // bidirectional
// Wx validation
if (Wx->rankOf() != 3 || Wx->sizeAt(0) != 2 || Wx->sizeAt(1) != nIn)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of input weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, nIn, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
// Wr validation
if (Wr->rankOf() != 3 || Wr->sizeAt(0) != 2 || Wr->sizeAt(1) != nOut || Wr->sizeAt(2) != 4 * nOut)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of recurrent weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, nOut, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wr).c_str());
// biases validation
if (b != nullptr && (b->rankOf() != 2 || b->sizeAt(0) != 2 || b->sizeAt(1) != 4 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER_BP operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(b).c_str());
// initial output validation
if (hI != nullptr && (hI->rankOf() != 3 || hI->sizeAt(0) != 2 || hI->sizeAt(1) != bS || hI->sizeAt(2) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of initial output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, bS, nOut}).c_str(), ShapeUtils::shapeAsString(hI).c_str());
// initial cell validation
if (cI != nullptr && (cI->rankOf() != 3 || cI->sizeAt(0) != 2 || cI->sizeAt(1) != bS || cI->sizeAt(2) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, bS, nOut}).c_str(), ShapeUtils::shapeAsString(cI).c_str());
// peephole weights validation
if (Wp != nullptr && (Wp->rankOf() != 2 || Wp->sizeAt(0) != 2 || Wp->sizeAt(1) != 3 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER_BP operation: wrong peephole weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({2, 3 * nOut}).c_str(), ShapeUtils::shapeAsString(Wp).c_str());
// gradient vs. output at last time step validation
if (dLdhL != nullptr &&
(dLdhL->rankOf() != 3 || dLdhL->sizeAt(0) != 2 || dLdhL->sizeAt(1) != bS || dLdhL->sizeAt(2) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of gradient vs. output at last time step, expected is %s, but "
"got %s instead !",
ShapeUtils::shapeAsString({2, bS, nOut}).c_str(), ShapeUtils::shapeAsString(dLdhL).c_str());
// gradient vs. cell state at last time step validation
if (dLdcL != nullptr &&
(dLdcL->rankOf() != 3 || dLdcL->sizeAt(0) != 2 || dLdcL->sizeAt(1) != bS || dLdcL->sizeAt(2) != nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_BP operation: wrong shape of gradient vs. cell state at last time, expected is %s, but "
"got %s instead !",
ShapeUtils::shapeAsString({2, bS, nOut}).c_str(), ShapeUtils::shapeAsString(dLdcL).c_str());
}
// gradient vs. output validation
if (dLdh) {
REQUIRE_TRUE(
dLdh->isSameShape(expdLdhShape), 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of gradient vs. output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(expdLdhShape).c_str(), ShapeUtils::shapeAsString(dLdh).c_str());
}
std::vector<float> params = {
static_cast<float>(dataFormat), static_cast<float>(directionMode), static_cast<float>(cellClip),
static_cast<float>(gateAct), static_cast<float>(gateAlpha), static_cast<float>(gateBeta),
static_cast<float>(cellAct), static_cast<float>(cellAlpha), static_cast<float>(cellBeta),
static_cast<float>(outAct), static_cast<float>(outAlpha), static_cast<float>(outBeta)};
if (directionMode == 0) { // forward
helpers::lstmLayerTimeLoopBp(x, Wx, Wr, b, seqLen, hI, cI, Wp, dLdh, dLdhL, dLdcL, params, true, dLdx, dLdWx, dLdWr,
dLdb, dLdhI, dLdcI, dLdWp);
} else if (directionMode == 1) { // backward
helpers::lstmLayerTimeLoopBp(x, Wx, Wr, b, seqLen, hI, cI, Wp, dLdh, dLdhL, dLdcL, params, false, dLdx, dLdWx,
dLdWr, dLdb, dLdhI, dLdcI, dLdWp);
} else { // bidirectional
NDArray *WxFwd = (*Wx)({0, 1, 0, 0, 0, 0});
NDArray *WxBwd = (*Wx)({1, 2, 0, 0, 0, 0});
NDArray *dLdWxFwd = (*dLdWx)({0, 1, 0, 0, 0, 0});
NDArray *dLdWxBwd = (*dLdWx)({1, 2, 0, 0, 0, 0});
NDArray *WrFwd = (*Wr)({0, 1, 0, 0, 0, 0});
NDArray *WrBwd = (*Wr)({1, 2, 0, 0, 0, 0});
NDArray *dLdWrFwd = (*dLdWr)({0, 1, 0, 0, 0, 0});
NDArray *dLdWrBwd = (*dLdWr)({1, 2, 0, 0, 0, 0});
NDArray *WpFwd(nullptr), *WpBwd(nullptr), *bFwd(nullptr), *bBwd(nullptr), *hIFwd(nullptr), *hIBwd(nullptr),
*cIFwd(nullptr), *cIBwd(nullptr), *dLdhFwd(nullptr), *dLdhBwd(nullptr), *dLdhLFwd(nullptr), *dLdhLBwd(nullptr),
*dLdcLFwd(nullptr), *dLdcLBwd(nullptr), *dLdWpFwd(nullptr), *dLdWpBwd(nullptr), *dLdbFwd(nullptr),
*dLdbBwd(nullptr), *dLdhIFwd(nullptr), *dLdhIBwd(nullptr), *dLdcIFwd(nullptr), *dLdcIBwd(nullptr);
if (Wp) {
WpFwd = (*Wp)({0, 1, 0, 0});
WpBwd = (*Wp)({1, 2, 0, 0});
dLdWpFwd = (*dLdWp)({0, 1, 0, 0});
dLdWpBwd = (*dLdWp)({1, 2, 0, 0});
}
if (b) {
bFwd = (*b)({0, 1, 0, 0});
bBwd = (*b)({1, 2, 0, 0});
dLdbFwd = (*dLdb)({0, 1, 0, 0});
dLdbBwd = (*dLdb)({1, 2, 0, 0});
}
if (hI) {
hIFwd = (*hI)({0, 1, 0, 0, 0, 0});
hIBwd = (*hI)({1, 2, 0, 0, 0, 0});
dLdhIFwd = (*dLdhI)({0, 1, 0, 0, 0, 0});
dLdhIBwd = (*dLdhI)({1, 2, 0, 0, 0, 0});
}
if (cI) {
cIFwd = (*cI)({0, 1, 0, 0, 0, 0});
cIBwd = (*cI)({1, 2, 0, 0, 0, 0});
dLdcIFwd = (*dLdcI)({0, 1, 0, 0, 0, 0});
dLdcIBwd = (*dLdcI)({1, 2, 0, 0, 0, 0});
}
if (dLdhL) {
dLdhLFwd = (*dLdhL)({0, 1, 0, 0, 0, 0});
dLdhLBwd = (*dLdhL)({1, 2, 0, 0, 0, 0});
}
if (dLdcL) {
dLdcLFwd = (*dLdcL)({0, 1, 0, 0, 0, 0});
dLdcLBwd = (*dLdcL)({1, 2, 0, 0, 0, 0});
}
if (dLdh) {
if (directionMode == 2) { // sum
dLdhFwd = dLdh;
dLdhBwd = dLdh;
} else if (directionMode == 3) { // concat
dLdhFwd = dataFormat <= 1 ? (*dLdh)({0, 0, 0, 0, 0, nOut}) : (*dLdh)({0, 0, 0, nOut, 0, 0});
dLdhBwd = dataFormat <= 1 ? (*dLdh)({0, 0, 0, 0, nOut, 2 * nOut})
: (*dLdh)({0, 0, nOut, 2 * nOut, 0, 0});
} else { // directionMode == 4
dLdhFwd = (*dLdh)({0, 0, 0, 1, 0, 0, 0, 0});
dLdhBwd = (*dLdh)({0, 0, 1, 2, 0, 0, 0, 0});
}
}
NDArray *dLdxBwd = dLdx->ulike();
// FIXME - following two calls are independent and may run in different streams
helpers::lstmLayerTimeLoopBp(x, WxFwd, WrFwd, bFwd, seqLen, hIFwd, cIFwd, WpFwd, dLdhFwd, dLdhLFwd, dLdcLFwd,
params, true, dLdx,dLdWxFwd, dLdWrFwd, dLdbFwd, dLdhIFwd, dLdcIFwd, dLdWpFwd);
helpers::lstmLayerTimeLoopBp(x, WxBwd, WrBwd, bBwd, seqLen, hIBwd, cIBwd, WpBwd, dLdhBwd, dLdhLBwd, dLdcLBwd,
params, false, dLdxBwd, dLdWxBwd, dLdWrBwd, dLdbBwd, dLdhIBwd, dLdcIBwd, dLdWpBwd);
*dLdx += *dLdxBwd;
delete WpFwd;
delete WpBwd;
delete bFwd;
delete bBwd;
delete hIFwd;
delete hIBwd;
delete cIFwd;
delete cIBwd;
delete dLdhLFwd;
delete dLdhLBwd;
delete dLdcLFwd;
delete dLdcLBwd;
delete dLdWpFwd;
delete dLdWpBwd;
delete dLdbFwd;
delete dLdbBwd;
delete dLdhIFwd;
delete dLdhIBwd;
delete dLdcIFwd;
delete dLdcIBwd;
delete WxFwd;
delete WxBwd;
delete dLdWxFwd;
delete dLdWxBwd;
delete dLdWrBwd;
delete WrFwd;
delete WrBwd;
delete dLdWrFwd;
if (!(dLdh && directionMode == 2)) {
delete dLdhFwd;
delete dLdhBwd;
}
if(directionMode > 2) {
delete dLdhFwd;
delete dLdhBwd;
}
}
return Status::OK;
}
DECLARE_TYPES(lstmLayer_bp) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(lstmLayer_bp) {
const auto hasBiases = B_ARG(0); // indicates whether biases array is provided
const auto hasSeqLen = B_ARG(1); // indicates whether seqLen array is provided
const auto hasInitH = B_ARG(2); // indicates whether initial output is provided
const auto hasInitC = B_ARG(3); // indicates whether initial cell state is provided
const auto hasPH = B_ARG(4); // indicates whether peephole connections are present
int count = 3;
const auto x = INPUT_VARIABLE(0); // input
const auto Wx = INPUT_VARIABLE(1); // input weights
const auto Wr = INPUT_VARIABLE(2); // recurrent weights
const auto b = hasBiases ? INPUT_VARIABLE(count++) : nullptr; // biases
const auto seqLen = hasSeqLen ? INPUT_VARIABLE(count++) : nullptr; // seqLen vector
const auto hI = hasInitH ? INPUT_VARIABLE(count++) : nullptr; // initial output
const auto cI = hasInitC ? INPUT_VARIABLE(count++) : nullptr; // initial cell state
const auto Wp = hasPH ? INPUT_VARIABLE(count++) : nullptr; // peephole weights
auto outShapes = SHAPELIST(x->shapeInfo(), Wx->shapeInfo(), Wr->shapeInfo());
if (b != nullptr) {
outShapes->push_back(b->shapeInfo());
}
if (seqLen != nullptr) {
outShapes->push_back(seqLen->shapeInfo());
}
if (hI != nullptr) {
outShapes->push_back(hI->shapeInfo());
}
if (cI != nullptr) {
outShapes->push_back(cI->shapeInfo());
}
if (Wp != nullptr) {
outShapes->push_back(Wp->shapeInfo());
}
return outShapes;
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,362 @@
/* ******************************************************************************
*
*
* 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 <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_lstmLayerCell)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/lstmLayer.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(lstmLayerCell, 5, 2, false, 1, 3) {
// equations (no peephole connections)
// it = σ(Wxi * xt + Wri * ht-1 + bi)
// ft = σ(Wxf * xt + Wrf * ht-1 + bf)
// c't = tanh(Wxc * xt + Wrc * ht-1 + bc)
// ct = ft ◦ ct-1 + it ◦ c't
// ot = σ(Wxo * xt + Wro * ht-1 + bo)
// ht = ot ◦ tanh(ct)
// equations (peephole connections are present)
// it = σ(Wxi * xt + Wri * ht-1 + Wpi ◦ ct-1 + bi)
// ft = σ(Wxf * xt + Wrf * ht-1 + Wpf ◦ ct-1 + bf)
// c't = tanh(Wxc * xt + Wrc * ht-1 + bc)
// ct = clip(ft ◦ ct-1 + it ◦ c't)
// ot = σ(Wxo * xt + Wro * ht-1 + Wpo ◦ ct + bo)
// ht = ot ◦ tanh(ct)
// notations:
// bS - batch size
// nIn - input size
// nOut - output size (hidden size)
// INPUTS:
// input x: [bS, nIn] or [nIn]
// input weights Wx: [nIn, 4*nOut]
// recurrent weights Wr: [nOut, 4*nOut]
// initial (previous) output hI: [bS, nOut] or [nOut]
// initial (previous) cell state cI: [bS, nOut] or [nOut]
// biases b (optional): [4*nOut]
// peephole weights Wp (optional): [3*nOut]
// OUTPUTS:
// current output h: [bS, nOut] or [nOut]
// current cell state c: [bS, nOut] or [nOut]
// !!! dimension 4*nOut implies order it, ft, c't, ot
// !!! dimension 3*nOut implies order it, ft, ot
// integer numbers corresponding to activations: 0=tanh, 1=relu, 2=sigmoid, 3=affine, 4=leaky relu, 5= thresholded
// relu, 6=scaled tanh, 7=hard sigmoid, 8=ELU, 9=softsign, 10=softplus
const auto gateAct = INT_ARG(0); // activation for input (i), forget (f) and output (o) gates
const auto cellAct = INT_ARG(1); // activation for cell state (c)
const auto outAct = INT_ARG(2); // activation for output (h)
const auto hasBiases = B_ARG(0); // indicates whether biases array is provided
const auto hasPH = B_ARG(1); // indicates whether peephole connections are present
const auto gateActHasAlpha = gateAct == 3 || gateAct == 4 || gateAct == 5 || gateAct == 6 || gateAct == 8;
const auto cellActHasAlpha = cellAct == 3 || cellAct == 4 || cellAct == 5 || cellAct == 6 || cellAct == 8;
const auto outActHasAlpha = outAct == 3 || outAct == 4 || outAct == 5 || outAct == 6 || outAct == 8;
const auto gateActHasBeta = gateAct == 3 || gateAct == 6;
const auto cellActHasBeta = cellAct == 3 || cellAct == 6;
const auto outActHasBeta = outAct == 3 || outAct == 6;
LongType count = 1;
const auto cellClip = T_ARG(0); // cell clipping value, if it = 0 then do not apply clipping
const auto gateAlpha = gateActHasAlpha ? T_ARG(count++) : 0;
const auto gateBeta = gateActHasBeta ? T_ARG(count++) : 0;
const auto cellAlpha = cellActHasAlpha ? T_ARG(count++) : 0;
const auto cellBeta = cellActHasBeta ? T_ARG(count++) : 0;
const auto outAlpha = outActHasAlpha ? T_ARG(count++) : 0;
const auto outBeta = outActHasBeta ? T_ARG(count++) : 0;
count = 3;
const auto x = INPUT_VARIABLE(0); // input
const auto Wx = INPUT_VARIABLE(1); // input weights
const auto Wr = INPUT_VARIABLE(2); // recurrent weights
const auto b = hasBiases ? INPUT_VARIABLE(count++) : nullptr; // biases
const auto hI = INPUT_VARIABLE(count++); // initial output
const auto cI = INPUT_VARIABLE(count++); // initial cell state
const auto Wp = hasPH ? INPUT_VARIABLE(count) : nullptr; // peephole weights
REQUIRE_TRUE(cellClip >= 0, 0, "LSTM_LAYER_CELL operation: cell clipping value should be nonnegative (>=0) !");
auto h = OUTPUT_VARIABLE(0);
auto c = OUTPUT_VARIABLE(1);
// evaluate dimensions
const LongType bS = x->rankOf() == 1 ? 0 : x->sizeAt(0);
const LongType nIn = x->sizeAt(-1);
const LongType nOut = Wx->sizeAt(-1) / 4;
// inputs validations
// Wx validation
if (Wx->rankOf() != 2 || Wx->sizeAt(0) != nIn)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_CELL operation: wrong shape of input weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({nIn, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
// Wr validation
if (Wr->rankOf() != 2 || Wr->sizeAt(0) != nOut || Wr->sizeAt(1) != 4 * nOut)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_CELL operation: wrong shape of recurrent weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({nOut, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wr).c_str());
// initial output/cell validation
std::vector<LongType> exphIcIShape =
x->rankOf() == 1 ? std::vector<LongType>{nOut} : std::vector<LongType>{bS, nOut};
REQUIRE_TRUE(hI->isSameShape(exphIcIShape), 0,
"LSTM_LAYER_CELL operation: wrong shape of initial output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(exphIcIShape).c_str(), ShapeUtils::shapeAsString(hI).c_str());
REQUIRE_TRUE(cI->isSameShape(exphIcIShape), 0,
"LSTM_LAYER_CELL operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(exphIcIShape).c_str(), ShapeUtils::shapeAsString(cI).c_str());
// biases validation
if (b != nullptr && (b->rankOf() != 1 || b->sizeAt(0) != 4 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER_CELL operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({4 * nOut}).c_str(), ShapeUtils::shapeAsString(b).c_str());
// peephole weights validation
if (Wp != nullptr && (Wp->rankOf() != 1 || Wp->sizeAt(0) != 3 * nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_CELL operation: wrong shape of peephole weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({3 * nOut}).c_str(), ShapeUtils::shapeAsString(Wp).c_str());
std::vector<float> params = {
static_cast<float>(0) /*ignore*/, static_cast<float>(0) /*ignore*/, static_cast<float>(cellClip),
static_cast<float>(gateAct), static_cast<float>(gateAlpha), static_cast<float>(gateBeta),
static_cast<float>(cellAct), static_cast<float>(cellAlpha), static_cast<float>(cellBeta),
static_cast<float>(outAct), static_cast<float>(outAlpha), static_cast<float>(outBeta)};
helpers::lstmLayerCell(x, Wx, Wr, b, hI, cI, Wp, params, h, c);
return Status::OK;
}
DECLARE_TYPES(lstmLayerCell) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(lstmLayerCell) {
const auto hasBiases = B_ARG(0); // indicates whether biases array is provided
LongType count = hasBiases ? 4 : 3;
const auto hI = INPUT_VARIABLE(count++); // initial output
const auto cI = INPUT_VARIABLE(count); // initial cell state
return new ShapeList({hI->shapeInfo(), cI->shapeInfo()});
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(lstmLayerCellBp, 7, 5, false, 1, 3) {
// equations (no peephole connections)
// it = σ(Wxi * xt + Wri * ht-1 + bi)
// ft = σ(Wxf * xt + Wrf * ht-1 + bf)
// c't = tanh(Wxc * xt + Wrc * ht-1 + bc)
// ct = ft ◦ ct-1 + it ◦ c't
// ot = σ(Wxo * xt + Wro * ht-1 + bo)
// ht = ot ◦ tanh(ct)
// equations (peephole connections are present)
// it = σ(Wxi * xt + Wri * ht-1 + Wpi ◦ ct-1 + bi)
// ft = σ(Wxf * xt + Wrf * ht-1 + Wpf ◦ ct-1 + bf)
// c't = tanh(Wxc * xt + Wrc * ht-1 + bc)
// ct = clip(ft ◦ ct-1 + it ◦ c't)
// ot = σ(Wxo * xt + Wro * ht-1 + Wpo ◦ ct + bo)
// ht = ot ◦ tanh(ct)
// notations:
// bS - batch size
// nIn - input size
// nOut - output size (hidden size)
// INPUTS:
// input x: [bS, nIn] or [nIn]
// input weights Wx: [nIn, 4*nOut]
// recurrent weights Wr: [nOut, 4*nOut]
// initial (previous) output hI: [bS, nOut] or [nOut]
// initial (previous) cell state cI: [bS, nOut] or [nOut]
// gradient wrt output dLdh: [bS, nOut] or [nOut]
// gradient wrt cell state dLdc: [bS, nOut] or [nOut]
// peephole weights Wp (optional): [3*nOut]
// biases b (optional): [4*nOut]
// OUTPUTS:
// gradient wrt x dLdx: [bS, nIn] or [nIn]
// gradient wrt Wx dLdWx: [nIn, 4*nOut]
// gradient wrt Wr dLdWr: [nOut, 4*nOut]
// gradient wrt hI dLdhI: [bS, nOut] or [nOut]
// gradient wrt cI dLdcI: [bS, nOut] or [nOut]
// gradient wrt b dLdb (optional): [4*nOut]
// gradient wrt Wp dLdWp (optional): [3*nOut]
// !!! dimension 4*nOut implies order it, ft, c't, ot
// !!! dimension 3*nOut implies order it, ft, ot
// integer numbers corresponding to activations: 0=tanh, 1=relu, 2=sigmoid, 3=affine, 4=leaky relu, 5= thresholded
// relu, 6=scaled tanh, 7=hard sigmoid, 8=ELU, 9=softsign, 10=softplus
const auto gateAct = INT_ARG(0); // activation for input (i), forget (f) and output (o) gates
const auto cellAct = INT_ARG(1); // activation for cell state (c)
const auto outAct = INT_ARG(2); // activation for output (h)
const auto hasBiases = B_ARG(0); // indicates whether biases array is provided
const auto hasPH = B_ARG(1); // indicates whether peephole connections are present
const auto gateActHasAlpha = gateAct == 3 || gateAct == 4 || gateAct == 5 || gateAct == 6 || gateAct == 8;
const auto cellActHasAlpha = cellAct == 3 || cellAct == 4 || cellAct == 5 || cellAct == 6 || cellAct == 8;
const auto outActHasAlpha = outAct == 3 || outAct == 4 || outAct == 5 || outAct == 6 || outAct == 8;
const auto gateActHasBeta = gateAct == 3 || gateAct == 6;
const auto cellActHasBeta = cellAct == 3 || cellAct == 6;
const auto outActHasBeta = outAct == 3 || outAct == 6;
LongType count = 1;
const auto cellClip = T_ARG(0); // cell clipping value, if it = 0 then do not apply clipping
const auto gateAlpha = gateActHasAlpha ? T_ARG(count++) : 0;
const auto gateBeta = gateActHasBeta ? T_ARG(count++) : 0;
const auto cellAlpha = cellActHasAlpha ? T_ARG(count++) : 0;
const auto cellBeta = cellActHasBeta ? T_ARG(count++) : 0;
const auto outAlpha = outActHasAlpha ? T_ARG(count++) : 0;
const auto outBeta = outActHasBeta ? T_ARG(count++) : 0;
count = 3;
const auto x = INPUT_VARIABLE(0); // input
const auto Wx = INPUT_VARIABLE(1); // input weights
const auto Wr = INPUT_VARIABLE(2); // recurrent weights
const auto b = hasBiases ? INPUT_VARIABLE(count++) : nullptr; // biases
const auto hI = INPUT_VARIABLE(count++); // initial output
const auto cI = INPUT_VARIABLE(count++); // initial cell state
const auto Wp = hasPH ? INPUT_VARIABLE(count++) : nullptr; // peephole weights
const auto dLdh = INPUT_VARIABLE(count); // gradient wrt output
REQUIRE_TRUE(cellClip >= 0, 0, "LSTM_LAYER_CELL_BP operation: cell clipping value should be nonnegative (>=0) !");
count = 3;
auto dLdx = OUTPUT_VARIABLE(0);
auto dLdWx = OUTPUT_VARIABLE(1);
auto dLdWr = OUTPUT_VARIABLE(2);
auto dLdb = hasBiases ? OUTPUT_VARIABLE(count++) : nullptr;
auto dLdhI = OUTPUT_VARIABLE(count++);
auto dLdcI = OUTPUT_VARIABLE(count++);
auto dLdWp = hasPH ? OUTPUT_VARIABLE(count) : nullptr;
// evaluate dimensions
const LongType bS = x->rankOf() == 1 ? 0 : x->sizeAt(0);
const LongType nIn = x->sizeAt(-1);
const LongType nOut = Wx->sizeAt(-1) / 4;
// inputs validations
// Wx validation
if (Wx->rankOf() != 2 || Wx->sizeAt(0) != nIn)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of input weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({nIn, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wx).c_str());
// Wr validation
if (Wr->rankOf() != 2 || Wr->sizeAt(0) != nOut || Wr->sizeAt(1) != 4 * nOut)
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of recurrent weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({nOut, 4 * nOut}).c_str(), ShapeUtils::shapeAsString(Wr).c_str());
// initial output/cell validation
std::vector<LongType> exphIcIShape =
x->rankOf() == 1 ? std::vector<LongType>{nOut} : std::vector<LongType>{bS, nOut};
REQUIRE_TRUE(hI->isSameShape(exphIcIShape), 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of initial output, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(exphIcIShape).c_str(), ShapeUtils::shapeAsString(hI).c_str());
REQUIRE_TRUE(cI->isSameShape(exphIcIShape), 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of initial cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(exphIcIShape).c_str(), ShapeUtils::shapeAsString(cI).c_str());
REQUIRE_TRUE(dLdh->isSameShape(exphIcIShape), 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of dLdh gradient, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(exphIcIShape).c_str(), ShapeUtils::shapeAsString(dLdh).c_str());
// biases validation
if (b != nullptr && (b->rankOf() != 1 || b->sizeAt(0) != 4 * nOut))
REQUIRE_TRUE(false, 0, "LSTM_LAYER_CELL_BP operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({4 * nOut}).c_str(), ShapeUtils::shapeAsString(b).c_str());
if (dLdb != nullptr && (dLdb->rankOf() != 1 || dLdb->sizeAt(0) != 4 * nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of dLdb gradient, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({4 * nOut}).c_str(), ShapeUtils::shapeAsString(dLdb).c_str());
// peephole weights validation
if (Wp != nullptr && (Wp->rankOf() != 1 || Wp->sizeAt(0) != 3 * nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of peephole weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({3 * nOut}).c_str(), ShapeUtils::shapeAsString(Wp).c_str());
if (dLdWp != nullptr && (dLdWp->rankOf() != 1 || dLdWp->sizeAt(0) != 3 * nOut))
REQUIRE_TRUE(false, 0,
"LSTM_LAYER_CELL_BP operation: wrong shape of dLdWp gradient, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({3 * nOut}).c_str(), ShapeUtils::shapeAsString(dLdWp).c_str());
std::vector<float> params = {
static_cast<float>(0) /*ignore*/, static_cast<float>(0) /*ignore*/, static_cast<float>(cellClip),
static_cast<float>(gateAct), static_cast<float>(gateAlpha), static_cast<float>(gateBeta),
static_cast<float>(cellAct), static_cast<float>(cellAlpha), static_cast<float>(cellBeta),
static_cast<float>(outAct), static_cast<float>(outAlpha), static_cast<float>(outBeta)};
std::vector<LongType> zShape =
x->rankOf() == 1 ? std::vector<LongType>({4 * nOut}) : std::vector<LongType>({bS, 4 * nOut});
NDArray z(x->ordering(), zShape, x->dataType(), block.launchContext());
NDArray *a = z.ulike();
NDArray *h = cI->ulike();
NDArray *c = cI->ulike();
helpers::lstmLayerCell(x, Wx, Wr, b, hI, cI, Wp, params, &z, a, h, c);
helpers::lstmLayerCellBp(x, Wx, Wr, b, hI, cI, Wp, dLdh, nullptr, nullptr, &z, a, c, params, dLdx, dLdWx, dLdWr,
dLdhI, dLdcI, dLdb, dLdWp);
return Status::OK;
}
DECLARE_TYPES(lstmLayerCellBp) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(lstmLayerCellBp) {
const auto hasBiases = B_ARG(0); // indicates whether biases array is provided
const auto hasPH = B_ARG(1); // indicates whether peephole connections are present
LongType count = 3;
const auto x = INPUT_VARIABLE(0); // input
const auto Wx = INPUT_VARIABLE(1); // input weights
const auto Wr = INPUT_VARIABLE(2); // recurrent weights
const auto b = hasBiases ? INPUT_VARIABLE(count++) : nullptr; // biases
const auto hI = INPUT_VARIABLE(count++); // initial output
const auto cI = INPUT_VARIABLE(count++); // initial cell state
const auto Wp = hasPH ? INPUT_VARIABLE(count) : nullptr; // peephole weights
auto shapes = SHAPELIST(x->shapeInfo(), Wx->shapeInfo(), Wr->shapeInfo());
if (b != nullptr) shapes->push_back(b->shapeInfo());
shapes->push_back(hI->shapeInfo());
shapes->push_back(cI->shapeInfo());
if (Wp != nullptr) shapes->push_back(Wp->shapeInfo());
return shapes;
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,663 @@
/* ******************************************************************************
*
*
* 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
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_sru)
#include <helpers/MmulHelper.h>
#include <helpers/PointersManager.h>
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/sru.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sru, 5, 2, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // X, input 3d tensor [bS x inSize x time], time - number of time steps, bS - batch size,
// inSize - number of features
auto w = INPUT_VARIABLE(1); // W, 2d tensor of weights [3*inSize x inSize]
auto b = INPUT_VARIABLE(2); // B, row of biases with twice length [2*inSize]
auto c0 = INPUT_VARIABLE(3); // C_{0}, 2d tensor of initial state [bS x inSize] at time t=0
auto mask = block.width() > 4 ? INPUT_VARIABLE(4) : nullptr; // optional, 2d tensor of dropout mask [bS x inSize]
auto h = OUTPUT_VARIABLE(0); // cell outputs, [bS x inSize x time]
auto c = OUTPUT_VARIABLE(1); // cell states, [bS x inSize x time]
const int rank = x->rankOf(); // = 3
const auto bS = x->sizeAt(0);
const auto inSize = x->sizeAt(1);
const auto time = x->sizeAt(2);
// input shapes validation
REQUIRE_TRUE(w->rankOf() == rank - 1, 0,
"SRU operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
w->rankOf());
REQUIRE_TRUE(b->rankOf() == 1, 0, "SRU operation: wrong rank of biases array, expected is %i, but got %i instead !",
1, b->rankOf());
REQUIRE_TRUE(c0->rankOf() == rank - 1, 0,
"SRU operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0->rankOf());
if (mask)
REQUIRE_TRUE(mask->rankOf() == rank - 1, 0,
"SRU operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
mask->rankOf());
const std::vector<LongType> wCorrectShape = {3 * inSize, inSize};
const std::vector<LongType> bCorrectShape = {2 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, inSize};
REQUIRE_TRUE(w->isSameShape(wCorrectShape), 0,
"SRU operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(w).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"SRU operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(c0->isSameShape(c0CorrectShape), 0,
"SRU operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0).c_str());
if (mask)
REQUIRE_TRUE(mask->isSameShape(c0CorrectShape), 0,
"SRU operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(mask).c_str());
// xm = x * mask
auto xm = x;
if (mask) {
xm = new NDArray(x->shapeInfo(), true, block.launchContext());
std::vector<LongType> dims = {0, 1};
x->applyBroadcast(broadcast::Multiply,&dims , mask, xm);
}
// time loop
helpers::sruTimeLoop(block.launchContext(), xm, c0, w, b, h, c);
if (mask) delete xm;
return Status::OK;
}
DECLARE_TYPES(sru) { getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS}); }
DECLARE_SHAPE_FN(sru) {
auto xShapeInfo = inputShape->at(0); // X, input 3d tensor [bS x inSize x time], time - number of time steps, bS -
// batch size, inSize - number of features
auto wShapeInfo = inputShape->at(1); // W, 2d tensor of weights [3*inSize x inSize]
auto bShapeInfo = inputShape->at(2); // B, row of biases with twice length [2*inSize]
auto c0ShapeInfo = inputShape->at(3); // C_{0}, 2d tensor of initial state [bS x inSize] at time t=0
auto maskShapeInfo =
block.width() > 4 ? inputShape->at(4) : nullptr; // optional, 2d tensor of dropout mask [bS x inSize]
const int rank = xShapeInfo[0]; // = 3
const int bS = xShapeInfo[1];
const int inSize = xShapeInfo[2];
const int time = xShapeInfo[3];
// input shapes validation
REQUIRE_TRUE(wShapeInfo[0] == rank - 1, 0,
"SRU operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
wShapeInfo[0]);
REQUIRE_TRUE(bShapeInfo[0] == 1, 0,
"SRU operation: wrong rank of biases array, expected is %i, but got %i instead !", 1, bShapeInfo[0]);
REQUIRE_TRUE(c0ShapeInfo[0] == rank - 1, 0,
"SRU operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0ShapeInfo[0]);
if (maskShapeInfo)
REQUIRE_TRUE(maskShapeInfo[0] == rank - 1, 0,
"SRU operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
maskShapeInfo[0]);
const std::vector<LongType> wCorrectShape = {3 * inSize, inSize};
const std::vector<LongType> bCorrectShape = {2 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, inSize};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wShapeInfo, wCorrectShape), 0,
"SRU operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(wShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, bCorrectShape), 0,
"SRU operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(c0ShapeInfo, c0CorrectShape), 0,
"SRU operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0ShapeInfo).c_str());
if (maskShapeInfo)
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maskShapeInfo, c0CorrectShape), 0,
"SRU operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(maskShapeInfo).c_str());
LongType * newShapeInfo1 = nullptr;
ALLOCATE(newShapeInfo1, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [bS x inSize x time]
newShapeInfo1[0] = rank;
newShapeInfo1[1] = bS;
newShapeInfo1[2] = inSize;
newShapeInfo1[3] = time;
ShapeUtils::updateStridesAndType(newShapeInfo1, xShapeInfo, shape::order(xShapeInfo));
auto result = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo1)->primary();
RELEASE(newShapeInfo1, block.getWorkspace());
return SHAPELIST(result, result);
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sru_bp, 8, 4, true, 0, 0) {
auto x = INPUT_VARIABLE(0);
// X, input 3d tensor [bS x K x N], N - number of time steps, bS - batch size, K - number of features
auto w = INPUT_VARIABLE(1); // W, 2d tensor of weights [3K x K]
auto b = INPUT_VARIABLE(2); // B, row of biases with twice length [1 x 2*K]
auto c0 = INPUT_VARIABLE(3); // C_{0}, 2d tensor of initial state [bS x K] at time t=0
auto c = INPUT_VARIABLE(4); // C, [bS x K x N]
auto inGradCt = INPUT_VARIABLE(5); // [bS x K]
auto inGradH = INPUT_VARIABLE(6); // [bS x K x N]
NDArray* mask = nullptr; // optional, 2d tensor of dropout mask [bS x K]
bool applyMask = false;
if (block.width() > 7) {
mask = INPUT_VARIABLE(7);
applyMask = true;
}
auto gradX = OUTPUT_VARIABLE(0); // [bS x K x N]
auto gradW = OUTPUT_VARIABLE(1); // [bS x 3K x K]
auto gradB = OUTPUT_VARIABLE(2); // [1 x 2K]
auto gradInit = OUTPUT_VARIABLE(3); // [bS x K]
const int bS = x->shapeOf()[0];
const int K = x->shapeOf()[1];
const int N = x->shapeOf()[2]; // N - number of time steps
std::vector<sd::LongType> gradBiasShape = {bS, 2 * K, N};
std::vector<sd::LongType> gradUShape = {bS, 3 * K, N};
std::vector<sd::LongType> gradHXShape = {bS, K, N};
std::vector<sd::LongType> gctShape = {bS, K};
std::vector<sd::LongType> gradTanhShape = {bS, K};
std::vector<sd::LongType> gradCtShape = {bS, K};
std::vector<sd::LongType> ftMinusShape = {bS, K};
std::vector<sd::LongType> rtMinusShape = {bS, K};
std::vector<sd::LongType> temp1Shape = {bS, K};
std::vector<sd::LongType> temp2Shape = {bS, K};
auto gradBias = NDArrayFactory::create_(x->ordering(), gradBiasShape, gradX->dataType(), block.launchContext());
auto gradU = NDArrayFactory::create_(x->ordering(), gradUShape, gradX->dataType(), block.launchContext());
auto gradHX = NDArrayFactory::create_(x->ordering(), gradHXShape, gradX->dataType(), block.launchContext());
auto gct = NDArrayFactory::create_(c->ordering(), gctShape, gradX->dataType(), block.launchContext());
auto gradTanh = NDArrayFactory::create_(c->ordering(), gradTanhShape, gradX->dataType(), block.launchContext());
auto gradCt = NDArrayFactory::create_(c->ordering(), gradCtShape, gradX->dataType(), block.launchContext());
auto ftMinus = NDArrayFactory::create_(c->ordering(), ftMinusShape, gradX->dataType(), block.launchContext());
auto rtMinus = NDArrayFactory::create_(c->ordering(), rtMinusShape, gradX->dataType(), block.launchContext());
auto temp1 = NDArrayFactory::create_(c->ordering(), temp1Shape, gradX->dataType(), block.launchContext());
auto temp2 = NDArrayFactory::create_(c->ordering(), temp2Shape, gradX->dataType(), block.launchContext());
std::vector<LongType> axes = {0, 1};
// x = x * mask
if (applyMask) x->applyBroadcast(broadcast::Multiply, &axes, mask, x); // apply mask
// multiplication matrix wi = matmul(w,x), U = WX
auto wi = MmulHelper::mmul(w, x, nullptr, 1., 0.); // U [bS x 3K x N]
auto wiZ = (*wi)({0, 0, 0, K, 0, 0}, true); // [bS x K x N]
auto wiF = (*wi)({0, 0, K, 2 * K, 0, 0}, true); // forget gate [bS x K x N]
auto wiR = (*wi)({0, 0, 2 * K, 3 * K, 0, 0}, true); // reset gate [bS x K x N]
auto bF = (*b)({0, 0, 0, K}, true); // biases for forget gate [1 x K]
auto bR = (*b)({0, 0, K, 2 * K}, true); // biases for reset gate [1 x K]
auto gradBF = (*gradBias)({0, 0, 0, K, 0, 0}, true); // [bS x K x N]
auto gradBR = (*gradBias)({0, 0, K, 2 * K, 0, 0}, true); // [bS x K x N]
auto gradUZ = (*gradU)({0, 0, 0, K, 0, 0}, true); // [bS x K x N]
auto gradUF = (*gradU)({0, 0, K, 2 * K, 0, 0}, true); // [bS x K x N]
auto gradUR = (*gradU)({0, 0, 2 * K, 3 * K, 0, 0}, true); // [bS x K x N]
NDArray* ct_1 = nullptr;
std::vector<LongType> idx = {0, 0, 0, 0, 0, 0};
for (int t = N - 1; t >= 0; --t) {
// initialization
idx[4] = t;
idx[5] = t + 1;
auto xt = (*x)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto zt = (*wiZ)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto ft = (*wiF)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto rt = (*wiR)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto ct = (*c)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto inGradHt = (*inGradH)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradBRt = (*gradBR)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradBFt = (*gradBF)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradHXt = (*gradHX)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradUZt = (*gradUZ)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradUFt = (*gradUF)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
auto gradURt = (*gradUR)(idx); // [bS x K x N] -> [bS x K x 1] -> [bS x K]
if (t != 0) {
idx[4] = t - 1;
idx[5] = t;
ct_1 = new NDArray((*c)(idx)); // previous c_{t-1}
} else
ct_1 = c0;
///////////////// forward
// ft = sigmoid(ft + bf), rt = sigmoid(rt + bR)
ft->addRowVector(bF, ft);
rt->addRowVector(bR, rt);
ft->applyTransform(transform::Sigmoid, ft);
rt->applyTransform(transform::Sigmoid, rt);
// TODO T val = (activation_type == 1) ? tanh(cur) : ((activation_type == 2) ? reluf(cur) : cur );
ct->applyTransform(transform::Tanh, gct);
// ftMinus = 1-ft, rtMinus = 1-rt
ft->applyTransform(transform::OneMinus, ftMinus);
rt->applyTransform(transform::OneMinus, rtMinus);
///////////////// backward
// bR, *grad_brt_ptr = inGradHt * (g_ct - xt) * (1.0f - rt) * rt;
gct->applyPairwiseTransform(pairwise::Subtract, xt, temp1); // temp1 = (g_ct - xt)
rtMinus->applyPairwiseTransform(pairwise::Multiply, rt, temp2); // temp2 = (1.0f - rt) * rt;
temp1->applyPairwiseTransform(pairwise::Multiply, temp2); // temp1 = (g_ct - xt) * (1.0f - rt) * rt;
inGradHt->applyPairwiseTransform(pairwise::Multiply, temp1,
gradBRt); // = inGradHt * (g_ct - xt) * (1.0f - rt) * rt;
// bF, TODO - tanh
// gradTanh = (1.0f - g_ct * g_ct);
gct->applyPairwiseTransform(pairwise::Multiply, gct, gradTanh); // gradTanh = g_ct * g_ct
gradTanh->applyTransform(transform::OneMinus, gradTanh); // gradTanh = (1.0f - g_ct * g_ct)
// gradCt = inGradHt * rt * gradTanh
rt->applyPairwiseTransform(pairwise::Multiply, gradTanh, gradCt); // gradCt = rt * gradTanh
inGradHt->applyPairwiseTransform(pairwise::Multiply, gradCt, gradCt); // gradCt = inGradHt * rt * gradTanh
// gradBFt = (gradCt + inGradCt) * (ct_1 - zt) * (1 - ft) * ft;
gradCt->applyPairwiseTransform(pairwise::Add, inGradCt, temp1); // temp1 = (gradCt + inGradCt)
ct_1->applyPairwiseTransform(pairwise::Subtract, zt, temp2); // temp2 = (ct_1 - zt)
temp1->applyPairwiseTransform(pairwise::Multiply, ftMinus, temp1); // temp1 = (gradCt + inGradCt)*(1-ft)
temp1->applyPairwiseTransform(pairwise::Multiply, ft, temp1); // temp1 = (gradCt + inGradCt)*(1-ft)*ft
temp1->applyPairwiseTransform(pairwise::Multiply, temp2,
gradBFt); // gradBFt = (gradCt + inGradCt) * (ct_1 - zt) * (1 - ft) * ft;
// x_t (highway connection), gradHXt = inGradHt * (1.0f - rt);
inGradHt->applyPairwiseTransform(pairwise::Multiply, rtMinus, gradHXt);
// U_t, gradUZt = (inGradHt * rt * grad_tanh + inGradCt) * (1.0f - ft);
rt->applyPairwiseTransform(pairwise::Multiply, gradTanh, temp1); // temp1 = rt * grad_tanh
inGradHt->applyPairwiseTransform(pairwise::Multiply, temp1,temp1); // temp1 = inGradHt * rt * grad_tanh
temp1->applyPairwiseTransform(pairwise::Add, inGradCt, temp1); // temp1 = inGradHt * rt * grad_tanh + inGradCt
temp1->applyPairwiseTransform(pairwise::Multiply, ftMinus,
gradUZt); // gradUZt = (inGradHt * rt * grad_tanh + inGradCt) * (1.0f - ft);
gradUFt->assign(gradBFt);
gradURt->assign(gradBRt);
// c_{t-1}, inGradCt = (gradCt + inGradCt) * ft;
gradCt->applyPairwiseTransform(pairwise::Add, inGradCt, temp1); // temp1 = (gradCt + inGradCt)
temp1->applyPairwiseTransform(pairwise::Multiply, ft, inGradCt); // inGradCt = (gradCt + inGradCt) * ft;
if (t != 0) delete ct_1;
delete xt;
delete zt;
delete ft;
delete rt;
delete ct;
delete inGradHt;
delete gradBRt;
delete gradHXt;
delete gradUZt;
delete gradUFt;
delete gradURt;
}
// gradInit
gradInit->assign(inGradCt);
// gradX
auto weightsT = w->transpose(); // [K x 3K]
MmulHelper::mmul(weightsT, gradU, gradX, 1., 0.); // [bS x K x N]
gradX->applyPairwiseTransform(pairwise::Add, gradHX, gradX);
std::vector<LongType> axes3 = {0, 1};
// + grad_highway_x
if (applyMask) gradX->applyBroadcast(broadcast::Multiply, &axes3, mask, gradX); // apply mask
// gradB
std::vector<sd::LongType> gradBShape = { 2 * K};
auto gradB2 = gradB->reshape(gradB->ordering(), gradBShape);
std::vector<LongType> axes2;
axes.push_back(0);
axes.push_back(2);
gradBias->reduceAlongDimension(reduce::Sum, gradB2, &axes2); // [1 x 2K]
// gradW [bS x 3K x K]
x->permutei({0, 2, 1}, false, false); // [bS x N x K]
MmulHelper::mmul(gradU, x, gradW, 1., 0.); // [bS x 3K x K]
delete gct;
delete gradU;
delete gradHX;
delete temp1;
delete temp2;
delete gradCt;
delete wi;
delete gradTanh;
delete ftMinus;
delete rtMinus;
delete gradBias;
delete weightsT;
return Status::OK;
}
DECLARE_TYPES(sru_bp) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(sru_bp) {
auto inShape = inputShape->at(0); // [bS x inSize x time]
auto bS = inShape[1];
auto inSize = inShape[2];
auto time = inShape[3];
char order = (char)(inShape[9]);
auto ret = SHAPELIST(
ConstantShapeHelper::getInstance().bufferForShapeInfo(ArrayOptions::dataType(inShape), order,
std::vector<sd::LongType>{bS, inSize, time})->primary(),
ConstantShapeHelper::getInstance().bufferForShapeInfo(ArrayOptions::dataType(inShape), order,
std::vector<sd::LongType>{bS, 3 * inSize, inSize})->primary(),
ConstantShapeHelper::getInstance().bufferForShapeInfo(ArrayOptions::dataType(inShape), order,
std::vector<sd::LongType>{1, 2 * inSize})->primary(),
ConstantShapeHelper::getInstance().bufferForShapeInfo(ArrayOptions::dataType(inShape), order,
std::vector<sd::LongType>{bS, inSize})->primary()
);
return ret;
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sru_bi, 5, 2, true, 0, 0) {
auto x = INPUT_VARIABLE(0); // X, input 3d tensor [time x bS x 2*inSize], time - number of time steps, bS - batch
// size, inSize - number of features
auto w = INPUT_VARIABLE(1); // W, 2d tensor of weights [2*inSize x 6*inSize]
auto b = INPUT_VARIABLE(2); // B, row of biases with twice length [1 x 4*inSize]
auto c0 = INPUT_VARIABLE(3); // C_{0}, 2d tensor of initial state [bS x 2*inSize] at time t=0
NDArray* mask =
block.width() > 4 ? INPUT_VARIABLE(4) : nullptr; // optional, 2d tensor of dropout mask [bS x 2*inSize]
auto ht = OUTPUT_VARIABLE(0); // h_t, [time x bS x 2*inSize]
auto ct = OUTPUT_VARIABLE(1); // c_t, [time x bS x 2*inSize]
// input shapes validation
const int rank = x->rankOf();
const LongType bS = x->sizeAt(1);
const LongType inSize = x->sizeAt(2) / 2;
REQUIRE_TRUE(x->rankOf() == rank, 0,
"SRU_BI operation: wrong rank of input array, expected is %i, but got %i instead !", rank, x->rankOf());
REQUIRE_TRUE(w->rankOf() == rank - 1, 0,
"SRU_BI operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
w->rankOf());
REQUIRE_TRUE(b->rankOf() == 1, 0, "SRU_BI operation: wrong rank of biases array, expected is 1, but got %i instead !",
b->rankOf());
REQUIRE_TRUE(c0->rankOf() == rank - 1, 0,
"SRU_BI operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0->rankOf());
if (mask)
REQUIRE_TRUE(mask->rankOf() == rank - 1, 0,
"SRU_BI operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
mask->rankOf());
const std::vector<LongType> wCorrectShape = {2 * inSize, 6 * inSize};
const std::vector<LongType> bCorrectShape = {4 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, 2 * inSize};
REQUIRE_TRUE(w->isSameShape(wCorrectShape), 0,
"SRU_BI operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(w).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"SRU_BI operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(c0->isSameShape(c0CorrectShape), 0,
"SRU_BI operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0).c_str());
if (mask)
REQUIRE_TRUE(mask->isSameShape(c0CorrectShape), 0,
"SRU_BI operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(mask).c_str());
helpers::sruBI(block.launchContext(), x, w, b, c0, mask, ht, ct);
return Status::OK;
}
DECLARE_TYPES(sru_bi) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(sru_bi) {
auto xShapeInfo = inputShape->at(0); // [time x bS x 2K ]
auto wShapeInfo = inputShape->at(1);
auto bShapeInfo = inputShape->at(2);
auto c0ShapeInfo = inputShape->at(3);
auto maskShapeInfo =
block.width() > 4 ? inputShape->at(4) : nullptr; // optional, 2d tensor of dropout mask [bS x inSize]
const int rank = xShapeInfo[0]; // = 3
const LongType time = xShapeInfo[1];
const LongType bS = xShapeInfo[2];
const LongType inSize = xShapeInfo[3] / 2;
// input shapes validation
REQUIRE_TRUE(wShapeInfo[0] == rank - 1, 0,
"SRU_BI operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
wShapeInfo[0]);
REQUIRE_TRUE(bShapeInfo[0] == 1, 0,
"SRU_BI operation: wrong rank of biases array, expected is 1, but got %i instead !", bShapeInfo[0]);
REQUIRE_TRUE(c0ShapeInfo[0] == rank - 1, 0,
"SRU_BI operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0ShapeInfo[0]);
if (maskShapeInfo)
REQUIRE_TRUE(maskShapeInfo[0] == rank - 1, 0,
"SRU_BI operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
maskShapeInfo[0]);
const std::vector<LongType> wCorrectShape = {2 * inSize, 6 * inSize};
const std::vector<LongType> bCorrectShape = {4 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, 2 * inSize};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wShapeInfo, wCorrectShape), 0,
"SRU_BI operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(wShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, bCorrectShape), 0,
"SRU_BI operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(c0ShapeInfo, c0CorrectShape), 0,
"SRU_BI operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0ShapeInfo).c_str());
if (maskShapeInfo)
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maskShapeInfo, c0CorrectShape), 0,
"SRU_BI operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(maskShapeInfo).c_str());
char order = shape::order(xShapeInfo);
ShapeDescriptor *descriptor = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {time, bS, 2 * inSize});
auto result = ConstantShapeHelper::getInstance().createShapeInfo(descriptor);
return SHAPELIST(result, result);
}
DECLARE_TYPES(sru_bi_bp) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sru_bi_bp, 8, 4, true, 0, 0) {
auto x = INPUT_VARIABLE(0); // X, input 3d tensor [time x bS x 2*inSize], time - number of time steps, bS - batch
// size, inSize - number of features
auto w = INPUT_VARIABLE(1); // W, 2d tensor of weights [2*inSize x 6*inSize]
auto b = INPUT_VARIABLE(2); // B, row of biases with twice length [4*inSize]
auto c0 = INPUT_VARIABLE(3); // C_{0}, 2d tensor of initial state [bS x 2*inSize] at time t=0
auto ct = INPUT_VARIABLE(4); // C, [time x bS x 2*inSize]
auto inGradC0 = INPUT_VARIABLE(5); // [bS x 2*inSize]
auto inGradHt = INPUT_VARIABLE(6); // [time x bS x 2*inSize]
NDArray* mask =
block.width() > 7 ? INPUT_VARIABLE(7) : nullptr; // optional, 2d tensor of dropout mask [bS x 2*inSize]
// input shapes validation
const int rank = x->rankOf();
const LongType time = x->sizeAt(0);
const LongType bS = x->sizeAt(1);
const LongType inSize = x->sizeAt(2) / 2;
REQUIRE_TRUE(w->rankOf() == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
w->rankOf());
REQUIRE_TRUE(b->rankOf() == 1, 0,
"SRU_BI_BP operation: wrong rank of biases array, expected is 1, but got %i instead !", b->rankOf());
REQUIRE_TRUE(c0->rankOf() == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0->rankOf());
REQUIRE_TRUE(ct->rankOf() == rank, 0,
"SRU_BI_BP operation: wrong rank of state array, expected is %i, but got %i instead !", rank,
ct->rankOf());
REQUIRE_TRUE(inGradC0->rankOf() == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of gradient c0, expected is %i, but got %i instead !", rank - 1,
inGradC0->rankOf());
REQUIRE_TRUE(inGradHt->rankOf() == rank, 0,
"SRU_BI_BP operation: wrong rank of gradient ht, expected is %i, but got %i instead !", rank,
inGradHt->rankOf());
if (mask)
REQUIRE_TRUE(mask->rankOf() == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
mask->rankOf());
const std::vector<LongType> wCorrectShape = {2 * inSize, 6 * inSize};
const std::vector<LongType> bCorrectShape = {4 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, 2 * inSize};
const std::vector<LongType> ctCorrectShape = {time, bS, 2 * inSize};
REQUIRE_TRUE(w->isSameShape(wCorrectShape), 0,
"SRU_BI operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(w).c_str());
REQUIRE_TRUE(b->isSameShape(bCorrectShape), 0,
"SRU_BI operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
REQUIRE_TRUE(c0->isSameShape(c0CorrectShape), 0,
"SRU_BI operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0).c_str());
REQUIRE_TRUE(ct->isSameShape(ctCorrectShape), 0,
"SRU_BI operation: wrong shape of state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(ctCorrectShape).c_str(), ShapeUtils::shapeAsString(ct).c_str());
if (mask)
REQUIRE_TRUE(mask->isSameShape(c0CorrectShape), 0,
"SRU_BI operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(mask).c_str());
auto gradI = OUTPUT_VARIABLE(0); // [time x bS x 2*inSize]
auto gradW = OUTPUT_VARIABLE(1); // [time x 2*inSize x 6*inSize]
auto gradB = OUTPUT_VARIABLE(2); // [1 x 4*inSize]
auto gradC0 = OUTPUT_VARIABLE(3); // [bS x 2*inSize]
helpers::sruBIBP(block.launchContext(), x, w, b, c0, ct, inGradC0, inGradHt, mask, gradI, gradW, gradB, gradC0);
return Status::OK;
}
DECLARE_SHAPE_FN(sru_bi_bp) {
auto xShapeInfo = inputShape->at(0); // [time x bS x 2K ]
auto wShapeInfo = inputShape->at(1);
auto bShapeInfo = inputShape->at(2);
auto c0ShapeInfo = inputShape->at(3);
auto ctShapeInfo = inputShape->at(4);
auto inGradC0ShapeInfo = inputShape->at(5);
auto inGradHtShapeInfo = inputShape->at(6);
auto maskShapeInfo =
block.width() > 7 ? inputShape->at(7) : nullptr; // optional, 2d tensor of dropout mask [bS x inSize]
// input shapes validation
const int rank = xShapeInfo[0];
const LongType time = xShapeInfo[1];
const LongType bS = xShapeInfo[2];
const LongType inSize = xShapeInfo[3] / 2;
REQUIRE_TRUE(wShapeInfo[0] == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of weights array, expected is %i, but got %i instead !", rank - 1,
wShapeInfo[0]);
REQUIRE_TRUE(bShapeInfo[0] == 1, 0,
"SRU_BI_BP operation: wrong rank of biases array, expected is 1, but got %i instead !", bShapeInfo[0]);
REQUIRE_TRUE(c0ShapeInfo[0] == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of initial state array, expected is %i, but got %i instead !", rank - 1,
c0ShapeInfo);
REQUIRE_TRUE(ctShapeInfo[0] == rank, 0,
"SRU_BI_BP operation: wrong rank of state array, expected is %i, but got %i instead !", rank,
ctShapeInfo);
REQUIRE_TRUE(inGradC0ShapeInfo[0] == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of gradient c0, expected is %i, but got %i instead !", rank - 1,
inGradC0ShapeInfo[0]);
REQUIRE_TRUE(inGradHtShapeInfo[0] == rank, 0,
"SRU_BI_BP operation: wrong rank of gradient ht, expected is %i, but got %i instead !", rank,
inGradHtShapeInfo[0]);
if (maskShapeInfo)
REQUIRE_TRUE(maskShapeInfo[0] == rank - 1, 0,
"SRU_BI_BP operation: wrong rank of mask array, expected is %i, but got %i instead !", rank - 1,
maskShapeInfo[0]);
const std::vector<LongType> wCorrectShape = {2 * inSize, 6 * inSize};
const std::vector<LongType> bCorrectShape = {4 * inSize};
const std::vector<LongType> c0CorrectShape = {bS, 2 * inSize};
const std::vector<LongType> ctCorrectShape = {time, bS, 2 * inSize};
const std::vector<LongType> inGradC0CorrectShape = {bS, 2 * inSize};
const std::vector<LongType> inGradHtCorrectShape = {time, bS, 2 * inSize};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wShapeInfo, wCorrectShape), 0,
"SRU_BI operation: wrong shape of weights array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(wCorrectShape).c_str(), ShapeUtils::shapeAsString(wShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, bCorrectShape), 0,
"SRU_BI operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(bCorrectShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(c0ShapeInfo, c0CorrectShape), 0,
"SRU_BI operation: wrong shape of initial state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(c0ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(ctShapeInfo, ctCorrectShape), 0,
"SRU_BI operation: wrong shape of state array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(ctCorrectShape).c_str(), ShapeUtils::shapeAsString(ctShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(inGradC0ShapeInfo, inGradC0CorrectShape), 0,
"SRU_BI operation: wrong shape of gradient c0 array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(inGradC0CorrectShape).c_str(),
ShapeUtils::shapeAsString(inGradC0ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(inGradHtShapeInfo, inGradHtCorrectShape), 0,
"SRU_BI operation: wrong shape of gradient ht array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(inGradHtCorrectShape).c_str(),
ShapeUtils::shapeAsString(inGradHtShapeInfo).c_str());
if (maskShapeInfo)
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maskShapeInfo, c0CorrectShape), 0,
"SRU_BI operation: wrong shape of mask array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(c0CorrectShape).c_str(), ShapeUtils::shapeAsString(maskShapeInfo).c_str());
const char order = shape::order(xShapeInfo);
ShapeDescriptor *descriptor1 = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {time, bS, 2 * inSize});
ShapeDescriptor *descriptor2 = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {time, 2 * inSize, 6 * inSize});
ShapeDescriptor *descriptor3 = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {4 * inSize});
ShapeDescriptor *descriptor4 = new ShapeDescriptor(ArrayOptions::dataType(xShapeInfo), order, {bS, 2 * inSize});
return SHAPELIST(ConstantShapeHelper::getInstance().createShapeInfo(descriptor1),
ConstantShapeHelper::getInstance().createShapeInfo(descriptor2),
ConstantShapeHelper::getInstance().createShapeInfo(descriptor3),
ConstantShapeHelper::getInstance().createShapeInfo(descriptor4));
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,115 @@
/* ******************************************************************************
*
*
* 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 05.12.2017
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_sruCell)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/sru.h>
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(sruCell, 4, 2, false, 0, 0) {
auto xt = INPUT_VARIABLE(0); // input [bS x inSize], bS - batch size, inSize - number of features
auto ct_1 = INPUT_VARIABLE(1); // previous cell state ct [bS x inSize], that is at previous time step t-1
auto w = INPUT_VARIABLE(2); // weights [inSize x 3*inSize]
auto b = INPUT_VARIABLE(3); // biases [2*inSize]
auto ht = OUTPUT_VARIABLE(0); // current cell output [bS x inSize], that is at current time step t
auto ct = OUTPUT_VARIABLE(1); // current cell state [bS x inSize], that is at current time step t
const int rank = xt->rankOf();
const int bS = xt->sizeAt(0);
const int inSize = xt->sizeAt(1); // inSize - number of features
// input shapes validation
const std::vector<LongType> correctCt_1Shape = {bS, inSize};
const std::vector<LongType> correctWShape = {inSize, 3 * inSize};
const std::vector<LongType> correctBShape = {2 * inSize};
REQUIRE_TRUE(ct_1->isSameShape(correctCt_1Shape), 0,
"SRUCELL operation: wrong shape of previous cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctCt_1Shape).c_str(), ShapeUtils::shapeAsString(ct_1).c_str());
REQUIRE_TRUE(w->isSameShape(correctWShape), 0,
"SRUCELL operation: wrong shape of weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWShape).c_str(), ShapeUtils::shapeAsString(w).c_str());
REQUIRE_TRUE(b->isSameShape(correctBShape), 0,
"SRUCELL operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctBShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
// fixme: shitty initializer lists
helpers::sruCell(block.launchContext(), xt, ct_1, w, b, ht, ct);
return Status::OK;
}
DECLARE_TYPES(sruCell) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(sruCell) {
auto xtShapeInfo = inputShape->at(0); // input [bS x inSize], bS - batch size, inSize - number of features
auto ct_1ShapeInfo = inputShape->at(1); // previous cell state ct [bS x inSize], that is at previous time step t-1
auto wShapeInfo = inputShape->at(2); // weights [inSize x 3*inSize]
auto bShapeInfo = inputShape->at(3); // biases [2*inSize]
const int rank = xtShapeInfo[0];
const int bS = xtShapeInfo[1];
const int inSize = xtShapeInfo[2]; // inSize - number of features
// input shapes validation
const std::vector<LongType> correctCt_1Shape = {bS, inSize};
const std::vector<LongType> correctWShape = {inSize, 3 * inSize};
const std::vector<LongType> correctBShape = {2 * inSize};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(ct_1ShapeInfo, correctCt_1Shape), 0,
"SRUCELL operation: wrong shape of previous cell state, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctCt_1Shape).c_str(), ShapeUtils::shapeAsString(ct_1ShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(wShapeInfo, correctWShape), 0,
"SRUCELL operation: wrong shape of weights, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctWShape).c_str(), ShapeUtils::shapeAsString(wShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, correctBShape), 0,
"SRUCELL operation: wrong shape of biases, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(correctBShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
// evaluate output shapeInfos
LongType *hShapeInfo(nullptr), *cShapeInfo(nullptr);
ALLOCATE(hShapeInfo, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [bS x numProj]
ALLOCATE(cShapeInfo, block.getWorkspace(), shape::shapeInfoLength(rank), sd::LongType); // [bS x numUnits]
hShapeInfo[0] = cShapeInfo[0] = rank;
hShapeInfo[1] = cShapeInfo[1] = bS;
hShapeInfo[2] = cShapeInfo[2] = inSize;
ShapeUtils::updateStridesAndType(hShapeInfo, ct_1ShapeInfo, shape::order(ct_1ShapeInfo));
ShapeUtils::updateStridesAndType(cShapeInfo, ct_1ShapeInfo, shape::order(ct_1ShapeInfo));
return SHAPELIST(ConstantShapeHelper::getInstance().createFromExisting(hShapeInfo),
ConstantShapeHelper::getInstance().createFromExisting(cShapeInfo));
}
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,286 @@
/* ******************************************************************************
*
*
* 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 03.04.2018
//
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/reverse.h>
#include <ops/declarable/helpers/rnn.h>
#include <ops/declarable/helpers/transforms.h>
#if NOT_EXCLUDED(OP_static_bi_directional_rnn)
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(static_bidirectional_rnn, 7, 3, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // input [time x bS x inSize]
auto WxFW = INPUT_VARIABLE(1); // input-to-hidden weights for forward RNN, [inSize x numUnitsFW]
auto WhFW = INPUT_VARIABLE(2); // hidden-to-hidden weights for forward RNN, [numUnitsFW x numUnitsFW]
auto bFW = INPUT_VARIABLE(3); // biases for forward RNN, [2*numUnitsFW]
auto WxBW = INPUT_VARIABLE(4); // input-to-hidden weights for backward RNN, [inSize x numUnitsBW]
auto WhBW = INPUT_VARIABLE(5); // hidden-to-hidden weights for backward RNN, [numUnitsBW x numUnitsBW]
auto bBW = INPUT_VARIABLE(6); // biases for backward RNN, [2*v]
NDArray* h0FW = nullptr; // initial cell output for forward RNN (at time step = 0) [bS x numUnitsFW]
NDArray* h0BW = nullptr; // initial cell output for backward RNN (at time step = 0) [bS x numUnitsBW]
NDArray* maxTimeStep =
nullptr; // vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
switch (block.width()) {
case 8:
maxTimeStep = INPUT_VARIABLE(7);
break;
case 9:
h0FW = INPUT_VARIABLE(7);
h0BW = INPUT_VARIABLE(8);
break;
case 10:
h0FW = INPUT_VARIABLE(7);
h0BW = INPUT_VARIABLE(8);
maxTimeStep = INPUT_VARIABLE(9);
break;
}
auto h = OUTPUT_VARIABLE(0); // cell outputs [time x bS x (numUnitsFW + numUnitsBW)], that is per each time step
auto hFWFinal = OUTPUT_VARIABLE(1); // final cell out for forward RNN [bS x numUnitsFW]
auto hBWFinal = OUTPUT_VARIABLE(2); // final cell out for backward RNN [bS x numUnitsBF]
REQUIRE_TRUE(x->rankOf() == 3, 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: input array must have rank = 3, but got %i instead !",
x->rankOf());
REQUIRE_TRUE(WxFW->rankOf() == 2, 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: input-to-hidden weights array (for forward RNN) must have "
"rank = 2, but got %i instead !",
WxFW->rankOf());
REQUIRE_TRUE(WxBW->rankOf() == 2, 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: input-to-hidden weights array (for backward RNN) must have "
"rank = 2, but got %i instead !",
WxBW->rankOf());
const LongType inRank = x->rankOf();
const LongType time = x->sizeAt(0);
const LongType bS = x->sizeAt(1);
const LongType numUnitsFW = WxFW->sizeAt(1);
const LongType numUnitsBW = WxBW->sizeAt(1);
const std::vector<LongType> expectedWhFWshape = {numUnitsFW, numUnitsFW};
const std::vector<LongType> expectedWhBWshape = {numUnitsBW, numUnitsBW};
const std::vector<LongType> expectedbFWshape = {2 * numUnitsFW};
const std::vector<LongType> expectedbBWshape = {2 * numUnitsBW};
REQUIRE_TRUE(WhFW->isSameShape(expectedWhFWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of hidden-to-hidden weights array (for forward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedWhFWshape).c_str(), ShapeUtils::shapeAsString(WhFW).c_str());
REQUIRE_TRUE(WhBW->isSameShape(expectedWhBWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of hidden-to-hidden weights array (for backward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedWhBWshape).c_str(), ShapeUtils::shapeAsString(WhBW).c_str());
REQUIRE_TRUE(bFW->isSameShape(expectedbFWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of biases array (for forward RNN), expected is "
"%s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbFWshape).c_str(), ShapeUtils::shapeAsString(bFW).c_str());
REQUIRE_TRUE(bBW->isSameShape(expectedbBWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of biases array (for backward RNN), expected is "
"%s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbBWshape).c_str(), ShapeUtils::shapeAsString(bBW).c_str());
if (h0FW) {
const std::vector<LongType> expectedh0FWshape = {bS, numUnitsFW};
REQUIRE_TRUE(h0FW->isSameShape(expectedh0FWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of initial cell output array (for forward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0FWshape).c_str(), ShapeUtils::shapeAsString(h0FW).c_str());
}
if (h0BW) {
const std::vector<LongType> expectedh0BWshape = {bS, numUnitsBW};
REQUIRE_TRUE(h0BW->isSameShape(expectedh0BWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of initial cell output array (for backward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0BWshape).c_str(), ShapeUtils::shapeAsString(h0BW).c_str());
}
if (maxTimeStep)
REQUIRE_TRUE(maxTimeStep->isSameShape({bS}), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of maxTimeStep array, expected is [%i], but "
"got %s instead !",
bS, ShapeUtils::shapeAsString(maxTimeStep).c_str());
// forward steps
std::vector<sd::LongType> expectedHshape = {time, bS, numUnitsFW + numUnitsBW};
auto hFW = new NDArray(x->ordering(),expectedHshape, x->dataType(), block.launchContext());
helpers::rnnTimeLoop(block.launchContext(), x, WxFW, WhFW, bFW, h0FW, maxTimeStep, hFW, hFWFinal);
auto seqLen = maxTimeStep;
if (seqLen == nullptr) {
std::vector<sd::LongType> seqShape = {x->sizeAt(1)};
seqLen = new NDArray(x->ordering(),seqShape, INT64, block.launchContext()); // [bS]
*seqLen = x->sizeAt(0); // set each element of seqLen to be equal to time
}
// reverse x
auto revOut = new NDArray(x, false, block.launchContext());
helpers::reverseSequence(block.launchContext(), x, seqLen, revOut, 0, 1);
std::vector<sd::LongType> shape = {time, bS, numUnitsBW};
// backward steps
auto hBW = new NDArray(x->ordering(),shape, x->dataType(), block.launchContext());
helpers::rnnTimeLoop(block.launchContext(), revOut, WxBW, WhBW, bBW, h0BW, maxTimeStep, hBW, hBWFinal);
// reverse hBW
auto hBWcopy = new NDArray(*hBW);
helpers::reverseSequence(block.launchContext(), hBWcopy, seqLen, hBW, 0, 1);
// concatenate hFW and hBW along last third dimension
// NDArrayFactory<T>::concat({hFW, hBW}, 2, h);
helpers::concat(block.launchContext(), {hFW, hBW}, *h, 2);
delete hBW;
delete hFW;
delete hBWcopy;
delete revOut;
if (seqLen != maxTimeStep) delete seqLen;
return Status::OK;
}
DECLARE_TYPES(static_bidirectional_rnn) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(static_bidirectional_rnn) {
auto xShapeInfo = inputShape->at(0); // input [time x bS x inSize]
auto WxFWShapeInfo = inputShape->at(1); // input-to-hidden weights for forward RNN, [inSize x numUnitsFW]
auto WhFWShapeInfo = inputShape->at(2); // hidden-to-hidden weights for forward RNN, [numUnitsFW x numUnitsFW]
auto bFWShapeInfo = inputShape->at(3); // biases for forward RNN, [2*numUnitsFW]
auto WxBWShapeInfo = inputShape->at(4); // input-to-hidden weights for backward RNN, [inSize x numUnitsBW]
auto WhBWShapeInfo = inputShape->at(5); // hidden-to-hidden weights for backward RNN, [numUnitsBW x numUnitsBW]
auto bBWShapeInfo = inputShape->at(6); // biases for backward RNN, [2*numUnitsBW]
LongType const* h0FWShapeInfo =
nullptr; // initial cell output for forward RNN (at time step = 0) [bS x numUnitsFW]
LongType const* h0BWShapeInfo =
nullptr; // initial cell output for backward RNN (at time step = 0) [bS x numUnitsBW]
LongType const* maxTimeStepShapeInfo =
nullptr; // vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
switch (block.width()) {
case 8:
maxTimeStepShapeInfo = inputShape->at(7);
break;
case 9:
h0FWShapeInfo = inputShape->at(7);
h0BWShapeInfo = inputShape->at(8);
break;
case 10:
h0FWShapeInfo = inputShape->at(7);
h0BWShapeInfo = inputShape->at(8);
maxTimeStepShapeInfo = inputShape->at(9);
break;
}
REQUIRE_TRUE(xShapeInfo[0] == 3, 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: input array must have rank = 3, but got %i instead !",
xShapeInfo[0]);
REQUIRE_TRUE(WxFWShapeInfo[0] == 2, 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: input-to-hidden weights array (for forward RNN) must have "
"rank = 2, but got %i instead !",
WxFWShapeInfo[0]);
REQUIRE_TRUE(WxBWShapeInfo[0] == 2, 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: input-to-hidden weights array (for backward RNN) must have "
"rank = 2, but got %i instead !",
WxBWShapeInfo[0]);
const int inRank = xShapeInfo[0];
const int time = xShapeInfo[1];
const int bS = xShapeInfo[2];
const int numUnitsFW = WxFWShapeInfo[2];
const int numUnitsBW = WxBWShapeInfo[2];
const std::vector<LongType> expectedWhFWshape = {numUnitsFW, numUnitsFW};
const std::vector<LongType> expectedWhBWshape = {numUnitsBW, numUnitsBW};
const std::vector<LongType> expectedbFWshape = {2 * numUnitsFW};
const std::vector<LongType> expectedbBWshape = {2 * numUnitsBW};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WhFWShapeInfo, expectedWhFWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of hidden-to-hidden weights array (for forward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedWhFWshape).c_str(), ShapeUtils::shapeAsString(WhFWShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WhBWShapeInfo, expectedWhBWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of hidden-to-hidden weights array (for backward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedWhBWshape).c_str(), ShapeUtils::shapeAsString(WhBWShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bFWShapeInfo, expectedbFWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of biases array (for forward RNN), expected is "
"%s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbFWshape).c_str(), ShapeUtils::shapeAsString(bFWShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bBWShapeInfo, expectedbBWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of biases array (for backward RNN), expected is "
"%s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbBWshape).c_str(), ShapeUtils::shapeAsString(bBWShapeInfo).c_str());
if (h0FWShapeInfo) {
const std::vector<LongType> expectedh0FWshape = {bS, numUnitsFW};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(h0FWShapeInfo, expectedh0FWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of initial cell output array (for forward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0FWshape).c_str(),
ShapeUtils::shapeAsString(h0FWShapeInfo).c_str());
}
if (h0BWShapeInfo) {
const std::vector<LongType> expectedh0BWshape = {bS, numUnitsBW};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(h0BWShapeInfo, expectedh0BWshape), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of initial cell output array (for backward "
"RNN), expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0BWshape).c_str(),
ShapeUtils::shapeAsString(h0BWShapeInfo).c_str());
}
if (maxTimeStepShapeInfo)
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maxTimeStepShapeInfo, {bS}), 0,
"STATIC_BIDIRECTIONAL_RNN custom operation: wrong shape of maxTimeStep array, expected is [%i], but "
"got %s instead !",
bS, ShapeUtils::shapeAsString(maxTimeStepShapeInfo).c_str());
// evaluate output shapeInfos
LongType *hShapeInfo(nullptr), *hFWFinalPrevShapeInfo(nullptr), *hBWFinalPrevShapeInfo(nullptr);
ALLOCATE(hShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank), sd::LongType);
ALLOCATE(hFWFinalPrevShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank - 1), sd::LongType);
ALLOCATE(hBWFinalPrevShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank - 1), sd::LongType);
hShapeInfo[0] = inRank;
hFWFinalPrevShapeInfo[0] = hBWFinalPrevShapeInfo[0] = inRank - 1;
hShapeInfo[1] = time;
hShapeInfo[2] = hFWFinalPrevShapeInfo[1] = hBWFinalPrevShapeInfo[1] = bS;
hShapeInfo[3] = numUnitsFW + numUnitsBW;
hFWFinalPrevShapeInfo[2] = numUnitsFW;
hBWFinalPrevShapeInfo[2] = numUnitsBW;
ShapeUtils::updateStridesAndType(hShapeInfo, xShapeInfo, shape::order(xShapeInfo));
ShapeUtils::updateStridesAndType(hFWFinalPrevShapeInfo, xShapeInfo, shape::order(xShapeInfo));
ShapeUtils::updateStridesAndType(hBWFinalPrevShapeInfo, xShapeInfo, shape::order(xShapeInfo));
return SHAPELIST(CONSTANT(hShapeInfo), CONSTANT(hFWFinalPrevShapeInfo), CONSTANT(hBWFinalPrevShapeInfo));
}
} // namespace ops
} // namespace sd
#endif
@@ -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, created on 02.04.2018
//
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/rnn.h>
#if NOT_EXCLUDED(OP_static_rnn)
namespace sd {
namespace ops {
//////////////////////////////////////////////////////////////////////////
CUSTOM_OP_IMPL(static_rnn, 4, 2, false, 0, 0) {
auto x = INPUT_VARIABLE(0); // input [time x bS x inSize]
auto Wx = INPUT_VARIABLE(1); // input-to-hidden weights, [inSize x numUnits]
auto Wh = INPUT_VARIABLE(2); // hidden-to-hidden weights, [numUnits x numUnits]
auto b = INPUT_VARIABLE(3); // biases for, [2*numUnits]
NDArray* h0 = nullptr; // initial cell output (at time step = 0) [bS x numUnits]
NDArray* maxTimeStep =
nullptr; // vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
if (block.width() == 5) {
if ((*INPUT_VARIABLE(4)).rankOf() == 2)
h0 = INPUT_VARIABLE(4);
else
maxTimeStep = INPUT_VARIABLE(4);
} else if (block.width() == 6) {
h0 = INPUT_VARIABLE(4);
maxTimeStep = INPUT_VARIABLE(5);
}
auto h = OUTPUT_VARIABLE(0); // cell outputs [time x bS x numUnits]
auto hFinal = OUTPUT_VARIABLE(1); // at the end it will store cell final non-zero output [bS x numUnits]
REQUIRE_TRUE(x->rankOf() == 3, 0,
"STATIC_RNN custom operation: input array x must have rank = 3, but got %i instead !", x->rankOf());
REQUIRE_TRUE(Wx->rankOf() == 2, 0,
"STATIC_RNN custom operation: input-to-hidden weights array must have rank = 2, but got %i instead !",
Wx->rankOf());
const int time = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int inSize = x->sizeAt(2);
const int numUnits = Wx->sizeAt(1);
const std::vector<LongType> expectedWhShape = {numUnits, numUnits};
const std::vector<LongType> expectedbShape = {2 * numUnits};
REQUIRE_TRUE(Wh->isSameShape(expectedWhShape), 0,
"STATIC_RNN custom operation: wrong shape of hidden-to-hidden weights array, expected is %s, but got %s "
"instead !",
ShapeUtils::shapeAsString(expectedWhShape).c_str(), ShapeUtils::shapeAsString(Wh).c_str());
REQUIRE_TRUE(b->isSameShape(expectedbShape), 0,
"STATIC_RNN custom operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbShape).c_str(), ShapeUtils::shapeAsString(b).c_str());
if (h0) {
const std::vector<LongType> expectedh0Shape = {bS, numUnits};
REQUIRE_TRUE(
h0->isSameShape(expectedh0Shape), 0,
"STATIC_RNN custom operation: wrong shape of initial cell output array, expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0Shape).c_str(), ShapeUtils::shapeAsString(h0).c_str());
}
if (maxTimeStep)
REQUIRE_TRUE(maxTimeStep->isSameShape({bS}), 0,
"STATIC_RNN custom operation: wrong shape of maxTimeStep array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({bS}).c_str(), ShapeUtils::shapeAsString(maxTimeStep).c_str());
helpers::rnnTimeLoop(block.launchContext(), x, Wx, Wh, b, h0, maxTimeStep, h, hFinal);
return Status::OK;
}
DECLARE_TYPES(static_rnn) {
getOpDescriptor()->setAllowedInputTypes(ANY)->setAllowedOutputTypes({ALL_FLOATS});
}
DECLARE_SHAPE_FN(static_rnn) {
auto xShapeInfo = inputShape->at(0); // input [time x bS x inSize]
auto WxShapeInfo = inputShape->at(1); // input-to-hidden weights, [inSize x numUnits]
auto WhShapeInfo = inputShape->at(2); // hidden-to-hidden weights, [numUnits x numUnits]
auto bShapeInfo = inputShape->at(3); // biases for, [2*numUnits]
const LongType* h0ShapeInfo = nullptr; // initial cell output (at time step = 0) [bS x numUnits]
const LongType* maxTimeStepShapeInfo =
nullptr; // vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
if (block.width() == 5) {
if (inputShape->at(4)[0] == 2)
h0ShapeInfo = inputShape->at(4);
else
maxTimeStepShapeInfo = inputShape->at(4);
} else if (block.width() == 6) {
h0ShapeInfo = inputShape->at(4);
maxTimeStepShapeInfo = inputShape->at(5);
}
REQUIRE_TRUE(xShapeInfo[0] == 3, 0,
"STATIC_RNN custom operation: input array x must have rank = 3, but got %i instead !", xShapeInfo[0]);
REQUIRE_TRUE(WxShapeInfo[0] == 2, 0,
"STATIC_RNN custom operation: input-to-hidden weights array must have rank = 2, but got %i instead !",
WxShapeInfo[0]);
const int inRank = xShapeInfo[0];
const int time = xShapeInfo[1];
const int bS = xShapeInfo[2];
const int numUnits = WxShapeInfo[2];
const std::vector<LongType> expectedWhShape = {numUnits, numUnits};
const std::vector<LongType> expectedbShape = {2 * numUnits};
REQUIRE_TRUE(ShapeUtils::areShapesEqual(WhShapeInfo, expectedWhShape), 0,
"STATIC_RNN custom operation: wrong shape of hidden-to-hidden weights array, expected is %s, but got %s "
"instead !",
ShapeUtils::shapeAsString(expectedWhShape).c_str(), ShapeUtils::shapeAsString(WhShapeInfo).c_str());
REQUIRE_TRUE(ShapeUtils::areShapesEqual(bShapeInfo, expectedbShape), 0,
"STATIC_RNN custom operation: wrong shape of biases array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString(expectedbShape).c_str(), ShapeUtils::shapeAsString(bShapeInfo).c_str());
if (h0ShapeInfo) {
const std::vector<LongType> expectedh0Shape = {bS, numUnits};
REQUIRE_TRUE(
ShapeUtils::areShapesEqual(h0ShapeInfo, expectedh0Shape), 0,
"STATIC_RNN custom operation: wrong shape of initial cell output array, expected is %s but got %s instead !",
ShapeUtils::shapeAsString(expectedh0Shape).c_str(), ShapeUtils::shapeAsString(h0ShapeInfo).c_str());
}
if (maxTimeStepShapeInfo)
REQUIRE_TRUE(ShapeUtils::areShapesEqual(maxTimeStepShapeInfo, {bS}), 0,
"STATIC_RNN custom operation: wrong shape of maxTimeStep array, expected is %s, but got %s instead !",
ShapeUtils::shapeAsString({bS}).c_str(), ShapeUtils::shapeAsString(maxTimeStepShapeInfo).c_str());
// evaluate output shapeInfos
LongType *hShapeInfo(nullptr), *hPrevShapeInfo(nullptr);
ALLOCATE(hShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank), sd::LongType);
ALLOCATE(hPrevShapeInfo, block.getWorkspace(), shape::shapeInfoLength(inRank - 1), sd::LongType);
hShapeInfo[0] = inRank;
hPrevShapeInfo[0] = inRank - 1;
hShapeInfo[1] = time;
hShapeInfo[2] = hPrevShapeInfo[1] = bS;
hShapeInfo[3] = hPrevShapeInfo[2] = numUnits;
ShapeUtils::updateStridesAndType(hShapeInfo, xShapeInfo, shape::order(xShapeInfo));
ShapeUtils::updateStridesAndType(hPrevShapeInfo, xShapeInfo, shape::order(xShapeInfo));
return SHAPELIST(CONSTANT(hShapeInfo), CONSTANT(hPrevShapeInfo));
}
} // namespace ops
} // namespace sd
#endif