diff --git a/quest/include/calculations.h b/quest/include/calculations.h index 54ff5ed9..bf9e5712 100644 --- a/quest/include/calculations.h +++ b/quest/include/calculations.h @@ -42,6 +42,57 @@ extern "C" { +/** + * @defgroup example_prs Example PR functions + * @brief Nonsensical functions to demonstrate good PRs. + * @{ + */ + + +/** Calculates the real component of the sum of every amplitude in the state, + * but only for states which contain an even number of qubits (for no reason :3 ). + * + * @formulae + * Let @f$ n @f$ qubits be the number of qubits in @p qureg, assumed even. + * + * - When @p qureg is a statevector @f$ \svpsi @f$, this function returns + * @f[ + \text{Re}\left( \sum\limits_i^{2^n} \langle i \svpsi \right) \in \mathbb{R}. + * @f] + * - When @p qureg is a density matrix @f$ \dmrho @f$, this function returns + * @f[ + \text{Re}\left( \sum\limits_i^{2^n} \sum\limits_j^{2^n} \bra{i} \dmrho \ket{j} \right) \in \mathbb{R}. + * @f] + * + * @constraints + * - The number of qubits in the register must be even. + * + * @myexample + * ``` + Qureg qureg = createQureg(4); + initRandomPureState(qureg); + + qreal reAmpSum = calcRealAmpSum(qureg); + reportScalar("reAmpSum", reAmpSum); + * ``` + * + * @see + * - calcTotalProb() + + * @param[in] qureg the state with the processed amplitudes. + * @returns The real component of the sum of all contained amplitudes. + * @throws @validationerror + * - if @p qureg is uninitialised. + * - if @p qureg contains an odd number of qubits. + * @author Tyson Jones + */ +qreal calcRealAmpSum(Qureg qureg); + + +/** @} */ + + + /** * @defgroup calc_expec Expectation values * @brief Functions for calculating expected values of Hermitian observables. @@ -291,6 +342,44 @@ Qureg calcReducedDensityMatrix(Qureg qureg, int* retainQubits, int numRetainQubi */ +/** @ingroup example_prs + * + * Calculates the sum of every amplitude in the state. + * + * @formulae + * Let @f$ n @f$ qubits be the number of qubits in @p qureg. + * + * - When @p qureg is a statevector @f$ \svpsi @f$, this function returns + * @f[ + \sum\limits_i^{2^n} \langle i \svpsi \in \mathbb{C}. + * @f] + * - When @p qureg is a density matrix @f$ \dmrho @f$, this function returns + * @f[ + \sum\limits_i^{2^n} \sum\limits_j^{2^n} \bra{i} \dmrho \ket{j} \in \mathbb{C}. + * @f] + * + * @myexample + * ``` + Qureg qureg = createQureg(4); + initRandomPureState(qureg); + + qcomp ampSum = calcAmpSum(qureg); + reportScalar("ampSum", ampSum); + * ``` + * + * @see + * - calcRealAmpSum() + + * @param[in] qureg the state with the processed amplitudes. + * @returns The the sum of all contained amplitudes. + * @throws @validationerror + * - if @p qureg is uninitialised. + * - if @p qureg contains an odd number of qubits. + * @author Tyson Jones + */ +qcomp calcAmpSum(Qureg qureg); + + /// @ingroup calc_comparisons /// @notdoced /// @notvalidated diff --git a/quest/include/wrappers.h b/quest/include/wrappers.h index 73a281c1..c2256819 100644 --- a/quest/include/wrappers.h +++ b/quest/include/wrappers.h @@ -42,6 +42,15 @@ #ifndef __cplusplus +extern void _wrap_calcAmpSum(Qureg qureg, qcomp* out); + +qcomp calcAmpSum(Qureg qureg) { + + qcomp out; + _wrap_calcAmpSum(qureg, &out); + return out; +} + extern void _wrap_calcInnerProduct(Qureg bra, Qureg ket, qcomp* out); diff --git a/quest/src/api/calculations.cpp b/quest/src/api/calculations.cpp index 3c6213f2..0f4c80f0 100644 --- a/quest/src/api/calculations.cpp +++ b/quest/src/api/calculations.cpp @@ -46,6 +46,17 @@ extern Qureg validateAndCreateCustomQureg( */ +qcomp calcAmpSum(Qureg qureg) { + validate_quregFields(qureg, __func__); + + return localiser_statevec_calcAmpSum(qureg); +} +extern "C" void _wrap_calcAmpSum(Qureg qureg, qcomp* out) { + + *out = calcAmpSum(qureg); +} + + qcomp calcInnerProduct(Qureg quregA, Qureg quregB) { validate_quregFields(quregA, __func__); validate_quregFields(quregB, __func__); @@ -125,6 +136,23 @@ extern "C" { +/* + * PR DEMOS + */ + + +qreal calcRealAmpSum(Qureg qureg) { + validate_quregFields(qureg, __func__); + validate_quregHasEvenNumQubits(qureg, __func__); + + // logic is identical for both statevectors and density matrices + qcomp value = localiser_statevec_calcAmpSum(qureg); + + return std::real(value); +} + + + /* * EXPECTED VALUES */ diff --git a/quest/src/core/accelerator.cpp b/quest/src/core/accelerator.cpp index 9f877fba..9426e326 100644 --- a/quest/src/core/accelerator.cpp +++ b/quest/src/core/accelerator.cpp @@ -182,6 +182,20 @@ using std::min; +/* + * PR DEMOS + */ + + +qcomp accel_statevec_calcAmpSum(Qureg qureg) { + + return (qureg.isGpuAccelerated)? + gpu_statevec_calcAmpSum(qureg): + cpu_statevec_calcAmpSum(qureg); +} + + + /* * GETTERS */ diff --git a/quest/src/core/accelerator.hpp b/quest/src/core/accelerator.hpp index 01cf3efc..8309c4fd 100644 --- a/quest/src/core/accelerator.hpp +++ b/quest/src/core/accelerator.hpp @@ -147,6 +147,13 @@ using std::vector; name = compileval; +/* + * PR DEMOS + */ + +qcomp accel_statevec_calcAmpSum(Qureg qureg); + + /* * GETTERS */ diff --git a/quest/src/core/localiser.cpp b/quest/src/core/localiser.cpp index 7cc84ba0..65a8b5f9 100644 --- a/quest/src/core/localiser.cpp +++ b/quest/src/core/localiser.cpp @@ -461,6 +461,27 @@ void exchangeAmpsToBuffersWhereQubitsAreInStates(Qureg qureg, int pairRank, vect +/* + * PR DEMOS + */ + + +qcomp localiser_statevec_calcAmpSum(Qureg qureg) { + + // each node computes the sum of its local amps + // in an embarrassingly parallel fashion + qcomp out = accel_statevec_calcAmpSum(qureg); + + // node partial sums are combined, and every + // node receives a copy of the full sum (consensus) + if (qureg.isDistributed) + comm_reduceAmp(&out); + + return out; +} + + + /* * GETTERS */ diff --git a/quest/src/core/localiser.hpp b/quest/src/core/localiser.hpp index a9b0391a..075c833a 100644 --- a/quest/src/core/localiser.hpp +++ b/quest/src/core/localiser.hpp @@ -23,6 +23,13 @@ using std::vector; +/* + * PR DEMOS + */ + +qcomp localiser_statevec_calcAmpSum(Qureg qureg); + + /* * GETTERS */ diff --git a/quest/src/core/validation.cpp b/quest/src/core/validation.cpp index 4d316cdf..b86e933e 100644 --- a/quest/src/core/validation.cpp +++ b/quest/src/core/validation.cpp @@ -57,6 +57,14 @@ using std::vector; namespace report { + /* + * NONSENSE VALIDATION FOR DEMO PR + */ + + string QUREG_HAS_ODD_NUM_QUBITS = + "The given Qureg contained ${NUM_QUBITS} qubits, but must contain an even number."; + + /* * ENVIRONMENT CREATION */ @@ -1306,6 +1314,21 @@ bool isIndexListUnique(int* list, int len) { +/* + * NONSENSE VALIDATION FOR DEMO PR + */ + +void validate_quregHasEvenNumQubits(Qureg qureg, const char* caller) { + + tokenSubs vars = { + {"${NUM_QUBITS}", qureg.numQubits} + }; + + assertThat(qureg.numQubits % 2 == 0, report::QUREG_HAS_ODD_NUM_QUBITS, vars, caller); +} + + + /* * ENVIRONMENT CREATION */ diff --git a/quest/src/core/validation.hpp b/quest/src/core/validation.hpp index 2aa9da71..415548c4 100644 --- a/quest/src/core/validation.hpp +++ b/quest/src/core/validation.hpp @@ -63,6 +63,14 @@ qreal validateconfig_getEpsilon(); +/* + * NONSENSE VALIDATION FOR DEMO PR + */ + +void validate_quregHasEvenNumQubits(Qureg qureg, const char* caller); + + + /* * ENVIRONMENT CREATION */ diff --git a/quest/src/cpu/cpu_subroutines.cpp b/quest/src/cpu/cpu_subroutines.cpp index 9c90e08c..132f8989 100644 --- a/quest/src/cpu/cpu_subroutines.cpp +++ b/quest/src/cpu/cpu_subroutines.cpp @@ -40,6 +40,34 @@ using std::vector; +/* + * PR DEMOS + */ + + +qcomp cpu_statevec_calcAmpSum(Qureg qureg) { + + // we brazenly perform this all-state reduction without + // a single thought to finite precision effects - arrest me! + + // separately reduce real and imag components to make MSVC happy + qreal outRe = 0; + qreal outIm = 0; + + // every local amplitude contributes to the sum + qindex numIts = qureg.numAmpsPerNode; + + #pragma omp parallel for reduction(+:outRe,outIm) if(qureg.isMultithreaded) + for (qindex n=0; n()); + + return out; +} + + + /* * MATRIX INITIALISATION */ diff --git a/tests/unit/calculations.cpp b/tests/unit/calculations.cpp index f5d30c75..40614be9 100644 --- a/tests/unit/calculations.cpp +++ b/tests/unit/calculations.cpp @@ -157,6 +157,71 @@ void TEST_ON_CACHED_QUREG_AND_MATRIX(quregCache quregs, matrixCache matrices, au +TEST_CASE( "calcRealAmpSum", TEST_CATEGORY ) { + + SECTION( LABEL_CORRECTNESS ) { + + // The boilerplate for testing a function differs + // greatly depending on what the function does; + // this function is trivial so has a simple test, + // re-using the existing TEST_ALL_QUREGS() macro. + // This macro invokes the below RHS expressions + // passing substitutions of the LHS expressions + // with Quregs (statevector or density matrix) + // and reference objects (qvector or qmatrix), for + // every possible deployment (i.e. multithreading, + // GPU-acceleration, distribution, hybrids, etc). + + TEST_ALL_QUREGS( + qureg, calcRealAmpSum(qureg), + refer, std::real(getTotal(refer)) + ); + } + + SECTION( LABEL_VALIDATION ) { + + SECTION( "qureg uninitialised" ) { + + // prepare an un-initialised qureg + Qureg qureg; + + // manually mangle the fields for validation + // to detect, since the default values are + // undefined behaviour and might not trigger + // (e.g. compiler could re-use a valid Qureg) + qureg.numQubits = -123; + + REQUIRE_THROWS_WITH( calcRealAmpSum(qureg), ContainsSubstring("invalid Qureg") ); + } + } +} + + + +TEST_CASE( "calcAmpSum", TEST_CATEGORY ) { + + SECTION( LABEL_CORRECTNESS ) { + + TEST_ALL_QUREGS( + qureg, calcAmpSum(qureg), + refer, getTotal(refer) + ); + } + + SECTION( LABEL_VALIDATION ) { + + SECTION( "qureg uninitialised" ) { + + Qureg qureg; + qureg.numQubits = -123; + + REQUIRE_THROWS_WITH( calcRealAmpSum(qureg), ContainsSubstring("invalid Qureg") ); + } + } +} + + + TEST_CASE( "calcExpecPauliStr", TEST_CATEGORY ) { SECTION( LABEL_CORRECTNESS ) { diff --git a/tests/utils/linalg.cpp b/tests/utils/linalg.cpp index f4686fc4..716b14b6 100644 --- a/tests/utils/linalg.cpp +++ b/tests/utils/linalg.cpp @@ -107,6 +107,37 @@ int getNumPermutations(int n, int k) { +/* + * NONSENSE PR OPERATIONS + */ + + +qcomp getTotal(qvector in) { + + qcomp out = 0; + + // no compensated summation + for (auto& elem : in) + out += elem; + + return out; +} + + +qcomp getTotal(qmatrix in) { + + qcomp out = 0; + + // no compensated summation + for (auto& row : in) + for (auto& elem : row) + out += elem; + + return out; +} + + + /* * VECTOR OPERATIONS */ diff --git a/tests/utils/linalg.hpp b/tests/utils/linalg.hpp index 87cd116b..ebca2b4c 100644 --- a/tests/utils/linalg.hpp +++ b/tests/utils/linalg.hpp @@ -29,6 +29,9 @@ qindex setBitAt(qindex num, int ind, int bit); qindex setBitsAt(qindex num, vector inds, qindex bits); qindex getPow2(int); +qcomp getTotal(qvector); +qcomp getTotal(qmatrix); + qreal getSum(vector vec); qcomp getSum(qvector); qvector getNormalised(qvector);