-
-
Notifications
You must be signed in to change notification settings - Fork 398
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
Add a few missing methods for AbstractJuMPScalar
to support e.g. Distances.jl
#3585
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #3585 +/- ##
=======================================
Coverage 98.39% 98.39%
=======================================
Files 38 38
Lines 5655 5664 +9
=======================================
+ Hits 5564 5573 +9
Misses 91 91 ☔ View full report in Codecov by Sentry. |
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 would just add tests for the new methods that you've added. I don't think we should add Distances.jl as a test dependency just for this.
Aha, doing that now... |
This is another example where Julia's lack of a formal interface specification hurts us. It's really hard to know what third-party packages expect from the implementer of a set of methods. |
FWIW, i'm a bit surprised |
10812cc
to
5fcaeee
Compare
``` using JuMP, Ipopt, Distances model = Model(Ipopt.Optimizer) @variable(model, a) @variable(model, b) euclidean(a, b) ``` Results in: ``` ERROR: MethodError: no method matching length(::VariableRef) Closest candidates are: length(::Union{Base.KeySet, Base.ValueIterator}) @ Base abstractdict.jl:58 length(::Union{LinearAlgebra.Adjoint{T, S}, LinearAlgebra.Transpose{T, S}} where {T, S}) @ LinearAlgebra /builddirs/julia/usr/share/julia/stdlib/v1.9/LinearAlgebra/src/adjtrans.jl:295 length(::Union{SparseArrays.FixedSparseVector{Tv, Ti}, SparseArrays.SparseVector{Tv, Ti}} where {Tv, Ti}) @ SparseArrays /builddirs/julia/usr/share/julia/stdlib/v1.9/SparseArrays/src/sparsevector.jl:95 ... ```
``` julia> euclidean(a, b) ERROR: MethodError: no method matching oneunit(::Type{Any}) Closest candidates are: oneunit(::Type{Union{Missing, T}}) where T @ Base missing.jl:105 oneunit(::Type{T}) where T @ Base number.jl:370 oneunit(::T) where T @ Base number.jl:369 ... Stacktrace: [1] oneunit(#unused#::Type{Any}) @ Base ./missing.jl:106 [2] _eval_start(d::Euclidean, #unused#::Type{Any}, #unused#::Type{Any}, #unused#::Nothing) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:320 [3] _eval_start(d::Euclidean, #unused#::Type{Any}, #unused#::Type{Any}) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:318 [4] eval_start(d::Euclidean, a::VariableRef, b::VariableRef) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:317 [5] _evaluate(d::Euclidean, a::VariableRef, b::VariableRef, #unused#::Nothing) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:236 [6] (::Euclidean)(a::VariableRef, b::VariableRef) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:328 [7] top-level scope @ REPL[10]:1 ```
5fcaeee
to
53f3e38
Compare
Because they can be things other than In general, there is some need in Julia for As a general comment, the composability of Julia is beneficial, but it often leads to more problems that it is worth. JuMP has been very conservative in how we interact with other Julia packages. When we find things that are broken, we'll generally improve the error message rather than enable new features. |
53f3e38
to
837549c
Compare
``` julia> euclidean(a, b) ERROR: MethodError: no method matching VariableRef(::AffExpr) Closest candidates are: (::Type{GenericVariableRef{T}} where T)(::Any, ::Any) @ JuMP ~/.julia/packages/JuMP/h0lrf/src/variables.jl:251 GenericVariableRef{T}(::ConstraintRef{GenericModel{T}, <:MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex}}) where T @ JuMP ~/.julia/packages/JuMP/h0lrf/src/variables.jl:520 GenericVariableRef{T}(::GenericModel{T}) where T @ JuMP ~/.julia/packages/JuMP/h0lrf/src/variables.jl:494 Stacktrace: [1] oneunit(#unused#::Type{VariableRef}) @ Base ./number.jl:370 [2] _eval_start(d::Euclidean, #unused#::Type{VariableRef}, #unused#::Type{VariableRef}, #unused#::Nothing) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:320 [3] _eval_start(d::Euclidean, #unused#::Type{VariableRef}, #unused#::Type{VariableRef}) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:318 [4] eval_start(d::Euclidean, a::VariableRef, b::VariableRef) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:317 [5] _evaluate(d::Euclidean, a::VariableRef, b::VariableRef, #unused#::Nothing) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:236 [6] (::Euclidean)(a::VariableRef, b::VariableRef) @ Distances ~/.julia/packages/Distances/PvoXa/src/metrics.jl:328 [7] top-level scope @ REPL[8]:1 ```
837549c
to
61ef5f5
Compare
@odow thank you! |
Right, but so what, so can the
Ah, i guess i see what the bigger picture/problem is now:
But i'm rather disappointed that JuMP has to invent it's own |
Yes 😄. As a general comment, we've put a lot of thought into the design of JuMP. If something seems odd, it's probably that way for a reason.
Development of JuMP started in 2013, long before Symbolics existed. When we added |
Currently,
AbstractJuMPScalar
is somewhat partial, and e.g.Distances.jl
does not want to play along:Results in:
That is solved by providing
Base.length(::AbstractJuMPScalar) = 1
But fixing that we hit yet another issue,
with a call to
oneunit()
with bogusType{Any}
.There, the fix appears to be providing
Base.IteratorEltype()
/Base.eltype()
,this looks a bit weird to me, but seems symmetrical with e.g.
Int32
?And then we finally hit the missing
Base.oneunit(a::GenericAffExpr)
,and since i don't believe there are any types here,
it's identical to the
one()
.Afterwards, we finally get the right answer:
Thanks!