Skip to content

Commit

Permalink
Add more tests and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Sep 4, 2019
1 parent f96a3dc commit 4fcf55b
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 14 deletions.
23 changes: 23 additions & 0 deletions docs/src/apireference.md
Original file line number Diff line number Diff line change
Expand Up @@ -734,10 +734,33 @@ Bridges.Constraint.add_all_bridges

### Objective bridges

When an objective is set with [`set`](@ref), objective bridges
allow to set a *bridged objective* to the underlying model that do not
correspond to the objective set by the user. This equivalent formulation may add
constraints (and possibly also variables) in the underlying model in addition
to setting an objective function.

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

Below is the list of objective bridges implemented in this package.
```@docs
Bridges.Objective.SlackBridge
Bridges.Objective.FunctionizeBridge
```
For each bridge defined in this package, a corresponding
[`Bridges.Objective.SingleBridgeOptimizer`](@ref) is available with the same
name without the "Bridge" suffix, e.g., `Slack` is a `SingleBridgeOptimizer`
for the `SlackBridge`. Moreover, they are all added in the
[`Bridges.LazyBridgeOptimizer`](@ref) returned by
[`Bridges.full_bridge_optimizer`](@ref) as it calls
[`Bridges.Objective.add_all_bridges`](@ref).
```@docs
Bridges.Objective.SingleBridgeOptimizer
Bridges.Objective.add_all_bridges
```

### Bridge interface

A bridge should implement the following functions to be usable by a bridge optimizer:
Expand Down
8 changes: 8 additions & 0 deletions src/Bridges/Objective/functionize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ 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
Expand Down
21 changes: 20 additions & 1 deletion src/Bridges/Objective/slack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function bridge_objective(::Type{SlackBridge{T, F, G}}, model::MOI.ModelLike,
error("Set `MOI.ObjectiveSense` before `MOI.ObjectiveFunction` when",
" using `MOI.Bridges.Objective.SlackBridge`.")
end
constraint = MOIU.add_scalar_constraint(model, f, set)
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
Expand All @@ -54,6 +54,25 @@ function concrete_bridge_type(::Type{<:SlackBridge{T}},
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)
Expand Down
20 changes: 19 additions & 1 deletion src/Bridges/bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,9 @@ function MOI.get(b::AbstractBridgeOptimizer,
for bridge in values(Constraint.bridges(b))
_remove_bridged(list, bridge, attr)
end
for bridge in values(Objective.bridges(b))
_remove_bridged(list, bridge, attr)
end
return list
end
function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.NumberOfVariables)
Expand All @@ -422,6 +425,9 @@ function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.NumberOfVariables)
for bridge in values(Constraint.bridges(b))
s -= MOI.get(bridge, attr)
end
for bridge in values(Objective.bridges(b))
s -= MOI.get(bridge, attr)
end
return s
end

