diff --git a/src/Test/Test.jl b/src/Test/Test.jl index b4ff5aaa37..f774a6203a 100644 --- a/src/Test/Test.jl +++ b/src/Test/Test.jl @@ -12,6 +12,7 @@ mutable struct Config{T<:Real} atol::T rtol::T optimal_status::MOI.TerminationStatusCode + infeasible_status::MOI.TerminationStatusCode exclude::Vector{Any} end @@ -21,6 +22,7 @@ end atol::Real = Base.rtoldefault(T), rtol::Real = Base.rtoldefault(T), optimal_status::MOI.TerminationStatusCode = MOI.OPTIMAL, + infeasible_status::MOI.TerminationStatusCode = MOI.INFEASIBLE, exclude::Vector{Any} = Any[], ) where {T} @@ -34,6 +36,8 @@ Return an object that is used to configure various tests. when comparing solutions. * `optimal_status = MOI.OPTIMAL`: Set to `MOI.LOCALLY_SOLVED` if the solver cannot prove global optimality. + * `infeasible_status = MOI.INFEASIBLE`: Set to `MOI.LOCALLY_INFEASIBLE` if the + solver cannot prove global infeasibility. * `exclude = Vector{Any}`: Pass attributes or functions to `exclude` to skip parts of tests that require certain functionality. Common arguments include: - `MOI.delete` to skip deletion-related tests @@ -64,9 +68,10 @@ function Config( atol::Real = Base.rtoldefault(T), rtol::Real = Base.rtoldefault(T), optimal_status::MOI.TerminationStatusCode = MOI.OPTIMAL, + infeasible_status::MOI.TerminationStatusCode = MOI.INFEASIBLE, exclude::Vector{Any} = Any[], ) where {T<:Real} - return Config{T}(atol, rtol, optimal_status, exclude) + return Config{T}(atol, rtol, optimal_status, infeasible_status, exclude) end function Base.copy(config::Config{T}) where {T} @@ -74,6 +79,7 @@ function Base.copy(config::Config{T}) where {T} config.atol, config.rtol, config.optimal_status, + config.infeasible_status, copy(config.exclude), ) end diff --git a/src/Test/test_conic.jl b/src/Test/test_conic.jl index fbd1a37f49..bed8b1d5ea 100644 --- a/src/Test/test_conic.jl +++ b/src/Test/test_conic.jl @@ -521,7 +521,7 @@ function test_conic_linear_INFEASIBLE(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED MOI.optimize!(model) @test MOI.get(model, MOI.TerminationStatus()) in - [MOI.INFEASIBLE, MOI.INFEASIBLE_OR_UNBOUNDED] + [config.infeasible_status, MOI.INFEASIBLE_OR_UNBOUNDED] # TODO test dual feasibility and objective sign end return @@ -597,7 +597,7 @@ function test_conic_linear_INFEASIBLE_2(model::MOI.ModelLike, config::Config) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED MOI.optimize!(model) @test MOI.get(model, MOI.TerminationStatus()) in - [MOI.INFEASIBLE, MOI.INFEASIBLE_OR_UNBOUNDED] + [config.infeasible_status, MOI.INFEASIBLE_OR_UNBOUNDED] # TODO test dual feasibility and objective sign end return @@ -928,7 +928,8 @@ function test_conic_NormInfinityCone_INFEASIBLE( if _supports(config, MOI.optimize!) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == + config.infeasible_status @test MOI.get(model, MOI.PrimalStatus()) in (MOI.NO_SOLUTION, MOI.INFEASIBLE_POINT) # TODO test dual feasibility and objective sign @@ -1351,7 +1352,8 @@ function test_conic_NormOneCone_INFEASIBLE(model::MOI.ModelLike, config::Config) if _supports(config, MOI.optimize!) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == + config.infeasible_status @test MOI.get(model, MOI.PrimalStatus()) in (MOI.NO_SOLUTION, MOI.INFEASIBLE_POINT) # TODO test dual feasibility and objective sign @@ -2005,7 +2007,8 @@ function test_conic_SecondOrderCone_INFEASIBLE( if _supports(config, MOI.optimize!) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == + config.infeasible_status @test MOI.get(model, MOI.PrimalStatus()) in (MOI.NO_SOLUTION, MOI.INFEASIBLE_POINT) # TODO test dual feasibility and objective sign @@ -2445,7 +2448,7 @@ function test_conic_RotatedSecondOrderCone_INFEASIBLE( @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED MOI.optimize!(model) @test MOI.get(model, MOI.TerminationStatus()) in - [MOI.INFEASIBLE, MOI.INFEASIBLE_OR_UNBOUNDED] + [config.infeasible_status, MOI.INFEASIBLE_OR_UNBOUNDED] has_certificate = MOI.get(model, MOI.DualStatus()) in [ MOI.INFEASIBILITY_CERTIFICATE, MOI.NEARLY_INFEASIBILITY_CERTIFICATE, diff --git a/src/Test/test_constraint.jl b/src/Test/test_constraint.jl index 7b315a3933..57cca1aa75 100644 --- a/src/Test/test_constraint.jl +++ b/src/Test/test_constraint.jl @@ -609,7 +609,7 @@ function test_constraint_ZeroOne_bounds_3(model::MOI.ModelLike, config::Config) ) x = MOI.get(model, MOI.VariableIndex, "x") MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status return end diff --git a/src/Test/test_infeasibility_certificates.jl b/src/Test/test_infeasibility_certificates.jl index 560dbb8c99..c9d20458ef 100644 --- a/src/Test/test_infeasibility_certificates.jl +++ b/src/Test/test_infeasibility_certificates.jl @@ -76,7 +76,8 @@ for sense in (MOI.MIN_SENSE, MOI.MAX_SENSE), offset in (0.0, 1.2) cu = MOI.add_constraint(model, 1.0 * x, MOI.LessThan(1.4)) MOI.optimize!(model) @requires( - MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE, + MOI.get(model, MOI.TerminationStatus()) == + config.infeasible_status, ) @requires( MOI.get(model, MOI.DualStatus()) == @@ -137,7 +138,8 @@ for sense in (MOI.MIN_SENSE, MOI.MAX_SENSE), offset in (0.0, 1.2) cu = MOI.add_constraint(model, x, MOI.LessThan(1.4)) MOI.optimize!(model) @requires( - MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE, + MOI.get(model, MOI.TerminationStatus()) == + config.infeasible_status, ) @requires( MOI.get(model, MOI.DualStatus()) == diff --git a/src/Test/test_linear.jl b/src/Test/test_linear.jl index 8c3c9bd9c7..0610da3c05 100644 --- a/src/Test/test_linear.jl +++ b/src/Test/test_linear.jl @@ -1432,9 +1432,8 @@ function test_linear_INFEASIBLE( if _supports(config, MOI.optimize!) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE || - MOI.get(model, MOI.TerminationStatus()) == - MOI.INFEASIBLE_OR_UNBOUNDED + @test MOI.get(model, MOI.TerminationStatus()) in + (config.infeasible_status, MOI.INFEASIBLE_OR_UNBOUNDED) has_certificate = MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE if _supports(config, MOI.ConstraintDual) && has_certificate @@ -2334,9 +2333,8 @@ function test_linear_INFEASIBLE_2( if _supports(config, MOI.optimize!) @test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE || - MOI.get(model, MOI.TerminationStatus()) == - MOI.INFEASIBLE_OR_UNBOUNDED + @test MOI.get(model, MOI.TerminationStatus()) in + (config.infeasible_status, MOI.INFEASIBLE_OR_UNBOUNDED) has_certificate = MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE if _supports(config, MOI.ConstraintDual) && has_certificate @@ -3982,9 +3980,8 @@ function _test_linear_SemiXXX_integration( MOI.set(model, MOI.ConstraintSet(), vc2, MOI.EqualTo(T(4))) if _supports(config, MOI.optimize!) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE || - MOI.get(model, MOI.TerminationStatus()) == - MOI.INFEASIBLE_OR_UNBOUNDED + @test MOI.get(model, MOI.TerminationStatus()) in + (config.infeasible_status, MOI.INFEASIBLE_OR_UNBOUNDED) end return end diff --git a/src/Test/test_solve.jl b/src/Test/test_solve.jl index 75f62ad2d6..fc39096c38 100644 --- a/src/Test/test_solve.jl +++ b/src/Test/test_solve.jl @@ -426,7 +426,7 @@ function test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_upper( MOI.EqualTo(-1.0), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status if MOI.get(model, MOI.DualStatus()) != MOI.INFEASIBILITY_CERTIFICATE return end @@ -481,7 +481,7 @@ function test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_EqualTo_lower( MOI.EqualTo(1.0), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status if MOI.get(model, MOI.DualStatus()) != MOI.INFEASIBILITY_CERTIFICATE return end @@ -535,7 +535,7 @@ function test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_LessThan( MOI.LessThan(-1.0), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status if MOI.get(model, MOI.DualStatus()) != MOI.INFEASIBILITY_CERTIFICATE return end @@ -590,7 +590,7 @@ function test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_GreaterThan( MOI.GreaterThan(1.0), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status if MOI.get(model, MOI.DualStatus()) != MOI.INFEASIBILITY_CERTIFICATE return end @@ -645,7 +645,7 @@ function test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_upper( MOI.Interval(-2.0, -1.0), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status if MOI.get(model, MOI.DualStatus()) != MOI.INFEASIBILITY_CERTIFICATE return end @@ -700,7 +700,7 @@ function test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_Interval_lower( MOI.Interval(1.0, 2.0), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status if MOI.get(model, MOI.DualStatus()) != MOI.INFEASIBILITY_CERTIFICATE return end @@ -755,7 +755,7 @@ function test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_VariableIndex_LessThan( MOI.GreaterThan(1.0), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status if MOI.get(model, MOI.DualStatus()) != MOI.INFEASIBILITY_CERTIFICATE return end @@ -814,7 +814,7 @@ function test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_VariableIndex_LessThan_ MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) MOI.set(model, MOI.ObjectiveFunction{MOI.VariableIndex}(), x[1]) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status if MOI.get(model, MOI.DualStatus()) != MOI.INFEASIBILITY_CERTIFICATE return end @@ -910,7 +910,7 @@ function test_solve_conflict_bound_bound( @test MOI.get(model, MOI.ConflictStatus()) == MOI.COMPUTE_CONFLICT_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status MOI.compute_conflict!(model) @test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND @test MOI.get(model, MOI.ConstraintConflictStatus(), c1) == MOI.IN_CONFLICT @@ -974,7 +974,7 @@ function test_solve_conflict_two_affine( @test MOI.get(model, MOI.ConflictStatus()) == MOI.COMPUTE_CONFLICT_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status MOI.compute_conflict!(model) @test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND @test MOI.get(model, MOI.ConstraintConflictStatus(), c1) == MOI.IN_CONFLICT @@ -1033,7 +1033,7 @@ function test_solve_conflict_invalid_interval( @test MOI.get(model, MOI.ConflictStatus()) == MOI.COMPUTE_CONFLICT_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status MOI.compute_conflict!(model) @test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND @test MOI.get(model, MOI.ConstraintConflictStatus(), c1) == MOI.IN_CONFLICT @@ -1095,7 +1095,7 @@ function test_solve_conflict_affine_affine( @test MOI.get(model, MOI.ConflictStatus()) == MOI.COMPUTE_CONFLICT_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status MOI.compute_conflict!(model) @test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND @test MOI.get(model, MOI.ConstraintConflictStatus(), b1) == MOI.IN_CONFLICT @@ -1169,7 +1169,7 @@ function test_solve_conflict_EqualTo( @test MOI.get(model, MOI.ConflictStatus()) == MOI.COMPUTE_CONFLICT_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status MOI.compute_conflict!(model) @test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND @test MOI.get(model, MOI.ConstraintConflictStatus(), b1) == MOI.IN_CONFLICT @@ -1243,7 +1243,7 @@ function test_solve_conflict_NOT_IN_CONFLICT( @test MOI.get(model, MOI.ConflictStatus()) == MOI.COMPUTE_CONFLICT_NOT_CALLED MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status MOI.compute_conflict!(model) @test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND @test MOI.get(model, MOI.ConstraintConflictStatus(), b1) == MOI.IN_CONFLICT @@ -1363,7 +1363,7 @@ function test_solve_conflict_zeroone( MOI.GreaterThan(T(2)), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status MOI.compute_conflict!(model) @test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND zeroone_conflict = MOI.get(model, MOI.ConstraintConflictStatus(), c1) @@ -1426,7 +1426,7 @@ function test_solve_conflict_zeroone_2( MOI.EqualTo(one(T) / T(2)), ) MOI.optimize!(model) - @test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE + @test MOI.get(model, MOI.TerminationStatus()) == config.infeasible_status MOI.compute_conflict!(model) @test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND zeroone_conflict = MOI.get(model, MOI.ConstraintConflictStatus(), c1)