Skip to content

Commit 78e7db2

Browse files
authored
[Bridges] Add IntervalToHyperRectangle constraint bridge (#2754)
1 parent 252ca45 commit 78e7db2

File tree

6 files changed

+357
-12
lines changed

6 files changed

+357
-12
lines changed

src/Bridges/Constraint/Constraint.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ function add_all_bridges(model, ::Type{T}) where {T}
137137
MOI.Bridges.add_bridge(model, ToVectorQuadraticBridge{T})
138138
MOI.Bridges.add_bridge(model, VectorFunctionizeBridge{T})
139139
MOI.Bridges.add_bridge(model, VectorizeBridge{T})
140+
MOI.Bridges.add_bridge(model, IntervalToHyperRectangleBridge{T})
140141
MOI.Bridges.add_bridge(model, VectorSlackBridge{T})
141142
MOI.Bridges.add_bridge(model, ZeroOneBridge{T})
142143
return
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
"""
8+
IntervalToHyperRectangleBridge{T,F,G} <: Bridges.Constraint.AbstractBridge
9+
10+
`IntervalToHyperRectangleBridge` implements the following reformulations:
11+
12+
* ``l \\le g(x) \\le u`` into ``[g(x)] \\in `` `MOI.HyperRectangle([l], [u])`
13+
14+
where `T` is the coefficient type of `g(x)` and the type of `l` and `u`.
15+
16+
See also [`VectorizeBridge`](@ref) for equality and single-sided bound
17+
constraints.
18+
19+
## Source node
20+
21+
`IntervalToHyperRectangleBridge` supports:
22+
23+
* `G` in [`MOI.Interval{T}`](@ref)
24+
25+
## Target nodes
26+
27+
`IntervalToHyperRectangleBridge` creates:
28+
29+
* `F` in [`MOI.HyperRectangle{T}`](@ref).
30+
"""
31+
mutable struct IntervalToHyperRectangleBridge{T,F,G} <: AbstractBridge
32+
vector_constraint::MOI.ConstraintIndex{F,MOI.HyperRectangle{T}}
33+
end
34+
35+
const IntervalToHyperRectangle{T,OT<:MOI.ModelLike} =
36+
SingleBridgeOptimizer{IntervalToHyperRectangleBridge{T},OT}
37+
38+
function bridge_constraint(
39+
::Type{IntervalToHyperRectangleBridge{T,F,G}},
40+
model::MOI.ModelLike,
41+
scalar_f::G,
42+
set::MOI.Interval{T},
43+
) where {T,F,G}
44+
MOI.throw_if_scalar_and_constant_not_zero(scalar_f, typeof(set))
45+
vector_f = MOI.Utilities.operate(vcat, T, scalar_f)
46+
rect = MOI.HyperRectangle([set.lower], [set.upper])
47+
vector_constraint = MOI.add_constraint(model, vector_f, rect)
48+
return IntervalToHyperRectangleBridge{T,F,G}(vector_constraint)
49+
end
50+
51+
function MOI.supports_constraint(
52+
::Type{IntervalToHyperRectangleBridge{T}},
53+
::Type{F},
54+
::Type{MOI.Interval{T}},
55+
) where {T,F<:MOI.AbstractScalarFunction}
56+
return MOI.Utilities.is_coefficient_type(F, T)
57+
end
58+
59+
function MOI.Bridges.added_constrained_variable_types(
60+
::Type{<:IntervalToHyperRectangleBridge},
61+
)
62+
return Tuple{Type}[]
63+
end
64+
65+
function MOI.Bridges.added_constraint_types(
66+
::Type{<:IntervalToHyperRectangleBridge{T,F}},
67+
) where {T,F}
68+
return Tuple{Type,Type}[(F, MOI.HyperRectangle{T})]
69+
end
70+
71+
function concrete_bridge_type(
72+
::Type{<:IntervalToHyperRectangleBridge{T}},
73+
G::Type{<:MOI.AbstractScalarFunction},
74+
S::Type{MOI.Interval{T}},
75+
) where {T}
76+
F = MOI.Utilities.promote_operation(vcat, T, G)
77+
return IntervalToHyperRectangleBridge{T,F,G}
78+
end
79+
80+
function MOI.get(
81+
::IntervalToHyperRectangleBridge{T,F},
82+
::MOI.NumberOfConstraints{F,MOI.HyperRectangle{T}},
83+
)::Int64 where {T,F}
84+
return 1
85+
end
86+
87+
function MOI.get(
88+
bridge::IntervalToHyperRectangleBridge{T,F},
89+
::MOI.ListOfConstraintIndices{F,MOI.HyperRectangle{T}},
90+
) where {T,F}
91+
return [bridge.vector_constraint]
92+
end
93+
94+
function MOI.delete(
95+
model::MOI.ModelLike,
96+
bridge::IntervalToHyperRectangleBridge,
97+
)
98+
MOI.delete(model, bridge.vector_constraint)
99+
return
100+
end
101+
102+
function MOI.supports(
103+
model::MOI.ModelLike,
104+
attr::Union{MOI.ConstraintPrimalStart,MOI.ConstraintDualStart},
105+
::Type{IntervalToHyperRectangleBridge{T,F,G}},
106+
) where {T,F,G}
107+
return MOI.supports(
108+
model,
109+
attr,
110+
MOI.ConstraintIndex{F,MOI.HyperRectangle{T}},
111+
)
112+
end
113+
114+
function MOI.get(
115+
model::MOI.ModelLike,
116+
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
117+
bridge::IntervalToHyperRectangleBridge,
118+
)
119+
x = MOI.get(model, attr, bridge.vector_constraint)
120+
if isnothing(x)
121+
return nothing
122+
end
123+
return only(x)
124+
end
125+
126+
function MOI.set(
127+
model::MOI.ModelLike,
128+
attr::MOI.ConstraintPrimalStart,
129+
bridge::IntervalToHyperRectangleBridge,
130+
value,
131+
)
132+
MOI.set(model, attr, bridge.vector_constraint, [value])
133+
return
134+
end
135+
136+
function MOI.set(
137+
model::MOI.ModelLike,
138+
attr::MOI.ConstraintPrimalStart,
139+
bridge::IntervalToHyperRectangleBridge,
140+
::Nothing,
141+
)
142+
MOI.set(model, attr, bridge.vector_constraint, nothing)
143+
return
144+
end
145+
146+
function MOI.get(
147+
model::MOI.ModelLike,
148+
attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart},
149+
bridge::IntervalToHyperRectangleBridge,
150+
)
151+
x = MOI.get(model, attr, bridge.vector_constraint)
152+
if isnothing(x)
153+
return nothing
154+
end
155+
return only(x)
156+
end
157+
158+
function MOI.set(
159+
model::MOI.ModelLike,
160+
attr::MOI.ConstraintDualStart,
161+
bridge::IntervalToHyperRectangleBridge,
162+
value,
163+
)
164+
if isnothing(value)
165+
MOI.set(model, attr, bridge.vector_constraint, nothing)
166+
else
167+
MOI.set(model, attr, bridge.vector_constraint, [value])
168+
end
169+
return
170+
end
171+
172+
function MOI.modify(
173+
model::MOI.ModelLike,
174+
bridge::IntervalToHyperRectangleBridge,
175+
change::MOI.ScalarCoefficientChange,
176+
)
177+
MOI.modify(
178+
model,
179+
bridge.vector_constraint,
180+
MOI.MultirowChange(change.variable, [(1, change.new_coefficient)]),
181+
)
182+
return
183+
end
184+
185+
function MOI.set(
186+
model::MOI.ModelLike,
187+
attr::MOI.ConstraintSet,
188+
bridge::IntervalToHyperRectangleBridge,
189+
new_set::MOI.Interval,
190+
)
191+
MOI.set(
192+
model,
193+
attr,
194+
bridge.vector_constraint,
195+
MOI.HyperRectangle([new_set.lower], [new_set.upper]),
196+
)
197+
return
198+
end
199+
200+
function MOI.get(
201+
model::MOI.ModelLike,
202+
attr::MOI.ConstraintFunction,
203+
bridge::IntervalToHyperRectangleBridge{T,F,G},
204+
) where {T,F,G}
205+
return convert(
206+
G,
207+
only(
208+
MOI.Utilities.scalarize(
209+
MOI.get(model, attr, bridge.vector_constraint),
210+
),
211+
),
212+
)
213+
end
214+
215+
function MOI.get(
216+
model::MOI.ModelLike,
217+
::MOI.ConstraintSet,
218+
bridge::IntervalToHyperRectangleBridge,
219+
)
220+
rect = MOI.get(model, MOI.ConstraintSet(), bridge.vector_constraint)
221+
return MOI.Interval(only(rect.lower), only(rect.upper))
222+
end

