Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_Slice.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_SLICE
2#define TMVA_SOFIE_ROPERATOR_SLICE
3
5#include "TMVA/ROperator.hxx"
6#include "TMVA/RModel.hxx"
7
8#include <cassert>
9#include <sstream>
10#include <numeric>
11
12namespace TMVA{
13namespace Experimental{
14namespace SOFIE{
15
16// slice operator
17
18template <typename IType>
20{
21
22private:
23
24 // flags to indicate if start/end and steps are not defined at compiled time
25 bool fIsStartUndef = false;
26 bool fIsEndUndef = false;
27 bool fIsStepUndef = false;
28 bool fIdentitySlice = false;
29 bool fIsAlias = false; // identity slice whose output shares the memory of the input
30 std::string fNData; // input data tensor name
31 std::string fNOutput; // output data name
32 std::vector<std::string> fNames; // tensor names for meta(axis) information
33 std::vector<Dim> fShapeInput; // input shape
34 std::vector<Dim> fShapeOutput; // output shape
35 std::vector<Dim> fOutputShapeData; // output shape data in case output is a shape param tensor
36
37 // saved Start/End.Steps are corrected from initial ONNX for negative/default values
38 // and are available for each axis
39 std::vector<Dim> fStart; // starting values of slices for all axes
40 std::vector<Dim> fEnd; // End values of slices for all axes
41 std::vector<Dim> fSteps; // step values of slices for all axes
42 std::vector<Dim> fStartDims; // input starting values of slices
43 std::vector<Dim> fEndDims; // input End values of slices
44 std::vector<Dim> fStepDims; // input step values of slices
45 std::vector<IType> fAxes; // axes for input start/emd/step values
46
47 std::vector<std::vector<IType>> fAttributes; // attributes for the version <=10 case
48
49
50public:
51
53
54 // ctor for versions >= 10
55 ROperator_Slice(std::string nameData, std::vector<std::string> names, std::string nameOutput)
56 : fNData(UTILITY::Clean_name(nameData)),
57 fNOutput(UTILITY::Clean_name(nameOutput))
58 {
59 fNames.resize(4);
60 // axes and steps can be optional
61 for (size_t i = 0; i < names.size(); ++i) {
62 fNames[i] = UTILITY::Clean_name(names[i]);
63 }
64
67 }
68 // ctor for versions < 10
69 ROperator_Slice(std::string nameData, std::vector<IType> starts, std::vector<IType> ends, std::vector<IType> axes, std::string nameOutput)
70 : fNData(UTILITY::Clean_name(nameData)),
71 fNOutput(UTILITY::Clean_name(nameOutput))
72 {
73 fAttributes.push_back(starts);
74 fAttributes.push_back(ends);
75 fAttributes.push_back(axes);
76 }
77
78
79
80 void Initialize(RModel& model) override {
81 if (model.CheckIfTensorAlreadyExist(fNData) == false){ //input must be a graph input, or already initialized intermediate tensor
82 throw std::runtime_error("TMVA Slice Op Input Tensor is not found in model");
83 }
84
85 std::vector<std::vector<Dim>> shapes;
86 fShapeInput = model.GetDimTensorShape(fNData);
87 shapes.push_back(fShapeInput);
88
89 std::vector<std::vector<IType>> itensors(4);
90
91 if (fNames.size() > 0) { // size has to be equal to 4
92 // loop on the extra 2 or 3 or 4 inputs
93 for (size_t i = 0; i < 4; ++i) {
94 if (!fNames[i].empty()) {
95 if (model.IsInitializedTensor(fNames[i])) {
96 auto dptr = model.GetInitializedTensorData(fNames[i]);
97 auto tensor = static_cast<IType *>(dptr.get());
98 auto vec = model.GetTensorShape(fNames[i]);
99 assert(vec.size() == 1);
100 itensors[i] = std::vector<IType>(tensor, tensor + vec[0]);
101
102 } else if (model.IsShapeTensor(fNames[i])) {
103 // case is a shape tensor
104 if (i == 0) {
105 fStartDims = model.GetShapeTensorValues(fNames[i]);
106 } else if (i == 1) {
107 fEndDims = model.GetShapeTensorValues(fNames[i]);
108 } else if (i == 3) {
109 fStepDims = model.GetShapeTensorValues(fNames[i]);
110 }
111 } else {
112 // case is an intermediate tensor
113 auto shape = model.GetTensorShape(fNames[i]);
114 size_t s = shape[0];
115 for (size_t k = 0; k < s; k++) {
116 if (i == 0) {
117 fStartDims.push_back( Dim{std::string("start_") + fNOutput + "_" + std::to_string(k)});
118 fIsStartUndef = true;
119 } else if (i == 1) {
120 fEndDims.push_back(Dim{std::string("end_") + fNOutput + "_" + std::to_string(k)});
121 fIsEndUndef = true;
122 } else if (i == 3) {
123 fStepDims.push_back(Dim{std::string("step_") + fNOutput + "_" + std::to_string(k)});
124 fIsStepUndef = true;
125 }
126 }
127 }
128 }
129 }
130 } else {
131 // old slice versions
132 assert(fAttributes.size() > 1);
133 for (size_t i = 0; i < fAttributes.size(); i++) {
134 itensors[i] = fAttributes[i];
135 }
136 }
137 size_t dim = fShapeInput.size();
138
139 // default values
140 fSteps = std::vector<Dim>(dim, Dim{1});
141 fStart = std::vector<Dim>(dim, Dim{0});
143
144 // default axes
145 if (itensors[2].empty()) {
146 fAxes.resize(dim);
147 std::iota(fAxes.begin(), fAxes.end(), 0);
148 } else {
149 fAxes = itensors[2];
150 for (size_t i = 0; i < fAxes.size(); i++) {
151 // negative axes - they count from the back
152 if (fAxes[i] < 0) fAxes[i] = dim + fAxes[i];
153 if (fAxes[i] < 0 || fAxes[i] >= static_cast<IType>(dim))
154 throw std::runtime_error("TMVA Slice Op : invalid axis value " + std::to_string(fAxes[i]) +
155 " for " + std::to_string(i));
156 }
157 }
158 // Loop on axis to get start/end/step values
159 for (size_t i = 0; i < fAxes.size(); i++) {
160 if (!itensors[0].empty() )
161 fStartDims.push_back(Dim{ static_cast<size_t>(itensors[0][i])});
162 if (fStartDims.empty())
163 throw std::runtime_error("TMVA Slice Op : Missing start input tensor");
164
165 if (!itensors[1].empty())
166 fEndDims.push_back(Dim{ static_cast<size_t>(itensors[1][i])});
167 else if (fEndDims.empty())
168 throw std::runtime_error("TMVA Slice Op : Missing end input tensor");
169
170 if (!itensors[3].empty()) {
171 fStepDims.push_back(Dim{ static_cast<size_t>(itensors[3][i])});
172 }
173 else if (fStepDims.size() < fAxes.size()) // this can happen since it is optional
174 fStepDims.push_back(Dim{size_t(1)});
175
176 if (!fShapeInput[fAxes[i]].isParam) {
177 size_t iAxisDim = fShapeInput[fAxes[i]].dim;
178 //correct values if too large or too small
179 IType istart = 0;
180 if (!fStartDims[i].isParam) {
181 istart = static_cast<IType>(fStartDims[i].dim);
182 if (istart < 0) istart = iAxisDim + istart;
183 }
184 IType iend = static_cast<IType>(iAxisDim);
185 if (!fEndDims[i].isParam) {
186 iend = static_cast<IType>(fEndDims[i].dim);
187 if (iend < 0) iend = iAxisDim + iend;
188 }
189 //steps
190 IType istep = 1;
191 if (!fStepDims[i].isParam) {
192 istep = static_cast<IType>(fStepDims[i].dim);
193 } else {
194 throw std::runtime_error("TMVA Slice Op : parametric step inputs are not supported");
195 }
196 // clamp start end values depending on steps
197 // start must be [0,N] for positive steps or [0,N-1] for negative
198 // end must be [0,N] for positive steps or [-1, N-1] for negative
199 if (istart < 0) istart = 0;
200 if (istep > 0) {
201 if (istart > static_cast<IType>(iAxisDim)) istart = static_cast<IType>(iAxisDim);
202 if (iend < 0) iend = 0;
203 if (iend > static_cast<IType>(iAxisDim)) iend = static_cast<IType>(iAxisDim);
204 } else if (istep < 0) {
205 if (istart > static_cast<IType>(iAxisDim)-1) istart = static_cast<IType>(iAxisDim) -1;
206 if (iend < -1) iend = -1;
207 if (iend > static_cast<IType>(iAxisDim)-1) iend = static_cast<IType>(iAxisDim) -1;
208 } else {
209 throw std::runtime_error("TMVA Slice Op : invalid step value " + std::to_string(istep) +
210 " for " + std::to_string(i));
211 }
212 // for parametric values clamping we will done at run time
213 if (fStartDims[i].isParam)
214 fStart[fAxes[i]] = fStartDims[i];
215 else
216 fStart[fAxes[i]] = Dim{size_t(istart)};
217 if (fStartDims[i].isParam)
218 fEnd[fAxes[i]] = fEndDims[i];
219 else
220 fEnd[fAxes[i]] = Dim{size_t(iend)};
221
222 fSteps[fAxes[i]] = Dim{size_t(istep)};
223 } else {
224 //std::cout << i << " Param dim for " << fAxes[i] << " " << fShapeInput[fAxes[i]] << std::endl;
225 // correct only negative values
226 if (!fStartDims[i].isParam) {
227 IType istart = static_cast<IType>(fStartDims[i].dim);
228 if (istart < 0) {
229 std::string sstart = std::string("(") + fShapeInput[fAxes[i]].param + "-" + std::to_string(-istart) +")";
230 fStart[fAxes[i]] = Dim{sstart,size_t(-1)};
231 } else {
232 fStart[fAxes[i]] = Dim{size_t(istart)};
233 }
234 } else {
235 fStart[fAxes[i]] = fStartDims[i];
236 }
237 if (!fEndDims[i].isParam) {
238 IType iend = static_cast<IType>(fEndDims[i].dim);
239 if (iend < 0) {
240 std::string send = std::string("(") + fShapeInput[fAxes[i]].param + "-" + std::to_string(-iend) +")";
241 fEnd[fAxes[i]] = Dim{send,size_t(-1)};
242 } else if (iend == std::numeric_limits<IType>::max()){
243 fEnd[fAxes[i]] = fShapeInput[fAxes[i]];
244 } else {
245 fEnd[fAxes[i]] = Dim{size_t(iend)};
246 }
247 } else {
248 fEnd[fAxes[i]] = fEndDims[i];
249 }
250
251 fSteps[fAxes[i]] = fStepDims[i];
252 }
253
254 }
255 // find output shape
256 fShapeOutput.resize(dim);
257 for (size_t i = 0; i < dim; i++) {
258 if (!fEnd[i].isParam && !fStart[i].isParam && !fSteps[i].isParam) {
259 int64_t istart = static_cast<int64_t>(fStart[i].dim);
260 int64_t iend = static_cast<int64_t>(fEnd[i].dim);
261 int64_t istep= static_cast<int64_t>(fSteps[i].dim);
262 int64_t s = (iend-istart)/istep;
263 fShapeOutput[i] = Dim{static_cast<size_t>(s)};
264 } else {
265 std::string s;
266 if (fStart[i].GetVal() != "0")
267 s = "(" + fEnd[i].GetVal() + "-" + fStart[i].GetVal() + ")";
268 else
269 s = fEnd[i].GetVal();
270 if (fSteps[i].GetVal() != "1") {
271 s.insert(0,"(");
272 s += ")/" + fSteps[i].GetVal() + ")";
273 }
274 fShapeOutput[i] = Dim{s,size_t(-1)};
275 // add also the shape parameters to RModel to declare them when
276 // allocating output tensor
277 if (fEnd[i].isParam && fEnd[i].dim != size_t(-1))
278 model.AddShapeParam(fEnd[i].param,fEnd[i].dim );
279 if (fStart[i].isParam && fStart[i].dim != size_t(-1))
280 model.AddShapeParam(fStart[i].param,fStart[i].dim );
281 if (fSteps[i].isParam && fSteps[i].dim != size_t(-1))
282 model.AddShapeParam(fSteps[i].param,fSteps[i].dim );
283
284 }
285 }
286 // case input is a constant tensor and of int64 type
287 if (model.IsInitializedTensor(fNData) && model.GetTensorType(fNData) == ETensorType::INT64) {
288 fIsOutputConstant = true;
289 auto inputData = static_cast<int64_t*>(model.GetInitializedTensorData(fNData).get());
290 size_t outputSize = ConvertShapeToLength(ConvertShapeToInt(fShapeOutput));
291 std::vector<int64_t> outputData(outputSize);
292 std::vector<size_t> inputStride = UTILITY::ComputeStrideFromShape(ConvertShapeToInt(fShapeInput));
293 if (model.Verbose()) {
294 std::cout << "Do slice for initialized input ..(start, end, step)\n";
295 for (size_t ii = 0; ii< fStart.size(); ii++)
296 std::cout << fStart [ii] << " " << fEnd[ii] << " " << fSteps[ii] << std::endl;
297 }
298 // perform slice using a recursive function- need to use two lambda functions for this
299 auto sliceRecursive = [&](size_t iaxis, size_t & outIdx, size_t & inOffset) {
300 auto slice_impl = [&](size_t iax, size_t & outputIdx, size_t & inputOffset, auto & sliceRecImpl) {
301 if (fStart[iax].isParam || fEnd[iax].isParam || fSteps[iax].isParam)
302 throw std::runtime_error("TMVA Slice Op : cannot have parametric values when input is constant");
303 // compute indices
304 std::vector<IType> indices;
305 for (IType i = (IType) fStart[iax].dim; (IType(fSteps[iax].dim) > 0) ? i < IType(fEnd[iax].dim) : i > IType(fEnd[iax].dim); i += IType(fSteps[iax].dim) )
306 indices.push_back(i);
307 if (iax == dim-1) { // last axis
308 for (size_t i = 0; i < indices.size(); i++) {
309 outputData[outputIdx] = inputData[inputOffset + indices[i]];
310 outputIdx++;
311 }
312 return;
313 } else {
314 for (size_t i = 0; i < indices.size(); i++) {
315 size_t offset = inputOffset + inputStride[iax]*indices[i];
317 }
318 }
319 };
321 };
322 size_t idx = 0;
323 size_t offset = 0;
324 sliceRecursive(0, idx, offset);
325
326 model.AddConstantTensor<int64_t>(fNOutput, ConvertShapeToInt(fShapeOutput), outputData.data());
327 if (model.Verbose()) {
328 std::cout << "Slice: output is a constant tensor " << ConvertDimShapeToString(fShapeOutput) << " : "
329 << ConvertValuesToString(outputData) << std::endl;
330 }
331 }
332 else if (model.IsShapeTensor(fNData) && !fStart[0].isParam && !fEnd[0].isParam) {
333 // case of input is a shape tensor. In this case rank=1 always, axis =0 and Slice is trivial
334 auto inputData = model.GetShapeTensorValues(fNData);
335 fOutputShapeData = std::vector<Dim>(inputData.begin() + fStart[0].dim, inputData.begin() + fEnd[0].dim);
336 // try to convert to integer values if possible
337 auto outputData = ConvertShapeToInt(fOutputShapeData);
338 fShapeOutput = { Dim{fOutputShapeData.size()}};
339 if (outputData.empty()) {
340 // is a param shape tensor
341 model.AddShapeTensor(fNOutput, fOutputShapeData);
342 fIsOutputParamShape = true;
343 if (model.Verbose()) {
344 std::cout << "Slice: output is a shape tensor -> " << fNOutput << " " << ConvertDimShapeToString(fShapeOutput) << " with values "
345 << ConvertDimShapeToString(fOutputShapeData) << " (shape)" << std::endl;
346 }
347 } else {
348 fIsOutputConstant = true;
349 std::vector<int64_t> data(outputData.size());
350 std::copy(outputData.begin(), outputData.end(), data.begin());
351 model.AddConstantTensor<int64_t>(fNOutput, {data.size()}, data.data());
352 if (model.Verbose()) {
353 std::cout << "Slice: output is a constant tensor -> " << fNOutput << " " << ConvertDimShapeToString(fShapeOutput) << " with values "
354 << ConvertDimShapeToString(fOutputShapeData) << " constant " << std::endl;
355 }
356 }
357 }
358 else {
359 // check if Slice is just an Identity operator in case start = 0, end = input_shape and step=1
360 size_t ndim = fShapeInput.size();
361 fIdentitySlice = fShapeOutput.size() == ndim;
362 // check also if input data is not input to the model. In that case we copy the data since we cannot just copy from the input pointer
363 fIdentitySlice &= (!model.IsReadyInputTensor(fNData) && !model.IsDimInputTensor(fNData));
364 for (size_t idim = 0; idim < ndim; idim++) {
365 if (!fIdentitySlice) break;
366 fIdentitySlice &= (fStart[idim].GetVal() == "0");
367 fIdentitySlice &= (fSteps[idim].GetVal() == "1");
368 fIdentitySlice &= (fEnd[idim].GetVal() == fShapeInput[idim].GetVal());
369 }
370
371 model.AddIntermediateTensor(fNOutput, model.GetTensorType(fNData), fShapeOutput);
372 // an identity slice does not change the data, so the output can share the memory of the input
373 if (fIdentitySlice)
374 fIsAlias = model.AddAliasTensor(fNOutput, fNData);
375
376 if (model.Verbose()) {
377 std::cout << "Slice " << fNData << " " << ConvertDimShapeToString(fShapeInput)
378 << "---> " << fNOutput << " " << ConvertDimShapeToString(fShapeOutput);
379 if (fIsAlias)
380 std::cout << " (using alias tensor since slice is an identity) ";
381 std::cout << std::endl;
382
383 }
384 }
385 }
386
387 std::string Generate(std::string opName) override {
388
389 if (fShapeInput.empty() || fShapeOutput.empty()){
390 throw std::runtime_error("TMVA SOFIE Slice Op called to Generate without being initialized first");
391 }
392
393 std::stringstream out;
394
395 out << "///------- Slice operator " << opName << "---> " << fNOutput << " "
396 << ConvertDimShapeToString(fShapeOutput) << "\n" << std::endl;
397 if (fIsOutputConstant) return out.str(); //no op for constant tensors
398 if (fIsOutputParamShape) {
399 out << "/// Slice output is a shape tensor with values : " << ConvertDimShapeToString(fShapeOutput) << "\n";
400 // need to generate code assigning values to shape tensors
401 for (int i = 0; i < static_cast<int>(fShapeOutput[0].dim); i++) {
402 out << SP << "tensor_" << fNOutput << "[" << i << "] = " << fOutputShapeData[i] << ";\n";
403 }
404 return out.str();
405 }
406
407 size_t ndim = fShapeInput.size();
408
409 if (fIdentitySlice) {
410 if (fIsAlias) {
411 out << "/// Slice is just an identity: the output points to the memory of the input\n";
412 out << SP << "auto * tensor_" << fNOutput << " = tensor_" << fNData << ";\n";
413 } else {
414 out << "/// Slice is just an identity (copy) \n";
415 out << SP << "std::copy(tensor_" << fNData << ", tensor_" << fNData << " + "
416 << ConvertDimShapeToLength(fShapeInput) << ", tensor_" << fNOutput << ");\n";
417 }
418 return out.str();
419 }
420
421 // loop on the dimensions depending no the orders
422 auto strides = UTILITY::ComputeStrideFromShape(fShapeInput);
423
424
425 out << SP << "{\n"; // define operator scope
426 for (size_t i = 0; i < fStepDims.size(); i++) {
427 if (fStepDims[i].isParam) {
428 if (fIsStepUndef)
429 out << SP << "size_t " << fStepDims[i] << " = tensor_" << fNames[3] << "[" << i << "];\n";
430 }
431 }
432 // special case for parametric values for start/end. Need to do clipping
433 for (size_t i = 0; i < fStartDims.size(); i++) {
434 if (fStartDims[i].isParam && fStartDims[i].param != fShapeInput[fAxes[i]].param) {
435 std::string s_start = "start_" + std::to_string(i);
436 if (fIsStartUndef) {
437 s_start = fStartDims[i].param;
438 out << SP << "size_t " << s_start << " = tensor_" << fNames[0] << "[" << i << "];\n";
439 } else {
440 out << SP << "size_t " << s_start << " = " << fStartDims[i] << ";\n";
441 fStart[fAxes[i]] = s_start; // need to use this value later when slicing
442 }
443 out << SP << "if (" << s_start << " < 0) " << s_start << " += " << fShapeInput[fAxes[i]] <<";\n";
444 out << SP << "if (" << s_start << " < 0) " << s_start << " = 0;\n";
445 if (!fStepDims[i].isParam) {
446 if (static_cast<IType>(fStepDims[i].dim) > 0 )
447 out << SP << "if (" << s_start << " > " << fShapeInput[fAxes[i]] << " ) " << s_start << " = " << fShapeInput[fAxes[i]] <<";\n";
448 else
449 out << SP << "if (" << s_start << " > " << fShapeInput[fAxes[i]] << " - 1" << " ) " << s_start << " = " << fShapeInput[fAxes[i]] << " - 1;\n";
450 }
451 }
452 // special case if step is negative and shape are equal and step is negative
453 else if (fStartDims[i].isParam && fStartDims[i].param == fShapeInput[fAxes[i]].param && !fStepDims[i].isParam && static_cast<IType>(fStepDims[i].dim) < 0 ) {
454 fStart[fAxes[i]] = Dim{ fStartDims[i].param + "-1" };
455 }
456 }
457 // now to for end
458 for (size_t i = 0; i < fEndDims.size(); i++) {
459 if (fEndDims[i].isParam && fEndDims[i].param != fShapeInput[fAxes[i]].param) {
460 std::string s_end = "end_" + std::to_string(i);
461 if (fIsEndUndef) {
462 s_end = fEndDims[i].param;
463 out << SP << "size_t " << s_end << " = tensor_" << fNames[1] << "[" << i << "];\n";
464 } else {
465 out << SP << "size_t " << s_end << " = " << fEndDims[i] << ";\n";
466 fEnd[fAxes[i]] = s_end; // need to use this value later when slicing
467 }
468 out << SP << "if (" << s_end << " < 0) " << s_end << " += " << fShapeInput[fAxes[i]] <<";\n";
469 if (!fStepDims[i].isParam) {
470 if (static_cast<IType>(fStepDims[i].dim) > 0 ) {
471 out << SP << "if (" << s_end << " < 0) " << s_end << " = 0;\n";
472 out << SP << "if (" << s_end << " > " << fShapeInput[fAxes[i]] << " ) " << s_end << " = " << fShapeInput[fAxes[i]] <<";\n";
473 } else {
474 out << SP << "if (" << s_end << " < -1) " << s_end << " = -1;\n";
475 out << SP << "if (" << s_end << " > " << fShapeInput[fAxes[i]] << " - 1" << " ) " << s_end << " = " << fShapeInput[fAxes[i]] << " - 1;\n";
476 }
477 }
478 }
479 // special case if step is negative and shape are equal and step is negative
480 else if (fEndDims[i].isParam && fEndDims[i].param == fShapeInput[fAxes[i]].param && !fStepDims[i].isParam && static_cast<IType>(fStepDims[i].dim) < 0 ) {
481 fEnd[fAxes[i]] = Dim{ fEndDims[i].param + "-1" };
482 }
483 }
484
485 out << SP << "size_t iOut = 0;\n";
486 std::string MSP = SP;
487 for (size_t idim = 0; idim < ndim; idim++) {
488 out << MSP << "for (size_t i" << idim << " = " << fStart[idim] << "; i" << idim << " < " << fEnd[idim]
489 << "; i" << idim << "+= " << fSteps[idim] << ") {\n";
490 MSP += SP;
491 if (idim < ndim-1) out << MSP << "size_t stride" << idim << " = " << strides[idim] << "*i" << idim << ";\n";
492 }
493 out << MSP << "size_t iInput = ";
494 for (size_t idim = 0; idim < ndim-1; idim++) out << " stride" << idim << " + ";
495 // here should be step size ?
496 out << "i" << ndim-1 << ";\n";
497 out << MSP << "tensor_" << fNOutput << "[iOut++] = tensor_" <<fNData << "[iInput];\n";
498 for (size_t idim = 0; idim < ndim; idim++) {
499 MSP = MSP.replace(0,SP.length(),"");
500 out << MSP << "}\n";
501 }
502 out << SP << "}\n"; // end operator scope
503
504 return out.str();
505 }
506
507};
508
509}//SOFIE
510}//Experimental
511}//TMVA
512
513
514#endif //TMVA_SOFIE_ROPERATOR_SLICE
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 data
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
const_iterator begin() const
const_iterator end() const
std::vector< std::vector< IType > > fAttributes
ROperator_Slice(std::string nameData, std::vector< IType > starts, std::vector< IType > ends, std::vector< IType > axes, std::string nameOutput)
ROperator_Slice(std::string nameData, std::vector< std::string > names, std::string nameOutput)
std::string Generate(std::string opName) override
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:44
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:45
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 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 ConvertDimShapeToLength(const std::vector< Dim > &shape)
create variable transformations