Using Krotov with QuTiP¶
The krotov
package is designed around QuTiP, a very powerful “Quantum
Toolbox” in Python. This means that all operators and states are expressed as
qutip.Qobj
quantum objects. The optimize_pulses()
interface
for Krotov’s optimization method is closely linked to the interface of QuTiP’s
central mesolve()
routine for simulating the system
dynamics of a closed or open quantum system. In particular, when setting up an
optimization, the (time-dependent) system Hamiltonian should be represented by
a nested list. This is, a Hamiltonian of the form \(\Op{H} = \Op{H}_0 +
\epsilon(t) \Op{H}_1\) is represented as H = [H0, [H1, eps]]
where H0
and H1
are Qobj
operators, and eps
is a function with
signature eps(t, args)
, or an array of control values with the length of the
time grid (tlist parameter in mesolve()
). The operator
can depend on multiple controls, resulting in expressions of the form H =
[H0, [H1, eps1], [H2, eps2], ...]
.
The central routine provided by the krotov
package is
optimize_pulses()
. It takes as input a list of objectives, each of which
is an instance of Objective
. Each objective has an
initial_state
, which is a qutip.Qobj
representing
a Hilbert space state or density matrix, a target
(usually
the target state that the initial_state
should evolve into
when the objective is fulfilled), and a Hamiltonian H
in
the nested-list format described above. For dissipative dynamics,
H
should be a Liouvillian, which can be obtained from the
Hamiltonian and a set of Lindblad operators via
krotov.objectives.liouvillian()
. The Liouvillian again is in nested list
format to express time-dependencies. Alternatively, each objective could also
directly include a list c_ops
of collapse (Lindblad)
operators , where each collapse operator is a Qobj
operator.
However, this only makes sense if the time propagation routine takes the
collapse operators into account explicitly, such as in the Monte-Carlo
mcsolve()
. Otherwise, the use of
c_ops
is strongly discouraged.
If the control function (eps
in the above example) relies on the dict
args
for static parameters, those args
can be specified via the
pulse_options argument in optimize_pulses()
. See How to use args in time-dependent control fields.
In order to simulate the dynamics of the guess control, you can use
Objective.mesolve()
, which delegates to qutip.mesolve.mesolve()
.
There is also a related method Objective.propagate()
that uses a
different sampling of the control values, see krotov.propagators
.
The optimization routine will automatically extract all controls that it can
find in the objectives, and iteratively calculate updates to all controls in
order to meet all objectives simultaneously. The result of the optimization
will be in the returned Result
object, with a list of the optimized
controls in optimized_controls
.
The optimized_objectives
property contains a copy of the
objectives with the optimized_controls
plugged into the
Hamiltonian or Liouvillian and/or collapse operators. The dynamics under the
optimized controls can then again be simulated through
Objective.mesolve()
.
While the guess controls that are in the objectives on input may be
functions, or an array of control values on the time grid, the output
optimized_controls
will always be an array of control values.