Skip to content

Commit 661c278

Browse files
committed
Fix SplitHyperRectangleBridge with all free rows
1 parent b3e7bff commit 661c278

File tree

2 files changed

+100
-8
lines changed

2 files changed

+100
-8
lines changed

src/Bridges/Constraint/bridges/SplitHyperRectangleBridge.jl

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
* `G` in [`MOI.Nonnegatives`](@ref)
2626
"""
2727
mutable struct SplitHyperRectangleBridge{T,G,F} <: AbstractBridge
28-
ci::MOI.ConstraintIndex{G,MOI.Nonnegatives}
28+
ci::Union{Nothing,MOI.ConstraintIndex{G,MOI.Nonnegatives}}
2929
set::MOI.HyperRectangle{T}
3030
free_rows::F
31+
free_primal_start::Union{Nothing,Vector{T}}
32+
free_dual_start::Union{Nothing,Vector{T}}
3133
end
3234

3335
const SplitHyperRectangle{T,OT<:MOI.ModelLike} =
@@ -67,9 +69,18 @@ function bridge_constraint(
6769
push!(free_rows, i)
6870
end
6971
end
72+
if length(free_rows) == N
73+
return SplitHyperRectangleBridge{T,G,F}(nothing, s, f, nothing, nothing)
74+
end
7075
g = MOI.Utilities.vectorize(g_vec[rows_to_keep])
7176
ci = MOI.add_constraint(model, g, MOI.Nonnegatives(MOI.output_dimension(g)))
72-
return SplitHyperRectangleBridge{T,G,F}(ci, s, scalars[free_rows])
77+
return SplitHyperRectangleBridge{T,G,F}(
78+
ci,
79+
s,
80+
scalars[free_rows],
81+
nothing,
82+
nothing,
83+
)
7384
end
7485

7586
function MOI.supports_constraint(
@@ -106,6 +117,9 @@ function MOI.get(
106117
::MOI.ConstraintFunction,
107118
bridge::SplitHyperRectangleBridge{T,G,F},
108119
) where {T,G,F}
120+
if bridge.ci === nothing
121+
return bridge.free_rows
122+
end
109123
f = MOI.get(model, MOI.ConstraintFunction(), bridge.ci)
110124
f_s = MOI.Utilities.eachscalar(f)
111125
func = Vector{eltype(f_s)}(undef, MOI.dimension(bridge.set))
@@ -154,22 +168,28 @@ function MOI.get(
154168
end
155169

156170
function MOI.delete(model::MOI.ModelLike, bridge::SplitHyperRectangleBridge)
157-
MOI.delete(model, bridge.ci)
171+
if bridge.ci !== nothing
172+
MOI.delete(model, bridge.ci)
173+
end
158174
return
159175
end
160176

161177
function MOI.get(
162-
::SplitHyperRectangleBridge{T,G},
178+
bridge::SplitHyperRectangleBridge{T,G},
163179
::MOI.NumberOfConstraints{G,MOI.Nonnegatives},
164180
)::Int64 where {T,G}
165-
return 1
181+
return ifelse(bridge.ci === nothing, 0, 1)
166182
end
167183

168184
function MOI.get(
169-
bridge::SplitHyperRectangleBridge{T,G},
185+
bridge::SplitHyperRectangleBridge{T,G,F},
170186
::MOI.ListOfConstraintIndices{G,MOI.Nonnegatives},
171-
) where {T,G}
172-
return [bridge.ci]
187+
) where {T,G,F}
188+
ret = MOI.ConstraintIndex{G,MOI.Nonnegatives}[]
189+
if bridge.ci !== nothing
190+
push!(ret, bridge.ci)
191+
end
192+
return ret
173193
end
174194

175195
function MOI.supports(
@@ -180,12 +200,49 @@ function MOI.supports(
180200
return MOI.supports(model, attr, MOI.ConstraintIndex{G,MOI.Nonnegatives})
181201
end
182202

203+
_get_free_start(bridge, ::MOI.ConstraintDualStart) = bridge.free_dual_start
204+
205+
function _set_free_start(bridge, ::MOI.ConstraintDualStart, value)
206+
bridge.free_dual_start = value
207+
return
208+
end
209+
210+
_get_free_start(bridge, ::MOI.ConstraintPrimalStart) = bridge.free_primal_start
211+
212+
function _set_free_start(bridge, ::MOI.ConstraintPrimalStart, value)
213+
bridge.free_primal_start = value
214+
return
215+
end
216+
217+
# This is a punned overload. We use Union{MOI.ConstraintDual,MOI.ConstraintDualStart}
218+
# in MOI.get, so this hits the ConstraintDual branch. Since no constraints are
219+
# ever added, we just assuem that the dual is `0.0` (this is feasible because)
220+
# the set is really `f(x) in Reals()`, so the dual set is `Zeros()`
221+
function _get_free_start(
222+
bridge::SplitHyperRectangleBridge{T},
223+
::MOI.ConstraintDual,
224+
) where {T}
225+
return zeros(T, MOI.dimension(bridge.set))
226+
end
227+
228+
# The same cannot be said for ConstraintPrimal because we have no mechanism for
229+
# evaluating the primal of the free rows. Throw an error instead.
230+
function _get_free_start(
231+
::SplitHyperRectangleBridge,
232+
attr::MOI.ConstraintPrimal
233+
)
234+
return throw(MOI.GetAttributeNotAllowed(attr))
235+
end
236+
183237
function MOI.set(
184238
model::MOI.ModelLike,
185239
attr::MOI.ConstraintPrimalStart,
186240
bridge::SplitHyperRectangleBridge{T},
187241
value::AbstractVector{T},
188242
) where {T}
243+
if bridge.ci === nothing
244+
return _set_free_start(bridge, attr, value)
245+
end
189246
new_values = vcat(
190247
T[v - l for (v, l) in zip(value, bridge.set.lower) if isfinite(l)],
191248
T[u - v for (v, u) in zip(value, bridge.set.upper) if isfinite(u)],
@@ -199,6 +256,9 @@ function MOI.get(
199256
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
200257
bridge::SplitHyperRectangleBridge{T},
201258
) where {T}
259+
if bridge.ci === nothing
260+
return _get_free_start(bridge, attr)
261+
end
202262
values = MOI.get(model, attr, bridge.ci)
203263
if values === nothing
204264
return nothing
@@ -226,6 +286,9 @@ function MOI.set(
226286
bridge::SplitHyperRectangleBridge{T},
227287
values::AbstractVector{T},
228288
) where {T}
289+
if bridge.ci === nothing
290+
return _set_free_start(bridge, attr, values)
291+
end
229292
set = bridge.set
230293
new_values = vcat(
231294
T[max(T(0), v) for (v, l) in zip(values, set.lower) if isfinite(l)],
@@ -240,6 +303,9 @@ function MOI.get(
240303
attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart},
241304
bridge::SplitHyperRectangleBridge{T},
242305
) where {T}
306+
if bridge.ci === nothing
307+
return _get_free_start(bridge, attr)
308+
end
243309
values = MOI.get(model, attr, bridge.ci)
244310
if values === nothing
245311
return nothing
@@ -267,6 +333,9 @@ function MOI.set(
267333
bridge::SplitHyperRectangleBridge{T},
268334
::Nothing,
269335
) where {T}
336+
if bridge.ci === nothing
337+
return _set_free_start(bridge, attr, nothing)
338+
end
270339
MOI.set(model, attr, bridge.ci, nothing)
271340
return
272341
end

test/Bridges/Constraint/SplitHyperRectangleBridge.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,29 @@ function test_runtests_free_row()
9999
return
100100
end
101101

102+
function test_runtests_all_free_rows()
103+
MOI.Bridges.runtests(
104+
MOI.Bridges.Constraint.SplitHyperRectangleBridge,
105+
"""
106+
variables: x
107+
[x] in HyperRectangle([-Inf], [Inf])
108+
""",
109+
"""
110+
variables: x
111+
""",
112+
)
113+
inner = MOI.Utilities.Model{Float64}()
114+
model = MOI.Bridges.Constraint.SplitHyperRectangle{Float64}(inner)
115+
x = MOI.add_variable(model)
116+
f = MOI.Utilities.operate(vcat, Float64, 1.0 * x)
117+
c = MOI.add_constraint(model, f, MOI.HyperRectangle([-Inf], [Inf]))
118+
@test_throws(
119+
MOI.GetAttributeNotAllowed{MOI.ConstraintPrimal},
120+
MOI.get(model, MOI.ConstraintPrimal(), c)
121+
)
122+
return
123+
end
124+
102125
function test_basic_HyperRectangle()
103126
model = MOI.Bridges.Constraint.SplitHyperRectangle{Float64}(
104127
MOI.Utilities.Model{Float64}(),

0 commit comments

Comments
 (0)