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<std::vector<Dim>> fInputShapes;
28
29 public:
30
32 ROperator_Concat(std::vector<std::string> inputs, int axis, int newAxis, std::string output):
33 fAxis(axis), fnewAxis(newAxis), fOutput(UTILITY::Clean_name(output)) {
34 fInputs.reserve(inputs.size());
35 for (auto & name : inputs)
37
38 fInputTensorNames.resize(fInputs.size());
39 std::transform(fInputs.begin(), fInputs.end(), fInputTensorNames.begin(),
40 [](const std::string& s) -> std::string_view { return s; });
42 }
43
44 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override {
45 return input;
46 }
47
48 // get shape of output given inputs. It is going to be called after initialized
49 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> inputs) override {
50 std::vector<std::vector<size_t>> ret(1);
51 // treat negative axis case
52 if (fAxis<0) {
53 fAxis = inputs[0].size()+fAxis;
54 }
55 if (fAxis < 0 || fAxis >= (int) inputs[0].size())
56 throw std::runtime_error("TMVA SOFIE Concat Op - invalid axis value ");
57
58 int concat_dim=0;
59 // case of Concat (fNewAxis = 0) and not ConcatFromSequence
60 if(fnewAxis == 0){
61 for (size_t i = 0; i < inputs.size(); i++) {
62 if (i > 0 && inputs[i].size() != inputs[i - 1].size())
63 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " +
65 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
66 if ((int)iaxis == fAxis)
67 concat_dim += inputs[i][iaxis];
68 else if (i > 0 && inputs[i][iaxis] != inputs[i - 1][iaxis])
69 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
70 ConvertShapeToString(inputs[i]) + " and " +
72 }
73 }
74
75 // output shape
76 ret[0] = inputs[0];
77 ret[0][fAxis] = concat_dim;
78 }
79 std::vector<int> stack;
80 // case ConCatFromSequence
81 if(fnewAxis == 1){
82 for(size_t i = 0; i < inputs.size(); i++) {
83 if (i > 0 && inputs[i].size() != inputs[i-1].size() )
84 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " + fInputs[i] + " : " +
85 ConvertShapeToString(inputs[i]) + " and " + fInputs[i-1] + " : " + ConvertShapeToString(inputs[i-1]));
86 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
87 if ((int) iaxis == fAxis)
88 stack.push_back(inputs[i][iaxis]);
89 else
90 if (i> 0 && inputs[i][iaxis] != inputs[i-1][iaxis])
91 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
93 }
94
95 }
96 for(auto it:stack)
97 ret[0].push_back(it);
98 }
99
100 return ret;
101 }
102
103 // get shape of output given inputs. It is going to be called after initialized
104 std::vector<Dim> ShapeInference(const std::vector<std::vector<Dim>> & inputs, const RModel & model) {
105 std::vector<Dim> ret(inputs[0].size());
106 // treat negative axis case
107 if (fAxis<0) {
108 fAxis = inputs[0].size()+fAxis;
109 }
110 if (fAxis < 0 || fAxis >= (int) inputs[0].size())
111 throw std::runtime_error("TMVA SOFIE Concat Op - invalid axis value ");
112
114 if(fnewAxis == 0){
115 for (size_t i = 0; i < inputs.size(); i++) {
116 if (i > 0 && inputs[i].size() != inputs[i - 1].size())
117 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " + fInputs[i] + " : " +
118 ConvertShapeToString(inputs[i]) + " and " + fInputs[i-1] + " : " + ConvertShapeToString(inputs[i - 1]));
119 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
120 if ((int)iaxis == fAxis) {
121 // support both integer and params shape for the concatenation axis
122 if (concat_dim.param.empty() && concat_dim.dim == 0)
123 concat_dim = inputs[i][iaxis];
124 else if (inputs[i][iaxis].isParam || concat_dim.isParam) {
125 concat_dim =
126 Dim{ concat_dim.GetVal() + std::string("+ ") + inputs[i][iaxis].GetVal(),
127 static_cast<size_t>(-1)};
128 } else {
129 concat_dim = Dim { concat_dim.dim + inputs[i][iaxis].dim };
130 }
131 }
132 else if (i == 0) {
133 ret[iaxis] = inputs[i][iaxis];
134 }
135 else if ((!inputs[i][iaxis].isParam && !ret[iaxis].isParam) && (inputs[i][iaxis].dim != ret[iaxis].dim)) {
136 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
137 ConvertShapeToString(inputs[i]) + " and " +
139 }
140 else if (!inputs[i][iaxis].isParam && ret[iaxis].isParam){
141 // if shape is not parametric use it
142 ret[iaxis] = inputs[i][iaxis];
143 }
144 else if (inputs[i][iaxis].isParam && ret[iaxis].isParam) {
145 // check which parameter is first in RModel list
146 auto & dimNames = model.GetDimShapeNames();
147 auto p1 = std::find(dimNames.begin(), dimNames.end(), inputs[i][iaxis].param);
148 auto p2 = std::find(dimNames.begin(), dimNames.end(), ret[iaxis].param);
149 if (p1 < p2) ret[iaxis] = inputs[i][iaxis];
150 }
151
152 }
153 // add parenthesis in case is an expression
154 if (concat_dim.isParam && concat_dim.dim == static_cast<size_t>(-1))
155 concat_dim = Dim{ std::string("(") + concat_dim.GetVal() + std::string(")"), concat_dim.dim };
156 }
157
158 // output shape for concatenated axis
160
161 }
162 // case of stacking (not supported yet)
163 // here we need to check that input shapes are the same
164 // for example for fAxis == 0
165 // output shapes: [inputs.size(), inputs[0][0], inputs[0][1],....]
166 if(fnewAxis == 1){
167 throw std::runtime_error("TMVA SOFIE Concat Op - stacking (i.e. COncatFromSequence with new_axis=1) is not supported ");
168 }
169 return ret;
170 }
171
172 void Initialize(RModel& model) override {
173 for (auto &it : fInputs) {
174 if (model.CheckIfTensorAlreadyExist(it) == false) {
175 throw std::runtime_error("TMVA SOFIE Concat Op Input Tensor " + it + " is not found in model");
176 }
177 fInputShapes.push_back(model.GetDimTensorShape(it));
178 }
180 if (model.Verbose())
181 std::cout << "Output of concat operator has shape " << ConvertDimShapeToString(fOutputShape) << std::endl;
182
183 // check if concat has constant inputs , axis 0(concat contigous memory and type is integer)
184 bool isOutputShape = false;
185 if (model.GetTensorType(fInputs[0]) == ETensorType::INT64 && fAxis == 0) {
186 fIsOutputConstant = true;
187 isOutputShape = true;
188
189 for ( auto & input : fInputs) {
190 if (!model.IsInitializedTensor(input)) {
191 fIsOutputConstant = false;
192 if (!model.IsShapeTensor(input)) {
193 isOutputShape = false;
194 break;
195 }
196 }
197 }
198 if (fIsOutputConstant) {
199 auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
200 std::vector<int64_t> outputData(ConvertShapeToLength(outputShape));
201 size_t offset = 0;
202 for ( auto & input : fInputs) {
203 auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(input).get());
204 auto inputShape = model.GetTensorShape(input); // shape is not dynamic if it is constant
208 // data do not need to be written as a weight
210 }
211 model.AddConstantTensor<int64_t>(fOutput, outputShape, outputData.data());
212 if (model.Verbose()) {
213 std::cout << "output of Concat is a constant tensor " << ConvertShapeToString(outputShape) << " : "
214 << ConvertValuesToString(outputData) << " (constant)" << std::endl;
215 }
216 } else if (isOutputShape) {
217 auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
219 size_t offset = 0;
220 for ( auto & input : fInputs) {
221 std::vector<Dim> inputData;
222 auto inputShape = model.GetTensorShape(input); // shape is not dynamic
223 size_t inputLength = ConvertShapeToLength(inputShape); // shape can be a scalar
224 if (model.IsShapeTensor(input))
226 else if (model.IsConstantTensor(input)) {
227 inputData.resize(inputLength);
228 auto intData = static_cast<int64_t*>(model.GetInitializedTensorData(input).get());
229 for (size_t i = 0; i < inputData.size(); i++)
230 inputData[i] = Dim{ static_cast<size_t>(intData[i])};
231 }
232 std::cout << "concatenating input data " << inputLength << " " << inputData[0] << std::endl;
233 std::copy(inputData.begin(), inputData.end(), outputData.begin() + offset );
235 }
236 // add output tensor
237 model.AddShapeTensor(fOutput,outputData, false); // cannot be a scalar
238 if (model.Verbose()) {
239 std::cout << "output of Concat is a shape tensor " << ConvertShapeToString(outputShape) << " : "
240 << ConvertShapeToString(outputData) << " (shape)" << std::endl;
241 }
242 fIsOutputConstant = true;
243 }
244 }
245 if (!fIsOutputConstant) {
247 if (model.Verbose()) {
248 std::cout << "Concat ---> " << fOutput << " " << ConvertDimShapeToString(fOutputShape) << std::endl;
249 }
250 }
251 }
252
253 std::string Generate(std::string opName) override {
254 if (fIsOutputConstant) return "";
255 opName = "op_" + opName;
256 if(fOutputShape.empty()){
257 throw std::runtime_error("TMVA SOFIE Concat called to Generate without being initialized first");
258 }
259 std::stringstream out;
260 out<<"\n//--------- Concat " << opName << " --> " << ConvertShapeToString(fOutputShape) << "\n";
261 // special case when memory is contiguous
262 bool hasShapeOnes = true;
263 for(int i = 0; i<fAxis; ++i){
264 if(fInputShapes[0][i].dim !=1){
265 hasShapeOnes = false;
266 break;
267 }
268 }
269 if (fAxis == 0 || hasShapeOnes) {
270 std::string offset;
271 for(size_t i=0; i<fInputs.size(); ++i) {
273 out << SP << "std::copy(tensor_" <<fInputs[i] << ", tensor_" <<fInputs[i] << "+" << length <<", tensor_"<<fOutput;
274 if (i > 0) out << offset;
275 offset += " + " + length;
276 out << ");\n";
277 }
278 }
279 else {
280
282 std::vector<std::vector<Dim>> inStrides(fInputs.size());
283 int idx = 0;
284 for ( auto &s : inStrides) {
286 idx++;
287 }
288 for (int i = 0; i < fAxis; ++i) {
289 // loop on dimensions
290 out << SP << "for (size_t i" << i << " = 0; i" << i << " < " << fOutputShape[i].GetVal() << "; ++i" << i <<") {\n";
291 }
292
293 out << SP << SP << SP << "int idxOut = ";
294 for (int k = 0; k < fAxis; k++) {
295 if (k > 0) out << " + ";
296 out << outStride[k].GetVal() << "*i" << k;
297 }
298 out << ";\n";
299
300 for (size_t j = 0; j < fInputs.size(); j++) {
301 if (j>0)
302 out << SP << SP << SP << "idxOut += " << fInputShapes[j-1][fAxis].GetVal() << ";\n";
303 out << SP << SP << SP << "int idxIn" << j <<" = ";
304 for (int k = 0; k < fAxis; k++) {
305 if (k > 0) out << " + ";
306 out << inStrides[j][k].GetVal() << "*i" << k;
307 }
308 out << ";\n";
309 out << SP << SP << SP << "for (size_t iC = 0; iC < " << fInputShapes[j][fAxis].GetVal() << "; ++iC) {\n";
310 out << SP << SP << SP << SP << "tensor_" << fOutput << "[idxOut+iC] = tensor_" << fInputs[j] << "[idxIn" << j << "+iC];\n";
311 out << SP << SP << SP << "}\n";
312 // concatenate the axis values
313 }
314 for (int i = 0; i < fAxis; ++i) {
315 out << SP << "}\n";
316 }
317 }
318
319 return out.str();
320 }
321 };
322 }//SOFIE
323 }//Experimental
324 }//TMVA
325
326 #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:110
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
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
const std::vector< std::string > & GetDimShapeNames() const
Definition RModel.hxx:203
void AddShapeTensor(const std::string &name, const std::vector< Dim > &shapeValues, bool scalar=false)
Definition RModel.cxx:203
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: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
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)
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
static void output()