diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 0f6e0c8..ed0c0b6 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-07-17T14:06:06","documenter_version":"1.5.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-07-26T11:34:44","documenter_version":"1.5.0"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index f4a98ca..fc6f8f2 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,3 +1,3 @@ -API · Krotov.jl

API

Krotov.KrotovResultType

Result object returned by optimize_krotov.

Attributes

The attributes of a KrotovResult object include

  • iter: The number of the current iteration
  • J_T: The value of the final-time functional in the current iteration
  • J_T_prev: The value of the final-time functional in the previous iteration
  • tlist: The time grid on which the control are discretized.
  • guess_controls: A vector of the original control fields (each field discretized to the points of tlist)
  • optimized_controls: A vector of the optimized control fields. Calculated only at the end of the optimization, not after each iteration.
  • tau_vals: For any trajectory that defines a target_state, the complex overlap of that target state with the propagated state. For any trajectory for which the target_state is nothing, the value is zero.
  • records: A vector of tuples with values returned by a callback routine passed to optimize
  • converged: A boolean flag on whether the optimization is converged. This may be set to true by a check_convergence function.
  • message: A message string to explain the reason for convergence. This may be set by a check_convergence function.

All of the above attributes may be referenced in a check_convergence function passed to optimize(problem; method=Krotov)

source
Krotov.KrotovWrkType

Krotov workspace.

The workspace is for internal use. However, it is also accessible in a callback function. The callback may use or modify some of the following attributes:

  • trajectories: a copy of the trajectories defining the control problem
  • adjoint_trajectories: The trajectories with the adjoint generator
  • kwargs: The keyword arguments from the ControlProblem or the call to optimize.
  • controls: A tuple of the original controls (probably functions)
  • ga_a_int: The current value of $∫gₐ(t)dt$ for each control
  • update_shapes: The update shapes $S(t)$ for each pulse, discretized on the intervals of the time grid.
  • lambda_vals: The current value of λₐ for each control
  • result: The current result object
  • fw_storage: The storage of states for the forward propagation
  • fw_propagators: The propagators used for the forward propagation
  • bw_propagators: The propagators used for the backward propagation
  • use_threads: Flag indicating whether the propagations are performed in parallel.
source
QuantumControlBase.optimizeMethod
using Krotov
-result = optimize(problem; method=Krotov, kwargs...)

optimizes the given control problem using Krotov's method, returning a KrotovResult.

Keyword arguments that control the optimization are taken from the keyword arguments used in the instantiation of problem; any of these can be overridden with explicit keyword arguments to optimize.

Required problem keyword arguments

  • J_T: A function J_T(Ψ, trajectories) that evaluates the final time functional from a list Ψ of forward-propagated states and problem.trajectories. The function J_T may also take a keyword argument tau. If it does, a vector containing the complex overlaps of the target states (target_state property of each trajectory in problem.trajectories) with the propagated states will be passed to J_T.

Recommended problem keyword arguments

  • lambda_a=1.0: The inverse Krotov step width λₐ for every pulse.
  • update_shape=(t->1.0): A function S(t) for the "update shape" that scales the update for every pulse.

If different controls require different lambda_a or update_shape, a dict pulse_options must be given instead of a global lambda_a and update_shape; see below.

Optional problem keyword arguments

The following keyword arguments are supported (with default values):

  • pulse_options: A dictionary that maps every control (as obtained by get_controls from the problem.trajectories) to the following dict:

    • :lambda_a: The value for inverse Krotov step width λₐ.
    • :update_shape: A function S(t) for the "update shape" that scales the Krotov pulse update.

    This overrides the global lambda_a and update_shape arguments.

  • chi: A function chi(Ψ, trajectories) that receives a list Ψ of the forward propagated states and returns a vector of states $|χₖ⟩ = -∂J_T/∂⟨Ψₖ|$. If not given, it will be automatically determined from J_T via make_chi with the default parameters. Similarly to J_T, if chi accepts a keyword argument tau, it will be passed a vector of complex overlaps.

  • sigma=nothing: A function that calculates the second-order contribution. If not given, the first-order Krotov method is used.

  • iter_start=0: The initial iteration number.

  • iter_stop=5000: The maximum iteration number.

  • prop_method: The propagation method to use for each trajectory; see below.

  • print_iters=true: Whether to print information after each iteration.

  • store_iter_info=Set(): Which fields from print_iters to store in result.records. A subset of Set(["iter.", "J_T", "∫gₐ(t)dt", "J", "ΔJ_T", "ΔJ", "secs"]).

  • callback: A function (or tuple of functions) that receives the Krotov workspace, the iteration number, the list of updated pulses, and the list of guess pulses as positional arguments. The function may return a tuple of values which are stored in the KrotovResult object result.records. The function can also mutate any of its arguments, in particular the updated pulses. This may be used, e.g., to apply a spectral filter to the updated pulses or to perform similar manipulations. Note that print_iters=true (default) adds an automatic callback to print information after each iteration. With store_iter_info, that callback automatically stores a subset of the printed information.

  • check_convergence: A function to check whether convergence has been reached. Receives a KrotovResult object result, and should set result.converged to true and result.message to an appropriate string in case of convergence. Multiple convergence checks can be performed by chaining functions with . The convergence check is performed after any callback.

  • verbose=false: If true, print information during initialization.

  • rethrow_exceptions: By default, any exception ends the optimization but still returns a KrotovResult that captures the message associated with the exception. This is to avoid losing results from a long-running optimization when an exception occurs in a later iteration. If rethrow_exceptions=true, instead of capturing the exception, it will be thrown normally.

Trajectory propagation

Krotov's method involves the forward and backward propagation for every Trajectory in the problem. The keyword arguments for each propagation (see propagate) are determined from any properties of each Trajectory that have a prop_ prefix, cf. init_prop_trajectory.

In situations where different parameters are required for the forward and backward propagation, instead of the prop_ prefix, the fw_prop_ and bw_prop_ prefixes can be used, respectively. These override any setting with the prop_ prefix. This applies both to the properties of each Trajectory and the problem keyword arguments.

Note that the propagation method for each propagation must be specified. In most cases, it is sufficient (and recommended) to pass a global prop_method problem keyword argument.

source
+API · Krotov.jl

API

Krotov.KrotovResultType

Result object returned by optimize_krotov.

Attributes

The attributes of a KrotovResult object include

  • iter: The number of the current iteration
  • J_T: The value of the final-time functional in the current iteration
  • J_T_prev: The value of the final-time functional in the previous iteration
  • tlist: The time grid on which the control are discretized.
  • guess_controls: A vector of the original control fields (each field discretized to the points of tlist)
  • optimized_controls: A vector of the optimized control fields. Calculated only at the end of the optimization, not after each iteration.
  • tau_vals: For any trajectory that defines a target_state, the complex overlap of that target state with the propagated state. For any trajectory for which the target_state is nothing, the value is zero.
  • records: A vector of tuples with values returned by a callback routine passed to optimize
  • converged: A boolean flag on whether the optimization is converged. This may be set to true by a check_convergence function.
  • message: A message string to explain the reason for convergence. This may be set by a check_convergence function.

All of the above attributes may be referenced in a check_convergence function passed to optimize(problem; method=Krotov)

source
Krotov.KrotovWrkType

Krotov workspace.

The workspace is for internal use. However, it is also accessible in a callback function. The callback may use or modify some of the following attributes:

  • trajectories: a copy of the trajectories defining the control problem
  • adjoint_trajectories: The trajectories with the adjoint generator
  • kwargs: The keyword arguments from the ControlProblem or the call to optimize.
  • controls: A tuple of the original controls (probably functions)
  • ga_a_int: The current value of $∫gₐ(t)dt$ for each control
  • update_shapes: The update shapes $S(t)$ for each pulse, discretized on the intervals of the time grid.
  • lambda_vals: The current value of λₐ for each control
  • result: The current result object
  • fw_storage: The storage of states for the forward propagation
  • fw_propagators: The propagators used for the forward propagation
  • bw_propagators: The propagators used for the backward propagation
  • use_threads: Flag indicating whether the propagations are performed in parallel.
source
QuantumControlBase.optimizeMethod
using Krotov
+result = optimize(problem; method=Krotov, kwargs...)

optimizes the given control problem using Krotov's method, returning a KrotovResult.

Keyword arguments that control the optimization are taken from the keyword arguments used in the instantiation of problem; any of these can be overridden with explicit keyword arguments to optimize.

Required problem keyword arguments

  • J_T: A function J_T(Ψ, trajectories) that evaluates the final time functional from a list Ψ of forward-propagated states and problem.trajectories. The function J_T may also take a keyword argument tau. If it does, a vector containing the complex overlaps of the target states (target_state property of each trajectory in problem.trajectories) with the propagated states will be passed to J_T.

Recommended problem keyword arguments

  • lambda_a=1.0: The inverse Krotov step width λₐ for every pulse.
  • update_shape=(t->1.0): A function S(t) for the "update shape" that scales the update for every pulse.

If different controls require different lambda_a or update_shape, a dict pulse_options must be given instead of a global lambda_a and update_shape; see below.

Optional problem keyword arguments

The following keyword arguments are supported (with default values):

  • pulse_options: A dictionary that maps every control (as obtained by get_controls from the problem.trajectories) to the following dict:

    • :lambda_a: The value for inverse Krotov step width λₐ.
    • :update_shape: A function S(t) for the "update shape" that scales the Krotov pulse update.

    This overrides the global lambda_a and update_shape arguments.

  • chi: A function chi(Ψ, trajectories) that receives a list Ψ of the forward propagated states and returns a vector of states $|χₖ⟩ = -∂J_T/∂⟨Ψₖ|$. If not given, it will be automatically determined from J_T via make_chi with the default parameters. Similarly to J_T, if chi accepts a keyword argument tau, it will be passed a vector of complex overlaps.

  • sigma=nothing: A function that calculates the second-order contribution. If not given, the first-order Krotov method is used.

  • iter_start=0: The initial iteration number.

  • iter_stop=5000: The maximum iteration number.

  • prop_method: The propagation method to use for each trajectory; see below.

  • print_iters=true: Whether to print information after each iteration.

  • store_iter_info=Set(): Which fields from print_iters to store in result.records. A subset of Set(["iter.", "J_T", "∫gₐ(t)dt", "J", "ΔJ_T", "ΔJ", "secs"]).

  • callback: A function (or tuple of functions) that receives the Krotov workspace, the iteration number, the list of updated pulses, and the list of guess pulses as positional arguments. The function may return a tuple of values which are stored in the KrotovResult object result.records. The function can also mutate any of its arguments, in particular the updated pulses. This may be used, e.g., to apply a spectral filter to the updated pulses or to perform similar manipulations. Note that print_iters=true (default) adds an automatic callback to print information after each iteration. With store_iter_info, that callback automatically stores a subset of the printed information.

  • check_convergence: A function to check whether convergence has been reached. Receives a KrotovResult object result, and should set result.converged to true and result.message to an appropriate string in case of convergence. Multiple convergence checks can be performed by chaining functions with . The convergence check is performed after any callback.

  • verbose=false: If true, print information during initialization.

  • rethrow_exceptions: By default, any exception ends the optimization but still returns a KrotovResult that captures the message associated with the exception. This is to avoid losing results from a long-running optimization when an exception occurs in a later iteration. If rethrow_exceptions=true, instead of capturing the exception, it will be thrown normally.

Trajectory propagation

Krotov's method involves the forward and backward propagation for every Trajectory in the problem. The keyword arguments for each propagation (see propagate) are determined from any properties of each Trajectory that have a prop_ prefix, cf. init_prop_trajectory.

In situations where different parameters are required for the forward and backward propagation, instead of the prop_ prefix, the fw_prop_ and bw_prop_ prefixes can be used, respectively. These override any setting with the prop_ prefix. This applies both to the properties of each Trajectory and the problem keyword arguments.

Note that the propagation method for each propagation must be specified. In most cases, it is sufficient (and recommended) to pass a global prop_method problem keyword argument.

source
diff --git a/dev/examples/index.html b/dev/examples/index.html index 075a1d8..9d7509b 100644 --- a/dev/examples/index.html +++ b/dev/examples/index.html @@ -1,2 +1,2 @@ -Examples · Krotov.jl
+Examples · Krotov.jl
diff --git a/dev/externals/index.html b/dev/externals/index.html index 7a9c5a9..e0a2171 100644 --- a/dev/externals/index.html +++ b/dev/externals/index.html @@ -13,12 +13,10 @@ ampl; tlist, for_gradient_optimization=true, quiet=false )

verifies that the given ampl is a valid element in the list of amplitudes of a Generator object. This checks all the conditions of QuantumPropagators.Interfaces.check_amplitude. In addition, the following conditions must be met.

If for_gradient_optimization:

The function returns true for a valid amplitude and false for an invalid amplitude. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumControlBase.check_generatorMethod

Check the dynamical generator in the context of optimal control.

@test check_generator(
     generator; state, tlist,
-    for_mutable_operator=true, for_immutable_operator=true,
-    for_mutable_state=true, for_immutable_state=true,
     for_expval=true, for_pwc=true, for_time_continuous=false,
     for_parameterization=false, for_gradient_optimization=true,
     atol=1e-15, quiet=false
-)

verifies the given generator. This checks all the conditions of QuantumPropagators.Interfaces.check_generator. In addition, the following conditions must be met.

If for_gradient_optimization:

The function returns true for a valid generator and false for an invalid generator. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumControlBase.get_control_derivMethod
a = get_control_deriv(ampl, control)

returns the derivative $∂a_l(t)/∂ϵ_{l'}(t)$ of the given amplitude $a_l(\{ϵ_{l''}(t)\}, t)$ with respect to the given control $ϵ_{l'}(t)$. For "trivial" amplitudes, where $a_l(t) ≡ ϵ_l(t)$, the result with be either 1.0 or 0.0 (depending on whether ampl ≡ control). For non-trivial amplitudes, the result may be another amplitude that depends on the controls and potentially on time, but can be evaluated to a constant with evaluate.

source
QuantumControlBase.get_control_derivMethod

Get the derivative of the generator $G$ w.r.t. the control $ϵ(t)$.

μ  = get_control_deriv(generator, control)

returns nothing if the generator (Hamiltonian or Liouvillian) does not depend on control, or a generator

\[μ = \frac{∂G}{∂ϵ(t)}\]

otherwise. For linear control terms, μ will be a static operator, e.g. an AbstractMatrix or an Operator. For non-linear controls, μ will be time-dependent, e.g. a Generator. In either case, evaluate should be used to evaluate μ into a constant operator for particular values of the controls and a particular point in time.

For constant generators, e.g. an Operator, the result is always nothing.

source
QuantumControlBase.get_control_derivsMethod

Get a vector of the derivatives of generator w.r.t. each control.

get_control_derivs(generator, controls)

return as vector containing the derivative of generator with respect to each control in controls. The elements of the vector are either nothing if generator does not depend on that particular control, or a function μ(α) that evaluates the derivative for a particular value of the control, see get_control_deriv.

source
QuantumControlBase.init_prop_trajectoryMethod

Initialize a propagator for a given Trajectory.

propagator = init_prop_trajectory(
+)

verifies the given generator. This checks all the conditions of QuantumPropagators.Interfaces.check_generator. In addition, the following conditions must be met.

If for_gradient_optimization:

The function returns true for a valid generator and false for an invalid generator. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumControlBase.get_control_derivMethod
a = get_control_deriv(ampl, control)

returns the derivative $∂a_l(t)/∂ϵ_{l'}(t)$ of the given amplitude $a_l(\{ϵ_{l''}(t)\}, t)$ with respect to the given control $ϵ_{l'}(t)$. For "trivial" amplitudes, where $a_l(t) ≡ ϵ_l(t)$, the result with be either 1.0 or 0.0 (depending on whether ampl ≡ control). For non-trivial amplitudes, the result may be another amplitude that depends on the controls and potentially on time, but can be evaluated to a constant with evaluate.

source
QuantumControlBase.get_control_derivMethod

Get the derivative of the generator $G$ w.r.t. the control $ϵ(t)$.

μ  = get_control_deriv(generator, control)

returns nothing if the generator (Hamiltonian or Liouvillian) does not depend on control, or a generator

\[μ = \frac{∂G}{∂ϵ(t)}\]

otherwise. For linear control terms, μ will be a static operator, e.g. an AbstractMatrix or an Operator. For non-linear controls, μ will be time-dependent, e.g. a Generator. In either case, evaluate should be used to evaluate μ into a constant operator for particular values of the controls and a particular point in time.

For constant generators, e.g. an Operator, the result is always nothing.

source
QuantumControlBase.get_control_derivsMethod

Get a vector of the derivatives of generator w.r.t. each control.

get_control_derivs(generator, controls)

return as vector containing the derivative of generator with respect to each control in controls. The elements of the vector are either nothing if generator does not depend on that particular control, or a function μ(α) that evaluates the derivative for a particular value of the control, see get_control_deriv.

source
QuantumControlBase.init_prop_trajectoryMethod

Initialize a propagator for a given Trajectory.

