Skip to content

Commit 0e367b5

Browse files
committed
[Bridges] improve function getter of SplitHyperRectangleBridge
This avoids adding and subtracting 0 to avoid terms like +(-(+(x), 0), 0)
1 parent 927c58b commit 0e367b5

File tree

2 files changed

+83
-39
lines changed

2 files changed

+83
-39
lines changed

src/Bridges/Constraint/bridges/SplitHyperRectangleBridge.jl

Lines changed: 52 additions & 39 deletions
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,35 @@ 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 in upper_bound_rows
132+
n_f_rows += 1
133+
func[row] = if iszero(bridge.set.upper[row])
134+
MOI.Utilities.operate(-, T, f_s[n_f_rows])
135+
else
136+
MOI.Utilities.operate(-, T, bridge.set.upper[row], f_s[n_f_rows])
137+
end
124138
end
125-
g = MOI.Utilities.operate(vcat, T, func...)
126-
return MOI.Utilities.convert_approx(F, g)
139+
return MOI.Utilities.convert_approx(F, MOI.Utilities.vectorize(func))
127140
end
128141

129142
function MOI.get(

test/Bridges/Constraint/SplitHyperRectangleBridge.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ function test_runtests_VectorOfVariables()
3636
return
3737
end
3838

39+
function test_runtests_VectorOfVariables_zeros()
40+
MOI.Bridges.runtests(
41+
MOI.Bridges.Constraint.SplitHyperRectangleBridge,
42+
"""
43+
variables: x, y
44+
[x, y] in HyperRectangle([0.0, -1.0], [1.0, 0.0])
45+
""",
46+
"""
47+
variables: x, y
48+
[1.0 * x, 1.0 * y + 1.0, 1.0 + -1.0 * x, -1.0 * y] in Nonnegatives(4)
49+
""",
50+
)
51+
return
52+
end
53+
3954
function test_runtests_infinity_lower()
4055
MOI.Bridges.runtests(
4156
MOI.Bridges.Constraint.SplitHyperRectangleBridge,
@@ -99,6 +114,22 @@ function test_runtests_free_row()
99114
return
100115
end
101116

117+
function test_basic_HyperRectangle()
118+
model = MOI.Bridges.Constraint.SplitHyperRectangle{Float64}(
119+
MOI.Utilities.Model{Float64}(),
120+
)
121+
config = MOI.Test.Config()
122+
MOI.empty!(model)
123+
MOI.Test.test_basic_VectorOfVariables_HyperRectangle(model, config)
124+
MOI.empty!(model)
125+
MOI.Test.test_basic_VectorAffineFunction_HyperRectangle(model, config)
126+
MOI.empty!(model)
127+
MOI.Test.test_basic_VectorQuadraticFunction_HyperRectangle(model, config)
128+
MOI.empty!(model)
129+
MOI.Test.test_basic_VectorNonlinearFunction_HyperRectangle(model, config)
130+
return
131+
end
132+
102133
end # module
103134

104135
TestConstraintHyperRectangle.runtests()

0 commit comments

Comments
 (0)