Skip to content

Commit f1969ca

Browse files
authored
Merge pull request #112 from Langhaarzombie/feature/ntbk_qoc
QuTiPv5 Paper Notebook: QOC
2 parents 4c03c49 + 29d95c8 commit f1969ca

File tree

3 files changed

+331
-0
lines changed

3 files changed

+331
-0
lines changed

.github/workflows/nightly_ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ jobs:
6565
cd ..
6666
python -m pip install git+https://github.com/qutip/qutip-qip
6767
python -m pip install --no-deps git+https://github.com/qutip/qutip-jax
68+
python -m pip install --no-deps git+https://github.com/qutip/qutip-qoc
6869
6970
git clone -b master https://github.com/qutip/qutip-qtrl.git
7071
cd qutip-qtrl

.github/workflows/notebook_ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ jobs:
6767
cd ..
6868
python -m pip install git+https://github.com/qutip/qutip-qip
6969
python -m pip install --no-deps git+https://github.com/qutip/qutip-jax
70+
python -m pip install --no-deps git+https://github.com/qutip/qutip-qoc
7071
7172
git clone -b master https://github.com/qutip/qutip-qtrl.git
7273
cd qutip-qtrl
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
---
2+
jupyter:
3+
jupytext:
4+
text_representation:
5+
extension: .md
6+
format_name: markdown
7+
format_version: '1.3'
8+
jupytext_version: 1.13.8
9+
kernelspec:
10+
display_name: Python 3 (ipykernel)
11+
language: python
12+
name: python3
13+
---
14+
15+
# QuTiPv5 Paper Example: The Quantum Optimal Control Package
16+
17+
Authors: Maximilian Meyer-Mölleringhof ([email protected]), Boxi Li ([email protected]), Neill Lambert ([email protected])
18+
19+
Quantum systems are naturally sensitive to their environment and external perturbations.
20+
This is great, as it allows for very precise measurements.
21+
However, it also makes handling errors and imprecisions a big challenge.
22+
In the case for quantum computing, finding the optimal parameters to achieve a desired operation is this thus an important problem.
23+
Optimization parameters may include amplitude, frequency, duration, bandwidth, etc. and are generally directly dependent on the considered hardware.
24+
25+
To find these optimal control parameters, several methods have been developed.
26+
Here, we look at three algorithms: *gradient ascent pulse engineering* (GRAPE) [\[3\]](#References), *chopped random basis* (CRAB) [\[4\]](#References) and *gradient optimization af analytic controls* (GOAT) [\[5\]](#References).
27+
Whereas the former two have been part of the `QuTiP-QTRL` package of QuTiPv4, the latter is a new addition in version 5.
28+
Althogether, these algorithms are now included in the new `QuTiP-QOC` package that also adds `QuTiP-JAX` [\[6\]](#References) integration via the JAX optimization technique (JOPT).
29+
30+
```python
31+
import matplotlib.pyplot as plt
32+
import numpy as np
33+
from jax import jit, numpy
34+
from qutip import (about, gates, liouvillian, qeye, sigmam, sigmax, sigmay,
35+
sigmaz)
36+
from qutip_qoc import Objective, optimize_pulses
37+
38+
%matplotlib inline
39+
```
40+
41+
## Introduction
42+
43+
In this example we want to implement a Hadamard gate on a single qubit.
44+
In general, a qubit might be subject to decoherence which can be captured using the Lindblad formalism with the jump operator $\sigma_{-}$.
45+
46+
For simplicity, we consider a control Hamiltonian parametrized by $\sigma_x$, $\sigma_y$ and $\sigma_z$:
47+
48+
$H_c(t) = c_x(t) \sigma_x + c_y(t) \sigma_y + c_z(t) \sigma_z$
49+
50+
with $c_x(t)$, $c_y(t)$ and $c_z(t)$ as independent control parameters.
51+
Additionally, we model a constant drift Hamiltonian
52+
53+
$H_d = \dfrac{1}{2} (\omega \sigma_z + \delta \sigma_x)$,
54+
55+
with associated energy splitting $\omega$ and tunneling rate $\delta$.
56+
The amplitude damping rate for the collapse operator $C = \sqrt{\gamma} \sigma_-$ is denoted as $\gamma$.
57+
58+
```python
59+
# energy splitting, tunneling, amplitude damping
60+
omega = 0.1 # energy splitting
61+
delta = 1.0 # tunneling
62+
gamma = 0.1 # amplitude damping
63+
sx, sy, sz = sigmax(), sigmay(), sigmaz()
64+
65+
Hc = [sx, sy, sz] # control operator
66+
Hc = [liouvillian(H) for H in Hc]
67+
68+
Hd = 1 / 2 * (omega * sz + delta * sx) # drift term
69+
Hd = liouvillian(H=Hd, c_ops=[np.sqrt(gamma) * sigmam()])
70+
71+
# combined operator list
72+
H = [Hd, Hc[0], Hc[1], Hc[2]]
73+
```
74+
75+
```python
76+
# objectives for optimization
77+
initial = qeye(2)
78+
target = gates.hadamard_transform()
79+
fid_err = 0.01
80+
```
81+
82+
```python
83+
# pulse time interval
84+
times = np.linspace(0, np.pi / 2, 100)
85+
```
86+
87+
## Implementation
88+
89+
### GRAPE Algorithm
90+
91+
The GRAPE algorithm works by minimizing an infidelity loss function that measures how close the final state or unitary tranformation is to the desired target.
92+
Starting from the provided `guess` control pulse, it optimizes evenly spaced piecewise constant pulse amplitudes.
93+
In the end, it strives to achieve the desired target infidelity, sepcified by the `fid_err_targ` keyword.
94+
95+
```python
96+
res_grape = optimize_pulses(
97+
objectives=Objective(initial, H, target),
98+
control_parameters={
99+
"ctrl_x": {"guess": np.sin(times), "bounds": [-1, 1]},
100+
"ctrl_y": {"guess": np.cos(times), "bounds": [-1, 1]},
101+
"ctrl_z": {"guess": np.tanh(times), "bounds": [-1, 1]},
102+
},
103+
tlist=times,
104+
algorithm_kwargs={"alg": "GRAPE", "fid_err_targ": fid_err},
105+
)
106+
```
107+
108+
### CRAB Algorithm
109+
110+
This algorithm is based on the idea of expanding the control fields in a random basis and optimizing the expansion coefficients $\vec{\alpha}$.
111+
This has the advantage of using analytical control functions $c(\vec{\alpha}, t)$ on a continuous time interval, and is by default a Fourier expansion.
112+
This reduces the search space to the function parameters.
113+
Typically, these parameters can efficiently be calculated through direct search algorithms (like Nelder-Mead).
114+
The basis function is only expanded for some finite number of summands and the initial basis coefficients are usually picked at random.
115+
116+
```python
117+
n_params = 3 # adjust in steps of 3
118+
alg_args = {"alg": "CRAB", "fid_err_targ": fid_err, "fix_frequency": False}
119+
```
120+
121+
```python
122+
res_crab = optimize_pulses(
123+
objectives=Objective(initial, H, target),
124+
control_parameters={
125+
"ctrl_x": {
126+
"guess": [1 for _ in range(n_params)],
127+
"bounds": [(-1, 1)] * n_params,
128+
},
129+
"ctrl_y": {
130+
"guess": [1 for _ in range(n_params)],
131+
"bounds": [(-1, 1)] * n_params,
132+
},
133+
"ctrl_z": {
134+
"guess": [1 for _ in range(n_params)],
135+
"bounds": [(-1, 1)] * n_params,
136+
},
137+
},
138+
tlist=times,
139+
algorithm_kwargs=alg_args,
140+
)
141+
```
142+
143+
### GOAT Algorithm
144+
145+
Similar to CRAB, this method also works with analytical control functions.
146+
By constructing a coupled system of equations of motion, the derivative of the (time ordered) evolution operator with respect to the control parameters can be calculated after numerical forward integration.
147+
In unconstrained settings, GOAT was found to outperform the previous described methods in terms of convergence and fidelity achievement.
148+
The QuTiP implementation allows for arbitrary control functions provided together with their respective derivatives in a common python manner.
149+
150+
```python
151+
def sin(t, c):
152+
return c[0] * np.sin(c[1] * t)
153+
154+
155+
# derivatives
156+
def grad_sin(t, c, idx):
157+
if idx == 0: # w.r.t. c0
158+
return np.sin(c[1] * t)
159+
if idx == 1: # w.r.t. c1
160+
return c[0] * np.cos(c[1] * t) * t
161+
if idx == 2: # w.r.t. time
162+
return c[0] * np.cos(c[1] * t) * c[1]
163+
```
164+
165+
```python
166+
H = [Hd] + [[hc, sin, {"grad": grad_sin}] for hc in Hc]
167+
168+
bnds = [(-1, 1), (0, 2 * np.pi)]
169+
ctrl_param = {id: {"guess": [1, 0], "bounds": bnds} for id in ["x", "y", "z"]}
170+
```
171+
172+
For even faster convergence QuTiP extends to original algorithm with the option to optimize controls with
173+
respect to the overall time evolution, which can be enabled by specifying the additional time keyword
174+
argument:
175+
176+
```python
177+
# treats time as optimization variable
178+
ctrl_param["__time__"] = {
179+
"guess": times[len(times) // 2],
180+
"bounds": [times[0], times[-1]],
181+
}
182+
```
183+
184+
```python
185+
# run the optimization
186+
res_goat = optimize_pulses(
187+
objectives=Objective(initial, H, target),
188+
control_parameters=ctrl_param,
189+
tlist=times,
190+
algorithm_kwargs={
191+
"alg": "GOAT",
192+
"fid_err_targ": fid_err,
193+
},
194+
)
195+
```
196+
197+
### JOT Algorithm - JAX integration
198+
199+
QuTiP's new JAX backend provides automatic differentiation capabilities that can be directly be used with the new control framework.
200+
As with QuTiP’s GOAT implementation, any analytically defined control function can be handed to the algorithm.
201+
However, in this method, JAX automatic differentiation abilities take care of calculating the derivative throughout the whole system evolution.
202+
Therefore we don't have to provide any derivatives manually.
203+
Compared to the previous example, this simply means to swap the control functions with their just-in-time compiled version.
204+
205+
```python
206+
@jit
207+
def sin_y(t, d, **kwargs):
208+
return d[0] * numpy.sin(d[1] * t)
209+
210+
211+
@jit
212+
def sin_z(t, e, **kwargs):
213+
return e[0] * numpy.sin(e[1] * t)
214+
215+
216+
@jit
217+
def sin_x(t, c, **kwargs):
218+
return c[0] * numpy.sin(c[1] * t)
219+
```
220+
221+
```python
222+
H = [Hd] + [[Hc[0], sin_x], [Hc[1], sin_y], [Hc[2], sin_z]]
223+
```
224+
225+
```python
226+
res_jopt = optimize_pulses(
227+
objectives=Objective(initial, H, target),
228+
control_parameters=ctrl_param,
229+
tlist=times,
230+
algorithm_kwargs={
231+
"alg": "JOPT",
232+
"fid_err_targ": fid_err,
233+
},
234+
)
235+
```
236+
237+
## Comparison of Results
238+
239+
After running the global and local optimization, one can compare the results obtained by the various
240+
algorithms through a `qoc.Result` object, which provides common optimization metrics along with the `optimized_controls`.
241+
242+
### Pulse Amplitudes
243+
244+
```python
245+
fig, ax = plt.subplots(1, 3, figsize=(13, 5))
246+
247+
goat_range = times < res_goat.optimized_params[-1]
248+
jopt_range = times < res_jopt.optimized_params[-1]
249+
250+
for i in range(3):
251+
ax[i].plot(times, res_grape.optimized_controls[i], ":", label="GRAPE")
252+
ax[i].plot(times, res_crab.optimized_controls[i], "-.", label="CRAB")
253+
ax[i].plot(
254+
times[goat_range],
255+
np.array(res_goat.optimized_controls[i])[goat_range],
256+
"-",
257+
label="GOAT",
258+
)
259+
ax[i].plot(
260+
times[jopt_range],
261+
np.array(res_jopt.optimized_controls[i])[jopt_range],
262+
"--",
263+
label="JOPT",
264+
)
265+
266+
ax[i].set_xlabel(r"Time $t$")
267+
268+
ax[0].legend(loc=0)
269+
ax[0].set_ylabel(r"Pulse amplitude $c_x(t)$", labelpad=-5)
270+
ax[1].set_ylabel(r"Pulse amplitude $c_y(t)$", labelpad=-5)
271+
ax[2].set_ylabel(r"Pulse amplitude $c_z(t)$", labelpad=-5)
272+
273+
plt.show()
274+
```
275+
276+
### Infidelities and Processing Time
277+
278+
```python
279+
print("GRAPE: ", res_grape.fid_err)
280+
print(res_grape.total_seconds, " seconds")
281+
print()
282+
print("CRAB : ", res_crab.fid_err)
283+
print(res_crab.total_seconds, " seconds")
284+
print()
285+
print("GOAT : ", res_goat.fid_err)
286+
print(res_goat.total_seconds, " seconds")
287+
print()
288+
print("JOPT : ", res_jopt.fid_err)
289+
print(res_jopt.total_seconds, " seconds")
290+
```
291+
292+
## References
293+
294+
[1] [QuTiP 5: The Quantum Toolbox in Python](https://arxiv.org/abs/2412.04705)
295+
296+
[2] [QuTiP-QOC Repository](https://github.com/qutip/qutip-qoc)
297+
298+
[3] [Khaneja, et. al, Journal of Magnetic Resonance (2005)](https://www.sciencedirect.com/science/article/pii/S1090780704003696)
299+
300+
[4] [Caneva, et. al, Phys. Rev. A (2011)](https://link.aps.org/doi/10.1103/PhysRevA.84.022326)
301+
302+
[5] [Machnes, et. al, Phys. Rev. Lett. (2018)](https://link.aps.org/doi/10.1103/PhysRevLett.120.150401)
303+
304+
[6] [QuTiP-JAX Repository](https://github.com/qutip/qutip-jax)
305+
306+
307+
308+
## About
309+
310+
```python
311+
about()
312+
```
313+
314+
## Testing
315+
316+
```python
317+
assert (
318+
res_grape.fid_err < fid_err
319+
), f"GRAPE did not reach the target infidelity of < {fid_err}."
320+
assert (
321+
res_crab.fid_err < fid_err
322+
), f"CRAB did not reach the target infidelity of < {fid_err}."
323+
assert (
324+
res_goat.fid_err < fid_err
325+
), f"GOAT did not reach the target infidelity of < {fid_err}."
326+
assert (
327+
res_jopt.fid_err < fid_err
328+
), f"JOPT did not reach the target infidelity of < {fid_err}."
329+
```

0 commit comments

Comments
 (0)