The Quantum Exact Simulation Toolkit v4.1.0
Loading...
Searching...
No Matches
channels.cpp
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 * @author Tyson Jones
8 */
9
10#include "quest/include/channels.h"
11#include "quest/include/types.h"
12
13#include "quest/src/core/bitwise.hpp"
14#include "quest/src/core/memory.hpp"
15#include "quest/src/core/printer.hpp"
16#include "quest/src/core/utilities.hpp"
17#include "quest/src/core/validation.hpp"
18#include "quest/src/comm/comm_config.hpp"
19#include "quest/src/comm/comm_routines.hpp"
20#include "quest/src/cpu/cpu_config.hpp"
21#include "quest/src/gpu/gpu_config.hpp"
22
23#include <vector>
24#include <cstdlib>
25
26using std::vector;
27
28
29
30/*
31 * PRIVATE UTILITIES
32 */
33
34
35void freeSuperOp(SuperOp op) {
36
37 // free CPU memory, even if it is nullptr
38 cpu_deallocArray(op.cpuElemsFlat);
39 cpu_deallocMatrixWrapper(op.cpuElems);
40
41 // free teeniy-tiny heap flag
42 cpu_deallocHeapFlag(op.wasGpuSynced);
43
44 // we avoid invoking a GPU function in non-GPU mode
45 auto gpuPtr = util_getGpuMemPtr(op);
46 if (mem_isAllocated(gpuPtr))
47 gpu_deallocArray(gpuPtr);
48}
49
50
51void freeKrausMap(KrausMap map) {
52
53 // free CPU matrices of Kraus operators
54 cpu_deallocMatrixList(map.matrices, map.numRows, map.numMatrices);
55
56 // free teeny-tiny heap flag
57 util_deallocEpsilonSensitiveHeapFlag(map.isApproxCPTP);
58
59 // free superoperator (which may include freeing GPU memory)
60 freeSuperOp(map.superop);
61}
62
63
64void freeObj(SuperOp& op) {
65 freeSuperOp(op);
66}
67void freeObj(KrausMap& map) {
68 freeKrausMap(map);
69
70 // must overwrite any nested pointer structure to have a null
71 // outer pointer, so that post-alloc validationw won't seg-fault
72 map.matrices = nullptr;
73}
74
75
76bool didAnyLocalAllocsFail(SuperOp op) {
77
78 if (!mem_isAllocated(op.wasGpuSynced)) return true;
79 if (!mem_isAllocated(op.cpuElemsFlat)) return true;
80 if (!mem_isOuterAllocated(op.cpuElems)) return true;
81
82 if (getQuESTEnv().isGpuAccelerated && !mem_isAllocated(op.gpuElemsFlat))
83 return true;
84
85 return false;
86}
87
88
89bool didAnyLocalAllocsFail(KrausMap map) {
90
91 if (!mem_isAllocated(map.isApproxCPTP))
92 return true;
93
94 if (!mem_isAllocated(map.matrices, map.numRows, map.numMatrices))
95 return true;
96
97 if (didAnyLocalAllocsFail(map.superop))
98 return true;
99
100 return false;
101}
102
103
104// T will be SuperOp or KrausMap (passed by reference, so modifiable)
105template <typename T>
106void freeAllMemoryIfAnyAllocsFailed(T& obj) {
107
108 // determine whether any node experienced a failure
109 bool anyFail = didAnyLocalAllocsFail(obj);
110 if (comm_isInit())
111 anyFail = comm_isTrueOnAllNodes(anyFail);
112
113 // if so, free all memory before subsequent validation
114 if (anyFail)
115 freeObj(obj); // may modify obj
116}
117
118
119
120/*
121 * CONSTRUCTORS
122 */
123
124
125SuperOp allocSuperOp(int numQubits) {
126
127 // this inner function will NOT validate, verify whether
128 // allocations succeeded, nor attempt to free memory if not
129
130 // prior validation ensures these never overflow
131 qindex numRows = powerOf2(2 * numQubits);
132 qindex numElems = numRows * numRows;
133
134 // attempt top allocate 1D memory
135 qcomp* cpuMem = cpu_allocArray(numElems); // nullptr if failed
136 qcomp* gpuMem = nullptr;
137 if (getQuESTEnv().isGpuAccelerated)
138 gpuMem = gpu_allocArray(numElems); // nullptr if failed
139
140 // prepare output SuperOp (avoiding C++20 designated initialiser)
141 SuperOp out;
142 out.numQubits = numQubits;
143 out.numRows = numRows;
144
145 // attemptedly allocate 2D alias for 1D CPU memory
146 out.cpuElems = cpu_allocAndInitMatrixWrapper(cpuMem, numRows); // nullptr if failed
147 out.cpuElemsFlat = cpuMem;
148 out.gpuElemsFlat = gpuMem;
149
150 // attemptedly allocate (un-initialised) flags in the heap so that struct copies are mutable
151 out.wasGpuSynced = cpu_allocHeapFlag(); // nullptr if failed
152
153 // if heap flag allocated, mark it as unsynced (caller will handle if allocation failed)
154 if (mem_isAllocated(out.wasGpuSynced))
155 *(out.wasGpuSynced) = 0;
156
157 // caller will handle if any above allocations failed
158 return out;
159}
160
161
162extern "C" SuperOp createSuperOp(int numQubits) {
163 validate_envIsInit(__func__);
164 validate_newSuperOpParams(numQubits, __func__);
165
166 SuperOp out = allocSuperOp(numQubits); // fields may be or contain nullptr if failed
167
168 // free all memory before we throw validation errors to avoid leaks
169 freeAllMemoryIfAnyAllocsFailed(out);
170 validate_newSuperOpAllocs(out, __func__);
171
172 return out;
173}
174
175
176extern "C" KrausMap createKrausMap(int numQubits, int numOperators) {
177 validate_envIsInit(__func__);
178 validate_newKrausMapParams(numQubits, numOperators, __func__);
179
180 // validation ensures this never overflows
181 qindex numRows = powerOf2(numQubits);
182
183 // attempt to allocate output KrausMap fields (avoiding C++20 designated initialiser)
184 KrausMap out;
185 out.numQubits = numQubits,
186 out.numMatrices = numOperators,
187 out.numRows = numRows,
188 out.matrices = cpu_allocMatrixList(numRows, numOperators); // is or contains nullptr if failed
189 out.superop = allocSuperOp(numQubits); // heap fields are or contain nullptr if failed
190 out.isApproxCPTP = util_allocEpsilonSensitiveHeapFlag(); // nullptr if failed
191
192 // free memory before throwing validation error to avoid memory leaks
193 freeAllMemoryIfAnyAllocsFailed(out); // sets out.matrices=nullptr if failed
194 validate_newKrausMapAllocs(out, __func__);
195
196 // mark CPTP as unknown; it will be lazily evaluated whene a function asserts CPTP-ness
197 util_setFlagToUnknown(out.isApproxCPTP);
198
199 return out;
200}
201
202
203extern "C" void destroySuperOp(SuperOp op) {
204 validate_superOpFields(op, __func__);
205
206 freeSuperOp(op);
207}
208
209
210extern "C" void destroyKrausMap(KrausMap map) {
211 validate_krausMapFields(map, __func__);
212
213 freeKrausMap(map);
214}
215
216
217
218/*
219 * SYNC
220 */
221
222
223extern "C" void syncSuperOp(SuperOp op) {
224 validate_superOpFields(op, __func__);
225
226 // optionally overwrite GPU elements with user-modified CPU elements
227 if (mem_isAllocated(util_getGpuMemPtr(op)))
228 gpu_copyCpuToGpu(op);
229
230 // indicate that the matrix is now permanently GPU synchronised, even
231 // if we are not in GPU-accelerated mode - it hardly matters
232 *(op.wasGpuSynced) = 1;
233}
234
235
236extern "C" void syncKrausMap(KrausMap map) {
237 validate_krausMapFields(map, __func__);
238
239 // re-compute and GPU-sync the superoperator from the modified matrices
240 util_setSuperoperator(map.superop.cpuElems, map.matrices, map.numMatrices, map.numQubits);
241 syncSuperOp(map.superop);
242
243 // indicate that we do not know whether the revised map is
244 // is CPTP; we defer establishing that until a CPTP check
245 util_setFlagToUnknown(map.isApproxCPTP);
246}
247
248
249
250/*
251 * SUPEROPERATOR SETTERS
252 *
253 * defining the C and C++ compatible pointer version,
254 * and the C++-only vector overloads. Note the C-only
255 * VLA overloads are defined in the header
256 */
257
258
259// T can be qcomp** or vector<vector<qcomp>>
260template <typename T>
261void setAndSyncSuperOpElems(SuperOp op, T matrix) {
262
263 // overwrite the CPU matrix
264 cpu_copyMatrix(op.cpuElems, matrix, op.numRows);
265
266 // sync CPU matrix to flat GPU array
267 syncSuperOp(op);
268}
269
270
271extern "C" void setSuperOp(SuperOp op, qcomp** matrix) {
272 validate_superOpFields(op, __func__);
273 validate_matrixNewElemsPtrNotNull(matrix, op.numRows, __func__); // fine for superop
274
275 setAndSyncSuperOpElems(op, matrix);
276}
277
278void setSuperOp(SuperOp op, vector<vector<qcomp>> matrix) {
279 validate_superOpFields(op, __func__);
280 validate_superOpNewMatrixDims(op, matrix, __func__);
281
282 setAndSyncSuperOpElems(op, matrix);
283}
284
285
286
287/*
288 * KRAUS MAP SETTERS
289 *
290 * defining the C and C++ compatible pointer version,
291 * and the C++-only vector overloads. Note the C-only
292 * VLA overloads are defined in the header
293 */
294
295
296// type T can be qcomp*** or vector<vector<vector<qcomp>>>
297template <typename T>
298void setAndSyncKrausMapElems(KrausMap map, T matrices) {
299
300 // copy over the given matrices into the map's CPU mmemory
301 for (int n=0; n<map.numMatrices; n++)
302 cpu_copyMatrix(map.matrices[n], matrices[n], map.numRows);
303
304 // update the superoperator, including its GPU memory, and its isApproxCPTP flag
305 syncKrausMap(map);
306}
307
308
309extern "C" void setKrausMap(KrausMap map, qcomp*** matrices) {
310 validate_krausMapFields(map, __func__);
311
312 setAndSyncKrausMapElems(map, matrices);
313}
314
315
316void setKrausMap(KrausMap map, vector<vector<vector<qcomp>>> matrices) {
317 validate_krausMapFields(map, __func__);
318 validate_krausMapNewMatrixDims(map, matrices, __func__);
319
320 setAndSyncKrausMapElems(map, matrices);
321}
322
323
324
325/*
326 * LITERAL SETTERS
327 *
328 * Only the C++ versions are defined here, because the C versions are macros
329 * defined in the header. Note the C++ versions themselves are entirely
330 * superfluous and merely call the above vector setters, but we still define
331 * them for API consistency, and we additionally validate the superfluous
332 * additional parameters they pass.
333 */
334
335
336void setInlineKrausMap(KrausMap map, int numQb, int numOps, vector<vector<vector<qcomp>>> matrices) {
337 validate_krausMapFields(map, __func__);
338 validate_krausMapFieldsMatchPassedParams(map, numQb, numOps, __func__);
339 validate_krausMapNewMatrixDims(map, matrices, __func__);
340
341 setAndSyncKrausMapElems(map, matrices);
342}
343
344
345void setInlineSuperOp(SuperOp op, int numQb, vector<vector<qcomp>> matrix) {
346 validate_superOpFields(op, __func__);
347 validate_superOpFieldsMatchPassedParams(op, numQb, __func__);
348 validate_superOpNewMatrixDims(op, matrix, __func__);
349
350 setAndSyncSuperOpElems(op, matrix);
351}
352
353
354
355/*
356 * LITERAL CREATORS
357 *
358 * Only the C++ versions are defined here; the C versions are in-header macros.
359 */
360
361
362KrausMap createInlineKrausMap(int numQubits, int numOperators, vector<vector<vector<qcomp>>> matrices) {
363 validate_envIsInit(__func__);
364 validate_newKrausMapParams(numQubits, numOperators, __func__);
365 validate_newInlineKrausMapDimMatchesVectors(numQubits, numOperators, matrices, __func__);
366
367 // pre-validation gauranteed to pass, but malloc failures will trigger an error
368 // message specific to 'createKrausMap', rather than this 'inline' version. Alas!
369 KrausMap map = createKrausMap(numQubits, numOperators);
370 setAndSyncKrausMapElems(map, matrices);
371 return map;
372}
373
374
375SuperOp createInlineSuperOp(int numQubits, vector<vector<qcomp>> matrix) {
376 validate_envIsInit(__func__);
377 validate_newSuperOpParams(numQubits, __func__);
378 validate_newInlineSuperOpDimMatchesVectors(numQubits, matrix, __func__);
379
380 // pre-validation gauranteed to pass, but malloc failures will trigger an error
381 // message specific to 'createSuperOp', rather than this 'inline' version. Alas!
382 SuperOp op = createSuperOp(numQubits);
383 setAndSyncSuperOpElems(op, matrix);
384 return op;
385}
386
387
388
389/*
390 * EXPOSING SOME SETTER VALIDATION TO HEADER
391 *
392 * Some setters are necessarily defined in the header, because they accept
393 * C-only VLAs which need to be cast into pointers before being passed to
394 * this C++ backend (which does not support VLA). These setters need their
395 * validators exposed, though we cannot expose the entirety of validation.hpp
396 * because it cannot be parsed by C; so we here wrap specific functions.
397 */
398
399
400extern "C" {
401
402 void _validateParamsToSetKrausMapFromArr(KrausMap map) {
403 validate_krausMapFields(map, "setKrausMap");
404 }
405
406 void _validateParamsToSetSuperOpFromArr(SuperOp op) {
407 validate_superOpFields(op, "setSuperOp");
408 }
409
410 void _validateParamsToSetInlineKrausMap(KrausMap map, int numQb, int numOps) {
411
412 const char* caller = "setInlineKrausMap";
413 validate_krausMapFields(map, caller);
414 validate_krausMapFieldsMatchPassedParams(map, numQb, numOps, caller);
415 }
416
417 void _validateParamsToSetInlineSuperOp(SuperOp op, int numQb) {
418
419 const char* caller = "setInlineSuperOp";
420 validate_superOpFields(op, caller);
421 validate_superOpFieldsMatchPassedParams(op, numQb, caller);
422 }
423
424 void _validateParamsToCreateInlineKrausMap(int numQb, int numOps) {
425
426 const char* caller = "createInlineKrausMap";
427 validate_envIsInit(caller);
428 validate_newKrausMapParams(numQb, numOps, caller);
429 }
430
431 void _validateParamsToCreateInlineSuperOp(int numQb) {
432
433 const char* caller = "createInlineSuperOp";
434 validate_envIsInit(caller);
435 validate_newSuperOpParams(numQb, caller);
436 }
437
438}
439
440
441
442/*
443 * REPORTING
444 */
445
446
447extern "C" void reportSuperOp(SuperOp op) {
448 validate_superOpFields(op, __func__);
449 validate_numReportedNewlinesAboveZero(__func__); // because trailing newline mandatory
450
451 // demand that SuperOp is GPU-synced, since the
452 // to-be-printed GPU elems will overwrite CPU
453 validate_superOpIsSynced(op, __func__);
454
455 // determine memory costs; heap memory and struct fields
456 size_t elemMem = mem_getLocalSuperOpMemoryRequired(op.numQubits);
457 size_t structMem = sizeof(op);
458
459 print_header(op, elemMem + structMem);
460 print_elems(op);
461
462 // exclude mandatory newline above
463 print_oneFewerNewlines();
464}
465
466
467extern "C" void reportKrausMap(KrausMap map) {
468 validate_krausMapFields(map, __func__);
469 validate_numReportedNewlinesAboveZero(__func__); // because trailing newline mandatory
470
471 // pedantically demand that the map's SuperOP is GPU-synced,
472 // even though only the CPU Kraus operators are printed
473 validate_krausMapIsSynced(map, __func__);
474
475 // determine memory costs (gauranteed not to overflow)
476 bool isDense = true;
477 int numNodes = 1;
478 size_t krausMem = mem_getLocalMatrixMemoryRequired(map.numQubits, isDense, numNodes) * map.numMatrices;
479 size_t superMem = mem_getLocalSuperOpMemoryRequired(map.superop.numQubits);
480 size_t strucMem = sizeof(map);
481
482 // gauranteed not to overflow
483 size_t totalMem = krausMem + superMem + strucMem;
484 print_header(map, totalMem);
485 print_elems(map);
486
487 // exclude mandatory newline above
488 print_oneFewerNewlines();
489}
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: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:467
void reportSuperOp(SuperOp op)
Definition channels.cpp:447
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: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
QuESTEnv getQuESTEnv()