Skip to content
Merged
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
4 changes: 4 additions & 0 deletions docs/src/submodules/Utilities/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Utilities.UniversalFallback
Utilities.@model
Utilities.GenericModel
Utilities.GenericOptimizer
Utilities.@struct_of_constraints_by_function_types
Utilities.@struct_of_constraints_by_set_types
Utilities.struct_of_constraint_code
```

Expand Down Expand Up @@ -106,6 +108,8 @@ Utilities.OneBasedIndexing

```@docs
Utilities.load_constants
Utilities.function_constants
Utilities.set_from_constants
```

```@docs
Expand Down
65 changes: 38 additions & 27 deletions src/Test/modellike.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function default_status_test(model::MOI.ModelLike)
@test MOI.get(model, MOI.DualStatus()) == MOI.NO_SOLUTION
end

function nametest(model::MOI.ModelLike)
function nametest(model::MOI.ModelLike; delete::Bool = true)
@testset "Variables" begin
MOI.empty!(model)
x = MOI.add_variables(model, 2)
Expand Down Expand Up @@ -69,7 +69,7 @@ function nametest(model::MOI.ModelLike)
MOI.set(model, MOI.ConstraintName(), c1, "c1")
@test_throws ErrorException MOI.get(model, MOI.ConstraintIndex, "c1")
end
@testset "Name test with $(typeof(model))" begin
@testset "Name" begin
MOI.empty!(model)
@test MOI.supports_incremental_interface(model, true) #=copy_names=#
@test MOI.supports(model, MOI.Name())
Expand Down Expand Up @@ -218,25 +218,30 @@ function nametest(model::MOI.ModelLike)
@test MOI.get(model, typeof(ca), nameb) === nothing
end
end
MOI.delete(model, v[2])
@test MOI.get(model, MOI.VariableIndex, "Var2") === nothing
MOI.delete(model, c)
@test MOI.get(model, typeof(c), "Con1") === nothing
@test MOI.get(model, MOI.ConstraintIndex, "Con1") === nothing
MOI.delete(model, x)
@test MOI.get(model, MOI.VariableIndex, "Varx") === nothing
@test MOI.get(model, MOI.ConstraintIndex, "Con3") === nothing
@test MOI.get(model, typeof(c2), "Con2") === c2
@test MOI.get(model, MOI.ConstraintIndex, "Con2") === c2
MOI.delete(model, y)
@test MOI.get(model, typeof(cy), "Con4") === nothing
@test MOI.get(model, MOI.ConstraintIndex, "Con4") === nothing
for i in 1:4
@test MOI.get(model, MOI.VariableIndex, "Vary$i") === nothing
if delete
MOI.delete(model, v[2])
@test MOI.get(model, MOI.VariableIndex, "Var2") === nothing

MOI.delete(model, c)
@test MOI.get(model, typeof(c), "Con1") === nothing
@test MOI.get(model, MOI.ConstraintIndex, "Con1") === nothing

MOI.delete(model, x)
@test MOI.get(model, MOI.VariableIndex, "Varx") === nothing
@test MOI.get(model, MOI.ConstraintIndex, "Con3") === nothing
@test MOI.get(model, typeof(c2), "Con2") === c2
@test MOI.get(model, MOI.ConstraintIndex, "Con2") === c2

MOI.delete(model, y)
@test MOI.get(model, typeof(cy), "Con4") === nothing
@test MOI.get(model, MOI.ConstraintIndex, "Con4") === nothing
for i in 1:4
@test MOI.get(model, MOI.VariableIndex, "Vary$i") === nothing
end
MOI.set(model, MOI.ConstraintName(), c2, "Con4")
@test MOI.get(model, typeof(c2), "Con4") === c2
@test MOI.get(model, MOI.ConstraintIndex, "Con4") === c2
end
MOI.set(model, MOI.ConstraintName(), c2, "Con4")
@test MOI.get(model, typeof(c2), "Con4") === c2
@test MOI.get(model, MOI.ConstraintIndex, "Con4") === c2
end
@testset "Duplicate names" begin
@testset "Variables" begin
Expand All @@ -252,8 +257,10 @@ function nametest(model::MOI.ModelLike)
@test MOI.get(model, MOI.VariableIndex, "y") == y
MOI.set(model, MOI.VariableName(), z, "x")
@test_throws ErrorException MOI.get(model, MOI.VariableIndex, "x")
MOI.delete(model, x)
@test MOI.get(model, MOI.VariableIndex, "x") == z
if delete
MOI.delete(model, x)
@test MOI.get(model, MOI.VariableIndex, "x") == z
end
end
@testset "ScalarAffineFunction" begin
MOI.empty!(model)
Expand All @@ -272,23 +279,27 @@ function nametest(model::MOI.ModelLike)
@test MOI.get(model, MOI.ConstraintIndex, "y") == c[2]
MOI.set(model, MOI.ConstraintName(), c[3], "x")
@test_throws ErrorException MOI.get(model, MOI.ConstraintIndex, "x")
MOI.delete(model, c[1])
@test MOI.get(model, MOI.ConstraintIndex, "x") == c[3]
if delete
MOI.delete(model, c[1])
@test MOI.get(model, MOI.ConstraintIndex, "x") == c[3]
end
end
end
end

