using NamedTrajectories
using PiccoloQuantumObjects
using QuantumCollocation

Problem Templates

We provide a number of problem templates for making it simple and easy to set up and solve certain types of quantum optimal control problems. These templates all construct a DirectTrajOptProblem object, which stores all the parts of the optimal control problem.

This page provides a brief overview of each problem template, broken down by the state of the problem being solved.

Ket Problem Templates:

Unitary Problem Templates:


Ket Problem Templates

Quantum State Smooth Pulse Problem

QuantumCollocation.ProblemTemplates.QuantumStateSmoothPulseProblemFunction
QuantumStateSmoothPulseProblem(system, ψ_inits, ψ_goals, T, Δt; kwargs...)
QuantumStateSmoothPulseProblem(system, ψ_init, ψ_goal, T, Δt; kwargs...)
QuantumStateSmoothPulseProblem(H_drift, H_drives, args...; kwargs...)

Create a quantum state smooth pulse problem. The goal is to find a control pulse a(t) that drives all of the initial states ψ_inits to the corresponding target states ψ_goals using T timesteps of size Δt. This problem also controls the first and second derivatives of the control pulse, da(t) and dda(t), to ensure smoothness.

Arguments

  • system::AbstractQuantumSystem: The quantum system.

or

  • H_drift::AbstractMatrix{<:Number}: The drift Hamiltonian.
  • H_drives::Vector{<:AbstractMatrix{<:Number}}: The control Hamiltonians.

with

  • ψ_inits::Vector{<:AbstractVector{<:ComplexF64}}: The initial states.
  • ψ_goals::Vector{<:AbstractVector{<:ComplexF64}}: The target states.

or

  • ψ_init::AbstractVector{<:ComplexF64}: The initial state.
  • ψ_goal::AbstractVector{<:ComplexF64}: The target state.

with

  • T::Int: The number of timesteps.
  • Δt::Float64: The timestep size.

Keyword Arguments

  • state_name::Symbol=:ψ̃: The name of the state variable.
  • control_name::Symbol=:a: The name of the control variable.
  • timestep_name::Symbol=:Δt: The name of the timestep variable.
  • init_trajectory::Union{NamedTrajectory, Nothing}=nothing: The initial trajectory.
  • a_bound::Float64=1.0: The bound on the control pulse.
  • a_bounds::Vector{Float64}=fill(a_bound, length(system.G_drives)): The bounds on the control pulse.
  • a_guess::Union{Matrix{Float64}, Nothing}=nothing: The initial guess for the control pulse.
  • da_bound::Float64=Inf: The bound on the first derivative of the control pulse.
  • da_bounds::Vector{Float64}=fill(da_bound, length(system.G_drives)): The bounds on the first derivative of the control pulse.
  • dda_bound::Float64=1.0: The bound on the second derivative of the control pulse.
  • dda_bounds::Vector{Float64}=fill(dda_bound, length(system.G_drives)): The bounds on the second derivative of the control pulse.
  • Δt_min::Float64=0.5 * Δt: The minimum timestep size.
  • Δt_max::Float64=1.5 * Δt: The maximum timestep size.
  • drive_derivative_σ::Float64=0.01: The standard deviation of the drive derivative random initialization.
  • Q::Float64=100.0: The weight on the state objective.
  • R=1e-2: The weight on the control pulse and its derivatives.
  • R_a::Union{Float64, Vector{Float64}}=R: The weight on the control pulse.
  • R_da::Union{Float64, Vector{Float64}}=R: The weight on the first derivative of the control pulse.
  • R_dda::Union{Float64, Vector{Float64}}=R: The weight on the second derivative of the control pulse.
  • constraints::Vector{<:AbstractConstraint}=AbstractConstraint[]: The constraints.
  • piccolo_options::PiccoloOptions=PiccoloOptions(): The Piccolo options.
source

Each problem starts with a QuantumSystem object, which is used to define the system's Hamiltonian and control operators. The goal is to find a control pulse that drives the intial state, ψ_init, to a target state, ψ_goal.

define the quantum system

