Skip to content

Commit f3baf34

Browse files
patched Trotter sign and added non-unitary Trotter (#647)
- applyTrotterizedPauliStrSumGadget() was documented to effect exp(i t H) but actually effected exp(-i t H), eep! Thankfully the doc warned the function was untested. It now correctly effects exp(i t H). - defensively removed hardcoding of the scalar responsible for the above bug, which undoes the coefficient convention of the applyPauliGadget() functions - added applyNonUnitaryTrotterizedPauliStrSumGadget() which permits a non-Hermitian Hamiltonian (i.e. with non-negligible imaginary components of the coefficients) and a complex angle parameter. Among other things, this permits simple imaginary-time evolution - added the full doc for both functions - fixed defunct doxygen command (@cppoverload) Note that both these Trotter functions remain without unit tests since impending new functions are anticipated which will generalise the tests.
1 parent 5fa4f60 commit f3baf34

File tree

6 files changed

+207
-21
lines changed

6 files changed

+207
-21
lines changed

quest/include/calculations.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ qcomp calcExpecNonHermitianFullStateDiagMatrPower(Qureg qureg, FullStateDiagMatr
336336
/// @notyettested
337337
/// @notyetdoced
338338
/// @notyetvalidated
339-
/// @cppoverload
339+
/// @cppvectoroverload
340340
/// @see calcProbOfMultiQubitOutcome()
341341
qreal calcProbOfMultiQubitOutcome(Qureg qureg, std::vector<int> qubits, std::vector<int> outcomes);
342342

@@ -354,7 +354,7 @@ std::vector<qreal> calcProbsOfAllMultiQubitOutcomes(Qureg qureg, std::vector<int
354354
/// @notyettested
355355
/// @notyetdoced
356356
/// @notyetvalidated
357-
/// @cppoverload
357+
/// @cppvectoroverload
358358
/// @see calcPartialTrace()
359359
Qureg calcPartialTrace(Qureg qureg, std::vector<int> traceOutQubits);
360360

@@ -363,7 +363,7 @@ Qureg calcPartialTrace(Qureg qureg, std::vector<int> traceOutQubits);
363363
/// @notyettested
364364
/// @notyetdoced
365365
/// @notyetvalidated
366-
/// @cppoverload
366+
/// @cppvectoroverload
367367
/// @see calcReducedDensityMatrix()
368368
Qureg calcReducedDensityMatrix(Qureg qureg, std::vector<int> retainQubits);
369369

quest/include/operations.h

Lines changed: 173 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,7 @@ void multiplyPauliGadget(Qureg qureg, PauliStr str, qreal angle);
18341834
qcomp factor = cexp(- theta / 2 * 1.i);
18351835
setQuregToSuperposition(factor, qureg, 0,qureg,0,qureg);
18361836
* ```
1837+
* - Passing @p angle=0 is equivalent to effecting the identity, leaving the state unchanged.
18371838
*
18381839
* @myexample
18391840
* ```
@@ -1850,13 +1851,20 @@ void multiplyPauliGadget(Qureg qureg, PauliStr str, qreal angle);
18501851
// concisely
18511852
applyPauliGadget(qureg, getInlinePauliStr("XYZ",{0,1,7}), theta);
18521853
* ```
1853-
* - Passing @p angle=0 is equivalent to effecting the identity, leaving the state unchanged.
1854+
*
1855+
* @see
1856+
* - applyNonUnitaryPauliGadget()
18541857
*/
18551858
void applyPauliGadget(Qureg qureg, PauliStr str, qreal angle);
18561859

1857-
/// @notyetdoced
1860+
1861+
/** @notyetdoced
1862+
*
1863+
* This function generalises applyPauliGadget() to accept a complex angle.
1864+
*/
18581865
void applyNonUnitaryPauliGadget(Qureg qureg, PauliStr str, qcomp angle);
18591866

1867+
18601868
/// @notyetdoced
18611869
void applyControlledPauliGadget(Qureg qureg, int control, PauliStr str, qreal angle);
18621870

@@ -2265,8 +2273,13 @@ extern "C" {
22652273
void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace);
22662274

22672275

2268-
/** @notyetdoced
2269-
* @notyettested
2276+
/** @notyettested
2277+
*
2278+
* Effects (an approximation to) the exponential of @p sum, weighted by @p angle, upon @p qureg,
2279+
* via the symmetrized Trotter-Suzuki decomposition (<a href="https://arxiv.org/abs/math-ph/0506007">arXiv</a>).
2280+
* Increasing @p reps (the number of Trotter repetitions) or @p order (an even, positive integer or one)
2281+
* improves the accuracy of the approximation (reducing the "Trotter error" due to non-commuting
2282+
* terms of @p sum), though increases the runtime linearly and exponentially respectively.
22702283
*
22712284
* @formulae
22722285
*
@@ -2275,14 +2288,18 @@ void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace);
22752288
\exp \left(\iu \, \theta \, \hat{H} \right)
22762289
* @f]
22772290
* via a Trotter-Suzuki decomposition of the specified @p order and number of repetitions (@p reps).
2291+
* Simulation is exact, regardless of @p order or @p reps, only when all terms in @p sum commute.
22782292
*
2293+
* @important
2294+
* Note that @f$ \theta @f$ lacks the @f$ -\frac{1}{2} @f$ prefactor present in other functions like
2295+
* applyPauliGadget().
22792296
*
22802297
* To be precise, let @f$ r = @f$ @p reps and assume @p sum is composed of
22812298
* @f$ T @f$-many terms of the form
22822299
* @f[
22832300
\hat{H} = \sum\limits_j^T c_j \, \hat{\sigma}_j
22842301
* @f]
2285-
* where @f$ c_j @f$ is the (necessarily real) coefficient of the @f$ j @f$-th PauliStr @f$ \hat{\sigma}_j @f$.
2302+
* where @f$ c_j @f$ is the coefficient of the @f$ j @f$-th PauliStr @f$ \hat{\sigma}_j @f$.
22862303
*
22872304
* - When @p order=1, this function performs first-order Trotterisation, whereby
22882305
* @f[
@@ -2315,10 +2332,161 @@ void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace);
23152332
*
23162333
* > These formulations are taken from 'Finding Exponential Product Formulas
23172334
* > of Higher Orders', Naomichi Hatano and Masuo Suzuki (2005) (<a href="https://arxiv.org/abs/math-ph/0506007">arXiv</a>).
2335+
*
2336+
* @equivalences
2337+
*
2338+
* - Time evolution of duration @f$ t @f$ under a time-independent Hamiltonian @p sum = @f$ \hat{H} @f$, as
2339+
* per the unitary time evolution operator
2340+
* @f[
2341+
\hat{U}(t) = \exp(- \iu \, t \,\hat{H} \, / \, \hbar)
2342+
* @f]
2343+
* is approximated via @f$ \theta = - t / \hbar @f$.
2344+
* ```
2345+
qreal time = 3.14;
2346+
qreal angle = - time / hbar;
2347+
applyTrotterizedPauliStrSumGadget(qureg, sum, angle, order, reps);
2348+
* ```
2349+
* - This function is equivalent to applyNonUnitaryTrotterizedPauliStrSumGadget() when passing
2350+
* a @p qcomp instance with a zero imaginary component as the @p angle parameter. This latter
2351+
* function is useful for generalising dynamical simulation to imaginary-time evolution.
2352+
*
2353+
* @constraints
2354+
* - Unitarity of the prescribed exponential(s) requires that @p sum is Hermitian, ergo containing
2355+
* only real coefficients. Validation will check that @p sum is approximately Hermitian, permitting
2356+
* coefficients with imaginary components smaller (in magnitude) than epsilon.
2357+
* @f[
2358+
\max\limits_{i} \Big|c_i| \le \valeps
2359+
* @f]
2360+
* where the validation epsilon @f$ \valeps @f$ can be adjusted with setValidationEpsilon().
2361+
* Otherwise, use applyNonUnitaryTrotterizedPauliStrSumGadget() to permit non-Hermitian @p sum
2362+
* and ergo effect a non-unitary exponential(s).
2363+
* - The @p angle parameter is necessarily real despite the validation epsilon, but can be relaxed
2364+
* to an arbitrary complex scalar using applyNonUnitaryTrotterizedPauliStrSumGadget().
2365+
* - This function only ever effects @f$ \exp \left(\iu \, \theta \, \hat{H} \right) @f$ exactly
2366+
* when all PauliStr in @p sum = @f$ \hat{H} @f$ commute.
2367+
*
2368+
* @param[in,out] qureg the state to modify.
2369+
* @param[in] sum a weighted sum of Pauli strings to approximately exponentiate.
2370+
* @param[in] angle an effective prefactor of @p sum in the exponent.
2371+
* @param[in] order the order of the Trotter-Suzuki decomposition (e.g. @p 1, @p 2, @p 4, ...)
2372+
* @param[in] reps the number of Trotter repetitions
2373+
*
2374+
* @throws @validationerror
2375+
* - if @p qureg or @p sum are uninitialised.
2376+
* - if @p sum is not approximately Hermitian.
2377+
* - if @p sum contains non-identities on qubits beyond the size of @p qureg.
2378+
* - if @p order is not 1 nor a positive, @b even integer.
2379+
* - if @p reps is not a positive integer.
2380+
*
2381+
* @see
2382+
* - applyPauliGadget()
2383+
* - applyNonUnitaryTrotterizedPauliStrSumGadget()
2384+
*
2385+
* @author Tyson Jones
23182386
*/
23192387
void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps);
23202388

23212389

2390+
/** @notyettested
2391+
*
2392+
* A generalisation of applyTrotterizedPauliStrSumGadget() which accepts a complex angle and permits
2393+
* @p sum to be non-Hermitian, thereby effecting a potentially non-unitary and non-CPTP operation.
2394+
*
2395+
* @formulae
2396+
*
2397+
* Let @f$ \hat{H} = @f$ @p sum and @f$ \theta = @f$ @p angle. This function approximates the action of
2398+
* @f[
2399+
\exp \left(\iu \, \theta \, \hat{H} \right)
2400+
* @f]
2401+
* via a Trotter-Suzuki decomposition of the specified @p order and number of repetitions (@p reps).
2402+
*
2403+
* See applyTrotterizedPauliStrSumGadget() for more information about the decomposition.
2404+
*
2405+
* @equivalences
2406+
*
2407+
* - When @p angle is set to @f$ \theta = \iu \, \tau @f$ and @p sum = @f$ \hat{H} @f$ is Hermitian,
2408+
* this function (approximately) evolves @p qureg in imaginary-time. That is, letting
2409+
* @f$ \hat{U}(t) = \exp(-\iu \, t \, \hat{H}) @f$ be the normalised unitary evolution operator, this
2410+
* function effects the imaginary-time operator
2411+
@f[
2412+
\hat{V}(\tau) = \hat{U}(t=-\iu \tau) = \exp(- \tau \hat{H}).
2413+
* @f]
2414+
* This operation drives the system toward the (unnormalised) groundstate.
2415+
* Let @f$ \{ \ket{\phi_i} \} @f$ and @f$ \{ \ket{\lambda_i} \} @f$ be the eigenstates and respective
2416+
* eigenvalues of @f$ \hat{H} @f$, which are real due to Hermiticity.
2417+
* @f[
2418+
\hat{H} = \sum \limits_i \lambda_i \ket{\phi_i}\bra{\phi_i},
2419+
\;\;\;\;\; \lambda_i \in \mathbb{R}.
2420+
* @f]
2421+
*
2422+
* - When @p qureg is a statevector @f$ \svpsi @f$ and can ergo be expressed in the basis of
2423+
* @f$ \{ \ket{\phi_i} \} @f$ as @f$ \svpsi = \sum_i \alpha_i \ket{\phi_i} @f$,
2424+
* this function approximates
2425+
* @f[
2426+
\svpsi \, \rightarrow \, \hat{V}(\tau) \svpsi =
2427+
\sum\limits_i \alpha_i \exp(- \tau \, \lambda_i) \ket{\phi_i}.
2428+
* @f]
2429+
* - When @p qureg is a density matrix and is ergo expressible as
2430+
* @f$ \dmrho = \sum\limits_{ij} \alpha_{ij} \ket{\phi_i}\bra{\phi_j} @f$, this function effects
2431+
* @f[
2432+
\dmrho \, \rightarrow \, \hat{V}(\tau) \dmrho \hat{V}(\tau)^\dagger =
2433+
\sum\limits_{ij} \alpha_{ij} \exp(-\tau (\lambda_i + \lambda_j)) \ket{\phi_i}\bra{\phi_j}.
2434+
* @f]
2435+
*
2436+
* As @f$ \tau \rightarrow \infty @f$, the resulting unnormalised state approaches statevector
2437+
* @f$ \svpsi \rightarrow \alpha_0 \exp(-\tau \lambda_0) \ket{\phi_0} @f$ or density matrix
2438+
* @f$ \dmrho \rightarrow \alpha_{0,0} \exp(-2 \tau \lambda_0) \ket{\phi_0}\bra{\phi_0} @f$,
2439+
* where @f$ \lambda_0 @f$ is the minimum eigenvalue and @f$ \ket{\phi_0} @f$ is the groundstate.
2440+
* Assuming the initial overlap @f$ \alpha_0 @f$ is not zero (or exponentially tiny),
2441+
* subsequent renormalisation via setQuregToRenormalized() produces the pure
2442+
* ground-state @f$ \ket{\phi_0} @f$.
2443+
*
2444+
* ```
2445+
// pray for a non-zero initial overlap
2446+
initRandomPureState(qureg); // works even for density matrices
2447+
2448+
// minimize then renormalise
2449+
qreal tau = 10; // impatient infinity
2450+
int order = 4;
2451+
int reps = 100;
2452+
applyNonUnitaryTrotterizedPauliStrSumGadget(qureg, hamil, tau * 1i, order, reps);
2453+
setQuregToRenormalized(qureg);
2454+
2455+
// ground-state (phi_0)
2456+
reportQureg(qureg);
2457+
2458+
// lowest lying eigenvalue (lambda_0)
2459+
qreal expec = calcExpecPauliStrSum(qureg, hamil);
2460+
reportScalar("expec", expec);
2461+
* ```
2462+
*
2463+
* Note degenerate eigenvalues will yield a pure superposition of the corresponding eigenstates, with
2464+
* coefficients informed by the initial, relative populations.
2465+
*
2466+
* - When @p angle is real and @p sum is Hermitian (has approximately real coefficients), this
2467+
* function is equivalent to applyTrotterizedPauliStrSumGadget()
2468+
*
2469+
* @constraints
2470+
* - This function only ever effects @f$ \exp \left(\iu \, \theta \, \hat{H} \right) @f$ exactly
2471+
* when all PauliStr in @p sum = @f$ \hat{H} @f$ commute.
2472+
*
2473+
* @param[in,out] qureg the state to modify.
2474+
* @param[in] sum a weighted sum of Pauli strings to approximately exponentiate.
2475+
* @param[in] angle an effective prefactor of @p sum in the exponent.
2476+
* @param[in] order the order of the Trotter-Suzuki decomposition (e.g. @p 1, @p 2, @p 4, ...)
2477+
* @param[in] reps the number of Trotter repetitions
2478+
*
2479+
* @throws @validationerror
2480+
* - if @p qureg or @p sum are uninitialised.
2481+
* - if @p sum contains non-identities on qubits beyond the size of @p qureg.
2482+
* - if @p order is not 1 nor a positive, @b even integer.
2483+
* - if @p reps is not a positive integer.
2484+
*
2485+
* @author Tyson Jones
2486+
*/
2487+
void applyNonUnitaryTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps);
2488+
2489+
23222490
// end de-mangler
23232491
#ifdef __cplusplus
23242492
}

quest/src/api/operations.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,18 +1130,20 @@ void multiplyPauliStrSum(Qureg qureg, PauliStrSum sum, Qureg workspace) {
11301130
// workspace -> qureg, and qureg -> sum * qureg
11311131
}
11321132

1133-
void applyFirstOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, bool reverse) {
1133+
void applyFirstOrderTrotter(Qureg qureg, PauliStrSum sum, qcomp angle, bool reverse) {
11341134

11351135
// (internal, invoked by applyTrotterizedPauliStrSumGadget)
11361136

11371137
for (qindex i=0; i<sum.numTerms; i++) {
11381138
int j = reverse? sum.numTerms - i - 1 : i;
1139-
qreal arg = 2 * angle * std::real(sum.coeffs[j]); // 2 undoes Gadget convention
1140-
applyPauliGadget(qureg, sum.strings[j], arg); // caller disabled valiation therein
1139+
1140+
// effect exp(i angle * sum) by undoing gadget pre-factor
1141+
qcomp arg = angle * sum.coeffs[j] / util_getPhaseFromGateAngle(1);
1142+
applyNonUnitaryPauliGadget(qureg, sum.strings[j], arg); // caller disabled valiation therein
11411143
}
11421144
}
11431145

1144-
void applyHigherOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, int order) {
1146+
void applyHigherOrderTrotter(Qureg qureg, PauliStrSum sum, qcomp angle, int order) {
11451147

11461148
// (internal, invoked by applyTrotterizedPauliStrSumGadget)
11471149

@@ -1154,8 +1156,8 @@ void applyHigherOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, int orde
11541156

11551157
} else {
11561158
qreal p = 1. / (4 - std::pow(4, 1./(order-1)));
1157-
qreal a = p * angle;
1158-
qreal b = (1-4*p) * angle;
1159+
qcomp a = p * angle;
1160+
qcomp b = (1-4*p) * angle;
11591161

11601162
int lower = order - 2;
11611163
applyFirstOrderTrotter(qureg, sum, a, lower);
@@ -1166,15 +1168,15 @@ void applyHigherOrderTrotter(Qureg qureg, PauliStrSum sum, qreal angle, int orde
11661168
}
11671169
}
11681170

1169-
void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps) {
1171+
void applyNonUnitaryTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps) {
11701172
validate_quregFields(qureg, __func__);
11711173
validate_pauliStrSumFields(sum, __func__);
11721174
validate_pauliStrSumTargets(sum, qureg, __func__);
1173-
validate_pauliStrSumIsHermitian(sum, __func__);
11741175
validate_trotterParams(qureg, order, reps, __func__);
1176+
// sum is permitted to be non-Hermitian
11751177

11761178
// exp(i angle sum) = identity when angle=0
1177-
if (angle == 0)
1179+
if (angle == qcomp(0,0))
11781180
return;
11791181

11801182
// record validation state then disable to avoid repeated
@@ -1196,6 +1198,20 @@ void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle
11961198
/// implement these above or into another function?
11971199
}
11981200

1201+
void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps) {
1202+
1203+
// validate inputs here despite re-validation below so that func name is correct in error message
1204+
validate_quregFields(qureg, __func__);
1205+
validate_pauliStrSumFields(sum, __func__);
1206+
validate_pauliStrSumTargets(sum, qureg, __func__);
1207+
validate_trotterParams(qureg, order, reps, __func__);
1208+
1209+
// crucially, require that sum coefficients are real
1210+
validate_pauliStrSumIsHermitian(sum, __func__);
1211+
1212+
applyNonUnitaryTrotterizedPauliStrSumGadget(qureg, sum, angle, order, reps);
1213+
}
1214+
11991215
} // end de-mangler
12001216

12011217

quest/src/core/utilities.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -912,12 +912,13 @@ qreal util_getPhaseFromGateAngle(qreal angle) {
912912

913913
return - angle / 2;
914914
}
915-
916915
qcomp util_getPhaseFromGateAngle(qcomp angle) {
917916

918-
return -angle / qcomp(2.0, 0.0);
917+
return - angle / 2;
919918
}
920919

920+
921+
921922
/*
922923
* DECOHERENCE FACTORS
923924
*/

quest/src/core/utilities.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@ util_VectorIndexRange util_getLocalIndRangeOfVectorElemsWithinNode(int rank, qin
349349
*/
350350

351351
qreal util_getPhaseFromGateAngle(qreal angle);
352-
353352
qcomp util_getPhaseFromGateAngle(qcomp angle);
354353

355354

tests/unit/operations.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ void TEST_ON_CACHED_QUREG_AND_MATRIX(quregCache quregs, matrixCache matrices, au
200200
* - applyDiagMatrPower: diagpower
201201
* - applyCompMatr: compmatr
202202
* - applyPauliStr: paulistr
203-
* - applyPauliGadgt: pauligad
203+
* - applyPauliGadget: pauligad
204204
*/
205205

206206
enum ArgsFlag { none, scalar, axisrots, diagmatr, diagpower, compmatr, paulistr, pauligad };
@@ -1959,3 +1959,5 @@ TEST_CASE( "applyNonUnitaryPauliGadget", TEST_CATEGORY ) {
19591959
*/
19601960

19611961
void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps);
1962+
1963+
void applyNonUnitaryTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps);

0 commit comments

Comments
 (0)