diff --git a/src/DeprecatedTest/contlinear.jl b/src/DeprecatedTest/contlinear.jl index e524740993..9a013f9dc4 100644 --- a/src/DeprecatedTest/contlinear.jl +++ b/src/DeprecatedTest/contlinear.jl @@ -263,9 +263,6 @@ function linear1test(model::MOI.ModelLike, config::Config{T}) where {T} end @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [-1, 0, 2] atol = atol rtol = rtol - if config.duals - @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT - end end # put lb of x back to 0 and fix z to zero to get : # max x + 2z diff --git a/src/Test/test_conic.jl b/src/Test/test_conic.jl index c8176d3222..09a466dec6 100644 --- a/src/Test/test_conic.jl +++ b/src/Test/test_conic.jl @@ -2488,7 +2488,8 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.INFEASIBLE, - tuple(), + MOI.NO_SOLUTION, + MOI.INFEASIBILITY_CERTIFICATE, (MOI.SingleVariable, MOI.LessThan{Float64}) => [-1], (MOI.SingleVariable, MOI.EqualTo{Float64}) => [-1], (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1], diff --git a/src/Test/test_linear.jl b/src/Test/test_linear.jl index 604ee3ab5f..b351c1fefc 100644 --- a/src/Test/test_linear.jl +++ b/src/Test/test_linear.jl @@ -265,9 +265,6 @@ function test_linear_integration( end @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [-1, 0, 2] atol = atol rtol = rtol - if _supports(config, MOI.ConstraintDual) - @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT - end end # put lb of x back to 0 and fix z to zero to get : # max x + 2z @@ -659,10 +656,10 @@ function setup_test( [1, 0], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], ], - var_basis = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], + variable_basis_status = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], ), ) return @@ -816,18 +813,18 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [3], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.NONBASIC], ], - var_basis = [MOI.BASIC], + variable_basis_status = [MOI.BASIC], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.NONBASIC_AT_UPPER], + variable_basis_status = [MOI.NONBASIC_AT_UPPER], ), ) return @@ -1510,7 +1507,8 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.INFEASIBLE, - tuple(), + MOI.NO_SOLUTION, + MOI.INFEASIBILITY_CERTIFICATE, (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], ), @@ -1828,11 +1826,11 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [650 / 11, 400 / 11], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC, MOI.NONBASIC], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], ), ) return @@ -2041,20 +2039,20 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [5.0, 5.0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [2.5, 2.5], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1], ), @@ -2063,20 +2061,20 @@ function setup_test( [1.0, 1.0], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [6.0, 6.0], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], ), ) return @@ -2186,10 +2184,13 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [0.0, 0.0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_LOWER], + variable_basis_status = [ + MOI.NONBASIC_AT_LOWER, + MOI.NONBASIC_AT_LOWER, + ], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [0], ), @@ -2431,7 +2432,8 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.INFEASIBLE, - tuple(), + MOI.NO_SOLUTION, + MOI.INFEASIBILITY_CERTIFICATE, (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1, -1], ), @@ -2699,10 +2701,10 @@ function setup_test( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [0, 1 / 2, 1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], ], - var_basis = [ + variable_basis_status = [ MOI.NONBASIC_AT_LOWER, MOI.BASIC, MOI.NONBASIC_AT_UPPER, diff --git a/src/Utilities/mockoptimizer.jl b/src/Utilities/mockoptimizer.jl index 876bdb7d0e..d8b5c88f21 100644 --- a/src/Utilities/mockoptimizer.jl +++ b/src/Utilities/mockoptimizer.jl @@ -10,9 +10,7 @@ struct MockConstraintAttribute <: MOI.AbstractConstraintAttribute end # A mock optimizer used for testing. mutable struct MockOptimizer{MT<:MOI.ModelLike} <: MOI.AbstractOptimizer inner_model::MT - attribute::Int # MockModelAttribute - varattribute::Dict{MOI.VariableIndex,Int} # MockVariableAttribute - conattribute::Dict{MOI.ConstraintIndex,Int} # MockConstraintAttribute + # Flags supports_names::Bool # Allows to test with optimizer not supporting names needs_allocate_load::Bool # Allows to tests the Allocate-Load interface, see copy_to add_var_allowed::Bool # If false, the optimizer throws AddVariableNotAllowed @@ -20,49 +18,52 @@ mutable struct MockOptimizer{MT<:MOI.ModelLike} <: MOI.AbstractOptimizer modify_allowed::Bool # If false, the optimizer throws Modify...NotAllowed delete_allowed::Bool # If false, the optimizer throws DeleteNotAllowed scalar_function_constant_non_zero::Bool - optimize!::Function - solved::Bool - hasprimal::Bool - hasdual::Bool - result_count::Int - terminationstatus::MOI.TerminationStatusCode - conflictstatus::MOI.ConflictStatusCode # Computes `ObjectiveValue` by evaluating the `ObjectiveFunction` with # `VariablePrimal`. See `get_fallback`. eval_objective_value::Bool - objective_value::Dict{Int,Float64} # set this using MOI.set(model, MOI.ObjectiveValue(), value) # Computes `DualObjectiveValue` using `get_fallback` eval_dual_objective_value::Bool - dual_objective_value::Dict{Int,Float64} # set this using MOI.set(model, MOI.DualObjectiveValue(), value) - primal_status::Dict{Int,MOI.ResultStatusCode} - dual_status::Dict{Int,MOI.ResultStatusCode} - constraint_conflict_status::Dict{ - MOI.ConstraintIndex, - MOI.ConflictParticipationStatusCode, - } - varprimal::Dict{MOI.VariableIndex,Dict{Int,Float64}} - callback_variable_primal::Dict{MOI.VariableIndex,Float64} # Computes `ConstraintDual` of constraints with `SingleVariable` or # `VectorOfVariables` functions by evaluating the `ConstraintDual` of # constraints having the variable in the function. See `get_fallback`. eval_variable_constraint_dual::Bool - condual::Dict{MOI.ConstraintIndex,Dict{Int,Any}} - con_basis::Dict{MOI.ConstraintIndex,Dict{Int,MOI.BasisStatusCode}} - var_basis::Dict{MOI.VariableIndex,Dict{Int,MOI.BasisStatusCode}} + # Attributes # The attributes set by `MOI.optimize!` cannot be set to `model`. # We detect them with `is_set_by_optimize` and store them in the following: optimizer_attributes::Dict{MOI.AbstractOptimizerAttribute,Any} model_attributes::Dict{MOI.AbstractModelAttribute,Any} submitted::Dict{MOI.AbstractSubmittable,Vector{Tuple}} + mock_model_attribute::Int + mock_variable_attribute::Dict{MOI.VariableIndex,Int} + mock_constraint_attribute::Dict{MOI.ConstraintIndex,Int} + # + optimize!::Function + optimize_called::Bool + termination_status::MOI.TerminationStatusCode + result_count::Int + objective_value::Dict{Int,Float64} + dual_objective_value::Dict{Int,Float64} + # Primal solution + primal_status::Dict{Int,MOI.ResultStatusCode} + variable_primal::Dict{MOI.VariableIndex,Dict{Int,Float64}} + callback_variable_primal::Dict{MOI.VariableIndex,Float64} + # Dual solution + dual_status::Dict{Int,MOI.ResultStatusCode} + constraint_dual::Dict{MOI.ConstraintIndex,Dict{Int,Any}} + # Constraint conflicts + conflict_status::MOI.ConflictStatusCode + constraint_conflict_status::Dict{ + MOI.ConstraintIndex, + MOI.ConflictParticipationStatusCode, + } + # Basis status + constraint_basis_status::Dict{ + MOI.ConstraintIndex, + Dict{Int,MOI.BasisStatusCode}, + } + variable_basis_status::Dict{MOI.VariableIndex,Dict{Int,MOI.BasisStatusCode}} end -# All user-facing indices are xor'd with this mask to produce unusual indices. -# This is good at catching bugs. -const internal_xor_mask = Int64(12345678) -xor_index(vi::VI) = VI(xor(vi.value, internal_xor_mask)) -xor_index(ci::CI{F,S}) where {F,S} = CI{F,S}(xor(ci.value, internal_xor_mask)) -xor_indices(x) = map_indices(xor_index, x) - function MockOptimizer( inner_model::MOI.ModelLike; supports_names = true, @@ -76,9 +77,7 @@ function MockOptimizer( ) return MockOptimizer( inner_model, - 0, - Dict{MOI.VariableIndex,Int}(), - Dict{MOI.ConstraintIndex,Int}(), + # Flags supports_names, needs_allocate_load, add_var_allowed, @@ -86,32 +85,50 @@ function MockOptimizer( true, true, scalar_function_constant_non_zero, - (::MockOptimizer) -> begin end, - false, - false, + eval_objective_value, + eval_dual_objective_value, + eval_variable_constraint_dual, + # Attributes + Dict{MOI.AbstractOptimizerAttribute,Any}(), + Dict{MOI.AbstractModelAttribute,Any}(), + Dict{MOI.AbstractSubmittable,Vector{Tuple}}(), + 0, + Dict{MOI.VariableIndex,Int}(), + Dict{MOI.ConstraintIndex,Int}(), + # + (::MockOptimizer) -> nothing, false, - 1, MOI.OPTIMIZE_NOT_CALLED, - MOI.COMPUTE_CONFLICT_NOT_CALLED, - eval_objective_value, + 1, Dict{Int,Float64}(), - eval_dual_objective_value, Dict{Int,Float64}(), + # PrimalStatus Dict{Int,MOI.ResultStatusCode}(), - Dict{Int,MOI.ResultStatusCode}(), - Dict{MOI.ConstraintIndex,MOI.ConflictParticipationStatusCode}(), Dict{MOI.VariableIndex,Dict{Int,Float64}}(), Dict{MOI.VariableIndex,Float64}(), - eval_variable_constraint_dual, + # DualStatus + Dict{Int,MOI.ResultStatusCode}(), Dict{MOI.ConstraintIndex,Dict{Int,Any}}(), + # + MOI.COMPUTE_CONFLICT_NOT_CALLED, + Dict{MOI.ConstraintIndex,MOI.ConflictParticipationStatusCode}(), + # Basis status Dict{MOI.ConstraintIndex,Dict{Int,MOI.BasisStatusCode}}(), Dict{MOI.VariableIndex,Dict{Int,MOI.BasisStatusCode}}(), - Dict{MOI.AbstractOptimizerAttribute,Any}(), - Dict{MOI.AbstractModelAttribute,Any}(), - Dict{MOI.AbstractSubmittable,Vector{Tuple}}(), ) end +""" +All user-facing indices are xor'd with this mask to produce unusual indices. +This is good at catching bugs in solvers which assume indices are ordered 1, 2, +3, ... +""" +const _INTERNAL_XOR_MASK = Int64(12345678) + +xor_index(vi::VI) = VI(xor(vi.value, _INTERNAL_XOR_MASK)) +xor_index(ci::CI{F,S}) where {F,S} = CI{F,S}(xor(ci.value, _INTERNAL_XOR_MASK)) +xor_indices(x) = map_indices(xor_index, x) + function MOI.add_variable(mock::MockOptimizer) if mock.add_var_allowed return xor_index(MOI.add_variable(mock.inner_model)) @@ -173,10 +190,9 @@ function MOI.add_constraint( end function MOI.optimize!(mock::MockOptimizer) - mock.solved = true - mock.hasprimal = true - mock.hasdual = true - return mock.optimize!(mock) + mock.optimize_called = true + mock.optimize!(mock) + return end MOI.compute_conflict!(::MockOptimizer) = nothing @@ -193,12 +209,13 @@ function throw_mock_unsupported_names(attr) end function MOI.supports( - mock::MockOptimizer, + ::MockOptimizer, ::Union{MOI.VariablePrimal,MockVariableAttribute}, ::Type{MOI.VariableIndex}, ) return true end + function MOI.supports( mock::MockOptimizer, attr::MOI.AbstractVariableAttribute, @@ -206,13 +223,15 @@ function MOI.supports( ) return MOI.supports(mock.inner_model, attr, IdxT) end + function MOI.supports( - mock::MockOptimizer, + ::MockOptimizer, ::Union{MOI.ConstraintDual,MockConstraintAttribute}, ::Type{<:MOI.ConstraintIndex}, ) return true end + function MOI.supports( mock::MockOptimizer, attr::MOI.AbstractConstraintAttribute, @@ -222,49 +241,66 @@ function MOI.supports( end MOI.supports(mock::MockOptimizer, ::MockModelAttribute) = true + function MOI.set( mock::MockOptimizer, ::MOI.TerminationStatus, value::MOI.TerminationStatusCode, ) - return (mock.terminationstatus = value) + mock.termination_status = value + return end + function MOI.set(mock::MockOptimizer, attr::MOI.ObjectiveValue, value::Real) - return mock.objective_value[attr.result_index] = value + mock.objective_value[attr.result_index] = value + return end + function MOI.set(mock::MockOptimizer, attr::MOI.DualObjectiveValue, value::Real) - return mock.dual_objective_value[attr.result_index] = value + mock.dual_objective_value[attr.result_index] = value + return end + function MOI.set( mock::MockOptimizer, attr::MOI.PrimalStatus, value::MOI.ResultStatusCode, ) - return mock.primal_status[attr.result_index] = value + mock.primal_status[attr.result_index] = value + return end + function MOI.set( mock::MockOptimizer, attr::MOI.DualStatus, value::MOI.ResultStatusCode, ) - return mock.dual_status[attr.result_index] = value + mock.dual_status[attr.result_index] = value + return end + function MOI.set( mock::MockOptimizer, ::MOI.ConflictStatus, value::MOI.ConflictStatusCode, ) - return (mock.conflictstatus = value) + mock.conflict_status = value + return end -MOI.get(mock::MockOptimizer, ::MOI.ConflictStatus) = mock.conflictstatus + +MOI.get(mock::MockOptimizer, ::MOI.ConflictStatus) = mock.conflict_status + function MOI.set(mock::MockOptimizer, ::MockModelAttribute, value::Integer) - return (mock.attribute = value) + mock.mock_model_attribute = value + return end + function MOI.supports(mock::MockOptimizer, attr::MOI.AbstractOptimizerAttribute) # `supports` is not defined if `is_set_by_optimize(attr)` so we pass it to # `mock.inner_model`. return MOI.supports(mock.inner_model, attr) end + function MOI.set( mock::MockOptimizer, attr::MOI.AbstractOptimizerAttribute, @@ -275,18 +311,22 @@ function MOI.set( else MOI.set(mock.inner_model, attr, xor_indices(value)) end + return end + function MOI.supports(mock::MockOptimizer, attr::MOI.AbstractModelAttribute) # `supports` is not defined if `is_set_by_optimize(attr)` so we pass it to # `mock.inner_model`. return MOI.supports(mock.inner_model, attr) end + function MOI.set(mock::MockOptimizer, attr::MOI.AbstractModelAttribute, value) if MOI.is_set_by_optimize(attr) mock.model_attributes[attr] = value else MOI.set(mock.inner_model, attr, xor_indices(value)) end + return end function MOI.set( @@ -295,55 +335,68 @@ function MOI.set( idx::MOI.VariableIndex, value, ) - return MOI.set(mock.inner_model, attr, xor_index(idx), xor_indices(value)) + MOI.set(mock.inner_model, attr, xor_index(idx), xor_indices(value)) + return end + function MOI.set( mock::MockOptimizer, attr::MOI.VariablePrimal, idx::MOI.VariableIndex, value, ) - return _safe_set_result(mock.varprimal, attr, idx, value) + _safe_set_result(mock.variable_primal, attr, idx, value) + return end + function MOI.set( mock::MockOptimizer, ::MOI.CallbackVariablePrimal, idx::MOI.VariableIndex, value, ) - return mock.callback_variable_primal[xor_index(idx)] = value + mock.callback_variable_primal[xor_index(idx)] = value + return end + function MOI.set( mock::MockOptimizer, ::MockVariableAttribute, idx::MOI.VariableIndex, value, ) - return mock.varattribute[xor_index(idx)] = value + mock.mock_variable_attribute[xor_index(idx)] = value + return end + function MOI.set( mock::MockOptimizer, attr::MOI.AbstractConstraintAttribute, idx::MOI.ConstraintIndex, value, ) - return MOI.set(mock.inner_model, attr, xor_index(idx), value) + MOI.set(mock.inner_model, attr, xor_index(idx), value) + return end + function MOI.set( mock::MockOptimizer, ::MockConstraintAttribute, idx::MOI.ConstraintIndex, value, ) - return mock.conattribute[xor_index(idx)] = value + mock.mock_constraint_attribute[xor_index(idx)] = value + return end + function MOI.set( mock::MockOptimizer, attr::MOI.ConstraintDual, idx::MOI.ConstraintIndex, value, ) - return _safe_set_result(mock.condual, attr, idx, value) + _safe_set_result(mock.constraint_dual, attr, idx, value) + return end function MOI.set( @@ -352,7 +405,8 @@ function MOI.set( idx::MOI.ConstraintIndex, value, ) - return _safe_set_result(mock.con_basis, attr, idx, value) + _safe_set_result(mock.constraint_basis_status, attr, idx, value) + return end function MOI.set( @@ -361,7 +415,8 @@ function MOI.set( idx::MOI.VariableIndex, value, ) - return _safe_set_result(mock.var_basis, attr, idx, value) + _safe_set_result(mock.variable_basis_status, attr, idx, value) + return end function MOI.set( @@ -375,6 +430,7 @@ function MOI.set( end MOI.get(mock::MockOptimizer, ::MOI.RawSolver) = mock + function MOI.get(mock::MockOptimizer, attr::MOI.AbstractOptimizerAttribute) if MOI.is_set_by_optimize(attr) return mock.optimizer_attributes[attr] @@ -397,13 +453,15 @@ end function MOI.supports(mock::MockOptimizer, attr::MOI.Name) return mock.supports_names && MOI.supports(mock.inner_model, attr) end + function MOI.set(mock::MockOptimizer, attr::MOI.Name, value) - if mock.supports_names - MOI.set(mock.inner_model, attr, value) - else + if !mock.supports_names throw_mock_unsupported_names(attr) end + MOI.set(mock.inner_model, attr, value) + return end + function MOI.supports( mock::MockOptimizer, attr::MOI.VariableName, @@ -411,18 +469,20 @@ function MOI.supports( ) return mock.supports_names && MOI.supports(mock.inner_model, attr, IdxT) end + function MOI.set( mock::MockOptimizer, attr::MOI.VariableName, index::MOI.VariableIndex, value, ) - if mock.supports_names - MOI.set(mock.inner_model, attr, xor_index(index), value) - else + if !mock.supports_names throw_mock_unsupported_names(attr) end + MOI.set(mock.inner_model, attr, xor_index(index), value) + return end + function MOI.supports( mock::MockOptimizer, attr::MOI.ConstraintName, @@ -430,26 +490,23 @@ function MOI.supports( ) return mock.supports_names && MOI.supports(mock.inner_model, attr, IdxT) end + function MOI.set( mock::MockOptimizer, attr::MOI.ConstraintName, index::MOI.ConstraintIndex, value, ) - if mock.supports_names - MOI.set(mock.inner_model, attr, xor_index(index), value) - else + if !mock.supports_names throw_mock_unsupported_names(attr) end + MOI.set(mock.inner_model, attr, xor_index(index), value) + return end function MOI.get(b::MockOptimizer, IdxT::Type{<:MOI.Index}, name::String) index = MOI.get(b.inner_model, IdxT, name) - if index === nothing - return nothing - else - return xor_index(index) - end + return index === nothing ? nothing : xor_index(index) end ##### @@ -457,40 +514,45 @@ end ##### MOI.get(mock::MockOptimizer, ::MOI.ResultCount) = mock.result_count -MOI.set(mock::MockOptimizer, ::MOI.ResultCount, x) = (mock.result_count = x) -MOI.get(mock::MockOptimizer, ::MOI.TerminationStatus) = mock.terminationstatus +function MOI.set(mock::MockOptimizer, ::MOI.ResultCount, x) + mock.result_count = x + return +end + +MOI.get(mock::MockOptimizer, ::MOI.TerminationStatus) = mock.termination_status + function MOI.get(mock::MockOptimizer, attr::MOI.ObjectiveValue) MOI.check_result_index_bounds(mock, attr) if mock.eval_objective_value return get_fallback(mock, attr) - else - return get(mock.objective_value, attr.result_index, NaN) end + return get(mock.objective_value, attr.result_index, NaN) end + function MOI.get(mock::MockOptimizer, attr::MOI.DualObjectiveValue) MOI.check_result_index_bounds(mock, attr) if mock.eval_dual_objective_value return get_fallback(mock, attr, Float64) - else - return get(mock.dual_objective_value, attr.result_index, NaN) end + return get(mock.dual_objective_value, attr.result_index, NaN) end + function MOI.get(mock::MockOptimizer, attr::MOI.PrimalStatus) if attr.result_index > mock.result_count return MOI.NO_SOLUTION - else - return get(mock.primal_status, attr.result_index, MOI.NO_SOLUTION) end + return get(mock.primal_status, attr.result_index, MOI.NO_SOLUTION) end + function MOI.get(mock::MockOptimizer, attr::MOI.DualStatus) if attr.result_index > mock.result_count return MOI.NO_SOLUTION - else - return get(mock.dual_status, attr.result_index, MOI.NO_SOLUTION) end + return get(mock.dual_status, attr.result_index, MOI.NO_SOLUTION) end -MOI.get(mock::MockOptimizer, ::MockModelAttribute) = mock.attribute + +MOI.get(mock::MockOptimizer, ::MockModelAttribute) = mock.mock_model_attribute function MOI.get( mock::MockOptimizer, @@ -499,12 +561,13 @@ function MOI.get( ) return xor_indices(MOI.get(mock.inner_model, attr, xor_index(idx))) end + function MOI.get( mock::MockOptimizer, ::MockVariableAttribute, idx::MOI.VariableIndex, ) - return mock.varattribute[xor_index(idx)] + return mock.mock_variable_attribute[xor_index(idx)] end function MOI.get( @@ -514,22 +577,22 @@ function MOI.get( ) MOI.check_result_index_bounds(mock, attr) MOI.throw_if_not_valid(mock, idx) - return _safe_get_result(mock.varprimal, attr, idx, "primal") + return _safe_get_result(mock.variable_primal, attr, idx, "primal") end function MOI.get( mock::MockOptimizer, - attr::MOI.CallbackVariablePrimal, + ::MOI.CallbackVariablePrimal, idx::MOI.VariableIndex, ) + if !MOI.is_valid(mock, idx) + throw(MOI.InvalidIndex(idx)) + end primal = get(mock.callback_variable_primal, xor_index(idx), nothing) - if primal !== nothing - return primal - elseif MOI.is_valid(mock, idx) + if primal === nothing error("No mock callback primal is set for variable `", idx, "`.") - else - throw(MOI.InvalidIndex(idx)) end + return primal end function MOI.get( @@ -550,6 +613,7 @@ function MOI.get( MOI.throw_if_not_valid(mock, idx) return MOI.get(mock.inner_model, attr, xor_index(idx)) end + function MOI.get( mock::MockOptimizer, attr::MOI.CanonicalConstraintFunction, @@ -562,6 +626,7 @@ function MOI.get( xor_indices(MOI.get(mock.inner_model, attr, xor_index(idx))), ) end + function MOI.get( mock::MockOptimizer, attr::Union{MOI.CanonicalConstraintFunction,MOI.ConstraintFunction}, @@ -583,7 +648,7 @@ function MOI.get( (F == MOI.SingleVariable || F == MOI.VectorOfVariables) return get_fallback(mock, attr, idx) else - return _safe_get_result(mock.condual, attr, idx, "dual") + return _safe_get_result(mock.constraint_dual, attr, idx, "dual") end end function MOI.get( @@ -591,8 +656,9 @@ function MOI.get( ::MockConstraintAttribute, idx::MOI.ConstraintIndex, ) - return mock.conattribute[xor_index(idx)] + return mock.mock_constraint_attribute[xor_index(idx)] end + function MOI.get( mock::MockOptimizer, attr::MOI.ConstraintBasisStatus, @@ -600,7 +666,12 @@ function MOI.get( ) MOI.check_result_index_bounds(mock, attr) MOI.throw_if_not_valid(mock, idx) - return _safe_get_result(mock.con_basis, attr, idx, "basis status") + return _safe_get_result( + mock.constraint_basis_status, + attr, + idx, + "basis status", + ) end function MOI.get( @@ -610,7 +681,12 @@ function MOI.get( ) MOI.check_result_index_bounds(mock, attr) MOI.throw_if_not_valid(mock, idx) - return _safe_get_result(mock.var_basis, attr, idx, "basis status") + return _safe_get_result( + mock.variable_basis_status, + attr, + idx, + "basis status", + ) end function MOI.get( @@ -634,6 +710,7 @@ function _safe_set_result( end return dict[xored][attr.result_index] = value end + function _safe_get_result( dict::Dict, attr::MOI.AnyAttribute, @@ -648,13 +725,8 @@ function _safe_get_result( value = get(result_to_value, attr.result_index, nothing) if value === nothing error( - "No mock $name is set for ", - index_name, - " `", - index, - "` at result index `", - attr.result_index, - "`.", + "No mock $name is set for $(index_name) `$(index)` at result " * + "index `$(attr.result_index)`.", ) end return value @@ -664,22 +736,20 @@ MOI.get(::MockOptimizer, ::MOI.SolverName) = "Mock" function MOI.empty!(mock::MockOptimizer) MOI.empty!(mock.inner_model) - mock.attribute = 0 - empty!(mock.varattribute) - empty!(mock.conattribute) - mock.solved = false - mock.hasprimal = false - mock.hasdual = false - mock.terminationstatus = MOI.OPTIMIZE_NOT_CALLED + mock.mock_model_attribute = 0 + empty!(mock.mock_variable_attribute) + empty!(mock.mock_constraint_attribute) + mock.optimize_called = false + mock.termination_status = MOI.OPTIMIZE_NOT_CALLED empty!(mock.objective_value) empty!(mock.dual_objective_value) empty!(mock.primal_status) empty!(mock.dual_status) - empty!(mock.varprimal) + empty!(mock.variable_primal) empty!(mock.callback_variable_primal) - empty!(mock.condual) - empty!(mock.con_basis) - empty!(mock.var_basis) + empty!(mock.constraint_dual) + empty!(mock.constraint_basis_status) + empty!(mock.variable_basis_status) empty!(mock.optimizer_attributes) empty!(mock.model_attributes) empty!(mock.submitted) @@ -691,17 +761,15 @@ function MOI.is_empty(mock::MockOptimizer) # mock.inner_model is empty. # TODO: Default values are currently copied in three places, not good. return MOI.is_empty(mock.inner_model) && - mock.attribute == 0 && - !mock.solved && - !mock.hasprimal && - !mock.hasdual && - mock.terminationstatus == MOI.OPTIMIZE_NOT_CALLED && + mock.mock_model_attribute == 0 && + !mock.optimize_called && + mock.termination_status == MOI.OPTIMIZE_NOT_CALLED && isempty(mock.objective_value) && isempty(mock.dual_objective_value) && isempty(mock.primal_status) && isempty(mock.dual_status) && - isempty(mock.con_basis) && - isempty(mock.var_basis) && + isempty(mock.constraint_basis_status) && + isempty(mock.variable_basis_status) && isempty(mock.optimizer_attributes) && isempty(mock.model_attributes) && isempty(mock.submitted) @@ -720,9 +788,9 @@ function MOI.delete(mock::MockOptimizer, index::MOI.VariableIndex) throw(MOI.InvalidIndex(index)) end MOI.delete(mock.inner_model, xor_index(index)) - delete!(mock.varprimal, index) + delete!(mock.variable_primal, index) delete!(mock.callback_variable_primal, index) - delete!(mock.var_basis, index) + delete!(mock.variable_basis_status, index) return end @@ -736,9 +804,9 @@ function MOI.delete(mock::MockOptimizer, indices::Vector{MOI.VariableIndex}) end MOI.delete(mock.inner_model, xor_index.(indices)) for index in indices - delete!(mock.varprimal, index) + delete!(mock.variable_primal, index) delete!(mock.callback_variable_primal, index) - delete!(mock.var_basis, index) + delete!(mock.variable_basis_status, index) end return end @@ -752,8 +820,8 @@ function MOI.delete(mock::MockOptimizer, index::MOI.ConstraintIndex) throw(MOI.InvalidIndex(index)) end MOI.delete(mock.inner_model, xor_index(index)) - delete!(mock.condual, index) - delete!(mock.con_basis, index) + delete!(mock.constraint_dual, index) + delete!(mock.constraint_basis_status, index) return end @@ -765,7 +833,8 @@ function MOI.modify( if !mock.modify_allowed throw(MOI.ModifyConstraintNotAllowed(c, change)) end - return MOI.modify(mock.inner_model, xor_index(c), xor_indices(change)) + MOI.modify(mock.inner_model, xor_index(c), xor_indices(change)) + return end function MOI.set( @@ -774,20 +843,23 @@ function MOI.set( c::CI{<:MOI.AbstractFunction,S}, set::S, ) where {S<:MOI.AbstractSet} - return MOI.set(mock.inner_model, MOI.ConstraintSet(), xor_index(c), set) + MOI.set(mock.inner_model, MOI.ConstraintSet(), xor_index(c), set) + return end + function MOI.set( mock::MockOptimizer, ::MOI.ConstraintFunction, c::CI{F}, func::F, ) where {F<:MOI.AbstractFunction} - return MOI.set( + MOI.set( mock.inner_model, MOI.ConstraintFunction(), xor_index(c), xor_indices(func), ) + return end function MOI.modify( @@ -798,15 +870,18 @@ function MOI.modify( if !mock.modify_allowed throw(MOI.ModifyObjectiveNotAllowed(change)) end - return MOI.modify(mock.inner_model, obj, xor_indices(change)) + MOI.modify(mock.inner_model, obj, xor_indices(change)) + return end MOI.supports(::MockOptimizer, ::MOI.AbstractSubmittable) = true + function MOI.submit(mock::MockOptimizer, sub::MOI.AbstractSubmittable, args...) if !haskey(mock.submitted, sub) mock.submitted[sub] = Tuple[] end - return push!(mock.submitted[sub], args) + push!(mock.submitted[sub], args) + return end # TODO: transform @@ -818,18 +893,21 @@ function MOI.supports_constraint( ) return MOI.supports_constraint(mock.inner_model, F, S) end + function MOI.supports_add_constrained_variable( mock::MockOptimizer, S::Type{<:MOI.AbstractScalarSet}, ) return MOI.supports_add_constrained_variable(mock.inner_model, S) end + function MOI.supports_add_constrained_variables( mock::MockOptimizer, S::Type{<:MOI.AbstractVectorSet}, ) return MOI.supports_add_constrained_variables(mock.inner_model, S) end + # Add this method to avoid ambiguity function MOI.supports_add_constrained_variables( mock::MockOptimizer, @@ -841,6 +919,7 @@ end function MOI.copy_to(mock::MockOptimizer, src::MOI.ModelLike; kws...) return automatic_copy_to(mock, src; kws...) end + function MOI.supports_incremental_interface( mock::MockOptimizer, copy_names::Bool, @@ -848,197 +927,205 @@ function MOI.supports_incremental_interface( return !mock.needs_allocate_load && MOI.supports_incremental_interface(mock.inner_model, copy_names) end + function final_touch(uf::MockOptimizer, index_map) return final_touch(uf.inner_model, index_map) end -# Allocate-Load Interface -function supports_allocate_load(mock::MockOptimizer, copy_names::Bool) - return supports_allocate_load(mock.inner_model, copy_names) -end - -function allocate_variables(mock::MockOptimizer, nvars) - return xor_index.(allocate_variables(mock.inner_model, nvars)) -end -function allocate(mock::MockOptimizer, attr::MOI.AnyAttribute, value) - return allocate(mock.inner_model, attr, xor_indices(value)) -end -function allocate( - mock::MockOptimizer, - attr::MOI.AnyAttribute, - idx::MOI.Index, - value, -) - return allocate(mock.inner_model, attr, xor_index(idx), xor_indices(value)) -end -function allocate_constraint( - mock::MockOptimizer, - f::MOI.AbstractFunction, - s::MOI.AbstractSet, -) - return xor_index(allocate_constraint(mock.inner_model, xor_indices(f), s)) -end - -function load_variables(mock::MockOptimizer, nvars) - return load_variables(mock.inner_model, nvars) -end -function load(mock::MockOptimizer, attr::MOI.AnyAttribute, value) - return load(mock.inner_model, attr, xor_indices(value)) -end -function load( - mock::MockOptimizer, - attr::MOI.AnyAttribute, - idx::MOI.Index, - value, -) - return load(mock.inner_model, attr, xor_index(idx), xor_indices(value)) -end -function load_constraint( - mock::MockOptimizer, - ci::CI, - f::MOI.AbstractFunction, - s::MOI.AbstractSet, -) - return load_constraint(mock.inner_model, xor_index(ci), xor_indices(f), s) -end - """ set_mock_optimize!(mock::MockOptimizer, opt::Function...) -Sets multiple optimize! function. The first is to be used the first time `MOI.optimize!(mock)` is called, the second function is to be used the second time, ... +Sets multiple optimize! function. The first is to be used the first time +`MOI.optimize!(mock)` is called, the second function is to be used the second +time, ... """ function set_mock_optimize!(mock::MockOptimizer, opts::Function...) - return mock.optimize! = rec_mock_optimize(mock, opts...) + mock.optimize! = _recursive_mock_optimize(opts...) + return end -function rec_mock_optimize( - mock::MockOptimizer, - opt::Function, - opts::Function..., -) - return (mock::MockOptimizer) -> - (opt(mock); mock.optimize! = rec_mock_optimize(mock, opts...)) + +_recursive_mock_optimize(optimize::Function) = optimize + +function _recursive_mock_optimize(optimize::Function, tail::Function...) + return (mock::MockOptimizer) -> begin + optimize(mock) + mock.optimize! = _recursive_mock_optimize(tail...) + return + end end -rec_mock_optimize(mock::MockOptimizer, opt::Function) = opt """ - mock_optimize!(mock::MockOptimizer, termstatus::MOI.TerminationStatusCode, (primstatus::MOI.ResultStatusCode, varprim::Vector), dual_status::MOI.ResultStatusCode, conduals::Pair...) - -Sets the termination status of `mock` to `termstatus` and the primal (resp. dual) status to `primstatus` (resp. `dual_status`). -The primal values of the variables in the order returned by `ListOfVariableIndices` are set to `varprim`. -If `termstatus` is missing, it is assumed to be `MOI.OPTIMAL`. -If `primstatus` is missing, it is assumed to be `MOI.FEASIBLE_POINT`. -If `dual_status` is missing, it is assumed to be `MOI.FEASIBLE_POINT` if there is a primal solution and `primstatus` is not `MOI.INFEASIBLE_POINT`, otherwise it is `MOI.INFEASIBILITY_CERTIFICATE`. -The dual values are set to the values specified by `conduals`. Each pair is of the form `(F,S)=>[...]` where `[...]` is the the vector of dual values for the constraints `F`-in-`S` in the order returned by `ListOfConstraintIndices{F,S}`. - -The bases status are set to the status specified by `con_basis` and `var_basis`. -`con_basis` must be a vector of pairs, each of the form `(F,S)=>[...]`, where -`[...]` is the the vector of basis status for the constraints `F`-in-`S` in the -order returned by `ListOfConstraintIndices{F,S}`. -`var_basis` is a vector of statuses, corresponding the status of the variables -returned by `ListOfVariableIndices`. + mock_optimize!( + mock::MockOptimizer, + termination_status::MOI.TerminationStatusCode, + primal::Union{ + Tuple{MOI.ResultStatusCode,<:Vector}, + MOI.ResultStatusCode, + <:Vector, + } + dual_status::MOI.ResultStatusCode, + constraint_duals::Pair{Tuple{DataTypeDataType},<:Vector}...; + constraint_basis_status = Pair{Tuple{DataTypeDataType},<:Vector}[], + variable_basis_status = MOI.BasisStatusCode[], + ) + +Fake the result of a call to `optimize!` in the mock optimizer by storing the +solution. + +## Arguments + + * `termination_status`: defaults to `MOI.OPTIMAL` if not provided. + + * `primal`: pass one of the following: a tuple of `(status, result)` + corresponding to the `MOI.PrimalStatus` and `MOI.VariablePrimal` attributes, + or pass a `status` with no result vector, or pass only the result vector. If + the status is omitted, it is assumed to be `MOI.FEASIBLE_POINT`. The `result` + vector should correspond to the order of the primal values given by + `MOI.ListOfVariableIndices`. + + * `dual_status`: corresponds to the `MOI.DualStatus` attribute. If not + provided, it defaults to `MOI.FEASIBLE_POINT` if constriant duals are + provided and `MOI.NO_SOLUTION` otherwise. + + * `constraint_duals`: the remaining positional arguments are passed as pairs. + Each pair is of the form `(F, S) => result`, where `result` is the the vector + of `MOI.ConstraintDual` values for the constraints `F`-in-`S` in the order + returned by `MOI.ListOfConstraintIndices{F,S}`. + + * `constraint_basis_status`: a vector of pairs similar to `constraint_duals`, + except this time for the `MOI.ConstraintBasisStatus` attribute. + + * `variable_basis_status`: a vector of `MOI.BasisStatusCode`, corresponding to + the `MOI.VariableBasisStatus` attribute of the variables in the order + returned by `MOI.ListOfVariableIndices`. """ function mock_optimize!( mock::MockOptimizer, - termstatus::MOI.TerminationStatusCode, - primal, - dual...; - con_basis = [], - var_basis = [], + termination_status::MOI.TerminationStatusCode, + primal::Union{ + Tuple{MOI.ResultStatusCode,<:Vector}, + MOI.ResultStatusCode, + <:Vector, + }, + dual_status_constraint_duals...; + constraint_basis_status = [], + variable_basis_status = MOI.BasisStatusCode[], + var_basis = nothing, + con_basis = nothing, ) - MOI.set(mock, MOI.TerminationStatus(), termstatus) + if var_basis !== nothing + @warn( + "var_basis is deprecated. Use variable_basis_status instead.", + maxlog = 1, + ) + variable_basis_status = var_basis + end + if con_basis !== nothing + @warn( + "con_basis is deprecated. Use constraint_basis_status instead.", + maxlog = 1, + ) + constraint_basis_status = con_basis + end + MOI.set(mock, MOI.TerminationStatus(), termination_status) MOI.set(mock, MOI.ResultCount(), 1) - mock_primal!(mock, primal) - mock_dual!(mock, dual...) - for con_basis_pair in con_basis - mock_basis_status!(mock, con_basis_pair) + _set_mock_primal(mock, primal) + _set_mock_dual(mock, dual_status_constraint_duals...) + for con_basis_pair in constraint_basis_status + F, S = con_basis_pair.first + indices = MOI.get(mock, MOI.ListOfConstraintIndices{F,S}()) + for (i, ci) in enumerate(indices) + MOI.set( + mock, + MOI.ConstraintBasisStatus(), + ci, + con_basis_pair.second[i], + ) + end end - if length(var_basis) > 0 + if length(variable_basis_status) > 0 variables = MOI.get(mock, MOI.ListOfVariableIndices()) - @assert length(var_basis) == length(variables) - MOI.set.(mock, MOI.VariableBasisStatus(), variables, var_basis) + @assert length(variable_basis_status) == length(variables) + MOI.set.( + mock, + MOI.VariableBasisStatus(), + variables, + variable_basis_status, + ) end return end -# Default termination status -function mock_optimize!(mock::MockOptimizer, primdual...; kws...) - return mock_optimize!(mock, MOI.OPTIMAL, primdual...; kws...) -end +# The fallback for default termination_status function mock_optimize!( mock::MockOptimizer, - termstatus::MOI.TerminationStatusCode, + primal::Union{ + Tuple{MOI.ResultStatusCode,<:Vector}, + MOI.ResultStatusCode, + <:Vector, + }, + args...; + kwargs..., ) - MOI.set(mock, MOI.TerminationStatus(), termstatus) - return MOI.set(mock, MOI.ResultCount(), 0) + mock_optimize!(mock, MOI.OPTIMAL, primal, args...; kwargs...) + return +end + +# The fallback if no primal solution is provided +function mock_optimize!(mock::MockOptimizer, status::MOI.TerminationStatusCode) + MOI.set(mock, MOI.TerminationStatus(), status) + MOI.set(mock, MOI.ResultCount(), 0) + return end -# Primal -mock_primal!(mock, primal::Tuple) = mock_primal!(mock, primal...) -function mock_primal!( +# _set_mock_primal + +function _set_mock_primal( mock::MockOptimizer, - primstatus::MOI.ResultStatusCode, - varprim::Vector..., + primal::Tuple{MOI.ResultStatusCode,<:Vector}, ) - MOI.set(mock, MOI.PrimalStatus(), primstatus) - return mock_varprimal!(mock, varprim...) -end -# Default primal status -function mock_primal!(mock::MockOptimizer, varprim::Vector) - return mock_primal!(mock, MOI.FEASIBLE_POINT, varprim) -end -function mock_primal!(mock::MockOptimizer) - # No primal solution - return mock.hasprimal = false -end - -# Sets variable primal to varprim -function mock_varprimal!(mock::MockOptimizer) end -function mock_varprimal!(mock::MockOptimizer, varprim::Vector) - return MOI.set( - mock, - MOI.VariablePrimal(), - MOI.get(mock, MOI.ListOfVariableIndices()), - varprim, - ) + MOI.set(mock, MOI.PrimalStatus(), primal[1]) + x = MOI.get(mock, MOI.ListOfVariableIndices()) + MOI.set(mock, MOI.VariablePrimal(), x, primal[2]) + return +end + +function _set_mock_primal(mock::MockOptimizer, primal::MOI.ResultStatusCode) + MOI.set(mock, MOI.PrimalStatus(), primal) + return end -# Dual -function mock_dual!( +function _set_mock_primal(mock::MockOptimizer, primal::Vector) + MOI.set(mock, MOI.PrimalStatus(), MOI.FEASIBLE_POINT) + x = MOI.get(mock, MOI.ListOfVariableIndices()) + MOI.set(mock, MOI.VariablePrimal(), x, primal) + return +end + +# _set_mock_dual + +function _set_mock_dual(mock::MockOptimizer) + MOI.set(mock, MOI.DualStatus(), MOI.NO_SOLUTION) + return +end + +function _set_mock_dual( mock::MockOptimizer, dual_status::MOI.ResultStatusCode, - conduals::Pair..., + constraint_duals::Pair..., ) MOI.set(mock, MOI.DualStatus(), dual_status) - return mock_condual!(mock, conduals...) -end -# Default dual status -function mock_dual!(mock::MockOptimizer, conduals::Pair...) - status = - !mock.hasprimal || - MOI.get(mock, MOI.PrimalStatus()) == MOI.INFEASIBLE_POINT ? - MOI.INFEASIBILITY_CERTIFICATE : MOI.FEASIBLE_POINT - return mock_dual!(mock, status, conduals...) -end -function mock_dual!(mock::MockOptimizer) - # No dual solution - return mock.hasdual = false -end - -# Sets constraint dual to conduals -function mock_condual!(mock::MockOptimizer) end -function mock_condual!(mock::MockOptimizer, condual::Pair, conduals...) - F, S = condual.first - duals = condual.second - for (i, ci) in enumerate(MOI.get(mock, MOI.ListOfConstraintIndices{F,S}())) - MOI.set(mock, MOI.ConstraintDual(), ci, duals[i]) - end - return mock_condual!(mock, conduals...) -end -# Set the basis status of the provided constraints. -function mock_basis_status!(mock::MockOptimizer, con_basis::Pair) - F, S = con_basis.first - bases = con_basis.second - for (i, ci) in enumerate(MOI.get(mock, MOI.ListOfConstraintIndices{F,S}())) - MOI.set(mock, MOI.ConstraintBasisStatus(), ci, bases[i]) + for ((F, S), result) in constraint_duals + indices = MOI.get(mock, MOI.ListOfConstraintIndices{F,S}()) + for (i, ci) in enumerate(indices) + MOI.set(mock, MOI.ConstraintDual(), ci, result[i]) + end end + return +end + +# fallback for no status +function _set_mock_dual(mock::MockOptimizer, args::Pair...) + _set_mock_dual(mock, MOI.FEASIBLE_POINT, args...) + return end diff --git a/test/Bridges/Constraint/functionize.jl b/test/Bridges/Constraint/functionize.jl index c8d327c132..6b6b7b1722 100644 --- a/test/Bridges/Constraint/functionize.jl +++ b/test/Bridges/Constraint/functionize.jl @@ -57,7 +57,7 @@ config_with_basis = MOIT.Config(basis = true) [1, 0], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [0, 1], - con_basis = [ + constraint_basis_status = [ ( MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, @@ -67,7 +67,7 @@ config_with_basis = MOIT.Config(basis = true) MOI.GreaterThan{Float64}, ) => [MOI.BASIC, MOI.NONBASIC], ], - var_basis = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], + variable_basis_status = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], ), ) MOIT.linear2test(bridged_mock, config_with_basis) diff --git a/test/Bridges/Constraint/interval.jl b/test/Bridges/Constraint/interval.jl index 0d49f9799e..1a8e403348 100644 --- a/test/Bridges/Constraint/interval.jl +++ b/test/Bridges/Constraint/interval.jl @@ -39,11 +39,11 @@ end (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [5.0, 5.0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [0], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => @@ -52,11 +52,11 @@ end (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [2.5, 2.5], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.NONBASIC], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => @@ -69,20 +69,20 @@ end [1], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.NONBASIC], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [6.0, 6.0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], ), ) MOIT.linear10test(bridged_mock, config_with_basis) @@ -95,11 +95,14 @@ end [0], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_LOWER], + variable_basis_status = [ + MOI.NONBASIC_AT_LOWER, + MOI.NONBASIC_AT_LOWER, + ], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [0], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => diff --git a/test/Bridges/Constraint/slack.jl b/test/Bridges/Constraint/slack.jl index 64453ee4ec..d95259ad94 100644 --- a/test/Bridges/Constraint/slack.jl +++ b/test/Bridges/Constraint/slack.jl @@ -83,11 +83,11 @@ config = MOIT.Config() (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [1, 0, 1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [MOI.NONBASIC], ], - var_basis = [ + variable_basis_status = [ MOI.BASIC, MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_UPPER, diff --git a/test/DeprecatedTest/contconic.jl b/test/DeprecatedTest/contconic.jl index 09db6b3f1b..7577c59d76 100644 --- a/test/DeprecatedTest/contconic.jl +++ b/test/DeprecatedTest/contconic.jl @@ -226,7 +226,8 @@ end (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.INFEASIBLE, - tuple(), + MOI.NO_SOLUTION, + MOI.INFEASIBILITY_CERTIFICATE, (MOI.SingleVariable, MOI.LessThan{Float64}) => [-1], (MOI.SingleVariable, MOI.EqualTo{Float64}) => [-1], (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [1], diff --git a/test/DeprecatedTest/contlinear.jl b/test/DeprecatedTest/contlinear.jl index eb5bfbfec1..a89d977b78 100644 --- a/test/DeprecatedTest/contlinear.jl +++ b/test/DeprecatedTest/contlinear.jl @@ -53,11 +53,11 @@ MOIU.set_mock_optimize!( mock, [1, 0], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], ], - var_basis = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], + variable_basis_status = [MOI.BASIC, MOI.NONBASIC_AT_LOWER], ), ) MOIT.linear2test(mock, config) @@ -66,20 +66,20 @@ MOIU.set_mock_optimize!( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [3], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.NONBASIC], ], - var_basis = [MOI.BASIC], + variable_basis_status = [MOI.BASIC], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.NONBASIC_AT_UPPER], + variable_basis_status = [MOI.NONBASIC_AT_UPPER], ), ) MOIT.linear3test(mock, config) @@ -127,7 +127,8 @@ MOIU.set_mock_optimize!( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.INFEASIBLE, - tuple(), + MOI.NO_SOLUTION, + MOI.INFEASIBILITY_CERTIFICATE, (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], ), ) @@ -172,13 +173,13 @@ MOIU.set_mock_optimize!( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [650 / 11, 400 / 11], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC, MOI.NONBASIC], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], ), ) MOIT.linear9test(mock, config) @@ -187,42 +188,42 @@ MOIU.set_mock_optimize!( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [5.0, 5.0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [2.5, 2.5], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [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 = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [6.0, 6.0], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], ], - var_basis = [MOI.BASIC, MOI.BASIC], + variable_basis_status = [MOI.BASIC, MOI.BASIC], ), ) MOIT.linear10test(mock, config) @@ -231,11 +232,11 @@ MOIU.set_mock_optimize!( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [0.0, 0.0], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.BASIC], ], - var_basis = [MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_LOWER], + variable_basis_status = [MOI.NONBASIC_AT_LOWER, MOI.NONBASIC_AT_LOWER], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [0], ), ) @@ -252,7 +253,8 @@ MOIU.set_mock_optimize!( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, MOI.INFEASIBLE, - tuple(), + MOI.NO_SOLUTION, + MOI.INFEASIBILITY_CERTIFICATE, (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1, -1], ), @@ -279,11 +281,15 @@ MOIU.set_mock_optimize!( (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!( mock, [0, 1 / 2, 1], - con_basis = [ + constraint_basis_status = [ (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.NONBASIC], ], - var_basis = [MOI.NONBASIC_AT_LOWER, MOI.BASIC, MOI.NONBASIC_AT_UPPER], + variable_basis_status = [ + 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], diff --git a/test/Utilities/mockoptimizer.jl b/test/Utilities/mockoptimizer.jl index 774a8aedf5..5d67e3fc80 100644 --- a/test/Utilities/mockoptimizer.jl +++ b/test/Utilities/mockoptimizer.jl @@ -1,101 +1,37 @@ +module TestMockOptimizer + using Test import MathOptInterface const MOI = MathOptInterface -const MOIT = MOI.DeprecatedTest const MOIU = MOI.Utilities -@testset "Default objective sense" begin - MOIT.default_objective_test(MOIU.MockOptimizer(MOIU.Model{Float64}())) -end - -@testset "Default statuses" begin - model = MOIU.MockOptimizer(MOIU.Model{Float64}()) - MOIT.default_status_test(model) - MOI.empty!(model) - MOIT.default_status_test(model) -end - -@testset "Name test" begin - MOIT.nametest(MOIU.MockOptimizer(MOIU.Model{Float64}())) -end - -struct NoFreeModel <: MOI.ModelLike end -MOI.supports_add_constrained_variables(::NoFreeModel, ::Type{MOI.Reals}) = false - -@testset "supports_add_constrained_variable" begin - optimizer = MOIU.MockOptimizer(MOIU.Model{Float64}()) - @test MOI.supports_add_constrained_variable( - optimizer, - MOI.GreaterThan{Float64}, - ) - @test !MOI.supports_add_constrained_variable( - optimizer, - MOIT.UnknownScalarSet{Float64}, - ) - @test MOI.supports_add_constrained_variables(optimizer, MOI.Nonnegatives) - @test !MOI.supports_add_constrained_variables( - optimizer, - MOIT.UnknownVectorSet, - ) - - nofree_optimizer = MOIU.MockOptimizer(NoFreeModel()) - @test !MOI.supports_add_constrained_variable( - nofree_optimizer, - MOI.GreaterThan{Float64}, - ) - @test !MOI.supports_add_constrained_variables( - nofree_optimizer, - MOI.Nonnegatives, - ) - @test !MOI.supports_add_constrained_variables(nofree_optimizer, MOI.Reals) -end - -@testset "Optimizer attributes" begin - optimizer = MOIU.MockOptimizer(MOIU.Model{Float64}()) - @test MOI.supports(optimizer, MOIU.MockModelAttribute()) - MOI.set(optimizer, MOIU.MockModelAttribute(), 10) - @test MOI.get(optimizer, MOIU.MockModelAttribute()) == 10 - - v1 = MOI.add_variable(optimizer) - @test MOI.supports(optimizer, MOIU.MockVariableAttribute(), typeof(v1)) - MOI.set(optimizer, MOIU.MockVariableAttribute(), v1, 11) - @test MOI.get(optimizer, MOIU.MockVariableAttribute(), v1) == 11 - MOI.set(optimizer, MOIU.MockVariableAttribute(), [v1], [-11]) - @test MOI.get(optimizer, MOIU.MockVariableAttribute(), [v1]) == [-11] - - @test MOI.supports_constraint( - optimizer, - MOI.SingleVariable, - MOI.GreaterThan{Float64}, - ) - c1 = MOI.add_constraint( - optimizer, - MOI.SingleVariable(v1), - MOI.GreaterThan(1.0), - ) - @test MOI.supports(optimizer, MOIU.MockConstraintAttribute(), typeof(c1)) - MOI.set(optimizer, MOIU.MockConstraintAttribute(), c1, 12) - @test MOI.get(optimizer, MOIU.MockConstraintAttribute(), c1) == 12 - MOI.set(optimizer, MOIU.MockConstraintAttribute(), [c1], [-12]) - @test MOI.get(optimizer, MOIU.MockConstraintAttribute(), [c1]) == [-12] +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 "Optimizer solve no result" begin - optimizer = MOIU.MockOptimizer(MOIU.Model{Float64}()) +# function test_optimizer_solve_no_result() +# optimizer = MOIU.MockOptimizer(MOIU.Model{Float64}()) - v1 = MOI.add_variable(optimizer) +# v1 = MOI.add_variable(optimizer) - # Load fake solution - MOI.set(optimizer, MOI.TerminationStatus(), MOI.INFEASIBLE) - MOI.optimize!(optimizer) - @test MOI.get(optimizer, MOI.TerminationStatus()) == MOI.INFEASIBLE +# # Load fake solution +# MOI.set(optimizer, MOI.TerminationStatus(), MOI.INFEASIBLE) +# MOI.optimize!(optimizer) +# @test MOI.get(optimizer, MOI.TerminationStatus()) == MOI.INFEASIBLE - MOI.set(optimizer, MOI.ConflictStatus(), MOI.CONFLICT_FOUND) - MOI.compute_conflict!(optimizer) - @test MOI.get(optimizer, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND -end +# MOI.set(optimizer, MOI.ConflictStatus(), MOI.CONFLICT_FOUND) +# MOI.compute_conflict!(optimizer) +# @test MOI.get(optimizer, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND +# end -@testset "Optimizer solve with result" begin +function test_optimizer_solve_with_result() optimizer = MOIU.MockOptimizer( MOIU.Model{Float64}(), eval_objective_value = false, @@ -200,12 +136,7 @@ end ) end -@testset "Delete" begin - mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) - MOIT.delete_test(mock) -end - -@testset "CanonicalConstraintFunction" begin +function test_CanonicalConstraintFunction() mock = MOIU.MockOptimizer(MOIU.Model{Int}()) fx, fy = MOI.SingleVariable.(MOI.add_variables(mock, 2)) cx = MOI.add_constraint(mock, fx, MOI.LessThan(0)) @@ -223,7 +154,7 @@ end end end -@testset "Conflict access" begin +function test_conflict_access() mock = MOIU.MockOptimizer(MOIU.Model{Int}()) fx, fy = MOI.SingleVariable.(MOI.add_variables(mock, 2)) cx = MOI.add_constraint(mock, fx, MOI.LessThan(0)) @@ -236,3 +167,57 @@ end MOI.NOT_IN_CONFLICT @test MOI.get(mock, MOI.ConstraintConflictStatus(), c) == MOI.IN_CONFLICT end + +function test_MockVariableAttribute() + mock = MOIU.MockOptimizer(MOIU.Model{Int}()) + x = MOI.add_variable(mock) + MOI.set(mock, MOI.Utilities.MockVariableAttribute(), x, 1) + @test MOI.get(mock, MOI.Utilities.MockVariableAttribute(), x) == 1 + return +end + +function test_MockConstraintAttribute() + mock = MOIU.MockOptimizer(MOIU.Model{Int}()) + x = MOI.add_variable(mock) + c = MOI.add_constraint(mock, MOI.SingleVariable(x), MOI.LessThan(0)) + MOI.set(mock, MOI.Utilities.MockConstraintAttribute(), c, 1) + @test MOI.get(mock, MOI.Utilities.MockConstraintAttribute(), c) == 1 + return +end + +function test_DualObjectiveValue() + mock = + MOIU.MockOptimizer(MOIU.Model{Int}(); eval_dual_objective_value = false) + @test isnan(MOI.get(mock, MOI.DualObjectiveValue())) + return +end + +function test_mock_deprecated() + mock = MOIU.MockOptimizer(MOIU.Model{Float64}()) + x = MOI.add_variable(mock) + c = MOI.add_constraint( + mock, + MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, x)], 0.0), + MOI.EqualTo(2.0), + ) + MOIU.set_mock_optimize!( + mock, + m -> MOI.Utilities.mock_optimize!( + m, + MOI.OPTIMAL, + MOI.FEASIBLE_POINT, + MOI.NO_SOLUTION, + var_basis = [MOI.BASIC], + con_basis = [ + (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => + [MOI.BASIC], + ], + ), + ) + @test_logs (:warn,) (:warn,) MOI.optimize!(mock) + return +end + +end # module + +TestMockOptimizer.runtests()