src/Bridges/Constraint/bridges/VectorizeBridge.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
1616
where `T` is the coefficient type of `g(x) - a`.
1717
18+
See also [`IntervalToHyperRectangleBridge`](@ref) for double-sided bound
19+
constraints.
20+
1821
## Source node
1922
2023
`VectorizeBridge` supports:
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
module TestConstraintIntervalToHyperRectangle
8+
9+
using Test
10+
11+
import MathOptInterface as MOI
12+
13+
function runtests()
14+
for name in names(@__MODULE__; all = true)
15+
if startswith("$(name)", "test_")
16+
@testset "$(name) $T" for T in [Float32, Float64]
17+
getfield(@__MODULE__, name)(T)
18+
end
19+
end
20+
end
21+
return
22+
end
23+
24+
include("../utilities.jl")
25+
26+
function test_ScalarFunctionConstantNotZero(T)
27+
mock = MOI.Utilities.MockOptimizer(
28+
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{T}()),
29+
)
30+
bridged_mock = MOI.Bridges.Constraint.IntervalToHyperRectangle{T}(mock)
31+
config = MOI.Test.Config(T)
32+
MOI.Test.test_model_ScalarFunctionConstantNotZero(bridged_mock, config)
33+
return
34+
end
35+
36+
function test_basic(T)
37+
mock = MOI.Utilities.MockOptimizer(
38+
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{T}()),
39+
)
40+
bridged_mock = MOI.Bridges.Constraint.IntervalToHyperRectangle{T}(mock)
41+
config = MOI.Test.Config()
42+
MOI.Test.runtests(
43+
bridged_mock,
44+
config,
45+
include = ["test_basic_ScalarAffineFunction_Interval"],
46+
)
47+
return
48+
end
49+
50+
function test_linear_integration(T)
51+
mock = MOI.Utilities.MockOptimizer(
52+
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{T}()),
53+
)
54+
bridged_mock = MOI.Bridges.Constraint.IntervalToHyperRectangle{T}(mock)
55+
config = MOI.Test.Config(T, exclude = Any[MOI.ConstraintBasisStatus])
56+
MOI.Utilities.set_mock_optimize!(
57+
mock,
58+
(mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
59+
mock,
60+
T[5, 5],
61+
(MOI.VectorAffineFunction{T}, MOI.HyperRectangle{T}) => [T[-1]],
62+
),
63+
(mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
64+
mock,
65+
T[5//2, 5//2],
66+
(MOI.VectorAffineFunction{T}, MOI.HyperRectangle{T}) => [T[1]],
67+
),
68+
(mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
69+
mock,
70+
T[1, 1],
71+
(MOI.VectorAffineFunction{T}, MOI.HyperRectangle{T}) => [T[1]],
72+
),
73+
(mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
74+
mock,
75+
T[6, 6],
76+
(MOI.VectorAffineFunction{T}, MOI.HyperRectangle{T}) => [T[-1]],
77+
),
78+
)
79+
MOI.Test.test_linear_integration_Interval(bridged_mock, config)
80+
return
81+
end
82+
83+
function test_runtests(T)
84+
MOI.Bridges.runtests(
85+
MOI.Bridges.Constraint.IntervalToHyperRectangleBridge,
86+
model -> begin
87+
x = MOI.add_variable(model)
88+
MOI.add_constraint(model, x, MOI.Interval{T}(3, 5))
89+
end,
90+
model -> begin
91+
x = MOI.add_variable(model)
92+
MOI.add_constraint(
93+
model,
94+
MOI.VectorOfVariables([x]),
95+
MOI.HyperRectangle(T[3], T[5]),
96+
)
97+
end,
98+
eltype = T,
99+
)
100+
MOI.Bridges.runtests(
101+
MOI.Bridges.Constraint.IntervalToHyperRectangleBridge,
102+
model -> begin
103+
x = MOI.add_variable(model)
104+
MOI.add_constraint(model, T(2) * x, MOI.Interval{T}(3, 5))
105+
end,
106+
model -> begin
107+
x = MOI.add_variable(model)
108+
MOI.add_constraint(
109+
model,
110+
MOI.Utilities.vectorize([T(2) * x]),
111+
MOI.HyperRectangle(T[3], T[5]),
112+
)
113+
end,
114+
eltype = T,
115+
)
116+
return
117+
end
118+
119+
end # module
120+
121+
TestConstraintIntervalToHyperRectangle.runtests()

test/Bridges/debug.jl

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,22 +98,13 @@ function test_print_active_bridges()
9898
| * Supported constraint: MOI.VectorAffineFunction{Float64}-in-MOI.Zeros
9999
* Unsupported constraint: MOI.ScalarAffineFunction{Float64}-in-MOI.Interval{Float64}
100100
| bridged by:
101-
| MOIB.Constraint.SplitIntervalBridge{Float64, MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}, MOI.GreaterThan{Float64}, MOI.LessThan{Float64}}
101+
| MOIB.Constraint.IntervalToHyperRectangleBridge{Float64, MOI.VectorAffineFunction{Float64}, MOI.ScalarAffineFunction{Float64}}
102102
| may introduce:
103-
| * Unsupported constraint: MOI.ScalarAffineFunction{Float64}-in-MOI.GreaterThan{Float64}
103+
| * Unsupported constraint: MOI.VectorAffineFunction{Float64}-in-MOI.HyperRectangle{Float64}
104104
| | bridged by:
105-
| | MOIB.Constraint.VectorizeBridge{Float64, MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, MOI.ScalarAffineFunction{Float64}}
105+
| | MOIB.Constraint.SplitHyperRectangleBridge{Float64, MOI.VectorAffineFunction{Float64}, MOI.VectorAffineFunction{Float64}}
106106
| | may introduce:
107107
| | * Supported constraint: MOI.VectorAffineFunction{Float64}-in-MOI.Nonnegatives
108-
| * Unsupported constraint: MOI.ScalarAffineFunction{Float64}-in-MOI.LessThan{Float64}
109-
| | bridged by:
110-
| | MOIB.Constraint.VectorizeBridge{Float64, MOI.VectorAffineFunction{Float64}, MOI.Nonpositives, MOI.ScalarAffineFunction{Float64}}
111-
| | may introduce:
112-
| | * Unsupported constraint: MOI.VectorAffineFunction{Float64}-in-MOI.Nonpositives
113-
| | | bridged by:
114-
| | | MOIB.Constraint.NonposToNonnegBridge{Float64, MOI.VectorAffineFunction{Float64}, MOI.VectorAffineFunction{Float64}}
115-
| | | may introduce:
116-
| | | * Supported constraint: MOI.VectorAffineFunction{Float64}-in-MOI.Nonnegatives
117108
* Unsupported constraint: MOI.ScalarQuadraticFunction{Float64}-in-MOI.LessThan{Float64}
118109
| bridged by:
119110
| MOIB.Constraint.QuadtoSOCBridge{Float64}

0 commit comments

Comments
 (0)