From 9c9dd83efecbd0f0291ed87106b2daa5ba35abe6 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Tue, 21 Jun 2022 21:32:11 +0530 Subject: [PATCH 1/6] Added all 4 Basic Binary Operators:- Add,Sub,Mul and Div. Added the tests for all 4 Binary Operators --- tmva/sofie/CMakeLists.txt | 2 +- tmva/sofie/inc/TMVA/OperatorList.hxx | 2 +- ...ator_Add.hxx => ROperator_BasicBinary.hxx} | 64 +++++++--- tmva/sofie/test/TestCustomModelsFromONNX.cxx | 116 ++++++++++++++++++ tmva/sofie/test/input_models/Add.onnx | 16 +++ tmva/sofie/test/input_models/Div.onnx | 16 +++ tmva/sofie/test/input_models/Mul.onnx | 16 +++ tmva/sofie/test/input_models/Sub.onnx | 16 +++ .../test/input_models/references/Add.ref.hxx | 5 + .../test/input_models/references/Div.ref.hxx | 5 + .../test/input_models/references/Mul.ref.hxx | 5 + .../test/input_models/references/Sub.ref.hxx | 5 + .../inc/TMVA/RModelParser_ONNX.hxx | 8 +- tmva/sofie_parsers/src/RModelParser_ONNX.cxx | 12 +- 14 files changed, 264 insertions(+), 24 deletions(-) rename tmva/sofie/inc/TMVA/{ROperator_Add.hxx => ROperator_BasicBinary.hxx} (51%) create mode 100644 tmva/sofie/test/input_models/Add.onnx create mode 100644 tmva/sofie/test/input_models/Div.onnx create mode 100644 tmva/sofie/test/input_models/Mul.onnx create mode 100644 tmva/sofie/test/input_models/Sub.onnx create mode 100644 tmva/sofie/test/input_models/references/Add.ref.hxx create mode 100644 tmva/sofie/test/input_models/references/Div.ref.hxx create mode 100644 tmva/sofie/test/input_models/references/Mul.ref.hxx create mode 100644 tmva/sofie/test/input_models/references/Sub.ref.hxx 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_BasicBinary.hxx similarity index 51% rename from tmva/sofie/inc/TMVA/ROperator_Add.hxx rename to tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx index c3d06222ba6b2..d3810cfdac188 100644 --- a/tmva/sofie/inc/TMVA/ROperator_Add.hxx +++ b/tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx @@ -1,5 +1,5 @@ -#ifndef TMVA_SOFIE_ROPERATOR_ADD -#define TMVA_SOFIE_ROPERATOR_ADD +#ifndef TMVA_SOFIE_ROperator_BasicBinary +#define TMVA_SOFIE_ROperator_BasicBinary #include "TMVA/SOFIE_common.hxx" #include "TMVA/ROperator.hxx" @@ -11,10 +11,39 @@ 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 -class ROperator_Add final : public ROperator -{ +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; @@ -22,12 +51,15 @@ private: std::string fNY; std::vector fShape; + // template + // BinaryOperatorTrait *s; + public: - ROperator_Add(){} - ROperator_Add(std::string nameX1, std::string nameX2, std::string nameY): + 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 + // type of output given input std::vector TypeInference(std::vector input){ return input; } @@ -42,16 +74,16 @@ public: 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"); + 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 Add Op Input Tensor ") + fNX1 + "is not found in model"); + 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 + // 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 " + + std::string msg = "TMVA SOFIE Binary Op: Support only inputs with same shape, shape 1 is " + ConvertShapeToString(shapeX1) + "shape 2 is " + ConvertShapeToString(shapeX2); throw std::runtime_error(msg); } @@ -62,8 +94,9 @@ public: 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"); + throw std::runtime_error("TMVA SOFIE Binary Op called to Generate without being initialized first"); } std::stringstream out; // int length = 1; @@ -71,9 +104,10 @@ public: // length *= i; // } size_t length = ConvertShapeToLength(fShape); - out << "\n//------ Add\n"; + 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] + tensor_" << fNX2 << "[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(); } @@ -85,4 +119,4 @@ public: }//TMVA -#endif //TMVA_SOFIE_ROPERATOR_Add +#endif //TMVA_SOFIE_ROperator_BasicBinary \ No newline at end of file diff --git a/tmva/sofie/test/TestCustomModelsFromONNX.cxx b/tmva/sofie/test/TestCustomModelsFromONNX.cxx index 57da92f9d9ee7..9a12764f210e1 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,110 @@ 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({ + 1, 2 + }); + std::vector input2({ + 0, 1 + }); + 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..c1773f247ae7e --- /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..9822c7a81c9a8 --- /dev/null +++ b/tmva/sofie/test/input_models/references/Add.ref.hxx @@ -0,0 +1,5 @@ +namespace Add_ExpectedOutput{ + float outputs[] = { + 1, 3 + }; +} // 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..8ad138f560a3e 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,7 @@ 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"); + throw std::runtime_error("TMVA::SOFIE ONNX Parser Binary op has input tensor" + input_name + " but its type is not yet registered"); } } @@ -45,10 +46,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 +60,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; From 573f8393af9891b5531d0976205c80fa4a37a3cc Mon Sep 17 00:00:00 2001 From: moneta Date: Fri, 24 Jun 2022 13:19:52 +0200 Subject: [PATCH 2/6] Fix issue in parsing binary operators when one input is an initialized tensor In Add,Sub, Mul or Div one of the input can be an initialized tensor therefore we don;t have its input type registered before parsing. We need to look if the tensor is in Initilizer tensor list --- tmva/sofie_parsers/src/RModelParser_ONNX.cxx | 33 +++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx index 8ad138f560a3e..d0d3d22a0a416 100644 --- a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx +++ b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx @@ -25,7 +25,7 @@ std::unique_ptr make_ROperator(size_t idx, const onnx::GraphProto& gr } // 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){ +std::unique_ptr make_ROperator_BasicBinary(const onnx::NodeProto& nodeproto, const onnx::GraphProto& graphproto, std::unordered_map& tensor_type){ ETensorType input_type = ETensorType::UNDEFINED; @@ -38,7 +38,16 @@ std::unique_ptr make_ROperator_BasicBinary(const onnx::NodeProto& nod else assert(it->second == input_type); } else { - throw std::runtime_error("TMVA::SOFIE ONNX Parser Binary 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 i=0; i < graphproto.initializer_size(); i++){ + if (input_name == graphproto.initializer(i).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"); } } @@ -151,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))); @@ -476,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; @@ -532,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()) { @@ -561,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(); From a2150e582e6e2b2aca6d1f3cb7f6f37eb1157011 Mon Sep 17 00:00:00 2001 From: Neel-Shah-29 Date: Sat, 25 Jun 2022 15:00:47 +0530 Subject: [PATCH 3/6] Implemented the Multi-directional Broadcasting for SOFIE --- tmva/sofie/inc/TMVA/SOFIE_common.hxx | 2 ++ tmva/sofie/src/SOFIE_common.cxx | 53 +++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tmva/sofie/inc/TMVA/SOFIE_common.hxx b/tmva/sofie/inc/TMVA/SOFIE_common.hxx index 7876727eef0ae..6208bda197f58 100644 --- a/tmva/sofie/inc/TMVA/SOFIE_common.hxx +++ b/tmva/sofie/inc/TMVA/SOFIE_common.hxx @@ -105,6 +105,8 @@ ETensorType GetTemplatedType(T /*obj*/ ){ namespace UTILITY{ template T* Unidirectional_broadcast(const T* original_data, const std::vector original_shape, const std::vector target_shape); +template +std::vector Multidirectional_broadcast(const T* original_data, std::vector input1_shape, 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..ea94424c38f10 100644 --- a/tmva/sofie/src/SOFIE_common.cxx +++ b/tmva/sofie/src/SOFIE_common.cxx @@ -135,6 +135,56 @@ T* UTILITY::Unidirectional_broadcast(const T* original_data, const std::vector +std::vector UTILITY::Multidirectional_broadcast(const T* original_data, std::vector input1_shape, std::vector input2_shape) +{ + std::vector input_shape = (input1_shape > input2_shape)?input1_shape:input2_shape; + std::vector output_shape(input_shape); + + //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){ + return output_shape; + } + else{ + //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; + } + } + else 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); + } + if(input1_shape.size()==input1_shape.size()){ + UTILITY::Multidirectional_broadcast(original_data,input1_shape,input2_shape); + } + } + 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); + } + if(input1_shape.size()==input1_shape.size()){ + UTILITY::Multidirectional_broadcast(original_data,input1_shape,input2_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()); @@ -142,7 +192,8 @@ std::string UTILITY::Clean_name(std::string input_tensor_name){ } template float* UTILITY::Unidirectional_broadcast(const float* original_data, const std::vector original_shape, const std::vector target_shape); +template std::vector UTILITY::Multidirectional_broadcast(const float* original_data, std::vector input1_shape, std::vector input2_shape); }//SOFIE }//Experimental -}//TMVA +}//TMVA \ No newline at end of file From 3f05469c45692d5751983d4f00cabf23def0b7e9 Mon Sep 17 00:00:00 2001 From: Neel-Shah-29 Date: Mon, 27 Jun 2022 18:09:53 +0530 Subject: [PATCH 4/6] Fix the warning related to Multi-directional Broadcasting --- tmva/sofie/inc/TMVA/SOFIE_common.hxx | 4 +- tmva/sofie/src/SOFIE_common.cxx | 52 ++++++++++---------- tmva/sofie_parsers/src/RModelParser_ONNX.cxx | 4 +- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/tmva/sofie/inc/TMVA/SOFIE_common.hxx b/tmva/sofie/inc/TMVA/SOFIE_common.hxx index 6208bda197f58..628ed9c237226 100644 --- a/tmva/sofie/inc/TMVA/SOFIE_common.hxx +++ b/tmva/sofie/inc/TMVA/SOFIE_common.hxx @@ -105,8 +105,8 @@ ETensorType GetTemplatedType(T /*obj*/ ){ namespace UTILITY{ template T* Unidirectional_broadcast(const T* original_data, const std::vector original_shape, const std::vector target_shape); -template -std::vector Multidirectional_broadcast(const T* original_data, std::vector input1_shape, std::vector input2_shape); + +std::vector Multidirectional_broadcast(std::vector input1_shape, 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 ea94424c38f10..65208383917e4 100644 --- a/tmva/sofie/src/SOFIE_common.cxx +++ b/tmva/sofie/src/SOFIE_common.cxx @@ -135,41 +135,21 @@ T* UTILITY::Unidirectional_broadcast(const T* original_data, const std::vector -std::vector UTILITY::Multidirectional_broadcast(const T* original_data, std::vector input1_shape, std::vector input2_shape) + + +std::vector UTILITY::Multidirectional_broadcast(std::vector input1_shape, std::vector input2_shape) { std::vector input_shape = (input1_shape > input2_shape)?input1_shape:input2_shape; std::vector output_shape(input_shape); - //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){ - return output_shape; - } - else{ - //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; - } - } - else if(input1_shape.size() < input2_shape.size()){ + 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); } if(input1_shape.size()==input1_shape.size()){ - UTILITY::Multidirectional_broadcast(original_data,input1_shape,input2_shape); + UTILITY::Multidirectional_broadcast(input1_shape,input2_shape); } } else if(input2_shape.size() < input1_shape.size()){ @@ -179,9 +159,28 @@ std::vector UTILITY::Multidirectional_broadcast(const T* original_data, it = input2_shape.insert(it, 1); } if(input1_shape.size()==input1_shape.size()){ - UTILITY::Multidirectional_broadcast(original_data,input1_shape,input2_shape); + UTILITY::Multidirectional_broadcast(input1_shape,input2_shape); } } + //check if both the input have same shape, nothing to do directly return the output_shape as the same shape. + else 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; } @@ -192,7 +191,6 @@ std::string UTILITY::Clean_name(std::string input_tensor_name){ } template float* UTILITY::Unidirectional_broadcast(const float* original_data, const std::vector original_shape, const std::vector target_shape); -template std::vector UTILITY::Multidirectional_broadcast(const float* original_data, std::vector input1_shape, std::vector input2_shape); }//SOFIE }//Experimental diff --git a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx index d0d3d22a0a416..91e27ecc64ec6 100644 --- a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx +++ b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx @@ -40,8 +40,8 @@ std::unique_ptr make_ROperator_BasicBinary(const onnx::NodeProto& nod } else { // check if input tensor is an initialized tensor bool isInitializer = false; - for (int i=0; i < graphproto.initializer_size(); i++){ - if (input_name == graphproto.initializer(i).name()) { + for (int j=0; j < graphproto.initializer_size(); j++){ + if (input_name == graphproto.initializer(j).name()) { isInitializer = true; break; } From 7871f77ca085be0c79f94ee1c466dc4051d36556 Mon Sep 17 00:00:00 2001 From: Neel-Shah-29 Date: Tue, 28 Jun 2022 01:18:02 +0530 Subject: [PATCH 5/6] Fixing some test cases regarding Multi directional Broadcasting --- tmva/sofie/inc/TMVA/SOFIE_common.hxx | 3 +-- tmva/sofie/src/SOFIE_common.cxx | 12 +++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/tmva/sofie/inc/TMVA/SOFIE_common.hxx b/tmva/sofie/inc/TMVA/SOFIE_common.hxx index 628ed9c237226..6f72b27104c5a 100644 --- a/tmva/sofie/inc/TMVA/SOFIE_common.hxx +++ b/tmva/sofie/inc/TMVA/SOFIE_common.hxx @@ -105,8 +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(std::vector input1_shape, std::vector input2_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 65208383917e4..3df8032c6b274 100644 --- a/tmva/sofie/src/SOFIE_common.cxx +++ b/tmva/sofie/src/SOFIE_common.cxx @@ -137,9 +137,9 @@ T* UTILITY::Unidirectional_broadcast(const T* original_data, const std::vector UTILITY::Multidirectional_broadcast(std::vector input1_shape, std::vector input2_shape) +std::vector UTILITY::Multidirectional_broadcast(std::vector input1_shape, std::vector input2_shape) { - std::vector input_shape = (input1_shape > input2_shape)?input1_shape: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()){ @@ -148,9 +148,6 @@ std::vector UTILITY::Multidirectional_broadcast(std::vector inp while (input1_shape.size() < input2_shape.size()) { it = input1_shape.insert(it, 1); } - if(input1_shape.size()==input1_shape.size()){ - UTILITY::Multidirectional_broadcast(input1_shape,input2_shape); - } } 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() @@ -158,12 +155,9 @@ std::vector UTILITY::Multidirectional_broadcast(std::vector inp while (input2_shape.size() < input1_shape.size()) { it = input2_shape.insert(it, 1); } - if(input1_shape.size()==input1_shape.size()){ - UTILITY::Multidirectional_broadcast(input1_shape,input2_shape); - } } //check if both the input have same shape, nothing to do directly return the output_shape as the same shape. - else if(input1_shape.size() == input2_shape.size()){ + 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++){ From d5e37c2ab8c7d1d9f19a50ea4b538451fc9d6e19 Mon Sep 17 00:00:00 2001 From: Neel-Shah-29 Date: Tue, 28 Jun 2022 17:49:19 +0530 Subject: [PATCH 6/6] Add_broadcast test added --- tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx | 8 +++--- tmva/sofie/test/TestCustomModelsFromONNX.cxx | 6 ++-- tmva/sofie/test/input_models/Add.onnx | 28 +++++++++---------- .../test/input_models/references/Add.ref.hxx | 4 ++- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx b/tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx index d3810cfdac188..a2d0632edcdf2 100644 --- a/tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx +++ b/tmva/sofie/inc/TMVA/ROperator_BasicBinary.hxx @@ -83,11 +83,11 @@ public: auto shapeX2 = model.GetTensorShape(fNX2); // assume same shape X1 and X2 if (shapeX1 != shapeX2) { - std::string msg = "TMVA SOFIE Binary Op: Support only inputs with same shape, shape 1 is " + - ConvertShapeToString(shapeX1) + "shape 2 is " + ConvertShapeToString(shapeX2); - throw std::runtime_error(msg); + fShape = UTILITY::Multidirectional_broadcast(shapeX1,shapeX2); } - fShape = shapeX1; + else if(shapeX1 == shapeX2){ + fShape = shapeX1; + } model.AddIntermediateTensor(fNY, model.GetTensorType(fNX1), fShape); } diff --git a/tmva/sofie/test/TestCustomModelsFromONNX.cxx b/tmva/sofie/test/TestCustomModelsFromONNX.cxx index 9a12764f210e1..fdb5b8c129919 100644 --- a/tmva/sofie/test/TestCustomModelsFromONNX.cxx +++ b/tmva/sofie/test/TestCustomModelsFromONNX.cxx @@ -179,10 +179,12 @@ TEST(ONNX, Add) // Preparing the standard input std::vector input1({ - 1, 2 + 5, 10, 15, 20, + 25, 30, 35, 40, + 45, 50, 55, 60 }); std::vector input2({ - 0, 1 + 5, 10, 15, 20 }); TMVA_SOFIE_Add::Session s("Add_FromONNX.dat"); diff --git a/tmva/sofie/test/input_models/Add.onnx b/tmva/sofie/test/input_models/Add.onnx index c1773f247ae7e..7bf6428f689af 100644 --- a/tmva/sofie/test/input_models/Add.onnx +++ b/tmva/sofie/test/input_models/Add.onnx @@ -1,16 +1,16 @@ -pytorch1.11.0: +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 + 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/references/Add.ref.hxx b/tmva/sofie/test/input_models/references/Add.ref.hxx index 9822c7a81c9a8..6bf222f3a18ae 100644 --- a/tmva/sofie/test/input_models/references/Add.ref.hxx +++ b/tmva/sofie/test/input_models/references/Add.ref.hxx @@ -1,5 +1,7 @@ namespace Add_ExpectedOutput{ float outputs[] = { - 1, 3 + 10, 20, 30, 40, + 30, 40, 50, 60, + 50, 60, 70, 80 }; } // namespace Add_ExpectedOutput \ No newline at end of file