microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billti/qdk_package

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

library/chemistry/src/JordanWigner/EvolutionSet.qs

259lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4export JWGeneratorSystem;
5export JWFermionEvolutionSet;
6
7import Std.Arrays.IndexRange;
8import Std.Arrays.Subarray;
9
10import JordanWigner.Data.JWOptimizedHTerms;
11import Generators.GeneratorIndex;
12import Generators.GeneratorSystem;
13import Generators.HTermsToGenSys;
14import Utils.IsNotZero;
15
16// This evolution set runs off data optimized for a Jordan–Wigner encoding.
17// This collects terms Z, ZZ, PQandPQQR, hpqrs separately.
18// This only apples the needed hpqrs XXXX XXYY terms.
19// Operations here are expressed in terms of Exp([...])
20
21// Convention for GeneratorIndex = ((Int[],Double[]), Int[])
22// We index single Paulis as 0 for I, 1 for X, 2 for Y, 3 for Z.
23// We index Pauli strings with arrays of integers e.g. a = [3,1,1,2] for ZXXY.
24// We assume the zeroth element of Double[] is the angle of rotation
25// We index the qubits that Pauli strings act on with arrays of integers e.g. q = [2,4,5,8] for Z_2 X_4 X_5, Y_8
26// An example of a Pauli string GeneratorIndex is thus ((a,b), q)
27
28/// # Summary
29/// Converts a Hamiltonian described by `JWOptimizedHTerms`
30/// to a `GeneratorSystem` expressed in terms of the
31/// `GeneratorIndex` convention defined in this file.
32///
33/// # Input
34/// ## data
35/// Description of Hamiltonian in `JWOptimizedHTerms` format.
36///
37/// # Output
38/// Representation of Hamiltonian as `GeneratorSystem`.
39function JWGeneratorSystem(data : JWOptimizedHTerms) : GeneratorSystem {
40 let ZData = data.HTerm0;
41 let ZZData = data.HTerm1;
42 let PQandPQQRData = data.HTerm2;
43 let h0123Data = data.HTerm3;
44 let ZGenSys = HTermsToGenSys(ZData, [0]);
45 let ZZGenSys = HTermsToGenSys(ZZData, [1]);
46 let PQandPQQRGenSys = HTermsToGenSys(PQandPQQRData, [2]);
47 let h0123GenSys = HTermsToGenSys(h0123Data, [3]);
48 let sum = AddGeneratorSystems(ZGenSys, ZZGenSys);
49 let sum = AddGeneratorSystems(sum, PQandPQQRGenSys);
50 return AddGeneratorSystems(sum, h0123GenSys);
51}
52
53/// # Summary
54/// Represents a dynamical generator as a set of simulatable gates and an
55/// expansion in the JordanWigner basis.
56///
57/// # Output
58/// An evolution set function that maps a `GeneratorIndex` for the JordanWigner basis to
59/// an evolution unitary operation.
60function JWFermionEvolutionSet() : GeneratorIndex -> (Double, Qubit[]) => Unit is Adj + Ctl {
61 generatorIndex -> (stepSize, qubits) => JWFermionEvolutionSetImpl(generatorIndex, stepSize, qubits)
62}
63
64/// # Summary
65/// Represents a dynamical generator as a set of simulatable gates and an
66/// expansion in the JordanWigner basis.
67///
68/// # Input
69/// ## generatorIndex
70/// A generator index to be represented as unitary evolution in the JordanWigner.
71/// ## stepSize
72/// A multiplier on the duration of time-evolution by the term referenced
73/// in `generatorIndex`.
74/// ## qubits
75/// Register acted upon by time-evolution operator.
76operation JWFermionEvolutionSetImpl(generatorIndex : GeneratorIndex, stepSize : Double, qubits : Qubit[]) : Unit is Adj + Ctl {
77 let (idxTermType, idxDoubles) = generatorIndex.Term;
78 let termType = idxTermType[0];
79
80 if (termType == 0) {
81 ApplyZTerm(generatorIndex, stepSize, qubits);
82 } elif (termType == 1) {
83 ApplyZZTerm(generatorIndex, stepSize, qubits);
84 } elif (termType == 2) {
85 ApplyPQandPQQRTerm(generatorIndex, stepSize, qubits);
86 } elif (termType == 3) {
87 Apply0123Term(generatorIndex, stepSize, qubits);
88 }
89}
90
91/// # Summary
92/// Adds two `GeneratorSystem`s to create a new `GeneratorSystem`.
93///
94/// # Input
95/// ## generatorSystemA
96/// The first `GeneratorSystem`.
97/// ## generatorSystemB
98/// The second `GeneratorSystem`.
99///
100/// # Output
101/// A `GeneratorSystem` representing a system that is the sum of the
102/// input generator systems.
103function AddGeneratorSystems(generatorSystemA : GeneratorSystem, generatorSystemB : GeneratorSystem) : GeneratorSystem {
104 let nTermsA = generatorSystemA.NumEntries;
105 let nTermsB = generatorSystemB.NumEntries;
106 let generatorIndexFunction = idx -> {
107 if idx < nTermsA {
108 return generatorSystemA.EntryAt(idx);
109 } else {
110 return generatorSystemB.EntryAt(idx - nTermsA);
111 }
112 };
113 return new GeneratorSystem { NumEntries = nTermsA + nTermsB, EntryAt = generatorIndexFunction };
114}
115
116// Consider the Hamiltonian H = 0.1 XI + 0.2 IX + 0.3 ZY
117// Its GeneratorTerms are (([1],b),[0]), 0.1), (([1],b),[1]), 0.2), (([3,2],b),[0,1]), 0.3).
118
119/// # Summary
120/// Applies time-evolution by a Z term described by a `GeneratorIndex`.
121///
122/// # Input
123/// ## term
124/// `GeneratorIndex` representing a Z term.
125/// ## stepSize
126/// Duration of time-evolution.
127/// ## qubits
128/// Qubits of Hamiltonian.
129operation ApplyZTerm(term : GeneratorIndex, stepSize : Double, qubits : Qubit[]) : Unit is Adj + Ctl {
130 let (_, coeff) = term.Term;
131 let angle = (1.0 * coeff[0]) * stepSize;
132 let qubit = qubits[term.Subsystem[0]];
133 Exp([PauliZ], angle, [qubit]);
134}
135
136/// # Summary
137/// Applies time-evolution by a ZZ term described by a `GeneratorIndex`.
138///
139/// # Input
140/// ## term
141/// `GeneratorIndex` representing a ZZ term.
142/// ## stepSize
143/// Duration of time-evolution.
144/// ## qubits
145/// Qubits of Hamiltonian.
146operation ApplyZZTerm(term : GeneratorIndex, stepSize : Double, qubits : Qubit[]) : Unit is Adj + Ctl {
147 let (_, coeff) = term.Term;
148 let angle = (1.0 * coeff[0]) * stepSize;
149 let qubitsZZ = Subarray(term.Subsystem[0..1], qubits);
150 Exp([PauliZ, PauliZ], angle, qubitsZZ);
151}
152
153/// # Summary
154/// Applies time-evolution by a PQ term described by a `GeneratorIndex`.
155///
156/// # Input
157/// ## term
158/// `GeneratorIndex` representing a PQ term.
159/// ## stepSize
160/// Duration of time-evolution.
161/// ## extraParityQubits
162/// Optional parity qubits that flip the sign of time-evolution.
163/// ## qubits
164/// Qubits of Hamiltonian.
165operation ApplyPQTerm(
166 term : GeneratorIndex,
167 stepSize : Double,
168 extraParityQubits : Qubit[],
169 qubits : Qubit[]
170) : Unit is Adj + Ctl {
171 let (_, coeff) = term.Term;
172 let idxFermions = term.Subsystem;
173 let angle = (1.0 * coeff[0]) * stepSize;
174 let qubitsPQ = Subarray(idxFermions[0..1], qubits);
175 let qubitsJW = qubits[idxFermions[0] + 1..idxFermions[1] - 1];
176 let ops = [[PauliX, PauliX], [PauliY, PauliY]];
177 let padding = Repeated(PauliZ, Length(qubitsJW) + Length(extraParityQubits));
178
179 for op in ops {
180 Exp(op + padding, angle, qubitsPQ + qubitsJW + extraParityQubits);
181 }
182}
183
184/// # Summary
185/// Applies time-evolution by a PQ or PQQR term described by a `GeneratorIndex`.
186///
187/// # Input
188/// ## term
189/// `GeneratorIndex` representing a PQ or PQQR term.
190/// ## stepSize
191/// Duration of time-evolution.
192/// ## qubits
193/// Qubits of Hamiltonian.
194operation ApplyPQandPQQRTerm(term : GeneratorIndex, stepSize : Double, qubits : Qubit[]) : Unit is Adj + Ctl {
195 let (idxTermType, coeff) = term.Term;
196 let idxFermions = term.Subsystem;
197 let angle = (1.0 * coeff[0]) * stepSize;
198 let qubitQidx = idxFermions[1];
199
200 // For all cases, do the same thing:
201 // p < r < q (1/4)(1-Z_q)(Z_{r-1,p+1})(X_p X_r + Y_p Y_r) (same as Hermitian conjugate of r < p < q)
202 // q < p < r (1/4)(1-Z_q)(Z_{r-1,p+1})(X_p X_r + Y_p Y_r)
203 // p < q < r (1/4)(1-Z_q)(Z_{r-1,p+1})(X_p X_r + Y_p Y_r)
204
205 // This amounts to applying a PQ term, followed by same PQ term after a CNOT from q to the parity bit.
206 if Length(idxFermions) == 2 {
207 let termPR0 = new GeneratorIndex { Term = (idxTermType, [1.0]), Subsystem = idxFermions };
208 ApplyPQTerm(termPR0, angle, [], qubits);
209 } else {
210 if idxFermions[0] < qubitQidx and qubitQidx < idxFermions[3] {
211 let termPR1 = new GeneratorIndex { Term = (idxTermType, [1.0]), Subsystem = [idxFermions[0], idxFermions[3] - 1] };
212 let excludingQ = if qubitQidx > 0 { qubits[0..qubitQidx-1] + qubits[qubitQidx + 1...] } else { qubits[1...] };
213 ApplyPQTerm(termPR1, angle, [], excludingQ);
214 } else {
215 let termPR1 = new GeneratorIndex { Term = (idxTermType, [1.0]), Subsystem = [0, idxFermions[3] - idxFermions[0]] };
216 ApplyPQTerm(termPR1, angle, [qubits[qubitQidx]], qubits[idxFermions[0]..idxFermions[3]]);
217 }
218 }
219}
220
221/// # Summary
222/// Applies time-evolution by a PQRS term described by a given index.
223///
224/// # Input
225/// ## term
226/// The index representing a PQRS term to be applied.
227/// ## stepSize
228/// Duration of time-evolution.
229/// ## qubits
230/// Qubits to apply the given term to.
231operation Apply0123Term(term : GeneratorIndex, stepSize : Double, qubits : Qubit[]) : Unit is Adj + Ctl {
232 let (idxTermType, v0123) = term.Term;
233 let idxFermions = term.Subsystem;
234 let angle = stepSize;
235 let qubitsPQ = Subarray(idxFermions[0..1], qubits);
236 let qubitsRS = Subarray(idxFermions[2..3], qubits);
237 let qubitsPQJW = qubits[idxFermions[0] + 1..idxFermions[1] - 1];
238 let qubitsRSJW = qubits[idxFermions[2] + 1..idxFermions[3] - 1];
239 let ops = [
240 [PauliX, PauliX, PauliX, PauliX],
241 [PauliX, PauliX, PauliY, PauliY],
242 [PauliX, PauliY, PauliX, PauliY],
243 [PauliY, PauliX, PauliX, PauliY],
244 [PauliY, PauliY, PauliY, PauliY],
245 [PauliY, PauliY, PauliX, PauliX],
246 [PauliY, PauliX, PauliY, PauliX],
247 [PauliX, PauliY, PauliY, PauliX]
248 ];
249
250 for idxOp in IndexRange(ops) {
251 if (IsNotZero(v0123[idxOp % 4])) {
252 Exp(
253 ops[idxOp] + Repeated(PauliZ, Length(qubitsPQJW) + Length(qubitsRSJW)),
254 angle * v0123[idxOp % 4],
255 ((qubitsPQ + qubitsRS) + qubitsPQJW) + qubitsRSJW
256 );
257 }
258 }
259}
260