Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-14 09:29:39

0001 #ifndef TMVA_SOFIE_ROPERATOR_RNN
0002 #define TMVA_SOFIE_ROPERATOR_RNN
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 <vector>
0011 
0012 namespace TMVA::Experimental::SOFIE {
0013 
0014 /*! \brief Recurrent Neural Network operator
0015  *
0016  * Inference code generation for one-layer vanilla RNN. Supports forward, reverse and bidirectional RNNs.
0017  * See the <a href="https://github.com/onnx/onnx/blob/master/docs/Operators.md#RNN">ONNX documentation</a>
0018  * for details about the supported RNN architectures.
0019  */
0020 template <typename T> class ROperator_RNN final : public ROperator {
0021  private:
0022    std::vector<float> fAttrActivationAlpha;   ///< Scaling values used by some activation functions
0023    std::vector<float> fAttrActivationBeta;    ///< Scaling values used by some activation functions
0024    std::vector<std::string> fAttrActivations; ///< Activation functions
0025    float fAttrClip;                           ///< Clip threshold
0026    std::string fAttrDirection;                ///< Direction of processing
0027    size_t fAttrHiddenSize;                    ///< Number of the hidden layers
0028    size_t fAttrLayout;                        ///< Data layout
0029 
0030    std::string fNX;                           ///< Name of the input
0031    std::string fNW;                           ///< Name of the weights
0032    std::string fNR;                           ///< Name of the recurrence
0033    std::string fNB;                           ///< Name of the bias
0034    std::string fNSequence_lens;               ///< Name of the length of the sequences
0035    std::string fNInitial_h;                   ///< Name of the initial value of the hidden states
0036    std::string fNY;                           ///< Name of the output
0037    std::string fNY_h;                         ///< Name of the last sequence of the output
0038 
0039    std::vector<size_t> fShapeX;               ///< Shape of the input
0040    std::vector<size_t> fShapeW;               ///< Shape of the weights
0041    std::vector<size_t> fShapeR;               ///< Shape of the recurrence
0042    std::vector<size_t> fShapeB;               ///< Shape of the bias
0043    std::vector<size_t> fShapeSequence_lens;   ///< Shape of the length of the sequences
0044    std::vector<size_t> fShapeInitial_h;       ///< Shape of the initial value of the hidden states
0045    std::vector<size_t> fShapeY;               ///< Shape of the output
0046    std::vector<size_t> fShapeY_h;             ///< Shape of the last sequence of the output
0047 
0048    std::string fType; ///< Type of the tensors
0049 
0050  public:
0051    /*! Default constructor of ROperator_RNN */
0052    ROperator_RNN() {}
0053 
0054    /*! \brief Constructor of ROperator_RNN from the attributes
0055     *
0056     * \param activation_alpha scaling values used by some activation functions
0057     * \param activation_beta scaling values used by some activation functions
0058     * \param activations activation functions
0059     * \param clip clip threshold
0060     * \param direction direction of processing of the sequneces
0061     * \param hidden_size number of hidden layers
0062     * \param layout data layout
0063     * \param nameX name of the input tensor
0064     * \param nameW name of the weight tensor
0065     * \param nameR name of the recurrence tensor
0066     * \param nameB name of the bias tensor
0067     * \param nameSequence_lens name of the length of the sequences
0068     * \param nameInitial_h name of the initial value of the hidden states
0069     * \param nameY name of the output
0070     * \param nameY_h name of the last sequence of the output
0071     */
0072    ROperator_RNN(std::vector<float> activation_alpha,
0073                  std::vector<float> activation_beta,
0074                  std::vector<std::string> activations, float clip,
0075                  std::string direction, size_t hidden_size, size_t layout,
0076                  std::string nameX, std::string nameW, std::string nameR,
0077                  std::string nameB, std::string nameSequence_lens,
0078                  std::string nameInitial_h, std::string nameY,
0079                  std::string nameY_h)
0080        : fAttrActivationAlpha(activation_alpha),
0081          fAttrActivationBeta(activation_beta), fAttrActivations(activations),
0082          fAttrClip(clip), fAttrDirection(direction),
0083          fAttrHiddenSize(hidden_size), fAttrLayout(layout),
0084          fNX(UTILITY::Clean_name(nameX)), fNW(UTILITY::Clean_name(nameW)),
0085          fNR(UTILITY::Clean_name(nameR)), fNB(UTILITY::Clean_name(nameB)),
0086          fNSequence_lens(UTILITY::Clean_name(nameSequence_lens)),
0087          fNInitial_h(UTILITY::Clean_name(nameInitial_h)),
0088          fNY(UTILITY::Clean_name(nameY)), fNY_h(UTILITY::Clean_name(nameY_h)) {
0089       if (std::is_same<T, float>::value) {
0090          fType = "float";
0091       } else {
0092          throw std::runtime_error(
0093              "TMVA SOFIE Encountered unsupported type parsing a RNN operator");
0094       }
0095 
0096       fInputTensorNames = { fNX, fNW, fNR };
0097       if(!fNB.empty()){
0098          fInputTensorNames.emplace_back(fNB);
0099       }
0100       if(!fNSequence_lens.empty()){
0101          fInputTensorNames.emplace_back(fNSequence_lens);
0102       }
0103       if(!fNInitial_h.empty()){
0104          fInputTensorNames.emplace_back(fNInitial_h);
0105       }
0106 
0107       fOutputTensorNames = { };
0108       if(!fNY.empty()){
0109          fOutputTensorNames.emplace_back(fNY);
0110       }
0111       if(!fNY_h.empty()){
0112          fOutputTensorNames.emplace_back(fNY_h);
0113       }
0114    }
0115 
0116    /*! \brief Infers the type of the output tensors
0117     *
0118     * \param input type of the input tensors
0119     */
0120    std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override;
0121 
0122    /*! \brief Infers the shape of the output tensors
0123     *
0124     * \param input shape of the input tensors
0125     */
0126    std::vector<std::vector<size_t>>
0127    ShapeInference(std::vector<std::vector<size_t>> input) override;
0128 
0129    /*! \brief Initialize the model
0130     *
0131     * \param model Model
0132     */
0133    void Initialize(RModel &) override;
0134 
0135    /*! \brief Generates the inference code
0136     *
0137     * \param OpName name of the operator
0138     */
0139    std::string Generate(std::string OpName) override;
0140 
0141    // generate code for Session data members (e.g. internal vectors)
0142    std::string GenerateSessionMembersCode(std::string opName) override;
0143 
0144    /*! \brief Returns the blas routines needed to compile the generated code
0145     */
0146    std::vector<std::string> GetBlasRoutines() override { return { std::string("Gemm"), std::string("Axpy") }; }
0147 };
0148 
0149 template <typename T>
0150 auto ROperator_RNN<T>::TypeInference(std::vector<ETensorType> input) -> std::vector<ETensorType>
0151 {
0152    ETensorType out = input[0];
0153    return {out, out};
0154 }
0155 
0156 template <typename T>
0157 auto ROperator_RNN<T>::ShapeInference(std::vector<std::vector<size_t>> input) -> std::vector<std::vector<size_t>>
0158 {
0159    size_t num_directions = input[1][0];
0160    size_t hidden_size = input[1][1];
0161    if (fAttrLayout == 0) {
0162       size_t seq_length = input[0][0];
0163       size_t batch_size = input[0][1];
0164       std::vector<std::vector<size_t>> ret(
0165          {{seq_length, num_directions, batch_size, hidden_size}, {num_directions, batch_size, hidden_size}});
0166       return ret;
0167    } else {
0168       size_t batch_size = input[0][0];
0169       size_t seq_length = input[0][1];
0170       std::vector<std::vector<size_t>> ret(
0171          {{batch_size, seq_length, num_directions, hidden_size}, {batch_size, num_directions, hidden_size}});
0172       return ret;
0173    }
0174 }
0175 
0176 template <typename T>
0177 auto ROperator_RNN<T>::Initialize(RModel &model) -> void
0178 {
0179    fUseSession = model.UseSession();
0180    // Check the input and output tensors
0181    if (!model.CheckIfTensorAlreadyExist(fNX)) {
0182       throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNX + "  is not found in model.");
0183    }
0184    fShapeX = model.GetTensorShape(fNX);
0185    if (fShapeX.size() != 3) {
0186       throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNX + " is not of 3 dimensions.");
0187    }
0188    if (!model.CheckIfTensorAlreadyExist(fNW)) {
0189       throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNW + "  is not found in model.");
0190    }
0191    fShapeW = model.GetTensorShape(fNW);
0192    if (fShapeW.size() != 3) {
0193       throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNW + " is not of 3 dimensions.");
0194    }
0195    if (!model.CheckIfTensorAlreadyExist(fNR)) {
0196       throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNR + "  is not found in model.");
0197    }
0198    fShapeR = model.GetTensorShape(fNR);
0199    if (fShapeR.size() != 3) {
0200       throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNR + " is not of 3 dimensions.");
0201    }
0202    if (!fNB.empty()) {
0203       if (!model.CheckIfTensorAlreadyExist(fNB)) {
0204          throw std::runtime_error("TMVA SOFIE RNN op input tensor " + fNB + " is not  found in model.");
0205       }
0206       fShapeB = model.GetTensorShape(fNB);
0207       if (fShapeB.size() != 2 && fShapeB.size() != 4) {
0208          throw std::runtime_error("TMVA SOFIE RNN op input tensor " + fNB + " is not of 2 or 4 dimensions.");
0209       }
0210       if (fShapeB.size() == 2) {
0211          // Broadcasting the bias
0212          auto original_data = model.GetInitializedTensorData(fNB);
0213          size_t num_directions = fShapeW[0];
0214          size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
0215          size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
0216          if (fType == "float") {
0217             float *original_bias = static_cast<float *>(original_data.get());
0218             float *new_bias = new float[num_directions * seq_length * batch_size * fAttrHiddenSize];
0219             std::vector<float> sum(fAttrHiddenSize);
0220             for (size_t direction = 0; direction < num_directions; direction++) {
0221                for (size_t h = 0; h < fAttrHiddenSize; h++) {
0222                   sum[h] = original_bias[direction * 2 * fAttrHiddenSize + h] +
0223                            original_bias[(2 * direction + 1) * fAttrHiddenSize + h];
0224                }
0225                for (size_t seq = 0; seq < seq_length; seq++) {
0226                   for (size_t batch = 0; batch < batch_size; batch++) {
0227                      size_t bias_offset = direction * seq_length * batch_size * fAttrHiddenSize +
0228                                           seq * batch_size * fAttrHiddenSize + batch * fAttrHiddenSize;
0229                      std::copy(sum.begin(), sum.end(), new_bias + bias_offset);
0230                   }
0231                }
0232             }
0233             std::vector<size_t> new_bias_shape = {num_directions, seq_length, batch_size, fAttrHiddenSize};
0234             std::shared_ptr<void> new_bias_ptr(new_bias, std::default_delete<float[]>());
0235             model.UpdateInitializedTensor(fNB, model.GetTensorType(fNB), new_bias_shape, new_bias_ptr);
0236             fShapeB = model.GetTensorShape(fNB);
0237          }
0238       }
0239    }
0240    if (!fNSequence_lens.empty()) {
0241       if (!model.CheckIfTensorAlreadyExist(fNSequence_lens)) {
0242          throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNSequence_lens + "is not found in model.");
0243       }
0244       fShapeSequence_lens = model.GetTensorShape(fNSequence_lens);
0245       if (fShapeSequence_lens.size() != 1) {
0246          throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNSequence_lens + " is not of 1 dimension.");
0247       }
0248    }
0249    if (!fNInitial_h.empty()) {
0250       if (!model.CheckIfTensorAlreadyExist(fNInitial_h)) {
0251          throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNInitial_h + " is not found in model.");
0252       }
0253       fShapeInitial_h = model.GetTensorShape(fNInitial_h);
0254       if (fShapeInitial_h.size() != 3) {
0255          throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNInitial_h + " is not of 3 dimensions.");
0256       }
0257    }
0258    if (!fNY.empty()) {
0259       fShapeY = ShapeInference({fShapeX, fShapeW})[0];
0260       if (!model.CheckIfTensorAlreadyExist(fNY)) {
0261          model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShapeY);
0262       }
0263    }
0264    if (!fNY_h.empty()) {
0265       fShapeY_h = ShapeInference({fShapeX, fShapeW})[1];
0266       if (!model.CheckIfTensorAlreadyExist(fNY_h)) {
0267          model.AddIntermediateTensor(fNY_h, model.GetTensorType(fNX), fShapeY_h);
0268       }
0269    }
0270    // Check the attributes
0271    for (auto &activation : fAttrActivations) {
0272       if (activation != "Relu" && activation != "Tanh" && activation != "Sigmoid" && activation != "Affine" &&
0273           activation != "LeakyRelu" && activation != "ThresholdRelu" && activation != "ScaledTanh" &&
0274           activation != "HardSigmoid" && activation != "Elu" && activation != "Softsign" && activation != "Softplus") {
0275          throw std::runtime_error("TMVA SOFIE - Activation function " + activation + " not implemented");
0276       }
0277    }
0278    if (fAttrDirection != "forward" && fAttrDirection != "backward" && fAttrDirection != "bidirectional") {
0279       throw std::runtime_error("TMVA SOFIE - Invalid RNN direction fAttrDirection = " + fAttrDirection);
0280    }
0281    if (fAttrHiddenSize != fShapeW[1]) {
0282       throw std::runtime_error("TMVA SOFIE - fAttrHiddenSize must be equal to " + std::to_string(fShapeW[1]));
0283    }
0284    if (fAttrLayout > 1) {
0285       throw std::runtime_error("TMVA SOFIE - Layout fAttrLayout = " + std::to_string(fAttrLayout) +
0286                                " must be 0 (timewise) or 1 (batchwise)");
0287    }
0288    if (fAttrActivations.empty()) {
0289       if (fAttrDirection == "bidirectional") {
0290          fAttrActivations = {"Tanh", "Tanh"};
0291       } else {
0292          fAttrActivations = {"Tanh"};
0293       }
0294    }
0295    // Add needed standard library headers
0296    model.AddNeededStdLib("cmath");
0297 }
0298 
0299 // generate code for Session data members (e.g. internal vectors)
0300 template <typename T>
0301 std::string ROperator_RNN<T>::GenerateSessionMembersCode(std::string opName)
0302 {
0303    opName = "op_" + opName;
0304    std::stringstream out;
0305 
0306    size_t num_directions = fShapeW[0];
0307    size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
0308    size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
0309    size_t input_size = fShapeX[2];
0310 
0311    struct Block {
0312       std::string name;
0313       size_t size;
0314    };
0315 
0316    std::vector<Block> blocks;
0317 
0318    if (fAttrLayout != 0) {
0319       blocks.push_back({"input", seq_length * batch_size * input_size});
0320       blocks.push_back({"initial_hidden_state", num_directions * batch_size * fAttrHiddenSize});
0321    }
0322    blocks.push_back({"feedforward", seq_length * batch_size * fAttrHiddenSize});
0323    if (fAttrLayout != 0 || fNY.empty()) {
0324       blocks.push_back({"hidden_state", seq_length * num_directions * batch_size * fAttrHiddenSize});
0325    }
0326 
0327    // Compute total size
0328    size_t total_size = 0;
0329    for (const auto &b : blocks) {
0330       total_size += b.size;
0331    }
0332 
0333    // Emit backing storage
0334    out << "std::vector<" << fType << "> fVec_" << opName << "_buffer = std::vector<" << fType << ">(" << total_size
0335        << ");\n";
0336 
0337    // Emit pointers
0338    std::size_t offset = 0;
0339    for (const auto &b : blocks) {
0340       out << fType << "* fVec_" << opName << "_" << b.name << " = fVec_" << opName << "_buffer.data() + " << offset
0341           << ";\n";
0342       offset += b.size;
0343    }
0344 
0345    out << "\n";
0346 
0347    return out.str();
0348 }
0349 
0350 //////////////////////////////////////////////////////////////////////////////////////////////////
0351 template <typename T>
0352 auto ROperator_RNN<T>::Generate(std::string OpName) -> std::string
0353 {
0354    OpName = "op_" + OpName;
0355    std::stringstream out;
0356 
0357    size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
0358    size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
0359    size_t input_size = fShapeX[2];
0360    size_t num_directions = fShapeW[0];
0361 
0362    // set the input
0363    if (fAttrLayout == 0) {
0364       if (fType == "float") {
0365          out << SP << "float const*" << OpName << "_input = tensor_" << fNX << ";\n";
0366       }
0367    } else {
0368       if (fUseSession)
0369          out << SP << fType << " * " << OpName << "_input = this->fVec_" << OpName << "_input;\n";
0370       else
0371          out << SP << fType << " " << OpName << "_input[" << seq_length * batch_size * input_size << "];\n";
0372       out << SP << "for(size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
0373       out << SP << SP << "for(size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0374       out << SP << SP << SP << "for(size_t i = 0; i < " << input_size << "; i++) {\n";
0375       out << SP << SP << SP << SP << OpName << "_input[seq * " << batch_size * input_size << " + batch * " << input_size
0376           << " + i] = " << "tensor_" << fNX << "[batch * " << seq_length * input_size << " + seq * " << input_size
0377           << " + i];\n";
0378       out << SP << SP << SP << "}\n";
0379       out << SP << SP << "}\n";
0380       out << SP << "}\n";
0381    }
0382 
0383    // Set the initial hidden state
0384    if (!fNInitial_h.empty()) {
0385       if (fAttrLayout == 0) {
0386          out << SP << fType << " *" << OpName << "_initial_hidden_state = " << " tensor_" << fNInitial_h << ";\n";
0387       } else {
0388          if (fUseSession)
0389             out << SP << fType << " * " << OpName << "_initial_hidden_state = this->fVec_" << OpName
0390                 << "_initial_hidden_state;\n";
0391          else
0392             out << fType << " " << OpName << "_initial_hidden_state[" << num_directions * batch_size * fAttrHiddenSize
0393                 << "] = {0};\n";
0394 
0395          for (size_t direction = 0; direction < num_directions; direction++) {
0396             out << SP << "for(size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0397             out << SP << SP << "for(size_t h = 0; h < " << fAttrHiddenSize << "; h++) {\n";
0398             out << SP << SP << SP << OpName << "_initial_hidden_state[" << direction * batch_size * fAttrHiddenSize
0399                 << " + batch * " << fAttrHiddenSize << " + h] = tensor_" << fNInitial_h << "[batch * "
0400                 << num_directions * fAttrHiddenSize << " + " << direction * fAttrHiddenSize << " + h];\n";
0401             out << SP << SP << "}\n";
0402             out << SP << "}\n";
0403          }
0404       }
0405    }
0406 
0407    if (fUseSession)
0408       out << SP << fType << " * " << OpName << "_feedforward = this->fVec_" << OpName << "_feedforward;\n";
0409    else
0410       out << SP << fType << " " << OpName << "_feedforward[" << seq_length * batch_size * fAttrHiddenSize
0411           << "] = {0};\n";
0412 
0413    // Set the hidden state
0414    if (fAttrLayout == 0 && !fNY.empty()) {
0415       out << SP << fType << " *" << OpName << "_hidden_state = tensor_" << fNY << ";\n";
0416    } else {
0417       if (fUseSession)
0418          out << SP << fType << " * " << OpName << "_hidden_state = this->fVec_" << OpName << "_hidden_state;\n";
0419       else
0420          out << SP << fType << " " << OpName << "_hidden_state["
0421              << seq_length * num_directions * batch_size * fAttrHiddenSize << "] = {0};\n";
0422    }
0423 
0424    out << SP << "char " << OpName << "_transA = 'N';\n";
0425    out << SP << "char " << OpName << "_transB = 'T';\n";
0426    out << SP << "int " << OpName << "_m = " << seq_length * batch_size << ";\n";
0427    out << SP << "int " << OpName << "_n = " << fAttrHiddenSize << ";\n";
0428    out << SP << "int " << OpName << "_k = " << input_size << ";\n";
0429    if (fType == "float") {
0430       out << SP << "float " << OpName << "_alpha = 1.;\n";
0431       out << SP << "float " << OpName << "_beta = .0;\n";
0432    }
0433    if (!fNB.empty()) {
0434       out << SP << "int " << OpName << "_bias_size = " << seq_length * batch_size * fAttrHiddenSize << ";\n";
0435       out << SP << "int " << OpName << "_incx = 1;\n";
0436       out << SP << "int " << OpName << "_incy = 1;\n";
0437    }
0438 
0439    for (size_t direction = 0; direction < num_directions; direction++) {
0440       // feedforward = input * W^T + bias
0441       if (fType == "float") {
0442          if (direction == 0) {
0443             out << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName << "_n, &"
0444                 << OpName << "_m, &" << OpName << "_k, &" << OpName << "_alpha, tensor_" << fNW << ", &" << OpName
0445                 << "_k, " << OpName << "_input, &" << OpName << "_k, &" << OpName << "_beta, " << OpName
0446                 << "_feedforward, &" << OpName << "_n);\n";
0447          } else {
0448             out << SP << "size_t " << OpName << "_w_offset = " << fAttrHiddenSize * input_size << ";\n";
0449             out << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName << "_n, &"
0450                 << OpName << "_m, &" << OpName << "_k, &" << OpName << "_alpha, tensor_" << fNW << " + " << OpName
0451                 << "_w_offset, &" << OpName << "_k, " << OpName << "_input, &" << OpName << "_k, &" << OpName
0452                 << "_beta, " << OpName << "_feedforward, &" << OpName << "_n);\n";
0453          }
0454       }
0455       // Add the bias
0456       if (!fNB.empty()) {
0457          if (fType == "float") {
0458             if (direction == 0) {
0459                out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << ", &"
0460                    << OpName << "_incx, " << OpName << "_feedforward, &" << OpName << "_incy);\n";
0461             } else {
0462                out << SP << "size_t " << OpName << "_bias_offset = " << seq_length * batch_size * fAttrHiddenSize
0463                    << ";\n";
0464                out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << " + "
0465                    << OpName << "_bias_offset, &" << OpName << "_incx, " << OpName << "_feedforward, &" << OpName
0466                    << "_incy);\n";
0467             }
0468          }
0469       }
0470 
0471       // Copy feedforward into hidden state
0472       out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
0473       out << SP << SP << "size_t offset = seq * " << batch_size * fAttrHiddenSize << ";\n";
0474       out << SP << SP << "size_t size = " << batch_size * fAttrHiddenSize << ";\n";
0475       out << SP << SP << "size_t h_offset = seq * " << num_directions * batch_size * fAttrHiddenSize << " + "
0476           << direction * batch_size * fAttrHiddenSize << ";\n";
0477       out << SP << SP << "std::copy(" << OpName << "_feedforward + offset, " << OpName
0478           << "_feedforward + offset + size, " << OpName << "_hidden_state + h_offset);\n";
0479       out << SP << "}\n";
0480 
0481       out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
0482       if (fAttrDirection == "backward" || direction == 1) {
0483          out << SP << SP << "size_t index = " << seq_length - 1 << " - seq;\n";
0484       } else {
0485          out << SP << SP << "size_t index = seq;\n";
0486       }
0487 
0488       out << SP << SP << "int m2 = " << batch_size << ";\n";
0489       out << SP << SP << "size_t offset = index * " << num_directions * batch_size * fAttrHiddenSize << " + "
0490           << direction * batch_size * fAttrHiddenSize << ";\n";
0491       out << SP << SP << "size_t size = " << batch_size * fAttrHiddenSize << ";\n";
0492       out << SP << SP << "if (seq == 0) {\n";
0493       if (!fNInitial_h.empty()) {
0494          // hidden_state = hidden_state + initial_hidden_state * R^T
0495          out << SP << SP << SP << "size_t r_offset = " << direction * fAttrHiddenSize * fAttrHiddenSize << ";\n";
0496          out << SP << SP << SP << "size_t initial_hidden_state_offset = " << direction * batch_size * fAttrHiddenSize
0497              << ";\n";
0498          if (fType == "float") {
0499             out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0500                 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + r_offset, &" << OpName
0501                 << "_n, " << OpName << "_initial_hidden_state + initial_hidden_state_offset, &" << OpName << "_n, &"
0502                 << OpName << "_alpha, " << OpName << "_hidden_state + offset, &" << OpName << "_n);\n";
0503          }
0504       }
0505       out << SP << SP << "} else {\n";
0506       // hidden_state = hidden_state + previous_hidden_state * R^T
0507       out << SP << SP << SP << "size_t r_offset = " << direction * fAttrHiddenSize * fAttrHiddenSize << ";\n";
0508       if (fAttrDirection == "backward" || direction == 1) {
0509          out << SP << SP << SP << "size_t previous_offset = (index + 1) * "
0510              << num_directions * batch_size * fAttrHiddenSize << " + " << direction * batch_size * fAttrHiddenSize
0511              << ";\n";
0512       } else {
0513          out << SP << SP << SP << "size_t previous_offset = (seq - 1) * "
0514              << num_directions * batch_size * fAttrHiddenSize << " + " << direction * batch_size * fAttrHiddenSize
0515              << ";\n";
0516       }
0517       if (fType == "float") {
0518          out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
0519              << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + r_offset, &" << OpName
0520              << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &" << OpName << "_alpha, "
0521              << OpName << "_hidden_state + offset, &" << OpName << "_n);\n";
0522       }
0523       out << SP << SP << "}\n";
0524 
0525       // Clip the elements of the hidden state into the range [-fAttrClip, fAttrClip]
0526       if (fAttrClip > .0) {
0527          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0528          if (fType == "float") {
0529             out << SP << SP << SP << "float x = (" << OpName << "_hidden_state[i] > " << -fAttrClip << ") ? " << OpName
0530                 << "_hidden_state[i] : " << -fAttrClip << ";\n";
0531          }
0532          out << SP << SP << SP << OpName << "_hidden_state[i] = (x < " << fAttrClip << ") ? x : " << fAttrClip << ";\n";
0533          out << SP << SP << "}\n";
0534       }
0535 
0536       // Apply the activation function to the hidden state
0537       if (fAttrActivations[direction] == "Relu") {
0538          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0539          out << SP << SP << SP << "if (" << OpName << "_hidden_state[i] < 0.)\n";
0540          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = 0.;\n";
0541          out << SP << SP << "}\n";
0542       } else if (fAttrActivations[direction] == "Tanh") {
0543          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0544          if (fType == "float") {
0545             out << SP << SP << SP << "float ex = std::exp(-2 * " << OpName << "_hidden_state[i]);\n";
0546          }
0547          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = (1. - ex) / (1. + ex);\n";
0548          out << SP << SP << "}\n";
0549       } else if (fAttrActivations[direction] == "Sigmoid") {
0550          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0551          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = 1. / (1. + std::exp(-" << OpName
0552              << "_hidden_state[i]));\n";
0553          out << SP << SP << "}\n";
0554       } else if (fAttrActivations[direction] == "Affine") {
0555          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0556          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << fAttrActivationAlpha[direction] << " * "
0557              << OpName << "_hidden_state[i] + " << fAttrActivationBeta[direction] << ";\n";
0558          out << SP << SP << "}\n";
0559       } else if (fAttrActivations[direction] == "ScaledTanh") {
0560          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0561          if (fType == "float") {
0562             out << SP << SP << SP << "float ex = std::exp(-2 * " << fAttrActivationBeta[direction] << " * " << OpName
0563                 << "_hidden_state[i]);\n";
0564          }
0565          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << fAttrActivationAlpha[direction]
0566              << " * (1. - ex) / (1. + ex);\n";
0567          out << SP << SP << "}\n";
0568       } else if (fAttrActivations[direction] == "HardSigmoid") {
0569          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0570          if (fType == "float") {
0571             out << SP << SP << SP << "float a = " << fAttrActivationAlpha[direction] << " * " << OpName
0572                 << "_hidden_state[i] + " << fAttrActivationBeta[direction] << ";\n";
0573             out << SP << SP << SP << "float b = (a > 0.) ? a : 0.;\n";
0574          }
0575          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = (b < 1.) ? b : 1.;\n";
0576          out << SP << SP << "}\n";
0577       } else if (fAttrActivations[direction] == "LeakyRelu") {
0578          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0579          out << SP << SP << SP << "if (" << OpName << "_hidden_state[i] < 0.)\n";
0580          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << fAttrActivationAlpha[direction] << " * "
0581              << OpName << "_hidden_state[i];\n";
0582          out << SP << SP << "}\n";
0583       } else if (fAttrActivations[direction] == "ThresholdRelu") {
0584          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0585          out << SP << SP << SP << "if (" << OpName << "_hidden_state[i] < " << fAttrActivationAlpha[direction] << ")\n";
0586          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = 0.;\n";
0587          out << SP << SP << "}";
0588       } else if (fAttrActivations[direction] == "Elu") {
0589          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0590          out << SP << SP << SP << "if (" << OpName << "_hidden_state[i] < 0.)\n";
0591          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << fAttrActivationAlpha[direction]
0592              << " * std::exp(" << OpName << "_hidden_state[i] - 1.);\n";
0593          out << SP << SP << "}\n";
0594       } else if (fAttrActivations[direction] == "Softsign") {
0595          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0596          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << OpName << "_hidden_state[i] / (1. + abs("
0597              << OpName << "_hidden_state[i]));\n";
0598          out << SP << SP << "}\n";
0599       } else { // fAttrActivations[direction] = Softplus
0600          out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
0601          out << SP << SP << SP << SP << OpName << "_hidden_state[i] = log(1. + std::exp(" << OpName
0602              << "_hidden_state[i]));\n";
0603          out << SP << SP << "}\n";
0604          out << SP << "}\n";
0605       }
0606       out << SP << "}\n";
0607    }
0608 
0609    // Padding the hidden state for RNN with different sequence lengths
0610    if (!fNSequence_lens.empty()) {
0611       out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
0612       out << SP << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0613       out << SP << SP << SP << "if (seq >= tensor_" << fNSequence_lens << "[batch]) {\n";
0614       out << SP << SP << SP << SP << "for (size_t h = 0; h < " << fAttrHiddenSize << "; h++) {\n";
0615       if (num_directions == 1) {
0616          out << SP << SP << SP << SP << SP << OpName << "_hidden_state[seq * "
0617              << num_directions * batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize << " + h] = 0.;\n";
0618       } else {
0619          out << SP << SP << SP << SP << SP << OpName << "_hidden_state[seq * "
0620              << num_directions * batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize << " + h] = 0.;\n";
0621          out << SP << SP << SP << SP << SP << OpName << "_hidden_state[seq * "
0622              << num_directions * batch_size * fAttrHiddenSize << " + " << batch_size * fAttrHiddenSize << " + batch * "
0623              << fAttrHiddenSize << " + h] = 0.;\n";
0624       }
0625       out << SP << SP << SP << SP << "}\n";
0626       out << SP << SP << SP << "}\n";
0627       out << SP << SP << "}\n";
0628       out << SP << "}\n";
0629    }
0630 
0631    // Copy the hidden state into y and y_h
0632    if (fAttrLayout == 0) {
0633       if (!fNY_h.empty()) {
0634          if (fNSequence_lens.empty()) {
0635             size_t yh_size = batch_size * fAttrHiddenSize;
0636             if (fAttrDirection == "backward") {
0637                out << SP << "std::copy(" << OpName << "_hidden_state, " << OpName << "_hidden_state + " << yh_size
0638                    << ", tensor_" << fNY_h << ");\n";
0639             } else {
0640                size_t offset = (seq_length - 1) * num_directions * batch_size * fAttrHiddenSize;
0641                out << SP << "std::copy(" << OpName << "_hidden_state + " << offset << ", " << OpName
0642                    << "_hidden_state + " << offset << " + " << yh_size << ", tensor_" << fNY_h << ");\n";
0643             }
0644             if (num_directions == 2) {
0645                out << SP << "std::copy(" << OpName << "_hidden_state + " << yh_size << ", " << OpName
0646                    << "_hidden_state + " << 2 * yh_size << ", tensor_" << fNY_h << " + " << yh_size << ");\n";
0647             }
0648          } else { // RNN with different sequence lengths
0649             if (fAttrDirection == "backward") {
0650                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0651                out << SP << SP << "size_t offset = batch * " << fAttrHiddenSize << ";\n";
0652                out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
0653                    << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + offset);\n";
0654                out << SP << "}\n";
0655             } else {
0656                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0657                out << SP << SP << "size_t seq = " << "tensor_" << fNSequence_lens << "[batch] - 1;\n";
0658                out << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize
0659                    << " + batch * " << fAttrHiddenSize << ";\n";
0660                out << SP << SP << "size_t yh_offset = batch * " << fAttrHiddenSize << ";\n";
0661                out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
0662                    << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
0663                out << SP << "}\n";
0664             }
0665             if (num_directions == 2) {
0666                out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0667                out << SP << SP << "size_t offset = " << batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize
0668                    << ";\n";
0669                out << SP << SP << "size_t yh_offset = " << batch_size * fAttrHiddenSize << " + batch * "
0670                    << fAttrHiddenSize << ";\n";
0671                out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
0672                    << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
0673                out << SP << "}\n";
0674             }
0675          }
0676       }
0677    } else { // fAttrLayout=1
0678       if (!fNY.empty()) {
0679          for (size_t direction = 0; direction < num_directions; direction++) {
0680             out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
0681             out << SP << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0682             out << SP << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize << " + "
0683                 << direction * batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize << ";\n";
0684             out << SP << SP << SP << "size_t y_offset = batch * " << seq_length * num_directions * fAttrHiddenSize
0685                 << " + seq * " << num_directions * fAttrHiddenSize << " + " << direction * fAttrHiddenSize << ";\n";
0686             out << SP << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
0687                 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY << " + y_offset);\n";
0688             out << SP << SP << "}\n";
0689             out << SP << "}\n";
0690          }
0691       }
0692       if (!fNY_h.empty()) {
0693          if (fAttrDirection == "backward") {
0694             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0695             out << SP << SP << "size_t offset = batch * " << fAttrHiddenSize << ";\n";
0696             out << SP << SP << "size_t yh_offset = batch * " << num_directions * fAttrHiddenSize << ";\n";
0697             out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
0698                 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
0699             out << SP << "}\n";
0700          } else {
0701             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0702             if (fNSequence_lens.empty()) {
0703                out << SP << SP << "size_t seq = " << seq_length - 1 << ";\n";
0704             } else {
0705                out << SP << SP << "size_t seq = " << "tensor_" << fNSequence_lens << "[batch] - 1;\n";
0706             }
0707             out << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize
0708                 << " + batch * " << fAttrHiddenSize << ";\n";
0709             out << SP << SP << "size_t yh_offset = batch * " << num_directions * fAttrHiddenSize << ";\n";
0710             out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
0711                 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
0712             out << SP << "}\n";
0713          }
0714          if (num_directions == 2) {
0715             out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
0716             out << SP << SP << "size_t offset = " << batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize
0717                 << ";\n";
0718             out << SP << SP << "size_t yh_offset = batch * " << num_directions * fAttrHiddenSize << " + "
0719                 << fAttrHiddenSize << ";\n";
0720             out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
0721                 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
0722             out << SP << "}\n";
0723          }
0724       }
0725    }
0726 
0727    return out.str();
0728 }
0729 
0730 } // namespace TMVA::Experimental::SOFIE
0731 
0732 #endif