From 7de34075214384330cb84db99c11d8d46ffac1cb Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 4 Sep 2023 10:49:04 +1200 Subject: [PATCH 1/6] Fix promotion rules for GenericNonlinearExpr and VectorNonlinearFunction --- src/nlp_expr.jl | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index 99e28b683ac..81adc20937a 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -673,11 +673,25 @@ function _evaluate_expr( end end -# MutableArithmetics.jl +# MutableArithmetics.jl and promotion -# These converts are used in the {add,sub}mul definition for AbstractJuMPScalar. +function Base.promote_rule( + ::Type{GenericNonlinearExpr{V}}, + ::Type{V}, +) where {V<:AbstractVariableRef} + return GenericNonlinearExpr{V} +end + +function Base.promote_rule( + ::Type{GenericNonlinearExpr{V}}, + ::Type{<:Union{GenericAffExpr{C,V},GenericQuadExpr{C,V}}}, +) where {C,V<:AbstractVariableRef} + return GenericNonlinearExpr{V} +end -Base.convert(::Type{<:GenericNonlinearExpr}, x::AbstractVariableRef) = x +function Base.convert(::Type{GenericNonlinearExpr{V}}, x::V) where {V} + return GenericNonlinearExpr{V}(:+, Any[x]) +end function Base.convert( ::Type{<:GenericNonlinearExpr}, @@ -1097,17 +1111,14 @@ function jump_function( ] end -# We use `AbstractJuMPScalar` as a catch-all fallback for any mix of JuMP -# scalars that have not been dispatched by some other method. - -function moi_function_type(::Type{<:Vector{<:AbstractJuMPScalar}}) +function moi_function_type(::Type{<:AbstractVector{<:GenericNonlinearExpr}}) return MOI.VectorNonlinearFunction end -function moi_function(f::Vector{<:AbstractJuMPScalar}) +function moi_function(f::AbstractVector{<:GenericNonlinearExpr}) return MOI.VectorNonlinearFunction(f) end -function MOI.VectorNonlinearFunction(f::Vector{<:AbstractJuMPScalar}) +function MOI.VectorNonlinearFunction(f::AbstractVector{<:GenericNonlinearExpr}) return MOI.VectorNonlinearFunction(map(moi_function, f)) end From 91a13974d52a44bacf46782fa7502757c15a706f Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 4 Sep 2023 11:04:25 +1200 Subject: [PATCH 2/6] Fix test --- test/test_nlp_expr.jl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/test_nlp_expr.jl b/test/test_nlp_expr.jl index 51d2189ab0d..cec9c1a9b04 100644 --- a/test/test_nlp_expr.jl +++ b/test/test_nlp_expr.jl @@ -745,7 +745,7 @@ function test_VectorNonlinearFunction_moi_function_AbstractJuMPScalar() model = Model() @variable(model, x) F = [sin(x), x] - @test F isa Vector{AbstractJuMPScalar} + @test F isa Vector{NonlinearExpr} @test moi_function_type(typeof(F)) == MOI.VectorNonlinearFunction @test isapprox( moi_function(F), @@ -757,10 +757,7 @@ function test_VectorNonlinearFunction_moi_function_AbstractJuMPScalar() @test MOI.VectorNonlinearFunction(F) ≈ moi_function(F) @test jump_function_type(model, MOI.VectorNonlinearFunction) == Vector{NonlinearExpr} - @test isequal_canonical( - jump_function(model, moi_function(F)), - [sin(x), NonlinearExpr(:+, x)], - ) + @test isequal_canonical(jump_function(model, moi_function(F)), F) return end From dcf808e4fce2c849c7b9a363acf02cb380fc4254 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 4 Sep 2023 11:52:07 +1200 Subject: [PATCH 3/6] Update test_nlp_expr.jl --- test/test_nlp_expr.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_nlp_expr.jl b/test/test_nlp_expr.jl index cec9c1a9b04..6b6826a078f 100644 --- a/test/test_nlp_expr.jl +++ b/test/test_nlp_expr.jl @@ -741,10 +741,10 @@ function test_VectorNonlinearFunction_moi_function() return end -function test_VectorNonlinearFunction_moi_function_AbstractJuMPScalar() +function test_VectorNonlinearFunction_moi_function_conversionn() model = Model() @variable(model, x) - F = [sin(x), x] + F = [sin(x), x, x + 1, x^2] @test F isa Vector{NonlinearExpr} @test moi_function_type(typeof(F)) == MOI.VectorNonlinearFunction @test isapprox( @@ -752,6 +752,8 @@ function test_VectorNonlinearFunction_moi_function_AbstractJuMPScalar() MOI.VectorNonlinearFunction([ MOI.ScalarNonlinearFunction(:sin, Any[index(x)]), MOI.ScalarNonlinearFunction(:+, Any[index(x)]), + MOI.ScalarNonlinearFunction(:+, Any[index(x), 1.0]), + MOI.ScalarNonlinearFunction(:*, Any[index(x), index(x)]), ]), ) @test MOI.VectorNonlinearFunction(F) ≈ moi_function(F) From 4a1f40827d5061094eb1a7513eea4aaa9eb7a5c8 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 4 Sep 2023 13:29:53 +1200 Subject: [PATCH 4/6] Add more tests --- test/test_nlp_expr.jl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/test_nlp_expr.jl b/test/test_nlp_expr.jl index 6b6826a078f..b8e0639f22e 100644 --- a/test/test_nlp_expr.jl +++ b/test/test_nlp_expr.jl @@ -741,7 +741,7 @@ function test_VectorNonlinearFunction_moi_function() return end -function test_VectorNonlinearFunction_moi_function_conversionn() +function test_VectorNonlinearFunction_moi_function_conversion() model = Model() @variable(model, x) F = [sin(x), x, x + 1, x^2] @@ -763,6 +763,26 @@ function test_VectorNonlinearFunction_moi_function_conversionn() return end +function test_VectorNonlinearFunction_moi_function_conversion_variable() + model = Model() + @variable(model, x) + F = [sin(x), x] + @test F isa Vector{NonlinearExpr} + @test moi_function_type(typeof(F)) == MOI.VectorNonlinearFunction + @test isapprox( + moi_function(F), + MOI.VectorNonlinearFunction([ + MOI.ScalarNonlinearFunction(:sin, Any[index(x)]), + MOI.ScalarNonlinearFunction(:+, Any[index(x)]), + ]), + ) + @test MOI.VectorNonlinearFunction(F) ≈ moi_function(F) + @test jump_function_type(model, MOI.VectorNonlinearFunction) == + Vector{NonlinearExpr} + @test isequal_canonical(jump_function(model, moi_function(F)), F) + return +end + function test_VectorNonlinearFunction_objective() model = Model() @variable(model, x) From 42679e3cc3eb08b718bf8222ae4b20c4a5cb598f Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 4 Sep 2023 14:03:19 +1200 Subject: [PATCH 5/6] Add fallback for generic case --- src/aff_expr.jl | 21 +++++++++++++++++++++ test/test_nlp_expr.jl | 8 ++++++++ 2 files changed, 29 insertions(+) diff --git a/src/aff_expr.jl b/src/aff_expr.jl index 75634bee377..f6b3db65361 100644 --- a/src/aff_expr.jl +++ b/src/aff_expr.jl @@ -649,6 +649,27 @@ See also: [`jump_function`](@ref). """ function moi_function end +function moi_function(x::AbstractArray{AbstractJuMPScalar}) + return error( + "Unable to convert array of type `::$(typeof(x))` to an equivalent " * + "function in MathOptInterface because the array has the abstract " * + "element type `AbstractJuMPScalar`. To fix this error, convert every " * + "element in the array to the same concrete element type.\n\n" * + """For example, instead of: + ```julia + model = Model(); + @variable(model, x); + y = AbstractJuMPScalar[x, sin(x)] + @objective(model, Min, y) + ``` + do + ```julia + @objective(model, Min, convert.(NonlinearExpr, y)) + ``` + """, + ) +end + """ moi_function_type(::Type{T}) where {T} diff --git a/test/test_nlp_expr.jl b/test/test_nlp_expr.jl index b8e0639f22e..7a7a1f3aca9 100644 --- a/test/test_nlp_expr.jl +++ b/test/test_nlp_expr.jl @@ -847,4 +847,12 @@ function test_redefinition_of_function() return end +function test_moi_function_abstract_jump_scalar() + model = Model(); + @variable(model, x); + y = AbstractJuMPScalar[x, sin(x)] + @test_throws ErrorException moi_function(y) + return +end + end # module From 700235638b7f2e37f7df0672ac0aa11e11df5349 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 5 Sep 2023 17:51:05 +1200 Subject: [PATCH 6/6] Update src/nlp_expr.jl --- src/nlp_expr.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index c3e54bdb797..9230e3b3bd2 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -1119,7 +1119,7 @@ function moi_function(f::AbstractVector{<:GenericNonlinearExpr}) return MOI.VectorNonlinearFunction(f) end -function MOI.VectorNonlinearFunction(f::AbstractVector{<:GenericNonlinearExpr}) +function MOI.VectorNonlinearFunction(f::Vector{<:AbstractJuMPScalar}) return MOI.VectorNonlinearFunction(map(moi_function, f)) end