system = QuantumSystem(0.1 * PAULIS.Z, [PAULIS.X, PAULIS.Y])
ψ_init = Vector{ComplexF64}([1.0, 0.0])
ψ_goal = Vector{ComplexF64}([0.0, 1.0])
T = 51
Δt = 0.2
0.2

create the smooth pulse problem

state_prob = QuantumStateSmoothPulseProblem(system, ψ_init, ψ_goal, T, Δt);
    constructing QuantumStateSmoothPulseProblem...
	using integrator: typeof(KetIntegrator)
	using 1 initial state(s)
	applying timesteps_all_equal constraint: Δt

check the fidelity before solving

println("Before: ", rollout_fidelity(state_prob.trajectory, system))
Before: 0.2702650322149119

solve the problem

solve!(state_prob, max_iter=100, verbose=true, print_level=1);
    initializing optimizer...
        applying constraint: timesteps all equal constraint
        applying constraint: initial value of ψ̃
        applying constraint: initial value of a
        applying constraint: initial value of da
        applying constraint: final value of a
        applying constraint: final value of da
        applying constraint: bounds on a
        applying constraint: bounds on da
        applying constraint: bounds on dda
        applying constraint: bounds on Δt

******************************************************************************
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
******************************************************************************

check the fidelity after solving

println("After: ", rollout_fidelity(state_prob.trajectory, system))
After: 0.9999999999792313

extract the control pulses

state_prob.trajectory.a |> size
(2, 51)

Quantum State Minimum Time Problem

QuantumCollocation.ProblemTemplates.QuantumStateMinimumTimeProblemFunction
QuantumStateMinimumTimeProblem(traj, sys, obj, integrators, constraints; kwargs...)
QuantumStateMinimumTimeProblem(prob; kwargs...)

Construct a DirectTrajOptProblem for the minimum time problem of reaching a target state.

Keyword Arguments

  • state_name::Symbol=:ψ̃: The symbol for the state variables.
  • final_fidelity::Union{Real, Nothing}=nothing: The final fidelity.
  • D=1.0: The cost weight on the time.
  • piccolo_options::PiccoloOptions=PiccoloOptions(): The Piccolo options.
source

create the minimum time problem

min_state_prob = QuantumStateMinimumTimeProblem(state_prob, ψ_goal);
    constructing QuantumStateMinimumTimeProblem...
	final fidelity: 1.0

check the previous duration

println("Duration before: ", get_duration(state_prob.trajectory))
Duration before: 10.46338258283023

solve the minimum time problem

solve!(min_state_prob, max_iter=100, verbose=true, print_level=1);
    initializing optimizer...
        applying constraint: timesteps all equal constraint
        applying constraint: initial value of ψ̃
        applying constraint: initial value of a
        applying constraint: initial value of da
        applying constraint: final value of a
        applying constraint: final value of da
        applying constraint: bounds on a
        applying constraint: bounds on da
        applying constraint: bounds on dda
        applying constraint: bounds on Δt

check the new duration

println("Duration after: ", get_duration(min_state_prob.trajectory))
Duration after: 3.681381318546689

the fidelity is preserved by a constraint

println("Fidelity after: ", rollout_fidelity(min_state_prob.trajectory, system))
Fidelity after: 0.999985409162291

Quantum State Sampling Problem

create a sampling problem

driftless_system = QuantumSystem([PAULIS.X, PAULIS.Y])
sampling_state_prob = QuantumStateSamplingProblem([system, driftless_system], ψ_init, ψ_goal, T, Δt);
    constructing QuantumStateSamplingProblem...
	using integrator: typeof(KetIntegrator)
	using 2 initial state(s)
	applying timesteps_all_equal constraint: Δt

new keys are added to the trajectory for the new states

println(sampling_state_prob.trajectory.state_names)
(:ψ̃1_system_1, :a, :da, :ψ̃1_system_2)

solve the sampling problem for a few iterations

solve!(sampling_state_prob, max_iter=25, verbose=true, print_level=1);
    initializing optimizer...
        applying constraint: timesteps all equal constraint
        applying constraint: initial value of ψ̃1_system_1
        applying constraint: initial value of a
        applying constraint: initial value of da
        applying constraint: initial value of ψ̃1_system_2
        applying constraint: final value of a
        applying constraint: final value of da
        applying constraint: bounds on a
        applying constraint: bounds on da
        applying constraint: bounds on dda
        applying constraint: bounds on Δt

