1#ifndef TMVA_SOFIE_ROPERATOR_CONV
2#define TMVA_SOFIE_ROPERATOR_CONV
55 size_t group, std::vector<size_t> kernelShape, std::vector<size_t> pads,
56 std::vector<size_t> strides, std::string nameX, std::string nameW,
57 std::string nameB, std::string nameY):
63 if(std::is_same<T, float>::value) {
67 std::runtime_error(
"TMVA SOFIE Encountered unsupported type parsing a Conv operator");
74 size_t group, std::vector<size_t> kernelShape, std::vector<size_t> pads,
75 std::vector<size_t> strides, std::string nameX, std::string nameW,
81 if(std::is_same<T, float>::value) {
85 std::runtime_error(
"TMVA SOFIE Encountered unsupported type parsing a Conv operator");
91 std::vector<ETensorType>
TypeInference(std::vector<ETensorType> input)
override {
97 std::vector<Dim>
DoShapeInference(
const std::vector<Dim> & input,
const std::vector<size_t> & weight) {
101 if (input.size() -2 !=
fDim) {
102 throw std::runtime_error(
"TMVA SOFIE Conv Op Shape inference - invalid input ");
104 if (weight.size() -2 !=
fDim) {
105 throw std::runtime_error(
"TMVA SOFIE Conv Op Shape inference - invalid weights ");
108 throw std::runtime_error(
"TMVA SOFIE Conv - param shapes not supported without group attr");
110 if (input[2].isParam || (
fDim > 1 && input[3].isParam) || (
fDim > 2 && input[4].isParam))
111 throw std::runtime_error(
"TMVA SOFIE Conv - param shapes not supported without kernel attr");
124 size_t i1 = (
fDim > 1) ? ((
fDim > 2) ? 3 : 2) : 1;
125 size_t i2 = (
fDim > 2) ? 4 : 3;
151 for (
size_t d = 0;
d <
fDim; ++
d) {
152 if (input[
d + 2].isParam)
153 throw std::runtime_error(
154 "TMVA SOFIE Conv Op: SAME padding with parametric input shape is not supported");
159 for (
size_t d = 0;
d <
fDim; ++
d) {
160 size_t inSize = input[
d + 2].dim;
162 size_t outSize = (inSize + stride_d - 1) / stride_d;
163 int totalPad = std::max(0, (
int)((outSize - 1) * stride_d +
fAttrKernelShape[
d]) - (
int)inSize);
168 fAttrPads[
d] = (size_t)(totalPad - totalPad / 2);
174 std::runtime_error(
"TMVA SOFIE Conv Op invalid fAutopad");
179 Dim input1 = input[2];
187 auto computeOutput = [&](
Dim inputDim,
size_t kernel,
size_t pad,
size_t stride) {
189 size_t outSize = (inputDim.
dim + pad - kernel) / stride + 1;
193 if ((pad - kernel + 1) == 0 )
197 int64_t
v = pad - kernel + 1;
198 std::string outStr =
"(" + inputDim.
param +
"+" + std::to_string(
v) +
")";
199 return Dim{ outStr,
static_cast<size_t>(-1)};
202 int64_t
v = pad - kernel;
204 "((" + inputDim.
param +
"+" + std::to_string(
v) +
")/" + std::to_string(stride) +
"+1)";
205 return Dim{ outStr,
static_cast<size_t>(-1)};
208 throw std::runtime_error(
"TMVA SOFIE Conv Op - invalid values");
214 Dim batch_size = input[0];
215 Dim output_channels =
Dim{weight[0]};
217 std::vector<Dim>
ret({ batch_size, output_channels, output1 });
226 ret.push_back(output2);
234 ret.push_back(output3);
240 if (!model.CheckIfTensorAlreadyExist(
fNX)) {
242 std::runtime_error(
"TMVA SOFIE Conv op Input Tensor " +
fNX +
" is not found in model");
248 std::runtime_error(
"TMVA SOFIE Conv Op input data tensor" +
fNX +
" is not of 3,4 or 5 dimensions");
251 if (!model.CheckIfTensorAlreadyExist(
fNW)) {
253 std::runtime_error(
"TMVA SOFIE Conv op Input weight Tensor " +
fNW +
" is not found in model");
258 throw std::runtime_error(
"TMVA SOFIE Conv Op input weight tensor" +
fNW +
" is not of 3,4 or 5 dimensions");
261 model.AddIntermediateTensor(
fNY, model.GetTensorType(
fNX),
fShapeY);
263 if (!model.CheckIfTensorAlreadyExist(
fNB)) {
265 std::runtime_error(
"TMVA SOFIE Conv op Input Tensor " +
fNB +
" is not found in model");
270 std::runtime_error(
"TMVA SOFIE Conv op : invalid shape for Bias tensor (is not 1D)");
272 auto shapeDimB = model.GetDimTensorShape(
fNB);
274 if (broadcast_needed) {
275 auto original_data = model.GetInitializedTensorData(
fNB);
278 throw std::runtime_error(
"TMVA SOFIE Conv op: Bias Tensor has empty shape");
281 if (!(shapeDimB[0] ==
fShapeY[1]))
282 throw std::runtime_error(
"TMVA SOFIE Conv op: Bias Tensor has wrong shape: " +
284 if (
fType !=
"float")
285 throw std::runtime_error(
"TMVA SOFIE Conv op: Broadcasting for non-float type tensors is not supported");
290 std::vector<size_t> shape(
fDim + 1, 1);
293 std::shared_ptr<void> new_data_ptr(
295 std::default_delete<
float[]>());
296 model.UpdateInitializedTensor(
fNB, model.GetTensorType(
fNB), intTargetShape, new_data_ptr);
302 std::vector<Dim> outputDims = std::vector<Dim>(
fShapeY.begin()+2,
fShapeY.end());
306 if (outputInts.empty()) {
308 channelDim =
Dim{ outputChannelSize,
static_cast<size_t>(-1)};
311 channelDim =
Dim{ outputChannelSize };
314 for (
size_t i = 1; i <
fDim; i++) {
318 std::vector<size_t> shape1 = {
fShapeW[0],
fShapeW[1], kernelSize};
319 std::vector<Dim> shape2 = {
Dim{
fShapeW[1]},
Dim{kernelSize}, channelDim };
329 if (model.Verbose()) {
336 std::stringstream out;
340 std::vector<size_t> shape(
fDim + 1, 1);
344 out <<
"//--- broadcast bias tensor " <<
fNB <<
"for Conv op if needed \n";
352 out <<
SP <<
SP <<
"float * data = TMVA::Experimental::SOFIE::UTILITY::UnidirectionalBroadcast(tensor_"
354 out <<
SP <<
SP <<
"fTensor_" <<
fNB <<
".resize(" << length <<
");\n";
355 out <<
SP <<
SP <<
"std::copy(data, data + " << length <<
", fTensor_" <<
fNB <<
".begin());\n";
356 out <<
SP <<
SP <<
"tensor_" <<
fNB <<
" = fTensor_" <<
fNB <<
".data();\n";
357 out <<
SP <<
SP <<
"delete[] data;\n";
363 std::string
Generate(std::string OpName)
override {
364 OpName =
"op_" + OpName;
368 std::runtime_error(
"TMVA SOFIE Conv Op called to Generate without being initialized first");
371 std::stringstream out;
389 out <<
"\n//---- operator Conv " << OpName <<
"\n";
395 size_t id = (
fDim > 2) ?
fDim-3 : 2;
396 size_t ih = (
fDim > 1) ?
fDim-2 : 1;
400 size_t hstride = kWidth;
402 size_t dstride = kHeight * kWidth;
404 size_t icstride = kHeight * kWidth * kDepth;
406 size_t ocstride =
fShapeW[1] * icstride;
407 size_t ocstrideDil =
fShapeW[1] * icstrideDil;
409 out <<
SP <<
"for (std::size_t oc = 0; oc < " <<
fShapeW[0] <<
"; oc++) {\n";
410 out <<
SP <<
SP <<
"for (std::size_t ic = 0; ic < " <<
fShapeW[1] <<
"; ic++) {\n";
412 out <<
SP <<
SP <<
SP <<
"for (std::size_t kd = 0; kd < " << kDepth <<
"; kd++) {\n";
414 out <<
SP <<
SP <<
SP <<
"for (std::size_t kh = 0; kh < " << kHeight <<
"; kh++) {\n";
415 out <<
SP <<
SP <<
SP <<
SP <<
"for (std::size_t kw = 0; kw < " << kWidth <<
"; kw++) {\n";
417 out <<
SP <<
SP <<
SP <<
SP <<
SP <<
"tensor_" <<
fNX <<
"_f[oc * "
418 << ocstrideDil <<
" + ic * " << icstrideDil;
419 if (
fDim > 2) out <<
" + kd * " << dstrideDil;
420 if (
fDim > 1) out <<
" + kh * " << hstrideDil;
421 out <<
" + kw * " << wstrideDil <<
" ] = tensor_" <<
fNW <<
"[oc * " << ocstride <<
" + ic * " << icstride;
422 if (
fDim > 2) out <<
" + kd * " << dstride;
423 if (
fDim > 1) out <<
" + kh * " << hstride;
426 out <<
SP <<
SP <<
SP <<
SP <<
"}\n";
429 out <<
SP <<
SP <<
"}\n";
433 out <<
SP <<
"char " << OpName <<
"_transA = 'N';\n";
434 out <<
SP <<
"char " << OpName <<
"_transB = 'N';\n";
435 out <<
SP <<
"int " << OpName <<
"_m = " << outputChannelStride <<
";\n";
438 out <<
SP <<
"int " << OpName <<
"_n = " <<
fShapeW[0] <<
";\n";
440 out <<
SP <<
"float " << OpName <<
"_alpha = 1.0;\n";
442 out <<
SP <<
"float " << OpName <<
"_beta = 1.0;\n";
444 out <<
SP <<
"float " << OpName <<
"_beta = 0.0;\n";
448 out <<
SP <<
"for (size_t n = 0; n < " << bsize <<
"; n++) {\n";
459 std::cout <<
"TMVA SOFIE Operator Conv: asymmetric padding not supported. Assume an average padding "
468 std::cout <<
"TMVA SOFIE Operator Conv: asymmetric padding not supported. Assume an average padding " << std::endl;
475 std::cout <<
"TMVA SOFIE Operator Conv: asymmetric padding not supported. Assume an average padding " << std::endl;
481 out <<
SP <<
SP <<
"size_t out_offset = n * " << outputBatchStride <<
";\n";
484 out <<
SP <<
SP <<
"size_t x_offset = n * " << inputBatchStride <<
";\n";
488 out <<
SP <<
SP <<
"TMVA::Experimental::SOFIE::UTILITY::Im2col<float>(tensor_" <<
fNX
493 <<
fShapeW[1] <<
"," << iHeight <<
"," << iWidth <<
",";
501 out <<
"," <<
"tensor_" <<
fNX <<
"_xcol);\n\n ";
504 out <<
SP <<
SP <<
"TMVA::Experimental::SOFIE::UTILITY::Im2col_3d<float>(tensor_" <<
fNX
509 <<
fShapeW[1] <<
"," << iDepth <<
"," << iHeight <<
"," << iWidth <<
","
514 <<
"tensor_" <<
fNX <<
"_xcol);\n\n ";
517 out <<
SP <<
"TMVA::Experimental::SOFIE::Gemm_Call("
518 <<
"tensor_" <<
fNY <<
" + out_offset, false, false, "
519 << OpName <<
"_m, " << OpName <<
"_n, " << OpName <<
"_k, "
520 << OpName <<
"_alpha, " <<
"tensor_" <<
fNX <<
"_xcol, tensor_" <<
fNX <<
"_f, "
521 << OpName <<
"_beta, ";
523 out <<
"tensor_" <<
fNB;
539 out <<
SP <<
SP <<
"for (size_t g = 0; g < " <<
fAttrGroup <<
"; g++) {\n";
540 out <<
SP <<
SP <<
"size_t x_offset = n * " << inputBatchStride <<
" + g * "
541 <<
fShapeW[1] <<
" * " << inputChannelStride <<
";\n ";
542 out <<
SP <<
SP <<
"size_t g_offset = g * " <<
fShapeW[0] <<
" * (" << outputChannelStride <<
") / " <<
fAttrGroup <<
";\n ";
543 out <<
SP <<
SP <<
"size_t out_offset = n * " << outputBatchStride <<
" + g_offset;\n";
546 out <<
SP <<
SP <<
"TMVA::Experimental::SOFIE::UTILITY::Im2col<float>(tensor_" <<
fNX
551 <<
fShapeW[1] <<
"," << iHeight <<
"," << iWidth <<
",";
559 out <<
", tensor_" <<
fNX <<
"_xcol);\n\n ";
562 out <<
SP <<
SP <<
"TMVA::Experimental::SOFIE::UTILITY::Im2col_3d<float>(tensor_" <<
fNX
578 out <<
SP <<
SP <<
SP <<
"size_t offset_f = g * "
582 out <<
SP <<
"TMVA::Experimental::SOFIE::Gemm_Call("
583 <<
"tensor_" <<
fNY <<
" + out_offset, false, false, "
584 << OpName <<
"_m, " << OpName <<
"_n, " << OpName <<
"_k, "
585 << OpName <<
"_alpha, " <<
"tensor_" <<
fNX <<
"_xcol, tensor_" <<
fNX <<
"_f + offset_f, "
586 << OpName <<
"_beta, ";
588 out <<
"tensor_" <<
fNB <<
" + g_offset";
600 out <<
SP <<
SP <<
"}\n";
620 std::vector<std::string>
GetBlasRoutines()
override {
return { std::string(
"Gemm"), std::string(
"Axpy") }; }
std::vector< size_t > fAttrDilations
std::string Generate(std::string OpName) override
std::vector< Dim > fShapeY
std::vector< size_t > fShapeW
ROperator_Conv(std::string autopad, std::vector< size_t > dilations, size_t group, std::vector< size_t > kernelShape, std::vector< size_t > pads, std::vector< size_t > strides, std::string nameX, std::string nameW, std::string nameB, std::string nameY)
std::vector< std::string > GetBlasRoutines() override
Returns the blas routines needed to compile the generated code.
std::vector< Dim > fShapeX
void Initialize(RModel &model) override
std::string GenerateInitCode() override
std::vector< size_t > fAttrStrides
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input) override
std::vector< size_t > fShapeB
std::vector< size_t > fAttrPads
ROperator_Conv(std::string autopad, std::vector< size_t > dilations, size_t group, std::vector< size_t > kernelShape, std::vector< size_t > pads, std::vector< size_t > strides, std::string nameX, std::string nameW, std::string nameY)
std::vector< Dim > DoShapeInference(const std::vector< Dim > &input, const std::vector< size_t > &weight)
std::vector< size_t > fAttrKernelShape
std::vector< std::string_view > fInputTensorNames
const std::string SP
space used to correctly indent the generated C++ code
bool fUseSession
flag to identify if using the session class
std::vector< std::string_view > fOutputTensorNames
bool AreSameShape(const std::vector< size_t > &, const std::vector< size_t > &)
T * UnidirectionalBroadcast(const T *data, const std::vector< size_t > &shape, const std::vector< size_t > &targetShape)
std::string ConvertDimShapeToString(const std::vector< Dim > &shape)
std::size_t ConvertShapeToLength(const std::vector< size_t > &shape)
std::vector< size_t > ConvertShapeToInt(const std::vector< Dim > &shape)
Convert shape based on Dim to integer format.
ETensorType ConvertStringToType(std::string type)
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
create variable transformations