-
-
Notifications
You must be signed in to change notification settings - Fork 116
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
WIP: Function interface specs #2
Conversation
I think this is a fundamental issue, not an implementation issue. If having a trait can be inferred at compile time, then it must be constant (or at least, the What we could do is have a different |
Actually, the way around it, is to call (Note the constant function used for trait-dispatch is |
That seems fine to me. It would just go in the same place of the documentation as defining the Jacobian (I want to make a full example for that first, and then below have the full interface. That way most users will likely define just the Jacobian which is fine: the others probably can't be done by hand). The second probably needs a better title: https://juliadiffeq.github.io/DiffEqDocs.jl/latest/features/performance_overloads.html#Declaring-Explicit-Jacobians-1 |
return false | ||
end | ||
|
||
has_jac(f) = check_first_arg(f, Val{:jac}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least as stated before, the interface has a capital here:
https://juliadiffeq.github.io/DiffEqDocs.jl/latest/features/performance_overloads.html
f(Val{:Jac},t,u,J) # Call the explicit Jacobian function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sorry, I'm not up to date with the exact specific. Thanks for the link.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, would you mind updating the readme with links to all relevant places in the docs? (I have no overview)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
@assert has_jac(T123()) | ||
@assert !has_jac(G123()) | ||
# Arguably below could be ==false as T123 is a constructor | ||
# function. However, I'm not sure how to implement this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is weird but not a show-stopper
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just need to change to :Jac
The tests should move to /test and I think the following test should be used to make sure the traits option is a compile-time check: function testing(f)
if has_jac(f)
a = 2
else
a = 3.0
end
a
end
@inferred testing(f) I think we should also just go forward with the SimpleTraits.jl dependency: it has a compelling enough use case. After this goes through I'll set ParameterizedFunctions.jl up with traits. Could you also expand the interface to have the other checks as well? |
The other way is that ParameterizedFunctions and the docs can switch to |
I think it's fine either way as long as we're consistent. Actually, to be consistent with variable use, I'd think |
I took it to JuliaPraxis, and after some discussion agree it should change to |
ParameterizedFunctions.jl was changed to have everything de-captialized ( |
I looked into this. It seems that as I was saying before, any method which uses the methods list / table will not be able to compute this at compile time. We could go through the trouble of creating a workaround that is at runtime and etc. etc., but really, the user experience isn't bad at all: f123(x,y) = 1
f123(::Val{:jac}, x) = 2
@traitimpl HasJac{typeof(f123)} If we just have it documented to put that one line after the Jacobian declaration, then the three lines: @traitdef HasJac{F}
@traitfn has_jac(x::::HasJac) = true
@traitfn has_jac(x::::(!HasJac)) = false makes everything work at compile time. If someone is looking through the manual for how to declare Jacobians by hand (something which is already done automatically for many systems of ODEs), I think they can spare that one line of code. If we just accept that, then everything works perfectly. I am willing to take that option, and if you want to work on a macro to make it easier: @add_jac f(t,u,J) = ... # Makes f(Val{:jac},t,u,J) and does @traitimpl then I think that's a nice and easy solution for both developers and users. |
@traitfn testing(f::::HasJac) =2
@traitfn testing(f::::!(HasJac))=3.0
@code_llvm testing(f123)
@code_llvm testing(g123)
@code_llvm testing(h123) I am really dumb. After playing with it a bit, I see if you use the Since this all works, I'll pull this in and add the other traits. Looks like there's a merge conflict to handle though. |
I got it! @traitdef HasJac{F}
__has_jac(f) = check_first_arg(f, Val{:jac})
@generated SimpleTraits.trait{F}(::Type{HasJac{F}}) = __has_jac(F) ? :(HasJac{F}) : :(Not{HasJac{F}})
has_jac{T}(f::T) = istrait(HasJac{T}) Only check the methods table to generate the trait and you're good. Now it auto-finds the trait and |
Concering comment SciML/Roadmap#5 (comment):
I only saw the
jac_exists
function after I put this together. However, neitherhas_jac
norjac_exits
passes the test in above comment.However, when using a trait-function it would be fully inferred. Note however, that the traits would struggle with adding, say a jacobian, to the function. It may would still dispatch to the wrong one.