@@ -29,7 +29,7 @@ extern PauliStr paulis_getShiftedPauliStr(PauliStr str, int pauliShift);
2929
3030void internal_applyFirstOrderTrotterRepetition (
3131 Qureg qureg, vector<int >& ketCtrls, vector<int >& braCtrls,
32- vector<int >& states, PauliStrSum sum, qcomp angle, bool reverse
32+ vector<int >& states, PauliStrSum sum, qcomp angle, bool postmultiply, bool reverse
3333) {
3434 // apply each sum term as a gadget, in forward or reverse order
3535 for (qindex i=0 ; i<sum.numTerms ; i++) {
@@ -41,9 +41,14 @@ void internal_applyFirstOrderTrotterRepetition(
4141 qcomp arg = angle * coeff;
4242 localiser_statevec_anyCtrlPauliGadget (qureg, ketCtrls, states, str, arg);
4343
44+ // term finished upon statevector
4445 if (!qureg.isDensityMatrix )
4546 continue ;
4647
48+ // Linbladian propagator is only ever pre-multiplied
49+ if (!postmultiply)
50+ continue ;
51+
4752 // effect rho -> rho dagger(i angle * sum)
4853 arg *= paulis_hasOddNumY (str) ? 1 : -1 ;
4954 str = paulis_getShiftedPauliStr (str, qureg.numQubits );
@@ -53,32 +58,32 @@ void internal_applyFirstOrderTrotterRepetition(
5358
5459void internal_applyHigherOrderTrotterRepetition (
5560 Qureg qureg, vector<int >& ketCtrls, vector<int >& braCtrls,
56- vector<int >& states, PauliStrSum sum, qcomp angle, int order
61+ vector<int >& states, PauliStrSum sum, qcomp angle, int order, bool postmultiply
5762) {
5863 if (order == 1 ) {
59- internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, angle, false );
64+ internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, angle, postmultiply, false );
6065
6166 } else if (order == 2 ) {
62- internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, angle/2 , false );
63- internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, angle/2 , true );
67+ internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, angle/2 , postmultiply, false );
68+ internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, angle/2 , postmultiply, true );
6469
6570 } else {
6671 qreal p = 1 . / (4 - std::pow (4 , 1 ./(order-1 )));
6772 qcomp a = p * angle;
6873 qcomp b = (1 -4 *p) * angle;
6974
7075 int lower = order - 2 ;
71- internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, a, lower);
72- internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, a, lower);
73- internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, b, lower);
74- internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, a, lower);
75- internal_applyFirstOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, a, lower);
76+ internal_applyHigherOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, a, lower, postmultiply); // angle -> a
77+ internal_applyHigherOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, a, lower, postmultiply );
78+ internal_applyHigherOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, b, lower, postmultiply); // angle -> b
79+ internal_applyHigherOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, a, lower, postmultiply );
80+ internal_applyHigherOrderTrotterRepetition (qureg, ketCtrls, braCtrls, states, sum, a, lower, postmultiply );
7681 }
7782}
7883
7984void internal_applyAllTrotterRepetitions (
8085 Qureg qureg, int * controls, int * states, int numControls,
81- PauliStrSum sum, qcomp angle, int order, int reps
86+ PauliStrSum sum, qcomp angle, int order, int reps, bool postmultiply
8287) {
8388 // exp(i angle sum) = identity when angle=0
8489 if (angle == qcomp (0 ,0 ))
@@ -94,7 +99,7 @@ void internal_applyAllTrotterRepetitions(
9499 // perform carefully-ordered sequence of gadgets
95100 for (int r=0 ; r<reps; r++)
96101 internal_applyHigherOrderTrotterRepetition (
97- qureg, ketCtrlsVec, braCtrlsVec, statesVec, sum, arg, order);
102+ qureg, ketCtrlsVec, braCtrlsVec, statesVec, sum, arg, order, postmultiply );
98103
99104 // / @todo
100105 // / the accuracy of Trotterisation is greatly improved by randomisation
@@ -117,7 +122,8 @@ void applyNonUnitaryTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, q
117122 validate_trotterParams (qureg, order, reps, __func__);
118123 // sum is permitted to be non-Hermitian
119124
120- internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , sum, angle, order, reps);
125+ bool postmultiply = true ;
126+ internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , sum, angle, order, reps, postmultiply);
121127}
122128
123129void applyTrotterizedPauliStrSumGadget (Qureg qureg, PauliStrSum sum, qreal angle, int order, int reps) {
@@ -127,7 +133,8 @@ void applyTrotterizedPauliStrSumGadget(Qureg qureg, PauliStrSum sum, qreal angle
127133 validate_pauliStrSumIsHermitian (sum, __func__);
128134 validate_trotterParams (qureg, order, reps, __func__);
129135
130- internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , sum, angle, order, reps);
136+ bool postmultiply = true ;
137+ internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , sum, angle, order, reps, postmultiply);
131138}
132139
133140void applyControlledTrotterizedPauliStrSumGadget (Qureg qureg, int control, PauliStrSum sum, qreal angle, int order, int reps) {
@@ -137,7 +144,8 @@ void applyControlledTrotterizedPauliStrSumGadget(Qureg qureg, int control, Pauli
137144 validate_controlAndPauliStrSumTargets (qureg, control, sum, __func__);
138145 validate_trotterParams (qureg, order, reps, __func__);
139146
140- internal_applyAllTrotterRepetitions (qureg, &control, nullptr , 1 , sum, angle, order, reps);
147+ bool postmultiply = true ;
148+ internal_applyAllTrotterRepetitions (qureg, &control, nullptr , 1 , sum, angle, order, reps, postmultiply);
141149}
142150
143151void applyMultiControlledTrotterizedPauliStrSumGadget (Qureg qureg, int * controls, int numControls, PauliStrSum sum, qreal angle, int order, int reps) {
@@ -147,7 +155,8 @@ void applyMultiControlledTrotterizedPauliStrSumGadget(Qureg qureg, int* controls
147155 validate_controlsAndPauliStrSumTargets (qureg, controls, numControls, sum, __func__);
148156 validate_trotterParams (qureg, order, reps, __func__);
149157
150- internal_applyAllTrotterRepetitions (qureg, controls, nullptr , numControls, sum, angle, order, reps);
158+ bool postmultiply = true ;
159+ internal_applyAllTrotterRepetitions (qureg, controls, nullptr , numControls, sum, angle, order, reps, postmultiply);
151160}
152161
153162void applyMultiStateControlledTrotterizedPauliStrSumGadget (Qureg qureg, int * controls, int * states, int numControls, PauliStrSum sum, qreal angle, int order, int reps) {
@@ -158,7 +167,8 @@ void applyMultiStateControlledTrotterizedPauliStrSumGadget(Qureg qureg, int* con
158167 validate_controlStates (states, numControls, __func__); // permits states==nullptr
159168 validate_trotterParams (qureg, order, reps, __func__);
160169
161- internal_applyAllTrotterRepetitions (qureg, controls, states, numControls, sum, angle, order, reps);
170+ bool postmultiply = true ;
171+ internal_applyAllTrotterRepetitions (qureg, controls, states, numControls, sum, angle, order, reps, postmultiply);
162172}
163173
164174} // end de-mangler
@@ -180,25 +190,116 @@ void applyMultiStateControlledTrotterizedPauliStrSumGadget(Qureg qureg, vector<i
180190 * CLOSED TIME EVOLUTION
181191 */
182192
183- void applyTrotterizedUnitaryTimeEvolution (Qureg qureg, PauliStrSum hamiltonian , qreal time, int order, int reps) {
193+ void applyTrotterizedUnitaryTimeEvolution (Qureg qureg, PauliStrSum hamil , qreal time, int order, int reps) {
184194 validate_quregFields (qureg, __func__);
185- validate_pauliStrSumFields (hamiltonian , __func__);
186- validate_pauliStrSumTargets (hamiltonian , qureg, __func__);
187- validate_pauliStrSumIsHermitian (hamiltonian , __func__);
195+ validate_pauliStrSumFields (hamil , __func__);
196+ validate_pauliStrSumTargets (hamil , qureg, __func__);
197+ validate_pauliStrSumIsHermitian (hamil , __func__);
188198 validate_trotterParams (qureg, order, reps, __func__);
189199
190200 // exp(-i t H) = exp(x i H) | x=-t
191- qreal angle = - time;
192- internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , hamiltonian, angle, order, reps);
201+ qcomp angle = - time;
202+ bool postmultiply = true ;
203+ internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , hamil, angle, order, reps, postmultiply);
193204}
194205
195- void applyTrotterizedImaginaryTimeEvolution (Qureg qureg, PauliStrSum hamiltonian , qreal tau, int order, int reps) {
206+ void applyTrotterizedImaginaryTimeEvolution (Qureg qureg, PauliStrSum hamil , qreal tau, int order, int reps) {
196207 validate_quregFields (qureg, __func__);
197- validate_pauliStrSumFields (hamiltonian , __func__);
198- validate_pauliStrSumTargets (hamiltonian , qureg, __func__);
208+ validate_pauliStrSumFields (hamil , __func__);
209+ validate_pauliStrSumTargets (hamil , qureg, __func__);
199210 validate_trotterParams (qureg, order, reps, __func__);
200211
201212 // exp(-tau H) = exp(x i H) | x=tau*i
202213 qcomp angle = qcomp (0 , tau);
203- internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , hamiltonian, angle, order, reps);
214+ bool postmultiply = true ;
215+ internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , hamil, angle, order, reps, postmultiply);
216+ }
217+
218+
219+
220+ /*
221+ * OPEN TIME EVOLUTION
222+ */
223+
224+ extern PauliStr paulis_getShiftedPauliStr (PauliStr str, int pauliShift);
225+ extern PauliStr paulis_getKetAndBraPauliStr (PauliStr str, Qureg qureg);
226+
227+ extern " C" {
228+
229+ void applyTrotterizedPauliNoisyTimeEvolution (Qureg qureg, PauliStrSum hamil, qreal* damps, PauliStr* jumps, int numJumps, qreal time, int order, int reps) {
230+ validate_quregFields (qureg, __func__);
231+ validate_quregIsDensityMatrix (qureg, __func__);
232+ validate_pauliStrSumFields (hamil, __func__);
233+ validate_pauliStrSumTargets (hamil, qureg, __func__);
234+ validate_pauliStrSumIsHermitian (hamil, __func__);
235+ validate_trotterParams (qureg, order, reps, __func__);
236+
237+ // TODO: validate numJumps
238+
239+ // TODO; validate damps (looped); must be non-negative (0 legally disables one)
240+
241+ for (int n=0 ; n<numJumps; n++)
242+ validate_pauliStrTargets (qureg, jumps[n], __func__);
243+
244+ // when all jump operators are Paulis, the linblad superop simplifies to:
245+ // L = -i (Id (x) H - conj(H) (x) I) + sum_k gamma_k conj(L_k) (x) L_k - (sum_k gamma_k) Id
246+ // where the final term commutes with everything and can just be brought out the front of
247+ // the Trotter circuit; it becomes just a final scalar factor upon the state.
248+ //
249+ // So we need merely prepare a new PauliStrSum which contains
250+ // - old hamil times -i
251+ // - conj and shifted hamil times -i
252+ // - gamma_k conj(L_k) (x) L_k = +- gamma_k L_k (x) L_k depending on L_k Y parity
253+ // then after effecting that, scale the state
254+
255+ vector<PauliStr> newStrings;
256+ vector<qcomp> newCoeffs;
257+
258+ // premature optimisation
259+ qindex numNewTerms = 2 * hamil.numTerms + numJumps;
260+ newStrings.reserve (numNewTerms);
261+ newCoeffs.reserve (numNewTerms);
262+
263+ // collect -i[H,rho] terms
264+ for (qindex n=0 ; n<hamil.numTerms ; n++) {
265+ PauliStr oldStr = hamil.strings [n];
266+ qcomp oldCoeff = hamil.coeffs [n];
267+
268+ // term of -i Id (x) H
269+ newStrings.push_back (oldStr);
270+ newCoeffs.push_back (-1_i * oldCoeff);
271+
272+ // term of i conj(H) (x) I
273+ newStrings.push_back (paulis_getShiftedPauliStr (oldStr, qureg.numQubits ));
274+ newCoeffs.push_back (1_i * (paulis_hasOddNumY (oldStr) ? -1 : 1 ) * std::conj (oldCoeff));
275+ }
276+
277+ // collect jump terms
278+ for (int n=0 ; n<numJumps; n++) {
279+
280+ // gamma_k conj(L_k) (x) L_k
281+ newStrings.push_back (paulis_getKetAndBraPauliStr (jumps[n], qureg));
282+ newCoeffs.push_back (damps[n] * (paulis_hasOddNumY (jumps[n]) ? -1 : 1 ));
283+ }
284+
285+ // spoof a PauliStrSum to avoid superfluous alloc
286+ PauliStrSum temp;
287+ temp.numTerms = numNewTerms;
288+ temp.strings = newStrings.data ();
289+ temp.coeffs = newCoeffs.data ();
290+ temp.isApproxHermitian = nullptr ; // will not be queried
291+
292+ // effect exp(t S) = exp(x i S) | x=-i*time, premultiplying only
293+ qcomp angle = qcomp (0 , -time);
294+ bool postmultiply = false ;
295+ internal_applyAllTrotterRepetitions (qureg, nullptr , nullptr , 0 , temp, angle, order, reps, postmultiply);
296+
297+ // scale by exp(- time sum_k gamma_k)
298+ qreal dampSum = 0 ;
299+ for (int n=0 ; n<numJumps; n++)
300+ dampSum += damps[n];
301+ qcomp fac = std::exp (- time * dampSum);
302+ localiser_statevec_setQuregToSuperposition (fac, qureg, 0 , qureg, 0 , qureg);
303+ }
304+
204305}
0 commit comments