From 9989e363317e8da0ac6d971f13e61f3795e73f9e Mon Sep 17 00:00:00 2001 From: Robert Schwarz Date: Wed, 13 Mar 2019 15:21:56 +0100 Subject: [PATCH 1/8] add test qcp4: mixed quadratic term in x*y <= 4 --- src/Test/contquadratic.jl | 54 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Test/contquadratic.jl b/src/Test/contquadratic.jl index ac1e8da691..445f412f57 100644 --- a/src/Test/contquadratic.jl +++ b/src/Test/contquadratic.jl @@ -385,9 +385,61 @@ function qcp3test(model::MOI.ModelLike, config::TestConfig) end end +function qcp4test(model::MOI.ModelLike, config::TestConfig) + atol = config.atol + rtol = config.rtol + # Max 2x + y + # s.t. x*y <= 4 (c) + # x, y >= 1 + + @test MOIU.supports_default_copy_to(model, #=copy_names=# false) + @test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}()) + @test MOI.supports_constraint(model, MOI.ScalarQuadraticFunction{Float64},MOI.LessThan{Float64}) + + MOI.empty!(model) + @test MOI.is_empty(model) + + x = MOI.add_variable(model) + y = MOI.add_variable(model) + @test MOI.get(model, MOI.NumberOfVariables()) == 2 + + MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(1.0)) + MOI.add_constraint(model, MOI.SingleVariable(y), MOI.GreaterThan(1.0)) + + cf = MOI.ScalarQuadraticFunction([MOI.ScalarAffineTerm(0.0, x)], [MOI.ScalarQuadraticTerm(1.0, x, y)], 0.0) + c = MOI.add_constraint(model, cf, MOI.LessThan(4.0)) + @test MOI.get(model, MOI.NumberOfConstraints{MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}}()) == 1 + + MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], [x, y]), 0.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE + + if config.query + @test cf ≈ MOI.get(model, MOI.ConstraintFunction(), c) + end + + 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 + + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 9.0 atol=atol rtol=rtol + + @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4.0 atol=atol rtol=rtol + @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 1.0 atol=atol rtol=rtol + + @test MOI.get(model, MOI.ConstraintPrimal(), c) ≈ 4.0 atol=atol rtol=rtol + end +end + const qcptests = Dict("qcp1" => qcp1test, "qcp2" => qcp2test, - "qcp3" => qcp3test) + "qcp3" => qcp3test, + "qcp4" => qcp4test) @moitestset qcp From 3e1c22f81e7bed015b4971d2bebf2031da17cf5d Mon Sep 17 00:00:00 2001 From: Robert Schwarz Date: Wed, 13 Mar 2019 15:30:28 +0100 Subject: [PATCH 2/8] add test qcp5 with quadratic equations (x^2 == 4, x*y == 4) --- src/Test/contquadratic.jl | 54 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Test/contquadratic.jl b/src/Test/contquadratic.jl index 445f412f57..1516f4a917 100644 --- a/src/Test/contquadratic.jl +++ b/src/Test/contquadratic.jl @@ -436,10 +436,62 @@ function qcp4test(model::MOI.ModelLike, config::TestConfig) end end +function qcp5test(model::MOI.ModelLike, config::TestConfig) + atol = config.atol + rtol = config.rtol + # Find x,y + # s.t. x*y == 4 (c) + # x*x == 4 (c2) + # x, y >= 0 + + @test MOIU.supports_default_copy_to(model, #=copy_names=# false) + @test MOI.supports_constraint(model, MOI.ScalarQuadraticFunction{Float64},MOI.EqualTo{Float64}) + + MOI.empty!(model) + @test MOI.is_empty(model) + + x = MOI.add_variable(model) + y = MOI.add_variable(model) + @test MOI.get(model, MOI.NumberOfVariables()) == 2 + + MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(0.0)) + MOI.add_constraint(model, MOI.SingleVariable(y), MOI.GreaterThan(0.0)) + + cf = MOI.ScalarQuadraticFunction([MOI.ScalarAffineTerm(0.0, x)], [MOI.ScalarQuadraticTerm(1.0, x, y)], 0.0) + c = MOI.add_constraint(model, cf, MOI.EqualTo(4.0)) + + cf2 = MOI.ScalarQuadraticFunction([MOI.ScalarAffineTerm(0.0, x)], [MOI.ScalarQuadraticTerm(2.0, x, x)], 0.0) + c2 = MOI.add_constraint(model, cf2, MOI.EqualTo(4.0)) + + @test MOI.get(model, MOI.NumberOfConstraints{MOI.ScalarQuadraticFunction{Float64}, MOI.EqualTo{Float64}}()) == 2 + + if config.query + @test cf ≈ MOI.get(model, MOI.ConstraintFunction(), c) + @test cf2 ≈ MOI.get(model, MOI.ConstraintFunction(), c2) + end + + 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 + + @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 2.0 atol=atol rtol=rtol + @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 2.0 atol=atol rtol=rtol + + @test MOI.get(model, MOI.ConstraintPrimal(), c) ≈ 4.0 atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintPrimal(), c2) ≈ 4.0 atol=atol rtol=rtol + end +end + const qcptests = Dict("qcp1" => qcp1test, "qcp2" => qcp2test, "qcp3" => qcp3test, - "qcp4" => qcp4test) + "qcp4" => qcp4test, + "qcp5" => qcp5test) @moitestset qcp From 65b62d83932d4b92530f917e6ad11c86371a665a Mon Sep 17 00:00:00 2001 From: Robert Schwarz Date: Wed, 13 Mar 2019 15:48:14 +0100 Subject: [PATCH 3/8] add example to docstring of ScalarQuadraticFunction --- src/functions.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/functions.jl b/src/functions.jl index 208140f942..8c39de9998 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -162,6 +162,10 @@ Duplicate indices in ``a`` or ``Q`` are accepted, and the corresponding coefficients are summed together. "Mirrored" indices `(q,r)` and `(r,q)` (where `r` and `q` are `VariableIndex`es) are considered duplicates; only one need be specified. + +For example, for two scalar variables ``y, z``, the quadratic expression +``yz + y^2`` is represented by the terms +`ScalarQuadraticTerm.([1.0, 2.0], [y, y], [z, y])`. """ mutable struct ScalarQuadraticFunction{T} <: AbstractScalarFunction affine_terms::Vector{ScalarAffineTerm{T}} From 0191f1ec41328db1bf00c1ffae6df332c63d7f1e Mon Sep 17 00:00:00 2001 From: Robert Schwarz Date: Fri, 29 Mar 2019 10:57:40 +0100 Subject: [PATCH 4/8] test the new tests with MockOptimizer --- test/Test/contquadratic.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/Test/contquadratic.jl b/test/Test/contquadratic.jl index edd802f5bb..913f2b2c8e 100644 --- a/test/Test/contquadratic.jl +++ b/test/Test/contquadratic.jl @@ -22,6 +22,12 @@ MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [√2], MOI.FEASIBLE_POINT)) MOIT.qcp3test(mock, config) + MOIU.set_mock_optimize!(mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4.0, 1.0], MOI.FEASIBLE_POINT)) + MOIT.qcp4test(mock, config) + MOIU.set_mock_optimize!(mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2.0, 2.0], MOI.FEASIBLE_POINT)) + MOIT.qcp5test(mock, config) end @testset "SOCP" begin MOIU.set_mock_optimize!(mock, From fd9e9540ab5857368d170cc070612b8e904f60a5 Mon Sep 17 00:00:00 2001 From: Robert Schwarz Date: Fri, 29 Mar 2019 11:59:13 +0100 Subject: [PATCH 5/8] fix style (space after comma) --- src/Test/contquadratic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Test/contquadratic.jl b/src/Test/contquadratic.jl index 1516f4a917..7e24df10f7 100644 --- a/src/Test/contquadratic.jl +++ b/src/Test/contquadratic.jl @@ -394,7 +394,7 @@ function qcp4test(model::MOI.ModelLike, config::TestConfig) @test MOIU.supports_default_copy_to(model, #=copy_names=# false) @test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}()) - @test MOI.supports_constraint(model, MOI.ScalarQuadraticFunction{Float64},MOI.LessThan{Float64}) + @test MOI.supports_constraint(model, MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) MOI.empty!(model) @test MOI.is_empty(model) @@ -445,7 +445,7 @@ function qcp5test(model::MOI.ModelLike, config::TestConfig) # x, y >= 0 @test MOIU.supports_default_copy_to(model, #=copy_names=# false) - @test MOI.supports_constraint(model, MOI.ScalarQuadraticFunction{Float64},MOI.EqualTo{Float64}) + @test MOI.supports_constraint(model, MOI.ScalarQuadraticFunction{Float64}, MOI.EqualTo{Float64}) MOI.empty!(model) @test MOI.is_empty(model) From 9a8bace0154e541cb95dc97e7e669079da8fba2e Mon Sep 17 00:00:00 2001 From: Robert Schwarz Date: Fri, 29 Mar 2019 15:03:48 +0100 Subject: [PATCH 6/8] fix style (space around operators) --- src/Test/contquadratic.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Test/contquadratic.jl b/src/Test/contquadratic.jl index 7e24df10f7..0cdf97487c 100644 --- a/src/Test/contquadratic.jl +++ b/src/Test/contquadratic.jl @@ -389,7 +389,7 @@ function qcp4test(model::MOI.ModelLike, config::TestConfig) atol = config.atol rtol = config.rtol # Max 2x + y - # s.t. x*y <= 4 (c) + # s.t. x * y <= 4 (c) # x, y >= 1 @test MOIU.supports_default_copy_to(model, #=copy_names=# false) @@ -439,9 +439,9 @@ end function qcp5test(model::MOI.ModelLike, config::TestConfig) atol = config.atol rtol = config.rtol - # Find x,y - # s.t. x*y == 4 (c) - # x*x == 4 (c2) + # Find x, y + # s.t. x * y == 4 (c) + # x * x == 4 (c2) # x, y >= 0 @test MOIU.supports_default_copy_to(model, #=copy_names=# false) From 30f7ac7c6ec9b751b4bce8acda5efeaba1207834 Mon Sep 17 00:00:00 2001 From: Robert Schwarz Date: Fri, 29 Mar 2019 16:31:12 +0100 Subject: [PATCH 7/8] move non-convex problems from `qcp` to `ncqcp` --- src/Test/contquadratic.jl | 26 +++++++++++++++++--------- test/Test/contquadratic.jl | 6 ++++-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Test/contquadratic.jl b/src/Test/contquadratic.jl index 0cdf97487c..443f1098e7 100644 --- a/src/Test/contquadratic.jl +++ b/src/Test/contquadratic.jl @@ -208,7 +208,7 @@ const qptests = Dict("qp1" => qp1test, @moitestset qp #= - Quadratically constrained programs + Quadratically constrained (convex) programs =# function qcp1test(model::MOI.ModelLike, config::TestConfig) @@ -385,7 +385,17 @@ function qcp3test(model::MOI.ModelLike, config::TestConfig) end end -function qcp4test(model::MOI.ModelLike, config::TestConfig) +const qcptests = Dict("qcp1" => qcp1test, + "qcp2" => qcp2test, + "qcp3" => qcp3test) + +@moitestset qcp + +#= + Quadratically constrained (non-convex) programs +=# + +function ncqcp1test(model::MOI.ModelLike, config::TestConfig) atol = config.atol rtol = config.rtol # Max 2x + y @@ -436,7 +446,7 @@ function qcp4test(model::MOI.ModelLike, config::TestConfig) end end -function qcp5test(model::MOI.ModelLike, config::TestConfig) +function ncqcp2test(model::MOI.ModelLike, config::TestConfig) atol = config.atol rtol = config.rtol # Find x, y @@ -487,13 +497,10 @@ function qcp5test(model::MOI.ModelLike, config::TestConfig) end end -const qcptests = Dict("qcp1" => qcp1test, - "qcp2" => qcp2test, - "qcp3" => qcp3test, - "qcp4" => qcp4test, - "qcp5" => qcp5test) +const ncqcptests = Dict("ncqcp1" => ncqcp1test, + "ncqcp2" => ncqcp2test) -@moitestset qcp +@moitestset ncqcp #= SOCP @@ -565,6 +572,7 @@ const socptests = Dict("socp1" => socp1test) const contquadratictests = Dict("qp" => qptest, "qcp" => qcptest, + "ncqcp" => ncqcptest, "socp" => socptest) @moitestset contquadratic true diff --git a/test/Test/contquadratic.jl b/test/Test/contquadratic.jl index 913f2b2c8e..0f8f662247 100644 --- a/test/Test/contquadratic.jl +++ b/test/Test/contquadratic.jl @@ -22,12 +22,14 @@ MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [√2], MOI.FEASIBLE_POINT)) MOIT.qcp3test(mock, config) + end + @testset "Non-convex QCP" begin MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4.0, 1.0], MOI.FEASIBLE_POINT)) - MOIT.qcp4test(mock, config) + MOIT.ncqcp1test(mock, config) MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2.0, 2.0], MOI.FEASIBLE_POINT)) - MOIT.qcp5test(mock, config) + MOIT.ncqcp2test(mock, config) end @testset "SOCP" begin MOIU.set_mock_optimize!(mock, From 1aa0b7cee56dc405438cd1915517df46305aa2f4 Mon Sep 17 00:00:00 2001 From: Robert Schwarz Date: Fri, 29 Mar 2019 16:46:15 +0100 Subject: [PATCH 8/8] add (new) test qcp4 with convex quadratic constraint --- src/Test/contquadratic.jl | 55 +++++++++++++++++++++++++++++++++++++- test/Test/contquadratic.jl | 3 +++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/Test/contquadratic.jl b/src/Test/contquadratic.jl index 443f1098e7..4eebd5c2f1 100644 --- a/src/Test/contquadratic.jl +++ b/src/Test/contquadratic.jl @@ -385,9 +385,62 @@ function qcp3test(model::MOI.ModelLike, config::TestConfig) end end +function qcp4test(model::MOI.ModelLike, config::TestConfig) + atol = config.atol + rtol = config.rtol + # Max x + # s.t. x^2 + x * y + y^2 <= 3 (c) + # y == 1 + + @test MOIU.supports_default_copy_to(model, #=copy_names=# false) + @test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}()) + @test MOI.supports_constraint(model, MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) + + MOI.empty!(model) + @test MOI.is_empty(model) + + x = MOI.add_variable(model) + y = MOI.add_variable(model) + @test MOI.get(model, MOI.NumberOfVariables()) == 2 + + MOI.add_constraint(model, MOI.SingleVariable(y), MOI.EqualTo(1.0)) + + cf = MOI.ScalarQuadraticFunction([MOI.ScalarAffineTerm(0.0, x)], + MOI.ScalarQuadraticTerm.([2.0, 1.0, 2.0], [x, x, y], [x, y, y]), 0.0) + c = MOI.add_constraint(model, cf, MOI.LessThan(3.0)) + @test MOI.get(model, MOI.NumberOfConstraints{MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}}()) == 1 + + MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, x)], 0.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE + + if config.query + @test cf ≈ MOI.get(model, MOI.ConstraintFunction(), c) + end + + 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 + + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 1.0 atol=atol rtol=rtol + + @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 1.0 atol=atol rtol=rtol + @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 1.0 atol=atol rtol=rtol + + @test MOI.get(model, MOI.ConstraintPrimal(), c) ≈ 3.0 atol=atol rtol=rtol + end +end + const qcptests = Dict("qcp1" => qcp1test, "qcp2" => qcp2test, - "qcp3" => qcp3test) + "qcp3" => qcp3test, + "qcp4" => qcp4test) @moitestset qcp diff --git a/test/Test/contquadratic.jl b/test/Test/contquadratic.jl index 0f8f662247..3e7771fc54 100644 --- a/test/Test/contquadratic.jl +++ b/test/Test/contquadratic.jl @@ -22,6 +22,9 @@ MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [√2], MOI.FEASIBLE_POINT)) MOIT.qcp3test(mock, config) + MOIU.set_mock_optimize!(mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0], MOI.FEASIBLE_POINT)) + MOIT.qcp4test(mock, config) end @testset "Non-convex QCP" begin MOIU.set_mock_optimize!(mock,