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 PiccoloQuantumObjects
using NamedTrajectories
using LinearAlgebra

using PiccoloPlots
using CairoMakie

⊗(a, b) = kron(a, b)

# 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)
QuantumSystem: levels = 4, n_drives = 4

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 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=PiccoloOptions(bound_state=true),
)
fid_init = unitary_rollout_fidelity(prob.trajectory, sys, drive_name=:a)
println(fid_init)
    constructing UnitarySmoothPulseProblem...
	using integrator: typeof(UnitaryIntegrator)
	control derivative names: [:da, :dda]
	applying timesteps_all_equal constraint: Δt
0.00039542607543285754

Solve the problem

solve!(prob; max_iter=100)
    initializing optimizer...
        applying constraint: timesteps all equal constraint
        applying constraint: initial value of Ũ⃗
        applying constraint: initial value of a
        applying constraint: final value of a
        applying constraint: bounds on a
        applying constraint: bounds on da
        applying constraint: bounds on dda
        applying constraint: bounds on Δt
        applying constraint: bounds on Ũ⃗

******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
 Ipopt is released as open source code under the Eclipse Public License (EPL).
         For more information visit https://github.com/coin-or/Ipopt
******************************************************************************

This is Ipopt version 3.14.19, running with linear solver MUMPS 5.8.1.

Number of nonzeros in equality constraint Jacobian...:   122590
Number of nonzeros in inequality constraint Jacobian.:        0
Number of nonzeros in Lagrangian Hessian.............:    22843

Total number of variables............................:     4460
                     variables with only lower bounds:        0
                variables with lower and upper bounds:     4460
                     variables with only upper bounds:        0
