From f382b1b536880d9166a8a292bff2091bbd32c3a5 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 12:13:04 +1200 Subject: [PATCH 01/23] [Tests] Large refactoring of Test/contlinear.jl The basic design is 'runtests', a single entry point to all solver. Instead of breaking down tests by files or dictionaries, tests are normal Julia functions with descriptive names that can be excluded or included by the user. --- docs/src/submodules/Test/overview.md | 4 +- src/Test/Test.jl | 99 ++++ src/Test/contlinear.jl | 437 ++++++++++----- test/Bridges/Constraint/flip_sign.jl | 9 +- test/Bridges/Constraint/functionize.jl | 2 +- test/Bridges/Constraint/interval.jl | 6 +- test/Bridges/Constraint/ltgt_to_interval.jl | 5 +- test/Bridges/Constraint/slack.jl | 9 +- test/Bridges/Constraint/vectorize.jl | 10 +- test/Bridges/Variable/free.jl | 9 +- test/Bridges/bridge_optimizer.jl | 8 +- test/Bridges/lazy_bridge_optimizer.jl | 10 +- test/Test/contlinear.jl | 588 ++++++++++++-------- test/Utilities/cachingoptimizer.jl | 8 +- test/Utilities/matrix_of_constraints.jl | 2 +- test/Utilities/model.jl | 9 +- test/Utilities/universalfallback.jl | 2 +- 17 files changed, 826 insertions(+), 391 deletions(-) diff --git a/docs/src/submodules/Test/overview.md b/docs/src/submodules/Test/overview.md index c6938f9880..94c1ec5468 100644 --- a/docs/src/submodules/Test/overview.md +++ b/docs/src/submodules/Test/overview.md @@ -82,7 +82,7 @@ function test_modification() end function test_contlinear() - MOI.Test.contlineartest(BRIDGED, CONFIG) + MOI.Test.runtests(OPTIMIZER, CONFIG, include = ["test_linear_"]) end function test_contquadratictest() @@ -90,7 +90,7 @@ function test_contquadratictest() end function test_contconic() - MOI.Test.contlineartest(BRIDGED, CONFIG) + MOI.Test.contconictest(BRIDGED, CONFIG) end function test_intconic() diff --git a/src/Test/Test.jl b/src/Test/Test.jl index b88ad44263..aa7906ca1e 100644 --- a/src/Test/Test.jl +++ b/src/Test/Test.jl @@ -21,4 +21,103 @@ include("nlp.jl") include("UnitTests/unit_tests.jl") +""" + setup_test(::typeof(f), model::MOI.ModelLike, config::Config) + +Overload this method to modify `model` before running the test function `f` on +`model` with `config`. + +This function should either return `nothing`, or return a function which, when +called with zero arguments, undoes the setup to return the model to it's +previous state. + +This is most useful when writing new tests of the tests for MOI, but can also be +used to set test-specific tolerances, etc. + +See also: [`runtests`](@ref) + +## Example + +```julia +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_VariablePrimalStart_partial), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.0]), + ) + mock.eval_variable_constraint_dual = false + + function reset_function() + mock.eval_variable_constraint_dual = true + return + end + return reset_function +end +``` +""" +setup_test(::Any, ::MOI.ModelLike, ::Config) = nothing + +""" + runtests( + model::MOI.ModelLike, + config::Config; + include::Vector{String} = String[], + exclude::Vector{String} = String[], + ) + +Run all tests in `MathOptInterface.Test` on `model`. + +## Configuration arguments + + * `config` is a [`Test.Config`](@ref) object that can be used to modify the + behavior of tests. + * If `include` is not empty, only run tests that contain an element from + `include` in their name. + * If `exclude` is not empty, skip tests that contain an element from `exclude` + in their name. + * `exclude` takes priority over `include`. + +See also: [`setup_test`](@ref). + +## Example + +```julia +config = MathOptInterface.Test.Config() +MathOptInterface.Test.runtests( + model, + config; + include = ["test_linear_"], + exclude = ["VariablePrimalStart"], +) +``` +""" +function runtests( + model::MOI.ModelLike, + config::Config; + include::Vector{String} = String[], + exclude::Vector{String} = String[], +) + for name_sym in names(@__MODULE__; all = true) + name = string(name_sym) + if !startswith(name, "test_") + continue # All test functions start with test_ + elseif !isempty(include) && !any(s -> occursin(s, name), include) + continue + elseif !isempty(exclude) && any(s -> occursin(s, name), exclude) + continue + end + @testset "$(name)" begin + test_function = getfield(@__MODULE__, name_sym) + tear_down = setup_test(test_function, model, config) + test_function(model, config) + if tear_down !== nothing + tear_down() + end + end + end +end + end # module diff --git a/src/Test/contlinear.jl b/src/Test/contlinear.jl index e524740993..c411bbb463 100644 --- a/src/Test/contlinear.jl +++ b/src/Test/contlinear.jl @@ -1,13 +1,16 @@ -# Continuous linear problems +""" + test_linear_integration(model::MOI.ModelLike, config::Config{T}) where {T} -# Basic solver, query, resolve -function linear1test(model::MOI.ModelLike, config::Config{T}) where {T} +This test checks the integration of a sequence of common operations for linear +programs, including adding and deleting variables, and modifying variable +bounds and the objective function. +""" +function test_linear_integration( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol - # simple 2 variable, 1 constraint problem - # min -x - # st x + y <= 1 (x + y - 1 ∈ Nonpositives) - # x, y >= 0 (x, y ∈ Nonnegatives) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -31,9 +34,6 @@ function linear1test(model::MOI.ModelLike, config::Config{T}) where {T} ) @test MOI.supports_constraint(model, MOI.SingleVariable, MOI.EqualTo{T}) @test MOI.supports_constraint(model, MOI.SingleVariable, MOI.GreaterThan{T}) - #@test MOI.get(model, MOI.SupportsAddConstraintAfterSolve()) - #@test MOI.get(model, MOI.SupportsAddVariableAfterSolve()) - #@test MOI.get(model, MOI.SupportsDeleteConstraint()) MOI.empty!(model) @test MOI.is_empty(model) v = MOI.add_variables(model, 2) @@ -69,7 +69,8 @@ function linear1test(model::MOI.ModelLike, config::Config{T}) where {T} MOI.NumberOfConstraints{MOI.SingleVariable,MOI.GreaterThan{T}}(), ) == 2 end - # note: adding some redundant zero coefficients to catch solvers that don't handle duplicate coefficients correctly: + # Note: adding some redundant zero coefficients to catch solvers that don't + # handle duplicate coefficients correctly: objf = MOI.ScalarAffineFunction{T}( MOI.ScalarAffineTerm{ T, @@ -115,7 +116,6 @@ function linear1test(model::MOI.ModelLike, config::Config{T}) where {T} @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol = atol rtol = rtol - # reduced costs @test MOI.get(model, MOI.ConstraintDual(), vc1) ≈ 0 atol = atol rtol = rtol @test MOI.get(model, MOI.ConstraintDual(), vc2) ≈ 1 atol = atol rtol = @@ -299,7 +299,8 @@ function linear1test(model::MOI.ModelLike, config::Config{T}) where {T} # s.t. x + y + z == 2 (c) # x,y >= 0, z = 0 MOI.delete(model, c) - # note: adding some redundant zero coefficients to catch solvers that don't handle duplicate coefficients correctly: + # Note: adding some redundant zero coefficients to catch solvers that don't + # handle duplicate coefficients correctly: cf = MOI.ScalarAffineFunction{T}( MOI.ScalarAffineTerm{ T, @@ -494,8 +495,18 @@ function linear1test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# add_variable (one by one) -function linear2test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_program( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving a simple linear program. +""" +function test_linear_integration_2( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # Min -x @@ -577,7 +588,6 @@ function linear2test(model::MOI.ModelLike, config::Config{T}) where {T} @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol = atol rtol = rtol - # reduced costs @test MOI.get(model, MOI.ConstraintDual(), vc1) ≈ 0 atol = atol rtol = rtol @test MOI.get(model, MOI.ConstraintDual(), vc2) ≈ 1 atol = atol rtol = @@ -592,8 +602,18 @@ function linear2test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# Issue #40 from Gurobi.jl -function linear3test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_inactive_bounds( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +This test checks solving a linear program with bounds that are not binding. +""" +function test_linear_inactive_bounds( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # min x @@ -714,8 +734,18 @@ function linear3test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# Modify GreaterThan{T} and LessThan{T} sets as bounds -function linear4test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_LessThan_and_GreaterThan( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving a linear program with variabless containing lower and upper bounds. +""" +function test_linear_LessThan_and_GreaterThan( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -794,12 +824,20 @@ function linear4test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# Change coeffs, del constr, del var -function linear5test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_integration_modification( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test a range of problem modifications for a linear program. +""" +function test_linear_integration_modification( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol - #@test MOI.get(model, MOI.SupportsDeleteVariable()) - ##################################### # Start from simple LP # Solve it # Copy and solve again @@ -949,8 +987,18 @@ function linear5test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# Modify GreaterThan{T} and LessThan{T} sets as linear constraints -function linear6test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_modify_GreaterThan_and_LessThan_constraints( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test modifying linear constraints. +""" +function test_linear_modify_GreaterThan_and_LessThan_constraints( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -1052,8 +1100,18 @@ function linear6test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# Modify constants in Nonnegatives and Nonpositives -function linear7test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_VectorAffineFunction( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving a linear program provided in vector-form. +""" +function test_linear_VectorAffineFunction( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # Min x - y @@ -1198,8 +1256,18 @@ function linear7test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# infeasible problem -function linear8atest(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_INFEASIBLE( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving an infeasible linear program. +""" +function test_linear_INFEASIBLE( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # min x @@ -1277,8 +1345,18 @@ function linear8atest(model::MOI.ModelLike, config::Config{T}) where {T} end end -# unbounded problem -function linear8btest(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_DUAL_INFEASIBLE( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving a problem that is dual infeasible (unbounded). +""" +function test_linear_DUAL_INFEASIBLE( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # min -x-y @@ -1347,8 +1425,18 @@ function linear8btest(model::MOI.ModelLike, config::Config{T}) where {T} end end -# unbounded problem with unique ray -function linear8ctest(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_DUAL_INFEASIBLE_2( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving a linear program that is dual infeasible (unbounded). +""" +function test_linear_DUAL_INFEASIBLE_2( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # min -x-y @@ -1419,8 +1507,19 @@ function linear8ctest(model::MOI.ModelLike, config::Config{T}) where {T} end end -# add_constraints -function linear9test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_add_constraints( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving a linear program constructed using the plural `add_constraints` +function. +""" +function test_linear_add_constraints( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # maximize 1000 x + 350 y @@ -1518,8 +1617,19 @@ function linear9test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# ranged constraints -function linear10test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_integration_Interval( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +This test checks a variety of properties of a model with +ScalarAffineFunction-in-Interval constraints. +""" +function test_linear_integration_Interval( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # maximize x + y @@ -1586,7 +1696,8 @@ function linear10test(model::MOI.ModelLike, config::Config{T}) where {T} rtol end if config.basis - # There are multiple optimal bases. Either x or y can be in the optimal basis. + # There are multiple optimal bases. Either x or y can be in the + # optimal basis. @test ( MOI.get(model, MOI.VariableBasisStatus(), x) == MOI.BASIC || MOI.get(model, MOI.VariableBasisStatus(), y) == MOI.BASIC @@ -1623,7 +1734,8 @@ function linear10test(model::MOI.ModelLike, config::Config{T}) where {T} rtol end if config.basis - # There are multiple optimal bases. Either x or y can be in the optimal basis." + # There are multiple optimal bases. Either x or y can be in the + # optimal basis. @test ( MOI.get(model, MOI.VariableBasisStatus(), x) == MOI.BASIC || MOI.get(model, MOI.VariableBasisStatus(), y) == MOI.BASIC @@ -1650,7 +1762,8 @@ function linear10test(model::MOI.ModelLike, config::Config{T}) where {T} @test MOI.get(model, MOI.ConstraintPrimal(), c) ≈ 2 atol = atol rtol = rtol if config.basis - # There are multiple optimal bases. Either x or y can be in the optimal basis. + # There are multiple optimal bases. Either x or y can be in the + # optimal basis. @test ( MOI.get(model, MOI.VariableBasisStatus(), x) == MOI.BASIC || MOI.get(model, MOI.VariableBasisStatus(), y) == MOI.BASIC @@ -1683,7 +1796,8 @@ function linear10test(model::MOI.ModelLike, config::Config{T}) where {T} @test MOI.get(model, MOI.ConstraintPrimal(), c) ≈ 12 atol = atol rtol = rtol if config.basis - # There are multiple optimal bases. Either x or y can be in the optimal basis. + # There are multiple optimal bases. Either x or y can be in the + # optimal basis. @test ( MOI.get(model, MOI.VariableBasisStatus(), x) == MOI.BASIC || MOI.get(model, MOI.VariableBasisStatus(), y) == MOI.BASIC @@ -1694,8 +1808,19 @@ function linear10test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# inactive ranged constraints -function linear10btest(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_Interval_inactive( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +This test checks linear programs with a non-binding +ScalarAffineFunction-in-Interval constraint. +""" +function test_linear_Interval_inactive( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # minimize x + y @@ -1775,41 +1900,51 @@ function linear10btest(model::MOI.ModelLike, config::Config{T}) where {T} end end -# changing constraint sense -function linear11test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_transform( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +This test checks calling `MOI.transform` to flip the sense of constraints. + +It starts with: +``` +min x + y +st x + y >= 1 + x + y >= 2 + sol: x+y = 2 (degenerate) +``` +with the dual problem: +``` +max w + 2z +st w + z == 1 + w + z == 1 + w, z >= 0 +sol: z = 1, w = 0 +``` +Then it tranforms problem into: +``` +min x + y +st x + y >= 1 + x + y <= 2 +sol: x+y = 1 (degenerate) +``` +with the dual problem: +``` +max w + 2z +st w + z == 1 + w + z == 1 + w >= 0, z <= 0 +sol: w = 1, z = 0 +``` +""" +function test_linear_transform( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol - # simple 2 variable, 1 constraint problem - # - # starts with - # - # min x + y - # st x + y >= 1 - # x + y >= 2 - # sol: x+y = 2 (degenerate) - # - # with dual - # - # max w + 2z - # st w + z == 1 - # w + z == 1 - # w, z >= 0 - # sol: z = 1, w = 0 - # - # tranforms problem into: - # - # min x + y - # st x + y >= 1 - # x + y <= 2 - # sol: x+y = 1 (degenerate) - # - # with dual - # - # max w + 2z - # st w + z == 1 - # w + z == 1 - # w >= 0, z <= 0 - # sol: w = 1, z = 0 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -1878,14 +2013,28 @@ function linear11test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# infeasible problem with 2 linear constraints -function linear12test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_INFEASIBLE_2( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving an infeasible linear program. + +The model is +``` + min x +s.t. 2x - 3y <= -7 + y <= 2 + x, y >= 0 +``` +""" +function test_linear_INFEASIBLE_2( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol - # min x - # s.t. 2x-3y <= -7 - # y <= 2 - # x,y >= 0 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -1946,7 +2095,6 @@ function linear12test(model::MOI.ModelLike, config::Config{T}) where {T} MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE_OR_UNBOUNDED if config.duals && config.infeas_certificates - # solver returned an infeasibility ray @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE @@ -1963,13 +2111,27 @@ function linear12test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# feasibility problem -function linear13test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_FEASIBILITY_SENSE( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +Test solving a linear program with no objective function. + +The problem is +``` +Find x, y +s.t. 2x + 3y >= 1 + x - y == 0 +``` +""" +function test_linear_FEASIBILITY_SENSE( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol - # find x, y - # s.t. 2x + 3y >= 1 - # x - y == 0 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports_constraint( model, @@ -2029,8 +2191,18 @@ function linear13test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# Deletion of vector of variables -function linear14test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_integration_delete_variables( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +This function tests calling `delete(::ModelLike, ::Vector{VariableIndex})`. +""" +function test_linear_integration_delete_variables( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol # max x + 2y + 3z + 4 @@ -2121,7 +2293,6 @@ function linear14test(model::MOI.ModelLike, config::Config{T}) where {T} @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol = atol rtol = rtol - # reduced costs @test MOI.get(model, MOI.ConstraintDual(), clbx) ≈ 2 atol = atol rtol = rtol @test MOI.get(model, MOI.ConstraintDual(), clby) ≈ 0 atol = atol rtol = @@ -2161,20 +2332,29 @@ function linear14test(model::MOI.ModelLike, config::Config{T}) where {T} @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol = atol rtol = rtol - # reduced costs @test MOI.get(model, MOI.ConstraintDual(), clby) ≈ 0 atol = atol rtol = rtol end end end -# Empty vector affine function rows (LQOI Issue #48) -function linear15test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_VectorAffineFunction_empty_row( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +This test checks solving a problem given a `VectorAffineFunction` with an empty +row equivalent to `0 == 0`. + +Models not supporting `VectorAffineFunction`-in-`Zeros` should use a bridge. +""" +function test_linear_VectorAffineFunction_empty_row( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol - # minimize 0 - # s.t. 0 == 0 - # x == 1 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -2185,9 +2365,9 @@ function linear15test(model::MOI.ModelLike, config::Config{T}) where {T} MOI.empty!(model) @test MOI.is_empty(model) x = MOI.add_variables(model, 1) - # Create a VectorAffineFunction with two rows, but only - # one term, belonging to the second row. The first row, - # which is empty, is essentially a constraint that 0 == 0. + # Create a VectorAffineFunction with two rows, but only one term, belonging + # to the second row. The first row, which is empty, is essentially a + # constraint that 0 == 0. c = MOI.add_constraint( model, MOI.VectorAffineFunction{T}( @@ -2223,15 +2403,32 @@ function linear15test(model::MOI.ModelLike, config::Config{T}) where {T} end end -# This test can be passed by solvers that don't support VariablePrimalStart -# because copy_to drops start information with a warning. -function partial_start_test(model::MOI.ModelLike, config::Config{T}) where {T} +""" + test_linear_VariablePrimalStart_partial( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} + +This test checks solving a problem with a partial solution provided via +VariablePrimalStart. + +This test can be passed by solvers that don't support VariablePrimalStart +because copy_to drops start information with a warning. + +The problem is +``` + max 2x + y +s.t. x + y <= 1 + x, y >= 0 +``` +where `x` starts at `one(T)`. Start point for `y` is unspecified. +""" +function test_linear_VariablePrimalStart_partial( + model::MOI.ModelLike, + config::Config{T}, +) where {T} atol = config.atol rtol = config.rtol - # maximize 2x + y - # s.t. x + y <= 1 - # x, y >= 0 - # x starts at one(T). Start point for y is unspecified. MOI.empty!(model) @test MOI.is_empty(model) x = MOI.add_variable(model) @@ -2262,27 +2459,3 @@ function partial_start_test(model::MOI.ModelLike, config::Config{T}) where {T} rtol end end - -const contlineartests = Dict( - "linear1" => linear1test, - "linear2" => linear2test, - "linear3" => linear3test, - "linear4" => linear4test, - "linear5" => linear5test, - "linear6" => linear6test, - "linear7" => linear7test, - "linear8a" => linear8atest, - "linear8b" => linear8btest, - "linear8c" => linear8ctest, - "linear9" => linear9test, - "linear10" => linear10test, - "linear10b" => linear10btest, - "linear11" => linear11test, - "linear12" => linear12test, - "linear13" => linear13test, - "linear14" => linear14test, - "linear15" => linear15test, - "partial_start" => partial_start_test, -) - -@moitestset contlinear diff --git a/test/Bridges/Constraint/flip_sign.jl b/test/Bridges/Constraint/flip_sign.jl index baa137ea61..d23c6b1513 100644 --- a/test/Bridges/Constraint/flip_sign.jl +++ b/test/Bridges/Constraint/flip_sign.jl @@ -33,7 +33,10 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100.0, -100.0]), ) - MOIT.linear6test(bridged_mock, config) + MOIT.test_linear_modify_GreaterThan_and_LessThan_constraints( + bridged_mock, + config, + ) ci = first( MOI.get( @@ -168,7 +171,7 @@ end (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100.0, -100.0]), ) - MOIT.linear7test(bridged_mock, config) + MOIT.test_linear_VectorAffineFunction(bridged_mock, config) ci = first( MOI.get( @@ -250,7 +253,7 @@ end (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100.0, -100.0]), ) - MOIT.linear7test(bridged_mock, config) + MOIT.test_linear_VectorAffineFunction(bridged_mock, config) MOIU.set_mock_optimize!( mock, diff --git a/test/Bridges/Constraint/functionize.jl b/test/Bridges/Constraint/functionize.jl index 13d7f24796..6f36983c7e 100644 --- a/test/Bridges/Constraint/functionize.jl +++ b/test/Bridges/Constraint/functionize.jl @@ -70,7 +70,7 @@ config_with_basis = MOIT.Config(basis = true) var_basis = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], ), ) - MOIT.linear2test(bridged_mock, config_with_basis) + MOIT.test_linear_integration_2(bridged_mock, config_with_basis) cis = MOI.get( bridged_mock, diff --git a/test/Bridges/Constraint/interval.jl b/test/Bridges/Constraint/interval.jl index b8f35ec140..f3ad338c06 100644 --- a/test/Bridges/Constraint/interval.jl +++ b/test/Bridges/Constraint/interval.jl @@ -85,7 +85,7 @@ end var_basis = [MOI.BASIC, MOI.BASIC], ), ) - MOIT.linear10test(bridged_mock, config_with_basis) + MOIT.test_linear_integration_Interval(bridged_mock, config_with_basis) MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -106,7 +106,7 @@ end [0], ), ) - MOIT.linear10btest(bridged_mock, config_with_basis) + MOIT.test_linear_Interval_inactive(bridged_mock, config_with_basis) ci = first( MOI.get( @@ -176,7 +176,7 @@ end (MOI.ScalarAffineFunction{T}, MOI.LessThan{T}) => zeros(T, 1), ), ) - MOIT.linear13test(bridged_mock, MOIT.Config{T}()) + MOIT.test_linear_FEASIBILITY_SENSE(bridged_mock, MOIT.Config{T}()) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/ltgt_to_interval.jl b/test/Bridges/Constraint/ltgt_to_interval.jl index 35a579bd4f..f0badd7afe 100644 --- a/test/Bridges/Constraint/ltgt_to_interval.jl +++ b/test/Bridges/Constraint/ltgt_to_interval.jl @@ -34,7 +34,10 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100.0, -100.0]), ) - MOIT.linear6test(bridged_mock, config) + MOIT.test_linear_modify_GreaterThan_and_LessThan_constraints( + bridged_mock, + config, + ) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/slack.jl b/test/Bridges/Constraint/slack.jl index e1c66b8826..4250a8229a 100644 --- a/test/Bridges/Constraint/slack.jl +++ b/test/Bridges/Constraint/slack.jl @@ -94,7 +94,10 @@ config = MOIT.Config() ], ), ) - MOIT.linear2test(bridged_mock, MOIT.Config(duals = false, basis = true)) + MOIT.test_linear_integration_2( + bridged_mock, + MOIT.Config(duals = false, basis = true), + ) c1 = MOI.get( bridged_mock, MOI.ListOfConstraintIndices{ @@ -119,7 +122,7 @@ config = MOIT.Config() (MOI.SingleVariable, MOI.LessThan{Float64}) => [0], ), ) - MOIT.linear11test(bridged_mock, MOIT.Config(duals = false)) + MOIT.test_linear_transform(bridged_mock, MOIT.Config(duals = false)) c1 = MOI.get( bridged_mock, @@ -253,7 +256,7 @@ end (MOI.VectorOfVariables, MOI.Nonpositives) => [[1.0]], ), ) - MOIT.linear7test(bridged_mock, config) + MOIT.test_linear_VectorAffineFunction(bridged_mock, config) c1 = MOI.get( bridged_mock, diff --git a/test/Bridges/Constraint/vectorize.jl b/test/Bridges/Constraint/vectorize.jl index 173da8a984..25d0b40de4 100644 --- a/test/Bridges/Constraint/vectorize.jl +++ b/test/Bridges/Constraint/vectorize.jl @@ -44,7 +44,7 @@ config = MOIT.Config() [[0], [1]], ), ) - MOIT.linear2test(bridged_mock, config) + MOIT.test_linear_integration_2(bridged_mock, config) MOIU.set_mock_optimize!( mock, @@ -52,7 +52,7 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), ) - MOIT.linear4test(bridged_mock, config) + MOIT.test_linear_LessThan_and_GreaterThan(bridged_mock, config) MOIU.set_mock_optimize!( mock, @@ -61,7 +61,7 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4, 0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2]), ) - MOIT.linear5test(mock, config) + MOIT.test_linear_integration_modification(mock, config) MOIU.set_mock_optimize!( mock, @@ -69,7 +69,7 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), ) - MOIT.linear6test(mock, config) + MOIT.test_linear_modify_GreaterThan_and_LessThan_constraints(mock, config) MOIU.set_mock_optimize!( mock, @@ -90,7 +90,7 @@ config = MOIT.Config() ) # linear14 has double variable bounds for the z variable mock.eval_variable_constraint_dual = false - MOIT.linear14test(bridged_mock, config) + MOIT.test_linear_integration_delete_variables(bridged_mock, config) mock.eval_variable_constraint_dual = true mock.optimize! = diff --git a/test/Bridges/Variable/free.jl b/test/Bridges/Variable/free.jl index cb0d540168..dfee3977d5 100644 --- a/test/Bridges/Variable/free.jl +++ b/test/Bridges/Variable/free.jl @@ -98,7 +98,10 @@ end [-1.0], ), ) - MOIT.linear6test(bridged_mock, config) + MOIT.test_linear_modify_GreaterThan_and_LessThan_constraints( + bridged_mock, + config, + ) loc = MOI.get(bridged_mock, MOI.ListOfConstraintTypesPresent()) @test length(loc) == 2 @@ -158,7 +161,7 @@ end ) end set_mock_optimize_linear7Test!(mock) - MOIT.linear7test(bridged_mock, config) + MOIT.test_linear_VectorAffineFunction(bridged_mock, config) x, y = MOI.get(bridged_mock, MOI.ListOfVariableIndices()) @@ -195,7 +198,7 @@ end (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 0.5, 0.0, 0.0]), ) - MOIT.linear11test(bridged_mock, config) + MOIT.test_linear_transform(bridged_mock, config) vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices()) @test vis == MOI.VariableIndex.([-1, -2]) diff --git a/test/Bridges/bridge_optimizer.jl b/test/Bridges/bridge_optimizer.jl index fce8be17d6..abcf350e7b 100644 --- a/test/Bridges/bridge_optimizer.jl +++ b/test/Bridges/bridge_optimizer.jl @@ -457,8 +457,12 @@ end end @testset "Continuous Linear" begin - exclude = ["partial_start"] # VariablePrimalStart not supported. - MOIT.contlineartest(bridged_mock, MOIT.Config(solve = false), exclude) + MOI.Test.runtests( + bridged_mock, + MOIT.Config(solve = false), + include = ["test_linear_"], + exclude = ["VariablePrimalStart"], + ) end @testset "Show" begin diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index 8dfcd2573f..5ad4e61178 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -1152,10 +1152,14 @@ end # is equivalent to # `Constraint.VectorizeBridge` -> `Constraint.VectorSlackBridge` # however, `Variable.VectorizeBridge` do not support modification of the - # set hence it makes some tests of `contlineartest` fail so we disable it. + # set hence it makes some tests fail so we disable it. MOIB.remove_bridge(bridged, MOIB.Constraint.ScalarSlackBridge{T}) - exclude = ["partial_start"] # `VariablePrimalStart` not supported. - MOIT.contlineartest(bridged, MOIT.Config{T}(solve = false), exclude) + MOI.Test.runtests( + bridged, + MOIT.Config{T}(solve = false), + include = ["test_linear_"], + exclude = ["VariablePrimalStart"], + ) end @testset "SDPAModel with bridges and caching" begin diff --git a/test/Test/contlinear.jl b/test/Test/contlinear.jl index 594a0ed342..02ebf51b6d 100644 --- a/test/Test/contlinear.jl +++ b/test/Test/contlinear.jl @@ -1,15 +1,15 @@ using Test import MathOptInterface + const MOI = MathOptInterface -const MOIT = MOI.Test const MOIU = MOI.Utilities -mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) -config = MOIT.Config(basis = true) -config_no_lhs_modif = MOIT.Config(modify_lhs = false) - -function set_mock_optimize_linear1Test!(mock) - return MOIU.set_mock_optimize!( +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_integration), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, +) + MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, @@ -42,275 +42,411 @@ function set_mock_optimize_linear1Test!(mock) [0.5], ), ) + return end -set_mock_optimize_linear1Test!(mock) -MOIT.linear1test(mock, config) -set_mock_optimize_linear1Test!(mock) -MOIT.linear1test(mock, config_no_lhs_modif) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_integration_2), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, +) + MOIU.set_mock_optimize!( mock, - [1, 0], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], - con_basis = [ + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 0], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [MOI.NONBASIC], - ], - var_basis = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], - ), + [-1], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], + ], + var_basis = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], + ), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_inactive_bounds), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear2test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [3], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [MOI.NONBASIC], - ], - var_basis = [MOI.BASIC], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - [0], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [MOI.BASIC], - ], - var_basis = [MOI.NONBASIC_AT_UPPER], - ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [3], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.NONBASIC], + ], + var_basis = [MOI.BASIC], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], + ], + var_basis = [MOI.NONBASIC_AT_UPPER], + ), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_LessThan_and_GreaterThan), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear3test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_integration_modification), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear4test(mock, config) -function set_mock_optimize_linear5Test!(mock) - return MOIU.set_mock_optimize!( + MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4 / 3, 4 / 3]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2, 0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4, 0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2]), ) + return end -set_mock_optimize_linear5Test!(mock) -MOIT.linear5test(mock, config) -set_mock_optimize_linear5Test!(mock) -MOIT.linear5test(mock, config_no_lhs_modif) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_modify_GreaterThan_and_LessThan_constraints), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear6test(mock, config) -function set_mock_optimize_linear7Test!(mock) - return MOIU.set_mock_optimize!( + MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), ) + return end -set_mock_optimize_linear7Test!(mock) -MOIT.linear7test(mock, config) -set_mock_optimize_linear7Test!(mock) -MOIT.linear7test(mock, config_no_lhs_modif) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - tuple(), - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], - ), -) -MOIT.linear8atest(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.INFEASIBLE), + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_VectorAffineFunction), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear8atest(mock, MOIT.Config(infeas_certificates = false)) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - MOI.DUAL_INFEASIBLE, - MOI.INFEASIBILITY_CERTIFICATE, - ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_INFEASIBLE), + mock::MOIU.MockOptimizer, + config::MOI.Test.Config, ) -MOIT.linear8btest(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE), + if config.infeas_certificates + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + tuple(), + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], + ), + ) + else + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.INFEASIBLE), + ) + end + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_DUAL_INFEASIBLE), + mock::MOIU.MockOptimizer, + config::MOI.Test.Config, ) -MOIT.linear8btest(mock, MOIT.Config(infeas_certificates = false)) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + primal_status = if config.infeas_certificates + MOI.INFEASIBILITY_CERTIFICATE + else + MOI.NO_SOLUTION + end + MOIU.set_mock_optimize!( mock, - MOI.DUAL_INFEASIBLE, - (MOI.INFEASIBILITY_CERTIFICATE, [1, 1]), - ), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE, primal_status), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_DUAL_INFEASIBLE_2), + mock::MOIU.MockOptimizer, + config::MOI.Test.Config, ) -MOIT.linear8ctest(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE), + if config.infeas_certificates + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.DUAL_INFEASIBLE, + (MOI.INFEASIBILITY_CERTIFICATE, [1, 1]), + ), + ) + else + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE), + ) + end + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_add_constraints), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear8ctest(mock, MOIT.Config(infeas_certificates = false)) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - [650 / 11, 400 / 11], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [MOI.NONBASIC, MOI.NONBASIC], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [MOI.BASIC], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [650 / 11, 400 / 11], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC, MOI.NONBASIC], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], + ], + var_basis = [MOI.BASIC, MOI.BASIC], + ), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_integration_Interval), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear9test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - [5.0, 5.0], - con_basis = [ + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [5.0, 5.0], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], + ], + var_basis = [MOI.BASIC, MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [MOI.NONBASIC_AT_UPPER], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [2.5, 2.5], - con_basis = [ + [-1], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [2.5, 2.5], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], + ], + var_basis = [MOI.BASIC, MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [MOI.NONBASIC_AT_LOWER], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1], - con_basis = [ + [1], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 1.0], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [MOI.NONBASIC_AT_LOWER], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [6.0, 6.0], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1], - con_basis = [ + [1], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], + ], + var_basis = [MOI.BASIC, MOI.BASIC], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [6.0, 6.0], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [MOI.NONBASIC_AT_UPPER], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - ), + [-1], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], + ], + var_basis = [MOI.BASIC, MOI.BASIC], + ), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_Interval_inactive), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear10test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - [0.0, 0.0], - con_basis = [ + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.0, 0.0], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.BASIC], + ], + var_basis = [MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_LOWER], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [MOI.BASIC], - ], - var_basis = [MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_LOWER], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [0], - ), -) -MOIT.linear10btest(mock, config) + [0], + ), + ) + return +end -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 0.5]), +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_transform), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear11test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - MOI.INFEASIBLE, - tuple(), - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [-1, -1], - ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 0.5]), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_INFEASIBLE_2), + mock::MOIU.MockOptimizer, + config::MOI.Test.Config, ) -MOIT.linear12test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.INFEASIBLE), + if config.infeas_certificates + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + tuple(), + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1, -1], + ), + ) + else + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.INFEASIBLE), + ) + end + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_FEASIBILITY_SENSE), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear12test(mock, MOIT.Config(infeas_certificates = false)) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - [1 / 5, 1 / 5], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [0], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [0], - ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1 / 5, 1 / 5], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [0], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [0], + ), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_integration_delete_variables), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -MOIT.linear13test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - [0, 1 / 2, 1], - con_basis = [ + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0, 1 / 2, 1], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], + ], + var_basis = [ + MOI.NONBASIC_AT_LOWER, + MOI.BASIC, + MOI.NONBASIC_AT_UPPER, + ], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2, 0, 0], + (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [MOI.NONBASIC], - ], - var_basis = [MOI.NONBASIC_AT_LOWER, MOI.BASIC, MOI.NONBASIC_AT_UPPER], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2, 0, 0], - (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + [-1], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [0], + ), + ) + # test_linear_integration_delete_variables has double variable bounds for + # the z variable + mock.eval_variable_constraint_dual = false + function reset_function() + mock.eval_variable_constraint_dual = true + return + end + return reset_function +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_VectorAffineFunction_empty_row), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, +) + MOIU.set_mock_optimize!( mock, - [1], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [0], - ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.0], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[0.0, 0.0]], + ), + ) + return +end + +function MOI.Test.setup_test( + ::typeof(MOI.Test.test_linear_VariablePrimalStart_partial), + mock::MOIU.MockOptimizer, + ::MOI.Test.Config, ) -# linear14 has double variable bounds for the z variable -mock.eval_variable_constraint_dual = false -MOIT.linear14test(mock, config) -mock.eval_variable_constraint_dual = true -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + MOIU.set_mock_optimize!( mock, - [0.0], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[0.0, 0.0]], - ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.0]), + ) + return +end + +MOI.Test.runtests( + MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), + MOI.Test.Config(basis = true), + include = ["test_linear_"], + # Oops! Name clash. + exclude = ["test_linear_mixed_complementarity"], ) -MOIT.linear15test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.0]), +MOI.Test.runtests( + MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), + MOI.Test.Config(modify_lhs = false), + include = ["test_linear_"], + # Oops! Name clash. + exclude = ["test_linear_mixed_complementarity"], ) -MOIT.partial_start_test(mock, config) diff --git a/test/Utilities/cachingoptimizer.jl b/test/Utilities/cachingoptimizer.jl index 66ceb85692..db5bdbaef7 100644 --- a/test/Utilities/cachingoptimizer.jl +++ b/test/Utilities/cachingoptimizer.jl @@ -570,8 +570,12 @@ for state in (MOIU.NO_OPTIMIZER, MOIU.EMPTY_OPTIMIZER, MOIU.ATTACHED_OPTIMIZER) MOIT.unittest(m, config) end @testset "Continuous Linear" begin - exclude = ["partial_start"] # VariablePrimalStart not supported. - MOIT.contlineartest(m, config, exclude) + MOI.Test.runtests( + m, + config, + include = ["test_linear_"], + exclue = ["VariablePrimalStart"], + ) end end end diff --git a/test/Utilities/matrix_of_constraints.jl b/test/Utilities/matrix_of_constraints.jl index cb83a2b063..20c1d742e7 100644 --- a/test/Utilities/matrix_of_constraints.jl +++ b/test/Utilities/matrix_of_constraints.jl @@ -187,7 +187,7 @@ MOIU.@product_of_sets( F = MOI.ScalarAffineFunction{Float64} @testset "$SetType" for SetType in [MixLP{Float64}, OrdLP{Float64}] _test( - MOIT.linear2test, + MOIT.test_linear_integration_2, MOI.Utilities.Box{Float64}, SetType, A2, diff --git a/test/Utilities/model.jl b/test/Utilities/model.jl index d1f733b893..7ca72b8460 100644 --- a/test/Utilities/model.jl +++ b/test/Utilities/model.jl @@ -236,9 +236,12 @@ end end @testset "Continuous Linear tests" begin - config = MOIT.Config(solve = false) - exclude = ["partial_start"] # Model doesn't support VariablePrimalStart. - MOIT.contlineartest(MOIU.Model{Float64}(), config, exclude) + MOI.Test.runtests( + MOIU.Model{Float64}(), + MOIT.Config(solve = false), + include = ["test_linear_"], + exclude = ["VariablePrimalStart"], + ) end @testset "Continuous Conic tests" begin diff --git a/test/Utilities/universalfallback.jl b/test/Utilities/universalfallback.jl index 2df241592f..2694e9ba64 100644 --- a/test/Utilities/universalfallback.jl +++ b/test/Utilities/universalfallback.jl @@ -297,7 +297,7 @@ end MOIT.modificationtest(uf, config) end @testset "Continuous Linear" begin - MOIT.contlineartest(uf, config) + MOI.Test.runtests(uf, config, include = ["test_linear_"]) end @testset "Duplicate names" begin From e2da0478f9062225339176a717d139773f93ba01 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 15:21:05 +1200 Subject: [PATCH 02/23] Move the tests' tests into the same files and migrate UnitTests/variables.jl This makes things much easier: you now write the test and a check with MockOptimizer in a single place. It's also a demonstration that this works for unit tests as well as the larger integration tests. --- src/Test/UnitTests/variables.jl | 392 ++++++++++++++-------- src/Test/contlinear.jl | 431 ++++++++++++++++++++++++ test/Bridges/bridge_optimizer.jl | 2 +- test/Bridges/lazy_bridge_optimizer.jl | 2 +- test/Test/Test.jl | 24 ++ test/Test/contlinear.jl | 452 -------------------------- test/Test/unit.jl | 63 ---- test/Utilities/cachingoptimizer.jl | 5 +- test/Utilities/universalfallback.jl | 7 +- 9 files changed, 721 insertions(+), 657 deletions(-) delete mode 100644 test/Test/contlinear.jl diff --git a/src/Test/UnitTests/variables.jl b/src/Test/UnitTests/variables.jl index 54c042504d..c1373c60a2 100644 --- a/src/Test/UnitTests/variables.jl +++ b/src/Test/UnitTests/variables.jl @@ -1,55 +1,37 @@ -#= - Functions in this file test functionality relating to variables in MOI. - -### Functionality currently tested - - add_variables - - add_variable - - deleting variables - - get/set VariableName - - is_valid for VariableIndex - - get VariableIndex by name - - NumberOfVariables - -### Functionality not yet tested - - VariablePrimalStart - - VariablePrimal - - ListOfVariableIndices -=# - """ - add_variable(model::MOI.ModelLike, config::Config) + test_add_variable(model::MOI.ModelLike, config::Config) Test adding a single variable. """ -function add_variable(model::MOI.ModelLike, config::Config) +function test_add_variable(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) @test MOI.get(model, MOI.NumberOfVariables()) == 0 v = MOI.add_variable(model) @test MOI.get(model, MOI.NumberOfVariables()) == 1 + return end -unittests["add_variable"] = add_variable """ - add_variables(model::MOI.ModelLike, config::Config) + test_add_variables(model::MOI.ModelLike, config::Config) Test adding multiple variables. """ -function add_variables(model::MOI.ModelLike, config::Config) +function test_add_variables(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) @test MOI.get(model, MOI.NumberOfVariables()) == 0 v = MOI.add_variables(model, 2) @test MOI.get(model, MOI.NumberOfVariables()) == 2 + return end -unittests["add_variables"] = add_variables """ - delete_variable(model::MOI.ModelLike, config::Config) + test_delete_variable(model::MOI.ModelLike, config::Config) Tess adding, and then deleting, a single variable. """ -function delete_variable(model::MOI.ModelLike, config::Config) +function test_delete_variable(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) @test MOI.get(model, MOI.NumberOfVariables()) == 0 @@ -57,15 +39,15 @@ function delete_variable(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.NumberOfVariables()) == 1 MOI.delete(model, v) @test MOI.get(model, MOI.NumberOfVariables()) == 0 + return end -unittests["delete_variable"] = delete_variable """ - delete_variables(model::MOI.ModelLike, config::Config) + test_delete_variables(model::MOI.ModelLike, config::Config) Test adding, and then deleting, multiple variables. """ -function delete_variables(model::MOI.ModelLike, config::Config) +function test_delete_variables(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) @test MOI.get(model, MOI.NumberOfVariables()) == 0 @@ -85,15 +67,15 @@ function delete_variables(model::MOI.ModelLike, config::Config) end @test !MOI.is_valid(model, v[1]) @test MOI.is_valid(model, v[2]) + return end -unittests["delete_variables"] = delete_variable """ - delete_nonnegative_variables(model::MOI.ModelLike, config::Config) + test_delete_nonnegative_variables(model::MOI.ModelLike, config::Config) Test adding, and then deleting, nonnegative variables. """ -function delete_nonnegative_variables(model::MOI.ModelLike, config::Config) +function test_delete_nonnegative_variables(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) @test MOI.get(model, MOI.NumberOfVariables()) == 0 @@ -113,17 +95,17 @@ function delete_nonnegative_variables(model::MOI.ModelLike, config::Config) @test_throws MOI.InvalidIndex(v[1]) MOI.delete(model, v[1]) @test !MOI.is_valid(model, cv) @test MOI.get(model, MOI.NumberOfVariables()) == 0 + return end -unittests["delete_nonnegative_variables"] = delete_nonnegative_variables """ - update_dimension_nonnegative_variables(model::MOI.ModelLike, config::Config) + test_update_dimension_nonnegative_variables(model::MOI.ModelLike, ::Config) Test adding, and then deleting one by one, nonnegative variables. """ -function update_dimension_nonnegative_variables( +function test_update_dimension_nonnegative_variables( model::MOI.ModelLike, - config::Config, + ::Config, ) MOI.empty!(model) @test MOI.is_empty(model) @@ -145,17 +127,18 @@ function update_dimension_nonnegative_variables( @test !MOI.is_valid(model, v[2]) @test_throws MOI.InvalidIndex(v[2]) MOI.delete(model, v[2]) @test !MOI.is_valid(model, cv) + return end -unittests["update_dimension_nonnegative_variables"] = - update_dimension_nonnegative_variables """ - delete_soc_variables(model::MOI.ModelLike, config::Config) + test_delete_soc_variables(model::MOI.ModelLike, config::Config) Test adding, and then deleting, second-order cone variables. """ -function delete_soc_variables(model::MOI.ModelLike, config::Config) - MOI.supports_add_constrained_variables(model, MOI.SecondOrderCone) || return +function test_delete_soc_variables(model::MOI.ModelLike, config::Config) + if !MOI.supports_add_constrained_variables(model, MOI.SecondOrderCone) + return + end MOI.empty!(model) @test MOI.is_empty(model) @test MOI.get(model, MOI.NumberOfVariables()) == 0 @@ -171,15 +154,15 @@ function delete_soc_variables(model::MOI.ModelLike, config::Config) v, cv = MOI.add_constrained_variables(model, MOI.SecondOrderCone(3)) @test MOI.get(model, MOI.NumberOfVariables()) == 3 @test_throws MOI.DeleteNotAllowed MOI.delete(model, v[1]) + return end -unittests["delete_soc_variables"] = delete_soc_variables """ - getvariable(model::MOI.ModelLike, config::Config) + test_get_variable(model::MOI.ModelLike, config::Config) Test getting variables by name. """ -function getvariable(model::MOI.ModelLike, config::Config) +function test_get_variable_by_name(model::MOI.ModelLike, ::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -193,15 +176,15 @@ function getvariable(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.VariableIndex, "y") === nothing x = MOI.get(model, MOI.VariableIndex, "x") @test MOI.is_valid(model, x) + return end -unittests["getvariable"] = getvariable """ - variablenames(model::MOI.ModelLike, config::Config) + test_VariableName(model::MOI.ModelLike, config::Config) Test getting and setting variable names. """ -function variablenames(model::MOI.ModelLike, config::Config) +function test_VariableName(model::MOI.ModelLike, ::Config) MOI.empty!(model) v = MOI.add_variable(model) @test MOI.get(model, MOI.VariableName(), v) == "" @@ -213,17 +196,16 @@ function variablenames(model::MOI.ModelLike, config::Config) x = MOI.add_variable(model) MOI.set(model, MOI.VariableName(), x, "x") @test MOI.get(model, MOI.VariableName(), x) == "x" + return end -unittests["variablenames"] = variablenames """ - solve_with_upperbound(model::MOI.ModelLike, config::Config) + test_solve_with_upperbound(model::MOI.ModelLike, config::Config) Test setting the upper bound of a variable, confirm that it solves correctly, and if `config.duals=true`, check that the dual is computed correctly. """ -function solve_with_upperbound(model::MOI.ModelLike, config::Config) - atol, rtol = config.atol, config.rtol +function test_solve_with_upperbound(model::MOI.ModelLike, config::Config) MOI.empty!(model) @test MOI.is_empty(model) MOIU.loadfromstring!( @@ -244,7 +226,7 @@ function solve_with_upperbound(model::MOI.ModelLike, config::Config) x.value, ) @test c2.value == x.value - return test_model_solution( + test_model_solution( model, config; objective_value = 2.0, @@ -252,17 +234,36 @@ function solve_with_upperbound(model::MOI.ModelLike, config::Config) constraint_primal = [(c1, 1.0), (c2, 1.0)], constraint_dual = [(c1, -2.0), (c2, 0.0)], ) + return +end + +function setup_test( + ::typeof(test_solve_with_upperbound), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1]), + MOI.FEASIBLE_POINT, + (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [0.0], + ), + ) + model.eval_variable_constraint_dual = false + return () -> model.eval_variable_constraint_dual = true end -unittests["solve_with_upperbound"] = solve_with_upperbound """ - solve_with_lowerbound(model::MOI.ModelLike, config::Config) + test_solve_with_lowerbound(model::MOI.ModelLike, config::Config) Test setting the lower bound of a variable, confirm that it solves correctly, and if `config.duals=true`, check that the dual is computed correctly. """ -function solve_with_lowerbound(model::MOI.ModelLike, config::Config) - atol, rtol = config.atol, config.rtol +function test_solve_with_lowerbound(model::MOI.ModelLike, config::Config) MOI.empty!(model) @test MOI.is_empty(model) MOIU.loadfromstring!( @@ -281,7 +282,7 @@ function solve_with_lowerbound(model::MOI.ModelLike, config::Config) @test c1.value == x.value c2 = MOI.ConstraintIndex{MOI.SingleVariable,MOI.LessThan{Float64}}(x.value) @test c2.value == x.value - return test_model_solution( + test_model_solution( model, config; objective_value = 2.0, @@ -289,94 +290,209 @@ function solve_with_lowerbound(model::MOI.ModelLike, config::Config) constraint_primal = [(c1, 1.0), (c2, 1.0)], constraint_dual = [(c1, 2.0), (c2, 0.0)], ) + return +end + +function setup_test( + ::typeof(test_solve_with_lowerbound), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1]), + MOI.FEASIBLE_POINT, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0], + (MOI.SingleVariable, MOI.LessThan{Float64}) => [0.0], + ), + ) + model.eval_variable_constraint_dual = false + return () -> model.eval_variable_constraint_dual = true end -unittests["solve_with_lowerbound"] = solve_with_lowerbound """ - solve_integer_edge_cases(model::MOI.ModelLike, config::Config) + test_solve_Integer_with_lower_bound( + model::MOI.ModelLike, + config::Config, + ) -Test a variety of edge cases related to binary and integer variables. +Test an integer variable with fractional lower bound. """ -function solve_integer_edge_cases(model::MOI.ModelLike, config::Config) - @testset "integer with lower bound" begin - MOI.empty!(model) - @test MOI.is_empty(model) - MOIU.loadfromstring!( - model, - """ - variables: x - minobjective: 2.0x - x >= 1.5 - x in Integer() +function test_solve_Integer_with_lower_bound( + model::MOI.ModelLike, + config::Config, +) + MOI.empty!(model) + @test MOI.is_empty(model) + MOIU.loadfromstring!( + model, + """ +variables: x +minobjective: 2.0x +x >= 1.5 +x in Integer() """, - ) - x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( - model, - config; - objective_value = 4.0, - variable_primal = [(x, 2.0)], - ) - end - @testset "integer with upper bound" begin - MOI.empty!(model) - @test MOI.is_empty(model) - MOIU.loadfromstring!( - model, - """ - variables: x - minobjective: -2.0x - x <= 1.5 - x in Integer() + ) + x = MOI.get(model, MOI.VariableIndex, "x") + test_model_solution( + model, + config; + objective_value = 4.0, + variable_primal = [(x, 2.0)], + ) + return +end + +function setup_test( + ::typeof(test_solve_Integer_with_lower_bound), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [2.0])), + ) + return +end + +""" + test_solve_Integer_with_upper_bound( + model::MOI.ModelLike, + config::Config, + ) + +Test an integer variable with fractional upper bound. +""" +function test_solve_Integer_with_upper_bound( + model::MOI.ModelLike, + config::Config, +) + MOI.empty!(model) + @test MOI.is_empty(model) + MOIU.loadfromstring!( + model, + """ +variables: x +minobjective: -2.0x +x <= 1.5 +x in Integer() """, - ) - x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( - model, - config; - objective_value = -2.0, - variable_primal = [(x, 1.0)], - ) - end - @testset "binary with upper" begin - MOI.empty!(model) - @test MOI.is_empty(model) - MOIU.loadfromstring!( - model, - """ - variables: x - minobjective: -2.0x - x <= 2.0 - x in ZeroOne() + ) + x = MOI.get(model, MOI.VariableIndex, "x") + test_model_solution( + model, + config; + objective_value = -2.0, + variable_primal = [(x, 1.0)], + ) + return +end + +function setup_test( + ::typeof(test_solve_Integer_with_upper_bound), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + ) + return +end + +""" + test_solve_Integer_with_0_upper_bound( + model::MOI.ModelLike, + config::Config, + ) + +Test a binary variable `<= 2`. +""" +function test_solve_ZeroOne_with_upper_bound( + model::MOI.ModelLike, + config::Config, +) + MOI.empty!(model) + @test MOI.is_empty(model) + MOIU.loadfromstring!( + model, + """ +variables: x +minobjective: -2.0x +x <= 2.0 +x in ZeroOne() """, - ) - x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( - model, - config; - objective_value = -2.0, - variable_primal = [(x, 1.0)], - ) - end - @testset "binary with 0 upper" begin - MOI.empty!(model) - @test MOI.is_empty(model) - MOIU.loadfromstring!( - model, - """ - variables: x - minobjective: 1.0x - x <= 0.0 - x in ZeroOne() + ) + x = MOI.get(model, MOI.VariableIndex, "x") + test_model_solution( + model, + config; + objective_value = -2.0, + variable_primal = [(x, 1.0)], + ) + return +end + +function setup_test( + ::typeof(test_solve_ZeroOne_with_upper_bound), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + ) + return +end + +""" + test_solve_ZeroOne_with_0_upper_bound( + model::MOI.ModelLike, + config::Config, + ) + +Test a binary variable `<= 0`. +""" +function test_solve_ZeroOne_with_0_upper_bound( + model::MOI.ModelLike, + config::Config, +) + MOI.empty!(model) + @test MOI.is_empty(model) + MOIU.loadfromstring!( + model, + """ +variables: x +minobjective: 1.0x +x <= 0.0 +x in ZeroOne() """, - ) - x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( - model, - config; - objective_value = 0.0, - variable_primal = [(x, 0.0)], - ) - end + ) + x = MOI.get(model, MOI.VariableIndex, "x") + test_model_solution( + model, + config; + objective_value = 0.0, + variable_primal = [(x, 0.0)], + ) + return +end + +function setup_test( + ::typeof(test_solve_ZeroOne_with_0_upper_bound), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])), + ) + return end -unittests["solve_integer_edge_cases"] = solve_integer_edge_cases diff --git a/src/Test/contlinear.jl b/src/Test/contlinear.jl index c411bbb463..f2beaae051 100644 --- a/src/Test/contlinear.jl +++ b/src/Test/contlinear.jl @@ -495,6 +495,47 @@ function test_linear_integration( end end +function setup_test( + ::typeof(test_linear_integration), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0, 0, 1], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-2], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [-1, 0, 2]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1, 0, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2, 0, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 2, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 1, 0], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-1.5], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [0.5], + ), + ) + return +end + """ test_linear_program( model::MOI.ModelLike, @@ -602,6 +643,27 @@ function test_linear_integration_2( end end +function setup_test( + ::typeof(test_linear_integration_2), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], + ], + var_basis = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], + ), + ) + return +end + """ test_linear_inactive_bounds( model::MOI.ModelLike, @@ -734,6 +796,33 @@ function test_linear_inactive_bounds( end end +function setup_test( + ::typeof(test_linear_inactive_bounds), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [3], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.NONBASIC], + ], + var_basis = [MOI.BASIC], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], + ], + var_basis = [MOI.NONBASIC_AT_UPPER], + ), + ) + return +end + """ test_linear_LessThan_and_GreaterThan( model::MOI.ModelLike, @@ -824,6 +913,20 @@ function test_linear_LessThan_and_GreaterThan( end end +function setup_test( + ::typeof(test_linear_LessThan_and_GreaterThan), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), + ) + return +end + """ test_linear_integration_modification( model::MOI.ModelLike, @@ -987,6 +1090,21 @@ function test_linear_integration_modification( end end +function setup_test( + ::typeof(test_linear_integration_modification), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4 / 3, 4 / 3]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2]), + ) + return +end + """ test_linear_modify_GreaterThan_and_LessThan_constraints( model::MOI.ModelLike, @@ -1100,6 +1218,20 @@ function test_linear_modify_GreaterThan_and_LessThan_constraints( end end +function setup_test( + ::typeof(test_linear_modify_GreaterThan_and_LessThan_constraints), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), + ) + return +end + """ test_linear_VectorAffineFunction( model::MOI.ModelLike, @@ -1256,6 +1388,20 @@ function test_linear_VectorAffineFunction( end end +function setup_test( + ::typeof(test_linear_VectorAffineFunction), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), + ) + return +end + """ test_linear_INFEASIBLE( model::MOI.ModelLike, @@ -1345,6 +1491,31 @@ function test_linear_INFEASIBLE( end end +function setup_test( + ::typeof(test_linear_INFEASIBLE), + mock::MOIU.MockOptimizer, + config::Config, +) + if config.infeas_certificates + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + tuple(), + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], + ), + ) + else + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.INFEASIBLE), + ) + end + return +end + """ test_linear_DUAL_INFEASIBLE( model::MOI.ModelLike, @@ -1425,6 +1596,24 @@ function test_linear_DUAL_INFEASIBLE( end end +function setup_test( + ::typeof(test_linear_DUAL_INFEASIBLE), + mock::MOIU.MockOptimizer, + config::Config, +) + primal_status = if config.infeas_certificates + MOI.INFEASIBILITY_CERTIFICATE + else + MOI.NO_SOLUTION + end + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE, primal_status), + ) + return +end + """ test_linear_DUAL_INFEASIBLE_2( model::MOI.ModelLike, @@ -1507,6 +1696,30 @@ function test_linear_DUAL_INFEASIBLE_2( end end +function setup_test( + ::typeof(test_linear_DUAL_INFEASIBLE_2), + mock::MOIU.MockOptimizer, + config::Config, +) + if config.infeas_certificates + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.DUAL_INFEASIBLE, + (MOI.INFEASIBILITY_CERTIFICATE, [1, 1]), + ), + ) + else + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE), + ) + end + return +end + """ test_linear_add_constraints( model::MOI.ModelLike, @@ -1617,6 +1830,26 @@ function test_linear_add_constraints( end end +function setup_test( + ::typeof(test_linear_add_constraints), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [650 / 11, 400 / 11], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC, MOI.NONBASIC], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], + ], + var_basis = [MOI.BASIC, MOI.BASIC], + ), + ) + return +end + """ test_linear_integration_Interval( model::MOI.ModelLike, @@ -1808,6 +2041,57 @@ function test_linear_integration_Interval( end end +function setup_test( + ::typeof(test_linear_integration_Interval), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [5.0, 5.0], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], + ], + var_basis = [MOI.BASIC, MOI.BASIC], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => + [-1], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [2.5, 2.5], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], + ], + var_basis = [MOI.BASIC, MOI.BASIC], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => + [1], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => + [1], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], + ], + var_basis = [MOI.BASIC, MOI.BASIC], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [6.0, 6.0], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => + [-1], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], + ], + var_basis = [MOI.BASIC, MOI.BASIC], + ), + ) + return +end + """ test_linear_Interval_inactive( model::MOI.ModelLike, @@ -1900,6 +2184,27 @@ function test_linear_Interval_inactive( end end +function setup_test( + ::typeof(test_linear_Interval_inactive), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.0, 0.0], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.BASIC], + ], + var_basis = [MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_LOWER], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => + [0], + ), + ) + return +end + """ test_linear_transform( model::MOI.ModelLike, @@ -2013,6 +2318,19 @@ function test_linear_transform( end end +function setup_test( + ::typeof(test_linear_transform), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 0.5]), + ) + return +end + """ test_linear_INFEASIBLE_2( model::MOI.ModelLike, @@ -2111,6 +2429,31 @@ function test_linear_INFEASIBLE_2( end end +function setup_test( + ::typeof(test_linear_INFEASIBLE_2), + mock::MOIU.MockOptimizer, + config::Config, +) + if config.infeas_certificates + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + tuple(), + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1, -1], + ), + ) + else + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.INFEASIBLE), + ) + end + return +end + """ test_linear_FEASIBILITY_SENSE( model::MOI.ModelLike, @@ -2191,6 +2534,25 @@ function test_linear_FEASIBILITY_SENSE( end end +function setup_test( + ::typeof(test_linear_FEASIBILITY_SENSE), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1 / 5, 1 / 5], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [0], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [0], + ), + ) + return +end + """ test_linear_integration_delete_variables( model::MOI.ModelLike, @@ -2338,6 +2700,47 @@ function test_linear_integration_delete_variables( end end +function setup_test( + ::typeof(test_linear_integration_delete_variables), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0, 1 / 2, 1], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], + ], + var_basis = [ + MOI.NONBASIC_AT_LOWER, + MOI.BASIC, + MOI.NONBASIC_AT_UPPER, + ], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2, 0, 0], + (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [0], + ), + ) + # test_linear_integration_delete_variables has double variable bounds for + # the z variable + mock.eval_variable_constraint_dual = false + function reset_function() + mock.eval_variable_constraint_dual = true + return + end + return reset_function +end + """ test_linear_VectorAffineFunction_empty_row( model::MOI.ModelLike, @@ -2403,6 +2806,22 @@ function test_linear_VectorAffineFunction_empty_row( end end +function setup_test( + ::typeof(test_linear_VectorAffineFunction_empty_row), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.0], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[0.0, 0.0]], + ), + ) + return +end + """ test_linear_VariablePrimalStart_partial( model::MOI.ModelLike, @@ -2459,3 +2878,15 @@ function test_linear_VariablePrimalStart_partial( rtol end end + +function setup_test( + ::typeof(test_linear_VariablePrimalStart_partial), + mock::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.0]), + ) + return +end diff --git a/test/Bridges/bridge_optimizer.jl b/test/Bridges/bridge_optimizer.jl index abcf350e7b..c7e1ab3a74 100644 --- a/test/Bridges/bridge_optimizer.jl +++ b/test/Bridges/bridge_optimizer.jl @@ -461,7 +461,7 @@ end bridged_mock, MOIT.Config(solve = false), include = ["test_linear_"], - exclude = ["VariablePrimalStart"], + exclude = ["VariablePrimalStart", "linear_mixed_complementarity"], ) end diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index 5ad4e61178..bf5a5529f2 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -1158,7 +1158,7 @@ end bridged, MOIT.Config{T}(solve = false), include = ["test_linear_"], - exclude = ["VariablePrimalStart"], + exclude = ["VariablePrimalStart", "linear_mixed_complementarity"], ) end diff --git a/test/Test/Test.jl b/test/Test/Test.jl index 1e26b3d94d..c0fc2b8c69 100644 --- a/test/Test/Test.jl +++ b/test/Test/Test.jl @@ -1,4 +1,8 @@ using Test +using MathOptInterface + +const MOI = MathOptInterface +const MOIU = MOI.Utilities @testset "$(file)" for file in readdir(@__DIR__) if file == "Test.jl" @@ -6,3 +10,23 @@ using Test end include(file) end + +MOI.Test.runtests( + MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), + MOI.Test.Config(basis = true), + # Oops! Name clash. + exclude = [ + "test_linear_mixed_complementarity", + "test_qp_complementarity_constraint", + ], +) + +MOI.Test.runtests( + MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), + MOI.Test.Config(modify_lhs = false), + # Oops! Name clash. + exclude = [ + "test_linear_mixed_complementarity", + "test_qp_complementarity_constraint", + ], +) diff --git a/test/Test/contlinear.jl b/test/Test/contlinear.jl deleted file mode 100644 index 02ebf51b6d..0000000000 --- a/test/Test/contlinear.jl +++ /dev/null @@ -1,452 +0,0 @@ -using Test -import MathOptInterface - -const MOI = MathOptInterface -const MOIU = MOI.Utilities - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_integration), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 0], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [-1], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 0], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [-1], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0, 0, 1], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [-2], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [-1, 0, 2]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1, 0, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2, 0, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 2, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 1, 0], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-1.5], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [0.5], - ), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_integration_2), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 0], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [-1], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], - ], - var_basis = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], - ), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_inactive_bounds), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [3], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.NONBASIC], - ], - var_basis = [MOI.BASIC], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], - ], - var_basis = [MOI.NONBASIC_AT_UPPER], - ), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_LessThan_and_GreaterThan), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_integration_modification), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4 / 3, 4 / 3]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [4, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [2]), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_modify_GreaterThan_and_LessThan_constraints), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_VectorAffineFunction), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, -100]), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_INFEASIBLE), - mock::MOIU.MockOptimizer, - config::MOI.Test.Config, -) - if config.infeas_certificates - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - tuple(), - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], - ), - ) - else - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.INFEASIBLE), - ) - end - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_DUAL_INFEASIBLE), - mock::MOIU.MockOptimizer, - config::MOI.Test.Config, -) - primal_status = if config.infeas_certificates - MOI.INFEASIBILITY_CERTIFICATE - else - MOI.NO_SOLUTION - end - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE, primal_status), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_DUAL_INFEASIBLE_2), - mock::MOIU.MockOptimizer, - config::MOI.Test.Config, -) - if config.infeas_certificates - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.DUAL_INFEASIBLE, - (MOI.INFEASIBILITY_CERTIFICATE, [1, 1]), - ), - ) - else - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE), - ) - end - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_add_constraints), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [650 / 11, 400 / 11], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC, MOI.NONBASIC], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - ), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_integration_Interval), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [5.0, 5.0], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [-1], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [2.5, 2.5], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [1], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [1], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [6.0, 6.0], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [-1], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], - ], - var_basis = [MOI.BASIC, MOI.BASIC], - ), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_Interval_inactive), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0.0, 0.0], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.BASIC], - ], - var_basis = [MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_LOWER], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => - [0], - ), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_transform), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 0.5]), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_INFEASIBLE_2), - mock::MOIU.MockOptimizer, - config::MOI.Test.Config, -) - if config.infeas_certificates - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - tuple(), - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1, -1], - ), - ) - else - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.INFEASIBLE), - ) - end - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_FEASIBILITY_SENSE), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1 / 5, 1 / 5], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [0], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [0], - ), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_integration_delete_variables), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0, 1 / 2, 1], - con_basis = [ - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], - ], - var_basis = [ - MOI.NONBASIC_AT_LOWER, - MOI.BASIC, - MOI.NONBASIC_AT_UPPER, - ], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [-1], - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2, 0, 0], - (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [-1], - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [0], - ), - ) - # test_linear_integration_delete_variables has double variable bounds for - # the z variable - mock.eval_variable_constraint_dual = false - function reset_function() - mock.eval_variable_constraint_dual = true - return - end - return reset_function -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_VectorAffineFunction_empty_row), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0.0], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[0.0, 0.0]], - ), - ) - return -end - -function MOI.Test.setup_test( - ::typeof(MOI.Test.test_linear_VariablePrimalStart_partial), - mock::MOIU.MockOptimizer, - ::MOI.Test.Config, -) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.0]), - ) - return -end - -MOI.Test.runtests( - MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), - MOI.Test.Config(basis = true), - include = ["test_linear_"], - # Oops! Name clash. - exclude = ["test_linear_mixed_complementarity"], -) - -MOI.Test.runtests( - MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), - MOI.Test.Config(modify_lhs = false), - include = ["test_linear_"], - # Oops! Name clash. - exclude = ["test_linear_mixed_complementarity"], -) diff --git a/test/Test/unit.jl b/test/Test/unit.jl index 961b3476bc..896f3d2fc4 100644 --- a/test/Test/unit.jl +++ b/test/Test/unit.jl @@ -33,8 +33,6 @@ end "solve_blank_obj", "solve_constant_obj", "solve_singlevariable_obj", - "solve_with_lowerbound", - "solve_with_upperbound", "solve_affine_lessthan", "solve_affine_greaterthan", "solve_affine_equalto", @@ -46,7 +44,6 @@ end "solve_qcp_edge_cases", "solve_affine_deletion_edge_cases", "solve_duplicate_terms_obj", - "solve_integer_edge_cases", "solve_objbound_edge_cases", "raw_status_string", "solve_time", @@ -119,40 +116,6 @@ end ) MOIT.solve_singlevariable_obj(mock, config) end - @testset "solve_with_lowerbound" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1]), - MOI.FEASIBLE_POINT, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0], - (MOI.SingleVariable, MOI.LessThan{Float64}) => [0.0], - ), - ) - # x has two variable constraints - mock.eval_variable_constraint_dual = false - MOIT.solve_with_lowerbound(mock, config) - mock.eval_variable_constraint_dual = true - end - @testset "solve_with_upperbound" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1]), - MOI.FEASIBLE_POINT, - (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0], - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [0.0], - ), - ) - # x has two variable constraints - mock.eval_variable_constraint_dual = false - MOIT.solve_with_upperbound(mock, config) - mock.eval_variable_constraint_dual = true - end @testset "solve_affine_lessthan" begin MOIU.set_mock_optimize!( mock, @@ -331,32 +294,6 @@ end ) MOIT.solve_affine_deletion_edge_cases(mock, config) end - @testset "solve_integer_edge_cases" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ), - ) - MOIT.solve_integer_edge_cases(mock, config) - end @testset "solve_objbound_edge_cases" begin MOIU.set_mock_optimize!( mock, diff --git a/test/Utilities/cachingoptimizer.jl b/test/Utilities/cachingoptimizer.jl index db5bdbaef7..3da83d9ee4 100644 --- a/test/Utilities/cachingoptimizer.jl +++ b/test/Utilities/cachingoptimizer.jl @@ -574,7 +574,10 @@ for state in (MOIU.NO_OPTIMIZER, MOIU.EMPTY_OPTIMIZER, MOIU.ATTACHED_OPTIMIZER) m, config, include = ["test_linear_"], - exclue = ["VariablePrimalStart"], + exclude = [ + "VariablePrimalStart", + "linear_mixed_complementarity", + ], ) end end diff --git a/test/Utilities/universalfallback.jl b/test/Utilities/universalfallback.jl index 2694e9ba64..87d463b3e6 100644 --- a/test/Utilities/universalfallback.jl +++ b/test/Utilities/universalfallback.jl @@ -297,7 +297,12 @@ end MOIT.modificationtest(uf, config) end @testset "Continuous Linear" begin - MOI.Test.runtests(uf, config, include = ["test_linear_"]) + MOI.Test.runtests( + uf, + config, + include = ["test_linear_"], + exclude = ["linear_mixed_complementarity"], + ) end @testset "Duplicate names" begin From c8726dc06a509d72c967ac06179f386cc3e9d7fa Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 16:04:10 +1200 Subject: [PATCH 03/23] Fix tests --- test/Utilities/model.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Utilities/model.jl b/test/Utilities/model.jl index 7ca72b8460..7dedb5d516 100644 --- a/test/Utilities/model.jl +++ b/test/Utilities/model.jl @@ -240,7 +240,7 @@ end MOIU.Model{Float64}(), MOIT.Config(solve = false), include = ["test_linear_"], - exclude = ["VariablePrimalStart"], + exclude = ["VariablePrimalStart", "linear_mixed_complementarity"], ) end From 6dc39d37de50a0b9453e86830d9dc88802af59d7 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 16:04:18 +1200 Subject: [PATCH 04/23] Migrate intconic --- src/Test/intconic.jl | 36 ++++++++++++++++++++++-------------- test/Test/intconic.jl | 14 -------------- 2 files changed, 22 insertions(+), 28 deletions(-) delete mode 100644 test/Test/intconic.jl diff --git a/src/Test/intconic.jl b/src/Test/intconic.jl index b9ef229a08..fccfab2c9c 100644 --- a/src/Test/intconic.jl +++ b/src/Test/intconic.jl @@ -1,13 +1,17 @@ -# Integer conic problems +""" + test_Integer_SecondOrderCone(model::MOI.ModelLike, config::Config) -function intsoc1test(model::MOI.ModelLike, config::Config) +Run an integration test on the problem: +``` + min - 2y - 1z +s.t. x == 1 + [x, y, z] in SecondOrderCone() + y, z in ZeroOne() +``` +""" +function test_Integer_SecondOrderCone(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # Problem SINTSOC1 - # min 0x - 2y - 1z - # st x == 1 - # x >= ||(y,z)|| - # (y,z) binary @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -87,10 +91,14 @@ function intsoc1test(model::MOI.ModelLike, config::Config) end end -const intsoctests = Dict("intsoc1" => intsoc1test) - -@moitestset intsoc - -const intconictests = Dict("intsoc" => intsoctest) - -@moitestset intconic true +function setup_test( + ::typeof(test_Integer_SecondOrderCone), + model::MOI.Utilities.MockOptimizer, + config::Config, +) + MOIU.set_mock_optimize( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0, 0.0]), + ) + return +end diff --git a/test/Test/intconic.jl b/test/Test/intconic.jl deleted file mode 100644 index 08d18cbfeb..0000000000 --- a/test/Test/intconic.jl +++ /dev/null @@ -1,14 +0,0 @@ -using Test -import MathOptInterface -const MOI = MathOptInterface -const MOIT = MOI.Test -const MOIU = MOI.Utilities - -mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) -config = MOIT.Config() - -@testset "SOC" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0, 0.0]) - MOIT.intsoc1test(mock, config) -end From bcaa26ece26acec8d9036f49fbe226b9eef9e1d8 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 16:42:35 +1200 Subject: [PATCH 05/23] Migrate intlinear --- src/Test/intconic.jl | 2 +- src/Test/intlinear.jl | 843 ++++++++++++++++++---------- test/Bridges/Constraint/zero_one.jl | 4 +- test/Test/Test.jl | 10 - test/Test/intlinear.jl | 118 ---- 5 files changed, 548 insertions(+), 429 deletions(-) delete mode 100644 test/Test/intlinear.jl diff --git a/src/Test/intconic.jl b/src/Test/intconic.jl index fccfab2c9c..ba2935f44f 100644 --- a/src/Test/intconic.jl +++ b/src/Test/intconic.jl @@ -96,7 +96,7 @@ function setup_test( model::MOI.Utilities.MockOptimizer, config::Config, ) - MOIU.set_mock_optimize( + MOIU.set_mock_optimize!( model, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0, 0.0]), ) diff --git a/src/Test/intlinear.jl b/src/Test/intlinear.jl index 852ae5e7a9..5c8466e77d 100644 --- a/src/Test/intlinear.jl +++ b/src/Test/intlinear.jl @@ -1,17 +1,19 @@ -# MIP01 from CPLEX.jl -function int1test(model::MOI.ModelLike, config::Config) +""" + test_integer_integration(model::MOI.ModelLike, config::Config) + +Run an integration test on the MILP: +``` +maximize 1.1x + 2 y + 5 z +s.t. x + y + z <= 10 + x + 2 y + z <= 15 + x is continuous: 0 <= x <= 5 + y is integer: 0 <= y <= 10 + z is binary +``` +""" +function test_integer_integration(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # an example on mixed integer programming - # - # maximize 1.1x + 2 y + 5 z - # - # s.t. x + y + z <= 10 - # x + 2 y + z <= 15 - # - # x is continuous: 0 <= x <= 5 - # y is integer: 0 <= y <= 10 - # z is binary @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -132,251 +134,318 @@ function int1test(model::MOI.ModelLike, config::Config) # @test MOI.get(model, MOI.BarrierIterations()) >= 0 # @test MOI.get(model, MOI.NodeCount()) >= 0 end + return +end + +function setup_test( + ::typeof(test_integer_integration), + model::MOI.Utilities.MockOptimizer, + ::Config +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 20.0) + MOIU.mock_optimize!(mock, [4, 5, 1]) + end, + ) + return end -# sos from CPLEX.jl" begin -function int2test(model::MOI.ModelLike, config::Config) +""" + test_SOS1_integration(model::MOI.ModelLike, config::Config) + +Test Special Ordered Sets of type 1. +""" +function test_SOS1_integration(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - @testset "SOSI" begin - @test MOI.supports_incremental_interface(model, false) #=copy_names=# - @test MOI.supports( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - ) - @test MOI.supports(model, MOI.ObjectiveSense()) - @test MOI.supports_constraint( - model, - MOI.VectorOfVariables, - MOI.SOS1{Float64}, - ) - @test MOI.supports_constraint( - model, - MOI.SingleVariable, - MOI.LessThan{Float64}, - ) - MOI.empty!(model) - @test MOI.is_empty(model) - v = MOI.add_variables(model, 3) - @test MOI.get(model, MOI.NumberOfVariables()) == 3 - vc1 = MOI.add_constraint( - model, - MOI.SingleVariable(v[1]), - MOI.LessThan(1.0), - ) - @test vc1.value == v[1].value - vc2 = MOI.add_constraint( - model, - MOI.SingleVariable(v[2]), - MOI.LessThan(1.0), - ) - @test vc2.value == v[2].value - vc3 = MOI.add_constraint( - model, - MOI.SingleVariable(v[3]), - MOI.LessThan(2.0), - ) - @test vc3.value == v[3].value - c1 = MOI.add_constraint( - model, - MOI.VectorOfVariables([v[1], v[2]]), - MOI.SOS1([1.0, 2.0]), - ) - c2 = MOI.add_constraint( - model, - MOI.VectorOfVariables([v[1], v[3]]), - MOI.SOS1([1.0, 2.0]), - ) - if config.query_number_of_constraints - @test MOI.get( - model, - MOI.NumberOfConstraints{ - MOI.VectorOfVariables, - MOI.SOS1{Float64}, - }(), - ) == 2 - end - #= - To allow for permutations in the sets and variable vectors - we're going to sort according to the weights - =# - cs_sos = MOI.get(model, MOI.ConstraintSet(), c2) - cf_sos = MOI.get(model, MOI.ConstraintFunction(), c2) - p = sortperm(cs_sos.weights) - @test cs_sos.weights[p] ≈ [1.0, 2.0] atol = atol rtol = rtol - @test cf_sos.variables[p] == v[[1, 3]] - objf = MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([2.0, 1.0, 1.0], v), - 0.0, - ) - MOI.set( + @test MOI.supports_incremental_interface(model, false) #=copy_names=# + @test MOI.supports( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + ) + @test MOI.supports(model, MOI.ObjectiveSense()) + @test MOI.supports_constraint( + model, + MOI.VectorOfVariables, + MOI.SOS1{Float64}, + ) + @test MOI.supports_constraint( + model, + MOI.SingleVariable, + MOI.LessThan{Float64}, + ) + MOI.empty!(model) + @test MOI.is_empty(model) + v = MOI.add_variables(model, 3) + @test MOI.get(model, MOI.NumberOfVariables()) == 3 + vc1 = MOI.add_constraint( + model, + MOI.SingleVariable(v[1]), + MOI.LessThan(1.0), + ) + @test vc1.value == v[1].value + vc2 = MOI.add_constraint( + model, + MOI.SingleVariable(v[2]), + MOI.LessThan(1.0), + ) + @test vc2.value == v[2].value + vc3 = MOI.add_constraint( + model, + MOI.SingleVariable(v[3]), + MOI.LessThan(2.0), + ) + @test vc3.value == v[3].value + c1 = MOI.add_constraint( + model, + MOI.VectorOfVariables([v[1], v[2]]), + MOI.SOS1([1.0, 2.0]), + ) + c2 = MOI.add_constraint( + model, + MOI.VectorOfVariables([v[1], v[3]]), + MOI.SOS1([1.0, 2.0]), + ) + if config.query_number_of_constraints + @test MOI.get( model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - objf, - ) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE - 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.ResultCount()) >= 1 - @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 3 atol = atol rtol = - rtol - @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [0, 1, 2] atol = - atol rtol = rtol - end - MOI.delete(model, c1) - MOI.delete(model, c2) - if config.solve - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == - config.optimal_status - @test MOI.get(model, MOI.ResultCount()) >= 1 - @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 5 atol = atol rtol = - rtol - @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [1, 1, 2] atol = - atol rtol = rtol - end + MOI.NumberOfConstraints{ + MOI.VectorOfVariables, + MOI.SOS1{Float64}, + }(), + ) == 2 end - @testset "SOSII" begin - @test MOI.supports_incremental_interface(model, false) #=copy_names=# - @test MOI.supports( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - ) - @test MOI.supports(model, MOI.ObjectiveSense()) - @test MOI.supports_constraint( - model, - MOI.VectorOfVariables, - MOI.SOS1{Float64}, - ) - @test MOI.supports_constraint( - model, - MOI.VectorOfVariables, - MOI.SOS2{Float64}, - ) - @test MOI.supports_constraint(model, MOI.SingleVariable, MOI.ZeroOne) - @test MOI.supports_constraint( + #= + To allow for permutations in the sets and variable vectors + we're going to sort according to the weights + =# + cs_sos = MOI.get(model, MOI.ConstraintSet(), c2) + cf_sos = MOI.get(model, MOI.ConstraintFunction(), c2) + p = sortperm(cs_sos.weights) + @test cs_sos.weights[p] ≈ [1.0, 2.0] atol = atol rtol = rtol + @test cf_sos.variables[p] == v[[1, 3]] + objf = MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.([2.0, 1.0, 1.0], v), + 0.0, + ) + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + objf, + ) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE + 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.ResultCount()) >= 1 + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 3 atol = atol rtol = + rtol + @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [0, 1, 2] atol = + atol rtol = rtol + end + MOI.delete(model, c1) + MOI.delete(model, c2) + if config.solve + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == + config.optimal_status + @test MOI.get(model, MOI.ResultCount()) >= 1 + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 5 atol = atol rtol = + rtol + @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [1, 1, 2] atol = + atol rtol = rtol + end + return +end + +function setup_test( + ::typeof(test_SOS1_integration), + model::MOI.Utilities.MockOptimizer, + ::Config +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 1, 2]), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1, 1, 2]), + ) + return +end + +""" + test_SOS2_integration(model::MOI.ModelLike, config::Config) + +Test Special Ordered Sets of type 2. +""" +function test_SOS2_integration(model::MOI.ModelLike, config::Config) + atol = config.atol + rtol = config.rtol + + @test MOI.supports_incremental_interface(model, false) #=copy_names=# + @test MOI.supports( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + ) + @test MOI.supports(model, MOI.ObjectiveSense()) + @test MOI.supports_constraint( + model, + MOI.VectorOfVariables, + MOI.SOS1{Float64}, + ) + @test MOI.supports_constraint( + model, + MOI.VectorOfVariables, + MOI.SOS2{Float64}, + ) + @test MOI.supports_constraint(model, MOI.SingleVariable, MOI.ZeroOne) + @test MOI.supports_constraint( + model, + MOI.ScalarAffineFunction{Float64}, + MOI.EqualTo{Float64}, + ) + MOI.empty!(model) + @test MOI.is_empty(model) + v = MOI.add_variables(model, 10) + @test MOI.get(model, MOI.NumberOfVariables()) == 10 + bin_constraints = [] + for i in 1:8 + vc = MOI.add_constraint( model, - MOI.ScalarAffineFunction{Float64}, - MOI.EqualTo{Float64}, + MOI.SingleVariable(v[i]), + MOI.Interval(0.0, 2.0), ) - MOI.empty!(model) - @test MOI.is_empty(model) - v = MOI.add_variables(model, 10) - @test MOI.get(model, MOI.NumberOfVariables()) == 10 - bin_constraints = [] - for i in 1:8 - vc = MOI.add_constraint( + @test vc.value == v[i].value + push!( + bin_constraints, + MOI.add_constraint( model, MOI.SingleVariable(v[i]), - MOI.Interval(0.0, 2.0), - ) - @test vc.value == v[i].value - push!( - bin_constraints, - MOI.add_constraint( - model, - MOI.SingleVariable(v[i]), - MOI.ZeroOne(), - ), - ) - @test bin_constraints[i].value == v[i].value - end - MOI.add_constraint( - model, - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([1.0, 2.0, 3.0, -1.0], v[[1, 2, 3, 9]]), - 0.0, + MOI.ZeroOne(), ), - MOI.EqualTo(0.0), ) - MOI.add_constraint( - model, - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.( - [5.0, 4.0, 7.0, 2.0, 1.0, -1.0], - v[[4, 5, 6, 7, 8, 10]], - ), - 0.0, + @test bin_constraints[i].value == v[i].value + end + MOI.add_constraint( + model, + MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.([1.0, 2.0, 3.0, -1.0], v[[1, 2, 3, 9]]), + 0.0, + ), + MOI.EqualTo(0.0), + ) + MOI.add_constraint( + model, + MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.( + [5.0, 4.0, 7.0, 2.0, 1.0, -1.0], + v[[4, 5, 6, 7, 8, 10]], ), - MOI.EqualTo(0.0), - ) - MOI.add_constraint( - model, - MOI.VectorOfVariables(v[[1, 2, 3]]), - MOI.SOS1([1.0, 2.0, 3.0]), - ) - vv = MOI.VectorOfVariables(v[[4, 5, 6, 7, 8]]) - sos2 = MOI.SOS2([5.0, 4.0, 7.0, 2.0, 1.0]) - c = MOI.add_constraint(model, vv, sos2) - #= - To allow for permutations in the sets and variable vectors - we're going to sort according to the weights - =# - cs_sos = MOI.get(model, MOI.ConstraintSet(), c) - cf_sos = MOI.get(model, MOI.ConstraintFunction(), c) - p = sortperm(cs_sos.weights) - @test cs_sos.weights[p] ≈ [1.0, 2.0, 4.0, 5.0, 7.0] atol = atol rtol = - rtol - @test cf_sos.variables[p] == v[[8, 7, 5, 4, 6]] - objf = MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([1.0, 1.0], [v[9], v[10]]), 0.0, - ) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - objf, - ) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE - 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.ResultCount()) >= 1 - @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 15.0 atol = atol rtol = - rtol - @test MOI.get(model, MOI.VariablePrimal(), v) ≈ - [0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 3.0, 12.0] atol = - atol rtol = rtol - end - for cref in bin_constraints - MOI.delete(model, cref) - end - if config.solve - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == - config.optimal_status - @test MOI.get(model, MOI.ResultCount()) >= 1 - @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 30.0 atol = atol rtol = - rtol - @test MOI.get(model, MOI.VariablePrimal(), v) ≈ - [0.0, 0.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 6.0, 24.0] atol = - atol rtol = rtol - end + ), + MOI.EqualTo(0.0), + ) + MOI.add_constraint( + model, + MOI.VectorOfVariables(v[[1, 2, 3]]), + MOI.SOS1([1.0, 2.0, 3.0]), + ) + vv = MOI.VectorOfVariables(v[[4, 5, 6, 7, 8]]) + sos2 = MOI.SOS2([5.0, 4.0, 7.0, 2.0, 1.0]) + c = MOI.add_constraint(model, vv, sos2) + #= + To allow for permutations in the sets and variable vectors + we're going to sort according to the weights + =# + cs_sos = MOI.get(model, MOI.ConstraintSet(), c) + cf_sos = MOI.get(model, MOI.ConstraintFunction(), c) + p = sortperm(cs_sos.weights) + @test cs_sos.weights[p] ≈ [1.0, 2.0, 4.0, 5.0, 7.0] atol = atol rtol = + rtol + @test cf_sos.variables[p] == v[[8, 7, 5, 4, 6]] + objf = MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.([1.0, 1.0], [v[9], v[10]]), + 0.0, + ) + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + objf, + ) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE + 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.ResultCount()) >= 1 + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 15.0 atol = atol rtol = + rtol + @test MOI.get(model, MOI.VariablePrimal(), v) ≈ + [0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 3.0, 12.0] atol = + atol rtol = rtol end + for cref in bin_constraints + MOI.delete(model, cref) + end + if config.solve + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == + config.optimal_status + @test MOI.get(model, MOI.ResultCount()) >= 1 + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 30.0 atol = atol rtol = + rtol + @test MOI.get(model, MOI.VariablePrimal(), v) ≈ + [0.0, 0.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 6.0, 24.0] atol = + atol rtol = rtol + end + return +end + +function setup_test( + ::typeof(test_SOS2_integration), + model::MOI.Utilities.MockOptimizer, + ::Config +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 3.0, 12.0], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.0, 0.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 6.0, 24.0], + ), + ) + return end -# CPLEX #76 -function int3test(model::MOI.ModelLike, config::Config) +""" + test_integer_solve_twice(model::MOI.ModelLike, config::Config) + +Test solving a model twice on the integer knapsack problem. +The problem is: +``` +max z - 0.5 ( b1 + b2 + b3) / 40 +s.t. 0 <= z - 0.5 eᵀ b / 40 <= 0.999 + b1, b2, ... b10 ∈ {0, 1} + z in {0, 1, 2, ..., 100} +``` +""" +function test_integer_solve_twice(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # integer knapsack problem - # max z - 0.5 ( b1 + b2 + b3) / 40 - # s.t. 0 <= z - 0.5 eᵀ b / 40 <= 0.999 - # b1, b2, ... b10 ∈ {0, 1} - # z in {0, 1, 2, ..., 100} MOI.empty!(model) @test MOI.is_empty(model) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -443,17 +512,35 @@ function int3test(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ObjectiveValue()) ≈ 1 atol = atol rtol = rtol end + return +end + +function setup_test( + ::typeof(test_integer_solve_twice), + model::MOI.Utilities.MockOptimizer, + ::Config +) + # FIXME [1, 0...] is not the correct optimal solution but it passes the test + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0; zeros(10)]), + ) + return end -# Mixed-integer linear problems +""" + test_integer_knapsack(model::MOI.ModelLike, config::Config) -function knapsacktest(model::MOI.ModelLike, config::Config) +Test the integer knapsack problem +``` +max 5a + 3b + 2c + 7d + 4e +st 2a + 8b + 4c + 2d + 5e <= 10 + a, b, c, d, e ∈ binary +``` +""" +function test_integer_knapsack(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # integer knapsack problem - # max 5a + 3b + 2c + 7d + 4e - # st 2a + 8b + 4c + 2d + 5e <= 10 - # a,b,c,d,e ∈ binary MOI.empty!(model) @test MOI.is_empty(model) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -519,17 +606,36 @@ function knapsacktest(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [1, 0, 0, 1, 1] atol = atol rtol = rtol end + return +end + +function setup_test( + ::typeof(test_integer_knapsack), + model::MOI.Utilities.MockOptimizer, + ::Config +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1, 0, 0, 1, 1]), + ) + return end -function indicator1_test(model::MOI.ModelLike, config::Config) +""" + test_Indicator_integration(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +max 2x1 + 3x2 +s.t. x1 + x2 <= 10 + z1 ==> x2 <= 8 + z2 ==> x2 + x1/5 <= 9 + z1 + z2 >= 1 +``` +""" +function test_Indicator_integration(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # linear problem with indicator constraint - # max 2x1 + 3x2 - # s.t. x1 + x2 <= 10 - # z1 ==> x2 <= 8 - # z2 ==> x2 + x1/5 <= 9 - # z1 + z2 >= 1 MOI.empty!(model) @test MOI.is_empty(model) @test MOI.supports( @@ -621,20 +727,39 @@ function indicator1_test(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.VariablePrimal(), z2) ≈ 1.0 atol = atol rtol = rtol end + return +end + +function setup_test( + ::typeof(test_Indicator_integration), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [1.25, 8.75, 0.0, 1.0]), + ) + return end -function indicator2_test(model::MOI.ModelLike, config::Config) +""" + test_Indicator_ON_ONE(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +max 2x1 + 3x2 - 30 z2 +s.t. x1 + x2 <= 10 + z1 ==> x2 <= 8 + z2 ==> x2 + x1/5 <= 9 + z1 + z2 >= 1 +``` +""" +function test_Indicator_ON_ONE(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # linear problem with indicator constraint - # max 2x1 + 3x2 - 30 z2 - # s.t. x1 + x2 <= 10 - # z1 ==> x2 <= 8 - # z2 ==> x2 + x1/5 <= 9 - # z1 + z2 >= 1 MOI.empty!(model) @test MOI.is_empty(model) - # This is the same model as indicator_test1, except that the penalty on z2 forces z1 to be 1. x1 = MOI.add_variable(model) x2 = MOI.add_variable(model) z1 = MOI.add_variable(model) @@ -704,18 +829,37 @@ function indicator2_test(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.VariablePrimal(), z2) ≈ 0.0 atol = atol rtol = rtol end + return +end + +function setup_test( + ::typeof(test_Indicator_ON_ONE), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [2.0, 8.0, 1.0, 0.0]), + ) + return end -function indicator3_test(model::MOI.ModelLike, config::Config) +""" + test_Indicator_ON_ZERO(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +max 2x1 + 3x2 +s.t. x1 + x2 <= 10 + z1 == 0 ==> x2 <= 8 + z2 == 1 ==> x2 + x1/5 <= 9 + (1-z1) + z2 >= 1 <=> z2 - z1 >= 0 +``` +""" +function test_Indicator_ON_ZERO(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # linear problem with indicator constraint - # similar to indicator1_test with reversed z1 - # max 2x1 + 3x2 - # s.t. x1 + x2 <= 10 - # z1 == 0 ==> x2 <= 8 - # z2 == 1 ==> x2 + x1/5 <= 9 - # (1-z1) + z2 >= 1 <=> z2 - z1 >= 0 MOI.empty!(model) @test MOI.is_empty(model) @test MOI.supports( @@ -809,18 +953,38 @@ function indicator3_test(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.VariablePrimal(), z2) ≈ 1.0 atol = atol rtol = rtol end + return +end + +function setup_test( + ::typeof(test_Indicator_ON_ZERO), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [1.25, 8.75, 1.0, 1.0]), + ) + return end -function indicator4_test(model::MOI.ModelLike, config::Config) +""" + test_Indicator_constant_term(model::MOI.ModelLike, config::Config) + +Test Indicator constraints with a constant term on the left-hand side. The +problem is: +``` +max 2x1 + 3x2 +s.t. x1 + x2 <= 10 + z1 ==> x2 - 1 <= 7 + z2 ==> x2 + x1/5 + 1 <= 10 + z1 + z2 >= 1 +``` +""" +function test_Indicator_constant_term(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # equivalent to indicator1_test with left-hand-side partially in LHS constant - # linear problem with indicator constraint and - # max 2x1 + 3x2 - # s.t. x1 + x2 <= 10 - # z1 ==> x2 - 1 <= 7 - # z2 ==> x2 + x1/5 + 1 <= 10 - # z1 + z2 >= 1 MOI.empty!(model) @test MOI.is_empty(model) @test MOI.supports( @@ -912,9 +1076,27 @@ function indicator4_test(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.VariablePrimal(), z2) ≈ 1.0 atol = atol rtol = rtol end + return +end + +function setup_test( + ::typeof(test_Indicator_constant_term), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [1.25, 8.75, 0.0, 1.0]), + ) + return end -function _semitest(model::MOI.ModelLike, config::Config{T}, int::Bool) where {T} +function _test_SemiXXX_integration( + model::MOI.ModelLike, + config::Config{T}, + use_semiinteger::Bool, +) where {T} atol = config.atol rtol = config.rtol @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -923,17 +1105,17 @@ function _semitest(model::MOI.ModelLike, config::Config{T}, int::Bool) where {T} MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if !int + if use_semiinteger @test MOI.supports_constraint( model, MOI.SingleVariable, - MOI.Semicontinuous{T}, + MOI.Semiinteger{T}, ) else @test MOI.supports_constraint( model, MOI.SingleVariable, - MOI.Semiinteger{T}, + MOI.Semicontinuous{T}, ) end # 2 variables @@ -949,7 +1131,7 @@ function _semitest(model::MOI.ModelLike, config::Config{T}, int::Bool) where {T} @test MOI.is_empty(model) v = MOI.add_variables(model, 2) @test MOI.get(model, MOI.NumberOfVariables()) == 2 - if !int + if !use_semiinteger vc1 = MOI.add_constraint( model, MOI.SingleVariable(v[1]), @@ -1057,7 +1239,7 @@ function _semitest(model::MOI.ModelLike, config::Config{T}, int::Bool) where {T} @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - if !int + if !use_semiinteger @test MOI.get(model, MOI.ObjectiveValue()) ≈ 2.5 atol = atol rtol = rtol @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [2.5, 2.5] atol = @@ -1095,26 +1277,91 @@ function _semitest(model::MOI.ModelLike, config::Config{T}, int::Bool) where {T} MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE_OR_UNBOUNDED end + return end -function semiconttest(model::MOI.ModelLike, config::Config) - return _semitest(model, config, false) -end -function semiinttest(model::MOI.ModelLike, config::Config) - return _semitest(model, config, true) +""" + test_Semicontinuous_integration(model::MOI.ModelLike, config::Config) + +Run an integration test on Semicontinuous constraints. +""" +function test_Semicontinuous_integration(model::MOI.ModelLike, config::Config) + _test_SemiXXX_integration(model, config, false) + return end -const intlineartests = Dict( - "knapsack" => knapsacktest, - "int1" => int1test, - "int2" => int2test, - "int3" => int3test, - "indicator1" => indicator1_test, - "indicator2" => indicator2_test, - "indicator3" => indicator3_test, - "indicator4" => indicator4_test, - "semiconttest" => semiconttest, - "semiinttest" => semiinttest, +function setup_test( + ::typeof(test_Semicontinuous_integration), + model::MOIU.MockOptimizer, + ::Config, ) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 0.0) + MOIU.mock_optimize!(mock, [0.0, 0.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!(mock, [2.0, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!(mock, [2.0, 2.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.5) + MOIU.mock_optimize!(mock, [2.5, 2.5]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 3.0) + MOIU.mock_optimize!(mock, [3.0, 3.0]) + end, + (mock::MOIU.MockOptimizer) -> + MOI.set(mock, MOI.TerminationStatus(), MOI.INFEASIBLE), + ) + return +end + +""" + test_Semiinteger_integration(model::MOI.ModelLike, config::Config) + +Run an integration test on Semiinteger constraints. +""" +function test_Semiinteger_integration(model::MOI.ModelLike, config::Config) + _test_SemiXXX_integration(model, config, true) + return +end -@moitestset intlinear +function setup_test( + ::typeof(test_Semiinteger_integration), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 0.0) + MOIU.mock_optimize!(mock, [0.0, 0.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!(mock, [2.0, 1.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!(mock, [2.0, 2.0]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 3.0) + MOIU.mock_optimize!(mock, [3.0, 2.5]) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 3.0) + MOIU.mock_optimize!(mock, [3.0, 3.0]) + end, + (mock::MOIU.MockOptimizer) -> + MOI.set(mock, MOI.TerminationStatus(), MOI.INFEASIBLE), + ) + return +end diff --git a/test/Bridges/Constraint/zero_one.jl b/test/Bridges/Constraint/zero_one.jl index 07698c0f91..cfb1a6844d 100644 --- a/test/Bridges/Constraint/zero_one.jl +++ b/test/Bridges/Constraint/zero_one.jl @@ -65,14 +65,14 @@ config = MOIT.Config() MOIU.mock_optimize!(mock, [4, 5, 1]) end, ) - MOIT.int1test(bridged_mock, config) + MOIT.test_integer_integration(bridged_mock, config) MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0; zeros(10)]), ) - MOIT.int3test(bridged_mock, config) + MOIT.test_integer_solve_twice(bridged_mock, config) ci = first( MOI.get( diff --git a/test/Test/Test.jl b/test/Test/Test.jl index c0fc2b8c69..82916cd8c9 100644 --- a/test/Test/Test.jl +++ b/test/Test/Test.jl @@ -20,13 +20,3 @@ MOI.Test.runtests( "test_qp_complementarity_constraint", ], ) - -MOI.Test.runtests( - MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), - MOI.Test.Config(modify_lhs = false), - # Oops! Name clash. - exclude = [ - "test_linear_mixed_complementarity", - "test_qp_complementarity_constraint", - ], -) diff --git a/test/Test/intlinear.jl b/test/Test/intlinear.jl deleted file mode 100644 index 0416541a70..0000000000 --- a/test/Test/intlinear.jl +++ /dev/null @@ -1,118 +0,0 @@ -using Test -import MathOptInterface -const MOI = MathOptInterface -const MOIT = MOI.Test -const MOIU = MOI.Utilities - -mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) -config = MOIT.Config() - -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 20.0) - MOIU.mock_optimize!(mock, [4, 5, 1]) - end, -) -MOIT.int1test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 1, 2]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1, 1, 2]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 3.0, 12.0], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0.0, 0.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 6.0, 24.0], - ), -) -MOIT.int2test(mock, config) -# FIXME [1, 0...] is not the correct optimal solution but it passes the test -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0; zeros(10)]), -) -MOIT.int3test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1, 0, 0, 1, 1]), -) -MOIT.knapsacktest(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, [1.25, 8.75, 0.0, 1.0]), -) -MOIT.indicator1_test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, [2.0, 8.0, 1.0, 0.0]), -) -MOIT.indicator2_test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, [1.25, 8.75, 1.0, 1.0]), -) -MOIT.indicator3_test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, [1.25, 8.75, 0.0, 1.0]), -) -MOIT.indicator4_test(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 0.0) - MOIU.mock_optimize!(mock, [0.0, 0.0]) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 2.0) - MOIU.mock_optimize!(mock, [2.0, 1.0]) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 2.0) - MOIU.mock_optimize!(mock, [2.0, 2.0]) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 2.5) - MOIU.mock_optimize!(mock, [2.5, 2.5]) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 3.0) - MOIU.mock_optimize!(mock, [3.0, 3.0]) - end, - (mock::MOIU.MockOptimizer) -> - MOI.set(mock, MOI.TerminationStatus(), MOI.INFEASIBLE), -) -MOIT.semiconttest(mock, config) -MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 0.0) - MOIU.mock_optimize!(mock, [0.0, 0.0]) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 2.0) - MOIU.mock_optimize!(mock, [2.0, 1.0]) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 2.0) - MOIU.mock_optimize!(mock, [2.0, 2.0]) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 3.0) - MOIU.mock_optimize!(mock, [3.0, 2.5]) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 3.0) - MOIU.mock_optimize!(mock, [3.0, 3.0]) - end, - (mock::MOIU.MockOptimizer) -> - MOI.set(mock, MOI.TerminationStatus(), MOI.INFEASIBLE), -) -MOIT.semiinttest(mock, config) From a88782060364c61e8a7bda895ce98e97653722ae Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 16:50:04 +1200 Subject: [PATCH 06/23] Fix formatting --- src/Test/intconic.jl | 3 +- src/Test/intlinear.jl | 90 +++++++++++++++---------------------------- 2 files changed, 33 insertions(+), 60 deletions(-) diff --git a/src/Test/intconic.jl b/src/Test/intconic.jl index ba2935f44f..4ddf7dfff1 100644 --- a/src/Test/intconic.jl +++ b/src/Test/intconic.jl @@ -98,7 +98,8 @@ function setup_test( ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0, 0.0]), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [1.0, 1.0, 0.0]), ) return end diff --git a/src/Test/intlinear.jl b/src/Test/intlinear.jl index 5c8466e77d..d8e425e4b9 100644 --- a/src/Test/intlinear.jl +++ b/src/Test/intlinear.jl @@ -140,7 +140,7 @@ end function setup_test( ::typeof(test_integer_integration), model::MOI.Utilities.MockOptimizer, - ::Config + ::Config, ) MOIU.set_mock_optimize!( model, @@ -180,23 +180,11 @@ function test_SOS1_integration(model::MOI.ModelLike, config::Config) @test MOI.is_empty(model) v = MOI.add_variables(model, 3) @test MOI.get(model, MOI.NumberOfVariables()) == 3 - vc1 = MOI.add_constraint( - model, - MOI.SingleVariable(v[1]), - MOI.LessThan(1.0), - ) + vc1 = MOI.add_constraint(model, MOI.SingleVariable(v[1]), MOI.LessThan(1.0)) @test vc1.value == v[1].value - vc2 = MOI.add_constraint( - model, - MOI.SingleVariable(v[2]), - MOI.LessThan(1.0), - ) + vc2 = MOI.add_constraint(model, MOI.SingleVariable(v[2]), MOI.LessThan(1.0)) @test vc2.value == v[2].value - vc3 = MOI.add_constraint( - model, - MOI.SingleVariable(v[3]), - MOI.LessThan(2.0), - ) + vc3 = MOI.add_constraint(model, MOI.SingleVariable(v[3]), MOI.LessThan(2.0)) @test vc3.value == v[3].value c1 = MOI.add_constraint( model, @@ -211,10 +199,7 @@ function test_SOS1_integration(model::MOI.ModelLike, config::Config) if config.query_number_of_constraints @test MOI.get( model, - MOI.NumberOfConstraints{ - MOI.VectorOfVariables, - MOI.SOS1{Float64}, - }(), + MOI.NumberOfConstraints{MOI.VectorOfVariables,MOI.SOS1{Float64}}(), ) == 2 end #= @@ -226,10 +211,8 @@ function test_SOS1_integration(model::MOI.ModelLike, config::Config) p = sortperm(cs_sos.weights) @test cs_sos.weights[p] ≈ [1.0, 2.0] atol = atol rtol = rtol @test cf_sos.variables[p] == v[[1, 3]] - objf = MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([2.0, 1.0, 1.0], v), - 0.0, - ) + objf = + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0, 1.0], v), 0.0) MOI.set( model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), @@ -238,30 +221,25 @@ function test_SOS1_integration(model::MOI.ModelLike, config::Config) MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE if config.solve - @test MOI.get(model, MOI.TerminationStatus()) == - MOI.OPTIMIZE_NOT_CALLED + @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.TerminationStatus()) == config.optimal_status @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 3 atol = atol rtol = + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 3 atol = atol rtol = rtol + @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [0, 1, 2] atol = atol rtol = rtol - @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [0, 1, 2] atol = - atol rtol = rtol end MOI.delete(model, c1) MOI.delete(model, c2) if config.solve MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == - config.optimal_status + @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 5 atol = atol rtol = + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 5 atol = atol rtol = rtol + @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [1, 1, 2] atol = atol rtol = rtol - @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [1, 1, 2] atol = - atol rtol = rtol end return end @@ -269,7 +247,7 @@ end function setup_test( ::typeof(test_SOS1_integration), model::MOI.Utilities.MockOptimizer, - ::Config + ::Config, ) MOIU.set_mock_optimize!( model, @@ -324,11 +302,7 @@ function test_SOS2_integration(model::MOI.ModelLike, config::Config) @test vc.value == v[i].value push!( bin_constraints, - MOI.add_constraint( - model, - MOI.SingleVariable(v[i]), - MOI.ZeroOne(), - ), + MOI.add_constraint(model, MOI.SingleVariable(v[i]), MOI.ZeroOne()), ) @test bin_constraints[i].value == v[i].value end @@ -366,8 +340,7 @@ function test_SOS2_integration(model::MOI.ModelLike, config::Config) cs_sos = MOI.get(model, MOI.ConstraintSet(), c) cf_sos = MOI.get(model, MOI.ConstraintFunction(), c) p = sortperm(cs_sos.weights) - @test cs_sos.weights[p] ≈ [1.0, 2.0, 4.0, 5.0, 7.0] atol = atol rtol = - rtol + @test cs_sos.weights[p] ≈ [1.0, 2.0, 4.0, 5.0, 7.0] atol = atol rtol = rtol @test cf_sos.variables[p] == v[[8, 7, 5, 4, 6]] objf = MOI.ScalarAffineFunction( MOI.ScalarAffineTerm.([1.0, 1.0], [v[9], v[10]]), @@ -381,33 +354,30 @@ function test_SOS2_integration(model::MOI.ModelLike, config::Config) MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE if config.solve - @test MOI.get(model, MOI.TerminationStatus()) == - MOI.OPTIMIZE_NOT_CALLED + @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.TerminationStatus()) == config.optimal_status @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ObjectiveValue()) ≈ 15.0 atol = atol rtol = rtol @test MOI.get(model, MOI.VariablePrimal(), v) ≈ - [0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 3.0, 12.0] atol = - atol rtol = rtol + [0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 3.0, 12.0] atol = atol rtol = + rtol end for cref in bin_constraints MOI.delete(model, cref) end if config.solve MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == - config.optimal_status + @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ObjectiveValue()) ≈ 30.0 atol = atol rtol = rtol @test MOI.get(model, MOI.VariablePrimal(), v) ≈ - [0.0, 0.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 6.0, 24.0] atol = - atol rtol = rtol + [0.0, 0.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 6.0, 24.0] atol = atol rtol = + rtol end return end @@ -415,7 +385,7 @@ end function setup_test( ::typeof(test_SOS2_integration), model::MOI.Utilities.MockOptimizer, - ::Config + ::Config, ) MOIU.set_mock_optimize!( model, @@ -518,12 +488,13 @@ end function setup_test( ::typeof(test_integer_solve_twice), model::MOI.Utilities.MockOptimizer, - ::Config + ::Config, ) # FIXME [1, 0...] is not the correct optimal solution but it passes the test MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0; zeros(10)]), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [1.0; zeros(10)]), ) return end @@ -612,11 +583,12 @@ end function setup_test( ::typeof(test_integer_knapsack), model::MOI.Utilities.MockOptimizer, - ::Config + ::Config, ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1, 0, 0, 1, 1]), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [1, 0, 0, 1, 1]), ) return end From 4df1cdb2fd7ed3ec5be082e07feab2e80de5cd8f Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 17:21:28 +1200 Subject: [PATCH 07/23] Migrate nlp.jl tests --- src/Test/nlp.jl | 506 +++++++++++++++++++++++++++++----------------- test/Test/Test.jl | 15 +- test/Test/nlp.jl | 108 ---------- 3 files changed, 328 insertions(+), 301 deletions(-) delete mode 100644 test/Test/nlp.jl diff --git a/src/Test/nlp.jl b/src/Test/nlp.jl index c78e39de38..bb3258d958 100644 --- a/src/Test/nlp.jl +++ b/src/Test/nlp.jl @@ -1,5 +1,19 @@ -const VI = MOI.VariableIndex +""" + HS071( + enable_hessian::Bool, + enable_hessian_vector_product::Bool = false, + ) +An AbstractNLPEvaluator for the problem: +``` +min x1 * x4 * (x1 + x2 + x3) + x3 +st x1 * x2 * x3 * x4 >= 25 + x1^2 + x2^2 + x3^2 + x4^2 = 40 + 1 <= x1, x2, x3, x4 <= 5 +``` +Start at (1,5,5,1) +End at (1.000..., 4.743..., 3.821..., 1.379...) +""" struct HS071 <: MOI.AbstractNLPEvaluator enable_hessian::Bool enable_hessian_vector_product::Bool @@ -11,14 +25,6 @@ struct HS071 <: MOI.AbstractNLPEvaluator end end -# hs071 -# min x1 * x4 * (x1 + x2 + x3) + x3 -# st x1 * x2 * x3 * x4 >= 25 -# x1^2 + x2^2 + x3^2 + x4^2 = 40 -# 1 <= x1, x2, x3, x4 <= 5 -# Start at (1,5,5,1) -# End at (1.000..., 4.743..., 3.821..., 1.379...) - function MOI.initialize(d::HS071, requested_features::Vector{Symbol}) for feat in requested_features if !(feat in MOI.features_available(d)) @@ -39,35 +45,47 @@ function MOI.features_available(d::HS071) return features end -function MOI.objective_expr(d::HS071) +function MOI.objective_expr(::HS071) return :( - x[$(VI(1))] * x[$(VI(4))] * (x[$(VI(1))] + x[$(VI(2))] + x[$(VI(3))]) + - x[$(VI(3))] + x[$(MOI.VariableIndex(1))] * + x[$(MOI.VariableIndex(4))] * + ( + x[$(MOI.VariableIndex(1))] + + x[$(MOI.VariableIndex(2))] + + x[$(MOI.VariableIndex(3))] + ) + x[$(MOI.VariableIndex(3))] ) end -function MOI.constraint_expr(d::HS071, i::Int) +function MOI.constraint_expr(::HS071, i::Int) if i == 1 - return :(x[$(VI(1))] * x[$(VI(2))] * x[$(VI(3))] * x[$(VI(4))] >= 25.0) + return :( + x[$(MOI.VariableIndex(1))] * + x[$(MOI.VariableIndex(2))] * + x[$(MOI.VariableIndex(3))] * + x[$(MOI.VariableIndex(4))] >= 25.0 + ) elseif i == 2 return :( - x[$(VI(1))]^2 + x[$(VI(2))]^2 + x[$(VI(3))]^2 + x[$(VI(4))]^2 == - 40.0 + x[$(MOI.VariableIndex(1))]^2 + + x[$(MOI.VariableIndex(2))]^2 + + x[$(MOI.VariableIndex(3))]^2 + + x[$(MOI.VariableIndex(4))]^2 == 40.0 ) else error("Out of bounds constraint.") end end -MOI.eval_objective(d::HS071, x) = x[1] * x[4] * (x[1] + x[2] + x[3]) + x[3] +MOI.eval_objective(::HS071, x) = x[1] * x[4] * (x[1] + x[2] + x[3]) + x[3] -function MOI.eval_constraint(d::HS071, g, x) +function MOI.eval_constraint(::HS071, g, x) g[1] = x[1] * x[2] * x[3] * x[4] g[2] = x[1]^2 + x[2]^2 + x[3]^2 + x[4]^2 return end -function MOI.eval_objective_gradient(d::HS071, grad_f, x) +function MOI.eval_objective_gradient(::HS071, grad_f, x) grad_f[1] = x[1] * x[4] + x[4] * (x[1] + x[2] + x[3]) grad_f[2] = x[1] * x[4] grad_f[3] = x[1] * x[4] + 1 @@ -75,7 +93,7 @@ function MOI.eval_objective_gradient(d::HS071, grad_f, x) return end -function MOI.jacobian_structure(d::HS071) +function MOI.jacobian_structure(::HS071) return Tuple{Int64,Int64}[ (1, 1), (1, 2), @@ -87,7 +105,7 @@ function MOI.jacobian_structure(d::HS071) (2, 4), ] end -# lower triangle only + function MOI.hessian_lagrangian_structure(d::HS071) @assert d.enable_hessian return Tuple{Int64,Int64}[ @@ -104,7 +122,7 @@ function MOI.hessian_lagrangian_structure(d::HS071) ] end -function MOI.eval_constraint_jacobian(d::HS071, J, x) +function MOI.eval_constraint_jacobian(::HS071, J, x) # Constraint (row) 1 J[1] = x[2] * x[3] * x[4] # 1,1 J[2] = x[1] * x[3] * x[4] # 1,2 @@ -176,11 +194,81 @@ function MOI.eval_hessian_lagrangian_product(d::HS071, h, x, v, σ, μ) return end -function hs071test_template( - model::MOI.ModelLike, - config::Config, - evaluator::HS071, +""" + FeasibilitySenseEvaluator(enable_hessian::Bool) + +An AbstractNLPEvaluator for the problem: +``` +Test for FEASIBILITY_SENSE. +Find x satisfying x^2 == 1. +``` +""" +struct FeasibilitySenseEvaluator <: MOI.AbstractNLPEvaluator + enable_hessian::Bool +end + +function MOI.initialize( + d::FeasibilitySenseEvaluator, + requested_features::Vector{Symbol}, ) + for feat in requested_features + if !(feat in MOI.features_available(d)) + error("Unsupported feature $feat") + # TODO: implement Jac-vec and Hess-vec products + # for solvers that need them + end + end + return +end + +function MOI.features_available(d::FeasibilitySenseEvaluator) + if d.enable_hessian + return [:Grad, :Jac, :Hess, :ExprGraph] + else + return [:Grad, :Jac, :ExprGraph] + end +end + +MOI.objective_expr(::FeasibilitySenseEvaluator) = :() + +function MOI.constraint_expr(::FeasibilitySenseEvaluator, i::Int) + @assert i == 1 + return :(x[$(MOI.VariableIndex(1))]^2 == 1) +end + +MOI.eval_objective(d::FeasibilitySenseEvaluator, x) = 0.0 + +function MOI.eval_constraint(::FeasibilitySenseEvaluator, g, x) + g[1] = x[1]^2 + return +end + +function MOI.eval_objective_gradient(::FeasibilitySenseEvaluator, grad_f, x) + grad_f[1] = 0.0 + return +end + +function MOI.jacobian_structure(::FeasibilitySenseEvaluator) + return Tuple{Int64,Int64}[(1, 1)] +end + +function MOI.hessian_lagrangian_structure(d::FeasibilitySenseEvaluator) + @assert d.enable_hessian + return Tuple{Int64,Int64}[(1, 1)] +end + +function MOI.eval_constraint_jacobian(::FeasibilitySenseEvaluator, J, x) + J[1] = 2 * x[1] + return +end + +function MOI.eval_hessian_lagrangian(d::FeasibilitySenseEvaluator, H, x, σ, μ) + @assert d.enable_hessian + H[1] = 2 * μ[1] # 1,1 + return +end + +function _test_HS071(model::MOI.ModelLike, config::Config, evaluator::HS071) atol = config.atol rtol = config.rtol @test MOI.supports(model, MOI.NLPBlock()) @@ -238,162 +326,115 @@ function hs071test_template( rtol # TODO: Duals? Maybe better to test on a convex instance. end + return end -function hs071_test(model, config) - return hs071test_template(model, config, HS071(true)) -end - -function hs071_no_hessian_test(model, config) - return hs071test_template(model, config, HS071(false)) -end - -function hs071_hessian_vector_product_test(model, config) - return hs071test_template(model, config, HS071(false, true)) -end +""" + test_nonlinear_hs071(model::MOI.ModelLike, config::Config) -# Test for FEASIBILITY_SENSE. -# Find x satisfying x^2 == 1. -struct FeasibilitySenseEvaluator <: MOI.AbstractNLPEvaluator - enable_hessian::Bool +Test the nonlinear HS071 problem. +""" +function test_nonlinear_hs071(model::MOI.ModelLike, config::Config) + _test_HS071(model, config, HS071(true)) + return end -function MOI.initialize( - d::FeasibilitySenseEvaluator, - requested_features::Vector{Symbol}, +function setup_test( + ::typeof(test_nonlinear_hs071), + model::MOIU.MockOptimizer, + config::Config, ) - for feat in requested_features - if !(feat in MOI.features_available(d)) - error("Unsupported feature $feat") - # TODO: implement Jac-vec and Hess-vec products - # for solvers that need them - end - end -end - -function MOI.features_available(d::FeasibilitySenseEvaluator) - if d.enable_hessian - return [:Grad, :Jac, :Hess, :ExprGraph] - else - return [:Grad, :Jac, :ExprGraph] - end -end - -MOI.objective_expr(d::FeasibilitySenseEvaluator) = :() - -function MOI.constraint_expr(d::FeasibilitySenseEvaluator, i::Int) - if i == 1 - return :(x[$(VI(1))]^2 == 1) - else - error("Out of bounds constraint.") - end -end - -MOI.eval_objective(d::FeasibilitySenseEvaluator, x) = 0.0 - -function MOI.eval_constraint(d::FeasibilitySenseEvaluator, g, x) - return g[1] = x[1]^2 -end - -function MOI.eval_objective_gradient(d::FeasibilitySenseEvaluator, grad_f, x) - return grad_f[1] = 0.0 -end - -function MOI.jacobian_structure(d::FeasibilitySenseEvaluator) - return Tuple{Int64,Int64}[(1, 1)] -end - -function MOI.hessian_lagrangian_structure(d::FeasibilitySenseEvaluator) - @assert d.enable_hessian - return Tuple{Int64,Int64}[(1, 1)] + MOI.Utilities.set_mock_optimize!( + model, + (mock) -> begin + MOI.Utilities.mock_optimize!( + mock, + config.optimal_status, + [ + 1.0, + 4.7429996418092970, + 3.8211499817883077, + 1.379408289755698, + ], + ) + MOI.set(mock, MOI.ObjectiveValue(), 17.014017145179164) + end, + ) + return end -function MOI.eval_constraint_jacobian(d::FeasibilitySenseEvaluator, J, x) - return J[1] = 2x[1] -end +""" + test_nonlinear_hs071_no_hessian(model::MOI.ModelLike, config::Config) -function MOI.eval_hessian_lagrangian(d::FeasibilitySenseEvaluator, H, x, σ, μ) - @assert d.enable_hessian - return H[1] = 2μ[1] # 1,1 +Test the nonlinear HS071 problem without hessians. +""" +function test_nonlinear_hs071_no_hessian(model, config) + _test_HS071(model, config, HS071(false)) + return end -function feasibility_sense_test_template( - model::MOI.ModelLike, +function setup_test( + ::typeof(test_nonlinear_hs071_no_hessian), + model::MOIU.MockOptimizer, config::Config, - set_has_objective::Bool, - evaluator::FeasibilitySenseEvaluator, ) - atol = config.atol - rtol = config.rtol - @test MOI.supports(model, MOI.NLPBlock()) - @test MOI.supports(model, MOI.VariablePrimalStart(), MOI.VariableIndex) - MOI.empty!(model) - @test MOI.is_empty(model) - lb = [1.0] - ub = [1.0] - block_data = MOI.NLPBlockData( - MOI.NLPBoundsPair.(lb, ub), - evaluator, - set_has_objective, - ) - x = MOI.add_variable(model) - @test MOI.get(model, MOI.NumberOfVariables()) == 1 - # Avoid starting at zero because it's a critial point. - MOI.set(model, MOI.VariablePrimalStart(), x, 1.5) - MOI.set(model, MOI.NLPBlock(), block_data) - MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) - # TODO: config.query tests - if config.solve - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status - @test MOI.get(model, MOI.ResultCount()) >= 1 - @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 0.0 atol = atol rtol = rtol - @test abs(MOI.get(model, MOI.VariablePrimal(), x)) ≈ 1.0 atol = atol rtol = - rtol - end -end - -function feasibility_sense_with_objective_and_hessian_test(model, config) - return feasibility_sense_test_template( + MOI.Utilities.set_mock_optimize!( model, - config, - true, - FeasibilitySenseEvaluator(true), + (mock) -> begin + MOI.Utilities.mock_optimize!( + mock, + config.optimal_status, + [ + 1.0, + 4.7429996418092970, + 3.8211499817883077, + 1.379408289755698, + ], + ) + MOI.set(mock, MOI.ObjectiveValue(), 17.014017145179164) + end, ) + return end -function feasibility_sense_with_objective_and_no_hessian_test(model, config) - return feasibility_sense_test_template( - model, - config, - true, - FeasibilitySenseEvaluator(false), +""" + test_nonlinear_hs071_hessian_vector_product( + model::MOI.ModelLike, + config::Config, ) -end -function feasibility_sense_with_no_objective_and_with_hessian_test( - model, - config, -) - return feasibility_sense_test_template( - model, - config, - false, - FeasibilitySenseEvaluator(true), - ) +Test the nonlinear HS071 problem with a hessian vector product. +""" +function hs071_hessian_vector_product_test(model, config) + _test_HS071(model, config, HS071(false, true)) + return end -function feasibility_sense_with_no_objective_and_no_hessian_test(model, config) - return feasibility_sense_test_template( +function setup_test( + ::typeof(hs071_hessian_vector_product_test), + model::MOIU.MockOptimizer, + config::Config, +) + MOI.Utilities.set_mock_optimize!( model, - config, - false, - FeasibilitySenseEvaluator(false), + (mock) -> begin + MOI.Utilities.mock_optimize!( + mock, + config.optimal_status, + [ + 1.0, + 4.7429996418092970, + 3.8211499817883077, + 1.379408289755698, + ], + ) + MOI.set(mock, MOI.ObjectiveValue(), 17.014017145179164) + end, ) + return end -function nlp_objective_and_moi_objective_test( +function test_nonlinear_objective_and_moi_objective_test( model::MOI.ModelLike, config::Config, ) @@ -426,36 +467,36 @@ function nlp_objective_and_moi_objective_test( @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ObjectiveValue()) ≈ 0.0 atol = atol rtol = rtol - @test 1.0 ≤ MOI.get(model, MOI.VariablePrimal(), x) ≤ 2.0 + xv = MOI.get(model, MOI.VariablePrimal(), x) + @test abs(xv) ≈ 1.0 atol = atol rtol = rtol end end -const nlptests = Dict( - "hs071" => hs071_test, - "hs071_no_hessian" => hs071_no_hessian_test, - "hs071_hessian_vector_product_test" => - hs071_hessian_vector_product_test, - "feasibility_sense_with_objective_and_hessian" => - feasibility_sense_with_objective_and_hessian_test, - "feasibility_sense_with_objective_and_no_hessian" => - feasibility_sense_with_objective_and_no_hessian_test, - "feasibility_sense_with_no_objective_and_with_hessian" => - feasibility_sense_with_no_objective_and_with_hessian_test, - "feasibility_sense_with_no_objective_and_no_hessian" => - feasibility_sense_with_no_objective_and_no_hessian_test, - "nlp_objective_and_moi_objective" => - nlp_objective_and_moi_objective_test, +function setup_test( + ::typeof(test_nonlinear_objective_and_moi_objective_test), + model::MOIU.MockOptimizer, + config::Config, ) - -@moitestset nlp + MOI.Utilities.set_mock_optimize!( + model, + (mock) -> begin + MOI.Utilities.mock_optimize!(mock, config.optimal_status, [-1.0]), + MOI.set(mock, MOI.ObjectiveValue(), 0.0) + end, + ) + return +end """ - test_linear_mixed_complementarity(model::MOI.ModelLike, config::Config) + test_nonlinear_mixed_complementarity(model::MOI.ModelLike, config::Config) Test the solution of the linear mixed-complementarity problem: `F(x) complements x`, where `F(x) = M * x .+ q` and `0 <= x <= 10`. """ -function test_linear_mixed_complementarity(model::MOI.ModelLike, config::Config) +function test_nonlinear_mixed_complementarity( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) x = MOI.add_variables(model, 4) MOI.add_constraint.(model, MOI.SingleVariable.(x), MOI.Interval(0.0, 10.0)) @@ -493,17 +534,30 @@ function test_linear_mixed_complementarity(model::MOI.ModelLike, config::Config) rtol = config.rtol, ) end + return end -const mixed_complementaritytests = Dict( - "test_linear_mixed_complementarity" => - test_linear_mixed_complementarity, +function setup_test( + ::typeof(test_nonlinear_mixed_complementarity), + model::MOIU.MockOptimizer, + config::Config, ) - -@moitestset mixed_complementarity + MOI.Utilities.set_mock_optimize!( + model, + (mock) -> MOI.Utilities.mock_optimize!( + mock, + config.optimal_status, + [2.8, 0.0, 0.8, 1.2], + ), + ) + return +end """ - test_qp_complementarity_constraint(model::MOI.ModelLike, config::Config) + test_nonlinear_qp_complementarity_constraint( + model::MOI.ModelLike, + config::Config, + ) Test the solution of the quadratic program with complementarity constraints: @@ -531,7 +585,7 @@ which rewrites, with auxiliary variables ``` """ -function test_qp_complementarity_constraint( +function test_nonlinear_qp_complementarity_constraint( model::MOI.ModelLike, config::Config, ) @@ -603,11 +657,87 @@ function test_qp_complementarity_constraint( rtol = config.rtol, ) end + return end -const math_program_complementarity_constraintstests = Dict( - "test_qp_complementarity_constraint" => - test_qp_complementarity_constraint, +function setup_test( + ::typeof(test_nonlinear_qp_complementarity_constraint), + model::MOIU.MockOptimizer, + config::Config, ) + MOI.Utilities.set_mock_optimize!( + model, + (mock) -> begin + MOI.Utilities.mock_optimize!( + mock, + config.optimal_status, + [1.0, 0.0, 3.5, 0.0, 0.0, 0.0, 3.0, 6.0], + ) + MOI.set(mock, MOI.ObjectiveValue(), 17.0) + end, + ) + return +end + +""" + test_nonlinear_HS071_internal(::MOI.ModelLike, ::Config) + +A test for the correctness of the HS071 evaluator. -@moitestset math_program_complementarity_constraints +This is mainly for the internal purpose of checking their correctness as +written. External solvers can exclude this test without consequence. +""" +function test_nonlinear_HS071_internal(::MOI.ModelLike, ::Config) + d = MOI.Test.HS071(true, true) + @test MOI.objective_expr(d) == :( + x[$(MOI.VariableIndex(1))] * + x[$(MOI.VariableIndex(4))] * + ( + x[$(MOI.VariableIndex(1))] + + x[$(MOI.VariableIndex(2))] + + x[$(MOI.VariableIndex(3))] + ) + x[$(MOI.VariableIndex(3))] + ) + @test MOI.constraint_expr(d, 1) == :( + x[$(MOI.VariableIndex(1))] * + x[$(MOI.VariableIndex(2))] * + x[$(MOI.VariableIndex(3))] * + x[$(MOI.VariableIndex(4))] >= 25.0 + ) + @test MOI.constraint_expr(d, 2) == :( + x[$(MOI.VariableIndex(1))]^2 + + x[$(MOI.VariableIndex(2))]^2 + + x[$(MOI.VariableIndex(3))]^2 + + x[$(MOI.VariableIndex(4))]^2 == 40.0 + ) + @test_throws ErrorException MOI.constraint_expr(d, 3) + MOI.initialize(d, [:Grad, :Jac, :ExprGraph, :Hess, :HessVec]) + @test :HessVec in MOI.features_available(d) + x = ones(4) + # f(x) + @test MOI.eval_objective(d, x) == 4.0 + # g(x) + g = zeros(2) + MOI.eval_constraint(d, g, x) + @test g == [1.0, 4.0] + # f'(x) + ∇f = fill(NaN, length(x)) + MOI.eval_objective_gradient(d, ∇f, x) + @test ∇f == [4.0, 1.0, 2.0, 3.0] + # Jacobian + Js = MOI.jacobian_structure(d) + J = fill(NaN, length(Js)) + MOI.eval_constraint_jacobian(d, J, x) + @test J == [1, 1, 1, 1, 2, 2, 2, 2] + # Hessian-lagrangian + Hs = MOI.hessian_lagrangian_structure(d) + H = fill(NaN, length(Hs)) + MOI.eval_hessian_lagrangian(d, H, x, 1.0, [1.0, 1.0]) + @test H == [4, 2, 2, 2, 1, 2, 5, 2, 2, 2] + # Hessian-lagrangian-product + Hv = fill(NaN, length(x)) + v = [1.0, 1.1, 1.2, 1.3] + MOI.eval_hessian_lagrangian_product(d, Hv, x, v, 1.0, [1.0, 1.0]) + @test Hv == [15.1, 8.0, 8.1, 12.2] + return +end diff --git a/test/Test/Test.jl b/test/Test/Test.jl index 82916cd8c9..ed6e32e1ce 100644 --- a/test/Test/Test.jl +++ b/test/Test/Test.jl @@ -14,9 +14,14 @@ end MOI.Test.runtests( MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), MOI.Test.Config(basis = true), - # Oops! Name clash. - exclude = [ - "test_linear_mixed_complementarity", - "test_qp_complementarity_constraint", - ], + exclude = ["test_nonlinear_"], +) + +MOI.Test.runtests( + MOIU.MockOptimizer( + MOIU.UniversalFallback(MOIU.Model{Float64}()), + eval_objective_value = false, + ), + MOI.Test.Config(optimal_status = MOI.LOCALLY_SOLVED), + include = ["test_nonlinear_"], ) diff --git a/test/Test/nlp.jl b/test/Test/nlp.jl deleted file mode 100644 index d2b931d261..0000000000 --- a/test/Test/nlp.jl +++ /dev/null @@ -1,108 +0,0 @@ -using Test - -import MathOptInterface -const MOI = MathOptInterface - -@testset "hs071-manual" begin - d = MOI.Test.HS071(true, true) - MOI.initialize(d, [:Grad, :Jac, :ExprGraph, :Hess, :HessVec]) - @test :HessVec in MOI.features_available(d) - x = ones(4) - # f(x) - @test MOI.eval_objective(d, x) == 4.0 - # g(x) - g = zeros(2) - MOI.eval_constraint(d, g, x) - @test g == [1.0, 4.0] - # f'(x) - ∇f = fill(NaN, length(x)) - MOI.eval_objective_gradient(d, ∇f, x) - @test ∇f == [4.0, 1.0, 2.0, 3.0] - # Jacobian - Js = MOI.jacobian_structure(d) - J = fill(NaN, length(Js)) - MOI.eval_constraint_jacobian(d, J, x) - @test J == [1, 1, 1, 1, 2, 2, 2, 2] - # Hessian-lagrangian - Hs = MOI.hessian_lagrangian_structure(d) - H = fill(NaN, length(Hs)) - MOI.eval_hessian_lagrangian(d, H, x, 1.0, [1.0, 1.0]) - @test H == [4, 2, 2, 2, 1, 2, 5, 2, 2, 2] - # Hessian-lagrangian-product - Hv = fill(NaN, length(x)) - v = [1.0, 1.1, 1.2, 1.3] - MOI.eval_hessian_lagrangian_product(d, Hv, x, v, 1.0, [1.0, 1.0]) - @test Hv == [15.1, 8.0, 8.1, 12.2] -end - -@testset "hs071" begin - mock = MOI.Utilities.MockOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - eval_objective_value = false, - ) - config = MOI.Test.Config(optimal_status = MOI.LOCALLY_SOLVED) - MOI.Utilities.set_mock_optimize!( - mock, - (mock) -> begin - MOI.Utilities.mock_optimize!( - mock, - config.optimal_status, - [ - 1.0, - 4.7429996418092970, - 3.8211499817883077, - 1.379408289755698, - ], - ) - MOI.set(mock, MOI.ObjectiveValue(), 17.014017145179164) - end, - ) - MOI.Test.hs071_test(mock, config) - MOI.Test.hs071_no_hessian_test(mock, config) - MOI.Test.hs071_hessian_vector_product_test(mock, config) - - d = MOI.Test.HS071(false) - VI = MOI.VariableIndex - @test MOI.objective_expr(d) == :( - x[$(VI(1))] * x[$(VI(4))] * (x[$(VI(1))] + x[$(VI(2))] + x[$(VI(3))]) + - x[$(VI(3))] - ) - @test MOI.constraint_expr(d, 1) == - :(x[$(VI(1))] * x[$(VI(2))] * x[$(VI(3))] * x[$(VI(4))] >= 25.0) - @test MOI.constraint_expr(d, 2) == :( - x[$(VI(1))]^2 + x[$(VI(2))]^2 + x[$(VI(3))]^2 + x[$(VI(4))]^2 == 40.0 - ) - @test_throws ErrorException MOI.constraint_expr(d, 3) -end - -@testset "mixed_complementarity" begin - mock = MOI.Utilities.MockOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - ) - config = MOI.Test.Config(optimal_status = MOI.LOCALLY_SOLVED) - MOI.Utilities.set_mock_optimize!( - mock, - (mock) -> MOI.Utilities.mock_optimize!( - mock, - config.optimal_status, - [2.8, 0.0, 0.8, 1.2], - ), - ) - MOI.Test.mixed_complementaritytest(mock, config) -end - -@testset "math_program_complementarity_constraints" begin - mock = MOI.Utilities.MockOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - ) - config = MOI.Test.Config(optimal_status = MOI.LOCALLY_SOLVED) - MOI.Utilities.set_mock_optimize!( - mock, - (mock) -> MOI.Utilities.mock_optimize!( - mock, - config.optimal_status, - [1.0, 0.0, 3.5, 0.0, 0.0, 0.0, 3.0, 6.0], - ), - ) - MOI.Test.math_program_complementarity_constraintstest(mock, config) -end From 12c5b6c49ad1c2a8831c6e242df71b3751e100be Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 17:41:01 +1200 Subject: [PATCH 08/23] Fix tests --- test/Bridges/Constraint/semi_to_binary.jl | 2 +- test/Bridges/Constraint/zero_one.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Bridges/Constraint/semi_to_binary.jl b/test/Bridges/Constraint/semi_to_binary.jl index 596bdeb391..8299352117 100644 --- a/test/Bridges/Constraint/semi_to_binary.jl +++ b/test/Bridges/Constraint/semi_to_binary.jl @@ -63,7 +63,7 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOI.set(mock, MOI.TerminationStatus(), MOI.INFEASIBLE), ) - MOIT.semiconttest(bridged_mock, config) + MOIT.test_Semicontinuous_integration(bridged_mock, config) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/zero_one.jl b/test/Bridges/Constraint/zero_one.jl index cfb1a6844d..45d99da54a 100644 --- a/test/Bridges/Constraint/zero_one.jl +++ b/test/Bridges/Constraint/zero_one.jl @@ -36,7 +36,7 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1, 0, 0, 1, 1]), ) - MOIT.knapsacktest(bridged_mock, config) + MOIT.test_integer_knapsack(bridged_mock, config) ci = first( MOI.get( From 66dbc5d4c8f0b4af6e6fbf960cf2a2574b5b7d7e Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 17:57:09 +1200 Subject: [PATCH 09/23] Another fix --- test/Bridges/Constraint/semi_to_binary.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Bridges/Constraint/semi_to_binary.jl b/test/Bridges/Constraint/semi_to_binary.jl index 8299352117..7ac5bd1b65 100644 --- a/test/Bridges/Constraint/semi_to_binary.jl +++ b/test/Bridges/Constraint/semi_to_binary.jl @@ -113,7 +113,7 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOI.set(mock, MOI.TerminationStatus(), MOI.INFEASIBLE), ) - MOIT.semiinttest(bridged_mock, config) + MOIT.test_Semiinteger_integration(bridged_mock, config) ci = first( MOI.get( From 3c9cad5c239b86be2e35788f3ce03f99f7c40019 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 19:04:54 +1200 Subject: [PATCH 10/23] Improve code coverage --- src/Test/nlp.jl | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Test/nlp.jl b/src/Test/nlp.jl index bb3258d958..670a697f3e 100644 --- a/src/Test/nlp.jl +++ b/src/Test/nlp.jl @@ -405,13 +405,13 @@ end Test the nonlinear HS071 problem with a hessian vector product. """ -function hs071_hessian_vector_product_test(model, config) +function test_nonlinear_hs071_hessian_vector_product(model, config) _test_HS071(model, config, HS071(false, true)) return end function setup_test( - ::typeof(hs071_hessian_vector_product_test), + ::typeof(test_nonlinear_hs071_hessian_vector_product), model::MOIU.MockOptimizer, config::Config, ) @@ -741,3 +741,42 @@ function test_nonlinear_HS071_internal(::MOI.ModelLike, ::Config) @test Hv == [15.1, 8.0, 8.1, 12.2] return end + +""" + test_nonlinear_Feasibility_internal(::MOI.ModelLike, ::Config) + +A test for the correctness of the FeasibilitySenseEvaluator evaluator. + +This is mainly for the internal purpose of checking their correctness as +written. External solvers can exclude this test without consequence. +""" +function test_nonlinear_Feasibility_internal(::MOI.ModelLike, ::Config) + d = MOI.Test.FeasibilitySenseEvaluator(true) + @test MOI.objective_expr(d) == :() + @test MOI.constraint_expr(d, 1) == :(x[$(MOI.VariableIndex(1))]^2 == 1.0) + @test_throws AssertionError MOI.constraint_expr(d, 2) + MOI.initialize(d, [:Grad, :Jac, :ExprGraph, :Hess]) + @test :Hess in MOI.features_available(d) + x = [1.5] + # f(x) + @test MOI.eval_objective(d, x) == 0.0 + # g(x) + g = zeros(1) + MOI.eval_constraint(d, g, x) + @test g == [1.5^2] + # f'(x) + ∇f = fill(NaN, length(x)) + MOI.eval_objective_gradient(d, ∇f, x) + @test ∇f == [0.0] + # Jacobian + Js = MOI.jacobian_structure(d) + J = fill(NaN, length(Js)) + MOI.eval_constraint_jacobian(d, J, x) + @test J == [3.0] + # Hessian-lagrangian + Hs = MOI.hessian_lagrangian_structure(d) + H = fill(NaN, length(Hs)) + MOI.eval_hessian_lagrangian(d, H, x, 1.0, [1.1]) + @test H == [2.2] + return +end From c21c8cbb2074546dcfb2913f811251ad0ecfbf5d Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 19:47:32 +1200 Subject: [PATCH 11/23] Migrate contquadratic --- src/Test/contquadratic.jl | 430 ++++++++++++++++++++----- test/Bridges/Constraint/quad_to_soc.jl | 10 +- test/Bridges/Objective/slack.jl | 6 +- test/Test/config.jl | 28 -- test/Test/contquadratic.jl | 125 ------- 5 files changed, 351 insertions(+), 248 deletions(-) delete mode 100644 test/Test/config.jl delete mode 100644 test/Test/contquadratic.jl diff --git a/src/Test/contquadratic.jl b/src/Test/contquadratic.jl index 1c019497a3..0fa2dcbe56 100644 --- a/src/Test/contquadratic.jl +++ b/src/Test/contquadratic.jl @@ -1,13 +1,17 @@ -# Continuous quadratic problems +""" + test_quadratic_integration(model::MOI.ModelLike, config::Config) -function qp1test(model::MOI.ModelLike, config::Config) +Test the problem: +``` +Min x^2 + xy + y^2 + yz + z^2 +st x + 2y + 3z >= 4 (c1) + x + y >= 1 (c2) + x, y, z in R +``` +""" +function test_quadratic_integration(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # homogeneous quadratic objective - # Min x^2 + xy + y^2 + yz + z^2 - # st x + 2y + 3z >= 4 (c1) - # x + y >= 1 (c2) - # x, y, z \in R @test MOI.supports_incremental_interface(model, false) #=copy_names=# MOI.supports( model, @@ -96,17 +100,40 @@ function qp1test(model::MOI.ModelLike, config::Config) rtol end end + return end -function qp2test(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_quadratic_integration), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [4 / 7, 3 / 7, 6 / 7], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [5 / 7, 6 / 7], + ), + ) + return +end + +""" + test_quadratic_duplicate_terms(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +Min x^2 + xy + y^2 + yz + z^2 +st x + 2y + 3z >= 4 (c1) + x + y >= 1 (c2) + x, y, z in R +``` +""" +function test_quadratic_duplicate_terms(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # Same as `qp1` but with duplicate terms then change the objective and sense - # simple quadratic objective - # Min x^2 + xy + y^2 + yz + z^2 - # st x + 2y + 3z >= 4 (c1) - # x + y >= 1 (c2) - # x, y, z \in R @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -232,15 +259,45 @@ function qp2test(model::MOI.ModelLike, config::Config) rtol end end + return +end + +function setup_test( + ::typeof(test_quadratic_duplicate_terms), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [4 / 7, 3 / 7, 6 / 7], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [5 / 7, 6 / 7], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [4 / 7, 3 / 7, 6 / 7], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [10 / 7, 12 / 7], + ), + ) + return end -function qp3test(model::MOI.ModelLike, config::Config) +""" + test_quadratic_nonhomogeneous(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +minimize 2 x^2 + y^2 + xy + x + y + 1 + s.t. x, y >= 0 + x + y = 1 +``` +""" +function test_quadratic_nonhomogeneous(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # non-homogeneous quadratic objective - # minimize 2 x^2 + y^2 + xy + x + y + 1 - # s.t. x, y >= 0 - # x + y = 1 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -296,7 +353,8 @@ function qp3test(model::MOI.ModelLike, config::Config) rtol if config.duals @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT - # The dual constraint gives [λ_vc1 + λ_c1, λ_vc2 + λ_c1] = [4 1; 1 2] * x + [1, 1] = [11, 11] / 4 + # The dual constraint gives + # [λ_vc1 + λ_c1, λ_vc2 + λ_c1] = [4 1; 1 2] * x + [1, 1] = [11, 11] / 4 # since `vc1` and `vc2` are not active, `λ_vc1` and `λ_vc2` are # zero so `λ_c1 = 11/4`. @test MOI.get(model, MOI.ConstraintDual(), c1) ≈ 11 / 4 atol = atol rtol = @@ -312,7 +370,8 @@ function qp3test(model::MOI.ModelLike, config::Config) # s.t. x, y >= 0 # x + y = 1 # (x,y) = (1,0), obj = 3 - # First clear the objective, this is needed if a `Bridges.Objective.SlackBridge` is used. + # First clear the objective, this is needed if a + # `Bridges.Objective.SlackBridge` is used. MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) objf = @@ -343,26 +402,51 @@ function qp3test(model::MOI.ModelLike, config::Config) rtol end end + return end -const qptests = Dict("qp1" => qp1test, "qp2" => qp2test, "qp3" => qp3test) - -@moitestset qp +function setup_test( + ::typeof(test_quadratic_nonhomogeneous), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1 / 4, 3 / 4], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [11 / 4], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 0.0], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-2.0], + ), + ) + return +end -#= - Quadratically constrained (convex) programs -=# +""" + test_quadratic_constraint_integration(model::MOI.ModelLike, config::Config) -function qcp1test(model::MOI.ModelLike, config::Config) +Test the problem: +``` +Max x + y +st -x + y >= 0 (c1[1]) + x + y >= 0 (c1[2]) + x² + y <= 2 (c2) +Optimal solution +x = 1/2, y = 7/4 +``` +""" +function test_quadratic_constraint_integration( + model::MOI.ModelLike, + config::Config, +) atol = config.atol rtol = config.rtol - # quadratic constraint - # Max x + y - # st -x + y >= 0 (c1[1]) - # x + y >= 0 (c1[2]) - # x² + y <= 2 (c2) - # Optimal solution - # x = 1/2, y = 7/4 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -469,13 +553,40 @@ function qcp1test(model::MOI.ModelLike, config::Config) # @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT # # @test MOI.get(model, MOI.ObjectiveValue()) ≈ 0.0 atol=atol rtol=rtol + return +end + +function setup_test( + ::typeof(test_quadratic_constraint_integration), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1 / 2, 7 / 4], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [zeros(2)], + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => + [-1.0], + ), + ) + return end -function qcp2test(model::MOI.ModelLike, config::Config) +""" + test_quadratic_constraint_basic(model::MOI.ModelLike, config::Config) + +Test the problem: +``` + Max x +s.t. x^2 <= 2 (c) +``` +""" +function test_quadratic_constraint_basic(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # Max x - # s.t. x^2 <= 2 (c) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -535,13 +646,44 @@ function qcp2test(model::MOI.ModelLike, config::Config) atol rtol = rtol end end + return +end + +function setup_test( + ::typeof(test_quadratic_constraint_basic), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [√2], + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => + [-1 / (2 * √2)], + ), + ) + return end -function qcp3test(model::MOI.ModelLike, config::Config) +""" + test_quadratic_constraint_minimize( + model::MOI.ModelLike, + config::Config, + ) + +Test the problem: +``` + Min -x +s.t. x^2 <= 2 +``` +""" +function test_quadratic_constraint_minimize( + model::MOI.ModelLike, + config::Config, +) atol = config.atol rtol = config.rtol - # Min -x - # s.t. x^2 <= 2 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -604,9 +746,34 @@ function qcp3test(model::MOI.ModelLike, config::Config) atol rtol = rtol end end + return end -function _qcp4test(model::MOI.ModelLike, config::Config, less_than::Bool) +function setup_test( + ::typeof(test_quadratic_constraint_minimize), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [√2], + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => + [-1 / (2 * √2)], + ), + ) + return +end + +""" + +""" +function _test_quadratic_constraint_helper( + model::MOI.ModelLike, + config::Config, + less_than::Bool, +) atol = config.atol rtol = config.rtol # Max x @@ -687,35 +854,77 @@ function _qcp4test(model::MOI.ModelLike, config::Config, less_than::Bool) rtol end end + return end -function qcp4test(model::MOI.ModelLike, config::Config) - return _qcp4test(model, config, true) +function test_quadratic_constraint_LessThan( + model::MOI.ModelLike, + config::Config, +) + _test_quadratic_constraint_helper(model, config, true) + return end -function qcp5test(model::MOI.ModelLike, config::Config) - return _qcp4test(model, config, false) + +function setup_test( + ::typeof(test_quadratic_constraint_LessThan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 1.0], + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => + [-1 / 3], + ), + ) + return end -const qcptests = Dict( - "qcp1" => qcp1test, - "qcp2" => qcp2test, - "qcp3" => qcp3test, - "qcp4" => qcp4test, - "qcp5" => qcp5test, +function test_quadratic_constraint_GreaterThan( + model::MOI.ModelLike, + config::Config, ) + _test_quadratic_constraint_helper(model, config, false) + return +end -@moitestset qcp +function setup_test( + ::typeof(test_quadratic_constraint_GreaterThan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 1.0], + (MOI.ScalarQuadraticFunction{Float64}, MOI.GreaterThan{Float64}) => [1 / 3], + ), + ) + return +end -#= - Quadratically constrained (non-convex) programs -=# +""" + test_nonconvex_quadratic_constraint_integration( + model::MOI.ModelLike, + config::Config, + ) -function ncqcp1test(model::MOI.ModelLike, config::Config) +Test the problem: +``` +Max 2x + y +s.t. x * y <= 4 (c) + x, y >= 1 +``` +""" +function test_nonconvex_quadratic_constraint_integration( + model::MOI.ModelLike, + config::Config, +) atol = config.atol rtol = config.rtol - # Max 2x + y - # s.t. x * y <= 4 (c) - # x, y >= 1 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -780,15 +989,42 @@ function ncqcp1test(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.ConstraintPrimal(), vc2) ≈ 1.0 atol = atol rtol = rtol end + return +end + +function setup_test( + ::typeof(test_nonconvex_quadratic_constraint_integration), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [4.0, 1.0], MOI.FEASIBLE_POINT), + ) + return end -function ncqcp2test(model::MOI.ModelLike, config::Config) +""" + test_nonconvex_quadratic_constraint_basic( + model::MOI.ModelLike, + config::Config, + ) + +Test the problem: +``` +Find x, y +s.t. x * y == 4 (c) + x * x == 4 (c2) + x, y >= 0 +``` +""" +function test_nonconvex_quadratic_constraint_basic( + model::MOI.ModelLike, + config::Config, +) atol = config.atol rtol = config.rtol - # Find x, y - # s.t. x * y == 4 (c) - # x * x == 4 (c2) - # x, y >= 0 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports_constraint( model, @@ -847,23 +1083,36 @@ function ncqcp2test(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.ConstraintPrimal(), vc2) ≈ 2.0 atol = atol rtol = rtol end + return end -const ncqcptests = Dict("ncqcp1" => ncqcp1test, "ncqcp2" => ncqcp2test) - -@moitestset ncqcp +function setup_test( + ::typeof(test_nonconvex_quadratic_constraint_basic), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, [2.0, 2.0], MOI.FEASIBLE_POINT), + ) + return +end -#= - SOCP -=# +""" + test_socp_basic(model::MOI.ModelLike, config::Config) -function socp1test(model::MOI.ModelLike, config::Config) +Test the problem: +``` +min t +s.t. x + y >= 1 (c1) + x^2 + y^2 <= t^2 (c2) + t >= 0 (bound) +``` +""" +function test_socp_basic(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # min t - # s.t. x + y >= 1 (c1) - # x^2 + y^2 <= t^2 (c2) - # t >= 0 (bound) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -966,17 +1215,24 @@ function socp1test(model::MOI.ModelLike, config::Config) rtol end end + return end -const socptests = Dict("socp1" => socp1test) - -@moitestset socp - -const contquadratictests = Dict( - "qp" => qptest, - "qcp" => qcptest, - "ncqcp" => ncqcptest, - "socp" => socptest, +function setup_test( + ::typeof(test_socp_basic), + model::MOIU.MockOptimizer, + ::Config, ) - -@moitestset contquadratic true + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1 / 2, 1 / 2, 1 / √2], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [1 / √2], + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => + [-1 / √2], + ), + ) + return +end diff --git a/test/Bridges/Constraint/quad_to_soc.jl b/test/Bridges/Constraint/quad_to_soc.jl index fe72710782..fe3051d6b5 100644 --- a/test/Bridges/Constraint/quad_to_soc.jl +++ b/test/Bridges/Constraint/quad_to_soc.jl @@ -92,7 +92,7 @@ config = MOIT.Config() ) => [[0.25, 1.0, -1 / √2]], ), ) - MOIT.qcp1test(bridged_mock, config) + MOIT.test_quadratic_constraint_integration(bridged_mock, config) MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -104,8 +104,8 @@ config = MOIT.Config() ) => [[1 / √2, 1 / (2 * √2), -1 / √2]], ), ) - MOIT.qcp2test(bridged_mock, config) - MOIT.qcp3test(bridged_mock, config) + MOIT.test_quadratic_constraint_basic(bridged_mock, config) + MOIT.test_quadratic_constraint_minimize(bridged_mock, config) @testset "Bridge deletion" begin ci = first( MOI.get( @@ -138,7 +138,7 @@ config = MOIT.Config() ) => [[1.0, 1 / 3, -1 / √2, -1 / √6]], ), ) - MOIT.qcp4test(bridged_mock, config) - MOIT.qcp5test(bridged_mock, config) + MOIT.test_quadratic_constraint_LessThan(bridged_mock, config) + MOIT.test_quadratic_constraint_GreaterThan(bridged_mock, config) end end diff --git a/test/Bridges/Objective/slack.jl b/test/Bridges/Objective/slack.jl index db7c668db1..333d528b0e 100644 --- a/test/Bridges/Objective/slack.jl +++ b/test/Bridges/Objective/slack.jl @@ -213,7 +213,7 @@ end (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [5 / 7, 6 / 7], ), ) - MOIT.qp1test(bridged_mock, config) + MOIT.test_quadratic_integration(bridged_mock, config) end @testset "QP2" begin MOIU.set_mock_optimize!( @@ -229,7 +229,7 @@ end (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [10 / 7, 12 / 7], ), ) - MOIT.qp2test(bridged_mock, config) + MOIT.test_quadratic_duplicate_terms(bridged_mock, config) end @testset "QP3" begin MOIU.set_mock_optimize!( @@ -249,6 +249,6 @@ end (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], ), ) - MOIT.qp3test(bridged_mock, config) + MOIT.test_quadratic_nonhomogeneous(bridged_mock, config) end end diff --git a/test/Test/config.jl b/test/Test/config.jl deleted file mode 100644 index e4433fe706..0000000000 --- a/test/Test/config.jl +++ /dev/null @@ -1,28 +0,0 @@ -using Test -import MathOptInterface -const MOI = MathOptInterface -const MOIT = MOI.Test -const MOIU = MOI.Utilities - -function atest(model::MOI.ModelLike, config::MOIT.Config{T}) where {T<:Real} - @test config.atol == Base.rtoldefault(T) - @test config.rtol == Base.rtoldefault(T) - @test config.solve - @test config.query - @test config.duals - @test config.infeas_certificates -end - -function btest(model::MOI.ModelLike, config::MOIT.Config) - @test false # b is in exclude -end - -const customtests = Dict("a" => atest, "b" => btest) - -MOIT.@moitestset custom - -@testset "Config" begin - mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) - config = MOIT.Config() - customtest(mock, config, ["b"]) -end diff --git a/test/Test/contquadratic.jl b/test/Test/contquadratic.jl deleted file mode 100644 index 6faf97e45f..0000000000 --- a/test/Test/contquadratic.jl +++ /dev/null @@ -1,125 +0,0 @@ -using Test -import MathOptInterface -const MOI = MathOptInterface -const MOIT = MOI.Test -const MOIU = MOI.Utilities - -mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) -config = MOIT.Config() - -@testset "QP" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [4 / 7, 3 / 7, 6 / 7], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [5 / 7, 6 / 7], - ), - ) - MOIT.qp1test(mock, config) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [4 / 7, 3 / 7, 6 / 7], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [5 / 7, 6 / 7], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [4 / 7, 3 / 7, 6 / 7], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [10 / 7, 12 / 7], - ), - ) - MOIT.qp2test(mock, config) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1 / 4, 3 / 4], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [11 / 4], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 0.0], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-2.0], - ), - ) - MOIT.qp3test(mock, config) -end -@testset "QCP" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1 / 2, 7 / 4], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [zeros(2)], - (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => - [-1.0], - ), - ) - MOIT.qcp1test(mock, config) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [√2], - (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => - [-1 / (2 * √2)], - ), - ) - MOIT.qcp2test(mock, config) - MOIT.qcp3test(mock, config) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 1.0], - (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => - [-1 / 3], - ), - ) - MOIT.qcp4test(mock, config) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 1.0], - (MOI.ScalarQuadraticFunction{Float64}, MOI.GreaterThan{Float64}) => [1 / 3], - ), - ) - MOIT.qcp5test(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.ncqcp1test(mock, config) - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, [2.0, 2.0], MOI.FEASIBLE_POINT), - ) - MOIT.ncqcp2test(mock, config) -end -@testset "SOCP" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1 / 2, 1 / 2, 1 / √2], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => - [1 / √2], - (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => - [-1 / √2], - ), - ) - MOIT.socp1test(mock, config) -end From 7c9a073d96047497b73314571ffb6784ec61703e Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 20:04:49 +1200 Subject: [PATCH 12/23] Migrate UnitTests/attributes.jl --- src/Test/UnitTests/attributes.jl | 106 +++++++++++++++++++++++++------ test/Test/unit.jl | 41 ------------ 2 files changed, 88 insertions(+), 59 deletions(-) diff --git a/src/Test/UnitTests/attributes.jl b/src/Test/UnitTests/attributes.jl index 0de503b44d..d244cadcc8 100644 --- a/src/Test/UnitTests/attributes.jl +++ b/src/Test/UnitTests/attributes.jl @@ -1,21 +1,21 @@ """ - solver_name(model::MOI.ModelLike, config::Config) + test_solver_name(model::MOI.ModelLike, config::Config) Test that the [`MOI.SolverName`](@ref) attribute is implemented for `model`. """ -function solver_name(model::MOI.ModelLike, config::Config) +function test_SolverName(model::MOI.ModelLike, config::Config) if config.solve @test MOI.get(model, MOI.SolverName()) isa AbstractString end + return end -unittests["solver_name"] = solver_name """ - silent(model::MOI.ModelLike, config::Config) + test_Silent(model::MOI.ModelLike, config::Config) Test that the [`MOI.Silent`](@ref) attribute is implemented for `model`. """ -function silent(model::MOI.ModelLike, config::Config) +function test_Silent(model::MOI.ModelLike, config::Config) if config.solve @test MOI.supports(model, MOI.Silent()) # Get the current value to restore it at the end of the test @@ -28,15 +28,25 @@ function silent(model::MOI.ModelLike, config::Config) MOI.set(model, MOI.Silent(), value) @test value == MOI.get(model, MOI.Silent()) end + return end -unittests["silent"] = silent + +function setup_test( + ::typeof(test_Silent), + model::MOIU.MockOptimizer, + ::Config, +) + MOI.set(model, MOI.Silent(), true) + return +end + """ - time_limit_sec(model::MOI.ModelLike, config::Config) + test_TimeLimitSec(model::MOI.ModelLike, config::Config) Test that the [`MOI.TimeLimitSec`](@ref) attribute is implemented for `model`. """ -function time_limit_sec(model::MOI.ModelLike, config::Config) +function test_TimeLimitSec(model::MOI.ModelLike, config::Config) if config.solve @test MOI.supports(model, MOI.TimeLimitSec()) # Get the current value to restore it at the end of the test @@ -48,15 +58,24 @@ function time_limit_sec(model::MOI.ModelLike, config::Config) MOI.set(model, MOI.TimeLimitSec(), value) @test value == MOI.get(model, MOI.TimeLimitSec()) # Equality should hold end + return +end + +function setup_test( + ::typeof(test_TimeLimitSec), + model::MOIU.MockOptimizer, + ::Config, +) + MOI.set(model, MOI.TimeLimitSec(), nothing) + return end -unittests["time_limit_sec"] = time_limit_sec """ - number_threads(model::MOI.ModelLike, config::Config) + test_NumberThreads(model::MOI.ModelLike, config::Config) Test that the [`MOI.NumberOfThreads`](@ref) attribute is implemented for `model`. """ -function number_threads(model::MOI.ModelLike, config::Config) +function test_NumberThreads(model::MOI.ModelLike, config::Config) if config.solve @test MOI.supports(model, MOI.NumberOfThreads()) # Get the current value to restore it at the end of the test @@ -68,16 +87,25 @@ function number_threads(model::MOI.ModelLike, config::Config) MOI.set(model, MOI.NumberOfThreads(), value) @test value == MOI.get(model, MOI.NumberOfThreads()) end + return +end + +function setup_test( + ::typeof(test_NumberThreads), + model::MOIU.MockOptimizer, + ::Config, +) + MOI.set(model, MOI.NumberOfThreads(), nothing) + return end -unittests["number_threads"] = number_threads """ - raw_status_string(model::MOI.ModelLike, config::Config) + test_RawStatusString(model::MOI.ModelLike, config::Config) Test that the [`MOI.RawStatusString`](@ref) attribute is implemented for `model`. """ -function raw_status_string(model::MOI.ModelLike, config::Config) +function test_RawStatusString(model::MOI.ModelLike, config::Config) MOI.empty!(model) @test MOI.is_empty(model) x = MOI.add_variable(model) @@ -97,15 +125,38 @@ function raw_status_string(model::MOI.ModelLike, config::Config) if config.solve @test MOI.get(model, MOI.RawStatusString()) isa AbstractString end + return +end + +function setup_test( + ::typeof(test_RawStatusString), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> begin + MOI.set( + mock, + MOI.RawStatusString(), + "Mock solution set by `mock_optimize!`.", + ) + MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.0]), + ) + end, + ) + return end -unittests["raw_status_string"] = raw_status_string """ - solve_time(model::MOI.ModelLike, config::Config) + test_SolveTimeSec(model::MOI.ModelLike, config::Config) Test that the [`MOI.SolveTimeSec`](@ref) attribute is implemented for `model`. """ -function solve_time(model::MOI.ModelLike, config::Config) +function test_SolveTimeSec(model::MOI.ModelLike, config::Config) MOI.empty!(model) @test MOI.is_empty(model) x = MOI.add_variable(model) @@ -126,5 +177,24 @@ function solve_time(model::MOI.ModelLike, config::Config) time = MOI.get(model, MOI.SolveTimeSec()) @test time ≥ 0.0 end + return +end + +function setup_test( + ::typeof(test_SolveTimeSec), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.SolveTimeSec(), 0.0) + MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.0]), + ) + end, + ) + return end -unittests["solve_time"] = solve_time diff --git a/test/Test/unit.jl b/test/Test/unit.jl index 896f3d2fc4..bc625c6aa3 100644 --- a/test/Test/unit.jl +++ b/test/Test/unit.jl @@ -13,11 +13,6 @@ end @testset "Unit Tests" begin # `UniversalFallback` needed for `MOI.Silent` mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) - # Optimizers attributes have to be set to default value since the mock - # optimizer doesn't handle this - MOI.set(mock, MOI.Silent(), true) - MOI.set(mock, MOI.TimeLimitSec(), nothing) - MOI.set(mock, MOI.NumberOfThreads(), nothing) config = MOIT.Config() for model in [ mock, @@ -45,8 +40,6 @@ end "solve_affine_deletion_edge_cases", "solve_duplicate_terms_obj", "solve_objbound_edge_cases", - "raw_status_string", - "solve_time", "solve_zero_one_with_bounds_1", "solve_zero_one_with_bounds_2", "solve_zero_one_with_bounds_3", @@ -333,40 +326,6 @@ end MOIT.solve_objbound_edge_cases(mock, config) end - @testset "raw_status_string" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> begin - MOI.set( - mock, - MOI.RawStatusString(), - "Mock solution set by `mock_optimize!`.", - ) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ) - end, - ) - MOIT.raw_status_string(mock, config) - end - - @testset "solve_time" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.SolveTimeSec(), 0.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ) - end, - ) - MOIT.solve_time(mock, config) - end - @testset "solve_zero_one_with_bounds" begin MOIU.set_mock_optimize!( mock, From 2b35b1656502ea878411d6a96a344ee8b49b17a9 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 22 Jun 2021 20:23:20 +1200 Subject: [PATCH 13/23] Migrate UnitTests/modifications.jl --- src/Test/UnitTests/modifications.jl | 383 ++++++++++++++++---- test/Bridges/Constraint/flip_sign.jl | 8 +- test/Bridges/Constraint/ltgt_to_interval.jl | 4 +- test/Bridges/Constraint/scalarize.jl | 2 +- test/Bridges/Variable/free.jl | 4 +- test/Test/unit.jl | 197 ---------- 6 files changed, 330 insertions(+), 268 deletions(-) diff --git a/src/Test/UnitTests/modifications.jl b/src/Test/UnitTests/modifications.jl index 7a12f2dd29..61cb7295ef 100644 --- a/src/Test/UnitTests/modifications.jl +++ b/src/Test/UnitTests/modifications.jl @@ -1,12 +1,16 @@ -const modificationtests = Dict{String,Function}() - """ - set_function_single_variable(model::MOI.ModelLike, config::Config) + test_modification_set_function_single_variable( + model::MOI.ModelLike, + config::Config, + ) Test that modifying the function of a `SingleVariable`-in-`LessThan` constraint throws a [`SettingSingleVariableFunctionNotAllowed`](@ref) error. """ -function set_function_single_variable(model::MOI.ModelLike, config::Config) +function test_modification_set_function_single_variable( + model::MOI.ModelLike, + ::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -25,17 +29,17 @@ function set_function_single_variable(model::MOI.ModelLike, config::Config) err = MOI.SettingSingleVariableFunctionNotAllowed() func = MOI.SingleVariable(y) @test_throws err MOI.set(model, MOI.ConstraintFunction(), c, func) + return end -modificationtests["set_function_single_variable"] = set_function_single_variable """ - solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Config) + test_solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Config) Test set modification SingleVariable-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Config) +function test_solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -58,7 +62,7 @@ function solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Config) ) MOI.set(model, MOI.ConstraintSet(), c, MOI.LessThan(2.0)) @test MOI.get(model, MOI.ConstraintSet(), c) == MOI.LessThan(2.0) - return test_model_solution( + test_model_solution( model, config; objective_value = 2.0, @@ -66,18 +70,40 @@ function solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 2.0)], constraint_dual = [(c, -1.0)], ) + return +end + +function setup_test( + ::typeof(test_solve_set_singlevariable_lessthan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [2.0]), + MOI.FEASIBLE_POINT, + ), + ) + return end -modificationtests["solve_set_singlevariable_lessthan"] = - solve_set_singlevariable_lessthan """ - solve_transform_singlevariable_lessthan(model::MOI.ModelLike, config::Config) + test_solve_transform_singlevariable_lessthan(model::MOI.ModelLike, config::Config) Test set transformation of a SingleVariable-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_transform_singlevariable_lessthan( +function test_solve_transform_singlevariable_lessthan( model::MOI.ModelLike, config::Config, ) @@ -105,7 +131,7 @@ function solve_transform_singlevariable_lessthan( @test !MOI.is_valid(model, c) @test MOI.is_valid(model, c2) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) - return test_model_solution( + test_model_solution( model, config; objective_value = 2.0, @@ -113,18 +139,40 @@ function solve_transform_singlevariable_lessthan( constraint_primal = [(c2, 2.0)], constraint_dual = [(c2, 1.0)], ) + return +end + +function setup_test( + ::typeof(test_solve_transform_singlevariable_lessthan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [2.0]), + MOI.FEASIBLE_POINT, + ), + ) + return end -modificationtests["solve_transform_singlevariable_lessthan"] = - solve_transform_singlevariable_lessthan """ - solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) + test_solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) Test modifying set of ScalarAffineFunction-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) +function test_solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -146,7 +194,7 @@ function solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) ) MOI.set(model, MOI.ConstraintSet(), c, MOI.LessThan(2.0)) @test MOI.get(model, MOI.ConstraintSet(), c) == MOI.LessThan(2.0) - return test_model_solution( + test_model_solution( model, config; objective_value = 2.0, @@ -154,18 +202,42 @@ function solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 2.0)], constraint_dual = [(c, -1.0)], ) + return +end + +function setup_test( + ::typeof(test_solve_set_scalaraffine_lessthan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [2.0]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + ), + ) + return end -modificationtests["solve_set_scalaraffine_lessthan"] = - solve_set_scalaraffine_lessthan """ - solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) + test_solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) Test modifying a variable coefficient in a ScalarAffineFunction-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) +function test_solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -186,7 +258,7 @@ function solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) constraint_dual = [(c, -1.0)], ) MOI.modify(model, c, MOI.ScalarCoefficientChange(x, 2.0)) - return test_model_solution( + test_model_solution( model, config; objective_value = 0.5, @@ -194,18 +266,42 @@ function solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 1.0)], constraint_dual = [(c, -0.5)], ) + return +end + +function setup_test( + ::typeof(test_solve_coef_scalaraffine_lessthan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], + ), + ) + return end -modificationtests["solve_coef_scalaraffine_lessthan"] = - solve_coef_scalaraffine_lessthan """ - solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) + test_solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) Test setting the function in a ScalarAffineFunction-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) +function test_solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -233,7 +329,7 @@ function solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) ) foo = MOI.get(model, MOI.ConstraintFunction(), c) @test foo ≈ MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(2.0, x)], 0.0) - return test_model_solution( + test_model_solution( model, config; objective_value = 0.5, @@ -241,18 +337,42 @@ function solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) # constraint_primal = [(c, 1.0)], constraint_dual = [(c, -0.5)], ) + return +end + +function setup_test( + ::typeof(test_solve_func_scalaraffine_lessthan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], + ), + ) + return end -modificationtests["solve_func_scalaraffine_lessthan"] = - solve_func_scalaraffine_lessthan """ - solve_func_vectoraffine_nonneg(model::MOI.ModelLike, config::Config) + test_solve_func_vectoraffine_nonneg(model::MOI.ModelLike, config::Config) Test setting the function in a VectorAffineFunction-in-Nonnegatives constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_func_vectoraffine_nonneg(model::MOI.ModelLike, config::Config) +function test_solve_func_vectoraffine_nonneg(model::MOI.ModelLike, config::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -292,25 +412,45 @@ function solve_func_vectoraffine_nonneg(model::MOI.ModelLike, config::Config) ], [-1.0, -1.5], ) - return test_model_solution( + test_model_solution( model, config; objective_value = 2.5, variable_primal = [(x, 1.0), (y, 0.75)], constraint_primal = [(c, [0.0, 0.0])], ) + return +end + +function setup_test( + ::typeof(test_solve_func_vectoraffine_nonneg), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.0, 0.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 0.75]), + ), + ) + return end -modificationtests["solve_func_vectoraffine_nonneg"] = - solve_func_vectoraffine_nonneg """ - solve_const_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) + test_solve_const_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) Test modifying the constant term in a VectorAffineFunction-in-Nonpositives constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_const_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) +function test_solve_const_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -340,25 +480,45 @@ function solve_const_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) constraint_primal = [(c, [0.0, 0.0])], ) MOI.modify(model, c, MOI.VectorConstantChange([-1.0, -1.5])) - return test_model_solution( + test_model_solution( model, config; objective_value = 2.5, variable_primal = [(x, 1.0), (y, 0.75)], constraint_primal = [(c, [0.0, 0.0])], ) + return +end + +function setup_test( + ::typeof(test_solve_const_vectoraffine_nonpos), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.0, 0.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 0.75]), + ), + ) + return end -modificationtests["solve_const_vectoraffine_nonpos"] = - solve_const_vectoraffine_nonpos """ - solve_multirow_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) + test_solve_multirow_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) Test modifying the variable coefficients in a VectorAffineFunction-in-Nonpositives constraint. If `config.solve=true` confirm that it solves correctly. """ -function solve_multirow_vectoraffine_nonpos( +function test_solve_multirow_vectoraffine_nonpos( model::MOI.ModelLike, config::Config, ) @@ -390,24 +550,44 @@ function solve_multirow_vectoraffine_nonpos( constraint_primal = [(c, [-0.5, 0.0])], ) MOI.modify(model, c, MOI.MultirowChange(x, [(1, 4.0), (2, 3.0)])) - return test_model_solution( + test_model_solution( model, config; objective_value = 0.25, variable_primal = [(x, 0.25)], constraint_primal = [(c, [0.0, -0.25])], ) + return +end + +function setup_test( + ::typeof(test_solve_multirow_vectoraffine_nonpos), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.25]), + ), + ) + return end -modificationtests["solve_multirow_vectoraffine_nonpos"] = - solve_multirow_vectoraffine_nonpos """ - solve_const_scalar_objective(model::MOI.ModelLike, config::Config) + test_solve_const_scalar_objective(model::MOI.ModelLike, config::Config) Test the constant of a scalaraffine objective. If `config.solve=true` confirm that it solves correctly. """ -function solve_const_scalar_objective(model::MOI.ModelLike, config::Config) +function test_solve_const_scalar_objective(model::MOI.ModelLike, config::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -429,22 +609,43 @@ function solve_const_scalar_objective(model::MOI.ModelLike, config::Config) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarConstantChange(3.0), ) - return test_model_solution( + test_model_solution( model, config; objective_value = 4.0, variable_primal = [(x, 1.0)], ) + return +end + +function setup_test( + ::typeof(test_solve_const_scalar_objective), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + ) + return end -modificationtests["solve_const_scalar_objective"] = solve_const_scalar_objective """ - solve_coef_scalar_objective(model::MOI.ModelLike, config::Config) + test_solve_coef_scalar_objective(model::MOI.ModelLike, config::Config) Test modifying a variable coefficient in a scalaraffine objective. If `config.solve=true` confirm that it solves correctly. """ -function solve_coef_scalar_objective(model::MOI.ModelLike, config::Config) +function test_solve_coef_scalar_objective(model::MOI.ModelLike, config::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -466,20 +667,40 @@ function solve_coef_scalar_objective(model::MOI.ModelLike, config::Config) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarCoefficientChange(x, 3.0), ) - return test_model_solution( + test_model_solution( model, config; objective_value = 3.0, variable_primal = [(x, 1.0)], ) + return +end + +function setup_test( + ::typeof(test_solve_coef_scalar_objective), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + ) + return end -modificationtests["solve_coef_scalar_objective"] = solve_coef_scalar_objective -function delete_variable_with_single_variable_obj( +function test_delete_variable_with_single_variable_obj( model::MOI.ModelLike, config::Config, ) - atol, rtol = config.atol, config.rtol MOI.empty!(model) @test MOI.is_empty(model) MOIU.loadfromstring!( @@ -497,7 +718,7 @@ function delete_variable_with_single_variable_obj( ) @test c.value == x.value MOI.delete(model, y) - return test_model_solution( + test_model_solution( model, config; objective_value = 1.0, @@ -505,18 +726,35 @@ function delete_variable_with_single_variable_obj( constraint_primal = [(c, 1.0)], constraint_dual = [(c, 1.0)], ) + return +end + +function setup_test( + ::typeof(test_delete_variable_with_single_variable_obj), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1.0], + ), + ) + return end -modificationtests["delete_variable_with_single_variable_obj"] = - delete_variable_with_single_variable_obj """ - delete_variables_in_a_batch(model::MOI.ModelLike, config::Config) + test_delete_variables_in_a_batch(model::MOI.ModelLike, config::Config) Test deleting many variables in a batch (i.e. using the delete method which takes a vector of variable references). If `config.solve=true` confirm that it solves correctly. """ -function delete_variables_in_a_batch(model::MOI.ModelLike, config::Config) +function test_delete_variables_in_a_batch(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol MOI.empty!(model) @@ -547,7 +785,28 @@ function delete_variables_in_a_batch(model::MOI.ModelLike, config::Config) MOI.optimize!(model) @test MOI.get(model, MOI.ObjectiveValue()) ≈ 2.0 atol = atol rtol = rtol end + return end -modificationtests["delete_variables_in_a_batch"] = delete_variables_in_a_batch -@moitestset modification +function setup_test( + ::typeof(test_delete_variables_in_a_batch), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 1.0, 1.0]), + MOI.FEASIBLE_POINT, + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + ), + ) + return +end diff --git a/test/Bridges/Constraint/flip_sign.jl b/test/Bridges/Constraint/flip_sign.jl index d23c6b1513..968e0243fb 100644 --- a/test/Bridges/Constraint/flip_sign.jl +++ b/test/Bridges/Constraint/flip_sign.jl @@ -99,7 +99,7 @@ end [1.0], ), ) - MOIT.solve_set_scalaraffine_lessthan(bridged_mock, config) + MOIT.test_solve_set_scalaraffine_lessthan(bridged_mock, config) MOIU.set_mock_optimize!( mock, @@ -120,7 +120,7 @@ end [0.5], ), ) - MOIT.solve_coef_scalaraffine_lessthan(bridged_mock, config) + MOIT.test_solve_coef_scalaraffine_lessthan(bridged_mock, config) ci = first( MOI.get( @@ -268,7 +268,7 @@ end (MOI.FEASIBLE_POINT, [1.0, 0.75]), ), ) - MOIT.solve_const_vectoraffine_nonpos(bridged_mock, config) + MOIT.test_solve_const_vectoraffine_nonpos(bridged_mock, config) MOIU.set_mock_optimize!( mock, @@ -280,7 +280,7 @@ end (MOI.FEASIBLE_POINT, [0.25]), ), ) - MOIT.solve_multirow_vectoraffine_nonpos(bridged_mock, config) + MOIT.test_solve_multirow_vectoraffine_nonpos(bridged_mock, config) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/ltgt_to_interval.jl b/test/Bridges/Constraint/ltgt_to_interval.jl index f0badd7afe..f38167ea26 100644 --- a/test/Bridges/Constraint/ltgt_to_interval.jl +++ b/test/Bridges/Constraint/ltgt_to_interval.jl @@ -100,7 +100,7 @@ end [-1.0], ), ) - MOIT.solve_set_scalaraffine_lessthan(bridged_mock, config) + MOIT.test_solve_set_scalaraffine_lessthan(bridged_mock, config) MOIU.set_mock_optimize!( mock, @@ -121,7 +121,7 @@ end [-0.5], ), ) - MOIT.solve_coef_scalaraffine_lessthan(bridged_mock, config) + MOIT.test_solve_coef_scalaraffine_lessthan(bridged_mock, config) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/scalarize.jl b/test/Bridges/Constraint/scalarize.jl index e0c12d0742..17d940d3f7 100644 --- a/test/Bridges/Constraint/scalarize.jl +++ b/test/Bridges/Constraint/scalarize.jl @@ -168,7 +168,7 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.0, 0.0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.75]), ) - MOIT.solve_func_vectoraffine_nonneg(bridged_mock, config) + MOIT.test_solve_func_vectoraffine_nonneg(bridged_mock, config) # VectorOfVariables-in-Nonnegatives # VectorOfVariables-in-Nonpositives diff --git a/test/Bridges/Variable/free.jl b/test/Bridges/Variable/free.jl index dfee3977d5..a1b65da9dd 100644 --- a/test/Bridges/Variable/free.jl +++ b/test/Bridges/Variable/free.jl @@ -13,7 +13,7 @@ config = MOIT.Config() bridged_mock = MOIB.Variable.Free{Float64}(mock) -@testset "solve_multirow_vectoraffine_nonpos" begin +@testset "test_solve_multirow_vectoraffine_nonpos" begin MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -27,7 +27,7 @@ bridged_mock = MOIB.Variable.Free{Float64}(mock) (MOI.FEASIBLE_POINT, [0.25, 0.0]), ), ) - MOIT.solve_multirow_vectoraffine_nonpos(bridged_mock, config) + MOIT.test_solve_multirow_vectoraffine_nonpos(bridged_mock, config) MOI.set( bridged_mock, diff --git a/test/Test/unit.jl b/test/Test/unit.jl index bc625c6aa3..9d13bd3f8a 100644 --- a/test/Test/unit.jl +++ b/test/Test/unit.jl @@ -564,200 +564,3 @@ end MOIT.solve_twice(mock, config) end end - -@testset "modifications" begin - # `UniversalFallback` needed for `MOI.Silent` - mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) - config = MOIT.Config() - @testset "delete_variables_in_a_batch" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 1.0, 1.0]), - MOI.FEASIBLE_POINT, - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - ), - ) - MOIT.delete_variables_in_a_batch(mock, config) - end - @testset "solve_set_singlevariable_lessthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - MOI.FEASIBLE_POINT, - ), - ) - MOIT.solve_set_singlevariable_lessthan(mock, config) - end - @testset "solve_transform_singlevariable_lessthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - MOI.FEASIBLE_POINT, - ), - ) - MOIT.solve_transform_singlevariable_lessthan(mock, config) - end - @testset "solve_set_scalaraffine_lessthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], - ), - ) - MOIT.solve_set_scalaraffine_lessthan(mock, config) - end - @testset "solve_coef_scalaraffine_lessthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], - ), - ) - MOIT.solve_coef_scalaraffine_lessthan(mock, config) - end - @testset "solve_func_scalaraffine_lessthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], - ), - ) - MOIT.solve_func_scalaraffine_lessthan(mock, config) - end - @testset "solve_const_vectoraffine_nonpos" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0, 0.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 0.75]), - ), - ) - MOIT.solve_const_vectoraffine_nonpos(mock, config) - end - @testset "solve_multirow_vectoraffine_nonpos" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.25]), - ), - ) - MOIT.solve_multirow_vectoraffine_nonpos(mock, config) - end - @testset "solve_const_scalar_objective" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - ) - MOIT.solve_const_scalar_objective(mock, config) - end - @testset "solve_coef_scalar_objective" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - ) - MOIT.solve_coef_scalar_objective(mock, config) - end - @testset "delete_variable_with_single_variable_obj" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1.0], - ), - ) - MOIT.delete_variable_with_single_variable_obj(mock, config) - end -end From 79263f1d5537ead302bc54d44bb98f77069cfe1c Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 09:13:38 +1200 Subject: [PATCH 14/23] Various fixes and updates to documentation --- docs/src/submodules/Test/overview.md | 320 +++++++++++++-------------- src/Test/UnitTests/attributes.jl | 19 +- src/Test/UnitTests/modifications.jl | 191 ++++++++++------ test/Utilities/universalfallback.jl | 13 +- 4 files changed, 284 insertions(+), 259 deletions(-) diff --git a/docs/src/submodules/Test/overview.md b/docs/src/submodules/Test/overview.md index 94c1ec5468..bc80373f0f 100644 --- a/docs/src/submodules/Test/overview.md +++ b/docs/src/submodules/Test/overview.md @@ -20,9 +20,7 @@ so that all solvers can benefit. ## How to test a solver The skeleton below can be used for the wrapper test file of a solver named -`FooBar`. Remove unnecessary tests as appropriate, for example tests for -features that the solver does not support (tests are not skipped depending -on the value of `supports`). +`FooBar`. ```julia # ============================ /test/MOI_wrapper.jl ============================ @@ -34,15 +32,15 @@ using Test const MOI = MathOptInterface -const OPTIMIZER_CONSTRUCTOR = MOI.OptimizerWithAttributes( - FooBar.Optimizer, - MOI.Silent() => true +const OPTIMIZER = MOI.instantiate( + MOI.OptimizerWithAttributes(FooBar.Optimizer, MOI.Silent() => true), ) -const OPTIMIZER = MOI.instantiate(OPTIMIZER_CONSTRUCTOR) const BRIDGED = MOI.instantiate( - OPTIMIZER_CONSTRUCTOR, with_bridge_type = Float64 + MOI.OptimizerWithAttributes(FooBar.Optimizer, MOI.Silent() => true), + with_bridge_type = Float64, ) + const CONFIG = MOI.Test.Config( # Modify tolerances as necessary. atol = 1e-6, @@ -57,196 +55,184 @@ const CONFIG = MOI.Test.Config( basis = false, ) -function test_SolverName() - @test MOI.get(OPTIMIZER, MOI.SolverName()) == "FooBar" -end +""" + runtests() -function test_supports_incremental_interface() - @test MOI.supports_incremental_interface(OPTIMIZER, false) - # Use `@test !...` if names are not supported - @test MOI.supports_incremental_interface(OPTIMIZER, true) +This function runs all functions in the this Module starting with `test_`. +""" +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end end -function test_unittest() - # Test all the functions included in dictionary `MOI.Test.unittests`, - # except functions "number_threads" and "solve_qcp_edge_cases." - MOI.Test.unittest( +""" + test_runtests() + +This function runs all the tests in MathOptInterface.Test. + +Pass arguments to `exclude` to skip tests for functionality that is not +implemented or that your solver doesn't support. +""" +function test_runtests() + MOI.Test.runtests( BRIDGED, CONFIG, - ["number_threads", "solve_qcp_edge_cases"] + exclude = [ + "test_NumberOfThreads", + "solve_qcp_edge_cases", + ] ) + return end -function test_modification() - MOI.Test.modificationtest(BRIDGED, CONFIG) -end - -function test_contlinear() - MOI.Test.runtests(OPTIMIZER, CONFIG, include = ["test_linear_"]) -end +""" + test_SolverName() -function test_contquadratictest() - MOI.Test.contquadratictest(OPTIMIZER, CONFIG) +You can also write new tests for solver-specific functionality. Write each new +test as a function with a name beginning with `test_`. +""" +function test_SolverName() + @test MOI.get(FooBar.Optimizer(), MOI.SolverName()) == "FooBar" end -function test_contconic() - MOI.Test.contconictest(BRIDGED, CONFIG) -end +end # module TestFooBar -function test_intconic() - MOI.Test.intconictest(BRIDGED, CONFIG) -end +# This line at tne end of the file runs all the tests! +TestFooBar.runtests() +``` -function test_default_objective_test() - MOI.Test.default_objective_test(OPTIMIZER) -end +Then modify your `runtests.jl` file to include the `MOI_wrapper.jl` file: +```julia +# ============================ /test/runtests.jl ============================ -function test_default_status_test() - MOI.Test.default_status_test(OPTIMIZER) -end +using Test -function test_nametest() - MOI.Test.nametest(OPTIMIZER) +@testset "MOI" begin + include("test/MOI_wrapper.jl") end +``` -function test_validtest() - MOI.Test.validtest(OPTIMIZER) -end +!!! info + The optimizer `BRIDGED` constructed with [`instantiate`](@ref) + automatically bridges constraints that are not supported by `OPTIMIZER` + using the bridges listed in [Bridges](@ref). It is recommended for an + implementation of MOI to only support constraints that are natively + supported by the solver and let bridges transform the constraint to the + appropriate form. For this reason it is expected that tests may not pass if + `OPTIMIZER` is used instead of `BRIDGED`. -function test_emptytest() - MOI.Test.emptytest(OPTIMIZER) -end +## How to add a test -function test_orderedindicestest() - MOI.Test.orderedindicestest(OPTIMIZER) -end +To detect bugs in solvers, we add new tests to `MOI.Test`. -function test_scalar_function_constant_not_zero() - MOI.Test.scalar_function_constant_not_zero(OPTIMIZER) -end +As an example, ECOS errored calling [`optimize!`](@ref) twice in a row. (See +[ECOS.jl PR #72](https://github.com/jump-dev/ECOS.jl/pull/72).) We could add a +test to ECOS.jl, but that would only stop us from re-introducing the bug to +ECOS.jl in the future, but it would not catch other solvers in the ecosystem +with the same bug! Instead, if we add a test to `MOI.Test`, then all solvers +will also check that they handle a double optimize call! -# This function runs all functions in this module starting with `test_`. -function runtests() - for name in names(@__MODULE__; all = true) - if startswith("$(name)", "test_") - @testset "$(name)" begin - getfield(@__MODULE__, name)() - end - end - end -end +For this test, we care about correctness, rather than performance. therefore, we +don't expect solvers to efficiently decide that they have already solved the +problem, only that calling [`optimize!`](@ref) twice doesn't throw an error or +give the wrong answer. -end # module TestFooBar +**Step 1** -TestFooBar.runtests() +Install the `MathOptInterface` julia package in `dev` mode +([ref](https://julialang.github.io/Pkg.jl/v1/managing-packages/#developing-1)): +```julia +julia> ] +(@v1.6) pkg> dev MathOptInterface ``` -Test functions like `MOI.Test.unittest` and `MOI.Test.modificationtest` are -wrappers around corresponding dictionaries `MOI.Test.unittests` and -`MOI.Test.modificationtests`. Exclude tests by passing a vector of strings -corresponding to the test keys you want to exclude as the third positional -argument to the test function. +**Step 2** -!!! tip - Print a list of all keys using `println.(keys(MOI.Test.unittests))` - -The optimizer `BRIDGED` constructed with [`instantiate`](@ref) -automatically bridges constraints that are not supported by `OPTIMIZER` -using the bridges listed in [Bridges](@ref). It is recommended for an -implementation of MOI to only support constraints that are natively supported -by the solver and let bridges transform the constraint to the appropriate form. -For this reason it is expected that tests may not pass if `OPTIMIZER` is used -instead of `BRIDGED`. - -To test that a specific problem can be solved without bridges, a specific test -can be added with `OPTIMIZER` instead of `BRIDGED`. For example: +From here on, proceed with making the following changes in the +`~/.julia/dev/MathOptInterface` folder (or equivalent `dev` path on your +machine). + +**Step 3** + +Since the double-optimize error involves solving an optimization problem, +add a new test to [src/Test/UnitTests/solve.jl](https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Test/UnitTests/solve.jl). + +The test should be something like ```julia -function test_interval_constraints() - MOI.Test.linear10test(OPTIMIZER, CONFIG) +""" + test_solve_twice(model::MOI.ModelLike, config::Config) + +Test that calling `MOI.optimize!` twice does not error. + +This problem was first detected in ECOS.jl PR#72: +https://github.com/jump-dev/ECOS.jl/pull/72 +""" +function test_solve_twice( + model::MOI.ModelLike, + config::Config{T}, +) where {T} + if !config.solve + # Use `config` to modify the behavior of the tests. Since this test is + # concerned with `optimize!`, we should skip the test if + # `config.solve == false`. + return + end + # This is important! Make sure to empty the existing model! + MOI.empty!(model) + # Create a simple model. Try to make this as simple as possible so that the + # majority of solvers can run the test. + x = MOI.add_variable(model) + MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(one(T))) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + MOI.set( + model, + MOI.ObjectiveFunction{MOI.SingleVariable}(), + MOI.SingleVariable(x), + ) + # The main component of the test: does calling `optimize!` twice error? + MOI.optimize!(model) + MOI.optimize!(model) + # Check we have a solution. + MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL + MOI.get(model, MOI.VariablePrimal(), x) == one(T) + return end ``` -checks that `OPTIMIZER` implements support for -[`ScalarAffineFunction`](@ref)-in-[`Interval`](@ref). - -## How to add a test -To give an example, ECOS errored calling [`optimize!`](@ref) twice in a row. -(See [ECOS.jl PR #72](https://github.com/jump-dev/ECOS.jl/pull/72).) +!!! info + Make sure the function is agnoistic to the number type `T`! Don't assume it + is a `Float64` capable solver! -We could add a test to ECOS.jl, but that would only stop us from re-introducing -the bug to ECOS.jl in the future. +We also need to write a test for the test. Place this function immediately below +the test you just wrote in the same file: +```julia +function setup_test( + ::typeof(test_solve_twice), + model::MOI.Utilities.MockOptimizer, + config::Config, +) + MOI.Utilities.set_mock_optimize!( + model, + (mock::MOI.Utilities.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + ) + return +end +``` -Instead, if we add a test to `MOI.Test`, then all solvers will also check that -they handle a double optimize call! +**Step 6** -For this test, we care about correctness, rather than performance. therefore, we -don't expect solvers to efficiently decide that they have already solved the -problem, only that calling [`optimize!`](@ref) twice doesn't throw an error or -give the wrong answer. +Commit the changes to git from `~/.julia/dev/MathOptInterface` and +submit the PR for review. -To resolve this issue, follow these steps (tested on Julia v1.5): - -1. Install the `MathOptInterface` julia package in `dev` mode - ([ref](https://julialang.github.io/Pkg.jl/v1/managing-packages/#developing-1)): - ```julia - julia> ] - (@v1.5) pkg> dev ECOS - (@v1.5) pkg> dev MathOptInterface - ``` -2. From here on, proceed with making the following changes in the - `~/.julia/dev/MathOptInterface` folder (or equivalent `dev` path on your - machine). -3. Since the double-optimize error involves solving an optimization problem, - add a new test to [src/Test/UnitTests/solve.jl](https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Test/UnitTests/solve.jl). - The test should be something like - ```julia - function solve_twice(model::MOI.ModelLike, config::Config) - MOI.empty!(model) - x = MOI.add_variable(model) - c = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(1.0)) - MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) - MOI.set(model, MOI.ObjectiveFunction{MOI.SingleVariable}(), MOI.SingleVariable(x)) - if config.solve - MOI.optimize!(model) - MOI.optimize!(model) - MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL - MOI.get(model, MOI.VariablePrimal(), x) == 1.0 - end - end - unittests["solve_twice"] = solve_twice - ``` -2. Add a test for the test you just wrote. (We test the tests!) - a. Add the name of the test (`"solve_twice"`) to the end of the array in - `MOI.Test.unittest(...)` ([link](https://github.com/jump-dev/MathOptInterface.jl/blob/7543afe4b5151cf36bbd18181c1bb5c83266ae2f/test/Test/unit.jl#L51-L52)). - b. Add a test for the test towards the end of the "Unit Tests" test set - ([link](https://github.com/jump-dev/MathOptInterface.jl/blob/7543afe4b5151cf36bbd18181c1bb5c83266ae2f/test/Test/unit.jl#L394)). - The test should look something like - ```julia - @testset "solve_twice" begin - MOI.Utilities.set_mock_optimize!(mock, - (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ) - ) - MOI.Test.solve_twice(mock, config) - end - ``` - In the above `mock` is a `MOI.Utilities.MockOptimizer` that is defined - tesearlier in the file. In this test, `MOI.Utilities.set_mock_optimize!` loads - `mock` with two results. Each says that the - [`TerminationStatus`](@ref) is `MOI.OPTIMAL`, that the - [`PrimalStatus`](@ref) is `MOI.FEASIBLE_POINT`, and that there is one - variable with a `MOI.VariableValue` or `1.0` -3. Run the tests: - ```julia - (@v1.5) pkg> test ECOS - ``` -4. Finally, commit the changes to git from `~/.julia/dev/MathOptInterface` and - submit the PR for review. +!!! tip + If you need help writing a test, [open an issue on GitHub](https://github.com/jump-dev/MathOptInterface.jl/issues/new), + or ask the [Developer Chatroom](https://gitter.im/JuliaOpt/JuMP.jl) diff --git a/src/Test/UnitTests/attributes.jl b/src/Test/UnitTests/attributes.jl index d244cadcc8..65d7634000 100644 --- a/src/Test/UnitTests/attributes.jl +++ b/src/Test/UnitTests/attributes.jl @@ -31,16 +31,11 @@ function test_Silent(model::MOI.ModelLike, config::Config) return end -function setup_test( - ::typeof(test_Silent), - model::MOIU.MockOptimizer, - ::Config, -) +function setup_test(::typeof(test_Silent), model::MOIU.MockOptimizer, ::Config) MOI.set(model, MOI.Silent(), true) return end - """ test_TimeLimitSec(model::MOI.ModelLike, config::Config) @@ -141,11 +136,7 @@ function setup_test( MOI.RawStatusString(), "Mock solution set by `mock_optimize!`.", ) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ) + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])) end, ) return @@ -189,11 +180,7 @@ function setup_test( model, (mock::MOIU.MockOptimizer) -> begin MOI.set(mock, MOI.SolveTimeSec(), 0.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ) + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])) end, ) return diff --git a/src/Test/UnitTests/modifications.jl b/src/Test/UnitTests/modifications.jl index 61cb7295ef..c90816f0b3 100644 --- a/src/Test/UnitTests/modifications.jl +++ b/src/Test/UnitTests/modifications.jl @@ -33,13 +33,19 @@ function test_modification_set_function_single_variable( end """ - test_solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Config) + test_modification_set_singlevariable_lessthan( + model::MOI.ModelLike, + config::Config, + ) Test set modification SingleVariable-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function test_solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Config) +function test_modification_set_singlevariable_lessthan( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -74,7 +80,7 @@ function test_solve_set_singlevariable_lessthan(model::MOI.ModelLike, config::Co end function setup_test( - ::typeof(test_solve_set_singlevariable_lessthan), + ::typeof(test_modification_set_singlevariable_lessthan), model::MOIU.MockOptimizer, ::Config, ) @@ -97,13 +103,16 @@ function setup_test( end """ - test_solve_transform_singlevariable_lessthan(model::MOI.ModelLike, config::Config) + test_modification_transform_singlevariable_lessthan( + model::MOI.ModelLike, + config::Config, + ) Test set transformation of a SingleVariable-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function test_solve_transform_singlevariable_lessthan( +function test_modification_transform_singlevariable_lessthan( model::MOI.ModelLike, config::Config, ) @@ -143,7 +152,7 @@ function test_solve_transform_singlevariable_lessthan( end function setup_test( - ::typeof(test_solve_transform_singlevariable_lessthan), + ::typeof(test_modification_transform_singlevariable_lessthan), model::MOIU.MockOptimizer, ::Config, ) @@ -166,13 +175,19 @@ function setup_test( end """ - test_solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) + test_modification_set_scalaraffine_lessthan( + model::MOI.ModelLike, + config::Config, + ) Test modifying set of ScalarAffineFunction-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function test_solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) +function test_modification_set_scalaraffine_lessthan( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -206,7 +221,7 @@ function test_solve_set_scalaraffine_lessthan(model::MOI.ModelLike, config::Conf end function setup_test( - ::typeof(test_solve_set_scalaraffine_lessthan), + ::typeof(test_modification_set_scalaraffine_lessthan), model::MOIU.MockOptimizer, ::Config, ) @@ -217,27 +232,35 @@ function setup_test( MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1.0], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [2.0]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1.0], ), ) return end """ - test_solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) + test_modification_coef_scalaraffine_lessthan( + model::MOI.ModelLike, + config::Config, + ) Test modifying a variable coefficient in a ScalarAffineFunction-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function test_solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) +function test_modification_coef_scalaraffine_lessthan( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -270,7 +293,7 @@ function test_solve_coef_scalaraffine_lessthan(model::MOI.ModelLike, config::Con end function setup_test( - ::typeof(test_solve_coef_scalaraffine_lessthan), + ::typeof(test_modification_coef_scalaraffine_lessthan), model::MOIU.MockOptimizer, ::Config, ) @@ -281,27 +304,35 @@ function setup_test( MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1.0], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.5]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-0.5], ), ) return end """ - test_solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) + test_modification_func_scalaraffine_lessthan( + model::MOI.ModelLike, + config::Config, + ) Test setting the function in a ScalarAffineFunction-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function test_solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Config) +function test_modification_func_scalaraffine_lessthan( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -341,7 +372,7 @@ function test_solve_func_scalaraffine_lessthan(model::MOI.ModelLike, config::Con end function setup_test( - ::typeof(test_solve_func_scalaraffine_lessthan), + ::typeof(test_modification_func_scalaraffine_lessthan), model::MOIU.MockOptimizer, ::Config, ) @@ -352,27 +383,35 @@ function setup_test( MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1.0], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.5]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-0.5], ), ) return end """ - test_solve_func_vectoraffine_nonneg(model::MOI.ModelLike, config::Config) + test_modification_func_vectoraffine_nonneg( + model::MOI.ModelLike, + config::Config, + ) Test setting the function in a VectorAffineFunction-in-Nonnegatives constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function test_solve_func_vectoraffine_nonneg(model::MOI.ModelLike, config::Config) +function test_modification_func_vectoraffine_nonneg( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -423,7 +462,7 @@ function test_solve_func_vectoraffine_nonneg(model::MOI.ModelLike, config::Confi end function setup_test( - ::typeof(test_solve_func_vectoraffine_nonneg), + ::typeof(test_modification_func_vectoraffine_nonneg), model::MOIU.MockOptimizer, ::Config, ) @@ -444,13 +483,19 @@ function setup_test( end """ - test_solve_const_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) + test_modification_const_vectoraffine_nonpos( + model::MOI.ModelLike, + config::Config, + ) Test modifying the constant term in a VectorAffineFunction-in-Nonpositives constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function test_solve_const_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) +function test_modification_const_vectoraffine_nonpos( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -491,7 +536,7 @@ function test_solve_const_vectoraffine_nonpos(model::MOI.ModelLike, config::Conf end function setup_test( - ::typeof(test_solve_const_vectoraffine_nonpos), + ::typeof(test_modification_const_vectoraffine_nonpos), model::MOIU.MockOptimizer, ::Config, ) @@ -512,13 +557,16 @@ function setup_test( end """ - test_solve_multirow_vectoraffine_nonpos(model::MOI.ModelLike, config::Config) + test_modification_multirow_vectoraffine_nonpos( + model::MOI.ModelLike, + config::Config, + ) Test modifying the variable coefficients in a VectorAffineFunction-in-Nonpositives constraint. If `config.solve=true` confirm that it solves correctly. """ -function test_solve_multirow_vectoraffine_nonpos( +function test_modification_multirow_vectoraffine_nonpos( model::MOI.ModelLike, config::Config, ) @@ -561,17 +609,14 @@ function test_solve_multirow_vectoraffine_nonpos( end function setup_test( - ::typeof(test_solve_multirow_vectoraffine_nonpos), + ::typeof(test_modification_multirow_vectoraffine_nonpos), model::MOIU.MockOptimizer, ::Config, ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - ), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.5])), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.OPTIMAL, @@ -582,12 +627,18 @@ function setup_test( end """ - test_solve_const_scalar_objective(model::MOI.ModelLike, config::Config) + test_modification_const_scalar_objective( + model::MOI.ModelLike, + config::Config, + ) Test the constant of a scalaraffine objective. If `config.solve=true` confirm that it solves correctly. """ -function test_solve_const_scalar_objective(model::MOI.ModelLike, config::Config) +function test_modification_const_scalar_objective( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -619,33 +670,33 @@ function test_solve_const_scalar_objective(model::MOI.ModelLike, config::Config) end function setup_test( - ::typeof(test_solve_const_scalar_objective), + ::typeof(test_modification_const_scalar_objective), model::MOIU.MockOptimizer, ::Config, ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), ) return end """ - test_solve_coef_scalar_objective(model::MOI.ModelLike, config::Config) + test_modification_coef_scalar_objective( + model::MOI.ModelLike, + config::Config + ) Test modifying a variable coefficient in a scalaraffine objective. If `config.solve=true` confirm that it solves correctly. """ -function test_solve_coef_scalar_objective(model::MOI.ModelLike, config::Config) +function test_modification_coef_scalar_objective( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -677,27 +728,29 @@ function test_solve_coef_scalar_objective(model::MOI.ModelLike, config::Config) end function setup_test( - ::typeof(test_solve_coef_scalar_objective), + ::typeof(test_modification_coef_scalar_objective), model::MOIU.MockOptimizer, ::Config, ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), ) return end -function test_delete_variable_with_single_variable_obj( +""" + test_modification_delete_variable_with_single_variable_obj( + model::MOI.ModelLike, + config::Config, + ) + +Test deleting a variable that is the objective function. +""" +function test_modification_delete_variable_with_single_variable_obj( model::MOI.ModelLike, config::Config, ) @@ -730,7 +783,7 @@ function test_delete_variable_with_single_variable_obj( end function setup_test( - ::typeof(test_delete_variable_with_single_variable_obj), + ::typeof(test_modification_delete_variable_with_single_variable_obj), model::MOIU.MockOptimizer, ::Config, ) @@ -748,13 +801,19 @@ function setup_test( end """ - test_delete_variables_in_a_batch(model::MOI.ModelLike, config::Config) + test_modification_delete_variables_in_a_batch( + model::MOI.ModelLike, + config::Config, + ) Test deleting many variables in a batch (i.e. using the delete method which takes a vector of variable references). If `config.solve=true` confirm that it solves correctly. """ -function test_delete_variables_in_a_batch(model::MOI.ModelLike, config::Config) +function test_modification_delete_variables_in_a_batch( + model::MOI.ModelLike, + config::Config, +) atol = config.atol rtol = config.rtol MOI.empty!(model) @@ -789,7 +848,7 @@ function test_delete_variables_in_a_batch(model::MOI.ModelLike, config::Config) end function setup_test( - ::typeof(test_delete_variables_in_a_batch), + ::typeof(test_modification_delete_variables_in_a_batch), model::MOIU.MockOptimizer, ::Config, ) diff --git a/test/Utilities/universalfallback.jl b/test/Utilities/universalfallback.jl index 87d463b3e6..62638e63c4 100644 --- a/test/Utilities/universalfallback.jl +++ b/test/Utilities/universalfallback.jl @@ -293,16 +293,9 @@ end @testset "Unit" begin MOIT.unittest(uf, config) end -@testset "Modification" begin - MOIT.modificationtest(uf, config) -end -@testset "Continuous Linear" begin - MOI.Test.runtests( - uf, - config, - include = ["test_linear_"], - exclude = ["linear_mixed_complementarity"], - ) + +@testset "MOI.Test.runtests" begin + MOI.Test.runtests(uf, config, exclude = ["_complementarity"]) end @testset "Duplicate names" begin From 754410b41911efbd52655391129951496bb5a1a4 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 09:25:35 +1200 Subject: [PATCH 15/23] Rename tests in attributes.jl --- src/Test/Test.jl | 2 +- src/Test/UnitTests/attributes.jl | 127 +++++++++++++++++-------------- 2 files changed, 70 insertions(+), 59 deletions(-) diff --git a/src/Test/Test.jl b/src/Test/Test.jl index aa7906ca1e..fcf68a94b6 100644 --- a/src/Test/Test.jl +++ b/src/Test/Test.jl @@ -28,7 +28,7 @@ Overload this method to modify `model` before running the test function `f` on `model` with `config`. This function should either return `nothing`, or return a function which, when -called with zero arguments, undoes the setup to return the model to it's +called with zero arguments, undoes the setup to return the model to its previous state. This is most useful when writing new tests of the tests for MOI, but can also be diff --git a/src/Test/UnitTests/attributes.jl b/src/Test/UnitTests/attributes.jl index 65d7634000..a561cd4604 100644 --- a/src/Test/UnitTests/attributes.jl +++ b/src/Test/UnitTests/attributes.jl @@ -1,63 +1,70 @@ """ - test_solver_name(model::MOI.ModelLike, config::Config) + test_attribute_SolverName(model::MOI.ModelLike, config::Config) Test that the [`MOI.SolverName`](@ref) attribute is implemented for `model`. """ -function test_SolverName(model::MOI.ModelLike, config::Config) - if config.solve - @test MOI.get(model, MOI.SolverName()) isa AbstractString +function test_attribute_SolverName(model::MOI.ModelLike, config::Config) + if !config.solve + return end + @test MOI.get(model, MOI.SolverName()) isa AbstractString return end """ - test_Silent(model::MOI.ModelLike, config::Config) + test_attribute_Silent(model::MOI.ModelLike, config::Config) Test that the [`MOI.Silent`](@ref) attribute is implemented for `model`. """ -function test_Silent(model::MOI.ModelLike, config::Config) - if config.solve - @test MOI.supports(model, MOI.Silent()) - # Get the current value to restore it at the end of the test - value = MOI.get(model, MOI.Silent()) - MOI.set(model, MOI.Silent(), !value) - @test !value == MOI.get(model, MOI.Silent()) - # Check that `set` does not just take `!` of the current value - MOI.set(model, MOI.Silent(), !value) - @test !value == MOI.get(model, MOI.Silent()) - MOI.set(model, MOI.Silent(), value) - @test value == MOI.get(model, MOI.Silent()) +function test_attribute_Silent(model::MOI.ModelLike, config::Config) + if !config.solve + return end + @test MOI.supports(model, MOI.Silent()) + # Get the current value to restore it at the end of the test + value = MOI.get(model, MOI.Silent()) + MOI.set(model, MOI.Silent(), !value) + @test !value == MOI.get(model, MOI.Silent()) + # Check that `set` does not just take `!` of the current value + MOI.set(model, MOI.Silent(), !value) + @test !value == MOI.get(model, MOI.Silent()) + MOI.set(model, MOI.Silent(), value) + @test value == MOI.get(model, MOI.Silent()) return end -function setup_test(::typeof(test_Silent), model::MOIU.MockOptimizer, ::Config) +function setup_test( + ::typeof(test_attribute_Silent), + model::MOIU.MockOptimizer, + ::Config, +) MOI.set(model, MOI.Silent(), true) return end """ - test_TimeLimitSec(model::MOI.ModelLike, config::Config) + test_attribute_TimeLimitSec(model::MOI.ModelLike, config::Config) Test that the [`MOI.TimeLimitSec`](@ref) attribute is implemented for `model`. """ -function test_TimeLimitSec(model::MOI.ModelLike, config::Config) - if config.solve - @test MOI.supports(model, MOI.TimeLimitSec()) - # Get the current value to restore it at the end of the test - value = MOI.get(model, MOI.TimeLimitSec()) - MOI.set(model, MOI.TimeLimitSec(), 0.0) - @test MOI.get(model, MOI.TimeLimitSec()) == 0.0 - MOI.set(model, MOI.TimeLimitSec(), 1.0) - @test MOI.get(model, MOI.TimeLimitSec()) == 1.0 - MOI.set(model, MOI.TimeLimitSec(), value) - @test value == MOI.get(model, MOI.TimeLimitSec()) # Equality should hold +function test_attribute_TimeLimitSec(model::MOI.ModelLike, config::Config) + if !config.solve + return end + @test MOI.supports(model, MOI.TimeLimitSec()) + # Get the current value to restore it at the end of the test + value = MOI.get(model, MOI.TimeLimitSec()) + MOI.set(model, MOI.TimeLimitSec(), 0.0) + @test MOI.get(model, MOI.TimeLimitSec()) == 0.0 + MOI.set(model, MOI.TimeLimitSec(), 1.0) + @test MOI.get(model, MOI.TimeLimitSec()) == 1.0 + MOI.set(model, MOI.TimeLimitSec(), value) + @test value == MOI.get(model, MOI.TimeLimitSec()) # Equality should hold return end function setup_test( - ::typeof(test_TimeLimitSec), + ::typeof(test_attribute_TimeLimitSec), model::MOIU.MockOptimizer, ::Config, ) @@ -66,27 +73,29 @@ function setup_test( end """ - test_NumberThreads(model::MOI.ModelLike, config::Config) + test_attribute_NumberThreads(model::MOI.ModelLike, config::Config) -Test that the [`MOI.NumberOfThreads`](@ref) attribute is implemented for `model`. +Test that the [`MOI.NumberOfThreads`](@ref) attribute is implemented for +`model`. """ -function test_NumberThreads(model::MOI.ModelLike, config::Config) - if config.solve - @test MOI.supports(model, MOI.NumberOfThreads()) - # Get the current value to restore it at the end of the test - value = MOI.get(model, MOI.NumberOfThreads()) - MOI.set(model, MOI.NumberOfThreads(), 1) - @test MOI.get(model, MOI.NumberOfThreads()) == 1 - MOI.set(model, MOI.NumberOfThreads(), 3) - @test MOI.get(model, MOI.NumberOfThreads()) == 3 - MOI.set(model, MOI.NumberOfThreads(), value) - @test value == MOI.get(model, MOI.NumberOfThreads()) +function test_attribute_NumberThreads(model::MOI.ModelLike, config::Config) + if !config.solve + return end + @test MOI.supports(model, MOI.NumberOfThreads()) + # Get the current value to restore it at the end of the test + value = MOI.get(model, MOI.NumberOfThreads()) + MOI.set(model, MOI.NumberOfThreads(), 1) + @test MOI.get(model, MOI.NumberOfThreads()) == 1 + MOI.set(model, MOI.NumberOfThreads(), 3) + @test MOI.get(model, MOI.NumberOfThreads()) == 3 + MOI.set(model, MOI.NumberOfThreads(), value) + @test value == MOI.get(model, MOI.NumberOfThreads()) return end function setup_test( - ::typeof(test_NumberThreads), + ::typeof(test_attribute_NumberThreads), model::MOIU.MockOptimizer, ::Config, ) @@ -95,12 +104,15 @@ function setup_test( end """ - test_RawStatusString(model::MOI.ModelLike, config::Config) + test_attribute_RawStatusString(model::MOI.ModelLike, config::Config) Test that the [`MOI.RawStatusString`](@ref) attribute is implemented for `model`. """ -function test_RawStatusString(model::MOI.ModelLike, config::Config) +function test_attribute_RawStatusString(model::MOI.ModelLike, config::Config) + if !config.solve + return + end MOI.empty!(model) @test MOI.is_empty(model) x = MOI.add_variable(model) @@ -117,14 +129,12 @@ function test_RawStatusString(model::MOI.ModelLike, config::Config) objective_value = 0.0, variable_primal = [(x, 0.0)], ) - if config.solve - @test MOI.get(model, MOI.RawStatusString()) isa AbstractString - end + @test MOI.get(model, MOI.RawStatusString()) isa AbstractString return end function setup_test( - ::typeof(test_RawStatusString), + ::typeof(test_attribute_RawStatusString), model::MOIU.MockOptimizer, ::Config, ) @@ -143,11 +153,14 @@ function setup_test( end """ - test_SolveTimeSec(model::MOI.ModelLike, config::Config) + test_attribute_SolveTimeSec(model::MOI.ModelLike, config::Config) Test that the [`MOI.SolveTimeSec`](@ref) attribute is implemented for `model`. """ -function test_SolveTimeSec(model::MOI.ModelLike, config::Config) +function test_attribute_SolveTimeSec(model::MOI.ModelLike, config::Config) + if !config.solve + return + end MOI.empty!(model) @test MOI.is_empty(model) x = MOI.add_variable(model) @@ -164,15 +177,13 @@ function test_SolveTimeSec(model::MOI.ModelLike, config::Config) objective_value = 0.0, variable_primal = [(x, 0.0)], ) - if config.solve - time = MOI.get(model, MOI.SolveTimeSec()) - @test time ≥ 0.0 - end + time = MOI.get(model, MOI.SolveTimeSec()) + @test time >= 0.0 return end function setup_test( - ::typeof(test_SolveTimeSec), + ::typeof(test_attribute_SolveTimeSec), model::MOIU.MockOptimizer, ::Config, ) From 528f1151395b87d1a32937a2d065ebb03f70cc82 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 09:46:32 +1200 Subject: [PATCH 16/23] Fix bridge tests --- test/Bridges/Constraint/flip_sign.jl | 8 ++++---- test/Bridges/Constraint/ltgt_to_interval.jl | 4 ++-- test/Bridges/Constraint/scalarize.jl | 2 +- test/Bridges/Variable/free.jl | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/Bridges/Constraint/flip_sign.jl b/test/Bridges/Constraint/flip_sign.jl index 968e0243fb..468845e588 100644 --- a/test/Bridges/Constraint/flip_sign.jl +++ b/test/Bridges/Constraint/flip_sign.jl @@ -99,7 +99,7 @@ end [1.0], ), ) - MOIT.test_solve_set_scalaraffine_lessthan(bridged_mock, config) + MOIT.test_modification_set_scalaraffine_lessthan(bridged_mock, config) MOIU.set_mock_optimize!( mock, @@ -120,7 +120,7 @@ end [0.5], ), ) - MOIT.test_solve_coef_scalaraffine_lessthan(bridged_mock, config) + MOIT.test_modification_coef_scalaraffine_lessthan(bridged_mock, config) ci = first( MOI.get( @@ -268,7 +268,7 @@ end (MOI.FEASIBLE_POINT, [1.0, 0.75]), ), ) - MOIT.test_solve_const_vectoraffine_nonpos(bridged_mock, config) + MOIT.test_modification_const_vectoraffine_nonpos(bridged_mock, config) MOIU.set_mock_optimize!( mock, @@ -280,7 +280,7 @@ end (MOI.FEASIBLE_POINT, [0.25]), ), ) - MOIT.test_solve_multirow_vectoraffine_nonpos(bridged_mock, config) + MOIT.test_modification_multirow_vectoraffine_nonpos(bridged_mock, config) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/ltgt_to_interval.jl b/test/Bridges/Constraint/ltgt_to_interval.jl index f38167ea26..991e4e9de4 100644 --- a/test/Bridges/Constraint/ltgt_to_interval.jl +++ b/test/Bridges/Constraint/ltgt_to_interval.jl @@ -100,7 +100,7 @@ end [-1.0], ), ) - MOIT.test_solve_set_scalaraffine_lessthan(bridged_mock, config) + MOIT.test_modification_set_scalaraffine_lessthan(bridged_mock, config) MOIU.set_mock_optimize!( mock, @@ -121,7 +121,7 @@ end [-0.5], ), ) - MOIT.test_solve_coef_scalaraffine_lessthan(bridged_mock, config) + MOIT.test_modification_coef_scalaraffine_lessthan(bridged_mock, config) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/scalarize.jl b/test/Bridges/Constraint/scalarize.jl index 17d940d3f7..bc03085ebc 100644 --- a/test/Bridges/Constraint/scalarize.jl +++ b/test/Bridges/Constraint/scalarize.jl @@ -168,7 +168,7 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.0, 0.0]), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.75]), ) - MOIT.test_solve_func_vectoraffine_nonneg(bridged_mock, config) + MOIT.test_modification_func_vectoraffine_nonneg(bridged_mock, config) # VectorOfVariables-in-Nonnegatives # VectorOfVariables-in-Nonpositives diff --git a/test/Bridges/Variable/free.jl b/test/Bridges/Variable/free.jl index a1b65da9dd..cba8105699 100644 --- a/test/Bridges/Variable/free.jl +++ b/test/Bridges/Variable/free.jl @@ -27,7 +27,7 @@ bridged_mock = MOIB.Variable.Free{Float64}(mock) (MOI.FEASIBLE_POINT, [0.25, 0.0]), ), ) - MOIT.test_solve_multirow_vectoraffine_nonpos(bridged_mock, config) + MOIT.test_modification_multirow_vectoraffine_nonpos(bridged_mock, config) MOI.set( bridged_mock, From b19ba03489f397aa28d932a1953b1dbcc17f98d9 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 10:47:18 +1200 Subject: [PATCH 17/23] More migration of UnitTests --- src/Test/UnitTests/objectives.jl | 396 +++++++----- src/Test/UnitTests/solve.jl | 849 +++++++++++++++++++------- test/Bridges/Objective/functionize.jl | 8 +- test/Bridges/Objective/slack.jl | 4 +- test/Test/unit.jl | 381 +----------- 5 files changed, 918 insertions(+), 720 deletions(-) diff --git a/src/Test/UnitTests/objectives.jl b/src/Test/UnitTests/objectives.jl index e2b57ac35e..f35c929056 100644 --- a/src/Test/UnitTests/objectives.jl +++ b/src/Test/UnitTests/objectives.jl @@ -1,67 +1,50 @@ -#= - Functions in this file test functionality relating to objectives in MOI. - -### Requires - - optimize! - -### Functionality currently tested - - get/set ObjectiveSense - - a constant in a affine objective - - a blank objective - -### Functionality not yet tested - - Quadratic Objectives - - Modifications -=# - """ - max_sense(model::MOI.ModelLike, config::Config) + test_ObjectiveSense_MAX_SENSE(model::MOI.ModelLike, config::Config) Test setting objective sense to MAX_SENSE. """ -function max_sense(model::MOI.ModelLike, config::Config) +function test_ObjectiveSense_MAX_SENSE(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) @test MOI.supports(model, MOI.ObjectiveSense()) MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE + return end -unittests["max_sense"] = max_sense """ - min_sense(model::MOI.ModelLike, config::Config) + test_ObjectiveSense_MIN_SENSE(model::MOI.ModelLike, config::Config) Test setting objective sense to MIN_SENSE. """ -function min_sense(model::MOI.ModelLike, config::Config) +function test_ObjectiveSense_MIN_SENSE(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) @test MOI.supports(model, MOI.ObjectiveSense()) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE + return end -unittests["min_sense"] = min_sense """ - feasibility_sense(model::MOI.ModelLike, config::Config) + test_ObjectiveSense_FEASIBILITY_SENSE(model::MOI.ModelLike, config::Config) Test setting objective sense to FEASIBILITY_SENSE. """ -function feasibility_sense(model::MOI.ModelLike, config::Config) +function test_ObjectiveSense_FEASIBILITY_SENSE(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) @test MOI.supports(model, MOI.ObjectiveSense()) MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) @test MOI.get(model, MOI.ObjectiveSense()) == MOI.FEASIBILITY_SENSE end -unittests["feasibility_sense"] = feasibility_sense """ - get_objective_function(model::MOI.ModelLike, config::Config) + test_get_ObjectiveFunction(model::MOI.ModelLike, config::Config) Test get objective function. """ -function get_objective_function(model::MOI.ModelLike, config::Config) +function test_get_ObjectiveFunction(model::MOI.ModelLike, ::Config) MOI.empty!(model) @test MOI.is_empty(model) obj_attr = MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}() @@ -76,9 +59,10 @@ function get_objective_function(model::MOI.ModelLike, config::Config) x = MOI.get(model, MOI.VariableIndex, "x") expected_obj_fun = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(2.0, x)], 1.0) - @test_throws InexactError begin - MOI.get(model, MOI.ObjectiveFunction{MOI.SingleVariable}()) - end + @test_throws( + InexactError, + MOI.get(model, MOI.ObjectiveFunction{MOI.SingleVariable}()), + ) obj_fun = MOI.get(model, obj_attr) @test obj_fun ≈ expected_obj_fun quad_obj_attr = @@ -86,18 +70,17 @@ function get_objective_function(model::MOI.ModelLike, config::Config) quad_obj_fun = MOI.get(model, quad_obj_attr) @test convert(MOI.ScalarAffineFunction{Float64}, quad_obj_fun) ≈ expected_obj_fun + return end -unittests["get_objective_function"] = get_objective_function """ - solve_constant_obj(model::MOI.ModelLike, config::Config) + test_ObjectiveFunction_constant(model::MOI.ModelLike, config::Config) Test constant in linear objective, if `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_constant_obj(model::MOI.ModelLike, config::Config) - atol, rtol = config.atol, config.rtol +function test_ObjectiveFunction_constant(model::MOI.ModelLike, config::Config) MOI.empty!(model) @test MOI.is_empty(model) MOIU.loadfromstring!( @@ -115,7 +98,7 @@ function solve_constant_obj(model::MOI.ModelLike, config::Config) # We test this after the creation of every `SingleVariable` constraint # to ensure a good coverage of corner cases. @test c.value == x.value - return test_model_solution( + test_model_solution( model, config; objective_value = 3.0, @@ -123,17 +106,37 @@ function solve_constant_obj(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 1.0)], constraint_dual = [(c, 2.0)], ) + return +end + +function setup_test( + ::typeof(test_ObjectiveFunction_constant), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1]), + MOI.FEASIBLE_POINT, + ), + ) + return end -unittests["solve_constant_obj"] = solve_constant_obj """ - solve_blank_obj(model::MOI.ModelLike, config::Config) + test_ObjectiveFunction_blank(model::MOI.ModelLike, config::Config) Test blank linear objective, if `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_blank_obj(model::MOI.ModelLike, config::Config) +function test_ObjectiveFunction_blank(model::MOI.ModelLike, config::Config) + if !config.solve + return + end atol, rtol = config.atol, config.rtol MOI.empty!(model) @test MOI.is_empty(model) @@ -156,24 +159,41 @@ function solve_blank_obj(model::MOI.ModelLike, config::Config) objective_value = 0.0, constraint_dual = [(c, 0.0)], ) - if config.solve - # The objective is blank so any primal value ≥ 1 is correct - @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.VariablePrimal(), x) + atol + rtol ≥ 1.0 - @test MOI.get(model, MOI.ConstraintPrimal(), c) + atol + rtol ≥ 1.0 - end + # The objective is blank so any primal value ≥ 1 is correct + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.VariablePrimal(), x) + atol + rtol >= 1.0 + @test MOI.get(model, MOI.ConstraintPrimal(), c) + atol + rtol >= 1.0 + return +end + +function setup_test( + ::typeof(test_ObjectiveFunction_blank), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.2]), + MOI.FEASIBLE_POINT, + ), + ) + return end -unittests["solve_blank_obj"] = solve_blank_obj """ - solve_singlevariable_obj(model::MOI.ModelLike, config::Config) + test_ObjectiveFunction_SingleVariable(model::MOI.ModelLike, config::Config) Test SingleVariable objective, if `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_singlevariable_obj(model::MOI.ModelLike, config::Config) - atol, rtol = config.atol, config.rtol +function test_ObjectiveFunction_SingleVariable( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) @test MOI.is_empty(model) MOIU.loadfromstring!( @@ -189,7 +209,7 @@ function solve_singlevariable_obj(model::MOI.ModelLike, config::Config) x.value, ) @test c.value == x.value - return test_model_solution( + test_model_solution( model, config; objective_value = 1.0, @@ -197,20 +217,42 @@ function solve_singlevariable_obj(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 1.0)], constraint_dual = [(c, 1.0)], ) + return +end + +function setup_test( + ::typeof(test_ObjectiveFunction_SingleVariable), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1]), + MOI.FEASIBLE_POINT, + ), + ) + return end -unittests["solve_singlevariable_obj"] = solve_singlevariable_obj """ - solve_qp_edge_cases(model::MOI.ModelLike, config::Config) + test_qp_ObjectiveFunction_edge_cases(model::MOI.ModelLike, config::Config) Test various edge cases relating to quadratic programs (i.e., with a quadratic objective function). If `config.solve=true` confirm that it solves correctly. """ -function solve_qp_edge_cases(model::MOI.ModelLike, config::Config) +function test_qp_ObjectiveFunction_edge_cases( + model::MOI.ModelLike, + config::Config, +) obj_attr = MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}() - MOI.supports(model, obj_attr) || return + if !MOI.supports(model, obj_attr) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) @@ -226,99 +268,131 @@ function solve_qp_edge_cases(model::MOI.ModelLike, config::Config) MOI.GreaterThan(2.0), ) @test vc2.value == x[2].value - @testset "Basic model" begin - # min x^2 + y^2 | x>=1, y>=2 - MOI.set( - model, - obj_attr, - MOI.ScalarQuadraticFunction( - MOI.ScalarAffineTerm{Float64}[], # affine terms - MOI.ScalarQuadraticTerm.([2.0, 2.0], x, x), # quad - 0.0, # constant - ), - ) - test_model_solution( - model, - config; - objective_value = 5.0, - variable_primal = [(x[1], 1.0), (x[2], 2.0)], - ) - end - @testset "Duplicate linear terms" begin - # min x + x + x^2 + y^2 | x>=1, y>=2 - MOI.set( - model, - obj_attr, - MOI.ScalarQuadraticFunction( - MOI.ScalarAffineTerm.([1.0, 1.0], [x[1], x[1]]), # affine terms - MOI.ScalarQuadraticTerm.([2.0, 2.0], x, x), # quad - 0.0, # constant - ), - ) - test_model_solution( - model, - config; - objective_value = 7.0, - variable_primal = [(x[1], 1.0), (x[2], 2.0)], - ) - end - @testset "Duplicate diagonal terms" begin - # min x^2 + x^2 | x>=1, y>=2 - MOI.set( - model, - obj_attr, - MOI.ScalarQuadraticFunction( - MOI.ScalarAffineTerm{Float64}[], # affine terms - MOI.ScalarQuadraticTerm.( - [2.0, 2.0], - [x[1], x[1]], - [x[1], x[1]], - ), # quad - 0.0, # constant - ), - ) - test_model_solution( - model, - config; - objective_value = 2.0, - variable_primal = [(x[1], 1.0)], - ) - end - @testset "Duplicate off-diagonal terms" begin - # min x^2 + 0.25x*y + 0.25y*x + 0.5x*y + y^2 | x>=1, y>=2 - MOI.set( - model, - obj_attr, - MOI.ScalarQuadraticFunction( - MOI.ScalarAffineTerm{Float64}[], # affine terms - MOI.ScalarQuadraticTerm.( - [2.0, 0.25, 0.25, 0.5, 2.0], - [x[1], x[1], x[2], x[1], x[2]], - [x[1], x[2], x[1], x[2], x[2]], - ), # quad - 0.0, # constant - ), - ) - test_model_solution( - model, - config; - objective_value = 7.0, - variable_primal = [(x[1], 1.0), (x[2], 2.0)], - ) - end + # Basic model + # min x^2 + y^2 | x>=1, y>=2 + MOI.set( + model, + obj_attr, + MOI.ScalarQuadraticFunction( + MOI.ScalarAffineTerm{Float64}[], # affine terms + MOI.ScalarQuadraticTerm.([2.0, 2.0], x, x), # quad + 0.0, # constant + ), + ) + test_model_solution( + model, + config; + objective_value = 5.0, + variable_primal = [(x[1], 1.0), (x[2], 2.0)], + ) + # Duplicate linear terms + # min x + x + x^2 + y^2 | x>=1, y>=2 + MOI.set( + model, + obj_attr, + MOI.ScalarQuadraticFunction( + MOI.ScalarAffineTerm.([1.0, 1.0], [x[1], x[1]]), # affine terms + MOI.ScalarQuadraticTerm.([2.0, 2.0], x, x), # quad + 0.0, # constant + ), + ) + test_model_solution( + model, + config; + objective_value = 7.0, + variable_primal = [(x[1], 1.0), (x[2], 2.0)], + ) + # Duplicate diagonal terms + # min x^2 + x^2 | x>=1, y>=2 + MOI.set( + model, + obj_attr, + MOI.ScalarQuadraticFunction( + MOI.ScalarAffineTerm{Float64}[], # affine terms + MOI.ScalarQuadraticTerm.( + [2.0, 2.0], + [x[1], x[1]], + [x[1], x[1]], + ), # quad + 0.0, # constant + ), + ) + test_model_solution( + model, + config; + objective_value = 2.0, + variable_primal = [(x[1], 1.0)], + ) + # Duplicate off-diagonal terms" begin + # min x^2 + 0.25x*y + 0.25y*x + 0.5x*y + y^2 | x>=1, y>=2 + MOI.set( + model, + obj_attr, + MOI.ScalarQuadraticFunction( + MOI.ScalarAffineTerm{Float64}[], # affine terms + MOI.ScalarQuadraticTerm.( + [2.0, 0.25, 0.25, 0.5, 2.0], + [x[1], x[1], x[2], x[1], x[2]], + [x[1], x[2], x[1], x[2], x[2]], + ), # quad + 0.0, # constant + ), + ) + test_model_solution( + model, + config; + objective_value = 7.0, + variable_primal = [(x[1], 1.0), (x[2], 2.0)], + ) + return +end + +function setup_test( + ::typeof(test_qp_ObjectiveFunction_edge_cases), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0]), + ), + ) + return end -unittests["solve_qp_edge_cases"] = solve_qp_edge_cases """ - solve_qp_zero_offdiag(model::MOI.ModelLike, config::Config) + test_qp_ObjectiveFunction_zero_ofdiag(model::MOI.ModelLike, config::Config) Test quadratic program with a zero off-diagonal term. If `config.solve=true` confirm that it solves correctly. """ -function solve_qp_zero_offdiag(model::MOI.ModelLike, config::Config) +function test_qp_ObjectiveFunction_zero_ofdiag( + model::MOI.ModelLike, + config::Config, +) obj_attr = MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}() - MOI.supports(model, obj_attr) || return + if !MOI.supports(model, obj_attr) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) @@ -347,22 +421,41 @@ function solve_qp_zero_offdiag(model::MOI.ModelLike, config::Config) 0.0, # constant ), ) - return test_model_solution( + test_model_solution( model, config; objective_value = 5.0, variable_primal = [(x[1], 1.0), (x[2], 2.0)], ) + return +end + +function setup_test( + ::typeof(test_qp_ObjectiveFunction_zero_ofdiag), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0]), + ), + ) + return end -unittests["solve_qp_zero_offdiag"] = solve_qp_zero_offdiag """ - solve_duplicate_terms_obj(model::MOI.ModelLike, config::Config) + test_ObjectiveFunction_duplicate_terms(model::MOI.ModelLike, config::Config) Test duplicate terms in linear objective, if `config.solve=true` confirm that it solves correctly. """ -function solve_duplicate_terms_obj(model::MOI.ModelLike, config::Config) +function test_ObjectiveFunction_duplicate_terms( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) @test MOI.is_empty(model) x = MOI.add_variable(model) @@ -377,7 +470,7 @@ function solve_duplicate_terms_obj(model::MOI.ModelLike, config::Config) 0.0, ), ) - return test_model_solution( + test_model_solution( model, config; objective_value = 3.0, @@ -385,5 +478,22 @@ function solve_duplicate_terms_obj(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 1.0)], constraint_dual = [(c, 3.0)], ) + return +end + +function setup_test( + ::typeof(test_ObjectiveFunction_duplicate_terms), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1]), + MOI.FEASIBLE_POINT, + ), + ) + return end -unittests["solve_duplicate_terms_obj"] = solve_duplicate_terms_obj diff --git a/src/Test/UnitTests/solve.jl b/src/Test/UnitTests/solve.jl index 2374ee0642..7938acef59 100644 --- a/src/Test/UnitTests/solve.jl +++ b/src/Test/UnitTests/solve.jl @@ -1,9 +1,12 @@ """ - solve_objbound_edge_cases(model::MOI.ModelLike, config::Config) + test_ObjectiveBound_edge_cases(model::MOI.ModelLike, config::Config) Test a variety of edge cases related to the ObjectiveBound attribute. """ -function solve_objbound_edge_cases(model::MOI.ModelLike, config::Config) +function test_ObjectiveBound_edge_cases(model::MOI.ModelLike, config::Config) + if !config.solve + return + end @testset "Min IP with constant" begin MOI.empty!(model) @test MOI.is_empty(model) @@ -23,9 +26,7 @@ function solve_objbound_edge_cases(model::MOI.ModelLike, config::Config) objective_value = 3.0, variable_primal = [(x, 2.0)], ) - if config.solve - @test MOI.get(model, MOI.ObjectiveBound()) <= 3.0 - end + @test MOI.get(model, MOI.ObjectiveBound()) <= 3.0 end @testset "Max IP with constant" begin MOI.empty!(model) @@ -46,9 +47,7 @@ function solve_objbound_edge_cases(model::MOI.ModelLike, config::Config) objective_value = 3.0, variable_primal = [(x, 1.0)], ) - if config.solve - @test MOI.get(model, MOI.ObjectiveBound()) >= 3.0 - end + @test MOI.get(model, MOI.ObjectiveBound()) >= 3.0 end @testset "Min LP with constant" begin MOI.empty!(model) @@ -68,9 +67,7 @@ function solve_objbound_edge_cases(model::MOI.ModelLike, config::Config) objective_value = 2.0, variable_primal = [(x, 1.5)], ) - if config.solve - @test MOI.get(model, MOI.ObjectiveBound()) <= 2.0 - end + @test MOI.get(model, MOI.ObjectiveBound()) <= 2.0 end @testset "Max LP with constant" begin MOI.empty!(model) @@ -90,14 +87,68 @@ function solve_objbound_edge_cases(model::MOI.ModelLike, config::Config) objective_value = 4.0, variable_primal = [(x, 1.5)], ) - if config.solve - @test MOI.get(model, MOI.ObjectiveBound()) >= 4.0 - end + @test MOI.get(model, MOI.ObjectiveBound()) >= 4.0 end end -unittests["solve_objbound_edge_cases"] = solve_objbound_edge_cases -function solve_unbounded_model(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_ObjectiveBound_edge_cases), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 3.0) + MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [2.0]), + ) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 3.0) + MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 2.0) + MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.5]), + ) + end, + (mock::MOIU.MockOptimizer) -> begin + MOI.set(mock, MOI.ObjectiveBound(), 4.0) + MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.5]), + ) + end, + ) + return +end + +""" + test_TerminationStatus_DUAL_INFEASIBLE( + model::MOI.ModelLike, + config::Config, + ) + +Test an unbounded linear program. +""" +function test_TerminationStatus_DUAL_INFEASIBLE( + model::MOI.ModelLike, + config::Config, +) + if !config.solve + return + end MOI.empty!(model) x = MOI.add_variables(model, 5) MOI.set( @@ -106,14 +157,37 @@ function solve_unbounded_model(model::MOI.ModelLike, config::Config) MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, x), 0.0), ) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) - if config.solve - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.DUAL_INFEASIBLE - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.DUAL_INFEASIBLE end -unittests["solve_unbounded_model"] = solve_unbounded_model -function solve_single_variable_dual_min(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_TerminationStatus_DUAL_INFEASIBLE), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE), + ) + return +end + +""" + test_SingleVariable_ConstraintDual_MIN_SENSE( + model::MOI.ModelLike, + config::Config, + ) + +Test `ConstraintDual` of a `SingleVariable` constraint when minimizing. +""" +function test_SingleVariable_ConstraintDual_MIN_SENSE( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.duals) + return + end MOI.empty!(model) x = MOI.add_variable(model) xl = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(1.0)) @@ -124,23 +198,56 @@ function solve_single_variable_dual_min(model::MOI.ModelLike, config::Config) MOI.ObjectiveFunction{MOI.SingleVariable}(), MOI.SingleVariable(x), ) - if config.solve && config.duals - MOI.optimize!(model) - @test isapprox( - MOI.get(model, MOI.VariablePrimal(), x), - 1.0, - atol = config.atol, - ) - sl = MOI.get(model, MOI.ConstraintDual(), xl) - su = MOI.get(model, MOI.ConstraintDual(), xu) - @test isapprox(sl + su, 1.0, atol = config.atol) - @test sl >= -config.atol - @test su <= config.atol - end + MOI.optimize!(model) + @test isapprox( + MOI.get(model, MOI.VariablePrimal(), x), + 1.0, + atol = config.atol, + ) + sl = MOI.get(model, MOI.ConstraintDual(), xl) + su = MOI.get(model, MOI.ConstraintDual(), xu) + @test isapprox(sl + su, 1.0, atol = config.atol) + @test sl >= -config.atol + @test su <= config.atol + return end -unittests["solve_single_variable_dual_min"] = solve_single_variable_dual_min -function solve_single_variable_dual_max(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_SingleVariable_ConstraintDual_MIN_SENSE), + model::MOIU.MockOptimizer, + ::Config, +) + flag = model.eval_variable_constraint_dual + model.eval_variable_constraint_dual = false + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1.0], + (MOI.SingleVariable, MOI.LessThan{Float64}) => [0.0], + ), + ) + return () -> model.eval_variable_constraint_dual = flag +end + +""" + test_SingleVariable_ConstraintDual_MAX_SENSE( + model::MOI.ModelLike, + config::Config, + ) + +Test `ConstraintDual` of a `SingleVariable` constraint when maximizing. +""" +function test_SingleVariable_ConstraintDual_MAX_SENSE( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.duals) + return + end MOI.empty!(model) x = MOI.add_variable(model) xl = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(1.0)) @@ -151,23 +258,50 @@ function solve_single_variable_dual_max(model::MOI.ModelLike, config::Config) MOI.ObjectiveFunction{MOI.SingleVariable}(), MOI.SingleVariable(x), ) - if config.solve && config.duals - MOI.optimize!(model) - @test isapprox( - MOI.get(model, MOI.VariablePrimal(), x), - 1.0, - atol = config.atol, - ) - sl = MOI.get(model, MOI.ConstraintDual(), xl) - su = MOI.get(model, MOI.ConstraintDual(), xu) - @test isapprox(sl + su, -1.0, atol = config.atol) - @test sl >= -config.atol - @test su <= config.atol - end + MOI.optimize!(model) + @test isapprox( + MOI.get(model, MOI.VariablePrimal(), x), + 1.0, + atol = config.atol, + ) + sl = MOI.get(model, MOI.ConstraintDual(), xl) + su = MOI.get(model, MOI.ConstraintDual(), xu) + @test isapprox(sl + su, -1.0, atol = config.atol) + @test sl >= -config.atol + @test su <= config.atol + return end -unittests["solve_single_variable_dual_max"] = solve_single_variable_dual_max -function solve_result_index(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_SingleVariable_ConstraintDual_MAX_SENSE), + model::MOIU.MockOptimizer, + ::Config, +) + flag = model.eval_variable_constraint_dual + model.eval_variable_constraint_dual = false + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [0.0], + (MOI.SingleVariable, MOI.LessThan{Float64}) => [-1.0], + ), + ) + return () -> model.eval_variable_constraint_dual = flag +end + +""" + test_result_index(model::MOI.ModelLike, config::Config) + +Test that various attributess implement `.result_index` correctly. +""" +function test_result_index(model::MOI.ModelLike, config::Config) + if !config.solve + return + end atol = config.atol rtol = config.rtol MOI.empty!(model) @@ -179,60 +313,90 @@ function solve_result_index(model::MOI.ModelLike, config::Config) MOI.ObjectiveFunction{MOI.SingleVariable}(), MOI.SingleVariable(x), ) - if config.solve - MOI.optimize!(model) - result_count = MOI.get(model, MOI.ResultCount()) - function result_err(attr) - return MOI.ResultIndexBoundsError{typeof(attr)}(attr, result_count) - end - result_index = result_count + 1 - @test MOI.get(model, MOI.ObjectiveValue(1)) ≈ 1.0 atol = atol rtol = - rtol - @test_throws result_err(MOI.ObjectiveValue(result_index)) MOI.get( - model, - MOI.ObjectiveValue(result_index), - ) - if config.dual_objective_value - @test MOI.get(model, MOI.DualObjectiveValue(1)) ≈ 1.0 atol = atol rtol = - rtol - @test_throws result_err(MOI.DualObjectiveValue(result_index)) MOI.get( - model, - MOI.DualObjectiveValue(result_index), - ) - end - @test MOI.get(model, MOI.PrimalStatus(1)) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.PrimalStatus(result_index)) == MOI.NO_SOLUTION - @test MOI.get(model, MOI.VariablePrimal(1), x) ≈ 1.0 atol = atol rtol = + MOI.optimize!(model) + result_count = MOI.get(model, MOI.ResultCount()) + function result_err(attr) + return MOI.ResultIndexBoundsError{typeof(attr)}(attr, result_count) + end + result_index = result_count + 1 + @test MOI.get(model, MOI.ObjectiveValue(1)) ≈ 1.0 atol = atol rtol = + rtol + @test_throws result_err(MOI.ObjectiveValue(result_index)) MOI.get( + model, + MOI.ObjectiveValue(result_index), + ) + if config.dual_objective_value + @test MOI.get(model, MOI.DualObjectiveValue(1)) ≈ 1.0 atol = atol rtol = rtol - @test_throws result_err(MOI.VariablePrimal(result_index)) MOI.get( + @test_throws result_err(MOI.DualObjectiveValue(result_index)) MOI.get( model, - MOI.VariablePrimal(result_index), - x, + MOI.DualObjectiveValue(result_index), ) - @test MOI.get(model, MOI.ConstraintPrimal(1), c) ≈ 1.0 atol = atol rtol = + end + @test MOI.get(model, MOI.PrimalStatus(1)) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.PrimalStatus(result_index)) == MOI.NO_SOLUTION + @test MOI.get(model, MOI.VariablePrimal(1), x) ≈ 1.0 atol = atol rtol = + rtol + @test_throws result_err(MOI.VariablePrimal(result_index)) MOI.get( + model, + MOI.VariablePrimal(result_index), + x, + ) + @test MOI.get(model, MOI.ConstraintPrimal(1), c) ≈ 1.0 atol = atol rtol = + rtol + @test_throws result_err(MOI.ConstraintPrimal(result_index)) MOI.get( + model, + MOI.ConstraintPrimal(result_index), + c, + ) + if config.duals + @test MOI.get(model, MOI.DualStatus(1)) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualStatus(result_index)) == + MOI.NO_SOLUTION + @test MOI.get(model, MOI.ConstraintDual(1), c) ≈ 1.0 atol = atol rtol = rtol - @test_throws result_err(MOI.ConstraintPrimal(result_index)) MOI.get( + @test_throws result_err(MOI.ConstraintDual(result_index)) MOI.get( model, - MOI.ConstraintPrimal(result_index), + MOI.ConstraintDual(result_index), c, ) - if config.duals - @test MOI.get(model, MOI.DualStatus(1)) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.DualStatus(result_index)) == - MOI.NO_SOLUTION - @test MOI.get(model, MOI.ConstraintDual(1), c) ≈ 1.0 atol = atol rtol = - rtol - @test_throws result_err(MOI.ConstraintDual(result_index)) MOI.get( - model, - MOI.ConstraintDual(result_index), - c, - ) - end end + return +end + +function setup_test( + ::typeof(test_result_index), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1.0], + ), + ) + return end -unittests["solve_result_index"] = solve_result_index -function solve_farkas_equalto_upper(model::MOI.ModelLike, config::Config) +""" + test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, + ) + +Test the Farkas dual of an equality constraint violated above. +""" +function test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.infeas_certificates) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) clb = @@ -242,21 +406,54 @@ function solve_farkas_equalto_upper(model::MOI.ModelLike, config::Config) MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0), MOI.EqualTo(-1.0), ) - if config.solve && config.infeas_certificates - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE - @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE - clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) - c_dual = MOI.get(model, MOI.ConstraintDual(), c) - @test clb_dual[1] > config.atol - @test clb_dual[2] > config.atol - @test c_dual[1] < -config.atol - @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) + c_dual = MOI.get(model, MOI.ConstraintDual(), c) + @test clb_dual[1] > config.atol + @test clb_dual[2] > config.atol + @test c_dual[1] < -config.atol + @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol + return end -unittests["solve_farkas_equalto_upper"] = solve_farkas_equalto_upper -function solve_farkas_equalto_lower(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + (MOI.NO_SOLUTION, [NaN, NaN]), + MOI.INFEASIBILITY_CERTIFICATE, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => + [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-1.0], + ), + ) + return +end + +""" + test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, + ) + +Test the Farkas dual of an equality constraint violated below. +""" +function test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_lower( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.infeas_certificates) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) clb = @@ -266,21 +463,53 @@ function solve_farkas_equalto_lower(model::MOI.ModelLike, config::Config) MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], x), 0.0), MOI.EqualTo(1.0), ) - if config.solve && config.infeas_certificates - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE - @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE - clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) - c_dual = MOI.get(model, MOI.ConstraintDual(), c) - @test clb_dual[1] > config.atol - @test clb_dual[2] > config.atol - @test c_dual[1] > config.atol - @test clb_dual ≈ [2, 1] .* c_dual atol = config.atol rtol = config.rtol - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) + c_dual = MOI.get(model, MOI.ConstraintDual(), c) + @test clb_dual[1] > config.atol + @test clb_dual[2] > config.atol + @test c_dual[1] > config.atol + @test clb_dual ≈ [2, 1] .* c_dual atol = config.atol rtol = config.rtol end -unittests["solve_farkas_equalto_lower"] = solve_farkas_equalto_lower -function solve_farkas_lessthan(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_lower), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + (MOI.NO_SOLUTION, [NaN, NaN]), + MOI.INFEASIBILITY_CERTIFICATE, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => + [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [1.0], + ), + ) + return +end + +""" + test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, + ) + +Test the Farkas dual of a less-than constraint. +""" +function test_DualStatus_INFEASIBILITY_CERTIFICATE_LessThan( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.infeas_certificates) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) clb = @@ -290,21 +519,53 @@ function solve_farkas_lessthan(model::MOI.ModelLike, config::Config) MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0), MOI.LessThan(-1.0), ) - if config.solve && config.infeas_certificates - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE - @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE - clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) - c_dual = MOI.get(model, MOI.ConstraintDual(), c) - @test clb_dual[1] > config.atol - @test clb_dual[2] > config.atol - @test c_dual[1] < -config.atol - @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) + c_dual = MOI.get(model, MOI.ConstraintDual(), c) + @test clb_dual[1] > config.atol + @test clb_dual[2] > config.atol + @test c_dual[1] < -config.atol + @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol + return +end + +function setup_test( + ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_LessThan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + (MOI.NO_SOLUTION, [NaN, NaN]), + MOI.INFEASIBILITY_CERTIFICATE, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => + [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + ), + ) + return end -unittests["solve_farkas_lessthan"] = solve_farkas_lessthan -function solve_farkas_greaterthan(model::MOI.ModelLike, config::Config) +""" + test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, + ) + +Test the Farkas dual of a greater-than constraint. +""" +function test_DualStatus_INFEASIBILITY_CERTIFICATE_GreaterThan( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.infeas_certificates) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) clb = @@ -314,21 +575,53 @@ function solve_farkas_greaterthan(model::MOI.ModelLike, config::Config) MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], x), 0.0), MOI.GreaterThan(1.0), ) - if config.solve && config.infeas_certificates - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE - @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE - clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) - c_dual = MOI.get(model, MOI.ConstraintDual(), c) - @test clb_dual[1] > config.atol - @test clb_dual[2] > config.atol - @test c_dual[1] > config.atol - @test clb_dual ≈ [2, 1] .* c_dual atol = config.atol rtol = config.rtol - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) + c_dual = MOI.get(model, MOI.ConstraintDual(), c) + @test clb_dual[1] > config.atol + @test clb_dual[2] > config.atol + @test c_dual[1] > config.atol + @test clb_dual ≈ [2, 1] .* c_dual atol = config.atol rtol = config.rtol + return end -unittests["solve_farkas_greaterthan"] = solve_farkas_greaterthan -function solve_farkas_interval_upper(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_GreaterThan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + (MOI.NO_SOLUTION, [NaN, NaN]), + MOI.INFEASIBILITY_CERTIFICATE, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => + [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], + ), + ) + return +end + +""" + test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, + ) + +Test the Farkas dual of an interval constraint violated above. +""" +function test_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_upper( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.infeas_certificates) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) clb = @@ -338,21 +631,53 @@ function solve_farkas_interval_upper(model::MOI.ModelLike, config::Config) MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0), MOI.Interval(-2.0, -1.0), ) - if config.solve && config.infeas_certificates - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE - @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE - clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) - c_dual = MOI.get(model, MOI.ConstraintDual(), c) - @test clb_dual[1] > config.atol - @test clb_dual[2] > config.atol - @test c_dual[1] < -config.atol - @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) + c_dual = MOI.get(model, MOI.ConstraintDual(), c) + @test clb_dual[1] > config.atol + @test clb_dual[2] > config.atol + @test c_dual[1] < -config.atol + @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol + return +end + +function setup_test( + ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_upper), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + (MOI.NO_SOLUTION, [NaN, NaN]), + MOI.INFEASIBILITY_CERTIFICATE, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => + [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.0], + ), + ) + return end -unittests["solve_farkas_interval_upper"] = solve_farkas_interval_upper -function solve_farkas_interval_lower(model::MOI.ModelLike, config::Config) +""" + test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, + ) + +Test the Farkas dual of an interval constraint violated below. +""" +function test_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_lower( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.infeas_certificates) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) clb = @@ -362,21 +687,53 @@ function solve_farkas_interval_lower(model::MOI.ModelLike, config::Config) MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], x), 0.0), MOI.Interval(1.0, 2.0), ) - if config.solve && config.infeas_certificates - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE - @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE - clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) - c_dual = MOI.get(model, MOI.ConstraintDual(), c) - @test clb_dual[1] > config.atol - @test clb_dual[2] > config.atol - @test c_dual[1] > config.atol - @test clb_dual ≈ [2, 1] .* c_dual atol = config.atol rtol = config.rtol - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) + c_dual = MOI.get(model, MOI.ConstraintDual(), c) + @test clb_dual[1] > config.atol + @test clb_dual[2] > config.atol + @test c_dual[1] > config.atol + @test clb_dual ≈ [2, 1] .* c_dual atol = config.atol rtol = config.rtol + return end -unittests["solve_farkas_interval_lower"] = solve_farkas_interval_lower -function solve_farkas_variable_lessthan(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_lower), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + (MOI.NO_SOLUTION, [NaN, NaN]), + MOI.INFEASIBILITY_CERTIFICATE, + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => + [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1.0], + ), + ) + return +end + +""" + test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, + ) + +Test the Farkas dual of a variable upper bound violated above when minimizing. +""" +function test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan( + model::MOI.ModelLike, + config::Config, +) + if !(config.solve && config.infeas_certificates) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) clb = MOI.add_constraint.(model, MOI.SingleVariable.(x), MOI.LessThan(0.0)) @@ -385,24 +742,52 @@ function solve_farkas_variable_lessthan(model::MOI.ModelLike, config::Config) MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0), MOI.GreaterThan(1.0), ) - if config.solve && config.infeas_certificates - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE - @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE - clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) - c_dual = MOI.get(model, MOI.ConstraintDual(), c) - @test clb_dual[1] < -config.atol - @test clb_dual[2] < -config.atol - @test c_dual[1] > config.atol - @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) + c_dual = MOI.get(model, MOI.ConstraintDual(), c) + @test clb_dual[1] < -config.atol + @test clb_dual[2] < -config.atol + @test c_dual[1] > config.atol + @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol + return end -unittests["solve_farkas_variable_lessthan"] = solve_farkas_variable_lessthan -function solve_farkas_variable_lessthan_max( +function setup_test( + ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + (MOI.NO_SOLUTION, [NaN, NaN]), + MOI.INFEASIBILITY_CERTIFICATE, + (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0, -1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], + ), + ) + return +end + +""" + test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( + model::MOI.ModelLike, + config::Config, + ) + +Test the Farkas dual of a variable upper bound violated above when maximizing. +""" +function test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan_max( model::MOI.ModelLike, config::Config, ) + if !(config.solve && config.infeas_certificates) + return + end MOI.empty!(model) x = MOI.add_variables(model, 2) clb = MOI.add_constraint.(model, MOI.SingleVariable.(x), MOI.LessThan(0.0)) @@ -417,36 +802,82 @@ function solve_farkas_variable_lessthan_max( MOI.ObjectiveFunction{MOI.SingleVariable}(), MOI.SingleVariable(x[1]), ) - if config.solve && config.infeas_certificates - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE - @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE - clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) - c_dual = MOI.get(model, MOI.ConstraintDual(), c) - @test clb_dual[1] < -config.atol - @test clb_dual[2] < -config.atol - @test c_dual[1] > config.atol - @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol - end + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE + clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb) + c_dual = MOI.get(model, MOI.ConstraintDual(), c) + @test clb_dual[1] < -config.atol + @test clb_dual[2] < -config.atol + @test c_dual[1] > config.atol + @test clb_dual ≈ [2, 1] .* -c_dual atol = config.atol rtol = config.rtol + return +end + +function setup_test( + ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan_max), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + (MOI.NO_SOLUTION, [NaN, NaN]), + MOI.INFEASIBILITY_CERTIFICATE, + (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0, -1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], + ), + ) + return end -unittests["solve_farkas_variable_lessthan_max"] = - solve_farkas_variable_lessthan_max -function solve_twice(model::MOI.ModelLike, config::Config) +""" + test_optimize_twice( + model::MOI.ModelLike, + config::Config, + ) + +Test that calling `optimize!` twice in succession does not error. +""" +function test_optimize_twice(model::MOI.ModelLike, config::Config{T}) where {T} + if !config.solve + return + end MOI.empty!(model) x = MOI.add_variable(model) - c = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(1.0)) + MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(one(T))) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) MOI.set( model, MOI.ObjectiveFunction{MOI.SingleVariable}(), MOI.SingleVariable(x), ) - if config.solve - MOI.optimize!(model) - MOI.optimize!(model) - MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL - MOI.get(model, MOI.VariablePrimal(), x) == 1.0 - end + MOI.optimize!(model) + MOI.optimize!(model) + MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL + MOI.get(model, MOI.VariablePrimal(), x) == one(T) + return +end + +function setup_test( + ::typeof(test_optimize_twice), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + ) + return end -unittests["solve_twice"] = solve_twice diff --git a/test/Bridges/Objective/functionize.jl b/test/Bridges/Objective/functionize.jl index 0ba095809f..72df7e9598 100644 --- a/test/Bridges/Objective/functionize.jl +++ b/test/Bridges/Objective/functionize.jl @@ -13,13 +13,13 @@ config = MOIT.Config() bridged_mock = MOIB.Objective.Functionize{Float64}(mock) -@testset "solve_singlevariable_obj" begin +@testset "test_ObjectiveFunction_SingleVariable" begin MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0], MOI.FEASIBLE_POINT), ) - MOIT.solve_singlevariable_obj(bridged_mock, config) + MOIT.test_ObjectiveFunction_SingleVariable(bridged_mock, config) @test MOI.get(mock, MOI.ObjectiveFunctionType()) == MOI.ScalarAffineFunction{Float64} @test MOI.get(bridged_mock, MOI.ObjectiveFunctionType()) == @@ -42,7 +42,7 @@ end # Tests that the `ObjectiveValue` attribute passed has the correct # `result_index`. -@testset "solve_result_index" begin +@testset "test_result_index" begin MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -53,5 +53,5 @@ end (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1.0], ), ) - MOIT.solve_result_index(bridged_mock, config) + MOIT.test_result_index(bridged_mock, config) end diff --git a/test/Bridges/Objective/slack.jl b/test/Bridges/Objective/slack.jl index 333d528b0e..984fc47696 100644 --- a/test/Bridges/Objective/slack.jl +++ b/test/Bridges/Objective/slack.jl @@ -22,7 +22,7 @@ bridged_mock = MOIB.Objective.Slack{Float64}(mock) @test_throws err MOI.set(bridged_mock, MOI.ObjectiveFunction{F}(), zero(F)) end -@testset "solve_qp_edge_cases" begin +@testset "test_qp_ObjectiveFunction_edge_cases" begin MOIU.set_mock_optimize!( mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -46,7 +46,7 @@ end (MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0]), ), ) - MOIT.solve_qp_edge_cases(bridged_mock, config) + MOIT.test_qp_ObjectiveFunction_edge_cases(bridged_mock, config) @test MOIB.is_objective_bridged(bridged_mock) @test MOI.get(bridged_mock, MOI.ObjectiveFunctionType()) == diff --git a/test/Test/unit.jl b/test/Test/unit.jl index 9d13bd3f8a..aac1d54dbf 100644 --- a/test/Test/unit.jl +++ b/test/Test/unit.jl @@ -25,90 +25,41 @@ end model, config, [ - "solve_blank_obj", - "solve_constant_obj", - "solve_singlevariable_obj", + "test_ObjectiveFunction_blank", + "test_ObjectiveFunction_SingleVariable", "solve_affine_lessthan", "solve_affine_greaterthan", "solve_affine_equalto", "solve_affine_interval", "solve_duplicate_terms_scalar_affine", "solve_duplicate_terms_vector_affine", - "solve_qp_edge_cases", - "solve_qp_zero_offdiag", + "test_qp_ObjectiveFunction_edge_cases", + "test_qp_ObjectiveFunction_zero_ofdiag", "solve_qcp_edge_cases", "solve_affine_deletion_edge_cases", - "solve_duplicate_terms_obj", - "solve_objbound_edge_cases", + "test_ObjectiveFunction_duplicate_terms", + "test_ObjectiveBound_edge_cases", "solve_zero_one_with_bounds_1", "solve_zero_one_with_bounds_2", "solve_zero_one_with_bounds_3", "solve_start_soc", - "solve_unbounded_model", - "solve_single_variable_dual_min", - "solve_single_variable_dual_max", - "solve_result_index", - "solve_farkas_equalto_lower", - "solve_farkas_equalto_upper", - "solve_farkas_lessthan", - "solve_farkas_greaterthan", - "solve_farkas_interval_lower", - "solve_farkas_interval_upper", - "solve_farkas_variable_lessthan", - "solve_farkas_variable_lessthan_max", - "solve_twice", + "test_TerminationStatus_DUAL_INFEASIBLE", + "test_SingleVariable_ConstraintDual_MIN_SENSE", + "test_SingleVariable_ConstraintDual_MAX_SENSE", + "test_result_index", + "test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_lower", + "test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper", + "test_DualStatus_INFEASIBILITY_CERTIFICATE_LessThan", + "test_DualStatus_INFEASIBILITY_CERTIFICATE_GreaterThan", + "test_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_lower", + "test_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_upper", + "test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan", + "test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan_max", + "test_optimize_twice", ], ) MOI.empty!(model) end - - @testset "solve_blank_obj" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1]), - MOI.FEASIBLE_POINT, - ), - ) - MOIT.solve_blank_obj(mock, config) - # The objective is blank so any primal value ≥ 1 is correct - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2]), - MOI.FEASIBLE_POINT, - ), - ) - MOIT.solve_blank_obj(mock, config) - end - @testset "solve_constant_obj" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1]), - MOI.FEASIBLE_POINT, - ), - ) - MOIT.solve_constant_obj(mock, config) - end - @testset "solve_singlevariable_obj" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1]), - MOI.FEASIBLE_POINT, - ), - ) - MOIT.solve_singlevariable_obj(mock, config) - end @testset "solve_affine_lessthan" begin MOIU.set_mock_optimize!( mock, @@ -206,56 +157,6 @@ end ) MOIT.solve_qcp_edge_cases(mock, config) end - - @testset "solve_qp_edge_cases" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]), - ), - ) - MOIT.solve_qp_edge_cases(mock, config) - end - @testset "solve_qp_zero_offdiag" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]), - ), - ) - MOIT.solve_qp_zero_offdiag(mock, config) - end - @testset "solve_duplicate_terms_obj" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1]), - MOI.FEASIBLE_POINT, - ), - ) - MOIT.solve_duplicate_terms_obj(mock, config) - end @testset "solve_affine_deletion_edge_cases" begin MOIU.set_mock_optimize!( mock, @@ -287,45 +188,6 @@ end ) MOIT.solve_affine_deletion_edge_cases(mock, config) end - @testset "solve_objbound_edge_cases" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 3.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - ) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 3.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 2.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.5]), - ) - end, - (mock::MOIU.MockOptimizer) -> begin - MOI.set(mock, MOI.ObjectiveBound(), 4.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.5]), - ) - end, - ) - MOIT.solve_objbound_edge_cases(mock, config) - end - @testset "solve_zero_one_with_bounds" begin MOIU.set_mock_optimize!( mock, @@ -358,209 +220,4 @@ end ) MOIT.solve_start_soc(mock, config) end - - @testset "solve_unbounded_model" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> begin - MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE) - end, - ) - MOIT.solve_unbounded_model(mock, config) - end - - @testset "solve_single_variable_dual_min" begin - flag = mock.eval_variable_constraint_dual - mock.eval_variable_constraint_dual = false - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1.0], - (MOI.SingleVariable, MOI.LessThan{Float64}) => [0.0], - ), - ) - MOIT.solve_single_variable_dual_min(mock, config) - mock.eval_variable_constraint_dual = flag - end - - @testset "solve_single_variable_dual_max" begin - flag = mock.eval_variable_constraint_dual - mock.eval_variable_constraint_dual = false - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [0.0], - (MOI.SingleVariable, MOI.LessThan{Float64}) => [-1.0], - ), - ) - MOIT.solve_single_variable_dual_max(mock, config) - mock.eval_variable_constraint_dual = flag - end - - @testset "solve_result_index" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1.0], - ), - ) - MOIT.solve_result_index(mock, config) - end - - @testset "solve_farkas_equalto_upper" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - (MOI.NO_SOLUTION, [NaN, NaN]), - MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-1.0], - ), - ) - MOIT.solve_farkas_equalto_upper(mock, config) - end - - @testset "solve_farkas_equalto_lower" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - (MOI.NO_SOLUTION, [NaN, NaN]), - MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [1.0], - ), - ) - MOIT.solve_farkas_equalto_lower(mock, config) - end - - @testset "solve_farkas_lessthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - (MOI.NO_SOLUTION, [NaN, NaN]), - MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], - ), - ) - MOIT.solve_farkas_lessthan(mock, config) - end - - @testset "solve_farkas_greaterthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - (MOI.NO_SOLUTION, [NaN, NaN]), - MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], - ), - ) - MOIT.solve_farkas_greaterthan(mock, config) - end - - @testset "solve_farkas_interval_upper" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - (MOI.NO_SOLUTION, [NaN, NaN]), - MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.0], - ), - ) - MOIT.solve_farkas_interval_upper(mock, config) - end - - @testset "solve_farkas_interval_lower" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - (MOI.NO_SOLUTION, [NaN, NaN]), - MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1.0], - ), - ) - MOIT.solve_farkas_interval_lower(mock, config) - end - - @testset "solve_farkas_variable_lessthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - (MOI.NO_SOLUTION, [NaN, NaN]), - MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0, -1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], - ), - ) - MOIT.solve_farkas_variable_lessthan(mock, config) - end - - @testset "solve_farkas_variable_lessthan_max" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - (MOI.NO_SOLUTION, [NaN, NaN]), - MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0, -1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], - ), - ) - MOIT.solve_farkas_variable_lessthan_max(mock, config) - end - - @testset "solve_twice" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - ) - MOIT.solve_twice(mock, config) - end end From 453a1f5a1a0a950b9f4a0604f47221b2b22f7871 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 11:07:08 +1200 Subject: [PATCH 18/23] Refactoring of basic functions --- src/Test/Test.jl | 230 ++++++++++++++++++++++++++-- src/Test/UnitTests/attributes.jl | 4 +- src/Test/UnitTests/constraints.jl | 32 ++-- src/Test/UnitTests/modifications.jl | 42 ++--- src/Test/UnitTests/objectives.jl | 24 ++- src/Test/UnitTests/solve.jl | 98 +++++------- src/Test/UnitTests/unit_tests.jl | 102 ------------ src/Test/UnitTests/variables.jl | 12 +- src/Test/config.jl | 124 --------------- 9 files changed, 314 insertions(+), 354 deletions(-) delete mode 100644 src/Test/UnitTests/unit_tests.jl delete mode 100644 src/Test/config.jl diff --git a/src/Test/Test.jl b/src/Test/Test.jl index fcf68a94b6..3027d9ce9e 100644 --- a/src/Test/Test.jl +++ b/src/Test/Test.jl @@ -6,20 +6,92 @@ const MOIU = MOI.Utilities using Test -include("config.jl") +struct Config{T<:Real} + atol::T + rtol::T + solve::Bool + query_number_of_constraints::Bool + query::Bool + modify_lhs::Bool + duals::Bool + dual_objective_value::Bool + infeas_certificates::Bool + optimal_status::MOI.TerminationStatusCode + basis::Bool -include("modellike.jl") + """ + Config{T}(; + atol::Real = Base.rtoldefault(T), + rtol::Real = Base.rtoldefault(T), + solve::Bool = true, + query_number_of_constraints::Bool = true, + query::Bool = true, + modify_lhs::Bool = true, + duals::Bool = true, + dual_objective_value::Bool = duals, + infeas_certificates::Bool = true, + optimal_status = MOI.OPTIMAL, + basis::Bool = false, + ) -include("contlinear.jl") -include("contconic.jl") -include("contquadratic.jl") + Return an object that is used to configure various tests. -include("intlinear.jl") -include("intconic.jl") + ## Keywords -include("nlp.jl") + * `atol::Real = Base.rtoldefault(T)`: Control the absolute tolerance used + when comparing solutions. + * `rtol::Real = Base.rtoldefault(T)`: Control the relative tolerance used + when comparing solutions. + * `solve::Bool = true`: Set to `false` to skip tests requiring a call to + [`MOI.optimize!`](@ref) + * `query_number_of_constraints::Bool = true`: Set to `false` to skip tests + requiring a call to [`MOI.NumberOfConstraints`](@ref). + * `query::Bool = true`: Set to `false` to skip tests requiring a call to + [`MOI.get`](@ref) for [`MOI.ConstraintFunction`](@ref) and + [`MOI.ConstraintSet`](@ref) + * `modify_lhs::Bool = true`: + * `duals::Bool = true`: Set to `false` to skip tests querying + [`MOI.ConstraintDual`](@ref). + * `dual_objective_value::Bool = duals`: Set to `false` to skip tests + querying [`MOI.DualObjectiveValue`](@ref). + * `infeas_certificates::Bool = true`: Set to `false` to skip tests querying + primal and dual infeasibility certificates. + * `optimal_status = MOI.OPTIMAL`: Set to `MOI.LOCALLY_SOLVED` if the solver + cannot prove global optimality. + * `basis::Bool = false`: Set to `true` if the solver supports + [`MOI.ConstraintBasisStatus`](@ref) and [`MOI.VariableBasisStatus`](@ref). + """ + function Config{T}(; + atol::Real = Base.rtoldefault(T), + rtol::Real = Base.rtoldefault(T), + solve::Bool = true, + query_number_of_constraints::Bool = true, + query::Bool = true, + modify_lhs::Bool = true, + duals::Bool = true, + dual_objective_value::Bool = duals, + infeas_certificates::Bool = true, + optimal_status = MOI.OPTIMAL, + basis::Bool = false, + ) where {T<:Real} + return new( + atol, + rtol, + solve, + query_number_of_constraints, + query, + modify_lhs, + duals, + dual_objective_value, + infeas_certificates, + optimal_status, + basis, + ) + end + Config(; kwargs...) = Config{Float64}(; kwargs...) +end -include("UnitTests/unit_tests.jl") +@deprecate TestConfig Config """ setup_test(::typeof(f), model::MOI.ModelLike, config::Config) @@ -120,4 +192,144 @@ function runtests( end end +""" + _test_model_solution( + model::MOI.ModelLike, + config::Config; + objective_value = nothing, + variable_primal = nothing, + constraint_primal = nothing, + constraint_dual = nothing, + ) + +Solve, and then test, various aspects of a model. + +First, check that `TerminationStatus == MOI.OPTIMAL`. + +If `objective_value` is not nothing, check that the attribute `ObjectiveValue()` +is approximately `objective_value`. + +If `variable_primal` is not nothing, check that the attribute `PrimalStatus` is +`MOI.FEASIBLE_POINT`. Then for each `(index, value)` in `variable_primal`, check +that the primal value of the variable `index` is approximately `value`. + +If `constraint_primal` is not nothing, check that the attribute `PrimalStatus` is +`MOI.FEASIBLE_POINT`. Then for each `(index, value)` in `constraint_primal`, check +that the primal value of the constraint `index` is approximately `value`. + +Finally, if `config.duals = true`, and if `constraint_dual` is not nothing, +check that the attribute `DualStatus` is `MOI.FEASIBLE_POINT`. Then for each +`(index, value)` in `constraint_dual`, check that the dual of the constraint +`index` is approximately `value`. + +### Example + +```julia +MOIU.loadfromstring!(model, \"\"\" + variables: x + minobjective: 2.0x + 1.0 + c: x >= 1.0 +\"\"\") +x = MOI.get(model, MOI.VariableIndex, "x") +c = MOI.get(model, MOI.ConstraintIndex{MOI.SingleVariable, MOI.GreaterThan{Float64}}, "c") +_test_model_solution( + model, + config; + objective_value = 3.0, + variable_primal = [(x, 1.0)], + constraint_primal = [(c, 1.0)], + constraint_dual = [(c, 2.0)], +) +``` +""" +function _test_model_solution( + model, + config; + objective_value = nothing, + variable_primal = nothing, + constraint_primal = nothing, + constraint_dual = nothing, +) + if !config.solve + return + end + atol, rtol = config.atol, config.rtol + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status + if objective_value !== nothing + @test MOI.get(model, MOI.ObjectiveValue()) ≈ objective_value atol = atol rtol = + rtol + end + if variable_primal !== nothing + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + for (index, solution_value) in variable_primal + @test MOI.get(model, MOI.VariablePrimal(), index) ≈ solution_value atol = + atol rtol = rtol + end + end + if constraint_primal !== nothing + @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + for (index, solution_value) in constraint_primal + @test MOI.get(model, MOI.ConstraintPrimal(), index) ≈ solution_value atol = + atol rtol = rtol + end + end + if config.duals + if constraint_dual !== nothing + @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + for (index, solution_value) in constraint_dual + @test MOI.get(model, MOI.ConstraintDual(), index) ≈ + solution_value atol = atol rtol = rtol + end + end + end + return +end + +macro moitestset(setname, subsets = false) + testname = Symbol(string(setname) * "test") + testdict = Symbol(string(testname) * "s") + if subsets + runtest = :(f(model, config, exclude)) + else + runtest = :(f(model, config)) + end + return esc( + :( + function $testname( + model::$MOI.ModelLike, + config::$MOI.Test.Config, + exclude::Vector{String} = String[], + ) + for (name, f) in $testdict + if name in exclude + continue + end + @testset "$name" begin + $runtest + end + end + end + ), + ) +end +const unittests = Dict{String,Function}() +@moitestset unit + +include("modellike.jl") +include("contlinear.jl") +include("contconic.jl") +include("contquadratic.jl") +include("intlinear.jl") +include("intconic.jl") +include("nlp.jl") + +include("UnitTests/attributes.jl") +include("UnitTests/basic_constraint_tests.jl") +include("UnitTests/constraints.jl") +include("UnitTests/modifications.jl") +include("UnitTests/objectives.jl") +include("UnitTests/solve.jl") +include("UnitTests/variables.jl") + end # module diff --git a/src/Test/UnitTests/attributes.jl b/src/Test/UnitTests/attributes.jl index a561cd4604..7ae7c597a7 100644 --- a/src/Test/UnitTests/attributes.jl +++ b/src/Test/UnitTests/attributes.jl @@ -123,7 +123,7 @@ function test_attribute_RawStatusString(model::MOI.ModelLike, config::Config) MOI.ObjectiveFunction{MOI.SingleVariable}(), MOI.SingleVariable(x), ) - test_model_solution( + _test_model_solution( model, config, objective_value = 0.0, @@ -171,7 +171,7 @@ function test_attribute_SolveTimeSec(model::MOI.ModelLike, config::Config) MOI.ObjectiveFunction{MOI.SingleVariable}(), MOI.SingleVariable(x), ) - test_model_solution( + _test_model_solution( model, config, objective_value = 0.0, diff --git a/src/Test/UnitTests/constraints.jl b/src/Test/UnitTests/constraints.jl index e3c5abbafe..5b74b5931f 100644 --- a/src/Test/UnitTests/constraints.jl +++ b/src/Test/UnitTests/constraints.jl @@ -58,7 +58,7 @@ function solve_affine_lessthan(model::MOI.ModelLike, config::Config) }, "c", ) - return test_model_solution( + return _test_model_solution( model, config; objective_value = 0.5, @@ -95,7 +95,7 @@ function solve_affine_greaterthan(model::MOI.ModelLike, config::Config) }, "c", ) - return test_model_solution( + return _test_model_solution( model, config; objective_value = 0.5, @@ -132,7 +132,7 @@ function solve_affine_equalto(model::MOI.ModelLike, config::Config) }, "c", ) - return test_model_solution( + return _test_model_solution( model, config; objective_value = 0.5, @@ -169,7 +169,7 @@ function solve_affine_interval(model::MOI.ModelLike, config::Config) }, "c", ) - return test_model_solution( + return _test_model_solution( model, config; objective_value = 6.0, @@ -205,7 +205,7 @@ function solve_duplicate_terms_scalar_affine( MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 1.0], [x, x]), 0.0) c = MOI.add_constraint(model, f, MOI.LessThan(1.0)) - return test_model_solution( + return _test_model_solution( model, config; objective_value = 0.5, @@ -244,7 +244,7 @@ function solve_duplicate_terms_vector_affine( [-1.0], ) c = MOI.add_constraint(model, f, MOI.Nonpositives(1)) - return test_model_solution( + return _test_model_solution( model, config; objective_value = 0.5, @@ -309,7 +309,7 @@ function solve_qcp_edge_cases(model::MOI.ModelLike, config::Config) ), MOI.LessThan(1.0), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 1.5, @@ -351,7 +351,7 @@ function solve_qcp_edge_cases(model::MOI.ModelLike, config::Config) ), MOI.LessThan(1.0), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 0.5 + (√13 - 1) / 2, @@ -392,7 +392,7 @@ function solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::Config) ) # test adding a VectorAffineFunction -in- LessThan c1 = MOI.add_constraint(model, vaf, MOI.Nonpositives(1)) - test_model_solution( + _test_model_solution( model, config; objective_value = 0.0, @@ -400,7 +400,7 @@ function solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::Config) ) # test adding a ScalarAffineFunction -in- LessThan c2 = MOI.add_constraint(model, saf, MOI.LessThan(1.0)) - test_model_solution( + _test_model_solution( model, config; objective_value = 0.0, @@ -414,7 +414,7 @@ function solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::Config) catch err @test err.index == c1 end - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -422,7 +422,7 @@ function solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::Config) ) # add a different VectorAffineFunction constraint c3 = MOI.add_constraint(model, vaf2, MOI.Nonpositives(1)) - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -430,7 +430,7 @@ function solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::Config) ) # delete the ScalarAffineFunction MOI.delete(model, c2) - return test_model_solution( + return _test_model_solution( model, config; objective_value = 2.0, @@ -452,7 +452,7 @@ function solve_zero_one_with_bounds_1(model::MOI.ModelLike, config::Config) """, ) x = MOI.get(model, MOI.VariableIndex, "x") - return test_model_solution( + return _test_model_solution( model, config; objective_value = 2.0, @@ -474,7 +474,7 @@ function solve_zero_one_with_bounds_2(model::MOI.ModelLike, config::Config) """, ) x = MOI.get(model, MOI.VariableIndex, "x") - return test_model_solution( + return _test_model_solution( model, config; objective_value = 0.0, @@ -540,7 +540,7 @@ function solve_start_soc(model::MOI.ModelLike, config::Config{T}) where {T} end if config.solve MOI.optimize!(model) - MOI.Test.test_model_solution( + MOI.Test._test_model_solution( model, config; objective_value = o, diff --git a/src/Test/UnitTests/modifications.jl b/src/Test/UnitTests/modifications.jl index c90816f0b3..71e32a5f18 100644 --- a/src/Test/UnitTests/modifications.jl +++ b/src/Test/UnitTests/modifications.jl @@ -58,7 +58,7 @@ function test_modification_set_singlevariable_lessthan( x = MOI.get(model, MOI.VariableIndex, "x") c = MOI.ConstraintIndex{MOI.SingleVariable,MOI.LessThan{Float64}}(x.value) @test c.value == x.value - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -68,7 +68,7 @@ function test_modification_set_singlevariable_lessthan( ) MOI.set(model, MOI.ConstraintSet(), c, MOI.LessThan(2.0)) @test MOI.get(model, MOI.ConstraintSet(), c) == MOI.LessThan(2.0) - test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, @@ -128,7 +128,7 @@ function test_modification_transform_singlevariable_lessthan( x = MOI.get(model, MOI.VariableIndex, "x") c = MOI.ConstraintIndex{MOI.SingleVariable,MOI.LessThan{Float64}}(x.value) @test c.value == x.value - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -140,7 +140,7 @@ function test_modification_transform_singlevariable_lessthan( @test !MOI.is_valid(model, c) @test MOI.is_valid(model, c2) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) - test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, @@ -199,7 +199,7 @@ function test_modification_set_scalaraffine_lessthan( ) x = MOI.get(model, MOI.VariableIndex, "x") c = MOI.get(model, MOI.ConstraintIndex, "c") - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -209,7 +209,7 @@ function test_modification_set_scalaraffine_lessthan( ) MOI.set(model, MOI.ConstraintSet(), c, MOI.LessThan(2.0)) @test MOI.get(model, MOI.ConstraintSet(), c) == MOI.LessThan(2.0) - test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, @@ -272,7 +272,7 @@ function test_modification_coef_scalaraffine_lessthan( ) x = MOI.get(model, MOI.VariableIndex, "x") c = MOI.get(model, MOI.ConstraintIndex, "c") - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -281,7 +281,7 @@ function test_modification_coef_scalaraffine_lessthan( constraint_dual = [(c, -1.0)], ) MOI.modify(model, c, MOI.ScalarCoefficientChange(x, 2.0)) - test_model_solution( + _test_model_solution( model, config; objective_value = 0.5, @@ -344,7 +344,7 @@ function test_modification_func_scalaraffine_lessthan( ) x = MOI.get(model, MOI.VariableIndex, "x") c = MOI.get(model, MOI.ConstraintIndex, "c") - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -360,7 +360,7 @@ function test_modification_func_scalaraffine_lessthan( ) foo = MOI.get(model, MOI.ConstraintFunction(), c) @test foo ≈ MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(2.0, x)], 0.0) - test_model_solution( + _test_model_solution( model, config; objective_value = 0.5, @@ -424,7 +424,7 @@ function test_modification_func_vectoraffine_nonneg( x = MOI.get(model, MOI.VariableIndex, "x") y = MOI.get(model, MOI.VariableIndex, "y") c = MOI.get(model, MOI.ConstraintIndex, "c") - test_model_solution( + _test_model_solution( model, config; objective_value = 0.0, @@ -451,7 +451,7 @@ function test_modification_func_vectoraffine_nonneg( ], [-1.0, -1.5], ) - test_model_solution( + _test_model_solution( model, config; objective_value = 2.5, @@ -517,7 +517,7 @@ function test_modification_const_vectoraffine_nonpos( ), MOI.Nonpositives(2), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 0.0, @@ -525,7 +525,7 @@ function test_modification_const_vectoraffine_nonpos( constraint_primal = [(c, [0.0, 0.0])], ) MOI.modify(model, c, MOI.VectorConstantChange([-1.0, -1.5])) - test_model_solution( + _test_model_solution( model, config; objective_value = 2.5, @@ -590,7 +590,7 @@ function test_modification_multirow_vectoraffine_nonpos( ), MOI.Nonpositives(2), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 0.5, @@ -598,7 +598,7 @@ function test_modification_multirow_vectoraffine_nonpos( constraint_primal = [(c, [-0.5, 0.0])], ) MOI.modify(model, c, MOI.MultirowChange(x, [(1, 4.0), (2, 3.0)])) - test_model_solution( + _test_model_solution( model, config; objective_value = 0.25, @@ -649,7 +649,7 @@ function test_modification_const_scalar_objective( """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = 3.0, @@ -660,7 +660,7 @@ function test_modification_const_scalar_objective( MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarConstantChange(3.0), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 4.0, @@ -707,7 +707,7 @@ function test_modification_coef_scalar_objective( """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -718,7 +718,7 @@ function test_modification_coef_scalar_objective( MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarCoefficientChange(x, 3.0), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 3.0, @@ -771,7 +771,7 @@ function test_modification_delete_variable_with_single_variable_obj( ) @test c.value == x.value MOI.delete(model, y) - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, diff --git a/src/Test/UnitTests/objectives.jl b/src/Test/UnitTests/objectives.jl index f35c929056..2064fc8bec 100644 --- a/src/Test/UnitTests/objectives.jl +++ b/src/Test/UnitTests/objectives.jl @@ -98,7 +98,7 @@ function test_ObjectiveFunction_constant(model::MOI.ModelLike, config::Config) # We test this after the creation of every `SingleVariable` constraint # to ensure a good coverage of corner cases. @test c.value == x.value - test_model_solution( + _test_model_solution( model, config; objective_value = 3.0, @@ -153,7 +153,7 @@ function test_ObjectiveFunction_blank(model::MOI.ModelLike, config::Config) x.value, ) @test c.value == x.value - test_model_solution( + _test_model_solution( model, config; objective_value = 0.0, @@ -209,7 +209,7 @@ function test_ObjectiveFunction_SingleVariable( x.value, ) @test c.value == x.value - test_model_solution( + _test_model_solution( model, config; objective_value = 1.0, @@ -279,7 +279,7 @@ function test_qp_ObjectiveFunction_edge_cases( 0.0, # constant ), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 5.0, @@ -296,7 +296,7 @@ function test_qp_ObjectiveFunction_edge_cases( 0.0, # constant ), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 7.0, @@ -309,15 +309,11 @@ function test_qp_ObjectiveFunction_edge_cases( obj_attr, MOI.ScalarQuadraticFunction( MOI.ScalarAffineTerm{Float64}[], # affine terms - MOI.ScalarQuadraticTerm.( - [2.0, 2.0], - [x[1], x[1]], - [x[1], x[1]], - ), # quad + MOI.ScalarQuadraticTerm.([2.0, 2.0], [x[1], x[1]], [x[1], x[1]]), # quad 0.0, # constant ), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, @@ -338,7 +334,7 @@ function test_qp_ObjectiveFunction_edge_cases( 0.0, # constant ), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 7.0, @@ -421,7 +417,7 @@ function test_qp_ObjectiveFunction_zero_ofdiag( 0.0, # constant ), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 5.0, @@ -470,7 +466,7 @@ function test_ObjectiveFunction_duplicate_terms( 0.0, ), ) - test_model_solution( + _test_model_solution( model, config; objective_value = 3.0, diff --git a/src/Test/UnitTests/solve.jl b/src/Test/UnitTests/solve.jl index 7938acef59..03552c6ef5 100644 --- a/src/Test/UnitTests/solve.jl +++ b/src/Test/UnitTests/solve.jl @@ -20,7 +20,7 @@ function test_ObjectiveBound_edge_cases(model::MOI.ModelLike, config::Config) """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = 3.0, @@ -41,7 +41,7 @@ function test_ObjectiveBound_edge_cases(model::MOI.ModelLike, config::Config) """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = 3.0, @@ -61,7 +61,7 @@ function test_ObjectiveBound_edge_cases(model::MOI.ModelLike, config::Config) """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, @@ -81,7 +81,7 @@ function test_ObjectiveBound_edge_cases(model::MOI.ModelLike, config::Config) """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = 4.0, @@ -100,35 +100,19 @@ function setup_test( model, (mock::MOIU.MockOptimizer) -> begin MOI.set(mock, MOI.ObjectiveBound(), 3.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - ) + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [2.0])) end, (mock::MOIU.MockOptimizer) -> begin MOI.set(mock, MOI.ObjectiveBound(), 3.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ) + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])) end, (mock::MOIU.MockOptimizer) -> begin MOI.set(mock, MOI.ObjectiveBound(), 2.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.5]), - ) + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.5])) end, (mock::MOIU.MockOptimizer) -> begin MOI.set(mock, MOI.ObjectiveBound(), 4.0) - MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.5]), - ) + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.5])) end, ) return @@ -168,7 +152,8 @@ function setup_test( ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.DUAL_INFEASIBLE), ) return end @@ -319,8 +304,7 @@ function test_result_index(model::MOI.ModelLike, config::Config) return MOI.ResultIndexBoundsError{typeof(attr)}(attr, result_count) end result_index = result_count + 1 - @test MOI.get(model, MOI.ObjectiveValue(1)) ≈ 1.0 atol = atol rtol = - rtol + @test MOI.get(model, MOI.ObjectiveValue(1)) ≈ 1.0 atol = atol rtol = rtol @test_throws result_err(MOI.ObjectiveValue(result_index)) MOI.get( model, MOI.ObjectiveValue(result_index), @@ -335,8 +319,7 @@ function test_result_index(model::MOI.ModelLike, config::Config) end @test MOI.get(model, MOI.PrimalStatus(1)) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.PrimalStatus(result_index)) == MOI.NO_SOLUTION - @test MOI.get(model, MOI.VariablePrimal(1), x) ≈ 1.0 atol = atol rtol = - rtol + @test MOI.get(model, MOI.VariablePrimal(1), x) ≈ 1.0 atol = atol rtol = rtol @test_throws result_err(MOI.VariablePrimal(result_index)) MOI.get( model, MOI.VariablePrimal(result_index), @@ -351,8 +334,7 @@ function test_result_index(model::MOI.ModelLike, config::Config) ) if config.duals @test MOI.get(model, MOI.DualStatus(1)) == MOI.FEASIBLE_POINT - @test MOI.get(model, MOI.DualStatus(result_index)) == - MOI.NO_SOLUTION + @test MOI.get(model, MOI.DualStatus(result_index)) == MOI.NO_SOLUTION @test MOI.get(model, MOI.ConstraintDual(1), c) ≈ 1.0 atol = atol rtol = rtol @test_throws result_err(MOI.ConstraintDual(result_index)) MOI.get( @@ -430,8 +412,7 @@ function setup_test( MOI.INFEASIBLE, (MOI.NO_SOLUTION, [NaN, NaN]), MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0], (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [-1.0], ), @@ -486,8 +467,7 @@ function setup_test( MOI.INFEASIBLE, (MOI.NO_SOLUTION, [NaN, NaN]), MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0], (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [1.0], ), @@ -543,9 +523,9 @@ function setup_test( MOI.INFEASIBLE, (MOI.NO_SOLUTION, [NaN, NaN]), MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-1.0], ), ) return @@ -599,9 +579,9 @@ function setup_test( MOI.INFEASIBLE, (MOI.NO_SOLUTION, [NaN, NaN]), MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [1.0], ), ) return @@ -655,9 +635,9 @@ function setup_test( MOI.INFEASIBLE, (MOI.NO_SOLUTION, [NaN, NaN]), MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.0], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => + [-1.0], ), ) return @@ -711,9 +691,9 @@ function setup_test( MOI.INFEASIBLE, (MOI.NO_SOLUTION, [NaN, NaN]), MOI.INFEASIBILITY_CERTIFICATE, - (MOI.SingleVariable, MOI.GreaterThan{Float64}) => - [2.0, 1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1.0], + (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [2.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => + [1.0], ), ) return @@ -767,7 +747,8 @@ function setup_test( (MOI.NO_SOLUTION, [NaN, NaN]), MOI.INFEASIBILITY_CERTIFICATE, (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0, -1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [1.0], ), ) return @@ -815,7 +796,9 @@ function test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan_max( end function setup_test( - ::typeof(test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan_max), + ::typeof( + test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan_max, + ), model::MOIU.MockOptimizer, ::Config, ) @@ -827,7 +810,8 @@ function setup_test( (MOI.NO_SOLUTION, [NaN, NaN]), MOI.INFEASIBILITY_CERTIFICATE, (MOI.SingleVariable, MOI.LessThan{Float64}) => [-2.0, -1.0], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [1.0], ), ) return @@ -868,16 +852,10 @@ function setup_test( ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), ) return end diff --git a/src/Test/UnitTests/unit_tests.jl b/src/Test/UnitTests/unit_tests.jl deleted file mode 100644 index 0bf89496a5..0000000000 --- a/src/Test/UnitTests/unit_tests.jl +++ /dev/null @@ -1,102 +0,0 @@ -#= - These tests aim to minimally test each expected feature in MOI, in addition - to the full end-to-end tests in contlinear.jl etc -=# - -const unittests = Dict{String,Function}() - -""" - test_model_solution(model::MOI.ModelLike, config::Config; - objective_value = nothing, - variable_primal = nothing, - constraint_primal = nothing, - constraint_dual = nothing - ) - -Solve, and then test, various aspects of a model. - -First, check that `TerminationStatus == MOI.OPTIMAL`. - -If `objective_value` is not nothing, check that the attribute `ObjectiveValue()` -is approximately `objective_value`. - -If `variable_primal` is not nothing, check that the attribute `PrimalStatus` is -`MOI.FEASIBLE_POINT`. Then for each `(index, value)` in `variable_primal`, check -that the primal value of the variable `index` is approximately `value`. - -If `constraint_primal` is not nothing, check that the attribute `PrimalStatus` is -`MOI.FEASIBLE_POINT`. Then for each `(index, value)` in `constraint_primal`, check -that the primal value of the constraint `index` is approximately `value`. - -Finally, if `config.duals = true`, and if `constraint_dual` is not nothing, -check that the attribute `DualStatus` is `MOI.FEASIBLE_POINT`. Then for each -`(index, value)` in `constraint_dual`, check that the dual of the constraint -`index` is approximately `value`. - -### Example - - MOIU.loadfromstring!(model, \"\"\" - variables: x - minobjective: 2.0x + 1.0 - c: x >= 1.0 - \"\"\") - x = MOI.get(model, MOI.VariableIndex, "x") - c = MOI.get(model, MOI.ConstraintIndex{MOI.SingleVariable, MOI.GreaterThan{Float64}}, "c") - test_model_solution(model, config; - objective_value = 3.0, - variable_primal = [(x, 1.0)], - constraint_primal = [(c, 1.0)], - constraint_dual = [(c, 2.0)] - ) - -""" -function test_model_solution( - model, - config; - objective_value = nothing, - variable_primal = nothing, - constraint_primal = nothing, - constraint_dual = nothing, -) - config.solve || return - atol, rtol = config.atol, config.rtol - MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status - if objective_value != nothing - @test MOI.get(model, MOI.ObjectiveValue()) ≈ objective_value atol = atol rtol = - rtol - end - if variable_primal != nothing - @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - for (index, solution_value) in variable_primal - @test MOI.get(model, MOI.VariablePrimal(), index) ≈ solution_value atol = - atol rtol = rtol - end - end - if constraint_primal != nothing - @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT - for (index, solution_value) in constraint_primal - @test MOI.get(model, MOI.ConstraintPrimal(), index) ≈ solution_value atol = - atol rtol = rtol - end - end - if config.duals - if constraint_dual != nothing - @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT - for (index, solution_value) in constraint_dual - @test MOI.get(model, MOI.ConstraintDual(), index) ≈ - solution_value atol = atol rtol = rtol - end - end - end -end - -include("variables.jl") -include("objectives.jl") -include("constraints.jl") -include("basic_constraint_tests.jl") -include("modifications.jl") -include("solve.jl") -include("attributes.jl") - -@moitestset unit diff --git a/src/Test/UnitTests/variables.jl b/src/Test/UnitTests/variables.jl index c1373c60a2..92ea15390b 100644 --- a/src/Test/UnitTests/variables.jl +++ b/src/Test/UnitTests/variables.jl @@ -226,7 +226,7 @@ function test_solve_with_upperbound(model::MOI.ModelLike, config::Config) x.value, ) @test c2.value == x.value - test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, @@ -282,7 +282,7 @@ function test_solve_with_lowerbound(model::MOI.ModelLike, config::Config) @test c1.value == x.value c2 = MOI.ConstraintIndex{MOI.SingleVariable,MOI.LessThan{Float64}}(x.value) @test c2.value == x.value - test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, @@ -337,7 +337,7 @@ x in Integer() """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = 4.0, @@ -383,7 +383,7 @@ x in Integer() """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = -2.0, @@ -429,7 +429,7 @@ x in ZeroOne() """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = -2.0, @@ -475,7 +475,7 @@ x in ZeroOne() """, ) x = MOI.get(model, MOI.VariableIndex, "x") - test_model_solution( + _test_model_solution( model, config; objective_value = 0.0, diff --git a/src/Test/config.jl b/src/Test/config.jl deleted file mode 100644 index 01a8d8e358..0000000000 --- a/src/Test/config.jl +++ /dev/null @@ -1,124 +0,0 @@ -struct Config{T<:Real} - atol::T # absolute tolerance for ... - rtol::T # relative tolerance for ... - solve::Bool # optimize and test result - query_number_of_constraints::Bool # can get `MOI.NumberOfConstraints` attribute - query::Bool # can get objective function, and constraint functions, and constraint sets - modify_lhs::Bool # can modify function of a constraint - duals::Bool # test dual solutions - dual_objective_value::Bool # test `DualObjectiveValue` - infeas_certificates::Bool # check for primal or dual infeasibility certificates when appropriate - # The expected "optimal" status returned by the solver. Either - # MOI.OPTIMAL or MOI.LOCALLY_SOLVED. - optimal_status::MOI.TerminationStatusCode - basis::Bool # can get variable and constraint basis status - - """ - Config{T}(; - atol::Real = Base.rtoldefault(T), - rtol::Real = Base.rtoldefault(T), - solve::Bool = true, - query_number_of_constraints::Bool = true, - query::Bool = true, - modify_lhs::Bool = true, - duals::Bool = true, - dual_objective_value::Bool = duals, - infeas_certificates::Bool = true, - optimal_status = MOI.OPTIMAL, - basis::Bool = false, - ) - - Return an object that is used to configure various tests. - - ## Keywords - - * `atol::Real = Base.rtoldefault(T)`: Control the absolute tolerance used - when comparing solutions. - * `rtol::Real = Base.rtoldefault(T)`: Control the relative tolerance used - when comparing solutions. - * `solve::Bool = true`: Set to `false` to skip tests requiring a call to - [`MOI.optimize!`](@ref) - * `query_number_of_constraints::Bool = true`: Set to `false` to skip tests - requiring a call to [`MOI.NumberOfConstraints`](@ref). - * `query::Bool = true`: Set to `false` to skip tests requiring a call to - [`MOI.get`](@ref) for [`MOI.ConstraintFunction`](@ref) and - [`MOI.ConstraintSet`](@ref) - * `modify_lhs::Bool = true`: - * `duals::Bool = true`: Set to `false` to skip tests querying - [`MOI.ConstraintDual`](@ref). - * `dual_objective_value::Bool = duals`: Set to `false` to skip tests - querying [`MOI.DualObjectiveValue`](@ref). - * `infeas_certificates::Bool = true`: Set to `false` to skip tests querying - primal and dual infeasibility certificates. - * `optimal_status = MOI.OPTIMAL`: Set to `MOI.LOCALLY_SOLVED` if the solver - cannot prove global optimality. - * `basis::Bool = false`: Set to `true` if the solver supports - [`MOI.ConstraintBasisStatus`](@ref) and [`MOI.VariableBasisStatus`](@ref). - """ - function Config{T}(; - atol::Real = Base.rtoldefault(T), - rtol::Real = Base.rtoldefault(T), - solve::Bool = true, - query_number_of_constraints::Bool = true, - query::Bool = true, - modify_lhs::Bool = true, - duals::Bool = true, - dual_objective_value::Bool = duals, - infeas_certificates::Bool = true, - optimal_status = MOI.OPTIMAL, - basis::Bool = false, - ) where {T<:Real} - return new( - atol, - rtol, - solve, - query_number_of_constraints, - query, - modify_lhs, - duals, - dual_objective_value, - infeas_certificates, - optimal_status, - basis, - ) - end - Config(; kwargs...) = Config{Float64}(; kwargs...) -end - -@deprecate TestConfig Config - -""" - @moitestset setname subsets - -Defines a function `setnametest(model, config, exclude)` that runs the tests defined in the dictionary `setnametests` -with the model `model` and config `config` except the tests whose dictionary key is in `exclude`. -If `subsets` is `true` then each test runs in fact multiple tests hence the `exclude` argument is passed -as it can also contains test to be excluded from these subsets of tests. -""" -macro moitestset(setname, subsets = false) - testname = Symbol(string(setname) * "test") - testdict = Symbol(string(testname) * "s") - if subsets - runtest = :(f(model, config, exclude)) - else - runtest = :(f(model, config)) - end - return esc( - :( - function $testname( - model::$MOI.ModelLike, - config::$MOI.Test.Config, - exclude::Vector{String} = String[], - ) - for (name, f) in $testdict - if name in exclude - continue - end - @testset "$name" begin - $runtest - end - end - end - ), - ) -end From 1530835601736cfaae6042280ffcf443087d6a25 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 14:07:38 +1200 Subject: [PATCH 19/23] Migrate UnitTests/constraints.jl --- src/Test/Test.jl | 2 - src/Test/UnitTests/constraints.jl | 636 +++++++++++++++++++------ test/Bridges/Constraint/quad_to_soc.jl | 3 +- test/Bridges/lazy_bridge_optimizer.jl | 18 +- test/Test/Test.jl | 6 + test/Test/unit.jl | 223 --------- test/Utilities/cachingoptimizer.jl | 23 +- test/Utilities/universalfallback.jl | 9 +- 8 files changed, 518 insertions(+), 402 deletions(-) delete mode 100644 test/Test/unit.jl diff --git a/src/Test/Test.jl b/src/Test/Test.jl index 3027d9ce9e..c3d0f58362 100644 --- a/src/Test/Test.jl +++ b/src/Test/Test.jl @@ -313,8 +313,6 @@ macro moitestset(setname, subsets = false) ), ) end -const unittests = Dict{String,Function}() -@moitestset unit include("modellike.jl") include("contlinear.jl") diff --git a/src/Test/UnitTests/constraints.jl b/src/Test/UnitTests/constraints.jl index 5b74b5931f..87fa550556 100644 --- a/src/Test/UnitTests/constraints.jl +++ b/src/Test/UnitTests/constraints.jl @@ -1,9 +1,9 @@ """ - getconstraint(model::MOI.ModelLike, config::Config) + test_get_ConstraintIndex(model::MOI.ModelLike, config::Config) Test getting constraints by name. """ -function getconstraint(model::MOI.ModelLike, config::Config) +function test_get_ConstraintIndex(model::MOI.ModelLike, ::Config) MOI.empty!(model) MOIU.loadfromstring!( model, @@ -29,17 +29,30 @@ function getconstraint(model::MOI.ModelLike, config::Config) c2 = MOI.get(model, MOI.ConstraintIndex{F,MOI.LessThan{Float64}}, "c2") @test MOI.get(model, MOI.ConstraintIndex, "c2") == c2 @test MOI.is_valid(model, c2) + return end -unittests["getconstraint"] = getconstraint """ - solve_affine_lessthan(model::MOI.ModelLike, config::Config) + test_constraint_ScalarAffineFunction_LessThan( + model::MOI.ModelLike, + config::Config, + ) Add an ScalarAffineFunction-in-LessThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_affine_lessthan(model::MOI.ModelLike, config::Config) +function test_constraint_ScalarAffineFunction_LessThan( + model::MOI.ModelLike, + config::Config, +) + if !MOI.supports_constraint( + model, + MOI.ScalarAffineFunction{Float64}, + MOI.LessThan{Float64}, + ) + return + end MOI.empty!(model) MOIU.loadfromstring!( model, @@ -58,7 +71,7 @@ function solve_affine_lessthan(model::MOI.ModelLike, config::Config) }, "c", ) - return _test_model_solution( + _test_model_solution( model, config; objective_value = 0.5, @@ -66,17 +79,48 @@ function solve_affine_lessthan(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 1.0)], constraint_dual = [(c, -0.5)], ) + return +end + +function setup_test( + ::typeof(test_constraint_ScalarAffineFunction_LessThan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], + ), + ) + return end -unittests["solve_affine_lessthan"] = solve_affine_lessthan """ - solve_affine_greaterthan(model::MOI.ModelLike, config::Config) + test_constraint_ScalarAffineFunction_GreaterThan( + model::MOI.ModelLike, + config::Config, + ) Add an ScalarAffineFunction-in-GreaterThan constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_affine_greaterthan(model::MOI.ModelLike, config::Config) +function test_constraint_ScalarAffineFunction_GreaterThan( + model::MOI.ModelLike, + config::Config, +) + if !MOI.supports_constraint( + model, + MOI.ScalarAffineFunction{Float64}, + MOI.GreaterThan{Float64}, + ) + return + end MOI.empty!(model) MOIU.loadfromstring!( model, @@ -95,7 +139,7 @@ function solve_affine_greaterthan(model::MOI.ModelLike, config::Config) }, "c", ) - return _test_model_solution( + _test_model_solution( model, config; objective_value = 0.5, @@ -103,17 +147,48 @@ function solve_affine_greaterthan(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 1.0)], constraint_dual = [(c, 0.5)], ) + return +end + +function setup_test( + ::typeof(test_constraint_ScalarAffineFunction_GreaterThan), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [0.5], + ), + ) + return end -unittests["solve_affine_greaterthan"] = solve_affine_greaterthan """ - solve_affine_equalto(model::MOI.ModelLike, config::Config) + test_constraint_ScalarAffineFunction_EqualTo( + model::MOI.ModelLike, + config::Config, + ) Add an ScalarAffineFunction-in-EqualTo constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_affine_equalto(model::MOI.ModelLike, config::Config) +function test_constraint_ScalarAffineFunction_EqualTo( + model::MOI.ModelLike, + config::Config, +) + if !MOI.supports_constraint( + model, + MOI.ScalarAffineFunction{Float64}, + MOI.EqualTo{Float64}, + ) + return + end MOI.empty!(model) MOIU.loadfromstring!( model, @@ -132,7 +207,7 @@ function solve_affine_equalto(model::MOI.ModelLike, config::Config) }, "c", ) - return _test_model_solution( + _test_model_solution( model, config; objective_value = 0.5, @@ -140,17 +215,49 @@ function solve_affine_equalto(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 1.0)], constraint_dual = [(c, 0.5)], ) + return +end + +function setup_test( + ::typeof(test_constraint_ScalarAffineFunction_EqualTo), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [0.5], + ), + ) + return end -unittests["solve_affine_equalto"] = solve_affine_equalto """ - solve_affine_interval(model::MOI.ModelLike, config::Config) + test_constraint_ScalarAffineFunction_Interval( + model::MOI.ModelLike, + config::Config, + ) Add an ScalarAffineFunction-in-Interval constraint. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_affine_interval(model::MOI.ModelLike, config::Config) +function test_constraint_ScalarAffineFunction_Interval( + model::MOI.ModelLike, + config::Config, +) + if !MOI.supports_constraint( + model, + MOI.ScalarAffineFunction{Float64}, + MOI.Interval{Float64}, + ) + return + end MOI.empty!(model) MOIU.loadfromstring!( model, @@ -169,7 +276,7 @@ function solve_affine_interval(model::MOI.ModelLike, config::Config) }, "c", ) - return _test_model_solution( + _test_model_solution( model, config; objective_value = 6.0, @@ -177,19 +284,40 @@ function solve_affine_interval(model::MOI.ModelLike, config::Config) constraint_primal = [(c, 4.0)], constraint_dual = [(c, -1.5)], ) + return +end + +function setup_test( + ::typeof(test_constraint_ScalarAffineFunction_Interval), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [2.0]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.5], + ), + ) + return end -unittests["solve_affine_interval"] = solve_affine_interval -# Taken from https://github.com/JuliaOpt/MathOptInterfaceMosek.jl/issues/41 """ - solve_duplicate_terms_scalar_affine(model::MOI.ModelLike, - config::Config) + test_constraint_ScalarAffineFunction_duplicate( + model::MOI.ModelLike, + config::Config, + ) Add a `ScalarAffineFunction`-in-`LessThan` constraint with duplicate terms in the function. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. + +Taken from https://github.com/JuliaOpt/MathOptInterfaceMosek.jl/issues/41 """ -function solve_duplicate_terms_scalar_affine( +function test_constraint_ScalarAffineFunction_duplicate( model::MOI.ModelLike, config::Config, ) @@ -205,7 +333,7 @@ function solve_duplicate_terms_scalar_affine( MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 1.0], [x, x]), 0.0) c = MOI.add_constraint(model, f, MOI.LessThan(1.0)) - return _test_model_solution( + _test_model_solution( model, config; objective_value = 0.5, @@ -213,19 +341,38 @@ function solve_duplicate_terms_scalar_affine( constraint_primal = [(c, 1.0)], constraint_dual = [(c, -0.5)], ) + return +end + +function setup_test( + ::typeof(test_constraint_ScalarAffineFunction_duplicate), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5]), + MOI.FEASIBLE_POINT, + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], + ), + ) + return end -unittests["solve_duplicate_terms_scalar_affine"] = - solve_duplicate_terms_scalar_affine """ - solve_duplicate_terms_vector_affine(model::MOI.ModelLike, - config::Config) + test_constraint_VectorAffineFunction_duplicate( + model::MOI.ModelLike, + config::Config, + ) Add a `VectorAffineFunction`-in-`Nonpositives` constraint with duplicate terms in the function. If `config.solve=true` confirm that it solves correctly, and if `config.duals=true`, check that the duals are computed correctly. """ -function solve_duplicate_terms_vector_affine( +function test_constraint_VectorAffineFunction_duplicate( model::MOI.ModelLike, config::Config, ) @@ -244,7 +391,7 @@ function solve_duplicate_terms_vector_affine( [-1.0], ) c = MOI.add_constraint(model, f, MOI.Nonpositives(1)) - return _test_model_solution( + _test_model_solution( model, config; objective_value = 0.5, @@ -252,19 +399,36 @@ function solve_duplicate_terms_vector_affine( constraint_primal = [(c, [0.0])], constraint_dual = [(c, [-0.5])], ) + return +end + +function setup_test( + ::typeof(test_constraint_VectorAffineFunction_duplicate), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5]), + MOI.FEASIBLE_POINT, + (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => + [[-0.5]], + ), + ) + return end -unittests["solve_duplicate_terms_vector_affine"] = - solve_duplicate_terms_vector_affine """ - solve_qcp_edge_cases(model::MOI.ModelLike, config::Config) + test_qcp_duplicate_diagonal(model::MOI.ModelLike, config::Config) -Test various edge cases relating to quadratically constrainted programs (i.e., -with a ScalarQuadraticFunction-in-Set constraint. +Test a QCP problem with a duplicate diagonal term. -If `config.solve=true` confirm that it solves correctly. +The problem is `max x + 2y | y + x^2 + x^2 <= 1, x >= 0.5, y >= 0.5`. """ -function solve_qcp_edge_cases(model::MOI.ModelLike, config::Config) +function test_qcp_duplicate_diagonal(model::MOI.ModelLike, config::Config) if !MOI.supports_constraint( model, MOI.ScalarQuadraticFunction{Float64}, @@ -272,97 +436,145 @@ function solve_qcp_edge_cases(model::MOI.ModelLike, config::Config) ) return end - @testset "Duplicate on-diagonal" begin - # max x + 2y | y + x^2 + x^2 <= 1, x >= 0.5, y >= 0.5 - MOI.empty!(model) - x = MOI.add_variables(model, 2) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], x), 0.0), - ) - vc1 = MOI.add_constraint( - model, - MOI.SingleVariable(x[1]), - MOI.GreaterThan(0.5), - ) - # We test this after the creation of every `SingleVariable` constraint - # to ensure a good coverage of corner cases. - @test vc1.value == x[1].value - vc2 = MOI.add_constraint( - model, - MOI.SingleVariable(x[2]), - MOI.GreaterThan(0.5), - ) - @test vc2.value == x[2].value - MOI.add_constraint( - model, - MOI.ScalarQuadraticFunction( - MOI.ScalarAffineTerm.([1.0], [x[2]]), # affine terms - MOI.ScalarQuadraticTerm.( - [2.0, 2.0], - [x[1], x[1]], - [x[1], x[1]], - ), # quad - 0.0, # constant - ), - MOI.LessThan(1.0), - ) - _test_model_solution( - model, - config; - objective_value = 1.5, - variable_primal = [(x[1], 0.5), (x[2], 0.5)], - ) - end - @testset "Duplicate off-diagonal" begin - # max x + 2y | x^2 + 0.25y*x + 0.25x*y + 0.5x*y + y^2 <= 1, x >= 0.5, y >= 0.5 - MOI.empty!(model) - x = MOI.add_variables(model, 2) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], x), 0.0), - ) - vc1 = MOI.add_constraint( - model, - MOI.SingleVariable(x[1]), - MOI.GreaterThan{Float64}(0.5), - ) - @test vc1.value == x[1].value - vc2 = MOI.add_constraint( - model, - MOI.SingleVariable(x[2]), - MOI.GreaterThan{Float64}(0.5), - ) - @test vc2.value == x[2].value - MOI.add_constraint( - model, - MOI.ScalarQuadraticFunction( - MOI.ScalarAffineTerm{Float64}[], # affine terms - MOI.ScalarQuadraticTerm.( - [2.0, 0.25, 0.25, 0.5, 2.0], - [x[1], x[1], x[2], x[1], x[2]], - [x[1], x[2], x[1], x[2], x[2]], - ), # quad - 0.0, # constant - ), - MOI.LessThan(1.0), - ) - _test_model_solution( - model, - config; - objective_value = 0.5 + (√13 - 1) / 2, - variable_primal = [(x[1], 0.5), (x[2], (√13 - 1) / 4)], - ) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], x), 0.0), + ) + vc1 = MOI.add_constraint( + model, + MOI.SingleVariable(x[1]), + MOI.GreaterThan(0.5), + ) + # We test this after the creation of every `SingleVariable` constraint + # to ensure a good coverage of corner cases. + @test vc1.value == x[1].value + vc2 = MOI.add_constraint( + model, + MOI.SingleVariable(x[2]), + MOI.GreaterThan(0.5), + ) + @test vc2.value == x[2].value + MOI.add_constraint( + model, + MOI.ScalarQuadraticFunction( + MOI.ScalarAffineTerm.([1.0], [x[2]]), # affine terms + MOI.ScalarQuadraticTerm.( + [2.0, 2.0], + [x[1], x[1]], + [x[1], x[1]], + ), # quad + 0.0, # constant + ), + MOI.LessThan(1.0), + ) + _test_model_solution( + model, + config; + objective_value = 1.5, + variable_primal = [(x[1], 0.5), (x[2], 0.5)], + ) + return +end + +function setup_test( + ::typeof(test_qcp_duplicate_diagonal), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5, 0.5]), + ), + ) + return +end + +""" + test_qcp_duplicate_off_diagonal(model::MOI.ModelLike, config::Config) + +Test a QCP problem with a duplicate off-diagonal term. + +The problem is +`max x + 2y | x^2 + 0.25y*x + 0.25x*y + 0.5x*y + y^2 <= 1, x >= 0.5, y >= 0.5`. +""" +function test_qcp_duplicate_off_diagonal(model::MOI.ModelLike, config::Config) + if !MOI.supports_constraint( + model, + MOI.ScalarQuadraticFunction{Float64}, + MOI.LessThan{Float64}, + ) + return end + MOI.empty!(model) + x = MOI.add_variables(model, 2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], x), 0.0), + ) + vc1 = MOI.add_constraint( + model, + MOI.SingleVariable(x[1]), + MOI.GreaterThan{Float64}(0.5), + ) + @test vc1.value == x[1].value + vc2 = MOI.add_constraint( + model, + MOI.SingleVariable(x[2]), + MOI.GreaterThan{Float64}(0.5), + ) + @test vc2.value == x[2].value + MOI.add_constraint( + model, + MOI.ScalarQuadraticFunction( + MOI.ScalarAffineTerm{Float64}[], # affine terms + MOI.ScalarQuadraticTerm.( + [2.0, 0.25, 0.25, 0.5, 2.0], + [x[1], x[1], x[2], x[1], x[2]], + [x[1], x[2], x[1], x[2], x[2]], + ), # quad + 0.0, # constant + ), + MOI.LessThan(1.0), + ) + _test_model_solution( + model, + config; + objective_value = 0.5 + (√13 - 1) / 2, + variable_primal = [(x[1], 0.5), (x[2], (√13 - 1) / 4)], + ) + return +end + +function setup_test( + ::typeof(test_qcp_duplicate_off_diagonal), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.5, (√13 - 1) / 4]), + ), + ) + return end -unittests["solve_qcp_edge_cases"] = solve_qcp_edge_cases """ - solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::Config) + test_modification_affine_deletion_edge_cases( + model::MOI.ModelLike, + config::Config, + ) Test various edge cases relating to deleting affine constraints. This requires + ScalarAffineFunction-in-LessThan; and @@ -370,7 +582,10 @@ Test various edge cases relating to deleting affine constraints. This requires If `config.solve=true` confirm that it solves correctly. """ -function solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::Config) +function test_modification_affine_deletion_edge_cases( + model::MOI.ModelLike, + config::Config, +) MOI.empty!(model) x = MOI.add_variable(model) # helpers. The function 1.0x + 0.0 @@ -430,16 +645,60 @@ function solve_affine_deletion_edge_cases(model::MOI.ModelLike, config::Config) ) # delete the ScalarAffineFunction MOI.delete(model, c2) - return _test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, constraint_primal = [(c3, [0.0])], ) + return +end + +function setup_test( + ::typeof(test_modification_affine_deletion_edge_cases), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [0.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + ), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [2.0]), + ), + ) + return end -unittests["solve_affine_deletion_edge_cases"] = solve_affine_deletion_edge_cases -function solve_zero_one_with_bounds_1(model::MOI.ModelLike, config::Config) +""" + test_constraint_ZeroOne_bounds(model::MOI.ModelLike, config::Config) + +Test a problem with a bounded ZeroOne variable. +""" +function test_constraint_ZeroOne_bounds(model::MOI.ModelLike, config::Config) + if !MOI.supports_constraint(model, MOI.SingleVariable, MOI.ZeroOne) + return + end MOI.empty!(model) MOIU.loadfromstring!( model, @@ -452,16 +711,37 @@ function solve_zero_one_with_bounds_1(model::MOI.ModelLike, config::Config) """, ) x = MOI.get(model, MOI.VariableIndex, "x") - return _test_model_solution( + _test_model_solution( model, config; objective_value = 2.0, variable_primal = [(x, 1.0)], ) + return +end + +function setup_test( + ::typeof(test_constraint_ZeroOne_bounds), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + ) + return end -unittests["solve_zero_one_with_bounds_1"] = solve_zero_one_with_bounds_1 -function solve_zero_one_with_bounds_2(model::MOI.ModelLike, config::Config) +""" + test_constraint_ZeroOne_bounds_2(model::MOI.ModelLike, config::Config) + +Test a problem with a ZeroOne and binding fractional upper bound. +""" +function test_constraint_ZeroOne_bounds_2(model::MOI.ModelLike, config::Config) + if !MOI.supports_constraint(model, MOI.SingleVariable, MOI.ZeroOne) + return + end MOI.empty!(model) MOIU.loadfromstring!( model, @@ -474,16 +754,37 @@ function solve_zero_one_with_bounds_2(model::MOI.ModelLike, config::Config) """, ) x = MOI.get(model, MOI.VariableIndex, "x") - return _test_model_solution( + _test_model_solution( model, config; objective_value = 0.0, variable_primal = [(x, 0.0)], ) + return +end + +function setup_test( + ::typeof(test_constraint_ZeroOne_bounds_2), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])), + ) + return end -unittests["solve_zero_one_with_bounds_2"] = solve_zero_one_with_bounds_2 -function solve_zero_one_with_bounds_3(model::MOI.ModelLike, config::Config) +""" + test_constraint_ZeroOne_bounds_3(model::MOI.ModelLike, config::Config) + +Test a problem with a ZeroOne and infeasible fractional bounds. +""" +function test_constraint_ZeroOne_bounds_3(model::MOI.ModelLike, config::Config) + if !MOI.supports_constraint(model, MOI.SingleVariable, MOI.ZeroOne) + return + end MOI.empty!(model) MOIU.loadfromstring!( model, @@ -500,23 +801,44 @@ function solve_zero_one_with_bounds_3(model::MOI.ModelLike, config::Config) MOI.optimize!(model) @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE end + return +end + +function setup_test( + ::typeof(test_constraint_ZeroOne_bounds_3), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.INFEASIBLE), + ) + return end -unittests["solve_zero_one_with_bounds_3"] = solve_zero_one_with_bounds_3 """ - solve_start_soc(model::MOI.ModelLike, config::Config{T}) where {T} + test_constraint_PrimalStart_DualStart_SecondOrderCone( + model::MOI.ModelLike, + config::Config{T}, + ) where {T} Test combining the [`MOI.VariablePrimalStart`](@ref), [`MOI.ConstraintPrimalStart`](@ref) and [`MOI.ConstraintDualStart`](@ref) attributes with a `MOI.VectorAffineFunction{T}`-in-`MOI.SecondOrderCone`. """ -function solve_start_soc(model::MOI.ModelLike, config::Config{T}) where {T} +function test_constraint_PrimalStart_DualStart_SecondOrderCone( + model::MOI.ModelLike, + config::Config{T}, +) where {T} if !MOI.supports_constraint( model, MOI.VectorAffineFunction{T}, MOI.SecondOrderCone, ) return + elseif !config.solve + return end MOI.empty!(model) x = MOI.add_variable(model) @@ -538,17 +860,33 @@ function solve_start_soc(model::MOI.ModelLike, config::Config{T}) where {T} if MOI.supports(model, MOI.ConstraintDualStart(), typeof(c)) MOI.set(model, MOI.ConstraintDualStart(), c, T[2, -2]) end - if config.solve - MOI.optimize!(model) - MOI.Test._test_model_solution( - model, - config; - objective_value = o, - variable_primal = [(x, o)], - constraint_primal = [(c, [o, o])], - constraint_dual = [(c, [o, -o])], - ) - end + MOI.optimize!(model) + MOI.Test._test_model_solution( + model, + config; + objective_value = o, + variable_primal = [(x, o)], + constraint_primal = [(c, [o, o])], + constraint_dual = [(c, [o, -o])], + ) + return +end + +function setup_test( + ::typeof(test_constraint_PrimalStart_DualStart_SecondOrderCone), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0]), + MOI.FEASIBLE_POINT, + (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => + [[1.0, -1.0]], + ), + ) return end -unittests["solve_start_soc"] = solve_start_soc diff --git a/test/Bridges/Constraint/quad_to_soc.jl b/test/Bridges/Constraint/quad_to_soc.jl index fe3051d6b5..13b7827f94 100644 --- a/test/Bridges/Constraint/quad_to_soc.jl +++ b/test/Bridges/Constraint/quad_to_soc.jl @@ -52,7 +52,8 @@ config = MOIT.Config() (MOI.FEASIBLE_POINT, [0.5, (√13 - 1) / 4]), ), ) - MOIT.solve_qcp_edge_cases(bridged_mock, config) + MOIT.test_qcp_duplicate_diagonal(bridged_mock, config) + MOIT.test_qcp_duplicate_off_diagonal(bridged_mock, config) ci = first( MOI.get( mock, diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index bf5a5529f2..c6ef2abba2 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -332,13 +332,21 @@ end @testset "Unit" begin model = LPModel{Float64}() bridged = MOIB.full_bridge_optimizer(model, Float64) - MOIT.unittest( + MOI.Test.runtests( bridged, MOIT.Config(solve = false), - [ - # SOC and quadratic constraints not supported - "solve_qcp_edge_cases", - "delete_soc_variables", + # TODO(odow): we should reconsider how we test all this. + exclude = [ + "_quadratic_", + "_qcp_", + "_socp_", + "_soc_", + "_SOS1_", + "_SOS2_", + "_Indicator_", + "_nonlinear_", + "_SecondOrderCone", + "_VariablePrimalStart", ], ) end diff --git a/test/Test/Test.jl b/test/Test/Test.jl index ed6e32e1ce..9127261d6b 100644 --- a/test/Test/Test.jl +++ b/test/Test/Test.jl @@ -11,6 +11,11 @@ const MOIU = MOI.Utilities include(file) end +MOI.Test.basic_constraint_tests( + MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), + MOI.Test.Config(), +) + MOI.Test.runtests( MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), MOI.Test.Config(basis = true), @@ -25,3 +30,4 @@ MOI.Test.runtests( MOI.Test.Config(optimal_status = MOI.LOCALLY_SOLVED), include = ["test_nonlinear_"], ) + diff --git a/test/Test/unit.jl b/test/Test/unit.jl deleted file mode 100644 index aac1d54dbf..0000000000 --- a/test/Test/unit.jl +++ /dev/null @@ -1,223 +0,0 @@ -using Test -import MathOptInterface -const MOI = MathOptInterface -const MOIT = MOI.Test -const MOIU = MOI.Utilities - -@testset "Basic Constraint Tests" begin - mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) - config = MOIT.Config() - MOIT.basic_constraint_tests(mock, config) -end - -@testset "Unit Tests" begin - # `UniversalFallback` needed for `MOI.Silent` - mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) - config = MOIT.Config() - for model in [ - mock, - MOIU.CachingOptimizer( - MOIU.UniversalFallback(MOIU.Model{Float64}()), - mock, - ), - ] - MOIT.unittest( - model, - config, - [ - "test_ObjectiveFunction_blank", - "test_ObjectiveFunction_SingleVariable", - "solve_affine_lessthan", - "solve_affine_greaterthan", - "solve_affine_equalto", - "solve_affine_interval", - "solve_duplicate_terms_scalar_affine", - "solve_duplicate_terms_vector_affine", - "test_qp_ObjectiveFunction_edge_cases", - "test_qp_ObjectiveFunction_zero_ofdiag", - "solve_qcp_edge_cases", - "solve_affine_deletion_edge_cases", - "test_ObjectiveFunction_duplicate_terms", - "test_ObjectiveBound_edge_cases", - "solve_zero_one_with_bounds_1", - "solve_zero_one_with_bounds_2", - "solve_zero_one_with_bounds_3", - "solve_start_soc", - "test_TerminationStatus_DUAL_INFEASIBLE", - "test_SingleVariable_ConstraintDual_MIN_SENSE", - "test_SingleVariable_ConstraintDual_MAX_SENSE", - "test_result_index", - "test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_lower", - "test_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper", - "test_DualStatus_INFEASIBILITY_CERTIFICATE_LessThan", - "test_DualStatus_INFEASIBILITY_CERTIFICATE_GreaterThan", - "test_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_lower", - "test_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_upper", - "test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan", - "test_DualStatus_INFEASIBILITY_CERTIFICATE_SingleVariable_LessThan_max", - "test_optimize_twice", - ], - ) - MOI.empty!(model) - end - @testset "solve_affine_lessthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], - ), - ) - MOIT.solve_affine_lessthan(mock, config) - end - @testset "solve_affine_greaterthan" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [0.5], - ), - ) - MOIT.solve_affine_greaterthan(mock, config) - end - @testset "solve_affine_equalto" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [0.5], - ), - ) - MOIT.solve_affine_equalto(mock, config) - end - @testset "solve_affine_interval" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.5], - ), - ) - MOIT.solve_affine_interval(mock, config) - end - @testset "solve_duplicate_terms_scalar_affine" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], - ), - ) - MOIT.solve_duplicate_terms_scalar_affine(mock, config) - end - @testset "solve_duplicate_terms_vector_affine" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5]), - MOI.FEASIBLE_POINT, - (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => - [[-0.5]], - ), - ) - MOIT.solve_duplicate_terms_vector_affine(mock, config) - end - - @testset "solve_qcp_edge_cases" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5, 0.5]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.5, (√13 - 1) / 4]), - ), - ) - MOIT.solve_qcp_edge_cases(mock, config) - end - @testset "solve_affine_deletion_edge_cases" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - ), - ) - MOIT.solve_affine_deletion_edge_cases(mock, config) - end - @testset "solve_zero_one_with_bounds" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> begin - MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])) - end, - (mock::MOIU.MockOptimizer) -> begin - MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])) - end, - (mock::MOIU.MockOptimizer) -> begin - MOIU.mock_optimize!(mock, MOI.INFEASIBLE) - end, - ) - MOIT.solve_zero_one_with_bounds_1(mock, config) - MOIT.solve_zero_one_with_bounds_2(mock, config) - MOIT.solve_zero_one_with_bounds_3(mock, config) - end - - @testset "solve_start_soc" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => - [[1.0, -1.0]], - ), - ) - MOIT.solve_start_soc(mock, config) - end -end diff --git a/test/Utilities/cachingoptimizer.jl b/test/Utilities/cachingoptimizer.jl index 3da83d9ee4..290aef466b 100644 --- a/test/Utilities/cachingoptimizer.jl +++ b/test/Utilities/cachingoptimizer.jl @@ -566,20 +566,15 @@ for state in (MOIU.NO_OPTIMIZER, MOIU.EMPTY_OPTIMIZER, MOIU.ATTACHED_OPTIMIZER) end config = MOIT.Config(solve = false) - @testset "Unit" begin - MOIT.unittest(m, config) - end - @testset "Continuous Linear" begin - MOI.Test.runtests( - m, - config, - include = ["test_linear_"], - exclude = [ - "VariablePrimalStart", - "linear_mixed_complementarity", - ], - ) - end + MOI.Test.runtests( + m, + config, + include = ["test_linear_"], + exclude = [ + "VariablePrimalStart", + "linear_mixed_complementarity", + ], + ) end end diff --git a/test/Utilities/universalfallback.jl b/test/Utilities/universalfallback.jl index 62638e63c4..b49913a891 100644 --- a/test/Utilities/universalfallback.jl +++ b/test/Utilities/universalfallback.jl @@ -285,16 +285,9 @@ end @test MOI.get(uf, typeof(cx), "EqualTo") === nothing end end -config = MOIT.Config(solve = false) -@testset "empty" begin - MOI.empty!(uf) - @test MOI.is_empty(uf) -end -@testset "Unit" begin - MOIT.unittest(uf, config) -end @testset "MOI.Test.runtests" begin + config = MOIT.Config(solve = false) MOI.Test.runtests(uf, config, exclude = ["_complementarity"]) end From aede960b61550921e170c342df7f2042bb89f366 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 14:09:06 +1200 Subject: [PATCH 20/23] Fix formatting --- src/Test/UnitTests/constraints.jl | 60 +++++++++++------------------- test/Test/Test.jl | 1 - test/Utilities/cachingoptimizer.jl | 5 +-- 3 files changed, 23 insertions(+), 43 deletions(-) diff --git a/src/Test/UnitTests/constraints.jl b/src/Test/UnitTests/constraints.jl index 87fa550556..8bedea5c9b 100644 --- a/src/Test/UnitTests/constraints.jl +++ b/src/Test/UnitTests/constraints.jl @@ -94,7 +94,8 @@ function setup_test( MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.5]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-0.5], ), ) return @@ -162,7 +163,8 @@ function setup_test( MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.5]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [0.5], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [0.5], ), ) return @@ -299,7 +301,8 @@ function setup_test( MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [2.0]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1.5], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => + [-1.5], ), ) return @@ -356,7 +359,8 @@ function setup_test( MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.5]), MOI.FEASIBLE_POINT, - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-0.5], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-0.5], ), ) return @@ -462,11 +466,7 @@ function test_qcp_duplicate_diagonal(model::MOI.ModelLike, config::Config) model, MOI.ScalarQuadraticFunction( MOI.ScalarAffineTerm.([1.0], [x[2]]), # affine terms - MOI.ScalarQuadraticTerm.( - [2.0, 2.0], - [x[1], x[1]], - [x[1], x[1]], - ), # quad + MOI.ScalarQuadraticTerm.([2.0, 2.0], [x[1], x[1]], [x[1], x[1]]), # quad 0.0, # constant ), MOI.LessThan(1.0), @@ -661,31 +661,16 @@ function setup_test( ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [0.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [2.0]), - ), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + (mock::MOIU.MockOptimizer) -> + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [2.0])), ) return end @@ -728,7 +713,7 @@ function setup_test( MOIU.set_mock_optimize!( model, (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0])), ) return end @@ -771,7 +756,7 @@ function setup_test( MOIU.set_mock_optimize!( model, (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])), + MOIU.mock_optimize!(mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.0])), ) return end @@ -811,8 +796,7 @@ function setup_test( ) MOIU.set_mock_optimize!( model, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.INFEASIBLE), + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.INFEASIBLE), ) return end diff --git a/test/Test/Test.jl b/test/Test/Test.jl index 9127261d6b..660a850ea6 100644 --- a/test/Test/Test.jl +++ b/test/Test/Test.jl @@ -30,4 +30,3 @@ MOI.Test.runtests( MOI.Test.Config(optimal_status = MOI.LOCALLY_SOLVED), include = ["test_nonlinear_"], ) - diff --git a/test/Utilities/cachingoptimizer.jl b/test/Utilities/cachingoptimizer.jl index 290aef466b..7c13cbcbb3 100644 --- a/test/Utilities/cachingoptimizer.jl +++ b/test/Utilities/cachingoptimizer.jl @@ -570,10 +570,7 @@ for state in (MOIU.NO_OPTIMIZER, MOIU.EMPTY_OPTIMIZER, MOIU.ATTACHED_OPTIMIZER) m, config, include = ["test_linear_"], - exclude = [ - "VariablePrimalStart", - "linear_mixed_complementarity", - ], + exclude = ["VariablePrimalStart", "linear_mixed_complementarity"], ) end end From 1a158234e67b86aedae673ed2c240cd845d678e0 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 19:20:16 +1200 Subject: [PATCH 21/23] WIP: migrate contconic.jl --- src/Test/contconic.jl | 2036 ++++++++++++++--- test/Bridges/Constraint/det.jl | 24 +- test/Bridges/Constraint/flip_sign.jl | 2 +- test/Bridges/Constraint/functionize.jl | 2 +- test/Bridges/Constraint/interval.jl | 2 +- .../Constraint/norm_spec_nuc_to_psd.jl | 4 +- test/Bridges/Constraint/norm_to_lp.jl | 16 +- test/Bridges/Constraint/relentr_to_exp.jl | 2 +- test/Bridges/Constraint/scalarize.jl | 8 +- test/Bridges/Variable/flip_sign.jl | 4 +- test/Test/Test.jl | 38 +- test/Test/contconic.jl | 367 +-- test/Utilities/matrix_of_constraints.jl | 6 +- test/Utilities/model.jl | 7 +- 14 files changed, 1721 insertions(+), 797 deletions(-) diff --git a/src/Test/contconic.jl b/src/Test/contconic.jl index 0f395532a4..2d6aa2d897 100644 --- a/src/Test/contconic.jl +++ b/src/Test/contconic.jl @@ -1,22 +1,35 @@ -# Continuous conic problems -using LinearAlgebra # for dot +""" + _test_conic_linear_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, + ) + +A helper function for writing other conic tests. -function _lin1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) +Constructs the problem: +``` +min -3x - 2y - 4z +st x + y + z == 3 + y + z == 2 + x>=0 y>=0 z>=0 +Opt obj = -11, soln x = 1, y = 0, z = 2 +``` +""" +function _test_conic_linear_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, +) atol = config.atol rtol = config.rtol - # linear conic problem - # min -3x - 2y - 4z - # st x + y + z == 3 - # y + z == 2 - # x>=0 y>=0 z>=0 - # Opt obj = -11, soln x = 1, y = 0, z = 2 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -39,7 +52,7 @@ function _lin1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) v = MOI.add_variables(model, 3) @test MOI.get(model, MOI.NumberOfVariables()) == 3 vov = MOI.VectorOfVariables(v) - if vecofvars + if use_VectorOfVariables vc = MOI.add_constraint(model, vov, MOI.Nonnegatives(3)) else vc = MOI.add_constraint( @@ -63,7 +76,7 @@ function _lin1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, }(), @@ -79,7 +92,8 @@ function _lin1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) loc = MOI.get(model, MOI.ListOfConstraintTypesPresent()) @test length(loc) == 2 @test ( - vecofvars ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, + use_VectorOfVariables ? MOI.VectorOfVariables : + MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, ) in loc @test (MOI.VectorAffineFunction{Float64}, MOI.Zeros) in loc @@ -118,42 +132,106 @@ function _lin1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) rtol end end + return end -function lin1vtest(model::MOI.ModelLike, config::Config) - return _lin1test(model, config, true) +""" + test_conic_linear_VectorOfVariables(model::MOI.ModelLike, config::Config) + +Test a conic formulation of a linear program using standard conic form. +""" +function test_conic_linear_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_conic_linear_helper(model, config, true) + return end -function lin1ftest(model::MOI.ModelLike, config::Config) - return _lin1test(model, config, false) + +function setup_test( + ::typeof(test_conic_linear_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 0.0, 2.0], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-3, -1]], + ), + ) + return end -function _lin2test(model::MOI.ModelLike, config::Config, vecofvars::Bool) +""" + test_conic_linear_VectorAffineFunction(model::MOI.ModelLike, config::Config) + +Test a conic formulation of a linear program using geometric conic form. +""" +function test_conic_linear_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_conic_linear_helper(model, config, false) + return +end + +function setup_test( + ::typeof(test_conic_linear_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 0.0, 2.0], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[0, 2, 0]], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-3, -1]], + ), + ) + return +end + +""" + _test_conic_linear_helper_2( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, + ) + +Another helper for linear conic problems. + +Builds the problem: +``` +min 3x + 2y - 4z + 0s +st x - s == -4 (i.e. x >= -4) + y == -3 + x + z == 12 + x free + y <= 0 + z >= 0 + s zero +Opt solution = -82 +x = -4, y = -3, z = 16, s == 0 +``` +""" +function _test_conic_linear_helper_2( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, +) atol = config.atol rtol = config.rtol - #@test MOI.supportsproblem(model, MOI.ScalarAffineFunction{Float64}, - #[ - # (MOI.VectorAffineFunction{Float64},MOI.Zeros), - # (MOI.VectorOfVariables,MOI.Nonnegatives), - # (MOI.VectorOfVariables,MOI.Nonpositives) - #]) - # mixed cones - # min 3x + 2y - 4z + 0s - # st x - s == -4 (i.e. x >= -4) - # y == -3 - # x + z == 12 - # x free - # y <= 0 - # z >= 0 - # s zero - # Opt solution = -82 - # x = -4, y = -3, z = 16, s == 0 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -182,7 +260,7 @@ function _lin2test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test MOI.is_empty(model) x = MOI.add_variable(model) @test MOI.get(model, MOI.NumberOfVariables()) == 1 - if vecofvars + if use_VectorOfVariables ys, vc = MOI.add_constrained_variables(model, MOI.Nonpositives(1)) y = ys[1] else @@ -216,7 +294,7 @@ function _lin2test(model::MOI.ModelLike, config::Config, vecofvars::Bool) ), MOI.Zeros(3), ) - if vecofvars + if use_VectorOfVariables # test fallback vz = MOI.add_constraint(model, [z], MOI.Nonnegatives(1)) else @@ -230,7 +308,7 @@ function _lin2test(model::MOI.ModelLike, config::Config, vecofvars::Bool) ) end vov = MOI.VectorOfVariables([s]) - if vecofvars + if use_VectorOfVariables vs = MOI.add_constraint(model, vov, MOI.Zeros(1)) else vs = MOI.add_constraint( @@ -246,11 +324,11 @@ function _lin2test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.VectorAffineFunction{Float64}, MOI.Zeros, }(), - ) == 2 - vecofvars + ) == 2 - use_VectorOfVariables @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.Nonpositives, }(), @@ -258,7 +336,7 @@ function _lin2test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives, }(), @@ -304,26 +382,89 @@ function _lin2test(model::MOI.ModelLike, config::Config, vecofvars::Bool) rtol end end + return end -function lin2vtest(model::MOI.ModelLike, config::Config) - return _lin2test(model, config, true) +""" + test_conic_linear_VectorOfVariables_2( + model::MOI.ModelLike, + config::Config, + ) + +Test a linear program in standard conic form. +""" +function test_conic_linear_VectorOfVariables_2( + model::MOI.ModelLike, + config::Config, +) + _test_conic_linear_helper_2(model, config, true) + return end -function lin2ftest(model::MOI.ModelLike, config::Config) - return _lin2test(model, config, false) + +function setup_test( + ::typeof(test_conic_linear_VectorOfVariables_2), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [-4, -3, 16, 0], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[7, 2, -4]], + ), + ) + return end -function lin3test(model::MOI.ModelLike, config::Config) - atol = config.atol - rtol = config.rtol - # Problem LIN3 - Infeasible LP - # min 0 - # s.t. x ≥ 1 - # x ≤ -1 - # in conic form: - # min 0 - # s.t. -1 + x ∈ R₊ - # 1 + x ∈ R₋ +""" + test_conic_linear_VectorAffineFunction_2( + model::MOI.ModelLike, + config::Config, + ) + +Test a linear program in geometric conic form. +""" +function test_conic_linear_VectorAffineFunction_2( + model::MOI.ModelLike, + config::Config, +) + _test_conic_linear_helper_2(model, config, false) + return +end + +function setup_test( + ::typeof(test_conic_linear_VectorAffineFunction_2), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [-4, -3, 16, 0], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0]], + (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => [[0]], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => + [[7, 2, -4], [7]], + ), + ) + return +end + +""" + test_conic_linear_INFEASIBLE(model::MOI.ModelLike, config::Config) + +Test an infeasible linear program in conic form. + +The problem is: +``` +min 0 +s.t. -1 + x ∈ R₊ + 1 + x ∈ R₋ +``` +""" +function test_conic_linear_INFEASIBLE(model::MOI.ModelLike, config::Config) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -395,19 +536,39 @@ function lin3test(model::MOI.ModelLike, config::Config) end # TODO test dual feasibility and objective sign end + return end -function lin4test(model::MOI.ModelLike, config::Config) - atol = config.atol - rtol = config.rtol - # Problem LIN4 - Infeasible LP - # min 0 - # s.t. x ≥ 1 - # x ≤ 0 - # in conic form: - # min 0 - # s.t. -1 + x ∈ R₊ - # x ∈ R₋ +function setup_test( + ::typeof(test_conic_linear_INFEASIBLE), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + MOI.INFEASIBLE_POINT, + MOI.INFEASIBILITY_CERTIFICATE, + ), + ) + return +end + +""" + test_conic_linear_INFEASIBLE_2(model::MOI.ModelLike, config::Config) + +Test an infeasible linear program in conic form. + +The problem is: +``` +min 0 +s.t. -1 + x ∈ R₊ + x ∈ R₋ +``` +""" +function test_conic_linear_INFEASIBLE_2(model::MOI.ModelLike, config::Config) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -465,20 +626,48 @@ function lin4test(model::MOI.ModelLike, config::Config) end # TODO test dual feasibility and objective sign end + return end -const lintests = Dict( - "lin1v" => lin1vtest, - "lin1f" => lin1ftest, - "lin2v" => lin2vtest, - "lin2f" => lin2ftest, - "lin3" => lin3test, - "lin4" => lin4test, +function setup_test( + ::typeof(test_conic_linear_INFEASIBLE_2), + model::MOIU.MockOptimizer, + ::Config, ) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + MOI.INFEASIBLE_POINT, + MOI.INFEASIBILITY_CERTIFICATE, + ), + ) + return +end -@moitestset lin +""" + _test_conic_NormInfinityCone_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, + ) -function _norminf1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) +A helper function for testing NormInfinityCone. +""" +function _test_conic_NormInfinityCone_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, +) + F = if use_VectorOfVariables + MOI.VectorOfVariables + else + MOI.VectorAffineFunction{Float64} + end + if !MOI.supports_constraint(model, F, MOI.NormInfinityCone) + return + end atol = config.atol rtol = config.rtol # Problem NormInf1 @@ -492,7 +681,7 @@ function _norminf1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint(model, MOI.VectorOfVariables, MOI.Zeros) else @test MOI.supports_constraint( @@ -535,7 +724,7 @@ function _norminf1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.Zeros(1), ) vov = MOI.VectorOfVariables([x, y, z]) - if vecofvars + if use_VectorOfVariables ccone = MOI.add_constraint(model, vov, MOI.NormInfinityCone(3)) else ccone = MOI.add_constraint( @@ -555,7 +744,7 @@ function _norminf1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone, }(), @@ -565,7 +754,8 @@ function _norminf1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test length(loc) == 2 @test (MOI.VectorAffineFunction{Float64}, MOI.Zeros) in loc @test ( - vecofvars ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, + use_VectorOfVariables ? MOI.VectorOfVariables : + MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone, ) in loc if config.solve @@ -602,28 +792,94 @@ function _norminf1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) atol rtol = rtol end end + return +end + +""" + test_conic_NormInfinityCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) + +Test a NormInfinityCone in standard conic form. +""" +function test_conic_NormInfinityCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_conic_NormInfinityCone_helper(model, config, true) + return +end + +function setup_test( + ::typeof(test_conic_NormInfinityCone_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 0.5, 1], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]], + (MOI.VectorOfVariables, MOI.NormInfinityCone) => [[1, 0, -1]], + ), + ) + return end -function norminf1vtest(model::MOI.ModelLike, config::Config) - return _norminf1test(model, config, true) +""" + test_conic_NormInfinityCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) + +Test a NormInfinityCone in geometric conic form. +""" +function test_conic_NormInfinityCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_conic_NormInfinityCone_helper(model, config, false) + return end -function norminf1ftest(model::MOI.ModelLike, config::Config) - return _norminf1test(model, config, false) + +function setup_test( + ::typeof(test_conic_NormInfinityCone_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 0.5, 1], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]], + (MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) => + [[1, 0, -1]], + ), + ) + return end -function norminf2test(model::MOI.ModelLike, config::Config) - 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_conic_NormInfinityCone_INFEASIBLE( + model::MOI.ModelLike, + config::Config, + ) + +Test the problem: +``` +min 0 +s.t. -2 + y ∈ R₊ + -1 + x ∈ R₋ + (x,y) ∈ NormInf₂ +``` +""" +function test_conic_NormInfinityCone_INFEASIBLE( + model::MOI.ModelLike, + config::Config, +) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -707,16 +963,40 @@ function norminf2test(model::MOI.ModelLike, config::Config) end # TODO test dual feasibility and objective sign end + return end -function norminf3test(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_conic_NormInfinityCone_INFEASIBLE), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + MOI.INFEASIBLE_POINT, + MOI.INFEASIBILITY_CERTIFICATE, + ), + ) + return +end + +""" + test_conic_NormInfinityCone_3(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +min x + st (-1 + x, 2 .+ y) in NormInf(1 + n) + (1 .+ y) in Nonnegatives(n) +let n = 3. optimal solution: y .= -1, x = 2 +``` +""" +function test_conic_NormInfinityCone_3(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # Problem NormInf3 - # min x - # st (-1 + x, 2 .+ y) in NormInf(1 + n) - # (1 .+ y) in Nonnegatives(n) - # let n = 3. optimal solution: y .= -1, x = 2 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports(model, MOI.ObjectiveFunction{MOI.SingleVariable}()) @test MOI.supports(model, MOI.ObjectiveSense()) @@ -794,18 +1074,34 @@ function norminf3test(model::MOI.ModelLike, config::Config) vcat(1, -dual_nonneg) atol = atol rtol = rtol end end + return end -const norminftests = Dict( - "norminf1v" => norminf1vtest, - "norminf1f" => norminf1ftest, - "norminf2" => norminf2test, - "norminf3" => norminf3test, +function setup_test( + ::typeof(test_conic_NormInfinityCone_3), + model::MOIU.MockOptimizer, + ::Config, ) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [2, -1, -1, -1], + (MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) => + [vcat(1, fill(-inv(3), 3))], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [fill(inv(3), 3)], + ), + ) + return +end -@moitestset norminf -function _normone1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) +function _test_conic_NormOneCone_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, +) atol = config.atol rtol = config.rtol # Problem NormOne1 @@ -819,7 +1115,7 @@ function _normone1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint(model, MOI.VectorOfVariables, MOI.Zeros) else @test MOI.supports_constraint( @@ -858,7 +1154,7 @@ function _normone1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.Zeros(1), ) vov = MOI.VectorOfVariables([x, y, z]) - if vecofvars + if use_VectorOfVariables ccone = MOI.add_constraint(model, vov, MOI.NormOneCone(3)) else ccone = MOI.add_constraint( @@ -878,7 +1174,7 @@ function _normone1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.NormOneCone, }(), @@ -888,7 +1184,8 @@ function _normone1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test length(loc) == 2 @test (MOI.VectorAffineFunction{Float64}, MOI.Zeros) in loc @test ( - vecofvars ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, + use_VectorOfVariables ? MOI.VectorOfVariables : + MOI.VectorAffineFunction{Float64}, MOI.NormOneCone, ) in loc if config.solve @@ -925,28 +1222,91 @@ function _normone1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) [1.0, -1.0, -1.0] atol = atol rtol = rtol end end + return end -function normone1vtest(model::MOI.ModelLike, config::Config) - return _normone1test(model, config, true) +""" + test_conic_NormOneCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) + +Test NormOneCone in standard conic form. +""" +function test_conic_NormOneCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + return _test_conic_NormOneCone_helper(model, config, true) end -function normone1ftest(model::MOI.ModelLike, config::Config) - return _normone1test(model, config, false) + +function setup_test( + ::typeof(test_conic_NormOneCone_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 0.5, 0.5], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [0]], + (MOI.VectorOfVariables, MOI.NormOneCone) => [[1, -1, -1]], + ), + ) + return end -function normone2test(model::MOI.ModelLike, config::Config) - 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_conic_NormOneCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) + +Test NormOneCone in geometric conic form. +""" +function test_conic_NormOneCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + return _test_conic_NormOneCone_helper(model, config, false) +end + +function setup_test( + ::typeof(test_conic_NormOneCone_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 0.5, 0.5], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [0]], + (MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) => + [[1, -1, -1]], + ), + ) + return +end + +""" + test_conic_NormOneCone_INFEASIBLE(model::MOI.ModelLike, config::Config) + +Test and infeasible problem with NormOneCone. + +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₂ +""" +function test_conic_NormOneCone_INFEASIBLE(model::MOI.ModelLike, config::Config) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -1030,16 +1390,40 @@ function normone2test(model::MOI.ModelLike, config::Config) end # TODO test dual feasibility and objective sign end + return end -function normone3test(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_conic_NormOneCone_INFEASIBLE), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + MOI.INFEASIBLE_POINT, + MOI.INFEASIBILITY_CERTIFICATE, + ), + ) + return +end + +""" + test_conic_NormOneCone(model::MOI.ModelLike, config::Config) + +Test the following problem: +``` +min x + st (-1 + x, 2 .+ y) in NormOne(1 + n) + (1 .+ y) in Nonnegatives(n) +let n = 3. optimal solution: y .= -1, x = 4 +``` +""" +function test_conic_NormOneCone(model::MOI.ModelLike, config::Config) atol = config.atol rtol = config.rtol - # Problem NormOne3 - # min x - # st (-1 + x, 2 .+ y) in NormOne(1 + n) - # (1 .+ y) in Nonnegatives(n) - # let n = 3. optimal solution: y .= -1, x = 4 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports(model, MOI.ObjectiveFunction{MOI.SingleVariable}()) @test MOI.supports(model, MOI.ObjectiveSense()) @@ -1116,18 +1500,33 @@ function normone3test(model::MOI.ModelLike, config::Config) atol rtol = rtol end end + return end -const normonetests = Dict( - "normone1v" => normone1vtest, - "normone1f" => normone1ftest, - "normone2" => normone2test, - "normone3" => normone3test, +function setup_test( + ::typeof(test_conic_NormOneCone), + model::MOIU.MockOptimizer, + ::Config, ) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [4, -1, -1, -1], + (MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) => + [vcat(1, fill(-1, 3))], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [ones(3)], + ), + ) + return +end -@moitestset normone - -function _soc1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) +function _soc1test( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, +) atol = config.atol rtol = config.rtol # Problem SOC1 @@ -1140,7 +1539,7 @@ function _soc1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_add_constrained_variables(model, MOI.SecondOrderCone) else @test MOI.supports_constraint( @@ -1152,7 +1551,7 @@ function _soc1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test MOI.supports_constraint(model, MOI.VectorOfVariables, MOI.Zeros) MOI.empty!(model) @test MOI.is_empty(model) - if vecofvars + if use_VectorOfVariables xyz, csoc = MOI.add_constrained_variables(model, MOI.SecondOrderCone(3)) x, y, z = xyz else @@ -1192,7 +1591,7 @@ function _soc1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone, }(), @@ -1202,7 +1601,8 @@ function _soc1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) @test length(loc) == 2 @test (MOI.VectorAffineFunction{Float64}, MOI.Zeros) in loc @test ( - vecofvars ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, + use_VectorOfVariables ? MOI.VectorOfVariables : + MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone, ) in loc if config.solve @@ -1853,7 +2253,7 @@ function rotatedsoc2test(model::MOI.ModelLike, config::Config) vardual = MOI.get(model, MOI.ConstraintDual(), rsoc) @test vardual ≈ -y atol = atol rtol = rtol @test 2 * vardual[1] * vardual[2] ≥ vardual[3]^2 - atol - @test dot(b, y) > atol + @test b' * y > atol end end end @@ -2124,7 +2524,12 @@ const rsoctests = Dict( @moitestset rsoc -function _geomean1test(model::MOI.ModelLike, config::Config, vecofvars, n = 3) +function _geomean1test( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables, + n = 3, +) atol = config.atol rtol = config.rtol # Problem GeoMean1 @@ -2146,7 +2551,7 @@ function _geomean1test(model::MOI.ModelLike, config::Config, vecofvars, n = 3) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -2169,7 +2574,7 @@ function _geomean1test(model::MOI.ModelLike, config::Config, vecofvars, n = 3) t = MOI.add_variable(model) x = MOI.add_variables(model, n) vov = MOI.VectorOfVariables([t; x]) - if vecofvars + if use_VectorOfVariables gmc = MOI.add_constraint(model, vov, MOI.GeometricMeanCone(n + 1)) else gmc = MOI.add_constraint( @@ -2187,7 +2592,7 @@ function _geomean1test(model::MOI.ModelLike, config::Config, vecofvars, n = 3) @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.GeometricMeanCone, }(), @@ -2237,7 +2642,11 @@ function geomean1ftest(model::MOI.ModelLike, config::Config) end # addresses bug https://github.com/jump-dev/MathOptInterface.jl/pull/962 -function _geomean2test(model::MOI.ModelLike, config::Config, vecofvars) +function _geomean2test( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables, +) atol = config.atol rtol = config.rtol # Problem GeoMean2 @@ -2251,7 +2660,7 @@ function _geomean2test(model::MOI.ModelLike, config::Config, vecofvars) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -2271,7 +2680,7 @@ function _geomean2test(model::MOI.ModelLike, config::Config, vecofvars) x = MOI.add_variables(model, n) @test MOI.get(model, MOI.NumberOfVariables()) == n + 1 vov = MOI.VectorOfVariables([t; x]) - if vecofvars + if use_VectorOfVariables gmc = MOI.add_constraint(model, vov, MOI.GeometricMeanCone(n + 1)) else gmc = MOI.add_constraint( @@ -2300,7 +2709,7 @@ function _geomean2test(model::MOI.ModelLike, config::Config, vecofvars) @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.GeometricMeanCone, }(), @@ -2350,7 +2759,11 @@ function geomean2ftest(model::MOI.ModelLike, config::Config) end # Tests case where the dimension of the geometric mean cone is 2 -function _geomean3test(model::MOI.ModelLike, config::Config, vecofvars) +function _geomean3test( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables, +) atol = config.atol rtol = config.rtol # Problem GeoMean3 @@ -2364,7 +2777,7 @@ function _geomean3test(model::MOI.ModelLike, config::Config, vecofvars) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -2383,7 +2796,7 @@ function _geomean3test(model::MOI.ModelLike, config::Config, vecofvars) x = MOI.add_variable(model) @test MOI.get(model, MOI.NumberOfVariables()) == 2 vov = MOI.VectorOfVariables([t; x]) - if vecofvars + if use_VectorOfVariables gmc = MOI.add_constraint(model, vov, MOI.GeometricMeanCone(2)) else gmc = MOI.add_constraint( @@ -2401,7 +2814,7 @@ function _geomean3test(model::MOI.ModelLike, config::Config, vecofvars) @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, MOI.GeometricMeanCone, }(), @@ -2461,7 +2874,19 @@ geomeantests = Dict( @moitestset geomean -function _exp1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) +function _test_conic_Exponential_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, +) + F = if use_VectorOfVariables + MOI.VectorOfVariables + else + MOI.VectorAffineFunction{Float64} + end + if !MOI.supports_constraint(model, F, MOI.ExponentialCone) + return + end atol = config.atol rtol = config.rtol # Problem EXP1 - ExpPrimal @@ -2475,7 +2900,7 @@ function _exp1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -2498,7 +2923,7 @@ function _exp1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) v = MOI.add_variables(model, 3) @test MOI.get(model, MOI.NumberOfVariables()) == 3 vov = MOI.VectorOfVariables(v) - if vecofvars + if use_VectorOfVariables vc = MOI.add_constraint(model, vov, MOI.ExponentialCone()) else vc = MOI.add_constraint( @@ -2556,18 +2981,90 @@ function _exp1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) atol rtol = rtol end end + return end -function exp1vtest(model::MOI.ModelLike, config::Config) - return _exp1test(model, config, true) +""" + test_conic_Exponential_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) + +Test an exponential cone in standard conic form. +""" +function test_conic_Exponential_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_conic_Exponential_helper(model, config, true) + return end -function exp1ftest(model::MOI.ModelLike, config::Config) - return _exp1test(model, config, false) + +function setup_test( + ::typeof(test_conic_Exponential_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 2.0, 2exp(1 / 2)], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [1 + exp(1 / 2), 1 + exp(1 / 2) / 2], + ), + ) + return end -function exp2test(model::MOI.ModelLike, config::Config) - # Problem EXP2 - # A problem where ECOS was failing +""" + test_conic_Exponential_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) + +Test an exponential cone in geometric conic form. +""" +function test_conic_Exponential_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_conic_Exponential_helper(model, config, false) + return +end + +function setup_test( + ::typeof(test_conic_Exponential_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 2.0, 2exp(1 / 2)], + (MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone) => + [[-exp(1 / 2), -exp(1 / 2) / 2, 1.0]], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [1 + exp(1 / 2), 1 + exp(1 / 2) / 2], + ), + ) + return +end + +""" + test_conic_Exponential_hard_2(model::MOI.ModelLike, config::Config) + +Test an exponential cone problem that ECOS failed. +""" +function test_conic_Exponential_hard_2(model::MOI.ModelLike, config::Config) + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{Float64}, + MOI.ExponentialCone, + ) + return + end atol = config.atol rtol = config.rtol @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -2728,11 +3225,49 @@ function exp2test(model::MOI.ModelLike, config::Config) atol rtol = rtol end end + return +end + +function setup_test( + ::typeof(test_conic_Exponential_hard_2), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.0, -0.3, 0.0, exp(-0.3), exp(-0.3), exp(-0.3), 0.0, 1.0, 0.0], + (MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone) => [ + [-exp(-0.3) / 2, -1.3exp(-0.3) / 2, 0.5], + [-exp(-0.3) / 2, -1.3exp(-0.3) / 2, 0.5], + ], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-1.0, exp(-0.3) * 0.3], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [-exp(-0.3) * 0.3], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [ + [0.0, exp(-0.3), exp(-0.3) / 2], + [0.0, 0.0, exp(-0.3) / 2], + ], + ), + ) + return end -function exp3test(model::MOI.ModelLike, config::Config) - # Problem EXP3 - # A problem where ECOS was failing +""" + test_conic_Exponential_hard(model::MOI.ModelLike, config::Config) + +Test an exponential problem that ECOS failed. +""" +function test_conic_Exponential_hard(model::MOI.ModelLike, config::Config) + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{Float64}, + MOI.ExponentialCone, + ) + return + end atol = config.atol rtol = config.rtol @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -2815,18 +3350,41 @@ function exp3test(model::MOI.ModelLike, config::Config) [-1.0, log(5) - 1, 1 / 5] atol = atol rtol = rtol end end + return end -exptests = Dict( - "exp1v" => exp1vtest, - "exp1f" => exp1ftest, - "exp2" => exp2test, - "exp3" => exp3test, +function setup_test( + ::typeof(test_conic_Exponential_hard), + model::MOIU.MockOptimizer, + ::Config, ) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [log(5), 5.0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => + [0.0], + (MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone) => + [[-1.0, log(5) - 1, 1 / 5]], + ), + ) + return +end -@moitestset exp - -function _dualexp1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) +function _test_conic_DualExponentialCone_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, +) + F = if use_VectorOfVariables + MOI.VectorOfVariables + else + MOI.VectorAffineFunction{Float64} + end + if !MOI.supports_constraint(model, F, MOI.DualExponentialCone) + return + end atol = config.atol rtol = config.rtol # Problem dual exp @@ -2842,7 +3400,7 @@ function _dualexp1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -2866,7 +3424,7 @@ function _dualexp1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) x = MOI.add_variables(model, 2) @test MOI.get(model, MOI.NumberOfVariables()) == 5 vov = MOI.VectorOfVariables(v) - if vecofvars + if use_VectorOfVariables vc = MOI.add_constraint(model, vov, MOI.DualExponentialCone()) else vc = MOI.add_constraint( @@ -2941,33 +3499,106 @@ function _dualexp1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) atol rtol = rtol end end + return end -function dualexp1vtest(model::MOI.ModelLike, config::Config) - return _dualexp1test(model, config, true) +function test_conic_DualExponentialCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_conic_DualExponentialCone_helper(model, config, true) + return end -function dualexp1ftest(model::MOI.ModelLike, config::Config) - return _dualexp1test(model, config, false) + +function setup_test( + ::typeof(test_conic_DualExponentialCone_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [ + -exp(1 / 2), + -exp(1 / 2) / 2, + 1.0, + 1 + exp(1 / 2), + 1 + exp(1 / 2) / 2, + ], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-1.0, -2.0, -2exp(1 / 2)], + ), + ) + return end -dualexptests = Dict("dualexp1v" => dualexp1vtest, "dualexp1f" => dualexp1ftest) -@moitestset dualexp +function test_conic_DualExponentialCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_conic_DualExponentialCone_helper(model, config, false) + return +end -function _pow1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) +function setup_test( + ::typeof(test_conic_DualExponentialCone_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [ + -exp(1 / 2), + -exp(1 / 2) / 2, + 1.0, + 1 + exp(1 / 2), + 1 + exp(1 / 2) / 2, + ], + (MOI.VectorAffineFunction{Float64}, MOI.DualExponentialCone) => + [[1.0, 2.0, 2exp(1 / 2)]], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-1.0, -2.0, -2exp(1 / 2)], + ), + ) + return +end + + +""" +``` +max z + st x^0.9 * y^(0.1) >= |z| (i.e (x, y, z) are in the 3d power cone with a=0.9) + x == 2 + y == 1 +``` +Dual +``` +min -2α - β + st (u/0.9)^0.9 (v/0.1)^0.1 >= |w| + u + α = 0 + v + β = 0 + w = -1 +``` +""" +function _test_conic_PowerCone_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, +) + F = if use_VectorOfVariables + MOI.VectorOfVariables + else + MOI.VectorAffineFunction{Float64} + end + if !MOI.supports_constraint(model, F, MOI.PowerCone{Float64}) + return + end atol = config.atol rtol = config.rtol - # Problem POW1 - # max z - # st x^0.9 * y^(0.1) >= |z| (i.e (x, y, z) are in the 3d power cone with a=0.9) - # x == 2 - # y == 1 - # Dual - # min -2α - β - # st (u/0.9)^0.9 (v/0.1)^0.1 >= |w| - # u + α = 0 - # v + β = 0 - # w = -1 a = 0.9 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( @@ -2975,7 +3606,7 @@ function _pow1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -2998,7 +3629,7 @@ function _pow1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) v = MOI.add_variables(model, 3) @test MOI.get(model, MOI.NumberOfVariables()) == 3 vov = MOI.VectorOfVariables(v) - if vecofvars + if use_VectorOfVariables vc = MOI.add_constraint(model, vov, MOI.PowerCone(a)) else vc = MOI.add_constraint( @@ -3055,49 +3686,118 @@ function _pow1test(model::MOI.ModelLike, config::Config, vecofvars::Bool) atol rtol = rtol end end + return +end + +function test_conic_PowerCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_conic_PowerCone_helper(model, config, true) + return end -function pow1vtest(model::MOI.ModelLike, config::Config) - return _pow1test(model, config, true) +function setup_test( + ::typeof(test_conic_PowerCone_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + u_value = 0.839729692 + v_value = 2^0.9 - 2 * u_value + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [2.0, 1.0, 2^0.9], + (MOI.VectorOfVariables, MOI.PowerCone{Float64}) => [ + [u_value, v_value, -1], + ], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-u_value, -v_value], + ), + ) + return end -function pow1ftest(model::MOI.ModelLike, config::Config) - return _pow1test(model, config, false) + +function test_conic_PowerCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_conic_PowerCone_helper(model, config, false) + return end -powtests = Dict("pow1v" => pow1vtest, "pow1f" => pow1ftest) +function setup_test( + ::typeof(test_conic_PowerCone_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + u_value = 0.839729692 + v_value = 2^0.9 - 2 * u_value + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [2.0, 1.0, 2^0.9], + (MOI.VectorAffineFunction{Float64}, MOI.PowerCone{Float64}) => [ + [u_value, v_value, -1], + ], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-u_value, -v_value], + ), + ) + return +end -@moitestset pow +""" + _test_conic_DualPowerCone_helper( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool; + exponent::Float64 = 0.9, + ) -function _dualpow1test( +Problem dual POW1 +``` +min -x_1 - x_2 + st x_1 + u == 0 + x_2 + v == 0 + w == 1 + (u, v, w) ∈ DualPowerCone(exponent) +``` +By the Weighted AM–GM inequality, you have +0.9a + 0.1b >= a^0.9 b^0.1 +with equality if and only if a == b +here taking a = u/0.9 and b = v/0.1, we have +u + v >= (u/0.9)^0.9 (v/0.1)^0.1 +with equality if and only if u/0.9 == v/0.1. +Here the best you can do is u + v == 1 and for that inequality must hold so u = 9v +hence you get v = 0.1 and u = 0.9. +The same works for other values of exponent as key word argument +""" +function _test_conic_DualPowerCone_helper( model::MOI.ModelLike, config::Config, - vecofvars::Bool; + use_VectorOfVariables::Bool; exponent::Float64 = 0.9, ) + F = if use_VectorOfVariables + MOI.VectorOfVariables + else + MOI.VectorAffineFunction{Float64} + end + if !MOI.supports_constraint(model, F, MOI.DualPowerCone{Float64}) + return + end atol = config.atol rtol = config.rtol - # Problem dual POW1 - # min -x_1 - x_2 - # st x_1 + u == 0 - # x_2 + v == 0 - # w == 1 - # (u, v, w) ∈ DualPowerCone(exponent) - # By the Weighted AM–GM inequality, you have - # 0.9a + 0.1b >= a^0.9 b^0.1 - # with equality if and only if a == b - # here taking a = u/0.9 and b = v/0.1, we have - # u + v >= (u/0.9)^0.9 (v/0.1)^0.1 - # with equality if and only if u/0.9 == v/0.1. - # Here the best you can do is u + v == 1 and for that inequality must hold so u = 9v - # hence you get v = 0.1 and u = 0.9. - # The same works for other values of exponent as key word argument @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint( model, MOI.VectorOfVariables, @@ -3121,7 +3821,7 @@ function _dualpow1test( x = MOI.add_variables(model, 2) @test MOI.get(model, MOI.NumberOfVariables()) == 5 vov = MOI.VectorOfVariables(v) - if vecofvars + if use_VectorOfVariables vc = MOI.add_constraint(model, vov, MOI.DualPowerCone(exponent)) else vc = MOI.add_constraint( @@ -3194,27 +3894,100 @@ function _dualpow1test( rtol end end + return end -function dualpow1vtest(model::MOI.ModelLike, config::Config) - return _dualpow1test(model, config, true) +""" + test_conic_DualPowerCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) + +Test DualPowerCone in the standard conic form. +""" +function test_conic_DualPowerCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_conic_DualPowerCone_helper(model, config, true) + return end -function dualpow1ftest(model::MOI.ModelLike, config::Config) - return _dualpow1test(model, config, false) + +function setup_test( + ::typeof(test_conic_DualPowerCone_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.9, 0.1, 1.0, -0.9, -0.1], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-1.0, -1.0, 1.0], + ), + ) + return end -dualpowtests = Dict("dualpow1v" => dualpow1vtest, "dualpow1f" => dualpow1ftest) +""" + test_conic_DualPowerCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) -@moitestset dualpow +Test DualPowerCone in the geometric conic form. +""" +function test_conic_DualPowerCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_conic_DualPowerCone_helper(model, config, false) + return +end + +function setup_test( + ::typeof(test_conic_DualPowerCone_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0.9, 0.1, 1.0, -0.9, -0.1], + (MOI.VectorAffineFunction{Float64}, MOI.DualPowerCone{Float64}) => [[1.0, 1.0, -1.0]], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-1.0, -1.0, 1.0], + ), + ) + return +end -function relentr1test(model::MOI.ModelLike, config::Config) +""" + test_conic_RelativeEntropyCone(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +min u + st u >= 2*log(2/1) + 3*log(3/5) (i.e. (u, 1, 5, 2, 3) in RelativeEntropyCone(5)) +Optimal solution is: +u = 2*log(2/1) + 3*log(3/5) ≈ -0.1461825 +``` +""" +function test_conic_RelativeEntropyCone( + model::MOI.ModelLike, + config::Config{T}, +) where {T} + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{T}, + MOI.RelativeEntropyCone, + ) + return + end atol = config.atol rtol = config.rtol - # Problem RelEntr1 - # min u - # st u >= 2*log(2/1) + 3*log(3/5) (i.e. (u, 1, 5, 2, 3) in RelativeEntropyCone(5)) - # Optimal solution is: - # u = 2*log(2/1) + 3*log(3/5) ≈ -0.1461825 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports(model, MOI.ObjectiveFunction{MOI.SingleVariable}()) @test MOI.supports(model, MOI.ObjectiveSense()) @@ -3266,20 +4039,51 @@ function relentr1test(model::MOI.ModelLike, config::Config) rtol end end + return end -relentrtests = Dict("relentr1" => relentr1test) +function setup_test( + ::typeof(test_conic_RelativeEntropyCone), + model::MOIU.MockOptimizer, + ::Config, +) + u_opt = 2 * log(2 / 1) + 3 * log(3 / 5) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [u_opt], + (MOI.VectorAffineFunction{Float64}, MOI.RelativeEntropyCone) => + [[1, 2, 0.6, log(0.5) - 1, log(5 / 3) - 1]], + ), + ) + return +end -@moitestset relentr +""" + test_conic_NormSpectralCone(model::MOI.ModelLike, config::Config) -function normspec1test(model::MOI.ModelLike, config::Config) +Test the problem: +``` +min t + st t >= sigma_1([1 1 0; 1 -1 1]) (i.e (t, 1, 1, 1, -1, 0, 1]) is in NormSpectralCone(2, 3)) +Singular values are [sqrt(3), sqrt(2)], so optimal solution is: +t = sqrt(3) +``` +""" +function test_conic_NormSpectralCone( + model::MOI.ModelLike, + config::Config{T}, +) where {T} + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{T}, + MOI.NormSpectralCone, + ) + return + end atol = config.atol rtol = config.rtol - # Problem NormSpec1 - # min t - # st t >= sigma_1([1 1 0; 1 -1 1]) (i.e (t, 1, 1, 1, -1, 0, 1]) is in NormSpectralCone(2, 3)) - # Singular values are [sqrt(3), sqrt(2)], so optimal solution is: - # t = sqrt(3) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports(model, MOI.ObjectiveFunction{MOI.SingleVariable}()) @test MOI.supports(model, MOI.ObjectiveSense()) @@ -3332,16 +4136,52 @@ function normspec1test(model::MOI.ModelLike, config::Config) rtol end end + return end -function normspec2test(model::MOI.ModelLike, config::Config) +function setup_test( + ::typeof(test_conic_NormSpectralCone), + model::MOIU.MockOptimizer, + ::Config, +) + invrt3 = inv(sqrt(3)) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [sqrt(3)], + (MOI.VectorAffineFunction{Float64}, MOI.NormSpectralCone) => [ + Float64[1, 0, -invrt3, 0, invrt3, 0, -invrt3], + ], + ), + ) + return +end + +""" + test_conic_NormSpectralCone_2(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +min t + st t >= sigma_1([1 1; 1 -1; 0 1]) (i.e (t, 1, 1, 0, 1, -1, 1]) is in NormSpectralCone(3, 2)) +Singular values are [sqrt(3), sqrt(2)], so optimal solution is: +t = sqrt(3) +``` +""" +function test_conic_NormSpectralCone_2( + model::MOI.ModelLike, + config::Config{T}, +) where {T} + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{T}, + MOI.NormSpectralCone, + ) + return + end atol = config.atol rtol = config.rtol - # Problem NormSpec2 - # min t - # st t >= sigma_1([1 1; 1 -1; 0 1]) (i.e (t, 1, 1, 0, 1, -1, 1]) is in NormSpectralCone(3, 2)) - # Singular values are [sqrt(3), sqrt(2)], so optimal solution is: - # t = sqrt(3) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports(model, MOI.ObjectiveFunction{MOI.SingleVariable}()) @test MOI.supports(model, MOI.ObjectiveSense()) @@ -3394,20 +4234,52 @@ function normspec2test(model::MOI.ModelLike, config::Config) rtol end end + return end -normspectests = Dict("normspec1" => normspec1test, "normspec2" => normspec2test) +function setup_test( + ::typeof(test_conic_NormSpectralCone_2), + model::MOIU.MockOptimizer, + ::Config, +) + invrt3 = inv(sqrt(3)) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [sqrt(3)], + (MOI.VectorAffineFunction{Float64}, MOI.NormSpectralCone) => [ + Float64[1, 0, 0, 0, -invrt3, invrt3, -invrt3], + ], + ), + ) + return +end -@moitestset normspec +""" + test_conic_NormNuclearCone(model::MOI.ModelLike, config::Config) -function normnuc1test(model::MOI.ModelLike, config::Config) +Test the problem: +``` +min t + st t >= sum_i sigma_i([1 1 0; 1 -1 1]) (i.e (t, 1, 1, 1, -1, 0, 1]) is in NormNuclearCone(2, 3)) +Singular values are [sqrt(3), sqrt(2)], so optimal solution is: +t = sqrt(3) + sqrt(2) +``` +""" +function test_conic_NormNuclearCone( + model::MOI.ModelLike, + config::Config{T}, +) where {T} + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{T}, + MOI.NormNuclearCone, + ) + return + end atol = config.atol rtol = config.rtol - # Problem NormNuc1 - # min t - # st t >= sum_i sigma_i([1 1 0; 1 -1 1]) (i.e (t, 1, 1, 1, -1, 0, 1]) is in NormNuclearCone(2, 3)) - # Singular values are [sqrt(3), sqrt(2)], so optimal solution is: - # t = sqrt(3) + sqrt(2) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports(model, MOI.ObjectiveFunction{MOI.SingleVariable}()) @test MOI.supports(model, MOI.ObjectiveSense()) @@ -3463,16 +4335,53 @@ function normnuc1test(model::MOI.ModelLike, config::Config) atol rtol = rtol end end + return +end + +function setup_test( + ::typeof(test_conic_NormNuclearCone), + model::MOIU.MockOptimizer, + ::Config, +) + invrt2 = inv(sqrt(2)) + invrt3 = inv(sqrt(3)) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [sqrt(2) + sqrt(3)], + (MOI.VectorAffineFunction{Float64}, MOI.NormNuclearCone) => [ + Float64[1, -invrt2, -invrt3, -invrt2, invrt3, 0, -invrt3], + ], + ), + ) + return end -function normnuc2test(model::MOI.ModelLike, config::Config) +""" + test_conic_NormNuclearCone_2(model::MOI.ModelLike, config::Config) + +Test the problem: +``` +min t + st t >= sum_i sigma_i([1 1; 1 -1; 0 1]) (i.e (t, 1, 1, 0, 1, -1, 1]) is in NormNuclearCone(3, 2)) +Singular values are [sqrt(3), sqrt(2)], so optimal solution is: +t = sqrt(3) + sqrt(2) +``` +""" +function test_conic_NormNuclearCone_2( + model::MOI.ModelLike, + config::Config{T}, +) where {T} + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{T}, + MOI.NormNuclearCone, + ) + return + end atol = config.atol rtol = config.rtol - # Problem NormNuc2 - # min t - # st t >= sum_i sigma_i([1 1; 1 -1; 0 1]) (i.e (t, 1, 1, 0, 1, -1, 1]) is in NormNuclearCone(3, 2)) - # Singular values are [sqrt(3), sqrt(2)], so optimal solution is: - # t = sqrt(3) + sqrt(2) @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports(model, MOI.ObjectiveFunction{MOI.SingleVariable}()) @test MOI.supports(model, MOI.ObjectiveSense()) @@ -3528,15 +4437,32 @@ function normnuc2test(model::MOI.ModelLike, config::Config) atol rtol = rtol end end + return end -normnuctests = Dict("normnuc1" => normnuc1test, "normnuc2" => normnuc2test) - -@moitestset normnuc +function setup_test( + ::typeof(test_conic_NormNuclearCone_2), + model::MOIU.MockOptimizer, + ::Config, +) + invrt2 = inv(sqrt(2)) + invrt3 = inv(sqrt(3)) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [sqrt(2) + sqrt(3)], + (MOI.VectorAffineFunction{Float64}, MOI.NormNuclearCone) => [ + Float64[1, -invrt2, -invrt2, 0, -invrt3, invrt3, -invrt3], + ], + ), + ) + return +end function _psd0test( model::MOI.ModelLike, - vecofvars::Bool, + use_VectorOfVariables::Bool, psdcone, config::Config, ) @@ -3557,7 +4483,7 @@ function _psd0test( MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), ) @test MOI.supports(model, MOI.ObjectiveSense()) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint(model, MOI.VectorOfVariables, psdcone) else @test MOI.supports_constraint( @@ -3576,7 +4502,7 @@ function _psd0test( X = MOI.add_variables(model, square ? 4 : 3) @test MOI.get(model, MOI.NumberOfVariables()) == (square ? 4 : 3) vov = MOI.VectorOfVariables(X) - if vecofvars + if use_VectorOfVariables cX = MOI.add_constraint(model, vov, psdcone(2)) else cX = MOI.add_constraint( @@ -3594,7 +4520,7 @@ function _psd0test( @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, psdcone, }(), @@ -3659,7 +4585,7 @@ end function _psd1test( model::MOI.ModelLike, - vecofvars::Bool, + use_VectorOfVariables::Bool, psdcone, config::Config, ) @@ -3746,7 +4672,7 @@ function _psd1test( MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, ) - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint(model, MOI.VectorOfVariables, psdcone) else @test MOI.supports_constraint( @@ -3767,7 +4693,7 @@ function _psd1test( x = MOI.add_variables(model, 3) @test MOI.get(model, MOI.NumberOfVariables()) == (square ? 12 : 9) vov = MOI.VectorOfVariables(X) - if vecofvars + if use_VectorOfVariables cX = MOI.add_constraint(model, vov, psdcone(3)) else cX = MOI.add_constraint( @@ -3818,7 +4744,7 @@ function _psd1test( @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, psdcone, }(), @@ -4173,29 +5099,44 @@ const sdptests = Dict("psdt" => psdttest, "psds" => psdstest) @moitestset sdp true -function _det1test( +""" + _test_det_cone_helper_ellipsoid( + model::MOI.ModelLike, + config::Config, + use_VectorOfVariables::Bool, + detcone, + ) + +We look for an ellipsoid x^T P x ≤ 1 contained in the square. +Let Q = inv(P) (x^T Q x ≤ 1 is its polar ellipsoid), we have +``` +max t + t <= log det Q (or t <= (det Q)^(1/n)) + Q22 ≤ 1 + _________ + | | + | | +-Q11 ≥ -1 | + | Q11 ≤ 1 + | | + |_________| + -Q22 ≥ -1 +``` +""" +function _test_det_cone_helper_ellipsoid( model::MOI.ModelLike, config::Config, - vecofvars::Bool, + use_VectorOfVariables::Bool, detcone, ) + F = use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64} + if !MOI.supports_constraint(model, F, detcone) + return + end atol = config.atol rtol = config.rtol square = detcone == MOI.LogDetConeSquare || detcone == MOI.RootDetConeSquare use_logdet = detcone == MOI.LogDetConeTriangle || detcone == MOI.LogDetConeSquare - # We look for an ellipsoid x^T P x ≤ 1 contained in the square. - # Let Q = inv(P) (x^T Q x ≤ 1 is its polar ellipsoid), we have - # max t - # t <= log det Q (or t <= (det Q)^(1/n)) - # Q22 ≤ 1 - # _________ - # | | - # | | - # -Q11 ≥ -1 | + | Q11 ≤ 1 - # | | - # |_________| - # -Q22 ≥ -1 @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -4209,7 +5150,7 @@ function _det1test( MOI.EqualTo{Float64}, ) end - if vecofvars + if use_VectorOfVariables @test MOI.supports_constraint(model, MOI.VectorOfVariables, detcone) else @test MOI.supports_constraint( @@ -4237,7 +5178,7 @@ function _det1test( else vov = MOI.VectorOfVariables([t; Q]) end - if vecofvars + if use_VectorOfVariables cX = MOI.add_constraint(model, vov, detcone(2)) else cX = MOI.add_constraint( @@ -4261,7 +5202,7 @@ function _det1test( @test MOI.get( model, MOI.NumberOfConstraints{ - vecofvars ? MOI.VectorOfVariables : + use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64}, detcone, }(), @@ -4329,32 +5270,34 @@ function _det1test( end end -function logdett1vtest(model::MOI.ModelLike, config::Config) - return _det1test(model, config, true, MOI.LogDetConeTriangle) -end -function logdett1ftest(model::MOI.ModelLike, config::Config) - return _det1test(model, config, false, MOI.LogDetConeTriangle) -end -function logdets1vtest(model::MOI.ModelLike, config::Config) - return _det1test(model, config, true, MOI.LogDetConeSquare) -end -function logdets1ftest(model::MOI.ModelLike, config::Config) - return _det1test(model, config, false, MOI.LogDetConeSquare) -end +""" + _test_det_cone_helper(model::MOI.ModelLike, config::Config, detcone) + +A helper function for testing {Log,Root}DetCone{Square,Triangle}. -function _det2test(model::MOI.ModelLike, config::Config, detcone) +We find logdet or rootdet of a symmetric PSD matrix: +``` +mat = |3 2 1| + |2 2 1| + |1 1 3| +det(mat) = 5, so: +rootdet(mat) ≈ 1.709976 +logdet(mat) ≈ 1.609438 +``` +""" +function _test_det_cone_helper(model::MOI.ModelLike, config::Config, detcone) + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{Float64}, + detcone, + ) + return + end atol = config.atol rtol = config.rtol square = detcone == MOI.LogDetConeSquare || detcone == MOI.RootDetConeSquare use_logdet = detcone == MOI.LogDetConeTriangle || detcone == MOI.LogDetConeSquare - # We find logdet or rootdet of a symmetric PSD matrix: - # mat = |3 2 1| - # |2 2 1| - # |1 1 3| - # det(mat) = 5, so: - # rootdet(mat) ≈ 1.709976 - # logdet(mat) ≈ 1.609438 mat = Float64[3 2 1; 2 2 1; 1 1 3] matL = Float64[3, 2, 2, 1, 1, 3] @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -4418,90 +5361,421 @@ function _det2test(model::MOI.ModelLike, config::Config, detcone) end end -function logdett2test(model::MOI.ModelLike, config::Config) - return _det2test(model, config, MOI.LogDetConeTriangle) +""" + test_conic_LogDetConeTriangle_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) + +Test a problem with LogDetConeTriangle. +""" +function test_conic_LogDetConeTriangle_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_det_cone_helper_ellipsoid(model, config, true, MOI.LogDetConeTriangle) + return end -function logdets2test(model::MOI.ModelLike, config::Config) - return _det2test(model, config, MOI.LogDetConeSquare) + +function setup_test( + ::typeof(test_conic_LogDetConeTriangle_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0, 1, 0, 1, 1], + (MOI.SingleVariable, MOI.EqualTo{Float64}) => [2], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[1, 1]], + (MOI.VectorOfVariables, MOI.LogDetConeTriangle) => + [[-1, -2, 1, 0, 1]], + ), + ) + flag = model.eval_variable_constraint_dual + model.eval_variable_constraint_dual = false + return () -> model.eval_variable_constraint_dual = flag end -const logdetttests = Dict( - "logdett1v" => logdett1vtest, - "logdett1f" => logdett1ftest, - "logdett2" => logdett2test, +""" + test_conic_LogDetConeTriangle_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) + +Test a problem with LogDetConeTriangle. +""" +function test_conic_LogDetConeTriangle_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, ) + _test_det_cone_helper_ellipsoid( + model, + config, + false, + MOI.LogDetConeTriangle, + ) + return +end + +function setup_test( + ::typeof(test_conic_LogDetConeTriangle_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0, 1, 0, 1, 1], + (MOI.SingleVariable, MOI.EqualTo{Float64}) => [2], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[1, 1]], + (MOI.VectorAffineFunction{Float64}, MOI.LogDetConeTriangle) => + [[-1, -2, 1, 0, 1]], + ), + ) + return +end -@moitestset logdett +""" + test_conic_LogDetConeSquare_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) -const logdetstests = Dict( - "logdets1v" => logdets1vtest, - "logdets1f" => logdets1ftest, - "logdets2" => logdets2test, +Test a problem with LogDetConeSquare. +""" +function test_conic_LogDetConeSquare_VectorOfVariables( + model::MOI.ModelLike, + config::Config, ) + _test_det_cone_helper_ellipsoid(model, config, true, MOI.LogDetConeSquare) + return +end -@moitestset logdets +function setup_test( + ::typeof(test_conic_LogDetConeSquare_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0, 1, 0, 0, 1, 1], + (MOI.SingleVariable, MOI.EqualTo{Float64}) => [2], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[1, 1]], + (MOI.VectorOfVariables, MOI.LogDetConeSquare) => + [[-1, -2, 1, 0, 0, 1]], + ), + ) + flag = model.eval_variable_constraint_dual + model.eval_variable_constraint_dual = false + return () -> model.eval_variable_constraint_dual = flag +end -const logdettests = Dict("logdett" => logdetttest, "logdets" => logdetstest) +""" + test_conic_LogDetConeSquare_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) -@moitestset logdet true +Test a problem with LogDetConeSquare. +""" +function test_conic_LogDetConeSquare_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_det_cone_helper_ellipsoid(model, config, false, MOI.LogDetConeSquare) + return +end -function rootdett1vtest(model::MOI.ModelLike, config::Config) - return _det1test(model, config, true, MOI.RootDetConeTriangle) +function setup_test( + ::typeof(test_conic_LogDetConeSquare_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [0, 1, 0, 0, 1, 1], + (MOI.SingleVariable, MOI.EqualTo{Float64}) => [2], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[1, 1]], + (MOI.VectorAffineFunction{Float64}, MOI.LogDetConeSquare) => + [[-1, -2, 1, 0, 0, 1]], + ), + ) + return end -function rootdett1ftest(model::MOI.ModelLike, config::Config) - return _det1test(model, config, false, MOI.RootDetConeTriangle) + +""" + test_conic_RootDetConeTriangle_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) + +Test a problem with RootDetConeTriangle. +""" +function test_conic_RootDetConeTriangle_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_det_cone_helper_ellipsoid( + model, + config, + true, + MOI.RootDetConeTriangle, + ) + return end -function rootdets1vtest(model::MOI.ModelLike, config::Config) - return _det1test(model, config, true, MOI.RootDetConeSquare) + +function setup_test( + ::typeof(test_conic_RootDetConeTriangle_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 1, 0, 1], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[0.5, 0.5]], + (MOI.VectorOfVariables, MOI.RootDetConeTriangle) => + [[-1.0, 0.5, 0.0, 0.5]], + ), + ) + return end -function rootdets1ftest(model::MOI.ModelLike, config::Config) - return _det1test(model, config, false, MOI.RootDetConeSquare) + +""" + test_conic_RootDetConeTriangle_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) + +Test a problem with RootDetConeTriangle. +""" +function test_conic_RootDetConeTriangle_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_det_cone_helper_ellipsoid( + model, + config, + false, + MOI.RootDetConeTriangle, + ) + return end -function rootdett2test(model::MOI.ModelLike, config::Config) - return _det2test(model, config, MOI.RootDetConeTriangle) + +function setup_test( + ::typeof(test_conic_RootDetConeTriangle_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 1, 0, 1], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[0.5, 0.5]], + (MOI.VectorAffineFunction{Float64}, MOI.RootDetConeTriangle) => + [[-1.0, 0.5, 0.0, 0.5]], + ), + ) + return end -function rootdets2test(model::MOI.ModelLike, config::Config) - return _det2test(model, config, MOI.RootDetConeSquare) + +""" + test_conic_RootDetConeSquare_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) + +Test a problem with RootDetConeSquare. +""" +function test_conic_RootDetConeSquare_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_det_cone_helper_ellipsoid(model, config, true, MOI.RootDetConeSquare) + return end -const rootdetttests = Dict( - "rootdett1v" => rootdett1vtest, - "rootdett1f" => rootdett1ftest, - "rootdett2" => rootdett2test, +function setup_test( + ::typeof(test_conic_RootDetConeSquare_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, ) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 1, 0, 0, 1], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[0.5, 0.5]], + (MOI.VectorOfVariables, MOI.RootDetConeSquare) => + [[-1.0, 0.5, 0.0, 0.0, 0.5]], + ), + ) + return +end -@moitestset rootdett +""" + test_conic_RootDetConeSquare_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) -const rootdetstests = Dict( - "rootdets1v" => rootdets1vtest, - "rootdets1f" => rootdets1ftest, - "rootdets2" => rootdets2test, +Test a problem with RootDetConeSquare. +""" +function test_conic_RootDetConeSquare_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, ) + _test_det_cone_helper_ellipsoid(model, config, false, MOI.RootDetConeSquare) + return +end -@moitestset rootdets - -const rootdettests = - Dict("rootdett" => rootdetttest, "rootdets" => rootdetstest) - -@moitestset rootdet true - -const contconictests = Dict( - "lin" => lintest, - "norminf" => norminftest, - "normone" => normonetest, - "soc" => soctest, - "rsoc" => rsoctest, - "geomean" => geomeantest, - "exp" => exptest, - "dualexp" => dualexptest, - "pow" => powtest, - "dualpow" => dualpowtest, - "relentr" => relentrtest, - "normspec" => normspectest, - "normnuc" => normnuctest, - "sdp" => sdptest, - "logdet" => logdettest, - "rootdet" => rootdettest, +function setup_test( + ::typeof(test_conic_RootDetConeSquare_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, ) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1, 1, 0, 0, 1], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[0.5, 0.5]], + (MOI.VectorAffineFunction{Float64}, MOI.RootDetConeSquare) => + [[-1.0, 0.5, 0.0, 0.0, 0.5]], + ), + ) + return +end -@moitestset contconic true +""" + test_conic_LogDetConeTriangle(model::MOI.ModelLike, config::Config) + +Test a problem with LogDetConeTriangle. +""" +function test_conic_LogDetConeTriangle(model::MOI.ModelLike, config::Config) + _test_det_cone_helper(model, config, MOI.LogDetConeTriangle) + return +end + +function setup_test( + ::typeof(test_conic_LogDetConeTriangle), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [log(5)], + (MOI.VectorAffineFunction{Float64}, MOI.LogDetConeTriangle) => + [[-1, log(5) - 3, 1, -1, 1.6, 0, -0.2, 0.4]], + ), + ) + flag = model.eval_variable_constraint_dual + model.eval_variable_constraint_dual = false + return () -> model.eval_variable_constraint_dual = flag +end + +""" + test_conic_LogDetConeSquare(model::MOI.ModelLike, config::Config) + +Test a problem with LogDetConeSquare. +""" +function test_conic_LogDetConeSquare(model::MOI.ModelLike, config::Config) + _test_det_cone_helper(model, config, MOI.LogDetConeSquare) + return +end + +function setup_test( + ::typeof(test_conic_LogDetConeSquare), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [log(5)], + (MOI.VectorAffineFunction{Float64}, MOI.LogDetConeSquare) => + [[-1, log(5) - 3, 1, -1, 0, -1, 1.6, -0.2, 0, -0.2, 0.4]], + ), + ) + return +end + +""" + test_conic_RootDetConeTriangle(model::MOI.ModelLike, config::Config) + +Test a problem with RootDetConeTriangle. +""" +function test_conic_RootDetConeTriangle(model::MOI.ModelLike, config::Config) + _test_det_cone_helper(model, config, MOI.RootDetConeTriangle) + return +end + +function setup_test( + ::typeof(test_conic_RootDetConeTriangle), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [5^inv(3)], + (MOI.VectorAffineFunction{Float64}, MOI.RootDetConeTriangle) => + [vcat(-1, [1, -1, 1.6, 0, -0.2, 0.4] / 3 * (5^inv(3)))], + ), + ) + flag = model.eval_variable_constraint_dual + model.eval_variable_constraint_dual = false + return () -> model.eval_variable_constraint_dual = flag +end + +""" + test_conic_RootDetConeSquare(model::MOI.ModelLike, config::Config) + +Test a problem with RootDetConeSquare. +""" +function test_conic_RootDetConeSquare(model::MOI.ModelLike, config::Config) + _test_det_cone_helper(model, config, MOI.RootDetConeSquare) + return +end + +function setup_test( + ::typeof(test_conic_RootDetConeSquare), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [5^inv(3)], + (MOI.VectorAffineFunction{Float64}, MOI.RootDetConeSquare) => [ + vcat( + -1, + [1, -1, 0, -1, 1.6, -0.2, 0, -0.2, 0.4] / 3 * (5^inv(3)), + ), + ], + ), + ) + return +end diff --git a/test/Bridges/Constraint/det.jl b/test/Bridges/Constraint/det.jl index 0f5c1c690d..8efc9446f6 100644 --- a/test/Bridges/Constraint/det.jl +++ b/test/Bridges/Constraint/det.jl @@ -33,8 +33,14 @@ config = MOIT.Config() ) => [psd_dual], ) - MOIT.logdett1vtest(bridged_mock, config) - MOIT.logdett1ftest(bridged_mock, config) + MOIT.test_conic_LogDetConeTriangle_VectorOfVariables( + bridged_mock, + config, + ) + MOIT.test_conic_LogDetConeTriangle_VectorAffineFunction( + bridged_mock, + config, + ) # set primal/dual start is not yet implemented for LogDet bridge ci = first( @@ -106,7 +112,7 @@ config = MOIT.Config() ) => [psd_dual], ) - MOIT.logdett2test(bridged_mock, config) + MOIT.test_conic_LogDetConeTriangle(bridged_mock, config) # set primal/dual start is not yet implemented for LogDet bridge ci = first( @@ -155,8 +161,14 @@ end ) => [psd_dual], ) - MOIT.rootdett1vtest(bridged_mock, config) - MOIT.rootdett1ftest(bridged_mock, config) + MOIT.test_conic_RootDetConeTriangle_VectorOfVariables( + bridged_mock, + config, + ) + MOIT.test_conic_RootDetConeTriangle_VectorAffineFunction( + bridged_mock, + config, + ) # set primal/dual start is not yet implemented for RootDet bridge ci = first( @@ -227,7 +239,7 @@ end ) => [psd_dual], ) - MOIT.rootdett2test(bridged_mock, config) + MOIT.test_conic_RootDetConeTriangle(bridged_mock, config) # set primal/dual start is not yet implemented for RootDet bridge ci = first( diff --git a/test/Bridges/Constraint/flip_sign.jl b/test/Bridges/Constraint/flip_sign.jl index 468845e588..8bd1ccdba4 100644 --- a/test/Bridges/Constraint/flip_sign.jl +++ b/test/Bridges/Constraint/flip_sign.jl @@ -198,7 +198,7 @@ end (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-3.0, -1.0]], ) - MOIT.lin1vtest(bridged_mock, config) + MOIT.test_conic_linear_VectorOfVariables(bridged_mock, config) ci = first( MOI.get( bridged_mock, diff --git a/test/Bridges/Constraint/functionize.jl b/test/Bridges/Constraint/functionize.jl index 6f36983c7e..e96ac0a64a 100644 --- a/test/Bridges/Constraint/functionize.jl +++ b/test/Bridges/Constraint/functionize.jl @@ -166,7 +166,7 @@ end (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-3, -1]], ) - MOIT.lin1vtest(bridged_mock, config) + MOIT.test_conic_linear_VectorOfVariables(bridged_mock, config) ci = first( MOI.get( bridged_mock, diff --git a/test/Bridges/Constraint/interval.jl b/test/Bridges/Constraint/interval.jl index f3ad338c06..3753f3166f 100644 --- a/test/Bridges/Constraint/interval.jl +++ b/test/Bridges/Constraint/interval.jl @@ -209,7 +209,7 @@ end (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => [[-3, -1]], ) - MOIT.lin1vtest(bridged_mock, config) + MOIT.test_conic_linear_VectorOfVariables(bridged_mock, config) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/norm_spec_nuc_to_psd.jl b/test/Bridges/Constraint/norm_spec_nuc_to_psd.jl index 09c5860955..4782a29b57 100644 --- a/test/Bridges/Constraint/norm_spec_nuc_to_psd.jl +++ b/test/Bridges/Constraint/norm_spec_nuc_to_psd.jl @@ -56,7 +56,7 @@ config = MOIT.Config() ) => [psd_dual], ) - MOIT.normspec1test(bridged_mock, config) + MOIT.test_conic_NormSpectralCone(bridged_mock, config) var_names = ["t"] MOI.set( @@ -203,7 +203,7 @@ end ) => [psd_dual], ) - MOIT.normnuc1test(bridged_mock, config) + MOIT.test_conic_NormNuclearCone(bridged_mock, config) greater = MOI.get( mock, diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index d8e56b3cc9..7a1f4efdfe 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -37,8 +37,8 @@ config = MOIT.Config() [[-1], [-1]], ) - MOIT.norminf1vtest(bridged_mock, config) - MOIT.norminf1ftest(bridged_mock, config) + MOIT.test_conic_NormInfinityCone_VectorOfVariables(bridged_mock, config) + MOIT.test_conic_NormInfinityCone_VectorAffineFunction(bridged_mock, config) var_names = ["x", "y", "z"] @@ -171,7 +171,7 @@ config = MOIT.Config() ) end - @testset "norminf3test" begin + @testset "test_conic_NormInfinityCone_3" begin mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, @@ -179,7 +179,7 @@ config = MOIT.Config() (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [vcat(fill(inv(3), 3), zeros(3)), fill(inv(3), 3)], ) - MOIT.norminf3test(bridged_mock, config) + MOIT.test_conic_NormInfinityCone_3(bridged_mock, config) var_names = ["x", "y1", "y2", "y3"] @@ -327,8 +327,8 @@ end [[-1], [0]], ) - MOIT.normone1vtest(bridged_mock, config) - MOIT.normone1ftest(bridged_mock, config) + MOIT.test_conic_NormOneCone_VectorOfVariables(bridged_mock, config) + MOIT.test_conic_NormOneCone_VectorAffineFunction(bridged_mock, config) var_names = ["x", "y", "z"] MOI.set( @@ -452,7 +452,7 @@ end ) end - @testset "normone3test" begin + @testset "test_conic_NormOneCone" begin mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, @@ -460,7 +460,7 @@ end (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [vcat(ones(4), zeros(3)), ones(3)], ) - MOIT.normone3test(bridged_mock, config) + MOIT.test_conic_NormOneCone(bridged_mock, config) var_names = ["x", "y1", "y2", "y3"] diff --git a/test/Bridges/Constraint/relentr_to_exp.jl b/test/Bridges/Constraint/relentr_to_exp.jl index fe75bf1c7d..aa71e0d547 100644 --- a/test/Bridges/Constraint/relentr_to_exp.jl +++ b/test/Bridges/Constraint/relentr_to_exp.jl @@ -40,7 +40,7 @@ config = MOIT.Config() exps_duals, ) - MOIT.relentr1test(bridged_mock, config) + MOIT.test_conic_RelativeEntropyCone(bridged_mock, config) var_names = ["u"] MOI.set( diff --git a/test/Bridges/Constraint/scalarize.jl b/test/Bridges/Constraint/scalarize.jl index bc03085ebc..85c85b94da 100644 --- a/test/Bridges/Constraint/scalarize.jl +++ b/test/Bridges/Constraint/scalarize.jl @@ -35,7 +35,7 @@ config = MOIT.Config() (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [-3, -1], ) - MOIT.lin1vtest(bridged_mock, config) + MOIT.test_conic_linear_VectorOfVariables(bridged_mock, config) ci = first( MOI.get( bridged_mock, @@ -100,7 +100,7 @@ config = MOIT.Config() (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [-3, -1], ) - MOIT.lin1ftest(bridged_mock, config) + MOIT.test_conic_linear_VectorAffineFunction(bridged_mock, config) ci = first( MOI.get( @@ -181,7 +181,7 @@ config = MOIT.Config() (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [7, 2, -4], ) - MOIT.lin2vtest(bridged_mock, config) + MOIT.test_conic_linear_VectorOfVariables_2(bridged_mock, config) # VectorAffineFunction-in-Nonnegatives # VectorAffineFunction-in-Nonpositives @@ -197,5 +197,5 @@ config = MOIT.Config() (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [7, 2, -4, 7], ) - MOIT.lin2ftest(bridged_mock, config) + MOIT.test_conic_linear_VectorAffineFunction_2(bridged_mock, config) end diff --git a/test/Bridges/Variable/flip_sign.jl b/test/Bridges/Variable/flip_sign.jl index 4101109d14..df0f46397c 100644 --- a/test/Bridges/Variable/flip_sign.jl +++ b/test/Bridges/Variable/flip_sign.jl @@ -22,7 +22,7 @@ config = MOIT.Config() (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[7, 2, -4]], ) - MOIT.lin2vtest(bridged_mock, config) + MOIT.test_conic_linear_VectorOfVariables_2(bridged_mock, config) @test MOI.get(mock, MOI.NumberOfVariables()) == 4 @test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 4 @@ -143,7 +143,7 @@ config = MOIT.Config() MOI.INFEASIBILITY_CERTIFICATE, ), ) - MOIT.lin4test(bridged_mock, config) + MOIT.test_conic_linear_INFEASIBLE_2(bridged_mock, config) @test MOI.get(mock, MOI.NumberOfVariables()) == 1 @test length(MOI.get(mock, MOI.ListOfVariableIndices())) == 1 diff --git a/test/Test/Test.jl b/test/Test/Test.jl index 660a850ea6..aa3732c373 100644 --- a/test/Test/Test.jl +++ b/test/Test/Test.jl @@ -11,22 +11,26 @@ const MOIU = MOI.Utilities include(file) end -MOI.Test.basic_constraint_tests( - MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), - MOI.Test.Config(), -) +@testset "runtests" begin + MOI.Test.runtests( + MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), + MOI.Test.Config(basis = true), + exclude = ["test_nonlinear_"], + ) -MOI.Test.runtests( - MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), - MOI.Test.Config(basis = true), - exclude = ["test_nonlinear_"], -) + MOI.Test.runtests( + MOIU.MockOptimizer( + MOIU.UniversalFallback(MOIU.Model{Float64}()), + eval_objective_value = false, + ), + MOI.Test.Config(optimal_status = MOI.LOCALLY_SOLVED), + include = ["test_nonlinear_"], + ) +end -MOI.Test.runtests( - MOIU.MockOptimizer( - MOIU.UniversalFallback(MOIU.Model{Float64}()), - eval_objective_value = false, - ), - MOI.Test.Config(optimal_status = MOI.LOCALLY_SOLVED), - include = ["test_nonlinear_"], -) +@testset "basic_constraint_tests" begin + MOI.Test.basic_constraint_tests( + MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())), + MOI.Test.Config(), + ) +end diff --git a/test/Test/contconic.jl b/test/Test/contconic.jl index 1a77ae6af0..574aacf463 100644 --- a/test/Test/contconic.jl +++ b/test/Test/contconic.jl @@ -7,145 +7,6 @@ const MOIU = MOI.Utilities mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) config = MOIT.Config() -@testset "Linear" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 0.0, 2.0], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-3, -1]], - ) - MOIT.lin1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 0.0, 2.0], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[0, 2, 0]], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-3, -1]], - ) - MOIT.lin1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [-4, -3, 16, 0], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[7, 2, -4]], - ) - MOIT.lin2vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [-4, -3, 16, 0], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[0]], - (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => [[0]], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => - [[7, 2, -4], [7]], - ) - MOIT.lin2ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - MOI.INFEASIBLE_POINT, - MOI.INFEASIBILITY_CERTIFICATE, - ) - MOIT.lin3test(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.INFEASIBLE) - MOIT.lin3test(mock, MOIT.Config(infeas_certificates = false)) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.INFEASIBLE_OR_UNBOUNDED) - MOIT.lin3test(mock, MOIT.Config(infeas_certificates = false)) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - MOI.INFEASIBLE_POINT, - MOI.INFEASIBILITY_CERTIFICATE, - ) - MOIT.lin4test(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.INFEASIBLE) - MOIT.lin4test(mock, MOIT.Config(infeas_certificates = false)) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, MOI.INFEASIBLE_OR_UNBOUNDED) - MOIT.lin4test(mock, MOIT.Config(infeas_certificates = false)) -end -@testset "NormInf" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 0.5, 1], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]], - (MOI.VectorOfVariables, MOI.NormInfinityCone) => [[1, 0, -1]], - ) - MOIT.norminf1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 0.5, 1], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [-1]], - (MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) => - [[1, 0, -1]], - ) - MOIT.norminf1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - MOI.INFEASIBLE_POINT, - MOI.INFEASIBILITY_CERTIFICATE, - ) - MOIT.norminf2test(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [2, -1, -1, -1], - (MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) => - [vcat(1, fill(-inv(3), 3))], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [fill(inv(3), 3)], - ) - MOIT.norminf3test(mock, config) -end -@testset "NormOne" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 0.5, 0.5], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [0]], - (MOI.VectorOfVariables, MOI.NormOneCone) => [[1, -1, -1]], - ) - MOIT.normone1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 0.5, 0.5], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-1], [0]], - (MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) => - [[1, -1, -1]], - ) - MOIT.normone1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - MOI.INFEASIBLE_POINT, - MOI.INFEASIBILITY_CERTIFICATE, - ) - MOIT.normone2test(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [4, -1, -1, -1], - (MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) => - [vcat(1, fill(-1, 3))], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [ones(3)], - ) - MOIT.normone3test(mock, config) -end @testset "SOC" begin mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -202,6 +63,7 @@ end ) MOIT.soc4test(mock, config) end + @testset "RSOC" begin mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -272,6 +134,7 @@ end ) MOIT.rotatedsoc4test(mock, config) end + @testset "GeoMean" begin mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -328,105 +191,7 @@ end ) MOIT.geomean3ftest(mock, config) end -@testset "Exponential" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 2.0, 2exp(1 / 2)], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [1 + exp(1 / 2), 1 + exp(1 / 2) / 2], - ) - MOIT.exp1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 2.0, 2exp(1 / 2)], - (MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone) => - [[-exp(1 / 2), -exp(1 / 2) / 2, 1.0]], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [1 + exp(1 / 2), 1 + exp(1 / 2) / 2], - ) - MOIT.exp1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0.0, -0.3, 0.0, exp(-0.3), exp(-0.3), exp(-0.3), 0.0, 1.0, 0.0], - (MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone) => [ - [-exp(-0.3) / 2, -1.3exp(-0.3) / 2, 0.5], - [-exp(-0.3) / 2, -1.3exp(-0.3) / 2, 0.5], - ], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-1.0, exp(-0.3) * 0.3], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [-exp(-0.3) * 0.3], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [ - [0.0, exp(-0.3), exp(-0.3) / 2], - [0.0, 0.0, exp(-0.3) / 2], - ], - ) - MOIT.exp2test(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [log(5), 5.0], - (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => - [0.0], - (MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone) => - [[-1.0, log(5) - 1, 1 / 5]], - ) - MOIT.exp3test(mock, config) -end -@testset "Dual Exponential" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [ - -exp(1 / 2), - -exp(1 / 2) / 2, - 1.0, - 1 + exp(1 / 2), - 1 + exp(1 / 2) / 2, - ], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-1.0, -2.0, -2exp(1 / 2)], - ) - MOIT.dualexp1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [ - -exp(1 / 2), - -exp(1 / 2) / 2, - 1.0, - 1 + exp(1 / 2), - 1 + exp(1 / 2) / 2, - ], - (MOI.VectorAffineFunction{Float64}, MOI.DualExponentialCone) => - [[1.0, 2.0, 2exp(1 / 2)]], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-1.0, -2.0, -2exp(1 / 2)], - ) - MOIT.dualexp1ftest(mock, config) -end -@testset "Dual Power" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0.9, 0.1, 1.0, -0.9, -0.1], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-1.0, -1.0, 1.0], - ) - MOIT.dualpow1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0.9, 0.1, 1.0, -0.9, -0.1], - (MOI.VectorAffineFunction{Float64}, MOI.DualPowerCone{Float64}) => [[1.0, 1.0, -1.0]], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-1.0, -1.0, 1.0], - ) - MOIT.dualpow1ftest(mock, config) -end + @testset "PSD" begin # PSD0 mock.optimize! = @@ -575,129 +340,3 @@ end ) MOIT.psds3test(mock, config) end -@testset "LogDet" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0, 1, 0, 1, 1], - (MOI.SingleVariable, MOI.EqualTo{Float64}) => [2], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[1, 1]], - (MOI.VectorOfVariables, MOI.LogDetConeTriangle) => - [[-1, -2, 1, 0, 1]], - ) - mock.eval_variable_constraint_dual = false - MOIT.logdett1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0, 1, 0, 1, 1], - (MOI.SingleVariable, MOI.EqualTo{Float64}) => [2], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[1, 1]], - (MOI.VectorAffineFunction{Float64}, MOI.LogDetConeTriangle) => - [[-1, -2, 1, 0, 1]], - ) - MOIT.logdett1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [log(5)], - (MOI.VectorAffineFunction{Float64}, MOI.LogDetConeTriangle) => - [[-1, log(5) - 3, 1, -1, 1.6, 0, -0.2, 0.4]], - ) - MOIT.logdett2test(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0, 1, 0, 0, 1, 1], - (MOI.SingleVariable, MOI.EqualTo{Float64}) => [2], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[1, 1]], - (MOI.VectorOfVariables, MOI.LogDetConeSquare) => - [[-1, -2, 1, 0, 0, 1]], - ) - mock.eval_variable_constraint_dual = false - MOIT.logdets1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [0, 1, 0, 0, 1, 1], - (MOI.SingleVariable, MOI.EqualTo{Float64}) => [2], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[1, 1]], - (MOI.VectorAffineFunction{Float64}, MOI.LogDetConeSquare) => - [[-1, -2, 1, 0, 0, 1]], - ) - MOIT.logdets1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [log(5)], - (MOI.VectorAffineFunction{Float64}, MOI.LogDetConeSquare) => - [[-1, log(5) - 3, 1, -1, 0, -1, 1.6, -0.2, 0, -0.2, 0.4]], - ) - MOIT.logdets2test(mock, config) -end -@testset "RootDet" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 1, 0, 1], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[0.5, 0.5]], - (MOI.VectorOfVariables, MOI.RootDetConeTriangle) => - [[-1.0, 0.5, 0.0, 0.5]], - ) - MOIT.rootdett1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 1, 0, 1], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[0.5, 0.5]], - (MOI.VectorAffineFunction{Float64}, MOI.RootDetConeTriangle) => - [[-1.0, 0.5, 0.0, 0.5]], - ) - MOIT.rootdett1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [5^inv(3)], - (MOI.VectorAffineFunction{Float64}, MOI.RootDetConeTriangle) => - [vcat(-1, [1, -1, 1.6, 0, -0.2, 0.4] / 3 * (5^inv(3)))], - ) - MOIT.rootdett2test(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 1, 0, 0, 1], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[0.5, 0.5]], - (MOI.VectorOfVariables, MOI.RootDetConeSquare) => - [[-1.0, 0.5, 0.0, 0.0, 0.5]], - ) - MOIT.rootdets1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1, 1, 0, 0, 1], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[0.5, 0.5]], - (MOI.VectorAffineFunction{Float64}, MOI.RootDetConeSquare) => - [[-1.0, 0.5, 0.0, 0.0, 0.5]], - ) - MOIT.rootdets1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [5^inv(3)], - (MOI.VectorAffineFunction{Float64}, MOI.RootDetConeSquare) => [ - vcat( - -1, - [1, -1, 0, -1, 1.6, -0.2, 0, -0.2, 0.4] / 3 * (5^inv(3)), - ), - ], - ) - MOIT.rootdets2test(mock, config) -end diff --git a/test/Utilities/matrix_of_constraints.jl b/test/Utilities/matrix_of_constraints.jl index 20c1d742e7..bbf44ef6b0 100644 --- a/test/Utilities/matrix_of_constraints.jl +++ b/test/Utilities/matrix_of_constraints.jl @@ -299,7 +299,7 @@ MOIU.@product_of_sets(NonnegNonpos, MOI.Nonnegatives, MOI.Nonpositives) b = [-1.0, 1.0] F = MOI.VectorAffineFunction{Float64} _test( - MOIT.lin3test, + MOIT.test_conic_linear_INFEASIBLE, Vector{Float64}, NonnegNonpos{Float64}, A, @@ -314,7 +314,7 @@ MOIU.@product_of_sets(NonnegNonpos, MOI.Nonnegatives, MOI.Nonpositives) end b = [1.0, -1.0] _test( - MOIT.lin3test, + MOIT.test_conic_linear_INFEASIBLE, Vector{Float64}, NonposNonneg{Float64}, A, @@ -331,7 +331,7 @@ MOIU.@product_of_sets(NonnegNonpos, MOI.Nonnegatives, MOI.Nonpositives) A = sparse([1, 2], [1, 1], [1.0, -1.0]) b = -ones(2) _test( - MOIT.lin3test, + MOIT.test_conic_linear_INFEASIBLE, Vector{Float64}, Nonneg{Float64}, A, diff --git a/test/Utilities/model.jl b/test/Utilities/model.jl index 7dedb5d516..b6cf60345c 100644 --- a/test/Utilities/model.jl +++ b/test/Utilities/model.jl @@ -239,16 +239,11 @@ end MOI.Test.runtests( MOIU.Model{Float64}(), MOIT.Config(solve = false), - include = ["test_linear_"], + include = ["test_linear_", "test_conic_"], exclude = ["VariablePrimalStart", "linear_mixed_complementarity"], ) end -@testset "Continuous Conic tests" begin - config = MOIT.Config(solve = false) - MOIT.contconictest(MOIU.Model{Float64}(), config) -end - @testset "Quadratic functions" begin model = MOIU.Model{Int}() From 532b6f00ee58b83fe2d2747bfe9a8725c70e9bee Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 19:21:29 +1200 Subject: [PATCH 22/23] Fix formatting --- src/Test/contconic.jl | 41 +++++++++++---------------- test/Bridges/Constraint/norm_to_lp.jl | 5 +++- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/Test/contconic.jl b/src/Test/contconic.jl index 2d6aa2d897..4d37cfd668 100644 --- a/src/Test/contconic.jl +++ b/src/Test/contconic.jl @@ -1096,7 +1096,6 @@ function setup_test( return end - function _test_conic_NormOneCone_helper( model::MOI.ModelLike, config::Config, @@ -3533,7 +3532,6 @@ function setup_test( return end - function test_conic_DualExponentialCone_VectorAffineFunction( model::MOI.ModelLike, config::Config, @@ -3567,7 +3565,6 @@ function setup_test( return end - """ ``` max z @@ -3709,11 +3706,10 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [2.0, 1.0, 2^0.9], - (MOI.VectorOfVariables, MOI.PowerCone{Float64}) => [ - [u_value, v_value, -1], - ], + (MOI.VectorOfVariables, MOI.PowerCone{Float64}) => + [[u_value, v_value, -1]], (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-u_value, -v_value], + [-u_value, -v_value], ), ) return @@ -3739,11 +3735,10 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [2.0, 1.0, 2^0.9], - (MOI.VectorAffineFunction{Float64}, MOI.PowerCone{Float64}) => [ - [u_value, v_value, -1], - ], + (MOI.VectorAffineFunction{Float64}, MOI.PowerCone{Float64}) => + [[u_value, v_value, -1]], (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-u_value, -v_value], + [-u_value, -v_value], ), ) return @@ -4150,9 +4145,8 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [sqrt(3)], - (MOI.VectorAffineFunction{Float64}, MOI.NormSpectralCone) => [ - Float64[1, 0, -invrt3, 0, invrt3, 0, -invrt3], - ], + (MOI.VectorAffineFunction{Float64}, MOI.NormSpectralCone) => + [Float64[1, 0, -invrt3, 0, invrt3, 0, -invrt3]], ), ) return @@ -4248,9 +4242,8 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [sqrt(3)], - (MOI.VectorAffineFunction{Float64}, MOI.NormSpectralCone) => [ - Float64[1, 0, 0, 0, -invrt3, invrt3, -invrt3], - ], + (MOI.VectorAffineFunction{Float64}, MOI.NormSpectralCone) => + [Float64[1, 0, 0, 0, -invrt3, invrt3, -invrt3]], ), ) return @@ -4350,9 +4343,8 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [sqrt(2) + sqrt(3)], - (MOI.VectorAffineFunction{Float64}, MOI.NormNuclearCone) => [ - Float64[1, -invrt2, -invrt3, -invrt2, invrt3, 0, -invrt3], - ], + (MOI.VectorAffineFunction{Float64}, MOI.NormNuclearCone) => + [Float64[1, -invrt2, -invrt3, -invrt2, invrt3, 0, -invrt3]], ), ) return @@ -4452,9 +4444,8 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [sqrt(2) + sqrt(3)], - (MOI.VectorAffineFunction{Float64}, MOI.NormNuclearCone) => [ - Float64[1, -invrt2, -invrt2, 0, -invrt3, invrt3, -invrt3], - ], + (MOI.VectorAffineFunction{Float64}, MOI.NormNuclearCone) => + [Float64[1, -invrt2, -invrt2, 0, -invrt3, invrt3, -invrt3]], ), ) return @@ -5128,7 +5119,9 @@ function _test_det_cone_helper_ellipsoid( use_VectorOfVariables::Bool, detcone, ) - F = use_VectorOfVariables ? MOI.VectorOfVariables : MOI.VectorAffineFunction{Float64} + F = + use_VectorOfVariables ? MOI.VectorOfVariables : + MOI.VectorAffineFunction{Float64} if !MOI.supports_constraint(model, F, detcone) return end diff --git a/test/Bridges/Constraint/norm_to_lp.jl b/test/Bridges/Constraint/norm_to_lp.jl index 7a1f4efdfe..919b4b29e8 100644 --- a/test/Bridges/Constraint/norm_to_lp.jl +++ b/test/Bridges/Constraint/norm_to_lp.jl @@ -38,7 +38,10 @@ config = MOIT.Config() ) MOIT.test_conic_NormInfinityCone_VectorOfVariables(bridged_mock, config) - MOIT.test_conic_NormInfinityCone_VectorAffineFunction(bridged_mock, config) + MOIT.test_conic_NormInfinityCone_VectorAffineFunction( + bridged_mock, + config, + ) var_names = ["x", "y", "z"] From 6f8d581ed87134d5a57063fb2ad33128496097e8 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 23 Jun 2021 20:05:44 +1200 Subject: [PATCH 23/23] WIP: continue migrating contconic --- src/Test/contconic.jl | 335 +++++++++++++++--- test/Bridges/Constraint/rsoc.jl | 14 +- .../Constraint/soc_to_nonconvex_quad.jl | 7 +- test/Bridges/Constraint/soc_to_psd.jl | 14 +- test/Bridges/Variable/rsoc_to_psd.jl | 2 +- test/Bridges/Variable/rsoc_to_soc.jl | 2 +- test/Bridges/Variable/soc_to_rsoc.jl | 2 +- test/Test/contconic.jl | 65 +--- 8 files changed, 312 insertions(+), 129 deletions(-) diff --git a/src/Test/contconic.jl b/src/Test/contconic.jl index 4d37cfd668..c44fb674e3 100644 --- a/src/Test/contconic.jl +++ b/src/Test/contconic.jl @@ -1421,6 +1421,13 @@ let n = 3. optimal solution: y .= -1, x = 4 ``` """ function test_conic_NormOneCone(model::MOI.ModelLike, config::Config) + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{Float64}, + MOI.NormOneCone, + ) + return + end atol = config.atol rtol = config.rtol @test MOI.supports_incremental_interface(model, false) #=copy_names=# @@ -1521,17 +1528,26 @@ function setup_test( return end -function _soc1test( +""" +Problem SOC1 +max 0x + 1y + 1z + st x == 1 + x >= ||(y,z)|| +""" +function _test_conic_SecondOrderCone_helper( model::MOI.ModelLike, config::Config, use_VectorOfVariables::Bool, ) + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{Float64}, + MOI.SecondOrderCone, + ) + return + end atol = config.atol rtol = config.rtol - # Problem SOC1 - # max 0x + 1y + 1z - # st x == 1 - # x >= ||(y,z)|| @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -1636,14 +1652,84 @@ function _soc1test( end end -function soc1vtest(model::MOI.ModelLike, config::Config) - return _soc1test(model, config, true) +""" + test_conic_SecondOrderCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, + ) + +Test a SecondOrderCone in standard conic form. +""" +function test_conic_SecondOrderCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) + _test_conic_SecondOrderCone_helper(model, config, true) + return +end + +function setup_test( + ::typeof(test_conic_SecondOrderCone_VectorOfVariables), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 1 / √2, 1 / √2], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]], + ), + ) + return end -function soc1ftest(model::MOI.ModelLike, config::Config) - return _soc1test(model, config, false) + +""" + test_conic_SecondOrderCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, + ) + +Test a SecondOrderCone in geometric conic form. +""" +function test_conic_SecondOrderCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) + _test_conic_SecondOrderCone_helper(model, config, false) + return end -function _soc2test(model::MOI.ModelLike, config::Config, nonneg::Bool) +function setup_test( + ::typeof(test_conic_SecondOrderCone_VectorAffineFunction), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 1 / √2, 1 / √2], + (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => + [[√2, -1, -1]], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]], + ), + ) + return +end + +function _test_conic_SecondOrderCone_helper_2( + model::MOI.ModelLike, + config::Config, + nonneg::Bool, +) + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{Float64}, + MOI.SecondOrderCone, + ) + return + end atol = config.atol rtol = config.rtol # Problem SOC2 @@ -1789,28 +1875,108 @@ function _soc2test(model::MOI.ModelLike, config::Config, nonneg::Bool) atol rtol = rtol end end + return end -function soc2ntest(model::MOI.ModelLike, config::Config) - return _soc2test(model, config, true) +""" + test_conic_SecondOrderCone_Nonnegatives( + model::MOI.ModelLike, + config::Config, + ) + +Test a SecondOrderCone with Nonnegatives constraints. +""" +function test_conic_SecondOrderCone_Nonnegatives( + model::MOI.ModelLike, + config::Config, +) + _test_conic_SecondOrderCone_helper_2(model, config, true) + return end -function soc2ptest(model::MOI.ModelLike, config::Config) - return _soc2test(model, config, false) + +function setup_test( + ::typeof(test_conic_SecondOrderCone_Nonnegatives), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [-1 / √2, 1 / √2, 1.0], + (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => + [[√2, 1, -1]], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[√2]], + (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => + [[1.0]], + ), + ) + return end -function soc3test(model::MOI.ModelLike, config::Config) +""" + test_conic_SecondOrderCone_Nonpositives( + model::MOI.ModelLike, + config::Config, + ) + +Test a SecondOrderCone with Nonpositives constraints. +""" +function test_conic_SecondOrderCone_Nonpositives( + model::MOI.ModelLike, + config::Config, +) + _test_conic_SecondOrderCone_helper_2(model, config, false) + return +end + +function setup_test( + ::typeof(test_conic_SecondOrderCone_Nonpositives), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [-1 / √2, 1 / √2, 1.0], + (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => + [[√2, 1, -1]], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[√2]], + (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => + [[-1.0]], + ), + ) + return +end + +""" + test_conic_SecondOrderCone_INFEASIBLE(model::MOI.ModelLike, config::Config) + +Problem SOC3 - 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) ∈ SOC₂ +""" +function test_conic_SecondOrderCone_INFEASIBLE( + model::MOI.ModelLike, + config::Config, +) + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{Float64}, + MOI.SecondOrderCone, + ) + return + end atol = config.atol rtol = config.rtol - # Problem SOC3 - 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) ∈ SOC₂ @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -1894,23 +2060,58 @@ function soc3test(model::MOI.ModelLike, config::Config) end # TODO test dual feasibility and objective sign end + return +end + +function setup_test( + ::typeof(test_conic_SecondOrderCone_INFEASIBLE), + model::MOIU.MockOptimizer, + ::Config, +) + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + MOI.INFEASIBLE, + MOI.INFEASIBLE_POINT, + MOI.INFEASIBILITY_CERTIFICATE, + ), + ) + return end -function soc4test(model::MOI.ModelLike, config::Config) +""" + test_conic_SecondOrderCone_out_of_order( + model::MOI.ModelLike, + config::Config, + ) + +Problem SOC4 +min 0x[1] - 2x[2] - 1x[3] + st x[1] == 1 (c1a) + x[2] - x[4] == 0 (c1b) + x[3] - x[5] == 0 (c1c) + x[1] >= ||(x[4],x[5])|| (c2) +in conic form: +min c^Tx +s.t. Ax + b ∈ {0}₃ + (x[1],x[4],x[5]) ∈ SOC₃ +Like SOCINT1 but with copies of variables and integrality relaxed +Tests out-of-order indices in cones +""" +function test_conic_SecondOrderCone_out_of_order( + model::MOI.ModelLike, + config::Config, +) + if !MOI.supports_constraint( + model, + MOI.VectorAffineFunction{Float64}, + MOI.SecondOrderCone, + ) + return + end atol = config.atol rtol = config.rtol - # Problem SOC4 - # min 0x[1] - 2x[2] - 1x[3] - # st x[1] == 1 (c1a) - # x[2] - x[4] == 0 (c1b) - # x[3] - x[5] == 0 (c1c) - # x[1] >= ||(x[4],x[5])|| (c2) - # in conic form: - # min c^Tx - # s.t. Ax + b ∈ {0}₃ - # (x[1],x[4],x[5]) ∈ SOC₃ - # Like SOCINT1 but with copies of variables and integrality relaxed - # Tests out-of-order indices in cones @test MOI.supports_incremental_interface(model, false) #=copy_names=# @test MOI.supports( model, @@ -1994,18 +2195,25 @@ function soc4test(model::MOI.ModelLike, config::Config) atol rtol = rtol end end + return end -const soctests = Dict( - "soc1v" => soc1vtest, - "soc1f" => soc1ftest, - "soc2n" => soc2ntest, - "soc2p" => soc2ptest, - "soc3" => soc3test, - "soc4" => soc4test, +function setup_test( + ::typeof(test_conic_SecondOrderCone_out_of_order), + model::MOIU.MockOptimizer, + ::Config, ) - -@moitestset soc + MOIU.set_mock_optimize!( + model, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + mock, + [1.0, 2 / √5, 1 / √5, 2 / √5, 1 / √5], + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => + [[-√5, -2.0, -1.0]], + ), + ) + return +end function _rotatedsoc1test(model::MOI.ModelLike, config::Config, abvars::Bool) atol = config.atol @@ -2136,10 +2344,16 @@ function _rotatedsoc1test(model::MOI.ModelLike, config::Config, abvars::Bool) end end -function rotatedsoc1vtest(model::MOI.ModelLike, config::Config) +function rotatedtest_conic_SecondOrderCone_VectorOfVariables( + model::MOI.ModelLike, + config::Config, +) return _rotatedsoc1test(model, config, true) end -function rotatedsoc1ftest(model::MOI.ModelLike, config::Config) +function rotatedtest_conic_SecondOrderCone_VectorAffineFunction( + model::MOI.ModelLike, + config::Config, +) return _rotatedsoc1test(model, config, false) end @@ -2257,7 +2471,12 @@ function rotatedsoc2test(model::MOI.ModelLike, config::Config) end end -function rotatedsoc3test(model::MOI.ModelLike, config::Config; n = 2, ub = 3.0) +function rotatedtest_conic_SecondOrderCone_INFEASIBLE( + model::MOI.ModelLike, + config::Config; + n = 2, + ub = 3.0, +) atol = config.atol rtol = config.rtol # Problem SOCRotated3 @@ -2429,7 +2648,12 @@ function rotatedsoc3test(model::MOI.ModelLike, config::Config; n = 2, ub = 3.0) end end -function rotatedsoc4test(model::MOI.ModelLike, config::Config; n = 2, ub = 3.0) +function rotatedtest_conic_SecondOrderCone_out_of_order( + model::MOI.ModelLike, + config::Config; + n = 2, + ub = 3.0, +) atol = config.atol rtol = config.rtol # Problem SOCRotated4 @@ -2514,11 +2738,12 @@ function rotatedsoc4test(model::MOI.ModelLike, config::Config; n = 2, ub = 3.0) end const rsoctests = Dict( - "rotatedsoc1v" => rotatedsoc1vtest, - "rotatedsoc1f" => rotatedsoc1ftest, + "rotatedsoc1v" => rotatedtest_conic_SecondOrderCone_VectorOfVariables, + "rotatedsoc1f" => + rotatedtest_conic_SecondOrderCone_VectorAffineFunction, "rotatedsoc2" => rotatedsoc2test, - "rotatedsoc3" => rotatedsoc3test, - "rotatedsoc4" => rotatedsoc4test, + "rotatedsoc3" => rotatedtest_conic_SecondOrderCone_INFEASIBLE, + "rotatedsoc4" => rotatedtest_conic_SecondOrderCone_out_of_order, ) @moitestset rsoc diff --git a/test/Bridges/Constraint/rsoc.jl b/test/Bridges/Constraint/rsoc.jl index f06b2429a2..fa99e34b2b 100644 --- a/test/Bridges/Constraint/rsoc.jl +++ b/test/Bridges/Constraint/rsoc.jl @@ -34,7 +34,10 @@ config = MOIT.Config() (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => [[3 / 2, 1 / 2, -1.0, -1.0]], ) - MOIT.rotatedsoc1vtest(bridged_mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_VectorOfVariables( + bridged_mock, + config, + ) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, @@ -42,7 +45,10 @@ config = MOIT.Config() (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => [[3 / 2, 1 / 2, -1.0, -1.0]], ) - MOIT.rotatedsoc1ftest(bridged_mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_VectorAffineFunction( + bridged_mock, + config, + ) ci = first( MOI.get( bridged_mock, @@ -93,7 +99,7 @@ end (MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[1 - 1 / √2, 1 + 1 / √2, -1]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]], ) - MOIT.soc1vtest(bridged_mock, config) + MOIT.test_conic_SecondOrderCone_VectorOfVariables(bridged_mock, config) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, @@ -101,7 +107,7 @@ end (MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[1 - 1 / √2, 1 + 1 / √2, -1]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]], ) - MOIT.soc1ftest(bridged_mock, config) + MOIT.test_conic_SecondOrderCone_VectorAffineFunction(bridged_mock, config) ci = first( MOI.get( bridged_mock, diff --git a/test/Bridges/Constraint/soc_to_nonconvex_quad.jl b/test/Bridges/Constraint/soc_to_nonconvex_quad.jl index e7d4093c8d..138f3f0256 100644 --- a/test/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/test/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -44,7 +44,10 @@ config = MOIT.Config(duals = false) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 1.0, 1 / √2, 1 / √2]) - MOIT.rotatedsoc1vtest(bridged_mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_VectorOfVariables( + bridged_mock, + config, + ) ci = first( MOI.get( @@ -98,7 +101,7 @@ end mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1 / √2, 1 / √2]) - MOIT.soc1vtest(bridged_mock, config) + MOIT.test_conic_SecondOrderCone_VectorOfVariables(bridged_mock, config) ci = first( MOI.get( diff --git a/test/Bridges/Constraint/soc_to_psd.jl b/test/Bridges/Constraint/soc_to_psd.jl index f4ab85339f..91356128a8 100644 --- a/test/Bridges/Constraint/soc_to_psd.jl +++ b/test/Bridges/Constraint/soc_to_psd.jl @@ -36,8 +36,8 @@ config = MOIT.Config() ) => [[√2 / 2, -1 / 2, √2 / 4, -1 / 2, √2 / 4, √2 / 4]], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]], ) - MOIT.soc1vtest(bridged_mock, config) - MOIT.soc1ftest(bridged_mock, config) + MOIT.test_conic_SecondOrderCone_VectorOfVariables(bridged_mock, config) + MOIT.test_conic_SecondOrderCone_VectorAffineFunction(bridged_mock, config) ci = first( MOI.get( @@ -96,7 +96,10 @@ end MOI.PositiveSemidefiniteConeTriangle, ) => [[√2, -1 / 2, √2 / 8, -1 / 2, √2 / 8, √2 / 8]], ) - MOIT.rotatedsoc1vtest(bridged_mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_VectorOfVariables( + bridged_mock, + config, + ) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, @@ -106,7 +109,10 @@ end MOI.PositiveSemidefiniteConeTriangle, ) => [[√2, -1 / 2, √2 / 8, -1 / 2, √2 / 8, √2 / 8]], ) - MOIT.rotatedsoc1ftest(bridged_mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_VectorAffineFunction( + bridged_mock, + config, + ) ci = first( MOI.get( diff --git a/test/Bridges/Variable/rsoc_to_psd.jl b/test/Bridges/Variable/rsoc_to_psd.jl index 914c520ea8..51c0f1efb4 100644 --- a/test/Bridges/Variable/rsoc_to_psd.jl +++ b/test/Bridges/Variable/rsoc_to_psd.jl @@ -125,7 +125,7 @@ end [[1.0, -0.5, 0.25, -0.5, 0.25, 0.25]], ) mock.eval_variable_constraint_dual = false - MOIT.rotatedsoc4test(bridged_mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_out_of_order(bridged_mock, config) mock.eval_variable_constraint_dual = true @testset "Test mock model" begin diff --git a/test/Bridges/Variable/rsoc_to_soc.jl b/test/Bridges/Variable/rsoc_to_soc.jl index 93381160ec..57ca612f40 100644 --- a/test/Bridges/Variable/rsoc_to_soc.jl +++ b/test/Bridges/Variable/rsoc_to_soc.jl @@ -21,7 +21,7 @@ bridged_mock = MOIB.Variable.RSOCtoSOC{Float64}(mock) (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], ) - MOIT.rotatedsoc4test(bridged_mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_out_of_order(bridged_mock, config) ceqs = MOI.get( mock, diff --git a/test/Bridges/Variable/soc_to_rsoc.jl b/test/Bridges/Variable/soc_to_rsoc.jl index a22208fc83..0cb76ea206 100644 --- a/test/Bridges/Variable/soc_to_rsoc.jl +++ b/test/Bridges/Variable/soc_to_rsoc.jl @@ -20,7 +20,7 @@ bridged_mock = MOIB.Variable.SOCtoRSOC{Float64}(mock) [1 / √2 + 1 / 2, 1 / √2 - 1 / 2, 1 / √2], (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]], ) - MOIT.soc1vtest(bridged_mock, config) + MOIT.test_conic_SecondOrderCone_VectorOfVariables(bridged_mock, config) ceqs = MOI.get( mock, diff --git a/test/Test/contconic.jl b/test/Test/contconic.jl index 574aacf463..e524ec2945 100644 --- a/test/Test/contconic.jl +++ b/test/Test/contconic.jl @@ -7,63 +7,6 @@ const MOIU = MOI.Utilities mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) config = MOIT.Config() -@testset "SOC" begin - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 1 / √2, 1 / √2], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]], - ) - MOIT.soc1vtest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 1 / √2, 1 / √2], - (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => - [[√2, -1, -1]], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]], - ) - MOIT.soc1ftest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [-1 / √2, 1 / √2, 1.0], - (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => - [[√2, 1, -1]], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[√2]], - (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => - [[1.0]], - ) - MOIT.soc2ntest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [-1 / √2, 1 / √2, 1.0], - (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => - [[√2, 1, -1]], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[√2]], - (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => - [[-1.0]], - ) - MOIT.soc2ptest(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.INFEASIBLE, - MOI.INFEASIBLE_POINT, - MOI.INFEASIBILITY_CERTIFICATE, - ) - MOIT.soc3test(mock, config) - mock.optimize! = - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 2 / √5, 1 / √5, 2 / √5, 1 / √5], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => - [[-√5, -2.0, -1.0]], - ) - MOIT.soc4test(mock, config) -end - @testset "RSOC" begin mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -75,7 +18,7 @@ end ) # double variable bounds on a and b variables mock.eval_variable_constraint_dual = false - MOIT.rotatedsoc1vtest(mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_VectorOfVariables(mock, config) mock.eval_variable_constraint_dual = true mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( @@ -83,7 +26,7 @@ end [1 / √2, 1 / √2], (MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[√2, 1 / √2, -1.0, -1.0]], ) - MOIT.rotatedsoc1ftest(mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_VectorAffineFunction(mock, config) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, @@ -120,7 +63,7 @@ end ) # double variable bounds on u mock.eval_variable_constraint_dual = false - MOIT.rotatedsoc3test(mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_INFEASIBLE(mock, config) mock.eval_variable_constraint_dual = true mock.optimize! = @@ -132,7 +75,7 @@ end (MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => [[1.0, 1.0, -1.0, -1.0]], ) - MOIT.rotatedsoc4test(mock, config) + MOIT.rotatedtest_conic_SecondOrderCone_out_of_order(mock, config) end @testset "GeoMean" begin