Skip to content

Commit b92af7b

Browse files
authored
Merge pull request #439 from JuliaOpt/bl/intervalanyfunc
Support any function for interval bridge
2 parents 8ee34ab + 2b86e42 commit b92af7b

File tree

8 files changed

+290
-133
lines changed

8 files changed

+290
-133
lines changed

src/Bridges/Bridges.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function fullbridgeoptimizer(model::MOI.ModelLike, ::Type{T}) where T
4747
end
4848

4949
include("intervalbridge.jl")
50-
@bridge SplitInterval SplitIntervalBridge () (Interval,) () () () (ScalarAffineFunction,) () ()
50+
@bridge SplitInterval SplitIntervalBridge () (Interval,) () () (SingleVariable,) (ScalarAffineFunction, ScalarQuadraticFunction) () ()
5151
include("rsocbridge.jl")
5252
@bridge RSOC RSOCBridge () () (RotatedSecondOrderCone,) () () () (VectorOfVariables,) (VectorAffineFunction,)
5353
include("geomeanbridge.jl")

src/Bridges/bridge.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,25 @@ MOI.supportsconstraint(::Type{<:AbstractBridge}, ::Type{<:MOI.AbstractFunction},
4343
Return a list of the types of constraints that bridges of type `BT` add for bridging an `F`-in-`S` constraints.
4444
"""
4545
function addedconstrainttypes end
46+
47+
"""
48+
concrete_bridge_type(BT::Type{<:AbstractBridge},
49+
F::Type{<:MOI.AbstractFunction},
50+
S::Type{<:MOI.AbstractSet})::DataType
51+
52+
Return the concrete type of the bridge supporting `F`-in-`S` constraints. This
53+
function can only be called if `MOI.supportsconstraint(BT, F, S)` is `true`.
54+
55+
## Examples
56+
57+
The following returns `SplitIntervalBridge{Float64, MOI.SingleVariable}`:
58+
```julia
59+
concrete_bridge_type(SplitIntervalBridge{Float64}, MOI.SingleVariable,
60+
MOI.Interval{Float64})
61+
```
62+
"""
63+
function concrete_bridge_type(bridge_type::DataType,
64+
::Type{<:MOI.AbstractFunction},
65+
::Type{<:MOI.AbstractSet})
66+
return bridge_type
67+
end

src/Bridges/bridgeoptimizer.jl

Lines changed: 225 additions & 99 deletions
Large diffs are not rendered by default.

src/Bridges/intervalbridge.jl

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,25 @@
33
44
The `SplitIntervalBridge` splits a constraint ``l ≤ ⟨a, x⟩ + α ≤ u`` into the constraints ``⟨a, x⟩ + α ≥ l`` and ``⟨a, x⟩ + α ≤ u``.
55
"""
6-
struct SplitIntervalBridge{T} <: AbstractBridge
7-
lower::CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}
8-
upper::CI{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}}
6+
struct SplitIntervalBridge{T, F<:MOI.AbstractScalarFunction} <: AbstractBridge
7+
lower::CI{F, MOI.GreaterThan{T}}
8+
upper::CI{F, MOI.LessThan{T}}
99
end
10-
function SplitIntervalBridge{T}(model, f::MOI.ScalarAffineFunction{T}, s::MOI.Interval{T}) where T
10+
function SplitIntervalBridge{T, F}(model, f::F, s::MOI.Interval{T}) where {T, F}
1111
lower = MOI.addconstraint!(model, f, MOI.GreaterThan(s.lower))
1212
upper = MOI.addconstraint!(model, f, MOI.LessThan(s.upper))
13-
SplitIntervalBridge(lower, upper)
13+
return SplitIntervalBridge(lower, upper)
1414
end
1515

16-
MOI.supportsconstraint(::Type{SplitIntervalBridge{T}}, ::Type{MOI.ScalarAffineFunction{T}}, ::Type{MOI.Interval{T}}) where T = true
17-
addedconstrainttypes(::Type{SplitIntervalBridge{T}}, ::Type{MOI.ScalarAffineFunction{T}}, ::Type{MOI.Interval{T}}) where T = [(MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), (MOI.ScalarAffineFunction{T}, MOI.LessThan{T})]
16+
MOI.supportsconstraint(::Type{SplitIntervalBridge{T}}, ::Type{<:MOI.AbstractScalarFunction}, ::Type{MOI.Interval{T}}) where T = true
17+
addedconstrainttypes(::Type{SplitIntervalBridge{T}}, F::Type{<:MOI.AbstractScalarFunction}, ::Type{MOI.Interval{T}}) where T = [(F, MOI.GreaterThan{T}), (F, MOI.LessThan{T})]
18+
concrete_bridge_type(::Type{<:SplitIntervalBridge}, F::Type{<:MOI.AbstractScalarFunction}, ::Type{MOI.Interval{T}}) where T = SplitIntervalBridge{T, F}
1819

