Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
SOFIE_common.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_SOFIE_COMMON
2#define TMVA_SOFIE_SOFIE_COMMON
3
4#include "TMVA/RTensor.hxx"
5
6#include "ROOT/RSpan.hxx"
7
8#include <stdexcept>
9#include <type_traits>
10#include <cstdint>
11#include <cstring>
12#include <complex>
13#include <string>
14#include <vector>
15#include <map>
16#include <memory>
17#include <regex>
18#include <sstream>
19#include <iostream>
20#include <iomanip>
21#include <cassert>
22#include <limits>
23
24namespace TMVA {
25namespace Experimental {
26namespace SOFIE {
27
28enum class ETensorType{
29 UNDEFINED = 0, FLOAT = 1, UINT8 = 2, INT8 = 3, UINT16 = 4, INT16 = 5, INT32 = 6, INT64 = 7, STRING = 8, BOOL = 9, //order sensitive
30 FLOAT16 = 10, DOUBLE = 11, UINT32 = 12, UINT64 = 13, COMPLEX64 = 14, COMPLEX28 = 15, BFLOAT16 = 16
31};
32
33enum class EActivationType{
34 UNDEFINED = 0, RELU = 1, SOFTMAX = 2, SIGMOID = 3, LEAKYRELU = 4, TANH = 5, ELU = 6
35};
36
37constexpr size_t GetTypeSize(ETensorType type) {
38 switch (type) {
39 case ETensorType::FLOAT: return sizeof(float);
40 case ETensorType::DOUBLE: return sizeof(double);
41 case ETensorType::UINT8: return sizeof(uint8_t);
42 case ETensorType::INT8: return sizeof(int8_t);
43 case ETensorType::UINT16: return sizeof(uint16_t);
44 case ETensorType::INT16: return sizeof(int16_t);
45 case ETensorType::INT32: return sizeof(int32_t);
46 case ETensorType::INT64: return sizeof(int64_t);
47 case ETensorType::UINT32: return sizeof(uint32_t);
48 case ETensorType::UINT64: return sizeof(uint64_t);
49 case ETensorType::BOOL: return sizeof(bool);
50 case ETensorType::STRING: return sizeof(std::string);
51 default: return 0;
52 }
53}
54
55typedef std::int64_t int_t;
56
59
60// find if a string represents a number
61bool IsInteger(const std::string & s);
62
63struct Dim{
64 bool isParam = false;
65 size_t dim = 0;
66 std::string param;
67
68 // default constructor (for I/O)
69 Dim() {}
70
71 // constructor for a parametric dimension with the option to pass a default dim value
72 // We use -1 for dim to indicate that the param dimension is an expression (e.g. "d1+d2")
73 // in case the string represents a number make Dim not parametric
74 Dim(const std::string & p, size_t d = 0) : isParam(true), dim(d), param(p)
75 {
76 if (IsInteger(p)) {
77 isParam = false;
78 dim = std::stoi(p);
79 }
80 }
81
82 // constructor for a non-parametric dimension
83 Dim(size_t d) : dim(d) {}
84
85 std::string GetVal() const {
86 // cast to int64_t for negative shape values
87 return (isParam) ? param : std::to_string(static_cast<int64_t>(dim));
88 }
89
90 std::ostream& operator<< (std::ostream& os) const {
91 os << GetVal();
92 return os;
93 }
94
95 bool operator==(const Dim& rhs) const {
96 return (isParam && rhs.isParam) ? param == rhs.param : dim == rhs.dim;
97 }
98 bool operator!=(const Dim& rhs) const {
99 return !(*this == rhs);
100 }
101};
102
103//bool operator==(const Dim& lhs, const Dim& rhs);
104inline std::ostream & operator<< (std::ostream &os, const Dim &d) {
105 os << d.GetVal();
106 return os;
107}
108
111 std::vector<Dim> shape;
112};
113
116 std::vector<size_t> shape;
117};
118
121 std::vector<Dim> shape;
122};
123
124// template traits for Tensor Shape
125template <typename T>
126struct TensorShape {};
127template<>
129 static bool IsDim() { return true; }
130};
131template<>
132struct TensorShape<size_t> {
133 static bool IsDim() { return false; }
134};
135
136// template traits for Tensor type
137template <typename T>
138struct TensorType {};
139template<>
140struct TensorType<float> {
141 static const std::string Name() { return "float"; }
142};
143template<>
145 static const std::string Name() { return "double"; }
146};
147template<>
148struct TensorType<int64_t> {
149 static const std::string Name() { return "int64_t"; }
150};
151template<>
152struct TensorType<int32_t> {
153 static const std::string Name() { return "int32_t"; }
154};
155template<>
156struct TensorType<uint32_t> {
157 static const std::string Name() { return "uint32_t"; }
158};
159template<>
160struct TensorType<uint64_t> {
161 static const std::string Name() { return "uint64_t"; }
162};
163template<>
165 static const std::string Name() { return "bool"; }
166};
167template<>
168struct TensorType<int8_t> {
169 static const std::string Name() { return "int8_t"; }
170};
171template<>
172struct TensorType<uint8_t> {
173 static const std::string Name() { return "uint8_t"; }
174};
175
177 std::string_view tensor_name;
179
180 TensorMemoryInfo split(const std::string_view new_name, size_t new_size) {
181 if (new_size > tensor_size) {
182 throw std::invalid_argument("New size exceeds available tensor size.");
183 }
186 }
187
188 // Method to merge another struct into this one
190 tensor_size += other.tensor_size;
191 }
192};
193
195
196 // ordered map with chunk_idx as key and TensorMemoryInfo as value
197 std::map<size_t, TensorMemoryInfo> total_stack;
198
199 // ordered map with chunk_idx as key and chunk_size as value
200 std::map<size_t, size_t> available_stack;
201};
202
203std::vector<Dim> ConvertShapeToDim(const std::vector<size_t> & shape);
204
205std::vector<size_t> ConvertShapeToInt(const std::vector<Dim> & shape);
206
207std::size_t ConvertShapeToLength(const std::vector<size_t> & shape);
208
209std::string ConvertShapeToString(const std::vector<size_t> & shape);
210std::string ConvertDimShapeToString(const std::vector<Dim> & shape);
211
212std::string ConvertDimShapeToLength(const std::vector<Dim> & shape);
213
214
215template<class T>
216std::string ConvertValToString(T value) {
217 std::stringstream ret;
218 ret << std::to_string(value);
219 return ret.str();
220}
221// float specialization
222template<>
223inline std::string ConvertValToString<float>(float value) {
224 std::stringstream ret;
225 // special case for infinity and Nan
226 if (std::isinf(value))
227 ret << (value > 0 ? "std::numeric_limits<float>::infinity()" :
228 "-std::numeric_limits<float>::infinity()");
229 else if (std::isnan(value))
230 ret << "std::numeric_limits<float>::quiet_NaN()";
231 else {
232 ret << std::setprecision(std::numeric_limits<float>::max_digits10);
233 ret << value;
234 }
235 return ret.str();
236}
237// double specialization
238template<>
239inline std::string ConvertValToString<double>(double value) {
240 std::stringstream ret;
241 // special case for infinity and Nan
242 if (std::isinf(value))
243 ret << (value > 0 ? "std::numeric_limits<double>::infinity()" :
244 "-std::numeric_limits<double>::infinity()");
245 else if (std::isnan(value))
246 ret << "std::numeric_limits<double>::quiet_NaN()";
247 else {
248 ret << std::setprecision(std::numeric_limits<double>::max_digits10);
249 ret << value;
250 }
251 return ret.str();
252}
253// int64_t specialization for INT64_MIN
254template<>
255inline std::string ConvertValToString<int64_t>(int64_t value) {
256 std::stringstream ret;
257 if (value == INT64_MIN)
258 ret << "INT64_MIN";
259 else
260 ret << std::to_string(value);
261 return ret.str();
262}
263
264
265// convert list of values in a string taking into account the precision
266template<class T>
267std::string ConvertValuesToString(size_t n, const T * data, size_t maxprint = -1) {
268 std::stringstream ret;
269 ret << "{ ";
270 for (size_t i = 0; i < std::min(n,maxprint); i++) {
272 if (i < n-1) ret << ", ";
273 if (i < n-1 && i == maxprint-1) ret << "..... ";
274 }
275 ret << "}";
276 return ret.str();
277}
278template<class T>
279std::string ConvertValuesToString(const std::vector<T> & data, size_t maxprint = 5) {
280 return ConvertValuesToString(data.size(), data.data(), maxprint);
281}
282
284public:
285 InitializedTensor() = default;
286 InitializedTensor(ETensorType type, std::span<std::size_t> shape, std::shared_ptr<void> data, bool typeConstant = false)
287 : fConstant(typeConstant), fType{type}, fShape{shape.begin(), shape.end()}, fData{data}
288 {
289 }
290
291 ETensorType const &type() const { return fType; }
292 std::vector<std::size_t> const &shape() const { return fShape; }
293 std::shared_ptr<void> const &sharedptr() const { return fData; }
294 // query if tensor comes from a Constant operator
295 bool IsConstantTensor() const { return fConstant;}
296 // query if tensor needs to be written in a weight file. Constant tensors are not written in a separate file
297 bool IsWeightTensor() const { return !fConstant && !fIsNotWritable;}
298 // check if a Tensor is Writable (need to be written in the file or in the generated code (e.g. as a constant tensor)
299 // if an initialized tensors is used in a constant operator at compile time does not need to be written and can be omitted in
300 // the generated code
301 bool IsNotWritable() const { return fIsNotWritable; }
302 // set not writable initialized tensors - i.e. tensor that must not be written in a file
304 // set writable initialized tensors - i.e. tensor that must be written in a file
305 void SetWritable() { fIsNotWritable = false;}
306 // set as constant (needed for non-float initialized tensors)
307 void SetConstant() { fConstant = true;}
308
309 template <class T = void>
310 T const *data() const
311 {
312 return static_cast<T const *>(fData.get());
313 }
314
315private:
316 bool fConstant = false; ///< Flag specifying if tensor is a Constant one (coming from a Constant operator)
317 bool fIsNotWritable = false; ///< Flag to indicate that tensor values do not need to be written as weight or generated code
318 ETensorType fType; ///< Encodes the type of the data
319 std::vector<std::size_t> fShape; ///< The shape of the data in terms of elements in each dimension
320 std::shared_ptr<void> fData; ///<! Transient shared data
321};
322
323template <typename T>
325 if (std::is_same<T, float>::value) return ETensorType::FLOAT;
326 if (std::is_same<T, uint8_t>::value) return ETensorType::UINT8;
327 if (std::is_same<T, int8_t>::value) return ETensorType::INT8;
328 if (std::is_same<T, uint16_t>::value) return ETensorType::UINT16;
329 if (std::is_same<T, int16_t>::value) return ETensorType::INT16;
330 if (std::is_same<T, int32_t>::value) return ETensorType::INT32;
331 if (std::is_same<T, int64_t>::value) return ETensorType::INT64;
332 if (std::is_same<T, std::string>::value) return ETensorType::STRING;
333 if (std::is_same<T, bool>::value) return ETensorType::BOOL;
334 //float16 unimplemented
335 if (std::is_same<T, double>::value) return ETensorType::DOUBLE;
336 if (std::is_same<T, uint32_t>::value) return ETensorType::UINT32;
337 if (std::is_same<T, uint64_t>::value) return ETensorType::UINT64;
338 //complex 64, 28, bfloat 16 unimplemented
339}
340
341namespace UTILITY{
342
343
344
345// clean operator and tensor names
346std::string Clean_name(std::string input_tensor_name);
347
348// Check if two shapes are equal
349bool AreSameShape(const std::vector<size_t>&, const std::vector<size_t>&);
350bool AreSameShape(const std::vector<size_t>&, const std::vector<Dim>&);
351bool AreSameShape(const std::vector<Dim>&, const std::vector<Dim>&);
352
353
354// Multidirectional broadcast a list of tensors to the same shape
355std::vector<size_t> MultidirectionalBroadcastShape(std::vector<std::vector<size_t>>);
356
357// Multidirectional broadcast two shapes to the same shape
358
359std::pair<int, std::vector<size_t>> MultidirectionalBroadcastShape(std::vector<size_t> &, std::vector<size_t> &);
360std::vector<size_t> UnidirectionalBroadcastShape(std::vector<size_t> &, std::vector<size_t> &);
361
362std::pair<int, std::vector<Dim>> MultidirectionalBroadcastShape(std::vector<Dim> &, std::vector<Dim> &);
363
364
365
366template<typename T>
367T* BroadcastConvBias(const T* data, const size_t channel, const std::vector<size_t>& targetShape) {
368 size_t size = targetShape.size();
369 if (targetShape[1] != channel) {
370 std::stringstream ss;
371 ss << "TMVA::SOFIE - Error broadcasting Conv Bias of shape {";
372 ss << std::to_string(channel);
373 ss << "} to ";
375 throw
376 std::runtime_error(ss.str());
377 }
378
380 T* newData = new T[targetLength];
381
382 if (targetLength == channel) {
383 std::copy(data, data + channel, newData);
384 return newData;
385 }
386
387 // cStride = OutDepth * outHeight * outWidth
388 size_t cStride = 1;
389 for (size_t i = 2; i < size; i++)
390 cStride *= targetShape[i];
391 // Broadcast each element of the bias to a vector of size cStride and concatenate them
392 // into a vector of size channel * cStride
393 for (size_t i = 0; i < channel; i++) {
394 std::fill(newData + i * cStride, newData + (i + 1) * cStride, data[i]);
395 }
396 // Broadcast newData[0...channel * cStride) to newData[0...batch * channel * cStride)
397 size_t batch = targetShape[0];
398 size_t bStride = channel * cStride;
399 for (size_t i = 1; i < batch; i++) {
400 std::copy(newData, newData + bStride, newData + i * bStride);
401 }
402 return newData;
403}
404
405// Broadcast a tensor from shape to targetShape according to numpy broadcasting rules
406// See more at https://numpy.org/doc/stable/user/basics.broadcasting.html
407// and https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md .
408template<typename T, class ConstContT = std::span<const T>>
409void BroadcastTensor(ConstContT data, const std::vector<size_t>& shape, const std::vector<size_t>& targetShape, T *broadcastedData) {
410 // Size of the shapes (tensor input here have shapes with same sizes, we have already added the needed ones )
411 size_t size = shape.size();
412 // Current length of the broadcasted tensor
413 size_t curLength = data.size();
414 // special case when broadcasting last dimensions (initial shapes must be the same)
415 if (size > 1 && shape.front() == targetShape.front() && shape.back() == 1) {
416 size_t bsize = targetShape.back();
417 // compute the size of the data to broadcast
418 for (int k = int(size)-2; k >=0; k--) {
419 if (shape[k] != 1) break;
420 bsize *= targetShape[k];
421 }
422 for (size_t i = 0; i < curLength; i++) {
423 std::fill(broadcastedData + i*bsize, broadcastedData + (i+1)*bsize , data[i]);
424 }
425 return;
426 }
427
428 std::copy(data.begin(), data.end(), broadcastedData);
429 // Product of the previous dimensions of targetShape
430 size_t arrayNum = 1;
431 // New broadcasted data: is this needed?
433
434 for (size_t idx = 0; idx < size; idx++) {
435 size_t dim = shape[idx];
436 size_t targetDim = targetShape[idx];
437 if (dim == 1 && targetDim > 1) {
438 // Set the new length of the data
439 size_t newLength = curLength * targetDim;
440 // View the data as a list of arrayNum arrays of size arrayLength
441 size_t arrayLength = curLength / arrayNum;
442 // Broadcast each array dim times
443 if (arrayLength > 1) {
444 // If each array has at least two elements
445 for (size_t arrayIdx = 0; arrayIdx < arrayNum; arrayIdx++) {
446 for (size_t targetIdx = 0; targetIdx < targetDim; targetIdx++) {
450 newData.begin() + offset);
451 }
452 }
453 } else {
454 // If each array has one element
455 for (size_t arrayIdx = 0; arrayIdx < arrayNum; arrayIdx++) {
456 std::fill(newData.begin() + arrayIdx * targetDim,
458 }
459 }
460 // Update current length
462 // Update broadcasted data
464 }
465 // Update the number of arrays
467 }
468}
469
470// interface where we allocate a new array for broadcasted data
471template<typename T>
472T* CreateBroadcastTensor(const T* data, const std::vector<size_t>& shape, const std::vector<size_t>& targetShape, size_t targetLength) {
473 // newShape is an array of size equal to dimension along which we are broadcasting the tensor
474 T* broadcastedData = new T[targetLength];
475 size_t curLength = ConvertShapeToLength(shape);
477 return broadcastedData;
478}
479// Unidirectional broadcasting shape to targetShape// In unidirectional broadcast - only tensor B can have the shape changed not
480// tensor A - otherwise is a multidirectional broadcast
481template<typename T>
482T* UnidirectionalBroadcast(const T* data, const std::vector<size_t>& shape, const std::vector<size_t>& targetShape) {
483 // Prepend shape with ones
484 if (shape.size() < targetShape.size()) {
485 size_t targetSize = targetShape.size();
486 std::vector<size_t> newShape(targetSize, 1);
487 size_t offset = targetSize - shape.size();
488 std::copy(shape.begin(), shape.end(), newShape.begin() + offset);
490 }
492}
493
494// Unidirectional broadcasting shape to targetShape using a passed vector to avoid allocations
495template<typename T>
496void UnidirectionalBroadcast(const T* data, const std::vector<size_t>& shape, const std::vector<size_t>& targetShape, T *broadcastedData) {
497 size_t curLength = ConvertShapeToLength(shape);
498 std::span<T> inData(const_cast<T*>(data), curLength);
499 // Prepend shape with ones
500 if (shape.size() < targetShape.size()) {
501 size_t targetSize = targetShape.size();
502 std::vector<size_t> newShape(targetSize, 1);
503 size_t offset = targetSize - shape.size();
504 std::copy(shape.begin(), shape.end(), newShape.begin() + offset);
506 }
508}
509
510/// compute stride of a tensor given its shape (assume layout is row-major)
511std::vector<size_t> ComputeStrideFromShape(const std::vector<size_t> & shape);
512std::vector<Dim> ComputeStrideFromShape(const std::vector<Dim> & shape);
513
514/// function to check if a >> 0 and a < MAX using a single comparison
515//// use trick casting to unsigned values so it becomes a single comparison
516inline bool is_a_ge_zero_and_a_lt_b(int a, int b) {
517 return static_cast<unsigned>(a) < static_cast<unsigned>(b);
518}
519
520
521/// im2col : efficient function to re-arrange input data of convolution to a matrix
522/// that can be used by BLAS
523/// Use trick to loop on each element of filtered region first and follow input data layout
524/// By doing this reads and writes are of consecutive data in memory and one gains in efficiency
525/// The resulting matrix will be already transposed and can be used directly in BLAS
526/// since output will be a matrix : (channels*kernel_h*kernel_w , output_h*output_w)
527/// Example: with an input matrix
528/// a1 a2 a3
529/// b1 b2 b3 and a 2x2 kernel (k1,k2,k3,k4) and padding 1 :
530/// c1 c2 c3
531/// outpout will be a matrix (4 x 16)
532/// the routine will follow output order :
533// first all elements which will be operated by k1 then k2 then k3
534/// -> ( 0 0 0 0 0 a1 a2 a3 0 b1 b2 b3 0 c1 c2 c3 ) all elements for k1
535/// ( 0 0 0 0 a1 a2 a3 0 b1 b2 b3 0 c1 c2 c3 0 ) for k2
536/// ( 0 a1 a2 a3 0 b1 b2 b3 0 c1 c2 c3 0 0 0 0 ) for k3
537/// ( a1 a2 a3 0 b1 b2 b3 0 c1 c2 c3 0 0 0 0 0 ) for k4
538///
539
540template <typename T>
541void Im2col(const T *data_im, const int channels, const int height, const int width, const int kernel_h,
542 const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w,
543 const int dilation_h, const int dilation_w, T *data_col)
544{
545 const int output_h = (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;
546 const int output_w = (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1;
547 const int channel_size = height * width;
548 for (int channel = channels; channel--; data_im += channel_size) {
549 for (int kernel_row = 0; kernel_row < kernel_h; kernel_row++) {
550 for (int kernel_col = 0; kernel_col < kernel_w; kernel_col++) {
555 *(data_col++) = 0;
556 }
557 } else {
559 for (int output_col = output_w; output_col; output_col--) {
562 } else {
563 *(data_col++) = 0;
564 }
566 }
567 }
569 }
570 }
571 }
572 }
573}
574
575/// 3d implementation
576template <typename T>
577void Im2col_3d(const T *data_im, const int channels,
578 const int depth, const int height, const int width,
579 const int kernel_d, const int kernel_h, const int kernel_w,
580 const int pad_d, const int pad_h, const int pad_w,
581 const int stride_d, const int stride_h, const int stride_w,
582 const int dilation_d, const int dilation_h, const int dilation_w, T *data_col)
583{
584 const int output_h = (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;
585 const int output_w = (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1;
586 const int output_d = (depth + 2 * pad_d - (dilation_d * (kernel_d - 1) + 1)) / stride_d + 1;
587 const int channel_size = height * width * depth;
588 // assume data are c x d x h x w
589 for (int channel = channels; channel--; data_im += channel_size) {
590 for (int kernel_depth = 0; kernel_depth < kernel_d; kernel_depth++) {
591 for (int kernel_row = 0; kernel_row < kernel_h; kernel_row++) {
592 for (int kernel_col = 0; kernel_col < kernel_w; kernel_col++) {
594 for (int output_dep = output_d; output_dep; output_dep--) {
598 *(data_col++) = 0;
599 }
600 }
601 } else {
606 *(data_col++) = 0;
607 }
608 } else {
610 for (int output_col = output_w; output_col; output_col--) {
613 } else {
614 *(data_col++) = 0;
615 }
617 }
618 }
620 }
621 }
623 }
624 }
625 }
626 }
627 }
628}
629
630template <typename Dtype>
631void col2im(const Dtype* data_col, const int channels,
632 const int height, const int width, const int kernel_h, const int kernel_w,
633 const int pad_h, const int pad_w,
634 const int stride_h, const int stride_w,
635 const int dilation_h, const int dilation_w,
636 Dtype* data_im) {
637 // note that output data_im needs to be set to zero value!!!!
638 std::fill(data_im, data_im + height * width * channels, 0.);
639 //caffe_set(height * width * channels, Dtype(0), data_im);
640 // data_im must be a zero vector
641 //const Dtype * data_col_0 = data_col;
642 const int output_h = (height + 2 * pad_h -
643 (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;
644 const int output_w = (width + 2 * pad_w -
645 (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1;
646 const int channel_size = height * width;
647 for (int channel = channels; channel--; data_im += channel_size) {
648 for (int kernel_row = 0; kernel_row < kernel_h; kernel_row++) {
649 for (int kernel_col = 0; kernel_col < kernel_w; kernel_col++) {
654 } else {
656 for (int output_col = output_w; output_col; output_col--) {
658 //assert(input_row*width+input_col < height * width * channels);
659 //assert(data_col - data_col_0 < output_h*output_w*channels);
660 // std::cout << "COL2IM: input_row" << " " << input_row << " " << input_col
661 // << " <---- " << data_col - data_col_0 << " values: "
662 // << data_im[input_row * width + input_col] << " <--- " << *data_col << std::endl;
664 }
665 data_col++;
667 }
668 }
670 }
671 }
672 }
673 }
674 //std::cout << "finishing col2imp" << std::endl;
675}
676
677} // end namespace UTILITY
678
679namespace BLAS{
680extern "C" void sgemm_(const char * transa, const char * transb, const int * m, const int * n, const int * k,
681 const float * alpha, const float * A, const int * lda, const float * B, const int * ldb,
682 const float * beta, float * C, const int * ldc);
683}//BLAS
684
685
686struct GNN_Data {
687 RTensor<float> node_data; // the node feature data, tensor with shape (num_nodes, num_node_features)
688 RTensor<float> edge_data; // the edge feature data, tensor with shape (num_edges, num_edge_features)
689 RTensor<float> global_data; // the global features, tensor with shape (1, num_global_features)
690 RTensor<int> edge_index; // the edge index (receivers and senders for each edge), tensor with shape (2, num_edges)
691 // edge_index[0,:] are the receivers and edge_index[1,:] are the senders
692
693
694 // need to have default constructor since RTensor has not one
696
697};
698
699template<typename T>
701{
702 // concatenate tensor along axis. Shape must be the same except in the dimension of the concatenated axis
703 if (t1.GetMemoryLayout() != t2.GetMemoryLayout())
704 throw std::runtime_error("TMVA RTensor Concatenate - tensors have different memory layout");
705 auto & shape1 = t1.GetShape();
706 auto & shape2 = t2.GetShape();
707 if (t1.GetSize()/shape1[axis] != t2.GetSize()/shape2[axis]) {
708 std::cout << "axis " << axis << " sizes " << t1.GetSize() << " " << t2.GetSize() << " ";
709 std::cout << "shape 1 : " << ConvertShapeToString(t1.GetShape());
710 std::cout << " shape 2 : " << ConvertShapeToString(t2.GetShape()) << std::endl;
711 throw std::runtime_error("TMVA RTensor Concatenate - tensors have incompatible shapes");
712 }
713 std::vector<size_t> outShape = shape1;
714 outShape[axis] = shape1[axis] + shape2[axis];
716 if (t1.GetMemoryLayout() == TMVA::Experimental::MemoryLayout::ColumnMajor) {
717 throw std::runtime_error("TMVA RTensor Concatenate is not yet supported for column major tensors");
718 }
719
720 auto & stride1 = t1.GetStrides();
721 auto & stride2 = t2.GetStrides();
722 auto & outStride = tout.GetStrides();
723
724 size_t s1 = (axis > 0) ? stride1[axis-1] : t1.GetSize(); // block size to copy from first tensor
725 size_t s2 = (axis > 0) ? stride2[axis-1] : t2.GetSize(); // block size to copy from second tensor
726 size_t sout = (axis > 0) ? outStride[axis-1] : tout.GetSize();
727 size_t nb = t1.GetSize()/s1;
728 for (size_t i = 0; i < nb; i++) {
729 std::copy(t1.GetData() + i*s1, t1.GetData() + (i+1)*s1, tout.GetData() + i * sout );
730 std::copy(t2.GetData() + i*s2, t2.GetData() + (i+1)*s2, tout.GetData() + i * sout + s1 );
731 }
732
733 return tout;
734}
735
736
737inline GNN_Data Concatenate(GNN_Data & data1, GNN_Data & data2, int axis = 0) {
738 GNN_Data out;
739 out.node_data = Concatenate(data1.node_data,data2.node_data, axis);
740 out.edge_data = Concatenate(data1.edge_data,data2.edge_data, axis);
741 out.global_data = Concatenate<float>(data1.global_data,data2.global_data, axis-1);
742 // assume sender/receivers of data1 and data2 are the same
743 out.edge_index = data1.edge_index.Copy();
744 return out;
745}
746
747inline GNN_Data Copy(const GNN_Data & data) {
748 GNN_Data out;
749 out.node_data = RTensor<float>(data.node_data.GetShape());
750 out.edge_data = RTensor<float>(data.edge_data.GetShape());
751 out.global_data = RTensor<float>(data.global_data.GetShape());
752 out.edge_index = RTensor<int>(data.edge_index.GetShape());
753 std::copy(data.node_data.GetData(), data.node_data.GetData()+ data.node_data.GetSize(), out.node_data.GetData());
754 std::copy(data.edge_data.GetData(), data.edge_data.GetData()+ data.edge_data.GetSize(), out.edge_data.GetData());
755 std::copy(data.global_data.GetData(), data.global_data.GetData()+ data.global_data.GetSize(), out.global_data.GetData());
756 std::copy(data.edge_index.GetData(), data.edge_index.GetData()+ data.edge_index.GetSize(), out.edge_index.GetData());
757 return out;
758}
759
760inline void Gemm_Call(float *output, bool transa, bool transb, int m, int n, int k, float alpha, const float *A,
761 const float *B, float beta, const float *C)
762{
763 char ct = 't';
764 char cn = 'n';
765 const int *lda = transa ? &k : &m;
766 const int *ldb = transb ? &n : &k;
767 const int *ldc = &m;
768 if (C != nullptr) {
769 std::copy(C, C + m * n, output);
770 }
771 TMVA::Experimental::SOFIE::BLAS::sgemm_(transa ? &ct : &cn, transb ? &ct : &cn, &m, &n, &k, &alpha, A, lda, B, ldb,
772 &beta, output, ldc);
773}
774
775inline void Fill(float *output, float value, int size)
776{
777 std::fill(output, output + size, value);
778}
779
780template <class T>
781inline void Copy(T *output, T const *input, int size)
782{
783 std::copy(input, input + size, output);
784}
785
786inline void Relu(float *output, float const *input, int size)
787{
788 for (int i = 0; i < size; i++) {
789 output[i] = (input[i] > 0.0f) ? input[i] : 0.0f;
790 }
791}
792// function to read float from the file dealing with inf and nan values
793inline float ParseFloatToken (const std::string & s) {
794 if (s == "inf") return std::numeric_limits<float>::infinity();
795 if (s == "-inf") return -std::numeric_limits<float>::infinity();
796 if (s == "nan") return std::numeric_limits<float>::quiet_NaN();
797 return std::stof(s);
798}
799
800template <class T>
801void ReadTensorFromStream(std::istream &is, T &target, std::string const &expectedName, std::size_t expectedLength)
802{
803 std::string name;
804 std::size_t length;
805 is >> name >> length;
806 if (name != expectedName) {
807 std::string err_msg =
808 "TMVA-SOFIE failed to read the correct tensor name; expected name is " + expectedName + " , read " + name;
809 throw std::runtime_error(err_msg);
810 }
811 if (length != expectedLength) {
812 std::string err_msg = "TMVA-SOFIE failed to read the correct tensor size; expected size is " +
813 std::to_string(expectedLength) + " , read " + std::to_string(length);
814 throw std::runtime_error(err_msg);
815 }
816 std::string token;
817 for (size_t i = 0; i < length; ++i) {
818 is >> token;
819 target[i] = ParseFloatToken(token);
820 }
821 if (is.fail()) {
822 throw std::runtime_error("TMVA-SOFIE failed to read the values for tensor " + expectedName);
823 }
824}
825
826//Utility functions to generate code
827void EmitNestedLoops(std::stringstream &out, size_t loopRank, const std::vector<Dim> shape);
828void CloseNestedLoops(std::stringstream &out, size_t loopRank);
829
830
831// code for the memory greeding allocations
833 int begin; // start time (op index) lifetime
834 int end; // end time lifetime
835 size_t size; // size of tensors in bytes
836};
837
839 std::size_t total_bytes = 0; // total memory needed
840 std::vector<size_t> offsets; // resulted offsets for each tensor
841};
842
843/// Greedy best-fit planner with coalescing free list.
844MemoryResult OrganizeMemory(const std::vector<TensorLifeInfo> & tensorsInfo );
845
846// Simple Dimension classes ans helpers to add constexpr meta info on input
847// tensors to the emitted code.
848struct SingleDim {
849 enum class Kind {
850 Static,
852 };
853
855 std::size_t dim;
856 std::string_view name;
857
858 constexpr SingleDim(std::size_t v) : kind(Kind::Static), dim(v), name() {}
859 constexpr SingleDim(const char *v) : kind(Kind::Symbolic), dim(0), name(v) {}
860};
861
864 std::size_t size;
865
866 constexpr std::size_t total_size() const
867 {
868 std::size_t result = 1;
869 for (std::size_t i = 0; i < size; ++i) {
870 result *= data[i].dim;
871 }
872 return result;
873 }
874};
875
876template<class Arr>
877constexpr TensorDims makeDims(Arr const &arr)
878{
879 return TensorDims{arr.data(), arr.size()};
880}
881
882} // namespace SOFIE
883} // namespace Experimental
884} // namespace TMVA
885
886#endif //TMVA_SOFIE_COMMON
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define a(i)
Definition RSha256.hxx:99
#define s1(x)
Definition RSha256.hxx:91
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
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 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 offset
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 target
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 result
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 value
Option_t Option_t width
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
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t height
char name[80]
Definition TGX11.cxx:148
const_iterator begin() const
RTensor is a container with contiguous memory and shape information.
Definition RTensor.hxx:163
std::shared_ptr< void > const & sharedptr() const
std::shared_ptr< void > fData
! Transient shared data
ETensorType fType
Encodes the type of the data.
std::vector< std::size_t > const & shape() const
std::vector< std::size_t > fShape
The shape of the data in terms of elements in each dimension.
bool fIsNotWritable
Flag to indicate that tensor values do not need to be written as weight or generated code.
bool fConstant
Flag specifying if tensor is a Constant one (coming from a Constant operator)
InitializedTensor(ETensorType type, std::span< std::size_t > shape, std::shared_ptr< void > data, bool typeConstant=false)
const Int_t n
Definition legend1.C:16
void sgemm_(const char *transa, const char *transb, const int *m, const int *n, const int *k, const float *alpha, const float *A, const int *lda, const float *B, const int *ldb, const float *beta, float *C, const int *ldc)
bool AreSameShape(const std::vector< size_t > &, const std::vector< size_t > &)
void Im2col_3d(const T *data_im, const int channels, const int depth, const int height, const int width, const int kernel_d, const int kernel_h, const int kernel_w, const int pad_d, const int pad_h, const int pad_w, const int stride_d, const int stride_h, const int stride_w, const int dilation_d, const int dilation_h, const int dilation_w, T *data_col)
3d implementation
T * BroadcastConvBias(const T *data, const size_t channel, const std::vector< size_t > &targetShape)
std::vector< size_t > UnidirectionalBroadcastShape(std::vector< size_t > &, std::vector< size_t > &)
void col2im(const Dtype *data_col, const int channels, const int height, const int width, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, Dtype *data_im)
void BroadcastTensor(ConstContT data, const std::vector< size_t > &shape, const std::vector< size_t > &targetShape, T *broadcastedData)
std::string Clean_name(std::string input_tensor_name)
bool is_a_ge_zero_and_a_lt_b(int a, int b)
function to check if a >> 0 and a < MAX using a single comparison / use trick casting to unsigned val...
std::vector< size_t > MultidirectionalBroadcastShape(std::vector< std::vector< size_t > >)
T * UnidirectionalBroadcast(const T *data, const std::vector< size_t > &shape, const std::vector< size_t > &targetShape)
void Im2col(const T *data_im, const int channels, const int height, const int width, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, T *data_col)
im2col : efficient function to re-arrange input data of convolution to a matrix that can be used by B...
T * CreateBroadcastTensor(const T *data, const std::vector< size_t > &shape, const std::vector< size_t > &targetShape, size_t targetLength)
std::vector< size_t > ComputeStrideFromShape(const std::vector< size_t > &shape)
compute stride of a tensor given its shape (assume layout is row-major)
MemoryResult OrganizeMemory(const std::vector< TensorLifeInfo > &tensorsInfo)
Greedy best-fit planner with coalescing free list.
constexpr TensorDims makeDims(Arr const &arr)
std::string ConvertDimShapeToString(const std::vector< Dim > &shape)
std::size_t ConvertShapeToLength(const std::vector< size_t > &shape)
std::string ConvertValToString< double >(double value)
void ReadTensorFromStream(std::istream &is, T &target, std::string const &expectedName, std::size_t expectedLength)
std::string ConvertValuesToString(size_t n, const T *data, size_t maxprint=-1)
std::vector< Dim > ConvertShapeToDim(const std::vector< size_t > &shape)
Convert shape from integer format to dynamic one (based on Dim)
constexpr size_t GetTypeSize(ETensorType type)
ETensorType GetTemplatedType(T)
void Gemm_Call(float *output, bool transa, bool transb, int m, int n, int k, float alpha, const float *A, const float *B, float beta, const float *C)
std::string ConvertValToString< float >(float value)
void Fill(float *output, float value, int size)
std::vector< size_t > ConvertShapeToInt(const std::vector< Dim > &shape)
Convert shape based on Dim to integer format.
std::string ConvertTypeToString(ETensorType type)
void Relu(float *output, float const *input, int size)
ETensorType ConvertStringToType(std::string type)
TMVA::Experimental::RTensor< T > Concatenate(TMVA::Experimental::RTensor< T > &t1, TMVA::Experimental::RTensor< T > &t2, int axis=0)
float ParseFloatToken(const std::string &s)
std::ostream & operator<<(std::ostream &os, const Dim &d)
std::string ConvertDimShapeToLength(const std::vector< Dim > &shape)
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)
std::string ConvertValToString(T value)
std::string ConvertValToString< int64_t >(int64_t value)
bool IsInteger(const std::string &s)
GNN_Data Copy(const GNN_Data &data)
create variable transformations
bool operator!=(const Dim &rhs) const
bool operator==(const Dim &rhs) const
Dim(const std::string &p, size_t d=0)
std::ostream & operator<<(std::ostream &os) const
std::map< size_t, TensorMemoryInfo > total_stack
std::map< size_t, size_t > available_stack
constexpr SingleDim(std::size_t v)
constexpr SingleDim(const char *v)
constexpr std::size_t total_size() const
void merge(const TensorMemoryInfo &other)
TensorMemoryInfo split(const std::string_view new_name, size_t new_size)
TMarker m
Definition textangle.C:8
auto * t1
Definition textangle.C:20