/** @file * Unoptimised, analytic implementations of matrix operations used by QuEST_unit_tests * * @author Tyson Jones */ #include "QuEST.h" #include "utilities.hpp" #include "catch.hpp" #include #include #include #ifdef DISTRIBUTED_MODE #include #endif /* (don't generate doxygen doc) * * preconditions to the internal unit testing functions are checked using * DEMAND rather than Catch2's REQUIRE, so that they are not counted in the * total unit testing statistics (e.g. number of checks passed). */ #define DEMAND( cond ) if (!(cond)) FAIL( ); QVector operator + (const QVector& v1, const QVector& v2) { DEMAND( v1.size() == v2.size() ); QVector out = v1; for (size_t i=0; i 1 ); QMatrix matr = QMatrix(dim); for (size_t i=0; i 1 ); QMatrix matr = getZeroMatrix(dim); for (size_t i=0; i 1 ); DEMAND( (qb1 >= 0 && qb1 < numQb) ); DEMAND( (qb2 >= 0 && qb2 < numQb) ); if (qb1 > qb2) std::swap(qb1, qb2); if (qb1 == qb2) return getIdentityMatrix(1 << numQb); QMatrix swap; if (qb2 == qb1 + 1) { // qubits are adjacent swap = QMatrix{{1,0,0,0},{0,0,1,0},{0,1,0,0},{0,0,0,1}}; } else { // qubits are distant int block = 1 << (qb2 - qb1); swap = getZeroMatrix(block*2); QMatrix iden = getIdentityMatrix(block/2); // Lemma 3.1 of arxiv.org/pdf/1711.09765.pdf QMatrix p0{{1,0},{0,0}}; QMatrix l0{{0,1},{0,0}}; QMatrix l1{{0,0},{1,0}}; QMatrix p1{{0,0},{0,1}}; /* notating a^(n+1) = identity(1< 0) swap = getKroneckerProduct(swap, getIdentityMatrix(1<= 0 ); DEMAND( numTargs >= 0 ); DEMAND( numQubits >= (numCtrls+numTargs) ); DEMAND( op.size() == (1u << numTargs) ); // copy {ctrls} and {targs}to restore at end std::vector ctrlsCopy(ctrls, ctrls+numCtrls); std::vector targsCopy(targs, targs+numTargs); // full-state matrix of qubit swaps QMatrix swaps = getIdentityMatrix(1 << numQubits); QMatrix unswaps = getIdentityMatrix(1 << numQubits); QMatrix matr; // swap targs to {0, ..., numTargs-1} for (int i=0; i numCtrls+numTargs) { size_t pad = 1 << (numQubits - numCtrls - numTargs); fullOp = getKroneckerProduct(getIdentityMatrix(pad), fullOp); } // apply swap to either side (to swap qubits back and forth) fullOp = unswaps * fullOp * swaps; // restore {ctrls and targs} for (int i=0; i>= 1) n++; return n; } QMatrix getRandomQMatrix(int dim) { DEMAND( dim > 1 ); QMatrix matr = getZeroMatrix(dim); for (int i=0; i REAL_EPS) return false; return true; } bool areEqual(QMatrix a, QMatrix b) { DEMAND( a.size() == b.size() ); for (size_t i=0; i REAL_EPS) return false; return true; } qcomp expI(qreal phase) { return cos(phase) + 1i*sin(phase); } qreal getRandomReal(qreal min, qreal max) { DEMAND( min <= max ); qreal r = min + (max - min) * (rand() / (qreal) RAND_MAX); // check bounds satisfied DEMAND( r >= min ); DEMAND( r <= max ); return r; } QVector getRandomQVector(int dim) { QVector vec = QVector(dim); for (int i=0; i 0 ); // generate random probabilities to weight random pure states int dim = 1<= 1 ); QMatrix matr = getRandomQMatrix(1 << numQb); for (size_t i=0; i=0; k--) { // compute row . prev = sum_n row_n conj(prev_n) qcomp prod = 0; for (size_t n=0; n= 3 && !areEqual(conjprod, iden) ) { matr = getIdentityMatrix(1 << numQb); conjprod = matr; } DEMAND( areEqual(conjprod, iden) ); // return the new orthonormal matrix return matr; } std::vector getRandomKrausMap(int numQb, int numOps) { DEMAND( numOps >= 1 ); DEMAND( numOps <= 4*numQb*numQb ); // generate random unitaries std::vector ops; for (int i=0; i precision || imagDif > precision) { ampsAgree = 0; // debug printf("Disagreement at %lld: %g + i(%g) VS %g + i(%g)\n", startInd+i, qureg.stateVec.real[i], qureg.stateVec.imag[i], real(vec[startInd+i]), imag(vec[startInd+i])); break; } } // if one node's partition wasn't equal, all-nodes must report not-equal int allAmpsAgree = ampsAgree; #ifdef DISTRIBUTED_MODE MPI_Allreduce(&sAgree, &allAmpsAgree, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD); #endif return allAmpsAgree; } bool areEqual(Qureg qureg, QVector vec) { return areEqual(qureg, vec, REAL_EPS); } bool areEqual(Qureg qureg, QMatrix matr, qreal precision) { DEMAND( qureg.isDensityMatrix ); DEMAND( (int) (matr.size()*matr.size()) == qureg.numAmpsTotal ); // ensure local qureg.stateVec is up to date copyStateFromGPU(qureg); syncQuESTEnv(QUEST_ENV); // the starting index in vec of this node's qureg partition. long long int startInd = qureg.chunkId * qureg.numAmpsPerChunk; long long int globalInd, row, col, i; int ampsAgree; // compare each of this node's amplitude to the corresponding matr sub-matrix for (i=0; i { int* list; int* sublist; int len; int sublen; vector featured; private: void createSublist() { // sublist to send to the user sublist = (int*) malloc(sublen * sizeof *sublist); // indicates which list members are currently in sublist featured = vector(len); fill(featured.end() - sublen, featured.end(), true); } void prepareSublist() { // choose the next combination int j=0; for (int i=0; i&& gen, int numSamps, const int* exclude, int numExclude ) { // extract all generator elems vector elems = vector(); do { elems.push_back(gen.get()); } while (gen.next()); // make (int*) of non-excluded elems len = 0; list = (int*) malloc(elems.size() * sizeof *list); for (size_t i=0; i sublists( int* list, int len, int sublen ) { return Catch::Generators::GeneratorWrapper( std::unique_ptr>( new SubListGenerator(list, len, sublen))); } Catch::Generators::GeneratorWrapper sublists( Catch::Generators::GeneratorWrapper&& gen, int numSamps, const int* exclude, int numExclude ) { return Catch::Generators::GeneratorWrapper( std::unique_ptr>( new SubListGenerator(std::move(gen), numSamps, exclude, numExclude))); } Catch::Generators::GeneratorWrapper sublists( Catch::Generators::GeneratorWrapper&& gen, int numSamps, int excluded ) { int exclude[] = {excluded}; return Catch::Generators::GeneratorWrapper( std::unique_ptr>( new SubListGenerator(std::move(gen), numSamps, exclude, 1))); } Catch::Generators::GeneratorWrapper sublists( Catch::Generators::GeneratorWrapper&& gen, int numSamps ) { int exclude[] = {}; return Catch::Generators::GeneratorWrapper( std::unique_ptr>( new SubListGenerator(std::move(gen), numSamps, exclude, 0))); } template class SequenceGenerator : public Catch::Generators::IGenerator { T* digits; int len; T maxDigit; int ind; int seqLen; public: SequenceGenerator(T maxDigit_, int numDigits) { ind = 0; len = numDigits; maxDigit = maxDigit_; seqLen = (int) pow(1 + (int) maxDigit, len); digits = (T*) malloc(numDigits * sizeof *digits); for (int i=0; i bitsets(int numBits) { return Catch::Generators::GeneratorWrapper( std::unique_ptr>( new SequenceGenerator(1, numBits))); } Catch::Generators::GeneratorWrapper sequences(int base, int numDigits) { return Catch::Generators::GeneratorWrapper( std::unique_ptr>( new SequenceGenerator(base-1, numDigits))); } Catch::Generators::GeneratorWrapper pauliseqs(int numPaulis) { return Catch::Generators::GeneratorWrapper( std::unique_ptr>( new SequenceGenerator(PAULI_Z, numPaulis))); }