Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Robust control objectives #54

Merged
merged 9 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ minimum allowed fidelity.
- `statedim::Union{Int,Nothing}=nothing`: the dimension of the state
- `zdim::Union{Int,Nothing}=nothing`: the dimension of a single time step of the trajectory
- `T::Union{Int,Nothing}=nothing`: the number of time steps
- `subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing`: the subspace indices of the fidelity

"""

Expand All @@ -128,6 +129,7 @@ function FinalFidelityConstraint(;
statedim::Union{Int,Nothing}=nothing,
zdim::Union{Int,Nothing}=nothing,
T::Union{Int,Nothing}=nothing,
subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing
)
@assert !isnothing(fidelity_function) "must provide a fidelity function"
@assert !isnothing(value) "must provide a fidelity value"
Expand All @@ -139,7 +141,11 @@ function FinalFidelityConstraint(;

fidelity_function_symbol = Symbol(fidelity_function)

fid = x -> fidelity_function(x, goal)
if isnothing(subspace)
fid = x -> fidelity_function(x, goal)
else
fid = x -> fidelity_function(x, goal; subspace=subspace)
end

@assert fid(randn(statedim)) isa Float64 "fidelity function must return a scalar"

Expand All @@ -157,6 +163,7 @@ function FinalFidelityConstraint(;
params[:statedim] = statedim
params[:zdim] = zdim
params[:T] = T
params[:subspace] = subspace
end

state_slice = slice(T, comps, zdim)
Expand Down Expand Up @@ -226,7 +233,8 @@ is the NamedTrajectory symbol representing the unitary.
function FinalUnitaryFidelityConstraint(
statesymb::Symbol,
val::Float64,
traj::NamedTrajectory
traj::NamedTrajectory;
subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing
)
@assert statesymb ∈ traj.names
return FinalFidelityConstraint(;
Expand All @@ -236,7 +244,8 @@ function FinalUnitaryFidelityConstraint(
goal=traj.goal[statesymb],
statedim=traj.dims[statesymb],
zdim=traj.dim,
T=traj.T
T=traj.T,
subspace=subspace
)
end

Expand Down
117 changes: 117 additions & 0 deletions src/objectives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ export QuantumUnitaryObjective
export UnitaryInfidelityObjective

export MinimumTimeObjective
export InfidelityRobustnessObjective

export QuadraticRegularizer
export QuadraticSmoothnessRegularizer
export L1Regularizer

using TrajectoryIndexingUtils
using ..QuantumUtils
using ..QuantumSystems
using ..Losses
using ..Constraints
Expand Down Expand Up @@ -662,4 +664,119 @@ function MinimumTimeObjective(traj::NamedTrajectory; D=1.0)
return MinimumTimeObjective(; D=D, Δt_indices=Δt_indices)
end

@doc raw"""
InfidelityRobustnessObjective(
Hₑ::AbstractMatrix{<:Number},
Z::NamedTrajectory;
eval_hessian::Bool=false,
subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing
)

Create a control objective which penalizes the sensitivity of the infidelity
to the provided operator defined in the subspace of the control dynamics,
thereby realizing robust control.

The control dynamics are
```math
U_C(a)= \prod_t \exp{-i H_C(a_t)}
```

In the control frame, the Hₑ operator is (proportional to)
```math
R_{Robust}(a) = \frac{1}{T \norm{H_e}_2} \sum_t U_C(a_t)^\dag H_e U_C(a_t) \Delta t
```
where we have adjusted to a unitless expression of the operator.

