Cytnx v1.0.0
Loading...
Searching...
No Matches
Type.hpp
Go to the documentation of this file.
1#ifndef CYTNX_TYPE_H_
2#define CYTNX_TYPE_H_
3
4#include <complex>
5#include <cstdint>
6#include <string>
7#include <type_traits>
8#include <tuple>
9#include <array>
10#include <utility>
11#include <vector>
12#include <variant>
13
14#include "cytnx_error.hpp" // also brings in cuComplex.h
15
16#ifdef UNI_GPU
17 #include <cuda/std/complex>
18#endif
19
20#define MKL_Complex8 std::complex<float>
21#define MKL_Complex16 std::complex<double>
22
23#ifdef BACKEND_TORCH
24typedef int32_t blas_int;
25#else
26
27 #ifdef UNI_MKL
28 #include <mkl.h>
29typedef MKL_INT blas_int;
30 #else
31typedef int32_t blas_int;
32 #endif
33
34#endif
35
36// @cond
37namespace cytnx {
38
39 template <class T>
40 using vec3d = std::vector<std::vector<std::vector<T>>>;
41
42 template <class T>
43 using vec2d = std::vector<std::vector<T>>;
44
45 typedef double cytnx_double;
46 typedef float cytnx_float;
47 typedef uint64_t cytnx_uint64;
48 typedef uint32_t cytnx_uint32;
49 typedef uint16_t cytnx_uint16;
50 typedef int64_t cytnx_int64;
51 typedef int32_t cytnx_int32;
52 typedef int16_t cytnx_int16;
53 typedef std::size_t cytnx_size_t;
54 typedef std::complex<float> cytnx_complex64;
55 typedef std::complex<double> cytnx_complex128;
56 typedef bool cytnx_bool;
57
58#ifdef UNI_GPU
59 using cytnx_cuda_complex64 = cuda::std::complex<float>;
60 using cytnx_cuda_complex128 = cuda::std::complex<double>;
61#endif
62
63 namespace internal {
64 template <class>
65 struct is_complex_impl : std::false_type {};
66
67 template <class T>
68 struct is_complex_impl<std::complex<T>> : std::true_type {};
69
70#ifdef UNI_GPU
71 template <class T>
72 struct is_complex_impl<cuda::std::complex<T>> : std::true_type {};
73#endif
74
75 template <typename>
76 struct is_complex_floating_point_impl : std::false_type {};
77
78 template <typename T>
79 struct is_complex_floating_point_impl<std::complex<T>> : std::is_floating_point<T> {};
80
81#ifdef UNI_GPU
82 template <typename T>
83 struct is_complex_floating_point_impl<cuda::std::complex<T>> : std::is_floating_point<T> {};
84#endif
85
86 template <std::size_t Idx, typename T, typename Tuple>
87 constexpr std::size_t index_in_tuple_helper() {
88 static_assert(Idx < std::tuple_size_v<Tuple>, "Type not found!");
89 if constexpr (std::is_same_v<T, std::tuple_element_t<Idx, Tuple>>) {
90 return Idx;
91 } else {
92 return index_in_tuple_helper<Idx + 1, T, Tuple>();
93 }
94 }
95
96 } // namespace internal
97
98 // helper metafunction to transform a variant into another variant via a
99 // transform template alias
100 template <typename V, template <typename> class Transform>
101 struct make_variant_from_transform;
102
103 template <template <typename> class Transform, typename... Args>
104 struct make_variant_from_transform<std::variant<Args...>, Transform> {
105 using type = std::variant<typename Transform<Args>::type...>;
106 };
107
108 // helper type alias for make_variant_from_transform
109 template <typename V, template <typename> class Transform>
110 using make_variant_from_transform_t = typename make_variant_from_transform<V, Transform>::type;
111
112 template <typename T>
113 using is_complex = internal::is_complex_impl<std::remove_cv_t<T>>;
114
115 template <typename T>
116 using is_complex_floating_point = internal::is_complex_floating_point_impl<std::remove_cv_t<T>>;
117
118 // is_complex_v checks if a data type is of type std::complex
119 // usage: is_complex_v<T> returns true or false for a data type T
120 template <typename T>
121 constexpr bool is_complex_v = is_complex<T>::value;
122
123 // is_complex_floating_point_v<T> is a template constant that is true if T is of type
124 // std::complex<U> where U is a floating point type, and false otherwise.
125 template <typename T>
126 constexpr bool is_complex_floating_point_v = is_complex_floating_point<T>::value;
127
128 template <typename>
129 inline constexpr bool always_false_v = false;
130
131 template <typename T, typename Variant>
132 inline constexpr bool variant_contains_v = false;
133
134 template <typename T, typename... Types>
135 inline constexpr bool variant_contains_v<T, std::variant<Types...>> = (std::is_same_v<T, Types> ||
136 ...);
137
138 // variant_index<T, Variant> returns the index of type T in the Variant.
139 template <typename T, typename Variant>
140 struct variant_index;
141
142 template <typename T>
143 struct variant_index<T, std::variant<>> {
144 static_assert(always_false_v<T>, "variant_index<T, Variant>: T is not in Variant");
145 static constexpr std::size_t value = 0;
146 };
147
148 template <typename T, typename... Types>
149 struct variant_index<T, std::variant<T, Types...>> {
150 static constexpr std::size_t value = 0;
151 };
152
153 template <typename T, typename U, typename... Types>
154 struct variant_index<T, std::variant<U, Types...>> {
155 static constexpr std::size_t value = 1 + variant_index<T, std::variant<Types...>>::value;
156 };
157
158 // helper template variable
159 template <typename T, typename Variant>
160 static constexpr std::size_t variant_index_v = variant_index<T, Variant>::value;
161
162 namespace internal {
163 // type_size returns the sizeof(T) for the supported types. This is the same as
164 // sizeof(T), except that size_type<void> is 0.
165 template <typename T>
166 inline constexpr int type_size = sizeof(T);
167 template <>
168 inline constexpr int type_size<void> = 0;
169 } // namespace internal
170
171 // the list of supported types. The dtype() of an object is an index into this list.
172 // std::variant works better than std::tuple here since a variant is constrained to only
173 // hold each type once, and we have std::variant_alternative_t<n> to get the n'th type,
174 // as well as the variant_index_v helper to get the index of a given type
175 using Type_list =
176 std::variant<void, cytnx_complex128, cytnx_complex64, cytnx_double, cytnx_float, cytnx_int64,
177 cytnx_uint64, cytnx_int32, cytnx_uint32, cytnx_int16, cytnx_uint16, cytnx_bool>;
178
179 // For GPU kernels, use cuda::std::complex for complex arithmetic. Low-level CUDA library calls
180 // that require cuComplex ABI pointers should cast explicitly at those call boundaries.
181#ifdef UNI_GPU
182 using Type_list_gpu =
183 std::variant<void, cytnx_cuda_complex128, cytnx_cuda_complex64, cytnx_double, cytnx_float,
184 cytnx_int64, cytnx_uint64, cytnx_int32, cytnx_uint32, cytnx_int16, cytnx_uint16,
185 cytnx_bool>;
186#endif
187
188 // CytnxType<T> is satisfied by the element types that have a cytnx dtype (the members of
189 // Type_list, excluding the Void placeholder). Storage_base::data/at/back are constrained to it
190 // so that requesting an unsupported T is a compile-time error at the call site. The GPU cuComplex
191 // / cuda::std::complex pointer views are non-cytnx-dtype types and are provided separately as
192 // explicit specializations.
193 template <typename T>
194 concept CytnxType = variant_contains_v<T, Type_list> && !std::is_void_v<T>;
195
196#ifdef UNI_GPU
197 // The GPU complex pointer-view types that Storage_base::data<T>() specializes for. They are not
198 // cytnx dtypes: cuDoubleComplex/cuFloatComplex are the cuComplex ABI types for CUDA library
199 // calls, and cuda::std::complex<...> is the representation GPU kernels use internally.
200 template <typename T>
201 concept GpuComplexView =
202 std::is_same_v<T, cuDoubleComplex> || std::is_same_v<T, cuFloatComplex> ||
203 std::is_same_v<T, cytnx_cuda_complex128> || std::is_same_v<T, cytnx_cuda_complex64>;
204
205 // The element types data<T>() accepts: cytnx dtypes plus the GPU complex pointer views.
206 template <typename T>
207 concept StorageDataType = CytnxType<T> || GpuComplexView<T>;
208#else
209 template <typename T>
210 concept StorageDataType = CytnxType<T>;
211#endif
212
213 // The number of supported types
214 constexpr int N_Type = std::variant_size_v<Type_list>;
215 constexpr int N_fType = 5;
216
217 // The friendly name of each type
218 template <typename T>
219 inline constexpr char* Type_names = nullptr;
220 template <>
221 inline constexpr const char* Type_names<void> = "Void";
222 template <>
223 inline constexpr const char* Type_names<cytnx_complex128> = "Complex Double (Complex Float64)";
224 template <>
225 inline constexpr const char* Type_names<cytnx_complex64> = "Complex Float (Complex Float32)";
226 template <>
227 inline constexpr const char* Type_names<cytnx_double> = "Double (Float64)";
228 template <>
229 inline constexpr const char* Type_names<cytnx_float> = "Float (Float32)";
230 template <>
231 inline constexpr const char* Type_names<cytnx_int64> = "Int64";
232 template <>
233 inline constexpr const char* Type_names<cytnx_uint64> = "Uint64";
234 template <>
235 inline constexpr const char* Type_names<cytnx_int32> = "Int32";
236 template <>
237 inline constexpr const char* Type_names<cytnx_uint32> = "Uint32";
238 template <>
239 inline constexpr const char* Type_names<cytnx_int16> = "Int16";
240 template <>
241 inline constexpr const char* Type_names<cytnx_uint16> = "Uint16";
242 template <>
243 inline constexpr const char* Type_names<cytnx_bool> = "Bool";
244
245 // The corresponding Python enumeration name
246 template <typename T>
247 inline constexpr char* Type_enum_name = nullptr;
248 template <>
249 inline constexpr const char* Type_enum_name<void> = "Void";
250 // std::monostate is Void's stand-in inside value variants (e.g.
251 // Scalar::ScalarVariant, where a real alternative is needed at index 0);
252 // give it the same display name so visit-based error messages can name it.
253 template <>
254 inline constexpr const char* Type_enum_name<std::monostate> = "Void";
255 template <>
256 inline constexpr const char* Type_enum_name<cytnx_complex128> = "ComplexDouble";
257 template <>
258 inline constexpr const char* Type_enum_name<cytnx_complex64> = "ComplexFloat";
259 template <>
260 inline constexpr const char* Type_enum_name<cytnx_double> = "Double";
261 template <>
262 inline constexpr const char* Type_enum_name<cytnx_float> = "Float";
263 template <>
264 inline constexpr const char* Type_enum_name<cytnx_int64> = "Int64";
265 template <>
266 inline constexpr const char* Type_enum_name<cytnx_uint64> = "Uint64";
267 template <>
268 inline constexpr const char* Type_enum_name<cytnx_int32> = "Int32";
269 template <>
270 inline constexpr const char* Type_enum_name<cytnx_uint32> = "Uint32";
271 template <>
272 inline constexpr const char* Type_enum_name<cytnx_int16> = "Int16";
273 template <>
274 inline constexpr const char* Type_enum_name<cytnx_uint16> = "Uint16";
275 template <>
276 inline constexpr const char* Type_enum_name<cytnx_bool> = "Bool";
277
278 struct Type_struct {
279 const char* name; // char* is OK here, it is only ever initialized from a string literal
280 const char* enum_name;
281 bool is_unsigned;
282 bool is_complex;
283 bool is_float;
284 bool is_int;
285 unsigned int typeSize;
286 };
287
288 template <typename T>
289 struct Type_struct_t {
290 static constexpr unsigned int cy_typeid = variant_index_v<T, Type_list>;
291#ifdef UNI_GPU
292 static constexpr unsigned int cy_typeid_gpu = variant_index_v<T, Type_list_gpu>;
293#endif
294 static constexpr const char* name = Type_names<T>;
295 static constexpr const char* enum_name = Type_enum_name<T>;
296 static constexpr bool is_complex = is_complex_v<T>;
297 static constexpr bool is_unsigned = std::is_unsigned_v<T>;
298 static constexpr bool is_float = std::is_floating_point_v<T> || is_complex_floating_point_v<T>;
299 static constexpr bool is_int = std::is_integral_v<T> && !std::is_same_v<T, bool>;
300 static constexpr std::size_t typeSize = internal::type_size<T>;
301
302 static constexpr Type_struct construct() {
303 return {name, enum_name, is_unsigned, is_complex, is_float, is_int, typeSize};
304 }
305 };
306
307 namespace internal {
308 template <typename Variant, std::size_t... Indices>
309 constexpr auto make_type_array_helper(std::index_sequence<Indices...>) {
310 return std::array<Type_struct, sizeof...(Indices)>{
311 Type_struct_t<std::variant_alternative_t<Indices, Variant>>::construct()...};
312 }
313 template <typename Variant>
314 constexpr auto make_type_array() {
315 return make_type_array_helper<Variant>(
316 std::make_index_sequence<std::variant_size_v<Variant>>());
317 }
318 } // namespace internal
319
320 class Type_class {
321 private:
322 public:
323 // Typeinfos is a std::array<Type_struct> for each type in Type_list
324 static constexpr auto Typeinfos = internal::make_type_array<Type_list>();
325
326 template <typename T>
327 static constexpr unsigned int cy_typeid_v = variant_index_v<T, Type_list>;
328
329#ifdef UNI_GPU
330 template <typename T>
331 static constexpr unsigned int cy_typeid_gpu_v = variant_index_v<T, Type_list_gpu>;
332#endif
333
334 enum Type : unsigned int {
335 Void = cy_typeid_v<void>,
336 ComplexDouble = cy_typeid_v<cytnx_complex128>,
337 ComplexFloat = cy_typeid_v<cytnx_complex64>,
338 Double = cy_typeid_v<cytnx_double>,
339 Float = cy_typeid_v<cytnx_float>,
340 Int64 = cy_typeid_v<cytnx_int64>,
341 Uint64 = cy_typeid_v<cytnx_uint64>,
342 Int32 = cy_typeid_v<cytnx_int32>,
343 Uint32 = cy_typeid_v<cytnx_uint32>,
344 Int16 = cy_typeid_v<cytnx_int16>,
345 Uint16 = cy_typeid_v<cytnx_uint16>,
346 Bool = cy_typeid_v<cytnx_bool>
347 };
348
349 static constexpr void check_type(unsigned int type_id) {
350 cytnx_error_msg(type_id >= N_Type, "[ERROR] invalid type_id: %s", type_id);
351 }
352
353 // This could be constexpr returning constexpr char*, but there is lots of code that
354 // assumes that it returns a std::string and calls getname(n).c_str()
355 static std::string getname(unsigned int type_id) {
356 check_type(type_id);
357 return Typeinfos[type_id].name;
358 }
359 // This cannot be constexpr as we define it in a .cpp file,
360 // and typeid(T).name() is not constexpr
361 static unsigned int c_typename_to_id(const std::string& c_name);
362 static char const* enum_name(unsigned int type_id) {
363 check_type(type_id);
364 return Typeinfos[type_id].enum_name;
365 }
366 static constexpr unsigned int typeSize(unsigned int type_id) {
367 check_type(type_id);
368 return Typeinfos[type_id].typeSize;
369 }
370 static constexpr bool is_unsigned(unsigned int type_id) {
371 check_type(type_id);
372 return Typeinfos[type_id].is_unsigned;
373 }
374 static constexpr bool is_complex(unsigned int type_id) {
375 check_type(type_id);
376 return Typeinfos[type_id].is_complex;
377 }
378 static constexpr bool is_float(unsigned int type_id) {
379 check_type(type_id);
380 return Typeinfos[type_id].is_float;
381 }
382 static constexpr bool is_int(unsigned int type_id) {
383 check_type(type_id);
384 return Typeinfos[type_id].is_int;
385 }
386
387 template <class T>
388 static constexpr unsigned int cy_typeid(const T& rc) {
389 return cy_typeid_v<T>;
390 }
391
392 // Real counterpart of a dtype: ComplexDouble -> Double, ComplexFloat -> Float,
393 // anything else unchanged. Replaces the "dtype <= 2 ? dtype + 2 : dtype" idiom
394 // (without depending on the enum layout).
395 static constexpr unsigned int to_real(unsigned int type_id) {
396 check_type(type_id);
397 if (type_id == ComplexDouble) return Double;
398 if (type_id == ComplexFloat) return Float;
399 return type_id;
400 }
401
402 // Complex counterpart of a dtype: Double -> ComplexDouble, Float -> ComplexFloat,
403 // complex types unchanged, Void unchanged, integral/bool -> ComplexDouble.
404 static constexpr unsigned int to_complex(unsigned int type_id) {
405 check_type(type_id);
406 if (is_complex(type_id)) return type_id;
407 if (type_id == Double) return ComplexDouble;
408 if (type_id == Float) return ComplexFloat;
409 if (type_id == Void) return Void;
410 return ComplexDouble;
411 }
412
413 // The dtype linalg::Norm() produces for an input of the given dtype: the
414 // real counterpart for floating/complex dtypes, Double for integer/bool
415 // inputs (which Norm computes in double precision). This is the single
416 // home of that policy -- src/linalg/Norm.cpp and callers pre-sizing
417 // norm accumulators (e.g. UniTensor::normalize_) both use it, so they
418 // cannot drift apart.
419 static constexpr unsigned int norm_result_dtype(unsigned int type_id) {
420 check_type(type_id);
421 // Void has no norm: reject it here rather than silently mapping it to
422 // Double (which would let an uninitialized tensor flow onward).
423 cytnx_error_msg(type_id == Void,
424 "[ERROR] norm_result_dtype: Void has no norm result dtype.%s", "\n");
425 if (is_float(type_id)) return to_real(type_id);
426 return Double;
427 }
428
429 // Find a common type for typeL and typeR
430 static constexpr unsigned int type_promote(unsigned int typeL, unsigned int typeR) {
431 if (typeL == Void || typeR == Void) return Void;
432 // Mixed complex/real: promote the real counterparts, then re-complexify.
433 // Fixes ComplexFloat + Double -> ComplexDouble (previously ComplexFloat,
434 // discarding precision, because the enum interleaves complexness and
435 // precision and promotion picked the lower index).
436 if (is_complex(typeL) != is_complex(typeR)) {
437 return to_complex(type_promote(to_real(typeL), to_real(typeR)));
438 }
439 if (typeL < typeR) {
440 if (!is_unsigned(typeR) && is_unsigned(typeL)) {
441 return typeL - 1;
442 } else {
443 return typeL;
444 }
445 } else {
446 if (!is_unsigned(typeL) && is_unsigned(typeR)) {
447 return typeR - 1;
448 } else {
449 return typeR;
450 }
451 }
452 }
453
454 // type metafunction for type promotion
455 template <typename TL, typename TR>
456 using type_promote_t =
457 std::variant_alternative_t<Type_class::type_promote(variant_index_v<TL, Type_list>,
458 variant_index_v<TR, Type_list>),
459 Type_list>;
460
461 // Runtime counterpart of make_floating_point_t, for call sites that only
462 // have a dtype id (not a C++ type) at the point an operation's output
463 // Tensor/Storage is pre-sized, e.g. Div's out-of-place output allocation
464 // before the typed visitor runs. Named distinctly from the
465 // make_floating_point<T> type-trait below (a function and a class
466 // template cannot share a name in the same scope).
467 static constexpr unsigned int make_floating_point_dtype(unsigned int type_id) {
468 check_type(type_id);
469 if (is_float(type_id)) return type_id;
470 return Double;
471 }
472
473 // The true-division output type for a promoted dtype: integral/bool dtypes
474 // become cytnx_double (Python true-division semantics), existing floating
475 // dtypes and complex dtypes are unchanged. Used by Div's output-type rule
476 // (make_floating_point_t<type_promote_t<TL,TR>>) so int/int division
477 // produces a floating result instead of truncating (#941).
478 template <typename T>
479 struct make_floating_point {
480 using type =
481 std::conditional_t<std::is_floating_point_v<T> || is_complex_v<T>, T, cytnx_double>;
482 };
483
484 template <typename T>
485 using make_floating_point_t = typename make_floating_point<T>::type;
486
487 // Helper to promote two pointer types (note does _not_ return another pointer type)
488 template <typename TL, typename TR>
489 struct type_promote_from_pointer {
490 using type = void;
491 };
492
493 template <typename TL, typename TR>
494 struct type_promote_from_pointer<TL*, TR*> {
495 using type = type_promote_t<std::decay_t<TL>, std::decay_t<TR>>;
496 };
497
498 // helper typedef
499 template <typename TL, typename TR>
500 using type_promote_from_pointer_t = typename type_promote_from_pointer<TL, TR>::type;
501
502#ifdef UNI_GPU
503 // .. and we need a version where TL and TR are GPU device pointers
504 template <typename TL, typename TR>
505 using type_promote_gpu_t =
506 std::variant_alternative_t<Type_class::type_promote(variant_index_v<TL, Type_list_gpu>,
507 variant_index_v<TR, Type_list_gpu>),
508 Type_list_gpu>;
509
510 template <typename TL, typename TR>
511 struct type_promote_from_gpu_pointer {
512 using type = void;
513 };
514
515 template <typename TL, typename TR>
516 struct type_promote_from_gpu_pointer<TL*, TR*> {
517 using type = type_promote_gpu_t<std::decay_t<TL>, std::decay_t<TR>>;
518 };
519
520 // helper typedef
521 template <typename TL, typename TR>
522 using type_promote_from_gpu_pointer_t = typename type_promote_from_gpu_pointer<TL, TR>::type;
523#endif
524
525 }; // Type_class
527
555 constexpr Type_class Type;
556
557 extern int __blasINTsize__;
558
559 // User_debug is declared in cytnx_error.hpp (included above); no need to redeclare it here.
560
561} // namespace cytnx
562
563#endif // CYTNX_TYPE_H_
int __blasINTsize__
int32_t blas_int
Definition Type.hpp:31
constexpr Type_class Type
data type
Definition Type.hpp:555
#define cytnx_error_msg(is_true, format,...)
Definition cytnx_error.hpp:115
Definition Accessor.hpp:12
@ U
Definition Symmetry.hpp:30