-
-
Notifications
You must be signed in to change notification settings - Fork 241
Description
I think it's time that we put down a design for a type-based DSL which can specify ODEs, higher order ODEs, SDEs, regular jumps, DAEs, DDEs, PDEs, and all combinations. I think we actually get a DSL for linear and nonlinear solve problems for free. It sounds terrifying, but the specification doesn't seem all that bad after giving it a try.
I think I can just explain by examples. First there's the simple example for the fully type-based form:
# Define some variables
x = DependentVariable(:x)
y = DependentVariable(:y)
z = DependentVariable(:z)
t = IndependentVariable(:t)
D = Derivative(t) # Default of first derivative, Derivative(t,1)
σ = Parameter(:σ)
ρ = Parameter(:ρ)
β = Parameter(:β)
# Define some expressions
eqs = [D*x == σ*(y-x)
D*y == x*(ρ-z)-y
D*z == x*y - β*z]
# Define a differential equation
de = DiffEq(eqs)
# Now go to the DiffEq pipeline
prob = ODEProblem(de,u0,tspan,p)
Now we start adding sugar:
@DV x y z
@IV t
@Parameter σ,ρ,β
D = Derivative(t)
de = @diffeq begin
D*x = σ*(y-x)
D*y = x*(ρ-z)-y
D*z = x*y - β*z
end
prob = ODEProblem(de,u0,tspan,p)
Now we start adding features.
# Array Variables
x = DependentVariable(:x,1:10) # Array of variables
@DV x[1:10]
# Can do the same on IndependentVariable, Parameters, ...
# Noise
dW = NoiseVariable(:dW,3)
# Regular Jump
dp = JumpVariable(5x) # rate(u,p,t) = 5u[1]
# Delayed Variable
y_τ = y(t-5)
y_τ2 = y(t-f)
# Mass matrix DAE
de = @diffeq begin
D*x + D*y = σ*(y-x)
D*y = x*(ρ-z)-y
D*z = x*y - β*z
end
# Fully Implicit SDAE
de = @diffeq begin
exp(D*x + D*y) = σ*(y-x) + dW[1]
D*y = x*(ρ-z)-y + dW[2]
D*z = x*y - β*z + dW_3 # Same as dW[3]
end
# Define explicit orderings for variables, parameters, etc.
de = DiffEq(eqs,dep_vars,indep_vars,parameters,noises)
Now we start specifying some PDEs:
@DV u
@IV x t
D_t = Derivative(t)
D_x = Derivative(x)
D_xx = D_x*D_x
D_xx == Derivative(x,2)
D_t*u == D_xx*u
# Assume μ(u,p,t,x) <: Function and σ(u,p,t,x) <: Function are defined
L = Operator(mu*D_x + σ*D_xx)
D_t*u = L*u
And as an added bonus
# Nonlinear rootfinding problem
nl = @nl begin
σ*(y-x)
x*(ρ-z)-y
x*y - β*z
end
nlprob = NonlinearProblem(nl,u0)
# Now send this over to NLsolve with pre-defined Jacobians!
# Similar for linear
# Possible extension down the line
nl = @nl begin
σ*(y-x) + x*(ρ-z)-y + x*y - β*z
end
scalar_function = Scalar(nl) # Defines and computes Jacobians/Hessians, send to Optim!
Extra Features
The purpose of this is not to do a full physical modeling setup (though it should be possible to build one off of it), but it's to make it easy to specify/modify/optimize differential equations in a natural form that's both computer and human readable. Also, if Modia.jl is going to come out then I don't see a reason to directly build something like that, so I don't plan to spend much time in feature areas that they focus on (but of course those extensions can be readily added). But, there's definitely a few things from Modelica that we can put in here.
One thing is the possibility of initial conditions and parameter values in the model specification.
x = DependentVariable(:x,2.0)
@DV x 2.0 y 3.0 z 4.0
σ = Parameter(:σ,3.0)
Then u0
and p
wouldn't be needed in the ODEProblem
construction.
Model composition can come later. It could be something like:
struct Lorenz <: DiffEqComponent
x
y
z::Flow
end
eqs = [D*x == σ*(y-x)
D*y == x*(ρ-z)-y
D*z == x*y - β*z]
lorenz = Component(Lorenz,eqs)
or
@component struct Lorenz
x
y
z::Flow
end begin
D*x = σ*(y-x)
D*y = x*(ρ-z)-y
D*z = x*y - β*z
end
and then have some kind of connect
. This is stuff that can be added later for people to build modeling frameworks, but it's not truly essential to the original goal.
Implementation details
Most of the implementation details are pretty obvious. It's a bunch of expressions with small types. But a few things to mention. Variables, parameters, etc. have direct conversions to SymEngine.Basic
s. This is then makes it easy to do math on them.
The "missing" implementation detail is that any arithmetic operation forms a DEStatement
which holds the components by their types by their addition (differential statements, jump statements, noise statements, base statements). ==
then generates a DEquation
with a statement on the lhs
and a statement on the rhs
.
Remaining Issues
- How do you specify history functions (for DDEs)?
Deprecation Path
There won't truly be a "deprecation" of @ode_def
since it is still nice. Instead, it would just output a DiffEq
when this is ready, so it would be directly incorporated into the next DSL.
Remarks
Based off of SciML/DiffEqDSL.jl#1
Pinging @tshort @gabrielgellner @zahachtah @mauro3 due to how helpful you all were in round one!
@korsbo and @Gaussia probably have great ideas as well. @vjd and @simonbyrne