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 if (std::is_floating_point_v<T>)
219 ret << std::setprecision(std::numeric_limits<T>::max_digits10);
220 ret << value;
221 return ret.str();
222}
223
224
225// convert list of values in a string taking into account the precision
226template<class T>
227std::string ConvertValuesToString(size_t n, const T * data, size_t maxprint = -1) {
228 std::stringstream ret;
229 ret << "{ ";
230 for (size_t i = 0; i < std::min(n,maxprint); i++) {
231 if (std::is_floating_point_v<T>)
232 ret << std::setprecision(std::numeric_limits<T>::max_digits10) << data[i];
233 else
234 // cast in case of boolean (int8)
235 ret << data[i];
236
237 if (i < n-1) ret << ", ";
238 if (i < n-1 && i == maxprint-1) ret << "..... ";
239 }
240 ret << "}";
241 return ret.str();
242}
243template<class T>
244std::string ConvertValuesToString(const std::vector<T> & data, size_t maxprint = 5) {
245 return ConvertValuesToString(data.size(), data.data(), maxprint);
246}
247
249public:
250 InitializedTensor() = default;
251 InitializedTensor(ETensorType type, std::span<std::size_t> shape, std::shared_ptr<void> data, bool typeConstant = false)
252 : fConstant(typeConstant), fType{type}, fShape{shape.begin(), shape.end()}, fData{data}
253 {
254 }
255
256 ETensorType const &type() const { return fType; }
257 std::vector<std::size_t> const &shape() const { return fShape; }
258 std::shared_ptr<void> const &sharedptr() const { return fData; }
259 // query if tensor comes from a Constant operator
260 bool IsConstantTensor() const { return fConstant;}
261 // query if tensor needs to be written in a weight file. Constant tensors are not written in a separate file
262 bool IsWeightTensor() const { return !fConstant && !fIsNotWritable;}
263 // check if a Tensor is Writable (need to be written in the file or in the generated code (e.g. as a constant tensor)
264 // if an initialized tensors is used in a constant operator at compile time does not need to be written and can be omitted in
265 // the generated code
266 bool IsNotWritable() const { return fIsNotWritable; }
267 // set not writable initialized tensors - i.e. tensor that must not be written in a file
269 // set writable initialized tensors - i.e. tensor that must be written in a file
270 void SetWritable() { fIsNotWritable = false;}
271 // set as constant (needed for non-float initialized tensors)
272 void SetConstant() { fConstant = true;}
273
274 template <class T = void>
275 T const *data() const
276 {
277 return static_cast<T const *>(fData.get());
278 }
279
281 {
282 // We only calculate fSize here, because it is only used for IO to know
283 // the size of the persistent data.
284 fSize = 1;
285 for (std::size_t item : fShape) {
286 fSize *= static_cast<int>(item);
287 }
288 // get size in bytes
290 fPersistentData = static_cast<char *>(fData.get());
291 }
293 {
294 // If there is no persistent data, do nothing
295 if (fSize == 0 || fPersistentData == nullptr) {
296 return;
297 }
298
299 // Nothing to be done if the pointed-to data is the same
300 if (fPersistentData == static_cast<char *>(fData.get())) {
301 return;
302 }
303
304 // Initialize the shared_ptr
305 fData = std::shared_ptr<void>{malloc(fSize), free};
306 std::memcpy(fData.get(), fPersistentData, fSize);
307
308 // Make sure the data read from disk doesn't leak and delete the
309 // persistent data
310 delete[] fPersistentData;
311 fPersistentData = nullptr;
312 fSize = 0;
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 int fSize = 0; ///< The size of the persistent data in bytes (not number of elements!)
322 char *fPersistentData = nullptr; ///<[fSize] Persistent version of the data
323};
324
325template <typename T>
327 if (std::is_same<T, float>::value) return ETensorType::FLOAT;
328 if (std::is_same<T, uint8_t>::value) return ETensorType::UINT8;
329 if (std::is_same<T, int8_t>::value) return ETensorType::INT8;
330 if (std::is_same<T, uint16_t>::value) return ETensorType::UINT16;
331 if (std::is_same<T, int16_t>::value) return ETensorType::INT16;
332 if (std::is_same<T, int32_t>::value) return ETensorType::INT32;
333 if (std::is_same<T, int64_t>::value) return ETensorType::INT64;
334 if (std::is_same<T, std::string>::value) return ETensorType::STRING;
335 if (std::is_same<T, bool>::value) return ETensorType::BOOL;
336 //float16 unimplemented
337 if (std::is_same<T, double>::value) return ETensorType::DOUBLE;
338 if (std::is_same<T, uint32_t>::value) return ETensorType::UINT32;
339 if (std::is_same<T, uint64_t>::value) return ETensorType::UINT64;
340 //complex 64, 28, bfloat 16 unimplemented
341}
342
343namespace UTILITY{
344
345
346
347// clean operator and tensor names
348std::string Clean_name(std::string input_tensor_name);
349
350// Check if two shapes are equal
351bool AreSameShape(const std::vector<size_t>&, const std::vector<size_t>&);
352bool AreSameShape(const std::vector<size_t>&, const std::vector<Dim>&);
353bool AreSameShape(const std::vector<Dim>&, const std::vector<Dim>&);
354
355
356// Multidirectional broadcast a list of tensors to the same shape
357std::vector<size_t> MultidirectionalBroadcastShape(std::vector<std::vector<size_t>>);
358
359// Multidirectional broadcast two shapes to the same shape
360
361std::pair<int, std::vector<size_t>> MultidirectionalBroadcastShape(std::vector<size_t> &, std::vector<size_t> &);
362std::vector<size_t> UnidirectionalBroadcastShape(std::vector<size_t> &, std::vector<size_t> &);
363
364std::pair<int, std::vector<Dim>> MultidirectionalBroadcastShape(std::vector<Dim> &, std::vector<Dim> &);
365
366
367
368template<typename T>
369T* BroadcastConvBias(const T* data, const size_t channel, const std::vector<size_t>& targetShape) {
370 size_t size = targetShape.size();
371 if (targetShape[1] != channel) {
372 std::stringstream ss;
373 ss << "TMVA::SOFIE - Error broadcasting Conv Bias of shape {";
374 ss << std::to_string(channel);
375 ss << "} to ";
377 throw
378 std::runtime_error(ss.str());
379 }
380
382 T* newData = new T[targetLength];
383
384 if (targetLength == channel) {
385 std::copy(data, data + channel, newData);
386 return newData;
387 }
388
389 // cStride = OutDepth * outHeight * outWidth
390 size_t cStride = 1;
391 for (size_t i = 2; i < size; i++)
392 cStride *= targetShape[i];
393 // Broadcast each element of the bias to a vector of size cStride and concatenate them
394 // into a vector of size channel * cStride
395 for (size_t i = 0; i < channel; i++) {
396 std::fill(newData + i * cStride, newData + (i + 1) * cStride, data[i]);
397 }
398 // Broadcast newData[0...channel * cStride) to newData[0...batch * channel * cStride)
399 size_t batch = targetShape[0];
400 size_t bStride = channel * cStride;
401 for (size_t i = 1; i < batch; i++) {
402 std::copy(newData, newData + bStride, newData + i * bStride);
403 }
404 return newData;
405}
406
407// Broadcast a tensor from shape to targetShape according to numpy broadcasting rules
408// See more at https://numpy.org/doc/stable/user/basics.broadcasting.html
409// and https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md .
410template<typename T, class ConstContT = std::span<const T>>
411void BroadcastTensor(ConstContT data, const std::vector<size_t>& shape, const std::vector<size_t>& targetShape, T *broadcastedData) {
412 // Size of the shapes (tensor input here have shapes with same sizes, we have already added the needed ones )
413 size_t size = shape.size();
414 // Current length of the broadcasted tensor
415 size_t curLength = data.size();
416 // special case when broadcasting last dimensions (initial shapes must be the same)
417 if (size > 1 && shape.front() == targetShape.front() && shape.back() == 1) {
418 size_t bsize = targetShape.back();
419 // compute the size of the data to broadcast
420 for (int k = int(size)-2; k >=0; k--) {
421 if (shape[k] != 1) break;
422 bsize *= targetShape[k];
423 }
424 for (size_t i = 0; i < curLength; i++) {
425 std::fill(broadcastedData + i*bsize, broadcastedData + (i+1)*bsize , data[i]);
426 }
427 return;
428 }
429
430 std::copy(data.begin(), data.end(), broadcastedData);
431 // Product of the previous dimensions of targetShape
432 size_t arrayNum = 1;
433 // New broadcasted data: is this needed?
435
436 for (size_t idx = 0; idx < size; idx++) {
437 size_t dim = shape[idx];
438 size_t targetDim = targetShape[idx];
439 if (dim == 1 && targetDim > 1) {
440 // Set the new length of the data
441 size_t newLength = curLength * targetDim;
442 // View the data as a list of arrayNum arrays of size arrayLength
443 size_t arrayLength = curLength / arrayNum;
444 // Broadcast each array dim times
445 if (arrayLength > 1) {
446 // If each array has at least two elements
447 for (size_t arrayIdx = 0; arrayIdx < arrayNum; arrayIdx++) {
448 for (size_t targetIdx = 0; targetIdx < targetDim; targetIdx++) {
452 newData.begin() + offset);
453 }
454 }
455 } else {
456 // If each array has one element
457 for (size_t arrayIdx = 0; arrayIdx < arrayNum; arrayIdx++) {
458 std::fill(newData.begin() + arrayIdx * targetDim,
460 }
461 }
462 // Update current length
464 // Update broadcasted data
466 }
467 // Update the number of arrays
469 }
470}
471
472// interface where we allocate a new array for broadcasted data
473template<typename T>
474T* CreateBroadcastTensor(const T* data, const std::vector<size_t>& shape, const std::vector<size_t>& targetShape, size_t targetLength) {
475 // newShape is an array of size equal to dimension along which we are broadcasting the tensor
476 T* broadcastedData = new T[targetLength];
477 size_t curLength = ConvertShapeToLength(shape);
479 return broadcastedData;
480}
481// Unidirectional broadcasting shape to targetShape// In unidirectional broadcast - only tensor B can have the shape changed not
482// tensor A - otherwise is a multidirectional broadcast
483template<typename T>
484T* UnidirectionalBroadcast(const T* data, const std::vector<size_t>& shape, const std::vector<size_t>& targetShape) {
485 // Prepend shape with ones
486 if (shape.size() < targetShape.size()) {
487 size_t targetSize = targetShape.size();
488 std::vector<size_t> newShape(targetSize, 1);
489 size_t offset = targetSize - shape.size();
490 std::copy(shape.begin(), shape.end(), newShape.begin() + offset);
492 }
494}
495
496// Unidirectional broadcasting shape to targetShape using a passed vector to avoid allocations
497template<typename T>
498void UnidirectionalBroadcast(const T* data, const std::vector<size_t>& shape, const std::vector<size_t>& targetShape, T *broadcastedData) {
499 size_t curLength = ConvertShapeToLength(shape);
500 std::span<T> inData(const_cast<T*>(data), curLength);
501 // Prepend shape with ones
502 if (shape.size() < targetShape.size()) {
503 size_t targetSize = targetShape.size();
504 std::vector<size_t> newShape(targetSize, 1);
505 size_t offset = targetSize - shape.size();
506 std::copy(shape.begin(), shape.end(), newShape.begin() + offset);
508 }
510}
511
512/// compute stride of a tensor given its shape (assume layout is row-major)
513std::vector<size_t> ComputeStrideFromShape(const std::vector<size_t> & shape);
514std::vector<Dim> ComputeStrideFromShape(const std::vector<Dim> & shape);
515
516/// function to check if a >> 0 and a < MAX using a single comparison
517//// use trick casting to unsigned values so it becomes a single comparison
518inline bool is_a_ge_zero_and_a_lt_b(int a, int b) {
519 return static_cast<unsigned>(a) < static_cast<unsigned>(b);
520}
521
522
523/// im2col : efficient function to re-arrange input data of convolution to a matrix
524/// that can be used by BLAS
525/// Use trick to loop on each element of filtered region first and follow input data layout
526/// By doing this reads and writes are of consecutive data in memory and one gains in efficiency
527/// The resulting matrix will be already transposed and can be used directly in BLAS
528/// since output will be a matrix : (channels*kernel_h*kernel_w , output_h*output_w)
529/// Example: with an input matrix
530/// a1 a2 a3
531/// b1 b2 b3 and a 2x2 kernel (k1,k2,k3,k4) and padding 1 :
532/// c1 c2 c3
533/// outpout will be a matrix (4 x 16)
534/// the routine will follow output order :
535// first all elements which will be operated by k1 then k2 then k3
536/// -> ( 0 0 0 0 0 a1 a2 a3 0 b1 b2 b3 0 c1 c2 c3 ) all elements for k1
537/// ( 0 0 0 0 a1 a2 a3 0 b1 b2 b3 0 c1 c2 c3 0 ) for k2
538/// ( 0 a1 a2 a3 0 b1 b2 b3 0 c1 c2 c3 0 0 0 0 ) for k3
539/// ( a1 a2 a3 0 b1 b2 b3 0 c1 c2 c3 0 0 0 0 0 ) for k4
540///
541
542template <typename T>
543void Im2col(const T *data_im, const int channels, const int height, const int width, const int kernel_h,
544 const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w,
545 const int dilation_h, const int dilation_w, T *data_col)
546{
547 const int output_h = (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;
548 const int output_w = (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1;
549 const int channel_size = height * width;
550 for (int channel = channels; channel--; data_im += channel_size) {
551 for (int kernel_row = 0; kernel_row < kernel_h; kernel_row++) {
552 for (int kernel_col = 0; kernel_col < kernel_w; kernel_col++) {
557 *(data_col++) = 0;
558 }
559 } else {
561 for (int output_col = output_w; output_col; output_col--) {
564 } else {
565 *(data_col++) = 0;
566 }
568 }
569 }
571 }
572 }
573 }
574 }
575}
576
577/// 3d implementation
578template <typename T>
579void Im2col_3d(const T *data_im, const int channels,
580 const int depth, const int height, const int width,
581 const int kernel_d, const int kernel_h, const int kernel_w,
582 const int pad_d, const int pad_h, const int pad_w,
583 const int stride_d, const int stride_h, const int stride_w,
584 const int dilation_d, const int dilation_h, const int dilation_w, T *data_col)
585{
586 const int output_h = (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;
587 const int output_w = (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1;
588 const int output_d = (depth + 2 * pad_d - (dilation_d * (kernel_d - 1) + 1)) / stride_d + 1;
589 const int channel_size = height * width * depth;
590 // assume data are c x d x h x w
591 for (int channel = channels; channel--; data_im += channel_size) {
592 for (int kernel_depth = 0; kernel_depth < kernel_d; kernel_depth++) {
593 for (int kernel_row = 0; kernel_row < kernel_h; kernel_row++) {
594 for (int kernel_col = 0; kernel_col < kernel_w; kernel_col++) {
596 for (int output_dep = output_d; output_dep; output_dep--) {
600 *(data_col++) = 0;
601 }
602 }
603 } else {
608 *(data_col++) = 0;
609 }
610 } else {
612 for (int output_col = output_w; output_col; output_col--) {
615 } else {
616 *(data_col++) = 0;
617 }
619 }
620 }
622 }
623 }
625 }
626 }
627 }
628 }
629 }
630}
631
632template <typename Dtype>
633void col2im(const Dtype* data_col, const int channels,
634 const int height, const int width, const int kernel_h, const int kernel_w,
635 const int pad_h, const int pad_w,
636 const int stride_h, const int stride_w,
637 const int dilation_h, const int dilation_w,
638 Dtype* data_im) {
639 // note that output data_im needs to be set to zero value!!!!
640 std::fill(data_im, data_im + height * width * channels, 0.);
641 //caffe_set(height * width * channels, Dtype(0), data_im);
642 // data_im must be a zero vector
643 //const Dtype * data_col_0 = data_col;
644 const int output_h = (height + 2 * pad_h -
645 (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;
646 const int output_w = (width + 2 * pad_w -
647 (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1;
648 const int channel_size = height * width;
649 for (int channel = channels; channel--; data_im += channel_size) {
650 for (int kernel_row = 0; kernel_row < kernel_h; kernel_row++) {
651 for (int kernel_col = 0; kernel_col < kernel_w; kernel_col++) {
656 } else {
658 for (int output_col = output_w; output_col; output_col--) {
660 //assert(input_row*width+input_col < height * width * channels);
661 //assert(data_col - data_col_0 < output_h*output_w*channels);
662 // std::cout << "COL2IM: input_row" << " " << input_row << " " << input_col
663 // << " <---- " << data_col - data_col_0 << " values: "
664 // << data_im[input_row * width + input_col] << " <--- " << *data_col << std::endl;
666 }
667 data_col++;
669 }
670 }
672 }
673 }
674 }
675 }
676 //std::cout << "finishing col2imp" << std::endl;
677}
678
679} // end namespace UTILITY
680
681namespace BLAS{
682extern "C" void sgemm_(const char * transa, const char * transb, const int * m, const int * n, const int * k,
683 const float * alpha, const float * A, const int * lda, const float * B, const int * ldb,
684 const float * beta, float * C, const int * ldc);
685}//BLAS
686
687
688struct GNN_Data {
689 RTensor<float> node_data; // the node feature data, tensor with shape (num_nodes, num_node_features)
690 RTensor<float> edge_data; // the edge feature data, tensor with shape (num_edges, num_edge_features)
691 RTensor<float> global_data; // the global features, tensor with shape (1, num_global_features)
692 RTensor<int> edge_index; // the edge index (receivers and senders for each edge), tensor with shape (2, num_edges)
693 // edge_index[0,:] are the receivers and edge_index[1,:] are the senders
694
695
696 // need to have default constructor since RTensor has not one
698
699};
700
701template<typename T>
703{
704 // concatenate tensor along axis. Shape must be the same except in the dimension of the concatenated axis
705 if (t1.GetMemoryLayout() != t2.GetMemoryLayout())
706 throw std::runtime_error("TMVA RTensor Concatenate - tensors have different memory layout");
707 auto & shape1 = t1.GetShape();
708 auto & shape2 = t2.GetShape();
709 if (t1.GetSize()/shape1[axis] != t2.GetSize()/shape2[axis]) {
710 std::cout << "axis " << axis << " sizes " << t1.GetSize() << " " << t2.GetSize() << " ";
711 std::cout << "shape 1 : " << ConvertShapeToString(t1.GetShape());
712 std::cout << " shape 2 : " << ConvertShapeToString(t2.GetShape()) << std::endl;
713 throw std::runtime_error("TMVA RTensor Concatenate - tensors have incompatible shapes");
714 }
715 std::vector<size_t> outShape = shape1;
716 outShape[axis] = shape1[axis] + shape2[axis];
718 if (t1.GetMemoryLayout() == TMVA::Experimental::MemoryLayout::ColumnMajor) {
719 throw std::runtime_error("TMVA RTensor Concatenate is not yet supported for column major tensors");
720 }
721
722 auto & stride1 = t1.GetStrides();
723 auto & stride2 = t2.GetStrides();
724 auto & outStride = tout.GetStrides();
725
726 size_t s1 = (axis > 0) ? stride1[axis-1] : t1.GetSize(); // block size to copy from first tensor
727 size_t s2 = (axis > 0) ? stride2[axis-1] : t2.GetSize(); // block size to copy from second tensor
728 size_t sout = (axis > 0) ? outStride[axis-1] : tout.GetSize();
729 size_t nb = t1.GetSize()/s1;
730 for (size_t i = 0; i < nb; i++) {
731 std::copy(t1.GetData() + i*s1, t1.GetData() + (i+1)*s1, tout.GetData() + i * sout );
732 std::copy(t2.GetData() + i*s2, t2.GetData() + (i+1)*s2, tout.GetData() + i * sout + s1 );
733 }
734
735 return tout;
736}
737
738
739inline GNN_Data Concatenate(GNN_Data & data1, GNN_Data & data2, int axis = 0) {
740 GNN_Data out;
741 out.node_data = Concatenate(data1.node_data,data2.node_data, axis);
742 out.edge_data = Concatenate(data1.edge_data,data2.edge_data, axis);
743 out.global_data = Concatenate<float>(data1.global_data,data2.global_data, axis-1);
744 // assume sender/receivers of data1 and data2 are the same
745 out.edge_index = data1.edge_index.Copy();
746 return out;
747}
748
749inline GNN_Data Copy(const GNN_Data & data) {
750 GNN_Data out;
751 out.node_data = RTensor<float>(data.node_data.GetShape());
752 out.edge_data = RTensor<float>(data.edge_data.GetShape());
753 out.global_data = RTensor<float>(data.global_data.GetShape());
754 out.edge_index = RTensor<int>(data.edge_index.GetShape());
755 std::copy(data.node_data.GetData(), data.node_data.GetData()+ data.node_data.GetSize(), out.node_data.GetData());
756 std::copy(data.edge_data.GetData(), data.edge_data.GetData()+ data.edge_data.GetSize(), out.edge_data.GetData());
757 std::copy(data.global_data.GetData(), data.global_data.GetData()+ data.global_data.GetSize(), out.global_data.GetData());
758 std::copy(data.edge_index.GetData(), data.edge_index.GetData()+ data.edge_index.GetSize(), out.edge_index.GetData());
759 return out;
760}
761
762inline void Gemm_Call(float *output, bool transa, bool transb, int m, int n, int k, float alpha, const float *A,
763 const float *B, float beta, const float *C)
764{
765 char ct = 't';
766 char cn = 'n';
767 const int *lda = transa ? &k : &m;
768 const int *ldb = transb ? &n : &k;
769 const int *ldc = &m;
770 if (C != nullptr) {
771 std::copy(C, C + m * n, output);
772 }
773 TMVA::Experimental::SOFIE::BLAS::sgemm_(transa ? &ct : &cn, transb ? &ct : &cn, &m, &n, &k, &alpha, A, lda, B, ldb,
774 &beta, output, ldc);
775}
776
777inline void Fill(float *output, float value, int size)
778{
779 std::fill(output, output + size, value);
780}
781
782inline void Copy(float *output, float const *input, int size)
783{
784 std::copy(input, input + size, output);
785}
786
787inline void Relu(float *output, float const *input, int size)
788{
789 for (int i = 0; i < size; i++) {
790 output[i] = (input[i] > 0.0f) ? input[i] : 0.0f;
791 }
792}
793// function to read float from the file dealing with inf and nan values
794inline float ParseFloatToken (const std::string & s) {
795 if (s == "inf") return std::numeric_limits<float>::infinity();
796 if (s == "-inf") return -std::numeric_limits<float>::infinity();
797 if (s == "nan") return std::numeric_limits<float>::quiet_NaN();
798 return std::stof(s);
799}
800
801template <class T>
802void ReadTensorFromStream(std::istream &is, T &target, std::string const &expectedName, std::size_t expectedLength)
803{
804 std::string name;
805 std::size_t length;
806 is >> name >> length;
807 if (name != expectedName) {
808 std::string err_msg =
809 "TMVA-SOFIE failed to read the correct tensor name; expected name is " + expectedName + " , read " + name;
810 throw std::runtime_error(err_msg);
811 }
812 if (length != expectedLength) {
813 std::string err_msg = "TMVA-SOFIE failed to read the correct tensor size; expected size is " +
814 std::to_string(expectedLength) + " , read " + std::to_string(length);
815 throw std::runtime_error(err_msg);
816 }
817 std::string token;
818 for (size_t i = 0; i < length; ++i) {
819 is >> token;
820 target[i] = ParseFloatToken(token);
821 }
822 if (is.fail()) {
823 throw std::runtime_error("TMVA-SOFIE failed to read the values for tensor " + expectedName);
824 }
825}
826
827//Utility functions to generate code
828void EmitNestedLoops(std::stringstream &out, size_t loopRank, const std::vector<Dim> shape);
829void CloseNestedLoops(std::stringstream &out, size_t loopRank);
830
831
832// code for the memory greeding allocations
834 int begin; // start time (op index) lifetime
835 int end; // end time lifetime
836 size_t size; // size of tensors in bytes
837};
838
840 std::size_t total_bytes = 0; // total memory needed
841 std::vector<size_t> offsets; // resulted offsets for each tensor
842};
843
844/// Greedy best-fit planner with coalescing free list.
845MemoryResult OrganizeMemory(const std::vector<TensorLifeInfo> & tensorsInfo );
846
847// Simple Dimension classes ans helpers to add constexpr meta info on input
848// tensors to the emitted code.
849struct SingleDim {
850 enum class Kind {
851 Static,
853 };
854
856 std::size_t dim;
857 std::string_view name;
858
859 constexpr SingleDim(std::size_t v) : kind(Kind::Static), dim(v), name() {}
860 constexpr SingleDim(const char *v) : kind(Kind::Symbolic), dim(0), name(v) {}
861};
862
865 std::size_t size;
866
867 constexpr std::size_t total_size() const
868 {
869 std::size_t result = 1;
870 for (std::size_t i = 0; i < size; ++i) {
871 result *= data[i].dim;
872 }
873 return result;
874 }
875};
876
877template<class Arr>
878constexpr TensorDims makeDims(Arr const &arr)
879{
880 return TensorDims{arr.data(), arr.size()};
881}
882
883} // namespace SOFIE
884} // namespace Experimental
885} // namespace TMVA
886
887#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:145
#define malloc
Definition civetweb.c:1575
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
char * fPersistentData
[fSize] Persistent version of the data
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)
int fSize
The size of the persistent data in bytes (not number of elements!)
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)
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)
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)
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
static void output()