From e141fb9c28e7362df069e030d7e3c97778282b56 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sat, 4 Apr 2020 22:04:41 -0300 Subject: [PATCH 1/8] add semi bridges --- src/Bridges/Constraint/Constraint.jl | 3 + src/Bridges/Constraint/semi_to_binary.jl | 194 ++++++++++++++++++++++ test/Bridges/Constraint/semi_to_binary.jl | 105 ++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 src/Bridges/Constraint/semi_to_binary.jl create mode 100644 test/Bridges/Constraint/semi_to_binary.jl diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 770a6f2ecf..26ab889631 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -62,6 +62,8 @@ const RSOCtoPSD{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoPSDBridge{T} include("indicator_activate_on_zero.jl") include("indicator_sos.jl") const IndicatortoSOS1{T, BC <: MOI.AbstractScalarSet, MaybeBC} = SingleBridgeOptimizer{IndicatorSOS1Bridge{T, BC, MaybeBC}} +include("semi_to_binary.jl") +const SemiToBinary{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SemiToBinaryBridge{T}, OT} """ add_all_bridges(bridged_model, ::Type{T}) @@ -98,6 +100,7 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T} MOIB.add_bridge(bridged_model, RSOCtoPSDBridge{T}) MOIB.add_bridge(bridged_model, IndicatorActiveOnFalseBridge{T}) MOIB.add_bridge(bridged_model, IndicatorSOS1Bridge{T}) + MOIB.add_bridge(bridged_model, SemiToBinaryBridge{T}) return end diff --git a/src/Bridges/Constraint/semi_to_binary.jl b/src/Bridges/Constraint/semi_to_binary.jl new file mode 100644 index 0000000000..7435f333a6 --- /dev/null +++ b/src/Bridges/Constraint/semi_to_binary.jl @@ -0,0 +1,194 @@ +const SemiSets{T} = Union{MOI.Semicontinuous{T}, MOI.Semiinteger{T}} + +""" + SemiToBinaryBridge{T, S <: MOI.AbstractScalarSet} + +The `SemiToBinaryBridge` replaces an Semicontinuous constraint: +``x \\in Semicontinuous(LowerBound, UpperBound)`` +is replaced by: +``z \\in \\ZeroOne``, +``x \\leq z \\cdot UpperBound ``, +``x \\geq z \\cdot LowerBound ``. + +The `SemiToBinaryBridge` replaces an Semiinteger constraint: +``x \\in Semiinteger(LowerBound, UpperBound)`` +is replaced by: +``z \\in \\ZeroOne``, +``x \\in \\Integer``, +``x \\leq z \\cdot UpperBound ``, +``x \\geq z \\cdot LowerBound ``. +""" +struct SemiToBinaryBridge{T, S <: SemiSets{T}} <: AbstractBridge + semi_set::S + variable_index::MOI.VariableIndex + binary_variable_index::MOI.VariableIndex + binary_constraint_index::MOI.ConstraintIndex{MOI.SingleVariable, MOI.ZeroOne} + lower_bound_index::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}} + upper_bound_index::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}} + integer_index::Union{Nothing, MOI.ConstraintIndex{MOI.SingleVariable, MOI.Integer}} +end + +function bridge_constraint(::Type{SemiToBinaryBridge{T,S}}, model::MOI.ModelLike, f::MOI.SingleVariable, s::S) where {T <: Real, S<:SemiSets{T}} + binary = MOI.add_variable(model) + binary_ctr = MOI.add_constraint(model, MOI.SingleVariable(binary), MOI.ZeroOne()) + + # var - LB * bin >= 0 + lb = MOIU.operate(*, T, -s.lower, MOI.SingleVariable(binary)) + lb = MOIU.operate!(+, T, lb, f) + lb_ci = MOI.add_constraint(model, lb, MOI.GreaterThan{T}(zero(T))) + + # var - UB * bin <= 0 + ub = MOIU.operate(*, T, -s.upper, MOI.SingleVariable(binary)) + ub = MOIU.operate!(+, T, ub, f) + ub_ci = MOI.add_constraint(model, lb, MOI.LessThan{T}(zero(T))) + + if S == MOI.Semiinteger{T} + int_ci = MOI.add_constraint(model, f, MOI.Integer()) + else + int_ci = nothing + end + + return SemiToBinaryBridge{T,S}(s, f.variable, binary, binary_ctr, lb_ci, ub_ci, int_ci) +end + +function MOI.supports_constraint(::Type{<:SemiToBinaryBridge}, + ::Type{MOI.SingleVariable}, + ::Type{<:SemiSets}) + return true +end + +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, + b::SemiToBinaryBridge) + return b.semi_set +end + +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction, + b::SemiToBinaryBridge{T}) where {T} + return MOI.SingleVariable(b.variable_index) +end + +function MOI.delete(model::MOI.ModelLike, bridge::SemiToBinaryBridge) + if bridge.integer_index !== nothing + MOI.delete(model, bridge.integer_index) + end + MOI.delete(model, bridge.upper_bound_index) + MOI.delete(model, bridge.lower_bound_index) + MOI.delete(model, bridge.binary_constraint_index) + MOI.delete(model, bridge.binary_variable_index) + return +end + +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, + bridge::SemiToBinaryBridge) + MOI.get(model, MOI.VariablePrimal(attr.N), bridge.variable_index) +end + +function MOIB.added_constrained_variable_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S} + return [] +end + +function MOIB.added_constraint_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S<:MOI.Semicontinuous{T}} + return [ + (MOI.SingleVariable, MOI.ZeroOne), + (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}), + (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), + ] +end + +function MOIB.added_constraint_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S <: MOI.Semiinteger{T}} + return [ + (MOI.SingleVariable, MOI.ZeroOne), + (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}), + (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), + (MOI.SingleVariable, MOI.Integer), + ] +end + +function concrete_bridge_type(::Type{<:SemiToBinaryBridge{T}}, + ::Type{MOI.SingleVariable}, + ::Type{S}) where {T, S<:SemiSets} + return SemiToBinaryBridge{T, S} +end + +# Attributes, Bridge acting as a model + +function MOI.get(::SemiToBinaryBridge, ::MOI.NumberOfVariables) + return 1 +end + +function MOI.get(b::SemiToBinaryBridge, ::MOI.ListOfVariableIndices) + return [b.binary_variable_index] +end + +function MOI.get(::SemiToBinaryBridge{T, S}, + ::MOI.NumberOfConstraints{MOI.SingleVariable, MOI.ZeroOne}) where {T, S} + return 1 +end + +function MOI.get(::SemiToBinaryBridge{T, S}, + ::MOI.NumberOfConstraints{MOI.SingleVariable, MOI.Integer}) where {T, S<:MOI.Semiinteger} + return 1 +end + +function MOI.get(::SemiToBinaryBridge{T, S}, + ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where {T, S} + return 1 +end + +function MOI.get(::SemiToBinaryBridge{T, S}, + ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}}) where {T, S} + return 1 +end + +function MOI.get(b::SemiToBinaryBridge{T, S}, + ::MOI.ListOfConstraintIndices{MOI.SingleVariable, MOI.ZeroOne}) where {T, S} + return [b.binary_constraint_index] +end + +function MOI.get(b::SemiToBinaryBridge{T, S}, + ::MOI.ListOfConstraintIndices{MOI.SingleVariable, MOI.Integer}) where {T, S<:MOI.Semiinteger} + return [b.integer_index] +end + +function MOI.get(b::SemiToBinaryBridge{T, S}, + ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}}) where {T, S} + return [b.upper_bound_index] +end + +function MOI.get(b::SemiToBinaryBridge{T, S}, + ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where {T, S} + return [b.lower_bound_index] +end + +function MOI.supports( + ::MOI.ModelLike, + ::MOI.ConstraintPrimalStart, + ::Type{<:SemiToBinaryBridge}) + return true +end + +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, bridge::SemiToBinaryBridge) + return MOI.get(model, MOI.VariablePrimalStart(), bridge.variable_index) +end + +function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, + bridge::SemiToBinaryBridge{T}, value) where {T} + MOI.set(model, MOI.VariablePrimalStart(), bridge.variable_index, value) + bin_value = ifelse(value == 0.0, 0.0, 1.0) + MOI.set(model, MOI.VariablePrimalStart(), bridge.binary_variable_index, bin_value) + MOI.set(model, MOI.ConstraintPrimalStart(), + bridge.upper_bound_index, value - bridge.semi_set.upper * bin_value) + MOI.set(model, MOI.ConstraintPrimalStart(), + bridge.lower_bound_index, value - bridge.semi_set.lower * bin_value) + return +end + +function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintSet, + bridge::SemiToBinaryBridge{T, S}, set::S) where {T, S} + bridge.semi_set = set + MOI.modify(model, bridge.upper_bound_index, + MOI.ScalarCoefficientChange(bridge.binary_variable_index, -set.upper)) + MOI.modify(model, bridge.lower_bound_index, + MOI.ScalarCoefficientChange(bridge.binary_variable_index, -set.lower)) + return +end \ No newline at end of file diff --git a/test/Bridges/Constraint/semi_to_binary.jl b/test/Bridges/Constraint/semi_to_binary.jl new file mode 100644 index 0000000000..65ea2ba579 --- /dev/null +++ b/test/Bridges/Constraint/semi_to_binary.jl @@ -0,0 +1,105 @@ +using Test + +using MathOptInterface +const MOI = MathOptInterface +const MOIT = MathOptInterface.Test +const MOIU = MathOptInterface.Utilities +const MOIB = MathOptInterface.Bridges + +include("../utilities.jl") + +mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) +config = MOIT.TestConfig() + +@testset "SemiToBinary" begin + bridged_mock = MOIB.Constraint.SemiToBinary{Float64}(mock) + + MOIT.basic_constraint_tests( + bridged_mock, config, + include = [(F, S) + for F in [MOI.SingleVariable] + for S in [MOI.Semiinteger{Float64}, MOI.Semicontinuous{Float64}]]) + + MOIU.set_mock_optimize!(mock, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 0.0) + MOIU.mock_optimize!(mock, [0.0, 0.0, 0.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!(mock, [2.0, 1.0, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!(mock, [2.0, 2.0, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.5) + MOIU.mock_optimize!(mock, [2.5, 2.5, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 3.0) + MOIU.mock_optimize!(mock, [3.0, 3.0, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> MOI.set(mock, MOI.TerminationStatus(), MOI.INFEASIBLE) + ) + MOIT.semiconttest(bridged_mock,config) + + ci = first(MOI.get( + bridged_mock, + MOI.ListOfConstraintIndices{MOI.SingleVariable, + MOI.Semicontinuous{Float64}}())) + + test_delete_bridge(bridged_mock, ci, 2, ( + (MOI.SingleVariable, MOI.EqualTo{Float64}, 1), + (MOI.SingleVariable, MOI.ZeroOne, 0), + (MOI.SingleVariable, MOI.Integer, 0), + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, 0), + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 1), + )) + + MOIU.set_mock_optimize!(mock, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 0.0) + MOIU.mock_optimize!(mock, [0.0, 0.0, 0.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!(mock, [2.0, 1.0, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!(mock, [2.0, 2.0, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 3.0) + MOIU.mock_optimize!(mock, [3.0, 2.5, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 3.0) + MOIU.mock_optimize!(mock, [3.0, 3.0, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> MOI.set(mock, MOI.TerminationStatus(), MOI.INFEASIBLE) + ) + MOIT.semiinttest(bridged_mock,config) + + ci = first(MOI.get( + bridged_mock, + MOI.ListOfConstraintIndices{MOI.SingleVariable, + MOI.Semiinteger{Float64}}())) + + @testset "$attr" for attr in [MOI.ConstraintPrimalStart(),] + @test MOI.supports(bridged_mock, attr, typeof(ci)) + value = 2.0 + MOI.set(bridged_mock, attr, ci, value) + @test MOI.get(bridged_mock, attr, ci) ≈ value + end + + test_delete_bridge(bridged_mock, ci, 2, ( + (MOI.SingleVariable, MOI.EqualTo{Float64}, 1), + (MOI.SingleVariable, MOI.ZeroOne, 0), + (MOI.SingleVariable, MOI.Integer, 0), + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, 0), + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 1), + )) +end From 71a7a1d076a7f070b7e8d9b9897a78525be16368 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 6 Apr 2020 20:47:03 -0300 Subject: [PATCH 2/8] add suggestions --- src/Bridges/Constraint/semi_to_binary.jl | 27 +++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/Bridges/Constraint/semi_to_binary.jl b/src/Bridges/Constraint/semi_to_binary.jl index 7435f333a6..d6e6c6706c 100644 --- a/src/Bridges/Constraint/semi_to_binary.jl +++ b/src/Bridges/Constraint/semi_to_binary.jl @@ -4,19 +4,19 @@ const SemiSets{T} = Union{MOI.Semicontinuous{T}, MOI.Semiinteger{T}} SemiToBinaryBridge{T, S <: MOI.AbstractScalarSet} The `SemiToBinaryBridge` replaces an Semicontinuous constraint: -``x \\in Semicontinuous(LowerBound, UpperBound)`` +``x \\in \\mathsf{Semicontinuous}(l, u)`` is replaced by: -``z \\in \\ZeroOne``, -``x \\leq z \\cdot UpperBound ``, -``x \\geq z \\cdot LowerBound ``. +``z \\in \\{0, 1\\}``, +``x \\leq z \\cdot u ``, +``x \\geq z \\cdot l ``. The `SemiToBinaryBridge` replaces an Semiinteger constraint: -``x \\in Semiinteger(LowerBound, UpperBound)`` +``x \\in Semiinteger(l, u)`` is replaced by: -``z \\in \\ZeroOne``, +``z \\in \\{0, 1\\}``, ``x \\in \\Integer``, -``x \\leq z \\cdot UpperBound ``, -``x \\geq z \\cdot LowerBound ``. +``x \\leq z \\cdot u ``, +``x \\geq z \\cdot l ``. """ struct SemiToBinaryBridge{T, S <: SemiSets{T}} <: AbstractBridge semi_set::S @@ -29,8 +29,7 @@ struct SemiToBinaryBridge{T, S <: SemiSets{T}} <: AbstractBridge end function bridge_constraint(::Type{SemiToBinaryBridge{T,S}}, model::MOI.ModelLike, f::MOI.SingleVariable, s::S) where {T <: Real, S<:SemiSets{T}} - binary = MOI.add_variable(model) - binary_ctr = MOI.add_constraint(model, MOI.SingleVariable(binary), MOI.ZeroOne()) + binary, binary_con = MOI.add_constrained_variable(model, MOI.ZeroOne()) # var - LB * bin >= 0 lb = MOIU.operate(*, T, -s.lower, MOI.SingleVariable(binary)) @@ -42,13 +41,13 @@ function bridge_constraint(::Type{SemiToBinaryBridge{T,S}}, model::MOI.ModelLike ub = MOIU.operate!(+, T, ub, f) ub_ci = MOI.add_constraint(model, lb, MOI.LessThan{T}(zero(T))) - if S == MOI.Semiinteger{T} + if s isa MOI.Semiinteger{T} int_ci = MOI.add_constraint(model, f, MOI.Integer()) else int_ci = nothing end - return SemiToBinaryBridge{T,S}(s, f.variable, binary, binary_ctr, lb_ci, ub_ci, int_ci) + return SemiToBinaryBridge{T,S}(s, f.variable, binary, binary_con, lb_ci, ub_ci, int_ci) end function MOI.supports_constraint(::Type{<:SemiToBinaryBridge}, @@ -84,12 +83,11 @@ function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, end function MOIB.added_constrained_variable_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S} - return [] + return [(MOI.ZeroOne,)] end function MOIB.added_constraint_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S<:MOI.Semicontinuous{T}} return [ - (MOI.SingleVariable, MOI.ZeroOne), (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}), (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), ] @@ -97,7 +95,6 @@ end function MOIB.added_constraint_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S <: MOI.Semiinteger{T}} return [ - (MOI.SingleVariable, MOI.ZeroOne), (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}), (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), (MOI.SingleVariable, MOI.Integer), From 9509a7e692fb384adfd9d5887c4f655816490e88 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Tue, 7 Apr 2020 01:02:40 -0300 Subject: [PATCH 3/8] improve coverage --- src/Bridges/Constraint/semi_to_binary.jl | 12 ++++++------ test/Bridges/Constraint/semi_to_binary.jl | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Bridges/Constraint/semi_to_binary.jl b/src/Bridges/Constraint/semi_to_binary.jl index d6e6c6706c..8d2bc80f84 100644 --- a/src/Bridges/Constraint/semi_to_binary.jl +++ b/src/Bridges/Constraint/semi_to_binary.jl @@ -19,7 +19,7 @@ is replaced by: ``x \\geq z \\cdot l ``. """ struct SemiToBinaryBridge{T, S <: SemiSets{T}} <: AbstractBridge - semi_set::S + semi_set::Ref{S} variable_index::MOI.VariableIndex binary_variable_index::MOI.VariableIndex binary_constraint_index::MOI.ConstraintIndex{MOI.SingleVariable, MOI.ZeroOne} @@ -47,7 +47,7 @@ function bridge_constraint(::Type{SemiToBinaryBridge{T,S}}, model::MOI.ModelLike int_ci = nothing end - return SemiToBinaryBridge{T,S}(s, f.variable, binary, binary_con, lb_ci, ub_ci, int_ci) + return SemiToBinaryBridge{T,S}(Ref(s), f.variable, binary, binary_con, lb_ci, ub_ci, int_ci) end function MOI.supports_constraint(::Type{<:SemiToBinaryBridge}, @@ -58,7 +58,7 @@ end function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, b::SemiToBinaryBridge) - return b.semi_set + return b.semi_set[] end function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction, @@ -174,15 +174,15 @@ function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, bin_value = ifelse(value == 0.0, 0.0, 1.0) MOI.set(model, MOI.VariablePrimalStart(), bridge.binary_variable_index, bin_value) MOI.set(model, MOI.ConstraintPrimalStart(), - bridge.upper_bound_index, value - bridge.semi_set.upper * bin_value) + bridge.upper_bound_index, value - bridge.semi_set[].upper * bin_value) MOI.set(model, MOI.ConstraintPrimalStart(), - bridge.lower_bound_index, value - bridge.semi_set.lower * bin_value) + bridge.lower_bound_index, value - bridge.semi_set[].lower * bin_value) return end function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintSet, bridge::SemiToBinaryBridge{T, S}, set::S) where {T, S} - bridge.semi_set = set + bridge.semi_set[] = set MOI.modify(model, bridge.upper_bound_index, MOI.ScalarCoefficientChange(bridge.binary_variable_index, -set.upper)) MOI.modify(model, bridge.lower_bound_index, diff --git a/test/Bridges/Constraint/semi_to_binary.jl b/test/Bridges/Constraint/semi_to_binary.jl index 65ea2ba579..11e0b376bc 100644 --- a/test/Bridges/Constraint/semi_to_binary.jl +++ b/test/Bridges/Constraint/semi_to_binary.jl @@ -5,6 +5,7 @@ const MOI = MathOptInterface const MOIT = MathOptInterface.Test const MOIU = MathOptInterface.Utilities const MOIB = MathOptInterface.Bridges +const MOIBC = MathOptInterface.Bridges.Constraint include("../utilities.jl") @@ -12,7 +13,16 @@ mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) config = MOIT.TestConfig() @testset "SemiToBinary" begin - bridged_mock = MOIB.Constraint.SemiToBinary{Float64}(mock) + bridged_mock = MOIBC.SemiToBinary{Float64}(mock) + + bridge_type = MOIBC.SemiToBinaryBridge{Float64, MOI.Semiinteger{Float64}} + @test MOI.supports_constraint(bridge_type, + MOI.SingleVariable, MOI.Semiinteger{Float64}) + @test MOIBC.concrete_bridge_type(bridge_type, + MOI.SingleVariable, + MOI.Semiinteger{Float64}) == bridge_type + + @test MOI.supports(bridged_mock, MOI.ConstraintPrimalStart(), bridge_type) MOIT.basic_constraint_tests( bridged_mock, config, @@ -88,6 +98,11 @@ config = MOIT.TestConfig() MOI.ListOfConstraintIndices{MOI.SingleVariable, MOI.Semiinteger{Float64}}())) + @test MOI.get(bridged_mock, MOI.ConstraintPrimal(), ci) == 3 + new_set = MOI.Semiinteger{Float64}(19.0, 20.0) + MOI.set(bridged_mock, MOI.ConstraintSet(), ci, new_set) + @test MOI.get(bridged_mock, MOI.ConstraintSet(), ci) == new_set + @testset "$attr" for attr in [MOI.ConstraintPrimalStart(),] @test MOI.supports(bridged_mock, attr, typeof(ci)) value = 2.0 From 77e17d93cb277aa9310ac4eb5c74fdeb3a335ee4 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Tue, 7 Apr 2020 12:57:37 -0300 Subject: [PATCH 4/8] re-order file and add tests --- src/Bridges/Constraint/semi_to_binary.jl | 105 +++++++++++----------- test/Bridges/Constraint/semi_to_binary.jl | 17 +++- 2 files changed, 69 insertions(+), 53 deletions(-) diff --git a/src/Bridges/Constraint/semi_to_binary.jl b/src/Bridges/Constraint/semi_to_binary.jl index 8d2bc80f84..a441eeaccb 100644 --- a/src/Bridges/Constraint/semi_to_binary.jl +++ b/src/Bridges/Constraint/semi_to_binary.jl @@ -50,6 +50,32 @@ function bridge_constraint(::Type{SemiToBinaryBridge{T,S}}, model::MOI.ModelLike return SemiToBinaryBridge{T,S}(Ref(s), f.variable, binary, binary_con, lb_ci, ub_ci, int_ci) end + +function MOIB.added_constrained_variable_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S} + return [(MOI.ZeroOne,)] +end + +function MOIB.added_constraint_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S<:MOI.Semicontinuous{T}} + return [ + (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}), + (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), + ] +end + +function MOIB.added_constraint_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S <: MOI.Semiinteger{T}} + return [ + (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}), + (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), + (MOI.SingleVariable, MOI.Integer), + ] +end + +function concrete_bridge_type(::Type{<:SemiToBinaryBridge{T}}, + ::Type{MOI.SingleVariable}, + ::Type{S}) where {T, S<:SemiSets} + return SemiToBinaryBridge{T, S} +end + function MOI.supports_constraint(::Type{<:SemiToBinaryBridge}, ::Type{MOI.SingleVariable}, ::Type{<:SemiSets}) @@ -61,6 +87,16 @@ function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, return b.semi_set[] end +function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintSet, + bridge::SemiToBinaryBridge{T, S}, set::S) where {T, S} + bridge.semi_set[] = set + MOI.modify(model, bridge.upper_bound_index, + MOI.ScalarCoefficientChange(bridge.binary_variable_index, -set.upper)) + MOI.modify(model, bridge.lower_bound_index, + MOI.ScalarCoefficientChange(bridge.binary_variable_index, -set.lower)) + return +end + function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction, b::SemiToBinaryBridge{T}) where {T} return MOI.SingleVariable(b.variable_index) @@ -82,29 +118,27 @@ function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, MOI.get(model, MOI.VariablePrimal(attr.N), bridge.variable_index) end -function MOIB.added_constrained_variable_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S} - return [(MOI.ZeroOne,)] -end - -function MOIB.added_constraint_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S<:MOI.Semicontinuous{T}} - return [ - (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}), - (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), - ] +function MOI.supports( + ::MOI.ModelLike, + ::MOI.ConstraintPrimalStart, + ::Type{<:SemiToBinaryBridge}) + return true end -function MOIB.added_constraint_types(::Type{<:SemiToBinaryBridge{T, S}}) where {T, S <: MOI.Semiinteger{T}} - return [ - (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}), - (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), - (MOI.SingleVariable, MOI.Integer), - ] +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, bridge::SemiToBinaryBridge) + return MOI.get(model, MOI.VariablePrimalStart(), bridge.variable_index) end -function concrete_bridge_type(::Type{<:SemiToBinaryBridge{T}}, - ::Type{MOI.SingleVariable}, - ::Type{S}) where {T, S<:SemiSets} - return SemiToBinaryBridge{T, S} +function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, + bridge::SemiToBinaryBridge{T}, value) where {T} + MOI.set(model, MOI.VariablePrimalStart(), bridge.variable_index, value) + bin_value = ifelse(iszero(value), 0.0, 1.0) + MOI.set(model, MOI.VariablePrimalStart(), bridge.binary_variable_index, bin_value) + MOI.set(model, MOI.ConstraintPrimalStart(), + bridge.upper_bound_index, value - bridge.semi_set[].upper * bin_value) + MOI.set(model, MOI.ConstraintPrimalStart(), + bridge.lower_bound_index, value - bridge.semi_set[].lower * bin_value) + return end # Attributes, Bridge acting as a model @@ -155,37 +189,4 @@ end function MOI.get(b::SemiToBinaryBridge{T, S}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where {T, S} return [b.lower_bound_index] -end - -function MOI.supports( - ::MOI.ModelLike, - ::MOI.ConstraintPrimalStart, - ::Type{<:SemiToBinaryBridge}) - return true -end - -function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, bridge::SemiToBinaryBridge) - return MOI.get(model, MOI.VariablePrimalStart(), bridge.variable_index) -end - -function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, - bridge::SemiToBinaryBridge{T}, value) where {T} - MOI.set(model, MOI.VariablePrimalStart(), bridge.variable_index, value) - bin_value = ifelse(value == 0.0, 0.0, 1.0) - MOI.set(model, MOI.VariablePrimalStart(), bridge.binary_variable_index, bin_value) - MOI.set(model, MOI.ConstraintPrimalStart(), - bridge.upper_bound_index, value - bridge.semi_set[].upper * bin_value) - MOI.set(model, MOI.ConstraintPrimalStart(), - bridge.lower_bound_index, value - bridge.semi_set[].lower * bin_value) - return -end - -function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintSet, - bridge::SemiToBinaryBridge{T, S}, set::S) where {T, S} - bridge.semi_set[] = set - MOI.modify(model, bridge.upper_bound_index, - MOI.ScalarCoefficientChange(bridge.binary_variable_index, -set.upper)) - MOI.modify(model, bridge.lower_bound_index, - MOI.ScalarCoefficientChange(bridge.binary_variable_index, -set.lower)) - return end \ No newline at end of file diff --git a/test/Bridges/Constraint/semi_to_binary.jl b/test/Bridges/Constraint/semi_to_binary.jl index 11e0b376bc..6f9a3b6fa0 100644 --- a/test/Bridges/Constraint/semi_to_binary.jl +++ b/test/Bridges/Constraint/semi_to_binary.jl @@ -30,7 +30,7 @@ config = MOIT.TestConfig() for F in [MOI.SingleVariable] for S in [MOI.Semiinteger{Float64}, MOI.Semicontinuous{Float64}]]) - MOIU.set_mock_optimize!(mock, + MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> begin MOI.set(mock, MOI.ObjectiveBound(), 0.0) MOIU.mock_optimize!(mock, [0.0, 0.0, 0.0]) @@ -117,4 +117,19 @@ config = MOIT.TestConfig() (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, 0), (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 1), )) + + + s = """ + variables: x, y + cy: y == 4.0 + cx: x in Semiinteger(2.0, 3.0) + cxy: x + -1.0y >= 0.0 + minobjective: x + """ + model = MOIU.Model{Float64}() + MOIU.loadfromstring!(model, s) + MOI.empty!(bridged_mock) + @test MOI.is_empty(bridged_mock) + MOIU.loadfromstring!(bridged_mock, s) + MOIU.test_models_equal(bridged_mock, model, ["x", "y"], ["cy", "cx", "cxy"]) end From c13773a86f02bb01a54fd0ef223aae095e61a66a Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Tue, 7 Apr 2020 22:51:13 -0300 Subject: [PATCH 5/8] change to mutable remove ref --- src/Bridges/Constraint/semi_to_binary.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Bridges/Constraint/semi_to_binary.jl b/src/Bridges/Constraint/semi_to_binary.jl index a441eeaccb..d70d77e5a8 100644 --- a/src/Bridges/Constraint/semi_to_binary.jl +++ b/src/Bridges/Constraint/semi_to_binary.jl @@ -18,8 +18,8 @@ is replaced by: ``x \\leq z \\cdot u ``, ``x \\geq z \\cdot l ``. """ -struct SemiToBinaryBridge{T, S <: SemiSets{T}} <: AbstractBridge - semi_set::Ref{S} +mutable struct SemiToBinaryBridge{T, S <: SemiSets{T}} <: AbstractBridge + semi_set::S variable_index::MOI.VariableIndex binary_variable_index::MOI.VariableIndex binary_constraint_index::MOI.ConstraintIndex{MOI.SingleVariable, MOI.ZeroOne} @@ -47,7 +47,7 @@ function bridge_constraint(::Type{SemiToBinaryBridge{T,S}}, model::MOI.ModelLike int_ci = nothing end - return SemiToBinaryBridge{T,S}(Ref(s), f.variable, binary, binary_con, lb_ci, ub_ci, int_ci) + return SemiToBinaryBridge{T,S}(s, f.variable, binary, binary_con, lb_ci, ub_ci, int_ci) end @@ -84,12 +84,12 @@ end function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, b::SemiToBinaryBridge) - return b.semi_set[] + return b.semi_set end function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintSet, bridge::SemiToBinaryBridge{T, S}, set::S) where {T, S} - bridge.semi_set[] = set + bridge.semi_set = set MOI.modify(model, bridge.upper_bound_index, MOI.ScalarCoefficientChange(bridge.binary_variable_index, -set.upper)) MOI.modify(model, bridge.lower_bound_index, @@ -135,9 +135,9 @@ function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, bin_value = ifelse(iszero(value), 0.0, 1.0) MOI.set(model, MOI.VariablePrimalStart(), bridge.binary_variable_index, bin_value) MOI.set(model, MOI.ConstraintPrimalStart(), - bridge.upper_bound_index, value - bridge.semi_set[].upper * bin_value) + bridge.upper_bound_index, value - bridge.semi_set.upper * bin_value) MOI.set(model, MOI.ConstraintPrimalStart(), - bridge.lower_bound_index, value - bridge.semi_set[].lower * bin_value) + bridge.lower_bound_index, value - bridge.semi_set.lower * bin_value) return end From 0f1513d130f224894930accd358135d707e97fae Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sat, 11 Apr 2020 20:11:32 -0300 Subject: [PATCH 6/8] add tests and fixes --- src/Bridges/Constraint/semi_to_binary.jl | 2 +- test/Bridges/Constraint/semi_to_binary.jl | 51 ++++++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/Bridges/Constraint/semi_to_binary.jl b/src/Bridges/Constraint/semi_to_binary.jl index d70d77e5a8..cdd3e75423 100644 --- a/src/Bridges/Constraint/semi_to_binary.jl +++ b/src/Bridges/Constraint/semi_to_binary.jl @@ -39,7 +39,7 @@ function bridge_constraint(::Type{SemiToBinaryBridge{T,S}}, model::MOI.ModelLike # var - UB * bin <= 0 ub = MOIU.operate(*, T, -s.upper, MOI.SingleVariable(binary)) ub = MOIU.operate!(+, T, ub, f) - ub_ci = MOI.add_constraint(model, lb, MOI.LessThan{T}(zero(T))) + ub_ci = MOI.add_constraint(model, ub, MOI.LessThan{T}(zero(T))) if s isa MOI.Semiinteger{T} int_ci = MOI.add_constraint(model, f, MOI.Integer()) diff --git a/test/Bridges/Constraint/semi_to_binary.jl b/test/Bridges/Constraint/semi_to_binary.jl index 6f9a3b6fa0..942ff10fc6 100644 --- a/test/Bridges/Constraint/semi_to_binary.jl +++ b/test/Bridges/Constraint/semi_to_binary.jl @@ -123,13 +123,60 @@ config = MOIT.TestConfig() variables: x, y cy: y == 4.0 cx: x in Semiinteger(2.0, 3.0) - cxy: x + -1.0y >= 0.0 minobjective: x """ model = MOIU.Model{Float64}() MOIU.loadfromstring!(model, s) + sb = """ + variables: x, y, z + cy: y == 4.0 + bin: z in ZeroOne() + int: x in Integer() + clo: x + -2.0z >= 0.0 + cup: x + -3.0z <= 0.0 + minobjective: x + """ + modelb = MOIU.Model{Float64}() + MOIU.loadfromstring!(modelb, sb) + MOI.empty!(bridged_mock) @test MOI.is_empty(bridged_mock) MOIU.loadfromstring!(bridged_mock, s) - MOIU.test_models_equal(bridged_mock, model, ["x", "y"], ["cy", "cx", "cxy"]) + MOIU.test_models_equal(bridged_mock, model, ["x", "y"], ["cy", "cx"]) + + # setting names on mock + ci = first(MOI.get(mock,MOI.ListOfConstraintIndices{ + MOI.SingleVariable, MOI.ZeroOne}())) + MOI.set(mock, MOI.ConstraintName(), ci, "bin") + z = MOI.VariableIndex(ci.value) + MOI.set(mock, MOI.VariableName(), z, "z") + ci = first(MOI.get(mock, MOI.ListOfConstraintIndices{ + MOI.SingleVariable, MOI.Integer}())) + MOI.set(mock, MOI.ConstraintName(), ci, "int") + x = MOI.VariableIndex(ci.value) + MOI.set(mock, MOI.VariableName(), x, "x") + ci = first(MOI.get(mock,MOI.ListOfConstraintIndices{ + MOI.SingleVariable, MOI.EqualTo{Float64}}())) + MOI.set(mock, MOI.ConstraintName(), ci, "cy") + y = MOI.VariableIndex(ci.value) + MOI.set(mock, MOI.VariableName(), y, "y") + ci = first(MOI.get(mock,MOI.ListOfConstraintIndices{ + MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}}())) + MOI.set(mock, MOI.ConstraintName(), ci, "clo") + ci = first(MOI.get(mock,MOI.ListOfConstraintIndices{ + MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}}())) + MOI.set(mock, MOI.ConstraintName(), ci, "cup") + + MOIU.test_models_equal(mock, modelb, ["x", "y", "z"], ["cy", "bin", "int", "clo", "cup"]) + + + # inds = MOI.get(model, MOI.ListOfVariableIndices()) + # for i in inds + # if !(i in [x,z]) + # y = i + # MOI.set(model, MOI.VariableName(), y, "y") + # break + # end + # end + end From 602aed98851905d07bd2cca6f38272d88a268d0c Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sat, 11 Apr 2020 20:11:50 -0300 Subject: [PATCH 7/8] fix typos --- src/Utilities/functions.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 0bd4ac8dcc..b0fedc7018 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -489,7 +489,7 @@ function test_variablenames_equal(model, variablenames) end for (vname,seen) in seen_name if !seen - error("Did not find variable with name $vname in intance.") + error("Did not find variable with name $vname in instance.") end end end @@ -509,7 +509,7 @@ function test_constraintnames_equal(model, constraintnames) end for (cname,seen) in seen_name if !seen - error("Did not find constraint with name $cname in intance.") + error("Did not find constraint with name $cname in instance.") end end end From 53cbef039f819c72869677fd693e5ff5c4c9a149 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 12 Apr 2020 21:33:59 -0300 Subject: [PATCH 8/8] cleanup tests --- test/Bridges/Constraint/semi_to_binary.jl | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test/Bridges/Constraint/semi_to_binary.jl b/test/Bridges/Constraint/semi_to_binary.jl index 942ff10fc6..63a1fe7c41 100644 --- a/test/Bridges/Constraint/semi_to_binary.jl +++ b/test/Bridges/Constraint/semi_to_binary.jl @@ -153,13 +153,6 @@ config = MOIT.TestConfig() ci = first(MOI.get(mock, MOI.ListOfConstraintIndices{ MOI.SingleVariable, MOI.Integer}())) MOI.set(mock, MOI.ConstraintName(), ci, "int") - x = MOI.VariableIndex(ci.value) - MOI.set(mock, MOI.VariableName(), x, "x") - ci = first(MOI.get(mock,MOI.ListOfConstraintIndices{ - MOI.SingleVariable, MOI.EqualTo{Float64}}())) - MOI.set(mock, MOI.ConstraintName(), ci, "cy") - y = MOI.VariableIndex(ci.value) - MOI.set(mock, MOI.VariableName(), y, "y") ci = first(MOI.get(mock,MOI.ListOfConstraintIndices{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}}())) MOI.set(mock, MOI.ConstraintName(), ci, "clo") @@ -169,14 +162,4 @@ config = MOIT.TestConfig() MOIU.test_models_equal(mock, modelb, ["x", "y", "z"], ["cy", "bin", "int", "clo", "cup"]) - - # inds = MOI.get(model, MOI.ListOfVariableIndices()) - # for i in inds - # if !(i in [x,z]) - # y = i - # MOI.set(model, MOI.VariableName(), y, "y") - # break - # end - # end - end