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 29 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
184 changes: 184 additions & 0 deletions src/Bridges/Constraint/indicator_sos.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
"""
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)
if attr.N == 1
odow marked this conversation as resolved.
Show resolved Hide resolved
return MOI.get(model, MOI.VariablePrimal(), bridge.z_variable_index)
end
wv = MOI.get(model, MOI.VariablePrimal(), bridge.w_variable_index)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be MOI.VariablePrimal(attr.N)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how come? w is a scalar variable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the Nth result, you have VariablePrimal(N), ConstraintPrimal(N) and ConstraintDual(N). The value of ConstraintPrimal(N) is the value of the function when the variables have values VariablePrimal(N)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, that's not really intuitive, I thought ConstraintPrimal(N) would be the primal of the N-th function in the vector function. I'll update that

lin_primal = MOI.get(model, MOI.ConstraintPrimal(), b.linear_constraint_index)
matbesancon marked this conversation as resolved.
Show resolved Hide resolved
return lin_primal - wv
end

function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimalStart, bridge::IndicatorSOS1Bridge)
blegat marked this conversation as resolved.
Show resolved Hide resolved
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, MOI.ConstraintPrimalStart(), b.linear_constraint_index)
return [zstart, lin_primal_start - wstart]
end

function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintPrimalStart,
bridge::IndicatorSOS1Bridge, value)
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)
MOI.set(model, MOI.ConstraintPrimalStart(), 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