Skip to content

Commit fd8ca78

Browse files
authored
Add HyperRectangle set (#1961)
1 parent 0fcd485 commit fd8ca78

File tree

11 files changed

+511
-0
lines changed

11 files changed

+511
-0
lines changed

docs/src/manual/standard_form.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ The vector-valued set types implemented in MathOptInterface.jl are:
7979
| [`NormOneCone(d)`](@ref MathOptInterface.NormOneCone) | ``\{ (t,x) \in \mathbb{R}^{d} : t \ge \sum_i \lvert x_i \rvert \}`` |
8080
| [`NormInfinityCone(d)`](@ref MathOptInterface.NormInfinityCone) | ``\{ (t,x) \in \mathbb{R}^{d} : t \ge \max_i \lvert x_i \rvert \}`` |
8181
| [`RelativeEntropyCone(d)`](@ref MathOptInterface.RelativeEntropyCone) | ``\{ (u, v, w) \in \mathbb{R}^{d} : u \ge \sum_i w_i \log (\frac{w_i}{v_i}), v_i \ge 0, w_i \ge 0 \}`` |
82+
| [`HyperRectangle(l, u)`](@ref MathOptInterface.HyperRectangle) | ``\{x \in \bar{\mathbb{R}}^d: x_i \in [l_i, u_i] \forall i=1,\ldots,d\}`` |
8283

8384
## Matrix cones
8485

docs/src/reference/standard_form.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ SOS1
9494
SOS2
9595
Indicator
9696
Complements
97+
HyperRectangle
9798
```
9899

99100
## Constraint programming sets

docs/src/submodules/Bridges/list_of_bridges.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Bridges.Constraint.ScalarFunctionizeBridge
3131
Bridges.Constraint.VectorFunctionizeBridge
3232
Bridges.Constraint.SplitComplexEqualToBridge
3333
Bridges.Constraint.SplitComplexZerosBridge
34+
Bridges.Constraint.SplitHyperRectangleBridge
3435
Bridges.Constraint.SplitIntervalBridge
3536
Bridges.Constraint.SOCtoRSOCBridge
3637
Bridges.Constraint.RSOCtoSOCBridge

src/Bridges/Constraint/Constraint.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ include("bridges/soc_to_nonconvex_quad.jl") # do not add these bridges by defaul
5151
include("bridges/soc_to_psd.jl")
5252
include("bridges/split_complex_equalto.jl")
5353
include("bridges/split_complex_zeros.jl")
54+
include("bridges/split_hyperrectangle.jl")
5455
include("bridges/hermitian.jl")
5556
include("bridges/square.jl")
5657
include("bridges/table.jl")
@@ -78,6 +79,7 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
7879
MOI.Bridges.add_bridge(bridged_model, VectorSlackBridge{T})
7980
MOI.Bridges.add_bridge(bridged_model, ScalarFunctionizeBridge{T})
8081
MOI.Bridges.add_bridge(bridged_model, VectorFunctionizeBridge{T})
82+
MOI.Bridges.add_bridge(bridged_model, SplitHyperRectangleBridge{T})
8183
MOI.Bridges.add_bridge(bridged_model, SplitIntervalBridge{T})
8284
MOI.Bridges.add_bridge(bridged_model, SplitComplexEqualToBridge{T})
8385
MOI.Bridges.add_bridge(bridged_model, SplitComplexZerosBridge{T})
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
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+
SplitHyperRectangleBridge{T,G,F} <: Bridges.Constraint.AbstractBridge
9+
10+
`SplitHyperRectangleBridge` implements the following reformulation:
11+
12+
* ``f(x) \\in \\textsf{HyperRectangle}(l, u)`` to
13+
``[f(x) - l; u - f(x)] \\in \\mathbb{R}_+``.
14+
15+
## Source node
16+
17+
`SplitHyperRectangleBridge` supports:
18+
19+
* `F` in [`MOI.HyperRectangle`](@ref)
20+
21+
## Target nodes
22+
23+
`SplitHyperRectangleBridge` creates:
24+
25+
* `G` in [`MOI.Nonnegatives`](@ref)
26+
"""
27+
mutable struct SplitHyperRectangleBridge{T,G,F} <: AbstractBridge
28+
ci::MOI.ConstraintIndex{G,MOI.Nonnegatives}
29+
set::MOI.HyperRectangle{T}
30+
free_rows::F
31+
end
32+
33+
const SplitHyperRectangle{T,OT<:MOI.ModelLike} =
34+
SingleBridgeOptimizer{SplitHyperRectangleBridge{T},OT}
35+
36+
function bridge_constraint(
37+
::Type{SplitHyperRectangleBridge{T,G,F}},
38+
model::MOI.ModelLike,
39+
f::F,
40+
s::MOI.HyperRectangle,
41+
) where {T,G,F}
42+
lower = MOI.Utilities.operate(-, T, f, s.lower)
43+
upper = MOI.Utilities.operate(-, T, s.upper, f)
44+
if any(!isfinite, s.lower)
45+
indices = [i for (i, l) in enumerate(s.lower) if isfinite(l)]
46+
lower = MOI.Utilities.eachscalar(lower)[indices]
47+
end
48+
if any(!isfinite, s.upper)
49+
indices = [i for (i, u) in enumerate(s.upper) if isfinite(u)]
50+
upper = MOI.Utilities.eachscalar(upper)[indices]
51+
end
52+
free_indices = Int[]
53+
for (i, (l, u)) in enumerate(zip(s.lower, s.upper))
54+
if !isfinite(l) && !isfinite(u)
55+
push!(free_indices, i)
56+
end
57+
end
58+
free_rows = MOI.Utilities.eachscalar(f)[free_indices]
59+
g = MOI.Utilities.operate(vcat, T, lower, upper)
60+
ci = MOI.add_constraint(model, g, MOI.Nonnegatives(MOI.output_dimension(g)))
61+
return SplitHyperRectangleBridge{T,G,F}(ci, s, free_rows)
62+
end
63+
64+
function MOI.supports_constraint(
65+
::Type{<:SplitHyperRectangleBridge{T}},
66+
::Type{<:MOI.AbstractVectorFunction},
67+
::Type{MOI.HyperRectangle{T}},
68+
) where {T}
69+
return true
70+
end
71+
72+
function MOI.Bridges.added_constrained_variable_types(
73+
::Type{<:SplitHyperRectangleBridge},
74+
)
75+
return Tuple{Type}[]
76+
end
77+
78+
function MOI.Bridges.added_constraint_types(
79+
::Type{<:SplitHyperRectangleBridge{T,G}},
80+
) where {T,G}
81+
return Tuple{Type,Type}[(G, MOI.Nonnegatives),]
82+
end
83+
84+
function concrete_bridge_type(
85+
::Type{<:SplitHyperRectangleBridge{T}},
86+
::Type{F},
87+
::Type{MOI.HyperRectangle{T}},
88+
) where {T,F<:MOI.AbstractVectorFunction}
89+
G = MOI.Utilities.promote_operation(-, T, F, Vector{T})
90+
return SplitHyperRectangleBridge{T,G,F}
91+
end
92+
93+
function MOI.get(
94+
model::MOI.ModelLike,
95+
::MOI.ConstraintFunction,
96+
bridge::SplitHyperRectangleBridge{T,G,F},
97+
) where {T,G,F}
98+
f = MOI.get(model, MOI.ConstraintFunction(), bridge.ci)
99+
f_s = MOI.Utilities.eachscalar(f)
100+
s = bridge.set
101+
func = Vector{eltype(f_s)}(undef, MOI.dimension(s))
102+
103+
lower_indices = [i for (i, l) in enumerate(s.lower) if isfinite(l)]
104+
for (i, index) in enumerate(lower_indices)
105+
func[index] = MOI.Utilities.operate(+, T, f_s[i], s.lower[index])
106+
end
107+
108+
upper_indices = [i for (i, u) in enumerate(s.upper) if isfinite(u)]
109+
for (j, index) in enumerate(upper_indices)
110+
i = length(lower_indices) + j
111+
if !(index in lower_indices)
112+
func[index] = MOI.Utilities.operate(-, T, s.upper[index], f_s[i])
113+
end
114+
end
115+
free_s = MOI.Utilities.eachscalar(bridge.free_rows)
116+
free_indices = Int[]
117+
for (i, (l, u)) in enumerate(zip(s.lower, s.upper))
118+
if !isfinite(l) && !isfinite(u)
119+
push!(free_indices, i)
120+
end
121+
end
122+
for (i, index) in enumerate(free_indices)
123+
func[index] = free_s[i]
124+
end
125+
g = MOI.Utilities.operate(vcat, T, func...)
126+
return MOI.Utilities.convert_approx(F, g)
127+
end
128+
129+
function MOI.get(
130+
::MOI.ModelLike,
131+
::MOI.ConstraintSet,
132+
bridge::SplitHyperRectangleBridge,
133+
)
134+
return bridge.set
135+
end
136+
137+
function MOI.delete(model::MOI.ModelLike, bridge::SplitHyperRectangleBridge)
138+
MOI.delete(model, bridge.ci)
139+
return
140+
end
141+
142+
function MOI.get(
143+
::SplitHyperRectangleBridge{T,G},
144+
::MOI.NumberOfConstraints{G,MOI.Nonnegatives},
145+
)::Int64 where {T,G}
146+
return 1
147+
end
148+
149+
function MOI.get(
150+
bridge::SplitHyperRectangleBridge{T,G},
151+
::MOI.ListOfConstraintIndices{G,MOI.Nonnegatives},
152+
) where {T,G}
153+
return [bridge.ci]
154+
end
155+
156+
function MOI.supports(
157+
::MOI.ModelLike,
158+
::Union{MOI.ConstraintPrimalStart,MOI.ConstraintDualStart},
159+
::Type{<:SplitHyperRectangleBridge},
160+
)
161+
return true
162+
end
163+
164+
function MOI.set(
165+
model::MOI.ModelLike,
166+
attr::MOI.ConstraintPrimalStart,
167+
bridge::SplitHyperRectangleBridge{T},
168+
value::AbstractVector{T},
169+
) where {T}
170+
new_values = vcat(
171+
T[v - l for (v, l) in zip(value, bridge.set.lower) if isfinite(l)],
172+
T[u - v for (v, u) in zip(value, bridge.set.upper) if isfinite(u)],
173+
)
174+
MOI.set(model, attr, bridge.ci, new_values)
175+
return
176+
end
177+
178+
function MOI.get(
179+
model::MOI.ModelLike,
180+
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
181+
bridge::SplitHyperRectangleBridge{T},
182+
) where {T}
183+
values = MOI.get(model, attr, bridge.ci)
184+
if values === nothing
185+
return nothing
186+
end
187+
ret = zeros(T, MOI.dimension(bridge.set))
188+
row = 0
189+
for (i, l) in enumerate(bridge.set.lower)
190+
if isfinite(l)
191+
row += 1
192+
ret[i] = l + values[row]
193+
end
194+
end
195+
for (i, u) in enumerate(bridge.set.upper)
196+
if isfinite(u)
197+
row += 1
198+
ret[i] = u - values[row]
199+
end
200+
end
201+
return ret
202+
end
203+
204+
function MOI.set(
205+
model::MOI.ModelLike,
206+
attr::MOI.ConstraintDualStart,
207+
bridge::SplitHyperRectangleBridge{T},
208+
values::AbstractVector{T},
209+
) where {T}
210+
set = bridge.set
211+
new_values = vcat(
212+
T[max(T(0), v) for (v, l) in zip(values, set.lower) if isfinite(l)],
213+
T[min(T(0), v) for (v, u) in zip(values, set.upper) if isfinite(u)],
214+
)
215+
MOI.set(model, attr, bridge.ci, new_values)
216+
return
217+
end
218+
219+
function MOI.get(
220+
model::MOI.ModelLike,
221+
attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart},
222+
bridge::SplitHyperRectangleBridge{T},
223+
) where {T}
224+
values = MOI.get(model, attr, bridge.ci)
225+
if values === nothing
226+
return nothing
227+
end
228+
ret = zeros(T, MOI.dimension(bridge.set))
229+
row = 0
230+
for (i, l) in enumerate(bridge.set.lower)
231+
if isfinite(l)
232+
row += 1
233+
ret[i] += values[row]
234+
end
235+
end
236+
for (i, u) in enumerate(bridge.set.upper)
237+
if isfinite(u)
238+
row += 1
239+
ret[i] += values[row]
240+
end
241+
end
242+
return ret
243+
end
244+
245+
function MOI.set(
246+
model::MOI.ModelLike,
247+
attr::Union{MOI.ConstraintPrimalStart,MOI.ConstraintDualStart},
248+
bridge::SplitHyperRectangleBridge{T},
249+
::Nothing,
250+
) where {T}
251+
MOI.set(model, attr, bridge.ci, nothing)
252+
return
253+
end

src/Test/test_basic_constraint.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ function _set(
138138
return MOI.Indicator{MOI.ACTIVATE_ON_ONE}(MOI.GreaterThan(convert(T, 3)))
139139
end
140140

141+
function _set(::Type{T}, ::Type{MOI.HyperRectangle}) where {T}
142+
return MOI.HyperRectangle(zeros(T, 3), ones(T, 3))
143+
end
144+
141145
function _basic_constraint_test_helper(
142146
model::MOI.ModelLike,
143147
config::Config{T},
@@ -298,6 +302,7 @@ for s in [
298302
:Cumulative,
299303
:Table,
300304
:Path,
305+
:HyperRectangle,
301306
]
302307
S = getfield(MOI, s)
303308
functions = if S <: MOI.AbstractScalarSet

0 commit comments

Comments
 (0)