Skip to content

Commit 6ac7ebd

Browse files
committed
Add scalar variable bridge
1 parent 869f811 commit 6ac7ebd

20 files changed

+447
-220
lines changed

docs/src/apireference.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,8 @@ A bridge should implement the following functions to be usable by a bridge optim
451451
supports_constraint(::Type{<:Bridges.Constraint.AbstractBridge}, ::Type{<:AbstractFunction}, ::Type{<:AbstractSet})
452452
Bridges.Constraint.concrete_bridge_type
453453
Bridges.Constraint.bridge_constraint
454-
Bridges.Constraint.added_constraint_types
454+
Bridges.added_constrained_variable_types
455+
Bridges.added_constraint_types
455456
```
456457

457458
When querying the [`NumberOfVariables`](@ref), [`NumberOfConstraints`](@ref)

src/Bridges/Constraint/scalarize.jl

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
const VectorLinearSet = Union{MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives}
2-
3-
scalar_set_type(::Type{<:MOI.Zeros}, T::Type) = MOI.EqualTo{T}
4-
scalar_set_type(::Type{<:MOI.Nonpositives}, T::Type) = MOI.LessThan{T}
5-
scalar_set_type(::Type{<:MOI.Nonnegatives}, T::Type) = MOI.GreaterThan{T}
6-
71
"""
82
ScalarizeBridge{T, F, S}
93
10-
Transforms a constraint `AbstractVectorFunction`-in-`vector_set(S)` where
4+
Transforms a constraint `AbstractVectorFunction`-in-`vector_set_type(S)` where
115
`S <: LPCone{T}` to `F`-in-`S`.
126
"""
137
mutable struct ScalarizeBridge{T, F, S} <: AbstractBridge
@@ -17,7 +11,7 @@ end
1711
function bridge_constraint(::Type{ScalarizeBridge{T, F, S}},
1812
model::MOI.ModelLike,
1913
f::MOI.AbstractVectorFunction,
20-
set::VectorLinearSet) where {T, F, S}
14+
set::MOIU.VectorLinearSet) where {T, F, S}
2115
dimension = MOI.output_dimension(f)
2216
constants = MOI.constant(f, T)
2317
new_f = MOIU.scalarize(f, true)
@@ -30,7 +24,7 @@ end
3024

3125
function MOI.supports_constraint(::Type{ScalarizeBridge{T}},
3226
::Type{<:MOI.AbstractVectorFunction},
33-
::Type{<:VectorLinearSet}) where T
27+
::Type{<:MOIU.VectorLinearSet}) where T
3428
return true
3529
end
3630
MOIB.added_constrained_variable_types(::Type{<:ScalarizeBridge}) = Tuple{DataType}[]
@@ -39,8 +33,8 @@ function MOIB.added_constraint_types(::Type{ScalarizeBridge{T, F, S}}) where {T,
3933
end
4034
function concrete_bridge_type(::Type{<:ScalarizeBridge{T}},
4135
F::Type{<:MOI.AbstractVectorFunction},
42-
S::Type{<:VectorLinearSet}) where T
43-
return ScalarizeBridge{T, MOIU.scalar_type(F), scalar_set_type(S, T)}
36+
S::Type{<:MOIU.VectorLinearSet}) where T
37+
return ScalarizeBridge{T, MOIU.scalar_type(F), MOIU.scalar_set_type(S, T)}
4438
end
4539

4640
# Attributes, Bridge acting as a model
@@ -70,7 +64,7 @@ function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
7064
end
7165
function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet,
7266
bridge::ScalarizeBridge{T, F, S}) where {T, F, S}
73-
return vector_set_type(S)(length(bridge.constants))
67+
return MOIU.vector_set_type(S)(length(bridge.constants))
7468
end
7569

7670
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal,

src/Bridges/Constraint/single_bridge_optimizer.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function Base.iterate(map::Map, state)
128128
if state[1] == 1
129129
return _iterate1(map, state[2])
130130
else
131-
return _iterate2(map, iterate(map.single_variable_constraint, state[2]))
131+
return _iterate2(map, iterate(map.single_variable_constraints, state[2]))
132132
end
133133
end
134134

@@ -174,7 +174,7 @@ function MOIB.is_bridged(b::SingleBridgeOptimizer, F::Type{<:MOI.AbstractFunctio
174174
S::Type{<:MOI.AbstractSet})
175175
return MOIB.supports_bridging_constraint(b, F, S)
176176
end
177-
function MOIB.is_bridged(b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractVectorSet})
177+
function MOIB.is_bridged(b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractSet})
178178
return false
179179
end
180180
function MOIB.bridge_type(::SingleBridgeOptimizer{BT},

src/Bridges/Constraint/vectorize.jl

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
const ScalarLinearSet{T} = Union{MOI.EqualTo{T}, MOI.LessThan{T}, MOI.GreaterThan{T}}
2-
3-
vector_set(::MOI.EqualTo) = MOI.Zeros(1)
4-
vector_set(::MOI.LessThan) = MOI.Nonpositives(1)
5-
vector_set(::MOI.GreaterThan) = MOI.Nonnegatives(1)
6-
7-
vector_set_type(::Type{<:MOI.EqualTo}) = MOI.Zeros
8-
vector_set_type(::Type{<:MOI.LessThan}) = MOI.Nonpositives
9-
vector_set_type(::Type{<:MOI.GreaterThan}) = MOI.Nonnegatives
10-
111
"""
122
VectorizeBridge{T, F, S, G}
133
@@ -20,7 +10,7 @@ mutable struct VectorizeBridge{T, F, S, G} <: AbstractBridge
2010
end
2111
function bridge_constraint(::Type{VectorizeBridge{T, F, S, G}},
2212
model::MOI.ModelLike, g::G,
23-
set::MOI.AbstractScalarSet) where {T, F, S, G}
13+
set::MOIU.ScalarLinearSet{T}) where {T, F, S, G}
2414
set_constant = MOI.constant(set)
2515
h = MOIU.operate(-, T, g, set_constant)
2616
if -set_constant != MOI.constant(h)[1]
@@ -30,13 +20,13 @@ function bridge_constraint(::Type{VectorizeBridge{T, F, S, G}},
3020
typeof(set)}(constant))
3121
end
3222
f = MOIU.operate(vcat, T, h)
33-
vector_constraint = MOI.add_constraint(model, f, vector_set(set))
23+
vector_constraint = MOI.add_constraint(model, f, S(1))
3424
return VectorizeBridge{T, F, S, G}(vector_constraint, set_constant)
3525
end
3626

3727
function MOI.supports_constraint(::Type{VectorizeBridge{T}},
3828
::Type{<:MOI.AbstractScalarFunction},
39-
::Type{<:ScalarLinearSet{T}}) where T
29+
::Type{<:MOIU.ScalarLinearSet{T}}) where T
4030
return true
4131
end
4232
MOIB.added_constrained_variable_types(::Type{<:VectorizeBridge}) = Tuple{DataType}[]
@@ -45,10 +35,10 @@ function MOIB.added_constraint_types(::Type{<:VectorizeBridge{T, F, S}}) where {
4535
end
4636
function concrete_bridge_type(::Type{<:VectorizeBridge{T}},
4737
G::Type{<:MOI.AbstractScalarFunction},
48-
S::Type{<:ScalarLinearSet{T}}) where T
38+
S::Type{<:MOIU.ScalarLinearSet{T}}) where T
4939
H = MOIU.promote_operation(-, T, G, T)
5040
F = MOIU.promote_operation(vcat, T, H)
51-
return VectorizeBridge{T, F, vector_set_type(S), G}
41+
return VectorizeBridge{T, F, MOIU.vector_set_type(S), G}
5242
end
5343

5444
# Attributes, Bridge acting as a model
@@ -74,8 +64,7 @@ function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal,
7464
@assert length(x) == 1
7565
y = x[1]
7666
status = MOI.get(model, MOI.PrimalStatus(attr.N))
77-
if status != MOI.INFEASIBILITY_CERTIFICATE &&
78-
status != MOI.NEARLY_INFEASIBILITY_CERTIFICATE
67+
if !MOIU.is_ray(status)
7968
# If it is an infeasibility certificate, it is a ray and satisfies the
8069
# homogenized problem, see https://github.com/JuliaOpt/MathOptInterface.jl/issues/433
8170
# Otherwise, we need to add the set constant since the ConstraintPrimal
@@ -98,7 +87,7 @@ function MOI.modify(model::MOI.ModelLike, bridge::VectorizeBridge,
9887
[(1, change.new_coefficient)]))
9988
end
10089
function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintSet,
101-
bridge::VectorizeBridge, new_set::ScalarLinearSet)
90+
bridge::VectorizeBridge, new_set::MOIU.ScalarLinearSet)
10291
bridge.set_constant = MOI.constant(new_set)
10392
MOI.modify(model, bridge.vector_constraint,
10493
MOI.VectorConstantChange([-bridge.set_constant]))
@@ -112,5 +101,5 @@ function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
112101
end
113102
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet,
114103
bridge::VectorizeBridge{T, F, S}) where {T, F, S}
115-
return scalar_set_type(S, T)(bridge.set_constant)
104+
return MOIU.scalar_set_type(S, T)(bridge.set_constant)
116105
end

src/Bridges/Variable/Variable.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ include("single_bridge_optimizer.jl")
1414
# Variable bridges
1515
include("flip_sign.jl")
1616
const NonposToNonneg{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NonposToNonnegBridge{T}, OT}
17+
include("vectorize.jl")
18+
const Vectorize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorizeBridge{T}, OT}
1719

1820
function add_all_bridges(bridged_model, T::Type)
1921
MOIB.add_bridge(bridged_model, NonposToNonnegBridge{T})
22+
MOIB.add_bridge(bridged_model, VectorizeBridge{T})
2023
return
2124
end
2225

src/Bridges/Variable/bridge.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ struct IndexInVector
44
value::Int
55
end
66

7+
function bridge_constrained_variable end
8+
79
"""
810
bridge_constrained_variables(BT::Type{<:AbstractBridge}, model::MOI.ModelLike,
911
set::MOI.AbstractVectorSet)
@@ -29,13 +31,13 @@ MOI.get(b::AbstractBridge, ::MOI.NumberOfConstraints) = 0
2931

3032
"""
3133
supports_constrained_variables(::Type{<:AbstractBridge},
32-
::Type{<:MOI.AbstractVectorSet})::Bool
34+
::Type{<:MOI.AbstractSet})::Bool
3335
3436
Return a `Bool` indicating whether the bridges of type `BT` support bridging
3537
constrained variables in `S`.
3638
"""
3739
function supports_constrained_variables(::Type{<:AbstractBridge},
38-
::Type{<:MOI.AbstractVectorSet})
40+
::Type{<:MOI.AbstractSet})
3941
return false
4042
end
4143

src/Bridges/Variable/flip_sign.jl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@ function MOIB.added_constraint_types(::Type{<:FlipSignBridge})
2121
return Tuple{DataType, DataType}[]
2222
end
2323

24-
# Attributes, Bridge acting as an model
25-
function MOI.get(bridge::FlipSignBridge{T, <:MOI.AbstractVectorSet},
26-
::MOI.NumberOfVariables) where T
24+
# Attributes, Bridge acting as a model
25+
function MOI.get(bridge::FlipSignBridge, ::MOI.NumberOfVariables)
2726
return length(bridge.flipped_variables)
2827
end
29-
function MOI.get(bridge::FlipSignBridge{T, <:MOI.AbstractVectorSet},
30-
::MOI.ListOfVariableIndices) where T
28+
function MOI.get(bridge::FlipSignBridge, ::MOI.ListOfVariableIndices)
3129
return bridge.flipped_variables
3230
end
3331
function MOI.get(::FlipSignBridge{T, S1, S2},
@@ -39,6 +37,7 @@ function MOI.get(::FlipSignBridge{T, S1, S2},
3937
return [bridge.flipped_constraint]
4038
end
4139

40+
# Attributes, Bridge acting as a constraint
4241

4342
function MOI.get(model::MOI.ModelLike,
4443
attr::Union{MOI.ConstraintPrimal, MOI.ConstraintDual},
@@ -55,6 +54,11 @@ function MOIB.bridged_function(bridge::FlipSignBridge{T}, i::IndexInVector) wher
5554
func = MOI.SingleVariable(bridge.flipped_variables[i.value])
5655
return MOIU.operate(-, T, func)
5756
end
57+
function unbridged_map(bridge::FlipSignBridge{T}, vi::MOI.VariableIndex,
58+
i::IndexInVector) where T
59+
func = MOIU.operate(-, T, MOI.SingleVariable(vi))
60+
return bridge.flipped_variables[i.value] => func
61+
end
5862

5963
"""
6064
NonposToNonnegBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <:

src/Bridges/Variable/single_bridge_optimizer.jl

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@ struct Map <: AbstractDict{MOI.VariableIndex, AbstractBridge}
77
# `i` -> `bridge`: `VariableIndex(-i)` was bridged by `bridge`.
88
bridges::Vector{Union{Nothing, AbstractBridge}}
99
sets::Vector{Union{Nothing, DataType}}
10+
unbridged_function::Dict{MOI.VariableIndex, MOI.AbstractScalarFunction}
1011
end
11-
Map() = Map(Int[], Union{Nothing, AbstractBridge}[], Union{Nothing, DataType}[])
12+
Map() = Map(Int[], Union{Nothing, AbstractBridge}[], Union{Nothing, DataType}[], Dict{MOI.VariableIndex, MOI.AbstractScalarFunction}())
1213
Base.isempty(map::Map) = all(bridge -> bridge === nothing, map.bridges)
1314
function Base.empty!(map::Map)
1415
empty!(map.index_in_vector_of_variables)
1516
empty!(map.bridges)
1617
empty!(map.sets)
18+
empty!(map.unbridged_function)
1719
return map
1820
end
1921
function bridge_index(map::Map, vi::MOI.VariableIndex)
20-
return -vi.value - map.index_in_vector_of_variables[-vi.value] + 1
22+
index = map.index_in_vector_of_variables[-vi.value]
23+
if iszero(index)
24+
return -vi.value
25+
else
26+
return -vi.value - index + 1
27+
end
2128
end
2229
function Base.haskey(map::Map, vi::MOI.VariableIndex)
2330
return -length(map.bridges) vi.value -1 &&
@@ -27,8 +34,8 @@ function Base.getindex(map::Map, vi::MOI.VariableIndex)
2734
return map.bridges[bridge_index(map, vi)]
2835
end
2936
function Base.delete!(map::Map, vi::MOI.VariableIndex)
30-
map.bridges[bridge_index(vi)] = nothing
31-
map.sets[bridge_index(vi)] = nothing
37+
map.bridges[bridge_index(map, vi)] = nothing
38+
map.sets[bridge_index(map, vi)] = nothing
3239
return map
3340
end
3441
function Base.keys(map::Map)
@@ -49,6 +56,16 @@ function index_in_vector_of_variables(map::Map, vi::MOI.VariableIndex)
4956
return IndexInVector(map.index_in_vector_of_variables[-vi.value])
5057
end
5158
has_bridges(map::Map) = !isempty(map.index_in_vector_of_variables)
59+
function add_key_for_bridge(map::Map, bridge::AbstractBridge,
60+
set::MOI.AbstractScalarSet)
61+
index = -(length(map.bridges) + 1)
62+
variable = MOI.VariableIndex(index)
63+
push!(map.index_in_vector_of_variables, 0)
64+
push!(map.bridges, bridge)
65+
push!(map.sets, typeof(set))
66+
push!(map.unbridged_function, unbridged_map(bridge, variable))
67+
return variable, MOI.ConstraintIndex{MOI.SingleVariable, typeof(set)}(index)
68+
end
5269
function add_keys_for_bridge(map::Map, bridge::AbstractBridge,
5370
set::MOI.AbstractVectorSet)
5471
if iszero(MOI.dimension(set))
@@ -64,6 +81,10 @@ function add_keys_for_bridge(map::Map, bridge::AbstractBridge,
6481
push!(map.bridges, nothing)
6582
push!(map.sets, nothing)
6683
end
84+
for i in 1:MOI.dimension(set)
85+
push!(map.unbridged_function, unbridged_map(bridge, variables[i],
86+
IndexInVector(i)))
87+
end
6788
index = first(variables).value
6889
return variables, MOI.ConstraintIndex{MOI.VectorOfVariables, typeof(set)}(index)
6990
end
@@ -78,6 +99,9 @@ function Base.iterate(map::Map, state=1)
7899
return MOI.VariableIndex(-state) => map.bridges[state], state + 1
79100
end
80101
end
102+
function unbridged_function(map::Map, vi::MOI.VariableIndex)
103+
return get(map.unbridged_function, vi, MOI.SingleVariable(vi))
104+
end
81105

82106
struct EmptyMap <: AbstractDict{MOI.VariableIndex, AbstractBridge} end
83107
Base.isempty(::EmptyMap) = true
@@ -109,19 +133,27 @@ function bridges(bridge::MOI.Bridges.AbstractBridgeOptimizer)
109133
end
110134
bridges(bridge::SingleBridgeOptimizer) = bridge.map
111135

136+
function MOIB.supports_bridging_constraint(
137+
b::SingleBridgeOptimizer{BT}, ::Type{MOI.SingleVariable},
138+
S::Type{<:MOI.AbstractScalarSet}) where BT
139+
return supports_constrained_variables(BT, S)
140+
end
112141
function MOIB.supports_bridging_constraint(
113142
b::SingleBridgeOptimizer{BT}, ::Type{MOI.VectorOfVariables},
114143
S::Type{<:MOI.AbstractVectorSet}) where BT
115144
return supports_constrained_variables(BT, S)
116145
end
146+
function MOIB.is_bridged(b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractScalarSet})
147+
return MOIB.supports_bridging_constraint(b, MOI.SingleVariable, S)
148+
end
149+
function MOIB.is_bridged(b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractVectorSet})
150+
return MOIB.supports_bridging_constraint(b, MOI.VectorOfVariables, S)
151+
end
117152
function MOIB.is_bridged(::SingleBridgeOptimizer,
118153
::Type{<:MOI.AbstractFunction},
119154
::Type{<:MOI.AbstractSet})
120155
return false
121156
end
122-
function MOIB.is_bridged(b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractVectorSet})
123-
return MOIB.supports_bridging_constraint(b, MOI.VectorOfVariables, S)
124-
end
125157
function MOIB.bridge_type(::SingleBridgeOptimizer{BT},
126158
::Type{<:MOI.AbstractSet}) where BT
127159
return BT

0 commit comments

Comments
 (0)