Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Pool.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_POOL
2#define TMVA_SOFIE_ROPERATOR_POOL
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 // structure conatin Pool attributes
21 std::string auto_pad = "NOTSET";
22 int ceil_mode = 0;
23 int count_include_pad = 0; // not for MaxPool
24 int storage_order = 0; // not for AveragePool
25 std::vector<size_t> dilations; // not for AveragePool
26 std::vector<size_t> kernel_shape;
27 std::vector<size_t> pads;
28 std::vector<size_t> strides;
29};
30
32
33template<typename T>
34class ROperator_Pool final : public ROperator
35{
36
37private:
38
40
44 std::string fAttrAutopad;
45 std::vector<size_t> fAttrDilations;
46 std::vector<size_t> fAttrKernelShape;
47 std::vector<size_t> fAttrPads;
48 std::vector<size_t> fAttrStrides;
49
50 std::string fNX;
51 std::string fNY;
52
53 std::vector<size_t> fShapeX;
54 std::vector<size_t> fShapeY;
55
56 std::string fType;
57
58 bool fUseSession = false;
59
60public:
61
62 std::string Name() {
63 if (fPoolMode == AveragePool) return "AveragePool";
64 if (fPoolMode == MaxPool) return "MaxPool";
65 return "Invalid";
66 }
67
69
70 ROperator_Pool(PoolOpMode mode, RAttributes_Pool attr, std::string nameX, std::string nameY)
71 : fPoolMode(mode), fAttrCeilMode(attr.ceil_mode), fAttrCountIncludePad(attr.count_include_pad),
72 fAttrStorageOrder(attr.storage_order), fAttrAutopad(attr.auto_pad),
73 fAttrDilations(attr.dilations), fAttrKernelShape(attr.kernel_shape), fAttrPads(attr.pads), fAttrStrides(attr.strides),
74 fNX(UTILITY::Clean_name(nameX)), fNY(UTILITY::Clean_name(nameY))
75 {
76 if(std::is_same<T, float>::value) {
77 fType = "float";
78 } else {
79 throw
80 std::runtime_error("TMVA SOFIE Encountered unsupported type parsing a Pool operator");
81 }
82 }
83
84 // return input type (defined abstract in ROperator class )
85 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) {
86 // only one input in Pool operators
87 return input;
88 }
89
90 // function returning output shape given input
91 std::vector<std::vector<size_t>> ShapeInference(std::vector<std::vector<size_t>> input) {
92 // shape of pooling input has to be (according to ONNX): NxCxHxW
93 // Where N is batch size, C : input channels, H : input height, W = input width
94 // or it can be [N, C, F1,F2,....FN] . Minimum dimension is 3
95 if (input.size() != 1 ) {
96 throw std::runtime_error("TMVA SOFIE" + Name() + "Op Shape inference need 1 input tensor");
97 }
98 if (input[0].size() < 3) {
99 throw std::runtime_error("TMVA SOFIE" + Name() + "Op Shape inference only accept tensor with at leat 3 dimensions");
100 }
101 // for the time being support only 4 dimens
102 if (input[0].size() != 4) {
103 throw std::runtime_error("TMVA SOFIE" + Name() + "Op : tensors with dimension " + std::to_string(input[0].size()) + " are not yet supported");
104 }
105
106 assert(!fAttrKernelShape.empty());
107 size_t kHeight = fAttrKernelShape[0];
108 size_t kWidth = fAttrKernelShape[1];
109
110 if (fAttrDilations.empty()) {
111 fAttrDilations = {1, 1};
112 }
113 // Shape of the kernel
114 fAttrKernelShape = {kHeight + (fAttrDilations[0] - 1) * (kHeight - 1), kWidth + (fAttrDilations[1] - 1) * (kWidth - 1)};
115
116 if (fAttrAutopad == "NOTSET") {
117 if (fAttrPads.empty()) {
118 fAttrPads = {1, 1, 1, 1};
119 }
120 } else if (fAttrAutopad == "SAME_UPPER" || fAttrAutopad == "SAME_LOWER") {
122 if (fAttrKernelShape[0] % 2 == 1) {
123 (fAttrAutopad == "SAME_UPPER") ? fAttrPads[0]++ : fAttrPads[2]++;
124 }
125 if (fAttrKernelShape[1] % 2 == 1) {
126 (fAttrAutopad == "SAME_UPPER") ? fAttrPads[1]++ : fAttrPads[3]++;
127 }
128 } else if (fAttrAutopad != "VALID") {
129 throw
130 std::runtime_error("TMVA SOFIE" + Name() + "Op invalid Autopad value : " + fAttrAutopad);
131 }
132
133 if (fAttrStrides.empty()) {
134 fAttrStrides = {1, 1};
135 }
136
137 size_t outputHeight =
138 (input[0][2] + fAttrPads[0] + fAttrPads[2] - fAttrKernelShape[0] + fAttrStrides[0]) /
139 fAttrStrides[0];
140 size_t outputWidth =
141 (input[0][3] + fAttrPads[1] + fAttrPads[3] - fAttrKernelShape[1] + fAttrStrides[1]) /
142 fAttrStrides[1];
143
144 // output is N x M x OH x OW
145 std::vector<std::vector<size_t>> ret({{input[0][0], input[0][1], outputHeight, outputWidth}});
146 return ret;
147 }
148
149 void Initialize(RModel& model) {
150 if (!model.CheckIfTensorAlreadyExist(fNX)) {
151 throw
152 std::runtime_error("TMVA SOFIE Conv op Input Tensor " + fNX + " is not found in model");
153 }
154 fShapeX = model.GetTensorShape(fNX);
155 if (fShapeX.size() != 4) {
156 throw
157 std::runtime_error("TMVA SOFIE Conv Op input tensor" + fNX + " is not of 4 dimensions");
158 }
159
160 // case of GlobalAveragePool. It is a pool case with kernel shape == image shape
163 fAttrKernelShape.resize(2);
166 fAttrAutopad = "VALID";
167 fAttrPads = {0, 0, 0, 0};
168 assert(fAttrStrides.empty());
169 }
170 // find shape of Y and add it in the list of intermidiate tensors
173
174 fUseSession = model.UseSession();
175
176 // need cmath for INFINITY when using MaxPool
177 if (fPoolMode == MaxPool) model.AddNeededStdLib("cmath");
178
179 }
180
181 std::string GenerateInitCode() {
182 std::stringstream out;
183 return out.str();
184 }
185
186 // generate code for Session data members (e.g. internal vectors)
187 virtual std::string GenerateSessionMembersCode(std::string opName)
188 {
189 opName = "op_" + opName;
190 std::stringstream out;
191 // input matrix padded with zero
192 out << "std::vector<" << fType << "> fVec_" << opName << "_xpad = std::vector<" << fType << ">("
193 << fShapeX[1] * (fShapeX[2] + fAttrPads[0] + fAttrPads[2]) *
194 (fShapeX[3] + fAttrPads[1] + fAttrPads[3])
195 << ");\n";
196
197 return out.str();
198 }
199
200 std::string Generate(std::string OpName) {
201 OpName = "op_" + OpName;
202
203 if (fShapeX.empty() || fShapeY.empty()) {
204 throw std::runtime_error("TMVA SOFIE Pool Op called to Generate without being initialized first");
205 }
206
207 std::stringstream out;
208
209 out << "\n//---- operator " << Name() << " " << OpName << "\n";
210 out << "{\n"; // create a new scope to avoid name clash
211
212 // vectorize the (dilated)convolution kernels into a matrix
213 // no need to transpose the matrix
214 // size_t hstride = fShapeW[3];
215 // size_t hstrideDil = fAttrDilations[0] * fAttrKernelShape[1]; // stride dilated in the height
216 // size_t wstrideDil = fAttrDilations[1];
217 // size_t dstride = fShapeW[2] * fShapeW[3];
218 // size_t dstrideDil = fAttrKernelShape[0] * fAttrKernelShape[1];
219 // size_t kstride = fShapeW[1] * fShapeW[2] * fShapeW[3];
220 // size_t kstrideDil = fShapeW[1] * dstrideDil;
221
222
223 assert(fShapeX[0] == fShapeY[0]);
224 assert(fShapeX[1] == fShapeY[1]);
225 assert(fAttrPads.size() == 4);
226 assert(fAttrKernelShape.size() == 2);
227 // find lower bounds of filtered area
228 int hmin = - fAttrPads[0]; // minimum lower bound value of filter area
229 int hmax = fShapeX[2] + fAttrPads[1] - fAttrKernelShape[0] +1; // maximum lower bound value + 1
230 int wmin = - fAttrPads[2]; // minimum lower bound value of filter area
231 int wmax = fShapeX[3] + fAttrPads[3] - fAttrKernelShape[1] +1; // maximum lower bound value + 1
232 out << SP << "constexpr int hsize = " << fShapeX[2] << ";\n";
233 out << SP << "constexpr int wsize = " << fShapeX[3] << ";\n";
234 out << SP << "constexpr int hmin = " << hmin << ";\n";
235 out << SP << "constexpr int hmax = " << hmax << ";\n";
236 out << SP << "constexpr int wmin = " << wmin << ";\n";
237 out << SP << "constexpr int wmax = " << wmax << ";\n";
238 out << SP << "constexpr int kh = " << fAttrKernelShape[0] << ";\n";
239 out << SP << "constexpr int kw = " << fAttrKernelShape[1] << ";\n";
240
241 bool doPadding = false;
242 for ( auto & e : fAttrPads)
243 doPadding |= (e > 0);
244
245 // loop on batches and channels
246 out << SP << "size_t outIndex = 0;\n";
247 out << SP << "for (size_t n = 0; n < " << fShapeX[0]*fShapeX[1] << "; n++) {\n";
248 out << SP << SP << "size_t inputOffset = n*" << fShapeX[2]*fShapeX[3] << ";\n";
249 out << SP << SP << "for (int i = hmin; i < hmax; i+=" << fAttrStrides[0] << ") {\n";
250 out << SP << SP << SP << "for (int j = wmin; j < wmax; j+=" << fAttrStrides[1] << ") {\n";
251 // loop on elements of filter region to compute maximum
252 if (fPoolMode == MaxPool)
253 out << SP << SP << SP << SP << "float value = -INFINITY;\n";
254 else if (fPoolMode == AveragePool) {
255 out << SP << SP << SP << SP << "float value = 0;\n";
256 if (fAttrCountIncludePad == 0 && doPadding)
257 out << SP << SP << SP << SP << "int nsum = 0;\n";
258 else // in case we count the pad values in average
259 out << SP << SP << SP << SP << "constexpr int nsum = kw*kh;\n";
260 }
261 // loop on rows of filtered region
262 out << SP << SP << SP << SP << "for (int l = i; l < i + kh; l++) {\n";
263 out << SP << SP << SP << SP << SP << "if (l < 0 || l >= hsize) continue;\n";
264 // loop on columns of filtered region
265 out << SP << SP << SP << SP << SP << "for (int k = j; k < j + kw; k++) {\n";
266 out << SP << SP << SP << SP << SP << SP << "if (k<0 || k>= wsize) continue;\n";
267 out << SP << SP << SP << SP << SP << SP << "int index = inputOffset + l*hsize + k;\n";
268 if (fPoolMode == MaxPool) {
269 out << SP << SP << SP << SP << SP << SP << "auto xval = tensor_" << fNX << "[index];\n";
270 out << SP << SP << SP << SP << SP << SP << "if (xval > value) value = xval;\n";
271 }
272 else if (fPoolMode == AveragePool) {
273 // compute sum of values
274 out << SP << SP << SP << SP << SP << SP << "value += tensor_" << fNX << "[index];\n";
275 if (fAttrCountIncludePad == 0 && doPadding)
276 // compute number of elements used for the average
277 out << SP << SP << SP << SP << SP << SP << "nsum++;\n";
278 }
279 out << SP << SP << SP << SP << SP << "}\n";
280 out << SP << SP << SP << SP << "}\n"; // end loop on region elements
281 if (fPoolMode == AveragePool) {
282 // compute average
283 out << SP << SP << SP << SP << "value /= float(nsum);\n";
284 }
285
286 out << SP << SP << SP << SP << "tensor_" << fNY << "[outIndex++] = value;\n";
287 out << SP << SP << SP << "}\n"; // end loop on j (columns of image)
288 out << SP << SP << "}\n"; // end loop on i (image rows)
289 out << SP << "}\n"; // end loop on c*b
290 // end scope
291 out << SP << "}\n";
292
293
294 return out.str();
295 }
296};
297
298} // namespace SOFIE
299} // namespace Experimental
300} // namespace TMVA
301
302
303#endif
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const ETensorType & GetTensorType(std::string name)
Definition RModel.cxx:70
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape)
Definition RModel.cxx:136
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:91
void AddNeededStdLib(std::string libname)
Definition RModel.hxx:77
const std::vector< size_t > & GetTensorShape(std::string name)
Definition RModel.cxx:49
std::string Generate(std::string OpName)
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input)
std::vector< std::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > input)
virtual std::string GenerateSessionMembersCode(std::string opName)
ROperator_Pool(PoolOpMode mode, RAttributes_Pool attr, std::string nameX, std::string nameY)
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:39
create variable transformations