The Quantum Exact Simulation Toolkit v4.2.0
Loading...
Searching...
No Matches
trotterisation.cpp
1/** @file
2 * API definitions for functions which involve Trotterising
3 * exponential operators, such as PauliStrSum gadgets, and
4 * so are inherently approximate.
5 *
6 * @author Tyson Jones
7 */
8
9#include "quest/include/qureg.h"
10#include "quest/include/paulis.h"
11#include "quest/include/matrices.h"
12
13#include "quest/src/core/validation.hpp"
14#include "quest/src/core/utilities.hpp"
15#include "quest/src/core/localiser.hpp"
16#include "quest/src/core/paulilogic.hpp"
17#include "quest/src/core/errors.hpp"
18
19#include <vector>
20
21using std::vector;
22
23
24
25/*
26 * INTERNAL UTILS
27 */
28
29void internal_applyFirstOrderTrotterRepetition(
30 Qureg qureg, vector<int>& ketCtrls, vector<int>& braCtrls,
31 vector<int>& states, PauliStrSum sum, qcomp angle, bool onlyLeftApply, bool reverse
32) {
33 // apply each sum term as a gadget, in forward or reverse order
34 for (qindex i=0; i<sum.numTerms; i++) {
35 int j = reverse? sum.numTerms - i - 1 : i;
36 qcomp coeff = sum.coeffs[j];
37 PauliStr str = sum.strings[j];
38
39 // effect |psi> -> exp(i angle * coeff * term)|psi>
40 qcomp arg = angle * coeff;
41 localiser_statevec_anyCtrlPauliGadget(qureg, ketCtrls, states, str, arg);
42
43 // term finished upon statevector
44 if (!qureg.isDensityMatrix)
45 continue;
46
47 // Linbladian propagator is only ever pre-multiplied
48 if (onlyLeftApply)
49 continue;
50
51 // effect rho -> rho exp(i angle * coeff * term)^dagger via linearised
52 // ||rho>> -> conj(exp(i angle * coeff * term)) (x) I ||rho>>
53 // = exp(- i conj(angle coeff) sign term) (x) I ||rho>>
54 arg = - std::conj(arg) * paulis_getSignOfPauliStrConj(str);
55 str = paulis_getShiftedPauliStr(str, qureg.numQubits);
56 localiser_statevec_anyCtrlPauliGadget(qureg, braCtrls, states, str, arg);
57 }
58}
59
60void internal_applyHigherOrderTrotterRepetition(
61 Qureg qureg, vector<int>& ketCtrls, vector<int>& braCtrls,
62 vector<int>& states, PauliStrSum sum, qcomp angle, int order, bool onlyLeftApply
63) {
64 if (order == 1) {
65 internal_applyFirstOrderTrotterRepetition(qureg, ketCtrls, braCtrls, states, sum, angle, onlyLeftApply, false);
66
67 } else if (order == 2) {
68 internal_applyFirstOrderTrotterRepetition(qureg, ketCtrls, braCtrls, states, sum, angle/2, onlyLeftApply, false);
69 internal_applyFirstOrderTrotterRepetition(qureg, ketCtrls, braCtrls, states, sum, angle/2, onlyLeftApply, true);
70
71 } else {
72 qreal p = 1. / (4 - std::pow(4, 1./(order-1)));
73 qcomp a = p * angle;
74 qcomp b = (1-4*p) * angle;
75
76 int lower = order - 2;
77 internal_applyHigherOrderTrotterRepetition(qureg, ketCtrls, braCtrls, states, sum, a, lower, onlyLeftApply); // angle -> a
78 internal_applyHigherOrderTrotterRepetition(qureg, ketCtrls, braCtrls, states, sum, a, lower, onlyLeftApply);
79 internal_applyHigherOrderTrotterRepetition(qureg, ketCtrls, braCtrls, states, sum, b, lower, onlyLeftApply); // angle -> b
80 internal_applyHigherOrderTrotterRepetition(qureg, ketCtrls, braCtrls, states, sum, a, lower, onlyLeftApply);
81 internal_applyHigherOrderTrotterRepetition(qureg, ketCtrls, braCtrls, states, sum, a, lower, onlyLeftApply);
82 }
83}
84
85void internal_applyAllTrotterRepetitions(
86 Qureg qureg, int* controls, int* states, int numControls,
87 PauliStrSum sum, qcomp angle, int order, int reps, bool onlyLeftApply
88) {
89 // exp(i angle sum) = identity when angle=0
90 if (angle == qcomp(0,0))
91 return;
92
93 // prepare control-qubit lists once for all invoked gadgets below
94 auto ketCtrlsVec = util_getVector(controls, numControls);
95 auto braCtrlsVec = (qureg.isDensityMatrix)? util_getBraQubits(ketCtrlsVec, qureg) : vector<int>{};
96 auto statesVec = util_getVector(states, numControls);
97
98 qcomp arg = angle / reps;
99
100 // perform carefully-ordered sequence of gadgets
101 for (int r=0; r<reps; r++)
102 internal_applyHigherOrderTrotterRepetition(
103 qureg, ketCtrlsVec, braCtrlsVec, statesVec, sum, arg, order, onlyLeftApply);
104
105 /// @todo
106 /// the accuracy of Trotterisation is greatly improved by randomisation
107 /// or (even sub-optimal) grouping into commuting terms. Should we
108 /// implement these above or into another function?
109}
110
111qindex internal_getNumTotalSuperPropagatorTerms(PauliStrSum hamil, PauliStrSum* jumps, int numJumps) {
112
113 // this function returns 0 to indicate an overflow, which will never
114 // be confused for the correct non-overflowed output because hamil.numTerms>0
115 int OVERFLOW_FLAG = 0;
116
117 if (util_willProdOverflow({2,hamil.numTerms}))
118 return OVERFLOW_FLAG;
119
120 // I (x) H + conj(H) (x) I
121 qindex numTerms = 2 * hamil.numTerms;
122
123 for (int i=0; i<numJumps; i++) {
124 qindex n = jumps[i].numTerms;
125
126 if (util_willProdOverflow({n,n,3}))
127 return OVERFLOW_FLAG;
128 if (util_willSumOverflow({numTerms, 3*n*n}))
129 return OVERFLOW_FLAG;
130
131 // conj(J) (x) J has n^2 terms
132 numTerms += n * n;
133
134 // I (x) (adj(J) . J ) + conj(...) (x) I is bounded by 2*n^2 terms
135 numTerms += 2 * paulis_getNumTermsInPauliStrSumProdOfAdjointWithSelf(jumps[i]);
136 }
137
138 // indicate no overflow
139 return numTerms;
140}
141
142
143
144/*
145 * PAULI STR SUM GADGETS
146 */
147
148extern "C" {
149
150void applyTrotterizedNonUnitaryPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps) {
151 validate_quregFields(qureg, __func__);
152 validate_pauliStrSumFields(sum, __func__);
153 validate_pauliStrSumTargets(sum, qureg, __func__);
154 validate_trotterParams(qureg, order, reps, __func__);
155 // sum is permitted to be non-Hermitian
156
157 // |psi> -> U |psi>, rho -> U rho U^dagger
158 bool onlyLeftApply = false;
159 internal_applyAllTrotterRepetitions(qureg, nullptr, nullptr, 0, sum, angle, order, reps, onlyLeftApply);
160}
161
162void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps) {
163 validate_quregFields(qureg, __func__);
164 validate_pauliStrSumFields(sum, __func__);
165 validate_pauliStrSumTargets(sum, qureg, __func__);
166 validate_pauliStrSumIsHermitian(sum, __func__);
167 validate_trotterParams(qureg, order, reps, __func__);
168
169 bool onlyLeftApply = false;
170 internal_applyAllTrotterRepetitions(qureg, nullptr, nullptr, 0, sum, angle, order, reps, onlyLeftApply);
171}
172
173void applyTrotterizedControlledPauliStrSumGadget(Qureg qureg, int control, PauliStrSum sum, qreal angle, int order, int reps) {
174 validate_quregFields(qureg, __func__);
175 validate_pauliStrSumFields(sum, __func__);
176 validate_pauliStrSumIsHermitian(sum, __func__);
177 validate_controlAndPauliStrSumTargets(qureg, control, sum, __func__);
178 validate_trotterParams(qureg, order, reps, __func__);
179
180 bool onlyLeftApply = false;
181 internal_applyAllTrotterRepetitions(qureg, &control, nullptr, 1, sum, angle, order, reps, onlyLeftApply);
182}
183
184void applyTrotterizedMultiControlledPauliStrSumGadget(Qureg qureg, int* controls, int numControls, PauliStrSum sum, qreal angle, int order, int reps) {
185 validate_quregFields(qureg, __func__);
186 validate_pauliStrSumFields(sum, __func__);
187 validate_pauliStrSumIsHermitian(sum, __func__);
188 validate_controlsAndPauliStrSumTargets(qureg, controls, numControls, sum, __func__);
189 validate_trotterParams(qureg, order, reps, __func__);
190
191 bool onlyLeftApply = false;
192 internal_applyAllTrotterRepetitions(qureg, controls, nullptr, numControls, sum, angle, order, reps, onlyLeftApply);
193}
194
195void applyTrotterizedMultiStateControlledPauliStrSumGadget(Qureg qureg, int* controls, int* states, int numControls, PauliStrSum sum, qreal angle, int order, int reps) {
196 validate_quregFields(qureg, __func__);
197 validate_pauliStrSumFields(sum, __func__);
198 validate_pauliStrSumIsHermitian(sum, __func__);
199 validate_controlsAndPauliStrSumTargets(qureg, controls, numControls, sum, __func__);
200 validate_controlStates(states, numControls, __func__); // permits states==nullptr
201 validate_trotterParams(qureg, order, reps, __func__);
202
203 bool onlyLeftApply = false;
204 internal_applyAllTrotterRepetitions(qureg, controls, states, numControls, sum, angle, order, reps, onlyLeftApply);
205}
206
207} // end de-mangler
208
209void applyTrotterizedMultiControlledPauliStrSumGadget(Qureg qureg, vector<int> controls, PauliStrSum sum, qreal angle, int order, int reps) {
210
211 applyTrotterizedMultiControlledPauliStrSumGadget(qureg, controls.data(), controls.size(), sum, angle, order, reps);
212}
213
214void applyTrotterizedMultiStateControlledPauliStrSumGadget(Qureg qureg, vector<int> controls, vector<int> states, PauliStrSum sum, qreal angle, int order, int reps) {
215 validate_controlsMatchStates(controls.size(), states.size(), __func__);
216
217 applyTrotterizedMultiStateControlledPauliStrSumGadget(qureg, controls.data(), states.data(), controls.size(), sum, angle, order, reps);
218}
219
220
221
222/*
223 * CLOSED TIME EVOLUTION
224 */
225
226extern "C" {
227
228void applyTrotterizedUnitaryTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal time, int order, int reps) {
229 validate_quregFields(qureg, __func__);
230 validate_pauliStrSumFields(hamil, __func__);
231 validate_pauliStrSumTargets(hamil, qureg, __func__);
232 validate_pauliStrSumIsHermitian(hamil, __func__);
233 validate_trotterParams(qureg, order, reps, __func__);
234
235 // exp(-i t H) = exp(x i H) | x=-t
236 qcomp angle = - time;
237 bool onlyLeftApply = false;
238 internal_applyAllTrotterRepetitions(qureg, nullptr, nullptr, 0, hamil, angle, order, reps, onlyLeftApply);
239}
240
241void applyTrotterizedImaginaryTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal tau, int order, int reps) {
242 validate_quregFields(qureg, __func__);
243 validate_pauliStrSumFields(hamil, __func__);
244 validate_pauliStrSumTargets(hamil, qureg, __func__);
245 validate_pauliStrSumIsHermitian(hamil, __func__);
246 validate_trotterParams(qureg, order, reps, __func__);
247
248 // exp(-tau H) = exp(x i H) | x=tau*i
249 qcomp angle = qcomp(0, tau);
250 bool onlyLeftApply = false;
251 internal_applyAllTrotterRepetitions(qureg, nullptr, nullptr, 0, hamil, angle, order, reps, onlyLeftApply);
252}
253
254} // end de-mangler
255
256
257
258/*
259 * OPEN TIME EVOLUTION
260 */
261
262extern "C" {
263
264void applyTrotterizedNoisyTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal* damps, PauliStrSum* jumps, int numJumps, qreal time, int order, int reps) {
265 validate_quregFields(qureg, __func__);
266 validate_quregIsDensityMatrix(qureg, __func__);
267 validate_pauliStrSumFields(hamil, __func__);
268 validate_pauliStrSumTargets(hamil, qureg, __func__);
269 validate_pauliStrSumIsHermitian(hamil, __func__);
270 validate_trotterParams(qureg, order, reps, __func__);
271 validate_lindbladJumpOps(jumps, numJumps, qureg, __func__);
272 validate_lindbladDampingRates(damps, numJumps, __func__);
273
274 qindex numSuperTerms = internal_getNumTotalSuperPropagatorTerms(hamil, jumps, numJumps); // 0 indicates overflow
275 validate_numLindbladSuperPropagatorTerms(numSuperTerms, __func__);
276
277 // validate memory allocations for all super-propagator terms
278 vector<PauliStr> superStrings;
279 vector<qcomp> superCoeffs;
280 auto callbackString = [&]() { validate_tempAllocSucceeded(false, numSuperTerms, sizeof(PauliStr), __func__); };
281 auto callbackCoeff = [&]() { validate_tempAllocSucceeded(false, numSuperTerms, sizeof(qcomp), __func__); };
282 util_tryAllocVector(superStrings, numSuperTerms, callbackString);
283 util_tryAllocVector(superCoeffs, numSuperTerms, callbackCoeff);
284
285 qindex superTermInd = 0;
286
287 // collect -i[H,rho] terms
288 for (qindex n=0; n<hamil.numTerms; n++) {
289 PauliStr oldStr = hamil.strings[n];
290 qcomp oldCoeff = hamil.coeffs[n];
291
292 // term of -i Id (x) H
293 superStrings[superTermInd] = oldStr;
294 superCoeffs [superTermInd] = -1_i * oldCoeff;
295 superTermInd++;
296
297 // term of i conj(H) (x) I
298 superStrings[superTermInd] = paulis_getShiftedPauliStr(oldStr, qureg.numQubits);
299 superCoeffs [superTermInd] = 1_i * paulis_getSignOfPauliStrConj(oldStr) * std::conj(oldCoeff);
300 superTermInd++;
301 }
302
303 // below we bind superStrings/Coeffs to a spoofed PauliStrSum to pass to paulis functions
304 PauliStrSum temp;
305 int flagForDebugSafety = -1;
306 temp.isApproxHermitian = &flagForDebugSafety;
307
308 // collect jump terms
309 for (int n=0; n<numJumps; n++) {
310
311 // damp conj(J) (x) J
312 temp.strings = &superStrings[superTermInd];
313 temp.coeffs = &superCoeffs[superTermInd];
314 temp.numTerms = jumps[n].numTerms * jumps[n].numTerms;
315 superTermInd += temp.numTerms;
316 paulis_setPauliStrSumToScaledTensorProdOfConjWithSelf(temp, damps[n], jumps[n], qureg.numQubits);
317
318 // -damp/2 I (x) (adj(J) . J)
319 temp.strings = &superStrings[superTermInd];
320 temp.coeffs = &superCoeffs[superTermInd];
321 temp.numTerms = paulis_getNumTermsInPauliStrSumProdOfAdjointWithSelf(jumps[n]);
322 superTermInd += temp.numTerms;
323 paulis_setPauliStrSumToScaledProdOfAdjointWithSelf(temp, -damps[n]/2, jumps[n]);
324
325 // -damp/2 conj(adj(J) . J) (x) I = conj(above) when damp is real
326 PauliStrSum temp2;
327 temp2.strings = &superStrings[superTermInd];
328 temp2.coeffs = &superCoeffs[superTermInd];
329 temp2.numTerms = temp.numTerms;
330 superTermInd += temp2.numTerms;
331 paulis_setPauliStrSumToShiftedConj(temp2, temp, qureg.numQubits);
332 }
333
334 // defensively check we didn't write too few (or too many, though that'd segfault
335 // above) Lindblad terms, in case the above code changes when jump ops are generalised
336 if (superTermInd != numSuperTerms)
337 error_unexpectedNumLindbladSuperpropTerms();
338
339 // pass superpropagator terms as temporary PauliStrSum
340 PauliStrSum superSum;
341 superSum.numTerms = numSuperTerms;
342 superSum.strings = superStrings.data();
343 superSum.coeffs = superCoeffs.data();
344 superSum.isApproxHermitian = nullptr; // will not be queried
345
346 // effect exp(t S) = exp(x i S) | x=-i*time, left-multiplying only
347 qcomp angle = qcomp(0, -time);
348 bool onlyLeftApply = true;
349 internal_applyAllTrotterRepetitions(qureg, nullptr, nullptr, 0, superSum, angle, order, reps, onlyLeftApply);
350}
351
352} // end de-mangler
void applyTrotterizedMultiControlledPauliStrSumGadget(Qureg qureg, int *controls, int numControls, PauliStrSum sum, qreal angle, int order, int reps)
void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps)
void applyTrotterizedNonUnitaryPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qcomp angle, int order, int reps)
void applyTrotterizedControlledPauliStrSumGadget(Qureg qureg, int control, PauliStrSum sum, qreal angle, int order, int reps)
void applyTrotterizedMultiStateControlledPauliStrSumGadget(Qureg qureg, int *controls, int *states, int numControls, PauliStrSum sum, qreal angle, int order, int reps)
void applyTrotterizedUnitaryTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal time, int order, int reps)
void applyTrotterizedNoisyTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal *damps, PauliStrSum *jumps, int numJumps, qreal time, int order, int reps)
void applyTrotterizedImaginaryTimeEvolution(Qureg qureg, PauliStrSum hamil, qreal tau, int order, int reps)
Definition qureg.h:49