krotov.optimize module¶
Summary¶
Functions:
Use Krotov’s method to optimize towards the given objectives. |
__all__
: optimize_pulses
Reference¶
-
krotov.optimize.
optimize_pulses
(objectives, pulse_options, tlist, *, propagator, chi_constructor, mu=None, sigma=None, iter_start=0, iter_stop=5000, check_convergence=None, info_hook=None, modify_params_after_iter=None, storage='array', parallel_map=None, store_all_pulses=False, continue_from=None, skip_initial_forward_propagation=False, norm=None, overlap=None, limit_thread_pool=None)[source]¶ Use Krotov’s method to optimize towards the given objectives.
Optimize all time-dependent controls found in the Hamiltonians or Liouvillians of the given objectives.
- Parameters
pulse_options (dict) –
Mapping of time-dependent controls found in the Hamiltonians of the objectives to a dictionary of options for that control. There must be options given for every control. As numpy arrays are unhashable and thus cannot be used as dict keys, the options for a control that is an array must be set using the key
id(control)
(see the example below). The options of any particular control must contain the following keys:'lambda_a'
: the Krotov step size (float value). This governs the overall magnitude of the pulse update. Large values result in small updates. Small values may lead to sharp spikes and numerical instability.'update_shape'
: Function S(t) in the range [0, 1] that scales the pulse update for the pulse value at t. This can be used to ensure boundary conditions (S(0) = S(T) = 0), and enforce smooth switch-on and switch-off. This can be a callable that takes a single argument t; or the values 1 or 0 for a constant update-shape. The value 0 disables the optimization of that particular control.
In addition, the following keys may occur:
'args'
: If the control is a callable with arguments(t, args)
(as required by QuTiP), a dict of argument values to pass as args. If'args'
is not specified via the pulse_options, controls will be discretized using the defaultargs=None
.
For example, for objectives that contain a Hamiltonian of the form
[H0, [H1, u], [H2, g]]
, whereH0
,H1
, andH2
areQobj
instances,u
is a numpy array>>> u = numpy.zeros(1000)
and
g
is a control function>>> def g(t, args): ... E0 = args.get('E0', 0.0) ... return E0
then a possible value for pulse_options would look like this:
>>> from krotov.shapes import flattop >>> from functools import partial >>> pulse_options = { ... id(u): {'lambda_a': 1.0, 'update_shape': 1}, ... g: dict( ... lambda_a=1.0, ... update_shape=partial( ... flattop, t_start=0, t_stop=10, t_rise=1.5 ... ), ... args=dict(E0=1.0) ... ) ... }
The use of
dict
and the{...}
syntax are completely equivalent, butdict
is better for nested indentation.tlist (numpy.ndarray) – Array of time grid values, cf.
mesolve()
propagator (callable or list[callable]) – Function that propagates the state backward or forwards in time by a single time step, between two points in tlist. Alternatively, a list of functions, one for each objective. If the propagator is stateful, it should be an instance of
krotov.propagators.Propagator
. Seekrotov.propagators
for details.chi_constructor (callable) – Function that calculates the boundary condition for the backward propagation. This is where the final-time functional (indirectly) enters the optimization. See
krotov.functionals
for details.mu (None or callable) – Function that calculates the derivative \(\frac{\partial H}{\partial\epsilon}\) for an equation of motion \(\dot{\phi}(t) = -i H[\phi(t)]\) of an abstract operator \(H\) and an abstract state \(\phi\). If None, defaults to
krotov.mu.derivative_wrt_pulse()
, which covers the standard Schrödinger and master equations. Seekrotov.mu
for a full explanation of the role of mu in the optimization, and the required function signature.sigma (None or krotov.second_order.Sigma) – Function (instance of a
Sigma
subclass) that calculates the second-order contribution. If None, the first-order Krotov method is used.iter_start (int) – The formal iteration number at which to start the optimization
iter_stop (int) – The iteration number after which to end the optimization, whether or not convergence has been reached
check_convergence (None or callable) – Function that determines whether the optimization has converged. If None, the optimization will only end when iter_stop is reached. See
krotov.convergence
for details.info_hook (None or callable) – Function that is called after each iteration of the optimization, for the purpose of analysis. Any value returned by info_hook (e.g. an evaluated functional \(J_T\)) will be stored, for each iteration, in the info_vals attribute of the returned
Result
. The info_hook must have the same signature askrotov.info_hooks.print_debug_information()
. It should not modify its arguments in any way, except for shared_data.modify_params_after_iter (None or callable) – Function that is called after each iteration, which may modify its arguments for certain advanced use cases, such as dynamically adjusting lambda_vals, or applying spectral filters to the optimized_pulses. It has the same interface as info_hook but should not return anything. The modify_params_after_iter function is called immediately before info_hook, and can transfer arbitrary data to any subsequent info_hook via the shared_data argument.
storage (callable) – Storage constructor for the storage of propagated states. Must accept an integer parameter N and return an empty array-like container of length N. The default value ‘array’ is equivalent to
functools.partial(numpy.empty, dtype=object)
.parallel_map (callable or tuple or None) – Parallel function evaluator. If given as a callable, the argument must have the same specification as
qutip.parallel.serial_map()
. A value of None is the same as passingqutip.parallel.serial_map()
. If given as a tuple, that tuple must contain three callables, each of which has the same specification asqutip.parallel.serial_map()
. These three callables are used to parallelize (1) the initial forward-propagation, (2) the backward-propagation under the guess pulses, and (3) the forward-propagation by a single time step under the optimized pulses. Seekrotov.parallelization
for details.store_all_pulses (bool) – Whether or not to store the optimized pulses from all iterations in
Result
.continue_from (None or Result) – If given, continue an optimization from a previous
Result
. The result must have identical objectives.skip_initial_forward_propagation (bool) – If given as True together with continue_from, skip the initial forward propagation (“zeroth iteration”), and take the forward-propagated states from
Result.states
instead.norm (callable or None) – A single-argument function to calculate the norm of states. If None, delegate to the
norm()
method of the states.overlap (callable or None) – A two-argument function to calculate the complex overlap of two states. If None, delegate to
qutip.Qobj.overlap()
for Hilbert space states and to the Hilbert-Schmidt norm \(\tr[\rho_1^\dagger \rho2]\) for density matrices or operators.limit_thread_pool (bool or None) – If True, try to eliminate multi-threading in low-level numerical routines like
numpy
, via the use of thethreadpoolctl
package. Single-threaded execution is usually faster, but if you know what you are doing and can benchmark multi-threaded execution, you may set this to False to place no restrictions on multi-threading. The default value (None) delegates tokrotov.parallelization.USE_THREADPOOL_LIMITS
.
- Returns
The result of the optimization.
- Return type
- Raises
ValueError – If any controls are not real-valued, or if any update shape is not a real-valued function in the range [0, 1]; if using continue_from with a
Result
with differing objectives; if there are any required keys missing in pulse_options.