1920
# Attributes, Bridge acting as an model
20-
MOI.get(b::SplitIntervalBridge{T}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}}) where T = 1
21-
MOI.get(b::SplitIntervalBridge{T}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where T = 1
22-
MOI.get(b::SplitIntervalBridge{T}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where {T} = [b.lower]
23-
MOI.get(b::SplitIntervalBridge{T}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}}) where {T} = [b.upper]
21+
MOI.get(b::SplitIntervalBridge{T, F}, ::MOI.NumberOfConstraints{F, MOI.LessThan{T}}) where {T, F} = 1
22+
MOI.get(b::SplitIntervalBridge{T, F}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F} = 1
23+
MOI.get(b::SplitIntervalBridge{T, F}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F} = [b.lower]
24+
MOI.get(b::SplitIntervalBridge{T, F}, ::MOI.ListOfConstraintIndices{F, MOI.LessThan{T}}) where {T, F} = [b.upper]
2425

2526
# Indices
2627
function MOI.delete!(model::MOI.ModelLike, c::SplitIntervalBridge)
@@ -29,25 +30,23 @@ function MOI.delete!(model::MOI.ModelLike, c::SplitIntervalBridge)
2930
end
3031

3132
# Attributes, Bridge acting as a constraint
32-
function MOI.canget(model::MOI.ModelLike, a::MOI.ConstraintPrimal, ::Type{SplitIntervalBridge{T}}) where T
33-
MOI.canget(model, a, CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}})
33+
function MOI.canget(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, ::Type{SplitIntervalBridge{T, F}}) where {T, F}
34+
return MOI.canget(model, attr, CI{F, MOI.GreaterThan{T}})
3435
end
35-
function MOI.get(model::MOI.ModelLike, a::MOI.ConstraintPrimal, c::SplitIntervalBridge)
36+
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, c::SplitIntervalBridge)
3637
# lower and upper should give the same value
37-
MOI.get(model, MOI.ConstraintPrimal(), c.lower)
38+
return MOI.get(model, attr, c.lower)
3839
end
39-
function MOI.canget(model::MOI.ModelLike, a::MOI.ConstraintDual, ::Type{SplitIntervalBridge{T}}) where T
40-
MOI.canget(model, a, CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) &&
41-
MOI.canget(model, a, CI{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}})
40+
function MOI.canget(model::MOI.ModelLike, attr::MOI.ConstraintDual, ::Type{SplitIntervalBridge{T, F}}) where {T, F}
41+
return MOI.canget(model, attr, CI{F, MOI.GreaterThan{T}}) &&
42+
MOI.canget(model, attr, CI{F, MOI.LessThan{T}})
4243
end
4344
function MOI.get(model::MOI.ModelLike, a::MOI.ConstraintDual, c::SplitIntervalBridge)
44-
lowd = MOI.get(model, MOI.ConstraintDual(), c.lower) # Should be nonnegative
45-
uppd = MOI.get(model, MOI.ConstraintDual(), c.upper) # Should be nonpositive
46-
if lowd > -uppd
47-
lowd
48-
else
49-
uppd
50-
end
45+
# Should be nonnegative
46+
lower_dual = MOI.get(model, MOI.ConstraintDual(), c.lower)
47+
# Should be nonpositive
48+
upper_dual = MOI.get(model, MOI.ConstraintDual(), c.upper)
49+
return lower_dual > -upper_dual ? lower_dual : upper_dual
5150
end
5251

