Turns an ordinary differential equation written for
deSolve::ode() into a pinn_problem(). The same derivative function
can be used to solve the system (a forward problem) or, with unknown
and data, to estimate its parameters from observations.
Usage
pinn_ode(
func,
y0,
times,
parms = NULL,
unknown = NULL,
positive = TRUE,
data = NULL,
time = "time",
hard_initial = TRUE
)Arguments
- func
A derivative function
function(t, y, parms)indeSolvestyle. See Writing the model.- y0
Named numeric vector of initial states at the first time.
- times
Numeric vector of times. Only its range is used for training; the full vector becomes the default grid for
predict().- parms
Named numeric vector or list of known parameters.
- unknown
Named numeric vector of initial guesses for parameters to estimate. These are passed to
funcalongsideparms.- positive
Logical; see
pinn_problem().- data
Optional data frame of observations with a column named as
timeand one column per observed state. The output ofdeSolve::ode()converted withas.data.frame()works directly.- time
Name of the time variable. The default,
"time", matches the column name indeSolveoutput.- hard_initial
Logical; enforce the initial state exactly. See Initial conditions.
Value
A pinn_problem() object.
Writing the model
func has the usual deSolve signature
function(t, y, parms) and returns a list whose first element holds
the derivatives, in the order of y0. The recommended style works
unchanged:
function(t, y, parms) {
with(as.list(c(y, parms)), {
dS <- -beta * S * I
...
list(c(dS, dI, dR))
})
}Indexing with y["S"], y[1] or parms["beta"] also works. Because
the states are torch tensors, the body must use arithmetic and maths
functions; if, ifelse, max and pmin on states do not work.
Forcing functions of time can be written with tensor operations, for
example torch_sin(t) or sin(t).
Initial conditions
By default the initial state is enforced
exactly by writing each state as
\(y(t) = y_0 + \frac{t - t_0}{t_1 - t_0} N(t)\), where \(N\) is the
network. This removes one loss term and usually speeds up training.
Set hard_initial = FALSE to use a soft penalty instead.
Examples
# Logistic growth dN/dt = r N (1 - N / K)
logistic <- function(t, y, parms) {
with(as.list(c(y, parms)), list(r * N * (1 - N / K)))
}
prob <- pinn_ode(logistic, y0 = c(N = 0.1), times = seq(0, 10, by = 0.5),
parms = c(r = 1, K = 1))
prob
#> <pinn_problem>
#> Domain: time in [0, 10]
#> Outputs: N
#> Parameters: r = 1, K = 1
#> Output transform: yes