The Quantum Exact Simulation Toolkit v4.3.0
Loading...
Searching...
No Matches
initialisations.cpp
1/** @file
2 * Unit tests of the initialisations module.
3 *
4 * @author Tyson Jones
5 *
6 * @defgroup unitinit Initialisation
7 * @ingroup unittests
8 */
9
10#include "quest.h"
11
12#include <catch2/catch_test_macros.hpp>
13#include <catch2/matchers/catch_matchers_string.hpp>
14#include <catch2/generators/catch_generators_range.hpp>
15
16#include "tests/utils/qvector.hpp"
17#include "tests/utils/qmatrix.hpp"
18#include "tests/utils/cache.hpp"
19#include "tests/utils/compare.hpp"
20#include "tests/utils/convert.hpp"
21#include "tests/utils/evolve.hpp"
22#include "tests/utils/linalg.hpp"
23#include "tests/utils/lists.hpp"
24#include "tests/utils/macros.hpp"
25#include "tests/utils/measure.hpp"
26#include "tests/utils/random.hpp"
27
28using Catch::Matchers::ContainsSubstring;
29
30
31
32/*
33 * UTILITIES
34 */
35
36
37#define TEST_CATEGORY \
38 LABEL_UNIT_TAG "[initialisations]"
39
40
41void TEST_ON_CACHED_QUREGS(quregCache quregs, auto testFunc) {
42
43 for (auto& [label, qureg]: quregs) {
44
45 DYNAMIC_SECTION( label ) {
46
47 testFunc(qureg);
48 }
49 }
50}
51
52
53void TEST_ON_CACHED_QUREGS(quregCache quregs, auto apiFunc, auto refState) {
54
55 // assumes refState is already initialised
56
57 auto testFunc = [&](Qureg qureg) {
58 apiFunc(qureg);
59 REQUIRE_AGREE( qureg, refState );
60 };
61
62 TEST_ON_CACHED_QUREGS(quregs, testFunc);
63}
64
65
66
67/**
68 * TESTS
69 *
70 * @ingroup unitinit
71 * @{
72 */
73
74
75TEST_CASE( "initBlankState", TEST_CATEGORY ) {
76
77 SECTION( LABEL_CORRECTNESS ) {
78
79 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), initBlankState, getRefStatevec()); }
80 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), initBlankState, getRefDensmatr()); }
81 }
82
83 /// @todo input validation
84}
85
86
87TEST_CASE( "initZeroState", TEST_CATEGORY ) {
88
89 SECTION( LABEL_CORRECTNESS ) {
90
91 qvector refVec = getRefStatevec(); refVec[0] = 1; // |0> = {1, 0...}
92 qmatrix refMat = getRefDensmatr(); refMat[0][0] = 1; // |0><0| = {{1,0...},{0...}...}
93
94 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), initZeroState, refVec); }
95 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), initZeroState, refMat); }
96 }
97
98 /// @todo input validation
99}
100
101
102TEST_CASE( "initPlusState", TEST_CATEGORY ) {
103
104 SECTION( LABEL_CORRECTNESS ) {
105
106 int numQubits = getNumCachedQubits();
107 qreal vecElem = 1. / std::sqrt(getPow2(numQubits));
108 qreal matElem = 1. / getPow2(numQubits);
109
110 qvector refVec = getConstantVector(getPow2(numQubits), vecElem); // |+> = 1/sqrt(2^N) {1, ...}
111 qmatrix refMat = getConstantMatrix(getPow2(numQubits), matElem); // |+><+| = 1/2^N {{1, ...}, ...}
112
113 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), initPlusState, refVec); }
114 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), initPlusState, refMat); }
115 }
116
117 /// @todo input validation
118}
119
120
121TEST_CASE( "initClassicalState", TEST_CATEGORY ) {
122
123 SECTION( LABEL_CORRECTNESS ) {
124
125 int numQubits = getNumCachedQubits();
126 int numInds = (int) getPow2(numQubits);
127 int stateInd = GENERATE_COPY( range(0,numInds) );
128
129 qvector refVec = getRefStatevec(); refVec[stateInd] = 1; // |i> = {0, ..., 1, 0, ...}
130 qmatrix refMat = getRefDensmatr(); refMat[stateInd][stateInd] = 1; // |i><i|
131
132 auto apiFunc = [&](Qureg qureg) { initClassicalState(qureg, stateInd); };
133
134 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), apiFunc, refVec); }
135 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), apiFunc, refMat); }
136 }
137
138 /// @todo input validation
139}
140
141
142TEST_CASE( "initDebugState", TEST_CATEGORY ) {
143
144 SECTION( LABEL_CORRECTNESS ) {
145
146 qvector refVec = getRefStatevec(); setToDebugState(refVec); // |debug>
147 qmatrix refMat = getRefDensmatr(); setToDebugState(refMat); // ||debug>>
148
149 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), initDebugState, refVec); }
150 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), initDebugState, refMat); }
151 }
152
153 /// @todo input validation
154}
155
156
157TEST_CASE( "initRandomPureState", TEST_CATEGORY ) {
158
159 SECTION( LABEL_CORRECTNESS ) {
160
161 // this test does not use reference states
162 GENERATE( range(0,10) );
163
164 auto testFunc = [&](Qureg qureg) {
165
166 initRandomPureState(qureg);
167
168 /// @todo
169 /// these not-all-same-amp checks can be made much more rigorous,
170 /// by e.g. asserting distinct nodes haven't generated all the same
171 /// amplitudes (we currently observe this by eye)
172 syncQuregFromGpu(qureg);
173 REQUIRE( qureg.cpuAmps[0] != qureg.cpuAmps[1] );
174
175 qreal prob = calcTotalProb(qureg);
176 REQUIRE_AGREE( prob, 1 );
177
178 qreal purity = calcPurity(qureg);
179 REQUIRE_AGREE( purity, 1 );
180 };
181
182 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), testFunc); }
183 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), testFunc); }
184 }
185
186 /// @todo input validation
187}
188
189
190TEST_CASE( "initRandomMixedState", TEST_CATEGORY ) {
191
192 SECTION( LABEL_CORRECTNESS ) {
193
194 // this test does not use reference states
195
196 GENERATE( range(0,10) );
197 int numPureStates = GENERATE( 1, 2, 10 );
198
199 auto testFunc = [&](Qureg qureg) {
200
201 initRandomMixedState(qureg, numPureStates);
202
203 /// @todo
204 /// these not-all-same-amp checks can be made much more rigorous,
205 /// by e.g. asserting distinct nodes haven't generated all the same
206 /// amplitudes (we currently observe this by eye)
207 syncQuregFromGpu(qureg);
208 REQUIRE( qureg.cpuAmps[0] != qureg.cpuAmps[1] ); // performed on all nodes
209
210 qreal prob = calcTotalProb(qureg);
211 REQUIRE_AGREE( prob, 1 );
212
213 qreal purity = calcPurity(qureg);
214 if (numPureStates == 1)
215 REQUIRE_AGREE( purity, 1 );
216 else
217 REQUIRE( purity < 1 );
218 };
219
220 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), testFunc); }
221 }
222
223 /// @todo input validation
224}
225
226
227TEST_CASE( "initArbitraryPureState", TEST_CATEGORY ) {
228
229 SECTION( LABEL_CORRECTNESS ) {
230
231 // works for unnormalised states
232 qvector refVec = getRandomVector(getPow2(getNumCachedQubits()));
233 qmatrix refMat = getOuterProduct(refVec, refVec);
234
235 auto apiFunc = [&](Qureg qureg) { initArbitraryPureState(qureg, refVec.data()); };
236
237 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), apiFunc, refVec); }
238 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), apiFunc, refMat); }
239 }
240
241 /// @todo input validation
242}
243
244
245
246
247TEST_CASE( "setQuregAmps", TEST_CATEGORY ) {
248
249 SECTION( LABEL_CORRECTNESS ) {
250
251 int numTotalAmps = getPow2(getNumCachedQubits());
252 int numSetAmps = GENERATE_COPY( range(0,numTotalAmps+1) );
253
254 // Bounds-checking causes GENERATE_COPY( range(0,0) ) to fail
255 // when tests are compiled in Debug
256 int startInd = 0;
257 if (numTotalAmps - numSetAmps > 0) {
258 startInd = GENERATE_COPY( range(0,numTotalAmps-numSetAmps) );
259 }
260
261 qvector amps = getRandomVector(numSetAmps);
262
263 auto testFunc = [&](Qureg qureg) {
264
265 // initialise qureg randomly
266 qvector refVec = getRandomVector(numTotalAmps);
267 setQuregToReference(qureg, refVec);
268
269 // modify only subset of refVec amps and qureg...
270 setSubVector(refVec, amps, startInd);
271 setQuregAmps(qureg, startInd, amps.data(), numSetAmps);
272
273 // so that we simultaneously check targeted amps
274 // are modified while non-targeted are not
275 REQUIRE_AGREE( qureg, refVec );
276 };
277
278 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), testFunc); }
279 }
280
281 /// @todo input validation
282}
283
284
285TEST_CASE( "setDensityQuregFlatAmps", TEST_CATEGORY ) {
286
287 SECTION( LABEL_CORRECTNESS ) {
288
289 int numTotalRows = getPow2(getNumCachedQubits());
290 int numTotalAmps = numTotalRows * numTotalRows;
291
292 // systematic iteration is WAY too slow
293 GENERATE( range(0,1000) );
294 int numSetAmps = getRandomInt(0, numTotalAmps + 1);
295 int startInd = getRandomInt(0, numTotalAmps - numSetAmps);
296 qvector amps = getRandomVector(numSetAmps);
297
298 auto testFunc = [&](Qureg qureg) {
299
300 // initialise qureg randomly
301 qmatrix refMat = getRandomMatrix(numTotalRows);
302 setQuregToReference(qureg, refMat);
303
304 // overwrite a contiguous region of row-major refMat, column-wise
305 refMat = getTranspose(refMat);
306 setSubMatrix(refMat, amps, startInd);
307 refMat = getTranspose(refMat);
308
309 // modify the same contiguous region of column-major qureg
310 setDensityQuregFlatAmps(qureg, startInd, amps.data(), numSetAmps);
311
312 // check that both targeted and non-targeted amps agree
313 REQUIRE_AGREE( qureg, refMat );
314 };
315
316 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), testFunc); }
317 }
318
319 /// @todo input validation
320}
321
322
323TEST_CASE( "setDensityQuregAmps", TEST_CATEGORY ) {
324
325 SECTION( LABEL_CORRECTNESS ) {
326
327 int numTotalRowsCols = getPow2(getNumCachedQubits());
328
329 // systematic iteration is WAY too slow
330 GENERATE( range(0,1000) );
331 int numSetRows = getRandomInt(1, numTotalRowsCols+1);
332 int numSetCols = getRandomInt(1, numTotalRowsCols+1);
333 int startRow = getRandomInt(0, numTotalRowsCols - numSetRows);
334 int startCol = getRandomInt(0, numTotalRowsCols - numSetCols);
335
336 // caution that amps is 'qmatrix' despite not being square
337 qmatrix amps = getRandomNonSquareMatrix(numSetRows, numSetCols);
338
339 auto testFunc = [&](Qureg qureg) {
340
341 // initialise qureg randomly
342 qmatrix refMat = getRandomMatrix(numTotalRowsCols);
343 setQuregToReference(qureg, refMat);
344
345 // API needs nested pointers
346 std::vector<qcomp*> rowPtrs(numSetRows);
347 for (size_t r=0; r<numSetRows; r++)
348 rowPtrs[r] = amps[r].data();
349
350 // overwrite a sub-matrix of refMat and Qureg
351 setSubMatrix(refMat, amps, startRow, startCol);
352 setDensityQuregAmps(qureg, startRow, startCol, rowPtrs.data(), numSetRows, numSetCols);
353
354 // check that both targeted and non-targeted amps agree
355 REQUIRE_AGREE( qureg, refMat );
356 };
357
358 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), testFunc); }
359
360 }
361
362 /// @todo input validation
363}
364
365
366TEST_CASE( "setQuregToRenormalized", TEST_CATEGORY ) {
367
368 SECTION( LABEL_CORRECTNESS ) {
369
370 GENERATE( range(0,10) );
371 qindex dim = getPow2(getNumCachedQubits());
372 qvector refVec = getRandomVector(dim);
373 qmatrix refMat = getRandomMatrix(dim);
374
375 // eliminate random chance of tr(refMat)=0, triggering validation
376 if (doScalarsAgree(getTrace(refMat), 0))
377 refMat[0][0] += 1/(qreal) dim;
378
379 // [=] stores current (pre-normalised) reference objects
380 auto funcVec = [=](Qureg qureg) { setQuregToReference(qureg, refVec); setQuregToRenormalized(qureg); };
381 auto funcMat = [=](Qureg qureg) { setQuregToReference(qureg, refMat); setQuregToRenormalized(qureg); };
382
383 // setQuregToRenormalized() makes statevectors become valid
384 refVec = getNormalised(refVec);
385
386 // but it only divides density matrices by the sum of the real-elems of their diagonals
387 refMat /= getReferenceProbability(refMat);
388
389 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), funcVec, refVec); }
390 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), funcMat, refMat); }
391 }
392
393 /// @todo input validation
394}
395
396
397TEST_CASE( "setQuregToPauliStrSum", TEST_CATEGORY ) {
398
399 SECTION( LABEL_CORRECTNESS ) {
400
401 GENERATE( range(0,10) );
402 int numQubits = getNumCachedQubits();
403 int numTerms = GENERATE_COPY( 1, numQubits, getPow2(2*numQubits) );
404 PauliStrSum sum = createRandomPauliStrSum(numQubits, numTerms);
405 qmatrix refMat = getMatrix(sum, numQubits);
406
407 auto apiFunc = [&](Qureg qureg) { setQuregToPauliStrSum(qureg, sum); };
408
409 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), apiFunc, refMat); }
410 }
411
412 /// @todo input validation
413}
414
415
416TEST_CASE( "setQuregToWeightedSum", TEST_CATEGORY ) {
417
418 SECTION( LABEL_CORRECTNESS ) {
419
420 // @todo
421 // below, we test when every inQureg is unqiue and distinct
422 // from the outQureg, and so do not test the valid scenarios of:
423 // - outQureg being among inQuregs
424 // - one or more inQuregs being repeated
425 // However, both CPU and GPU implementations are sufficiently
426 // trivial to validate by inspection (eep...)
427
428 qindex quregDim = getPow2(getNumCachedQubits());
429
430 // compile-time optimisations apply for <= 5
431 int numInQuregs = GENERATE( 1, 2, 3, 4, 5, 6, 20 );
432 CAPTURE( numInQuregs );
433
434 vector<qcomp> coeffs = getRandomVector(numInQuregs);
435
436 // we must pass identically-deployed inQureg as outQureg,
437 // which itself gets tested being each possible deployment,
438 // so we defer allocation of the inQuregs
439 vector<Qureg> inQuregs(numInQuregs);
440
441 // this function generates apiFunc in a way agnostic to
442 // whether outQureg is a statevector (inRefs are qvector)
443 // or a density matrix (inRefs are qmatrix)
444 auto apiFuncGen = [&](auto& inRefs) {
445
446 return [&](Qureg outQureg) {
447
448 // prepare input quregs
449 for (int i=0; i<numInQuregs; i++) {
450 inQuregs[i] = createCloneQureg(outQureg);
451 setQuregToReference(inQuregs[i], inRefs[i]);
452 }
453
454 // modify outQureg
455 setQuregToWeightedSum(outQureg, coeffs.data(), inQuregs.data(), numInQuregs);
456
457 // free input quregs
458 for (int i=0; i<numInQuregs; i++)
459 destroyQureg(inQuregs[i]);
460 };
461 };
462
463 SECTION( LABEL_STATEVEC ) {
464
465 // generate (unnormalised) input reference vectors
466 vector<qvector> inVecRefs(numInQuregs);
467 for (int i=0; i<numInQuregs; i++)
468 inVecRefs[i] = getRandomVector(quregDim);
469
470 // compute output reference vector
471 qvector outVecRef = getZeroVector(quregDim);
472 for (int i=0; i<numInQuregs; i++)
473 outVecRef += coeffs[i] * inVecRefs[i];
474
475 TEST_ON_CACHED_QUREGS(getCachedStatevecs(), apiFuncGen(inVecRefs), outVecRef);
476 }
477
478 SECTION( LABEL_DENSMATR ) {
479
480 // generate (unnormalised) input reference matrices
481 vector<qmatrix> inMatrRefs(numInQuregs);
482 for (int i=0; i<numInQuregs; i++)
483 inMatrRefs[i] = getRandomMatrix(quregDim);
484
485 // compute output reference matrix
486 qmatrix outMatrRef = getZeroMatrix(quregDim);
487 for (int i=0; i<numInQuregs; i++)
488 outMatrRef += coeffs[i] * inMatrRefs[i];
489
490 TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), apiFuncGen(inMatrRefs), outMatrRef);
491 }
492 }
493
494 SECTION( LABEL_VALIDATION ) {
495
496 Qureg qureg = getArbitraryCachedStatevec();
497
498 SECTION( "out qureg uninitialised" ) {
499
500 // spoof uninitialised value to be sure
501 Qureg badQureg;
502 badQureg.numQubits = -123;
503
504 REQUIRE_THROWS_WITH(
505 setQuregToWeightedSum(badQureg, nullptr, nullptr, 1),
506 ContainsSubstring("invalid Qureg") );
507 }
508
509 SECTION( "in qureg uninitialised" ) {
510
511 // set all inQureg to arbitrary existing qureg
512 int numIn = 5;
513 vector<Qureg> inQuregs(numIn, qureg);
514
515 // hide an uninitialised qureg among them
516 Qureg badQureg;
517 badQureg.numQubits = -123;
518 int badInd = GENERATE_COPY( range(0,numIn) );
519 inQuregs[badInd] = badQureg;
520
521 REQUIRE_THROWS_WITH(
522 setQuregToWeightedSum(qureg, nullptr, inQuregs.data(), numIn),
523 ContainsSubstring("invalid Qureg") );
524 }
525
526 SECTION( "invalid number of quregs" ) {
527
528 int numIn = GENERATE( -1, 0 );
529
530 REQUIRE_THROWS_WITH(
531 setQuregToWeightedSum(qureg, nullptr, nullptr, numIn),
532 ContainsSubstring("number of passed Quregs") && ContainsSubstring("is invalid") );
533 }
534
535 SECTION( "inconsistent qureg types" ) {
536
537 // must create new Quregs to ensure they are identically deployed
538 Qureg sv = createCustomQureg(getNumCachedQubits(), 0, 0,0,0);
539 Qureg dm = createCustomQureg(getNumCachedQubits(), 1, 0,0,0);
540
541 // set all inQureg to sv (as will be outQureg)
542 int numIn = 5;
543 vector<Qureg> inQuregs(numIn, sv);
544
545 // set one to dm
546 int badInd = GENERATE_COPY( range(0,numIn) );
547 inQuregs[badInd] = dm;
548
549 REQUIRE_THROWS_WITH(
550 setQuregToWeightedSum(sv, nullptr, inQuregs.data(), numIn),
551 ContainsSubstring("inconsistent attributes") );
552
553 destroyQureg(sv);
554 destroyQureg(dm);
555 }
556
557 SECTION( "inconsistent qureg sizes" ) {
558
559 // must create new Quregs to ensure they are identically deployed
560 Qureg quregA = createCustomQureg(getNumCachedQubits(), 0,0,0,0);
561 Qureg quregB = createCustomQureg(getNumCachedQubits() + 1, 0,0,0,0);
562
563 // set all inQureg to quregA (as will be outQureg)
564 int numIn = 5;
565 vector<Qureg> inQuregs(numIn, quregA);
566
567 // set one to quregB
568 int badInd = GENERATE_COPY( range(0,numIn) );
569 inQuregs[badInd] = quregB;
570
571 REQUIRE_THROWS_WITH(
572 setQuregToWeightedSum(quregA, nullptr, inQuregs.data(), numIn),
573 ContainsSubstring("inconsistent attributes") );
574
575 destroyQureg(quregA);
576 destroyQureg(quregB);
577 }
578
579 SECTION( "inconsistent qureg deployments" ) {
580
581 // we do not necessarily have differently-distributed/GPU Quregs at
582 // runtime, so we enumerate all deployments and test when they differ
583
584 for (auto& [label, badQureg]: getCachedStatevecs()) {
585
586 if ((badQureg.isGpuAccelerated == qureg.isGpuAccelerated) &&
587 (badQureg.isDistributed == qureg.isDistributed))
588 continue;
589
590 // set all inQureg to qureg (as will be outQureg)
591 int numIn = 5;
592 vector<Qureg> inQuregs(numIn, qureg);
593
594 // set one to badQureg
595 int badInd = GENERATE_COPY( range(0,numIn) );
596 inQuregs[badInd] = badQureg;
597
598 REQUIRE_THROWS_WITH(
599 setQuregToWeightedSum(qureg, nullptr, inQuregs.data(), numIn),
600 ContainsSubstring("inconsistent attributes") );
601 }
602
603 // automatically pass when there are no differing deployments
604 SUCCEED( );
605 }
606
607 SECTION( "different number of quregs and coeffs") {
608
609 // relevant only to the C++ overload
610
611 qcomp coeff = getQcomp(0,0);
612
613 REQUIRE_THROWS_WITH(
614 setQuregToWeightedSum(qureg, {coeff,coeff}, {qureg}),
615 ContainsSubstring("different number of coefficients") );
616 }
617 }
618}
619
620
621TEST_CASE( "setQuregToMixture", TEST_CATEGORY ) {
622
623 SECTION( LABEL_CORRECTNESS ) {
624
625 // @todo
626 // below, we test when every inQureg is unqiue and distinct
627 // from the outQureg, and so do not test the valid scenarios of:
628 // - outQureg being among inQuregs
629 // - one or more inQuregs being repeated
630 // However, both CPU and GPU implementations are sufficiently
631 // trivial to validate by inspection (eep...)
632
633 // compile-time optimisations apply for <= 5
634 int numInQuregs = GENERATE( 1, 2, 3, 4, 5, 6, 20 );
635 CAPTURE( numInQuregs );
636
637 vector<qreal> probs = getRandomProbabilities(numInQuregs);
638
639 // we must pass identically-deployed inQureg as outQureg,
640 // which itself gets tested being each possible deployment,
641 // so we defer allocation of the inQuregs
642 vector<Qureg> inQuregs(numInQuregs);
643
644 SECTION( LABEL_DENSMATR ) {
645
646 // generate (unnormalised) input reference matrices
647 vector<qmatrix> inMatrRefs(numInQuregs);
648 for (int i=0; i<numInQuregs; i++)
649 inMatrRefs[i] = getRandomMatrix(getPow2(getNumCachedQubits()));
650
651 // compute output reference matrix
652 qmatrix outMatrRef = getZeroMatrix(getPow2(getNumCachedQubits()));
653 for (int i=0; i<numInQuregs; i++)
654 outMatrRef += probs[i] * inMatrRefs[i];
655
656 auto apiFunc = [&](Qureg outQureg) {
657
658 // prepare input quregs
659 for (int i=0; i<numInQuregs; i++) {
660 inQuregs[i] = createCloneQureg(outQureg);
661 setQuregToReference(inQuregs[i], inMatrRefs[i]);
662 }
663
664 // modify outQureg
665 setQuregToMixture(outQureg, probs.data(), inQuregs.data(), numInQuregs);
666
667 // free input quregs
668 for (int i=0; i<numInQuregs; i++)
669 destroyQureg(inQuregs[i]);
670 };
671
672 TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), apiFunc, outMatrRef);
673 }
674 }
675
676 SECTION( LABEL_VALIDATION ) {
677
678 // arbitrary existing qureg
679 Qureg qureg = getCachedDensmatrs().begin()->second;
680
681 SECTION( "out qureg uninitialised" ) {
682
683 // spoof uninitialised value to be sure
684 Qureg badQureg;
685 badQureg.numQubits = -123;
686
687 REQUIRE_THROWS_WITH(
688 setQuregToMixture(badQureg, nullptr, nullptr, 1),
689 ContainsSubstring("invalid Qureg") );
690 }
691
692 SECTION( "in qureg uninitialised" ) {
693
694 // set all inQureg to arbitrary existing qureg
695 int numIn = 5;
696 vector<Qureg> inQuregs(numIn, qureg);
697
698 // hide an unitialised qureg among them
699 Qureg badQureg;
700 badQureg.numQubits = -123;
701 int badInd = GENERATE_COPY( range(0,numIn) );
702 inQuregs[badInd] = badQureg;
703
704 REQUIRE_THROWS_WITH(
705 setQuregToMixture(qureg, nullptr, inQuregs.data(), numIn),
706 ContainsSubstring("invalid Qureg") );
707 }
708
709 SECTION( "out qureg is statevector" ) {
710
711 Qureg badQureg = getArbitraryCachedStatevec();
712
713 REQUIRE_THROWS_WITH(
714 setQuregToMixture(badQureg, nullptr, nullptr, 1),
715 ContainsSubstring("received a statevector") );
716 }
717
718 SECTION( "in qureg is statevector" ) {
719
720 // set all inQureg to arbitrary existing density matrix
721 int numIn = 5;
722 vector<Qureg> inQuregs(numIn, qureg);
723
724 // hide a statevector among them
725 int badInd = GENERATE_COPY( range(0,numIn) );
726 inQuregs[badInd] = getArbitraryCachedStatevec();
727
728 REQUIRE_THROWS_WITH(
729 setQuregToMixture(qureg, nullptr, inQuregs.data(), numIn),
730 ContainsSubstring("One or more Quregs were statevectors") );
731 }
732
733 SECTION( "number of quregs" ) {
734
735 int numIn = GENERATE( -1, 0 );
736
737 REQUIRE_THROWS_WITH(
738 setQuregToMixture(qureg, nullptr, nullptr, numIn),
739 ContainsSubstring("number of passed Quregs") && ContainsSubstring("is invalid") );
740 }
741
742 SECTION( "inconsistent qureg sizes" ) {
743
744 // must create new Quregs to ensure they are identically deployed
745 Qureg quregA = createCustomQureg(getNumCachedQubits(), 1,0,0,0);
746 Qureg quregB = createCustomQureg(getNumCachedQubits() + 1, 1,0,0,0);
747
748 // set all inQureg to quregA (as will be outQureg)
749 int numIn = 5;
750 vector<Qureg> inQuregs(numIn, quregA);
751
752 // set one to quregB
753 int badInd = GENERATE_COPY( range(0,numIn) );
754 inQuregs[badInd] = quregB;
755
756 REQUIRE_THROWS_WITH(
757 setQuregToMixture(quregA, nullptr, inQuregs.data(), numIn),
758 ContainsSubstring("inconsistent attributes") );
759
760 destroyQureg(quregA);
761 destroyQureg(quregB);
762 }
763
764 SECTION( "inconsistent qureg deployments" ) {
765
766 // we do not necessarily have differently-distributed/GPU Quregs at
767 // runtime, so we enumerate all deployments and test when they differ
768
769 for (auto& [label, badQureg]: getCachedDensmatrs()) {
770
771 if ((badQureg.isGpuAccelerated == qureg.isGpuAccelerated) &&
772 (badQureg.isDistributed == qureg.isDistributed))
773 continue;
774
775 // set all inQureg to qureg (as will be outQureg)
776 int numIn = 5;
777 vector<Qureg> inQuregs(numIn, qureg);
778
779 // set one to badQureg
780 int badInd = GENERATE_COPY( range(0,numIn) );
781 inQuregs[badInd] = badQureg;
782
783 REQUIRE_THROWS_WITH(
784 setQuregToMixture(qureg, nullptr, inQuregs.data(), numIn),
785 ContainsSubstring("inconsistent attributes") );
786 }
787
788 // automatically pass when there are no differing deployments
789 SUCCEED( );
790 }
791
792 SECTION( "invalid probs" ) {
793
794 // set all inQureg to arbitrary existing density matrix
795 int numIn = 5;
796 vector<Qureg> inQuregs(numIn, qureg);
797
798 // get valid probabilities then mess one up
799 int badInd = GENERATE_COPY( range(0,numIn) );
800 vector<qreal> probs = getRandomProbabilities(numIn);
801 probs[badInd] = GENERATE( -1., -0.1, 1.1, 2. );
802
803 REQUIRE_THROWS_WITH(
804 setQuregToMixture(qureg, probs.data(), inQuregs.data(), numIn),
805 ContainsSubstring("One or more given probabilities are invalid") );
806 }
807
808 SECTION( "unnormalised probs" ) {
809
810 // set all inQureg to arbitrary existing density matrix
811 int numIn = 5;
812 vector<Qureg> inQuregs(numIn, qureg);
813
814 // these illegal non-unity values assume eps < 0.1
815 qreal probSum = GENERATE( 0.9, 1.1 );
816 vector<qreal> probs(numIn, probSum / numIn);
817
818 REQUIRE_THROWS_WITH(
819 setQuregToMixture(qureg, probs.data(), inQuregs.data(), numIn),
820 ContainsSubstring("probabilities do not sum to") && ContainsSubstring("one") );
821 }
822
823 SECTION( "different number of quregs and probs") {
824
825 // relevant only to the C++ overload
826
827 qreal prob = 0;
828
829 REQUIRE_THROWS_WITH(
830 setQuregToMixture(qureg, {prob,prob}, {qureg}),
831 ContainsSubstring("different number of probabilities") );
832 }
833 }
834}
835
836
837/** @} (end defgroup) */
838
839
840
841/**
842 * @todo
843 * UNTESTED FUNCTIONS
844 */
845
846// these require we deploy each passed Qureg distinctly
847// to thoroughly test all QuEST control flows, for which
848// we do not yet have the macros/scaffolding
849
850void initPureState(Qureg qureg, Qureg pure);
851
852void setQuregToClone(Qureg targetQureg, Qureg copyQureg);
853
854void setQuregToPartialTrace(Qureg out, Qureg in, int* traceOutQubits, int numTraceQubits);
855
856void setQuregToReducedDensityMatrix(Qureg out, Qureg in, int* retainQubits, int numRetainQubits);
qreal calcPurity(Qureg qureg)
qreal calcTotalProb(Qureg qureg)
void setDensityQuregFlatAmps(Qureg qureg, qindex startInd, qcomp *amps, qindex numAmps)
void setQuregToReducedDensityMatrix(Qureg out, Qureg in, int *retainQubits, int numRetainQubits)
void setQuregToWeightedSum(Qureg out, qcomp *coeffs, Qureg *in, int numIn)
void setQuregToPauliStrSum(Qureg qureg, PauliStrSum sum)
void setQuregToClone(Qureg targetQureg, Qureg copyQureg)
void setQuregToMixture(Qureg out, qreal *probs, Qureg *in, int numIn)
void setQuregAmps(Qureg qureg, qindex startInd, qcomp *amps, qindex numAmps)
void setQuregToPartialTrace(Qureg out, Qureg in, int *traceOutQubits, int numTraceQubits)
qreal setQuregToRenormalized(Qureg qureg)
void setDensityQuregAmps(Qureg qureg, qindex startRow, qindex startCol, qcomp **amps, qindex numRows, qindex numCols)
void initArbitraryPureState(Qureg qureg, qcomp *amps)
void initRandomPureState(Qureg qureg)
void initPlusState(Qureg qureg)
void initZeroState(Qureg qureg)
void initPureState(Qureg qureg, Qureg pure)
void initDebugState(Qureg qureg)
void initRandomMixedState(Qureg qureg, qindex numPureStates)
void initClassicalState(Qureg qureg, qindex stateInd)
void initBlankState(Qureg qureg)
Qureg createCloneQureg(Qureg qureg)
Definition qureg.cpp:325
Qureg createCustomQureg(int numQubits, int isDensMatr, int useDistrib, int useGpuAccel, int useMultithread)
Definition qureg.cpp:283
void destroyQureg(Qureg qureg)
Definition qureg.cpp:340
void syncQuregFromGpu(Qureg qureg)
Definition qureg.cpp:416
void setSubMatrix(qmatrix &dest, qmatrix sub, size_t r, size_t c)
Definition qmatrix.cpp:203
qmatrix getZeroMatrix(size_t dim)
Definition qmatrix.cpp:18
vector< qreal > getRandomProbabilities(int numProbs)
Definition random.cpp:160
int getRandomInt(int min, int maxExcl)
Definition random.cpp:90
static qcomp getQcomp(qreal re, qreal im)
Definition types.h:91
TEST_CASE("initBlankState", TEST_CATEGORY)
Definition qureg.h:49