Two Qubit Gates

In this example we will solve for a selection of two-qubit gates using a simple two-qubit system. We will use the UnitarySmoothPulseProblem template to solve for the optimal control fields.

Defining our Hamiltonian

In quantum optimal control we work with Hamiltonians of the form

\[H(t) = H_{\text{drift}} + \sum_{j} u^j(t) H_{\text{drive}}^j,\]

Specifically, for a simple two-qubit system in a rotating frame, we have

\[H = J_{12} \sigma_1^x \sigma_2^x + \sum_{i \in {1,2}} a_i^R(t) {\sigma^x_i \over 2} + a_i^I(t) {\sigma^y_i \over 2}.\]

where

\[\begin{align*} J_{12} &= 0.001 \text{ GHz}, \\ |a_i^R(t)| &\leq 0.1 \text{ GHz} \\ \end{align*}\]

And the duration of the gate will be capped at $400 \ \mu s$.

Let's now set this up using some of the convenience functions available in QuantumCollocation.jl.

using QuantumCollocation
using NamedTrajectories
using LinearAlgebra

# Define our operators
σx = GATES[:X]
σy = GATES[:Y]
Id = GATES[:I]

# Lift the operators to the two-qubit Hilbert space
σx_1 = σx ⊗ Id
σx_2 = Id ⊗ σx

σy_1 = σy ⊗ Id
σy_2 = Id ⊗ σy

# Define the parameters of the Hamiltonian
J_12 = 0.001 # GHz
a_bound = 0.100 # GHz

# Define the drift (coupling) Hamiltonian
H_drift = J_12 * (σx ⊗ σx)

# Define the control Hamiltonians
H_drives = [σx_1 / 2, σy_1 / 2, σx_2 / 2, σy_2 / 2]

# Define control (and higher derivative) bounds
a_bound = 0.1
da_bound = 0.0005
dda_bound = 0.0025

# Scale the Hamiltonians by 2π
H_drift *= 2π
H_drives .*= 2π

# Define the time parameters
T = 100 # timesteps
duration = 100 # μs
Δt = duration / T
Δt_max = 400 / T

# Define the system
sys = QuantumSystem(H_drift, H_drives)

# Look at max eigenvalue of the generator (for deciding if Pade integrators are viable)
maximum(abs.(eigvals(Δt_max * (H_drift + sum(a_bound .* H_drives)))))
3.5669831654130384

That this value above is greater than one means that we must use an exponential integrator for these problems. We can set the kwarg integrator=:exponential in the PiccoloOptions struct as follows.

piccolo_options = PiccoloOptions(
    integrator=:exponential,
)
PiccoloOptions(true, false, true, true, :exponential, 4, false, false, ExponentialAction.expv, true, true, true)

SWAP gate

# Define the goal operation
U_goal = [
    1 0 0 0;
    0 0 1 0;
    0 1 0 0;
    0 0 0 1
] |> Matrix{ComplexF64}

# Set up and solve the problem

prob = UnitarySmoothPulseProblem(
    sys,
    U_goal,
    T,
    Δt;
    a_bound=a_bound,
    da_bound=da_bound,
    dda_bound=dda_bound,
    R_da=0.01,
    R_dda=0.01,
    Δt_max=Δt_max,
    piccolo_options=piccolo_options
)

solve!(prob; max_iter=100)

# Let's take a look at the final fidelity
unitary_fidelity(prob)
0.996735636258253

Looks good!

Now let's plot the pulse and the population trajectories for the first two columns of the unitary, i.e. initial state of $\ket{00}$ and $\ket{01}$. For this we provide the function plot_unitary_populations.

plot_unitary_populations(prob)
Example block output

For fun, let's look at a minimum time pulse for this problem

min_time_prob = UnitaryMinimumTimeProblem(prob; final_fidelity=.99)

solve!(min_time_prob; max_iter=300)

unitary_fidelity(min_time_prob)
0.9900060465200272

And let's plot this solution

plot_unitary_populations(min_time_prob)
Example block output

It looks like our pulse derivative bounds are holding back the solution, but regardless, the duration has decreased:

get_duration(prob.trajectory) - get_duration(min_time_prob.trajectory)
27.71807136998825

Mølmer–Sørensen gate

Here we will solve for a Mølmer–Sørensen gate between two. The gate is generally described, for N qubits, by the unitary matrix

\[U_{\text{MS}}(\vec\theta) = \exp\left(i\sum_{j=1}^{N-1}\sum_{k=j+1}^{N}\theta_{jk}\sigma_j^x\sigma_k^x\right),\]

where $\sigma_j^x$ is the Pauli-X operator acting on the $j$-th qubit, and $\vec\theta$ is a vector of real parameters. The Mølmer–Sørensen gate is a two-qubit gate that is particularly well-suited for trapped-ion qubits, where the interaction between qubits is mediated.

Here we will focus on the simplest case of a Mølmer–Sørensen gate between two qubits. The gate is described by the unitary matrix

\[U_{\text{MS}}\left({\pi \over 4}\right) = \exp\left(i\frac{\pi}{4}\sigma_1^x\sigma_2^x\right).\]

Let's set up the problem.

# Define the goal operation
U_goal = exp(im * π/4 * σx_1 * σx_2)

# Set up and solve the problem

prob = UnitarySmoothPulseProblem(
    sys,
    U_goal,
    T,
    Δt;
    a_bound=a_bound,
    da_bound=da_bound,
    dda_bound=dda_bound,
    R_da=0.01,
    R_dda=0.01,
    Δt_max=Δt_max,
    piccolo_options=piccolo_options
)

solve!(prob; max_iter=1_000)

# Let's take a look at the final fidelity
unitary_fidelity(prob)
0.9998792839054915

Again, looks good!

Now let's plot the pulse and the population trajectories for the first two columns of the unitary, i.e. initial state of $\ket{00}$ and $\ket{01}$.

plot_unitary_populations(prob)
Example block output

For fun, let's look at a minimum time pulse for this problem

min_time_prob = UnitaryMinimumTimeProblem(prob; final_fidelity=.999)

solve!(min_time_prob; max_iter=300)

unitary_fidelity(min_time_prob)
0.9976198220873692

And let's plot this solution

plot_unitary_populations(min_time_prob)
Example block output

It looks like our pulse derivative bounds are holding back the solution, but regardless, the duration has decreased:

get_duration(prob.trajectory) - get_duration(min_time_prob.trajectory)
74.97495582878966

This page was generated using Literate.jl.