From 04bdbeb99623af05782fb3b8a26d62c186a20dfc Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 20 Jul 2021 12:56:49 +1200 Subject: [PATCH 1/6] Migrate tests and fix bugs in objective bridges --- src/Bridges/Objective/slack.jl | 7 +- test/Bridges/Objective/functionize.jl | 53 ++-- test/Bridges/Objective/map.jl | 77 ++++-- test/Bridges/Objective/slack.jl | 351 +++++++++----------------- 4 files changed, 205 insertions(+), 283 deletions(-) diff --git a/src/Bridges/Objective/slack.jl b/src/Bridges/Objective/slack.jl index a395c40b29..308a0e6db5 100644 --- a/src/Bridges/Objective/slack.jl +++ b/src/Bridges/Objective/slack.jl @@ -131,11 +131,16 @@ function MOI.get( return obj_slack_constant + slack - constant end +_constant_term(set::MOI.LessThan) = set.upper +_constant_term(set::MOI.GreaterThan) = set.lower + function MOI.get( model::MOI.ModelLike, ::MOI.ObjectiveFunction{G}, bridge::SlackBridge{T,F,G}, ) where {T,F,G<:MOI.AbstractScalarFunction} func = MOI.get(model, MOI.ConstraintFunction(), bridge.constraint) - return MOIU.convert_approx(G, MOIU.remove_variable(func, bridge.slack)) + set = MOI.get(model, MOI.ConstraintSet(), bridge.constraint) + f = MOIU.operate(-, T, func, _constant_term(set)) + return MOIU.convert_approx(G, MOIU.remove_variable(f, bridge.slack)) end diff --git a/test/Bridges/Objective/functionize.jl b/test/Bridges/Objective/functionize.jl index 1f1f90ecdd..56c105bead 100644 --- a/test/Bridges/Objective/functionize.jl +++ b/test/Bridges/Objective/functionize.jl @@ -1,25 +1,35 @@ +module TestObjectiveFunctionize + using Test using MathOptInterface const MOI = MathOptInterface -const MOIT = MathOptInterface.DeprecatedTest -const MOIU = MathOptInterface.Utilities -const MOIB = MathOptInterface.Bridges - -include("../utilities.jl") -mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) -config = MOIT.Config() +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return +end -bridged_mock = MOIB.Objective.Functionize{Float64}(mock) +include("../utilities.jl") -@testset "solve_singlevariable_obj" begin - MOIU.set_mock_optimize!( +function test_solve_singlevariable_obj() + mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + bridged_mock = MOI.Bridges.Objective.Functionize{Float64}(mock) + MOI.Utilities.set_mock_optimize!( mock, - (mock::MOIU.MockOptimizer) -> - MOIU.mock_optimize!(mock, [1.0], MOI.FEASIBLE_POINT), + (mock::MOI.Utilities.MockOptimizer) -> + MOI.Utilities.mock_optimize!(mock, [1.0], MOI.FEASIBLE_POINT), + ) + MOI.Test.test_objective_ObjectiveFunction_SingleVariable( + bridged_mock, + MOI.Test.Config(), ) - MOIT.solve_singlevariable_obj(bridged_mock, config) @test MOI.get(mock, MOI.ObjectiveFunctionType()) == MOI.ScalarAffineFunction{Float64} @test MOI.get(bridged_mock, MOI.ObjectiveFunctionType()) == @@ -38,14 +48,15 @@ bridged_mock = MOIB.Objective.Functionize{Float64}(mock) @test MOI.get(mock, MOI.ObjectiveSense()) == MOI.MAX_SENSE @test MOI.get(bridged_mock, MOI.ObjectiveSense()) == MOI.MAX_SENSE _test_delete_objective(bridged_mock, 1, tuple()) + return end -# Tests that the `ObjectiveValue` attribute passed has the correct -# `result_index`. -@testset "solve_result_index" begin - MOIU.set_mock_optimize!( +function test_solve_result_index() + mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + bridged_mock = MOI.Bridges.Objective.Functionize{Float64}(mock) + MOI.Utilities.set_mock_optimize!( mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( mock, MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [1.0]), @@ -53,5 +64,9 @@ end (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1.0], ), ) - MOIT.solve_result_index(bridged_mock, config) + return MOI.Test.test_solve_result_index(bridged_mock, MOI.Test.Config()) end + +end # module + +TestObjectiveFunctionize.runtests() diff --git a/test/Bridges/Objective/map.jl b/test/Bridges/Objective/map.jl index f9194dd3a7..f2b0c8cd61 100644 --- a/test/Bridges/Objective/map.jl +++ b/test/Bridges/Objective/map.jl @@ -1,42 +1,65 @@ +module TestObjectiveMap + using Test + using MathOptInterface const MOI = MathOptInterface -const MOIB = MOI.Bridges -struct ObjectiveDummyBridge <: MOIB.Objective.AbstractBridge +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return +end + +struct ObjectiveDummyBridge <: MOI.Bridges.Objective.AbstractBridge id::Int end -function test_empty(map) +function _test_empty(map) @test isempty(map) @test length(map) == 0 @test isempty(values(map)) @test iterate(map) === nothing - @test MOIB.Objective.function_type(map) === nothing + @test MOI.Bridges.Objective.function_type(map) === nothing end -map = MOIB.Objective.Map() -test_empty(map) -@test sprint(MOIB.print_num_bridges, map) == "\nwith 0 objective bridges" - -x = MOI.VariableIndex(1) -fx = MOI.SingleVariable(x) -MOIB.Objective.add_key_for_bridge(map, ObjectiveDummyBridge(1), fx) -@test MOIB.Objective.root_bridge(map) == ObjectiveDummyBridge(1) -@test sprint(MOIB.print_num_bridges, map) == "\nwith 1 objective bridge" -func = 1.0fx -MOIB.Objective.add_key_for_bridge(map, ObjectiveDummyBridge(2), func) -@test MOIB.Objective.root_bridge(map) == ObjectiveDummyBridge(2) -@test sprint(MOIB.print_num_bridges, map) == "\nwith 2 objective bridges" - -empty!(map) -test_empty(map) -@test sprint(MOIB.print_num_bridges, map) == "\nwith 0 objective bridges" - -@testset "EmptyMap" begin - map = MOIB.Objective.EmptyMap() - test_empty(map) +function test_Map() + map = MOI.Bridges.Objective.Map() + _test_empty(map) + @test sprint(MOI.Bridges.print_num_bridges, map) == + "\nwith 0 objective bridges" + x = MOI.VariableIndex(1) + fx = MOI.SingleVariable(x) + MOI.Bridges.Objective.add_key_for_bridge(map, ObjectiveDummyBridge(1), fx) + @test MOI.Bridges.Objective.root_bridge(map) == ObjectiveDummyBridge(1) + @test sprint(MOI.Bridges.print_num_bridges, map) == + "\nwith 1 objective bridge" + func = 1.0fx + MOI.Bridges.Objective.add_key_for_bridge(map, ObjectiveDummyBridge(2), func) + @test MOI.Bridges.Objective.root_bridge(map) == ObjectiveDummyBridge(2) + @test sprint(MOI.Bridges.print_num_bridges, map) == + "\nwith 2 objective bridges" empty!(map) - test_empty(map) - @test sprint(MOIB.print_num_bridges, map) == "" + _test_empty(map) + @test sprint(MOI.Bridges.print_num_bridges, map) == + "\nwith 0 objective bridges" + return end + +function test_EmptyMap() + map = MOI.Bridges.Objective.EmptyMap() + _test_empty(map) + empty!(map) + _test_empty(map) + @test sprint(MOI.Bridges.print_num_bridges, map) == "" + return +end + +end # module + +TestObjectiveMap.runtests() diff --git a/test/Bridges/Objective/slack.jl b/test/Bridges/Objective/slack.jl index c506755789..495b026944 100644 --- a/test/Bridges/Objective/slack.jl +++ b/test/Bridges/Objective/slack.jl @@ -1,254 +1,133 @@ +module TestObjectiveMap + using Test using MathOptInterface const MOI = MathOptInterface -const MOIT = MathOptInterface.DeprecatedTest -const MOIU = MathOptInterface.Utilities -const MOIB = MathOptInterface.Bridges - -include("../utilities.jl") - -mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) -config = MOIT.Config() - -bridged_mock = MOIB.Objective.Slack{Float64}(mock) -@testset "Set objective before sense" begin - err = ErrorException( - "Set `MOI.ObjectiveSense` before `MOI.ObjectiveFunction` when" * - " using `MOI.Bridges.Objective.SlackBridge`.", - ) - F = MOI.ScalarAffineFunction{Float64} - @test_throws err MOI.set(bridged_mock, MOI.ObjectiveFunction{F}(), zero(F)) +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return 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, 5.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0, 2.0]), - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0]), +function test_SlackBridge_ObjectiveSense_error() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], 1.2) + @test_throws( + ErrorException( + "Set `MOI.ObjectiveSense` before `MOI.ObjectiveFunction` when" * + " using `MOI.Bridges.Objective.SlackBridge`.", ), + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f), ) - MOIT.solve_qp_edge_cases(bridged_mock, config) - - @test MOIB.is_objective_bridged(bridged_mock) - @test MOI.get(bridged_mock, MOI.ObjectiveFunctionType()) == - MOI.ScalarQuadraticFunction{Float64} - @test MOI.get(bridged_mock, MOI.ListOfModelAttributesSet()) == [ - MOI.ObjectiveSense(), - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - ] - - var_names = ["x", "y"] - xy = MOI.get(bridged_mock, MOI.ListOfVariableIndices()) - MOI.set(bridged_mock, MOI.VariableName(), xy, var_names) - @testset "Test mock model" begin - abs = MOI.get(mock, MOI.ListOfVariableIndices()) - @test length(abs) == 3 - MOI.set(mock, MOI.VariableName(), abs[3], "s") - cquad = MOI.get( - mock, - MOI.ListOfConstraintIndices{ - MOI.ScalarQuadraticFunction{Float64}, - MOI.LessThan{Float64}, - }(), - ) - MOI.set(bridged_mock, MOI.ConstraintName(), cquad[1], "quad") - - s = """ - variables: x, y, s - x >= 1.0 - y >= 2.0 - quad: 1.0 * x * x + 1.0 * x * y + 1.0 * y * y + -1.0 * s <= 0.0 - minobjective: s - """ - model = MOIU.Model{Float64}() - MOIU.loadfromstring!(model, s) - MOIU.test_models_equal( - mock, - model, - [var_names; "s"], - ["quad"], - [ - ("x", MOI.GreaterThan{Float64}(1.0)), - ("y", MOI.GreaterThan{Float64}(2.0)), - ], - ) - end + return +end - bridged_var_names = ["x", "y"] - @testset "Test bridged model" begin - s = """ - variables: x, y - x >= 1.0 - y >= 2.0 - minobjective: 1.0 * x * x + 1.0 * x * y + 1.0 * y * y - """ - model = MOIU.Model{Float64}() - MOIU.loadfromstring!(model, s) - MOIU.test_models_equal( - bridged_mock, - model, - var_names, - String[], - [ - ("x", MOI.GreaterThan{Float64}(1.0)), - ("y", MOI.GreaterThan{Float64}(2.0)), - ], - ) - end +function test_SlackBridge_get_ObjectiveFunction_MIN() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], -1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test MOI.get(model, MOI.ObjectiveFunction{typeof(f)}()) ≈ f + return +end - err = ArgumentError( - "Objective bridge of type `$(MathOptInterface.Bridges.Objective.SlackBridge{Float64,MathOptInterface.ScalarQuadraticFunction{Float64},MathOptInterface.ScalarQuadraticFunction{Float64}})`" * - " does not support modifying the objective sense. As a workaround, set" * - " the sense to `MOI.FEASIBILITY_SENSE` to clear the objective function" * - " and bridges.", - ) - @test_throws err MOI.set(bridged_mock, MOI.ObjectiveSense(), MOI.MAX_SENSE) - obj = MOI.get( - bridged_mock, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - ) - MOI.set(bridged_mock, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) - @test !MOIB.is_objective_bridged(bridged_mock) - @test MOI.get(bridged_mock, MOI.ObjectiveSense()) == MOI.FEASIBILITY_SENSE - @test MOI.get(bridged_mock, MOI.ListOfModelAttributesSet()) == - [MOI.ObjectiveSense()] - MOI.set(bridged_mock, MOI.ObjectiveSense(), MOI.MAX_SENSE) - MOI.set( - bridged_mock, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - obj, - ) +function test_SlackBridge_get_ObjectiveFunction_MAX() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], -1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test MOI.get(model, MOI.ObjectiveFunction{typeof(f)}()) ≈ f + return +end - @testset "Test mock model" begin - abs = MOI.get(mock, MOI.ListOfVariableIndices()) - @test length(abs) == 3 - MOI.set(mock, MOI.VariableName(), abs[3], "s") - cquad = MOI.get( - mock, - MOI.ListOfConstraintIndices{ - MOI.ScalarQuadraticFunction{Float64}, - MOI.GreaterThan{Float64}, - }(), - ) - MOI.set(bridged_mock, MOI.ConstraintName(), cquad[1], "quad") +function test_SlackBridge_get_ObjectiveSense() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE + return +end - s = """ - variables: x, y, s - x >= 1.0 - y >= 2.0 - quad: 1.0 * x * x + 1.0 * x * y + 1.0 * y * y + -1.0 * s >= 0.0 - maxobjective: s - """ - model = MOIU.Model{Float64}() - MOIU.loadfromstring!(model, s) - MOIU.test_models_equal( - mock, - model, - [var_names; "s"], - ["quad"], - [ - ("x", MOI.GreaterThan{Float64}(1.0)), - ("y", MOI.GreaterThan{Float64}(2.0)), - ], - ) - end +function test_SlackBridge_NumberOfVariables() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], 1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test MOI.get(model, MOI.NumberOfVariables()) == 1 + @test MOI.get(inner, MOI.NumberOfVariables()) == 2 + MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) + @test MOI.get(model, MOI.NumberOfVariables()) == 1 + @test MOI.get(inner, MOI.NumberOfVariables()) == 1 + return +end - @testset "Test bridged model" begin - s = """ - variables: x, y - x >= 1.0 - y >= 2.0 - maxobjective: 1.0 * x * x + 1.0 * x * y + 1.0 * y * y - """ - model = MOIU.Model{Float64}() - MOIU.loadfromstring!(model, s) - MOIU.test_models_equal( - bridged_mock, - model, - var_names, - String[], - [ - ("x", MOI.GreaterThan{Float64}(1.0)), - ("y", MOI.GreaterThan{Float64}(2.0)), - ], - ) - end +function test_SlackBridge_ListOfVariableIndices() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], 1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test MOI.get(model, MOI.ListOfVariableIndices()) == [x] + @test length(MOI.get(inner, MOI.ListOfVariableIndices())) == 2 + MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) + @test MOI.get(model, MOI.ListOfVariableIndices()) == [x] + @test MOI.get(inner, MOI.ListOfVariableIndices()) == [x] + return +end - _test_delete_objective( - bridged_mock, - 2, - ( - (MOI.ScalarQuadraticFunction{Float64}, MOI.GreaterThan{Float64}, 0), - (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}, 0), - ), - ) +function test_SlackBridge_ListOfConstraintTypesPresent() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], 1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test MOI.get(model, MOI.ListOfConstraintTypesPresent()) == [] + @test MOI.get(inner, MOI.ListOfConstraintTypesPresent()) == + [(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64})] + MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) + @test MOI.get(model, MOI.ListOfConstraintTypesPresent()) == [] + @test MOI.get(inner, MOI.ListOfConstraintTypesPresent()) == [] + return end -@testset "QP" begin - @testset "QP1" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [4 / 7, 3 / 7, 6 / 7, 13 / 7], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [5 / 7, 6 / 7], - ), - ) - MOIT.qp1test(bridged_mock, config) - end - @testset "QP2" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [4 / 7, 3 / 7, 6 / 7, 13 / 7], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [5 / 7, 6 / 7], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [4 / 7, 3 / 7, 6 / 7, -2 * 13 / 7], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [10 / 7, 12 / 7], - ), - ) - MOIT.qp2test(bridged_mock, config) - end - @testset "QP3" begin - MOIU.set_mock_optimize!( - mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1 / 4, 3 / 4, 2.875], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [11 / 4], - (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => [-1.0], - ), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( - mock, - [1.0, 0.0, 3.0], - (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => - [-2.0], - (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0], - ), - ) - MOIT.qp3test(bridged_mock, config) - end +function test_SlackBridge_ListOfConstraintIndices() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], 1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + attr = MOI.ListOfConstraintIndices{ + MOI.ScalarAffineFunction{Float64}, + MOI.GreaterThan{Float64}, + }() + @test MOI.get(model, attr) == [] + @test length(MOI.get(inner, attr)) == 1 + MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) + @test MOI.get(model, attr) == [] + @test length(MOI.get(inner, attr)) == 0 + return end + +end # module + +TestObjectiveMap.runtests() From be8689a5dc1542fa050f707c8536e87dc21d40e2 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 20 Jul 2021 13:24:45 +1200 Subject: [PATCH 2/6] Fix MOIB --- test/Bridges/utilities.jl | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/test/Bridges/utilities.jl b/test/Bridges/utilities.jl index c05c00c552..f38ed9ae74 100644 --- a/test/Bridges/utilities.jl +++ b/test/Bridges/utilities.jl @@ -7,7 +7,7 @@ function _test_num_constraints(bridged_mock, F, S, n) end function _warn_incomplete_list_num_constraints(BT, list_num_constraints) - for (S,) in MOIB.added_constrained_variable_types(BT) + for (S,) in MOI.Bridges.added_constrained_variable_types(BT) F = MOIU.variable_function_type(S) if !any(c -> c[1] == F && c[2] == S, list_num_constraints) error( @@ -16,7 +16,7 @@ function _warn_incomplete_list_num_constraints(BT, list_num_constraints) ) end end - for (F, S) in MOIB.added_constraint_types(BT) + for (F, S) in MOI.Bridges.added_constraint_types(BT) if !any(c -> c[1] == F && c[2] == S, list_num_constraints) error( "Bridges of type $BT add $F-in-$S constraints but their " * @@ -29,7 +29,7 @@ end """ _test_delete_bridge( - m::MOIB.AbstractBridgeOptimizer, + m::MOI.Bridges.AbstractBridgeOptimizer, ci::MOI.ConstraintIndex{F, S}, nvars::Int, list_num_constraints::Tuple; @@ -51,7 +51,7 @@ Test deletion of the constraint `ci` in model `m`. * The number of `F`-in-`S` constraints is `num_bridged`. """ function _test_delete_bridge( - m::MOIB.AbstractBridgeOptimizer, + m::MOI.Bridges.AbstractBridgeOptimizer, ci::MOI.ConstraintIndex{F,S}, nvars::Int, list_num_constraints::Tuple; @@ -59,11 +59,11 @@ function _test_delete_bridge( num_bridged = 1, ) where {F,S} _warn_incomplete_list_num_constraints( - typeof(MOIB.bridge(m, ci)), + typeof(MOI.Bridges.bridge(m, ci)), list_num_constraints, ) function num_bridges() - return count(bridge -> true, values(MOIB.Constraint.bridges(m))) + return count(bridge -> true, values(MOI.Bridges.Constraint.bridges(m))) end start_num_bridges = num_bridges() @test MOI.get(m, MOI.NumberOfVariables()) == nvars @@ -90,7 +90,7 @@ end # Test deletion of variable bridge used for variable `vi` function _test_delete_bridged_variable( - m::MOIB.AbstractBridgeOptimizer, + m::MOI.Bridges.AbstractBridgeOptimizer, vi::MOI.VariableIndex, S::Type, nvars::Int, @@ -100,11 +100,11 @@ function _test_delete_bridged_variable( used_constraints = 1, ) _warn_incomplete_list_num_constraints( - typeof(MOIB.bridge(m, vi)), + typeof(MOI.Bridges.bridge(m, vi)), list_num_constraints, ) function num_bridges() - return count(bridge -> true, values(MOIB.Variable.bridges(m))) + return count(bridge -> true, values(MOI.Bridges.Variable.bridges(m))) end start_num_bridges = num_bridges() @test MOI.get(m, MOI.NumberOfVariables()) == nvars @@ -136,7 +136,7 @@ end # Test deletion of variable bridge used for vector of variables `vis` function _test_delete_bridged_variables( - m::MOIB.AbstractBridgeOptimizer, + m::MOI.Bridges.AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex}, S::Type, nvars::Int, @@ -145,11 +145,11 @@ function _test_delete_bridged_variables( num_bridged = 1, ) _warn_incomplete_list_num_constraints( - typeof(MOIB.bridge(m, vis[1])), + typeof(MOI.Bridges.bridge(m, vis[1])), list_num_constraints, ) function num_bridges() - return count(bridge -> true, values(MOIB.Variable.bridges(m))) + return count(bridge -> true, values(MOI.Bridges.Variable.bridges(m))) end start_num_bridges = num_bridges() @test MOI.get(m, MOI.NumberOfVariables()) == nvars @@ -180,17 +180,19 @@ function _test_delete_bridged_variables( end function _test_delete_objective( - m::MOIB.AbstractBridgeOptimizer, + m::MOI.Bridges.AbstractBridgeOptimizer, nvars::Int, list_num_constraints::Tuple; used_bridges = 1, ) _warn_incomplete_list_num_constraints( - typeof(MOIB.Objective.root_bridge(MOIB.Objective.bridges(m))), + typeof( + MOI.Bridges.Objective.root_bridge(MOI.Bridges.Objective.bridges(m)), + ), list_num_constraints, ) function num_bridges() - return length(MOIB.Objective.bridges(m)) + return length(MOI.Bridges.Objective.bridges(m)) end start_num_bridges = num_bridges() @test MOI.get(m, MOI.NumberOfVariables()) == nvars From 672b64875d0f73a097447e2e9147aba2ae0e6db0 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 20 Jul 2021 14:42:53 +1200 Subject: [PATCH 3/6] Improve coverage --- test/Bridges/Objective/slack.jl | 73 +++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/test/Bridges/Objective/slack.jl b/test/Bridges/Objective/slack.jl index 495b026944..eeb7a58dc1 100644 --- a/test/Bridges/Objective/slack.jl +++ b/test/Bridges/Objective/slack.jl @@ -31,6 +31,38 @@ function test_SlackBridge_ObjectiveSense_error() return end +function test_SlackBridge_ObjectiveSense_modify() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], -1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test_throws( + ArgumentError, + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE), + ) + MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test MOI.get(model, MOI.ObjectiveFunction{typeof(f)}()) ≈ f + return +end + +function test_SlackBridge_ObjectiveFunction_modify() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], -1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test MOI.get(model, MOI.ObjectiveFunction{typeof(f)}()) ≈ f + g = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(-1.1, x)], 1.2) + MOI.set(model, MOI.ObjectiveFunction{typeof(g)}(), g) + @test MOI.get(model, MOI.ObjectiveFunction{typeof(g)}()) ≈ g + return +end + function test_SlackBridge_get_ObjectiveFunction_MIN() inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) model = MOI.Bridges.Objective.Slack{Float64}(inner) @@ -78,6 +110,24 @@ function test_SlackBridge_NumberOfVariables() return end +function test_SlackBridge_ListOfModelAttributesSet() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], 1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + attr = MOI.get(model, MOI.ListOfModelAttributesSet()) + @test length(attr) == 2 + @test MOI.ObjectiveSense() in attr + @test MOI.ObjectiveFunction{typeof(f)}() in attr + attr = MOI.get(inner, MOI.ListOfModelAttributesSet()) + @test length(attr) == 2 + @test MOI.ObjectiveSense() in attr + @test MOI.ObjectiveFunction{MOI.SingleVariable}() in attr + return +end + function test_SlackBridge_ListOfVariableIndices() inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) model = MOI.Bridges.Objective.Slack{Float64}(inner) @@ -122,12 +172,35 @@ function test_SlackBridge_ListOfConstraintIndices() }() @test MOI.get(model, attr) == [] @test length(MOI.get(inner, attr)) == 1 + attr = MOI.ListOfConstraintIndices{ + MOI.ScalarAffineFunction{Float64}, + MOI.LessThan{Float64}, + }() + @test MOI.get(model, attr) == [] + @test length(MOI.get(inner, attr)) == 0 MOI.set(model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) @test MOI.get(model, attr) == [] @test length(MOI.get(inner, attr)) == 0 return end +function test_SlackBridge_ObjectiveFunctionValue() + inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Objective.Slack{Float64}(inner) + x = MOI.add_variable(model) + MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(2.0)) + f = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.1, x)], -1.2) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.Utilities.set_mock_optimize!( + inner, + mock -> MOI.Utilities.mock_optimize!(mock, [2.0, 1.0]), + ) + MOI.optimize!(model) + @test MOI.get(model, MOI.ObjectiveValue()) ≈ 1.0 + return +end + end # module TestObjectiveMap.runtests() From cc5ba4e9e7c03819c663d689bdc689a5c79a3ec5 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 20 Jul 2021 14:51:43 +1200 Subject: [PATCH 4/6] Fix typo --- test/Bridges/Objective/slack.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Bridges/Objective/slack.jl b/test/Bridges/Objective/slack.jl index eeb7a58dc1..c4e5a43af0 100644 --- a/test/Bridges/Objective/slack.jl +++ b/test/Bridges/Objective/slack.jl @@ -1,4 +1,4 @@ -module TestObjectiveMap +module TestObjectiveSlack using Test @@ -203,4 +203,4 @@ end end # module -TestObjectiveMap.runtests() +TestObjectiveSlack.runtests() From b7bc8631436622a4c4c13fe9c6988902471a8f03 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 21 Jul 2021 15:57:51 +1200 Subject: [PATCH 5/6] Add old tests --- test/Bridges/Objective/slack.jl | 240 ++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/test/Bridges/Objective/slack.jl b/test/Bridges/Objective/slack.jl index c4e5a43af0..8e3a03e3e4 100644 --- a/test/Bridges/Objective/slack.jl +++ b/test/Bridges/Objective/slack.jl @@ -16,6 +16,8 @@ function runtests() return end +include("../utilities.jl") + function test_SlackBridge_ObjectiveSense_error() inner = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) model = MOI.Bridges.Objective.Slack{Float64}(inner) @@ -201,6 +203,244 @@ function test_SlackBridge_ObjectiveFunctionValue() return end +function test_original() + mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + bridged_mock = MOI.Bridges.Objective.Slack{Float64}(mock) + MOI.Utilities.set_mock_optimize!( + mock, + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0, 5.0]), + ), + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0]), + ), + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0, 2.0]), + ), + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + MOI.OPTIMAL, + (MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0]), + ), + ) + MOI.Test.test_objective_qp_ObjectiveFunction_edge_cases( + bridged_mock, + MOI.Test.Config(), + ) + + @test MOI.Bridges.is_objective_bridged(bridged_mock) + @test MOI.get(bridged_mock, MOI.ObjectiveFunctionType()) == + MOI.ScalarQuadraticFunction{Float64} + @test MOI.get(bridged_mock, MOI.ListOfModelAttributesSet()) == [ + MOI.ObjectiveSense(), + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + ] + + var_names = ["x", "y"] + xy = MOI.get(bridged_mock, MOI.ListOfVariableIndices()) + MOI.set(bridged_mock, MOI.VariableName(), xy, var_names) + abs = MOI.get(mock, MOI.ListOfVariableIndices()) + @test length(abs) == 3 + MOI.set(mock, MOI.VariableName(), abs[3], "s") + cquad = MOI.get( + mock, + MOI.ListOfConstraintIndices{ + MOI.ScalarQuadraticFunction{Float64}, + MOI.LessThan{Float64}, + }(), + ) + MOI.set(bridged_mock, MOI.ConstraintName(), cquad[1], "quad") + + s = """ + variables: x, y, s + x >= 1.0 + y >= 2.0 + quad: 1.0 * x * x + 1.0 * x * y + 1.0 * y * y + -1.0 * s <= 0.0 + minobjective: s + """ + model = MOI.Utilities.Model{Float64}() + MOI.Utilities.loadfromstring!(model, s) + MOI.Utilities.test_models_equal( + mock, + model, + [var_names; "s"], + ["quad"], + [ + ("x", MOI.GreaterThan{Float64}(1.0)), + ("y", MOI.GreaterThan{Float64}(2.0)), + ], + ) + bridged_var_names = ["x", "y"] + s = """ + variables: x, y + x >= 1.0 + y >= 2.0 + minobjective: 1.0 * x * x + 1.0 * x * y + 1.0 * y * y + """ + model = MOI.Utilities.Model{Float64}() + MOI.Utilities.loadfromstring!(model, s) + MOI.Utilities.test_models_equal( + bridged_mock, + model, + var_names, + String[], + [ + ("x", MOI.GreaterThan{Float64}(1.0)), + ("y", MOI.GreaterThan{Float64}(2.0)), + ], + ) + err = ArgumentError( + "Objective bridge of type `$(MathOptInterface.Bridges.Objective.SlackBridge{Float64,MathOptInterface.ScalarQuadraticFunction{Float64},MathOptInterface.ScalarQuadraticFunction{Float64}})`" * + " does not support modifying the objective sense. As a workaround, set" * + " the sense to `MOI.FEASIBILITY_SENSE` to clear the objective function" * + " and bridges.", + ) + @test_throws err MOI.set(bridged_mock, MOI.ObjectiveSense(), MOI.MAX_SENSE) + obj = MOI.get( + bridged_mock, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + ) + MOI.set(bridged_mock, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) + @test !MOI.Bridges.is_objective_bridged(bridged_mock) + @test MOI.get(bridged_mock, MOI.ObjectiveSense()) == MOI.FEASIBILITY_SENSE + @test MOI.get(bridged_mock, MOI.ListOfModelAttributesSet()) == + [MOI.ObjectiveSense()] + MOI.set(bridged_mock, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set( + bridged_mock, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + obj, + ) + abs = MOI.get(mock, MOI.ListOfVariableIndices()) + @test length(abs) == 3 + MOI.set(mock, MOI.VariableName(), abs[3], "s") + cquad = MOI.get( + mock, + MOI.ListOfConstraintIndices{ + MOI.ScalarQuadraticFunction{Float64}, + MOI.GreaterThan{Float64}, + }(), + ) + MOI.set(bridged_mock, MOI.ConstraintName(), cquad[1], "quad") + + s = """ + variables: x, y, s + x >= 1.0 + y >= 2.0 + quad: 1.0 * x * x + 1.0 * x * y + 1.0 * y * y + -1.0 * s >= 0.0 + maxobjective: s + """ + model = MOI.Utilities.Model{Float64}() + MOI.Utilities.loadfromstring!(model, s) + MOI.Utilities.test_models_equal( + mock, + model, + [var_names; "s"], + ["quad"], + [ + ("x", MOI.GreaterThan{Float64}(1.0)), + ("y", MOI.GreaterThan{Float64}(2.0)), + ], + ) + s = """ + variables: x, y + x >= 1.0 + y >= 2.0 + maxobjective: 1.0 * x * x + 1.0 * x * y + 1.0 * y * y + """ + model = MOI.Utilities.Model{Float64}() + MOI.Utilities.loadfromstring!(model, s) + MOI.Utilities.test_models_equal( + bridged_mock, + model, + var_names, + String[], + [ + ("x", MOI.GreaterThan{Float64}(1.0)), + ("y", MOI.GreaterThan{Float64}(2.0)), + ], + ) + _test_delete_objective( + bridged_mock, + 2, + ( + (MOI.ScalarQuadraticFunction{Float64}, MOI.GreaterThan{Float64}, 0), + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}, 0), + ), + ) + return +end + +function test_quadratic_integration() + mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + bridged_mock = MOI.Bridges.Objective.Slack{Float64}(mock) + MOI.Utilities.set_mock_optimize!( + mock, + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + [4 / 7, 3 / 7, 6 / 7, 13 / 7], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [5 / 7, 6 / 7], + ), + ) + MOI.Test.test_quadratic_integration(bridged_mock, MOI.Test.Config()) + return +end + +function test_quadratic_duplicate_terms() + mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + bridged_mock = MOI.Bridges.Objective.Slack{Float64}(mock) + MOI.Utilities.set_mock_optimize!( + mock, + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + [4 / 7, 3 / 7, 6 / 7, 13 / 7], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [5 / 7, 6 / 7], + ), + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + [4 / 7, 3 / 7, 6 / 7, -2 * 13 / 7], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [10 / 7, 12 / 7], + ), + ) + MOI.Test.test_quadratic_duplicate_terms(bridged_mock, MOI.Test.Config()) + return +end + +function test_() + mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) + bridged_mock = MOI.Bridges.Objective.Slack{Float64}(mock) + MOI.Utilities.set_mock_optimize!( + mock, + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + [1 / 4, 3 / 4, 2.875], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [11 / 4], + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}) => + [-1.0], + ), + (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( + mock, + [1.0, 0.0, 3.0], + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [-2.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => + [1.0], + ), + ) + MOI.Test.test_quadratic_nonhomogeneous(bridged_mock, MOI.Test.Config()) + return +end + end # module TestObjectiveSlack.runtests() From 077153b916eae05af2b76603483908fcc029a6e3 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 22 Jul 2021 10:12:09 +1200 Subject: [PATCH 6/6] Fix test name --- test/Bridges/Objective/slack.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Bridges/Objective/slack.jl b/test/Bridges/Objective/slack.jl index 8e3a03e3e4..c4bdb0c986 100644 --- a/test/Bridges/Objective/slack.jl +++ b/test/Bridges/Objective/slack.jl @@ -415,7 +415,7 @@ function test_quadratic_duplicate_terms() return end -function test_() +function test_quadratic_nonhomogeneous() mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) bridged_mock = MOI.Bridges.Objective.Slack{Float64}(mock) MOI.Utilities.set_mock_optimize!(