-
Notifications
You must be signed in to change notification settings - Fork 147
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
Violating assumptions about Dual comparisons #609
Comments
First, perhaps a cleaner version of the complaint is this: julia> using ForwardDiff: Dual
julia> function f_3(x; lb = 0.0, ub = 1.0)
(lb <= x < ub) && return 0.5*x*x
x >= ub && return 0.5*ub*ub + (x - ub)
x < lb && return 0.0*x
missing
end
f_3 (generic function with 1 method)
julia> f_3.(-0.5:0.5:1.5)
5-element Vector{Float64}:
-0.0
0.0
0.125
0.5
1.0
julia> f_3.(Dual.(-0.5:0.5:1.5, 1))
5-element Vector{Dual{Nothing, Float64, 1}}:
Dual{Nothing}(-0.0,0.0)
Dual{Nothing}(0.0,0.0)
Dual{Nothing}(0.125,0.5)
Dual{Nothing}(0.5,1.0)
Dual{Nothing}(1.0,1.0)
julia> f_3.(WrappedNumber.(-0.5:0.5:1.5))
5-element Vector{WrappedNumber{Float64}}:
WrappedNumber{Float64}(-0.0)
WrappedNumber{Float64}(0.0)
WrappedNumber{Float64}(0.125)
WrappedNumber{Float64}(0.5)
WrappedNumber{Float64}(1.0)
julia> f_3.(WrappedNumber.(Dual.(-0.5:0.5:1.5, 1))) # ForwardDiff v0.10.33
5-element Vector{Union{Missing, WrappedNumber{Dual{Nothing, Float64, 1}}}}:
WrappedNumber{Dual{Nothing, Float64, 1}}(Dual{Nothing}(-0.0,0.0))
missing
WrappedNumber{Dual{Nothing, Float64, 1}}(Dual{Nothing}(0.125,0.5))
missing
WrappedNumber{Dual{Nothing, Float64, 1}}(Dual{Nothing}(1.0,1.0)) This code expects that the conditions exhaust all possibilities, but they do not. Because by overloading only julia> Dual(1,2) == Dual(1,3)
false
julia> Dual(1,2) <= Dual(1,3)
true
julia> WrappedNumber(Dual(1,2)) <= WrappedNumber(Dual(1,3))
false
# @less points to this definition: <=(x, y) = (x < y) | (x == y) Note that there is a fallback definition for That would happen had the code chosen to overload say I don't quite know what to suggest here. I don't see how ForwardDiff using a different definition for I know this may not help much, but I never trust myself to write code like the above and not introduce an edge case (which random tests will never find), so perhaps I assumed such expressions were rare in the wild. I would try to write instead single tests, like function f_4(x; lb = 0.0, ub = 1.0)
if x < lb
0.0*x
elseif x < ub # and >= lb
0.5*x*x
else
0.5*ub*ub + (x - ub)
end
end |
Thanks! I'm not super familiar with ForwardDiff internals so I don't have a fix on this side in mind. On my side, yeah, I should be able to rewrite all my multiple comparisons to avoid this issue by only using
Kind of? My understanding is that
The upshot is that you typically want to overload either |
I don't disagree with these bullet points, but overloading Whenever I have tried to make a new number type, I have run into never-ending problems (esp. ambiguities against other such types). There's a package which aims to automate this, and I note that it overloads far more things: https://github.com/SimonDanisch/AbstractNumbers.jl/blob/master/src/overloads.jl |
For sure, my |
Not the same as Dual numbers, but a similar species is provided by Measurements.jl julia> using Measurements
julia> f_3.((-0.5:0.5:1.5) .± 0.001)
5-element Vector{Measurement{Float64}}:
-0.0 ± 0.0
0.0 ± 0.0
0.125 ± 0.0005
0.5 ± 0.001
1.0 ± 0.001
julia> f_3.((WrappedNumber.(-0.5:0.5:1.5)) .± 0.001)
ERROR: MethodError: no method matching measurement(::WrappedNumber{Float64}, ::Float64)
julia> f_3.(WrappedNumber.((-0.5:0.5:1.5) .± 0.001))
5-element Vector{Union{Missing, WrappedNumber{Measurement{Float64}}}}:
WrappedNumber{Measurement{Float64}}(-0.0 ± 0.0)
missing
WrappedNumber{Measurement{Float64}}(0.125 ± 0.0005)
missing
WrappedNumber{Measurement{Float64}}(1.0 ± 0.001) None of these functions work with units: julia> using Unitful
julia> f_3.((-0.5:0.5:1.5) .* 1u"m")
ERROR: DimensionError: 0.0 and -0.5 m are not dimensionally compatible.
julia> f_4.((-0.5:0.5:1.5) .* 1u"m"; lb = 0u"m", ub = 1u"m")
ERROR: DimensionError: 0.5 m² and 0.0 m are not dimensionally compatible. |
Yeah, Measurements.jl also violates the total-order expectation for It seems where I'm coming down is
If you agree, then this issue can probably be closed. |
I think the main alternative would be to have The drawback is that we're saying |
I think that the same rule for #481 tried to change as few things as possible to solve existing issues. #480 argued that one consequence of changing |
There are already a few issues talking about #481 (#606, #607) and related problems (#480). I've been running into breakage hinted at in this comment and this workaround where
This seems to be an issue for piecewise functions and wrapper types that assume
x <= y
is equivalent tox == y || x < y
.For example, something like this previously worked:
And now:
Maybe I'm not supposed to do this. But it's been very convenient, and I suspect this isn't the only case with problems of this kind. Or maybe the issue is just a matter of following through on the changes to
==
andisequal
for other comparisons?The text was updated successfully, but these errors were encountered: