@@ -174,3 +174,79 @@ function MOI.get(
174
174
g = MOI. Utilities. remove_variable (f, bridge. slack)
175
175
return MOI. Utilities. convert_approx (G, g)
176
176
end
177
+
178
+ """
179
+ struct SlackBridgePrimalDualStart <: MOI.AbstractModelAttribute end
180
+
181
+ [`Bridges.Objective.SlackBridge`](@ref) introduces a new constraint into the
182
+ problem. However, because it is not a constraint bridge, it cannot intercept
183
+ calls to set [`ConstraintDualStart`](@ref) or [`ConstraintPrimalStart`](@ref).
184
+
185
+ As a work-around, set this attribute to `nothing` to set the primal and dual
186
+ start for the new constraint. This attribute must be set after
187
+ [`VariablePrimalStart`](@ref).
188
+ """
189
+ struct SlackBridgePrimalDualStart <: MOI.AbstractModelAttribute end
190
+
191
+ function MOI. throw_set_error_fallback (
192
+ :: MOI.ModelLike ,
193
+ :: SlackBridgePrimalDualStart ,
194
+ :: Nothing ,
195
+ )
196
+ return # Silently ignore if the model does not support.
197
+ end
198
+
199
+ # Pretend that every model supports, and silently skip in set if unsupported
200
+ MOI. supports_fallback (:: MOI.ModelLike , :: SlackBridgePrimalDualStart ) = true
201
+
202
+ function MOI. set (
203
+ model:: MOI.ModelLike ,
204
+ :: SlackBridgePrimalDualStart ,
205
+ b:: SlackBridge{T} ,
206
+ :: Nothing ,
207
+ ) where {T}
208
+ # !!! note
209
+ # This attribute should silently skip if the `model` does not support it.
210
+ # For other attributes, we set `supports(...) = false`, but this would
211
+ # cause `copy_to` to throw an `UnsupportedAttributeError`, which we don't
212
+ # want. The solution is to check `supports(model, ...)` in this method,
213
+ # and bail if not supported.
214
+ # ConstraintDual: if the objective function had a dual, it would be `-1` for
215
+ # the Lagrangian function to be the same.
216
+ if MOI. supports (model, MOI. ConstraintDualStart (), typeof (b. constraint))
217
+ MOI. set (model, MOI. ConstraintDualStart (), b. constraint, - one (T))
218
+ end
219
+ # ConstraintPrimal: we should set the slack of f(x) - y to be 0, and the
220
+ # start of y to be f(x).
221
+ if ! MOI. supports (model, MOI. VariablePrimalStart (), MOI. VariableIndex) ||
222
+ ! MOI. supports (model, MOI. ConstraintPrimalStart (), typeof (b. constraint))
223
+ return
224
+ end
225
+ MOI. set (model, MOI. VariablePrimalStart (), b. slack, zero (T))
226
+ f = MOI. get (model, MOI. ConstraintFunction (), b. constraint)
227
+ f_val = MOI. Utilities. eval_variables (f) do v
228
+ return MOI. get (model, MOI. VariablePrimalStart (), v)
229
+ end
230
+ f_val -= MOI. constant (MOI. get (model, MOI. ConstraintSet (), b. constraint))
231
+ MOI. set (model, MOI. VariablePrimalStart (), b. slack, f_val)
232
+ MOI. set (model, MOI. ConstraintPrimalStart (), b. constraint, zero (T))
233
+ return
234
+ end
235
+
236
+ function MOI. set (
237
+ b:: MOI.Bridges.AbstractBridgeOptimizer ,
238
+ attr:: SlackBridgePrimalDualStart ,
239
+ :: Nothing ,
240
+ )
241
+ # TODO (odow): this might fail if the SlackBridge is not the first bridge in
242
+ # the chain, but it should be for our current setup of bridges, so we
243
+ # choose to simplify this implementation.
244
+ if MOI. Bridges. is_objective_bridged (b)
245
+ obj_attr = MOI. ObjectiveFunction {function_type(bridges(b))} ()
246
+ if MOI. Bridges. is_bridged (b, obj_attr)
247
+ bridge = MOI. Bridges. bridge (b, obj_attr)
248
+ MOI. set (MOI. Bridges. recursive_model (b), attr, bridge, nothing )
249
+ end
250
+ end
251
+ return
252
+ end
0 commit comments