Total number of equality constraints.................:     4059
Total number of inequality constraints...............:        0
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  1.9970603e+00 4.63e-01 1.55e+01   0.0 0.00e+00    -  0.00e+00 0.00e+00   0
   1  1.9076693e+00 4.61e-01 1.54e+01  -2.2 8.78e-01    -  2.71e-03 3.51e-03f  1
   2  1.7680105e+00 4.59e-01 1.53e+01  -2.2 8.04e-01    -  5.36e-03 4.33e-03f  1
   3  1.5539301e+00 4.56e-01 1.52e+01  -4.0 8.13e-01    -  4.71e-03 7.71e-03h  1
   4  1.3684504e+00 4.52e-01 1.50e+01  -2.2 7.27e-01    -  1.18e-02 8.52e-03f  1
   5  1.2622220e+00 4.47e-01 2.48e+01  -1.7 7.84e-01    -  1.80e-02 1.04e-02f  1
   6  1.2891831e+00 4.39e-01 1.29e+02  -1.5 7.72e-01    -  5.01e-02 1.70e-02f  1
   7  1.6074653e+00 4.16e-01 1.74e+02  -1.5 7.35e-01    -  7.82e-02 5.18e-02f  1
   8  2.0322168e+00 3.85e-01 1.59e+02  -1.2 7.50e-01    -  7.10e-02 7.13e-02f  1
   9  2.0932113e+00 3.73e-01 7.91e+01  -1.3 7.65e-01    -  7.95e-02 3.26e-02f  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  10  1.9884811e+00 3.59e-01 2.54e+02  -1.9 7.52e-01    -  1.76e-02 3.62e-02h  1
  11  2.3390484e+00 3.32e-01 2.02e+02  -0.9 9.10e-01    -  6.27e-02 7.31e-02f  1
  12  1.5765832e+00 3.06e-01 2.15e+02  -1.7 9.10e-01    -  1.33e-01 7.46e-02h  1
  13  1.0040588e-01 2.75e-01 2.11e+02  -2.2 1.01e+00    -  1.17e-01 9.69e-02h  1
  14  6.3858464e-02 2.40e-01 1.31e+02  -2.1 9.09e-01    -  8.24e-02 1.22e-01h  1
  15  5.0436191e-01 2.37e-01 1.98e+02  -1.6 1.33e+00    -  9.43e-02 1.21e-02h  1
  16  5.3876730e-01 2.23e-01 2.49e+02  -0.6 2.01e+00    -  2.66e-02 6.02e-02f  1
  17  2.3674022e+00 2.12e-01 4.12e+02  -1.4 1.78e+00    -  8.46e-02 4.33e-02h  1
  18  6.5431422e-01 1.73e-01 3.86e+02  -1.2 1.05e+00    -  1.13e-01 1.81e-01f  1
  19  1.1951694e+00 1.61e-01 3.53e+02  -1.1 1.63e+00    -  7.07e-02 6.59e-02h  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  20  1.4743227e+00 1.61e-01 3.32e+02  -4.0 2.13e+01    -  1.80e-03 3.03e-03h  1
  21  1.3724494e+00 1.60e-01 2.69e+02  -1.5 3.85e+00    -  2.93e-02 4.78e-03h  1
  22  6.9695105e-01 1.57e-01 2.53e+02  -1.7 4.03e+00    -  2.28e-02 1.45e-02f  1
  23  2.0538158e-02 1.54e-01 2.62e+02  -2.3 1.09e+00    -  1.47e-02 2.52e-02h  1
  24  7.8524390e-02 1.50e-01 2.50e+02  -0.2 3.69e+00    -  1.57e-02 2.00e-02f  1
  25  4.2914051e+00 1.48e-01 3.71e+02  -4.0 2.68e+00    -  4.08e-02 1.70e-02h  1
  26  3.6816385e+00 1.45e-01 4.29e+02  -1.3 1.78e+00    -  7.04e-02 1.64e-02f  1
  27  2.3185859e+00 1.42e-01 4.32e+02  -1.6 2.75e+00    -  3.41e-02 2.13e-02f  1
  28  1.8041227e+00 1.36e-01 4.36e+02  -1.8 4.61e+00    -  1.94e-02 3.81e-02f  1
  29  5.5239599e+00 1.29e-01 4.43e+02  -0.4 1.99e+00    -  3.24e-02 5.39e-02f  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  30  4.7614581e-01 1.24e-01 6.08e+02  -1.8 9.25e-01   0.0 1.55e-01 4.28e-02f  1
  31  1.1345180e+01 1.09e-01 5.53e+02  -1.0 1.06e+00  -0.5 1.13e-01 1.06e-01h  1
  32  5.0785142e+00 9.27e-02 5.51e+02  -4.0 7.17e-01  -0.1 6.87e-02 1.66e-01f  1
  33  1.6310527e-01 8.59e-02 3.34e+02  -2.3 8.75e-01  -0.5 1.54e-01 8.12e-02f  1
  34  8.4889924e+00 7.56e-02 2.88e+02  -1.2 7.64e-01  -0.1 1.23e-01 1.32e-01h  1
  35  2.7797649e+01 6.91e-02 7.66e+02  -0.4 1.79e+00  -0.6 3.86e-02 1.03e-01f  1
  36  4.5000628e+00 4.59e-02 7.52e+02  -0.6 8.72e-01  -0.2 2.29e-01 2.87e-01f  1
  37  2.5825827e+01 2.27e-02 4.92e+02  -1.6 3.28e-01   0.3 3.82e-01 5.98e-01h  1
  38  4.9997522e+01 1.90e-02 1.88e+03  -1.2 2.91e-01  -0.2 4.07e-01 1.00e+00h  1
  39  4.8860687e+01 6.17e-03 1.24e+03  -0.8 1.74e-01   0.2 1.00e+00 6.43e-01f  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  40  3.0276475e+01 1.11e-02 4.34e+03  -0.3 1.11e+00  -0.3 1.00e+00 3.89e-01f  1
  41  2.0160346e+00 1.36e-02 4.45e+03  -0.9 4.90e-01    -  6.20e-01 1.00e+00f  1
  42  1.6008616e+01 9.81e-03 2.25e+03  -0.7 4.91e-01  -0.7 6.64e-01 5.00e-01h  2
  43  1.2775014e+01 1.74e-03 1.10e+02  -0.9 9.90e-02   0.6 1.00e+00 1.00e+00f  1
  44  1.0095478e+01 1.55e-03 3.71e+01  -1.4 9.30e-02   0.1 1.00e+00 1.00e+00f  1
  45  4.3065561e+00 1.02e-02 1.68e+02  -1.1 3.18e-01  -0.4 1.00e+00 7.27e-01f  1
  46  3.1601959e+00 5.95e-04 2.04e+01  -1.7 8.90e-02   0.1 1.00e+00 1.00e+00h  1
  47  7.7733517e-01 1.41e-03 8.50e+00  -2.4 1.48e-01  -0.4 1.00e+00 1.00e+00f  1
  48  2.9467632e-01 1.03e-03 7.12e+01  -3.1 1.69e-01  -0.9 1.00e+00 1.00e+00f  1
  49  1.3273041e-01 2.05e-03 1.27e+02  -2.1 5.15e-01  -1.4 1.00e+00 1.53e-01f  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  50  2.0775989e-01 3.10e-04 7.10e+01  -2.8 9.13e-02  -0.9 1.00e+00 1.00e+00h  1
  51  1.4322973e-01 3.13e-04 1.63e+02  -3.7 4.45e-02  -0.5 8.48e-01 1.00e+00h  1
  52  1.0098985e-01 6.51e-06 1.33e+01  -4.0 2.56e-03   1.7 1.00e+00 1.00e+00h  1
  53  9.5444738e-02 3.63e-06 3.44e-02  -4.0 1.96e-03   1.2 1.00e+00 1.00e+00h  1
  54  8.3467046e-02 2.58e-06 4.20e-02  -4.0 2.60e-03   0.8 1.00e+00 1.00e+00h  1
  55  6.0419877e-02 6.99e-06 5.56e-02  -4.0 6.97e-03   0.3 1.00e+00 1.00e+00f  1
  56  2.9062547e-02 1.47e-05 6.25e-02  -4.0 1.55e-02  -0.2 1.00e+00 1.00e+00h  1
  57  1.7465214e-03 2.39e-05 5.48e-02  -4.0 2.31e-02  -0.7 1.00e+00 1.00e+00h  1
  58  1.2589203e-03 1.50e-05 7.08e+01  -4.0 1.75e-02  -1.1 1.00e+00 5.00e-01h  2
  59  1.1670816e-03 1.31e-05 8.88e+00  -4.0 2.45e-02  -1.6 1.00e+00 1.25e-01h  4
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  60  3.8168472e-04 1.23e-05 6.30e+01  -4.0 2.15e-02  -2.1 1.00e+00 1.25e-01h  4
  61  5.7408543e-04 1.23e-05 8.76e+00  -4.0 1.07e-01  -2.6 1.00e+00 1.56e-02h  7
  62  4.0348505e-04 1.22e-05 6.26e+01  -4.0 2.13e-02  -2.1 1.00e+00 6.25e-02h  5
  63  5.3346741e-04 1.22e-05 9.19e+00  -4.0 1.11e-01  -2.6 1.00e+00 1.56e-02h  7
  64  4.2956191e-04 1.19e-05 6.22e+01  -4.1 1.90e-02  -2.2 1.00e+00 6.25e-02h  5
  65  4.9526229e-04 1.34e-05 9.59e+00  -3.8 1.81e-01  -2.7 1.00e+00 1.56e-02h  7
  66  4.2989553e-04 1.33e-05 6.18e+01  -3.9 2.20e-02  -2.2 1.00e+00 6.25e-02h  5
  67  3.5493235e-04 1.32e-05 9.47e+00  -3.9 1.28e-01  -2.7 1.00e+00 7.81e-03h  8
  68  4.2253831e-04 1.30e-05 6.16e+01  -4.0 2.19e-02  -2.3 1.00e+00 3.12e-02h  6
  69  1.7755955e-03 1.21e-05 1.69e+01  -4.1 2.71e-02  -1.9 1.00e+00 1.25e-01h  4
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  70  7.3018400e-04 4.35e-05 1.34e+01  -4.0 2.02e-02  -1.4 1.00e+00 1.00e+00H  1
  71  7.4066421e-04 3.66e-06 7.07e+01  -4.1 3.56e-03  -1.0 1.00e+00 1.00e+00h  1
  72  3.3342726e-04 1.51e-05 7.07e+01  -4.0 4.83e-03  -1.5 1.00e+00 1.00e+00h  1
  73  3.5021811e-04 2.50e-10 5.13e-02  -4.0 3.20e-05   2.5 1.00e+00 1.00e+00h  1
  74  3.4992868e-04 6.86e-11 1.19e-03  -4.0 1.03e-05   2.1 1.00e+00 1.00e+00h  1
  75  3.4934236e-04 1.76e-10 5.72e-04  -4.0 1.49e-05   1.6 1.00e+00 1.00e+00h  1
  76  3.4818259e-04 2.89e-10 3.25e-04  -4.0 2.54e-05   1.1 1.00e+00 1.00e+00h  1
  77  3.4610485e-04 1.98e-09 3.21e-04  -4.0 6.08e-05   0.6 1.00e+00 1.00e+00h  1
  78  3.4402420e-04 1.10e-08 3.24e-04  -4.0 1.42e-04   0.2 1.00e+00 1.00e+00h  1
  79  3.4499280e-04 5.64e-08 3.23e-04  -4.0 3.56e-04  -0.3 1.00e+00 1.00e+00h  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  80  3.4402565e-04 2.74e-07 1.51e-04  -4.1 7.09e-04  -0.8 1.00e+00 1.00e+00h  1
  81  3.3631900e-04 2.27e-08 7.07e+01  -6.1 8.74e-04  -1.3 1.00e+00 1.00e+00h  1
  82  3.2881373e-04 2.94e-08 7.07e+01  -6.1 2.63e-04  -1.8 1.00e+00 1.00e+00h  1
  83  3.2999092e-04 4.32e-11 2.90e-02  -4.0 8.04e-06   2.3 1.00e+00 1.00e+00h  1
  84  3.3005556e-04 3.41e-11 4.23e-04  -4.0 6.63e-06   1.8 1.00e+00 1.00e+00h  1
  85  3.3013855e-04 5.40e-11 1.91e-04  -4.1 8.97e-06   1.3 1.00e+00 1.00e+00h  1
  86  3.2971112e-04 2.89e-10 1.99e-04  -6.1 1.55e-05   0.9 1.00e+00 1.00e+00h  1
  87  3.3081095e-04 7.82e-09 5.70e-04  -4.0 1.07e-04   0.4 1.00e+00 1.00e+00h  1
  88  3.3301804e-04 1.20e-08 1.20e-04  -4.1 1.52e-04  -0.1 1.00e+00 1.00e+00h  1
  89  3.2624417e-04 6.23e-09 5.83e-04  -6.1 3.14e-04  -0.6 1.00e+00 1.00e+00h  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  90  3.3558132e-04 1.39e-06 3.95e-03  -4.0 1.64e-03  -1.1 1.00e+00 1.00e+00h  1
  91  3.4235882e-04 4.20e-06 2.75e-03  -4.1 3.25e-03  -1.5 1.00e+00 1.00e+00h  1
  92  3.4120533e-04 1.96e-05 1.53e-02  -4.1 8.31e-03  -2.0 1.00e+00 1.00e+00h  1
  93  3.4853048e-04 7.90e-05 4.41e-02  -4.1 1.87e-02  -2.5 1.00e+00 1.00e+00h  1
  94  5.4028795e-04 1.31e-04 7.07e+01  -4.1 3.40e-02  -3.0 1.00e+00 1.00e+00h  1
  95  4.6883327e-04 1.30e-04 4.18e-01  -4.1 6.31e-01  -2.5 3.99e-01 2.93e-03h  8
  96  5.4123568e-04 1.02e-04 7.06e+01  -4.1 2.52e-02  -3.0 1.00e+00 2.50e-01h  3
  97  4.5061511e-04 9.98e-05 4.78e-01  -4.1 8.40e+00  -3.5 3.47e-02 5.50e-04h  7
  98  6.8568765e-04 8.10e-05 7.06e+01  -4.1 3.13e-02  -3.1 1.00e+00 5.00e-01h  2
  99  5.6112915e-04 7.62e-05 4.72e-01  -4.1 3.34e+00  -3.5 1.20e-01 2.02e-03h  7
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
 100  7.3781311e-04 1.30e-04 7.07e+01  -4.1 3.67e-02  -3.1 1.00e+00 1.00e+00h  1

