-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add set conversion bridge * Remove debug * Add doc and tests * fix format * Fixes * Fixes * Fix format * Fix * Update src/Bridges/Constraint/bridges/set_conversion.jl * Add test * Update src/Bridges/Constraint/bridges/set_conversion.jl --------- Co-authored-by: Oscar Dowson <[email protected]>
- Loading branch information
Showing
4 changed files
with
206 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# Copyright (c) 2017: Miles Lubin and contributors | ||
# Copyright (c) 2017: Google Inc. | ||
# | ||
# Use of this source code is governed by an MIT-style license that can be found | ||
# in the LICENSE.md file or at https://opensource.org/licenses/MIT. | ||
|
||
""" | ||
SetConversionBridge{T,S2,S1,F} <: | ||
MOI.Bridges.Constraint.SetMapBridge{T,S2,S1,F,F} | ||
`SetConversionBridge` implements the following reformulations: | ||
* ``f(x) \\in S1`` into ``f(x) \\in S2`` | ||
In order to add this bridge, you need to create a bridge specific | ||
for a given type `T` and set `S2`: | ||
```julia | ||
MOI.Bridges.add_bridge(model, MOI.Bridges.Constraint.SetConversionBridge{T,S2}) | ||
``` | ||
In order to define a bridge with `S2` specified but `T` unspecified, for example | ||
for `JuMP.add_bridge`, you can use | ||
```julia | ||
const MyBridge{T,S1,F} = MOI.Bridges.Constraint.SetConversionBridge{T,S2,S1,F} | ||
``` | ||
See also [`FunctionConversionBridge`](@ref). | ||
## Source node | ||
`SetConversionBridge` supports: | ||
* `F` in `S1` | ||
## Target nodes | ||
`SetConversionBridge` creates: | ||
* `F` in `S2` | ||
""" | ||
struct SetConversionBridge{T,S2,S1,F} <: | ||
MOI.Bridges.Constraint.SetMapBridge{T,S2,S1,F,F} | ||
constraint::MOI.ConstraintIndex{F,S2} | ||
end | ||
|
||
function MOI.supports_constraint( | ||
::Type{SetConversionBridge{T,S2}}, | ||
::Type{F}, | ||
::Type{S1}, | ||
) where {T,F<:MOI.AbstractFunction,S1<:MOI.AbstractSet,S2} | ||
return isfinite(MOI.Bridges.Constraint.conversion_cost(S2, S1)) | ||
end | ||
|
||
function MOI.Bridges.Constraint.concrete_bridge_type( | ||
::Type{SetConversionBridge{T,S2}}, | ||
::Type{F}, | ||
::Type{S1}, | ||
) where {T,F<:MOI.AbstractFunction,S1<:MOI.AbstractSet,S2} | ||
return SetConversionBridge{T,S2,S1,F} | ||
end | ||
|
||
function MOI.Bridges.Constraint.conversion_cost( | ||
::Type{<:MOI.AbstractSet}, | ||
::Type{<:MOI.AbstractSet}, | ||
) | ||
return Inf | ||
end | ||
|
||
function MOI.Bridges.bridging_cost( | ||
::Type{<:SetConversionBridge{T,S2,S1}}, | ||
) where {T,S2,S1} | ||
return MOI.Bridges.Constraint.conversion_cost(S2, S1) | ||
end | ||
|
||
function MOI.Bridges.map_set( | ||
::Type{<:SetConversionBridge{T,S2,S1}}, | ||
set::S1, | ||
) where {T,S2,S1} | ||
return convert(S2, set) | ||
end | ||
|
||
function MOI.Bridges.inverse_map_set( | ||
::Type{<:SetConversionBridge{T,S2,S1}}, | ||
set::S2, | ||
) where {T,S2,S1} | ||
return convert(S1, set) | ||
end | ||
|
||
function MOI.Bridges.map_function(::Type{<:SetConversionBridge}, func) | ||
return func | ||
end | ||
|
||
function MOI.Bridges.inverse_map_function(::Type{<:SetConversionBridge}, func) | ||
return func | ||
end | ||
|
||
function MOI.Bridges.adjoint_map_function(::Type{<:SetConversionBridge}, func) | ||
return func | ||
end | ||
|
||
function MOI.Bridges.inverse_adjoint_map_function( | ||
::Type{<:SetConversionBridge}, | ||
func, | ||
) | ||
return func | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# Copyright (c) 2017: Miles Lubin and contributors | ||
# Copyright (c) 2017: Google Inc. | ||
# | ||
# Use of this source code is governed by an MIT-style license that can be found | ||
# in the LICENSE.md file or at https://opensource.org/licenses/MIT. | ||
|
||
module TestConstraintSetConversion | ||
|
||
using Test | ||
|
||
import MathOptInterface as MOI | ||
|
||
function runtests() | ||
for name in names(@__MODULE__; all = true) | ||
if startswith("$(name)", "test_") | ||
@testset "$(name)" begin | ||
getfield(@__MODULE__, name)() | ||
end | ||
end | ||
end | ||
return | ||
end | ||
|
||
struct Zero <: MOI.AbstractScalarSet end | ||
|
||
function MOI.Bridges.Constraint.conversion_cost( | ||
::Type{MOI.EqualTo{Float64}}, | ||
::Type{Zero}, | ||
) | ||
return 1.0 | ||
end | ||
|
||
Base.convert(::Type{MOI.EqualTo{Float64}}, ::Zero) = MOI.EqualTo(0.0) | ||
|
||
function Base.convert(::Type{Zero}, s::MOI.EqualTo) | ||
if !iszero(s.value) | ||
throw(InexactError(convert, (Zero, s))) | ||
end | ||
return Zero() | ||
end | ||
|
||
# Does not make sense that this is convertible but it's | ||
# just to test `conversion_cost` | ||
function MOI.Bridges.Constraint.conversion_cost( | ||
::Type{MOI.LessThan{Float64}}, | ||
::Type{Zero}, | ||
) | ||
return 10.0 | ||
end | ||
|
||
const EqualToBridge{T,S1,F} = | ||
MOI.Bridges.Constraint.SetConversionBridge{T,MOI.EqualTo{T},S1,F} | ||
|
||
function test_runtests() | ||
MOI.Bridges.runtests( | ||
EqualToBridge, | ||
model -> begin | ||
x = MOI.add_variable(model) | ||
MOI.add_constraint(model, x, Zero()) | ||
end, | ||
model -> begin | ||
x = MOI.add_variable(model) | ||
MOI.add_constraint(model, x, MOI.EqualTo(0.0)) | ||
end, | ||
) | ||
return | ||
end | ||
|
||
function test_conversion_cost(T = Float64) | ||
model = MOI.Utilities.Model{T}() | ||
bridged = MOI.Bridges.LazyBridgeOptimizer(model) | ||
MOI.Bridges.add_bridge( | ||
bridged, | ||
MOI.Bridges.Constraint.SetConversionBridge{T,MOI.LessThan{T}}, | ||
) | ||
@test MOI.Bridges.bridge_type(bridged, MOI.VariableIndex, Zero) == | ||
MOI.Bridges.Constraint.SetConversionBridge{ | ||
T, | ||
MOI.LessThan{T}, | ||
Zero, | ||
MOI.VariableIndex, | ||
} | ||
MOI.Bridges.add_bridge( | ||
bridged, | ||
MOI.Bridges.Constraint.SetConversionBridge{T,MOI.EqualTo{T}}, | ||
) | ||
@test MOI.Bridges.bridge_type(bridged, MOI.VariableIndex, Zero) == | ||
MOI.Bridges.Constraint.SetConversionBridge{ | ||
T, | ||
MOI.EqualTo{T}, | ||
Zero, | ||
MOI.VariableIndex, | ||
} | ||
end | ||
|
||
end # module | ||
|
||
TestConstraintSetConversion.runtests() |