Skip to content

Commit 278fba5

Browse files
blegatodow
andauthored
Add set conversion bridge (#2536)
* 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]>
1 parent 95e16c4 commit 278fba5

File tree

4 files changed

+206
-0
lines changed

4 files changed

+206
-0
lines changed

src/Bridges/Constraint/Constraint.jl

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ include("bridges/split_complex_zeros.jl")
5959
include("bridges/split_hyperrectangle.jl")
6060
include("bridges/hermitian.jl")
6161
include("bridges/square.jl")
62+
include("bridges/set_conversion.jl")
6263
include("bridges/set_dot_scaling.jl")
6364
include("bridges/table.jl")
6465
include("bridges/vectorize.jl")

src/Bridges/Constraint/bridges/functionize.jl

+2
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ for these pairs of functions:
187187
* [`MOI.ScalarQuadraticFunction`](@ref) to [`MOI.ScalarNonlinearFunction`](@ref)
188188
* [`MOI.VectorAffineFunction`](@ref) to [`MOI.VectorQuadraticFunction`](@ref)
189189
190+
See also [`SetConversionBridge`](@ref).
191+
190192
## Source node
191193
192194
`FunctionConversionBridge` supports:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
"""
8+
SetConversionBridge{T,S2,S1,F} <:
9+
MOI.Bridges.Constraint.SetMapBridge{T,S2,S1,F,F}
10+
11+
`SetConversionBridge` implements the following reformulations:
12+
13+
* ``f(x) \\in S1`` into ``f(x) \\in S2``
14+
15+
In order to add this bridge, you need to create a bridge specific
16+
for a given type `T` and set `S2`:
17+
```julia
18+
MOI.Bridges.add_bridge(model, MOI.Bridges.Constraint.SetConversionBridge{T,S2})
19+
```
20+
In order to define a bridge with `S2` specified but `T` unspecified, for example
21+
for `JuMP.add_bridge`, you can use
22+
```julia
23+
const MyBridge{T,S1,F} = MOI.Bridges.Constraint.SetConversionBridge{T,S2,S1,F}
24+
```
25+
26+
See also [`FunctionConversionBridge`](@ref).
27+
28+
## Source node
29+
30+
`SetConversionBridge` supports:
31+
32+
* `F` in `S1`
33+
34+
## Target nodes
35+
36+
`SetConversionBridge` creates:
37+
38+
* `F` in `S2`
39+
"""
40+
struct SetConversionBridge{T,S2,S1,F} <:
41+
MOI.Bridges.Constraint.SetMapBridge{T,S2,S1,F,F}
42+
constraint::MOI.ConstraintIndex{F,S2}
43+
end
44+
45+
function MOI.supports_constraint(
46+
::Type{SetConversionBridge{T,S2}},
47+
::Type{F},
48+
::Type{S1},
49+
) where {T,F<:MOI.AbstractFunction,S1<:MOI.AbstractSet,S2}
50+
return isfinite(MOI.Bridges.Constraint.conversion_cost(S2, S1))
51+
end
52+
53+
function MOI.Bridges.Constraint.concrete_bridge_type(
54+
::Type{SetConversionBridge{T,S2}},
55+
::Type{F},
56+
::Type{S1},
57+
) where {T,F<:MOI.AbstractFunction,S1<:MOI.AbstractSet,S2}
58+
return SetConversionBridge{T,S2,S1,F}
59+
end
60+
61+
function MOI.Bridges.Constraint.conversion_cost(
62+
::Type{<:MOI.AbstractSet},
63+
::Type{<:MOI.AbstractSet},
64+
)
65+
return Inf
66+
end
67+
68+
function MOI.Bridges.bridging_cost(
69+
::Type{<:SetConversionBridge{T,S2,S1}},
70+
) where {T,S2,S1}
71+
return MOI.Bridges.Constraint.conversion_cost(S2, S1)
72+
end
73+
74+
function MOI.Bridges.map_set(
75+
::Type{<:SetConversionBridge{T,S2,S1}},
76+
set::S1,
77+
) where {T,S2,S1}
78+
return convert(S2, set)
79+
end
80+
81+
function MOI.Bridges.inverse_map_set(
82+
::Type{<:SetConversionBridge{T,S2,S1}},
83+
set::S2,
84+
) where {T,S2,S1}
85+
return convert(S1, set)
86+
end
87+
88+
function MOI.Bridges.map_function(::Type{<:SetConversionBridge}, func)
89+
return func
90+
end
91+
92+
function MOI.Bridges.inverse_map_function(::Type{<:SetConversionBridge}, func)
93+
return func
94+
end
95+
96+
function MOI.Bridges.adjoint_map_function(::Type{<:SetConversionBridge}, func)
97+
return func
98+
end
99+
100+
function MOI.Bridges.inverse_adjoint_map_function(
101+
::Type{<:SetConversionBridge},
102+
func,
103+
)
104+
return func
105+
end
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
module TestConstraintSetConversion
8+
9+
using Test
10+
11+
import MathOptInterface as MOI
12+
13+
function runtests()
14+
for name in names(@__MODULE__; all = true)
15+
if startswith("$(name)", "test_")
16+
@testset "$(name)" begin
17+
getfield(@__MODULE__, name)()
18+
end
19+
end
20+
end
21+
return
22+
end
23+
24+
struct Zero <: MOI.AbstractScalarSet end
25+
26+
function MOI.Bridges.Constraint.conversion_cost(
27+
::Type{MOI.EqualTo{Float64}},
28+
::Type{Zero},
29+
)
30+
return 1.0
31+
end
32+
33+
Base.convert(::Type{MOI.EqualTo{Float64}}, ::Zero) = MOI.EqualTo(0.0)
34+
35+
function Base.convert(::Type{Zero}, s::MOI.EqualTo)
36+
if !iszero(s.value)
37+
throw(InexactError(convert, (Zero, s)))
38+
end
39+
return Zero()
40+
end
41+
42+
# Does not make sense that this is convertible but it's
43+
# just to test `conversion_cost`
44+
function MOI.Bridges.Constraint.conversion_cost(
45+
::Type{MOI.LessThan{Float64}},
46+
::Type{Zero},
47+
)
48+
return 10.0
49+
end
50+
51+
const EqualToBridge{T,S1,F} =
52+
MOI.Bridges.Constraint.SetConversionBridge{T,MOI.EqualTo{T},S1,F}
53+
54+
function test_runtests()
55+
MOI.Bridges.runtests(
56+
EqualToBridge,
57+
model -> begin
58+
x = MOI.add_variable(model)
59+
MOI.add_constraint(model, x, Zero())
60+
end,
61+
model -> begin
62+
x = MOI.add_variable(model)
63+
MOI.add_constraint(model, x, MOI.EqualTo(0.0))
64+
end,
65+
)
66+
return
67+
end
68+
69+
function test_conversion_cost(T = Float64)
70+
model = MOI.Utilities.Model{T}()
71+
bridged = MOI.Bridges.LazyBridgeOptimizer(model)
72+
MOI.Bridges.add_bridge(
73+
bridged,
74+
MOI.Bridges.Constraint.SetConversionBridge{T,MOI.LessThan{T}},
75+
)
76+
@test MOI.Bridges.bridge_type(bridged, MOI.VariableIndex, Zero) ==
77+
MOI.Bridges.Constraint.SetConversionBridge{
78+
T,
79+
MOI.LessThan{T},
80+
Zero,
81+
MOI.VariableIndex,
82+
}
83+
MOI.Bridges.add_bridge(
84+
bridged,
85+
MOI.Bridges.Constraint.SetConversionBridge{T,MOI.EqualTo{T}},
86+
)
87+
@test MOI.Bridges.bridge_type(bridged, MOI.VariableIndex, Zero) ==
88+
MOI.Bridges.Constraint.SetConversionBridge{
89+
T,
90+
MOI.EqualTo{T},
91+
Zero,
92+
MOI.VariableIndex,
93+
}
94+
end
95+
96+
end # module
97+
98+
TestConstraintSetConversion.runtests()

0 commit comments

Comments
 (0)