Number of Iterations....: 100

                                   (scaled)                 (unscaled)
Objective...............:   7.3781311208779361e-04    7.3781311208779361e-04
Dual infeasibility......:   7.0711035025248620e+01    7.0711035025248620e+01
Constraint violation....:   1.3014656127243590e-04    1.3014656127243590e-04
Variable bound violation:   0.0000000000000000e+00    0.0000000000000000e+00
Complementarity.........:   8.7829115609029527e-05    8.7829115609029527e-05
Overall NLP error.......:   7.0711035025248620e+01    7.0711035025248620e+01


Number of objective function evaluations             = 196
Number of objective gradient evaluations             = 101
Number of equality constraint evaluations            = 196
Number of inequality constraint evaluations          = 0
Number of equality constraint Jacobian evaluations   = 101
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations             = 100
Total seconds in IPOPT                               = 414.728

EXIT: Maximum Number of Iterations Exceeded.
# Let's take a look at the final fidelity
fid_final = unitary_rollout_fidelity(prob.trajectory, sys, drive_name=:a)
println(fid_final)
@assert fid_final > 0.99
0.9999987200684024

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.trajectory, control_name=:a)
Example block output

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

min_time_prob = UnitaryMinimumTimeProblem(prob, U_goal; final_fidelity=.995)
DirectTrajOptProblem
   timesteps            = 100
   duration             = 375.53104362185144
   variable names       = (:Ũ⃗, :a, :da, :dda, :Δt)
   knot point dimension = 45
