|
|
|
@ -4,48 +4,161 @@
|
|
|
|
|
|
|
|
|
|
using KLU
|
|
|
|
|
using TimerOutputs
|
|
|
|
|
using Gurobi
|
|
|
|
|
|
|
|
|
|
function get_basis(model::JuMP.Model)::Basis
|
|
|
|
|
var_basic = Int[]
|
|
|
|
|
var_nonbasic = Int[]
|
|
|
|
|
constr_basic = Int[]
|
|
|
|
|
constr_nonbasic = Int[]
|
|
|
|
|
|
|
|
|
|
# Variables
|
|
|
|
|
for (i, var) in enumerate(all_variables(model))
|
|
|
|
|
bstatus = MOI.get(model, MOI.VariableBasisStatus(), var)
|
|
|
|
|
if bstatus == MOI.BASIC
|
|
|
|
|
push!(var_basic, i)
|
|
|
|
|
elseif bstatus == MOI.NONBASIC_AT_LOWER
|
|
|
|
|
push!(var_nonbasic, i)
|
|
|
|
|
else
|
|
|
|
|
error("Unknown basis status: $bstatus")
|
|
|
|
|
if isa(unsafe_backend(model), Gurobi.Optimizer)
|
|
|
|
|
return get_basis_gurobi(model)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@timeit "Initialization" begin
|
|
|
|
|
var_basic = Int[]
|
|
|
|
|
var_nonbasic = Int[]
|
|
|
|
|
constr_basic = Int[]
|
|
|
|
|
constr_nonbasic = Int[]
|
|
|
|
|
nvars = num_variables(model)
|
|
|
|
|
sizehint!(var_basic, nvars)
|
|
|
|
|
sizehint!(var_nonbasic, nvars)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@timeit "Query variables" begin
|
|
|
|
|
for (i, var) in enumerate(all_variables(model))
|
|
|
|
|
bstatus = MOI.get(model, MOI.VariableBasisStatus(), var)
|
|
|
|
|
if bstatus == MOI.BASIC
|
|
|
|
|
push!(var_basic, i)
|
|
|
|
|
elseif bstatus == MOI.NONBASIC_AT_LOWER
|
|
|
|
|
push!(var_nonbasic, i)
|
|
|
|
|
else
|
|
|
|
|
error("Unknown basis status: $bstatus")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Constraints
|
|
|
|
|
constr_index = 1
|
|
|
|
|
for (ftype, stype) in list_of_constraint_types(model)
|
|
|
|
|
for constr in all_constraints(model, ftype, stype)
|
|
|
|
|
if ftype == VariableRef
|
|
|
|
|
# nop
|
|
|
|
|
elseif ftype == AffExpr
|
|
|
|
|
bstatus = MOI.get(model, MOI.ConstraintBasisStatus(), constr)
|
|
|
|
|
if bstatus == MOI.BASIC
|
|
|
|
|
push!(constr_basic, constr_index)
|
|
|
|
|
elseif bstatus == MOI.NONBASIC
|
|
|
|
|
push!(constr_nonbasic, constr_index)
|
|
|
|
|
@timeit "Query constraints" begin
|
|
|
|
|
constr_index = 1
|
|
|
|
|
for (ftype, stype) in list_of_constraint_types(model)
|
|
|
|
|
for constr in all_constraints(model, ftype, stype)
|
|
|
|
|
if ftype == VariableRef
|
|
|
|
|
# nop
|
|
|
|
|
elseif ftype == AffExpr
|
|
|
|
|
bstatus = MOI.get(model, MOI.ConstraintBasisStatus(), constr)
|
|
|
|
|
if bstatus == MOI.BASIC
|
|
|
|
|
push!(constr_basic, constr_index)
|
|
|
|
|
elseif bstatus == MOI.NONBASIC
|
|
|
|
|
push!(constr_nonbasic, constr_index)
|
|
|
|
|
else
|
|
|
|
|
error("Unknown basis status: $bstatus")
|
|
|
|
|
end
|
|
|
|
|
constr_index += 1
|
|
|
|
|
else
|
|
|
|
|
error("Unknown basis status: $bstatus")
|
|
|
|
|
error("Unsupported constraint type: ($ftype, $stype)")
|
|
|
|
|
end
|
|
|
|
|
constr_index += 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@timeit "Build basis struct" begin
|
|
|
|
|
basis = Basis(; var_basic, var_nonbasic, constr_basic, constr_nonbasic)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return basis
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function set_basis(model::JuMP.Model, basis::Basis)
|
|
|
|
|
if isa(unsafe_backend(model), Gurobi.Optimizer)
|
|
|
|
|
# NOP
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@timeit "Initialization" begin
|
|
|
|
|
nvars = num_variables(model)
|
|
|
|
|
gurobi_model = unsafe_backend(model).inner
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@timeit "Set variable basis" begin
|
|
|
|
|
var_basis_statuses = Vector{Cint}(undef, nvars)
|
|
|
|
|
fill!(var_basis_statuses, -1) # Default to GRB_NONBASIC_LOWER
|
|
|
|
|
|
|
|
|
|
for var_idx in basis.var_basic
|
|
|
|
|
var_basis_statuses[var_idx] = 0 # GRB_BASIC
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ret = GRBsetintattrarray(gurobi_model, "VBasis", 0, nvars, var_basis_statuses)
|
|
|
|
|
if ret != 0
|
|
|
|
|
error("Failed to set variable basis statuses in Gurobi: error code $ret")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@timeit "Set constraint basis" begin
|
|
|
|
|
nconstr = num_constraints(model, AffExpr, MOI.EqualTo{Float64})
|
|
|
|
|
constr_basis_statuses = Vector{Cint}(undef, nconstr)
|
|
|
|
|
fill!(constr_basis_statuses, -1) # Default to GRB_NONBASIC
|
|
|
|
|
|
|
|
|
|
for constr_idx in basis.constr_basic
|
|
|
|
|
constr_basis_statuses[constr_idx] = 0 # GRB_BASIC
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ret = GRBsetintattrarray(gurobi_model, "CBasis", 0, nconstr, constr_basis_statuses)
|
|
|
|
|
if ret != 0
|
|
|
|
|
error("Failed to set constraint basis statuses in Gurobi: error code $ret")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return nothing
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_basis_gurobi(model::JuMP.Model)::Basis
|
|
|
|
|
@timeit "Initialization" begin
|
|
|
|
|
var_basic = Int[]
|
|
|
|
|
var_nonbasic = Int[]
|
|
|
|
|
constr_basic = Int[]
|
|
|
|
|
constr_nonbasic = Int[]
|
|
|
|
|
nvars = num_variables(model)
|
|
|
|
|
sizehint!(var_basic, nvars)
|
|
|
|
|
sizehint!(var_nonbasic, nvars)
|
|
|
|
|
gurobi_model = unsafe_backend(model).inner
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@timeit "Query variables" begin
|
|
|
|
|
var_basis_statuses = Vector{Cint}(undef, nvars)
|
|
|
|
|
ret = GRBgetintattrarray(gurobi_model, "VBasis", 0, nvars, var_basis_statuses)
|
|
|
|
|
if ret != 0
|
|
|
|
|
error("Failed to get variable basis statuses from Gurobi: error code $ret")
|
|
|
|
|
end
|
|
|
|
|
for i in 1:nvars
|
|
|
|
|
if var_basis_statuses[i] == 0 # GRB_BASIC
|
|
|
|
|
push!(var_basic, i)
|
|
|
|
|
elseif var_basis_statuses[i] == -1 # GRB_NONBASIC_LOWER
|
|
|
|
|
push!(var_nonbasic, i)
|
|
|
|
|
else
|
|
|
|
|
error("Unknown variable basis status: $(var_basis_statuses[i])")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@timeit "Query constraints" begin
|
|
|
|
|
nconstr = num_constraints(model, AffExpr, MOI.EqualTo{Float64})
|
|
|
|
|
constr_basis_statuses = Vector{Cint}(undef, nconstr)
|
|
|
|
|
ret = GRBgetintattrarray(gurobi_model, "CBasis", 0, nconstr, constr_basis_statuses)
|
|
|
|
|
if ret != 0
|
|
|
|
|
error("Failed to get constraint basis statuses from Gurobi: error code $ret")
|
|
|
|
|
end
|
|
|
|
|
for i in 1:nconstr
|
|
|
|
|
if constr_basis_statuses[i] == 0 # GRB_BASIC
|
|
|
|
|
push!(constr_basic, i)
|
|
|
|
|
elseif constr_basis_statuses[i] == -1 # GRB_NONBASIC
|
|
|
|
|
push!(constr_nonbasic, i)
|
|
|
|
|
else
|
|
|
|
|
error("Unsupported constraint type: ($ftype, $stype)")
|
|
|
|
|
error("Unknown constraint basis status: $(constr_basis_statuses[i])")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return Basis(; var_basic, var_nonbasic, constr_basic, constr_nonbasic)
|
|
|
|
|
@timeit "Build basis struct" begin
|
|
|
|
|
basis = Basis(; var_basic, var_nonbasic, constr_basic, constr_nonbasic)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return basis
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function get_x(model::JuMP.Model)
|
|
|
|
@ -150,4 +263,4 @@ function compute_tableau(
|
|
|
|
|
return Tableau(obj = tableau_obj, lhs = tableau_lhs, rhs = tableau_rhs, z = z)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
export get_basis, get_x, compute_tableau
|
|
|
|
|
export get_basis, get_basis_gurobi, set_basis, get_x, compute_tableau
|
|
|
|
|