You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MIPLearn.jl/src/Cuts/tableau/moi.jl

181 lines
6.0 KiB

# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2023, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
using JuMP
function ProblemData(model::Model)::ProblemData
vars = all_variables(model)
# Objective function
obj = objective_function(model)
obj_offset = obj.constant
obj_sense = objective_sense(model)
obj = [v keys(obj.terms) ? obj.terms[v] : 0.0 for v in vars]
# Variable types, lower bounds and upper bounds
var_lb = [is_binary(v) ? 0.0 : has_lower_bound(v) ? lower_bound(v) : -Inf for v in vars]
var_ub = [is_binary(v) ? 1.0 : has_upper_bound(v) ? upper_bound(v) : Inf for v in vars]
var_types = [is_binary(v) || is_integer(v) ? 'I' : 'C' for v in vars]
var_names = [name(v) for v in vars]
# Constraints
constr_lb = Float64[]
constr_ub = Float64[]
constr_lhs_rows = Int[]
constr_lhs_cols = Int[]
constr_lhs_values = Float64[]
constr_index = 1
for (ftype, stype) in list_of_constraint_types(model)
for constr in all_constraints(model, ftype, stype)
cset = MOI.get(constr.model.moi_backend, MOI.ConstraintSet(), constr.index)
cf = MOI.get(constr.model.moi_backend, MOI.ConstraintFunction(), constr.index)
if ftype == VariableRef
var_idx = cf.value
if stype == MOI.Integer || stype == MOI.ZeroOne
# nop
elseif stype == MOI.EqualTo{Float64}
var_lb[var_idx] = max(var_lb[var_idx], cset.value)
var_ub[var_idx] = min(var_ub[var_idx], cset.value)
elseif stype == MOI.LessThan{Float64}
var_ub[var_idx] = min(var_ub[var_idx], cset.upper)
elseif stype == MOI.GreaterThan{Float64}
var_lb[var_idx] = max(var_lb[var_idx], cset.lower)
elseif stype == MOI.Interval{Float64}
var_lb[var_idx] = max(var_lb[var_idx], cset.lower)
var_ub[var_idx] = min(var_ub[var_idx], cset.upper)
else
error("Unsupported set: $stype")
end
elseif ftype == AffExpr
if stype == MOI.EqualTo{Float64}
push!(constr_lb, cset.value)
push!(constr_ub, cset.value)
elseif stype == MOI.LessThan{Float64}
push!(constr_lb, -Inf)
push!(constr_ub, cset.upper)
elseif stype == MOI.GreaterThan{Float64}
push!(constr_lb, cset.lower)
push!(constr_ub, Inf)
elseif stype == MOI.Interval{Float64}
push!(constr_lb, cset.lower)
push!(constr_ub, cset.upper)
else
error("Unsupported set: $stype")
end
for term in cf.terms
push!(constr_lhs_cols, term.variable.value)
push!(constr_lhs_rows, constr_index)
push!(constr_lhs_values, term.coefficient)
end
constr_index += 1
else
error("Unsupported constraint type: ($ftype, $stype)")
end
end
end
n = length(vars)
m = constr_index - 1
constr_lhs = sparse(constr_lhs_rows, constr_lhs_cols, constr_lhs_values, m, n)
@assert length(obj) == n
@assert length(var_lb) == n
@assert length(var_ub) == n
@assert length(var_types) == n
@assert length(var_names) == n
@assert length(constr_lb) == m
@assert length(constr_ub) == m
return ProblemData(
obj,
obj_offset,
obj_sense,
constr_lb,
constr_ub,
constr_lhs,
var_lb,
var_ub,
var_types,
var_names,
)
end
function to_model(data::ProblemData, tol = 1e-6)::Model
model = Model()
# Variables
obj_expr = AffExpr(data.obj_offset)
nvars = length(data.obj)
@variable(model, x[1:nvars])
for i = 1:nvars
set_name(x[i], data.var_names[i])
if data.var_types[i] == 'B'
set_binary(x[i])
elseif data.var_types[i] == 'I'
set_integer(x[i])
end
if isfinite(data.var_lb[i])
set_lower_bound(x[i], data.var_lb[i])
end
if isfinite(data.var_ub[i])
set_upper_bound(x[i], data.var_ub[i])
end
add_to_expression!(obj_expr, x[i], data.obj[i])
end
@objective(model, data.obj_sense, obj_expr)
# Constraints
lhs = data.constr_lhs * x
for (j, lhs_expr) in enumerate(lhs)
lb = data.constr_lb[j]
ub = data.constr_ub[j]
if abs(lb - ub) < tol
@constraint(model, lb == lhs_expr)
elseif isfinite(lb) && !isfinite(ub)
@constraint(model, lb <= lhs_expr)
elseif !isfinite(lb) && isfinite(ub)
@constraint(model, lhs_expr <= ub)
else
@constraint(model, lb <= lhs_expr <= ub)
end
end
return model
end
function add_constraint_set(model::JuMP.Model, cs::ConstraintSet)
constrs = build_constraints(model, cs)
for c in constrs
add_constraint(model, c)
end
return constrs
end
function set_warm_start(model::JuMP.Model, x::Vector{Float64})
vars = all_variables(model)
for (i, xi) in enumerate(x)
set_start_value(vars[i], xi)
end
end
function build_constraints(model::JuMP.Model, cs::ConstraintSet)
vars = all_variables(model)
nrows, _ = size(cs.lhs)
constrs = []
for i = 1:nrows
c = nothing
if isinf(cs.ub[i])
c = @build_constraint(cs.lb[i] <= dot(cs.lhs[i, :], vars))
elseif isinf(cs.lb[i])
c = @build_constraint(dot(cs.lhs[i, :], vars) <= cs.ub[i])
else
c = @build_constraint(cs.lb[i] <= dot(cs.lhs[i, :], vars) <= cs.ub[i])
end
push!(constrs, c)
end
return constrs
end
export to_model, ProblemData, add_constraint_set, set_warm_start, build_constraints