Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

indicator to SOS bridge #877

Merged
merged 40 commits into from
Nov 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7506209
indicator to SOS bridge
matbesancon Sep 11, 2019
bf829d9
fix type signatures
matbesancon Sep 11, 2019
f4a32ef
register bridge
matbesancon Sep 11, 2019
11f5032
added tests
matbesancon Sep 11, 2019
b2be67d
ignore benchmarks
matbesancon Sep 11, 2019
62ae22b
remove equalto constraint
matbesancon Sep 16, 2019
94665e7
remove ambiguity
matbesancon Sep 16, 2019
23fd06b
file reformatting
matbesancon Sep 24, 2019
3e54c05
split files
matbesancon Sep 24, 2019
32e2493
Merge branch 'master' into indicator-sos
matbesancon Oct 30, 2019
fc3102d
revisions
matbesancon Oct 30, 2019
4fd52d6
remove redundant constraint from bridge
matbesancon Oct 30, 2019
c5b0b2f
add single bridge
matbesancon Oct 30, 2019
2946115
convert to VariableIndex
matbesancon Oct 30, 2019
3a6a676
add intermediate expression
matbesancon Nov 4, 2019
32d7baa
replace VAF with abstract vector function
matbesancon Nov 5, 2019
cb78e29
fix VAF
matbesancon Nov 5, 2019
de49ca4
Merge branch 'master' into indicator-sos
matbesancon Nov 5, 2019
89ff760
Merge branch 'master' into indicator-sos
matbesancon Nov 7, 2019
c753d2f
added get for set and func
matbesancon Nov 12, 2019
91a4c0a
remove log
matbesancon Nov 12, 2019
6813017
Merge branch 'master' into indicator-sos
matbesancon Nov 12, 2019
2cf8729
added delete
matbesancon Nov 12, 2019
8995b14
test model equals
matbesancon Nov 14, 2019
08fc80f
does not need adding binary
matbesancon Nov 14, 2019
e35ffd1
failing delete test
matbesancon Nov 14, 2019
7a8cd2f
add get for bridge
matbesancon Nov 14, 2019
f7c2e85
add primal start
matbesancon Nov 14, 2019
a01b091
add supports
matbesancon Nov 14, 2019
2fb4b59
added get tests
matbesancon Nov 15, 2019
3603585
more tests
matbesancon Nov 18, 2019
007e23e
fix test types
matbesancon Nov 19, 2019
970d801
added test for mock bridged
matbesancon Nov 20, 2019
7938dd5
use attr
matbesancon Nov 21, 2019
4d4a8f0
replace attr with N
matbesancon Nov 21, 2019
9a7aaca
variable attr propagation
matbesancon Nov 22, 2019
dbe41e2
remove ConstraintPrimal
matbesancon Nov 25, 2019
0c8ad66
reset test VariablePrimal
matbesancon Nov 25, 2019
ec89295
replace rebuilt attribute with attr to propage result number
matbesancon Nov 26, 2019
5d3ab8c
no primal start N
matbesancon Nov 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
docs/build/
docs/site/
Manifest.toml
test/Benchmarks/*.json
9 changes: 6 additions & 3 deletions src/Bridges/Constraint/Constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,17 @@ const RootDet{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RootDetBridge{T}, OT
include("soc_to_psd.jl")
const SOCtoPSD{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoPSDBridge{T}, OT}
const RSOCtoPSD{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoPSDBridge{T}, OT}
include("indicator.jl")
include("indicator_activate_on_zero.jl")
include("indicator_sos.jl")
matbesancon marked this conversation as resolved.
Show resolved Hide resolved
const IndicatortoSOS1{T, BC <: MOI.AbstractScalarSet, MaybeBC} = SingleBridgeOptimizer{IndicatorSOS1Bridge{T, BC, MaybeBC}}

"""
add_all_bridges(bridged_model, T::Type)
add_all_bridges(bridged_model, ::Type{T})

Add all bridges defined in the `Bridges.Constraint` submodule to
`bridged_model`. The coefficient type used is `T`.
"""
function add_all_bridges(bridged_model, T::Type)
function add_all_bridges(bridged_model, ::Type{T}) where {T}
blegat marked this conversation as resolved.
Show resolved Hide resolved
MOIB.add_bridge(bridged_model, GreaterToLessBridge{T})
MOIB.add_bridge(bridged_model, LessToGreaterBridge{T})
MOIB.add_bridge(bridged_model, NonnegToNonposBridge{T})
Expand All @@ -87,6 +89,7 @@ function add_all_bridges(bridged_model, T::Type)
# then to `PSD` produces a smaller SDP constraint.
MOIB.add_bridge(bridged_model, RSOCtoPSDBridge{T})
MOIB.add_bridge(bridged_model, IndicatorActiveOnFalseBridge{T})
MOIB.add_bridge(bridged_model, IndicatorSOS1Bridge{T})
return
end

Expand Down
183 changes: 183 additions & 0 deletions src/Bridges/Constraint/indicator_sos.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
"""
IndicatorSOS1Bridge{T, BC <: MOI.AbstractScalarSet}

The `IndicatorSOS1Bridge` replaces an indicator constraint of the following form:
``z \\in \\mathbb{B}, z == 1 \\implies f(x) \\leq b`` with a SOS1 constraint:
``z \\in \\mathbb{B}, w \\leq 0, f(x) + w \\leq b, SOS1(w, z)``.
`GreaterThan` constraints are handled in a symmetric way:
``z \\in \\mathbb{B}, z == 1 \\implies f(x) \\geq b`` is reformulated as:
``z \\in \\mathbb{B}, w \\geq 0, f(x) + w \\geq b, SOS1(w, z)``.
Other scalar sets are handled without a bound constraint:
``z \\in \\mathbb{B}, z == 1 \\implies f(x) == b`` is reformulated as:
``z \\in \\mathbb{B}, w \\text{ free}, f(x) + w == b, SOS1(w, z)``.

If `BC !<: Union{LessThan, GreaterThan}`, `bound_constraint_index` is `nothing`.
"""
struct IndicatorSOS1Bridge{T, BC <: MOI.AbstractScalarSet, MaybeBC <: Union{MOI.ConstraintIndex{MOI.SingleVariable, BC}, Nothing}} <: AbstractBridge
w_variable_index::MOI.VariableIndex
z_variable_index::MOI.VariableIndex
affine_func::MOI.ScalarAffineFunction{T}
bound_constraint_index::MaybeBC
sos_constraint_index::MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.SOS1{T}}
linear_constraint_index::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T}, BC}
end

function bridge_constraint(::Type{IndicatorSOS1Bridge{T,BC,MaybeBC}}, model::MOI.ModelLike, f::MOI.VectorAffineFunction{T}, s::MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE, BC}) where {T <: Real, BC, MaybeBC}
f_scalars = MOIU.eachscalar(f)
(w, bound_constraint) = _add_bound_constraint!(model, BC)
z = convert(MOI.SingleVariable, f_scalars[1]).variable
sos_vector = MOI.VectorOfVariables([w, z])
sos_constraint = MOI.add_constraint(model, sos_vector, MOI.SOS1{T}([0.4, 0.6]))
affine_func = f_scalars[2]
affine_expr = MOIU.operate(+, T, affine_func, MOI.SingleVariable(w))
linear_constraint = MOI.add_constraint(model, affine_expr, s.set)
return IndicatorSOS1Bridge{T,BC,MaybeBC}(w, z, affine_func, bound_constraint, sos_constraint, linear_constraint)
end

function _add_bound_constraint!(model::MOI.ModelLike, ::Type{BC}) where {T <: Real, BC <: Union{MOI.LessThan{T}, MOI.GreaterThan{T}}}
return MOI.add_constrained_variable(model, BC(zero(T)))
end

function _add_bound_constraint!(model::MOI.ModelLike, ::Type{<:MOI.AbstractScalarSet})
return (MOI.add_variable(model), nothing)
end

function MOI.supports_constraint(::Type{<:IndicatorSOS1Bridge},
::Type{<:MOI.AbstractVectorFunction},
::Type{<:MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE, <:MOI.AbstractScalarSet}})
return true
end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
b::IndicatorSOS1Bridge)
return MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE}(
MOI.get(model, attr, b.linear_constraint_index),
)
end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
b::IndicatorSOS1Bridge{T}) where {T}
z = b.z_variable_index
terms = [MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(one(T), z))]
for affine_term in b.affine_func.terms
push!(terms, MOI.VectorAffineTerm(2, affine_term))
end
return MOI.VectorAffineFunction(
terms, [zero(T), b.affine_func.constant]
)
end

function MOI.delete(model::MOI.ModelLike, bridge::IndicatorSOS1Bridge)
if bridge.bound_constraint_index !== nothing
MOI.delete(model, bridge.bound_constraint_index)
end
MOI.delete(model, bridge.sos_constraint_index)
MOI.delete(model, bridge.linear_constraint_index)
MOI.delete(model, bridge.w_variable_index)
return
end

function MOIB.added_constrained_variable_types(::Type{<:IndicatorSOS1Bridge{T, BC}}) where {T, BC <: Union{MOI.LessThan{T}, MOI.GreaterThan{T}}}
return [(BC,)]
end

function MOIB.added_constrained_variable_types(::Type{<:IndicatorSOS1Bridge{T, BC}}) where {T, BC}
return []
end

function MOIB.added_constraint_types(::Type{<:IndicatorSOS1Bridge{T, BC}}) where {T, BC <: Union{MOI.LessThan{T}, MOI.GreaterThan{T}}}
return [(MOI.VectorOfVariables, MOI.SOS1{T}),
(MOI.ScalarAffineFunction{T}, BC),
]
end

function MOIB.added_constraint_types(::Type{<:IndicatorSOS1Bridge{T, S}}) where {T, S <: MOI.AbstractScalarSet}
return [(MOI.VectorOfVariables, MOI.SOS1{T}),
(MOI.ScalarAffineFunction{T}, S),
]
end

function concrete_bridge_type(::Type{<:IndicatorSOS1Bridge{T}},
::Type{<:MOI.AbstractVectorFunction},
::Type{MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE, S}}) where {T, S<:Union{MOI.LessThan, MOI.GreaterThan}}
return IndicatorSOS1Bridge{T, S, MOI.ConstraintIndex{MOI.SingleVariable, S}}
end

function concrete_bridge_type(::Type{<:IndicatorSOS1Bridge{T}},
::Type{<:MOI.AbstractVectorFunction},
::Type{MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE, S}}) where {T, S <: MOI.AbstractScalarSet}
return IndicatorSOS1Bridge{T, S, Nothing}
end

# Attributes, Bridge acting as a model

function MOI.get(::IndicatorSOS1Bridge, ::MOI.NumberOfVariables)
return 1
end

function MOI.get(b::IndicatorSOS1Bridge, ::MOI.ListOfVariableIndices)
return [b.w_variable_index]
end

function MOI.get(::IndicatorSOS1Bridge{T, BC, Nothing}, ::MOI.NumberOfConstraints{MOI.SingleVariable, BC}) where {T, BC}
return 0
end

function MOI.get(::IndicatorSOS1Bridge{T, BC, CI}, ::MOI.NumberOfConstraints{MOI.SingleVariable, BC}) where {T, BC, CI <: MOI.ConstraintIndex{MOI.SingleVariable, BC}}
return 1
end

function MOI.get(::IndicatorSOS1Bridge, ::MOI.NumberOfConstraints{MOI.VectorOfVariables, <:MOI.SOS1})
return 1
end

function MOI.get(::IndicatorSOS1Bridge{T, BC}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, BC}) where {T, BC, CI <: MOI.ConstraintIndex{MOI.SingleVariable, BC}}
return 1
end

function MOI.get(b::IndicatorSOS1Bridge{T, BC, CI}, ::MOI.ListOfConstraintIndices{MOI.SingleVariable, BC}) where {T, BC, CI <: MOI.ConstraintIndex}
return [b.bound_constraint_index]
end

function MOI.get(::IndicatorSOS1Bridge{T, BC, Nothing}, ::MOI.ListOfConstraintIndices{MOI.SingleVariable, BC}) where {T, BC}
return MOI.ConstraintIndex{MOI.SingleVariable, BC}[]
end

function MOI.get(b::IndicatorSOS1Bridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables, <:MOI.SOS1}) where {T}
return [b.sos_constraint_index]
end

function MOI.get(b::IndicatorSOS1Bridge{T, BC}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, BC}) where {T, BC}
return [b.linear_constraint_index]
end

function MOI.supports(
::MOI.ModelLike,
::MOI.ConstraintPrimalStart,
::Type{<:IndicatorSOS1Bridge})
return true
end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, bridge::IndicatorSOS1Bridge)
zvalue = MOI.get(model, MOI.VariablePrimal(attr.N), bridge.z_variable_index)
wvalue = MOI.get(model, MOI.VariablePrimal(attr.N), bridge.w_variable_index)
lin_primal_start = MOI.get(model, attr, bridge.linear_constraint_index)
return [zvalue, lin_primal_start - wvalue]
end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, bridge::IndicatorSOS1Bridge)
zstart = MOI.get(model, MOI.VariablePrimalStart(), bridge.z_variable_index)
wstart = MOI.get(model, MOI.VariablePrimalStart(), bridge.w_variable_index)
lin_primal_start = MOI.get(model, attr, bridge.linear_constraint_index)
return [zstart, lin_primal_start - wstart]
end

function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart,
bridge::IndicatorSOS1Bridge{T}, value) where {T}
zvalue = value[1]
lin_start = value[2]
MOI.set(model, MOI.VariablePrimalStart(), bridge.z_variable_index, zvalue)
wstart = MOI.get(model, MOI.VariablePrimalStart(), bridge.w_variable_index)
wstart = wstart === nothing ? zero(T) : wstart
MOI.set(model, attr, bridge.linear_constraint_index, lin_start + wstart)
end
3 changes: 2 additions & 1 deletion src/Test/UnitTests/basic_constraint_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ const BasicConstraintTests = Dict(
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => ( dummy_vector_affine, 3, MOI.RotatedSecondOrderCone(3) ),
(MOI.VectorAffineFunction{Float64}, MOI.GeometricMeanCone) => ( dummy_vector_affine, 3, MOI.GeometricMeanCone(3) ),
(MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeSquare) => ( dummy_vector_affine, 9, MOI.GeometricMeanCone(3) ),

(MOI.VectorAffineFunction{Float64}, MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE, MOI.LessThan{Float64}}) => ( dummy_vector_affine, 2, MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE}(MOI.LessThan(3.0))),
(MOI.VectorAffineFunction{Float64}, MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE, MOI.GreaterThan{Float64}}) => ( dummy_vector_affine, 2, MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE}(MOI.GreaterThan(3.0))),
(MOI.VectorQuadraticFunction{Float64}, MOI.Zeros) => ( dummy_vector_quadratic, 2, MOI.Zeros(2) ),
(MOI.VectorQuadraticFunction{Float64}, MOI.Nonpositives) => ( dummy_vector_quadratic, 2, MOI.Nonpositives(2) ),
(MOI.VectorQuadraticFunction{Float64}, MOI.Nonnegatives) => ( dummy_vector_quadratic, 2, MOI.Nonnegatives(2) ),
Expand Down
4 changes: 2 additions & 2 deletions src/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ If `model` does not support free variables, it should not implement
this method and return `false`. This allows free variables to be bridged as the
sum of a nonnegative and a nonpositive variables.
""" # Implemented as only one method to avoid ambiguity
function supports_constraint(model::ModelLike, F::Type{<:AbstractFunction},
function supports_constraint(::ModelLike, F::Type{<:AbstractFunction},
S::Type{<:AbstractSet})
return F == VectorOfVariables && S == Reals
return F === VectorOfVariables && S === Reals
end

"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const MOIB = MathOptInterface.Bridges

include("../utilities.jl")

@testset "Indicator" begin
@testset "Indicator activated on 0" begin
# linear problem with indicator constraint
# similar to indicator1_test with reversed z1
# max 2x1 + 3x2
Expand Down Expand Up @@ -41,7 +41,7 @@ include("../utilities.jl")
BT2 = MOIB.Constraint.concrete_bridge_type(MOIB.Constraint.IndicatorActiveOnFalseBridge, typeof(f1), typeof(iset1))
bridge = MOIB.Constraint.bridge_constraint(BT, model, f1, iset1)

@test BT == BT2
@test BT === BT2
@test bridge isa BT

z1comp = bridge.variable_index
Expand Down
Loading