The Quantum Exact Simulation Toolkit v4.3.0
Loading...
Searching...
No Matches
types.h
1/** @file
2 * Definitions of the variable-precision API
3 * scalar types (qindex, qreal and qcomp) and
4 * convenience arithmetic operator overloads.
5 * Note both the user code and backend parse
6 * this file and may disagree on the underlying
7 * qcomp type which is no issue; we ensure the
8 * frontend-backend interface is agnostic to it.
9 *
10 * @author Tyson Jones
11 * @author Ali Rezaei (aided in design)
12 *
13 * @defgroup types Types
14 * @ingroup api
15 * @brief Macros for precision-agnostic real and complex arithmetic.
16 * @{
17 */
18
19#ifndef TYPES_H
20#define TYPES_H
21
22#include "quest/include/config.h"
23#include "quest/include/precision.h"
24
25
26
27/*
28 * REAL TYPE ALIASES
29 */
30
31typedef FLOAT_TYPE qreal;
32typedef INDEX_TYPE qindex;
33
34
35
36/*
37 * COMPLEX TYPE ALIAS
38 */
39
40// when C++ parses this header during backend or C++ user-code compilation...
41#ifdef __cplusplus
42
43 // resolve qcomp as the standard C++ complex type
44 #include <complex>
45 typedef std::complex<FLOAT_TYPE> qcomp;
46
47// when C parses this header, during compilation of C user code...
48#else
49
50 // pretend that the API's qcomp is the C complex type
51 #include <complex.h>
52
53 // which is either MSVC's custom C complex...
54 #ifdef _MSC_VER
55
56 #if (QUEST_FLOAT_PRECISION == 1)
57 typedef _Fcomplex qcomp;
58
59 #elif (QUEST_FLOAT_PRECISION == 2)
60 typedef _Dcomplex qcomp;
61
62 #elif (QUEST_FLOAT_PRECISION == 4)
63 typedef _Lcomplex qcomp;
64
65 #endif
66
67 // or that used by GNU & Clang
68 #else
69 typedef FLOAT_TYPE _Complex qcomp;
70
71 #endif
72
73#endif
74
75
76
77/*
78 * COMPLEX TYPE INSTANTIATION
79 *
80 * which is already elegantly possible in C++ using qcomp(re,im),
81 * but which cannot be extended nicely to C using a macro qcomp(),
82 * because it will break compilation of function signatures which
83 * accept funciton pointers which return qcomp. For example:
84 * myfunc( qcomp (*callback)(int,int) )
85 * So instead, we define a new, ugly 'getQcomp()' helper. Aw! :(
86 * We define it here in the header, inlining directly into user
87 * code, to avoid C & C++ qcomp interoperability issues.
88 */
89
90/// @notyetdoced
91static inline qcomp getQcomp(qreal re, qreal im) {
92
93 #if defined(__cplusplus)
94 return qcomp(re, im);
95
96 #elif defined(_MSC_VER)
97 return (qcomp) {re, im};
98
99 #else
100 return re + I*im;
101
102 #endif
103}
104
105
106
107/*
108 * COMPLEX TYPE LITERALS
109 */
110
111// MSVC C literals are literally impossible, and
112// C11 literals are already defined in complex header
113
114/// @cond EXCLUDE_FROM_DOXYGEN
115
116#ifdef __cplusplus
117
118 // enables C++14 literals like "3.5i" which are specifically double precision
119 using namespace std::complex_literals;
120
121 // the above literals are irksome for C++ users changing precision; for example, qcomp x = 3.5i
122 // will not compile in single precision, because complex<double> cannot be assigned to a
123 // complex<float>. To spare C++ users from having to change their literals when recompiling,
124 // we define variable-precision custom literals, enabling qcomp = 3.5_i. Note custom literal
125 // args always have max-precision (hence here are "long double" and "long long int")
126
127 static inline qcomp operator ""_i(long double y) {
128 return qcomp(0, static_cast<qreal>(y)); // discarding y precision
129 }
130
131 static inline qcomp operator ""_i(unsigned long long int y) {
132 return qcomp(0, static_cast<qreal>(y)); // discarding y precision
133 }
134
135#endif
136
137/// @endcond // EXCLUDE_FROM_DOXYGEN
138
139
140
141/*
142 * COMPLEX TYPE ARITHMETIC OVERLOADS
143 */
144
145// C11 arithmetic is already defined in complex header, and beautifully
146// permits mixing of parameterised types and precisions
147
148#ifdef __cplusplus
149
150 // <complex> defines overloads between complex and same-precision floats,
151 // so e.g. complex<double> + double work fine. However, for differing
152 // types and precisions, we must ourselves define operators which cast
153 // the non-complex type to a qreal (to match qcomp). We must do this for
154 // all operators the user might wish to use (e.g. + - * /) upon all
155 // natural literal precisions (typically double, e.g. "3.5"), plus all
156 // assignment forms (x += 2), and when their argument order is reversed.
157 // Furthermore, the user might do arithmetic on complex literals which are
158 // not the same precision as qcomp, so compilation will fail depending
159 // on the setting of PRECISION. To avoid this, we'll define overloads
160 // between all type/precision permutations, always returning qcomp. These
161 // overloads are also used by the QuEST source code (though incidentally,
162 // not the high performance backend which uses custom complex overloads).
163 // Via the unholy macros below, we create 312 overloads; no doubt this is
164 // going to break something in the future, for which I am already sorry :'(
165
166 /// @cond EXCLUDE_FROM_DOXYGEN
167
168 // shortcuts for below overload definitions
169 #define COMP_TO_QCOMP(a) \
170 qcomp( \
171 static_cast<qreal>(std::real(a)), \
172 static_cast<qreal>(std::imag(a)))
173
174 #define REAL_TO_QCOMP(b) \
175 qcomp(static_cast<qreal>(b), 0)
176
177 // define operator between complex<compprec> and realtype, in either order, casting to qcomp
178 #define DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_REAL(op, compprec, realtype) \
179 static inline qcomp operator op (const std::complex<compprec>& a, const realtype& b) { \
180 return \
181 COMP_TO_QCOMP(a) \
182 op \
183 REAL_TO_QCOMP(b); \
184 } \
185 static inline qcomp operator op (const realtype& b, const std::complex<compprec>& a) { \
186 return \
187 REAL_TO_QCOMP(b) \
188 op \
189 COMP_TO_QCOMP(a); \
190 } \
191 static inline std::complex<compprec>& operator op##= (std::complex<compprec>& a, const realtype& b) { \
192 a = a op b; \
193 return a; \
194 }
195
196 // defines + - * / between complex<compprec> and the given real type
197 #define DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, realtype) \
198 DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_REAL(+, compprec, realtype) \
199 DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_REAL(-, compprec, realtype) \
200 DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_REAL(*, compprec, realtype) \
201 DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_REAL(/, compprec, realtype)
202
203 // defines + - * / between complex<compprec> and all (relevant) integer types
204 #define DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER(compprec) \
205 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, int) \
206 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, long int) \
207 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, long long int) \
208 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, unsigned) \
209 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, long unsigned) \
210 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, long long unsigned)
211
212 // define arithmetic between any-precision-decimal complex and any-precision integer
213 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER(float)
214 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER(double)
215 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER(long double)
216
217 // define arithmetic between any-precision decimal complex and any-differing-precision decimal real
218 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(float, double)
219 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(float, long double)
220 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(double, long double)
221 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(double, float)
222 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(long double, float)
223 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(long double, double)
224
225 // defines arithmetic between differing-precision decimal complex, casting to qcomp, except assignment
226 #define DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_COMPLEX(op, precA, precB) \
227 static inline qcomp operator op (const std::complex<precA>& a, const std::complex<precB>& b) { \
228 return \
229 COMP_TO_QCOMP(a) \
230 op \
231 COMP_TO_QCOMP(b); \
232 }
233
234 // defines + - * / between the given differing-precision complex, casting to qcomp, except assignment
235 #define DEFINE_SINGLE_DIRECTION_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(precA, precB) \
236 DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_COMPLEX(+, precA, precB) \
237 DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_COMPLEX(-, precA, precB) \
238 DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_COMPLEX(*, precA, precB) \
239 DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_COMPLEX(/, precA, precB)
240
241 #define DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(precA, precB) \
242 DEFINE_SINGLE_DIRECTION_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(precA, precB) \
243 DEFINE_SINGLE_DIRECTION_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(precB, precA)
244
245 // define arithmetic between all differing-precision decimal complex, casting to qcomp, except assignment
246 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(float, double)
247 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(float, long double)
248 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(double, long double)
249
250 // keep these unholy macros away from the users
251 #undef COMP_TO_QCOMP
252 #undef REAL_TO_QCOMP
253 #undef DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_REAL
254 #undef DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_COMPLEX
255 #undef DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL
256 #undef DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER
257 #undef DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX
258 #undef DEFINE_SINGLE_DIRECTION_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX
259
260 /// @endcond // EXCLUDE_FROM_DOXYGEN
261
262#endif
263
264
265
266/*
267 * REPORTERS
268 */
269
270// both C and C++ declare "reportScalar" accepting
271// char array and a qcomp; but each language then
272// separately defines convenience overloads for
273// C++ strings and using C11 generics to overload
274
275#ifdef __cplusplus
276
277 #include <string>
278
279
280 /// @notyetdoced
281 /// @notyettested
282 extern "C" void reportStr(const char* str);
283
284
285 /// @notyetdoced
286 /// @notyettested
287 /// @cpponly
288 /// @see reportStr()
289 void reportStr(std::string str);
290
291
292 /// @notyetdoced
293 /// @notyettested
294 extern "C" void reportScalar(const char* label, qcomp num);
295
296
297 /// @notyetdoced
298 /// @notyettested
299 void reportScalar(const char* label, qreal num);
300
301
302 /// @notyetdoced
303 /// @notyettested
304 /// @cpponly
305 /// @see reportScalar()
306 void reportScalar(std::string label, qcomp num);
307
308
309 /// @notyetdoced
310 /// @notyettested
311 /// @cpponly
312 void reportScalar(std::string label, qreal num);
313
314#else
315
316 /// @notyetdoced
317 /// @notyettested
318 void reportStr(const char* str);
319
320
321 /// @notyetdoced
322 /// @notyettested
323 void reportScalar(const char* label, qcomp num);
324
325
326 /// @private
327 void _reportScalar_real(const char* label, qreal num);
328
329
330 // no need to be doc'd since signatures identical to C++ above
331 /// @neverdoced
332 #define reportScalar(label, num) \
333 _Generic((num), \
334 qcomp : reportScalar, \
335 qreal : _reportScalar_real, \
336 default : _reportScalar_real \
337 )(label, num)
338
339#endif
340
341
342
343#endif // TYPES_H
344
345/** @} */ // (end file-wide doxygen defgroup)
long long int INDEX_TYPE
Definition precision.h:43
double int FLOAT_TYPE
Definition precision.h:106
void reportStr(const char *str)
Definition types.cpp:33
static qcomp getQcomp(qreal re, qreal im)
Definition types.h:91
void reportScalar(const char *label, qcomp num)
Definition types.cpp:55