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 {
73 }
74
75 // output type is same as input
76 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override {
77 auto ret = std::vector<ETensorType>(1, input[0]);
78 return ret;
79 }
80 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> input) override {
81 return input;
82 }
83
84 // output shape
85 std::vector<std::vector<Dim>> ShapeInference(const std::vector<std::vector<Dim>> & input) {
86 std::vector<std::vector<Dim>> ret;
87 auto & input_shape = input[0];
88 if (fOpMode == Reshape) {
89 // correct the provided shape (here we have the value) for 0 or -1
90 std::vector<Dim> output_shape(fShape.size());
91 assert(!fShape.empty() && !fDynamicShape);
92 for (size_t i = 0; i < output_shape.size(); i++) {
93 if (fShape[i] > 0 || (fAllowZero && fShape[i] >= 0))
94 output_shape[i] = Dim{ static_cast<size_t>(fShape[i]) };
95 else if (!fAllowZero && fShape[i] == 0)
97 }
98 // now case of -1 in shape
99 for (size_t i = 0; i < output_shape.size(); i++) {
100 if (fShape[i] == -1) {
101 auto tmp = output_shape;
102 tmp.erase(tmp.begin() + i);
105 if (fVerbose)
106 std::cout << "reshape- try simplifying " << ConvertDimShapeToString(input_shape) << " with length "
107 << input_length << " to " << tmp_length << std::endl;
108
110 output_shape[i] = Dim{static_cast<size_t>(std::stoi(input_length) / std::stoi(tmp_length))};
111 else if (IsInteger(tmp_length) && std::stoi(tmp_length) == 1) {
112 output_shape[i] = Dim{input_length, static_cast<size_t>(-1)};
113 }
114 else {
115 //we can try simplifying expression if tmp_length is integer and part of input_length
116 // contains tmp_length
117 bool canSimplify = false;
118 std::vector <Dim> reduced_input;
119 if (IsInteger(tmp_length)) {
120
121 // try to tokenize with * the input length
122
123 std::stringstream ss(input_length);
124
125 std::string token;
126
127 // Tokenizing w.r.t. space '*'
128 while(getline(ss, token, '*'))
129 {
130 // remove any whitespace
131 token.erase(std::remove_if(token.begin(), token.end(),
132 [](unsigned char x) { return std::isspace(x); }), token.end());
133 if (token != tmp_length) {
134 if (IsInteger(token)) {
135 size_t il = static_cast<size_t>(std::stoi(input_length));
136 size_t tl = static_cast<size_t>(std::stoi(tmp_length));
137 if ((il % tl) == 0) {
138 canSimplify = true;
139 reduced_input.push_back(Dim{il / tl});
140 }
141 } else {
142 reduced_input.push_back(Dim{token});
143 }
144 } else {
145 // token is equal to tmp_length, can be not considered and is simplified
146 canSimplify = true;
147 }
148 }
149 }
150 if (canSimplify) {
151 // if length contains * we need to add some brackets
153 if (res_shape.find('*') != std::string::npos)
154 output_shape[i] = Dim{std::string("(") + res_shape + ")", static_cast<size_t>(-1)};
155 else
157 }
158 if (!canSimplify)
159 output_shape[i] = Dim{std::string("(") + input_length + " / (" + tmp_length + "))", static_cast<size_t>(-1)};
160 }
161
162 break; // cannot have more than -1
163 }
164 // throw std::runtime_error(
165 // "TMVA Reshape Op : output shape has multiple negative or zero values");
166 }
167
168 if (fVerbose)
169 std::cout << "Reshape: correct output shape to " << ConvertShapeToString(output_shape) << std::endl;
170
172 throw std::runtime_error("TMVA Reshape Op : Invalid shapes : " + ConvertShapeToString(input_shape) +
174 }
175 ret.push_back(output_shape);
176
177 } else if (fOpMode == Flatten) {
178 // flatten case
179 if (fAxis < 0)
180 fAxis += input_shape.size();
181 auto s1 = std::vector<Dim>(input_shape.begin(), input_shape.begin() + fAxis);
182 auto s2 = std::vector<Dim>(input_shape.begin() + fAxis, input_shape.end());
185 std::vector<Dim> newShape = {Dim{l1}, Dim{l2}};
186 ret.push_back(newShape);
187 } else if (fOpMode == Squeeze) {
188 // squeeze
189 // assume no axis is provided - remove all axes with value equal to 1
191 if (fAttrAxes.empty()) {
192 size_t i = 0;
193 while (i < output_shape.size()) {
194 if (output_shape[i] == Dim{1}) {
195 output_shape.erase(output_shape.begin() + i);
196 } else {
197 i++;
198 }
199 }
200 } else {
201 auto &axes = fAttrAxes;
202 for (size_t i = 0; i < axes.size(); i++) {
203 if (axes[i] < 0)
204 axes[i] += input_shape.size();
205 if (!(output_shape[axes[i]] == Dim{1}))
206 throw std::runtime_error("TMVA Squeeze Op : Invalid axis value " + std::to_string(axes[i]) +
208 output_shape.erase(output_shape.begin() + axes[i]);
209 }
210 }
211 ret.push_back(output_shape);
212 }
213 else if (fOpMode == Unsqueeze) {
214 // unsqueeze
215 std::cout << "doing unsqueeze....\n";
216 assert(!fAttrAxes.empty());
218 auto &axes = fAttrAxes;
219 // output rank
220 int64_t r = input[0].size() + axes.size();
221 for (auto &a : axes) {
222 int64_t i = static_cast<int64_t>(a);
223 if (i < -r || i > r - 1)
224 throw std::runtime_error("TMVA Unsqueeze Op - axes input is not in correct range");
225 if (i >= 0)
226 output_shape.insert(output_shape.begin() + i, Dim{1});
227 else
228 // negative axes
229 output_shape.insert(output_shape.end() + i + 1, Dim{1});
230 }
231 ret.push_back(output_shape);
232 }
233 return ret;
234 }
235
236 void Initialize(RModel& model) override {
237
238 std::cout << "initialize reshape op type " << fOpMode << " - " << fNInput2 << " " << fNData << std::endl;
239 fVerbose = model.Verbose();
240 if (model.CheckIfTensorAlreadyExist(fNData) == false) {
241 // input must be a graph input, or already initialized intermediate tensor
242 throw std::runtime_error("TMVA Reshape Op Input Tensor " + fNData + " is not found in model");
243 }
246 // check if optional tensor exists defining shape or axes
247 if (!fNInput2.empty()) {
249 if (model.IsInitializedTensor(fNInput2)) {
250 // assume input shape is an initialized tensor
252 auto values = static_cast<int64_t *>(dptr.get());
253 auto vec = model.GetTensorShape(fNInput2);
254 size_t n = 1;
255 if (vec.size() > 0)
256 n = vec[0]; // size of shape input tensor
257 // copy values in fShape vector or fAttrAxes
258 if (fOpMode == Reshape)
259 fShape = std::vector<int64_t>(values, values + n);
260 else
261 fAttrAxes = std::vector<int64_t>(values, values + n);
262
264 // set flag to not write tensor in weight file. Its data will be hard-coded in way model is constructed
266 } else if (model.IsShapeTensor(fNInput2)) {
269 } else {
270 // we cannot get shape at initialization time but at run-time
271 fDynamicShape = true;
272 // size of shape output us given by size of shape input tensor
273 auto shapeInput2 = model.GetTensorShape(fNInput2);
274 fShapeOutput.resize(shapeInput2[0]);
275 for (size_t i = 0; i < fShapeOutput.size(); i++) {
276 fShapeOutput[i] = Dim{ std::string("s_") + fNOutput + "_" + std::to_string(i)};
277 }
278 }
279 } else {
280 throw std::runtime_error("TMVA Reshape Op 2nd input Tensor " + fNInput2 + " is not found in model");
281 }
282 } else if (!fAttrAxes.empty()) {
283 // case fNShape is empty and axes are provided as attributes (e.g. for Unsqueeze)
284 std::cout << "attribute axes exists\n";
286 } else if (fOpMode == Flatten || fOpMode == Squeeze) {
288 } else {
289 throw std::runtime_error("TMVA Reshape Op : Invalid Input/Attribute data");
290 }
291 // check if output is constant or not
293 fIsOutputConstant = true;
294 auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(fNData).get());
297 throw std::runtime_error("TMVA Reshape Op : Invalid Input/Output lengths");
299 if (model.Verbose()) {
300 std::cout << Name() << " : " << fNData << " " << ConvertShapeToString(fShapeInput) << " --> " << fNOutput << " (constant) " << ConvertShapeToString(fShapeOutput) << " : " <<
302 }
303 }
304 // for shape tensors we can have it if output shape is size==1 or a scalar
305 else if (model.IsShapeTensor(fNData) && fShapeOutput.size() <=1) {
306 fIsOutputConstant = true;
309 if (model.Verbose()) {
310 std::cout << Name() << " : " << fNData << " " << ConvertShapeToString(fShapeInput) << " --> " << fNOutput << " (shape) " << ConvertShapeToString(fShapeOutput) << " : " <<
311 ConvertShapeToString(inputData) << std::endl;
312 }
313 }
314 else {
315 // non-constant case
317 if (model.Verbose())
318 std::cout << Name() << " : " << fNData << " " << ConvertShapeToString(fShapeInput) << " --> "<< fNOutput << " " << ConvertShapeToString(fShapeOutput) << std::endl;
319 }
320 }
321
322 std::string Generate(std::string opName) override {
323 if (fIsOutputConstant) return ""; //no op for constant tensors
324
325 std::stringstream out;
326 std::string opType = "Reshape";
327 if (fOpMode == Flatten)
328 opType = "Flatten";
329 else if (fOpMode == Squeeze)
330 opType = "Squeeze";
331 else if (fOpMode == Unsqueeze)
332 opType = "Unsquueze";
333
334 out << SP << "///--------" << opType << " operator " << opName << " --> " << ConvertShapeToString(fShapeOutput) << "\n";
335
336 // in case of dynamic output shape we need to set the shape value from input shape tensor
337 // and take case of the zero values
338 if (fDynamicShape) {
339 for (size_t i = 0; i < fShapeOutput.size(); i++) {
340 // since fNInput2 values are int64_t, should we check if they are negative?
341 out << SP << "size_t " << fShapeOutput[i].param << " = " << "tensor_" << fNInput2 << "[" << i << "];\n";
342 if (!fAllowZero)
343 out << SP << "if (tensor_" << fNInput2 << "[" << i << "] <= 0 ) "
344 << fShapeOutput[i].param << " = " << fShapeInput[i] << ";\n";
345 }
346 }
347
348 // output of reshape is same as input
351 if (lengthOut != lengthIn) {
352 // check needs to be done at run-time
353 out << SP << "if (" << lengthOut << "!=" << lengthIn << ")\n";
354 out << "throw std::runtime_error(\"TMVA SOFIE Reshape Op : output lengths is different than input one\");\n";
355 }
356
357
358 out << SP << "std::copy( tensor_" << fNData << ", tensor_" << fNData << " + " << lengthIn << ", " << "tensor_" << fNOutput
359 << ");\n";
360 return out.str();
361 }
362};
363
364}//SOFIE
365}//Experimental
366}//TMVA
367
368
369#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