Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Reshape.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_RESHAPE
2#define TMVA_SOFIE_ROPERATOR_RESHAPE
3
5#include "TMVA/ROperator.hxx"
6#include "TMVA/RModel.hxx"
7
8#include <cassert>
9#include <cctype>
10#include <sstream>
11#include <algorithm>
12
13namespace TMVA{
14namespace Experimental{
15namespace SOFIE{
16
18
19
21{
22
23private:
24
25 bool fVerbose = false;
26 bool fDimInput = false;
27 bool fDynamicShape = false;
28 ReshapeOpMode fOpMode = Reshape; // type of Reshape operator
29
30 int fAllowZero = 0; // (for Reshape) zero in tensor shape makes output shape equal to input tensor shape
31 int fAxis = 1; // (for Flatten)
32
33 std::string fNData; // input data tensor name
34 std::string fNInput2; // reshape or axes tensor name depending on operator
35 std::string fNOutput; // output tensor name
36 std::vector<Dim> fShapeInput; // input shape data
37 std::vector<Dim> fShapeOutput; // output shape data
38 std::vector<int64_t> fAttrAxes; // axes attributes (provided for all version of Squeeze/Unsqueeze)
39 std::vector<int64_t> fShape; // shape tensor values provided for Reshape
40
41public:
42
43 std::string Name() const {
44 if (fOpMode == Reshape) return "Reshape";
45 if (fOpMode == Flatten) return "Flatten";
46 if (fOpMode == Squeeze) return "Squeeze";
47 if (fOpMode == Unsqueeze) return "Unsqueeze";
48 return "";
49 }
50
52 ROperator_Reshape(ReshapeOpMode opMode, int attr_value, std::string nameData, std::string nameInput2, std::string nameOutput)
53 : fOpMode(opMode), fNData(UTILITY::Clean_name(nameData)), fNInput2(UTILITY::Clean_name(nameInput2)),
54 fNOutput(UTILITY::Clean_name(nameOutput))
55 {
58
60 if(!fNInput2.empty()){
61 fInputTensorNames.emplace_back(fNInput2);
62 }
64 }
65
66 // for squeeze/unsqueezed operators following old ONNX version (< 10)
67 // In this cases axes are passed as attribute values
68 ROperator_Reshape(ReshapeOpMode opMode, std::vector<int64_t> attrAxes, std::string nameData, std::string nameOutput)
69 : fOpMode(opMode), fNData(UTILITY::Clean_name(nameData)), fNOutput(UTILITY::Clean_name(nameOutput)),
71 {
75 }
76
77 // output type is same as input
78 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override {
79 auto ret = std::vector<ETensorType>(1, input[0]);
80 return ret;
81 }
82 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> input) override {
83 return input;
84 }
85
86 // output shape
87 std::vector<std::vector<Dim>> ShapeInference(const std::vector<std::vector<Dim>> & input) {
88 std::vector<std::vector<Dim>> ret;
89 auto & input_shape = input[0];
90 if (fOpMode == Reshape) {
91 // correct the provided shape (here we have the value) for 0 or -1
92 std::vector<Dim> output_shape(fShape.size());
93 assert(!fShape.empty() && !fDynamicShape);
94 for (size_t i = 0; i < output_shape.size(); i++) {
95 if (fShape[i] > 0 || (fAllowZero && fShape[i] >= 0))
96 output_shape[i] = Dim{ static_cast<size_t>(fShape[i]) };
97 else if (!fAllowZero && fShape[i] == 0)
99 }
100 // now case of -1 in shape
101 for (size_t i = 0; i < output_shape.size(); i++) {
102 if (fShape[i] == -1) {
103 auto tmp = output_shape;
104 tmp.erase(tmp.begin() + i);
107 if (fVerbose)
108 std::cout << "reshape- try simplifying " << ConvertDimShapeToString(input_shape) << " with length "
109 << input_length << " to " << tmp_length << std::endl;
110
112 output_shape[i] = Dim{static_cast<size_t>(std::stoi(input_length) / std::stoi(tmp_length))};
113 else if (IsInteger(tmp_length) && std::stoi(tmp_length) == 1) {
114 output_shape[i] = Dim{input_length, static_cast<size_t>(-1)};
115 }
116 else {
117 //we can try simplifying expression if tmp_length is integer and part of input_length
118 // contains tmp_length
119 bool canSimplify = false;
120 std::vector <Dim> reduced_input;
121 if (IsInteger(tmp_length)) {
122
123 // try to tokenize with * the input length
124
125 std::stringstream ss(input_length);
126
127 std::string token;
128
129 // Tokenizing w.r.t. space '*'
130 while(getline(ss, token, '*'))
131 {
132 // remove any whitespace
133 token.erase(std::remove_if(token.begin(), token.end(),
134 [](unsigned char x) { return std::isspace(x); }), token.end());
135 if (token != tmp_length) {
136 if (IsInteger(token)) {
137 size_t il = static_cast<size_t>(std::stoi(input_length));
138 size_t tl = static_cast<size_t>(std::stoi(tmp_length));
139 if ((il % tl) == 0) {
140 canSimplify = true;
141 reduced_input.push_back(Dim{il / tl});
142 }
143 } else {
144 reduced_input.push_back(Dim{token});
145 }
146 } else {
147 // token is equal to tmp_length, can be not considered and is simplified
148 canSimplify = true;
149 }
150 }
151 }
152 if (canSimplify) {
153 // if length contains * we need to add some brackets
155 if (res_shape.find('*') != std::string::npos)
156 output_shape[i] = Dim{std::string("(") + res_shape + ")", static_cast<size_t>(-1)};
157 else
159 }
160 if (!canSimplify)
161 output_shape[i] = Dim{std::string("(") + input_length + " / (" + tmp_length + "))", static_cast<size_t>(-1)};
162 }
163
164 break; // cannot have more than -1
165 }
166 // throw std::runtime_error(
167 // "TMVA Reshape Op : output shape has multiple negative or zero values");
168 }
169
170 if (fVerbose)
171 std::cout << "Reshape: correct output shape to " << ConvertShapeToString(output_shape) << std::endl;
172
174 throw std::runtime_error("TMVA Reshape Op : Invalid shapes : " + ConvertShapeToString(input_shape) +
176 }
177 ret.push_back(output_shape);
178
179 } else if (fOpMode == Flatten) {
180 // flatten case
181 if (fAxis < 0)
182 fAxis += input_shape.size();
183 auto s1 = std::vector<Dim>(input_shape.begin(), input_shape.begin() + fAxis);
184 auto s2 = std::vector<Dim>(input_shape.begin() + fAxis, input_shape.end());
187 std::vector<Dim> newShape = {Dim{l1}, Dim{l2}};
188 ret.push_back(newShape);
189 } else if (fOpMode == Squeeze) {
190 // squeeze
191 // assume no axis is provided - remove all axes with value equal to 1
193 if (fAttrAxes.empty()) {
194 size_t i = 0;
195 while (i < output_shape.size()) {
196 if (output_shape[i] == Dim{1}) {
197 output_shape.erase(output_shape.begin() + i);
198 } else {
199 i++;
200 }
201 }
202 } else {
203 std::cout << "getting shape for Squeeze...from attribute\n";
204 auto axes = fAttrAxes;
205 for (size_t i = 0; i < axes.size(); i++) {
206 std::cout << i << " " << axes[i] << std::endl;
207 if (axes[i] < 0)
208 axes[i] += input_shape.size();
209 if (!(output_shape[axes[i]] == Dim{1}))
210 throw std::runtime_error("TMVA Squeeze Op : Invalid axis value " + std::to_string(axes[i]) +
212 }
213 // for calling vector::erase we must sort axes in decreasing order to avoid
214 std::sort(axes.begin(), axes.end(), std::greater<int>());
215 for (auto & axis : axes) {
216 std::cout << "erase give axis " << axis << " -> ";
217 for (auto & o : output_shape) std::cout << o << " , ";
218 std::cout << std::endl;
219 output_shape.erase(output_shape.begin() + axis);
220 }
221 }
222 ret.push_back(output_shape);
223 }
224 else if (fOpMode == Unsqueeze) {
225 // unsqueeze
226 std::cout << "doing unsqueeze....\n";
227 assert(!fAttrAxes.empty());
229 auto &axes = fAttrAxes;
230 // output rank
231 int64_t r = input[0].size() + axes.size();
232 for (auto &a : axes) {
233 int64_t i = static_cast<int64_t>(a);
234 if (i < -r || i > r - 1)
235 throw std::runtime_error("TMVA Unsqueeze Op - axes input is not in correct range");
236 if (i >= 0)
237 output_shape.insert(output_shape.begin() + i, Dim{1});
238 else
239 // negative axes
240 output_shape.insert(output_shape.end() + i + 1, Dim{1});
241 }
242 ret.push_back(output_shape);
243 }
244 return ret;
245 }
246
247 void Initialize(RModel& model) override {
248
249 fVerbose = model.Verbose();
250 if (fVerbose)
251 std::cout << "initialize reshape op type " << fOpMode << " - " << fNInput2 << " " << fNData << std::endl;
252
253 if (model.CheckIfTensorAlreadyExist(fNData) == false) {
254 // input must be a graph input, or already initialized intermediate tensor
255 throw std::runtime_error("TMVA Reshape Op Input Tensor " + fNData + " is not found in model");
256 }
259 // check if optional tensor exists defining shape or axes
260 if (!fNInput2.empty()) {
262 if (model.IsInitializedTensor(fNInput2)) {
263 // assume input shape is an initialized tensor
265 auto values = static_cast<int64_t *>(dptr.get());
266 auto vec = model.GetTensorShape(fNInput2);
267 size_t n = 1;
268 if (vec.size() > 0)
269 n = vec[0]; // size of shape input tensor
270 // copy values in fShape vector or fAttrAxes
271 if (fOpMode == Reshape)
272 fShape = std::vector<int64_t>(values, values + n);
273 else
274 fAttrAxes = std::vector<int64_t>(values, values + n);
275
277 // set flag to not write tensor in weight file. Its data will be hard-coded in way model is constructed
279 } else if (model.IsShapeTensor(fNInput2)) {
282 } else {
283 // we cannot get shape at initialization time but at run-time
284 fDynamicShape = true;
285 // size of shape output us given by size of shape input tensor
286 auto shapeInput2 = model.GetTensorShape(fNInput2);
287 fShapeOutput.resize(shapeInput2[0]);
288 for (size_t i = 0; i < fShapeOutput.size(); i++) {
289 fShapeOutput[i] = Dim{ std::string("s_") + fNOutput + "_" + std::to_string(i)};
290 }
291 }
292 } else {
293 throw std::runtime_error("TMVA Reshape Op 2nd input Tensor " + fNInput2 + " is not found in model");
294 }
295 } else if (!fAttrAxes.empty()) {
296 // case fNShape is empty and axes are provided as attributes (e.g. for Unsqueeze)
297 std::cout << "attribute axes exists\n";
299 } else if (fOpMode == Flatten || fOpMode == Squeeze) {
301 } else {
302 throw std::runtime_error("TMVA Reshape Op : Invalid Input/Attribute data");
303 }
304 // check if output is constant or not
306 fIsOutputConstant = true;
307 auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(fNData).get());
310 throw std::runtime_error("TMVA Reshape Op : Invalid Input/Output lengths");
312 if (model.Verbose()) {
313 std::cout << Name() << " : " << fNData << " " << ConvertShapeToString(fShapeInput) << " --> " << fNOutput << " (constant) " << ConvertShapeToString(fShapeOutput) << " : " <<
315 }
316 }
317 // for shape tensors we can have it if output shape is size==1 or a scalar
318 else if (model.IsShapeTensor(fNData) && fShapeOutput.size() <=1) {
319 fIsOutputConstant = true;
322 if (model.Verbose()) {
323 std::cout << Name() << " : " << fNData << " " << ConvertShapeToString(fShapeInput) << " --> " << fNOutput << " (shape) " << ConvertShapeToString(fShapeOutput) << " : " <<
324 ConvertShapeToString(inputData) << std::endl;
325 }
326 }
327 else {
328 // non-constant case
330 if (model.Verbose())
331 std::cout << Name() << " : " << fNData << " " << ConvertShapeToString(fShapeInput) << " --> "<< fNOutput << " " << ConvertShapeToString(fShapeOutput) << std::endl;
332 }
333 }
334
335 std::string Generate(std::string opName) override {
336 if (fIsOutputConstant) return ""; //no op for constant tensors
337
338 std::stringstream out;
339 std::string opType = "Reshape";
340 if (fOpMode == Flatten)
341 opType = "Flatten";
342 else if (fOpMode == Squeeze)
343 opType = "Squeeze";
344 else if (fOpMode == Unsqueeze)
345 opType = "Unsquueze";
346
347 out << SP << "///--------" << opType << " operator " << opName << " --> " << ConvertShapeToString(fShapeOutput) << "\n";
348
349 // in case of dynamic output shape we need to set the shape value from input shape tensor
350 // and take case of the zero values
351 if (fDynamicShape) {
352 for (size_t i = 0; i < fShapeOutput.size(); i++) {
353 // since fNInput2 values are int64_t, should we check if they are negative?
354 out << SP << "size_t " << fShapeOutput[i].param << " = " << "tensor_" << fNInput2 << "[" << i << "];\n";
355 if (!fAllowZero)
356 out << SP << "if (tensor_" << fNInput2 << "[" << i << "] <= 0 ) "
357 << fShapeOutput[i].param << " = " << fShapeInput[i] << ";\n";
358 }
359 }
360
361 // output of reshape is same as input
364 if (lengthOut != lengthIn) {
365 // check needs to be done at run-time
366 out << SP << "if (" << lengthOut << "!=" << lengthIn << ")\n";
367 out << "throw std::runtime_error(\"TMVA SOFIE Reshape Op : output lengths is different than input one\");\n";
368 }
369
370
371 out << SP << "std::copy( tensor_" << fNData << ", tensor_" << fNData << " + " << lengthIn << ", " << "tensor_" << fNOutput
372 << ");\n";
373 return out.str();
374 }
375};
376
377}//SOFIE
378}//Experimental
379}//TMVA
380
381
382#endif //TMVA_SOFIE_ROPERATOR_RESHAPE
#define a(i)
Definition RSha256.hxx:99
#define s1(x)
Definition RSha256.hxx:91
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
const_iterator begin() const
const_iterator end() const
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
bool IsDynamicTensor(const std::string &name) const
Definition RModel.cxx:247
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
void SetNotWritableInitializedTensor(const std::string &tensor_name)
Definition RModel.cxx:336
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
ROperator_Reshape(ReshapeOpMode opMode, std::vector< int64_t > attrAxes, std::string nameData, std::string nameOutput)
std::vector< std::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > input) override
ROperator_Reshape(ReshapeOpMode opMode, int attr_value, std::string nameData, std::string nameInput2, std::string nameOutput)
std::vector< std::vector< Dim > > ShapeInference(const std::vector< std::vector< Dim > > &input)
std::string Generate(std::string opName) override
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
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
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)
std::vector< size_t > ConvertShapeToInt(const std::vector< Dim > &shape)
Convert shape based on Dim to integer format.
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
bool IsInteger(const std::string &s)
create variable transformations