The Quantum Exact Simulation Toolkit v4.0.0
Loading...
Searching...
No Matches
matrices.h
1/** @file
2 * Definitions of all dense and diagonal matrices, their getters and setters,
3 * as well as their reporting utilities. Note that Kraus maps are treated in
4 * a bespoke file (channels.h).
5 *
6 * This file uses extensive preprocessor trickery to achieve overloaded,
7 * platform agnostic, C and C++ compatible, precision agnostic, getters
8 * and setters of complex matrices. All macros herein expand to single-line
9 * definitions, for safety. Some intendedly private functions are necessarily
10 * exposed here to the user, and are prefixed with an underscore.
11 *
12 * @author Tyson Jones
13 * @author Richard Meister (aided in design)
14 * @author Erich Essmann (aided in design, patched on MSVC)
15 *
16 * @defgroup matrices Matrices
17 * @ingroup api
18 * @brief Data structures for representing operator matrices.
19 * @{
20 */
21
22#ifndef MATRICES_H
23#define MATRICES_H
24
25#include "quest/include/types.h"
26#include "quest/include/paulis.h"
27
28// C++ gets vector initialiser overloads, whereas C gets a macro
29#ifdef __cplusplus
30 #include <vector>
31#endif
32
33
34
35/*
36 * unlike some other headers, we here intermix the C and C++-only
37 * signatures, grouping them semantically & by their doc groups
38 */
39
40
41
42/**
43 * @defgroup matrices_structs Structs
44 * @brief Data structures for representing operator matrices.
45 * @{
46 */
47
48
49/*
50 * DENSE MATRIX STRUCTS
51 *
52 * which are visible to both C and C++, where qcomp resolves
53 * to the native complex type. These are not de-mangled because
54 * C++ structs are already C compatible.
55 *
56 * The compile-time sized structs have field 'elems', while
57 * dynamic-sized structs have separate 'cpuElems' and
58 * 'gpuElemsFlat', for persistent GPU allocation, and ergo need
59 * syncing. Note 'gpuElemsFlat' is always 1D (hence the name).
60 */
61
62
63/// @notdoced
64typedef struct {
65
66 int numQubits;
67 qindex numRows;
68
69 qcomp elems[2][2];
70
71} CompMatr1;
72
73
74/// @notdoced
75typedef struct {
76
77 int numQubits;
78 qindex numRows;
79
80 qcomp elems[4][4];
81
82} CompMatr2;
83
84
85/// @notdoced
86typedef struct {
87
88 // beware that CompMatr instances are sometimes 'spoofed' inside localiser.cpp,
89 // which will set the fields from other object instances (like a SuperOp). As
90 // such, additional fields to this struct may require updating these spoofers.
91
92 int numQubits;
93 qindex numRows;
94
95 // properties of the matrix (0, 1, or -1 to indicate unknown) which are lazily evaluated,
96 // deferred until a function actually validates them, at which point they are computed
97 // and the flags fixed until the user modifies the matrix (through sync() or setAmps() etc).
98 // flag is stored in heap so even copies of structs are mutable, but pointer is immutable.
99 // otherwise, the field of a user's struct could never be modified because of pass-by-copy.
100 int* isApproxUnitary;
101 int* isApproxHermitian; /// @todo currently unused (relevant to not-yet-implemented calc-expec-val)
102
103 // whether the user has ever synchronised memory to the GPU, which is performed automatically
104 // when calling functions like setCompMatr(), but which requires manual invocation with
105 // syncCompMatr() after manual modification of the cpuElem. Note this can only indicate whether
106 // the matrix has EVER been synced; it cannot be used to detect whether manual modifications
107 // made after an initial sync have been re-synched. This is a heap pointer, as above.
109
110 // 2D CPU memory, which users can manually overwrite like cpuElems[i][j],
111 // but which actually merely aliases the 1D cpuElemsFlat below
112 qcomp** cpuElems;
113
114 // row-major flattened elements of cpuElems, always allocated
115 qcomp* cpuElemsFlat;
116
117 // row-major flattened elems in GPU memory, allocated
118 // only and always in GPU-enabled QuEST environments
119 qcomp* gpuElemsFlat;
120
121} CompMatr;
122
123
124/*
125 * DIAGONAL MATRIX STRUCTS
126 *
127 * with all the same nuances as the CompMatr structs described above.
128 */
129
130
131/// @notdoced
132typedef struct {
133
134 int numQubits;
135 qindex numElems;
136
137 qcomp elems[2];
138
139} DiagMatr1;
140
141
142/// @notdoced
143typedef struct {
144
145 int numQubits;
146 qindex numElems;
147
148 qcomp elems[4];
149
150} DiagMatr2;
151
152
153/// @notdoced
154typedef struct {
155
156 int numQubits;
157 qindex numElems;
158
159 // properties of the matrix (0, 1, or -1 to indicate unknown) which are lazily evaluated,
160 // deferred until a function actually validates them, at which point they are computed
161 // and the flags fixed until the user modifies the matrix (through sync() or setAmps() etc).
162 // flag is stored in heap so even copies of structs are mutable, but pointer is immutable.
163 // otherwise, the field of a user's struct could never be modified because of pass-by-copy.
164 int* isApproxUnitary;
165 int* isApproxHermitian; /// @todo currently unused (relevant to not-yet-implemented calc-expec-val)
166 int* isApproxNonZero; /// @todo currently unused (relevant to not-yet-implemented calc-expec-val)
167 int* isStrictlyNonNegative; /// @todo currently unused (relevant to not-yet-implemented calc-expec-val)
168
169 // whether the user has ever synchronised memory to the GPU, which is performed automatically
170 // when calling functions like setCompMatr(), but which requires manual invocation with
171 // syncCompMatr() after manual modification of the cpuElem. Note this can only indicate whether
172 // the matrix has EVER been synced; it cannot be used to detect whether manual modifications
173 // made after an initial sync have been re-synched. This is a heap pointer, as above.
175
176 // CPU memory; not const, so users can overwrite addresses (e.g. with nullptr)
177 qcomp* cpuElems;
178
179 // GPU memory, allocated only and always in GPU-enabled QuEST environments
180 qcomp* gpuElems;
181
182} DiagMatr;
183
184
185/*
186 * DISTRIBUTED MATRIX STRUCTS
187 */
188
189
190/// @notdoced
191typedef struct {
192
193 int numQubits;
194 qindex numElems;
195
196 // unlike other heap-matrices, GPU memory is not always allocated when the QuEST
197 // env is GPU-accelerated; instead, it can be disabled by auto-deployer, or the user
198 int isGpuAccelerated;
199 int isMultithreaded;
200 int isDistributed;
201 qindex numElemsPerNode;
202
203 // properties of the matrix (0, 1, or -1 to indicate unknown) which are lazily evaluated,
204 // deferred until a function actually validates them, at which point they are computed
205 // and the flags fixed until the user modifies the matrix (through sync() or setAmps() etc).
206 // flag is stored in heap so even copies of structs are mutable, but pointer is immutable.
207 // otherwise, the field of a user's struct could never be modified because of pass-by-copy.
208 int* isApproxUnitary;
209 int* isApproxHermitian;
210 int* isApproxNonZero;
211 int* isStrictlyNonNegative;
212
213 // whether the user has ever synchronised memory to the GPU, which is performed automatically
214 // when calling functions like setCompMatr(), but which requires manual invocation with
215 // syncCompMatr() after manual modification of the cpuElem. Note this can only indicate whether
216 // the matrix has EVER been synced; it cannot be used to detect whether manual modifications
217 // made after an initial sync have been re-synched. This is a heap pointer, as above.
218 int* wasGpuSynced;
219
220 // CPU memory; not const, so users can overwrite addresses (e.g. with nullptr)
221 qcomp* cpuElems;
222
223 // GPU memory, allocated only and always in GPU-enabled QuEST environments
224 qcomp* gpuElems;
225
227
228
229/** @} */
230
231
232
233// we define the remaining doc groups in advance, since their signatures are
234// more naturally grouped in an implementation-specific way below. Note the
235// above structs were not doc'd this way (which would be more consistent)
236// because it inexplicably causes Doxygen to duplicate their section at the
237// top-level under Matrices (rather than under Structs). Bizarre! The order
238// of declaration below will match the order shown in the html doc.
239/**
240 * @defgroup matrices_getters Getters
241 * @brief Functions for obtaining fixed-size matrices.
242 *
243 * @defgroup matrices_create Constructors
244 * @brief Functions for creating variable-size matrices.
245 *
246 * @defgroup matrices_destroy Destructors
247 * @brief Functions for destroying existing matrices.
248 *
249 * @defgroup matrices_reporters Reporters
250 * @brief Functions for printing matrices.
251 *
252 * @defgroup matrices_setters Setters
253 * @brief Functions for overwriting the elements of matrices.
254 *
255 * @defgroup matrices_sync Synchronisation
256 * @brief Functions for overwriting a matrix's GPU (VRAM) memory with its CPU (RAM) contents.
257 * @details These functions are only necessary when the user wishes to manually modify the
258 * elements of a matrix (in lieu of using the @ref matrices_setters "Setters"), to
259 * thereafter synchronise the changes to the GPU copy of the channel. These functions
260 * have no effect when running without GPU-acceleration, but remain legal and harmless
261 * to call (to achieve platform agnosticism).
262 */
263
264
265
266/*
267 * FIZED-SIZE MATRIX GETTERS VIA POINTERS
268 *
269 * which are defined here in the header because the 'qcomp' type is interpreted
270 * distinctly by C++ (the backend) and C (user code). The C and C++ ABIs do not
271 * agree on a complex type, so a qcomp (to C; a _Complex, and to C++; a std::complex)
272 * cannot be directly passed between C and C++ compiled binaries; nor can a CompMatr1
273 * struct which unwraps the qcomp[][] array. However, the C and C++ complex types have
274 * identical memory layouts, so pointers to qcomp types can safely be passed between
275 * C and C++ binaries. Ordinarily we leverage this by defining all qcomp-handling API
276 * functions in C++, and defining additional C-only wrappers in wrappers.h, which pass
277 * only pointers to qcomp. Alas, we cannot use this trick here, because the CompMatr1/2
278 * fields are declared 'const'; we cannot modify them through pointers, nor should we
279 * try to address them. Ergo we directly define these functions below (static inline to
280 * avoid symbol duplication), initializing the struct in one line. These functions will be
281 * separately interpreted by the C and C++ compilers, resolving qcomp to their individual
282 * native complex types.
283 *
284 * These functions permit users to pass heap and stack pointers:
285 * - qcomp** ptr = malloc(...); getCompMatr1(ptr);
286 * - qcomp* ptrs[2]; getCompMatr1(ptrs);
287 * in both C and C++. Because of 1D pointer decay, they also permit:
288 * - qcomp* ptr = malloc(...); getDiagMatr1(ptr);
289 * - qcomp arr[2]; getDiagMatr1(arr);
290 */
291
292
293// private validators
294#ifdef __cplusplus
295extern "C" {
296#endif
297
298/// @private
299extern void _validateNewNestedElemsPtrNotNull(qcomp** ptrs, int numQubits, const char* caller);
300
301/// @private
302extern void _validateNewElemsPtrNotNull(qcomp* ptr, const char* caller);
303
304#ifdef __cplusplus
305}
306#endif
307
308
309/// @ingroup matrices_getters
310/// @notdoced
311static inline CompMatr1 getCompMatr1(qcomp** in) {
312 _validateNewNestedElemsPtrNotNull(in, 1, __func__);
313
314 CompMatr1 out = {
315 .numQubits = 1,
316 .numRows = 2,
317 .elems = {
318 {in[0][0], in[0][1]},
319 {in[1][0], in[1][1]}}
320 };
321 return out;
322}
323
324
325/// @ingroup matrices_getters
326/// @notdoced
327static inline CompMatr2 getCompMatr2(qcomp** in) {
328 _validateNewNestedElemsPtrNotNull(in, 2, __func__);
329
330 CompMatr2 out = {
331 .numQubits = 2,
332 .numRows = 4,
333 .elems = {
334 {in[0][0], in[0][1], in[0][2], in[0][3]},
335 {in[1][0], in[1][1], in[1][2], in[1][3]},
336 {in[2][0], in[2][1], in[2][2], in[2][3]},
337 {in[3][0], in[3][1], in[3][2], in[3][3]}}
338 };
339 return out;
340}
341
342
343/// @ingroup matrices_getters
344/// @notdoced
345static inline DiagMatr1 getDiagMatr1(qcomp* in) {
346 _validateNewElemsPtrNotNull(in, __func__);
347
348 DiagMatr1 out = {
349 .numQubits = 1,
350 .numElems = 2,
351 .elems = {in[0], in[1]}
352 };
353 return out;
354}
355
356
357/// @ingroup matrices_getters
358/// @notdoced
359static inline DiagMatr2 getDiagMatr2(qcomp* in) {
360 _validateNewElemsPtrNotNull(in, __func__);
361
362 DiagMatr2 out = {
363 .numQubits = 2,
364 .numElems = 4,
365 .elems = {in[0], in[1], in[2], in[3]}
366 };
367 return out;
368}
369
370
371
372/*
373 * FIZED-SIZE MATRIX GETTERS VIA ARRAYS & VECTORS
374 *
375 * which define additional overloads for arrays, VLAs, C99 temporary arrays,
376 * vectors and vector initialisation lists. This empowers C users to call:
377 * - qcomp arr[2][2]; getCompMatr1(arr);
378 * - int n=2; qcomp arr[n][n]; getCompMatr1(arr);
379 * - getCompMatr1( (qcomp[2][2]) {...} );
380 * and C++ users call:
381 * - qcomp arr[2][2]; getCompMatr1(arr);
382 * - std::vector vec(2); getCompMatr1(vec);
383 * - getCompMatr1( {...} );
384 */
385
386
387// define the array overloads with a distinct name from the base
388// C function - we will alias it with getCompMatr() using Generics
389
390/// @private
391static inline CompMatr1 _getCompMatr1FromArr(qcomp in[2][2]) {
392
393 qcomp* rowPtrs[] = {in[0], in[1]};
394 return getCompMatr1(rowPtrs);
395}
396
397/// @private
398static inline CompMatr2 _getCompMatr2FromArr(qcomp in[4][4]) {
399
400 qcomp* rowPtrs[] = {in[0], in[1], in[2], in[3]};
401 return getCompMatr2(rowPtrs);
402}
403
404
405// no array overloads are necessary for getDiagMatr(), because
406// a 1D array automatically decays to a pointer
407
408
409#ifdef __cplusplus
410
411 // C++ defines overloads which merely wrap _getCompMatr1FromArr(), for fixed-size arrays.
412 // C++ also defines additional std::vector overloads (for convenience, and for inline initialisation).
413 // these are defined in matrices.cpp because they invoke validation (checking vector sizes)
414
415
416 /// @ingroup matrices_getters
417 /// @notdoced
418 static inline CompMatr1 getCompMatr1(qcomp in[2][2]) { return _getCompMatr1FromArr(in); }
419
420
421 /// @ingroup matrices_getters
422 /// @notdoced
423 static inline CompMatr2 getCompMatr2(qcomp in[4][4]) { return _getCompMatr2FromArr(in); }
424
425
426 /// @ingroup matrices_getters
427 /// @notdoced
428 /// @cpponly
429 CompMatr1 getCompMatr1(std::vector<std::vector<qcomp>> in);
430
431
432 /// @ingroup matrices_getters
433 /// @notdoced
434 /// @cpponly
435 CompMatr2 getCompMatr2(std::vector<std::vector<qcomp>> in);
436
437
438 /// @ingroup matrices_getters
439 /// @notdoced
440 /// @cpponly
441 DiagMatr1 getDiagMatr1(std::vector<qcomp> in);
442
443
444 /// @ingroup matrices_getters
445 /// @notdoced
446 /// @cpponly
447 DiagMatr2 getDiagMatr2(std::vector<qcomp> in);
448
449
450#else
451
452 // C uses C11 Generics to effectively overload getCompMatr1/2 to accept both
453 // pointers (as prior defined) and arrays (wrapping _getCompMatr1FromArr()). Note:
454 // - our macros below accept C99 variadic arguments so that users pass C99
455 // compound literals (e.g. (qcomp[]) {1,2}) in addition to existing ptrs.
456 // they cannot however exclude the (qcomp[]) syntax like C++ users enjoy,
457 // which is why we will subsequently define a getInlineCompMatr1()
458 // - Generics evaluate at compile-time (AFTER preprocessing) so their RHS
459 // expressions are limited; because of this, it is impossible to avoid
460 // defining the _getCompMatr1FromArr() inner functions to avoid exposing them.
461 // - our Generics explicitly check for pointer types (qcomp**), but we use default
462 // to catch all array types (qcomp[][n], or qcomp(*)[] due to automatic Generic
463 // pointer decay in GCC). This makes the code more consistent with our variable-size
464 // CompMatr macros later in this file, which cannot use VLA in Generics at all. It
465 // also avoids the user having to see a Generic compilation error message when they
466 // pass an invalid type.
467 // - Generic expansion does not recurse, hence our macro safely has the same name
468 // (e.g. getCompMatr1) as the inner function, defining a true overload
469 // - we could not have _Generic's 'default' to catch unrecognised types at compile
470 // time to issue a custom message, because we must expand _Generic to a function
471 // rather than a macro; preprocessing is finished by the time _Generic evaluates,
472 // so a macro would always be substituted before compilation and if it contained
473 // a compile-time error, it will always be triggered. A function error however
474 // would compile fine, but the error message would only be triggered at runtime
475 // when the user actually calls getCompMatr1() which is much worse than a slightly
476 // less clear compile-time error! A non-portable solution to this is to use
477 // _Pragma() in the RHS which is evaluated at compile-time (NOT pre-procesing),
478 // e.g. default: _Pragma("GCC error \"arg not allowed\"").
479
480
481 /// @neverdoced
482 #define getCompMatr1(...) \
483 _Generic((__VA_ARGS__), \
484 qcomp** : getCompMatr1, \
485 default : _getCompMatr1FromArr \
486 )((__VA_ARGS__))
487
488
489 /// @neverdoced
490 #define getCompMatr2(...) \
491 _Generic((__VA_ARGS__), \
492 qcomp** : getCompMatr2, \
493 default : _getCompMatr2FromArr \
494 )((__VA_ARGS__))
495
496
497 // note the above macros do not need explicit, separate doxygen
498 // doc because the C++ overloads above it have identical signatures
499
500#endif
501
502
503
504/*
505 * FIXED-SIZE MATRIX GETTERS VIA LITERALS
506 *
507 * which enable C users to give inline 2D array literals without having to use the
508 * compound literal syntax. We expose these macros to C++ too for API consistency.
509 * although C++'s getCompMatr1 vector overload achieves the same thing, and cannot
510 * use C-style temporary arrays.
511 *
512 * These empower C and C++ users to call
513 * - getInlineCompMatr1( {{1,2},{3,4}} )
514 */
515
516
517#ifdef __cplusplus
518
519 // C++ merely invokes the std::vector initialiser overload
520
521 /// @neverdoced
522 #define getInlineCompMatr1(...) \
523 getCompMatr1(__VA_ARGS__)
524
525 /// @neverdoced
526 #define getInlineCompMatr2(...) \
527 getCompMatr2(__VA_ARGS__)
528
529 /// @neverdoced
530 #define getInlineDiagMatr1(...) \
531 getDiagMatr1(__VA_ARGS__)
532
533 /// @neverdoced
534 #define getInlineDiagMatr2(...) \
535 getDiagMatr2(__VA_ARGS__)
536
537#else
538
539 // C adds compound literal syntax to make a temporary array. Helpfully,
540 // explicitly specifying the DiagMatr dimension enables defaulting-to-zero
541
542 /// @neverdoced
543 #define getInlineCompMatr1(...) \
544 _getCompMatr1FromArr((qcomp[2][2]) __VA_ARGS__)
545
546 /// @neverdoced
547 #define getInlineCompMatr2(...) \
548 _getCompMatr2FromArr((qcomp[4][4]) __VA_ARGS__)
549
550 /// @neverdoced
551 #define getInlineDiagMatr1(...) \
552 getDiagMatr1((qcomp[2]) __VA_ARGS__)
553
554 /// @neverdoced
555 #define getInlineDiagMatr2(...) \
556 getDiagMatr2((qcomp[4]) __VA_ARGS__)
557
558#endif
559
560// spoofing above macros as functions to doc
561#if 0
562
563 /// @ingroup matrices_getters
564 /// @notdoced
565 /// @macrodoc
567
568 /// @ingroup matrices_getters
569 /// @notdoced
570 /// @macrodoc
572
573 /// @ingroup matrices_getters
574 /// @notdoced
575 /// @macrodoc
577
578 /// @ingroup matrices_getters
579 /// @notdoced
580 /// @macrodoc
582
583#endif
584
585
586
587/*
588 * VARIABLE-SIZE MATRIX CONSTRUCTORS, DESTRUCTORS, SYNC
589 */
590
591
592// de-mangle so below are directly callable by C and C++ binary
593#ifdef __cplusplus
594extern "C" {
595#endif
596
597
598 /// @ingroup matrices_create
599 /// @notdoced
600 CompMatr createCompMatr(int numQubits);
601
602
603 /// @ingroup matrices_create
604 /// @notdoced
605 DiagMatr createDiagMatr(int numQubits);
606
607
608 /// @ingroup matrices_create
609 /// @notdoced
611
612
613 /// @ingroup matrices_create
614 /// @notdoced
615 FullStateDiagMatr createCustomFullStateDiagMatr(int numQubits, int useDistrib, int useGpuAccel, int useMultithread);
616
617
618 /// @ingroup matrices_destroy
619 /// @notdoced
620 void destroyCompMatr(CompMatr matrix);
621
622
623 /// @ingroup matrices_destroy
624 /// @notdoced
625 void destroyDiagMatr(DiagMatr matrix);
626
627
628 /// @ingroup matrices_destroy
629 /// @notdoced
631
632
633 /// @ingroup matrices_sync
634 /// @notdoced
635 void syncCompMatr(CompMatr matr);
636
637
638 /// @ingroup matrices_sync
639 /// @notdoced
640 void syncDiagMatr(DiagMatr matr);
641
642
643 /// @ingroup matrices_sync
644 /// @notdoced
646
647
648#ifdef __cplusplus
649}
650#endif
651
652
653
654/*
655 * VARIABLE-SIZE MATRIX SETTERS VIA POINTERS
656 *
657 * These functions permit users to pass heap and stack pointers:
658 * - qcomp** ptr = malloc(...); setCompMatr(m, ptr);
659 * - qcomp* ptrs[8]; setCompMatr(m, ptrs);
660 * in both C and C++. By decay, they also permit arrays to diagonals:
661 * - qcomp* ptr = malloc(...); setDiagMatr(m, ptr);
662 * - qcomp arr[8]; setDiagMatr(m, arr);
663 */
664
665
666// de-mangle so below are directly callable by C and C++ binary
667#ifdef __cplusplus
668extern "C" {
669#endif
670
671
672 /// @ingroup matrices_setters
673 /// @notdoced
674 void setCompMatr(CompMatr matr, qcomp** vals);
675
676
677 /// @ingroup matrices_setters
678 /// @notdoced
679 void setDiagMatr(DiagMatr out, qcomp* in);
680
681
682 /// @ingroup matrices_setters
683 /// @notdoced
684 /// @nottested
685 void setFullStateDiagMatr(FullStateDiagMatr out, qindex startInd, qcomp* in, qindex numElems);
686
687
688#ifdef __cplusplus
689}
690#endif
691
692
693
694/*
695 * VARIABLE-SIZE MATRIX SETTERS VIA ARRAYS & VECTORS
696 *
697 * which define additional overloads for arrays, VLAs, vectors and vector initialisation lists.
698 * C users can call:
699 * - qcomp arr[8][8]; setCompMatr(m, arr);
700 * - int n=8; qcomp arr[n][n]; setCompMatr(m, arr);
701 * - setCompMatr(m, (qcomp[8][8]) {{...}});
702 * - inline temporary VLA remains impossible even in C99, however
703 * and C++ users can call:
704 * - int n=8; std::vector vec(n); setCompMatr(m, vec);
705 * - setCompMatr(m, {{...}});
706 */
707
708
709#if defined(__cplusplus)
710
711 // C++ defines vector overloads, permitting inline initialisation
712
713
714 /// @ingroup matrices_setters
715 /// @notdoced
716 /// @cpponly
717 void setCompMatr(CompMatr out, std::vector<std::vector<qcomp>> in);
718
719
720 /// @ingroup matrices_setters
721 /// @notdoced
722 /// @cpponly
723 void setDiagMatr(DiagMatr out, std::vector<qcomp> in);
724
725
726 /// @ingroup matrices_setters
727 /// @notdoced
728 /// @nottested
729 /// @cpponly
730 void setFullStateDiagMatr(FullStateDiagMatr out, qindex startInd, std::vector<qcomp> in);
731
732
733 // C++ cannot accept 2D arrays at all, because it does not support C99 VLA.
734 // It can however accept 1D arrays (which decay to pointers) already to setDiagMatr()
735
736#elif !defined(_MSC_VER)
737
738 // C first defines a bespoke functions receiving C99 VLAs, which we have to define here in
739 // the header becauses the C++ source cannot use VLA, nor should we pass a 2D qcomp array
740 // directly between C and C++ binaries (due to limited interoperability)
741
742
743 // C must validate struct fields before accessing passed 2D arrays to avoid seg-faults
744 /// @private
745 extern void _validateParamsToSetCompMatrFromArr(CompMatr matr);
746
747
748 // static inline to avoid header-symbol duplication
749 /// @private
750 static inline void _setCompMatrFromArr(CompMatr matr, qcomp arr[matr.numRows][matr.numRows]) {
751 _validateParamsToSetCompMatrFromArr(matr);
752
753 // new ptrs array safely fits in stack, since it's sqrt-smaller than user's passed stack array
754 qcomp* ptrs[matr.numRows];
755
756 // collect pointers to each row of arr
757 for (qindex r=0; r<matr.numRows; r++)
758 ptrs[r] = arr[r];
759
760 // array decays to qcomp**, and *FromPtr function re-performs validation (eh)
761 setCompMatr(matr, ptrs); // validation gauranteed to pass
762 }
763
764
765 // C then overloads setCompMatr() to call the above VLA when given arrays, using C11 Generics.
766 // See the doc of getCompMatr1() above for an explanation of Generic, and its nuances
767
768
769 /// @neverdoced
770 #define setCompMatr(matr, ...) \
771 _Generic((__VA_ARGS__), \
772 qcomp** : setCompMatr, \
773 default : _setCompMatrFromArr \
774 )((matr), (__VA_ARGS__))
775
776 // spoofing above macro as functions to doc
777 #if 0
778
779 /// @ingroup matrices_setters
780 /// @notdoced
781 /// @macrodoc
782 /// @conly
783 void setCompMatr(CompMatr matr, qcomp arr[matr.numRows][matr.numRows]);
784
785 #endif
786
787
788 // no need to define bespoke overload for diagonal matrices, because 1D arrays decay to pointers
789
790#else
791
792 // MSVC's C11 does not support C99 VLAs (which the standard left optional, grr!), so
793 // we cannot support 2D-array initialisation of CompMatr at all. This means only the
794 // existing setCompMatr(qcomp**) declared previously is usable by MSVC C users
795
796#endif
797
798
799
800/*
801 * VARIABLE-SIZE MATRIX SETTERS VIA LITERALS
802 *
803 * which enable C users to give inline 2D array literals without having to use the
804 * VLA compound literal syntax. We expose these macros to C++ too for API consistency,
805 * although C++'s vector overloads achieve the same thing.
806 *
807 * These empower C and C++ users to call e.g.
808 * - setInlineCompMatr(m, 1, {{1,2},{3,4}})
809 */
810
811
812#if defined(__cplusplus)
813
814 // C++ redirects to vector overloads, passing initialiser lists. The args like 'numQb'
815 // are superfluous, but needed for consistency with the C API, so we additionally
816 // validate that they match the struct dimensions (which requires validating the structs).
817
818
819 /// @ingroup matrices_setters
820 /// @notdoced
821 /// @cpponly
822 void setInlineCompMatr(CompMatr matr, int numQb, std::vector<std::vector<qcomp>> in);
823
824
825 /// @ingroup matrices_setters
826 /// @notdoced
827 /// @cpponly
828 void setInlineDiagMatr(DiagMatr matr, int numQb, std::vector<qcomp> in);
829
830
831 /// @ingroup matrices_setters
832 /// @notdoced
833 /// @nottested
834 /// @cpponly
835 void setInlineFullStateDiagMatr(FullStateDiagMatr matr, qindex startInd, qindex numElems, std::vector<qcomp> in);
836
837
838#elif !defined(_MSC_VER)
839
840 // C defines macros which add compound literal syntax so that the user's passed lists
841 // become compile-time-sized temporary arrays. C99 does not permit inline-initialised
842 // VLAs, so we cannot have the macro expand to add (qcomp[matr.numRows][matr.numRows])
843 // in order to preclude passing 'numQb'. We ergo accept and validate 'numQb' macro param.
844 // We define private inner-functions of a macro, in lieu of writing multiline macros
845 // using do-while, just to better emulate a function call for users - e.g. they
846 // can wrap the macro invocations with another function call, etc.
847
848
849 // the C validators check 'numQb' is consistent with the struct, but cannot check the user's passed literal sizes
850 /// @private
851 extern void _validateParamsToSetInlineCompMatr(CompMatr matr, int numQb);
852 /// @private
853 extern void _validateParamsToSetInlineDiagMatr(DiagMatr matr, int numQb);
854 /// @private
855 extern void _validateParamsToSetInlineFullStateDiagMatr(FullStateDiagMatr matr, qindex startInd, qindex numElems);
856
857
858 /// @private
859 static inline void _setInlineCompMatr(CompMatr matr, int numQb, qcomp elems[1<<numQb][1<<numQb]) {
860 _validateParamsToSetInlineCompMatr(matr, numQb);
861 _setCompMatrFromArr(matr, elems); // validation gauranteed to pass
862 }
863
864 /// @private
865 static inline void _setInlineDiagMatr(DiagMatr matr, int numQb, qcomp elems[1<<numQb]) {
866 _validateParamsToSetInlineDiagMatr(matr, numQb);
867 setDiagMatr(matr, elems); // 1D array decays into pointer, validation gauranteed to pass
868 }
869
870 /// @private
871 static inline void _setInlineFullStateDiagMatr(FullStateDiagMatr matr, qindex startInd, qindex numElems, qcomp elems[numElems]) {
872 _validateParamsToSetInlineFullStateDiagMatr(matr, startInd, numElems);
873 setFullStateDiagMatr(matr, startInd, elems, numElems); // 1D array decays into pointer, validation gauranteed to pass
874 }
875
876
877 // happily, macro arg 'numQb' must be a compile-time constant, so there is no risk of
878 // unexpectedly re-evaluating user expressions due to its repetition in the macro
879
880
881 /// @neverdoced
882 #define setInlineCompMatr(matr, numQb, ...) \
883 _setInlineCompMatr((matr), (numQb), (qcomp[1<<(numQb)][1<<(numQb)]) __VA_ARGS__)
884
885 /// @neverdoced
886 #define setInlineDiagMatr(matr, numQb, ...) \
887 _setInlineDiagMatr((matr), (numQb), (qcomp[1<<(numQb)]) __VA_ARGS__)
888
889 /// @neverdoced
890 #define setInlineFullStateDiagMatr(matr, startInd, numElems, ...) \
891 _setInlineFullStateDiagMatr((matr), (startInd), (numElems), (qcomp[(numElems)]) __VA_ARGS__)
892
893 // spoofing above macros as functions to doc
894 #if 0
895
896 /// @ingroup matrices_setters
897 /// @notdoced
898 /// @macrodoc
899 void setInlineCompMatr(CompMatr matr, int numQb, {{ matrix }});
900
901 /// @ingroup matrices_setters
902 /// @notdoced
903 /// @macrodoc
904 void setInlineDiagMatr(DiagMatr matr, int numQb, { list });
905
906 /// @ingroup matrices_setters
907 /// @nottested
908 /// @notdoced
909 /// @macrodoc
910 void setInlineFullStateDiagMatr(FullStateDiagMatr matr, qindex startInd, qindex numElems, { list });
911
912 #endif
913
914
915#else
916
917 // MSVC C11 does not support C99 VLAs, so the inner functions above are illegal.
918 // As such, we must choose to either forego the internal validation (which
919 // checks that the passed matrix object has been prior created with e.g.
920 // createDiagMatr), or expand the macro into a do-while which users cannot ergo
921 // place inside another function call. We opt to preclude the latter, since it
922 // seems an unlikely use-case (because the function returns void) and will give
923 // a compile-time error, whereas removing validation could cause silent seg-faults
924 // when users incorrectly initialise an un-created matrix.
925
926 // Note however that because MSVC does not support C99 VLA in C11, such that
927 // _setCompMatrFromArr() was not defined, so we cannot define setInlineCompMatr();
928 // MSVC C users simply miss out on this convenience function. Take it up with Bill!
929
930 /// @private
931 extern void _validateParamsToSetInlineDiagMatr(DiagMatr matr, int numQb);
932 /// @private
933 extern void _validateParamsToSetInlineFullStateDiagMatr(FullStateDiagMatr matr, qindex startInd, qindex numElems);
934
935
936 /// @neverdoced
937 #define setInlineDiagMatr(matr, numQb, ...) \
938 do { \
939 _validateParamsToSetInlineDiagMatr((matr), (numQb)); \
940 setDiagMatr((matr), (numQb), (qcomp[1<<(numQb)]) __VA_ARGS__); \
941 } while (0)
942
943
944 /// @neverdoced
945 #define setInlineFullStateDiagMatr(matr, startInd, numElems, ...) \
946 do { \
947 _validateParamsToSetInlineFullStateDiagMatr((matr), (startInd), (numElems)); \
948 setFullStateDiagMatr((matr), (startInd), (elems), (numElems)); \
949 } while (0)
950
951
952 // the above macros are documented in the previous #if branch
953
954#endif
955
956
957
958/*
959 * VARIABLE-SIZE MATRIX CREATORS VIA LITERALS
960 *
961 * which simply combine the create*() and setInline*() functions, for
962 * user convenience, and to reduce their risk of passing inconsistent params.
963 * We do not define inline creators for FullStateDiagMatr, since the
964 * creator automatically decides whether or not to distribute the matrix;
965 * ergo the user cannot know how many elements to pass in their literal
966 * (nor should they ever distribute data which fits into a single literal!)
967 *
968 * These empower C and C++ users to call e.g.
969 * - CompMatr m = createInlineCompMatr(1, {{1,2},{3,4}})
970 */
971
972
973#if defined(__cplusplus)
974
975 // C++ accepts vector initialiser lists
976
977
978 /// @ingroup matrices_create
979 /// @notdoced
980 /// @cpponly
981 CompMatr createInlineCompMatr(int numQb, std::vector<std::vector<qcomp>> elems);
982
983
984 /// @ingroup matrices_create
985 /// @notdoced
986 /// @cpponly
987 DiagMatr createInlineDiagMatr(int numQb, std::vector<qcomp> elems);
988
989
990#elif !defined(_MSC_VER)
991
992 // C defines macros which add compound literal syntax so that the user's passed lists
993 // become compile-time-sized temporary arrays. We use bespoke validation so that the
994 // error messages reflect the name of the macro, rather than the inner called functions.
995 // We define a private inner function per macro, in lieu of writing multiline macros
996 // using do-while, just to better emulate a function call for users - e.g. they
997 // can wrap the macro invocation with another function call.
998
999
1000 /// @private
1001 extern void _validateParamsToCreateInlineCompMatr(int numQb);
1002 /// @private
1003 extern void _validateParamsToCreateInlineDiagMatr(int numQb);
1004
1005
1006 /// @private
1007 static inline CompMatr _createInlineCompMatr(int numQb, qcomp elems[1<<numQb][1<<numQb]) {
1008 _validateParamsToCreateInlineCompMatr(numQb);
1009 CompMatr out = createCompMatr(numQb); // malloc failures will report 'createCompMatr', rather than 'inline' version. Alas!
1010 _setCompMatrFromArr(out, elems);
1011 return out;
1012 }
1013
1014 /// @private
1015 static inline DiagMatr _createInlineDiagMatr(int numQb, qcomp elems[1<<numQb]) {
1016 _validateParamsToCreateInlineDiagMatr(numQb);
1017 DiagMatr out = createDiagMatr(numQb); // malloc failures will report 'createCompMatr', rather than 'inline' version. Alas!
1018 setDiagMatr(out, elems); // 1D array decays to ptr
1019 return out;
1020 }
1021
1022
1023 /// @neverdoced
1024 #define createInlineCompMatr(numQb, ...) \
1025 _createInlineCompMatr((numQb), (qcomp[1<<(numQb)][1<<(numQb)]) __VA_ARGS__)
1026
1027 /// @neverdoced
1028 #define createInlineDiagMatr(numQb, ...) \
1029 _createInlineDiagMatr((numQb), (qcomp[1<<(numQb)]) __VA_ARGS__)
1030
1031 // spoofing above macros as functions to doc
1032 #if 0
1033
1034 /// @ingroup matrices_create
1035 /// @notdoced
1036 /// @macrodoc
1037 CompMatr createInlineCompMatr(int numQb, {{ matrix }});
1038
1039 /// @ingroup matrices_create
1040 /// @notdoced
1041 /// @macrodoc
1042 DiagMatr createInlineDiagMatr(int numQb, { list });
1043
1044 #endif
1045
1046#else
1047
1048 // MSVC's C11 does not support C99 VLA, so we cannot use the above inner functions.
1049 // The nuisance of trying to create, modify then return a matrix instance using
1050 // MSVC-specific preprocessors is too annoying, so Windows C users miss out! :(
1051
1052#endif
1053
1054
1055
1056/*
1057 * SPECIAL CREATORS AND SETTERS
1058 */
1059
1060
1061#ifdef __cplusplus
1062extern "C" {
1063#endif
1064
1065
1066 /// @todo
1067 /// add std::vector<int> overloads for C++ users for the
1068 /// below functions (missed during original overload work)
1069
1070
1071 /// @ingroup matrices_setters
1072 /// @notdoced
1073 /// @nottested
1074 void setDiagMatrFromMultiVarFunc(DiagMatr out, qcomp (*func)(qindex*), int* numQubitsPerVar, int numVars, int areSigned);
1075
1076
1077 /// @ingroup matrices_setters
1078 /// @notdoced
1079 /// @nottested
1080 void setDiagMatrFromMultiDimLists(DiagMatr out, void* lists, int* numQubitsPerDim, int numDims);
1081
1082
1083 /// @ingroup matrices_create
1084 /// @notdoced
1085 /// @nottested
1087
1088
1089 /// @ingroup matrices_setters
1090 /// @notdoced
1091 /// @nottested
1093
1094
1095 /// @ingroup matrices_setters
1096 /// @notdoced
1097 /// @nottested
1098 void setFullStateDiagMatrFromMultiVarFunc(FullStateDiagMatr out, qcomp (*func)(qindex*), int* numQubitsPerVar, int numVars, int areSigned);
1099
1100
1101 /// @ingroup matrices_setters
1102 /// @notdoced
1103 /// @nottested
1104 void setFullStateDiagMatrFromMultiDimLists(FullStateDiagMatr out, void* lists, int* numQubitsPerDim, int numDims);
1105
1106
1107#ifdef __cplusplus
1108}
1109#endif
1110
1111
1112
1113/*
1114 * MATRIX REPORTERS
1115 */
1116
1117
1118// de-mangle so below are directly callable by C binary
1119#ifdef __cplusplus
1120extern "C" {
1121#endif
1122
1123
1124 /// @ingroup matrices_reporters
1125 /// @notdoced
1126 /// @nottested
1127 void reportCompMatr1(CompMatr1 matrix);
1128
1129
1130 /// @ingroup matrices_reporters
1131 /// @notdoced
1132 /// @nottested
1133 void reportCompMatr2(CompMatr2 matrix);
1134
1135
1136 /// @ingroup matrices_reporters
1137 /// @notdoced
1138 /// @nottested
1139 void reportCompMatr(CompMatr matrix);
1140
1141
1142 /// @ingroup matrices_reporters
1143 /// @notdoced
1144 /// @nottested
1145 void reportDiagMatr1(DiagMatr1 matrix);
1146
1147
1148 /// @ingroup matrices_reporters
1149 /// @notdoced
1150 /// @nottested
1151 void reportDiagMatr2(DiagMatr2 matrix);
1152
1153
1154 /// @ingroup matrices_reporters
1155 /// @notdoced
1156 /// @nottested
1157 void reportDiagMatr(DiagMatr matrix);
1158
1159
1160 /// @ingroup matrices_reporters
1161 /// @notdoced
1162 /// @nottested
1164
1165
1166#ifdef __cplusplus
1167}
1168#endif
1169
1170
1171
1172#endif // MATRICES_H
1173
1174/** @} */ // (end file-wide doxygen defgroup)
FullStateDiagMatr createCustomFullStateDiagMatr(int numQubits, int useDistrib, int useGpuAccel, int useMultithread)
Definition matrices.cpp:322
DiagMatr createInlineDiagMatr(int numQb, std::vector< qcomp > elems)
FullStateDiagMatr createFullStateDiagMatr(int numQubits)
Definition matrices.cpp:327
CompMatr createInlineCompMatr(int numQb, std::vector< std::vector< qcomp > > elems)
CompMatr createCompMatr(int numQubits)
Definition matrices.cpp:211
DiagMatr createDiagMatr(int numQubits)
Definition matrices.cpp:246
FullStateDiagMatr createFullStateDiagMatrFromPauliStrSum(PauliStrSum in)
Definition matrices.cpp:658
void destroyDiagMatr(DiagMatr matrix)
Definition matrices.cpp:403
void destroyFullStateDiagMatr(FullStateDiagMatr matrix)
Definition matrices.cpp:404
void destroyCompMatr(CompMatr matrix)
Definition matrices.cpp:402
static CompMatr2 getCompMatr2(qcomp **in)
Definition matrices.h:327
static CompMatr1 getCompMatr1(qcomp **in)
Definition matrices.h:311
static DiagMatr2 getDiagMatr2(qcomp *in)
Definition matrices.h:359
CompMatr1 getInlineCompMatr1({{ matrix }})
CompMatr2 getInlineCompMatr2({{ matrix }})
DiagMatr2 getInlineDiagMatr2({ list })
static DiagMatr1 getDiagMatr1(qcomp *in)
Definition matrices.h:345
DiagMatr1 getInlineDiagMatr1({ list })
void reportCompMatr2(CompMatr2 matrix)
Definition matrices.cpp:785
void reportDiagMatr1(DiagMatr1 matrix)
Definition matrices.cpp:787
void reportCompMatr(CompMatr matrix)
Definition matrices.cpp:786
void reportDiagMatr2(DiagMatr2 matrix)
Definition matrices.cpp:788
void reportCompMatr1(CompMatr1 matrix)
Definition matrices.cpp:784
void reportDiagMatr(DiagMatr matrix)
Definition matrices.cpp:789
void reportFullStateDiagMatr(FullStateDiagMatr matr)
Definition matrices.cpp:790
void setInlineDiagMatr(DiagMatr matr, int numQb, std::vector< qcomp > in)
void setFullStateDiagMatrFromMultiDimLists(FullStateDiagMatr out, void *lists, int *numQubitsPerDim, int numDims)
Definition matrices.cpp:730
void setDiagMatrFromMultiDimLists(DiagMatr out, void *lists, int *numQubitsPerDim, int numDims)
Definition matrices.cpp:708
void setFullStateDiagMatrFromMultiVarFunc(FullStateDiagMatr out, qcomp(*func)(qindex *), int *numQubitsPerVar, int numVars, int areSigned)
Definition matrices.cpp:694
void setInlineCompMatr(CompMatr matr, int numQb, std::vector< std::vector< qcomp > > in)
void setDiagMatr(DiagMatr out, qcomp *in)
Definition matrices.cpp:435
void setCompMatr(CompMatr matr, qcomp **vals)
Definition matrices.cpp:427
void setFullStateDiagMatr(FullStateDiagMatr out, qindex startInd, qcomp *in, qindex numElems)
Definition matrices.cpp:447
void setDiagMatrFromMultiVarFunc(DiagMatr out, qcomp(*func)(qindex *), int *numQubitsPerVar, int numVars, int areSigned)
Definition matrices.cpp:674
void setFullStateDiagMatrFromPauliStrSum(FullStateDiagMatr out, PauliStrSum in)
Definition matrices.cpp:641
void setInlineFullStateDiagMatr(FullStateDiagMatr matr, qindex startInd, qindex numElems, std::vector< qcomp > in)
void syncFullStateDiagMatr(FullStateDiagMatr matr)
Definition matrices.cpp:379
void syncDiagMatr(DiagMatr matr)
Definition matrices.cpp:378
void syncCompMatr(CompMatr matr)
Definition matrices.cpp:377
int * wasGpuSynced
Definition matrices.h:108
int * isApproxNonZero
Definition matrices.h:166
int * isStrictlyNonNegative
Definition matrices.h:167
int * wasGpuSynced
Definition matrices.h:174