Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Reduce.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_Reduce
2#define TMVA_SOFIE_ROPERATOR_Reduce
3
5#include "TMVA/ROperator.hxx"
6#include "TMVA/RModel.hxx"
7
8#include <memory>
9#include <sstream>
10#include <algorithm>
11#include <stdexcept>
12#include <vector>
13#include <cassert>
14
15namespace TMVA{
16namespace Experimental{
17namespace SOFIE{
18
20
21template <typename T, EReduceOpMode Op>
22class ROperator_Reduce final : public ROperator
23{
24private:
25 /* Attributes*/
26 int fkeepdims = 1; //default value
27 std::vector<int64_t> fAttrAxes;
29 std::string fNX;
30 std::string fNAxes;
31 std::string fNY;
32 std::vector<size_t> fShapeX;
33 std::vector<size_t> fShapeY;
34 std::vector<size_t> fShapeYNotPruned; // needed for fKeepdims=0
35
36
37public:
38
39 std::string Name() {
40 if (fReduceOpMode == ReduceMean) return "ReduceMean";
41 else if (fReduceOpMode == ReduceSumSquare ) return "ReduceSumSquare";
42 else if (fReduceOpMode == ReduceProd ) return "ReduceProd";
43 else if (fReduceOpMode == ReduceSum) return "ReduceSum";
44 return "Invalid";
45 }
46
48 ROperator_Reduce(int keepdims, std::vector<int64_t> attrAxes, std::string nameX, std::string nameAxes, std::string nameY):
49 fkeepdims(keepdims), fAttrAxes(attrAxes), fNX(UTILITY::Clean_name(nameX)), fNAxes(UTILITY::Clean_name(nameAxes)), fNY(UTILITY::Clean_name(nameY)) {
50 fReduceOpMode = Op;
51 }
52
53 // type of output given input
54 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input){
55 return input;
56 }
57
58 // shape of output tensors given input tensors
59 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> input){
60 auto ret = input; //suggest copy to compiler
61 auto & outputShape = ret[0];
62 for (size_t j = 0; j < fAttrAxes.size(); j++) {
63 if (fAttrAxes[j] < 0) fAttrAxes[j] += outputShape.size();
64 if (fAttrAxes[j] < 0 || (size_t) fAttrAxes[j] >= outputShape.size() )
65 throw std::runtime_error("TMVA SOFIE Reduce Op - invalid axes values " + std::to_string(fAttrAxes[j]));
66 // set to 1 the reduced dims
67 outputShape[fAttrAxes[j]] = 1;
68 }
69 fShapeYNotPruned = outputShape;
70 // in case of pruning dimension we need to sort axes attributes
71 if (fkeepdims == 0) {
72 auto ax = fAttrAxes;
73 std::sort(ax.begin(), ax.end());
74 for (size_t j = 0; j < ax.size(); j++) {
75 // erase reduced dimensions, but keep last one
76 if (outputShape.size() > 1) {
77 outputShape.erase(outputShape.begin() + ax[j]);
78 for (size_t k = j+1; k < ax.size(); k++)
79 ax[k] -= 1; // decrease by one since we have removed a value
80 }
81 }
82 }
83 return ret;
84 }
85 void Initialize(RModel &model) {
86
87 fUseSession = model.UseSession();
88
89 if (!model.CheckIfTensorAlreadyExist(fNX)) {
90 // input must be a graph input, or already initialized intermediate tensor
91 throw std::runtime_error("TMVA SOFIE Reduce Op Input Tensor " + fNX + " is not found in model");
92 }
93 fShapeX = model.GetTensorShape(fNX);
94 // check if tensor with axes is provided
95 if (!fNAxes.empty()) {
96 auto ax_shptr = model.GetInitializedTensorData(fNAxes);
97 auto ax_ptr = static_cast<int64_t *>(ax_shptr.get());
98 auto ax_shape = model.GetTensorShape(fNAxes);
99 size_t ax_length = ConvertShapeToLength(ax_shape);
100 fAttrAxes = std::vector<int64_t>(ax_ptr, ax_ptr+ax_length);
101 } else if (fAttrAxes.empty()) {
102 // in case no axes is passed assume full reduction
103 fAttrAxes.resize(fShapeX.size());
104 for (size_t i = 0; i < fAttrAxes.size(); i++)
105 fAttrAxes[i] = i;
106 }
107 // find shape of Y and add it in the list of intermediate tensors
110 if (model.Verbose()){
111 std::cout << Name() << " : " << fNX << " -> " << fNY << " shape " << ConvertShapeToString(fShapeY) << std::endl;
112 }
113 }
114
115 std::string Generate(std::string opName){
116 opName = "op_" + opName;
117 if (fShapeX.empty() || fShapeY.empty()) {
118 throw std::runtime_error("TMVA SOFIE Reduce Op called to Generate without being initialized first");
119 }
120
123
125 // output stride (or not pruned vector)
127
128 // write here according to size of shape
129 // in generation code can be done automatically
130 // i0 = i / stride0 % shape0; i1 = i / stride1 % shape1 and so on
131 // and we have for the inverse
132 // i = i0 * s0 + i1 * s1 + i2 * s2 + i3 * s3 ....
133
134 // don't need to divide by last stride s[n-1] since it is 1 by definition
135
136 std::stringstream out;
137 out << "\n//---- operator " << Name() << " " << opName << "\n";
138 // check where is reduced axes are first or last one. In these case we can do a faster implementation
139 enum EReduceDim {kFirst, kLast, kMiddle};
140 EReduceDim reduceDims = kLast;
141 int kmin = fShapeX.size()-fAttrAxes.size();
142 for (int k = fShapeX.size()-1; k >= kmin; k--) {
143 // if k is not a reduced axis is not last ones
144 if (std::find(fAttrAxes.begin(), fAttrAxes.end(), k) == fAttrAxes.end()) {
145 reduceDims = kMiddle;
146 break;
147 }
148 }
149 if (reduceDims == kMiddle) {
150 reduceDims = kFirst;
151 // check if at the beginning
152 for (size_t k = 0; k < fAttrAxes.size(); k++) {
153 // if k is not a reduced axis is not first ones
154 if (std::find(fAttrAxes.begin(), fAttrAxes.end(), k) == fAttrAxes.end()) {
155 reduceDims = kMiddle;
156 break;
157 }
158 }
159 }
160 size_t reducedLength = inputLength / outputLength;
161 if (reduceDims == kLast) {
162 //std::cout << "reduction for operator " << opName << " is last" << std::endl;
163 // new faster implementation using a single loop
164 // faster to loop first on reduced dimension and then output
165 // reset output tensors
166
167 // loop on output dimensions
168 out << SP << "for (size_t i = 0; i < " << outputLength << "; i++) {\n";
169 // loop on reduce dimensions
170 std::string startingValue = (fReduceOpMode == ReduceProd) ? "1" : "0";
171 out << SP << SP << "tensor_" << fNY << "[i] = " << startingValue << ";\n";
172 out << SP << SP << "for (size_t j = 0; j < " << reducedLength << "; j++) {\n";
173
175 out << SP << SP << SP << "tensor_" << fNY << "[i] *= tensor_" << fNX << "[i * " << reducedLength << " + j];\n";
177 out << SP << SP << SP << "tensor_" << fNY << "[i] += tensor_" << fNX << "[i * " << reducedLength << " + j];\n";
179 out << SP << SP << SP << "tensor_" << fNY << "[i] += tensor_" << fNX << "[i * " << reducedLength << " + j] * tensor_"
180 << fNX << "[i * " << reducedLength << " + j];\n";
181 out << SP << SP << "}\n"; // end j loop
183 out << SP << SP << "tensor_" << fNY << "[i] /= static_cast<float>(" << reducedLength << ");\n";
184
185 out << SP << "}\n"; // end i loop
186 } else if (reduceDims == kFirst) {
187 //std::cout << "reduction for operator " << opName << " is first" << std::endl;
188 // case reduction is at beginning
189 // reset output tensors
191 out << SP << "fTensor_" << fNY << ".assign(" << outputLength << ",1);\n";
192 else
193 out << SP << "fTensor_" << fNY << ".assign(" << outputLength << ",0);\n";
194
195 out << SP << "for (size_t i = 0; i < " << reducedLength << "; i++) {\n";
196 out << SP << SP << "for (size_t j = 0; j < " << outputLength << "; j++) {\n";
197
199 out << SP << SP << SP << "tensor_" << fNY << "[j] *= tensor_" << fNX << "[i * " << outputLength << " + j];\n";
201 out << SP << SP << SP << "tensor_" << fNY << "[j] += tensor_" << fNX << "[i * " << outputLength << " + j];\n";
203 out << SP << SP << SP << "tensor_" << fNY << "[j] += tensor_" << fNX << "[i * " << outputLength << " + j] * tensor_"
204 << fNX << "[i * " << outputLength << " + j];\n";
205 out << SP << SP << "}\n"; // end j loop
206 out << SP << "}\n"; // end i loop
208 out << SP << "for (size_t j = 0; i < " << outputLength << "; j++) {\n";
209 out << SP << SP << "tensor_" << fNY << "[j] /= static_cast<float>(" << reducedLength << ");\n";
210 out << SP << "}\n"; // end j loop
211 }
212 }
213 else
214 { // standard case
215 //std::cout << "reduction for operator " << opName << " is middle" << std::endl;
216 // reset output tensors
218 out << SP << "fTensor_" << fNY << ".assign(" << outputLength << ",1);\n";
219 else
220 out << SP << "fTensor_" << fNY << ".assign(" << outputLength << ",0);\n";
221
222 out << SP << "for (size_t i = 0; i < " << inputLength << "; i++) {\n";
223
224 size_t dim = fShapeX.size(); // this is the input dimension (e.g. 2, 3 or 4 or more)
225
226 // here we find output index
227 out << SP << SP << "size_t outputIndex = 0;\n";
228 for (size_t k = 0; k < dim; k++) {
229 if (std::find(fAttrAxes.begin(), fAttrAxes.end(), k) == fAttrAxes.end()) {
230 // do for not reducing axes
231 out << SP << SP << "size_t i_" << k << " = i / " << inputStrides[k] << " % " << fShapeX[k] << ";\n";
232 out << SP << SP << "outputIndex += i_" << k << " * " << outputStrides[k] << ";\n";
233 }
234 }
235 // now compute reduction
236 out << SP << SP << "// compute reduction....\n";
238 out << SP << SP << "tensor_" << fNY << "[outputIndex] *= tensor_" << fNX << "[i];\n";
240 out << SP << SP << "tensor_" << fNY << "[outputIndex] += tensor_" << fNX << "[i];\n";
241 else if (fReduceOpMode == ReduceSumSquare) {
242 out << SP << SP << "tensor_" << fNY << "[outputIndex] += tensor_" << fNX << "[i] * tensor_" << fNX
243 << "[i];\n";
244 }
245 out << SP << "}\n"; // end loop on input elements
246 // normalize for reduced mean
247 if (fReduceOpMode == ReduceMean) {
248 out << SP << "for (size_t i = 0; i < " << outputLength << "; i++) {\n";
249 out << SP << SP << "tensor_" << fNY << "[i] /= static_cast<float>(" << reducedLength << ");\n";
250 out << SP << "}\n";
251 }
252 }
253
254 return out.str();
255 }
256
257};
258
259}//SOFIE
260}//Experimental
261}//TMVA
262
263
264#endif //TMVA_SOFIE_ROPERATOR_Reduce
265
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
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
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:122
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
ROperator_Reduce(int keepdims, std::vector< int64_t > attrAxes, std::string nameX, std::string nameAxes, std::string nameY)
std::vector< std::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > input)
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input)
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:41
bool fUseSession
flag to identify if using the session class
Definition ROperator.hxx:42
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 ConvertShapeToString(std::vector< size_t > shape)
std::size_t ConvertShapeToLength(std::vector< size_t > shape)
create variable transformations