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