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

Define objective bridge and objective map #867

Merged
merged 2 commits into from
Sep 4, 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
14 changes: 13 additions & 1 deletion docs/src/apireference.md
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,12 @@ Bridges.Constraint.SingleBridgeOptimizer
Bridges.Constraint.add_all_bridges
```

### Objective bridges

```@docs
Bridges.Objective.AbstractBridge
```

### Bridge interface

A bridge should implement the following functions to be usable by a bridge optimizer:
Expand All @@ -744,12 +750,18 @@ Bridges.Variable.supports_constrained_variable
Bridges.Variable.concrete_bridge_type
Bridges.Variable.bridge_constrained_variable
```
and constraint bridges should implement
constraint bridges should implement:
```@docs
supports_constraint(::Type{<:Bridges.Constraint.AbstractBridge}, ::Type{<:AbstractFunction}, ::Type{<:AbstractSet})
Bridges.Constraint.concrete_bridge_type
Bridges.Constraint.bridge_constraint
```
and objective bridges should implement:
```@docs
Bridges.set_objective_function_type
Bridges.Objective.concrete_bridge_type
Bridges.Objective.bridge_objective
```

When querying the [`NumberOfVariables`](@ref), [`NumberOfConstraints`](@ref)
and [`ListOfConstraintIndices`](@ref), the variables and constraints created
Expand Down
2 changes: 2 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 Down
14 changes: 14 additions & 0 deletions src/Bridges/Objective/Objective.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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")

end
139 changes: 139 additions & 0 deletions src/Bridges/Objective/bridge.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
"""
AbstractBridge

Subtype of [`MathOptInterface.Bridges.AbstractBridge`](@ref) for objective
bridges.
"""
abstract type AbstractBridge <: MOIB.AbstractBridge end

"""
bridge_objective(BT::Type{<:MOI.Bridges.Objective.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, ::MOI.ObjectiveSense,
bridge::MOI.Bridges.Objective.AbstractBridge,
sense::MOI.ObjectiveSense)

Informs `bridge` that the objective sense is changed to `sense`. If changing
the objective sense is not supported, the bridge should not implement this
method.
"""
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::MOI.Bridges.Objective.AbstractBridge)

Return the objective function object 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

"""
function MOI.delete(model::MOI.ModelLike, bridge::MOI.Bridges.Objective.AbstractBridge)

Delete any variable or constraint added by `bridge`.
"""
function MOI.delete(::MOI.ModelLike, bridge::AbstractBridge)
throw(ArgumentError(
"`MOI.delete` not implemented for `ObjectiveFunction` bridges of type `$(typeof(bridge))`"))
end

"""
supports_objective_function(
BT::Type{<:MOI.Bridges.Objective.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
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

"""
set_objective_function_type(BT::Type{<:MOI.Bridges.Objective.AbstractBridge},
F::Type{<:MOI.AbstractScalarFunction})

Return the type of objective function that bridges of type `BT` set
for bridging objective functions of type `F`. This fallbacks to
`set_objective_function_type(concrete_bridge_type(BT, F))`
so bridges should not implement this method.
"""
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
83 changes: 83 additions & 0 deletions src/Bridges/Objective/map.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
Map <: AbstractDict{MOI.ObjectiveFunction, AbstractBridge}

A mapping from a bridged objective function type to the bridge responsible for
bridging that type of objective function.
"""
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.Variable.SingleBridgeOptimizer`](@ref) and
[`MathOptInterface.Bridges.Constraint.SingleBridgeOptimizer`](@ref) as they do
not bridge any objective function.
"""
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) = MOIU.EmptyVector{AbstractBridge}()
Base.iterate(::EmptyMap) = nothing
function_type(::EmptyMap) = nothing
12 changes: 10 additions & 2 deletions src/Bridges/bridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,25 @@ function MOI.get(::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute,
end

"""
added_constrained_variable_types(BT::Type{<:Variable.AbstractBridge})::Bool
added_constrained_variable_types(BT::Type{<:Variable.AbstractBridge})::Vector{Tuple{DataType}}

Return a list of the types of constrained variables that bridges of concrete
type `BT` add. This is used by the [`LazyBridgeOptimizer`](@ref).
"""
function added_constrained_variable_types end

"""
added_constraint_types(BT::Type{<:Constraint.AbstractBridge})::Bool
added_constraint_types(BT::Type{<:Constraint.AbstractBridge})::Vector{Tuple{DataType, DataType}}

Return a list of the types of constraints that bridges of concrete type `BT`
add. This is used by the [`LazyBridgeOptimizer`](@ref).
"""
function added_constraint_types end

"""
set_objective_function_type(BT::Type{<:Objective.AbstractBridge})::Type{<:MOI.AbstractScalarFunction}

Return the type of objective function that bridges of concrete type `BT`
set. This is used by the [`LazyBridgeOptimizer`](@ref).
"""
function set_objective_function_type end
6 changes: 6 additions & 0 deletions test/Bridges/Bridges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ end
_timed_include(joinpath(constraint_dir, file))
end
end
@testset "Objective bridges" begin
objective_dir = joinpath(@__DIR__, "Objective")
@testset "$(file)" for file in readdir(objective_dir)
_timed_include(joinpath(objective_dir, file))
end
end
29 changes: 29 additions & 0 deletions test/Bridges/Objective/map.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Test
using MathOptInterface
const MOI = MathOptInterface
const MOIB = MOI.Bridges

struct ObjectiveDummyBridge <: MOIB.Objective.AbstractBridge
id::Int
end

function test_empty(map)
@test isempty(map)
@test length(map) == 0
@test isempty(values(map))
@test iterate(map) === nothing
@test MOIB.Objective.function_type(map) === nothing
end

map= MOIB.Objective.Map()
test_empty(map)

empty!(map)
test_empty(map)

@testset "EmptyMap" begin
map = MOIB.Objective.EmptyMap()
test_empty(map)
empty!(map)
test_empty(map)
end