Hosted by
Alice & Bob
aqora template dynamiqs-2025-1
test
.aqora lab -p dynamiqs-2025-1
submission/solution.ipynb
.
Fill in your solution. You can run the notebook locally to test your
solution by running the following in the terminalaqora test
aqora upload
data
variable. We then infer from this data.import dynamiqs as dq
import jax
import jax.numpy as jnp
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
dq.plot.mplstyle(dpi=150)
# Declare useful helpers to handle units
MHz = 2 * jnp.pi
kHz = 2 * jnp.pi * 1e-3
us = 1.0
ns = 1.0e-3
# Instanciate our bosonic annihilation operators
Na, Nb = 15, 7
a, b = dq.destroy(Na, Nb)
# Load the data
def load(file):
x = np.loadtxt(".aqora/data/data/%s" % file)
x = jnp.array(x)
return x
if isinstance(input, tuple):
data, tsave = input
else:
data = load("short.npy")
tsave = load("time_short.npy")
alphas = jnp.sqrt(data[0])
# Plot experimental data
plt.figure()
plt.plot(tsave / us, data, "+")
plt.grid()
plt.xlabel("Time [us]")
plt.ylabel("Photon number")
# expectation operator: number of photons
exp_ops = [dq.dag(a) @ a]
def simulate(g2, kappa_a, kappa_b, nth_a, nth_b, progress_meter=True):
# Redefine Hamiltonian with the new g2 value
H = g2 * a @ a @ dq.dag(b)
H += dq.dag(H)
jump_ops = [
jnp.sqrt(kappa_a * (1 + nth_a)) * a,
jnp.sqrt(kappa_a * nth_a) * dq.dag(a),
jnp.sqrt(kappa_b * (1 + nth_b)) * b,
jnp.sqrt(kappa_b * nth_b) * dq.dag(b)
]
# intialize buffer state with some thermal population
psi0_b = dq.unit(dq.fock(Nb, 0) + nth_b ** 0.5 * dq.fock(Nb, 1))
psi0 = dq.stack([dq.tensor(dq.coherent(Na, alpha), psi0_b) for alpha in alphas])
options = dq.Options(progress_meter=
dq.progress_meter.TqdmProgressMeter() if progress_meter
else dq.progress_meter.NoProgressMeter()
)
# Perform the master equation simulation
return dq.mesolve(
H,
jump_ops,
psi0,
tsave,
exp_ops=exp_ops,
options=options
)
# Initial guess
g2 = jnp.array(1.0 * MHz)
nth_a, nth_b = 0.1, 0.011
kappa_a = 5 * kHz
kappa_b = 10 * MHz
# Run the simulation with the initial guess
result = simulate(alphas, tsave, g2, kappa_a, kappa_b, nth_a, nth_b)
# Plot the initial guess simulation and the data
plt.figure()
for i in range(len(alphas)):
plt.plot(tsave / us, data[:, i].real, f"+C{i}", alpha=0.6)
plt.plot(tsave / us, result.expects[i, 0].real, f"C{i}")
plt.grid()
plt.xlabel("Time [us]")
plt.ylabel("Photon number")
@jax.jit
def cost_fn(x, data):
"""Simulate the system with the given the parameters and return the cost."""
g2, kappa_a, kappa_b, nth_a, nth_b = x
res = simulate(*x, progress_meter=False)
return jnp.sum((data - res.expects[:, 0].real.T) ** 2)
physical_parameters = [jnp.array([g2, kappa_a, kappa_b, nth_a, nth_b])]
costs = [cost_fn(physical_parameters[-1], data, tsave)]
lr = 1e-3 # learning rate
for _ in tqdm(range(40)):
# Compute the cost and its gradient
parameter = physical_parameters[-1]
cost, cost_grad = jax.value_and_grad(cost_fn)(parameter, data, tsave)
# Clip the gradient to avoid numerical instability
cost_grad = jnp.clip(cost_grad, -100, 100)
# Perform the gradient descent step
parameter = parameter - lr * cost_grad
# all parameters in the problem are bounded by some lower values
parameter = np.maximum(
parameter,
jnp.array([0.1 * MHz, 0.1 * kHz, 0.1 * MHz, 0.01, 1e-5])
)
# Save the cost and the parameter values
costs.append(cost)
physical_parameters.append(parameter)
# Save the final cost and convert to jnp arrays
costs, physical_parameters = jnp.array(costs), jnp.array(physical_parameters)
fig, axs = plt.subplots(2, 3, tight_layout=True)
ax = axs[0, 0]
ax.plot(costs)
ax.set_xlabel("Epoch")
ax.set_ylabel("Cost")
labels = ["g2", "kappa_a", "kappa_b", "nth_a", "nth_b"]
ref_unit = [MHz, kHz, MHz, 1, 1]
for i, label in enumerate(labels):
ax = axs.ravel()[i+1]
ax.plot(physical_parameters[:, i] / ref_unit[i])
ax.set_xlabel("Epoch")
ax.set_ylabel(label)
plt.tight_layout()
g2, kappa_a, kappa_b, nth_a, nth_b = physical_parameters[-1]
# Print the final g2 value
print(f"g2 = {g2 / MHz:.3f} MHz")
print(f"kappa_a = {kappa_a / kHz:.3f} kHz")
print(f"kappa_b = {kappa_b / MHz:.3f} MHz")
print(f"nth_a = {nth_a:.3f}")
print(f"nth_b = {nth_b:.3f}")
# Run a final simulation with the fitted g2 value
result = simulate(alphas, tsave, g2, kappa_a, kappa_b, nth_a, nth_b)
# Plot the fit result
plt.figure()
for i in range(len(alphas)):
plt.plot(tsave / us, data[:, i].real, f"+C{i}", alpha=0.6)
plt.plot(tsave / us, result.expects[i, 0].real, f"C{i}")
plt.grid()
plt.xlabel("Time [us]")
plt.ylabel("Photon number")
output = g2, kappa_a, kappa_b, nth_a, nth_b
$ aqora upload