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 std::vector<Dim> fOutputShapeData;
30
31 std::vector<int64_t> fIndices; // indices vector in case they are known at initialization
32
33 std::string fType;
34
35
36public:
38 ROperator_Gather(int64_t attrAxis, std::string nameX, std::string nameIndices, std::string nameY):
39 fAttrAxis(attrAxis), fNX(UTILITY::Clean_name(nameX)), fNIndices(UTILITY::Clean_name(nameIndices)), fNY(UTILITY::Clean_name(nameY)) {
42 }
43
44 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override {
45 return input;
46 }
47
48 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> input) override {
49 auto ret = input;
50 return ret;
51 }
52
53 void Initialize(RModel& model) override {
54 if (!model.CheckIfTensorAlreadyExist(fNX)) {
55 throw std::runtime_error("TMVA SOFIE Gather Op Input Tensor " + fNX + " is not found in model");
56 }
58 if (model.Verbose())
59 std::cout << "Gather - initial shape " << ConvertDimShapeToString(fShapeX) << " shape of indices "
61 // fShapeIndices can be dynamic
63 size_t q = fShapeIndices.size();
64 // Axis in range [0, r) where r=rank(X)
65 size_t r = fShapeX.size();
66 // Set the axis
67 if (fAttrAxis < 0) {
68 fAttrAxis = fAttrAxis + int64_t(r);
69 }
70
71
72 // case indices tensor is initialized
73 if (model.IsInitializedTensor(fNIndices)) {
74 // empty shape Indices is a scalar value for the indices
75 bool hasNegativeIndex = false;
77 int64_t* data = static_cast<int64_t*>(model.GetInitializedTensorData(fNIndices).get());
78 // copy in a vector since we may need to update the values in case of negative indices
79 fIndices =std::vector<int64_t>(data, data + indicesLength);
80 // update indices data in case of negative dim values
81 for (size_t i = 0; i < indicesLength; i++) {
82 // move this at generation time?
83 if (!fShapeX[fAttrAxis].isParam) {
84 if (fIndices[i] < 0) {
85 hasNegativeIndex = true;
86 fIndices[i] += fShapeX[fAttrAxis].dim;
87 }
88 }
89 }
90 // for negative indices we need to add an extra constant tensor
91 if (hasNegativeIndex) {
92 std::string nameIndicesUpdated = fNIndices + "_updated";
95 }
96 }
97 // Output shape
98 if (model.Verbose())
99 std::cout << "Gather: q and r " << q << " " << r << " shape indices " << ConvertDimShapeToString(fShapeIndices) << std::endl;
100
101 if (fShapeY.empty()) {
102 fShapeY.resize(q + r - 1);
103 if (fAttrAxis > 0) {
104 // Copy shape of X[0, ..., axis-1) to Shape of Y[0, ..., axis-1)
105 std::copy(fShapeX.begin(), fShapeX.begin() + fAttrAxis, fShapeY.begin());
106 }
107 // Set shape of Y[axis, ..., axis + q)
108 for (size_t i = 0; i < q; i++) {
110 }
111 // Copy shape of X[axis + 1, ..., r) to shape of Y[axis + q, ... q + r - 1)
112 std::copy(fShapeX.begin() + fAttrAxis + 1, fShapeX.end(), fShapeY.begin() + fAttrAxis + q);
113 }
114 // case input is known (type is an integer) and input indices is a scalar (or vector of size 1)
115 if (model.IsInitializedTensor(fNX) && q <= 1 && r == 1 && fIndices.size() > 0) {
116 auto shapeX = ConvertShapeToInt(fShapeX); // we assume model is not dynamic
118 if (model.GetTensorType(fNX) == ETensorType::INT64) {
119 auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(fNX).get());
120 // if q <=1 and r = 1 output length = 1 (it is a scalar)
121 std::vector<int64_t> outputData(1); //ConvertShapeToLength(shapeY));
123 model.AddConstantTensor(fNY, shapeY, outputData.data());
124 if (model.Verbose())
125 std::cout << "Gather: " << fNX << " " << ConvertShapeToString(shapeX) << " -> " << fNY << " with shape " << ConvertShapeToString(shapeY)
126 << " and values " << ConvertValuesToString(outputData) << " (constant) " << std::endl;
127 fIsOutputConstant = true;
128 }
129 }
130 // case input is a shape tensor (r is == 1 by definition) and indices are known
131 else if (model.IsShapeTensor(fNX) && q <=1 && fIndices.size() > 0) {
132 auto inputData = model.GetShapeTensorValues(fNX);
133 // if r == 1 and q<=1 then output length is 1 (is a scalar or tensor of size1)
134 fOutputShapeData.resize(1);
136 if (fOutputShapeData[0].isParam) {
137 fIsOutputParamShape = true;
138 // shapeY can be scalar or vector of size1
139 model.AddShapeTensor(fNY, fOutputShapeData, fShapeY.size() == 0);
140 if (model.Verbose())
141 std::cout << "Gather: " << fNX << " " << ConvertDimShapeToString(fShapeX) << " -> " << fNY << " with shape " << ConvertDimShapeToString(fShapeY)
142 << " and values " << ConvertDimShapeToString(fOutputShapeData) << " (shape) " << std::endl;
143 } else {
144 int64_t value = static_cast<int64_t>(fOutputShapeData[0].dim);
147 fIsOutputConstant = true;
148 if (model.Verbose())
149 std::cout << "Gather: " << fNX << " " << ConvertDimShapeToString(fShapeX) << " -> " << fNY << " with shape " << ConvertDimShapeToString(fShapeY)
150 << " and values {" << value << "} (constant) " << std::endl;
151 }
152 }
154 // Add output tensor
157 if (model.Verbose())
158 std::cout << "Gather: input " << fNX << " " << ConvertDimShapeToString(fShapeX) << " indices " << fNIndices << ConvertDimShapeToString(fShapeIndices)
159 << " -> " << fNY << " with shape " << ConvertDimShapeToString(fShapeY) << std::endl;
160 }
161 }
162
163 std::string Generate(std::string opName) override {
164 opName = "op_" + opName;
165 std::stringstream out;
166 out << "//--------- Gather " << opName << " --> " << fNY << " " << ConvertDimShapeToString(fShapeY) << "\n";
167 if (fIsOutputConstant) {
168 // no code to generate here for constant output. Tensor output is defined in Session constructor
169 out << "//--------------------(constant)----------\n";
170 return out.str();
171 }
173 // no code to generate here for param shape output. Tensor output is defined in Session constructor
174 out << "//--------------------(shape)----------\n";
175 for (int i = 0; i < static_cast<int>(fOutputShapeData.size()); i++) {
176 out << SP << "tensor_" << fNY << "[" << i << " ] = " << fOutputShapeData[i].GetVal() << ";\n";
177 }
178 return out.str();
179 }
180 // The shape of the output is q + r - 1
181 size_t r = fShapeX.size();
182 // Indices of shape q
183 size_t q = fShapeIndices.size();
184 // Strides
188
189 // case fIndices is not known we need to correct for negative axis indices at run-time
190 if (fIndices.empty()) {
192 out << SP << "// correct in case of negative gather indices\n";
193 out << SP << "for (size_t i = 0; i < " << indicesLength << "; i++){\n";
194 out << SP << SP << "if (tensor_" << fNIndices << "[i] < 0)\n";
195 out << SP << SP << SP << "tensor_" << fNIndices << "[i] += " << fShapeX[fAttrAxis] << ";\n";
196 out << SP << "}\n";
197 }
198
199 // Fill the output Y[j_0, j_1, ..., j_{axis - 1}, i_0, i_1, ..., i_{q - 1}, j_{axis + 1}, ..., j_{r - 1}]
200 // [0 ... axis) [axis ... axis + q) [axis + q ... q + r - 1)
201 // iterate in [0 ... axis) [0 ... q) [axis ... r - 1)
202 // for j_0, j_1, ..., j_{axis-1}
203
204 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
205 std::string index = "j_" + std::to_string(j);
206 for (size_t k = 0; k <= j; k++) out << SP;
207 out << "for (size_t " << index << " = 0; " << index << " < " << fShapeY[j] << "; " << index << "++) {\n";
208 }
209 // for i_0, i_1, ..., i_{q - 1}
210 for (size_t i = 0; i < q; i++) {
211 std::string index = "i_" + std::to_string(i);
212 for (size_t k = 0; k <= i + fAttrAxis; k++) out << SP;
213 out << "for (size_t " << index << " = " << 0 << "; " << index << " < " << fShapeIndices[i] << "; " << index << "++) {\n";
214 }
215 // for j_axis, j_{axis + 1}, ..., j_{r - 1}
216 for (size_t j = fAttrAxis; j + 1 < r; j++) {
217 std::string index = "j_" + std::to_string(q+j); // annotate index using output axis
218 for (size_t k = 0; k <= q + j; k++) out << SP;
219 out << "for (size_t " << index << " = 0; " << index << " < " << fShapeY[q + j] << "; " << index << "++) {\n";
220 }
221
222 // add a scope for local variables in case above loop are not done
223 if (fAttrAxis == 0 && q == 0 && r <= 1)
224 out << SP << "{ // scalar case \n";
225
226 // output index
227 for (size_t k = 0; k < q + r; k++) out << SP;
228 out << "size_t y_index = ";
229 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
230 if (j > 0) out << " + ";
231 out << "j_" << j;
232 if (stridesY[j].dim != 1) out << " * " << stridesY[j];
233 }
234 for (size_t i = 0; i < q; i++) {
235 if (fAttrAxis + i > 0) out << " + ";
236 out << "i_" << i;
237 if (stridesY[fAttrAxis + i].dim != 1) out << " * " << stridesY[fAttrAxis + i];
238 }
239 for (size_t j = fAttrAxis; j + 1 < r; j++) {
240 if (j + q > 0) out << " + ";
241 out << "j_" << q+j;
242 if (stridesY[q+j].dim != 1) out << " * " << stridesY[q+j];
243 }
244 // empty case
245 if (fAttrAxis == 0 && q == 0 && r <= 1)
246 out << "0";
247 out << ";\n";
248
249 // input Indices
250 for (size_t k = 0; k < q + r; k++) out << SP;
251 out << "size_t i_index = ";
252 for (size_t i = 0; i < q; i++) {
253 if (i > 0) out << " + ";
254 out << "i_" << i;
255 if (stridesIndices[i].dim != 1) out << " * " << stridesIndices[i];
256 }
257 // empty case
258 if (q == 0)
259 out << "0";
260 out << ";\n";
261
262 // K
263 for (size_t k = 0; k < q + r; k++) out << SP;
264 out << "size_t k = static_cast<size_t>(" << "tensor_" << fNIndices << "[i_index]" << ");\n";
265 // Input
266 for (size_t k = 0; k < q + r; k++) out << SP;
267 out << "size_t x_index = k";
268 if (stridesX[fAttrAxis].dim != 1) out << " * " << stridesX[fAttrAxis];
269 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
270 out << " + ";
271 out << " j_" << j;
272 if (stridesX[j].dim != 1) out << " * " << stridesX[j];
273 }
274 // for input corresponding stride is axis+1,.... r
275 // loop is on j from fAttrAxis, so consider stridesX[j+1]
276 for (size_t j = fAttrAxis; j+1 < r; j++) {
277 out << " + ";
278 out << " j_" << q+j;
279 if (stridesX[j+1].dim != 1) out << " * " << stridesX[j+1];
280 }
281 out << ";\n";
282 for (size_t k = 0; k < q + r; k++) out << SP;
283 out << "tensor_" << fNY << "[y_index] = tensor_" << fNX << "[x_index];\n";
284
285 // end loops j_k, j_{k + 1}, ..., j_{r - 2}
286 for (size_t j = q+r-1; j > 0; j--) {
287 for (size_t k = 0; k <j; k++) out << SP;
288 out << "}\n";
289 }
290 // close empty scope if it was opened
291 if (q == 0 && fAttrAxis == 0 && r <= 1)
292 out << SP << "} // close Gather scope for scalar case \n";
293
294
295 return out.str();
296 }
297
298};
299
300}//SOFIE
301}//Experimental
302}//TMVA
303
304
305#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 data
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:64
std::vector< Dim > GetDimTensorShape(const std::string &name) const
Definition RModel.cxx:100
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:301
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:157
void AddConstantTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:232
bool IsShapeTensor(const std::string &name) const
check if a tensor is a shape tensor
Definition RModel.cxx:260
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:273
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:366
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:125
const std::vector< Dim > & GetShapeTensorValues(const std::string &tensor_name) const
Definition RModel.cxx:268
void AddShapeTensor(const std::string &name, const std::vector< Dim > &shapeValues, bool scalar=false)
Definition RModel.cxx:242
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:50
bool fIsOutputParamShape
flag to identify of the output represents a parametric shape (can be known at compile time)
Definition ROperator.hxx:48
bool fIsOutputConstant
flag to identify if operator has a constant output (no need to generate code)
Definition ROperator.hxx:47
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:45
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:51
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::size_t ConvertShapeToLength(const std::vector< size_t > &shape)
std::string ConvertValuesToString(size_t n, const T *data, size_t maxprint=-1)
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