Skip to content

Commit

Permalink
Fix promotion rules for GenericNonlinearExpr (#3483)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Sep 5, 2023
1 parent 37d5b70 commit f496535
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 14 deletions.
21 changes: 21 additions & 0 deletions src/aff_expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
27 changes: 19 additions & 8 deletions src/nlp_expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -675,11 +675,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},
Expand Down Expand Up @@ -1099,14 +1113,11 @@ 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

Expand Down
39 changes: 33 additions & 6 deletions test/test_nlp_expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -744,26 +744,45 @@ function test_VectorNonlinearFunction_moi_function()
return
end

function test_VectorNonlinearFunction_moi_function_AbstractJuMPScalar()
function test_VectorNonlinearFunction_moi_function_conversion()
model = Model()
@variable(model, x)
F = [sin(x), x]
@test F isa Vector{AbstractJuMPScalar}
F = [sin(x), x, x + 1, x^2]
@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)]),
MOI.ScalarNonlinearFunction(:+, Any[index(x), 1.0]),
MOI.ScalarNonlinearFunction(:*, Any[index(x), 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)),
[sin(x), NonlinearExpr(:+, x)],
@test isequal_canonical(jump_function(model, moi_function(F)), F)
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

Expand Down Expand Up @@ -831,6 +850,14 @@ 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

function test_linear_algebra_errors()
model = Model()
@variable(model, x[1:2, 1:2])
Expand Down

0 comments on commit f496535

Please sign in to comment.