forked from jump-dev/MathOptInterface.jl
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add bridge that converts upper/lower constraints to interval constrai…
…nts. Closes jump-dev#1193
- Loading branch information
1 parent
6092c3d
commit 5e0e937
Showing
7 changed files
with
213 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ docs/build/ | |
docs/site/ | ||
test/Benchmarks/*.json | ||
Manifest.toml | ||
*.swp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# The code here is mostly copied from the flip_sign.jl code for FlipSignBridge and GreaterToLessBridge | ||
|
||
""" | ||
AbstractToIntervalBridge{T, S1, F} | ||
Bridge a `F`-in-`S1` constraint into an `F`-in-`Interval` constraint where we have either: | ||
* `S1 = MOI.GreaterThan{T}` | ||
* `S1 = MOI.LessThan{T}` | ||
The `F`-in-`S1` constraint is stored in the `constraint` | ||
field by convention. | ||
""" | ||
abstract type AbstractToIntervalBridge{ | ||
T, S1<:MOI.AbstractSet, | ||
F<:MOI.AbstractFunction} <: SetMapBridge{T, MOI.Interval{T}, S1, F, F} end | ||
|
||
# The function map is the identity. It is also an involution, symmetric, and a symmetric involution. | ||
map_function(::Type{<:AbstractToIntervalBridge{T}}, func) where {T} = func | ||
inverse_map_function(BT::Type{<:AbstractToIntervalBridge}, func) = func | ||
adjoint_map_function(BT::Type{<:AbstractToIntervalBridge}, func) = func | ||
inverse_adjoint_map_function(BT::Type{<:AbstractToIntervalBridge}, func) = func | ||
|
||
# FIXME are these modify functions necessary? | ||
function MOI.modify(model::MOI.ModelLike, bridge::AbstractToIntervalBridge, | ||
change::MOI.ScalarCoefficientChange) | ||
MOI.modify( | ||
model, bridge.constraint, | ||
MOI.ScalarCoefficientChange(change.variable, change.new_coefficient)) | ||
end | ||
function MOI.modify(model::MOI.ModelLike, bridge::AbstractToIntervalBridge, | ||
change::MOI.MultirowChange{T}) where T | ||
MOI.modify(model, bridge.constraint, | ||
MOI.MultirowChange(change.variable, | ||
change.new_coefficients)) | ||
end | ||
|
||
""" | ||
GreaterToIntervalBridge{T, F<:MOI.AbstractScalarFunction} <: | ||
AbstractToIntervalBridge{T, MOI.GreaterThan{T}, F} | ||
Transforms a `F`-in-`GreaterThan{T}` constraint into an `F`-in-`Interval{T}` | ||
constraint. | ||
""" | ||
struct GreaterToIntervalBridge{T, F<:MOI.AbstractScalarFunction} <: | ||
AbstractToIntervalBridge{T, MOI.GreaterThan{T}, F} | ||
constraint::CI{F, MOI.Interval{T}} | ||
end | ||
map_set(::Type{<:GreaterToIntervalBridge}, set::MOI.GreaterThan) = MOI.Interval(set.lower, typemax(set.lower)) | ||
inverse_map_set(::Type{<:GreaterToIntervalBridge}, set::MOI.Interval) = MOI.GreaterThan(set.lower) | ||
function concrete_bridge_type(::Type{<:GreaterToIntervalBridge{T}}, | ||
F::Type{<:MOI.AbstractScalarFunction}, | ||
::Type{MOI.GreaterThan{T}}) where T | ||
return GreaterToIntervalBridge{T, F} | ||
end | ||
|
||
""" | ||
LessToIntervalBridge{T, F<:MOI.AbstractScalarFunction} <: | ||
AbstractToIntervalBridge{T, MOI.LessThan{T}, F} | ||
Transforms a `F`-in-`LessThan{T}` constraint into an `F`-in-`Interval{T}` | ||
constraint. | ||
""" | ||
struct LessToIntervalBridge{T, F<:MOI.AbstractScalarFunction} <: | ||
AbstractToIntervalBridge{T, MOI.LessThan{T}, F} | ||
constraint::CI{F, MOI.Interval{T}} | ||
end | ||
map_set(::Type{<:LessToIntervalBridge}, set::MOI.LessThan) = MOI.Interval(typemin(set.upper), set.upper) | ||
inverse_map_set(::Type{<:LessToIntervalBridge}, set::MOI.Interval) = MOI.LessThan(set.upper) | ||
function concrete_bridge_type(::Type{<:LessToIntervalBridge{T}}, | ||
F::Type{<:MOI.AbstractScalarFunction}, | ||
::Type{MOI.LessThan{T}}) where T | ||
return LessToIntervalBridge{T, F} | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# These tests are mostly copies of the flip_sign.jl tests for GreaterToLess | ||
|
||
using Test | ||
|
||
using MathOptInterface | ||
const MOI = MathOptInterface | ||
const MOIT = MathOptInterface.Test | ||
const MOIU = MathOptInterface.Utilities | ||
const MOIB = MathOptInterface.Bridges | ||
|
||
include("../utilities.jl") | ||
|
||
mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) | ||
config = MOIT.TestConfig() | ||
|
||
@testset "GreaterToInterval" begin | ||
bridged_mock = MOIB.Constraint.GreaterToInterval{Float64}(mock) | ||
|
||
MOIT.basic_constraint_tests( | ||
bridged_mock, config, | ||
include = [(F, S) | ||
for F in [MOI.ScalarAffineFunction{Float64}, | ||
MOI.ScalarQuadraticFunction{Float64}] | ||
for S in [MOI.GreaterThan{Float64}]]) | ||
|
||
MOIU.set_mock_optimize!(mock, | ||
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.0, 0.0]), | ||
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100.0, 0.0]), | ||
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100.0, -100.0])) | ||
MOIT.linear6test(bridged_mock, config) | ||
|
||
ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}}())) | ||
|
||
@testset "$attr" for attr in [MOI.ConstraintPrimalStart(), MOI.ConstraintDualStart()] | ||
@test MOI.supports(bridged_mock, attr, typeof(ci)) | ||
MOI.set(bridged_mock, attr, ci, 2.0) | ||
@test MOI.get(bridged_mock, attr, ci) ≈ 2.0 | ||
end | ||
|
||
test_delete_bridge(bridged_mock, ci, 2, | ||
((MOI.ScalarAffineFunction{Float64}, | ||
MOI.Interval{Float64}, 0), | ||
)) | ||
end | ||
|
||
|
||
@testset "LessToInterval" begin | ||
bridged_mock = MOIB.Constraint.LessToInterval{Float64}(mock) | ||
|
||
MOIT.basic_constraint_tests( | ||
bridged_mock, config, | ||
include = [(F, S) | ||
for F in [MOI.SingleVariable, MOI.ScalarAffineFunction{Float64}, | ||
MOI.ScalarQuadraticFunction{Float64}] | ||
for S in [MOI.LessThan{Float64}]]) | ||
|
||
MOIU.set_mock_optimize!(mock, | ||
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, | ||
MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0]), | ||
MOI.FEASIBLE_POINT, | ||
(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.0] | ||
), | ||
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, | ||
MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [2.0]), | ||
MOI.FEASIBLE_POINT, | ||
(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.0] | ||
) | ||
) | ||
MOIT.solve_set_scalaraffine_lessthan(bridged_mock, config) | ||
|
||
MOIU.set_mock_optimize!(mock, | ||
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, | ||
MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0]), | ||
MOI.FEASIBLE_POINT, | ||
(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.0] | ||
), | ||
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, | ||
MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.5]), | ||
MOI.FEASIBLE_POINT, | ||
(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-0.5] | ||
) | ||
) | ||
MOIT.solve_coef_scalaraffine_lessthan(bridged_mock, config) | ||
|
||
ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}}())) | ||
|
||
@testset "$attr" for attr in [MOI.ConstraintPrimalStart(), MOI.ConstraintDualStart()] | ||
@test MOI.supports(bridged_mock, attr, typeof(ci)) | ||
MOI.set(bridged_mock, attr, ci, 2.0) | ||
@test MOI.get(bridged_mock, attr, ci) ≈ 2.0 | ||
end | ||
|
||
test_delete_bridge(bridged_mock, ci, 1, | ||
((MOI.ScalarAffineFunction{Float64}, | ||
MOI.Interval{Float64}, 0),)) | ||
end | ||
|
||
|
||
@testset "GreaterOrLessToInterval_unmocked" begin | ||
""" | ||
Dummy optimizer that supports Interval only | ||
""" | ||
|
||
mutable struct Optimizer <: MOI.AbstractOptimizer | ||
function Optimizer() | ||
return new() | ||
end | ||
end | ||
|
||
MOI.get(model::Optimizer, ::MOI.SolverName) = "OnlyIntervalOptimizer" | ||
|
||
MOI.supports_constraint(::Optimizer, ::Type{MOI.ScalarAffineFunction{Float64}}, ::Type{MOI.Interval{Float64}}) = true | ||
|
||
|
||
# model supports Interval but not LessThan or GreaterThan | ||
model = Optimizer() | ||
@test MOI.supports_constraint(model, MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) | ||
@test !MOI.supports_constraint(model, MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) | ||
@test !MOI.supports_constraint(model, MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) | ||
|
||
# bridged model supports all | ||
bridged = MOIB.Constraint.GreaterToInterval{Float64}(MOIB.Constraint.LessToInterval{Float64}(model)) | ||
@test MOI.supports_constraint(bridged, MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) | ||
@test MOI.supports_constraint(bridged, MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) | ||
@test MOI.supports_constraint(bridged, MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters