Pulse Parameterization for Krotov's Method
$\gdef\op#1{\hat{#1}}$ $\gdef\init{\text{init}}$ $\gdef\tgt{\text{tgt}}$
This example illustrates the parameterization of control pulses as a form of amplitude constraint.
datadir(names...) = joinpath(@__DIR__, names...);
using QuantumControl
using QuantumControl.Shapes: flattop
using QuantumControl.Generators
using QuantumControl.Controls
using QuantumControl.PulseParameterizations:
SquareParameterization,
TanhParameterization,
TanhSqParameterization,
LogisticParameterization,
LogisticSqParameterization,
ParameterizedAmplitude
using QuantumPropagators: ExpProp
using LinearAlgebra
using Plots
Plots.default(
linewidth = 3,
size = (550, 300),
legend = :top,
foreground_color_legend = nothing,
background_color_legend = RGBA(1, 1, 1, 0.8),
)
Parameterizations
Symmetric Bounded Controls
Positive (Bounded) Controls
Two-level Hamiltonian
We consider the Hamiltonian $\op{H}_{0} = - \frac{\omega}{2} \op{\sigma}_{z}$, representing a simple qubit with energy level splitting $\omega$ in the basis $\{\ket{0},\ket{1}\}$. The control field $\epsilon(t)$ is assumed to couple via the Hamiltonian $\op{H}_{1}(t) = \epsilon(t) \op{\sigma}_{x}$ to the qubit, i.e., the control field effectively drives transitions between both qubit states.
We we will use
ϵ(t) = 0.2 * flattop(t, T=5, t_rise=0.3, func=:blackman);
"""Two-level-system Hamiltonian."""
function tls_hamiltonian(; Ω=1.0, ampl=ϵ)
σ̂_z = ComplexF64[
1 0
0 -1
]
σ̂_x = ComplexF64[
0 1
1 0
]
Ĥ₀ = -0.5 * Ω * σ̂_z
Ĥ₁ = σ̂_x
return hamiltonian(Ĥ₀, (Ĥ₁, ampl))
end;
H = tls_hamiltonian();
The control field here switches on from zero at $t=0$ to it's maximum amplitude 0.2 within the time period 0.3 (the switch-on shape is half a Blackman pulse). It switches off again in the time period 0.3 before the final time $T=5$). We use a time grid with 500 time steps between 0 and $T$:
tlist = collect(range(0, 5, length=500));
function plot_amplitude(ampl, tlist)
plot(tlist, discretize(ampl, tlist), xlabel="time", ylabel="amplitude", legend=false)
end
fig = plot_amplitude(ϵ, tlist)
Optimization target
The Krotov
package requires the optimization to be expressed over a set of "trajectories". In this example, there is only a single trajectory: the state-to-state transfer from initial state $\ket{\Psi_{\init}} = \ket{0}$ to the target state $\ket{\Psi_{\tgt}} = \ket{1}$, under the dynamics of the Hamiltonian $\op{H}(t)$:
function ket(label)
result = Dict("0" => Vector{ComplexF64}([1, 0]), "1" => Vector{ComplexF64}([0, 1]),)
return result[string(label)]
end;
trajectories = [Trajectory(ket(0), H, target_state=ket(1))]
1-element Vector{QuantumControl.Trajectory{Vector{ComplexF64}, QuantumPropagators.Generators.Generator{Matrix{ComplexF64}, typeof(Main.var"##230".ϵ)}}}:
Trajectory with 2-element Vector{ComplexF64} initial state, Generator with 2 ops and 1 amplitudes, 2-element Vector{ComplexF64} target state
Square-parameterization for positive pulses
a = ParameterizedAmplitude(
ϵ,
tlist;
parameterization=SquareParameterization(),
parameterize=true
)
ParameterizedAmplitude(::Vector{Float64}; parameterization=SquareParameterization())
function plot_amplitude(ampl::ParameterizedAmplitude, tlist)
plot(
tlist,
discretize(Array(ampl), tlist),
xlabel="time",
ylabel="amplitude",
legend=false
)
end
fig = plot_amplitude(a, tlist)
problem = ControlProblem(
trajectories=substitute(trajectories, IdDict(ϵ => a)),
prop_method=ExpProp,
lambda_a=5,
update_shape=(t -> flattop(t, T=5, t_rise=0.3, func=:blackman)),
tlist=tlist,
iter_stop=50,
J_T=QuantumControl.Functionals.J_T_ss,
check_convergence=res -> begin
((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³"))
end
);
using Krotov
opt_result_positive = @optimize_or_load(
datadir("parameterization#opt_result_positive.jld2"),
problem;
method=Krotov
);
iter. J_T ∫gₐ(t)dt J ΔJ_T ΔJ secs
0 9.51e-01 0.00e+00 9.51e-01 n/a n/a 1.6
[ Info: Set callback to store result in parameterization#opt_result_positive.jld2 on unexpected exit.
1 9.31e-01 9.29e-03 9.40e-01 -2.05e-02 -1.12e-02 0.3
2 9.01e-01 1.34e-02 9.15e-01 -2.96e-02 -1.63e-02 0.0
3 8.59e-01 1.91e-02 8.78e-01 -4.25e-02 -2.34e-02 0.0
4 7.99e-01 2.67e-02 8.26e-01 -5.96e-02 -3.29e-02 0.0
5 7.20e-01 3.56e-02 7.56e-01 -7.91e-02 -4.35e-02 0.0
6 6.25e-01 4.33e-02 6.68e-01 -9.52e-02 -5.19e-02 0.0
7 5.25e-01 4.62e-02 5.72e-01 -9.96e-02 -5.33e-02 0.0
8 4.36e-01 4.25e-02 4.79e-01 -8.92e-02 -4.66e-02 0.0
9 3.65e-01 3.49e-02 4.00e-01 -7.14e-02 -3.64e-02 0.0
10 3.10e-01 2.74e-02 3.37e-01 -5.48e-02 -2.74e-02 0.0
11 2.68e-01 2.14e-02 2.89e-01 -4.24e-02 -2.09e-02 0.0
12…
37 5.05e-02 8.59e-04 5.13e-02 -1.70e-03 -8.45e-04 0.0
38 4.89e-02 8.05e-04 4.97e-02 -1.60e-03 -7.93e-04 0.0
39 4.74e-02 7.56e-04 4.81e-02 -1.50e-03 -7.45e-04 0.0
40 4.60e-02 7.12e-04 4.67e-02 -1.41e-03 -7.02e-04 0.0
41 4.46e-02 6.71e-04 4.53e-02 -1.33e-03 -6.62e-04 0.0
42 4.34e-02 6.34e-04 4.40e-02 -1.26e-03 -6.26e-04 0.0
43 4.22e-02 5.99e-04 4.28e-02 -1.19e-03 -5.92e-04 0.0
44 4.11e-02 5.68e-04 4.16e-02 -1.13e-03 -5.61e-04 0.0
45 4.00e-02 5.38e-04 4.05e-02 -1.07e-03 -5.32e-04 0.0
46 3.90e-02 5.11e-04 3.95e-02 -1.02e-03 -5.06e-04 0.0
47 3.80e-02 4.86e-04 3.85e-02 -9.67e-04 -4.81e-04 0.0
48 3.71e-02 4.63e-04 3.75e-02 -9.21e-04 -4.58e-04 0.0
49 3.62e-02 4.41e-04 3.66e-02 -8.78e-04 -4.37e-04 0.0
50 3.54e-02 4.21e-04 3.58e-02 -8.38e-04 -4.17e-04 0.0
opt_result_positive
Krotov Optimization Result
--------------------------
- Started at 2024-12-02T19:20:57.435
- Number of trajectories: 1
- Number of iterations: 50
- Value of functional: 3.53585e-02
- Reason for termination: Reached maximum number of iterations
- Ended at 2024-12-02T19:20:59.686 (2 seconds, 251 milliseconds)
We can plot the optimized field:
fig = plot_amplitude(
substitute(a, IdDict(a.control => opt_result_positive.optimized_controls[1])),
tlist
)
Tanh-Square-Parameterization for positive amplitude-constrained pulses
a = ParameterizedAmplitude(
ϵ,
tlist;
parameterization=TanhSqParameterization(3),
parameterize=true
)
problem_tanhsq = ControlProblem(
trajectories=substitute(trajectories, IdDict(ϵ => a)),
prop_method=ExpProp,
lambda_a=10,
update_shape=(t -> flattop(t, T=5, t_rise=0.3, func=:blackman)),
tlist=tlist,
iter_stop=50,
J_T=QuantumControl.Functionals.J_T_ss,
check_convergence=res -> begin
((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³"))
end
);
opt_result_tanhsq = @optimize_or_load(
datadir("parameterization#opt_result_tanhsq.jld2"),
problem_tanhsq;
method=Krotov
);
iter. J_T ∫gₐ(t)dt J ΔJ_T ΔJ secs
0 9.51e-01 0.00e+00 9.51e-01 n/a n/a 0.1
[ Info: Set callback to store result in parameterization#opt_result_tanhsq.jld2 on unexpected exit.
1 9.24e-01 1.21e-02 9.36e-01 -2.76e-02 -1.54e-02 0.0
2 8.81e-01 1.90e-02 9.00e-01 -4.32e-02 -2.42e-02 0.0
3 8.15e-01 2.88e-02 8.44e-01 -6.54e-02 -3.65e-02 0.0
4 7.24e-01 4.06e-02 7.65e-01 -9.08e-02 -5.02e-02 0.0
5 6.16e-01 4.99e-02 6.65e-01 -1.09e-01 -5.90e-02 0.0
6 5.08e-01 5.11e-02 5.59e-01 -1.08e-01 -5.66e-02 0.0
7 4.19e-01 4.38e-02 4.62e-01 -8.92e-02 -4.54e-02 0.0
8 3.52e-01 3.37e-02 3.85e-01 -6.69e-02 -3.33e-02 0.0
9 3.02e-01 2.51e-02 3.28e-01 -4.92e-02 -2.41e-02 0.0
10 2.66e-01 1.89e-02 2.84e-01 -3.69e-02 -1.79e-02 0.0
11 2.37e-01 1.46e-02 2.52e-01 -2.84e-02 -1.38e-02 0.0
12 …
37 8.06e-02 7.52e-04 8.14e-02 -1.49e-03 -7.37e-04 0.0
38 7.92e-02 7.10e-04 8.00e-02 -1.41e-03 -6.96e-04 0.0
39 7.79e-02 6.72e-04 7.86e-02 -1.33e-03 -6.59e-04 0.0
40 7.67e-02 6.36e-04 7.73e-02 -1.26e-03 -6.24e-04 0.0
41 7.55e-02 6.03e-04 7.61e-02 -1.20e-03 -5.92e-04 0.0
42 7.43e-02 5.73e-04 7.49e-02 -1.14e-03 -5.63e-04 0.0
43 7.32e-02 5.45e-04 7.38e-02 -1.08e-03 -5.35e-04 0.0
44 7.22e-02 5.19e-04 7.27e-02 -1.03e-03 -5.10e-04 0.0
45 7.12e-02 4.95e-04 7.17e-02 -9.81e-04 -4.87e-04 0.0
46 7.03e-02 4.72e-04 7.08e-02 -9.37e-04 -4.65e-04 0.0
47 6.94e-02 4.52e-04 6.98e-02 -8.96e-04 -4.44e-04 0.0
48 6.85e-02 4.32e-04 6.90e-02 -8.57e-04 -4.25e-04 0.0
49 6.77e-02 4.14e-04 6.81e-02 -8.21e-04 -4.07e-04 0.0
50 6.69e-02 3.97e-04 6.73e-02 -7.87e-04 -3.91e-04 0.0
opt_result_tanhsq
Krotov Optimization Result
--------------------------
- Started at 2024-12-02T19:21:00.750
- Number of trajectories: 1
- Number of iterations: 50
- Value of functional: 6.69321e-02
- Reason for termination: Reached maximum number of iterations
- Ended at 2024-12-02T19:21:01.147 (397 milliseconds)
We can plot the optimized field:
fig = plot_amplitude(
substitute(a, IdDict(a.control => opt_result_tanhsq.optimized_controls[1])),
tlist
)
Logistic-Square-Parameterization for positive amplitude-constrained pulses
a = ParameterizedAmplitude(
ϵ,
tlist;
parameterization=LogisticSqParameterization(3, k=1.0),
parameterize=true
)
problem_logisticsq = ControlProblem(
trajectories=substitute(trajectories, IdDict(ϵ => a)),
prop_method=ExpProp,
lambda_a=1,
update_shape=(t -> flattop(t, T=5, t_rise=0.3, func=:blackman)),
tlist=tlist,
iter_stop=50,
J_T=QuantumControl.Functionals.J_T_ss,
check_convergence=res -> begin
((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³"))
end
);
opt_result_logisticsq = @optimize_or_load(
datadir("parameterization#opt_result_logisticsq.jld2"),
problem_logisticsq;
method=Krotov
);
iter. J_T ∫gₐ(t)dt J ΔJ_T ΔJ secs
0 9.51e-01 0.00e+00 9.51e-01 n/a n/a 0.1
[ Info: Set callback to store result in parameterization#opt_result_logisticsq.jld2 on unexpected exit.
1 8.72e-01 2.97e-02 9.02e-01 -7.91e-02 -4.93e-02 0.0
2 6.93e-01 6.94e-02 7.63e-01 -1.79e-01 -1.10e-01 0.0
3 4.57e-01 1.03e-01 5.60e-01 -2.36e-01 -1.33e-01 0.0
4 3.07e-01 7.51e-02 3.82e-01 -1.50e-01 -7.49e-02 0.0
5 2.31e-01 4.00e-02 2.71e-01 -7.60e-02 -3.60e-02 0.0
6 1.87e-01 2.31e-02 2.10e-01 -4.37e-02 -2.06e-02 0.0
7 1.59e-01 1.48e-02 1.74e-01 -2.82e-02 -1.34e-02 0.0
8 1.39e-01 1.03e-02 1.49e-01 -1.97e-02 -9.38e-03 0.0
9 1.25e-01 7.53e-03 1.32e-01 -1.45e-02 -6.93e-03 0.0
10 1.14e-01 5.74e-03 1.19e-01 -1.11e-02 -5.33e-03 0.0
11 1.05e-01 4.52e-03 1.09e-01 -8.75e-03 -4.23e-03 0.0
…
37 4.98e-02 3.05e-04 5.01e-02 -6.03e-04 -2.99e-04 0.0
38 4.92e-02 2.88e-04 4.95e-02 -5.71e-04 -2.83e-04 0.0
39 4.87e-02 2.74e-04 4.89e-02 -5.42e-04 -2.68e-04 0.0
40 4.81e-02 2.60e-04 4.84e-02 -5.15e-04 -2.55e-04 0.0
41 4.76e-02 2.47e-04 4.79e-02 -4.90e-04 -2.43e-04 0.0
42 4.72e-02 2.35e-04 4.74e-02 -4.66e-04 -2.31e-04 0.0
43 4.67e-02 2.24e-04 4.70e-02 -4.45e-04 -2.20e-04 0.0
44 4.63e-02 2.14e-04 4.65e-02 -4.25e-04 -2.11e-04 0.0
45 4.59e-02 2.05e-04 4.61e-02 -4.06e-04 -2.01e-04 0.0
46 4.55e-02 1.96e-04 4.57e-02 -3.88e-04 -1.93e-04 0.0
47 4.51e-02 1.87e-04 4.53e-02 -3.72e-04 -1.84e-04 0.0
48 4.48e-02 1.80e-04 4.50e-02 -3.57e-04 -1.77e-04 0.0
49 4.44e-02 1.72e-04 4.46e-02 -3.42e-04 -1.70e-04 0.0
50 4.41e-02 1.66e-04 4.43e-02 -3.29e-04 -1.63e-04 0.0
We can plot the optimized field:
fig = plot_amplitude(
substitute(a, IdDict(a.control => opt_result_logisticsq.optimized_controls[1])),
tlist
)
Tanh-parameterization for amplitude-constrained pulses
a = ParameterizedAmplitude(
ϵ,
tlist;
parameterization=TanhParameterization(-0.5, 0.5),
parameterize=true
)
problem_tanh = ControlProblem(
trajectories=substitute(trajectories, IdDict(ϵ => a)),
prop_method=ExpProp,
lambda_a=1,
update_shape=(t -> flattop(t, T=5, t_rise=0.3, func=:blackman)),
tlist=tlist,
iter_stop=50,
J_T=QuantumControl.Functionals.J_T_ss,
check_convergence=res -> begin
((res.J_T < 1e-3) && (res.converged = true) && (res.message = "J_T < 10⁻³"))
end
);
opt_result_tanh = @optimize_or_load(
datadir("parameterization#opt_result_tanh.jld2"),
problem_tanh;
method=Krotov
);
iter. J_T ∫gₐ(t)dt J ΔJ_T ΔJ secs
0 9.51e-01 0.00e+00 9.51e-01 n/a n/a 0.1
[ Info: Set callback to store result in parameterization#opt_result_tanh.jld2 on unexpected exit.
1 9.27e-01 1.09e-02 9.38e-01 -2.40e-02 -1.31e-02 0.0
2 8.93e-01 1.56e-02 9.09e-01 -3.42e-02 -1.87e-02 0.0
3 8.46e-01 2.18e-02 8.67e-01 -4.77e-02 -2.59e-02 0.0
4 7.82e-01 2.94e-02 8.11e-01 -6.37e-02 -3.43e-02 0.0
5 7.03e-01 3.70e-02 7.40e-01 -7.89e-02 -4.18e-02 0.0
6 6.16e-01 4.18e-02 6.58e-01 -8.68e-02 -4.50e-02 0.0
7 5.33e-01 4.12e-02 5.74e-01 -8.33e-02 -4.21e-02 0.0
8 4.62e-01 3.59e-02 4.98e-01 -7.11e-02 -3.52e-02 0.0
9 4.05e-01 2.89e-02 4.34e-01 -5.66e-02 -2.77e-02 0.0
10 3.61e-01 2.26e-02 3.84e-01 -4.40e-02 -2.14e-02 0.0
11 3.27e-01 1.76e-02 3.44e-01 -3.43e-02 -1.67e-02 0.0
12 3…
37 1.35e-01 9.69e-04 1.36e-01 -1.92e-03 -9.52e-04 0.0
38 1.33e-01 9.17e-04 1.34e-01 -1.82e-03 -9.02e-04 0.0
39 1.31e-01 8.70e-04 1.32e-01 -1.73e-03 -8.56e-04 0.0
40 1.29e-01 8.26e-04 1.30e-01 -1.64e-03 -8.13e-04 0.0
41 1.28e-01 7.86e-04 1.29e-01 -1.56e-03 -7.74e-04 0.0
42 1.26e-01 7.49e-04 1.27e-01 -1.49e-03 -7.38e-04 0.0
43 1.25e-01 7.14e-04 1.26e-01 -1.42e-03 -7.04e-04 0.0
44 1.24e-01 6.82e-04 1.24e-01 -1.35e-03 -6.73e-04 0.0
45 1.22e-01 6.52e-04 1.23e-01 -1.30e-03 -6.43e-04 0.0
46 1.21e-01 6.24e-04 1.22e-01 -1.24e-03 -6.16e-04 0.0
47 1.20e-01 5.98e-04 1.21e-01 -1.19e-03 -5.91e-04 0.0
48 1.19e-01 5.74e-04 1.19e-01 -1.14e-03 -5.67e-04 0.0
49 1.18e-01 5.51e-04 1.18e-01 -1.10e-03 -5.45e-04 0.0
50 1.17e-01 5.30e-04 1.17e-01 -1.05e-03 -5.24e-04 0.0
fig = plot_amplitude(
substitute(a, IdDict(a.control => opt_result_tanh.optimized_controls[1])),
tlist
)