check the fidelity of the sampling problem (use the updated key to get the initial and goal)

println("After (original system): ", rollout_fidelity(sampling_state_prob.trajectory, system, state_name=:ψ̃1_system_1))
println("After (new system): ", rollout_fidelity(sampling_state_prob.trajectory, driftless_system, state_name=:ψ̃1_system_1))
After (original system): 0.9877387017241955
After (new system): 0.9885756196665351

compare this to using the original problem on the new system

println("After (new system, original `prob`): ", rollout_fidelity(state_prob.trajectory, driftless_system))
After (new system, original `prob`): 0.7760663640031801

Unitary Problem Templates

Unitary Smooth Pulse Problem

QuantumCollocation.ProblemTemplates.UnitarySmoothPulseProblemFunction
UnitarySmoothPulseProblem(system::AbstractQuantumSystem, operator, T, Δt; kwargs...)
UnitarySmoothPulseProblem(H_drift, H_drives, operator, T, Δt; kwargs...)

Construct a DirectTrajOptProblem for a free-time unitary gate problem with smooth control pulses enforced by constraining the second derivative of the pulse trajectory, i.e.,

\[\begin{aligned} \underset{\vec{\tilde{U}}, a, \dot{a}, \ddot{a}, \Delta t}{\text{minimize}} & \quad Q \cdot \ell\qty(\vec{\tilde{U}}_T, \vec{\tilde{U}}_{\text{goal}}) + \frac{1}{2} \sum_t \qty(R_a a_t^2 + R_{\dot{a}} \dot{a}_t^2 + R_{\ddot{a}} \ddot{a}_t^2) \\ \text{ subject to } & \quad \vb{P}^{(n)}\qty(\vec{\tilde{U}}_{t+1}, \vec{\tilde{U}}_t, a_t, \Delta t_t) = 0 \\ & \quad a_{t+1} - a_t - \dot{a}_t \Delta t_t = 0 \\ & \quad \dot{a}_{t+1} - \dot{a}_t - \ddot{a}_t \Delta t_t = 0 \\ & \quad |a_t| \leq a_{\text{bound}} \\ & \quad |\ddot{a}_t| \leq \ddot{a}_{\text{bound}} \\ & \quad \Delta t_{\text{min}} \leq \Delta t_t \leq \Delta t_{\text{max}} \\ \end{aligned}\]

where, for $U \in SU(N)$,

\[\ell\qty(\vec{\tilde{U}}_T, \vec{\tilde{U}}_{\text{goal}}) = \abs{1 - \frac{1}{N} \abs{ \tr \qty(U_{\text{goal}}, U_T)} }\]

is the infidelity objective function, $Q$ is a weight, $R_a$, $R_{\dot{a}}$, and $R_{\ddot{a}}$ are weights on the regularization terms, and $\vb{P}^{(n)}$ is the $n$th-order Pade integrator.

Arguments

  • system::AbstractQuantumSystem: the system to be controlled

or

  • H_drift::AbstractMatrix{<:Number}: the drift hamiltonian
  • H_drives::Vector{<:AbstractMatrix{<:Number}}: the control hamiltonians

