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 
143 QMatrix getZeroMatrix(size_t dim) {
144  DEMAND( dim > 1 );
145  QMatrix matr = QMatrix(dim);
146  for (size_t i=0; i<dim; i++)
147  matr[i].resize(dim);
148  return matr;
149 }
150 
152  DEMAND( dim > 1 );
153  QMatrix matr = getZeroMatrix(dim);
154  for (size_t i=0; i<dim; i++)
155  matr[i][i] = 1;
156  return matr;
157 }
158 
160  DEMAND( ket.size() == bra.size() );
161  QMatrix mat = getZeroMatrix(ket.size());
162 
163  for (size_t r=0; r<ket.size(); r++)
164  for (size_t c=0; c<ket.size(); c++)
165  mat[r][c] = ket[r] * conj(bra[c]);
166  return mat;
167 }
168 
170  QMatrix prod = getZeroMatrix(a.size() * b.size());
171  for (size_t r=0; r<b.size(); r++)
172  for (size_t c=0; c<b.size(); c++)
173  for (size_t i=0; i<a.size(); i++)
174  for (size_t j=0; j<a.size(); j++)
175  prod[r+b.size()*i][c+b.size()*j] = a[i][j] * b[r][c];
176  return prod;
177 }
178 
180  QMatrix b = a;
181  for (size_t r=0; r<a.size(); r++)
182  for (size_t c=0; c<a.size(); c++)
183  b[r][c] = conj(a[c][r]);
184  return b;
185 }
186 
188 
189  // ensure diagonal
190  for (size_t r=0; r<a.size(); r++)
191  for (size_t c=0; c<a.size(); c++) {
192  if (r == c)
193  continue;
194  DEMAND( a[r][c] == 0. );
195  }
196 
197  // exp(diagonal) = diagonal(exp)
198  QMatrix diag = a;
199  for (size_t i=0; i<a.size(); i++)
200  diag[i][i] = exp(diag[i][i]);
201 
202  return diag;
203 }
204 
206  QMatrix iden = getIdentityMatrix(a.size());
207  QMatrix expo = (cos(angle/2) * iden) + (-1i * sin(angle/2) * a);
208  return expo;
209 }
210 
211 void setSubMatrix(QMatrix &dest, QMatrix sub, size_t r, size_t c) {
212  DEMAND( sub.size() + r <= dest.size() );
213  DEMAND( sub.size() + c <= dest.size() );
214  for (size_t i=0; i<sub.size(); i++)
215  for (size_t j=0; j<sub.size(); j++)
216  dest[r+i][c+j] = sub[i][j];
217 }
218 
219 QMatrix getSwapMatrix(int qb1, int qb2, int numQb) {
220  DEMAND( numQb > 1 );
221  DEMAND( (qb1 >= 0 && qb1 < numQb) );
222  DEMAND( (qb2 >= 0 && qb2 < numQb) );
223 
224  if (qb1 > qb2)
225  std::swap(qb1, qb2);
226 
227  if (qb1 == qb2)
228  return getIdentityMatrix(1 << numQb);
229 
230  QMatrix swap;
231 
232  if (qb2 == qb1 + 1) {
233  // qubits are adjacent
234  swap = QMatrix{{1,0,0,0},{0,0,1,0},{0,1,0,0},{0,0,0,1}};
235 
236  } else {
237  // qubits are distant
238  int block = 1 << (qb2 - qb1);
239  swap = getZeroMatrix(block*2);
240  QMatrix iden = getIdentityMatrix(block/2);
241 
242  // Lemma 3.1 of arxiv.org/pdf/1711.09765.pdf
243  QMatrix p0{{1,0},{0,0}};
244  QMatrix l0{{0,1},{0,0}};
245  QMatrix l1{{0,0},{1,0}};
246  QMatrix p1{{0,0},{0,1}};
247 
248  /* notating a^(n+1) = identity(1<<n) (otimes) a, we construct the matrix
249  * [ p0^(N) l1^N ]
250  * [ l0^(N) p1^N ]
251  * where N = qb2 - qb1 */
252  setSubMatrix(swap, getKroneckerProduct(iden, p0), 0, 0);
253  setSubMatrix(swap, getKroneckerProduct(iden, l0), block, 0);
254  setSubMatrix(swap, getKroneckerProduct(iden, l1), 0, block);
255  setSubMatrix(swap, getKroneckerProduct(iden, p1), block, block);
256  }
257 
258  // pad swap with outer identities
259  if (qb1 > 0)
260  swap = getKroneckerProduct(swap, getIdentityMatrix(1<<qb1));
261  if (qb2 < numQb-1)
262  swap = getKroneckerProduct(getIdentityMatrix(1<<(numQb-qb2-1)), swap);
263 
264  return swap;
265 }
266 
267 /* (don't generate doxygen doc)
268  *
269  * iterates list1 (of length len1) and replaces element oldEl with newEl, which is
270  * gauranteed to be present at most once (between list1 AND list2), though may
271  * not be present at all. If oldEl isn't present in list1, does the same for list2.
272  * list1 is skipped if == NULL. This is used by getFullOperatorMatrix() to ensure
273  * that, when qubits are swapped, their appearences in the remaining qubit lists
274  * are updated.
275  */
276 void updateIndices(int oldEl, int newEl, int* list1, int len1, int* list2, int len2) {
277  if (list1 != NULL) {
278  for (int i=0; i<len1; i++) {
279  if (list1[i] == oldEl) {
280  list1[i] = newEl;
281  return;
282  }
283  }
284  }
285  for (int i=0; i<len2; i++) {
286  if (list2[i] == oldEl) {
287  list2[i] = newEl;
288  return;
289  }
290  }
291 }
292 
294  int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op, int numQubits
295 ) {
296  DEMAND( numCtrls >= 0 );
297  DEMAND( numTargs >= 0 );
298  DEMAND( numQubits >= (numCtrls+numTargs) );
299  DEMAND( op.size() == (1u << numTargs) );
300 
301  // copy {ctrls} and {targs}to restore at end
302  std::vector<int> ctrlsCopy(ctrls, ctrls+numCtrls);
303  std::vector<int> targsCopy(targs, targs+numTargs);
304 
305  // full-state matrix of qubit swaps
306  QMatrix swaps = getIdentityMatrix(1 << numQubits);
307  QMatrix unswaps = getIdentityMatrix(1 << numQubits);
308  QMatrix matr;
309 
310  // swap targs to {0, ..., numTargs-1}
311  for (int i=0; i<numTargs; i++) {
312  if (i != targs[i]) {
313  matr = getSwapMatrix(i, targs[i], numQubits);
314  swaps = matr * swaps;
315  unswaps = unswaps * matr;
316 
317  // even if this is the last targ, ctrls might still need updating
319  i, targs[i], (i < numTargs-1)? &targs[i+1] : NULL,
320  numTargs-i-1, ctrls, numCtrls);
321  }
322  }
323 
324  // swap ctrls to {numTargs, ..., numTargs+numCtrls-1}
325  for (int i=0; i<numCtrls; i++) {
326  int newInd = numTargs+i;
327  if (newInd != ctrls[i]) {
328  matr = getSwapMatrix(newInd, ctrls[i], numQubits);
329  swaps = matr * swaps;
330  unswaps = unswaps * matr;
331 
332  // update remaining ctrls (if any exist)
333  if (i < numCtrls-1)
334  updateIndices(newInd, ctrls[i], NULL, 0, &ctrls[i+1], numCtrls-i-1);
335  }
336  }
337 
338  // construct controlled-op matrix for qubits {0, ..., numCtrls+numTargs-1}
339  size_t dim = 1 << (numCtrls+numTargs);
340  QMatrix fullOp = getIdentityMatrix(dim);
341  setSubMatrix(fullOp, op, dim-op.size(), dim-op.size());
342 
343  // create full-state controlled-op matrix (left-pad identities)
344  if (numQubits > numCtrls+numTargs) {
345  size_t pad = 1 << (numQubits - numCtrls - numTargs);
346  fullOp = getKroneckerProduct(getIdentityMatrix(pad), fullOp);
347  }
348 
349  // apply swap to either side (to swap qubits back and forth)
350  fullOp = unswaps * fullOp * swaps;
351 
352  // restore {ctrls and targs}
353  for (int i=0; i<numCtrls; i++)
354  ctrls[i] = ctrlsCopy[i];
355  for (int i=0; i<numTargs; i++)
356  targs[i] = targsCopy[i];
357 
358  return fullOp;
359 }
360 
361 unsigned int calcLog2(long unsigned int res) {
362  unsigned int n = 0;
363  while (res >>= 1)
364  n++;
365  return n;
366 }
367 
369  DEMAND( dim > 1 );
370 
371  QMatrix matr = getZeroMatrix(dim);
372  for (int i=0; i<dim; i++) {
373  for (int j=0; j<dim; j++) {
374 
375  // generate 2 normally-distributed random numbers via Box-Muller
376  qreal a = rand()/(qreal) RAND_MAX;
377  qreal b = rand()/(qreal) RAND_MAX;
378  qreal r1 = sqrt(-2 * log(a)) * cos(2 * 3.14159265 * b);
379  qreal r2 = sqrt(-2 * log(a)) * sin(2 * 3.14159265 * b);
380 
381  matr[i][j] = r1 + r2*1i;
382  }
383  }
384  return matr;
385 }
386 
388  DEMAND( a.size() == b.size() );
389 
390  for (size_t i=0; i<a.size(); i++)
391  if (abs(a[i] - b[i]) > REAL_EPS)
392  return false;
393  return true;
394 }
395 
397  DEMAND( a.size() == b.size() );
398 
399  for (size_t i=0; i<a.size(); i++)
400  for (size_t j=0; j<b.size(); j++)
401  if (abs(a[i][j] - b[i][j]) > REAL_EPS)
402  return false;
403  return true;
404 }
405 
406 qcomp expI(qreal phase) {
407  return cos(phase) + 1i*sin(phase);
408 }
409 
411  DEMAND( min <= max );
412  qreal r = min + (max - min) * (rand() / (qreal) RAND_MAX);
413 
414  // check bounds satisfied
415  DEMAND( r >= min );
416  DEMAND( r <= max );
417  return r;
418 }
419 
421  QVector vec = QVector(dim);
422  for (int i=0; i<dim; i++)
423  vec[i] = getRandomReal(-1,1) + 1i*getRandomReal(-1,1);
424 
425  // check we didn't get the impossibly-unlikely zero-amplitude outcome
426  DEMAND( real(vec[0]) != 0 );
427 
428  return vec;
429 }
430 
432  qreal norm = 0;
433  qreal y, t, c;
434  c = 0;
435 
436  for (size_t i=0; i<vec.size(); i++) {
437  y = real(vec[i])*real(vec[i]) - c;
438  t = norm + y;
439  c = ( t - norm ) - y;
440  norm = t;
441 
442  y = imag(vec[i])*imag(vec[i]) - c;
443  t = norm + y;
444  c = ( t - norm ) - y;
445  norm = t;
446  }
447 
448  for (size_t i=0; i<vec.size(); i++)
449  vec[i] /= sqrt(norm);
450  return vec;
451 }
452 
454  return getNormalised(getRandomQVector(1<<numQb));
455 }
456 
458  DEMAND( numQb > 0 );
459 
460  // generate random probabilities to weight random pure states
461  int dim = 1<<numQb;
462  qreal probs[dim];
463  qreal probNorm = 0;
464  for (int i=0; i<dim; i++) {
465  probs[i] = getRandomReal(0, 1);
466  probNorm += probs[i];
467  }
468  for (int i=0; i<dim; i++)
469  probs[i] /= probNorm;
470 
471  // add random pure states
472  QMatrix dens = getZeroMatrix(dim);
473  for (int i=0; i<dim; i++) {
474  QVector pure = getRandomStateVector(numQb);
475  dens += probs[i] * getKetBra(pure, pure);
476  }
477 
478  return dens;
479 }
480 
481 int getRandomInt(int min, int max) {
482  return round(getRandomReal(min, max-1));
483 }
484 
486  DEMAND( numQb >= 1 );
487 
488  QMatrix matr = getRandomQMatrix(1 << numQb);
489 
490  for (size_t i=0; i<matr.size(); i++) {
491  QVector row = matr[i];
492 
493  // compute new orthogonal row by subtracting proj row onto prevs
494  for (int k=i-1; k>=0; k--) {
495 
496  // compute row . prev = sum_n row_n conj(prev_n)
497  qcomp prod = 0;
498  for (size_t n=0; n<row.size(); n++)
499  prod += row[n] * conj(matr[k][n]);
500 
501  // subtract (proj row onto prev) = (prod * prev) from final row
502  for (size_t n=0; n<row.size(); n++)
503  matr[i][n] -= prod * matr[k][n];
504  }
505 
506  // compute row magnitude
507  qreal mag = 0;
508  for (size_t j=0; j<row.size(); j++)
509  mag += pow(abs(matr[i][j]), 2);
510  mag = sqrt(mag);
511 
512  // normalise row
513  for (size_t j=0; j<row.size(); j++)
514  matr[i][j] /= mag;
515  }
516 
517  // ensure matrix is indeed unitary
518  QMatrix conjprod = matr * getConjugateTranspose(matr);
519  QMatrix iden = getIdentityMatrix(1 << numQb);
520 
521  // generating big unitary matrices is hard; if we fail, default to identity
522  if ( numQb >= 3 && !areEqual(conjprod, iden) ) {
523 
524  matr = getIdentityMatrix(1 << numQb);
525  conjprod = matr;
526  }
527  DEMAND( areEqual(conjprod, iden) );
528 
529  // return the new orthonormal matrix
530  return matr;
531 }
532 
533 std::vector<QMatrix> getRandomKrausMap(int numQb, int numOps) {
534  DEMAND( numOps >= 1 );
535  DEMAND( numOps <= 4*numQb*numQb );
536 
537  // generate random unitaries
538  std::vector<QMatrix> ops;
539  for (int i=0; i<numOps; i++)
540  ops.push_back(getRandomUnitary(numQb));
541 
542  // generate random weights
543  qreal weights[numOps];
544  for (int i=0; i<numOps; i++)
545  weights[i] = getRandomReal(0, 1);
546 
547  // normalise random weights
548  qreal weightSum = 0;
549  for (int i=0; i<numOps; i++)
550  weightSum += weights[i];
551  for (int i=0; i<numOps; i++)
552  weights[i] = sqrt(weights[i]/weightSum);
553 
554  // normalise ops
555  for (int i=0; i<numOps; i++)
556  ops[i] *= weights[i];
557 
558  // check what we produced was a valid Kraus map
559  QMatrix iden = getIdentityMatrix(1 << numQb);
560  QMatrix prodSum = getZeroMatrix(1 << numQb);
561  for (int i=0; i<numOps; i++)
562  prodSum += getConjugateTranspose(ops[i]) * ops[i];
563  DEMAND( areEqual(prodSum, iden) );
564 
565  return ops;
566 }
567 
568 /* (do not generate doxygen doc)
569  *
570  * Overloads for applyReferenceOp, to conveniently specify all families of
571  * unitary operations on state-vectors.
572  */
574  QVector &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
575 ) {
576  int numQubits = calcLog2(state.size());
577  QMatrix fullOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
578  state = fullOp * state;
579 }
581  QVector &state, int* ctrls, int numCtrls, int targ1, int targ2, QMatrix op
582 ) {
583  int targs[2] = {targ1, targ2};
584  applyReferenceOp(state, ctrls, numCtrls, targs, 2, op);
585 }
587  QVector &state, int* ctrls, int numCtrls, int target, QMatrix op
588 ) {
589  int targs[1] = {target};
590  applyReferenceOp(state, ctrls, numCtrls, targs, 1, op);
591 }
593  QVector &state, int *targs, int numTargs, QMatrix op
594 ) {
595  applyReferenceOp(state, NULL, 0, targs, numTargs, op);
596 }
598  QVector &state, int ctrl, int targ, QMatrix op
599 ) {
600  int ctrls[1] = {ctrl};
601  int targs[1] = {targ};
602  applyReferenceOp(state, ctrls, 1, targs, 1, op);
603 }
605  QVector &state, int ctrl, int* targs, int numTargs, QMatrix op
606 ) {
607  int ctrls[1] = {ctrl};
608  applyReferenceOp(state, ctrls, 1, targs, numTargs, op);
609 }
611  QVector &state, int ctrl, int targ1, int targ2, QMatrix op
612 ) {
613  int ctrls[1] = {ctrl};
614  int targs[2] = {targ1, targ2};
615  applyReferenceOp(state, ctrls, 1, targs, 2, op);
616 }
618  QVector &state, int targ, QMatrix op
619 ) {
620  int targs[1] = {targ};
621  applyReferenceOp(state, NULL, 0, targs, 1, op);
622 }
623 
624 /* (do not generate doxygen doc)
625  *
626  * Overloads for applyReferenceOp, to conveniently specify all families of
627  * unitary operations on state-vectors.
628  */
630  QMatrix &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
631 ) {
632  int numQubits = calcLog2(state.size());
633  QMatrix leftOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
634  QMatrix rightOp = getConjugateTranspose(leftOp);
635  state = leftOp * state * rightOp;
636 }
638  QMatrix &state, int* ctrls, int numCtrls, int targ1, int targ2, QMatrix op
639 ) {
640  int targs[2] = {targ1, targ2};
641  applyReferenceOp(state, ctrls, numCtrls, targs, 2, op);
642 }
644  QMatrix &state, int* ctrls, int numCtrls, int target, QMatrix op
645 ) {
646  int targs[1] = {target};
647  applyReferenceOp(state, ctrls, numCtrls, targs, 1, op);
648 }
650  QMatrix &state, int *targs, int numTargs, QMatrix op
651 ) {
652  applyReferenceOp(state, NULL, 0, targs, numTargs, op);
653 }
655  QMatrix &state, int ctrl, int targ, QMatrix op
656 ) {
657  int ctrls[1] = {ctrl};
658  int targs[1] = {targ};
659  applyReferenceOp(state, ctrls, 1, targs, 1, op);
660 }
662  QMatrix &state, int ctrl, int* targs, int numTargs, QMatrix op
663 ) {
664  int ctrls[1] = {ctrl};
665  applyReferenceOp(state, ctrls, 1, targs, numTargs, op);
666 }
668  QMatrix &state, int ctrl, int targ1, int targ2, QMatrix op
669 ) {
670  int ctrls[1] = {ctrl};
671  int targs[2] = {targ1, targ2};
672  applyReferenceOp(state, ctrls, 1, targs, 2, op);
673 }
675  QMatrix &state, int targ, QMatrix op
676 ) {
677  int targs[1] = {targ};
678  applyReferenceOp(state, NULL, 0, targs, 1, op);
679 }
680 
681 /* (do not generate doxygen doc)
682  *
683  * Overloads for applyReferenceMatrix, to simply left-multiply a matrix (possibly
684  * with additional control qubits) onto a state.
685  */
687  QVector &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
688 ) {
689  // for state-vectors, the op is always just left-multiplied
690  applyReferenceOp(state, ctrls, numCtrls, targs, numTargs, op);
691 }
693  QMatrix &state, int* ctrls, int numCtrls, int *targs, int numTargs, QMatrix op
694 ) {
695  // for density matrices, op is left-multiplied only
696  int numQubits = calcLog2(state.size());
697  QMatrix leftOp = getFullOperatorMatrix(ctrls, numCtrls, targs, numTargs, op, numQubits);
698  state = leftOp * state;
699 }
700 
701 bool areEqual(Qureg qureg1, Qureg qureg2, qreal precision) {
702  DEMAND( qureg1.isDensityMatrix == qureg2.isDensityMatrix );
703  DEMAND( qureg1.numAmpsTotal == qureg2.numAmpsTotal );
704 
705  copyStateFromGPU(qureg1);
706  copyStateFromGPU(qureg2);
708 
709  // loop terminates when areEqual = 0
710  int ampsAgree = 1;
711  for (long long int i=0; ampsAgree && i<qureg1.numAmpsPerChunk; i++)
712  ampsAgree = (
713  absReal(qureg1.stateVec.real[i] - qureg2.stateVec.real[i]) < precision
714  && absReal(qureg1.stateVec.imag[i] - qureg2.stateVec.imag[i]) < precision);
715 
716  // if one node's partition wasn't equal, all-nodes must report not-equal
717  int allAmpsAgree = ampsAgree;
718 #ifdef DISTRIBUTED_MODE
719  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
720 #endif
721 
722  return allAmpsAgree;
723 }
724 bool areEqual(Qureg qureg1, Qureg qureg2) {
725  return areEqual(qureg1, qureg2, REAL_EPS);
726 }
727 
728 bool areEqual(Qureg qureg, QVector vec, qreal precision) {
729  DEMAND( !qureg.isDensityMatrix );
730  DEMAND( (int) vec.size() == qureg.numAmpsTotal );
731 
732  copyStateFromGPU(qureg);
734 
735  // the starting index in vec of this node's qureg partition.
736  long long int startInd = qureg.chunkId * qureg.numAmpsPerChunk;
737 
738  int ampsAgree = 1;
739  for (long long int i=0; i<qureg.numAmpsPerChunk; i++) {
740  qreal realDif = absReal(qureg.stateVec.real[i] - real(vec[startInd+i]));
741  qreal imagDif = absReal(qureg.stateVec.imag[i] - imag(vec[startInd+i]));
742 
743  if (realDif > precision || imagDif > precision) {
744  ampsAgree = 0;
745 
746  // debug
747  printf("Disagreement at %lld: %g + i(%g) VS %g + i(%g)\n",
748  startInd+i, qureg.stateVec.real[i], qureg.stateVec.imag[i],
749  real(vec[startInd+i]), imag(vec[startInd+i]));
750 
751  break;
752  }
753  }
754 
755  // if one node's partition wasn't equal, all-nodes must report not-equal
756  int allAmpsAgree = ampsAgree;
757 #ifdef DISTRIBUTED_MODE
758  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
759 #endif
760 
761  return allAmpsAgree;
762 }
763 bool areEqual(Qureg qureg, QVector vec) {
764  return areEqual(qureg, vec, REAL_EPS);
765 }
766 
767 bool areEqual(Qureg qureg, QMatrix matr, qreal precision) {
768  DEMAND( qureg.isDensityMatrix );
769  DEMAND( (int) (matr.size()*matr.size()) == qureg.numAmpsTotal );
770 
771  // ensure local qureg.stateVec is up to date
772  copyStateFromGPU(qureg);
774 
775  // the starting index in vec of this node's qureg partition.
776  long long int startInd = qureg.chunkId * qureg.numAmpsPerChunk;
777  long long int globalInd, row, col, i;
778  int ampsAgree;
779 
780  // compare each of this node's amplitude to the corresponding matr sub-matrix
781  for (i=0; i<qureg.numAmpsPerChunk; i++) {
782  globalInd = startInd + i;
783  row = globalInd % matr.size();
784  col = globalInd / matr.size();
785  qreal realDif = absReal(qureg.stateVec.real[i] - real(matr[row][col]));
786  qreal imagDif = absReal(qureg.stateVec.imag[i] - imag(matr[row][col]));
787  ampsAgree = (realDif < precision && imagDif < precision);
788 
789  // DEBUG
790  if (!ampsAgree) {
791  printf("[msg from utilities.cpp] node %d has a disagreement at (global) index %lld of (%g) + i(%g)\n",
792  qureg.chunkId, globalInd, realDif, imagDif
793  );
794  }
795 
796  // break loop as soon as amplitudes disagree
797  if (!ampsAgree)
798  break;
799 
800  /* TODO:
801  * of the nodes which disagree, the lowest-rank should send its
802  * disagreeing (i, row, col, stateVec[i]) to rank 0 which should
803  * report it immediately (before the impending DEMAND failure)
804  * using FAIL_CHECK, so users can determine nature of disagreement
805  * (e.g. numerical precision).
806  * Note FAIL_CHECK accepts << like cout, e.g.
807  * FAIL_CHECK( "Amp at (" << row << ", " << col ") disagreed" );
808  */
809  }
810 
811  // if one node's partition wasn't equal, all-nodes must report not-equal
812  int allAmpsAgree = ampsAgree;
813 #ifdef DISTRIBUTED_MODE
814  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
815 #endif
816 
817  return allAmpsAgree;
818 }
819 bool areEqual(Qureg qureg, QMatrix matr) {
820  return areEqual(qureg, matr, REAL_EPS);
821 }
822 
823 bool areEqual(QVector vec, qreal* reals, qreal* imags) {
824 
825  qreal dif;
826  for (size_t i=0; i<vec.size(); i++) {
827  dif = abs(real(vec[i]) - reals[i]);
828  if (dif > REAL_EPS)
829  return false;
830  dif = abs(imag(vec[i]) - imags[i]);
831  if (dif > REAL_EPS)
832  return false;
833  }
834  return true;
835 }
836 
837 /* Copies QMatrix into a CompelxMAtrix struct */
838 #define macro_copyQMatrix(dest, src) { \
839  for (size_t i=0; i<src.size(); i++) { \
840  for (size_t j=0; j<src.size(); j++) { \
841  dest.real[i][j] = real(src[i][j]); \
842  dest.imag[i][j] = imag(src[i][j]); \
843  } \
844  } \
845 }
847  DEMAND( qm.size() == 2 );
848  ComplexMatrix2 cm;
849  macro_copyQMatrix(cm, qm);
850  return cm;
851 }
853  DEMAND( qm.size() == 4 );
854  ComplexMatrix4 cm;
855  macro_copyQMatrix(cm, qm);
856  return cm;
857 }
859  DEMAND( qm.size() == (1u<<cm.numQubits) );
860  macro_copyQMatrix(cm, qm);
861 }
862 
864 #define macro_copyComplexMatrix(dest, src) { \
865  for (size_t i=0; i<dest.size(); i++) \
866  for (size_t j=0; j<dest.size(); j++) \
867  dest[i][j] = qcomp(src.real[i][j], src.imag[i][j]); \
868 }
870  QMatrix dest = getZeroMatrix(2);
871  macro_copyComplexMatrix(dest, src);
872  return dest;
873 }
875  QMatrix dest = getZeroMatrix(4);
876  macro_copyComplexMatrix(dest, src);
877  return dest;
878 }
880  DEMAND( src.real != NULL );
881  DEMAND( src.imag != NULL );
882  QMatrix dest = getZeroMatrix(1 << src.numQubits);
883  macro_copyComplexMatrix(dest, src);
884  return dest;
885 }
886 
888  qcomp a = qcomp(alpha.real, alpha.imag);
889  qcomp b = qcomp(beta.real, beta.imag);
890  QMatrix matr{
891  {a, -conj(b)},
892  {b, conj(a)}};
893  return matr;
894 }
895 
897  DEMAND( qureg.isDensityMatrix );
898 #ifdef DISTRIBUTED_MODE
899  DEMAND( qureg.numAmpsTotal < MPI_MAX_AMPS_IN_MSG );
900 #endif
901 
902  // ensure local qureg.stateVec is up to date
903  copyStateFromGPU(qureg);
905 
906  qreal* fullRe;
907  qreal* fullIm;
908 
909  // in distributed mode, give every node the full state vector
910 #ifdef DISTRIBUTED_MODE
911  fullRe = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullRe);
912  fullIm = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullIm);
913  MPI_Allgather(
914  qureg.stateVec.real, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
915  fullRe, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
916  MPI_Allgather(
917  qureg.stateVec.imag, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
918  fullIm, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
919 #else
920  fullRe = qureg.stateVec.real;
921  fullIm = qureg.stateVec.imag;
922 #endif
923 
924  // copy full state vector into a QVector
925  long long int dim = (1 << qureg.numQubitsRepresented);
926  QMatrix matr = getZeroMatrix(dim);
927  for (long long int n=0; n<qureg.numAmpsTotal; n++)
928  matr[n%dim][n/dim] = qcomp(fullRe[n], fullIm[n]);
929 
930  // clean up if we malloc'd the distributed array
931 #ifdef DISTRIBUTED_MODE
932  free(fullRe);
933  free(fullIm);
934 #endif
935  return matr;
936 }
937 
939  DEMAND( !qureg.isDensityMatrix );
940 #ifdef DISTRIBUTED_MODE
941  DEMAND( qureg.numAmpsTotal < MPI_MAX_AMPS_IN_MSG );
942 #endif
943 
944  // ensure local qureg.stateVec is up to date
945  copyStateFromGPU(qureg);
947 
948  qreal* fullRe;
949  qreal* fullIm;
950 
951  // in distributed mode, give every node the full state vector
952 #ifdef DISTRIBUTED_MODE
953  fullRe = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullRe);
954  fullIm = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullIm);
955 
956  MPI_Allgather(
957  qureg.stateVec.real, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
958  fullRe, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
959  MPI_Allgather(
960  qureg.stateVec.imag, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
961  fullIm, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
962 #else
963  fullRe = qureg.stateVec.real;
964  fullIm = qureg.stateVec.imag;
965 #endif
966 
967  // copy full state vector into a QVector
968  QVector vec = QVector(qureg.numAmpsTotal);
969  for (long long int i=0; i<qureg.numAmpsTotal; i++)
970  vec[i] = qcomp(fullRe[i], fullIm[i]);
971 
972  // clean up if we malloc'd distrib array
973 #ifdef DISTRIBUTED_MODE
974  free(fullRe);
975  free(fullIm);
976 #endif
977  return vec;
978 }
979 
981  long long int totalElems = (1LL << op.numQubits);
982 #ifdef DISTRIBUTED_MODE
983  DEMAND( totalElems < MPI_MAX_AMPS_IN_MSG );
984 #endif
985 
986  qreal* fullRe;
987  qreal* fullIm;
988 
989  // in distributed mode, give every node the full diagonal operator
990 #ifdef DISTRIBUTED_MODE
991  fullRe = (qreal*) malloc(totalElems * sizeof *fullRe);
992  fullIm = (qreal*) malloc(totalElems * sizeof *fullIm);
993 
994  MPI_Allgather(
995  op.real, op.numElemsPerChunk, MPI_QuEST_REAL,
996  fullRe, op.numElemsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
997  MPI_Allgather(
998  op.imag, op.numElemsPerChunk, MPI_QuEST_REAL,
999  fullIm, op.numElemsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
1000 #else
1001  fullRe = op.real;
1002  fullIm = op.imag;
1003 #endif
1004 
1005  // copy full state vector into a QVector
1006  QVector vec = QVector(totalElems);
1007  for (long long int i=0; i<totalElems; i++)
1008  vec[i] = qcomp(fullRe[i], fullIm[i]);
1009 
1010  // clean up if we malloc'd distrib array
1011 #ifdef DISTRIBUTED_MODE
1012  free(fullRe);
1013  free(fullIm);
1014 #endif
1015  return vec;
1016 }
1017 
1019  QVector vec = toQVector(op);
1020  QMatrix mat = getZeroMatrix(1LL << op.numQubits);
1021  for (size_t i=0; i<mat.size(); i++)
1022  mat[i][i] = vec[i];
1023  return mat;
1024 }
1025 
1026 void toQureg(Qureg qureg, QVector vec) {
1027  DEMAND( !qureg.isDensityMatrix );
1028  DEMAND( qureg.numAmpsTotal == (int) vec.size() );
1029 
1031 
1032  for (int i=0; i<qureg.numAmpsPerChunk; i++) {
1033  int ind = qureg.chunkId*qureg.numAmpsPerChunk + i;
1034  qureg.stateVec.real[i] = real(vec[ind]);
1035  qureg.stateVec.imag[i] = imag(vec[ind]);
1036  }
1037  copyStateToGPU(qureg);
1038 }
1039 void toQureg(Qureg qureg, QMatrix mat) {
1040  DEMAND( qureg.isDensityMatrix );
1041  DEMAND( (1 << qureg.numQubitsRepresented) == (int) mat.size() );
1042 
1044 
1045  int len = (1 << qureg.numQubitsRepresented);
1046  for (int i=0; i<qureg.numAmpsPerChunk; i++) {
1047  int ind = qureg.chunkId*qureg.numAmpsPerChunk + i;
1048  qureg.stateVec.real[i] = real(mat[ind%len][ind/len]);
1049  qureg.stateVec.imag[i] = imag(mat[ind%len][ind/len]);
1050  }
1051  copyStateToGPU(qureg);
1052 }
1053 
1054 void setRandomPauliSum(qreal* coeffs, pauliOpType* codes, int numQubits, int numTerms) {
1055  int i=0;
1056  for (int n=0; n<numTerms; n++) {
1057  coeffs[n] = getRandomReal(-5, 5);
1058  for (int q=0; q<numQubits; q++)
1059  codes[i++] = (pauliOpType) getRandomInt(0,4);
1060  }
1061 }
1063  setRandomPauliSum(hamil.termCoeffs, hamil.pauliCodes, hamil.numQubits, hamil.numSumTerms);
1064 }
1065 
1066 QMatrix toQMatrix(qreal* coeffs, pauliOpType* paulis, int numQubits, int numTerms) {
1067 
1068  // produce a numTargs-big matrix 'pauliSum' by pauli-matrix tensoring and summing
1069  QMatrix iMatr{{1,0},{0,1}};
1070  QMatrix xMatr{{0,1},{1,0}};
1071  QMatrix yMatr{{0,-1i},{1i,0}};
1072  QMatrix zMatr{{1,0},{0,-1}};
1073  QMatrix pauliSum = getZeroMatrix(1<<NUM_QUBITS);
1074 
1075  for (int t=0; t<numTerms; t++) {
1076  QMatrix pauliProd = QMatrix{{1}};
1077 
1078  for (int q=0; q<numQubits; q++) {
1079  int i = q + t*numQubits;
1080 
1081  QMatrix fac;
1082  pauliOpType code = paulis[i];
1083  if (code == PAULI_I) fac = iMatr;
1084  if (code == PAULI_X) fac = xMatr;
1085  if (code == PAULI_Y) fac = yMatr;
1086  if (code == PAULI_Z) fac = zMatr;
1087  pauliProd = getKroneckerProduct(fac, pauliProd);
1088  }
1089  pauliSum += coeffs[t] * pauliProd;
1090  }
1091 
1092  // a now 2^numQubits by 2^numQubits Hermitian matrix
1093  return pauliSum;
1094 }
1096  return toQMatrix(hamil.termCoeffs, hamil.pauliCodes, hamil.numQubits, hamil.numSumTerms);
1097 }
1098 
1099 class SubListGenerator : public Catch::Generators::IGenerator<int*> {
1100  int* list;
1101  int* sublist;
1102  int len;
1103  int sublen;
1104  vector<bool> featured;
1105 private:
1106  void createSublist() {
1107 
1108  // sublist to send to the user
1109  sublist = (int*) malloc(sublen * sizeof *sublist);
1110 
1111  // indicates which list members are currently in sublist
1112  featured = vector<bool>(len);
1113  fill(featured.end() - sublen, featured.end(), true);
1114  }
1115 
1117 
1118  // choose the next combination
1119  int j=0;
1120  for (int i=0; i<len; i++)
1121  if (featured[i])
1122  sublist[j++] = list[i];
1123 
1124  // prepare for permuting
1125  std::sort(sublist, sublist+sublen);
1126  }
1127 public:
1128  SubListGenerator(int* elems, int numElems, int numSamps) {
1129 
1130  DEMAND( numSamps <= numElems );
1131 
1132  // make a record of all elements
1133  len = numElems;
1134  list = (int*) malloc(len * sizeof *list);
1135  for (int i=0; i<len; i++)
1136  list[i] = elems[i];
1137 
1138  // prepare sublist
1139  sublen = numSamps;
1140  createSublist();
1141  prepareSublist();
1142  }
1143 
1145  Catch::Generators::GeneratorWrapper<int>&& gen,
1146  int numSamps, const int* exclude, int numExclude
1147  ) {
1148  // extract all generator elems
1149  vector<int> elems = vector<int>();
1150  do { elems.push_back(gen.get()); } while (gen.next());
1151 
1152  // make (int*) of non-excluded elems
1153  len = 0;
1154  list = (int*) malloc(elems.size() * sizeof *list);
1155  for (size_t i=0; i<elems.size(); i++) {
1156  int elem = elems[i];
1157  bool present = false;
1158  for (int j=0; j<numExclude; j++)
1159  if (elem == exclude[j]) {
1160  present = true;
1161  break;
1162  }
1163  if (!present)
1164  list[len++] = elem;
1165  }
1166 
1167  DEMAND( numSamps <= len );
1168 
1169  // prepare sublist
1170  sublen = numSamps;
1171  createSublist();
1172  prepareSublist();
1173  }
1174 
1175  int* const& get() const override {
1176  return sublist;
1177  }
1178 
1179  bool next() override {
1180 
1181  // offer next permutation of the current combination
1182  if (next_permutation(sublist, sublist+sublen))
1183  return true;
1184 
1185  // else generate the next combination
1186  if (next_permutation(featured.begin(), featured.end())) {
1187  prepareSublist();
1188  return true;
1189  }
1190 
1191  return false;
1192  }
1193 
1195  free(list);
1196  free(sublist);
1197  }
1198 };
1199 Catch::Generators::GeneratorWrapper<int*> sublists(
1200  int* list, int len, int sublen
1201 ) {
1202  return Catch::Generators::GeneratorWrapper<int*>(
1203  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1204  new SubListGenerator(list, len, sublen)));
1205 }
1206 Catch::Generators::GeneratorWrapper<int*> sublists(
1207  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps, const int* exclude, int numExclude
1208 ) {
1209  return Catch::Generators::GeneratorWrapper<int*>(
1210  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1211  new SubListGenerator(std::move(gen), numSamps, exclude, numExclude)));
1212 }
1213 Catch::Generators::GeneratorWrapper<int*> sublists(
1214  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps, int excluded
1215 ) {
1216  int exclude[] = {excluded};
1217  return Catch::Generators::GeneratorWrapper<int*>(
1218  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1219  new SubListGenerator(std::move(gen), numSamps, exclude, 1)));
1220 }
1221 Catch::Generators::GeneratorWrapper<int*> sublists(
1222  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps
1223 ) {
1224  int exclude[] = {};
1225  return Catch::Generators::GeneratorWrapper<int*>(
1226  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1227  new SubListGenerator(std::move(gen), numSamps, exclude, 0)));
1228 }
1229 
1230 template <typename T>
1231 class SequenceGenerator : public Catch::Generators::IGenerator<T*> {
1233  int len;
1235  int ind;
1236  int seqLen;
1237 public:
1238  SequenceGenerator(T maxDigit_, int numDigits) {
1239  ind = 0;
1240  len = numDigits;
1241  maxDigit = maxDigit_;
1242  seqLen = (int) pow(1 + (int) maxDigit, len);
1243  digits = (T*) malloc(numDigits * sizeof *digits);
1244  for (int i=0; i<numDigits; i++)
1245  digits[i] = (T) 0;
1246  ind++;
1247  }
1248 
1249  T* const& get() const override {
1250  return digits;
1251  }
1252 
1253  bool next() override {
1254  bool isNext = (ind++) < seqLen;
1255  if (isNext) {
1256  int i=0;
1257  while (digits[i] == maxDigit)
1258  digits[i++] = (T) 0;
1259  digits[i] = (T) ((int) digits[i] + 1);
1260  }
1261  return isNext;
1262  }
1263 
1265  free(digits);
1266  }
1267 };
1268 Catch::Generators::GeneratorWrapper<int*> bitsets(int numBits) {
1269  return Catch::Generators::GeneratorWrapper<int*>(
1270  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1271  new SequenceGenerator<int>(1, numBits)));
1272 }
1273 Catch::Generators::GeneratorWrapper<int*> sequences(int base, int numDigits) {
1274  return Catch::Generators::GeneratorWrapper<int*>(
1275  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1276  new SequenceGenerator<int>(base-1, numDigits)));
1277 }
1278 Catch::Generators::GeneratorWrapper<pauliOpType*> pauliseqs(int numPaulis) {
1279  return Catch::Generators::GeneratorWrapper<pauliOpType*>(
1280  std::unique_ptr<Catch::Generators::IGenerator<pauliOpType*>>(
1281  new SequenceGenerator<pauliOpType>(PAULI_Z, numPaulis)));
1282 }
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:293
void copyStateFromGPU(Qureg qureg)
In GPU mode, this copies the state-vector (or density matrix) from GPU memory (qureg....
Definition: QuEST_cpu.c:39
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
SequenceGenerator(T maxDigit_, int numDigits)
Definition: utilities.cpp:1238
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:864
@ PAULI_I
Definition: QuEST.h:96
QMatrix getConjugateTranspose(QMatrix a)
Returns the conjugate transpose of the complex square matrix a.
Definition: utilities.cpp:179
int getRandomInt(int min, int max)
Returns a random integer between min (inclusive) and max (exclusive), from the uniform distribution.
Definition: utilities.cpp:481
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:533
SubListGenerator(Catch::Generators::GeneratorWrapper< int > &&gen, int numSamps, const int *exclude, int numExclude)
Definition: utilities.cpp:1144
#define NUM_QUBITS
The default number of qubits in the registers created for unit testing (both statevectors and density...
Definition: utilities.hpp:36
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:219
QVector operator+(const QVector &v1, const QVector &v2)
Definition: utilities.cpp:26
QMatrix getRandomUnitary(int numQb)
Returns a uniformly random (under Haar) 2^numQb-by-2^numQb unitary matrix.
Definition: utilities.cpp:485
qreal getRandomReal(qreal min, qreal max)
Returns a random real between min (inclusive) and max (exclusive), from the uniform distribution.
Definition: utilities.cpp:410
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:159
Represents a 4x4 matrix of complex numbers.
Definition: QuEST.h:125
bool next() override
Definition: utilities.cpp:1253
Represents a general 2^N by 2^N matrix of complex numbers.
Definition: QuEST.h:136
#define qreal
QMatrix toQMatrix(ComplexMatrix2 src)
Returns a copy of the given 2-by-2 matrix.
Definition: utilities.cpp:869
void toQureg(Qureg qureg, QVector vec)
Initialises the state-vector qureg to have the same amplitudes as vec.
Definition: utilities.cpp:1026
unsigned int calcLog2(long unsigned int res)
Returns log2 of numbers which must be gauranteed to be 2^n.
Definition: utilities.cpp:361
@ PAULI_X
Definition: QuEST.h:96
void operator-=(QVector &v1, const QVector &v2)
Definition: utilities.cpp:67
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:205
QVector operator*(const qcomp &a, const QVector &v)
Definition: utilities.cpp:40
SubListGenerator(int *elems, int numElems, int numSamps)
Definition: utilities.cpp:1128
bool next() override
Definition: utilities.cpp:1179
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:1054
void toComplexMatrixN(QMatrix qm, ComplexMatrixN cm)
Initialises cm with the values of qm.
Definition: utilities.cpp:858
int chunkId
The position of the chunk of the state vector held by this process in the full state vector.
Definition: QuEST.h:217
qcomp expI(qreal phase)
Returns the unit-norm complex number exp(i*phase).
Definition: utilities.cpp:406
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:191
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:211
QVector toQVector(Qureg qureg)
Returns an equal-size copy of the given state-vector qureg.
Definition: utilities.cpp:938
long long int numAmpsPerChunk
Number of probability amplitudes held in stateVec by this process In the non-MPI version,...
Definition: QuEST.h:213
qreal * termCoeffs
The coefficient of each Pauli product. This is a length numSumTerms array.
Definition: QuEST.h:164
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:1273
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:162
ComplexMatrix4 toComplexMatrix4(QMatrix qm)
Returns a ComplexMatrix4 copy of QMatix qm.
Definition: utilities.cpp:852
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:36
void operator*=(QVector &v1, const qcomp &a)
Definition: utilities.cpp:70
int numQubits
The number of qubits this operator can act on (informing its size)
Definition: QuEST.h:181
int numSumTerms
The number of terms in the weighted sum, or the number of Pauli products.
Definition: QuEST.h:166
Represents a diagonal complex operator on the full Hilbert state of a Qureg.
Definition: QuEST.h:178
@ PAULI_Y
Definition: QuEST.h:96
Represents a weighted sum of pauli products.
Definition: QuEST.h:158
QVector getRandomStateVector(int numQb)
Returns a random numQb-length L2-normalised state-vector from an undisclosed distribution.
Definition: utilities.cpp:453
qreal ** real
Definition: QuEST.h:139
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:368
Represents a system of qubits.
Definition: QuEST.h:203
qreal ** imag
Definition: QuEST.h:140
T *const & get() const override
Definition: utilities.cpp:1249
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:1278
ComplexArray stateVec
Computational state amplitudes - a subset thereof in the MPI version.
Definition: QuEST.h:222
vector< bool > featured
Definition: utilities.cpp:1104
long long int numElemsPerChunk
The number of the 2^numQubits amplitudes stored on each distributed node.
Definition: QuEST.h:183
int isDensityMatrix
Whether this instance is a density-state representation.
Definition: QuEST.h:206
int numQubits
Definition: QuEST.h:138
void updateIndices(int oldEl, int newEl, int *list1, int len1, int *list2, int len2)
Definition: utilities.cpp:276
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:846
int numQubits
The number of qubits for which this Hamiltonian is defined.
Definition: QuEST.h:168
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:1199
QVector getNormalised(QVector vec)
Returns an L2-normalised copy of vec, using Kahan summation for improved accuracy.
Definition: utilities.cpp:431
int *const & get() const override
Definition: utilities.cpp:1175
int numQubitsRepresented
The number of qubits represented in either the state-vector or density matrix.
Definition: QuEST.h:208
long long int numAmpsTotal
Total number of amplitudes, which are possibly distributed among machines.
Definition: QuEST.h:215
qreal * real
The real values of the 2^numQubits complex elements.
Definition: QuEST.h:189
qreal real
Definition: QuEST.h:105
QMatrix getIdentityMatrix(size_t dim)
Returns a dim-by-dim identity matrix.
Definition: utilities.cpp:151
qreal imag
Definition: QuEST.h:106
QMatrix getKroneckerProduct(QMatrix a, QMatrix b)
Returns the kronecker product of a and b, where a and b are square but possibly differently-sized com...
Definition: utilities.cpp:169
QMatrix getRandomDensityMatrix(int numQb)
Returns a random numQb-by-numQb density matrix, from an undisclosed distribution, in a very mixed sta...
Definition: utilities.cpp:457
QMatrix getExponentialOfDiagonalMatrix(QMatrix a)
Returns the matrix exponential of a diagonal, square, complex matrix.
Definition: utilities.cpp:187
QMatrix getZeroMatrix(size_t dim)
Returns a dim-by-dim square complex matrix, initialised to all zeroes.
Definition: utilities.cpp:143
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:420
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:686
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:387
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:573
Catch::Generators::GeneratorWrapper< int * > bitsets(int numBits)
Returns a Catch2 generator of every numBits-length bit-set, in increasing lexographic order,...
Definition: utilities.cpp:1268
#define DEMAND(cond)
Definition: utilities.cpp:24
QVector operator-(const QVector &v1, const QVector &v2)
Definition: utilities.cpp:33
#define macro_copyQMatrix(dest, src)
Definition: utilities.cpp:838
Represents a 2x2 matrix of complex numbers.
Definition: QuEST.h:114