The Quantum Exact Simulation Toolkit v4.2.0
Loading...
Searching...
No Matches
test_utilities.cpp
1/** @file
2 * Utility functions used by the ported v3 tests of QuEST's deprecated v3 API.
3 *
4 * @author Tyson Jones
5 * @author Oliver Thomson Brown (ported to Catch2 v3)
6 * @author Ali Rezaei (tested porting to QuEST v4)
7 */
8
9#include <catch2/catch_test_macros.hpp>
10#include <catch2/generators/catch_generators.hpp>
11
12#include "quest.h"
13#include "test_utilities.hpp"
14
15#include <random>
16#include <vector>
17#include <algorithm>
18#include <bitset>
19
20#if COMPILE_MPI
21
22 #include <mpi.h>
23
24 #if (FLOAT_PRECISION == 1)
25 #define MPI_QCOMP MPI_CXX_FLOAT_COMPLEX
26 #elif (FLOAT_PRECISION == 2)
27 #define MPI_QCOMP MPI_CXX_DOUBLE_COMPLEX
28 #elif (FLOAT_PRECISION == 4) && defined(MPI_CXX_LONG_DOUBLE_COMPLEX)
29 #define MPI_QCOMP MPI_CXX_LONG_DOUBLE_COMPLEX
30 #else
31 #define MPI_QCOMP MPI_C_LONG_DOUBLE_COMPLEX
32 #endif
33
34 #ifdef MPI_MAX_AMPS_IN_MSG
35 #undef MPI_MAX_AMPS_IN_MSG
36 #endif
37 #define MPI_MAX_AMPS_IN_MSG (1 << 30)
38
39#endif
40
41using std::vector;
42
43
44
45/*
46 * resolve reprecated absReal()
47 */
48
49#ifdef absReal
50#undef absReal
51#endif
52
53// not sure where these will go! Maybe into QuEST v itself
54qreal absReal(qreal x) {
55 return abs(x); // TODO: make precision agnostic
56}
57qreal absComp(qcomp x) {
58 return abs(x); // TODO: make precision agnostic
59}
60
61
62
63/* RNG used for generating random test states,
64 * independently used from C's rand() which is
65 * used to generate random test data (e.g. operators)
66 */
67
68static std::mt19937 randomGenerator;
69
70
71
72/* (don't generate doxygen doc)
73 *
74 * preconditions to the internal unit testing functions are checked using
75 * DEMAND rather than Catch2's REQUIRE, so that they are not counted in the
76 * total unit testing statistics (e.g. number of checks passed).
77 */
78#define DEMAND( cond ) if (!(cond)) FAIL( );
79
80QVector operator + (const QVector& v1, const QVector& v2) {
81 DEMAND( v1.size() == v2.size() );
82 QVector out = v1;
83 for (size_t i=0; i<v2.size(); i++)
84 out[i] += v2[i];
85 return out;
86}
87QVector operator - (const QVector& v1, const QVector& v2) {
88 DEMAND( v1.size() == v2.size() );
89 QVector out = v1;
90 for (size_t i=0; i<v2.size(); i++)
91 out[i] -= v2[i];
92 return out;
93}
94QVector operator * (const qcomp& a, const QVector& v) {
95 QVector out = v;
96 for (size_t i=0; i<v.size(); i++)
97 out[i] *= a;
98 return out;
99}
100QVector operator * (const QVector& v, const qcomp& a) {
101 return a * v;
102}
103QVector operator / (const QVector& v, const qcomp& a) {
104 DEMAND( abs(a) != 0 );
105 QVector out = v;
106 for (size_t i=0; i<v.size(); i++)
107 out[i] /= a;
108 return out;
109}
110qcomp operator * (const QVector &v1, const QVector& v2) {
111 // this is sum_i v1_i conj(v2_i)
112 DEMAND( v1.size() == v2.size() );
113 qcomp out = 0;
114 for (size_t i=0; i<v1.size(); i++)
115 out += v1[i] * conj(v2[i]);
116 return out;
117}
118void operator += (QVector& v1, const QVector& v2) { // these violate += returns (fine)
119 v1 = v1 + v2;
120}
121void operator -= (QVector& v1, const QVector& v2) {
122 v1 = v1 - v2;
123}
124void operator *= (QVector& v1, const qcomp& a) {
125 v1 = v1 * a;
126}
127void operator /= (QVector& v1, const qcomp& a) {
128 v1 = v1 / a;
129}
130QMatrix operator + (const QMatrix& m1, const QMatrix& m2) {
131 DEMAND( m1.size() == m2.size() );
132 QMatrix out = m1;
133 for (size_t r=0; r<m1.size(); r++)
134 for (size_t c=0; c<m1.size(); c++)
135 out[r][c] += m2[r][c];
136 return out;
137}
138QMatrix operator - (const QMatrix& m1, const QMatrix& m2) {
139 DEMAND( m1.size() == m2.size() );
140 QMatrix out = m1;
141 for (size_t r=0; r<m1.size(); r++)
142 for (size_t c=0; c<m1.size(); c++)
143 out[r][c] -= m2[r][c];
144 return out;
145}
146QMatrix operator * (const qcomp& a, const QMatrix& m) {
147 QMatrix out = m;
148 for (size_t r=0; r<m.size(); r++)
149 for (size_t c=0; c<m.size(); c++)
150 out[r][c] *= a;
151 return out;
152}
153QMatrix operator * (const QMatrix& m, const qcomp& a) {
154 return a * m;
155}
156QMatrix operator / (const QMatrix& m, const qcomp& a) {
157 QMatrix out = m;
158 for (size_t r=0; r<m.size(); r++)
159 for (size_t c=0; c<m.size(); c++)
160 out[r][c] /= a;
161 return out;
162}
163QMatrix operator * (const QMatrix& m1, const QMatrix& m2) {
164 QMatrix prod = m1; // will be completely overwritten
165 for (size_t r=0; r<m1.size(); r++)
166 for (size_t c=0; c<m1.size(); c++) {
167 prod[r][c] = 0;
168 for (size_t k=0; k<m1.size(); k++)
169 prod[r][c] += m1[r][k] * m2[k][c];
170 }
171 return prod;
172}
173void operator += (QMatrix& m1, const QMatrix& m2) {
174 m1 = m1 + m2;
175}
176void operator -= (QMatrix& m1, const QMatrix& m2) {
177 m1 = m1 - m2;
178}
179void operator *= (QMatrix& m1, const qreal& a) {
180 m1 = m1 * a;
181}
182void operator /= (QMatrix& m1, const qreal& a) {
183 m1 = m1 / a;
184}
185void operator *= (QMatrix& m1, const QMatrix& m2) {
186 m1 = m1 * m2;
187}
188QVector operator * (const QMatrix& m, const QVector& v) {
189 DEMAND( m.size() == v.size() );
190 QVector prod = QVector(v.size());
191 for (size_t r=0; r<v.size(); r++)
192 for (size_t c=0; c<v.size(); c++)
193 prod[r] += m[r][c] * v[c];
194 return prod;
195}
196
198
199 // must seed both rand() and this C++ generator (used for shuffling)
200
201 // obtain a seed from hardware
202 std::random_device cspnrg;
203 unsigned seed = cspnrg();
204
205 // broadcast to ensure node consensus
206#if COMPILE_MPI
207 int sendRank = 0;
208 MPI_Bcast(&seed, 1, MPI_UNSIGNED, sendRank, MPI_COMM_WORLD);
209#endif
210
211 // initilise both C (rand()) and C++ (randomGenerator) RNGs
212 srand(seed);
213 randomGenerator.seed(seed);
214}
215
217 DEMAND( qureg.isDensityMatrix == 0 );
218 DEMAND( qureg.numAmps == (long long int) ref.size() );
219
220 // assert ref is in the debug state (else initDebugState failed)
221 for (size_t i=0; i<ref.size(); i++) {
222 qcomp val = qcomp(.2*i, .2*i+.1);
223 DEMAND( abs(ref[i] - val) < REAL_EPS );
224 }
225
226 // check qureg and ref agree
227 DEMAND( areEqual(qureg, ref) );
228}
229
231 DEMAND( qureg.isDensityMatrix == 1 );
232 DEMAND( (1LL << qureg.numQubits) == (long long int) ref.size() );
233
234 // assert ref is in the (column-wise) debug state (else initDebugState failed)
235 size_t i = 0;
236 for (size_t c=0; c<ref.size(); c++) {
237 for (size_t r=0; r<ref.size(); r++) {
238 qcomp val = qcomp(.2*i, .2*i+.1);
239 DEMAND( abs(ref[r][c] - val) < REAL_EPS );
240 i++;
241 }
242 }
243
244 // check qureg and ref agree
245 DEMAND( areEqual(qureg, ref) );
246}
247
249
250 QVector prod = QVector(a.size() * b.size());
251
252 for (size_t i=0; i<prod.size(); i++)
253 prod[i] = b[i / a.size()] * a[i % a.size()];
254
255 return prod;
256}
257
259 DEMAND( dim > 1 );
260 QMatrix matr = QMatrix(dim);
261 for (size_t i=0; i<dim; i++)
262 matr[i].resize(dim);
263 return matr;
264}
265
267 DEMAND( dim > 1 );
268 QMatrix matr = getZeroMatrix(dim);
269 for (size_t i=0; i<dim; i++)
270 matr[i][i] = 1;
271 return matr;
272}
273
275 DEMAND( ket.size() == bra.size() );
276 QMatrix mat = getZeroMatrix(ket.size());
277
278 for (size_t r=0; r<ket.size(); r++)
279 for (size_t c=0; c<ket.size(); c++)
280 mat[r][c] = ket[r] * conj(bra[c]);
281 return mat;
282}
283
285 QMatrix prod = getZeroMatrix(a.size() * b.size());
286 for (size_t r=0; r<b.size(); r++)
287 for (size_t c=0; c<b.size(); c++)
288 for (size_t i=0; i<a.size(); i++)
289 for (size_t j=0; j<a.size(); j++)
290 prod[r+b.size()*i][c+b.size()*j] = a[i][j] * b[r][c];
291 return prod;
292}
293
294QMatrix getTranspose(QMatrix a) {
295 QMatrix b = a;
296 for (size_t r=0; r<a.size(); r++)
297 for (size_t c=0; c<a.size(); c++)
298 b[r][c] = a[c][r];
299 return b;
300}
301
303 QMatrix b = a;
304 for (size_t r=0; r<a.size(); r++)
305 for (size_t c=0; c<a.size(); c++)
306 b[r][c] = conj(a[c][r]);
307 return b;
308}
309
311
312 // ensure diagonal
313 for (size_t r=0; r<a.size(); r++)
314 for (size_t c=0; c<a.size(); c++) {
315 if (r == c)
316 continue;
317 DEMAND( real(a[r][c]) == 0. );
318 DEMAND( imag(a[r][c]) == 0. );
319 }
320
321 // exp(diagonal) = diagonal(exp)
322 QMatrix diag = a;
323 for (size_t i=0; i<a.size(); i++)
324 diag[i][i] = exp(diag[i][i]);
325
326 return diag;
327}
328
330 QMatrix iden = getIdentityMatrix(a.size());
331 QMatrix expo = (cos(angle/2) * iden) + ((qcomp) -1i * sin(angle/2) * a);
332 return expo;
333}
334
335void setSubMatrix(QMatrix &dest, QMatrix sub, size_t r, size_t c) {
336 DEMAND( sub.size() + r <= dest.size() );
337 DEMAND( sub.size() + c <= dest.size() );
338 for (size_t i=0; i<sub.size(); i++)
339 for (size_t j=0; j<sub.size(); j++)
340 dest[r+i][c+j] = sub[i][j];
341}
342
343QMatrix getSwapMatrix(int qb1, int qb2, int numQb) {
344 DEMAND( numQb > 1 );
345 DEMAND( (qb1 >= 0 && qb1 < numQb) );
346 DEMAND( (qb2 >= 0 && qb2 < numQb) );
347
348 if (qb1 > qb2)
349 std::swap(qb1, qb2);
350
351 if (qb1 == qb2)
352 return getIdentityMatrix(1 << numQb);
353
354 QMatrix swap;
355
356 if (qb2 == qb1 + 1) {
357 // qubits are adjacent
358 swap = QMatrix{{1,0,0,0},{0,0,1,0},{0,1,0,0},{0,0,0,1}};
359
360 } else {
361 // qubits are distant
362 int block = 1 << (qb2 - qb1);
363 swap = getZeroMatrix(block*2);
364 QMatrix iden = getIdentityMatrix(block/2);
365
366 // Lemma 3.1 of arxiv.org/pdf/1711.09765.pdf
367 QMatrix p0{{1,0},{0,0}};
368 QMatrix l0{{0,1},{0,0}};
369 QMatrix l1{{0,0},{1,0}};
370 QMatrix p1{{0,0},{0,1}};
371
372 /* notating a^(n+1) = identity(1<<n) (otimes) a, we construct the matrix
373 * [ p0^(N) l1^N ]
374 * [ l0^(N) p1^N ]
375 * where N = qb2 - qb1 */
376 setSubMatrix(swap, getKroneckerProduct(iden, p0), 0, 0);
377 setSubMatrix(swap, getKroneckerProduct(iden, l0), block, 0);
378 setSubMatrix(swap, getKroneckerProduct(iden, l1), 0, block);
379 setSubMatrix(swap, getKroneckerProduct(iden, p1), block, block);
380 }
381
382 // pad swap with outer identities
383 if (qb1 > 0)
384 swap = getKroneckerProduct(swap, getIdentityMatrix(1<<qb1));
385 if (qb2 < numQb-1)
386 swap = getKroneckerProduct(getIdentityMatrix(1<<(numQb-qb2-1)), swap);
387
388 return swap;
389}
390
391/* (don't generate doxygen doc)
392 *
393 * iterates list1 (of length len1) and replaces element oldEl with newEl, which is
394 * gauranteed to be present at most once (between list1 AND list2), though may
395 * not be present at all. If oldEl isn't present in list1, does the same for list2.
396 * list1 is skipped if == NULL. This is used by getFullOperatorMatrix() to ensure
397 * that, when qubits are swapped, their appearences in the remaining qubit lists
398 * are updated.
399 */
400void updateIndices(int oldEl, int newEl, int* list1, int len1, int* list2, int len2) {
401 if (list1 != NULL) {
402 for (int i=0; i<len1; i++) {
403 if (list1[i] == oldEl) {
404 list1[i] = newEl;
405 return;
406 }
407 }
408 }
409 for (int i=0; i<len2; i++) {
410 if (list2[i] == oldEl) {
411 list2[i] = newEl;
412 return;
413 }
414 }
415}
416
418 int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op, int numQubits
419) {
420 DEMAND( numCtrls >= 0 );
421 DEMAND( numTargs >= 0 );
422 DEMAND( numQubits >= (numCtrls+numTargs) );
423 DEMAND( op.size() == (1u << numTargs) );
424
425 // copy {ctrls} and {targs}to restore at end
426 vector<int> ctrlsCopy(ctrls, ctrls+numCtrls);
427 vector<int> targsCopy(targs, targs+numTargs);
428
429 // full-state matrix of qubit swaps
430 QMatrix swaps = getIdentityMatrix(1 << numQubits);
431 QMatrix unswaps = getIdentityMatrix(1 << numQubits);
432 QMatrix matr;
433
434 // swap targs to {0, ..., numTargs-1}
435 for (int i=0; i<numTargs; i++) {
436 if (i != targs[i]) {
437 matr = getSwapMatrix(i, targs[i], numQubits);
438 swaps = matr * swaps;
439 unswaps = unswaps * matr;
440
441 // even if this is the last targ, ctrls might still need updating
442 updateIndices(
443 i, targs[i], (i < numTargs-1)? &targs[i+1] : NULL,
444 numTargs-i-1, ctrls, numCtrls);
445 }
446 }
447
448 // swap ctrls to {numTargs, ..., numTargs+numCtrls-1}
449 for (int i=0; i<numCtrls; i++) {
450 int newInd = numTargs+i;
451 if (newInd != ctrls[i]) {
452 matr = getSwapMatrix(newInd, ctrls[i], numQubits);
453 swaps = matr * swaps;
454 unswaps = unswaps * matr;
455
456 // update remaining ctrls (if any exist)
457 if (i < numCtrls-1)
458 updateIndices(newInd, ctrls[i], NULL, 0, &ctrls[i+1], numCtrls-i-1);
459 }
460 }
461
462 // construct controlled-op matrix for qubits {0, ..., numCtrls+numTargs-1}
463 size_t dim = 1 << (numCtrls+numTargs);
464 QMatrix fullOp = getIdentityMatrix(dim);
465 setSubMatrix(fullOp, op, dim-op.size(), dim-op.size());
466
467 // create full-state controlled-op matrix (left-pad identities)
468 if (numQubits > numCtrls+numTargs) {
469 size_t pad = 1 << (numQubits - numCtrls - numTargs);
470 fullOp = getKroneckerProduct(getIdentityMatrix(pad), fullOp);
471 }
472
473 // apply swap to either side (to swap qubits back and forth)
474 fullOp = unswaps * fullOp * swaps;
475
476 // restore {ctrls and targs}
477 for (int i=0; i<numCtrls; i++)
478 ctrls[i] = ctrlsCopy[i];
479 for (int i=0; i<numTargs; i++)
480 targs[i] = targsCopy[i];
481
482 return fullOp;
483}
484
485unsigned int calcLog2(long unsigned int res) {
486 unsigned int n = 0;
487 while (res >>= 1)
488 n++;
489 return n;
490}
491
493 DEMAND( dim > 1 );
494
495 QMatrix matr = getZeroMatrix(dim);
496 for (int i=0; i<dim; i++) {
497 for (int j=0; j<dim; j++) {
498
499 // generate 2 normally-distributed random numbers via Box-Muller
500 qreal a = rand()/(qreal) RAND_MAX;
501 qreal b = rand()/(qreal) RAND_MAX;
502 if (a == 0) a = REAL_EPS; // prevent rand()=0 creation of NaN
503 qreal r1 = sqrt(-2 * log(a)) * cos(2 * 3.14159265 * b);
504 qreal r2 = sqrt(-2 * log(a)) * sin(2 * 3.14159265 * b);
505
506 matr[i][j] = r1 + r2 * (qcomp) 1i;
507 }
508 }
509 return matr;
510}
511
513 DEMAND( a.size() == b.size() );
514
515 for (size_t i=0; i<a.size(); i++)
516 if (abs(a[i] - b[i]) > REAL_EPS)
517 return false;
518 return true;
519}
520
522 DEMAND( a.size() == b.size() );
523
524 for (size_t i=0; i<a.size(); i++)
525 for (size_t j=0; j<b.size(); j++)
526 if (abs(a[i][j] - b[i][j]) > REAL_EPS)
527 return false;
528 return true;
529}
530
531qcomp expI(qreal phase) {
532 return qcomp(cos(phase), sin(phase));
533}
534
535qreal getRandomReal(qreal min, qreal max) {
536 DEMAND( min <= max );
537 qreal r = min + (max - min) * (rand() / (qreal) RAND_MAX);
538
539 // check bounds satisfied
540 DEMAND( r >= min );
541 DEMAND( r <= max );
542 return r;
543}
544
546 return getRandomReal(-1,1) + getRandomReal(-1,1) * (qcomp) 1i;
547}
548
550 QVector vec = QVector(dim);
551 for (int i=0; i<dim; i++)
552 vec[i] = getRandomComplex();
553
554 return vec;
555}
556
558
559 // compute the vec norm via Kahan summation to suppress numerical error
560 qreal norm = 0;
561 qreal y, t, c;
562 c = 0;
563
564 for (size_t i=0; i<vec.size(); i++) {
565 y = real(vec[i])*real(vec[i]) - c;
566 t = norm + y;
567 c = ( t - norm ) - y;
568 norm = t;
569
570 y = imag(vec[i])*imag(vec[i]) - c;
571 t = norm + y;
572 c = ( t - norm ) - y;
573 norm = t;
574 }
575
576 for (size_t i=0; i<vec.size(); i++)
577 vec[i] /= sqrt(norm);
578 return vec;
579}
580
582 return getNormalised(getRandomQVector(1<<numQb));
583}
584
585vector<qreal> getRandomProbabilities(int numProbs) {
586
587 // generate random unnormalised scalars
588 vector<qreal> probs;
589 qreal total = 0;
590 for (int i=0; i<numProbs; i++) {
591 qreal prob = getRandomReal(0, 1);
592 probs.push_back(prob);
593 total += prob;
594 }
595
596 // normalise
597 for (int i=0; i<numProbs; i++)
598 probs[i] /= total;
599
600 return probs;
601}
602
604 DEMAND( numQb > 0 );
605
606 // generate random probabilities to weight random pure states
607 int dim = 1<<numQb;
608 vector<qreal> probs = getRandomProbabilities(dim);
609
610 // add random pure states
611 QMatrix dens = getZeroMatrix(dim);
612 for (int i=0; i<dim; i++) {
613 QVector pure = getRandomStateVector(numQb);
614 dens += probs[i] * getKetBra(pure, pure);
615 }
616
617 return dens;
618}
619
621 return getKetBra(state, state);
622}
623
625 QVector vec = getRandomStateVector(numQb);
626 QMatrix mat = getPureDensityMatrix(vec);
627 return mat;
628}
629
631
632 QVector vec = QVector(matr.size());
633 for (size_t i=0; i<vec.size(); i++)
634 vec[i] = matr[i][i];
635
636 return vec;
637}
638
639int getRandomInt(int min, int max) {
640 return (int) round(getRandomReal(min, max-1));
641}
642
643QMatrix getOrthonormalisedRows(QMatrix matr) {
644
645 // perform the Gram-Schmidt process, processing each row of matr in-turn
646 for (size_t i=0; i<matr.size(); i++) {
647 QVector row = matr[i];
648
649 // compute new orthogonal row by subtracting proj row onto prevs
650 for (int k=i-1; k>=0; k--) {
651
652 // compute inner_product(row, prev) = row . conj(prev)
653 qcomp prod = row * matr[k];
654
655 // subtract (proj row onto prev) = (prod * prev) from final row
656 matr[i] -= prod * matr[k];
657 }
658
659 // normalise the row
660 matr[i] = getNormalised(matr[i]);
661 }
662
663 // return the new orthonormal matrix
664 return matr;
665}
666
667QMatrix getRandomDiagonalUnitary(int numQb) {
668 DEMAND( numQb >= 1 );
669
670 QMatrix matr = getZeroMatrix(1 << numQb);
671 for (size_t i=0; i<matr.size(); i++)
672 matr[i][i] = expI(getRandomReal(0,4*M_PI));
673
674 return matr;
675}
676
678 DEMAND( numQb >= 1 );
679
680 // create Z ~ random complex matrix (distribution not too important)
681 size_t dim = 1 << numQb;
682 QMatrix matrZ = getRandomQMatrix(dim);
683 QMatrix matrZT = getTranspose(matrZ);
684
685 // create Z = Q R (via QR decomposition) ...
686 QMatrix matrQT = getOrthonormalisedRows(matrZ);
687 QMatrix matrQ = getTranspose(matrQT);
688 QMatrix matrR = getZeroMatrix(dim);
689
690 // ... where R_rc = (columm c of Z) . (column r of Q) = (row c of ZT) . (row r of QT)
691 for (size_t r=0; r<dim; r++)
692 for (size_t c=r; c<dim; c++)
693 matrR[r][c] = matrZT[c] * matrQT[r];
694
695 // create D = normalised diagonal of R
696 QMatrix matrD = getZeroMatrix(dim);
697 for (size_t i=0; i<dim; i++)
698 matrD[i][i] = matrR[i][i] / abs(matrR[i][i]);
699
700 // create U = Q D
701 QMatrix matrU = matrQ * matrD;
702
703 // in the rare scenario the result is not sufficiently precisely unitary,
704 // replace it with a trivially unitary diagonal matrix
705 QMatrix daggerProd = matrU * getConjugateTranspose(matrU);
706 QMatrix iden = getIdentityMatrix(dim);
707 if( ! areEqual(daggerProd, iden) )
708 matrU = getRandomDiagonalUnitary(numQb);
709
710 return matrU;
711}
712
713vector<QMatrix> getRandomKrausMap(int numQb, int numOps) {
714 DEMAND( numOps >= 1 );
715 DEMAND( numOps <= 4*numQb*numQb );
716
717 // generate random unitaries
718 vector<QMatrix> ops;
719 for (int i=0; i<numOps; i++)
720 ops.push_back(getRandomUnitary(numQb));
721
722 // generate random weights
723 vector<qreal> weights(numOps);
724 for (int i=0; i<numOps; i++)
725 weights[i] = getRandomReal(0, 1);
726
727 // normalise random weights
728 qreal weightSum = 0;
729 for (int i=0; i<numOps; i++)
730 weightSum += weights[i];
731 for (int i=0; i<numOps; i++)
732 weights[i] = sqrt((qreal) weights[i]/weightSum);
733
734 // normalise ops
735 for (int i=0; i<numOps; i++)
736 ops[i] *= weights[i];
737
738 // check what we produced was a valid Kraus map
739 QMatrix iden = getIdentityMatrix(1 << numQb);
740 QMatrix prodSum = getZeroMatrix(1 << numQb);
741 for (int i=0; i<numOps; i++)
742 prodSum += getConjugateTranspose(ops[i]) * ops[i];
743
744 // in the rare scenario it is insufficiently numerically precise,
745 // replace the map with trivially precise diagonals
746 if( ! areEqual(prodSum, iden) )
747 for (int i=0; i<numOps; i++)
748 ops[i] = weights[i] * getRandomDiagonalUnitary(numQb);
749
750 return ops;
751}
752
753vector<QVector> getRandomOrthonormalVectors(int numQb, int numStates) {
754 DEMAND( numQb >= 1 );
755 DEMAND( numStates >= 1);
756
757 // set of orthonormal vectors
758 vector<QVector> vecs;
759
760 for (int n=0; n<numStates; n++) {
761
762 QVector vec = getRandomStateVector(numQb);
763
764 // orthogonalise by substracting projections of existing vectors
765 for (int m=0; m<n; m++) {
766 qcomp prod = vec * vecs[m];
767 vec -= (prod * vecs[m]);
768 }
769
770 // renormalise
771 vec = getNormalised(vec);
772
773 // add to orthonormal set
774 vecs.push_back(vec);
775 }
776
777 return vecs;
778}
779
780QMatrix getMixedDensityMatrix(vector<qreal> probs, vector<QVector> states) {
781 DEMAND( probs.size() == states.size() );
782 DEMAND( probs.size() >= 1 );
783
784 QMatrix matr = getZeroMatrix(states[0].size());
785
786 for (size_t i=0; i<probs.size(); i++)
787 matr += probs[i] * getPureDensityMatrix(states[i]);
788
789 return matr;
790}
791
793 REQUIRE( in.size() > 0 );
794
795 size_t dim = in.size();
796 qreal ampFac = 1 / sqrt( dim );
797 qreal phaseFac = 2 * M_PI / dim;
798
799 QVector dftVec = QVector(dim);
800
801 for (size_t x=0; x<dim; x++) {
802 dftVec[x] = 0;
803 for (size_t y=0; y<dim; y++)
804 dftVec[x] += expI(phaseFac * x * y) * in[y];
805 dftVec[x] *= ampFac;
806 }
807 return dftVec;
808}
809
810long long int getValueOfTargets(long long int ind, int* targs, int numTargs) {
811 DEMAND( ind >= 0 );
812
813 long long int val = 0;
814
815 for (int t=0; t<numTargs; t++)
816 val += ((ind >> targs[t]) & 1) * (1LL << t);
817
818 return val;
819}
820
821long long int setBit(long long int num, int bitInd, int bitVal) {
822 DEMAND( (bitVal == 0 || bitVal == 1) );
823 DEMAND( num >= 0 );
824 DEMAND( bitInd >= 0 );
825
826 return (num & ~(1UL << bitInd)) | (bitVal << bitInd);
827}
828
829long long int getIndexOfTargetValues(long long int ref, int* targs, int numTargs, long long int targVal) {
830 // ref state is the starting index, where the targets can be in any bit state;
831 // on the bits of the non-target qubits matter
832
833 for (int t=0; t<numTargs; t++) {
834 int bit = (targVal >> t) & 1;
835 ref = setBit(ref, targs[t], bit);
836 }
837 return ref;
838}
839
840QVector getDFT(QVector in, int* targs, int numTargs) {
841
842 QVector out = QVector(in.size());
843 long long int inDim = (long long int) in.size();
844 long long int targDim = (1LL << numTargs);
845
846 for (long long int j=0; j<inDim; j++) {
847
848 // |j> = |x> (x) |...>, but mixed (not separated)
849 long long int x = getValueOfTargets(j, targs, numTargs);
850
851 for (long long int y=0; y<targDim; y++) {
852
853 // modifies sum_y |y> (x) ...
854 long long int outInd = getIndexOfTargetValues(j, targs, numTargs, y);
855
856 qcomp elem = (in[j] / sqrt(pow(2,numTargs))) * expI(2*M_PI * x * y / pow(2,numTargs));
857 out[outInd] += elem;
858 }
859 }
860
861 return out;
862}
863
864/* (do not generate doxygen doc)
865 *
866 * Overloads for applyReferenceOp, to conveniently specify all families of
867 * unitary operations on state-vectors.
868 */
870 QVector &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
871) {
872 int numQubits = calcLog2(state.size());
873 QMatrix fullOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
874 state = fullOp * state;
875}
877 QVector &state, int* ctrls, int numCtrls, int targ1, int targ2, QMatrix op
878) {
879 int targs[2] = {targ1, targ2};
880 applyReferenceOp(state, ctrls, numCtrls, targs, 2, op);
881}
883 QVector &state, int* ctrls, int numCtrls, int target, QMatrix op
884) {
885 int targs[1] = {target};
886 applyReferenceOp(state, ctrls, numCtrls, targs, 1, op);
887}
889 QVector &state, int *targs, int numTargs, QMatrix op
890) {
891 applyReferenceOp(state, NULL, 0, targs, numTargs, op);
892}
894 QVector &state, int ctrl, int targ, QMatrix op
895) {
896 int ctrls[1] = {ctrl};
897 int targs[1] = {targ};
898 applyReferenceOp(state, ctrls, 1, targs, 1, op);
899}
901 QVector &state, int ctrl, int* targs, int numTargs, QMatrix op
902) {
903 int ctrls[1] = {ctrl};
904 applyReferenceOp(state, ctrls, 1, targs, numTargs, op);
905}
907 QVector &state, int ctrl, int targ1, int targ2, QMatrix op
908) {
909 int ctrls[1] = {ctrl};
910 int targs[2] = {targ1, targ2};
911 applyReferenceOp(state, ctrls, 1, targs, 2, op);
912}
914 QVector &state, int targ, QMatrix op
915) {
916 int targs[1] = {targ};
917 applyReferenceOp(state, NULL, 0, targs, 1, op);
918}
919
920/* (do not generate doxygen doc)
921 *
922 * Overloads for applyReferenceOp, to conveniently specify all families of
923 * unitary operations on state-vectors.
924 */
926 QMatrix &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
927) {
928 int numQubits = calcLog2(state.size());
929 QMatrix leftOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
930 QMatrix rightOp = getConjugateTranspose(leftOp);
931 state = leftOp * state * rightOp;
932}
934 QMatrix &state, int* ctrls, int numCtrls, int targ1, int targ2, QMatrix op
935) {
936 int targs[2] = {targ1, targ2};
937 applyReferenceOp(state, ctrls, numCtrls, targs, 2, op);
938}
940 QMatrix &state, int* ctrls, int numCtrls, int target, QMatrix op
941) {
942 int targs[1] = {target};
943 applyReferenceOp(state, ctrls, numCtrls, targs, 1, op);
944}
946 QMatrix &state, int *targs, int numTargs, QMatrix op
947) {
948 applyReferenceOp(state, NULL, 0, targs, numTargs, op);
949}
951 QMatrix &state, int ctrl, int targ, QMatrix op
952) {
953 int ctrls[1] = {ctrl};
954 int targs[1] = {targ};
955 applyReferenceOp(state, ctrls, 1, targs, 1, op);
956}
958 QMatrix &state, int ctrl, int* targs, int numTargs, QMatrix op
959) {
960 int ctrls[1] = {ctrl};
961 applyReferenceOp(state, ctrls, 1, targs, numTargs, op);
962}
964 QMatrix &state, int ctrl, int targ1, int targ2, QMatrix op
965) {
966 int ctrls[1] = {ctrl};
967 int targs[2] = {targ1, targ2};
968 applyReferenceOp(state, ctrls, 1, targs, 2, op);
969}
971 QMatrix &state, int targ, QMatrix op
972) {
973 int targs[1] = {targ};
974 applyReferenceOp(state, NULL, 0, targs, 1, op);
975}
976
977/* (do not generate doxygen doc)
978 *
979 * Overloads for applyReferenceMatrix, to simply left-multiply a matrix (possibly
980 * with additional control qubits) onto a state.
981 */
983 QVector &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
984) {
985 // for state-vectors, the op is always just left-multiplied
986 applyReferenceOp(state, ctrls, numCtrls, targs, numTargs, op);
987}
989 QVector &state, int *targs, int numTargs, QMatrix op
990) {
991 // for state-vectors, the op is always just left-multiplied
992 applyReferenceOp(state, targs, numTargs, op);
993}
995 QMatrix &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
996) {
997 // for density matrices, op is left-multiplied only
998 int numQubits = calcLog2(state.size());
999 QMatrix leftOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
1000 state = leftOp * state;
1001}
1003 QMatrix &state, int *targs, int numTargs, QMatrix op
1004) {
1005 applyReferenceMatrix(state, NULL, 0, targs, numTargs, op);
1006}
1007
1008bool areEqual(Qureg qureg1, Qureg qureg2, qreal precision) {
1009 DEMAND( qureg1.isDensityMatrix == qureg2.isDensityMatrix );
1010 DEMAND( qureg1.numAmps == qureg2.numAmps );
1011
1012 copyStateFromGPU(qureg1);
1013 copyStateFromGPU(qureg2);
1014 syncQuESTEnv();
1015
1016 // loop terminates when areEqual = 0
1017 int ampsAgree = 1;
1018 for (long long int i=0; ampsAgree && i<qureg1.numAmpsPerNode; i++)
1019 ampsAgree = absComp(qureg1.cpuAmps[i] - qureg2.cpuAmps[i]) < precision;
1020
1021 // if one node's partition wasn't equal, all-nodes must report not-equal
1022 int allAmpsAgree = ampsAgree;
1023#if COMPILE_MPI
1024 MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
1025#endif
1026
1027 return allAmpsAgree;
1028}
1029bool areEqual(Qureg qureg1, Qureg qureg2) {
1030 return areEqual(qureg1, qureg2, REAL_EPS);
1031}
1032
1033bool areEqual(Qureg qureg, QVector vec, qreal precision) {
1034 DEMAND( !qureg.isDensityMatrix );
1035 DEMAND( (int) vec.size() == qureg.numAmps );
1036
1037 copyStateFromGPU(qureg);
1038 syncQuESTEnv();
1039
1040 // the starting index in vec of this node's qureg partition.
1041 long long int startInd = qureg.rank * qureg.numAmpsPerNode;
1042
1043 int ampsAgree = 1;
1044 for (long long int i=0; i<qureg.numAmpsPerNode; i++) {
1045 qcomp dif = (qureg.cpuAmps[i] - vec[startInd+i]);
1046
1047 if (absComp(dif) > precision) {
1048 ampsAgree = 0;
1049
1050 // debug
1051 char buff[200];
1052 snprintf(buff, 200, "Disagreement at %lld of (%s) + i(%s):\n\t%s + i(%s) VS %s + i(%s)\n",
1053 startInd+i,
1056 printf(buff,
1057 real(dif), imag(dif),
1058 real(qureg.cpuAmps[i]), imag(qureg.cpuAmps[i]),
1059 real(vec[startInd+i]), imag(vec[startInd+i]));
1060
1061 break;
1062 }
1063 }
1064
1065 // if one node's partition wasn't equal, all-nodes must report not-equal
1066 int allAmpsAgree = ampsAgree;
1067#if COMPILE_MPI
1068 MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
1069#endif
1070
1071 return allAmpsAgree;
1072}
1073bool areEqual(Qureg qureg, QVector vec) {
1074 return areEqual(qureg, vec, REAL_EPS);
1075}
1076
1077bool areEqual(Qureg qureg, QMatrix matr, qreal precision) {
1078 DEMAND( qureg.isDensityMatrix );
1079 DEMAND( (long long int) (matr.size()*matr.size()) == qureg.numAmps );
1080
1081 // ensure local qureg amps is up to date
1082 copyStateFromGPU(qureg);
1083 syncQuESTEnv();
1084
1085 // the starting index in vec of this node's qureg partition.
1086 long long int startInd = qureg.rank * qureg.numAmpsPerNode;
1087 long long int globalInd, row, col, i;
1088 int ampsAgree;
1089
1090 // compare each of this node's amplitude to the corresponding matr sub-matrix
1091 for (i=0; i<qureg.numAmpsPerNode; i++) {
1092 globalInd = startInd + i;
1093 row = globalInd % matr.size();
1094 col = globalInd / matr.size();
1095
1096 qreal realDif = absReal(real(qureg.cpuAmps[i]) - real(matr[row][col]));
1097 qreal imagDif = absReal(imag(qureg.cpuAmps[i]) - imag(matr[row][col]));
1098 ampsAgree = (realDif < precision && imagDif < precision);
1099
1100 // DEBUG
1101 if (!ampsAgree) {
1102 char buff[200];
1103 snprintf(buff, 200, "[msg from utilities.cpp] node %d has a disagreement at %lld of (%s) + i(%s):\n\t[qureg] %s + i(%s) VS [ref] %s + i(%s)\n",
1104 qureg.rank, startInd+i,
1107 printf(buff,
1108 realDif, imagDif,
1109 real(qureg.cpuAmps[i]), imag(qureg.cpuAmps[i]),
1110 real(matr[row][col]), imag(matr[row][col]));
1111 }
1112
1113 // break loop as soon as amplitudes disagree
1114 if (!ampsAgree)
1115 break;
1116
1117 /* TODO:
1118 * of the nodes which disagree, the lowest-rank should send its
1119 * disagreeing (i, row, col, stateVec[i]) to rank 0 which should
1120 * report it immediately (before the impending DEMAND failure)
1121 * using FAIL_CHECK, so users can determine nature of disagreement
1122 * (e.g. numerical precision).
1123 * Note FAIL_CHECK accepts << like cout, e.g.
1124 * FAIL_CHECK( "Amp at (" << row << ", " << col ") disagreed" );
1125 */
1126 }
1127
1128 // if one node's partition wasn't equal, all-nodes must report not-equal
1129 int allAmpsAgree = ampsAgree;
1130#if COMPILE_MPI
1131 MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
1132#endif
1133
1134 return allAmpsAgree;
1135}
1136bool areEqual(Qureg qureg, QMatrix matr) {
1137 return areEqual(qureg, matr, REAL_EPS);
1138}
1139
1140bool areEqual(QVector vec, qreal* reals, qreal* imags) {
1141
1142 qreal dif;
1143 for (size_t i=0; i<vec.size(); i++) {
1144 dif = absReal(real(vec[i]) - reals[i]);
1145 if (dif > REAL_EPS)
1146 return false;
1147 dif = absReal(imag(vec[i]) - imags[i]);
1148 if (dif > REAL_EPS)
1149 return false;
1150 }
1151 return true;
1152}
1153
1154bool areEqual(QVector vec, qreal* reals) {
1155 for (size_t i=0; i<vec.size(); i++) {
1156 DEMAND( imag(vec[i]) == 0. );
1157
1158 qreal dif = abs(real(vec[i]) - reals[i]);
1159 if (dif > REAL_EPS)
1160 return false;
1161 }
1162 return true;
1163}
1164
1165/* Copies QMatrix into a CompelxMAtrix struct */
1166#define macro_copyQMatrixToDeprecatedComplexMatrix(dest, src) { \
1167 for (size_t i=0; i<src.size(); i++) { \
1168 for (size_t j=0; j<src.size(); j++) { \
1169 dest.real[i][j] = real(src[i][j]); \
1170 dest.imag[i][j] = imag(src[i][j]); \
1171 } \
1172 } \
1173}
1175 DEMAND( qm.size() == 2 );
1176 ComplexMatrix2 cm;
1177 macro_copyQMatrixToDeprecatedComplexMatrix(cm, qm);
1178 return cm;
1179}
1181 DEMAND( qm.size() == 4 );
1182 ComplexMatrix4 cm;
1183 macro_copyQMatrixToDeprecatedComplexMatrix(cm, qm);
1184 return cm;
1185}
1186
1187/** Copies ComplexMatrix structures into a QMatrix */
1188#define macro_copyComplexMatrix(dest, src, dim) \
1189 for (size_t i=0; i<dim; i++) \
1190 for (size_t j=0; j<dim; j++) \
1191 dest[i][j] = src[i][j];
1192
1193void toComplexMatrixN(QMatrix qm, ComplexMatrixN cm) {
1194 DEMAND( qm.size() == (1u<<cm.numQubits) );
1195 macro_copyComplexMatrix(cm.cpuElems, qm, qm.size());
1196 syncCompMatr(cm);
1197}
1198
1200 QMatrix dest = getZeroMatrix(2);
1201 macro_copyComplexMatrix(dest, src.elems, dest.size());
1202 return dest;
1203}
1205 QMatrix dest = getZeroMatrix(4);
1206 macro_copyComplexMatrix(dest, src.elems, dest.size());
1207 return dest;
1208}
1210 QMatrix dest = getZeroMatrix(1 << src.numQubits);
1211 macro_copyComplexMatrix(dest, src.cpuElems, dest.size());
1212 return dest;
1213}
1214
1216 DEMAND( qureg.isDensityMatrix );
1217#if COMPILE_MPI
1218 DEMAND( qureg.numAmps < MPI_MAX_AMPS_IN_MSG );
1219#endif
1220
1221 // ensure local qureg amps are up to date
1222 copyStateFromGPU(qureg);
1223 syncQuESTEnv();
1224
1225 // collect all amps between all nodes
1226 qcomp* allAmps = qureg.cpuAmps;
1227
1228 // in distributed mode, give every node the full state vector
1229#if COMPILE_MPI
1230 if (qureg.isDistributed) {
1231 allAmps = (qcomp*) malloc(qureg.numAmps * sizeof *allAmps);
1232 MPI_Allgather(
1233 qureg.cpuAmps, qureg.numAmpsPerNode, MPI_QCOMP,
1234 allAmps, qureg.numAmpsPerNode, MPI_QCOMP, MPI_COMM_WORLD);
1235 }
1236#endif
1237
1238 // copy full state vector into a QVector
1239 long long int dim = (1LL << qureg.numQubits);
1240 QMatrix matr = getZeroMatrix(dim);
1241 for (long long int n=0; n<qureg.numAmps; n++)
1242 matr[n%dim][n/dim] = allAmps[n];
1243
1244 // clean up if we malloc'd the distributed array
1245 if (qureg.isDistributed)
1246 free(allAmps);
1247 return matr;
1248}
1249
1251 DEMAND( !qureg.isDensityMatrix );
1252#if COMPILE_MPI
1253 DEMAND( qureg.numAmps < MPI_MAX_AMPS_IN_MSG );
1254#endif
1255
1256 // ensure local qureg amps are up to date
1257 copyStateFromGPU(qureg);
1258 syncQuESTEnv();
1259
1260 qcomp* allAmps = qureg.cpuAmps;
1261
1262 // in distributed mode, give every node the full state vector
1263#if COMPILE_MPI
1264 if (qureg.isDistributed) {
1265 allAmps = (qcomp*) malloc(qureg.numAmps * sizeof *allAmps);
1266
1267 MPI_Allgather(
1268 qureg.cpuAmps, qureg.numAmpsPerNode, MPI_QCOMP,
1269 allAmps, qureg.numAmpsPerNode, MPI_QCOMP, MPI_COMM_WORLD);
1270 }
1271#endif
1272
1273 // copy full state vector into a QVector
1274 QVector vec = QVector(qureg.numAmps);
1275 for (long long int i=0; i<qureg.numAmps; i++)
1276 vec[i] = allAmps[i];
1277
1278 // clean up if we malloc'd distrib array
1279 if (qureg.isDistributed)
1280 free(allAmps);
1281
1282 return vec;
1283}
1284
1286
1287 return vector<qcomp>(matr.cpuElems, matr.cpuElems + matr.numElems);
1288}
1289
1291
1292#if COMPILE_MPI
1293 DEMAND( matr.numElems < MPI_MAX_AMPS_IN_MSG );
1294#endif
1295
1296 vector<qcomp> vec(matr.numElems);
1297
1298 // in distributed mode, give every node the full diagonal operator
1299 if (matr.isDistributed) {
1300 #if COMPILE_MPI
1301 MPI_Allgather(
1302 matr.cpuElems, matr.numElemsPerNode, MPI_QCOMP,
1303 vec.data(), matr.numElemsPerNode, MPI_QCOMP, MPI_COMM_WORLD);
1304 #endif
1305 } else {
1306 vec.assign(matr.cpuElems, matr.cpuElems + matr.numElems);
1307 }
1308
1309 return vec;
1310}
1311
1313 QVector vec = toQVector(in);
1314 QMatrix mat = getZeroMatrix(in.numElems);
1315 for (size_t i=0; i<mat.size(); i++)
1316 mat[i][i] = vec[i];
1317 return mat;
1318}
1319
1321 QMatrix mat = getZeroMatrix(in.numElems);
1322 for (size_t i=0; i<mat.size(); i++)
1323 mat[i][i] = in.cpuElems[i];
1324 return mat;
1325}
1326
1327void toQureg(Qureg qureg, QVector vec) {
1328 DEMAND( !qureg.isDensityMatrix );
1329 DEMAND( qureg.numAmps == (long long int) vec.size() );
1330
1331 syncQuESTEnv();
1332
1333 for (int i=0; i<qureg.numAmpsPerNode; i++) {
1334 int ind = qureg.rank*qureg.numAmpsPerNode + i;
1335 qureg.cpuAmps[i] = vec[ind];
1336 }
1337 copyStateToGPU(qureg);
1338}
1339void toQureg(Qureg qureg, QMatrix mat) {
1340 DEMAND( qureg.isDensityMatrix );
1341 DEMAND( (1LL << qureg.numQubits) == (long long int) mat.size() );
1342
1343 syncQuESTEnv();
1344
1345 int len = (1 << qureg.numQubits);
1346 for (int i=0; i<qureg.numAmpsPerNode; i++) {
1347 int ind = qureg.rank*qureg.numAmpsPerNode + i;
1348 qureg.cpuAmps[i] = mat[ind%len][ind/len];
1349 }
1350 copyStateToGPU(qureg);
1351}
1352
1353PauliStr getRandomPauliStr(int numQubits) {
1354
1355 std::string paulis = "";
1356 for (int i=0; i<numQubits; i++)
1357 paulis += "IXYZ"[getRandomInt(0,4)];
1358
1359 return getPauliStr(paulis);
1360}
1361PauliStr getRandomDiagPauliStr(int numQubits) {
1362
1363 std::string paulis = "";
1364 for (int i=0; i<numQubits; i++)
1365 paulis += "IX"[getRandomInt(0,2)];
1366
1367 return getPauliStr(paulis);
1368}
1369
1370void setRandomPauliSum(qreal* coeffs, pauliOpType* codes, int numQubits, int numTerms) {
1371 int i=0;
1372 for (int n=0; n<numTerms; n++) {
1373 coeffs[n] = getRandomReal(-5, 5);
1374 for (int q=0; q<numQubits; q++)
1375 codes[i++] = (pauliOpType) getRandomInt(0,4);
1376 }
1377}
1378
1379void setRandomPauliSum(PauliHamil hamil, int numQubits) {
1380
1381 for (int n=0; n<hamil.numTerms; n++) {
1382 hamil.coeffs[n] = getRandomReal(-5, 5);
1383 hamil.strings[n] = getRandomPauliStr(numQubits);
1384 }
1385}
1386
1387void setRandomDiagPauliHamil(PauliHamil hamil, int numQubits) {
1388 for (int n=0; n<hamil.numTerms; n++) {
1389 hamil.coeffs[n] = getRandomReal(-5, 5);
1390 hamil.strings[n] = getRandomDiagPauliStr(numQubits);
1391 }
1392}
1393
1394void setRandomTargets(int* targs, int numTargs, int numQb) {
1395 DEMAND( numQb >= 1 );
1396 DEMAND( numTargs >= 1);
1397 DEMAND( numTargs <= numQb );
1398
1399 // create an ordered list of all possible qubits
1400 vector<int> allQb(numQb);
1401 for (int q=0; q<numQb; q++)
1402 allQb[q] = q;
1403
1404 // shuffle all qubits (must be consistent on each node)
1405 std::shuffle(&allQb[0], &allQb[numQb], randomGenerator);
1406
1407 // select numTargs of all qubits
1408 for (int i=0; i<numTargs; i++)
1409 targs[i] = allQb[i];
1410}
1411void setRandomTargets(vector<int> &targs, int numQb) {
1412
1413 setRandomTargets(targs.data(), targs.size(), numQb);
1414}
1415
1416QMatrix toQMatrix(qreal* coeffs, pauliOpType* paulis, int numQubits, int numTerms) {
1417
1418 // produce a numTargs-big matrix 'pauliSum' by pauli-matrix tensoring and summing
1419 QMatrix iMatr{{1,0},{0,1}};
1420 QMatrix xMatr{{0,1},{1,0}};
1421 QMatrix yMatr{{0,-qcomp(0,1)},{qcomp(0,1),0}};
1422 QMatrix zMatr{{1,0},{0,-1}};
1423 QMatrix pauliSum = getZeroMatrix(1<<numQubits);
1424
1425 for (int t=0; t<numTerms; t++) {
1426 QMatrix pauliProd = QMatrix{{1}};
1427
1428 for (int q=0; q<numQubits; q++) {
1429 int i = q + t*numQubits;
1430
1431 QMatrix fac;
1432 pauliOpType code = paulis[i];
1433 if (code == PAULI_I) fac = iMatr;
1434 if (code == PAULI_X) fac = xMatr;
1435 if (code == PAULI_Y) fac = yMatr;
1436 if (code == PAULI_Z) fac = zMatr;
1437 pauliProd = getKroneckerProduct(fac, pauliProd);
1438 }
1439 pauliSum += coeffs[t] * pauliProd;
1440 }
1441
1442 // a now 2^numQubits by 2^numQubits Hermitian matrix
1443 return pauliSum;
1444}
1445
1446// QMatrix toQMatrix(PauliHamil hamil) {
1447// return toQMatrix(hamil.termCoeffs, hamil.pauliCodes, hamil.numQubits, hamil.numSumTerms);
1448// }
1449
1450
1451long long int getTwosComplement(long long int decimal, int numBits) {
1452 DEMAND( decimal >= 0 );
1453 DEMAND( numBits >= 2 );
1454 DEMAND( decimal < (1LL << numBits) );
1455
1456 long long int maxMag = 1LL << (numBits-1);
1457 if (decimal >= maxMag)
1458 return -maxMag + (decimal - maxMag);
1459 else
1460 return decimal;
1461}
1462
1463long long int getUnsigned(long long int twosComp, int numBits) {
1464 DEMAND( numBits >= 2 );
1465 DEMAND( twosComp < (1LL << (numBits-1)) );
1466 DEMAND( twosComp >= - (1LL << (numBits-1)) );
1467
1468 if (twosComp >= 0)
1469 return twosComp;
1470 else
1471 return (1<<numBits) + twosComp;
1472}
1473
1475 QMatrix mat = getZeroMatrix(vec.size());
1476 for (size_t i=0; i<vec.size(); i++)
1477 mat[i][i] = vec[i];
1478 return mat;
1479}
1480
1481// void setDiagMatrixOverrides(QMatrix &matr, int* numQubitsPerReg, int numRegs, enum bitEncoding encoding, long long int* overrideInds, qreal* overridePhases, int numOverrides) {
1482// DEMAND( (encoding == UNSIGNED || encoding == TWOS_COMPLEMENT) );
1483// DEMAND( numRegs > 0 );
1484// DEMAND( numOverrides >= 0 );
1485
1486// int totalQb = 0;
1487// for (int r=0; r<numRegs; r++) {
1488// DEMAND( numQubitsPerReg[r] > 0 );
1489// totalQb += numQubitsPerReg[r];
1490// }
1491// DEMAND( matr.size() == (1 << totalQb) );
1492
1493// // record whether a diagonal index has been already overriden
1494// vector<int> hasBeenOverriden(1 << totalQb);
1495// for (int i=0; i<(1 << totalQb); i++)
1496// hasBeenOverriden[i] = 0;
1497
1498// int flatInd = 0;
1499// for (int v=0; v<numOverrides; v++) {
1500// int matrInd = 0;
1501// int numQubitsLeft = 0;
1502
1503// for (int r=0; r<numRegs; r++) {
1504
1505// if (encoding == UNSIGNED)
1506// matrInd += overrideInds[flatInd] * (1 << numQubitsLeft);
1507// else if (encoding == TWOS_COMPLEMENT)
1508// matrInd += getUnsigned(overrideInds[flatInd], numQubitsPerReg[r]) * (1 << numQubitsLeft);
1509
1510// numQubitsLeft += numQubitsPerReg[r];
1511// flatInd += 1;
1512// }
1513
1514// if (!hasBeenOverriden[matrInd]) {
1515// matr[matrInd][matrInd] = expI(overridePhases[v]);
1516// hasBeenOverriden[matrInd] = 1;
1517// }
1518// }
1519// }
1520
1521static int fn_unique_suffix_id = 0;
1522
1523void setUniqueFilename(char* outFn, int maxlen, char* prefix) {
1524 snprintf(outFn, maxlen, "%s_%d.txt", prefix, fn_unique_suffix_id++);
1525}
1526
1527void writeToFileSynch(char* fn, const string& contents) {
1528
1529 // master node writes
1530 if (getQuESTEnv().rank == 0) {
1531 FILE* file = fopen(fn, "w");
1532 fputs(contents.c_str(), file);
1533 fclose(file);
1534 }
1535
1536 // other nodes wait
1537 syncQuESTEnv();
1538}
1539
1540void deleteFilesWithPrefixSynch(char* prefix) {
1541
1542 // master node deletes all files
1543 if (getQuESTEnv().rank == 0) {
1544 char cmd[200];
1545 snprintf(cmd, 200, "exec rm %s*", prefix);
1546 system(cmd);
1547 }
1548
1549 // other nodes wait
1550 syncQuESTEnv();
1551}
1552
1553/// @private
1554class SubListGenerator : public Catch::Generators::IGenerator<int*> {
1555 int* list;
1556 int* sublist;
1557 int len;
1558 int sublen;
1559 vector<bool> featured;
1560private:
1561 void createSublist() {
1562
1563 // sublist to send to the user
1564 sublist = (int*) malloc(sublen * sizeof *sublist);
1565
1566 // indicates which list members are currently in sublist
1567 featured = vector<bool>(len);
1568 fill(featured.end() - sublen, featured.end(), true);
1569 }
1570
1571 void prepareSublist() {
1572
1573 // choose the next combination
1574 int j=0;
1575 for (int i=0; i<len; i++)
1576 if (featured[i])
1577 sublist[j++] = list[i];
1578
1579 // prepare for permuting
1580 std::sort(sublist, sublist+sublen);
1581 }
1582public:
1583 SubListGenerator(int* elems, int numElems, int numSamps) {
1584
1585 DEMAND( numSamps <= numElems );
1586
1587 // make a record of all elements
1588 len = numElems;
1589 list = (int*) malloc(len * sizeof *list);
1590 for (int i=0; i<len; i++)
1591 list[i] = elems[i];
1592
1593 // prepare sublist
1594 sublen = numSamps;
1595 createSublist();
1596 prepareSublist();
1597 }
1598
1599 SubListGenerator(
1600 Catch::Generators::GeneratorWrapper<int>&& gen,
1601 int numSamps, const int* exclude, int numExclude
1602 ) {
1603 // extract all generator elems
1604 vector<int> elems = vector<int>();
1605 do { elems.push_back(gen.get()); } while (gen.next());
1606
1607 // make (int*) of non-excluded elems
1608 len = 0;
1609 list = (int*) malloc(elems.size() * sizeof *list);
1610 for (size_t i=0; i<elems.size(); i++) {
1611 int elem = elems[i];
1612 bool present = false;
1613 for (int j=0; j<numExclude; j++)
1614 if (elem == exclude[j]) {
1615 present = true;
1616 break;
1617 }
1618 if (!present)
1619 list[len++] = elem;
1620 }
1621
1622 DEMAND( numSamps <= len );
1623
1624 // prepare sublist
1625 sublen = numSamps;
1626 createSublist();
1627 prepareSublist();
1628 }
1629
1630 int* const& get() const override {
1631 return sublist;
1632 }
1633
1634 bool next() override {
1635
1636 // offer next permutation of the current combination
1637 if (std::next_permutation(sublist, sublist+sublen))
1638 return true;
1639
1640 // else generate the next combination
1641 if (std::next_permutation(featured.begin(), featured.end())) {
1642 prepareSublist();
1643 return true;
1644 }
1645
1646 return false;
1647 }
1648
1649 ~SubListGenerator() {
1650 free(list);
1651 free(sublist);
1652 }
1653};
1654Catch::Generators::GeneratorWrapper<int*> sublists(
1655 int* list, int len, int sublen
1656) {
1657 return Catch::Generators::GeneratorWrapper<int*>(
1658 Catch::Detail::make_unique<SubListGenerator>(list, len, sublen));
1659}
1660Catch::Generators::GeneratorWrapper<int*> sublists(
1661 Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps, const int* exclude, int numExclude
1662) {
1663 return Catch::Generators::GeneratorWrapper<int*>(
1664 Catch::Detail::make_unique<SubListGenerator>(std::move(gen), numSamps, exclude, numExclude));
1665}
1666Catch::Generators::GeneratorWrapper<int*> sublists(
1667 Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps, int excluded
1668) {
1669 int exclude[] = {excluded};
1670 return Catch::Generators::GeneratorWrapper<int*>(
1671 Catch::Detail::make_unique<SubListGenerator>(std::move(gen), numSamps, exclude, 1));
1672}
1673Catch::Generators::GeneratorWrapper<int*> sublists(
1674 Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps
1675) {
1676 int exclude[] = {-1}; // non-empty to satisfy MSVC
1677 return Catch::Generators::GeneratorWrapper<int*>(
1678 Catch::Detail::make_unique<SubListGenerator>(std::move(gen), numSamps, exclude, 0));
1679}
1680
1681/// @private
1682template <typename T>
1683class SequenceGenerator : public Catch::Generators::IGenerator<T*> {
1684 T* digits;
1685 int len;
1686 T maxDigit;
1687 int ind;
1688 int seqLen;
1689public:
1690 SequenceGenerator(T maxDigit_, int numDigits) {
1691 ind = 0;
1692 len = numDigits;
1693 maxDigit = maxDigit_;
1694 seqLen = (int) pow(1 + (int) maxDigit, len);
1695 digits = (T*) malloc(numDigits * sizeof *digits);
1696 for (int i=0; i<numDigits; i++)
1697 digits[i] = (T) 0;
1698 ind++;
1699 }
1700
1701 T* const& get() const override {
1702 return digits;
1703 }
1704
1705 bool next() override {
1706 bool isNext = (ind++) < seqLen;
1707 if (isNext) {
1708 int i=0;
1709 while (digits[i] == maxDigit)
1710 digits[i++] = (T) 0;
1711 digits[i] = (T) ((int) digits[i] + 1);
1712 }
1713 return isNext;
1714 }
1715
1716 ~SequenceGenerator() {
1717 free(digits);
1718 }
1719};
1720Catch::Generators::GeneratorWrapper<int*> bitsets(int numBits) {
1721 return Catch::Generators::GeneratorWrapper<int*>(
1722 Catch::Detail::make_unique<SequenceGenerator<int>>(1, numBits));
1723}
1724Catch::Generators::GeneratorWrapper<int*> sequences(int base, int numDigits) {
1725 return Catch::Generators::GeneratorWrapper<int*>(
1726 Catch::Detail::make_unique<SequenceGenerator<int>>(base-1, numDigits));
1727}
1728Catch::Generators::GeneratorWrapper<pauliOpType*> pauliseqs(int numPaulis) {
1729 return Catch::Generators::GeneratorWrapper<pauliOpType*>(
1730 Catch::Detail::make_unique<SequenceGenerator<pauliOpType>>(PAULI_Z, numPaulis));
1731}
QMatrix getExponentialOfPauliMatrix(qreal angle, QMatrix a)
void applyReferenceMatrix(QVector &state, int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op)
void setRandomPauliSum(qreal *coeffs, pauliOpType *codes, int numQubits, int numTerms)
void setUniqueFilename(char *outFn, int maxlen, char *prefix)
Catch::Generators::GeneratorWrapper< int * > sequences(int base, int numDigits)
QMatrix getFullOperatorMatrix(int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op, int numQubits)
ComplexMatrix4 toComplexMatrix4(QMatrix qm)
unsigned int calcLog2(long unsigned int res)
QVector getRandomQVector(int dim)
QMatrix getKetBra(QVector ket, QVector bra)
Catch::Generators::GeneratorWrapper< int * > sublists(int *list, int len, int sublen)
QMatrix getSwapMatrix(int qb1, int qb2, int numQb)
QMatrix getMixedDensityMatrix(vector< qreal > probs, vector< QVector > states)
bool areEqual(QVector a, QVector b)
long long int getTwosComplement(long long int decimal, int numBits)
long long int getValueOfTargets(long long int ind, int *targs, int numTargs)
QVector getNormalised(QVector vec)
void assertQuregAndRefInDebugState(Qureg qureg, QVector ref)
vector< vector< qcomp > > QMatrix
ComplexMatrix2 toComplexMatrix2(QMatrix qm)
void applyReferenceOp(QVector &state, int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op)
Catch::Generators::GeneratorWrapper< pauliOpType * > pauliseqs(int numPaulis)
QVector getDFT(QVector in)
void toComplexMatrixN(QMatrix qm, ComplexMatrixN cm)
vector< QVector > getRandomOrthonormalVectors(int numQb, int numStates)
QVector getMatrixDiagonal(QMatrix matr)
void setRandomDiagPauliHamil(PauliHamil hamil, int numQubits)
void writeToFileSynch(char *fn, const string &contents)
QMatrix getRandomQMatrix(int dim)
void setRandomTargets(int *targs, int numTargs, int numQb)
QMatrix toDiagonalQMatrix(QVector vec)
QMatrix toQMatrix(CompMatr1 src)
QVector getKroneckerProduct(QVector b, QVector a)
vector< qcomp > QVector
void deleteFilesWithPrefixSynch(char *prefix)
QVector toQVector(Qureg qureg)
QMatrix getPureDensityMatrix(QVector state)
long long int getUnsigned(long long int twosComp, int numBits)
Catch::Generators::GeneratorWrapper< int * > bitsets(int numBits)
void toQureg(Qureg qureg, QVector vec)
QuESTEnv getQuESTEnv()
void syncQuESTEnv()
void syncCompMatr(CompMatr matr)
Definition matrices.cpp:373
PauliStr getPauliStr(const char *paulis, int *indices, int numPaulis)
Definition paulis.cpp:76
const char * QREAL_FORMAT_SPECIFIER
Definition precision.h:161
QMatrix getConjugateTranspose(QMatrix a)
QMatrix getExponentialOfDiagonalMatrix(QMatrix a)
QMatrix getIdentityMatrix(size_t dim)
void setSubMatrix(QMatrix &dest, QMatrix sub, size_t r, size_t c)
QMatrix getZeroMatrix(size_t dim)
QVector getRandomStateVector(int numQb)
qcomp getRandomComplex()
QMatrix getRandomPureDensityMatrix(int numQb)
QMatrix getRandomUnitary(int numQb)
void setRandomTestStateSeeds()
qreal getRandomReal(qreal min, qreal max)
QMatrix getRandomDensityMatrix(int numQb)
vector< qreal > getRandomProbabilities(int numProbs)
vector< QMatrix > getRandomKrausMap(int numQb, int numOps)
int getRandomInt(int min, int max)
Definition qureg.h:49