Expand All @@ -446,6 +452,9 @@ function MOI.get(b::AbstractBridgeOptimizer,
for bridge in values(Constraint.bridges(b))
s -= MOI.get(bridge, attr)
end
for bridge in values(Objective.bridges(b))
s -= MOI.get(bridge, attr)
end
return s
end
function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ListOfConstraints)
Expand Down Expand Up @@ -569,6 +578,7 @@ function MOI.get(b::AbstractBridgeOptimizer,
end
function MOI.set(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveSense,
value::MOI.OptimizationSense)
MOI.set(b.model, attr, value)
if is_objective_bridged(b)
if value == MOI.FEASIBILITY_SENSE
_delete_objective_bridges(b)
Expand All @@ -578,7 +588,6 @@ function MOI.set(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveSense,
end
end
end
MOI.set(b.model, attr, value)
end
function _bridge_objective(b, BridgeType, func)
bridge = Objective.bridge_objective(BridgeType, b, func)
Expand All @@ -588,7 +597,16 @@ function MOI.set(b::AbstractBridgeOptimizer,
attr::MOI.ObjectiveFunction,
func::MOI.AbstractScalarFunction)
if is_objective_bridged(b)
# Clear objective function by setting sense to `MOI.FEASIBILITY_SENSE`
# first. This is needed if the objective function of `b.model` is
# `MOI.SingleVariable(slack)` where `slack` is the slack variable
# created by `Objective.SlackBridge`.
sense = MOI.get(b.model, MOI.ObjectiveSense())
MOI.set(b.model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
_delete_objective_bridges(b)
if sense != MOI.FEASIBILITY_SENSE
MOI.set(b.model, MOI.ObjectiveSense(), sense)
end
end
if Variable.has_bridges(Variable.bridges(b))
if func isa MOI.SingleVariable
Expand Down
9 changes: 7 additions & 2 deletions src/Utilities/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,12 @@ end
# Objective
MOI.get(model::AbstractModel, ::MOI.ObjectiveSense) = model.sense
MOI.supports(model::AbstractModel, ::MOI.ObjectiveSense) = true
function MOI.set(model::AbstractModel, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense)
function MOI.set(model::AbstractModel{T}, ::MOI.ObjectiveSense,
sense::MOI.OptimizationSense) where T
if sense == MOI.FEASIBILITY_SENSE
model.objectiveset = false
model.objective = zero(MOI.ScalarAffineFunction{T})
end
model.senseset = true
model.sense = sense
end
Expand Down Expand Up @@ -922,7 +927,7 @@ macro model(model_name, ss, sst, vs, vst, sf, sft, vf, vft)
model.senseset = false
model.sense = $MOI.FEASIBILITY_SENSE
model.objectiveset = false
model.objective = $SAF{T}(MOI.ScalarAffineTerm{T}[], zero(T))
model.objective = zero($MOI.ScalarAffineFunction{T})
model.num_variables_created = 0
model.variable_indices = nothing
model.single_variable_mask = UInt8[]
Expand Down
14 changes: 9 additions & 5 deletions test/Bridges/Objective/slack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,24 @@ end
MOIU.set_mock_optimize!(mock,
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.OPTIMAL,
(MOI.FEASIBLE_POINT, [1.0, 2.0])
(MOI.FEASIBLE_POINT, [1.0, 2.0, 5.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.OPTIMAL,
(MOI.FEASIBLE_POINT, [1.0, 2.0])
(MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.OPTIMAL,
(MOI.FEASIBLE_POINT, [1.0, 2.0])
(MOI.FEASIBLE_POINT, [1.0, 2.0, 2.0])
),
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
MOI.OPTIMAL,
(MOI.FEASIBLE_POINT, [1.0, 2.0])
(MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0])
)
)
MOIT.solve_qp_edge_cases(mock, config)
MOIT.solve_qp_edge_cases(bridged_mock, config)
test_delete_objective(bridged_mock, 2, (
(MOI.ScalarQuadraticFunction{Float64}, MOI.GreaterThan{Float64}, 0),
(MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}, 0),
))
end
11 changes: 6 additions & 5 deletions test/Bridges/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,23 @@ function test_delete_bridged_variables(
end
function test_delete_objective(
m::MOIB.AbstractBridgeOptimizer, nvars::Int,
nocs::Tuple; used_bridges = 1)
list_num_constraints::Tuple; used_bridges = 1)
warn_incomplete_list_num_constraints(typeof(MOIB.Objective.root_bridge(MOIB.Objective.bridges(m))), list_num_constraints)
function num_bridges()
return length(MOIB.Objective.bridges(m))
end
start_num_bridges = num_bridges()
@test MOI.get(m, MOI.NumberOfVariables()) == nvars
@test length(MOI.get(m, MOI.ListOfVariableIndices())) == nvars
for noc in nocs
test_noc(m, noc...)
for num_constraints in list_num_constraints
test_num_constraints(m, num_constraints...)
end
MOI.set(m, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
@test MOI.get(m, MOI.ObjectiveSense()) == MOI.FEASIBILITY_SENSE
@test num_bridges() == start_num_bridges - used_bridges
@test MOI.get(m, MOI.NumberOfVariables()) == nvars
@test length(MOI.get(m, MOI.ListOfVariableIndices())) == nvars
for noc in nocs
test_noc(m, noc...)
for num_constraints in list_num_constraints
test_num_constraints(m, num_constraints...)
end
end

0 comments on commit 4fcf55b

Please sign in to comment.