From 3b60aa9ec7f44fe633e4bcf97730cb4cc58c650d Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Sun, 26 Nov 2023 23:18:51 +0300 Subject: [PATCH 1/4] Provide `Base.length(::AbstractJuMPScalar)` ``` 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 ... ``` --- src/JuMP.jl | 1 + test/test_variable.jl | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/JuMP.jl b/src/JuMP.jl index 1ceb3e88831..9bc01c96cad 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -1059,6 +1059,7 @@ LinearAlgebra.adjoint(scalar::AbstractJuMPScalar) = conj(scalar) Base.iterate(x::AbstractJuMPScalar) = (x, true) Base.iterate(::AbstractJuMPScalar, state) = nothing Base.isempty(::AbstractJuMPScalar) = false +Base.length(::AbstractJuMPScalar) = 1 # Check if two arrays of AbstractJuMPScalars are equal. Useful for testing. function isequal_canonical( diff --git a/test/test_variable.jl b/test/test_variable.jl index 6f3f50205ae..cfa83ec768e 100644 --- a/test/test_variable.jl +++ b/test/test_variable.jl @@ -1601,4 +1601,11 @@ function test_bad_bound_types() return end +function test_variable_length() + model = Model() + @variable(model, x) + @test length(x) == 1 + return +end + end # module TestVariable From bc09f55e9c3167a669b47eaa942f084322da0603 Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Mon, 27 Nov 2023 00:46:06 +0300 Subject: [PATCH 2/4] Provide `Base.IteratorEltype()` / `Base.eltype()` ``` 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 ``` --- src/JuMP.jl | 3 +++ test/test_variable.jl | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/JuMP.jl b/src/JuMP.jl index 9bc01c96cad..659709c6b41 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -1049,6 +1049,9 @@ function owner_model end Base.ndims(::Type{<:AbstractJuMPScalar}) = 0 Base.ndims(::AbstractJuMPScalar) = 0 +Base.IteratorEltype(::Type{<:AbstractJuMPScalar}) = Base.HasEltype() +Base.eltype(::Type{T}) where {T<:AbstractJuMPScalar} = T + # These are required to create symmetric containers of AbstractJuMPScalars. LinearAlgebra.symmetric_type(::Type{T}) where {T<:AbstractJuMPScalar} = T LinearAlgebra.hermitian_type(::Type{T}) where {T<:AbstractJuMPScalar} = T diff --git a/test/test_variable.jl b/test/test_variable.jl index cfa83ec768e..773c2527a2d 100644 --- a/test/test_variable.jl +++ b/test/test_variable.jl @@ -1608,4 +1608,12 @@ function test_variable_length() return end +function test_variable_eltype() + model = Model() + @variable(model, x) + @test Base.IteratorEltype(x) == Base.HasEltype() + @test Base.eltype(typeof(x)) == typeof(x) + return +end + end # module TestVariable From 61ef5f58384a67b837afaf5052dedae2ca54ef86 Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Mon, 27 Nov 2023 01:32:29 +0300 Subject: [PATCH 3/4] Provide `Base.oneunit()` ``` 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 ``` --- src/aff_expr.jl | 6 ++++++ src/variables.jl | 6 ++++++ test/test_variable.jl | 10 ++++++++++ 3 files changed, 22 insertions(+) diff --git a/src/aff_expr.jl b/src/aff_expr.jl index 86570a9ad2b..4c76f6ddc2e 100644 --- a/src/aff_expr.jl +++ b/src/aff_expr.jl @@ -204,8 +204,14 @@ function Base.one(::Type{GenericAffExpr{C,V}}) where {C,V} return GenericAffExpr{C,V}(one(C), OrderedDict{V,C}()) end +function Base.oneunit(::Type{GenericAffExpr{C,V}}) where {C,V} + return GenericAffExpr{C,V}(oneunit(C), OrderedDict{V,C}()) +end + Base.one(a::GenericAffExpr) = one(typeof(a)) +Base.oneunit(a::GenericAffExpr) = oneunit(typeof(a)) + Base.copy(a::GenericAffExpr) = GenericAffExpr(copy(a.constant), copy(a.terms)) Base.broadcastable(a::GenericAffExpr) = Ref(a) diff --git a/src/variables.jl b/src/variables.jl index 203722e9e00..7a4a16b5088 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -361,10 +361,16 @@ end Base.one(v::AbstractVariableRef) = one(typeof(v)) +Base.oneunit(v::AbstractVariableRef) = oneunit(typeof(v)) + function Base.one(::Type{V}) where {V<:AbstractVariableRef} return one(GenericAffExpr{value_type(V),V}) end +function Base.oneunit(::Type{V}) where {V<:AbstractVariableRef} + return oneunit(GenericAffExpr{value_type(V),V}) +end + """ coefficient(v1::GenericVariableRef{T}, v2::GenericVariableRef{T}) where {T} diff --git a/test/test_variable.jl b/test/test_variable.jl index 773c2527a2d..c3006b610c6 100644 --- a/test/test_variable.jl +++ b/test/test_variable.jl @@ -1616,4 +1616,14 @@ function test_variable_eltype() return end +function test_variable_one() + model = Model() + @variable(model, x) + @test Base.one(x) == 1 + @test Base.one(2 * x) == 1 + @test Base.oneunit(x) == 1 + @test Base.oneunit(2 * x) == 1 + return +end + end # module TestVariable From 0405c3c67bbf1203d7ce2f93ed60e7701efa2903 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 27 Nov 2023 15:05:07 +1300 Subject: [PATCH 4/4] Apply suggestions from code review --- test/test_variable.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_variable.jl b/test/test_variable.jl index c3006b610c6..22c8d951144 100644 --- a/test/test_variable.jl +++ b/test/test_variable.jl @@ -1619,10 +1619,10 @@ end function test_variable_one() model = Model() @variable(model, x) - @test Base.one(x) == 1 - @test Base.one(2 * x) == 1 - @test Base.oneunit(x) == 1 - @test Base.oneunit(2 * x) == 1 + @test one(x) == AffExpr(1.0) + @test one(2 * x) == AffExpr(1.0) + @test oneunit(x) == AffExpr(1.0) + @test oneunit(2 * x) == AffExpr(1.0) return end