@@ -124,26 +124,46 @@ end
124124function 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
133151end
134152
135153function 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
148168end
149169
209229struct 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}}
214235end
@@ -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)
230251end
231252
232253# Start by allowing all scalar constraints:
341362struct 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}
346368end
@@ -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)
362384end
363385
364386function MOI. supports_constraint (
0 commit comments