with

  • goal::AbstractPiccoloOperator: the target unitary, either in the form of an EmbeddedOperator or a `Matrix{ComplexF64}
  • T::Int: the number of timesteps
  • Δt::Float64: the (initial) time step size

Keyword Arguments

  • piccolo_options::PiccoloOptions=PiccoloOptions(): the options for the Piccolo solver
  • state_name::Symbol = :Ũ⃗: the name of the state
  • control_name::Symbol = :a: the name of the control
  • timestep_name::Symbol = :Δt: the name of the timestep
  • init_trajectory::Union{NamedTrajectory, Nothing}=nothing: an initial trajectory to use
  • a_guess::Union{Matrix{Float64}, Nothing}=nothing: an initial guess for the control pulses
  • a_bound::Float64=1.0: the bound on the control pulse
  • a_bounds::Vector{Float64}=fill(a_bound, length(system.G_drives)): the bounds on the control pulses, one for each drive
  • da_bound::Float64=Inf: the bound on the control pulse derivative
  • da_bounds::Vector{Float64}=fill(da_bound, length(system.G_drives)): the bounds on the control pulse derivatives, one for each drive
  • dda_bound::Float64=1.0: the bound on the control pulse second derivative
  • dda_bounds::Vector{Float64}=fill(dda_bound, length(system.G_drives)): the bounds on the control pulse second derivatives, one for each drive
  • Δt_min::Float64=Δt isa Float64 ? 0.5 * Δt : 0.5 * mean(Δt): the minimum time step size
  • Δt_max::Float64=Δt isa Float64 ? 1.5 * Δt : 1.5 * mean(Δt): the maximum time step size
  • Q::Float64=100.0: the weight on the infidelity objective
  • R=1e-2: the weight on the regularization terms
  • R_a::Union{Float64, Vector{Float64}}=R: the weight on the regularization term for the control pulses
  • R_da::Union{Float64, Vector{Float64}}=R: the weight on the regularization term for the control pulse derivatives
  • R_dda::Union{Float64, Vector{Float64}}=R: the weight on the regularization term for the control pulse second derivatives
  • constraints::Vector{<:AbstractConstraint}=AbstractConstraint[]: the constraints to enforce
source

The UnitarySmoothPulseProblem is similar to the QuantumStateSmoothPulseProblem, but instead of driving the system to a target state, the goal is to drive the system to a target unitary operator, U_goal.

system = QuantumSystem(0.1 * PAULIS.Z, [PAULIS.X, PAULIS.Y])
U_goal = GATES.H
T = 51
Δt = 0.2

prob = UnitarySmoothPulseProblem(system, U_goal, T, Δt);
    constructing UnitarySmoothPulseProblem...
	using integrator: typeof(UnitaryIntegrator)
	control derivative names: [:da, :dda]
	applying timesteps_all_equal constraint: Δt

check the fidelity before solving

println("Before: ", unitary_rollout_fidelity(prob.trajectory, system))
Before: 0.22043303242736734

finding an optimal control is as simple as calling solve!

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

check the fidelity after solving

println("After: ", unitary_rollout_fidelity(prob.trajectory, system))
After: 0.9996060497974855

The NamedTrajectory object stores the control pulse, state variables, and the time grid.

extract the control pulses

prob.trajectory.a |> size
(2, 51)

Unitary Minimum Time Problem

QuantumCollocation.ProblemTemplates.UnitaryMinimumTimeProblemFunction
UnitaryMinimumTimeProblem(
    goal::AbstractPiccoloOperator,
    trajectory::NamedTrajectory,
    objective::Objective,
    dynamics::TrajectoryDynamics,
    constraints::AbstractVector{<:AbstractConstraint};
    kwargs...
)

UnitaryMinimumTimeProblem(
    goal::AbstractPiccoloOperator,
    prob::DirectTrajOptProblem;
    kwargs...
)

Create a minimum-time problem for unitary control.

\[\begin{aligned} \underset{\vec{\tilde{U}}, a, \dot{a}, \ddot{a}, \Delta t}{\text{minimize}} & \quad J(\vec{\tilde{U}}, a, \dot{a}, \ddot{a}) + D \sum_t \Delta t_t \\ \text{ subject to } & \quad \vb{P}^{(n)}\qty(\vec{\tilde{U}}_{t+1}, \vec{\tilde{U}}_t, a_t, \Delta t_t) = 0 \\ & c(\vec{\tilde{U}}, a, \dot{a}, \ddot{a}) = 0 \\ & \quad \Delta t_{\text{min}} \leq \Delta t_t \leq \Delta t_{\text{max}} \\ \end{aligned}\]

Keyword Arguments

  • piccolo_options::PiccoloOptions=PiccoloOptions(): The Piccolo options.
  • unitary_name::Symbol=:Ũ⃗: The name of the unitary for the goal.
  • final_fidelity::Float64=1.0: The final fidelity constraint.
  • D::Float64=1.0: The scaling factor for the minimum-time objective.
source

The goal of this problem is to find the shortest time it takes to drive the system to a target unitary operator, U_goal. The problem is solved by minimizing the sum of all of the time steps. It is constructed from prob in the previous example.

min_prob = UnitaryMinimumTimeProblem(prob, U_goal);
    constructing UnitaryMinimumTimeProblem...
	final fidelity: 1.0

check the previous duration

println("Duration before: ", get_duration(prob.trajectory))
Duration before: 8.369711303490133

solve the minimum time problem

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

check the new duration

println("Duration after: ", get_duration(min_prob.trajectory))
Duration after: 5.775410534511182

the fidelity is preserved by a constraint

println("Fidelity after: ", unitary_rollout_fidelity(min_prob.trajectory, system))
Fidelity after: 0.999999996816126

Unitary Sampling Problem

QuantumCollocation.ProblemTemplates.UnitarySamplingProblemFunction
UnitarySamplingProblem(systemns, operator, T, Δt; kwargs...)

A UnitarySamplingProblem is a quantum control problem where the goal is to find a control pulse that generates a target unitary operator for a set of quantum systems. The controls are shared among all systems, and the optimization seeks to find the control pulse that achieves the operator for each system. The idea is to enforce a robust solution by including multiple systems reflecting the problem uncertainty.

Arguments

  • systems::AbstractVector{<:AbstractQuantumSystem}: A vector of quantum systems.
  • operators::AbstractVector{<:AbstractPiccoloOperator}: A vector of target operators.
  • T::Int: The number of time steps.
  • Δt::Union{Float64, Vector{Float64}}: The time step value or vector of time steps.

Keyword Arguments

  • system_labels::Vector{String} = string.(1:length(systems)): The labels for each system.
  • system_weights::Vector{Float64} = fill(1.0, length(systems)): The weights for each system.
  • init_trajectory::Union{NamedTrajectory, Nothing} = nothing: The initial trajectory.
  • state_name::Symbol = :Ũ⃗: The name of the state variable.
  • control_name::Symbol = :a: The name of the control variable.
  • timestep_name::Symbol = :Δt: The name of the timestep variable.
  • constraints::Vector{<:AbstractConstraint} = AbstractConstraint[]: The constraints.
  • a_bound::Float64 = 1.0: The bound for the control amplitudes.
  • a_bounds::Vector{Float64} = fill(a_bound, length(systems[1].G_drives)): The bounds for the control amplitudes.
  • a_guess::Union{Matrix{Float64}, Nothing} = nothing: The initial guess for the control amplitudes.
  • da_bound::Float64 = Inf: The bound for the control first derivatives.
  • da_bounds::Vector{Float64} = fill(da_bound, length(systems[1].G_drives)): The bounds for the control first derivatives.
  • dda_bound::Float64 = 1.0: The bound for the control second derivatives.
  • dda_bounds::Vector{Float64} = fill(dda_bound, length(systems[1].G_drives)): The bounds for the control second derivatives.
  • Δt_min::Float64 = 0.5 * Δt: The minimum time step size.
  • Δt_max::Float64 = 1.5 * Δt: The maximum time step size.
  • Q::Float64 = 100.0: The fidelity weight.
  • R::Float64 = 1e-2: The regularization weight.
  • R_a::Union{Float64, Vector{Float64}} = R: The regularization weight for the control amplitudes.
  • R_da::Union{Float64, Vector{Float64}} = R: The regularization weight for the control first derivatives.
  • R_dda::Union{Float64, Vector{Float64}} = R: The regularization weight for the control second derivatives.
  • piccolo_options::PiccoloOptions = PiccoloOptions(): The Piccolo options.
source

A sampling problem is used to solve over multiple quantum systems with the same control. This can be useful for exploring robustness, for example.

create a sampling problem

driftless_system = QuantumSystem([PAULIS.X, PAULIS.Y])
sampling_prob = UnitarySamplingProblem([system, driftless_system], U_goal, T, Δt);
    constructing UnitarySamplingProblem...
	using integrator: typeof(UnitaryIntegrator)
	using 2 systems
	applying timesteps_all_equal constraint: Δt

new keys are addded to the trajectory for the new states

println(sampling_prob.trajectory.state_names)
(:Ũ⃗_system_1, :a, :da, :Ũ⃗_system_2)

the solve! proceeds as in the Quantum State Sampling Problem]


This page was generated using Literate.jl.