#include "catch.hpp"
#include "QuEST.h"
#include "utilities.hpp"
using Catch::Matchers::Contains;
#include <sys/time.h>
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( "calcExpecDiagonalOp", "[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);
SECTION( "correctness" ) {
GENERATE( range(0,10) );
DiagonalOp op = createDiagonalOp(NUM_QUBITS, QUEST_ENV);
for (long long int i=0; i<op.numElemsPerChunk; i++) {
op.real[i] = getRandomReal(-5, 5);
op.imag[i] = getRandomReal(-5, 5);
}
syncDiagonalOp(op);
SECTION( "state-vector" ) {
QVector sumRef = toQMatrix(op) * vecRef;
qcomp prod = 0;
for (size_t i=0; i<vecRef.size(); i++)
prod += conj(vecRef[i]) * sumRef[i];
Complex res = calcExpecDiagonalOp(vec, op);
REQUIRE( res.real == Approx(real(prod)).margin(REAL_EPS) );
REQUIRE( res.imag == Approx(imag(prod)).margin(REAL_EPS) );
}
SECTION( "density-matrix" ) {
matRef = toQMatrix(op) * matRef;
qcomp tr = 0;
for (size_t i=0; i<matRef.size(); i++)
tr += matRef[i][i];
Complex res = calcExpecDiagonalOp(mat, op);
REQUIRE( res.real == Approx(real(tr)).margin(100*REAL_EPS) );
REQUIRE( res.imag == Approx(imag(tr)).margin(100*REAL_EPS) );
}
destroyDiagonalOp(op, QUEST_ENV);
}
SECTION( "input validation" ) {
SECTION( "mismatching size" ) {
DiagonalOp op = createDiagonalOp(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcExpecDiagonalOp(vec, op), Contains("equal number of qubits"));
REQUIRE_THROWS_WITH( calcExpecDiagonalOp(mat, op), Contains("equal number of qubits"));
destroyDiagonalOp(op, QUEST_ENV);
}
}
destroyQureg(vec, QUEST_ENV);
destroyQureg(mat, QUEST_ENV);
}
TEST_CASE( "calcExpecPauliHamil", "[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" ) {
GENERATE( range(0,10) );
int numTerms = GENERATE( 1, 2, 10, 15 );
PauliHamil hamil = createPauliHamil(NUM_QUBITS, numTerms);
setRandomPauliSum(hamil);
QMatrix refHamil = toQMatrix(hamil);
SECTION( "state-vector" ) {
QVector sumRef = refHamil * 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 = calcExpecPauliHamil(vec, hamil, vecWork);
REQUIRE( res == Approx(real(prod)).margin(10*REAL_EPS) );
}
SECTION( "density-matrix" ) {
matRef = refHamil * matRef;
qreal tr = 0;
for (size_t i=0; i<matRef.size(); i++)
tr += real(matRef[i][i]);
qreal res = calcExpecPauliHamil(mat, hamil, matWork);
REQUIRE( res == Approx(tr).margin(1E2*REAL_EPS) );
}
destroyPauliHamil(hamil);
}
SECTION( "input validation" ) {
SECTION( "pauli codes" ) {
int numTerms = 3;
PauliHamil hamil = createPauliHamil(NUM_QUBITS, numTerms);
hamil.pauliCodes[GENERATE_COPY( range(0,numTerms*NUM_QUBITS) )] = (pauliOpType) GENERATE( -1, 4 );
REQUIRE_THROWS_WITH( calcExpecPauliHamil(vec, hamil, vecWork), Contains("Invalid Pauli code") );
destroyPauliHamil(hamil);
}
SECTION( "workspace type" ) {
int numTerms = 1;
PauliHamil hamil = createPauliHamil(NUM_QUBITS, numTerms);
REQUIRE_THROWS_WITH( calcExpecPauliHamil(vec, hamil, mat), Contains("Registers must both be state-vectors or both be density matrices") );
REQUIRE_THROWS_WITH( calcExpecPauliHamil(mat, hamil, vec), Contains("Registers must both be state-vectors or both be density matrices") );
destroyPauliHamil(hamil);
}
SECTION( "workspace dimensions" ) {
int numTerms = 1;
PauliHamil hamil = createPauliHamil(NUM_QUBITS, numTerms);
Qureg vec2 = createQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcExpecPauliHamil(vec, hamil, vec2), Contains("Dimensions") && Contains("don't match") );
destroyQureg(vec2, QUEST_ENV);
Qureg mat2 = createDensityQureg(NUM_QUBITS + 1, QUEST_ENV);
REQUIRE_THROWS_WITH( calcExpecPauliHamil(mat, hamil, mat2), Contains("Dimensions") && Contains("don't match") );
destroyQureg(mat2, QUEST_ENV);
destroyPauliHamil(hamil);
}
SECTION( "matching hamiltonian qubits" ) {
int numTerms = 1;
PauliHamil hamil = createPauliHamil(NUM_QUBITS + 1, numTerms);
REQUIRE_THROWS_WITH( calcExpecPauliHamil(vec, hamil, vecWork), Contains("same number of qubits") );
REQUIRE_THROWS_WITH( calcExpecPauliHamil(mat, hamil, matWork), Contains("same number of qubits") );
destroyPauliHamil(hamil);
}
}
destroyQureg(vec, QUEST_ENV);
destroyQureg(mat, QUEST_ENV);
destroyQureg(vecWork, QUEST_ENV);
destroyQureg(matWork, 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,-qcomp(0,1)},{qcomp(0,1),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( "input 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];
qreal coeffs[numSumTerms];
setRandomPauliSum(coeffs, paulis, NUM_QUBITS, numSumTerms);
QMatrix pauliSum = toQMatrix(coeffs, paulis, NUM_QUBITS, numSumTerms);
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( "input 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( "calcProbOfAllOutcomes", "[calculations]" ) {
Qureg vec = createQureg(NUM_QUBITS, QUEST_ENV);
Qureg mat = createDensityQureg(NUM_QUBITS, QUEST_ENV);
SECTION( "correctness" ) {
int numQubits = GENERATE_COPY( range(1,NUM_QUBITS+1) );
int* qubits = GENERATE_COPY( sublists(range(0,NUM_QUBITS), numQubits) );
int numOutcomes = 1<<numQubits;
qreal probs[numOutcomes];
QVector refProbs = QVector(numOutcomes);
SECTION( "state-vector" ) {
SECTION( "normalised" ) {
QVector ref = getRandomStateVector(NUM_QUBITS);
toQureg(vec, ref);
for (size_t i=0; i<ref.size(); i++) {
int outcome = 0;
for (int q=0; q<numQubits; q++) {
int bit = (i >> qubits[q]) & 1;
outcome += bit * (1 << q);
}
refProbs[outcome] += pow(abs(ref[i]), 2);
}
calcProbOfAllOutcomes(probs, vec, qubits, numQubits);
REQUIRE( areEqual(refProbs, probs) );
}
SECTION( "unnormalised" ) {
QVector ref = getRandomQVector(1<<NUM_QUBITS);
toQureg(vec, ref);
for (size_t i=0; i<ref.size(); i++) {
int outcome = 0;
for (int q=0; q<numQubits; q++) {
int bit = (i >> qubits[q]) & 1;
outcome += bit * (1 << q);
}
refProbs[outcome] += pow(abs(ref[i]), 2);
}
calcProbOfAllOutcomes(probs, vec, qubits, numQubits);
REQUIRE( areEqual(refProbs, probs) );
}
}
SECTION( "density-matrix" ) {
SECTION( "normalised" ) {
QMatrix ref = getRandomDensityMatrix(NUM_QUBITS);
toQureg(mat, ref);
for (size_t i=0; i<ref.size(); i++) {
int outcome = 0;
for (int q=0; q<numQubits; q++) {
int bit = (i >> qubits[q]) & 1;
outcome += bit * (1 << q);
}
refProbs[outcome] += real(ref[i][i]);
}
calcProbOfAllOutcomes(probs, mat, qubits, numQubits);
REQUIRE( areEqual(refProbs, probs) );
}
SECTION( "unnormalised" ) {
QMatrix ref = getRandomQMatrix(1<<NUM_QUBITS);
toQureg(mat, ref);
for (size_t i=0; i<ref.size(); i++) {
int outcome = 0;
for (int q=0; q<numQubits; q++) {
int bit = (i >> qubits[q]) & 1;
outcome += bit * (1 << q);
}
refProbs[outcome] += real(ref[i][i]);
}
calcProbOfAllOutcomes(probs, mat, qubits, numQubits);
REQUIRE( areEqual(refProbs, probs) );
}
}
}
SECTION( "input validation" ) {
int numQubits = 3;
int qubits[] = {0, 1, 2};
qreal probs[8];
SECTION( "number of qubits" ) {
numQubits = GENERATE( -1, 0, NUM_QUBITS+1 );
REQUIRE_THROWS_WITH( calcProbOfAllOutcomes(probs, mat, qubits, numQubits), Contains("Invalid number of target qubits") );
}
SECTION( "qubit indices" ) {
qubits[GENERATE_COPY(range(0,numQubits))] = GENERATE( -1, NUM_QUBITS );
REQUIRE_THROWS_WITH( calcProbOfAllOutcomes(probs, mat, qubits, numQubits), Contains("Invalid target qubit") );
}
SECTION( "repetition of qubits" ) {
qubits[GENERATE_COPY(1,2)] = qubits[0];
REQUIRE_THROWS_WITH( calcProbOfAllOutcomes(probs, mat, qubits, numQubits), Contains("qubits must be unique") );
}
}
destroyQureg(vec, QUEST_ENV);
destroyQureg(mat, 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( "input 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);
}