Skip to content

Commit 193aec6

Browse files
blegatodow
andauthored
[Bridges] use dual of equality in slack bridge (take 2) (#2515)
* [Bridges] use dual of equality in slack bridge (#2508) * Add scaling * Update slack.jl * Update src/Bridges/Constraint/bridges/slack.jl * Update slack.jl * Update slack.jl * Update slack.jl * Update * Update src/Bridges/Constraint/bridges/slack.jl * Update --------- Co-authored-by: Oscar Dowson <[email protected]> Co-authored-by: odow <[email protected]>
1 parent 9d37d19 commit 193aec6

File tree

2 files changed

+48
-14
lines changed

2 files changed

+48
-14
lines changed

src/Bridges/Constraint/bridges/slack.jl

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,26 +124,46 @@ end
124124
function MOI.get(
125125
model::MOI.ModelLike,
126126
a::Union{MOI.ConstraintDual,MOI.ConstraintDualStart},
127-
bridge::_AbstractSlackBridge,
128-
)
127+
bridge::_AbstractSlackBridge{T},
128+
) where {T}
129129
# The dual constraint on slack (since it is free) is
130-
# -dual_slack_in_set + dual_equality = 0 so the two duals are
131-
# equal and we can return either one of them.
132-
return MOI.get(model, a, bridge.slack_in_set)
130+
# `-dual_slack_in_set + dual_equality = 0` so the two duals are
131+
# equal (modulo a rescaling for things like symmetric matrices) and we can
132+
# return either one of them.
133+
#
134+
# We decide to use the dual of the equality constraints because the
135+
# `slack_in_set` constraint might be bridged by a variable bridge that does
136+
# not support `ConstraintDual`. This is the case if the adjoint of the
137+
# linear map on which the bridge is based is not invertible, for example,
138+
# `Variable.ZerosBridge` or `SumOfSquares.Bridges.Variable.KernelBridge`.
139+
dual = MOI.get(model, a, bridge.equality)
140+
if dual === nothing
141+
return nothing
142+
elseif dual isa AbstractVector
143+
# The equality constraints gives the term <dual, primal> with the
144+
# standard inner product but <dual, primal>_PSD is like scaling each
145+
# entry of dual and primal by the entry of SetDotScalingVector. To undo,
146+
# we need to divide by the square.
147+
scale = MOI.Utilities.SetDotScalingVector{T}(bridge.set)
148+
return dual ./ scale .^ 2
149+
end
150+
return dual
133151
end
134152

135153
function MOI.set(
136154
model::MOI.ModelLike,
137155
attr::MOI.ConstraintDualStart,
138-
bridge::_AbstractSlackBridge,
156+
bridge::_AbstractSlackBridge{T},
139157
value,
140-
)
141-
# As the slack appears `+slack` in `slack_in_set` and `-slack` in equality,
142-
# giving `value` to both will cancel it out in the Lagrangian.
143-
# Giving `value` to `bridge.equality` will put the function in the
144-
# Lagrangian as expected.
158+
) where {T}
159+
# See comments in MOI.get for why we need to rescale, etc.
145160
MOI.set(model, attr, bridge.slack_in_set, value)
146-
MOI.set(model, attr, bridge.equality, value)
161+
if value isa AbstractVector
162+
scale = MOI.Utilities.SetDotScalingVector{T}(bridge.set)
163+
MOI.set(model, attr, bridge.equality, value .* scale .^ 2)
164+
else
165+
MOI.set(model, attr, bridge.equality, value)
166+
end
147167
return
148168
end
149169

@@ -209,6 +229,7 @@ end
209229
struct ScalarSlackBridge{T,F,S} <:
210230
_AbstractSlackBridge{T,MOI.VariableIndex,MOI.EqualTo{T},F,S}
211231
slack::MOI.VariableIndex
232+
set::S
212233
slack_in_set::MOI.ConstraintIndex{MOI.VariableIndex,S}
213234
equality::MOI.ConstraintIndex{F,MOI.EqualTo{T}}
214235
end
@@ -226,7 +247,7 @@ function bridge_constraint(
226247
slack, slack_in_set = MOI.add_constrained_variable(model, s)
227248
new_f = MOI.Utilities.operate(-, T, f, slack)
228249
equality = MOI.add_constraint(model, new_f, MOI.EqualTo(zero(T)))
229-
return ScalarSlackBridge{T,F,S}(slack, slack_in_set, equality)
250+
return ScalarSlackBridge{T,F,S}(slack, s, slack_in_set, equality)
230251
end
231252

232253
# Start by allowing all scalar constraints:
@@ -341,6 +362,7 @@ end
341362
struct VectorSlackBridge{T,F,S} <:
342363
_AbstractSlackBridge{T,MOI.VectorOfVariables,MOI.Zeros,F,S}
343364
slack::Vector{MOI.VariableIndex}
365+
set::S
344366
slack_in_set::MOI.ConstraintIndex{MOI.VectorOfVariables,S}
345367
equality::MOI.ConstraintIndex{F,MOI.Zeros}
346368
end
@@ -358,7 +380,7 @@ function bridge_constraint(
358380
slack, slack_in_set = MOI.add_constrained_variables(model, s)
359381
new_f = MOI.Utilities.operate(-, T, f, MOI.VectorOfVariables(slack))
360382
equality = MOI.add_constraint(model, new_f, MOI.Zeros(d))
361-
return VectorSlackBridge{T,F,S}(slack, slack_in_set, equality)
383+
return VectorSlackBridge{T,F,S}(slack, s, slack_in_set, equality)
362384
end
363385

364386
function MOI.supports_constraint(

test/Bridges/Constraint/slack.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,18 @@ function test_runtests()
384384
[y, z] in SecondOrderCone(2)
385385
""",
386386
)
387+
MOI.Bridges.runtests(
388+
MOI.Bridges.Constraint.VectorSlackBridge,
389+
"""
390+
variables: x11, x12, x22
391+
[1.0 * x11, x12, 2.0 * x22] in PositiveSemidefiniteConeTriangle(2)
392+
""",
393+
"""
394+
variables: x11, x12, x22, y11, y12, y22
395+
[1.0 * x11 + -1.0 * y11, x12 + -1.0 * y12, 2.0 * x22 + -1.0 * y22] in Zeros(3)
396+
[y11, y12, y22] in PositiveSemidefiniteConeTriangle(2)
397+
""",
398+
)
387399
return
388400
end
389401

0 commit comments

Comments
 (0)