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 std::vector<std::vector<size_t>> inputIntShapes;
175 for (auto &it : fInputs) {
176 if (model.CheckIfTensorAlreadyExist(it) == false) {
177 throw std::runtime_error("TMVA SOFIE Concat Op Input Tensor " + it + " is not found in model");
178 }
179 fInputShapes.push_back(model.GetDimTensorShape(it));
180 if (!model.IsDynamicTensor(it)) {
182 }
183 }
184 if (inputIntShapes.size() == fInputs.size()) {
185 // if all input shapes are static we can compute output shape at initialization time
188 if (model.Verbose())
189 std::cout << "Initialize Concat operator with defined inputs shapes, "
190 << "output has shape " << ConvertShapeToString(outputIntShape) << std::endl;
191
192 } else {
193 // if at least one input shape is dynamic we need to compute output shape using the symbolic expression for the dimensions
195 if (model.Verbose())
196 std::cout << "Initialize Concat operator with dynamic inputs shapes, "
197 << "output has shape " << ConvertDimShapeToString(fOutputShape) << std::endl;
198 }
199
200 // check if concat has constant inputs , axis 0(concat contigous memory and type is integer)
201 bool isOutputShape = false;
202
203 // if (model.GetTensorType(fInputs[0]) == ETensorType::INT64 && fAxis == 0) {
204 fIsOutputConstant = true;
205 isOutputShape = true;
206
207 for (auto &input : fInputs) {
208 if (model.IsDynamicTensor(input)) {
209 fIsOutputConstant = false;
210 isOutputShape = false;
211 break;
212 }
213 if (!model.IsInitializedTensor(input)) {
214 if (model.IsShapeTensor(input)) {
215 // if it is a shape tensor we can have constant output if the shapes are defined)
218 if (!isShapeFullyDefined) {
219 fIsOutputConstant = false;
220 } else {
221 // if shape is fully defined we can consider output as constant and we can compute the output
222 // shape at initialization time
224 }
225 // inputs are then shape tensors and output is a shape tensor
226 isOutputShape = true;
227 } else {
228 // case of standard intermediate tensor
229 fIsOutputConstant = false;
230 isOutputShape = false;
231 break;
232 }
233 } else {
235 }
236 }
237 //}
238
239 if (fIsOutputConstant) {
240 auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
241 std::vector<int64_t> outputData(ConvertShapeToLength(outputShape));
242 size_t offset = 0;
243 for (auto &input : fInputs) {
244 auto inputData = static_cast<int64_t *>(model.GetInitializedTensorData(input).get());
245 auto inputShape = model.GetTensorShape(input); // shape is not dynamic if it is constant
249 // the data of the input tensor don't need to be written in the generated code and data file
251 }
252 model.AddConstantTensor<int64_t>(fOutput, outputShape, outputData.data());
253 if (model.Verbose()) {
254 std::cout << "output of Concat is a constant tensor " << ConvertShapeToString(outputShape) << " : "
255 << ConvertValuesToString(outputData) << " (constant)" << std::endl;
256 }
257 } else if (isOutputShape) {
258 auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
259 if (outputShape.size() != 1)
260 throw std::runtime_error("TMVA SOFIE Concat Op - output shape for shape tensor must have rank 1");
261 // output shape is a rank 1 tensor with size equal to the output rank
262 std::vector<Dim> outputData(outputShape[0]);
263 size_t offset = 0;
264 for (auto &input : fInputs) {
265 std::vector<Dim> inputData;
266 auto inputShape = model.GetTensorShape(input); // shape is not dynamic
267 size_t inputLength = ConvertShapeToLength(inputShape); // shape can be a scalar
268 if (model.IsShapeTensor(input)) {
270 } else if (model.IsInitializedTensor(input)) {
271 inputData.resize(inputLength);
272 auto intData = static_cast<int64_t *>(model.GetInitializedTensorData(input).get());
273 for (size_t i = 0; i < inputData.size(); i++)
274 inputData[i] = Dim{static_cast<size_t>(intData[i])};
275 } else {
276 // this should not happen
277 throw std::runtime_error("TMVA SOFIE Concat Operator- invalid tensor input " + input +
278 " for shape output type");
279 }
280 std::copy(inputData.begin(), inputData.end(), outputData.begin() + offset);
282 }
283 // add output tensor
284 model.AddShapeTensor(fOutput, outputData, false); // cannot be a scalar
286 if (model.Verbose()) {
287 std::cout << "output of Concat is a shape tensor " << ConvertShapeToString(outputShape) << " : "
288 << ConvertDimShapeToString(outputData) << " (shape)" << std::endl;
289 }
290 fIsOutputParamShape = true;
291 }
294 if (model.Verbose()) {
295 std::cout << "Concat ---> " << fOutput << " " << ConvertDimShapeToString(fOutputShape) << std::endl;
296 }
297 }
298 }
299
300 std::string Generate(std::string opName) override {
301 opName = "op_" + opName;
302 std::stringstream out;
303 out<<"\n//--------- Concat " << opName << " --> " << fOutput << " " << ConvertDimShapeToString(fOutputShape) << "\n";
304
305 if (fIsOutputConstant) return out.str();
306
308 // output is a shape tensor defined by the concatenation of the input shapes
309 out << "// output is a shape tensor defined by the concatenation of the input shapes\n";
310 for (int i = 0; i < static_cast<int>(fOutputShape
311 [0].dim); i++) {
312 out << SP << "tensor_" << fOutput << "[" << i << "] = " << fOutputShapeData[i] << ";\n";
313 }
314 return out.str();
315 }
316 // special case when memory is contiguous
317 bool hasShapeOnes = true;
318 for(int i = 0; i<fAxis; ++i){
319 if(fInputShapes[0][i].dim !=1){
320 hasShapeOnes = false;
321 break;
322 }
323 }
324 if (fAxis == 0 || hasShapeOnes) {
325 std::string offset;
326 for(size_t i=0; i<fInputs.size(); ++i) {
328 out << SP << "TMVA::Experimental::SOFIE::Copy(tensor_" << fOutput;
329 if (i > 0)
330 out << offset;
331 offset += " + " + length;
332 out << ", " << "tensor_" << fInputs[i] << ", " + length << ");\n";
333 }
334 }
335 else {
336
338 std::vector<std::vector<Dim>> inStrides(fInputs.size());
339 int idx = 0;
340 for ( auto &s : inStrides) {
342 idx++;
343 }
344 for (int i = 0; i < fAxis; ++i) {
345 // loop on dimensions
346 out << SP << "for (size_t i" << i << " = 0; i" << i << " < " << fOutputShape[i].GetVal() << "; ++i" << i <<") {\n";
347 }
348
349 out << SP << SP << SP << "int idxOut = ";
350 for (int k = 0; k < fAxis; k++) {
351 if (k > 0) out << " + ";
352 out << outStride[k].GetVal() << "*i" << k;
353 }
354 out << ";\n";
355
356 for (size_t j = 0; j < fInputs.size(); j++) {
357 if (j>0)
358 out << SP << SP << SP << "idxOut += " << inStrides[j-1][fAxis-1].GetVal() << ";\n";
359 out << SP << SP << SP << "int idxIn" << j <<" = ";
360 for (int k = 0; k < fAxis; k++) {
361 if (k > 0) out << " + ";
362 out << inStrides[j][k].GetVal() << "*i" << k;
363 }
364 out << ";\n";
365 out << SP << SP << SP << "for (size_t iC = 0; iC < " << inStrides[j][fAxis-1].GetVal() << "; ++iC) {\n";
366 out << SP << SP << SP << SP << "tensor_" << fOutput << "[idxOut+iC] = tensor_" << fInputs[j] << "[idxIn" << j << "+iC];\n";
367 out << SP << SP << SP << "}\n";
368 // concatenate the axis values
369 }
370 for (int i = 0; i < fAxis; ++i) {
371 out << SP << "}\n";
372 }
373 }
374
375 return out.str();
376 }
377 };
378 }//SOFIE
379 }//Experimental
380 }//TMVA
381
382 #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
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:286
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:301
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:232
bool IsShapeTensor(const std::string &name) const
check if a tensor is a shape tensor
Definition RModel.cxx:260
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:273
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:366
void SetNotWritableInitializedTensor(const std::string &tensor_name)
Definition RModel.cxx:375
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:125
const std::vector< Dim > & GetShapeTensorValues(const std::string &tensor_name) const
Definition RModel.cxx:268
const std::vector< std::string > & GetDimShapeNames() const
Definition RModel.hxx:208
void AddShapeTensor(const std::string &name, const std::vector< Dim > &shapeValues, bool scalar=false)
Definition RModel.cxx:242
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