-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #789 from JuliaOpt/bl/objective_bridge
Add Objective bridges
- Loading branch information
Showing
15 changed files
with
898 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
""" | ||
FunctionizeBridge{T} | ||
The `FunctionizeBridge` converts a `SingleVariable` objective into a | ||
`ScalarAffineFunction{T}` objective. | ||
""" | ||
struct FunctionizeBridge{T} <: AbstractBridge end | ||
function bridge_objective(::Type{FunctionizeBridge{T}}, model::MOI.ModelLike, | ||
func::MOI.SingleVariable) where T | ||
F = MOI.ScalarAffineFunction{T} | ||
MOI.set(model, MOI.ObjectiveFunction{F}(), convert(F, func)) | ||
return FunctionizeBridge{T}() | ||
end | ||
|
||
function supports_objective_function( | ||
::Type{<:FunctionizeBridge}, ::Type{MOI.SingleVariable}) | ||
return true | ||
end | ||
MOIB.added_constrained_variable_types(::Type{<:FunctionizeBridge}) = Tuple{DataType}[] | ||
function MOIB.added_constraint_types(::Type{<:FunctionizeBridge}) | ||
return Tuple{DataType, DataType}[] | ||
end | ||
function MOIB.set_objective_function_type(::Type{FunctionizeBridge{T}}) where T | ||
return MOI.ScalarAffineFunction{T} | ||
end | ||
|
||
# Attributes, Bridge acting as a model | ||
function MOI.get(bridge::FunctionizeBridge, ::MOI.NumberOfVariables) | ||
return 0 | ||
end | ||
function MOI.get(bridge::FunctionizeBridge, ::MOI.ListOfVariableIndices) | ||
return MOI.VariableIndex[] | ||
end | ||
|
||
# No variables or constraints are created in this bridge so there is nothing to | ||
# delete. | ||
function MOI.delete(model::MOI.ModelLike, bridge::FunctionizeBridge) end | ||
|
||
function MOI.set(::MOI.ModelLike, ::MOI.ObjectiveSense, | ||
::FunctionizeBridge, ::MOI.OptimizationSense) | ||
# `FunctionizeBridge` is sense agnostic, therefore, we don't need to change | ||
# anything. | ||
end | ||
function MOI.get(model::MOI.ModelLike, | ||
attr::MOIB.ObjectiveFunctionValue{MOI.SingleVariable}, | ||
bridge::FunctionizeBridge{T}) where T | ||
F = MOI.ScalarAffineFunction{T} | ||
return MOI.get(model, MOIB.ObjectiveFunctionValue{F}()) | ||
end | ||
function MOI.get(model::MOI.ModelLike, attr::MOI.ObjectiveFunction{MOI.SingleVariable}, | ||
bridge::FunctionizeBridge{T}) where T | ||
F = MOI.ScalarAffineFunction{T} | ||
func = MOI.get(model, MOI.ObjectiveFunction{F}()) | ||
return convert(MOI.SingleVariable, func) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
""" | ||
SingleBridgeOptimizer{BT<:AbstractBridge, OT<:MOI.ModelLike} <: AbstractBridgeOptimizer | ||
The `SingleBridgeOptimizer` bridges any objective functions supported by the | ||
bridge `BT`. This is in contrast with the [`MathOptInterface.Bridges.LazyBridgeOptimizer`](@ref) | ||
which only bridges the objective functions that are unsupported by the internal model, | ||
even if they are supported by one of its bridges. | ||
""" | ||
mutable struct SingleBridgeOptimizer{BT<:AbstractBridge, OT<:MOI.ModelLike} <: MOIB.AbstractBridgeOptimizer | ||
model::OT | ||
map::Map # `MOI.ObjectiveFunction` -> objective bridge | ||
end | ||
function SingleBridgeOptimizer{BT}(model::OT) where {BT, OT <: MOI.ModelLike} | ||
SingleBridgeOptimizer{BT, OT}(model, Map()) | ||
end | ||
|
||
function bridges(bridge::MOI.Bridges.AbstractBridgeOptimizer) | ||
return EmptyMap() | ||
end | ||
bridges(bridge::SingleBridgeOptimizer) = bridge.map | ||
|
||
MOIB.supports_constraint_bridges(::SingleBridgeOptimizer) = false | ||
function MOIB.is_bridged(::SingleBridgeOptimizer, ::Type{<:MOI.AbstractSet}) | ||
return false | ||
end | ||
function MOIB.is_bridged(::SingleBridgeOptimizer, | ||
::Type{<:MOI.AbstractFunction}, | ||
::Type{<:MOI.AbstractSet}) | ||
return false | ||
end | ||
function MOIB.supports_bridging_objective_function( | ||
::SingleBridgeOptimizer{BT}, F::Type{<:MOI.AbstractScalarFunction}) where BT | ||
return supports_objective_function(BT, F) | ||
end | ||
function MOIB.is_bridged( | ||
b::SingleBridgeOptimizer, F::Type{<:MOI.AbstractScalarFunction}) | ||
return MOIB.supports_bridging_objective_function(b, F) | ||
end | ||
function MOIB.bridge_type(::SingleBridgeOptimizer{BT}, | ||
::Type{<:MOI.AbstractScalarFunction}) where BT | ||
return BT | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
""" | ||
SlackBridge{T, F, G} | ||
The `SlackBridge` converts an objective function of type `G` into a | ||
[`SingleVariable`](@ref) objective by creating a slack variable and a | ||
`F`-in-[`LessThan`](@ref) constraint for minimization or | ||
`F`-in-[`LessThan`](@ref) constraint for maximization where `F` is | ||
`MOI.Utilities.promote_operation(-, T, G, MOI.SingleVariable}`. | ||
Note that when using this bridge, changing the optimization sense | ||
is not supported. Set the sense to `MOI.FEASIBILITY_SENSE` first | ||
to delete the bridge in order to change the sense, then re-add the objective. | ||
""" | ||
struct SlackBridge{T, F<:MOI.AbstractScalarFunction, G<:MOI.AbstractScalarFunction} <: AbstractBridge | ||
slack::MOI.VariableIndex | ||
constraint::Union{MOI.ConstraintIndex{F, MOI.LessThan{T}}, | ||
MOI.ConstraintIndex{F, MOI.GreaterThan{T}}} | ||
end | ||
function bridge_objective(::Type{SlackBridge{T, F, G}}, model::MOI.ModelLike, | ||
func::G) where {T, F, G<:MOI.AbstractScalarFunction} | ||
slack = MOI.add_variable(model) | ||
fslack = MOI.SingleVariable(slack) | ||
f = MOIU.operate(-, T, func, fslack) | ||
if MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE | ||
set = MOI.LessThan(zero(T)) | ||
elseif MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE | ||
set = MOI.GreaterThan(zero(T)) | ||
else | ||
error("Set `MOI.ObjectiveSense` before `MOI.ObjectiveFunction` when", | ||
" using `MOI.Bridges.Objective.SlackBridge`.") | ||
end | ||
constraint = MOIU.normalize_and_add_constraint(model, f, set) | ||
MOI.set(model, MOI.ObjectiveFunction{MOI.SingleVariable}(), fslack) | ||
return SlackBridge{T, F, G}(slack, constraint) | ||
end | ||
|
||
function supports_objective_function( | ||
::Type{<:SlackBridge}, ::Type{MOI.SingleVariable}) | ||
return false | ||
end | ||
function supports_objective_function( | ||
::Type{<:SlackBridge}, ::Type{<:MOI.AbstractScalarFunction}) | ||
return true | ||
end | ||
MOIB.added_constrained_variable_types(::Type{<:SlackBridge}) = Tuple{DataType}[] | ||
function MOIB.added_constraint_types(::Type{<:SlackBridge{T, F}}) where {T, F} | ||
return [(F, MOI.GreaterThan{T}), (F, MOI.LessThan{T})] | ||
end | ||
function MOIB.set_objective_function_type(::Type{<:SlackBridge}) | ||
return MOI.SingleVariable | ||
end | ||
function concrete_bridge_type(::Type{<:SlackBridge{T}}, | ||
G::Type{<:MOI.AbstractScalarFunction}) where T | ||
F = MOIU.promote_operation(-, T, G, MOI.SingleVariable) | ||
return SlackBridge{T, F, G} | ||
end | ||
|
||
# Attributes, Bridge acting as a model | ||
function MOI.get(bridge::SlackBridge, ::MOI.NumberOfVariables) | ||
return 1 | ||
end | ||
function MOI.get(bridge::SlackBridge, ::MOI.ListOfVariableIndices) | ||
return [bridge.slack] | ||
end | ||
function MOI.get(bridge::SlackBridge{T, F}, ::MOI.NumberOfConstraints{F, S}) where { | ||
T, F, S <: Union{MOI.GreaterThan{T}, MOI.LessThan{T}}} | ||
return bridge.constraint isa MOI.ConstraintIndex{F, S} ? 1 : 0 | ||
end | ||
function MOI.get(bridge::SlackBridge{T, F}, ::MOI.ListOfConstraintIndices{F, S}) where { | ||
T, F, S <: Union{MOI.GreaterThan{T}, MOI.LessThan{T}}} | ||
if bridge.constraint isa MOI.ConstraintIndex{F, S} | ||
return [bridge.constraint] | ||
else | ||
return MOI.ConstraintIndex{F, S}[] | ||
end | ||
end | ||
|
||
function MOI.delete(model::MOI.ModelLike, bridge::SlackBridge) | ||
MOI.delete(model, bridge.constraint) | ||
MOI.delete(model, bridge.slack) | ||
end | ||
|
||
function MOI.get(model::MOI.ModelLike, | ||
attr::MOIB.ObjectiveFunctionValue{G}, | ||
bridge::SlackBridge{T, F, G}) where {T, F, G} | ||
slack = MOI.get(model, MOIB.ObjectiveFunctionValue{MOI.SingleVariable}()) | ||
obj_slack_constant = MOI.get(model, MOI.ConstraintPrimal(), bridge.constraint) | ||
# The constant was moved to the set as it is a scalar constraint. | ||
constant = MOI.constant(MOI.get(model, MOI.ConstraintSet(), bridge.constraint)) | ||
return obj_slack_constant + slack + constant | ||
end | ||
function MOI.get(model::MOI.ModelLike, attr::MOI.ObjectiveFunction{G}, | ||
bridge::SlackBridge{T, F, G}) where {T, F, G<:MOI.AbstractScalarFunction} | ||
func = MOI.get(model, MOI.ConstraintFunction(), bridge.constraint) | ||
return MOIU.convert_approx(G, MOIU.remove_variable(func, bridge.slack)) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.