From 8519a2dc03fe7a30dd065d7324fbb1e26e8d8378 Mon Sep 17 00:00:00 2001 From: Maurice Jamieson Date: Wed, 14 Jan 2026 14:51:54 +0000 Subject: [PATCH 1/3] Added unit tests requested by Quantum Motion --- tests/unit/operations.cpp | 587 ++++++++++++++++++++++++++++++++-- tests/unit/trotterisation.cpp | 491 +++++++++++++++++++++++++++- 2 files changed, 1043 insertions(+), 35 deletions(-) diff --git a/tests/unit/operations.cpp b/tests/unit/operations.cpp index 05d46418..d2922421 100644 --- a/tests/unit/operations.cpp +++ b/tests/unit/operations.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "tests/utils/config.hpp" @@ -1042,8 +1043,12 @@ void testOperationValidation(auto operation) { SECTION( "targeted amps fit in node" ) { - // simplest to trigger validation using a statevector - qureg = getCachedStatevecs().begin()->second; + // use any qureg which is otherwise compatible + // (but beware statevecs vs density matrices permit + // different num targets before validation is triggered) + qureg = (Apply == rightapply)? + getCachedDensmatrs().begin()->second: + getCachedStatevecs().begin()->second; // can only be validated when environment AND qureg // are distributed (over more than 1 node, of course) @@ -1053,7 +1058,8 @@ void testOperationValidation(auto operation) { // can only be validated if forced ctrl qubits permit // enough remaining targets int minNumCtrls = (Ctrls == one)? 1 : 0; - int minNumTargs = numQubits - qureg.logNumNodes + 1; + int numVecQubits = (qureg.isDensityMatrix)? 2*numQubits : numQubits; + int minNumTargs = numVecQubits - qureg.logNumNodes + 1; int maxNumTargs = numQubits - minNumCtrls; if (minNumTargs > maxNumTargs) return; @@ -1406,7 +1412,47 @@ TEST_CASE( "applyQuantumFourierTransform", TEST_CATEGORY_OPS ) { } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + int targs[] = {0, 1, 2}; + int numTargs = 3; + + SECTION( "qureg uninitialised" ) { + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyQuantumFourierTransform(badQureg, targs, numTargs), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubits" ) { + int badTargs[] = {0, 1, 10}; + REQUIRE_THROWS_WITH( + applyQuantumFourierTransform(qureg, badTargs, 3), + ContainsSubstring("target") + ); + } + + SECTION( "duplicate target qubits" ) { + int dupTargs[] = {0, 1, 1}; + REQUIRE_THROWS_WITH( + applyQuantumFourierTransform(qureg, dupTargs, 3), + ContainsSubstring("duplicate") + ); + } + + SECTION( "zero number of targets" ) { + REQUIRE_THROWS_WITH( + applyQuantumFourierTransform(qureg, targs, 0), + ContainsSubstring("targets") + ); + } + + destroyQureg(qureg); + } } @@ -1451,7 +1497,22 @@ TEST_CASE( "applyFullQuantumFourierTransform", TEST_CATEGORY_OPS ) { } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + + SECTION( "qureg uninitialised" ) { + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyFullQuantumFourierTransform(badQureg), + ContainsSubstring("invalid Qureg") + ); + } + + destroyQureg(qureg); + } } @@ -1477,7 +1538,30 @@ TEST_CASE( "applyQubitProjector", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyQubitProjector(badQureg, 0, 0), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubit" ) { + // Try to project a qubit that doesn't exist (qubit index 10 on a 5-qubit system) + REQUIRE_THROWS_WITH( + applyQubitProjector(qureg, 10, 0), + ContainsSubstring("target") + ); + } + + destroyQureg(qureg); + } } @@ -1503,7 +1587,51 @@ TEST_CASE( "applyMultiQubitProjector", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + int targets[] = {0, 1, 2}; + int outcomes[] = {0, 1, 0}; + int numTargets = 3; + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyMultiQubitProjector(badQureg, targets, outcomes, numTargets), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubits" ) { + // Try to project qubits that don't exist (qubit index 10 on a 5-qubit system) + int badTargets[] = {0, 1, 10}; + REQUIRE_THROWS_WITH( + applyMultiQubitProjector(qureg, badTargets, outcomes, numTargets), + ContainsSubstring("target") + ); + } + + SECTION( "duplicate target qubits" ) { + // Try to project the same qubit twice in one operation + int dupTargets[] = {0, 1, 1}; + REQUIRE_THROWS_WITH( + applyMultiQubitProjector(qureg, dupTargets, outcomes, numTargets), + ContainsSubstring("duplicate") + ); + } + + SECTION( "zero number of targets" ) { + // Try to project no qubits at all (empty list) + REQUIRE_THROWS_WITH( + applyMultiQubitProjector(qureg, targets, outcomes, 0), + ContainsSubstring("targets") + ); + } + + destroyQureg(qureg); + } } @@ -1542,7 +1670,30 @@ TEST_CASE( "applyForcedQubitMeasurement", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyForcedQubitMeasurement(badQureg, 0, 0), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubit" ) { + // Try to measure a qubit that doesn't exist (qubit index 10 on a 5-qubit system) + REQUIRE_THROWS_WITH( + applyForcedQubitMeasurement(qureg, 10, 0), + ContainsSubstring("target") + ); + } + + destroyQureg(qureg); + } } @@ -1588,7 +1739,51 @@ TEST_CASE( "applyForcedMultiQubitMeasurement", TEST_CATEGORY_OPS ) { setValidationEpsilonToDefault(); } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + int targets[] = {0, 1, 2}; + int outcomes[] = {0, 1, 0}; + int numTargets = 3; + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyForcedMultiQubitMeasurement(badQureg, targets, outcomes, numTargets), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubits" ) { + // Try to measure qubits that don't exist (qubit index 10 on a 5-qubit system) + int badTargets[] = {0, 1, 10}; + REQUIRE_THROWS_WITH( + applyForcedMultiQubitMeasurement(qureg, badTargets, outcomes, numTargets), + ContainsSubstring("target") + ); + } + + SECTION( "duplicate target qubits" ) { + // Try to measure the same qubit twice in one operation + int dupTargets[] = {0, 1, 1}; + REQUIRE_THROWS_WITH( + applyForcedMultiQubitMeasurement(qureg, dupTargets, outcomes, numTargets), + ContainsSubstring("duplicate") + ); + } + + SECTION( "zero number of targets" ) { + // Try to measure no qubits at all (empty list) + REQUIRE_THROWS_WITH( + applyForcedMultiQubitMeasurement(qureg, targets, outcomes, 0), + ContainsSubstring("targets") + ); + } + + destroyQureg(qureg); + } } @@ -1625,7 +1820,50 @@ TEST_CASE( "applyMultiQubitMeasurement", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + int targets[] = {0, 1, 2}; + int numTargets = 3; + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyMultiQubitMeasurement(badQureg, targets, numTargets), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubits" ) { + // Try to measure qubits that don't exist (qubit index 10 on a 5-qubit system) + int badTargets[] = {0, 1, 10}; + REQUIRE_THROWS_WITH( + applyMultiQubitMeasurement(qureg, badTargets, numTargets), + ContainsSubstring("target") + ); + } + + SECTION( "duplicate target qubits" ) { + // Try to measure the same qubit twice in one operation + int dupTargets[] = {0, 1, 1}; + REQUIRE_THROWS_WITH( + applyMultiQubitMeasurement(qureg, dupTargets, numTargets), + ContainsSubstring("duplicate") + ); + } + + SECTION( "zero number of targets" ) { + // Try to measure no qubits at all (empty list) + REQUIRE_THROWS_WITH( + applyMultiQubitMeasurement(qureg, targets, 0), + ContainsSubstring("targets") + ); + } + + destroyQureg(qureg); + } } @@ -1664,7 +1902,53 @@ TEST_CASE( "applyMultiQubitMeasurementAndGetProb", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + int targets[] = {0, 1, 2}; + int numTargets = 3; + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyMultiQubitMeasurementAndGetProb(badQureg, targets, numTargets, nullptr), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubits" ) { + // Try to measure qubits that don't exist (qubit index 10 on a 5-qubit system) + int badTargets[] = {0, 1, 10}; + qreal prob = 0; + REQUIRE_THROWS_WITH( + applyMultiQubitMeasurementAndGetProb(qureg, badTargets, numTargets, &prob), + ContainsSubstring("target") + ); + } + + SECTION( "duplicate target qubits" ) { + // Try to measure the same qubit twice in one operation + int dupTargets[] = {0, 1, 1}; + qreal prob = 0; + REQUIRE_THROWS_WITH( + applyMultiQubitMeasurementAndGetProb(qureg, dupTargets, numTargets, &prob), + ContainsSubstring("duplicate") + ); + } + + SECTION( "zero number of targets" ) { + // Try to measure no qubits at all (empty list) + qreal prob = 0; + REQUIRE_THROWS_WITH( + applyMultiQubitMeasurementAndGetProb(qureg, targets, 0, &prob), + ContainsSubstring("targets") + ); + } + + destroyQureg(qureg); + } } @@ -1700,7 +1984,30 @@ TEST_CASE( "applyQubitMeasurement", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyQubitMeasurement(badQureg, 0), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubit" ) { + // Try to measure a qubit that doesn't exist (qubit index 10 on a 5-qubit system) + REQUIRE_THROWS_WITH( + applyQubitMeasurement(qureg, 10), + ContainsSubstring("target") + ); + } + + destroyQureg(qureg); + } } @@ -1738,7 +2045,31 @@ TEST_CASE( "applyQubitMeasurementAndGetProb", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyQubitMeasurementAndGetProb(badQureg, 0, nullptr), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubit" ) { + // Try to measure a qubit that doesn't exist (qubit index 10 on a 5-qubit system) + qreal prob = 0; + REQUIRE_THROWS_WITH( + applyQubitMeasurementAndGetProb(qureg, 10, &prob), + ContainsSubstring("target") + ); + } + + destroyQureg(qureg); + } } @@ -1769,8 +2100,6 @@ TEST_CASE( "applyFullStateDiagMatr", TEST_CATEGORY_OPS LABEL_MIXED_DEPLOY_TAG ) TEST_ON_CACHED_QUREG_AND_MATRIX( cachedDM, cachedMatrs, apiFunc, refDM, refMatr, refFunc); } } - - /// @todo input validation } @@ -1824,8 +2153,6 @@ TEST_CASE( "applyFullStateDiagMatrPower", TEST_CATEGORY_OPS LABEL_MIXED_DEPLOY_T setValidationEpsilonToDefault(); } - - /// @todo input validation } @@ -1854,7 +2181,23 @@ TEST_CASE( "applyNonUnitaryPauliGadget", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + PauliStr str = getPauliStr("XY", {0, 1}); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyNonUnitaryPauliGadget(badQureg, str, qcomp(0.5, 0)), + ContainsSubstring("invalid Qureg") + ); + } + + destroyQureg(qureg); + } } @@ -1984,8 +2327,6 @@ TEST_CASE( "leftapplyFullStateDiagMatr", TEST_CATEGORY_MULT LABEL_MIXED_DEPLOY_T TEST_ON_CACHED_QUREG_AND_MATRIX( cachedDM, cachedMatrs, apiFunc, refDM, refMatr, refFunc); } } - - /// @todo input validation } @@ -2009,8 +2350,6 @@ TEST_CASE( "rightapplyFullStateDiagMatr", TEST_CATEGORY_MULT LABEL_MIXED_DEPLOY_ TEST_ON_CACHED_QUREG_AND_MATRIX( cachedDM, cachedMatrs, apiFunc, refDM, refMatr, refFunc); } } - - /// @todo input validation } @@ -2053,8 +2392,6 @@ TEST_CASE( "leftapplyFullStateDiagMatrPower", TEST_CATEGORY_MULT LABEL_MIXED_DEP TEST_ON_CACHED_QUREG_AND_MATRIX( cachedDM, cachedMatrs, apiFunc, refDM, refMatr, refFunc); } } - - /// @todo input validation } @@ -2088,7 +2425,23 @@ TEST_CASE( "rightapplyFullStateDiagMatrPower", TEST_CATEGORY_MULT LABEL_MIXED_DE } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + FullStateDiagMatr matr = getCachedFullStateDiagMatrs()[0]; + REQUIRE_THROWS_WITH( + rightapplyFullStateDiagMatrPower(badQureg, matr, qcomp(2.0, 0)), + ContainsSubstring("invalid Qureg") + ); + } + + destroyQureg(qureg); + } } @@ -2114,7 +2467,30 @@ TEST_CASE( "leftapplyQubitProjector", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + leftapplyQubitProjector(badQureg, 0, 0), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubit" ) { + // Try to project a qubit that doesn't exist (qubit index 10 on a 5-qubit system) + REQUIRE_THROWS_WITH( + leftapplyQubitProjector(qureg, 10, 0), + ContainsSubstring("target") + ); + } + + destroyQureg(qureg); + } } @@ -2139,7 +2515,30 @@ TEST_CASE( "rightapplyQubitProjector", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + rightapplyQubitProjector(badQureg, 0, 0), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubit" ) { + // Try to project a qubit that doesn't exist (qubit index 10 on a 5-qubit system) + REQUIRE_THROWS_WITH( + rightapplyQubitProjector(qureg, 10, 0), + ContainsSubstring("target") + ); + } + + destroyQureg(qureg); + } } @@ -2165,7 +2564,51 @@ TEST_CASE( "leftapplyMultiQubitProjector", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + int targets[] = {0, 1, 2}; + int outcomes[] = {0, 1, 0}; + int numTargets = 3; + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + leftapplyMultiQubitProjector(badQureg, targets, outcomes, numTargets), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubits" ) { + // Try to project qubits that don't exist (qubit index 10 on a 5-qubit system) + int badTargets[] = {0, 1, 10}; + REQUIRE_THROWS_WITH( + leftapplyMultiQubitProjector(qureg, badTargets, outcomes, numTargets), + ContainsSubstring("target") + ); + } + + SECTION( "duplicate target qubits" ) { + // Try to project the same qubit twice in one operation + int dupTargets[] = {0, 1, 1}; + REQUIRE_THROWS_WITH( + leftapplyMultiQubitProjector(qureg, dupTargets, outcomes, numTargets), + ContainsSubstring("duplicate") + ); + } + + SECTION( "zero number of targets" ) { + // Try to project no qubits at all (empty list) + REQUIRE_THROWS_WITH( + leftapplyMultiQubitProjector(qureg, targets, outcomes, 0), + ContainsSubstring("targets") + ); + } + + destroyQureg(qureg); + } } @@ -2190,7 +2633,51 @@ TEST_CASE( "rightapplyMultiQubitProjector", TEST_CATEGORY_OPS ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + int targets[] = {0, 1, 2}; + int outcomes[] = {0, 1, 0}; + int numTargets = 3; + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + rightapplyMultiQubitProjector(badQureg, targets, outcomes, numTargets), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "invalid target qubits" ) { + // Try to project qubits that don't exist (qubit index 10 on a 5-qubit system) + int badTargets[] = {0, 1, 10}; + REQUIRE_THROWS_WITH( + rightapplyMultiQubitProjector(qureg, badTargets, outcomes, numTargets), + ContainsSubstring("target") + ); + } + + SECTION( "duplicate target qubits" ) { + // Try to project the same qubit twice in one operation + int dupTargets[] = {0, 1, 1}; + REQUIRE_THROWS_WITH( + rightapplyMultiQubitProjector(qureg, dupTargets, outcomes, numTargets), + ContainsSubstring("duplicate") + ); + } + + SECTION( "zero number of targets" ) { + // Try to project no qubits at all (empty list) + REQUIRE_THROWS_WITH( + rightapplyMultiQubitProjector(qureg, targets, outcomes, 0), + ContainsSubstring("targets") + ); + } + + destroyQureg(qureg); + } } @@ -2220,7 +2707,26 @@ TEST_CASE( "leftapplyPauliStrSum", TEST_CATEGORY_MULT LABEL_MIXED_DEPLOY_TAG ) { SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + PauliStrSum sum = createRandomPauliStrSum(numQubits, 2); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + Qureg workspace = createCloneQureg(qureg); + REQUIRE_THROWS_WITH( + leftapplyPauliStrSum(badQureg, sum, workspace), + ContainsSubstring("invalid Qureg") + ); + destroyQureg(workspace); + } + + destroyQureg(qureg); + destroyPauliStrSum(sum); + } } @@ -2249,7 +2755,26 @@ TEST_CASE( "rightapplyPauliStrSum", TEST_CATEGORY_MULT LABEL_MIXED_DEPLOY_TAG ) SECTION( LABEL_DENSMATR ) { TEST_ON_CACHED_QUREGS(densmatrQuregs, densmatrRef, testFunc); } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + PauliStrSum sum = createRandomPauliStrSum(numQubits, 2); + + SECTION( "qureg uninitialised" ) { + // Invalidate the qureg by setting a negative qubit count + Qureg badQureg = qureg; + badQureg.numQubits = -1; + Qureg workspace = createCloneQureg(qureg); + REQUIRE_THROWS_WITH( + rightapplyPauliStrSum(badQureg, sum, workspace), + ContainsSubstring("invalid Qureg") + ); + destroyQureg(workspace); + } + + destroyQureg(qureg); + destroyPauliStrSum(sum); + } } diff --git a/tests/unit/trotterisation.cpp b/tests/unit/trotterisation.cpp index cc691402..f1ca904b 100644 --- a/tests/unit/trotterisation.cpp +++ b/tests/unit/trotterisation.cpp @@ -9,6 +9,22 @@ #include "quest.h" +#include +#include +#include + +#include "tests/utils/macros.hpp" +#include "tests/utils/cache.hpp" +#include "tests/utils/compare.hpp" +#include "tests/utils/random.hpp" + +#include +#include + +using std::vector; +using std::string; +using namespace Catch::Matchers; + /* @@ -19,10 +35,127 @@ LABEL_UNIT_TAG "[trotterisation]" +/* + * Prepare a Hamiltonian H under which dynamical + * evolution will be simulated via Trotterisation + * of unitary-time evolution operator e^(-itH). + * If the Hamiltonian was fixed/known in advance, + * we could instead use createInlinePauliStrSum() + * + * (Adapted from dynamics.cpp, @author Tyson Jones) + */ +PauliStrSum createHeisenbergHamiltonian(int numQubits) { + + // we prepare a Heisenberg XYZ spin-ring Hamiltonian, + // i.e. H = -1/2 sum( Jx XX + Jy YY + Jz ZZ + h Z ) + // upon all nearest neighbour qubits, with periodicity. + // The coefficients must be real for H to be Hermitian + // and ergo its time-evolution operator to be unitary, + // although they must be represented with a qcomp type. + vector operators = {"XX", "YY", "ZZ", "Z"}; + vector coefficients = {.1, .2, .3, .4}; // Jx,Jy,Jz,h + + // we will populate the below vectors with 4*numQubits + // elements which we could pre-allocate with .reserve, + // but we might incur Donald Knuth's justified wrath. + vector allStrings; + vector allCoeffs; + + // prepare all XX + YY + ZZ + for (int p=0; p<3; p++) { + for (int i=0; i targs = {i, (i+1)%numQubits}; + PauliStr str = getPauliStr(operators[p], targs); + + allStrings.push_back(str); + allCoeffs.push_back(coefficients[p]); + } + } + + // prepare Z + for (int i=0; i strings(numQubits); + vector coeffs(numQubits); + + for (int i=0; i}^{N} Z_{i}Z_{j} + * where, + * \mu = magField, + * J = interactionStrength, + * indicates nearest-neighbour interactions only, + * and boundary conditions are periodic such that site N-1 interacts with site 0. + * + * The asymmetricBias term can be used to break the symmetry of the system + * in order to 'choose' a preferred antiferromagnetic state, and ensure repeatable + * predictable outcomes. + * It adds a term of the form: + * -BZ_{0} + */ +PauliStrSum createIsingHamiltonian(int numQubits, qreal magField, + qreal interactionStrength, qreal asymmetricBias) { + const int NTERMS = 2 * numQubits + 1; + + vector coeffs; + vector pauli_terms; + coeffs.reserve(NTERMS); + pauli_terms.reserve(NTERMS); + + for (int i = 0; i < numQubits; ++i) { + pauli_terms.push_back(getPauliStr("Z", {i})); + coeffs.push_back(getQcomp(-magField, 0)); + + int next = (i + 1) % numQubits; + pauli_terms.push_back(getPauliStr("ZZ", {i, next})); + coeffs.push_back(getQcomp(-interactionStrength, 0)); + } + + pauli_terms.push_back(getPauliStr("Z", {0})); + coeffs.push_back(getQcomp(-asymmetricBias, 0)); + + return createPauliStrSum(pauli_terms, coeffs); +} + /** * @todo - * UNTESTED FUNCTIONS + * UNTESTED FUNCTIONS (NOT YET VALIDATED BY REFERENCE TESTS) */ void applyTrotterizedNonUnitaryPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps); @@ -35,8 +168,358 @@ void applyTrotterizedMultiControlledPauliStrSumGadget(Qureg qureg, int* controls void applyTrotterizedMultiStateControlledPauliStrSumGadget(Qureg qureg, int* controls, int* states, int numControls, PauliStrSum sum, qreal angle, int order, int reps); -void applyTrotterizedUnitaryTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal time, int order, int reps); +void applyTrotterizedNoisyTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal* damps, PauliStr* jumps, int numJumps, qreal time, int order, int reps); -void applyTrotterizedImaginaryTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal tau, int order, int reps); -void applyTrotterizedNoisyTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal* damps, PauliStr* jumps, int numJumps, qreal time, int order, int reps); +/** @} */ + +/* + * TESTS + */ + +TEST_CASE( "applyTrotterizedUnitaryTimeEvolution", TEST_CATEGORY ) { + + SECTION( LABEL_CORRECTNESS ) { + + int numQubits = 20; + Qureg qureg = createQureg(numQubits); + initPlusState(qureg); + + PauliStrSum hamil = createHeisenbergHamiltonian(numQubits); + PauliStrSum observ = createAlternatingPauliObservable(numQubits); + + qreal dt = 0.1; + int order = 4; + int reps = 5; + int steps = 10; + + // Tolerance for floating-point comparison + // Allows for minor numerical differences between runs + qreal eps = 1E-10; + + vector refObservables = { + 19.26827777028073, + 20.34277275871839, + 21.21120737889526, + 21.86585902741717, + 22.30371711358924, + 22.52644660547882, + 22.54015748825067, + 22.35499202583118, + 21.9845541501027, + 21.44521638719462 + }; + + for (int i = 0; i < steps; i++) { + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, dt, order, reps); + qreal expec = calcExpecPauliStrSum(qureg, observ); + + REQUIRE_THAT( expec, WithinAbs(refObservables[i], eps) ); + } + + // Verify state remains normalized + REQUIRE_THAT( calcTotalProb(qureg), WithinAbs(1.0, 1E-10) ); + + destroyQureg(qureg); + destroyPauliStrSum(hamil); + destroyPauliStrSum(observ); + } + + SECTION( LABEL_VALIDATION ) { + + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + PauliStrSum hamil = createHeisenbergHamiltonian(numQubits); + + SECTION( "qureg uninitialised" ) { + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyTrotterizedUnitaryTimeEvolution(badQureg, hamil, 0.1, 4, 5), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "pauli sum uninitialized" ) { + PauliStrSum badHamil = hamil; + badHamil.numTerms = 0; + REQUIRE_THROWS_WITH( + applyTrotterizedUnitaryTimeEvolution(qureg, badHamil, 0.1, 4, 5), + ContainsSubstring("Pauli") + ); + } + + SECTION( "pauli sum exceeds qureg qubits" ) { + Qureg smallQureg = createQureg(3); + PauliStrSum largeHamil = createHeisenbergHamiltonian(numQubits); + REQUIRE_THROWS_WITH( + applyTrotterizedUnitaryTimeEvolution(smallQureg, largeHamil, 0.1, 4, 5), + ContainsSubstring("only compatible") + ); + destroyQureg(smallQureg); + destroyPauliStrSum(largeHamil); + } + + SECTION( "invalid trotter order (zero)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 0, 5), + ContainsSubstring("order") + ); + } + + SECTION( "invalid trotter order (negative)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, -2, 5), + ContainsSubstring("order") + ); + } + + SECTION( "invalid trotter order (odd, not 1)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 3, 5), + ContainsSubstring("order") + ); + } + + SECTION( "invalid trotter reps (zero)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 4, 0), + ContainsSubstring("repetitions") + ); + } + + SECTION( "invalid trotter reps (negative)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 4, -3), + ContainsSubstring("repetitions") + ); + } + + destroyQureg(qureg); + destroyPauliStrSum(hamil); + } +} + + +TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { + + SECTION( LABEL_CORRECTNESS ) { + + int numQubits = 16; + qreal tau = 0.1; + int order = 6; + int reps = 5; + int steps = 10; + + // Tolerance for ground state amplitude + qreal eps = 1E-2; + + // Ground state: all qubits align down (driven by strong magnetic field) + { + Qureg qureg = createQureg(numQubits); + initPlusState(qureg); + + PauliStrSum ising = createIsingHamiltonian(numQubits, 10.0, 0.0, 0.0); + + for (int i = 0; i < steps; ++i) { + applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps); + setQuregToRenormalized(qureg); + } + + qcomp amp = getQuregAmp(qureg, 0); + qreal amp_mag = amp.real() * amp.real() + amp.imag() * amp.imag(); + + REQUIRE_THAT( amp_mag, WithinAbs(1.0, eps) ); + + for (long long i = 1; i < (1LL << numQubits); i++) { + qcomp other_amp = getQuregAmp(qureg, i); + qreal other_mag = other_amp.real() * other_amp.real() + + other_amp.imag() * other_amp.imag(); + REQUIRE( other_mag < eps ); + } + + destroyQureg(qureg); + destroyPauliStrSum(ising); + } + + // Ground state: all qubits align up (driven by opposite magnetic field) + { + Qureg qureg = createQureg(numQubits); + initPlusState(qureg); + + PauliStrSum ising = createIsingHamiltonian(numQubits, -10.0, 0.0, 0.0); + + for (int i = 0; i < steps; ++i) { + applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps); + setQuregToRenormalized(qureg); + } + + long long last_state = (1LL << numQubits) - 1; + qcomp amp = getQuregAmp(qureg, last_state); + qreal amp_mag = amp.real() * amp.real() + amp.imag() * amp.imag(); + + REQUIRE_THAT( amp_mag, WithinAbs(1.0, eps) ); + + for (long long i = 0; i < (1LL << numQubits); i++) { + if (i == last_state) continue; + qcomp other_amp = getQuregAmp(qureg, i); + qreal other_mag = other_amp.real() * other_amp.real() + + other_amp.imag() * other_amp.imag(); + REQUIRE( other_mag < eps ); + } + + destroyQureg(qureg); + destroyPauliStrSum(ising); + } + + // Ground state: all qubits align down (driven by ferromagnetic interactions and bias) + { + Qureg qureg = createQureg(numQubits); + initPlusState(qureg); + + PauliStrSum ising = createIsingHamiltonian(numQubits, 0.0, 10.0, 10.0); + + for (int i = 0; i < steps; ++i) { + applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps); + setQuregToRenormalized(qureg); + } + + qcomp amp = getQuregAmp(qureg, 0); + qreal amp_mag = amp.real() * amp.real() + amp.imag() * amp.imag(); + + REQUIRE_THAT( amp_mag, WithinAbs(1.0, eps) ); + + for (long long i = 1; i < (1LL << numQubits); i++) { + qcomp other_amp = getQuregAmp(qureg, i); + qreal other_mag = other_amp.real() * other_amp.real() + + other_amp.imag() * other_amp.imag(); + REQUIRE( other_mag < eps ); + } + + destroyQureg(qureg); + destroyPauliStrSum(ising); + } + + // Ground state: alternating pattern (driven by antiferromagnetic interactions) + { + Qureg qureg = createQureg(numQubits); + initPlusState(qureg); + + PauliStrSum ising = createIsingHamiltonian(numQubits, 0.0, -10.0, 10.0); + + for (int i = 0; i < steps; ++i) { + applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps); + setQuregToRenormalized(qureg); + } + + unsigned long long idx = 0; + for (int i = 0; i < numQubits / 2; ++i) { + idx += (1ULL << (2*i + 1)); + } + + qcomp amp = getQuregAmp(qureg, idx); + qreal amp_mag = amp.real() * amp.real() + amp.imag() * amp.imag(); + + REQUIRE_THAT( amp_mag, WithinAbs(1.0, eps) ); + + for (long long i = 0; i < (1LL << numQubits); i++) { + if (i == idx) continue; + qcomp other_amp = getQuregAmp(qureg, i); + qreal other_mag = other_amp.real() * other_amp.real() + + other_amp.imag() * other_amp.imag(); + REQUIRE( other_mag < eps ); + } + + destroyQureg(qureg); + destroyPauliStrSum(ising); + } + } + + SECTION( LABEL_VALIDATION ) { + + int numQubits = 5; + Qureg qureg = createQureg(numQubits); + PauliStrSum ising = createIsingHamiltonian(numQubits, 1.0, 1.0, 0.0); + + SECTION( "qureg uninitialised" ) { + Qureg badQureg = qureg; + badQureg.numQubits = -1; + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(badQureg, ising, 0.1, 4, 5), + ContainsSubstring("invalid Qureg") + ); + } + + SECTION( "pauli sum uninitialized" ) { + PauliStrSum badIsing = ising; + badIsing.numTerms = 0; + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(qureg, badIsing, 0.1, 4, 5), + ContainsSubstring("Pauli") + ); + } + + SECTION( "pauli sum exceeds qureg qubits" ) { + Qureg smallQureg = createQureg(3); + PauliStrSum largeIsing = createIsingHamiltonian(numQubits, 1.0, 1.0, 0.0); + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(smallQureg, largeIsing, 0.1, 4, 5), + ContainsSubstring("only compatible") + ); + destroyQureg(smallQureg); + destroyPauliStrSum(largeIsing); + } + + SECTION( "hamiltonian not hermitian" ) { + vector strings; + vector coeffs; + strings.push_back(getPauliStr("X", {0})); + coeffs.push_back(getQcomp(1.0, 1.0)); + PauliStrSum nonHermitian = createPauliStrSum(strings, coeffs); + + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(qureg, nonHermitian, 0.1, 4, 5), + ContainsSubstring("Hermitian") + ); + destroyPauliStrSum(nonHermitian); + } + + SECTION( "invalid trotter order (zero)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 0, 5), + ContainsSubstring("order") + ); + } + + SECTION( "invalid trotter order (negative)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, -2, 5), + ContainsSubstring("order") + ); + } + + SECTION( "invalid trotter order (odd, not 1)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 3, 5), + ContainsSubstring("order") + ); + } + + SECTION( "invalid trotter reps (zero)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 4, 0), + ContainsSubstring("repetitions") + ); + } + + SECTION( "invalid trotter reps (negative)" ) { + REQUIRE_THROWS_WITH( + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 4, -3), + ContainsSubstring("repetitions") + ); + } + + destroyQureg(qureg); + destroyPauliStrSum(ising); + } +} From 019ab1d176bda63dca68f0f963b3d767a534b8ea Mon Sep 17 00:00:00 2001 From: Oliver Thomson Brown Date: Thu, 15 Jan 2026 17:30:42 +0000 Subject: [PATCH 2/3] tests/unit/trotterisation.cpp: updated time evo calls to new API --- tests/unit/trotterisation.cpp | 58 +++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/tests/unit/trotterisation.cpp b/tests/unit/trotterisation.cpp index 786b1937..41764ed7 100644 --- a/tests/unit/trotterisation.cpp +++ b/tests/unit/trotterisation.cpp @@ -178,13 +178,12 @@ PauliStrSum createIsingHamiltonian(int numQubits, qreal magField, * TESTS */ + /** * @todo * Basic validation for randomisation, should be expanded and merged * once the Trotterisation function tests have been implemented. */ - - TEST_CASE( "randomisedTrotter", TEST_CATEGORY ) { SECTION( LABEL_CORRECTNESS ) { @@ -212,14 +211,18 @@ TEST_CASE( "randomisedTrotter", TEST_CATEGORY ) { } } - -TEST_CASE( "applyTrotterizedUnitaryTimeEvolution", TEST_CATEGORY ) { +/* +* Time evolution tests +* @todo Add Pauli permutation variants +*/ +TEST_CASE( "applyTrotterizedUnitaryTimeEvolution", TEST_CATEGORY ) { SECTION( LABEL_CORRECTNESS ) { int numQubits = 20; Qureg qureg = createQureg(numQubits); initPlusState(qureg); + bool permutePaulis = false; PauliStrSum hamil = createHeisenbergHamiltonian(numQubits); PauliStrSum observ = createAlternatingPauliObservable(numQubits); @@ -247,7 +250,7 @@ TEST_CASE( "applyTrotterizedUnitaryTimeEvolution", TEST_CATEGORY ) { }; for (int i = 0; i < steps; i++) { - applyTrotterizedUnitaryTimeEvolution(qureg, hamil, dt, order, reps); + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, dt, order, reps, permutePaulis); qreal expec = calcExpecPauliStrSum(qureg, observ); REQUIRE_THAT( expec, WithinAbs(refObservables[i], eps) ); @@ -266,12 +269,13 @@ TEST_CASE( "applyTrotterizedUnitaryTimeEvolution", TEST_CATEGORY ) { int numQubits = 5; Qureg qureg = createQureg(numQubits); PauliStrSum hamil = createHeisenbergHamiltonian(numQubits); + bool permutePaulis = false; SECTION( "qureg uninitialised" ) { Qureg badQureg = qureg; badQureg.numQubits = -1; REQUIRE_THROWS_WITH( - applyTrotterizedUnitaryTimeEvolution(badQureg, hamil, 0.1, 4, 5), + applyTrotterizedUnitaryTimeEvolution(badQureg, hamil, 0.1, 4, 5, permutePaulis), ContainsSubstring("invalid Qureg") ); } @@ -280,7 +284,7 @@ TEST_CASE( "applyTrotterizedUnitaryTimeEvolution", TEST_CATEGORY ) { PauliStrSum badHamil = hamil; badHamil.numTerms = 0; REQUIRE_THROWS_WITH( - applyTrotterizedUnitaryTimeEvolution(qureg, badHamil, 0.1, 4, 5), + applyTrotterizedUnitaryTimeEvolution(qureg, badHamil, 0.1, 4, 5, permutePaulis), ContainsSubstring("Pauli") ); } @@ -289,7 +293,7 @@ TEST_CASE( "applyTrotterizedUnitaryTimeEvolution", TEST_CATEGORY ) { Qureg smallQureg = createQureg(3); PauliStrSum largeHamil = createHeisenbergHamiltonian(numQubits); REQUIRE_THROWS_WITH( - applyTrotterizedUnitaryTimeEvolution(smallQureg, largeHamil, 0.1, 4, 5), + applyTrotterizedUnitaryTimeEvolution(smallQureg, largeHamil, 0.1, 4, 5, permutePaulis), ContainsSubstring("only compatible") ); destroyQureg(smallQureg); @@ -298,35 +302,35 @@ TEST_CASE( "applyTrotterizedUnitaryTimeEvolution", TEST_CATEGORY ) { SECTION( "invalid trotter order (zero)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 0, 5), + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 0, 5, permutePaulis), ContainsSubstring("order") ); } SECTION( "invalid trotter order (negative)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, -2, 5), + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, -2, 5, permutePaulis), ContainsSubstring("order") ); } SECTION( "invalid trotter order (odd, not 1)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 3, 5), + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 3, 5, permutePaulis), ContainsSubstring("order") ); } SECTION( "invalid trotter reps (zero)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 4, 0), + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 4, 0, permutePaulis), ContainsSubstring("repetitions") ); } SECTION( "invalid trotter reps (negative)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 4, -3), + applyTrotterizedUnitaryTimeEvolution(qureg, hamil, 0.1, 4, -3, permutePaulis), ContainsSubstring("repetitions") ); } @@ -346,6 +350,7 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { int order = 6; int reps = 5; int steps = 10; + bool permutePaulis = false; // Tolerance for ground state amplitude qreal eps = 1E-2; @@ -358,7 +363,7 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { PauliStrSum ising = createIsingHamiltonian(numQubits, 10.0, 0.0, 0.0); for (int i = 0; i < steps; ++i) { - applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps); + applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps, permutePaulis); setQuregToRenormalized(qureg); } @@ -386,7 +391,7 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { PauliStrSum ising = createIsingHamiltonian(numQubits, -10.0, 0.0, 0.0); for (int i = 0; i < steps; ++i) { - applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps); + applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps, permutePaulis); setQuregToRenormalized(qureg); } @@ -416,7 +421,7 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { PauliStrSum ising = createIsingHamiltonian(numQubits, 0.0, 10.0, 10.0); for (int i = 0; i < steps; ++i) { - applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps); + applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps, permutePaulis); setQuregToRenormalized(qureg); } @@ -444,7 +449,7 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { PauliStrSum ising = createIsingHamiltonian(numQubits, 0.0, -10.0, 10.0); for (int i = 0; i < steps; ++i) { - applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps); + applyTrotterizedImaginaryTimeEvolution(qureg, ising, tau, order, reps, permutePaulis); setQuregToRenormalized(qureg); } @@ -476,12 +481,13 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { int numQubits = 5; Qureg qureg = createQureg(numQubits); PauliStrSum ising = createIsingHamiltonian(numQubits, 1.0, 1.0, 0.0); + bool permutePaulis = false; SECTION( "qureg uninitialised" ) { Qureg badQureg = qureg; badQureg.numQubits = -1; REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(badQureg, ising, 0.1, 4, 5), + applyTrotterizedImaginaryTimeEvolution(badQureg, ising, 0.1, 4, 5, permutePaulis), ContainsSubstring("invalid Qureg") ); } @@ -490,7 +496,7 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { PauliStrSum badIsing = ising; badIsing.numTerms = 0; REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(qureg, badIsing, 0.1, 4, 5), + applyTrotterizedImaginaryTimeEvolution(qureg, badIsing, 0.1, 4, 5, permutePaulis), ContainsSubstring("Pauli") ); } @@ -499,7 +505,7 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { Qureg smallQureg = createQureg(3); PauliStrSum largeIsing = createIsingHamiltonian(numQubits, 1.0, 1.0, 0.0); REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(smallQureg, largeIsing, 0.1, 4, 5), + applyTrotterizedImaginaryTimeEvolution(smallQureg, largeIsing, 0.1, 4, 5, permutePaulis), ContainsSubstring("only compatible") ); destroyQureg(smallQureg); @@ -514,7 +520,7 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { PauliStrSum nonHermitian = createPauliStrSum(strings, coeffs); REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(qureg, nonHermitian, 0.1, 4, 5), + applyTrotterizedImaginaryTimeEvolution(qureg, nonHermitian, 0.1, 4, 5, permutePaulis), ContainsSubstring("Hermitian") ); destroyPauliStrSum(nonHermitian); @@ -522,35 +528,35 @@ TEST_CASE( "applyTrotterizedImaginaryTimeEvolution", TEST_CATEGORY ) { SECTION( "invalid trotter order (zero)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 0, 5), + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 0, 5, permutePaulis), ContainsSubstring("order") ); } SECTION( "invalid trotter order (negative)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, -2, 5), + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, -2, 5, permutePaulis), ContainsSubstring("order") ); } SECTION( "invalid trotter order (odd, not 1)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 3, 5), + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 3, 5, permutePaulis), ContainsSubstring("order") ); } SECTION( "invalid trotter reps (zero)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 4, 0), + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 4, 0, permutePaulis), ContainsSubstring("repetitions") ); } SECTION( "invalid trotter reps (negative)" ) { REQUIRE_THROWS_WITH( - applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 4, -3), + applyTrotterizedImaginaryTimeEvolution(qureg, ising, 0.1, 4, -3, permutePaulis), ContainsSubstring("repetitions") ); } From 7356386faf4b0336adc8c49b22fecb81ec4e627d Mon Sep 17 00:00:00 2001 From: Oliver Thomson Brown Date: Thu, 15 Jan 2026 17:32:59 +0000 Subject: [PATCH 3/3] tests/unit/trotterisation.cpp: updated authorlist --- tests/unit/trotterisation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/trotterisation.cpp b/tests/unit/trotterisation.cpp index 41764ed7..121b36b4 100644 --- a/tests/unit/trotterisation.cpp +++ b/tests/unit/trotterisation.cpp @@ -2,6 +2,9 @@ * Unit tests of the trotterisation module. * * @author Tyson Jones + * @author Vasco Ferreira (initial Pauli permutation tests) + * @author Maurice Jamieson (real and imaginary time evolution tests) + * @author Oliver Thomson Brown (real and imaginary time evolution tests) * * @defgroup unittrotter Trotterisation * @ingroup unittests