Skip to content

Commit 6e9fcac

Browse files
authored
[Bridges] improve function getter of SplitHyperRectangleBridge (#2681)
1 parent 71044dc commit 6e9fcac

File tree

3 files changed

+99
-39
lines changed

3 files changed

+99
-39
lines changed

src/Bridges/Bridges.jl

+2
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,8 @@ _fake_start(value, ::MOI.AbstractScalarSet) = value
416416

417417
_fake_start(value, set::MOI.AbstractVectorSet) = fill(value, MOI.dimension(set))
418418

419+
_fake_start(value::AbstractVector, set::MOI.AbstractVectorSet) = value
420+
419421
function _bridged_model(Bridge::Type{<:Constraint.AbstractBridge}, inner)
420422
return Constraint.SingleBridgeOptimizer{Bridge}(inner)
421423
end

src/Bridges/Constraint/bridges/SplitHyperRectangleBridge.jl

+58-39
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,37 @@ function bridge_constraint(
3939
f::F,
4040
s::MOI.HyperRectangle,
4141
) 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)
42+
N = MOI.dimension(s)
43+
g_vec = Vector{MOI.Utilities.scalar_type(G)}(undef, 2 * MOI.dimension(s))
44+
rows_to_keep = fill(true, length(g_vec))
45+
free_rows = Int[]
46+
scalars = MOI.Utilities.eachscalar(f)
47+
for (i, fi) in enumerate(scalars)
48+
if !isfinite(s.lower[i])
49+
rows_to_keep[i] = false
50+
# It doesn't really matter what goes here. We're going to drop it
51+
# when we vectorize the function
52+
g_vec[i] = fi
53+
elseif iszero(s.lower[i])
54+
g_vec[i] = fi
55+
else
56+
g_vec[i] = MOI.Utilities.operate(-, T, fi, s.lower[i])
57+
end
58+
if !isfinite(s.upper[i])
59+
rows_to_keep[N+i] = false
60+
g_vec[N+i] = fi
61+
elseif iszero(s.upper[i])
62+
g_vec[N+i] = MOI.Utilities.operate(-, T, fi)
63+
else
64+
g_vec[N+i] = MOI.Utilities.operate(-, T, s.upper[i], fi)
65+
end
66+
if !isfinite(s.lower[i]) && !isfinite(s.upper[i])
67+
push!(free_rows, i)
5668
end
5769
end
58-
free_rows = MOI.Utilities.eachscalar(f)[free_indices]
59-
g = MOI.Utilities.operate(vcat, T, lower, upper)
70+
g = MOI.Utilities.vectorize(g_vec[rows_to_keep])
6071
ci = MOI.add_constraint(model, g, MOI.Nonnegatives(MOI.output_dimension(g)))
61-
return SplitHyperRectangleBridge{T,G,F}(ci, s, free_rows)
72+
return SplitHyperRectangleBridge{T,G,F}(ci, s, scalars[free_rows])
6273
end
6374

6475
function MOI.supports_constraint(
@@ -97,33 +108,41 @@ function MOI.get(
97108
) where {T,G,F}
98109
f = MOI.get(model, MOI.ConstraintFunction(), bridge.ci)
99110
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
111+
func = Vector{eltype(f_s)}(undef, MOI.dimension(bridge.set))
115112
free_s = MOI.Utilities.eachscalar(bridge.free_rows)
116-
free_indices = Int[]
117-
for (i, (l, u)) in enumerate(zip(s.lower, s.upper))
113+
n_free_rows, n_f_rows, upper_bound_rows = 0, 0, Int[]
114+
for (row, (l, u)) in enumerate(zip(bridge.set.lower, bridge.set.upper))
118115
if !isfinite(l) && !isfinite(u)
119-
push!(free_indices, i)
116+
n_free_rows += 1
117+
func[row] = free_s[n_free_rows]
118+
elseif iszero(l)
119+
n_f_rows += 1
120+
func[row] = f_s[n_f_rows]
121+
elseif isfinite(l)
122+
n_f_rows += 1
123+
func[row] = MOI.Utilities.operate(+, T, f_s[n_f_rows], l)
124+
else
125+
@assert isfinite(u)
126+
# This row exists only as u - f, but we don't know where it starts
127+
# yet because we need to count all the `f - l` rows first.
128+
push!(upper_bound_rows, row)
120129
end
121130
end
122-
for (i, index) in enumerate(free_indices)
123-
func[index] = free_s[i]
131+
for (row, (l, u)) in enumerate(zip(bridge.set.lower, bridge.set.upper))
132+
if !isfinite(u)
133+
continue
134+
end
135+
n_f_rows += 1
136+
if !(row in upper_bound_rows)
137+
continue
138+
end
139+
func[row] = if iszero(bridge.set.upper[row])
140+
MOI.Utilities.operate(-, T, f_s[n_f_rows])
141+
else
142+
MOI.Utilities.operate(-, T, bridge.set.upper[row], f_s[n_f_rows])
143+
end
124144
end
125-
g = MOI.Utilities.operate(vcat, T, func...)
126-
return MOI.Utilities.convert_approx(F, g)
145+
return MOI.Utilities.convert_approx(F, MOI.Utilities.vectorize(func))
127146
end
128147

129148
function MOI.get(

test/Bridges/Constraint/SplitHyperRectangleBridge.jl

+39
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,45 @@ function test_runtests_free_row()
9999
return
100100
end
101101

102+
function test_basic_HyperRectangle()
103+
model = MOI.Bridges.Constraint.SplitHyperRectangle{Float64}(
104+
MOI.Utilities.Model{Float64}(),
105+
)
106+
config = MOI.Test.Config()
107+
MOI.empty!(model)
108+
MOI.Test.test_basic_VectorOfVariables_HyperRectangle(model, config)
109+
MOI.empty!(model)
110+
MOI.Test.test_basic_VectorAffineFunction_HyperRectangle(model, config)
111+
MOI.empty!(model)
112+
MOI.Test.test_basic_VectorQuadraticFunction_HyperRectangle(model, config)
113+
MOI.empty!(model)
114+
MOI.Test.test_basic_VectorNonlinearFunction_HyperRectangle(model, config)
115+
return
116+
end
117+
118+
function test_runtests_VectorOfVariables_mix_of_signs()
119+
# 0 <= a <= 1 | a >= 0
120+
# -Inf <= b <= Inf | c >= 0
121+
# 0 <= c <= Inf | -1 + d >= 0
122+
# 1 <= d <= Inf | 1 - a >= 0
123+
# -Inf <= e <= 0 | - e >= 0
124+
# -Inf <= f <= Inf | 1 - g >= 0
125+
# -Inf <= g <= 1 |
126+
MOI.Bridges.runtests(
127+
MOI.Bridges.Constraint.SplitHyperRectangleBridge,
128+
"""
129+
variables: a, b, c, d, e, f, g
130+
[a, b, c, d, e, f, g] in HyperRectangle([0.0, -Inf, 0.0, 1.0, -Inf, -Inf, -Inf], [1.0, Inf, Inf, Inf, 0.0, Inf, 1.0])
131+
""",
132+
"""
133+
variables: a, b, c, d, e, f, g
134+
[1.0 * a, 1.0 * c, 1.0 * d + -1.0, 1.0 + -1.0 * a, -1.0 * e, 1.0 + -1.0 * g] in Nonnegatives(6)
135+
""";
136+
constraint_start = [1.1, 0.0, 1.2, 1.3, -1.1, 0.0, -1.2],
137+
)
138+
return
139+
end
140+
102141
end # module
103142

104143
TestConstraintHyperRectangle.runtests()

0 commit comments

Comments
 (0)