propagator = init_prop_trajectory(
     traj,
     tlist;
     initial_state=traj.initial_state,
@@ -83,7 +81,7 @@
     @threadsif use_threads for k = 1:length(trajectories)
     # ...
     end
-end
source
QuantumPropagators.AbstractPropagatorType

Abstract base type for all Propagator objects.

All Propagator objects must be instantiated via init_prop and implement the following interface.

Properties

  • state (read-only): The current quantum state in the propagation
  • tlist (read-only): The time grid for the propagation
  • t (read-only): The time at which state is defined. An element of tlist.
  • parameters: parameters that determine the dynamics. The structure of the parameters depends on the concrete Propagator type (i.e., the propagation method). Mutating the parameters affects subsequent propagation steps.
  • backward: Boolean flag to indicate whether the propagation moves forward or backward in time
  • inplace: Boolean flag to indicate whether propagator.state is modified in-place or is recreated by every call of prop_step! or set_state!. With inplace=false, the propagator should generally avoid in-place operations, such as calls to QuantumPropagators.Controls.evaluate!.

Concrete Propagator types may have additional properties or fields, but these should be considered private.

Methods

  • reinit_prop! — reset the propagator to a new initial state at the beginning of the time grid (or the end, for backward propagation)
  • prop_step! – advance the propagator by one step forward (or backward) on the time grid.
  • set_state! — safely mutate the current quantum state of the propagation. Note that directly mutating the state property is not safe. However, Ψ = propagator.state; foo_mutate!(Ψ), set_state!(propagator, Ψ) for some mutating function foo_mutate! is guaranteed to be safe and efficient for both in-place and not-in-place propagators.
  • set_t! — safely mutate the current time (propagator.t), snapping to the values of tlist.

See also

source
QuantumPropagators.ChebyPropagatorType

Propagator for Chebychev propagation (method=QuantumPropagators.Cheby).

This is a PWCPropagator.

source
QuantumPropagators.ExpPropagatorType

Propagator for propagation via direct exponentiation (method=QuantumPropagators.ExpProp)

This is a PWCPropagator.

source
QuantumPropagators.NewtonPropagatorType

Propagator for Newton propagation (method=QuantumPropagators.Newton).

This is a PWCPropagator.

source
QuantumPropagators.PWCPropagatorType

PiecewisePropagator sub-type for piecewise-constant propagators.

Like the more general PiecewisePropagator, this is characterized by propagator.parameters mapping the controls in the generator to a vector of amplitude value on the midpoints of the time grid intervals.

The propagation will use these values as constant within each interval.

source
QuantumPropagators.PiecewisePropagatorType

AbstractPropagator sub-type for piecewise propagators.

A piecewise propagator is determined by a single parameter per control and time grid interval. Consequently, the propagator.parameters are a dictionary mapping the controls found in the generator via get_controls to a vector of values defined on the intervals of the time grid, see discretize_on_midpoints. This does not necessarily imply that these values are the piecewise-constant amplitudes for the intervals. A general piecewise propagator might use interpolation to obtain actual amplitudes within any given time interval.

When the amplitudes are piecewise-constant, the propagator should be a concrete instantiation of a PWCPropagator.

source
QuantumPropagators.PropagationType

Wrapper around the parameters of a call to propagate.

Propagation(
+end
source
QuantumPropagators.AbstractPropagatorType

Abstract base type for all Propagator objects.

All Propagator objects must be instantiated via init_prop and implement the following interface.

Properties

  • state (read-only): The current quantum state in the propagation
  • tlist (read-only): The time grid for the propagation
  • t (read-only): The time at which state is defined. An element of tlist.
  • parameters: parameters that determine the dynamics. The structure of the parameters depends on the concrete Propagator type (i.e., the propagation method). Mutating the parameters affects subsequent propagation steps.
  • backward: Boolean flag to indicate whether the propagation moves forward or backward in time
  • inplace: Boolean flag to indicate whether propagator.state is modified in-place or is recreated by every call of prop_step! or set_state!. With inplace=false, the propagator should generally avoid in-place operations, such as calls to QuantumPropagators.Controls.evaluate!.

Concrete Propagator types may have additional properties or fields, but these should be considered private.

Methods

  • reinit_prop! — reset the propagator to a new initial state at the beginning of the time grid (or the end, for backward propagation)
  • prop_step! – advance the propagator by one step forward (or backward) on the time grid.
  • set_state! — safely mutate the current quantum state of the propagation. Note that directly mutating the state property is not safe. However, Ψ = propagator.state; foo_mutate!(Ψ), set_state!(propagator, Ψ) for some mutating function foo_mutate! is guaranteed to be safe and efficient for both in-place and not-in-place propagators.
  • set_t! — safely mutate the current time (propagator.t), snapping to the values of tlist.

See also

source
QuantumPropagators.ChebyPropagatorType

Propagator for Chebychev propagation (method=QuantumPropagators.Cheby).

This is a PWCPropagator.

source
QuantumPropagators.ExpPropagatorType

Propagator for propagation via direct exponentiation (method=QuantumPropagators.ExpProp)

This is a PWCPropagator.

source
QuantumPropagators.NewtonPropagatorType

Propagator for Newton propagation (method=QuantumPropagators.Newton).

This is a PWCPropagator.

source
QuantumPropagators.PWCPropagatorType

PiecewisePropagator sub-type for piecewise-constant propagators.

Like the more general PiecewisePropagator, this is characterized by propagator.parameters mapping the controls in the generator to a vector of amplitude value on the midpoints of the time grid intervals.

The propagation will use these values as constant within each interval.

source
QuantumPropagators.PiecewisePropagatorType

AbstractPropagator sub-type for piecewise propagators.

A piecewise propagator is determined by a single parameter per control and time grid interval. Consequently, the propagator.parameters are a dictionary mapping the controls found in the generator via get_controls to a vector of values defined on the intervals of the time grid, see discretize_on_midpoints. This does not necessarily imply that these values are the piecewise-constant amplitudes for the intervals. A general piecewise propagator might use interpolation to obtain actual amplitudes within any given time interval.

When the amplitudes are piecewise-constant, the propagator should be a concrete instantiation of a PWCPropagator.

source
QuantumPropagators.PropagationType

Wrapper around the parameters of a call to propagate.

Propagation(
     generator, tlist;
     pre_propagation=nothing, post_propagation=nothing,
     kwargs...
@@ -95,14 +93,14 @@
     kwargs...
 )

is a wrapper around the arguments for propagate / init_prop, for use within propagate_sequence.

The positional and keyword arguments are those accepted by the above propagation routines, excluding the initial state. A Propagation may in addition include the pre_propagation and post_propagation keyword arguments recognized by propagate_sequence.

source
QuantumPropagators.cheby_get_spectral_envelopeMethod

Determine the spectral envelope of a generator.

E_min, E_max = cheby_get_spectral_envelope(
     generator, tlist, control_ranges, method; kwargs...
-)

estimates a lower bound E_min the lowest eigenvalue of the generator for any values of the controls specified by control_ranges, and an upper bound E_max for the highest eigenvalue.

This is done by constructing operators from the extremal values for the controls as specified in control_ranges and taking the smallest/largest return values from specrange for those operators.

Arguments

  • generator: dynamical generator, e.g. a time-dependent
  • tlist: The time grid for the propagation
  • control_ranges: a dict that maps controls that occur in generator (cf. get_controls to a tuple of minimum and maximum amplitude for that control
  • method: method name to pass to specrange
  • kwargs: Any remaining keyword arguments are passed to specrange
source
QuantumPropagators.disable_timingsMethod

Disable the collection of TimerOutputs data.

QuantumPropagators.disable_timings()

disables the collection of timing data previously enabled with enable_timings. This triggers recompilation to completely remove profiling from the code. That is, there is zero cost when the collection of timing data is disabled.

Returns QuantumPropagators.timings_enabled(), i.e., false if successful.

source
QuantumPropagators.enable_timingsMethod

Enable the collection of TimerOutputs data.

QuantumPropagators.enable_timings()

enables certain portions of the package to collect TimerOutputs internally. This aids in profiling and benchmarking propagation methods.

Specifically, after enable_timings(), for any ChebyPropagator or NewtonPropagator, timing data will become available in propagator.wrk.timing_data (as a TimerOutput instance). This data is reset when the propagator is re-instantiated with init_prop or re-initialized with reinit_prop!. This makes the data local to any call of propagate.

Note that enable_timings() triggers recompilation, so propagate should be called at least twice to avoid compilation overhead in the timing data. There is still a small overhead for collecting the timing data.

The collection of timing data can be disabled again with disable_timings.

Returns QuantumPropagators.timings_enabled(), i.e., true if successful.

source
QuantumPropagators.init_propMethod
using QuantumPropagators: Cheby
+)

estimates a lower bound E_min the lowest eigenvalue of the generator for any values of the controls specified by control_ranges, and an upper bound E_max for the highest eigenvalue.

This is done by constructing operators from the extremal values for the controls as specified in control_ranges and taking the smallest/largest return values from specrange for those operators.

Arguments

  • generator: dynamical generator, e.g. a time-dependent
  • tlist: The time grid for the propagation
  • control_ranges: a dict that maps controls that occur in generator (cf. get_controls to a tuple of minimum and maximum amplitude for that control
  • method: method name to pass to specrange
  • kwargs: Any remaining keyword arguments are passed to specrange
source
QuantumPropagators.disable_timingsMethod

Disable the collection of TimerOutputs data.

QuantumPropagators.disable_timings()

disables the collection of timing data previously enabled with enable_timings. This triggers recompilation to completely remove profiling from the code. That is, there is zero cost when the collection of timing data is disabled.

Returns QuantumPropagators.timings_enabled(), i.e., false if successful.

source
QuantumPropagators.enable_timingsMethod

Enable the collection of TimerOutputs data.

QuantumPropagators.enable_timings()

enables certain portions of the package to collect TimerOutputs internally. This aids in profiling and benchmarking propagation methods.

Specifically, after enable_timings(), for any ChebyPropagator or NewtonPropagator, timing data will become available in propagator.wrk.timing_data (as a TimerOutput instance). This data is reset when the propagator is re-instantiated with init_prop or re-initialized with reinit_prop!. This makes the data local to any call of propagate.

Note that enable_timings() triggers recompilation, so propagate should be called at least twice to avoid compilation overhead in the timing data. There is still a small overhead for collecting the timing data.

The collection of timing data can be disabled again with disable_timings.

Returns QuantumPropagators.timings_enabled(), i.e., true if successful.

source
QuantumPropagators.init_propMethod
using QuantumPropagators: Cheby
 
 cheby_propagator = init_prop(
     state,
     generator,
     tlist;
     method=Cheby,
-    inplace=true,
+    inplace=QuantumPropagators.Interfaces.supports_inplace(state),
     backward=false,
     verbose=false,
     parameters=nothing,
@@ -112,14 +110,14 @@
     cheby_coeffs_limit=1e-12,
     check_normalization=false,
     specrange_kwargs...
-)

initializes a ChebyPropagator.

Method-specific keyword arguments

  • control_ranges: a dict the maps the controls in generator (see get_controls) to a tuple of min/max values. The Chebychev coefficients will be calculated based on a spectral envelope that assumes that each control can take arbitrary values within the min/max range. If not given, the ranges are determined automatically. Specifying manual control ranges can be useful when the the control amplitudes (parameters) may change during the propagation, e.g. in a sequential-update control scheme.
  • specrange_method: Method to pass to the specrange function
  • specrange_buffer: An additional factor by which to enlarge the estimated spectral range returned by specrange, in order to ensure that Chebychev coefficients are based on an overestimation of the spectral range.
  • cheby_coeffs_limit: The maximum magnitude of Chebychev coefficients that should be treated as non-zero
  • check_normalization: Check whether the Hamiltonian has been properly normalized, i.e., that the spectral range of generator has not been underestimated. This slowes down the propagation, but is advisable for novel generators.
  • uniform_dt_tolerance=1e-12: How much the intervals of tlist are allowed to vary while still being considered constant.
  • specrange_kwargs: All further keyword arguments are passed to the specrange function. Most notably, with the default specrange_method=:auto (or specrange_method=:manual), passing E_min and E_max allows to manually specify the spectral range of generator.
source
QuantumPropagators.init_propMethod
using QuantumPropagators: ExpProp
+)

initializes a ChebyPropagator.

Method-specific keyword arguments

  • control_ranges: a dict the maps the controls in generator (see get_controls) to a tuple of min/max values. The Chebychev coefficients will be calculated based on a spectral envelope that assumes that each control can take arbitrary values within the min/max range. If not given, the ranges are determined automatically. Specifying manual control ranges can be useful when the the control amplitudes (parameters) may change during the propagation, e.g. in a sequential-update control scheme.
  • specrange_method: Method to pass to the specrange function
  • specrange_buffer: An additional factor by which to enlarge the estimated spectral range returned by specrange, in order to ensure that Chebychev coefficients are based on an overestimation of the spectral range.
  • cheby_coeffs_limit: The maximum magnitude of Chebychev coefficients that should be treated as non-zero
  • check_normalization: Check whether the Hamiltonian has been properly normalized, i.e., that the spectral range of generator has not been underestimated. This slowes down the propagation, but is advisable for novel generators.
  • uniform_dt_tolerance=1e-12: How much the intervals of tlist are allowed to vary while still being considered constant.
  • specrange_kwargs: All further keyword arguments are passed to the specrange function. Most notably, with the default specrange_method=:auto (or specrange_method=:manual), passing E_min and E_max allows to manually specify the spectral range of generator.
source
QuantumPropagators.init_propMethod
using QuantumPropagators: ExpProp
 
 exp_propagator = init_prop(
     state,
     generator,
     tlist;
     method=ExpProp,
-    inplace=true,
+    inplace=QuantumPropagators.Interfaces.supports_inplace(state),
     backward=false,
     verbose=false,
     parameters=nothing,
@@ -127,14 +125,14 @@
     convert_state=_exp_prop_convert_state(state),
     convert_operator=_exp_prop_convert_operator(generator),
     _...
-)

initializes an ExpPropagator.

Method-specific keyword arguments

  • func: The function to evaluate. The argument H_dt is obtained by constructing an operator H from generator via the evaluate function and the multiplied with the time step dt for the current time interval. The propagation then simply multiplies the return value of func with the current state
  • convert_state: Type to which to temporarily convert states before multiplying the return value of func.
  • convert_operator: Type to which to convert the operator H before multiplying it with dt and plugging the result into func

The convert_state and convert_operator parameters are useful for when the generator and or state are unusual data structures for which the relevant methods to calculate func are not defined. Often, it is easier to temporarily convert them to standard complex matrices and vectors than to implement the missing methods.

source
QuantumPropagators.init_propMethod
using QuantumPropagators: Newton
+)

initializes an ExpPropagator.

Method-specific keyword arguments

  • func: The function to evaluate. The argument H_dt is obtained by constructing an operator H from generator via the evaluate function and the multiplied with the time step dt for the current time interval. The propagation then simply multiplies the return value of func with the current state
  • convert_state: Type to which to temporarily convert states before multiplying the return value of func.
  • convert_operator: Type to which to convert the operator H before multiplying it with dt and plugging the result into func

The convert_state and convert_operator parameters are useful for when the generator and or state are unusual data structures for which the relevant methods to calculate func are not defined. Often, it is easier to temporarily convert them to standard complex matrices and vectors than to implement the missing methods.

source
QuantumPropagators.init_propMethod
using QuantumPropagators: Newton
 
 newton_propagator = init_prop(
     state,
     generator,
     tlist;
     method=Newton,
-    inplace=true,
+    inplace=QuantumPropagators.Interfaces.supports_inplace(state),
     backward=false,
     verbose=false,
     parameters=nothing,
@@ -144,25 +142,25 @@
     relerr=1e-12,
     max_restarts=50,
     _...
-)

initializes a NewtonPropagator.

Method-specific keyword arguments

  • m_max: maximum Krylov dimension, cf. NewtonWrk
  • func, norm_min, relerr, max_restarts: parameter to pass to newton!
source
QuantumPropagators.init_propMethod

Initialize a Propagator.

propagator = init_prop(
+)

initializes a NewtonPropagator.

Method-specific keyword arguments

  • m_max: maximum Krylov dimension, cf. NewtonWrk
  • func, norm_min, relerr, max_restarts: parameter to pass to newton!
source
QuantumPropagators.init_propMethod

Initialize a Propagator.

propagator = init_prop(
     state, generator, tlist;
     method,  # mandatory keyword argument
     backward=false,
-    inplace=true,
+    inplace=QuantumPropagators.Interfaces.supports_inplace(state),
     piecewise=nothing,
     pwc=nothing,
     kwargs...
-)

initializes a propagator for the time propagation of the given state over a time grid tlist under the time-dependent generator (Hamiltonian/Liouvillian) generator.

Arguments

  • state: The "initial" state for the propagation. For backward=false, this state is taken to be at initial time (tlist[begin]); and for backward=true, at the final time (tlist[end])
  • generator: The time-dependent generator of the dynamics
  • tlist: The time grid over which which the propagation is defined. This may or may not be equidistant.

Mandatory keyword arguments

  • method: The propagation method to use. May be given as a name (Symbol), but the recommended usage is to pass a module implementing the propagation method, e.g., using QuantumPropagators: Cheby; method = Cheby. Passing a module ensures that the code implementing the method is correctly loaded. This is particularly important for propagators using third-party backends, like with method=OrdinaryDiffEq.

Optional keyword arguments

  • backward: If true, initialize the propagator for a backward propagation. The resulting propagator.t will be tlist[end], and subsequent calls to prop_step! will move backward on tlist.
  • inplace: If true, the state property of the resulting propagator will be changed in-place by any call to prop_step!. If false, each call to prop_step! changes the reference for propagator.state, and the propagation will not use any in-place operations. Not all propagation methods may support both in-place and not-in-place propagation. In-place propagation is generally more efficient but may not be compatible, e.g., with automatic differentiation.
  • piecewise: If given as a boolean, true enforces that the resulting propagator is a PiecewisePropagator, and false enforces that it not a PiecewisePropagator. For the default piecewise=nothing, whatever type of propagation is the default for the given method will be used. Throw an error if the given method does not support the required type of propagation.
  • pwc: Like piecewise, but for the stronger PWCPropagator.

All other kwargs are method-dependent and are ignored for methods that do not support them.

The type of the returned propagator is a sub-type of AbstractPropagator, respectively a sub-type of PiecewisePropagator if piecewise=true or a sub-type of PWCPropagator if pwc=true.

Internals

Internally, the (mandatory) keyword method is converted into a fourth positional argument. This allows propagation methods to define their own implementation of init_prop via multiple dispatch. However, when calling init_prop in high-level code, method must always be given as a keyword argument.

See also

source
QuantumPropagators.ode_functionMethod

Wrap around a Generator, for use as an ODE function.

f = ode_function(generator, tlist; c=-1im)

creates a function suitable to be passed to ODEProblem.

\[\gdef\op#1{\hat{#1}} +)

initializes a propagator for the time propagation of the given state over a time grid tlist under the time-dependent generator (Hamiltonian/Liouvillian) generator.

Arguments

  • state: The "initial" state for the propagation. For backward=false, this state is taken to be at initial time (tlist[begin]); and for backward=true, at the final time (tlist[end])
  • generator: The time-dependent generator of the dynamics
  • tlist: The time grid over which which the propagation is defined. This may or may not be equidistant.

Mandatory keyword arguments

  • method: The propagation method to use. May be given as a name (Symbol), but the recommended usage is to pass a module implementing the propagation method, e.g., using QuantumPropagators: Cheby; method = Cheby. Passing a module ensures that the code implementing the method is correctly loaded. This is particularly important for propagators using third-party backends, like with method=OrdinaryDiffEq.

Optional keyword arguments

  • backward: If true, initialize the propagator for a backward propagation. The resulting propagator.t will be tlist[end], and subsequent calls to prop_step! will move backward on tlist.
  • inplace: If true, the state property of the resulting propagator will be changed in-place by any call to prop_step!. If false, each call to prop_step! changes the reference for propagator.state, and the propagation will not use any in-place operations. Not all propagation methods may support both in-place and not-in-place propagation. In-place propagation is generally more efficient for larger Hilbert space dimensions, but may not be compatible, e.g., with automatic differentiation.
  • piecewise: If given as a boolean, true enforces that the resulting propagator is a PiecewisePropagator, and false enforces that it not a PiecewisePropagator. For the default piecewise=nothing, whatever type of propagation is the default for the given method will be used. Throw an error if the given method does not support the required type of propagation.
  • pwc: Like piecewise, but for the stronger PWCPropagator.

All other kwargs are method-dependent and are ignored for methods that do not support them.

The type of the returned propagator is a sub-type of AbstractPropagator, respectively a sub-type of PiecewisePropagator if piecewise=true or a sub-type of PWCPropagator if pwc=true.

Internals

Internally, the (mandatory) keyword method is converted into a fourth positional argument. This allows propagation methods to define their own implementation of init_prop via multiple dispatch. However, when calling init_prop in high-level code, method must always be given as a keyword argument.

See also

source
QuantumPropagators.ode_functionMethod

Wrap around a Generator, for use as an ODE function.

f = ode_function(generator, tlist; c=-1im)

creates a function suitable to be passed to ODEProblem.

\[\gdef\op#1{\hat{#1}} \gdef\ket#1{\vert{#1}\rangle}\]

With generator corresponding to $\op{H}(t)$, this implicitly encodes the ODE

\[\frac{\partial}{\partial t} \ket{\Psi(t)} = c \op{H}(t) \ket{\Psi(t)}\]

for the state $\ket{\Psi(t)}$. With the default $c = -i$, this corresponds to the Schrödinger equation, or the Liouville equation with convention=:LvN.

The resulting f works both in-place and not-in-place, as

f(ϕ, Ψ, vals_dict, t)   # in-place `f(du, u, p, t)`
 ϕ = f(Ψ, vals_dict, t)  # not-in-place `f(u, p, t)`

Calling f as above is functionally equivalent to calling evaluate to obtain an operator H from the original time-dependent generator, and then applying H to the current quantum state Ψ:

H = evaluate(f.generator, t; vals_dict=vals_dict)
-ϕ = c * H * Ψ

where vals_dict may be a dictionary mapping controls to values (set as the parameters p of the underlying ODE solver).

If QuantumPropagators.enable_timings() has been called, profiling data is collected in f.timing_data.

source
QuantumPropagators.prop_step!Function

Advance the propagator by a single time step.

state = prop_step!(propagator)

returns the state obtained from propagating to the next point on the time grid from propagator.t, respectively the previous point if propagator.backward is true.

When the propagation would lead out of the time grid, prop_step! leaves propagator unchanged and returns nothing. Thus, a return value of nothing may be used to signal that a propagation has completed.

source
QuantumPropagators.propagateMethod

Propagate a state over an entire time grid.

state = propagate(
+ϕ = c * H * Ψ

where vals_dict may be a dictionary mapping controls to values (set as the parameters p of the underlying ODE solver).

If QuantumPropagators.enable_timings() has been called, profiling data is collected in f.timing_data.

source
QuantumPropagators.prop_step!Function

Advance the propagator by a single time step.

state = prop_step!(propagator)

returns the state obtained from propagating to the next point on the time grid from propagator.t, respectively the previous point if propagator.backward is true.

When the propagation would lead out of the time grid, prop_step! leaves propagator unchanged and returns nothing. Thus, a return value of nothing may be used to signal that a propagation has completed.

source
QuantumPropagators.propagateMethod

Propagate a state over an entire time grid.

state = propagate(
     state,
     generator,
     tlist;
     method,  # mandatory keyword argument
     check=true,
     backward=false,
-    inplace=true,
+    inplace=QuantumPropagators.Interfaces.supports_inplace(state),
     verbose=false,
     piecewise=nothing,
     pwc=nothing,
@@ -170,11 +168,11 @@
     observables=<store state>,
     callback=nothing,
     show_progress=false,
-    init_prop_kwargs...)

propagates state of the entire time grid and returns the propagated states, or a storage array of data collected during the propagation. This high-level routine performs the following three steps:

  1. If check=true (default), check that state, generator, and tlist are consistent with the required interface.

  2. Initialize a propagator via init_prop:

    init_prop(state, generator, tlist; method, inplace, init_prop_kwargs...)

  3. Call and return the result of

    propagate(propagator; storage, observables, show_progress, callback)

Arguments

  • state: The "initial" state for the propagation. For backward=false, this state is taken to be at initial time (tlist[begin]); and for backward=true, at the final time (tlist[end])
  • generator: The time-dependent generator of the dynamics
  • tlist: The time grid over which which the propagation is defined. This may or may not be equidistant.

Mandatory keyword arguments

  • method: The propagation method to use. May be given as a name (Symbol), but the recommended usage is to pass a module implementing the propagation method, cf. init_prop.

Optional keyword arguments

  • check: if true, check that state, generator, and tlist pass check_state, check_generator and check_tlist, respectively.
  • backward: If true, propagate backward in time
  • inplace: If true, propagate using in-place operations. If false, avoid in-place operations. Not all propagation methods support both in-place and not-in-place propagation.
  • piecewise: If given as a boolean, ensure that the internal propagator is an instance of PiecewisePropagator, cf. init_prop.
  • pwc: If given a a boolean, do a piecewise constant propagation where the generator in each interval is constant (the internal propagator is a PWCPropagator, cf. init_prop)
  • storage: Flag whether to store and return the propagated states / observables, or pre-allocated storage array. See Notes below.
  • observables: Converters for data to be stored in storage. See Notes below.
  • callback: Function to call after each propagation step. See Notes below.
  • show_progress: Whether to show a progress bar. See Notes below.

All remaining keyword arguments are passed to init_prop to initialize the Propagator that is used internally to drive the optimization. Unknown keyword arguments will be ignored.

Notes

In general, there is no requirement that tlist has a constant time step, although some propagation methods (most notably Cheby) only support a uniform time grid.

If storage is given as a container pre-allocated via init_storage, it will be filled with data determined by the observables. Specifically, after each propagation step,

data = map_observables(observables, tlist, i, state)
+    init_prop_kwargs...)

propagates state of the entire time grid and returns the propagated states, or a storage array of data collected during the propagation. This high-level routine performs the following three steps:

  1. If check=true (default), check that state, generator, and tlist are consistent with the required interface.

  2. Initialize a propagator via init_prop:

    init_prop(state, generator, tlist; method, inplace, init_prop_kwargs...)

  3. Call and return the result of

    propagate(propagator; storage, observables, show_progress, callback)

Arguments

  • state: The "initial" state for the propagation. For backward=false, this state is taken to be at initial time (tlist[begin]); and for backward=true, at the final time (tlist[end])
  • generator: The time-dependent generator of the dynamics
  • tlist: The time grid over which which the propagation is defined. This may or may not be equidistant.

Mandatory keyword arguments

  • method: The propagation method to use. May be given as a name (Symbol), but the recommended usage is to pass a module implementing the propagation method, cf. init_prop.

Optional keyword arguments

  • check: if true, check that state, generator, and tlist pass check_state, check_generator and check_tlist, respectively.
  • backward: If true, propagate backward in time
  • inplace: If true, propagate using in-place operations. If false, avoid in-place operations. Not all propagation methods support both in-place and not-in-place propagation. Note that inplace=true requires that QuantumPropagators.Interfaces.supports_inplace for state is true.
  • piecewise: If given as a boolean, ensure that the internal propagator is an instance of PiecewisePropagator, cf. init_prop.
  • pwc: If given a a boolean, do a piecewise constant propagation where the generator in each interval is constant (the internal propagator is a PWCPropagator, cf. init_prop)
  • storage: Flag whether to store and return the propagated states / observables, or pre-allocated storage array. See Notes below.
  • observables: Converters for data to be stored in storage. See Notes below.
  • callback: Function to call after each propagation step. See Notes below.
  • show_progress: Whether to show a progress bar. See Notes below.

All remaining keyword arguments are passed to init_prop to initialize the Propagator that is used internally to drive the optimization. Unknown keyword arguments will be ignored.

Notes

In general, there is no requirement that tlist has a constant time step, although some propagation methods (most notably Cheby) only support a uniform time grid.

If storage is given as a container pre-allocated via init_storage, it will be filled with data determined by the observables. Specifically, after each propagation step,

data = map_observables(observables, tlist, i, state)
 write_to_storage!(storage, i, data)

is executed, where state is defined at time tlist[i]. See map_observables and write_to_storage! for details. The default values for observables results simply in the propagated states at every point in time being stored.

The storage parameter may also be given as true, and a new storage array will be created internally with init_storage and returned instead of the propagated state:

data = propagate(
     state, generator, tlist; method,
     backward=false; storage=true, observables=observables,
-    callback=nothing, show_progress=false, init_prop_kwargs...)

If backward is true, the input state is assumed to be at time tlist[end], and the propagation progresses backward in time (with a negative time step dt). If storage is given, it will be filled back-to-front during the backward propagation.

If callback is given as a callable, it will be called after each propagation step, as callback(propagator, observables) where propagator is Propagator object driving the propagation. The callback is called before calculating any observables. Example usage includes writing data to file, or modifying state via set_state!, e.g., removing amplitude from the lowest and highest level to mitigate "truncation error".

If show_progress is given as true, a progress bar will be shown for long-running propagation. In order to customize the progress bar, show_progress may also be a function that receives length(tlist) and returns a ProgressMeter.Progress instance.

If in_place=false is given, the propagation avoids in-place operations. This is slower than inplace=true, but is often required in the context of automatic differentiation (AD), e.g., with Zygote. That is, use in_place=false if propagate is called inside a function to be passed to Zygote.gradient, Zygote.pullback, or a similar function. In an AD context, storage and show_progress should not be used.

The propagate routine returns the propagated state at tlist[end], respectively tlist[1] if backward=true, or a storage array with the stored states / observable data if storage=true.

source
QuantumPropagators.propagateMethod
state = propagate(
+    callback=nothing, show_progress=false, init_prop_kwargs...)

If backward is true, the input state is assumed to be at time tlist[end], and the propagation progresses backward in time (with a negative time step dt). If storage is given, it will be filled back-to-front during the backward propagation.

If callback is given as a callable, it will be called after each propagation step, as callback(propagator, observables) where propagator is Propagator object driving the propagation. The callback is called before calculating any observables. Example usage includes writing data to file, or modifying state via set_state!, e.g., removing amplitude from the lowest and highest level to mitigate "truncation error".

If show_progress is given as true, a progress bar will be shown for long-running propagation. In order to customize the progress bar, show_progress may also be a function that receives length(tlist) and returns a ProgressMeter.Progress instance.

If in_place=false is given, the propagation avoids in-place operations. This is slower than inplace=true, but is often required in the context of automatic differentiation (AD), e.g., with Zygote. That is, use in_place=false if propagate is called inside a function to be passed to Zygote.gradient, Zygote.pullback, or a similar function. In an AD context, storage and show_progress should not be used.

The propagate routine returns the propagated state at tlist[end], respectively tlist[1] if backward=true, or a storage array with the stored states / observable data if storage=true.

source
QuantumPropagators.propagateMethod
state = propagate(
     state,
     propagator;
     storage=nothing,
@@ -182,13 +180,13 @@
     show_progress=false,
     callback=nothing,
     reinit_prop_kwargs...
-)

re-initializes the given propagator with state (see reinit_prop!) and then calls the lower-level propagate(propagator; ...).

source
QuantumPropagators.propagateMethod
state = propagate(
+)

re-initializes the given propagator with state (see reinit_prop!) and then calls the lower-level propagate(propagator; ...).

source
QuantumPropagators.propagateMethod
state = propagate(
     propagator;
     storage=nothing,
     observables=<store state>,
     show_progress=false,
     callback=nothing,
-)

propagates a freshly initialized propagator (immediately after init_prop). Used in the higher-level propagate(state, generator, tlist; kwargs...).

source
QuantumPropagators.propagate_sequenceMethod

Propagate a state through a sequence of generators.

states = propagate_sequence(
+)

propagates a freshly initialized propagator (immediately after init_prop). Used in the higher-level propagate(state, generator, tlist; kwargs...).

source
QuantumPropagators.propagate_sequenceMethod

Propagate a state through a sequence of generators.

states = propagate_sequence(
     state,
     propagations;
     storage=nothing,
@@ -206,9 +204,9 @@
     else
         return (min(ϵ_min, 5 * ϵ_min), max(ϵ_max, 5 * ϵ_max))
     end
-end

will re-calculate the Chebychev coefficients only if the current amplitudes differ by more than a factor of two from the ranges that were used when initializing the propagator (control_ranges parameter in init_prop, which would have had to overestimate the actual amplitudes by at least a factor of two). When re-calculating, the control_ranges will overestimate the amplitudes by a factor of five. With this transform_control_ranges, the propagation will be stable as long as the amplitudes do not change dynamically by more than a factor of 2.5 from their original range, while also not re-calculating coefficients unnecessarily in each pass because of modest changes in the amplitudes.

The transform_control_ranges argument is only relevant in the context of optimal control, where the same propagator will be used for many iterations with changing control field amplitudes.

All other keyword arguments are ignored.

source
QuantumPropagators.set_state!Method

Set the current state of the propagator.

set_state!(propagator, state)

sets the propagator.state property and returns propagator.state. In order to mutate the current state after a call to prop_step!, the following pattern is recommended:

Ψ = propagator.state
+end

will re-calculate the Chebychev coefficients only if the current amplitudes differ by more than a factor of two from the ranges that were used when initializing the propagator (control_ranges parameter in init_prop, which would have had to overestimate the actual amplitudes by at least a factor of two). When re-calculating, the control_ranges will overestimate the amplitudes by a factor of five. With this transform_control_ranges, the propagation will be stable as long as the amplitudes do not change dynamically by more than a factor of 2.5 from their original range, while also not re-calculating coefficients unnecessarily in each pass because of modest changes in the amplitudes.

The transform_control_ranges argument is only relevant in the context of optimal control, where the same propagator will be used for many iterations with changing control field amplitudes.

All other keyword arguments are ignored.

source
QuantumPropagators.set_state!Method

Set the current state of the propagator.

set_state!(propagator, state)

sets the propagator.state property and returns propagator.state. In order to mutate the current state after a call to prop_step!, the following pattern is recommended:

Ψ = propagator.state
 foo_mutate!(Ψ)
-set_state!(propagator, Ψ)

where foo_mutate! is some function that mutates Ψ. This is guaranteed to work efficiently both for in-place and not-in-place propagators, without incurring unnecessary copies.

Warning
foo_mutate!(propagator.state)

by itself is not a safe operation. Always follow it by

set_state!(propagator, propagator.state)

See also

source
QuantumPropagators.set_t!Method

Set the current time for the propagation.

set_t!(propagator, t)

Sets propagator.t to the given value of t, where t must be an element of propagator.tlist.

See also

source
QuantumPropagators.timings_enabledMethod

Check whether the collection of TimerOutputs data is active.

QuantumPropagators.timings_enabled()

returns true if QuantumPropagators.enable_timings() was called, and false otherwise or after QuantumPropagators.disable_timings().

source
QuantumPropagators.Generators.GeneratorType

A time-dependent generator.

Generator(ops::Vector{OT}, amplitudes::Vector{AT})

produces an object of type Generator{OT,AT} that represents

\[Ĥ(t)= Ĥ_0 + \sum_l a_l(\{ϵ_{l'}(t)\}, t) \, Ĥ_l\,,\]

where $Ĥ_l$ are the ops and $a_l(t)$ are the amplitudes. $Ĥ(t)$ and $Ĥ_l$ may represent operators in Hilbert space or super-operators in Liouville space. If the number of amplitudes is less than the number of ops, the first ops are considered as drift terms ($Ĥ_0$, respectively subsequent terms with $a_l ≡ 1$). At least one time-dependent amplitude is required. Each amplitude may depend on one or more control functions $ϵ_{l'}(t)$, although most typically $a_l(t) ≡ ϵ_l(t)$, that is, the amplitudes are simply a vector of the controls. See hamiltonian for details.

A Generator object should generally not be instantiated directly, but via hamiltonian or liouvillian.

The list of ops and amplitudes are properties of the Generator. They should not be mutated.

See also

  • Operator for static generators, which may be obtained from a Generator via evaluate.
source
QuantumPropagators.Generators.OperatorType

A static operator in Hilbert or Liouville space.

Operator(ops::Vector{OT}, coeffs::Vector{CT})

produces an object of type Operator{OT,CT} that encapsulates the "lazy" sum

\[Ĥ = \sum_l c_l Ĥ_l\,,\]

where $Ĥ_l$ are the ops and $c_l$ are the coeffs, which each must be a constant Number. If the number of coefficients is less than the number of operators, the first ops are considered to have $c_l = 1$.

An Operator object would generally not be instantiated directly, but be obtained from a (@ref) via evaluate.

source
QuantumPropagators.Generators.ScaledOperatorType

A static operator with a scalar pre-factor.

op = ScaledOperator(α, Ĥ)

represents the "lazy" product $α Ĥ$ where $Ĥ$ is an operator (typically an Operator instance) and $α$ is a scalar.

source
QuantumPropagators.Generators.hamiltonianMethod

Initialize a (usually time-dependent) Hamiltonian.

The most common usage is, e.g.,

using QuantumPropagators
+set_state!(propagator, Ψ)

where foo_mutate! is some function that mutates Ψ. This is guaranteed to work efficiently both for in-place and not-in-place propagators, without incurring unnecessary copies.

Warning
foo_mutate!(propagator.state)

by itself is not a safe operation. Always follow it by

set_state!(propagator, propagator.state)

See also

source
QuantumPropagators.set_t!Method

Set the current time for the propagation.

set_t!(propagator, t)

Sets propagator.t to the given value of t, where t must be an element of propagator.tlist.

See also

source
QuantumPropagators.timings_enabledMethod

Check whether the collection of TimerOutputs data is active.

QuantumPropagators.timings_enabled()

returns true if QuantumPropagators.enable_timings() was called, and false otherwise or after QuantumPropagators.disable_timings().

source
QuantumPropagators.Generators.GeneratorType

A time-dependent generator.

Generator(ops::Vector{OT}, amplitudes::Vector{AT})

produces an object of type Generator{OT,AT} that represents

\[Ĥ(t)= Ĥ_0 + \sum_l a_l(\{ϵ_{l'}(t)\}, t) \, Ĥ_l\,,\]

where $Ĥ_l$ are the ops and $a_l(t)$ are the amplitudes. $Ĥ(t)$ and $Ĥ_l$ may represent operators in Hilbert space or super-operators in Liouville space. If the number of amplitudes is less than the number of ops, the first ops are considered as drift terms ($Ĥ_0$, respectively subsequent terms with $a_l ≡ 1$). At least one time-dependent amplitude is required. Each amplitude may depend on one or more control functions $ϵ_{l'}(t)$, although most typically $a_l(t) ≡ ϵ_l(t)$, that is, the amplitudes are simply a vector of the controls. See hamiltonian for details.

A Generator object should generally not be instantiated directly, but via hamiltonian or liouvillian.

The list of ops and amplitudes are properties of the Generator. They should not be mutated.

See also

  • Operator for static generators, which may be obtained from a Generator via evaluate.
source
QuantumPropagators.Generators.OperatorType

A static operator in Hilbert or Liouville space.

Operator(ops::Vector{OT}, coeffs::Vector{CT})

produces an object of type Operator{OT,CT} that encapsulates the "lazy" sum

\[Ĥ = \sum_l c_l Ĥ_l\,,\]

where $Ĥ_l$ are the ops and $c_l$ are the coeffs, which each must be a constant Number. If the number of coefficients is less than the number of operators, the first ops are considered to have $c_l = 1$.

An Operator object would generally not be instantiated directly, but be obtained from a Generator via evaluate.

The $Ĥ_l$ in the sum are considered immutable. This implies that an Operator can be updated in-place with evaluate! by only changing the coeffs.

source
QuantumPropagators.Generators.ScaledOperatorType

A static operator with a scalar pre-factor.

op = ScaledOperator(α, Ĥ)

represents the "lazy" product $α Ĥ$ where $Ĥ$ is an operator (typically an Operator instance) and $α$ is a scalar.

source
QuantumPropagators.Generators.hamiltonianMethod

Initialize a (usually time-dependent) Hamiltonian.

The most common usage is, e.g.,

using QuantumPropagators
 
 H₀ = ComplexF64[0 0; 0 1];
 H₁ = ComplexF64[0 1; 1 0];
@@ -233,12 +231,12 @@
  ops::Vector{Matrix{ComplexF64}}:
   ComplexF64[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]
   ComplexF64[0.0 + 0.0im 1.0 + 0.0im; 1.0 + 0.0im 0.0 + 0.0im]
- coeffs: [2.0]

The hamiltonian function may generate warnings if the terms are of an unexpected type or structure. These can be suppressed with check=false.

source
QuantumPropagators.Generators.liouvillianFunction

Construct a Liouvillian Generator.

ℒ = liouvillian(Ĥ, c_ops=(); convention=:LvN, check=true)

calculates the sparse Liouvillian super-operator from the Hamiltonian and a list c_ops of Lindblad operators.

With convention=:LvN, applying the resulting to a vectorized density matrix ρ⃗ calculates $\frac{d}{dt} \vec{\rho}(t) = ℒ \vec{\rho}(t)$ equivalent to the Liouville-von-Neumann equation for the density matrix $ρ̂$,

\[\frac{d}{dt} ρ̂(t) + coeffs: [2.0]

The hamiltonian function may generate warnings if the terms are of an unexpected type or structure. These can be suppressed with check=false.

source
QuantumPropagators.Generators.liouvillianFunction

Construct a Liouvillian Generator.

ℒ = liouvillian(Ĥ, c_ops=(); convention=:LvN, check=true)

calculates the sparse Liouvillian super-operator from the Hamiltonian and a list c_ops of Lindblad operators.

With convention=:LvN, applying the resulting to a vectorized density matrix ρ⃗ calculates $\frac{d}{dt} \vec{\rho}(t) = ℒ \vec{\rho}(t)$ equivalent to the Liouville-von-Neumann equation for the density matrix $ρ̂$,

\[\frac{d}{dt} ρ̂(t) = -i [Ĥ, ρ̂(t)] + \sum_k\left( Â_k ρ̂ Â_k^\dagger - \frac{1}{2} A_k^\dagger Â_k ρ̂ - \frac{1}{2} ρ̂ Â_k^\dagger Â_k - \right)\,,\]

where the Lindblad operators $Â_k$ are the elements of c_ops.

The Hamiltonian $Ĥ$ will generally be time-dependent. For example, it may be a Generator as returned by hamiltonian. For example, for a Hamiltonian with the terms (Ĥ₀, (Ĥ₁, ϵ₁), (Ĥ₂, ϵ₂)), where Ĥ₀, Ĥ₁, Ĥ₂ are matrices, and ϵ₁ and ϵ₂ are functions of time, the resulting will be a Generator corresponding to terms (ℒ₀, (ℒ₁, ϵ₁), (ℒ₂, ϵ₂)), where the initial terms is the superoperator ℒ₀ for the static component of the Liouvillian, i.e., the commutator with the drift Hamiltonian Ĥ₀, plus the dissipator (sum over $k$), as a sparse matrix. Time-dependent Lindblad operators are not currently supported. The remaining elements are tuples (ℒ₁, ϵ₁) and (ℒ₂, ϵ₂) corresponding to the commutators with the two control Hamiltonians, where ℒ₁ and ℒ₂ again are sparse matrices.

If $Ĥ$ is not time-dependent, the resulting will likewise be a static operator. Passing H=nothing with non-empty c_ops initializes a pure dissipator.

With convention=:TDSE, the Liouvillian will be constructed for the equation of motion $i \hbar \frac{d}{dt} \vec{\rho}(t) = ℒ \vec{\rho}(t)$ to match exactly the form of the time-dependent Schrödinger equation. While this notation is not standard in the literature of open quantum systems, it has the benefit that the resulting can be used in a numerical propagator for a (non-Hermitian) Schrödinger equation without any change. Thus, for numerical applications, convention=:TDSE is generally preferred. The returned between the two conventions differs only by a factor of $i$, since we generally assume $\hbar=1$.

The convention keyword argument is mandatory, to force a conscious choice.

See Goerz et. al. "Optimal control theory for a unitary operation under dissipative evolution", arXiv 1312.0111v2, Appendix B.2 for the explicit construction of the Liouvillian superoperator as a sparse matrix.

Passing check=false, suppresses warnings and errors about unexpected types or the structure of the arguments, cf. hamiltonian.

source
QuantumPropagators.Arnoldi.arnoldi!Method
m = arnoldi!(Hess, q, m, Ψ, H, dt=1.0; extended=true, norm_min=1e-15)

Calculate the Hessenberg matrix and Arnoldi vectors of H dt, from Ψ.

For a given order m, the m×m Hessemberg matrix is calculated and stored in in the pre-allocated Hess. Further an array of m normalized Arnoldi vectors is stored in in the pre-allocated q, plus one additional unnormalized Arnoldi vector. The unnormalized m+1st vector could be used to easily extend a given m×m Hessenberg matrix to a (m+1)×(m+1) matrix.

If the extended Hessenberg matrix is requested (extended=true, default), the m+1st Arnoldi vector is also normalized, and it's norm will be stored in m+1, m entry of the (extended) Hessenberg matrix, which is an (m+1)×(m+1) matrix.

Return the size m of the calculated Hessenberg matrix. This will usually be the input m, except when the Krylov dimension of H starting from Ψ is less then m. E.g., if Ψ is an eigenstate of H, the returned m will be 1.

See https://en.wikipedia.org/wiki/Arnoldi_iteration for a description of the algorithm.

Arguments

  • Hess::Matrix{ComplexF64}: Pre-allocated storage for the Hessemberg matrix. Can be uninitialized on input. The matrix must be at least of size m×m, or (m+1)×(m+1) if extended=true. On output, the m×m sub-matrix of Hess (with the returned output m) will contain the Hessenberg matrix, and all other elements of Hess be be set to zero.
  • q: Pre-allocated array of states similar to Ψ, as storage for the calculated Arnoldi vectors. These may be un-initialized on input. Must be at least of length m+1
  • m: The requested dimensions of the output Hessenberg matrix.
  • Ψ: The starting vector for the Arnoldi procedure. This can be of any type, as long as Φ = H * Ψ results in a vector similar to Ψ, there is an inner products of Φ and Ψ (Ψ⋅Φ is defined), and norm(Ψ) is defined.
  • H: The operator (up to dt) for which to calculate the Arnoldi procedure. Can be of any type, as long as H * Ψ is defined.
  • dt: The implicit time step; the total operator for which to calculate the Arnoldi procedure is H * dt
  • extended: If true (default), calculate the extended Hessenberg matrix, and normalized the final Arnoldi vector
  • norm_min: the minimum value of the norm of Ψ at which Ψ should be considered the zero vector
source
QuantumPropagators.Arnoldi.diagonalize_hessenberg_matrixMethod
diagonalize_hessenberg_matrix(Hess, m; accumulate=false)

Diagonalize the m × m top left submatrix of the given Hessenberg matrix.

If accumulate is true, return the concatenated eigenvalues for Hess[1:1,1:1] to Hess[1:m,1:m], that is, all sumatrices of size 1 through m.

source
QuantumPropagators.Arnoldi.extend_arnoldi!Function

Extend dimension of Hessenberg matrix by one.

extend_arnoldi!(Hess, q, m, H, dt; norm_min=1e-15)

extends the entries in Hess from size (m-1)×(m-1) to size m×m, and the list q of Arnoldi vectors from m to (m+1). It is assumed that the input Hess was created by a call to arnoldi! with extended=false or a previous call to extend_arnoldi!. Note that Hess itself is not resized, so it must be allocated to size m×m or greater on input.

source
QuantumPropagators.Interfaces.check_amplitudeMethod

Check amplitude appearing in Generator.

@test check_amplitude(ampl; tlist, quiet=false)

verifies that the given ampl is a valid element in the list of amplitudes of a Generator object. Specifically:

If for_parameterization (may require the RecursiveArrayTools package to be loaded):

  • get_parameters(ampl) must be defined and return a vector of floats. Mutating that vector must mutate the controls inside the ampl.

The function returns true for a valid amplitude and false for an invalid amplitude. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_controlMethod

Check that control can be evaluated on a time grid.

@test check_control(
+  \right)\,,\]

where the Lindblad operators $Â_k$ are the elements of c_ops.

The Hamiltonian $Ĥ$ will generally be time-dependent. For example, it may be a Generator as returned by hamiltonian. For example, for a Hamiltonian with the terms (Ĥ₀, (Ĥ₁, ϵ₁), (Ĥ₂, ϵ₂)), where Ĥ₀, Ĥ₁, Ĥ₂ are matrices, and ϵ₁ and ϵ₂ are functions of time, the resulting will be a Generator corresponding to terms (ℒ₀, (ℒ₁, ϵ₁), (ℒ₂, ϵ₂)), where the initial terms is the superoperator ℒ₀ for the static component of the Liouvillian, i.e., the commutator with the drift Hamiltonian Ĥ₀, plus the dissipator (sum over $k$), as a sparse matrix. Time-dependent Lindblad operators are not currently supported. The remaining elements are tuples (ℒ₁, ϵ₁) and (ℒ₂, ϵ₂) corresponding to the commutators with the two control Hamiltonians, where ℒ₁ and ℒ₂ again are sparse matrices.

If $Ĥ$ is not time-dependent, the resulting will likewise be a static operator. Passing H=nothing with non-empty c_ops initializes a pure dissipator.

With convention=:TDSE, the Liouvillian will be constructed for the equation of motion $i \hbar \frac{d}{dt} \vec{\rho}(t) = ℒ \vec{\rho}(t)$ to match exactly the form of the time-dependent Schrödinger equation. While this notation is not standard in the literature of open quantum systems, it has the benefit that the resulting can be used in a numerical propagator for a (non-Hermitian) Schrödinger equation without any change. Thus, for numerical applications, convention=:TDSE is generally preferred. The returned between the two conventions differs only by a factor of $i$, since we generally assume $\hbar=1$.

The convention keyword argument is mandatory, to force a conscious choice.

See Goerz et. al. "Optimal control theory for a unitary operation under dissipative evolution", arXiv 1312.0111v2, Appendix B.2 for the explicit construction of the Liouvillian superoperator as a sparse matrix.

Passing check=false, suppresses warnings and errors about unexpected types or the structure of the arguments, cf. hamiltonian.

source
QuantumPropagators.Arnoldi.arnoldi!Method
m = arnoldi!(Hess, q, m, Ψ, H, dt=1.0; extended=true, norm_min=1e-15)

Calculate the Hessenberg matrix and Arnoldi vectors of H dt, from Ψ.

For a given order m, the m×m Hessemberg matrix is calculated and stored in in the pre-allocated Hess. Further an array of m normalized Arnoldi vectors is stored in in the pre-allocated q, plus one additional unnormalized Arnoldi vector. The unnormalized m+1st vector could be used to easily extend a given m×m Hessenberg matrix to a (m+1)×(m+1) matrix.

If the extended Hessenberg matrix is requested (extended=true, default), the m+1st Arnoldi vector is also normalized, and it's norm will be stored in m+1, m entry of the (extended) Hessenberg matrix, which is an (m+1)×(m+1) matrix.

Return the size m of the calculated Hessenberg matrix. This will usually be the input m, except when the Krylov dimension of H starting from Ψ is less then m. E.g., if Ψ is an eigenstate of H, the returned m will be 1.

See https://en.wikipedia.org/wiki/Arnoldi_iteration for a description of the algorithm.

Arguments

  • Hess::Matrix{ComplexF64}: Pre-allocated storage for the Hessemberg matrix. Can be uninitialized on input. The matrix must be at least of size m×m, or (m+1)×(m+1) if extended=true. On output, the m×m sub-matrix of Hess (with the returned output m) will contain the Hessenberg matrix, and all other elements of Hess be be set to zero.
  • q: Pre-allocated array of states similar to Ψ, as storage for the calculated Arnoldi vectors. These may be un-initialized on input. Must be at least of length m+1
  • m: The requested dimensions of the output Hessenberg matrix.
  • Ψ: The starting vector for the Arnoldi procedure. This can be of any type, as long as Φ = H * Ψ results in a vector similar to Ψ, there is an inner products of Φ and Ψ (Ψ⋅Φ is defined), and norm(Ψ) is defined.
  • H: The operator (up to dt) for which to calculate the Arnoldi procedure. Can be of any type, as long as H * Ψ is defined.
  • dt: The implicit time step; the total operator for which to calculate the Arnoldi procedure is H * dt
  • extended: If true (default), calculate the extended Hessenberg matrix, and normalized the final Arnoldi vector
  • norm_min: the minimum value of the norm of Ψ at which Ψ should be considered the zero vector
source
QuantumPropagators.Arnoldi.diagonalize_hessenberg_matrixMethod
diagonalize_hessenberg_matrix(Hess, m; accumulate=false)

Diagonalize the m × m top left submatrix of the given Hessenberg matrix.

If accumulate is true, return the concatenated eigenvalues for Hess[1:1,1:1] to Hess[1:m,1:m], that is, all sumatrices of size 1 through m.

source
QuantumPropagators.Arnoldi.extend_arnoldi!Function

Extend dimension of Hessenberg matrix by one.

extend_arnoldi!(Hess, q, m, H, dt; norm_min=1e-15)

extends the entries in Hess from size (m-1)×(m-1) to size m×m, and the list q of Arnoldi vectors from m to (m+1). It is assumed that the input Hess was created by a call to arnoldi! with extended=false or a previous call to extend_arnoldi!. Note that Hess itself is not resized, so it must be allocated to size m×m or greater on input.

source
QuantumPropagators.Interfaces.check_amplitudeMethod

Check amplitude appearing in Generator.

@test check_amplitude(ampl; tlist, quiet=false)

verifies that the given ampl is a valid element in the list of amplitudes of a Generator object. Specifically:

If for_parameterization (may require the RecursiveArrayTools package to be loaded):

  • get_parameters(ampl) must be defined and return a vector of floats. Mutating that vector must mutate the controls inside the ampl.

The function returns true for a valid amplitude and false for an invalid amplitude. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_controlMethod

Check that control can be evaluated on a time grid.

@test check_control(
     control;
     tlist,
     for_parameterization=true,
@@ -246,15 +244,10 @@
     quiet=false
 )

verifies the given control (one of the elements of the tuple returned by get_controls):

If for_time_continuous:

If for_parameterization:

  • get_parameters(control) must be defined and return a vector of floats. Mutating that vector must mutate the control.

The function returns true for a valid control and false for an invalid control. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_generatorMethod

Check the dynamical generator for propagating state over tlist.

@test check_generator(
     generator; state, tlist,
-    for_mutable_operator=true, for_immutable_operator=true,
-    for_mutable_state=true, for_immutable_state=true,
     for_pwc=true, for_time_continuous=false,
     for_expval=true, for_parameterization=false,
-    atol=1e-14, quiet=false)

verifies the given generator:

If for_pwc (default):

If for_time_continuous:

If for_parameterization (may require the RecursiveArrayTools package to be loaded):

  • get_parameters(generator) must be defined and return a vector of floats. Mutating that vector must mutate the controls inside the generator.

The function returns true for a valid generator and false for an invalid generator. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_operatorMethod

Check that op is a valid operator that can be applied to state.

@test check_operator(op; state, tlist=[0.0, 1.0],
-                     for_mutable_state=true, for_immutable_state=true,
-                     for_expval=true, atol=1e-14, quiet=false)

verifies the given op relative to state. The state must pass check_state.

An "operator" is any object that evaluate returns when evaluating a time-dependent dynamic generator. The specific requirements for op are:

If for_immutable_state (e.g., for use in propagators with inplace=false):

  • op * state must be defined

If for_mutable_state (e.g., for use in propagators with inplace=true):

  • The 3-argument LinearAlgebra.mul! must apply op to the given state
  • The 5-argument LinearAlgebra.mul! must apply op to the given state
  • LinearAlgebra.mul! must match *, if applicable
  • LinearAlgebra.mul! must return the resulting state

If for_expval (typically required for optimal control):

  • LinearAlgebra.dot(state, op, state) must return return a number
  • dot(state, op, state) must match dot(state, op * state), if applicable

The function returns true for a valid operator and false for an invalid operator. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_parameterizedMethod

Check that that the object supports the parameterization interface.

@test check_parameterized(object; name="::$typeof(object))", quiet=false)

verifies that the given object:

  • can be passed to get_parameters, which must return an AbstractVector of Float64
  • is mutated by mutating the parameters obtained by get_parameters

See also

source
QuantumPropagators.Interfaces.check_parameterized_functionMethod

Check a ParameterizedFunction instance.

@test check_parameterized_function(f; tlist; quiet=false)

verifies that the given f:

See also

source
QuantumPropagators.Interfaces.check_propagatorMethod

Check that the given propagator implements the required interface.

@test check_propagator(propagator; atol=1e-14, quiet=false)

verifies that the propagator matches the interface described for an AbstractPropagator. The propagator must have been freshly initialized with init_prop.

  • propagator must have the properties state, tlist, t, parameters, backward, and inplace
  • propagator.state must be a valid state (see check_state), with support for in-place operations (for_mutable_state=true) if propagator.inplace is true.
  • propagator.tlist must be monotonically increasing.
  • propagator.t must be the first or last element of propagator.tlist, depending on propagator.backward
  • prop_step!(propagator) must be defined and return a valid state until the time grid is exhausted
  • For an in-place propagator, the state returned by prop_step! must be the propagator.state object
  • For a not-in-place propagator, the state returned by prop_step! must be a new object
  • prop_step! must advance propagator.t forward or backward one step on the time grid
  • prop_step! must return nothing when going beyond the time grid
  • set_t!(propagator, t) must be defined and set propagator.t
  • set_state!(propagator, state) must be defined and set propagator.state.
  • set_state!(propagator, state) for an in-place propagator must overwrite propagator.state in-place.
  • set_state! must return the set propagator.state
  • In a PiecewisePropagator, propagator.parameters must be a dict mapping controls to a vector of values, one for each interval on propagator.tlist
  • reinit_prop! must be defined and re-initialize the propagator
  • reinit_prop!(propagator, state) must be idempotent. That is, repeated calls to reinit_prop! leave the propagator unchanged.

The function returns true for a valid propagator and false for an invalid propagator. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_stateMethod

Check that state is a valid element of a Hilbert space.

@test check_state(state;
-                  for_immutable_state=true, for_mutable_state=true,
-                  normalized=false, atol=1e-15, quiet=false)

verifies the following requirements:

  • The inner product (LinearAlgebra.dot) of two states must return a Complex number type.
  • The LinearAlgebra.norm of state must be defined via the inner product. This is the definition of a Hilbert space, a.k.a a complete inner product space or more precisely a Banach space (normed vector space) where the norm is induced by an inner product.

If for_immutable_state:

  • state + state and state - state must be defined
  • copy(state) must be defined
  • c * state for a scalar c must be defined
  • norm(state + state) must fulfill the triangle inequality
  • 0.0 * state must produce a state with norm 0
  • copy(state) - state must have norm 0
  • norm(state) must have absolute homogeneity: norm(s * state) = s * norm(state)

If for_mutable_state:

  • similar(state) must be defined and return a valid state
  • copyto!(other, state) must be defined
  • LinearAlgebra.lmul!(c, state) for a scalar c must be defined
  • LinearAlgebra.axpy!(c, state, other) must be defined
  • norm(state) must fulfill the same general mathematical norm properties as with for_immutable_state.

If normalized (not required by default):

  • LinearAlgebra.norm(state) must be 1

It is strongly recommended to always support immutable operations (also for mutable states)

The function returns true for a valid state and false for an invalid state. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_tlistMethod

Check that the given tlist is valid.

@test check_tlist(tlist; quiet=false)

verifies the given time grid. A valid time grid must

  • be a Vector{Float64},
  • contain at least two points (beginning and end),
  • be monotonically increasing

The function returns true for a valid time grid and false for an invalid time grid. Unless quiet=true, it will log an error to indicated which of the conditions failed.

source
QuantumPropagators.Controls.ParameterizedFunctionType

Abstract type for function-like objects with parameters.

A struct that is an implementation of a ParameterizedFunction:

  • must have a parameters field that is an AbstractVector of floats (e.g., a ComponentArrays.ComponentVector)
  • must be callable with a single float argument t,
  • may define getters and setters for referencing the values in parameters with convenient names.

The parameters field of any ParameterizedFunction can be accessed via get_parameters.

See How to define a parameterized control for an example. You may use the QuantumPropagators.Interfaces.check_parameterized_function to check the implementation of a ParameterizedFunction subtype.

source
QuantumPropagators.Controls.discretizeMethod

Evaluate control at every point of tlist.

values = discretize(control, tlist; via_midpoints=true)

discretizes the given control to a Vector of values defined on the points of tlist.

If control is a function, it is first evaluated at the midpoint of tlist, see discretize_on_midpoints, and then the values on the midpoints are converted to values on tlist. This discretization is more stable than directly evaluating the control function at the values of tlist, and ensures that repeated round-trips between discretize and discretize_on_midpoints can be done safely, see the note in the documentation of discretize_on_midpoints.

The latter can still be achieved by passing via_midpoints=false. While such a direct discretization is suitable e.g. for plotting, but it is unsuitable for round-trips between discretize and discretize_on_midpoints (constant controls on tlist may result in a zig-zag on the intervals of tlist).

If control is a vector, a copy of control will be returned if it is of the same length as tlist. Otherwise, control must have one less value than tlist, and is assumed to be defined on the midpoints of tlist. In that case, discretize acts as the inverse of discretize_on_midpoints. See discretize_on_midpoints for how control values on tlist and control values on the intervals of tlist are related.

source
QuantumPropagators.Controls.discretize_on_midpointsMethod

Evaluate control at the midpoints of tlist.

values = discretize_on_midpoints(control, tlist)

discretizes the given control to a Vector of values on the midpoints of tlist. Hence, the resulting values will contain one less value than tlist.

If control is a vector of values defined on tlist (i.e., of the same length as tlist), it will be converted to a vector of values on the intervals of tlist. The value for the first and last "midpoint" will remain the original values at the beginning and end of tlist, in order to ensure exact boundary conditions. For all other midpoints, the value for that midpoint will be calculated by "un-averaging".

For example, for a control and tlist of length 5, consider the following diagram:

tlist index:       1   2   3   4   5
+    atol=1e-14, quiet=false)

verifies the given generator:

If for_pwc (default):

If for_time_continuous:

If for_parameterization (may require the RecursiveArrayTools package to be loaded):

  • get_parameters(generator) must be defined and return a vector of floats. Mutating that vector must mutate the controls inside the generator.

The function returns true for a valid generator and false for an invalid generator. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_operatorMethod

Check that op is a valid operator that can be applied to state.

@test check_operator(op; state, tlist=[0.0, 1.0],
+                     for_expval=true, atol=1e-14, quiet=false)

verifies the given op relative to state. The state must pass check_state.

An "operator" is any object that evaluate returns when evaluating a time-dependent dynamic generator. The specific requirements for op are:

If QuantumPropagators.Interfaces.supports_inplace(state):

  • The 3-argument LinearAlgebra.mul! must apply op to the given state
  • The 5-argument LinearAlgebra.mul! must apply op to the given state
  • LinearAlgebra.mul! must match *, if applicable
  • LinearAlgebra.mul! must return the resulting state

If for_expval (typically required for optimal control):

  • LinearAlgebra.dot(state, op, state) must return return a number
  • dot(state, op, state) must match dot(state, op * state), if applicable

The function returns true for a valid operator and false for an invalid operator. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_parameterizedMethod

Check that that the object supports the parameterization interface.

@test check_parameterized(object; name="::$typeof(object))", quiet=false)

verifies that the given object:

  • can be passed to get_parameters, which must return an AbstractVector of Float64
  • is mutated by mutating the parameters obtained by get_parameters

See also

source
QuantumPropagators.Interfaces.check_parameterized_functionMethod

Check a ParameterizedFunction instance.

@test check_parameterized_function(f; tlist; quiet=false)

verifies that the given f:

See also

source
QuantumPropagators.Interfaces.check_propagatorMethod

Check that the given propagator implements the required interface.

@test check_propagator(propagator; atol=1e-14, quiet=false)

verifies that the propagator matches the interface described for an AbstractPropagator. The propagator must have been freshly initialized with init_prop.

  • propagator must have the properties state, tlist, t, parameters, backward, and inplace
  • propagator.state must be a valid state (see check_state)
  • If propagator.inplace is true, supports_inplace for propagator.state must also be true
  • propagator.tlist must be monotonically increasing.
  • propagator.t must be the first or last element of propagator.tlist, depending on propagator.backward
  • prop_step!(propagator) must be defined and return a valid state until the time grid is exhausted
  • For an in-place propagator, the state returned by prop_step! must be the propagator.state object
  • For a not-in-place propagator, the state returned by prop_step! must be a new object
  • prop_step! must advance propagator.t forward or backward one step on the time grid
  • prop_step! must return nothing when going beyond the time grid
  • set_t!(propagator, t) must be defined and set propagator.t
  • set_state!(propagator, state) must be defined and set propagator.state.
  • set_state!(propagator, state) for an in-place propagator must overwrite propagator.state in-place.
  • set_state! must return the set propagator.state
  • In a PiecewisePropagator, propagator.parameters must be a dict mapping controls to a vector of values, one for each interval on propagator.tlist
  • reinit_prop! must be defined and re-initialize the propagator
  • reinit_prop!(propagator, state) must be idempotent. That is, repeated calls to reinit_prop! leave the propagator unchanged.

The function returns true for a valid propagator and false for an invalid propagator. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_stateMethod

Check that state is a valid element of a Hilbert space.

@test check_state(state; normalized=false, atol=1e-15, quiet=false)

verifies the following requirements:

  • The inner product (LinearAlgebra.dot) of two states must return a Complex number.
  • The LinearAlgebra.norm of state must be defined via the inner product. This is the definition of a Hilbert space, a.k.a a "complete inner product space" or more precisely a "Banach space (normed vector space) where the norm is induced by an inner product".
  • The `QuantumPropagators.Interfaces.supports_inplace method must be defined for state

Any state must support the following not-in-place operations:

  • state + state and state - state must be defined
  • copy(state) must be defined and return an object of the same type as state
  • c * state for a scalar c must be defined
  • norm(state + state) must fulfill the triangle inequality
  • zero(state) must be defined and produce a state with norm 0
  • 0.0 * state must produce a state with norm 0
  • copy(state) - state must have norm 0
  • norm(state) must have absolute homogeneity: norm(s * state) = s * norm(state)

If supports_inplace(state) is true, the state must also support the following:

  • similar(state) must be defined and return a valid state of the same type a state
  • copyto!(other, state) must be defined
  • fill!(state, c) must be defined
  • LinearAlgebra.lmul!(c, state) for a scalar c must be defined
  • LinearAlgebra.axpy!(c, state, other) must be defined
  • norm(state) must fulfill the same general mathematical norm properties as for the non-in-place norm.

If normalized (not required by default):

  • LinearAlgebra.norm(state) must be 1

It is strongly recommended to always support immutable operations (also for mutable states)

The function returns true for a valid state and false for an invalid state. Unless quiet=true, it will log an error to indicate which of the conditions failed.

source
QuantumPropagators.Interfaces.check_tlistMethod

Check that the given tlist is valid.

@test check_tlist(tlist; quiet=false)

verifies the given time grid. A valid time grid must

  • be a Vector{Float64},
  • contain at least two points (beginning and end),
  • be monotonically increasing

The function returns true for a valid time grid and false for an invalid time grid. Unless quiet=true, it will log an error to indicated which of the conditions failed.

source
QuantumPropagators.Interfaces.supports_inplaceMethod

Indicate whether a given state or operator supports in-place operations

supports_inplace(state)

Indicates that propagators can assume that the in-place requirements defined in QuantumPropagators.Interfaces.check_state hold. States with in-place support must also fulfill specific properties when interacting with operators, see QuantumPropagators.Interfaces.check_operator.

supports_inplace(op)

Indicates that the operator can be evaluated in-place with evaluate!, see QuantumPropagators.Interfaces.check_generator

Note that supports_inplace is not quite the same as Base.ismutable: When using custom structs for states or operators, even if those structs are not defined as mutable, they may still define the in-place interface (typically because their components are mutable).

source
QuantumPropagators.Controls.ParameterizedFunctionType

Abstract type for function-like objects with parameters.

A struct that is an implementation of a ParameterizedFunction:

  • must have a parameters field that is an AbstractVector of floats (e.g., a ComponentArrays.ComponentVector)
  • must be callable with a single float argument t,
  • may define getters and setters for referencing the values in parameters with convenient names.

The parameters field of any ParameterizedFunction can be accessed via get_parameters.

See How to define a parameterized control for an example. You may use the QuantumPropagators.Interfaces.check_parameterized_function to check the implementation of a ParameterizedFunction subtype.

source
QuantumPropagators.Controls.discretizeMethod

Evaluate control at every point of tlist.

values = discretize(control, tlist; via_midpoints=true)

discretizes the given control to a Vector of values defined on the points of tlist.

If control is a function, it is first evaluated at the midpoint of tlist, see discretize_on_midpoints, and then the values on the midpoints are converted to values on tlist. This discretization is more stable than directly evaluating the control function at the values of tlist, and ensures that repeated round-trips between discretize and discretize_on_midpoints can be done safely, see the note in the documentation of discretize_on_midpoints.

The latter can still be achieved by passing via_midpoints=false. While such a direct discretization is suitable e.g. for plotting, but it is unsuitable for round-trips between discretize and discretize_on_midpoints (constant controls on tlist may result in a zig-zag on the intervals of tlist).

If control is a vector, a copy of control will be returned if it is of the same length as tlist. Otherwise, control must have one less value than tlist, and is assumed to be defined on the midpoints of tlist. In that case, discretize acts as the inverse of discretize_on_midpoints. See discretize_on_midpoints for how control values on tlist and control values on the intervals of tlist are related.

source
QuantumPropagators.Controls.discretize_on_midpointsMethod

Evaluate control at the midpoints of tlist.

values = discretize_on_midpoints(control, tlist)

discretizes the given control to a Vector of values on the midpoints of tlist. Hence, the resulting values will contain one less value than tlist.

If control is a vector of values defined on tlist (i.e., of the same length as tlist), it will be converted to a vector of values on the intervals of tlist. The value for the first and last "midpoint" will remain the original values at the beginning and end of tlist, in order to ensure exact boundary conditions. For all other midpoints, the value for that midpoint will be calculated by "un-averaging".

For example, for a control and tlist of length 5, consider the following diagram:

tlist index:       1   2   3   4   5
 tlist:             ⋅   ⋅   ⋅   ⋅   ⋅   input values cᵢ (i ∈ 1..5)
                    |̂/ ̄ ̄ ̂\ / ̂\ / ̂ ̄ ̄\|̂
 midpoints:         x     x   x     x   output values pᵢ (i ∈ 1..4)
@@ -274,4 +267,4 @@
     norm_min=1e-15,
     enlarge=true
 )

uses Arnoldi iteration with state as the starting vector. It approximates the eigenvalues of H with between m_min and m_max Ritz values, until the lowest and highest eigenvalue are stable to a relative precision of prec. The norm_min parameter is passed to the underlying arnoldi!.

If enlarge=true (default) the returned E_min and E_max will be enlarged via a heuristic to slightly over-estimate the spectral radius instead of under-estimating it.

source
QuantumPropagators.SpectralRange.specrangeMethod
E_min, E_max = specrange(H, :diag)

uses exact diagonization via the standard eigvals function to obtain the smallest and largest eigenvalue. This should only be used for relatively small matrices.

source
QuantumPropagators.SpectralRange.specrangeMethod
E_min, E_max = specrange(H, :manual; E_min, E_max)

directly returns the given E_min and E_max without considering H.

source
QuantumPropagators.SpectralRange.specrangeMethod

Calculate the spectral range of a Hamiltonian H on the real axis.

E_min, E_max = specrange(H; method=:auto, kwargs...)

calculates the approximate lowest and highest eigenvalues of H. Any imaginary part in the eigenvalues is ignored: the routine is intended for (although not strictly limited to) a Hermitian H.

This delegates to

specrange(H, method; kwargs...)

for the different methods.

The default method=:auto chooses the best method for the given H. This is :diag for small matrices, and :arnoldi otherwise. If both E_min and E_max are given in the kwargs, those will be returned directly (method=:manual).

Keyword arguments not relevant to the underlying implementation will be ignored.

source
QuantumPropagators.Newton.NewtonWrkType
NewtonWrk(v0, m_max=10)

Workspace for the Newton-with-restarted-Arnoldi propagation routine.

Initializes the workspace for the propagation of a vector v0, using a maximum Krylov dimension of m_max in each restart iteration. Note that m_max should be smaller than the length of v0.

source
QuantumPropagators.Newton.extend_leja!Method
extend_leja!(leja, n, newpoints, n_use)

Given an array of n (ordered) Leja points, extract n_use points from newpoints, and append them to the existing Leja points. The array leja should be sufficiently large to hold the new Leja points, which are appended after index n_old. It will be re-allocated if necessary and may have a size of up to 2*(n+n_use).

Arguments

  • leja: Array of leja values. Must contain the "old" leja values to be kept in leja(0:n-1). On output, n_use new leja points will be in leja(n+:n+n_use-1), for the original value of n. The leja array must use zero-based indexing.
  • n: On input, number of "old" leja points in leja. On output, total number of leja points (i.e. n=n+n_use)
  • newpoints: On input, candidate points for new leja points. The n_use best values will be chosen and added to leja. On output, the values of new_points are undefined.
  • n_use: Number of points that should be added to leja
source
QuantumPropagators.Newton.extend_newton_coeffs!Method
extend_newton_coeffs!(a, n_a, leja, func, n_leja, radius)

Extend the array a of existing Newton coefficients for the expansion of the func from n_a coefficients to n_leja coefficients. Return a new value n_a=n_a+n_leja with the total number of Newton coefficients in the updated a.

Arguments

  • a: On input, a zero-based array of length n_a or greater, containing Newton coefficients. On output, array containing a total n_leja coefficients. The array a will be resized if necessary, and may have a length greater than n_leja on output
  • n_a: The number of Newton coefficients in a, on input. Elements of a beyond the first n_a elements will be overwritten.
  • leja: Array of normalized Leja points, containing at least n_leja elements.
  • func: Function for which to calculate Newton coefficients
  • n_leja: The number of elements in leja to use for calculating new coefficients, and the total number of Newton coefficients on output
  • radius: Normalization radius for divided differences
source
QuantumPropagators.Newton.newton!Method
newton!(Ψ, H, dt, wrk; func=(z -> exp(-1im*z)), norm_min=1e-14, relerr=1e-12,
-        max_restarts=50, _...)

Evaluate Ψ = func(H*dt) Ψ using a Newton-with-restarted-Arnoldi scheme.

Arguments

  • Ψ: The state to propagate, will be overwritten in-place with the propagated state
  • H: Operator acting on Ψ. Together with dt, this is the argument to func
  • dt: Implicit time step. Together with H, this is the argument to func
  • wkr: Work array, initialized with NewtonWrk
  • func: The function to apply to H dt, taking a single (scalar) complex-valued argument z in place of H dt. The default func is to evaluate the time evaluations operator for the Schrödinger equation
  • norm_min: the minimum norm at which to consider a state similar to Ψ as zero
  • relerr: The relative error defining the convergence condition for the restart iteration. Propagation stops when the norm of the accumulated Ψ is stable up to the given relative error
  • max_restarts: The maximum number of restart iterations. Exceeding max_restarts will throw an AssertionError.

All other keyword arguments are ignored.

source
QuantumPropagators.Cheby.ChebyWrkType

Workspace for the Chebychev propagation routine.

ChebyWrk(Ψ, Δ, E_min, dt; limit=1e-12)

initializes the workspace for the propagation of a state similar to Ψ under a Hamiltonian with eigenvalues between E_min and E_min + Δ, and a time step dt. Chebychev coefficients smaller than the given limit are discarded.

source
QuantumPropagators.Cheby.cheby!Method

Evaluate Ψ = exp(-𝕚 * H * dt) Ψ in-place.

cheby!(Ψ, H, dt, wrk; E_min=nothing, check_normalization=false)

Arguments

  • Ψ: on input, initial vector. Will be overwritten with result.
  • H: Hermitian operator
  • dt: time step
  • wrk: internal workspace
  • E_min: minimum eigenvalue of H, to be used instead of the E_min from the initialization of wrk. The same wrk may be used for different values E_min, as long as the spectra radius Δ and the time step dt are the same as those used for the initialization of wrk.
  • check_normalizataion: perform checks that the H does not exceed the spectral radius for which the workspace was initialized.

The routine will not allocate any internal storage. This implementation requires copyto! lmul!, and axpy! to be implemented for Ψ, and the three-argument mul! for Ψ and H.

source
QuantumPropagators.Cheby.chebyMethod

Evaluate Ψ = exp(-𝕚 * H * dt) Ψ.

Ψ_out = cheby(Ψ, H, dt, wrk; E_min=nothing, check_normalization=false)

acts like cheby! but does not modify Ψ in-place.

source
QuantumPropagators.Cheby.cheby_coeffs!Function

Calculate Chebychev coefficients in-place.

n::Int = cheby_coeffs!(coeffs, Δ, dt, limit=1e-12)

overwrites the first n values in coeffs with new coefficients larger than limit for the given new spectral radius Δ and time step dt. The coeffs array will be resized if necessary, and may have a length > n on exit.

See also cheby_coeffs for an non-in-place version.

source
QuantumPropagators.Cheby.cheby_coeffsMethod

Calculate Chebychev coefficients.

a::Vector{Float64} = cheby_coeffs(Δ, dt; limit=1e-12)

return an array of coefficients larger than limit.

Arguments

  • Δ: the spectral radius of the underlying operator
  • dt: the time step

See also cheby_coeffs! for an in-place version.

source
+ max_restarts=50, _...)

Evaluate Ψ = func(H*dt) Ψ using a Newton-with-restarted-Arnoldi scheme.

Arguments

All other keyword arguments are ignored.

source
QuantumPropagators.Cheby.ChebyWrkType

Workspace for the Chebychev propagation routine.

ChebyWrk(Ψ, Δ, E_min, dt; limit=1e-12)

initializes the workspace for the propagation of a state similar to Ψ under a Hamiltonian with eigenvalues between E_min and E_min + Δ, and a time step dt. Chebychev coefficients smaller than the given limit are discarded.

source
QuantumPropagators.Cheby.cheby!Method

Evaluate Ψ = exp(-𝕚 * H * dt) Ψ in-place.

cheby!(Ψ, H, dt, wrk; E_min=nothing, check_normalization=false)

Arguments

  • Ψ: on input, initial vector. Will be overwritten with result.
  • H: Hermitian operator
  • dt: time step
  • wrk: internal workspace
  • E_min: minimum eigenvalue of H, to be used instead of the E_min from the initialization of wrk. The same wrk may be used for different values E_min, as long as the spectra radius Δ and the time step dt are the same as those used for the initialization of wrk.
  • check_normalizataion: perform checks that the H does not exceed the spectral radius for which the workspace was initialized.

The routine will not allocate any internal storage. This implementation requires copyto! lmul!, and axpy! to be implemented for Ψ, and the three-argument mul! for Ψ and H.

source
QuantumPropagators.Cheby.chebyMethod

Evaluate Ψ = exp(-𝕚 * H * dt) Ψ.

Ψ_out = cheby(Ψ, H, dt, wrk; E_min=nothing, check_normalization=false)

acts like cheby! but does not modify Ψ in-place.

source
QuantumPropagators.Cheby.cheby_coeffs!Function

Calculate Chebychev coefficients in-place.

n::Int = cheby_coeffs!(coeffs, Δ, dt, limit=1e-12)

overwrites the first n values in coeffs with new coefficients larger than limit for the given new spectral radius Δ and time step dt. The coeffs array will be resized if necessary, and may have a length > n on exit.

See also cheby_coeffs for an non-in-place version.

source
QuantumPropagators.Cheby.cheby_coeffsMethod

Calculate Chebychev coefficients.

a::Vector{Float64} = cheby_coeffs(Δ, dt; limit=1e-12)

return an array of coefficients larger than limit.

Arguments

  • Δ: the spectral radius of the underlying operator
  • dt: the time step

See also cheby_coeffs! for an in-place version.

source
diff --git a/dev/index.html b/dev/index.html index e4a02bb..f9cda47 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Home · Krotov.jl
+Home · Krotov.jl
diff --git a/dev/objects.inv b/dev/objects.inv index bd02049..3c8b765 100644 Binary files a/dev/objects.inv and b/dev/objects.inv differ diff --git a/dev/overview/index.html b/dev/overview/index.html index 19744b8..5cd1b26 100644 --- a/dev/overview/index.html +++ b/dev/overview/index.html @@ -1,2 +1,2 @@ -Overview · Krotov.jl
+Overview · Krotov.jl
diff --git a/dev/references/index.html b/dev/references/index.html index 708bed5..beb64a4 100644 --- a/dev/references/index.html +++ b/dev/references/index.html @@ -1,2 +1,2 @@ -References · Krotov.jl

References

[1]
V. F. Krotov. Global Methods in Optimal Control (Dekker, New York, NY, USA, 1996).
[2]
J. Somlói, V. A. Kazakov and D. J. Tannor. Controlled dissociation of I$_2$ via optical transitions between the X and B electronic states. Chem. Phys. 172, 85 (1993).
[3]
A. Bartana, R. Kosloff and D. J. Tannor. Laser cooling of internal degrees of freedom. II. J. Chem. Phys. 106, 1435 (1997).
[4]
J. P. Palao and R. Kosloff. Optimal control theory for unitary transformations. Phys. Rev. A 68, 062308 (2003).
[5]
D. M. Reich, M. Ndong and C. P. Koch. Monotonically convergent optimization in quantum control using Krotov's method. J. Chem. Phys. 136, 104103 (2012).
[6]
M. H. Goerz, D. Basilewitsch, F. Gago-Encinas, M. G. Krauss, K. P. Horn, D. M. Reich and C. P. Koch. Krotov: A Python implementation of Krotov's method for quantum optimal control. SciPost Phys. 7, 080 (2019).
[7]
M. H. Goerz, S. C. Carrasco and V. S. Malinovsky. Quantum Optimal Control via Semi-Automatic Differentiation. Quantum 6, 871 (2022).
[8]
M. H. Goerz, D. M. Reich and C. P. Koch. Optimal control theory for a unitary operation under dissipative evolution. New J. Phys. 16, 055012 (2014).
[9]
S. Machnes, E. Assémat, D. Tannor and F. K. Wilhelm. Tunable, Flexible, and Efficient Optimization of Control Pulses for Practical Qubits. Phys. Rev. Lett. 120, 150401 (2018).
[10]
T. Caneva, T. Calarco and S. Montangero. Chopped random-basis quantum optimization. Phys. Rev. A 84, 022326 (2011).
+References · Krotov.jl

References

[1]
V. F. Krotov. Global Methods in Optimal Control (Dekker, New York, NY, USA, 1996).
[2]
J. Somlói, V. A. Kazakov and D. J. Tannor. Controlled dissociation of I$_2$ via optical transitions between the X and B electronic states. Chem. Phys. 172, 85 (1993).
[3]
A. Bartana, R. Kosloff and D. J. Tannor. Laser cooling of internal degrees of freedom. II. J. Chem. Phys. 106, 1435 (1997).
[4]
J. P. Palao and R. Kosloff. Optimal control theory for unitary transformations. Phys. Rev. A 68, 062308 (2003).
[5]
D. M. Reich, M. Ndong and C. P. Koch. Monotonically convergent optimization in quantum control using Krotov's method. J. Chem. Phys. 136, 104103 (2012).
[6]
M. H. Goerz, D. Basilewitsch, F. Gago-Encinas, M. G. Krauss, K. P. Horn, D. M. Reich and C. P. Koch. Krotov: A Python implementation of Krotov's method for quantum optimal control. SciPost Phys. 7, 080 (2019).
[7]
M. H. Goerz, S. C. Carrasco and V. S. Malinovsky. Quantum Optimal Control via Semi-Automatic Differentiation. Quantum 6, 871 (2022).
[8]
M. H. Goerz, D. M. Reich and C. P. Koch. Optimal control theory for a unitary operation under dissipative evolution. New J. Phys. 16, 055012 (2014).
[9]
S. Machnes, E. Assémat, D. Tannor and F. K. Wilhelm. Tunable, Flexible, and Efficient Optimization of Control Pulses for Practical Qubits. Phys. Rev. Lett. 120, 150401 (2018).
[10]
T. Caneva, T. Calarco and S. Montangero. Chopped random-basis quantum optimization. Phys. Rev. A 84, 022326 (2011).
diff --git a/dev/search_index.js b/dev/search_index.js index 756dc7b..c0c1b66 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"references/#References","page":"References","title":"References","text":"","category":"section"},{"location":"references/","page":"References","title":"References","text":"V. F. Krotov. Global Methods in Optimal Control (Dekker, New York, NY, USA, 1996).\n\n\n\nJ. Somlói, V. A. Kazakov and D. J. Tannor. Controlled dissociation of I_2 via optical transitions between the X and B electronic states. Chem. Phys. 172, 85 (1993).\n\n\n\nA. Bartana, R. Kosloff and D. J. Tannor. Laser cooling of internal degrees of freedom. II. J. Chem. Phys. 106, 1435 (1997).\n\n\n\nJ. P. Palao and R. Kosloff. Optimal control theory for unitary transformations. Phys. Rev. A 68, 062308 (2003).\n\n\n\nD. M. Reich, M. Ndong and C. P. Koch. Monotonically convergent optimization in quantum control using Krotov's method. J. Chem. Phys. 136, 104103 (2012).\n\n\n\nM. H. Goerz, D. Basilewitsch, F. Gago-Encinas, M. G. Krauss, K. P. Horn, D. M. Reich and C. P. Koch. Krotov: A Python implementation of Krotov's method for quantum optimal control. SciPost Phys. 7, 080 (2019).\n\n\n\nM. H. Goerz, S. C. Carrasco and V. S. Malinovsky. Quantum Optimal Control via Semi-Automatic Differentiation. Quantum 6, 871 (2022).\n\n\n\nM. H. Goerz, D. M. Reich and C. P. Koch. Optimal control theory for a unitary operation under dissipative evolution. New J. Phys. 16, 055012 (2014).\n\n\n\nS. Machnes, E. Assémat, D. Tannor and F. K. Wilhelm. Tunable, Flexible, and Efficient Optimization of Control Pulses for Practical Qubits. Phys. Rev. Lett. 120, 150401 (2018).\n\n\n\nT. Caneva, T. Calarco and S. Montangero. Chopped random-basis quantum optimization. Phys. Rev. A 84, 022326 (2011).\n\n\n\n","category":"page"},{"location":"api/#API","page":"API","title":"API","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Pages = [\"api.md\"]","category":"page"},{"location":"api/","page":"API","title":"API","text":"Modules = [Krotov]","category":"page"},{"location":"api/#Krotov.KrotovResult","page":"API","title":"Krotov.KrotovResult","text":"Result object returned by optimize_krotov.\n\nAttributes\n\nThe attributes of a KrotovResult object include\n\niter: The number of the current iteration\nJ_T: The value of the final-time functional in the current iteration\nJ_T_prev: The value of the final-time functional in the previous iteration\ntlist: The time grid on which the control are discretized.\nguess_controls: A vector of the original control fields (each field discretized to the points of tlist)\noptimized_controls: A vector of the optimized control fields. Calculated only at the end of the optimization, not after each iteration.\ntau_vals: For any trajectory that defines a target_state, the complex overlap of that target state with the propagated state. For any trajectory for which the target_state is nothing, the value is zero.\nrecords: A vector of tuples with values returned by a callback routine passed to optimize\nconverged: A boolean flag on whether the optimization is converged. This may be set to true by a check_convergence function.\nmessage: A message string to explain the reason for convergence. This may be set by a check_convergence function.\n\nAll of the above attributes may be referenced in a check_convergence function passed to optimize(problem; method=Krotov)\n\n\n\n\n\n","category":"type"},{"location":"api/#Krotov.KrotovWrk","page":"API","title":"Krotov.KrotovWrk","text":"Krotov workspace.\n\nThe workspace is for internal use. However, it is also accessible in a callback function. The callback may use or modify some of the following attributes:\n\ntrajectories: a copy of the trajectories defining the control problem\nadjoint_trajectories: The trajectories with the adjoint generator\nkwargs: The keyword arguments from the ControlProblem or the call to optimize.\ncontrols: A tuple of the original controls (probably functions)\nga_a_int: The current value of gₐ(t)dt for each control\nupdate_shapes: The update shapes S(t) for each pulse, discretized on the intervals of the time grid.\nlambda_vals: The current value of λₐ for each control\nresult: The current result object\nfw_storage: The storage of states for the forward propagation\nfw_propagators: The propagators used for the forward propagation\nbw_propagators: The propagators used for the backward propagation\nuse_threads: Flag indicating whether the propagations are performed in parallel.\n\n\n\n\n\n","category":"type"},{"location":"api/#Krotov.optimize_krotov-Tuple{Any}","page":"API","title":"Krotov.optimize_krotov","text":"See optimize(problem; method=Krotov, kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"api/#QuantumControlBase.optimize-Tuple{Any, Val{:Krotov}}","page":"API","title":"QuantumControlBase.optimize","text":"using Krotov\nresult = optimize(problem; method=Krotov, kwargs...)\n\noptimizes the given control problem using Krotov's method, returning a KrotovResult.\n\nKeyword arguments that control the optimization are taken from the keyword arguments used in the instantiation of problem; any of these can be overridden with explicit keyword arguments to optimize.\n\nRequired problem keyword arguments\n\nJ_T: A function J_T(Ψ, trajectories) that evaluates the final time functional from a list Ψ of forward-propagated states and problem.trajectories. The function J_T may also take a keyword argument tau. If it does, a vector containing the complex overlaps of the target states (target_state property of each trajectory in problem.trajectories) with the propagated states will be passed to J_T.\n\nRecommended problem keyword arguments\n\nlambda_a=1.0: The inverse Krotov step width λₐ for every pulse.\nupdate_shape=(t->1.0): A function S(t) for the \"update shape\" that scales the update for every pulse.\n\nIf different controls require different lambda_a or update_shape, a dict pulse_options must be given instead of a global lambda_a and update_shape; see below.\n\nOptional problem keyword arguments\n\nThe following keyword arguments are supported (with default values):\n\npulse_options: A dictionary that maps every control (as obtained by get_controls from the problem.trajectories) to the following dict:\n:lambda_a: The value for inverse Krotov step width λₐ.\n:update_shape: A function S(t) for the \"update shape\" that scales the Krotov pulse update.\nThis overrides the global lambda_a and update_shape arguments.\nchi: A function chi(Ψ, trajectories) that receives a list Ψ of the forward propagated states and returns a vector of states χₖ = -J_TΨₖ. If not given, it will be automatically determined from J_T via make_chi with the default parameters. Similarly to J_T, if chi accepts a keyword argument tau, it will be passed a vector of complex overlaps.\nsigma=nothing: A function that calculates the second-order contribution. If not given, the first-order Krotov method is used.\niter_start=0: The initial iteration number.\niter_stop=5000: The maximum iteration number.\nprop_method: The propagation method to use for each trajectory; see below.\nprint_iters=true: Whether to print information after each iteration.\nstore_iter_info=Set(): Which fields from print_iters to store in result.records. A subset of Set([\"iter.\", \"J_T\", \"∫gₐ(t)dt\", \"J\", \"ΔJ_T\", \"ΔJ\", \"secs\"]).\ncallback: A function (or tuple of functions) that receives the Krotov workspace, the iteration number, the list of updated pulses, and the list of guess pulses as positional arguments. The function may return a tuple of values which are stored in the KrotovResult object result.records. The function can also mutate any of its arguments, in particular the updated pulses. This may be used, e.g., to apply a spectral filter to the updated pulses or to perform similar manipulations. Note that print_iters=true (default) adds an automatic callback to print information after each iteration. With store_iter_info, that callback automatically stores a subset of the printed information.\ncheck_convergence: A function to check whether convergence has been reached. Receives a KrotovResult object result, and should set result.converged to true and result.message to an appropriate string in case of convergence. Multiple convergence checks can be performed by chaining functions with ∘. The convergence check is performed after any callback.\nverbose=false: If true, print information during initialization.\nrethrow_exceptions: By default, any exception ends the optimization but still returns a KrotovResult that captures the message associated with the exception. This is to avoid losing results from a long-running optimization when an exception occurs in a later iteration. If rethrow_exceptions=true, instead of capturing the exception, it will be thrown normally.\n\nTrajectory propagation\n\nKrotov's method involves the forward and backward propagation for every Trajectory in the problem. The keyword arguments for each propagation (see propagate) are determined from any properties of each Trajectory that have a prop_ prefix, cf. init_prop_trajectory.\n\nIn situations where different parameters are required for the forward and backward propagation, instead of the prop_ prefix, the fw_prop_ and bw_prop_ prefixes can be used, respectively. These override any setting with the prop_ prefix. This applies both to the properties of each Trajectory and the problem keyword arguments.\n\nNote that the propagation method for each propagation must be specified. In most cases, it is sufficient (and recommended) to pass a global prop_method problem keyword argument.\n\n\n\n\n\n","category":"method"},{"location":"examples/#Examples","page":"Examples","title":"Examples","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The following examples illustrate the use of Krotov's method:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Example: Entangling quantum gates for coupled transmon qubits. Written for GRAPE, but could use Krotov's method interchangeably.\nExample: Optimization of a Dissipative Quantum Gate\nTutorial: Pulse Parametrization for Krotov's Method","category":"page"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumControlBase]","category":"page"},{"location":"externals/#QuantumControlBase.ControlProblem","page":"-","title":"QuantumControlBase.ControlProblem","text":"A full control problem with multiple trajectories.\n\nControlProblem(\n trajectories,\n tlist;\n kwargs...\n)\n\nThe trajectories are a list of Trajectory instances, each defining an initial state and a dynamical generator for the evolution of that state. Usually, the trajectory will also include a target state (see Trajectory) and possibly a weight. The trajectories may also be given together with tlist as a mandatory keyword argument.\n\nThe tlist is the time grid on which the time evolution of the initial states of each trajectory should be propagated. It may also be given as a (mandatory) keyword argument.\n\nThe remaining kwargs are keyword arguments that are passed directly to the optimal control method. These typically include e.g. the optimization functional.\n\nThe control problem is solved by finding a set of controls that minimize an optimization functional over all trajectories.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumControlBase.Trajectory","page":"-","title":"QuantumControlBase.Trajectory","text":"Description of a state's time evolution.\n\nTrajectory(\n initial_state,\n generator;\n target_state=nothing,\n weight=1.0,\n kwargs...\n)\n\ndescribes the time evolution of the initial_state under a time-dependent dynamical generator (e.g., a Hamiltonian or Liouvillian).\n\nTrajectories are central to quantum control problems: an optimization functional depends on the result of propagating one or more trajectories. For example, when optimizing for a quantum gate, the optimization considers the trajectories of all logical basis states.\n\nIn addition to the initial_state and generator, a Trajectory may include data relevant to the propagation and to evaluating a particular optimization functional. Most functionals have the notion of a \"target state\" that the initial_state should evolve towards, which can be given as the target_state keyword argument. In some functionals, different trajectories enter with different weights [8], which can be given as a weight keyword argument. Any other keyword arguments are also available to a functional as properties of the Trajectory .\n\nA Trajectory can also be instantiated using all keyword arguments.\n\nProperties\n\nAll keyword arguments used in the instantiation are available as properties of the Trajectory. At a minimum, this includes initial_state, generator, target_state, and weight.\n\nBy convention, properties with a prop_ prefix, e.g., prop_method, will be taken into account when propagating the trajectory. See propagate_trajectory for details.\n\n\n\n\n\n","category":"type"},{"location":"externals/#Base.adjoint-Tuple{Trajectory}","page":"-","title":"Base.adjoint","text":"Construct the adjoint of a Trajectory.\n\nadj_trajectory = adjoint(trajectory)\n\nThe adjoint trajectory contains the adjoint of the dynamical generator traj.generator. All other fields contain a copy of the original field value.\n\nThe primary purpose of this adjoint is to facilitate the backward propagation under the adjoint generator that is central to gradient-based optimization methods such as GRAPE and Krotov's method.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.chain_callbacks-Tuple","page":"-","title":"QuantumControlBase.chain_callbacks","text":"Combine multiple callback functions.\n\nchain_callbacks(funcs...)\n\ncombines funcs into a single Function that can be passes as callback to ControlProblem or any optimize-function.\n\nEach function in func must be a suitable callback by itself. This means that it should receive the optimization workspace object as its first positional parameter, then positional parameters specific to the optimization method, and then an arbitrary number of data parameters. It must return either nothing or a tuple of \"info\" objects (which will end up in the records field of the optimization result).\n\nWhen chaining callbacks, the funcs will be called in series, and the \"info\" objects will be accumulated into a single result tuple. The combined results from previous funcs will be given to the subsequent funcs as data parameters. This allows for the callbacks in the chain to communicate.\n\nThe chain will return the final combined result tuple, or nothing if all funcs return nothing.\n\nnote: Note\nWhen calling optimize, any callback that is a tuple will be automatically processed with chain_callbacks. Thus, chain_callbacks rarely has to be invoked manually.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.check_amplitude-Tuple{Any}","page":"-","title":"QuantumControlBase.check_amplitude","text":"Check an amplitude in a Generator in the context of optimal control.\n\n@test check_amplitude(\n ampl; tlist, for_gradient_optimization=true, quiet=false\n)\n\nverifies that the given ampl is a valid element in the list of amplitudes of a Generator object. This checks all the conditions of QuantumPropagators.Interfaces.check_amplitude. In addition, the following conditions must be met.\n\nIf for_gradient_optimization:\n\nThe function get_control_deriv(ampl, control) must be defined\nIf ampl does not depend on control, get_control_deriv(ampl, control) must return 0.0\nIf ampl depends on control, get_control_deriv(ampl, control) must return an object u so that evaluate(u, tlist, n) returns a Number. In most cases, u itself will be a Number.\n\nThe function returns true for a valid amplitude and false for an invalid amplitude. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.check_generator-Tuple{Any}","page":"-","title":"QuantumControlBase.check_generator","text":"Check the dynamical generator in the context of optimal control.\n\n@test check_generator(\n generator; state, tlist,\n for_mutable_operator=true, for_immutable_operator=true,\n for_mutable_state=true, for_immutable_state=true,\n for_expval=true, for_pwc=true, for_time_continuous=false,\n for_parameterization=false, for_gradient_optimization=true,\n atol=1e-15, quiet=false\n)\n\nverifies the given generator. This checks all the conditions of QuantumPropagators.Interfaces.check_generator. In addition, the following conditions must be met.\n\nIf for_gradient_optimization:\n\nget_control_derivs(generator, controls) must be defined and return a vector containing the result of get_control_deriv(generator, control) for every control in controls.\nget_control_deriv(generator, control) must return an object that passes the less restrictive QuantumPropagators.Interfaces.check_generator if control is in get_controls(generator).\nget_control_deriv(generator, control) must return nothing if control is not in get_controls(generator)\nIf generator is a Generator instance, every ampl in generator.amplitudes must pass check_amplitude(ampl; tlist).\n\nThe function returns true for a valid generator and false for an invalid generator. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.get_control_deriv-Tuple{Function, Any}","page":"-","title":"QuantumControlBase.get_control_deriv","text":"a = get_control_deriv(ampl, control)\n\nreturns the derivative a_l(t)ϵ_l(t) of the given amplitude a_l(ϵ_l(t) t) with respect to the given control ϵ_l(t). For \"trivial\" amplitudes, where a_l(t) ϵ_l(t), the result with be either 1.0 or 0.0 (depending on whether ampl ≡ control). For non-trivial amplitudes, the result may be another amplitude that depends on the controls and potentially on time, but can be evaluated to a constant with evaluate.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.get_control_deriv-Tuple{Tuple, Any}","page":"-","title":"QuantumControlBase.get_control_deriv","text":"Get the derivative of the generator G w.r.t. the control ϵ(t).\n\nμ = get_control_deriv(generator, control)\n\nreturns nothing if the generator (Hamiltonian or Liouvillian) does not depend on control, or a generator\n\nμ = fracGϵ(t)\n\notherwise. For linear control terms, μ will be a static operator, e.g. an AbstractMatrix or an Operator. For non-linear controls, μ will be time-dependent, e.g. a Generator. In either case, evaluate should be used to evaluate μ into a constant operator for particular values of the controls and a particular point in time.\n\nFor constant generators, e.g. an Operator, the result is always nothing.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.get_control_derivs-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.get_control_derivs","text":"Get a vector of the derivatives of generator w.r.t. each control.\n\nget_control_derivs(generator, controls)\n\nreturn as vector containing the derivative of generator with respect to each control in controls. The elements of the vector are either nothing if generator does not depend on that particular control, or a function μ(α) that evaluates the derivative for a particular value of the control, see get_control_deriv.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.init_prop_trajectory-Tuple{Trajectory, Any}","page":"-","title":"QuantumControlBase.init_prop_trajectory","text":"Initialize a propagator for a given Trajectory.\n\npropagator = init_prop_trajectory(\n traj,\n tlist;\n initial_state=traj.initial_state,\n kwargs...\n)\n\ninitializes a Propagator for the propagation of the initial_state under the dynamics described by traj.generator.\n\nAll keyword arguments are forwarded to QuantumPropagators.init_prop, with default values from any property of traj with a prop_ prefix. That is, the keyword arguments for the underlying QuantumPropagators.init_prop are determined as follows:\n\nFor any property of traj whose name starts with the prefix prop_, strip the prefix and use that property as a keyword argument for init_prop. For example, if traj.prop_method is defined, method=traj.prop_method will be passed to init_prop. Similarly, traj.prop_inplace would be passed as inplace=traj.prop_inplace, etc.\nAny explicitly keyword argument to init_prop_trajectory overrides the values from the properties of traj.\n\nNote that the propagation method in particular must be specified, as it is a mandatory keyword argument in QuantumPropagators.propagate). Thus, either traj must have a property prop_method of the trajectory, or method must be given as an explicit keyword argument.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.make_chi-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.make_chi","text":"Return a function that calculates χ_k = -J_TΨ_k.\n\nchi = make_chi(\n J_T,\n trajectories;\n mode=:any,\n automatic=:default,\n via=(any(isnothing(t.target_state) for t in trajectories) ? :states : :tau),\n)\n\ncreates a function chi(Ψ, trajectories; τ) that returns a vector of states χ with χ_k = -J_TΨ_k, where Ψ_k is the k'th element of Ψ. These are the states used as the boundary condition for the backward propagation propagation in Krotov's method and GRAPE. Each χₖ is defined as a matrix calculus Wirtinger derivative,\n\nχ_k(T) = -fracJ_TΨ_k = -frac12 _Ψ_k J_Tqquad\n_Ψ_k J_T fracJ_TReΨ_k + i fracJ_TImΨ_k\n\nThe function J_T must take a vector of states Ψ and a vector of trajectories as positional parameters. If via=:tau, it must also a vector tau as a keyword argument, see e.g. J_T_sm). that contains the overlap of the states Ψ with the target states from the trajectories\n\nThe derivative can be calculated analytically of automatically (via automatic differentiation) depending on the value of mode. For mode=:any, an analytic derivative is returned if available, with a fallback to an automatic derivative.\n\nIf mode=:analytic, return an analytically known -J_TΨ_k, e.g.,\n\nQuantumControl.Functionals.J_T_sm → QuantumControl.Functionals.chi_sm,\nQuantumControl.Functionals.J_T_re → QuantumControl.Functionals.chi_re,\nQuantumControl.Functionals.J_T_ss → QuantumControl.Functionals.chi_ss.\n\nand throw an error if no analytic derivative is known.\n\nIf mode=:automatic, return an automatic derivative (even if an analytic derivative is known). The calculation of an automatic derivative (whether via mode=:any or mode=:automatic) requires that a suitable framework (e.g., Zygote or FiniteDifferences) has been loaded. The loaded module must be passed as automatic keyword argument. Alternatively, it can be registered as a default value for automatic by calling QuantumControl.set_default_ad_framework.\n\nWhen evaluating χ_k automatically, if via=:states is given , χ_k(T) is calculated directly as defined above from the gradient with respect to the states Ψ_k(T).\n\nIf via=:tau is given instead, the functional J_T is considered a function of overlaps τ_k = Ψ_k^tgtΨ_k(T). This requires that all trajectories define a target_state and that J_T calculates the value of the functional solely based on the values of tau passed as a keyword argument. With only the complex conjugate τ_k = Ψ_k(T)Ψ_k^tgt having an explicit dependency on Ψ_k(T), the chain rule in this case is\n\nχ_k(T)\n= -fracJ_TΨ_k\n= -left(\n fracJ_Tτ_k\n fracτ_kΨ_k\n right)\n= - frac12 (_τ_k J_T) Ψ_k^tgt\n\nAgain, we have used the definition of the Wirtinger derivatives,\n\nbeginalign*\n fracJ_Tτ_k\n frac12left(\n frac J_T Reτ_k\n - i frac J_T Imτ_k\n right)\n fracJ_Tτ_k\n frac12left(\n frac J_T Reτ_k\n + i frac J_T Imτ_k\n right)\nendalign*\n\nand the definition of the Zygote gradient with respect to a complex scalar,\n\n_τ_k J_T = left(\n frac J_T Reτ_k\n + i frac J_T Imτ_k\nright)\n\ntip: Tip\nIn order to extend make_chi with an analytic implementation for a new J_T function, define a new method make_analytic_chi like so:QuantumControlBase.make_analytic_chi(::typeof(J_T_sm), trajectories) = chi_smwhich links make_chi for QuantumControl.Functionals.J_T_sm to QuantumControl.Functionals.chi_sm.\n\nwarning: Warning\nZygote is notorious for being buggy (silently returning incorrect gradients). Always test automatic derivatives against finite differences and/or other automatic differentiation frameworks.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.make_grad_J_a-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.make_grad_J_a","text":"Return a function to evaluate J_aϵ_ln for a pulse value running cost.\n\ngrad_J_a! = make_grad_J_a(\n J_a,\n tlist;\n mode=:any,\n automatic=:default,\n)\n\nreturns a function so that grad_J_a!(∇J_a, pulsevals, tlist) sets J_aϵ_ln as the elements of the (vectorized) ∇J_a. The function J_a must have the interface J_a(pulsevals, tlist), see, e.g., J_a_fluence.\n\nThe parameters mode and automatic are handled as in make_chi, where mode is one of :any, :analytic, :automatic, and automatic is he loaded module of an automatic differentiation framework, where :default refers to the framework set with QuantumControl.set_default_ad_framework.\n\ntip: Tip\nIn order to extend make_grad_J_a with an analytic implementation for a new J_a function, define a new method make_analytic_grad_J_a like so:make_analytic_grad_J_a(::typeof(J_a_fluence), tlist) = grad_J_a_fluence!which links make_grad_J_a for J_a_fluence to grad_J_a_fluence!.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.make_print_iters-Tuple{Module}","page":"-","title":"QuantumControlBase.make_print_iters","text":"Construct a method-specific automatic callback for printing iter information.\n\nprint_iters = make_print_iters(Method; kwargs...)\n\nconstructs the automatic callback to be used by optimize(problem; method=Method, print_iters=true) to print information after each iteration. The keyword arguments are those used to instantiate problem and those explicitly passed to optimize.\n\nOptimization methods should implement make_print_iters(::Val{:Method}; kwargs...) where :Method is the name of the module/package implementing the method.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.optimize-Tuple{ControlProblem}","page":"-","title":"QuantumControlBase.optimize","text":"Optimize a quantum control problem.\n\nresult = optimize(\n problem;\n method, # mandatory keyword argument\n check=true,\n callback=nothing,\n print_iters=true,\n kwargs...\n)\n\noptimizes towards a solution of given problem with the given method, which should be a Module implementing the method, e.g.,\n\nusing Krotov\nresult = optimize(problem; method=Krotov)\n\nIf check is true (default), the initial_state and generator of each trajectory is checked with check_state and check_generator. Any other keyword argument temporarily overrides the corresponding keyword argument in problem. These arguments are available to the optimizer, see each optimization package's documentation for details.\n\nThe callback can be given as a function to be called after each iteration in order to analyze the progress of the optimization or to modify the state of the optimizer or the current controls. The signature of callback is method-specific, but callbacks should receive a workspace objects as the first parameter as the first argument, the iteration number as the second parameter, and then additional method-specific parameters.\n\nThe callback function may return a tuple of values, and an optimization method should store these values fore each iteration in a records field in their Result object. The callback should be called once with an iteration number of 0 before the first iteration. The callback can also be given as a tuple of vector of functions, which are automatically combined via chain_callbacks.\n\nIf print_iters is true (default), an automatic callback is created via the method-specific make_print_iters to print the progress of the optimization after each iteration. This automatic callback runs after any manually given callback.\n\nAll remaining keyword argument are method-specific. To obtain the documentation for which options a particular method uses, run, e.g.,\n\n? optimize(problem, ::Val{:Krotov})\n\nwhere :Krotov is the name of the module implementing the method. The above is also the method signature that a Module wishing to implement a control method must define.\n\nThe returned result object is specific to the optimization method.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.propagate_trajectories-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.propagate_trajectories","text":"Propagate multiple trajectories in parallel.\n\nresult = propagate_trajectories(\n trajectories, tlist; use_threads=true, kwargs...\n)\n\nruns propagate_trajectory for every trajectory in trajectories, collects and returns a vector of results. The propagation happens in parallel if use_threads=true (default). All keyword parameters are passed to propagate_trajectory, except that if initial_state is given, it must be a vector of initial states, one for each trajectory. Likewise, to pass pre-allocated storage arrays to storage, a vector of storage arrays must be passed. A simple storage=true will still work to return a vector of storage results.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.propagate_trajectory-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.propagate_trajectory","text":"Propagate a Trajectory.\n\npropagate_trajectory(\n traj,\n tlist;\n initial_state=traj.initial_state,\n kwargs...\n)\n\npropagates initial_state under the dynamics described by traj.generator. It takes the same keyword arguments as QuantumPropagators.propagate, with default values from any property of traj with a prop_ prefix (prop_method, prop_inplace, prop_callback, …). See init_prop_trajectory for details.\n\nNote that method (a mandatory keyword argument in QuantumPropagators.propagate) must be specified, either as a property prop_method of the trajectory, or by passing a method keyword argument explicitly.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.set_atexit_save_optimization-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.set_atexit_save_optimization","text":"Register a callback to dump a running optimization to disk on unexpected exit.\n\nA long-running optimization routine may use\n\nif !isnothing(atexit_filename)\n set_atexit_save_optimization(\n atexit_filename, result; msg_property=:message, msg=\"Abort: ATEXIT\"\n )\n # ...\n popfirst!(Base.atexit_hooks) # remove callback\nend\n\nto register a callback that writes the given result object to the given filename in JLD2 format in the event that the program terminates unexpectedly. The idea is to avoid data loss if the user presses CTRL-C in a non-interactive program (SIGINT), or if the process receives a SIGTERM from an HPC scheduler because the process has reached its allocated runtime limit. Note that the callback cannot protect against data loss in all possible scenarios, e.g., a SIGKILL will terminate the program without giving the callback a chance to run (as will yanking the power cord).\n\nAs in the above example, the optimization routine should make set_atexit_save_optimization conditional on an atexit_filename keyword argument, which is what QuantumControl.@optimize_or_load will pass to the optimization routine. The optimization routine must remove the callback from Base.atexit_hooks when it exits normally. Note that in an interactive context, CTRL-C will throw an InterruptException, but not cause a shutdown. Optimization routines that want to prevent data loss in this situation should handle the InterruptException and return result, in addition to using set_atexit_save_optimization.\n\nIf msg_property is not nothing, the given msg string will be stored in the corresponding property of the (mutable) result object before it is written out.\n\nThe resulting JLD2 file is compatible with QuantumControl.load_optimization.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.taus!-Tuple{Vector{ComplexF64}, Any, Any}","page":"-","title":"QuantumControlBase.taus!","text":"Overlaps of target states with propagates states, calculated in-place.\n\ntaus!(τ, Ψ, trajectories; ignore_missing_target_state=false)\n\noverwrites the complex vector τ with the results of taus(Ψ, trajectories).\n\nThrows an ArgumentError if any of trajectories have a target_state of nothing. If ignore_missing_target_state=true, values in τ instead will remain unchanged for any trajectories with a missing target state.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.taus-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.taus","text":"Overlaps of target states with propagates states\n\nτ = taus(Ψ, trajectories)\n\ncalculates a vector of values τ_k = Ψ_k^tgtΨ_k where Ψ_k^tgt is the traj.target_state of the k'th element of trajectories and Ψₖ is the k'th element of Ψ.\n\nThe definition of the τ values with Ψ_k^tgt on the left (overlap of target states with propagated states, as opposed to overlap of propagated states with target states) matches Refs. [4] and [7].\n\nThe function requires that each trajectory defines a target state. See also taus! for an in-place version that includes well-defined error handling for any trajectories whose target_state property is nothing.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_controls-Tuple{ControlProblem}","page":"-","title":"QuantumPropagators.Controls.get_controls","text":"controls = get_controls(problem)\n\nextracts the controls from problem.trajectories.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_controls-Tuple{Vector{<:Trajectory}}","page":"-","title":"QuantumPropagators.Controls.get_controls","text":"controls = get_controls(trajectories)\n\nextracts the controls from a list of trajectories (i.e., from each trajectory's generator). Controls that occur multiple times in the different trajectories will occur only once in the result.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_parameters-Tuple{ControlProblem}","page":"-","title":"QuantumPropagators.Controls.get_parameters","text":"parameters = get_parameters(problem)\n\nextracts the parameters from problem.trajectories.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_parameters-Tuple{Vector{<:Trajectory}}","page":"-","title":"QuantumPropagators.Controls.get_parameters","text":"parameters = get_parameters(trajectories)\n\ncollects and combines get parameter arrays from all the generators in trajectories. Note that this allows any custom generator type to define a custom get_parameters method to override the default of obtaining the parameters recursively from the controls inside the generator.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.substitute-Tuple{ControlProblem, Any}","page":"-","title":"QuantumPropagators.Controls.substitute","text":"problem = substitute(problem::ControlProblem, replacements)\n\nsubstitutes in problem.trajectories\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.substitute-Tuple{Trajectory, Any}","page":"-","title":"QuantumPropagators.Controls.substitute","text":"trajectory = substitute(trajectory::Trajectory, replacements)\ntrajectories = substitute(trajectories::Vector{<:Trajectory}, replacements)\n\nrecursively substitutes the initial_state, generator, and target_state.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.@threadsif-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.@threadsif","text":"Conditionally apply multi-threading to for loops.\n\nThis is a variation on Base.Threads.@threads that adds a run-time boolean flag to enable or disable threading. It is intended for internal use in packages building on QuantumControlBase.\n\nUsage:\n\nusing QuantumControlBase: @threadsif\n\nfunction optimize(trajectories; use_threads=true)\n @threadsif use_threads for k = 1:length(trajectories)\n # ...\n end\nend\n\n\n\n\n\n","category":"macro"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators]","category":"page"},{"location":"externals/#QuantumPropagators.AbstractPropagator","page":"-","title":"QuantumPropagators.AbstractPropagator","text":"Abstract base type for all Propagator objects.\n\nAll Propagator objects must be instantiated via init_prop and implement the following interface.\n\nProperties\n\nstate (read-only): The current quantum state in the propagation\ntlist (read-only): The time grid for the propagation\nt (read-only): The time at which state is defined. An element of tlist.\nparameters: parameters that determine the dynamics. The structure of the parameters depends on the concrete Propagator type (i.e., the propagation method). Mutating the parameters affects subsequent propagation steps.\nbackward: Boolean flag to indicate whether the propagation moves forward or backward in time\ninplace: Boolean flag to indicate whether propagator.state is modified in-place or is recreated by every call of prop_step! or set_state!. With inplace=false, the propagator should generally avoid in-place operations, such as calls to QuantumPropagators.Controls.evaluate!.\n\nConcrete Propagator types may have additional properties or fields, but these should be considered private.\n\nMethods\n\nreinit_prop! — reset the propagator to a new initial state at the beginning of the time grid (or the end, for backward propagation)\nprop_step! – advance the propagator by one step forward (or backward) on the time grid.\nset_state! — safely mutate the current quantum state of the propagation. Note that directly mutating the state property is not safe. However, Ψ = propagator.state; foo_mutate!(Ψ), set_state!(propagator, Ψ) for some mutating function foo_mutate! is guaranteed to be safe and efficient for both in-place and not-in-place propagators.\nset_t! — safely mutate the current time (propagator.t), snapping to the values of tlist.\n\nSee also\n\nPiecewisePropagator — specialization of AbstractPropagator for piecewise propagation methods.\nPWCPropagator — specialization of PiecewisePropagator for piecewise-constant propagation methods.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.ChebyPropagator","page":"-","title":"QuantumPropagators.ChebyPropagator","text":"Propagator for Chebychev propagation (method=QuantumPropagators.Cheby).\n\nThis is a PWCPropagator.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.ExpPropagator","page":"-","title":"QuantumPropagators.ExpPropagator","text":"Propagator for propagation via direct exponentiation (method=QuantumPropagators.ExpProp)\n\nThis is a PWCPropagator.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.NewtonPropagator","page":"-","title":"QuantumPropagators.NewtonPropagator","text":"Propagator for Newton propagation (method=QuantumPropagators.Newton).\n\nThis is a PWCPropagator.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.PWCPropagator","page":"-","title":"QuantumPropagators.PWCPropagator","text":"PiecewisePropagator sub-type for piecewise-constant propagators.\n\nLike the more general PiecewisePropagator, this is characterized by propagator.parameters mapping the controls in the generator to a vector of amplitude value on the midpoints of the time grid intervals.\n\nThe propagation will use these values as constant within each interval.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.PiecewisePropagator","page":"-","title":"QuantumPropagators.PiecewisePropagator","text":"AbstractPropagator sub-type for piecewise propagators.\n\nA piecewise propagator is determined by a single parameter per control and time grid interval. Consequently, the propagator.parameters are a dictionary mapping the controls found in the generator via get_controls to a vector of values defined on the intervals of the time grid, see discretize_on_midpoints. This does not necessarily imply that these values are the piecewise-constant amplitudes for the intervals. A general piecewise propagator might use interpolation to obtain actual amplitudes within any given time interval.\n\nWhen the amplitudes are piecewise-constant, the propagator should be a concrete instantiation of a PWCPropagator.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Propagation","page":"-","title":"QuantumPropagators.Propagation","text":"Wrapper around the parameters of a call to propagate.\n\nPropagation(\n generator, tlist;\n pre_propagation=nothing, post_propagation=nothing,\n kwargs...\n)\n\nPropagation(\n propagator;\n pre_propagation=nothing, post_propagation=nothing,\n kwargs...\n)\n\nis a wrapper around the arguments for propagate / init_prop, for use within propagate_sequence.\n\nThe positional and keyword arguments are those accepted by the above propagation routines, excluding the initial state. A Propagation may in addition include the pre_propagation and post_propagation keyword arguments recognized by propagate_sequence.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.cheby_get_spectral_envelope-NTuple{4, Any}","page":"-","title":"QuantumPropagators.cheby_get_spectral_envelope","text":"Determine the spectral envelope of a generator.\n\nE_min, E_max = cheby_get_spectral_envelope(\n generator, tlist, control_ranges, method; kwargs...\n)\n\nestimates a lower bound E_min the lowest eigenvalue of the generator for any values of the controls specified by control_ranges, and an upper bound E_max for the highest eigenvalue.\n\nThis is done by constructing operators from the extremal values for the controls as specified in control_ranges and taking the smallest/largest return values from specrange for those operators.\n\nArguments\n\ngenerator: dynamical generator, e.g. a time-dependent\ntlist: The time grid for the propagation\ncontrol_ranges: a dict that maps controls that occur in generator (cf. get_controls to a tuple of minimum and maximum amplitude for that control\nmethod: method name to pass to specrange\nkwargs: Any remaining keyword arguments are passed to specrange\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.disable_timings-Tuple{}","page":"-","title":"QuantumPropagators.disable_timings","text":"Disable the collection of TimerOutputs data.\n\nQuantumPropagators.disable_timings()\n\ndisables the collection of timing data previously enabled with enable_timings. This triggers recompilation to completely remove profiling from the code. That is, there is zero cost when the collection of timing data is disabled.\n\nReturns QuantumPropagators.timings_enabled(), i.e., false if successful.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.enable_timings-Tuple{}","page":"-","title":"QuantumPropagators.enable_timings","text":"Enable the collection of TimerOutputs data.\n\nQuantumPropagators.enable_timings()\n\nenables certain portions of the package to collect TimerOutputs internally. This aids in profiling and benchmarking propagation methods.\n\nSpecifically, after enable_timings(), for any ChebyPropagator or NewtonPropagator, timing data will become available in propagator.wrk.timing_data (as a TimerOutput instance). This data is reset when the propagator is re-instantiated with init_prop or re-initialized with reinit_prop!. This makes the data local to any call of propagate.\n\nNote that enable_timings() triggers recompilation, so propagate should be called at least twice to avoid compilation overhead in the timing data. There is still a small overhead for collecting the timing data.\n\nThe collection of timing data can be disabled again with disable_timings.\n\nReturns QuantumPropagators.timings_enabled(), i.e., true if successful.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.init_prop-Tuple{Any, Any, Any, Val{:Cheby}}","page":"-","title":"QuantumPropagators.init_prop","text":"using QuantumPropagators: Cheby\n\ncheby_propagator = init_prop(\n state,\n generator,\n tlist;\n method=Cheby,\n inplace=true,\n backward=false,\n verbose=false,\n parameters=nothing,\n control_ranges=nothing,\n specrange_method=:auto,\n specrange_buffer=0.01,\n cheby_coeffs_limit=1e-12,\n check_normalization=false,\n specrange_kwargs...\n)\n\ninitializes a ChebyPropagator.\n\nMethod-specific keyword arguments\n\ncontrol_ranges: a dict the maps the controls in generator (see get_controls) to a tuple of min/max values. The Chebychev coefficients will be calculated based on a spectral envelope that assumes that each control can take arbitrary values within the min/max range. If not given, the ranges are determined automatically. Specifying manual control ranges can be useful when the the control amplitudes (parameters) may change during the propagation, e.g. in a sequential-update control scheme.\nspecrange_method: Method to pass to the specrange function\nspecrange_buffer: An additional factor by which to enlarge the estimated spectral range returned by specrange, in order to ensure that Chebychev coefficients are based on an overestimation of the spectral range.\ncheby_coeffs_limit: The maximum magnitude of Chebychev coefficients that should be treated as non-zero\ncheck_normalization: Check whether the Hamiltonian has been properly normalized, i.e., that the spectral range of generator has not been underestimated. This slowes down the propagation, but is advisable for novel generators.\nuniform_dt_tolerance=1e-12: How much the intervals of tlist are allowed to vary while still being considered constant.\nspecrange_kwargs: All further keyword arguments are passed to the specrange function. Most notably, with the default specrange_method=:auto (or specrange_method=:manual), passing E_min and E_max allows to manually specify the spectral range of generator.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.init_prop-Tuple{Any, Any, Any, Val{:ExpProp}}","page":"-","title":"QuantumPropagators.init_prop","text":"using QuantumPropagators: ExpProp\n\nexp_propagator = init_prop(\n state,\n generator,\n tlist;\n method=ExpProp,\n inplace=true,\n backward=false,\n verbose=false,\n parameters=nothing,\n func=(H_dt -> exp(-1im * H_dt))\n convert_state=_exp_prop_convert_state(state),\n convert_operator=_exp_prop_convert_operator(generator),\n _...\n)\n\ninitializes an ExpPropagator.\n\nMethod-specific keyword arguments\n\nfunc: The function to evaluate. The argument H_dt is obtained by constructing an operator H from generator via the evaluate function and the multiplied with the time step dt for the current time interval. The propagation then simply multiplies the return value of func with the current state\nconvert_state: Type to which to temporarily convert states before multiplying the return value of func.\nconvert_operator: Type to which to convert the operator H before multiplying it with dt and plugging the result into func\n\nThe convert_state and convert_operator parameters are useful for when the generator and or state are unusual data structures for which the relevant methods to calculate func are not defined. Often, it is easier to temporarily convert them to standard complex matrices and vectors than to implement the missing methods.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.init_prop-Tuple{Any, Any, Any, Val{:Newton}}","page":"-","title":"QuantumPropagators.init_prop","text":"using QuantumPropagators: Newton\n\nnewton_propagator = init_prop(\n state,\n generator,\n tlist;\n method=Newton,\n inplace=true,\n backward=false,\n verbose=false,\n parameters=nothing,\n m_max=10,\n func=(z -> exp(-1im * z)),\n norm_min=1e-14,\n relerr=1e-12,\n max_restarts=50,\n _...\n)\n\ninitializes a NewtonPropagator.\n\nMethod-specific keyword arguments\n\nm_max: maximum Krylov dimension, cf. NewtonWrk\nfunc, norm_min, relerr, max_restarts: parameter to pass to newton!\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.init_prop-Tuple{Any, Any, Any}","page":"-","title":"QuantumPropagators.init_prop","text":"Initialize a Propagator.\n\npropagator = init_prop(\n state, generator, tlist;\n method, # mandatory keyword argument\n backward=false,\n inplace=true,\n piecewise=nothing,\n pwc=nothing,\n kwargs...\n)\n\ninitializes a propagator for the time propagation of the given state over a time grid tlist under the time-dependent generator (Hamiltonian/Liouvillian) generator.\n\nArguments\n\nstate: The \"initial\" state for the propagation. For backward=false, this state is taken to be at initial time (tlist[begin]); and for backward=true, at the final time (tlist[end])\ngenerator: The time-dependent generator of the dynamics\ntlist: The time grid over which which the propagation is defined. This may or may not be equidistant.\n\nMandatory keyword arguments\n\nmethod: The propagation method to use. May be given as a name (Symbol), but the recommended usage is to pass a module implementing the propagation method, e.g., using QuantumPropagators: Cheby; method = Cheby. Passing a module ensures that the code implementing the method is correctly loaded. This is particularly important for propagators using third-party backends, like with method=OrdinaryDiffEq.\n\nOptional keyword arguments\n\nbackward: If true, initialize the propagator for a backward propagation. The resulting propagator.t will be tlist[end], and subsequent calls to prop_step! will move backward on tlist.\ninplace: If true, the state property of the resulting propagator will be changed in-place by any call to prop_step!. If false, each call to prop_step! changes the reference for propagator.state, and the propagation will not use any in-place operations. Not all propagation methods may support both in-place and not-in-place propagation. In-place propagation is generally more efficient but may not be compatible, e.g., with automatic differentiation.\npiecewise: If given as a boolean, true enforces that the resulting propagator is a PiecewisePropagator, and false enforces that it not a PiecewisePropagator. For the default piecewise=nothing, whatever type of propagation is the default for the given method will be used. Throw an error if the given method does not support the required type of propagation.\npwc: Like piecewise, but for the stronger PWCPropagator.\n\nAll other kwargs are method-dependent and are ignored for methods that do not support them.\n\nThe type of the returned propagator is a sub-type of AbstractPropagator, respectively a sub-type of PiecewisePropagator if piecewise=true or a sub-type of PWCPropagator if pwc=true.\n\nInternals\n\nInternally, the (mandatory) keyword method is converted into a fourth positional argument. This allows propagation methods to define their own implementation of init_prop via multiple dispatch. However, when calling init_prop in high-level code, method must always be given as a keyword argument.\n\nSee also\n\nreinit_prop! — Re-initialize a propagator\npropagate — Higher-level propagation interface\n`check_propagator — a function to verify the interface described above.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.ode_function-Union{Tuple{GT}, Tuple{GT, Any}} where GT","page":"-","title":"QuantumPropagators.ode_function","text":"Wrap around a Generator, for use as an ODE function.\n\nf = ode_function(generator, tlist; c=-1im)\n\ncreates a function suitable to be passed to ODEProblem.\n\ngdefop1hat1\ngdefket1vert1rangle\n\nWith generator corresponding to opH(t), this implicitly encodes the ODE\n\nfracpartialpartial t ketPsi(t) = c opH(t) ketPsi(t)\n\nfor the state ketPsi(t). With the default c = -i, this corresponds to the Schrödinger equation, or the Liouville equation with convention=:LvN.\n\nThe resulting f works both in-place and not-in-place, as\n\nf(ϕ, Ψ, vals_dict, t) # in-place `f(du, u, p, t)`\nϕ = f(Ψ, vals_dict, t) # not-in-place `f(u, p, t)`\n\nCalling f as above is functionally equivalent to calling evaluate to obtain an operator H from the original time-dependent generator, and then applying H to the current quantum state Ψ:\n\nH = evaluate(f.generator, t; vals_dict=vals_dict)\nϕ = c * H * Ψ\n\nwhere vals_dict may be a dictionary mapping controls to values (set as the parameters p of the underlying ODE solver).\n\nIf QuantumPropagators.enable_timings() has been called, profiling data is collected in f.timing_data.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.prop_step!","page":"-","title":"QuantumPropagators.prop_step!","text":"Advance the propagator by a single time step.\n\nstate = prop_step!(propagator)\n\nreturns the state obtained from propagating to the next point on the time grid from propagator.t, respectively the previous point if propagator.backward is true.\n\nWhen the propagation would lead out of the time grid, prop_step! leaves propagator unchanged and returns nothing. Thus, a return value of nothing may be used to signal that a propagation has completed.\n\n\n\n\n\n","category":"function"},{"location":"externals/#QuantumPropagators.propagate-Tuple{Any, Any, Any}","page":"-","title":"QuantumPropagators.propagate","text":"Propagate a state over an entire time grid.\n\nstate = propagate(\n state,\n generator,\n tlist;\n method, # mandatory keyword argument\n check=true,\n backward=false,\n inplace=true,\n verbose=false,\n piecewise=nothing,\n pwc=nothing,\n storage=nothing,\n observables=,\n callback=nothing,\n show_progress=false,\n init_prop_kwargs...)\n\npropagates state of the entire time grid and returns the propagated states, or a storage array of data collected during the propagation. This high-level routine performs the following three steps:\n\nIf check=true (default), check that state, generator, and tlist are consistent with the required interface.\nInitialize a propagator via init_prop:\ninit_prop(state, generator, tlist; method, inplace, init_prop_kwargs...)\nCall and return the result of\npropagate(propagator; storage, observables, show_progress, callback)\n\nArguments\n\nstate: The \"initial\" state for the propagation. For backward=false, this state is taken to be at initial time (tlist[begin]); and for backward=true, at the final time (tlist[end])\ngenerator: The time-dependent generator of the dynamics\ntlist: The time grid over which which the propagation is defined. This may or may not be equidistant.\n\nMandatory keyword arguments\n\nmethod: The propagation method to use. May be given as a name (Symbol), but the recommended usage is to pass a module implementing the propagation method, cf. init_prop.\n\nOptional keyword arguments\n\ncheck: if true, check that state, generator, and tlist pass check_state, check_generator and check_tlist, respectively.\nbackward: If true, propagate backward in time\ninplace: If true, propagate using in-place operations. If false, avoid in-place operations. Not all propagation methods support both in-place and not-in-place propagation.\npiecewise: If given as a boolean, ensure that the internal propagator is an instance of PiecewisePropagator, cf. init_prop.\npwc: If given a a boolean, do a piecewise constant propagation where the generator in each interval is constant (the internal propagator is a PWCPropagator, cf. init_prop)\nstorage: Flag whether to store and return the propagated states / observables, or pre-allocated storage array. See Notes below.\nobservables: Converters for data to be stored in storage. See Notes below.\ncallback: Function to call after each propagation step. See Notes below.\nshow_progress: Whether to show a progress bar. See Notes below.\n\nAll remaining keyword arguments are passed to init_prop to initialize the Propagator that is used internally to drive the optimization. Unknown keyword arguments will be ignored.\n\nNotes\n\nIn general, there is no requirement that tlist has a constant time step, although some propagation methods (most notably Cheby) only support a uniform time grid.\n\nIf storage is given as a container pre-allocated via init_storage, it will be filled with data determined by the observables. Specifically, after each propagation step,\n\ndata = map_observables(observables, tlist, i, state)\nwrite_to_storage!(storage, i, data)\n\nis executed, where state is defined at time tlist[i]. See map_observables and write_to_storage! for details. The default values for observables results simply in the propagated states at every point in time being stored.\n\nThe storage parameter may also be given as true, and a new storage array will be created internally with init_storage and returned instead of the propagated state:\n\ndata = propagate(\n state, generator, tlist; method,\n backward=false; storage=true, observables=observables,\n callback=nothing, show_progress=false, init_prop_kwargs...)\n\nIf backward is true, the input state is assumed to be at time tlist[end], and the propagation progresses backward in time (with a negative time step dt). If storage is given, it will be filled back-to-front during the backward propagation.\n\nIf callback is given as a callable, it will be called after each propagation step, as callback(propagator, observables) where propagator is Propagator object driving the propagation. The callback is called before calculating any observables. Example usage includes writing data to file, or modifying state via set_state!, e.g., removing amplitude from the lowest and highest level to mitigate \"truncation error\".\n\nIf show_progress is given as true, a progress bar will be shown for long-running propagation. In order to customize the progress bar, show_progress may also be a function that receives length(tlist) and returns a ProgressMeter.Progress instance.\n\nIf in_place=false is given, the propagation avoids in-place operations. This is slower than inplace=true, but is often required in the context of automatic differentiation (AD), e.g., with Zygote. That is, use in_place=false if propagate is called inside a function to be passed to Zygote.gradient, Zygote.pullback, or a similar function. In an AD context, storage and show_progress should not be used.\n\nThe propagate routine returns the propagated state at tlist[end], respectively tlist[1] if backward=true, or a storage array with the stored states / observable data if storage=true.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.propagate-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.propagate","text":"state = propagate(\n state,\n propagator;\n storage=nothing,\n observables=,\n show_progress=false,\n callback=nothing,\n reinit_prop_kwargs...\n)\n\nre-initializes the given propagator with state (see reinit_prop!) and then calls the lower-level propagate(propagator; ...).\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.propagate-Tuple{Any}","page":"-","title":"QuantumPropagators.propagate","text":"state = propagate(\n propagator;\n storage=nothing,\n observables=,\n show_progress=false,\n callback=nothing,\n)\n\npropagates a freshly initialized propagator (immediately after init_prop). Used in the higher-level propagate(state, generator, tlist; kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.propagate_sequence-Tuple{Any, Vector{Propagation}}","page":"-","title":"QuantumPropagators.propagate_sequence","text":"Propagate a state through a sequence of generators.\n\nstates = propagate_sequence(\n state,\n propagations;\n storage=nothing,\n pre_propagation=nothing,\n post_propagation=nothing,\n kwargs...\n)\n\ntakes an initial state and performs a sequence of propagate calls using the parameters in propagations. The initial state for each step in the sequence is the state resulting from the previous step. Optionally, before and after each step, a pre_propagation and post_propagation function may modify the state instantaneously, e.g., to perform a frame transformation. Return the vector of states at the end of each step (after any post_propagation, before any next pre_propagation of the next step).\n\nArguments\n\nstate: The initial state\npropagations: A vector of Propagation instances, one per step in the sequence, each containing the arguments for the call to propagate for that step. The Propagation contains the generator and time grid for each step as positional parameters, or alternatively a pre-initialized Propagator, and any keyword arguments for propagate that are specific to that step. Note that propagate keyword arguments that are common to all steps can be given directly to propagate_sequence.\nstorage: If storage=true, return a vector of storage objects as returned by propagate(…, storage=true) for each propagation step, instead of the state after each step. To use a pre-initialized storage, each Propagation in propagations should have a storage keyword argument instead.\npre_propagation: If not nothing, must be a function that receives the same arguments as propagate and returns a state. Called immediately before the propagate of each step, and the state returned by pre_propagation will become the initial state for the subsequent call to propagate. Generally, pre_propagation would be different in each step of the sequence, and should be given as a keyword argument in a particular Propagation.\npost_propagation: If not nothing, a function that receives the same arguments as propagate and returns a state, see pre_propagation. The returned state becomes the initial state for the next step in the sequence (and may be further processed by the following pre_propagation). Like pre_propagation, this will generally be set as a keyword argument for a particular Propagation, not as a global keyword argument to propagate_sequence.\n\nAll other keyword arguments are forwarded to propagate. Thus, keyword arguments that are common to all steps in the sequence should be given as keyword arguments to propagate_sequence directly.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.reinit_prop!-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.reinit_prop!","text":"Re-initialize a propagator.\n\nreinit_prop!(propagator, state; kwargs...)\n\nresets the propagator to state at the beginning of the time grid, respectively the end of the time grid if propagator.backward is true.\n\nAt a minimum, this is equivalent to a call to set_state! follow by a call to set_t!, but some propagators may have additional requirements on re-initialization, such as refreshing expansion coefficients for ChebyPropagator. In this case, the kwargs may be additional keyword arguments specific to the concrete type of propagator.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.reinit_prop!-Tuple{QuantumPropagators.ChebyPropagator, Any}","page":"-","title":"QuantumPropagators.reinit_prop!","text":"reinit_prop!(\n propagator::ChebyPropagator,\n state;\n transform_control_ranges=((c, ϵ_min, ϵ_max, check) => (ϵ_min, ϵ_max)),\n kwargs...\n)\n\nre-initializes an existing ChebyPropagator. This may or may not involve recalculating the Chebychev coefficients based on the current control amplitudes in propagator.parameters.\n\nMethod-specific keyword arguments\n\ntransform_control_ranges: a function (c, ϵ_min, ϵ_max, check) => (ϵ_min′, ϵ_max′). For each control c, the function is called with check=true and ϵ_min (ϵ_max) the current minimum (maximum) values for the control from propagator.parameters). The Chebychev coefficients will be recalculated if the existing coefficients were obtained assuming a range for c outside the returned ϵ_min′, ϵ_max′.\nIf the coefficients do need to be recalculated, transform_control_ranges is called a second time with check=false, and the returned (ϵ_min′, ϵ_max′) are used for estimating the new spectral range.\nFor example,\nfunction transform_control_ranges(c, ϵ_min, ϵ_max, check)\n if check\n return (min(ϵ_min, 2 * ϵ_min), max(ϵ_max, 2 * ϵ_max))\n else\n return (min(ϵ_min, 5 * ϵ_min), max(ϵ_max, 5 * ϵ_max))\n end\nend\nwill re-calculate the Chebychev coefficients only if the current amplitudes differ by more than a factor of two from the ranges that were used when initializing the propagator (control_ranges parameter in init_prop, which would have had to overestimate the actual amplitudes by at least a factor of two). When re-calculating, the control_ranges will overestimate the amplitudes by a factor of five. With this transform_control_ranges, the propagation will be stable as long as the amplitudes do not change dynamically by more than a factor of 2.5 from their original range, while also not re-calculating coefficients unnecessarily in each pass because of modest changes in the amplitudes.\nThe transform_control_ranges argument is only relevant in the context of optimal control, where the same propagator will be used for many iterations with changing control field amplitudes.\n\nAll other keyword arguments are ignored.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.set_state!-Tuple{QuantumPropagators.AbstractPropagator, Any}","page":"-","title":"QuantumPropagators.set_state!","text":"Set the current state of the propagator.\n\nset_state!(propagator, state)\n\nsets the propagator.state property and returns propagator.state. In order to mutate the current state after a call to prop_step!, the following pattern is recommended:\n\nΨ = propagator.state\nfoo_mutate!(Ψ)\nset_state!(propagator, Ψ)\n\nwhere foo_mutate! is some function that mutates Ψ. This is guaranteed to work efficiently both for in-place and not-in-place propagators, without incurring unnecessary copies.\n\nwarning: Warning\nfoo_mutate!(propagator.state)by itself is not a safe operation. Always follow it byset_state!(propagator, propagator.state)\n\nSee also\n\nset_t! — set propagator.t.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.set_t!-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.set_t!","text":"Set the current time for the propagation.\n\nset_t!(propagator, t)\n\nSets propagator.t to the given value of t, where t must be an element of propagator.tlist.\n\nSee also\n\nset_state! — set propagator.state\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.timings_enabled-Tuple{}","page":"-","title":"QuantumPropagators.timings_enabled","text":"Check whether the collection of TimerOutputs data is active.\n\nQuantumPropagators.timings_enabled()\n\nreturns true if QuantumPropagators.enable_timings() was called, and false otherwise or after QuantumPropagators.disable_timings().\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Generators]","category":"page"},{"location":"externals/#QuantumPropagators.Generators.Generator","page":"-","title":"QuantumPropagators.Generators.Generator","text":"A time-dependent generator.\n\nGenerator(ops::Vector{OT}, amplitudes::Vector{AT})\n\nproduces an object of type Generator{OT,AT} that represents\n\nH(t)= H_0 + sum_l a_l(ϵ_l(t) t) H_l\n\nwhere H_l are the ops and a_l(t) are the amplitudes. H(t) and H_l may represent operators in Hilbert space or super-operators in Liouville space. If the number of amplitudes is less than the number of ops, the first ops are considered as drift terms (H_0, respectively subsequent terms with a_l 1). At least one time-dependent amplitude is required. Each amplitude may depend on one or more control functions ϵ_l(t), although most typically a_l(t) ϵ_l(t), that is, the amplitudes are simply a vector of the controls. See hamiltonian for details.\n\nA Generator object should generally not be instantiated directly, but via hamiltonian or liouvillian.\n\nThe list of ops and amplitudes are properties of the Generator. They should not be mutated.\n\nSee also\n\nOperator for static generators, which may be obtained from a Generator via evaluate.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Generators.Operator","page":"-","title":"QuantumPropagators.Generators.Operator","text":"A static operator in Hilbert or Liouville space.\n\nOperator(ops::Vector{OT}, coeffs::Vector{CT})\n\nproduces an object of type Operator{OT,CT} that encapsulates the \"lazy\" sum\n\nH = sum_l c_l H_l\n\nwhere H_l are the ops and c_l are the coeffs, which each must be a constant Number. If the number of coefficients is less than the number of operators, the first ops are considered to have c_l = 1.\n\nAn Operator object would generally not be instantiated directly, but be obtained from a (@ref) via evaluate.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Generators.ScaledOperator","page":"-","title":"QuantumPropagators.Generators.ScaledOperator","text":"A static operator with a scalar pre-factor.\n\nop = ScaledOperator(α, Ĥ)\n\nrepresents the \"lazy\" product α H where H is an operator (typically an Operator instance) and α is a scalar.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Generators.hamiltonian-Tuple","page":"-","title":"QuantumPropagators.Generators.hamiltonian","text":"Initialize a (usually time-dependent) Hamiltonian.\n\nThe most common usage is, e.g.,\n\nusing QuantumPropagators\n\nH₀ = ComplexF64[0 0; 0 1];\nH₁ = ComplexF64[0 1; 1 0];\nϵ₁(t) = 1.0;\n\nhamiltonian(H₀, (H₀, ϵ₁))\n\n# output\n\nGenerator with 2 ops and 1 amplitudes\n ops::Vector{Matrix{ComplexF64}}:\n ComplexF64[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]\n ComplexF64[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]\n amplitudes::Vector{typeof(ϵ₁)}:\n ϵ₁\n\nIn general,\n\nH = hamiltonian(terms...; check=true)\n\nconstructs a Hamiltonian based on the given terms. Each term must be an operator or a tuple (op, ampl) of an operator and a control amplitude. Single operators are considered \"drift\" terms.\n\nIn most cases, each control amplitude will simply be a control function or vector of pulse values. In general, ampl can be an arbitrary object that depends on one or more controls, which must be obtainable via get_controls(ampl). See QuantumPropagators.Interfaces.check_amplitude for the required interface.\n\nThe hamiltonian function will generally return a Generator instance. However, if none of the given terms are time-dependent, it may return a static operator (e.g., an AbstractMatrix or Operator):\n\nhamiltonian(H₀)\n# output\n2×2 Matrix{ComplexF64}:\n 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 1.0+0.0im\n\nhamiltonian(H₀, (H₁, 2.0))\n# output\nOperator with 2 ops and 1 coeffs\n ops::Vector{Matrix{ComplexF64}}:\n ComplexF64[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]\n ComplexF64[0.0 + 0.0im 1.0 + 0.0im; 1.0 + 0.0im 0.0 + 0.0im]\n coeffs: [2.0]\n\nThe hamiltonian function may generate warnings if the terms are of an unexpected type or structure. These can be suppressed with check=false.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Generators.liouvillian","page":"-","title":"QuantumPropagators.Generators.liouvillian","text":"Construct a Liouvillian Generator.\n\nℒ = liouvillian(Ĥ, c_ops=(); convention=:LvN, check=true)\n\ncalculates the sparse Liouvillian super-operator ℒ from the Hamiltonian Ĥ and a list c_ops of Lindblad operators.\n\nWith convention=:LvN, applying the resulting ℒ to a vectorized density matrix ρ⃗ calculates fracddt vecrho(t) = ℒ vecrho(t) equivalent to the Liouville-von-Neumann equation for the density matrix ρ,\n\nfracddt ρ(t)\n= -i H ρ(t) + sum_kleft(\n A_k ρ A_k^dagger\n - frac12 A_k^dagger A_k ρ\n - frac12 ρ A_k^dagger A_k\n right)\n\nwhere the Lindblad operators A_k are the elements of c_ops.\n\nThe Hamiltonian H will generally be time-dependent. For example, it may be a Generator as returned by hamiltonian. For example, for a Hamiltonian with the terms (Ĥ₀, (Ĥ₁, ϵ₁), (Ĥ₂, ϵ₂)), where Ĥ₀, Ĥ₁, Ĥ₂ are matrices, and ϵ₁ and ϵ₂ are functions of time, the resulting ℒ will be a Generator corresponding to terms (ℒ₀, (ℒ₁, ϵ₁), (ℒ₂, ϵ₂)), where the initial terms is the superoperator ℒ₀ for the static component of the Liouvillian, i.e., the commutator with the drift Hamiltonian Ĥ₀, plus the dissipator (sum over k), as a sparse matrix. Time-dependent Lindblad operators are not currently supported. The remaining elements are tuples (ℒ₁, ϵ₁) and (ℒ₂, ϵ₂) corresponding to the commutators with the two control Hamiltonians, where ℒ₁ and ℒ₂ again are sparse matrices.\n\nIf H is not time-dependent, the resulting ℒ will likewise be a static operator. Passing H=nothing with non-empty c_ops initializes a pure dissipator.\n\nWith convention=:TDSE, the Liouvillian will be constructed for the equation of motion i hbar fracddt vecrho(t) = ℒ vecrho(t) to match exactly the form of the time-dependent Schrödinger equation. While this notation is not standard in the literature of open quantum systems, it has the benefit that the resulting ℒ can be used in a numerical propagator for a (non-Hermitian) Schrödinger equation without any change. Thus, for numerical applications, convention=:TDSE is generally preferred. The returned ℒ between the two conventions differs only by a factor of i, since we generally assume hbar=1.\n\nThe convention keyword argument is mandatory, to force a conscious choice.\n\nSee Goerz et. al. \"Optimal control theory for a unitary operation under dissipative evolution\", arXiv 1312.0111v2, Appendix B.2 for the explicit construction of the Liouvillian superoperator as a sparse matrix.\n\nPassing check=false, suppresses warnings and errors about unexpected types or the structure of the arguments, cf. hamiltonian.\n\n\n\n\n\n","category":"function"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Arnoldi]","category":"page"},{"location":"externals/#QuantumPropagators.Arnoldi.arnoldi!-Union{Tuple{T}, Tuple{Matrix{ComplexF64}, Array{T}, Int64, T, Any}, Tuple{Matrix{ComplexF64}, Array{T}, Int64, T, Any, Float64}} where T","page":"-","title":"QuantumPropagators.Arnoldi.arnoldi!","text":"m = arnoldi!(Hess, q, m, Ψ, H, dt=1.0; extended=true, norm_min=1e-15)\n\nCalculate the Hessenberg matrix and Arnoldi vectors of H dt, from Ψ.\n\nFor a given order m, the m×m Hessemberg matrix is calculated and stored in in the pre-allocated Hess. Further an array of m normalized Arnoldi vectors is stored in in the pre-allocated q, plus one additional unnormalized Arnoldi vector. The unnormalized m+1st vector could be used to easily extend a given m×m Hessenberg matrix to a (m+1)×(m+1) matrix.\n\nIf the extended Hessenberg matrix is requested (extended=true, default), the m+1st Arnoldi vector is also normalized, and it's norm will be stored in m+1, m entry of the (extended) Hessenberg matrix, which is an (m+1)×(m+1) matrix.\n\nReturn the size m of the calculated Hessenberg matrix. This will usually be the input m, except when the Krylov dimension of H starting from Ψ is less then m. E.g., if Ψ is an eigenstate of H, the returned m will be 1.\n\nSee https://en.wikipedia.org/wiki/Arnoldi_iteration for a description of the algorithm.\n\nArguments\n\nHess::Matrix{ComplexF64}: Pre-allocated storage for the Hessemberg matrix. Can be uninitialized on input. The matrix must be at least of size m×m, or (m+1)×(m+1) if extended=true. On output, the m×m sub-matrix of Hess (with the returned output m) will contain the Hessenberg matrix, and all other elements of Hess be be set to zero.\nq: Pre-allocated array of states similar to Ψ, as storage for the calculated Arnoldi vectors. These may be un-initialized on input. Must be at least of length m+1\nm: The requested dimensions of the output Hessenberg matrix.\nΨ: The starting vector for the Arnoldi procedure. This can be of any type, as long as Φ = H * Ψ results in a vector similar to Ψ, there is an inner products of Φ and Ψ (Ψ⋅Φ is defined), and norm(Ψ) is defined.\nH: The operator (up to dt) for which to calculate the Arnoldi procedure. Can be of any type, as long as H * Ψ is defined.\ndt: The implicit time step; the total operator for which to calculate the Arnoldi procedure is H * dt\nextended: If true (default), calculate the extended Hessenberg matrix, and normalized the final Arnoldi vector\nnorm_min: the minimum value of the norm of Ψ at which Ψ should be considered the zero vector\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Arnoldi.diagonalize_hessenberg_matrix-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.Arnoldi.diagonalize_hessenberg_matrix","text":"diagonalize_hessenberg_matrix(Hess, m; accumulate=false)\n\nDiagonalize the m × m top left submatrix of the given Hessenberg matrix.\n\nIf accumulate is true, return the concatenated eigenvalues for Hess[1:1,1:1] to Hess[1:m,1:m], that is, all sumatrices of size 1 through m.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Arnoldi.extend_arnoldi!","page":"-","title":"QuantumPropagators.Arnoldi.extend_arnoldi!","text":"Extend dimension of Hessenberg matrix by one.\n\nextend_arnoldi!(Hess, q, m, H, dt; norm_min=1e-15)\n\nextends the entries in Hess from size (m-1)×(m-1) to size m×m, and the list q of Arnoldi vectors from m to (m+1). It is assumed that the input Hess was created by a call to arnoldi! with extended=false or a previous call to extend_arnoldi!. Note that Hess itself is not resized, so it must be allocated to size m×m or greater on input.\n\n\n\n\n\n","category":"function"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Interfaces]","category":"page"},{"location":"externals/#QuantumPropagators.Interfaces.check_amplitude-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_amplitude","text":"Check amplitude appearing in Generator.\n\n@test check_amplitude(ampl; tlist, quiet=false)\n\nverifies that the given ampl is a valid element in the list of amplitudes of a Generator object. Specifically:\n\nget_controls(ampl) must be defined and return a tuple\nall controls in ampl must pass check_control\nsubstitute(ampl, controls_replacements) must be defined\nevaluate(ampl, tlist, n) must be defined and return a Number\nevaluate(ampl, tlist, n; vals_dict) must be defined and return a Number\n\nIf for_parameterization (may require the RecursiveArrayTools package to be loaded):\n\nget_parameters(ampl) must be defined and return a vector of floats. Mutating that vector must mutate the controls inside the ampl.\n\nThe function returns true for a valid amplitude and false for an invalid amplitude. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_control-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_control","text":"Check that control can be evaluated on a time grid.\n\n@test check_control(\n control;\n tlist,\n for_parameterization=true,\n for_time_continuous=(control isa Function),\n quiet=false\n)\n\nverifies the given control (one of the elements of the tuple returned by get_controls):\n\nevaluate(control, tlist, n) must be defined and return a Float64\nevaluate(control, tlist, n; vals_dict=IdDict(control => v)) must be defined and return v\ndiscretize(control, tlist) must be defined and return a vector of floats of the same size as tlist. Only if length(tlist) > 2.\nall values in discretize(control, tlist) must be finite (isfinite).\ndiscretize_on_midpoints(control, tlist) must be defined and return a vector of floats with one element less than tlist. Only if length(tlist) > 2.\nall values in discretize_on_midpoints(control, tlist) must be finite (isfinite)\n\nIf for_time_continuous:\n\nevaluate(control, t) must be defined and return a Float64\nevaluate(control, t; vals_dict=IdDict(control => v)) must be defined and return v\n\nIf for_parameterization:\n\nget_parameters(control) must be defined and return a vector of floats. Mutating that vector must mutate the control.\n\nThe function returns true for a valid control and false for an invalid control. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_generator-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_generator","text":"Check the dynamical generator for propagating state over tlist.\n\n@test check_generator(\n generator; state, tlist,\n for_mutable_operator=true, for_immutable_operator=true,\n for_mutable_state=true, for_immutable_state=true,\n for_pwc=true, for_time_continuous=false,\n for_expval=true, for_parameterization=false,\n atol=1e-14, quiet=false)\n\nverifies the given generator:\n\nget_controls(generator) must be defined and return a tuple\nall controls returned by get_controls(generator) must pass check_control\nsubstitute(generator, replacements) must be defined\nIf generator is a Generator instance, all elements of generator.amplitudes must pass check_amplitude with for_parameterization.\n\nIf for_pwc (default):\n\nevaluate(generator, tlist, n) must return a valid operator (check_operator), with forwarded keyword arguments (including for_expval)\nIf for_mutable_operator, evaluate!(op, generator, tlist, n) must be defined\n\nIf for_time_continuous:\n\nevaluate(generator, t) must return a valid operator (check_operator), with forwarded keyword arguments (including for_expval)\nIf for_mutable_operator, evaluate!(op, generator, t) must be defined\n\nIf for_parameterization (may require the RecursiveArrayTools package to be loaded):\n\nget_parameters(generator) must be defined and return a vector of floats. Mutating that vector must mutate the controls inside the generator.\n\nThe function returns true for a valid generator and false for an invalid generator. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_operator-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_operator","text":"Check that op is a valid operator that can be applied to state.\n\n@test check_operator(op; state, tlist=[0.0, 1.0],\n for_mutable_state=true, for_immutable_state=true,\n for_expval=true, atol=1e-14, quiet=false)\n\nverifies the given op relative to state. The state must pass check_state.\n\nAn \"operator\" is any object that evaluate returns when evaluating a time-dependent dynamic generator. The specific requirements for op are:\n\nop must not be time-dependent: evaluate(op, tlist, 1) ≡ op\nop must not contain any controls: length(get_controls(op)) == 0\n\nIf for_immutable_state (e.g., for use in propagators with inplace=false):\n\nop * state must be defined\n\nIf for_mutable_state (e.g., for use in propagators with inplace=true):\n\nThe 3-argument LinearAlgebra.mul! must apply op to the given state\nThe 5-argument LinearAlgebra.mul! must apply op to the given state\nLinearAlgebra.mul! must match *, if applicable\nLinearAlgebra.mul! must return the resulting state\n\nIf for_expval (typically required for optimal control):\n\nLinearAlgebra.dot(state, op, state) must return return a number\ndot(state, op, state) must match dot(state, op * state), if applicable\n\nThe function returns true for a valid operator and false for an invalid operator. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_parameterized-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_parameterized","text":"Check that that the object supports the parameterization interface.\n\n@test check_parameterized(object; name=\"::$typeof(object))\", quiet=false)\n\nverifies that the given object:\n\ncan be passed to get_parameters, which must return an AbstractVector of Float64\nis mutated by mutating the parameters obtained by get_parameters\n\nSee also\n\ncheck_parameterized_function is object is a ParameterizedFunction\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_parameterized_function-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_parameterized_function","text":"Check a ParameterizedFunction instance.\n\n@test check_parameterized_function(f; tlist; quiet=false)\n\nverifies that the given f:\n\nis an instance of ParameterizedFunction.\nhas a field parameters that is an AbstractVector{Float64}.\nis a callable as f(t) for values of t in tlist, returning a Float64.\nget_parameters provides access to the parameters field.\npasses check_parameterized\n\nSee also\n\ncheck_parameterized for objects that have parameters (get_parameters), but are not instances of ParameterizedFunction\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_propagator-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_propagator","text":"Check that the given propagator implements the required interface.\n\n@test check_propagator(propagator; atol=1e-14, quiet=false)\n\nverifies that the propagator matches the interface described for an AbstractPropagator. The propagator must have been freshly initialized with init_prop.\n\npropagator must have the properties state, tlist, t, parameters, backward, and inplace\npropagator.state must be a valid state (see check_state), with support for in-place operations (for_mutable_state=true) if propagator.inplace is true.\npropagator.tlist must be monotonically increasing.\npropagator.t must be the first or last element of propagator.tlist, depending on propagator.backward\nprop_step!(propagator) must be defined and return a valid state until the time grid is exhausted\nFor an in-place propagator, the state returned by prop_step! must be the propagator.state object\nFor a not-in-place propagator, the state returned by prop_step! must be a new object\nprop_step! must advance propagator.t forward or backward one step on the time grid\nprop_step! must return nothing when going beyond the time grid\nset_t!(propagator, t) must be defined and set propagator.t\nset_state!(propagator, state) must be defined and set propagator.state.\nset_state!(propagator, state) for an in-place propagator must overwrite propagator.state in-place.\nset_state! must return the set propagator.state\nIn a PiecewisePropagator, propagator.parameters must be a dict mapping controls to a vector of values, one for each interval on propagator.tlist\nreinit_prop! must be defined and re-initialize the propagator\nreinit_prop!(propagator, state) must be idempotent. That is, repeated calls to reinit_prop! leave the propagator unchanged.\n\nThe function returns true for a valid propagator and false for an invalid propagator. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_state-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_state","text":"Check that state is a valid element of a Hilbert space.\n\n@test check_state(state;\n for_immutable_state=true, for_mutable_state=true,\n normalized=false, atol=1e-15, quiet=false)\n\nverifies the following requirements:\n\nThe inner product (LinearAlgebra.dot) of two states must return a Complex number type.\nThe LinearAlgebra.norm of state must be defined via the inner product. This is the definition of a Hilbert space, a.k.a a complete inner product space or more precisely a Banach space (normed vector space) where the norm is induced by an inner product.\n\nIf for_immutable_state:\n\nstate + state and state - state must be defined\ncopy(state) must be defined\nc * state for a scalar c must be defined\nnorm(state + state) must fulfill the triangle inequality\n0.0 * state must produce a state with norm 0\ncopy(state) - state must have norm 0\nnorm(state) must have absolute homogeneity: norm(s * state) = s * norm(state)\n\nIf for_mutable_state:\n\nsimilar(state) must be defined and return a valid state\ncopyto!(other, state) must be defined\nLinearAlgebra.lmul!(c, state) for a scalar c must be defined\nLinearAlgebra.axpy!(c, state, other) must be defined\nnorm(state) must fulfill the same general mathematical norm properties as with for_immutable_state.\n\nIf normalized (not required by default):\n\nLinearAlgebra.norm(state) must be 1\n\nIt is strongly recommended to always support immutable operations (also for mutable states)\n\nThe function returns true for a valid state and false for an invalid state. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_tlist-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_tlist","text":"Check that the given tlist is valid.\n\n@test check_tlist(tlist; quiet=false)\n\nverifies the given time grid. A valid time grid must\n\nbe a Vector{Float64},\ncontain at least two points (beginning and end),\nbe monotonically increasing\n\nThe function returns true for a valid time grid and false for an invalid time grid. Unless quiet=true, it will log an error to indicated which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Controls]","category":"page"},{"location":"externals/#QuantumPropagators.Controls.ParameterizedFunction","page":"-","title":"QuantumPropagators.Controls.ParameterizedFunction","text":"Abstract type for function-like objects with parameters.\n\nA struct that is an implementation of a ParameterizedFunction:\n\nmust have a parameters field that is an AbstractVector of floats (e.g., a ComponentArrays.ComponentVector)\nmust be callable with a single float argument t,\nmay define getters and setters for referencing the values in parameters with convenient names.\n\nThe parameters field of any ParameterizedFunction can be accessed via get_parameters.\n\nSee How to define a parameterized control for an example. You may use the QuantumPropagators.Interfaces.check_parameterized_function to check the implementation of a ParameterizedFunction subtype.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Controls.discretize-Tuple{Function, Any}","page":"-","title":"QuantumPropagators.Controls.discretize","text":"Evaluate control at every point of tlist.\n\nvalues = discretize(control, tlist; via_midpoints=true)\n\ndiscretizes the given control to a Vector of values defined on the points of tlist.\n\nIf control is a function, it is first evaluated at the midpoint of tlist, see discretize_on_midpoints, and then the values on the midpoints are converted to values on tlist. This discretization is more stable than directly evaluating the control function at the values of tlist, and ensures that repeated round-trips between discretize and discretize_on_midpoints can be done safely, see the note in the documentation of discretize_on_midpoints.\n\nThe latter can still be achieved by passing via_midpoints=false. While such a direct discretization is suitable e.g. for plotting, but it is unsuitable for round-trips between discretize and discretize_on_midpoints (constant controls on tlist may result in a zig-zag on the intervals of tlist).\n\nIf control is a vector, a copy of control will be returned if it is of the same length as tlist. Otherwise, control must have one less value than tlist, and is assumed to be defined on the midpoints of tlist. In that case, discretize acts as the inverse of discretize_on_midpoints. See discretize_on_midpoints for how control values on tlist and control values on the intervals of tlist are related.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.discretize_on_midpoints-Union{Tuple{T}, Tuple{T, Any}} where T<:Function","page":"-","title":"QuantumPropagators.Controls.discretize_on_midpoints","text":"Evaluate control at the midpoints of tlist.\n\nvalues = discretize_on_midpoints(control, tlist)\n\ndiscretizes the given control to a Vector of values on the midpoints of tlist. Hence, the resulting values will contain one less value than tlist.\n\nIf control is a vector of values defined on tlist (i.e., of the same length as tlist), it will be converted to a vector of values on the intervals of tlist. The value for the first and last \"midpoint\" will remain the original values at the beginning and end of tlist, in order to ensure exact boundary conditions. For all other midpoints, the value for that midpoint will be calculated by \"un-averaging\".\n\nFor example, for a control and tlist of length 5, consider the following diagram:\n\ntlist index: 1 2 3 4 5\ntlist: ⋅ ⋅ ⋅ ⋅ ⋅ input values cᵢ (i ∈ 1..5)\n |̂/ ̄ ̄ ̂\\ / ̂\\ / ̂ ̄ ̄\\|̂\nmidpoints: x x x x output values pᵢ (i ∈ 1..4)\nmidpoints index: 1 2 3 4\n\nWe will have p₁=c₁ for the first value, p₄=c₅ for the last value. For all other points, the control values cᵢ = fracp_i-1 + p_i2 are the average of the values on the midpoints. This implies the \"un-averaging\" for the midpoint values pᵢ = 2 c_i - p_i-1.\n\nnote: Note\nAn arbitrary input control array may not be compatible with the above averaging formula. In this case, the conversion will be \"lossy\" (discretize will not recover the original control array; the difference should be considered a \"discretization error\"). However, any further round-trip conversions between points and intervals are bijective and preserve the boundary conditions. In this case, the discretize_on_midpoints and discretize methods are each other's inverse. This also implies that for an optimal control procedure, it is safe to modify midpoint values. Modifying the the values on the time grid directly on the other hand may accumulate discretization errors.\n\nIf control is a vector of one less length than tlist, a copy of control will be returned, under the assumption that the input is already properly discretized.\n\nIf control is a function, the function will be directly evaluated at the midpoints marked as x in the above diagram..\n\nSee also\n\nget_tlist_midpoints – get all the midpoints on which the control will be discretized.\nt_mid – get a particular midpoint.\ndiscretize – discretize directly on tlist instead of on the midpoints\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.evaluate!-Tuple{Any, Tuple, Vararg{Any}}","page":"-","title":"QuantumPropagators.Controls.evaluate!","text":"Update an existing evaluation of a generator.\n\nevaluate!(op, generator, args..; vals_dict=IdDict())\n\nperforms an in-place update on an op the was obtained from a previous call to evaluate with the same generator, but for a different point in time and/or different values in vals_dict.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.evaluate-Tuple{Any, Vararg{Any}}","page":"-","title":"QuantumPropagators.Controls.evaluate","text":"Evaluate all controls.\n\nIn general, evaluate(object, args...; vals_dict=IdDict()) evaluates the object for a specific point in time indicated by the positional args. Any control in object is evaluated at the specified point in time. Alternatively, the vals_dict maps a controls to value (\"plug in this value for the given control\")\n\nFor example,\n\nop = evaluate(generator, t)\n\nevaluates generator at time t. This requires that any control in generator is a callable that takes t as a single argument.\n\nop = evaluate(generator, tlist, n)\n\nevaluates generator for the n'th interval of tlist. This uses the definitions for the midpoints in discretize_on_midpoints. The controls in generator may be vectors (see discretize, discretize_on_midpoints) or callables of t.\n\nop = evaluate(generator, t; vals_dict)\nop = evaluate(generator, tlist, n; vals_dict)\n\nresolves any explicit time dependencies in generator at the specified point in time, but uses the value in the given vals_dict for any control in vals_dict.\n\na = evaluate(ampl, tlist, n; vals_dict=IdDict())\na = evaluate(ampl, t; vals_dict=IdDict())\n\nevaluates a control amplitude to a scalar by evaluating any explicit time dependency, and by replacing each control with the corresponding value in vals_dict.\n\nCalling evaluate for an object with no implicit or explicit time dependence should return the object unchanged.\n\nFor generators without any explicit time dependence,\n\nop = evaluate(generator; vals_dict)\n\ncan be used. The vals_dict in this case must contain values for all controls in generator.\n\nSee also:\n\nevaluate! — update an existing operator with a re-evaluation of a\n\ngenerator at a different point in time.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_controls-Tuple{AbstractMatrix}","page":"-","title":"QuantumPropagators.Controls.get_controls","text":"get_controls(operator)\n\nfor a static operator (matrix) returns an empty tuple.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_controls-Tuple{Function}","page":"-","title":"QuantumPropagators.Controls.get_controls","text":"Extract a Tuple of controls.\n\ncontrols = get_controls(generator)\n\nextracts the controls from a single dynamical generator.\n\nFor example, if generator = hamiltonian(H0, (H1, ϵ1), (H2, ϵ2)), extracts (ϵ1, ϵ2).\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_parameters-Tuple{Any}","page":"-","title":"QuantumPropagators.Controls.get_parameters","text":"Obtain analytic parameters of the given control.\n\nparameters = get_parameters(control)\n\nobtains parameters as an AbstractVector{Float64} containing any tunable analytic parameters associated with the control. The specific type of parameters depends on how control is defined, but a ComponentArrays.ComponentVector should be a common array type.\n\nMutating the resulting vector must directly affect the control in any subsequent call to evaluate. That is, the values in parameters must alias values inside the control.\n\nNote that the control must be an object specifically designed to have analytic parameters. Typically, it should be implemented as a subtype of ParameterizedFunction. For a simple function ϵ(t) or a vector of pulse values, which are the default types of controls discussed in the documentation of hamiltonian, the get_parameters function will return an empty vector.\n\nMore generally,\n\nparameters = get_parameters(object)\n\ncollects and combines all unique parameter arrays from the controls inside the object. The object may be a Generator, Trajectory, ControlProblem, or any other object for which get_controls(object) is defined. If there are multiple controls with different parameter arrays, these are combined in a RecursiveArrayTools.ArrayPartition. This requires the RecursiveArrayTools package to be loaded. Again, mutating parameters directly affects the underlying controls.\n\nThe parameters may be used as part of the parameters attribute of a propagator for time-continuous dynamics, like a general ODE solver, or in an optimization that tunes analytic control parameters, e.g., with a Nelder-Mead method. Examples might include the widths, peak amplitudes, and times of a superposition of Gaussians [9], cf. the example of a ParameterizedFunction, or the amplitudes associated with spectral components in a random truncated basis [10].\n\nThe parameters are not intended for optimization methods such as GRAPE or Krotov that fundamentally use a piecewise-constant control ansatz. In the context of such methods, the \"control parameters\" are always the amplitudes of the control at the mid-points of the time grid, as obtained by discretize_on_midpoints, and get_parameters is ignored.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_tlist_midpoints-Tuple{AbstractVector}","page":"-","title":"QuantumPropagators.Controls.get_tlist_midpoints","text":"Shift time grid values to the interval midpoints\n\ntlist_midpoints = get_tlist_midpoints(\n tlist; preserve_start=true, preserve_end=true\n)\n\ntakes a vector tlist of length n and returns a Vector{Float64} of length n-1 containing the midpoint values of each interval. The intervals in tlist are not required to be uniform.\n\nBy default, the first and last point of tlist is preserved, see discretize_on_midpoints. This behavior can be disabled by passing preserve_start and preserve_end as false in order to use the midpoints of the first and last interval, respectively.\n\nSee also\n\nt_mid – get a particular midpoint.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.substitute-Union{Tuple{T}, Tuple{T, Any}} where T","page":"-","title":"QuantumPropagators.Controls.substitute","text":"Substitute inside the given object.\n\nobject = substitute(object, replacements)\n\nreturns a modified object with the replacements defined in the given replacements dictionary. Things that can be replaced include operators, controls, and amplitudes. For example,\n\ngenerator = substitute(generator::Generator, replacements)\noperator = substitute(operator::Operator, replacements)\namplitude = substitute(amplitude, controls_replacements)\n\nNote that substitute cannot be used to replace dynamic quantities, e.g. controls, with static value. Use evaluate instead for that purpose.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.t_mid-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.Controls.t_mid","text":"Midpoint of n'th interval of tlist.\n\nt = t_mid(tlist, n)\n\nreturns the t that is the midpoint between points tlist[n+1] and tlist[n], but snapping to the beginning/end to follow the convention explained in discretize_on_midpoints (to preserve exact boundary conditions at the edges of the time grid.)\n\nSee also\n\nget_tlist_midpoints – get all the midpoints in one go.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Storage]","category":"page"},{"location":"externals/#QuantumPropagators.Storage.get_from_storage!-Tuple{Any, AbstractVector, Any}","page":"-","title":"QuantumPropagators.Storage.get_from_storage!","text":"Obtain data from storage.\n\nget_from_storage!(data, storage, i)\n\nextracts data from the storage for the i'th time slot. Inverse of write_to_storage!. This modifies data in-place. If get_from_storage! is implemented for arbitrary observables, it is the developer's responsibility that init_storage, write_to_storage!, and get_from_storage! are compatible.\n\nTo extract immutable data, the non-in-place version\n\ndata = get_from_storage(storage, i)\n\ncan be used.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.get_from_storage-Tuple{AbstractVector, Any}","page":"-","title":"QuantumPropagators.Storage.get_from_storage","text":"Obtain immutable data from storage.\n\ndata = get_from_storage(storage, i)\n\nSee get_from_storage!.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.init_storage-Tuple{Any, AbstractVector}","page":"-","title":"QuantumPropagators.Storage.init_storage","text":"Create a storage array for propagation.\n\nstorage = init_storage(state, tlist)\n\ncreates a storage array suitable for storing a state for each point in tlist.\n\nstorage = init_storage(state, tlist, observables)\n\ncreates a storage array suitable for the data generated by the observables applied to state, see map_observables, for each point in tlist.\n\nstorage = init_storage(data, nt)\n\ncreates a storage arrays suitable for storing data nt times, where nt=length(tlist). By default, this will be a vector of typeof(data) and length nt, or a n × nt Matrix with the same eltype as data if data is a Vector of length n.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.map_observable-Union{Tuple{ST}, Tuple{IT}, Tuple{TT}, Tuple{F}, Tuple{F, TT, IT, ST}} where {F<:Function, TT, IT, ST}","page":"-","title":"QuantumPropagators.Storage.map_observable","text":"Apply a single observable to state.\n\ndata = map_observable(observable, tlist, i, state)\n\nBy default, observable can be one of the following:\n\nA function taking the three arguments state, tlist, i, where state is defined at time tlist[i].\nA function taking a single argument state, under the assumption that the observable is time-independent\nA matrix for which to calculate the expectation value with respect to the vector state.\n\nThe default map_observables delegates to this function.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.map_observables-NTuple{4, Any}","page":"-","title":"QuantumPropagators.Storage.map_observables","text":"Obtain \"observable\" data from state.\n\ndata = map_observables(observables, tlist, i, state)\n\ncalculates the data for a tuple of observables applied to state defined at time tlist[i]. For a single observable (tuple of length 1), simply return the result of map_observable.\n\nFor multiple observables, return the tuple resulting from applying map_observable for each observable. If the tuple is \"uniform\" (all elements are of the same type, e.g. if each observable calculates the expectation value of a Hermitian operator), it is converted to a Vector. This allows for compact storage in a storage array, see init_storage.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.write_to_storage!-Tuple{AbstractVector, Integer, Any}","page":"-","title":"QuantumPropagators.Storage.write_to_storage!","text":"Place data into storage for time slot i.\n\nwrite_to_storage!(storage, i, data)\n\nfor a storage array created by init_storage stores the data obtained from map_observables at time slot i.\n\nConceptually, this corresponds roughly to storage[i] = data, but storage may have its own idea on how to store data for a specific time slot. For example, with the default init_storage Vector data will be stored in a matrix, and write_to_storage! will in this case write data to the i'th column of the matrix.\n\nFor a given type of storage and data, it is the developer's responsibility that init_storage and write_to_storage! are compatible.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.SpectralRange]","category":"page"},{"location":"externals/#QuantumPropagators.SpectralRange.random_state-Tuple{Any}","page":"-","title":"QuantumPropagators.SpectralRange.random_state","text":"Random normalized quantum state.\n\n Ψ = random_state(H; rng=Random.GLOBAL_RNG)\n\nreturns a random normalized state compatible with the Hamiltonian H. This is intended to provide a starting vector for estimating the spectral radius of H via an Arnoldi method.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.SpectralRange.ritzvals","page":"-","title":"QuantumPropagators.SpectralRange.ritzvals","text":"Calculate a vector for Ritz values converged to a given precision.\n\nR = ritzvals(G, state, m_min, m_max=2*m_min; prec=1e-5, norm_min=1e-15)\n\ncalculates a complex vector R of at least m_min (assuming a sufficient Krylov dimension) and at most m_max Ritz values.\n\n\n\n\n\n","category":"function"},{"location":"externals/#QuantumPropagators.SpectralRange.specrange-Tuple{Any, Val{:arnoldi}}","page":"-","title":"QuantumPropagators.SpectralRange.specrange","text":"E_min, E_max = specrange(\n H, :arnoldi;\n rng=Random.GLOBAL_RNG,\n state=random_state(H; rng),\n m_min=20,\n m_max=60,\n prec=1e-3,\n norm_min=1e-15,\n enlarge=true\n)\n\nuses Arnoldi iteration with state as the starting vector. It approximates the eigenvalues of H with between m_min and m_max Ritz values, until the lowest and highest eigenvalue are stable to a relative precision of prec. The norm_min parameter is passed to the underlying arnoldi!.\n\nIf enlarge=true (default) the returned E_min and E_max will be enlarged via a heuristic to slightly over-estimate the spectral radius instead of under-estimating it.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.SpectralRange.specrange-Tuple{Any, Val{:diag}}","page":"-","title":"QuantumPropagators.SpectralRange.specrange","text":"E_min, E_max = specrange(H, :diag)\n\nuses exact diagonization via the standard eigvals function to obtain the smallest and largest eigenvalue. This should only be used for relatively small matrices.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.SpectralRange.specrange-Tuple{Any, Val{:manual}}","page":"-","title":"QuantumPropagators.SpectralRange.specrange","text":"E_min, E_max = specrange(H, :manual; E_min, E_max)\n\ndirectly returns the given E_min and E_max without considering H.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.SpectralRange.specrange-Tuple{Any}","page":"-","title":"QuantumPropagators.SpectralRange.specrange","text":"Calculate the spectral range of a Hamiltonian H on the real axis.\n\nE_min, E_max = specrange(H; method=:auto, kwargs...)\n\ncalculates the approximate lowest and highest eigenvalues of H. Any imaginary part in the eigenvalues is ignored: the routine is intended for (although not strictly limited to) a Hermitian H.\n\nThis delegates to\n\nspecrange(H, method; kwargs...)\n\nfor the different methods.\n\nThe default method=:auto chooses the best method for the given H. This is :diag for small matrices, and :arnoldi otherwise. If both E_min and E_max are given in the kwargs, those will be returned directly (method=:manual).\n\nKeyword arguments not relevant to the underlying implementation will be ignored.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Newton]","category":"page"},{"location":"externals/#QuantumPropagators.Newton.NewtonWrk","page":"-","title":"QuantumPropagators.Newton.NewtonWrk","text":"NewtonWrk(v0, m_max=10)\n\nWorkspace for the Newton-with-restarted-Arnoldi propagation routine.\n\nInitializes the workspace for the propagation of a vector v0, using a maximum Krylov dimension of m_max in each restart iteration. Note that m_max should be smaller than the length of v0.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Newton.extend_leja!-Tuple{OffsetArrays.OffsetVector{ComplexF64, AA} where AA<:AbstractVector{ComplexF64}, Any, OffsetArrays.OffsetVector{ComplexF64, AA} where AA<:AbstractVector{ComplexF64}, Any}","page":"-","title":"QuantumPropagators.Newton.extend_leja!","text":"extend_leja!(leja, n, newpoints, n_use)\n\nGiven an array of n (ordered) Leja points, extract n_use points from newpoints, and append them to the existing Leja points. The array leja should be sufficiently large to hold the new Leja points, which are appended after index n_old. It will be re-allocated if necessary and may have a size of up to 2*(n+n_use).\n\nArguments\n\nleja: Array of leja values. Must contain the \"old\" leja values to be kept in leja(0:n-1). On output, n_use new leja points will be in leja(n+:n+n_use-1), for the original value of n. The leja array must use zero-based indexing.\nn: On input, number of \"old\" leja points in leja. On output, total number of leja points (i.e. n=n+n_use)\nnewpoints: On input, candidate points for new leja points. The n_use best values will be chosen and added to leja. On output, the values of new_points are undefined.\nn_use: Number of points that should be added to leja\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Newton.extend_newton_coeffs!-Tuple{OffsetArrays.OffsetVector{ComplexF64, AA} where AA<:AbstractVector{ComplexF64}, Int64, OffsetArrays.OffsetVector{ComplexF64, AA} where AA<:AbstractVector{ComplexF64}, Any, Int64, Float64}","page":"-","title":"QuantumPropagators.Newton.extend_newton_coeffs!","text":"extend_newton_coeffs!(a, n_a, leja, func, n_leja, radius)\n\nExtend the array a of existing Newton coefficients for the expansion of the func from n_a coefficients to n_leja coefficients. Return a new value n_a=n_a+n_leja with the total number of Newton coefficients in the updated a.\n\nArguments\n\na: On input, a zero-based array of length n_a or greater, containing Newton coefficients. On output, array containing a total n_leja coefficients. The array a will be resized if necessary, and may have a length greater than n_leja on output\nn_a: The number of Newton coefficients in a, on input. Elements of a beyond the first n_a elements will be overwritten.\nleja: Array of normalized Leja points, containing at least n_leja elements.\nfunc: Function for which to calculate Newton coefficients\nn_leja: The number of elements in leja to use for calculating new coefficients, and the total number of Newton coefficients on output\nradius: Normalization radius for divided differences\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Newton.newton!-NTuple{4, Any}","page":"-","title":"QuantumPropagators.Newton.newton!","text":"newton!(Ψ, H, dt, wrk; func=(z -> exp(-1im*z)), norm_min=1e-14, relerr=1e-12,\n max_restarts=50, _...)\n\nEvaluate Ψ = func(H*dt) Ψ using a Newton-with-restarted-Arnoldi scheme.\n\nArguments\n\nΨ: The state to propagate, will be overwritten in-place with the propagated state\nH: Operator acting on Ψ. Together with dt, this is the argument to func\ndt: Implicit time step. Together with H, this is the argument to func\nwkr: Work array, initialized with NewtonWrk\nfunc: The function to apply to H dt, taking a single (scalar) complex-valued argument z in place of H dt. The default func is to evaluate the time evaluations operator for the Schrödinger equation\nnorm_min: the minimum norm at which to consider a state similar to Ψ as zero\nrelerr: The relative error defining the convergence condition for the restart iteration. Propagation stops when the norm of the accumulated Ψ is stable up to the given relative error\nmax_restarts: The maximum number of restart iterations. Exceeding max_restarts will throw an AssertionError.\n\nAll other keyword arguments are ignored.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Cheby]","category":"page"},{"location":"externals/#QuantumPropagators.Cheby.ChebyWrk","page":"-","title":"QuantumPropagators.Cheby.ChebyWrk","text":"Workspace for the Chebychev propagation routine.\n\nChebyWrk(Ψ, Δ, E_min, dt; limit=1e-12)\n\ninitializes the workspace for the propagation of a state similar to Ψ under a Hamiltonian with eigenvalues between E_min and E_min + Δ, and a time step dt. Chebychev coefficients smaller than the given limit are discarded.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Cheby.cheby!-NTuple{4, Any}","page":"-","title":"QuantumPropagators.Cheby.cheby!","text":"Evaluate Ψ = exp(-𝕚 * H * dt) Ψ in-place.\n\ncheby!(Ψ, H, dt, wrk; E_min=nothing, check_normalization=false)\n\nArguments\n\nΨ: on input, initial vector. Will be overwritten with result.\nH: Hermitian operator\ndt: time step\nwrk: internal workspace\nE_min: minimum eigenvalue of H, to be used instead of the E_min from the initialization of wrk. The same wrk may be used for different values E_min, as long as the spectra radius Δ and the time step dt are the same as those used for the initialization of wrk.\ncheck_normalizataion: perform checks that the H does not exceed the spectral radius for which the workspace was initialized.\n\nThe routine will not allocate any internal storage. This implementation requires copyto! lmul!, and axpy! to be implemented for Ψ, and the three-argument mul! for Ψ and H.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Cheby.cheby-NTuple{4, Any}","page":"-","title":"QuantumPropagators.Cheby.cheby","text":"Evaluate Ψ = exp(-𝕚 * H * dt) Ψ.\n\nΨ_out = cheby(Ψ, H, dt, wrk; E_min=nothing, check_normalization=false)\n\nacts like cheby! but does not modify Ψ in-place.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Cheby.cheby_coeffs!","page":"-","title":"QuantumPropagators.Cheby.cheby_coeffs!","text":"Calculate Chebychev coefficients in-place.\n\nn::Int = cheby_coeffs!(coeffs, Δ, dt, limit=1e-12)\n\noverwrites the first n values in coeffs with new coefficients larger than limit for the given new spectral radius Δ and time step dt. The coeffs array will be resized if necessary, and may have a length > n on exit.\n\nSee also cheby_coeffs for an non-in-place version.\n\n\n\n\n\n","category":"function"},{"location":"externals/#QuantumPropagators.Cheby.cheby_coeffs-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.Cheby.cheby_coeffs","text":"Calculate Chebychev coefficients.\n\na::Vector{Float64} = cheby_coeffs(Δ, dt; limit=1e-12)\n\nreturn an array of coefficients larger than limit.\n\nArguments\n\nΔ: the spectral radius of the underlying operator\ndt: the time step\n\nSee also cheby_coeffs! for an in-place version.\n\n\n\n\n\n","category":"method"},{"location":"overview/#Overview","page":"Overview","title":"Overview","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = Krotov","category":"page"},{"location":"#Krotov.jl","page":"Home","title":"Krotov.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"using Markdown\nusing Pkg\n\nVERSION = Pkg.dependencies()[Base.UUID(\"b05dcdc7-62f6-4360-bf2c-0898bba419de\")].version\n\ngithub_badge = \"[![Github](https://img.shields.io/badge/JuliaQuantumControl-Krotov.jl-blue.svg?logo=github)](https://github.com/JuliaQuantumControl/Krotov.jl)\"\n\nversion_badge = \"![v$VERSION](https://img.shields.io/badge/version-v$VERSION-green.svg)\"\n\nMarkdown.parse(\"$github_badge $version_badge\")","category":"page"},{"location":"","page":"Home","title":"Home","text":"Implementation of Krotov's method of optimal control [1–6] enhanced with automatic differentiation [7].","category":"page"},{"location":"","page":"Home","title":"Home","text":"Part of QuantumControl.jl and the JuliaQuantumControl organization.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Krotov.jl is a port of the krotov Python package, adapted to the API of QuantumControl.jl.","category":"page"},{"location":"#Contents","page":"Home","title":"Contents","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Depth = 2\nPages = [pair[2] for pair in Main.PAGES[2:end-1]]","category":"page"},{"location":"#History","page":"Home","title":"History","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"See the Releases on Github.","category":"page"}] +[{"location":"references/#References","page":"References","title":"References","text":"","category":"section"},{"location":"references/","page":"References","title":"References","text":"V. F. Krotov. Global Methods in Optimal Control (Dekker, New York, NY, USA, 1996).\n\n\n\nJ. Somlói, V. A. Kazakov and D. J. Tannor. Controlled dissociation of I_2 via optical transitions between the X and B electronic states. Chem. Phys. 172, 85 (1993).\n\n\n\nA. Bartana, R. Kosloff and D. J. Tannor. Laser cooling of internal degrees of freedom. II. J. Chem. Phys. 106, 1435 (1997).\n\n\n\nJ. P. Palao and R. Kosloff. Optimal control theory for unitary transformations. Phys. Rev. A 68, 062308 (2003).\n\n\n\nD. M. Reich, M. Ndong and C. P. Koch. Monotonically convergent optimization in quantum control using Krotov's method. J. Chem. Phys. 136, 104103 (2012).\n\n\n\nM. H. Goerz, D. Basilewitsch, F. Gago-Encinas, M. G. Krauss, K. P. Horn, D. M. Reich and C. P. Koch. Krotov: A Python implementation of Krotov's method for quantum optimal control. SciPost Phys. 7, 080 (2019).\n\n\n\nM. H. Goerz, S. C. Carrasco and V. S. Malinovsky. Quantum Optimal Control via Semi-Automatic Differentiation. Quantum 6, 871 (2022).\n\n\n\nM. H. Goerz, D. M. Reich and C. P. Koch. Optimal control theory for a unitary operation under dissipative evolution. New J. Phys. 16, 055012 (2014).\n\n\n\nS. Machnes, E. Assémat, D. Tannor and F. K. Wilhelm. Tunable, Flexible, and Efficient Optimization of Control Pulses for Practical Qubits. Phys. Rev. Lett. 120, 150401 (2018).\n\n\n\nT. Caneva, T. Calarco and S. Montangero. Chopped random-basis quantum optimization. Phys. Rev. A 84, 022326 (2011).\n\n\n\n","category":"page"},{"location":"api/#API","page":"API","title":"API","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Pages = [\"api.md\"]","category":"page"},{"location":"api/","page":"API","title":"API","text":"Modules = [Krotov]","category":"page"},{"location":"api/#Krotov.KrotovResult","page":"API","title":"Krotov.KrotovResult","text":"Result object returned by optimize_krotov.\n\nAttributes\n\nThe attributes of a KrotovResult object include\n\niter: The number of the current iteration\nJ_T: The value of the final-time functional in the current iteration\nJ_T_prev: The value of the final-time functional in the previous iteration\ntlist: The time grid on which the control are discretized.\nguess_controls: A vector of the original control fields (each field discretized to the points of tlist)\noptimized_controls: A vector of the optimized control fields. Calculated only at the end of the optimization, not after each iteration.\ntau_vals: For any trajectory that defines a target_state, the complex overlap of that target state with the propagated state. For any trajectory for which the target_state is nothing, the value is zero.\nrecords: A vector of tuples with values returned by a callback routine passed to optimize\nconverged: A boolean flag on whether the optimization is converged. This may be set to true by a check_convergence function.\nmessage: A message string to explain the reason for convergence. This may be set by a check_convergence function.\n\nAll of the above attributes may be referenced in a check_convergence function passed to optimize(problem; method=Krotov)\n\n\n\n\n\n","category":"type"},{"location":"api/#Krotov.KrotovWrk","page":"API","title":"Krotov.KrotovWrk","text":"Krotov workspace.\n\nThe workspace is for internal use. However, it is also accessible in a callback function. The callback may use or modify some of the following attributes:\n\ntrajectories: a copy of the trajectories defining the control problem\nadjoint_trajectories: The trajectories with the adjoint generator\nkwargs: The keyword arguments from the ControlProblem or the call to optimize.\ncontrols: A tuple of the original controls (probably functions)\nga_a_int: The current value of gₐ(t)dt for each control\nupdate_shapes: The update shapes S(t) for each pulse, discretized on the intervals of the time grid.\nlambda_vals: The current value of λₐ for each control\nresult: The current result object\nfw_storage: The storage of states for the forward propagation\nfw_propagators: The propagators used for the forward propagation\nbw_propagators: The propagators used for the backward propagation\nuse_threads: Flag indicating whether the propagations are performed in parallel.\n\n\n\n\n\n","category":"type"},{"location":"api/#Krotov.optimize_krotov-Tuple{Any}","page":"API","title":"Krotov.optimize_krotov","text":"See optimize(problem; method=Krotov, kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"api/#QuantumControlBase.optimize-Tuple{Any, Val{:Krotov}}","page":"API","title":"QuantumControlBase.optimize","text":"using Krotov\nresult = optimize(problem; method=Krotov, kwargs...)\n\noptimizes the given control problem using Krotov's method, returning a KrotovResult.\n\nKeyword arguments that control the optimization are taken from the keyword arguments used in the instantiation of problem; any of these can be overridden with explicit keyword arguments to optimize.\n\nRequired problem keyword arguments\n\nJ_T: A function J_T(Ψ, trajectories) that evaluates the final time functional from a list Ψ of forward-propagated states and problem.trajectories. The function J_T may also take a keyword argument tau. If it does, a vector containing the complex overlaps of the target states (target_state property of each trajectory in problem.trajectories) with the propagated states will be passed to J_T.\n\nRecommended problem keyword arguments\n\nlambda_a=1.0: The inverse Krotov step width λₐ for every pulse.\nupdate_shape=(t->1.0): A function S(t) for the \"update shape\" that scales the update for every pulse.\n\nIf different controls require different lambda_a or update_shape, a dict pulse_options must be given instead of a global lambda_a and update_shape; see below.\n\nOptional problem keyword arguments\n\nThe following keyword arguments are supported (with default values):\n\npulse_options: A dictionary that maps every control (as obtained by get_controls from the problem.trajectories) to the following dict:\n:lambda_a: The value for inverse Krotov step width λₐ.\n:update_shape: A function S(t) for the \"update shape\" that scales the Krotov pulse update.\nThis overrides the global lambda_a and update_shape arguments.\nchi: A function chi(Ψ, trajectories) that receives a list Ψ of the forward propagated states and returns a vector of states χₖ = -J_TΨₖ. If not given, it will be automatically determined from J_T via make_chi with the default parameters. Similarly to J_T, if chi accepts a keyword argument tau, it will be passed a vector of complex overlaps.\nsigma=nothing: A function that calculates the second-order contribution. If not given, the first-order Krotov method is used.\niter_start=0: The initial iteration number.\niter_stop=5000: The maximum iteration number.\nprop_method: The propagation method to use for each trajectory; see below.\nprint_iters=true: Whether to print information after each iteration.\nstore_iter_info=Set(): Which fields from print_iters to store in result.records. A subset of Set([\"iter.\", \"J_T\", \"∫gₐ(t)dt\", \"J\", \"ΔJ_T\", \"ΔJ\", \"secs\"]).\ncallback: A function (or tuple of functions) that receives the Krotov workspace, the iteration number, the list of updated pulses, and the list of guess pulses as positional arguments. The function may return a tuple of values which are stored in the KrotovResult object result.records. The function can also mutate any of its arguments, in particular the updated pulses. This may be used, e.g., to apply a spectral filter to the updated pulses or to perform similar manipulations. Note that print_iters=true (default) adds an automatic callback to print information after each iteration. With store_iter_info, that callback automatically stores a subset of the printed information.\ncheck_convergence: A function to check whether convergence has been reached. Receives a KrotovResult object result, and should set result.converged to true and result.message to an appropriate string in case of convergence. Multiple convergence checks can be performed by chaining functions with ∘. The convergence check is performed after any callback.\nverbose=false: If true, print information during initialization.\nrethrow_exceptions: By default, any exception ends the optimization but still returns a KrotovResult that captures the message associated with the exception. This is to avoid losing results from a long-running optimization when an exception occurs in a later iteration. If rethrow_exceptions=true, instead of capturing the exception, it will be thrown normally.\n\nTrajectory propagation\n\nKrotov's method involves the forward and backward propagation for every Trajectory in the problem. The keyword arguments for each propagation (see propagate) are determined from any properties of each Trajectory that have a prop_ prefix, cf. init_prop_trajectory.\n\nIn situations where different parameters are required for the forward and backward propagation, instead of the prop_ prefix, the fw_prop_ and bw_prop_ prefixes can be used, respectively. These override any setting with the prop_ prefix. This applies both to the properties of each Trajectory and the problem keyword arguments.\n\nNote that the propagation method for each propagation must be specified. In most cases, it is sufficient (and recommended) to pass a global prop_method problem keyword argument.\n\n\n\n\n\n","category":"method"},{"location":"examples/#Examples","page":"Examples","title":"Examples","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The following examples illustrate the use of Krotov's method:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Example: Entangling quantum gates for coupled transmon qubits. Written for GRAPE, but could use Krotov's method interchangeably.\nExample: Optimization of a Dissipative Quantum Gate\nTutorial: Pulse Parametrization for Krotov's Method","category":"page"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumControlBase]","category":"page"},{"location":"externals/#QuantumControlBase.ControlProblem","page":"-","title":"QuantumControlBase.ControlProblem","text":"A full control problem with multiple trajectories.\n\nControlProblem(\n trajectories,\n tlist;\n kwargs...\n)\n\nThe trajectories are a list of Trajectory instances, each defining an initial state and a dynamical generator for the evolution of that state. Usually, the trajectory will also include a target state (see Trajectory) and possibly a weight. The trajectories may also be given together with tlist as a mandatory keyword argument.\n\nThe tlist is the time grid on which the time evolution of the initial states of each trajectory should be propagated. It may also be given as a (mandatory) keyword argument.\n\nThe remaining kwargs are keyword arguments that are passed directly to the optimal control method. These typically include e.g. the optimization functional.\n\nThe control problem is solved by finding a set of controls that minimize an optimization functional over all trajectories.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumControlBase.Trajectory","page":"-","title":"QuantumControlBase.Trajectory","text":"Description of a state's time evolution.\n\nTrajectory(\n initial_state,\n generator;\n target_state=nothing,\n weight=1.0,\n kwargs...\n)\n\ndescribes the time evolution of the initial_state under a time-dependent dynamical generator (e.g., a Hamiltonian or Liouvillian).\n\nTrajectories are central to quantum control problems: an optimization functional depends on the result of propagating one or more trajectories. For example, when optimizing for a quantum gate, the optimization considers the trajectories of all logical basis states.\n\nIn addition to the initial_state and generator, a Trajectory may include data relevant to the propagation and to evaluating a particular optimization functional. Most functionals have the notion of a \"target state\" that the initial_state should evolve towards, which can be given as the target_state keyword argument. In some functionals, different trajectories enter with different weights [8], which can be given as a weight keyword argument. Any other keyword arguments are also available to a functional as properties of the Trajectory .\n\nA Trajectory can also be instantiated using all keyword arguments.\n\nProperties\n\nAll keyword arguments used in the instantiation are available as properties of the Trajectory. At a minimum, this includes initial_state, generator, target_state, and weight.\n\nBy convention, properties with a prop_ prefix, e.g., prop_method, will be taken into account when propagating the trajectory. See propagate_trajectory for details.\n\n\n\n\n\n","category":"type"},{"location":"externals/#Base.adjoint-Tuple{Trajectory}","page":"-","title":"Base.adjoint","text":"Construct the adjoint of a Trajectory.\n\nadj_trajectory = adjoint(trajectory)\n\nThe adjoint trajectory contains the adjoint of the dynamical generator traj.generator. All other fields contain a copy of the original field value.\n\nThe primary purpose of this adjoint is to facilitate the backward propagation under the adjoint generator that is central to gradient-based optimization methods such as GRAPE and Krotov's method.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.chain_callbacks-Tuple","page":"-","title":"QuantumControlBase.chain_callbacks","text":"Combine multiple callback functions.\n\nchain_callbacks(funcs...)\n\ncombines funcs into a single Function that can be passes as callback to ControlProblem or any optimize-function.\n\nEach function in func must be a suitable callback by itself. This means that it should receive the optimization workspace object as its first positional parameter, then positional parameters specific to the optimization method, and then an arbitrary number of data parameters. It must return either nothing or a tuple of \"info\" objects (which will end up in the records field of the optimization result).\n\nWhen chaining callbacks, the funcs will be called in series, and the \"info\" objects will be accumulated into a single result tuple. The combined results from previous funcs will be given to the subsequent funcs as data parameters. This allows for the callbacks in the chain to communicate.\n\nThe chain will return the final combined result tuple, or nothing if all funcs return nothing.\n\nnote: Note\nWhen calling optimize, any callback that is a tuple will be automatically processed with chain_callbacks. Thus, chain_callbacks rarely has to be invoked manually.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.check_amplitude-Tuple{Any}","page":"-","title":"QuantumControlBase.check_amplitude","text":"Check an amplitude in a Generator in the context of optimal control.\n\n@test check_amplitude(\n ampl; tlist, for_gradient_optimization=true, quiet=false\n)\n\nverifies that the given ampl is a valid element in the list of amplitudes of a Generator object. This checks all the conditions of QuantumPropagators.Interfaces.check_amplitude. In addition, the following conditions must be met.\n\nIf for_gradient_optimization:\n\nThe function get_control_deriv(ampl, control) must be defined\nIf ampl does not depend on control, get_control_deriv(ampl, control) must return 0.0\nIf ampl depends on control, get_control_deriv(ampl, control) must return an object u so that evaluate(u, tlist, n) returns a Number. In most cases, u itself will be a Number.\n\nThe function returns true for a valid amplitude and false for an invalid amplitude. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.check_generator-Tuple{Any}","page":"-","title":"QuantumControlBase.check_generator","text":"Check the dynamical generator in the context of optimal control.\n\n@test check_generator(\n generator; state, tlist,\n for_expval=true, for_pwc=true, for_time_continuous=false,\n for_parameterization=false, for_gradient_optimization=true,\n atol=1e-15, quiet=false\n)\n\nverifies the given generator. This checks all the conditions of QuantumPropagators.Interfaces.check_generator. In addition, the following conditions must be met.\n\nIf for_gradient_optimization:\n\nget_control_derivs(generator, controls) must be defined and return a vector containing the result of get_control_deriv(generator, control) for every control in controls.\nget_control_deriv(generator, control) must return an object that passes the less restrictive QuantumPropagators.Interfaces.check_generator if control is in get_controls(generator).\nget_control_deriv(generator, control) must return nothing if control is not in get_controls(generator)\nIf generator is a Generator instance, every ampl in generator.amplitudes must pass check_amplitude(ampl; tlist).\n\nThe function returns true for a valid generator and false for an invalid generator. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.get_control_deriv-Tuple{Function, Any}","page":"-","title":"QuantumControlBase.get_control_deriv","text":"a = get_control_deriv(ampl, control)\n\nreturns the derivative a_l(t)ϵ_l(t) of the given amplitude a_l(ϵ_l(t) t) with respect to the given control ϵ_l(t). For \"trivial\" amplitudes, where a_l(t) ϵ_l(t), the result with be either 1.0 or 0.0 (depending on whether ampl ≡ control). For non-trivial amplitudes, the result may be another amplitude that depends on the controls and potentially on time, but can be evaluated to a constant with evaluate.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.get_control_deriv-Tuple{Tuple, Any}","page":"-","title":"QuantumControlBase.get_control_deriv","text":"Get the derivative of the generator G w.r.t. the control ϵ(t).\n\nμ = get_control_deriv(generator, control)\n\nreturns nothing if the generator (Hamiltonian or Liouvillian) does not depend on control, or a generator\n\nμ = fracGϵ(t)\n\notherwise. For linear control terms, μ will be a static operator, e.g. an AbstractMatrix or an Operator. For non-linear controls, μ will be time-dependent, e.g. a Generator. In either case, evaluate should be used to evaluate μ into a constant operator for particular values of the controls and a particular point in time.\n\nFor constant generators, e.g. an Operator, the result is always nothing.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.get_control_derivs-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.get_control_derivs","text":"Get a vector of the derivatives of generator w.r.t. each control.\n\nget_control_derivs(generator, controls)\n\nreturn as vector containing the derivative of generator with respect to each control in controls. The elements of the vector are either nothing if generator does not depend on that particular control, or a function μ(α) that evaluates the derivative for a particular value of the control, see get_control_deriv.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.init_prop_trajectory-Tuple{Trajectory, Any}","page":"-","title":"QuantumControlBase.init_prop_trajectory","text":"Initialize a propagator for a given Trajectory.\n\npropagator = init_prop_trajectory(\n traj,\n tlist;\n initial_state=traj.initial_state,\n kwargs...\n)\n\ninitializes a Propagator for the propagation of the initial_state under the dynamics described by traj.generator.\n\nAll keyword arguments are forwarded to QuantumPropagators.init_prop, with default values from any property of traj with a prop_ prefix. That is, the keyword arguments for the underlying QuantumPropagators.init_prop are determined as follows:\n\nFor any property of traj whose name starts with the prefix prop_, strip the prefix and use that property as a keyword argument for init_prop. For example, if traj.prop_method is defined, method=traj.prop_method will be passed to init_prop. Similarly, traj.prop_inplace would be passed as inplace=traj.prop_inplace, etc.\nAny explicitly keyword argument to init_prop_trajectory overrides the values from the properties of traj.\n\nNote that the propagation method in particular must be specified, as it is a mandatory keyword argument in QuantumPropagators.propagate). Thus, either traj must have a property prop_method of the trajectory, or method must be given as an explicit keyword argument.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.make_chi-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.make_chi","text":"Return a function that calculates χ_k = -J_TΨ_k.\n\nchi = make_chi(\n J_T,\n trajectories;\n mode=:any,\n automatic=:default,\n via=(any(isnothing(t.target_state) for t in trajectories) ? :states : :tau),\n)\n\ncreates a function chi(Ψ, trajectories; τ) that returns a vector of states χ with χ_k = -J_TΨ_k, where Ψ_k is the k'th element of Ψ. These are the states used as the boundary condition for the backward propagation propagation in Krotov's method and GRAPE. Each χₖ is defined as a matrix calculus Wirtinger derivative,\n\nχ_k(T) = -fracJ_TΨ_k = -frac12 _Ψ_k J_Tqquad\n_Ψ_k J_T fracJ_TReΨ_k + i fracJ_TImΨ_k\n\nThe function J_T must take a vector of states Ψ and a vector of trajectories as positional parameters. If via=:tau, it must also a vector tau as a keyword argument, see e.g. J_T_sm). that contains the overlap of the states Ψ with the target states from the trajectories\n\nThe derivative can be calculated analytically of automatically (via automatic differentiation) depending on the value of mode. For mode=:any, an analytic derivative is returned if available, with a fallback to an automatic derivative.\n\nIf mode=:analytic, return an analytically known -J_TΨ_k, e.g.,\n\nQuantumControl.Functionals.J_T_sm → QuantumControl.Functionals.chi_sm,\nQuantumControl.Functionals.J_T_re → QuantumControl.Functionals.chi_re,\nQuantumControl.Functionals.J_T_ss → QuantumControl.Functionals.chi_ss.\n\nand throw an error if no analytic derivative is known.\n\nIf mode=:automatic, return an automatic derivative (even if an analytic derivative is known). The calculation of an automatic derivative (whether via mode=:any or mode=:automatic) requires that a suitable framework (e.g., Zygote or FiniteDifferences) has been loaded. The loaded module must be passed as automatic keyword argument. Alternatively, it can be registered as a default value for automatic by calling QuantumControl.set_default_ad_framework.\n\nWhen evaluating χ_k automatically, if via=:states is given , χ_k(T) is calculated directly as defined above from the gradient with respect to the states Ψ_k(T).\n\nIf via=:tau is given instead, the functional J_T is considered a function of overlaps τ_k = Ψ_k^tgtΨ_k(T). This requires that all trajectories define a target_state and that J_T calculates the value of the functional solely based on the values of tau passed as a keyword argument. With only the complex conjugate τ_k = Ψ_k(T)Ψ_k^tgt having an explicit dependency on Ψ_k(T), the chain rule in this case is\n\nχ_k(T)\n= -fracJ_TΨ_k\n= -left(\n fracJ_Tτ_k\n fracτ_kΨ_k\n right)\n= - frac12 (_τ_k J_T) Ψ_k^tgt\n\nAgain, we have used the definition of the Wirtinger derivatives,\n\nbeginalign*\n fracJ_Tτ_k\n frac12left(\n frac J_T Reτ_k\n - i frac J_T Imτ_k\n right)\n fracJ_Tτ_k\n frac12left(\n frac J_T Reτ_k\n + i frac J_T Imτ_k\n right)\nendalign*\n\nand the definition of the Zygote gradient with respect to a complex scalar,\n\n_τ_k J_T = left(\n frac J_T Reτ_k\n + i frac J_T Imτ_k\nright)\n\ntip: Tip\nIn order to extend make_chi with an analytic implementation for a new J_T function, define a new method make_analytic_chi like so:QuantumControlBase.make_analytic_chi(::typeof(J_T_sm), trajectories) = chi_smwhich links make_chi for QuantumControl.Functionals.J_T_sm to QuantumControl.Functionals.chi_sm.\n\nwarning: Warning\nZygote is notorious for being buggy (silently returning incorrect gradients). Always test automatic derivatives against finite differences and/or other automatic differentiation frameworks.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.make_grad_J_a-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.make_grad_J_a","text":"Return a function to evaluate J_aϵ_ln for a pulse value running cost.\n\ngrad_J_a! = make_grad_J_a(\n J_a,\n tlist;\n mode=:any,\n automatic=:default,\n)\n\nreturns a function so that grad_J_a!(∇J_a, pulsevals, tlist) sets J_aϵ_ln as the elements of the (vectorized) ∇J_a. The function J_a must have the interface J_a(pulsevals, tlist), see, e.g., J_a_fluence.\n\nThe parameters mode and automatic are handled as in make_chi, where mode is one of :any, :analytic, :automatic, and automatic is he loaded module of an automatic differentiation framework, where :default refers to the framework set with QuantumControl.set_default_ad_framework.\n\ntip: Tip\nIn order to extend make_grad_J_a with an analytic implementation for a new J_a function, define a new method make_analytic_grad_J_a like so:make_analytic_grad_J_a(::typeof(J_a_fluence), tlist) = grad_J_a_fluence!which links make_grad_J_a for J_a_fluence to grad_J_a_fluence!.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.make_print_iters-Tuple{Module}","page":"-","title":"QuantumControlBase.make_print_iters","text":"Construct a method-specific automatic callback for printing iter information.\n\nprint_iters = make_print_iters(Method; kwargs...)\n\nconstructs the automatic callback to be used by optimize(problem; method=Method, print_iters=true) to print information after each iteration. The keyword arguments are those used to instantiate problem and those explicitly passed to optimize.\n\nOptimization methods should implement make_print_iters(::Val{:Method}; kwargs...) where :Method is the name of the module/package implementing the method.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.optimize-Tuple{ControlProblem}","page":"-","title":"QuantumControlBase.optimize","text":"Optimize a quantum control problem.\n\nresult = optimize(\n problem;\n method, # mandatory keyword argument\n check=true,\n callback=nothing,\n print_iters=true,\n kwargs...\n)\n\noptimizes towards a solution of given problem with the given method, which should be a Module implementing the method, e.g.,\n\nusing Krotov\nresult = optimize(problem; method=Krotov)\n\nIf check is true (default), the initial_state and generator of each trajectory is checked with check_state and check_generator. Any other keyword argument temporarily overrides the corresponding keyword argument in problem. These arguments are available to the optimizer, see each optimization package's documentation for details.\n\nThe callback can be given as a function to be called after each iteration in order to analyze the progress of the optimization or to modify the state of the optimizer or the current controls. The signature of callback is method-specific, but callbacks should receive a workspace objects as the first parameter as the first argument, the iteration number as the second parameter, and then additional method-specific parameters.\n\nThe callback function may return a tuple of values, and an optimization method should store these values fore each iteration in a records field in their Result object. The callback should be called once with an iteration number of 0 before the first iteration. The callback can also be given as a tuple of vector of functions, which are automatically combined via chain_callbacks.\n\nIf print_iters is true (default), an automatic callback is created via the method-specific make_print_iters to print the progress of the optimization after each iteration. This automatic callback runs after any manually given callback.\n\nAll remaining keyword argument are method-specific. To obtain the documentation for which options a particular method uses, run, e.g.,\n\n? optimize(problem, ::Val{:Krotov})\n\nwhere :Krotov is the name of the module implementing the method. The above is also the method signature that a Module wishing to implement a control method must define.\n\nThe returned result object is specific to the optimization method.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.propagate_trajectories-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.propagate_trajectories","text":"Propagate multiple trajectories in parallel.\n\nresult = propagate_trajectories(\n trajectories, tlist; use_threads=true, kwargs...\n)\n\nruns propagate_trajectory for every trajectory in trajectories, collects and returns a vector of results. The propagation happens in parallel if use_threads=true (default). All keyword parameters are passed to propagate_trajectory, except that if initial_state is given, it must be a vector of initial states, one for each trajectory. Likewise, to pass pre-allocated storage arrays to storage, a vector of storage arrays must be passed. A simple storage=true will still work to return a vector of storage results.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.propagate_trajectory-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.propagate_trajectory","text":"Propagate a Trajectory.\n\npropagate_trajectory(\n traj,\n tlist;\n initial_state=traj.initial_state,\n kwargs...\n)\n\npropagates initial_state under the dynamics described by traj.generator. It takes the same keyword arguments as QuantumPropagators.propagate, with default values from any property of traj with a prop_ prefix (prop_method, prop_inplace, prop_callback, …). See init_prop_trajectory for details.\n\nNote that method (a mandatory keyword argument in QuantumPropagators.propagate) must be specified, either as a property prop_method of the trajectory, or by passing a method keyword argument explicitly.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.set_atexit_save_optimization-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.set_atexit_save_optimization","text":"Register a callback to dump a running optimization to disk on unexpected exit.\n\nA long-running optimization routine may use\n\nif !isnothing(atexit_filename)\n set_atexit_save_optimization(\n atexit_filename, result; msg_property=:message, msg=\"Abort: ATEXIT\"\n )\n # ...\n popfirst!(Base.atexit_hooks) # remove callback\nend\n\nto register a callback that writes the given result object to the given filename in JLD2 format in the event that the program terminates unexpectedly. The idea is to avoid data loss if the user presses CTRL-C in a non-interactive program (SIGINT), or if the process receives a SIGTERM from an HPC scheduler because the process has reached its allocated runtime limit. Note that the callback cannot protect against data loss in all possible scenarios, e.g., a SIGKILL will terminate the program without giving the callback a chance to run (as will yanking the power cord).\n\nAs in the above example, the optimization routine should make set_atexit_save_optimization conditional on an atexit_filename keyword argument, which is what QuantumControl.@optimize_or_load will pass to the optimization routine. The optimization routine must remove the callback from Base.atexit_hooks when it exits normally. Note that in an interactive context, CTRL-C will throw an InterruptException, but not cause a shutdown. Optimization routines that want to prevent data loss in this situation should handle the InterruptException and return result, in addition to using set_atexit_save_optimization.\n\nIf msg_property is not nothing, the given msg string will be stored in the corresponding property of the (mutable) result object before it is written out.\n\nThe resulting JLD2 file is compatible with QuantumControl.load_optimization.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.taus!-Tuple{Vector{ComplexF64}, Any, Any}","page":"-","title":"QuantumControlBase.taus!","text":"Overlaps of target states with propagates states, calculated in-place.\n\ntaus!(τ, Ψ, trajectories; ignore_missing_target_state=false)\n\noverwrites the complex vector τ with the results of taus(Ψ, trajectories).\n\nThrows an ArgumentError if any of trajectories have a target_state of nothing. If ignore_missing_target_state=true, values in τ instead will remain unchanged for any trajectories with a missing target state.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.taus-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.taus","text":"Overlaps of target states with propagates states\n\nτ = taus(Ψ, trajectories)\n\ncalculates a vector of values τ_k = Ψ_k^tgtΨ_k where Ψ_k^tgt is the traj.target_state of the k'th element of trajectories and Ψₖ is the k'th element of Ψ.\n\nThe definition of the τ values with Ψ_k^tgt on the left (overlap of target states with propagated states, as opposed to overlap of propagated states with target states) matches Refs. [4] and [7].\n\nThe function requires that each trajectory defines a target state. See also taus! for an in-place version that includes well-defined error handling for any trajectories whose target_state property is nothing.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_controls-Tuple{ControlProblem}","page":"-","title":"QuantumPropagators.Controls.get_controls","text":"controls = get_controls(problem)\n\nextracts the controls from problem.trajectories.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_controls-Tuple{Vector{<:Trajectory}}","page":"-","title":"QuantumPropagators.Controls.get_controls","text":"controls = get_controls(trajectories)\n\nextracts the controls from a list of trajectories (i.e., from each trajectory's generator). Controls that occur multiple times in the different trajectories will occur only once in the result.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_parameters-Tuple{ControlProblem}","page":"-","title":"QuantumPropagators.Controls.get_parameters","text":"parameters = get_parameters(problem)\n\nextracts the parameters from problem.trajectories.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_parameters-Tuple{Vector{<:Trajectory}}","page":"-","title":"QuantumPropagators.Controls.get_parameters","text":"parameters = get_parameters(trajectories)\n\ncollects and combines get parameter arrays from all the generators in trajectories. Note that this allows any custom generator type to define a custom get_parameters method to override the default of obtaining the parameters recursively from the controls inside the generator.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.substitute-Tuple{ControlProblem, Any}","page":"-","title":"QuantumPropagators.Controls.substitute","text":"problem = substitute(problem::ControlProblem, replacements)\n\nsubstitutes in problem.trajectories\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.substitute-Tuple{Trajectory, Any}","page":"-","title":"QuantumPropagators.Controls.substitute","text":"trajectory = substitute(trajectory::Trajectory, replacements)\ntrajectories = substitute(trajectories::Vector{<:Trajectory}, replacements)\n\nrecursively substitutes the initial_state, generator, and target_state.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumControlBase.@threadsif-Tuple{Any, Any}","page":"-","title":"QuantumControlBase.@threadsif","text":"Conditionally apply multi-threading to for loops.\n\nThis is a variation on Base.Threads.@threads that adds a run-time boolean flag to enable or disable threading. It is intended for internal use in packages building on QuantumControlBase.\n\nUsage:\n\nusing QuantumControlBase: @threadsif\n\nfunction optimize(trajectories; use_threads=true)\n @threadsif use_threads for k = 1:length(trajectories)\n # ...\n end\nend\n\n\n\n\n\n","category":"macro"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators]","category":"page"},{"location":"externals/#QuantumPropagators.AbstractPropagator","page":"-","title":"QuantumPropagators.AbstractPropagator","text":"Abstract base type for all Propagator objects.\n\nAll Propagator objects must be instantiated via init_prop and implement the following interface.\n\nProperties\n\nstate (read-only): The current quantum state in the propagation\ntlist (read-only): The time grid for the propagation\nt (read-only): The time at which state is defined. An element of tlist.\nparameters: parameters that determine the dynamics. The structure of the parameters depends on the concrete Propagator type (i.e., the propagation method). Mutating the parameters affects subsequent propagation steps.\nbackward: Boolean flag to indicate whether the propagation moves forward or backward in time\ninplace: Boolean flag to indicate whether propagator.state is modified in-place or is recreated by every call of prop_step! or set_state!. With inplace=false, the propagator should generally avoid in-place operations, such as calls to QuantumPropagators.Controls.evaluate!.\n\nConcrete Propagator types may have additional properties or fields, but these should be considered private.\n\nMethods\n\nreinit_prop! — reset the propagator to a new initial state at the beginning of the time grid (or the end, for backward propagation)\nprop_step! – advance the propagator by one step forward (or backward) on the time grid.\nset_state! — safely mutate the current quantum state of the propagation. Note that directly mutating the state property is not safe. However, Ψ = propagator.state; foo_mutate!(Ψ), set_state!(propagator, Ψ) for some mutating function foo_mutate! is guaranteed to be safe and efficient for both in-place and not-in-place propagators.\nset_t! — safely mutate the current time (propagator.t), snapping to the values of tlist.\n\nSee also\n\nPiecewisePropagator — specialization of AbstractPropagator for piecewise propagation methods.\nPWCPropagator — specialization of PiecewisePropagator for piecewise-constant propagation methods.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.ChebyPropagator","page":"-","title":"QuantumPropagators.ChebyPropagator","text":"Propagator for Chebychev propagation (method=QuantumPropagators.Cheby).\n\nThis is a PWCPropagator.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.ExpPropagator","page":"-","title":"QuantumPropagators.ExpPropagator","text":"Propagator for propagation via direct exponentiation (method=QuantumPropagators.ExpProp)\n\nThis is a PWCPropagator.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.NewtonPropagator","page":"-","title":"QuantumPropagators.NewtonPropagator","text":"Propagator for Newton propagation (method=QuantumPropagators.Newton).\n\nThis is a PWCPropagator.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.PWCPropagator","page":"-","title":"QuantumPropagators.PWCPropagator","text":"PiecewisePropagator sub-type for piecewise-constant propagators.\n\nLike the more general PiecewisePropagator, this is characterized by propagator.parameters mapping the controls in the generator to a vector of amplitude value on the midpoints of the time grid intervals.\n\nThe propagation will use these values as constant within each interval.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.PiecewisePropagator","page":"-","title":"QuantumPropagators.PiecewisePropagator","text":"AbstractPropagator sub-type for piecewise propagators.\n\nA piecewise propagator is determined by a single parameter per control and time grid interval. Consequently, the propagator.parameters are a dictionary mapping the controls found in the generator via get_controls to a vector of values defined on the intervals of the time grid, see discretize_on_midpoints. This does not necessarily imply that these values are the piecewise-constant amplitudes for the intervals. A general piecewise propagator might use interpolation to obtain actual amplitudes within any given time interval.\n\nWhen the amplitudes are piecewise-constant, the propagator should be a concrete instantiation of a PWCPropagator.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Propagation","page":"-","title":"QuantumPropagators.Propagation","text":"Wrapper around the parameters of a call to propagate.\n\nPropagation(\n generator, tlist;\n pre_propagation=nothing, post_propagation=nothing,\n kwargs...\n)\n\nPropagation(\n propagator;\n pre_propagation=nothing, post_propagation=nothing,\n kwargs...\n)\n\nis a wrapper around the arguments for propagate / init_prop, for use within propagate_sequence.\n\nThe positional and keyword arguments are those accepted by the above propagation routines, excluding the initial state. A Propagation may in addition include the pre_propagation and post_propagation keyword arguments recognized by propagate_sequence.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.cheby_get_spectral_envelope-NTuple{4, Any}","page":"-","title":"QuantumPropagators.cheby_get_spectral_envelope","text":"Determine the spectral envelope of a generator.\n\nE_min, E_max = cheby_get_spectral_envelope(\n generator, tlist, control_ranges, method; kwargs...\n)\n\nestimates a lower bound E_min the lowest eigenvalue of the generator for any values of the controls specified by control_ranges, and an upper bound E_max for the highest eigenvalue.\n\nThis is done by constructing operators from the extremal values for the controls as specified in control_ranges and taking the smallest/largest return values from specrange for those operators.\n\nArguments\n\ngenerator: dynamical generator, e.g. a time-dependent\ntlist: The time grid for the propagation\ncontrol_ranges: a dict that maps controls that occur in generator (cf. get_controls to a tuple of minimum and maximum amplitude for that control\nmethod: method name to pass to specrange\nkwargs: Any remaining keyword arguments are passed to specrange\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.disable_timings-Tuple{}","page":"-","title":"QuantumPropagators.disable_timings","text":"Disable the collection of TimerOutputs data.\n\nQuantumPropagators.disable_timings()\n\ndisables the collection of timing data previously enabled with enable_timings. This triggers recompilation to completely remove profiling from the code. That is, there is zero cost when the collection of timing data is disabled.\n\nReturns QuantumPropagators.timings_enabled(), i.e., false if successful.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.enable_timings-Tuple{}","page":"-","title":"QuantumPropagators.enable_timings","text":"Enable the collection of TimerOutputs data.\n\nQuantumPropagators.enable_timings()\n\nenables certain portions of the package to collect TimerOutputs internally. This aids in profiling and benchmarking propagation methods.\n\nSpecifically, after enable_timings(), for any ChebyPropagator or NewtonPropagator, timing data will become available in propagator.wrk.timing_data (as a TimerOutput instance). This data is reset when the propagator is re-instantiated with init_prop or re-initialized with reinit_prop!. This makes the data local to any call of propagate.\n\nNote that enable_timings() triggers recompilation, so propagate should be called at least twice to avoid compilation overhead in the timing data. There is still a small overhead for collecting the timing data.\n\nThe collection of timing data can be disabled again with disable_timings.\n\nReturns QuantumPropagators.timings_enabled(), i.e., true if successful.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.init_prop-Tuple{Any, Any, Any, Val{:Cheby}}","page":"-","title":"QuantumPropagators.init_prop","text":"using QuantumPropagators: Cheby\n\ncheby_propagator = init_prop(\n state,\n generator,\n tlist;\n method=Cheby,\n inplace=QuantumPropagators.Interfaces.supports_inplace(state),\n backward=false,\n verbose=false,\n parameters=nothing,\n control_ranges=nothing,\n specrange_method=:auto,\n specrange_buffer=0.01,\n cheby_coeffs_limit=1e-12,\n check_normalization=false,\n specrange_kwargs...\n)\n\ninitializes a ChebyPropagator.\n\nMethod-specific keyword arguments\n\ncontrol_ranges: a dict the maps the controls in generator (see get_controls) to a tuple of min/max values. The Chebychev coefficients will be calculated based on a spectral envelope that assumes that each control can take arbitrary values within the min/max range. If not given, the ranges are determined automatically. Specifying manual control ranges can be useful when the the control amplitudes (parameters) may change during the propagation, e.g. in a sequential-update control scheme.\nspecrange_method: Method to pass to the specrange function\nspecrange_buffer: An additional factor by which to enlarge the estimated spectral range returned by specrange, in order to ensure that Chebychev coefficients are based on an overestimation of the spectral range.\ncheby_coeffs_limit: The maximum magnitude of Chebychev coefficients that should be treated as non-zero\ncheck_normalization: Check whether the Hamiltonian has been properly normalized, i.e., that the spectral range of generator has not been underestimated. This slowes down the propagation, but is advisable for novel generators.\nuniform_dt_tolerance=1e-12: How much the intervals of tlist are allowed to vary while still being considered constant.\nspecrange_kwargs: All further keyword arguments are passed to the specrange function. Most notably, with the default specrange_method=:auto (or specrange_method=:manual), passing E_min and E_max allows to manually specify the spectral range of generator.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.init_prop-Tuple{Any, Any, Any, Val{:ExpProp}}","page":"-","title":"QuantumPropagators.init_prop","text":"using QuantumPropagators: ExpProp\n\nexp_propagator = init_prop(\n state,\n generator,\n tlist;\n method=ExpProp,\n inplace=QuantumPropagators.Interfaces.supports_inplace(state),\n backward=false,\n verbose=false,\n parameters=nothing,\n func=(H_dt -> exp(-1im * H_dt))\n convert_state=_exp_prop_convert_state(state),\n convert_operator=_exp_prop_convert_operator(generator),\n _...\n)\n\ninitializes an ExpPropagator.\n\nMethod-specific keyword arguments\n\nfunc: The function to evaluate. The argument H_dt is obtained by constructing an operator H from generator via the evaluate function and the multiplied with the time step dt for the current time interval. The propagation then simply multiplies the return value of func with the current state\nconvert_state: Type to which to temporarily convert states before multiplying the return value of func.\nconvert_operator: Type to which to convert the operator H before multiplying it with dt and plugging the result into func\n\nThe convert_state and convert_operator parameters are useful for when the generator and or state are unusual data structures for which the relevant methods to calculate func are not defined. Often, it is easier to temporarily convert them to standard complex matrices and vectors than to implement the missing methods.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.init_prop-Tuple{Any, Any, Any, Val{:Newton}}","page":"-","title":"QuantumPropagators.init_prop","text":"using QuantumPropagators: Newton\n\nnewton_propagator = init_prop(\n state,\n generator,\n tlist;\n method=Newton,\n inplace=QuantumPropagators.Interfaces.supports_inplace(state),\n backward=false,\n verbose=false,\n parameters=nothing,\n m_max=10,\n func=(z -> exp(-1im * z)),\n norm_min=1e-14,\n relerr=1e-12,\n max_restarts=50,\n _...\n)\n\ninitializes a NewtonPropagator.\n\nMethod-specific keyword arguments\n\nm_max: maximum Krylov dimension, cf. NewtonWrk\nfunc, norm_min, relerr, max_restarts: parameter to pass to newton!\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.init_prop-Tuple{Any, Any, Any}","page":"-","title":"QuantumPropagators.init_prop","text":"Initialize a Propagator.\n\npropagator = init_prop(\n state, generator, tlist;\n method, # mandatory keyword argument\n backward=false,\n inplace=QuantumPropagators.Interfaces.supports_inplace(state),\n piecewise=nothing,\n pwc=nothing,\n kwargs...\n)\n\ninitializes a propagator for the time propagation of the given state over a time grid tlist under the time-dependent generator (Hamiltonian/Liouvillian) generator.\n\nArguments\n\nstate: The \"initial\" state for the propagation. For backward=false, this state is taken to be at initial time (tlist[begin]); and for backward=true, at the final time (tlist[end])\ngenerator: The time-dependent generator of the dynamics\ntlist: The time grid over which which the propagation is defined. This may or may not be equidistant.\n\nMandatory keyword arguments\n\nmethod: The propagation method to use. May be given as a name (Symbol), but the recommended usage is to pass a module implementing the propagation method, e.g., using QuantumPropagators: Cheby; method = Cheby. Passing a module ensures that the code implementing the method is correctly loaded. This is particularly important for propagators using third-party backends, like with method=OrdinaryDiffEq.\n\nOptional keyword arguments\n\nbackward: If true, initialize the propagator for a backward propagation. The resulting propagator.t will be tlist[end], and subsequent calls to prop_step! will move backward on tlist.\ninplace: If true, the state property of the resulting propagator will be changed in-place by any call to prop_step!. If false, each call to prop_step! changes the reference for propagator.state, and the propagation will not use any in-place operations. Not all propagation methods may support both in-place and not-in-place propagation. In-place propagation is generally more efficient for larger Hilbert space dimensions, but may not be compatible, e.g., with automatic differentiation.\npiecewise: If given as a boolean, true enforces that the resulting propagator is a PiecewisePropagator, and false enforces that it not a PiecewisePropagator. For the default piecewise=nothing, whatever type of propagation is the default for the given method will be used. Throw an error if the given method does not support the required type of propagation.\npwc: Like piecewise, but for the stronger PWCPropagator.\n\nAll other kwargs are method-dependent and are ignored for methods that do not support them.\n\nThe type of the returned propagator is a sub-type of AbstractPropagator, respectively a sub-type of PiecewisePropagator if piecewise=true or a sub-type of PWCPropagator if pwc=true.\n\nInternals\n\nInternally, the (mandatory) keyword method is converted into a fourth positional argument. This allows propagation methods to define their own implementation of init_prop via multiple dispatch. However, when calling init_prop in high-level code, method must always be given as a keyword argument.\n\nSee also\n\nreinit_prop! — Re-initialize a propagator\npropagate — Higher-level propagation interface\n`check_propagator — a function to verify the interface described above.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.ode_function-Union{Tuple{GT}, Tuple{GT, Any}} where GT","page":"-","title":"QuantumPropagators.ode_function","text":"Wrap around a Generator, for use as an ODE function.\n\nf = ode_function(generator, tlist; c=-1im)\n\ncreates a function suitable to be passed to ODEProblem.\n\ngdefop1hat1\ngdefket1vert1rangle\n\nWith generator corresponding to opH(t), this implicitly encodes the ODE\n\nfracpartialpartial t ketPsi(t) = c opH(t) ketPsi(t)\n\nfor the state ketPsi(t). With the default c = -i, this corresponds to the Schrödinger equation, or the Liouville equation with convention=:LvN.\n\nThe resulting f works both in-place and not-in-place, as\n\nf(ϕ, Ψ, vals_dict, t) # in-place `f(du, u, p, t)`\nϕ = f(Ψ, vals_dict, t) # not-in-place `f(u, p, t)`\n\nCalling f as above is functionally equivalent to calling evaluate to obtain an operator H from the original time-dependent generator, and then applying H to the current quantum state Ψ:\n\nH = evaluate(f.generator, t; vals_dict=vals_dict)\nϕ = c * H * Ψ\n\nwhere vals_dict may be a dictionary mapping controls to values (set as the parameters p of the underlying ODE solver).\n\nIf QuantumPropagators.enable_timings() has been called, profiling data is collected in f.timing_data.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.prop_step!","page":"-","title":"QuantumPropagators.prop_step!","text":"Advance the propagator by a single time step.\n\nstate = prop_step!(propagator)\n\nreturns the state obtained from propagating to the next point on the time grid from propagator.t, respectively the previous point if propagator.backward is true.\n\nWhen the propagation would lead out of the time grid, prop_step! leaves propagator unchanged and returns nothing. Thus, a return value of nothing may be used to signal that a propagation has completed.\n\n\n\n\n\n","category":"function"},{"location":"externals/#QuantumPropagators.propagate-Tuple{Any, Any, Any}","page":"-","title":"QuantumPropagators.propagate","text":"Propagate a state over an entire time grid.\n\nstate = propagate(\n state,\n generator,\n tlist;\n method, # mandatory keyword argument\n check=true,\n backward=false,\n inplace=QuantumPropagators.Interfaces.supports_inplace(state),\n verbose=false,\n piecewise=nothing,\n pwc=nothing,\n storage=nothing,\n observables=,\n callback=nothing,\n show_progress=false,\n init_prop_kwargs...)\n\npropagates state of the entire time grid and returns the propagated states, or a storage array of data collected during the propagation. This high-level routine performs the following three steps:\n\nIf check=true (default), check that state, generator, and tlist are consistent with the required interface.\nInitialize a propagator via init_prop:\ninit_prop(state, generator, tlist; method, inplace, init_prop_kwargs...)\nCall and return the result of\npropagate(propagator; storage, observables, show_progress, callback)\n\nArguments\n\nstate: The \"initial\" state for the propagation. For backward=false, this state is taken to be at initial time (tlist[begin]); and for backward=true, at the final time (tlist[end])\ngenerator: The time-dependent generator of the dynamics\ntlist: The time grid over which which the propagation is defined. This may or may not be equidistant.\n\nMandatory keyword arguments\n\nmethod: The propagation method to use. May be given as a name (Symbol), but the recommended usage is to pass a module implementing the propagation method, cf. init_prop.\n\nOptional keyword arguments\n\ncheck: if true, check that state, generator, and tlist pass check_state, check_generator and check_tlist, respectively.\nbackward: If true, propagate backward in time\ninplace: If true, propagate using in-place operations. If false, avoid in-place operations. Not all propagation methods support both in-place and not-in-place propagation. Note that inplace=true requires that QuantumPropagators.Interfaces.supports_inplace for state is true.\npiecewise: If given as a boolean, ensure that the internal propagator is an instance of PiecewisePropagator, cf. init_prop.\npwc: If given a a boolean, do a piecewise constant propagation where the generator in each interval is constant (the internal propagator is a PWCPropagator, cf. init_prop)\nstorage: Flag whether to store and return the propagated states / observables, or pre-allocated storage array. See Notes below.\nobservables: Converters for data to be stored in storage. See Notes below.\ncallback: Function to call after each propagation step. See Notes below.\nshow_progress: Whether to show a progress bar. See Notes below.\n\nAll remaining keyword arguments are passed to init_prop to initialize the Propagator that is used internally to drive the optimization. Unknown keyword arguments will be ignored.\n\nNotes\n\nIn general, there is no requirement that tlist has a constant time step, although some propagation methods (most notably Cheby) only support a uniform time grid.\n\nIf storage is given as a container pre-allocated via init_storage, it will be filled with data determined by the observables. Specifically, after each propagation step,\n\ndata = map_observables(observables, tlist, i, state)\nwrite_to_storage!(storage, i, data)\n\nis executed, where state is defined at time tlist[i]. See map_observables and write_to_storage! for details. The default values for observables results simply in the propagated states at every point in time being stored.\n\nThe storage parameter may also be given as true, and a new storage array will be created internally with init_storage and returned instead of the propagated state:\n\ndata = propagate(\n state, generator, tlist; method,\n backward=false; storage=true, observables=observables,\n callback=nothing, show_progress=false, init_prop_kwargs...)\n\nIf backward is true, the input state is assumed to be at time tlist[end], and the propagation progresses backward in time (with a negative time step dt). If storage is given, it will be filled back-to-front during the backward propagation.\n\nIf callback is given as a callable, it will be called after each propagation step, as callback(propagator, observables) where propagator is Propagator object driving the propagation. The callback is called before calculating any observables. Example usage includes writing data to file, or modifying state via set_state!, e.g., removing amplitude from the lowest and highest level to mitigate \"truncation error\".\n\nIf show_progress is given as true, a progress bar will be shown for long-running propagation. In order to customize the progress bar, show_progress may also be a function that receives length(tlist) and returns a ProgressMeter.Progress instance.\n\nIf in_place=false is given, the propagation avoids in-place operations. This is slower than inplace=true, but is often required in the context of automatic differentiation (AD), e.g., with Zygote. That is, use in_place=false if propagate is called inside a function to be passed to Zygote.gradient, Zygote.pullback, or a similar function. In an AD context, storage and show_progress should not be used.\n\nThe propagate routine returns the propagated state at tlist[end], respectively tlist[1] if backward=true, or a storage array with the stored states / observable data if storage=true.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.propagate-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.propagate","text":"state = propagate(\n state,\n propagator;\n storage=nothing,\n observables=,\n show_progress=false,\n callback=nothing,\n reinit_prop_kwargs...\n)\n\nre-initializes the given propagator with state (see reinit_prop!) and then calls the lower-level propagate(propagator; ...).\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.propagate-Tuple{Any}","page":"-","title":"QuantumPropagators.propagate","text":"state = propagate(\n propagator;\n storage=nothing,\n observables=,\n show_progress=false,\n callback=nothing,\n)\n\npropagates a freshly initialized propagator (immediately after init_prop). Used in the higher-level propagate(state, generator, tlist; kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.propagate_sequence-Tuple{Any, Vector{Propagation}}","page":"-","title":"QuantumPropagators.propagate_sequence","text":"Propagate a state through a sequence of generators.\n\nstates = propagate_sequence(\n state,\n propagations;\n storage=nothing,\n pre_propagation=nothing,\n post_propagation=nothing,\n kwargs...\n)\n\ntakes an initial state and performs a sequence of propagate calls using the parameters in propagations. The initial state for each step in the sequence is the state resulting from the previous step. Optionally, before and after each step, a pre_propagation and post_propagation function may modify the state instantaneously, e.g., to perform a frame transformation. Return the vector of states at the end of each step (after any post_propagation, before any next pre_propagation of the next step).\n\nArguments\n\nstate: The initial state\npropagations: A vector of Propagation instances, one per step in the sequence, each containing the arguments for the call to propagate for that step. The Propagation contains the generator and time grid for each step as positional parameters, or alternatively a pre-initialized Propagator, and any keyword arguments for propagate that are specific to that step. Note that propagate keyword arguments that are common to all steps can be given directly to propagate_sequence.\nstorage: If storage=true, return a vector of storage objects as returned by propagate(…, storage=true) for each propagation step, instead of the state after each step. To use a pre-initialized storage, each Propagation in propagations should have a storage keyword argument instead.\npre_propagation: If not nothing, must be a function that receives the same arguments as propagate and returns a state. Called immediately before the propagate of each step, and the state returned by pre_propagation will become the initial state for the subsequent call to propagate. Generally, pre_propagation would be different in each step of the sequence, and should be given as a keyword argument in a particular Propagation.\npost_propagation: If not nothing, a function that receives the same arguments as propagate and returns a state, see pre_propagation. The returned state becomes the initial state for the next step in the sequence (and may be further processed by the following pre_propagation). Like pre_propagation, this will generally be set as a keyword argument for a particular Propagation, not as a global keyword argument to propagate_sequence.\n\nAll other keyword arguments are forwarded to propagate. Thus, keyword arguments that are common to all steps in the sequence should be given as keyword arguments to propagate_sequence directly.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.reinit_prop!-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.reinit_prop!","text":"Re-initialize a propagator.\n\nreinit_prop!(propagator, state; kwargs...)\n\nresets the propagator to state at the beginning of the time grid, respectively the end of the time grid if propagator.backward is true.\n\nAt a minimum, this is equivalent to a call to set_state! follow by a call to set_t!, but some propagators may have additional requirements on re-initialization, such as refreshing expansion coefficients for ChebyPropagator. In this case, the kwargs may be additional keyword arguments specific to the concrete type of propagator.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.reinit_prop!-Tuple{QuantumPropagators.ChebyPropagator, Any}","page":"-","title":"QuantumPropagators.reinit_prop!","text":"reinit_prop!(\n propagator::ChebyPropagator,\n state;\n transform_control_ranges=((c, ϵ_min, ϵ_max, check) => (ϵ_min, ϵ_max)),\n kwargs...\n)\n\nre-initializes an existing ChebyPropagator. This may or may not involve recalculating the Chebychev coefficients based on the current control amplitudes in propagator.parameters.\n\nMethod-specific keyword arguments\n\ntransform_control_ranges: a function (c, ϵ_min, ϵ_max, check) => (ϵ_min′, ϵ_max′). For each control c, the function is called with check=true and ϵ_min (ϵ_max) the current minimum (maximum) values for the control from propagator.parameters). The Chebychev coefficients will be recalculated if the existing coefficients were obtained assuming a range for c outside the returned ϵ_min′, ϵ_max′.\nIf the coefficients do need to be recalculated, transform_control_ranges is called a second time with check=false, and the returned (ϵ_min′, ϵ_max′) are used for estimating the new spectral range.\nFor example,\nfunction transform_control_ranges(c, ϵ_min, ϵ_max, check)\n if check\n return (min(ϵ_min, 2 * ϵ_min), max(ϵ_max, 2 * ϵ_max))\n else\n return (min(ϵ_min, 5 * ϵ_min), max(ϵ_max, 5 * ϵ_max))\n end\nend\nwill re-calculate the Chebychev coefficients only if the current amplitudes differ by more than a factor of two from the ranges that were used when initializing the propagator (control_ranges parameter in init_prop, which would have had to overestimate the actual amplitudes by at least a factor of two). When re-calculating, the control_ranges will overestimate the amplitudes by a factor of five. With this transform_control_ranges, the propagation will be stable as long as the amplitudes do not change dynamically by more than a factor of 2.5 from their original range, while also not re-calculating coefficients unnecessarily in each pass because of modest changes in the amplitudes.\nThe transform_control_ranges argument is only relevant in the context of optimal control, where the same propagator will be used for many iterations with changing control field amplitudes.\n\nAll other keyword arguments are ignored.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.set_state!-Tuple{QuantumPropagators.AbstractPropagator, Any}","page":"-","title":"QuantumPropagators.set_state!","text":"Set the current state of the propagator.\n\nset_state!(propagator, state)\n\nsets the propagator.state property and returns propagator.state. In order to mutate the current state after a call to prop_step!, the following pattern is recommended:\n\nΨ = propagator.state\nfoo_mutate!(Ψ)\nset_state!(propagator, Ψ)\n\nwhere foo_mutate! is some function that mutates Ψ. This is guaranteed to work efficiently both for in-place and not-in-place propagators, without incurring unnecessary copies.\n\nwarning: Warning\nfoo_mutate!(propagator.state)by itself is not a safe operation. Always follow it byset_state!(propagator, propagator.state)\n\nSee also\n\nset_t! — set propagator.t.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.set_t!-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.set_t!","text":"Set the current time for the propagation.\n\nset_t!(propagator, t)\n\nSets propagator.t to the given value of t, where t must be an element of propagator.tlist.\n\nSee also\n\nset_state! — set propagator.state\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.timings_enabled-Tuple{}","page":"-","title":"QuantumPropagators.timings_enabled","text":"Check whether the collection of TimerOutputs data is active.\n\nQuantumPropagators.timings_enabled()\n\nreturns true if QuantumPropagators.enable_timings() was called, and false otherwise or after QuantumPropagators.disable_timings().\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Generators]","category":"page"},{"location":"externals/#QuantumPropagators.Generators.Generator","page":"-","title":"QuantumPropagators.Generators.Generator","text":"A time-dependent generator.\n\nGenerator(ops::Vector{OT}, amplitudes::Vector{AT})\n\nproduces an object of type Generator{OT,AT} that represents\n\nH(t)= H_0 + sum_l a_l(ϵ_l(t) t) H_l\n\nwhere H_l are the ops and a_l(t) are the amplitudes. H(t) and H_l may represent operators in Hilbert space or super-operators in Liouville space. If the number of amplitudes is less than the number of ops, the first ops are considered as drift terms (H_0, respectively subsequent terms with a_l 1). At least one time-dependent amplitude is required. Each amplitude may depend on one or more control functions ϵ_l(t), although most typically a_l(t) ϵ_l(t), that is, the amplitudes are simply a vector of the controls. See hamiltonian for details.\n\nA Generator object should generally not be instantiated directly, but via hamiltonian or liouvillian.\n\nThe list of ops and amplitudes are properties of the Generator. They should not be mutated.\n\nSee also\n\nOperator for static generators, which may be obtained from a Generator via evaluate.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Generators.Operator","page":"-","title":"QuantumPropagators.Generators.Operator","text":"A static operator in Hilbert or Liouville space.\n\nOperator(ops::Vector{OT}, coeffs::Vector{CT})\n\nproduces an object of type Operator{OT,CT} that encapsulates the \"lazy\" sum\n\nH = sum_l c_l H_l\n\nwhere H_l are the ops and c_l are the coeffs, which each must be a constant Number. If the number of coefficients is less than the number of operators, the first ops are considered to have c_l = 1.\n\nAn Operator object would generally not be instantiated directly, but be obtained from a Generator via evaluate.\n\nThe H_l in the sum are considered immutable. This implies that an Operator can be updated in-place with evaluate! by only changing the coeffs.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Generators.ScaledOperator","page":"-","title":"QuantumPropagators.Generators.ScaledOperator","text":"A static operator with a scalar pre-factor.\n\nop = ScaledOperator(α, Ĥ)\n\nrepresents the \"lazy\" product α H where H is an operator (typically an Operator instance) and α is a scalar.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Generators.hamiltonian-Tuple","page":"-","title":"QuantumPropagators.Generators.hamiltonian","text":"Initialize a (usually time-dependent) Hamiltonian.\n\nThe most common usage is, e.g.,\n\nusing QuantumPropagators\n\nH₀ = ComplexF64[0 0; 0 1];\nH₁ = ComplexF64[0 1; 1 0];\nϵ₁(t) = 1.0;\n\nhamiltonian(H₀, (H₀, ϵ₁))\n\n# output\n\nGenerator with 2 ops and 1 amplitudes\n ops::Vector{Matrix{ComplexF64}}:\n ComplexF64[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]\n ComplexF64[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]\n amplitudes::Vector{typeof(ϵ₁)}:\n ϵ₁\n\nIn general,\n\nH = hamiltonian(terms...; check=true)\n\nconstructs a Hamiltonian based on the given terms. Each term must be an operator or a tuple (op, ampl) of an operator and a control amplitude. Single operators are considered \"drift\" terms.\n\nIn most cases, each control amplitude will simply be a control function or vector of pulse values. In general, ampl can be an arbitrary object that depends on one or more controls, which must be obtainable via get_controls(ampl). See QuantumPropagators.Interfaces.check_amplitude for the required interface.\n\nThe hamiltonian function will generally return a Generator instance. However, if none of the given terms are time-dependent, it may return a static operator (e.g., an AbstractMatrix or Operator):\n\nhamiltonian(H₀)\n# output\n2×2 Matrix{ComplexF64}:\n 0.0+0.0im 0.0+0.0im\n 0.0+0.0im 1.0+0.0im\n\nhamiltonian(H₀, (H₁, 2.0))\n# output\nOperator with 2 ops and 1 coeffs\n ops::Vector{Matrix{ComplexF64}}:\n ComplexF64[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]\n ComplexF64[0.0 + 0.0im 1.0 + 0.0im; 1.0 + 0.0im 0.0 + 0.0im]\n coeffs: [2.0]\n\nThe hamiltonian function may generate warnings if the terms are of an unexpected type or structure. These can be suppressed with check=false.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Generators.liouvillian","page":"-","title":"QuantumPropagators.Generators.liouvillian","text":"Construct a Liouvillian Generator.\n\nℒ = liouvillian(Ĥ, c_ops=(); convention=:LvN, check=true)\n\ncalculates the sparse Liouvillian super-operator ℒ from the Hamiltonian Ĥ and a list c_ops of Lindblad operators.\n\nWith convention=:LvN, applying the resulting ℒ to a vectorized density matrix ρ⃗ calculates fracddt vecrho(t) = ℒ vecrho(t) equivalent to the Liouville-von-Neumann equation for the density matrix ρ,\n\nfracddt ρ(t)\n= -i H ρ(t) + sum_kleft(\n A_k ρ A_k^dagger\n - frac12 A_k^dagger A_k ρ\n - frac12 ρ A_k^dagger A_k\n right)\n\nwhere the Lindblad operators A_k are the elements of c_ops.\n\nThe Hamiltonian H will generally be time-dependent. For example, it may be a Generator as returned by hamiltonian. For example, for a Hamiltonian with the terms (Ĥ₀, (Ĥ₁, ϵ₁), (Ĥ₂, ϵ₂)), where Ĥ₀, Ĥ₁, Ĥ₂ are matrices, and ϵ₁ and ϵ₂ are functions of time, the resulting ℒ will be a Generator corresponding to terms (ℒ₀, (ℒ₁, ϵ₁), (ℒ₂, ϵ₂)), where the initial terms is the superoperator ℒ₀ for the static component of the Liouvillian, i.e., the commutator with the drift Hamiltonian Ĥ₀, plus the dissipator (sum over k), as a sparse matrix. Time-dependent Lindblad operators are not currently supported. The remaining elements are tuples (ℒ₁, ϵ₁) and (ℒ₂, ϵ₂) corresponding to the commutators with the two control Hamiltonians, where ℒ₁ and ℒ₂ again are sparse matrices.\n\nIf H is not time-dependent, the resulting ℒ will likewise be a static operator. Passing H=nothing with non-empty c_ops initializes a pure dissipator.\n\nWith convention=:TDSE, the Liouvillian will be constructed for the equation of motion i hbar fracddt vecrho(t) = ℒ vecrho(t) to match exactly the form of the time-dependent Schrödinger equation. While this notation is not standard in the literature of open quantum systems, it has the benefit that the resulting ℒ can be used in a numerical propagator for a (non-Hermitian) Schrödinger equation without any change. Thus, for numerical applications, convention=:TDSE is generally preferred. The returned ℒ between the two conventions differs only by a factor of i, since we generally assume hbar=1.\n\nThe convention keyword argument is mandatory, to force a conscious choice.\n\nSee Goerz et. al. \"Optimal control theory for a unitary operation under dissipative evolution\", arXiv 1312.0111v2, Appendix B.2 for the explicit construction of the Liouvillian superoperator as a sparse matrix.\n\nPassing check=false, suppresses warnings and errors about unexpected types or the structure of the arguments, cf. hamiltonian.\n\n\n\n\n\n","category":"function"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Arnoldi]","category":"page"},{"location":"externals/#QuantumPropagators.Arnoldi.arnoldi!-Union{Tuple{T}, Tuple{Matrix{ComplexF64}, Array{T}, Int64, T, Any}, Tuple{Matrix{ComplexF64}, Array{T}, Int64, T, Any, Float64}} where T","page":"-","title":"QuantumPropagators.Arnoldi.arnoldi!","text":"m = arnoldi!(Hess, q, m, Ψ, H, dt=1.0; extended=true, norm_min=1e-15)\n\nCalculate the Hessenberg matrix and Arnoldi vectors of H dt, from Ψ.\n\nFor a given order m, the m×m Hessemberg matrix is calculated and stored in in the pre-allocated Hess. Further an array of m normalized Arnoldi vectors is stored in in the pre-allocated q, plus one additional unnormalized Arnoldi vector. The unnormalized m+1st vector could be used to easily extend a given m×m Hessenberg matrix to a (m+1)×(m+1) matrix.\n\nIf the extended Hessenberg matrix is requested (extended=true, default), the m+1st Arnoldi vector is also normalized, and it's norm will be stored in m+1, m entry of the (extended) Hessenberg matrix, which is an (m+1)×(m+1) matrix.\n\nReturn the size m of the calculated Hessenberg matrix. This will usually be the input m, except when the Krylov dimension of H starting from Ψ is less then m. E.g., if Ψ is an eigenstate of H, the returned m will be 1.\n\nSee https://en.wikipedia.org/wiki/Arnoldi_iteration for a description of the algorithm.\n\nArguments\n\nHess::Matrix{ComplexF64}: Pre-allocated storage for the Hessemberg matrix. Can be uninitialized on input. The matrix must be at least of size m×m, or (m+1)×(m+1) if extended=true. On output, the m×m sub-matrix of Hess (with the returned output m) will contain the Hessenberg matrix, and all other elements of Hess be be set to zero.\nq: Pre-allocated array of states similar to Ψ, as storage for the calculated Arnoldi vectors. These may be un-initialized on input. Must be at least of length m+1\nm: The requested dimensions of the output Hessenberg matrix.\nΨ: The starting vector for the Arnoldi procedure. This can be of any type, as long as Φ = H * Ψ results in a vector similar to Ψ, there is an inner products of Φ and Ψ (Ψ⋅Φ is defined), and norm(Ψ) is defined.\nH: The operator (up to dt) for which to calculate the Arnoldi procedure. Can be of any type, as long as H * Ψ is defined.\ndt: The implicit time step; the total operator for which to calculate the Arnoldi procedure is H * dt\nextended: If true (default), calculate the extended Hessenberg matrix, and normalized the final Arnoldi vector\nnorm_min: the minimum value of the norm of Ψ at which Ψ should be considered the zero vector\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Arnoldi.diagonalize_hessenberg_matrix-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.Arnoldi.diagonalize_hessenberg_matrix","text":"diagonalize_hessenberg_matrix(Hess, m; accumulate=false)\n\nDiagonalize the m × m top left submatrix of the given Hessenberg matrix.\n\nIf accumulate is true, return the concatenated eigenvalues for Hess[1:1,1:1] to Hess[1:m,1:m], that is, all sumatrices of size 1 through m.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Arnoldi.extend_arnoldi!","page":"-","title":"QuantumPropagators.Arnoldi.extend_arnoldi!","text":"Extend dimension of Hessenberg matrix by one.\n\nextend_arnoldi!(Hess, q, m, H, dt; norm_min=1e-15)\n\nextends the entries in Hess from size (m-1)×(m-1) to size m×m, and the list q of Arnoldi vectors from m to (m+1). It is assumed that the input Hess was created by a call to arnoldi! with extended=false or a previous call to extend_arnoldi!. Note that Hess itself is not resized, so it must be allocated to size m×m or greater on input.\n\n\n\n\n\n","category":"function"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Interfaces]","category":"page"},{"location":"externals/#QuantumPropagators.Interfaces.check_amplitude-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_amplitude","text":"Check amplitude appearing in Generator.\n\n@test check_amplitude(ampl; tlist, quiet=false)\n\nverifies that the given ampl is a valid element in the list of amplitudes of a Generator object. Specifically:\n\nget_controls(ampl) must be defined and return a tuple\nall controls in ampl must pass check_control\nsubstitute(ampl, controls_replacements) must be defined\nevaluate(ampl, tlist, n) must be defined and return a Number\nevaluate(ampl, tlist, n; vals_dict) must be defined and return a Number\n\nIf for_parameterization (may require the RecursiveArrayTools package to be loaded):\n\nget_parameters(ampl) must be defined and return a vector of floats. Mutating that vector must mutate the controls inside the ampl.\n\nThe function returns true for a valid amplitude and false for an invalid amplitude. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_control-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_control","text":"Check that control can be evaluated on a time grid.\n\n@test check_control(\n control;\n tlist,\n for_parameterization=true,\n for_time_continuous=(control isa Function),\n quiet=false\n)\n\nverifies the given control (one of the elements of the tuple returned by get_controls):\n\nevaluate(control, tlist, n) must be defined and return a Float64\nevaluate(control, tlist, n; vals_dict=IdDict(control => v)) must be defined and return v\ndiscretize(control, tlist) must be defined and return a vector of floats of the same size as tlist. Only if length(tlist) > 2.\nall values in discretize(control, tlist) must be finite (isfinite).\ndiscretize_on_midpoints(control, tlist) must be defined and return a vector of floats with one element less than tlist. Only if length(tlist) > 2.\nall values in discretize_on_midpoints(control, tlist) must be finite (isfinite)\n\nIf for_time_continuous:\n\nevaluate(control, t) must be defined and return a Float64\nevaluate(control, t; vals_dict=IdDict(control => v)) must be defined and return v\n\nIf for_parameterization:\n\nget_parameters(control) must be defined and return a vector of floats. Mutating that vector must mutate the control.\n\nThe function returns true for a valid control and false for an invalid control. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_generator-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_generator","text":"Check the dynamical generator for propagating state over tlist.\n\n@test check_generator(\n generator; state, tlist,\n for_pwc=true, for_time_continuous=false,\n for_expval=true, for_parameterization=false,\n atol=1e-14, quiet=false)\n\nverifies the given generator:\n\nget_controls(generator) must be defined and return a tuple\nall controls returned by get_controls(generator) must pass check_control\nsubstitute(generator, replacements) must be defined\nIf generator is a Generator instance, all elements of generator.amplitudes must pass check_amplitude with for_parameterization.\n\nIf for_pwc (default):\n\nop = evaluate(generator, tlist, n) must return a valid operator (check_operator), with forwarded keyword arguments (including for_expval)\nIf QuantumPropagators.Interfaces.supports_inplace(op) is true, evaluate!(op, generator, tlist, n) must be defined\n\nIf for_time_continuous:\n\nevaluate(generator, t) must return a valid operator (check_operator), with forwarded keyword arguments (including for_expval)\nIf QuantumPropagators.Interfaces.supports_inplace(op) is true, evaluate!(op, generator, t) must be defined\n\nIf for_parameterization (may require the RecursiveArrayTools package to be loaded):\n\nget_parameters(generator) must be defined and return a vector of floats. Mutating that vector must mutate the controls inside the generator.\n\nThe function returns true for a valid generator and false for an invalid generator. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_operator-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_operator","text":"Check that op is a valid operator that can be applied to state.\n\n@test check_operator(op; state, tlist=[0.0, 1.0],\n for_expval=true, atol=1e-14, quiet=false)\n\nverifies the given op relative to state. The state must pass check_state.\n\nAn \"operator\" is any object that evaluate returns when evaluating a time-dependent dynamic generator. The specific requirements for op are:\n\nop must not be time-dependent: evaluate(op, tlist, 1) ≡ op\nop must not contain any controls: length(get_controls(op)) == 0\nop * state must be defined\nThe QuantumPropagators.Interfaces.supports_inplace method must be defined for op. If it returns true, it must be possible to evaluate a generator in-place into the existing op. See check_generator.\n\nIf QuantumPropagators.Interfaces.supports_inplace(state):\n\nThe 3-argument LinearAlgebra.mul! must apply op to the given state\nThe 5-argument LinearAlgebra.mul! must apply op to the given state\nLinearAlgebra.mul! must match *, if applicable\nLinearAlgebra.mul! must return the resulting state\n\nIf for_expval (typically required for optimal control):\n\nLinearAlgebra.dot(state, op, state) must return return a number\ndot(state, op, state) must match dot(state, op * state), if applicable\n\nThe function returns true for a valid operator and false for an invalid operator. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_parameterized-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_parameterized","text":"Check that that the object supports the parameterization interface.\n\n@test check_parameterized(object; name=\"::$typeof(object))\", quiet=false)\n\nverifies that the given object:\n\ncan be passed to get_parameters, which must return an AbstractVector of Float64\nis mutated by mutating the parameters obtained by get_parameters\n\nSee also\n\ncheck_parameterized_function is object is a ParameterizedFunction\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_parameterized_function-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_parameterized_function","text":"Check a ParameterizedFunction instance.\n\n@test check_parameterized_function(f; tlist; quiet=false)\n\nverifies that the given f:\n\nis an instance of ParameterizedFunction.\nhas a field parameters that is an AbstractVector{Float64}.\nis a callable as f(t) for values of t in tlist, returning a Float64.\nget_parameters provides access to the parameters field.\npasses check_parameterized\n\nSee also\n\ncheck_parameterized for objects that have parameters (get_parameters), but are not instances of ParameterizedFunction\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_propagator-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_propagator","text":"Check that the given propagator implements the required interface.\n\n@test check_propagator(propagator; atol=1e-14, quiet=false)\n\nverifies that the propagator matches the interface described for an AbstractPropagator. The propagator must have been freshly initialized with init_prop.\n\npropagator must have the properties state, tlist, t, parameters, backward, and inplace\npropagator.state must be a valid state (see check_state)\nIf propagator.inplace is true, supports_inplace for propagator.state must also be true\npropagator.tlist must be monotonically increasing.\npropagator.t must be the first or last element of propagator.tlist, depending on propagator.backward\nprop_step!(propagator) must be defined and return a valid state until the time grid is exhausted\nFor an in-place propagator, the state returned by prop_step! must be the propagator.state object\nFor a not-in-place propagator, the state returned by prop_step! must be a new object\nprop_step! must advance propagator.t forward or backward one step on the time grid\nprop_step! must return nothing when going beyond the time grid\nset_t!(propagator, t) must be defined and set propagator.t\nset_state!(propagator, state) must be defined and set propagator.state.\nset_state!(propagator, state) for an in-place propagator must overwrite propagator.state in-place.\nset_state! must return the set propagator.state\nIn a PiecewisePropagator, propagator.parameters must be a dict mapping controls to a vector of values, one for each interval on propagator.tlist\nreinit_prop! must be defined and re-initialize the propagator\nreinit_prop!(propagator, state) must be idempotent. That is, repeated calls to reinit_prop! leave the propagator unchanged.\n\nThe function returns true for a valid propagator and false for an invalid propagator. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_state-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_state","text":"Check that state is a valid element of a Hilbert space.\n\n@test check_state(state; normalized=false, atol=1e-15, quiet=false)\n\nverifies the following requirements:\n\nThe inner product (LinearAlgebra.dot) of two states must return a Complex number.\nThe LinearAlgebra.norm of state must be defined via the inner product. This is the definition of a Hilbert space, a.k.a a \"complete inner product space\" or more precisely a \"Banach space (normed vector space) where the norm is induced by an inner product\".\nThe `QuantumPropagators.Interfaces.supports_inplace method must be defined for state\n\nAny state must support the following not-in-place operations:\n\nstate + state and state - state must be defined\ncopy(state) must be defined and return an object of the same type as state\nc * state for a scalar c must be defined\nnorm(state + state) must fulfill the triangle inequality\nzero(state) must be defined and produce a state with norm 0\n0.0 * state must produce a state with norm 0\ncopy(state) - state must have norm 0\nnorm(state) must have absolute homogeneity: norm(s * state) = s * norm(state)\n\nIf supports_inplace(state) is true, the state must also support the following:\n\nsimilar(state) must be defined and return a valid state of the same type a state\ncopyto!(other, state) must be defined\nfill!(state, c) must be defined\nLinearAlgebra.lmul!(c, state) for a scalar c must be defined\nLinearAlgebra.axpy!(c, state, other) must be defined\nnorm(state) must fulfill the same general mathematical norm properties as for the non-in-place norm.\n\nIf normalized (not required by default):\n\nLinearAlgebra.norm(state) must be 1\n\nIt is strongly recommended to always support immutable operations (also for mutable states)\n\nThe function returns true for a valid state and false for an invalid state. Unless quiet=true, it will log an error to indicate which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.check_tlist-Tuple{Any}","page":"-","title":"QuantumPropagators.Interfaces.check_tlist","text":"Check that the given tlist is valid.\n\n@test check_tlist(tlist; quiet=false)\n\nverifies the given time grid. A valid time grid must\n\nbe a Vector{Float64},\ncontain at least two points (beginning and end),\nbe monotonically increasing\n\nThe function returns true for a valid time grid and false for an invalid time grid. Unless quiet=true, it will log an error to indicated which of the conditions failed.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Interfaces.supports_inplace-Tuple{Vector{ComplexF64}}","page":"-","title":"QuantumPropagators.Interfaces.supports_inplace","text":"Indicate whether a given state or operator supports in-place operations\n\nsupports_inplace(state)\n\nIndicates that propagators can assume that the in-place requirements defined in QuantumPropagators.Interfaces.check_state hold. States with in-place support must also fulfill specific properties when interacting with operators, see QuantumPropagators.Interfaces.check_operator.\n\nsupports_inplace(op)\n\nIndicates that the operator can be evaluated in-place with evaluate!, see QuantumPropagators.Interfaces.check_generator\n\nNote that supports_inplace is not quite the same as Base.ismutable: When using custom structs for states or operators, even if those structs are not defined as mutable, they may still define the in-place interface (typically because their components are mutable).\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Controls]","category":"page"},{"location":"externals/#QuantumPropagators.Controls.ParameterizedFunction","page":"-","title":"QuantumPropagators.Controls.ParameterizedFunction","text":"Abstract type for function-like objects with parameters.\n\nA struct that is an implementation of a ParameterizedFunction:\n\nmust have a parameters field that is an AbstractVector of floats (e.g., a ComponentArrays.ComponentVector)\nmust be callable with a single float argument t,\nmay define getters and setters for referencing the values in parameters with convenient names.\n\nThe parameters field of any ParameterizedFunction can be accessed via get_parameters.\n\nSee How to define a parameterized control for an example. You may use the QuantumPropagators.Interfaces.check_parameterized_function to check the implementation of a ParameterizedFunction subtype.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Controls.discretize-Tuple{Function, Any}","page":"-","title":"QuantumPropagators.Controls.discretize","text":"Evaluate control at every point of tlist.\n\nvalues = discretize(control, tlist; via_midpoints=true)\n\ndiscretizes the given control to a Vector of values defined on the points of tlist.\n\nIf control is a function, it is first evaluated at the midpoint of tlist, see discretize_on_midpoints, and then the values on the midpoints are converted to values on tlist. This discretization is more stable than directly evaluating the control function at the values of tlist, and ensures that repeated round-trips between discretize and discretize_on_midpoints can be done safely, see the note in the documentation of discretize_on_midpoints.\n\nThe latter can still be achieved by passing via_midpoints=false. While such a direct discretization is suitable e.g. for plotting, but it is unsuitable for round-trips between discretize and discretize_on_midpoints (constant controls on tlist may result in a zig-zag on the intervals of tlist).\n\nIf control is a vector, a copy of control will be returned if it is of the same length as tlist. Otherwise, control must have one less value than tlist, and is assumed to be defined on the midpoints of tlist. In that case, discretize acts as the inverse of discretize_on_midpoints. See discretize_on_midpoints for how control values on tlist and control values on the intervals of tlist are related.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.discretize_on_midpoints-Union{Tuple{T}, Tuple{T, Any}} where T<:Function","page":"-","title":"QuantumPropagators.Controls.discretize_on_midpoints","text":"Evaluate control at the midpoints of tlist.\n\nvalues = discretize_on_midpoints(control, tlist)\n\ndiscretizes the given control to a Vector of values on the midpoints of tlist. Hence, the resulting values will contain one less value than tlist.\n\nIf control is a vector of values defined on tlist (i.e., of the same length as tlist), it will be converted to a vector of values on the intervals of tlist. The value for the first and last \"midpoint\" will remain the original values at the beginning and end of tlist, in order to ensure exact boundary conditions. For all other midpoints, the value for that midpoint will be calculated by \"un-averaging\".\n\nFor example, for a control and tlist of length 5, consider the following diagram:\n\ntlist index: 1 2 3 4 5\ntlist: ⋅ ⋅ ⋅ ⋅ ⋅ input values cᵢ (i ∈ 1..5)\n |̂/ ̄ ̄ ̂\\ / ̂\\ / ̂ ̄ ̄\\|̂\nmidpoints: x x x x output values pᵢ (i ∈ 1..4)\nmidpoints index: 1 2 3 4\n\nWe will have p₁=c₁ for the first value, p₄=c₅ for the last value. For all other points, the control values cᵢ = fracp_i-1 + p_i2 are the average of the values on the midpoints. This implies the \"un-averaging\" for the midpoint values pᵢ = 2 c_i - p_i-1.\n\nnote: Note\nAn arbitrary input control array may not be compatible with the above averaging formula. In this case, the conversion will be \"lossy\" (discretize will not recover the original control array; the difference should be considered a \"discretization error\"). However, any further round-trip conversions between points and intervals are bijective and preserve the boundary conditions. In this case, the discretize_on_midpoints and discretize methods are each other's inverse. This also implies that for an optimal control procedure, it is safe to modify midpoint values. Modifying the the values on the time grid directly on the other hand may accumulate discretization errors.\n\nIf control is a vector of one less length than tlist, a copy of control will be returned, under the assumption that the input is already properly discretized.\n\nIf control is a function, the function will be directly evaluated at the midpoints marked as x in the above diagram..\n\nSee also\n\nget_tlist_midpoints – get all the midpoints on which the control will be discretized.\nt_mid – get a particular midpoint.\ndiscretize – discretize directly on tlist instead of on the midpoints\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.evaluate!-Tuple{Any, Tuple, Vararg{Any}}","page":"-","title":"QuantumPropagators.Controls.evaluate!","text":"Update an existing evaluation of a generator.\n\nevaluate!(op, generator, args..; vals_dict=IdDict())\n\nperforms an in-place update on an op the was obtained from a previous call to evaluate with the same generator, but for a different point in time and/or different values in vals_dict.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.evaluate-Tuple{Any, Vararg{Any}}","page":"-","title":"QuantumPropagators.Controls.evaluate","text":"Evaluate all controls.\n\nIn general, evaluate(object, args...; vals_dict=IdDict()) evaluates the object for a specific point in time indicated by the positional args. Any control in object is evaluated at the specified point in time. Alternatively, the vals_dict maps a controls to value (\"plug in this value for the given control\")\n\nFor example,\n\nop = evaluate(generator, t)\n\nevaluates generator at time t. This requires that any control in generator is a callable that takes t as a single argument.\n\nop = evaluate(generator, tlist, n)\n\nevaluates generator for the n'th interval of tlist. This uses the definitions for the midpoints in discretize_on_midpoints. The controls in generator may be vectors (see discretize, discretize_on_midpoints) or callables of t.\n\nop = evaluate(generator, t; vals_dict)\nop = evaluate(generator, tlist, n; vals_dict)\n\nresolves any explicit time dependencies in generator at the specified point in time, but uses the value in the given vals_dict for any control in vals_dict.\n\na = evaluate(ampl, tlist, n; vals_dict=IdDict())\na = evaluate(ampl, t; vals_dict=IdDict())\n\nevaluates a control amplitude to a scalar by evaluating any explicit time dependency, and by replacing each control with the corresponding value in vals_dict.\n\nCalling evaluate for an object with no implicit or explicit time dependence should return the object unchanged.\n\nFor generators without any explicit time dependence,\n\nop = evaluate(generator; vals_dict)\n\ncan be used. The vals_dict in this case must contain values for all controls in generator.\n\nSee also:\n\nevaluate! — update an existing operator with a re-evaluation of a\n\ngenerator at a different point in time.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_controls-Tuple{AbstractMatrix}","page":"-","title":"QuantumPropagators.Controls.get_controls","text":"get_controls(operator)\n\nfor a static operator (matrix) returns an empty tuple.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_controls-Tuple{Function}","page":"-","title":"QuantumPropagators.Controls.get_controls","text":"Extract a Tuple of controls.\n\ncontrols = get_controls(generator)\n\nextracts the controls from a single dynamical generator.\n\nFor example, if generator = hamiltonian(H0, (H1, ϵ1), (H2, ϵ2)), extracts (ϵ1, ϵ2).\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_parameters-Tuple{Any}","page":"-","title":"QuantumPropagators.Controls.get_parameters","text":"Obtain analytic parameters of the given control.\n\nparameters = get_parameters(control)\n\nobtains parameters as an AbstractVector{Float64} containing any tunable analytic parameters associated with the control. The specific type of parameters depends on how control is defined, but a ComponentArrays.ComponentVector should be a common array type.\n\nMutating the resulting vector must directly affect the control in any subsequent call to evaluate. That is, the values in parameters must alias values inside the control.\n\nNote that the control must be an object specifically designed to have analytic parameters. Typically, it should be implemented as a subtype of ParameterizedFunction. For a simple function ϵ(t) or a vector of pulse values, which are the default types of controls discussed in the documentation of hamiltonian, the get_parameters function will return an empty vector.\n\nMore generally,\n\nparameters = get_parameters(object)\n\ncollects and combines all unique parameter arrays from the controls inside the object. The object may be a Generator, Trajectory, ControlProblem, or any other object for which get_controls(object) is defined. If there are multiple controls with different parameter arrays, these are combined in a RecursiveArrayTools.ArrayPartition. This requires the RecursiveArrayTools package to be loaded. Again, mutating parameters directly affects the underlying controls.\n\nThe parameters may be used as part of the parameters attribute of a propagator for time-continuous dynamics, like a general ODE solver, or in an optimization that tunes analytic control parameters, e.g., with a Nelder-Mead method. Examples might include the widths, peak amplitudes, and times of a superposition of Gaussians [9], cf. the example of a ParameterizedFunction, or the amplitudes associated with spectral components in a random truncated basis [10].\n\nThe parameters are not intended for optimization methods such as GRAPE or Krotov that fundamentally use a piecewise-constant control ansatz. In the context of such methods, the \"control parameters\" are always the amplitudes of the control at the mid-points of the time grid, as obtained by discretize_on_midpoints, and get_parameters is ignored.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.get_tlist_midpoints-Tuple{AbstractVector}","page":"-","title":"QuantumPropagators.Controls.get_tlist_midpoints","text":"Shift time grid values to the interval midpoints\n\ntlist_midpoints = get_tlist_midpoints(\n tlist; preserve_start=true, preserve_end=true\n)\n\ntakes a vector tlist of length n and returns a Vector{Float64} of length n-1 containing the midpoint values of each interval. The intervals in tlist are not required to be uniform.\n\nBy default, the first and last point of tlist is preserved, see discretize_on_midpoints. This behavior can be disabled by passing preserve_start and preserve_end as false in order to use the midpoints of the first and last interval, respectively.\n\nSee also\n\nt_mid – get a particular midpoint.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.substitute-Union{Tuple{T}, Tuple{T, Any}} where T","page":"-","title":"QuantumPropagators.Controls.substitute","text":"Substitute inside the given object.\n\nobject = substitute(object, replacements)\n\nreturns a modified object with the replacements defined in the given replacements dictionary. Things that can be replaced include operators, controls, and amplitudes. For example,\n\ngenerator = substitute(generator::Generator, replacements)\noperator = substitute(operator::Operator, replacements)\namplitude = substitute(amplitude, controls_replacements)\n\nNote that substitute cannot be used to replace dynamic quantities, e.g. controls, with static value. Use evaluate instead for that purpose.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Controls.t_mid-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.Controls.t_mid","text":"Midpoint of n'th interval of tlist.\n\nt = t_mid(tlist, n)\n\nreturns the t that is the midpoint between points tlist[n+1] and tlist[n], but snapping to the beginning/end to follow the convention explained in discretize_on_midpoints (to preserve exact boundary conditions at the edges of the time grid.)\n\nSee also\n\nget_tlist_midpoints – get all the midpoints in one go.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Storage]","category":"page"},{"location":"externals/#QuantumPropagators.Storage.get_from_storage!-Tuple{Any, AbstractVector, Any}","page":"-","title":"QuantumPropagators.Storage.get_from_storage!","text":"Obtain data from storage.\n\nget_from_storage!(data, storage, i)\n\nextracts data from the storage for the i'th time slot. Inverse of write_to_storage!. This modifies data in-place. If get_from_storage! is implemented for arbitrary observables, it is the developer's responsibility that init_storage, write_to_storage!, and get_from_storage! are compatible.\n\nTo extract immutable data, the non-in-place version\n\ndata = get_from_storage(storage, i)\n\ncan be used.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.get_from_storage-Tuple{AbstractVector, Any}","page":"-","title":"QuantumPropagators.Storage.get_from_storage","text":"Obtain immutable data from storage.\n\ndata = get_from_storage(storage, i)\n\nSee get_from_storage!.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.init_storage-Tuple{Any, AbstractVector}","page":"-","title":"QuantumPropagators.Storage.init_storage","text":"Create a storage array for propagation.\n\nstorage = init_storage(state, tlist)\n\ncreates a storage array suitable for storing a state for each point in tlist.\n\nstorage = init_storage(state, tlist, observables)\n\ncreates a storage array suitable for the data generated by the observables applied to state, see map_observables, for each point in tlist.\n\nstorage = init_storage(data, nt)\n\ncreates a storage arrays suitable for storing data nt times, where nt=length(tlist). By default, this will be a vector of typeof(data) and length nt, or a n × nt Matrix with the same eltype as data if data is a Vector of length n.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.map_observable-Union{Tuple{ST}, Tuple{IT}, Tuple{TT}, Tuple{F}, Tuple{F, TT, IT, ST}} where {F<:Function, TT, IT, ST}","page":"-","title":"QuantumPropagators.Storage.map_observable","text":"Apply a single observable to state.\n\ndata = map_observable(observable, tlist, i, state)\n\nBy default, observable can be one of the following:\n\nA function taking the three arguments state, tlist, i, where state is defined at time tlist[i].\nA function taking a single argument state, under the assumption that the observable is time-independent\nA matrix for which to calculate the expectation value with respect to the vector state.\n\nThe default map_observables delegates to this function.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.map_observables-NTuple{4, Any}","page":"-","title":"QuantumPropagators.Storage.map_observables","text":"Obtain \"observable\" data from state.\n\ndata = map_observables(observables, tlist, i, state)\n\ncalculates the data for a tuple of observables applied to state defined at time tlist[i]. For a single observable (tuple of length 1), simply return the result of map_observable.\n\nFor multiple observables, return the tuple resulting from applying map_observable for each observable. If the tuple is \"uniform\" (all elements are of the same type, e.g. if each observable calculates the expectation value of a Hermitian operator), it is converted to a Vector. This allows for compact storage in a storage array, see init_storage.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Storage.write_to_storage!-Tuple{AbstractVector, Integer, Any}","page":"-","title":"QuantumPropagators.Storage.write_to_storage!","text":"Place data into storage for time slot i.\n\nwrite_to_storage!(storage, i, data)\n\nfor a storage array created by init_storage stores the data obtained from map_observables at time slot i.\n\nConceptually, this corresponds roughly to storage[i] = data, but storage may have its own idea on how to store data for a specific time slot. For example, with the default init_storage Vector data will be stored in a matrix, and write_to_storage! will in this case write data to the i'th column of the matrix.\n\nFor a given type of storage and data, it is the developer's responsibility that init_storage and write_to_storage! are compatible.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.SpectralRange]","category":"page"},{"location":"externals/#QuantumPropagators.SpectralRange.random_state-Tuple{Any}","page":"-","title":"QuantumPropagators.SpectralRange.random_state","text":"Random normalized quantum state.\n\n Ψ = random_state(H; rng=Random.GLOBAL_RNG)\n\nreturns a random normalized state compatible with the Hamiltonian H. This is intended to provide a starting vector for estimating the spectral radius of H via an Arnoldi method.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.SpectralRange.ritzvals","page":"-","title":"QuantumPropagators.SpectralRange.ritzvals","text":"Calculate a vector for Ritz values converged to a given precision.\n\nR = ritzvals(G, state, m_min, m_max=2*m_min; prec=1e-5, norm_min=1e-15)\n\ncalculates a complex vector R of at least m_min (assuming a sufficient Krylov dimension) and at most m_max Ritz values.\n\n\n\n\n\n","category":"function"},{"location":"externals/#QuantumPropagators.SpectralRange.specrange-Tuple{Any, Val{:arnoldi}}","page":"-","title":"QuantumPropagators.SpectralRange.specrange","text":"E_min, E_max = specrange(\n H, :arnoldi;\n rng=Random.GLOBAL_RNG,\n state=random_state(H; rng),\n m_min=20,\n m_max=60,\n prec=1e-3,\n norm_min=1e-15,\n enlarge=true\n)\n\nuses Arnoldi iteration with state as the starting vector. It approximates the eigenvalues of H with between m_min and m_max Ritz values, until the lowest and highest eigenvalue are stable to a relative precision of prec. The norm_min parameter is passed to the underlying arnoldi!.\n\nIf enlarge=true (default) the returned E_min and E_max will be enlarged via a heuristic to slightly over-estimate the spectral radius instead of under-estimating it.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.SpectralRange.specrange-Tuple{Any, Val{:diag}}","page":"-","title":"QuantumPropagators.SpectralRange.specrange","text":"E_min, E_max = specrange(H, :diag)\n\nuses exact diagonization via the standard eigvals function to obtain the smallest and largest eigenvalue. This should only be used for relatively small matrices.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.SpectralRange.specrange-Tuple{Any, Val{:manual}}","page":"-","title":"QuantumPropagators.SpectralRange.specrange","text":"E_min, E_max = specrange(H, :manual; E_min, E_max)\n\ndirectly returns the given E_min and E_max without considering H.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.SpectralRange.specrange-Tuple{Any}","page":"-","title":"QuantumPropagators.SpectralRange.specrange","text":"Calculate the spectral range of a Hamiltonian H on the real axis.\n\nE_min, E_max = specrange(H; method=:auto, kwargs...)\n\ncalculates the approximate lowest and highest eigenvalues of H. Any imaginary part in the eigenvalues is ignored: the routine is intended for (although not strictly limited to) a Hermitian H.\n\nThis delegates to\n\nspecrange(H, method; kwargs...)\n\nfor the different methods.\n\nThe default method=:auto chooses the best method for the given H. This is :diag for small matrices, and :arnoldi otherwise. If both E_min and E_max are given in the kwargs, those will be returned directly (method=:manual).\n\nKeyword arguments not relevant to the underlying implementation will be ignored.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Newton]","category":"page"},{"location":"externals/#QuantumPropagators.Newton.NewtonWrk","page":"-","title":"QuantumPropagators.Newton.NewtonWrk","text":"NewtonWrk(v0, m_max=10)\n\nWorkspace for the Newton-with-restarted-Arnoldi propagation routine.\n\nInitializes the workspace for the propagation of a vector v0, using a maximum Krylov dimension of m_max in each restart iteration. Note that m_max should be smaller than the length of v0.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Newton.extend_leja!-Tuple{OffsetArrays.OffsetVector{ComplexF64, AA} where AA<:AbstractVector{ComplexF64}, Any, OffsetArrays.OffsetVector{ComplexF64, AA} where AA<:AbstractVector{ComplexF64}, Any}","page":"-","title":"QuantumPropagators.Newton.extend_leja!","text":"extend_leja!(leja, n, newpoints, n_use)\n\nGiven an array of n (ordered) Leja points, extract n_use points from newpoints, and append them to the existing Leja points. The array leja should be sufficiently large to hold the new Leja points, which are appended after index n_old. It will be re-allocated if necessary and may have a size of up to 2*(n+n_use).\n\nArguments\n\nleja: Array of leja values. Must contain the \"old\" leja values to be kept in leja(0:n-1). On output, n_use new leja points will be in leja(n+:n+n_use-1), for the original value of n. The leja array must use zero-based indexing.\nn: On input, number of \"old\" leja points in leja. On output, total number of leja points (i.e. n=n+n_use)\nnewpoints: On input, candidate points for new leja points. The n_use best values will be chosen and added to leja. On output, the values of new_points are undefined.\nn_use: Number of points that should be added to leja\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Newton.extend_newton_coeffs!-Tuple{OffsetArrays.OffsetVector{ComplexF64, AA} where AA<:AbstractVector{ComplexF64}, Int64, OffsetArrays.OffsetVector{ComplexF64, AA} where AA<:AbstractVector{ComplexF64}, Any, Int64, Float64}","page":"-","title":"QuantumPropagators.Newton.extend_newton_coeffs!","text":"extend_newton_coeffs!(a, n_a, leja, func, n_leja, radius)\n\nExtend the array a of existing Newton coefficients for the expansion of the func from n_a coefficients to n_leja coefficients. Return a new value n_a=n_a+n_leja with the total number of Newton coefficients in the updated a.\n\nArguments\n\na: On input, a zero-based array of length n_a or greater, containing Newton coefficients. On output, array containing a total n_leja coefficients. The array a will be resized if necessary, and may have a length greater than n_leja on output\nn_a: The number of Newton coefficients in a, on input. Elements of a beyond the first n_a elements will be overwritten.\nleja: Array of normalized Leja points, containing at least n_leja elements.\nfunc: Function for which to calculate Newton coefficients\nn_leja: The number of elements in leja to use for calculating new coefficients, and the total number of Newton coefficients on output\nradius: Normalization radius for divided differences\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Newton.newton!-NTuple{4, Any}","page":"-","title":"QuantumPropagators.Newton.newton!","text":"newton!(Ψ, H, dt, wrk; func=(z -> exp(-1im*z)), norm_min=1e-14, relerr=1e-12,\n max_restarts=50, _...)\n\nEvaluate Ψ = func(H*dt) Ψ using a Newton-with-restarted-Arnoldi scheme.\n\nArguments\n\nΨ: The state to propagate, will be overwritten in-place with the propagated state\nH: Operator acting on Ψ. Together with dt, this is the argument to func\ndt: Implicit time step. Together with H, this is the argument to func\nwkr: Work array, initialized with NewtonWrk\nfunc: The function to apply to H dt, taking a single (scalar) complex-valued argument z in place of H dt. The default func is to evaluate the time evaluations operator for the Schrödinger equation\nnorm_min: the minimum norm at which to consider a state similar to Ψ as zero\nrelerr: The relative error defining the convergence condition for the restart iteration. Propagation stops when the norm of the accumulated Ψ is stable up to the given relative error\nmax_restarts: The maximum number of restart iterations. Exceeding max_restarts will throw an AssertionError.\n\nAll other keyword arguments are ignored.\n\n\n\n\n\n","category":"method"},{"location":"externals/","page":"-","title":"-","text":"Modules = [QuantumPropagators.Cheby]","category":"page"},{"location":"externals/#QuantumPropagators.Cheby.ChebyWrk","page":"-","title":"QuantumPropagators.Cheby.ChebyWrk","text":"Workspace for the Chebychev propagation routine.\n\nChebyWrk(Ψ, Δ, E_min, dt; limit=1e-12)\n\ninitializes the workspace for the propagation of a state similar to Ψ under a Hamiltonian with eigenvalues between E_min and E_min + Δ, and a time step dt. Chebychev coefficients smaller than the given limit are discarded.\n\n\n\n\n\n","category":"type"},{"location":"externals/#QuantumPropagators.Cheby.cheby!-NTuple{4, Any}","page":"-","title":"QuantumPropagators.Cheby.cheby!","text":"Evaluate Ψ = exp(-𝕚 * H * dt) Ψ in-place.\n\ncheby!(Ψ, H, dt, wrk; E_min=nothing, check_normalization=false)\n\nArguments\n\nΨ: on input, initial vector. Will be overwritten with result.\nH: Hermitian operator\ndt: time step\nwrk: internal workspace\nE_min: minimum eigenvalue of H, to be used instead of the E_min from the initialization of wrk. The same wrk may be used for different values E_min, as long as the spectra radius Δ and the time step dt are the same as those used for the initialization of wrk.\ncheck_normalizataion: perform checks that the H does not exceed the spectral radius for which the workspace was initialized.\n\nThe routine will not allocate any internal storage. This implementation requires copyto! lmul!, and axpy! to be implemented for Ψ, and the three-argument mul! for Ψ and H.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Cheby.cheby-NTuple{4, Any}","page":"-","title":"QuantumPropagators.Cheby.cheby","text":"Evaluate Ψ = exp(-𝕚 * H * dt) Ψ.\n\nΨ_out = cheby(Ψ, H, dt, wrk; E_min=nothing, check_normalization=false)\n\nacts like cheby! but does not modify Ψ in-place.\n\n\n\n\n\n","category":"method"},{"location":"externals/#QuantumPropagators.Cheby.cheby_coeffs!","page":"-","title":"QuantumPropagators.Cheby.cheby_coeffs!","text":"Calculate Chebychev coefficients in-place.\n\nn::Int = cheby_coeffs!(coeffs, Δ, dt, limit=1e-12)\n\noverwrites the first n values in coeffs with new coefficients larger than limit for the given new spectral radius Δ and time step dt. The coeffs array will be resized if necessary, and may have a length > n on exit.\n\nSee also cheby_coeffs for an non-in-place version.\n\n\n\n\n\n","category":"function"},{"location":"externals/#QuantumPropagators.Cheby.cheby_coeffs-Tuple{Any, Any}","page":"-","title":"QuantumPropagators.Cheby.cheby_coeffs","text":"Calculate Chebychev coefficients.\n\na::Vector{Float64} = cheby_coeffs(Δ, dt; limit=1e-12)\n\nreturn an array of coefficients larger than limit.\n\nArguments\n\nΔ: the spectral radius of the underlying operator\ndt: the time step\n\nSee also cheby_coeffs! for an in-place version.\n\n\n\n\n\n","category":"method"},{"location":"overview/#Overview","page":"Overview","title":"Overview","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = Krotov","category":"page"},{"location":"#Krotov.jl","page":"Home","title":"Krotov.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"using Markdown\nusing Pkg\n\nVERSION = Pkg.dependencies()[Base.UUID(\"b05dcdc7-62f6-4360-bf2c-0898bba419de\")].version\n\ngithub_badge = \"[![Github](https://img.shields.io/badge/JuliaQuantumControl-Krotov.jl-blue.svg?logo=github)](https://github.com/JuliaQuantumControl/Krotov.jl)\"\n\nversion_badge = \"![v$VERSION](https://img.shields.io/badge/version-v$VERSION-green.svg)\"\n\nMarkdown.parse(\"$github_badge $version_badge\")","category":"page"},{"location":"","page":"Home","title":"Home","text":"Implementation of Krotov's method of optimal control [1–6] enhanced with automatic differentiation [7].","category":"page"},{"location":"","page":"Home","title":"Home","text":"Part of QuantumControl.jl and the JuliaQuantumControl organization.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Krotov.jl is a port of the krotov Python package, adapted to the API of QuantumControl.jl.","category":"page"},{"location":"#Contents","page":"Home","title":"Contents","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Depth = 2\nPages = [pair[2] for pair in Main.PAGES[2:end-1]]","category":"page"},{"location":"#History","page":"Home","title":"History","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"See the Releases on Github.","category":"page"}] }