Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Objective bridges #789

Merged
merged 5 commits into from
Sep 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Bridges/Bridges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function full_bridge_optimizer(model::MOI.ModelLike, T::Type)
bridged_model = LazyBridgeOptimizer(model)
Variable.add_all_bridges(bridged_model, T)
Constraint.add_all_bridges(bridged_model, T)
Objective.add_all_bridges(bridged_model, T)
return bridged_model
end

Expand Down
3 changes: 3 additions & 0 deletions src/Bridges/Constraint/single_bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ function MOIB.is_bridged(b::SingleBridgeOptimizer, F::Type{<:MOI.AbstractFunctio
S::Type{<:MOI.AbstractSet})
return MOIB.supports_bridging_constraint(b, F, S)
end
function MOIB.is_bridged(::SingleBridgeOptimizer, ::Type{<:MOI.AbstractScalarFunction})
return false
end
function MOIB.bridge_type(::SingleBridgeOptimizer{BT},
::Type{<:MOI.AbstractFunction},
::Type{<:MOI.AbstractSet}) where BT
Expand Down
21 changes: 21 additions & 0 deletions src/Bridges/Objective/Objective.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,25 @@ include("bridge.jl")
# Mapping between objective function attributes and bridges
include("map.jl")

# Bridge optimizer bridging a specific objective bridge
include("single_bridge_optimizer.jl")

# Objective bridges
include("functionize.jl")
const Functionize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{FunctionizeBridge{T}, OT}
include("slack.jl")
const Slack{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SlackBridge{T}, OT}

"""
add_all_bridges(bridged_model, T::Type)

Add all bridges defined in the `Bridges.Objective` submodule to `bridged_model`.
The coefficient type used is `T`.
"""
function add_all_bridges(bridged_model, T::Type)
MOIB.add_bridge(bridged_model, FunctionizeBridge{T})
MOIB.add_bridge(bridged_model, SlackBridge{T})
return
end

end
55 changes: 55 additions & 0 deletions src/Bridges/Objective/functionize.jl
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
blegat marked this conversation as resolved.
Show resolved Hide resolved

function MOI.set(::MOI.ModelLike, ::MOI.ObjectiveSense,
::FunctionizeBridge, ::MOI.OptimizationSense)
# `FunctionizeBridge` is sense agnostic, therefore, we don't need to change
# anything.
end
odow marked this conversation as resolved.
Show resolved Hide resolved
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
42 changes: 42 additions & 0 deletions src/Bridges/Objective/single_bridge_optimizer.jl
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
95 changes: 95 additions & 0 deletions src/Bridges/Objective/slack.jl
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
3 changes: 3 additions & 0 deletions src/Bridges/Variable/single_bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ function MOIB.is_bridged(::SingleBridgeOptimizer,
::Type{<:MOI.AbstractSet})
return false
end
function MOIB.is_bridged(::SingleBridgeOptimizer, ::Type{<:MOI.AbstractScalarFunction})
return false
end
function MOIB.bridge_type(::SingleBridgeOptimizer{BT},
::Type{<:MOI.AbstractSet}) where BT
return BT
Expand Down
Loading