From dce45e9c2ab9918a947030a87639280ff92c7363 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 7 Aug 2019 14:59:30 +0200 Subject: [PATCH 01/26] add L1 and Linfinity norm cones and tests --- src/Test/UnitTests/basic_constraint_tests.jl | 2 + src/Test/contconic.jl | 276 ++++++++++++++++++- src/Utilities/model.jl | 1 + src/sets.jl | 25 +- test/Bridges/lazy_bridge_optimizer.jl | 1 + test/Utilities/sets.jl | 13 +- 6 files changed, 315 insertions(+), 3 deletions(-) diff --git a/src/Test/UnitTests/basic_constraint_tests.jl b/src/Test/UnitTests/basic_constraint_tests.jl index 3456e22e7f..5422ae82c9 100644 --- a/src/Test/UnitTests/basic_constraint_tests.jl +++ b/src/Test/UnitTests/basic_constraint_tests.jl @@ -35,6 +35,8 @@ const BasicConstraintTests = Dict( (MOI.VectorOfVariables, MOI.Nonnegatives) => ( dummy_vectorofvariables, 2, MOI.Nonnegatives(2) ), (MOI.VectorOfVariables, MOI.SecondOrderCone) => ( dummy_vectorofvariables, 3, MOI.SecondOrderCone(3) ), + (MOI.VectorOfVariables, MOI.NormInfinityCone) => ( dummy_vectorofvariables, 3, MOI.NormInfinityCone(3) ), + (MOI.VectorOfVariables, MOI.NormOneCone) => ( dummy_vectorofvariables, 3, MOI.NormOneCone(3) ), (MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => ( dummy_vectorofvariables, 3, MOI.RotatedSecondOrderCone(3) ), (MOI.VectorOfVariables, MOI.GeometricMeanCone) => ( dummy_vectorofvariables, 3, MOI.GeometricMeanCone(3) ), (MOI.VectorOfVariables, MOI.ExponentialCone) => ( dummy_vectorofvariables, 3, MOI.ExponentialCone() ), diff --git a/src/Test/contconic.jl b/src/Test/contconic.jl index 7a44bab367..f6ebf71bb1 100644 --- a/src/Test/contconic.jl +++ b/src/Test/contconic.jl @@ -302,12 +302,284 @@ const lintests = Dict("lin1v" => lin1vtest, @moitestset lin +function _norminf1test(model::MOI.ModelLike, config::TestConfig, vecofvars::Bool) + atol = config.atol + rtol = config.rtol + # Problem NormInf1 + # max 0x + 1y + 1z + # st x == 1 + # y == 1/2 + # x >= ||(y,z)||_∞ + + @test MOIU.supports_default_copy_to(model, #=copy_names=# false) + @test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}()) + @test MOI.supports(model, MOI.ObjectiveSense()) + if vecofvars + @test MOI.supports_constraint(model, MOI.VectorOfVariables, MOI.Zeros) + else + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.Zeros) + end + @test MOI.supports_constraint(model, MOI.VectorOfVariables, MOI.NormInfinityCone) + + MOI.empty!(model) + @test MOI.is_empty(model) + + x,y,z = MOI.add_variables(model, 3) + + MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0,1.0], [y,z]), 0.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + + ceq1 = MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x))], [-1.0]), MOI.Zeros(1)) + ceq2 = MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, y))], [-0.5]), MOI.Zeros(1)) + vov = MOI.VectorOfVariables([x,y,z]) + if vecofvars + ccone = MOI.add_constraint(model, vov, MOI.NormInfinityCone(3)) + else + ccone = MOI.add_constraint(model, MOI.VectorAffineFunction{Float64}(vov), MOI.NormInfinityCone(3)) + end + + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.Zeros}()) == 2 + @test MOI.get(model, MOI.NumberOfConstraints{vecofvars ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64},MOI.NormInfinityCone}()) == 1 + loc = MOI.get(model, MOI.ListOfConstraints()) + @test length(loc) == 2 + @test (MOI.VectorAffineFunction{Float64},MOI.Zeros) in loc + @test (vecofvars ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) in loc + + if config.solve + @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED + + MOI.optimize!(model) + + @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status + + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + if config.duals + @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + end + + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 1.5 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 1.5 atol=atol rtol=rtol + end + + @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 1 atol=atol rtol=rtol + @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 0.5 atol=atol rtol=rtol + @test MOI.get(model, MOI.VariablePrimal(), z) ≈ 1 atol=atol rtol=rtol + + @test MOI.get(model, MOI.ConstraintPrimal(), ceq1) ≈ [0] atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintPrimal(), ceq2) ≈ [0] atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintPrimal(), ccone) ≈ [1.0, 0.5, 1.0] atol=atol rtol=rtol + + if config.duals + @test MOI.get(model, MOI.ConstraintDual(), ceq1) ≈ [-1] atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintDual(), ceq2) ≈ [-1] atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintDual(), ccone) ≈ [1.0, 0.0, -1.0] atol=atol rtol=rtol + end + end +end + +norminf1vtest(model::MOI.ModelLike, config::TestConfig) = _norminf1test(model, config, true) +norminf1ftest(model::MOI.ModelLike, config::TestConfig) = _norminf1test(model, config, false) + +function norminf2test(model::MOI.ModelLike, config::TestConfig) + atol = config.atol + rtol = config.rtol + # Problem NormInf2 - Infeasible + # min 0 + # s.t. y ≥ 2 + # x ≤ 1 + # |y| ≤ x + # in conic form: + # min 0 + # s.t. -2 + y ∈ R₊ + # -1 + x ∈ R₋ + # (x,y) ∈ NormInf₂ + + @test MOIU.supports_default_copy_to(model, #=copy_names=# false) + @test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}()) + @test MOI.supports(model, MOI.ObjectiveSense()) + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.NormInfCone) + + MOI.empty!(model) + @test MOI.is_empty(model) + + x,y = MOI.add_variables(model, 2) + + MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, y))], [-2.0]), MOI.Nonnegatives(1)) + MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x))], [-1.0]), MOI.Nonpositives(1)) + MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.([1,2], MOI.ScalarAffineTerm.(1.0, [x,y])), zeros(2)), MOI.NormInfCone(2)) + + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.Nonnegatives}()) == 1 + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.Nonpositives}()) == 1 + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.NormInfCone}()) == 1 + + if config.solve + @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED + + MOI.optimize!(model) + + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + + @test MOI.get(model, MOI.PrimalStatus()) in (MOI.NO_SOLUTION, + MOI.INFEASIBLE_POINT) + if config.duals + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + end + + # TODO test dual feasibility and objective sign + end +end + +const norminftests = Dict("norminf1v" => norminf1vtest, + "norminf1f" => norminf1ftest, + "norminf2" => norminf2test) + +@moitestset norminf + +function _normone1test(model::MOI.ModelLike, config::TestConfig, vecofvars::Bool) + atol = config.atol + rtol = config.rtol + # Problem NormOne1 + # max 0x + 1y + 1z + # st x == 1 + # y == 1/2 + # x >= ||(y,z)||_1 + + @test MOIU.supports_default_copy_to(model, #=copy_names=# false) + @test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}()) + @test MOI.supports(model, MOI.ObjectiveSense()) + if vecofvars + @test MOI.supports_constraint(model, MOI.VectorOfVariables, MOI.Zeros) + else + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.Zeros) + end + @test MOI.supports_constraint(model, MOI.VectorOfVariables, MOI.NormOneCone) + + MOI.empty!(model) + @test MOI.is_empty(model) + + x,y,z = MOI.add_variables(model, 3) + + MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0,1.0], [y,z]), 0.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + + ceq1 = MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x))], [-1.0]), MOI.Zeros(1)) + ceq2 = MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, y))], [-0.5]), MOI.Zeros(1)) + vov = MOI.VectorOfVariables([x,y,z]) + if vecofvars + ccone = MOI.add_constraint(model, vov, MOI.NormOneCone(3)) + else + ccone = MOI.add_constraint(model, MOI.VectorAffineFunction{Float64}(vov), MOI.NormOneCone(3)) + end + + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.Zeros}()) == 2 + @test MOI.get(model, MOI.NumberOfConstraints{vecofvars ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64},MOI.NormOneCone}()) == 1 + loc = MOI.get(model, MOI.ListOfConstraints()) + @test length(loc) == 2 + @test (MOI.VectorAffineFunction{Float64},MOI.Zeros) in loc + @test (vecofvars ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) in loc + + if config.solve + @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED + + MOI.optimize!(model) + + @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status + + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + if config.duals + @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + end + + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 1 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 1 atol=atol rtol=rtol + end + + @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 1 atol=atol rtol=rtol + @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 0.5 atol=atol rtol=rtol + @test MOI.get(model, MOI.VariablePrimal(), z) ≈ 0.5 atol=atol rtol=rtol + + @test MOI.get(model, MOI.ConstraintPrimal(), ceq1) ≈ [0] atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintPrimal(), ceq2) ≈ [0] atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintPrimal(), ccone) ≈ [1.0, 0.5, 0.5] atol=atol rtol=rtol + + if config.duals + @test MOI.get(model, MOI.ConstraintDual(), ceq1) ≈ [-1] atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintDual(), ceq2) ≈ [0] atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintDual(), ccone) ≈ [1.0, -1.0, -1.0] atol=atol rtol=rtol + end + end +end + +normone1vtest(model::MOI.ModelLike, config::TestConfig) = _normone1test(model, config, true) +normone1ftest(model::MOI.ModelLike, config::TestConfig) = _normone1test(model, config, false) + +function normone2test(model::MOI.ModelLike, config::TestConfig) + atol = config.atol + rtol = config.rtol + # Problem NormOne2 - Infeasible + # min 0 + # s.t. y ≥ 2 + # x ≤ 1 + # |y| ≤ x + # in conic form: + # min 0 + # s.t. -2 + y ∈ R₊ + # -1 + x ∈ R₋ + # (x,y) ∈ NormOne₂ + + @test MOIU.supports_default_copy_to(model, #=copy_names=# false) + @test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}()) + @test MOI.supports(model, MOI.ObjectiveSense()) + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) + + MOI.empty!(model) + @test MOI.is_empty(model) + + x,y = MOI.add_variables(model, 2) + + MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, y))], [-2.0]), MOI.Nonnegatives(1)) + MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x))], [-1.0]), MOI.Nonpositives(1)) + MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.([1,2], MOI.ScalarAffineTerm.(1.0, [x,y])), zeros(2)), MOI.NormOneCone(2)) + + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.Nonnegatives}()) == 1 + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.Nonpositives}()) == 1 + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.NormOneCone}()) == 1 + + if config.solve + @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED + + MOI.optimize!(model) + + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + + @test MOI.get(model, MOI.PrimalStatus()) in (MOI.NO_SOLUTION, + MOI.INFEASIBLE_POINT) + if config.duals + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + end + + # TODO test dual feasibility and objective sign + end +end + +const normonetests = Dict("normone1v" => normone1vtest, + "normone1f" => normone1ftest, + "normone2" => normone2test) + +@moitestset normone + function _soc1test(model::MOI.ModelLike, config::TestConfig, vecofvars::Bool) atol = config.atol rtol = config.rtol # Problem SOC1 # max 0x + 1y + 1z - # st x == 1 + # st x == 1 # x >= ||(y,z)|| @test MOIU.supports_default_copy_to(model, #=copy_names=# false) @@ -1772,6 +2044,8 @@ const rootdettests = Dict("rootdett" => rootdetttest, @moitestset rootdet true const contconictests = Dict("lin" => lintest, + "norminf" => norminftest, + "normone" => normonetest, "soc" => soctest, "rsoc" => rsoctest, "geomean" => geomeantest, diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index 165ebe50be..77cc4747ad 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -1010,6 +1010,7 @@ const LessThanIndicatorSetZero{T} = MOI.IndicatorSet{MOI.ACTIVATE_ON_ZERO, MOI.L (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval, MOI.Semicontinuous, MOI.Semiinteger), (MOI.Reals, MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, + MOI.NormInfinityCone, MOI.NormOneCone, MOI.SecondOrderCone, MOI.RotatedSecondOrderCone, MOI.GeometricMeanCone, MOI.ExponentialCone, MOI.DualExponentialCone, MOI.PositiveSemidefiniteConeTriangle, MOI.PositiveSemidefiniteConeSquare, diff --git a/src/sets.jl b/src/sets.jl index bcbabb6303..8c51c37cae 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -35,7 +35,7 @@ function dimension end """ dual_set(s::AbstractSet) -Return the dual set of `s`, that is the dual cone of the set. This follows the +Return the dual set of `s`, that is the dual cone of the set. This follows the definition of duality discussed in [Duals](@ref). See [Dual cone](https://en.wikipedia.org/wiki/Dual_cone_and_polar_cone) for more information. If the dual cone is not defined it returns an error. @@ -184,6 +184,28 @@ constant(s::EqualTo) = s.value constant(s::GreaterThan) = s.lower constant(s::LessThan) = s.upper +""" + NormInfinityCone(dimension) + +The epigraph of the L∞ norm function ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_∞ = \\max_i |x_i| \\}`` of dimension `dimension`. +""" +struct NormInfinityCone <: AbstractVectorSet + dimension::Int +end + +dual_set(s::NormInfinityCone) = NormOneCone(dimension(s)) + +""" + NormOneCone(dimension) + +The epigraph of the L1 norm function ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_1 = \\sum_i |x_i| \\}`` of dimension `dimension`. +""" +struct NormOneCone <: AbstractVectorSet + dimension::Int +end + +dual_set(s::NormOneCone) = NormInfinityCone(dimension(s)) + """ SecondOrderCone(dimension) @@ -636,6 +658,7 @@ end # isbits types, nothing to copy function Base.copy(set::Union{Reals, Zeros, Nonnegatives, Nonpositives, GreaterThan, LessThan, EqualTo, Interval, + NormInfinityCone, NormOneCone, SecondOrderCone, RotatedSecondOrderCone, GeometricMeanCone, ExponentialCone, DualExponentialCone, PowerCone, DualPowerCone, diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index e5b669cd82..8c02c8968e 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -97,6 +97,7 @@ MOIU.@model(ModelNoZeroIndicator, (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval, MOI.Semicontinuous, MOI.Semiinteger), (MOI.Reals, MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, + MOI.NormInfinityCone, MOI.NormOneCone, MOI.SecondOrderCone, MOI.RotatedSecondOrderCone, MOI.GeometricMeanCone, MOI.ExponentialCone, MOI.DualExponentialCone, MOI.PositiveSemidefiniteConeTriangle, MOI.PositiveSemidefiniteConeSquare, diff --git a/test/Utilities/sets.jl b/test/Utilities/sets.jl index e2db3594fc..9397b7f59e 100644 --- a/test/Utilities/sets.jl +++ b/test/Utilities/sets.jl @@ -16,6 +16,8 @@ end @testset "Dimension" begin @test MOI.dimension(MOI.EqualTo(3.0)) === 1 @test MOI.dimension(MOI.Reals(8)) === 8 + @test MOI.dimension(MOI.NormInfinityCone(5)) === 5 + @test MOI.dimension(MOI.NormOneCone(5)) === 5 @test MOI.dimension(MOI.DualExponentialCone()) === 3 @test MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(4)) === 10 @test MOI.dimension(MOI.PositiveSemidefiniteConeSquare(5)) === 25 @@ -50,6 +52,15 @@ end @test MOI.dual_set(zeros4) == reals4 @test MOI.dual_set(reals4) == zeros4 @test MOI.dual_set(zeros4) != reals3 + # Norm-1 and norm-∞ cones + norminf2 = MOI.NormInfinityCone(2) + norminf3 = MOI.NormInfinityCone(3) + normone2 = MOI.NormOneCone(2) + normone3 = MOI.NormOneCone(3) + @test MOI.dual_set(norminf2) == normone2 + @test MOI.dual_set(normone2) == norminf2 + @test MOI.dual_set(norminf2) != normone3 + @test MOI.dual_set(normone2) != norminf3 #SOC soc2 = MOI.SecondOrderCone(2) soc3 = MOI.SecondOrderCone(3) @@ -118,7 +129,7 @@ end @test MOIU. set_dot(vec, vec, MOI.LogDetConeTriangle(3)) == 0 vec[5] = 1 @test MOIU.set_dot(vec, vec, MOI.LogDetConeTriangle(3)) == 1 - + sp_vec = spzeros(6) @test MOIU.set_dot(sp_vec, sp_vec, MOI.SecondOrderCone(6)) == 0 @test MOIU.set_dot(sp_vec, sp_vec, MOI.PositiveSemidefiniteConeTriangle(3)) == 0 From 5feb57ff9d6c092356941eed64da6e4059a8431c Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 7 Aug 2019 15:04:52 +0200 Subject: [PATCH 02/26] add news item --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index af07d3f785..13180690a3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ MathOptInterface (MOI) release notes v0.9.0 (May 2?, 2019) --------------------- +- L_1 and L_∞ norm epigraph cones were added (#818). - Support for Julia v0.6 and v0.7 was dropped (#714, #717). - A `MOI.Utilities.Model` implementation of `ModelLike`, this should replace most use cases of `MOI.Utilities.@model` (#781). From 0772963d0f4090b2292381b2f44d0a4b73cec87b Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 7 Aug 2019 15:13:45 +0200 Subject: [PATCH 03/26] fix incorrect name --- src/Test/contconic.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Test/contconic.jl b/src/Test/contconic.jl index f6ebf71bb1..1a4b19cb04 100644 --- a/src/Test/contconic.jl +++ b/src/Test/contconic.jl @@ -400,7 +400,7 @@ function norminf2test(model::MOI.ModelLike, config::TestConfig) @test MOI.supports(model, MOI.ObjectiveSense()) @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) - @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.NormInfCone) + @test MOI.supports_constraint(model, MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) MOI.empty!(model) @test MOI.is_empty(model) @@ -409,11 +409,11 @@ function norminf2test(model::MOI.ModelLike, config::TestConfig) MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, y))], [-2.0]), MOI.Nonnegatives(1)) MOI.add_constraint(model, MOI.VectorAffineFunction([MOI.VectorAffineTerm(1, MOI.ScalarAffineTerm(1.0, x))], [-1.0]), MOI.Nonpositives(1)) - MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.([1,2], MOI.ScalarAffineTerm.(1.0, [x,y])), zeros(2)), MOI.NormInfCone(2)) + MOI.add_constraint(model, MOI.VectorAffineFunction(MOI.VectorAffineTerm.([1,2], MOI.ScalarAffineTerm.(1.0, [x,y])), zeros(2)), MOI.NormInfinityCone(2)) @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.Nonnegatives}()) == 1 @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.Nonpositives}()) == 1 - @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.NormInfCone}()) == 1 + @test MOI.get(model, MOI.NumberOfConstraints{MOI.VectorAffineFunction{Float64},MOI.NormInfinityCone}()) == 1 if config.solve @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED From a9c1c6e8aa28579074758456beddf27438406cd6 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 7 Aug 2019 18:05:25 +0200 Subject: [PATCH 04/26] add new cones in bridge tests --- test/Bridges/bridge_optimizer.jl | 3 ++- test/Bridges/lazy_bridge_optimizer.jl | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/Bridges/bridge_optimizer.jl b/test/Bridges/bridge_optimizer.jl index a3f45e2b78..de377e47e1 100644 --- a/test/Bridges/bridge_optimizer.jl +++ b/test/Bridges/bridge_optimizer.jl @@ -12,7 +12,8 @@ include("utilities.jl") MOIU.@model(NoIntervalModel, (), (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval), - (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, MOI.SecondOrderCone, + (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, + MOI.NormInfinityCone, MOI.NormOneCone, MOI.SecondOrderCone, MOI.RotatedSecondOrderCone, MOI.GeometricMeanCone, MOI.PositiveSemidefiniteConeTriangle, MOI.ExponentialCone), (MOI.PowerCone, MOI.DualPowerCone), diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index 8c02c8968e..3271bfd542 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -13,6 +13,7 @@ MOIU.@model(NoRSOCModel, (), (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan), (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, MOI.SecondOrderCone, + MOI.NormInfinityCone, MOI.NormOneCone, MOI.ExponentialCone, MOI.PositiveSemidefiniteConeTriangle), (MOI.PowerCone,), (), @@ -56,6 +57,7 @@ MOIU.@model(ModelNoVAFinSOC, (), (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval), (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, MOI.SecondOrderCone, + MOI.NormInfinityCone, MOI.NormOneCone, MOI.RotatedSecondOrderCone, MOI.GeometricMeanCone, MOI.PositiveSemidefiniteConeTriangle, MOI.ExponentialCone), (MOI.PowerCone, MOI.DualPowerCone), From 930327a681d883a7e4111beeece16b435bcec0e3 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 8 Aug 2019 07:27:16 +0200 Subject: [PATCH 05/26] start on adding NormOne to LP bridge --- src/Bridges/Bridges.jl | 1 + src/Bridges/Constraint/Constraint.jl | 2 ++ src/Bridges/Constraint/norm_to_lp.jl | 52 +++++++++++++++++++++++++++ test/Bridges/Constraint/Constraint.jl | 1 + test/Bridges/Constraint/norm_to_lp.jl | 22 ++++++++++++ 5 files changed, 78 insertions(+) create mode 100644 src/Bridges/Constraint/norm_to_lp.jl create mode 100644 test/Bridges/Constraint/norm_to_lp.jl diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 8aa142e286..38a7d661a1 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -36,6 +36,7 @@ function full_bridge_optimizer(model::MOI.ModelLike, ::Type{T}) where T add_bridge(bridged_model, Constraint.VectorFunctionizeBridge{T}) add_bridge(bridged_model, Constraint.SplitIntervalBridge{T}) add_bridge(bridged_model, Constraint.QuadtoSOCBridge{T}) + add_bridge(bridged_model, Constraint.NormOneBridge{T}) add_bridge(bridged_model, Constraint.GeoMeanBridge{T}) add_bridge(bridged_model, Constraint.SquareBridge{T}) add_bridge(bridged_model, Constraint.LogDetBridge{T}) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index ce21266c83..1398c9600f 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -38,6 +38,8 @@ include("rsoc.jl") const RSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCBridge{T}, OT} include("quad_to_soc.jl") const QuadtoSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T}, OT} +include("norm_to_lp.jl") +const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT} include("geomean.jl") const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT} include("square.jl") diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl new file mode 100644 index 0000000000..c93fdb37f9 --- /dev/null +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -0,0 +1,52 @@ +""" + NormOneBridge{T} + +The `NormOneCone` is representable with LP constraints, since ``t \\ge \\sum_i |x_i|`` if and only if there exists a vector y such that ``t = \\sum_i y_i`` and ``y_i \\ge x_i`` and ``y_i \\ge -x_i`` for all ``i``. +""" +struct NormOneBridge{T} <: AbstractBridge + y::Vector{MOI.VariableIndex} + eq_index::CI{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}} + ge_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} +end +function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.VectorOfVariables, s::MOI.NormOneCone) where T + return bridge_constraint(NormOneBridge{T}, model, MOI.VectorAffineFunction{T}(f), s) +end +function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.VectorAffineFunction{T}, s::MOI.NormOneCone) where T + f_scalars = MOIU.eachscalar(f) + t = f_scalars[1] + x = f_scalars[2:end] + d = MOI.dimension(s) - 1 + y = MOI.add_variables(model, d) + eq_index = MOI.add_constraint(model, t - sum(y), MOI.EqualTo(zero(T))) # TODO fix + ge_index = MOI.add_constraint(model, MOIU.operate(vcat, T, y - x, y + x), MOI.Nonnegatives(2d)) # TODO fix + return NormOneBridge(y, eq_index, ge_index) +end + +MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = true +MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[] +MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] + +# Attributes, Bridge acting as a model +MOI.get(b::NormOneBridge, ::MOI.NumberOfVariables) = length(b.y) +MOI.get(b::NormOneBridge, ::MOI.ListOfVariableIndices) = b.y +MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}}) where T = 1 +MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = 1 +MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}}) where T = [b.eq_index] +MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = [b.ge_index] + +# References +function MOI.delete(model::MOI.ModelLike, c::NormOneBridge) + MOI.delete(model, c.ge_index) + MOI.delete(model, c.eq_index) + MOI.delete(model, c.y) +end + +# Attributes, Bridge acting as a constraint +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormOneBridge) + eq_value = MOI.get(model, MOI.ConstraintPrimal(), c.eq_index) + ge_value = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index) + t = eq_value + sum(ge_value) / 2 + d = length(c.y) + x = (ge_value[(d + 1):end] - ge_value[1:d]) / 2 + return vcat(t, x) +end diff --git a/test/Bridges/Constraint/Constraint.jl b/test/Bridges/Constraint/Constraint.jl index dbce4c1017..e24b3af367 100644 --- a/test/Bridges/Constraint/Constraint.jl +++ b/test/Bridges/Constraint/Constraint.jl @@ -10,6 +10,7 @@ include("functionize.jl") include("interval.jl") include("rsoc.jl") include("quad_to_soc.jl") +include("norm_to_lp.jl") include("geomean.jl") include("square.jl") include("det.jl") diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl new file mode 100644 index 0000000000..b3a34c661c --- /dev/null +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -0,0 +1,22 @@ +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.Model{Float64}()) +config = MOIT.TestConfig() + +@testset "NormOne" begin + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 0.5, 0.5, 0.5]) + bridged_mock = MOIB.Constraint.NormOne{Float64}(mock) + MOIT.normone1vtest(bridged_mock, config) + MOIT.normone1ftest(bridged_mock, config) + # Dual is not yet implemented for NormOne bridge + ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}())) + test_delete_bridge(bridged_mock, ci, 3, ((MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, 2), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) +end From 93ca1bd6d553b129843646e0eefc66a89a89dbd2 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 8 Aug 2019 07:48:44 +0200 Subject: [PATCH 06/26] start adding NormInfinityCone bridge --- src/Bridges/Bridges.jl | 1 + src/Bridges/Constraint/Constraint.jl | 1 + src/Bridges/Constraint/norm_to_lp.jl | 44 ++++++++++++++++++++++++++- test/Bridges/Constraint/norm_to_lp.jl | 10 ++++++ 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 38a7d661a1..002e806293 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -36,6 +36,7 @@ function full_bridge_optimizer(model::MOI.ModelLike, ::Type{T}) where T add_bridge(bridged_model, Constraint.VectorFunctionizeBridge{T}) add_bridge(bridged_model, Constraint.SplitIntervalBridge{T}) add_bridge(bridged_model, Constraint.QuadtoSOCBridge{T}) + add_bridge(bridged_model, Constraint.NormInfinityBridge{T}) add_bridge(bridged_model, Constraint.NormOneBridge{T}) add_bridge(bridged_model, Constraint.GeoMeanBridge{T}) add_bridge(bridged_model, Constraint.SquareBridge{T}) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 1398c9600f..720afa00ec 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -40,6 +40,7 @@ include("quad_to_soc.jl") const QuadtoSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T}, OT} include("norm_to_lp.jl") const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT} +const NormInfinity{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T}, OT} include("geomean.jl") const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT} include("square.jl") diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index c93fdb37f9..50ad753154 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -1,3 +1,45 @@ +""" + NormInfinityBridge{T} + +The `NormInfinityCone` is representable with LP constraints, since ``t \\ge \\max_i |x_i|`` if and only if ``t \\ge x_i`` and ``t \\ge -x_i`` for all ``i``. +""" +struct NormInfinityBridge{T} <: AbstractBridge + ge_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} +end +function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::MOI.VectorOfVariables, s::MOI.NormInfinityCone) where T + return bridge_constraint(NormInfinityBridge{T}, model, MOI.VectorAffineFunction{T}(f), s) +end +function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::MOI.VectorAffineFunction{T}, s::MOI.NormInfinityCone) where T + f_scalars = MOIU.eachscalar(f) + t = f_scalars[1] + x = f_scalars[2:end] + d = MOI.dimension(s) - 1 + ge_index = MOI.add_constraint(model, MOIU.operate(vcat, T, t - x, t + x), MOI.Nonnegatives(2d)) # TODO fix + return NormInfinityBridge(ge_index) +end + +MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = true +MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[] +MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorAffineFunction{T}, MOI.Nonnegatives),] + +# Attributes, Bridge acting as a model +MOI.get(b::NormInfinityBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = 1 +MOI.get(b::NormInfinityBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = [b.ge_index] + +# References +function MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) + MOI.delete(model, c.ge_index) +end + +# Attributes, Bridge acting as a constraint +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormInfinityBridge) + ge_value = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index) + t = sum(ge_value) / length(ge_value) + d = div(length(ge_value), 2) + x = (ge_value[(d + 1):end] - ge_value[1:d]) / 2 + return vcat(t, x) +end + """ NormOneBridge{T} @@ -18,7 +60,7 @@ function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.VectorAffineF d = MOI.dimension(s) - 1 y = MOI.add_variables(model, d) eq_index = MOI.add_constraint(model, t - sum(y), MOI.EqualTo(zero(T))) # TODO fix - ge_index = MOI.add_constraint(model, MOIU.operate(vcat, T, y - x, y + x), MOI.Nonnegatives(2d)) # TODO fix + ge_index = MOI.add_constraint(model, MOIU.operate(vcat, T, y - x, y + x), MOI.Nonnegatives(2d)) # TODO fix return NormOneBridge(y, eq_index, ge_index) end diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index b3a34c661c..21a847bcdf 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -11,6 +11,16 @@ include("../utilities.jl") mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) config = MOIT.TestConfig() +@testset "NormInfinity" begin + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 1.0]) + bridged_mock = MOIB.Constraint.NormInfinity{Float64}(mock) + MOIT.norminf1vtest(bridged_mock, config) + MOIT.norminf1ftest(bridged_mock, config) + # Dual is not yet implemented for NormInfinity bridge + ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone}())) + test_delete_bridge(bridged_mock, ci, 3, ((MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0),)) +end + @testset "NormOne" begin mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 0.5, 0.5, 0.5]) bridged_mock = MOIB.Constraint.NormOne{Float64}(mock) From 1bf304a53ae166084e5f1dede0bf5605e480525b Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 8 Aug 2019 07:58:51 +0200 Subject: [PATCH 07/26] update NEWS --- NEWS.md | 2 +- src/Bridges/Constraint/Constraint.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 13180690a3..15f779cfa8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,7 @@ MathOptInterface (MOI) release notes v0.9.0 (May 2?, 2019) --------------------- -- L_1 and L_∞ norm epigraph cones were added (#818). +- L_1 and L_∞ norm epigraph cones and corresponding bridges to LP were added (#818). - Support for Julia v0.6 and v0.7 was dropped (#714, #717). - A `MOI.Utilities.Model` implementation of `ModelLike`, this should replace most use cases of `MOI.Utilities.@model` (#781). diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 720afa00ec..0a7b3cb49e 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -39,8 +39,8 @@ const RSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCBridge{T}, OT} include("quad_to_soc.jl") const QuadtoSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T}, OT} include("norm_to_lp.jl") -const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT} const NormInfinity{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T}, OT} +const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT} include("geomean.jl") const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT} include("square.jl") From 5467abf0cef46b174068064c2af63697a837bae7 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 8 Aug 2019 10:06:34 +0200 Subject: [PATCH 08/26] fix bridge tests --- src/Bridges/Constraint/norm_to_lp.jl | 16 +++++++--------- test/Bridges/Constraint/norm_to_lp.jl | 4 ++-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 50ad753154..0a26f364b7 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -12,9 +12,9 @@ end function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::MOI.VectorAffineFunction{T}, s::MOI.NormInfinityCone) where T f_scalars = MOIU.eachscalar(f) t = f_scalars[1] - x = f_scalars[2:end] - d = MOI.dimension(s) - 1 - ge_index = MOI.add_constraint(model, MOIU.operate(vcat, T, t - x, t + x), MOI.Nonnegatives(2d)) # TODO fix + d = MOI.dimension(s) + new_f = MOIU.vectorize(vcat([MOIU.operate(-, T, t, f_scalars[i]) for i in 2:d], [MOIU.operate(+, T, t, f_scalars[i]) for i in 2:d])) + ge_index = MOI.add_constraint(model, new_f, MOI.Nonnegatives(2d - 2)) return NormInfinityBridge(ge_index) end @@ -55,12 +55,10 @@ function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.VectorOfVaria end function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.VectorAffineFunction{T}, s::MOI.NormOneCone) where T f_scalars = MOIU.eachscalar(f) - t = f_scalars[1] - x = f_scalars[2:end] - d = MOI.dimension(s) - 1 - y = MOI.add_variables(model, d) - eq_index = MOI.add_constraint(model, t - sum(y), MOI.EqualTo(zero(T))) # TODO fix - ge_index = MOI.add_constraint(model, MOIU.operate(vcat, T, y - x, y + x), MOI.Nonnegatives(2d)) # TODO fix + y = MOI.add_variables(model, MOI.dimension(s) - 1) + eq_index = MOI.add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)), MOI.EqualTo(zero(T))) + new_f = MOIU.vectorize(vcat([MOIU.operate(-, T, MOI.SingleVariable(y[i]), f_scalars[1 + i]) for i in eachindex(y)], [MOIU.operate(+, T, MOI.SingleVariable(y[i]), f_scalars[1 + i]) for i in eachindex(y)])) + ge_index = MOI.add_constraint(model, new_f, MOI.Nonnegatives(2 * MOI.dimension(s) - 2)) return NormOneBridge(y, eq_index, ge_index) end diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index 21a847bcdf..c1054f7bbe 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -9,7 +9,7 @@ const MOIB = MathOptInterface.Bridges include("../utilities.jl") mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) -config = MOIT.TestConfig() +config = MOIT.TestConfig(duals = false) @testset "NormInfinity" begin mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 1.0]) @@ -28,5 +28,5 @@ end MOIT.normone1ftest(bridged_mock, config) # Dual is not yet implemented for NormOne bridge ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}())) - test_delete_bridge(bridged_mock, ci, 3, ((MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, 2), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) + test_delete_bridge(bridged_mock, ci, 3, ((MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, 0), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) end From c1594b8d2051ad65f345f959c1af6eda101cf683 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 8 Aug 2019 16:13:43 +0200 Subject: [PATCH 09/26] address some comments --- src/Bridges/Constraint/norm_to_lp.jl | 44 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 0a26f364b7..3e81c7157b 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -6,21 +6,26 @@ The `NormInfinityCone` is representable with LP constraints, since ``t \\ge \\ma struct NormInfinityBridge{T} <: AbstractBridge ge_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::MOI.VectorOfVariables, s::MOI.NormInfinityCone) where T - return bridge_constraint(NormInfinityBridge{T}, model, MOI.VectorAffineFunction{T}(f), s) -end -function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::MOI.VectorAffineFunction{T}, s::MOI.NormInfinityCone) where T +function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where T f_scalars = MOIU.eachscalar(f) t = f_scalars[1] + if t isa MOI.SingleVariable # TODO currently needed for operate_output_index! + t = MOI.ScalarAffineFunction{T}(t) + end d = MOI.dimension(s) - new_f = MOIU.vectorize(vcat([MOIU.operate(-, T, t, f_scalars[i]) for i in 2:d], [MOIU.operate(+, T, t, f_scalars[i]) for i in 2:d])) - ge_index = MOI.add_constraint(model, new_f, MOI.Nonnegatives(2d - 2)) + lb = f_scalars[2:d] + ub = MOIU.operate(-, T, lb) + f_new = MOIU.operate(vcat, T, ub, lb) + for i in 1:MOI.output_dimension(f_new) + MOIU.operate_output_index!(+, T, i, f_new, t) + end + ge_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(MOI.output_dimension(f_new))) return NormInfinityBridge(ge_index) end -MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = true +MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorAffineFunction{T}, MOI.Nonnegatives),] +MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorAffineFunction{T}, MOI.Nonnegatives),] # Attributes, Bridge acting as a model MOI.get(b::NormInfinityBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = 1 @@ -50,21 +55,26 @@ struct NormOneBridge{T} <: AbstractBridge eq_index::CI{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}} ge_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.VectorOfVariables, s::MOI.NormOneCone) where T - return bridge_constraint(NormOneBridge{T}, model, MOI.VectorAffineFunction{T}(f), s) -end -function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.VectorAffineFunction{T}, s::MOI.NormOneCone) where T +function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where T + if f isa MOI.VectorOfVariables # TODO currently needed for test to pass + f = MOI.VectorAffineFunction{T}(f) + end f_scalars = MOIU.eachscalar(f) - y = MOI.add_variables(model, MOI.dimension(s) - 1) + d = MOI.dimension(s) + y = MOI.add_variables(model, d - 1) eq_index = MOI.add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)), MOI.EqualTo(zero(T))) - new_f = MOIU.vectorize(vcat([MOIU.operate(-, T, MOI.SingleVariable(y[i]), f_scalars[1 + i]) for i in eachindex(y)], [MOIU.operate(+, T, MOI.SingleVariable(y[i]), f_scalars[1 + i]) for i in eachindex(y)])) - ge_index = MOI.add_constraint(model, new_f, MOI.Nonnegatives(2 * MOI.dimension(s) - 2)) + lb = f_scalars[2:d] + ub = MOIU.operate(-, T, lb) + MOIU.operate!(+, T, lb, MOI.VectorOfVariables(y)) + MOIU.operate!(+, T, ub, MOI.VectorOfVariables(y)) + f_new = MOIU.operate(vcat, T, ub, lb) + ge_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(2d - 2)) return NormOneBridge(y, eq_index, ge_index) end -MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = true +MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] +MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] # Attributes, Bridge acting as a model MOI.get(b::NormOneBridge, ::MOI.NumberOfVariables) = length(b.y) From 1e7316274cb1987082fa8f109b2a886da66cb3e6 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 9 Aug 2019 09:59:21 +0200 Subject: [PATCH 10/26] disallow VQF; start on duals thru new bridges --- src/Bridges/Constraint/norm_to_lp.jl | 28 ++++-- test/Bridges/Constraint/norm_to_lp.jl | 119 ++++++++++++++++++++++++-- 2 files changed, 136 insertions(+), 11 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 3e81c7157b..a8383a7fc0 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -6,7 +6,7 @@ The `NormInfinityCone` is representable with LP constraints, since ``t \\ge \\ma struct NormInfinityBridge{T} <: AbstractBridge ge_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where T +function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, s::MOI.NormInfinityCone) where T f_scalars = MOIU.eachscalar(f) t = f_scalars[1] if t isa MOI.SingleVariable # TODO currently needed for operate_output_index! @@ -23,9 +23,9 @@ function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::MOI.Abstract return NormInfinityBridge(ge_index) end -MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = true +MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorAffineFunction{T}, MOI.Nonnegatives),] +MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorAffineFunction{T}, MOI.Nonnegatives),] # Attributes, Bridge acting as a model MOI.get(b::NormInfinityBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = 1 @@ -44,6 +44,14 @@ function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormInfinityBr x = (ge_value[(d + 1):end] - ge_value[1:d]) / 2 return vcat(t, x) end +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormInfinityBridge) + ge_dual = MOI.get(model, MOI.ConstraintDual(), c.ge_index) + t = sum(abs, ge_dual) + d = div(length(ge_dual), 2) + x = (ge_dual[(d + 1):end] - ge_dual[1:d]) + return vcat(t, x) +end + """ NormOneBridge{T} @@ -55,7 +63,7 @@ struct NormOneBridge{T} <: AbstractBridge eq_index::CI{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}} ge_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where T +function bridge_constraint(::Type{NormOneBridge{T}}, model, f::Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, s::MOI.NormOneCone) where T if f isa MOI.VectorOfVariables # TODO currently needed for test to pass f = MOI.VectorAffineFunction{T}(f) end @@ -72,9 +80,9 @@ function bridge_constraint(::Type{NormOneBridge{T}}, model, f::MOI.AbstractVecto return NormOneBridge(y, eq_index, ge_index) end -MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = true +MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] +MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] # Attributes, Bridge acting as a model MOI.get(b::NormOneBridge, ::MOI.NumberOfVariables) = length(b.y) @@ -100,3 +108,11 @@ function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormOneBridge) x = (ge_value[(d + 1):end] - ge_value[1:d]) / 2 return vcat(t, x) end +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormOneBridge) + eq_dual = MOI.get(model, MOI.ConstraintDual(), c.eq_index) + ge_dual = MOI.get(model, MOI.ConstraintDual(), c.ge_index) + t = eq_dual + d = div(length(ge_dual), 2) + x = (ge_dual[(d + 1):end] - ge_dual[1:d]) + return vcat(t, x) +end diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index c1054f7bbe..d9c36566f7 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -9,24 +9,133 @@ const MOIB = MathOptInterface.Bridges include("../utilities.jl") mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) -config = MOIT.TestConfig(duals = false) +config = MOIT.TestConfig() @testset "NormInfinity" begin - mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 1.0]) bridged_mock = MOIB.Constraint.NormInfinity{Float64}(mock) + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 1.0], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0.0, 1.0, 0.0, 0.0]]) MOIT.norminf1vtest(bridged_mock, config) MOIT.norminf1ftest(bridged_mock, config) - # Dual is not yet implemented for NormInfinity bridge ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone}())) test_delete_bridge(bridged_mock, ci, 3, ((MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0),)) end @testset "NormOne" begin - mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 0.5, 0.5, 0.5]) bridged_mock = MOIB.Constraint.NormOne{Float64}(mock) + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 0.5, 0.5, 0.5], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[1.0, 1.0, 0.0, 0.0]], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [[1.0]], + ) MOIT.normone1vtest(bridged_mock, config) MOIT.normone1ftest(bridged_mock, config) - # Dual is not yet implemented for NormOne bridge ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}())) test_delete_bridge(bridged_mock, ci, 3, ((MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, 0), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) end + +# TODO adapt for these cones +# +# 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.Model{Float64}()) +# config = MOIT.TestConfig() +# +# bridged_mock = MOIB.Variable.RSOCtoPSD{Float64}(mock) +# +# @testset "RSOC4" begin +# mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0, 2.0, 1.0, 0.0, 2.0], +# (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [0.25], +# (MOI.SingleVariable, MOI.EqualTo{Float64}) => [-0.5], +# (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], +# (MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle) => [[1.0, -0.5, 0.25, -0.5, 0.25, 0.25]]) +# mock.eval_variable_constraint_dual = false +# MOIT.rotatedsoc4test(bridged_mock, config) +# mock.eval_variable_constraint_dual = true +# +# @testset "Test mock model" begin +# var_names = ["Q$i$j" for j in 1:3 for i in 1:j] +# MOI.set( +# mock, MOI.VariableName(), +# MOI.get(mock, MOI.ListOfVariableIndices()), var_names) +# psd = MOI.get(mock, MOI.ListOfConstraintIndices{ +# MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle}()) +# @test length(psd) == 1 +# MOI.set(mock, MOI.ConstraintName(), psd[1], "psd") +# off_diag = MOI.get(mock, MOI.ListOfConstraintIndices{ +# MOI.SingleVariable, MOI.EqualTo{Float64}}()) +# @test length(off_diag) == 1 +# MOI.set(mock, MOI.ConstraintName(), off_diag[1], "off_diag23") +# diag = MOI.get(mock, MOI.ListOfConstraintIndices{ +# MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}}()) +# @test length(diag) == 1 +# MOI.set(mock, MOI.ConstraintName(), diag[1], "diag33") +# c = MOI.get(mock, MOI.ListOfConstraintIndices{ +# MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}}()) +# @test length(c) == 1 +# MOI.set(mock, MOI.ConstraintName(), c[1], "c") +# +# s = """ +# variables: Q11, Q12, Q13, Q22, Q23, Q33 +# psd: [Q11, Q12, Q22, Q13, Q23, Q33] in MathOptInterface.PositiveSemidefiniteConeTriangle(3) +# off_diag23: Q23 == 0.0 +# diag33: Q22 + -1.0Q33 == 0.0 +# c: Q11 + 0.5Q22 <= 2.0 +# maxobjective: Q12 + Q13 +# """ +# model = MOIU.Model{Float64}() +# MOIU.loadfromstring!(model, s) +# MOIU.test_models_equal(mock, model, var_names, ["psd", "off_diag23", "diag33", "c"]) +# end +# +# @testset "Test bridged model" begin +# var_names = ["t", "u", "x", "y"] +# MOI.set( +# bridged_mock, MOI.VariableName(), +# MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names) +# rsoc = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{ +# MOI.VectorOfVariables, MOI.RotatedSecondOrderCone}()) +# @test length(rsoc) == 1 +# MOI.set(bridged_mock, MOI.ConstraintName(), rsoc[1], "rsoc") +# c = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{ +# MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}}()) +# @test length(c) == 1 +# MOI.set(bridged_mock, MOI.ConstraintName(), c[1], "c") +# +# s = """ +# variables: t, u, x, y +# rsoc: [t, u, x, y] in MathOptInterface.RotatedSecondOrderCone(4) +# c: t + u <= 2.0 +# maxobjective: x + y +# """ +# model = MOIU.Model{Float64}() +# MOIU.loadfromstring!(model, s) +# MOIU.test_models_equal(bridged_mock, model, var_names, ["rsoc", "c"]) +# end +# +# +# @testset "Delete" begin +# v = MOI.get(bridged_mock, MOI.ListOfVariableIndices()) +# @test length(v) == 4 +# +# message = string("Cannot delete variable as it is constrained with other", +# " variables in a `MOI.VectorOfVariables`.") +# for i in 1:4 +# err = MOI.DeleteNotAllowed(v[i], message) +# @test_throws err MOI.delete(bridged_mock, v[i]) +# end +# +# test_delete_bridged_variables(bridged_mock, v, MOI.RotatedSecondOrderCone, 4, ( +# (MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle, 0), +# (MOI.SingleVariable, MOI.EqualTo{Float64}, 0), +# (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, 0), +# )) +# end +# end From ca192876777565d31d68d994ef68f398942162ff Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 9 Aug 2019 15:27:44 +0200 Subject: [PATCH 11/26] get dual bridge tests working --- src/Bridges/Constraint/norm_to_lp.jl | 60 ++++++++++++++------------- test/Bridges/Constraint/norm_to_lp.jl | 29 +++++-------- 2 files changed, 42 insertions(+), 47 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index a8383a7fc0..4ce30cbf92 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -4,7 +4,7 @@ The `NormInfinityCone` is representable with LP constraints, since ``t \\ge \\max_i |x_i|`` if and only if ``t \\ge x_i`` and ``t \\ge -x_i`` for all ``i``. """ struct NormInfinityBridge{T} <: AbstractBridge - ge_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} + nn_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} end function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, s::MOI.NormInfinityCone) where T f_scalars = MOIU.eachscalar(f) @@ -19,8 +19,8 @@ function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::Union{MOI.Ve for i in 1:MOI.output_dimension(f_new) MOIU.operate_output_index!(+, T, i, f_new, t) end - ge_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(MOI.output_dimension(f_new))) - return NormInfinityBridge(ge_index) + nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(MOI.output_dimension(f_new))) + return NormInfinityBridge(nn_index) end MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = true @@ -29,39 +29,41 @@ MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.Ve # Attributes, Bridge acting as a model MOI.get(b::NormInfinityBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = 1 -MOI.get(b::NormInfinityBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = [b.ge_index] +MOI.get(b::NormInfinityBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = [b.nn_index] # References function MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) - MOI.delete(model, c.ge_index) + MOI.delete(model, c.nn_index) end # Attributes, Bridge acting as a constraint function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormInfinityBridge) - ge_value = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index) + ge_value = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index) t = sum(ge_value) / length(ge_value) d = div(length(ge_value), 2) x = (ge_value[(d + 1):end] - ge_value[1:d]) / 2 return vcat(t, x) end +# Given a_i is dual on t - x_i >= 0 and b_i is dual on t + x_i >= 0, +# the dual on (t, x) in NormInfinityCone is (u, v) in NormOneCone, where +# v_i = -a_i + b_i and u = sum(a) + sum(b). function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormInfinityBridge) - ge_dual = MOI.get(model, MOI.ConstraintDual(), c.ge_index) - t = sum(abs, ge_dual) + ge_dual = MOI.get(model, MOI.ConstraintDual(), c.nn_index) + t = sum(ge_dual) d = div(length(ge_dual), 2) x = (ge_dual[(d + 1):end] - ge_dual[1:d]) return vcat(t, x) end - """ NormOneBridge{T} -The `NormOneCone` is representable with LP constraints, since ``t \\ge \\sum_i |x_i|`` if and only if there exists a vector y such that ``t = \\sum_i y_i`` and ``y_i \\ge x_i`` and ``y_i \\ge -x_i`` for all ``i``. +The `NormOneCone` is representable with LP constraints, since ``t \\ge \\sum_i |x_i|`` if and only if there exists a vector y such that ``t \\ge \\sum_i y_i`` and ``y_i \\ge x_i`` and ``y_i \\ge -x_i`` for all ``i``. """ struct NormOneBridge{T} <: AbstractBridge y::Vector{MOI.VariableIndex} - eq_index::CI{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}} - ge_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} + ge_index::CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}} + nn_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} end function bridge_constraint(::Type{NormOneBridge{T}}, model, f::Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, s::MOI.NormOneCone) where T if f isa MOI.VectorOfVariables # TODO currently needed for test to pass @@ -70,49 +72,51 @@ function bridge_constraint(::Type{NormOneBridge{T}}, model, f::Union{MOI.VectorO f_scalars = MOIU.eachscalar(f) d = MOI.dimension(s) y = MOI.add_variables(model, d - 1) - eq_index = MOI.add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)), MOI.EqualTo(zero(T))) + ge_index = MOI.add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)), MOI.GreaterThan(zero(T))) lb = f_scalars[2:d] ub = MOIU.operate(-, T, lb) MOIU.operate!(+, T, lb, MOI.VectorOfVariables(y)) MOIU.operate!(+, T, ub, MOI.VectorOfVariables(y)) f_new = MOIU.operate(vcat, T, ub, lb) - ge_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(2d - 2)) - return NormOneBridge(y, eq_index, ge_index) + nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(2d - 2)) + return NormOneBridge(y, ge_index, nn_index) end MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] +MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] # Attributes, Bridge acting as a model MOI.get(b::NormOneBridge, ::MOI.NumberOfVariables) = length(b.y) MOI.get(b::NormOneBridge, ::MOI.ListOfVariableIndices) = b.y -MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}}) where T = 1 +MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where T = 1 MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = 1 -MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}}) where T = [b.eq_index] -MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = [b.ge_index] +MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where T = [b.ge_index] +MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = [b.nn_index] # References function MOI.delete(model::MOI.ModelLike, c::NormOneBridge) + MOI.delete(model, c.nn_index) MOI.delete(model, c.ge_index) - MOI.delete(model, c.eq_index) MOI.delete(model, c.y) end # Attributes, Bridge acting as a constraint function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormOneBridge) - eq_value = MOI.get(model, MOI.ConstraintPrimal(), c.eq_index) ge_value = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index) - t = eq_value + sum(ge_value) / 2 + nn_value = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index) + t = ge_value + sum(nn_value) / 2 d = length(c.y) - x = (ge_value[(d + 1):end] - ge_value[1:d]) / 2 + x = (nn_value[(d + 1):end] - nn_value[1:d]) / 2 return vcat(t, x) end +# Given a_i is dual on y_i - x_i >= 0 and b_i is dual on y_i + x_i >= 0 and c is dual on t - sum(y) >= 0, +# the dual on (t, x) in NormOneCone is (u, v) in NormInfinityCone, where +# v_i = -a_i + b_i and u = c. function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormOneBridge) - eq_dual = MOI.get(model, MOI.ConstraintDual(), c.eq_index) - ge_dual = MOI.get(model, MOI.ConstraintDual(), c.ge_index) - t = eq_dual - d = div(length(ge_dual), 2) - x = (ge_dual[(d + 1):end] - ge_dual[1:d]) + t = MOI.get(model, MOI.ConstraintDual(), c.ge_index) + nn_dual = MOI.get(model, MOI.ConstraintDual(), c.nn_index) + d = div(length(nn_dual), 2) + x = (nn_dual[(d + 1):end] - nn_dual[1:d]) return vcat(t, x) end diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index d9c36566f7..9d27387d5d 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -13,8 +13,10 @@ config = MOIT.TestConfig() @testset "NormInfinity" begin bridged_mock = MOIB.Constraint.NormInfinity{Float64}(mock) + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 1.0], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0.0, 1.0, 0.0, 0.0]]) + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0.0, 1.0, 0.0, 0.0]], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]]) MOIT.norminf1vtest(bridged_mock, config) MOIT.norminf1ftest(bridged_mock, config) ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone}())) @@ -25,30 +27,19 @@ end bridged_mock = MOIB.Constraint.NormOne{Float64}(mock) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 0.5, 0.5, 0.5], (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[1.0, 1.0, 0.0, 0.0]], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [[1.0]], - ) + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [[1.0]], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [0]]) MOIT.normone1vtest(bridged_mock, config) MOIT.normone1ftest(bridged_mock, config) ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}())) - test_delete_bridge(bridged_mock, ci, 3, ((MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, 0), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) + test_delete_bridge(bridged_mock, ci, 3, ( + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0), + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) end -# TODO adapt for these cones -# -# 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.Model{Float64}()) -# config = MOIT.TestConfig() + # -# bridged_mock = MOIB.Variable.RSOCtoPSD{Float64}(mock) +# # TODO adapt for these cones # # @testset "RSOC4" begin # mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0, 2.0, 1.0, 0.0, 2.0], From 58b33eca9daf196eb311d00f40ab25a035a7c168 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 10 Aug 2019 08:38:40 -0400 Subject: [PATCH 12/26] cleanup and support VQF --- src/Bridges/Constraint/norm_to_lp.jl | 28 ++++---- test/Bridges/Constraint/norm_to_lp.jl | 95 --------------------------- 2 files changed, 15 insertions(+), 108 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 4ce30cbf92..95b71e9f92 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -4,9 +4,9 @@ The `NormInfinityCone` is representable with LP constraints, since ``t \\ge \\max_i |x_i|`` if and only if ``t \\ge x_i`` and ``t \\ge -x_i`` for all ``i``. """ struct NormInfinityBridge{T} <: AbstractBridge - nn_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} + nn_index::CI{<:Union{MOI.VectorAffineFunction{T}, MOI.VectorQuadraticFunction{T}}, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, s::MOI.NormInfinityCone) where T +function bridge_constraint(::Type{NormInfinityBridge{T}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where T f_scalars = MOIU.eachscalar(f) t = f_scalars[1] if t isa MOI.SingleVariable # TODO currently needed for operate_output_index! @@ -23,13 +23,14 @@ function bridge_constraint(::Type{NormInfinityBridge{T}}, model, f::Union{MOI.Ve return NormInfinityBridge(nn_index) end -MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = true +MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[] MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorAffineFunction{T}, MOI.Nonnegatives),] +MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.VectorQuadraticFunction{T}}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorQuadraticFunction{T}, MOI.Nonnegatives),] # Attributes, Bridge acting as a model -MOI.get(b::NormInfinityBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = 1 -MOI.get(b::NormInfinityBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = [b.nn_index] +MOI.get(b::NormInfinityBridge{T}, ::MOI.NumberOfConstraints{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, MOI.Nonnegatives}) where T = 1 +MOI.get(b::NormInfinityBridge{T}, ::MOI.ListOfConstraintIndices{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, MOI.Nonnegatives}) where T = [b.nn_index] # References function MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) @@ -62,10 +63,10 @@ The `NormOneCone` is representable with LP constraints, since ``t \\ge \\sum_i | """ struct NormOneBridge{T} <: AbstractBridge y::Vector{MOI.VariableIndex} - ge_index::CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}} - nn_index::CI{MOI.VectorAffineFunction{T}, MOI.Nonnegatives} + ge_index::CI{<:Union{MOI.ScalarAffineFunction{T}, MOI.ScalarQuadraticFunction{T}}, MOI.GreaterThan{T}} + nn_index::CI{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormOneBridge{T}}, model, f::Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, s::MOI.NormOneCone) where T +function bridge_constraint(::Type{NormOneBridge{T}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where T if f isa MOI.VectorOfVariables # TODO currently needed for test to pass f = MOI.VectorAffineFunction{T}(f) end @@ -82,17 +83,18 @@ function bridge_constraint(::Type{NormOneBridge{T}}, model, f::Union{MOI.VectorO return NormOneBridge(y, ge_index, nn_index) end -MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = true +MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[] MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] +MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:MOI.VectorQuadraticFunction{T}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarQuadraticFunction{T}, MOI.GreaterThan{T}), (MOI.VectorQuadraticFunction{T}, MOI.Nonnegatives)] # Attributes, Bridge acting as a model MOI.get(b::NormOneBridge, ::MOI.NumberOfVariables) = length(b.y) MOI.get(b::NormOneBridge, ::MOI.ListOfVariableIndices) = b.y -MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where T = 1 -MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = 1 -MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}) where T = [b.ge_index] -MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, MOI.Nonnegatives}) where T = [b.nn_index] +MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{<:Union{MOI.ScalarAffineFunction{T}, MOI.ScalarQuadraticFunction{T}}, MOI.GreaterThan{T}}) where T = 1 +MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{<:Union{MOI.VectorAffineFunction{T}, MOI.VectorQuadraticFunction{T}}, MOI.Nonnegatives}) where T = 1 +MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{<:Union{MOI.ScalarAffineFunction{T}, MOI.ScalarQuadraticFunction{T}}, MOI.GreaterThan{T}}) where T = [b.ge_index] +MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{<:Union{MOI.VectorAffineFunction{T}, MOI.VectorQuadraticFunction{T}}, MOI.Nonnegatives}) where T = [b.nn_index] # References function MOI.delete(model::MOI.ModelLike, c::NormOneBridge) diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index 9d27387d5d..4fddcd2cec 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -13,7 +13,6 @@ config = MOIT.TestConfig() @testset "NormInfinity" begin bridged_mock = MOIB.Constraint.NormInfinity{Float64}(mock) - mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 1.0], (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0.0, 1.0, 0.0, 0.0]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]]) @@ -36,97 +35,3 @@ end (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) end - - -# -# # TODO adapt for these cones -# -# @testset "RSOC4" begin -# mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0, 2.0, 1.0, 0.0, 2.0], -# (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [0.25], -# (MOI.SingleVariable, MOI.EqualTo{Float64}) => [-0.5], -# (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], -# (MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle) => [[1.0, -0.5, 0.25, -0.5, 0.25, 0.25]]) -# mock.eval_variable_constraint_dual = false -# MOIT.rotatedsoc4test(bridged_mock, config) -# mock.eval_variable_constraint_dual = true -# -# @testset "Test mock model" begin -# var_names = ["Q$i$j" for j in 1:3 for i in 1:j] -# MOI.set( -# mock, MOI.VariableName(), -# MOI.get(mock, MOI.ListOfVariableIndices()), var_names) -# psd = MOI.get(mock, MOI.ListOfConstraintIndices{ -# MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle}()) -# @test length(psd) == 1 -# MOI.set(mock, MOI.ConstraintName(), psd[1], "psd") -# off_diag = MOI.get(mock, MOI.ListOfConstraintIndices{ -# MOI.SingleVariable, MOI.EqualTo{Float64}}()) -# @test length(off_diag) == 1 -# MOI.set(mock, MOI.ConstraintName(), off_diag[1], "off_diag23") -# diag = MOI.get(mock, MOI.ListOfConstraintIndices{ -# MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}}()) -# @test length(diag) == 1 -# MOI.set(mock, MOI.ConstraintName(), diag[1], "diag33") -# c = MOI.get(mock, MOI.ListOfConstraintIndices{ -# MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}}()) -# @test length(c) == 1 -# MOI.set(mock, MOI.ConstraintName(), c[1], "c") -# -# s = """ -# variables: Q11, Q12, Q13, Q22, Q23, Q33 -# psd: [Q11, Q12, Q22, Q13, Q23, Q33] in MathOptInterface.PositiveSemidefiniteConeTriangle(3) -# off_diag23: Q23 == 0.0 -# diag33: Q22 + -1.0Q33 == 0.0 -# c: Q11 + 0.5Q22 <= 2.0 -# maxobjective: Q12 + Q13 -# """ -# model = MOIU.Model{Float64}() -# MOIU.loadfromstring!(model, s) -# MOIU.test_models_equal(mock, model, var_names, ["psd", "off_diag23", "diag33", "c"]) -# end -# -# @testset "Test bridged model" begin -# var_names = ["t", "u", "x", "y"] -# MOI.set( -# bridged_mock, MOI.VariableName(), -# MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names) -# rsoc = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{ -# MOI.VectorOfVariables, MOI.RotatedSecondOrderCone}()) -# @test length(rsoc) == 1 -# MOI.set(bridged_mock, MOI.ConstraintName(), rsoc[1], "rsoc") -# c = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{ -# MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}}()) -# @test length(c) == 1 -# MOI.set(bridged_mock, MOI.ConstraintName(), c[1], "c") -# -# s = """ -# variables: t, u, x, y -# rsoc: [t, u, x, y] in MathOptInterface.RotatedSecondOrderCone(4) -# c: t + u <= 2.0 -# maxobjective: x + y -# """ -# model = MOIU.Model{Float64}() -# MOIU.loadfromstring!(model, s) -# MOIU.test_models_equal(bridged_mock, model, var_names, ["rsoc", "c"]) -# end -# -# -# @testset "Delete" begin -# v = MOI.get(bridged_mock, MOI.ListOfVariableIndices()) -# @test length(v) == 4 -# -# message = string("Cannot delete variable as it is constrained with other", -# " variables in a `MOI.VectorOfVariables`.") -# for i in 1:4 -# err = MOI.DeleteNotAllowed(v[i], message) -# @test_throws err MOI.delete(bridged_mock, v[i]) -# end -# -# test_delete_bridged_variables(bridged_mock, v, MOI.RotatedSecondOrderCone, 4, ( -# (MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle, 0), -# (MOI.SingleVariable, MOI.EqualTo{Float64}, 0), -# (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, 0), -# )) -# end -# end From 09ed86acd659b0e52daf870584bc68c1ee9f3bdd Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 10 Aug 2019 11:25:24 -0400 Subject: [PATCH 13/26] parametrize new bridges with function types --- src/Bridges/Constraint/norm_to_lp.jl | 61 +++++++++++++++++---------- test/Bridges/Constraint/norm_to_lp.jl | 4 +- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 95b71e9f92..5c8310e078 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -1,10 +1,12 @@ """ NormInfinityBridge{T} -The `NormInfinityCone` is representable with LP constraints, since ``t \\ge \\max_i |x_i|`` if and only if ``t \\ge x_i`` and ``t \\ge -x_i`` for all ``i``. +The `NormInfinityCone` is representable with LP constraints, since +``t \\ge \\max_i |x_i|`` if and only if +``t \\ge x_i`` and ``t \\ge -x_i`` for all ``i``. """ -struct NormInfinityBridge{T} <: AbstractBridge - nn_index::CI{<:Union{MOI.VectorAffineFunction{T}, MOI.VectorQuadraticFunction{T}}, MOI.Nonnegatives} +struct NormInfinityBridge{T, F} <: AbstractBridge + nn_index::CI{F, MOI.Nonnegatives} end function bridge_constraint(::Type{NormInfinityBridge{T}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where T f_scalars = MOIU.eachscalar(f) @@ -23,19 +25,23 @@ function bridge_constraint(::Type{NormInfinityBridge{T}}, model::MOI.ModelLike, return NormInfinityBridge(nn_index) end -MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = true +MOI.supports_constraint(::Type{NormInfinityBridge}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) = true MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorAffineFunction{T}, MOI.Nonnegatives),] -MOIB.added_constraint_types(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.VectorQuadraticFunction{T}}, ::Type{MOI.NormInfinityCone}) where T = [(MOI.VectorQuadraticFunction{T}, MOI.Nonnegatives),] +MOIB.added_constraint_types(::Type{NormInfinityBridge{T, F}}) where {T, F} = [(F, MOI.Nonnegatives)] +function concrete_bridge_type(::Type{<:NormInfinityBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T + G1 = MOIU.promote_operation(-, T, H) + S = MOIU.scalar_type(H) + G2 = MOIU.promote_operation(+, T, S, H) + F = MOIU.promote_operation(vcat, T, G1, G2) + return NormInfinityBridge{T, F} +end # Attributes, Bridge acting as a model -MOI.get(b::NormInfinityBridge{T}, ::MOI.NumberOfConstraints{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, MOI.Nonnegatives}) where T = 1 -MOI.get(b::NormInfinityBridge{T}, ::MOI.ListOfConstraintIndices{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, MOI.Nonnegatives}) where T = [b.nn_index] +MOI.get(b::NormInfinityBridge{T, F}, ::MOI.NumberOfConstraints{F, MOI.Nonnegatives}) where {T, F} = 1 +MOI.get(b::NormInfinityBridge{T, F}, ::MOI.ListOfConstraintIndices{F, MOI.Nonnegatives}) where {T, F} = [b.nn_index] # References -function MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) - MOI.delete(model, c.nn_index) -end +MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) = MOI.delete(model, c.nn_index) # Attributes, Bridge acting as a constraint function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormInfinityBridge) @@ -59,12 +65,14 @@ end """ NormOneBridge{T} -The `NormOneCone` is representable with LP constraints, since ``t \\ge \\sum_i |x_i|`` if and only if there exists a vector y such that ``t \\ge \\sum_i y_i`` and ``y_i \\ge x_i`` and ``y_i \\ge -x_i`` for all ``i``. +The `NormOneCone` is representable with LP constraints, since +``t \\ge \\sum_i |x_i|`` if and only if there exists a vector y such that +``t \\ge \\sum_i y_i`` and ``y_i \\ge x_i``, ``y_i \\ge -x_i`` for all ``i``. """ -struct NormOneBridge{T} <: AbstractBridge +struct NormOneBridge{T, F, G} <: AbstractBridge y::Vector{MOI.VariableIndex} - ge_index::CI{<:Union{MOI.ScalarAffineFunction{T}, MOI.ScalarQuadraticFunction{T}}, MOI.GreaterThan{T}} - nn_index::CI{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}, MOI.Nonnegatives} + ge_index::CI{F, MOI.GreaterThan{T}} + nn_index::CI{G, MOI.Nonnegatives} end function bridge_constraint(::Type{NormOneBridge{T}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where T if f isa MOI.VectorOfVariables # TODO currently needed for test to pass @@ -83,18 +91,27 @@ function bridge_constraint(::Type{NormOneBridge{T}}, model::MOI.ModelLike, f::MO return NormOneBridge(y, ge_index, nn_index) end -MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = true +MOI.supports_constraint(::Type{NormOneBridge}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) = true MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), (MOI.VectorAffineFunction{T}, MOI.Nonnegatives)] -MOIB.added_constraint_types(::Type{NormOneBridge{T}}, ::Type{<:MOI.VectorQuadraticFunction{T}}, ::Type{MOI.NormOneCone}) where T = [(MOI.ScalarQuadraticFunction{T}, MOI.GreaterThan{T}), (MOI.VectorQuadraticFunction{T}, MOI.Nonnegatives)] +MOIB.added_constraint_types(::Type{NormOneBridge{T, F, G}}) where {T, F, G} = [(F, MOI.GreaterThan{T}), (G, MOI.Nonnegatives)] +function concrete_bridge_type(::Type{<:NormOneBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T + S = MOIU.scalar_type(H) + F1 = MOIU.promote_operation(+, T, MOI.SingleVariable, MOI.SingleVariable) + F = MOIU.promote_operation(-, T, S, F1) + G1 = MOIU.promote_operation(-, T, H) + G2 = MOIU.promote_operation(+, T, G1, MOI.VectorOfVariables) + G3 = MOIU.promote_operation(+, T, H, MOI.VectorOfVariables) + G = MOIU.promote_operation(vcat, T, G2, G3) + return NormOneBridge{T, F, G} +end # Attributes, Bridge acting as a model MOI.get(b::NormOneBridge, ::MOI.NumberOfVariables) = length(b.y) MOI.get(b::NormOneBridge, ::MOI.ListOfVariableIndices) = b.y -MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{<:Union{MOI.ScalarAffineFunction{T}, MOI.ScalarQuadraticFunction{T}}, MOI.GreaterThan{T}}) where T = 1 -MOI.get(b::NormOneBridge{T}, ::MOI.NumberOfConstraints{<:Union{MOI.VectorAffineFunction{T}, MOI.VectorQuadraticFunction{T}}, MOI.Nonnegatives}) where T = 1 -MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{<:Union{MOI.ScalarAffineFunction{T}, MOI.ScalarQuadraticFunction{T}}, MOI.GreaterThan{T}}) where T = [b.ge_index] -MOI.get(b::NormOneBridge{T}, ::MOI.ListOfConstraintIndices{<:Union{MOI.VectorAffineFunction{T}, MOI.VectorQuadraticFunction{T}}, MOI.Nonnegatives}) where T = [b.nn_index] +MOI.get(b::NormOneBridge{T, F, G}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F, G} = 1 +MOI.get(b::NormOneBridge{T, F, G}, ::MOI.NumberOfConstraints{G, MOI.Nonnegatives}) where {T, F, G} = 1 +MOI.get(b::NormOneBridge{T, F, G}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F, G} = [b.ge_index] +MOI.get(b::NormOneBridge{T, F, G}, ::MOI.ListOfConstraintIndices{G, MOI.Nonnegatives}) where {T, F, G} = [b.nn_index] # References function MOI.delete(model::MOI.ModelLike, c::NormOneBridge) diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index 4fddcd2cec..958fafd444 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -12,10 +12,10 @@ mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) config = MOIT.TestConfig() @testset "NormInfinity" begin - bridged_mock = MOIB.Constraint.NormInfinity{Float64}(mock) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 1.0], (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0.0, 1.0, 0.0, 0.0]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]]) + bridged_mock = MOIB.Constraint.NormInfinity{Float64}(mock) MOIT.norminf1vtest(bridged_mock, config) MOIT.norminf1ftest(bridged_mock, config) ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone}())) @@ -23,11 +23,11 @@ config = MOIT.TestConfig() end @testset "NormOne" begin - bridged_mock = MOIB.Constraint.NormOne{Float64}(mock) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 0.5, 0.5, 0.5], (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[1.0, 1.0, 0.0, 0.0]], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [[1.0]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [0]]) + bridged_mock = MOIB.Constraint.NormOne{Float64}(mock) MOIT.normone1vtest(bridged_mock, config) MOIT.normone1ftest(bridged_mock, config) ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}())) From a8fe9529dfff9a6e85354c5aa15759d2e9a7b06e Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 10 Aug 2019 13:56:47 -0400 Subject: [PATCH 14/26] use normalize_and_add_constraint; fix test failures --- src/Bridges/Constraint/norm_to_lp.jl | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 5c8310e078..5b1f2a363d 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -8,7 +8,7 @@ The `NormInfinityCone` is representable with LP constraints, since struct NormInfinityBridge{T, F} <: AbstractBridge nn_index::CI{F, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormInfinityBridge{T}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where T +function bridge_constraint(::Type{NormInfinityBridge{T, F}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where {T, F} f_scalars = MOIU.eachscalar(f) t = f_scalars[1] if t isa MOI.SingleVariable # TODO currently needed for operate_output_index! @@ -22,17 +22,14 @@ function bridge_constraint(::Type{NormInfinityBridge{T}}, model::MOI.ModelLike, MOIU.operate_output_index!(+, T, i, f_new, t) end nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(MOI.output_dimension(f_new))) - return NormInfinityBridge(nn_index) + return NormInfinityBridge{T, F}(nn_index) end -MOI.supports_constraint(::Type{NormInfinityBridge}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) = true +MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[] MOIB.added_constraint_types(::Type{NormInfinityBridge{T, F}}) where {T, F} = [(F, MOI.Nonnegatives)] function concrete_bridge_type(::Type{<:NormInfinityBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T - G1 = MOIU.promote_operation(-, T, H) - S = MOIU.scalar_type(H) - G2 = MOIU.promote_operation(+, T, S, H) - F = MOIU.promote_operation(vcat, T, G1, G2) + F = MOIU.promote_operation(+, T, H, H) return NormInfinityBridge{T, F} end @@ -74,34 +71,30 @@ struct NormOneBridge{T, F, G} <: AbstractBridge ge_index::CI{F, MOI.GreaterThan{T}} nn_index::CI{G, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormOneBridge{T}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where T +function bridge_constraint(::Type{NormOneBridge{T, F, G}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where {T, F, G} if f isa MOI.VectorOfVariables # TODO currently needed for test to pass f = MOI.VectorAffineFunction{T}(f) end f_scalars = MOIU.eachscalar(f) d = MOI.dimension(s) y = MOI.add_variables(model, d - 1) - ge_index = MOI.add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)), MOI.GreaterThan(zero(T))) + ge_index = MOIU.normalize_and_add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)), MOI.GreaterThan(zero(T)), allow_modify_function=true) lb = f_scalars[2:d] ub = MOIU.operate(-, T, lb) MOIU.operate!(+, T, lb, MOI.VectorOfVariables(y)) MOIU.operate!(+, T, ub, MOI.VectorOfVariables(y)) f_new = MOIU.operate(vcat, T, ub, lb) nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(2d - 2)) - return NormOneBridge(y, ge_index, nn_index) + return NormOneBridge{T, F, G}(y, ge_index, nn_index) end -MOI.supports_constraint(::Type{NormOneBridge}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) = true +MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[] MOIB.added_constraint_types(::Type{NormOneBridge{T, F, G}}) where {T, F, G} = [(F, MOI.GreaterThan{T}), (G, MOI.Nonnegatives)] function concrete_bridge_type(::Type{<:NormOneBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T S = MOIU.scalar_type(H) - F1 = MOIU.promote_operation(+, T, MOI.SingleVariable, MOI.SingleVariable) - F = MOIU.promote_operation(-, T, S, F1) - G1 = MOIU.promote_operation(-, T, H) - G2 = MOIU.promote_operation(+, T, G1, MOI.VectorOfVariables) - G3 = MOIU.promote_operation(+, T, H, MOI.VectorOfVariables) - G = MOIU.promote_operation(vcat, T, G2, G3) + F = MOIU.promote_operation(+, T, S, S) + G = MOIU.promote_operation(+, T, H, H) return NormOneBridge{T, F, G} end From 7806da895a90cd5d86537a3980a0a1aa8f319b83 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Sat, 10 Aug 2019 16:31:50 -0400 Subject: [PATCH 15/26] address comments --- src/Bridges/Constraint/norm_to_lp.jl | 13 +++++-------- src/sets.jl | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 5b1f2a363d..0960deccab 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -11,13 +11,13 @@ end function bridge_constraint(::Type{NormInfinityBridge{T, F}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where {T, F} f_scalars = MOIU.eachscalar(f) t = f_scalars[1] - if t isa MOI.SingleVariable # TODO currently needed for operate_output_index! - t = MOI.ScalarAffineFunction{T}(t) - end d = MOI.dimension(s) lb = f_scalars[2:d] ub = MOIU.operate(-, T, lb) f_new = MOIU.operate(vcat, T, ub, lb) + if t isa MOI.SingleVariable # TODO currently needed for operate_output_index! + t = MOI.ScalarAffineFunction{T}(t) + end for i in 1:MOI.output_dimension(f_new) MOIU.operate_output_index!(+, T, i, f_new, t) end @@ -72,17 +72,14 @@ struct NormOneBridge{T, F, G} <: AbstractBridge nn_index::CI{G, MOI.Nonnegatives} end function bridge_constraint(::Type{NormOneBridge{T, F, G}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where {T, F, G} - if f isa MOI.VectorOfVariables # TODO currently needed for test to pass - f = MOI.VectorAffineFunction{T}(f) - end f_scalars = MOIU.eachscalar(f) d = MOI.dimension(s) y = MOI.add_variables(model, d - 1) ge_index = MOIU.normalize_and_add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)), MOI.GreaterThan(zero(T)), allow_modify_function=true) lb = f_scalars[2:d] ub = MOIU.operate(-, T, lb) - MOIU.operate!(+, T, lb, MOI.VectorOfVariables(y)) - MOIU.operate!(+, T, ub, MOI.VectorOfVariables(y)) + lb = MOIU.operate!(+, T, lb, MOI.VectorOfVariables(y)) + ub = MOIU.operate!(+, T, ub, MOI.VectorOfVariables(y)) f_new = MOIU.operate(vcat, T, ub, lb) nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(2d - 2)) return NormOneBridge{T, F, G}(y, ge_index, nn_index) diff --git a/src/sets.jl b/src/sets.jl index 8c51c37cae..4fdfe21d06 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -187,7 +187,7 @@ constant(s::LessThan) = s.upper """ NormInfinityCone(dimension) -The epigraph of the L∞ norm function ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_∞ = \\max_i |x_i| \\}`` of dimension `dimension`. +The epigraph of the ``\\ell_\\infty``-norm function ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_∞ = \\max_i |x_i| \\}`` of dimension `dimension`. """ struct NormInfinityCone <: AbstractVectorSet dimension::Int @@ -198,7 +198,7 @@ dual_set(s::NormInfinityCone) = NormOneCone(dimension(s)) """ NormOneCone(dimension) -The epigraph of the L1 norm function ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_1 = \\sum_i |x_i| \\}`` of dimension `dimension`. +The epigraph of the \\ell_1``-norm function ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_1 = \\sum_i |x_i| \\}`` of dimension `dimension`. """ struct NormOneCone <: AbstractVectorSet dimension::Int From bce186ad69c35b45ddcacd5b46e7cb58097cae51 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Mon, 12 Aug 2019 10:40:38 -0400 Subject: [PATCH 16/26] address comments --- src/Bridges/Constraint/norm_to_lp.jl | 4 ++-- src/sets.jl | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 0960deccab..2ebc95dd2f 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -2,7 +2,7 @@ NormInfinityBridge{T} The `NormInfinityCone` is representable with LP constraints, since -``t \\ge \\max_i |x_i|`` if and only if +``t \\ge \\max_i \\lvert x_i \\rvert`` if and only if ``t \\ge x_i`` and ``t \\ge -x_i`` for all ``i``. """ struct NormInfinityBridge{T, F} <: AbstractBridge @@ -63,7 +63,7 @@ end NormOneBridge{T} The `NormOneCone` is representable with LP constraints, since -``t \\ge \\sum_i |x_i|`` if and only if there exists a vector y such that +``t \\ge \\sum_i \\lvert x_i \\rvert`` if and only if there exists a vector y such that ``t \\ge \\sum_i y_i`` and ``y_i \\ge x_i``, ``y_i \\ge -x_i`` for all ``i``. """ struct NormOneBridge{T, F, G} <: AbstractBridge diff --git a/src/sets.jl b/src/sets.jl index 4fdfe21d06..6e8575a283 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -187,7 +187,7 @@ constant(s::LessThan) = s.upper """ NormInfinityCone(dimension) -The epigraph of the ``\\ell_\\infty``-norm function ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_∞ = \\max_i |x_i| \\}`` of dimension `dimension`. +The ``\\ell_\\infty``-norm cone ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge \\lVert x \\rVert_\\infty = \\max_i \\lvert x_i \\rvert \\}`` of dimension `dimension`. """ struct NormInfinityCone <: AbstractVectorSet dimension::Int @@ -198,7 +198,7 @@ dual_set(s::NormInfinityCone) = NormOneCone(dimension(s)) """ NormOneCone(dimension) -The epigraph of the \\ell_1``-norm function ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_1 = \\sum_i |x_i| \\}`` of dimension `dimension`. +The ``\\ell_1``-norm cone ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge \\lVert x \\rVert_\\infty_1 = \\sum_i \\lvert x_i \\rvert \\}`` of dimension `dimension`. """ struct NormOneCone <: AbstractVectorSet dimension::Int @@ -209,7 +209,7 @@ dual_set(s::NormOneCone) = NormInfinityCone(dimension(s)) """ SecondOrderCone(dimension) -The second-order cone (or Lorenz cone) ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge || x ||_2 \\}`` of dimension `dimension`. +The second-order cone (or Lorenz cone or ``\\ell_2``-norm cone) ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge \\lVert x \\rVert_2 \\}`` of dimension `dimension`. """ struct SecondOrderCone <: AbstractVectorSet dimension::Int @@ -220,7 +220,7 @@ dual_set(s::SecondOrderCone) = copy(s) """ RotatedSecondOrderCone(dimension) -The rotated second-order cone ``\\{ (t,u,x) \\in \\mathbb{R}^{dimension} : 2tu \\ge || x ||_2^2, t,u \\ge 0 \\}`` of dimension `dimension`. +The rotated second-order cone ``\\{ (t,u,x) \\in \\mathbb{R}^{dimension} : 2tu \\ge \\lVert x \\rVert_2^2, t,u \\ge 0 \\}`` of dimension `dimension`. """ struct RotatedSecondOrderCone <: AbstractVectorSet dimension::Int From 913ffcbf4471bb09e4a283b815d5757d54610f71 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 14 Aug 2019 14:42:16 -0400 Subject: [PATCH 17/26] add basic constraint tests; add test_models_equal tests --- test/Bridges/Constraint/norm_to_lp.jl | 124 ++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 6 deletions(-) diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index 958fafd444..fec3790805 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -12,25 +12,137 @@ mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) config = MOIT.TestConfig() @testset "NormInfinity" begin + bridged_mock = MOIB.Constraint.NormInfinity{Float64}(mock) + + MOIT.basic_constraint_tests(bridged_mock, config, + include = [(F, MOI.NormInfinityCone) for F in [ + MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64}, MOI.VectorQuadraticFunction{Float64} + ]]) + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 1.0], (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0.0, 1.0, 0.0, 0.0]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]]) - bridged_mock = MOIB.Constraint.NormInfinity{Float64}(mock) - MOIT.norminf1vtest(bridged_mock, config) + MOIT.norminf1ftest(bridged_mock, config) - ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone}())) + MOIT.norminf1vtest(bridged_mock, config) + + @testset "Test mock model" begin + var_names = ["x", "y", "z"] + MOI.set(mock, MOI.VariableName(), MOI.get(mock, MOI.ListOfVariableIndices()), var_names) + nonneg = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives}()) + @test length(nonneg) == 1 + MOI.set(mock, MOI.ConstraintName(), nonneg[1], "nonneg") + zeros = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Zeros}()) + @test length(zeros) == 2 + MOI.set(mock, MOI.ConstraintName(), zeros[1], "x_eq") + MOI.set(mock, MOI.ConstraintName(), zeros[2], "y_eq") + + s = """ + variables: x, y, z + nonneg: [x + -1.0y, x + -1.0z, x + y, x + z] in MathOptInterface.Nonnegatives(4) + x_eq: [-1.0 + x] in MathOptInterface.Zeros(1) + y_eq: [-0.5 + y] in MathOptInterface.Zeros(1) + maxobjective: y + z + """ + model = MOIU.Model{Float64}() + MOIU.loadfromstring!(model, s) + MOIU.test_models_equal(mock, model, var_names, ["nonneg", "x_eq", "y_eq"]) + end + + @testset "Test bridged model" begin + var_names = ["x", "y", "z"] + MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names) + norminf = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, MOI.NormInfinityCone}()) + @test length(norminf) == 1 + MOI.set(bridged_mock, MOI.ConstraintName(), norminf[1], "norminf") + zeros = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Zeros}()) + @test length(zeros) == 2 + MOI.set(bridged_mock, MOI.ConstraintName(), zeros[1], "x_eq") + MOI.set(bridged_mock, MOI.ConstraintName(), zeros[2], "y_eq") + + s = """ + variables: x, y, z + norminf: [x, y, z] in MathOptInterface.NormInfinityCone(3) + x_eq: [-1.0 + x] in MathOptInterface.Zeros(1) + y_eq: [-0.5 + y] in MathOptInterface.Zeros(1) + maxobjective: y + z + """ + model = MOIU.Model{Float64}() + MOIU.loadfromstring!(model, s) + MOIU.test_models_equal(bridged_mock, model, var_names, ["norminf", "x_eq", "y_eq"]) + end + + ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, MOI.NormInfinityCone}())) test_delete_bridge(bridged_mock, ci, 3, ((MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0),)) end @testset "NormOne" begin + bridged_mock = MOIB.Constraint.NormOne{Float64}(mock) + + MOIT.basic_constraint_tests(bridged_mock, config, + include = [(F, MOI.NormOneCone) for F in [ + MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64}, MOI.VectorQuadraticFunction{Float64} + ]]) + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.5, 0.5, 0.5, 0.5], (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[1.0, 1.0, 0.0, 0.0]], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [[1.0]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [0]]) - bridged_mock = MOIB.Constraint.NormOne{Float64}(mock) - MOIT.normone1vtest(bridged_mock, config) + MOIT.normone1ftest(bridged_mock, config) - ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}())) + MOIT.normone1vtest(bridged_mock, config) + + @testset "Test mock model" begin + var_names = ["x", "y", "z", "u", "v"] + MOI.set(mock, MOI.VariableName(), MOI.get(mock, MOI.ListOfVariableIndices()), var_names) + nonneg = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives}()) + @test length(nonneg) == 1 + MOI.set(mock, MOI.ConstraintName(), nonneg[1], "nonneg") + greater = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}}()) + @test length(greater) == 1 + MOI.set(mock, MOI.ConstraintName(), greater[1], "greater") + zeros = MOI.get(mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Zeros}()) + @test length(zeros) == 2 + MOI.set(mock, MOI.ConstraintName(), zeros[1], "x_eq") + MOI.set(mock, MOI.ConstraintName(), zeros[2], "y_eq") + + s = """ + variables: x, y, z, u, v + nonneg: [u + -1.0y, v + -1.0z, u + y, v + z] in MathOptInterface.Nonnegatives(4) + greater: x + -1.0u + -1.0v >= 0.0 + x_eq: [-1.0 + x] in MathOptInterface.Zeros(1) + y_eq: [-0.5 + y] in MathOptInterface.Zeros(1) + maxobjective: y + z + """ + model = MOIU.Model{Float64}() + MOIU.loadfromstring!(model, s) + MOIU.test_models_equal(mock, model, var_names, ["nonneg", "greater", "x_eq", "y_eq"]) + end + + @testset "Test bridged model" begin + var_names = ["x", "y", "z"] + MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names) + normone = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, MOI.NormOneCone}()) + @test length(normone) == 1 + MOI.set(bridged_mock, MOI.ConstraintName(), normone[1], "normone") + zeros = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Zeros}()) + @test length(zeros) == 2 + MOI.set(bridged_mock, MOI.ConstraintName(), zeros[1], "x_eq") + MOI.set(bridged_mock, MOI.ConstraintName(), zeros[2], "y_eq") + + s = """ + variables: x, y, z + normone: [x, y, z] in MathOptInterface.NormOneCone(3) + x_eq: [-1.0 + x] in MathOptInterface.Zeros(1) + y_eq: [-0.5 + y] in MathOptInterface.Zeros(1) + maxobjective: y + z + """ + model = MOIU.Model{Float64}() + MOIU.loadfromstring!(model, s) + MOIU.test_models_equal(bridged_mock, model, var_names, ["normone", "x_eq", "y_eq"]) + end + + ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, MOI.NormOneCone}())) test_delete_bridge(bridged_mock, ci, 3, ( (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) From be11aceda39333a44eef81193add8d7a7f9ab88d Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 14 Aug 2019 15:42:10 -0400 Subject: [PATCH 18/26] start on adding constraint function and set getters for NormInfinityCone --- src/Bridges/Constraint/norm_to_lp.jl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 2ebc95dd2f..32a1d5da78 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -41,6 +41,23 @@ MOI.get(b::NormInfinityBridge{T, F}, ::MOI.ListOfConstraintIndices{F, MOI.Nonneg MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) = MOI.delete(model, c.nn_index) # Attributes, Bridge acting as a constraint +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormInfinityBridge{T, F}) where {T, F} + ge = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.nn_index)) + t = MOIU.operate!(/, T, sum(ge), T(length(ge))) + d = div(length(ge), 2) + x = MOIU.operate!(-, T, ge[(d + 1):end], ge[1:d]) + # x = x / T(2) + # x = MOIU.operate!(/, T, x, T(2)) + # for i in 1:d + # MOIU.operate_output_index!(/, T, i, x, T(2)) + # end + @show MOIU.canonicalize!(MOIU.operate(vcat, T, t, x)) + return MOIU.canonicalize!(MOIU.operate(vcat, T, t, x)) +end +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormInfinityBridge) + dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2) + return MOI.NormInfinityCone(dim) +end function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormInfinityBridge) ge_value = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index) t = sum(ge_value) / length(ge_value) From 913e3db3cd4abf4e57ccba2336e0f16327fc6b88 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 14 Aug 2019 16:00:59 -0400 Subject: [PATCH 19/26] add get constraint function/set for NormOneCone; cleanup --- src/Bridges/Constraint/norm_to_lp.jl | 50 ++++++++++++++++------------ 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 32a1d5da78..eda94013f3 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -42,16 +42,10 @@ MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) = MOI.delete(model, c.nn # Attributes, Bridge acting as a constraint function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormInfinityBridge{T, F}) where {T, F} - ge = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.nn_index)) - t = MOIU.operate!(/, T, sum(ge), T(length(ge))) - d = div(length(ge), 2) - x = MOIU.operate!(-, T, ge[(d + 1):end], ge[1:d]) - # x = x / T(2) - # x = MOIU.operate!(/, T, x, T(2)) - # for i in 1:d - # MOIU.operate_output_index!(/, T, i, x, T(2)) - # end - @show MOIU.canonicalize!(MOIU.operate(vcat, T, t, x)) + nn_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.nn_index)) + t = MOIU.operate!(/, T, sum(nn_func), T(length(nn_func))) + d = div(length(nn_func), 2) + x = MOIU.operate!(/, T, MOIU.operate!(-, T, nn_func[(d + 1):end], nn_func[1:d]), T(2)) return MOIU.canonicalize!(MOIU.operate(vcat, T, t, x)) end function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormInfinityBridge) @@ -59,20 +53,20 @@ function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormInfinityBridg return MOI.NormInfinityCone(dim) end function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormInfinityBridge) - ge_value = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index) - t = sum(ge_value) / length(ge_value) - d = div(length(ge_value), 2) - x = (ge_value[(d + 1):end] - ge_value[1:d]) / 2 + nn_primal = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index) + t = sum(nn_primal) / length(nn_primal) + d = div(length(nn_primal), 2) + x = (nn_primal[(d + 1):end] - nn_primal[1:d]) / 2 return vcat(t, x) end # Given a_i is dual on t - x_i >= 0 and b_i is dual on t + x_i >= 0, # the dual on (t, x) in NormInfinityCone is (u, v) in NormOneCone, where # v_i = -a_i + b_i and u = sum(a) + sum(b). function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormInfinityBridge) - ge_dual = MOI.get(model, MOI.ConstraintDual(), c.nn_index) - t = sum(ge_dual) - d = div(length(ge_dual), 2) - x = (ge_dual[(d + 1):end] - ge_dual[1:d]) + nn_dual = MOI.get(model, MOI.ConstraintDual(), c.nn_index) + t = sum(nn_dual) + d = div(length(nn_dual), 2) + x = (nn_dual[(d + 1):end] - nn_dual[1:d]) return vcat(t, x) end @@ -128,12 +122,24 @@ function MOI.delete(model::MOI.ModelLike, c::NormOneBridge) end # Attributes, Bridge acting as a constraint +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormOneBridge{T, F, G}) where {T, F, G} + ge_func = MOI.get(model, MOI.ConstraintFunction(), c.ge_index) + nn_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.nn_index)) + t = MOIU.operate!(+, T, ge_func, MOIU.operate!(/, T, sum(nn_func), T(2))) + d = div(length(nn_func), 2) + x = MOIU.operate!(/, T, MOIU.operate!(-, T, nn_func[(d + 1):end], nn_func[1:d]), T(2)) + return MOIU.canonicalize!(MOIU.operate(vcat, T, t, x)) +end +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormOneBridge) + dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2) + return MOI.NormOneCone(dim) +end function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormOneBridge) - ge_value = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index) - nn_value = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index) - t = ge_value + sum(nn_value) / 2 + ge_primal = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index) + nn_primal = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index) + t = ge_primal + sum(nn_primal) / 2 d = length(c.y) - x = (nn_value[(d + 1):end] - nn_value[1:d]) / 2 + x = (nn_primal[(d + 1):end] - nn_primal[1:d]) / 2 return vcat(t, x) end # Given a_i is dual on y_i - x_i >= 0 and b_i is dual on y_i + x_i >= 0 and c is dual on t - sum(y) >= 0, From 2fc69c72a098af1cbabddb1df93ccbf82a7fd667 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 15 Aug 2019 09:31:12 -0400 Subject: [PATCH 20/26] use VAF in test instead of VOV --- test/Bridges/Constraint/norm_to_lp.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index fec3790805..f80c2312ef 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -23,8 +23,8 @@ config = MOIT.TestConfig() (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0.0, 1.0, 0.0, 0.0]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]]) - MOIT.norminf1ftest(bridged_mock, config) MOIT.norminf1vtest(bridged_mock, config) + MOIT.norminf1ftest(bridged_mock, config) @testset "Test mock model" begin var_names = ["x", "y", "z"] @@ -52,7 +52,7 @@ config = MOIT.TestConfig() @testset "Test bridged model" begin var_names = ["x", "y", "z"] MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names) - norminf = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, MOI.NormInfinityCone}()) + norminf = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone}()) @test length(norminf) == 1 MOI.set(bridged_mock, MOI.ConstraintName(), norminf[1], "norminf") zeros = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Zeros}()) @@ -62,7 +62,7 @@ config = MOIT.TestConfig() s = """ variables: x, y, z - norminf: [x, y, z] in MathOptInterface.NormInfinityCone(3) + norminf: [1.0x, y, z] in MathOptInterface.NormInfinityCone(3) x_eq: [-1.0 + x] in MathOptInterface.Zeros(1) y_eq: [-0.5 + y] in MathOptInterface.Zeros(1) maxobjective: y + z @@ -72,7 +72,7 @@ config = MOIT.TestConfig() MOIU.test_models_equal(bridged_mock, model, var_names, ["norminf", "x_eq", "y_eq"]) end - ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, MOI.NormInfinityCone}())) + ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone}())) test_delete_bridge(bridged_mock, ci, 3, ((MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0),)) end @@ -89,8 +89,8 @@ end (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [[1.0]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [0]]) - MOIT.normone1ftest(bridged_mock, config) MOIT.normone1vtest(bridged_mock, config) + MOIT.normone1ftest(bridged_mock, config) @testset "Test mock model" begin var_names = ["x", "y", "z", "u", "v"] @@ -122,7 +122,7 @@ end @testset "Test bridged model" begin var_names = ["x", "y", "z"] MOI.set(bridged_mock, MOI.VariableName(), MOI.get(bridged_mock, MOI.ListOfVariableIndices()), var_names) - normone = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, MOI.NormOneCone}()) + normone = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}()) @test length(normone) == 1 MOI.set(bridged_mock, MOI.ConstraintName(), normone[1], "normone") zeros = MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.Zeros}()) @@ -132,7 +132,7 @@ end s = """ variables: x, y, z - normone: [x, y, z] in MathOptInterface.NormOneCone(3) + normone: [1.0x, y, z] in MathOptInterface.NormOneCone(3) x_eq: [-1.0 + x] in MathOptInterface.Zeros(1) y_eq: [-0.5 + y] in MathOptInterface.Zeros(1) maxobjective: y + z @@ -142,7 +142,7 @@ end MOIU.test_models_equal(bridged_mock, model, var_names, ["normone", "x_eq", "y_eq"]) end - ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, MOI.NormOneCone}())) + ci = first(MOI.get(bridged_mock, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64}, MOI.NormOneCone}())) test_delete_bridge(bridged_mock, ci, 3, ( (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, 0))) From 49c87ff06f1837d5a6b04a0cde7c95207e08ac0d Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 15 Aug 2019 09:39:50 -0400 Subject: [PATCH 21/26] fix some tests --- src/Test/UnitTests/basic_constraint_tests.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Test/UnitTests/basic_constraint_tests.jl b/src/Test/UnitTests/basic_constraint_tests.jl index 5422ae82c9..45be86fc8b 100644 --- a/src/Test/UnitTests/basic_constraint_tests.jl +++ b/src/Test/UnitTests/basic_constraint_tests.jl @@ -34,9 +34,9 @@ const BasicConstraintTests = Dict( (MOI.VectorOfVariables, MOI.Nonpositives) => ( dummy_vectorofvariables, 2, MOI.Nonpositives(2) ), (MOI.VectorOfVariables, MOI.Nonnegatives) => ( dummy_vectorofvariables, 2, MOI.Nonnegatives(2) ), - (MOI.VectorOfVariables, MOI.SecondOrderCone) => ( dummy_vectorofvariables, 3, MOI.SecondOrderCone(3) ), (MOI.VectorOfVariables, MOI.NormInfinityCone) => ( dummy_vectorofvariables, 3, MOI.NormInfinityCone(3) ), (MOI.VectorOfVariables, MOI.NormOneCone) => ( dummy_vectorofvariables, 3, MOI.NormOneCone(3) ), + (MOI.VectorOfVariables, MOI.SecondOrderCone) => ( dummy_vectorofvariables, 3, MOI.SecondOrderCone(3) ), (MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => ( dummy_vectorofvariables, 3, MOI.RotatedSecondOrderCone(3) ), (MOI.VectorOfVariables, MOI.GeometricMeanCone) => ( dummy_vectorofvariables, 3, MOI.GeometricMeanCone(3) ), (MOI.VectorOfVariables, MOI.ExponentialCone) => ( dummy_vectorofvariables, 3, MOI.ExponentialCone() ), @@ -65,6 +65,8 @@ const BasicConstraintTests = Dict( (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => ( dummy_vector_affine, 2, MOI.Nonpositives(2) ), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => ( dummy_vector_affine, 2, MOI.Nonnegatives(2) ), + (MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) => ( dummy_vector_affine, 3, MOI.NormInfinityCone(3) ), + (MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) => ( dummy_vector_affine, 3, MOI.NormOneCone(3) ), (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => ( dummy_vector_affine, 3, MOI.SecondOrderCone(3) ), (MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => ( dummy_vector_affine, 3, MOI.RotatedSecondOrderCone(3) ), @@ -72,6 +74,8 @@ const BasicConstraintTests = Dict( (MOI.VectorQuadraticFunction{Float64}, MOI.Nonpositives) => ( dummy_vector_quadratic, 2, MOI.Nonpositives(2) ), (MOI.VectorQuadraticFunction{Float64}, MOI.Nonnegatives) => ( dummy_vector_quadratic, 2, MOI.Nonnegatives(2) ), + (MOI.VectorQuadraticFunction{Float64}, MOI.NormInfinityCone) => ( dummy_vector_quadratic, 3, MOI.NormInfinityCone(3) ), + (MOI.VectorQuadraticFunction{Float64}, MOI.NormOneCone) => ( dummy_vector_quadratic, 3, MOI.NormOneCone(3) ), (MOI.VectorQuadraticFunction{Float64}, MOI.SecondOrderCone) => ( dummy_vector_quadratic, 3, MOI.SecondOrderCone(3) ), (MOI.VectorQuadraticFunction{Float64}, MOI.RotatedSecondOrderCone) => ( dummy_vector_quadratic, 3, MOI.RotatedSecondOrderCone(3) ) ) From a5a479c27a6bf03944c28a6a315439848ebfc0e3 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 15 Aug 2019 14:57:36 -0400 Subject: [PATCH 22/26] use MOIU.convert_approx --- src/Bridges/Constraint/norm_to_lp.jl | 42 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index eda94013f3..5b54f4ee9c 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -5,10 +5,10 @@ The `NormInfinityCone` is representable with LP constraints, since ``t \\ge \\max_i \\lvert x_i \\rvert`` if and only if ``t \\ge x_i`` and ``t \\ge -x_i`` for all ``i``. """ -struct NormInfinityBridge{T, F} <: AbstractBridge +struct NormInfinityBridge{T, F, G} <: AbstractBridge nn_index::CI{F, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormInfinityBridge{T, F}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where {T, F} +function bridge_constraint(::Type{NormInfinityBridge{T, F, G}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where {T, F, G} f_scalars = MOIU.eachscalar(f) t = f_scalars[1] d = MOI.dimension(s) @@ -22,31 +22,31 @@ function bridge_constraint(::Type{NormInfinityBridge{T, F}}, model::MOI.ModelLik MOIU.operate_output_index!(+, T, i, f_new, t) end nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(MOI.output_dimension(f_new))) - return NormInfinityBridge{T, F}(nn_index) + return NormInfinityBridge{T, F, G}(nn_index) end MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[] MOIB.added_constraint_types(::Type{NormInfinityBridge{T, F}}) where {T, F} = [(F, MOI.Nonnegatives)] -function concrete_bridge_type(::Type{<:NormInfinityBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T - F = MOIU.promote_operation(+, T, H, H) - return NormInfinityBridge{T, F} +function concrete_bridge_type(::Type{<:NormInfinityBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T + F = MOIU.promote_operation(+, T, G, G) + return NormInfinityBridge{T, F, G} end # Attributes, Bridge acting as a model -MOI.get(b::NormInfinityBridge{T, F}, ::MOI.NumberOfConstraints{F, MOI.Nonnegatives}) where {T, F} = 1 -MOI.get(b::NormInfinityBridge{T, F}, ::MOI.ListOfConstraintIndices{F, MOI.Nonnegatives}) where {T, F} = [b.nn_index] +MOI.get(b::NormInfinityBridge{T, F, G}, ::MOI.NumberOfConstraints{F, MOI.Nonnegatives}) where {T, F, G} = 1 +MOI.get(b::NormInfinityBridge{T, F, G}, ::MOI.ListOfConstraintIndices{F, MOI.Nonnegatives}) where {T, F, G} = [b.nn_index] # References MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) = MOI.delete(model, c.nn_index) # Attributes, Bridge acting as a constraint -function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormInfinityBridge{T, F}) where {T, F} +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormInfinityBridge{T, F, G}) where {T, F, G} nn_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.nn_index)) t = MOIU.operate!(/, T, sum(nn_func), T(length(nn_func))) d = div(length(nn_func), 2) x = MOIU.operate!(/, T, MOIU.operate!(-, T, nn_func[(d + 1):end], nn_func[1:d]), T(2)) - return MOIU.canonicalize!(MOIU.operate(vcat, T, t, x)) + return MOIU.convert_approx(G, MOIU.operate(vcat, T, t, x)) end function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormInfinityBridge) dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2) @@ -77,12 +77,12 @@ The `NormOneCone` is representable with LP constraints, since ``t \\ge \\sum_i \\lvert x_i \\rvert`` if and only if there exists a vector y such that ``t \\ge \\sum_i y_i`` and ``y_i \\ge x_i``, ``y_i \\ge -x_i`` for all ``i``. """ -struct NormOneBridge{T, F, G} <: AbstractBridge +struct NormOneBridge{T, F, G, H} <: AbstractBridge y::Vector{MOI.VariableIndex} ge_index::CI{F, MOI.GreaterThan{T}} nn_index::CI{G, MOI.Nonnegatives} end -function bridge_constraint(::Type{NormOneBridge{T, F, G}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where {T, F, G} +function bridge_constraint(::Type{NormOneBridge{T, F, G, H}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where {T, F, G, H} f_scalars = MOIU.eachscalar(f) d = MOI.dimension(s) y = MOI.add_variables(model, d - 1) @@ -93,26 +93,26 @@ function bridge_constraint(::Type{NormOneBridge{T, F, G}}, model::MOI.ModelLike, ub = MOIU.operate!(+, T, ub, MOI.VectorOfVariables(y)) f_new = MOIU.operate(vcat, T, ub, lb) nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(2d - 2)) - return NormOneBridge{T, F, G}(y, ge_index, nn_index) + return NormOneBridge{T, F, G, H}(y, ge_index, nn_index) end MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormOneBridge{T, F, G}}) where {T, F, G} = [(F, MOI.GreaterThan{T}), (G, MOI.Nonnegatives)] +MOIB.added_constraint_types(::Type{NormOneBridge{T, F, G, H}}) where {T, F, G, H} = [(F, MOI.GreaterThan{T}), (G, MOI.Nonnegatives)] function concrete_bridge_type(::Type{<:NormOneBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T S = MOIU.scalar_type(H) F = MOIU.promote_operation(+, T, S, S) G = MOIU.promote_operation(+, T, H, H) - return NormOneBridge{T, F, G} + return NormOneBridge{T, F, G, H} end # Attributes, Bridge acting as a model MOI.get(b::NormOneBridge, ::MOI.NumberOfVariables) = length(b.y) MOI.get(b::NormOneBridge, ::MOI.ListOfVariableIndices) = b.y -MOI.get(b::NormOneBridge{T, F, G}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F, G} = 1 -MOI.get(b::NormOneBridge{T, F, G}, ::MOI.NumberOfConstraints{G, MOI.Nonnegatives}) where {T, F, G} = 1 -MOI.get(b::NormOneBridge{T, F, G}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F, G} = [b.ge_index] -MOI.get(b::NormOneBridge{T, F, G}, ::MOI.ListOfConstraintIndices{G, MOI.Nonnegatives}) where {T, F, G} = [b.nn_index] +MOI.get(b::NormOneBridge{T, F, G, H}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F, G, H} = 1 +MOI.get(b::NormOneBridge{T, F, G, H}, ::MOI.NumberOfConstraints{G, MOI.Nonnegatives}) where {T, F, G, H} = 1 +MOI.get(b::NormOneBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F, G, H} = [b.ge_index] +MOI.get(b::NormOneBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{G, MOI.Nonnegatives}) where {T, F, G, H} = [b.nn_index] # References function MOI.delete(model::MOI.ModelLike, c::NormOneBridge) @@ -122,13 +122,13 @@ function MOI.delete(model::MOI.ModelLike, c::NormOneBridge) end # Attributes, Bridge acting as a constraint -function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormOneBridge{T, F, G}) where {T, F, G} +function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormOneBridge{T, F, G, H}) where {T, F, G, H} ge_func = MOI.get(model, MOI.ConstraintFunction(), c.ge_index) nn_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.nn_index)) t = MOIU.operate!(+, T, ge_func, MOIU.operate!(/, T, sum(nn_func), T(2))) d = div(length(nn_func), 2) x = MOIU.operate!(/, T, MOIU.operate!(-, T, nn_func[(d + 1):end], nn_func[1:d]), T(2)) - return MOIU.canonicalize!(MOIU.operate(vcat, T, t, x)) + return MOIU.convert_approx(H, MOIU.operate(vcat, T, t, x)) end function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormOneBridge) dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2) From 459972c5a45e78f263d586d78ac5b157322f9b81 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 15 Aug 2019 15:08:56 -0400 Subject: [PATCH 23/26] canonicalize in convert_approx; use VAQ in unsafe_add when needed; new tests pass --- src/Utilities/functions.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 13360f618e..8d9b655998 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -328,7 +328,7 @@ Sums the coefficients of `t1` and `t2` and returns an output `MOI.VectorAffineTe function unsafe_add(t1::VT, t2::VT) where VT <: Union{MOI.VectorAffineTerm, MOI.VectorQuadraticTerm} scalar_term = unsafe_add(t1.scalar_term, t2.scalar_term) - return MOI.VectorAffineTerm(t1.output_index, scalar_term) + return VT(t1.output_index, scalar_term) end """ @@ -1821,9 +1821,9 @@ function count_terms(dimension::I, terms::Vector{T}) where {I,T} return counting end -convert_approx(::Type{T}, func::T; kws...) where {T} = func +convert_approx(::Type{T}, func::T; kws...) where {T} = MOIU.canonical(func) function convert_approx(::Type{MOI.SingleVariable}, func::MOI.ScalarAffineFunction{T}; - tol=sqrt(eps(T))) where {T} + tol=sqrt(eps(T))) where {T} f = MOIU.canonical(func) i = findfirst(t -> isapprox(t.coefficient, one(T), atol=tol), f.terms) if abs(f.constant) > tol || i === nothing || @@ -1835,5 +1835,5 @@ end function convert_approx(::Type{MOI.VectorOfVariables}, func::MOI.VectorAffineFunction{T}; tol=sqrt(eps(T))) where {T} return MOI.VectorOfVariables([convert_approx(MOI.SingleVariable, f, tol=tol).variable - for f in scalarize(func)]) + for f in scalarize(func)]) end From 4d28606f7e4eafb5eb2117461eb29cb87c660257 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Thu, 15 Aug 2019 15:19:40 -0400 Subject: [PATCH 24/26] correct type signature --- src/Bridges/Constraint/norm_to_lp.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 5b54f4ee9c..49aed28e49 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -27,7 +27,7 @@ end MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = true MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[] -MOIB.added_constraint_types(::Type{NormInfinityBridge{T, F}}) where {T, F} = [(F, MOI.Nonnegatives)] +MOIB.added_constraint_types(::Type{NormInfinityBridge{T, F, G}}) where {T, F, G} = [(F, MOI.Nonnegatives)] function concrete_bridge_type(::Type{<:NormInfinityBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T F = MOIU.promote_operation(+, T, G, G) return NormInfinityBridge{T, F, G} From 786a246fbc4efec4a7a9bf13b957cf53c527f46f Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 16 Aug 2019 09:00:31 -0400 Subject: [PATCH 25/26] remove new variables to make NormOneBridge tests pass --- src/Bridges/Constraint/norm_to_lp.jl | 2 +- src/Utilities/functions.jl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 49aed28e49..3cbeb92baa 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -128,7 +128,7 @@ function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormOneBridg t = MOIU.operate!(+, T, ge_func, MOIU.operate!(/, T, sum(nn_func), T(2))) d = div(length(nn_func), 2) x = MOIU.operate!(/, T, MOIU.operate!(-, T, nn_func[(d + 1):end], nn_func[1:d]), T(2)) - return MOIU.convert_approx(H, MOIU.operate(vcat, T, t, x)) + return MOIU.convert_approx(H, MOIU.remove_variable(MOIU.operate(vcat, T, t, x), c.y)) end function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormOneBridge) dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 8d9b655998..01bc01c9e8 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -1821,9 +1821,9 @@ function count_terms(dimension::I, terms::Vector{T}) where {I,T} return counting end -convert_approx(::Type{T}, func::T; kws...) where {T} = MOIU.canonical(func) +convert_approx(::Type{T}, func::T; kws...) where {T} = func function convert_approx(::Type{MOI.SingleVariable}, func::MOI.ScalarAffineFunction{T}; - tol=sqrt(eps(T))) where {T} + tol=sqrt(eps(T))) where {T} f = MOIU.canonical(func) i = findfirst(t -> isapprox(t.coefficient, one(T), atol=tol), f.terms) if abs(f.constant) > tol || i === nothing || @@ -1835,5 +1835,5 @@ end function convert_approx(::Type{MOI.VectorOfVariables}, func::MOI.VectorAffineFunction{T}; tol=sqrt(eps(T))) where {T} return MOI.VectorOfVariables([convert_approx(MOI.SingleVariable, f, tol=tol).variable - for f in scalarize(func)]) + for f in scalarize(func)]) end From 6e6d5dbd95a4723e37fce955ee0bdf793bfc6dbe Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 16 Aug 2019 14:13:58 -0400 Subject: [PATCH 26/26] remove conversion no longer needed --- src/Bridges/Constraint/norm_to_lp.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 3cbeb92baa..729bfce602 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -15,9 +15,6 @@ function bridge_constraint(::Type{NormInfinityBridge{T, F, G}}, model::MOI.Model lb = f_scalars[2:d] ub = MOIU.operate(-, T, lb) f_new = MOIU.operate(vcat, T, ub, lb) - if t isa MOI.SingleVariable # TODO currently needed for operate_output_index! - t = MOI.ScalarAffineFunction{T}(t) - end for i in 1:MOI.output_dimension(f_new) MOIU.operate_output_index!(+, T, i, f_new, t) end