From c424201022c52c09de2517eb68afb1291d5d6ea9 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 27 Nov 2020 16:14:53 +1300 Subject: [PATCH] Add comprehensive tests for Farkas certificates (#1190) --- src/Test/UnitTests/solve.jl | 206 ++++++++++++++++++++++++++++++++++++ test/Test/unit.jl | 120 +++++++++++++++++++++ 2 files changed, 326 insertions(+) diff --git a/src/Test/UnitTests/solve.jl b/src/Test/UnitTests/solve.jl index 3c73c3b4d1..e39aa480b2 100644 --- a/src/Test/UnitTests/solve.jl +++ b/src/Test/UnitTests/solve.jl @@ -173,6 +173,212 @@ function solve_result_index(model::MOI.ModelLike, config::TestConfig) end unittests["solve_result_index"] = solve_result_index +function solve_farkas_equalto_upper(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + clb = MOI.add_constraint.( + model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0) + ) + c = MOI.add_constraint( + model, + 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 +end +unittests["solve_farkas_equalto_upper"] = solve_farkas_equalto_upper + +function solve_farkas_equalto_lower(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + clb = MOI.add_constraint.( + model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0) + ) + c = MOI.add_constraint( + model, + 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 +end +unittests["solve_farkas_equalto_lower"] = solve_farkas_equalto_lower + +function solve_farkas_lessthan(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + clb = MOI.add_constraint.( + model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0) + ) + c = MOI.add_constraint( + model, + 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 +end +unittests["solve_farkas_lessthan"] = solve_farkas_lessthan + +function solve_farkas_greaterthan(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + clb = MOI.add_constraint.( + model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0) + ) + c = MOI.add_constraint( + model, + 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 +end +unittests["solve_farkas_greaterthan"] = solve_farkas_greaterthan + +function solve_farkas_interval_upper(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + clb = MOI.add_constraint.( + model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0) + ) + c = MOI.add_constraint( + model, + 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 +end +unittests["solve_farkas_interval_upper"] = solve_farkas_interval_upper + +function solve_farkas_interval_lower(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + clb = MOI.add_constraint.( + model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0) + ) + c = MOI.add_constraint( + model, + 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 +end +unittests["solve_farkas_interval_lower"] = solve_farkas_interval_lower + +function solve_farkas_variable_lessthan(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + clb = MOI.add_constraint.( + model, MOI.SingleVariable.(x), MOI.LessThan(0.0) + ) + c = MOI.add_constraint( + model, + 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 +end +unittests["solve_farkas_variable_lessthan"] = solve_farkas_variable_lessthan + +function solve_farkas_variable_lessthan_max(model::MOI.ModelLike, config::TestConfig) + MOI.empty!(model) + x = MOI.add_variables(model, 2) + clb = MOI.add_constraint.( + model, MOI.SingleVariable.(x), MOI.LessThan(0.0) + ) + c = MOI.add_constraint( + model, + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0), + MOI.GreaterThan(1.0), + ) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.set( + model, + 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 +end +unittests["solve_farkas_variable_lessthan_max"] = solve_farkas_variable_lessthan_max + function solve_twice(model::MOI.ModelLike, config::TestConfig) MOI.empty!(model) x = MOI.add_variable(model) diff --git a/test/Test/unit.jl b/test/Test/unit.jl index 5596d802e3..8cfe461be3 100644 --- a/test/Test/unit.jl +++ b/test/Test/unit.jl @@ -49,6 +49,14 @@ end "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", ]) MOI.empty!(model) @@ -394,6 +402,118 @@ end 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!(