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