#include "catch.hpp" #include "QuEST.h" #include "utilities.hpp" #include /** Prepares a density matrix in the debug state, and the reference QMatrix */ #define PREPARE_TEST(qureg, ref) \ Qureg qureg = createDensityQureg(NUM_QUBITS, QUEST_ENV); \ initDebugState(qureg); \ QMatrix ref = toQMatrix(qureg); /* allows concise use of Contains in catch's REQUIRE_THROWS_WITH */ using Catch::Matchers::Contains; /** @sa mixDamping * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "mixDamping", "[decoherence]" ) { PREPARE_TEST(qureg, ref); SECTION( "correctness" ) { int target = GENERATE( range(0,NUM_QUBITS) ); qreal prob = getRandomReal(0, 1); mixDamping(qureg, target, prob); // ref -> kraus0 ref kraus0^dagger + kraus1 ref kraus1^dagger QMatrix kraus0{{1,0},{0,sqrt(1-prob)}}; QMatrix rho0 = ref; applyReferenceOp(rho0, target, kraus0); QMatrix kraus1{{0,sqrt(prob)},{0,0}}; QMatrix rho1 = ref; applyReferenceOp(rho1, target, kraus1); ref = rho0 + rho1; REQUIRE( areEqual(qureg, ref) ); } SECTION( "validation ") { SECTION( "qubit index" ) { int target = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( mixDamping(qureg, target, 0), Contains("Invalid target") ); } SECTION( "probability" ) { REQUIRE_THROWS_WITH( mixDamping(qureg, 0, -.1), Contains("Probabilities") ); REQUIRE_THROWS_WITH( mixDamping(qureg, 0, 1.1), Contains("Probabilities") ); } SECTION( "density-matrix" ) { Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV); REQUIRE_THROWS_WITH( mixDamping(vec, 0, 0), Contains("density matrices") ); destroyQureg(vec, QUEST_ENV); } } destroyQureg(qureg, QUEST_ENV); } /** @sa mixDensityMatrix * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "mixDensityMatrix", "[decoherence]" ) { Qureg qureg1 = createDensityQureg(NUM_QUBITS, QUEST_ENV); Qureg qureg2 = createDensityQureg(NUM_QUBITS, QUEST_ENV); initDebugState(qureg1); initDebugState(qureg2); QMatrix ref1 = toQMatrix(qureg1); QMatrix ref2 = toQMatrix(qureg2); SECTION( "correctness" ) { // test p in {0, 1} and 10 random values in (0,1) qreal prob = GENERATE( 0., 1., take(10, random(0.,1.)) ); mixDensityMatrix(qureg1, prob, qureg2); // ensure target qureg modified correctly ref1 = (1-prob)*ref1 + (prob)*ref2; REQUIRE( areEqual(qureg1, ref1) ); // enure other qureg was not modified REQUIRE( areEqual(qureg2, ref2) ); } SECTION( "input validation" ) { SECTION( "probabilities") { qreal prob = GENERATE( -0.1, 1.1 ); REQUIRE_THROWS_WITH( mixDensityMatrix(qureg1, prob, qureg2), Contains("Probabilities") ); } SECTION( "density matrices" ) { // one is statevec Qureg state1 = createQureg(qureg1.numQubitsRepresented, QUEST_ENV); REQUIRE_THROWS_WITH( mixDensityMatrix(qureg1, 0, state1), Contains("density matrices") ); REQUIRE_THROWS_WITH( mixDensityMatrix(state1, 0, qureg1), Contains("density matrices") ); // both are statevec Qureg state2 = createQureg(qureg1.numQubitsRepresented, QUEST_ENV); REQUIRE_THROWS_WITH( mixDensityMatrix(state1, 0, state2), Contains("density matrices") ); destroyQureg(state1, QUEST_ENV); destroyQureg(state2, QUEST_ENV); } SECTION( "matching dimensions" ) { Qureg qureg3 = createDensityQureg(1 + qureg1.numQubitsRepresented, QUEST_ENV); REQUIRE_THROWS_WITH( mixDensityMatrix(qureg1, 0, qureg3), Contains("Dimensions") ); REQUIRE_THROWS_WITH( mixDensityMatrix(qureg3, 0, qureg1), Contains("Dimensions") ); destroyQureg(qureg3, QUEST_ENV); } } destroyQureg(qureg1, QUEST_ENV); destroyQureg(qureg2, QUEST_ENV); } /** @sa mixDephasing * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "mixDephasing", "[decoherence]" ) { PREPARE_TEST(qureg, ref); SECTION( "correctness " ) { int target = GENERATE( range(0,NUM_QUBITS) ); qreal prob = getRandomReal(0, 1/2.); mixDephasing(qureg, target, prob); // ref -> (1 - prob) ref + prob Z ref Z QMatrix phaseRef = ref; applyReferenceOp(phaseRef, target, QMatrix{{1,0},{0,-1}}); // Z ref Z ref = ((1 - prob) * ref) + (prob * phaseRef); REQUIRE( areEqual(qureg, ref) ); } SECTION( "validation ") { SECTION( "qubit index" ) { int target = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( mixDephasing(qureg, target, 0), Contains("Invalid target") ); } SECTION( "probability" ) { REQUIRE_THROWS_WITH( mixDephasing(qureg, 0, -.1), Contains("Probabilities") ); REQUIRE_THROWS_WITH( mixDephasing(qureg, 0, .6), Contains("probability") && Contains("cannot exceed 1/2") ); } SECTION( "density-matrix" ) { Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV); REQUIRE_THROWS_WITH( mixDephasing(vec, 0, 0), Contains("density matrices") ); destroyQureg(vec, QUEST_ENV); } } destroyQureg(qureg, QUEST_ENV); } /** @sa mixDepolarising * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "mixDepolarising", "[decoherence]" ) { PREPARE_TEST(qureg, ref); SECTION( "correctness " ) { int target = GENERATE( range(0,NUM_QUBITS) ); qreal prob = getRandomReal(0, 3/4.); mixDepolarising(qureg, target, prob); QMatrix xRef = ref; applyReferenceOp(xRef, target, QMatrix{{0,1},{1,0}}); // X ref X QMatrix yRef = ref; applyReferenceOp(yRef, target, QMatrix{{0,-1i},{1i,0}}); // Y ref Y QMatrix zRef = ref; applyReferenceOp(zRef, target, QMatrix{{1,0},{0,-1}}); // Z ref Z ref = ((1 - prob) * ref) + ((prob/3.) * ( xRef + yRef + zRef)); REQUIRE( areEqual(qureg, ref) ); } SECTION( "validation ") { SECTION( "qubit index" ) { int target = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( mixDepolarising(qureg, target, 0), Contains("Invalid target") ); } SECTION( "probability" ) { REQUIRE_THROWS_WITH( mixDepolarising(qureg, 0, -.1), Contains("Probabilities") ); REQUIRE_THROWS_WITH( mixDepolarising(qureg, 0, .76), Contains("probability") && Contains("cannot exceed 3/4") ); } SECTION( "density-matrix" ) { Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV); REQUIRE_THROWS_WITH( mixDepolarising(vec, 0, 0), Contains("density matrices") ); destroyQureg(vec, QUEST_ENV); } } destroyQureg(qureg, QUEST_ENV); } /** @sa mixMultiQubitKrausMap * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "mixMultiQubitKrausMap", "[decoherence]" ) { PREPARE_TEST(qureg, ref); // figure out max-num (inclusive) targs allowed by hardware backend // (each node must contain as 2^(2*numTargs) amps) int maxNumTargs = calcLog2(qureg.numAmpsPerChunk) / 2; SECTION( "correctness" ) { /* note that this function incurs a stack overhead when numTargs < 4, * and a heap overhead when numTargs >= 4 */ int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); // inclusive upper bound // note this is very expensive to try every arrangement (2 min runtime for numTargs=5 alone) int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) ); // try the min and max number of operators, and 2 random numbers // (there are way too many to try all!) int maxNumOps = (2*numTargs)*(2*numTargs); int numOps = GENERATE_COPY( 1, maxNumOps, take(2,random(1,maxNumOps)) ); // use a new random map std::vector matrs = getRandomKrausMap(numTargs, numOps); // create map in QuEST datatypes ComplexMatrixN ops[numOps]; for (int i=0; i K_i ref K_i^dagger QMatrix matrRefs[numOps]; for (int i=0; i matrs = getRandomKrausMap(numTargs, numOps); ComplexMatrixN ops[numOps]; for (int i=0; i matrs = getRandomKrausMap(1, numOps); ComplexMatrix2 ops[numOps]; for (int i=0; i K_i ref K_i^dagger QMatrix matrRefs[numOps]; for (int i=0; i matrs = getRandomKrausMap(1, numOps); ComplexMatrix2 ops[numOps]; for (int i=0; i (1 - prob) ref + prob/3 (Z1 ref Z1 + Z2 ref Z2 + Z1 Z2 ref Z1 Z2) QMatrix zMatr{{1,0},{0,-1}}; QMatrix z1Ref = ref; applyReferenceOp(z1Ref, targ1, zMatr); // Z1 ref Z1 QMatrix z2Ref = ref; applyReferenceOp(z2Ref, targ2, zMatr); // Z2 ref Z2 QMatrix z1z2Ref = ref; applyReferenceOp(z1z2Ref, targ1, zMatr); applyReferenceOp(z1z2Ref, targ2, zMatr); // Z1 Z2 ref Z1 Z2 ref = ((1 - prob) * ref) + (prob/3.) * (z1Ref + z2Ref + z1z2Ref); REQUIRE( areEqual(qureg, ref) ); } SECTION( "input validation" ) { SECTION( "qubit indices" ) { int targ = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, 0, targ, 0), Contains("Invalid target") ); REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, targ, 0, 0), Contains("Invalid target") ); } SECTION( "target collision" ) { int targ = GENERATE( range(0,NUM_QUBITS) ); REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, targ, targ, 0), Contains("target") && Contains("unique") ); } SECTION( "probability" ) { REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, 0, 1, -.1), Contains("Probabilities") ); REQUIRE_THROWS_WITH( mixTwoQubitDephasing(qureg, 0, 1, 3/4. + .01), Contains("probability") && Contains("cannot exceed 3/4") ); } SECTION( "density-matrix" ) { Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV); REQUIRE_THROWS_WITH( mixTwoQubitDephasing(vec, 0, 1, 0), Contains("density matrices") ); destroyQureg(vec, QUEST_ENV); } } destroyQureg(qureg, QUEST_ENV); } /** @sa mixTwoQubitDepolarising * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "mixTwoQubitDepolarising", "[decoherence]" ) { PREPARE_TEST(qureg, ref); SECTION( "correctness" ) { int targ1 = GENERATE( range(0,NUM_QUBITS) ); int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) ); qreal prob = getRandomReal(0, 15/16.); mixTwoQubitDepolarising(qureg, targ1, targ2, prob); QMatrix paulis[4] = { QMatrix{{1,0},{0,1}}, // I QMatrix{{0,1},{1,0}}, // X QMatrix{{0,-1i},{1i,0}}, // Y QMatrix{{1,0},{0,-1}} // Z }; int targs[2] = {targ1, targ2}; QMatrix refInit = ref; ref = (1 - (16/15.)*prob) * ref; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { QMatrix term = refInit; applyReferenceOp(term, targs, 2, getKroneckerProduct(paulis[i], paulis[j])); ref += (prob/15.) * term; } } REQUIRE( areEqual(qureg, ref, 1E4*REAL_EPS) ); } SECTION( "input validation" ) { SECTION( "qubit indices" ) { int targ = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, 0, targ, 0), Contains("Invalid target") ); REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, targ, 0, 0), Contains("Invalid target") ); } SECTION( "target collision" ) { int targ = GENERATE( range(0,NUM_QUBITS) ); REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, targ, targ, 0), Contains("target") && Contains("unique") ); } SECTION( "probability" ) { REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, 0, 1, -.1), Contains("Probabilities") ); REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(qureg, 0, 1, 15/16. + .01), Contains("probability") && Contains("cannot exceed 15/16") ); } SECTION( "density-matrix" ) { Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV); REQUIRE_THROWS_WITH( mixTwoQubitDepolarising(vec, 0, 1, 0), Contains("density matrices") ); destroyQureg(vec, QUEST_ENV); } } destroyQureg(qureg, QUEST_ENV); } /** @sa mixTwoQubitKrausMap * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "mixTwoQubitKrausMap", "[decoherence]" ) { PREPARE_TEST(qureg, ref); SECTION( "correctness" ) { int targ1 = GENERATE( range(0,NUM_QUBITS) ); int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) ); int numOps = GENERATE( range(1,17) ); // max 16 inclusive std::vector matrs = getRandomKrausMap(2, numOps); ComplexMatrix4 ops[numOps]; for (int i=0; i K_i ref K_i^dagger int targs[2] = {targ1, targ2}; QMatrix matrRefs[numOps]; for (int i=0; i matrs = getRandomKrausMap(2, numOps); ComplexMatrix4 ops[numOps]; for (int i=0; i