From 48cb9c5e1d992a739abbe29377e2ea5559097f45 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 2 Jun 2021 15:13:59 +1200 Subject: [PATCH 1/6] WIP: breaking changes for MOI 0.10.0 --- Project.toml | 3 +- src/MOI_wrapper/MOI_wrapper.jl | 67 ++++++++++++++++++++-------------- test/MOI_wrapper.jl | 2 +- test/runtests.jl | 5 +++ 4 files changed, 47 insertions(+), 30 deletions(-) diff --git a/Project.toml b/Project.toml index 490cbea..fe7230c 100644 --- a/Project.toml +++ b/Project.toml @@ -19,7 +19,8 @@ MathOptInterface = "0.9.7" julia = "1" [extras] +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test"] +test = ["Pkg", "Test"] diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index 9404638..03d87a2 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -13,7 +13,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer termination_status::Cint """ - Optimizer(; kwargs...) + Optimizer() Create a new Cbc Optimizer. """ @@ -27,8 +27,17 @@ mutable struct Optimizer <: MOI.AbstractOptimizer 0.0, Cint(-1), ) + if length(kwargs) > 0 + @warn("""Passing optimizer attributes as keyword arguments to + Cbc.Optimizer is deprecated. Use + MOI.set(model, MOI.RawOptimizerAttribute("key"), value) + or + JuMP.set_optimizer_attribute(model, "key", value) + instead. + """) + end for (key, value) in kwargs - MOI.set(model, MOI.RawParameter(key), value) + MOI.set(model, MOI.RawOptimizerAttribute("$(key)"), value) end finalizer(model) do m return Cbc_deleteModel(m) @@ -40,17 +49,21 @@ end Base.cconvert(::Type{Ptr{Cvoid}}, model::Optimizer) = model Base.unsafe_convert(::Type{Ptr{Cvoid}}, model::Optimizer) = model.inner -function MOI.supports(::Optimizer, ::MOI.RawParameter) +function MOI.supports(::Optimizer, ::MOI.RawOptimizerAttribute) # TODO(odow): There is no programatical way throught the C API to check if a # parameter name (or value) is valid. Fix this upstream. return true end -function MOI.set(model::Optimizer, param::MOI.RawParameter, value) +function MOI.set(model::Optimizer, param::MOI.RawOptimizerAttribute, value) return MOI.set(model, param, string(value)) end -function MOI.set(model::Optimizer, param::MOI.RawParameter, value::String) +function MOI.set( + model::Optimizer, + param::MOI.RawOptimizerAttribute, + value::String, +) if !MOI.supports(model, param) throw(MOI.UnsupportedAttribute(param)) end @@ -62,7 +75,7 @@ function MOI.set(model::Optimizer, param::MOI.RawParameter, value::String) return end -function MOI.get(model::Optimizer, param::MOI.RawParameter) +function MOI.get(model::Optimizer, param::MOI.RawOptimizerAttribute) # TODO: This gives a poor error message if the name of the parameter is invalid. return model.params[string(param.name)] end @@ -86,7 +99,7 @@ function MOI.set(model::Optimizer, ::MOI.TimeLimitSec, value) delete!(model.params, "seconds") Cbc_setParameter(model, "seconds", "??") else - MOI.set(model, MOI.RawParameter("seconds"), value) + MOI.set(model, MOI.RawOptimizerAttribute("seconds"), value) end return end @@ -186,7 +199,7 @@ end ### function _column_value(map::MOI.Utilities.IndexMap, index::MOI.VariableIndex) - return map.varmap[index].value + return map[index].value end function _column_value(map::MOI.Utilities.IndexMap, func::MOI.SingleVariable) @@ -335,8 +348,8 @@ function _add_terms( func::MOI.ScalarAffineFunction{Float64}, ) where {S} for term in func.terms - push!(model.row_idx, mapping.conmap[index].value) - push!(model.col_idx, _column_value(mapping, term.variable_index)) + push!(model.row_idx, mapping[index].value) + push!(model.col_idx, _column_value(mapping, term.variable)) push!(model.values, term.coefficient) end return @@ -350,7 +363,7 @@ function _load_constraint( set::MOI.EqualTo, ) _add_terms(model, mapping, index, func) - row = mapping.conmap[index].value + row = mapping[index].value model.row_lb[row] = set.value - func.constant model.row_ub[row] = set.value - func.constant return @@ -364,7 +377,7 @@ function _load_constraint( set::MOI.GreaterThan, ) _add_terms(model, mapping, index, func) - row = mapping.conmap[index].value + row = mapping[index].value model.row_lb[row] = set.lower - func.constant return end @@ -377,7 +390,7 @@ function _load_constraint( set::MOI.LessThan, ) _add_terms(model, mapping, index, func) - row = mapping.conmap[index].value + row = mapping[index].value model.row_ub[row] = set.upper - func.constant return end @@ -390,7 +403,7 @@ function _load_constraint( set::MOI.Interval, ) _add_terms(model, mapping, index, func) - row = mapping.conmap[index].value + row = mapping[index].value model.row_ub[row] = set.upper - func.constant model.row_lb[row] = set.lower - func.constant return @@ -475,7 +488,7 @@ function _load_objective( # is initialized with zeros in the constructor and `_load_objective` only # gets called once. for term in f.terms - column = _column_value(mapping, term.variable_index) + column = _column_value(mapping, term.variable) model.obj[column] += term.coefficient end model.objective_constant = f.constant @@ -536,8 +549,8 @@ function _create_constraint_indices_for_types( for index in MOI.get(src, MOI.ListOfConstraintIndices{MOI.SingleVariable,S}()) f = MOI.get(src, MOI.ConstraintFunction(), index) - i = mapping.varmap[f.variable].value - mapping.conmap[index] = MOI.ConstraintIndex{MOI.SingleVariable,S}(i) + i = mapping[f.variable].value + mapping[index] = MOI.ConstraintIndex{MOI.SingleVariable,S}(i) end return num_rows end @@ -552,7 +565,7 @@ function _create_constraint_indices_for_types( n = 0 for index in MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) n += 1 - mapping.conmap[index] = MOI.ConstraintIndex{F,S}(n) + mapping[index] = MOI.ConstraintIndex{F,S}(n) end return num_rows end @@ -566,7 +579,7 @@ function _create_constraint_indices_for_types( ) for index in MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) num_rows += 1 - mapping.conmap[index] = MOI.ConstraintIndex{F,S}(num_rows) + mapping[index] = MOI.ConstraintIndex{F,S}(num_rows) end return num_rows end @@ -577,7 +590,7 @@ function _create_constraint_indices( mapping::MOI.Utilities.IndexMap, ) n = 0 - for (F, S) in MOI.get(src, MOI.ListOfConstraints()) + for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) if !(MOI.supports_constraint(dest, F, S)) throw( MOI.UnsupportedConstraint{F,S}( @@ -597,9 +610,9 @@ function _create_variable_indices( mapping::MOI.Utilities.IndexMap, ) for (i, x) in enumerate(MOI.get(src, MOI.ListOfVariableIndices())) - mapping.varmap[x] = MOI.VariableIndex(i) + mapping[x] = MOI.VariableIndex(i) end - return Cint(length(mapping.varmap)) + return Cint(length(mapping.var_map)) end function MOI.copy_to( @@ -615,7 +628,7 @@ function MOI.copy_to( _load_objective(tmp_model, mapping, cbc_dest, src) - for (F, S) in MOI.get(src, MOI.ListOfConstraints()) + for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) # The type of `F` and `S` is not type-stable, so we use a function # barrier (`_load_constraints`) to improve performance. _load_constraints(tmp_model, src, mapping, F, S) @@ -671,7 +684,7 @@ function MOI.copy_to( end end - for (F, S) in MOI.get(src, MOI.ListOfConstraints()) + for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) cis_src = MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) MOI.Utilities.pass_attributes( cbc_dest, @@ -756,9 +769,7 @@ function MOI.optimize!(model::Optimizer) return end -function MOI.get(model::Optimizer, ::MOI.SolveTime) - return model.solve_time -end +MOI.get(model::Optimizer, ::MOI.SolveTimeSec) = model.solve_time function MOI.get(model::Optimizer, ::MOI.NumberOfVariables) return Cbc_getNumCols(model) @@ -939,7 +950,7 @@ function MOI.get(model::Optimizer, ::MOI.TerminationStatus) end function MOI.get(model::Optimizer, attr::MOI.PrimalStatus) - if attr.N != 1 + if attr.result_index != 1 return MOI.NO_SOLUTION elseif MOI.get(model, MOI.ResultCount()) == 1 return MOI.FEASIBLE_POINT diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 0fd8f1e..5ad8162 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -26,7 +26,7 @@ const CACHED = MOI.Utilities.CachingOptimizer( ) const BRIDGED = MOI.Bridges.full_bridge_optimizer(CACHED, Float64) -const CONFIG = MOI.Test.TestConfig(duals = false, infeas_certificates = false) +const CONFIG = MOI.Test.Config(duals = false, infeas_certificates = false) function test_basic_constraint_tests() return MOI.Test.basic_constraint_tests(CACHED, CONFIG) diff --git a/test/runtests.jl b/test/runtests.jl index 858e6e5..bf57999 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,8 @@ +if get(ENV, "GITHUB_ACTIONS", "") == "true" + import Pkg + Pkg.add(Pkg.PackageSpec(name = "MathOptInterface", rev = "master")) +end + using Test @testset "MOI" begin From 1ebddcb936894b89cd6c0211b2000437f134cd47 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 10 Aug 2021 14:02:05 +1200 Subject: [PATCH 2/6] Update tests to MOI.Test --- test/MOI_wrapper.jl | 153 ++++++++++++++++---------------------------- 1 file changed, 56 insertions(+), 97 deletions(-) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 5ad8162..0bd7539 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -6,100 +6,69 @@ import Cbc const MOI = MathOptInterface -const OPTIMIZER = Cbc.Optimizer() -MOI.set(OPTIMIZER, MOI.Silent(), true) - -function test_SolverName() - @test MOI.get(OPTIMIZER, MOI.SolverName()) == "COIN Branch-and-Cut (Cbc)" +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return end -function test_supports_default_copy_to() - @test !MOI.Utilities.supports_allocate_load(OPTIMIZER, false) - @test !MOI.Utilities.supports_allocate_load(OPTIMIZER, true) - @test !MOI.Utilities.supports_default_copy_to(OPTIMIZER, false) - @test !MOI.Utilities.supports_default_copy_to(OPTIMIZER, true) +function test_SolverName() + @test MOI.get(Cbc.Optimizer(), MOI.SolverName()) == + "COIN Branch-and-Cut (Cbc)" end -const CACHED = MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - OPTIMIZER, -) -const BRIDGED = MOI.Bridges.full_bridge_optimizer(CACHED, Float64) - -const CONFIG = MOI.Test.Config(duals = false, infeas_certificates = false) - -function test_basic_constraint_tests() - return MOI.Test.basic_constraint_tests(CACHED, CONFIG) +function test_supports_incremental_interface() + model = Cbc.Optimizer() + @test !MOI.supports_incremental_interface(model, false) + @test !MOI.supports_incremental_interface(model, true) + return end -function test_Unit() - return MOI.Test.unittest( - BRIDGED, - CONFIG, - [ - # TODO(odow): implement attributes: - "number_threads", - - # INFEASIBLE_OR_UNBOUNDED instead of DUAL_INFEASIBLE - "solve_unbounded_model", - - # No quadratics - "delete_soc_variables", - "solve_qcp_edge_cases", - "solve_qp_edge_cases", - ], +function test_runtests() + model = MOI.Bridges.full_bridge_optimizer( + MOI.Utilities.CachingOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), + Cbc.Optimizer(), + ), + Float64, ) -end - -function test_solvername() - @test MOI.get(CACHED, MOI.SolverName()) == "COIN Branch-and-Cut (Cbc)" -end - -function test_default_objective_test() - return MOI.Test.default_objective_test(CACHED) -end - -function test_default_status_test() - return MOI.Test.default_status_test(CACHED) -end - -function test_nametest() - return MOI.Test.nametest(CACHED) -end - -function test_validtest() - return MOI.Test.validtest(CACHED) -end - -function test_emptytest() - return MOI.Test.emptytest(BRIDGED) -end - -function test_orderedindicestest() - return MOI.Test.orderedindicestest(CACHED) -end - -function test_ContinuousLinear() - return MOI.Test.contlineartest(BRIDGED, CONFIG) -end - -function test_IntegerLinear() - return MOI.Test.intlineartest( - BRIDGED, - CONFIG, - [ - # Cbc does not support indicator constraints. - "indicator1", - "indicator2", - "indicator3", - "indicator4", - # SOS issues - "int2", - ], + MOI.set(model, MOI.Silent(), true) + MOI.Test.runtests( + model, + MOI.Test.Config( + exclude = Any[ + MOI.ConstraintDual, + MOI.DualObjectiveValue, + MOI.ConstraintDual(), + MOI.ConstraintBasisStatus, + MOI.VariableBasisStatus, + ], + ), + exclude = [ + # TODO(odow): bug in Cbc.jl + "test_model_copy_to_UnsupportedAttribute", + # TODO(odow): bug in MOI + "test_model_LowerBoundAlreadySet", + "test_model_UpperBoundAlreadySet", + # TODO(odow): upstream bug in Cbc + "test_linear_Indicator_", + "test_linear_SOS1_integration", + "test_linear_SOS2_integration", + # Can't prove infeasible. + "test_conic_NormInfinityCone_INFEASIBLE", + "test_conic_NormOneCone_INFEASIBLE", + "test_solve_TerminationStatus_DUAL_INFEASIBLE", + ] ) + return end -function test_Testparams() +function test_params() # Note: we generate a non-trivial problem to ensure that Cbc struggles to # find a solution at the root node. knapsack_model = MOI.Utilities.Model{Float64}() @@ -138,9 +107,10 @@ function test_Testparams() MOI.optimize!(model) @test MOI.get(model, MOI.TerminationStatus()) == MOI.SOLUTION_LIMIT @test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + return end -function test_TestPrimalStatus() +function test_PrimalStatus() model = MOI.Utilities.Model{Float64}() x = MOI.add_variable(model) MOI.add_constraint(model, x, MOI.GreaterThan(1.0)) @@ -148,18 +118,7 @@ function test_TestPrimalStatus() cbc = Cbc.Optimizer() MOI.copy_to(cbc, model) MOI.optimize!(cbc) - return MOI.get(cbc, MOI.PrimalStatus()) == MOI.NO_SOLUTION -end - -function runtests() - for name in names(@__MODULE__; all = true) - if !startswith("$name", "test_") - continue - end - @testset "$(name)" begin - getfield(@__MODULE__, name)() - end - end + MOI.get(cbc, MOI.PrimalStatus()) == MOI.NO_SOLUTION return end From 2266dfda01d4c6d0dbc0e1a0ca59cc947955b0a8 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 10 Aug 2021 14:06:36 +1200 Subject: [PATCH 3/6] Fix formatting --- src/Cbc.jl | 14 ++++++++------ test/MOI_wrapper.jl | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Cbc.jl b/src/Cbc.jl index 0d86679..9871b63 100644 --- a/src/Cbc.jl +++ b/src/Cbc.jl @@ -57,12 +57,14 @@ end # TODO(odow): remove at Cbc.jl v1.0.0. function CbcSolver(args...; kwargs...) - error(""" - `CbcSolver` is no longer supported. If you are using JuMP, upgrade to the - latest version and use `Cbc.Optimizer` instead. If you are using - MathProgBase (e.g., via `lingprog`), you will need to upgrade to - MathOptInterface (https://github.com/jump-dev/MathOptInterface.jl). - """) + return error( + """ + `CbcSolver` is no longer supported. If you are using JuMP, upgrade to the + latest version and use `Cbc.Optimizer` instead. If you are using + MathProgBase (e.g., via `lingprog`), you will need to upgrade to + MathOptInterface (https://github.com/jump-dev/MathOptInterface.jl). + """, + ) end export CbcSolver diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 0bd7539..455b6d8 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -44,6 +44,7 @@ function test_runtests() exclude = Any[ MOI.ConstraintDual, MOI.DualObjectiveValue, + # TODO(odow): remove when MOI updated MOI.ConstraintDual(), MOI.ConstraintBasisStatus, MOI.VariableBasisStatus, @@ -63,7 +64,7 @@ function test_runtests() "test_conic_NormInfinityCone_INFEASIBLE", "test_conic_NormOneCone_INFEASIBLE", "test_solve_TerminationStatus_DUAL_INFEASIBLE", - ] + ], ) return end From f88305129258ab22669cc496ebc376c3db660c7f Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 2 Sep 2021 14:40:35 +1200 Subject: [PATCH 4/6] Updates for latest MOI --- src/MOI_wrapper/MOI_wrapper.jl | 123 +++++++++++++-------------------- test/MOI_wrapper.jl | 12 ++-- 2 files changed, 53 insertions(+), 82 deletions(-) diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index 03d87a2..751bf42 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -76,7 +76,8 @@ function MOI.set( end function MOI.get(model::Optimizer, param::MOI.RawOptimizerAttribute) - # TODO: This gives a poor error message if the name of the parameter is invalid. + # TODO: This gives a poor error message if the name of the parameter is + # invalid. return model.params[string(param.name)] end @@ -195,20 +196,14 @@ mutable struct _CbcModelFormat end ### -### SingleVariable-in-{EqualTo,LessThan,GreaterThan,Interval,ZeroOne,Integer} +### VariableIndex-in-{EqualTo,LessThan,GreaterThan,Interval,ZeroOne,Integer} ### -function _column_value(map::MOI.Utilities.IndexMap, index::MOI.VariableIndex) - return map[index].value -end - -function _column_value(map::MOI.Utilities.IndexMap, func::MOI.SingleVariable) - return _column_value(map, func.variable) -end +_column_value(map::MOI.IndexMap, index::MOI.VariableIndex) = map[index].value function MOI.supports_constraint( ::Optimizer, - ::Type{MOI.SingleVariable}, + ::Type{MOI.VariableIndex}, ::Type{ <:Union{ MOI.EqualTo{Float64}, @@ -234,8 +229,8 @@ end function _load_constraint( ::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, - func::MOI.SingleVariable, + mapping::MOI.IndexMap, + func::MOI.VariableIndex, set::MOI.EqualTo, ) column = _column_value(mapping, func) @@ -247,8 +242,8 @@ end function _load_constraint( ::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, - func::MOI.SingleVariable, + mapping::MOI.IndexMap, + func::MOI.VariableIndex, set::MOI.LessThan, ) column = _column_value(mapping, func) @@ -259,8 +254,8 @@ end function _load_constraint( ::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, - func::MOI.SingleVariable, + mapping::MOI.IndexMap, + func::MOI.VariableIndex, set::MOI.GreaterThan, ) column = _column_value(mapping, func) @@ -271,8 +266,8 @@ end function _load_constraint( ::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, - func::MOI.SingleVariable, + mapping::MOI.IndexMap, + func::MOI.VariableIndex, set::MOI.Interval, ) column = _column_value(mapping, func) @@ -284,8 +279,8 @@ end function _load_constraint( ::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, - func::MOI.SingleVariable, + mapping::MOI.IndexMap, + func::MOI.VariableIndex, ::MOI.ZeroOne, ) push!(model.binary, _column_value(mapping, func) - 1) @@ -295,8 +290,8 @@ end function _load_constraint( ::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, - func::MOI.SingleVariable, + mapping::MOI.IndexMap, + func::MOI.VariableIndex, ::MOI.Integer, ) push!(model.integer, _column_value(mapping, func) - 1) @@ -306,7 +301,7 @@ end function _load_constraints( model::_CbcModelFormat, src::MOI.ModelLike, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, ) @@ -343,7 +338,7 @@ end function _add_terms( model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, index::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},S}, func::MOI.ScalarAffineFunction{Float64}, ) where {S} @@ -358,7 +353,7 @@ end function _load_constraint( index::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, func::MOI.ScalarAffineFunction, set::MOI.EqualTo, ) @@ -372,7 +367,7 @@ end function _load_constraint( index::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, func::MOI.ScalarAffineFunction, set::MOI.GreaterThan, ) @@ -385,7 +380,7 @@ end function _load_constraint( index::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, func::MOI.ScalarAffineFunction, set::MOI.LessThan, ) @@ -398,7 +393,7 @@ end function _load_constraint( index::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, func::MOI.ScalarAffineFunction, set::MOI.Interval, ) @@ -410,31 +405,31 @@ function _load_constraint( end function _load_constraint( - index::MOI.ConstraintIndex, + ::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, func::MOI.VectorOfVariables, set::MOI.SOS1{Float64}, ) push!(model.sos1_starts, Cint(length(model.sos1_weights))) append!(model.sos1_weights, set.weights) for v in func.variables - push!(model.sos1_indices, Cint(v.value - 1)) + push!(model.sos1_indices, _column_value(mapping, v)) end return end function _load_constraint( - index::MOI.ConstraintIndex, + ::MOI.ConstraintIndex, model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, func::MOI.VectorOfVariables, set::MOI.SOS2{Float64}, ) push!(model.sos2_starts, Cint(length(model.sos2_weights))) append!(model.sos2_weights, set.weights) for v in func.variables - push!(model.sos2_indices, Cint(v.value - 1)) + push!(model.sos2_indices, _column_value(mapping, v)) end return end @@ -474,7 +469,7 @@ end function _load_objective( model::_CbcModelFormat, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, dest::Optimizer, src::MOI.ModelLike, ) @@ -541,23 +536,23 @@ end function _create_constraint_indices_for_types( src::MOI.ModelLike, - mapping::MOI.Utilities.IndexMap, - ::Type{MOI.SingleVariable}, + mapping::MOI.IndexMap, + ::Type{MOI.VariableIndex}, S::Type{<:MOI.AbstractSet}, num_rows::Int, ) - for index in - MOI.get(src, MOI.ListOfConstraintIndices{MOI.SingleVariable,S}()) + indices = MOI.get(src, MOI.ListOfConstraintIndices{MOI.VariableIndex,S}()) + for index in indices f = MOI.get(src, MOI.ConstraintFunction(), index) - i = mapping[f.variable].value - mapping[index] = MOI.ConstraintIndex{MOI.SingleVariable,S}(i) + i = mapping[f].value + mapping[index] = MOI.ConstraintIndex{MOI.VariableIndex,S}(i) end return num_rows end function _create_constraint_indices_for_types( src::MOI.ModelLike, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, F::Type{MOI.VectorOfVariables}, S::Type{<:Union{MOI.SOS1{Float64},MOI.SOS2{Float64}}}, num_rows::Int, @@ -572,7 +567,7 @@ end function _create_constraint_indices_for_types( src::MOI.ModelLike, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, F::Type{MOI.ScalarAffineFunction{Float64}}, S::Type{<:MOI.AbstractScalarSet}, num_rows::Int, @@ -587,60 +582,50 @@ end function _create_constraint_indices( dest::Optimizer, src::MOI.ModelLike, - mapping::MOI.Utilities.IndexMap, + mapping::MOI.IndexMap, ) n = 0 for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) if !(MOI.supports_constraint(dest, F, S)) throw( MOI.UnsupportedConstraint{F,S}( - "Cbc.Optimizer does not support constraints of type $F -in- $S.", + "Cbc does not support constraints of type $F-in-$S.", ), ) end # The type of `F` and `S` is not type-stable, so we use a function - # barrier (`_create_constraint_indices_for_types`) to improve performance. + # barrier (`_create_constraint_indices_for_types`) to improve + # performance. n = _create_constraint_indices_for_types(src, mapping, F, S, n) end return Cint(n) end -function _create_variable_indices( - src::MOI.ModelLike, - mapping::MOI.Utilities.IndexMap, -) +function _create_variable_indices(src::MOI.ModelLike, mapping::MOI.IndexMap) for (i, x) in enumerate(MOI.get(src, MOI.ListOfVariableIndices())) mapping[x] = MOI.VariableIndex(i) end return Cint(length(mapping.var_map)) end -function MOI.copy_to( - cbc_dest::Optimizer, - src::MOI.ModelLike; - copy_names::Bool = false, -) +function MOI.copy_to(cbc_dest::Optimizer, src::MOI.ModelLike) @assert MOI.is_empty(cbc_dest) - mapping = MOI.Utilities.IndexMap() + mapping = MOI.IndexMap() num_cols = _create_variable_indices(src, mapping) num_rows = _create_constraint_indices(cbc_dest, src, mapping) tmp_model = _CbcModelFormat(num_rows, num_cols) - _load_objective(tmp_model, mapping, cbc_dest, src) - for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) # The type of `F` and `S` is not type-stable, so we use a function # barrier (`_load_constraints`) to improve performance. _load_constraints(tmp_model, src, mapping, F, S) end - # Since Cbc doesn't have an explicit binary variable, we need to add [0, 1] # bounds and make it integer (which is done at the end of this function). for column in tmp_model.binary tmp_model.col_lb[column+1] = max(tmp_model.col_lb[column+1], 0.0) tmp_model.col_ub[column+1] = min(tmp_model.col_ub[column+1], 1.0) end - A = SparseArrays.sparse( tmp_model.row_idx, tmp_model.col_idx, @@ -648,7 +633,6 @@ function MOI.copy_to( tmp_model.num_rows, tmp_model.num_cols, ) - Cbc_loadProblem( cbc_dest, tmp_model.num_cols, @@ -662,20 +646,15 @@ function MOI.copy_to( tmp_model.row_lb, tmp_model.row_ub, ) - MOI.Utilities.pass_attributes( cbc_dest, src, - copy_names, mapping, MOI.get(src, MOI.ListOfVariableIndices()), ) - for attr in MOI.get(src, MOI.ListOfModelAttributesSet()) if attr isa MOI.ObjectiveFunction continue # Already copied - elseif !copy_names && attr isa MOI.Name - continue end value = MOI.get(src, attr) if value !== nothing @@ -683,18 +662,10 @@ function MOI.copy_to( MOI.set(cbc_dest, attr, mapped_value) end end - for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) cis_src = MOI.get(src, MOI.ListOfConstraintIndices{F,S}()) - MOI.Utilities.pass_attributes( - cbc_dest, - src, - copy_names, - mapping, - cis_src, - ) + MOI.Utilities.pass_attributes(cbc_dest, src, mapping, cis_src) end - cbc_dest.objective_constant = tmp_model.objective_constant if length(tmp_model.integer) > 0 Cbc_setInteger.(cbc_dest, tmp_model.integer) @@ -845,7 +816,7 @@ end function MOI.get( model::Optimizer, attr::MOI.ConstraintPrimal, - index::MOI.ConstraintIndex{MOI.SingleVariable,<:Any}, + index::MOI.ConstraintIndex{MOI.VariableIndex,<:Any}, ) MOI.check_result_index_bounds(model, attr) return MOI.get(model, MOI.VariablePrimal(), MOI.VariableIndex(index.value)) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 455b6d8..e9ba3cd 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -23,9 +23,7 @@ function test_SolverName() end function test_supports_incremental_interface() - model = Cbc.Optimizer() - @test !MOI.supports_incremental_interface(model, false) - @test !MOI.supports_incremental_interface(model, true) + @test !MOI.supports_incremental_interface(Cbc.Optimizer()) return end @@ -44,15 +42,16 @@ function test_runtests() exclude = Any[ MOI.ConstraintDual, MOI.DualObjectiveValue, - # TODO(odow): remove when MOI updated - MOI.ConstraintDual(), MOI.ConstraintBasisStatus, MOI.VariableBasisStatus, ], ), exclude = [ # TODO(odow): bug in Cbc.jl + "test_constraint_Indicator_ACTIVATE_ON_ZERO", "test_model_copy_to_UnsupportedAttribute", + "test_model_ModelFilter_AbstractConstraintAttribute", + "test_objective_FEASIBILITY_SENSE_clears_objective", # TODO(odow): bug in MOI "test_model_LowerBoundAlreadySet", "test_model_UpperBoundAlreadySet", @@ -60,6 +59,7 @@ function test_runtests() "test_linear_Indicator_", "test_linear_SOS1_integration", "test_linear_SOS2_integration", + "test_solve_SOS2_", # Can't prove infeasible. "test_conic_NormInfinityCone_INFEASIBLE", "test_conic_NormOneCone_INFEASIBLE", @@ -75,7 +75,7 @@ function test_params() knapsack_model = MOI.Utilities.Model{Float64}() N = 100 x = MOI.add_variables(knapsack_model, N) - MOI.add_constraint.(knapsack_model, MOI.SingleVariable.(x), MOI.ZeroOne()) + MOI.add_constraint.(knapsack_model, x, MOI.ZeroOne()) MOI.add_constraint( knapsack_model, MOI.ScalarAffineFunction( From be7e78a276bd808d2e0f9e7d07e93ba355778c33 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 2 Sep 2021 16:26:42 +1200 Subject: [PATCH 5/6] Fix FEASIBILITY_SENSE test --- src/MOI_wrapper/MOI_wrapper.jl | 3 +++ test/MOI_wrapper.jl | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index 751bf42..cafe89d 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -451,6 +451,9 @@ function MOI.set( Cbc_setObjSense(model, 1.0) else @assert sense == MOI.FEASIBILITY_SENSE + for col in Cint(0):Cint(Cbc_getNumCols(model) - 1) + Cbc_setObjCoeff(model, col, 0.0) + end Cbc_setObjSense(model, 0.0) end return diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index e9ba3cd..e907219 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -51,7 +51,6 @@ function test_runtests() "test_constraint_Indicator_ACTIVATE_ON_ZERO", "test_model_copy_to_UnsupportedAttribute", "test_model_ModelFilter_AbstractConstraintAttribute", - "test_objective_FEASIBILITY_SENSE_clears_objective", # TODO(odow): bug in MOI "test_model_LowerBoundAlreadySet", "test_model_UpperBoundAlreadySet", From 871fa7cf7831b71978a6eaa38e1848e85c49f0d9 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 8 Sep 2021 09:45:19 +1200 Subject: [PATCH 6/6] Update to MOI 0.10 --- Project.toml | 5 ++--- test/MOI_wrapper.jl | 8 +++----- test/runtests.jl | 5 ----- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Project.toml b/Project.toml index fe7230c..19c4719 100644 --- a/Project.toml +++ b/Project.toml @@ -15,12 +15,11 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" BinaryProvider = "0.5.9" CEnum = "0.3, 0.4" Cbc_jll = "=2.10.5, ~200.1000.500" -MathOptInterface = "0.9.7" +MathOptInterface = "0.10" julia = "1" [extras] -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Pkg", "Test"] +test = ["Test"] diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index e907219..e3b191a 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -48,17 +48,15 @@ function test_runtests() ), exclude = [ # TODO(odow): bug in Cbc.jl - "test_constraint_Indicator_ACTIVATE_ON_ZERO", "test_model_copy_to_UnsupportedAttribute", "test_model_ModelFilter_AbstractConstraintAttribute", # TODO(odow): bug in MOI "test_model_LowerBoundAlreadySet", "test_model_UpperBoundAlreadySet", # TODO(odow): upstream bug in Cbc - "test_linear_Indicator_", - "test_linear_SOS1_integration", - "test_linear_SOS2_integration", - "test_solve_SOS2_", + "_Indicator_", + "_SOS1_", + "_SOS2_", # Can't prove infeasible. "test_conic_NormInfinityCone_INFEASIBLE", "test_conic_NormOneCone_INFEASIBLE", diff --git a/test/runtests.jl b/test/runtests.jl index bf57999..858e6e5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,3 @@ -if get(ENV, "GITHUB_ACTIONS", "") == "true" - import Pkg - Pkg.add(Pkg.PackageSpec(name = "MathOptInterface", rev = "master")) -end - using Test @testset "MOI" begin