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