Skip to content

Commit

Permalink
Add Objective bridges
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Sep 1, 2019
1 parent 111fba5 commit bf29a46
Show file tree
Hide file tree
Showing 18 changed files with 894 additions and 34 deletions.
3 changes: 3 additions & 0 deletions src/Bridges/Bridges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ include("bridge_optimizer.jl")
include("Variable/Variable.jl")
# Constraint bridges
include("Constraint/Constraint.jl")
# Objective bridges
include("Objective/Objective.jl")

include("lazy_bridge_optimizer.jl")

Expand All @@ -26,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
35 changes: 35 additions & 0 deletions src/Bridges/Objective/Objective.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Objective

using MathOptInterface
const MOI = MathOptInterface
const MOIU = MOI.Utilities
const MOIB = MOI.Bridges

# Definition of an objective bridge
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
118 changes: 118 additions & 0 deletions src/Bridges/Objective/bridge.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
AbstractBridge
Subtype of [`MathOptInterface.Bridges.AbstractBridge`](@ref) for objective
bridges.
"""
abstract type AbstractBridge <: MOIB.AbstractBridge end

"""
bridge_objective(BT::Type{<:AbstractBridge}, model::MOI.ModelLike,
func::MOI.AbstractScalarFunction)
Bridge the objective function `func` using bridge `BT` to `model` and returns
a bridge object of type `BT`. The bridge type `BT` should be a concrete type,
that is, all the type parameters of the bridge should be set. Use
[`concrete_bridge_type`](@ref) to obtain a concrete type for a given function
type.
"""
function bridge_objective(::Type{<:AbstractBridge}, ::MOI.ModelLike,
func::MOI.AbstractScalarFunction)
throw(MOI.UnsupportedAttribute(MOI.ObjectiveFunction{typeof(func)}()))
end

"""
function MOI.set(model::MOI.ModelLike, attr::MOI.ObjectiveSense,
bridge::AbstractBridge, sense::MOI.ObjectiveSense)
Return the value of the attribute `attr` of the model `model` for the
variable bridged by `bridge`.
"""
function MOI.set(::MOI.ModelLike, ::MOI.ObjectiveSense,
bridge::AbstractBridge, ::MOI.OptimizationSense)
throw(ArgumentError(
"Objective bridge of type `$(typeof(bridge))` does not support" *
" modifying the objective sense. As a workaround, set the sense to" *
" `MOI.FEASIBILITY_SENSE` to clear the objective function and" *
" bridges."))
end

"""
function MOI.get(model::MOI.ModelLike, attr::MOI.ObjectiveFunction,
bridge::AbstractBridge)
Return the value of the objective function bridged by `bridge` for the model
`model`.
"""
function MOI.get(::MOI.ModelLike, ::MOI.ObjectiveFunction,
bridge::AbstractBridge)
throw(ArgumentError(
"ObjectiveFunction bridge of type `$(typeof(bridge))` does not" *
" support getting the objective function."))
end

"""
supports_objective_function(
BT::Type{<:AbstractBridge},
F::Type{<:MOI.AbstractScalarFunction})::Bool
Return a `Bool` indicating whether the bridges of type `BT` support bridging
objective functions of type `F`.
"""
function supports_objective_function(
::Type{<:AbstractBridge}, ::Type{<:MOI.AbstractScalarFunction})
return false
end

"""
added_constrained_variable_types(BT::Type{<:MOI.Bridges.Objective.AbstractBridge},
F::Type{<:MOI.AbstractScalarFunction})
Return a list of the types of constrained variables that bridges of type `BT`
add for bridging objective functions of type `F`. This fallbacks to
`added_constrained_variable_types(concrete_bridge_type(BT, F))`
so bridges should not implement this method.
```
"""
function MOIB.added_constrained_variable_types(
BT::Type{<:AbstractBridge}, F::Type{<:MOI.AbstractScalarFunction})
return MOIB.added_constrained_variable_types(concrete_bridge_type(BT, F))
end

"""
added_constraint_types(BT::Type{<:MOI.Bridges.Objective.AbstractBridge},
F::Type{<:MOI.AbstractScalarFunction})
Return a list of the types of constraints that bridges of type `BT` add
add for bridging objective functions of type `F`. This fallbacks to
`added_constraint_types(concrete_bridge_type(BT, S))`
so bridges should not implement this method.
"""
function MOIB.added_constraint_types(
BT::Type{<:AbstractBridge}, F::Type{<:MOI.AbstractScalarFunction})
return MOIB.added_constraint_types(concrete_bridge_type(BT, F))
end

function MOIB.set_objective_function_type(
BT::Type{<:AbstractBridge}, F::Type{<:MOI.AbstractScalarFunction})
return MOIB.set_objective_function_type(concrete_bridge_type(BT, F))
end


"""
concrete_bridge_type(BT::Type{<:MOI.Bridges.Objective.AbstractBridge},
F::Type{<:MOI.AbstractScalarFunction})::DataType
Return the concrete type of the bridge supporting objective functions of type
`F`. This function can only be called if `MOI.supports_objective_function(BT, F)`
is `true`.
"""
function concrete_bridge_type(bridge_type::DataType,
::Type{<:MOI.AbstractScalarFunction})
return bridge_type
end

function concrete_bridge_type(b::MOIB.AbstractBridgeOptimizer,
F::Type{<:MOI.AbstractScalarFunction})
return concrete_bridge_type(MOIB.bridge_type(b, F), F)
end
43 changes: 43 additions & 0 deletions src/Bridges/Objective/functionize.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
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,
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

function MOI.delete(model::MOI.ModelLike, bridge::FunctionizeBridge) end

function MOI.set(::MOI.ModelLike, ::MOI.ObjectiveSense,
::FunctionizeBridge, ::MOI.OptimizationSense)
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
81 changes: 81 additions & 0 deletions src/Bridges/Objective/map.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
Map <: AbstractDict{MOI.ObjectiveFunction, AbstractBridge}
Mapping between bridged variables and the bridge that bridged the variable.
"""
mutable struct Map <: AbstractDict{MOI.ObjectiveFunction, AbstractBridge}
bridges::Dict{MOI.ObjectiveFunction, AbstractBridge}
function_type::Union{Nothing, Type{<:MOI.AbstractScalarFunction}}
end
function Map()
return Map(Dict{MOI.ObjectiveFunction, AbstractBridge}(), nothing)
end

# Implementation of `AbstractDict` interface.

Base.isempty(map::Map) = isempty(map.bridges)
function Base.empty!(map::Map)
empty!(map.bridges)
map.function_type = nothing
end
function Base.haskey(map::Map, attr::MOI.ObjectiveFunction)
return haskey(map.bridges, attr)
end
function Base.getindex(map::Map, attr::MOI.ObjectiveFunction)
return map.bridges[attr]
end
Base.length(map::Map) = length(map.bridges)
Base.values(map::Map) = values(map.bridges)
Base.iterate(map::Map, args...) = iterate(map.bridges, args...)

# Custom interface for information needed by `AbstractBridgeOptimizer`s that is
# not part of the `AbstractDict` interface.

"""
function_type(map::Map)
Return the function type of the [`root_bridge`](@ref) or `nothing` if `map` is
empty.
"""
function_type(map::Map) = map.function_type

"""
root_bridge(map::Map)
Return the last bridge added.
"""
function root_bridge(map::Map)
attr = MOI.ObjectiveFunction{function_type(map)}()
return map[attr]
end

"""
add_key_for_bridge(map::Map, bridge::AbstractBridge,
func::MOI.AbstractScalarFunction)
Stores the mapping `atttr => bridge` where `attr` is
`MOI.ObjectiveFunction{typeof(func)}()` and set [`function_type`](@ref) to
`typeof(func)`.
"""
function add_key_for_bridge(map::Map, bridge::AbstractBridge,
func::MOI.AbstractScalarFunction)
attr = MOI.ObjectiveFunction{typeof(func)}()
map.function_type = typeof(func)
map.bridges[attr] = bridge
end

"""
EmptyMap <: AbstractDict{MOI.ObjectiveFunction, AbstractBridge}
Empty version of [`Map`](@ref). It is used by
[`MathOptInterface.Bridges.Constraint.SingleBridgeOptimizer`](@ref) as it does
not bridge any variable.
"""
struct EmptyMap <: AbstractDict{MOI.ObjectiveFunction, AbstractBridge} end
Base.isempty(::EmptyMap) = true
function Base.empty!(::EmptyMap) end
Base.length(::EmptyMap) = 0
Base.haskey(::EmptyMap, ::MOI.ObjectiveFunction) = false
Base.values(::EmptyMap) = MOIB.EmptyVector{AbstractBridge}()
Base.iterate(::EmptyMap) = nothing
function_type(::EmptyMap) = nothing
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
Loading

0 comments on commit bf29a46

Please sign in to comment.