/** @file * Unit testing for QuEST's 'unitaries' API. * The tests are in alphabetical order of the API doc. * * These tests work by constructing, from the unitary specification (e.g. * control and target qubits), a full-Hilbert space complex matrix. This is * then multiplied onto statevectors, or multiplied and it's conjugate-transpose * right-multiplied onto density matrices. * * QuEST's user validation handling is unit tested by redefining exitWithError * (a weak C symbol) to throw a C++ exception, caught by the Catch2 library. * * @author Tyson Jones */ #include "catch.hpp" #include "QuEST.h" #include "utilities.hpp" /** Prepares the needed data structures for unit testing unitaries. * This creates a statevector and density matrix of the size NUM_QUBITS, * and corresponding QVector and QMatrix instances for analytic comparison. */ #define PREPARE_TEST(quregVec, quregMatr, refVec, refMatr) \ Qureg quregVec = createQureg(NUM_QUBITS, QUEST_ENV); \ Qureg quregMatr = createDensityQureg(NUM_QUBITS, QUEST_ENV); \ initDebugState(quregVec); \ initDebugState(quregMatr); \ QVector refVec = toQVector(quregVec); \ QMatrix refMatr = toQMatrix(quregMatr); /** Destroys the data structures made by PREPARE_TEST */ #define CLEANUP_TEST(quregVec, quregMatr) \ destroyQureg(quregVec, QUEST_ENV); \ destroyQureg(quregMatr, QUEST_ENV); /* allows concise use of Contains in catch's REQUIRE_THROWS_WITH */ using Catch::Matchers::Contains; /** @sa compactUnitary * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "compactUnitary", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); qcomp a = getRandomReal(-1,1) * expI(getRandomReal(0,2*M_PI)); qcomp b = sqrt(1-abs(a)*abs(a)) * expI(getRandomReal(0,2*M_PI)); Complex alpha = toComplex( a ); Complex beta = toComplex( b ); QMatrix op = toQMatrix(alpha, beta); SECTION( "correctness" ) { int target = GENERATE( range(0,NUM_QUBITS) ); SECTION( "state-vector" ) { compactUnitary(quregVec, target, alpha, beta); applyReferenceOp(refVec, target, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { compactUnitary(quregMatr, target, alpha, beta); applyReferenceOp(refMatr, target, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "qubit indices" ) { int target = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( compactUnitary(quregVec, target, alpha, beta), Contains("Invalid target") ); } SECTION( "unitarity" ) { // unitary when |alpha|^2 + |beta|^2 = 1 alpha = {.real=1, .imag=2}; beta = {.real=3, .imag=4}; REQUIRE_THROWS_WITH( compactUnitary(quregVec, 0, alpha, beta), Contains("unitary") ); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa controlledCompactUnitary * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "controlledCompactUnitary", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); qcomp a = getRandomReal(-1,1) * expI(getRandomReal(0,2*M_PI)); qcomp b = sqrt(1-abs(a)*abs(a)) * expI(getRandomReal(0,2*M_PI)); Complex alpha = toComplex( a ); Complex beta = toComplex( b ); QMatrix op = toQMatrix(alpha, beta); SECTION( "correctness" ) { int target = GENERATE( range(0,NUM_QUBITS) ); int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) ); SECTION( "state-vector" ) { controlledCompactUnitary(quregVec, control, target, alpha, beta); applyReferenceOp(refVec, control, target, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { controlledCompactUnitary(quregMatr, control, target, alpha, beta); applyReferenceOp(refMatr, control, target, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "control and target collision" ) { int qb = 0; REQUIRE_THROWS_WITH( controlledCompactUnitary(quregVec, qb, qb, alpha, beta), Contains("Control") && Contains("target") ); } SECTION( "qubit indices" ) { int qb = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( controlledCompactUnitary(quregVec, qb, 0, alpha, beta), Contains("Invalid control") ); REQUIRE_THROWS_WITH( controlledCompactUnitary(quregVec, 0, qb, alpha, beta), Contains("Invalid target") ); } SECTION( "unitarity" ) { // unitary when |a|^2 + |b^2 = 1 alpha = {.real=1, .imag=2}; beta = {.real=3, .imag=4}; REQUIRE_THROWS_WITH( controlledCompactUnitary(quregVec, 0, 1, alpha, beta), Contains("unitary") ); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa controlledMultiQubitUnitary * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "controlledMultiQubitUnitary", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); // figure out max-num targs (inclusive) allowed by hardware backend int maxNumTargs = calcLog2(quregVec.numAmpsPerChunk); if (maxNumTargs >= NUM_QUBITS) maxNumTargs = NUM_QUBITS - 1; // make space for control qubit SECTION( "correctness" ) { // generate all possible qubit arrangements int ctrl = GENERATE( range(0,NUM_QUBITS) ); int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs, ctrl) ); // for each qubit arrangement, use a new random unitary QMatrix op = getRandomUnitary(numTargs); ComplexMatrixN matr = createComplexMatrixN(numTargs); toComplexMatrixN(op, matr); SECTION( "state-vector" ) { controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr); applyReferenceOp(refVec, ctrl, targs, numTargs, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { controlledMultiQubitUnitary(quregMatr, ctrl, targs, numTargs, matr); applyReferenceOp(refMatr, ctrl, targs, numTargs, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } destroyComplexMatrixN(matr); } SECTION( "input validation" ) { SECTION( "number of targets" ) { // there cannot be more targets than qubits in register // (numTargs=NUM_QUBITS is caught elsewhere, because that implies ctrl is invalid) int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 ); int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger ComplexMatrixN matr = createComplexMatrixN(NUM_QUBITS+1); // prevent seg-fault REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, 0, targs, numTargs, matr), Contains("Invalid number of target")); destroyComplexMatrixN(matr); } SECTION( "repetition in targets" ) { int ctrl = 0; int numTargs = 3; int targs[] = {1,2,2}; ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("target") && Contains("unique")); destroyComplexMatrixN(matr); } SECTION( "control and target collision" ) { int numTargs = 3; int targs[] = {0,1,2}; int ctrl = targs[GENERATE_COPY( range(0,numTargs) )]; ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("Control") && Contains("target")); destroyComplexMatrixN(matr); } SECTION( "qubit indices" ) { int ctrl = 0; int numTargs = 3; int targs[] = {1,2,3}; ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger int inv = GENERATE( -1, NUM_QUBITS ); ctrl = inv; REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("Invalid control") ); ctrl = 0; // restore valid ctrl targs[GENERATE_COPY( range(0,numTargs) )] = inv; // make invalid target REQUIRE_THROWS_WITH( controlledMultiQubitUnitary(quregVec, ctrl, targs, numTargs, matr), Contains("Invalid target") ); destroyComplexMatrixN(matr); } SECTION( "unitarity" ) { int ctrl = 0; int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); ComplexMatrixN matr = createComplexMatrixN(numTargs); // initially zero, hence not-unitary int targs[numTargs]; for (int i=0; i= 4 ); // every test will use a unique random matrix QMatrix op = getRandomUnitary(2); ComplexMatrix4 matr = toComplexMatrix4(op); SECTION( "correctness" ) { int targ1 = GENERATE( range(0,NUM_QUBITS) ); int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) ); int control = GENERATE_COPY( filter([=](int c){ return c!=targ1 && c!=targ2; }, range(0,NUM_QUBITS)) ); SECTION( "state-vector" ) { controlledTwoQubitUnitary(quregVec, control, targ1, targ2, matr); applyReferenceOp(refVec, control, targ1, targ2, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { controlledTwoQubitUnitary(quregMatr, control, targ1, targ2, matr); applyReferenceOp(refMatr, control, targ1, targ2, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "repetition of targets" ) { int targ = 0; int ctrl = 1; REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, ctrl, targ, targ, matr), Contains("target") && Contains("unique") ); } SECTION( "control and target collision" ) { int targ1 = 1; int targ2 = 2; int ctrl = GENERATE( 1,2 ); // catch2 bug; can't do GENERATE_COPY( targ1, targ2 ) REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, ctrl, targ1, targ2, matr), Contains("Control") && Contains("target") ); } SECTION( "qubit indices" ) { // valid config int ctrl = 0; int targ1 = 1; int targ2 = 2; int qb = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, qb, targ1, targ2, matr), Contains("Invalid control") ); REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, ctrl, qb, targ2, matr), Contains("Invalid target") ); REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, ctrl, targ1, qb, matr), Contains("Invalid target") ); } SECTION( "unitarity" ) { matr.real[0][0] = 0; // break matr unitarity REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, 0, 1, 2, matr), Contains("unitary") ); } SECTION( "unitary fits in node" ) { // pretend we have a very limited distributed memory quregVec.numAmpsPerChunk = 1; REQUIRE_THROWS_WITH( controlledTwoQubitUnitary(quregVec, 0, 1, 2, matr), Contains("targets too many qubits")); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa controlledUnitary * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "controlledUnitary", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); QMatrix op = getRandomUnitary(1); ComplexMatrix2 matr = toComplexMatrix2(op); SECTION( "correctness" ) { int target = GENERATE( range(0,NUM_QUBITS) ); int control = GENERATE_COPY( filter([=](int c){ return c!=target; }, range(0,NUM_QUBITS)) ); SECTION( "state-vector" ) { controlledUnitary(quregVec, control, target, matr); applyReferenceOp(refVec, control, target, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { controlledUnitary(quregMatr, control, target, matr); applyReferenceOp(refMatr, control, target, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "control and target collision" ) { int qb = GENERATE( range(0,NUM_QUBITS) ); REQUIRE_THROWS_WITH( controlledUnitary(quregVec, qb, qb, matr), Contains("Control") && Contains("target") ); } SECTION( "qubit indices" ) { int qb = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( controlledUnitary(quregVec, qb, 0, matr), Contains("Invalid control") ); REQUIRE_THROWS_WITH( controlledUnitary(quregVec, 0, qb, matr), Contains("Invalid target") ); } SECTION( "unitarity" ) { matr.real[0][0] = 0; // break unitarity REQUIRE_THROWS_WITH( controlledUnitary(quregVec, 0, 1, matr), Contains("unitary") ); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa hadamard * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "hadamard", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); qreal a = 1/sqrt(2); QMatrix op{{a,a},{a,-a}}; SECTION( "correctness" ) { int target = GENERATE( range(0,NUM_QUBITS) ); SECTION( "state-vector ") { hadamard(quregVec, target); applyReferenceOp(refVec, target, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { hadamard(quregMatr, target); applyReferenceOp(refMatr, target, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "qubit indices" ) { int target = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( hadamard(quregVec, target), Contains("Invalid target") ); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa multiControlledMultiQubitUnitary * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "multiControlledMultiQubitUnitary", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); // figure out max-num targs (inclusive) allowed by hardware backend int maxNumTargs = calcLog2(quregVec.numAmpsPerChunk); if (maxNumTargs >= NUM_QUBITS) maxNumTargs = NUM_QUBITS - 1; // leave room for min-number of control qubits SECTION( "correctness" ) { // try all possible numbers of targets and controls int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); int maxNumCtrls = NUM_QUBITS - numTargs; int numCtrls = GENERATE_COPY( range(1,maxNumCtrls+1) ); // generate all possible valid qubit arrangements int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) ); int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, targs, numTargs) ); // for each qubit arrangement, use a new random unitary QMatrix op = getRandomUnitary(numTargs); ComplexMatrixN matr = createComplexMatrixN(numTargs); toComplexMatrixN(op, matr); SECTION( "state-vector" ) { multiControlledMultiQubitUnitary(quregVec, ctrls, numCtrls, targs, numTargs, matr); applyReferenceOp(refVec, ctrls, numCtrls, targs, numTargs, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { multiControlledMultiQubitUnitary(quregMatr, ctrls, numCtrls, targs, numTargs, matr); applyReferenceOp(refMatr, ctrls, numCtrls, targs, numTargs, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } destroyComplexMatrixN(matr); } SECTION( "input validation" ) { SECTION( "number of targets" ) { // there cannot be more targets than qubits in register // (numTargs=NUM_QUBITS is caught elsewhere, because that implies ctrls are invalid) int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 ); int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger int ctrls[] = {0}; ComplexMatrixN matr = createComplexMatrixN(NUM_QUBITS+1); // prevent seg-fault toComplexMatrixN(getRandomUnitary(NUM_QUBITS+1), matr); // ensure unitary REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 1, targs, numTargs, matr), Contains("Invalid number of target")); destroyComplexMatrixN(matr); } SECTION( "repetition in targets" ) { int ctrls[] = {0}; int numTargs = 3; int targs[] = {1,2,2}; ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger toComplexMatrixN(getRandomUnitary(numTargs), matr); // ensure unitary REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 1, targs, numTargs, matr), Contains("target") && Contains("unique")); destroyComplexMatrixN(matr); } SECTION( "number of controls" ) { int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 ); int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered int targs[1] = {0}; ComplexMatrixN matr = createComplexMatrixN(1); toComplexMatrixN(getRandomUnitary(1), matr); // ensure unitary REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, numCtrls, targs, 1, matr), Contains("Invalid number of control")); destroyComplexMatrixN(matr); } SECTION( "repetition in controls" ) { int ctrls[] = {0,1,1}; int targs[] = {3}; ComplexMatrixN matr = createComplexMatrixN(1); toComplexMatrixN(getRandomUnitary(1), matr); // ensure unitary REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 3, targs, 1, matr), Contains("control") && Contains("unique")); destroyComplexMatrixN(matr); } SECTION( "control and target collision" ) { int ctrls[] = {0,1,2}; int targs[] = {3,1,4}; ComplexMatrixN matr = createComplexMatrixN(3); toComplexMatrixN(getRandomUnitary(3), matr); // ensure unitary REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, ctrls, 3, targs, 3, matr), Contains("Control") && Contains("target") && Contains("disjoint")); destroyComplexMatrixN(matr); } SECTION( "qubit indices" ) { // valid inds int numQb = 2; int qb1[2] = {0,1}; int qb2[2] = {2,3}; ComplexMatrixN matr = createComplexMatrixN(numQb); toComplexMatrixN(getRandomUnitary(numQb), matr); // ensure unitary // make qb1 invalid int inv = GENERATE( -1, NUM_QUBITS ); qb1[GENERATE_COPY(range(0,numQb))] = inv; REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, qb1, numQb, qb2, numQb, matr), Contains("Invalid control") ); REQUIRE_THROWS_WITH( multiControlledMultiQubitUnitary(quregVec, qb2, numQb, qb1, numQb, matr), Contains("Invalid target") ); destroyComplexMatrixN(matr); } SECTION( "unitarity" ) { int ctrls[1] = {0}; int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); int targs[numTargs]; for (int i=0; i= 4 ); // every test will use a unique random matrix QMatrix op = getRandomUnitary(2); ComplexMatrix4 matr = toComplexMatrix4(op); SECTION( "correctness" ) { // generate ALL valid qubit arrangements int targ1 = GENERATE( range(0,NUM_QUBITS) ); int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) ); int targs[] = {targ1, targ2}; int numCtrls = GENERATE( range(1,NUM_QUBITS-1) ); // leave room for 2 targets (upper bound is exclusive) int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, targs, 2) ); SECTION( "state-vector" ) { multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr); applyReferenceOp(refVec, ctrls, numCtrls, targ1, targ2, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { multiControlledTwoQubitUnitary(quregMatr, ctrls, numCtrls, targ1, targ2, matr); applyReferenceOp(refMatr, ctrls, numCtrls, targ1, targ2, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "number of controls" ) { // numCtrls=(NUM_QUBITS-1) is ok since requires ctrl qubit inds are invalid int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 ); int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, 0, 1, matr), Contains("Invalid number of control")); } SECTION( "repetition of controls" ) { int numCtrls = 3; int ctrls[] = {0,1,1}; int targ1 = 2; int targ2 = 3; REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr), Contains("control") && Contains("unique"));; } SECTION( "repetition of targets" ) { int numCtrls = 3; int ctrls[] = {0,1,2}; int targ1 = 3; int targ2 = targ1; REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr), Contains("target") && Contains("unique")); } SECTION( "control and target collision" ) { int numCtrls = 3; int ctrls[] = {0,1,2}; int targ1 = 3; int targ2 = ctrls[GENERATE_COPY( range(0,numCtrls) )]; REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr), Contains("Control") && Contains("target") ); } SECTION( "qubit indices" ) { // valid indices int targ1 = 0; int targ2 = 1; int numCtrls = 3; int ctrls[] = { 2, 3, 4 }; int inv = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, inv, targ2, matr), Contains("Invalid target") ); REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, inv, matr), Contains("Invalid target") ); ctrls[numCtrls-1] = inv; // make ctrls invalid REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, numCtrls, targ1, targ2, matr), Contains("Invalid control") ); } SECTION( "unitarity " ) { int ctrls[1] = {0}; matr.real[0][0] = 0; // break unitarity REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, 1, 1, 2, matr), Contains("unitary") ); } SECTION( "unitary fits in node" ) { // pretend we have a very limited distributed memory quregVec.numAmpsPerChunk = 1; int ctrls[1] = {0}; REQUIRE_THROWS_WITH( multiControlledTwoQubitUnitary(quregVec, ctrls, 1, 1, 2, matr), Contains("targets too many qubits")); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa multiControlledUnitary * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "multiControlledUnitary", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); // every test will use a unique random matrix QMatrix op = getRandomUnitary(1); ComplexMatrix2 matr = toComplexMatrix2(op); SECTION( "correctness" ) { int target = GENERATE( range(0,NUM_QUBITS) ); int numCtrls = GENERATE( range(1,NUM_QUBITS) ); // leave space for one target (exclusive upper bound) int* ctrls = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numCtrls, target) ); SECTION( "state-vector" ) { multiControlledUnitary(quregVec, ctrls, numCtrls, target, matr); applyReferenceOp(refVec, ctrls, numCtrls, target, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { multiControlledUnitary(quregMatr, ctrls, numCtrls, target, matr); applyReferenceOp(refMatr, ctrls, numCtrls, target, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "number of controls" ) { int numCtrls = GENERATE( -1, 0, NUM_QUBITS, NUM_QUBITS+1 ); int ctrls[NUM_QUBITS+1]; // avoids seg-fault if validation not triggered REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, numCtrls, 0, matr), Contains("Invalid number of control")); } SECTION( "repetition of controls" ) { int ctrls[] = {0,1,1}; REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 3, 2, matr), Contains("control") && Contains("unique")); } SECTION( "control and target collision" ) { int ctrls[] = {0,1,2}; int targ = ctrls[GENERATE( range(0,3) )]; REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 3, targ, matr), Contains("Control") && Contains("target") ); } SECTION( "qubit indices" ) { int ctrls[] = { 1, 2, GENERATE( -1, NUM_QUBITS ) }; REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 3, 0, matr), Contains("Invalid control") ); ctrls[2] = 3; // make ctrls valid int targ = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 3, targ, matr), Contains("Invalid target") ); } SECTION( "unitarity" ) { matr.real[0][0] = 0; // break matr unitarity int ctrls[] = {0}; REQUIRE_THROWS_WITH( multiControlledUnitary(quregVec, ctrls, 1, 1, matr), Contains("unitary") ); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa multiQubitUnitary * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "multiQubitUnitary", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); // figure out max-num (inclusive) targs allowed by hardware backend int maxNumTargs = calcLog2(quregVec.numAmpsPerChunk); SECTION( "correctness" ) { // generate all possible qubit arrangements int numTargs = GENERATE_COPY( range(1,maxNumTargs+1) ); // inclusive upper bound int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) ); // for each qubit arrangement, use a new random unitary QMatrix op = getRandomUnitary(numTargs); ComplexMatrixN matr = createComplexMatrixN(numTargs); toComplexMatrixN(op, matr); SECTION( "state-vector" ) { multiQubitUnitary(quregVec, targs, numTargs, matr); applyReferenceOp(refVec, targs, numTargs, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { multiQubitUnitary(quregMatr, targs, numTargs, matr); applyReferenceOp(refMatr, targs, numTargs, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } destroyComplexMatrixN(matr); } SECTION( "input validation" ) { SECTION( "number of targets" ) { // there cannot be more targets than qubits in register int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 ); int targs[NUM_QUBITS+1]; // prevents seg-fault if validation doesn't trigger ComplexMatrixN matr = createComplexMatrixN(NUM_QUBITS+1); // prevent seg-fault REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, numTargs, matr), Contains("Invalid number of target")); destroyComplexMatrixN(matr); } SECTION( "repetition in targets" ) { int numTargs = 3; int targs[] = {1,2,2}; ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, numTargs, matr), Contains("target") && Contains("unique")); destroyComplexMatrixN(matr); } SECTION( "qubit indices" ) { int numTargs = 3; int targs[] = {1,2,3}; ComplexMatrixN matr = createComplexMatrixN(numTargs); // prevents seg-fault if validation doesn't trigger int inv = GENERATE( -1, NUM_QUBITS ); targs[GENERATE_COPY( range(0,numTargs) )] = inv; // make invalid target REQUIRE_THROWS_WITH( multiQubitUnitary(quregVec, targs, numTargs, matr), Contains("Invalid target") ); destroyComplexMatrixN(matr); } SECTION( "unitarity" ) { int numTargs = GENERATE_COPY( range(1,maxNumTargs) ); int targs[numTargs]; for (int i=0; i 0) op = getExponentialOfPauliMatrix(param, pauliProd); SECTION( "state-vector" ) { multiRotatePauli(quregVec, targs, paulis, numTargs, param); if (numRefTargs > 0) applyReferenceOp(refVec, refTargs, numRefTargs, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { multiRotatePauli(quregMatr, targs, paulis, numTargs, param); if (numRefTargs > 0) applyReferenceOp(refMatr, refTargs, numRefTargs, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "number of targets" ) { int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 ); int targs[NUM_QUBITS+1]; // prevent seg-fault if validation isn't triggered pauliOpType paulis[NUM_QUBITS+1] = {PAULI_I}; REQUIRE_THROWS_WITH( multiRotatePauli(quregVec, targs, paulis, numTargs, param), Contains("Invalid number of target")); } SECTION( "repetition of targets" ) { int numTargs = 3; int targs[3] = {0, 1, 1}; pauliOpType paulis[3] = {PAULI_I}; REQUIRE_THROWS_WITH( multiRotatePauli(quregVec, targs, paulis, numTargs, param), Contains("target") && Contains("unique")); } SECTION( "qubit indices" ) { int numTargs = 3; int targs[3] = {0, 1, 2}; targs[GENERATE_COPY(range(0,numTargs))] = GENERATE( -1, NUM_QUBITS ); pauliOpType paulis[3] = {PAULI_I}; REQUIRE_THROWS_WITH( multiRotatePauli(quregVec, targs, paulis, numTargs, param), Contains("Invalid target")); } SECTION( "pauli codes" ) { int numTargs = 3; int targs[3] = {0, 1, 2}; pauliOpType paulis[3] = {PAULI_I, PAULI_I, PAULI_I}; paulis[GENERATE_COPY(range(0,numTargs))] = (pauliOpType) GENERATE( -1, 4 ); REQUIRE_THROWS_WITH( multiRotatePauli(quregVec, targs, paulis, numTargs, param), Contains("Invalid Pauli code")); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa multiRotateZ * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "multiRotateZ", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); qreal param = getRandomReal(-4*M_PI, 4*M_PI); SECTION( "correctness" ) { int numTargs = GENERATE( range(1,NUM_QUBITS+1) ); int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) ); // build correct reference matrix by diagonal-matrix exponentiation... QMatrix zMatr{{1,0},{0,-1}}; QMatrix zProd = zMatr; for (int t=0; t= 4 ); // every test will use a unique random matrix QMatrix op = getRandomUnitary(2); ComplexMatrix4 matr = toComplexMatrix4(op); SECTION( "correctness" ) { int targ1 = GENERATE( range(0,NUM_QUBITS) ); int targ2 = GENERATE_COPY( filter([=](int t){ return t!=targ1; }, range(0,NUM_QUBITS)) ); int targs[] = {targ1, targ2}; SECTION( "state-vector" ) { twoQubitUnitary(quregVec, targ1, targ2, matr); applyReferenceOp(refVec, targs, 2, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { twoQubitUnitary(quregMatr, targ1, targ2, matr); applyReferenceOp(refMatr, targs, 2, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "qubit indices" ) { int targ1 = GENERATE( -1, NUM_QUBITS ); int targ2 = 0; REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, targ1, targ2, matr), Contains("Invalid target") ); REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, targ2, targ1, matr), Contains("Invalid target") ); } SECTION( "repetition of targets" ) { int qb = 0; REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, qb, qb, matr), Contains("target") && Contains("unique") ); } SECTION( "unitarity" ) { matr.real[0][0] = 0; // break matr unitarity REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, 0, 1, matr), Contains("unitary") ); } SECTION( "unitary fits in node" ) { // pretend we have a very limited distributed memory quregVec.numAmpsPerChunk = 1; REQUIRE_THROWS_WITH( twoQubitUnitary(quregVec, 0, 1, matr), Contains("targets too many qubits")); } } CLEANUP_TEST( quregVec, quregMatr ); } /** @sa unitary * @ingroup unittest * @author Tyson Jones */ TEST_CASE( "unitary", "[unitaries]" ) { PREPARE_TEST( quregVec, quregMatr, refVec, refMatr ); // every test will use a unique random matrix QMatrix op = getRandomUnitary(1); ComplexMatrix2 matr = toComplexMatrix2(op); SECTION( "correctness" ) { int target = GENERATE( range(0,NUM_QUBITS) ); SECTION( "state-vector" ) { unitary(quregVec, target, matr); applyReferenceOp(refVec, target, op); REQUIRE( areEqual(quregVec, refVec) ); } SECTION( "density-matrix" ) { unitary(quregMatr, target, matr); applyReferenceOp(refMatr, target, op); REQUIRE( areEqual(quregMatr, refMatr, 10*REAL_EPS) ); } } SECTION( "input validation" ) { SECTION( "qubit indices" ) { int target = GENERATE( -1, NUM_QUBITS ); REQUIRE_THROWS_WITH( unitary(quregVec, target, matr), Contains("Invalid target") ); } SECTION( "unitarity" ) { matr.real[0][0] = 0; // break matr unitarity REQUIRE_THROWS_WITH( unitary(quregVec, 0, matr), Contains("unitary") ); } } CLEANUP_TEST( quregVec, quregMatr ); }