# Optimize Mølmer–Sørensen gates for trapped ions

Creating optimal operations with the trapped ions toolkit

In this tutorial you will create a gate to efficiently prepare quantum states of trapped ion qubits using Mølmer–Sørensen-type interactions.

This tutorial will introduce you to the Boulder Opal trapped ions toolkit to perform calculations and the Q-CTRL Visualizer package to visualize their results. You can refer to our Boulder Opal toolkits topic for more information on what Boulder Opal toolkits are and when and how to use them.

The Boulder Opal Toolkits are currently in beta phase of development. Breaking changes may be introduced.

## Optimizing a Mølmer–Sørensen gate

Your task will be to generate an entangling Mølmer–Sørensen gate for two trapped Ytterbium ions.

Mølmer–Sørensen-type operations entangle multiple ions using laser drives tuned close to the frequency of an ion chain oscillation mode. Particular drives give rise to relative phases and phase space displacements during an operation. The operational infidelity of the gate is determined by the relative phases and displacements at the final time of the drives. In a successful Mølmer–Sørensen operation in an ion chain, the phase-space trajectories for all modes return to zero (that is, avoiding residual internal-motional entanglement), and set the phase acquired between the internal states of each pair of ions to a target value.

The interaction Hamiltonian for Mølmer–Sørensen-type operations in a chain with $N$ ions takes the form ($\hbar=1$ as per Boulder Opal convention) $$H(t) = \frac{i}{2} \sum_{j=1}^N \sigma_{x, j} \sum_{p=1}^{3N} \eta_{pj} \left( \gamma_j(t) e^{i \delta_p t} a_{p}^\dagger - \gamma_j^\ast(t) e^{-i \delta_p t} a_p \right) ,$$ where the axis dimension and collective mode dimension are combined into a single index $p$ for simplicity, $\sigma_{x,j}$ is the Pauli $X$ operator for ion $j$, $\eta_{pj}$ is the Lamb–Dicke parameter of mode $p$ for ion $j$, $\gamma_j(t)$ is the drive addressing ion $j$, $a_p$ is the annihilation operator for mode $p$, and $\delta_p=\nu_p-\delta$ is the relative detuning of the laser frequency from the motional frequency of mode $p$.

The trapped ions toolkit is capable of finding optimal drives $\gamma_j(t)$ which create the target entangling phases in the system. With $\psi_{jk}$ indicating the phase between each pair of ions ($j, k$) (indexing from 0), the operation acting on the internal degrees of freedom of the ions can be described as: $$U = \exp\left(i \sum_{j=1}^{N-1} \sum_{k=0}^{j-1} \psi_{jk} \sigma_{x,j} \sigma_{x,k} \right) .$$ By imposing a combination of symmetry to the ions drives and penalizing the displacement of each mode's center of mass trajectory, the toolkit can also generate error-robust gates resilient against common noise sources (for example, laser-detuning noise).

Although the trapped ions toolkit can deal with larger ion chains, in this tutorial you will work with a pair of ions and create an entangling gate with a global beam addressing both ions. With a target phase of $\psi_{10} = \pi/4$, the corresponding entangling operation is then $$U = \exp\left(i \frac{\pi}{4} \sigma_{x,0} \sigma_{x,1} \right) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & \tfrac{1+i}{2} & \tfrac{-1+i}{2} & 0 \\ 0 & \tfrac{-1+i}{2} & \tfrac{1+i}{2} & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} .$$

### 1. Import libraries and start a Boulder Opal session

Before doing any calculation with Boulder Opal, you always need to import the necessary libraries and start a session.

In this case, import the numpy, matplotlib.pyplot, qctrlvisualizer, and qctrl packages. To learn more about installing Boulder Opal and starting a session, see the Get started guide.

# Import packages.
import numpy as np
import matplotlib.pyplot as plt
import qctrlvisualizer
from qctrl import Qctrl

# Apply Q-CTRL style to plots created in pyplot.
plt.style.use(qctrlvisualizer.get_qctrl_style())

# Start a Boulder Opal session.
qctrl = Qctrl()

### 2. Obtain ion chain properties

Start by specifying the physical parameters for the laser drive and ion trap, and calculating the ion chain properties with the qctrl.ions.obtain_ion_chain_properties function. The function returns two NumPy arrays with the Lamb–Dicke parameters and relative detunings.

# Number of ions in the system.
ion_count = 2

# Calculate Lamb–Dicke parameters and relative detunings.
lamb_dicke_parameters, relative_detunings = qctrl.ions.obtain_ion_chain_properties(
atomic_mass=171,  # Yb ions
ion_count=ion_count,
center_of_mass_frequencies=[1.6e6, 1.5e6, 0.3e6],  # Hz
wave_numbers=[(2 * np.pi) / 355e-9, (2 * np.pi) / 355e-9, 0.0],  # rad/m
laser_detuning=1.6e6 + 4.7e3,  # Hz
)
Your task calculate_ion_chain_properties (action_id="1599016") has completed.


### 3. Define optimizable drives

Define the optimizable drives according to the degrees of freedom in the hardware. In this case, define a single optimizable piecewise-constant complex drive $\gamma(t)$ by creating a qctrl.ions.ComplexOptimizableDrive object. Pass to it its maximum Rabi rate (modulus) and the ions it addresses in the system (both of them), as well as the number of optimizable piecewise-constant segments it should have.

# Optimizable drive characteristics.
maximum_rabi_rate = 2 * np.pi * 100e3  # rad/s
segment_count = 64

