Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Transpose.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_TRANSPOSE
2#define TMVA_SOFIE_ROPERATOR_TRANSPOSE
3
5#include "TMVA/ROperator.hxx"
6#include "TMVA/RModel.hxx"
7
8#include <sstream>
9#include <cassert>
10
11namespace TMVA{
12namespace Experimental{
13namespace SOFIE{
14
15
16
18{
19
20private:
21
22 std::vector<int64_t> fAttrPerm;
23
24 std::string fNX;
25 std::string fNY;
26 std::vector<Dim> fShapeX;
27 std::vector<Dim> fShapeY;
28
29public:
30
32 ROperator_Transpose(std::vector<int64_t> attr_perm, std::string nameData, std::string nameOutput):
33 fAttrPerm(attr_perm), fNX(UTILITY::Clean_name(nameData)), fNY(UTILITY::Clean_name(nameOutput)) {
36 }
37
38
39 template<class T>
41 // case input is a constant or initialized tensor we perform here the transpose
42 // here we know the shape
45 fIsOutputConstant = true;
46
50 auto inputData = static_cast<T *>(model.GetInitializedTensorData(fNX).get());
51 size_t dim = fShapeX.size();
52 std::vector<size_t> outputIdx(dim);
53 std::vector<T> outputData(length);
54 for (size_t i = 0; i < length; i++) {
55 outputIdx[0] = i / outStrides[0];
56 for (size_t j = 1; j < dim; j++) {
57 outputIdx[j] = (i % outStrides[j - 1]) / outStrides[j];
58 }
59 // compute input index
60 size_t inputIndex = 0;
61 for (size_t j = 0; j < dim; j++) {
62 // find value in fAtrrPerm corresponding to j
63 int k = std::find(fAttrPerm.begin(), fAttrPerm.end(), j) - fAttrPerm.begin();
65 }
67 }
68 model.AddConstantTensor<T>(fNY, shapeY, outputData.data());
69 if (model.Verbose()) {
70 std::cout << "Transpose: output is a constant tensor " << ConvertShapeToString(shapeY) << " : "
71 << ConvertValuesToString(outputData) << std::endl;
72 }
73 }
74
75 void Initialize(RModel& model) override {
76 if (model.CheckIfTensorAlreadyExist(fNX) == false){ //input must be a graph input, or already initialized intermediate tensor
77 std::cout<<"Input tensor for transpose: "<<fNX<<'\n';
78 throw std::runtime_error("TMVA SOFIE Tranpose Op Input Tensor is not found in model");
79 }
80 fShapeX = model.GetDimTensorShape(fNX);
81 if (fAttrPerm.empty()){
82 fAttrPerm.reserve(fShapeX.size());
83 for (int i = fShapeX.size() - 1; i >= 0; i--){
84 fAttrPerm.push_back(i);
85 }
86 }
87
88 // inference of output shape
89 if (fAttrPerm.size() != fShapeX.size() )
90 throw std::runtime_error("TMVA SOFIE Tranpose Op - Invalid axes attributes");
91
92 fShapeY.resize(fAttrPerm.size());
93 for (size_t i = 0; i < fAttrPerm.size(); i++){
94 fShapeY[i] = fShapeX[fAttrPerm[i]];
95 }
96
97 if (model.IsInitializedTensor(fNX) ) {
98 auto type = model.GetTensorType(fNX);
99 switch(type) {
102 break;
105 break;
108 break;
111 break;
112 default:
113 std::cout << "Transpose - no support for initialized tensor of type " << ConvertTypeToString(type) << std::endl;
114 }
115 return;
116 }
117 // case of intermediate tensors (non constant)
118 model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShapeY);
119 if (model.Verbose()) {
120 std::cout << "Transpose ---> " << fNY << " " << ConvertDimShapeToString(fShapeY) << std::endl;
121 }
122 }
123
124 std::string Generate(std::string opName) override {
125 if (fIsOutputConstant) return ""; //no op for constant tensors
126 opName = "op_" + opName;
127 if (fShapeX.empty() || fShapeY.empty()){
128 throw std::runtime_error("TMVA SOFIE Transpose Op called to Generate without being initialized first");
129 }
132
134 size_t rank = fShapeX.size();
135 bool isDynamic = (intShapeX.empty() && rank > 0);
136
137 std::string constQualifier = (isDynamic) ? "const" : "constexpr";
138
139 std::stringstream out;
140
141 out << SP << "///------- Transpose operator " << opName << ConvertDimShapeToString(fShapeX)
142 << " --> " << ConvertDimShapeToString(fShapeY) << std::endl;
143
144 // Implement more efficient implementation of transpose operator using strides
145 // For 2-dim rank tensors we could have an optimised implementation for rank = 2 tensors using Tiles
146
147 // General implementation : start pre-computing strides as const expr
148 // Emit strides for X (input) and Y (output) as constexpr
149 out << SP << "{\n";
150 out << SP << SP << "// Pre-baked input strides (row-major)\n";
151 out << SP << SP << constQualifier << " size_t " << opName << "_strX[] = {";
152 for (size_t i = 0; i < rank; ++i)
153 out << stridesX[i] << (i + 1 < rank ? ", " : "");
154 out << "};\n";
155
156 out << SP << SP << "// Pre-baked output strides (row-major)\n";
157 out << SP << SP << constQualifier << " size_t " << opName << "_strY[] = {";
158 for (size_t i = 0; i < rank; ++i)
159 out << stridesY[i] << (i + 1 < rank ? ", " : "");
160 out << "};\n\n";
161
162 // Check if last perm axis == rank-1 (contiguous inner axis fast path)
163 bool innerContiguous = (fAttrPerm.back() == (int64_t) (rank - 1));
164 size_t outerRank = innerContiguous ? rank - 1 : rank;
166 : 1;
167
168 if (innerContiguous && !isDynamic && innerSize > 1) {
169 // ---- Fast path: innermost axis is contiguous in source -----
170 out << SP << SP
171 << "// Fast path: last permuted axis is contiguous in source\n";
172 out << SP << SP
173 << "// Inner " << innerSize << " elements copied with pointer arithmetic\n";
174
175 // Nested loops over all axes except the last
177
178 // Compute flat src and dst offsets for the current outer indices
179 out << SP << SP << SP << "size_t src_off = ";
180 for (size_t i = 0; i < outerRank; ++i) {
181 out << "idx_" << i << " * " << opName << "_strX["
182 << fAttrPerm[i] << "]";
183 if (i + 1 < outerRank) out << " + ";
184 }
185 out << ";\n";
186
187 out << SP << SP << SP << "size_t dst_off = ";
188 for (size_t i = 0; i < outerRank; ++i) {
189 out << "idx_" << i << " * " << opName << "_strY[" << i << "]";
190 if (i + 1 < outerRank) out << " + ";
191 }
192 out << ";\n";
193
194 // Inner memcpy-style copy over the contiguous axis
195 out << SP << SP << SP
196 << "std::copy(tensor_" << fNX << " + src_off, "
197 << "tensor_" << fNX << " + src_off + " << innerSize << ", "
198 << "tensor_" << fNY << " + dst_off);\n";
199
201
202 } else {
203
204 // ---- General path: per-element index arithmetic -------------
205 out << SP << SP << "// General N-D transpose\n";
206
208
209 // Flat source index: sum over perm[i] * strideX[perm[i]]
210 out << SP << SP << SP << "size_t src_idx = ";
211 for (size_t i = 0; i < rank; ++i) {
212 out << "idx_" << i << " * " << opName << "_strX[" << fAttrPerm[i] << "]";
213 if (i + 1 < rank) out << " + ";
214 }
215 out << ";\n";
216
217 // Flat destination index: sum over i * strideY[i]
218 out << SP << SP << SP << "size_t dst_idx = ";
219 for (size_t i = 0; i < rank; ++i) {
220 out << "idx_" << i << " * " << opName << "_strY[" << i << "]";
221 if (i + 1 < rank) out << " + ";
222 }
223 out << ";\n";
224
225 out << SP << SP << SP
226 << "tensor_" << fNY << "[dst_idx] = "
227 << "tensor_" << fNX << "[src_idx];\n";
228
230
231 }
232
233 out << SP << "}\n";
234 return out.str();
235 }
236
237
238};
239
240}//SOFIE
241}//Experimental
242}//TMVA
243
244
245#endif //TMVA_SOFIE_ROPERATOR_TRANSPOSE
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 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
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 Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
std::string Generate(std::string opName) override
ROperator_Transpose(std::vector< int64_t > attr_perm, std::string nameData, std::string nameOutput)
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:44
bool fIsOutputConstant
flag to identify if operator has a constant output (no need to generate code)
Definition ROperator.hxx:41
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:40
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:45
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< size_t > ConvertShapeToInt(const std::vector< Dim > &shape)
Convert shape based on Dim to integer format.
std::string ConvertTypeToString(ETensorType type)
void EmitNestedLoops(std::stringstream &out, size_t loopRank, const std::vector< Dim > shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
void CloseNestedLoops(std::stringstream &out, size_t loopRank)
create variable transformations