Skip to content

Commit

Permalink
Throw error if Containers.SparseAxisArray used in constraint function
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Feb 16, 2024
1 parent a21e616 commit c53e704
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/macros/@constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,51 @@ function build_constraint(
return VectorConstraint(x, set)
end

function build_constraint(
error_fn::Function,
::Containers.SparseAxisArray{<:Union{Number,AbstractJuMPScalar},1},
::MOI.AbstractVectorSet,
)
return error_fn(
"""
Building a constraint in which the function is a `Containers.SparseAxisArray`.
## Work-around
Convert `x` into a vector by explicitly providing an ordered list of
indices. For example, instead of:
```julia
A = [[1, 2, 10], [2, 3, 30]]
model = Model()
@variable(model, x[i in 1:2, j in A[i]])
@constraint(model, x[1, :] in SecondOrderCone())
```
do:
```julia
A = [[1, 2, 10], [2, 3, 30]]
model = Model()
@variable(model, x[i in 1:2, j in A[i]])
y = [x[1, j] for j in A[1]]::Vector{VariableRef}
@constraint(model, y in SecondOrderCone())
```
## Explanation for breaking change in JuMP v1.21
Prior to JuMP v1.20, this constraint would have been allowed. It is now
an error because of the high risk of incorrect usage.
The backend data structure of a `Containers.SparseAxisArray` is a
`Dict`. JuMP used to create the constraint with
`[x[k] for k in eachindex(x)]`, however, the iteration order of
`eachindex` is undefined. This can silently cause incorrect models to be
built if the set of the constraint depends on the order of the elements.
"""
)
end

function build_constraint(
error_fn::Function,
x::AbstractArray,
Expand Down
15 changes: 15 additions & 0 deletions test/test_macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2358,4 +2358,19 @@ function test_op_or_short_circuit()
return
end

function test_sparseaxisarray_constraint_zeros()
A = [[1, 2, 10], [2, 3, 30]]
model = Model()
@variable(model, x[i in 1:2, j in A[i]])
@test_throws_runtime(
ErrorException,
@constraint(model, x[1, :] == 0),
)
@test_throws_runtime(
ErrorException,
@constraint(model, x[1, :] in SecondOrderCone()),
)
return
end

end # module

0 comments on commit c53e704

Please sign in to comment.