diff --git a/tmva/sofie/CMakeLists.txt b/tmva/sofie/CMakeLists.txt index 5378a27ff419a..1fefffc99b639 100644 --- a/tmva/sofie/CMakeLists.txt +++ b/tmva/sofie/CMakeLists.txt @@ -16,7 +16,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(ROOTTMVASofie TMVA/OperatorList.hxx TMVA/RModel.hxx TMVA/ROperator.hxx - TMVA/ROperator_Add.hxx + TMVA/ROperator_BasicBinary.hxx TMVA/ROperator_BatchNormalization.hxx TMVA/ROperator_Conv.hxx TMVA/ROperator_Gemm.hxx diff --git a/tmva/sofie/inc/TMVA/OperatorList.hxx b/tmva/sofie/inc/TMVA/OperatorList.hxx index 09222c18f02a2..179cfdac4af39 100644 --- a/tmva/sofie/inc/TMVA/OperatorList.hxx +++ b/tmva/sofie/inc/TMVA/OperatorList.hxx @@ -9,7 +9,7 @@ #include "TMVA/ROperator_LSTM.hxx" #include "TMVA/ROperator_BatchNormalization.hxx" #include "TMVA/ROperator_Pool.hxx" -#include "TMVA/ROperator_Add.hxx" +#include "TMVA/ROperator_BasicBinary.hxx" #include "TMVA/ROperator_Reshape.hxx" #include "TMVA/ROperator_Slice.hxx" #include "TMVA/ROperator_GRU.hxx" diff --git a/tmva/sofie/inc/TMVA/ROperator_Add.hxx b/tmva/sofie/inc/TMVA/ROperator_Add.hxx deleted file mode 100644 index c3d06222ba6b2..0000000000000 --- a/tmva/sofie/inc/TMVA/ROperator_Add.hxx +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef TMVA_SOFIE_ROPERATOR_ADD -#define TMVA_SOFIE_ROPERATOR_ADD - -#include "TMVA/SOFIE_common.hxx" -#include "TMVA/ROperator.hxx" -#include "TMVA/RModel.hxx" - -#include - -namespace TMVA{ -namespace Experimental{ -namespace SOFIE{ - -template -class ROperator_Add final : public ROperator -{ - -private: - - std::string fNX1; - std::string fNX2; - std::string fNY; - std::vector fShape; - -public: - ROperator_Add(){} - ROperator_Add(std::string nameX1, std::string nameX2, std::string nameY): - fNX1(UTILITY::Clean_name(nameX1)), fNX2(UTILITY::Clean_name(nameX2)), fNY(UTILITY::Clean_name(nameY)){} - - // type of output given input - std::vector TypeInference(std::vector input){ - return input; - } - - // shape of output tensors given input tensors - std::vector> ShapeInference(std::vector> input){ - // assume now inputs have same shape (no broadcasting) - auto ret = std::vector>(1, input[0]); // return vector size 1 with first input - return ret; - } - - void Initialize(RModel& model){ - // input must be a graph input, or already initialized intermediate tensor - if (model.CheckIfTensorAlreadyExist(fNX1) == false){ - throw std::runtime_error(std::string("TMVA SOFIE Add Op Input Tensor ") + fNX1 + "is not found in model"); - } - if (model.CheckIfTensorAlreadyExist(fNX2) == false) { - throw std::runtime_error(std::string("TMVA SOFIE Add Op Input Tensor ") + fNX1 + "is not found in model"); - } - auto shapeX1 = model.GetTensorShape(fNX1); - auto shapeX2 = model.GetTensorShape(fNX2); - // assume same shape X1 and X2 - if (shapeX1 != shapeX2) { - std::string msg = "TMVA SOFIE Add Op: Support only inputs with same shape, shape 1 is " + - ConvertShapeToString(shapeX1) + "shape 2 is " + ConvertShapeToString(shapeX2); - throw std::runtime_error(msg); - } - fShape = shapeX1; - model.AddIntermediateTensor(fNY, model.GetTensorType(fNX1), fShape); - } - - - std::string Generate(std::string OpName){ - OpName = "op_" + OpName; - if (fShape.empty()) { - throw std::runtime_error("TMVA SOFIE Add called to Generate without being initialized first"); - } - std::stringstream out; - // int length = 1; - // for(auto& i: fShape){ - // length *= i; - // } - size_t length = ConvertShapeToLength(fShape); - out << "\n//------ Add\n"; - out << SP << "for (size_t id = 0; id < " << length << " ; id++){\n"; - out << SP << SP << "tensor_" << fNY << "[id] = tensor_" << fNX1 << "[id] + tensor_" << fNX2 << "[id];\n"; - out << SP << "}\n"; - return out.str(); - } - -}; - -}//SOFIE -}//Experimental -}//TMVA - - -#endif //TMVA_SOFIE_ROPERATOR_Add diff --git a/tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx b/tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx new file mode 100644 index 0000000000000..a2d0632edcdf2 --- /dev/null +++ b/tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx @@ -0,0 +1,122 @@ +#ifndef TMVA_SOFIE_ROperator_BasicBinary +#define TMVA_SOFIE_ROperator_BasicBinary + +#include "TMVA/SOFIE_common.hxx" +#include "TMVA/ROperator.hxx" +#include "TMVA/RModel.hxx" + +#include + +namespace TMVA{ +namespace Experimental{ +namespace SOFIE{ + +enum EBasicBinaryOperator { Add, Sub, Mul, Div }; + +template +struct BinaryOperatorTrait { + const char *Name() { return ""; } + const char *Op() { return ""; } +}; +template +struct BinaryOperatorTrait { + static const char *Name() { return "Add"; } + static const char *Op() { return "+"; } +}; + +template +struct BinaryOperatorTrait { + static const char *Name() { return "Sub"; } + static const char *Op() { return "-"; } +}; + +template +struct BinaryOperatorTrait { + static const char *Name() { return "Mul"; } + static const char *Op() { return "*"; } +}; + +template +struct BinaryOperatorTrait { + static const char *Name() { return "Div"; } + static const char *Op() { return "/"; } +}; + +template +class ROperator_BasicBinary final : public ROperator{ +private: + + std::string fNX1; + std::string fNX2; + std::string fNY; + std::vector fShape; + + // template + // BinaryOperatorTrait *s; + +public: + ROperator_BasicBinary(){} + ROperator_BasicBinary(std::string nameX1, std::string nameX2, std::string nameY): + fNX1(UTILITY::Clean_name(nameX1)), fNX2(UTILITY::Clean_name(nameX2)), fNY(UTILITY::Clean_name(nameY)){} + + // type of output given input + std::vector TypeInference(std::vector input){ + return input; + } + + // shape of output tensors given input tensors + std::vector> ShapeInference(std::vector> input){ + // assume now inputs have same shape (no broadcasting) + auto ret = std::vector>(1, input[0]); // return vector size 1 with first input + return ret; + } + + void Initialize(RModel& model){ + // input must be a graph input, or already initialized intermediate tensor + if (model.CheckIfTensorAlreadyExist(fNX1) == false){ + throw std::runtime_error(std::string("TMVA SOFIE Binary Op Input Tensor ") + fNX1 + "is not found in model"); + } + if (model.CheckIfTensorAlreadyExist(fNX2) == false) { + throw std::runtime_error(std::string("TMVA SOFIE Binary Op Input Tensor ") + fNX2 + "is not found in model"); + } + auto shapeX1 = model.GetTensorShape(fNX1); + auto shapeX2 = model.GetTensorShape(fNX2); + // assume same shape X1 and X2 + if (shapeX1 != shapeX2) { + fShape = UTILITY::Multidirectional_broadcast(shapeX1,shapeX2); + } + else if(shapeX1 == shapeX2){ + fShape = shapeX1; + } + model.AddIntermediateTensor(fNY, model.GetTensorType(fNX1), fShape); + } + + + std::string Generate(std::string OpName){ + OpName = "op_" + OpName; + + if (fShape.empty()) { + throw std::runtime_error("TMVA SOFIE Binary Op called to Generate without being initialized first"); + } + std::stringstream out; + // int length = 1; + // for(auto& i: fShape){ + // length *= i; + // } + size_t length = ConvertShapeToLength(fShape); + out << "\n//------ " + std::string(BinaryOperatorTrait::Name())+"\n"; + out << SP << "for (size_t id = 0; id < " << length << " ; id++){\n"; + out << SP << SP << "tensor_" << fNY << "[id] = tensor_" << fNX1 << "[id]" + + std::string(BinaryOperatorTrait::Op()) + "tensor_" << fNX2 << "[id];\n"; + out << SP << "}\n"; + return out.str(); + } + +}; + +}//SOFIE +}//Experimental +}//TMVA + + +#endif //TMVA_SOFIE_ROperator_BasicBinary \ No newline at end of file diff --git a/tmva/sofie/inc/TMVA/SOFIE_common.hxx b/tmva/sofie/inc/TMVA/SOFIE_common.hxx index 7876727eef0ae..6f72b27104c5a 100644 --- a/tmva/sofie/inc/TMVA/SOFIE_common.hxx +++ b/tmva/sofie/inc/TMVA/SOFIE_common.hxx @@ -105,6 +105,7 @@ ETensorType GetTemplatedType(T /*obj*/ ){ namespace UTILITY{ template T* Unidirectional_broadcast(const T* original_data, const std::vector original_shape, const std::vector target_shape); +std::vector Multidirectional_broadcast(const std::vector input1_shape, const std::vector input2_shape); std::string Clean_name(std::string input_tensor_name); diff --git a/tmva/sofie/src/SOFIE_common.cxx b/tmva/sofie/src/SOFIE_common.cxx index 78feff703cb48..3df8032c6b274 100644 --- a/tmva/sofie/src/SOFIE_common.cxx +++ b/tmva/sofie/src/SOFIE_common.cxx @@ -135,6 +135,49 @@ T* UTILITY::Unidirectional_broadcast(const T* original_data, const std::vector UTILITY::Multidirectional_broadcast(std::vector input1_shape, std::vector input2_shape) +{ + std::vector input_shape = (input1_shape.size() > input2_shape.size())?input1_shape:input2_shape; + std::vector output_shape(input_shape); + + if(input1_shape.size() < input2_shape.size()){ + // Check if input1_shape.size() < input2_shape.size() we insert in the shape vector values of 1 at the beginning of the tensor until input1_shape.size() == input2_shape.size() + auto it = input1_shape.begin(); + while (input1_shape.size() < input2_shape.size()) { + it = input1_shape.insert(it, 1); + } + } + else if(input2_shape.size() < input1_shape.size()){ + // Check if input2_shape.size() < input1_shape.size() we insert in the shape vector values of 1 at the beginning of the tensor until input1_shape.size() == input2_shape.size() + auto it = input2_shape.begin(); + while (input2_shape.size() < input1_shape.size()) { + it = input2_shape.insert(it, 1); + } + } + //check if both the input have same shape, nothing to do directly return the output_shape as the same shape. + if(input1_shape.size() == input2_shape.size()){ + if(input1_shape != input2_shape){ + //Check the shape values, if input1[i] not equal to input2[i] we have the result shape equal to input1[i] if input2[i] = 1 or viceversa + for(size_t j = 0; j < input1_shape.size() ; j++){ + if(input1_shape[j] == input2_shape[j]){ + output_shape[j] = input1_shape[j]; + } + else if(input1_shape[j] > input2_shape[j] && input2_shape[j] == 1){ + output_shape[j] = input1_shape[j]; + } + else if(input2_shape[j] > input1_shape[j] && input1_shape[j] == 1){ + output_shape[j] = input2_shape[j]; + } + } + } + + } + return output_shape; + +} + std::string UTILITY::Clean_name(std::string input_tensor_name){ std::string s (input_tensor_name); s.erase(std::remove_if(s.begin(), s.end(), []( char const& c ) -> bool { return !std::isalnum(c); } ), s.end()); @@ -145,4 +188,4 @@ template float* UTILITY::Unidirectional_broadcast(const float* original_data, co }//SOFIE }//Experimental -}//TMVA +}//TMVA \ No newline at end of file diff --git a/tmva/sofie/test/TestCustomModelsFromONNX.cxx b/tmva/sofie/test/TestCustomModelsFromONNX.cxx index 57da92f9d9ee7..fdb5b8c129919 100644 --- a/tmva/sofie/test/TestCustomModelsFromONNX.cxx +++ b/tmva/sofie/test/TestCustomModelsFromONNX.cxx @@ -12,6 +12,18 @@ #include "LinearWithSelu_FromONNX.hxx" #include "input_models/references/LinearWithSelu.ref.hxx" +#include "Sub_FromONNX.hxx" +#include "input_models/references/Sub.ref.hxx" + +#include "Add_FromONNX.hxx" +#include "input_models/references/Add.ref.hxx" + +#include "Mul_FromONNX.hxx" +#include "input_models/references/Mul.ref.hxx" + +#include "Div_FromONNX.hxx" +#include "input_models/references/Div.ref.hxx" + #include "LinearWithLeakyRelu_FromONNX.hxx" #include "input_models/references/LinearWithLeakyRelu.ref.hxx" @@ -134,6 +146,112 @@ TEST(ONNX, Linear32) } } +TEST(ONNX, Sub) + { + constexpr float TOLERANCE = DEFAULT_TOLERANCE; + + // Preparing the standard input + std::vector input1({ + 1, 2 + }); + std::vector input2({ + 0, 1 + }); + TMVA_SOFIE_Sub::Session s("Sub_FromONNX.dat"); + + std::vector output = s.infer(input2.data(),input1.data()); + + // Checking output size + EXPECT_EQ(output.size(), sizeof(Sub_ExpectedOutput::outputs) / sizeof(float)); + + float *correct = Sub_ExpectedOutput::outputs; + + // Checking every output value, one by one + for (size_t i = 0; i < output.size(); ++i) { + EXPECT_LE(std::abs(output[i] - correct[i]), TOLERANCE); + } + } + + +TEST(ONNX, Add) + { + constexpr float TOLERANCE = DEFAULT_TOLERANCE; + + // Preparing the standard input + std::vector input1({ + 5, 10, 15, 20, + 25, 30, 35, 40, + 45, 50, 55, 60 + }); + std::vector input2({ + 5, 10, 15, 20 + }); + TMVA_SOFIE_Add::Session s("Add_FromONNX.dat"); + + std::vector output = s.infer(input1.data(),input2.data()); + + // Checking output size + EXPECT_EQ(output.size(), sizeof(Add_ExpectedOutput::outputs) / sizeof(float)); + + float *correct = Add_ExpectedOutput::outputs; + + // Checking every output value, one by one + for (size_t i = 0; i < output.size(); ++i) { + EXPECT_LE(std::abs(output[i] - correct[i]), TOLERANCE); + } + } + +TEST(ONNX, Mul) + { + constexpr float TOLERANCE = DEFAULT_TOLERANCE; + + // Preparing the standard input + std::vector input1({ + 1, 2 + }); + std::vector input2({ + 0, 1 + }); + TMVA_SOFIE_Mul::Session s("Mul_FromONNX.dat"); + + std::vector output = s.infer(input1.data(),input2.data()); + + // Checking output size + EXPECT_EQ(output.size(), sizeof(Mul_ExpectedOutput::outputs) / sizeof(float)); + + float *correct = Mul_ExpectedOutput::outputs; + + // Checking every output value, one by one + for (size_t i = 0; i < output.size(); ++i) { + EXPECT_LE(std::abs(output[i] - correct[i]), TOLERANCE); + } + } + +TEST(ONNX, Div) + { + constexpr float TOLERANCE = DEFAULT_TOLERANCE; + + // Preparing the standard input + std::vector input1({ + 4, 2 + }); + std::vector input2({ + 2, 2 + }); + TMVA_SOFIE_Div::Session s("Div_FromONNX.dat"); + + std::vector output = s.infer(input2.data(),input1.data()); + + // Checking output size + EXPECT_EQ(output.size(), sizeof(Div_ExpectedOutput::outputs) / sizeof(float)); + + float *correct = Div_ExpectedOutput::outputs; + + // Checking every output value, one by one + for (size_t i = 0; i < output.size(); ++i) { + EXPECT_LE(std::abs(output[i] - correct[i]), TOLERANCE); + } + } TEST(ONNX, Linear64) { diff --git a/tmva/sofie/test/input_models/Add.onnx b/tmva/sofie/test/input_models/Add.onnx new file mode 100644 index 0000000000000..7bf6428f689af --- /dev/null +++ b/tmva/sofie/test/input_models/Add.onnx @@ -0,0 +1,16 @@ +pytorch1.11.0: +) + onnx::Add_0 + onnx::Add_12Add_0"Addtorch-jit-exportZ + onnx::Add_0 +  + +Z + onnx::Add_1 +  + +b +2 +  + +B \ No newline at end of file diff --git a/tmva/sofie/test/input_models/Div.onnx b/tmva/sofie/test/input_models/Div.onnx new file mode 100644 index 0000000000000..c76721b368f48 --- /dev/null +++ b/tmva/sofie/test/input_models/Div.onnx @@ -0,0 +1,16 @@ +pytorch1.11.0:„ +) + onnx::Div_0 + onnx::Div_12Div_0"Divtorch-jit-exportZ + onnx::Div_0 + + +Z + onnx::Div_1 + + +b +2 + + +B \ No newline at end of file diff --git a/tmva/sofie/test/input_models/Mul.onnx b/tmva/sofie/test/input_models/Mul.onnx new file mode 100644 index 0000000000000..2f68a047d5762 --- /dev/null +++ b/tmva/sofie/test/input_models/Mul.onnx @@ -0,0 +1,16 @@ +pytorch1.11.0:„ +) + onnx::Mul_0 + onnx::Mul_12Mul_0"Multorch-jit-exportZ + onnx::Mul_0 + + +Z + onnx::Mul_1 + + +b +2 + + +B \ No newline at end of file diff --git a/tmva/sofie/test/input_models/Sub.onnx b/tmva/sofie/test/input_models/Sub.onnx new file mode 100644 index 0000000000000..3b86b93723c14 --- /dev/null +++ b/tmva/sofie/test/input_models/Sub.onnx @@ -0,0 +1,16 @@ +pytorch1.11.0:„ +) + onnx::Sub_0 + onnx::Sub_12Sub_0"Subtorch-jit-exportZ + onnx::Sub_0 + + +Z + onnx::Sub_1 + + +b +2 + + +B \ No newline at end of file diff --git a/tmva/sofie/test/input_models/references/Add.ref.hxx b/tmva/sofie/test/input_models/references/Add.ref.hxx new file mode 100644 index 0000000000000..6bf222f3a18ae --- /dev/null +++ b/tmva/sofie/test/input_models/references/Add.ref.hxx @@ -0,0 +1,7 @@ +namespace Add_ExpectedOutput{ + float outputs[] = { + 10, 20, 30, 40, + 30, 40, 50, 60, + 50, 60, 70, 80 + }; +} // namespace Add_ExpectedOutput \ No newline at end of file diff --git a/tmva/sofie/test/input_models/references/Div.ref.hxx b/tmva/sofie/test/input_models/references/Div.ref.hxx new file mode 100644 index 0000000000000..ad2859637cfb2 --- /dev/null +++ b/tmva/sofie/test/input_models/references/Div.ref.hxx @@ -0,0 +1,5 @@ +namespace Div_ExpectedOutput{ + float outputs[] = { + 2, 1 + }; +} // namespace Div_ExpectedOutput \ No newline at end of file diff --git a/tmva/sofie/test/input_models/references/Mul.ref.hxx b/tmva/sofie/test/input_models/references/Mul.ref.hxx new file mode 100644 index 0000000000000..465bf1b57bf7e --- /dev/null +++ b/tmva/sofie/test/input_models/references/Mul.ref.hxx @@ -0,0 +1,5 @@ +namespace Mul_ExpectedOutput{ + float outputs[] = { + 0, 2 + }; +} // namespace Mul_ExpectedOutput \ No newline at end of file diff --git a/tmva/sofie/test/input_models/references/Sub.ref.hxx b/tmva/sofie/test/input_models/references/Sub.ref.hxx new file mode 100644 index 0000000000000..bd2ac99126068 --- /dev/null +++ b/tmva/sofie/test/input_models/references/Sub.ref.hxx @@ -0,0 +1,5 @@ +namespace Sub_ExpectedOutput{ + float outputs[] = { + 1, 1 + }; +} // namespace Sub_ExpectedOutput \ No newline at end of file diff --git a/tmva/sofie_parsers/inc/TMVA/RModelParser_ONNX.hxx b/tmva/sofie_parsers/inc/TMVA/RModelParser_ONNX.hxx index c8acaa412a185..dd3a9fd01b21b 100644 --- a/tmva/sofie_parsers/inc/TMVA/RModelParser_ONNX.hxx +++ b/tmva/sofie_parsers/inc/TMVA/RModelParser_ONNX.hxx @@ -23,6 +23,7 @@ namespace Experimental{ namespace SOFIE{ namespace INTERNAL{ +// enum EBasicBinaryOperator { Add, Sub, Mul, Div }; std::unique_ptr make_ROperator_Transpose(const onnx::NodeProto& nodeproto, const onnx::GraphProto& graphproto, std::unordered_map& tensor_type); std::unique_ptr make_ROperator_Relu(const onnx::NodeProto& nodeproto, const onnx::GraphProto& graphproto, std::unordered_map& tensor_type); @@ -35,7 +36,7 @@ std::unique_ptr make_ROperator_RNN(const onnx::NodeProto& nodeproto, std::unique_ptr make_ROperator_LSTM(const onnx::NodeProto& nodeproto, const onnx::GraphProto& graphproto, std::unordered_map& tensor_type); std::unique_ptr make_ROperator_BatchNormalization(const onnx::NodeProto& nodeproto, const onnx::GraphProto& graphproto, std::unordered_map& tensor_type); std::unique_ptr make_ROperator_Pool(const onnx::NodeProto& nodeproto, const onnx::GraphProto& graphproto, std::unordered_map& tensor_type); -std::unique_ptr make_ROperator_Add(const onnx::NodeProto &nodeproto, const onnx::GraphProto &graphproto, std::unordered_map &tensor_type); +template std::unique_ptr make_ROperator_BasicBinary(const onnx::NodeProto &nodeproto, const onnx::GraphProto &graphproto, std::unordered_map &tensor_type); std::unique_ptr make_ROperator_Reshape(const onnx::NodeProto &nodeproto, const onnx::GraphProto &graphproto, std::unordered_map &tensor_type); std::unique_ptr make_ROperator_Slice(const onnx::NodeProto &nodeproto, const onnx::GraphProto &graphproto, std::unordered_map &tensor_type); std::unique_ptr make_ROperator_GRU(const onnx::NodeProto& nodeproto, const onnx::GraphProto& graphproto, std::unordered_map& tensor_type); @@ -57,7 +58,10 @@ const factoryMethodMap mapOptypeOperator = { {"AveragePool", &make_ROperator_Pool}, {"GlobalAveragePool", &make_ROperator_Pool}, {"MaxPool", &make_ROperator_Pool}, - {"Add", &make_ROperator_Add}, + {"Add", &make_ROperator_BasicBinary}, + {"Sub", &make_ROperator_BasicBinary}, + {"Mul", &make_ROperator_BasicBinary}, + {"Div", &make_ROperator_BasicBinary
}, {"Reshape", &make_ROperator_Reshape}, {"Flatten", &make_ROperator_Reshape}, {"Slice", &make_ROperator_Slice}, diff --git a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx index 92f06c265beaa..91e27ecc64ec6 100644 --- a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx +++ b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx @@ -23,8 +23,9 @@ std::unique_ptr make_ROperator(size_t idx, const onnx::GraphProto& gr return (find->second)(nodeproto, graphproto, tensor_type); } } - -std::unique_ptr make_ROperator_Add(const onnx::NodeProto& nodeproto, const onnx::GraphProto& /*graphproto */, std::unordered_map& tensor_type){ +// enum EBasicBinaryOperator { Add, Sub, Mul, Div }; +template +std::unique_ptr make_ROperator_BasicBinary(const onnx::NodeProto& nodeproto, const onnx::GraphProto& graphproto, std::unordered_map& tensor_type){ ETensorType input_type = ETensorType::UNDEFINED; @@ -37,7 +38,16 @@ std::unique_ptr make_ROperator_Add(const onnx::NodeProto& nodeproto, else assert(it->second == input_type); } else { - throw std::runtime_error("TMVA::SOFIE ONNX Parser Add op has input tensor" + input_name + " but its type is not yet registered"); + // check if input tensor is an initialized tensor + bool isInitializer = false; + for (int j=0; j < graphproto.initializer_size(); j++){ + if (input_name == graphproto.initializer(j).name()) { + isInitializer = true; + break; + } + } + if (!isInitializer) + throw std::runtime_error("TMVA::SOFIE ONNX Parser Binary op has input tensor " + input_name + " but its type is not yet registered"); } } @@ -45,10 +55,10 @@ std::unique_ptr make_ROperator_Add(const onnx::NodeProto& nodeproto, switch(input_type){ case ETensorType::FLOAT: - op.reset(new ROperator_Add(nodeproto.input(0), nodeproto.input(1), nodeproto.output(0))); + op.reset(new ROperator_BasicBinary(nodeproto.input(0), nodeproto.input(1), nodeproto.output(0))); break; default: - throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Add does not yet support input type " + std::to_string(static_cast(input_type))); + throw std::runtime_error("TMVA::SOFIE - Unsupported - Binary Operator does not yet support input type " + std::to_string(static_cast(input_type))); } ETensorType output_type = (op->TypeInference({input_type}))[0]; @@ -59,6 +69,7 @@ std::unique_ptr make_ROperator_Add(const onnx::NodeProto& nodeproto, return op; } + std::unique_ptr make_ROperator_Transpose(const onnx::NodeProto& nodeproto, const onnx::GraphProto& /*graphproto*/, std::unordered_map& tensor_type){ ETensorType input_type; @@ -149,9 +160,9 @@ std::unique_ptr make_ROperator_LeakyRelu(const onnx::NodeProto& nodep for (int_t i = 0; i < nodeproto.attribute_size(); i++) { std::string attribute_name = nodeproto.attribute(i).name(); - if (attribute_name == "alpha") + if (attribute_name == "alpha") attr_alpha = nodeproto.attribute(i).f(); - } + } switch(input_type){ case ETensorType::FLOAT: op.reset(new ROperator_LeakyRelu(attr_alpha,nodeproto.input(0), nodeproto.output(0))); @@ -474,7 +485,7 @@ std::unique_ptr make_ROperator_Pool(const onnx::NodeProto& nodeproto, RAttributes_Pool attr; // std::string attr_auto_pad = "NOTSET"; // int attr_ceil_mode = 0; - // int attr_count_include_pad = 0; + // int attr_count_include_pad = 0; // int attr_storage_order = 0; // not for AveragePool // std::vector attr_dilations; // not for AveragePool // std::vector attr_kernel_shape; @@ -530,22 +541,22 @@ std::unique_ptr make_ROperator_Reshape(const onnx::NodeProto &nodepro // make Reshape operator ETensorType input_type = ETensorType::UNDEFINED; - + ReshapeOpMode opMode = Reshape; - if (nodeproto.op_type() == "Flatten") + if (nodeproto.op_type() == "Flatten") opMode = Flatten; - else if (nodeproto.op_type() == "Squeeze") + else if (nodeproto.op_type() == "Squeeze") opMode = Squeeze; else if (nodeproto.op_type() == "Unsqueeze") opMode = Unsqueeze; - + //bool hasShapeInput = (opMode == Reshape) ? true : false; - // reshape has as extra input shape tensor (int64) but + // reshape has as extra input shape tensor (int64) but // it is not present for Flatten, Squeeze and Unsquueze auto input_name = nodeproto.input(0); - // for squeeze is optional ? + // for squeeze is optional ? auto shape_name = (opMode == Reshape || opMode == Unsqueeze) ? nodeproto.input(1) : ""; auto it = tensor_type.find(input_name); if (it != tensor_type.end()) { @@ -559,7 +570,7 @@ std::unique_ptr make_ROperator_Reshape(const onnx::NodeProto &nodepro // Flatten is having one attribute: axis (int) (default=1) // old version of reshape and squeeze have axes as attributes std::unique_ptr op; - int attr_value = (opMode == Reshape) ? 0 : 1; + int attr_value = (opMode == Reshape) ? 0 : 1; if (opMode == Reshape && nodeproto.attribute_size() > 0 ) attr_value = nodeproto.attribute(0).i();