@@ -174,3 +174,79 @@ function MOI.get(
174174 g = MOI. Utilities. remove_variable (f, bridge. slack)
175175 return MOI. Utilities. convert_approx (G, g)
176176end
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