The Quantum Exact Simulation Toolkit v4.0.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 qcomp* cpuMem = cpu_allocArray(numElems); // nullptr if failed
135 qcomp* gpuMem = nullptr;
136 if (getQuESTEnv().isGpuAccelerated)
137 gpuMem = gpu_allocArray(numElems); // nullptr if failed
138
139 SuperOp out = {
140 .numQubits = numQubits,
141 .numRows = numRows,
142
143 .cpuElems = cpu_allocAndInitMatrixWrapper(cpuMem, numRows), // nullptr if failed
144 .cpuElemsFlat = cpuMem,
145 .gpuElemsFlat = gpuMem,
146
147 .wasGpuSynced = cpu_allocHeapFlag() // nullptr if failed
148 };
149
150 // if heap flag allocated, mark it as unsynced (caller will handle if allocation failed)
151 if (mem_isAllocated(out.wasGpuSynced))
152 *(out.wasGpuSynced) = 0;
153
154 return out;
155}
156
157
158extern "C" SuperOp createSuperOp(int numQubits) {
159 validate_envIsInit(__func__);
160 validate_newSuperOpParams(numQubits, __func__);
161
162 SuperOp out = allocSuperOp(numQubits); // fields may be or contain nullptr if failed
163
164 // free all memory before we throw validation errors to avoid leaks
165 freeAllMemoryIfAnyAllocsFailed(out);
166 validate_newSuperOpAllocs(out, __func__);
167
168 return out;
169}
170
171
172extern "C" KrausMap createKrausMap(int numQubits, int numOperators) {
173 validate_envIsInit(__func__);
174 validate_newKrausMapParams(numQubits, numOperators, __func__);
175
176 // validation ensures this never overflows
177 qindex numRows = powerOf2(numQubits);
178
179 KrausMap out = {
180 .numQubits = numQubits,
181 .numMatrices = numOperators,
182 .numRows = numRows,
183
184 .matrices = cpu_allocMatrixList(numRows, numOperators), // is or contains nullptr if failed
185 .superop = allocSuperOp(numQubits), // heap fields are or contain nullptr if failed
186
187 .isApproxCPTP = util_allocEpsilonSensitiveHeapFlag(), // nullptr if failed
188 };
189
190 // free memory before throwing validation error to avoid memory leaks
191 freeAllMemoryIfAnyAllocsFailed(out); // sets out.matrices=nullptr if failed
192 validate_newKrausMapAllocs(out, __func__);
193
194 // mark CPTP as unknown; it will be lazily evaluated whene a function asserts CPTP-ness
195 util_setFlagToUnknown(out.isApproxCPTP);
196
197 return out;
198}
199
200
201extern "C" void destroySuperOp(SuperOp op) {
202 validate_superOpFields(op, __func__);
203
204 freeSuperOp(op);
205}
206
207
208extern "C" void destroyKrausMap(KrausMap map) {
209 validate_krausMapFields(map, __func__);
210
211 freeKrausMap(map);
212}
213
214
215
216/*
217 * SYNC
218 */
219
220
221extern "C" void syncSuperOp(SuperOp op) {
222 validate_superOpFields(op, __func__);
223
224 // optionally overwrite GPU elements with user-modified CPU elements
225 if (mem_isAllocated(util_getGpuMemPtr(op)))
226 gpu_copyCpuToGpu(op);
227
228 // indicate that the matrix is now permanently GPU synchronised, even
229 // if we are not in GPU-accelerated mode - it hardly matters
230 *(op.wasGpuSynced) = 1;
231}
232
233
234extern "C" void syncKrausMap(KrausMap map) {
235 validate_krausMapFields(map, __func__);
236
237 // re-compute and GPU-sync the superoperator from the modified matrices
238 util_setSuperoperator(map.superop.cpuElems, map.matrices, map.numMatrices, map.numQubits);
239 syncSuperOp(map.superop);
240
241 // indicate that we do not know whether the revised map is
242 // is CPTP; we defer establishing that until a CPTP check
243 util_setFlagToUnknown(map.isApproxCPTP);
244}
245
246
247
248/*
249 * SUPEROPERATOR SETTERS
250 *
251 * defining the C and C++ compatible pointer version,
252 * and the C++-only vector overloads. Note the C-only
253 * VLA overloads are defined in the header
254 */
255
256
257// T can be qcomp** or vector<vector<qcomp>>
258template <typename T>
259void setAndSyncSuperOpElems(SuperOp op, T matrix) {
260
261 // overwrite the CPU matrix
262 cpu_copyMatrix(op.cpuElems, matrix, op.numRows);
263
264 // sync CPU matrix to flat GPU array
265 syncSuperOp(op);
266}
267
268
269extern "C" void setSuperOp(SuperOp op, qcomp** matrix) {
270 validate_superOpFields(op, __func__);
271 validate_matrixNewElemsPtrNotNull(matrix, op.numRows, __func__); // fine for superop
272
273 setAndSyncSuperOpElems(op, matrix);
274}
275
276void setSuperOp(SuperOp op, vector<vector<qcomp>> matrix) {
277 validate_superOpFields(op, __func__);
278 validate_superOpNewMatrixDims(op, matrix, __func__);
279
280 setAndSyncSuperOpElems(op, matrix);
281}
282
283
284
285/*
286 * KRAUS MAP SETTERS
287 *
288 * defining the C and C++ compatible pointer version,
289 * and the C++-only vector overloads. Note the C-only
290 * VLA overloads are defined in the header
291 */
292
293
294// type T can be qcomp*** or vector<vector<vector<qcomp>>>
295template <typename T>
296void setAndSyncKrausMapElems(KrausMap map, T matrices) {
297
298 // copy over the given matrices into the map's CPU mmemory
299 for (int n=0; n<map.numMatrices; n++)
300 cpu_copyMatrix(map.matrices[n], matrices[n], map.numRows);
301
302 // update the superoperator, including its GPU memory, and its isApproxCPTP flag
303 syncKrausMap(map);
304}
305
306
307extern "C" void setKrausMap(KrausMap map, qcomp*** matrices) {
308 validate_krausMapFields(map, __func__);
309
310 setAndSyncKrausMapElems(map, matrices);
311}
312
313
314void setKrausMap(KrausMap map, vector<vector<vector<qcomp>>> matrices) {
315 validate_krausMapFields(map, __func__);
316 validate_krausMapNewMatrixDims(map, matrices, __func__);
317
318 setAndSyncKrausMapElems(map, matrices);
319}
320
321
322
323/*
324 * LITERAL SETTERS
325 *
326 * Only the C++ versions are defined here, because the C versions are macros
327 * defined in the header. Note the C++ versions themselves are entirely
328 * superfluous and merely call the above vector setters, but we still define
329 * them for API consistency, and we additionally validate the superfluous
330 * additional parameters they pass.
331 */
332
333
334void setInlineKrausMap(KrausMap map, int numQb, int numOps, vector<vector<vector<qcomp>>> matrices) {
335 validate_krausMapFields(map, __func__);
336 validate_krausMapFieldsMatchPassedParams(map, numQb, numOps, __func__);
337 validate_krausMapNewMatrixDims(map, matrices, __func__);
338
339 setAndSyncKrausMapElems(map, matrices);
340}
341
342
343void setInlineSuperOp(SuperOp op, int numQb, vector<vector<qcomp>> matrix) {
344 validate_superOpFields(op, __func__);
345 validate_superOpFieldsMatchPassedParams(op, numQb, __func__);
346 validate_superOpNewMatrixDims(op, matrix, __func__);
347
348 setAndSyncSuperOpElems(op, matrix);
349}
350
351
352
353/*
354 * LITERAL CREATORS
355 *
356 * Only the C++ versions are defined here; the C versions are in-header macros.
357 */
358
359
360KrausMap createInlineKrausMap(int numQubits, int numOperators, vector<vector<vector<qcomp>>> matrices) {
361 validate_envIsInit(__func__);
362 validate_newKrausMapParams(numQubits, numOperators, __func__);
363 validate_newInlineKrausMapDimMatchesVectors(numQubits, numOperators, matrices, __func__);
364
365 // pre-validation gauranteed to pass, but malloc failures will trigger an error
366 // message specific to 'createKrausMap', rather than this 'inline' version. Alas!
367 KrausMap map = createKrausMap(numQubits, numOperators);
368 setAndSyncKrausMapElems(map, matrices);
369 return map;
370}
371
372
373SuperOp createInlineSuperOp(int numQubits, vector<vector<qcomp>> matrix) {
374 validate_envIsInit(__func__);
375 validate_newSuperOpParams(numQubits, __func__);
376 validate_newInlineSuperOpDimMatchesVectors(numQubits, matrix, __func__);
377
378 // pre-validation gauranteed to pass, but malloc failures will trigger an error
379 // message specific to 'createSuperOp', rather than this 'inline' version. Alas!
380 SuperOp op = createSuperOp(numQubits);
381 setAndSyncSuperOpElems(op, matrix);
382 return op;
383}
384
385
386
387/*
388 * EXPOSING SOME SETTER VALIDATION TO HEADER
389 *
390 * Some setters are necessarily defined in the header, because they accept
391 * C-only VLAs which need to be cast into pointers before being passed to
392 * this C++ backend (which does not support VLA). These setters need their
393 * validators exposed, though we cannot expose the entirety of validation.hpp
394 * because it cannot be parsed by C; so we here wrap specific functions.
395 */
396
397
398extern "C" {
399
400 void _validateParamsToSetKrausMapFromArr(KrausMap map) {
401 validate_krausMapFields(map, "setKrausMap");
402 }
403
404 void _validateParamsToSetSuperOpFromArr(SuperOp op) {
405 validate_superOpFields(op, "setSuperOp");
406 }
407
408 void _validateParamsToSetInlineKrausMap(KrausMap map, int numQb, int numOps) {
409
410 const char* caller = "setInlineKrausMap";
411 validate_krausMapFields(map, caller);
412 validate_krausMapFieldsMatchPassedParams(map, numQb, numOps, caller);
413 }
414
415 void _validateParamsToSetInlineSuperOp(SuperOp op, int numQb) {
416
417 const char* caller = "setInlineSuperOp";
418 validate_superOpFields(op, caller);
419 validate_superOpFieldsMatchPassedParams(op, numQb, caller);
420 }
421
422 void _validateParamsToCreateInlineKrausMap(int numQb, int numOps) {
423
424 const char* caller = "createInlineKrausMap";
425 validate_envIsInit(caller);
426 validate_newKrausMapParams(numQb, numOps, caller);
427 }
428
429 void _validateParamsToCreateInlineSuperOp(int numQb) {
430
431 const char* caller = "createInlineSuperOp";
432 validate_envIsInit(caller);
433 validate_newSuperOpParams(numQb, caller);
434 }
435
436}
437
438
439
440/*
441 * REPORTING
442 */
443
444
445extern "C" void reportSuperOp(SuperOp op) {
446 validate_superOpFields(op, __func__);
447 validate_numReportedNewlinesAboveZero(__func__); // because trailing newline mandatory
448
449 // demand that SuperOp is GPU-synced, since the
450 // to-be-printed GPU elems will overwrite CPU
451 validate_superOpIsSynced(op, __func__);
452
453 // determine memory costs; heap memory and struct fields
454 size_t elemMem = mem_getLocalSuperOpMemoryRequired(op.numQubits);
455 size_t structMem = sizeof(op);
456
457 print_header(op, elemMem + structMem);
458 print_elems(op);
459
460 // exclude mandatory newline above
461 print_oneFewerNewlines();
462}
463
464
465extern "C" void reportKrausMap(KrausMap map) {
466 validate_krausMapFields(map, __func__);
467 validate_numReportedNewlinesAboveZero(__func__); // because trailing newline mandatory
468
469 // pedantically demand that the map's SuperOP is GPU-synced,
470 // even though only the CPU Kraus operators are printed
471 validate_krausMapIsSynced(map, __func__);
472
473 // determine memory costs (gauranteed not to overflow)
474 bool isDense = true;
475 int numNodes = 1;
476 size_t krausMem = mem_getLocalMatrixMemoryRequired(map.numQubits, isDense, numNodes) * map.numMatrices;
477 size_t superMem = mem_getLocalSuperOpMemoryRequired(map.superop.numQubits);
478 size_t strucMem = sizeof(map);
479
480 // gauranteed not to overflow
481 size_t totalMem = krausMem + superMem + strucMem;
482 print_header(map, totalMem);
483 print_elems(map);
484
485 // exclude mandatory newline above
486 print_oneFewerNewlines();
487}
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
QuESTEnv getQuESTEnv()