Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Gather.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_GATHER
2#define TMVA_SOFIE_ROPERATOR_GATHER
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 int64_t fAttrAxis = 0;
21
22 std::string fNX;
23 std::string fNIndices;
24 std::string fNY;
25
26 std::vector<Dim> fShapeX;
27 std::vector<Dim> fShapeIndices;
28 std::vector<Dim> fShapeY;
29
30 std::vector<int64_t> fIndices; // indices vector in case they are known at initialization
31
32 std::string fType;
33
34public:
36 ROperator_Gather(int64_t attrAxis, std::string nameX, std::string nameIndices, std::string nameY):
37 fAttrAxis(attrAxis), fNX(UTILITY::Clean_name(nameX)), fNIndices(UTILITY::Clean_name(nameIndices)), fNY(UTILITY::Clean_name(nameY)) {
40 }
41
42 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override {
43 return input;
44 }
45
46 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> input) override {
47 auto ret = input;
48 return ret;
49 }
50
51 void Initialize(RModel& model) override {
52 if (!model.CheckIfTensorAlreadyExist(fNX)) {
53 throw std::runtime_error("TMVA SOFIE Gather Op Input Tensor " + fNX + " is not found in model");
54 }
56 if (model.Verbose())
57 std::cout << "Gather - initial shape " << ConvertShapeToString(fShapeX) << " shape of indices "
58 << ConvertShapeToString(model.GetDimTensorShape(fNIndices)) << std::endl;
59 // fShapeIndices can be dynamic
61 size_t q = fShapeIndices.size();
62 // Axis in range [0, r) where r=rank(X)
63 size_t r = fShapeX.size();
64 // Set the axis
65 if (fAttrAxis < 0) {
66 fAttrAxis = fAttrAxis + int64_t(r);
67 }
68
69
70 // case indices tensor is initialized
71 if (model.IsInitializedTensor(fNIndices)) {
72 // empty shape Indices is a scalar value for the indices
74 int64_t* indicesData = static_cast<int64_t*>(model.GetInitializedTensorData(fNIndices).get());
75 // update indices data in case of negative dim values
76 for (size_t i = 0; i < indicesLength; i++) {
77 // move this at generation time?
78 if (!fShapeX[fAttrAxis].isParam) {
79 if (indicesData[i] < 0) {
80 indicesData[i] += fShapeX[fAttrAxis].dim;
81 }
82 }
83 }
84 // Save in a vector gather Indices of size q
85 fIndices = std::vector<int64_t>(indicesData, indicesData + indicesLength);
86 }
87 // Output shape
88 if (model.Verbose())
89 std::cout << "Gather: q and r " << q << " " << r << " shape indices " << ConvertShapeToString(fShapeIndices) << std::endl;
90
91 if (fShapeY.empty()) {
92 fShapeY.resize(q + r - 1);
93 if (fAttrAxis > 0) {
94 // Copy shape of X[0, ..., axis-1) to Shape of Y[0, ..., axis-1)
95 std::copy(fShapeX.begin(), fShapeX.begin() + fAttrAxis, fShapeY.begin());
96 }
97 // Set shape of Y[axis, ..., axis + q)
98 for (size_t i = 0; i < q; i++) {
100 }
101 // Copy shape of X[axis + 1, ..., r) to shape of Y[axis + q, ... q + r - 1)
102 std::copy(fShapeX.begin() + fAttrAxis + 1, fShapeX.end(), fShapeY.begin() + fAttrAxis + q);
103 }
104 // case input is known (type is an integer) and input indices is a scalar (or vector of size 1)
105 if (model.IsInitializedTensor(fNX) && q <= 1 && r == 1 && fIndices.size() > 0) {
106 auto shapeX = ConvertShapeToInt(fShapeX); // we assume model is not dynamic
108 if (model.GetTensorType(fNX) == ETensorType::INT64) {
109 auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(fNX).get());
110 // if q <=1 and r = 1 output length = 1 (it is a scalar)
111 std::vector<int64_t> outputData(1); //ConvertShapeToLength(shapeY));
113 model.AddConstantTensor(fNY, shapeY, outputData.data());
114 if (model.Verbose())
115 std::cout << "Gather: " << fNX << " " << ConvertShapeToString(shapeX) << " -> " << fNY << " with shape " << ConvertShapeToString(shapeY)
116 << " and values " << ConvertValuesToString(outputData) << " (constant) " << std::endl;
117 fIsOutputConstant = true;
118 }
119 }
120 // case input is a shape tensor (r is == 1 by definition) and indices are known
121 else if (model.IsShapeTensor(fNX) && q <=1 && fIndices.size() > 0) {
122 auto inputData = model.GetShapeTensorValues(fNX);
123 // if r == 1 and q<=1 then output length is 1 (is a scalar or tensor of size1)
124 std::vector<Dim> outputData(1);
126 if (outputData[0].isParam) {
127 fIsOutputConstant = true;
128 // shapeY can be scalar or vector of size1
129 model.AddShapeTensor(fNY, outputData, fShapeY.size() == 0);
130 if (model.Verbose())
131 std::cout << "Gather: " << fNX << " " << ConvertShapeToString(fShapeX) << " -> " << fNY << " with shape " << ConvertShapeToString(fShapeY)
132 << " and values " << ConvertShapeToString(outputData) << " (shape) " << std::endl;
133 } else {
134 int64_t value = static_cast<int64_t>(outputData[0].dim);
137 fIsOutputConstant = true;
138 if (model.Verbose())
139 std::cout << "Gather: " << fNX << " " << ConvertShapeToString(fShapeX) << " -> " << fNY << " with shape " << ConvertShapeToString(fShapeY)
140 << " and values {" << value << "} (constant) " << std::endl;
141 }
142 }
143 if (!fIsOutputConstant) {
144 // Add output tensor
147 if (model.Verbose())
148 std::cout << "Gather: input " << fNX << " " << ConvertShapeToString(fShapeX) << " indices " << fNIndices << ConvertShapeToString(fShapeIndices)
149 << " -> " << fNY << " with shape " << ConvertShapeToString(fShapeY) << std::endl;
150 }
151 }
152
153 std::string Generate(std::string opName) override {
154 opName = "op_" + opName;
155 std::stringstream out;
156 out << "//--------- Gather " << opName << " --> " << fNY << " " << ConvertShapeToString(fShapeY) << "\n";
157 if (fIsOutputConstant) {
158 // no code to generate here for constant output. Tensor output is defined in Session constructor
159 out << "//--------------------(constant)----------\n";
160 return out.str();
161 }
162 // The shape of the output is q + r - 1
163 size_t r = fShapeX.size();
164 // Indices of shape q
165 size_t q = fShapeIndices.size();
166 // Strides
170
171 // case fIndices is not known we need to correct for negative axis indices at run-time
172 if (fIndices.empty()) {
174 out << SP << "// correct in case of negative gather indices\n";
175 out << SP << "for (size_t i = 0; i < " << indicesLength << "; i++){\n";
176 out << SP << SP << "if (tensor_" << fNIndices << "[i] < 0)\n";
177 out << SP << SP << SP << "tensor_" << fNIndices << "[i] += " << fShapeX[fAttrAxis] << ";\n";
178 out << SP << "}\n";
179 }
180
181 // Fill the output Y[j_0, j_1, ..., j_{axis - 1}, i_0, i_1, ..., i_{q - 1}, j_{axis + 1}, ..., j_{r - 1}]
182 // [0 ... axis) [axis ... axis + q) [axis + q ... q + r - 1)
183 // iterate in [0 ... axis) [0 ... q) [axis ... r - 1)
184 // for j_0, j_1, ..., j_{axis-1}
185
186 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
187 std::string index = "j_" + std::to_string(j);
188 for (size_t k = 0; k <= j; k++) out << SP;
189 out << "for (size_t " << index << " = 0; " << index << " < " << fShapeY[j] << "; " << index << "++) {\n";
190 }
191 // for i_0, i_1, ..., i_{q - 1}
192 for (size_t i = 0; i < q; i++) {
193 std::string index = "i_" + std::to_string(i);
194 for (size_t k = 0; k <= i + fAttrAxis; k++) out << SP;
195 out << "for (size_t " << index << " = " << 0 << "; " << index << " < " << fShapeIndices[i] << "; " << index << "++) {\n";
196 }
197 // for j_axis, j_{axis + 1}, ..., j_{r - 1}
198 for (size_t j = fAttrAxis; j + 1 < r; j++) {
199 std::string index = "j_" + std::to_string(q+j); // annotate index using output axis
200 for (size_t k = 0; k <= q + j; k++) out << SP;
201 out << "for (size_t " << index << " = 0; " << index << " < " << fShapeY[q + j] << "; " << index << "++) {\n";
202 }
203
204 // add a scope for local variables in case above loop are not done
205 if (fAttrAxis == 0 && q == 0 && r <= 1)
206 out << SP << "{ // scalar case \n";
207
208 // output index
209 for (size_t k = 0; k < q + r; k++) out << SP;
210 out << "size_t y_index = ";
211 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
212 if (j > 0) out << " + ";
213 out << "j_" << j;
214 if (stridesY[j].dim != 1) out << " * " << stridesY[j];
215 }
216 for (size_t i = 0; i < q; i++) {
217 if (fAttrAxis + i > 0) out << " + ";
218 out << "i_" << i;
219 if (stridesY[fAttrAxis + i].dim != 1) out << " * " << stridesY[fAttrAxis + i];
220 }
221 for (size_t j = fAttrAxis; j + 1 < r; j++) {
222 if (j + q > 0) out << " + ";
223 out << "j_" << q+j;
224 if (stridesY[q+j].dim != 1) out << " * " << stridesY[q+j];
225 }
226 // empty case
227 if (fAttrAxis == 0 && q == 0 && r <= 1)
228 out << "0";
229 out << ";\n";
230
231 // input Indices
232 for (size_t k = 0; k < q + r; k++) out << SP;
233 out << "size_t i_index = ";
234 for (size_t i = 0; i < q; i++) {
235 if (i > 0) out << " + ";
236 out << "i_" << i;
237 if (stridesIndices[i].dim != 1) out << " * " << stridesIndices[i];
238 }
239 // empty case
240 if (q == 0)
241 out << "0";
242 out << ";\n";
243
244 // K
245 for (size_t k = 0; k < q + r; k++) out << SP;
246 out << "size_t k = static_cast<size_t>(" << "tensor_" << fNIndices << "[i_index]" << ");\n";
247 // Input
248 for (size_t k = 0; k < q + r; k++) out << SP;
249 out << "size_t x_index = k";
250 if (stridesX[fAttrAxis].dim != 1) out << " * " << stridesX[fAttrAxis];
251 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
252 out << " + ";
253 out << " j_" << j;
254 if (stridesX[j].dim != 1) out << " * " << stridesX[j];
255 }
256 // for input corresponding stride is axis+1,.... r
257 // loop is on j from fAttrAxis, so consider stridesX[j+1]
258 for (size_t j = fAttrAxis; j+1 < r; j++) {
259 out << " + ";
260 out << " j_" << q+j;
261 if (stridesX[j+1].dim != 1) out << " * " << stridesX[j+1];
262 }
263 out << ";\n";
264 for (size_t k = 0; k < q + r; k++) out << SP;
265 out << "tensor_" << fNY << "[y_index] = tensor_" << fNX << "[x_index];\n";
266
267 // end loops j_k, j_{k + 1}, ..., j_{r - 2}
268 for (size_t j = q+r-1; j > 0; j--) {
269 for (size_t k = 0; k <j; k++) out << SP;
270 out << "}\n";
271 }
272 // close empty scope if it was opened
273 if (q == 0 && fAttrAxis == 0 && r <= 1)
274 out << SP << "} // close Gather scope for scalar case \n";
275
276
277 return out.str();
278 }
279
280};
281
282}//SOFIE
283}//Experimental
284}//TMVA
285
286
287#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 input
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
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
float * q
std::vector< size_t > GetTensorShape(const std::string &name) const
Definition RModel.cxx:29
std::vector< Dim > GetDimTensorShape(const std::string &name) const
Definition RModel.cxx:65
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:262
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:122
void AddConstantTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:193
bool IsShapeTensor(const std::string &name) const
check if a tensor is a shape tensor
Definition RModel.cxx:221
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:234
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:327
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:90
const std::vector< Dim > & GetShapeTensorValues(const std::string &tensor_name) const
Definition RModel.cxx:229
void AddShapeTensor(const std::string &name, const std::vector< Dim > &shapeValues, bool scalar=false)
Definition RModel.cxx:203
std::vector< std::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > input) override
std::string Generate(std::string opName) override
ROperator_Gather(int64_t attrAxis, std::string nameX, std::string nameIndices, std::string nameY)
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input) override
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::size_t ConvertShapeToLength(const std::vector< size_t > &shape)
std::string ConvertValuesToString(size_t n, const T *data)
std::vector< size_t > ConvertShapeToInt(const std::vector< Dim > &shape)
Convert shape based on Dim to integer format.
std::string ConvertTypeToString(ETensorType type)
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
create variable transformations