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/ClusterOperatorEvolutionSet.qs

280lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4export JWClusterOperatorEvolutionSet;
5export JWClusterOperatorGeneratorSystem;
6
7import Std.Arrays.IndexRange;
8import Std.Math.Max;
9import Std.Math.Min;
10
11import Generators.GeneratorIndex;
12import Generators.GeneratorSystem;
13import JordanWigner.Data.JWInputState;
14
15/// # Summary
16/// Computes Z component of Jordan–Wigner string between
17/// fermion indices in a fermionic operator with an even
18/// number of creation / annihilation operators.
19///
20/// # Input
21/// ## nFermions
22/// The Number of fermions in the system.
23/// ## idxFermions
24/// fermionic operator indices.
25///
26/// # Output
27/// Bitstring `Bool[]` that is `true` where a `PauliZ` should be applied.
28///
29/// # Example
30/// ```qsharp
31/// let bitString = ComputeJWBitString(5, [0, 3]) ;
32/// // bitString is [false, true, true, false, false].
33/// ```
34function ComputeJWBitString(nFermions : Int, idxFermions : Int[]) : Bool[] {
35 if Length(idxFermions) % 2 != 0 {
36 fail $"ComputeJordanWignerString failed. `idxFermions` must contain an even number of terms.";
37 }
38
39 mutable zString = Repeated(false, nFermions);
40 for fermionIdx in idxFermions {
41 if fermionIdx >= nFermions {
42 fail $"ComputeJordanWignerString failed. fermionIdx {fermionIdx} out of range.";
43 }
44 // NOTE: This could be optimized
45 for idx in 0..fermionIdx {
46 zString w/= idx <- not zString[idx];
47 }
48 }
49
50 for fermionIdx in idxFermions {
51 zString w/= fermionIdx <- false;
52 }
53 return zString;
54}
55
56// Identical to `ComputeJWBitString`, except with the map
57// false -> PauliI and true -> PauliZ
58function ComputeJWPauliZString(nFermions : Int, idxFermions : Int[]) : Pauli[] {
59 let bitString = ComputeJWBitString(nFermions, idxFermions);
60 mutable pauliString = Repeated(PauliI, Length(bitString));
61 for idx in IndexRange(bitString) {
62 if bitString[idx] {
63 pauliString w/= idx <- PauliZ;
64 }
65 }
66 return pauliString;
67}
68
69// Identical to `ComputeJWPauliZString`, except that some
70// specified elements are substituted.
71function ComputeJWPauliString(
72 nFermions : Int,
73 idxFermions : Int[],
74 pauliReplacements : Pauli[]
75) : Pauli[] {
76
77 mutable pauliString = ComputeJWPauliZString(nFermions, idxFermions);
78
79 for idx in IndexRange(idxFermions) {
80 let idxFermion = idxFermions[idx];
81 let op = pauliReplacements[idx];
82 pauliString w/= idxFermion <- op;
83 }
84
85 return pauliString;
86}
87
88/// # Summary
89/// Applies time-evolution by a cluster operator PQ term described by a `GeneratorIndex`.
90///
91/// # Input
92/// ## term
93/// `GeneratorIndex` representing a cluster operator PQ term.
94/// ## stepSize
95/// Duration of time-evolution.
96/// ## qubits
97/// Qubits of Hamiltonian.
98operation ApplyJWClusterOperatorPQTerm(
99 term : GeneratorIndex,
100 stepSize : Double,
101 qubits : Qubit[]
102) : Unit is Adj + Ctl {
103
104 let (_, coeff) = term.Term;
105 let idxFermions = term.Subsystem;
106 let p = idxFermions[0];
107 let q = idxFermions[1];
108 if p == q {
109 fail $"Unitary coupled-cluster PQ failed: indices {p}, {q} must be distinct";
110 }
111 let angle = 0.5 * coeff[0] * stepSize;
112 let ops = [[PauliX, PauliY], [PauliY, PauliX]];
113 let signs = [+ 1.0, -1.0];
114
115 for i in IndexRange(ops) {
116 let pauliString = ComputeJWPauliString(Length(qubits), idxFermions, ops[i]);
117 Exp(pauliString, signs[i] * angle, qubits);
118 }
119}
120
121/// # Summary
122/// Applies time-evolution by a cluster operator PQRS term described by a `GeneratorIndex`.
123///
124/// # Input
125/// ## term
126/// `GeneratorIndex` representing a cluster operator PQRS term.
127/// ## stepSize
128/// Duration of time-evolution.
129/// ## qubits
130/// Qubits of Hamiltonian.
131operation ApplyJWClusterOperatorPQRSTerm(
132 term : GeneratorIndex,
133 stepSize : Double,
134 qubits : Qubit[]
135) : Unit is Adj + Ctl {
136
137 let (_, coeff) = term.Term;
138 let idxFermions = term.Subsystem;
139 let p = idxFermions[0];
140 let q = idxFermions[1];
141 let r = idxFermions[2];
142 let s = idxFermions[3];
143 let angle = 0.125 * coeff[0] * stepSize;
144
145 if p == q or p == r or p == s or q == r or q == s or r == s {
146 fail ($"Unitary coupled-cluster PQRS failed: indices {p}, {q}, {r}, {s} must be distinct");
147 }
148
149 let x = PauliX;
150 let y = PauliY;
151
152 let ops = [[y, y, x, y], [x, x, x, y], [x, y, y, y], [y, x, y, y], [x, y, x, x], [y, x, x, x], [y, y, y, x], [x, x, y, x]];
153 let (sortedIndices, signs, globalSign) = JWClusterOperatorPQRSTermSigns([p, q, r, s]);
154
155 for i in IndexRange(ops) {
156 let pauliString = ComputeJWPauliString(Length(qubits), sortedIndices, ops[i]);
157 Exp(pauliString, globalSign * signs[i] * angle, qubits);
158 }
159}
160
161function JWClusterOperatorPQRSTermSigns(indices : Int[]) : (Int[], Double[], Double) {
162 let p = indices[0];
163 let q = indices[1];
164 let r = indices[2];
165 let s = indices[3];
166 mutable sorted = [0, 0, 0, 0];
167 mutable signs = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
168 mutable sign = 1.0;
169
170 if (p > q) {
171 sign = sign * -1.0;
172 }
173 if (r > s) {
174 sign = sign * -1.0;
175 }
176 if (Min([p, q]) > Min([r, s])) {
177 sign = sign * -1.0;
178 sorted = [Min([r, s]), Max([r, s]), Min([p, q]), Max([p, q])];
179 } else {
180 sorted = [Min([p, q]), Max([p, q]), Min([r, s]), Max([r, s])];
181 }
182 // sorted is now in the order
183 // [p`,q`,r`,s`], where p`<q`; r`<s`, and Min(p`,q`) is smaller than Min(r`,s`).
184
185 let p1 = sorted[0];
186 let q1 = sorted[1];
187 let r1 = sorted[2];
188 let s1 = sorted[3];
189 // Case (p,q) < (r,s) and (p,q) > (r,s)
190 if (q1 < r1) {
191 // p1 < q1 < r1 < s1
192 return ([p1, q1, r1, s1], [1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0], sign);
193 }
194 // Case interleaved
195 elif (q1 > r1 and q1 < s1) {
196 // p1 < r1 < q1 < s1
197 return ([p1, r1, q1, s1], [-1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0], sign);
198 }
199 // Case contained
200 elif (q1 > r1 and q1 > s1) {
201 // p1 < r1 < s1 < q1
202 return ([p1, r1, s1, q1], [1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0], sign);
203 } else {
204 fail ("Completely invalid cluster operator specified.");
205 }
206}
207
208/// # Summary
209/// Converts a Hamiltonian described by `JWOptimizedHTerms`
210/// to a `GeneratorSystem` expressed in terms of the
211/// `GeneratorIndex` convention defined in this file.
212///
213/// # Input
214/// ## data
215/// Description of Hamiltonian in `JWOptimizedHTerms` format.
216///
217/// # Output
218/// Representation of Hamiltonian as `GeneratorSystem`.
219function JWClusterOperatorGeneratorSystem(
220 data : JWInputState[]
221) : GeneratorSystem {
222 new GeneratorSystem {
223 NumEntries = Length(data),
224 EntryAt = JWStateAsGeneratorIndex(data, _)
225 }
226}
227
228function JWStateAsGeneratorIndex(data : JWInputState[], idx : Int) : GeneratorIndex {
229 let real = data[idx].Amplitude.Real;
230 let idxFermions = data[idx].FermionIndices;
231
232 if Length(idxFermions) == 2 {
233 // PQ term
234 new GeneratorIndex { Term = ([0], [real]), Subsystem = idxFermions }
235 } elif Length(idxFermions) == 4 {
236 // PQRS term
237 new GeneratorIndex { Term = ([2], [real]), Subsystem = idxFermions }
238 } else {
239 // Any other term in invalid
240 new GeneratorIndex { Term = ([-1], [0.0]), Subsystem = [0] }
241 }
242}
243
244/// # Summary
245/// Represents a dynamical generator as a set of simulatable gates and an
246/// expansion in the JordanWigner basis.
247///
248/// # Input
249/// ## generatorIndex
250/// A generator index to be represented as unitary evolution in the JordanWigner.
251/// ## stepSize
252/// Dummy variable to match signature of simulation algorithms.
253/// ## qubits
254/// Register acted upon by time-evolution operator.
255operation JWClusterOperatorImpl(
256 generatorIndex : GeneratorIndex,
257 stepSize : Double,
258 qubits : Qubit[]
259) : Unit is Adj + Ctl {
260
261 let (idxTermType, _) = generatorIndex.Term;
262 let termType = idxTermType[0];
263
264 if termType == 0 {
265 ApplyJWClusterOperatorPQTerm(generatorIndex, stepSize, qubits);
266 } elif termType == 2 {
267 ApplyJWClusterOperatorPQRSTerm(generatorIndex, stepSize, qubits);
268 }
269}
270
271/// # Summary
272/// Represents a dynamical generator as a set of simulatable gates and an
273/// expansion in the JordanWigner basis.
274///
275/// # Output
276/// An evolution set function that maps a `GeneratorIndex` for the JordanWigner basis to
277/// an evolution unitary operation.
278function JWClusterOperatorEvolutionSet() : GeneratorIndex -> (Double, Qubit[]) => Unit is Adj + Ctl {
279 generatorIndex -> (stepSize, qubits) => JWClusterOperatorImpl(generatorIndex, stepSize, qubits)
280}
281