Skip to content

Commit 7ee339c

Browse files
committed
Function interface specs
1 parent 5979f5e commit 7ee339c

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

src/DiffEqBase.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ module DiffEqBase
55
using RecipesBase, Parameters, RecursiveArrayTools
66
using Ranges # For plot recipes with units
77
import Base: length, size, getindex, endof, show, print,
8-
next, start, done, eltype, eachindex
8+
next, start, done, eltype, eachindex,
9+
Callable
910

1011
"`DEProblem`: Defines differential equation problems via its internal functions"
1112
abstract DEProblem
@@ -46,6 +47,7 @@ module DiffEqBase
4647
include("solutions/solution_interface.jl")
4748
include("tableaus.jl")
4849
include("problems/ode_problems.jl")
50+
include("problems/functions.jl")
4951

5052
function solve end
5153

src/problems/functions.jl

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Functions for AbstractODEProblem and possibly other DEProblem types
2+
#
3+
# Implements passing in Jacobians (and possibly other functions) via
4+
# function overloading:
5+
#
6+
# - f(...) - objective function
7+
# - f(Val{:jac}, ...) - Jacobian of objective function
8+
# - the details of what `...` needs to be depends on the
9+
# AbstractODEProblem subtype
10+
11+
12+
# Method_exists does not work:
13+
#
14+
# julia> f(::Val{:jac}, a, b, c) = 5
15+
# f (generic function with 1 method)
16+
#
17+
# julia> method_exists(f, Tuple{Val{:jac}, Vararg})
18+
# false
19+
#
20+
# Thus hand-code it:
21+
check_first_arg(f,T::Type) = check_first_arg(typeof(f),T)
22+
function check_first_arg{F}(::Type{F}, T::Type)
23+
typ = Tuple{Any, T, Vararg}
24+
for m in Base.MethodList(F.name.mt) # F.name.mt gets the method-table
25+
m.sig<:typ && return true
26+
end
27+
return false
28+
end
29+
30+
has_jac(f) = check_first_arg(f, Val{:jac})
31+
32+
# Using SimpleTraits to dispatch on existence of jac-method
33+
using SimpleTraits
34+
@traitdef HasJac{F}
35+
@generated SimpleTraits.trait{F}(::Type{HasJac{F}}) = has_jac(F) ? :(HasJac{F}) : :(Not{HasJac{F}})
36+
# now a trait methods can dispatch on this:
37+
# @traitfn fn(g::::HasJac, ...) = ...
38+
# @traitfn fn(g::::(!HasJac), ...) = ...
39+
40+
41+
######################
42+
## Tests (TODO move to test/...)
43+
#####################
44+
f123(x,y) = 1
45+
f123(::Val{:jac}, x) = 2
46+
47+
g123(x,y) = 1
48+
g123{T}(::Val{:jac}, x::T) = 2
49+
50+
h123(x,y) = 1
51+
h123{T}(x::T) = 2
52+
53+
immutable T123 end
54+
(::T123)(a) = 1
55+
(::T123)(::Val{:jac}, a) = 1
56+
57+
immutable G123 end
58+
(::G123)(a) = 1
59+
(::G123)(b, a) = 1
60+
61+
@assert has_jac(f123)
62+
@assert has_jac(g123)
63+
@assert !has_jac(h123)
64+
@assert has_jac(T123())
65+
@assert !has_jac(G123())
66+
# Arguably below could be ==false as T123 is a constructor
67+
# function. However, I'm not sure how to implement this.
68+
@assert has_jac(T123)
69+
@assert !has_jac(G123)
70+
71+
@traitfn tfn(f::::HasJac) = true
72+
@traitfn tfn(f::::(!HasJac)) = false
73+
74+
@assert tfn(f123)
75+
@assert tfn(g123)
76+
@assert !tfn(h123)
77+
@assert tfn(T123())
78+
@assert !tfn(G123())
79+
@assert !tfn(T123) # NOTE this is inconsistent as has_jac(T123)==true
80+
@assert !tfn(G123)
81+
82+
# ParameterizedFunction:
83+
type LotkaVolterra <: ParameterizedFunction
84+
a::Float64
85+
b::Float64
86+
end
87+
(p::LotkaVolterra)(t,u,du) = begin
88+
du[1] = p.a * u[1] - p.b * u[1]*u[2]
89+
du[2] = -3 * u[2] + u[1]*u[2]
90+
end
91+
(p::LotkaVolterra)(::Val{:jac}, t, u, J) = 1
92+
93+
lv = LotkaVolterra(0.0,0.0)
94+
95+
@assert has_jac(lv)
96+
@assert tfn(lv)

0 commit comments

Comments
 (0)