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
19 class ROperator_Concat final : public ROperator
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:
31 ROperator_Concat(std::vector<std::string> inputs, int axis, int newAxis, std::string output):
32 fAxis(axis), fnewAxis(newAxis), fOutput(UTILITY::Clean_name(output)) {
33 fInputs.reserve(inputs.size());
34 for (auto & name : inputs)
36 }
37
38 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input){
39 return input;
40 }
41
42 // get shape of output given inputs. It is going to be called after initialized
43 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> inputs){
44 std::vector<std::vector<size_t>> ret(1);
45 // treat negative axis case
46 if (fAxis<0) {
47 fAxis = inputs[0].size()+fAxis;
48 }
49 if (fAxis < 0 || fAxis >= (int) inputs[0].size())
50 throw std::runtime_error("TMVA SOFIE Concat Op - invalid axis value ");
51
52 int concat_dim=0;
53 if(fnewAxis == 0){
54 for (size_t i = 0; i < inputs.size(); i++) {
55 if (i > 0 && inputs[i].size() != inputs[i - 1].size())
56 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " +
57 ConvertShapeToString(inputs[i]) + " and " + ConvertShapeToString(inputs[i - 1]));
58 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
59 if ((int)iaxis == fAxis)
60 concat_dim += inputs[i][iaxis];
61 else if (i > 0 && inputs[i][iaxis] != inputs[i - 1][iaxis])
62 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
63 ConvertShapeToString(inputs[i]) + " and " +
64 ConvertShapeToString(inputs[i - 1]));
65 }
66 }
67
68 // output shape
69 ret[0] = inputs[0];
70 ret[0][fAxis] = concat_dim;
71 }
72 std::vector<int> stack;
73 if(fnewAxis == 1){
74 for(size_t i = 0; i < inputs.size(); i++) {
75 if (i > 0 && inputs[i].size() != inputs[i-1].size() )
76 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " + fInputs[i] + " : " +
77 ConvertShapeToString(inputs[i]) + " and " + fInputs[i-1] + " : " + ConvertShapeToString(inputs[i-1]));
78 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
79 if ((int) iaxis == fAxis)
80 stack.push_back(inputs[i][iaxis]);
81 else
82 if (i> 0 && inputs[i][iaxis] != inputs[i-1][iaxis])
83 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
84 ConvertShapeToString(inputs[i]) + " and " + ConvertShapeToString(inputs[i-1]));
85 }
86
87 }
88 for(auto it:stack)
89 ret[0].push_back(it);
90 }
91
92 return ret;
93 }
94
95 // get shape of output given inputs. It is going to be called after initialized
96 std::vector<std::vector<Dim>> ShapeInference(const std::vector<std::vector<Dim>> & inputs){
97 std::vector<std::vector<Dim>> ret(1);
98 // treat negative axis case
99 if (fAxis<0) {
100 fAxis = inputs[0].size()+fAxis;
101 }
102 if (fAxis < 0 || fAxis >= (int) inputs[0].size())
103 throw std::runtime_error("TMVA SOFIE Concat Op - invalid axis value ");
104
105 int concat_dim=0;
106 if(fnewAxis == 0){
107 for (size_t i = 0; i < inputs.size(); i++) {
108 if (i > 0 && inputs[i].size() != inputs[i - 1].size())
109 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have different shapes " + fInputs[i] + " : " +
110 ConvertDynamicShapeToString(inputs[i]) + " and " + fInputs[i-1] + " : " + ConvertDynamicShapeToString(inputs[i - 1]));
111 for (size_t iaxis = 0; iaxis < inputs[i].size(); iaxis++) {
112 if ((int)iaxis == fAxis) {
113 // support only non-params shape for the concatenation axis
114 if (inputs[i][iaxis].isParam)
115 throw std::runtime_error("TMVA SOFIE Concat Op - not supporting input param dimensions for concatenation axis. Input shape is " +
116 ConvertDynamicShapeToString(inputs[i]));
117 concat_dim += inputs[i][iaxis].dim;
118 }
119 // other dimensions must be the same
120 else if (i > 0 && inputs[i][iaxis].GetVal() != inputs[i - 1][iaxis].GetVal())
121 throw std::runtime_error("TMVA SOFIE Concat Op - input tensors have wrong shapes " +
122 ConvertDynamicShapeToString(inputs[i]) + " and " +
123 ConvertDynamicShapeToString(inputs[i - 1]));
124 }
125 }
126
127 // output shape
128 ret[0] = inputs[0];
129 ret[0][fAxis].dim = concat_dim;
130 }
131 // case of stacking (not supported yet)
132 // here we need to check that input shapes are the same
133 // for example for fAxis == 0
134 // output shapes: [inputs.size(), inputs[0][0], inputs[0][1],....]
135 if(fnewAxis == 1){
136 throw std::runtime_error("TMVA SOFIE Concat Op - stacking (i.e. COncatFromSequence with new_axis=1) is not supported ");
137 }
138 return ret;
139 }
140
141 void Initialize(RModel &model)
142 {
143 for (auto &it : fInputs) {
144 if (model.CheckIfTensorAlreadyExist(it) == false) {
145 throw std::runtime_error("TMVA SOFIE Concat Op Input Tensor " + it + " is not found in model");
146 }
147 fInputShapes.push_back(model.GetDynamicTensorShape(it));
148 }
149 // patch for concat for case {1,x} with {y} remove the 1 in first tensor
150 // if (fInputShapes.size() == 2) {
151 // if (fInputShapes[1].size() < fInputShapes[0].size()) {
152 // if (fInputShapes[0].front().dim == 1) {
153 // std::cout << "Patch concat and make first input with shape " << ConvertDynamicShapeToString(fInputShapes[0])
154 // << " compatible with second input " << ConvertDynamicShapeToString(fInputShapes[1]) << "by removing firs dim" << std::endl;
155 // fInputShapes[0].erase(fInputShapes[0].begin());
156 // }
157 // }
158 // }
160 if (model.Verbose())
161 std::cout << "Output of concat operator has shape " << ConvertDynamicShapeToString(fOutputShape) << std::endl;
162
163 // check if concat has constant inputs , axis 0(concat contigous memory and type is integer)
164 if (model.GetTensorType(fInputs[0]) == ETensorType::INT64 && fAxis == 0) {
165 fIsOutputConstant = true;
166 for ( auto & input : fInputs) {
167 if (!model.IsInitializedTensor(input)) {
168 fIsOutputConstant = false;
169 break;
170 }
171 }
172 if (fIsOutputConstant) {
173 auto outputShape = ConvertShapeToInt(fOutputShape); // conversion must be possible
174 std::vector<int64_t> outputData(ConvertShapeToLength(outputShape));
175 size_t offset = 0;
176 for ( auto & input : fInputs) {
177 auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(input).get());
178 auto inputShape = model.GetTensorShape(input); // shape is not dynamic if it is constant
179 size_t inputLength = ConvertShapeToLength(inputShape);
180 std::copy(inputData, inputData + inputLength, outputData.begin() + offset );
181 offset += inputLength;
182 // data do not need to be written as a weight
184 }
185 model.AddConstantTensor<int64_t>(fOutput, outputShape, outputData.data());
186 if (model.Verbose()) {
187 std::cout << "output of Concat is a constant tensor " << ConvertShapeToString(outputShape) << " : "
188 << ConvertValuesToString(outputData) << std::endl;
189 }
190 }
191 }
192 if (!fIsOutputConstant) {
194 if (model.Verbose()) {
195 std::cout << "Concat ---> " << fOutput << " " << ConvertDynamicShapeToString(fOutputShape) << std::endl;
196 }
197 }
198 }
199
200 std::string Generate(std::string OpName){
201 if (fIsOutputConstant) return "";
202 OpName = "op_"+OpName;
203 if(fOutputShape.empty()){
204 throw std::runtime_error("TMVA SOFIE Concat called to Generate without being initialized first");
205 }
206 std::stringstream out;
207 out<<"\n//--------- Concat\n";
208 // special case when memory is contiguous
209 bool hasShapeOnes = true;
210 for(int i = 0; i<fAxis; ++i){
211 if(fInputShapes[0][i].dim !=1){
212 hasShapeOnes = false;
213 break;
214 }
215 }
216 if (fAxis == 0 || hasShapeOnes) {
217 std::string offset;
218 for(size_t i=0; i<fInputs.size(); ++i) {
220 out << SP << "std::copy(tensor_" <<fInputs[i] << ", tensor_" <<fInputs[i] << "+" << length <<", tensor_"<<fOutput;
221 if (i > 0) out << offset;
222 offset += " + " + length;
223 out << ");\n";
224 }
225 }
226 else {
227
228 std::vector<Dim> outStride = UTILITY::ComputeStrideFromShape(fOutputShape);
229 std::vector<std::vector<Dim>> inStrides(fInputs.size());
230 int idx = 0;
231 for ( auto &s : inStrides) {
233 idx++;
234 }
235 for (int i = 0; i < fAxis; ++i) {
236 // loop on dimensions
237 out << SP << "for (size_t i" << i << " = 0; i" << i << " < " << fOutputShape[i].GetVal() << "; ++i" << i <<") {\n";
238 }
239
240 out << SP << SP << SP << "int idxOut = ";
241 for (int k = 0; k < fAxis; k++) {
242 if (k > 0) out << " + ";
243 out << outStride[k].GetVal() << "*i" << k;
244 }
245 out << ";\n";
246
247 for (size_t j = 0; j < fInputs.size(); j++) {
248 if (j>0)
249 out << SP << SP << SP << "idxOut += " << fInputShapes[j-1][fAxis].GetVal() << ";\n";
250 out << SP << SP << SP << "int idxIn" << j <<" = ";
251 for (int k = 0; k < fAxis; k++) {
252 if (k > 0) out << " + ";
253 out << inStrides[j][k].GetVal() << "*i" << k;
254 }
255 out << ";\n";
256 out << SP << SP << SP << "for (size_t iC = 0; iC < " << fInputShapes[j][fAxis].GetVal() << "; ++iC) {\n";
257 out << SP << SP << SP << SP << "tensor_" << fOutput << "[idxOut+iC] = tensor_" << fInputs[j] << "[idxIn" << j << "+iC];\n";
258 out << SP << SP << SP << "}\n";
259 // concatenate the axis values
260 }
261 for (int i = 0; i < fAxis; ++i) {
262 out << SP << "}\n";
263 }
264 }
265
266 return out.str();
267 }
268 };
269 }//SOFIE
270 }//Experimental
271 }//TMVA
272
273 #endif //TMVA_SOFIE_ROPERATOR_CONCAT
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
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 ETensorType & GetTensorType(std::string name)
Definition RModel.cxx:94
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:203
std::vector< Dim > GetDynamicTensorShape(std::string name)
Definition RModel.cxx:82
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:178
bool IsInitializedTensor(const std::string &name) const
Definition RModel.cxx:188
const std::vector< size_t > & GetTensorShape(std::string name)
Definition RModel.cxx:56
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:264
void SetNotWritableInitializedTensor(const std::string &tensor_name)
Definition RModel.cxx:273
std::vector< std::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > inputs)
std::vector< std::vector< Dim > > fInputShapes
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input)
ROperator_Concat(std::vector< std::string > inputs, int axis, int newAxis, std::string output)
std::vector< std::vector< Dim > > ShapeInference(const std::vector< std::vector< Dim > > &inputs)
bool fIsOutputConstant
flag to identify if operator has a constant output (no need to generate code)
Definition ROperator.hxx:43
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:41
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 ConvertDynamicShapeToLength(std::vector< Dim > shape)
std::string ConvertValuesToString(size_t n, const T *data)
std::string ConvertShapeToString(std::vector< size_t > shape)
std::string ConvertDynamicShapeToString(std::vector< Dim > shape)
std::vector< size_t > ConvertShapeToInt(std::vector< Dim > shape)
Convert shape based on Dim to integer format.
std::size_t ConvertShapeToLength(std::vector< size_t > shape)
create variable transformations
static void output()