solve!(min_time_prob; max_iter=300)
fid_final_min_time = unitary_rollout_fidelity(min_time_prob.trajectory, sys, drive_name=:a)
println(fid_final_min_time)
@assert fid_final_min_time > 0.99
0.9949999904078358

And let's plot this solution

plot_unitary_populations(min_time_prob.trajectory, control_name=:a)
Example block output

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

duration = get_duration(prob.trajectory)
min_time_duration = get_duration(min_time_prob.trajectory)
println(duration, " - ", min_time_duration, " = ", duration - min_time_duration)
375.53104362185144 - 355.48583992851627 = 20.045203693335168

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=PiccoloOptions(bound_state=true),
)
fid_init = unitary_rollout_fidelity(prob.trajectory, sys, drive_name=:a)
println(fid_init)
    constructing UnitarySmoothPulseProblem...
	using integrator: typeof(UnitaryIntegrator)
	control derivative names: [:da, :dda]
	applying timesteps_all_equal constraint: Δt
0.12249963901581312
solve!(prob; max_iter=100)
# Let's take a look at the final fidelity
fid_final = unitary_rollout_fidelity(prob.trajectory, sys, drive_name=:a)
println(fid_final)
@assert fid_final > 0.999
0.9999999791551119

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.trajectory, control_name=:a)
Example block output

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

min_time_prob = UnitaryMinimumTimeProblem(prob, U_goal; final_fidelity=.9995)
DirectTrajOptProblem
   timesteps            = 100
   duration             = 337.2645208164223
   variable names       = (:Ũ⃗, :a, :da, :dda, :Δt)
   knot point dimension = 45
solve!(min_time_prob; max_iter=300)
fid_final_min_time = unitary_rollout_fidelity(min_time_prob.trajectory, sys, drive_name=:a)
println(fid_final_min_time)
@assert fid_final_min_time > 0.999
0.9994999872727864

And let's plot this solution

plot_unitary_populations(min_time_prob.trajectory, control_name=:a)
Example block output

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

duration = get_duration(prob.trajectory)
min_time_duration = get_duration(min_time_prob.trajectory)
println(duration, " - ", min_time_duration, " = ", duration - min_time_duration)
337.2645208164223 - 247.87559278517617 = 89.38892803124614

This page was generated using Literate.jl.