test_calculations.cpp
Go to the documentation of this file.
1 
2 #include "catch.hpp"
3 #include "QuEST.h"
4 #include "utilities.hpp"
5 
6 /* allows concise use of Contains in catch's REQUIRE_THROWS_WITH */
7 using Catch::Matchers::Contains;
8 
9 
10 
15 TEST_CASE( "calcDensityInnerProduct", "[calculations]" ) {
16 
19 
20  SECTION( "correctness" ) {
21 
22  // repeat these random tests 10 times
23  GENERATE( range(0,10) );
24 
25  SECTION( "density-matrix" ) {
26 
27  SECTION( "pure" ) {
28 
29  // mat1 = |r1><r1|
31  toQureg(mat1, getKetBra(r1,r1));
32 
33  // mat2 = |r2><r2|
35  toQureg(mat2, getKetBra(r2,r2));
36 
37  // prod( |r1><r1|, |r2><r2| ) = |<r1|r2>|^2
38  qcomp prod = 0;
39  for (size_t i=0; i<r1.size(); i++)
40  prod += conj(r1[i]) * r2[i];
41  qreal densProd = pow(abs(prod),2);
42 
43  REQUIRE( calcDensityInnerProduct(mat1,mat2) == Approx(densProd) );
44  }
45  SECTION( "mixed" ) {
46 
49  toQureg(mat1, ref1);
50  toQureg(mat2, ref2);
51 
52  // prod(mat1, mat2) = sum_{ij} conj(mat1_{ij}) * mat2_{ij}
53  qcomp refProd = 0;
54  for (size_t i=0; i<ref1.size(); i++)
55  for (size_t j=0; j<ref1.size(); j++)
56  refProd += conj(ref1[i][j]) * ref2[i][j];
57  REQUIRE( imag(refProd) == Approx(0).margin(REAL_EPS) );
58 
59  REQUIRE( calcDensityInnerProduct(mat1,mat2) == Approx(real(refProd)) );
60 
61  // should be invariant under ordering
62  REQUIRE( calcDensityInnerProduct(mat1,mat2) == Approx(calcDensityInnerProduct(mat2,mat1)) );
63  }
64  SECTION( "unnormalised" ) {
65 
66  // set both to random (non-Hermitian) complex matrices
69  toQureg(mat1, ref1);
70  toQureg(mat2, ref2);
71 
72  // prod(mat1, mat2) = real(sum_{ij} conj(mat1_{ij}) * mat2_{ij})
73  qcomp refProd = 0;
74  for (size_t i=0; i<ref1.size(); i++)
75  for (size_t j=0; j<ref1.size(); j++)
76  refProd += conj(ref1[i][j]) * ref2[i][j];
77 
78  REQUIRE( calcDensityInnerProduct(mat1,mat2) == Approx(real(refProd)) );
79  }
80  }
81  }
82  SECTION( "input validation" ) {
83 
84  SECTION( "dimensions" ) {
85 
87  REQUIRE_THROWS_WITH( calcDensityInnerProduct(mat1,mat3), Contains("Dimensions") && Contains("don't match") );
88  destroyQureg(mat3, QUEST_ENV);
89  }
90  SECTION( "state-vectors" ) {
91 
93 
94  REQUIRE_THROWS_WITH( calcDensityInnerProduct(mat1,vec), Contains("valid only for density matrices") );
95  REQUIRE_THROWS_WITH( calcDensityInnerProduct(vec,mat1), Contains("valid only for density matrices") );
96  REQUIRE_THROWS_WITH( calcDensityInnerProduct(vec,vec), Contains("valid only for density matrices") );
97 
98  destroyQureg(vec, QUEST_ENV);
99  }
100  }
101  destroyQureg(mat1, QUEST_ENV);
102  destroyQureg(mat2, QUEST_ENV);
103 }
104 
105 
106 
111 TEST_CASE( "calcExpecDiagonalOp", "[calculations]" ) {
112 
115  initDebugState(vec);
116  initDebugState(mat);
117  QVector vecRef = toQVector(vec);
118  QMatrix matRef = toQMatrix(mat);
119 
120  SECTION( "correctness" ) {
121 
122  // try 10 random operators
123  GENERATE( range(0,10) );
124 
125  // make a totally random (non-Hermitian) diagonal oeprator
127  for (long long int i=0; i<op.numElemsPerChunk; i++) {
128  op.real[i] = getRandomReal(-5, 5);
129  op.imag[i] = getRandomReal(-5, 5);
130  }
131  syncDiagonalOp(op);
132 
133  SECTION( "state-vector" ) {
134 
135  /* calcExpecDiagOp calculates <qureg|diag|qureg> */
136 
137  QVector sumRef = toQMatrix(op) * vecRef;
138  qcomp prod = 0;
139  for (size_t i=0; i<vecRef.size(); i++)
140  prod += conj(vecRef[i]) * sumRef[i];
141 
142  Complex res = calcExpecDiagonalOp(vec, op);
143  REQUIRE( res.real == Approx(real(prod)).margin(REAL_EPS) );
144  REQUIRE( res.imag == Approx(imag(prod)).margin(REAL_EPS) );
145  }
146  SECTION( "density-matrix" ) {
147 
148  /* calcExpecDiagOp calculates Trace( diag * qureg ) */
149  matRef = toQMatrix(op) * matRef;
150  qcomp tr = 0;
151  for (size_t i=0; i<matRef.size(); i++)
152  tr += matRef[i][i];
153 
154  Complex res = calcExpecDiagonalOp(mat, op);
155  REQUIRE( res.real == Approx(real(tr)).margin(100*REAL_EPS) );
156  REQUIRE( res.imag == Approx(imag(tr)).margin(100*REAL_EPS) );
157  }
158 
160  }
161  SECTION( "input validation" ) {
162 
163  SECTION( "mismatching size" ) {
164 
166 
167  REQUIRE_THROWS_WITH( calcExpecDiagonalOp(vec, op), Contains("equal number of qubits"));
168  REQUIRE_THROWS_WITH( calcExpecDiagonalOp(mat, op), Contains("equal number of qubits"));
169 
171  }
172  }
173  destroyQureg(vec, QUEST_ENV);
174  destroyQureg(mat, QUEST_ENV);
175 }
176 
177 
178 
183 TEST_CASE( "calcExpecPauliHamil", "[calculations]" ) {
184 
187  initDebugState(vec);
188  initDebugState(mat);
189  QVector vecRef = toQVector(vec);
190  QMatrix matRef = toQMatrix(mat);
191 
194 
195  SECTION( "correctness" ) {
196 
197  /* it's too expensive to try every possible Pauli configuration, so
198  * we'll try 10 random codes, and for each, random coefficients
199  */
200  GENERATE( range(0,10) );
201 
202  int numTerms = GENERATE( 1, 2, 10, 15 );
203  PauliHamil hamil = createPauliHamil(NUM_QUBITS, numTerms);
204  setRandomPauliSum(hamil);
205  QMatrix refHamil = toQMatrix(hamil);
206 
207  SECTION( "state-vector" ) {
208 
209  /* calcExpecPauliHamil calculates <qureg|pauliHum|qureg> */
210 
211  QVector sumRef = refHamil * vecRef;
212  qcomp prod = 0;
213  for (size_t i=0; i<vecRef.size(); i++)
214  prod += conj(vecRef[i]) * sumRef[i];
215  REQUIRE( imag(prod) == Approx(0).margin(10*REAL_EPS) );
216 
217  qreal res = calcExpecPauliHamil(vec, hamil, vecWork);
218  REQUIRE( res == Approx(real(prod)).margin(10*REAL_EPS) );
219  }
220  SECTION( "density-matrix" ) {
221 
222  /* calcExpecPauliHamil calculates Trace( pauliHamil * qureg ) */
223  matRef = refHamil * matRef;
224  qreal tr = 0;
225  for (size_t i=0; i<matRef.size(); i++)
226  tr += real(matRef[i][i]);
227  // (get real, since we start in a non-Hermitian state, hence diagonal isn't real)
228 
229  qreal res = calcExpecPauliHamil(mat, hamil, matWork);
230  REQUIRE( res == Approx(tr).margin(1E2*REAL_EPS) );
231  }
232 
233  destroyPauliHamil(hamil);
234  }
235  SECTION( "validation" ) {
236 
237  SECTION( "pauli codes" ) {
238 
239  int numTerms = 3;
240  PauliHamil hamil = createPauliHamil(NUM_QUBITS, numTerms);
241 
242  // make one pauli code wrong
243  hamil.pauliCodes[GENERATE_COPY( range(0,numTerms*NUM_QUBITS) )] = (pauliOpType) GENERATE( -1, 4 );
244  REQUIRE_THROWS_WITH( calcExpecPauliHamil(vec, hamil, vecWork), Contains("Invalid Pauli code") );
245 
246  destroyPauliHamil(hamil);
247  }
248  SECTION( "workspace type" ) {
249 
250  int numTerms = 1;
251  PauliHamil hamil = createPauliHamil(NUM_QUBITS, numTerms);
252 
253  REQUIRE_THROWS_WITH( calcExpecPauliHamil(vec, hamil, mat), Contains("Registers must both be state-vectors or both be density matrices") );
254  REQUIRE_THROWS_WITH( calcExpecPauliHamil(mat, hamil, vec), Contains("Registers must both be state-vectors or both be density matrices") );
255 
256  destroyPauliHamil(hamil);
257  }
258  SECTION( "workspace dimensions" ) {
259 
260  int numTerms = 1;
261  PauliHamil hamil = createPauliHamil(NUM_QUBITS, numTerms);
262 
263  Qureg vec2 = createQureg(NUM_QUBITS + 1, QUEST_ENV);
264  REQUIRE_THROWS_WITH( calcExpecPauliHamil(vec, hamil, vec2), Contains("Dimensions") && Contains("don't match") );
265  destroyQureg(vec2, QUEST_ENV);
266 
268  REQUIRE_THROWS_WITH( calcExpecPauliHamil(mat, hamil, mat2), Contains("Dimensions") && Contains("don't match") );
269  destroyQureg(mat2, QUEST_ENV);
270 
271  destroyPauliHamil(hamil);
272  }
273  SECTION( "matching hamiltonian qubits" ) {
274 
275  int numTerms = 1;
276  PauliHamil hamil = createPauliHamil(NUM_QUBITS + 1, numTerms);
277 
278  REQUIRE_THROWS_WITH( calcExpecPauliHamil(vec, hamil, vecWork), Contains("same number of qubits") );
279  REQUIRE_THROWS_WITH( calcExpecPauliHamil(mat, hamil, matWork), Contains("same number of qubits") );
280 
281  destroyPauliHamil(hamil);
282  }
283  }
284  destroyQureg(vec, QUEST_ENV);
285  destroyQureg(mat, QUEST_ENV);
286  destroyQureg(vecWork, QUEST_ENV);
287  destroyQureg(matWork, QUEST_ENV);
288 }
289 
290 
291 
296 TEST_CASE( "calcExpecPauliProd", "[calculations]" ) {
297 
300  initDebugState(vec);
301  initDebugState(mat);
302  QVector vecRef = toQVector(vec);
303  QMatrix matRef = toQMatrix(mat);
304 
307 
308  SECTION( "correctness" ) {
309 
310  int numTargs = GENERATE( range(1,NUM_QUBITS+1) );
311  int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
312 
313  /* it's too expensive to try ALL Pauli sequences, via
314  * pauliOpType* paulis = GENERATE_COPY( pauliseqs(numTargs) );.
315  * Furthermore, take(10, pauliseqs(numTargs)) will try the same pauli codes.
316  * Hence, we instead opt to repeatedlyrandomly generate pauliseqs
317  */
318  GENERATE( range(0,10) ); // gen 10 random pauli-codes for every targs
319  pauliOpType paulis[numTargs];
320  for (int i=0; i<numTargs; i++)
321  paulis[i] = (pauliOpType) getRandomInt(0,4);
322 
323  // produce a numTargs-big matrix 'pauliProd' by pauli-matrix tensoring
324  QMatrix iMatr{{1,0},{0,1}};
325  QMatrix xMatr{{0,1},{1,0}};
326  QMatrix yMatr{{0,-1i},{1i,0}};
327  QMatrix zMatr{{1,0},{0,-1}};
328  QMatrix pauliProd{{1}};
329  for (int i=0; i<numTargs; i++) {
330  QMatrix fac;
331  if (paulis[i] == PAULI_I) fac = iMatr;
332  if (paulis[i] == PAULI_X) fac = xMatr;
333  if (paulis[i] == PAULI_Y) fac = yMatr;
334  if (paulis[i] == PAULI_Z) fac = zMatr;
335  pauliProd = getKroneckerProduct(fac, pauliProd);
336  }
337 
338  SECTION( "state-vector" ) {
339 
340  /* calcExpecPauliProd calculates <qureg|pauliProd|qureg> */
341 
342  QVector prodRef = vecRef;
343  applyReferenceOp(prodRef, targs, numTargs, pauliProd);
344  qcomp prod = 0;
345  for (size_t i=0; i<vecRef.size(); i++)
346  prod += conj(vecRef[i]) * prodRef[i];
347  REQUIRE( imag(prod) == Approx(0).margin(REAL_EPS) );
348 
349  qreal res = calcExpecPauliProd(vec, targs, paulis, numTargs, vecWork);
350  REQUIRE( res == Approx(real(prod)).margin(REAL_EPS) );
351  }
352  SECTION( "density-matrix" ) {
353 
354  /* calcExpecPauliProd calculates Trace( pauliProd * qureg ) */
355 
356  // produce (pauliProd * mat)
357  QMatrix fullOp = getFullOperatorMatrix(NULL, 0, targs, numTargs, pauliProd, NUM_QUBITS);
358  matRef = fullOp * matRef;
359 
360  // compute real(trace(pauliProd * mat))
361  qreal tr = 0;
362  for (size_t i=0; i<matRef.size(); i++)
363  tr += real(matRef[i][i]);
364  // (get real, since we start in a non-Hermitian state, hence diagonal isn't real)
365 
366  qreal res = calcExpecPauliProd(mat, targs, paulis, numTargs, matWork);
367  REQUIRE( res == Approx(tr).margin(10*REAL_EPS) );
368  }
369  }
370  SECTION( "validation" ) {
371 
372  SECTION( "number of targets" ) {
373 
374  int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
375  REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, NULL, NULL, numTargs, vecWork), Contains("Invalid number of target") );
376  }
377  SECTION( "target indices" ) {
378 
379  int numTargs = 3;
380  int targs[3] = {0, 1, 2};
381 
382  // make one index wrong
383  targs[GENERATE( range(0,3) )] = GENERATE( -1, NUM_QUBITS );
384  REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, NULL, numTargs, vecWork), Contains("Invalid target qubit") );
385  }
386  SECTION( "repetition in targets" ) {
387 
388  int numTargs = 3;
389  int targs[3] = {0, 1, 1};
390  REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, NULL, numTargs, vecWork), Contains("target qubits must be unique") );
391  }
392  SECTION( "pauli codes" ) {
393 
394  int numTargs = 3;
395  int targs[3] = {0, 1, 2};
396  pauliOpType codes[3] = {PAULI_X, PAULI_Y, PAULI_Z};
397 
398  // make one pauli wrong
399  codes[GENERATE( range(0,3) )] = (pauliOpType) GENERATE( -1, 4 );
400  REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, codes, numTargs, vecWork), Contains("Invalid Pauli code") );
401  }
402  SECTION( "workspace type" ) {
403 
404  int numTargs = 1;
405  int targs[1] = {0};
406  pauliOpType codes[1] = {PAULI_I};
407 
408  REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, codes, numTargs, matWork), Contains("Registers must both be state-vectors or both be density matrices") );
409  REQUIRE_THROWS_WITH( calcExpecPauliProd(mat, targs, codes, numTargs, vecWork), Contains("Registers must both be state-vectors or both be density matrices") );
410  }
411  SECTION( "workspace dimensions" ) {
412 
413  int numTargs = 1;
414  int targs[1] = {0};
415  pauliOpType codes[1] = {PAULI_I};
416 
417  Qureg vec2 = createQureg(NUM_QUBITS + 1, QUEST_ENV);
418  REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, codes, numTargs, vec2), Contains("Dimensions") && Contains("don't match") );
419  destroyQureg(vec2, QUEST_ENV);
420 
422  REQUIRE_THROWS_WITH( calcExpecPauliProd(mat, targs, codes, numTargs, mat2), Contains("Dimensions") && Contains("don't match") );
423  destroyQureg(mat2, QUEST_ENV);
424  }
425  }
426  destroyQureg(vec, QUEST_ENV);
427  destroyQureg(mat, QUEST_ENV);
428  destroyQureg(vecWork, QUEST_ENV);
429  destroyQureg(matWork, QUEST_ENV);
430 }
431 
432 
433 
438 TEST_CASE( "calcExpecPauliSum", "[calculations]" ) {
439 
442  initDebugState(vec);
443  initDebugState(mat);
444  QVector vecRef = toQVector(vec);
445  QMatrix matRef = toQMatrix(mat);
446 
449 
450  SECTION( "correctness" ) {
451 
452  int numSumTerms = GENERATE( 1, 2, 10, 15 );
453 
454  /* it's too expensive to try every possible Pauli configuration, so
455  * we'll try 10 random codes, and for each, random coefficients
456  */
457  GENERATE( range(0,10) );
458  int totNumCodes = numSumTerms*NUM_QUBITS;
459  pauliOpType paulis[totNumCodes];
460  qreal coeffs[numSumTerms];
461  setRandomPauliSum(coeffs, paulis, NUM_QUBITS, numSumTerms);
462 
463  // produce a numTargs-big matrix 'pauliSum' by pauli-matrix tensoring and summing
464  QMatrix pauliSum = toQMatrix(coeffs, paulis, NUM_QUBITS, numSumTerms);
465 
466  SECTION( "state-vector" ) {
467 
468  /* calcExpecPauliSum calculates <qureg|pauliSum|qureg> */
469 
470  QVector sumRef = pauliSum * vecRef;
471  qcomp prod = 0;
472  for (size_t i=0; i<vecRef.size(); i++)
473  prod += conj(vecRef[i]) * sumRef[i];
474  REQUIRE( imag(prod) == Approx(0).margin(10*REAL_EPS) );
475 
476  qreal res = calcExpecPauliSum(vec, paulis, coeffs, numSumTerms, vecWork);
477  REQUIRE( res == Approx(real(prod)).margin(10*REAL_EPS) );
478  }
479  SECTION( "density-matrix" ) {
480 
481  /* calcExpecPauliSum calculates Trace( pauliSum * qureg ) */
482  matRef = pauliSum * matRef;
483  qreal tr = 0;
484  for (size_t i=0; i<matRef.size(); i++)
485  tr += real(matRef[i][i]);
486  // (get real, since we start in a non-Hermitian state, hence diagonal isn't real)
487 
488  qreal res = calcExpecPauliSum(mat, paulis, coeffs, numSumTerms, matWork);
489  REQUIRE( res == Approx(tr).margin(1E2*REAL_EPS) );
490  }
491  }
492  SECTION( "validation" ) {
493 
494  SECTION( "number of sum terms" ) {
495 
496  int numSumTerms = GENERATE( -1, 0 );
497  REQUIRE_THROWS_WITH( calcExpecPauliSum(vec, NULL, NULL, numSumTerms, vecWork), Contains("Invalid number of terms in the Pauli sum") );
498  }
499  SECTION( "pauli codes" ) {
500 
501  // make valid params
502  int numSumTerms = 3;
503  qreal coeffs[numSumTerms];
504  pauliOpType codes[numSumTerms*NUM_QUBITS];
505  for (int i=0; i<numSumTerms*NUM_QUBITS; i++)
506  codes[i] = PAULI_I;
507 
508  // make one pauli wrong
509  codes[GENERATE_COPY( range(0,numSumTerms*NUM_QUBITS) )] = (pauliOpType) GENERATE( -1, 4 );
510  REQUIRE_THROWS_WITH( calcExpecPauliSum(vec, codes, coeffs, numSumTerms, vecWork), Contains("Invalid Pauli code") );
511  }
512  SECTION( "workspace type" ) {
513 
514  // make valid params
515  int numSumTerms = 1;
516  qreal coeffs[1] = {0};
517  pauliOpType codes[NUM_QUBITS];
518  for (int i=0; i<NUM_QUBITS; i++)
519  codes[i] = PAULI_I;
520 
521  REQUIRE_THROWS_WITH( calcExpecPauliSum(vec, codes, coeffs, numSumTerms, mat), Contains("Registers must both be state-vectors or both be density matrices") );
522  REQUIRE_THROWS_WITH( calcExpecPauliSum(mat, codes, coeffs, numSumTerms, vec), Contains("Registers must both be state-vectors or both be density matrices") );
523  }
524  SECTION( "workspace dimensions" ) {
525 
526  // make valid params
527  int numSumTerms = 1;
528  qreal coeffs[1] = {0};
529  pauliOpType codes[NUM_QUBITS];
530  for (int i=0; i<NUM_QUBITS; i++)
531  codes[i] = PAULI_I;
532 
533  Qureg vec2 = createQureg(NUM_QUBITS + 1, QUEST_ENV);
534  REQUIRE_THROWS_WITH( calcExpecPauliSum(vec, codes, coeffs, numSumTerms, vec2), Contains("Dimensions") && Contains("don't match") );
535  destroyQureg(vec2, QUEST_ENV);
536 
538  REQUIRE_THROWS_WITH( calcExpecPauliSum(mat, codes, coeffs, numSumTerms, mat2), Contains("Dimensions") && Contains("don't match") );
539  destroyQureg(mat2, QUEST_ENV);
540  }
541  }
542  destroyQureg(vec, QUEST_ENV);
543  destroyQureg(mat, QUEST_ENV);
544  destroyQureg(vecWork, QUEST_ENV);
545  destroyQureg(matWork, QUEST_ENV);
546 }
547 
548 
549 
554 TEST_CASE( "calcFidelity", "[calculations]" ) {
555 
559 
560  SECTION( "correctness" ) {
561 
562  // repeat the below random tests 10 times
563  GENERATE( range(0,10) );
564 
565  SECTION( "state-vector" ) {
566 
567  /* calcFidelity computes |<vec|pure>|^2 */
568 
569  SECTION( "normalised" ) {
570 
571  // random L2 vectors
574  toQureg(vec, vecRef);
575  toQureg(pure, pureRef);
576 
577  // |<vec|vec>|^2 = |1|^2 = 1
578  REQUIRE( calcFidelity(vec,vec) == Approx(1) );
579 
580  // |<vec|pure>|^2 = |sum_j conj(vec_j) * pure_j|^2
581  qcomp dotProd = 0;
582  for (size_t i=0; i<vecRef.size(); i++)
583  dotProd += conj(vecRef[i]) * pureRef[i];
584  qreal refFid = pow(abs(dotProd), 2);
585 
586  REQUIRE( calcFidelity(vec,pure) == Approx(refFid) );
587  }
588  SECTION( "unnormalised" ) {
589 
590  // random unnormalised vectors
591  QVector vecRef = getRandomQVector(1<<NUM_QUBITS);
592  QVector pureRef = getRandomQVector(1<<NUM_QUBITS);
593  toQureg(vec, vecRef);
594  toQureg(pure, pureRef);
595 
596  // Let nv be magnitude of vec, hence |unit-vec> = 1/sqrt(nv)|vec>
597  qreal nv = 0;
598  for (size_t i=0; i<vecRef.size(); i++)
599  nv += pow(abs(vecRef[i]), 2);
600  // then <vec|vec> = sqrt(nv)*sqrt(nv) <unit-vec|unit-vec> = nv,
601  // hence |<vec|vec>|^2 = nv*nv
602  REQUIRE( calcFidelity(vec,vec) == Approx( nv*nv ) );
603 
604  qcomp dotProd = 0;
605  for (size_t i=0; i<vecRef.size(); i++)
606  dotProd += conj(vecRef[i]) * pureRef[i];
607  qreal refFid = pow(abs(dotProd), 2);
608 
609  REQUIRE( calcFidelity(vec,pure) == Approx(refFid) );
610  }
611  }
612  SECTION( "density-matrix" ) {
613 
614  /* calcFidelity computes <pure|mat|pure> */
615 
616  SECTION( "pure" ) {
617 
619  toQureg(pure, pureRef);
620 
621  // test when density matrix is the same pure state
622  QMatrix matRef = getKetBra(pureRef, pureRef);
623  toQureg(mat, matRef);
624  REQUIRE( calcFidelity(mat,pure) == Approx(1) );
625 
626  // test when density matrix is a random pure state
628  matRef = getKetBra(r1, r1); // actually pure |r1><r1|
629  toQureg(mat, matRef);
630 
631  // <pure|r1><r1|pure> = |<r1|pure>|^2 = |sum_j conj(r1_j) * pure_j|^2
632  qcomp dotProd = 0;
633  for (size_t i=0; i<r1.size(); i++)
634  dotProd += conj(r1[i]) * pureRef[i];
635  qreal refFid = pow(abs(dotProd), 2);
636 
637  REQUIRE( calcFidelity(mat,pure) == Approx(refFid) );
638  }
639  SECTION( "mixed" ) {
640 
642  toQureg(pure, pureRef);
643 
644  // test when density matrix is mixed
646  toQureg(mat, matRef);
647 
648  // <pure|mat|pure> = <pure| (Mat|pure>)
649  QVector rhs = matRef * pureRef;
650  qcomp dotProd = 0;
651  for (size_t i=0; i<rhs.size(); i++)
652  dotProd += conj(pureRef[i]) * rhs[i];
653 
654  REQUIRE( imag(dotProd) == Approx(0).margin(REAL_EPS) );
655  REQUIRE( calcFidelity(mat,pure) == Approx(real(dotProd)) );
656  }
657  SECTION( "unnormalised" ) {
658 
659  // test when both density matrix and pure state are unnormalised
660  QVector pureRef = getRandomQVector(1<<NUM_QUBITS);
661  QMatrix matRef = getRandomQMatrix(1<<NUM_QUBITS);
662  toQureg(pure, pureRef);
663  toQureg(mat, matRef);
664 
665  // real[ <pure|mat|pure> ] = real[ <pure| (Mat|pure>) ]
666  QVector rhs = matRef * pureRef;
667  qcomp dotProd = 0;
668  for (size_t i=0; i<rhs.size(); i++)
669  dotProd += conj(pureRef[i]) * rhs[i];
670 
671  REQUIRE( calcFidelity(mat,pure) == Approx(real(dotProd)) );
672  }
673  }
674  }
675  SECTION( "input validation" ) {
676 
677  SECTION( "dimensions" ) {
678 
679  // two state-vectors
681  REQUIRE_THROWS_WITH( calcFidelity(vec2,vec), Contains("Dimensions") && Contains("don't match") );
682  destroyQureg(vec2, QUEST_ENV);
683 
684  // density-matrix and state-vector
686  REQUIRE_THROWS_WITH( calcFidelity(mat2,vec), Contains("Dimensions") && Contains("don't match") );
687  destroyQureg(mat2, QUEST_ENV);
688  }
689  SECTION( "density-matrices" ) {
690 
691  // two mixed statess
692  REQUIRE_THROWS_WITH( calcFidelity(mat,mat), Contains("Second argument must be a state-vector") );
693  }
694  }
695  destroyQureg(vec, QUEST_ENV);
696  destroyQureg(mat, QUEST_ENV);
697  destroyQureg(pure, QUEST_ENV);
698 }
699 
700 
701 
706 TEST_CASE( "calcHilbertSchmidtDistance", "[calculations]" ) {
707 
710 
711  SECTION( "correctness" ) {
712 
713  // perform these random tests 10 times
714  GENERATE( range(0,10) );
715 
716  SECTION( "density-matrix" ) {
717 
718  SECTION( "pure" ) {
719 
720  // create random |r1><r1| and |r2><r2| states
722  QMatrix m1 = getKetBra(r1,r1);
723  toQureg(mat1, m1);
725  QMatrix m2 = getKetBra(r2,r2);
726  toQureg(mat2, m2);
727 
728  // Tr{ (a-b)(a-b)^dagger } = sum_{ij} |a_{ij} - b_{ij}|^2
729  qreal tr = 0;
730  for (size_t i=0; i<m1.size(); i++)
731  for (size_t j=0; j<m1.size(); j++)
732  tr += pow(abs(m1[i][j] - m2[i][j]), 2);
733 
734  qreal res = calcHilbertSchmidtDistance(mat1, mat2);
735  REQUIRE( res == Approx(sqrt(tr)) );
736 
737  }
738  SECTION( "normalised" ) {
739 
742  toQureg(mat1, ref1);
743  toQureg(mat2, ref2);
744 
745  // Tr{ (a-b)(a-b)^dagger } = sum_{ij} |a_{ij} - b_{ij}|^2
746  qreal tr = 0;
747  for (size_t i=0; i<ref1.size(); i++)
748  for (size_t j=0; j<ref1.size(); j++)
749  tr += pow(abs(ref1[i][j] - ref2[i][j]), 2);
750 
751  qreal res = calcHilbertSchmidtDistance(mat1, mat2);
752  REQUIRE( res == Approx(sqrt(tr)) );
753  }
754  SECTION( "unnormalised" ) {
755 
756  // mat1 and mat2 are both random matrices
759  toQureg(mat1, ref1);
760  toQureg(mat2, ref2);
761 
762  // Tr{ (a-b)(a-b)^dagger } = sum_{ij} |a_{ij} - b_{ij}|^2
763  qreal tr = 0;
764  for (size_t i=0; i<ref1.size(); i++)
765  for (size_t j=0; j<ref1.size(); j++)
766  tr += pow(abs(ref1[i][j] - ref2[i][j]), 2);
767 
768  qreal res = calcHilbertSchmidtDistance(mat1, mat2);
769  REQUIRE( res == Approx(sqrt(tr)) );
770  }
771  }
772  }
773  SECTION( "input validation") {
774 
775  SECTION( "dimensions" ) {
776 
778  REQUIRE_THROWS_WITH( calcHilbertSchmidtDistance(mat1,mat3), Contains("Dimensions") && Contains("don't match") );
779  destroyQureg(mat3, QUEST_ENV);
780  }
781  SECTION( "state-vector" ) {
782 
784 
785  REQUIRE_THROWS_WITH( calcHilbertSchmidtDistance(vec,mat1), Contains("valid only for density matrices") );
786  REQUIRE_THROWS_WITH( calcHilbertSchmidtDistance(mat1,vec), Contains("valid only for density matrices") );
787  REQUIRE_THROWS_WITH( calcHilbertSchmidtDistance(vec,vec), Contains("valid only for density matrices") );
788 
789  destroyQureg(vec, QUEST_ENV);
790  }
791  }
792  destroyQureg(mat1, QUEST_ENV);
793  destroyQureg(mat2, QUEST_ENV);
794 }
795 
796 
797 
802 TEST_CASE( "calcInnerProduct", "[calculations]" ) {
803 
806 
807  SECTION( "correctness" ) {
808 
809  // perform these random tests 10 times
810  GENERATE( range(0,10) );
811 
812  SECTION( "state-vector" ) {
813 
814  SECTION( "normalised" ) {
815 
816  // <r1|r2> = sum_j conj(r1_j) * r2_j
819  qcomp prod = 0;
820  for (size_t i=0; i<r1.size(); i++)
821  prod += conj(r1[i]) * r2[i];
822 
823  toQureg(vec1, r1);
824  toQureg(vec2, r2);
825  Complex res = calcInnerProduct(vec1,vec2);
826 
827  REQUIRE( res.real == Approx(real(prod)) );
828  REQUIRE( res.imag == Approx(imag(prod)) );
829  }
830  SECTION( "unnormalised" ) {
831 
832  // <r1|r2> = sum_j conj(r1_j) * r2_j
835  qcomp prod = 0;
836  for (size_t i=0; i<r1.size(); i++)
837  prod += conj(r1[i]) * r2[i];
838 
839  toQureg(vec1, r1);
840  toQureg(vec2, r2);
841  Complex res = calcInnerProduct(vec1,vec2);
842 
843  REQUIRE( res.real == Approx(real(prod)) );
844  REQUIRE( res.imag == Approx(imag(prod)) );
845  }
846  }
847  }
848  SECTION( "input validation" ) {
849 
850  SECTION( "dimensions" ) {
851 
852  Qureg vec3 = createQureg(NUM_QUBITS + 1, QUEST_ENV);
853  REQUIRE_THROWS_WITH( calcInnerProduct(vec1,vec3), Contains("Dimensions") && Contains("don't match") );
854  destroyQureg(vec3, QUEST_ENV);
855  }
856  SECTION( "density-matrix" ) {
857 
859 
860  REQUIRE_THROWS_WITH( calcInnerProduct(vec1,mat), Contains("valid only for state-vectors") );
861  REQUIRE_THROWS_WITH( calcInnerProduct(mat,vec1), Contains("valid only for state-vectors") );
862  REQUIRE_THROWS_WITH( calcInnerProduct(mat,mat), Contains("valid only for state-vectors") );
863 
864  destroyQureg(mat, QUEST_ENV);
865  }
866  }
867  destroyQureg(vec1, QUEST_ENV);
868  destroyQureg(vec2, QUEST_ENV);
869 }
870 
871 
872 
877 TEST_CASE( "calcProbOfOutcome", "[calculations]" ) {
878 
881 
882  SECTION( "correctness" ) {
883 
884  int target = GENERATE( range(0,NUM_QUBITS) );
885  int outcome = GENERATE( 0, 1 );
886 
887  SECTION( "state-vector" ) {
888 
889  SECTION( "normalised" ) {
890 
892  toQureg(vec, ref);
893 
894  // prob is sum of |amp|^2 of amplitudes where target bit is outcome
895  qreal prob = 0;
896  for (size_t ind=0; ind<ref.size(); ind++) {
897  int bit = (ind >> target) & 1; // target-th bit
898  if (bit == outcome)
899  prob += pow(abs(ref[ind]), 2);
900  }
901 
902  REQUIRE( calcProbOfOutcome(vec, target, outcome) == Approx(prob) );
903  }
904  SECTION( "unnormalised" ) {
905 
907  toQureg(vec, ref);
908 
909  // prob is (sum of |amp|^2 of amplitudes where target bit is zero)
910  // or 1 - (this) if outcome == 1
911  qreal prob = 0;
912  for (size_t ind=0; ind<ref.size(); ind++) {
913  int bit = (ind >> target) & 1; // target-th bit
914  if (bit == 0)
915  prob += pow(abs(ref[ind]), 2);
916  }
917  if (outcome == 1)
918  prob = 1 - prob;
919 
920  REQUIRE( calcProbOfOutcome(vec, target, outcome) == Approx(prob) );
921  }
922  }
923  SECTION( "density-matrix" ) {
924 
925  SECTION( "pure" ) {
926 
927  // set mat to a random |r><r|
929  toQureg(mat, getKetBra(ref, ref));
930 
931  // calc prob of the state-vector
932  qreal prob = 0;
933  for (size_t ind=0; ind<ref.size(); ind++) {
934  int bit = (ind >> target) & 1; // target-th bit
935  if (bit == outcome)
936  prob += pow(abs(ref[ind]), 2);
937  }
938 
939  REQUIRE( calcProbOfOutcome(mat, target, outcome) == Approx(prob) );
940  }
941  SECTION( "mixed" ) {
942 
944  toQureg(mat, ref);
945 
946  // prob is sum of diagonal amps (should be real) where target bit is outcome
947  qcomp tr = 0;
948  for (size_t ind=0; ind<ref.size(); ind++) {
949  int bit = (ind >> target) & 1; // target-th bit
950  if (bit == outcome)
951  tr += ref[ind][ind];
952  }
953  REQUIRE( imag(tr) == Approx(0).margin(REAL_EPS) );
954 
955  REQUIRE( calcProbOfOutcome(mat, target, outcome) == Approx(real(tr)) );
956  }
957  SECTION( "unnormalised" ) {
958 
960  toQureg(mat, ref);
961 
962  // prob is (sum of real of diagonal amps where target bit is outcome)
963  // or 1 - (this) if outcome == 1
964  qreal tr = 0;
965  for (size_t ind=0; ind<ref.size(); ind++) {
966  int bit = (ind >> target) & 1; // target-th bit
967  if (bit == 0)
968  tr += real(ref[ind][ind]);
969  }
970  if (outcome == 1)
971  tr = 1 - tr;
972 
973  REQUIRE( calcProbOfOutcome(mat, target, outcome) == Approx(tr) );
974  }
975  }
976  }
977  SECTION( "validation" ) {
978 
979  SECTION( "qubit indices" ) {
980 
981  int target = GENERATE( -1, NUM_QUBITS );
982  REQUIRE_THROWS_WITH( calcProbOfOutcome(vec, target, 0), Contains("Invalid target qubit") );
983  }
984  SECTION( "outcome value" ) {
985 
986  int outcome = GENERATE( -1, 2 );
987  REQUIRE_THROWS_WITH( calcProbOfOutcome(vec, 0, outcome), Contains("Invalid measurement outcome") );
988  }
989  }
990  destroyQureg(vec, QUEST_ENV);
991  destroyQureg(mat, QUEST_ENV);
992 }
993 
994 
995 
1000 TEST_CASE( "calcPurity", "[calculations]" ) {
1001 
1003 
1004  SECTION( "correctness" ) {
1005 
1006  // perform the following random tests 10 times
1007  GENERATE( range(1,10) );
1008 
1009  SECTION( "density-matrix" ) {
1010 
1011  SECTION( "pure" ) {
1012 
1013  // pure states have unity purity
1014  initZeroState(mat);
1015  REQUIRE( calcPurity(mat) == 1 );
1016 
1017  // (try also a pure random L2-vector)
1019  QMatrix m1 = getKetBra(r1, r1); // |r><r|
1020  toQureg(mat, m1);
1021  REQUIRE( calcPurity(mat) == Approx(1) );
1022 
1023  }
1024  SECTION( "mixed" ) {
1025 
1026  // mixed states have 1/2^N < purity < 1
1028  toQureg(mat, ref);
1029  qreal purity = calcPurity(mat);
1030  REQUIRE( purity < 1 );
1031  REQUIRE( purity >= 1/pow(2.,NUM_QUBITS) );
1032 
1033  // compare to Tr(rho^2)
1034  QMatrix prod = ref*ref;
1035  qreal tr = 0;
1036  for (size_t i=0; i<prod.size(); i++)
1037  tr += real(prod[i][i]);
1038  REQUIRE( purity == Approx(tr) );
1039  }
1040  SECTION( "unnormalised" ) {
1041 
1042  // unphysical states give sum_{ij} |rho_ij|^2
1044  qreal tot = 0;
1045  for (size_t i=0; i<ref.size(); i++)
1046  for (size_t j=0; j<ref.size(); j++)
1047  tot += pow(abs(ref[i][j]), 2);
1048 
1049  toQureg(mat, ref);
1050  REQUIRE( calcPurity(mat) == Approx(tot) );
1051  }
1052  }
1053  }
1054  SECTION( "input validation" ) {
1055 
1056  SECTION( "state-vector" ) {
1057 
1059  REQUIRE_THROWS_WITH( calcPurity(vec), Contains("valid only for density matrices") );
1060  destroyQureg(vec, QUEST_ENV);
1061  }
1062  }
1063  destroyQureg(mat, QUEST_ENV);
1064 }
1065 
1066 
1067 
1072 TEST_CASE( "calcTotalProb", "[calculations]" ) {
1073 
1076 
1077  SECTION( "correctness" ) {
1078 
1079  SECTION( "state-vector" ) {
1080 
1081  // normalised: prob(vec) = 1
1082  initPlusState(vec);
1083  REQUIRE( calcTotalProb(vec) == Approx(1) );
1084 
1085  // zero norm: prob(vec) = 0
1086  initBlankState(vec);
1087  REQUIRE( calcTotalProb(vec) == 0 );
1088 
1089  // random L2 state: prob(vec) = 1
1091  REQUIRE( calcTotalProb(vec) == Approx(1) );
1092 
1093  // unnormalised: prob(vec) = sum_i |vec_i|^2
1094  initDebugState(vec);
1095  QVector ref = toQVector(vec);
1096  qreal refProb = 0;
1097  for (size_t i=0; i<ref.size(); i++)
1098  refProb += pow(abs(ref[i]), 2);
1099  REQUIRE( calcTotalProb(vec) == Approx(refProb) );
1100  }
1101  SECTION( "density-matrix" ) {
1102 
1103  // normalised: prob(mat) = 1
1104  initPlusState(mat);
1105  REQUIRE( calcTotalProb(mat) == Approx(1) );
1106 
1107  // zero norm: prob(mat) = 0
1108  initBlankState(mat);
1109  REQUIRE( calcTotalProb(mat) == 0 );
1110 
1111  // random density matrix: prob(mat) = 1
1113  REQUIRE( calcTotalProb(mat) == Approx(1) );
1114 
1115  // unnormalised: prob(mat) = sum_i real(mat_{ii})
1116  initDebugState(mat);
1117  QMatrix ref = toQMatrix(mat);
1118  qreal refProb = 0;
1119  for (size_t i=0; i<ref.size(); i++)
1120  refProb += real(ref[i][i]);
1121  REQUIRE( calcTotalProb(mat) == Approx(refProb) );
1122  }
1123  }
1124  SECTION( "input validation" ) {
1125 
1126  // no validation
1127  SUCCEED();
1128  }
1129  destroyQureg(vec, QUEST_ENV);
1130  destroyQureg(mat, QUEST_ENV);
1131 }
1132 
1133 
1134 
1139 TEST_CASE( "getAmp", "[calculations]" ) {
1140 
1142 
1143  SECTION( "correctness" ) {
1144 
1145  SECTION( "state-vector" ) {
1146 
1147  initDebugState(vec);
1148  QVector ref = toQVector(vec);
1149 
1150  int ind = GENERATE( range(0,1<<NUM_QUBITS) );
1151  Complex amp = getAmp(vec,ind);
1152  REQUIRE( fromComplex(amp) == ref[ind] );
1153  }
1154  }
1155  SECTION( "input validation" ) {
1156 
1157  SECTION( "state index" ) {
1158 
1159  int ind = GENERATE( -1, 1<<NUM_QUBITS );
1160  REQUIRE_THROWS_WITH( getAmp(vec,ind), Contains("Invalid amplitude index") );
1161  }
1162  SECTION( "density-matrix" ) {
1163 
1165  REQUIRE_THROWS_WITH( getAmp(mat,0), Contains("valid only for state-vectors") );
1166  destroyQureg(mat, QUEST_ENV);
1167  }
1168  }
1169  destroyQureg(vec, QUEST_ENV);
1170 }
1171 
1172 
1173 
1178 TEST_CASE( "getDensityAmp", "[calculations]" ) {
1179 
1181 
1182  SECTION( "correctness" ) {
1183 
1184  SECTION( "density-matrix" ) {
1185 
1186  initDebugState(mat);
1187  QMatrix ref = toQMatrix(mat);
1188 
1189  int row = GENERATE( range(0,1<<NUM_QUBITS) );
1190  int col = GENERATE( range(0,1<<NUM_QUBITS) );
1191 
1192  Complex amp = getDensityAmp(mat,row,col);
1193  REQUIRE( fromComplex(amp) == ref[row][col] );
1194  }
1195  }
1196  SECTION( "input validation" ) {
1197 
1198  SECTION( "state index" ) {
1199 
1200  int ind = GENERATE( -1, 1<<NUM_QUBITS );
1201  REQUIRE_THROWS_WITH( getDensityAmp(mat,ind,0), Contains("Invalid amplitude index") );
1202  REQUIRE_THROWS_WITH( getDensityAmp(mat,0,ind), Contains("Invalid amplitude index") );
1203 
1204  }
1205  SECTION( "state-vector" ) {
1206 
1208  REQUIRE_THROWS_WITH( getDensityAmp(vec,0,0), Contains("valid only for density matrices") );
1209  destroyQureg(vec, QUEST_ENV);
1210  }
1211  }
1212  destroyQureg(mat, QUEST_ENV);
1213 }
1214 
1215 
1216 
1221 TEST_CASE( "getImagAmp", "[calculations]" ) {
1222 
1224 
1225  SECTION( "correctness" ) {
1226 
1227  SECTION( "state-vector" ) {
1228 
1229  initDebugState(vec);
1230  QVector ref = toQVector(vec);
1231 
1232  int ind = GENERATE( range(0,1<<NUM_QUBITS) );
1233  REQUIRE( getImagAmp(vec,ind) == imag(ref[ind]) );
1234  }
1235  }
1236  SECTION( "input validation" ) {
1237 
1238  SECTION( "state index" ) {
1239 
1240  int ind = GENERATE( -1, 1<<NUM_QUBITS );
1241  REQUIRE_THROWS_WITH( getImagAmp(vec,ind), Contains("Invalid amplitude index") );
1242  }
1243  SECTION( "density-matrix" ) {
1244 
1246  REQUIRE_THROWS_WITH( getImagAmp(mat,0), Contains("valid only for state-vectors") );
1247  destroyQureg(mat, QUEST_ENV);
1248  }
1249  }
1250  destroyQureg(vec, QUEST_ENV);
1251 }
1252 
1253 
1254 
1259 TEST_CASE( "getNumAmps", "[calculations]" ) {
1260 
1261  SECTION( "correctness" ) {
1262 
1263  // test >= NUM_QUBITS so as not to limit distribution size
1264  int numQb = GENERATE( range(NUM_QUBITS, NUM_QUBITS+10) );
1265 
1266  SECTION( "state-vector" ) {
1267 
1268  Qureg vec = createQureg(numQb, QUEST_ENV);
1269  REQUIRE( getNumAmps(vec) == (1<<numQb) );
1270  destroyQureg(vec, QUEST_ENV);
1271  }
1272  }
1273  SECTION( "input validation" ) {
1274 
1275  SECTION( "density-matrix" ) {
1277  REQUIRE_THROWS_WITH( getNumAmps(mat), Contains("valid only for state-vectors") );
1278  destroyQureg(mat, QUEST_ENV);
1279  }
1280  }
1281 }
1282 
1283 
1284 
1289 TEST_CASE( "getNumQubits", "[calculations]" ) {
1290 
1291  SECTION( "correctness" ) {
1292 
1293  // test >= NUM_QUBITS so as not to limit distribution size
1294  int numQb = GENERATE( range(NUM_QUBITS, NUM_QUBITS+10) );
1295 
1296  SECTION( "state-vector" ) {
1297 
1298  Qureg vec = createQureg(numQb, QUEST_ENV);
1299  REQUIRE( getNumQubits(vec) == numQb );
1300  destroyQureg(vec, QUEST_ENV);
1301  }
1302  SECTION( "density-matrix" ) {
1303 
1304  Qureg mat = createDensityQureg(numQb, QUEST_ENV);
1305  REQUIRE( getNumQubits(mat) == numQb );
1306  destroyQureg(mat, QUEST_ENV);
1307  }
1308  }
1309  SECTION( "input validation" ) {
1310 
1311  // no validation
1312  SUCCEED();
1313  }
1314 }
1315 
1316 
1317 
1322 TEST_CASE( "getProbAmp", "[calculations]" ) {
1323 
1325 
1326  SECTION( "correctness" ) {
1327 
1328  SECTION( "state-vector" ) {
1329 
1330  initDebugState(vec);
1331  QVector ref = toQVector(vec);
1332 
1333  int ind = GENERATE( range(0,1<<NUM_QUBITS) );
1334  qreal refCalc = pow(abs(ref[ind]), 2);
1335  REQUIRE( getProbAmp(vec,ind) == Approx(refCalc) );
1336  }
1337  }
1338  SECTION( "input validation" ) {
1339 
1340  SECTION( "state index" ) {
1341 
1342  int ind = GENERATE( -1, 1<<NUM_QUBITS );
1343  REQUIRE_THROWS_WITH( getProbAmp(vec,ind), Contains("Invalid amplitude index") );
1344  }
1345  SECTION( "density-matrix" ) {
1346 
1348  REQUIRE_THROWS_WITH( getProbAmp(mat,0), Contains("valid only for state-vectors") );
1349  destroyQureg(mat, QUEST_ENV);
1350  }
1351  }
1352  destroyQureg(vec, QUEST_ENV);
1353 }
1354 
1355 
1356 
1361 TEST_CASE( "getRealAmp", "[calculations]" ) {
1362 
1364 
1365  SECTION( "correctness" ) {
1366 
1367  SECTION( "state-vector" ) {
1368 
1369  initDebugState(vec);
1370  QVector ref = toQVector(vec);
1371 
1372  int ind = GENERATE( range(0,1<<NUM_QUBITS) );
1373  REQUIRE( getRealAmp(vec,ind) == real(ref[ind]) );
1374  }
1375  }
1376  SECTION( "input validation" ) {
1377 
1378  SECTION( "state index" ) {
1379 
1380  int ind = GENERATE( -1, 1<<NUM_QUBITS );
1381  REQUIRE_THROWS_WITH( getRealAmp(vec,ind), Contains("Invalid amplitude index") );
1382  }
1383  SECTION( "density-matrix" ) {
1384 
1386  REQUIRE_THROWS_WITH( getRealAmp(mat,0), Contains("valid only for state-vectors") );
1387  destroyQureg(mat, QUEST_ENV);
1388  }
1389  }
1390  destroyQureg(vec, QUEST_ENV);
1391 }
1392 
1393 
qreal getProbAmp(Qureg qureg, long long int index)
Get the probability of a state-vector at an index in the full state vector.
Definition: QuEST.c:692
void initBlankState(Qureg qureg)
Initialises a qureg to have all-zero-amplitudes.
Definition: QuEST.c:119
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
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
#define fromComplex(comp)
@ PAULI_Z
Definition: QuEST.h:96
qreal calcTotalProb(Qureg qureg)
A debugging function which calculates the probability of the qubits in qureg being in any state,...
Definition: QuEST.c:903
@ PAULI_I
Definition: QuEST.h:96
Complex getDensityAmp(Qureg qureg, long long int row, long long int col)
Get an amplitude from a density matrix at a given row and column.
Definition: QuEST.c:709
int getRandomInt(int min, int max)
Returns a random integer between min (inclusive) and max (exclusive), from the uniform distribution.
Definition: utilities.cpp:481
qreal getImagAmp(Qureg qureg, long long int index)
Get the imaginary component of the complex probability amplitude at an index in the state vector.
Definition: QuEST.c:685
void syncDiagonalOp(DiagonalOp op)
Copy the elements in DiagonalOp op.real and op.imag to the persisent GPU memory.
Definition: QuEST.c:1280
void destroyDiagonalOp(DiagonalOp op, QuESTEnv env)
Destroys a DiagonalOp created with createDiagonalOp(), freeing its memory.
Definition: QuEST.c:1273
#define NUM_QUBITS
The default number of qubits in the registers created for unit testing (both statevectors and density...
Definition: utilities.hpp:36
qreal calcPurity(Qureg qureg)
Calculates the purity of a density matrix, by the trace of the density matrix squared.
Definition: QuEST.c:936
qreal calcProbOfOutcome(Qureg qureg, int measureQubit, int outcome)
Gives the probability of a specified qubit being measured in the given outcome (0 or 1).
Definition: QuEST.c:926
qreal calcFidelity(Qureg qureg, Qureg pureState)
Calculates the fidelity of qureg (a statevector or density matrix) against a reference pure state (ne...
Definition: QuEST.c:942
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
Complex calcExpecDiagonalOp(Qureg qureg, DiagonalOp op)
Computes the expected value of the diagonal operator op for state qureg.
Definition: QuEST.c:979
#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
@ PAULI_X
Definition: QuEST.h:96
Complex getAmp(Qureg qureg, long long int index)
Get the complex amplitude at a given index in the state vector.
Definition: QuEST.c:699
qreal calcExpecPauliHamil(Qureg qureg, PauliHamil hamil, Qureg workspace)
Computes the expected value of qureg under Hermitian operator hamil.
Definition: QuEST.c:970
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
qreal calcHilbertSchmidtDistance(Qureg a, Qureg b)
Computes the Hilbert Schmidt distance between two density matrices a and b, defined as the Frobenius ...
Definition: QuEST.c:988
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
QVector toQVector(Qureg qureg)
Returns an equal-size copy of the given state-vector qureg.
Definition: utilities.cpp:938
#define qcomp
TEST_CASE("calcDensityInnerProduct", "[calculations]")
enum pauliOpType * pauliCodes
The Pauli operators acting on each qubit, flattened over every operator.
Definition: QuEST.h:162
qreal getRealAmp(Qureg qureg, long long int index)
Get the real component of the complex probability amplitude at an index in the state vector.
Definition: QuEST.c:678
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
int getNumQubits(Qureg qureg)
Get the number of qubits in a qureg object.
Definition: QuEST.c:668
Complex calcInnerProduct(Qureg bra, Qureg ket)
Computes the inner product of two equal-size state vectors, given by.
Definition: QuEST.c:910
void destroyQureg(Qureg qureg, QuESTEnv env)
Deallocate a Qureg object representing a set of qubits.
Definition: QuEST.c:77
QVector getRandomStateVector(int numQb)
Returns a random numQb-length L2-normalised state-vector from an undisclosed distribution.
Definition: utilities.cpp:453
void initDebugState(Qureg qureg)
Initialises qureg to be in the un-normalised, non-physical state with with n-th complex amplitude (2n...
Definition: QuEST.c:1308
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
long long int getNumAmps(Qureg qureg)
Get the number of probability amplitudes in a qureg object, given by 2^numQubits.
Definition: QuEST.c:672
long long int numElemsPerChunk
The number of the 2^numQubits amplitudes stored on each distributed node.
Definition: QuEST.h:183
std::vector< std::vector< qcomp > > QMatrix
A complex square matrix.
Definition: utilities.hpp:49
qreal calcExpecPauliSum(Qureg qureg, enum pauliOpType *allPauliCodes, qreal *termCoeffs, int numSumTerms, Qureg workspace)
Computes the expected value of a sum of products of Pauli operators.
Definition: QuEST.c:961
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
int numQubitsRepresented
The number of qubits represented in either the state-vector or density matrix.
Definition: QuEST.h:208
qreal * real
The real values of the 2^numQubits complex elements.
Definition: QuEST.h:189
qreal real
Definition: QuEST.h:105
Qureg createQureg(int numQubits, QuESTEnv env)
Create a Qureg object representing a set of qubits which will remain in a pure state.
Definition: QuEST.c:36
void destroyPauliHamil(PauliHamil h)
Destroy a PauliHamil instance, created with either createPauliHamil() or createPauliHamilFromFile().
Definition: QuEST.c:1163
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
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 initZeroState(Qureg qureg)
Initialise a set of qubits to the classical zero state .
Definition: QuEST.c:113
qreal calcDensityInnerProduct(Qureg rho1, Qureg rho2)
Computes the Hilbert-Schmidt scalar product (which is equivalent to the Frobenius inner product of ma...
Definition: QuEST.c:918
Qureg createDensityQureg(int numQubits, QuESTEnv env)
Create a Qureg for qubits which are represented by a density matrix, and can be in mixed states.
Definition: QuEST.c:50
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
PauliHamil createPauliHamil(int numQubits, int numSumTerms)
Create a PauliHamil instance, which is a Hamiltonian expressed as a real-weighted sum of products of ...
Definition: QuEST.c:1147
void initPlusState(Qureg qureg)
Initialise a set of qubits to the plus state (and similarly for density matrices).
Definition: QuEST.c:125
qreal calcExpecPauliProd(Qureg qureg, int *targetQubits, enum pauliOpType *pauliCodes, int numTargets, Qureg workspace)
Computes the expected value of a product of Pauli operators.
Definition: QuEST.c:952
DiagonalOp createDiagonalOp(int numQubits, QuESTEnv env)
Creates a DiagonalOp representing a diagonal operator on the full Hilbert space of a Qureg.
Definition: QuEST.c:1267