From 47dfc10501afa1493467cadeebd03bbee4472285 Mon Sep 17 00:00:00 2001 From: Tyson Jones Date: Sun, 8 Jun 2025 13:24:28 +0200 Subject: [PATCH 1/5] added applyNonUnitaryTrotterizedPauliStrSumGadget but still investigating the angle convention --- quest/include/operations.h | 24 ++++++++++++++++++++++-- quest/src/api/operations.cpp | 29 ++++++++++++++++++++++------- tests/unit/operations.cpp | 4 +++- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/quest/include/operations.h b/quest/include/operations.h index 3054802c2..e6a40ecc8 100644 --- a/quest/include/operations.h +++ b/quest/include/operations.h @@ -1834,6 +1834,7 @@ void multiplyPauliGadget(Qureg qureg, PauliStr str, qreal angle); qcomp factor = cexp(- theta / 2 * 1.i); setQuregToSuperposition(factor, qureg, 0,qureg,0,qureg); * ``` + * - Passing @p angle=0 is equivalent to effecting the identity, leaving the state unchanged. * * @myexample * ``` @@ -1850,13 +1851,20 @@ void multiplyPauliGadget(Qureg qureg, PauliStr str, qreal angle); // concisely applyPauliGadget(qureg, getInlinePauliStr("XYZ",{0,1,7}), theta); * ``` - * - Passing @p angle=0 is equivalent to effecting the identity, leaving the state unchanged. + * + * @see + * - applyNonUnitaryPauliGadget() */ void applyPauliGadget(Qureg qureg, PauliStr str, qreal angle); -/// @notyetdoced + +/** @notyetdoced + * + * This function generalises applyPauliGadget() to accept a complex angle. + */ void applyNonUnitaryPauliGadget(Qureg qureg, PauliStr str, qcomp angle); + /// @notyetdoced void applyControlledPauliGadget(Qureg qureg, int control, PauliStr str, qreal angle); @@ -2315,10 +2323,22 @@ void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace); * * > These formulations are taken from 'Finding Exponential Product Formulas * > of Higher Orders', Naomichi Hatano and Masuo Suzuki (2005) (arXiv). + * + * @see + * - applyNonUnitaryTrotterizedPauliStrSumGadget() */ void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps); +/** @notyetdoced + * @notyettested + * + * A generalisation of applyTrotterizedPauliStrSumGadget() which accepts a complex angle, + * permitting (for example) simulation of imaginary-time evolution + */ +void applyNonUnitaryTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps); + + // end de-mangler #ifdef __cplusplus } diff --git a/quest/src/api/operations.cpp b/quest/src/api/operations.cpp index f64336457..1940ecbd9 100644 --- a/quest/src/api/operations.cpp +++ b/quest/src/api/operations.cpp @@ -1130,7 +1130,7 @@ void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace) { // workspace -> qureg, and qureg -> sum * qureg } -void applyFirstOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, bool reverse) { +void applyFirstOrderTrotter(Qureg qureg, PauliStrSum sum, qcomp angle, bool reverse) { // (internal, invoked by applyTrotterizedPauliStrSumGadget) @@ -1138,10 +1138,11 @@ void applyFirstOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, bool reve int j = reverse? sum.numTerms - i - 1 : i; qreal arg = 2 * angle * std::real(sum.coeffs[j]); // 2 undoes Gadget convention applyPauliGadget(qureg, sum.strings[j], arg); // caller disabled valiation therein + applyNonUnitaryPauliGadget(qureg, sum.strings[j], arg); // caller disabled valiation therein } } -void applyHigherOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, int order) { +void applyHigherOrderTrotter(Qureg qureg, PauliStrSum sum, qcomp angle, int order) { // (internal, invoked by applyTrotterizedPauliStrSumGadget) @@ -1154,8 +1155,8 @@ void applyHigherOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, int orde } else { qreal p = 1. / (4 - std::pow(4, 1./(order-1))); - qreal a = p * angle; - qreal b = (1-4*p) * angle; + qcomp a = p * angle; + qcomp b = (1-4*p) * angle; int lower = order - 2; applyFirstOrderTrotter(qureg, sum, a, lower); @@ -1166,15 +1167,15 @@ void applyHigherOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, int orde } } -void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps) { +void applyNonUnitaryTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps) { validate_quregFields(qureg, __func__); validate_pauliStrSumFields(sum, __func__); validate_pauliStrSumTargets(sum, qureg, __func__); - validate_pauliStrSumIsHermitian(sum, __func__); validate_trotterParams(qureg, order, reps, __func__); + // sum is permitted to be non-Hermitian // exp(i angle sum) = identity when angle=0 - if (angle == 0) + if (angle == qcomp(0,0)) return; // record validation state then disable to avoid repeated @@ -1196,6 +1197,20 @@ void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle /// implement these above or into another function? } +void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps) { + + // validate inputs here despite re-validation below so that func name is correct in error message + validate_quregFields(qureg, __func__); + validate_pauliStrSumFields(sum, __func__); + validate_pauliStrSumTargets(sum, qureg, __func__); + validate_trotterParams(qureg, order, reps, __func__); + + // crucially, require that sum coefficients are real + validate_pauliStrSumIsHermitian(sum, __func__); + + applyNonUnitaryTrotterizedPauliStrSumGadget(qureg, sum, angle, order, reps); +} + } // end de-mangler diff --git a/tests/unit/operations.cpp b/tests/unit/operations.cpp index 74bca9046..0fc33851c 100644 --- a/tests/unit/operations.cpp +++ b/tests/unit/operations.cpp @@ -200,7 +200,7 @@ void TEST_ON_CACHED_QUREG_AND_MATRIX(quregCache quregs, matrixCache matrices, au * - applyDiagMatrPower: diagpower * - applyCompMatr: compmatr * - applyPauliStr: paulistr - * - applyPauliGadgt: pauligad + * - applyPauliGadget: pauligad */ enum ArgsFlag { none, scalar, axisrots, diagmatr, diagpower, compmatr, paulistr, pauligad }; @@ -1959,3 +1959,5 @@ TEST_CASE( "applyNonUnitaryPauliGadget", TEST_CATEGORY ) { */ void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps); + +void applyNonUnitaryTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps); From c591e9c49e35a57ae53ef7006830b571a47ca51f Mon Sep 17 00:00:00 2001 From: Tyson Jones Date: Sun, 8 Jun 2025 13:25:16 +0200 Subject: [PATCH 2/5] reversed the Trotter phase which I believe was previously erroneous; while documented as exp(i t H), the previous implementation appeared to effect exp(-i t H) since it did not undo the -1 factor in the PauliGadget convention. Eep! --- quest/src/api/operations.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quest/src/api/operations.cpp b/quest/src/api/operations.cpp index 1940ecbd9..677490a39 100644 --- a/quest/src/api/operations.cpp +++ b/quest/src/api/operations.cpp @@ -1136,8 +1136,8 @@ void applyFirstOrderTrotter(Qureg qureg, PauliStrSum sum, qcomp angle, bool reve for (qindex i=0; i Date: Thu, 12 Jun 2025 19:37:01 +0200 Subject: [PATCH 3/5] better defended Gadget convention undo --- quest/src/api/operations.cpp | 3 ++- quest/src/core/utilities.cpp | 5 +++-- quest/src/core/utilities.hpp | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/quest/src/api/operations.cpp b/quest/src/api/operations.cpp index 677490a39..3b58f298f 100644 --- a/quest/src/api/operations.cpp +++ b/quest/src/api/operations.cpp @@ -1136,8 +1136,9 @@ void applyFirstOrderTrotter(Qureg qureg, PauliStrSum sum, qcomp angle, bool reve for (qindex i=0; i Date: Thu, 12 Jun 2025 19:37:10 +0200 Subject: [PATCH 4/5] added doc --- quest/include/operations.h | 162 +++++++++++++++++++++++++++++++++++-- 1 file changed, 155 insertions(+), 7 deletions(-) diff --git a/quest/include/operations.h b/quest/include/operations.h index e6a40ecc8..0b11980e7 100644 --- a/quest/include/operations.h +++ b/quest/include/operations.h @@ -2273,8 +2273,13 @@ extern "C" { void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace); -/** @notyetdoced - * @notyettested +/** @notyettested + * + * Effects (an approximation to) the exponential of @p sum, weighted by @p angle, upon @p qureg, + * via the symmetrized Trotter-Suzuki decomposition (arXiv). + * Increasing @p reps (the number of Trotter repetitions) or @p order (an even, positive integer or one) + * improves the accuracy of the approximation (reducing the "Trotter error" due to non-commuting + * terms of @p sum), though increases the runtime linearly and exponentially respectively. * * @formulae * @@ -2283,14 +2288,18 @@ void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace); \exp \left(\iu \, \theta \, \hat{H} \right) * @f] * via a Trotter-Suzuki decomposition of the specified @p order and number of repetitions (@p reps). + * Simulation is exact, regardless of @p order or @p reps, only when all terms in @p sum commute. * + * @important + * Note that @f$ \theta @f$ lacks the @f$ -\frac{1}{2} @f$ prefactor present in other functions like + * applyPauliGadget(). * * To be precise, let @f$ r = @f$ @p reps and assume @p sum is composed of * @f$ T @f$-many terms of the form * @f[ \hat{H} = \sum\limits_j^T c_j \, \hat{\sigma}_j * @f] - * where @f$ c_j @f$ is the (necessarily real) coefficient of the @f$ j @f$-th PauliStr @f$ \hat{\sigma}_j @f$. + * where @f$ c_j @f$ is the coefficient of the @f$ j @f$-th PauliStr @f$ \hat{\sigma}_j @f$. * * - When @p order=1, this function performs first-order Trotterisation, whereby * @f[ @@ -2324,17 +2333,156 @@ void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace); * > These formulations are taken from 'Finding Exponential Product Formulas * > of Higher Orders', Naomichi Hatano and Masuo Suzuki (2005) (arXiv). * + * @equivalences + * + * - Time evolution of duration @f$ t @f$ under a time-independent Hamiltonian @p sum = @f$ \hat{H} @f$, as + * per the unitary time evolution operator + * @f[ + \hat{U}(t) = \exp(- \iu \, t \,\hat{H} \, / \, \hbar) + * @f] + * is approximated via @f$ \theta = - t / \hbar @f$. + * ``` + qreal time = 3.14; + qreal angle = - time / hbar; + applyTrotterizedPauliStrSumGadget(qureg, sum, angle, order, reps); + * ``` + * - This function is equivalent to applyNonUnitaryTrotterizedPauliStrSumGadget() when passing + * a @p qcomp instance with a zero imaginary component as the @p angle parameter. This latter + * function is useful for generalising dynamical simulation to imaginary-time evolution. + * + * @constraints + * - Unitarity of the prescribed exponential(s) requires that @p sum is Hermitian, ergo containing + * only real coefficients. Validation will check that @p sum is approximately Hermitian, permitting + * coefficients with imaginary components smaller (in magnitude) than epsilon. + * @f[ + \max\limits_{i} \Big|c_i| \le \valeps + * @f] + * where the validation epsilon @f$ \valeps @f$ can be adjusted with setValidationEpsilon(). + * Otherwise, use applyNonUnitaryTrotterizedPauliStrSumGadget() to permit non-Hermitian @p sum + * and ergo effect a non-unitary exponential(s). + * - The @p angle parameter is necessarily real despite the validation epsilon, but can be relaxed + * to an arbitrary complex scalar using applyNonUnitaryTrotterizedPauliStrSumGadget(). + * - This function only ever effects @f$ \exp \left(\iu \, \theta \, \hat{H} \right) @f$ exactly + * when all PauliStr in @p sum = @f$ \hat{H} @f$ commute. + * + * @param[in,out] qureg the state to modify. + * @param[in] sum a weighted sum of Pauli strings to approximately exponentiate. + * @param[in] angle an effective prefactor of @p sum in the exponent. + * @param[in] order the order of the Trotter-Suzuki decomposition (e.g. @p 1, @p 2, @p 4, ...) + * @param[in] reps the number of Trotter repetitions + * + * @throws @validationerror + * - if @p qureg or @p sum are uninitialised. + * - if @p sum is not approximately Hermitian. + * - if @p sum contains non-identities on qubits beyond the size of @p qureg. + * - if @p order is not 1 nor a positive, @b even integer. + * - if @p reps is not a positive integer. + * * @see + * - applyPauliGadget() * - applyNonUnitaryTrotterizedPauliStrSumGadget() + * + * @author Tyson Jones */ void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps); -/** @notyetdoced - * @notyettested +/** @notyettested + * + * A generalisation of applyTrotterizedPauliStrSumGadget() which accepts a complex angle and permits + * @p sum to be non-Hermitian, thereby effecting a potentially non-unitary and non-CPTP operation. * - * A generalisation of applyTrotterizedPauliStrSumGadget() which accepts a complex angle, - * permitting (for example) simulation of imaginary-time evolution + * @formulae + * + * Let @f$ \hat{H} = @f$ @p sum and @f$ \theta = @f$ @p angle. This function approximates the action of + * @f[ + \exp \left(\iu \, \theta \, \hat{H} \right) + * @f] + * via a Trotter-Suzuki decomposition of the specified @p order and number of repetitions (@p reps). + * + * See applyTrotterizedPauliStrSumGadget() for more information about the decomposition. + * + * @equivalences + * + * - When @p angle is set to @f$ \theta = \iu \, \tau @f$ and @p sum = @f$ \hat{H} @f$ is Hermitian, + * this function (approximately) evolves @p qureg in imaginary-time. That is, letting + * @f$ \hat{U}(t) = \exp(-\iu \, t \, \hat{H}) @f$ be the normalised unitary evolution operator, this + * function effects the imaginary-time operator + @f[ + \hat{V}(\tau) = \hat{U}(t=-\iu \tau) = \exp(- \tau \hat{H}). + * @f] + * This operation drives the system toward the (unnormalised) groundstate. + * Let @f$ \{ \ket{\phi_i} \} @f$ and @f$ \{ \ket{\lambda_i} \} @f$ be the eigenstates and respective + * eigenvalues of @f$ \hat{H} @f$, which are real due to Hermiticity. + * @f[ + \hat{H} = \sum \limits_i \lambda_i \ket{\phi_i}\bra{\phi_i}, + \;\;\;\;\; \lambda_i \in \mathbb{R}. + * @f] + * + * - When @p qureg is a statevector @f$ \svpsi @f$ and can ergo be expressed in the basis of + * @f$ \{ \ket{\phi_i} \} @f$ as @f$ \svpsi = \sum_i \alpha_i \ket{\phi_i} @f$, + * this function approximates + * @f[ + \svpsi \, \rightarrow \, \hat{V}(\tau) \svpsi = + \sum\limits_i \alpha_i \exp(- \tau \, \lambda_i) \ket{\phi_i}. + * @f] + * - When @p qureg is a density matrix and is ergo expressible as + * @f$ \dmrho = \sum\limits_{ij} \alpha_{ij} \ket{\phi_i}\bra{\phi_j} @f$, this function effects + * @f[ + \dmrho \, \rightarrow \, \hat{V}(\tau) \dmrho \hat{V}(\tau)^\dagger = + \sum\limits_{ij} \alpha_{ij} \exp(-\tau (\lambda_i + \lambda_j)) \ket{\phi_i}\bra{\phi_j}. + * @f] + * + * As @f$ \tau \rightarrow \infty @f$, the resulting unnormalised state approaches statevector + * @f$ \svpsi \rightarrow \alpha_0 \exp(-\tau \lambda_0) \ket{\phi_0} @f$ or density matrix + * @f$ \dmrho \rightarrow \alpha_{0,0} \exp(-2 \tau \lambda_0) \ket{\phi_0}\bra{\phi_0} @f$, + * where @f$ \lambda_0 @f$ is the minimum eigenvalue and @f$ \ket{\phi_0} @f$ is the groundstate. + * Assuming the initial overlap @f$ \alpha_0 @f$ is not zero (or exponentially tiny), + * subsequent renormalisation via setQuregToRenormalized() produces the pure + * ground-state @f$ \ket{\phi_0} @f$. + * + * ``` + // pray for a non-zero initial overlap + initRandomPureState(qureg); // works even for density matrices + + // minimize then renormalise + qreal tau = 10; // impatient infinity + int order = 4; + int reps = 100; + applyNonUnitaryTrotterizedPauliStrSumGadget(qureg, hamil, tau * 1i, order, reps); + setQuregToRenormalized(qureg); + + // ground-state (phi_0) + reportQureg(qureg); + + // lowest lying eigenvalue (lambda_0) + qreal expec = calcExpecPauliStrSum(qureg, hamil); + reportScalar("expec", expec); + * ``` + * + * Note degenerate eigenvalues will yield a pure superposition of the corresponding eigenstates, with + * coefficients informed by the initial, relative populations. + * + * - When @p angle is real and @p sum is Hermitian (has approximately real coefficients), this + * function is equivalent to applyTrotterizedPauliStrSumGadget() + * + * @constraints + * - This function only ever effects @f$ \exp \left(\iu \, \theta \, \hat{H} \right) @f$ exactly + * when all PauliStr in @p sum = @f$ \hat{H} @f$ commute. + * + * @param[in,out] qureg the state to modify. + * @param[in] sum a weighted sum of Pauli strings to approximately exponentiate. + * @param[in] angle an effective prefactor of @p sum in the exponent. + * @param[in] order the order of the Trotter-Suzuki decomposition (e.g. @p 1, @p 2, @p 4, ...) + * @param[in] reps the number of Trotter repetitions + * + * @throws @validationerror + * - if @p qureg or @p sum are uninitialised. + * - if @p sum contains non-identities on qubits beyond the size of @p qureg. + * - if @p order is not 1 nor a positive, @b even integer. + * - if @p reps is not a positive integer. + * + * @author Tyson Jones */ void applyNonUnitaryTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps); From 6c8ba3dc381770dc44a3ac81cb48b41b96ff52be Mon Sep 17 00:00:00 2001 From: Tyson Jones Date: Thu, 12 Jun 2025 19:37:31 +0200 Subject: [PATCH 5/5] fixed defunct references to @cppoverload --- quest/include/calculations.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quest/include/calculations.h b/quest/include/calculations.h index 3d721b573..b54af4492 100644 --- a/quest/include/calculations.h +++ b/quest/include/calculations.h @@ -336,7 +336,7 @@ qcomp calcExpecNonHermitianFullStateDiagMatrPower(Qureg qureg, FullStateDiagMatr /// @notyettested /// @notyetdoced /// @notyetvalidated -/// @cppoverload +/// @cppvectoroverload /// @see calcProbOfMultiQubitOutcome() qreal calcProbOfMultiQubitOutcome(Qureg qureg, std::vector qubits, std::vector outcomes); @@ -354,7 +354,7 @@ std::vector calcProbsOfAllMultiQubitOutcomes(Qureg qureg, std::vector traceOutQubits); @@ -363,7 +363,7 @@ Qureg calcPartialTrace(Qureg qureg, std::vector traceOutQubits); /// @notyettested /// @notyetdoced /// @notyetvalidated -/// @cppoverload +/// @cppvectoroverload /// @see calcReducedDensityMatrix() Qureg calcReducedDensityMatrix(Qureg qureg, std::vector retainQubits);