The Quantum Exact Simulation Toolkit v4.2.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 (FLOAT_PRECISION == 1)
57 typedef _Fcomplex qcomp;
58
59 #elif (FLOAT_PRECISION == 2)
60 typedef _Dcomplex qcomp;
61
62 #elif (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. Via the unholy macros
162 // below, we create 312 overloads; no doubt this is going to break something
163 // in the future, for which I am already sorry :'(
164
165 /// @cond EXCLUDE_FROM_DOXYGEN
166
167 // shortcuts for below overload definitions
168 #define COMP_TO_QCOMP(a) \
169 qcomp( \
170 static_cast<qreal>(std::real(a)), \
171 static_cast<qreal>(std::imag(a)))
172
173 #define REAL_TO_QCOMP(b) \
174 qcomp(static_cast<qreal>(b), 0)
175
176 // define operator between complex<compprec> and realtype, in either order, casting to qcomp
177 #define DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_REAL(op, compprec, realtype) \
178 static inline qcomp operator op (const std::complex<compprec>& a, const realtype& b) { \
179 return \
180 COMP_TO_QCOMP(a) \
181 op \
182 REAL_TO_QCOMP(b); \
183 } \
184 static inline qcomp operator op (const realtype& b, const std::complex<compprec>& a) { \
185 return \
186 REAL_TO_QCOMP(b) \
187 op \
188 COMP_TO_QCOMP(a); \
189 } \
190 static inline std::complex<compprec>& operator op##= (std::complex<compprec>& a, const realtype& b) { \
191 a = a op b; \
192 return a; \
193 }
194
195 // defines + - * / between complex<compprec> and the given real type
196 #define DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, realtype) \
197 DEFINE_OPERATOR_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
202 // defines + - * / between complex<compprec> and all (relevant) integer types
203 #define DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER(compprec) \
204 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, int) \
205 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, long int) \
206 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, long long int) \
207 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, unsigned) \
208 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, long unsigned) \
209 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(compprec, long long unsigned)
210
211 // define arithmetic between any-precision-decimal complex and any-precision integer
212 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER(float)
213 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER(double)
214 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER(long double)
215
216 // define arithmetic between any-precision decimal complex and any-differing-precision decimal real
217 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(float, double)
218 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(float, long double)
219 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(double, long double)
220 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(double, float)
221 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(long double, float)
222 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL(long double, double)
223
224 // defines arithmetic between differing-precision decimal complex, casting to qcomp, except assignment
225 #define DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_COMPLEX(op, precA, precB) \
226 static inline qcomp operator op (const std::complex<precA>& a, const std::complex<precB>& b) { \
227 return \
228 COMP_TO_QCOMP(a) \
229 op \
230 COMP_TO_QCOMP(b); \
231 }
232
233 // defines + - * / between the given differing-precision complex, casting to qcomp, except assignment
234 #define DEFINE_SINGLE_DIRECTION_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(precA, precB) \
235 DEFINE_OPERATOR_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
240 #define DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(precA, precB) \
241 DEFINE_SINGLE_DIRECTION_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(precA, precB) \
242 DEFINE_SINGLE_DIRECTION_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(precB, precA)
243
244 // define arithmetic between all differing-precision decimal complex, casting to qcomp, except assignment
245 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(float, double)
246 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(float, long double)
247 DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX(double, long double)
248
249 // keep these unholy macros away from the users
250 #undef COMP_TO_QCOMP
251 #undef REAL_TO_QCOMP
252 #undef DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_REAL
253 #undef DEFINE_OPERATOR_BETWEEN_COMPLEX_AND_COMPLEX
254 #undef DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_REAL
255 #undef DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_INTEGER
256 #undef DEFINE_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX
257 #undef DEFINE_SINGLE_DIRECTION_ARITHMETIC_BETWEEN_COMPLEX_AND_COMPLEX
258
259 /// @endcond // EXCLUDE_FROM_DOXYGEN
260
261#endif
262
263
264
265/*
266 * REPORTERS
267 */
268
269// both C and C++ declare "reportScalar" accepting
270// char array and a qcomp; but each language then
271// separately defines convenience overloads for
272// C++ strings and using C11 generics to overload
273
274#ifdef __cplusplus
275
276 #include <string>
277
278
279 /// @notyetdoced
280 /// @notyettested
281 extern "C" void reportStr(const char* str);
282
283
284 /// @notyetdoced
285 /// @notyettested
286 /// @cpponly
287 /// @see reportStr()
288 void reportStr(std::string str);
289
290
291 /// @notyetdoced
292 /// @notyettested
293 extern "C" void reportScalar(const char* label, qcomp num);
294
295
296 /// @notyetdoced
297 /// @notyettested
298 void reportScalar(const char* label, qreal num);
299
300
301 /// @notyetdoced
302 /// @notyettested
303 /// @cpponly
304 /// @see reportScalar()
305 void reportScalar(std::string label, qcomp num);
306
307
308 /// @notyetdoced
309 /// @notyettested
310 /// @cpponly
311 void reportScalar(std::string label, qreal num);
312
313#else
314
315 /// @notyetdoced
316 /// @notyettested
317 void reportStr(const char* str);
318
319
320 /// @notyetdoced
321 /// @notyettested
322 void reportScalar(const char* label, qcomp num);
323
324
325 /// @private
326 void _reportScalar_real(const char* label, qreal num);
327
328
329 // no need to be doc'd since signatures identical to C++ above
330 /// @neverdoced
331 #define reportScalar(label, num) \
332 _Generic((num), \
333 qcomp : reportScalar, \
334 qreal : _reportScalar_real, \
335 default : _reportScalar_real \
336 )(label, num)
337
338#endif
339
340
341
342#endif // TYPES_H
343
344/** @} */ // (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:29
static qcomp getQcomp(qreal re, qreal im)
Definition types.h:91
void reportScalar(const char *label, qcomp num)
Definition types.cpp:51