Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/root/TMVA/ROperator_ConvTranspose.hxx was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 #ifndef TMVA_SOFIE_ROPERATOR_CONVTRANSPOSE_HXX
0002 #define TMVA_SOFIE_ROPERATOR_CONVTRANSPOSE_HXX
0003 
0004 #include <TMVA/SOFIE_common.hxx>
0005 #include <TMVA/ROperator.hxx>
0006 #include <TMVA/RModel.hxx>
0007 
0008 #include <memory>
0009 #include <sstream>
0010 #include <algorithm>
0011 #include <stdexcept>
0012 #include <vector>
0013 #include <cassert>
0014 
0015 namespace TMVA::Experimental::SOFIE {
0016 
0017 /*! \brief Transposed Convolution operator
0018  *
0019  * Inference code generation for a transposed convolution layer.
0020  * See the <a href="https://github.com/onnx/onnx/blob/main/docs/Operators.md#convtranspose">ONNX documentation</a> for
0021  * details about the transposed conv layer.
0022  */
0023 template <typename T>
0024 class ROperator_ConvTranspose final : public ROperator {
0025 private:
0026    std::string fAttrAutopad;
0027    std::vector<size_t> fAttrDilations;
0028    size_t fAttrGroup;
0029    std::vector<size_t> fAttrKernelShape;
0030    std::vector<size_t> fAttrOutputPadding;
0031    std::vector<size_t> fAttrOutputShape;
0032    std::vector<size_t> fAttrPads;
0033    std::vector<size_t> fAttrStrides;
0034 
0035    std::string fNX;
0036    std::string fNW;
0037    std::string fNB;
0038    std::string fNBroadcastedB;
0039    std::string fNY;
0040 
0041    std::string fConvK;
0042    std::string fImcol;
0043 
0044    std::vector<size_t> fShapeX;
0045    std::vector<size_t> fShapeW;
0046    std::vector<size_t> fShapeB;
0047    std::vector<size_t> fShapeY;
0048 
0049    std::string fType;
0050 
0051    size_t fDim; // dimension of the convolution
0052 
0053 public:
0054    /*! Default constructor of ROperator_ConvTranspose */
0055    ROperator_ConvTranspose() {}
0056 
0057    /*! \brief Constructor of ROperator_ConvTranspose from the attributes
0058     *
0059     * \param autopad padding
0060     * \param dilations dilations of the kernel
0061     * \param group number of groups
0062     * \param kernelShape shape of the kernel
0063     * \param outputPadding padding of the output
0064     * \param outputShape shape of the output
0065     * \param pads padding of the input
0066     * \param strides strides
0067     * \param nameX name of the input
0068     * \param nameW name of the weight
0069     * \param nameB name of the bias
0070     * \param nameY name of the output
0071     */
0072    ROperator_ConvTranspose(std::string autopad, std::vector<size_t> dilations, size_t group,
0073                            std::vector<size_t> kernelShape, std::vector<size_t> outputPadding,
0074                            std::vector<size_t> outputShape, std::vector<size_t> pads, std::vector<size_t> strides,
0075                            std::string nameX, std::string nameW, std::string nameB, std::string nameY)
0076       : fAttrAutopad(autopad), fAttrDilations(dilations), fAttrGroup(group), fAttrKernelShape(kernelShape),
0077         fAttrOutputPadding(outputPadding), fAttrOutputShape(outputShape), fAttrPads(pads), fAttrStrides(strides),
0078         fNX(UTILITY::Clean_name(nameX)), fNW(UTILITY::Clean_name(nameW)), fNB(UTILITY::Clean_name(nameB)),
0079         fNY(UTILITY::Clean_name(nameY))
0080    {
0081       fInputTensorNames = { fNX, fNW };
0082       fOutputTensorNames = { fNY };
0083       if (!fNB.empty()) {
0084          fInputTensorNames.emplace_back(fNB);
0085       }
0086 
0087       if (std::is_same<T, float>::value) {
0088          fType = "float";
0089       } else {
0090          throw std::runtime_error("TMVA SOFIE Encountered unsupported type parsing a Conv operator");
0091       }
0092    }
0093 
0094    /*! \brief Infers the type of the output tensor
0095     * \param input type of the input tensors
0096     */
0097    std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override
0098    {
0099       ETensorType out = input[0];
0100       return {out};
0101    }
0102 
0103    /*! \brief Infers the shape of the input tensors
0104     * \param input shape of the input tensors
0105     */
0106    std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> /*input*/) override;
0107 
0108    /*! \brief Initialize the model
0109     * \param model Model
0110     */
0111    void Initialize(RModel &) override;
0112 
0113    /*! \brief Generate code for initializing the op
0114     */
0115    std::string GenerateInitCode() override;
0116 
0117    /*! \brief Generate the inference code
0118     * \param opName name of the operator
0119     */
0120    std::string Generate(std::string opName) override;
0121 
0122    /*! \brief Returns the blas routines needed to compile the generated code
0123     */
0124    std::vector<std::string> GetBlasRoutines() override { return { std::string("Gemm"), std::string("Axpy") }; }
0125 };
0126 
0127 template <typename T>
0128 auto ROperator_ConvTranspose<T>::ShapeInference(std::vector<std::vector<size_t>> input)
0129    -> std::vector<std::vector<size_t>>
0130 {
0131    const std::vector<size_t> &inputShape = input[0];
0132    const std::vector<size_t> &weightShape = input[1];
0133    size_t size = inputShape.size();
0134    // Dimension of the conv transpose op
0135    fDim = size - 2;
0136    // Number of groups
0137    if (fAttrGroup == 0)
0138       fAttrGroup = 1;
0139    if (fAttrStrides.empty()) {
0140       fAttrStrides = std::vector<size_t>(fDim, 1);
0141    }
0142    if (fAttrDilations.empty()) {
0143       fAttrDilations = std::vector<size_t>(fDim, 1);
0144    }
0145    // The shape of the kernel is kw for 1d image, kh x Kw for 2d images and kd x kh x kw for a 3d image
0146    if (fAttrKernelShape.empty()) {
0147       fAttrKernelShape.resize(fDim);
0148       for (size_t i = 0; i < fDim; i++)
0149          fAttrKernelShape[i] = fShapeW[i + 2] + (fAttrDilations[i] - 1) * (fShapeW[i + 2] - 1);
0150    }
0151    if (fAttrOutputPadding.empty())
0152       fAttrOutputPadding = std::vector<size_t>(fDim, 0);
0153 
0154    // The Shape of the output is batch_size x out_channel x out_w for a 1d image,
0155    // batch_size x out_channel x out_h x out_w for a 2d image and
0156    // batch_size x out_channel x out_d x out_h x out_w for a 3d image
0157    // where out_channel = weight_shape[1] * group
0158    std::vector<size_t> outShape(size);
0159    outShape[0] = inputShape[0];
0160    outShape[1] = weightShape[1] * fAttrGroup;
0161 
0162    // Generate the padding
0163    if (fAttrPads.empty()) {
0164       fAttrPads = std::vector<size_t>(2 * fDim, 0);
0165       if (fAttrOutputShape.size() == fDim) {
0166          // LM: to be checked...
0167          //  for time being not support
0168          throw std::runtime_error("ConvTranspose with output_shape explicitly set not yet supported.");
0169          /*
0170          std::vector<size_t> totalPadding(fDim, 1);
0171          for (size_t i = 0; i < fDim; i++) {
0172             size_t j = i + 2;
0173             totalPadding[i] =
0174                fAttrStrides[i] * (fAttrOutputShape[i] - 1) + fAttrOutputPadding[i] + fAttrKernelShape[i] - fShapeX[j];
0175          }
0176 
0177          for (size_t i = 0; i < fDim; i++) {
0178             size_t end_i = i + fDim;
0179             if (fAttrAutopad == "SAME_UPPER") {
0180                fAttrPads[i] = totalPadding[i] / 2;
0181                fAttrPads[end_i] = totalPadding[i] - fAttrPads[i];
0182             } else {
0183                fAttrPads[end_i] = totalPadding[i] / 2;
0184                fAttrPads[i] = totalPadding[i] - fAttrPads[end_i];
0185             }
0186          }
0187          */
0188       }
0189       if (fAttrAutopad != "NOTSET") {
0190          throw std::runtime_error("ConvTranspose with padding SAME_UPPER or SMAE_LOWER not supported");
0191       }
0192    }
0193    if (fAttrOutputShape.empty()) {
0194       fAttrOutputShape.resize(fDim);
0195       for (size_t i = 0; i < fDim; i++) {
0196          size_t j = i + 2;
0197          fAttrOutputShape[i] = fAttrStrides[i] * (inputShape[j] - 1) + fAttrKernelShape[i] + fAttrOutputPadding[i] -
0198                                fAttrPads[i] - fAttrPads[fDim + i];
0199       }
0200    } else {
0201       // The shape of the output is explicitly set
0202       // TODO Generate the padding from the output shape and the input shape
0203       throw std::runtime_error("ConvTranspose with output_shape explicitly set not yet supported.");
0204    }
0205 
0206    for (size_t i = 0; i < fDim; i++)
0207       outShape[i + 2] = fAttrOutputShape[i];
0208    std::vector<std::vector<size_t>> ret({outShape});
0209    return ret;
0210 }
0211 
0212 template <typename T>
0213 void ROperator_ConvTranspose<T>::Initialize(RModel &model)
0214 {
0215 
0216    fUseSession = model.UseSession();
0217    if (!model.CheckIfTensorAlreadyExist(fNX)) {
0218       throw std::runtime_error("TMVA SOFIE Conv Transpose op Input Tensor " + fNX + " is not found in model");
0219    }
0220    fShapeX = model.GetTensorShape(fNX);
0221    if (fShapeX.size() < 3 || fShapeX.size() > 5) {
0222       std::cout << fNX << " : " << ConvertShapeToString(fShapeX) << std::endl;
0223       throw std::runtime_error("TMVA SOFIE Conv Transpose Op input data tensor" + fNX +
0224                                " is not of 3,4 or 5 dimensions");
0225    }
0226    fDim = fShapeX.size() - 2;
0227    if (!model.CheckIfTensorAlreadyExist(fNW)) {
0228       throw std::runtime_error("TMVA SOFIE Conv op Input weight Tensor " + fNW + " is not found in model");
0229    }
0230    fShapeW = model.GetTensorShape(fNW);
0231    if (fShapeW.size() < 3 || fShapeW.size() > 5) {
0232       std::cout << fNW << " : " << ConvertShapeToString(fShapeW) << std::endl;
0233       throw std::runtime_error("TMVA SOFIE Conv Transpose Op input weight tensor" + fNW +
0234                                " is not of 3,4 or 5 dimensions");
0235    }
0236    fShapeY = ShapeInference({fShapeX, fShapeW})[0];
0237 
0238    model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShapeY);
0239    if (fNB != "") {
0240       if (!model.CheckIfTensorAlreadyExist(fNB)) {
0241          throw std::runtime_error("TMVA SOFIE ConvTrans op Input Tensor " + fNB + " is not found in model");
0242       }
0243       fShapeB = model.GetTensorShape(fNB);
0244       if (fShapeB.size() < 1)
0245          throw std::runtime_error("TMVA SOFIE ConvTrans op: Bias Tensor has empty shape");
0246 
0247       size_t bsize = ConvertShapeToLength(fShapeB);
0248       size_t ysize = ConvertShapeToLength(fShapeY);
0249       // broadcasting is needed if first stride of B is not same of Y
0250       bool broadcast_needed = (bsize != ysize);
0251       // Broadcast the bias B
0252       if (broadcast_needed) {
0253          // we assume bias tensor size is equal to number of filters that is the second dimension in
0254          // the output tensor
0255          if (bsize != fShapeY[1])
0256             throw std::runtime_error("TMVA SOFIE ConvTrans op: Bias Tensor has wrong shape: " +
0257                                      ConvertShapeToString(fShapeB));
0258 
0259          auto original_data = model.GetInitializedTensorData(fNB);
0260 
0261          if (fType != "float")
0262             throw std::runtime_error(
0263                "TMVA SOFIE ConvTrans op: Broadcasting for non-float type tensors is not supported");
0264          // here the acual broadcasting
0265          if (!fUseSession) {
0266             // Broadcast B from M to N x M x Od x Oh x Ow
0267             std::shared_ptr<void> new_data_ptr(
0268                UTILITY::BroadcastConvBias<float>(static_cast<float *>(original_data.get()), bsize, fShapeY),
0269                std::default_delete<float[]>());
0270 
0271             model.UpdateInitializedTensor(fNB, model.GetTensorType(fNB), fShapeY, new_data_ptr);
0272             fShapeB = model.GetTensorShape(fNB);
0273             fNBroadcastedB = fNB; // use same name
0274          } else {
0275             // In case of session add broadcasting code in Session constructor and in GenerateInitCode
0276             // we need to add a new intermediate tensor for broadcasted bias tensor
0277             fNBroadcastedB = "Broadcasted" + fNB;
0278             model.AddIntermediateTensor(fNBroadcastedB, model.GetTensorType(fNB), fShapeY);
0279          }
0280       } else {
0281          // bias tensor is already correct shape, no need to broadcast
0282          if (fShapeY != fShapeB)
0283             throw std::runtime_error("TMVA SOFIE ConvTrans op: Broadcasting is not needed but bias has wrong shape" +
0284                                      ConvertShapeToString(fShapeB));
0285          fNBroadcastedB = fNB;
0286       }
0287    }
0288 
0289    size_t kernelSize = 1;
0290    size_t inputSize = 1;
0291    for (size_t i = 0; i < fDim; i++) {
0292       inputSize *= fShapeX[2 + i];
0293       kernelSize *= fAttrKernelShape[i];
0294    }
0295 
0296    std::vector<size_t> shape1 = {fShapeW[0], fShapeW[1], kernelSize};
0297    std::vector<size_t> shape2 = {fShapeW[1], kernelSize, inputSize};
0298    model.AddIntermediateTensor(fNX + "_f", ConvertStringToType(fType), shape1);
0299    model.AddIntermediateTensor(fNX + "_xcol", ConvertStringToType(fType), shape2);
0300    fConvK = fNX + "_f";
0301    fImcol = fNX + "_xcol";
0302    fOutputTensorNames.emplace_back(fConvK);
0303    fOutputTensorNames.emplace_back(fImcol);
0304 }
0305 
0306 template <typename T>
0307 std::string ROperator_ConvTranspose<T>::GenerateInitCode()
0308 {
0309    std::stringstream out;
0310    // generate initialization code for broadcasting of bias tensor
0311    size_t bsize = ConvertShapeToLength(fShapeB);
0312    size_t ysize = ConvertShapeToLength(fShapeY);
0313    if (bsize != ysize && !fNBroadcastedB.empty()) {
0314       // include a separate scope to avoid defining unique operator temp variables
0315       out << SP << "{\n";
0316       out << SP << SP << "float * data = TMVA::Experimental::SOFIE::UTILITY::BroadcastConvBias<float>(tensor_" << fNB
0317           << ", " << bsize << ", " << ConvertShapeToString(fShapeY) << ");\n";
0318       out << SP << SP << "std::copy(data, data + " << ConvertShapeToLength(fShapeY) << ", tensor_" << fNBroadcastedB
0319           << ");\n";
0320       out << SP << SP << "delete[] data;\n";
0321       out << SP << "}\n";
0322    }
0323    return out.str();
0324 }
0325 
0326 template <typename T>
0327 std::string ROperator_ConvTranspose<T>::Generate(std::string OpName)
0328 {
0329    OpName = "op_" + OpName;
0330 
0331    if (fShapeX.empty() || fShapeW.empty() || (fNB != "" && fShapeB.empty()) || fShapeY.empty()) {
0332       throw std::runtime_error("TMVA SOFIE Conv Op called to Generate without being initialized first");
0333    }
0334 
0335    std::stringstream out;
0336 
0337    size_t bsize = fShapeX[0];
0338    size_t kDepth = (fDim > 2) ? fShapeW[2] : 1;     // kernel depth
0339    size_t kHeight = (fDim > 1) ? fShapeW[fDim] : 1; // kernel height
0340    size_t kWidth = fShapeW[fDim + 1];               // kernel width
0341 
0342    size_t iDepth = (fDim > 2) ? fShapeX[2] : 1;     // input depth
0343    size_t iHeight = (fDim > 1) ? fShapeX[fDim] : 1; // input height
0344    size_t iWidth = fShapeX[fDim + 1];               // input width
0345 
0346    size_t oDepth = (fDim > 2) ? fShapeY[2] : 1;     // output depth
0347    size_t oHeight = (fDim > 1) ? fShapeY[fDim] : 1; // ouput height
0348    size_t oWidth = fShapeY[fDim + 1];               // output width
0349 
0350    out << "\n//----  operator ConvTranspose " << OpName << "\n";
0351 
0352    // create first matrix with convolution kernels
0353    if (!fUseSession) {
0354       size_t kernelSize = fAttrKernelShape[0];
0355       if (fDim > 1)
0356          kernelSize *= fAttrKernelShape[1];
0357       out << SP << fType << " tensor_" << fNX << "_f[" << fShapeW[0] * fShapeW[1] * kernelSize << "] = {0};\n";
0358    }
0359 
0360    // vectorize the (dilated)convolution kernels into a matrix
0361    // The shape of the kernel is W for 1d image, H x W for 2d image and D x H x W
0362    // for 3d image
0363    size_t id = (fDim > 2) ? fDim - 3 : 2;
0364    size_t ih = (fDim > 1) ? fDim - 2 : 1;
0365    size_t iw = fDim - 1;
0366    size_t wstrideDil = fAttrDilations[iw];
0367    size_t hstride = kWidth;
0368    size_t hstrideDil = fAttrKernelShape[iw];
0369    if (fDim > 1)
0370       hstrideDil *= fAttrDilations[ih];
0371    // stride dilated in the height
0372    size_t dstride = kHeight * kWidth;
0373    size_t dstrideDil = fAttrKernelShape[iw];
0374    if (fDim > 1)
0375       dstrideDil *= fAttrKernelShape[ih];
0376    if (fDim > 2)
0377       dstrideDil *= fAttrDilations[id];
0378    size_t icstride = kHeight * kWidth * kDepth;
0379    size_t icstrideDil = 1;
0380    for (size_t i = 0; i < fDim; i++)
0381       icstrideDil *= fAttrKernelShape[i];
0382    size_t ocstride = fShapeW[1] * icstride;
0383    size_t ocstrideDil = fShapeW[1] * icstrideDil;
0384 
0385    // The shape of f is [M/group, kHeight x kWidth]
0386    out << SP << "for (std::size_t ic = 0; ic < " << fShapeW[0] << "; ic++) {\n";
0387    out << SP << SP << "for (std::size_t oc = 0; oc < " << fShapeW[1] << "; oc++) {\n";
0388    // out << SP << SP << SP << "size_t kIndex = 0;\n";  // filter index
0389    if (fDim > 2)
0390       out << SP << SP << SP << "for (std::size_t kd = 0; kd < " << kDepth << "; kd++) {\n";
0391    if (fDim > 1)
0392       out << SP << SP << SP << "for (std::size_t kh = 0; kh < " << kHeight << "; kh++) {\n";
0393    out << SP << SP << SP << SP << "for (std::size_t kw = 0; kw < " << kWidth << "; kw++) {\n";
0394 
0395    out << SP << SP << SP << SP << SP << "tensor_" << fNX << "_f[ic * " << ocstrideDil << " + oc * " << icstrideDil;
0396    if (fDim > 2)
0397       out << " + kd * " << dstrideDil;
0398    if (fDim > 1)
0399       out << " + kh * " << hstrideDil;
0400    out << " + kw * " << wstrideDil << "  ] = tensor_" << fNW << "[ic * " << ocstride << " + oc * " << icstride;
0401 
0402    if (fDim > 2)
0403       out << " + kd * " << dstride;
0404    if (fDim > 1)
0405       out << " + kh * " << hstride;
0406    out << " + kw ];\n";
0407 
0408    // here we rotate the input kernel tranforming  0,1,2,...N-1 in N-1,N-2,...,2,1,0
0409    // out << " + " << icstride -1 << " - kIndex ];\n"; // tranform 1,2,3,4 in 4,3,2,1
0410    // out << SP << SP << SP << SP << SP << "kIndex++;\n";  // update input filter index
0411 
0412    out << SP << SP << SP << SP << "}\n";
0413    if (fDim > 1)
0414       out << SP << SP << SP << "}\n";
0415    if (fDim > 2)
0416       out << SP << SP << SP << "}\n";
0417 
0418    out << SP << SP << "}\n";
0419    out << SP << "}\n";
0420 
0421    out << SP << "char " << OpName << "_transA = 'N';\n";
0422    out << SP << "char " << OpName << "_transB = 'T';\n";
0423    out << SP << "int " << OpName << "_m = " << iHeight * iWidth * iDepth << ";\n";
0424    out << SP << "int " << OpName << "_n = " << icstrideDil * fShapeW[1] << ";\n"; // output channels * filters
0425    out << SP << "int " << OpName << "_k = " << fShapeW[0] << ";\n";               // input channels
0426    out << SP << "float " << OpName << "_alpha = 1.0;\n";
0427    out << SP << "float " << OpName << "_beta = 0.0;\n";
0428 
0429    if (!fUseSession) {
0430       out << SP << fType << " tensor_" << fNX << "_xcol[" << fShapeW[0] * icstrideDil * oDepth * oHeight * oWidth
0431           << "] = {0};\n";
0432    }
0433 
0434    // Loop on batch size
0435    out << SP << "for (size_t n = 0; n < " << bsize << "; n++) {\n";
0436 
0437    // IM2COL: Unroll the input tensor
0438    // order input data as  (e.g. kernel 2x2)  and (xa,ya) is channel 1 and (xb,yb) is channel 2
0439    //   (xa1,..,xak,ya1,..yak)(xb1,...,xbk,yb1,..,ybk)
0440    //   (xa2,...xak+1,ya1,...yak)(......)
0441    // trick for speed is using caffe im2col and output a matrix which contains filtered values as rows.
0442    // By doing this one has consecutive memory reads and writes
0443    // Resulting matrix op_xcol is (output channels * filter_h * filter_w , output_h * output_w)
0444    if (fDim == 1) {
0445       if (fAttrPads[0] != fAttrPads[1]) {
0446          std::cout << "TMVA SOFIE Operator Conv:  asymmetric padding not supported. Assume an average padding "
0447                    << std::endl;
0448          fAttrPads[0] = (fAttrPads[0] + fAttrPads[1]) / 2;
0449       }
0450       fAttrPads[1] = 0;
0451    }
0452    if (fDim == 2) {
0453       if (fAttrPads[0] != fAttrPads[2] || fAttrPads[1] != fAttrPads[3]) {
0454          std::cout << "TMVA SOFIE Operator ConvTranspose:  asymmetric padding not supported. Assume an average padding "
0455                    << std::endl;
0456          fAttrPads[0] = (fAttrPads[0] + fAttrPads[2]) / 2;
0457          fAttrPads[1] = (fAttrPads[1] + fAttrPads[3]) / 2;
0458       }
0459    }
0460    if (fDim == 3) {
0461       if (fAttrPads[0] != fAttrPads[3] || fAttrPads[1] != fAttrPads[4] || fAttrPads[2] != fAttrPads[5]) {
0462          std::cout << "TMVA SOFIE Operator ConvTranspose:  asymmetric padding not supported. Assume an average padding "
0463                    << std::endl;
0464          fAttrPads[0] = (fAttrPads[0] + fAttrPads[3]) / 2;
0465          fAttrPads[1] = (fAttrPads[1] + fAttrPads[4]) / 2;
0466          fAttrPads[2] = (fAttrPads[2] + fAttrPads[5]) / 2;
0467       }
0468    }
0469 
0470    if (fAttrGroup == 1) {
0471       out << SP << SP << "size_t x_offset = n * " << fShapeX[1] * iDepth * iHeight * iWidth << ";\n";
0472       out << SP << SP << "size_t out_offset = n * " << fShapeY[1] * oDepth * oHeight * oWidth << ";\n";
0473 
0474       // DO BLAS before:
0475       // BLAS
0476       out << SP << SP << "BLAS::sgemm_(&" << OpName << "_transA, &" << OpName << "_transB, &" << OpName << "_m, &"
0477           << OpName << "_n, &" << OpName << "_k, &" << OpName << "_alpha, "
0478           << "tensor_" << fNX << " + x_offset, &" << OpName
0479           << "_m,\n"; // use m if op_xcol is not transpose , otherwise k
0480       out << SP << SP << SP << "tensor_" << fNX << "_f, &" << OpName << "_n, &" << OpName << "_beta, tensor_" << fNX
0481           << "_xcol, &" << OpName << "_m);\n";
0482 
0483       // when using im2col - resulting matrix is transposed, is (input_c * filter_h * filter_w,  output_h *
0484       // output_w)
0485       // before using col2im I need to transpose matrix
0486       if (fDim < 3) {
0487          out << SP << SP << "TMVA::Experimental::SOFIE::UTILITY::col2im<float>(tensor_" << fNX
0488              << "_xcol,"
0489              //  channels, height, width, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h,
0490              //  dilation_w,
0491              << fShapeY[1] << "," << oHeight << "," << oWidth << ",";
0492          if (fDim == 1)
0493             out << "1, " << fAttrKernelShape[0] << ",0," << fAttrPads[0] << ",1," << fAttrStrides[0] << ",1,"
0494                 << fAttrDilations[0];
0495          else // dim ==2
0496             out << fAttrKernelShape[0] << "," << fAttrKernelShape[1] << "," << fAttrPads[0] << "," << fAttrPads[1]
0497                 << "," << fAttrStrides[0] << "," << fAttrStrides[1] << "," << fAttrDilations[0] << ","
0498                 << fAttrDilations[1];
0499          out << ", tensor_" << fNY << " + out_offset);\n\n ";
0500       } else {
0501          // 3d : needs a col2im for 3d
0502          throw std::runtime_error("TMVA SOFIE 3D Conv Transpose not yet supported");
0503          out << SP << SP << "TMVA::Experimental::SOFIE::UTILITY::Im2col_3d<float>(tensor_" << fNX
0504              << " + x_offset,"
0505              //  channels, d, h, w, k_d, k_h, k_w, pad_d, pad_h, pad_w, stride_d, stride_h, stride_w,
0506              //  dilation_d, dilation_h, dilation_w,
0507              //
0508              << fShapeX[1] << "," << oDepth << "," << oHeight << "," << oWidth << "," << fAttrKernelShape[0] << ","
0509              << fAttrKernelShape[1] << "," << fAttrKernelShape[2] << "," << fAttrPads[0] << "," << fAttrPads[1] << ","
0510              << fAttrPads[2] << "," << fAttrStrides[0] << "," << fAttrStrides[1] << "," << fAttrStrides[2] << ","
0511              << fAttrDilations[0] << "," << fAttrDilations[1] << "," << fAttrDilations[2] << ",tensor_" << fNX
0512              << "_xcol);\n\n ";
0513       }
0514       // // BLAS
0515       // out << SP << SP << "BLAS::sgemm_(&" << OpName << "_transA, &" << OpName << "_transB, &" << OpName << "_m, &"
0516       //     << OpName << "_n, &" << OpName << "_k, &" << OpName << "_alpha, tensor_" << fNX << "_xcol, &" << OpName
0517       //     << "_m,\n"; // use m if op_xcol is not transpose , otherwise k
0518       // out << SP << SP << SP <<"tensor_" << fNX << "_f, &" << OpName << "_k, &" << OpName << "_beta, tensor_" << fNY
0519       //     << " + out_offset, &" << OpName << "_m);\n";
0520    } else {
0521       // case of group transposed convolution
0522       // Unroll (IM2COL) the input tensor- make loop on groups and repeat operations (IM2COL + GEMM for each
0523       // group)
0524       out << SP << SP << "for (size_t g = 0; g < " << fAttrGroup << "; g++) {\n";
0525       out << SP << SP << "size_t x_offset = n * " << fShapeX[1] * iHeight * iWidth << " + g * "
0526           << fShapeX[1] * iHeight * iWidth / fAttrGroup << ";\n ";
0527       out << SP << SP << "size_t out_offset = n * " << fShapeY[1] * oHeight * oWidth << " + g * "
0528           << fShapeY[1] * oHeight * oWidth / fAttrGroup << ";\n ";
0529 
0530       // do BLAS here (LM: probably need an offset for op_f the kernels)
0531       out << SP << SP << "BLAS::sgemm_(&" << OpName << "_transA, &" << OpName << "_transB, &" << OpName << "_m, &"
0532           << OpName << "_n, &" << OpName << "_k, &" << OpName << "_alpha, "
0533           << "tensor_" << fNX << " + x_offset, &" << OpName
0534           << "_m,\n"; // use m if op_xcol is not transpose , otherwise k
0535       out << SP << SP << SP << "tensor_" << fNX << "_f, &" << OpName << "_n, &" << OpName << "_beta, tensor_" << fNX
0536           << "_xcol , &" << OpName << "_m);\n";
0537 
0538       if (fDim < 3) {
0539          out << SP << SP << "TMVA::Experimental::SOFIE::UTILITY::col2im<float>(tensor_" << fNX
0540              << "_xcol,"
0541              //  channels, height, width, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h,
0542              //  dilation_w,
0543              << fShapeY[1] << "," << oHeight << "," << oWidth << ",";
0544          if (fDim == 1)
0545             out << "1, " << fAttrKernelShape[0] << ",0," << fAttrPads[0] << ",1," << fAttrStrides[0] << ",1,"
0546                 << fAttrDilations[0];
0547          else // dim ==2
0548             out << fAttrKernelShape[0] << "," << fAttrKernelShape[1] << "," << fAttrPads[0] << "," << fAttrPads[1]
0549                 << "," << fAttrStrides[0] << "," << fAttrStrides[1] << "," << fAttrDilations[0] << ","
0550                 << fAttrDilations[1];
0551          out << ", tensor_" << fNY << " + out_offset);\n\n ";
0552       } else {
0553          // 3d im2col
0554          throw std::runtime_error("TMVA SOFIE 3D Conv Transpose not yet supported");
0555 
0556          out << SP << SP << "TMVA::Experimental::SOFIE::UTILITY::Im2col_3d<float>(tensor_" << fNX
0557              << " + x_offset,"
0558              //  channels, d, h, w, k_d, k_h, k_w, pad_d, pad_h, pad_w, stride_d, stride_h, stride_w,
0559              //  dilation_d, dilation_h, dilation_w,
0560              //
0561              << fShapeX[1] << "," << oDepth << "," << oHeight << "," << oWidth << "," << fAttrKernelShape[0] << ","
0562              << fAttrKernelShape[1] << "," << fAttrKernelShape[2] << "," << fAttrPads[0] << "," << fAttrPads[1] << ","
0563              << fAttrPads[2] << "," << fAttrStrides[0] << "," << fAttrStrides[1] << "," << fAttrStrides[2] << ","
0564              << fAttrDilations[0] << "," << fAttrDilations[1] << "," << fAttrDilations[2] << "," << "tensor_" << fNX
0565              << "_xcol);\n\n ";
0566       }
0567 
0568       // // BLAS
0569       // // offset g must be  g * k * n
0570       // out << SP << SP << SP << "size_t offset_f = g * " << fShapeW[0] * fShapeW[1] * icstrideDil / fAttrGroup <<
0571       // ";\n"; out << SP << SP << "BLAS::sgemm_(&" << OpName << "_transA, &" << OpName << "_transB, &" << OpName <<
0572       // "_m, &"
0573       //     << OpName << "_n, &" << OpName << "_k, &" << OpName << "_alpha, tensor_" << fNX << "_xcol, &" << OpName
0574       //     << "_m,\n"; // use m if op_xcol is not transpose , otherwise k
0575       // out << SP << SP << SP << "tensor_" << fNX << "_f + offset_f, &" << OpName << "_k, &" << OpName << "_beta,
0576       // tensor_" << fNY
0577       //     << " + out_offset"
0578       //     << ", &" << OpName << "_m);\n";
0579 
0580       out << SP << SP << "}\n"; // end of group loop
0581    }
0582 
0583    out << SP << "}\n"; // end of batch size loop
0584 
0585    if (fNBroadcastedB != "") {
0586       out << SP << "int " << OpName << "_size = " << fShapeY[0] * fShapeY[1] * oDepth * oHeight * oWidth << ";\n";
0587       out << SP << "float " << OpName << "_gamma = 1.0;\n";
0588       out << SP << "int " << OpName << "_incx = 1;\n";
0589       out << SP << "int " << OpName << "_incy = 1;\n";
0590 
0591       out << SP << "BLAS::saxpy_(&" << OpName << "_size, &" << OpName << "_gamma, tensor_" << fNBroadcastedB << ", &"
0592           << OpName << "_incx, tensor_" << fNY << ", &" << OpName << "_incy);\n";
0593    }
0594 
0595    return out.str();
0596 }
0597 
0598 } // namespace TMVA::Experimental::SOFIE
0599 
0600 #endif