diff --git a/quest/src/api/operations.cpp b/quest/src/api/operations.cpp index 54807c16..850a992f 100644 --- a/quest/src/api/operations.cpp +++ b/quest/src/api/operations.cpp @@ -518,7 +518,7 @@ void applyMultiControlledS(Qureg qureg, int* controls, int numControls, int targ void applyMultiStateControlledS(Qureg qureg, int* controls, int* states, int numControls, int target) { - DiagMatr1 matr = getDiagMatr1({1, 1_i}); + static const DiagMatr1 matr = getDiagMatr1({1, 1_i}); validateAndApplyAnyCtrlAnyTargUnitaryMatrix(qureg, controls, states, numControls, &target, 1, matr, __func__); } @@ -569,7 +569,7 @@ void applyMultiControlledT(Qureg qureg, int* controls, int numControls, int targ void applyMultiStateControlledT(Qureg qureg, int* controls, int* states, int numControls, int target) { - DiagMatr1 matr = getDiagMatr1({1, 1/std::sqrt(2) + 1_i/std::sqrt(2)}); + static const DiagMatr1 matr = getDiagMatr1({1, (1 + 1_i)/std::sqrt(2)}); validateAndApplyAnyCtrlAnyTargUnitaryMatrix(qureg, controls, states, numControls, &target, 1, matr, __func__); } @@ -620,11 +620,11 @@ void applyMultiControlledHadamard(Qureg qureg, int* controls, int numControls, i void applyMultiStateControlledHadamard(Qureg qureg, int* controls, int* states, int numControls, int target) { - qcomp a = 1/std::sqrt(2); - CompMatr1 matr = getCompMatr1({ - {a, a}, - {a,-a}}); - + static const qcomp a = 1 / std::sqrt(2); + static const CompMatr1 matr = getCompMatr1({ + {a, a}, + {a, -a} + }); validateAndApplyAnyCtrlAnyTargUnitaryMatrix(qureg, controls, states, numControls, &target, 1, matr, __func__); } @@ -749,7 +749,7 @@ void applyMultiStateControlledSqrtSwap(Qureg qureg, int* controls, int* states, validate_mixedAmpsFitInNode(qureg, 2, __func__); // to throw SqrtSwap error, not generic CompMatr2 error - CompMatr2 matr = getCompMatr2({ + static const CompMatr2 matr = getCompMatr2({ {1, 0, 0, 0}, {0, .5+.5_i, .5-.5_i, 0}, {0, .5-.5_i, .5+.5_i, 0}, @@ -869,7 +869,7 @@ void applyMultiStateControlledPauliX(Qureg qureg, int* controls, int* states, in /// since it avoids all superfluous flops; check worthwhile for multi-qubit // harmlessly re-validates, including hardcoded matrix unitarity - CompMatr1 matrix = util_getPauliX(); + static const CompMatr1 matrix = util_getPauliX(); validateAndApplyAnyCtrlAnyTargUnitaryMatrix(qureg, controls, states, numControls, &target, 1, matrix, __func__); } @@ -879,7 +879,7 @@ void applyMultiStateControlledPauliY(Qureg qureg, int* controls, int* states, in validate_controlStates(states, numControls, __func__); // permits states==nullptr // harmlessly re-validates, including hardcoded matrix unitarity - CompMatr1 matrix = util_getPauliY(); + static const CompMatr1 matrix = util_getPauliY(); validateAndApplyAnyCtrlAnyTargUnitaryMatrix(qureg, controls, states, numControls, &target, 1, matrix, __func__); } @@ -889,7 +889,7 @@ void applyMultiStateControlledPauliZ(Qureg qureg, int* controls, int* states, in validate_controlStates(states, numControls, __func__); // permits states==nullptr // harmlessly re-validates, including hardcoded matrix unitarity - DiagMatr1 matrix = util_getPauliZ(); + static const DiagMatr1 matrix = util_getPauliZ(); validateAndApplyAnyCtrlAnyTargUnitaryMatrix(qureg, controls, states, numControls, &target, 1, matrix, __func__); } @@ -1424,7 +1424,8 @@ void applyMultiQubitPhaseShift(Qureg qureg, int* targets, int numTargets, qreal validate_targets(qureg, targets, numTargets, __func__); // treat as a (numTargets-1)-controlled 1-target diagonal matrix - DiagMatr1 matr = getDiagMatr1({1, std::exp(1_i * angle)}); + static DiagMatr1 matr = getDiagMatr1({1, /*un-init*/ 0}); + matr.elems[1] = std::exp(1_i * angle); // micro-optimisation // harmlessly re-validates applyMultiStateControlledDiagMatr1(qureg, &targets[1], nullptr, numTargets-1, targets[0], matr); @@ -1467,7 +1468,7 @@ void applyMultiQubitPhaseFlip(Qureg qureg, int* targets, int numTargets) { validate_targets(qureg, targets, numTargets, __func__); // treat as a (numTargets-1)-controlled 1-target Pauli Z - DiagMatr1 matr = getDiagMatr1({1, -1}); + static const DiagMatr1 matr = getDiagMatr1({1, -1}); // harmlessly re-validates applyMultiStateControlledDiagMatr1(qureg, &targets[1], nullptr, numTargets-1, targets[0], matr); diff --git a/quest/src/core/accelerator.cpp b/quest/src/core/accelerator.cpp index 74a7bb9e..53a82296 100644 --- a/quest/src/core/accelerator.cpp +++ b/quest/src/core/accelerator.cpp @@ -29,11 +29,12 @@ #include "quest/src/cpu/cpu_subroutines.hpp" #include "quest/src/gpu/gpu_subroutines.hpp" +#include #include #include -using std::vector; using std::min; +using std::array; @@ -46,19 +47,16 @@ using std::min; * number of controls or targets exceeds that which have optimised compilations, * we fall back to using a generic implementation, indicated by <-1>. In essence, * these macros simply call func albeit without illegally passing - * a runtime variable as a template parameter. Note an awkward use of decltype() - * is to workaround a GCC <12 bug with implicitly-typed vector initialisations. - * - * BEWARE that these macros are single-line expressions, so they can be used in - * braceless if/else or ternary operators - but stay vigilant! + * a runtime variable as a template parameter. */ -#define GET_FUNC_OPTIMISED_FOR_BOOL(funcname, value) \ + +#define GET_FUNC_OPTIMISED_FOR_BOOL( funcname, value ) \ ((value)? funcname : funcname) -#define GET_FUNC_OPTIMISED_FOR_TWO_BOOLS(funcname, b1, b2) \ +#define GET_FUNC_OPTIMISED_FOR_TWO_BOOLS( funcname, b1, b2 ) \ ((b1)? \ ((b2)? funcname : funcname) : \ ((b2)? funcname : funcname)) @@ -70,61 +68,74 @@ using std::min; ((value)? cpu_##funcsuffix : cpu_##funcsuffix )) -#if (MAX_OPTIMISED_NUM_CTRLS != 5) || (MAX_OPTIMISED_NUM_TARGS != 5) +#if (MAX_OPTIMISED_PARAM != 5) #error "The number of optimised, templated QuEST functions was inconsistent between accelerator's source and header." #endif +#define GET_TEMPLATE_PARAM( param ) \ + std::min((int) param, MAX_OPTIMISED_PARAM + 1) -#define GET_FUNC_OPTIMISED_FOR_NUM_QUREGS(f, numquregs) \ - (vector )> {&f<0>, &f<1>, &f<2>, &f<3>, &f<4>, &f<5>, &f<-1>}) \ - [std::min((int) numquregs, MAX_OPTIMISED_NUM_QUREGS + 1)] - -#define GET_FUNC_OPTIMISED_FOR_NUM_CTRLS(f, numctrls) \ - (vector )> {&f<0>, &f<1>, &f<2>, &f<3>, &f<4>, &f<5>, &f<-1>}) \ - [std::min((int) numctrls, MAX_OPTIMISED_NUM_CTRLS + 1)] - -#define GET_FUNC_OPTIMISED_FOR_NUM_TARGS(f, numtargs) \ - (vector )> {&f<0>, &f<1>, &f<2>, &f<3>, &f<4>, &f<5>, &f<-1>}) \ - [std::min((int) numtargs, MAX_OPTIMISED_NUM_TARGS + 1)] - -#define GET_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS(f, numctrls, numtargs) \ - (vector { \ - ARR(f) {&f<0,0>, &f<0,1>, &f<0,2>, &f<0,3>, &f<0,4>, &f<0,5>, &f<0,-1>}, \ - ARR(f) {&f<1,0>, &f<1,1>, &f<1,2>, &f<1,3>, &f<1,4>, &f<1,5>, &f<1,-1>}, \ - ARR(f) {&f<2,0>, &f<2,1>, &f<2,2>, &f<2,3>, &f<2,4>, &f<2,5>, &f<2,-1>}, \ - ARR(f) {&f<3,0>, &f<3,1>, &f<3,2>, &f<3,3>, &f<3,4>, &f<3,5>, &f<3,-1>}, \ - ARR(f) {&f<4,0>, &f<4,1>, &f<4,2>, &f<4,3>, &f<4,4>, &f<4,5>, &f<4,-1>}, \ - ARR(f) {&f<5,0>, &f<5,1>, &f<5,2>, &f<5,3>, &f<5,4>, &f<5,5>, &f<5,-1>}, \ - ARR(f) {&f<-1,0>, &f<-1,1>, &f<-1,2>, &f<-1,3>, &f<-1,4>, &f<-1,5>, &f<-1,-1>}}) \ - [std::min((int) numctrls, MAX_OPTIMISED_NUM_CTRLS + 1)] \ - [std::min((int) numtargs, MAX_OPTIMISED_NUM_TARGS + 1)] - -#define ARR(f) vector)> +#define GET_ONE_PARAM_TEMPLATED_FUNC_ARRAY( f ) \ + array {&f<0>, &f<1>, &f<2>, &f<3>, &f<4>, &f<5>, &f<-1>} -#define GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_QUREGS(funcsuffix, qureg, numquregs) \ - ((qureg.isGpuAccelerated)? \ - GET_FUNC_OPTIMISED_FOR_NUM_QUREGS( gpu_##funcsuffix, numquregs ) : \ - GET_FUNC_OPTIMISED_FOR_NUM_QUREGS( cpu_##funcsuffix, numquregs )) +#define GET_FUNC_OPTIMISED_FOR_ONE_PARAM( outvar, funcname, param ) \ + static constexpr auto (_ARRAY_##funcname) = GET_ONE_PARAM_TEMPLATED_FUNC_ARRAY( funcname ); \ + const auto outvar = (_ARRAY_##funcname)[GET_TEMPLATE_PARAM( param )]; -#define GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS(funcsuffix, qureg, numctrls) \ - ((qureg.isGpuAccelerated)? \ - GET_FUNC_OPTIMISED_FOR_NUM_CTRLS( gpu_##funcsuffix, numctrls ) : \ - GET_FUNC_OPTIMISED_FOR_NUM_CTRLS( cpu_##funcsuffix, numctrls )) +#define GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( outvar, funcsuffix, qureg, param ) \ + GET_FUNC_OPTIMISED_FOR_ONE_PARAM( _GPU_FUNC, gpu_##funcsuffix, param ) \ + GET_FUNC_OPTIMISED_FOR_ONE_PARAM( _CPU_FUNC, cpu_##funcsuffix, param ) \ + const auto outvar = qureg.isGpuAccelerated ? _GPU_FUNC : _CPU_FUNC; -#define GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_TARGS(funcsuffix, qureg, numtargs) \ - ((qureg.isGpuAccelerated)? \ - GET_FUNC_OPTIMISED_FOR_NUM_TARGS( gpu_##funcsuffix, numtargs ) : \ - GET_FUNC_OPTIMISED_FOR_NUM_TARGS( cpu_##funcsuffix, numtargs )) - -#define GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS(funcsuffix, qureg, numctrls, numtargs) \ - ((qureg.isGpuAccelerated)? \ - GET_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( gpu_##funcsuffix, numctrls, numtargs ) : \ - GET_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( cpu_##funcsuffix, numctrls, numtargs )) + +#define GET_TWO_PARAM_TEMPLATED_FUNC_MATRIX( f ) \ + array { \ + array {&f<0,0>, &f<0,1>, &f<0,2>, &f<0,3>, &f<0,4>, &f<0,5>, &f<0,-1>}, \ + array {&f<1,0>, &f<1,1>, &f<1,2>, &f<1,3>, &f<1,4>, &f<1,5>, &f<1,-1>}, \ + array {&f<2,0>, &f<2,1>, &f<2,2>, &f<2,3>, &f<2,4>, &f<2,5>, &f<2,-1>}, \ + array {&f<3,0>, &f<3,1>, &f<3,2>, &f<3,3>, &f<3,4>, &f<3,5>, &f<3,-1>}, \ + array {&f<4,0>, &f<4,1>, &f<4,2>, &f<4,3>, &f<4,4>, &f<4,5>, &f<4,-1>}, \ + array {&f<5,0>, &f<5,1>, &f<5,2>, &f<5,3>, &f<5,4>, &f<5,5>, &f<5,-1>}, \ + array {&f<-1,0>, &f<-1,1>, &f<-1,2>, &f<-1,3>, &f<-1,4>, &f<-1,5>, &f<-1,-1>}} + +#define GET_FUNC_OPTIMISED_FOR_TWO_PARAMS( outvar, funcname, param1, param2 ) \ + static constexpr auto (_MATRIX_##funcname) = GET_TWO_PARAM_TEMPLATED_FUNC_MATRIX( funcname ); \ + const auto outvar = (_MATRIX_##funcname)[GET_TEMPLATE_PARAM( param1 )][GET_TEMPLATE_PARAM( param2 )]; + +#define GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_TWO_PARAMS( outvar, funcsuffix, qureg, param1, param2 ) \ + GET_FUNC_OPTIMISED_FOR_TWO_PARAMS( _GPU_FUNC, gpu_##funcsuffix, param1, param2 ) \ + GET_FUNC_OPTIMISED_FOR_TWO_PARAMS( _CPU_FUNC, cpu_##funcsuffix, param1, param2 ) \ + const auto outvar = qureg.isGpuAccelerated ? _GPU_FUNC : _CPU_FUNC; + + +#define GET_TWO_PARAM_TWO_BOOL_SUB_MATRIX( f, b1, b2 ) \ + array { \ + array {&f<0,0,b1,b2>, &f<0,1,b1,b2>, &f<0,2,b1,b2>, &f<0,3,b1,b2>, &f<0,4,b1,b2>, &f<0,5,b1,b2>, &f<0,-1,b1,b2>}, \ + array {&f<1,0,b1,b2>, &f<1,1,b1,b2>, &f<1,2,b1,b2>, &f<1,3,b1,b2>, &f<1,4,b1,b2>, &f<1,5,b1,b2>, &f<1,-1,b1,b2>}, \ + array {&f<2,0,b1,b2>, &f<2,1,b1,b2>, &f<2,2,b1,b2>, &f<2,3,b1,b2>, &f<2,4,b1,b2>, &f<2,5,b1,b2>, &f<2,-1,b1,b2>}, \ + array {&f<3,0,b1,b2>, &f<3,1,b1,b2>, &f<3,2,b1,b2>, &f<3,3,b1,b2>, &f<3,4,b1,b2>, &f<3,5,b1,b2>, &f<3,-1,b1,b2>}, \ + array {&f<4,0,b1,b2>, &f<4,1,b1,b2>, &f<4,2,b1,b2>, &f<4,3,b1,b2>, &f<4,4,b1,b2>, &f<4,5,b1,b2>, &f<4,-1,b1,b2>}, \ + array {&f<5,0,b1,b2>, &f<5,1,b1,b2>, &f<5,2,b1,b2>, &f<5,3,b1,b2>, &f<5,4,b1,b2>, &f<5,5,b1,b2>, &f<5,-1,b1,b2>}, \ + array {&f<-1,0,b1,b2>, &f<-1,1,b1,b2>, &f<-1,2,b1,b2>, &f<-1,3,b1,b2>, &f<-1,4,b1,b2>, &f<-1,5,b1,b2>, &f<-1,-1,b1,b2>}} + +#define GET_TWO_PARAM_TWO_BOOL_TEMPLATED_FUNC_MATRIX( f ) \ + array { \ + array{ GET_TWO_PARAM_TWO_BOOL_SUB_MATRIX( f, 0, 0 ), GET_TWO_PARAM_TWO_BOOL_SUB_MATRIX( f, 0, 1 ) }, \ + array{ GET_TWO_PARAM_TWO_BOOL_SUB_MATRIX( f, 1, 0 ), GET_TWO_PARAM_TWO_BOOL_SUB_MATRIX( f, 1, 1 ) }} + +#define GET_FUNC_OPTIMISED_FOR_TWO_PARAMS_TWO_BOOLS( outvar, funcname, param1, param2, bool1, bool2 ) \ + static constexpr auto (_MATRIX_##funcname) = GET_TWO_PARAM_TWO_BOOL_TEMPLATED_FUNC_MATRIX( funcname ); \ + const auto outvar = (_MATRIX_##funcname)[bool1][bool2][GET_TEMPLATE_PARAM( param1 )][GET_TEMPLATE_PARAM( param2 )]; + +#define GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_TWO_PARAMS_TWO_BOOLS( outvar, funcsuffix, qureg, param1, param2, bool1, bool2 ) \ + GET_FUNC_OPTIMISED_FOR_TWO_PARAMS_TWO_BOOLS( _GPU_FUNC, gpu_##funcsuffix, param1, param2, bool1, bool2 ) \ + GET_FUNC_OPTIMISED_FOR_TWO_PARAMS_TWO_BOOLS( _CPU_FUNC, cpu_##funcsuffix, param1, param2, bool1, bool2 ) \ + const auto outvar = qureg.isGpuAccelerated ? _GPU_FUNC : _CPU_FUNC; /// @todo -/// GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS as defined below +/// GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_TWO_PARAMS_TWO_BOOLS as defined above /// is used by anyCtrlAnyTargDiagMatr and anyCtrlAnyTargDenseMatr; the /// latter only ever receives numTargs>=3 (due to accelerator redirecting /// fewer targets to faster bespoke functions which e.g. avoid global GPU @@ -134,40 +145,6 @@ using std::min; /// can ergo non-negligibly speed up compilation by avoiding these redundant /// instances at the cost of increased code complexity/asymmetry. Consider! -#define GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS(f, numctrls, numtargs, c, h) \ - (vector { \ - POWER_CONJ_ARR(f) {&f<0,0,c,h>, &f<0,1,c,h>, &f<0,2,c,h>, &f<0,3,c,h>, &f<0,4,c,h>, &f<0,5,c,h>, &f<0,-1,c,h>}, \ - POWER_CONJ_ARR(f) {&f<1,0,c,h>, &f<1,1,c,h>, &f<1,2,c,h>, &f<1,3,c,h>, &f<1,4,c,h>, &f<1,5,c,h>, &f<1,-1,c,h>}, \ - POWER_CONJ_ARR(f) {&f<2,0,c,h>, &f<2,1,c,h>, &f<2,2,c,h>, &f<2,3,c,h>, &f<2,4,c,h>, &f<2,5,c,h>, &f<2,-1,c,h>}, \ - POWER_CONJ_ARR(f) {&f<3,0,c,h>, &f<3,1,c,h>, &f<3,2,c,h>, &f<3,3,c,h>, &f<3,4,c,h>, &f<3,5,c,h>, &f<3,-1,c,h>}, \ - POWER_CONJ_ARR(f) {&f<4,0,c,h>, &f<4,1,c,h>, &f<4,2,c,h>, &f<4,3,c,h>, &f<4,4,c,h>, &f<4,5,c,h>, &f<4,-1,c,h>}, \ - POWER_CONJ_ARR(f) {&f<5,0,c,h>, &f<5,1,c,h>, &f<5,2,c,h>, &f<5,3,c,h>, &f<5,4,c,h>, &f<5,5,c,h>, &f<5,-1,c,h>}, \ - POWER_CONJ_ARR(f) {&f<-1,0,c,h>, &f<-1,1,c,h>, &f<-1,2,c,h>, &f<-1,3,c,h>, &f<-1,4,c,h>, &f<-1,5,c,h>, &f<-1,-1,c,h>}}) \ - [std::min((int) numctrls, MAX_OPTIMISED_NUM_CTRLS + 1)] \ - [std::min((int) numtargs, MAX_OPTIMISED_NUM_TARGS + 1)] - -#define POWER_CONJ_ARR(f) vector)> - -#define GET_CPU_OR_GPU_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS(funcsuffix, qureg, numctrls, numtargs, conj, haspower) \ - ((qureg.isGpuAccelerated)? \ - ((conj)? \ - ((haspower)? \ - GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( gpu_##funcsuffix, numctrls, numtargs, true, true ) : \ - GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( gpu_##funcsuffix, numctrls, numtargs, true, false ) ) : \ - ((haspower)? \ - GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( gpu_##funcsuffix, numctrls, numtargs, false, true ) : \ - GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( gpu_##funcsuffix, numctrls, numtargs, false, false ) ) ) : \ - ((conj)? \ - ((haspower)? \ - GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( cpu_##funcsuffix, numctrls, numtargs, true, true ) : \ - GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( cpu_##funcsuffix, numctrls, numtargs, true, false ) ) : \ - ((haspower)? \ - GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( cpu_##funcsuffix, numctrls, numtargs, false, true ) : \ - GET_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( cpu_##funcsuffix, numctrls, numtargs, false, false ) ) ) ) - -/// @todo -/// The above macro spaghetti is diabolical - update using C++ metaprogamming! - /* @@ -254,7 +231,7 @@ qindex accel_statevec_packAmpsIntoBuffer(Qureg qureg, SmallList qubits, SmallLis error_noCtrlsGivenToBufferPacker(); // note qubits may incidentally be ctrls or targs; it doesn't matter - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_TARGS( statevec_packAmpsIntoBuffer, qureg, qubits.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_packAmpsIntoBuffer, qureg, qubits.size() ); // return the number of packed amps, for caller convenience return func(qureg, qubits, qubitStates); @@ -277,17 +254,17 @@ qindex accel_statevec_packPairSummedAmpsIntoBuffer(Qureg qureg, int qubit1, int void accel_statevec_anyCtrlSwap_subA(Qureg qureg, SmallList ctrls, SmallList ctrlStates, int targ1, int targ2) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevec_anyCtrlSwap_subA, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_anyCtrlSwap_subA, qureg, ctrls.size() ); func(qureg, ctrls, ctrlStates, targ1, targ2); } void accel_statevec_anyCtrlSwap_subB(Qureg qureg, SmallList ctrls, SmallList ctrlStates) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevec_anyCtrlSwap_subB, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_anyCtrlSwap_subB, qureg, ctrls.size() ); func(qureg, ctrls, ctrlStates); } void accel_statevec_anyCtrlSwap_subC(Qureg qureg, SmallList ctrls, SmallList ctrlStates, int targ, int targState) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevec_anyCtrlSwap_subC, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_anyCtrlSwap_subC, qureg, ctrls.size() ); func(qureg, ctrls, ctrlStates, targ, targState); } @@ -300,26 +277,26 @@ void accel_statevec_anyCtrlSwap_subC(Qureg qureg, SmallList ctrls, SmallList ctr void accel_statevec_anyCtrlOneTargDenseMatr_subA(Qureg qureg, SmallList ctrls, SmallList ctrlStates, int targ, CompMatr1 matr) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevec_anyCtrlOneTargDenseMatr_subA, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_anyCtrlOneTargDenseMatr_subA, qureg, ctrls.size() ); func(qureg, ctrls, ctrlStates, targ, matr); } void accel_statevec_anyCtrlOneTargDenseMatr_subB(Qureg qureg, SmallList ctrls, SmallList ctrlStates, qcomp fac0, qcomp fac1) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevec_anyCtrlOneTargDenseMatr_subB, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_anyCtrlOneTargDenseMatr_subB, qureg, ctrls.size() ); func(qureg, ctrls, ctrlStates, fac0, fac1); } void accel_statevec_anyCtrlTwoTargDenseMatr_sub(Qureg qureg, SmallList ctrls, SmallList ctrlStates, int targ1, int targ2, CompMatr2 matr) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevec_anyCtrlTwoTargDenseMatr_sub, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_anyCtrlTwoTargDenseMatr_sub, qureg, ctrls.size() ); func(qureg, ctrls, ctrlStates, targ1, targ2, matr); } void accel_statevec_anyCtrlAnyTargDenseMatr_sub(Qureg qureg, SmallList ctrls, SmallList ctrlStates, SmallList targs, CompMatr matr, bool conj, bool transp) { - auto func = GET_CPU_OR_GPU_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( statevec_anyCtrlAnyTargDenseMatr_sub, qureg, ctrls.size(), targs.size(), conj, transp ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_TWO_PARAMS_TWO_BOOLS( func, statevec_anyCtrlAnyTargDenseMatr_sub, qureg, ctrls.size(), targs.size(), conj, transp ); func(qureg, ctrls, ctrlStates, targs, matr); } @@ -332,14 +309,14 @@ void accel_statevec_anyCtrlAnyTargDenseMatr_sub(Qureg qureg, SmallList ctrls, Sm void accel_statevec_anyCtrlOneTargDiagMatr_sub(Qureg qureg, SmallList ctrls, SmallList ctrlStates, int targ, DiagMatr1 matr) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevec_anyCtrlOneTargDiagMatr_sub, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_anyCtrlOneTargDiagMatr_sub, qureg, ctrls.size() ); func(qureg, ctrls, ctrlStates, targ, matr); } void accel_statevec_anyCtrlTwoTargDiagMatr_sub(Qureg qureg, SmallList ctrls, SmallList ctrlStates, int targ1, int targ2, DiagMatr2 matr) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevec_anyCtrlTwoTargDiagMatr_sub, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_anyCtrlTwoTargDiagMatr_sub, qureg, ctrls.size() ); func(qureg, ctrls, ctrlStates, targ1, targ2, matr); } @@ -348,7 +325,7 @@ void accel_statevec_anyCtrlAnyTargDiagMatr_sub(Qureg qureg, SmallList ctrls, Sma bool hasPower = exponent != qcomp(1, 0); - auto func = GET_CPU_OR_GPU_TWO_BOOL_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( statevec_anyCtrlAnyTargDiagMatr_sub, qureg, ctrls.size(), targs.size(), conj, hasPower ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_TWO_PARAMS_TWO_BOOLS( func, statevec_anyCtrlAnyTargDiagMatr_sub, qureg, ctrls.size(), targs.size(), conj, hasPower ); func(qureg, ctrls, ctrlStates, targs, matr, exponent); } @@ -526,19 +503,19 @@ void accel_statevector_anyCtrlPauliTensorOrGadget_subA(Qureg qureg, SmallList ct // only X and Y constitute target qubits (Z merely induces a phase) int numTargs = x.size() + y.size(); - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS( statevector_anyCtrlPauliTensorOrGadget_subA, qureg, ctrls.size(), numTargs ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_TWO_PARAMS( func, statevector_anyCtrlPauliTensorOrGadget_subA, qureg, ctrls.size(), numTargs ); func(qureg, ctrls, states, x, y, z, f0, f1); } void accel_statevector_anyCtrlPauliTensorOrGadget_subB(Qureg qureg, SmallList ctrls, SmallList states, SmallList x, SmallList y, SmallList z, qcomp f0, qcomp f1, qindex mask) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevector_anyCtrlPauliTensorOrGadget_subB, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevector_anyCtrlPauliTensorOrGadget_subB, qureg, ctrls.size() ); func(qureg, ctrls, states, x, y, z, f0, f1, mask); } void accel_statevector_anyCtrlAnyTargZOrPhaseGadget_sub(Qureg qureg, SmallList ctrls, SmallList states, SmallList targs, qcomp f0, qcomp f1) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_CTRLS( statevector_anyCtrlAnyTargZOrPhaseGadget_sub, qureg, ctrls.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevector_anyCtrlAnyTargZOrPhaseGadget_sub, qureg, ctrls.size() ); func(qureg, ctrls, states, targs, f0, f1); } @@ -549,10 +526,10 @@ void accel_statevector_anyCtrlAnyTargZOrPhaseGadget_sub(Qureg qureg, SmallList c */ -void accel_statevec_setQuregToWeightedSum_sub(Qureg outQureg, vector coeffs, vector inQuregs) { +void accel_statevec_setQuregToWeightedSum_sub(Qureg outQureg, std::vector coeffs, std::vector inQuregs) { // consult outQureg's deployment since others are prior validated to match - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_QUREGS( statevec_setQuregToWeightedSum_sub, outQureg, inQuregs.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_setQuregToWeightedSum_sub, outQureg, inQuregs.size() ); func(outQureg, coeffs, inQuregs); } @@ -849,12 +826,9 @@ void accel_densmatr_oneQubitDamping_subD(Qureg qureg, int qubit, qreal prob) { void accel_densmatr_partialTrace_sub(Qureg inQureg, Qureg outQureg, SmallList targs, SmallList pairTargs) { assert_partialTraceQuregsAreIdenticallyDeployed(inQureg, outQureg); - auto cpuFunc = GET_FUNC_OPTIMISED_FOR_NUM_TARGS( cpu_densmatr_partialTrace_sub, targs.size() ); - auto gpuFunc = GET_FUNC_OPTIMISED_FOR_NUM_TARGS( gpu_densmatr_partialTrace_sub, targs.size() ); - - // inQureg == outQureg except for dimension, so use common backend - auto useFunc = (inQureg.isGpuAccelerated)? gpuFunc : cpuFunc; - useFunc(inQureg, outQureg, targs, pairTargs); + // inQureg == outQureg (except for dimension), so use common backend, informed by inQureg + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, densmatr_partialTrace_sub, inQureg, targs.size() ); + func(inQureg, outQureg, targs, pairTargs); } @@ -880,24 +854,24 @@ qreal accel_densmatr_calcTotalProb_sub(Qureg qureg) { qreal accel_statevec_calcProbOfMultiQubitOutcome_sub(Qureg qureg, SmallList qubits, SmallList outcomes) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_TARGS( statevec_calcProbOfMultiQubitOutcome_sub, qureg, qubits.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_calcProbOfMultiQubitOutcome_sub, qureg, qubits.size() ); return func(qureg, qubits, outcomes); } qreal accel_densmatr_calcProbOfMultiQubitOutcome_sub(Qureg qureg, SmallList qubits, SmallList outcomes) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_TARGS( densmatr_calcProbOfMultiQubitOutcome_sub, qureg, qubits.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, densmatr_calcProbOfMultiQubitOutcome_sub, qureg, qubits.size() ); return func(qureg, qubits, outcomes); } void accel_statevec_calcProbsOfAllMultiQubitOutcomes_sub(qreal* outProbs, Qureg qureg, SmallList qubits) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_TARGS( statevec_calcProbsOfAllMultiQubitOutcomes_sub, qureg, qubits.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_calcProbsOfAllMultiQubitOutcomes_sub, qureg, qubits.size() ); func(outProbs, qureg, qubits); } void accel_densmatr_calcProbsOfAllMultiQubitOutcomes_sub(qreal* outProbs, Qureg qureg, SmallList qubits) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_TARGS( densmatr_calcProbsOfAllMultiQubitOutcomes_sub, qureg, qubits.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, densmatr_calcProbsOfAllMultiQubitOutcomes_sub, qureg, qubits.size() ); func(outProbs, qureg, qubits); } @@ -1113,12 +1087,12 @@ qcomp accel_densmatr_calcExpecFullStateDiagMatr_sub(Qureg qureg, FullStateDiagMa void accel_statevec_multiQubitProjector_sub(Qureg qureg, SmallList qubits, SmallList outcomes, qreal prob) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_TARGS( statevec_multiQubitProjector_sub, qureg, qubits.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, statevec_multiQubitProjector_sub, qureg, qubits.size() ); func(qureg, qubits, outcomes, prob); } void accel_densmatr_multiQubitProjector_sub(Qureg qureg, SmallList qubits, SmallList outcomes, qreal prob) { - auto func = GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_NUM_TARGS( densmatr_multiQubitProjector_sub, qureg, qubits.size() ); + GET_CPU_OR_GPU_FUNC_OPTIMISED_FOR_ONE_PARAM( func, densmatr_multiQubitProjector_sub, qureg, qubits.size() ); func(qureg, qubits, outcomes, prob); } diff --git a/quest/src/core/accelerator.hpp b/quest/src/core/accelerator.hpp index bd89bbb9..b1a2273f 100644 --- a/quest/src/core/accelerator.hpp +++ b/quest/src/core/accelerator.hpp @@ -28,8 +28,6 @@ #include -using std::vector; - /* * TEMPLATE INSTANTIATION MACROS @@ -44,9 +42,7 @@ using std::vector; */ // must match the macros below, and those in accelerator.cpp -#define MAX_OPTIMISED_NUM_CTRLS 5 -#define MAX_OPTIMISED_NUM_TARGS 5 -#define MAX_OPTIMISED_NUM_QUREGS 5 +#define MAX_OPTIMISED_PARAM 5 #define INSTANTIATE_FUNC_OPTIMISED_FOR_NUM_TARGS(returntype, funcname, args) \ @@ -84,10 +80,6 @@ using std::vector; template returntype funcname <-1,numtargs> args; -#define INSTANTIATE_CONJUGABLE_FUNC_OPTIMISED_FOR_NUM_CTRLS_AND_TARGS(returntype, funcname, args) \ - private_CONJUGABLE_INSTANTIATE_outer(returntype, funcname, true, args) \ - private_CONJUGABLE_INSTANTIATE_outer(returntype, funcname, false, args) - #define private_CONJUGABLE_INSTANTIATE_outer(returntype, funcname, conj, args) \ private_CONJUGABLE_INSTANTIATE_inner(returntype, funcname, 0, conj, args) \ private_CONJUGABLE_INSTANTIATE_inner(returntype, funcname, 1, conj, args) \ @@ -234,7 +226,7 @@ void accel_statevector_anyCtrlPauliTensorOrGadget_subB(Qureg qureg, SmallList ct * QUREG COMBINATION */ -void accel_statevec_setQuregToWeightedSum_sub(Qureg outQureg, vector coeffs, vector inQuregs); +void accel_statevec_setQuregToWeightedSum_sub(Qureg outQureg, std::vector coeffs, std::vector inQuregs); void accel_densmatr_mixQureg_subA(qreal outProb, Qureg out, qreal inProb, Qureg in); void accel_densmatr_mixQureg_subB(qreal outProb, Qureg out, qreal inProb, Qureg in); diff --git a/quest/src/core/validation.cpp b/quest/src/core/validation.cpp index 3b6fc18a..cf474096 100644 --- a/quest/src/core/validation.cpp +++ b/quest/src/core/validation.cpp @@ -1400,12 +1400,18 @@ bool doQuregsHaveIdenticalMemoryLayouts(Qureg a, Qureg b) { void validate_envNeverInit(bool isQuESTInit, bool isQuESTFinal, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(!isQuESTInit, report::QUEST_ENV_ALREADY_INIT, caller); assertThat(!isQuESTFinal, report::QUEST_ENV_ALREADY_FINAL, caller); } void validate_newEnvDeploymentMode(int isDistrib, int isGpuAccel, int isMultithread, const char* caller) { + if (!global_isValidationEnabled) + return; + // deployment flags must be boolean or auto tokenSubs vars = {{"${AUTO_DEPLOYMENT_FLAG}", modeflag::USE_AUTO}}; assertThat(isDistrib == 0 || isDistrib == 1 || isDistrib == modeflag::USE_AUTO, report::INVALID_OPTION_FOR_ENV_IS_DISTRIB, vars, caller); @@ -1435,6 +1441,9 @@ void validate_newEnvDeploymentMode(int isDistrib, int isGpuAccel, int isMultithr void validate_newEnvDistributedBetweenPower2Nodes(const char* caller) { + if (!global_isValidationEnabled) + return; + // note that we do NOT finalize MPI before erroring below, because that would necessitate // every node (launched by mpirun) serially print the error message, causing spam. // Instead, we permit the evil of every MPI process calling exit() and MPI aborting when @@ -1448,12 +1457,18 @@ void validate_newEnvDistributedBetweenPower2Nodes(const char* caller) { void validate_newEnvNodesEachHaveUniqueGpu(const char* caller) { + if (!global_isValidationEnabled) + return; + bool sharedGpus = gpu_areAnyNodesBoundToSameGpu(); assertAllNodesAgreeThat(!sharedGpus, report::MULTIPLE_NODES_BOUND_TO_SAME_GPU, caller); } void validate_gpuIsCuQuantumCompatible(const char* caller) { + if (!global_isValidationEnabled) + return; + int minCC = 70; int ourCC = gpu_getComputeCapability(); tokenSubs vars = { @@ -1474,6 +1489,9 @@ void validate_gpuIsCuQuantumCompatible(const char* caller) { void validate_envIsInit(const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(isQuESTEnvInit(), report::QUEST_ENV_NOT_INIT, caller); } @@ -1485,6 +1503,9 @@ void validate_envIsInit(const char* caller) { void validate_randomSeeds(unsigned* seeds, int numSeeds, const char* caller) { + if (!global_isValidationEnabled) + return; + // only the root node's seeds are consulted, so we permit all non-root // nodes to have invalid parameters. All nodes however must know/agree // when the root node's seeds are invalid, to synchronise validation @@ -1498,32 +1519,50 @@ void validate_randomSeeds(unsigned* seeds, int numSeeds, const char* caller) { void validate_newEpsilonValue(qreal eps, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(eps >= 0, report::INVALID_NEW_EPSILON, {{"${NEW_EPS}", eps}}, caller); } void validate_newMaxNumReportedScalars(qindex numRows, qindex numCols, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(numRows >= 0, report::INVALID_NUM_REPORTED_SCALARS, {{"${NUM_ITEMS}", numRows}}, caller); assertThat(numCols >= 0, report::INVALID_NUM_REPORTED_SCALARS, {{"${NUM_ITEMS}", numCols}}, caller); } void validate_newMaxNumReportedSigFigs(int numSigFigs, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(numSigFigs >= 1, report::INVALID_NUM_REPORTED_SIG_FIGS, {{"${NUM_SIG_FIGS}", numSigFigs}}, caller); } void validate_newNumReportedNewlines(int numNewlines, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(numNewlines >= 0, report::INVALID_NUM_REPORTED_NEWLINES, {{"${NUM_NEWLINES}", numNewlines}}, caller); } void validate_numReportedNewlinesAboveZero(const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(printer_getNumTrailingNewlines() > 0, report::INSUFFICIENT_NUM_REPORTED_NEWLINES, caller); } void validate_numPauliChars(const char* paulis, const char* caller) { + if (!global_isValidationEnabled) + return; + // check position of terminal char, else default to numChars=5 (illegal) int numChars = 0; for (int i=0; i<5 && paulis[i] != '\0'; i++) @@ -1534,6 +1573,9 @@ void validate_numPauliChars(const char* paulis, const char* caller) { void validate_reportedPauliStrStyleFlag(int flag, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(flag==0 || flag==1, report::INVALID_REPORTED_PAULI_STR_STYLE_FLAG, {{"${FLAG}",flag}}, caller); } @@ -1707,8 +1749,6 @@ void assertQuregFitsInGpuMem(int numQubits, int isDensMatr, int isDistrib, int i void validate_newQuregParams(int numQubits, int isDensMatr, int isDistrib, int isGpuAccel, int isMultithread, QuESTEnv env, const char* caller) { - // some of the below validation involves getting distributed node consensus, which - // can be an expensive synchronisation, which we avoid if validation is anyway disabled if (!global_isValidationEnabled) return; @@ -1724,6 +1764,9 @@ void validate_newQuregParams(int numQubits, int isDensMatr, int isDistrib, int i void validate_newQuregAllocs(Qureg qureg, const char* caller) { + if (!global_isValidationEnabled) + return; + // this validation is called AFTER the caller has checked for failed // allocs and (in that scenario) freed every pointer, but does not // overwrite any pointers to nullptr, so the failed alloc is known. @@ -1752,6 +1795,9 @@ void validate_newQuregAllocs(Qureg qureg, const char* caller) { void validate_quregFields(Qureg qureg, const char* caller) { + if (!global_isValidationEnabled) + return; + // attempt to detect the Qureg was not initialised with createQureg by the // struct fields being randomised, and ergo being dimensionally incompatible bool valid = true; @@ -1781,11 +1827,17 @@ void validate_quregFields(Qureg qureg, const char* caller) { void validate_quregIsStateVector(Qureg qureg, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(!qureg.isDensityMatrix, report::QUREG_NOT_STATE_VECTOR, caller); } void validate_quregIsDensityMatrix(Qureg qureg, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(qureg.isDensityMatrix, report::QUREG_NOT_DENSITY_MATRIX, caller); } @@ -2025,6 +2077,10 @@ void assertNewMatrixParamsAreValid(int numQubits, int useDistrib, int useGpu, in } void validate_newCompMatrParams(int numQubits, const char* caller) { + + if (!global_isValidationEnabled) + return; + validate_envIsInit(caller); // CompMatr can never be distributed nor multithreaded @@ -2040,6 +2096,10 @@ void validate_newCompMatrParams(int numQubits, const char* caller) { assertNewMatrixParamsAreValid(numQubits, useDistrib, useGpu, useMultithread, isDenseType, caller); } void validate_newDiagMatrParams(int numQubits, const char* caller) { + + if (!global_isValidationEnabled) + return; + validate_envIsInit(caller); // DiagMatr can never be distributed nor multithreaded @@ -2055,6 +2115,10 @@ void validate_newDiagMatrParams(int numQubits, const char* caller) { assertNewMatrixParamsAreValid(numQubits, useDistrib, useGpu, useMultithread, isDenseType, caller); } void validate_newFullStateDiagMatrParams(int numQubits, int useDistrib, int useGpu, int useMultithread, const char* caller) { + + if (!global_isValidationEnabled) + return; + validate_envIsInit(caller); // FullStateDiagMatr stores only the diagonals @@ -2127,6 +2191,9 @@ void assertNewMatrixAllocsSucceeded(T matr, size_t numBytes, const char* caller) void validate_newMatrixAllocs(CompMatr matr, const char* caller) { + if (!global_isValidationEnabled) + return; + bool isDenseMatrix = true; int numNodes = 1; size_t numBytes = mem_getLocalMatrixMemoryRequired(matr.numQubits, isDenseMatrix, numNodes); @@ -2134,6 +2201,9 @@ void validate_newMatrixAllocs(CompMatr matr, const char* caller) { } void validate_newMatrixAllocs(DiagMatr matr, const char* caller) { + if (!global_isValidationEnabled) + return; + bool isDenseMatrix = false; int numNodes = 1; size_t numBytes = mem_getLocalMatrixMemoryRequired(matr.numQubits, isDenseMatrix, numNodes); @@ -2141,6 +2211,9 @@ void validate_newMatrixAllocs(DiagMatr matr, const char* caller) { } void validate_newMatrixAllocs(FullStateDiagMatr matr, const char* caller) { + if (!global_isValidationEnabled) + return; + bool isDenseMatrix = false; int numNodes = (matr.isDistributed)? comm_getNumNodes() : 1; size_t numBytes = mem_getLocalMatrixMemoryRequired(matr.numQubits, isDenseMatrix, numNodes); @@ -2155,6 +2228,9 @@ void validate_newMatrixAllocs(FullStateDiagMatr matr, const char* caller) { void validate_matrixNumNewElems(int numQubits, vector> elems, const char* caller) { + if (!global_isValidationEnabled) + return; + // CompMatr accept 2D elems qindex dim = powerOf2(numQubits); tokenSubs vars = { @@ -2176,6 +2252,9 @@ void validate_matrixNumNewElems(int numQubits, vector> elems, cons } void validate_matrixNumNewElems(int numQubits, vector elems, const char* caller) { + if (!global_isValidationEnabled) + return; + // DiagMatr accept 1D elems qindex dim = powerOf2(numQubits); tokenSubs vars = { @@ -2188,11 +2267,17 @@ void validate_matrixNumNewElems(int numQubits, vector elems, const char* void validate_matrixNewElemsPtrNotNull(qcomp* elems, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(mem_isAllocated(elems), report::DIAG_MATR_NEW_ELEMS_NULL_PTR, caller); } void validate_matrixNewElemsPtrNotNull(qcomp** elems, qindex numRows, const char* caller) { + if (!global_isValidationEnabled) + return; + // messages are suitable for all dense matrices, including SuperOp assertThat(mem_isOuterAllocated(elems), report::DENSE_MATR_NEW_ELEMS_OUTER_NULL_PTR, caller); @@ -2203,6 +2288,9 @@ void validate_matrixNewElemsPtrNotNull(qcomp** elems, qindex numRows, const char void validate_fullStateDiagMatrNewElems(FullStateDiagMatr matr, qindex startInd, qindex numElems, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat( startInd >= 0 && startInd < matr.numElems, report::FULL_STATE_DIAG_MATR_NEW_ELEMS_INVALID_START_INDEX, @@ -2234,6 +2322,9 @@ void validate_fullStateDiagMatrNewElems(FullStateDiagMatr matr, qindex startInd, void validate_matrixNumQubitsMatchesParam(int numMatrQubits, int numSetterQubits, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = { {"${NUM_SETTER_QUBITS}", numSetterQubits}, {"${NUM_MATRIX_QUBITS}", numMatrQubits}}; @@ -2243,6 +2334,9 @@ void validate_matrixNumQubitsMatchesParam(int numMatrQubits, int numSetterQubits void validate_declaredNumElemsMatchesVectorLength(qindex numElems, qindex vecLength, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = { {"${NUM_ELEMS}", numElems}, {"${VEC_LENGTH}", vecLength}}; @@ -2252,6 +2346,9 @@ void validate_declaredNumElemsMatchesVectorLength(qindex numElems, qindex vecLen void validate_multiVarFuncQubits(int numMatrQubits, int* numQubitsPerVar, int numVars, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(numVars > 0, report::MULTI_VAR_FUNC_INVALID_NUM_VARS, {{"${NUM_VARS}", numVars}}, caller); for (int v=0; v> matrix, const char* caller) { + if (!global_isValidationEnabled) + return; + if (matrix.empty()) return; @@ -2396,13 +2499,55 @@ void assertMatrixFieldsAreValid(T matr, int expectedNumQb, string badFieldMsg, c // no risk that they're wrong (because they're const so users cannot modify them) unless // the struct was unitialised, which we have already validated against } -void validate_matrixFields(CompMatr1 m, const char* caller) { assertMatrixFieldsAreValid(m, 1, report::INVALID_COMP_MATR_1_FIELDS, caller); } -void validate_matrixFields(CompMatr2 m, const char* caller) { assertMatrixFieldsAreValid(m, 2, report::INVALID_COMP_MATR_2_FIELDS, caller); } -void validate_matrixFields(CompMatr m, const char* caller) { assertMatrixFieldsAreValid(m, m.numQubits, report::INVALID_COMP_MATR_FIELDS, caller); } -void validate_matrixFields(DiagMatr1 m, const char* caller) { assertMatrixFieldsAreValid(m, 1, report::INVALID_DIAG_MATR_1_FIELDS, caller); } -void validate_matrixFields(DiagMatr2 m, const char* caller) { assertMatrixFieldsAreValid(m, 2, report::INVALID_DIAG_MATR_2_FIELDS, caller); } -void validate_matrixFields(DiagMatr m, const char* caller) { assertMatrixFieldsAreValid(m, m.numQubits, report::INVALID_DIAG_MATR_FIELDS, caller); } -void validate_matrixFields(FullStateDiagMatr m, const char* caller) { assertMatrixFieldsAreValid(m, m.numQubits, report::INVALID_FULL_STATE_DIAG_MATR_FIELDS, caller); } +void validate_matrixFields(CompMatr1 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixFieldsAreValid(m, 1, report::INVALID_COMP_MATR_1_FIELDS, caller); +} +void validate_matrixFields(CompMatr2 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixFieldsAreValid(m, 2, report::INVALID_COMP_MATR_2_FIELDS, caller); +} +void validate_matrixFields(CompMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixFieldsAreValid(m, m.numQubits, report::INVALID_COMP_MATR_FIELDS, caller); +} +void validate_matrixFields(DiagMatr1 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixFieldsAreValid(m, 1, report::INVALID_DIAG_MATR_1_FIELDS, caller); +} +void validate_matrixFields(DiagMatr2 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixFieldsAreValid(m, 2, report::INVALID_DIAG_MATR_2_FIELDS, caller); +} +void validate_matrixFields(DiagMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixFieldsAreValid(m, m.numQubits, report::INVALID_DIAG_MATR_FIELDS, caller); +} +void validate_matrixFields(FullStateDiagMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixFieldsAreValid(m, m.numQubits, report::INVALID_FULL_STATE_DIAG_MATR_FIELDS, caller); +} // type T can be CompMatr, DiagMatr or FullStateDiagMatr template @@ -2417,9 +2562,27 @@ void assertMatrixIsSynced(T matr, string errMsg, const char* caller) { // NOT GPU-accelerated and ergo the GPU memory is not consulted. It's best to build the habit in the user! assertThat(*(matr.wasGpuSynced) == 1, errMsg, caller); } -void validate_matrixIsSynced(CompMatr matr, const char* caller) { assertMatrixIsSynced(matr, report::COMP_MATR_NOT_SYNCED_TO_GPU, caller);} -void validate_matrixIsSynced(DiagMatr matr, const char* caller) { assertMatrixIsSynced(matr, report::DIAG_MATR_NOT_SYNCED_TO_GPU, caller); } -void validate_matrixIsSynced(FullStateDiagMatr matr, const char* caller) { assertMatrixIsSynced(matr, report::FULL_STATE_DIAG_MATR_NOT_SYNCED_TO_GPU, caller); } +void validate_matrixIsSynced(CompMatr matr, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsSynced(matr, report::COMP_MATR_NOT_SYNCED_TO_GPU, caller); +} +void validate_matrixIsSynced(DiagMatr matr, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsSynced(matr, report::DIAG_MATR_NOT_SYNCED_TO_GPU, caller); +} +void validate_matrixIsSynced(FullStateDiagMatr matr, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsSynced(matr, report::FULL_STATE_DIAG_MATR_NOT_SYNCED_TO_GPU, caller); +} // type T can be CompMatr1, CompMatr2, CompMatr, DiagMatr1, DiagMatr2, DiagMatr, FullStateDiagMatr template @@ -2439,13 +2602,55 @@ void assertMatrixIsUnitary(T matr, const char* caller) { // may overwrite matr.isApproxUnitary of heap matrices, otherwise ignores epsilon assertThat(util_isUnitary(matr, global_validationEpsilon), report::MATRIX_NOT_UNITARY, caller); } -void validate_matrixIsUnitary(CompMatr1 m, const char* caller) { assertMatrixIsUnitary(m, caller); } -void validate_matrixIsUnitary(CompMatr2 m, const char* caller) { assertMatrixIsUnitary(m, caller); } -void validate_matrixIsUnitary(CompMatr m, const char* caller) { assertMatrixIsUnitary(m, caller); } -void validate_matrixIsUnitary(DiagMatr1 m, const char* caller) { assertMatrixIsUnitary(m, caller); } -void validate_matrixIsUnitary(DiagMatr2 m, const char* caller) { assertMatrixIsUnitary(m, caller); } -void validate_matrixIsUnitary(DiagMatr m, const char* caller) { assertMatrixIsUnitary(m, caller); } -void validate_matrixIsUnitary(FullStateDiagMatr m, const char* caller) { assertMatrixIsUnitary(m, caller); } +void validate_matrixIsUnitary(CompMatr1 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsUnitary(m, caller); +} +void validate_matrixIsUnitary(CompMatr2 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsUnitary(m, caller); +} +void validate_matrixIsUnitary(CompMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsUnitary(m, caller); +} +void validate_matrixIsUnitary(DiagMatr1 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsUnitary(m, caller); +} +void validate_matrixIsUnitary(DiagMatr2 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsUnitary(m, caller); +} +void validate_matrixIsUnitary(DiagMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsUnitary(m, caller); +} +void validate_matrixIsUnitary(FullStateDiagMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsUnitary(m, caller); +} void validate_unitaryExponentIsReal(qcomp exponent, const char* caller) { @@ -2479,13 +2684,55 @@ void assertMatrixIsHermitian(T matr, const char* caller) { // may overwrite matr.isApproxHermitian of heap matrices, otherwise ignores epsilon assertThat(util_isHermitian(matr, global_validationEpsilon), report::MATRIX_NOT_HERMITIAN, caller); } -void validate_matrixIsHermitian(CompMatr1 m, const char* caller) { assertMatrixIsHermitian(m, caller); } -void validate_matrixIsHermitian(CompMatr2 m, const char* caller) { assertMatrixIsHermitian(m, caller); } -void validate_matrixIsHermitian(CompMatr m, const char* caller) { assertMatrixIsHermitian(m, caller); } -void validate_matrixIsHermitian(DiagMatr1 m, const char* caller) { assertMatrixIsHermitian(m, caller); } -void validate_matrixIsHermitian(DiagMatr2 m, const char* caller) { assertMatrixIsHermitian(m, caller); } -void validate_matrixIsHermitian(DiagMatr m, const char* caller) { assertMatrixIsHermitian(m, caller); } -void validate_matrixIsHermitian(FullStateDiagMatr m, const char* caller) { assertMatrixIsHermitian(m, caller); } +void validate_matrixIsHermitian(CompMatr1 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsHermitian(m, caller); +} +void validate_matrixIsHermitian(CompMatr2 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsHermitian(m, caller); +} +void validate_matrixIsHermitian(CompMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsHermitian(m, caller); +} +void validate_matrixIsHermitian(DiagMatr1 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsHermitian(m, caller); +} +void validate_matrixIsHermitian(DiagMatr2 m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsHermitian(m, caller); +} +void validate_matrixIsHermitian(DiagMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsHermitian(m, caller); +} +void validate_matrixIsHermitian(FullStateDiagMatr m, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixIsHermitian(m, caller); +} // type T can be DiagMatr, FullStateDiagMatr template @@ -2512,8 +2759,20 @@ void assertMatrExpIsNonDiverging(T matr, qcomp exponent, const char* caller) { if (std::real(exponent) < 0) assertThat(util_isApproxNonZero(matr, global_validationEpsilon), report::DIAG_MATR_APPROX_ZERO_WHILE_EXPONENT_REAL_AND_NEGATIVE, caller); } -void validate_matrixExpIsNonDiverging(DiagMatr m, qcomp p, const char* caller) { assertMatrExpIsNonDiverging(m, p, caller); } -void validate_matrixExpIsNonDiverging(FullStateDiagMatr m, qcomp p, const char* caller) { assertMatrExpIsNonDiverging(m, p, caller); } +void validate_matrixExpIsNonDiverging(DiagMatr m, qcomp p, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrExpIsNonDiverging(m, p, caller); +} +void validate_matrixExpIsNonDiverging(FullStateDiagMatr m, qcomp p, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrExpIsNonDiverging(m, p, caller); +} // type T can be DiagMatr, FullStateDiagMatr template @@ -2551,8 +2810,20 @@ void assertMatrExpIsHermitian(T matr, qreal exponent, const char* caller) { // result tends to 1 so does not vanish or blow up unexpectedly. All fine! } -void validate_matrixExpIsHermitian(DiagMatr m, qreal p, const char* caller) { assertMatrExpIsHermitian(m, p, caller); } -void validate_matrixExpIsHermitian(FullStateDiagMatr m, qreal p, const char* caller) { assertMatrExpIsHermitian(m, p, caller); } +void validate_matrixExpIsHermitian(DiagMatr m, qreal p, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrExpIsHermitian(m, p, caller); +} +void validate_matrixExpIsHermitian(FullStateDiagMatr m, qreal p, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrExpIsHermitian(m, p, caller); +} template void assertMatrixDimMatchesTargs(T matr, int numTargs, const char* caller) { @@ -2573,15 +2844,54 @@ void assertMatrixDimMatchesTargs(T matr, int numTargs, const char* caller) { assertThat(numMatrQubits == numTargs, report::MATRIX_SIZE_MISMATCHES_NUM_TARGETS, vars, caller); } -void validate_matrixDimMatchesTargets(CompMatr1 matr, int numTargs, const char* caller) { assertMatrixDimMatchesTargs(matr, numTargs, caller); } -void validate_matrixDimMatchesTargets(CompMatr2 matr, int numTargs, const char* caller) { assertMatrixDimMatchesTargs(matr, numTargs, caller); } -void validate_matrixDimMatchesTargets(CompMatr matr, int numTargs, const char* caller) { assertMatrixDimMatchesTargs(matr, numTargs, caller); } -void validate_matrixDimMatchesTargets(DiagMatr1 matr, int numTargs, const char* caller) { assertMatrixDimMatchesTargs(matr, numTargs, caller); } -void validate_matrixDimMatchesTargets(DiagMatr2 matr, int numTargs, const char* caller) { assertMatrixDimMatchesTargs(matr, numTargs, caller); } -void validate_matrixDimMatchesTargets(DiagMatr matr, int numTargs, const char* caller) { assertMatrixDimMatchesTargs(matr, numTargs, caller); } +void validate_matrixDimMatchesTargets(CompMatr1 matr, int numTargs, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixDimMatchesTargs(matr, numTargs, caller); +} +void validate_matrixDimMatchesTargets(CompMatr2 matr, int numTargs, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixDimMatchesTargs(matr, numTargs, caller); +} +void validate_matrixDimMatchesTargets(CompMatr matr, int numTargs, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixDimMatchesTargs(matr, numTargs, caller); +} +void validate_matrixDimMatchesTargets(DiagMatr1 matr, int numTargs, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixDimMatchesTargs(matr, numTargs, caller); +} +void validate_matrixDimMatchesTargets(DiagMatr2 matr, int numTargs, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixDimMatchesTargs(matr, numTargs, caller); +} +void validate_matrixDimMatchesTargets(DiagMatr matr, int numTargs, const char* caller) { + + if (!global_isValidationEnabled) + return; + + assertMatrixDimMatchesTargs(matr, numTargs, caller); +} void validate_matrixAndQuregAreCompatible(FullStateDiagMatr matr, Qureg qureg, bool expecOnly, const char* caller) { + if (!global_isValidationEnabled) + return; + // we do not need to define this function for the other matrix types, // since their validation will happen through validation of the // user-given list of target qubits. But we do need to define it for @@ -2707,8 +3017,6 @@ void assertSuperOpFitsInGpuMem(int numQubits, int isEnvGpuAccel, bool isInKrausM void validate_newSuperOpParams(int numQubits, const char* caller) { - // some of the below validation involves getting distributed node consensus, which - // can be an expensive synchronisation, which we avoid if validation is anyway disabled if (!global_isValidationEnabled) return; @@ -2761,13 +3069,15 @@ void assertNewSuperOpAllocs(SuperOp op, bool isInKrausMap, const char* caller) { void validate_newSuperOpAllocs(SuperOp op, const char* caller) { + if (!global_isValidationEnabled) + return; + bool isInKrausMap = false; assertNewSuperOpAllocs(op, isInKrausMap, caller); } void validate_newInlineSuperOpDimMatchesVectors(int numDeclaredQubits, vector> matrix, const char* caller) { - // avoid potentially expensive matrix enumeration if validation is anyway disabled if (!global_isValidationEnabled) return; @@ -2796,7 +3106,6 @@ void validate_newInlineSuperOpDimMatchesVectors(int numDeclaredQubits, vector> matrix, const char* caller) { - // avoid potentially expensive matrix enumeration if validation is anyway disabled if (!global_isValidationEnabled) return; @@ -2818,6 +3127,9 @@ void validate_superOpNewMatrixDims(SuperOp op, vector> matrix, con void validate_superOpFieldsMatchPassedParams(SuperOp op, int numQb, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = { {"${NUM_PASSED_QUBITS}", numQb}, {"${NUM_OP_QUBITS}", op.numQubits}}; @@ -2864,12 +3176,18 @@ void assertSuperOpFieldsAreValid(SuperOp op, bool isInKrausMap, const char* call void validate_superOpFields(SuperOp op, const char* caller) { + if (!global_isValidationEnabled) + return; + bool isInKrausMap = false; assertSuperOpFieldsAreValid(op, isInKrausMap, caller); } 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; @@ -2880,6 +3198,9 @@ void validate_superOpIsSynced(SuperOp op, const char* caller) { void validate_superOpDimMatchesTargs(SuperOp op, int numTargets, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = {{"${OP_QUBITS}", op.numQubits}, {"{NUM_TARGS}", numTargets}}; assertThat(op.numQubits == numTargets, report::SUPER_OP_SIZE_MISMATCHES_NUM_TARGETS, vars, caller); } @@ -2915,8 +3236,6 @@ void assertKrausMapValidNumMatrices(int numQubits, int numMatrices, const char* void validate_newKrausMapParams(int numQubits, int numMatrices, const char* caller) { - // some of the below validation involves getting distributed node consensus, which - // can be an expensive synchronisation, which we avoid if validation is anyway disabled if (!global_isValidationEnabled) return; @@ -2944,6 +3263,9 @@ void validate_newKrausMapParams(int numQubits, int numMatrices, const char* call void validate_newKrausMapAllocs(KrausMap map, const char* caller) { + if (!global_isValidationEnabled) + return; + // unlike other post-creation allocation validation, this function // expects that when allocation failed and the heap fields have already // been cleared, that any nested field (like map.matrices) has had the @@ -2954,11 +3276,6 @@ void validate_newKrausMapAllocs(KrausMap map, const char* caller) { // (and is nullptr), so we must check it last so as not to false report // it as the cause of the failure! - // we expensively get node consensus about malloc failure, in case of heterogeneous hardware/loads, - // but we avoid this if validation is anyway disabled - if (!global_isValidationEnabled) - return; - // prior validation gaurantees this will not overflow qindex matrListMem = map.numMatrices * mem_getLocalMatrixMemoryRequired(map.numQubits, true, 1); tokenSubs vars = { @@ -2981,7 +3298,6 @@ void validate_newKrausMapAllocs(KrausMap map, const char* caller) { void validate_newInlineKrausMapDimMatchesVectors(int numQubits, int numOperators, vector>> matrices, const char* caller) { - // avoid potentially expensive matrix enumeration if validation is anyway disabled if (!global_isValidationEnabled) return; @@ -3012,10 +3328,9 @@ void validate_newInlineKrausMapDimMatchesVectors(int numQubits, int numOperators void validate_krausMapNewMatrixDims(KrausMap map, vector>> matrices, const char* caller) { - // avoid potentially expensive matrix enumeration if validation is anyway disabled if (!global_isValidationEnabled) return; - + assertThat(map.numMatrices == (int) matrices.size(), report::KRAUS_MAP_INCOMPATIBLE_NUM_NEW_MATRICES, {{"${NUM_GIVEN}", matrices.size()}, {"${NUM_EXPECTED}", map.numMatrices}}, caller); @@ -3035,6 +3350,9 @@ void validate_krausMapNewMatrixDims(KrausMap map, vector>> void validate_krausMapFieldsMatchPassedParams(KrausMap map, int numQb, int numOps, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = { {"${NUM_MAP_QUBITS}", map.numQubits}, {"${NUM_MAP_OPS}", map.numMatrices}, @@ -3053,6 +3371,9 @@ void validate_krausMapFieldsMatchPassedParams(KrausMap map, int numQb, int numOp void validate_krausMapFields(KrausMap map, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = { {"${NUM_QUBITS}", map.numQubits}, {"${NUM_MATRICES}", map.numMatrices}, @@ -3087,6 +3408,9 @@ void validate_krausMapFields(KrausMap map, const char* caller) { 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; @@ -3096,6 +3420,10 @@ void validate_krausMapIsSynced(KrausMap map, const char* caller) { } void validate_krausMapIsCPTP(KrausMap map, const char* caller) { + + if (!global_isValidationEnabled) + return; + validate_krausMapFields(map, caller); validate_krausMapIsSynced(map, caller); @@ -3109,6 +3437,9 @@ void validate_krausMapIsCPTP(KrausMap map, const char* caller) { void validate_krausMapMatchesTargets(KrausMap map, int numTargets, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = {{"${KRAUS_QUBITS}", map.numQubits}, {"${TARG_QUBITS}", numTargets}}; assertThat(map.numQubits == numTargets, report::KRAUS_MAP_SIZE_MISMATCHES_TARGETS, vars, caller); } @@ -3179,6 +3510,9 @@ void assertValidNewPauliIndices(int* indices, int numInds, int maxIndExcl, const void validate_newPauliStrNumPaulis(int numPaulis, int maxNumPaulis, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = {{"${NUM_PAULIS}", numPaulis}}; assertThat(numPaulis > 0, report::NEW_PAULI_STR_NON_POSITIVE_NUM_PAULIS, vars, caller); @@ -3188,6 +3522,9 @@ void validate_newPauliStrNumPaulis(int numPaulis, int maxNumPaulis, const char* void validate_newPauliStrParams(const char* paulis, int* indices, int numPaulis, int maxNumPaulis, const char* caller) { + if (!global_isValidationEnabled) + return; + validate_newPauliStrNumPaulis(numPaulis, maxNumPaulis, caller); assertCorrectNumPauliCharsBeforeTerminationChar(paulis, numPaulis, caller); assertRecognisedNewPaulis(paulis, numPaulis, caller); @@ -3195,6 +3532,9 @@ void validate_newPauliStrParams(const char* paulis, int* indices, int numPaulis, } void validate_newPauliStrParams(int* paulis, int* indices, int numPaulis, int maxNumPaulis, const char* caller) { + if (!global_isValidationEnabled) + return; + validate_newPauliStrNumPaulis(numPaulis, maxNumPaulis, caller); assertValidNewPauliCodes(paulis, numPaulis, caller); assertValidNewPauliIndices(indices, numPaulis, maxNumPaulis, caller); @@ -3202,6 +3542,9 @@ void validate_newPauliStrParams(int* paulis, int* indices, int numPaulis, int ma void validate_newPauliStrNumChars(int numPaulis, int numIndices, const char* caller) { + if (!global_isValidationEnabled) + return; + // this is a C++-only validation, because only std::string gaurantees we can know // the passed string length (C char arrays might not contain termination char) tokenSubs vars = {{"${NUM_PAULIS}", numPaulis}, {"${NUM_INDS}", numIndices}}; @@ -3216,6 +3559,9 @@ void validate_newPauliStrNumChars(int numPaulis, int numIndices, const char* cal void validate_pauliStrTargets(Qureg qureg, PauliStr str, const char* caller) { + if (!global_isValidationEnabled) + return; + // avoid producing a list of targets which requires enumerating all bits int maxTarg = paulis_getIndOfLefmostNonIdentityPauli(str); @@ -3225,6 +3571,9 @@ void validate_pauliStrTargets(Qureg qureg, PauliStr str, const char* caller) { void validate_controlsAndPauliStrTargets(Qureg qureg, int* ctrls, int numCtrls, PauliStr str, const char* caller) { + if (!global_isValidationEnabled) + return; + // validate targets and controls in isolation validate_pauliStrTargets(qureg, str, caller); validate_controls(qureg, ctrls, numCtrls, caller); @@ -3237,6 +3586,9 @@ void validate_controlsAndPauliStrTargets(Qureg qureg, int* ctrls, int numCtrls, void validate_controlAndPauliStrTargets(Qureg qureg, int ctrl, PauliStr str, const char* caller) { + if (!global_isValidationEnabled) + return; + validate_controlsAndPauliStrTargets(qureg, &ctrl, 1, str, caller); } @@ -3248,6 +3600,9 @@ void validate_controlAndPauliStrTargets(Qureg qureg, int ctrl, PauliStr str, con void validate_newPauliStrSumParams(qindex numTerms, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(numTerms > 0, report::NEW_PAULI_STR_SUM_NON_POSITIVE_NUM_STRINGS, {{"${NUM_TERMS}", numTerms}}, caller); // assert that the total memory required does not overflow @@ -3278,12 +3633,18 @@ void validate_newPauliStrSumParams(qindex numTerms, const char* caller) { void validate_newPauliStrSumMatchingListLens(qindex numStrs, qindex numCoeffs, const char* caller) { + if (!global_isValidationEnabled) + return; + tokenSubs vars = {{"${NUM_STRS}", numStrs}, {"${NUM_COEFFS}", numCoeffs}}; assertThat(numStrs == numCoeffs, report::NEW_PAULI_STR_SUM_DIFFERENT_NUM_STRINGS_AND_COEFFS, vars, caller); } void validate_newPauliStrSumAllocs(PauliStrSum sum, qindex numBytesStrings, qindex numBytesCoeffs, const char* caller) { + if (!global_isValidationEnabled) + return; + // this validation is called AFTER the caller has checked for failed // allocs and (in that scenario) freed every pointer, but does not // overwrite any pointers to nullptr, so the failed alloc is known. @@ -3311,6 +3672,9 @@ void validate_newPauliStrSumAllocs(PauliStrSum sum, qindex numBytesStrings, qind void validate_parsedPauliStrSumLineIsInterpretable(bool isInterpretable, string line, qindex lineIndex, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo we cannot yet report 'line' because tokenSubs so far only accepts integers :( tokenSubs vars = {{"${LINE_NUMBER}", lineIndex + 1}}; // line numbers begin at 1 @@ -3319,6 +3683,9 @@ void validate_parsedPauliStrSumLineIsInterpretable(bool isInterpretable, string void validate_parsedPauliStrSumLineHasConsistentNumPaulis(int numPaulis, int numLinePaulis, string line, qindex lineIndex, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo we cannot yet report 'line' because tokenSubs so far only accepts integers :( tokenSubs vars = { @@ -3330,6 +3697,9 @@ void validate_parsedPauliStrSumLineHasConsistentNumPaulis(int numPaulis, int num void validate_parsedPauliStrSumCoeffWithinQcompRange(bool isCoeffValid, string line, qindex lineIndex, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo we cannot yet report 'line' because tokenSubs so far only accepts integers :( tokenSubs vars = {{"${LINE_NUMBER}", lineIndex + 1}}; // lines begin at 1 @@ -3338,6 +3708,9 @@ void validate_parsedPauliStrSumCoeffWithinQcompRange(bool isCoeffValid, string l void validate_parsedStringIsNotEmpty(bool stringIsNotEmpty, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(stringIsNotEmpty, report::PARSED_STRING_IS_EMPTY, caller); } @@ -3351,6 +3724,9 @@ bool areQubitsDisjoint(qindex qubitsMaskA, int* qubitsB, int numQubitsB); void validate_pauliStrSumFields(PauliStrSum sum, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(sum.numTerms > 0, report::INVALID_PAULI_STR_SUM_FIELDS, {{"${NUM_TERMS}", sum.numTerms}}, caller); assertThat(mem_isAllocated(sum.coeffs), report::INVALID_PAULI_STR_HEAP_PTR, caller); @@ -3378,6 +3754,9 @@ void validate_pauliStrSumIsHermitian(PauliStrSum sum, const char* caller) { void validate_pauliStrSumTargets(PauliStrSum sum, Qureg qureg, const char* caller) { + if (!global_isValidationEnabled) + return; + int maxInd = paulis_getIndOfLefmostNonIdentityPauli(sum); int minNumQb = maxInd + 1; @@ -3391,6 +3770,9 @@ void validate_pauliStrSumTargets(PauliStrSum sum, Qureg qureg, const char* calle void validate_controlsAndPauliStrSumTargets(Qureg qureg, int* ctrls, int numCtrls, PauliStrSum sum, const char* caller) { + if (!global_isValidationEnabled) + return; + // validate targets and controls in isolation validate_pauliStrSumTargets(sum, qureg, caller); validate_controls(qureg, ctrls, numCtrls, caller); @@ -3402,11 +3784,17 @@ void validate_controlsAndPauliStrSumTargets(Qureg qureg, int* ctrls, int numCtrl void validate_controlAndPauliStrSumTargets(Qureg qureg, int ctrl, PauliStrSum sum, const char* caller) { + if (!global_isValidationEnabled) + return; + validate_controlsAndPauliStrSumTargets(qureg, &ctrl, 1, sum, caller); } void validate_pauliStrSumCanInitMatrix(FullStateDiagMatr matr, PauliStrSum sum, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(!paulis_containsXOrY(sum), report::PAULI_STR_SUM_NOT_ALL_I_Z, caller); int maxInd = paulis_getIndOfLefmostNonIdentityPauli(sum); @@ -3428,6 +3816,9 @@ void validate_pauliStrSumCanInitMatrix(FullStateDiagMatr matr, PauliStrSum sum, void validate_basisStateIndex(Qureg qureg, qindex ind, const char* caller) { + if (!global_isValidationEnabled) + return; + qindex maxIndExcl = powerOf2(qureg.numQubits); tokenSubs vars = { @@ -3440,6 +3831,9 @@ void validate_basisStateIndex(Qureg qureg, qindex ind, const char* caller) { void validate_basisStateRowCol(Qureg qureg, qindex row, qindex col, const char* caller) { + if (!global_isValidationEnabled) + return; + qindex maxIndExcl = powerOf2(qureg.numQubits); tokenSubs vars = { @@ -3454,6 +3848,9 @@ void validate_basisStateRowCol(Qureg qureg, qindex row, qindex col, const char* void validate_basisStateIndices(Qureg qureg, qindex startInd, qindex numInds, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat( startInd >= 0 && startInd < qureg.numAmps, report::INVALID_STARTING_BASIS_STATE_INDEX, @@ -3482,6 +3879,9 @@ void validate_basisStateIndices(Qureg qureg, qindex startInd, qindex numInds, co void validate_basisStateRowCols(Qureg qureg, qindex startRow, qindex startCol, qindex numRows, qindex numCols, const char* caller) { + if (!global_isValidationEnabled) + return; + qindex maxRowOrColExcl = powerOf2(qureg.numQubits); assertThat( @@ -3519,6 +3919,9 @@ void validate_basisStateRowCols(Qureg qureg, qindex startRow, qindex startCol, q void validate_localAmpIndices(Qureg qureg, qindex localStartInd, qindex numInds, const char* caller) { + if (!global_isValidationEnabled) + return; + // note that localStartInd and numInds can validly DIFFER between nodes, // so we use assertAllNodesAgreeThat() in lieu of assertThat() @@ -3616,11 +4019,17 @@ void assertValidQubits( void validate_target(Qureg qureg, int target, const char* caller) { + if (!global_isValidationEnabled) + return; + assertValidQubit(qureg, target, report::INVALID_TARGET_QUBIT, caller); } void validate_targets(Qureg qureg, int* targets, int numTargets, const char* caller) { + if (!global_isValidationEnabled) + return; + // must always have at least 1 target bool numCanBeZero = false; @@ -3631,12 +4040,18 @@ void validate_targets(Qureg qureg, int* targets, int numTargets, const char* cal } void validate_twoTargets(Qureg qureg, int target1, int target2, const char* caller) { + if (!global_isValidationEnabled) + return; + int targs[] = {target1, target2}; validate_targets(qureg, targs, 2, caller); } void validate_controls(Qureg qureg, int* ctrls, int numCtrls, const char* caller) { + if (!global_isValidationEnabled) + return; + // it is fine to have zero controls bool numCanBeZero = true; @@ -3648,6 +4063,9 @@ void validate_controls(Qureg qureg, int* ctrls, int numCtrls, const char* caller void validate_controlsAndTargets(Qureg qureg, int* ctrls, int numCtrls, int* targs, int numTargs, const char* caller) { + if (!global_isValidationEnabled) + return; + // validate controls and targets in isolation validate_targets(qureg, targs, numTargs, caller); validate_controls(qureg, ctrls, numCtrls, caller); @@ -3657,29 +4075,47 @@ void validate_controlsAndTargets(Qureg qureg, int* ctrls, int numCtrls, int* tar } void validate_controlAndTarget(Qureg qureg, int ctrl, int targ, const char* caller) { + if (!global_isValidationEnabled) + return; + validate_controlsAndTargets(qureg, &ctrl, 1, &targ, 1, caller); } void validate_controlAndTargets(Qureg qureg, int ctrl, int* targs, int numTargs, const char* caller) { + if (!global_isValidationEnabled) + return; + validate_controlsAndTargets(qureg, &ctrl, 1, targs, numTargs, caller); } void validate_controlsAndTarget(Qureg qureg, int* ctrls, int numCtrls, int targ, const char* caller) { + if (!global_isValidationEnabled) + return; + validate_controlsAndTargets(qureg, ctrls, numCtrls, &targ, 1, caller); } void validate_controlAndTwoTargets(Qureg qureg, int ctrl, int targ1, int targ2, const char* caller) { + if (!global_isValidationEnabled) + return; + int targs[] = {targ1, targ2}; validate_controlsAndTargets(qureg, &ctrl, 1, targs, 2, caller); } void validate_controlsAndTwoTargets(Qureg qureg, int* ctrls, int numCtrls, int targ1, int targ2, const char* caller) { + if (!global_isValidationEnabled) + return; + int targs[] = {targ1, targ2}; validate_controlsAndTargets(qureg, ctrls, numCtrls, targs, 2, caller); } void validate_controlStates(int* states, int numCtrls, const char* caller) { + if (!global_isValidationEnabled) + return; + // states is permittedly unallocated (nullptr) even when numCtrls != 0 if (!mem_isAllocated(states)) return; @@ -3690,6 +4126,9 @@ void validate_controlStates(int* states, int numCtrls, const char* caller) { void validate_controlsMatchStates(int numCtrls, int numStates, const char* caller) { + if (!global_isValidationEnabled) + return; + // only invocable by the C++ interface tokenSubs vars = { {"${NUM_CTRLS}", numCtrls}, @@ -3706,11 +4145,17 @@ void validate_controlsMatchStates(int numCtrls, int numStates, const char* calle void validate_measurementOutcomeIsValid(int outcome, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(outcome == 0 || outcome == 1, report::ONE_QUBIT_MEASUREMENT_OUTCOME_INVALID, {{"${OUTCOME}", outcome}}, caller); } void validate_measurementOutcomesAreValid(int* outcomes, int numOutcomes, const char* caller) { + if (!global_isValidationEnabled) + return; + // no need to validate numOutcomes; it is already validated by caller (e.g. through numTargets) for (int i=0; i probs, const char* cal void validate_measurementOutcomesMatchTargets(int numQubits, int numOutcomes, const char* caller) { + if (!global_isValidationEnabled) + return; + // invoked only by the C++ user interface tokenSubs vars = { {"${NUM_QUBITS}", numQubits}, @@ -3800,6 +4251,9 @@ void validate_rotationAxisNotZeroVector(qreal x, qreal y, qreal z, const char* c void validate_mixedAmpsFitInNode(Qureg qureg, int numTargets, const char* caller) { + if (!global_isValidationEnabled) + return; + // only relevant to distributed quregs if (!qureg.isDistributed) return; @@ -3831,6 +4285,9 @@ void validate_mixedAmpsFitInNode(Qureg qureg, int numTargets, const char* caller void validate_trotterParams(Qureg qureg, int order, int reps, const char* caller) { + if (!global_isValidationEnabled) + return; + bool isEven = (order % 2) == 0; assertThat(order > 0 && (isEven || order==1), report::INVALID_TROTTER_ORDER, {{"${ORDER}", order}}, caller); assertThat(reps > 0, report::INVALID_TROTTER_REPS, {{"${REPS}", reps}}, caller); @@ -3844,6 +4301,9 @@ void validate_trotterParams(Qureg qureg, int order, int reps, const char* caller void validate_lindbladJumpOps(PauliStrSum* jumps, int numJumps, Qureg qureg, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(numJumps >= 0, report::NEGATIVE_NUM_LINDBLAD_JUMP_OPS, caller); // @todo @@ -3860,6 +4320,9 @@ void validate_lindbladJumpOps(PauliStrSum* jumps, int numJumps, Qureg qureg, con void validate_lindbladDampingRates(qreal* damps, int numJumps, const char* caller) { + if (!global_isValidationEnabled) + return; + // possibly repeated from jump op validation, for safety assertThat(numJumps >= 0, report::NEGATIVE_NUM_LINDBLAD_JUMP_OPS, caller); @@ -3874,6 +4337,9 @@ void validate_lindbladDampingRates(qreal* damps, int numJumps, const char* calle void validate_numLindbladSuperPropagatorTerms(qindex numSuperTerms, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(numSuperTerms != 0, report::NUM_LINDBLAD_SUPER_PROPAGATOR_TERMS_OVERFLOWED, caller); // attempt to fetch RAM, and simply return if we fail; if we unknowingly @@ -3888,7 +4354,6 @@ void validate_numLindbladSuperPropagatorTerms(qindex numSuperTerms, const char* // check whether the superpropagator fits in memory bool fits = mem_canPauliStrSumFitInMemory(numSuperTerms, memPerNode); assertThat(fits, report::NEW_LINDBLAD_SUPER_PROPAGATOR_CANNOT_FIT_INTO_CPU_MEM, {{"${NUM_TERMS}", numSuperTerms}, {"${NUM_BYTES}", memPerNode}}, caller); - } @@ -3899,6 +4364,9 @@ void validate_numLindbladSuperPropagatorTerms(qindex numSuperTerms, const char* void validate_probability(qreal prob, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo report 'prob' once validation reporting can handle floats /// @todo @@ -3910,6 +4378,9 @@ void validate_probability(qreal prob, const char* caller) { void validate_probabilities(qreal* probs, int numProbs, const char* caller) { + if (!global_isValidationEnabled) + return; + // we assume that numProbs>0 was prior validated /// @todo like above, should we permit -eps <= prob <= 1+eps? @@ -3931,6 +4402,9 @@ void validate_probabilities(qreal* probs, int numProbs, const char* caller) { void validate_oneQubitDepashingProb(qreal prob, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo report 'prob' once validation reporting can handle floats validate_probability(prob, caller); @@ -3941,6 +4415,9 @@ void validate_oneQubitDepashingProb(qreal prob, const char* caller) { void validate_twoQubitDepashingProb(qreal prob, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo report 'prob' once validation reporting can handle floats validate_probability(prob, caller); @@ -3951,6 +4428,9 @@ void validate_twoQubitDepashingProb(qreal prob, const char* caller) { void validate_oneQubitDepolarisingProb(qreal prob, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo report 'prob' once validation reporting can handle floats validate_probability(prob, caller); @@ -3961,6 +4441,9 @@ void validate_oneQubitDepolarisingProb(qreal prob, const char* caller) { void validate_twoQubitDepolarisingProb(qreal prob, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo report 'prob' once validation reporting can handle floats validate_probability(prob, caller); @@ -3971,6 +4454,9 @@ void validate_twoQubitDepolarisingProb(qreal prob, const char* caller) { void validate_oneQubitDampingProb(qreal prob, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo report 'prob' once validation reporting can handle floats // permit one-qubit amplitude damping of any valid probability, @@ -3980,6 +4466,9 @@ void validate_oneQubitDampingProb(qreal prob, const char* caller) { void validate_oneQubitPauliChannelProbs(qreal pX, qreal pY, qreal pZ, const char* caller) { + if (!global_isValidationEnabled) + return; + validate_probability(pX, caller); validate_probability(pY, caller); validate_probability(pZ, caller); @@ -4005,6 +4494,9 @@ void validate_oneQubitPauliChannelProbs(qreal pX, qreal pY, qreal pZ, const char void validate_quregCanBeWorkspace(Qureg qureg, Qureg workspace, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat( doQuregsHaveIdenticalMemoryLayouts(qureg, workspace), report::QUREG_IS_INCOMPATIBLE_WITH_WORKSPACE, caller); @@ -4015,11 +4507,17 @@ void validate_quregCanBeWorkspace(Qureg qureg, Qureg workspace, const char* call void validate_numQuregsInSum(int numQuregs, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(numQuregs > 0, report::NON_POSITIVE_NUM_QUREGS_IN_SUM, {{"${NUM_QUREGS}", numQuregs}}, caller); } void validate_quregsCanBeSummed(Qureg out, Qureg* in, int numIn, const char* caller) { + if (!global_isValidationEnabled) + return; + for (int i=0; i= 1, report::INVALID_NUM_INIT_PURE_STATES, {{"${NUM_STATES}", numPureStates}}, caller); } @@ -4302,6 +4827,9 @@ void validate_densMatrExpecDiagMatrValueIsReal(qcomp value, qcomp exponent, cons void validate_quregCanBeReduced(Qureg qureg, int numTraceQubits, const char* caller) { + if (!global_isValidationEnabled) + return; + // 0 < numTraceQubits <= numQubits is assured by validate_targets(), but // numTraceQubits == numQubtis is permitted there though forbidden here assertThat(numTraceQubits < qureg.numQubits, report::NUM_TRACE_QUBITS_EQUALS_QUREG_SIZE, caller); @@ -4328,6 +4856,9 @@ void validate_quregCanBeReduced(Qureg qureg, int numTraceQubits, const char* cal void validate_quregCanBeSetToReducedDensMatr(Qureg out, Qureg in, int numTraceQubits, const char* caller) { + if (!global_isValidationEnabled) + return; + int numRemainingQubits = in.numQubits - numTraceQubits; tokenSubs vars = { @@ -4350,6 +4881,9 @@ void validate_quregCanBeSetToReducedDensMatr(Qureg out, Qureg in, int numTraceQu void validate_canReadFile(string fn, const char* caller) { + if (!global_isValidationEnabled) + return; + /// @todo embed filename into error message when tokenSubs is updated to permit strings assertThat(parser_canReadFile(fn), report::CANNOT_READ_FILE, caller); } @@ -4362,6 +4896,9 @@ void validate_canReadFile(string fn, const char* caller) { void validate_tempListAllocSucceeded(bool succeeded, qindex numElems, qindex numBytesPerElem, const char* caller) { + if (!global_isValidationEnabled) + return; + // avoid showing total bytes in case it overflows tokenSubs vars = { {"${NUM_ELEMS}", numElems}, @@ -4372,6 +4909,9 @@ void validate_tempListAllocSucceeded(bool succeeded, qindex numElems, qindex num void validate_tempAllocSucceeded(bool succeeded, size_t numBytes, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(succeeded, report::TEMP_ALLOC_FAILED, {{"${NUM_BYTES}", numBytes}}, caller); } @@ -4383,6 +4923,9 @@ void validate_tempAllocSucceeded(bool succeeded, size_t numBytes, const char* ca void validate_envVarPermitNodesToShareGpu(string varValue, const char* caller) { + if (!global_isValidationEnabled) + return; + // though caller should gaurantee varValue contains at least one character, // we'll still check to avoid a segfault if this gaurantee is broken bool isValid = (varValue.size() == 1) && (varValue[0] == '0' || varValue[0] == '1'); @@ -4391,6 +4934,9 @@ void validate_envVarPermitNodesToShareGpu(string varValue, const char* caller) { void validate_envVarDefaultValidationEpsilon(string varValue, const char* caller) { + if (!global_isValidationEnabled) + return; + assertThat(parser_isAnySizedReal(varValue), report::DEFAULT_EPSILON_ENV_VAR_NOT_A_REAL, caller); assertThat(parser_isValidReal(varValue), report::DEFAULT_EPSILON_ENV_VAR_EXCEEDS_QREAL_RANGE, caller);