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 //flag index tensor as not writable (not sure this is needed since index tensor might be used in generated code)
77 // update indices data in case of negative dim values
78 for (size_t i = 0; i < indicesLength; i++) {
79 // move this at generation time?
80 if (!fShapeX[fAttrAxis].isParam) {
81 if (indicesData[i] < 0) {
82 indicesData[i] += fShapeX[fAttrAxis].dim;
83 }
84 }
85 }
86 // Save in a vector gather Indices of size q
87 fIndices = std::vector<int64_t>(indicesData, indicesData + indicesLength);
88 }
89 // Output shape
90 if (model.Verbose())
91 std::cout << "Gather: q and r " << q << " " << r << " shape indices " << ConvertShapeToString(fShapeIndices) << std::endl;
92
93 if (fShapeY.empty()) {
94 fShapeY.resize(q + r - 1);
95 if (fAttrAxis > 0) {
96 // Copy shape of X[0, ..., axis-1) to Shape of Y[0, ..., axis-1)
97 std::copy(fShapeX.begin(), fShapeX.begin() + fAttrAxis, fShapeY.begin());
98 }
99 // Set shape of Y[axis, ..., axis + q)
100 for (size_t i = 0; i < q; i++) {
102 }
103 // Copy shape of X[axis + 1, ..., r) to shape of Y[axis + q, ... q + r - 1)
104 std::copy(fShapeX.begin() + fAttrAxis + 1, fShapeX.end(), fShapeY.begin() + fAttrAxis + q);
105 }
106 // case input is known (type is an integer) and input indices is a scalar (or vector of size 1)
107 if (model.IsInitializedTensor(fNX) && q <= 1 && r == 1 && fIndices.size() > 0) {
108 auto shapeX = ConvertShapeToInt(fShapeX); // we assume model is not dynamic
110 if (model.GetTensorType(fNX) == ETensorType::INT64) {
111 auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(fNX).get());
112 // if q <=1 and r = 1 output length = 1 (it is a scalar)
113 std::vector<int64_t> outputData(1); //ConvertShapeToLength(shapeY));
115 model.AddConstantTensor(fNY, shapeY, outputData.data());
116 if (model.Verbose())
117 std::cout << "Gather: " << fNX << " " << ConvertShapeToString(shapeX) << " -> " << fNY << " with shape " << ConvertShapeToString(shapeY)
118 << " and values " << ConvertValuesToString(outputData) << " (constant) " << std::endl;
119 fIsOutputConstant = true;
120 }
121 }
122 // case input is a shape tensor (r is == 1 by definition) and indices are known
123 else if (model.IsShapeTensor(fNX) && q <=1 && fIndices.size() > 0) {
124 auto inputData = model.GetShapeTensorValues(fNX);
125 // if r == 1 and q<=1 then output length is 1 (is a scalar or tensor of size1)
126 std::vector<Dim> outputData(1);
128 if (outputData[0].isParam) {
129 fIsOutputConstant = true;
130 // shapeY can be scalar or vector of size1
131 model.AddShapeTensor(fNY, outputData, fShapeY.size() == 0);
132 if (model.Verbose())
133 std::cout << "Gather: " << fNX << " " << ConvertShapeToString(fShapeX) << " -> " << fNY << " with shape " << ConvertShapeToString(fShapeY)
134 << " and values " << ConvertShapeToString(outputData) << " (shape) " << std::endl;
135 } else {
136 int64_t value = static_cast<int64_t>(outputData[0].dim);
139 fIsOutputConstant = true;
140 if (model.Verbose())
141 std::cout << "Gather: " << fNX << " " << ConvertShapeToString(fShapeX) << " -> " << fNY << " with shape " << ConvertShapeToString(fShapeY)
142 << " and values {" << value << "} (constant) " << std::endl;
143 }
144 }
145 if (!fIsOutputConstant) {
146 // Add output tensor
149 if (model.Verbose())
150 std::cout << "Gather: input " << fNX << " " << ConvertShapeToString(fShapeX) << " indices " << fNIndices << ConvertShapeToString(fShapeIndices)
151 << " -> " << fNY << " with shape " << ConvertShapeToString(fShapeY) << std::endl;
152 }
153 }
154
155 std::string Generate(std::string opName) override {
156 if (fIsOutputConstant) {
157 // no code to generate here for constant output. Tensor output is defined in Session constructor
158 return "//---------------------------------------\n";
159 }
160 opName = "op_" + opName;
161 std::stringstream out;
162 out << "//--------- Gather " << opName << " --> " << ConvertShapeToString(fShapeY) << "\n";
163 // The shape of the output is q + r - 1
164 size_t r = fShapeX.size();
165 // Indices of shape q
166 size_t q = fShapeIndices.size();
167 // Strides
171
172 // case fIndices is not known we need to correct for negative axis indices at run-time
173 if (fIndices.empty()) {
175 out << SP << "// correct in case of negative gather indices\n";
176 out << SP << "for (size_t i = 0; i < " << indicesLength << "; i++){\n";
177 out << SP << SP << "if (tensor_" << fNIndices << "[i] < 0)\n";
178 out << SP << SP << SP << "tensor_" << fNIndices << "[i] += " << fShapeX[fAttrAxis] << ";\n";
179 out << SP << "}\n";
180 }
181
182 // Fill the output Y[j_0, j_1, ..., j_{axis - 1}, i_0, i_1, ..., i_{q - 1}, j_{axis + 1}, ..., j_{r - 1}]
183 // [0 ... axis) [axis ... axis + q) [axis + q ... q + r - 1)
184 // iterate in [0 ... axis) [0 ... q) [axis ... r - 1)
185 // for j_0, j_1, ..., j_{axis-1}
186
187 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
188 std::string index = "j_" + std::to_string(j);
189 for (size_t k = 0; k <= j; k++) out << SP;
190 out << "for (size_t " << index << " = 0; " << index << " < " << fShapeY[j] << "; " << index << "++) {\n";
191 }
192 // for i_0, i_1, ..., i_{q - 1}
193 for (size_t i = 0; i < q; i++) {
194 std::string index = "i_" + std::to_string(i);
195 for (size_t k = 0; k <= i + fAttrAxis; k++) out << SP;
196 out << "for (size_t " << index << " = " << 0 << "; " << index << " < " << fShapeIndices[i] << "; " << index << "++) {\n";
197 }
198 // for j_axis, j_{axis + 1}, ..., j_{r - 1}
199 for (size_t j = fAttrAxis; j + 1 < r; j++) {
200 std::string index = "j_" + std::to_string(q+j); // annotate index using output axis
201 for (size_t k = 0; k <= q + j; k++) out << SP;
202 out << "for (size_t " << index << " = 0; " << index << " < " << fShapeY[q + j] << "; " << index << "++) {\n";
203 }
204
205 // add a scope for local variables in case above loop are not done
206 if (fAttrAxis == 0 && q == 0 && r <= 1)
207 out << SP << "{ // scalar case \n";
208
209 // output index
210 for (size_t k = 0; k < q + r; k++) out << SP;
211 out << "size_t y_index = ";
212 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
213 if (j > 0) out << " + ";
214 out << "j_" << j;
215 if (stridesY[j].dim != 1) out << " * " << stridesY[j];
216 }
217 for (size_t i = 0; i < q; i++) {
218 if (fAttrAxis + i > 0) out << " + ";
219 out << "i_" << i;
220 if (stridesY[fAttrAxis + i].dim != 1) out << " * " << stridesY[fAttrAxis + i];
221 }
222 for (size_t j = fAttrAxis; j + 1 < r; j++) {
223 if (j + q > 0) out << " + ";
224 out << "j_" << q+j;
225 if (stridesY[q+j].dim != 1) out << " * " << stridesY[q+j];
226 }
227 // empty case
228 if (fAttrAxis == 0 && q == 0 && r <= 1)
229 out << "0";
230 out << ";\n";
231
232 // input Indices
233 for (size_t k = 0; k < q + r; k++) out << SP;
234 out << "size_t i_index = ";
235 for (size_t i = 0; i < q; i++) {
236 if (i > 0) out << " + ";
237 out << "i_" << i;
238 if (stridesIndices[i].dim != 1) out << " * " << stridesIndices[i];
239 }
240 // empty case
241 if (q == 0)
242 out << "0";
243 out << ";\n";
244
245 // K
246 for (size_t k = 0; k < q + r; k++) out << SP;
247 out << "size_t k = static_cast<size_t>(" << "tensor_" << fNIndices << "[i_index]" << ");\n";
248 // Input
249 for (size_t k = 0; k < q + r; k++) out << SP;
250 out << "size_t x_index = k";
251 if (stridesX[fAttrAxis].dim != 1) out << " * " << stridesX[fAttrAxis];
252 for (size_t j = 0; j < size_t(fAttrAxis); j++) {
253 out << " + ";
254 out << " j_" << j;
255 if (stridesX[j].dim != 1) out << " * " << stridesX[j];
256 }
257 // for input corresponding stride is axis+1,.... r
258 // loop is on j from fAttrAxis, so consider stridesX[j+1]
259 for (size_t j = fAttrAxis; j+1 < r; j++) {
260 out << " + ";
261 out << " j_" << q+j;
262 if (stridesX[j+1].dim != 1) out << " * " << stridesX[j+1];
263 }
264 out << ";\n";
265 for (size_t k = 0; k < q + r; k++) out << SP;
266 out << "tensor_" << fNY << "[y_index] = tensor_" << fNX << "[x_index];\n";
267
268 // end loops j_k, j_{k + 1}, ..., j_{r - 2}
269 for (size_t j = q+r-1; j > 0; j--) {
270 for (size_t k = 0; k <j; k++) out << SP;
271 out << "}\n";
272 }
273 // close empty scope if it was opened
274 if (q == 0 && fAttrAxis == 0 && r <= 1)
275 out << SP << "} // close Gather scope for scalar case \n";
276
277
278 return out.str();
279 }
280
281};
282
283}//SOFIE
284}//Experimental
285}//TMVA
286
287
288#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:247
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:211
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:220
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:312
void SetNotWritableInitializedTensor(const std::string &tensor_name)
Definition RModel.cxx:321
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:90
const std::vector< Dim > & GetShapeTensorValues(const std::string &tensor_name) const
Definition RModel.cxx:215
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