test_data_structures.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( "fromComplex", "[data_structures]" ) {
16 
17  Complex a = {.real=.5, .imag=-.2};
18  qcomp b = fromComplex(a);
19 
20  REQUIRE( a.real == real(b) );
21  REQUIRE( a.imag == imag(b) );
22 }
23 
24 
25 
30 TEST_CASE( "getStaticComplexMatrixN", "[data_structures]" ) {
31 
32  /* use of this function is illegal in C++ */
33  SUCCEED( );
34 }
35 
36 
37 
42 TEST_CASE( "toComplex", "[data_structures]" ) {
43 
44  qcomp a = .5 - .2i;
45  Complex b = toComplex(a);
46 
47  REQUIRE( real(a) == b.real );
48  REQUIRE( imag(a) == b.imag );
49 }
50 
51 
52 
57 TEST_CASE( "createCloneQureg", "[data_structures]" ) {
58 
59  SECTION( "state-vector" ) {
60 
63 
64  // check properties are the same
65  REQUIRE( b.isDensityMatrix == a.isDensityMatrix );
67  REQUIRE( b.numQubitsInStateVec == a.numQubitsInStateVec );
68  REQUIRE( b.numAmpsPerChunk == a.numAmpsPerChunk );
69  REQUIRE( b.numAmpsTotal == a.numAmpsTotal );
70 
71  // check state-vector is the same (works for GPU and distributed)
72  REQUIRE( areEqual(a, b) );
73 
76  }
77  SECTION( "density-matrix" ) {
78 
81 
82  // check properties are the same
83  REQUIRE( b.isDensityMatrix == a.isDensityMatrix );
85  REQUIRE( b.numQubitsInStateVec == a.numQubitsInStateVec );
86  REQUIRE( b.numAmpsPerChunk == a.numAmpsPerChunk );
87  REQUIRE( b.numAmpsTotal == a.numAmpsTotal );
88 
89  // check state-vector is the same (works for GPU and distributed)
90  REQUIRE( areEqual(a, b) );
91 
94  }
95 }
96 
97 
98 
103 TEST_CASE( "createComplexMatrixN", "[data_structures]" ) {
104 
105  SECTION( "correctness" ) {
106 
107  int numQb = GENERATE( range(1,10+1) );
109 
110  // ensure elems are created and initialised to 0
111  REQUIRE( areEqual(toQMatrix(m), getZeroMatrix(1<<numQb)) );
112 
114  }
115  SECTION( "input validation" ) {
116 
117  SECTION( "number of qubits" ) {
118 
119  int numQb = GENERATE( -1, 0 );
120  REQUIRE_THROWS_WITH( createComplexMatrixN(numQb), Contains("Invalid number of qubits") );
121  }
122  }
123 }
124 
125 
126 
131 TEST_CASE( "createDensityQureg", "[data_structures]" ) {
132 
133  // must be at least one amplitude per node
134  int minNumQb = calcLog2(QUEST_ENV.numRanks) - 1; // density matrix has 2*numQb in state-vec
135  if (minNumQb <= 0)
136  minNumQb = 1;
137 
138  SECTION( "correctness" ) {
139 
140  // try 10 valid number of qubits
141  int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
142  Qureg reg = createDensityQureg(numQb, QUEST_ENV);
143 
144  // ensure elems (CPU and/or GPU) are created, and reg begins in |0><0|
145  QMatrix ref = getZeroMatrix(1<<numQb);
146  ref[0][0] = 1; // |0><0|
147  REQUIRE( areEqual(reg, ref) );
148 
149  destroyQureg(reg, QUEST_ENV);
150  }
151  SECTION( "input validation") {
152 
153  SECTION( "number of qubits" ) {
154 
155  int numQb = GENERATE( -1, 0 );
156  REQUIRE_THROWS_WITH( createDensityQureg(numQb, QUEST_ENV), Contains("Invalid number of qubits") );
157  }
158  SECTION( "number of amplitudes" ) {
159 
160  // use local QuESTEnv to safely modify
161  QuESTEnv env = QUEST_ENV;
162 
163  // too many amplitudes to store in type
164  int maxQb = (int) calcLog2(SIZE_MAX) / 2;
165  REQUIRE_THROWS_WITH( createDensityQureg(maxQb+1, env), Contains("Too many qubits") && Contains("size_t type") );
166 
167  /* n-qubit density matrix contains 2^(2n) amplitudes
168  * so can be spread between at most 2^(2n) ranks
169  */
170  int minQb = GENERATE_COPY( range(3,maxQb) );
171  env.numRanks = (int) pow(2, 2*minQb);
172  int numQb = GENERATE_COPY( range(1,minQb) );
173  REQUIRE_THROWS_WITH( createDensityQureg(numQb, env), Contains("Too few qubits") );
174  }
175  SECTION( "available memory" ) {
176 
177  /* there is no reliable way to force the malloc statements to
178  * fail, and hence trigger the matrixInit validation */
179  SUCCEED( );
180  }
181  }
182 }
183 
184 
185 
190 TEST_CASE( "createDiagonalOp", "[data_structures]" ) {
191 
192  // must be at least one amplitude per node
193  int minNumQb = calcLog2(QUEST_ENV.numRanks);
194  if (minNumQb == 0)
195  minNumQb = 1;
196 
197  SECTION( "correctness" ) {
198 
199  // try 10 valid number of qubits
200  int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
202 
203  // check properties are correct
204  REQUIRE( op.numQubits == numQb );
205  REQUIRE( op.chunkId == QUEST_ENV.rank );
206  REQUIRE( op.numChunks == QUEST_ENV.numRanks );
207  REQUIRE( op.numElemsPerChunk == (1LL << numQb) / QUEST_ENV.numRanks );
208  REQUIRE( op.real != NULL );
209  REQUIRE( op.imag != NULL );
210 
211  // check all elements in CPU are zero
212  REQUIRE( areEqual(toQVector(op), QVector(1LL << numQb)) );
213 
214  // (no concise way to check this for GPU)
215 
217  }
218  SECTION( "input validation" ) {
219 
220  SECTION( "number of qubits" ) {
221 
222  int numQb = GENERATE( -1, 0 );
223  REQUIRE_THROWS_WITH( createDiagonalOp(numQb, QUEST_ENV), Contains("Invalid number of qubits") );
224  }
225  SECTION( "number of amplitudes" ) {
226 
227  // use local QuESTEnv to safely modify
228  QuESTEnv env = QUEST_ENV;
229 
230  // too many amplitudes to store in type
231  int maxQb = (int) calcLog2(SIZE_MAX);
232  REQUIRE_THROWS_WITH( createDiagonalOp(maxQb+1, env), Contains("Too many qubits") && Contains("size_t type") );
233 
234  // too few amplitudes to distribute
235  int minQb = GENERATE_COPY( range(2,maxQb) );
236  env.numRanks = (int) pow(2, minQb);
237  int numQb = GENERATE_COPY( range(1,minQb) );
238  REQUIRE_THROWS_WITH( createDiagonalOp(numQb, env), Contains("Too few qubits") );
239  }
240  SECTION( "available memory" ) {
241 
242  /* there is no reliable way to force the malloc statements to
243  * fail, and hence trigger the diagonalOpInit validation */
244  SUCCEED( );
245  }
246  }
247 }
248 
249 
250 
255 TEST_CASE( "createPauliHamil", "[data_structures]" ) {
256 
257  SECTION( "correctness" ) {
258 
259  int numQb = GENERATE( range(1,5) );
260  int numTerms = GENERATE( range(1,5) );
261  PauliHamil hamil = createPauliHamil(numQb, numTerms);
262 
263  // check fields are correct
264  REQUIRE( hamil.numQubits == numQb );
265  REQUIRE( hamil.numSumTerms == numTerms );
266 
267  // check all Pauli codes are identity
268  int numPaulis = numQb * numTerms;
269  for (int i=0; i<numPaulis; i++) {
270  REQUIRE( hamil.pauliCodes[i] == PAULI_I );
271  }
272 
273  // check all term coefficients can be written to (no seg fault)
274  for (int j=0; j<numTerms; j++) {
275  hamil.termCoeffs[j] = 1;
276  REQUIRE( hamil.termCoeffs[j] == 1 );
277  }
278 
279  destroyPauliHamil(hamil);
280  }
281  SECTION( "input validation") {
282 
283  SECTION( "number of qubits" ) {
284 
285  int numQb = GENERATE( -1, 0 );
286  REQUIRE_THROWS_WITH( createPauliHamil(numQb, 1), Contains("The number of qubits and terms in the PauliHamil must be strictly positive.") );
287  }
288  SECTION( "number of terms" ) {
289 
290  int numTerms = GENERATE( -1, 0 );
291  REQUIRE_THROWS_WITH( createPauliHamil(1, numTerms), Contains("The number of qubits and terms in the PauliHamil must be strictly positive.") );
292  }
293  }
294 }
295 
296 
297 
302 TEST_CASE( "createPauliHamilFromFile", "[data_structures]" ) {
303 
304  // a file created & populated during the test, and deleted afterward
305  char fn[] = "temp_test_output_file.txt";
306 
307  SECTION( "correctness" ) {
308 
309  SECTION( "general" ) {
310 
311  // for several sizes...
312  int numQb = GENERATE( range(1,6) );
313  int numTerms = GENERATE( range(1,6) );
314  int numPaulis = numQb*numTerms;
315 
316  // create a PauliHamil with random elements
317  qreal coeffs[numTerms];
318  enum pauliOpType paulis[numPaulis];
319  setRandomPauliSum(coeffs, paulis, numQb, numTerms);
320 
321  // write the Hamiltonian to file (with trailing whitespace, and trailing newline)
322  FILE* file = fopen(fn, "w");
323  int i=0;
324  for (int n=0; n<numTerms; n++) {
325  fprintf(file, "%.20f ", coeffs[n]);
326  for (int q=0; q<numQb; q++)
327  fprintf(file, "%d ", (int) paulis[i++]);
328  fprintf(file, "\n");
329  }
330  fprintf(file, "\n");
331  fclose(file);
332 
333  // load the file as a PauliHamil
335 
336  // check fields agree
337  REQUIRE( hamil.numQubits == numQb );
338  REQUIRE( hamil.numSumTerms == numTerms );
339 
340  // check elements agree
341  i=0;
342  for (int n=0; n<numTerms; n++) {
343  REQUIRE( hamil.termCoeffs[n] == coeffs[n] );
344  for (int q=0; q<numQb; q++) {
345  REQUIRE( hamil.pauliCodes[i] == paulis[i] );
346  i++;
347  }
348  }
349 
350  destroyPauliHamil(hamil);
351  }
352  SECTION( "edge cases" ) {
353 
354  SECTION( "no trailing newline or space" ) {
355 
356  FILE* file = fopen(fn, "w");
357  fprintf(file, ".1 1 0 1");
358  fclose(file);
360 
361  REQUIRE( hamil.numSumTerms == 1 );
362  REQUIRE( hamil.numQubits == 3 );
363  destroyPauliHamil(hamil);
364  }
365  SECTION( "trailing newlines" ) {
366 
367  FILE* file = fopen(fn, "w");
368  fprintf(file, ".1 1 0 1\n\n\n");
369  fclose(file);
371 
372  REQUIRE( hamil.numSumTerms == 1 );
373  REQUIRE( hamil.numQubits == 3 );
374  destroyPauliHamil(hamil);
375  }
376  SECTION( "trailing spaces" ) {
377 
378  FILE* file = fopen(fn, "w");
379  fprintf(file, ".1 1 0 1 ");
380  fclose(file);
382 
383  REQUIRE( hamil.numSumTerms == 1 );
384  REQUIRE( hamil.numQubits == 3 );
385  destroyPauliHamil(hamil);
386  }
387  }
388  }
389  SECTION( "input validation") {
390 
391  SECTION( "number of qubits" ) {
392 
393  FILE* file = fopen(fn, "w");
394  fprintf(file, ".1 ");
395  fclose(file);
396  REQUIRE_THROWS_WITH( createPauliHamilFromFile(fn), Contains("The number of qubits") && Contains("strictly positive"));
397  }
398  SECTION( "coefficient type" ) {
399 
400  FILE* file = fopen(fn, "w");
401  fprintf(file, "notanumber 1 2 3");
402  fclose(file);
403  REQUIRE_THROWS_WITH( createPauliHamilFromFile(fn), Contains("Failed to parse") && Contains("coefficient"));
404  }
405  SECTION( "pauli code" ) {
406 
407  // invalid int
408  FILE* file = fopen(fn, "w");
409  fprintf(file, ".1 1 2 4");
410  fclose(file);
411  REQUIRE_THROWS_WITH( createPauliHamilFromFile(fn), Contains("invalid pauli code"));
412 
413  // invalid type
414  file = fopen(fn, "w");
415  fprintf(file, ".1 1 2 notanumber");
416  fclose(file);
417  REQUIRE_THROWS_WITH( createPauliHamilFromFile(fn), Contains("Failed to parse the next expected Pauli code"));
418  }
419  }
420 
421  // delete the test file
422  remove(fn);
423 }
424 
425 
426 
431 TEST_CASE( "createQuESTEnv", "[data_structures]" ) {
432 
433  /* there is no meaningful way to test this */
434  SUCCEED( );
435 }
436 
437 
438 
443 TEST_CASE( "createQureg", "[data_structures]" ) {
444 
445  // must be at least one amplitude per node
446  int minNumQb = calcLog2(QUEST_ENV.numRanks);
447  if (minNumQb == 0)
448  minNumQb = 1;
449 
450  SECTION( "correctness" ) {
451 
452  // try 10 valid number of qubits
453  int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
454  Qureg reg = createQureg(numQb, QUEST_ENV);
455 
456  // ensure elems (CPU and/or GPU) are created, and reg begins in |0>
457  QVector ref = QVector(1<<numQb);
458  ref[0] = 1; // |0>
459  REQUIRE( areEqual(reg, ref) );
460 
461  destroyQureg(reg, QUEST_ENV);
462  }
463  SECTION( "input validation") {
464 
465  SECTION( "number of qubits" ) {
466 
467  int numQb = GENERATE( -1, 0 );
468  REQUIRE_THROWS_WITH( createQureg(numQb, QUEST_ENV), Contains("Invalid number of qubits") );
469  }
470  SECTION( "number of amplitudes" ) {
471 
472  // use local QuESTEnv to safely modify
473  QuESTEnv env = QUEST_ENV;
474 
475  // too many amplitudes to store in type
476  int maxQb = (int) calcLog2(SIZE_MAX);
477  REQUIRE_THROWS_WITH( createQureg(maxQb+1, env), Contains("Too many qubits") && Contains("size_t type") );
478 
479  // too few amplitudes to distribute
480  int minQb = GENERATE_COPY( range(2,maxQb) );
481  env.numRanks = (int) pow(2, minQb);
482  int numQb = GENERATE_COPY( range(1,minQb) );
483  REQUIRE_THROWS_WITH( createQureg(numQb, env), Contains("Too few qubits") );
484  }
485  SECTION( "available memory" ) {
486 
487  /* there is no reliable way to force the malloc statements to
488  * fail, and hence trigger the matrixInit validation */
489  SUCCEED( );
490  }
491  }
492 }
493 
494 
495 
500 TEST_CASE( "destroyComplexMatrixN", "[data_structures]" ) {
501 
502  SECTION( "correctness" ) {
503 
504  /* there is no meaningful way to test this */
505  SUCCEED( );
506  }
507  SECTION( "input validation" ) {
508 
509  SECTION( "matrix not created" ) {
510 
511  /* this is an artificial test case since nothing in the QuEST API
512  * automatically sets un-initialised ComplexMatrixN fields to
513  * the NULL pointer.
514  */
515  ComplexMatrixN m;
516  m.real = NULL;
517 
518  /* the error message is also somewhat unrelated, but oh well
519  */
520  REQUIRE_THROWS_WITH( destroyComplexMatrixN(m), Contains("The ComplexMatrixN was not successfully created") );
521  }
522  }
523 }
524 
525 
526 
531 TEST_CASE( "destroyDiagonalOp", "[data_structures]" ) {
532 
533  /* there is no meaningful way to test this */
534  SUCCEED( );
535 }
536 
537 
538 
543 TEST_CASE( "destroyPauliHamil", "[data_structures]" ) {
544 
545  /* there is no meaningful way to test this.
546  * We e.g. cannot check that the pointers are NULL because
547  * they are not updated; this function passes the struct by value,
548  * not by reference. We also cannot reliably monitor the
549  * memory used in the heap at runtime.
550  */
551  SUCCEED( );
552 }
553 
554 
555 
560 TEST_CASE( "destroyQuESTEnv", "[data_structures]" ) {
561 
562  /* there is no meaningful way to test this */
563  SUCCEED( );
564 }
565 
566 
567 
572 TEST_CASE( "destroyQureg", "[data_structures]" ) {
573 
574  /* there is no meaningful way to test this.
575  * We e.g. cannot check that the pointers are NULL because
576  * they are not updated; this function passes the struct by value,
577  * not by reference. We also cannot reliably monitor the
578  * memory used in the heap at runtime.
579  */
580  SUCCEED( );
581 }
582 
583 
584 
589 TEST_CASE( "initComplexMatrixN", "[data_structures]" ) {
590 
591  /* use of this function is illegal in C++ */
592  SUCCEED( );
593 }
594 
595 
596 
601 TEST_CASE( "initDiagonalOp", "[data_structures]" ) {
602 
603  // must be at least one amplitude per node
604  int minNumQb = calcLog2(QUEST_ENV.numRanks);
605  if (minNumQb == 0)
606  minNumQb = 1;
607 
608  SECTION( "correctness" ) {
609 
610  // try 10 valid number of qubits
611  int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
613 
614  long long int len = (1LL << numQb);
615  qreal reals[len];
616  qreal imags[len];
617  long long int n;
618  for (n=0; n<len; n++) {
619  reals[n] = (qreal) n;
620  imags[n] = (qreal) -2*n; // (n - 2n i)
621  }
622  initDiagonalOp(op, reals, imags);
623 
624  // check that op.real and op.imag were modified...
625  REQUIRE( areEqual(toQVector(op), reals, imags) );
626 
627  // and also that GPU real and imag were modified
628  // via if it modifies an all-unity state-vec correctly
629  Qureg qureg = createQureg(numQb, QUEST_ENV);
630  for (long long int i=0; i<qureg.numAmpsPerChunk; i++) {
631  qureg.stateVec.real[i] = 1;
632  qureg.stateVec.imag[i] = 1;
633  }
634  copyStateToGPU(qureg);
635 
636  QVector prodRef = toQMatrix(op) * toQVector(qureg);
637 
638  // (n - 2n i) * (1 + 1i) = 3n - n*i
639  applyDiagonalOp(qureg, op);
640  copyStateFromGPU(qureg);
641  QVector result = toQVector(qureg);
642  REQUIRE( areEqual(prodRef, result) );
643 
644  destroyQureg(qureg, QUEST_ENV);
646  }
647 }
648 
649 
650 
655 TEST_CASE( "initPauliHamil", "[data_structures]" ) {
656 
657  SECTION( "correctness" ) {
658 
659  PauliHamil hamil = createPauliHamil(3, 2);
660 
661  qreal coeffs[] = {-5, 5};
662  enum pauliOpType codes[] = {
665  initPauliHamil(hamil, coeffs, codes);
666 
667  // check everything written correctly
668  for (int t=0; t<2; t++) {
669  REQUIRE( coeffs[t] == hamil.termCoeffs[t] );
670  for (int q=0; q<3; q++) {
671  int ind = 3*t+q;
672  REQUIRE( codes[ind] == hamil.pauliCodes[ind] );
673  }
674  }
675 
676  destroyPauliHamil(hamil);
677  }
678  SECTION( "input validation" ) {
679 
680  SECTION( "parameters" ) {
681 
682  // parameters checked before codes, so safe to leave un-initialised
683  qreal coeffs[1];
684  enum pauliOpType codes[1];
685  PauliHamil hamil;
686 
687  hamil.numQubits = GENERATE( -1, 0 );
688  hamil.numSumTerms = 1;
689  REQUIRE_THROWS_WITH( initPauliHamil(hamil, coeffs, codes), Contains("number of qubits") && Contains("strictly positive") );
690 
691  hamil.numQubits = 1;
692  hamil.numSumTerms = GENERATE( -1, 0 );
693  REQUIRE_THROWS_WITH( initPauliHamil(hamil, coeffs, codes), Contains("terms") && Contains("strictly positive") );
694  }
695  SECTION( "Pauli codes" ) {
696 
697  int numQb = 3;
698  int numTerms = 2;
699  int numCodes = numQb * numTerms;
700  qreal coeffs[numTerms];
701  enum pauliOpType codes[numCodes];
702 
703  // make only one code invalid
704  for (int i=0; i<numCodes; i++)
705  codes[i] = PAULI_I;
706  codes[GENERATE_COPY( range(0,numCodes) )] = (pauliOpType) GENERATE( -1, 4 );
707 
708  PauliHamil hamil = createPauliHamil(numQb, numTerms);
709  REQUIRE_THROWS_WITH( initPauliHamil(hamil, coeffs, codes), Contains("Invalid Pauli code") );
710  destroyPauliHamil(hamil);
711  }
712  }
713 }
714 
715 
716 
721 TEST_CASE( "setDiagonalOpElems", "[data_structures]" ) {
722 
723  // must be at least one amplitude per node
724  int minNumQb = calcLog2(QUEST_ENV.numRanks);
725  if (minNumQb == 0)
726  minNumQb = 1;
727 
728  // try 10 valid number of qubits (even for validation)
729  int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
731 
732  SECTION( "correctness" ) {
733 
734  // make entire array on every node
735  long long int len = (1LL << numQb);
736  qreal reals[len];
737  qreal imags[len];
738  long long int n;
739  for (n=0; n<len; n++) {
740  reals[n] = (qreal) n;
741  imags[n] = (qreal) -2*n; // (n - 2n i)
742  }
743 
744  // set one value at a time (only relevant nodes will update)
745  for (n=0; n<len; n++)
746  setDiagonalOpElems(op, n, &reals[n], &imags[n], 1);
747 
748  // check op.real and op.imag updated correctly
749  REQUIRE( areEqual(toQVector(op), reals, imags) );
750 
751  // no check that GPU values updated (occurs in initDiagonalOp)
752  }
753  SECTION( "input validation" ) {
754 
755  long long int maxInd = (1LL << numQb);
756  qreal *reals;
757  qreal *imags;
758 
759  SECTION( "start index" ) {
760 
761  int startInd = GENERATE_COPY( -1, maxInd );
762  int numAmps = 1;
763  REQUIRE_THROWS_WITH( setDiagonalOpElems(op, startInd, reals, imags, numAmps), Contains("Invalid element index") );
764  }
765 
766  SECTION( "number of amplitudes" ) {
767 
768  // independent
769  int startInd = 0;
770  int numAmps = GENERATE_COPY( -1, maxInd+1 );
771  REQUIRE_THROWS_WITH( setDiagonalOpElems(op, startInd, reals, imags, numAmps), Contains("Invalid number of elements") );
772 
773  // invalid considering start-index
774  startInd = maxInd - 1;
775  numAmps = 2;
776  REQUIRE_THROWS_WITH( setDiagonalOpElems(op, startInd, reals, imags, numAmps), Contains("More elements given than exist") );
777  }
778  }
779 
781 }
782 
783 
784 
789 TEST_CASE( "syncDiagonalOp", "[data_structures]" ) {
790 
791  // must be at least one amplitude per node
792  int minNumQb = calcLog2(QUEST_ENV.numRanks);
793  if (minNumQb == 0)
794  minNumQb = 1;
795 
796  SECTION( "correctness" ) {
797 
798  // try 10 valid number of qubits
799  int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
801 
802  // check that changes get sync'd to the GPU...
803  long long int n;
804  for (n=0; n<op.numElemsPerChunk; n++) {
805  op.real[n] = (qreal) n;
806  op.imag[n] = (qreal) -2*n; // (n - 2n i)
807  }
808  syncDiagonalOp(op);
809 
810  // via if it modifies an all-unity state-vec correctly
811  Qureg qureg = createQureg(numQb, QUEST_ENV);
812  for (long long int i=0; i<qureg.numAmpsPerChunk; i++) {
813  qureg.stateVec.real[i] = 1;
814  qureg.stateVec.imag[i] = 1;
815  }
816  copyStateToGPU(qureg);
817 
818  // (n - 2n i) * (1 + 1i) = 3n - n*i
819  applyDiagonalOp(qureg, op);
820  copyStateFromGPU(qureg);
821  for (n=0; n<qureg.numAmpsPerChunk; n++) {
822  REQUIRE( qureg.stateVec.real[n] == 3*n );
823  REQUIRE( qureg.stateVec.imag[n] == -n );
824  }
825 
826  destroyQureg(qureg, QUEST_ENV);
828  }
829 }
pauliOpType
Codes for specifying Pauli operators.
Definition: QuEST.h:96
void copyStateFromGPU(Qureg qureg)
In GPU mode, this copies the state-vector (or density matrix) from GPU memory (qureg....
Definition: QuEST_cpu.c:39
void initPauliHamil(PauliHamil hamil, qreal *coeffs, enum pauliOpType *codes)
Initialise a PauliHamil instance with the given term coefficients and Pauli codes (one for every qubi...
Definition: QuEST.c:1253
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
int rank
Definition: QuEST.h:244
void destroyComplexMatrixN(ComplexMatrixN m)
Destroy a ComplexMatrixN instance created with createComplexMatrixN()
Definition: QuEST.c:1120
int numChunks
The number of nodes between which the elements of this operator are split.
Definition: QuEST.h:185
@ PAULI_I
Definition: QuEST.h:96
TEST_CASE("fromComplex", "[data_structures]")
ComplexMatrixN createComplexMatrixN(int numQubits)
Create (dynamically) a square complex matrix which can be passed to the multi-qubit general unitary f...
Definition: QuEST.c:1099
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
int chunkId
The position of the chunk of the operator held by this process in the full operator.
Definition: QuEST.h:187
Information about the environment the program is running in.
Definition: QuEST.h:242
PauliHamil createPauliHamilFromFile(char *fn)
Create a PauliHamil instance, a real-weighted sum of products of Pauli operators, populated with the ...
Definition: QuEST.c:1169
void setDiagonalOpElems(DiagonalOp op, long long int startInd, qreal *real, qreal *imag, long long int numElems)
Modifies a subset (starting at index startInd) of the elements in DiagonalOp op with the given elemen...
Definition: QuEST.c:1292
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
unsigned int calcLog2(long unsigned int num)
returns log2 of numbers which must be gauranteed to be 2^n
@ PAULI_X
Definition: QuEST.h:96
int numQubitsInStateVec
Number of qubits in the state-vector - this is double the number represented for mixed states.
Definition: QuEST.h:210
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
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
long long int numAmpsPerChunk
Number of probability amplitudes held in stateVec by this process In the non-MPI version,...
Definition: QuEST.h:213
void applyDiagonalOp(Qureg qureg, DiagonalOp op)
Apply a diagonal complex operator, which is possibly non-unitary and non-Hermitian,...
Definition: QuEST.c:887
qreal * termCoeffs
The coefficient of each Pauli product. This is a length numSumTerms array.
Definition: QuEST.h:164
#define qcomp
enum pauliOpType * pauliCodes
The Pauli operators acting on each qubit, flattened over every operator.
Definition: QuEST.h:162
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
#define toComplex(scalar)
int numRanks
Definition: QuEST.h:245
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
void destroyQureg(Qureg qureg, QuESTEnv env)
Deallocate a Qureg object representing a set of qubits.
Definition: QuEST.c:77
qreal ** real
Definition: QuEST.h:139
Represents a system of qubits.
Definition: QuEST.h:203
void initDiagonalOp(DiagonalOp op, qreal *real, qreal *imag)
Updates the entire DiagonalOp op with the given elements, of which there must be 2^op....
Definition: QuEST.c:1286
ComplexArray stateVec
Computational state amplitudes - a subset thereof in the MPI version.
Definition: QuEST.h:222
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
Qureg createCloneQureg(Qureg qureg, QuESTEnv env)
Create a new Qureg which is an exact clone of the passed qureg, which can be either a statevector or ...
Definition: QuEST.c:64
std::vector< std::vector< qcomp > > QMatrix
A complex square matrix.
Definition: utilities.hpp:49
int numQubits
The number of qubits for which this Hamiltonian is defined.
Definition: QuEST.h:168
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
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 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
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
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
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
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