Overview
Control Parameters
The ParameterizedQuantumControl
package optimizes a set $\{u_n\}$ of arbitrary control parameters associated with a given control problem. The dynamic generators $\Op{H} = \Op{H}(\{u_n\}, t)$ from the different trajectories implicitly depend on these parameter values in arbitrary ways.
Contrast this with the form
\[\Op{H} = \Op{H}_0 + \sum_l \Op{H}_l(\{ϵ_{l′}(t)\}, t) \quad\text{or}\quad \Op{H}_0 + \sum_l a_l(\{ϵ_{l′}(t)\}, t) \Op{H}_l \quad\text{or}\quad \Op{H}_0 + \sum_l ϵ_l(t) \Op{H}_l\]
of a Generator, where $\Op{H}_0$ is the drift term, $\Op{H}_l$ are the control terms, and $a_l(t)$ and $ϵ_l(t)$ are the control amplitudes and control functions, respectively. Methods such as GRAPE and Krotov's method optimize each $ϵ_l(t)$ as a time-continuous function.
Control amplitudes or control functions still play a conceptual role: Even though in the most general case, the generator $\Op{H}$ can directly depend on the parameters, the most common case is for that dependency to be inside of the controls, $ϵ_l(t) → ϵ_l(\{u_n\}, t)$. The crucial difference is that in ParameterizedQuantumControl
, we solve the optimization problem by tuning the values $u_n$, not $ϵ_l(t)$ as arbitrary time-continuous control functions.
The ParameterizedQuantumControl
package evaluates an arbitrary optimization functional $J(\{u_n\})$ and optionally the gradient $\frac{∂J}{∂ u_n}$ and feeds that information to an optimization backend, e.g. Optimization.jl with its large collection of solvers. We may again contrast this with GRAPE and Krotov's method which conceptually optimize time-continuous control functions $ϵ_l(t)$ but in practice discretize these functions as piecewise-constant on a time grid. One might be tempted to think if this discretization as the use of control parameters $ϵ_l(t) = ϵ_l(\{ϵ_{nl}\})$ where each parameter $ϵ_{nl}$ is the amplitude of $ϵ_l(t)$ on the $n$'th interval of the time grid (what we refer to as a "pulse"). However, GRAPE and Krotov have a specific numerical scheme to evaluate the gradients of the optimization functional with respect to the pulse values. That scheme is dramatically more efficient than the more general scheme to determine gradients with respect to arbitrary parameters that is used in ParameterizedQuantumControl
.
Parameter API
ParameterizedQuantumControl
manages the evaluation of functionals and gradients, and organizes feeding that information into an optimization backend. However, the structures for working with control parameters are already provided by the QuantumPropagators
and QuantumControl
packages. We summarize them here in the context of optimal control.
Generally, the vector of parameters
is obtained from problem
via the get_parameters
function. This delegates to get_parameters(traj)
for each Trajectory
in problem.trajectories
, which in turn delegates to get_parameters(traj.generator)
. This makes it possible to define a custom datatype for the dynamic generator for which a get_parameters
method is defined. It is recommended for get_parameters
to return a ComponentArrays.ComponentVector
parameters
. That makes it easy to keep track of which value is which parameters. However, in general, get_parameters
can return an arbitrary AbstractVector
. What is important is that the returned parameters
alias into the generator. That is, mutating parameters
and then calling QuantumControl.Controls.evaluate(generator, args...; kwargs...)
must return an operator that takes into account the current values in parameters
. The easiest way to achieve this is to have parameters
be a field in the custom struct
of the generator and have get_parameters
return a reference to that field. The QuantumControl.Interfaces.check_parameterized
function of QuantumControl.Interfaces.check_generator
with for_parameterization=true
can be used to verify the implementation of a custom generator.
When there are multiple trajectories (and thus multiple generators) in the ControlProblem
, these are automatically combined into a RecursiveArrayTools.ArrayPartition
. The parameters from different generators are considered independent unless they are the same object.
When using a built-in QuantumControl.Generators.Generator
as returned by QuantumControl.hamiltonian
or QuantumControl.liouvillian
, the get_parameters
function delegates to the get_parameters(control)
for any control returned by get_controls(generator)
. The recommended way to implement a custom parameterized control is to subtype QuantumControl.Controls.ParameterizedFunction
. Just like parameters from different generators in the same control problem are automatically combined, the parameters from different controls are also automatically combined into a RecursiveArrayTools.ArrayPartition
, taking into account if get_parameters
returns the same object for two different controls. In any case, for any custom implementation of a parameterized system, and especially if control parameters are aliased between different components of the system, it is important to carefully check that the result of get_parameters
contains all the independent parameters of the problem.
Evaluation of the Functional
In order to evaluate an optimization functional $J(\{u_n\})$, the ParameterizedQuantumControl
package simply uses QuantumControl.propagate_trajectories
and passes the resulting states to a J_T
function. Running costs are a work in progress. Again, the dynamic generators are assumed to have been implemented in such a way that mutating the values in the array returned by get_parameters
are automatically taken into account.
In principle, any propagation method can be used for the evaluation of the functional. However, parameterized controls are typically time-continuous functions, and using piecewise-constant propagators such as ExpProp
, Cheby
, or Newton
is unnecessary and introduces a discretization error. Usually, using an ODE solver with method=OrdinaryDiffEq
is more appropriate.
It is also worth noting that the time discretization that happens in piecewise-constant propagators severs the connection between the control parameters and the pulse amplitudes at each interval of the time grid. Thus, any changes to the parameters
after QuantumControl.init_prop
will not be reflected in subsequent calls to QuantumControl.prop_step!
. This is not an issue inside of ParameterizedQuantumControl
, as QuantumControl.propagate_trajectories
initializes a new propagator for every evaluation of the functional.
Gradient Evaluation
Optimizers that rely on gradient information will be supported in a future release.
Running costs
Running costs are planned in a future release.