The Quantum Exact Simulation Toolkit v4.0.0
Loading...
Searching...
No Matches
channels.h
1/** @file
2 * Data structures for representing arbitrary channels as
3 * superoperators and Kraus maps, including their constructors,
4 * getters, setters and reporters. Note the functions to
5 * actually simulate these channels are exposed in decoherence.h
6 *
7 * Like matrices.h, this file makes extensive use of macros to
8 * overload struct initialisers for user convenience. All macros
9 * herein expand to single-line definitions for safety. Some
10 * intendedly private functions are necessarily exposed here to
11 * the user, and are prefixed with an underscore.
12 *
13 * Design nuances:
14 * - SuperOp is a separate, independent data-structure from KrausMap
15 * which is never assumed/validated to be CPTP. This is because the
16 * runtime assessment of CPTP of an arbitrary superoperator is expensive,
17 * requiring diagonalisation.
18 * - KrausMap contains an internal SuperOp instance which it uses to
19 * simulate the channel, which is re-populated whenever the constituent
20 * Kraus operators of the map are changed by the user.
21 * - KrausMap maintains an explicit list of Kraus operators, even though
22 * only the single resulting superoperator is used for simulation. This is
23 * so that CPTP validation can be efficiently performed at any time, and so
24 * that KrausMap reporting can display the individual quadratically-smaller
25 * Kraus operators, for user clarity. This is an insignificant memory waste.
26 * - KrausMap must know the number of constituent Kraus operators upfront, in
27 * order to allocate their memory. It is not possible to specify fewer Kraus
28 * operators later when initialising the KrausMap, because tracking a "number
29 * of Kraus operators" independently of the "maximum number of Kraus operators"
30 * is smelly and over-engineered. Changing operators requires a new KrausMap.
31 * - There are no fixed-size stack-memory versions of KrausMap and SuperOp,
32 * unlike their matrix counterparts which have (e.g.) CompMatr1. This is
33 * because fixed-size KrausMap creation involves populating the superoperator
34 * (and in GPU settings, copying to GPU memory) which may be an astonishingly
35 * large overhead, and expensive to copy between function stacks.
36 *
37 * @author Tyson Jones
38 * @author Richard Meister (aided in design)
39 * @author Erich Essmann (aided in design)
40 *
41 * @defgroup channels Channels
42 * @ingroup api
43 * @brief Data structures for representing arbitrary channels as Kraus maps and superoperators.
44 * @{
45 */
46
47#ifndef CHANNELS_H
48#define CHANNELS_H
49
50#include "quest/include/types.h"
51
52// C++ gets vector initialiser overloads, whereas C gets a macro
53#ifdef __cplusplus
54 #include <vector>
55#endif
56
57
58
59/*
60 * unlike some other headers, we here intermix the C and C++-only
61 * signatures, grouping them semantically & by their doc groups
62 */
63
64
65
66/**
67 * @defgroup channels_structs Structs
68 * @brief Data structures for representing decoherence channels.
69 * @{
70 */
71
72
73/// @notdoced
74typedef struct {
75
76 int numQubits;
77 qindex numRows;
78
79 // 2D CPU memory, which users can manually overwrite like cpuElems[i][j],
80 // but which actually merely aliases the 1D cpuElemsFlat below
81 qcomp** cpuElems;
82
83 // row-major flattened elements of cpuElems, always allocated
84 qcomp* cpuElemsFlat;
85
86 // row-major flattened elems in GPU memory, allocated
87 // only and always in GPU-enabled QuEST environments
88 qcomp* gpuElemsFlat;
89
90 // whether the user has ever synchronised memory to the GPU, which is performed automatically
91 // when calling functions like setCompMatr(), but which requires manual invocation with
92 // syncCompMatr() after manual modification of the cpuElem. Note this can only indicate whether
93 // the matrix has EVER been synced; it cannot be used to detect whether manual modifications
94 // made after an initial sync have been re-synched. This is a heap pointer to remain mutable.
95 int* wasGpuSynced;
96
97} SuperOp;
98
99
100/// @notdoced
101typedef struct {
102
103 int numQubits;
104
105 // representation of the map as a collection of Kraus operators, kept exclusively
106 // in CPU memory, and used only for CPTP validation and reporting the map
107 int numMatrices;
108 qindex numRows;
109 qcomp*** matrices;
110
111 // representation of the map as a single superoperator, used for simulation
112 SuperOp superop;
113
114 // CPTP-ness is determined at validation; 0 or 1, or -1 to indicate unknown. The flag is
115 // stored in heap so even copies of structs are mutable, but pointer itself is immutable.
116 int* isApproxCPTP;
117
118} KrausMap;
119
120
121// we define no fixed-size versions (e.g. KrausMap1/2), unlike we did for CompMatr1/2
122// and DiagMatr1/2. This is because the 2-qubit superoperator is 256 elements big, and
123// seems inadvisably large to be passing-by-copy through the QuEST backend layers, and
124// would need explicit GPU memory allocation at each invocation of mixKrausMap2() (it
125// exceeds the max number of CUDA kernel args). Furthermore, repeatedly calling
126// createKrausMap2() would repeatedly invoke ~256*16 flops to compute te superoperator,
127// which may be an user-astonishing overhead (more astonishing than the API asymmetry).
128// Finally, computing the fixed-size superoperators must be in the header (to avoid
129// the issues of qcmop interoperability, just like for getCompMatr1) and could not call
130// an inner function which wouldn't be user-exposed; so we would end up redefining the
131// superoperator calculation THREE times!
132
133
134/** @} */
135
136
137
138// we define the remaining doc groups in advance, since their signatures are
139// more naturally grouped in an implementation-specific way below. Note the
140// above structs were not doc'd this way (which would be more consistent)
141// because it inexplicably causes Doxygen to duplicate their section at the
142// top-level under Channels (rather than under Structs). Bizarre! The order
143// of declaration below will match the order shown in the html doc.
144/**
145 * @defgroup channels_create Constructors
146 * @brief Functions for creating channel data structures.
147 *
148 * @defgroup channels_destroy Destructors
149 * @brief Functions for destroying existing channel data structures.
150 *
151 * @defgroup channels_reporters Reporters
152 * @brief Functions for printing channels.
153 *
154 * @defgroup channels_setters Setters
155 * @brief Functions for overwriting the elements of channels.
156 *
157 * @defgroup channels_sync Synchronisation
158 * @brief Functions for overwriting a channel's GPU (VRAM) memory with its CPU (RAM) contents.
159 * @details These functions are only necessary when the user wishes to manually modify the
160 * elements of a channel (in lieu of using the @ref channels_setters "Setters"), to
161 * thereafter synchronise the changes to the GPU copy of the channel. These functions
162 * have no effect when running without GPU-acceleration, but remain legal and harmless
163 * to call (to achieve platform agnosticism).
164 */
165
166
167
168/*
169 * BASIC FUNCTIONS
170 */
171
172
173// de-mangle so below are directly callable by C and C++ binary
174#ifdef __cplusplus
175extern "C" {
176#endif
177
178
179 /// @ingroup channels_create
180 /// @notdoced
181 KrausMap createKrausMap(int numQubits, int numOperators);
182
183
184 /// @ingroup channels_sync
185 /// @notdoced
186 void syncKrausMap(KrausMap map);
187
188
189 /// @ingroup channels_destroy
190 /// @notdoced
191 void destroyKrausMap(KrausMap map);
192
193
194 /// @ingroup channels_reporters
195 /// @notdoced
196 /// @nottested
197 void reportKrausMap(KrausMap map);
198
199
200 /// @ingroup channels_create
201 /// @notdoced
202 SuperOp createSuperOp(int numQubits);
203
204
205 /// @ingroup channels_sync
206 /// @notdoced
207 void syncSuperOp(SuperOp op);
208
209
210 /// @ingroup channels_destroy
211 /// @notdoced
212 void destroySuperOp(SuperOp op);
213
214
215 /// @ingroup channels_reporters
216 /// @notdoced
217 /// @nottested
218 void reportSuperOp(SuperOp op);
219
220
221#ifdef __cplusplus
222}
223#endif
224
225
226
227/*
228 * POINTER INITIALISERS
229 *
230 * which permit users to pass heap and stack pointers in both C and C++, e.g.
231 * - qcomp** ptr = malloc(...); setSuperOp(m, ptr);
232 * - qcomp* ptrs[16]; setSuperOp(m, ptrs);
233 * - qcomp*** ptr = malloc(...); setKrausMap(m, ptr);
234 */
235
236
237// de-mangle so below are directly callable by C and C++ binary
238#ifdef __cplusplus
239extern "C" {
240#endif
241
242
243 /// @ingroup channels_setters
244 /// @notdoced
245 void setKrausMap(KrausMap map, qcomp*** matrices);
246
247
248 /// @ingroup channels_setters
249 /// @notdoced
250 void setSuperOp(SuperOp op, qcomp** matrix);
251
252
253#ifdef __cplusplus
254}
255#endif
256
257
258
259/*
260 * ARRAY, VECTOR, MATRIX INITIALISERS
261 *
262 * which define additional overloads for arrays, VLAs, vectors and vector initialisation lists.
263 * They permit C users to additionally call e.g.
264 * - qcomp arr[16][16]; setSuperOp(m, arr);
265 * - int n=16; qcomp arr[n][n]; setSuperOp(m, arr);
266 * - setKrausMap(m, (qcomp[5][16][16]) {{{...}}});
267 * - inline temporary VLA remains impossible even in C99, however
268 * and C++ users gain overloads:
269 * - int n=8; std::vector vec(n); setSuperOp(m, vec);
270 * - setKrausMap(m, {{{...}}} );
271 * An unintended but harmless side-effect is the exposure of functions setKrausMapFromArr(),
272 * setSuperOpFromArr(), validate_setCompMatrFromArr() and validate_setSuperOpFromArr to the user.
273 */
274
275
276#if defined(__cplusplus)
277
278 // C++ overloads to accept vectors, which also enables vector initialiser literals
279
280
281 /// @ingroup channels_setters
282 /// @notdoced
283 /// @cpponly
284 void setKrausMap(KrausMap map, std::vector<std::vector<std::vector<qcomp>>> matrices);
285
286
287 /// @ingroup channels_setters
288 /// @notdoced
289 /// @cpponly
290 void setSuperOp(SuperOp op, std::vector<std::vector<qcomp>> matrix);
291
292
293 // C++ cannot accept VLAs so does not define 2D array overloads
294
295#elif !defined(_MSC_VER)
296
297 // C first defines bespoke functions which accept C99 VLAs, which we have to define here in
298 // the header becauses the C++ source cannot use VLA, nor should we pass a 2D qcomp array
299 // directly between C and C++ binaries (due to limited interoperability)
300
301
302 // C must validate the struct fields before accessing passed 2D arrays to avoid seg-faults
303 /// @private
304 extern void _validateParamsToSetKrausMapFromArr(KrausMap map);
305 /// @private
306 extern void _validateParamsToSetSuperOpFromArr(SuperOp op);
307
308
309 /// @private
310 static inline void _setKrausMapFromArr(KrausMap map, qcomp matrices[map.numMatrices][map.numRows][map.numRows]) {
311 _validateParamsToSetKrausMapFromArr(map);
312
313 // create stack space for 2D collection of pointers, one to each input row
314 qcomp* rows[map.numMatrices][map.numRows];
315 qcomp** ptrs[map.numMatrices];
316
317 // copy decayed array pointers into stack
318 for (int n=0; n<map.numMatrices; n++) {
319 for (qindex r=0; r<map.numRows; r++)
320 rows[n][r] = matrices[n][r];
321 ptrs[n] = rows[n];
322 }
323
324 setKrausMap(map, ptrs); // validation gauranteed to pass
325 }
326
327 /// @private
328 static inline void _setSuperOpFromArr(SuperOp op, qcomp matrix[op.numRows][op.numRows]) {
329 _validateParamsToSetSuperOpFromArr(op);
330
331 // create stack space for pointers, one for each input row
332 qcomp* ptrs[op.numRows];
333
334 // copy decayed array pointers into stack
335 for (qindex r=0; r<op.numRows; r++)
336 ptrs[r] = matrix[r];
337
338 setSuperOp(op, ptrs); // validation gauranteed to pass
339 }
340
341
342 // C then overloads setKrausMap() to call the above VLA when given arrays, using C11 Generics.
343 // See the doc of getCompMatr1() in matrices.h for an explanation of Generic, and its nuances.
344
345 /// @neverdoced
346 #define setKrausMap(map, ...) \
347 _Generic((__VA_ARGS__), \
348 qcomp*** : setKrausMap, \
349 default : _setKrausMapFromArr \
350 )((map), (__VA_ARGS__))
351
352 /// @neverdoced
353 #define setSuperOp(op, ...) \
354 _Generic((__VA_ARGS__), \
355 qcomp** : setSuperOp, \
356 default : _setSuperOpFromArr \
357 )((op), (__VA_ARGS__))
358
359 // spoofing macros as functions
360 #if 0
361
362 /// @ingroup channels_setters
363 /// @notdoced
364 /// @conly
365 /// @macrodoc
366 void setKrausMap(KrausMap map, qcomp matrices[map.numMatrices][map.numRows][map.numRows]);
367
368 /// @ingroup channels_setters
369 /// @notdoced
370 /// @conly
371 /// @macrodoc
372 void setSuperOp(SuperOp op, qcomp matrix[op.numRows][op.numRows]);
373
374 #endif
375
376
377#else
378
379 // MSVC's C11 does not support C99 VLAs, so there is no way to support _setKrausMapFromArr(),
380 // and ergo no need for setKrausMap() or setSuperOp() wrappers. This sadly means MSVC C users
381 // can only use the existing functions which accept qcomp*** and qcomp** respectively.
382
383#endif
384
385
386
387/*
388 * LITERAL INITIALISERS
389 *
390 * which enable C users to give inline 2D and 3D array literals without having to use the
391 * VLA compound literal syntax. We expose these macros to C++ too for API consistency,
392 * although C++'s vector overloads achieve the same thing.
393 *
394 * These empower C and C++ users to call
395 * - setInlineSuperOp(m, 1, {{...}});
396 * - setInlineKrausMap(m, 2, 16, {{{...}}});
397 */
398
399
400#if defined(__cplusplus)
401
402 // C++ redirects to vector overloads, passing initialiser lists. The args like 'numQb'
403 // and 'numOps' are superfluous, but needed for consistency with the C API, so we additionally
404 // validate that they match the struct dimensions (which requires validating the structs).
405
406
407 /// @ingroup channels_setters
408 /// @notdoced
409 /// @cpponly
410 void setInlineKrausMap(KrausMap map, int numQb, int numOps, std::vector<std::vector<std::vector<qcomp>>> matrices);
411
412
413 /// @ingroup channels_setters
414 /// @notdoced
415 /// @cpponly
416 void setInlineSuperOp(SuperOp op, int numQb, std::vector<std::vector<qcomp>> matrix);
417
418
419#elif !defined(_MSC_VER)
420
421 // C defines macros which add compound literal syntax so that the user's passed lists
422 // become compile-time-sized temporary arrays. C99 does not permit inline-initialised
423 // VLAs, so we cannot have the macro expand to add (qcomp[matr.numRows][matr.numRows])
424 // in order to preclude passing 'numQb'. We ergo accept and validate 'numQb' macro param.
425 // We define private inner-functions of a macro, in lieu of writing multiline macros
426 // using do-while, just to better emulate a function call for users - e.g. they
427 // can wrap the macro invocations with another function call, etc.
428
429
430 // C validators check 'numQb' and 'numOps' are consistent with the struct, but cannot check the user's passed literal sizes
431 /// @private
432 extern void _validateParamsToSetInlineKrausMap(KrausMap map, int numQb, int numOps);
433 /// @private
434 extern void _validateParamsToSetInlineSuperOp(SuperOp op, int numQb);
435
436
437 /// @private
438 static inline void _setInlineKrausMap(KrausMap map, int numQb, int numOps, qcomp elems[numOps][1<<numQb][1<<numQb]) {
439 _validateParamsToSetInlineKrausMap(map, numQb, numOps);
440 _setKrausMapFromArr(map, elems);
441 }
442
443 /// @private
444 static inline void _setInlineSuperOp(SuperOp op, int numQb, qcomp elems[1<<(2*numQb)][1<<(2*numQb)] ) {
445 _validateParamsToSetInlineSuperOp(op, numQb);
446 _setSuperOpFromArr(op, elems);
447 }
448
449
450 /// @neverdoced
451 #define setInlineKrausMap(map, numQb, numOps, ...) \
452 _setInlineKrausMap((map), (numQb), (numOps), (qcomp[(numOps)][1<<(numQb)][1<<(numQb)]) __VA_ARGS__)
453
454
455 /// @neverdoced
456 #define setInlineSuperOp(matr, numQb, ...) \
457 _setInlineSuperOp((matr), (numQb), (qcomp[1<<(2*(numQb))][1<<(2*(numQb))]) __VA_ARGS__)
458
459 // spoofing macros as functions
460 #if 0
461
462 /// @ingroup channels_setters
463 /// @notdoced
464 /// @macrodoc
465 void setInlineKrausMap(KrausMap map, int numQb, int numOps, {{{ matrices }}});
466
467 /// @ingroup channels_setters
468 /// @notdoced
469 /// @macrodoc
470 void setInlineSuperOp(SuperOp op, int numQb, {{ matrix }});
471
472 #endif
473
474#else
475
476 // MSVC's C11 does not support C99 VLA, so the inner *FromArr() functions have not
477 // been defined, and ergo we cannot define setInlineKrausMap() nor setInlineSuperOp()
478
479#endif
480
481
482
483/*
484 * LITERAL CREATORS
485 *
486 * which combine creators and the inline initialisation functions, so that
487 * both C and C++ users can call e.g.
488 * - SuperOp op = createInlineSuperOp(2, {{...}});
489 */
490
491
492#if defined(__cplusplus)
493
494 // C++ accepts vector initialiser lists
495
496
497 /// @ingroup channels_create
498 /// @notdoced
499 /// @cpponly
500 KrausMap createInlineKrausMap(int numQubits, int numOperators, std::vector<std::vector<std::vector<qcomp>>> matrices);
501
502
503 /// @ingroup channels_create
504 /// @notdoced
505 /// @cpponly
506 SuperOp createInlineSuperOp(int numQubits, std::vector<std::vector<qcomp>> matrix);
507
508
509#elif !defined(_MSC_VER)
510
511 // C defines macros which add compound literal syntax so that the user's passed lists
512 // become compile-time-sized temporary arrays. We use bespoke validation so that the
513 // error messages reflect the name of the macro, rather than the inner called functions.
514 // We define a private inner function per macro, in lieu of writing multiline macros
515 // using do-while, just to better emulate a function call for users - e.g. they
516 // can wrap the macro invocation with another function call.
517
518
519 /// @private
520 extern void _validateParamsToCreateInlineKrausMap(int numQb, int numOps);
521 /// @private
522 extern void _validateParamsToCreateInlineSuperOp(int numQb);
523
524
525 /// @private
526 static inline KrausMap _createInlineKrausMap(int numQb, int numOps, qcomp matrices[numOps][1<<numQb][1<<numQb]) {
527 _validateParamsToCreateInlineKrausMap(numQb, numOps);
528 KrausMap out = createKrausMap(numQb, numOps); // malloc failures will report 'createKrausMap', rather than 'inline' version. Alas!
529 _setKrausMapFromArr(out, matrices);
530 return out;
531 }
532
533 /// @private
534 static inline SuperOp _createInlineSuperOp(int numQb, qcomp matrix[1<<numQb][1<<numQb]) {
535 _validateParamsToCreateInlineSuperOp(numQb);
536 SuperOp out = createSuperOp(numQb); // malloc failures will report 'createSuperOp', rather than 'inline' version. Alas!
537 _setSuperOpFromArr(out, matrix);
538 return out;
539 }
540
541
542 /// @neverdoced
543 #define createInlineKrausMap(numQb, numOps, ...) \
544 _createInlineKrausMap((numQb), (numOps), (qcomp[(numOps)][1<<(numQb)][1<<(numQb)]) __VA_ARGS__)
545
546 /// @neverdoced
547 #define createInlineSuperOp(numQb, ...) \
548 _createInlineSuperOp((numQb), (qcomp[1<<(2*(numQb))][1<<(2*(numQb))]) __VA_ARGS__)
549
550 // spoofing macros as functions
551 #if 0
552
553 /// @ingroup channels_create
554 /// @notdoced
555 /// @macrodoc
556 KrausMap createInlineKrausMap(int numQb, int numOps, {{{ matrices }}});
557
558 /// @ingroup channels_create
559 /// @notdoced
560 /// @macrodoc
561 SuperOp createInlineSuperOp(int numQb, {{ matrix }});
562
563 #endif
564
565#else
566
567 // MSVC's C11 does not support C99 VLA, so none of the necessary inner functions are defined,
568 // and ergo Windows C users cannot use createInlineKrausMap() nor createInlineSuperOp(). Tragic!
569
570#endif
571
572
573
574#endif // CHANNELS_H
575
576/** @} */ // (end file-wide doxygen defgroup)
KrausMap createInlineKrausMap(int numQubits, int numOperators, std::vector< std::vector< std::vector< qcomp > > > matrices)
SuperOp createInlineSuperOp(int numQubits, std::vector< std::vector< qcomp > > matrix)
KrausMap createKrausMap(int numQubits, int numOperators)
Definition channels.cpp:172
SuperOp createSuperOp(int numQubits)
Definition channels.cpp:158
void destroySuperOp(SuperOp op)
Definition channels.cpp:201
void destroyKrausMap(KrausMap map)
Definition channels.cpp:208
void reportKrausMap(KrausMap map)
Definition channels.cpp:465
void reportSuperOp(SuperOp op)
Definition channels.cpp:445
void setInlineSuperOp(SuperOp op, int numQb, std::vector< std::vector< qcomp > > matrix)
void setInlineKrausMap(KrausMap map, int numQb, int numOps, std::vector< std::vector< std::vector< qcomp > > > matrices)
void setSuperOp(SuperOp op, qcomp **matrix)
Definition channels.cpp:269
void setKrausMap(KrausMap map, qcomp ***matrices)
Definition channels.cpp:307
void syncSuperOp(SuperOp op)
Definition channels.cpp:221
void syncKrausMap(KrausMap map)
Definition channels.cpp:234