Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/src/submodules/Utilities/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,13 @@ Utilities.add_set
Utilities.rows
Utilities.num_rows
Utilities.set_with_dimension
```

```@docs
Utilities.ProductOfSets
Utilities.MixOfScalarSets
Utilities.@mix_of_scalar_sets
Utilities.OrderedProductOfSets
Utilities.@product_of_sets
Utilities.RuntimeProductOfSets
Utilities.add_set_type
```

## Fallbacks
Expand Down
79 changes: 79 additions & 0 deletions src/Utilities/product_of_sets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,82 @@ function MOI.is_valid(
end
return 1 <= ci.value <= length(sets.rows[i])
end

"""
RuntimeProductOfSets{T}()

An alternative to [`Utilities.@product_of_sets`](@ref), in which the set types
are decided at runtime using [`Utilities.add_set_type`](@ref).

## Examples

```jldoctest
julia> import MathOptInterface as MOI

julia> sets = MOI.Utilities.RuntimeProductOfSets{Int}();

julia> MOI.Utilities.set_types(sets)
Type[]

julia> MOI.Utilities.add_set_type(sets, MOI.Nonpositives)

julia> MOI.Utilities.set_types(sets)
1-element Vector{Type}:
MathOptInterface.Nonpositives
```
"""
mutable struct RuntimeProductOfSets{T} <: OrderedProductOfSets{T}
rows::Vector{Vector{UnitRange{Int}}}
final_touch::Bool
set_types::Vector{Type}

function RuntimeProductOfSets{T}() where {T}
return new(Vector{UnitRange{Int}}[], false, Type[])
end
end

function set_index(
set::RuntimeProductOfSets,
::Type{S},
) where {S<:MOI.AbstractSet}
return findfirst(==(S), set.set_types)
end

set_types(set::RuntimeProductOfSets) = set.set_types

"""
add_set_type(
set::RuntimeProductOfSets,
::Type{S},
) where {S<:MOI.AbstractSet}

Declare that the [`Utilities.RuntimeProductOfSets`](@ref) `set` supports the
[`MOI.AbstractSet`](@ref) `S`.

## Examples

```jldoctest
julia> import MathOptInterface as MOI

julia> sets = MOI.Utilities.RuntimeProductOfSets{Int}();

julia> MOI.Utilities.set_types(sets)
Type[]

julia> MOI.Utilities.add_set_type(sets, MOI.Nonpositives)

julia> MOI.Utilities.set_types(sets)
1-element Vector{Type}:
MathOptInterface.Nonpositives
```
"""
function add_set_type(
set::RuntimeProductOfSets,
::Type{S},
) where {S<:MOI.AbstractSet}
if set_index(set, S) === nothing
push!(set.rows, Vector{UnitRange{Int}}[])
push!(set.set_types, S)
end
return
end
65 changes: 65 additions & 0 deletions test/Utilities/test_product_of_sets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,71 @@ function test_property_num_sets()
return
end

function test_product_of_sets()
sets = MOI.Utilities.RuntimeProductOfSets{Int}()
@test MOI.Utilities.set_types(sets) == Type[]
MOI.Utilities.add_set_type(sets, MOI.Nonpositives)
@test MOI.Utilities.set_types(sets) == Type[MOI.Nonpositives]
MOI.Utilities.add_set_type(sets, MOI.Nonnegatives)
MOI.Utilities.add_set_type(sets, MOI.EqualTo{Int})
@test MOI.Utilities.set_types(sets) ==
Type[MOI.Nonpositives, MOI.Nonnegatives, MOI.EqualTo{Int}]
MOI.Utilities.add_set_type(sets, MOI.EqualTo{Int})
@test MOI.Utilities.set_types(sets) ==
Type[MOI.Nonpositives, MOI.Nonnegatives, MOI.EqualTo{Int}]
i1 = MOI.Utilities.set_index(sets, MOI.Nonpositives)
@test i1 == 1 # The tests below explicitly use this ordering.
i2 = MOI.Utilities.set_index(sets, MOI.Nonnegatives)
@test i2 == 2 # The tests below explicitly use this ordering.
i3 = MOI.Utilities.set_index(sets, MOI.EqualTo{Int})
@test i3 == 3 # The tests below explicitly use this ordering.
MOI.Utilities.add_set(sets, i1, 0)
MOI.Utilities.add_set(sets, i1, 2)
MOI.Utilities.add_set(sets, i2, 0)
MOI.Utilities.add_set(sets, i2, 1)
MOI.Utilities.add_set(sets, i1, 0)
MOI.Utilities.add_set(sets, i1, 1)
MOI.Utilities.add_set(sets, i3)
MOI.Utilities.add_set(sets, i1, 0)
@test MOI.Utilities.num_rows(sets, MOI.Nonpositives) == 3
@test MOI.Utilities.num_rows(sets, MOI.Nonnegatives) == 1
@test MOI.Utilities.num_rows(sets, MOI.EqualTo{Int}) == 1
MOI.Utilities.final_touch(sets)
# MOI.Nonpositives
F, S = MOI.VectorAffineFunction{Int}, MOI.Nonpositives
@test (F, S) in MOI.get(sets, MOI.ListOfConstraintTypesPresent())
@test MOI.get(sets, MOI.NumberOfConstraints{F,S}()) == 5
c = MOI.ConstraintIndex{F,S}.(1:5)
@test MOI.get(sets, MOI.ListOfConstraintIndices{F,S}()) == c
@test all(MOI.is_valid(sets, ci) for ci in c)
@test MOI.Utilities.num_rows(sets, S) == 3
@test MOI.Utilities.rows(sets, c[1]) == 1:0
@test MOI.Utilities.rows(sets, c[2]) == 1:2
@test MOI.Utilities.rows(sets, c[3]) == 3:2
@test MOI.Utilities.rows(sets, c[4]) == 3:3
@test MOI.Utilities.rows(sets, c[5]) == 4:3
# MOI.Nonnegatives
F, S = MOI.VectorAffineFunction{Int}, MOI.Nonnegatives
@test (F, S) in MOI.get(sets, MOI.ListOfConstraintTypesPresent())
@test MOI.get(sets, MOI.NumberOfConstraints{F,S}()) == 2
c = MOI.ConstraintIndex{F,S}.(1:2)
@test MOI.get(sets, MOI.ListOfConstraintIndices{F,S}()) == c
@test all(MOI.is_valid(sets, ci) for ci in c)
@test MOI.Utilities.num_rows(sets, S) == 1
@test MOI.Utilities.rows(sets, c[1]) == 4:3
@test MOI.Utilities.rows(sets, c[2]) == 4:4
# MOI.EqualTo
F, S = MOI.ScalarAffineFunction{Int}, MOI.EqualTo{Int}
@test (F, S) in MOI.get(sets, MOI.ListOfConstraintTypesPresent())
@test MOI.get(sets, MOI.NumberOfConstraints{F,S}()) == 1
c = MOI.ConstraintIndex{F,S}.(1:1)
@test MOI.get(sets, MOI.ListOfConstraintIndices{F,S}()) == c
@test all(MOI.is_valid(sets, ci) for ci in c)
@test MOI.Utilities.num_rows(sets, S) == 1
@test MOI.Utilities.rows(sets, c[1]) == 5
return
end

end

TestProductOfSets.runtests()
Loading