Skip to content

Commit ca1bc18

Browse files
DmitryVasilevskyDmitry Vasilevsky
and
Dmitry Vasilevsky
authored
Added simple VQE sample (#2073)
This PR adds simple self-contained example of a Variational Quantum Eigensolver (VQE). This example includes: 1. Simple classical optimization to find minimum of a multi-variable function in order to find the approximation to the minimum eigenvalue of a Hamiltonian. 2. Finding Hamiltonian expectation value as a weighted sum of terms. 3. Finding one term expectation value by performing multiple shots of preparing ansatz state and measuring it. 4. Ansatz state preparation similar to the circuit in the published paper "Ground-state energy estimation of the water molecule on a trapped ion quantum computer" (https://arxiv.org/abs/1902.10171) To keep this sample simple and self-contained Hamiltonian is artificial (doesn't correspond to any actual chemistry problem). Also, this sample doesn't include any application of VQE, for example, finding a geometry of a molecule. --------- Co-authored-by: Dmitry Vasilevsky <[email protected]>
1 parent 5f5546f commit ca1bc18

File tree

2 files changed

+216
-0
lines changed

2 files changed

+216
-0
lines changed

samples/algorithms/SimpleVQE.qs

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/// # Sample
2+
/// Simplified Sample of a Variational Quantum Eigensolver
3+
///
4+
/// # Description
5+
/// This is an example of a Variational Quantum Eigensolver (VQE).
6+
/// This example includes:
7+
/// 1. Simple classical optimization to find minimum of a multi-variable function
8+
/// in order to find an approximation to the minimum eigenvalue of a Hamiltonian
9+
/// 2. Finding Hamiltonian expectation value as a weighted sum of terms.
10+
/// 3. Finding one term expectation value by performing multiple shots.
11+
/// 4. Ansatz state preparation similar to the circuit in the referenced paper.
12+
/// To keep this sample simple Hamiltonian terms are generated randomly.
13+
///
14+
/// # Reference
15+
/// Ground-state energy estimation of the water molecule on a trapped ion quantum
16+
/// computer by Yunseong Nam et al., 2019. https://arxiv.org/abs/1902.10171
17+
18+
import Std.Arrays.IsEmpty;
19+
import Std.Arrays.IndexRange;
20+
import Std.Convert.IntAsDouble;
21+
import Std.Diagnostics.Fact;
22+
import Std.Math.AbsD;
23+
import Std.Math.PI;
24+
25+
/// # Summary
26+
/// Find the approximation to the minimum eigenvalue of a Hamiltonian by applying VQE
27+
operation Main() : Double {
28+
29+
// Find the approximation to the minimum eigenvalue of a Hamiltonian
30+
// by varying ansatz parameters to minimize its expectation value.
31+
SimpleDescent(
32+
// Use a number of shots when estimating Hamiltonian terms
33+
// Actual VQE implementations may require very large number of shots.
34+
FindHamiltonianExpectationValue(_, 100),
35+
// Start from these angles for ansatz state preparation
36+
[1.0, 1.0],
37+
// Initial step to search for minimum
38+
0.5,
39+
// Stop optimization if step is 0
40+
0.0,
41+
// Stop optimization after several attempts.
42+
// Actual VQE would need to make enough iterations
43+
// to find energy with sufficient chemical accuracy.
44+
50
45+
)
46+
}
47+
48+
/// # Summary
49+
/// Find expectation value of a Hamiltonian given parameters for the
50+
/// ansatz state and number of shots to evaluate each term.
51+
/// Different VQE applications will have different measurements and
52+
/// coefficients depending on the Hamiltonian being evaluated.
53+
operation FindHamiltonianExpectationValue(thetas : Double[], shots : Int) : Double {
54+
let terms = [
55+
([PauliZ, PauliI, PauliI, PauliI], 0.16),
56+
([PauliI, PauliI, PauliZ, PauliI], -0.25),
57+
([PauliZ, PauliZ, PauliI, PauliI], 0.17),
58+
([PauliI, PauliI, PauliZ, PauliZ], 0.45),
59+
([PauliX, PauliX, PauliX, PauliX], 0.2),
60+
([PauliY, PauliY, PauliY, PauliY], 0.1),
61+
([PauliY, PauliX, PauliX, PauliY], -0.02),
62+
([PauliX, PauliY, PauliY, PauliX], -0.22),
63+
];
64+
mutable value = 0.0;
65+
for (basis, coefficient) in terms {
66+
value += coefficient * FindTermExpectationValue(thetas, basis, shots);
67+
}
68+
value
69+
}
70+
71+
/// # Summary
72+
/// Find expectation value of a Hamiltonian term given parameters for the
73+
/// ansatz state, measurement basis and number of shots to evaluate each term.
74+
operation FindTermExpectationValue(
75+
thetas : Double[],
76+
pauliBasis : Pauli[],
77+
shots : Int
78+
) : Double {
79+
80+
mutable zeroCount = 0;
81+
for _ in 1..shots {
82+
use qs = Qubit[4];
83+
PrepareAnsatzState(qs, thetas);
84+
if Measure(pauliBasis, qs) == Zero {
85+
zeroCount += 1;
86+
}
87+
ResetAll(qs);
88+
}
89+
IntAsDouble(zeroCount) / IntAsDouble(shots)
90+
}
91+
92+
/// # Summary
93+
/// Prepare the ansatz state for given parameters on a qubit register
94+
/// This is an example of ansatz state preparation similar to the
95+
/// unitary couple clustered method used in the referenced paper.
96+
/// Actual VQE application will have different ansatz preparation operations.
97+
operation PrepareAnsatzState(qs : Qubit[], thetas : Double[]) : Unit {
98+
BosonicExitationTerm(thetas[0], qs[0], qs[2]);
99+
CNOT(qs[0], qs[1]);
100+
NonBosonicExitataionTerm(thetas[1], qs[0], qs[1], qs[2], qs[3]);
101+
}
102+
103+
/// # Summary
104+
/// Bosonic exitation circuit from the referenced paper.
105+
operation BosonicExitationTerm(
106+
theta : Double,
107+
moX : Qubit,
108+
moY : Qubit
109+
) : Unit {
110+
X(moX);
111+
Adjoint S(moX);
112+
Rxx(theta, moX, moY);
113+
S(moX);
114+
Adjoint S(moY);
115+
Rxx(-theta, moX, moY);
116+
S(moY);
117+
}
118+
119+
/// # Summary
120+
/// Non-bosonic exitation circuit from the referenced paper.
121+
operation NonBosonicExitataionTerm(
122+
theta : Double,
123+
moXsoX : Qubit,
124+
moXsoY : Qubit,
125+
moYsoX : Qubit,
126+
moYsoY : Qubit
127+
) : Unit {
128+
Adjoint S(moXsoX);
129+
within {
130+
CNOT(moXsoX, moYsoY);
131+
CNOT(moXsoX, moYsoX);
132+
CNOT(moXsoX, moXsoY);
133+
H(moXsoX);
134+
Rz(theta, moXsoX);
135+
CNOT(moXsoY, moXsoX);
136+
Rz(theta, moXsoX);
137+
CNOT(moYsoY, moXsoX);
138+
Rz(-theta, moXsoX);
139+
CNOT(moXsoY, moXsoX);
140+
Rz(-theta, moXsoX);
141+
} apply {
142+
Adjoint S(moYsoX);
143+
CNOT(moYsoX, moXsoX);
144+
}
145+
S(moYsoX);
146+
}
147+
148+
/// # Summary
149+
/// Simple classical optimizer. A descent to a local minimum of function `f`.
150+
/// Tries to takes steps in all directions and proceeds if the new point is better.
151+
/// If no moves result in function value improvement the step size is halved.
152+
/// Actual VQE implementations use more elaborate optimizers.
153+
operation SimpleDescent(
154+
f : Double[] => Double,
155+
initialPoint : Double[],
156+
initialStep : Double,
157+
minimalStep : Double,
158+
attemptLimit : Int
159+
) : Double {
160+
Fact(not IsEmpty(initialPoint), "Argument array must contain elements.");
161+
Fact(initialStep > 0.0, "Initial step must be positive.");
162+
Fact(minimalStep >= 0.0, "Minimal step must be non-negative.");
163+
164+
mutable bestPoint = initialPoint;
165+
mutable bestValue = f(bestPoint);
166+
mutable currentStep = initialStep;
167+
mutable currentAttempt = 0;
168+
169+
Message($"Beginning descent from value {bestValue}.");
170+
171+
while (currentAttempt < attemptLimit) and (currentStep > minimalStep) {
172+
mutable hadImprovement = false;
173+
for i in IndexRange(initialPoint) {
174+
let nextPoint = bestPoint w/ i <- bestPoint[i] + currentStep;
175+
let nextValue = f(nextPoint); // Evaluate quantum part
176+
currentAttempt = currentAttempt + 1;
177+
if nextValue < bestValue {
178+
hadImprovement = true;
179+
bestValue = nextValue;
180+
bestPoint = nextPoint;
181+
Message($"Value improved to {bestValue}.");
182+
}
183+
let nextPoint = bestPoint w/ i <- bestPoint[i] - currentStep;
184+
let nextValue = f(nextPoint); // Evaluate quantum part
185+
currentAttempt = currentAttempt + 1;
186+
if nextValue < bestValue {
187+
hadImprovement = true;
188+
bestValue = nextValue;
189+
bestPoint = nextPoint;
190+
Message($"Value improved to {bestValue}.");
191+
}
192+
}
193+
194+
if not hadImprovement {
195+
currentStep = currentStep / 2.0;
196+
}
197+
}
198+
Message($"Descent done. Attempts: {currentAttempt}, Step: {currentStep}, Arguments: {bestPoint}, Value: {bestValue}.");
199+
bestValue
200+
}

samples_test/src/tests/algorithms.rs

+16
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,22 @@ pub const SIMPLEISING_EXPECT: Expect =
258258
expect!["[Zero, Zero, Zero, One, One, Zero, One, One, Zero]"];
259259
pub const SIMPLEISING_EXPECT_DEBUG: Expect =
260260
expect!["[Zero, Zero, Zero, One, One, Zero, One, One, Zero]"];
261+
pub const SIMPLEVQE_EXPECT: Expect = expect![[r#"
262+
Beginning descent from value 0.43300000000000005.
263+
Value improved to 0.35300000000000004.
264+
Value improved to 0.3454.
265+
Value improved to 0.3422.
266+
Value improved to 0.3216.
267+
Descent done. Attempts: 52, Step: 0.0009765625, Arguments: [1.5, 1.0625], Value: 0.3216.
268+
0.3216"#]];
269+
pub const SIMPLEVQE_EXPECT_DEBUG: Expect = expect![[r#"
270+
Beginning descent from value 0.43300000000000005.
271+
Value improved to 0.35300000000000004.
272+
Value improved to 0.3454.
273+
Value improved to 0.3422.
274+
Value improved to 0.3216.
275+
Descent done. Attempts: 52, Step: 0.0009765625, Arguments: [1.5, 1.0625], Value: 0.3216.
276+
0.3216"#]];
261277
pub const SUPERDENSECODING_EXPECT: Expect = expect!["((false, true), (false, true))"];
262278
pub const SUPERDENSECODING_EXPECT_DEBUG: Expect = expect!["((false, true), (false, true))"];
263279
pub const SUPERPOSITION_EXPECT: Expect = expect!["Zero"];

0 commit comments

Comments
 (0)