The Quantum Exact Simulation Toolkit v4.2.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 int startInd = GENERATE_COPY( range(0,numTotalAmps-numSetAmps) );
254 qvector amps = getRandomVector(numSetAmps);
255
256 auto testFunc = [&](Qureg qureg) {
257
258 // initialise qureg randomly
259 qvector refVec = getRandomVector(numTotalAmps);
260 setQuregToReference(qureg, refVec);
261
262 // modify only subset of refVec amps and qureg...
263 setSubVector(refVec, amps, startInd);
264 setQuregAmps(qureg, startInd, amps.data(), numSetAmps);
265
266 // so that we simultaneously check targeted amps
267 // are modified while non-targeted are not
268 REQUIRE_AGREE( qureg, refVec );
269 };
270
271 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), testFunc); }
272 }
273
274 /// @todo input validation
275}
276
277
278TEST_CASE( "setDensityQuregFlatAmps", TEST_CATEGORY ) {
279
280 SECTION( LABEL_CORRECTNESS ) {
281
282 int numTotalRows = getPow2(getNumCachedQubits());
283 int numTotalAmps = numTotalRows * numTotalRows;
284
285 // systematic iteration is WAY too slow
286 GENERATE( range(0,1000) );
287 int numSetAmps = getRandomInt(0, numTotalAmps + 1);
288 int startInd = getRandomInt(0, numTotalAmps - numSetAmps);
289 qvector amps = getRandomVector(numSetAmps);
290
291 auto testFunc = [&](Qureg qureg) {
292
293 // initialise qureg randomly
294 qmatrix refMat = getRandomMatrix(numTotalRows);
295 setQuregToReference(qureg, refMat);
296
297 // overwrite a contiguous region of row-major refMat, column-wise
298 refMat = getTranspose(refMat);
299 setSubMatrix(refMat, amps, startInd);
300 refMat = getTranspose(refMat);
301
302 // modify the same contiguous region of column-major qureg
303 setDensityQuregFlatAmps(qureg, startInd, amps.data(), numSetAmps);
304
305 // check that both targeted and non-targeted amps agree
306 REQUIRE_AGREE( qureg, refMat );
307 };
308
309 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), testFunc); }
310 }
311
312 /// @todo input validation
313}
314
315
316TEST_CASE( "setDensityQuregAmps", TEST_CATEGORY ) {
317
318 SECTION( LABEL_CORRECTNESS ) {
319
320 int numTotalRowsCols = getPow2(getNumCachedQubits());
321
322 // systematic iteration is WAY too slow
323 GENERATE( range(0,1000) );
324 int numSetRows = getRandomInt(1, numTotalRowsCols+1);
325 int numSetCols = getRandomInt(1, numTotalRowsCols+1);
326 int startRow = getRandomInt(0, numTotalRowsCols - numSetRows);
327 int startCol = getRandomInt(0, numTotalRowsCols - numSetCols);
328
329 // caution that amps is 'qmatrix' despite not being square
330 qmatrix amps = getRandomNonSquareMatrix(numSetRows, numSetCols);
331
332 auto testFunc = [&](Qureg qureg) {
333
334 // initialise qureg randomly
335 qmatrix refMat = getRandomMatrix(numTotalRowsCols);
336 setQuregToReference(qureg, refMat);
337
338 // API needs nested pointers
339 std::vector<qcomp*> rowPtrs(numSetRows);
340 for (size_t r=0; r<numSetRows; r++)
341 rowPtrs[r] = amps[r].data();
342
343 // overwrite a sub-matrix of refMat and Qureg
344 setSubMatrix(refMat, amps, startRow, startCol);
345 setDensityQuregAmps(qureg, startRow, startCol, rowPtrs.data(), numSetRows, numSetCols);
346
347 // check that both targeted and non-targeted amps agree
348 REQUIRE_AGREE( qureg, refMat );
349 };
350
351 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), testFunc); }
352
353 }
354
355 /// @todo input validation
356}
357
358
359TEST_CASE( "setQuregToRenormalized", TEST_CATEGORY ) {
360
361 SECTION( LABEL_CORRECTNESS ) {
362
363 GENERATE( range(0,10) );
364 qindex dim = getPow2(getNumCachedQubits());
365 qvector refVec = getRandomVector(dim);
366 qmatrix refMat = getRandomMatrix(dim);
367
368 // eliminate random chance of tr(refMat)=0, triggering validation
369 if (doScalarsAgree(getTrace(refMat), 0))
370 refMat[0][0] += 1/(qreal) dim;
371
372 // [=] stores current (pre-normalised) reference objects
373 auto funcVec = [=](Qureg qureg) { setQuregToReference(qureg, refVec); setQuregToRenormalized(qureg); };
374 auto funcMat = [=](Qureg qureg) { setQuregToReference(qureg, refMat); setQuregToRenormalized(qureg); };
375
376 // setQuregToRenormalized() makes statevectors become valid
377 refVec = getNormalised(refVec);
378
379 // but it only divides density matrices by the sum of the real-elems of their diagonals
380 refMat /= getReferenceProbability(refMat);
381
382 SECTION( LABEL_STATEVEC ) { TEST_ON_CACHED_QUREGS(getCachedStatevecs(), funcVec, refVec); }
383 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), funcMat, refMat); }
384 }
385
386 /// @todo input validation
387}
388
389
390TEST_CASE( "setQuregToPauliStrSum", TEST_CATEGORY ) {
391
392 SECTION( LABEL_CORRECTNESS ) {
393
394 GENERATE( range(0,10) );
395 int numQubits = getNumCachedQubits();
396 int numTerms = GENERATE_COPY( 1, numQubits, getPow2(2*numQubits) );
397 PauliStrSum sum = createRandomPauliStrSum(numQubits, numTerms);
398 qmatrix refMat = getMatrix(sum, numQubits);
399
400 auto apiFunc = [&](Qureg qureg) { setQuregToPauliStrSum(qureg, sum); };
401
402 SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), apiFunc, refMat); }
403 }
404
405 /// @todo input validation
406}
407
408
409TEST_CASE( "setQuregToWeightedSum", TEST_CATEGORY ) {
410
411 SECTION( LABEL_CORRECTNESS ) {
412
413 // @todo
414 // below, we test when every inQureg is unqiue and distinct
415 // from the outQureg, and so do not test the valid scenarios of:
416 // - outQureg being among inQuregs
417 // - one or more inQuregs being repeated
418 // However, both CPU and GPU implementations are sufficiently
419 // trivial to validate by inspection (eep...)
420
421 qindex quregDim = getPow2(getNumCachedQubits());
422
423 // compile-time optimisations apply for <= 5
424 int numInQuregs = GENERATE( 1, 2, 3, 4, 5, 6, 20 );
425 CAPTURE( numInQuregs );
426
427 vector<qcomp> coeffs = getRandomVector(numInQuregs);
428
429 // we must pass identically-deployed inQureg as outQureg,
430 // which itself gets tested being each possible deployment,
431 // so we defer allocation of the inQuregs
432 vector<Qureg> inQuregs(numInQuregs);
433
434 // this function generates apiFunc in a way agnostic to
435 // whether outQureg is a statevector (inRefs are qvector)
436 // or a density matrix (inRefs are qmatrix)
437 auto apiFuncGen = [&](auto& inRefs) {
438
439 return [&](Qureg outQureg) {
440
441 // prepare input quregs
442 for (int i=0; i<numInQuregs; i++) {
443 inQuregs[i] = createCloneQureg(outQureg);
444 setQuregToReference(inQuregs[i], inRefs[i]);
445 }
446
447 // modify outQureg
448 setQuregToWeightedSum(outQureg, coeffs.data(), inQuregs.data(), numInQuregs);
449
450 // free input quregs
451 for (int i=0; i<numInQuregs; i++)
452 destroyQureg(inQuregs[i]);
453 };
454 };
455
456 SECTION( LABEL_STATEVEC ) {
457
458 // generate (unnormalised) input reference vectors
459 vector<qvector> inVecRefs(numInQuregs);
460 for (int i=0; i<numInQuregs; i++)
461 inVecRefs[i] = getRandomVector(quregDim);
462
463 // compute output reference vector
464 qvector outVecRef = getZeroVector(quregDim);
465 for (int i=0; i<numInQuregs; i++)
466 outVecRef += coeffs[i] * inVecRefs[i];
467
468 TEST_ON_CACHED_QUREGS(getCachedStatevecs(), apiFuncGen(inVecRefs), outVecRef);
469 }
470
471 SECTION( LABEL_DENSMATR ) {
472
473 // generate (unnormalised) input reference matrices
474 vector<qmatrix> inMatrRefs(numInQuregs);
475 for (int i=0; i<numInQuregs; i++)
476 inMatrRefs[i] = getRandomMatrix(quregDim);
477
478 // compute output reference matrix
479 qmatrix outMatrRef = getZeroMatrix(quregDim);
480 for (int i=0; i<numInQuregs; i++)
481 outMatrRef += coeffs[i] * inMatrRefs[i];
482
483 TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), apiFuncGen(inMatrRefs), outMatrRef);
484 }
485 }
486
487 SECTION( LABEL_VALIDATION ) {
488
489 // arbitrary existing qureg
490 Qureg qureg = getCachedStatevecs().begin()->second;
491
492 SECTION( "out qureg uninitialised" ) {
493
494 // spoof uninitialised value to be sure
495 Qureg badQureg;
496 badQureg.numQubits = -123;
497
498 REQUIRE_THROWS_WITH(
499 setQuregToWeightedSum(badQureg, nullptr, nullptr, 1),
500 ContainsSubstring("invalid Qureg") );
501 }
502
503 SECTION( "in qureg uninitialised" ) {
504
505 // set all inQureg to arbitrary existing qureg
506 int numIn = 5;
507 vector<Qureg> inQuregs(numIn, qureg);
508
509 // hide an uninitialised qureg among them
510 Qureg badQureg;
511 badQureg.numQubits = -123;
512 int badInd = GENERATE_COPY( range(0,numIn) );
513 inQuregs[badInd] = badQureg;
514
515 REQUIRE_THROWS_WITH(
516 setQuregToWeightedSum(qureg, nullptr, inQuregs.data(), numIn),
517 ContainsSubstring("invalid Qureg") );
518 }
519
520 SECTION( "invalid number of quregs" ) {
521
522 int numIn = GENERATE( -1, 0 );
523
524 REQUIRE_THROWS_WITH(
525 setQuregToWeightedSum(qureg, nullptr, nullptr, numIn),
526 ContainsSubstring("number of passed Quregs") && ContainsSubstring("is invalid") );
527 }
528
529 SECTION( "inconsistent qureg types" ) {
530
531 // must create new Quregs to ensure they are identically deployed
532 Qureg sv = createCustomQureg(getNumCachedQubits(), 0, 0,0,0);
533 Qureg dm = createCustomQureg(getNumCachedQubits(), 1, 0,0,0);
534
535 // set all inQureg to sv (as will be outQureg)
536 int numIn = 5;
537 vector<Qureg> inQuregs(numIn, sv);
538
539 // set one to dm
540 int badInd = GENERATE_COPY( range(0,numIn) );
541 inQuregs[badInd] = dm;
542
543 REQUIRE_THROWS_WITH(
544 setQuregToWeightedSum(sv, nullptr, inQuregs.data(), numIn),
545 ContainsSubstring("inconsistent attributes") );
546
547 destroyQureg(sv);
548 destroyQureg(dm);
549 }
550
551 SECTION( "inconsistent qureg sizes" ) {
552
553 // must create new Quregs to ensure they are identically deployed
554 Qureg quregA = createCustomQureg(getNumCachedQubits(), 0,0,0,0);
555 Qureg quregB = createCustomQureg(getNumCachedQubits() + 1, 0,0,0,0);
556
557 // set all inQureg to quregA (as will be outQureg)
558 int numIn = 5;
559 vector<Qureg> inQuregs(numIn, quregA);
560
561 // set one to quregB
562 int badInd = GENERATE_COPY( range(0,numIn) );
563 inQuregs[badInd] = quregB;
564
565 REQUIRE_THROWS_WITH(
566 setQuregToWeightedSum(quregA, nullptr, inQuregs.data(), numIn),
567 ContainsSubstring("inconsistent attributes") );
568
569 destroyQureg(quregA);
570 destroyQureg(quregB);
571 }
572
573 SECTION( "inconsistent qureg deployments" ) {
574
575 // we do not necessarily have differently-distributed/GPU Quregs at
576 // runtime, so we enumerate all deployments and test when they differ
577
578 for (auto& [label, badQureg]: getCachedStatevecs()) {
579
580 if ((badQureg.isGpuAccelerated == qureg.isGpuAccelerated) &&
581 (badQureg.isDistributed == qureg.isDistributed))
582 continue;
583
584 // set all inQureg to qureg (as will be outQureg)
585 int numIn = 5;
586 vector<Qureg> inQuregs(numIn, qureg);
587
588 // set one to badQureg
589 int badInd = GENERATE_COPY( range(0,numIn) );
590 inQuregs[badInd] = badQureg;
591
592 REQUIRE_THROWS_WITH(
593 setQuregToWeightedSum(qureg, nullptr, inQuregs.data(), numIn),
594 ContainsSubstring("inconsistent attributes") );
595 }
596
597 // automatically pass when there are no differing deployments
598 SUCCEED( );
599 }
600
601 SECTION( "different number of quregs and coeffs") {
602
603 // relevant only to the C++ overload
604
605 qcomp coeff = getQcomp(0,0);
606
607 REQUIRE_THROWS_WITH(
608 setQuregToWeightedSum(qureg, {coeff,coeff}, {qureg}),
609 ContainsSubstring("different number of coefficients") );
610 }
611 }
612}
613
614
615TEST_CASE( "setQuregToMixture", TEST_CATEGORY ) {
616
617 SECTION( LABEL_CORRECTNESS ) {
618
619 // @todo
620 // below, we test when every inQureg is unqiue and distinct
621 // from the outQureg, and so do not test the valid scenarios of:
622 // - outQureg being among inQuregs
623 // - one or more inQuregs being repeated
624 // However, both CPU and GPU implementations are sufficiently
625 // trivial to validate by inspection (eep...)
626
627 // compile-time optimisations apply for <= 5
628 int numInQuregs = GENERATE( 1, 2, 3, 4, 5, 6, 20 );
629 CAPTURE( numInQuregs );
630
631 vector<qreal> probs = getRandomProbabilities(numInQuregs);
632
633 // we must pass identically-deployed inQureg as outQureg,
634 // which itself gets tested being each possible deployment,
635 // so we defer allocation of the inQuregs
636 vector<Qureg> inQuregs(numInQuregs);
637
638 SECTION( LABEL_DENSMATR ) {
639
640 // generate (unnormalised) input reference matrices
641 vector<qmatrix> inMatrRefs(numInQuregs);
642 for (int i=0; i<numInQuregs; i++)
643 inMatrRefs[i] = getRandomMatrix(getPow2(getNumCachedQubits()));
644
645 // compute output reference matrix
646 qmatrix outMatrRef = getZeroMatrix(getPow2(getNumCachedQubits()));
647 for (int i=0; i<numInQuregs; i++)
648 outMatrRef += probs[i] * inMatrRefs[i];
649
650 auto apiFunc = [&](Qureg outQureg) {
651
652 // prepare input quregs
653 for (int i=0; i<numInQuregs; i++) {
654 inQuregs[i] = createCloneQureg(outQureg);
655 setQuregToReference(inQuregs[i], inMatrRefs[i]);
656 }
657
658 // modify outQureg
659 setQuregToMixture(outQureg, probs.data(), inQuregs.data(), numInQuregs);
660
661 // free input quregs
662 for (int i=0; i<numInQuregs; i++)
663 destroyQureg(inQuregs[i]);
664 };
665
666 TEST_ON_CACHED_QUREGS(getCachedDensmatrs(), apiFunc, outMatrRef);
667 }
668 }
669
670 SECTION( LABEL_VALIDATION ) {
671
672 // arbitrary existing qureg
673 Qureg qureg = getCachedDensmatrs().begin()->second;
674
675 SECTION( "out qureg uninitialised" ) {
676
677 // spoof uninitialised value to be sure
678 Qureg badQureg;
679 badQureg.numQubits = -123;
680
681 REQUIRE_THROWS_WITH(
682 setQuregToMixture(badQureg, nullptr, nullptr, 1),
683 ContainsSubstring("invalid Qureg") );
684 }
685
686 SECTION( "in qureg uninitialised" ) {
687
688 // set all inQureg to arbitrary existing qureg
689 int numIn = 5;
690 vector<Qureg> inQuregs(numIn, qureg);
691
692 // hide an unitialised qureg among them
693 Qureg badQureg;
694 badQureg.numQubits = -123;
695 int badInd = GENERATE_COPY( range(0,numIn) );
696 inQuregs[badInd] = badQureg;
697
698 REQUIRE_THROWS_WITH(
699 setQuregToMixture(qureg, nullptr, inQuregs.data(), numIn),
700 ContainsSubstring("invalid Qureg") );
701 }
702
703 SECTION( "out qureg is statevector" ) {
704
705 Qureg badQureg = getCachedStatevecs().begin()->second;
706
707 REQUIRE_THROWS_WITH(
708 setQuregToMixture(badQureg, nullptr, nullptr, 1),
709 ContainsSubstring("received a statevector") );
710 }
711
712 SECTION( "in qureg is statevector" ) {
713
714 // set all inQureg to arbitrary existing density matrix
715 int numIn = 5;
716 vector<Qureg> inQuregs(numIn, qureg);
717
718 // hide a statevector among them
719 int badInd = GENERATE_COPY( range(0,numIn) );
720 inQuregs[badInd] = getCachedStatevecs().begin()->second;;
721
722 REQUIRE_THROWS_WITH(
723 setQuregToMixture(qureg, nullptr, inQuregs.data(), numIn),
724 ContainsSubstring("One or more Quregs were statevectors") );
725 }
726
727 SECTION( "number of quregs" ) {
728
729 int numIn = GENERATE( -1, 0 );
730
731 REQUIRE_THROWS_WITH(
732 setQuregToMixture(qureg, nullptr, nullptr, numIn),
733 ContainsSubstring("number of passed Quregs") && ContainsSubstring("is invalid") );
734 }
735
736 SECTION( "inconsistent qureg sizes" ) {
737
738 // must create new Quregs to ensure they are identically deployed
739 Qureg quregA = createCustomQureg(getNumCachedQubits(), 1,0,0,0);
740 Qureg quregB = createCustomQureg(getNumCachedQubits() + 1, 1,0,0,0);
741
742 // set all inQureg to quregA (as will be outQureg)
743 int numIn = 5;
744 vector<Qureg> inQuregs(numIn, quregA);
745
746 // set one to quregB
747 int badInd = GENERATE_COPY( range(0,numIn) );
748 inQuregs[badInd] = quregB;
749
750 REQUIRE_THROWS_WITH(
751 setQuregToMixture(quregA, nullptr, inQuregs.data(), numIn),
752 ContainsSubstring("inconsistent attributes") );
753
754 destroyQureg(quregA);
755 destroyQureg(quregB);
756 }
757
758 SECTION( "inconsistent qureg deployments" ) {
759
760 // we do not necessarily have differently-distributed/GPU Quregs at
761 // runtime, so we enumerate all deployments and test when they differ
762
763 for (auto& [label, badQureg]: getCachedDensmatrs()) {
764
765 if ((badQureg.isGpuAccelerated == qureg.isGpuAccelerated) &&
766 (badQureg.isDistributed == qureg.isDistributed))
767 continue;
768
769 // set all inQureg to qureg (as will be outQureg)
770 int numIn = 5;
771 vector<Qureg> inQuregs(numIn, qureg);
772
773 // set one to badQureg
774 int badInd = GENERATE_COPY( range(0,numIn) );
775 inQuregs[badInd] = badQureg;
776
777 REQUIRE_THROWS_WITH(
778 setQuregToMixture(qureg, nullptr, inQuregs.data(), numIn),
779 ContainsSubstring("inconsistent attributes") );
780 }
781
782 // automatically pass when there are no differing deployments
783 SUCCEED( );
784 }
785
786 SECTION( "invalid probs" ) {
787
788 // set all inQureg to arbitrary existing density matrix
789 int numIn = 5;
790 vector<Qureg> inQuregs(numIn, qureg);
791
792 // get valid probabilities then mess one up
793 int badInd = GENERATE_COPY( range(0,numIn) );
794 vector<qreal> probs = getRandomProbabilities(numIn);
795 probs[badInd] = GENERATE( -1., -0.1, 1.1, 2. );
796
797 REQUIRE_THROWS_WITH(
798 setQuregToMixture(qureg, probs.data(), inQuregs.data(), numIn),
799 ContainsSubstring("One or more given probabilities are invalid") );
800 }
801
802 SECTION( "unnormalised probs" ) {
803
804 // set all inQureg to arbitrary existing density matrix
805 int numIn = 5;
806 vector<Qureg> inQuregs(numIn, qureg);
807
808 // these illegal non-unity values assume eps < 0.1
809 qreal probSum = GENERATE( 0.9, 1.1 );
810 vector<qreal> probs(numIn, probSum / numIn);
811
812 REQUIRE_THROWS_WITH(
813 setQuregToMixture(qureg, probs.data(), inQuregs.data(), numIn),
814 ContainsSubstring("probabilities do not sum to") && ContainsSubstring("one") );
815 }
816
817 SECTION( "different number of quregs and probs") {
818
819 // relevant only to the C++ overload
820
821 qreal prob = 0;
822
823 REQUIRE_THROWS_WITH(
824 setQuregToMixture(qureg, {prob,prob}, {qureg}),
825 ContainsSubstring("different number of probabilities") );
826 }
827 }
828}
829
830
831/** @} (end defgroup) */
832
833
834
835/**
836 * @todo
837 * UNTESTED FUNCTIONS
838 */
839
840// these require we deploy each passed Qureg distinctly
841// to thoroughly test all QuEST control flows, for which
842// we do not yet have the macros/scaffolding
843
844void initPureState(Qureg qureg, Qureg pure);
845
846void setQuregToClone(Qureg targetQureg, Qureg copyQureg);
847
848void setQuregToPartialTrace(Qureg out, Qureg in, int* traceOutQubits, int numTraceQubits);
849
850void 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:319
Qureg createCustomQureg(int numQubits, int isDensMatr, int useDistrib, int useGpuAccel, int useMultithread)
Definition qureg.cpp:277
void destroyQureg(Qureg qureg)
Definition qureg.cpp:334
void syncQuregFromGpu(Qureg qureg)
Definition qureg.cpp:403
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