Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Concat.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_Concat
2 #define TMVA_SOFIE_ROPERATOR_Concat
3
4
5 #include "TMVA/SOFIE_common.hxx"
6 #include "TMVA/ROperator.hxx"
7 #include "TMVA/RModel.hxx"
8
9 #include <sstream>
10 #include <algorithm>
11 #include <iterator>
12 #include <iomanip>
13 #include <limits>
14
15 namespace TMVA{
16 namespace Experimental{
17 namespace SOFIE{
18
20 {
21 private:
22 int fAxis=0;
23 int fnewAxis=0;
24 std::vector<std::string> fInputs;
25 std::string fOutput;
26 std::vector<Dim>fOutputShape;
27 std::vector<Dim> fOutputShapeData; // in case output is a shape tensor we store here the output shape value data (can be parametric)
28 std::vector<std::vector<Dim>> fInputShapes;
29
30 public:
31
33 ROperator_Concat(std::vector<std::string> inputs, int axis, int newAxis, std::string output):
34 fAxis(axis), fnewAxis(newAxis), fOutput(UTILITY::Clean_name(output)) {
35 fInputs.reserve(inputs.size());
36 for (auto & name : inputs)
38
39 fInputTensorNames.resize(fInputs.size());
40 std::transform(fInputs.begin(), fInputs.end(), fInputTensorNames.begin(),
41 [](const std::string& s) -> std::string_view { return s; });
43 }
44
45 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override {
46 return input;
47 }
48
49 // get shape of output given inputs. It is going to be called after initialized
50 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> inputs) override {
51 std::vector<std::vector<size_t>> ret(1);
52 // treat negative axis case
53 if (fAxis<0) {
54 fAxis = inputs[0].size()+fAxis;
55 }
56 if (fAxis < 0 || fAxis >= (int) inputs[0].size())
57 throw std::runtime_error("TMVA SOFIE Concat Op - invalid axis value ");
58
59 int concat_dim=0;
60 // case of Concat (fNewAxis = 0) and not ConcatFromSequence
61 if(fnewAxis == 0){
62 for (size_t i = 0; i < inputs.size(); i++) {
63 if (i > 0 && inputs[i].size() != inputs[i - 1].size())
64 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " +
66 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
67 if ((int)iaxis == fAxis)
68 concat_dim += inputs[i][iaxis];
69 else if (i > 0 && inputs[i][iaxis] != inputs[i - 1][iaxis])
70 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
71 ConvertShapeToString(inputs[i]) + " and " +
73 }
74 }
75
76 // output shape
77 ret[0] = inputs[0];
78 ret[0][fAxis] = concat_dim;
79 }
80 std::vector<int> stack;
81 // case ConCatFromSequence
82 if(fnewAxis == 1){
83 for(size_t i = 0; i < inputs.size(); i++) {
84 if (i > 0 && inputs[i].size() != inputs[i-1].size() )
85 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " + fInputs[i] + " : " +
86 ConvertShapeToString(inputs[i]) + " and " + fInputs[i-1] + " : " + ConvertShapeToString(inputs[i-1]));
87 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
88 if ((int) iaxis == fAxis)
89 stack.push_back(inputs[i][iaxis]);
90 else
91 if (i> 0 && inputs[i][iaxis] != inputs[i-1][iaxis])
92 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
94 }
95
96 }
97 for(auto it:stack)
98 ret[0].push_back(it);
99 }
100
101 return ret;
102 }
103
104 // get shape of output given inputs. It is going to be called after initialized
105 std::vector<Dim> ShapeInference(const std::vector<std::vector<Dim>> & inputs, const RModel & model) {
106 std::vector<Dim> ret(inputs[0].size());
107 // treat negative axis case
108 if (fAxis<0) {
109 fAxis = inputs[0].size()+fAxis;
110 }
111 if (fAxis < 0 || fAxis >= (int) inputs[0].size())
112 throw std::runtime_error("TMVA SOFIE Concat Op - invalid axis value ");
113
115 if(fnewAxis == 0){
116 for (size_t i = 0; i < inputs.size(); i++) {
117 if (i > 0 && inputs[i].size() != inputs[i - 1].size())
118 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " + fInputs[i] + " : " +
119 ConvertDimShapeToString(inputs[i]) + " and " + fInputs[i-1] + " : " + ConvertDimShapeToString(inputs[i - 1]));
120 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
121 if ((int)iaxis == fAxis) {
122 // support both integer and params shape for the concatenation axis
123 if (concat_dim.param.empty() && concat_dim.dim == 0)
124 concat_dim = inputs[i][iaxis];
125 else if (inputs[i][iaxis].isParam || concat_dim.isParam) {
126 concat_dim =
127 Dim{ concat_dim.GetVal() + std::string(" + ") + inputs[i][iaxis].GetVal(),
128 static_cast<size_t>(-1)};
129 } else {
130 concat_dim = Dim { concat_dim.dim + inputs[i][iaxis].dim };
131 }
132 }
133 else if (i == 0) {
134 ret[iaxis] = inputs[i][iaxis];
135 }
136 else if ((!inputs[i][iaxis].isParam && !ret[iaxis].isParam) && (inputs[i][iaxis].dim != ret[iaxis].dim)) {
137 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
138 ConvertDimShapeToString(inputs[i]) + " and " +
140 }
141 else if (!inputs[i][iaxis].isParam && ret[iaxis].isParam){
142 // if shape is not parametric use it
143 ret[iaxis] = inputs[i][iaxis];
144 }
145 else if (inputs[i][iaxis].isParam && ret[iaxis].isParam) {
146 // check which parameter is first in RModel list
147 auto & dimNames = model.GetDimShapeNames();
148 auto p1 = std::find(dimNames.begin(), dimNames.end(), inputs[i][iaxis].param);
149 auto p2 = std::find(dimNames.begin(), dimNames.end(), ret[iaxis].param);
150 if (p1 < p2) ret[iaxis] = inputs[i][iaxis];
151 }
152
153 }
154 // add parenthesis in case is an expression
155 if (concat_dim.isParam && concat_dim.dim == static_cast<size_t>(-1))
156 concat_dim = Dim{ std::string("(") + concat_dim.GetVal() + std::string(")"), concat_dim.dim };
157 }
158
159 // output shape for concatenated axis
161
162 }
163 // case of stacking (not supported yet)
164 // here we need to check that input shapes are the same
165 // for example for fAxis == 0
166 // output shapes: [inputs.size(), inputs[0][0], inputs[0][1],....]
167 if(fnewAxis == 1){
168 throw std::runtime_error("TMVA SOFIE Concat Op - stacking (i.e. COncatFromSequence with new_axis=1) is not supported ");
169 }
170 return ret;
171 }
172
173 void Initialize(RModel& model) override {
174 // the generated code may use the Copy inference helper
175 model.AddNeededHelperFunction("Copy");
176 std::vector<std::vector<size_t>> inputIntShapes;
177 for (auto &it : fInputs) {
178 if (model.CheckIfTensorAlreadyExist(it) == false) {
179 throw std::runtime_error("TMVA SOFIE Concat Op Input Tensor " + it + " is not found in model");
180 }
181 fInputShapes.push_back(model.GetDimTensorShape(it));
182 if (!model.IsDynamicTensor(it)) {
184 }
185 }
186 if (inputIntShapes.size() == fInputs.size()) {
187 // if all input shapes are static we can compute output shape at initialization time
190 if (model.Verbose())
191 std::cout << "Initialize Concat operator with defined inputs shapes, "
192 << "output has shape " << ConvertShapeToString(outputIntShape) << std::endl;
193
194 } else {
195 // if at least one input shape is dynamic we need to compute output shape using the symbolic expression for the dimensions
197 if (model.Verbose())
198 std::cout << "Initialize Concat operator with dynamic inputs shapes, "
199 << "output has shape " << ConvertDimShapeToString(fOutputShape) << std::endl;
200 }
201
202 // check if concat has constant inputs , axis 0(concat contigous memory and type is integer)
203 bool isOutputShape = false;
204
205 // if (model.GetTensorType(fInputs[0]) == ETensorType::INT64 && fAxis == 0) {
206 fIsOutputConstant = true;
207 isOutputShape = true;
208
209 for (auto &input : fInputs) {
210 if (model.IsDynamicTensor(input)) {
211 fIsOutputConstant = false;
212 isOutputShape = false;
213 break;
214 }
215 if (!model.IsInitializedTensor(input)) {
216 if (model.IsShapeTensor(input)) {
217 // if it is a shape tensor we can have constant output if the shapes are defined)
220 if (!isShapeFullyDefined) {
221 fIsOutputConstant = false;
222 } else {
223 // if shape is fully defined we can consider output as constant and we can compute the output
224 // shape at initialization time
226 }
227 // inputs are then shape tensors and output is a shape tensor
228 isOutputShape = true;
229 } else {
230 // case of standard intermediate tensor
231 fIsOutputConstant = false;
232 isOutputShape = false;
233 break;
234 }
235 } else {
237 }
238 }
239 //}
240
241 if (fIsOutputConstant) {
242 auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
243 std::vector<int64_t> outputData(ConvertShapeToLength(outputShape));
244 size_t offset = 0;
245 for (auto &input : fInputs) {
246 auto inputData = static_cast<int64_t *>(model.GetInitializedTensorData(input).get());
247 auto inputShape = model.GetTensorShape(input); // shape is not dynamic if it is constant
251 // the data of the input tensor don't need to be written in the generated code and data file
253 }
254 model.AddConstantTensor<int64_t>(fOutput, outputShape, outputData.data());
255 if (model.Verbose()) {
256 std::cout << "output of Concat is a constant tensor " << ConvertShapeToString(outputShape) << " : "
257 << ConvertValuesToString(outputData) << " (constant)" << std::endl;
258 }
259 } else if (isOutputShape) {
260 auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
261 if (outputShape.size() != 1)
262 throw std::runtime_error("TMVA SOFIE Concat Op - output shape for shape tensor must have rank 1");
263 // output shape is a rank 1 tensor with size equal to the output rank
264 std::vector<Dim> outputData(outputShape[0]);
265 size_t offset = 0;
266 for (auto &input : fInputs) {
267 std::vector<Dim> inputData;
268 auto inputShape = model.GetTensorShape(input); // shape is not dynamic
269 size_t inputLength = ConvertShapeToLength(inputShape); // shape can be a scalar
270 if (model.IsShapeTensor(input)) {
272 } else if (model.IsInitializedTensor(input)) {
273 inputData.resize(inputLength);
274 auto intData = static_cast<int64_t *>(model.GetInitializedTensorData(input).get());
275 for (size_t i = 0; i < inputData.size(); i++)
276 inputData[i] = Dim{static_cast<size_t>(intData[i])};
277 } else {
278 // this should not happen
279 throw std::runtime_error("TMVA SOFIE Concat Operator- invalid tensor input " + input +
280 " for shape output type");
281 }
282 std::copy(inputData.begin(), inputData.end(), outputData.begin() + offset);
284 }
285 // add output tensor
286 model.AddShapeTensor(fOutput, outputData, false); // cannot be a scalar
288 if (model.Verbose()) {
289 std::cout << "output of Concat is a shape tensor " << ConvertShapeToString(outputShape) << " : "
290 << ConvertDimShapeToString(outputData) << " (shape)" << std::endl;
291 }
292 fIsOutputParamShape = true;
293 }
296 if (model.Verbose()) {
297 std::cout << "Concat ---> " << fOutput << " " << ConvertDimShapeToString(fOutputShape) << std::endl;
298 }
299 }
300 }
301
302 std::string Generate(std::string opName) override {
303 opName = "op_" + opName;
304 std::stringstream out;
305 out<<"\n//--------- Concat " << opName << " --> " << fOutput << " " << ConvertDimShapeToString(fOutputShape) << "\n";
306
307 if (fIsOutputConstant) return out.str();
308
310 // output is a shape tensor defined by the concatenation of the input shapes
311 out << "// output is a shape tensor defined by the concatenation of the input shapes\n";
312 for (int i = 0; i < static_cast<int>(fOutputShape
313 [0].dim); i++) {
314 out << SP << "tensor_" << fOutput << "[" << i << "] = " << fOutputShapeData[i] << ";\n";
315 }
316 return out.str();
317 }
318 // special case when memory is contiguous
319 bool hasShapeOnes = true;
320 for(int i = 0; i<fAxis; ++i){
321 if(fInputShapes[0][i].dim !=1){
322 hasShapeOnes = false;
323 break;
324 }
325 }
326 if (fAxis == 0 || hasShapeOnes) {
327 std::string offset;
328 for(size_t i=0; i<fInputs.size(); ++i) {
330 out << SP << "Copy(tensor_" << fOutput;
331 if (i > 0)
332 out << offset;
333 offset += " + " + length;
334 out << ", " << "tensor_" << fInputs[i] << ", " + length << ");\n";
335 }
336 }
337 else {
338
340 std::vector<std::vector<Dim>> inStrides(fInputs.size());
341 int idx = 0;
342 for ( auto &s : inStrides) {
344 idx++;
345 }
346 for (int i = 0; i < fAxis; ++i) {
347 // loop on dimensions
348 out << SP << "for (size_t i" << i << " = 0; i" << i << " < " << fOutputShape[i].GetVal() << "; ++i" << i <<") {\n";
349 }
350
351 out << SP << SP << SP << "int idxOut = ";
352 for (int k = 0; k < fAxis; k++) {
353 if (k > 0) out << " + ";
354 out << outStride[k].GetVal() << "*i" << k;
355 }
356 out << ";\n";
357
358 for (size_t j = 0; j < fInputs.size(); j++) {
359 if (j>0)
360 out << SP << SP << SP << "idxOut += " << inStrides[j-1][fAxis-1].GetVal() << ";\n";
361 out << SP << SP << SP << "int idxIn" << j <<" = ";
362 for (int k = 0; k < fAxis; k++) {
363 if (k > 0) out << " + ";
364 out << inStrides[j][k].GetVal() << "*i" << k;
365 }
366 out << ";\n";
367 out << SP << SP << SP << "for (size_t iC = 0; iC < " << inStrides[j][fAxis-1].GetVal() << "; ++iC) {\n";
368 out << SP << SP << SP << SP << "tensor_" << fOutput << "[idxOut+iC] = tensor_" << fInputs[j] << "[idxIn" << j << "+iC];\n";
369 out << SP << SP << SP << "}\n";
370 // concatenate the axis values
371 }
372 for (int i = 0; i < fAxis; ++i) {
373 out << SP << "}\n";
374 }
375 }
376
377 return out.str();
378 }
379 };
380 }//SOFIE
381 }//Experimental
382 }//TMVA
383
384 #endif //TMVA_SOFIE_ROPERATOR_CONCAT
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
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 Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
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 Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h length
char name[80]
Definition TGX11.cxx:148
const_iterator begin() const
const_iterator end() const
void AddNeededHelperFunction(std::string name)
std::vector< size_t > GetTensorShape(const std::string &name) const
Definition RModel.cxx:64
std::vector< Dim > GetDimTensorShape(const std::string &name) const
Definition RModel.cxx:100
bool IsDynamicTensor(const std::string &name) const
Definition RModel.cxx:296
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:311
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:157
void AddConstantTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:242
bool IsShapeTensor(const std::string &name) const
check if a tensor is a shape tensor
Definition RModel.cxx:270
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:283
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:376
void SetNotWritableInitializedTensor(const std::string &tensor_name)
Definition RModel.cxx:385
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:125
const std::vector< Dim > & GetShapeTensorValues(const std::string &tensor_name) const
Definition RModel.cxx:278
const std::vector< std::string > & GetDimShapeNames() const
Definition RModel.hxx:201
void AddShapeTensor(const std::string &name, const std::vector< Dim > &shapeValues, bool scalar=false)
Definition RModel.cxx:252
std::vector< Dim > ShapeInference(const std::vector< std::vector< Dim > > &inputs, const RModel &model)
std::vector< std::vector< Dim > > fInputShapes
ROperator_Concat(std::vector< std::string > inputs, int axis, int newAxis, std::string output)
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input) override
std::vector< std::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > inputs) override
std::string Generate(std::string opName) override
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:50
bool fIsOutputParamShape
flag to identify of the output represents a parametric shape (can be known at compile time)
Definition ROperator.hxx:48
bool fIsOutputConstant
flag to identify if operator has a constant output (no need to generate code)
Definition ROperator.hxx:47
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:45
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:51
std::string Clean_name(std::string input_tensor_name)
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< Dim > ConvertShapeToDim(const std::vector< size_t > &shape)
Convert shape from integer format to dynamic one (based on Dim)
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)
create variable transformations