![]() |
Dormand-Prince explicit solver for non-stiff ODEs.
Inherits From: Solver
tfp.substrates.numpy.math.ode.DormandPrince(
rtol=0.001,
atol=1e-06,
first_step_size=0.001,
safety_factor=0.9,
min_step_size_factor=0.1,
max_step_size_factor=10.0,
max_num_steps=None,
make_adjoint_solver_fn=None,
validate_args=False,
name='dormand_prince'
)
Implements 5th order Runge-Kutta with adaptive step size control
and dense output, using the Dormand-Prince method. Similar to the 'dopri5'
method of scipy.integrate.ode
and MATLAB's ode45
. For details see [1].
For solver API see tfp.math.ode.Solver
.
References
[1]: Shampine, L. F. (1986). Some practical runge-kutta formulas. Mathematics of Computation, 46(173), 135-150, doi:10.2307/2008219
Args | |
---|---|
rtol
|
Optional float Tensor specifying an upper bound on relative error,
per element of the dependent variable. The error tolerance for the next
step is tol = atol + rtol * abs(state) where state is the computed
state at the current step (see also atol ). The next step is rejected
if it incurs a local truncation error larger than tol .
Default value: 1e-3 .
|
atol
|
Optional float Tensor specifying an upper bound on absolute error,
per element of the dependent variable (see also rtol ).
Default value: 1e-6 .
|
first_step_size
|
Scalar float Tensor specifying the size of the
first step.
Default value: 1e-3 .
|
safety_factor
|
Scalar positive float Tensor . At the end of every Runge
Kutta step, the solver may choose to update the step size by applying a
multiplicative factor to the current step size. This factor is factor =
clamp(factor_unclamped, min_step_size_factor, max_step_size_factor)
where factor_unclamped = error_ratio**(-1. / (order + 1)) *
safety_factor (see also min_step_size_factor and
max_step_size_factor ). A small (respectively, large) value for the
safety factor causes the solver to take smaller (respectively, larger)
step sizes. A value larger than one, though not explicitly prohibited,
is discouraged.
Default value: 0.9 .
|
min_step_size_factor
|
Scalar float Tensor (see safety_factor ).
Default value: 0.1 .
|
max_step_size_factor
|
Scalar float Tensor (see safety_factor ).
Default value: 10. .
|
max_num_steps
|
Optional scalar integer Tensor specifying the maximum
number of steps allowed (including rejected steps). If unspecified,
there is no upper bound on the number of steps.
Default value: None .
|
make_adjoint_solver_fn
|
Callable that takes no arguments that constructs a
Solver instance. The created solver is used in the adjoint senstivity
analysis to compute gradients (if they are requested).
Default value: A callable that returns this solver.
|
validate_args
|
Whether to validate input with asserts. If validate_args
is False and the inputs are invalid, correct behavior is not
guaranteed.
Default value: False .
|
name
|
Python str name prefixed to Ops created by this function.
Default value: None (i.e., 'dormand_prince').
|
Attributes | |
---|---|
name
|
Methods
solve
solve(
ode_fn,
initial_time,
initial_state,
solution_times,
jacobian_fn=None,
jacobian_sparsity=None,
batch_ndims=None,
previous_solver_internal_state=None,
constants=None
)
Solves an initial value problem.
An initial value problem consists of a system of ODEs and an initial condition:
dy/dt(t) = ode_fn(t, y(t), **constants)
y(initial_time) = initial_state
Here, t
(also called time) is a scalar float Tensor
and y(t)
(also
called the state at time t
) is an N-D float or complex Tensor
.
constants
is are values that are constant with respect to time. Passing
the constants here rather than just closing over them in ode_fn
is only
necessary if you want gradients with respect to these values.
Example
The ODE dy/dt(t) = dot(A, y(t))
is solved below.
t_init, t0, t1 = 0., 0.5, 1.
y_init = tf.constant([1., 1.], dtype=tf.float64)
A = tf.constant([[-1., -2.], [-3., -4.]], dtype=tf.float64)
def ode_fn(t, y):
return tf.linalg.matvec(A, y)
results = tfp.math.ode.BDF().solve(ode_fn, t_init, y_init,
solution_times=[t0, t1])
y0 = results.states[0] # == dot(matrix_exp(A * t0), y_init)
y1 = results.states[1] # == dot(matrix_exp(A * t1), y_init)
If the exact solution times are not important, it can be much
more efficient to let the solver choose them using
solution_times=tfp.math.ode.ChosenBySolver(final_time=1.)
.
This yields the state at various times between t_init
and final_time
,
in which case results.states[i]
is the state at time results.times[i]
.
Gradients
The gradients are computed using the adjoint sensitivity method described in [Chen et al. (2018)][1].
grad = tf.gradients(y1, y0) # == dot(e, J)
# J is the Jacobian of y1 with respect to y0. In this case, J = exp(A * t1).
# e = [1, ..., 1] is the row vector of ones.
This is not capable of computing gradients with respect to values closed
over by ode_fn
, e.g., in the example above:
def ode_fn(t, y):
return tf.linalg.matvec(A, y)
with tf.GradientTape() as tape:
tape.watch(A)
results = tfp.math.ode.BDF().solve(ode_fn, t_init, y_init,
solution_times=[t0, t1])
tape.gradient(results.states, A) # Undefined!
There are two options to get the gradients flowing through these values:
- Use
tf.Variable
for these values. - Pass the values in explicitly using the
constants
argument:
def ode_fn(t, y, A):
return tf.linalg.matvec(A, y)
with tf.GradientTape() as tape:
tape.watch(A)
results = tfp.math.ode.BDF().solve(ode_fn, t_init, y_init,
solution_times=[t0, t1],
constants={'A': A})
tape.gradient(results.states, A) # Fine.
By default, this uses the same solver for the augmented ODE. This can be
controlled via make_adjoint_solver_fn
.
References
[1]: Chen, Tian Qi, et al. "Neural ordinary differential equations." Advances in Neural Information Processing Systems. 2018.
Args | |
---|---|
ode_fn
|
Function of the form ode_fn(t, y, **constants) . The input t is
a scalar float Tensor . The input y and output are both Tensor s
with the same shape and dtype as initial_state . constants is are
values that are constant with respect to time. Passing the constants
here rather than just closing over them in ode_fn is only necessary if
you want gradients with respect to these values.
|
initial_time
|
Scalar float Tensor specifying the initial time.
|
initial_state
|
N-D float or complex Tensor specifying the initial state.
The dtype of initial_state must be complex for problems with
complex-valued states (even if the initial state is real).
|
solution_times
|
1-D float Tensor specifying a list of times. The solver
stores the computed state at each of these times in the returned
Results object. Must satisfy initial_time <= solution_times[0] and
solution_times[i] < solution_times[i+1] . Alternatively, the user can
pass tfp.math.ode.ChosenBySolver(final_time) where final_time is a
scalar float Tensor satisfying initial_time < final_time . Doing so
requests that the solver automatically choose suitable times up to and
including final_time at which to store the computed state.
|
jacobian_fn
|
Optional function of the form jacobian_fn(t, y) . The input
t is a scalar float Tensor . The input y has the same shape and
dtype as initial_state . The output is a 2N-D Tensor whose shape is
initial_state.shape + initial_state.shape and whose dtype is the
same as initial_state . In particular, the (i1, ..., iN, j1, ...,
jN) -th entry of jacobian_fn(t, y) is the derivative of the (i1, ...,
iN) -th entry of ode_fn(t, y) with respect to the (j1, ..., jN) -th
entry of y . If this argument is left unspecified, the solver
automatically computes the Jacobian if and when it is needed.
Default value: None .
|
jacobian_sparsity
|
Optional 2N-D boolean Tensor whose shape is
initial_state.shape + initial_state.shape specifying the sparsity
pattern of the Jacobian. This argument is ignored if jacobian_fn is
specified.
Default value: None .
|
batch_ndims
|
Optional nonnegative integer. When specified, the first
batch_ndims dimensions of initial_state are batch dimensions.
Default value: None .
|
previous_solver_internal_state
|
Optional solver-specific argument used to
warm-start this invocation of solve .
Default value: None .
|
constants
|
Optional dictionary with string keys and values being (possibly
nested) float Tensor s. These represent values that are constant with
respect to time. Specifying these here allows the adjoint sentitivity
method to compute gradients of the results with respect to these values.
|
Returns | |
---|---|
Object of type Results .
|
Class Variables | |
---|---|
ODE_FN_EVALS_PER_STEP |
6
|
ORDER |
5
|