Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-18 09:29:24

0001 #ifndef TMVA_SOFIE_ROPERATOR_LSTM
0002 #define TMVA_SOFIE_ROPERATOR_LSTM
0003 
0004 #include "TMVA/RModel.hxx"
0005 #include "TMVA/ROperator.hxx"
0006 #include "TMVA/SOFIE_common.hxx"
0007 
0008 #include <memory>
0009 #include <sstream>
0010 #include <string>
0011 #include <vector>
0012 
0013 namespace TMVA::Experimental::SOFIE {
0014 
0015 /*! \brief Long Short-Term Memory operator
0016  *
0017  * Inference code generation for one-layer LSTM. Supports forward, reverse and bidirectional LSTM.
0018  * See the <a href="https://github.com/onnx/onnx/blob/master/docs/Operators.md#LSTM">ONNX documentation</a>
0019  * for details about the supported LSTM architectures.
0020  */
0021 template <typename T> class ROperator_LSTM final : public ROperator {
0022  private:
0023    std::vector<float> fAttrActivationAlpha;   ///< Sacling values used by some activation functions
0024    std::vector<float> fAttrActivationBeta;    ///< Scaling values used by some activation functions
0025    std::vector<std::string> fAttrActivations; ///< Activation functions
0026    float fAttrClip;                           ///< Clip threshold
0027    std::string fAttrDirection;                ///< Direction of processing
0028    size_t fAttrHiddenSize;                    ///< Number of the hidden layers
0029    size_t fAttrInputForget;                   ///< Forget gate
0030    size_t fAttrLayout;                        ///< Data layout
0031 
0032    std::string fNX;                           ///< Name of the input
0033    std::string fNW;                           ///< Name of the weights
0034    std::string fNR;                           ///< Name of the recurrence
0035    std::string fNB;                           ///< Name of the bias
0036    std::string fNSequence_lens;               ///< Name of length of the sequences
0037    std::string fNInitial_h;                   ///< Name of the initial value of the hidden states
0038    std::string fNInitial_c;                   ///< Name of the initial value of the cell states
0039    std::string fNP;                           ///< Name of peepholes
0040    std::string fNY;                           ///< Name of the output
0041    std::string fNY_h;                         ///< Name of the last sequence of the output
0042    std::string fNY_c;                         ///< Name of the last sequence of the cell states
0043 
0044    std::vector<size_t> fShapeX;               ///< Shape of the input
0045    std::vector<size_t> fShapeW;               ///< Shape of the weights
0046    std::vector<size_t> fShapeR;               ///< Shape of the recurrence
0047    std::vector<size_t> fShapeB;               ///< Shape of the bias
0048    std::vector<size_t> fShapeSequence_lens;   ///< Shape of the length of the sequences
0049    std::vector<size_t> fShapeInitial_h;       ///< Shape of the initial value of the hidden states
0050    std::vector<size_t> fShapeInitial_c;       ///< Shape of the initial value of the cell states
0051    std::vector<size_t> fShapeP;               ///< Shape of the peepholes
0052    std::vector<size_t> fShapeY;               ///< Shape of the output
0053    std::vector<size_t> fShapeY_h;             ///< Shape of the last sequence of the output
0054    std::vector<size_t> fShapeY_c;             ///< Shape of the last sequence of the cell states
0055 
0056    std::string fType;                         ///< Type of the tensors
0057 
0058  public:
0059    /*! Default constructor of ROperator_LSTM */
0060    ROperator_LSTM() {}
0061 
0062    /*! \brief Constructor of ROperator_LSTM from the attributes
0063     *
0064     * \param activation_alpha scaling values used by some activation functions
0065     * \param activation_beta scaling values used by some activation functions
0066     * \param activations activation functions
0067     * \param clip clip threshold
0068     * \param direction direction of processing of the sequneces
0069     * \param hidden_size number of hidden layers
0070     * \param input_forget forget gate
0071     * \param layout data layout
0072     * \param nameX name of the input tensor
0073     * \param nameW name of the weight tensor
0074     * \param nameR name of the recurrence tensor
0075     * \param nameB name of the bias tensor
0076     * \param nameSequence_lens name of the length of the sequences
0077     * \param nameInitial_h name of the initial value of the hidden states
0078     * \param nameInitial_c name of the initial value of the cell states
0079     * \param nameP name of the peepholes tensor
0080     * \param nameY name of the output
0081     * \param nameY_h name of the last sequence of the output
0082     * \param nameY_c name of the last sequence of the cell states
0083     */
0084    ROperator_LSTM(std::vector<float> activation_alpha,
0085                  std::vector<float> activation_beta,
0086                  std::vector<std::string> activations, float clip,
0087                  std::string direction, size_t hidden_size,
0088                  size_t input_forget, size_t layout,
0089                  std::string nameX, std::string nameW, std::string nameR,
0090                  std::string nameB, std::string nameSequence_lens,
0091                  std::string nameInitial_h, std::string nameInitial_c, std::string nameP,
0092                  std::string nameY, std::string nameY_h, std::string nameY_c)
0093        : fAttrActivationAlpha(activation_alpha),
0094          fAttrActivationBeta(activation_beta), fAttrActivations(activations),
0095          fAttrClip(clip), fAttrDirection(direction), fAttrHiddenSize(hidden_size),
0096          fAttrInputForget(input_forget), fAttrLayout(layout),
0097          fNX(UTILITY::Clean_name(nameX)), fNW(UTILITY::Clean_name(nameW)),
0098          fNR(UTILITY::Clean_name(nameR)), fNB(UTILITY::Clean_name(nameB)),
0099          fNSequence_lens(UTILITY::Clean_name(nameSequence_lens)),
0100          fNInitial_h(UTILITY::Clean_name(nameInitial_h)),
0101          fNInitial_c(UTILITY::Clean_name(nameInitial_c)), fNP(UTILITY::Clean_name(nameP)),
0102          fNY(UTILITY::Clean_name(nameY)), fNY_h(UTILITY::Clean_name(nameY_h)),
0103          fNY_c(UTILITY::Clean_name(nameY_c)) {
0104       if (std::is_same<T, float>::value) {
0105          fType = "float";
0106       } else {
0107          throw std::runtime_error(
0108              "TMVA SOFIE Encountered unsupported type parsing a LSTM operator");
0109       }
0110 
0111       fInputTensorNames = { fNX, fNW, fNR };
0112       if (!fNB.empty()){
0113          fInputTensorNames.emplace_back(fNB);
0114       }
0115       if (!fNSequence_lens.empty()){
0116          fInputTensorNames.emplace_back(fNSequence_lens);
0117       }
0118       if (!fNInitial_h.empty()){
0119          fInputTensorNames.emplace_back(fNInitial_h);
0120       }
0121       if (!fNInitial_c.empty()){
0122          fInputTensorNames.emplace_back(fNInitial_c);
0123       }
0124       if (!fNP.empty()){
0125          fInputTensorNames.emplace_back(fNP);
0126       }
0127 
0128       fOutputTensorNames = { };
0129       if (!fNY.empty()){
0130          fOutputTensorNames.emplace_back(fNY);
0131       }
0132       if (!fNY_h.empty()){
0133          fOutputTensorNames.emplace_back(fNY_h);
0134       }
0135       if (!fNY_c.empty()){
0136          fOutputTensorNames.emplace_back(fNY_c);
0137       }
0138    }
0139 
0140    /*! \brief Infers the type of the output tensors
0141     *
0142     * \param input type of the input tensors
0143     */
0144    std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override;
0145 
0146    /*! \brief Infers the shape of the output tensors
0147     *
0148     * \param input shape of the input tensors
0149     */
0150    std::vector<std::vector<size_t>>
0151    ShapeInference(std::vector<std::vector<size_t>> input) override;
0152 
0153    /*! \brief Initialize the model
0154     *
0155     * \param model Model
0156     */
0157    void Initialize(RModel &) override;
0158 
0159    /*! \brief Generate the inference code
0160     *
0161     * \param OpName name of the operator
0162     */
0163    std::string Generate(std::string OpName) override;
0164 
0165    /*! \brief Generate the code for the Session internal data vectors
0166     *
0167     * \param opName name of the operator
0168     */
0169    std::string GenerateSessionMembersCode(std::string opName) override;
0170 
0171    /*! \brief Returns the blas routines needed to compile the generated code
0172     */
0173    std::vector<std::string> GetBlasRoutines() override { return { std::string("Gemm"), std::string("Axpy") }; }
0174 };
0175 
0176 template <typename T>
0177 auto ROperator_LSTM<T>::TypeInference(std::vector<ETensorType> input) -> std::vector<ETensorType>
0178 {
0179    ETensorType out = input[0];
0180    return {out, out};
0181 }
0182 
0183 template <typename T>
0184 auto ROperator_LSTM<T>::ShapeInference(std::vector<std::vector<size_t>> input) -> std::vector<std::vector<size_t>>
0185 {
0186    size_t num_directions = input[1][0];
0187    size_t hidden_size = input[1][1] / 4;
0188    if (fAttrLayout == 0) {
0189       size_t seq_length = input[0][0];
0190       size_t batch_size = input[0][1];
0191       std::vector<std::vector<size_t>> ret({{seq_length, num_directions, batch_size, hidden_size},
0192                                             {num_directions, batch_size, hidden_size},
0193                                             {num_directions, batch_size, hidden_size}});
0194       return ret;
0195    } else {
0196       size_t batch_size = input[0][0];
0197       size_t seq_length = input[0][1];
0198       std::vector<std::vector<size_t>> ret({{batch_size, seq_length, num_directions, hidden_size},
0199                                             {batch_size, num_directions, hidden_size},
0200                                             {batch_size, num_directions, hidden_size}});
0201       return ret;
0202    }
0203 }
0204 
0205 template <typename T>
0206 auto ROperator_LSTM<T>::Initialize(RModel &model) -> void
0207 {
0208    fUseSession = model.UseSession();
0209    // Check the input and output tensors
0210    if (!model.CheckIfTensorAlreadyExist(fNX)) {
0211       throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNX + "  is not found in model.");
0212    }
0213    fShapeX = model.GetTensorShape(fNX);
0214    if (fShapeX.size() != 3) {
0215       throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNX + " is not of 3 dimensions.");
0216    }
0217    if (!model.CheckIfTensorAlreadyExist(fNW)) {
0218       throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNW + "  is not found in model.");
0219    }
0220    fShapeW = model.GetTensorShape(fNW);
0221    if (fShapeW.size() != 3) {
0222       throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNW + " is not of 3 dimensions.");
0223    }
0224    if (!model.CheckIfTensorAlreadyExist(fNR)) {
0225       throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNR + "  is not found in model.");
0226    }
0227    fShapeR = model.GetTensorShape(fNR);
0228    if (fShapeR.size() != 3) {
0229       throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNR + " is not of 3 dimensions.");
0230    }
0231    if (!fNB.empty()) {
0232       if (!model.CheckIfTensorAlreadyExist(fNB)) {
0233          throw std::runtime_error("TMVA SOFIE LSTM op input tensor " + fNB + " is not  found in model.");
0234       }
0235       fShapeB = model.GetTensorShape(fNB);
0236       if (fShapeB.size() != 2 && fShapeB.size() != 5) {
0237          throw std::runtime_error("TMVA SOFIE LSTM op input tensor " + fNB + " is not of 2 or 5 dimensions.");
0238       }
0239       if (fShapeB.size() == 2) {
0240          // Broadcasting the bias
0241          auto original_data = model.GetInitializedTensorData(fNB);
0242          size_t num_directions = fShapeW[0];
0243          size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
0244          size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
0245          if (fType == "float") {
0246             float *original_bias = static_cast<float *>(original_data.get());
0247             float *new_bias = new float[4 * num_directions * seq_length * batch_size * fAttrHiddenSize];
0248             for (size_t gate = 0; gate < 4; gate++) {
0249                std::vector<float> sum(fAttrHiddenSize);
0250                for (size_t direction = 0; direction < num_directions; direction++) {
0251                   size_t offset = direction * 8 * fAttrHiddenSize + gate * fAttrHiddenSize;
0252                   for (size_t h = 0; h < fAttrHiddenSize; h++) {
0253                      sum[h] = original_bias[offset + h] + original_bias[offset + h + 4 * fAttrHiddenSize];
0254                   }
0255                   for (size_t seq = 0; seq < seq_length; seq++) {
0256                      for (size_t batch = 0; batch < batch_size; batch++) {
0257                         size_t bias_offset = gate * num_directions * seq_length * batch_size * fAttrHiddenSize +
0258                                              direction * seq_length * batch_size * fAttrHiddenSize +
0259                                              seq * batch_size * fAttrHiddenSize + batch * fAttrHiddenSize;
0260                         std::copy(sum.begin(), sum.end(), new_bias + bias_offset);
0261                      }
0262                   }
0263                }
0264             }
0265             std::vector<size_t> new_bias_shape = {4, num_directions, seq_length, batch_size, fAttrHiddenSize};
0266             std::shared_ptr<void> new_bias_ptr(new_bias, std::default_delete<float[]>());
0267             model.UpdateInitializedTensor(fNB, model.GetTensorType(fNB), new_bias_shape, new_bias_ptr);
0268             fShapeB = model.GetTensorShape(fNB);
0269          }
0270       }
0271    }
0272    if (!fNSequence_lens.empty()) {
0273       if (!model.CheckIfTensorAlreadyExist(fNSequence_lens)) {
0274          throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNSequence_lens + "is not found in model.");
0275       }
0276       fShapeSequence_lens = model.GetTensorShape(fNSequence_lens);
0277       if (fShapeSequence_lens.size() != 1) {
0278          throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNSequence_lens + " is not of 1 dimension.");
0279       }
0280    }
0281    if (!fNInitial_h.empty()) {
0282       if (!model.CheckIfTensorAlreadyExist(fNInitial_h)) {
0283          throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNInitial_h + " is not found in model.");
0284       }
0285       fShapeInitial_h = model.GetTensorShape(fNInitial_h);
0286       if (fShapeInitial_h.size() != 3) {
0287          throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNInitial_h + " is not of 3 dimensions.");
0288       }
0289    }
0290    if (!fNInitial_c.empty()) {
0291       if (!model.CheckIfTensorAlreadyExist(fNInitial_c)) {
0292          throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNInitial_c + " is not found in model.");
0293       }
0294       fShapeInitial_c = model.GetTensorShape(fNInitial_c);
0295       if (fShapeInitial_c.size() != 3) {
0296          throw std::runtime_error("TMVA SOFIE LSTM Op input tensor " + fNInitial_c + " is not of 3 dimensions.");
0297       }
0298    }
0299    if (!fNP.empty()) {
0300       if (!model.CheckIfTensorAlreadyExist(fNP)) {
0301          throw std::runtime_error("TMVA SOFIE LSTM op input tensor " + fNP + " is not  found in model.");
0302       }
0303       fShapeP = model.GetTensorShape(fNP);
0304       if (fShapeP.size() != 2 && fShapeP.size() != 4) {
0305          throw std::runtime_error("TMVA SOFIE LSTM op input tensor " + fNP + " is not of 2 or 4 dimensions.");
0306       }
0307       if (fShapeP.size() == 2) {
0308          // Broadcasting the weight for peepholes
0309          auto original_data = model.GetInitializedTensorData(fNP);
0310          size_t num_directions = fShapeW[0];
0311          size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
0312          if (fType == "float") {
0313             float *original_p = static_cast<float *>(original_data.get());
0314             float *new_p = new float[num_directions * 3 * batch_size * fAttrHiddenSize];
0315             for (size_t direction = 0; direction < num_directions; direction++) {
0316                for (size_t gate = 0; gate < 3; gate++) {
0317                   size_t p_offset = direction * 3 * fAttrHiddenSize + gate * fAttrHiddenSize;
0318                   for (size_t batch = 0; batch < batch_size; batch++) {
0319                      size_t offset = direction * 3 * batch_size * fAttrHiddenSize +
0320                                      gate * batch_size * fAttrHiddenSize + batch * fAttrHiddenSize;
0321                      std::copy(original_p + p_offset, original_p + p_offset + fAttrHiddenSize, new_p + offset);
0322                   }
0323                }
0324             }
0325             std::vector<size_t> new_p_shape = {num_directions, 3, batch_size, fAttrHiddenSize};
0326             std::shared_ptr<void> new_p_ptr(new_p, std::default_delete<float[]>());
0327             model.UpdateInitializedTensor(fNP, model.GetTensorType(fNP), new_p_shape, new_p_ptr);
0328             fShapeP = model.GetTensorShape(fNP);
0329          }
0330       }
0331    }
0332    if (!fNY.empty()) {
0333       fShapeY = ShapeInference({fShapeX, fShapeW})[0];
0334       if (!model.CheckIfTensorAlreadyExist(fNY)) {
0335          model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShapeY);
0336       }
0337    }
0338    if (!fNY_h.empty()) {
0339       fShapeY_h = ShapeInference({fShapeX, fShapeW})[1];
0340       if (!model.CheckIfTensorAlreadyExist(fNY_h)) {
0341          model.AddIntermediateTensor(fNY_h, model.GetTensorType(fNX), fShapeY_h);
0342       }
0343    }
0344    if (!fNY_c.empty()) {
0345       fShapeY_c = ShapeInference({fShapeX, fShapeW})[2];
0346       if (!model.CheckIfTensorAlreadyExist(fNY_c)) {
0347          model.AddIntermediateTensor(fNY_c, model.GetTensorType(fNX), fShapeY_c);
0348       }
0349    }
0350    // Check the attributes
0351    for (auto &activation : fAttrActivations) {
0352       if (activation != "Relu" && activation != "Tanh" && activation != "Sigmoid" && activation != "Affine" &&
0353           activation != "LeakyRelu" && activation != "ThresholdRelu" && activation != "ScaledTanh" &&
0354           activation != "HardSigmoid" && activation != "Elu" && activation != "Softsign" && activation != "Softplus") {
0355          throw std::runtime_error("TMVA SOFIE - Activation function " + activation + " not implemented");
0356       }
0357    }
0358    if (fAttrDirection != "forward" && fAttrDirection != "backward" && fAttrDirection != "bidirectional") {
0359       throw std::runtime_error("TMVA SOFIE - Invalid LSTM direction fAttrDirection = " + fAttrDirection);
0360    }
0361    if (4 * fAttrHiddenSize != fShapeW[1]) {
0362       throw std::runtime_error("TMVA SOFIE - fAttrHiddenSize must be equal to " + std::to_string(fShapeW[1] / 4));
0363    }
0364    if (fAttrInputForget > 1) {
0365       throw std::runtime_error("TMVA SOFIE - fAttrInputForget = " + std::to_string(fAttrInputForget) +
0366                                " must be 0 or 1.");
0367    }
0368    if (fAttrLayout > 1) {
0369       throw std::runtime_error("TMVA SOFIE - Layout fAttrLayout = " + std::to_string(fAttrLayout) +
0370                                " must be 0 (timewise) or 1 (batchwise)");
0371    }
0372    if (fAttrActivations.empty()) {
0373       if (fAttrDirection == "bidirectional") {
0374          fAttrActivations = {"Sigmoid", "Tanh", "Tanh", "Sigmoid", "Tanh", "Tanh"};
0375       } else {
0376          fAttrActivations = {"Sigmoid", "Tanh", "Tanh"};
0377       }
0378    }
0379 }
0380 
0381 // generate code for Session data members (e.g. internal vectors)
0382 template <typename T>
0383 std::string ROperator_LSTM<T>::GenerateSessionMembersCode(std::string opName)
0384 {
0385    opName = "op_" + opName;
0386    std::stringstream out;
0387 
0388    size_t num_directions = fShapeW[0];
0389    size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
0390    size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
0391    size_t input_size = fShapeX[2];
0392 
0393    struct Block {
0394       std::string name;
0395       size_t size;
0396    };
0397 
0398    std::vector<Block> blocks;
0399 
0400    size_t ff_size = seq_length * batch_size * fAttrHiddenSize;
0401    size_t hs_size = seq_length * num_directions * batch_size * fAttrHiddenSize;
0402 
0403    // Layout-dependent buffers
0404    if (fAttrLayout != 0) {
0405       blocks.push_back({"input", seq_length * batch_size * input_size});
0406       blocks.push_back({"initial_hidden_state", num_directions * batch_size * fAttrHiddenSize});
0407       blocks.push_back({"initial_cell_state", num_directions * batch_size * fAttrHiddenSize});
0408    }
0409 
0410    // Feedforward gates
0411    blocks.push_back({"ff_input_gate", ff_size});
0412    blocks.push_back({"ff_output_gate", ff_size});
0413    blocks.push_back({"ff_cell_gate", ff_size});
0414    if (fAttrInputForget == 0)
0415       blocks.push_back({"ff_forget_gate", ff_size});
0416 
0417    // Gate outputs
0418    blocks.push_back({"input_gate", hs_size});
0419    blocks.push_back({"output_gate", hs_size});
0420    blocks.push_back({"cell_gate", hs_size});
0421    if (fAttrInputForget == 0)
0422       blocks.push_back({"forget_gate", hs_size});
0423 
0424    // Cell state
0425    blocks.push_back({"cell_state", hs_size});
0426    blocks.push_back({"new_cell_state", hs_size});
0427 
0428    // Hidden state (conditional)
0429    if (fAttrLayout != 0 || fNY.empty()) {
0430       blocks.push_back({"hidden_state", hs_size});
0431    }
0432 
0433    // Compute total size
0434    size_t total_size = 0;
0435    for (const auto &b : blocks) {
0436       total_size += b.size;
0437    }
0438 
0439    // Backing storage
0440    out << "std::vector<" << fType << "> fVec_" << opName << "_buffer = std::vector<" << fType << ">(" << total_size
0441        << ");\n";
0442 
0443    // Emit pointers
0444    std::size_t offset = 0;
0445    for (const auto &b : blocks) {
0446       out << fType << "* fVec_" << opName << "_" << b.name << " = fVec_" << opName << "_buffer.data() + " << offset
0447           << ";\n";
0448       offset += b.size;
0449    }
0450 
0451    out << "\n";
0452 
0453    return out.str();
0454 }
0455 
0456 template <typename T>
0457 auto ROperator_LSTM<T>::Generate(std::string OpName) -> std::string
0458 {
0459    OpName = "op_" + OpName;
0460    std::stringstream out;
0461 
0462    size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
0463    size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
0464    size_t input_size = fShapeX[2];
0465    size_t num_directions = fShapeW[0];
0466 
0467    // set the input
0468    if (fAttrLayout == 0) {
0469       out << SP << fType << " const *" << OpName << "_input = tensor_" << fNX << ";\n";
0470    } else {
0471       if (fUseSession)
0472          out << SP << fType << " * " << OpName << "_input = this->fVec_" << OpName << "_input;\n";
0473       else
0474          out << SP << fType << "  " << OpName << "_input[" << seq_length * batch_size * input_size << "] = {0};\n";
0475 
0476       out << SP << "for(size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
0477       out << SP << SP << "for(size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0478       out << SP << SP << SP << "for(size_t i = 0; i < " << input_size << "; i++) {\n";
0479       out << SP << SP << SP << SP << OpName << "_input[seq * " << batch_size * input_size << " + batch * " << input_size
0480           << " + i] = " << "tensor_" << fNX << "[batch * " << seq_length * input_size << " + seq * " << input_size
0481           << " + i];\n";
0482       out << SP << SP << SP << "}\n";
0483       out << SP << SP << "}\n";
0484       out << SP << "}\n";
0485    }
0486 
0487    // Set the initial hidden state
0488    if (!fNInitial_h.empty()) {
0489       if (fAttrLayout == 0) {
0490          out << SP << fType << " const*" << OpName << "_initial_hidden_state = " << " tensor_" << fNInitial_h << ";\n";
0491       } else {
0492          if (fUseSession)
0493             out << SP << fType << " const* " << OpName << "_initial_hidden_state = this->fVec_" << OpName
0494                 << "_initial_hidden_state;\n";
0495          else
0496             out << SP << fType << "  " << OpName << "_initial_hidden_state["
0497                 << num_directions * batch_size * fAttrHiddenSize << "] = {0};\n";
0498 
0499          for (size_t direction = 0; direction < num_directions; direction++) {
0500             out << SP << "for(size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0501             out << SP << SP << "for(size_t h = 0; h < " << fAttrHiddenSize << "; h++) {\n";
0502             out << SP << SP << SP << OpName << "_initial_hidden_state[" << direction * batch_size * fAttrHiddenSize
0503                 << " + batch * " << fAttrHiddenSize << " + h] = tensor_" << fNInitial_h << "[batch * "
0504                 << num_directions * fAttrHiddenSize << " + " << direction * fAttrHiddenSize << " + h];\n";
0505             out << SP << SP << "}\n";
0506             out << SP << "}\n";
0507          }
0508       }
0509    }
0510 
0511    // Set the initial cell state
0512    if (!fNInitial_c.empty()) {
0513       if (fAttrLayout == 0) {
0514          out << SP << fType << " const*" << OpName << "_initial_cell_state = " << " tensor_" << fNInitial_c << ";\n";
0515       } else {
0516          if (fUseSession)
0517             out << SP << fType << " const* " << OpName << "_initial_cell_state = this->fVec_" << OpName
0518                 << "_initial_cell_state;\n";
0519          else
0520             out << SP << fType << "  " << OpName << "_initial_cell_state["
0521                 << num_directions * batch_size * fAttrHiddenSize << "] = {0};\n";
0522 
0523          for (size_t direction = 0; direction < num_directions; direction++) {
0524             out << SP << "for(size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0525             out << SP << SP << "for(size_t h = 0; h < " << fAttrHiddenSize << "; h++) {\n";
0526             out << SP << SP << SP << OpName << "_initial_cell_state[" << direction * batch_size * fAttrHiddenSize
0527                 << " + batch * " << fAttrHiddenSize << " + h] = tensor_" << fNInitial_c << "[batch * "
0528                 << num_directions * fAttrHiddenSize << " + " << direction * fAttrHiddenSize << " + h];\n";
0529             out << SP << SP << "}\n";
0530             out << SP << "}\n";
0531          }
0532       }
0533    }
0534 
0535    // Set the feedforward
0536    size_t ff_size = seq_length * batch_size * fAttrHiddenSize;
0537    if (fUseSession) {
0538       out << SP << fType << " * " << OpName << "_ff_input_gate = this->fVec_" << OpName << "_ff_input_gate;\n";
0539       out << SP << fType << " * " << OpName << "_ff_output_gate = this->fVec_" << OpName << "_ff_output_gate;\n";
0540       out << SP << fType << " * " << OpName << "_ff_cell_gate = this->fVec_" << OpName << "_ff_cell_gate;\n";
0541       if (fAttrInputForget == 0) {
0542          out << SP << fType << " * " << OpName << "_ff_forget_gate = this->fVec_" << OpName
0543              << "_ff_forget_gate;\n";
0544       }
0545    } else {
0546       out << SP << fType << "  " << OpName << "_ff_input_gate[" << ff_size << "] = {0};\n";
0547       out << SP << fType << "  " << OpName << "_ff_output_gate[" << ff_size << "] = {0};\n";
0548       out << SP << fType << "  " << OpName << "_ff_cell_gate[" << ff_size << "] = {0};\n";
0549       if (fAttrInputForget == 0) {
0550          out << SP << fType << "  " << OpName << "_ff_forget_gate[" << ff_size << "] = {0};\n";
0551       }
0552    }
0553    // Set the gates
0554    size_t hidden_state_size = seq_length * num_directions * batch_size * fAttrHiddenSize;
0555    if (fUseSession) {
0556       out << SP << fType << " * " << OpName << "_input_gate = this->fVec_" << OpName << "_input_gate;\n";
0557       out << SP << fType << " * " << OpName << "_output_gate = this->fVec_" << OpName << "_output_gate;\n";
0558       out << SP << fType << " * " << OpName << "_cell_gate = this->fVec_" << OpName << "_cell_gate;\n";
0559       if (fAttrInputForget == 0) {
0560          out << SP << fType << " * " << OpName << "_forget_gate = this->fVec_" << OpName << "_forget_gate;\n";
0561       }
0562    } else {
0563       out << SP << fType << "  " << OpName << "_input_gate[" << hidden_state_size << "] = {0};\n";
0564       out << SP << fType << "  " << OpName << "_output_gate[" << hidden_state_size << "] = {0};\n";
0565       out << SP << fType << "  " << OpName << "_cell_gate[" << hidden_state_size << "] = {0};\n";
0566       if (fAttrInputForget == 0) {
0567          out << SP << fType << "  " << OpName << "_forget_gate[" << hidden_state_size << "] = {0};\n";
0568       }
0569    }
0570    // Set the cell state and the new cell state = h(cell state)
0571    if (fUseSession) {
0572       out << SP << fType << " * " << OpName << "_cell_state = this->fVec_" << OpName << "_cell_state;\n";
0573       out << SP << fType << " * " << OpName << "_new_cell_state = this->fVec_" << OpName << "_new_cell_state;\n";
0574    } else {
0575       out << SP << fType << "  " << OpName << "_cell_state[" << hidden_state_size << "] = {0};\n";
0576       out << SP << fType << "  " << OpName << "_new_cell_state[" << hidden_state_size << "] = {0};\n";
0577    }
0578 
0579    // Set the hidden state
0580    if (fAttrLayout == 0 && !fNY.empty()) {
0581       out << SP << fType << " *" << OpName << "_hidden_state = tensor_" << fNY << ";\n";
0582    } else {
0583       if (fUseSession) {
0584          out << SP << fType << " * " << OpName << "_hidden_state = this->fVec_" << OpName << "_hidden_state;\n";
0585       } else {
0586          out << SP << fType << "  " << OpName << "_hidden_state[" << hidden_state_size << "] = {0};\n";
0587       }
0588    }
0589 
0590    out << SP << "char " << OpName << "_transA = 'N';\n";
0591    out << SP << "char " << OpName << "_transB = 'T';\n";
0592    out << SP << "int " << OpName << "_m = " << seq_length * batch_size << ";\n";
0593    out << SP << "int " << OpName << "_n = " << fAttrHiddenSize << ";\n";
0594    out << SP << "int " << OpName << "_k = " << input_size << ";\n";
0595    if (fType == "float") {
0596       out << SP << fType << "  " << OpName << "_alpha = 1.;\n";
0597       out << SP << fType << "  " << OpName << "_beta = 0.;\n";
0598    }
0599    if (!fNB.empty()) {
0600       out << SP << "int " << OpName << "_bias_size = " << seq_length * batch_size * fAttrHiddenSize << ";\n";
0601       out << SP << "int " << OpName << "_incx = 1;\n";
0602       out << SP << "int " << OpName << "_incy = 1;\n";
0603    }
0604 
0605    auto emit_sgemm = [&](const std::string &out_name, size_t offset) -> std::string {
0606       std::stringstream ss;
0607       ss << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName << "_n, &" << OpName
0608          << "_m, &" << OpName << "_k, &" << OpName << "_alpha, tensor_" << fNW;
0609 
0610       if (offset != 0)
0611          ss << " + " << offset;
0612 
0613       ss << ", &" << OpName << "_k, " << OpName << "_input, &" << OpName << "_k, &" << OpName << "_beta, " << OpName
0614          << "_" << out_name << ", &" << OpName << "_n);\n";
0615       return ss.str();
0616    };
0617 
0618    for (size_t direction = 0; direction < num_directions; direction++) {
0619       if (direction == 0) {
0620          if (fType == "float") {
0621             // input_gate = input * weight_i^T
0622             out << SP << emit_sgemm("ff_input_gate", 0);
0623             // output_gate = input * weight_o^T
0624             size_t wo_offset = fAttrHiddenSize * input_size;
0625             out << SP << emit_sgemm("ff_output_gate", wo_offset);
0626             // cell_gate = input * weight_c^T
0627             size_t wc_offset = 3 * fAttrHiddenSize * input_size;
0628             out << SP << emit_sgemm("ff_cell_gate", wc_offset);
0629          }
0630       } else {
0631          if (fType == "float") {
0632             // input_gate = input * weight_i^T
0633             out << SP << emit_sgemm("ff_input_gate", 4 * fAttrHiddenSize * input_size);
0634             // output_gate = input * weight_o^T
0635             size_t wo_offset = 4 * fAttrHiddenSize * input_size + 1 * fAttrHiddenSize * input_size;
0636             out << SP << emit_sgemm("ff_output_gate", wo_offset);
0637             // cell_gate = input * weight_c^T
0638             size_t wc_offset = 4 * fAttrHiddenSize * input_size + 3 * fAttrHiddenSize * input_size;
0639             out << SP << emit_sgemm("ff_cell_gate", wc_offset);
0640          }
0641       }
0642       if (fAttrInputForget == 0) {
0643          // forget_gate = input * weight_f^T
0644          if (direction == 0) {
0645             if (fType == "float") {
0646                size_t wf_offset = 2 * fAttrHiddenSize * input_size;
0647                out << SP << emit_sgemm("ff_forget_gate", wf_offset);
0648             }
0649          } else {
0650             if (fType == "float") {
0651                size_t wf_offset = 4 * fAttrHiddenSize * input_size + 2 * fAttrHiddenSize * input_size;
0652                out << SP << emit_sgemm("ff_forget_gate", wf_offset);
0653             }
0654          }
0655       }
0656 
0657       // Add the bias
0658       if (!fNB.empty()) {
0659          if (direction == 0) {
0660             if (fType == "float") {
0661                // ff_input_gate += bias_i
0662                out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << ", &"
0663                    << OpName << "_incx, " << OpName << "_ff_input_gate, &" << OpName << "_incy);\n";
0664                // ff_output_gate += bias_o
0665                size_t bo_offset = seq_length * batch_size * fAttrHiddenSize;
0666                out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << " + "
0667                    << bo_offset << ", &" << OpName << "_incx, " << OpName << "_ff_output_gate, &" << OpName
0668                    << "_incy);\n";
0669                // ff_cell_gate += bias_c
0670                size_t bc_offset = 3 * seq_length * batch_size * fAttrHiddenSize;
0671                out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << " + "
0672                    << bc_offset << ", &" << OpName << "_incx, " << OpName << "_ff_cell_gate, &" << OpName
0673                    << "_incy);\n";
0674             }
0675          } else {
0676             if (fType == "float") {
0677                // ff_input_gate += bias_i
0678                size_t bi_offset = 4 * seq_length * batch_size * fAttrHiddenSize;
0679                out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << " + "
0680                    << bi_offset << ", &" << OpName << "_incx, " << OpName << "_ff_input_gate, &" << OpName
0681                    << "_incy);\n";
0682                // ff_output_gate += bias_o
0683                size_t bo_offset =
0684                   4 * seq_length * batch_size * fAttrHiddenSize + seq_length * batch_size * fAttrHiddenSize;
0685                out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << " + "
0686                    << bo_offset << ", &" << OpName << "_incx, " << OpName << "_ff_output_gate, &" << OpName
0687                    << "_incy);\n";
0688                // ff_cell_gate += bias_c
0689                size_t bc_offset = 4 * num_directions * seq_length * batch_size * fAttrHiddenSize +
0690                                   3 * seq_length * batch_size * fAttrHiddenSize;
0691                out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << " + "
0692                    << bc_offset << ", &" << OpName << "_incx, " << OpName << "_ff_cell_gate, &" << OpName
0693                    << "_incy);\n";
0694             }
0695          }
0696          if (fAttrInputForget == 0) {
0697             // ff_forget_gate += bias_f
0698             if (direction == 0) {
0699                if (fType == "float") {
0700                   size_t bo_offset = 2 * seq_length * batch_size * fAttrHiddenSize;
0701                   out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB
0702                       << " + " << bo_offset << ", &" << OpName << "_incx, " << OpName << "_ff_forget_gate, &" << OpName
0703                       << "_incy);\n";
0704                }
0705             } else {
0706                if (fType == "float") {
0707                   size_t bo_offset =
0708                      4 * seq_length * batch_size * fAttrHiddenSize + 2 * seq_length * batch_size * fAttrHiddenSize;
0709                   out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB
0710                       << " + " << bo_offset << ", &" << OpName << "_incx, " << OpName << "_ff_forget_gate, &" << OpName
0711                       << "_incy);\n";
0712                }
0713             }
0714          }
0715       }
0716 
0717       // Copy ff_input_gate, ff_output_gate, ff_cell_gate and ff_forget_gate into input_gate, output_gate,
0718       //   cell_gate and forget_gate
0719       out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
0720       out << SP << SP << "size_t ff_offset = seq * " << batch_size * fAttrHiddenSize << ";\n";
0721       if (direction == 0) {
0722          out << SP << SP << "size_t gate_offset = seq * " << num_directions * batch_size * fAttrHiddenSize << ";\n";
0723       } else {
0724          out << SP << SP << "size_t gate_offset = seq * " << num_directions * batch_size * fAttrHiddenSize << " + "
0725              << batch_size * fAttrHiddenSize << ";\n";
0726       }
0727       size_t ff_seq_size = batch_size * fAttrHiddenSize;
0728       out << SP << SP << "std::copy(" << OpName << "_ff_input_gate + ff_offset, " << OpName
0729           << "_ff_input_gate + ff_offset + " << ff_seq_size << ", " << OpName << "_input_gate + gate_offset);\n";
0730       out << SP << SP << "std::copy(" << OpName << "_ff_output_gate + ff_offset, " << OpName
0731           << "_ff_output_gate + ff_offset + " << ff_seq_size << ", " << OpName << "_output_gate + gate_offset);\n";
0732       out << SP << SP << "std::copy(" << OpName << "_ff_cell_gate + ff_offset, " << OpName
0733           << "_ff_cell_gate + ff_offset + " << ff_seq_size << ", " << OpName << "_cell_gate + gate_offset);\n";
0734       if (fAttrInputForget == 0) {
0735          out << SP << SP << "std::copy(" << OpName << "_ff_forget_gate + ff_offset, " << OpName
0736              << "_ff_forget_gate + ff_offset + " << ff_seq_size << ", " << OpName << "_forget_gate + gate_offset);\n";
0737       }
0738       out << SP << "}\n";
0739 
0740       out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
0741       if (fAttrDirection == "backward" || direction == 1) {
0742          out << SP << SP << "size_t index = " << seq_length - 1 << " - seq;\n";
0743       } else {
0744          out << SP << SP << "size_t index = seq;\n";
0745       }
0746       out << SP << SP << "int m2 = " << batch_size << ";\n";
0747       if (direction == 0) {
0748          out << SP << SP << "size_t offset = index * " << num_directions * batch_size * fAttrHiddenSize << ";\n";
0749       } else {
0750          out << SP << SP << "size_t offset = index * " << num_directions * batch_size * fAttrHiddenSize << " + "
0751              << batch_size * fAttrHiddenSize << ";\n";
0752       }
0753       size_t size = batch_size * fAttrHiddenSize;
0754       // gate = gate + initial_hidden_state * Recurrence^T
0755       out << SP << SP << "if (seq == 0) {\n";
0756       if (!fNInitial_h.empty()) {
0757          if (direction == 0) {
0758             if (fType == "float") {
0759                out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0760                    << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << ", &" << OpName
0761                    << "_n, " << OpName << "_initial_hidden_state, &" << OpName << "_n, &" << OpName << "_alpha, "
0762                    << OpName << "_input_gate + offset, &" << OpName << "_n);\n";
0763                size_t ro_offset = fAttrHiddenSize * fAttrHiddenSize;
0764                out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0765                    << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << ro_offset
0766                    << ", &" << OpName << "_n, " << OpName << "_initial_hidden_state, &" << OpName << "_n, &" << OpName
0767                    << "_alpha, " << OpName << "_output_gate + offset, &" << OpName << "_n);\n";
0768                size_t rc_offset = 3 * fAttrHiddenSize * fAttrHiddenSize;
0769                out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0770                    << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << rc_offset
0771                    << ", &" << OpName << "_n, " << OpName << "_initial_hidden_state, &" << OpName << "_n, &" << OpName
0772                    << "_alpha, " << OpName << "_cell_gate + offset, &" << OpName << "_n);\n";
0773                if (fAttrInputForget == 0) {
0774                   size_t rf_offset = 2 * fAttrHiddenSize * fAttrHiddenSize;
0775                   out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &"
0776                       << OpName << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + "
0777                       << rf_offset << ", &" << OpName << "_n, " << OpName << "_initial_hidden_state, &" << OpName
0778                       << "_n, &" << OpName << "_alpha, " << OpName << "_forget_gate + offset, &" << OpName << "_n);\n";
0779                }
0780             }
0781          } else { // direction=1
0782             if (fType == "float") {
0783                size_t ri_offset = 4 * fAttrHiddenSize * fAttrHiddenSize;
0784                out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0785                    << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << ri_offset
0786                    << ", &" << OpName << "_n, " << OpName << "_initial_hidden_state, &" << OpName << "_n, &" << OpName
0787                    << "_alpha, " << OpName << "_input_gate + offset, &" << OpName << "_n);\n";
0788                size_t ro_offset = 4 * fAttrHiddenSize * fAttrHiddenSize + 1 * fAttrHiddenSize * fAttrHiddenSize;
0789                out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0790                    << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << ro_offset
0791                    << ", &" << OpName << "_n, " << OpName << "_initial_hidden_state, &" << OpName << "_n, &" << OpName
0792                    << "_alpha, " << OpName << "_output_gate + offset, &" << OpName << "_n);\n";
0793                size_t rc_offset = 4 * fAttrHiddenSize * fAttrHiddenSize + 3 * fAttrHiddenSize * fAttrHiddenSize;
0794                out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0795                    << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << rc_offset
0796                    << ", &" << OpName << "_n, " << OpName << "_initial_hidden_state, &" << OpName << "_n, &" << OpName
0797                    << "_alpha, " << OpName << "_cell_gate + offset, &" << OpName << "_n);\n";
0798                if (fAttrInputForget == 0) {
0799                   size_t rf_offset = 4 * fAttrHiddenSize * fAttrHiddenSize + 2 * fAttrHiddenSize * fAttrHiddenSize;
0800                   out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &"
0801                       << OpName << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + "
0802                       << rf_offset << ", &" << OpName << "_n, " << OpName << "_initial_hidden_state, &" << OpName
0803                       << "_n, &" << OpName << "_alpha, " << OpName << "_forget_gate + offset, &" << OpName << "_n);\n";
0804                }
0805             }
0806          }
0807       }
0808       out << SP << SP << "} else {\n";
0809       // gate = gate + previous_hidden_state * Recurrence^T
0810       if (direction == 0) {
0811          if (fAttrDirection == "backward") {
0812             out << SP << SP << SP << "size_t previous_offset = (index + 1) * "
0813                 << num_directions * batch_size * fAttrHiddenSize << ";\n";
0814          } else {
0815             out << SP << SP << SP << "size_t previous_offset = (seq - 1) * "
0816                 << num_directions * batch_size * fAttrHiddenSize << ";\n";
0817          }
0818          if (fType == "float") {
0819             out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0820                 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << ", &" << OpName << "_n, "
0821                 << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &" << OpName << "_alpha, " << OpName
0822                 << "_input_gate + offset, &" << OpName << "_n);\n";
0823             size_t ro_offset = 1 * fAttrHiddenSize * fAttrHiddenSize;
0824             out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0825                 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << ro_offset
0826                 << ", &" << OpName << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &"
0827                 << OpName << "_alpha, " << OpName << "_output_gate + offset, &" << OpName << "_n);\n";
0828             size_t rc_offset = 3 * fAttrHiddenSize * fAttrHiddenSize;
0829             out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0830                 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << rc_offset
0831                 << ", &" << OpName << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &"
0832                 << OpName << "_alpha, " << OpName << "_cell_gate + offset, &" << OpName << "_n);\n";
0833             if (fAttrInputForget == 0) {
0834                size_t rf_offset = 2 * fAttrHiddenSize * fAttrHiddenSize;
0835                out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0836                    << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << rf_offset
0837                    << ", &" << OpName << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &"
0838                    << OpName << "_alpha, " << OpName << "_forget_gate + offset, &" << OpName << "_n);\n";
0839             }
0840          }
0841       } else {
0842          out << SP << SP << SP << "size_t previous_offset = (index + 1) * "
0843              << num_directions * batch_size * fAttrHiddenSize << " + " << batch_size * fAttrHiddenSize << ";\n";
0844          if (fType == "float") {
0845             size_t ri_offset = 4 * fAttrHiddenSize * fAttrHiddenSize;
0846             out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0847                 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << ri_offset
0848                 << ", &" << OpName << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &"
0849                 << OpName << "_alpha, " << OpName << "_input_gate + offset, &" << OpName << "_n);\n";
0850             size_t ro_offset = 4 * fAttrHiddenSize * fAttrHiddenSize + fAttrHiddenSize * fAttrHiddenSize;
0851             out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0852                 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << ro_offset
0853                 << ", &" << OpName << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &"
0854                 << OpName << "_alpha, " << OpName << "_output_gate + offset, &" << OpName << "_n);\n";
0855             size_t rc_offset = 4 * fAttrHiddenSize * fAttrHiddenSize + 3 * fAttrHiddenSize * fAttrHiddenSize;
0856             out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0857                 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << rc_offset
0858                 << ", &" << OpName << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &"
0859                 << OpName << "_alpha, " << OpName << "_cell_gate + offset, &" << OpName << "_n);\n";
0860             if (fAttrInputForget == 0) {
0861                size_t rf_offset = 4 * fAttrHiddenSize * fAttrHiddenSize + 2 * fAttrHiddenSize * fAttrHiddenSize;
0862                out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0863                    << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + " << rf_offset
0864                    << ", &" << OpName << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &"
0865                    << OpName << "_alpha, " << OpName << "_forget_gate + offset, &" << OpName << "_n);\n";
0866             }
0867          }
0868       }
0869       out << SP << SP << "}\n";
0870 
0871       // Clip the elements of the cell gate into the range [-fAttrClip, fAttrClip]
0872       if (fAttrClip > .0) {
0873          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0874          if (fType == "float") {
0875             out << SP << SP << SP << "float x = (" << OpName << "_cell_gate[i] > " << -fAttrClip << ") ? " << OpName
0876                 << "_cell_gate[i] : " << -fAttrClip << ";\n";
0877          }
0878          out << SP << SP << SP << OpName << "_cell_gate[i] = (x < " << fAttrClip << ") ? x : " << fAttrClip << ";\n";
0879          out << SP << SP << "}\n";
0880       }
0881       // Apply the activation function to the cell gate, cell_gate = g(cell_gate)
0882       if (fAttrActivations[direction * 3 + 1] == "Relu") {
0883          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0884          out << SP << SP << SP << "if (" << OpName << "_cell_gate[i] < 0.)\n";
0885          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = 0.;\n";
0886          out << SP << SP << "}\n";
0887       } else if (fAttrActivations[direction * 3 + 1] == "Tanh") {
0888          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0889          if (fType == "float") {
0890             out << SP << SP << SP << "float ex = exp(-2 * " << OpName << "_cell_gate[i]);\n";
0891          }
0892          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = (1. - ex) / (1. + ex);\n";
0893          out << SP << SP << "}\n";
0894       } else if (fAttrActivations[direction * 3 + 1] == "Sigmoid") {
0895          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0896          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = 1. / (1. + exp(-" << OpName << "_cell_gate[i]));\n";
0897          out << SP << SP << "}\n";
0898       } else if (fAttrActivations[direction * 3 + 1] == "Affine") {
0899          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0900          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = " << fAttrActivationAlpha[direction * 3 + 1] << " * "
0901              << OpName << "_cell_gate[i] + " << fAttrActivationBeta[direction * 3 + 1] << ";\n";
0902          out << SP << SP << "}\n";
0903       } else if (fAttrActivations[direction * 3 + 1] == "ScaledTanh") {
0904          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0905          if (fType == "float") {
0906             out << SP << SP << SP << "float ex = exp(-2 * " << fAttrActivationBeta[direction * 3 + 1] << " * " << OpName
0907                 << "_cell_gate[i]);\n";
0908          }
0909          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = " << fAttrActivationAlpha[direction * 3 + 1]
0910              << " * (1. - ex) / (1. + ex);\n";
0911          out << SP << SP << "}\n";
0912       } else if (fAttrActivations[direction * 3 + 1] == "HardSigmoid") {
0913          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0914          if (fType == "float") {
0915             out << SP << SP << SP << "float a = " << fAttrActivationAlpha[direction * 3 + 1] << " * " << OpName
0916                 << "_cell_gate[i] + " << fAttrActivationBeta[direction * 3 + 1] << ";\n";
0917             out << SP << SP << SP << "float b = (a > 0.) ? a : 0.;\n";
0918          }
0919          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = (b < 1.) ? b : 1.;\n";
0920          out << SP << SP << "}\n";
0921       } else if (fAttrActivations[direction * 3 + 1] == "LeakyRelu") {
0922          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0923          out << SP << SP << SP << "if (" << OpName << "_cell_gate[i] < 0.)\n";
0924          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = " << fAttrActivationAlpha[direction * 3 + 1] << " * "
0925              << OpName << "_cell_gate[i];\n";
0926          out << SP << SP << "}\n";
0927       } else if (fAttrActivations[direction * 3 + 1] == "ThresholdRelu") {
0928          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0929          out << SP << SP << SP << "if (" << OpName << "_cell_gate[i] < " << fAttrActivationAlpha[direction * 3 + 1]
0930              << ")\n";
0931          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = 0.;\n";
0932          out << SP << SP << "}";
0933       } else if (fAttrActivations[direction * 3 + 1] == "Elu") {
0934          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0935          out << SP << SP << SP << "if (" << OpName << "_cell_gate[i] < 0.)\n";
0936          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = " << fAttrActivationAlpha[direction * 3 + 1]
0937              << " * exp(" << OpName << "_cell_gate[i] - 1.);\n";
0938          out << SP << SP << "}\n";
0939       } else if (fAttrActivations[direction * 3 + 1] == "Softsign") {
0940          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0941          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = " << OpName << "_cell_gate[i] / (1. + abs(" << OpName
0942              << "_cell_gate[i]));\n";
0943          out << SP << SP << "}\n";
0944       } else { // fAttrActivations[direction * 3 + 1] = Softplus
0945          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
0946          out << SP << SP << SP << SP << OpName << "_cell_gate[i] = log(1. + exp(" << OpName << "_cell_gate[i]));\n";
0947          out << SP << SP << "}\n";
0948       }
0949 
0950       // Peephole connections for the input gate and the forget gate
0951       if (!fNP.empty()) {
0952          // gate = 1.0 * gate + previous_cell_state * P^T
0953          out << SP << SP << "if (seq == 0) {\n";
0954          if (!fNInitial_c.empty()) {
0955             if (direction == 0) {
0956                out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
0957                out << SP << SP << SP << SP << OpName << "_input_gate[i + offset] += tensor_" << fNP << "[i] * "
0958                    << OpName << "_initial_cell_state[i];\n";
0959                out << SP << SP << SP << "}\n";
0960                if (fAttrInputForget == 0) {
0961                   size_t pf_offset = batch_size * fAttrHiddenSize;
0962                   out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
0963                   out << SP << SP << SP << SP << OpName << "_forget_gate[i + offset] += tensor_" << fNP << "[i + "
0964                       << pf_offset << "] * " << OpName << "_initial_cell_state[i];\n";
0965                   out << SP << SP << SP << "}\n";
0966                }
0967             } else {
0968                size_t pi_offset = 3 * batch_size * fAttrHiddenSize;
0969                size_t initial_c_offset = batch_size * fAttrHiddenSize;
0970                out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
0971                out << SP << SP << SP << SP << OpName << "_input_gate[i + offset] += tensor_" << fNP << "[i + "
0972                    << pi_offset << "] * " << OpName << "_initial_cell_state[i + " << initial_c_offset << "];\n";
0973                out << SP << SP << SP << "}\n";
0974                if (fAttrInputForget == 0) {
0975                   size_t pf_offset = 3 * batch_size * fAttrHiddenSize + batch_size * fAttrHiddenSize;
0976                   out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
0977                   out << SP << SP << SP << SP << OpName << "_forget_gate[i + offset] += tensor_" << fNP << "[i + "
0978                       << pf_offset << "] * " << OpName << "_initial_cell_state[i + " << initial_c_offset << "];\n";
0979                   out << SP << SP << SP << "}\n";
0980                }
0981             }
0982          }
0983          out << SP << SP << "} else {\n";
0984          if (direction == 0) {
0985             if (fAttrDirection == "backward") {
0986                out << SP << SP << SP << "size_t c_offset = (index + 1) * "
0987                    << num_directions * batch_size * fAttrHiddenSize << ";\n";
0988             } else {
0989                out << SP << SP << SP << "size_t c_offset = (seq - 1) * "
0990                    << num_directions * batch_size * fAttrHiddenSize << ";\n";
0991             }
0992             out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
0993             out << SP << SP << SP << SP << OpName << "_input_gate[i + offset] += tensor_" << fNP << "[i] * " << OpName
0994                 << "_cell_state[i + c_offset];\n";
0995             out << SP << SP << SP << "}\n";
0996             if (fAttrInputForget == 0) {
0997                size_t pf_offset = batch_size * fAttrHiddenSize;
0998                out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
0999                out << SP << SP << SP << SP << OpName << "_forget_gate[i + offset] += tensor_" << fNP << "[i + "
1000                    << pf_offset << "] * " << OpName << "_cell_state[i + c_offset];\n";
1001                out << SP << SP << SP << "}\n";
1002             }
1003          } else { // direction=1
1004             size_t pi_offset = 3 * batch_size * fAttrHiddenSize;
1005             out << SP << SP << SP << "size_t c_offset = (index + 1) * " << num_directions * batch_size * fAttrHiddenSize
1006                 << " + " << batch_size * fAttrHiddenSize << ";\n";
1007             out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
1008             out << SP << SP << SP << SP << OpName << "_input_gate[i + offset] += tensor_" << fNP << "[i + " << pi_offset
1009                 << "] * " << OpName << "_cell_state[i + c_offset];\n";
1010             out << SP << SP << SP << "}\n";
1011             if (fAttrInputForget == 0) {
1012                size_t pf_offset = 3 * batch_size * fAttrHiddenSize + batch_size * fAttrHiddenSize;
1013                out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
1014                out << SP << SP << SP << SP << OpName << "_forget_gate[i + offset] += tensor_" << fNP << "[i + "
1015                    << pf_offset << "] * " << OpName << "_cell_state[i + c_offset];\n";
1016                out << SP << SP << SP << "}\n";
1017             }
1018          }
1019          out << SP << SP << "}\n";
1020       }
1021 
1022       // Clip the elements of the input gate into the range [-fAttrClip, fAttrClip]
1023       if (fAttrClip > .0) {
1024          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1025          if (fType == "float") {
1026             out << SP << SP << SP << "float x = (" << OpName << "_input_gate[i] > " << -fAttrClip << ") ? " << OpName
1027                 << "_input_gate[i] : " << -fAttrClip << ";\n";
1028          }
1029          out << SP << SP << SP << OpName << "_input_gate[i] = (x < " << fAttrClip << ") ? x : " << fAttrClip << ";\n";
1030          out << SP << SP << "}\n";
1031       }
1032       // Apply the activation function to the input gate
1033       if (fAttrActivations[direction * 3] == "Relu") {
1034          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1035          out << SP << SP << SP << "if (" << OpName << "_input_gate[i] < 0.)\n";
1036          out << SP << SP << SP << SP << OpName << "_input_gate[i] = 0.;\n";
1037          out << SP << SP << "}\n";
1038       } else if (fAttrActivations[direction * 3] == "Tanh") {
1039          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1040          if (fType == "float") {
1041             out << SP << SP << SP << "float ex = exp(-2 * " << OpName << "_input_gate[i]);\n";
1042          }
1043          out << SP << SP << SP << SP << OpName << "_input_gate[i] = (1. - ex) / (1. + ex);\n";
1044          out << SP << SP << "}\n";
1045       } else if (fAttrActivations[direction * 3] == "Sigmoid") {
1046          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1047          out << SP << SP << SP << SP << OpName << "_input_gate[i] = 1. / (1. + exp(-" << OpName
1048              << "_input_gate[i]));\n";
1049          out << SP << SP << "}\n";
1050       } else if (fAttrActivations[direction * 3] == "Affine") {
1051          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1052          out << SP << SP << SP << SP << OpName << "_input_gate[i] = " << fAttrActivationAlpha[direction * 3] << " * "
1053              << OpName << "_input_gate[i] + " << fAttrActivationBeta[direction * 3] << ";\n";
1054          out << SP << SP << "}\n";
1055       } else if (fAttrActivations[direction * 3] == "ScaledTanh") {
1056          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1057          if (fType == "float") {
1058             out << SP << SP << SP << "float ex = exp(-2 * " << fAttrActivationBeta[direction * 3] << " * " << OpName
1059                 << "_input_gate[i]);\n";
1060          }
1061          out << SP << SP << SP << SP << OpName << "_input_gate[i] = " << fAttrActivationAlpha[direction * 3]
1062              << " * (1. - ex) / (1. + ex);\n";
1063          out << SP << SP << "}\n";
1064       } else if (fAttrActivations[direction * 3] == "HardSigmoid") {
1065          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1066          if (fType == "float") {
1067             out << SP << SP << SP << "float a = " << fAttrActivationAlpha[direction * 3] << " * " << OpName
1068                 << "_input_gate[i] + " << fAttrActivationBeta[direction * 3] << ";\n";
1069             out << SP << SP << SP << "float b = (a > 0.) ? a : 0.;\n";
1070          }
1071          out << SP << SP << SP << SP << OpName << "_input_gate[i] = (b < 1.) ? b : 1.;\n";
1072          out << SP << SP << "}\n";
1073       } else if (fAttrActivations[direction * 3] == "LeakyRelu") {
1074          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1075          out << SP << SP << SP << "if (" << OpName << "_input_gate[i] < 0.)\n";
1076          out << SP << SP << SP << SP << OpName << "_input_gate[i] = " << fAttrActivationAlpha[direction * 3] << " * "
1077              << OpName << "_input_gate[i];\n";
1078          out << SP << SP << "}\n";
1079       } else if (fAttrActivations[direction * 3] == "ThresholdRelu") {
1080          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1081          out << SP << SP << SP << "if (" << OpName << "_input_gate[i] < " << fAttrActivationAlpha[direction * 3]
1082              << ")\n";
1083          out << SP << SP << SP << SP << OpName << "_input_gate[i] = 0.;\n";
1084          out << SP << SP << "}";
1085       } else if (fAttrActivations[direction * 3] == "Elu") {
1086          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1087          out << SP << SP << SP << "if (" << OpName << "_input_gate[i] < 0.)\n";
1088          out << SP << SP << SP << SP << OpName << "_input_gate[i] = " << fAttrActivationAlpha[direction * 3]
1089              << " * exp(" << OpName << "_input_gate[i] - 1.);\n";
1090          out << SP << SP << "}\n";
1091       } else if (fAttrActivations[direction * 3] == "Softsign") {
1092          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1093          out << SP << SP << SP << SP << OpName << "_input_gate[i] = " << OpName << "_input_gate[i] / (1. + abs("
1094              << OpName << "_input_gate[i]));\n";
1095          out << SP << SP << "}\n";
1096       } else { // fAttrActivations[direction * 3] = Softplus
1097          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1098          out << SP << SP << SP << SP << OpName << "_input_gate[i] = log(1. + exp(" << OpName << "_input_gate[i]));\n";
1099          out << SP << SP << "}\n";
1100       }
1101 
1102       if (fAttrInputForget == 0) {
1103          // Clip the elements of the forget gate into the range [-fAttrClip, fAttrClip]
1104          if (fAttrClip > .0) {
1105             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1106             if (fType == "float") {
1107                out << SP << SP << SP << "float x = (" << OpName << "_forget_gate[i] > " << -fAttrClip << ") ? "
1108                    << OpName << "_forget_gate[i] : " << -fAttrClip << ";\n";
1109             }
1110             out << SP << SP << SP << OpName << "_forget_gate[i] = (x < " << fAttrClip << ") ? x : " << fAttrClip
1111                 << ";\n";
1112             out << SP << SP << "}\n";
1113          }
1114          // Apply the activation function to the forget gate, cell_gate = g(cell_gate)
1115          if (fAttrActivations[direction * 3] == "Relu") {
1116             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1117             out << SP << SP << SP << "if (" << OpName << "_forget_gate[i] < 0.)\n";
1118             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = 0.;\n";
1119             out << SP << SP << "}\n";
1120          } else if (fAttrActivations[direction * 3] == "Tanh") {
1121             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1122             if (fType == "float") {
1123                out << SP << SP << SP << "float ex = exp(-2 * " << OpName << "_forget_gate[i]);\n";
1124             }
1125             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = (1. - ex) / (1. + ex);\n";
1126             out << SP << SP << "}\n";
1127          } else if (fAttrActivations[direction * 3] == "Sigmoid") {
1128             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1129             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = 1. / (1. + exp(-" << OpName
1130                 << "_forget_gate[i]));\n";
1131             out << SP << SP << "}\n";
1132          } else if (fAttrActivations[direction * 3] == "Affine") {
1133             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1134             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = " << fAttrActivationAlpha[direction * 3]
1135                 << " * " << OpName << "_forget_gate[i] + " << fAttrActivationBeta[direction * 3] << ";\n";
1136             out << SP << SP << "}\n";
1137          } else if (fAttrActivations[direction * 3] == "ScaledTanh") {
1138             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1139             if (fType == "float") {
1140                out << SP << SP << SP << "float ex = exp(-2 * " << fAttrActivationBeta[direction * 3] << " * " << OpName
1141                    << "_forget_gate[i]);\n";
1142             }
1143             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = " << fAttrActivationAlpha[direction * 3]
1144                 << " * (1. - ex) / (1. + ex);\n";
1145             out << SP << SP << "}\n";
1146          } else if (fAttrActivations[direction * 3] == "HardSigmoid") {
1147             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1148             if (fType == "float") {
1149                out << SP << SP << SP << "float a = " << fAttrActivationAlpha[direction * 3] << " * " << OpName
1150                    << "_forget_gate[i] + " << fAttrActivationBeta[direction * 3] << ";\n";
1151                out << SP << SP << SP << "float b = (a > 0.) ? a : 0.;\n";
1152             }
1153             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = (b < 1.) ? b : 1.;\n";
1154             out << SP << SP << "}\n";
1155          } else if (fAttrActivations[direction * 3] == "LeakyRelu") {
1156             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1157             out << SP << SP << SP << "if (" << OpName << "_forget_gate[i] < 0.)\n";
1158             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = " << fAttrActivationAlpha[direction * 3]
1159                 << " * " << OpName << "_forget_gate[i];\n";
1160             out << SP << SP << "}\n";
1161          } else if (fAttrActivations[direction * 3] == "ThresholdRelu") {
1162             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1163             out << SP << SP << SP << "if (" << OpName << "_forget_gate[i] < " << fAttrActivationAlpha[direction * 3]
1164                 << ")\n";
1165             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = 0.;\n";
1166             out << SP << SP << "}";
1167          } else if (fAttrActivations[direction * 3] == "Elu") {
1168             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1169             out << SP << SP << SP << "if (" << OpName << "_forget_gate[i] < 0.)\n";
1170             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = " << fAttrActivationAlpha[direction * 3]
1171                 << " * exp(" << OpName << "_forget_gate[i] - 1.);\n";
1172             out << SP << SP << "}\n";
1173          } else if (fAttrActivations[direction * 3] == "Softsign") {
1174             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1175             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = " << OpName << "_forget_gate[i] / (1. + abs("
1176                 << OpName << "_forget_gate[i]));\n";
1177             out << SP << SP << "}\n";
1178          } else { // fAttrActivations[direction * 3] = Softplus
1179             out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1180             out << SP << SP << SP << SP << OpName << "_forget_gate[i] = log(1. + exp(" << OpName
1181                 << "_forget_gate[i]));\n";
1182             out << SP << SP << "}\n";
1183          }
1184       }
1185 
1186       // cell_state = input_gate o cell_gate
1187       out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1188       out << SP << SP << SP << OpName << "_cell_state[i] = " << OpName << "_input_gate[i] * " << OpName
1189           << "_cell_gate[i];\n";
1190       out << SP << SP << "}\n";
1191 
1192       if (fAttrInputForget == 0) {
1193          out << SP << SP << "if (seq == 0) {\n";
1194          if (!fNInitial_c.empty()) {
1195             // cell_state += forget_gate o initial_cell_state
1196             out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
1197             out << SP << SP << SP << SP << OpName << "_cell_state[i + offset] += " << OpName
1198                 << "_forget_gate[i + offset] * " << OpName << "_initial_cell_state[i];\n";
1199             out << SP << SP << SP << "}\n";
1200          }
1201          out << SP << SP << "} else {\n";
1202          // cell_state += forget_gate o previous_cell_state
1203          if (direction == 0) {
1204             if (fAttrDirection == "backward") {
1205                out << SP << SP << SP << "size_t previous_offset = (index + 1) * "
1206                    << num_directions * batch_size * fAttrHiddenSize << ";\n";
1207             } else {
1208                out << SP << SP << SP << "size_t previous_offset = (seq - 1) * "
1209                    << num_directions * batch_size * fAttrHiddenSize << ";\n";
1210             }
1211          } else { // direction=1
1212             out << SP << SP << SP << "size_t previous_offset = (index + 1) * "
1213                 << num_directions * batch_size * fAttrHiddenSize << " + " << batch_size * fAttrHiddenSize << ";\n";
1214          }
1215          out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
1216          out << SP << SP << SP << SP << OpName << "_cell_state[i + offset] += " << OpName
1217              << "_forget_gate[i + offset] * " << OpName << "_cell_state[i + previous_offset];\n";
1218          out << SP << SP << SP << "}\n";
1219          out << SP << SP << "}\n";
1220       }
1221 
1222       if (!fNP.empty()) {
1223          // Peephole connection for the output gate
1224          if (direction == 0) {
1225             size_t p_offset = 2 * batch_size * fAttrHiddenSize;
1226             out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
1227             out << SP << SP << SP << SP << OpName << "_output_gate[i + offset] += tensor_" << fNP << "[i + " << p_offset
1228                 << "] * " << OpName << "_cell_state[i + offset];\n";
1229             out << SP << SP << SP << "}\n";
1230          } else { // direction=1
1231             size_t p_offset = 3 * batch_size * fAttrHiddenSize + 2 * batch_size * fAttrHiddenSize;
1232             out << SP << SP << SP << "for (size_t i = 0; i < " << size << "; i++) {\n";
1233             out << SP << SP << SP << SP << OpName << "_output_gate[i + offset] += tensor_" << fNP << "[i + " << p_offset
1234                 << "] * " << OpName << "_cell_state[i + offset];\n";
1235             out << SP << SP << SP << "}\n";
1236          }
1237       }
1238 
1239       // Clip the elements of the output gate into the range [-fAttrClip, fAttrClip]
1240       if (fAttrClip > .0) {
1241          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1242          if (fType == "float") {
1243             out << SP << SP << SP << "float x = (" << OpName << "_output_gate[i] > " << -fAttrClip << ") ? " << OpName
1244                 << "_output_gate[i] : " << -fAttrClip << ";\n";
1245          }
1246          out << SP << SP << SP << OpName << "_output_gate[i] = (x < " << fAttrClip << ") ? x : " << fAttrClip << ";\n";
1247          out << SP << SP << "}\n";
1248       }
1249       // Apply the activation function to the output gate
1250       if (fAttrActivations[direction * 3] == "Relu") {
1251          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1252          out << SP << SP << SP << "if (" << OpName << "_output_gate[i] < 0.)\n";
1253          out << SP << SP << SP << SP << OpName << "_output_gate[i] = 0.;\n";
1254          out << SP << SP << "}\n";
1255       } else if (fAttrActivations[direction * 3] == "Tanh") {
1256          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1257          if (fType == "float") {
1258             out << SP << SP << SP << "float ex = exp(-2 * " << OpName << "_output_gate[i]);\n";
1259          }
1260          out << SP << SP << SP << SP << OpName << "_output_gate[i] = (1. - ex) / (1. + ex);\n";
1261          out << SP << SP << "}\n";
1262       } else if (fAttrActivations[direction * 3] == "Sigmoid") {
1263          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1264          out << SP << SP << SP << SP << OpName << "_output_gate[i] = 1. / (1. + exp(-" << OpName
1265              << "_output_gate[i]));\n";
1266          out << SP << SP << "}\n";
1267       } else if (fAttrActivations[direction * 3] == "Affine") {
1268          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1269          out << SP << SP << SP << SP << OpName << "_output_gate[i] = " << fAttrActivationAlpha[direction * 3] << " * "
1270              << OpName << "_output_gate[i] + " << fAttrActivationBeta[direction * 3] << ";\n";
1271          out << SP << SP << "}\n";
1272       } else if (fAttrActivations[direction * 3] == "ScaledTanh") {
1273          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1274          if (fType == "float") {
1275             out << SP << SP << SP << "float ex = exp(-2 * " << fAttrActivationBeta[direction * 3] << " * " << OpName
1276                 << "_output_gate[i]);\n";
1277          }
1278          out << SP << SP << SP << SP << OpName << "_output_gate[i] = " << fAttrActivationAlpha[direction * 3]
1279              << " * (1. - ex) / (1. + ex);\n";
1280          out << SP << SP << "}\n";
1281       } else if (fAttrActivations[direction * 3] == "HardSigmoid") {
1282          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1283          if (fType == "float") {
1284             out << SP << SP << SP << "float a = " << fAttrActivationAlpha[direction * 3] << " * " << OpName
1285                 << "_output_gate[i] + " << fAttrActivationBeta[direction * 3] << ";\n";
1286             out << SP << SP << SP << "float b = (a > 0.) ? a : 0.;\n";
1287          }
1288          out << SP << SP << SP << SP << OpName << "_output_gate[i] = (b < 1.) ? b : 1.;\n";
1289          out << SP << SP << "}\n";
1290       } else if (fAttrActivations[direction * 3] == "LeakyRelu") {
1291          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1292          out << SP << SP << SP << "if (" << OpName << "_output_gate[i] < 0.)\n";
1293          out << SP << SP << SP << SP << OpName << "_output_gate[i] = " << fAttrActivationAlpha[direction * 3] << " * "
1294              << OpName << "_output_gate[i];\n";
1295          out << SP << SP << "}\n";
1296       } else if (fAttrActivations[direction * 3] == "ThresholdRelu") {
1297          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1298          out << SP << SP << SP << "if (" << OpName << "_output_gate[i] < " << fAttrActivationAlpha[direction * 3]
1299              << ")\n";
1300          out << SP << SP << SP << SP << OpName << "_output_gate[i] = 0.;\n";
1301          out << SP << SP << "}";
1302       } else if (fAttrActivations[direction * 3] == "Elu") {
1303          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1304          out << SP << SP << SP << "if (" << OpName << "_output_gate[i] < 0.)\n";
1305          out << SP << SP << SP << SP << OpName << "_output_gate[i] = " << fAttrActivationAlpha[direction * 3]
1306              << " * exp(" << OpName << "_output_gate[i] - 1.);\n";
1307          out << SP << SP << "}\n";
1308       } else if (fAttrActivations[direction * 3] == "Softsign") {
1309          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1310          out << SP << SP << SP << SP << OpName << "_output_gate[i] = " << OpName << "_output_gate[i] / (1. + abs("
1311              << OpName << "_output_gate[i]));\n";
1312          out << SP << SP << "}\n";
1313       } else { // fAttrActivations[direction * 3] = Softplus
1314          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1315          out << SP << SP << SP << SP << OpName << "_output_gate[i] = log(1. + exp(" << OpName << "_output_gate[i]));\n";
1316          out << SP << SP << "}\n";
1317       }
1318 
1319       // copy cell_state into new_cell_state
1320       out << SP << SP << "std::copy(" << OpName << "_cell_state + offset, " << OpName << "_cell_state + offset + "
1321           << size << ", " << OpName << "_new_cell_state + offset);\n";
1322       // Clip the elements of the new_cell_state into the range [-fAttrClip, fAttrClip]
1323       if (fAttrClip > .0) {
1324          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1325          if (fType == "float") {
1326             out << SP << SP << SP << "float x = (" << OpName << "_new_cell_state[i] > " << -fAttrClip << ") ? "
1327                 << OpName << "_new_cell_state[i] : " << -fAttrClip << ";\n";
1328          }
1329          out << SP << SP << SP << OpName << "_new_cell_state[i] = (x < " << fAttrClip << ") ? x : " << fAttrClip
1330              << ";\n";
1331          out << SP << SP << "}\n";
1332       }
1333       // Apply the activation function to the new cell state
1334       if (fAttrActivations[direction * 3 + 2] == "Relu") {
1335          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1336          out << SP << SP << SP << "if (" << OpName << "_new_cell_state[i] < 0.)\n";
1337          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = 0.;\n";
1338          out << SP << SP << "}\n";
1339       } else if (fAttrActivations[direction * 3 + 2] == "Tanh") {
1340          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1341          if (fType == "float") {
1342             out << SP << SP << SP << "float ex = exp(-2 * " << OpName << "_new_cell_state[i]);\n";
1343          }
1344          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = (1. - ex) / (1. + ex);\n";
1345          out << SP << SP << "}\n";
1346       } else if (fAttrActivations[direction * 3 + 2] == "Sigmoid") {
1347          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1348          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = 1. / (1. + exp(-" << OpName
1349              << "_new_cell_state[i]));\n";
1350          out << SP << SP << "}\n";
1351       } else if (fAttrActivations[direction * 3 + 2] == "Affine") {
1352          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1353          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = " << fAttrActivationAlpha[direction * 3 + 2]
1354              << " * " << OpName << "_new_cell_state[i] + " << fAttrActivationBeta[direction * 3 + 2] << ";\n";
1355          out << SP << SP << "}\n";
1356       } else if (fAttrActivations[direction * 3 + 2] == "ScaledTanh") {
1357          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1358          if (fType == "float") {
1359             out << SP << SP << SP << "float ex = exp(-2 * " << fAttrActivationBeta[direction * 3 + 2] << " * " << OpName
1360                 << "_new_cell_state[i]);\n";
1361          }
1362          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = " << fAttrActivationAlpha[direction * 3 + 2]
1363              << " * (1. - ex) / (1. + ex);\n";
1364          out << SP << SP << "}\n";
1365       } else if (fAttrActivations[direction * 3 + 2] == "HardSigmoid") {
1366          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1367          if (fType == "float") {
1368             out << SP << SP << SP << "float a = " << fAttrActivationAlpha[direction * 3 + 2] << " * " << OpName
1369                 << "_new_cell_state[i] + " << fAttrActivationBeta[direction * 3 + 2] << ";\n";
1370             out << SP << SP << SP << "float b = (a > 0.) ? a : 0.;\n";
1371          }
1372          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = (b < 1.) ? b : 1.;\n";
1373          out << SP << SP << "}\n";
1374       } else if (fAttrActivations[direction * 3 + 2] == "LeakyRelu") {
1375          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1376          out << SP << SP << SP << "if (" << OpName << "_new_cell_state[i] < 0.)\n";
1377          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = " << fAttrActivationAlpha[direction * 3 + 2]
1378              << " * " << OpName << "_new_cell_state[i];\n";
1379          out << SP << SP << "}\n";
1380       } else if (fAttrActivations[direction * 3 + 2] == "ThresholdRelu") {
1381          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1382          out << SP << SP << SP << "if (" << OpName << "_new_cell_state[i] < " << fAttrActivationAlpha[direction * 3 + 2]
1383              << ")\n";
1384          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = 0.;\n";
1385          out << SP << SP << "}";
1386       } else if (fAttrActivations[direction * 3 + 2] == "Elu") {
1387          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1388          out << SP << SP << SP << "if (" << OpName << "_new_cell_state[i] < 0.)\n";
1389          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = " << fAttrActivationAlpha[direction * 3 + 2]
1390              << " * exp(" << OpName << "_new_cell_state[i] - 1.);\n";
1391          out << SP << SP << "}\n";
1392       } else if (fAttrActivations[direction * 3 + 2] == "Softsign") {
1393          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1394          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = " << OpName << "_new_cell_state[i] / (1. + abs("
1395              << OpName << "_new_cell_state[i]));\n";
1396          out << SP << SP << "}\n";
1397       } else { // fAttrActivations[direction * 3 + 2] = Softplus
1398          out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1399          out << SP << SP << SP << SP << OpName << "_new_cell_state[i] = log(1. + exp(" << OpName
1400              << "_new_cell_state[i]));\n";
1401          out << SP << SP << "}\n";
1402       }
1403 
1404       // hidden_state = output_gate o new_cell_state
1405       out << SP << SP << "for (size_t i = offset; i < offset + " << size << "; i++) {\n";
1406       out << SP << SP << SP << OpName << "_hidden_state[i] = " << OpName << "_output_gate[i] * " << OpName
1407           << "_new_cell_state[i];\n";
1408       out << SP << SP << "}\n";
1409       out << SP << "}\n";
1410    }
1411 
1412    // Padding the hidden state for LSTM with different sequence lengths
1413    if (!fNSequence_lens.empty()) {
1414       out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
1415       out << SP << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1416       out << SP << SP << SP << "if (seq >= tensor_" << fNSequence_lens << "[batch]) {\n";
1417       for (size_t direction = 0; direction < num_directions; direction++) {
1418          out << SP << SP << SP << SP << SP << "for (size_t h = 0; h < " << fAttrHiddenSize << "; h++) {\n";
1419          out << SP << SP << SP << SP << SP << SP << "size_t idx = seq * "
1420              << num_directions * batch_size * fAttrHiddenSize + direction * batch_size * fAttrHiddenSize
1421              << " + batch * " << fAttrHiddenSize << " + h;\n";
1422          out << SP << SP << SP << SP << SP << SP << OpName << "_cell_state[idx] = 0.;\n";
1423          out << SP << SP << SP << SP << SP << SP << OpName << "_hidden_state[idx] = 0.;\n";
1424          out << SP << SP << SP << SP << SP << "}\n";
1425       }
1426       out << SP << SP << SP << "}\n";
1427       out << SP << SP << "}\n";
1428       out << SP << "}\n";
1429    }
1430 
1431    // Copy the hidden state into y and y_h and copy cell_state into y_c
1432    if (fAttrLayout == 0) {
1433       if (!fNY_h.empty()) {
1434          // Copy hidden_state into Y_h
1435          if (fNSequence_lens.empty()) {
1436             size_t y_h_size = batch_size * fAttrHiddenSize;
1437             if (fAttrDirection == "backward") {
1438                out << SP << "std::copy(" << OpName << "_hidden_state, " << OpName << "_hidden_state + " << y_h_size
1439                    << ", tensor_" << fNY_h << ");\n";
1440             } else {
1441                size_t offset = (seq_length - 1) * num_directions * batch_size * fAttrHiddenSize;
1442                out << SP << "std::copy(" << OpName << "_hidden_state + " << offset << ", " << OpName
1443                    << "_hidden_state + " << offset << " + " << y_h_size << ", tensor_" << fNY_h << ");\n";
1444             }
1445             if (num_directions == 2) {
1446                out << SP << "std::copy(" << OpName << "_hidden_state + " << y_h_size << ", " << OpName
1447                    << "_hidden_state + " << 2 * y_h_size << ", tensor_" << fNY_h << " + " << y_h_size << ");\n";
1448             }
1449          } else { // LSTM with different sequence lengths
1450             if (fAttrDirection == "backward") {
1451                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1452                out << SP << SP << "size_t offset = batch * " << fAttrHiddenSize << ";\n";
1453                out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
1454                    << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + offset);\n";
1455                out << SP << "}\n";
1456             } else {
1457                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1458                out << SP << SP << "size_t seq = " << "tensor_" << fNSequence_lens << "[batch] - 1;\n";
1459                out << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize
1460                    << " + batch * " << fAttrHiddenSize << ";\n";
1461                out << SP << SP << "size_t y_h_offset = batch * " << fAttrHiddenSize << ";\n";
1462                out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
1463                    << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + y_h_offset);\n";
1464                out << SP << "}\n";
1465             }
1466             if (num_directions == 2) {
1467                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1468                out << SP << SP << "size_t offset = " << batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize
1469                    << ";\n";
1470                out << SP << SP << "size_t y_h_offset = " << batch_size * fAttrHiddenSize << " + batch * "
1471                    << fAttrHiddenSize << ";\n";
1472                out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
1473                    << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + y_h_offset);\n";
1474                out << SP << "}\n";
1475             }
1476          }
1477       }
1478       if (!fNY_c.empty()) {
1479          // Copy cell_state into Y_c
1480          if (fNSequence_lens.empty()) {
1481             size_t y_h_size = batch_size * fAttrHiddenSize;
1482             if (fAttrDirection == "backward") {
1483                out << SP << "std::copy(" << OpName << "_cell_state, " << OpName << "_hidden_state + " << y_h_size
1484                    << ", tensor_" << fNY_c << ");\n";
1485             } else {
1486                size_t offset = (seq_length - 1) * num_directions * batch_size * fAttrHiddenSize;
1487                out << SP << "std::copy(" << OpName << "_cell_state + " << offset << ", " << OpName << "_cell_state + "
1488                    << offset << " + " << y_h_size << ", tensor_" << fNY_c << ");\n";
1489             }
1490             if (num_directions == 2) {
1491                out << SP << "std::copy(" << OpName << "_cell_state + " << y_h_size << ", " << OpName << "_cell_state + "
1492                    << 2 * y_h_size << ", tensor_" << fNY_c << " + " << y_h_size << ");\n";
1493             }
1494          } else { // LSTM with different sequence lengths
1495             if (fAttrDirection == "backward") {
1496                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1497                out << SP << SP << "size_t offset = batch * " << fAttrHiddenSize << ";\n";
1498                out << SP << SP << "std::copy(" << OpName << "_cell_state + offset, " << OpName
1499                    << "_cell_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_c << " + offset);\n";
1500                out << SP << "}\n";
1501             } else {
1502                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1503                out << SP << SP << "size_t seq = " << "tensor_" << fNSequence_lens << "[batch] - 1;\n";
1504                out << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize
1505                    << " + batch * " << fAttrHiddenSize << ";\n";
1506                out << SP << SP << "size_t y_h_offset = batch * " << fAttrHiddenSize << ";\n";
1507                out << SP << SP << "std::copy(" << OpName << "_cell_state + offset, " << OpName
1508                    << "_cell_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_c << " + y_h_offset);\n";
1509                out << SP << "}\n";
1510             }
1511             if (num_directions == 2) {
1512                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1513                out << SP << SP << "size_t offset = " << batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize
1514                    << ";\n";
1515                out << SP << SP << "size_t y_h_offset = " << batch_size * fAttrHiddenSize << " + batch * "
1516                    << fAttrHiddenSize << ";\n";
1517                out << SP << SP << "std::copy(" << OpName << "_cell_state + offset, " << OpName
1518                    << "_cell_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_c << " + y_h_offset);\n";
1519                out << SP << "}\n";
1520             }
1521          }
1522       }
1523    } else { // fAttrLayout=1
1524       if (!fNY.empty()) {
1525          // Copy hidden_state into Y
1526          for (size_t direction = 0; direction < num_directions; direction++) {
1527             out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
1528             out << SP << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1529             out << SP << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize << " + "
1530                 << direction * batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize << ";\n";
1531             out << SP << SP << SP << "size_t y_offset = batch * " << seq_length * num_directions * fAttrHiddenSize
1532                 << " + seq * " << num_directions * fAttrHiddenSize << " + " << direction * fAttrHiddenSize << ";\n";
1533             out << SP << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
1534                 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY << " + y_offset);\n";
1535             out << SP << SP << "}\n";
1536             out << SP << "}\n";
1537          }
1538       }
1539       if (!fNY_h.empty()) {
1540          // Copy the hidden_state into Y_h
1541          if (fAttrDirection == "backward") {
1542             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1543             out << SP << SP << "size_t offset = batch * " << fAttrHiddenSize << ";\n";
1544             out << SP << SP << "size_t y_h_offset = batch * " << num_directions * fAttrHiddenSize << ";\n";
1545             out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
1546                 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + y_h_offset);\n";
1547             out << SP << "}\n";
1548          } else {
1549             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1550             if (fNSequence_lens.empty()) {
1551                out << SP << SP << "size_t seq = " << seq_length - 1 << ";\n";
1552             } else {
1553                out << SP << SP << "size_t seq = " << "tensor_" << fNSequence_lens << "[batch] - 1;\n";
1554             }
1555             out << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize
1556                 << " + batch * " << fAttrHiddenSize << ";\n";
1557             out << SP << SP << "size_t y_h_offset = batch * " << num_directions * fAttrHiddenSize << ";\n";
1558             out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
1559                 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + y_h_offset);\n";
1560             out << SP << "}\n";
1561          }
1562          if (num_directions == 2) {
1563             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1564             out << SP << SP << "size_t offset = " << batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize
1565                 << ";\n";
1566             out << SP << SP << "size_t y_h_offset = batch * " << num_directions * fAttrHiddenSize << " + "
1567                 << fAttrHiddenSize << ";\n";
1568             out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
1569                 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + y_h_offset);\n";
1570             out << SP << "}\n";
1571          }
1572       }
1573 
1574       if (!fNY_c.empty()) {
1575          // copy the cell_state into Y_c
1576          if (fAttrDirection == "backward") {
1577             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1578             out << SP << SP << "size_t offset = batch * " << fAttrHiddenSize << ";\n";
1579             out << SP << SP << "size_t y_h_offset = batch * " << num_directions * fAttrHiddenSize << ";\n";
1580             out << SP << SP << "std::copy(" << OpName << "_cell_state + offset, " << OpName << "_cell_state + offset + "
1581                 << fAttrHiddenSize << ", tensor_" << fNY_c << " + y_h_offset);\n";
1582             out << SP << "}\n";
1583          } else {
1584             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1585             if (fNSequence_lens.empty()) {
1586                out << SP << SP << "size_t seq = " << seq_length - 1 << ";\n";
1587             } else {
1588                out << SP << SP << "size_t seq = " << "tensor_" << fNSequence_lens << "[batch] - 1;\n";
1589             }
1590             out << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize
1591                 << " + batch * " << fAttrHiddenSize << ";\n";
1592             out << SP << SP << "size_t y_h_offset = batch * " << num_directions * fAttrHiddenSize << ";\n";
1593             out << SP << SP << "std::copy(" << OpName << "_cell_state + offset, " << OpName << "_cell_state + offset + "
1594                 << fAttrHiddenSize << ", tensor_" << fNY_c << " + y_h_offset);\n";
1595             out << SP << "}\n";
1596          }
1597          if (num_directions == 2) {
1598             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
1599             out << SP << SP << "size_t offset = " << batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize
1600                 << ";\n";
1601             out << SP << SP << "size_t y_h_offset = batch * " << num_directions * fAttrHiddenSize << " + "
1602                 << fAttrHiddenSize << ";\n";
1603             out << SP << SP << "std::copy(" << OpName << "_cell_state + offset, " << OpName << "_cell_state + offset + "
1604                 << fAttrHiddenSize << ", tensor_" << fNY_c << " + y_h_offset);\n";
1605             out << SP << "}\n";
1606          }
1607       }
1608    }
1609 
1610    return out.str();
1611 }
1612 
1613 } // namespace TMVA::Experimental::SOFIE
1614 
1615 #endif