krotov.info_hooks module

Routines that can be passed as info_hook to optimize_pulses()

Summary

Functions:

chain

Chain multiple info_hook or modify_params_after_iter callables together.

print_debug_information

Print full debug information about the current Krotov iteration

print_table

Print a tabular overview of the functional values in the iteration

__all__: chain, print_debug_information, print_table

Reference

krotov.info_hooks.chain(*hooks)[source]

Chain multiple info_hook or modify_params_after_iter callables together.

Example

>>> def print_fidelity(**kwargs):
...     F_re = np.average(np.array(kwargs['tau_vals']).real)
...     print("    F = %f" % F_re)
>>> info_hook = chain(print_debug_information, print_fidelity)

Note

Functions that are connected via chain() may use the shared_data share the same shared_data argument, which they can use to communicate down the chain.

krotov.info_hooks.print_debug_information(*, objectives, adjoint_objectives, backward_states, forward_states, forward_states0, guess_pulses, optimized_pulses, g_a_integrals, lambda_vals, shape_arrays, fw_states_T, tlist, tau_vals, start_time, stop_time, iteration, info_vals, shared_data, out=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>)[source]

Print full debug information about the current Krotov iteration

This routine is intended to be passed to optimize_pulses() as info_hook, and it exemplifies the full signature of a routine suitable for this purpose.

Keyword Arguments
  • objectives (list[Objective]) – list of the objectives

  • adjoint_objectives (list[Objective]) – list of the adjoint objectives

  • backward_states (list) – If available, for each objective, an array-like object containing the states of the Krotov backward propagation.

  • forward_states – If available (second order only), for each objective, an array-like object containing the forward-propagated states under the optimized pulses. None otherwise.

  • forward_states0 – If available (second order only), for each objective, an array-like object containing the forward-propagated states under the guess pulses. None otherwise.

  • guess_pulses (list[numpy.ndarray]) – list of guess pulses

  • optimized_pulses (list[numpy.ndarray]) – list of optimized pulses

  • g_a_integrals (numpy.ndarray) – array of values \(\int_0^T g_a(t) \dd t = \int_0^T \frac{\lambda_a}{S(t)} \Abs{\Delta \epsilon(t)}^2 \dd t\), for each pulse \(\epsilon(t)\). The pulse updates \(\Delta \epsilon(t)\) are the differences of the optimized_pulses and the guess_pulses (zero in the zeroth iteration that only performs a forward-propagation of the guess pulses). The quantity \(\int g_a(t) \dd t\) is a very useful measure of how much the pulse amplitudes changes in each iteration. This tells us whether we’ve chosen good values for \(\lambda_a\). Values that are too small cause “pulse explosions” which immediately show up in \(\int_0^T g_a(t) \dd t\). Also, whether \(\int g_a(t) \dd t\) is increasing or decreasing between iterations gives an indication whether the optimization is “speeding up” or “slowing down”, and thus whether convergence is reached (negligible pulse updates).

  • lambda_vals (numpy.ndarray) – for each pulse, the value of the \(\lambda_a\) parameter

  • shape_arrays (list[numpy.ndarray]) – for each pulse, the array of update-shape values \(S(t)\)

  • fw_states_T (list) – for each objective, the forward-propagated state

  • tlist (numpy.ndarray) – array of time grid values on which the states are defined

  • tau_vals (numpy.ndarray) – for each objective, the complex overlap for the target state with the forward-propagated state, or None if no target state is defined.

  • start_time (float) – The time at which the iteration started, in epoch seconds

  • stop_time (float) – The time at which the iteration started, in epoch seconds

  • iteration (int) – The current iteration number. For the initial propagation of the guess controls, zero.

  • shared_data (dict) – Dict of data shared between any modify_params_after_iter and any info_hook functions chained together via chain().

  • info_vals (list) – List of the return values of the info_hook from previous iterations

  • out – An open file handle where to write the information. This parameter is not part of the info_hook interface, and defaults to stdout. Use functools.partial() to pass a different value.

Note

This routine implements the full signature of an info_hook in optimize_pulses(), excluding out. However, since the info_hook only allows for keyword arguments, it is usually much simpler to use Python’s variable keyword arguments syntax (**kwargs). For example, consider the following info_hook that prints (and stores) the value of the real-part gate fidelity:

def print_fidelity(**kwargs):
    F_re = np.average(np.array(kwargs['tau_vals']).real)
    print("    F = %f" % F_re)
    return F_re
