The Quantum Exact Simulation Toolkit v4.0.0
Loading...
Searching...
No Matches
paulis.cpp
1/** @file
2 * API functions for creating PauliStr and PauliStrSum,
3 * and initialising and reporting them
4 *
5 * @author Tyson Jones
6 */
7
8#include "quest/include/precision.h"
9#include "quest/include/paulis.h"
10
11#include "quest/src/core/validation.hpp"
12#include "quest/src/core/printer.hpp"
13#include "quest/src/core/utilities.hpp"
14#include "quest/src/core/parser.hpp"
15#include "quest/src/core/memory.hpp"
16#include "quest/src/core/errors.hpp"
17#include "quest/src/core/bitwise.hpp"
18#include "quest/src/cpu/cpu_config.hpp"
19#include "quest/src/comm/comm_config.hpp"
20#include "quest/src/comm/comm_routines.hpp"
21
22#include <iostream>
23#include <vector>
24#include <string>
25#include <array>
26
27using std::string;
28using std::vector;
29using std::array;
30
31
32
33/*
34 * PRIVATE CONSTANTS
35 */
36
37
38static const int MAX_NUM_PAULIS_PER_MASK = sizeof(PAULI_MASK_TYPE) * 8 / 2;
39static const int MAX_NUM_PAULIS_PER_STR = MAX_NUM_PAULIS_PER_MASK * 2;
40
41
42
43/*
44 * PRIVATE UTILITIES
45 */
46
47
48int getPauliFromMaskAt(PAULI_MASK_TYPE mask, int ind) {
49
50 return getTwoAdjacentBits(mask, 2*ind); // bits at (ind+1, ind)
51}
52
53
54bool didAnyAllocsFailOnAnyNode(PauliStrSum sum) {
55
56 bool anyFail = (
57 ! mem_isAllocated(sum.strings) ||
58 ! mem_isAllocated(sum.coeffs) ||
59 ! mem_isAllocated(sum.isApproxHermitian) );
60
61 if (comm_isInit())
62 anyFail = comm_isTrueOnAllNodes(anyFail);
63
64 return anyFail;
65}
66
67
68void freePauliStrSum(PauliStrSum sum) {
69
70 // these do not need to be allocated (freeing nullptr is legal)
71 cpu_deallocPauliStrings(sum.strings);
72 cpu_deallocArray(sum.coeffs);
73 util_deallocEpsilonSensitiveHeapFlag(sum.isApproxHermitian);
74}
75
76
77void freeAllMemoryIfAnyAllocsFailed(PauliStrSum sum) {
78
79 // do nothing if everything allocated successfully between all nodes
80 if (!didAnyAllocsFailOnAnyNode(sum))
81 return;
82
83 // otherwise free every successful allocation (freeing nullptr is legal)
84 freePauliStrSum(sum);
85}
86
87
88
89/*
90 * INTERNAL UTILITIES
91 *
92 * callable by other internal files but which are not exposed in the header
93 * because we do not wish to make them visible to users. Ergo other internal
94 * files must declare these functions as extern where needed. Yes, it's ugly :(
95 */
96
97
98int paulis_getPauliAt(PauliStr str, int ind) {
99
100 return (ind < MAX_NUM_PAULIS_PER_MASK)?
101 getPauliFromMaskAt(str.lowPaulis, ind) :
102 getPauliFromMaskAt(str.highPaulis, ind - MAX_NUM_PAULIS_PER_MASK);
103}
104
105
106int paulis_getIndOfLefmostNonIdentityPauli(PauliStr str) {
107
108 int ind = (str.highPaulis == 0)? 0 : MAX_NUM_PAULIS_PER_MASK;
109 auto mask = (str.highPaulis == 0)? str.lowPaulis : str.highPaulis;
110
111 while (mask) {
112 mask >>= 2;
113 ind++;
114 }
115
116 return ind - 1;
117}
118
119
120int paulis_getIndOfLefmostNonIdentityPauli(PauliStr* strings, qindex numStrings) {
121
122 int maxInd = 0;
123
124 for (qindex i=0; i<numStrings; i++) {
125 int ind = paulis_getIndOfLefmostNonIdentityPauli(strings[i]);
126 if (ind > maxInd)
127 maxInd = ind;
128 }
129
130 return maxInd;
131}
132
133
134int paulis_getIndOfLefmostNonIdentityPauli(PauliStrSum sum) {
135
136 return paulis_getIndOfLefmostNonIdentityPauli(sum.strings, sum.numTerms);
137}
138
139
140bool paulis_containsXOrY(PauliStr str) {
141
142 int maxInd = paulis_getIndOfLefmostNonIdentityPauli(str);
143
144 for (int i=0; i<=maxInd; i++) {
145 int pauli = paulis_getPauliAt(str, i);
146
147 if (pauli == 1 || pauli == 2)
148 return true;
149 }
150
151 return false;
152}
153
154
155bool paulis_containsXOrY(PauliStrSum sum) {
156
157 for (qindex i=0; i<sum.numTerms; i++)
158 if (paulis_containsXOrY(sum.strings[i]))
159 return true;
160
161 return false;
162}
163
164
165bool paulis_hasOddNumY(PauliStr str) {
166
167 bool odd = false;
168
169 for (int targ=0; targ < MAX_NUM_PAULIS_PER_STR; targ++)
170 if (paulis_getPauliAt(str, targ) == 2)
171 odd = !odd;
172
173 return odd;
174}
175
176
177int paulis_getPrefixZSign(Qureg qureg, vector<int> prefixZ) {
178
179 int sign = 1;
180
181 // each Z contributes +- 1
182 for (int qubit : prefixZ)
183 sign *= util_getRankBitOfQubit(qubit, qureg)? -1 : 1;
184
185 return sign;
186}
187
188
189qcomp paulis_getPrefixPaulisElem(Qureg qureg, vector<int> prefixY, vector<int> prefixZ) {
190
191 // each Z contributes +- 1
192 qcomp elem = paulis_getPrefixZSign(qureg, prefixZ);
193
194 // each Y contributes -+ i
195 for (int qubit : prefixY)
196 elem *= 1_i * (util_getRankBitOfQubit(qubit, qureg)? 1 : -1);
197
198 return elem;
199}
200
201
202vector<int> paulis_getInds(PauliStr str) {
203
204 int maxInd = paulis_getIndOfLefmostNonIdentityPauli(str);
205
206 vector<int> inds(0);
207 inds.reserve(maxInd+1);
208
209 for (int i=0; i<=maxInd; i++)
210 if (paulis_getPauliAt(str, i) != 0)
211 inds.push_back(i);
212
213 return inds;
214}
215
216
217array<vector<int>,3> paulis_getSeparateInds(PauliStr str, Qureg qureg) {
218
219 vector<int> iXYZ = paulis_getInds(str);
220 vector<int> iX, iY, iZ;
221
222 vector<int>* ptrs[] = {&iX, &iY, &iZ};
223
224 for (int i : iXYZ)
225 ptrs[paulis_getPauliAt(str, i) - 1]->push_back(i);
226
227 return {iX, iY, iZ};
228}
229
230
231PauliStr paulis_getShiftedPauliStr(PauliStr str, int pauliShift) {
232
233 if (pauliShift <= 0 || pauliShift >= MAX_NUM_PAULIS_PER_MASK)
234 error_pauliStrShiftedByIllegalAmount();
235
236 int numBitsPerPauli = 2;
237 int numMaskBits = numBitsPerPauli * MAX_NUM_PAULIS_PER_MASK;
238 int bitShift = numBitsPerPauli * pauliShift;
239
240 // record the bits we will lose from lowPaulis, to move to highPaulis
241 PAULI_MASK_TYPE lostBits = getBitsLeftOfIndex(str.lowPaulis, numMaskBits - bitShift - 1);
242
243 // ensure we actually lose these bits from lowPaulis
244 PAULI_MASK_TYPE lowerBits = getBitsRightOfIndex(str.lowPaulis, numMaskBits - bitShift) << bitShift;
245
246 // and add them to highPaulis; we don't have to force lose upper bits of high paulis
247 PAULI_MASK_TYPE upperBits = concatenateBits(str.highPaulis, lostBits, bitShift);
248
249 return {
250 .lowPaulis = lowerBits,
251 .highPaulis = upperBits
252 };
253}
254
255
256PauliStr paulis_getKetAndBraPauliStr(PauliStr str, Qureg qureg) {
257
258 PauliStr shifted = paulis_getShiftedPauliStr(str, qureg.numQubits);
259
260 return {
261 .lowPaulis = str.lowPaulis | shifted.lowPaulis,
262 .highPaulis = str.highPaulis | shifted.highPaulis
263 };
264}
265
266
267PAULI_MASK_TYPE paulis_getKeyOfSameMixedAmpsGroup(PauliStr str) {
268
269 PAULI_MASK_TYPE key = 0;
270
271 // in theory, we can reduce the number of involved operations by bit-shifting
272 // str left by 1, XOR'ing this with str, and retaining every 2nd bit, producing
273 // e.g. key=0110 from str=IXYZ. However, this is an insignificant speedup which
274 // risks sneaky bugs related to handling str's two masks.
275
276 int maxInd = paulis_getIndOfLefmostNonIdentityPauli(str);
277
278 for (int i=0; i<=maxInd; i++) {
279 int pauli = paulis_getPauliAt(str, i);
280 int isXY = (pauli == 1 || pauli == 2);
281 key |= (isXY << i);
282 }
283
284 return key;
285}
286
287
288
289/*
290 * PAULI STRING INITIALISATION
291 *
292 * some of which are exposed directly to C, and some of which are C++-only overloads
293 */
294
295
296extern "C" PauliStr getPauliStr(const char* paulis, int* indices, int numPaulis) {
297 validate_newPauliStrParams(paulis, indices, numPaulis, MAX_NUM_PAULIS_PER_STR, __func__);
298
299 // begin masks at all-identity 'I' = 0
300 PAULI_MASK_TYPE lowPaulis = 0;
301 PAULI_MASK_TYPE highPaulis = 0;
302
303 // change targeted indices to the given Paulis
304 for (int i=0; i<numPaulis; i++) {
305
306 // cast single Pauli to full precision mask to enable below shifts
307 auto pauli = (PAULI_MASK_TYPE) parser_getPauliIntFromChar(paulis[i]);
308
309 // add the Pauli to either the lower or upper pauli masks
310 if (indices[i] < MAX_NUM_PAULIS_PER_MASK)
311 lowPaulis |= pauli << (2*indices[i]);
312 else
313 highPaulis |= pauli << (2*(indices[i] - MAX_NUM_PAULIS_PER_MASK));
314 }
315
316 // return a new stack PauliStr instance, returning by copy
317 return {
318 .lowPaulis = lowPaulis,
319 .highPaulis = highPaulis
320 };
321}
322
323
324PauliStr getPauliStr(int* paulis, int* indices, int numPaulis) {
325 validate_newPauliStrParams(paulis, indices, numPaulis, MAX_NUM_PAULIS_PER_STR, __func__);
326
327 // validation ensures never causes stack overflow
328 char pauliChars[MAX_NUM_PAULIS_PER_STR + 1]; // +1 for null-terminal
329
330 // made a char array from the pauli codes
331 for (int i=0; i<numPaulis; i++)
332 pauliChars[i] = "IXYZ"[paulis[i]];
333
334 // including the trailing null char, used to infer string end/length
335 pauliChars[numPaulis] = '\0';
336
337 return getPauliStr(pauliChars, indices, numPaulis);
338}
339
340
341extern "C" PauliStr _getPauliStrFromInts(int* paulis, int* indices, int numPaulis) {
342
343 return getPauliStr(paulis, indices, numPaulis);
344}
345
346
347PauliStr getPauliStr(string paulis, int* indices, int numPaulis) {
348
349 // additionally validate 'paulis' string has 'numPaulis' chars
350 validate_newPauliStrNumChars(paulis.length(), numPaulis, __func__);
351
352 return getPauliStr(paulis.data(), indices, numPaulis); // validates
353}
354
355PauliStr getPauliStr(string paulis, vector<int> indices) {
356
357 // additionally validate 'paulis' string has 'numPaulis' chars
358 validate_newPauliStrNumChars(paulis.length(), indices.size(), __func__);
359
360 return getPauliStr(paulis.data(), indices.data(), indices.size()); // validates
361}
362
363PauliStr getPauliStr(string paulis) {
364
365 // pedantically validate the string length isn't so long that it would stackoverflow a vector
366 validate_newPauliStrNumPaulis(paulis.size(), MAX_NUM_PAULIS_PER_STR, __func__);
367
368 // automatically target the lowest-index qubits, interpreting rightmost is least significant
369 vector<int> indices(paulis.size());
370 for (size_t i=0; i<paulis.size(); i++)
371 indices[i] = paulis.size() - 1 - i;
372
373 return getPauliStr(paulis, indices); // validates
374}
375
376
377
378/*
379 * PAULI STRING SUM CREATION
380 *
381 * some of which are exposed directly to C, and some of which are C++-only overloads
382 */
383
384
385extern "C" PauliStrSum createPauliStrSum(PauliStr* strings, qcomp* coeffs, qindex numTerms) {
386
387 // note we do not require nor impose the strings to be unique
388 validate_newPauliStrSumParams(numTerms, __func__);
389
390 // create struct
391 PauliStrSum out = {
392 .numTerms = numTerms,
393 .strings = cpu_allocPauliStrings(numTerms), // nullptr if failed
394 .coeffs = cpu_allocArray(numTerms), // nullptr if failed
395 .isApproxHermitian = util_allocEpsilonSensitiveHeapFlag(), // nullptr if failed
396 };
397
398 // if either alloc failed, clear both before validation to avoid leak
399 freeAllMemoryIfAnyAllocsFailed(out);
400 validate_newPauliStrSumAllocs(out, numTerms*sizeof(PauliStr), numTerms*sizeof(qcomp), __func__);
401
402 // otherwise copy given data into new heap structure, and set initial flags
403 cpu_copyPauliStrSum(out, strings, coeffs);
404 util_setFlagToUnknown(out.isApproxHermitian);
405
406 return out;
407}
408
409PauliStrSum createPauliStrSum(vector<PauliStr> strings, vector<qcomp> coeffs) {
410
411 // additionally validate 'strings' and 'coeffs' are the same length
412 validate_newPauliStrSumMatchingListLens(strings.size(), coeffs.size(), __func__);
413
414 return createPauliStrSum(strings.data(), coeffs.data(), coeffs.size()); // validates
415}
416
417
418extern "C" PauliStrSum createInlinePauliStrSum(const char* str) {
419
420 // str must be null-terminated
421 return createInlinePauliStrSum(string(str));
422}
423
425
426 bool rightIsLeastSig = true;
427 return parser_validateAndParsePauliStrSum(str, rightIsLeastSig, __func__);
428}
429
430
431extern "C" PauliStrSum createPauliStrSumFromFile(const char* fn) {
432
433 // fn must be null-terminated
434 return createPauliStrSumFromFile(string(fn));
435}
436
438 validate_canReadFile(fn, __func__);
439
440 // all distributed nodes will simultaneously read the file (that's fine)
441 string str = parser_loadFile(fn);
442
443 bool rightIsLeastSig = true;
444 return parser_validateAndParsePauliStrSum(str, rightIsLeastSig, __func__);
445}
446
447
449
450 // fn must be null-terminated
451 return createPauliStrSumFromReversedFile(string(fn));
452}
453
455 validate_canReadFile(fn, __func__);
456
457 // all distributed nodes will simultaneously read the file (that's fine)
458 string str = parser_loadFile(fn);
459
460 bool rightIsLeastSig = false;
461 return parser_validateAndParsePauliStrSum(str, rightIsLeastSig, __func__);
462}
463
464
465
466/*
467 * DESTROYERS
468 */
469
470
471extern "C" void destroyPauliStrSum(PauliStrSum sum) {
472 validate_pauliStrSumFields(sum, __func__);
473
474 freePauliStrSum(sum);
475}
476
477
478
479/*
480 * API REPORTERS
481 */
482
483
484extern "C" void reportPauliStr(PauliStr str) {
485
486 // no header, so no indentation
487 string indent = "";
488 print_elemsWithoutNewline(str, indent);
489
490 // print all user-set newlines (including none)
491 print_newlines();
492}
493
494
495extern "C" void reportPauliStrSum(PauliStrSum sum) {
496 validate_pauliStrSumFields(sum, __func__);
497 validate_numReportedNewlinesAboveZero(__func__);
498
499 // calculate memory usage
500 qindex numStrBytes = sum.numTerms * sizeof *sum.strings;
501 qindex numCoeffBytes = sum.numTerms * sizeof *sum.coeffs;
502 qindex numStrucBytes = sizeof(sum);
503
504 // we don't bother checking for overflow since total memory scales
505 // linearly with user input parameters, unlike Qureg and matrices.
506 qindex numTotalBytes = numStrBytes + numCoeffBytes + numStrucBytes;
507
508 print_header(sum, numTotalBytes);
509 print_elems(sum);
510
511 // exclude mandatory newline above
512 print_oneFewerNewlines();
513}
PauliStrSum createPauliStrSumFromFile(const char *fn)
Definition paulis.cpp:431
PauliStrSum createPauliStrSum(PauliStr *strings, qcomp *coeffs, qindex numTerms)
Definition paulis.cpp:385
PauliStrSum createInlinePauliStrSum(const char *str)
Definition paulis.cpp:418
PauliStrSum createPauliStrSumFromReversedFile(const char *fn)
Definition paulis.cpp:448
PauliStr getPauliStr(const char *paulis, int *indices, int numPaulis)
Definition paulis.cpp:296
void destroyPauliStrSum(PauliStrSum sum)
Definition paulis.cpp:471
void reportPauliStrSum(PauliStrSum sum)
Definition paulis.cpp:495
void reportPauliStr(PauliStr str)
Definition paulis.cpp:484
long long unsigned int PAULI_MASK_TYPE
Definition precision.h:69
Definition qureg.h:49