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