Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_ScatterND.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_ScatterND
2#define TMVA_SOFIE_ROPERATOR_ScatterND
3
5#include "TMVA/ROperator.hxx"
6#include "TMVA/RModel.hxx"
7
8#include <sstream>
9#include <stdexcept>
10#include <string>
11
12namespace TMVA{
13namespace Experimental{
14namespace SOFIE{
15
17{
18private:
19
20
21 std::string fNX;
22 std::string fNI;
23 std::string fNU;
24 std::string fNY;
25 std::string fReduction;
26
27 std::vector<Dim> fShapeX;
28 std::vector<Dim> fShapeI;
29 std::vector<Dim> fShapeY;
30
31
32 std::vector<int64_t> fIndices; // indices vector in case they are known at initialization
33
34 std::string fType;
35
36
37public:
39 ROperator_ScatterND(const std::string & nameX, const std::string & nameI, const std::string & nameU, const std::string & nameY,
40 std::string reduction):
41 fNX(UTILITY::Clean_name(nameX)), fNI(UTILITY::Clean_name(nameI)), fNU(UTILITY::Clean_name(nameU)),
42 fNY(UTILITY::Clean_name(nameY)), fReduction(reduction)
43 {
46 }
47
48 void Initialize(RModel& model) override {
49
50 // input must be a graph input, or already initialized intermediate tensor
51 if (!model.CheckIfTensorAlreadyExist(fNX)){
52 throw std::runtime_error(std::string("TMVA SOFIE ScatterND Op Input Tensor ") + fNX + "is not found in model");
53 }
54 if (!model.CheckIfTensorAlreadyExist(fNI)) {
55 throw std::runtime_error(std::string("TMVA SOFIE ScatterND Op Input Tensor ") + fNI + "is not found in model");
56 }
57 if (!model.CheckIfTensorAlreadyExist(fNU)) {
58 throw std::runtime_error(std::string("TMVA SOFIE ScatterND Op Input Tensor ") + fNU + "is not found in model");
59 }
60 //tbd check for constant tensors
61
64 auto shapeU = model.GetDimTensorShape(fNU);
65
66 // Validate inputs if fShapeI last is not dynamic
67
68 //if (!model.IsDynamicTensor(fNI)) {
69 const size_t r = fShapeX.size(); // rank of data
70 const size_t q = fShapeI.size(); // rank of indices
71 if (!(fShapeI.back().isParam) ) {
72 const size_t k = fShapeI.back().dim; // index depth
73
74 if (k > r)
75 throw std::invalid_argument(
76 "ScatterND: last dim of indices (" + std::to_string(k) +
77 ") must be <= rank of data (" + std::to_string(r) + ")");
78
79 // Expected updates rank = q - 1 + r - k
80 int64_t expected_updates_rank = q - 1 + r - k;
81 if ((int64_t) shapeU.size() != expected_updates_rank)
82 throw std::invalid_argument("ScatterND: updates rank mismatch");
83 } else {
84 // Assumption is that last dimension of index shape is known (is not dynamic)
85 throw std::runtime_error("TMVA SOFIE ScatterND : Index_shape(-1) is not known. This case is not supported");
86 }
87
88 // output shape is equal to input shape
90
92 if (model.Verbose()) {
93 std::cout << "ScatterElements: input: " << ConvertDimShapeToString(fShapeX)
94 << " indices " << ConvertDimShapeToString(fShapeI)
95 << " update " << ConvertDimShapeToString(shapeU);
96 std::cout << "\t----> " << ConvertDimShapeToString(fShapeY) << std::endl;
97 }
98 }
99
100 std::string Generate(std::string opName) override {
101 if (fIsOutputConstant) {
102 // no code to generate here for constant output. Tensor output is defined in Session constructor
103 return "//---------------------------------------\n";
104 }
105 opName = "op_" + opName;
106 std::stringstream out;
107 out << "//--------- ScatterND " << opName << " --> " << ConvertDimShapeToString(fShapeY) << "\n";
108
109 size_t r = fShapeX.size();
110
111 // Strides
115
116 // case input_index_shape == rank of input
117 size_t k = fShapeI.back().dim;
118
119 // Total number of index tuples = product of indices dims except last
120 std::vector<Dim> shapeIndFirst(fShapeI.begin(), fShapeI.begin()+ fShapeI.size()-1);
122
123 //slice size (is product of input from k to r)
124 std::vector<Dim> shapeSlice(fShapeX.begin()+k, fShapeX.end());
126
128
129 //step1: input->output
130 out << SP << "// Step 1: copy input data to output\n";
131 out << SP << "std::copy(tensor_" << fNX << ", tensor_" << fNX << " + " << data_length << ", tensor_" << fNY << ");\n";
132
133 // Step 2: Emit strides as a static constexpr array
134 out << SP << "// Step 2: data strides (row-major)\n";
135 //to do: use static constexpr for defined strides
136 out << SP << "size_t " << opName << "_data_strides[" << r << "] = {";
137 for (size_t i = 0; i < r; ++i)
138 out << stridesX[i] << (i + 1 < r ? ", " : "");
139 out << "};\n\n";
140
141 // Step 3: Scatter loop
142 out << SP << "// Step 3: scatter updates into output\n";
143 out << SP << "for (int64_t idx = 0; idx < " << num_index_tuples << "; idx++) {\n";
144
145 // Resolve flat data offset from k-dimensional index tuple
146 out << SP << SP << "int64_t data_offset = 0;\n";
147 for (size_t dim = 0; dim < k; ++dim) {
148 out << SP << SP << "{\n";
149 out << SP << SP << SP << "int64_t coord = tensor_" << fNI
150 << "[idx * " << k << " + " << dim << "];\n";
151 // Support negative indices
152 out << SP << SP << SP << "if (coord < 0) coord += " << fShapeX[dim] << ";\n";
153 out << SP << SP << SP << "data_offset += coord * "
154 << opName << "_data_strides[" << dim << "];\n";
155 out << SP << SP << "}\n";
156 }
157
158 // Apply updates with reduction
159 out << SP << SP << "for (int64_t s = 0; s < " << slice_size << "; s++) {\n";
160 out << SP << SP << SP << "auto upd = tensor_" << fNU
161 << "[idx * " << slice_size << " + s];\n";
162
163 if (fReduction.empty() || fReduction == "none") {
164 out << SP << SP << SP << "tensor_" << fNY << "[data_offset + s] = upd;\n";
165 } else if (fReduction == "add") {
166 out << SP << SP << SP << "tensor_" << fNY<< "[data_offset + s] += upd;\n";
167 } else if (fReduction == "mul") {
168 out << SP << SP << SP << "tensor_" << fNY << "[data_offset + s] *= upd;\n";
169 } else if (fReduction == "min") {
170 out << SP << SP << SP << "tensor_" << fNY<< "[data_offset + s] = "
171 << "std::min(tensor_" << fNY << "[data_offset + s], upd);\n";
172 } else if (fReduction == "max") {
173 out << SP << SP << SP << "tensor_" << fNY << "[data_offset + s] = "
174 << "std::max(tensor_" << fNY << "[data_offset + s], upd);\n";
175 } else {
176 throw std::runtime_error(
177 "TMVA SOFIE ScatterND: unsupported reduction '" + fReduction + "'");
178 }
179
180 out << SP << SP << "}\n"; // end slice loop
181 out << SP << "}\n"; // end index tuple loop
182
183 return out.str();
184 }
185
186};
187
188}//SOFIE
189}//Experimental
190}//TMVA
191
192
193#endif //TMVA_SOFIE_ROPERATOR_RELU
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
float * q
std::vector< Dim > GetDimTensorShape(const std::string &name) const
Definition RModel.cxx:87
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:284
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:144
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:112
std::string Generate(std::string opName) override
ROperator_ScatterND(const std::string &nameX, const std::string &nameI, const std::string &nameU, const std::string &nameY, std::string reduction)
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:47
bool fIsOutputConstant
flag to identify if operator has a constant output (no need to generate code)
Definition ROperator.hxx:44
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:42
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:48
std::vector< size_t > ComputeStrideFromShape(const std::vector< size_t > &shape)
compute stride of a tensor given its shape (assume layout is row-major)
std::string ConvertDimShapeToString(const std::vector< Dim > &shape)
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
create variable transformations