drive = qctrl.ions.ComplexOptimizableDrive(
)

### 4. Define optimization target

Define the duration of the gate to be implemented, as well as the target phases for all the ions. The target phases must be a strictly lower triangular matrix, that is, $\psi_{kl}$ indicates the total relative phase target for ions $k$ and $l$, with $k > l$.

# Gate duration.
duration = 2e-4  # s

# Target phases.
target_phase = np.pi / 4
target_phases = np.array([[0, 0], [target_phase, 0]])

### 5. Execute optimization

You now have everything necessary to carry out the optimization. Use the qctrl.ions.ms_optimize function to obtain the optimized pulse that implements the target Mølmer–Sørensen gate. Pass to it the system parameters, the optimizable drives, as well as the gate duration and the target phases matrix.

result = qctrl.ions.ms_optimize(
drives=[drive],
lamb_dicke_parameters=lamb_dicke_parameters,
relative_detunings=relative_detunings,
duration=duration,
target_phases=target_phases,
)
Your task calculate_optimization (action_id="1599020") has completed.


### 6. Extract the optimization outputs

After the calculation is complete, the results are stored in the returned dictionary, result. It contains a dictionary of "optimized_values" for the drives, as well as the optimized system dynamics (the "phases", "displacements", and "infidelities") and the "sample_times" at which they're sampled.

#### Extract the final gate infidelity

A low infidelity at the end of the operation ensures us that the ion pairs have acquired the right phases, without generating any entanglement without the internal and external degrees of freedom of the ions.

print(f"Infidelity reached: {result['infidelities'][-1]:.3e}")
Infidelity reached: 4.585e-12


#### Visualize the optimized pulse

You can retrieve the values of the optimized drive from the optimized values dictionary, and plot them with the plot_controls function from the Q-CTRL Visualizer package.

drive = qctrl.utils.pwc_arrays_to_pairs(duration, result["optimized_values"]["drive"])
qctrlvisualizer.plot_controls(controls={r"$\gamma$": drive})

#### Visualize the phase dynamics

You can easily visualize the dynamics of the relative phase between the two ions over the duration of the gate, by extracting the phases from the result.

sample_times = result["sample_times"] * 1e3
plt.plot(sample_times, result["phases"][:, 1, 0])
plt.plot([0, sample_times[-1]], [target_phase, target_phase], "k--")
plt.yticks([0, target_phase], ["0", r"$\frac{\pi}{4}$"])
plt.xlabel("Time (ms)")
plt.ylabel("Relative phase")

plt.show()

For high-fidelity operations, the relative phase obtained at the end of the operation should match the target phase for the ion pair (marked by a horizontal dashed black line).

#### Visualize phase space trajectories

You can visualize the trajectory of the center of a coherent state in (rotating) optical phase space for each mode. The closure of these trajectories is a necessary condition for an effective operation. You can obtain the overall displacement for each mode by summing the displacements over the ion dimension.

total_displacement = np.sum(result["displacements"], axis=-1)

fig, axs = plt.subplots(1, 2)
plot_range = 1.05 * np.max(np.abs(total_displacement))
fig.suptitle("Phase space trajectories")

for k in range(2):
for mode in range(ion_count):
axs[k].plot(
np.real(total_displacement[:, k, mode]),
np.imag(total_displacement[:, k, mode]),
label=f"mode {mode % ion_count}",
)
axs[k].plot(
np.real(total_displacement[-1, k, mode]),
np.imag(total_displacement[-1, k, mode]),
"k*",
markersize=10,
)

axs[k].set_xlim(-plot_range, plot_range)
axs[k].set_ylim(-plot_range, plot_range)
axs[k].set_aspect("equal")
axs[k].set_xlabel("q")

axs[0].set_title("$x$-axis")
axs[0].set_ylabel("p")
axs[1].set_title("$y$-axis")
axs[1].yaxis.set_ticklabels([])

hs, ls = axs[0].get_legend_handles_labels()
axs[1].legend(handles=hs, labels=ls, loc="upper left", bbox_to_anchor=(1, 1))

plt.show()

Here, $q \equiv q_m = (a_m^\dagger + a_m)/\sqrt{2}$ and $p \equiv p_m = i (a_m^\dagger - a_m)/\sqrt{2}$ are the dimensionless quadratures for each mode $m$. The black star marks the final displacement for each mode, which overlap at zero; this indicates that the operation has not caused any residual state-motional entanglement or motional heating. The z-axis modes are not addressed, and thus have no excursions in phase space.

You've reached the end of the tutorial. Congratulations on obtaining your first Mølmer–Sørensen gate.

You can easily extend the calculations you have done here by having longer ion chains, adding drives that address different ions, trying a robust optimization, etc. The different classes and functions in the trapped ions toolkit are described in their reference page.

You can perform more flexible optimizations and simulations of Mølmer–Sørensen-type interactions by using the graph framework in Boulder Opal. You can find more information in the How to optimize error-robust Mølmer–Sørensen gates for trapped ions and How to calculate system dynamics for arbitrary Mølmer–Sørensen gates user guides

This notebook was run using the following package versions. It should also be compatible with newer versions of the Q-CTRL Python package.

PackageVersion
Python3.10.8
matplotlib3.6.3
numpy1.24.1
scipy1.10.0
qctrl20.1.1
qctrl-commons17.7.0
boulder-opal-toolkits2.0.0-beta.3
qctrl-visualizer4.4.0