The robustness objective is
```math
R_{Robust}(a) = \frac{1}{N} \norm{R}^2_F
```
where N is the dimension of the Hilbert space.
"""
function InfidelityRobustnessObjective(
Hₑ::AbstractMatrix{<:Number},
Z::NamedTrajectory;
eval_hessian::Bool=false,
subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing
)
# Indices of all non-zero subspace components for iso_vec_operators
function iso_vec_subspace(subspace::AbstractVector{<:Integer}, Z::NamedTrajectory)
d = isqrt(Z.dims[:Ũ⃗] ÷ 2)
A = zeros(Complex, d, d)
A[subspace, subspace] .= 1 + im
# Return any index where there is a 1.
return [j for (s, j) ∈ zip(operator_to_iso_vec(A), Z.components[:Ũ⃗]) if convert(Bool, s)]
end
ivs = iso_vec_subspace(isnothing(subspace) ? collect(1:size(Hₑ, 1)) : subspace, Z)

@views function timesteps(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory)
return map(1:Z.T) do t
if Z.timestep isa Symbol
Z⃗[slice(t, Z.components[Z.timestep], Z.dim)][1]
else
Z.timestep
end
end
end

# Control frame
@views function toggle(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory)
Δts = timesteps(Z⃗, Z)
T = sum(Δts)
R = sum(
map(1:Z.T) do t
Uₜ = iso_vec_to_operator(Z⃗[slice(t, ivs, Z.dim)])
Uₜ'Hₑ*Uₜ .* Δts[t]
end
) / norm(Hₑ) / T
return R
end

function L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory)
R = toggle(Z⃗, Z)
return real(tr(R'R)) / size(R, 1)
end

@views function ∇L(Z⃗::AbstractVector{<:Real}, Z::NamedTrajectory)
∇ = zeros(Z.dim * Z.T)
R = toggle(Z⃗, Z)
Δts = timesteps(Z⃗, Z)
T = sum(Δts)
Threads.@threads for t ∈ 1:Z.T
# State gradients
Uₜ_slice = slice(t, ivs, Z.dim)
Uₜ = iso_vec_to_operator(Z⃗[Uₜ_slice])
∇[Uₜ_slice] .= 2 .* operator_to_iso_vec(
Hₑ * Uₜ * R .* Δts[t]
) / norm(Hₑ) / T
# Time gradients
if Z.timestep isa Symbol
t_slice = slice(t, Z.components[Z.timestep], Z.dim)
∂R = Uₜ'Hₑ*Uₜ
∇[t_slice] .= tr(∂R*R + R*∂R) / norm(Hₑ) / T
end
end
return ∇ / size(R, 1)
end

# Hessian is dense (Control frame R contains sum over all unitaries).
if eval_hessian
# TODO
∂²L = (Z⃗, Z) -> []
∂²L_structure = Z -> []
else
∂²L = nothing
∂²L_structure = nothing
end

params = Dict(
:type => :QuantumRobustnessObjective,
:error => Hₑ,
:eval_hessian => eval_hessian
)

return Objective(L, ∇L, ∂²L, ∂²L_structure, Dict[params])
end


end
84 changes: 81 additions & 3 deletions src/problem_templates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module ProblemTemplates

export UnitarySmoothPulseProblem
export UnitaryMinimumTimeProblem
export UnitaryRobustnessProblem

export QuantumStateSmoothPulseProblem
export QuantumStateMinimumTimeProblem
Expand Down Expand Up @@ -359,7 +360,7 @@ function UnitaryMinimumTimeProblem(
kwargs...
)
params = deepcopy(prob.params)
traj = copy(prob.trajectory)
trajectory = copy(prob.trajectory)
system = prob.system
objective = Objective(params[:objective_terms])
integrators = prob.integrators
Expand All @@ -368,7 +369,7 @@ function UnitaryMinimumTimeProblem(
NonlinearConstraint.(params[:nonlinear_constraints])...
]
return UnitaryMinimumTimeProblem(
traj,
trajectory,
system,
objective,
integrators,
Expand All @@ -378,7 +379,6 @@ function UnitaryMinimumTimeProblem(
)
end


function UnitaryMinimumTimeProblem(
data_path::String;
kwargs...
Expand All @@ -403,6 +403,84 @@ function UnitaryMinimumTimeProblem(
)
end

function UnitaryRobustnessProblem(
Hₑ::AbstractMatrix{<:Number},
trajectory::NamedTrajectory,
system::QuantumSystem,
objective::Objective,
integrators::Vector{<:AbstractIntegrator},
constraints::Vector{<:AbstractConstraint};
unitary_symbol::Symbol=:Ũ⃗,
final_fidelity::Float64=unitary_fidelity(trajectory[end][unitary_symbol], trajectory.goal[unitary_symbol]),
subspace::Union{AbstractVector{<:Integer}, Nothing}=nothing,
eval_hessian::Bool=false,
verbose::Bool=false,
ipopt_options::Options=Options(),
kwargs...
)
@assert unitary_symbol ∈ trajectory.names

if !eval_hessian
ipopt_options.hessian_approximation = "limited-memory"
end

objective += InfidelityRobustnessObjective(
Hₑ,
trajectory,
eval_hessian=eval_hessian,
subspace=subspace
)

fidelity_constraint = FinalUnitaryFidelityConstraint(
unitary_symbol,
final_fidelity,
trajectory;
subspace=subspace
)

constraints = AbstractConstraint[constraints..., fidelity_constraint]

return QuantumControlProblem(
system,
trajectory,
objective,
integrators;
constraints=constraints,
verbose=verbose,
ipopt_options=ipopt_options,
eval_hessian=eval_hessian,
kwargs...
)
end

function UnitaryRobustnessProblem(
Hₑ::AbstractMatrix{<:Number},
prob::QuantumControlProblem;
kwargs...
)
params = deepcopy(prob.params)
trajectory = copy(prob.trajectory)
system = prob.system
objective = Objective(params[:objective_terms])
integrators = prob.integrators
constraints = [
params[:linear_constraints]...,
NonlinearConstraint.(params[:nonlinear_constraints])...
]

return UnitaryRobustnessProblem(
Hₑ,
trajectory,
system,
objective,
integrators,
constraints;
build_trajectory_constraints=false,
kwargs...
)
end


# ------------------------------------------
# Quantum State Problem Templates
# ------------------------------------------
Expand Down
Loading
Loading