Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Conv.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_CONV
2#define TMVA_SOFIE_ROPERATOR_CONV
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
19template<typename T>
21{
22private:
23 bool fBroadcastBias = false;
24
25 std::string fAttrAutopad;
26 std::vector<size_t> fAttrDilations;
27 size_t fAttrGroup;
28 std::vector<size_t> fAttrKernelShape;
29 std::vector<size_t> fAttrPads;
30 std::vector<size_t> fAttrStrides;
31
32 std::string fNX;
33 std::string fNW;
34 std::string fNB;
35 std::string fNY;
36
37 std::string convK;
38 std::string imcol;
39
40 std::vector<Dim> fShapeX;
41 std::vector<size_t> fShapeW;
42 std::vector<size_t> fShapeB;
43 std::vector<Dim> fShapeY;
44
45 std::string fType;
46
47 size_t fDim; // dimension of the convolution
48
49
50public:
51
53
54 ROperator_Conv(std::string autopad, std::vector<size_t> dilations,
55 size_t group, std::vector<size_t> kernelShape, std::vector<size_t> pads,
56 std::vector<size_t> strides, std::string nameX, std::string nameW,
57 std::string nameB, std::string nameY):
59 fAttrPads(pads), fAttrStrides(strides),
60 fNX(UTILITY::Clean_name(nameX)), fNW(UTILITY::Clean_name(nameW)),
61 fNB(UTILITY::Clean_name(nameB)), fNY(UTILITY::Clean_name(nameY))
62 {
63 if(std::is_same<T, float>::value) {
64 fType = "float";
65 } else {
66 throw
67 std::runtime_error("TMVA SOFIE Encountered unsupported type parsing a Conv operator");
68 }
71 }
72
73 ROperator_Conv(std::string autopad, std::vector<size_t> dilations,
74 size_t group, std::vector<size_t> kernelShape, std::vector<size_t> pads,
75 std::vector<size_t> strides, std::string nameX, std::string nameW,
76 std::string nameY):
78 fAttrPads(pads), fAttrStrides(strides),
79 fNX(UTILITY::Clean_name(nameX)), fNW(UTILITY::Clean_name(nameW)), fNY(UTILITY::Clean_name(nameY))
80 {
81 if(std::is_same<T, float>::value) {
82 fType = "float";
83 } else {
84 throw
85 std::runtime_error("TMVA SOFIE Encountered unsupported type parsing a Conv operator");
86 }
89 }
90
91 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override {
92 ETensorType out = input[0];
93 return {out};
94 }
95
96 // function returning output shape given input
97 std::vector<Dim> DoShapeInference(const std::vector<Dim> & input, const std::vector<size_t> & weight) {
98 // shape of convolution input has to be (according to ONNX): N x C x H x W
99 // Where N : batch size, C : input channels, H : input height, W : input width
100
101 if (input.size() -2 != fDim) {
102 throw std::runtime_error("TMVA SOFIE Conv Op Shape inference - invalid input ");
103 }
104 if (weight.size() -2 != fDim) {
105 throw std::runtime_error("TMVA SOFIE Conv Op Shape inference - invalid weights ");
106 }
107 if (fAttrGroup == 0 && input[1].isParam)
108 throw std::runtime_error("TMVA SOFIE Conv - param shapes not supported without group attr");
109 if (fAttrKernelShape.empty()) {
110 if (input[2].isParam || (fDim > 1 && input[3].isParam) || (fDim > 2 && input[4].isParam))
111 throw std::runtime_error("TMVA SOFIE Conv - param shapes not supported without kernel attr");
112 }
113
114 if (fAttrGroup == 0) {
115 fAttrGroup = input[1].dim / weight[1];
116 }
117
118 // kernel shape
119 size_t k1 = ((fAttrKernelShape.empty())? weight[2] : fAttrKernelShape[0]);
120 size_t k2 = (fDim > 1) ? ((fAttrKernelShape.empty()) ? weight[3] : fAttrKernelShape[1]) : 1;
121 size_t k3 = (fDim > 2) ? ((fAttrKernelShape.empty()) ? weight[4] : fAttrKernelShape[2]) : 1;
122
123
124 size_t i1 = (fDim > 1) ? ((fDim > 2) ? 3 : 2) : 1;
125 size_t i2 = (fDim > 2) ? 4 : 3;
126 size_t i3 = 5;
127
128 if (fAttrDilations.empty()) {
129 fAttrDilations = {1, 1, 1};
130 }
131 fAttrDilations.resize(3);
132 if (fDim < 3) {
133 fAttrDilations.resize(3, 1);
134 }
135 // Shape of the kernel
136 fAttrKernelShape = {k1 + (fAttrDilations[0] - 1) * (k1 - 1),
137 k2 + (fAttrDilations[1] - 1) * (k2 - 1),
138 k3 + (fAttrDilations[2] - 1) * (k3 - 1)};
139
140 if (fAttrStrides.empty()) {
141 fAttrStrides = {1, 1, 1};
142 }
143 if (fDim < 3)
144 fAttrStrides.resize(3, 1);
145
146 if (fAttrAutopad == "NOTSET") {
147 if (fAttrPads.empty()) {
148 fAttrPads = {1, 1, 1, 1, 1, 1};
149 }
150 } else if (fAttrAutopad == "SAME_UPPER" || fAttrAutopad == "SAME_LOWER") {
151 for (size_t d = 0; d < fDim; ++d) {
152 if (input[d + 2].isParam)
153 throw std::runtime_error(
154 "TMVA SOFIE Conv Op: SAME padding with parametric input shape is not supported");
155 }
156 // ONNX SAME padding: total_pad = max(0, (ceil(in/stride)-1)*stride + kernel - in)
157 // SAME_UPPER places extra padding at end, SAME_LOWER at beginning
158 fAttrPads.assign(6, 0);
159 for (size_t d = 0; d < fDim; ++d) {
160 size_t inSize = input[d + 2].dim;
161 size_t stride_d = fAttrStrides[d];
162 size_t outSize = (inSize + stride_d - 1) / stride_d;
163 int totalPad = std::max(0, (int)((outSize - 1) * stride_d + fAttrKernelShape[d]) - (int)inSize);
164 if (fAttrAutopad == "SAME_UPPER") {
165 fAttrPads[d] = (size_t)(totalPad / 2);
166 fAttrPads[d + fDim] = (size_t)(totalPad - totalPad / 2);
167 } else {
168 fAttrPads[d] = (size_t)(totalPad - totalPad / 2);
169 fAttrPads[d + fDim] = (size_t)(totalPad / 2);
170 }
171 }
172 } else if (fAttrAutopad != "VALID") {
173 throw
174 std::runtime_error("TMVA SOFIE Conv Op invalid fAutopad");
175 }
176 // to be sure pad is vector of size 6
177 if (fDim < 3) fAttrPads.resize(6, 0);
178
179 Dim input1 = input[2];
180 Dim input2 = (fDim > 1) ? input[3] : Dim{1};
181 Dim input3 = (fDim > 2) ? input[4] : Dim{1};
182
183 size_t pad1 = fAttrPads[0] + fAttrPads[i1];
184
185 // function to get output dimension of convolution given input
186
187 auto computeOutput = [&](Dim inputDim, size_t kernel, size_t pad, size_t stride) {
188 if (!inputDim.isParam) {
189 size_t outSize = (inputDim.dim + pad - kernel) / stride + 1;
190 return Dim{outSize};
191 } else {
192 if (stride == 1){
193 if ((pad - kernel + 1) == 0 )
194 // output is same as input
195 return inputDim;
196 else {
197 int64_t v = pad - kernel + 1;
198 std::string outStr = "(" + inputDim.param + "+" + std::to_string(v) + ")";
199 return Dim{ outStr, static_cast<size_t>(-1)};
200 }
201 } else { // general case (stride not 1)
202 int64_t v = pad - kernel;
203 std::string outStr =
204 "((" + inputDim.param + "+" + std::to_string(v) + ")/" + std::to_string(stride) + "+1)";
205 return Dim{ outStr, static_cast<size_t>(-1)};
206 }
207 }
208 throw std::runtime_error("TMVA SOFIE Conv Op - invalid values");
209 return Dim{};
210 };
211
213
214 Dim batch_size = input[0]; // first element in input tensor
215 Dim output_channels = Dim{weight[0]}; // first element in weight tensor
216
217 std::vector<Dim> ret({ batch_size, output_channels, output1 });
218
219 if (fDim == 1)
220 return ret;
221
222 size_t pad2 = fAttrPads[1] + fAttrPads[i2];
224
225 // output is N x M x OH x OW
226 ret.push_back(output2);
227 if (fDim == 2)
228 return ret;
229
230 size_t pad3 = fAttrPads[2] + fAttrPads[i3];
232
233 // output is N x M x OH x OW x OD
234 ret.push_back(output3);
235 return ret;
236 }
237
238 void Initialize(RModel& model) override {
239 fUseSession = model.UseSession();
240 if (!model.CheckIfTensorAlreadyExist(fNX)) {
241 throw
242 std::runtime_error("TMVA SOFIE Conv op Input Tensor " + fNX + " is not found in model");
243 }
245 if (fShapeX.size() < 3 || fShapeX.size() > 5) {
246 std::cout << fNX << " : " << ConvertDimShapeToString(fShapeX) << std::endl;
247 throw
248 std::runtime_error("TMVA SOFIE Conv Op input data tensor" + fNX + " is not of 3,4 or 5 dimensions");
249 }
250 fDim = fShapeX.size() - 2;
251 if (!model.CheckIfTensorAlreadyExist(fNW)) {
252 throw
253 std::runtime_error("TMVA SOFIE Conv op Input weight Tensor " + fNW + " is not found in model");
254 }
255 fShapeW = model.GetTensorShape(fNW);
256 if (fShapeW.size() < 3 || fShapeW.size() > 5) {
257 std::cout << fNW << " : " << ConvertShapeToString(fShapeW) << std::endl;
258 throw std::runtime_error("TMVA SOFIE Conv Op input weight tensor" + fNW + " is not of 3,4 or 5 dimensions");
259 }
262 if (fNB != "") {
263 if (!model.CheckIfTensorAlreadyExist(fNB)) {
264 throw
265 std::runtime_error("TMVA SOFIE Conv op Input Tensor " + fNB + " is not found in model");
266 }
267 fShapeB = model.GetTensorShape(fNB);
268 if (fShapeB.size() != 1)
269 throw
270 std::runtime_error("TMVA SOFIE Conv op : invalid shape for Bias tensor (is not 1D)");
271 std::vector<Dim> targetShape(fShapeY.begin() + 1, fShapeY.end());
272 auto shapeDimB = model.GetDimTensorShape(fNB);
274 if (broadcast_needed) {
276 // make bias shape equal to Y shape by adding 1
277 if (fShapeB.size() < 1)
278 throw std::runtime_error("TMVA SOFIE Conv op: Bias Tensor has empty shape");
279 // we assume bias tensor dimension is equal to number of filters that is the second dimension in
280 // the output tensor
281 if (!(shapeDimB[0] == fShapeY[1]))
282 throw std::runtime_error("TMVA SOFIE Conv op: Bias Tensor has wrong shape: " +
284 if (fType != "float")
285 throw std::runtime_error("TMVA SOFIE Conv op: Broadcasting for non-float type tensors is not supported");
286 // here is the actual broadcasting
287 fBroadcastBias = true;
288 if (!fUseSession) {
289 // do here broadcasting
290 std::vector<size_t> shape(fDim + 1, 1);
291 shape[0] = fShapeB[0];
293 std::shared_ptr<void> new_data_ptr(
294 UTILITY::UnidirectionalBroadcast(static_cast<float *>(original_data.get()), shape, intTargetShape),
295 std::default_delete<float[]>());
297 fShapeB = model.GetTensorShape(fNB);
298 }
299 }
300 }
301 // output channel size can be parametric and is an expression
302 std::vector<Dim> outputDims = std::vector<Dim>(fShapeY.begin()+2, fShapeY.end());
303 //check if shape is not parametric
304 std::vector<size_t> outputInts = ConvertShapeToInt(outputDims);
306 if (outputInts.empty()) {
307 auto outputChannelSize = ConvertDimShapeToLength(outputDims); // size/channel = D * H * W
308 channelDim = Dim{ outputChannelSize, static_cast<size_t>(-1)};
309 } else {
312 }
313 size_t kernelSize = fAttrKernelShape[0];
314 for (size_t i = 1; i < fDim; i++) {
316 }
317
318 std::vector<size_t> shape1 = {fShapeW[0], fShapeW[1], kernelSize};
319 std::vector<Dim> shape2 = {Dim{fShapeW[1]}, Dim{kernelSize}, channelDim };
322 convK = fNX +"_f";
323 imcol = fNX +"_xcol";
324 fOutputTensorNames.emplace_back(convK);
325 fOutputTensorNames.emplace_back(imcol);
326 fInputTensorNames.emplace_back(convK);
327 fInputTensorNames.emplace_back(imcol);
328
329 if (model.Verbose()) {
330 std::cout << "Conv - " << fDim << " " << fNX << " : " << ConvertDimShapeToString(fShapeX)
331 << " --> " << fNY << " : " << ConvertDimShapeToString(fShapeY) << std::endl;
332 }
333
334 // register the inference helper functions used by the generated code
335 if (fDim < 3)
336 model.AddNeededHelperFunction("Im2col");
337 else
338 model.AddNeededHelperFunction("Im2col_3d");
339 model.AddNeededHelperFunction("Gemm_Call");
340 if (fBroadcastBias)
341 model.AddNeededHelperFunction("UnidirectionalBroadcast");
342 }
343
344 std::string GenerateInitCode() override {
345 std::stringstream out;
346 // Generate initialization code for broadcasting of bias tensor
347 if (fBroadcastBias) {
348 // include a separate scope to avoid defining unique operator temp variables
349 std::vector<size_t> shape(fDim + 1, 1);
350 // bias (is a 1D tensor)
351 shape[0] = fShapeB[0];
352 std::vector<Dim> targetShape(fShapeY.begin() + 1, fShapeY.end());
353 out << "//--- broadcast bias tensor " << fNB << "for Conv op if needed \n";
354 // in case of dynamic tensors check needs to be done at run time
357 if (isOutDynamic)
358 out << SP << "if (" << length << " > " << ConvertShapeToLength(shape) << ") {\n";
359 else
360 out << SP << "{\n";
361 out << SP << SP << "float * data = UTILITY::UnidirectionalBroadcast(tensor_"
362 << fNB << ", " << ConvertShapeToString(shape) << ", " << ConvertDimShapeToString(fShapeY) << ");\n";
363 out << SP << SP << "fTensor_" << fNB << ".resize(" << length << ");\n";
364 out << SP << SP << "std::copy(data, data + " << length << ", fTensor_" << fNB << ".begin());\n";
365 out << SP << SP << "tensor_" << fNB << " = fTensor_" << fNB << ".data();\n";
366 out << SP << SP << "delete[] data;\n";
367 out << SP << "}\n";
368 }
369 return out.str();
370 }
371
372 std::string Generate(std::string OpName) override {
373 OpName = "op_" + OpName;
374
375 if (fShapeX.empty() || fShapeW.empty() || (fNB != "" && fShapeB.empty()) || fShapeY.empty()) {
376 throw
377 std::runtime_error("TMVA SOFIE Conv Op called to Generate without being initialized first");
378 }
379
380 std::stringstream out;
381 auto bsize = fShapeX[0];
382 size_t kDepth = (fDim > 2) ? fShapeW[2] : 1; // kernel depth
383 size_t kHeight = (fDim > 1) ? fShapeW[fDim] : 1; // kernel height
384 size_t kWidth = fShapeW[fDim+1]; // kernel width
385 auto iDepth = (fDim > 2) ? fShapeX[2] : Dim{1}; // input depth
386 auto iHeight = (fDim > 1) ? fShapeX[fDim] : Dim{1}; // input height
387 auto iWidth = fShapeX[fDim+1]; // input width
388 auto oDepth = (fDim > 2) ? fShapeY[2] : Dim{1}; // output depth
389 auto oHeight = (fDim > 1) ? fShapeY[fDim] : Dim{1}; // ouput height
390 auto oWidth = fShapeY[fDim+1]; // output width
391 // total output size for a channel
392 auto outputChannelStride = ConvertDimShapeToLength(std::vector<Dim>{oDepth, oHeight, oWidth}); // size of channel = D * H * W
393 auto outputBatchStride = ConvertDimShapeToLength(std::vector<Dim>{fShapeY[1] , oDepth, oHeight, oWidth}); // size of C * D * H * W
394 // input size
396 auto inputBatchStride = ConvertDimShapeToLength(std::vector<Dim>{fShapeX[1] , iDepth, iHeight, iWidth}); // size of C * D * H * W
397
398 out << "\n//---- operator Conv " << OpName << "\n";
399
400 // vectorize the (dilated)convolution kernels into a matrix
401 // no need to transpose the matrix
402 // to fix for 1d and 3d
403
404 size_t id = (fDim > 2) ? fDim-3 : 2;
405 size_t ih = (fDim > 1) ? fDim-2 : 1;
406 size_t iw = fDim-1;
407
408 size_t wstrideDil = fAttrDilations[iw];
409 size_t hstride = kWidth;
410 size_t hstrideDil = fAttrDilations[ih] * fAttrKernelShape[iw]; // stride dilated in the height
411 size_t dstride = kHeight * kWidth;
413 size_t icstride = kHeight * kWidth * kDepth;
415 size_t ocstride = fShapeW[1] * icstride;
416 size_t ocstrideDil = fShapeW[1] * icstrideDil;
417
418 out << SP << "for (std::size_t oc = 0; oc < " << fShapeW[0] << "; oc++) {\n";
419 out << SP << SP << "for (std::size_t ic = 0; ic < " << fShapeW[1] << "; ic++) {\n";
420 if (fDim > 2)
421 out << SP << SP << SP << "for (std::size_t kd = 0; kd < " << kDepth << "; kd++) {\n";
422 if (fDim > 1)
423 out << SP << SP << SP << "for (std::size_t kh = 0; kh < " << kHeight << "; kh++) {\n";
424 out << SP << SP << SP << SP << "for (std::size_t kw = 0; kw < " << kWidth << "; kw++) {\n";
425
426 out << SP << SP << SP << SP << SP << "tensor_" <<fNX << "_f[oc * "
427 << ocstrideDil << " + ic * " << icstrideDil;
428 if (fDim > 2) out << " + kd * " << dstrideDil;
429 if (fDim > 1) out << " + kh * " << hstrideDil;
430 out << " + kw * " << wstrideDil << " ] = tensor_" << fNW << "[oc * " << ocstride << " + ic * " << icstride;
431 if (fDim > 2) out << " + kd * " << dstride;
432 if (fDim > 1) out << " + kh * " << hstride;
433 out << " + kw ];\n";
434
435 out << SP << SP << SP << SP << "}\n";
436 if (fDim > 1) out << SP << SP << SP << "}\n";
437 if (fDim > 2) out << SP << SP << SP << "}\n";
438 out << SP << SP << "}\n";
439 out << SP << "}\n";
440
441 // Dilation is already folded into the expanded kernel shape and the dilated tensor_<X>_f
442 // layout above, so the dense im2col below must use dilation 1 to avoid double-counting it.
443 fAttrDilations = std::vector<size_t>(3, 1);
444
445 //out << SP << "char " << OpName << "_transA = 'T';\n";
446 out << SP << "char " << OpName << "_transA = 'N';\n";
447 out << SP << "char " << OpName << "_transB = 'N';\n";
448 out << SP << "int " << OpName << "_m = " << outputChannelStride << ";\n"; // output h*w
449 assert(fShapeY[1] == fShapeW[0]);
450 //assert(fShapeW[1] == fShapeX[1] / fAttrGroup);
451 out << SP << "int " << OpName << "_n = " << fShapeW[0] << ";\n"; // output channels
452 out << SP << "int " << OpName << "_k = " << fShapeW[1] * fAttrKernelShape[0] * fAttrKernelShape[1] * fAttrKernelShape[2] << ";\n";
453 out << SP << "float " << OpName << "_alpha = 1.0;\n";
454 if (fNB != "")
455 out << SP << "float " << OpName << "_beta = 1.0;\n";
456 else // when bias is not present beta needs to be equal to zero to avoid re-using previous results in output tensor
457 out << SP << "float " << OpName << "_beta = 0.0;\n";
458
459
460 // Loop on batch size
461 out << SP << "for (size_t n = 0; n < " << bsize << "; n++) {\n";
462
463 // IM2COL: Unroll the input tensor
464 // order input data as (e.g. kernel 2x2) and (xa,ya) is channel 1 and (xb,yb) is channel 2
465 // (xa1,..,xak,ya1,..yak)(xb1,...,xbk,yb1,..,ybk)
466 // (xa2,...xak+1,ya1,...yak)(......)
467 // trick for speed is using caffe im2col and output a matrix which contains filtered values as rows.
468 // By doing this one has consecutive memory reads and writes
469 // Resulting matrix op_xcol is (input channels * filter_h * filter_w , output_h * output_w)
470 // fAttrPads holds the begin pads in [0, fDim) and the end pads in [fDim, 2 * fDim),
471 // which is the layout Im2col expects. They may differ: ONNX allows it through the
472 // "pads" attribute, and SAME_UPPER / SAME_LOWER produce it whenever the total
473 // padding along an axis is odd (an even kernel size).
474 if (fDim == 1) {
475 // the 1d case is emitted as a 2d one of height 1, for which stride_h is 1
476 fAttrStrides[1] = 1;
477 }
478 out << SP << SP << "size_t out_offset = n * " << outputBatchStride << ";\n";
479
480 if (fAttrGroup == 1) {
481 out << SP << SP << "size_t x_offset = n * " << inputBatchStride << ";\n";
482 // when using im2col - resulting matrix is transposed, the dimension is (input_c * filter_h * filter_y, output_h *
483 // output_w)
484 if (fDim < 3) {
485 out << SP << SP << "UTILITY::Im2col<float>(tensor_" << fNX
486 << " + x_offset,"
487 // channels, height, width, kernel_h, kernel_w, pad_h_begin, pad_h_end, pad_w_begin,
488 // pad_w_end, stride_h, stride_w, dilation_h, dilation_w,
489 //
490 << fShapeW[1] << "," << iHeight << "," << iWidth << ",";
491 if (fDim == 1)
492 out << "1, " << fAttrKernelShape[0] << ",0,0," << fAttrPads[0] << "," << fAttrPads[1] << ",1,"
493 << fAttrStrides[0] << ",1," << fAttrDilations[0];
494 else // dim ==2
495 out << fAttrKernelShape[0] << "," << fAttrKernelShape[1] << "," << fAttrPads[0] << ","
496 << fAttrPads[2] << "," << fAttrPads[1] << "," << fAttrPads[3]
497 << "," << fAttrStrides[0] << "," << fAttrStrides[1] << "," << fAttrDilations[0] << ","
498 << fAttrDilations[1];
499 out << "," << "tensor_" <<fNX << "_xcol);\n\n ";
500 } else {
501 // 3d im2col
502 out << SP << SP << "UTILITY::Im2col_3d<float>(tensor_" << fNX
503 << " + x_offset,"
504 // channels, d, h, w, k_d, k_h, k_w, pad_d_begin, pad_d_end, pad_h_begin, pad_h_end,
505 // pad_w_begin, pad_w_end, stride_d, stride_h, stride_w, dilation_d, dilation_h, dilation_w,
506 //
507 << fShapeW[1] << "," << iDepth << "," << iHeight << "," << iWidth << ","
508 << fAttrKernelShape[0] << "," << fAttrKernelShape[1] << "," << fAttrKernelShape[2] << ","
509 << fAttrPads[0] << "," << fAttrPads[3] << "," << fAttrPads[1] << "," << fAttrPads[4] << ","
510 << fAttrPads[2] << "," << fAttrPads[5] << ","
511 << fAttrStrides[0] << "," << fAttrStrides[1] << "," << fAttrStrides[2] << ","
512 << fAttrDilations[0] << "," << fAttrDilations[1] << "," << fAttrDilations[2] << ","
513 << "tensor_" << fNX << "_xcol);\n\n ";
514 }
515 // BLAS
516 out << SP << "Gemm_Call("
517 << "tensor_" << fNY << " + out_offset, false, false, "
518 << OpName << "_m, " << OpName << "_n, " << OpName << "_k, "
519 << OpName << "_alpha, " << "tensor_" << fNX << "_xcol, tensor_" << fNX << "_f, "
520 << OpName << "_beta, ";
521 if (fNB != "")
522 out << "tensor_" << fNB;
523 else
524 out << "nullptr";
525 out << ");\n";
526
527
528 // out << SP << SP << "BLAS::sgemm_(&" << OpName << "_transA, &" << OpName << "_transB, &" << OpName << "_m, &"
529 // << OpName << "_n, &" << OpName << "_k, &" << OpName << "_alpha, " << "tensor_" << fNX << "_xcol, &" << OpName
530 // << "_m,\n"; // use m if op_xcol is not transpose , otherwise k
531 // out << SP << SP << SP << "tensor_" << fNX << "_f, &" << OpName << "_k, &" << OpName << "_beta, tensor_" << fNY
532 // << " + out_offset, &" << OpName << "_m);\n";
533 } else {
534 // case of group convolution
535 // Unroll (IM2COL) the input tensor- make loop on groups and repeat operations (IM2COL + GEMM for each
536 // group)
537 // out << SP << SP << "size_t out_offset = n * " << fShapeY[1] * oDepth * oHeight * oWidth << ";\n";
538 out << SP << SP << "for (size_t g = 0; g < " << fAttrGroup << "; g++) {\n";
539 out << SP << SP << "size_t x_offset = n * " << inputBatchStride << " + g * "
540 << fShapeW[1] << " * " << inputChannelStride << ";\n ";
541 out << SP << SP << "size_t g_offset = g * " << fShapeW[0] << " * (" << outputChannelStride << ") / " << fAttrGroup << ";\n ";
542 out << SP << SP << "size_t out_offset = n * " << outputBatchStride << " + g_offset;\n";
543
544 if (fDim < 3) {
545 out << SP << SP << "UTILITY::Im2col<float>(tensor_" << fNX
546 << " + x_offset,"
547 // channels, height, width, kernel_h, kernel_w, pad_h_begin, pad_h_end, pad_w_begin,
548 // pad_w_end, stride_h, stride_w, dilation_h, dilation_w,
549 //
550 << fShapeW[1] << "," << iHeight << "," << iWidth << ",";
551 if (fDim == 1)
552 out << "1, " << fAttrKernelShape[0] << ",0,0," << fAttrPads[0] << "," << fAttrPads[1] << ",1,"
553 << fAttrStrides[0] << ",1," << fAttrDilations[0];
554 else // dim ==2
555 out << fAttrKernelShape[0] << "," << fAttrKernelShape[1] << "," << fAttrPads[0] << ","
556 << fAttrPads[2] << "," << fAttrPads[1] << "," << fAttrPads[3]
557 << "," << fAttrStrides[0] << "," << fAttrStrides[1] << "," << fAttrDilations[0] << ","
558 << fAttrDilations[1];
559 out << ", tensor_" << fNX << "_xcol);\n\n ";
560 } else {
561 // 3d im2col
562 out << SP << SP << "UTILITY::Im2col_3d<float>(tensor_" << fNX
563 << " + x_offset,"
564 // channels, d, h, w, k_d, k_h, k_w, pad_d_begin, pad_d_end, pad_h_begin, pad_h_end,
565 // pad_w_begin, pad_w_end, stride_d, stride_h, stride_w, dilation_d, dilation_h, dilation_w,
566 //
567 << fShapeW[1] << "," << iDepth << "," << iHeight << "," << iWidth << "," << fAttrKernelShape[0] << ","
568 << fAttrKernelShape[1] << "," << fAttrKernelShape[2] << "," << fAttrPads[0] << "," << fAttrPads[3]
569 << "," << fAttrPads[1] << "," << fAttrPads[4] << "," << fAttrPads[2] << "," << fAttrPads[5]
570 << "," << fAttrStrides[0] << "," << fAttrStrides[1] << "," << fAttrStrides[2]
571 << "," << fAttrDilations[0] << "," << fAttrDilations[1] << "," << fAttrDilations[2] << ",tensor_" << fNX
572 << "_xcol);\n\n ";
573 }
574
575 // BLAS
576 // n must be divided by the number of groups
577 out << SP << SP << SP << OpName << "_n = " << fShapeW[0] / fAttrGroup << ";\n";
578 // offset g must be g * k * n
579 out << SP << SP << SP << "size_t offset_f = g * "
581 << ";\n";
582
583 out << SP << "Gemm_Call("
584 << "tensor_" << fNY << " + out_offset, false, false, "
585 << OpName << "_m, " << OpName << "_n, " << OpName << "_k, "
586 << OpName << "_alpha, " << "tensor_" << fNX << "_xcol, tensor_" << fNX << "_f + offset_f, "
587 << OpName << "_beta, ";
588 if (fNB != "")
589 out << "tensor_" << fNB << " + g_offset";
590 else
591 out << "nullptr";
592 out << ");\n";
593
594 // out << SP << SP << "BLAS::sgemm_(&" << OpName << "_transA, &" << OpName << "_transB, &" << OpName << "_m, &"
595 // << OpName << "_n, &" << OpName << "_k, &" << OpName << "_alpha, tensor_" << fNX << "_xcol, &" << OpName
596 // << "_m,\n"; // use m if op_xcol is not transpose , otherwise k
597 // out << SP << SP << SP << "tensor_" << fNX << "_f + offset_f, &" << OpName << "_k, &" << OpName << "_beta, tensor_" << fNY
598 // << " + out_offset"
599 // << ", &" << OpName << "_m);\n";
600
601 out << SP << SP << "}\n"; // end of group loop
602 }
603
604 // if (fNB != "") {
605 // out << SP << "int " << OpName << "_size = " << outputBatchStride << ";\n";
606 // out << SP << "float " << OpName << "_gamma = 1.0;\n";
607 // out << SP << "int " << OpName << "_incx = 1;\n";
608 // out << SP << "int " << OpName << "_incy = 1;\n";
609
610 // out << SP << "BLAS::saxpy_(&" << OpName << "_size, &" << OpName << "_gamma, tensor_" << fNB << ", &"
611 // << OpName << "_incx, tensor_" << fNY << " + out_offset, &" << OpName << "_incy);\n";
612
613 // }
614 out << SP << "}\n"; // end of batch size loop
615
616 return out.str();
617 }
618
619 /*! \brief Returns the blas routines needed to compile the generated code
620 */
621 std::vector<std::string> GetBlasRoutines() override { return { std::string("Gemm"), std::string("Axpy") }; }
622};
623
624} // namespace SOFIE
625} // namespace Experimental
626} // namespace TMVA
627
628#endif
#define d(i)
Definition RSha256.hxx:102
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 length
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
void AddNeededHelperFunction(std::string name)
std::vector< size_t > GetTensorShape(const std::string &name) const
Definition RModel.cxx:64
std::vector< Dim > GetDimTensorShape(const std::string &name) const
Definition RModel.cxx:100
void AddIntermediateTensor(std::string tensor_name, ETensorType type, std::vector< Dim > dim_shape)
Definition RModel.cxx:311
bool CheckIfTensorAlreadyExist(std::string tensor_name)
Definition RModel.cxx:157
std::shared_ptr< void > GetInitializedTensorData(std::string tensor_name)
Definition RModel.cxx:376
ETensorType GetTensorType(std::string name) const
Definition RModel.cxx:125
void UpdateInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:367
std::string Generate(std::string OpName) override
ROperator_Conv(std::string autopad, std::vector< size_t > dilations, size_t group, std::vector< size_t > kernelShape, std::vector< size_t > pads, std::vector< size_t > strides, std::string nameX, std::string nameW, std::string nameB, std::string nameY)
std::vector< std::string > GetBlasRoutines() override
Returns the blas routines needed to compile the generated code.
void Initialize(RModel &model) override
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input) override
ROperator_Conv(std::string autopad, std::vector< size_t > dilations, size_t group, std::vector< size_t > kernelShape, std::vector< size_t > pads, std::vector< size_t > strides, std::string nameX, std::string nameW, std::string nameY)
std::vector< Dim > DoShapeInference(const std::vector< Dim > &input, const std::vector< size_t > &weight)
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:50
const std::string SP
space used to correctly indent the generated C++ code
Definition ROperator.hxx:45
bool fUseSession
flag to identify if using the session class
Definition ROperator.hxx:46
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:51
bool AreSameShape(const std::vector< size_t > &, const std::vector< size_t > &)
T * UnidirectionalBroadcast(const T *data, const std::vector< size_t > &shape, const std::vector< size_t > &targetShape)
std::string ConvertDimShapeToString(const std::vector< Dim > &shape)
std::size_t ConvertShapeToLength(const std::vector< size_t > &shape)
std::vector< size_t > ConvertShapeToInt(const std::vector< Dim > &shape)
Convert shape based on Dim to integer format.
ETensorType ConvertStringToType(std::string type)
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
std::string ConvertShapeToString(const std::vector< size_t > &shape)
create variable transformations