-
-
Notifications
You must be signed in to change notification settings - Fork 230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problem changing functions #230
Comments
This is a great suggestion. For the packages of DynamicalSystems.jl I have set up a small interface internally. # ODEProblem helper functions
ODEProblem(ds::ContinuousDS) = ds.prob
ODEProblem(
ds::ContinuousDS, t::Real, state = ds.prob.u0) =
ODEProblem{true}(ds.prob.f, state, (zero(t), t),
callback = ds.prob.callback, mass_matrix = ds.prob.mass_matrix)
ODEProblem(ds::ContinuousDS, tspan::Tuple, state = ds.prob.u0) =
ODEProblem{true}(ds.prob.f, state, tspan,
callback = ds.prob.callback, mass_matrix = ds.prob.mass_matrix)
"""
ODEProblem(ds::ContinuousDS, t, state = ds.prob.u0 [, callback])
Create a new `ODEProblem` for the given dynamical system that final time `t` (or `tspan`
for tuple `t`), `state` and if given, also merges the `callback` with other existing
callbacks currently in `ds.prob`.
"""
function ODEProblem(ds::ContinuousDS, t::Real, state, cb)
if ds.prob.callback == nothing
return ODEProblem{true}(ds.prob.f, state, (zero(t), t),
callback = cb, mass_matrix = ds.prob.mass_matrix)
else
return ODEProblem{true}(ds.prob.f, state, (zero(t), t),
callback = CallbackSet(cb, ds.prob.callback),
mass_matrix = ds.prob.mass_matrix)
end
end (the I think it is safe to say that I should just do a PR to DiffEqBase or to whatever repo @ChrisRackauckas instructs, with the above replacing If there exists some function edit: It should be debated on whether the parameters are keywords or optional arguments, and whether the keyword should be just |
Using dispatch to distinguish the cases here would be prone to bugs. For example, DynamicalODEProblems use a tuple for the |
You mean that I should not use |
Or just make |
I think that change_tspan(prob,tspan)
change_ic(prob,u0)
change_prob(prob;u0 = prob.u0, ...) |
Hm, but this would mean that one cannot use the same function name to change different aspects of the ODEProblem. I kind of dislike that. EDIT: But then again, you can just only ever use |
What about |
Yeah I think that is fine. With everything besides |
This is the implementation I have written up internally at the moment:
I've taken the (I think smart) choice to have two keyword arguments EDIT: I propose this as the canditate for solving the present issue. (EDIT: obviously replacing |
Here is an accompanying documentation string:
(clearly I'll replace |
Here is an idea to solve this in general with three ingredients. In short, my suggestion is to define keyword-only constructors so that re-creation is easy. First, let's define an API to extract a constructor of problems and other structs (BTW, is there a way to do this in Julia in more automated way? This sounds silly. I'm too Julia noob...) For basic struct, I guess you can either do some introspection and/or macro to slightly automate this part. constructor_of(::ODEProblem) = ODEProblem
constructor_of(::DiscreteProblem) = DiscreteProblem
constructor_of(::SDEProblem) = SDEProblem
constructor_of(::SplitFunction) = SplitFunction # not just problem types!
# ... and so on Second, let's define keyword-only constructors. Note this requires field names and arguments have to have one-to-one correspondence. Julia 1.0 would make this part easier JuliaLang/julia#25830 : ODEProblem(;
f = error("No argument f"),
u0 = error("No argument u0"),
tspan = error("No argument tspan"),
p = nothing,
kwargs...) =
ODEProblem(f, u0, tspan, p; kwargs) Finally, define reset(prob::DEProblem; kwargs...) =
constructor_of(prob)(;
[n => getfield(prob, n) for n in fieldnames(prob)]...,
kwargs...) Note that this still require users to call old_prob = SplitODEProblem(...)
new_prob = reset(old_prob; f = reset(old_prob.f; f1 = new_f1)) OK. In this case, by intercepting the constructor mapping by |
Oops, the signature Also, this is so general and I started to wonder if Parameters.jl or some other libraries already have it. |
Ah, of course, |
I'm not sure Parameters.jl allows inner constructors, in which case we can't. |
Might as well just make it named the same as the constructor like in @Datseris 's instead of |
I don't think using the constructor as resetting API is a good choice. What happens in the code that I don't know what the problem type is but just want to reset Besides, if users have to call BTW, full-automation to define """
constructor_of(thing)::Type
Return a type/constructor for `thing`.
"""
function constructor_of end
for n in names(DiffEqBase)
v = try
getfield(DiffEqBase, n)
catch
continue
end
if v isa Type && ! (v isa Union) && isempty(subtypes(v))
eval(:(constructor_of(::DiffEqBase.$n) = DiffEqBase.$n))
end
end |
Just for clarity: my suggestion is not about automation. It's about more factorized API (keyword-only constructor and |
Obviously, constructor_of(::T) where T = getfield(T.name.module, Symbol(T.name.name)) |
So you mean, define a generic |
Can you define a generic Here is the minimal implementation of my suggestion. Once you have the keyword-only constructor defined somehow, the rest is straightforward: using Parameters: type2dict
using DiffEqBase
import DiffEqBase: ODEProblem
# Manual implementation of the keyword-only constructor:
ODEProblem(;
f = error("No argument f"),
u0 = error("No argument u0"),
tspan = error("No argument tspan"),
p = nothing,
problem_type = StandardODEProblem(),
kwargs...) =
ODEProblem(f, u0, tspan, p; kwargs...)
constructor_of(::T) where T = getfield(T.name.module, Symbol(T.name.name))
remake(prob; kwargs...) = constructor_of(prob)(; type2dict(prob)..., kwargs...)
using Base.Test
@testset "remake" begin
function f(du,u,p,t)
du[1] = 0.2u[1]
du[2] = 0.4u[2]
end
u0 = ones(2)
tspan = (0,1.0)
prob1 = ODEProblem(f, u0, tspan; mass_matrix=eye(length(u0)))
new_u0 = [2.0, 2.0]
new_p = Dict()
prob2 = remake(prob1; u0 = new_u0, p = new_p)
@test prob1.f === prob2.f
@test prob1.mass_matrix === prob2.mass_matrix
@test prob2.u0 == new_u0
@test prob2.p === new_p
end Of course, we can instead define "re-constructors" ( remake(prob; kwargs...) = constructor_of(prob)(prob; type2dict(prob)..., kwargs...) The reason why I thought the implementation based on the keyword-only constructor was better was that it can be used to define the "re-constructors" but the reverse was not true. In a way, the keyword-only constructor is more fundamental. But this isn't a big deal anyway. I think defining the keyword-only constructor and/or "re-constructors" via macro/introspection are not so hard. I thought defining Anyway, the point I want to emphasize is that |
I am running into a problem where |
An interface for problem changing functions would be useful. It would help when creating
MonteCarloProblem
s for other types of problems thanODEProblem
s.The text was updated successfully, but these errors were encountered: