#include "catch.hpp"
#include "QuEST.h"
#include "utilities.hpp"
using Catch::Matchers::Contains;
TEST_CASE( "calcDensityInnerProduct", "[calculations]" ) {
Qureg mat1 = createDensityQureg(NUM_QUBITS, QUEST_ENV);
Qureg mat2 = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
GENERATE( range(0,10) );
SECTION( "density-matrix" ) {
SECTION( "pure" ) {
QVector r1 = getRandomStateVector(NUM_QUBITS);
toQureg(mat1, getKetBra(r1,r1));
QVector r2 = getRandomStateVector(NUM_QUBITS);
toQureg(mat2, getKetBra(r2,r2));
qcomp prod = 0;
for (size_t i=0; i<r1.size(); i++)
prod += conj(r1[i]) * r2[i];
qreal densProd = pow(abs(prod),2);
REQUIRE( calcDensityInnerProduct(mat1,mat2) == Approx(densProd) );
}
SECTION( "mixed" ) {
QMatrix ref1 = getRandomDensityMatrix(NUM_QUBITS);
QMatrix ref2 = getRandomDensityMatrix(NUM_QUBITS);
toQureg(mat1, ref1);
toQureg(mat2, ref2);
qcomp refProd = 0;
for (size_t i=0; i<ref1.size(); i++)
for (size_t j=0; j<ref1.size(); j++)
refProd += conj(ref1[i][j]) * ref2[i][j];
REQUIRE( imag(refProd) == Approx(0).margin(REAL_EPS) );
REQUIRE( calcDensityInnerProduct(mat1,mat2) == Approx(real(refProd)) );
REQUIRE( calcDensityInnerProduct(mat1,mat2) == Approx(calcDensityInnerProduct(mat2,mat1)) );
}
SECTION( "unnormalised" ) {
QMatrix ref1 = getRandomQMatrix(1<<NUM_QUBITS);
QMatrix ref2 = getRandomQMatrix(1<<NUM_QUBITS);
toQureg(mat1, ref1);
toQureg(mat2, ref2);
qcomp refProd = 0;
for (size_t i=0; i<ref1.size(); i++)
for (size_t j=0; j<ref1.size(); j++)
refProd += conj(ref1[i][j]) * ref2[i][j];
REQUIRE( calcDensityInnerProduct(mat1,mat2) == Approx(real(refProd)) );
}
}
}
SECTION( "input validation" ) {
SECTION( "dimensions" ) {
Qureg mat3 = createDensityQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcDensityInnerProduct(mat1,mat3), Contains("Dimensions") && Contains("don't match") );
destroyQureg(mat3, QUEST_ENV);
}
SECTION( "state-vectors" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( calcDensityInnerProduct(mat1,vec), Contains("valid only for density matrices") );
REQUIRE_THROWS_WITH( calcDensityInnerProduct(vec,mat1), Contains("valid only for density matrices") );
REQUIRE_THROWS_WITH( calcDensityInnerProduct(vec,vec), Contains("valid only for density matrices") );
destroyQureg(vec, QUEST_ENV);
}
}
destroyQureg(mat1, QUEST_ENV);
destroyQureg(mat2, QUEST_ENV);
}
TEST_CASE( "calcExpecPauliProd", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
initDebugState(vec);
initDebugState(mat);
QVector vecRef = toQVector(vec);
QMatrix matRef = toQMatrix(mat);
Qureg vecWork = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg matWork = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
int numTargs = GENERATE( range(1,NUM_QUBITS+1) );
int* targs = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numTargs) );
GENERATE( range(0,10) ); pauliOpType paulis[numTargs];
for (int i=0; i<numTargs; i++)
paulis[i] = (pauliOpType) getRandomInt(0,4);
QMatrix iMatr{{1,0},{0,1}};
QMatrix xMatr{{0,1},{1,0}};
QMatrix yMatr{{0,-1i},{1i,0}};
QMatrix zMatr{{1,0},{0,-1}};
QMatrix pauliProd{{1}};
for (int i=0; i<numTargs; i++) {
QMatrix fac;
if (paulis[i] == PAULI_I) fac = iMatr;
if (paulis[i] == PAULI_X) fac = xMatr;
if (paulis[i] == PAULI_Y) fac = yMatr;
if (paulis[i] == PAULI_Z) fac = zMatr;
pauliProd = getKroneckerProduct(fac, pauliProd);
}
SECTION( "state-vector" ) {
QVector prodRef = vecRef;
applyReferenceOp(prodRef, targs, numTargs, pauliProd);
qcomp prod = 0;
for (size_t i=0; i<vecRef.size(); i++)
prod += conj(vecRef[i]) * prodRef[i];
REQUIRE( imag(prod) == Approx(0).margin(REAL_EPS) );
qreal res = calcExpecPauliProd(vec, targs, paulis, numTargs, vecWork);
REQUIRE( res == Approx(real(prod)).margin(REAL_EPS) );
}
SECTION( "density matrix" ) {
QMatrix fullOp = getFullOperatorMatrix(NULL, 0, targs, numTargs, pauliProd, NUM_QUBITS);
matRef = fullOp * matRef;
qreal tr = 0;
for (size_t i=0; i<matRef.size(); i++)
tr += real(matRef[i][i]);
qreal res = calcExpecPauliProd(mat, targs, paulis, numTargs, matWork);
REQUIRE( res == Approx(tr).margin(10*REAL_EPS) );
}
}
SECTION( "validation" ) {
SECTION( "number of targets" ) {
int numTargs = GENERATE( -1, 0, NUM_QUBITS+1 );
REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, NULL, NULL, numTargs, vecWork), Contains("Invalid number of target") );
}
SECTION( "target indices" ) {
int numTargs = 3;
int targs[3] = {0, 1, 2};
targs[GENERATE( range(0,3) )] = GENERATE( -1, NUM_QUBITS );
REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, NULL, numTargs, vecWork), Contains("Invalid target qubit") );
}
SECTION( "repetition in targets" ) {
int numTargs = 3;
int targs[3] = {0, 1, 1};
REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, NULL, numTargs, vecWork), Contains("target qubits must be unique") );
}
SECTION( "pauli codes" ) {
int numTargs = 3;
int targs[3] = {0, 1, 2};
pauliOpType codes[3] = {PAULI_X, PAULI_Y, PAULI_Z};
codes[GENERATE( range(0,3) )] = (pauliOpType) GENERATE( -1, 4 );
REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, codes, numTargs, vecWork), Contains("Invalid Pauli code") );
}
SECTION( "workspace type" ) {
int numTargs = 1;
int targs[1] = {0};
pauliOpType codes[1] = {PAULI_I};
REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, codes, numTargs, matWork), Contains("Registers must both be state-vectors or both be density matrices") );
REQUIRE_THROWS_WITH( calcExpecPauliProd(mat, targs, codes, numTargs, vecWork), Contains("Registers must both be state-vectors or both be density matrices") );
}
SECTION( "workspace dimensions" ) {
int numTargs = 1;
int targs[1] = {0};
pauliOpType codes[1] = {PAULI_I};
Qureg vec2 = createQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcExpecPauliProd(vec, targs, codes, numTargs, vec2), Contains("Dimensions") && Contains("don't match") );
destroyQureg(vec2, QUEST_ENV);
Qureg mat2 = createDensityQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcExpecPauliProd(mat, targs, codes, numTargs, mat2), Contains("Dimensions") && Contains("don't match") );
destroyQureg(mat2, QUEST_ENV);
}
}
destroyQureg(vec, QUEST_ENV);
destroyQureg(mat, QUEST_ENV);
destroyQureg(vecWork, QUEST_ENV);
destroyQureg(matWork, QUEST_ENV);
}
TEST_CASE( "calcExpecPauliSum", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
initDebugState(vec);
initDebugState(mat);
QVector vecRef = toQVector(vec);
QMatrix matRef = toQMatrix(mat);
Qureg vecWork = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg matWork = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
int numSumTerms = GENERATE( 1, 2, 10, 15 );
GENERATE( range(0,10) );
int totNumCodes = numSumTerms*NUM_QUBITS;
pauliOpType paulis[totNumCodes];
for (int i=0; i<totNumCodes; i++)
paulis[i] = (pauliOpType) getRandomInt(0,4);
qreal coeffs[numSumTerms];
for (int i=0; i<numSumTerms; i++)
coeffs[i] = getRandomReal(-5, 5);
QMatrix iMatr{{1,0},{0,1}};
QMatrix xMatr{{0,1},{1,0}};
QMatrix yMatr{{0,-1i},{1i,0}};
QMatrix zMatr{{1,0},{0,-1}};
QMatrix pauliSum = getZeroMatrix(1<<NUM_QUBITS);
for (int t=0; t<numSumTerms; t++) {
QMatrix pauliProd = QMatrix{{1}};
for (int q=0; q<NUM_QUBITS; q++) {
int i = q + t*NUM_QUBITS;
QMatrix fac;
if (paulis[i] == PAULI_I) fac = iMatr;
if (paulis[i] == PAULI_X) fac = xMatr;
if (paulis[i] == PAULI_Y) fac = yMatr;
if (paulis[i] == PAULI_Z) fac = zMatr;
pauliProd = getKroneckerProduct(fac, pauliProd);
}
pauliSum += coeffs[t] * pauliProd;
}
SECTION( "state-vector" ) {
QVector sumRef = pauliSum * vecRef;
qcomp prod = 0;
for (size_t i=0; i<vecRef.size(); i++)
prod += conj(vecRef[i]) * sumRef[i];
REQUIRE( imag(prod) == Approx(0).margin(10*REAL_EPS) );
qreal res = calcExpecPauliSum(vec, paulis, coeffs, numSumTerms, vecWork);
REQUIRE( res == Approx(real(prod)).margin(10*REAL_EPS) );
}
SECTION( "density matrix" ) {
matRef = pauliSum * matRef;
qreal tr = 0;
for (size_t i=0; i<matRef.size(); i++)
tr += real(matRef[i][i]);
qreal res = calcExpecPauliSum(mat, paulis, coeffs, numSumTerms, matWork);
REQUIRE( res == Approx(tr).margin(1E2*REAL_EPS) );
}
}
SECTION( "validation" ) {
SECTION( "number of sum terms" ) {
int numSumTerms = GENERATE( -1, 0 );
REQUIRE_THROWS_WITH( calcExpecPauliSum(vec, NULL, NULL, numSumTerms, vecWork), Contains("Invalid number of terms in the Pauli sum") );
}
SECTION( "pauli codes" ) {
int numSumTerms = 3;
qreal coeffs[numSumTerms];
pauliOpType codes[numSumTerms*NUM_QUBITS];
for (int i=0; i<numSumTerms*NUM_QUBITS; i++)
codes[i] = PAULI_I;
codes[GENERATE_COPY( range(0,numSumTerms*NUM_QUBITS) )] = (pauliOpType) GENERATE( -1, 4 );
REQUIRE_THROWS_WITH( calcExpecPauliSum(vec, codes, coeffs, numSumTerms, vecWork), Contains("Invalid Pauli code") );
}
SECTION( "workspace type" ) {
int numSumTerms = 1;
qreal coeffs[1] = {0};
pauliOpType codes[NUM_QUBITS];
for (int i=0; i<NUM_QUBITS; i++)
codes[i] = PAULI_I;
REQUIRE_THROWS_WITH( calcExpecPauliSum(vec, codes, coeffs, numSumTerms, mat), Contains("Registers must both be state-vectors or both be density matrices") );
REQUIRE_THROWS_WITH( calcExpecPauliSum(mat, codes, coeffs, numSumTerms, vec), Contains("Registers must both be state-vectors or both be density matrices") );
}
SECTION( "workspace dimensions" ) {
int numSumTerms = 1;
qreal coeffs[1] = {0};
pauliOpType codes[NUM_QUBITS];
for (int i=0; i<NUM_QUBITS; i++)
codes[i] = PAULI_I;
Qureg vec2 = createQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcExpecPauliSum(vec, codes, coeffs, numSumTerms, vec2), Contains("Dimensions") && Contains("don't match") );
destroyQureg(vec2, QUEST_ENV);
Qureg mat2 = createDensityQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcExpecPauliSum(mat, codes, coeffs, numSumTerms, mat2), Contains("Dimensions") && Contains("don't match") );
destroyQureg(mat2, QUEST_ENV);
}
}
destroyQureg(vec, QUEST_ENV);
destroyQureg(mat, QUEST_ENV);
destroyQureg(vecWork, QUEST_ENV);
destroyQureg(matWork, QUEST_ENV);
}
TEST_CASE( "calcFidelity", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
Qureg pure = createQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
GENERATE( range(0,10) );
SECTION( "state-vector" ) {
SECTION( "normalised" ) {
QVector vecRef = getRandomStateVector(NUM_QUBITS);
QVector pureRef = getRandomStateVector(NUM_QUBITS);
toQureg(vec, vecRef);
toQureg(pure, pureRef);
REQUIRE( calcFidelity(vec,vec) == Approx(1) );
qcomp dotProd = 0;
for (size_t i=0; i<vecRef.size(); i++)
dotProd += conj(vecRef[i]) * pureRef[i];
qreal refFid = pow(abs(dotProd), 2);
REQUIRE( calcFidelity(vec,pure) == Approx(refFid) );
}
SECTION( "unnormalised" ) {
QVector vecRef = getRandomQVector(1<<NUM_QUBITS);
QVector pureRef = getRandomQVector(1<<NUM_QUBITS);
toQureg(vec, vecRef);
toQureg(pure, pureRef);
qreal nv = 0;
for (size_t i=0; i<vecRef.size(); i++)
nv += pow(abs(vecRef[i]), 2);
REQUIRE( calcFidelity(vec,vec) == Approx( nv*nv ) );
qcomp dotProd = 0;
for (size_t i=0; i<vecRef.size(); i++)
dotProd += conj(vecRef[i]) * pureRef[i];
qreal refFid = pow(abs(dotProd), 2);
REQUIRE( calcFidelity(vec,pure) == Approx(refFid) );
}
}
SECTION( "density-matrix" ) {
SECTION( "pure" ) {
QVector pureRef = getRandomStateVector(NUM_QUBITS);
toQureg(pure, pureRef);
QMatrix matRef = getKetBra(pureRef, pureRef);
toQureg(mat, matRef);
REQUIRE( calcFidelity(mat,pure) == Approx(1) );
QVector r1 = getRandomStateVector(NUM_QUBITS);
matRef = getKetBra(r1, r1); toQureg(mat, matRef);
qcomp dotProd = 0;
for (size_t i=0; i<r1.size(); i++)
dotProd += conj(r1[i]) * pureRef[i];
qreal refFid = pow(abs(dotProd), 2);
REQUIRE( calcFidelity(mat,pure) == Approx(refFid) );
}
SECTION( "mixed" ) {
QVector pureRef = getRandomStateVector(NUM_QUBITS);
toQureg(pure, pureRef);
QMatrix matRef = getRandomDensityMatrix(NUM_QUBITS);
toQureg(mat, matRef);
QVector rhs = matRef * pureRef;
qcomp dotProd = 0;
for (size_t i=0; i<rhs.size(); i++)
dotProd += conj(pureRef[i]) * rhs[i];
REQUIRE( imag(dotProd) == Approx(0).margin(REAL_EPS) );
REQUIRE( calcFidelity(mat,pure) == Approx(real(dotProd)) );
}
SECTION( "unnormalised" ) {
QVector pureRef = getRandomQVector(1<<NUM_QUBITS);
QMatrix matRef = getRandomQMatrix(1<<NUM_QUBITS);
toQureg(pure, pureRef);
toQureg(mat, matRef);
QVector rhs = matRef * pureRef;
qcomp dotProd = 0;
for (size_t i=0; i<rhs.size(); i++)
dotProd += conj(pureRef[i]) * rhs[i];
REQUIRE( calcFidelity(mat,pure) == Approx(real(dotProd)) );
}
}
}
SECTION( "input validation" ) {
SECTION( "dimensions" ) {
Qureg vec2 = createQureg(vec.numQubitsRepresented + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcFidelity(vec2,vec), Contains("Dimensions") && Contains("don't match") );
destroyQureg(vec2, QUEST_ENV);
Qureg mat2 = createDensityQureg(vec.numQubitsRepresented + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcFidelity(mat2,vec), Contains("Dimensions") && Contains("don't match") );
destroyQureg(mat2, QUEST_ENV);
}
SECTION( "density-matrices" ) {
REQUIRE_THROWS_WITH( calcFidelity(mat,mat), Contains("Second argument must be a state-vector") );
}
}
destroyQureg(vec, QUEST_ENV);
destroyQureg(mat, QUEST_ENV);
destroyQureg(pure, QUEST_ENV);
}
TEST_CASE( "calcHilbertSchmidtDistance", "[calculations]" ) {
Qureg mat1 = createDensityQureg(NUM_QUBITS, QUEST_ENV);
Qureg mat2 = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
GENERATE( range(0,10) );
SECTION( "density-matrix" ) {
SECTION( "pure" ) {
QVector r1 = getRandomStateVector(NUM_QUBITS);
QMatrix m1 = getKetBra(r1,r1);
toQureg(mat1, m1);
QVector r2 = getRandomStateVector(NUM_QUBITS);
QMatrix m2 = getKetBra(r2,r2);
toQureg(mat2, m2);
qreal tr = 0;
for (size_t i=0; i<m1.size(); i++)
for (size_t j=0; j<m1.size(); j++)
tr += pow(abs(m1[i][j] - m2[i][j]), 2);
qreal res = calcHilbertSchmidtDistance(mat1, mat2);
REQUIRE( res == Approx(sqrt(tr)) );
}
SECTION( "normalised" ) {
QMatrix ref1 = getRandomDensityMatrix(NUM_QUBITS);
QMatrix ref2 = getRandomDensityMatrix(NUM_QUBITS);
toQureg(mat1, ref1);
toQureg(mat2, ref2);
qreal tr = 0;
for (size_t i=0; i<ref1.size(); i++)
for (size_t j=0; j<ref1.size(); j++)
tr += pow(abs(ref1[i][j] - ref2[i][j]), 2);
qreal res = calcHilbertSchmidtDistance(mat1, mat2);
REQUIRE( res == Approx(sqrt(tr)) );
}
SECTION( "unnormalised" ) {
QMatrix ref1 = getRandomQMatrix(1<<NUM_QUBITS);
QMatrix ref2 = getRandomQMatrix(1<<NUM_QUBITS);
toQureg(mat1, ref1);
toQureg(mat2, ref2);
qreal tr = 0;
for (size_t i=0; i<ref1.size(); i++)
for (size_t j=0; j<ref1.size(); j++)
tr += pow(abs(ref1[i][j] - ref2[i][j]), 2);
qreal res = calcHilbertSchmidtDistance(mat1, mat2);
REQUIRE( res == Approx(sqrt(tr)) );
}
}
}
SECTION( "input validation") {
SECTION( "dimensions" ) {
Qureg mat3 = createDensityQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcHilbertSchmidtDistance(mat1,mat3), Contains("Dimensions") && Contains("don't match") );
destroyQureg(mat3, QUEST_ENV);
}
SECTION( "state-vector" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( calcHilbertSchmidtDistance(vec,mat1), Contains("valid only for density matrices") );
REQUIRE_THROWS_WITH( calcHilbertSchmidtDistance(mat1,vec), Contains("valid only for density matrices") );
REQUIRE_THROWS_WITH( calcHilbertSchmidtDistance(vec,vec), Contains("valid only for density matrices") );
destroyQureg(vec, QUEST_ENV);
}
}
destroyQureg(mat1, QUEST_ENV);
destroyQureg(mat2, QUEST_ENV);
}
TEST_CASE( "calcInnerProduct", "[calculations]" ) {
Qureg vec1 = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg vec2 = createQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
GENERATE( range(0,10) );
SECTION( "state-vector" ) {
SECTION( "normalised" ) {
QVector r1 = getRandomStateVector(NUM_QUBITS);
QVector r2 = getRandomStateVector(NUM_QUBITS);
qcomp prod = 0;
for (size_t i=0; i<r1.size(); i++)
prod += conj(r1[i]) * r2[i];
toQureg(vec1, r1);
toQureg(vec2, r2);
Complex res = calcInnerProduct(vec1,vec2);
REQUIRE( res.real == Approx(real(prod)) );
REQUIRE( res.imag == Approx(imag(prod)) );
}
SECTION( "unnormalised" ) {
QVector r1 = getRandomQVector(1<<NUM_QUBITS);
QVector r2 = getRandomQVector(1<<NUM_QUBITS);
qcomp prod = 0;
for (size_t i=0; i<r1.size(); i++)
prod += conj(r1[i]) * r2[i];
toQureg(vec1, r1);
toQureg(vec2, r2);
Complex res = calcInnerProduct(vec1,vec2);
REQUIRE( res.real == Approx(real(prod)) );
REQUIRE( res.imag == Approx(imag(prod)) );
}
}
}
SECTION( "input validation" ) {
SECTION( "dimensions" ) {
Qureg vec3 = createQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcInnerProduct(vec1,vec3), Contains("Dimensions") && Contains("don't match") );
destroyQureg(vec3, QUEST_ENV);
}
SECTION( "density-matrix" ) {
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( calcInnerProduct(vec1,mat), Contains("valid only for state-vectors") );
REQUIRE_THROWS_WITH( calcInnerProduct(mat,vec1), Contains("valid only for state-vectors") );
REQUIRE_THROWS_WITH( calcInnerProduct(mat,mat), Contains("valid only for state-vectors") );
destroyQureg(mat, QUEST_ENV);
}
}
destroyQureg(vec1, QUEST_ENV);
destroyQureg(vec2, QUEST_ENV);
}
TEST_CASE( "calcProbOfOutcome", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
int target = GENERATE( range(0,NUM_QUBITS) );
int outcome = GENERATE( 0, 1 );
SECTION( "state-vector" ) {
SECTION( "normalised" ) {
QVector ref = getRandomStateVector(NUM_QUBITS);
toQureg(vec, ref);
qreal prob = 0;
for (size_t ind=0; ind<ref.size(); ind++) {
int bit = (ind >> target) & 1; if (bit == outcome)
prob += pow(abs(ref[ind]), 2);
}
REQUIRE( calcProbOfOutcome(vec, target, outcome) == Approx(prob) );
}
SECTION( "unnormalised" ) {
QVector ref = getRandomQVector(1<<NUM_QUBITS);
toQureg(vec, ref);
qreal prob = 0;
for (size_t ind=0; ind<ref.size(); ind++) {
int bit = (ind >> target) & 1; if (bit == 0)
prob += pow(abs(ref[ind]), 2);
}
if (outcome == 1)
prob = 1 - prob;
REQUIRE( calcProbOfOutcome(vec, target, outcome) == Approx(prob) );
}
}
SECTION( "density-matrix" ) {
SECTION( "pure" ) {
QVector ref = getRandomStateVector(NUM_QUBITS);
toQureg(mat, getKetBra(ref, ref));
qreal prob = 0;
for (size_t ind=0; ind<ref.size(); ind++) {
int bit = (ind >> target) & 1; if (bit == outcome)
prob += pow(abs(ref[ind]), 2);
}
REQUIRE( calcProbOfOutcome(mat, target, outcome) == Approx(prob) );
}
SECTION( "mixed" ) {
QMatrix ref = getRandomDensityMatrix(NUM_QUBITS);
toQureg(mat, ref);
qcomp tr = 0;
for (size_t ind=0; ind<ref.size(); ind++) {
int bit = (ind >> target) & 1; if (bit == outcome)
tr += ref[ind][ind];
}
REQUIRE( imag(tr) == Approx(0).margin(REAL_EPS) );
REQUIRE( calcProbOfOutcome(mat, target, outcome) == Approx(real(tr)) );
}
SECTION( "unnormalised" ) {
QMatrix ref = getRandomQMatrix(1<<NUM_QUBITS);
toQureg(mat, ref);
qreal tr = 0;
for (size_t ind=0; ind<ref.size(); ind++) {
int bit = (ind >> target) & 1; if (bit == 0)
tr += real(ref[ind][ind]);
}
if (outcome == 1)
tr = 1 - tr;
REQUIRE( calcProbOfOutcome(mat, target, outcome) == Approx(tr) );
}
}
}
SECTION( "validation" ) {
SECTION( "qubit indices" ) {
int target = GENERATE( -1, NUM_QUBITS );
REQUIRE_THROWS_WITH( calcProbOfOutcome(vec, target, 0), Contains("Invalid target qubit") );
}
SECTION( "outcome value" ) {
int outcome = GENERATE( -1, 2 );
REQUIRE_THROWS_WITH( calcProbOfOutcome(vec, 0, outcome), Contains("Invalid measurement outcome") );
}
}
destroyQureg(vec, QUEST_ENV);
destroyQureg(mat, QUEST_ENV);
}
TEST_CASE( "calcPurity", "[calculations]" ) {
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
GENERATE( range(1,10) );
SECTION( "density-matrix" ) {
SECTION( "pure" ) {
initZeroState(mat);
REQUIRE( calcPurity(mat) == 1 );
QVector r1 = getRandomStateVector(NUM_QUBITS); QMatrix m1 = getKetBra(r1, r1); toQureg(mat, m1);
REQUIRE( calcPurity(mat) == Approx(1) );
}
SECTION( "mixed" ) {
QMatrix ref = getRandomDensityMatrix(NUM_QUBITS);
toQureg(mat, ref);
qreal purity = calcPurity(mat);
REQUIRE( purity < 1 );
REQUIRE( purity >= 1/pow(2.,NUM_QUBITS) );
QMatrix prod = ref*ref;
qreal tr = 0;
for (size_t i=0; i<prod.size(); i++)
tr += real(prod[i][i]);
REQUIRE( purity == Approx(tr) );
}
SECTION( "unnormalised" ) {
QMatrix ref = getRandomQMatrix(1<<NUM_QUBITS);
qreal tot = 0;
for (size_t i=0; i<ref.size(); i++)
for (size_t j=0; j<ref.size(); j++)
tot += pow(abs(ref[i][j]), 2);
toQureg(mat, ref);
REQUIRE( calcPurity(mat) == Approx(tot) );
}
}
}
SECTION( "input validation" ) {
SECTION( "state-vector" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( calcPurity(vec), Contains("valid only for density matrices") );
destroyQureg(vec, QUEST_ENV);
}
}
destroyQureg(mat, QUEST_ENV);
}
TEST_CASE( "calcTotalProb", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
SECTION( "state-vector" ) {
initPlusState(vec);
REQUIRE( calcTotalProb(vec) == Approx(1) );
initBlankState(vec);
REQUIRE( calcTotalProb(vec) == 0 );
toQureg(vec, getRandomStateVector(NUM_QUBITS));
REQUIRE( calcTotalProb(vec) == Approx(1) );
initDebugState(vec);
QVector ref = toQVector(vec);
qreal refProb = 0;
for (size_t i=0; i<ref.size(); i++)
refProb += pow(abs(ref[i]), 2);
REQUIRE( calcTotalProb(vec) == Approx(refProb) );
}
SECTION( "density-matrix" ) {
initPlusState(mat);
REQUIRE( calcTotalProb(mat) == Approx(1) );
initBlankState(mat);
REQUIRE( calcTotalProb(mat) == 0 );
toQureg(mat, getRandomDensityMatrix(NUM_QUBITS));
REQUIRE( calcTotalProb(mat) == Approx(1) );
initDebugState(mat);
QMatrix ref = toQMatrix(mat);
qreal refProb = 0;
for (size_t i=0; i<ref.size(); i++)
refProb += real(ref[i][i]);
REQUIRE( calcTotalProb(mat) == Approx(refProb) );
}
}
SECTION( "input validation" ) {
SUCCEED();
}
destroyQureg(vec, QUEST_ENV);
destroyQureg(mat, QUEST_ENV);
}
TEST_CASE( "getAmp", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
SECTION( "state-vector" ) {
initDebugState(vec);
QVector ref = toQVector(vec);
int ind = GENERATE( range(0,1<<NUM_QUBITS) );
Complex amp = getAmp(vec,ind);
REQUIRE( fromComplex(amp) == ref[ind] );
}
}
SECTION( "input validation" ) {
SECTION( "state index" ) {
int ind = GENERATE( -1, 1<<NUM_QUBITS );
REQUIRE_THROWS_WITH( getAmp(vec,ind), Contains("Invalid amplitude index") );
}
SECTION( "density-matrix" ) {
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( getAmp(mat,0), Contains("valid only for state-vectors") );
destroyQureg(mat, QUEST_ENV);
}
}
destroyQureg(vec, QUEST_ENV);
}
TEST_CASE( "getDensityAmp", "[calculations]" ) {
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
SECTION( "density-matrix" ) {
initDebugState(mat);
QMatrix ref = toQMatrix(mat);
int row = GENERATE( range(0,1<<NUM_QUBITS) );
int col = GENERATE( range(0,1<<NUM_QUBITS) );
Complex amp = getDensityAmp(mat,row,col);
REQUIRE( fromComplex(amp) == ref[row][col] );
}
}
SECTION( "input validation" ) {
SECTION( "state index" ) {
int ind = GENERATE( -1, 1<<NUM_QUBITS );
REQUIRE_THROWS_WITH( getDensityAmp(mat,ind,0), Contains("Invalid amplitude index") );
REQUIRE_THROWS_WITH( getDensityAmp(mat,0,ind), Contains("Invalid amplitude index") );
}
SECTION( "state-vector" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( getDensityAmp(vec,0,0), Contains("valid only for density matrices") );
destroyQureg(vec, QUEST_ENV);
}
}
destroyQureg(mat, QUEST_ENV);
}
TEST_CASE( "getImagAmp", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
SECTION( "state-vector" ) {
initDebugState(vec);
QVector ref = toQVector(vec);
int ind = GENERATE( range(0,1<<NUM_QUBITS) );
REQUIRE( getImagAmp(vec,ind) == imag(ref[ind]) );
}
}
SECTION( "input validation" ) {
SECTION( "state index" ) {
int ind = GENERATE( -1, 1<<NUM_QUBITS );
REQUIRE_THROWS_WITH( getImagAmp(vec,ind), Contains("Invalid amplitude index") );
}
SECTION( "density-matrix" ) {
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( getImagAmp(mat,0), Contains("valid only for state-vectors") );
destroyQureg(mat, QUEST_ENV);
}
}
destroyQureg(vec, QUEST_ENV);
}
TEST_CASE( "getNumAmps", "[calculations]" ) {
SECTION( "correctness" ) {
int numQb = GENERATE( range(NUM_QUBITS, NUM_QUBITS+10) );
SECTION( "state-vector" ) {
Qureg vec = createQureg(numQb, QUEST_ENV);
REQUIRE( getNumAmps(vec) == (1<<numQb) );
destroyQureg(vec, QUEST_ENV);
}
}
SECTION( "input validation" ) {
SECTION( "density-matrix" ) {
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( getNumAmps(mat), Contains("valid only for state-vectors") );
destroyQureg(mat, QUEST_ENV);
}
}
}
TEST_CASE( "getNumQubits", "[calculations]" ) {
SECTION( "correctness" ) {
int numQb = GENERATE( range(NUM_QUBITS, NUM_QUBITS+10) );
SECTION( "state-vector" ) {
Qureg vec = createQureg(numQb, QUEST_ENV);
REQUIRE( getNumQubits(vec) == numQb );
destroyQureg(vec, QUEST_ENV);
}
SECTION( "density-matrix" ) {
Qureg mat = createDensityQureg(numQb, QUEST_ENV);
REQUIRE( getNumQubits(mat) == numQb );
destroyQureg(mat, QUEST_ENV);
}
}
SECTION( "input validation" ) {
SUCCEED();
}
}
TEST_CASE( "getProbAmp", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
SECTION( "state-vector" ) {
initDebugState(vec);
QVector ref = toQVector(vec);
int ind = GENERATE( range(0,1<<NUM_QUBITS) );
qreal refCalc = pow(abs(ref[ind]), 2);
REQUIRE( getProbAmp(vec,ind) == Approx(refCalc) );
}
}
SECTION( "input validation" ) {
SECTION( "state index" ) {
int ind = GENERATE( -1, 1<<NUM_QUBITS );
REQUIRE_THROWS_WITH( getProbAmp(vec,ind), Contains("Invalid amplitude index") );
}
SECTION( "density-matrix" ) {
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( getProbAmp(mat,0), Contains("valid only for state-vectors") );
destroyQureg(mat, QUEST_ENV);
}
}
destroyQureg(vec, QUEST_ENV);
}
TEST_CASE( "getRealAmp", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
SECTION( "state-vector" ) {
initDebugState(vec);
QVector ref = toQVector(vec);
int ind = GENERATE( range(0,1<<NUM_QUBITS) );
REQUIRE( getRealAmp(vec,ind) == real(ref[ind]) );
}
}
SECTION( "input validation" ) {
SECTION( "state index" ) {
int ind = GENERATE( -1, 1<<NUM_QUBITS );
REQUIRE_THROWS_WITH( getRealAmp(vec,ind), Contains("Invalid amplitude index") );
}
SECTION( "density-matrix" ) {
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
REQUIRE_THROWS_WITH( getRealAmp(mat,0), Contains("valid only for state-vectors") );
destroyQureg(mat, QUEST_ENV);
}
}
destroyQureg(vec, QUEST_ENV);
}