diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index 9404638..14a7191 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -11,6 +11,14 @@ mutable struct Optimizer <: MOI.AbstractOptimizer objective_constant::Float64 solve_time::Float64 termination_status::Cint + # A cache for the primal solution vector to avoid having to query the full + # vector in order to lookup one element. + primal_solution_cache::Vector{Float64} + # Similar cache for constraints evaluated at primal solution. + primal_constraint_cache::Vector{Float64} + + solution_status::MOI.TerminationStatusCode + num_integers::Int """ Optimizer(; kwargs...) @@ -26,6 +34,10 @@ mutable struct Optimizer <: MOI.AbstractOptimizer 0.0, 0.0, Cint(-1), + Float64[], + Float64[], + MOI.OPTIMIZE_NOT_CALLED, + 0, ) for (key, value) in kwargs MOI.set(model, MOI.RawParameter(key), value) @@ -111,6 +123,10 @@ function MOI.empty!(model::Optimizer) Cbc_setParameter(model, "logLevel", "0") end empty!(model.variable_start) + model.primal_solution_cache = Float64[] + model.primal_constraint_cache = Float64[] + model.solution_status = MOI.OPTIMIZE_NOT_CALLED + model.num_integers = 0 return end @@ -728,16 +744,14 @@ end function _unsafe_wrap_cbc_array( model::Optimizer, f::F, - n::Integer, - indices; + n::Integer; own::Bool = false, ) where {F<:Function} p = f(model) if p == C_NULL - return map(x -> NaN, indices) + return fill(NaN, n) end - x = unsafe_wrap(Array, p, (n,); own = own) - return x[indices] + return unsafe_wrap(Array, p, (n,); own = own) end function MOI.optimize!(model::Optimizer) @@ -753,6 +767,20 @@ function MOI.optimize!(model::Optimizer) t = time() model.termination_status = Cbc_solve(model) model.solve_time = time() - t + model.solution_status = _get_solution_status(model) + model.num_integers = Cbc_getNumIntegers(model) + if MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT + model.primal_solution_cache = _unsafe_wrap_cbc_array( + model, + Cbc_getColSolution, + Cbc_getNumCols(model), + ) + model.primal_constraint_cache = _unsafe_wrap_cbc_array( + model, + Cbc_getRowActivity, + Cbc_getNumRows(model), + ) + end return end @@ -795,12 +823,7 @@ function MOI.get( x::MOI.VariableIndex, ) MOI.check_result_index_bounds(model, attr) - return _unsafe_wrap_cbc_array( - model, - Cbc_getColSolution, - Cbc_getNumCols(model), - x.value, - ) + return model.primal_solution_cache[x.value] end function MOI.get( @@ -809,12 +832,7 @@ function MOI.get( x::Vector{MOI.VariableIndex}, ) MOI.check_result_index_bounds(model, attr) - return _unsafe_wrap_cbc_array( - model, - Cbc_getColSolution, - Cbc_getNumCols(model), - [xi.value for xi in x], - ) + return Float64[model.primal_solution_cache[xi.value] for xi in x] end function MOI.get( @@ -823,12 +841,7 @@ function MOI.get( index::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},<:Any}, ) MOI.check_result_index_bounds(model, attr) - return _unsafe_wrap_cbc_array( - model, - Cbc_getRowActivity, - Cbc_getNumRows(model), - index.value, - ) + return model.primal_constraint_cache[index.value] end function MOI.get( @@ -897,7 +910,7 @@ function MOI.get(model::Optimizer, ::MOI.ResultCount) # can be removed when we drop support for Julia 1.0 and the 2.10.3 JLL. return Cbc_numberSavedSolutions(model) > 0 ? 1 : 0 end - if Cbc_getNumIntegers(model) == 0 + if model.num_integers == 0 # Cbc forwards the solve to the LP solver if there are no integers, so # check the termination status for the result count. return model.termination_status == 0 ? 1 : 0 @@ -906,6 +919,9 @@ function MOI.get(model::Optimizer, ::MOI.ResultCount) end function MOI.get(model::Optimizer, ::MOI.TerminationStatus) + return model.solution_status +end +function _get_solution_status(model::Optimizer) status = model.termination_status if status == -1 return MOI.OPTIMIZE_NOT_CALLED