Hi everyone,
I’m trying to define what is in effect a new Problem in SciML, but what actually is a special case of an existing Problem. As I can’t derive from an instantiated type, I’m making a new type with an instance of the existing Problem as a member. Here’s my code, which is horrible, but works.The explanation is below, and I’m looking for suggestions on how to fit in more with SciML coding conventions.
import SciMLBase: @add_kwonly, DiscreteProblem
import OrdinaryDiffEq: solve, FunctionMap
import Random: AbstractRNG
struct MarkovProblem{uType, tType, P, F, K}
f::F
u0::uType
tspan::Union{Tuple{tType,tType}, tType}
p::P
dt::tType
rng::AbstractRNG
prob::DiscreteProblem
kwargs::K
function MarkovProblem(f, u0, tspan, p, dt, rng; kwargs...)
new{typeof(u0), typpeof(dt), typeof(p), typeof(f), typeof(kwargs)}(
f,
u0,
tspan,
p,
dt,
rng,
DiscreteProblem((du,u,p,t) -> f(du,u,p,t,dt,rng),
u0,
tspan,
p;
#dt=dt,
kwargs...)
)
end
@add_kwonly function MarkovProblem(f, u0, tspan, p, dt, rng; kwargs...)
new{typeof(u0), typeof(dt), typeof(p), typeof(f), typeof(kwargs)}(
f,
u0,
tspan,
p,
dt,
rng,
DiscreteProblem((du,u,p,t) -> f(du,u,p,t,dt,rng),
u0,
tspan,
p;
#dt=dt,
kwargs...)
)
end
end
function solve(prob::MarkovProblem)
solve(prob.prob, FunctionMap(), dt=prob.dt)
end
function solve(prob::MarkovProblem, alg; kwargs...)
solve(prob.prob, alg, dt=prob.dt)
end
## Test
using Random
using Distributions
function sir_markov!(du,u,p,t,dt,rng)
(S,I) = u
(β,γ) = p
ifrac = 1-exp(-β*I*dt)
rfrac = 1-exp(-γ*dt)
infection=rand(rng, Binomial(S,ifrac))
recovery=rand(rng, Binomial(I,rfrac))
du[1] = S-infection
du[2] = I+infection-recovery
nothing
end
tspan = (0.0, 40.0)
dt = 0.1
u0 = [990, 10] # S,I
p = [0.0005, 0.25] # β,γ
rng = Xoshiro(1234)
## Solve direct using DiscreteProblem
discrete_prob = DiscreteProblem((du, u, p, t; kwargs...) -> sir_markov!(du,u,p,t,dt,rng; kwargs...),
u0,tspan,p)
discrete_sol = solve(discrete_prob, FunctionMap(), dt=dt)
## Solve using MarkovProblem
markov_prob = MarkovProblem(sir_markov!, u0, tspan, p, dt, rng)
markov_sol = solve(markov_prob)
My aim is to be able to solve models where in addition to the usual ODEProblem signature of (du,u,p,t), I have (du,u,p,t,dt,rng), where dt is the time step, and rng is an AbstractRNG, so I can have per-simulation instances of random number generators with their own seed.
Here are some questions:
- What should
solve
(as well asinit
andstep!
) actually look like? - How can I define a
MarkovFunction
class that fits with the current e.g.ODEFunction
template? - How can I best get a
MarkovSolution
that keeps all the standard solution-handling in SciML? - Does my use of imports and using look OK?
- How can I specify one keyword argument (dt) but then be able to splat others (I commented out the line above)?
- How can my definition of
MarkovProblem
be improved?
The documentation in this area is a bit sparse, and I haven’t really delved deep into types in Julia before, so any pointers would be great!
Thanks!