krotov.info_hooks.print_table(J_T, show_g_a_int_per_pulse=False, J_T_prev=None, unicode=True, out=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>)[source]

Print a tabular overview of the functional values in the iteration

An example output is:

iter.        J_T    ∫gₐ(t)dt          J       ΔJ_T         ΔJ  secs
    0   1.00e+00    0.00e+00   1.00e+00        n/a        n/a     0
    1   7.65e-01    2.33e-02   7.88e-01  -2.35e-01  -2.12e-01     1
    2   5.56e-01    2.07e-02   5.77e-01  -2.09e-01  -1.88e-01     1

The table has the following columns:

  • iteration number

  • value of the final-time functional \(J_T\)

  • If show_g_a_int_per_pulse is True and there is more than one control pulse: one column for each pulse containing the value of \(\int_0^T \frac{\lambda_{a, i}}{S_i(t)} \Abs{\Delta \epsilon_i(t)}^2 \dd t\)

  • The value of \(\sum_i \int_0^T g_a(\epsilon_i(t)) \dd t = \sum_i \int_0^T \frac{\lambda_{a, i}}{S_i(t)} \Abs{\Delta \epsilon_i(t)}^2 \dd t\), or just \(\int_0^T \frac{\lambda_{a}}{S(t)} \Abs{\Delta \epsilon(t)}^2 \dd t\) if there is only a single control pulse (as in the above example output). This value (respectively the individual values with show_g_a_int_per_pulse) should always be at least three orders of magnitude smaller than the pulse fluence \(\sum_i\int_0^T \Abs{\epsilon_i(t)}^2 \dd t\). Larger changes in the pulse amplitude may be a sign of a “pulse explosion” due to values for \(\lambda_{a,i}\) that are too small. Changes in \(\sum_i \int_0^T \frac{\lambda_{a, i}}{S_i(t)}\) are often a better indicator of whether the optimization is “speeding up”/”slowing down”/reaching convergence than the values of \(J_T\).

  • The value of the total functional \(J = J_T + \sum_i \int_0^T g_a(\epsilon_i(t)) \dd t\)

  • The change \(\Delta J_T\) in the final time functional compared to the previous iteration. This should be a negative value, indicating monotonic convergence in a minimization of \(J_T\).

  • The change \(\Delta J\) in the total functional compared to the previous iteration. This is evaluated as \(\Delta J = \Delta J_T + \sum_i \int_0^T g_a(\epsilon_i(t)) \dd t\). Somewhat counter-intuitively, \(\Delta J\) does not contain a contribution from the \(g_a(t)\) of the previous iteration. This is because the \(\Delta \epsilon_i(T)\) on which \(g_a(t)\) depends must be evaluated with respect to the same reference field (the guess pulse of the current iteration), to that \(\Delta \epsilon_i(T) = 0\) when evaluated with the optimized pulse of the previous iteration (i.e., the same guess pulse of the current iteration).

  • The number of seconds in wallclock time spent on the iteration

  • An indicator * or ** if there is a loss of monotonic convergence in \(\Delta J_T\) and/or \(\Delta J\). Krotov’s method mathematically guarantees a negative \(\Delta J\) in the continuous limit. Assuming there are no errors in the time propagation, or in the chi_constructor passed to optimize_pulses(), a loss of monotonic convergence is due to the \(\lambda_a\) associated with the pulses (via pulse_options in optimize_pulses()) being too small. In practice, we usually don’t care too much about a loss of monotonic convergence in \(\Delta J\), but a loss of convergence in \(\Delta J_T\) is a serious sign of trouble. It is often associated with sharp discontinuous spikes in the optimized pulses, or a dramatic increase in the pulse amplitude.

Parameters
  • J_T (callable) – A function that extract the value of the final time functional from the keyword-arguments passed to the info_hook.

  • show_g_a_int_per_pulse (callable) – If True, print a column with the value of \(\int_0^T g_a(\epsilon_i(t)) \dd t = \int_0^T \frac{\lambda_{a, i}}{S_i(t)} \Abs{\Delta \epsilon_i(t)}^2 \dd t\) for every pulse \(\epsilon_i(t)\). Otherwise, only print the sum over those integrals for all pulses.

  • J_T_prev (None or callable) – A function that extract the value of the final time functional from the previous iteration. If None, use the last values from the info_vals passed to the info_hook.

  • unicode (bool) – Whether to use unicode symbols for the column headers. Some systems have broken monospace fonts in the Jupyter notebook that cause the headers not to line up as intended.

  • out – An open file handle where to write the table. Defaults to stdout.