Skip to content

Commit

Permalink
Merge c2958e6 into 8b56fa1
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Jan 20, 2019
2 parents 8b56fa1 + c2958e6 commit 1f32742
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/src/extensions.md
Expand Up @@ -20,6 +20,7 @@ See the [bridge section in the MOI manual](http://www.juliaopt.org/MathOptInterf

```@docs
add_bridge
BridgeableConstraint
```

## Extending JuMP macros
Expand Down
8 changes: 5 additions & 3 deletions src/JuMP.jl
Expand Up @@ -147,8 +147,10 @@ mutable struct Model <: AbstractModel
# In DIRECT mode, will hold an AbstractOptimizer.
moi_backend::MOI.AbstractOptimizer
# List of bridges to add in addition to the ones added in
# `MOI.Bridges.full_bridge_optimizer`.
bridge_types::Vector{Any}
# `MOI.Bridges.full_bridge_optimizer`. With `BridgeableConstraint`, the
# same bridge may be added many times so we store them in a `Set` instead
# of, e.g., a `Vector`.
bridge_types::Set{Any}
# Hook into a solve call...function of the form f(m::Model; kwargs...),
# where kwargs get passed along to subsequent solve calls.
optimize_hook
Expand Down Expand Up @@ -240,7 +242,7 @@ function direct_model(backend::MOI.ModelLike)
Dict{MOIVAR, MOIINT}(),
Dict{MOIVAR, MOIBIN}(),
backend,
DataType[],
Set{Any}(),
nothing,
nothing,
Dict{Symbol, Any}(),
Expand Down
57 changes: 57 additions & 0 deletions src/constraints.jl
Expand Up @@ -272,6 +272,63 @@ end
# Abstract base type for all constraint types
abstract type AbstractConstraint end

"""
struct BridgeableConstraint{C, B} <: AbstractConstraint
constraint::C
bridge_type::B
end
Constraint `constraint` that can be bridged by the bridge of type `bridge_type`.
Adding this constraint to a model is equivalent to
```julia
add_bridge(model, bridge_type)
add_constraint(model, constraint)
```
## Examples
Given a new scalar set type `CustomSet` with a bridge `CustomBridge` that can
bridge `F`-in-`CustomSet` constraints, when the user does
```julia
model = Model()
@variable(model, x)
@constraint(model, x + 1 in CustomSet())
optimize!(model)
```
with an optimizer that does not support `F`-in-`CustomSet` constraints, the
constraint will not be bridge unless he manually calls `add_bridge(model,
CustomBridge)`. In order to automatically add the `CustomBridge` to any model to
which an `F`-in-`CustomSet` is added, simply add the following method:
```julia
function JuMP.build_constraint(_error::Function, func::AbstractJuMPScalar,
set::CustomSet)
constraint = ScalarConstraint(func, set)
return JuMP.BridgeableConstraint(constraint, CustomBridge)
end
### Note
JuMP extensions should extend `JuMP.build_constraint` only if they also defined
`CustomSet`, for three
reasons:
1. It is problematic if multiple extensions overload the same JuMP method.
2. A missing method will not inform the users that they forgot to load the
extension module defining the `build_constraint` method.
3. Defining a method where neither the function nor any of the argument types
are defined in the package is called [*type piracy*](https://docs.julialang.org/en/v1/manual/style-guide/index.html#Avoid-type-piracy-1)
and is discouraged in the Julia style guide.
```
"""
struct BridgeableConstraint{C, B} <: AbstractConstraint
constraint::C
bridge_type::B
end

function add_constraint(model::Model, c::BridgeableConstraint, name::String="")
add_bridge(model, c.bridge_type)
return add_constraint(model, c.constraint, name)
end

"""
jump_function(constraint::AbstractConstraint)
Expand Down
24 changes: 24 additions & 0 deletions test/model.jl
Expand Up @@ -258,6 +258,30 @@ function test_model()
@test JuMP.dual(c) == 2.0
end
end
@testset "automatically with BridgeableConstraint" begin
@testset "with_optimizer at Model" begin
model = Model(factory)
@variable(model, x)
constraint = ScalarConstraint(x, Nonnegative())
bc = BridgeableConstraint(constraint, NonnegativeBridge)
c = add_constraint(model, bc)
JuMP.optimize!(model)
@test JuMP.value(x) == 1.0
@test JuMP.value(c) == 1.0
@test JuMP.dual(c) == 2.0
end
@testset "with_optimizer at optimize!" begin
model = Model()
@variable(model, x)
constraint = ScalarConstraint(x, Nonnegative())
bc = BridgeableConstraint(constraint, NonnegativeBridge)
c = add_constraint(model, bc)
JuMP.optimize!(model, factory)
@test JuMP.value(x) == 1.0
@test JuMP.value(c) == 1.0
@test JuMP.dual(c) == 2.0
end
end
end
end

Expand Down

0 comments on commit 1f32742

Please sign in to comment.