# Taken from https://github.com/jump-dev/MathOptInterfaceUtilities.jl/issues/41
function validtest(model::MOI.ModelLike)
function validtest(model::MOI.ModelLike; delete::Bool = true)
MOI.empty!(model)
@test MOI.supports_incremental_interface(model, false) #=copy_names=#
v = MOI.add_variables(model, 2)
@test MOI.is_valid(model, v[1])
@test MOI.is_valid(model, v[2])
x = MOI.add_variable(model)
@test MOI.is_valid(model, x)
MOI.delete(model, x)
@test !MOI.is_valid(model, x)
if delete
MOI.delete(model, x)
@test !MOI.is_valid(model, x)
end
cf = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 1.0], v), 0.0)
@test MOI.supports_constraint(model, typeof(cf), MOI.LessThan{Float64})
c = MOI.add_constraint(model, cf, MOI.LessThan(1.0))
Expand Down
1 change: 1 addition & 0 deletions src/Utilities/Utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ include("copy.jl")
include("results.jl")
include("variables.jl")

include("box.jl")
include("vector_of_constraints.jl")
include("struct_of_constraints.jl")
include("model.jl")
Expand Down
132 changes: 132 additions & 0 deletions src/Utilities/box.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Sets setting lower bound:
extract_lower_bound(set::MOI.EqualTo) = set.value
function extract_lower_bound(
set::Union{MOI.GreaterThan,MOI.Interval,MOI.Semicontinuous,MOI.Semiinteger},
)
return set.lower
end
# 0xcb = 0x80 | 0x40 | 0x8 | 0x2 | 0x1
const LOWER_BOUND_MASK = 0xcb

# Sets setting upper bound:
extract_upper_bound(set::MOI.EqualTo) = set.value
function extract_upper_bound(
set::Union{MOI.LessThan,MOI.Interval,MOI.Semicontinuous,MOI.Semiinteger},
)
return set.upper
end
# 0xcd = 0x80 | 0x40 | 0x8 | 0x4 | 0x1
const UPPER_BOUND_MASK = 0xcd

const SUPPORTED_VARIABLE_SCALAR_SETS{T} = Union{
MOI.EqualTo{T},
MOI.GreaterThan{T},
MOI.LessThan{T},
MOI.Interval{T},
MOI.Integer,
MOI.ZeroOne,
MOI.Semicontinuous{T},
MOI.Semiinteger{T},
}

"""
struct Box{T}
lower::Vector{T}
upper::Vector{T}
end

Stores the constants of scalar constraints with the lower bound of the set in
`lower` and the upper bound in `upper`.
"""
struct Box{T}
lower::Vector{T}
upper::Vector{T}
end

Box{T}() where {T} = Box{T}(T[], T[])

Base.:(==)(a::Box, b::Box) = a.lower == b.lower && a.upper == b.upper

function Base.empty!(b::Box)
empty!(b.lower)
empty!(b.upper)
return b
end

function Base.resize!(b::Box, n)
resize!(b.lower, n)
resize!(b.upper, n)
return
end

function add_free(b::Box{T}) where {T}
push!(b.lower, _no_lower_bound(T))
push!(b.upper, _no_upper_bound(T))
return
end

# Use `-Inf` and `Inf` for `AbstractFloat` subtypes.
_no_lower_bound(::Type{T}) where {T} = zero(T)
_no_lower_bound(::Type{T}) where {T<:AbstractFloat} = typemin(T)
_no_upper_bound(::Type{T}) where {T} = zero(T)
_no_upper_bound(::Type{T}) where {T<:AbstractFloat} = typemax(T)

function load_constants(
b::Box{T},
offset,
set::SUPPORTED_VARIABLE_SCALAR_SETS{T},
) where {T}
flag = single_variable_flag(typeof(set))
if iszero(flag & LOWER_BOUND_MASK)
b.lower[offset+1] = _no_lower_bound(T)
else
b.lower[offset+1] = extract_lower_bound(set)
end
if iszero(flag & UPPER_BOUND_MASK)
b.upper[offset+1] = _no_upper_bound(T)
else
b.upper[offset+1] = extract_upper_bound(set)
end
return
end

function merge_bounds(b::Box, index, set)
flag = single_variable_flag(typeof(set))
if !iszero(flag & LOWER_BOUND_MASK)
b.lower[index] = extract_lower_bound(set)
end
if !iszero(flag & UPPER_BOUND_MASK)
b.upper[index] = extract_upper_bound(set)
end
end

function_constants(::Box{T}, row) where {T} = zero(T)

function set_from_constants(b::Box, ::Type{<:MOI.EqualTo}, index)
return MOI.EqualTo(b.lower[index])
end
function set_from_constants(
b::Box,
S::Type{<:Union{MOI.GreaterThan,MOI.EqualTo}},
index,
)
# Lower and upper bounds are equal for `EqualTo`, we can take either of them.
return S(b.lower[index])
end
function set_from_constants(b::Box, S::Type{<:MOI.LessThan}, index)
return S(b.upper[index])
end
function set_from_constants(
b::Box,
S::Type{<:Union{MOI.Interval,MOI.Semicontinuous,MOI.Semiinteger}},
index,
)
return S(b.lower[index], b.upper[index])
end
function set_from_constants(
::Box,
S::Type{<:Union{MOI.Integer,MOI.ZeroOne}},
index,
)
return S()
end
Loading