Update to JuMP 1.2 and MOI 1.7

This commit is contained in:
2022-08-17 11:23:36 -05:00
parent 8da3e9846d
commit dd13654e3e
5 changed files with 260 additions and 235 deletions

View File

@@ -16,6 +16,7 @@ end
function read!(mip::MIP, filename::AbstractString)::Nothing
@threads for t = 1:nthreads()
model = read_from_file(filename)
set_optimizer(model, mip.constructor)
mip.optimizers[t] = backend(model)
_replace_zero_one!(mip.optimizers[t])
if t == 1
@@ -24,21 +25,20 @@ function read!(mip::MIP, filename::AbstractString)::Nothing
mip.sense = _get_objective_sense(mip.optimizers[t])
end
_relax_integrality!(mip.optimizers[t])
set_optimizer(model, mip.constructor)
set_silent(model)
end
return
end
function _assert_supported(optimizer::MOI.AbstractOptimizer)::Nothing
types = MOI.get(optimizer, MOI.ListOfConstraints())
types = MOI.get(optimizer, MOI.ListOfConstraintTypesPresent())
for (F, S) in types
_assert_supported(F, S)
end
end
function _assert_supported(F::DataType, S::DataType)::Nothing
if F in [MOI.ScalarAffineFunction{Float64}, MOI.SingleVariable] && S in [
function _assert_supported(F::Type, S::Type)::Nothing
if F in [MOI.ScalarAffineFunction{Float64}, MOI.VariableIndex] && S in [
MOI.LessThan{Float64},
MOI.GreaterThan{Float64},
MOI.EqualTo{Float64},
@@ -46,7 +46,7 @@ function _assert_supported(F::DataType, S::DataType)::Nothing
]
return
end
if F in [MOI.SingleVariable] && S in [MOI.Integer, MOI.ZeroOne]
if F in [MOI.VariableIndex] && S in [MOI.Integer, MOI.ZeroOne]
return
end
error("MOI constraint not supported: $F in $S")
@@ -64,19 +64,19 @@ function _get_objective_sense(optimizer::MOI.AbstractOptimizer)::Float64
end
_bounds_constraint(v::Variable) =
MOI.ConstraintIndex{MOI.SingleVariable,MOI.Interval{Float64}}(v.index)
MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{Float64}}(v.index)
function _replace_zero_one!(optimizer::MOI.AbstractOptimizer)::Nothing
constrs_to_delete = MOI.ConstraintIndex[]
funcs = MOI.SingleVariable[]
funcs = MOI.VariableIndex[]
sets = Union{MOI.Interval,MOI.Integer}[]
for ci in
MOI.get(optimizer, MOI.ListOfConstraintIndices{MOI.SingleVariable,MOI.ZeroOne}())
MOI.get(optimizer, MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.ZeroOne}())
func = MOI.get(optimizer, MOI.ConstraintFunction(), ci)
var = func.variable
var = func.value
push!(constrs_to_delete, ci)
push!(funcs, MOI.SingleVariable(var))
push!(funcs, MOI.SingleVariable(var))
push!(funcs, MOI.VariableIndex(var))
push!(funcs, MOI.VariableIndex(var))
push!(sets, MOI.Interval{Float64}(0.0, 1.0))
push!(sets, MOI.Integer())
end
@@ -88,9 +88,9 @@ end
function _get_binary_variables(optimizer::MOI.AbstractOptimizer)::Vector{Variable}
vars = Variable[]
for ci in
MOI.get(optimizer, MOI.ListOfConstraintIndices{MOI.SingleVariable,MOI.Integer}())
MOI.get(optimizer, MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.Integer}())
func = MOI.get(optimizer, MOI.ConstraintFunction(), ci)
var = Variable(func.variable.value)
var = Variable(func.value)
MOI.is_valid(optimizer, _bounds_constraint(var)) ||
error("$var is not interval-constrained")
@@ -105,7 +105,7 @@ end
function _relax_integrality!(optimizer::MOI.AbstractOptimizer)::Nothing
indices =
MOI.get(optimizer, MOI.ListOfConstraintIndices{MOI.SingleVariable,MOI.Integer}())
MOI.get(optimizer, MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.Integer}())
MOI.delete(optimizer, indices)
end
@@ -146,7 +146,7 @@ function values(mip::MIP, vars::Vector{Variable})::Array{Float64}
return MOI.get(
mip.optimizers[threadid()],
MOI.VariablePrimal(),
convert.(MOI.VariableIndex, vars),
[MOI.VariableIndex(v.index) for v in vars],
)
end
@@ -170,10 +170,10 @@ function set_bounds!(
)::Nothing
t = threadid()
MOI.delete(mip.optimizers[t], _bounds_constraint.(vars))
funcs = MOI.SingleVariable[]
funcs = MOI.VariableIndex[]
sets = MOI.Interval[]
for j = 1:length(vars)
push!(funcs, MOI.SingleVariable(vars[j]))
push!(funcs, MOI.VariableIndex(vars[j].index))
push!(sets, MOI.Interval(lb[j], ub[j]))
end
MOI.add_constraints(mip.optimizers[t], funcs, sets)
@@ -187,7 +187,7 @@ Return the name of the decision variable `var`.
"""
function name(mip::MIP, var::Variable)::String
t = threadid()
return MOI.get(mip.optimizers[t], MOI.VariableName(), convert(MOI.VariableIndex, var))
return MOI.get(mip.optimizers[t], MOI.VariableName(), MOI.VariableIndex(var.index))
end
convert(::Type{MOI.VariableIndex}, v::Variable) = MOI.VariableIndex(v.index)

View File

@@ -14,7 +14,7 @@ import JuMP: value
Base.@kwdef mutable struct JuMPSolverData
optimizer_factory::Any
basis_status::Dict{ConstraintRef,MOI.BasisStatusCode} = Dict()
basis_status::Dict = Dict()
bin_vars::Vector{JuMP.VariableRef} = []
int_vars::Vector{JuMP.VariableRef} = []
cb_data::Any = nothing
@@ -64,6 +64,7 @@ function _update_solution!(data::JuMPSolverData)
data.reduced_costs = []
data.basis_status = Dict()
# Reduced costs
for var in vars
rc = 0.0
if has_upper_bound(var)
@@ -77,6 +78,13 @@ function _update_solution!(data::JuMPSolverData)
rc += shadow_price(FixRef(var))
end
push!(data.reduced_costs, rc)
# Basis status
data.basis_status[var] = MOI.get(
data.model,
MOI.VariableBasisStatus(),
var,
)
end
try
@@ -85,27 +93,18 @@ function _update_solution!(data::JuMPSolverData)
@warn "Sensitivity analysis is unavailable; ignoring" maxlog = 1
end
basis_status_supported = true
data.dual_values = Dict()
for (ftype, stype) in JuMP.list_of_constraint_types(data.model)
ftype != VariableRef || continue
for constr in JuMP.all_constraints(data.model, ftype, stype)
# Dual values (FIXME: Remove negative sign)
data.dual_values[constr] = -JuMP.dual(constr)
# Basis status
if basis_status_supported
try
data.basis_status[constr] =
MOI.get(data.model, MOI.ConstraintBasisStatus(), constr)
catch
@warn "Basis status is unavailable; ignoring" maxlog = 1
basis_status_supported = false
data.basis_status = Dict()
end
end
data.basis_status[constr] =
MOI.get(data.model, MOI.ConstraintBasisStatus(), constr)
end
end
else
data.reduced_costs = []
data.dual_values = Dict()
@@ -439,18 +438,15 @@ function get_variables(data::JuMPSolverData; with_static::Bool, with_sa::Bool)
if !isempty(data.basis_status)
basis_status = []
for v in vars
basis_status_v = "B"
if has_lower_bound(v)
constr = LowerBoundRef(v)
if data.basis_status[constr] == MOI.NONBASIC
basis_status_v = "L"
end
end
if has_upper_bound(v)
constr = UpperBoundRef(v)
if data.basis_status[constr] == MOI.NONBASIC
basis_status_v = "U"
end
bstatus = data.basis_status[v]
if bstatus == MOI.BASIC
basis_status_v = "B"
elseif bstatus == MOI.NONBASIC_AT_LOWER
basis_status_v = "L"
elseif bstatus == MOI.NONBASIC_AT_UPPER
basis_status_v = "U"
else
error("Unknown basis status: $(bstatus)")
end
push!(basis_status, basis_status_v)
end
@@ -530,7 +526,7 @@ function get_constraints(
end
push!(rhs, rhs_c)
for term in cf.terms
push!(lhs_cols, term.variable_index.value)
push!(lhs_cols, term.variable.value)
push!(lhs_rows, constr_index)
push!(lhs_values, term.coefficient)
end
@@ -631,7 +627,7 @@ function __init_JuMPSolver__()
)
function get_constraint_attrs(self)
attrs = [
return [
"categories",
"dual_values",
"lazy",
@@ -641,18 +637,18 @@ function __init_JuMPSolver__()
"senses",
"user_features",
"slacks",
"basis_status",
"sa_rhs_down",
"sa_rhs_up",
]
if repr(self.data.optimizer_factory) in ["Gurobi.Optimizer"]
append!(attrs, ["basis_status", "sa_rhs_down", "sa_rhs_up"])
end
return attrs
end
get_variables(self; with_static = true, with_sa = true) =
get_variables(self.data; with_static = with_static, with_sa = with_sa)
function get_variable_attrs(self)
attrs = [
return [
"basis_status",
"names",
"categories",
"lower_bounds",
@@ -662,22 +658,13 @@ function __init_JuMPSolver__()
"upper_bounds",
"user_features",
"values",
"sa_obj_down",
"sa_obj_up",
"sa_lb_down",
"sa_lb_up",
"sa_ub_down",
"sa_ub_up",
]
if repr(self.data.optimizer_factory) in ["Gurobi.Optimizer"]
append!(
attrs,
[
"basis_status",
"sa_obj_down",
"sa_obj_up",
"sa_lb_down",
"sa_lb_up",
"sa_ub_down",
"sa_ub_up",
],
)
end
return attrs
end
is_infeasible(self) = is_infeasible(self.data)