utilities.cpp
Go to the documentation of this file.
1 
7 #include "QuEST.h"
8 #include "utilities.hpp"
9 #include "catch.hpp"
10 #include <random>
11 #include <algorithm>
12 #include <bitset>
13 
14 #ifdef DISTRIBUTED_MODE
15 #include <mpi.h>
16 #endif
17 
18 /* (don't generate doxygen doc)
19  *
20  * preconditions to the internal unit testing functions are checked using
21  * DEMAND rather than Catch2's REQUIRE, so that they are not counted in the
22  * total unit testing statistics (e.g. number of checks passed).
23  */
24 #define DEMAND( cond ) if (!(cond)) FAIL( );
25 
26 QVector operator + (const QVector& v1, const QVector& v2) {
27  DEMAND( v1.size() == v2.size() );
28  QVector out = v1;
29  for (size_t i=0; i<v2.size(); i++)
30  out[i] += v2[i];
31  return out;
32 }
33 QVector operator - (const QVector& v1, const QVector& v2) {
34  DEMAND( v1.size() == v2.size() );
35  QVector out = v1;
36  for (size_t i=0; i<v2.size(); i++)
37  out[i] -= v2[i];
38  return out;
39 }
40 QVector operator * (const qcomp& a, const QVector& v) {
41  QVector out = v;
42  for (size_t i=0; i<v.size(); i++)
43  out[i] *= a;
44  return out;
45 }
46 QVector operator * (const QVector& v, const qcomp& a) {
47  return a * v;
48 }
49 QVector operator / (const QVector& v, const qcomp& a) {
50  DEMAND( abs(a) != 0 );
51  QVector out = v;
52  for (size_t i=0; i<v.size(); i++)
53  out[i] /= a;
54  return out;
55 }
56 qcomp operator * (const QVector &v1, const QVector& v2) {
57  // this is sum_i v1_i conj(v2_i)
58  DEMAND( v1.size() == v2.size() );
59  qcomp out = 0;
60  for (size_t i=0; i<v1.size(); i++)
61  out += v1[i] * conj(v2[i]);
62  return out;
63 }
64 void operator += (QVector& v1, const QVector& v2) { // these violate += returns (fine)
65  v1 = v1 + v2;
66 }
67 void operator -= (QVector& v1, const QVector& v2) {
68  v1 = v1 - v2;
69 }
70 void operator *= (QVector& v1, const qcomp& a) {
71  v1 = v1 * a;
72 }
73 void operator /= (QVector& v1, const qcomp& a) {
74  v1 = v1 / a;
75 }
76 QMatrix operator + (const QMatrix& m1, const QMatrix& m2) {
77  DEMAND( m1.size() == m2.size() );
78  QMatrix out = m1;
79  for (size_t r=0; r<m1.size(); r++)
80  for (size_t c=0; c<m1.size(); c++)
81  out[r][c] += m2[r][c];
82  return out;
83 }
84 QMatrix operator - (const QMatrix& m1, const QMatrix& m2) {
85  DEMAND( m1.size() == m2.size() );
86  QMatrix out = m1;
87  for (size_t r=0; r<m1.size(); r++)
88  for (size_t c=0; c<m1.size(); c++)
89  out[r][c] -= m2[r][c];
90  return out;
91 }
92 QMatrix operator * (const qcomp& a, const QMatrix& m) {
93  QMatrix out = m;
94  for (size_t r=0; r<m.size(); r++)
95  for (size_t c=0; c<m.size(); c++)
96  out[r][c] *= a;
97  return out;
98 }
99 QMatrix operator * (const QMatrix& m, const qcomp& a) {
100  return a * m;
101 }
102 QMatrix operator / (const QMatrix& m, const qcomp& a) {
103  QMatrix out = m;
104  for (size_t r=0; r<m.size(); r++)
105  for (size_t c=0; c<m.size(); c++)
106  out[r][c] /= a;
107  return out;
108 }
109 QMatrix operator * (const QMatrix& m1, const QMatrix& m2) {
110  QMatrix prod = m1; // will be completely overwritten
111  for (size_t r=0; r<m1.size(); r++)
112  for (size_t c=0; c<m1.size(); c++) {
113  prod[r][c] = 0;
114  for (size_t k=0; k<m1.size(); k++)
115  prod[r][c] += m1[r][k] * m2[k][c];
116  }
117  return prod;
118 }
119 void operator += (QMatrix& m1, const QMatrix& m2) {
120  m1 = m1 + m2;
121 }
122 void operator -= (QMatrix& m1, const QMatrix& m2) {
123  m1 = m1 - m2;
124 }
125 void operator *= (QMatrix& m1, const qreal& a) {
126  m1 = m1 * a;
127 }
128 void operator /= (QMatrix& m1, const qreal& a) {
129  m1 = m1 / a;
130 }
131 void operator *= (QMatrix& m1, const QMatrix& m2) {
132  m1 = m1 * m2;
133 }
134 QVector operator * (const QMatrix& m, const QVector& v) {
135  DEMAND( m.size() == v.size() );
136  QVector prod = QVector(v.size());
137  for (size_t r=0; r<v.size(); r++)
138  for (size_t c=0; c<v.size(); c++)
139  prod[r] += m[r][c] * v[c];
140  return prod;
141 }
142 
144 
145  QVector prod = QVector(a.size() * b.size());
146 
147  for (size_t i=0; i<prod.size(); i++)
148  prod[i] = b[i / a.size()] * a[i % a.size()];
149 
150  return prod;
151 }
152 
153 QMatrix getZeroMatrix(size_t dim) {
154  DEMAND( dim > 1 );
155  QMatrix matr = QMatrix(dim);
156  for (size_t i=0; i<dim; i++)
157  matr[i].resize(dim);
158  return matr;
159 }
160 
162  DEMAND( dim > 1 );
163  QMatrix matr = getZeroMatrix(dim);
164  for (size_t i=0; i<dim; i++)
165  matr[i][i] = 1;
166  return matr;
167 }
168 
170  DEMAND( ket.size() == bra.size() );
171  QMatrix mat = getZeroMatrix(ket.size());
172 
173  for (size_t r=0; r<ket.size(); r++)
174  for (size_t c=0; c<ket.size(); c++)
175  mat[r][c] = ket[r] * conj(bra[c]);
176  return mat;
177 }
178 
180  QMatrix prod = getZeroMatrix(a.size() * b.size());
181  for (size_t r=0; r<b.size(); r++)
182  for (size_t c=0; c<b.size(); c++)
183  for (size_t i=0; i<a.size(); i++)
184  for (size_t j=0; j<a.size(); j++)
185  prod[r+b.size()*i][c+b.size()*j] = a[i][j] * b[r][c];
186  return prod;
187 }
188 
190  QMatrix b = a;
191  for (size_t r=0; r<a.size(); r++)
192  for (size_t c=0; c<a.size(); c++)
193  b[r][c] = conj(a[c][r]);
194  return b;
195 }
196 
198 
199  // ensure diagonal
200  for (size_t r=0; r<a.size(); r++)
201  for (size_t c=0; c<a.size(); c++) {
202  if (r == c)
203  continue;
204  DEMAND( real(a[r][c]) == 0. );
205  DEMAND( imag(a[r][c]) == 0. );
206  }
207 
208  // exp(diagonal) = diagonal(exp)
209  QMatrix diag = a;
210  for (size_t i=0; i<a.size(); i++)
211  diag[i][i] = exp(diag[i][i]);
212 
213  return diag;
214 }
215 
217  QMatrix iden = getIdentityMatrix(a.size());
218  QMatrix expo = (cos(angle/2) * iden) + ((qcomp) -1i * sin(angle/2) * a);
219  return expo;
220 }
221 
222 void setSubMatrix(QMatrix &dest, QMatrix sub, size_t r, size_t c) {
223  DEMAND( sub.size() + r <= dest.size() );
224  DEMAND( sub.size() + c <= dest.size() );
225  for (size_t i=0; i<sub.size(); i++)
226  for (size_t j=0; j<sub.size(); j++)
227  dest[r+i][c+j] = sub[i][j];
228 }
229 
230 QMatrix getSwapMatrix(int qb1, int qb2, int numQb) {
231  DEMAND( numQb > 1 );
232  DEMAND( (qb1 >= 0 && qb1 < numQb) );
233  DEMAND( (qb2 >= 0 && qb2 < numQb) );
234 
235  if (qb1 > qb2)
236  std::swap(qb1, qb2);
237 
238  if (qb1 == qb2)
239  return getIdentityMatrix(1 << numQb);
240 
241  QMatrix swap;
242 
243  if (qb2 == qb1 + 1) {
244  // qubits are adjacent
245  swap = QMatrix{{1,0,0,0},{0,0,1,0},{0,1,0,0},{0,0,0,1}};
246 
247  } else {
248  // qubits are distant
249  int block = 1 << (qb2 - qb1);
250  swap = getZeroMatrix(block*2);
251  QMatrix iden = getIdentityMatrix(block/2);
252 
253  // Lemma 3.1 of arxiv.org/pdf/1711.09765.pdf
254  QMatrix p0{{1,0},{0,0}};
255  QMatrix l0{{0,1},{0,0}};
256  QMatrix l1{{0,0},{1,0}};
257  QMatrix p1{{0,0},{0,1}};
258 
259  /* notating a^(n+1) = identity(1<<n) (otimes) a, we construct the matrix
260  * [ p0^(N) l1^N ]
261  * [ l0^(N) p1^N ]
262  * where N = qb2 - qb1 */
263  setSubMatrix(swap, getKroneckerProduct(iden, p0), 0, 0);
264  setSubMatrix(swap, getKroneckerProduct(iden, l0), block, 0);
265  setSubMatrix(swap, getKroneckerProduct(iden, l1), 0, block);
266  setSubMatrix(swap, getKroneckerProduct(iden, p1), block, block);
267  }
268 
269  // pad swap with outer identities
270  if (qb1 > 0)
271  swap = getKroneckerProduct(swap, getIdentityMatrix(1<<qb1));
272  if (qb2 < numQb-1)
273  swap = getKroneckerProduct(getIdentityMatrix(1<<(numQb-qb2-1)), swap);
274 
275  return swap;
276 }
277 
278 /* (don't generate doxygen doc)
279  *
280  * iterates list1 (of length len1) and replaces element oldEl with newEl, which is
281  * gauranteed to be present at most once (between list1 AND list2), though may
282  * not be present at all. If oldEl isn't present in list1, does the same for list2.
283  * list1 is skipped if == NULL. This is used by getFullOperatorMatrix() to ensure
284  * that, when qubits are swapped, their appearences in the remaining qubit lists
285  * are updated.
286  */
287 void updateIndices(int oldEl, int newEl, int* list1, int len1, int* list2, int len2) {
288  if (list1 != NULL) {
289  for (int i=0; i<len1; i++) {
290  if (list1[i] == oldEl) {
291  list1[i] = newEl;
292  return;
293  }
294  }
295  }
296  for (int i=0; i<len2; i++) {
297  if (list2[i] == oldEl) {
298  list2[i] = newEl;
299  return;
300  }
301  }
302 }
303 
305  int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op, int numQubits
306 ) {
307  DEMAND( numCtrls >= 0 );
308  DEMAND( numTargs >= 0 );
309  DEMAND( numQubits >= (numCtrls+numTargs) );
310  DEMAND( op.size() == (1u << numTargs) );
311 
312  // copy {ctrls} and {targs}to restore at end
313  std::vector<int> ctrlsCopy(ctrls, ctrls+numCtrls);
314  std::vector<int> targsCopy(targs, targs+numTargs);
315 
316  // full-state matrix of qubit swaps
317  QMatrix swaps = getIdentityMatrix(1 << numQubits);
318  QMatrix unswaps = getIdentityMatrix(1 << numQubits);
319  QMatrix matr;
320 
321  // swap targs to {0, ..., numTargs-1}
322  for (int i=0; i<numTargs; i++) {
323  if (i != targs[i]) {
324  matr = getSwapMatrix(i, targs[i], numQubits);
325  swaps = matr * swaps;
326  unswaps = unswaps * matr;
327 
328  // even if this is the last targ, ctrls might still need updating
330  i, targs[i], (i < numTargs-1)? &targs[i+1] : NULL,
331  numTargs-i-1, ctrls, numCtrls);
332  }
333  }
334 
335  // swap ctrls to {numTargs, ..., numTargs+numCtrls-1}
336  for (int i=0; i<numCtrls; i++) {
337  int newInd = numTargs+i;
338  if (newInd != ctrls[i]) {
339  matr = getSwapMatrix(newInd, ctrls[i], numQubits);
340  swaps = matr * swaps;
341  unswaps = unswaps * matr;
342 
343  // update remaining ctrls (if any exist)
344  if (i < numCtrls-1)
345  updateIndices(newInd, ctrls[i], NULL, 0, &ctrls[i+1], numCtrls-i-1);
346  }
347  }
348 
349  // construct controlled-op matrix for qubits {0, ..., numCtrls+numTargs-1}
350  size_t dim = 1 << (numCtrls+numTargs);
351  QMatrix fullOp = getIdentityMatrix(dim);
352  setSubMatrix(fullOp, op, dim-op.size(), dim-op.size());
353 
354  // create full-state controlled-op matrix (left-pad identities)
355  if (numQubits > numCtrls+numTargs) {
356  size_t pad = 1 << (numQubits - numCtrls - numTargs);
357  fullOp = getKroneckerProduct(getIdentityMatrix(pad), fullOp);
358  }
359 
360  // apply swap to either side (to swap qubits back and forth)
361  fullOp = unswaps * fullOp * swaps;
362 
363  // restore {ctrls and targs}
364  for (int i=0; i<numCtrls; i++)
365  ctrls[i] = ctrlsCopy[i];
366  for (int i=0; i<numTargs; i++)
367  targs[i] = targsCopy[i];
368 
369  return fullOp;
370 }
371 
372 unsigned int calcLog2(long unsigned int res) {
373  unsigned int n = 0;
374  while (res >>= 1)
375  n++;
376  return n;
377 }
378 
380  DEMAND( dim > 1 );
381 
382  QMatrix matr = getZeroMatrix(dim);
383  for (int i=0; i<dim; i++) {
384  for (int j=0; j<dim; j++) {
385 
386  // generate 2 normally-distributed random numbers via Box-Muller
387  qreal a = rand()/(qreal) RAND_MAX;
388  qreal b = rand()/(qreal) RAND_MAX;
389  qreal r1 = sqrt(-2 * log(a)) * cos(2 * 3.14159265 * b);
390  qreal r2 = sqrt(-2 * log(a)) * sin(2 * 3.14159265 * b);
391 
392  matr[i][j] = r1 + r2 * (qcomp) 1i;
393  }
394  }
395  return matr;
396 }
397 
399  DEMAND( a.size() == b.size() );
400 
401  for (size_t i=0; i<a.size(); i++)
402  if (abs(a[i] - b[i]) > REAL_EPS)
403  return false;
404  return true;
405 }
406 
408  DEMAND( a.size() == b.size() );
409 
410  for (size_t i=0; i<a.size(); i++)
411  for (size_t j=0; j<b.size(); j++)
412  if (abs(a[i][j] - b[i][j]) > REAL_EPS)
413  return false;
414  return true;
415 }
416 
417 qcomp expI(qreal phase) {
418  return qcomp(cos(phase), sin(phase));
419 }
420 
422  DEMAND( min <= max );
423  qreal r = min + (max - min) * (rand() / (qreal) RAND_MAX);
424 
425  // check bounds satisfied
426  DEMAND( r >= min );
427  DEMAND( r <= max );
428  return r;
429 }
430 
432  return getRandomReal(-1,1) + getRandomReal(-1,1) * (qcomp) 1i;
433 }
434 
436  QVector vec = QVector(dim);
437  for (int i=0; i<dim; i++)
438  vec[i] = getRandomComplex();
439 
440  // check we didn't get the impossibly-unlikely zero-amplitude outcome
441  DEMAND( real(vec[0]) != 0 );
442 
443  return vec;
444 }
445 
447  qreal norm = 0;
448  qreal y, t, c;
449  c = 0;
450 
451  for (size_t i=0; i<vec.size(); i++) {
452  y = real(vec[i])*real(vec[i]) - c;
453  t = norm + y;
454  c = ( t - norm ) - y;
455  norm = t;
456 
457  y = imag(vec[i])*imag(vec[i]) - c;
458  t = norm + y;
459  c = ( t - norm ) - y;
460  norm = t;
461  }
462 
463  for (size_t i=0; i<vec.size(); i++)
464  vec[i] /= sqrt(norm);
465  return vec;
466 }
467 
469  return getNormalised(getRandomQVector(1<<numQb));
470 }
471 
472 std::vector<qreal> getRandomProbabilities(int numProbs) {
473 
474  // generate random unnormalised scalars
475  std::vector<qreal> probs;
476  qreal total = 0;
477  for (int i=0; i<numProbs; i++) {
478  qreal prob = getRandomReal(0, 1);
479  probs.push_back(prob);
480  total += prob;
481  }
482 
483  // normalise
484  for (int i=0; i<numProbs; i++)
485  probs[i] /= total;
486 
487  return probs;
488 }
489 
491  DEMAND( numQb > 0 );
492 
493  // generate random probabilities to weight random pure states
494  int dim = 1<<numQb;
495  std::vector<qreal> probs = getRandomProbabilities(dim);
496 
497  // add random pure states
498  QMatrix dens = getZeroMatrix(dim);
499  for (int i=0; i<dim; i++) {
500  QVector pure = getRandomStateVector(numQb);
501  dens += probs[i] * getKetBra(pure, pure);
502  }
503 
504  return dens;
505 }
506 
508  return getKetBra(state, state);
509 }
510 
512  QVector vec = getRandomStateVector(numQb);
513  QMatrix mat = getPureDensityMatrix(vec);
514  return mat;
515 }
516 
518 
519  QVector vec = QVector(matr.size());
520  for (size_t i=0; i<vec.size(); i++)
521  vec[i] = matr[i][i];
522 
523  return vec;
524 }
525 
526 int getRandomInt(int min, int max) {
527  return round(getRandomReal(min, max-1));
528 }
529 
531  DEMAND( numQb >= 1 );
532 
533  QMatrix matr = getRandomQMatrix(1 << numQb);
534 
535  for (size_t i=0; i<matr.size(); i++) {
536  QVector row = matr[i];
537 
538  // compute new orthogonal row by subtracting proj row onto prevs
539  for (int k=i-1; k>=0; k--) {
540 
541  // compute row . prev = sum_n row_n conj(prev_n)
542  qcomp prod = 0;
543  for (size_t n=0; n<row.size(); n++)
544  prod += row[n] * conj(matr[k][n]);
545 
546  // subtract (proj row onto prev) = (prod * prev) from final row
547  for (size_t n=0; n<row.size(); n++)
548  matr[i][n] -= prod * matr[k][n];
549  }
550 
551  // compute row magnitude
552  qreal mag = 0;
553  for (size_t j=0; j<row.size(); j++)
554  mag += pow(abs(matr[i][j]), 2);
555  mag = sqrt(mag);
556 
557  // normalise row
558  for (size_t j=0; j<row.size(); j++)
559  matr[i][j] /= mag;
560  }
561 
562  // ensure matrix is indeed unitary
563  QMatrix conjprod = matr * getConjugateTranspose(matr);
564  QMatrix iden = getIdentityMatrix(1 << numQb);
565 
566  // generating big unitary matrices is hard; if we fail, default to identity
567  if ( numQb >= 3 && !areEqual(conjprod, iden) ) {
568 
569  matr = getIdentityMatrix(1 << numQb);
570  conjprod = matr;
571  }
572  DEMAND( areEqual(conjprod, iden) );
573 
574  // return the new orthonormal matrix
575  return matr;
576 }
577 
578 std::vector<QMatrix> getRandomKrausMap(int numQb, int numOps) {
579  DEMAND( numOps >= 1 );
580  DEMAND( numOps <= 4*numQb*numQb );
581 
582  // generate random unitaries
583  std::vector<QMatrix> ops;
584  for (int i=0; i<numOps; i++)
585  ops.push_back(getRandomUnitary(numQb));
586 
587  // generate random weights
588  qreal weights[numOps];
589  for (int i=0; i<numOps; i++)
590  weights[i] = getRandomReal(0, 1);
591 
592  // normalise random weights
593  qreal weightSum = 0;
594  for (int i=0; i<numOps; i++)
595  weightSum += weights[i];
596  for (int i=0; i<numOps; i++)
597  weights[i] = sqrt(weights[i]/weightSum);
598 
599  // normalise ops
600  for (int i=0; i<numOps; i++)
601  ops[i] *= weights[i];
602 
603  // check what we produced was a valid Kraus map
604  QMatrix iden = getIdentityMatrix(1 << numQb);
605  QMatrix prodSum = getZeroMatrix(1 << numQb);
606  for (int i=0; i<numOps; i++)
607  prodSum += getConjugateTranspose(ops[i]) * ops[i];
608  DEMAND( areEqual(prodSum, iden) );
609 
610  return ops;
611 }
612 
613 std::vector<QVector> getRandomOrthonormalVectors(int numQb, int numStates) {
614  DEMAND( numQb >= 1 );
615  DEMAND( numStates >= 1);
616 
617  // set of orthonormal vectors
618  std::vector<QVector> vecs;
619 
620  for (size_t n=0; n<numStates; n++) {
621 
622  QVector vec = getRandomStateVector(numQb);
623 
624  // orthogonalise by substracting projections of existing vectors
625  for (int m=0; m<n; m++) {
626  qcomp prod = vec * vecs[m];
627  vec -= (prod * vecs[m]);
628  }
629 
630  // renormalise
631  vec = getNormalised(vec);
632 
633  // add to orthonormal set
634  vecs.push_back(vec);
635  }
636 
637  return vecs;
638 }
639 
640 QMatrix getMixedDensityMatrix(std::vector<qreal> probs, std::vector<QVector> states) {
641  DEMAND( probs.size() == states.size() );
642  DEMAND( probs.size() >= 1 );
643 
644  QMatrix matr = getZeroMatrix(states[0].size());
645 
646  for (size_t i=0; i<probs.size(); i++)
647  matr += probs[i] * getPureDensityMatrix(states[i]);
648 
649  return matr;
650 }
651 
653  REQUIRE( in.size() > 0 );
654 
655  size_t dim = in.size();
656  qreal ampFac = 1 / sqrt( dim );
657  qreal phaseFac = 2 * M_PI / dim;
658 
659  QVector dftVec = QVector(dim);
660 
661  for (size_t x=0; x<dim; x++) {
662  dftVec[x] = 0;
663  for (long long int y=0; y<dim; y++)
664  dftVec[x] += expI(phaseFac * x * y) * in[y];
665  dftVec[x] *= ampFac;
666  }
667  return dftVec;
668 }
669 
670 long long int getValueOfTargets(long long int ind, int* targs, int numTargs) {
671  DEMAND( ind >= 0 );
672 
673  long long int val = 0;
674 
675  for (int t=0; t<numTargs; t++)
676  val += ((ind >> targs[t]) & 1) * (1LL << t);
677 
678  return val;
679 }
680 
681 long long int setBit(long long int num, int bitInd, int bitVal) {
682  DEMAND( (bitVal == 0 || bitVal == 1) );
683  DEMAND( num >= 0 );
684  DEMAND( bitInd >= 0 );
685 
686  return (num & ~(1UL << bitInd)) | (bitVal << bitInd);
687 }
688 
689 long long int getIndexOfTargetValues(long long int ref, int* targs, int numTargs, int targVal) {
690  // ref state is the starting index, where the targets can be in any bit state;
691  // on the bits of the non-target qubits matter
692 
693  for (int t=0; t<numTargs; t++) {
694  int bit = (targVal >> t) & 1;
695  ref = setBit(ref, targs[t], bit);
696  }
697  return ref;
698 }
699 
700 QVector getDFT(QVector in, int* targs, int numTargs) {
701 
702  QVector out = QVector(in.size());
703  long long int targDim = (1LL << numTargs);
704 
705  for (size_t j=0; j<in.size(); j++) {
706 
707  // |j> = |x> (x) |...>, but mixed (not separated)
708  long long int x = getValueOfTargets(j, targs, numTargs);
709 
710  for (long long int y=0; y<targDim; y++) {
711 
712  // modifies sum_y |y> (x) ...
713  long long int outInd = getIndexOfTargetValues(j, targs, numTargs, y);
714 
715  qcomp elem = (in[j] / sqrt(pow(2,numTargs))) * expI(2*M_PI * x * y / pow(2,numTargs));
716  out[outInd] += elem;
717  }
718  }
719 
720  return out;
721 }
722 
723 /* (do not generate doxygen doc)
724  *
725  * Overloads for applyReferenceOp, to conveniently specify all families of
726  * unitary operations on state-vectors.
727  */
729  QVector &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
730 ) {
731  int numQubits = calcLog2(state.size());
732  QMatrix fullOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
733  state = fullOp * state;
734 }
736  QVector &state, int* ctrls, int numCtrls, int targ1, int targ2, QMatrix op
737 ) {
738  int targs[2] = {targ1, targ2};
739  applyReferenceOp(state, ctrls, numCtrls, targs, 2, op);
740 }
742  QVector &state, int* ctrls, int numCtrls, int target, QMatrix op
743 ) {
744  int targs[1] = {target};
745  applyReferenceOp(state, ctrls, numCtrls, targs, 1, op);
746 }
748  QVector &state, int *targs, int numTargs, QMatrix op
749 ) {
750  applyReferenceOp(state, NULL, 0, targs, numTargs, op);
751 }
753  QVector &state, int ctrl, int targ, QMatrix op
754 ) {
755  int ctrls[1] = {ctrl};
756  int targs[1] = {targ};
757  applyReferenceOp(state, ctrls, 1, targs, 1, op);
758 }
760  QVector &state, int ctrl, int* targs, int numTargs, QMatrix op
761 ) {
762  int ctrls[1] = {ctrl};
763  applyReferenceOp(state, ctrls, 1, targs, numTargs, op);
764 }
766  QVector &state, int ctrl, int targ1, int targ2, QMatrix op
767 ) {
768  int ctrls[1] = {ctrl};
769  int targs[2] = {targ1, targ2};
770  applyReferenceOp(state, ctrls, 1, targs, 2, op);
771 }
773  QVector &state, int targ, QMatrix op
774 ) {
775  int targs[1] = {targ};
776  applyReferenceOp(state, NULL, 0, targs, 1, op);
777 }
778 
779 /* (do not generate doxygen doc)
780  *
781  * Overloads for applyReferenceOp, to conveniently specify all families of
782  * unitary operations on state-vectors.
783  */
785  QMatrix &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
786 ) {
787  int numQubits = calcLog2(state.size());
788  QMatrix leftOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
789  QMatrix rightOp = getConjugateTranspose(leftOp);
790  state = leftOp * state * rightOp;
791 }
793  QMatrix &state, int* ctrls, int numCtrls, int targ1, int targ2, QMatrix op
794 ) {
795  int targs[2] = {targ1, targ2};
796  applyReferenceOp(state, ctrls, numCtrls, targs, 2, op);
797 }
799  QMatrix &state, int* ctrls, int numCtrls, int target, QMatrix op
800 ) {
801  int targs[1] = {target};
802  applyReferenceOp(state, ctrls, numCtrls, targs, 1, op);
803 }
805  QMatrix &state, int *targs, int numTargs, QMatrix op
806 ) {
807  applyReferenceOp(state, NULL, 0, targs, numTargs, op);
808 }
810  QMatrix &state, int ctrl, int targ, QMatrix op
811 ) {
812  int ctrls[1] = {ctrl};
813  int targs[1] = {targ};
814  applyReferenceOp(state, ctrls, 1, targs, 1, op);
815 }
817  QMatrix &state, int ctrl, int* targs, int numTargs, QMatrix op
818 ) {
819  int ctrls[1] = {ctrl};
820  applyReferenceOp(state, ctrls, 1, targs, numTargs, op);
821 }
823  QMatrix &state, int ctrl, int targ1, int targ2, QMatrix op
824 ) {
825  int ctrls[1] = {ctrl};
826  int targs[2] = {targ1, targ2};
827  applyReferenceOp(state, ctrls, 1, targs, 2, op);
828 }
830  QMatrix &state, int targ, QMatrix op
831 ) {
832  int targs[1] = {targ};
833  applyReferenceOp(state, NULL, 0, targs, 1, op);
834 }
835 
836 /* (do not generate doxygen doc)
837  *
838  * Overloads for applyReferenceMatrix, to simply left-multiply a matrix (possibly
839  * with additional control qubits) onto a state.
840  */
842  QVector &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
843 ) {
844  // for state-vectors, the op is always just left-multiplied
845  applyReferenceOp(state, ctrls, numCtrls, targs, numTargs, op);
846 }
848  QMatrix &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
849 ) {
850  // for density matrices, op is left-multiplied only
851  int numQubits = calcLog2(state.size());
852  QMatrix leftOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
853  state = leftOp * state;
854 }
855 
856 bool areEqual(Qureg qureg1, Qureg qureg2, qreal precision) {
857  DEMAND( qureg1.isDensityMatrix == qureg2.isDensityMatrix );
858  DEMAND( qureg1.numAmpsTotal == qureg2.numAmpsTotal );
859 
860  copyStateFromGPU(qureg1);
861  copyStateFromGPU(qureg2);
863 
864  // loop terminates when areEqual = 0
865  int ampsAgree = 1;
866  for (long long int i=0; ampsAgree && i<qureg1.numAmpsPerChunk; i++)
867  ampsAgree = (
868  absReal(qureg1.stateVec.real[i] - qureg2.stateVec.real[i]) < precision
869  && absReal(qureg1.stateVec.imag[i] - qureg2.stateVec.imag[i]) < precision);
870 
871  // if one node's partition wasn't equal, all-nodes must report not-equal
872  int allAmpsAgree = ampsAgree;
873 #ifdef DISTRIBUTED_MODE
874  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
875 #endif
876 
877  return allAmpsAgree;
878 }
879 bool areEqual(Qureg qureg1, Qureg qureg2) {
880  return areEqual(qureg1, qureg2, REAL_EPS);
881 }
882 
883 bool areEqual(Qureg qureg, QVector vec, qreal precision) {
884  DEMAND( !qureg.isDensityMatrix );
885  DEMAND( (int) vec.size() == qureg.numAmpsTotal );
886 
887  copyStateFromGPU(qureg);
889 
890  // the starting index in vec of this node's qureg partition.
891  long long int startInd = qureg.chunkId * qureg.numAmpsPerChunk;
892 
893  int ampsAgree = 1;
894  for (long long int i=0; i<qureg.numAmpsPerChunk; i++) {
895  qreal realDif = absReal(qureg.stateVec.real[i] - real(vec[startInd+i]));
896  qreal imagDif = absReal(qureg.stateVec.imag[i] - imag(vec[startInd+i]));
897 
898  if (realDif > precision || imagDif > precision) {
899  ampsAgree = 0;
900 
901  // debug
902  char buff[200];
903  sprintf(buff, "Disagreement at %lld of (%s) + i(%s):\n\t%s + i(%s) VS %s + i(%s)\n",
904  startInd+i,
905  REAL_STRING_FORMAT, REAL_STRING_FORMAT, REAL_STRING_FORMAT,
906  REAL_STRING_FORMAT, REAL_STRING_FORMAT, REAL_STRING_FORMAT);
907  printf(buff,
908  realDif, imagDif,
909  qureg.stateVec.real[i], qureg.stateVec.imag[i],
910  real(vec[startInd+i]), imag(vec[startInd+i]));
911 
912  break;
913  }
914  }
915 
916  // if one node's partition wasn't equal, all-nodes must report not-equal
917  int allAmpsAgree = ampsAgree;
918 #ifdef DISTRIBUTED_MODE
919  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
920 #endif
921 
922  return allAmpsAgree;
923 }
924 bool areEqual(Qureg qureg, QVector vec) {
925  return areEqual(qureg, vec, REAL_EPS);
926 }
927 
928 bool areEqual(Qureg qureg, QMatrix matr, qreal precision) {
929  DEMAND( qureg.isDensityMatrix );
930  DEMAND( (long long int) (matr.size()*matr.size()) == qureg.numAmpsTotal );
931 
932  // ensure local qureg.stateVec is up to date
933  copyStateFromGPU(qureg);
935 
936  // the starting index in vec of this node's qureg partition.
937  long long int startInd = qureg.chunkId * qureg.numAmpsPerChunk;
938  long long int globalInd, row, col, i;
939  int ampsAgree;
940 
941  // compare each of this node's amplitude to the corresponding matr sub-matrix
942  for (i=0; i<qureg.numAmpsPerChunk; i++) {
943  globalInd = startInd + i;
944  row = globalInd % matr.size();
945  col = globalInd / matr.size();
946  qreal realDif = absReal(qureg.stateVec.real[i] - real(matr[row][col]));
947  qreal imagDif = absReal(qureg.stateVec.imag[i] - imag(matr[row][col]));
948  ampsAgree = (realDif < precision && imagDif < precision);
949 
950  // DEBUG
951  if (!ampsAgree) {
952 
953  // debug
954  char buff[200];
955  sprintf(buff, "[msg from utilities.cpp] node %d has a disagreement at (global) index %lld of (%s) + i(%s)\n",
956  qureg.chunkId, globalInd, REAL_STRING_FORMAT, REAL_STRING_FORMAT);
957  printf(buff, realDif, imagDif);
958  }
959 
960  // break loop as soon as amplitudes disagree
961  if (!ampsAgree)
962  break;
963 
964  /* TODO:
965  * of the nodes which disagree, the lowest-rank should send its
966  * disagreeing (i, row, col, stateVec[i]) to rank 0 which should
967  * report it immediately (before the impending DEMAND failure)
968  * using FAIL_CHECK, so users can determine nature of disagreement
969  * (e.g. numerical precision).
970  * Note FAIL_CHECK accepts << like cout, e.g.
971  * FAIL_CHECK( "Amp at (" << row << ", " << col ") disagreed" );
972  */
973  }
974 
975  // if one node's partition wasn't equal, all-nodes must report not-equal
976  int allAmpsAgree = ampsAgree;
977 #ifdef DISTRIBUTED_MODE
978  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
979 #endif
980 
981  return allAmpsAgree;
982 }
983 bool areEqual(Qureg qureg, QMatrix matr) {
984  return areEqual(qureg, matr, REAL_EPS);
985 }
986 
987 bool areEqual(QVector vec, qreal* reals, qreal* imags) {
988 
989  qreal dif;
990  for (size_t i=0; i<vec.size(); i++) {
991  dif = absReal(real(vec[i]) - reals[i]);
992  if (dif > REAL_EPS)
993  return false;
994  dif = absReal(imag(vec[i]) - imags[i]);
995  if (dif > REAL_EPS)
996  return false;
997  }
998  return true;
999 }
1000 
1001 bool areEqual(QVector vec, qreal* reals) {
1002  for (size_t i=0; i<vec.size(); i++) {
1003  DEMAND( imag(vec[i]) == 0. );
1004 
1005  qreal dif = abs(real(vec[i]) - reals[i]);
1006  if (dif > REAL_EPS)
1007  return false;
1008  }
1009  return true;
1010 }
1011 
1012 /* Copies QMatrix into a CompelxMAtrix struct */
1013 #define macro_copyQMatrix(dest, src) { \
1014  for (size_t i=0; i<src.size(); i++) { \
1015  for (size_t j=0; j<src.size(); j++) { \
1016  dest.real[i][j] = real(src[i][j]); \
1017  dest.imag[i][j] = imag(src[i][j]); \
1018  } \
1019  } \
1020 }
1022  DEMAND( qm.size() == 2 );
1023  ComplexMatrix2 cm;
1024  macro_copyQMatrix(cm, qm);
1025  return cm;
1026 }
1028  DEMAND( qm.size() == 4 );
1029  ComplexMatrix4 cm;
1030  macro_copyQMatrix(cm, qm);
1031  return cm;
1032 }
1034  DEMAND( qm.size() == (1u<<cm.numQubits) );
1035  macro_copyQMatrix(cm, qm);
1036 }
1037 
1039 #define macro_copyComplexMatrix(dest, src) { \
1040  for (size_t i=0; i<dest.size(); i++) \
1041  for (size_t j=0; j<dest.size(); j++) \
1042  dest[i][j] = qcomp(src.real[i][j], src.imag[i][j]); \
1043 }
1045  QMatrix dest = getZeroMatrix(2);
1046  macro_copyComplexMatrix(dest, src);
1047  return dest;
1048 }
1050  QMatrix dest = getZeroMatrix(4);
1051  macro_copyComplexMatrix(dest, src);
1052  return dest;
1053 }
1055  DEMAND( src.real != NULL );
1056  DEMAND( src.imag != NULL );
1057  QMatrix dest = getZeroMatrix(1 << src.numQubits);
1058  macro_copyComplexMatrix(dest, src);
1059  return dest;
1060 }
1061 
1063  qcomp a = qcomp(alpha.real, alpha.imag);
1064  qcomp b = qcomp(beta.real, beta.imag);
1065  QMatrix matr{
1066  {a, -conj(b)},
1067  {b, conj(a)}};
1068  return matr;
1069 }
1070 
1072  DEMAND( qureg.isDensityMatrix );
1073 #ifdef DISTRIBUTED_MODE
1074  DEMAND( qureg.numAmpsTotal < MPI_MAX_AMPS_IN_MSG );
1075 #endif
1076 
1077  // ensure local qureg.stateVec is up to date
1078  copyStateFromGPU(qureg);
1080 
1081  qreal* fullRe;
1082  qreal* fullIm;
1083 
1084  // in distributed mode, give every node the full state vector
1085 #ifdef DISTRIBUTED_MODE
1086  fullRe = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullRe);
1087  fullIm = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullIm);
1088  MPI_Allgather(
1089  qureg.stateVec.real, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
1090  fullRe, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
1091  MPI_Allgather(
1092  qureg.stateVec.imag, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
1093  fullIm, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
1094 #else
1095  fullRe = qureg.stateVec.real;
1096  fullIm = qureg.stateVec.imag;
1097 #endif
1098 
1099  // copy full state vector into a QVector
1100  long long int dim = (1 << qureg.numQubitsRepresented);
1101  QMatrix matr = getZeroMatrix(dim);
1102  for (long long int n=0; n<qureg.numAmpsTotal; n++)
1103  matr[n%dim][n/dim] = qcomp(fullRe[n], fullIm[n]);
1104 
1105  // clean up if we malloc'd the distributed array
1106 #ifdef DISTRIBUTED_MODE
1107  free(fullRe);
1108  free(fullIm);
1109 #endif
1110  return matr;
1111 }
1112 
1114  DEMAND( !qureg.isDensityMatrix );
1115 #ifdef DISTRIBUTED_MODE
1116  DEMAND( qureg.numAmpsTotal < MPI_MAX_AMPS_IN_MSG );
1117 #endif
1118 
1119  // ensure local qureg.stateVec is up to date
1120  copyStateFromGPU(qureg);
1122 
1123  qreal* fullRe;
1124  qreal* fullIm;
1125 
1126  // in distributed mode, give every node the full state vector
1127 #ifdef DISTRIBUTED_MODE
1128  fullRe = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullRe);
1129  fullIm = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullIm);
1130 
1131  MPI_Allgather(
1132  qureg.stateVec.real, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
1133  fullRe, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
1134  MPI_Allgather(
1135  qureg.stateVec.imag, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
1136  fullIm, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
1137 #else
1138  fullRe = qureg.stateVec.real;
1139  fullIm = qureg.stateVec.imag;
1140 #endif
1141 
1142  // copy full state vector into a QVector
1143  QVector vec = QVector(qureg.numAmpsTotal);
1144  for (long long int i=0; i<qureg.numAmpsTotal; i++)
1145  vec[i] = qcomp(fullRe[i], fullIm[i]);
1146 
1147  // clean up if we malloc'd distrib array
1148 #ifdef DISTRIBUTED_MODE
1149  free(fullRe);
1150  free(fullIm);
1151 #endif
1152  return vec;
1153 }
1154 
1156  long long int totalElems = (1LL << op.numQubits);
1157 #ifdef DISTRIBUTED_MODE
1158  DEMAND( totalElems < MPI_MAX_AMPS_IN_MSG );
1159 #endif
1160 
1161  qreal* fullRe;
1162  qreal* fullIm;
1163 
1164  // in distributed mode, give every node the full diagonal operator
1165 #ifdef DISTRIBUTED_MODE
1166  fullRe = (qreal*) malloc(totalElems * sizeof *fullRe);
1167  fullIm = (qreal*) malloc(totalElems * sizeof *fullIm);
1168 
1169  MPI_Allgather(
1170  op.real, op.numElemsPerChunk, MPI_QuEST_REAL,
1171  fullRe, op.numElemsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
1172  MPI_Allgather(
1173  op.imag, op.numElemsPerChunk, MPI_QuEST_REAL,
1174  fullIm, op.numElemsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
1175 #else
1176  fullRe = op.real;
1177  fullIm = op.imag;
1178 #endif
1179 
1180  // copy full state vector into a QVector
1181  QVector vec = QVector(totalElems);
1182  for (long long int i=0; i<totalElems; i++)
1183  vec[i] = qcomp(fullRe[i], fullIm[i]);
1184 
1185  // clean up if we malloc'd distrib array
1186 #ifdef DISTRIBUTED_MODE
1187  free(fullRe);
1188  free(fullIm);
1189 #endif
1190  return vec;
1191 }
1192 
1194  QVector vec = toQVector(op);
1195  QMatrix mat = getZeroMatrix(1LL << op.numQubits);
1196  for (size_t i=0; i<mat.size(); i++)
1197  mat[i][i] = vec[i];
1198  return mat;
1199 }
1200 
1201 void toQureg(Qureg qureg, QVector vec) {
1202  DEMAND( !qureg.isDensityMatrix );
1203  DEMAND( qureg.numAmpsTotal == (long long int) vec.size() );
1204 
1206 
1207  for (int i=0; i<qureg.numAmpsPerChunk; i++) {
1208  int ind = qureg.chunkId*qureg.numAmpsPerChunk + i;
1209  qureg.stateVec.real[i] = real(vec[ind]);
1210  qureg.stateVec.imag[i] = imag(vec[ind]);
1211  }
1212  copyStateToGPU(qureg);
1213 }
1214 void toQureg(Qureg qureg, QMatrix mat) {
1215  DEMAND( qureg.isDensityMatrix );
1216  DEMAND( (1 << qureg.numQubitsRepresented) == (long long int) mat.size() );
1217 
1219 
1220  int len = (1 << qureg.numQubitsRepresented);
1221  for (int i=0; i<qureg.numAmpsPerChunk; i++) {
1222  int ind = qureg.chunkId*qureg.numAmpsPerChunk + i;
1223  qureg.stateVec.real[i] = real(mat[ind%len][ind/len]);
1224  qureg.stateVec.imag[i] = imag(mat[ind%len][ind/len]);
1225  }
1226  copyStateToGPU(qureg);
1227 }
1228 
1229 void setRandomPauliSum(qreal* coeffs, pauliOpType* codes, int numQubits, int numTerms) {
1230  int i=0;
1231  for (int n=0; n<numTerms; n++) {
1232  coeffs[n] = getRandomReal(-5, 5);
1233  for (int q=0; q<numQubits; q++)
1234  codes[i++] = (pauliOpType) getRandomInt(0,4);
1235  }
1236 }
1238  setRandomPauliSum(hamil.termCoeffs, hamil.pauliCodes, hamil.numQubits, hamil.numSumTerms);
1239 }
1240 
1242  int i=0;
1243  for (int n=0; n<hamil.numSumTerms; n++) {
1244  hamil.termCoeffs[n] = getRandomReal(-5, 5);
1245  for (int q=0; q<hamil.numQubits; q++)
1246  if (getRandomReal(-1,1) > 0)
1247  hamil.pauliCodes[i++] = PAULI_Z;
1248  else
1249  hamil.pauliCodes[i++] = PAULI_I;
1250  }
1251 }
1252 
1253 QMatrix toQMatrix(qreal* coeffs, pauliOpType* paulis, int numQubits, int numTerms) {
1254 
1255  // produce a numTargs-big matrix 'pauliSum' by pauli-matrix tensoring and summing
1256  QMatrix iMatr{{1,0},{0,1}};
1257  QMatrix xMatr{{0,1},{1,0}};
1258  QMatrix yMatr{{0,-qcomp(0,1)},{qcomp(0,1),0}};
1259  QMatrix zMatr{{1,0},{0,-1}};
1260  QMatrix pauliSum = getZeroMatrix(1<<numQubits);
1261 
1262  for (int t=0; t<numTerms; t++) {
1263  QMatrix pauliProd = QMatrix{{1}};
1264 
1265  for (int q=0; q<numQubits; q++) {
1266  int i = q + t*numQubits;
1267 
1268  QMatrix fac;
1269  pauliOpType code = paulis[i];
1270  if (code == PAULI_I) fac = iMatr;
1271  if (code == PAULI_X) fac = xMatr;
1272  if (code == PAULI_Y) fac = yMatr;
1273  if (code == PAULI_Z) fac = zMatr;
1274  pauliProd = getKroneckerProduct(fac, pauliProd);
1275  }
1276  pauliSum += coeffs[t] * pauliProd;
1277  }
1278 
1279  // a now 2^numQubits by 2^numQubits Hermitian matrix
1280  return pauliSum;
1281 }
1283  return toQMatrix(hamil.termCoeffs, hamil.pauliCodes, hamil.numQubits, hamil.numSumTerms);
1284 }
1285 
1286 long long int getTwosComplement(long long int decimal, int numBits) {
1287  DEMAND( decimal >= 0 );
1288  DEMAND( numBits >= 2 );
1289  DEMAND( decimal < (1LL << numBits) );
1290 
1291  long long int maxMag = 1LL << (numBits-1);
1292  if (decimal >= maxMag)
1293  return -maxMag + (decimal - maxMag);
1294  else
1295  return decimal;
1296 }
1297 
1298 long long int getUnsigned(long long int twosComp, int numBits) {
1299  DEMAND( numBits >= 2 );
1300  DEMAND( twosComp < (1LL << (numBits-1)) );
1301  DEMAND( twosComp >= - (1LL << (numBits-1)) );
1302 
1303  if (twosComp >= 0)
1304  return twosComp;
1305  else
1306  return (1<<numBits) + twosComp;
1307 }
1308 
1310  QMatrix mat = getZeroMatrix(vec.size());
1311  for (size_t i=0; i<vec.size(); i++)
1312  mat[i][i] = vec[i];
1313  return mat;
1314 }
1315 
1316 void setDiagMatrixOverrides(QMatrix &matr, int* numQubitsPerReg, int numRegs, enum bitEncoding encoding, long long int* overrideInds, qreal* overridePhases, int numOverrides) {
1317  DEMAND( (encoding == UNSIGNED || encoding == TWOS_COMPLEMENT) );
1318  DEMAND( numRegs > 0 );
1319  DEMAND( numOverrides >= 0 );
1320 
1321  int totalQb = 0;
1322  for (int r=0; r<numRegs; r++) {
1323  DEMAND( numQubitsPerReg[r] > 0 );
1324  totalQb += numQubitsPerReg[r];
1325  }
1326  DEMAND( matr.size() == (1 << totalQb) );
1327 
1328  // record whether a diagonal index has been already overriden
1329  int hasBeenOverriden[1 << totalQb];
1330  for (int i=0; i<(1 << totalQb); i++)
1331  hasBeenOverriden[i] = 0;
1332 
1333  int flatInd = 0;
1334  for (int v=0; v<numOverrides; v++) {
1335  int matrInd = 0;
1336  int numQubitsLeft = 0;
1337 
1338  for (int r=0; r<numRegs; r++) {
1339 
1340  if (encoding == UNSIGNED)
1341  matrInd += overrideInds[flatInd] * (1 << numQubitsLeft);
1342  else if (encoding == TWOS_COMPLEMENT)
1343  matrInd += getUnsigned(overrideInds[flatInd], numQubitsPerReg[r]) * (1 << numQubitsLeft);
1344 
1345  numQubitsLeft += numQubitsPerReg[r];
1346  flatInd += 1;
1347  }
1348 
1349  if (!hasBeenOverriden[matrInd]) {
1350  matr[matrInd][matrInd] = expI(overridePhases[v]);
1351  hasBeenOverriden[matrInd] = 1;
1352  }
1353  }
1354 }
1355 
1356 static int fn_unique_suffix_id = 0;
1357 
1358 void setUniqueFilename(char* outFn, char* prefix) {
1359  sprintf(outFn, "%s_%d.txt", prefix, fn_unique_suffix_id++);
1360 }
1361 
1362 void writeToFileSynch(char* fn, const string& contents) {
1363 
1364  // master node writes
1365  if (QUEST_ENV.rank == 0) {
1366  FILE* file = fopen(fn, "w");
1367  fputs(contents.c_str(), file);
1368  fclose(file);
1369  }
1370 
1371  // other nodes wait
1373 }
1374 
1375 void deleteFilesWithPrefixSynch(char* prefix) {
1376 
1377  // master node deletes all files
1378  if (QUEST_ENV.rank == 0) {
1379  char cmd[200];
1380  sprintf(cmd, "exec rm %s*", prefix);
1381  system(cmd);
1382  }
1383 
1384  // other nodes wait
1386 }
1387 
1388 class SubListGenerator : public Catch::Generators::IGenerator<int*> {
1389  int* list;
1390  int* sublist;
1391  int len;
1392  int sublen;
1393  vector<bool> featured;
1394 private:
1395  void createSublist() {
1396 
1397  // sublist to send to the user
1398  sublist = (int*) malloc(sublen * sizeof *sublist);
1399 
1400  // indicates which list members are currently in sublist
1401  featured = vector<bool>(len);
1402  fill(featured.end() - sublen, featured.end(), true);
1403  }
1404 
1406 
1407  // choose the next combination
1408  int j=0;
1409  for (int i=0; i<len; i++)
1410  if (featured[i])
1411  sublist[j++] = list[i];
1412 
1413  // prepare for permuting
1414  std::sort(sublist, sublist+sublen);
1415  }
1416 public:
1417  SubListGenerator(int* elems, int numElems, int numSamps) {
1418 
1419  DEMAND( numSamps <= numElems );
1420 
1421  // make a record of all elements
1422  len = numElems;
1423  list = (int*) malloc(len * sizeof *list);
1424  for (int i=0; i<len; i++)
1425  list[i] = elems[i];
1426 
1427  // prepare sublist
1428  sublen = numSamps;
1429  createSublist();
1430  prepareSublist();
1431  }
1432 
1434  Catch::Generators::GeneratorWrapper<int>&& gen,
1435  int numSamps, const int* exclude, int numExclude
1436  ) {
1437  // extract all generator elems
1438  vector<int> elems = vector<int>();
1439  do { elems.push_back(gen.get()); } while (gen.next());
1440 
1441  // make (int*) of non-excluded elems
1442  len = 0;
1443  list = (int*) malloc(elems.size() * sizeof *list);
1444  for (size_t i=0; i<elems.size(); i++) {
1445  int elem = elems[i];
1446  bool present = false;
1447  for (int j=0; j<numExclude; j++)
1448  if (elem == exclude[j]) {
1449  present = true;
1450  break;
1451  }
1452  if (!present)
1453  list[len++] = elem;
1454  }
1455 
1456  DEMAND( numSamps <= len );
1457 
1458  // prepare sublist
1459  sublen = numSamps;
1460  createSublist();
1461  prepareSublist();
1462  }
1463 
1464  int* const& get() const override {
1465  return sublist;
1466  }
1467 
1468  bool next() override {
1469 
1470  // offer next permutation of the current combination
1471  if (next_permutation(sublist, sublist+sublen))
1472  return true;
1473 
1474  // else generate the next combination
1475  if (next_permutation(featured.begin(), featured.end())) {
1476  prepareSublist();
1477  return true;
1478  }
1479 
1480  return false;
1481  }
1482 
1484  free(list);
1485  free(sublist);
1486  }
1487 };
1488 Catch::Generators::GeneratorWrapper<int*> sublists(
1489  int* list, int len, int sublen
1490 ) {
1491  return Catch::Generators::GeneratorWrapper<int*>(
1492  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1493  new SubListGenerator(list, len, sublen)));
1494 }
1495 Catch::Generators::GeneratorWrapper<int*> sublists(
1496  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps, const int* exclude, int numExclude
1497 ) {
1498  return Catch::Generators::GeneratorWrapper<int*>(
1499  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1500  new SubListGenerator(std::move(gen), numSamps, exclude, numExclude)));
1501 }
1502 Catch::Generators::GeneratorWrapper<int*> sublists(
1503  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps, int excluded
1504 ) {
1505  int exclude[] = {excluded};
1506  return Catch::Generators::GeneratorWrapper<int*>(
1507  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1508  new SubListGenerator(std::move(gen), numSamps, exclude, 1)));
1509 }
1510 Catch::Generators::GeneratorWrapper<int*> sublists(
1511  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps
1512 ) {
1513  int exclude[] = {};
1514  return Catch::Generators::GeneratorWrapper<int*>(
1515  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1516  new SubListGenerator(std::move(gen), numSamps, exclude, 0)));
1517 }
1518 
1519 template <typename T>
1520 class SequenceGenerator : public Catch::Generators::IGenerator<T*> {
1522  int len;
1524  int ind;
1525  int seqLen;
1526 public:
1527  SequenceGenerator(T maxDigit_, int numDigits) {
1528  ind = 0;
1529  len = numDigits;
1530  maxDigit = maxDigit_;
1531  seqLen = (int) pow(1 + (int) maxDigit, len);
1532  digits = (T*) malloc(numDigits * sizeof *digits);
1533  for (int i=0; i<numDigits; i++)
1534  digits[i] = (T) 0;
1535  ind++;
1536  }
1537 
1538  T* const& get() const override {
1539  return digits;
1540  }
1541 
1542  bool next() override {
1543  bool isNext = (ind++) < seqLen;
1544  if (isNext) {
1545  int i=0;
1546  while (digits[i] == maxDigit)
1547  digits[i++] = (T) 0;
1548  digits[i] = (T) ((int) digits[i] + 1);
1549  }
1550  return isNext;
1551  }
1552 
1554  free(digits);
1555  }
1556 };
1557 Catch::Generators::GeneratorWrapper<int*> bitsets(int numBits) {
1558  return Catch::Generators::GeneratorWrapper<int*>(
1559  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1560  new SequenceGenerator<int>(1, numBits)));
1561 }
1562 Catch::Generators::GeneratorWrapper<int*> sequences(int base, int numDigits) {
1563  return Catch::Generators::GeneratorWrapper<int*>(
1564  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1565  new SequenceGenerator<int>(base-1, numDigits)));
1566 }
1567 Catch::Generators::GeneratorWrapper<pauliOpType*> pauliseqs(int numPaulis) {
1568  return Catch::Generators::GeneratorWrapper<pauliOpType*>(
1569  std::unique_ptr<Catch::Generators::IGenerator<pauliOpType*>>(
1570  new SequenceGenerator<pauliOpType>(PAULI_Z, numPaulis)));
1571 }
long long int setBit(long long int num, int bitInd, int bitVal)
Definition: utilities.cpp:681
pauliOpType
Codes for specifying Pauli operators.
Definition: QuEST.h:96
QMatrix getFullOperatorMatrix(int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op, int numQubits)
Takes a 2^numTargs-by-2^numTargs matrix op and a returns a 2^numQubits-by-2^numQubits matrix where op...
Definition: utilities.cpp:304
long long int getUnsigned(long long int twosComp, int numBits)
Return the unsigned value of a number, made of #numBits bits, which under two's complement,...
Definition: utilities.cpp:1298
void copyStateFromGPU(Qureg qureg)
In GPU mode, this copies the state-vector (or density matrix) from GPU memory (qureg....
Definition: QuEST_cpu.c:45
QuESTEnv QUEST_ENV
The global QuESTEnv instance, to be created and destroyed once in this main(), so that the MPI enviro...
Definition: main.cpp:20
void syncQuESTEnv(QuESTEnv env)
Guarantees that all code up to the given point has been executed on all nodes (if running in distribu...
@ PAULI_Z
Definition: QuEST.h:96
QVector getKroneckerProduct(QVector b, QVector a)
Returns b (otimes) a.
Definition: utilities.cpp:143
SequenceGenerator(T maxDigit_, int numDigits)
Definition: utilities.cpp:1527
int rank
Definition: QuEST.h:364
void operator+=(QVector &v1, const QVector &v2)
Definition: utilities.cpp:64
#define macro_copyComplexMatrix(dest, src)
Copies ComplexMatrix structures into a QMatrix.
Definition: utilities.cpp:1039
@ PAULI_I
Definition: QuEST.h:96
QMatrix getConjugateTranspose(QMatrix a)
Returns the conjugate transpose of the complex square matrix a.
Definition: utilities.cpp:189
int getRandomInt(int min, int max)
Returns a random integer between min (inclusive) and max (exclusive), from the uniform distribution.
Definition: utilities.cpp:526
std::vector< QMatrix > getRandomKrausMap(int numQb, int numOps)
Returns a random Kraus map of #numOps 2^numQb-by-2^numQb operators, from an undisclosed distribution.
Definition: utilities.cpp:578
QMatrix getRandomPureDensityMatrix(int numQb)
Returns a random numQb-by-numQb density matrix, from an undisclosed distribution, which is pure (corr...
Definition: utilities.cpp:511
QMatrix getMixedDensityMatrix(std::vector< qreal > probs, std::vector< QVector > states)
Returns a mixed density matrix formed from mixing the given pure states, which are assumed normalised...
Definition: utilities.cpp:640
SubListGenerator(Catch::Generators::GeneratorWrapper< int > &&gen, int numSamps, const int *exclude, int numExclude)
Definition: utilities.cpp:1433
QMatrix getSwapMatrix(int qb1, int qb2, int numQb)
Returns the 2^numQb-by-2^numQb unitary matrix which swaps qubits qb1 and qb2; the SWAP gate of not-ne...
Definition: utilities.cpp:230
QVector operator+(const QVector &v1, const QVector &v2)
Definition: utilities.cpp:26
@ TWOS_COMPLEMENT
Definition: QuEST.h:269
QMatrix getRandomUnitary(int numQb)
Returns a uniformly random (under Haar) 2^numQb-by-2^numQb unitary matrix.
Definition: utilities.cpp:530
void writeToFileSynch(char *fn, const string &contents)
Writes contents to the file with filename fn, which is created and/or overwritten.
Definition: utilities.cpp:1362
qreal getRandomReal(qreal min, qreal max)
Returns a random real between min (inclusive) and max (exclusive), from the uniform distribution.
Definition: utilities.cpp:421
void setDiagMatrixOverrides(QMatrix &matr, int *numQubitsPerReg, int numRegs, enum bitEncoding encoding, long long int *overrideInds, qreal *overridePhases, int numOverrides)
Modifies the given diagonal matrix such that the diagonal elements which correspond to the coordinate...
Definition: utilities.cpp:1316
QMatrix getKetBra(QVector ket, QVector bra)
Returns the matrix |ket><bra|, with ith-jth element ket(i) conj(bra(j)), since |ket><bra| = sum_i a_i...
Definition: utilities.cpp:169
std::vector< QVector > getRandomOrthonormalVectors(int numQb, int numStates)
Returns a list of random orthonormal complex vectors, from an undisclosed distribution.
Definition: utilities.cpp:613
Represents a 4x4 matrix of complex numbers.
Definition: QuEST.h:175
bool next() override
Definition: utilities.cpp:1542
@ UNSIGNED
Definition: QuEST.h:269
Represents a general 2^N by 2^N matrix of complex numbers.
Definition: QuEST.h:186
#define qreal
QMatrix toQMatrix(ComplexMatrix2 src)
Returns a copy of the given 2-by-2 matrix.
Definition: utilities.cpp:1044
void toQureg(Qureg qureg, QVector vec)
Initialises the state-vector qureg to have the same amplitudes as vec.
Definition: utilities.cpp:1201
unsigned int calcLog2(long unsigned int res)
Returns log2 of numbers which must be gauranteed to be 2^n.
Definition: utilities.cpp:372
@ PAULI_X
Definition: QuEST.h:96
void operator-=(QVector &v1, const QVector &v2)
Definition: utilities.cpp:67
static int fn_unique_suffix_id
Definition: utilities.cpp:1356
QMatrix getExponentialOfPauliMatrix(qreal angle, QMatrix a)
Returns the matrix exponential of a kronecker product of pauli matrices (or of any involutory matrice...
Definition: utilities.cpp:216
QVector operator*(const qcomp &a, const QVector &v)
Definition: utilities.cpp:40
SubListGenerator(int *elems, int numElems, int numSamps)
Definition: utilities.cpp:1417
void setUniqueFilename(char *outFn, char *prefix)
Modifies outFn to be a filename of format prefix_NUM.txt where NUM is a new unique integer so far.
Definition: utilities.cpp:1358
bool next() override
Definition: utilities.cpp:1468
void setRandomPauliSum(qreal *coeffs, pauliOpType *codes, int numQubits, int numTerms)
Populates the coeffs array with random qreals in (-5, 5), and populates codes with random Pauli codes...
Definition: utilities.cpp:1229
void toComplexMatrixN(QMatrix qm, ComplexMatrixN cm)
Initialises cm with the values of qm.
Definition: utilities.cpp:1033
int chunkId
The position of the chunk of the state vector held by this process in the full state vector.
Definition: QuEST.h:336
qcomp expI(qreal phase)
Returns the unit-norm complex number exp(i*phase).
Definition: utilities.cpp:417
std::vector< qcomp > QVector
A complex vector, which can be zero-initialised with QVector(numAmps).
Definition: utilities.hpp:60
qreal * imag
The imaginary values of the 2^numQubits complex elements.
Definition: QuEST.h:310
void setSubMatrix(QMatrix &dest, QMatrix sub, size_t r, size_t c)
Modifies dest by overwriting its submatrix (from top-left corner (r, c) to bottom-right corner (r + d...
Definition: utilities.cpp:222
QVector toQVector(Qureg qureg)
Returns an equal-size copy of the given state-vector qureg.
Definition: utilities.cpp:1113
long long int numAmpsPerChunk
Number of probability amplitudes held in stateVec by this process In the non-MPI version,...
Definition: QuEST.h:332
qreal * termCoeffs
The real coefficient of each Pauli product. This is an array of length PauliHamil....
Definition: QuEST.h:283
Catch::Generators::GeneratorWrapper< int * > sequences(int base, int numDigits)
Returns a Catch2 generator of every numDigits-length sequence in the given base, in increasing lexogr...
Definition: utilities.cpp:1562
QVector operator/(const QVector &v, const qcomp &a)
Definition: utilities.cpp:49
#define qcomp
enum pauliOpType * pauliCodes
The Pauli operators acting on each qubit, flattened over every operator.
Definition: QuEST.h:281
ComplexMatrix4 toComplexMatrix4(QMatrix qm)
Returns a ComplexMatrix4 copy of QMatix qm.
Definition: utilities.cpp:1027
void copyStateToGPU(Qureg qureg)
In GPU mode, this copies the state-vector (or density matrix) from RAM (qureg.stateVec) to VRAM / GPU...
Definition: QuEST_cpu.c:42
void operator*=(QVector &v1, const qcomp &a)
Definition: utilities.cpp:70
QMatrix toDiagonalQMatrix(QVector vec)
Returns a diagonal complex matrix formed by the given vector.
Definition: utilities.cpp:1309
int numQubits
The number of qubits this operator can act on (informing its size)
Definition: QuEST.h:300
int numSumTerms
The number of terms in the weighted sum, or the number of Pauli products.
Definition: QuEST.h:285
Represents a diagonal complex operator on the full Hilbert state of a Qureg.
Definition: QuEST.h:297
@ PAULI_Y
Definition: QuEST.h:96
A Pauli Hamiltonian, expressed as a real-weighted sum of pauli products, and which can hence represen...
Definition: QuEST.h:277
QVector getRandomStateVector(int numQb)
Returns a random numQb-length L2-normalised state-vector from an undisclosed distribution.
Definition: utilities.cpp:468
void setRandomDiagPauliHamil(PauliHamil hamil)
Populates hamil with random coefficients and a random amount number of PAULI_I and PAULI_Z operators.
Definition: utilities.cpp:1241
qreal ** real
Definition: QuEST.h:189
QMatrix getRandomQMatrix(int dim)
Returns a dim-by-dim complex matrix, where the real and imaginary value of each element are independe...
Definition: utilities.cpp:379
long long int getIndexOfTargetValues(long long int ref, int *targs, int numTargs, int targVal)
Definition: utilities.cpp:689
long long int getTwosComplement(long long int decimal, int numBits)
Returns the two's complement signed encoding of the unsigned number decimal, which must be a number b...
Definition: utilities.cpp:1286
Represents a system of qubits.
Definition: QuEST.h:322
qreal ** imag
Definition: QuEST.h:190
T *const & get() const override
Definition: utilities.cpp:1538
Catch::Generators::GeneratorWrapper< pauliOpType * > pauliseqs(int numPaulis)
Returns a Catch2 generator of every numPaulis-length set of Pauli-matrix types (or base-4 integers).
Definition: utilities.cpp:1567
ComplexArray stateVec
Computational state amplitudes - a subset thereof in the MPI version.
Definition: QuEST.h:341
vector< bool > featured
Definition: utilities.cpp:1393
long long int numElemsPerChunk
The number of the 2^numQubits amplitudes stored on each distributed node.
Definition: QuEST.h:302
int isDensityMatrix
Whether this instance is a density-state representation.
Definition: QuEST.h:325
int numQubits
Definition: QuEST.h:188
void updateIndices(int oldEl, int newEl, int *list1, int len1, int *list2, int len2)
Definition: utilities.cpp:287
std::vector< std::vector< qcomp > > QMatrix
A complex square matrix.
Definition: utilities.hpp:49
ComplexMatrix2 toComplexMatrix2(QMatrix qm)
Returns a ComplexMatrix2 copy of QMatix qm.
Definition: utilities.cpp:1021
int numQubits
The number of qubits informing the Hilbert dimension of the Hamiltonian.
Definition: QuEST.h:287
Catch::Generators::GeneratorWrapper< int * > sublists(int *list, int len, int sublen)
Returns a Catch2 generator of every length-sublen sublist of length-len list, in increasing lexograph...
Definition: utilities.cpp:1488
QVector getNormalised(QVector vec)
Returns an L2-normalised copy of vec, using Kahan summation for improved accuracy.
Definition: utilities.cpp:446
int *const & get() const override
Definition: utilities.cpp:1464
int numQubitsRepresented
The number of qubits represented in either the state-vector or density matrix.
Definition: QuEST.h:327
std::vector< qreal > getRandomProbabilities(int numProbs)
Returns a list of random real scalars, each in [0, 1], which sum to unity.
Definition: utilities.cpp:472
long long int numAmpsTotal
Total number of amplitudes, which are possibly distributed among machines.
Definition: QuEST.h:334
qreal * real
The real values of the 2^numQubits complex elements.
Definition: QuEST.h:308
qreal real
Definition: QuEST.h:105
QMatrix getIdentityMatrix(size_t dim)
Returns a dim-by-dim identity matrix.
Definition: utilities.cpp:161
QMatrix getPureDensityMatrix(QVector state)
Returns a density matrix initialised into the given pure state.
Definition: utilities.cpp:507
qreal imag
Definition: QuEST.h:106
void deleteFilesWithPrefixSynch(char *prefix)
Deletes all files with filename starting with prefix.
Definition: utilities.cpp:1375
QMatrix getRandomDensityMatrix(int numQb)
Returns a random numQb-by-numQb density matrix, from an undisclosed distribution, in a very mixed sta...
Definition: utilities.cpp:490
QMatrix getExponentialOfDiagonalMatrix(QMatrix a)
Returns the matrix exponential of a diagonal, square, complex matrix.
Definition: utilities.cpp:197
QMatrix getZeroMatrix(size_t dim)
Returns a dim-by-dim square complex matrix, initialised to all zeroes.
Definition: utilities.cpp:153
Represents one complex number.
Definition: QuEST.h:103
QVector getRandomQVector(int dim)
Returns a dim-length vector with random complex amplitudes in the square joining {-1-i,...
Definition: utilities.cpp:435
void applyReferenceMatrix(QVector &state, int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op)
Modifies the state-vector state to be the result of left-multiplying the multi-target operator matrix...
Definition: utilities.cpp:841
QVector getDFT(QVector in)
Returns the discrete fourier transform of vector in.
Definition: utilities.cpp:652
QVector getMatrixDiagonal(QMatrix matr)
Returns the diagonal vector of the given matrix.
Definition: utilities.cpp:517
void operator/=(QVector &v1, const qcomp &a)
Definition: utilities.cpp:73
bool areEqual(QVector a, QVector b)
Returns true if the absolute value of the difference between every amplitude in vectors a and b is le...
Definition: utilities.cpp:398
void applyReferenceOp(QVector &state, int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op)
Modifies the state-vector state to be the result of applying the multi-target operator matrix op,...
Definition: utilities.cpp:728
#define M_PI
Definition: QuEST_common.c:41
Catch::Generators::GeneratorWrapper< int * > bitsets(int numBits)
Returns a Catch2 generator of every numBits-length bit-set, in increasing lexographic order,...
Definition: utilities.cpp:1557
qcomp getRandomComplex()
Returns a random complex number within the square closing (-1-i) and (1+i), from a distribution unifo...
Definition: utilities.cpp:431
#define DEMAND(cond)
Definition: utilities.cpp:24
QVector operator-(const QVector &v1, const QVector &v2)
Definition: utilities.cpp:33
bitEncoding
Flags for specifying how the bits in sub-register computational basis states are mapped to indices in...
Definition: QuEST.h:269
#define macro_copyQMatrix(dest, src)
Definition: utilities.cpp:1013
Represents a 2x2 matrix of complex numbers.
Definition: QuEST.h:137
long long int getValueOfTargets(long long int ind, int *targs, int numTargs)
Returns the integer value of the targeted sub-register for the given full state index ind.
Definition: utilities.cpp:670