The Quantum Exact Simulation Toolkit v4.2.0
Loading...
Searching...
No Matches
test_decoherence.cpp
1/** @file
2 * Ported tests of the deprecated QuEST v3 interface,
3 * unit testing the "decoherence" 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 */
12
13#include <catch2/catch_test_macros.hpp>
14#include <catch2/matchers/catch_matchers_string.hpp>
15#include <catch2/generators/catch_generators_adapters.hpp>
16#include <catch2/generators/catch_generators_range.hpp>
17#include <catch2/generators/catch_generators_random.hpp>
18
19#include "quest.h"
20#include "test_utilities.hpp"
21
22#include <random>
23#include <vector>
24#include <algorithm>
25
26using std::vector;
27
28/** Prepares a density matrix in the debug state, and the reference QMatrix
29 */
30#define PREPARE_TEST(qureg, ref) \
31 Qureg qureg = createForcedDensityQureg(NUM_QUBITS); \
32 initDebugState(qureg); \
33 QMatrix ref = toQMatrix(qureg); \
34 assertQuregAndRefInDebugState(qureg, ref); \
35 setValidationEpsilon(REAL_EPS);
36
37/* allows concise use of ContainsSubstring in catch's REQUIRE_THROWS_WITH */
38using Catch::Matchers::ContainsSubstring;
39
40
41
42/** @sa mixDamping
43 * @ingroup deprecatedtests
44 * @author Tyson Jones
45 */
46TEST_CASE( "mixDamping", "[decoherence]" ) {
47
48 PREPARE_TEST(qureg, ref);
49
50 SECTION( "correctness" ) {
51
52 int target = GENERATE( range(0, NUM_QUBITS) );
53 qreal prob = getRandomReal(0, 1);
54 mixDamping(qureg, target, prob);
55
56 // ref -> kraus0 ref kraus0^dagger + kraus1 ref kraus1^dagger
57 QMatrix kraus0{{1,0},{0,sqrt(1-prob)}};
58 QMatrix rho0 = ref;
59 applyReferenceOp(rho0, target, kraus0);
60 QMatrix kraus1{{0,sqrt(prob)},{0,0}};
61 QMatrix rho1 = ref;
62 applyReferenceOp(rho1, target, kraus1);
63 ref = rho0 + rho1;
64
65 REQUIRE( areEqual(qureg, ref) );
66 }
67 SECTION( "validation ") {
68
69 SECTION( "qubit index" ) {
70
71 int target = GENERATE( -1, NUM_QUBITS );
72 REQUIRE_THROWS_WITH( mixDamping(qureg, target, 0), ContainsSubstring("Invalid target") );
73
74 }
75 SECTION( "probability" ) {
76
77 REQUIRE_THROWS_WITH( mixDamping(qureg, 0, -.1), ContainsSubstring("probability is invalid") );
78 REQUIRE_THROWS_WITH( mixDamping(qureg, 0, 1.1), ContainsSubstring("probability is invalid") );
79 }
80 SECTION( "density-matrix" ) {
81
82 Qureg vec = createQureg(NUM_QUBITS);
83 REQUIRE_NOTHROW( mixDamping(vec, 0, 0) ); // zero-prob allowed in v4
84 REQUIRE_THROWS_WITH( mixDamping(vec, 0, .1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
86 }
87 }
88 destroyQureg(qureg, getQuESTEnv());
89}
90
91
92
93/** @sa mixDensityMatrix
94 * @ingroup deprecatedtests
95 * @author Tyson Jones
96 */
97TEST_CASE( "mixDensityMatrix", "[decoherence]" ) {
98
99 Qureg qureg1 = createForcedDensityQureg(NUM_QUBITS);
100 Qureg qureg2 = createForcedDensityQureg(NUM_QUBITS);
101 initDebugState(qureg1);
102 initDebugState(qureg2);
103 QMatrix ref1 = toQMatrix(qureg1);
104 QMatrix ref2 = toQMatrix(qureg2);
105
106 SECTION( "correctness" ) {
107
108 // test p in {0, 1} and 10 random values in (0,1)
109 qreal prob = GENERATE( 0., 1., take(10, random(0.,1.)) );
110 mixDensityMatrix(qureg1, prob, qureg2);
111
112 // ensure target qureg modified correctly
113 ref1 = (1-prob)*ref1 + (prob)*ref2;
114 REQUIRE( areEqual(qureg1, ref1) );
115
116 // enure other qureg was not modified
117 REQUIRE( areEqual(qureg2, ref2) );
118 }
119 SECTION( "input validation" ) {
120
121 SECTION( "probabilities" ) {
122
123 qreal prob = GENERATE( -0.1, 1.1 );
124 REQUIRE_THROWS_WITH( mixDensityMatrix(qureg1, prob, qureg2), ContainsSubstring("probability is invalid") );
125 }
126 SECTION( "density matrices" ) {
127
128 // one is statevec
129 Qureg state1 = createQureg(qureg1.numQubits, getQuESTEnv());
130 REQUIRE_THROWS_WITH( mixDensityMatrix(state1, 0, qureg1), ContainsSubstring("The first Qureg") && ContainsSubstring("must be a density matrix") );
131
132 // in v4, the 2nd arg can be a statevector
133
134 // REQUIRE_THROWS_WITH( mixDensityMatrix(qureg1, 0, state1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
135
136 // both are statevec
137 Qureg state2 = createQureg(qureg1.numQubits, getQuESTEnv());
138 REQUIRE_THROWS_WITH( mixDensityMatrix(state1, 0, state2), ContainsSubstring("The first Qureg") && ContainsSubstring("must be a density matrix") );
139
140 destroyQureg(state1, getQuESTEnv());
141 destroyQureg(state2, getQuESTEnv());
142 }
143 SECTION( "matching dimensions" ) {
144
145 Qureg qureg3 = createDensityQureg(1 + qureg1.numQubits, getQuESTEnv());
146 REQUIRE_THROWS_WITH( mixDensityMatrix(qureg1, 0, qureg3), ContainsSubstring("inconsistent number of qubits") );
147 REQUIRE_THROWS_WITH( mixDensityMatrix(qureg3, 0, qureg1), ContainsSubstring("inconsistent number of qubits") );
148 destroyQureg(qureg3, getQuESTEnv());
149 }
150 }
151 destroyQureg(qureg1, getQuESTEnv());
152 destroyQureg(qureg2, getQuESTEnv());
153}
154
155
156
157/** @sa mixDephasing
158 * @ingroup deprecatedtests
159 * @author Tyson Jones
160 */
161TEST_CASE( "mixDephasing", "[decoherence]" ) {
162
163 PREPARE_TEST(qureg, ref);
164
165 SECTION( "correctness" ) {
166
167 int target = GENERATE( range(0,NUM_QUBITS) );
168 qreal prob = getRandomReal(0, 1/2.);
169 mixDephasing(qureg, target, prob);
170
171 // ref -> (1 - prob) ref + prob Z ref Z
172 QMatrix phaseRef = ref;
173 applyReferenceOp(phaseRef, target, QMatrix{{1,0},{0,-1}}); // Z ref Z
174 ref = ((1 - prob) * ref) + (prob * phaseRef);
175
176 REQUIRE( areEqual(qureg, ref) );
177 }
178 SECTION( "validation" ) {
179
180 SECTION( "qubit index" ) {
181
182 int target = GENERATE( -1, NUM_QUBITS );
183 REQUIRE_THROWS_WITH( mixDephasing(qureg, target, 0), ContainsSubstring("Invalid target") );
184
185 }
186 SECTION( "probability" ) {
187
188 REQUIRE_THROWS_WITH( mixDephasing(qureg, 0, -.1), ContainsSubstring("probability is invalid") );
189 REQUIRE_THROWS_WITH( mixDephasing(qureg, 0, .6), ContainsSubstring("probability") && ContainsSubstring("1/2") );
190 }
191 SECTION( "density-matrix" ) {
192
193 Qureg vec = createQureg(NUM_QUBITS);
194 REQUIRE_NOTHROW( mixDephasing(vec, 0, 0) ); // zero prob ok in v4
195 REQUIRE_THROWS_WITH( mixDephasing(vec, 0, 0.1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
197 }
198 }
199 destroyQureg(qureg, getQuESTEnv());
200}
201
202
203
204/** @sa mixDepolarising
205 * @ingroup deprecatedtests
206 * @author Tyson Jones
207 */
208TEST_CASE( "mixDepolarising", "[decoherence]" ) {
209
210 PREPARE_TEST(qureg, ref);
211
212 SECTION( "correctness " ) {
213
214 int target = GENERATE( range(0,NUM_QUBITS) );
215 qreal prob = getRandomReal(0, 3/4.);
216 mixDepolarising(qureg, target, prob);
217
218 QMatrix xRef = ref;
219 applyReferenceOp(xRef, target, QMatrix{{0,1},{1,0}}); // X ref X
220 QMatrix yRef = ref;
221 applyReferenceOp(yRef, target, QMatrix{{0,-qcomp(0,1)},{qcomp(0,1),0}}); // Y ref Y
222 QMatrix zRef = ref;
223 applyReferenceOp(zRef, target, QMatrix{{1,0},{0,-1}}); // Z ref Z
224 ref = ((1 - prob) * ref) + ((prob/3.) * ( xRef + yRef + zRef));
225
226 REQUIRE( areEqual(qureg, ref) );
227 }
228 SECTION( "validation ") {
229
230 SECTION( "qubit index" ) {
231
232 int target = GENERATE( -1, NUM_QUBITS );
233 REQUIRE_THROWS_WITH( mixDepolarising(qureg, target, 0), ContainsSubstring("Invalid target") );
234
235 }
236 SECTION( "probability" ) {
237
238 REQUIRE_THROWS_WITH( mixDepolarising(qureg, 0, -.1), ContainsSubstring("probability is invalid") );
239 REQUIRE_THROWS_WITH( mixDepolarising(qureg, 0, .76), ContainsSubstring("probability") && ContainsSubstring("3/4") );
240 }
241 SECTION( "density-matrix" ) {
242
243 Qureg vec = createQureg(NUM_QUBITS);
244 REQUIRE_NOTHROW( mixDepolarising(vec, 0, 0) ); // zero-prob ok in v4
245 REQUIRE_THROWS_WITH( mixDepolarising(vec, 0, 0.1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
247 }
248 }
249 destroyQureg(qureg, getQuESTEnv());
250}
251
252
253
254/** @sa mixMultiQubitKrausMap
255 * @ingroup deprecatedtests
256 * @author Tyson Jones
257 */
258TEST_CASE( "mixMultiQubitKrausMap", "[decoherence]" ) {
259
260 PREPARE_TEST(qureg, ref);
261
262 // figure out max-num (inclusive) targs allowed by hardware backend
263 // (each node must contain as 2^(2*numTargs) amps)
264 int maxNumTargs = qureg.logNumAmpsPerNode / 2;
265
266 SECTION( "correctness" ) {
267
268 /* note that this function incurs a stack overhead when numTargs < 4,
269 * and a heap overhead when numTargs >= 4
270 */
271
272 // try every size (qubit wise) map
273 int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); // inclusive upper bound
274
275 // previously, we tried every unique set of targets, via:
276 // int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
277 // alas, this is too slow for CI, so we instead try a fixed number of random sets:
278 GENERATE( range(0, 10) );
279 vector<int> targs(numTargs);
280 setRandomTargets(targs, NUM_QUBITS);
281
282 // try the min and max number of operators, and 2 random numbers
283 // (there are way too many to try all!)
284 int maxNumOps = (2*numTargs)*(2*numTargs);
285 int numOps = GENERATE_COPY( 1, maxNumOps, take(2,random(1,maxNumOps)) );
286
287 // use a new random map
288 vector<QMatrix> matrs = getRandomKrausMap(numTargs, numOps);
289
290 // create map in QuEST datatypes
291 vector<ComplexMatrixN> ops(numOps);
292 for (int i=0; i<numOps; i++) {
293 ops[i] = createCompMatr(numTargs);
294 setCompMatr(ops[i], matrs[i]);
295 }
296
297 mixMultiQubitKrausMap(qureg, targs.data(), numTargs, ops.data(), numOps);
298
299 // set ref -> K_i ref K_i^dagger
300 vector<QMatrix> matrRefs(numOps);
301 for (int i=0; i<numOps; i++) {
302 matrRefs[i] = ref;
303 applyReferenceOp(matrRefs[i], targs.data(), numTargs, matrs[i]);
304 }
305 ref = getZeroMatrix(ref.size());
306 for (int i=0; i<numOps; i++)
307 ref += matrRefs[i];
308
309 REQUIRE( areEqual(qureg, ref, 1E2*REAL_EPS) );
310
311 // cleanup QuEST datatypes
312 for (int i=0; i<numOps; i++)
313 destroyCompMatr(ops[i]);
314 }
315 SECTION( "input validation" ) {
316
317 // spoof a list of properly-initialised CompMatr to avoid seg-fault in deprecation API.
318 // note some functions will need smaller matrices which is fine; a subset of each
319 // spoof'd matrix will be copied over by the deprecation layer
320 ComplexMatrixN spoofOps[NUM_QUBITS];
321 for (int i=0; i<NUM_QUBITS; i++) {
322 spoofOps[i] = createComplexMatrixN(NUM_QUBITS);
323 syncCompMatr(spoofOps[i]);
324 }
325
326 SECTION( "repetition of target" ) {
327
328 // make valid targets
329 int targs[NUM_QUBITS];
330 for (int i=0; i<NUM_QUBITS; i++)
331 targs[i] = i;
332
333 // duplicate one
334 int badInd = GENERATE( range(0,NUM_QUBITS) );
335 int copyInd = GENERATE_COPY( filter([=](int i){ return i!=badInd; }, range(0,NUM_QUBITS)) );
336 targs[badInd] = targs[copyInd];
337
338 REQUIRE_THROWS_WITH( mixMultiQubitKrausMap(qureg, targs, NUM_QUBITS, spoofOps, 1), ContainsSubstring("target qubits") && ContainsSubstring("unique") );
339 }
340 SECTION( "qubit indices" ) {
341
342 // make valid targets
343 int targs[NUM_QUBITS];
344 for (int i=0; i<NUM_QUBITS; i++)
345 targs[i] = i;
346
347 // make one invalid
348 targs[GENERATE( range(0,NUM_QUBITS) )] = GENERATE( -1, NUM_QUBITS );
349
350 REQUIRE_THROWS_WITH( mixMultiQubitKrausMap(qureg, targs, NUM_QUBITS, spoofOps, 1), ContainsSubstring("Invalid target qubit") );
351 }
352
353 // cannot test this with deprecated API, since 'numOps' informs a copy before validation
354
355 // SECTION( "number of operators" ) {
356
357 // int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) );
358 // int numOps = GENERATE_REF( -1, 0 );
359
360 // // make valid targets to avoid triggering target validation
361 // vector<int> targs(numTargs);
362 // for (int i=0; i<numTargs; i++)
363 // targs[i] = i;
364
365 // REQUIRE_THROWS_WITH( mixMultiQubitKrausMap(qureg, targs.data(), numTargs, spoofOps, numOps), ContainsSubstring("must be given a strictly positive number of matrices") );
366 // }
367
368 // this validation cannot be performed by the deprecation API which copies
369 // over all ops arguments without prior checking them as NULL
370
371 // SECTION( "initialisation of operators" ) {
372
373 // /* compilers don't auto-initialise to NULL; the below circumstance
374 // * only really occurs when 'malloc' returns NULL in createCompMatr,
375 // * which actually triggers its own validation. Hence this test is useless
376 // * currently.
377 // */
378
379 // int numTargs = NUM_QUBITS;
380 // int numOps = (2*numTargs)*(2*numTargs);
381
382 // vector<ComplexMatrixN> ops(numOps);
383 // for (int i=0; i<numOps; i++)
384 // ops[i] = createComplexMatrixN(numTargs);
385
386 // // make one of the max-ops explicitly NULL
387 // ops[GENERATE_COPY( range(0,numTargs) )].cpuElems = NULL;
388
389 // // make valid targets to avoid triggering target validation
390 // vector<int> targs(numTargs);
391 // for (int i=0; i<numTargs; i++)
392 // targs[i] = i;
393
394 // REQUIRE_THROWS_WITH( mixMultiQubitKrausMap(qureg, targs.data(), numTargs, ops.data(), numOps), ContainsSubstring("ComplexMatrixN") && ContainsSubstring("created") );
395
396 // for (int i=0; i<numOps; i++)
397 // destroyComplexMatrixN(ops[i]);
398 // }
399
400 // this validation cannot be performed by the deprecation API which copies
401 // over all ops arguments without prior checking them as matching in dimension
402
403 // SECTION( "dimension of operators" ) {
404
405 // // make valid (dimension-wise) max-qubits Kraus map
406 // int numTargs = NUM_QUBITS;
407 // int numOps = (2*numTargs)*(2*numTargs);
408 // vector<ComplexMatrixN> ops(numOps);
409 // for (int i=0; i<numOps; i++)
410 // ops[i] = createComplexMatrixN(numTargs);
411
412 // // make one have wrong-dimensions
413 // int badInd = GENERATE_COPY( range(0,numTargs) );
414 // destroyComplexMatrixN(ops[badInd]);
415 // ops[badInd] = createComplexMatrixN(numTargs - 1);
416
417 // // make valid targets to avoid triggering target validation
418 // vector<int> targs(numTargs);
419 // for (int i=0; i<numTargs; i++)
420 // targs[i] = i;
421
422 // REQUIRE_THROWS_WITH( mixMultiQubitKrausMap(qureg, targs.data(), numTargs, ops.data(), numOps), ContainsSubstring("same number of qubits") );
423
424 // for (int i=0; i<numOps; i++)
425 // destroyComplexMatrixN(ops[i]);
426 // }
427
428 SECTION( "trace preserving" ) {
429
430 int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) );
431 int maxNumOps = (2*numTargs) * (2*numTargs);
432 int numOps = GENERATE_COPY( 1, 2, maxNumOps );
433
434 // generate a valid map
435 vector<QMatrix> matrs = getRandomKrausMap(numTargs, numOps);
436 vector<ComplexMatrixN> ops(numOps);
437 for (int i=0; i<numOps; i++) {
438 ops[i] = createComplexMatrixN(numTargs);
439 toComplexMatrixN(matrs[i], ops[i]);
440 }
441
442 // make only one invalid
443 ops[GENERATE_COPY( 0, numOps - 1)].cpuElems[0][0] = -123456789;
444
445 // make valid targets to avoid triggering target validation
446 vector<int> targs(numTargs);
447 for (int i=0; i<numTargs; i++)
448 targs[i] = i;
449
450 REQUIRE_THROWS_WITH( mixMultiQubitKrausMap(qureg, targs.data(), numTargs, ops.data(), numOps), ContainsSubstring("trace preserving") );
451
452 for (int i=0; i<numOps; i++)
453 destroyComplexMatrixN(ops[i]);
454 }
455 SECTION( "density-matrix" ) {
456
457 Qureg statevec = createQureg(NUM_QUBITS);
458
459 // make valid targets to avoid triggering target validation
460 int targs[NUM_QUBITS];
461 for (int i=0; i<NUM_QUBITS; i++)
462 targs[i] = i;
463
464 REQUIRE_THROWS_WITH( mixMultiQubitKrausMap(statevec, targs, NUM_QUBITS, spoofOps, 1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
465 destroyQureg(statevec, getQuESTEnv());
466
467 }
468 SECTION( "operator fits in node" ) {
469
470 // each node requires (2 numTargs)^2 amplitudes
471 int minAmps = (2*NUM_QUBITS) * (2*NUM_QUBITS);
472
473 // make valid targets to avoid triggering target validation
474 int targs[NUM_QUBITS];
475 for (int i=0; i<NUM_QUBITS; i++)
476 targs[i] = i;
477
478 // make a simple Identity map
479 ComplexMatrixN ops[] = {createComplexMatrixN(NUM_QUBITS)};
480 for (int i=0; i<(1<<NUM_QUBITS); i++)
481 ops[0].cpuElems[i][i] = 1;
482 syncCompMatr(ops[0]);
483
484 // fake a smaller qureg
485 qureg.isDistributed = 1;
486 qureg.numAmpsPerNode = minAmps - 1;
487 qureg.logNumAmpsPerNode = log2(minAmps) - 1;
488 REQUIRE_THROWS_WITH( mixMultiQubitKrausMap(qureg, targs, NUM_QUBITS, ops, 1), ContainsSubstring("each node's communication buffer") && ContainsSubstring("cannot simultaneously store") );
489
490 destroyComplexMatrixN(ops[0]);
491 }
492
493 for (int i=0; i<NUM_QUBITS; i++)
494 destroyCompMatr(spoofOps[i]);
495 }
496 destroyQureg(qureg, getQuESTEnv());
497}
498
499
500
501/** @sa mixPauli
502 * @ingroup deprecatedtests
503 * @author Tyson Jones
504 */
505TEST_CASE( "mixPauli", "[decoherence]" ) {
506
507 PREPARE_TEST(qureg, ref);
508
509 SECTION( "correctness" ) {
510
511 int target = GENERATE( range(0,NUM_QUBITS) );
512
513 // randomly generate valid pauli-error probabilities
514 qreal probs[3];
515 qreal max0 = 1/2.; // satisfies p1 < 1 - py
516 probs[0] = getRandomReal(0, max0);
517 qreal max1 = (max0 - probs[0])/2.; // p2 can use half of p1's "unused space"
518 probs[1] = getRandomReal(0, max1);
519 qreal max2 = (max1 - probs[1])/2.; // p3 can use half of p2's "unused space"
520 probs[2] = getRandomReal(0, max2);
521
522 // uniformly randomly assign probs (bound to target)
523 int inds[3] = {0,1,2};
524 std::shuffle(inds,inds+3, std::default_random_engine(1E5 * target));
525 qreal probX = probs[inds[0]]; // seed:target shows no variation
526 qreal probY = probs[inds[1]];
527 qreal probZ = probs[inds[2]];
528
529 mixPauli(qureg, target, probX, probY, probZ);
530
531 QMatrix xRef = ref;
532 applyReferenceOp(xRef, target, QMatrix{{0,1},{1,0}}); // X ref X
533 QMatrix yRef = ref;
534 applyReferenceOp(yRef, target, QMatrix{{0,-qcomp(0,1)},{qcomp(0,1),0}}); // Y ref Y
535 QMatrix zRef = ref;
536 applyReferenceOp(zRef, target, QMatrix{{1,0},{0,-1}}); // Z ref Z
537 ref = ((1 - probX - probY - probZ) * ref) +
538 (probX * xRef) + (probY * yRef) + (probZ * zRef);
539
540 REQUIRE( areEqual(qureg, ref) );
541 }
542 SECTION( "input validation" ) {
543
544 SECTION( "qubit index" ) {
545
546 int target = GENERATE( -1, NUM_QUBITS );
547 REQUIRE_THROWS_WITH( mixPauli(qureg, target, 0, 0, 0), ContainsSubstring("Invalid target") );
548
549 }
550 SECTION( "probability" ) {
551
552 int target = 0;
553
554 // probs clearly must be in [0, 1]
555 REQUIRE_THROWS_WITH( mixPauli(qureg, target, -.1, 0, 0), ContainsSubstring("probability is invalid") );
556 REQUIRE_THROWS_WITH( mixPauli(qureg, target, 0, -.1, 0), ContainsSubstring("probability is invalid") );
557 REQUIRE_THROWS_WITH( mixPauli(qureg, target, 0, 0, -.1), ContainsSubstring("probability is invalid") );
558
559 // max single-non-zero-prob is 0.5
560 REQUIRE_THROWS_WITH( mixPauli(qureg, target, .6, 0, 0), ContainsSubstring("probabilities exceed that which induce maximal mixing") );
561 REQUIRE_THROWS_WITH( mixPauli(qureg, target, 0, .6, 0), ContainsSubstring("probabilities exceed that which induce maximal mixing") );
562 REQUIRE_THROWS_WITH( mixPauli(qureg, target, 0, 0, .6), ContainsSubstring("probabilities exceed that which induce maximal mixing") );
563
564 // must satisfy px, py, pz < 1 - px - py - pz
565 REQUIRE_THROWS_WITH( mixPauli(qureg, target, .3, .3, .3), ContainsSubstring("probabilities exceed that which induce maximal mixing") );
566 }
567 SECTION( "density-matrix" ) {
568
569 Qureg vec = createQureg(NUM_QUBITS);
570 REQUIRE_NOTHROW( mixPauli(vec, 0, 0, 0, 0) ); // zero-prob ok in v4
571 REQUIRE_THROWS_WITH( mixPauli(vec, 0, 0.1, 0, 0), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
572 REQUIRE_THROWS_WITH( mixPauli(vec, 0, 0, 0.1, 0), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
573 REQUIRE_THROWS_WITH( mixPauli(vec, 0, 0, 0, 0.1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
575 }
576 }
577 destroyQureg(qureg, getQuESTEnv());
578}
579
580
581
582/** @sa mixKrausMap
583 * @ingroup deprecatedtests
584 * @author Tyson Jones
585 */
586TEST_CASE( "mixKrausMap", "[decoherence]" ) {
587
588 PREPARE_TEST(qureg, ref);
589
590 SECTION( "correctness" ) {
591
592 int target = GENERATE( range(0,NUM_QUBITS) );
593 int numOps = GENERATE( range(1,5) ); // max 4 inclusive
594 vector<QMatrix> matrs = getRandomKrausMap(1, numOps);
595
596 vector<ComplexMatrix2> ops(numOps);
597 for (int i=0; i<numOps; i++)
598 ops[i] = toComplexMatrix2(matrs[i]);
599
600 v3_mixKrausMap(qureg, target, ops.data(), numOps);
601
602 // set ref -> K_i ref K_i^dagger
603 vector<QMatrix> matrRefs(numOps);
604 for (int i=0; i<numOps; i++) {
605 matrRefs[i] = ref;
606 applyReferenceOp(matrRefs[i], target, matrs[i]);
607 }
608 ref = getZeroMatrix(ref.size());
609 for (int i=0; i<numOps; i++)
610 ref += matrRefs[i];
611
612 REQUIRE( areEqual(qureg, ref, 10*REAL_EPS) );
613 }
614 SECTION( "input validation" ) {
615
616 // cannot use NULL because v3 deprecation API copies ops arg before v4 validation
617 ComplexMatrix2 spoofOps[1];
618
619 SECTION( "number of operators" ) {
620
621 int numOps = 0;
622
623 REQUIRE_THROWS_WITH( v3_mixKrausMap(qureg, 0, spoofOps, numOps), ContainsSubstring("must be given a strictly positive number of matrices") );
624 }
625 SECTION( "trace preserving" ) {
626
627 // valid Kraus map
628 int numOps = GENERATE( range(1,5) ); // max 4 inclusive
629 vector<QMatrix> matrs = getRandomKrausMap(1, numOps);
630 vector<ComplexMatrix2> ops(numOps);
631 for (int i=0; i<numOps; i++)
632 ops[i] = toComplexMatrix2(matrs[i]);
633
634 // make invalid
635 ops[GENERATE_REF( range(0,numOps) )].real[0][0] = 9999;
636 REQUIRE_THROWS_WITH( v3_mixKrausMap(qureg, 0, ops.data(), numOps), ContainsSubstring("trace preserving") );
637 }
638 SECTION( "qubit index" ) {
639
640 int target = GENERATE( -1, NUM_QUBITS );
641 REQUIRE_THROWS_WITH( v3_mixKrausMap(qureg, target, spoofOps, 1), ContainsSubstring("Invalid target qubit") );
642 }
643 SECTION( "density-matrix" ) {
644
645 Qureg vec = createQureg(NUM_QUBITS);
646 REQUIRE_THROWS_WITH( v3_mixKrausMap(vec, 0, spoofOps, 1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
648 }
649 SECTION( "operators fit in node" ) {
650
651 qureg.isDistributed = 1;
652 qureg.numAmpsPerNode = 3; // min 4
653 qureg.logNumAmpsPerNode = 1; // min 2
654
655 REQUIRE_THROWS_WITH( v3_mixKrausMap(qureg, 0, spoofOps, 1), ContainsSubstring("each node's communication buffer") && ContainsSubstring("cannot simultaneously store") );
656 }
657 }
658 destroyQureg(qureg, getQuESTEnv());
659}
660
661
662
663/** @sa mixNonTPKrausMap
664 * @ingroup deprecatedtests
665 * @author Tyson Jones
666 */
667TEST_CASE( "mixNonTPKrausMap", "[decoherence]" ) {
668
669 PREPARE_TEST(qureg, ref);
670
671 SECTION( "correctness" ) {
672
673 int target = GENERATE( range(0,NUM_QUBITS) );
674 int numOps = GENERATE( range(1,5) ); // max 4 inclusive
675
676 // map consists of unconstrained 2x2 random matrices
677 vector<QMatrix> matrs;
678 for (int i=0; i<numOps; i++)
679 matrs.push_back(getRandomQMatrix(2));
680
681 vector<ComplexMatrix2> ops(numOps);
682 for (int i=0; i<numOps; i++)
683 ops[i] = toComplexMatrix2(matrs[i]);
684 mixNonTPKrausMap(qureg, target, ops.data(), numOps);
685
686 // set ref -> K_i ref K_i^dagger
687 vector<QMatrix> matrRefs(numOps);
688 for (int i=0; i<numOps; i++) {
689 matrRefs[i] = ref;
690 applyReferenceOp(matrRefs[i], target, matrs[i]);
691 }
692 ref = getZeroMatrix(ref.size());
693 for (int i=0; i<numOps; i++)
694 ref += matrRefs[i];
695
696 REQUIRE( areEqual(qureg, ref, 1E2*REAL_EPS) );
697 }
698 SECTION( "input validation" ) {
699
700 // v3 deprecation API copies ops list before v4 validation, so we cannot pass ops=NULL
701 ComplexMatrix2 spoofOps[1];
702
703 SECTION( "number of operators" ) {
704
705 int numOps = 0;
706 REQUIRE_THROWS_WITH( mixNonTPKrausMap(qureg, 0, spoofOps, numOps), ContainsSubstring("must be given a strictly positive number of matrices") );
707 }
708 SECTION( "qubit index" ) {
709
710 int target = GENERATE( -1, NUM_QUBITS );
711 REQUIRE_THROWS_WITH( mixNonTPKrausMap(qureg, target, spoofOps, 1), ContainsSubstring("Invalid target qubit") );
712 }
713 SECTION( "density-matrix" ) {
714
715 Qureg vec = createQureg(NUM_QUBITS);
716 REQUIRE_THROWS_WITH( mixNonTPKrausMap(vec, 0, spoofOps, 1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
718 }
719 SECTION( "operators fit in node" ) {
720
721 qureg.isDistributed = 1;
722 qureg.numAmpsPerNode = 3; // min 4
723 qureg.logNumAmpsPerNode = 1; // min 2
724 REQUIRE_THROWS_WITH( mixNonTPKrausMap(qureg, 0, spoofOps, 1), ContainsSubstring("each node's communication buffer") && ContainsSubstring("cannot simultaneously store") );
725 }
726 }
727 destroyQureg(qureg, getQuESTEnv());
728}
729
730
731
732/** @sa mixNonTPMultiQubitKrausMap
733 * @ingroup deprecatedtests
734 * @author Tyson Jones
735 */
736TEST_CASE( "mixNonTPMultiQubitKrausMap", "[decoherence]" ) {
737
738 PREPARE_TEST(qureg, ref);
739
740 // figure out max-num (inclusive) targs allowed by hardware backend
741 // (each node must contain as 2^(2*numTargs) amps)
742 int maxNumTargs = calcLog2(qureg.numAmpsPerNode) / 2;
743
744 SECTION( "correctness" ) {
745
746 /* note that this function incurs a stack overhead when numTargs < 4,
747 * and a heap overhead when numTargs >= 4
748 */
749
750 int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); // inclusive upper bound
751
752 // previously, we tried every unique set of targets, via:
753 // int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
754 // alas, this is too slow for CI, so we instead try a fixed number of random sets:
755 GENERATE( range(0, 10) );
756 vector<int> targs(numTargs);
757 setRandomTargets(targs, NUM_QUBITS);
758
759 // try the min and max number of operators, and 2 random numbers
760 // (there are way too many to try all!)
761 int maxNumOps = (2*numTargs)*(2*numTargs);
762 int numOps = GENERATE_COPY( 1, maxNumOps, take(2,random(1,maxNumOps)) );
763
764 // use a new random map, of unconstrained random (2^numTargs x 2^numTargs) matrices
765 vector<QMatrix> matrs;
766 for (int i=0; i<numOps; i++)
767 matrs.push_back(getRandomQMatrix(1 << numTargs));
768
769 // create map in QuEST datatypes
770 vector<ComplexMatrixN> ops(numOps);
771 for (int i=0; i<numOps; i++) {
772 ops[i] = createComplexMatrixN(numTargs);
773 toComplexMatrixN(matrs[i], ops[i]);
774 }
775
776 mixNonTPMultiQubitKrausMap(qureg, targs.data(), numTargs, ops.data(), numOps);
777
778 // set ref -> K_i ref K_i^dagger
779 vector<QMatrix> matrRefs(numOps);
780 for (int i=0; i<numOps; i++) {
781 matrRefs[i] = ref;
782 applyReferenceOp(matrRefs[i], targs.data(), numTargs, matrs[i]);
783 }
784 ref = getZeroMatrix(ref.size());
785 for (int i=0; i<numOps; i++)
786 ref += matrRefs[i];
787
788 REQUIRE( areEqual(qureg, ref, 1E5*REAL_EPS) );
789
790 // cleanup QuEST datatypes
791 for (int i=0; i<numOps; i++)
792 destroyComplexMatrixN(ops[i]);
793 }
794 SECTION( "input validation" ) {
795
796 // spoof a list of properly-initialised CompMatr to avoid seg-fault in deprecation API.
797 // note some functions will need smaller matrices which is fine; a subset of each
798 // spoof'd matrix will be copied over by the deprecation layer
799 ComplexMatrixN spoofOps[NUM_QUBITS];
800 for (int i=0; i<NUM_QUBITS; i++) {
801 spoofOps[i] = createComplexMatrixN(NUM_QUBITS);
802 syncCompMatr(spoofOps[i]);
803 }
804
805 SECTION( "repetition of target" ) {
806
807 // make valid targets
808 int targs[NUM_QUBITS];
809 for (int i=0; i<NUM_QUBITS; i++)
810 targs[i] = i;
811
812 // duplicate one
813 int badInd = GENERATE( range(0,NUM_QUBITS) );
814 int copyInd = GENERATE_COPY( filter([=](int i){ return i!=badInd; }, range(0,NUM_QUBITS)) );
815 targs[badInd] = targs[copyInd];
816
817 REQUIRE_THROWS_WITH( mixNonTPMultiQubitKrausMap(qureg, targs, NUM_QUBITS, spoofOps, 1), ContainsSubstring("target qubits") && ContainsSubstring("unique") );
818 }
819 SECTION( "qubit indices" ) {
820
821 // make valid targets
822 int targs[NUM_QUBITS];
823 for (int i=0; i<NUM_QUBITS; i++)
824 targs[i] = i;
825
826 // make one invalid
827 targs[GENERATE( range(0,NUM_QUBITS) )] = GENERATE( -1, NUM_QUBITS );
828
829 REQUIRE_THROWS_WITH( mixNonTPMultiQubitKrausMap(qureg, targs, NUM_QUBITS, spoofOps, 1), ContainsSubstring("Invalid target qubit") );
830 }
831
832 // cannot test this with deprecated API, since 'numOps' informs a copy before validation
833
834 // SECTION( "number of operators" ) {
835
836 // int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) );
837 // int numOps = GENERATE_REF( -1, 0 );
838
839 // // make valid targets to avoid triggering target validation
840 // vector<int> targs(numTargs);
841 // for (int i=0; i<numTargs; i++)
842 // targs[i] = i;
843 // REQUIRE_THROWS_WITH( mixNonTPMultiQubitKrausMap(qureg, targs.data(), numTargs, spoofOps, numOps), ContainsSubstring("must be given a strictly positive number of matrices") );
844 // }
845
846 // this validation cannot be performed by the deprecation API which copies
847 // over all ops arguments without prior checking them as NULL
848
849 // SECTION( "initialisation of operators" ) {
850
851 // /* compilers don't auto-initialise to NULL; the below circumstance
852 // * only really occurs when 'malloc' returns NULL in createComplexMatrixN,
853 // * which actually triggers its own validation. Hence this test is useless
854 // * currently.
855 // */
856
857 // int numTargs = NUM_QUBITS;
858 // int numOps = (2*numTargs)*(2*numTargs);
859
860 // // no need to initialise ops, but set their attribs correct to avoid triggering other validation
861 // vector<ComplexMatrixN> ops(numOps);
862 // for (int i=0; i<numOps; i++)
863 // ops[i].numQubits = numTargs;
864
865 // // make one of the max-ops explicitly NULL
866 // ops[GENERATE_COPY( range(0,numTargs) )].cpuElems = NULL;
867
868 // // make valid targets to avoid triggering target validation
869 // vector<int> targs(numTargs);
870 // for (int i=0; i<numTargs; i++)
871 // targs[i] = i;
872
873 // REQUIRE_THROWS_WITH( mixNonTPMultiQubitKrausMap(qureg, targs.data(), numTargs, ops.data(), numOps), ContainsSubstring("ComplexMatrixN") && ContainsSubstring("created") );
874 // }
875
876 // this validation cannot be performed by the deprecation API which copies
877 // over all ops arguments without prior checking them as matching in dimension
878
879 // SECTION( "dimension of operators" ) {
880
881 // // make valid (dimension-wise) max-qubits Kraus map
882 // int numTargs = NUM_QUBITS;
883 // int numOps = (2*numTargs)*(2*numTargs);
884 // vector<ComplexMatrixN> ops(numOps);
885 // for (int i=0; i<numOps; i++)
886 // ops[i] = createComplexMatrixN(numTargs);
887
888 // // make one have wrong-dimensions
889 // int badInd = GENERATE_COPY( range(0,numTargs) );
890 // destroyComplexMatrixN(ops[badInd]);
891 // ops[badInd] = createComplexMatrixN(numTargs - 1);
892
893 // // make valid targets to avoid triggering target validation
894 // vector<int> targs(numTargs);
895 // for (int i=0; i<numTargs; i++)
896 // targs[i] = i;
897
898 // REQUIRE_THROWS_WITH( mixNonTPMultiQubitKrausMap(qureg, targs.data(), numTargs, ops.data(), numOps), ContainsSubstring("same number of qubits") );
899
900 // for (int i=0; i<numOps; i++)
901 // destroyComplexMatrixN(ops[i]);
902 // }
903
904 SECTION( "density-matrix" ) {
905
906 Qureg statevec = createQureg(NUM_QUBITS);
907
908 // make valid targets to avoid triggering target validation
909 int targs[NUM_QUBITS];
910 for (int i=0; i<NUM_QUBITS; i++)
911 targs[i] = i;
912
913 REQUIRE_THROWS_WITH( mixNonTPMultiQubitKrausMap(statevec, targs, NUM_QUBITS, spoofOps, 1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
914 destroyQureg(statevec, getQuESTEnv());
915
916 }
917 SECTION( "operator fits in node" ) {
918
919 // each node requires (2 numTargs)^2 amplitudes
920 int minAmps = (2*NUM_QUBITS) * (2*NUM_QUBITS);
921
922 // make valid targets to avoid triggering target validation
923 int targs[NUM_QUBITS];
924 for (int i=0; i<NUM_QUBITS; i++)
925 targs[i] = i;
926
927 // make a simple Identity map
928 ComplexMatrixN ops[] = {createComplexMatrixN(NUM_QUBITS)};
929 for (int i=0; i<(1<<NUM_QUBITS); i++)
930 ops[0].cpuElems[i][i] = 1;
931 syncCompMatr(ops[0]);
932
933 // fake a smaller qureg
934 qureg.isDistributed = 1;
935 qureg.numAmpsPerNode = minAmps - 1;
936 qureg.logNumAmpsPerNode = log2(minAmps) - 1;
937 REQUIRE_THROWS_WITH( mixNonTPMultiQubitKrausMap(qureg, targs, NUM_QUBITS, ops, 1), ContainsSubstring("each node's communication buffer") && ContainsSubstring("cannot simultaneously store") );
938
939 destroyComplexMatrixN(ops[0]);
940 }
941
942 for (int i=0; i<NUM_QUBITS; i++)
943 destroyComplexMatrixN(spoofOps[i]);
944 }
945 destroyQureg(qureg, getQuESTEnv());
946}
947
948
949
950/** @sa mixNonTPTwoQubitKrausMap
951 * @ingroup deprecatedtests
952 * @author Tyson Jones
953 */
954TEST_CASE( "mixNonTPTwoQubitKrausMap", "[decoherence]" ) {
955
956 PREPARE_TEST(qureg, ref);
957
958 SECTION( "correctness" ) {
959
960 int targ1 = GENERATE( range(0,NUM_QUBITS) );
961 int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
962 int numOps = GENERATE( range(1,17) ); // max 16 inclusive
963
964 // map consists of unconstrained 4x4 random matrices
965 vector<QMatrix> matrs;
966 for (int i=0; i<numOps; i++)
967 matrs.push_back(getRandomQMatrix(4));
968
969 vector<ComplexMatrix4> ops(numOps);
970 for (int i=0; i<numOps; i++)
971 ops[i] = toComplexMatrix4(matrs[i]);
972 mixNonTPTwoQubitKrausMap(qureg, targ1, targ2, ops.data(), numOps);
973
974 // set ref -> K_i ref K_i^dagger
975 int targs[2] = {targ1, targ2};
976 vector<QMatrix> matrRefs(numOps);
977 for (int i=0; i<numOps; i++) {
978 matrRefs[i] = ref;
979 applyReferenceOp(matrRefs[i], targs, 2, matrs[i]);
980 }
981 ref = getZeroMatrix(ref.size());
982 for (int i=0; i<numOps; i++)
983 ref += matrRefs[i];
984
985 REQUIRE( areEqual(qureg, ref, 1E4*REAL_EPS) );
986 }
987 SECTION( "input validation" ) {
988
989 // deprecated v3 API copies ops before v4 validation, so we cannot pass ops=NULL
990 ComplexMatrix4 spoofOps[1];
991
992 // cannot test this with deprecated API, since 'numOps' informs a copy before validation
993
994 // SECTION( "number of operators" ) {
995
996 // int numOps = GENERATE( -1, 0 );
997 // REQUIRE_THROWS_WITH( mixNonTPTwoQubitKrausMap(qureg, 0,1, spoofOps, numOps), ContainsSubstring("must be given a strictly positive number of matrices") );
998 // }
999
1000 SECTION( "target collision" ) {
1001
1002 int target = GENERATE( range(0,NUM_QUBITS) );
1003 REQUIRE_THROWS_WITH( mixNonTPTwoQubitKrausMap(qureg, target, target, spoofOps, 1), ContainsSubstring("target qubits") && ContainsSubstring("unique") );
1004 }
1005 SECTION( "qubit index" ) {
1006
1007 int target = GENERATE( -1, NUM_QUBITS );
1008 REQUIRE_THROWS_WITH( mixNonTPTwoQubitKrausMap(qureg, 0,target, spoofOps, 1), ContainsSubstring("Invalid target qubit") );
1009 REQUIRE_THROWS_WITH( mixNonTPTwoQubitKrausMap(qureg, target,0, spoofOps, 1), ContainsSubstring("Invalid target qubit") );
1010 }
1011 SECTION( "density-matrix" ) {
1012
1013 Qureg vec = createQureg(NUM_QUBITS);
1014 REQUIRE_THROWS_WITH( mixNonTPTwoQubitKrausMap(vec, 0,1, spoofOps, 1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
1015 destroyQureg(vec, getQuESTEnv());
1016 }
1017 SECTION( "operators fit in node" ) {
1018
1019 qureg.isDistributed = 1;
1020 qureg.numAmpsPerNode = 15; // min 16
1021 qureg.logNumAmpsPerNode = 3; // min 4
1022 REQUIRE_THROWS_WITH( mixNonTPTwoQubitKrausMap(qureg, 0,1, spoofOps, 1), ContainsSubstring("each node's communication buffer") && ContainsSubstring("cannot simultaneously store") );
1023 }
1024 }
1025 destroyQureg(qureg, getQuESTEnv());
1026}
1027
1028
1029
1030/** @sa mixTwoQubitDephasing
1031 * @ingroup deprecatedtests
1032 * @author Tyson Jones
1033 */
1034TEST_CASE( "mixTwoQubitDephasing", "[decoherence]" ) {
1035
1036 PREPARE_TEST(qureg, ref);
1037
1038 SECTION( "correctness" ) {
1039
1040 int targ1 = GENERATE( range(0,NUM_QUBITS) );
1041 int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
1042 qreal prob = getRandomReal(0, 3/4.);
1043
1044 mixTwoQubitDephasing(qureg, targ1, targ2, prob);
1045
1046 // ref -> (1 - prob) ref + prob/3 (Z1 ref Z1 + Z2 ref Z2 + Z1 Z2 ref Z1 Z2)
1047 QMatrix zMatr{{1,0},{0,-1}};
1048 QMatrix z1Ref = ref;
1049 applyReferenceOp(z1Ref, targ1, zMatr); // Z1 ref Z1
1050 QMatrix z2Ref = ref;
1051 applyReferenceOp(z2Ref, targ2, zMatr); // Z2 ref Z2
1052 QMatrix z1z2Ref = ref;
1053 applyReferenceOp(z1z2Ref, targ1, zMatr);
1054 applyReferenceOp(z1z2Ref, targ2, zMatr); // Z1 Z2 ref Z1 Z2
1055 ref = ((1 - prob) * ref) + (prob/3.) * (z1Ref + z2Ref + z1z2Ref);
1056
1057 REQUIRE( areEqual(qureg, ref) );
1058 }
1059 SECTION( "input validation" ) {
1060
1061 SECTION( "qubit indices" ) {
1062
1063 int targ = GENERATE( -1, NUM_QUBITS );
1064 REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, 0, targ, 0), ContainsSubstring("Invalid target") );
1065 REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, targ, 0, 0), ContainsSubstring("Invalid target") );
1066 }
1067 SECTION( "target collision" ) {
1068
1069 int targ = GENERATE( range(0,NUM_QUBITS) );
1070 REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, targ, targ, 0), ContainsSubstring("target") && ContainsSubstring("unique") );
1071 }
1072 SECTION( "probability" ) {
1073
1074 REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, 0, 1, -.1), ContainsSubstring("probability is invalid") );
1075 REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, 0, 1, 3/4. + .01), ContainsSubstring("probability") && ContainsSubstring("3/4") );
1076 }
1077 SECTION( "density-matrix" ) {
1078
1079 Qureg vec = createQureg(NUM_QUBITS);
1080 REQUIRE_NOTHROW( mixTwoQubitDephasing(vec, 0, 1, 0) ); // zero-prob ok in v4
1081 REQUIRE_THROWS_WITH( mixTwoQubitDephasing(vec, 0, 1, 0.1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
1082 destroyQureg(vec, getQuESTEnv());
1083 }
1084 }
1085 destroyQureg(qureg, getQuESTEnv());
1086}
1087
1088
1089
1090/** @sa mixTwoQubitDepolarising
1091 * @ingroup deprecatedtests
1092 * @author Tyson Jones
1093 */
1094TEST_CASE( "mixTwoQubitDepolarising", "[decoherence]" ) {
1095
1096 PREPARE_TEST(qureg, ref);
1097
1098 SECTION( "correctness" ) {
1099
1100 int targ1 = GENERATE( range(0,NUM_QUBITS) );
1101 int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
1102 qreal prob = getRandomReal(0, 15/16.);
1103
1104 mixTwoQubitDepolarising(qureg, targ1, targ2, prob);
1105
1106 QMatrix paulis[4] = {
1107 QMatrix{{1,0},{0,1}}, // I
1108 QMatrix{{0,1},{1,0}}, // X
1109 QMatrix{{0,-qcomp(0,1)},{qcomp(0,1),0}}, // Y
1110 QMatrix{{1,0},{0,-1}} // Z
1111 };
1112
1113 int targs[2] = {targ1, targ2};
1114 QMatrix refInit = ref;
1115 ref = (1 - (16/15.)*prob) * ref;
1116 for (int i=0; i<4; i++) {
1117 for (int j=0; j<4; j++) {
1118 QMatrix term = refInit;
1119 QMatrix op = getKroneckerProduct(paulis[i], paulis[j]);
1120 applyReferenceOp(term, targs, 2, op);
1121 ref += (prob/15.) * term;
1122 }
1123 }
1124
1125 REQUIRE( areEqual(qureg, ref, 1E4*REAL_EPS) );
1126 }
1127 SECTION( "input validation" ) {
1128
1129 SECTION( "qubit indices" ) {
1130
1131 int targ = GENERATE( -1, NUM_QUBITS );
1132 REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, 0, targ, 0), ContainsSubstring("Invalid target") );
1133 REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, targ, 0, 0), ContainsSubstring("Invalid target") );
1134 }
1135 SECTION( "target collision" ) {
1136
1137 int targ = GENERATE( range(0,NUM_QUBITS) );
1138 REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, targ, targ, 0), ContainsSubstring("target") && ContainsSubstring("unique") );
1139 }
1140 SECTION( "probability" ) {
1141
1142 REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, 0, 1, -.1), ContainsSubstring("probability is invalid") );
1143 REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, 0, 1, 15/16. + .01), ContainsSubstring("probability") && ContainsSubstring("15/16") );
1144 }
1145 SECTION( "density-matrix" ) {
1146
1147 Qureg vec = createQureg(NUM_QUBITS);
1148 REQUIRE_NOTHROW( mixTwoQubitDepolarising(vec, 0, 1, 0) ); // zero prob ok in v4
1149 REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(vec, 0, 1, 0.1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
1150 destroyQureg(vec, getQuESTEnv());
1151 }
1152 }
1153 destroyQureg(qureg, getQuESTEnv());
1154}
1155
1156
1157
1158/** @sa mixTwoQubitKrausMap
1159 * @ingroup deprecatedtests
1160 * @author Tyson Jones
1161 */
1162TEST_CASE( "mixTwoQubitKrausMap", "[decoherence]" ) {
1163
1164 PREPARE_TEST(qureg, ref);
1165
1166 SECTION( "correctness" ) {
1167
1168 int targ1 = GENERATE( range(0,NUM_QUBITS) );
1169 int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) );
1170 int numOps = GENERATE( range(1,17) ); // max 16 inclusive
1171 vector<QMatrix> matrs = getRandomKrausMap(2, numOps);
1172
1173 vector<ComplexMatrix4> ops(numOps);
1174 for (int i=0; i<numOps; i++)
1175 ops[i] = toComplexMatrix4(matrs[i]);
1176 mixTwoQubitKrausMap(qureg, targ1, targ2, ops.data(), numOps);
1177
1178 // set ref -> K_i ref K_i^dagger
1179 int targs[2] = {targ1, targ2};
1180 vector<QMatrix> matrRefs(numOps);
1181 for (int i=0; i<numOps; i++) {
1182 matrRefs[i] = ref;
1183 applyReferenceOp(matrRefs[i], targs, 2, matrs[i]);
1184 }
1185 ref = getZeroMatrix(ref.size());
1186 for (int i=0; i<numOps; i++)
1187 ref += matrRefs[i];
1188
1189 REQUIRE( areEqual(qureg, ref, 10*REAL_EPS) );
1190 }
1191 SECTION( "input validation" ) {
1192
1193 ComplexMatrix4 emptyOps[1];
1194
1195 SECTION( "number of operators" ) {
1196
1197 // in v4, a separate createKausMap call is made which throws the below error
1198 int numOps = 0;
1199 REQUIRE_THROWS_WITH( mixTwoQubitKrausMap(qureg, 0,1, emptyOps, numOps), ContainsSubstring("must be given a strictly positive number of matrices") );
1200 }
1201 SECTION( "trace preserving" ) {
1202
1203 // valid Kraus map
1204 int numOps = GENERATE( range(1,16) );
1205 vector<QMatrix> matrs = getRandomKrausMap(2, numOps);
1206 vector<ComplexMatrix4> ops(numOps);
1207 for (int i=0; i<numOps; i++)
1208 ops[i] = toComplexMatrix4(matrs[i]);
1209
1210 // make only one of the ops at a time invalid
1211 ops[GENERATE_REF( range(0,numOps) )].real[0][0] = 999;
1212
1213 REQUIRE_THROWS_WITH( mixTwoQubitKrausMap(qureg, 0,1, ops.data(), numOps), ContainsSubstring("trace preserving") );
1214 }
1215 SECTION( "target collision" ) {
1216
1217 int target = GENERATE( range(0,NUM_QUBITS) );
1218 REQUIRE_THROWS_WITH( mixTwoQubitKrausMap(qureg, target, target, emptyOps, 1), ContainsSubstring("target qubits") && ContainsSubstring("unique") );
1219 }
1220 SECTION( "qubit index" ) {
1221
1222 int target = GENERATE( -1, NUM_QUBITS );
1223 REQUIRE_THROWS_WITH( mixTwoQubitKrausMap(qureg, 0,target, emptyOps, 1), ContainsSubstring("Invalid target qubit") );
1224 REQUIRE_THROWS_WITH( mixTwoQubitKrausMap(qureg, target,0, emptyOps, 1), ContainsSubstring("Invalid target qubit") );
1225 }
1226 SECTION( "density-matrix" ) {
1227
1228 Qureg vec = createQureg(NUM_QUBITS);
1229 REQUIRE_THROWS_WITH( mixTwoQubitKrausMap(vec, 0,1, emptyOps, 1), ContainsSubstring("Expected a density matrix Qureg but received a statevector") );
1230 destroyQureg(vec, getQuESTEnv());
1231 }
1232 SECTION( "operators fit in node" ) {
1233
1234 qureg.isDistributed = 1;
1235 qureg.numAmpsPerNode = 15; // min 16
1236 qureg.logNumAmpsPerNode = 3; // min 4
1237 REQUIRE_THROWS_WITH( mixTwoQubitKrausMap(qureg, 0,1, emptyOps, 1), ContainsSubstring("each node's communication buffer") && ContainsSubstring("cannot simultaneously store") );
1238 }
1239 }
1240 destroyQureg(qureg, getQuESTEnv());
1241}
void mixTwoQubitDephasing(Qureg qureg, int target1, int target2, qreal prob)
void mixTwoQubitDepolarising(Qureg qureg, int target1, int target2, qreal prob)
void mixDamping(Qureg qureg, int target, qreal prob)
void mixDephasing(Qureg qureg, int target, qreal prob)
void mixDepolarising(Qureg qureg, int target, qreal prob)
TEST_CASE("mixDamping", "[decoherence]")
ComplexMatrix4 toComplexMatrix4(QMatrix qm)
unsigned int calcLog2(long unsigned int res)
bool areEqual(QVector a, QVector b)
vector< vector< qcomp > > QMatrix
ComplexMatrix2 toComplexMatrix2(QMatrix qm)
void applyReferenceOp(QVector &state, int *ctrls, int numCtrls, int *targs, int numTargs, QMatrix op)
void toComplexMatrixN(QMatrix qm, ComplexMatrixN cm)
QMatrix getRandomQMatrix(int dim)
void setRandomTargets(int *targs, int numTargs, int numQb)
QMatrix toQMatrix(CompMatr1 src)
QuESTEnv getQuESTEnv()
void initDebugState(Qureg qureg)
CompMatr createCompMatr(int numQubits)
Definition matrices.cpp:213
void destroyCompMatr(CompMatr matrix)
Definition matrices.cpp:398
void setCompMatr(CompMatr matr, qcomp **vals)
Definition matrices.cpp:423
void syncCompMatr(CompMatr matr)
Definition matrices.cpp:373
Qureg createDensityQureg(int numQubits)
Definition qureg.cpp:291
Qureg createForcedDensityQureg(int numQubits)
Definition qureg.cpp:309
Qureg createQureg(int numQubits)
Definition qureg.cpp:283
void destroyQureg(Qureg qureg)
Definition qureg.cpp:334
qmatrix getKroneckerProduct(qmatrix a, qmatrix b)
Definition linalg.cpp:523
qmatrix getZeroMatrix(size_t dim)
Definition qmatrix.cpp:18
qreal getRandomReal(qreal min, qreal maxExcl)
Definition random.cpp:63
vector< qmatrix > getRandomKrausMap(int numQb, int numOps)
Definition random.cpp:405
Definition qureg.h:49