The Quantum Exact Simulation Toolkit v4.3.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/** A superoperator which acts upon both the ket and bra space of a
74 * linearised density matrix, which can represent more transformations
75 * than a KrausMap.
76 *
77 * An @f$n@f$-qubit superoperator is instantiated as a @f$2^{2n}\times 2^{2n}@f$
78 * complex matrix, and can only be applied upon density matrices of @f$n@f$ or
79 * more density matrices.
80 *
81 * Like all QuEST structs, a SuperOp is safe to copy, and ergo to pass to
82 * functions by-value, or be returned from them. However, its destructor
83 * must only ever be called upon one such copy.
84 *
85 * @myexample
86 *
87 * See [C](https://github.com/QuEST-Kit/QuEST/blob/main/examples/isolated/initialising_superoperators.c) and
88 * [C++](https://github.com/QuEST-Kit/QuEST/blob/main/examples/isolated/initialising_superoperators.cpp)
89 * examples of initialising SuperOp.
90 *
91 * @see
92 * - createSuperOp()
93 * - [createInlineSuperOp()](https://quest-kit.github.io/QuEST/group__channels__create.html#ga0ee76da0f63c68a2bf26c6dda973436d)
94 * - setSuperOp()
95 * - syncSuperOp()
96 * - reportSuperOp()
97 * - mixSuperOp()
98 * - destroySuperOp()
99 */
100typedef struct {
101
102 /** The number of qubits of the superoperator.
103 *
104 * This is _half_ the number of qubit substates of the linearised density
105 * matrix upon which the superoperator acts, but is consistent with the space
106 * acted upon by an equivalent a channel or unitary.
107 *
108 * The total memory costs of the superoperator scale exponentially with the
109 * number of qubits; an @f$n@f$-qubit superoperator contains @f$16^n@f$
110 * complex elements.
111 */
113
114 /** The dimension of the superoperator, which is a square matrix, and so
115 * equals both the number of rows and columns.
116 *
117 * Letting @f$n=@f$ #numQubits, then #numRows @f$=4^n@f$.
118 */
119 qindex numRows;
120
121 /** The 2D matrix elements of the operator, stored in CPU host memory.
122 *
123 * It is safest to modify this matrix through setSuperOp(), but direct modification
124 * is possible; the matrix element of the `r`-th row and `c`-th colum is stored
125 * at `cpuElems[r][c]`.
126 *
127 * > [!IMPORTANT]
128 * > It is _critical_ to call syncSuperOp() after direct modification of
129 * > #cpuElems in order to update persistent superoperator properties,
130 * > such as its data in GPU device memory (even when not running in
131 * > GPU-accelerated mode).
132 *
133 * The field #cpuElems merely aliases the 1D #cpuElemsFlat field, such that
134 * modifications of #cpuElems also updates #cpuElemsFlat.
135 *
136 * @see
137 * - syncSuperOp()
138 */
139 qcomp** cpuElems;
140
141 /** A 1D row-major form of #cpuElems.
142 *
143 * Modification of the superoperator matrix should be done through #cpuElems
144 * for mathematical clarity, though this 1D contiguous form may be convenient
145 * when performing copying.
146 *
147 * > [!IMPORTANT]
148 * > It is _critical_ to call syncSuperOp() after direct modification of
149 * > #cpuElemsFlat in order to update persistent superoperator properties,
150 * > such as its data in GPU device memory (even when not running in
151 * > GPU-accelerated mode).
152 *
153 * @see
154 * - syncSuperOp()
155 */
157
158 /** The elements of the superoperator matrix, stored in GPU device memory,
159 * in a 1D row-major form.
160 *
161 * This is a copy of #cpuElemsFlat consulted by QuEST's GPU backend and should
162 * _never_ be modified directly. Instead, it is updated by calling syncSuperOp()
163 * after modifying #cpuElems or #cpuElemsFlat, which is performed automatically by
164 * API functions like setSuperOp(). In this way, #gpuElemsFlat and #cpuElemsFlat
165 * should never be out of sync with one another.
166 *
167 * Within a GPU-enabled QuEST environment, every SuperOp allocates #gpuElemsFlat
168 * in GPU memory, even if never ultimately consulted from the GPU backend.
169 */
171
172 /** Whether the superoperator matrix elements were ever synchronised.
173 *
174 * This is a heap pointer to a persistent flag which is initially @c 0 at SuperOp creation,
175 * but which is permanently overwritten to @c 1 when synchronisation is performed, such as
176 * via syncSuperOp() or setSuperOp(). The flag indicates whether the superoperator matrix
177 * elements have been initialised (and when QuEST is GPU-accelerated, whether they have been
178 * copied to GPU device memory), and ergo whether it is valid to pass the SuperOp to a
179 * simulation function like mixSuperOp().
180 *
181 * Note this flag can only indicate whether the matrix has _ever_ been synced; it cannot be
182 * used to detect whether manual modification of #cpuElems made after an initial sync have been
183 * re-synced, as required for correct behaviour in GPU mode.
184 *
185 * @see
186 * - syncSuperOp()
187 */
189
190} SuperOp;
191
192
193/** A Kraus map which can act upon density matrices, and can describe any
194 * physical operation or channel.
195 *
196 * A @f$t@f$-operator @f$n@f$-qubit KrausMap is described by @f$t@f$ complex matrices,
197 * each of dimension @f$2^n\times 2^n@f$; the equivalent size of an @f$n@f$-qubit unitary.
198 * A KrausMap can be applied upon density matrices of @f$n@f$ or more qubits.
199 *
200 * Due to its internal representation as a SuperOp with matrix dimension
201 * @f$2^{2n}\times 2^{2n}@f$, the total memory costs of a KrausMap scale exponentially
202 * with the number of qubits as @f$16^n@f$.
203 *
204 * Like all QuEST structs, a KrausMap is safe to copy, and ergo to pass to
205 * functions by-value, or be returned from them. However, its destructor
206 * must only ever be called upon one such copy.
207 *
208 * @myexample
209 *
210 * See [C](https://github.com/QuEST-Kit/QuEST/blob/main/examples/isolated/initialising_krausmaps.c) and
211 * [C++](https://github.com/QuEST-Kit/QuEST/blob/main/examples/isolated/initialising_krausmaps.cpp)
212 * examples of initialising KrausMap.
213 *
214 * @see
215 * - createKrausMap()
216 * - [createInlineKrausMap()](https://quest-kit.github.io/QuEST/group__channels__create.html#gae9c49a6443896ef590ff1e4cfaa4912b)
217 * - setKrausMap()
218 * - syncKrausMap()
219 * - reportKrausMap()
220 * - mixKrausMap()
221 * - destroyKrausMap()
222 */
223typedef struct {
224
225 /** The number of qubits of the Kraus map.
226 *
227 * Letting @f$n=@f$ #numQubits, each Kraus operator in the map is a @f$2^n\times 2^n@f$
228 * complex matrix, and the full map is described by a @f$2^{2n}\times 2^{2n}@f$
229 * superoperator; a total of @f$16^n@f$ complex elements.
230 */
232
233 /** The number of Kraus operators in the map.
234 *
235 * For example, a KrausMap form of an amplitude damping channel would contain
236 * @c numMatrices=2, and a two-qubit depolarising channel would contain
237 * @c numMatrices=16.
238 */
240
241 /** The dimension of each Kraus operator, which is a square matrix, and so
242 * equivalent to the number of rows and columns.
243 *
244 * Letting @f$n=@f$ #numQubits, then #numRows @f$=2^n@f$.
245 */
246 qindex numRows;
247
248 /** A list of each Kraus oeprator's 2D matrix, stored in CPU host memory.
249 *
250 * It is safest to modify this matrix through setKrausMap(), but direct modification
251 * is possible; the matrix element of the `r`-th row and `c`-th colum of the `i`-th
252 * operator is stored at `matrices[i][r][c]`.
253 *
254 * > [!IMPORTANT]
255 * > It is _critical_ to call syncKrausMap() after direct modification of
256 * > #matrices in order to update persistent KrausMap properties,
257 * > such as its SuperOp data in GPU device memory (even when not running in
258 * > GPU-accelerated mode).
259 *
260 * Unlike other data structures (such as CompMatr), KrausMap has no GPU device memory
261 * copy of #matrices, since only the superoperator (stored within #superop) is
262 * needed on the device. In fact, #matrices is only used for convenient
263 * initialisation of a KrausMap, for validation of CPTP, and for printing by
264 * reportKrausMap().
265 */
266 qcomp*** matrices;
267
268 /** A superoperator representation of the KrausMap.
269 *
270 * This is computed from #matrices during syncKrausMap() (as automatically invoked
271 * by setKrausMap()), and is used by QuEST's simulation backend to effect the Kraus
272 * map upon a density matrix.
273 *
274 * Since this is the only field of KrausMap needing synchronisation, KrausMap
275 * itself lacks an explicit @c wasGpuSynced field, and instead uses that attached
276 * to #superop.
277 */
279
280 /** Whether the Kraus map is known to be (within validation epsilon tolerance)
281 * completely positive and trace preserving (@c =1), or known to be non-CPTP (@c =0),
282 * or whether it is unknown (@c =-1).
283 *
284 * This is a heap pointer to a persistent flag which is initially @c =-1 at KrausMap
285 * creation, and is only ever consulted and/or updated by input validation within
286 * mixKrausMap(), when such validation is enabled. Calling setKrausMap() or syncKrausMap()
287 * restores #isApproxCPTP to @c =-1.
288 *
289 * The property of being CPTP is measured approximately, with reference to the validation
290 * epsilon as modified with setQuESTValidationEpsilon(). The flag will never be updated from
291 * @c =-1 when validation is disabled. Like all epsilon-dependent fields, it is restored
292 * to @c =-1 automatically whenever setValidationEpsilon() or setValidationEpsilonToDefault()
293 * are called.
294 *
295 * To skip CPTP validation in mixKrausMap(), users can directly mutate this field to
296 * @c =1, although it is not advised.
297 *
298 * @see
299 * - setQuESTValidationEpsilon()
300 */
302
303} KrausMap;
304
305
306// we define no fixed-size versions (e.g. KrausMap1/2), unlike we did for CompMatr1/2
307// and DiagMatr1/2. This is because the 2-qubit superoperator is 256 elements big, and
308// seems inadvisably large to be passing-by-copy through the QuEST backend layers, and
309// would need explicit GPU memory allocation at each invocation of mixKrausMap2() (it
310// exceeds the max number of CUDA kernel args). Furthermore, repeatedly calling
311// createKrausMap2() would repeatedly invoke ~256*16 flops to compute te superoperator,
312// which may be an user-astonishing overhead (more astonishing than the API asymmetry).
313// Finally, computing the fixed-size superoperators must be in the header (to avoid
314// the issues of qcmop interoperability, just like for getCompMatr1) and could not call
315// an inner function which wouldn't be user-exposed; so we would end up redefining the
316// superoperator calculation THREE times!
317
318
319/** @} */
320
321
322
323// we define the remaining doc groups in advance, since their signatures are
324// more naturally grouped in an implementation-specific way below. Note the
325// above structs were not doc'd this way (which would be more consistent)
326// because it inexplicably causes Doxygen to duplicate their section at the
327// top-level under Channels (rather than under Structs). Bizarre! The order
328// of declaration below will match the order shown in the html doc.
329/**
330 * @defgroup channels_create Constructors
331 * @brief Functions for creating channel data structures.
332 *
333 * @defgroup channels_destroy Destructors
334 * @brief Functions for destroying existing channel data structures.
335 *
336 * @defgroup channels_reporters Reporters
337 * @brief Functions for printing channels.
338 *
339 * @defgroup channels_setters Setters
340 * @brief Functions for overwriting the elements of channels.
341 *
342 * @defgroup channels_sync Synchronisation
343 * @brief Functions for overwriting a channel's GPU (VRAM) memory with its CPU (RAM) contents.
344 * @details These functions are only necessary when the user wishes to manually modify the
345 * elements of a channel (in lieu of using the @ref channels_setters "Setters"), to
346 * thereafter synchronise the changes to the GPU copy of the channel. These functions
347 * have no effect when running without GPU-acceleration, but remain legal and harmless
348 * to call (to achieve platform agnosticism).
349 */
350
351
352
353/*
354 * BASIC FUNCTIONS
355 */
356
357
358// de-mangle so below are directly callable by C and C++ binary
359#ifdef __cplusplus
360extern "C" {
361#endif
362
363
364 /** @ingroup channels_create
365 *
366 * Creates an uninitialised Kraus map.
367 *
368 * The returned KrausMap contains @p numOperators Kraus operators, each of which
369 * spans @p numQubits many qubits. Before being passed to functions like
370 * reportKrausMap() and mixKrausMap(), its elements must be populated with
371 * setKrausMap() or setInlineKrausMap(), or directly modified through KrausMap::matrices,
372 * though any direct modification must be followed by a call to syncKrausMap().
373 *
374 * The returned KrausMap should be later destroyed with destroyKrausMap().
375 *
376 * > See [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.c)
377 * > or [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp)
378 * > examples of initialising a KrausMap.
379 *
380 * @param[in] numQubits the number of qubits acted upon by the Kraus map.
381 * @param[in] numOperators the number of Kraus operators in the map.
382 * @returns A new KrausMap instance.
383 * @throws @validationerror
384 * - if the QuEST environment is not initialised.
385 * - if @p numQubits or @p numOperators are invalid.
386 * - if the dimensions or memory requirements overflow.
387 * - if any memory allocation fails.
388 * @see
389 * - [createInlineKrausMap()](https://quest-kit.github.io/QuEST/group__channels__create.html#gae9c49a6443896ef590ff1e4cfaa4912b)
390 * - createSuperOp()
391 * - setKrausMap()
392 * - [setInlineKrausMap()](https://quest-kit.github.io/QuEST/group__channels__setters.html#ga3c60440fa9503c235e46d964bc58d3ec)
393 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.c) or
394 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp) examples
395 * @author Tyson Jones
396 */
397 KrausMap createKrausMap(int numQubits, int numOperators);
398
399
400 /** @ingroup channels_sync
401 *
402 * Updates the internal state of @p map, necessary after manually modifying KrausMap::matrices.
403 *
404 * @param[in,out] map the KrausMap to synchronise.
405 * @throws @validationerror
406 * - if @p map is uninitialised.
407 * @see
408 * - setKrausMap()
409 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.c) or
410 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp) examples
411 * @author Tyson Jones
412 */
413 void syncKrausMap(KrausMap map);
414
415
416 /** @ingroup channels_destroy
417 * Destroys a KrausMap, freeing its Kraus operators and internal SuperOp.
418 *
419 * Since @p map is passed by value, this function cannot nullify the caller's
420 * copy of the KrausMap fields. The caller must not use @p map after destruction.
421 *
422 * @param[in] map the KrausMap to destroy.
423 * @throws @validationerror
424 * - if @p map is uninitialised.
425 * @author Tyson Jones
426 */
427 void destroyKrausMap(KrausMap map);
428
429
430 /** @ingroup channels_reporters
431 * Prints a KrausMap.
432 *
433 * @myexample
434 *
435 * ```cpp
436 KrausMap map = createInlineKrausMap(1, 3, {
437 {{1,2},{3,4}},
438 {{5,5},{6,6}},
439 {{1i,2i},{-3i,-4i}}
440 });
441 reportKrausMap(map);
442 * ```
443 * ```text
444 KrausMap (1 qubit, 3 2x2 matrices, 1 4x4 superoperator, 528 bytes):
445 [matrix 0]
446 1 2
447 3 4
448 [matrix 1]
449 5 5
450 6 6
451 [matrix 2]
452 i 2i
453 -3i -4i
454 * ```
455 *
456 * @param[in] map the KrausMap to print.
457 * @throws @validationerror
458 * - if @p map is uninitialised.
459 * @author Tyson Jones
460 */
461 void reportKrausMap(KrausMap map);
462
463
464 /** @ingroup channels_create
465 *
466 * Creates an uninitialised superoperator.
467 *
468 * The returned SuperOp represents an arbitrary linear map on vectorised density
469 * matrices, spanning @p numQubits many ket-qubits and an equal number of bra-qubits.
470 * Before being passed to functions
471 * like reportSuperOp() and mixSuperOp(), its elements must be populated with
472 * setSuperOp() or setInlineSuperOp(), or directly modified through SuperOp::cpuElems,
473 * though direct modification must be followed by a call to syncSuperOp().
474 *
475 * The returned SuperOp should be later destroyed with destroySuperOp().
476 *
477 * > See [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.c)
478 * > or [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.cpp)
479 * > examples of initialising a SuperOp.
480 *
481 * @param[in] numQubits the number of qubits acted upon by the superoperator.
482 * @returns A new SuperOp instance.
483 * @throws @validationerror
484 * - if the QuEST environment is not initialised.
485 * - if @p numQubits is invalid.
486 * - if the dimensions or memory requirements overflow.
487 * - if any memory allocation fails.
488 * @see
489 * - createInlineSuperOp()
490 * - createKrausMap()
491 * - setSuperOp()
492 * - setInlineSuperOp()
493 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.c) or
494 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.cpp) examples
495 * @author Tyson Jones
496 */
497 SuperOp createSuperOp(int numQubits);
498
499
500 /** @ingroup channels_sync
501 *
502 * Updates the internal state of @p op, necessary after manually modifying SuperOp::cpuElems
503 * or SuperOp::cpuElemsFlat.
504 *
505 * @param[in,out] op the SuperOp to synchronise.
506 * @throws @validationerror
507 * - if @p op is uninitialised.
508 * @see
509 * - setSuperOp()
510 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.c) or
511 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.cpp) examples
512 * @author Tyson Jones
513 */
514 void syncSuperOp(SuperOp op);
515
516
517 /** @ingroup channels_destroy
518 * Destroys a SuperOp, freeing all its CPU and GPU memory.
519 *
520 * Since @p op is passed by value, this function cannot nullify the caller's
521 * copy of the SuperOp fields. The caller must not use @p op after destruction.
522 *
523 * @param[in] op the SuperOp to destroy.
524 * @throws @validationerror
525 * - if @p op is uninitialised.
526 * @author Tyson Jones
527 */
528 void destroySuperOp(SuperOp op);
529
530
531 /** @ingroup channels_reporters
532 * Prints a SuperOp.
533 *
534 * @myexample
535 *
536 * ```cpp
537 SuperOp op = createInlineSuperOp(1, {
538 {1,2,3,4},
539 {5,-(10E-2)*3.14i,7,8},
540 {9,10,11,12},
541 {13,14,15,16+1.23i}
542 });
543 reportSuperOp(op);
544 * ```
545 * ```text
546 SuperOp (1 qubit, 4x4 qcomps, 304 bytes):
547 1 2 3 4
548 5 -0.314i 7 8
549 9 10 11 12
550 13 14 15 16+1.23i
551 * ```
552 *
553 * @param[in] op the SuperOp to print.
554 * @throws @validationerror
555 * - if @p op is uninitialised.
556 * @author Tyson Jones
557 */
558 void reportSuperOp(SuperOp op);
559
560
561#ifdef __cplusplus
562}
563#endif
564
565
566
567/*
568 * POINTER INITIALISERS
569 *
570 * which permit users to pass heap and stack pointers in both C and C++, e.g.
571 * - qcomp** ptr = malloc(...); setSuperOp(m, ptr);
572 * - qcomp* ptrs[16]; setSuperOp(m, ptrs);
573 * - qcomp*** ptr = malloc(...); setKrausMap(m, ptr);
574 */
575
576
577// de-mangle so below are directly callable by C and C++ binary
578#ifdef __cplusplus
579extern "C" {
580#endif
581
582
583 /** @ingroup channels_setters
584 *
585 * Overwrites the Kraus operators of @p map.
586 *
587 * Argument @p matrices must be a list of KrausMap::numMatrices matrices, each of
588 * dimension KrausMap::numRows by KrausMap::numRows.
589 *
590 * This updates KrausMap::matrices and other internal properties.
591 *
592 * @param[in,out] map the KrausMap to overwrite.
593 * @param[in] matrices a 3D nested list of the above dimensions.
594 * @throws @validationerror
595 * - if @p map is uninitialised.
596 * @throws seg-fault
597 * - if @p matrices is not of the expected dimensions.
598 * @see
599 * - [setInlineKrausMap()](https://quest-kit.github.io/QuEST/group__channels__setters.html#ga3c60440fa9503c235e46d964bc58d3ec)
600 * - syncKrausMap()
601 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.c) or
602 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp) examples
603 * @author Tyson Jones
604 */
605 void setKrausMap(KrausMap map, qcomp*** matrices);
606
607
608 /** @ingroup channels_setters
609 *
610 * Overwrites the elements of @p op.
611 *
612 * This copies @p matrix into SuperOp::cpuElems and synchronises @p op to GPU memory
613 * when relevant.
614 *
615 * @param[in,out] op the SuperOp to overwrite.
616 * @param[in] matrix a SuperOp::numRows by SuperOp::numRows matrix of new elements.
617 * @throws @validationerror
618 * - if @p op is uninitialised.
619 * - if @p matrix is a null or invalid pointer.
620 * @see
621 * - setInlineSuperOp()
622 * - syncSuperOp()
623 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.c) or
624 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.cpp) examples
625 * @author Tyson Jones
626 */
627 void setSuperOp(SuperOp op, qcomp** matrix);
628
629
630#ifdef __cplusplus
631}
632#endif
633
634
635
636/*
637 * ARRAY, VECTOR, MATRIX INITIALISERS
638 *
639 * which define additional overloads for arrays, VLAs, vectors and vector initialisation lists.
640 * They permit C users to additionally call e.g.
641 * - qcomp arr[16][16]; setSuperOp(m, arr);
642 * - int n=16; qcomp arr[n][n]; setSuperOp(m, arr);
643 * - setKrausMap(m, (qcomp[5][16][16]) {{{...}}});
644 * - inline temporary VLA remains impossible even in C99, however
645 * and C++ users gain overloads:
646 * - int n=8; std::vector vec(n); setSuperOp(m, vec);
647 * - setKrausMap(m, {{{...}}} );
648 * An unintended but harmless side-effect is the exposure of functions setKrausMapFromArr(),
649 * setSuperOpFromArr(), validate_setCompMatrFromArr() and validate_setSuperOpFromArr to the user.
650 */
651
652
653#if defined(__cplusplus)
654
655 // C++ overloads to accept vectors, which also enables vector initialiser literals
656
657
658 /** @ingroup channels_setters
659 * @notyetdoced
660 * @cpponly
661 *
662 * @see
663 * - [setInlineKrausMap()](https://quest-kit.github.io/QuEST/group__channels__setters.html#ga3c60440fa9503c235e46d964bc58d3ec)
664 * - [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp) examples
665 */
666 void setKrausMap(KrausMap map, std::vector<std::vector<std::vector<qcomp>>> matrices);
667
668
669 /** @ingroup channels_setters
670 * @notyetdoced
671 * @cpponly
672 *
673 * @see
674 * - setInlineSuperOp()
675 * - [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.cpp) examples
676 */
677 void setSuperOp(SuperOp op, std::vector<std::vector<qcomp>> matrix);
678
679
680 // C++ cannot accept VLAs so does not define 2D array overloads
681
682#elif !defined(_MSC_VER)
683
684 // C first defines bespoke functions which accept C99 VLAs, which we have to define here in
685 // the header becauses the C++ source cannot use VLA, nor should we pass a 2D qcomp array
686 // directly between C and C++ binaries (due to limited interoperability)
687
688
689 // C must validate the struct fields before accessing passed 2D arrays to avoid seg-faults
690 /// @private
691 extern void _validateParamsToSetKrausMapFromArr(KrausMap map);
692 /// @private
693 extern void _validateParamsToSetSuperOpFromArr(SuperOp op);
694
695
696 /// @private
697 static inline void _setKrausMapFromArr(KrausMap map, qcomp matrices[map.numMatrices][map.numRows][map.numRows]) {
698 _validateParamsToSetKrausMapFromArr(map);
699
700 // create stack space for 2D collection of pointers, one to each input row
701 qcomp* rows[map.numMatrices][map.numRows];
702 qcomp** ptrs[map.numMatrices];
703
704 // copy decayed array pointers into stack
705 for (int n=0; n<map.numMatrices; n++) {
706 for (qindex r=0; r<map.numRows; r++)
707 rows[n][r] = matrices[n][r];
708 ptrs[n] = rows[n];
709 }
710
711 setKrausMap(map, ptrs); // validation gauranteed to pass
712 }
713
714 /// @private
715 static inline void _setSuperOpFromArr(SuperOp op, qcomp matrix[op.numRows][op.numRows]) {
716 _validateParamsToSetSuperOpFromArr(op);
717
718 // create stack space for pointers, one for each input row
719 qcomp* ptrs[op.numRows];
720
721 // copy decayed array pointers into stack
722 for (qindex r=0; r<op.numRows; r++)
723 ptrs[r] = matrix[r];
724
725 setSuperOp(op, ptrs); // validation gauranteed to pass
726 }
727
728
729 // C then overloads setKrausMap() to call the above VLA when given arrays, using C11 Generics.
730 // See the doc of getCompMatr1() in matrices.h for an explanation of Generic, and its nuances.
731
732 /// @neverdoced
733 #define setKrausMap(map, ...) \
734 _Generic((__VA_ARGS__), \
735 qcomp*** : setKrausMap, \
736 default : _setKrausMapFromArr \
737 )((map), (__VA_ARGS__))
738
739 /// @neverdoced
740 #define setSuperOp(op, ...) \
741 _Generic((__VA_ARGS__), \
742 qcomp** : setSuperOp, \
743 default : _setSuperOpFromArr \
744 )((op), (__VA_ARGS__))
745
746 // spoofing macros as functions
747 #if 0
748
749
750 /** @ingroup channels_setters
751 *
752 * Overwrites the Kraus operators of @p map from a @c C array.
753 *
754 * @macrodoc
755 *
756 * @conly
757 *
758 * This is a @c C convenience macro equivalent to setKrausMap().
759 *
760 * @myexample
761 *
762 * ```c
763 qcomp arr[2][4][4] = {
764 {
765 {1,2,3,4},
766 {5,6,7,8},
767 {9,8,7,6},
768 {5,4,3,2},
769 }, {
770 {1i,2i,3i},
771 {5i}
772 }
773 };
774 KrausMap map = createKrausMap(2, 2);
775 setKrausMap(map, arr);
776 * ```
777 *
778 * @param[in,out] map the KrausMap to overwrite.
779 * @param[in] matrices a 3D array.
780 * @throws @validationerror
781 * - if @p map is uninitialised.
782 * @see
783 * - [setInlineKrausMap()](https://quest-kit.github.io/QuEST/group__channels__setters.html#ga3c60440fa9503c235e46d964bc58d3ec)
784 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.c) examples
785 * @author Tyson Jones
786 */
787 void setKrausMap(KrausMap map, qcomp matrices[map.numMatrices][map.numRows][map.numRows]);
788
789
790 /** @ingroup channels_setters
791 *
792 * Overwrites the elements of @p op from a @c C array.
793 *
794 * @macrodoc
795 *
796 * @conly
797 *
798 * This is a @c C convenience macro equivalent to setSuperOp().
799 *
800 * @myexample
801 *
802 * ```c
803 qcomp arr[4][4] = {
804 {1,2,3,4},
805 {5,6,7,8},
806 {9,8,7,6},
807 {5,4,3,2}
808 };
809 SuperOp a = createSuperOp(1);
810 setSuperOp(a, arr);
811 * ```
812 *
813 * @param[in,out] op the SuperOp to overwrite.
814 * @param[in] matrix a SuperOp::numRows by SuperOp::numRows array.
815 * @throws @validationerror
816 * - if @p op is uninitialised.
817 * @see
818 * - setSuperOp()
819 * - setInlineSuperOp()
820 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.c) examples
821 * @author Tyson Jones
822 */
823 void setSuperOp(SuperOp op, qcomp matrix[op.numRows][op.numRows]);
824
825
826 #endif
827
828
829#else
830
831 // MSVC's C11 does not support C99 VLAs, so there is no way to support _setKrausMapFromArr(),
832 // and ergo no need for setKrausMap() or setSuperOp() wrappers. This sadly means MSVC C users
833 // can only use the existing functions which accept qcomp*** and qcomp** respectively.
834
835#endif
836
837
838
839/*
840 * LITERAL INITIALISERS
841 *
842 * which enable C users to give inline 2D and 3D array literals without having to use the
843 * VLA compound literal syntax. We expose these macros to C++ too for API consistency,
844 * although C++'s vector overloads achieve the same thing.
845 *
846 * These empower C and C++ users to call
847 * - setInlineSuperOp(m, 1, {{...}});
848 * - setInlineKrausMap(m, 2, 16, {{{...}}});
849 */
850
851
852#if defined(__cplusplus)
853
854 // C++ redirects to vector overloads, passing initialiser lists. The args like 'numQb'
855 // and 'numOps' are superfluous, but needed for consistency with the C API, so we additionally
856 // validate that they match the struct dimensions (which requires validating the structs).
857
858
859 /** @ingroup channels_setters
860 * @notyetdoced
861 * @cpponly
862 *
863 * @see
864 * - setKrausMap()
865 * - syncKrausMap()
866 * - [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp) examples
867 */
868 void setInlineKrausMap(KrausMap map, int numQb, int numOps, std::vector<std::vector<std::vector<qcomp>>> matrices);
869
870
871 /** @ingroup channels_setters
872 * @notyetdoced
873 * @cpponly
874 *
875 * @see
876 * - setSuperOp()
877 * - syncSuperOp()
878 * - [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.cpp) examples
879 */
880 void setInlineSuperOp(SuperOp op, int numQb, std::vector<std::vector<qcomp>> matrix);
881
882
883#elif !defined(_MSC_VER)
884
885 // C defines macros which add compound literal syntax so that the user's passed lists
886 // become compile-time-sized temporary arrays. C99 does not permit inline-initialised
887 // VLAs, so we cannot have the macro expand to add (qcomp[matr.numRows][matr.numRows])
888 // in order to preclude passing 'numQb'. We ergo accept and validate 'numQb' macro param.
889 // We define private inner-functions of a macro, in lieu of writing multiline macros
890 // using do-while, just to better emulate a function call for users - e.g. they
891 // can wrap the macro invocations with another function call, etc.
892
893
894 // C validators check 'numQb' and 'numOps' are consistent with the struct, but cannot check the user's passed literal sizes
895 /// @private
896 extern void _validateParamsToSetInlineKrausMap(KrausMap map, int numQb, int numOps);
897 /// @private
898 extern void _validateParamsToSetInlineSuperOp(SuperOp op, int numQb);
899
900
901 /// @private
902 static inline void _setInlineKrausMap(KrausMap map, int numQb, int numOps, qcomp elems[numOps][1<<numQb][1<<numQb]) {
903 _validateParamsToSetInlineKrausMap(map, numQb, numOps);
904 _setKrausMapFromArr(map, elems);
905 }
906
907 /// @private
908 static inline void _setInlineSuperOp(SuperOp op, int numQb, qcomp elems[1<<(2*numQb)][1<<(2*numQb)] ) {
909 _validateParamsToSetInlineSuperOp(op, numQb);
910 _setSuperOpFromArr(op, elems);
911 }
912
913
914 /// @neverdoced
915 #define setInlineKrausMap(map, numQb, numOps, ...) \
916 _setInlineKrausMap((map), (numQb), (numOps), (qcomp[(numOps)][1<<(numQb)][1<<(numQb)]) __VA_ARGS__)
917
918 /// @neverdoced
919 #define setInlineSuperOp(matr, numQb, ...) \
920 _setInlineSuperOp((matr), (numQb), (qcomp[1<<(2*(numQb))][1<<(2*(numQb))]) __VA_ARGS__)
921
922 // spoofing macros as functions
923 #if 0
924
925
926 /** @ingroup channels_setters
927 *
928 * Overwrites the Kraus operators of @p map from an inline literal.
929 *
930 * The @c {{{matrices}}} argument is a 3D array literal, of dimensions
931 * @c numOps by @c 1<<numQb by @c 1<<numQb.
932 *
933 * - In @c C, this is a macro, where @p numQb and @p numOps must be
934 * compile-time literals, and turn @c {{{matrices}}} into a compound literal.
935 * - In @c C++, this is a function which accepts @c {{{matrices}}} as a nested @c std::vector literal.
936 *
937 * @myexample
938 *
939 * ```c
940 KrausMap map = createKrausMap(1, 3);
941 setInlineKrausMap(map, 1, 3, {
942 {{1,2},{3,4}},
943 {{5,5},{6,6}},
944 {{1i,2i},{-3i,-4i}}
945 });
946 * ```
947 *
948 * @param[in,out] map the KrausMap to overwrite.
949 * @param[in] numQb the number of qubits of the literal @c {{{matrices}}}.
950 * @param[in] numOps the number of Kraus operators of the literal @c {{{matrices}}}.
951 * @throws @validationerror
952 * - if @p map is uninitialised.
953 * - if @p numQb differs from the number of qubits in @p map.
954 * - if @p numOps differs from the number of operators in @p map.
955 * @see
956 * - setKrausMap()
957 * - syncKrausMap()
958 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.c) and
959 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp) examples
960 * @author Tyson Jones
961 */
962 void setInlineKrausMap(KrausMap map, int numQb, int numOps, {{{ matrices }}});
963
964
965 /** @ingroup channels_setters
966 *
967 * Overwrites the elements of @p op from an inline literal.
968 *
969 * The @c {{matrix}} argument is a 2D array literal, of dimensions
970 * `1<<(2*numQb)` by `1<<(2*numQb)`.
971 *
972 * - In @c C, this is a macro, where @p numQb must be a compile-time literal,
973 * amd turns @c {{matrix}} into a compound literal.
974 * - In @c C++, this is a function which accepts @c {{matrix}} as a nested @c std::vector literal.
975 *
976 * @myexample
977 *
978 * ```c
979 SuperOp a = createSuperOp(1);
980 setInlineSuperOp(a, 1, {
981 {1,2,3,4},
982 {5,3.14i,7,8},
983 {9,10,11,12},
984 {13,14,15,16+1.23i}
985 });
986 * ```
987 *
988 * @param[in,out] op the SuperOp to overwrite.
989 * @param[in] numQb the number of qubits of the @c {{matrix}} literal.
990 * @throws @validationerror
991 * - if @p op is uninitialised.
992 * - if @p numQb does not match the number of qubits in @p op.
993 * @see
994 * - setSuperOp()
995 * - syncSuperOp()
996 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.c) examples
997 * @author Tyson Jones
998 */
999 void setInlineSuperOp(SuperOp op, int numQb, {{ matrix }});
1000
1001
1002 #endif
1003
1004#else
1005
1006 // MSVC's C11 does not support C99 VLA, so the inner *FromArr() functions have not
1007 // been defined, and ergo we cannot define setInlineKrausMap() nor setInlineSuperOp()
1008
1009#endif
1010
1011
1012
1013/*
1014 * LITERAL CREATORS
1015 *
1016 * which combine creators and the inline initialisation functions, so that
1017 * both C and C++ users can call e.g.
1018 * - SuperOp op = createInlineSuperOp(2, {{...}});
1019 */
1020
1021
1022#if defined(__cplusplus)
1023
1024 // C++ accepts vector initialiser lists
1025
1026
1027 /** @ingroup channels_create
1028 * @notyetdoced
1029 * @cpponly
1030 *
1031 * @see
1032 * - createKrausMap()
1033 * - setKrausMap()
1034 * - syncKrausMap()
1035 * - [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp) examples
1036 */
1037 KrausMap createInlineKrausMap(int numQubits, int numOperators, std::vector<std::vector<std::vector<qcomp>>> matrices);
1038
1039
1040 /** @ingroup channels_create
1041 * @notyetdoced
1042 * @cpponly
1043 *
1044 * @see
1045 * - createSuperOp()
1046 * - setSuperOp()
1047 * - syncSuperOp()
1048 * - [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.cpp) examples
1049 */
1050 SuperOp createInlineSuperOp(int numQubits, std::vector<std::vector<qcomp>> matrix);
1051
1052
1053#elif !defined(_MSC_VER)
1054
1055 // C defines macros which add compound literal syntax so that the user's passed lists
1056 // become compile-time-sized temporary arrays. We use bespoke validation so that the
1057 // error messages reflect the name of the macro, rather than the inner called functions.
1058 // We define a private inner function per macro, in lieu of writing multiline macros
1059 // using do-while, just to better emulate a function call for users - e.g. they
1060 // can wrap the macro invocation with another function call.
1061
1062
1063 /// @private
1064 extern void _validateParamsToCreateInlineKrausMap(int numQb, int numOps);
1065 /// @private
1066 extern void _validateParamsToCreateInlineSuperOp(int numQb);
1067
1068
1069 /// @private
1070 static inline KrausMap _createInlineKrausMap(int numQb, int numOps, qcomp matrices[numOps][1<<numQb][1<<numQb]) {
1071 _validateParamsToCreateInlineKrausMap(numQb, numOps);
1072 KrausMap out = createKrausMap(numQb, numOps); // malloc failures will report 'createKrausMap', rather than 'inline' version. Alas!
1073 _setKrausMapFromArr(out, matrices);
1074 return out;
1075 }
1076
1077 /// @private
1078 static inline SuperOp _createInlineSuperOp(int numQb, qcomp matrix[1<<numQb][1<<numQb]) {
1079 _validateParamsToCreateInlineSuperOp(numQb);
1080 SuperOp out = createSuperOp(numQb); // malloc failures will report 'createSuperOp', rather than 'inline' version. Alas!
1081 _setSuperOpFromArr(out, matrix);
1082 return out;
1083 }
1084
1085
1086 /// @neverdoced
1087 #define createInlineKrausMap(numQb, numOps, ...) \
1088 _createInlineKrausMap((numQb), (numOps), (qcomp[(numOps)][1<<(numQb)][1<<(numQb)]) __VA_ARGS__)
1089
1090 /// @neverdoced
1091 #define createInlineSuperOp(numQb, ...) \
1092 _createInlineSuperOp((numQb), (qcomp[1<<(2*(numQb))][1<<(2*(numQb))]) __VA_ARGS__)
1093
1094 // spoofing macros as functions
1095 #if 0
1096
1097
1098 /** @ingroup channels_create
1099 *
1100 * Creates and initialises a KrausMap from an inline literal.
1101 *
1102 * This is a convenience macro which combines createKausMap() and setInlineKrausMap().
1103 *
1104 * The @c {{{matrices}}} argument is a 3D array literal, of dimensions
1105 * @c numOps by @c 1<<numQb by @c 1<<numQb.
1106 *
1107 * - In @c C, this is a macro, where @p numQb and @p numOps must be
1108 * compile-time literals, and turn @c {{{matrices}}} into a compound literal.
1109 * - In @c C++, this is a function which accepts @c {{{matrices}}} as a nested @c std::vector literal.
1110 *
1111 * The returned KrausMap should be later destroyed with destroyKrausMap().
1112 *
1113 * @myexample
1114 *
1115 * ```cpp
1116 KrausMap map = createInlineKrausMap(1, 3, {
1117 {{1,2},{3,4}},
1118 {{5,5},{6,6}},
1119 {{1i,2i},{-3i,-4i}}
1120 });
1121 * ```
1122 *
1123 * @param[in] numQb the number of qubits acted upon by the Kraus map.
1124 * @param[in] numOps the number of Kraus operators.
1125 * @returns A new KrausMap initialised with @p matrices.
1126 * @throws @validationerror
1127 * - if the QuEST environment is not initialised.
1128 * - if @p numQb or @p numOps are invalid.
1129 * - if dimensions or memory requirements overflow.
1130 * - if any memory allocation fails.
1131 * @see
1132 * - createKrausMap()
1133 * - setKrausMap()
1134 * - syncKrausMap()
1135 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.c) and
1136 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_krausmaps.cpp) examples
1137 * @author Tyson Jones
1138 */
1139 KrausMap createInlineKrausMap(int numQb, int numOps, {{{ matrices }}});
1140
1141
1142 /** @ingroup channels_create
1143 *
1144 * Creates and initialises a SuperOp from an inline literal.
1145 *
1146 * This is a convenience macro which combines createSuperOp() and setInlineSuperOp().
1147 *
1148 * The @c {{matrix}} argument is a 2D array literal, of dimensions
1149 * `1<<(2*numQb)` by `1<<(2*numQb)`.
1150 *
1151 * - In @c C, this is a macro, where @p numQb must be a compile-time literal,
1152 * amd turns @c {{matrix}} into a compound literal.
1153 * - In @c C++, this is a function which accepts @c {{matrix}} as a nested @c std::vector literal.
1154 *
1155 * The returned SuperOp should be later destroyed with destroySuperOp().
1156 *
1157 * @myexample
1158 *
1159 * ```cpp
1160 SuperOp op = createInlineSuperOp(1, {
1161 {1,2,3,4},
1162 {5,6*3.14i,7,8},
1163 {9,10,11,12},
1164 {13,14,15,16+1.23i}
1165 });
1166 * ```
1167 *
1168 * @param[in] numQb the number of qubits acted upon by the superoperator.
1169 * @returns A new SuperOp initialised with @p matrix.
1170 * @throws @validationerror
1171 * - if the QuEST environment is not initialised.
1172 * - if @p numQb is invalid.
1173 * - if dimensions or memory requirements overflow.
1174 * - if any memory allocation fails.
1175 * @see
1176 * - createSuperOp()
1177 * - setSuperOp()
1178 * - syncSuperOp()
1179 * - [C](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.c) and
1180 * [C++](https://github.com/QuEST-Kit/QuEST/blob/devel/examples/isolated/initialising_superoperators.cpp) examples
1181 * @author Tyson Jones
1182 */
1183 SuperOp createInlineSuperOp(int numQb, {{ matrix }});
1184
1185
1186 #endif
1187
1188#else
1189
1190 // MSVC's C11 does not support C99 VLA, so none of the necessary inner functions are defined,
1191 // and ergo Windows C users cannot use createInlineKrausMap() nor createInlineSuperOp(). Tragic!
1192
1193#endif
1194
1195
1196
1197#endif // CHANNELS_H
1198
1199/** @} */ // (end file-wide doxygen defgroup)
KrausMap createKrausMap(int numQubits, int numOperators)
Definition channels.cpp:176
SuperOp createSuperOp(int numQubits)
Definition channels.cpp:162
void destroySuperOp(SuperOp op)
Definition channels.cpp:203
void destroyKrausMap(KrausMap map)
Definition channels.cpp:210
void reportKrausMap(KrausMap map)
Definition channels.cpp:471
void reportSuperOp(SuperOp op)
Definition channels.cpp:447
void setSuperOp(SuperOp op, qcomp **matrix)
Definition channels.cpp:271
void setKrausMap(KrausMap map, qcomp ***matrices)
Definition channels.cpp:309
void syncSuperOp(SuperOp op)
Definition channels.cpp:223
void syncKrausMap(KrausMap map)
Definition channels.cpp:236
SuperOp superop
Definition channels.h:278
qcomp *** matrices
Definition channels.h:266
qindex numRows
Definition channels.h:246
int numQubits
Definition channels.h:231
int * isApproxCPTP
Definition channels.h:301
int numMatrices
Definition channels.h:239
int * wasGpuSynced
Definition channels.h:188
int numQubits
Definition channels.h:112
qcomp * cpuElemsFlat
Definition channels.h:156
qcomp ** cpuElems
Definition channels.h:139
qindex numRows
Definition channels.h:119
qcomp * gpuElemsFlat
Definition channels.h:170