-
Notifications
You must be signed in to change notification settings - Fork 155
[RFC/WIP] don't propagate NaN/Inf if Partials are zero #179
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
Conversation
|
The approach I ended up going with here is to make Unfortunately, I'm seeing about performance regressions of ~10% on this branch vs. master when benchmarking some DiffBase functions, and that's probably going to be near the lower bound on such regressions for this change - I'm only benchmarking cases where it only needs to do the Maybe it would be better to create a module-level flag for enabling this change so that users can opt-in only if they need it, preserving performance for users who don't need it. cc @mlubin @KristofferC for your opinions. |
src/partials.jl
Outdated
| #----------------------# | ||
|
|
||
| @inline function @compat(Base.:*)(partials::Partials, x::Real) | ||
| x = ifelse((isnan(x) || isinf(x)) && iszero(partials), one(x), x) |
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.
!isfinite could be better than isnan || isinf
|
10% seems quite a hefty penalty in my opinion. One way could be to define most operations for an AbstractDual and then have two concrete types where the NaN behaviour is dispatched on. I don't really like this way though. Feels a bit too intrusive. |
… checking isnan or isinf
"This way" referring to this PR (and the proposed "users switch this on/off via a module-level constant") or "this way" referring to your I do think the performance hit is too great here to make this the default behavior. The most convenient API for this, IMO, would be adding something like a |
From switching to |
|
The module-level option is problematic for packages that use ForwardDiff under the hood. What is JuMP supposed to do? |
|
I thought that introducing a second concrete Dual number type is perhaps a quite large change to solve this problem (at least compared to the solution you have now). A 5% performance penalty might be acceptable to skip the headache of two concrete types? I haven't really run into this problem myself so I feel I can't really weigh options against eachother in an informed way. |
Really good point. It's definitely bothersome that whatever module-level option the user sets would apply to all downstream module usage, when they might instead want to only apply it selectively (e.g. have In a future PR, we could add |
| - if (julia -e 'VERSION < v"0.5" && exit(1)'); then | ||
| julia -e 'include(joinpath(JULIA_HOME, Base.DATAROOTDIR, "julia", "build_sysimg.jl")); build_sysimg(force=true)'; | ||
| julia -e 'Pkg.clone(pwd()); Pkg.build("ForwardDiff"); Pkg.test("ForwardDiff"; coverage=true)'; | ||
| julia -e 'Pkg.clone(pwd()); Pkg.build("ForwardDiff"); Pkg.checkout("DiffBase"); Pkg.test("ForwardDiff"; coverage=true)'; |
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.
Do you still need to do this? These kind of things should be very temporary, otherwise you're not testing against the same versions of things your users will have installed.
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.
DiffBase serves as a repository of test functions. If I didn't do this, I'd have to re-tag DiffBase every time I wanted to add a new shared test function, which would get really annoying.
I share your concern though. I've been meaning to factor the shared test suite out of DiffBase and into an unregistered package (DiffTestSuite or something). Then DiffTestSuite could be a test-only dependency that I Pkg.checkout every time, and I could go back to CI-testing DiffBase's functionality with the proper versions. That's what I probably should've done this in the first place...I'll set it up tomorrow.
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.
Pkg.add("ForwardDiff"); Pkg.test("ForwardDiff") should pass. If that requires tagging more often, tag more often. Pkg.test shouldn't depend on unregistered packages.
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.
Same dependency versioning issue applies as everywhere else - if it's needed to pass tests and doesn't exist in every version of DiffBase, then at least test/REQUIRE needs to have a corresponding minimum version requirement.
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.
Fair enough - I'll fix this to use tagged DiffBase versions and start tagging DiffBase more often, then.
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.
I don't mind if you want to run additional tests on CI-only, or have things temporarily checked out to branches on CI if that's necessary while things are being developed -- as long as you keep in mind how that configuration will differ from what users will have installed, and that Pkg.test should work for them on releases too.
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.
Yeah, no worries! My future workflow will be to keep master branch's CI running against tagged dependency versions for the reasons you mentioned. I can always toggle a PR's CI config to use whatever DiffBase branch I want during development, and then before merging, I can tag the new DiffBase version (if necessary) and switch the config back to using only tagged versions.
|
This is what I expect to see after flipping the julia> Inf*ForwardDiff.Dual{Float64}(1.0,0.0)
Dual{Float64}(Inf,0.0)but should I expect to see julia> Inf*ForwardDiff.Dual{Float64}(1.0,0.0,1.0)
Dual{Float64}(Inf,NaN,Inf)? |
|
Hmm. Yeah, it would probably be consistent for us to check/propagate all partials individually when |
For anyone coming across this PR - this was fixed by #777 in ForwardDiff >= 1.2.2. With the latest release of ForwardDiff (and NaN-safe mode enabled): julia> Inf*ForwardDiff.Dual{Float64}(1.0,0.0)
Dual{Float64}(Inf,0.0)
julia> Inf*ForwardDiff.Dual{Float64}(1.0,0.0,1.0)
Dual{Float64}(Inf,0.0,Inf)
It actually improved performance of NaN-safe mode 🙂 |
This aims to fix #178, and any related issues with other functions.
I am not totally sure this does the right thing for nested Dual numbers; perhaps the perturbation check should be recursive?
There is a slight performance penalty here which is more or less significant depending on the function. I'm also interested to see what it does to SIMD performance.
EDIT:Removed a snippet of an alternate idea that I've now decided against.