Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
82 changes: 82 additions & 0 deletions src/Utilities/product_of_sets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,85 @@ 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)
true

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
[`AbstractSet`](@ref) `S`.
Comment thread
odow marked this conversation as resolved.
Outdated

## 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)
true

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)
return true
end
return false
end
62 changes: 62 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,68 @@ 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}]
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