SciML / DiffEqBase.jl

The lightweight Base library for shared types and functionality for defining differential equation and scientific machine learning (SciML) problems
Other
302 stars 106 forks source link

Accept both in-place and out-of-place styles in ODE function call #361

Open jlumpe opened 4 years ago

jlumpe commented 4 years ago

A while ago I filed a PR to allow ODEFunction to be called with both in-place and out-of-place signatures regardless of whether the actual wrapped function is in place. This was reverted because it ended up breaking DynamicalODEFunction, but I still think this would be a very useful feature. I always define my ODE functions to work in place for performance reasons, but quite often want to get the output of the function for testing or debugging and its a pain to always have to pre-allocate an array for it.

The original PR works for most applications, but the problem is that DynamicalODEFunction uses ODEFunctions that expect a different number of arguments than the standard. One solution I can think of is to add (yet another) type parameter that indicates how many arguments are in the standard out-of-place signature (3 standard, 4 with DynamicalODEFunction) so that you can tell which style is being used and dispatch on that and the iip parameter. That's getting to be a whole lot of parameters, though.

It would be something like this:

# ip -> ip
(f::ODEFunction{false, ..., N})(args:NTuple{Any, N}...) = f.f(args...)

# oop -> ip
(f::ODEFunction{false, ..., N})(du, args:NTuple{Any, N}...) = (du[:] = f(args...))

# ip -> oop
function (f::ODEFunction{true, ..., N})(args:NTuple{Any, N}...)
    du = similar(args[1])
    f(du, args...)
    return du
end

# oop -> oop
(f::ODEFunction{true, ..., N})(du, args:NTuple{Any, N}...) = f.f(args...)
ChrisRackauckas commented 4 years ago

How are you choosing N here? 

I think the fix is that we have to use Julia 1.3 where we can do (f::AbstractODEFunction)(args...) to give a standard form, and then overload (f::DynamicalODEFunction)(args...) for the special case. Or use some kind of trait-like system to know about special properties of the ODEFunction. It's not impossible to do, but just a little tricky.


Applied Mathematics Instructor, Massachusetts Institute of Technology Senior Research Analyst, University of Maryland, Baltimore, School of Pharmacy, Center for Translational Medicine On 10/22/2019 4:00:07 PM, Michael Jared Lumpe notifications@github.com wrote: A while ago I filed a PR [#233] to allow ODEFunction to be called with both in-place and out-of-place signatures regardless of whether the actual wrapped function is in place. This was reverted because it ended up breaking DynamicalODEFunction, but I still think this would be a very useful feature. I always define my ODE functions to work in place for performance reasons, but quite often want to get the output of the function for testing or debugging and its a pain to always have to pre-allocate an array for it. The original PR works for most applications, but the problem is that DynamicalODEFunction uses ODEFunctions that expect a different number of arguments than the standard. One solution I can think of is to add (yet another) type parameter that indicates how many arguments are in the standard out-of-place signature (3 standard, 4 with DynamicalODEFunction) so that you can tell which style is being used and dispatch on that and the iip parameter. That's getting to be a whole lot of parameters, though. It would be something like this:

ip -> ip (f::ODEFunction{false, ..., N})(args:NTuple{Any, N}...) = f.f(args...) # oop -> ip (f::ODEFunction{false, ..., N})(du, args:NTuple{Any, N}...) = (du[:] = f(args...)) # ip -> oop function (f::ODEFunction{true, ..., N})(args:NTuple{Any, N}...) du = similar(args[1]) f(du, args...) return du end # oop -> oop (f::ODEFunction{true, ..., N})(du, args:NTuple{Any, N}...) = f.f(args...)

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub [https://github.com/JuliaDiffEq/DiffEqBase.jl/issues/361?email_source=notifications&email_token=AAN25HWRI76AYJKL5YRLLMLQP5LUDA5CNFSM4JDUSEX2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4HTTKRLA], or unsubscribe [https://github.com/notifications/unsubscribe-auth/AAN25HTQJP26VMVTDUJN76LQP5LUDANCNFSM4JDUSEXQ].

jlumpe commented 4 years ago

N is going to be set by a special constructor argument/method and will default to 3. The issue isn't in the arguments to DynamicalODEFunction itself, it's that its fields f1 and f2 are expected to be ODEFunctions with an oop signature of (u, v, p, t). Basically N will never be used except in the constructor of DynamicalODEFunction.