Loading...
Searching...
No Matches
tensor_types.h
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 Artificial Intelligence Techniques, S.L. and Roberto López González
3
4#pragma once
5
9#include <cstdio>
10
11namespace opennn
12{
13
14template<Type T> struct TypeInfo;
15
16template<> struct TypeInfo<Type::FP32>
17{
18 using type = float;
19 static constexpr DnnDataType cudnn = CUDNN_DATA_FLOAT;
20 static constexpr DeviceDataType cuda = CUDA_R_32F;
21 static constexpr Index bytes = Index(sizeof(float));
22 static constexpr const char* name = "FP32";
23};
24
25template<> struct TypeInfo<Type::BF16>
26{
27 using type = bfloat16;
28 static constexpr DnnDataType cudnn = CUDNN_DATA_BFLOAT16;
29 static constexpr DeviceDataType cuda = CUDA_R_16BF;
30 static constexpr Index bytes = Index(sizeof(bfloat16));
31 static constexpr const char* name = "BF16";
32};
33
34template<> struct TypeInfo<Type::INT8>
35{
36 using type = int8_t;
37 static constexpr DnnDataType cudnn = CUDNN_DATA_INT8;
38 static constexpr DeviceDataType cuda = CUDA_R_8I;
39 static constexpr Index bytes = Index(sizeof(int8_t));
40 static constexpr const char* name = "INT8";
41};
42
43template<Type... Supported, typename F>
44void visit_type(Type t, F&& f)
45{
46 bool matched = false;
47 ([&]
48 {
49 if (!matched && t == Supported)
50 {
51 f.template operator()<typename TypeInfo<Supported>::type>();
52 matched = true;
53 }
54 }(), ...);
55 throw_if(!matched, "visit_type: unsupported Type value");
56}
57
58template<Type... Supported, typename F>
59void visit_type_pair(Type t_in, Type t_out, F&& f)
60{
61 visit_type<Supported...>(t_in, [&]<typename TIn>()
62 {
63 visit_type<Supported...>(t_out, [&]<typename TOut>()
64 {
65 f.template operator()<TIn, TOut>();
66 });
67 });
68}
69
70template<typename F>
71inline auto with_type_info(Type type, const char* caller, F&& f)
72{
73 switch (type)
74 {
75 case Type::FP32: return f(TypeInfo<Type::FP32>{});
76 case Type::BF16: return f(TypeInfo<Type::BF16>{});
77 case Type::INT8: return f(TypeInfo<Type::INT8>{});
78 case Type::Auto: break;
79 }
80
81 throw runtime_error(string(caller) + ": Type::Auto must be resolved before tensor use.");
82}
83
85{
86 return with_type_info(type, "to_cudnn", [](auto info) { return info.cudnn; });
87}
88
90{
91 return with_type_info(type, "to_cuda", [](auto info) { return info.cuda; });
92}
93
94inline Index type_bytes(Type type)
95{
96 return with_type_info(type, "type_bytes", [](auto info) { return info.bytes; });
97}
98
99static constexpr Index ALIGN_BYTES = EIGEN_MAX_ALIGN_BYTES;
100static constexpr Index ALIGN_ELEMENTS = ALIGN_BYTES / sizeof(float);
101
102inline int to_int(Index value)
103{
104 throw_if(value > Index(numeric_limits<int>::max()) || value < Index(numeric_limits<int>::min()),
105 "to_int: value {} exceeds int range.", value);
106 return static_cast<int>(value);
107}
108inline float to_type(Index value) { return static_cast<float>(value); }
109
110namespace detail
111{
112
113inline Index checked_index_add(Index left, Index right, string_view operation)
114{
115 throw_if(left < 0 || right < 0, "{}: values cannot be negative.", operation);
116 throw_if(left > numeric_limits<Index>::max() - right,
117 "{}: Index addition overflow.", operation);
118 return left + right;
119}
120
121inline Index checked_index_multiply(Index left, Index right, string_view operation)
122{
123 throw_if(left < 0 || right < 0, "{}: values cannot be negative.", operation);
124 if (left == 0 || right == 0) return 0;
125 throw_if(left > numeric_limits<Index>::max() / right,
126 "{}: Index multiplication overflow.", operation);
127 return left * right;
128}
129
130template<typename Range, typename Projection>
131Index checked_index_sum(const Range& values, Projection projection, string_view operation)
132{
133 Index total = 0;
134 for (const auto& value : values)
135 total = checked_index_add(total, projection(value), operation);
136 return total;
137}
138
139}
140
141inline Index align_up(Index value, Index alignment)
142{
143 throw_if(alignment <= 0 || (alignment & (alignment - 1)) != 0,
144 "align_up: alignment must be a positive power of two.");
145 if (value == 0) return 0;
146 return detail::checked_index_add(value, alignment - 1, "align_up") & ~(alignment - 1);
147}
148
149inline Index ceil_div(Index value, Index divisor)
150{
151 throw_if(value < 0 || divisor <= 0,
152 "ceil_div: value must be non-negative and divisor must be positive.");
153 return value / divisor + Index(value % divisor != 0);
154}
155
156inline Index get_aligned_size(Index size) { return align_up(size, ALIGN_ELEMENTS); }
157inline Index get_aligned_bytes(Index n_bytes) { return align_up(n_bytes, ALIGN_BYTES); }
158inline Index get_aligned_bytes(Index count, Type dtype)
159{
161 "get_aligned_bytes"));
162}
163
164inline bool is_aligned(const void* ptr)
165{
166 return reinterpret_cast<uintptr_t>(ptr) % ALIGN_BYTES == 0;
167}
168
169constexpr BlasComputeType CUBLAS_COMPUTE_DTYPE = CUBLAS_COMPUTE_32F_FAST_TF32;
170
171struct Shape
172{
173 static constexpr size_t MaxRank = 4;
174
175 Shape() noexcept = default;
176
177 static Shape filled(size_t rank, Index value)
178 {
179 throw_if(rank > MaxRank, "Shape::filled: rank {} exceeds MaxRank={}.", rank, MaxRank);
180 throw_if(value < 0, "Shape::filled: dimensions cannot be negative.");
181
182 Shape shape;
183 shape.rank = rank;
184 fill_n(shape.dims, rank, value);
185
186 return shape;
187 }
188
189 Shape(initializer_list<Index> list) : rank(list.size())
190 {
191 throw_if(list.size() > MaxRank,
192 "Shape: initializer rank {} exceeds MaxRank={}.",
193 list.size(), MaxRank);
194 throw_if(ranges::any_of(list, [](Index value) { return value < 0; }),
195 "Shape: dimensions cannot be negative.");
196 copy_n(list.begin(), rank, dims);
197 }
198
199 template<typename It>
200 Shape(It first, It last) : rank(static_cast<size_t>(distance(first, last)))
201 {
202 throw_if(rank > MaxRank,
203 "Shape: iterator-pair rank {} exceeds MaxRank={}.",
204 rank, MaxRank);
205 throw_if(any_of(first, last, [](Index value) { return value < 0; }),
206 "Shape: dimensions cannot be negative.");
207 copy_n(first, rank, dims);
208 }
209
210 size_t get_rank() const noexcept { return rank; }
211
212 const Index* begin() const noexcept { return dims; }
213 const Index* end() const noexcept { return dims + rank; }
214 Index operator[](size_t i) const noexcept { return dims[i]; }
215 Index back() const { throw_if(rank == 0, "Shape::back() on empty"); return dims[rank - 1]; }
216
217 bool empty() const noexcept { return rank == 0; }
218
219 Index dim_or_zero(size_t i) const noexcept { return i < rank ? dims[i] : Index(0); }
220
221 Index size() const
222 {
223 throw_if(rank > MaxRank, "Shape::size: rank exceeds MaxRank={}.", MaxRank);
224 if (rank == 0) return 0;
225
226 Index element_count = 1;
227 for (const Index dimension : *this)
228 element_count = detail::checked_index_multiply(element_count, dimension,
229 "Shape::size");
230 return element_count;
231 }
232
233 void clear() noexcept { rank = 0; }
234
235 void set_dimension(size_t index, Index value)
236 {
237 throw_if(index >= rank,
238 "Shape::set_dimension: index {} is out of range for rank {}.",
239 index, rank);
240 throw_if(value < 0, "Shape::set_dimension: dimensions cannot be negative.");
241 dims[index] = value;
242 }
243
244 void push_back(Index value)
245 {
246 throw_if(rank >= MaxRank, "Shape::push_back: rank exceeds MaxRank={}.", MaxRank);
247 throw_if(value < 0, "Shape::push_back: dimensions cannot be negative.");
248 dims[rank++] = value;
249 }
250
251 friend ostream& operator<<(ostream& os, const Shape& shape)
252 {
253 os << "[";
254 for (size_t i = 0; i < shape.get_rank(); ++i) os << (i ? ", " : " ") << shape.dims[i];
255 os << " ]";
256 return os;
257 }
258
259 bool operator==(const Shape& other) const noexcept
260 {
261 return rank == other.get_rank() && equal(begin(), end(), other.begin());
262 }
263
264 Shape& append(const Shape& other)
265 {
266 throw_if(rank > MaxRank || other.get_rank() > MaxRank - rank,
267 "Shape::append: combined rank exceeds MaxRank={}.", MaxRank);
268 throw_if(any_of(other.begin(), other.end(), [](Index value) { return value < 0; }),
269 "Shape::append: dimensions cannot be negative.");
270 copy_n(other.dims, other.get_rank(), dims + rank);
271 rank += other.get_rank();
272 return *this;
273 }
274
275private:
276 Index dims[MaxRank] = {0};
277 size_t rank = 0;
278};
279
281{
284
285 bool operator==(const TensorSpec&) const noexcept = default;
286};
287
288inline Index get_aligned_size(const vector<TensorSpec>& specs)
289{
290 return detail::checked_index_sum(specs,
291 [](const auto& spec) { return get_aligned_size(spec.shape.size()); },
292 "get_aligned_size(TensorSpec list)");
293}
294
295inline Index get_aligned_size(const vector<vector<TensorSpec>>& specs)
296{
297 return detail::checked_index_sum(specs,
298 [](const auto& group) { return get_aligned_size(group); },
299 "get_aligned_size(TensorSpec groups)");
300}
301
302inline Index get_aligned_bytes(const TensorSpec& spec) { return get_aligned_bytes(spec.shape.size(), spec.dtype); }
303
304inline Index get_aligned_bytes(const vector<TensorSpec>& specs)
305{
306 return detail::checked_index_sum(specs,
307 [](const TensorSpec& spec) { return get_aligned_bytes(spec); },
308 "get_aligned_bytes(TensorSpec list)");
309}
310
311inline Index get_aligned_bytes(const vector<vector<TensorSpec>>& specs)
312{
313 return detail::checked_index_sum(specs,
314 [](const auto& group) { return get_aligned_bytes(group); },
315 "get_aligned_bytes(TensorSpec groups)");
316}
317
318inline Index get_aligned_bytes(const vector<Shape>& shapes, Type dtype)
319{
320 return detail::checked_index_sum(shapes,
321 [dtype](const Shape& shape) { return get_aligned_bytes(shape.size(), dtype); },
322 "get_aligned_bytes(Shape list)");
323}
324
325inline Index get_aligned_bytes(const vector<TensorSpec>& specs, Type dtype)
326{
327 return detail::checked_index_sum(specs,
328 [dtype](const auto& spec) { return get_aligned_bytes(spec.shape.size(), dtype); },
329 "get_aligned_bytes(TensorSpec list, dtype)");
330}
331
332inline Index get_aligned_bytes(const vector<vector<TensorSpec>>& specs, Type dtype)
333{
334 return detail::checked_index_sum(specs,
335 [dtype](const auto& group) { return get_aligned_bytes(group, dtype); },
336 "get_aligned_bytes(TensorSpec groups, dtype)");
337}
338
339struct Buffer
340{
341 explicit Buffer(Device initial_device = Device::CPU) : allocation_device(initial_device)
342 {
343 validate_device(initial_device, "Buffer");
344 }
345 Buffer(const Buffer&) = delete;
346 Buffer& operator=(const Buffer&) = delete;
347
348 Buffer(Buffer&& other) noexcept : Buffer() { swap(other); }
349 Buffer& operator=(Buffer&& other) noexcept
350 {
351 if (this == &other) return *this;
352
353 free_buffer();
354 pointer = other.pointer;
355 allocated_bytes = other.allocated_bytes;
356 allocation_device = other.allocation_device;
357 owns_allocation = other.owns_allocation;
358
359 other.pointer = nullptr;
360 other.allocated_bytes = 0;
361 other.allocation_device = Device::CPU;
362 other.owns_allocation = true;
363
364 return *this;
365 }
366
367 ~Buffer() { free_buffer(); }
368
369 template<typename T>
370 T* as()
371 {
372 validate_state("Buffer::as");
373 return static_cast<T*>(pointer);
374 }
375
376 template<typename T>
377 const T* as() const
378 {
379 validate_state("Buffer::as");
380 return static_cast<const T*>(pointer);
381 }
382
383 void* data() const noexcept { return pointer; }
384 Index byte_size() const noexcept { return allocated_bytes; }
385 Device get_device() const noexcept { return allocation_device; }
386 bool owns_memory() const noexcept { return owns_allocation; }
387 Index size_in_floats() const noexcept { return allocated_bytes / Index(sizeof(float)); }
388 bool empty() const noexcept { return allocated_bytes == 0; }
389
391 {
392 validate_state("Buffer::as_vector");
393 throw_if(allocation_device != Device::CPU,
394 "Buffer::as_vector requires host storage.");
395 throw_if(allocated_bytes % Index(sizeof(float)) != 0,
396 "Buffer::as_vector requires a whole number of floats.");
397 return VectorMap(static_cast<float*>(pointer), size_in_floats());
398 }
399
401 {
402 validate_state("Buffer::as_vector");
403 throw_if(allocation_device != Device::CPU,
404 "Buffer::as_vector requires host storage.");
405 throw_if(allocated_bytes % Index(sizeof(float)) != 0,
406 "Buffer::as_vector requires a whole number of floats.");
407 return ConstVectorMap(static_cast<const float*>(pointer), size_in_floats());
408 }
409
410 void resize_bytes(Index byte_count, Device new_device)
411 {
412 validate_state("Buffer::resize_bytes");
413 validate_size_and_device(byte_count, new_device, "Buffer::resize_bytes");
414 if (owns_allocation
415 && byte_count == allocated_bytes
416 && allocation_device == new_device) return;
417
418 const bool changes_cuda_allocation =
419 (allocation_device == Device::CUDA && pointer)
420 || (new_device == Device::CUDA && byte_count > 0);
421 throw_if(changes_cuda_allocation && device::cuda_allocation_growth_forbidden(),
422 "CUDA buffer resize from {} to {} bytes while CUDA allocation growth is forbidden "
423 "(warmup incomplete before CUDA graph capture).",
424 allocated_bytes,
425 byte_count);
426
427 Buffer replacement(new_device);
428 if (byte_count > 0)
429 {
430 replacement.pointer = device::allocate(new_device, byte_count);
431 replacement.allocated_bytes = byte_count;
432 }
433
434 swap(replacement);
435 }
436
437 void set_view(void* external_data, Index byte_count, Device view_device)
438 {
439 validate_state("Buffer::set_view");
440 validate_size_and_device(byte_count, view_device, "Buffer::set_view");
441 throw_if((external_data == nullptr) != (byte_count == 0),
442 "Buffer::set_view requires null data exactly when the view is empty.");
443 throw_if(owns_allocation && pointer && external_data == pointer,
444 "Buffer::set_view cannot alias the buffer's owned allocation.");
445
446 Buffer replacement(view_device);
447 replacement.pointer = external_data;
448 replacement.allocated_bytes = byte_count;
449 replacement.owns_allocation = false;
450 swap(replacement);
451 }
452
453 void grow_to(Index minimum_bytes)
454 {
455 validate_state("Buffer::grow_to");
456 throw_if(minimum_bytes < 0, "Buffer::grow_to size cannot be negative.");
457 if (minimum_bytes > allocated_bytes)
458 resize_bytes(minimum_bytes, allocation_device);
459 }
460
461 template<typename T>
462 T* ensure(Index element_count)
463 {
464 grow_to(detail::checked_index_multiply(element_count, Index(sizeof(T)),
465 "Buffer::ensure"));
466 return as<T>();
467 }
468
469 void setZero()
470 {
471 validate_state("Buffer::setZero");
472 device::set_zero(pointer, allocated_bytes, allocation_device);
473 }
474
475 void migrate_to(Device target_device, DeviceStream stream = nullptr)
476 {
477 validate_state("Buffer::migrate_to");
478 validate_device(target_device, "Buffer::migrate_to");
479 if (allocation_device == target_device) return;
480
481 Buffer target_buffer(target_device);
482 if (!pointer)
483 return swap(target_buffer);
484
485 target_buffer.resize_bytes(allocated_bytes, target_device);
486 device::copy_async(target_buffer.pointer, pointer, allocated_bytes,
487 allocation_device, target_device, stream);
488 if (stream) device::synchronize(stream);
489
490 swap(target_buffer);
491 }
492
493 void swap(Buffer& other) noexcept
494 {
495 std::swap(pointer, other.pointer);
496 std::swap(allocated_bytes, other.allocated_bytes);
497 std::swap(allocation_device, other.allocation_device);
498 std::swap(owns_allocation, other.owns_allocation);
499 }
500
501private:
502
503 static void validate_device(Device device,
504 string_view operation)
505 {
506 throw_if(device == Device::Auto, "{} requires a concrete device.", operation);
507 }
508
509 static void validate_size_and_device(Index byte_count,
510 Device device,
511 string_view operation)
512 {
513 throw_if(byte_count < 0, "{} size cannot be negative.", operation);
514 validate_device(device, operation);
515 }
516
517 void validate_state(string_view operation) const
518 {
519 validate_size_and_device(allocated_bytes, allocation_device, operation);
520 throw_if((pointer == nullptr) != (allocated_bytes == 0),
521 "{} found inconsistent data and byte count.", operation);
522 }
523
524 void free_buffer()
525 {
526 if (pointer && owns_allocation)
527 device::deallocate(allocation_device, pointer, allocated_bytes);
528 pointer = nullptr;
529 allocated_bytes = 0;
530 owns_allocation = true;
531 }
532
533 void* pointer = nullptr;
534 Index allocated_bytes = 0;
535 Device allocation_device = Device::CPU;
536 bool owns_allocation = true;
537};
538
540{
541 TensorView(void* new_data = nullptr,
542 const Shape& new_shape = {},
543 Type new_type = Type::FP32,
544 Device new_device = Device::CPU) noexcept
545 : data(new_data),
546 shape(new_shape),
547 type(new_type),
548 device(new_device) {}
549
550 void* get_data() const noexcept { return data; }
551 const Shape& get_shape() const noexcept { return shape; }
552 Type get_type() const noexcept { return type; }
553 Device get_device() const noexcept { return device; }
554 Index get_rank() const noexcept { return Index(shape.get_rank()); }
555
556 Index size() const { return shape.size(); }
557
558 Index byte_size() const
559 {
561 "TensorView::byte_size");
562 }
563
564 Index flat_columns() const noexcept { return shape.get_rank() == 0 ? 0 : shape[shape.get_rank() - 1]; }
565 Index flat_rows() const noexcept
566 {
567 const Index columns = flat_columns();
568 return columns == 0 ? 0 : shape.size() / columns;
569 }
570
571 bool empty() const noexcept { return shape.empty(); }
572 bool is_cuda() const noexcept { return device == Device::CUDA; }
573 bool is_fp32() const noexcept { return type == Type::FP32; }
574 bool is_bf16() const noexcept { return type == Type::BF16; }
575 bool is_int8() const noexcept { return type == Type::INT8; }
576
577 template<typename T>
578 T* as() const noexcept
579 {
580 assert(data);
581 return reinterpret_cast<T*>(data);
582 }
583
584 float* as_float() const noexcept
585 {
586 return reinterpret_cast<float*>(data);
587 }
588
589 DeviceDataType cuda_dtype() const { return to_cuda(type); }
590
591 template<typename F>
592 void dispatch(F&& fn) const
593 {
595 }
596
597 TensorView reshape(const Shape& new_shape) const
598 {
599 const Index current_size = size();
600 throw_if(new_shape.size() != current_size,
601 "TensorView::reshape cannot change the element count from {} to {}.",
602 current_size, new_shape.size());
603 return TensorView(data, new_shape, type, device);
604 }
605
606 TensorView reshape_prefix(const Shape& new_shape) const
607 {
608 const Index current_size = size();
609 throw_if(new_shape.size() > current_size,
610 "TensorView::reshape_prefix cannot grow the element count from {} to {}.",
611 current_size, new_shape.size());
612 return TensorView(data, new_shape, type, device);
613 }
614
616 {
617 require_host_fp32_data("TensorView::as_matrix");
618 throw_if(shape.get_rank() < 2, "TensorView::as_matrix requires rank >= 2.");
619
620 const Index row_count = shape[0];
621 const Index column_count = row_count == 0 ? 0 : shape.size() / row_count;
622 return MatrixMap(reinterpret_cast<float*>(data), row_count, column_count);
623 }
624
625 MatrixMap as_matrix(Index matrix_index) const
626 {
627 require_host_fp32_data("TensorView::as_matrix(matrix_index)");
628 throw_if(shape.get_rank() < 2,
629 "TensorView::as_matrix(matrix_index) requires rank >= 2.");
630
631 const Index row_count = shape[shape.get_rank() - 2];
632 const Index column_count = shape[shape.get_rank() - 1];
633 const Index matrix_element_count = row_count * column_count;
634 const Index matrix_count = matrix_element_count == 0
635 ? 0
636 : shape.size() / matrix_element_count;
637
638 throw_if(matrix_index < 0 || matrix_index >= matrix_count,
639 "TensorView::as_matrix(matrix_index): matrix index {} out of range [0, {}).",
640 matrix_index, matrix_count);
641
642 return MatrixMap(reinterpret_cast<float*>(data) + matrix_index * matrix_element_count,
643 row_count,
644 column_count);
645 }
646
648 {
649 require_host_fp32_data("TensorView::as_flat_matrix");
650 throw_if(shape.get_rank() < 1, "TensorView::as_flat_matrix requires rank >= 1.");
651
652 return MatrixMap(reinterpret_cast<float*>(data), flat_rows(), flat_columns());
653 }
654
656 {
657 require_host_fp32_data("TensorView::as_vector");
658 return VectorMap(reinterpret_cast<float*>(data), shape.size());
659 }
660
661 template<int Rank>
663 {
664 require_host_fp32_data("TensorView::as_tensor");
665 throw_if(shape.get_rank() != Rank,
666 "TensorView::as_tensor requires rank {}, got {}.", Rank, shape.get_rank());
667
668 Eigen::array<Index, Rank> dims;
669 copy_n(shape.begin(), Rank, dims.begin());
670 return TensorMapR<Rank>(reinterpret_cast<float*>(data), dims);
671 }
672
673 template<int Rank>
674 TensorMapR<Rank> as_tensor(Index batch_index) const
675 {
676 require_host_fp32_data("TensorView::as_tensor(batch_index)");
677 throw_if(shape.get_rank() != Rank + 1,
678 "TensorView::as_tensor(batch_index) requires rank {}, got {}.",
679 Rank + 1, shape.get_rank());
680 throw_if(batch_index < 0 || batch_index >= shape[0],
681 "TensorView::as_tensor(batch_index): batch index {} out of range [0, {}).",
682 batch_index, shape[0]);
683
684 Eigen::array<Index, Rank> dims;
685 for (int i = 0; i < Rank; ++i) dims[i] = shape[i + 1];
686 const Index slice_element_count = shape.size() / shape[0];
687 return TensorMapR<Rank>(reinterpret_cast<float*>(data)
688 + batch_index * slice_element_count,
689 dims);
690 }
691
692 void fill(float) const;
693 void setZero() const { fill(0.0f); }
694 void set_zero_async() const;
695
697
698private:
699 void require_host_fp32(string_view accessor) const
700 {
701 if (device != Device::CPU || type != Type::FP32)
702 {
703 const char* dev_str = (device == Device::CUDA) ? "CUDA" : "CPU";
704 const char* typ_str = (type == Type::BF16) ? "BF16" : (type == Type::FP32 ? "FP32" : "other");
705 throw_if(true, "{} requires CPU FP32 storage. [device={} type={} rank={}]",
706 accessor, dev_str, typ_str, int(shape.get_rank()));
707 }
708 }
709
710 void require_host_fp32_data(string_view accessor) const
711 {
712 require_host_fp32(accessor);
713 throw_if(shape.size() > 0 && !data, "{} requires non-null data.", accessor);
714 }
715
716 void* data = nullptr;
717 Shape shape;
718 Type type = Type::FP32;
719 Device device = Device::CPU;
720};
721
722inline TensorView& slot_or(vector<TensorView>& views,
723 const vector<size_t>& slot_indices,
724 size_t i,
725 TensorView& fallback)
726{
727 if (i >= slot_indices.size()) return fallback;
728
729 throw_if(slot_indices[i] >= views.size(),
730 "slot_or: slot index {} is out of range for {} views.",
731 slot_indices[i], views.size());
732 return views[slot_indices[i]];
733}
734
735template<typename T, size_t N>
736using array = Eigen::array<T, N>;
737
738string shape_to_string(const Shape&, const string& = " ");
739Shape string_to_shape(const string&, const string& = " ");
740
741inline bool is_contiguous(const vector<Index>& indices)
742{
743 return ranges::adjacent_find(indices,
744 [](Index a, Index b) { return b != a + 1; }) == indices.end();
745}
746
748
749inline ColumnContiguity classify_column_contiguity(const vector<Index>& indices)
750{
753}
754
755inline bool resolve_column_contiguity(ColumnContiguity column_contiguity,
756 const vector<Index>& indices)
757{
758 return column_contiguity == ColumnContiguity::Unknown
759 ? is_contiguous(indices)
760 : column_contiguity == ColumnContiguity::Contiguous;
761}
762
763void fill_tensor_data(const MatrixR&, const vector<Index>&, const vector<Index>&,
765
766template<typename... Vs>
767size_t hash_combine(const Vs&... values)
768{
769 size_t h = 0;
770 ((h ^= hash<Vs>{}(values) + 0x9e3779b9 + (h << 6) + (h >> 2)), ...);
771 return h;
772}
773
774inline void TensorView::set_zero_async() const
775{
776 if (!data || byte_size() == 0) return;
778}
779
780inline const float one = 1.0f;
781inline const float zero = 0.0f;
782
784 Index, float*,
785 DeviceStream stream);
787 Index, float*,
788 DeviceStream stream,
789 vector<uint16_t>& bf16_staging);
790
791}
Index checked_index_sum(const Range &values, Projection projection, string_view operation)
Definition tensor_types.h:131
Index checked_index_multiply(Index left, Index right, string_view operation)
Definition tensor_types.h:121
Index checked_index_add(Index left, Index right, string_view operation)
Definition tensor_types.h:113
Definition energy_meter.h:7
void set_zero(void *, Index, Device)
void copy_async(void *, const void *, Index, CopyKind, DeviceStream=nullptr)
void * allocate(Device, Index)
bool cuda_allocation_growth_forbidden() noexcept
DeviceStream get_compute_stream()
void deallocate(Device, void *, Index) noexcept
void set_zero_async(void *, Index, DeviceStream=nullptr)
void synchronize(DeviceStream=nullptr)
Definition configuration.h:10
ColumnContiguity classify_column_contiguity(const vector< Index > &indices)
Definition tensor_types.h:749
bool is_aligned(const void *ptr)
Definition tensor_types.h:164
cudaDataType_t DeviceDataType
Definition opennn_types.h:168
size_t hash_combine(const Vs &... values)
Definition tensor_types.h:767
const float one
Definition tensor_types.h:780
float to_type(Index value)
Definition tensor_types.h:108
DeviceDataType to_cuda(Type type)
Definition tensor_types.h:89
Device
Definition configuration.h:12
@ Auto
Definition configuration.h:12
@ CPU
Definition configuration.h:12
@ CUDA
Definition configuration.h:12
Index get_aligned_bytes(Index n_bytes)
Definition tensor_types.h:157
Eigen::array< T, N > array
Definition tensor_types.h:736
void copy_device_to_host_float(const void *, Type, Index, float *, DeviceStream stream)
Index ceil_div(Index value, Index divisor)
Definition tensor_types.h:149
DnnDataType to_cudnn(Type type)
Definition tensor_types.h:84
Shape string_to_shape(const string &, const string &=" ")
constexpr BlasComputeType CUBLAS_COMPUTE_DTYPE
Definition tensor_types.h:169
const float zero
Definition tensor_types.h:781
bool is_contiguous(const vector< Index > &indices)
Definition tensor_types.h:741
Index get_aligned_size(Index size)
Definition tensor_types.h:156
cudnnDataType_t DnnDataType
Definition opennn_types.h:143
Type
Definition configuration.h:13
@ Auto
Definition configuration.h:13
@ FP32
Definition configuration.h:13
@ INT8
Definition configuration.h:13
@ BF16
Definition configuration.h:13
int to_int(Index value)
Definition tensor_types.h:102
auto with_type_info(Type type, const char *caller, F &&f)
Definition tensor_types.h:71
cublasComputeType_t BlasComputeType
Definition opennn_types.h:159
void fill_tensor_data(const MatrixR &, const vector< Index > &, const vector< Index > &, span< float >, ColumnContiguity=ColumnContiguity::Unknown)
Index type_bytes(Type type)
Definition tensor_types.h:94
void visit_type(Type t, F &&f)
Definition tensor_types.h:44
string shape_to_string(const Shape &, const string &=" ")
TensorView & slot_or(vector< TensorView > &views, const vector< size_t > &slot_indices, size_t i, TensorView &fallback)
Definition tensor_types.h:722
bool resolve_column_contiguity(ColumnContiguity column_contiguity, const vector< Index > &indices)
Definition tensor_types.h:755
cudaStream_t DeviceStream
Definition opennn_types.h:135
static constexpr Index ALIGN_BYTES
Definition tensor_types.h:99
void visit_type_pair(Type t_in, Type t_out, F &&f)
Definition tensor_types.h:59
Index align_up(Index value, Index alignment)
Definition tensor_types.h:141
__nv_bfloat16 bfloat16
Definition opennn_types.h:171
ColumnContiguity
Definition tensor_types.h:747
@ Contiguous
Definition tensor_types.h:747
@ NonContiguous
Definition tensor_types.h:747
@ Unknown
Definition tensor_types.h:747
static constexpr Index ALIGN_ELEMENTS
Definition tensor_types.h:100
float type
Definition opennn_types.h:197
Eigen::TensorMap< Eigen::Tensor< float, Rank, Layout|Eigen::AlignedMax >, Eigen::AlignedMax > TensorMapR
Definition opennn_types.h:362
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Layout > MatrixR
Definition opennn_types.h:331
Eigen::Map< const VectorR, Eigen::AlignedMax > ConstVectorMap
Definition opennn_types.h:340
Eigen::Map< VectorR, Eigen::AlignedMax > VectorMap
Definition opennn_types.h:339
#define EIGEN_MAX_ALIGN_BYTES
Definition opennn_types.h:16
#define throw_if(condition,...)
Definition opennn_types.h:243
Eigen::Map< MatrixR, Eigen::AlignedMax > MatrixMap
Definition opennn_types.h:342
Buffer & operator=(Buffer &&other) noexcept
Definition tensor_types.h:349
Index byte_size() const noexcept
Definition tensor_types.h:384
void resize_bytes(Index byte_count, Device new_device)
Definition tensor_types.h:410
void swap(Buffer &other) noexcept
Definition tensor_types.h:493
bool owns_memory() const noexcept
Definition tensor_types.h:386
void * data() const noexcept
Definition tensor_types.h:383
bool empty() const noexcept
Definition tensor_types.h:388
Buffer(Device initial_device=Device::CPU)
Definition tensor_types.h:341
VectorMap as_vector() &
Definition tensor_types.h:390
Buffer(Buffer &&other) noexcept
Definition tensor_types.h:348
void setZero()
Definition tensor_types.h:469
void migrate_to(Device target_device, DeviceStream stream=nullptr)
Definition tensor_types.h:475
void set_view(void *external_data, Index byte_count, Device view_device)
Definition tensor_types.h:437
T * ensure(Index element_count)
Definition tensor_types.h:462
ConstVectorMap as_vector() const &
Definition tensor_types.h:400
Index size_in_floats() const noexcept
Definition tensor_types.h:387
Buffer & operator=(const Buffer &)=delete
void grow_to(Index minimum_bytes)
Definition tensor_types.h:453
~Buffer()
Definition tensor_types.h:367
Buffer(const Buffer &)=delete
Device get_device() const noexcept
Definition tensor_types.h:385
T * as()
Definition tensor_types.h:370
const T * as() const
Definition tensor_types.h:377
Definition device_backend.h:17
Definition tensor_types.h:172
void set_dimension(size_t index, Index value)
Definition tensor_types.h:235
const Index * begin() const noexcept
Definition tensor_types.h:212
Shape() noexcept=default
friend ostream & operator<<(ostream &os, const Shape &shape)
Definition tensor_types.h:251
Index size() const
Definition tensor_types.h:221
Index dim_or_zero(size_t i) const noexcept
Definition tensor_types.h:219
static constexpr size_t MaxRank
Definition tensor_types.h:173
size_t get_rank() const noexcept
Definition tensor_types.h:210
bool empty() const noexcept
Definition tensor_types.h:217
Shape(initializer_list< Index > list)
Definition tensor_types.h:189
Shape(It first, It last)
Definition tensor_types.h:200
const Index * end() const noexcept
Definition tensor_types.h:213
bool operator==(const Shape &other) const noexcept
Definition tensor_types.h:259
void push_back(Index value)
Definition tensor_types.h:244
void clear() noexcept
Definition tensor_types.h:233
static Shape filled(size_t rank, Index value)
Definition tensor_types.h:177
Shape & append(const Shape &other)
Definition tensor_types.h:264
Index back() const
Definition tensor_types.h:215
Index operator[](size_t i) const noexcept
Definition tensor_types.h:214
Definition tensor_types.h:281
Shape shape
Definition tensor_types.h:282
Type dtype
Definition tensor_types.h:283
bool operator==(const TensorSpec &) const noexcept=default
Definition tensor_types.h:540
MatrixMap as_matrix() const
Definition tensor_types.h:615
bool is_cuda() const noexcept
Definition tensor_types.h:572
bool empty() const noexcept
Definition tensor_types.h:571
MatrixMap as_matrix(Index matrix_index) const
Definition tensor_types.h:625
Index flat_rows() const noexcept
Definition tensor_types.h:565
void set_zero_async() const
Definition tensor_types.h:774
bool is_int8() const noexcept
Definition tensor_types.h:575
Type get_type() const noexcept
Definition tensor_types.h:552
const Shape & get_shape() const noexcept
Definition tensor_types.h:551
Device get_device() const noexcept
Definition tensor_types.h:553
MatrixMap as_flat_matrix() const
Definition tensor_types.h:647
TensorMapR< Rank > as_tensor(Index batch_index) const
Definition tensor_types.h:674
TensorView reshape_prefix(const Shape &new_shape) const
Definition tensor_types.h:606
void setZero() const
Definition tensor_types.h:693
Index get_rank() const noexcept
Definition tensor_types.h:554
VectorMap as_vector() const
Definition tensor_types.h:655
CudnnDescriptor< DnnTensorDescriptor > get_descriptor() const
float * as_float() const noexcept
Definition tensor_types.h:584
void fill(float) const
TensorView reshape(const Shape &new_shape) const
Definition tensor_types.h:597
Index byte_size() const
Definition tensor_types.h:558
TensorView(void *new_data=nullptr, const Shape &new_shape={}, Type new_type=Type::FP32, Device new_device=Device::CPU) noexcept
Definition tensor_types.h:541
T * as() const noexcept
Definition tensor_types.h:578
TensorMapR< Rank > as_tensor() const
Definition tensor_types.h:662
DeviceDataType cuda_dtype() const
Definition tensor_types.h:589
void dispatch(F &&fn) const
Definition tensor_types.h:592
void * get_data() const noexcept
Definition tensor_types.h:550
bool is_bf16() const noexcept
Definition tensor_types.h:574
bool is_fp32() const noexcept
Definition tensor_types.h:573
Index size() const
Definition tensor_types.h:556
Index flat_columns() const noexcept
Definition tensor_types.h:564
bfloat16 type
Definition tensor_types.h:27
static constexpr DnnDataType cudnn
Definition tensor_types.h:28
static constexpr Index bytes
Definition tensor_types.h:30
static constexpr DeviceDataType cuda
Definition tensor_types.h:29
static constexpr const char * name
Definition tensor_types.h:31
float type
Definition tensor_types.h:18
static constexpr Index bytes
Definition tensor_types.h:21
static constexpr const char * name
Definition tensor_types.h:22
static constexpr DeviceDataType cuda
Definition tensor_types.h:20
static constexpr DnnDataType cudnn
Definition tensor_types.h:19
static constexpr DnnDataType cudnn
Definition tensor_types.h:37
int8_t type
Definition tensor_types.h:36
static constexpr const char * name
Definition tensor_types.h:40
static constexpr Index bytes
Definition tensor_types.h:39
static constexpr DeviceDataType cuda
Definition tensor_types.h:38
Definition tensor_types.h:14