diff --git a/quest/src/api/calculations.cpp b/quest/src/api/calculations.cpp index 47e5d8a63..60c517681 100644 --- a/quest/src/api/calculations.cpp +++ b/quest/src/api/calculations.cpp @@ -86,6 +86,7 @@ extern "C" void _wrap_calcExpecNonHermitianPauliStrSum(qcomp* out, Qureg qureg, qcomp calcExpecNonHermitianFullStateDiagMatr(Qureg qureg, FullStateDiagMatr matrix) { validate_quregFields(qureg, __func__); validate_matrixFields(matrix, __func__); + validate_matrixIsSynced(matrix, __func__); validate_matrixAndQuregAreCompatible(matrix, qureg, true, __func__); return calcExpecNonHermitianFullStateDiagMatrPower(qureg, matrix, 1); // harmlessly re-validates @@ -99,6 +100,7 @@ extern "C" void _wrap_calcExpecNonHermitianFullStateDiagMatr(qcomp* out, Qureg q qcomp calcExpecNonHermitianFullStateDiagMatrPower(Qureg qureg, FullStateDiagMatr matrix, qcomp exponent) { validate_quregFields(qureg, __func__); validate_matrixFields(matrix, __func__); + validate_matrixIsSynced(matrix, __func__); validate_matrixAndQuregAreCompatible(matrix, qureg, true, __func__); // this function never uses the qreal-pow overload (because we make diff --git a/quest/src/core/validation.cpp b/quest/src/core/validation.cpp index beeae12f1..c8994ebed 100644 --- a/quest/src/core/validation.cpp +++ b/quest/src/core/validation.cpp @@ -470,13 +470,13 @@ namespace report { string COMP_MATR_NOT_SYNCED_TO_GPU = - "The CompMatr has yet not been synchronised with its persistent GPU memory, so potential changes to its elements are being ignored. Please call syncCompMatr() after manually modifying elements, or overwrite all elements with setCompMatr() which automatically synchronises."; + "The CompMatr has yet not been synchronised, so potential changes to its elements may be ignored. Please call syncCompMatr() after manually modifying elements, or overwrite all elements with setCompMatr() which automatically synchronises."; string DIAG_MATR_NOT_SYNCED_TO_GPU = - "The DiagMatr has yet not been synchronised with its persistent GPU memory, so potential changes to its elements are being ignored. Please call syncDiagMatr() after manually modifying elements, or overwrite all elements with setDiagMatr() which automatically synchronises."; + "The DiagMatr has yet not been synchronised, so potential changes to its elements may be ignored. Please call syncDiagMatr() after manually modifying elements, or overwrite all elements with setDiagMatr() which automatically synchronises."; string FULL_STATE_DIAG_MATR_NOT_SYNCED_TO_GPU = - "The FullStateDiagMatr has yet not been synchronised with its persistent GPU memory, so potential changes to its elements are being ignored. Please call syncFullStateDiagMatr() after manually modifying elements, or overwrite elements in batch with setFullStateDiagMatr() which automatically synchronises."; + "The FullStateDiagMatr has yet not been synchronised, so potential changes to its elements may be ignored. Please call syncFullStateDiagMatr() after manually modifying elements, or overwrite elements in batch with setFullStateDiagMatr() which automatically synchronises."; string MATRIX_SIZE_MISMATCHES_NUM_TARGETS = @@ -580,7 +580,7 @@ namespace report { "The given SuperOp's pointer to its GPU memory was NULL. This may imply the superoperator was already destroyed and had its memory pointers manually overwritten by the user."; string SUPER_OP_NOT_SYNCED_TO_GPU = - "The SuperOp has yet not been synchronised with its persistent GPU memory, so potential changes to its elements are being ignored. Please call syncSuperOp() after manually modifying the superoperator elements, or overwrite all elements with setSuperOp() which will automatically syncrhonise."; + "The SuperOp has yet not been synchronised, so potential changes to its elements may be ignored. Please call syncSuperOp() after manually modifying the superoperator elements, or overwrite all elements with setSuperOp() which will automatically synchronise."; string SUPER_OP_SIZE_MISMATCHES_NUM_TARGETS = @@ -688,7 +688,7 @@ namespace report { string KRAUS_MAP_NOT_SYNCED_TO_GPU = - "The KrausMap has yet not been synchronised with its persistent GPU memory, so potential changes to its elements are being ignored. Please call syncKrausMap() after manually modifying the superoperator elements, or overwrite all elements with setKrausMap() which will automatically syncrhonise."; + "The KrausMap has yet not been synchronised, so potential changes to its elements may be ignored. Please call syncKrausMap() after manually modifying the Kraus operator elements, or overwrite all elements with setKrausMap() which will automatically synchronise."; string KRAUS_MAP_NOT_CPTP = "The KrausMap was not (approximately) completely positive and trace preserving (CPTP)."; @@ -2758,13 +2758,9 @@ void validate_matrixFields(FullStateDiagMatr m, const char* caller) { template void assertMatrixIsSynced(T matr, string errMsg, const char* caller) { - // we don't need to perform any sync check in CPU-only mode - if (!mem_isAllocated(util_getGpuMemPtr(matr))) - return; - - // check if GPU amps have EVER been overwritten; we sadly cannot check the LATEST changes were pushed though. - // note we check this whenever the matrix has GPU memory, even if it is being applied upon a Qureg which is - // NOT GPU-accelerated and ergo the GPU memory is not consulted. It's best to build the habit in the user! + // Check if sync was EVER performed; we sadly cannot check the LATEST changes were pushed though. + // We intentionally enforce this even in CPU-only mode because sync also marks the matrix as + // intentionally initialised after direct user modification. assertThat(*(matr.wasGpuSynced) == 1, errMsg, caller); } void validate_matrixIsSynced(CompMatr matr, const char* caller) { @@ -3393,11 +3389,9 @@ void validate_superOpIsSynced(SuperOp op, const char* caller) { if (!global_isValidationEnabled) return; - // we don't need to perform any sync check in CPU-only mode - if (!mem_isAllocated(util_getGpuMemPtr(op))) - return; - - // check if GPU amps have EVER been overwritten; we sadly cannot check the LATEST changes were pushed though + // Check if sync was EVER performed; we sadly cannot check the LATEST changes were pushed though. + // We intentionally enforce this even in CPU-only mode because sync also marks the superoperator + // as intentionally initialised after direct user modification. assertThat(*(op.wasGpuSynced), report::SUPER_OP_NOT_SYNCED_TO_GPU, caller); } @@ -3616,11 +3610,7 @@ void validate_krausMapIsSynced(KrausMap map, const char* caller) { if (!global_isValidationEnabled) return; - // we don't need to perform any sync check in CPU-only mode - if (!mem_isAllocated(util_getGpuMemPtr(map.superop))) - return; - - // assert the map's superoperator has been synced + // Assert the derived superoperator has been computed by syncKrausMap(), even in CPU-only mode. assertThat(*(map.superop.wasGpuSynced), report::KRAUS_MAP_NOT_SYNCED_TO_GPU, caller); } diff --git a/tests/unit/calculations.cpp b/tests/unit/calculations.cpp index 4b4db284e..8ba43b5bf 100644 --- a/tests/unit/calculations.cpp +++ b/tests/unit/calculations.cpp @@ -597,7 +597,20 @@ TEST_CASE( "calcExpecNonHermitianFullStateDiagMatr", TEST_CATEGORY LABEL_MIXED_D } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + + Qureg qureg = getArbitraryCachedStatevec(); + FullStateDiagMatr matr = createFullStateDiagMatr(qureg.numQubits); + + SECTION( "matrix unsynced" ) { + + REQUIRE_THROWS_WITH( + calcExpecNonHermitianFullStateDiagMatr(qureg, matr), + ContainsSubstring("not been synchronised") ); + } + + destroyFullStateDiagMatr(matr); + } } @@ -700,7 +713,20 @@ TEST_CASE( "calcExpecNonHermitianFullStateDiagMatrPower", TEST_CATEGORY LABEL_MI } } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + + Qureg qureg = getArbitraryCachedStatevec(); + FullStateDiagMatr matr = createFullStateDiagMatr(qureg.numQubits); + + SECTION( "matrix unsynced" ) { + + REQUIRE_THROWS_WITH( + calcExpecNonHermitianFullStateDiagMatrPower(qureg, matr, 1), + ContainsSubstring("not been synchronised") ); + } + + destroyFullStateDiagMatr(matr); + } } diff --git a/tests/unit/channels.cpp b/tests/unit/channels.cpp index e068368c5..f3dab5f2a 100644 --- a/tests/unit/channels.cpp +++ b/tests/unit/channels.cpp @@ -763,6 +763,38 @@ TEST_CASE( "createInlineSuperOp", TEST_CATEGORY ) { } +TEST_CASE( "reportKrausMap", TEST_CATEGORY ) { + + SECTION( LABEL_VALIDATION ) { + + KrausMap map = createKrausMap(1, 1); + + SECTION( "not synced" ) { + + REQUIRE_THROWS_WITH( reportKrausMap(map), ContainsSubstring("not been synchronised") ); + } + + destroyKrausMap(map); + } +} + + +TEST_CASE( "reportSuperOp", TEST_CATEGORY ) { + + SECTION( LABEL_VALIDATION ) { + + SuperOp op = createSuperOp(1); + + SECTION( "not synced" ) { + + REQUIRE_THROWS_WITH( reportSuperOp(op), ContainsSubstring("not been synchronised") ); + } + + destroySuperOp(op); + } +} + + /** @} (end defgroup) */ diff --git a/tests/unit/decoherence.cpp b/tests/unit/decoherence.cpp index 60b4cd640..3333b52f1 100644 --- a/tests/unit/decoherence.cpp +++ b/tests/unit/decoherence.cpp @@ -298,7 +298,18 @@ TEST_CASE( "mixKrausMap", TEST_CATEGORY ) { destroyKrausMap(map); } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + + Qureg qureg = getArbitraryCachedDensmatr(); + int targ = 0; + + SECTION( "map unsynced" ) { + + KrausMap map = createKrausMap(1, 1); + REQUIRE_THROWS_WITH( mixKrausMap(qureg, &targ, 1, map), ContainsSubstring("sync") ); + destroyKrausMap(map); + } + } } @@ -325,7 +336,18 @@ TEST_CASE( "mixSuperOp", TEST_CATEGORY ) { destroySuperOp(superOp); } - /// @todo input validation + SECTION( LABEL_VALIDATION ) { + + Qureg qureg = getArbitraryCachedDensmatr(); + int targ = 0; + + SECTION( "superoperator unsynced" ) { + + SuperOp op = createSuperOp(1); + REQUIRE_THROWS_WITH( mixSuperOp(qureg, &targ, 1, op), ContainsSubstring("sync") ); + destroySuperOp(op); + } + } } diff --git a/tests/unit/matrices.cpp b/tests/unit/matrices.cpp index 05abe33a2..d969a6edc 100644 --- a/tests/unit/matrices.cpp +++ b/tests/unit/matrices.cpp @@ -1287,6 +1287,22 @@ TEST_CASE( "createInlineDiagMatr", TEST_CATEGORY ) { } +TEST_CASE( "reportCompMatr", TEST_CATEGORY ) { + + SECTION( LABEL_VALIDATION ) { + + CompMatr matr = createCompMatr(1); + + SECTION( "not synced" ) { + + REQUIRE_THROWS_WITH( reportCompMatr(matr), ContainsSubstring("not been synchronised") ); + } + + destroyCompMatr(matr); + } +} + + /** @} (end defgroup) */ diff --git a/tests/unit/operations.cpp b/tests/unit/operations.cpp index 80b75b9c2..40db4560e 100644 --- a/tests/unit/operations.cpp +++ b/tests/unit/operations.cpp @@ -1029,9 +1029,6 @@ void testOperationValidation(auto operation) { SECTION( "matrix unsynced" ) { - if (!getQuESTEnv().isGpuAccelerated) - return; - // only relevant to variable-size matrix functions if constexpr (Targs == any && (Args == compmatr || Args == diagmatr || Args == diagpower)) *(std::get<0>(furtherArgs).wasGpuSynced) = 0;