The Quantum Exact Simulation Toolkit v4.2.0
Loading...
Searching...
No Matches
test_data_structures.cpp
1/** @file
2 * Ported tests of the deprecated QuEST v3 interface,
3 * unit testing the "data structures" module.
4 *
5 * This file should be excluded from doxygen parsing so
6 * as not to conflict with the doc of the v4 unit tests.
7 *
8 * @author Tyson Jones
9 * @author Oliver Thomson Brown (ported to Catch2 v3)
10 * @author Ali Rezaei (tested porting to QuEST v4)
11 * @author Richard Meister (QuEST v3 patches)
12 */
13
14#include <catch2/catch_test_macros.hpp>
15#include <catch2/matchers/catch_matchers_string.hpp>
16#include <catch2/generators/catch_generators_range.hpp>
17
18#include "quest.h"
19#include "test_utilities.hpp"
20
21/* allows concise use of ContainsSubstring in catch's REQUIRE_THROWS_WITH */
22using Catch::Matchers::ContainsSubstring;
23
24
25
26// fromComplex removed because Complex is completely deprecated
27
28 // /** @sa fromComplex
29 // * @ingroup deprecatedtests
30 // * @author Tyson Jones
31 // */
32 // TEST_CASE( "fromComplex", "[data_structures]" ) {
33
34 // Complex a;
35 // a.real= .5;
36 // a.imag= -.2;
37 // qcomp b = fromComplex(a);
38
39 // REQUIRE( a.real == real(b) );
40 // REQUIRE( a.imag == imag(b) );
41 // }
42
43
44
45// getStaticComplexMatrixN is completely deprecated
46
47 // /** @sa getStaticComplexMatrixN
48 // * @ingroup deprecatedtests
49 // * @author Tyson Jones
50 // */
51 // TEST_CASE( "getStaticComplexMatrixN", "[data_structures]" ) {
52
53 // /* use of this function is illegal in C++ */
54 // SUCCEED( );
55 // }
56
57
58
59// toComplex removed because Complex is completely deprecated
60
61 // /** @sa toComplex
62 // * @ingroup deprecatedtests
63 // * @author Tyson Jones
64 // */
65 // TEST_CASE( "toComplex", "[data_structures]" ) {
66
67 // qcomp a = qcomp(.5,-.2);
68 // #if (!defined(_WIN32)) && (!defined(_WIN64))
69 // Complex b = toComplex(a);
70 // #else
71 // // MSVC profanely forbids in-line struct initialisation
72 // Complex b; b.real = real(a); b.imag = imag(a);
73 // #endif
74
75 // REQUIRE( real(a) == b.real );
76 // REQUIRE( imag(a) == b.imag );
77 // }
78
79
80
81/** @sa createCloneQureg
82 * @ingroup deprecatedtests
83 * @author Tyson Jones
84 */
85TEST_CASE( "createCloneQureg", "[data_structures]" ) {
86
87 SECTION( "state-vector" ) {
88
89 Qureg a = createForcedQureg(NUM_QUBITS);
91
92 // check properties are the same
93 REQUIRE( b.isMultithreaded == a.isMultithreaded );
94 REQUIRE( b.isGpuAccelerated == a.isGpuAccelerated );
95 REQUIRE( b.isDistributed == a.isDistributed );
96
97 REQUIRE( b.rank == a.rank );
98 REQUIRE( b.numNodes == a.numNodes );
99 REQUIRE( b.logNumNodes == a.logNumNodes );
100
101 REQUIRE( b.isDensityMatrix == a.isDensityMatrix );
102 REQUIRE( b.numQubits == a.numQubits );
103 REQUIRE( b.numAmps == a.numAmps );
104 REQUIRE( b.logNumAmps == a.logNumAmps );
105
106 REQUIRE( b.numAmpsPerNode == a.numAmpsPerNode );
107 REQUIRE( b.logNumAmpsPerNode == a.logNumAmpsPerNode );
108 REQUIRE( b.logNumColsPerNode == a.logNumColsPerNode );
109
110 // check amps the same (works for GPU and distributed)
111 REQUIRE( areEqual(a, b) );
112
113 destroyQureg(a);
114 destroyQureg(b);
115 }
116 SECTION( "density-matrix" ) {
117
118 Qureg a = createForcedDensityQureg(NUM_QUBITS);
119 Qureg b = createCloneQureg(a);
120
121 // check properties are the same
122 REQUIRE( b.isMultithreaded == a.isMultithreaded );
123 REQUIRE( b.isGpuAccelerated == a.isGpuAccelerated );
124 REQUIRE( b.isDistributed == a.isDistributed );
125
126 REQUIRE( b.rank == a.rank );
127 REQUIRE( b.numNodes == a.numNodes );
128 REQUIRE( b.logNumNodes == a.logNumNodes );
129
130 REQUIRE( b.isDensityMatrix == a.isDensityMatrix );
131 REQUIRE( b.numQubits == a.numQubits );
132 REQUIRE( b.numAmps == a.numAmps );
133 REQUIRE( b.logNumAmps == a.logNumAmps );
134
135 REQUIRE( b.numAmpsPerNode == a.numAmpsPerNode );
136 REQUIRE( b.logNumAmpsPerNode == a.logNumAmpsPerNode );
137 REQUIRE( b.logNumColsPerNode == a.logNumColsPerNode );
138
139 // check amps are the same (works for GPU and distributed)
140 REQUIRE( areEqual(a, b) );
141
142 destroyQureg(a);
143 destroyQureg(b);
144 }
145}
146
147
148
149/** @sa createComplexMatrixN
150 * @ingroup deprecatedtests
151 * @author Tyson Jones
152 */
153TEST_CASE( "createComplexMatrixN", "[data_structures]" ) {
154
155 SECTION( "correctness" ) {
156
157 int numQb = GENERATE( range(1,10+1) );
158 ComplexMatrixN m = createComplexMatrixN(numQb);
159
160 // ensure elems are created and initialised to 0
161 REQUIRE( areEqual(toQMatrix(m), getZeroMatrix(1<<numQb)) );
162
163 destroyComplexMatrixN(m);
164 }
165 SECTION( "input validation" ) {
166
167 SECTION( "number of qubits" ) {
168
169 int numQb = GENERATE( -1, 0 );
170 REQUIRE_THROWS_WITH( createComplexMatrixN(numQb), ContainsSubstring("must target one or more qubits") );
171 }
172 }
173}
174
175
176
177/** @sa createDensityQureg
178 * @ingroup deprecatedtests
179 * @author Tyson Jones
180 */
181TEST_CASE( "createDensityQureg", "[data_structures]" ) {
182
183 // must be at least one column per node
184 int minNumQb = calcLog2(getQuESTEnv().numNodes);
185 if (minNumQb <= 0)
186 minNumQb = 1;
187
188 SECTION( "correctness" ) {
189
190 // try 10 valid number of qubits
191 int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
192 Qureg reg = createForcedDensityQureg(numQb);
193
194 // ensure elems (CPU and/or GPU) are created, and reg begins in |0><0|
195 QMatrix ref = getZeroMatrix(1<<numQb);
196 ref[0][0] = 1; // |0><0|
197 REQUIRE( areEqual(reg, ref) );
198
199 destroyQureg(reg);
200 }
201 SECTION( "input validation") {
202
203 SECTION( "number of qubits" ) {
204
205 int numQb = GENERATE( -1, 0 );
206 REQUIRE_THROWS_WITH( createForcedDensityQureg(numQb), ContainsSubstring("must contain one or more qubits") );
207 }
208 SECTION( "number of amplitudes" ) {
209
210 // v4 does not consult QuESTEnv
211
212 // // use local QuESTEnv to safely modify
213 // QuESTEnv env = getQuESTEnv();
214
215 // too many amplitudes to store in type
216 int maxQb = (int) calcLog2(SIZE_MAX) / 2;
217 REQUIRE_THROWS_WITH( createForcedDensityQureg(maxQb+1),
218 ContainsSubstring("the density matrix would contain more amplitudes") && ContainsSubstring("than can be addressed by the qindex type") );
219
220 // it is non-trivial to force an invalid distribution
221 // in v4, since QuESTEnv arg is no longer consulted; we
222 // will reserve this input validation for the full tests
223
224 // /* n-qubit density matrix contains 2^(2n) amplitudes
225 // * so can be spread between at most 2^(2n) ranks
226 // */
227 // /* env.numNodes is an int, so maxQb must be capped at 16 for this
228 // * test to avoid an integer overflow when storing 2**(2*minQb) in it
229 // */
230 // maxQb = maxQb > 16 ? 16 : maxQb;
231 // int minQb = GENERATE_COPY( range(3,maxQb) );
232 // env.numNodes = (int) pow(2, 2*minQb);
233 // int numQb = GENERATE_COPY( range(1,minQb) );
234 // REQUIRE_THROWS_WITH( createForcedDensityQureg(numQb, env), ContainsSubstring("Too few qubits") );
235 }
236 SECTION( "available memory" ) {
237
238 /* there is no reliable way to force the malloc statements to
239 * fail, and hence trigger the matrixInit validation */
240 SUCCEED( );
241 }
242 }
243}
244
245
246
247/** @sa createDiagonalOp
248 * @ingroup deprecatedtests
249 * @author Tyson Jones
250 */
251TEST_CASE( "createDiagonalOp", "[data_structures]" ) {
252
253 QuESTEnv env = getQuESTEnv();
254
255 // must be at least one amplitude per node
256 int minNumQb = calcLog2(env.numNodes);
257 if (minNumQb == 0)
258 minNumQb = 1;
259
260 SECTION( "correctness" ) {
261
262 // try 10 valid number of qubits
263 int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
264 DiagonalOp op = createDiagonalOp(numQb, env);
265
266 // check properties are correct
267 REQUIRE( op.numQubits == numQb );
268 REQUIRE( op.numElemsPerNode == (1LL << numQb) / (op.isDistributed? env.numNodes : 1) );
269 REQUIRE( op.cpuElems != NULL );
270
271 // check all elements in CPU are zero
272 REQUIRE( areEqual(toQVector(op), QVector(1LL << numQb)) );
273
274 // (no concise way to check this for GPU)
275
276 destroyDiagonalOp(op, env);
277 }
278 SECTION( "input validation" ) {
279
280 SECTION( "number of qubits" ) {
281
282 int numQb = GENERATE( -1, 0 );
283 REQUIRE_THROWS_WITH( createDiagonalOp(numQb, env), ContainsSubstring("must target one or more qubits") );
284 }
285 SECTION( "number of elements" ) {
286
287 // too many amplitudes to store in type
288 int maxQb = (int) calcLog2(SIZE_MAX);
289 REQUIRE_THROWS_WITH( createDiagonalOp(maxQb+1, env),
290 ContainsSubstring("the matrix would contain more elements") && ContainsSubstring("than the maximum which can be addressed by the qindex type") );
291
292 // cannot test when there are too few elements, since the deprecated
293 // interface is redirecting to createFullStateDiagMatr which auto-deploys,
294 // and so simply automatically disables distribution
295
296 // /* env.numNodes is an int, so maxQb must be capped at 32 for this
297 // * test to avoid an integer overflow when storing 2**minQb in it
298 // */
299 // maxQb = maxQb > 32 ? 32 : maxQb;
300 // // too few amplitudes to distribute
301 // int minQb = GENERATE_COPY( range(2,maxQb) );
302 // env.numNodes = (int) pow(2, minQb);
303 // int numQb = GENERATE_COPY( range(1,minQb) );
304 // REQUIRE_THROWS_WITH( createDiagonalOp(numQb, env), ContainsSubstring("Too few qubits") && ContainsSubstring("distributed"));
305 }
306 SECTION( "available memory" ) {
307
308 /* there is no reliable way to force the malloc statements to
309 * fail, and hence trigger the diagonalOpInit validation */
310 SUCCEED( );
311 }
312 }
313}
314
315
316
317// createDiagonalOpFromPauliHamilFile removed because PauliHamil
318// is deprecated, and replacement PauliStrSum (with analogous function
319// createFullStateDiagMatrFromPauliStrSumFile) is incompatible
320
321 // /** @sa createDiagonalOpFromPauliHamilFile
322 // * @ingroup deprecatedtests
323 // * @author Tyson Jones
324 // */
325 // TEST_CASE( "createDiagonalOpFromPauliHamilFile", "[data_structures]" ) {
326
327 // // files created & populated during the test, and deleted afterward
328 // char fnPrefix[] = "temp_createDiagonalOpFromPauliHamilFile";
329 // char fn[100];
330 // int fnMaxLen = 100;
331
332 // // each test uses a unique filename (managed by master node), to avoid file IO locks
333 // // (it's safe for sub-test to overwrite this, since after each test, all files
334 // // with prefix fnPrefix are deleted)
335 // setUniqueFilename(fn, fnMaxLen, fnPrefix);
336
337 // // diagonal op must have at least one amplitude per node
338 // int minNumQb = calcLog2(getQuESTEnv().numNodes);
339 // if (minNumQb == 0)
340 // minNumQb = 1;
341
342 // SECTION( "correctness" ) {
343
344 // SECTION( "general" ) {
345
346 // // try several Pauli Hamiltonian sizes
347 // int numQb = GENERATE_COPY( range(minNumQb, 6+minNumQb) );
348 // int numTerms = GENERATE_COPY( 1, minNumQb, 10*minNumQb );
349
350 // // create a PauliHamil with random elements
351
352 // PauliHamil hamil = createPauliHamil(numQb, numTerms);
353 // createPauliStrSum()
354 // setRandomDiagPauliHamil(hamil, numQb);
355
356 // // write the Hamiltonian to file (with trailing whitespace, and trailing newline)
357 // if (getQuESTEnv().rank == 0) {
358 // FILE* file = fopen(fn, "w");
359 // int i=0;
360 // for (int n=0; n<numTerms; n++) {
361 // fprintf(file, REAL_STRING_FORMAT, hamil.termCoeffs[n]);
362 // fprintf(file, " ");
363 // for (int q=0; q<numQb; q++)
364 // fprintf(file, "%d ", (int) hamil.pauliCodes[i++]);
365 // fprintf(file, "\n");
366 // }
367 // fprintf(file, "\n");
368 // fclose(file);
369 // }
370 // syncQuESTEnv(getQuESTEnv());
371
372 // // load the file as a diagonal operator, and compare
373 // DiagonalOp op = createDiagonalOpFromPauliHamilFile(fn, env);
374 // REQUIRE( areEqual(toQMatrix(op), toQMatrix(hamil)) );
375
376 // destroyPauliHamil(hamil);
377 // destroyDiagonalOp(op, env);
378 // }
379 // SECTION( "edge cases" ) {
380
381 // // prepare a valid single-term diagonal Pauli Hamiltonian
382 // qreal coeffs[] = {.1};
383 // VLA(pauliOpType, codes, minNumQb);
384 // for (int q=0; q<minNumQb; q++)
385 // codes[q] = (q%2)? PAULI_I : PAULI_Z;
386
387 // QMatrix ref = toQMatrix(coeffs, codes, minNumQb, 1);
388
389 // // prepare basic encoding string
390 // string line = to_string(coeffs[0]) + " ";
391 // for (int q=0; q<minNumQb; q++)
392 // line += to_string(codes[q]) + ((q<minNumQb-1)? " ":"");
393
394 // SECTION( "no trailing newline or space" ) {
395
396 // writeToFileSynch(fn, line);
397
398 // DiagonalOp op = createDiagonalOpFromPauliHamilFile(fn, env);
399 // REQUIRE( areEqual(ref, toQMatrix(op)) );
400
401 // destroyDiagonalOp(op, env);
402 // }
403 // SECTION( "trailing newlines" ) {
404
405 // writeToFileSynch(fn, line + "\n\n\n");
406
407 // DiagonalOp op = createDiagonalOpFromPauliHamilFile(fn, env);
408 // REQUIRE( areEqual(ref, toQMatrix(op)) );
409
410 // destroyDiagonalOp(op, env);
411 // }
412 // SECTION( "trailing spaces" ) {
413
414 // writeToFileSynch(fn, line + " ");
415
416 // DiagonalOp op = createDiagonalOpFromPauliHamilFile(fn, env);
417 // REQUIRE( areEqual(ref, toQMatrix(op)) );
418
419 // destroyDiagonalOp(op, env);
420 // }
421 // }
422 // }
423 // SECTION( "input validation") {
424
425 // SECTION( "number of qubits" ) {
426
427 // writeToFileSynch(fn, ".1 "); // 0 qubits
428 // REQUIRE_THROWS_WITH( createDiagonalOpFromPauliHamilFile(fn, env), ContainsSubstring("The number of qubits") && ContainsSubstring("strictly positive"));
429 // }
430 // SECTION( "number of elements" ) {
431
432 // // too many amplitudes to store in type
433 // int maxQb = (int) calcLog2(SIZE_MAX);
434
435 // // encode one more qubit than legal to file
436 // string line = ".1 ";
437 // for (int q=0; q<(maxQb+1); q++)
438 // line += "3 "; // trailing space ok
439 // writeToFileSynch(fn, line);
440
441 // REQUIRE_THROWS_WITH( createDiagonalOpFromPauliHamilFile(fn, env), ContainsSubstring("Too many qubits") && ContainsSubstring("size_t type") );
442
443 // // use local QuESTEnv to safely modify
444 // QuESTEnv env = getQuESTEnv();
445
446 // // too few elements to distribute
447 // /* env.numNodes is an int, so maxQb must be capped at 32 for this
448 // * test to avoid an integer overflow when storing 2**minQb in it
449 // */
450 // maxQb = maxQb > 32 ? 32 : maxQb;
451 // int minQb = GENERATE_COPY( range(2,maxQb) );
452 // env.numNodes = (int) pow(2, minQb);
453 // int numQb = GENERATE_COPY( range(1,minQb) );
454
455 // line = ".1 ";
456 // for (int q=0; q<numQb; q++)
457 // line += "3 "; // trailing space ok
458 // setUniqueFilename(fn, fnMaxLen, fnPrefix);
459 // writeToFileSynch(fn, line);
460
461 // REQUIRE_THROWS_WITH( createDiagonalOpFromPauliHamilFile(fn, env), ContainsSubstring("Too few qubits") && ContainsSubstring("distributed") );
462 // }
463 // SECTION( "coefficient type" ) {
464
465 // writeToFileSynch(fn, "notanumber 1 2 3");
466 // REQUIRE_THROWS_WITH( createDiagonalOpFromPauliHamilFile(fn, env), ContainsSubstring("Failed to parse") && ContainsSubstring("coefficient"));
467 // }
468 // SECTION( "pauli code" ) {
469
470 // writeToFileSynch(fn, ".1 0 3 2"); // final is invalid Y
471 // REQUIRE_THROWS_WITH( createDiagonalOpFromPauliHamilFile(fn, env), ContainsSubstring("contained operators other than PAULI_Z and PAULI_I"));
472
473 // setUniqueFilename(fn, fnMaxLen, fnPrefix);
474 // writeToFileSynch(fn, ".1 0 1 3"); // second is invalid X
475 // REQUIRE_THROWS_WITH( createDiagonalOpFromPauliHamilFile(fn, env), ContainsSubstring("contained operators other than PAULI_Z and PAULI_I"));
476
477 // setUniqueFilename(fn, fnMaxLen, fnPrefix);
478 // writeToFileSynch(fn, ".1 0 1 4"); // final is invalid Pauli code
479 // REQUIRE_THROWS_WITH( createDiagonalOpFromPauliHamilFile(fn, env), ContainsSubstring("invalid pauli code"));
480
481 // setUniqueFilename(fn, fnMaxLen, fnPrefix);
482 // writeToFileSynch(fn, ".1 3 0 notanumber"); // final is invalid type
483 // REQUIRE_THROWS_WITH( createDiagonalOpFromPauliHamilFile(fn, env), ContainsSubstring("Failed to parse the next expected Pauli code"));
484 // }
485 // }
486
487 // // delete all files created above
488 // deleteFilesWithPrefixSynch(fnPrefix);
489 // }
490
491
492
493// createPauliHamil removed because PauliHamil is deprecated, and
494// replacement PauliStrSum has an incompatible constructor
495
496 // /** @sa createPauliHamil
497 // * @ingroup deprecatedtests
498 // * @author Tyson Jones
499 // */
500 // TEST_CASE( "createPauliHamil", "[data_structures]" ) {
501
502 // SECTION( "correctness" ) {
503
504 // int numQb = GENERATE( range(1,5) );
505 // int numTerms = GENERATE( range(1,5) );
506 // PauliHamil hamil = createPauliHamil(numQb, numTerms);
507
508 // // check fields are correct
509 // REQUIRE( hamil.numQubits == numQb );
510 // REQUIRE( hamil.numSumTerms == numTerms );
511
512 // // check all Pauli codes are identity
513 // int numPaulis = numQb * numTerms;
514 // for (int i=0; i<numPaulis; i++) {
515 // REQUIRE( hamil.pauliCodes[i] == PAULI_I );
516 // }
517
518 // // check all term coefficients can be written to (no seg fault)
519 // for (int j=0; j<numTerms; j++) {
520 // hamil.termCoeffs[j] = 1;
521 // REQUIRE( hamil.termCoeffs[j] == 1 );
522 // }
523
524 // destroyPauliHamil(hamil);
525 // }
526 // SECTION( "input validation") {
527
528 // SECTION( "number of qubits" ) {
529
530 // int numQb = GENERATE( -1, 0 );
531 // REQUIRE_THROWS_WITH( createPauliHamil(numQb, 1), ContainsSubstring("The number of qubits and terms in the PauliHamil must be strictly positive.") );
532 // }
533 // SECTION( "number of terms" ) {
534
535 // int numTerms = GENERATE( -1, 0 );
536 // REQUIRE_THROWS_WITH( createPauliHamil(1, numTerms), ContainsSubstring("The number of qubits and terms in the PauliHamil must be strictly positive.") );
537 // }
538 // }
539 // }
540
541
542
543// createPauliHamilFromFile removed because PauliHamil is deprecated, and
544// replacement PauliStrSum (with analogous function createPauliStrSumFromFile
545// has incompatible struct fields
546
547 // /** @sa createPauliHamilFromFile
548 // * @ingroup deprecatedtests
549 // * @author Tyson Jones
550 // */
551 // TEST_CASE( "createPauliHamilFromFile", "[data_structures]" ) {
552
553 // // files created & populated during the test, and deleted afterward
554 // char fnPrefix[] = "temp_createPauliHamilFromFile";
555 // char fn[100];
556 // int fnMaxLen = 100;
557
558 // // each test uses a unique filename (managed by master node), to avoid file IO locks
559 // // (it's safe for sub-test to overwrite this, since after each test, all files
560 // // with prefix fnPrefix are deleted)
561 // setUniqueFilename(fn, fnMaxLen, fnPrefix);
562
563 // SECTION( "correctness" ) {
564
565 // SECTION( "general" ) {
566
567 // // for several sizes...
568 // int numQb = GENERATE( 1, 5, 10, 15 );
569 // int numTerms = GENERATE( 1, 10, 30 );
570 // int numPaulis = numQb*numTerms;
571
572 // // create a PauliHamil with random elements
573 // VLA(qreal, coeffs, numTerms);
574 // VLA(pauliOpType, paulis, numPaulis);
575 // setRandomPauliSum(coeffs, paulis, numQb, numTerms);
576
577 // // write the Hamiltonian to file (with trailing whitespace, and trailing newline)
578 // if (getQuESTEnv().rank == 0) {
579 // FILE* file = fopen(fn, "w");
580 // int i=0;
581 // for (int n=0; n<numTerms; n++) {
582 // fprintf(file, REAL_STRING_FORMAT, coeffs[n]);
583 // fprintf(file, " ");
584 // for (int q=0; q<numQb; q++)
585 // fprintf(file, "%d ", (int) paulis[i++]);
586 // fprintf(file, "\n");
587 // }
588 // fprintf(file, "\n");
589 // fclose(file);
590 // }
591 // syncQuESTEnv(getQuESTEnv());
592
593 // // load the file as a PauliHamil
594 // PauliHamil hamil = createPauliHamilFromFile(fn);
595
596 // // check fields agree
597 // REQUIRE( hamil.numQubits == numQb );
598 // REQUIRE( hamil.numSumTerms == numTerms );
599
600 // // check elements agree
601 // int j=0;
602 // for (int n=0; n<numTerms; n++) {
603 // REQUIRE( absReal(hamil.termCoeffs[n] - coeffs[n]) <= REAL_EPS );
604 // for (int q=0; q<numQb; q++) {
605 // REQUIRE( hamil.pauliCodes[j] == paulis[j] );
606 // j++;
607 // }
608 // }
609
610 // destroyPauliHamil(hamil);
611 // }
612 // SECTION( "edge cases" ) {
613
614 // SECTION( "no trailing newline or space" ) {
615
616 // writeToFileSynch(fn, ".1 1 0 1");
617
618 // PauliHamil hamil = createPauliHamilFromFile(fn);
619 // REQUIRE( hamil.numSumTerms == 1 );
620 // REQUIRE( hamil.numQubits == 3 );
621
622 // destroyPauliHamil(hamil);
623 // }
624 // SECTION( "trailing newlines" ) {
625
626 // writeToFileSynch(fn, ".1 1 0 1\n\n\n");
627
628 // PauliHamil hamil = createPauliHamilFromFile(fn);
629 // REQUIRE( hamil.numSumTerms == 1 );
630 // REQUIRE( hamil.numQubits == 3 );
631
632 // destroyPauliHamil(hamil);
633 // }
634 // SECTION( "trailing spaces" ) {
635
636 // writeToFileSynch(fn, ".1 1 0 1 ");
637
638 // PauliHamil hamil = createPauliHamilFromFile(fn);
639 // REQUIRE( hamil.numSumTerms == 1 );
640 // REQUIRE( hamil.numQubits == 3 );
641
642 // destroyPauliHamil(hamil);
643 // }
644 // }
645 // }
646 // SECTION( "input validation") {
647
648 // SECTION( "number of qubits" ) {
649
650 // writeToFileSynch(fn, ".1 ");
651
652 // REQUIRE_THROWS_WITH( createPauliHamilFromFile(fn), ContainsSubstring("The number of qubits") && ContainsSubstring("strictly positive"));
653 // }
654 // SECTION( "coefficient type" ) {
655
656 // writeToFileSynch(fn, "notanumber 1 2 3");
657
658 // REQUIRE_THROWS_WITH( createPauliHamilFromFile(fn), ContainsSubstring("Failed to parse") && ContainsSubstring("coefficient"));
659 // }
660 // SECTION( "pauli code" ) {
661
662 // writeToFileSynch(fn, ".1 1 2 4"); // invalid int
663
664 // REQUIRE_THROWS_WITH( createPauliHamilFromFile(fn), ContainsSubstring("invalid pauli code"));
665
666 // setUniqueFilename(fn, fnMaxLen, fnPrefix);
667 // writeToFileSynch(fn, ".1 1 2 notanumber"); // invalid type
668
669 // REQUIRE_THROWS_WITH( createPauliHamilFromFile(fn), ContainsSubstring("Failed to parse the next expected Pauli code"));
670 // }
671 // }
672
673 // // cleanup temp files
674 // deleteFilesWithPrefixSynch(fn);
675 // }
676
677
678
679/** @sa createQuESTEnv
680 * @ingroup deprecatedtests
681 * @author Tyson Jones
682 */
683TEST_CASE( "createQuESTEnv", "[data_structures]" ) {
684
685 /* there is no meaningful way to test this */
686 SUCCEED( );
687}
688
689
690
691/** @sa createQureg
692 * @ingroup deprecatedtests
693 * @author Tyson Jones
694 */
695TEST_CASE( "createQureg", "[data_structures]" ) {
696
697 // we use createForcedQureg() in lieu of createQureg()
698 // to override the auto-deployer and force Qureg to use
699 // every available parallellisation method
700
701 // must be at least one amplitude per node
702 int minNumQb = calcLog2(getQuESTEnv().numNodes);
703 if (minNumQb == 0)
704 minNumQb = 1;
705
706 SECTION( "correctness" ) {
707
708 // try 10 valid number of qubits
709 int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
710 Qureg reg = createForcedQureg(numQb);
711
712 // ensure elems (CPU and/or GPU) are created, and reg begins in |0>
713 QVector ref = QVector(1<<numQb);
714 ref[0] = 1; // |0>
715 REQUIRE( areEqual(reg, ref) );
716
717 destroyQureg(reg);
718 }
719 SECTION( "input validation") {
720
721 SECTION( "number of qubits" ) {
722
723 int numQb = GENERATE( -1, 0 );
724 REQUIRE_THROWS_WITH( createForcedQureg(numQb), ContainsSubstring("must contain one or more qubits") );
725 }
726 SECTION( "number of amplitudes" ) {
727
728 // too many amplitudes to store in type
729 int maxQb = (int) calcLog2(SIZE_MAX);
730 REQUIRE_THROWS_WITH( createForcedQureg(maxQb+1),
731 ContainsSubstring("the statevector would contain more amplitudes") &&
732 ContainsSubstring("than the maximum which can be addressed by the qindex type") );
733
734 // it is non-trivial to force an invalid distribution
735 // in v4, since QuESTEnv arg is no longer consulted; we
736 // will reserve this input validation for the full tests
737
738 // // too few amplitudes to distribute
739 // /* env.numNodes is an int, so maxQb must be capped at 32 for this
740 // * test to avoid an integer overflow when storing 2**minQb in it
741 // */
742 // maxQb = maxQb > 32 ? 32 : maxQb;
743 // int minQb = GENERATE_COPY( range(2,maxQb) );
744 // env.numNodes = (int) pow(2, minQb);
745 // int numQb = GENERATE_COPY( range(1,minQb) );
746 // REQUIRE_THROWS_WITH( createForcedQureg(numQb, env), ContainsSubstring("Too few qubits") );
747 }
748 SECTION( "available memory" ) {
749
750 /* there is no reliable way to force the malloc statements to
751 * fail, and hence trigger the matrixInit validation */
752 SUCCEED( );
753 }
754 }
755}
756
757
758
759/** @sa createSubDiagonalOp
760 * @ingroup deprecatedtests
761 * @author Tyson Jones
762 */
763TEST_CASE( "createSubDiagonalOp", "[data_structures]" ) {
764
765 SECTION( "correctness" ) {
766
767 int numQb = GENERATE( range(1,10+1) );
768 SubDiagonalOp op = createSubDiagonalOp(numQb);
769
770 // ensure elems are created and initialised to 0
771 REQUIRE( areEqual(toQMatrix(op), getZeroMatrix(1<<numQb)) );
772
773 destroySubDiagonalOp(op);
774 }
775 SECTION( "input validation" ) {
776
777 SECTION( "number of qubits" ) {
778
779 int numQb = GENERATE( -1, 0 );
780 REQUIRE_THROWS_WITH( createSubDiagonalOp(numQb), ContainsSubstring("must target one or more qubits") );
781
782 numQb = 100;
783 REQUIRE_THROWS_WITH( createSubDiagonalOp(numQb),
784 ContainsSubstring("the matrix would contain more elements") &&
785 ContainsSubstring("than the maximum which can be addressed by the qindex type") );
786 }
787 }
788}
789
790
791
792/** @sa destroyComplexMatrixN
793 * @ingroup deprecatedtests
794 * @author Tyson Jones
795 */
796TEST_CASE( "destroyComplexMatrixN", "[data_structures]" ) {
797
798 SECTION( "correctness" ) {
799
800 /* there is no meaningful way to test this */
801 SUCCEED( );
802 }
803 SECTION( "input validation" ) {
804
805 SECTION( "matrix not created" ) {
806
807 /* this is an artificial test case since nothing in the QuEST API
808 * automatically sets un-initialised ComplexMatrixN fields to
809 * the NULL pointer.
810 */
811 ComplexMatrixN m;
812 m.cpuElems = NULL;
813
814 /* the error message is also somewhat unrelated, but oh well
815 */
816 REQUIRE_THROWS_WITH( destroyComplexMatrixN(m), ContainsSubstring("Invalid CompMatr") );
817 }
818 }
819}
820
821
822
823/** @sa destroyDiagonalOp
824 * @ingroup deprecatedtests
825 * @author Tyson Jones
826 */
827TEST_CASE( "destroyDiagonalOp", "[data_structures]" ) {
828
829 /* there is no meaningful way to test this */
830 SUCCEED( );
831}
832
833
834
835/** @sa destroyPauliHamil
836 * @ingroup deprecatedtests
837 * @author Tyson Jones
838 */
839TEST_CASE( "destroyPauliHamil", "[data_structures]" ) {
840
841 /* there is no meaningful way to test this.
842 * We e.g. cannot check that the pointers are NULL because
843 * they are not updated; this function passes the struct by value,
844 * not by reference. We also cannot reliably monitor the
845 * memory used in the heap at runtime.
846 */
847 SUCCEED( );
848}
849
850
851
852/** @sa destroyQuESTEnv
853 * @ingroup deprecatedtests
854 * @author Tyson Jones
855 */
856TEST_CASE( "destroyQuESTEnv", "[data_structures]" ) {
857
858 /* there is no meaningful way to test this */
859 SUCCEED( );
860}
861
862
863
864/** @sa destroyQureg
865 * @ingroup deprecatedtests
866 * @author Tyson Jones
867 */
868TEST_CASE( "destroyQureg", "[data_structures]" ) {
869
870 /* there is no meaningful way to test this.
871 * We e.g. cannot check that the pointers are NULL because
872 * they are not updated; this function passes the struct by value,
873 * not by reference. We also cannot reliably monitor the
874 * memory used in the heap at runtime.
875 */
876 SUCCEED( );
877}
878
879
880
881/** @sa destroySubDiagonalOp
882 * @ingroup deprecatedtests
883 * @author Tyson Jones
884 */
885TEST_CASE( "destroySubDiagonalOp", "[data_structures]" ) {
886
887 /* there is no meaningful way to test this */
888 SUCCEED( );
889}
890
891
892
893// initComplexMatrixN removed because ComplexMatrixN is deprecated,
894// and replacement CompMatr has analogous function setCompMatr with
895// an incompatible signature
896
897 // /** @sa initComplexMatrixN
898 // * @ingroup deprecatedtests
899 // * @author Tyson Jones
900 // */
901 // TEST_CASE( "initComplexMatrixN", "[data_structures]" ) {
902
903 // /* use of this function is illegal in C++ */
904 // SUCCEED( );
905 // }
906
907
908
909// initDiagonalOp removed because DiagonalOp is deprecated, and
910// analogous function setFullStateDiagMatr of replacement
911// FullStateDiagMatr has an incompatible signature
912
913 /** @sa initDiagonalOp
914 * @ingroup deprecatedtests
915 * @author Tyson Jones
916 */
917 // TEST_CASE( "initDiagonalOp", "[data_structures]" ) {
918
919 // // must be at least one amplitude per node
920 // int minNumQb = calcLog2(getQuESTEnv().numNodes);
921 // if (minNumQb == 0)
922 // minNumQb = 1;
923
924 // SECTION( "correctness" ) {
925
926 // // try 10 valid number of qubits
927 // int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
928 // DiagonalOp op = createDiagonalOp(numQb, getQuESTEnv());
929
930 // long long int len = (1LL << numQb);
931 // vector<qcomp> elems(len);
932
933 // VLA(qreal, reals, len);
934 // VLA(qreal, imags, len);
935 // long long int n;
936 // for (n=0; n<len; n++) {
937 // reals[n] = (qreal) n;
938 // imags[n] = (qreal) -2*n; // (n - 2n i)
939 // }
940 // initDiagonalOp(op, reals, imags);
941
942 // // check that op.real and op.imag were modified...
943 // REQUIRE( areEqual(toQVector(op), reals, imags) );
944
945 // // and also that GPU real and imag were modified
946 // // via if it modifies an all-unity state-vec correctly
947 // Qureg qureg = createForcedQureg(numQb);
948 // for (long long int i=0; i<qureg.numAmpsPerChunk; i++) {
949 // qureg.stateVec.real[i] = 1;
950 // qureg.stateVec.imag[i] = 1;
951 // }
952 // copyStateToGPU(qureg);
953
954 // QVector prodRef = toQMatrix(op) * toQVector(qureg);
955
956 // // (n - 2n i) * (1 + 1i) = 3n - n*i
957 // applyDiagonalOp(qureg, op);
958 // copyStateFromGPU(qureg);
959 // QVector result = toQVector(qureg);
960 // REQUIRE( areEqual(prodRef, result) );
961
962 // destroyQureg(qureg, getQuESTEnv());
963 // destroyDiagonalOp(op, getQuESTEnv());
964 // }
965// }
966
967
968
969// initDiagonalOpFromPauliHamil removed because DiagonalOp is
970// deprecated in favour of FullStateDiagMatr, which indeed has
971// analogous function setFullStateDiagMatrFromPauliStrSum, but
972// the involved PauliStrSum is incompatible with the deprecated
973// PauliHamil
974
975 /** @sa initDiagonalOpFromPauliHamil
976 * @ingroup deprecatedtests
977 * @author Tyson Jones
978 */
979 // TEST_CASE( "initDiagonalOpFromPauliHamil", "[data_structures]" ) {
980
981 // // distributed diagonal op must contain at least one amplitude per node
982 // int minNumQb = calcLog2(getQuESTEnv().numNodes);
983 // if (minNumQb == 0)
984 // minNumQb = 1;
985
986 // SECTION( "correctness" ) {
987
988 // // try (at most) 10 valid number of qubits (even for validation)
989 // int numQb = GENERATE_COPY( range(minNumQb, std::min(10,minNumQb+10)) );
990 // DiagonalOp op = createDiagonalOp(numQb, getQuESTEnv());
991
992 // // try several sized random all-Z Hamiltonians
993 // int numTerms = GENERATE_COPY( 1, numQb, 5*numQb );
994 // PauliHamil hamil = createPauliHamil(numQb, numTerms);
995 // setRandomDiagPauliHamil(hamil);
996
997 // initDiagonalOpFromPauliHamil(op, hamil);
998 // REQUIRE( areEqual(toQMatrix(op), toQMatrix(hamil)) );
999
1000 // destroyPauliHamil(hamil);
1001 // destroyDiagonalOp(op, getQuESTEnv());
1002 // }
1003 // SECTION( "input validation" ) {
1004
1005 // SECTION( "hamiltonian parameters" ) {
1006
1007 // DiagonalOp op = createDiagonalOp(minNumQb, getQuESTEnv());
1008 // PauliHamil hamil;
1009
1010 // hamil.numQubits = GENERATE( -1, 0 );
1011 // hamil.numSumTerms = 1;
1012 // REQUIRE_THROWS_WITH( initDiagonalOpFromPauliHamil(op, hamil), ContainsSubstring("number of qubits") && ContainsSubstring("strictly positive") );
1013
1014 // hamil.numQubits = minNumQb;
1015 // hamil.numSumTerms = GENERATE( -1, 0 );
1016 // REQUIRE_THROWS_WITH( initDiagonalOpFromPauliHamil(op, hamil), ContainsSubstring("terms") && ContainsSubstring("strictly positive") );
1017
1018 // destroyDiagonalOp(op, getQuESTEnv());
1019 // }
1020 // SECTION( "mismatching dimensions" ) {
1021
1022 // int numQbA = minNumQb+1;
1023 // int numQbB = GENERATE_COPY( numQbA-1, numQbA+1 );
1024
1025 // DiagonalOp op = createDiagonalOp(numQbA, getQuESTEnv());
1026 // PauliHamil hamil = createPauliHamil(numQbB, 1);
1027
1028 // REQUIRE_THROWS_WITH( initDiagonalOpFromPauliHamil(op, hamil), ContainsSubstring("Pauli Hamiltonian and diagonal operator have different, incompatible dimensions") );
1029
1030 // destroyDiagonalOp(op, getQuESTEnv());
1031 // destroyPauliHamil(hamil);
1032 // }
1033 // SECTION( "pauli codes" ) {
1034
1035 // DiagonalOp op = createDiagonalOp(minNumQb, getQuESTEnv());
1036 // PauliHamil hamil = createPauliHamil(minNumQb, 5); // all I
1037
1038 // // make only one code invalid
1039 // int numCodes = minNumQb * hamil.numSumTerms;
1040 // int ind = GENERATE_COPY( range(0,numCodes) );
1041 // hamil.pauliCodes[ind] = GENERATE( PAULI_X, PAULI_Y );
1042
1043 // REQUIRE_THROWS_WITH( initDiagonalOpFromPauliHamil(op, hamil), ContainsSubstring("contained operators other than PAULI_Z and PAULI_I") );
1044
1045 // destroyDiagonalOp(op, getQuESTEnv());
1046 // destroyPauliHamil(hamil);
1047 // }
1048 // }
1049 // }
1050
1051
1052
1053// initPauliHamil removed because PauliHamil is deprecated in
1054// favour of PauliStrSum with (so far) an incompatible initialiser
1055
1056 /** @sa initPauliHamil
1057 * @ingroup deprecatedtests
1058 * @author Tyson Jones
1059 */
1060 // TEST_CASE( "initPauliHamil", "[data_structures]" ) {
1061
1062 // SECTION( "correctness" ) {
1063
1064 // PauliHamil hamil = createPauliHamil(3, 2);
1065
1066 // qreal coeffs[] = {-5, 5};
1067 // enum pauliOpType codes[] = {
1068 // PAULI_X, PAULI_Y, PAULI_Z,
1069 // PAULI_Z, PAULI_Y, PAULI_X};
1070 // initPauliHamil(hamil, coeffs, codes);
1071
1072 // // check everything written correctly
1073 // for (int t=0; t<2; t++) {
1074 // REQUIRE( coeffs[t] == hamil.termCoeffs[t] );
1075 // for (int q=0; q<3; q++) {
1076 // int ind = 3*t+q;
1077 // REQUIRE( codes[ind] == hamil.pauliCodes[ind] );
1078 // }
1079 // }
1080
1081 // destroyPauliHamil(hamil);
1082 // }
1083 // SECTION( "input validation" ) {
1084
1085 // SECTION( "parameters" ) {
1086
1087 // // parameters checked before codes, so safe to leave un-initialised
1088 // qreal coeffs[1];
1089 // enum pauliOpType codes[1];
1090 // PauliHamil hamil;
1091
1092 // hamil.numQubits = GENERATE( -1, 0 );
1093 // hamil.numSumTerms = 1;
1094 // REQUIRE_THROWS_WITH( initPauliHamil(hamil, coeffs, codes), ContainsSubstring("number of qubits") && ContainsSubstring("strictly positive") );
1095
1096 // hamil.numQubits = 1;
1097 // hamil.numSumTerms = GENERATE( -1, 0 );
1098 // REQUIRE_THROWS_WITH( initPauliHamil(hamil, coeffs, codes), ContainsSubstring("terms") && ContainsSubstring("strictly positive") );
1099 // }
1100 // SECTION( "Pauli codes" ) {
1101
1102 // int numQb = 3;
1103 // int numTerms = 2;
1104 // int numCodes = numQb * numTerms;
1105 // VLA(qreal, coeffs, numTerms);
1106 // VLA(pauliOpType, codes, numCodes);
1107
1108 // // make only one code invalid
1109 // for (int i=0; i<numCodes; i++)
1110 // codes[i] = PAULI_I;
1111 // codes[GENERATE_COPY( range(0,numCodes) )] = (pauliOpType) GENERATE( -1, 4 );
1112
1113 // PauliHamil hamil = createPauliHamil(numQb, numTerms);
1114 // REQUIRE_THROWS_WITH( initPauliHamil(hamil, coeffs, codes), ContainsSubstring("Invalid Pauli code") );
1115 // destroyPauliHamil(hamil);
1116 // }
1117 // }
1118 // }
1119
1120
1121
1122// setDiagonalOpElems removed because DiagonalOp is deprecated
1123// in favour of FullStateDiagMatr, which has an initialiser
1124// setFullStateDiagMatr() with an incompatible signature
1125
1126 // /** @sa setDiagonalOpElems
1127 // * @ingroup deprecatedtests
1128 // * @author Tyson Jones
1129 // */
1130 // TEST_CASE( "setDiagonalOpElems", "[data_structures]" ) {
1131
1132 // // must be at least one amplitude per node
1133 // int minNumQb = calcLog2(getQuESTEnv().numNodes);
1134 // if (minNumQb == 0)
1135 // minNumQb = 1;
1136
1137 // // try 10 valid number of qubits (even for validation)
1138 // int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
1139 // DiagonalOp op = createDiagonalOp(numQb, getQuESTEnv());
1140
1141 // SECTION( "correctness" ) {
1142
1143 // // make entire array on every node
1144 // long long int len = (1LL << numQb);
1145 // VLA(qreal, reals, len);
1146 // VLA(qreal, imags, len);
1147 // long long int n;
1148 // for (n=0; n<len; n++) {
1149 // reals[n] = (qreal) n;
1150 // imags[n] = (qreal) -2*n; // (n - 2n i)
1151 // }
1152
1153 // // set one value at a time (only relevant nodes will update)
1154 // for (n=0; n<len; n++)
1155 // setDiagonalOpElems(op, n, &reals[n], &imags[n], 1);
1156
1157 // // check op.real and op.imag updated correctly
1158 // REQUIRE( areEqual(toQVector(op), reals, imags) );
1159
1160 // // no check that GPU values updated (occurs in initDiagonalOp)
1161 // }
1162 // SECTION( "input validation" ) {
1163
1164 // long long int maxInd = (1LL << numQb);
1165 // qreal *reals = NULL;
1166 // qreal *imags = NULL;
1167
1168 // SECTION( "start index" ) {
1169
1170 // int startInd = GENERATE_COPY( -1, maxInd );
1171 // int numAmps = 1;
1172 // REQUIRE_THROWS_WITH( setDiagonalOpElems(op, startInd, reals, imags, numAmps), ContainsSubstring("Invalid element index") );
1173 // }
1174
1175 // SECTION( "number of elements" ) {
1176
1177 // // independent
1178 // int startInd = 0;
1179 // int numAmps = GENERATE_COPY( -1, maxInd+1 );
1180 // REQUIRE_THROWS_WITH( setDiagonalOpElems(op, startInd, reals, imags, numAmps), ContainsSubstring("Invalid number of elements") );
1181
1182 // // invalid considering start-index
1183 // startInd = maxInd - 1;
1184 // numAmps = 2;
1185 // REQUIRE_THROWS_WITH( setDiagonalOpElems(op, startInd, reals, imags, numAmps), ContainsSubstring("More elements given than exist") );
1186 // }
1187 // }
1188
1189 // destroyDiagonalOp(op);
1190 // }
1191
1192
1193
1194/** @sa syncDiagonalOp
1195 * @ingroup deprecatedtests
1196 * @author Tyson Jones
1197 */
1198TEST_CASE( "syncDiagonalOp", "[data_structures]" ) {
1199
1200 // must be at least one amplitude per node
1201 int minNumQb = calcLog2(getQuESTEnv().numNodes);
1202 if (minNumQb == 0)
1203 minNumQb = 1;
1204
1205 SECTION( "correctness" ) {
1206
1207 // try 10 valid number of qubits
1208 int numQb = GENERATE_COPY( range(minNumQb, minNumQb+10) );
1209
1210 // createDiagonalOp() redirects to createFullStateDiagMatr()
1211 // which auto-deploys and will auto disable distribution,
1212 // so we resort to custom invocation
1213
1214 QuESTEnv env = getQuESTEnv();
1215 DiagonalOp op = createCustomFullStateDiagMatr(numQb, env.isDistributed, env.isGpuAccelerated, env.isMultithreaded);
1216
1217 // check that changes get sync'd to the GPU...
1218 long long int n;
1219 for (n=0; n<op.numElemsPerNode; n++)
1220 op.cpuElems[n] = qcomp(n, -2*n);
1221 syncDiagonalOp(op);
1222
1223 // via if it modifies an all-unity state-vec correctly
1224 Qureg qureg = createForcedQureg(numQb);
1225 for (long long int i=0; i<qureg.numAmpsPerNode; i++)
1226 qureg.cpuAmps[i] = qcomp(1,1);
1227 copyStateToGPU(qureg);
1228
1229 // (n - 2n i) * (1 + 1i) = 3n - n*i
1230 applyDiagonalOp(qureg, op);
1231 copyStateFromGPU(qureg);
1232 for (n=0; n<qureg.numAmpsPerNode; n++) {
1233 REQUIRE( real(qureg.cpuAmps[n]) == 3*n );
1234 REQUIRE( imag(qureg.cpuAmps[n]) == -n );
1235 }
1236
1237 destroyQureg(qureg, getQuESTEnv());
1238 destroyDiagonalOp(op, getQuESTEnv());
1239 }
1240}
TEST_CASE("createCloneQureg", "[data_structures]")
unsigned int calcLog2(long unsigned int res)
bool areEqual(QVector a, QVector b)
vector< vector< qcomp > > QMatrix
QMatrix toQMatrix(CompMatr1 src)
vector< qcomp > QVector
QVector toQVector(Qureg qureg)
QuESTEnv getQuESTEnv()
FullStateDiagMatr createCustomFullStateDiagMatr(int numQubits, int useDistrib, int useGpuAccel, int useMultithread)
Definition matrices.cpp:318
Qureg createForcedQureg(int numQubits)
Definition qureg.cpp:299
Qureg createForcedDensityQureg(int numQubits)
Definition qureg.cpp:309
Qureg createCloneQureg(Qureg qureg)
Definition qureg.cpp:319
void destroyQureg(Qureg qureg)
Definition qureg.cpp:334
qmatrix getZeroMatrix(size_t dim)
Definition qmatrix.cpp:18
Definition qureg.h:49