1#ifndef TMVA_SOFIE_ROPERATOR_GEMM
2#define TMVA_SOFIE_ROPERATOR_GEMM
56 static_assert(std::is_same_v<T, float>,
57 "TMVA::SOFIE - Unsupported type parsing a Gemm operator");
73 std::vector<ETensorType>
TypeInference(std::vector<ETensorType> input)
override {
80 if (input.size() > 3)
throw std::runtime_error(
"TMVA SOFIE Gemm Op Shape Inference only need 2 or 3 input tensor");
85 throw std::runtime_error(
"TMVA SOFIE Gemm Op Shape Inference only accept input tensor with >=2 dimensions");
90 if (input.size() == 3){
95 int ioffset = input[0].size()-2;
97 std::vector<U> s_a(input[0].begin() + ioffset, input[0].begin() + ioffset + 2);
98 std::vector<U> s_b(input[1].begin() + ioffset, input[1].begin() + ioffset + 2);
101 std::reverse(s_a.begin(), s_a.end());
104 std::reverse(s_b.begin(), s_b.end());
107 s_y.reserve(input[0].
size());
108 if (input[0].
size() > 2 && input[1].
size() == input[0].
size()) {
112 for (
size_t i = 0; i < input[0].size()-2; i++) {
113 Dim valueA = input[0][i];
114 Dim valueB = input[1][i];
116 if (valueB.
GetVal() ==
"1")
117 s_y.push_back(input[0][i]);
118 else if (valueA.
GetVal() ==
"1")
119 s_y.push_back(input[1][i]);
121 throw std::runtime_error(
"TMVA SOFIE Gemm Op - invalid input shapes " + valueA.
GetVal() +
" and "
125 auto & dimNames =
fModel->GetDimShapeNames();
126 auto p1 = std::find(dimNames.begin(), dimNames.end(), valueA.
param);
127 auto p2 = std::find(dimNames.begin(), dimNames.end(), valueB.
param);
128 if (p1 < p2) s_y.push_back(input[0][i]);
129 else s_y.push_back(input[1][i]);
132 s_y.push_back(input[0][i]);
134 s_y.push_back(input[1][i]);
136 throw std::runtime_error(
"TMVA SOFIE Gemm Op - invalid input shapes " + valueA.
GetVal() +
" and "
140 s_y.push_back(input[0][i]);
144 s_y.push_back(s_a[0]);
145 s_y.push_back(s_b[1]);
149 std::vector<std::vector<size_t>>
ShapeInference(std::vector<std::vector<size_t>> input)
override {
150 std::vector<std::vector<size_t>>
ret;
164 if ((model.CheckIfTensorAlreadyExist(
fNA) ==
false) || (model.CheckIfTensorAlreadyExist(
fNB) ==
false) ){
165 throw std::runtime_error(
"TMVA SOFIE Gemm Op Input Tensor " +
fNA +
" or " +
fNB +
" is not found in model");
168 if (model.CheckIfTensorAlreadyExist(
fNC) ==
false){
169 throw std::runtime_error(
"TMVA SOFIE Gemm Op Input Tensor " +
fNC +
" is not found in model");
172 if (model.IsDynamicTensor(
fNA) || model.IsDimInputTensor(
fNA) ) {
176 auto shapeA_int = model.GetTensorShape(
fNA);
180 bool prependOne =
false;
186 if (model.IsDynamicTensor(
fNB) || model.IsDimInputTensor(
fNB)) {
191 auto shapeB_int = model.GetTensorShape(
fNB);
195 bool appendOne =
false;
216 if (model.IsDynamicTensor(
fNC))
223 bool broadcast_needed =
false;
225 broadcast_needed =
true;
228 broadcast_needed = (
fShapeC != shapeY);
231 if (broadcast_needed) {
248 shapeY.erase(shapeY.begin());
254 shapeY.erase(shapeY.end()-1);
258 model.AddIntermediateTensor(
fNY, model.GetTensorType(
fNA), shapeY);
260 model.AddDynamicTensor(
fNY, model.GetTensorType(
fNA),
fShapeY);
262 if (model.Verbose()){
263 std::cout <<
"Gemm (or MatMul) " <<
" ---> " <<
fNY <<
" shape ";
270 model.AddNeededStdLib(
"algorithm");
273 std::string
Generate(std::string opName)
override {
274 opName =
"op_" + opName;
277 throw std::runtime_error(
"TMVA SOFIE Gemm Op called to Generate without being initialized first");
279 std::stringstream out;
287 if (dimA != dimB || dimA != dimY || (
fBroadcastBias && dimC != dimY)) {
292 throw std::runtime_error(
"TMVA SOFIE Gemm(MatMul) has invalid shape for inputs or output");
301 std::vector<Dim> sExtraY;
302 for (int64_t i = 0; i < dimY-2; i++) {
307 std::string lengthExtra_C;
308 std::vector<Dim> sExtraC;
310 bool haveExtraC =
false;
313 for (int64_t i = 0; i < dimC-2; i++) {
317 if (lengthExtra_C !=
"1") haveExtraC =
true;
318 }
else if (dimC > 0) {
319 for (int64_t i = 0; i < dimC; i++) {
333 ( haveExtraC && std::stoi(lengthExtra_Y) != std::stoi(lengthExtra_C)))
334 throw std::runtime_error(
"TMVA SOFIE Gemm Op " + opName +
" Bias tensor " +
fNC +
" has not correct size "
339 if (haveExtraC) out <<
SP <<
"assert(" << lengthExtra_Y <<
" == " << lengthExtra_C <<
");\n";
347 throw std::runtime_error(
"TMVA SOFIE Gemm Op " + opName +
" Bias tensor is not present but beta value in Gemm is not zero");
353 bool doStackMul = dimY > 2 && (
fIsDynamic || std::stoi(lengthExtra_Y) > 1);
355 std::string lengthExtra_A;
356 std::string lengthExtra_B;
357 std::string increment_A;
358 std::string increment_B;
371 bool extraA = (doStackMul && lengthExtra_A !=
"1");
372 bool extraB = (doStackMul && lengthExtra_B !=
"1");
376 out <<
SP <<
"size_t " << opName <<
"_y_offset = 0;\n";
378 out <<
SP <<
"size_t " << opName <<
"_A_offset = 0;\n";
380 out <<
SP <<
"size_t " << opName <<
"_B_offset = 0;\n";
382 out <<
SP <<
"size_t " << opName <<
"_C_offset = 0;\n";
383 out <<
SP <<
"for (size_t i = 0; i < " << lengthExtra_Y <<
"; i++){\n";
391 out << SP2 <<
"for (size_t j = 0; j < " << sY[0] <<
"; j++) { \n";
392 out << SP2 <<
SP <<
"size_t y_index = ";
394 out << opName <<
"_y_offset + ";
395 if (sY[1].GetVal() !=
"1")
396 out << sY[1] <<
" * j;\n";
400 std::string prefix = SP2 +
SP +
"TMVA::Experimental::SOFIE::";
401 std::string target =
"tensor_" +
fNY;
402 if (sC.size() != 2) {
404 }
if (sC[0].GetVal() ==
"1" && sC[1].GetVal() == sY[1].GetVal()) {
405 out << prefix <<
"Copy(" << target <<
" + y_index, tensor_" <<
fNC <<
", " << sY[1] <<
");\n";
406 }
else if (sC[1].GetVal() ==
"1" && sC[0].GetVal() == sY[0].GetVal()) {
407 out << prefix <<
"Fill(" << target <<
" + y_index, tensor_" <<
fNC <<
"[j], " << sY[1] <<
");\n";
408 }
else if (sC[0].GetVal() ==
"1" && sC[1].GetVal() ==
"1") {
410 out << prefix <<
"Fill(" << target <<
" + y_index, tensor_" <<
fNC <<
"[0], " << sY[1] <<
");\n";
418 if (
fType ==
"float"){
420 out << SP2 <<
"TMVA::Experimental::SOFIE::Gemm_Call(" <<
"tensor_" <<
fNY;
421 if (doStackMul) out <<
" + " << opName <<
"_y_offset";
425 <<
n <<
", " <<
m <<
", " << k <<
", ";
426 out << std::setprecision(std::numeric_limits<float>::max_digits10) <<
fAttrAlpha <<
", tensor_" <<
fNB;
427 if (extraB) out <<
" + " << opName <<
"_B_offset";
428 out <<
", tensor_" <<
fNA;
429 if (extraA) out <<
" + " << opName <<
"_A_offset";
430 out <<
", " << std::setprecision(std::numeric_limits<float>::max_digits10) <<
fAttrBeta <<
",";
433 out <<
"tensor_" <<
fNC;
435 out <<
" + " << opName <<
"_C_offset";
445 out <<
SP <<
SP << opName <<
"_y_offset += " << lengthGemm <<
";\n";
446 if (lengthExtra_A !=
"1")
447 out <<
SP <<
SP << opName <<
"_A_offset += " << increment_A <<
";\n";
448 if (lengthExtra_B !=
"1")
449 out <<
SP <<
SP << opName <<
"_B_offset += " << increment_B <<
";\n";
452 out <<
SP <<
SP << opName <<
"_C_offset += " << lengthGemm <<
";\n";
458 out <<
SP <<
"//--- applying RELU to output\n";
459 std::string tnsr =
"tensor_" +
fNY;
461 out <<
SP <<
"TMVA::Experimental::SOFIE::Relu(" << tnsr <<
", " << tnsr <<
", " << reluSize <<
");\n";
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROperator_Gemm(float alpha, float beta, int_t transA, int_t transB, std::string nameA, std::string nameB, std::string nameC, std::string nameY, EActivationType activation=EActivationType::UNDEFINED)
std::vector< Dim > DynamicShapeInference(const std::vector< std::vector< Dim > > &input)
std::vector< Dim > fShapeY
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input) override
ROperator_Gemm(float alpha, float beta, int_t transA, int_t transB, std::string nameA, std::string nameB, std::string nameY, EActivationType activation=EActivationType::UNDEFINED)
std::vector< std::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > input) override
std::vector< U > DoShapeInference(const std::vector< std::vector< U > > &input)
std::vector< Dim > fShapeA
std::vector< Dim > fShapeB
std::vector< size_t > fShapeC
std::string Generate(std::string opName) override
void Initialize(RModel &model) override
std::vector< Dim > fDimShapeC
EActivationType fActivation
std::vector< std::string > GetBlasRoutines() override
std::vector< std::string_view > fInputTensorNames
const std::string SP
space used to correctly indent the generated C++ code
std::vector< std::string_view > fOutputTensorNames
std::vector< size_t > MultidirectionalBroadcastShape(std::vector< std::vector< size_t > >)
std::string ConvertDimShapeToString(const std::vector< Dim > &shape)
std::vector< Dim > ConvertShapeToDim(const std::vector< size_t > &shape)
Convert shape from integer format to dynamic one (based on Dim).
std::vector< size_t > ConvertShapeToInt(const std::vector< Dim > &shape)
Convert shape based on Dim to integer format.
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
create variable transformations
std::string GetVal() const