5352
# Constraints
@@ -57,7 +56,8 @@ function MOI.modify!(model::MOI.ModelLike, c::SplitIntervalBridge, change::MOI.A
5756
end
5857

5958
MOI.supports(model::MOI.ModelLike, ::MOI.ConstraintFunction, ::Type{<:SplitIntervalBridge}) = true
60-
function MOI.set!(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::SplitIntervalBridge, func::MOI.ScalarAffineFunction)
59+
function MOI.set!(model::MOI.ModelLike, ::MOI.ConstraintFunction,
60+
c::SplitIntervalBridge{T, F}, func::F) where {T, F}
6161
MOI.set!(model, MOI.ConstraintFunction(), c.lower, func)
6262
MOI.set!(model, MOI.ConstraintFunction(), c.upper, func)
6363
end

src/Bridges/lazybridgeoptimizer.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ struct LazyBridgeOptimizer{OT<:MOI.ModelLike, MT<:MOI.ModelLike} <: AbstractBrid
1414
model::OT # Internal model
1515
bridged::MT # Model containing bridged constraints
1616
bridges::Dict{CI, AbstractBridge} # Constraint Index of bridged constraint in bridged -> Bridge
17-
bridgetypes::Vector{DataType} # List of types of available bridges
17+
bridgetypes::Vector{Any} # List of types of available bridges
1818
dist::Dict{Tuple{DataType, DataType}, Int} # (F, S) -> Number of bridges that need to be used for an `F`-in-`S` constraint
1919
best::Dict{Tuple{DataType, DataType}, DataType} # (F, S) -> Bridge to be used for an `F`-in-`S` constraint
2020
end
2121
function LazyBridgeOptimizer(model::MOI.ModelLike, bridged::MOI.ModelLike)
2222
LazyBridgeOptimizer{typeof(model),
2323
typeof(bridged)}(model, bridged,
2424
Dict{CI, AbstractBridge}(),
25-
DataType[],
25+
Any[],
2626
Dict{Tuple{DataType, DataType}, Int}(),
2727
Dict{Tuple{DataType, DataType}, DataType}())
2828
end
@@ -49,7 +49,7 @@ function update_dist!(b::LazyBridgeOptimizer, constraints)
4949
# Is it better that what can currently be done ?
5050
if dist < _dist(b, F, S)
5151
b.dist[(F, S)] = dist
52-
b.best[(F, S)] = BT
52+
b.best[(F, S)] = concrete_bridge_type(BT, F, S)
5353
changed = true
5454
end
5555
end
@@ -103,7 +103,7 @@ function supportsbridgingconstraint(b::LazyBridgeOptimizer, F::Type{<:MOI.Abstra
103103
update_constraint!(b, F, S)
104104
(F, S) in keys(b.best)
105105
end
106-
function bridgetype(b::LazyBridgeOptimizer{BT}, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}) where BT
106+
function bridge_type(b::LazyBridgeOptimizer{BT}, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}) where BT
107107
update_constraint!(b, F, S)
108108
b.best[(F, S)]
109109
end

src/Bridges/singlebridgeoptimizer.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function SingleBridgeOptimizer{BT, MT}(model::OT) where {BT, MT, OT <: MOI.Model
1414
end
1515

1616
isbridged(b::SingleBridgeOptimizer, ::Type{<:MOI.AbstractFunction}, ::Type{<:MOI.AbstractSet}) = false
17-
bridgetype(b::SingleBridgeOptimizer{BT}, ::Type{<:MOI.AbstractFunction}, ::Type{<:MOI.AbstractSet}) where BT = BT
17+
bridge_type(b::SingleBridgeOptimizer{BT}, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}) where BT = BT
1818

1919
# :((Zeros, SecondOrderCone)) -> (:(MOI.Zeros), :(MOI.SecondOrderCone))
2020
_tuple_prefix_moi(t) = MOIU._moi.(t.args)

src/Bridges/soctopsdbridge.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,10 @@ struct RSOCtoPSDCBridge{T} <: AbstractBridge
116116
dim::Int
117117
cr::CI{MOI.VectorAffineFunction{T}, MOI.PositiveSemidefiniteConeTriangle}
118118
end
119+
119120
MOI.supportsconstraint(::Type{RSOCtoPSDCBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.RotatedSecondOrderCone}) where T = true
120121
addedconstrainttypes(::Type{RSOCtoPSDCBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.RotatedSecondOrderCone}) where T = [(MOI.VectorAffineFunction{T}, MOI.PositiveSemidefiniteConeTriangle)]
122+
121123
function RSOCtoPSDCBridge{T}(instance, f, s::MOI.RotatedSecondOrderCone) where T
122124
d = MOI.dimension(s)-1
123125
cr = MOI.addconstraint!(instance, _RSOCtoPSDCaff(f, T), MOI.PositiveSemidefiniteConeTriangle(d))

test/bridge.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Model not supporting Interval
2-
MOIU.@model SimpleModel () (EqualTo, GreaterThan, LessThan) (Zeros, Nonnegatives, Nonpositives, SecondOrderCone, RotatedSecondOrderCone, GeometricMeanCone, PositiveSemidefiniteConeTriangle, ExponentialCone) () (SingleVariable,) (ScalarAffineFunction,) (VectorOfVariables,) (VectorAffineFunction,)
2+
MOIU.@model SimpleModel () (EqualTo, GreaterThan, LessThan) (Zeros, Nonnegatives, Nonpositives, SecondOrderCone, RotatedSecondOrderCone, GeometricMeanCone, PositiveSemidefiniteConeTriangle, ExponentialCone) () (SingleVariable,) (ScalarAffineFunction, ScalarQuadraticFunction) (VectorOfVariables,) (VectorAffineFunction,)
33

44
function test_noc(bridgedmock, F, S, n)
55
@test MOI.canget(bridgedmock, MOI.NumberOfConstraints{F, S}())
@@ -159,6 +159,14 @@ end
159159
config = MOIT.TestConfig()
160160

161161
@testset "Interval" begin
162+
bridgedmock = MOIB.SplitInterval{Float64}(mock)
163+
MOIT.basic_constraint_tests(bridgedmock, config,
164+
include=[(MOI.SingleVariable,
165+
MOI.Interval{Float64}),
166+
(MOI.ScalarAffineFunction{Float64},
167+
MOI.Interval{Float64}),
168+
(MOI.ScalarQuadraticFunction{Float64},
169+
MOI.Interval{Float64})])
162170
MOIU.set_mock_optimize!(mock,
163171
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [5.0, 5.0],
164172
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [0],
@@ -168,7 +176,6 @@ end
168176
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [0]),
169177
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0]),
170178
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [6.0, 6.0]))
171-
bridgedmock = MOIB.SplitInterval{Float64}(mock)
172179
MOIT.linear10test(bridgedmock, config)
173180
ci = first(MOI.get(bridgedmock, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}}()))
174181
newf = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, -1.0], MOI.get(bridgedmock, MOI.ListOfVariableIndices())), 0.0)

0 commit comments

Comments
 (0)