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  QMatrix &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  QVector &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 bool areEqual(Qureg qureg1, Qureg qureg2, qreal precision) {
682  DEMAND( qureg1.isDensityMatrix == qureg2.isDensityMatrix );
683  DEMAND( qureg1.numAmpsTotal == qureg2.numAmpsTotal );
684 
685  copyStateFromGPU(qureg1);
686  copyStateFromGPU(qureg2);
688 
689  // loop terminates when areEqual = 0
690  int ampsAgree = 1;
691  for (long long int i=0; ampsAgree && i<qureg1.numAmpsPerChunk; i++)
692  ampsAgree = (
693  absReal(qureg1.stateVec.real[i] - qureg2.stateVec.real[i]) < precision
694  && absReal(qureg1.stateVec.imag[i] - qureg2.stateVec.imag[i]) < precision);
695 
696  // if one node's partition wasn't equal, all-nodes must report not-equal
697  int allAmpsAgree = ampsAgree;
698 #ifdef DISTRIBUTED_MODE
699  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
700 #endif
701 
702  return allAmpsAgree;
703 }
704 bool areEqual(Qureg qureg1, Qureg qureg2) {
705  return areEqual(qureg1, qureg2, REAL_EPS);
706 }
707 
708 bool areEqual(Qureg qureg, QVector vec, qreal precision) {
709  DEMAND( !qureg.isDensityMatrix );
710  DEMAND( (int) vec.size() == qureg.numAmpsTotal );
711 
712  copyStateFromGPU(qureg);
714 
715  // the starting index in vec of this node's qureg partition.
716  long long int startInd = qureg.chunkId * qureg.numAmpsPerChunk;
717 
718  int ampsAgree = 1;
719  for (long long int i=0; i<qureg.numAmpsPerChunk; i++) {
720  qreal realDif = absReal(qureg.stateVec.real[i] - real(vec[startInd+i]));
721  qreal imagDif = absReal(qureg.stateVec.imag[i] - imag(vec[startInd+i]));
722 
723  if (realDif > precision || imagDif > precision) {
724  ampsAgree = 0;
725 
726  // debug
727  printf("Disagreement at %lld: %g + i(%g) VS %g + i(%g)\n",
728  startInd+i, qureg.stateVec.real[i], qureg.stateVec.imag[i],
729  real(vec[startInd+i]), imag(vec[startInd+i]));
730 
731  break;
732  }
733  }
734 
735  // if one node's partition wasn't equal, all-nodes must report not-equal
736  int allAmpsAgree = ampsAgree;
737 #ifdef DISTRIBUTED_MODE
738  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
739 #endif
740 
741  return allAmpsAgree;
742 }
743 bool areEqual(Qureg qureg, QVector vec) {
744  return areEqual(qureg, vec, REAL_EPS);
745 }
746 
747 bool areEqual(Qureg qureg, QMatrix matr, qreal precision) {
748  DEMAND( qureg.isDensityMatrix );
749  DEMAND( (int) (matr.size()*matr.size()) == qureg.numAmpsTotal );
750 
751  // ensure local qureg.stateVec is up to date
752  copyStateFromGPU(qureg);
754 
755  // the starting index in vec of this node's qureg partition.
756  long long int startInd = qureg.chunkId * qureg.numAmpsPerChunk;
757  long long int globalInd, row, col, i;
758  int ampsAgree;
759 
760  // compare each of this node's amplitude to the corresponding matr sub-matrix
761  for (i=0; i<qureg.numAmpsPerChunk; i++) {
762  globalInd = startInd + i;
763  row = globalInd % matr.size();
764  col = globalInd / matr.size();
765  qreal realDif = absReal(qureg.stateVec.real[i] - real(matr[row][col]));
766  qreal imagDif = absReal(qureg.stateVec.imag[i] - imag(matr[row][col]));
767  ampsAgree = (realDif < precision && imagDif < precision);
768 
769  // DEBUG
770  if (!ampsAgree) {
771  printf("node %d has a disagreement at (global) index %lldd of (%g) + i(%g)\n",
772  qureg.chunkId, globalInd, realDif, imagDif
773  );
774  }
775 
776  // break loop as soon as amplitudes disagree
777  if (!ampsAgree)
778  break;
779 
780  /* TODO:
781  * of the nodes which disagree, the lowest-rank should send its
782  * disagreeing (i, row, col, stateVec[i]) to rank 0 which should
783  * report it immediately (before the impending DEMAND failure)
784  * using FAIL_CHECK, so users can determine nature of disagreement
785  * (e.g. numerical precision).
786  * Note FAIL_CHECK accepts << like cout, e.g.
787  * FAIL_CHECK( "Amp at (" << row << ", " << col ") disagreed" );
788  */
789  }
790 
791  // if one node's partition wasn't equal, all-nodes must report not-equal
792  int allAmpsAgree = ampsAgree;
793 #ifdef DISTRIBUTED_MODE
794  MPI_Allreduce(&ampsAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD);
795 #endif
796 
797  return allAmpsAgree;
798 }
799 bool areEqual(Qureg qureg, QMatrix matr) {
800  return areEqual(qureg, matr, REAL_EPS);
801 }
802 
803 /* Copies QMatrix into a CompelxMAtrix struct */
804 #define macro_copyQMatrix(dest, src) { \
805  for (size_t i=0; i<src.size(); i++) { \
806  for (size_t j=0; j<src.size(); j++) { \
807  dest.real[i][j] = real(src[i][j]); \
808  dest.imag[i][j] = imag(src[i][j]); \
809  } \
810  } \
811 }
813  DEMAND( qm.size() == 2 );
814  ComplexMatrix2 cm;
815  macro_copyQMatrix(cm, qm);
816  return cm;
817 }
819  DEMAND( qm.size() == 4 );
820  ComplexMatrix4 cm;
821  macro_copyQMatrix(cm, qm);
822  return cm;
823 }
825  DEMAND( qm.size() == (1u<<cm.numQubits) );
826  macro_copyQMatrix(cm, qm);
827 }
828 
830 #define macro_copyComplexMatrix(dest, src) { \
831  for (size_t i=0; i<dest.size(); i++) \
832  for (size_t j=0; j<dest.size(); j++) \
833  dest[i][j] = qcomp(src.real[i][j], src.imag[i][j]); \
834 }
836  QMatrix dest = getZeroMatrix(2);
837  macro_copyComplexMatrix(dest, src);
838  return dest;
839 }
841  QMatrix dest = getZeroMatrix(4);
842  macro_copyComplexMatrix(dest, src);
843  return dest;
844 }
846  DEMAND( src.real != NULL );
847  DEMAND( src.imag != NULL );
848  QMatrix dest = getZeroMatrix(1 << src.numQubits);
849  macro_copyComplexMatrix(dest, src);
850  return dest;
851 }
852 
854  qcomp a = qcomp(alpha.real, alpha.imag);
855  qcomp b = qcomp(beta.real, beta.imag);
856  QMatrix matr{
857  {a, -conj(b)},
858  {b, conj(a)}};
859  return matr;
860 }
861 
863  DEMAND( qureg.isDensityMatrix );
864 #ifdef DISTRIBUTED_MODE
865  DEMAND( qureg.numAmpsTotal < MPI_MAX_AMPS_IN_MSG );
866 #endif
867 
868  // ensure local qureg.stateVec is up to date
869  copyStateFromGPU(qureg);
871 
872  qreal* fullRe;
873  qreal* fullIm;
874 
875  // in distributed mode, give every node the full state vector
876 #ifdef DISTRIBUTED_MODE
877  fullRe = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullRe);
878  fullIm = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullIm);
879  MPI_Allgather(
880  qureg.stateVec.real, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
881  fullRe, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
882  MPI_Allgather(
883  qureg.stateVec.imag, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
884  fullIm, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
885 #else
886  fullRe = qureg.stateVec.real;
887  fullIm = qureg.stateVec.imag;
888 #endif
889 
890  // copy full state vector into a QVector
891  long long int dim = (1 << qureg.numQubitsRepresented);
892  QMatrix matr = getZeroMatrix(dim);
893  for (long long int n=0; n<qureg.numAmpsTotal; n++)
894  matr[n%dim][n/dim] = qcomp(fullRe[n], fullIm[n]);
895 
896  // clean up if we malloc'd the distributed array
897 #ifdef DISTRIBUTED_MODE
898  free(fullRe);
899  free(fullIm);
900 #endif
901  return matr;
902 }
903 
905  DEMAND( !qureg.isDensityMatrix );
906 #ifdef DISTRIBUTED_MODE
907  DEMAND( qureg.numAmpsTotal < MPI_MAX_AMPS_IN_MSG );
908 #endif
909 
910  // ensure local qureg.stateVec is up to date
911  copyStateFromGPU(qureg);
913 
914  qreal* fullRe;
915  qreal* fullIm;
916 
917  // in distributed mode, give every node the full state vector
918 #ifdef DISTRIBUTED_MODE
919  fullRe = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullRe);
920  fullIm = (qreal*) malloc(qureg.numAmpsTotal * sizeof *fullIm);
921 
922  MPI_Allgather(
923  qureg.stateVec.real, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
924  fullRe, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
925  MPI_Allgather(
926  qureg.stateVec.imag, qureg.numAmpsPerChunk, MPI_QuEST_REAL,
927  fullIm, qureg.numAmpsPerChunk, MPI_QuEST_REAL, MPI_COMM_WORLD);
928 #else
929  fullRe = qureg.stateVec.real;
930  fullIm = qureg.stateVec.imag;
931 #endif
932 
933  // copy full state vector into a QVector
934  QVector vec = QVector(qureg.numAmpsTotal);
935  for (long long int i=0; i<qureg.numAmpsTotal; i++)
936  vec[i] = qcomp(fullRe[i], fullIm[i]);
937 
938  // clean up if we malloc'd distrib array
939 #ifdef DISTRIBUTED_MODE
940  free(fullRe);
941  free(fullIm);
942 #endif
943  return vec;
944 }
945 
946 void toQureg(Qureg qureg, QVector vec) {
947  DEMAND( !qureg.isDensityMatrix );
948  DEMAND( qureg.numAmpsTotal == (int) vec.size() );
949 
951 
952  for (int i=0; i<qureg.numAmpsPerChunk; i++) {
953  int ind = qureg.chunkId*qureg.numAmpsPerChunk + i;
954  qureg.stateVec.real[i] = real(vec[ind]);
955  qureg.stateVec.imag[i] = imag(vec[ind]);
956  }
957  copyStateToGPU(qureg);
958 }
959 void toQureg(Qureg qureg, QMatrix mat) {
960  DEMAND( qureg.isDensityMatrix );
961  DEMAND( (1 << qureg.numQubitsRepresented) == (int) mat.size() );
962 
964 
965  int len = (1 << qureg.numQubitsRepresented);
966  for (int i=0; i<qureg.numAmpsPerChunk; i++) {
967  int ind = qureg.chunkId*qureg.numAmpsPerChunk + i;
968  qureg.stateVec.real[i] = real(mat[ind%len][ind/len]);
969  qureg.stateVec.imag[i] = imag(mat[ind%len][ind/len]);
970  }
971  copyStateToGPU(qureg);
972 }
973 
974 class SubListGenerator : public Catch::Generators::IGenerator<int*> {
975  int* list;
976  int* sublist;
977  int len;
978  int sublen;
979  vector<bool> featured;
980 private:
981  void createSublist() {
982 
983  // sublist to send to the user
984  sublist = (int*) malloc(sublen * sizeof *sublist);
985 
986  // indicates which list members are currently in sublist
987  featured = vector<bool>(len);
988  fill(featured.end() - sublen, featured.end(), true);
989  }
990 
991  void prepareSublist() {
992 
993  // choose the next combination
994  int j=0;
995  for (int i=0; i<len; i++)
996  if (featured[i])
997  sublist[j++] = list[i];
998 
999  // prepare for permuting
1000  std::sort(sublist, sublist+sublen);
1001  }
1002 public:
1003  SubListGenerator(int* elems, int numElems, int numSamps) {
1004 
1005  DEMAND( numSamps <= numElems );
1006 
1007  // make a record of all elements
1008  len = numElems;
1009  list = (int*) malloc(len * sizeof *list);
1010  for (int i=0; i<len; i++)
1011  list[i] = elems[i];
1012 
1013  // prepare sublist
1014  sublen = numSamps;
1015  createSublist();
1016  prepareSublist();
1017  }
1018 
1020  Catch::Generators::GeneratorWrapper<int>&& gen,
1021  int numSamps, const int* exclude, int numExclude
1022  ) {
1023  // extract all generator elems
1024  vector<int> elems = vector<int>();
1025  do { elems.push_back(gen.get()); } while (gen.next());
1026 
1027  // make (int*) of non-excluded elems
1028  len = 0;
1029  list = (int*) malloc(elems.size() * sizeof *list);
1030  for (size_t i=0; i<elems.size(); i++) {
1031  int elem = elems[i];
1032  bool present = false;
1033  for (int j=0; j<numExclude; j++)
1034  if (elem == exclude[j]) {
1035  present = true;
1036  break;
1037  }
1038  if (!present)
1039  list[len++] = elem;
1040  }
1041 
1042  DEMAND( numSamps <= len );
1043 
1044  // prepare sublist
1045  sublen = numSamps;
1046  createSublist();
1047  prepareSublist();
1048  }
1049 
1050  int* const& get() const override {
1051  return sublist;
1052  }
1053 
1054  bool next() override {
1055 
1056  // offer next permutation of the current combination
1057  if (next_permutation(sublist, sublist+sublen))
1058  return true;
1059 
1060  // else generate the next combination
1061  if (next_permutation(featured.begin(), featured.end())) {
1062  prepareSublist();
1063  return true;
1064  }
1065 
1066  return false;
1067  }
1068 
1070  free(list);
1071  free(sublist);
1072  }
1073 };
1074 Catch::Generators::GeneratorWrapper<int*> sublists(
1075  int* list, int len, int sublen
1076 ) {
1077  return Catch::Generators::GeneratorWrapper<int*>(
1078  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1079  new SubListGenerator(list, len, sublen)));
1080 }
1081 Catch::Generators::GeneratorWrapper<int*> sublists(
1082  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps, const int* exclude, int numExclude
1083 ) {
1084  return Catch::Generators::GeneratorWrapper<int*>(
1085  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1086  new SubListGenerator(std::move(gen), numSamps, exclude, numExclude)));
1087 }
1088 Catch::Generators::GeneratorWrapper<int*> sublists(
1089  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps, int excluded
1090 ) {
1091  int exclude[] = {excluded};
1092  return Catch::Generators::GeneratorWrapper<int*>(
1093  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1094  new SubListGenerator(std::move(gen), numSamps, exclude, 1)));
1095 }
1096 Catch::Generators::GeneratorWrapper<int*> sublists(
1097  Catch::Generators::GeneratorWrapper<int>&& gen, int numSamps
1098 ) {
1099  int exclude[] = {};
1100  return Catch::Generators::GeneratorWrapper<int*>(
1101  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1102  new SubListGenerator(std::move(gen), numSamps, exclude, 0)));
1103 }
1104 
1105 template <typename T>
1106 class SequenceGenerator : public Catch::Generators::IGenerator<T*> {
1108  int len;
1110  int ind;
1111  int seqLen;
1112 public:
1113  SequenceGenerator(T maxDigit_, int numDigits) {
1114  ind = 0;
1115  len = numDigits;
1116  maxDigit = maxDigit_;
1117  seqLen = (int) pow(1 + (int) maxDigit, len);
1118  digits = (T*) malloc(numDigits * sizeof *digits);
1119  for (int i=0; i<numDigits; i++)
1120  digits[i] = (T) 0;
1121  ind++;
1122  }
1123 
1124  T* const& get() const override {
1125  return digits;
1126  }
1127 
1128  bool next() override {
1129  bool isNext = (ind++) < seqLen;
1130  if (isNext) {
1131  int i=0;
1132  while (digits[i] == maxDigit)
1133  digits[i++] = (T) 0;
1134  digits[i] = (T) ((int) digits[i] + 1);
1135  }
1136  return isNext;
1137  }
1138 
1140  free(digits);
1141  }
1142 };
1143 Catch::Generators::GeneratorWrapper<int*> bitsets(int numBits) {
1144  return Catch::Generators::GeneratorWrapper<int*>(
1145  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1146  new SequenceGenerator<int>(1, numBits)));
1147 }
1148 Catch::Generators::GeneratorWrapper<int*> sequences(int base, int numDigits) {
1149  return Catch::Generators::GeneratorWrapper<int*>(
1150  std::unique_ptr<Catch::Generators::IGenerator<int*>>(
1151  new SequenceGenerator<int>(base-1, numDigits)));
1152 }
1153 Catch::Generators::GeneratorWrapper<pauliOpType*> pauliseqs(int numPaulis) {
1154  return Catch::Generators::GeneratorWrapper<pauliOpType*>(
1155  std::unique_ptr<Catch::Generators::IGenerator<pauliOpType*>>(
1156  new SequenceGenerator<pauliOpType>(PAULI_Z, numPaulis)));
1157 }
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:1113
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:830
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:1019
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:1128
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:835
void toQureg(Qureg qureg, QVector vec)
Initialises the state-vector qureg to have the same amplitudes as vec.
Definition: utilities.cpp:946
unsigned int calcLog2(long unsigned int res)
Returns log2 of numbers which must be gauranteed to be 2^n.
Definition: utilities.cpp:361
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:1003
bool next() override
Definition: utilities.cpp:1054
void toComplexMatrixN(QMatrix qm, ComplexMatrixN cm)
Initialises cm with the values of qm.
Definition: utilities.cpp:824
int chunkId
The position of the chunk of the state vector held by this process in the full state vector.
Definition: QuEST.h:174
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
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:904
long long int numAmpsPerChunk
Number of probability amplitudes held in stateVec by this process In the non-MPI version,...
Definition: QuEST.h:170
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:1148
QVector operator/(const QVector &v, const qcomp &a)
Definition: utilities.cpp:49
#define qcomp
ComplexMatrix4 toComplexMatrix4(QMatrix qm)
Returns a ComplexMatrix4 copy of QMatix qm.
Definition: utilities.cpp:818
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
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:160
qreal ** imag
Definition: QuEST.h:140
T *const & get() const override
Definition: utilities.cpp:1124
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:1153
ComplexArray stateVec
Computational state amplitudes - a subset thereof in the MPI version.
Definition: QuEST.h:179
vector< bool > featured
Definition: utilities.cpp:979
int isDensityMatrix
Whether this instance is a density-state representation.
Definition: QuEST.h:163
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:812
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:1074
QVector getNormalised(QVector vec)
Returns an L2-normalised copy of vec, using Kahan summation for improved accuracy.
Definition: utilities.cpp:431
void prepareSublist()
Definition: utilities.cpp:991
int *const & get() const override
Definition: utilities.cpp:1050
int numQubitsRepresented
The number of qubits represented in either the state-vector or density matrix.
Definition: QuEST.h:165
long long int numAmpsTotal
Total number of amplitudes, which are possibly distributed among machines.
Definition: QuEST.h:172
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
void createSublist()
Definition: utilities.cpp:981
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 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:1143
#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:804
Represents a 2x2 matrix of complex numbers.
Definition: QuEST.h:114