Skip to content

Commit 5db664a

Browse files
committed
[Bridges] Add Interval to HyperRectangle constraint bridge
1 parent 8ba5a93 commit 5db664a

File tree

2 files changed

+328
-0
lines changed

2 files changed

+328
-0
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
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(::Type{<:IntervalToHyperRectangleBridge})
60+
return Tuple{Type}[]
61+
end
62+
63+
function MOI.Bridges.added_constraint_types(
64+
::Type{<:IntervalToHyperRectangleBridge{T,F}},
65+
) where {T,F}
66+
return Tuple{Type,Type}[(F, MOI.HyperRectangle{T})]
67+
end
68+
69+
function concrete_bridge_type(
70+
::Type{<:IntervalToHyperRectangleBridge{T}},
71+
G::Type{<:MOI.AbstractScalarFunction},
72+
S::Type{MOI.Interval{T}},
73+
) where {T}
74+
F = MOI.Utilities.promote_operation(vcat, T, G)
75+
return IntervalToHyperRectangleBridge{T,F,G}
76+
end
77+
78+
function MOI.get(
79+
::IntervalToHyperRectangleBridge{T,F},
80+
::MOI.NumberOfConstraints{F,MOI.HyperRectangle{T}},
81+
)::Int64 where {T,F}
82+
return 1
83+
end
84+
85+
function MOI.get(
86+
bridge::IntervalToHyperRectangleBridge{T,F},
87+
::MOI.ListOfConstraintIndices{F,MOI.HyperRectangle{T}},
88+
) where {T,F}
89+
return [bridge.vector_constraint]
90+
end
91+
92+
function MOI.delete(model::MOI.ModelLike, bridge::IntervalToHyperRectangleBridge)
93+
MOI.delete(model, bridge.vector_constraint)
94+
return
95+
end
96+
97+
function MOI.supports(
98+
model::MOI.ModelLike,
99+
attr::Union{MOI.ConstraintPrimalStart,MOI.ConstraintDualStart},
100+
::Type{IntervalToHyperRectangleBridge{T,F,G}},
101+
) where {T,F,G}
102+
return MOI.supports(model, attr, MOI.ConstraintIndex{F,MOI.HyperRectangle{T}})
103+
end
104+
105+
function MOI.get(
106+
model::MOI.ModelLike,
107+
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
108+
bridge::IntervalToHyperRectangleBridge,
109+
)
110+
x = MOI.get(model, attr, bridge.vector_constraint)
111+
if isnothing(x)
112+
return nothing
113+
end
114+
return only(x)
115+
end
116+
117+
function MOI.set(
118+
model::MOI.ModelLike,
119+
attr::MOI.ConstraintPrimalStart,
120+
bridge::IntervalToHyperRectangleBridge,
121+
value,
122+
)
123+
MOI.set(
124+
model,
125+
attr,
126+
bridge.vector_constraint,
127+
[value],
128+
)
129+
return
130+
end
131+
132+
function MOI.set(
133+
model::MOI.ModelLike,
134+
attr::MOI.ConstraintPrimalStart,
135+
bridge::IntervalToHyperRectangleBridge,
136+
::Nothing,
137+
)
138+
MOI.set(model, attr, bridge.vector_constraint, nothing)
139+
return
140+
end
141+
142+
function MOI.get(
143+
model::MOI.ModelLike,
144+
attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart},
145+
bridge::IntervalToHyperRectangleBridge,
146+
)
147+
x = MOI.get(model, attr, bridge.vector_constraint)
148+
if isnothing(x)
149+
return nothing
150+
end
151+
return only(x)
152+
end
153+
154+
function MOI.set(
155+
model::MOI.ModelLike,
156+
attr::MOI.ConstraintDualStart,
157+
bridge::IntervalToHyperRectangleBridge,
158+
value,
159+
)
160+
if isnothing(value)
161+
MOI.set(model, attr, bridge.vector_constraint, nothing)
162+
else
163+
MOI.set(model, attr, bridge.vector_constraint, [value])
164+
end
165+
return
166+
end
167+
168+
function MOI.modify(
169+
model::MOI.ModelLike,
170+
bridge::IntervalToHyperRectangleBridge,
171+
change::MOI.ScalarCoefficientChange,
172+
)
173+
MOI.modify(
174+
model,
175+
bridge.vector_constraint,
176+
MOI.MultirowChange(change.variable, [(1, change.new_coefficient)]),
177+
)
178+
@show @__LINE__
179+
return
180+
end
181+
182+
function MOI.set(
183+
model::MOI.ModelLike,
184+
attr::MOI.ConstraintSet,
185+
bridge::IntervalToHyperRectangleBridge,
186+
new_set::MOI.Interval,
187+
)
188+
MOI.set(
189+
model,
190+
attr,
191+
bridge.vector_constraint,
192+
MOI.HyperRectangle([new_set.lower], [new_set.upper]),
193+
)
194+
return
195+
end
196+
197+
function MOI.get(
198+
model::MOI.ModelLike,
199+
attr::MOI.ConstraintFunction,
200+
bridge::IntervalToHyperRectangleBridge{T,F,G},
201+
) where {T,F,G}
202+
return convert(G, only(MOI.Utilities.scalarize(
203+
MOI.get(model, attr, bridge.vector_constraint),
204+
)))
205+
end
206+
207+
function MOI.get(
208+
model::MOI.ModelLike,
209+
::MOI.ConstraintSet,
210+
bridge::IntervalToHyperRectangleBridge,
211+
)
212+
rect = MOI.get(model, MOI.ConstraintSet(), bridge.vector_constraint)
213+
return MOI.Interval(only(rect.lower), only(rect.upper))
214+
end
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
84+
function test_runtests(T)
85+
MOI.Bridges.runtests(
86+
MOI.Bridges.Constraint.IntervalToHyperRectangleBridge,
87+
model -> begin
88+
x = MOI.add_variable(model)
89+
MOI.add_constraint(model, x, MOI.Interval{T}(3, 5))
90+
end,
91+
model -> begin
92+
x = MOI.add_variable(model)
93+
MOI.add_constraint(model, MOI.VectorOfVariables([x]), MOI.HyperRectangle(T[3], T[5]))
94+
end,
95+
eltype = T,
96+
)
97+
MOI.Bridges.runtests(
98+
MOI.Bridges.Constraint.IntervalToHyperRectangleBridge,
99+
model -> begin
100+
x = MOI.add_variable(model)
101+
MOI.add_constraint(model, T(2) * x, MOI.Interval{T}(3, 5))
102+
end,
103+
model -> begin
104+
x = MOI.add_variable(model)
105+
MOI.add_constraint(model, MOI.Utilities.vectorize([T(2) * x]), MOI.HyperRectangle(T[3], T[5]))
106+
end,
107+
eltype = T,
108+
)
109+
return
110+
end
111+
112+
end # module
113+
114+
TestConstraintIntervalToHyperRectangle.runtests()

0 commit comments

Comments
 (0)