From 1867a37942babd3d234dd1afb5de9118949924ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 7 Mar 2023 13:53:51 +0100 Subject: [PATCH 1/2] Objective starting values --- src/optimizer_interface.jl | 81 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/optimizer_interface.jl b/src/optimizer_interface.jl index 79941426f8f..eb47e8454b9 100644 --- a/src/optimizer_interface.jl +++ b/src/optimizer_interface.jl @@ -985,6 +985,85 @@ function optimizer_index(x::Union{VariableRef,ConstraintRef{Model}}) return _moi_optimizer_index(backend(model), index(x)) end +struct ObjectiveFunctionAttribute{A,F} + attr::A +end + +""" + struct ObjectiveDualStart <: MOI.AbstractModelAttribute end + +If the objective function had a dual, it would be `-1` for the Lagrangian +function to be the same. +When the `MOI.Bridges.Objective.SlackBridge` is used, it creates a constraint. +The dual of this constraint is therefore `-1` as well. +When setting this attribute, it allows to set the constraint dual of this +constraint. +""" +struct ObjectiveDualStart <: MOI.AbstractModelAttribute end +# Defining it for `MOI.set` leads to ambiguity +MOI.throw_set_error_fallback(::MOI.ModelLike, ::ObjectiveDualStart, value) = nothing + +""" + struct ObjectiveSlackGapPrimalStart <: MOI.AbstractModelAttribute end + +If the objective function had a dual, it would be `-1` for the Lagrangian +function to be the same. +When the `MOI.Bridges.Objective.SlackBridge` is used, it creates a constraint. +The dual of this constraint is therefore `-1` as well. +When setting this attribute, it allows to set the constraint dual of this +constraint. +""" +struct ObjectiveSlackGapPrimalStart <: MOI.AbstractModelAttribute end +MOI.throw_set_error_fallback(::MOI.ModelLike, ::ObjectiveSlackGapPrimalStart, value) = nothing + +function MOI.set( + b::MOI.Bridges.AbstractBridgeOptimizer, + attr::ObjectiveFunctionAttribute{A,F}, + value, +) where {A,F} + obj_attr = MOI.ObjectiveFunction{F}() + if MOI.Bridges.is_bridged(b, obj_attr) + return MOI.set(MOI.Bridges.recursive_model(b), attr, MOI.Bridges.bridge(b, obj_attr), value) + else + return MOI.set(b.model, attr.attr, value) + end +end + +function MOI.set( + b::MOI.Bridges.AbstractBridgeOptimizer, + attr::Union{ + ObjectiveDualStart, + ObjectiveSlackGapPrimalStart, + }, + value, +) + if MOI.Bridges.is_objective_bridged(b) + F = MOI.Bridges.Objective.function_type(MOI.Bridges.Objective.bridges(b)) + return MOI.set(b, ObjectiveFunctionAttribute{typeof(attr),F}(attr), value) + else + return MOI.set(b.model, attr, value) + end +end + +function MOI.set(model::MOI.ModelLike, ::ObjectiveFunctionAttribute{ObjectiveDualStart}, b::MOI.Bridges.Objective.SlackBridge, value) + MOI.set(model, MOI.ConstraintDualStart(), b.constraint, value) +end + +function MOI.set(model::MOI.ModelLike, ::ObjectiveFunctionAttribute{ObjectiveSlackGapPrimalStart}, b::MOI.Bridges.Objective.SlackBridge{T}, value) where {T} + # `f(x) - slack = value` so `slack = f(x) - value` + fun = MOI.get(model, MOI.ConstraintFunction(), b.constraint) + set = MOI.get(model, MOI.ConstraintSet(), b.constraint) + MOI.Utilities.operate!(-, T, fun, MOI.constant(set)) + # `fun = f - slack` so we remove the term `-slack` to get `f` + f = MOI.Utilities.remove_variable(fun, b.slack) + f_val = MOI.Utilities.eval_variables(f) do v + MOI.get(model, MOI.VariablePrimalStart(), v) + end + MOI.set(model, MOI.VariablePrimalStart(), b.slack, f_val - value) + MOI.set(model, MOI.ConstraintPrimalStart(), b.constraint, value) +end + + """ set_start_values( model::Model; @@ -1090,6 +1169,8 @@ function set_start_values( for (ci, dual_start) in constraint_dual set_dual_start_value(ci, dual_start) end + MOI.set(model, ObjectiveDualStart(), -1.0) + MOI.set(model, ObjectiveSlackGapPrimalStart(), 0.0) return end From 904b39142a2e7ba0313e846b1041146cfae2989a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 7 Mar 2023 14:25:43 +0100 Subject: [PATCH 2/2] Fix format --- src/optimizer_interface.jl | 59 ++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/optimizer_interface.jl b/src/optimizer_interface.jl index eb47e8454b9..1068113d7d4 100644 --- a/src/optimizer_interface.jl +++ b/src/optimizer_interface.jl @@ -1001,7 +1001,13 @@ constraint. """ struct ObjectiveDualStart <: MOI.AbstractModelAttribute end # Defining it for `MOI.set` leads to ambiguity -MOI.throw_set_error_fallback(::MOI.ModelLike, ::ObjectiveDualStart, value) = nothing +function MOI.throw_set_error_fallback( + ::MOI.ModelLike, + ::ObjectiveDualStart, + value, +) + return nothing +end """ struct ObjectiveSlackGapPrimalStart <: MOI.AbstractModelAttribute end @@ -1014,7 +1020,13 @@ When setting this attribute, it allows to set the constraint dual of this constraint. """ struct ObjectiveSlackGapPrimalStart <: MOI.AbstractModelAttribute end -MOI.throw_set_error_fallback(::MOI.ModelLike, ::ObjectiveSlackGapPrimalStart, value) = nothing +function MOI.throw_set_error_fallback( + ::MOI.ModelLike, + ::ObjectiveSlackGapPrimalStart, + value, +) + return nothing +end function MOI.set( b::MOI.Bridges.AbstractBridgeOptimizer, @@ -1023,7 +1035,12 @@ function MOI.set( ) where {A,F} obj_attr = MOI.ObjectiveFunction{F}() if MOI.Bridges.is_bridged(b, obj_attr) - return MOI.set(MOI.Bridges.recursive_model(b), attr, MOI.Bridges.bridge(b, obj_attr), value) + return MOI.set( + MOI.Bridges.recursive_model(b), + attr, + MOI.Bridges.bridge(b, obj_attr), + value, + ) else return MOI.set(b.model, attr.attr, value) end @@ -1031,25 +1048,38 @@ end function MOI.set( b::MOI.Bridges.AbstractBridgeOptimizer, - attr::Union{ - ObjectiveDualStart, - ObjectiveSlackGapPrimalStart, - }, + attr::Union{ObjectiveDualStart,ObjectiveSlackGapPrimalStart}, value, ) if MOI.Bridges.is_objective_bridged(b) - F = MOI.Bridges.Objective.function_type(MOI.Bridges.Objective.bridges(b)) - return MOI.set(b, ObjectiveFunctionAttribute{typeof(attr),F}(attr), value) + F = MOI.Bridges.Objective.function_type( + MOI.Bridges.Objective.bridges(b), + ) + return MOI.set( + b, + ObjectiveFunctionAttribute{typeof(attr),F}(attr), + value, + ) else return MOI.set(b.model, attr, value) end end -function MOI.set(model::MOI.ModelLike, ::ObjectiveFunctionAttribute{ObjectiveDualStart}, b::MOI.Bridges.Objective.SlackBridge, value) - MOI.set(model, MOI.ConstraintDualStart(), b.constraint, value) +function MOI.set( + model::MOI.ModelLike, + ::ObjectiveFunctionAttribute{ObjectiveDualStart}, + b::MOI.Bridges.Objective.SlackBridge, + value, +) + return MOI.set(model, MOI.ConstraintDualStart(), b.constraint, value) end -function MOI.set(model::MOI.ModelLike, ::ObjectiveFunctionAttribute{ObjectiveSlackGapPrimalStart}, b::MOI.Bridges.Objective.SlackBridge{T}, value) where {T} +function MOI.set( + model::MOI.ModelLike, + ::ObjectiveFunctionAttribute{ObjectiveSlackGapPrimalStart}, + b::MOI.Bridges.Objective.SlackBridge{T}, + value, +) where {T} # `f(x) - slack = value` so `slack = f(x) - value` fun = MOI.get(model, MOI.ConstraintFunction(), b.constraint) set = MOI.get(model, MOI.ConstraintSet(), b.constraint) @@ -1057,13 +1087,12 @@ function MOI.set(model::MOI.ModelLike, ::ObjectiveFunctionAttribute{ObjectiveSla # `fun = f - slack` so we remove the term `-slack` to get `f` f = MOI.Utilities.remove_variable(fun, b.slack) f_val = MOI.Utilities.eval_variables(f) do v - MOI.get(model, MOI.VariablePrimalStart(), v) + return MOI.get(model, MOI.VariablePrimalStart(), v) end MOI.set(model, MOI.VariablePrimalStart(), b.slack, f_val - value) - MOI.set(model, MOI.ConstraintPrimalStart(), b.constraint, value) + return MOI.set(model, MOI.ConstraintPrimalStart(), b.constraint, value) end - """ set_start_values( model::Model;