parent
64101c495c
commit
5bc909d62f
@ -0,0 +1,35 @@
|
|||||||
|
global H5File = PyNULL()
|
||||||
|
|
||||||
|
to_str_array(values) = py"to_str_array"(values)
|
||||||
|
|
||||||
|
from_str_array(values) = py"from_str_array"(values)
|
||||||
|
|
||||||
|
function __init_io__()
|
||||||
|
copy!(H5File, pyimport("miplearn.h5").H5File)
|
||||||
|
|
||||||
|
py"""
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def to_str_array(values):
|
||||||
|
if values is None:
|
||||||
|
return None
|
||||||
|
return np.array(values, dtype="S")
|
||||||
|
|
||||||
|
def from_str_array(values):
|
||||||
|
return [v.decode() for v in values]
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
function convert(::Type{SparseMatrixCSC}, o::PyObject)
|
||||||
|
I, J, V = pyimport("scipy.sparse").find(o)
|
||||||
|
return sparse(I .+ 1, J .+ 1, V, o.shape...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function PyObject(m::SparseMatrixCSC)
|
||||||
|
pyimport("scipy.sparse").csc_matrix(
|
||||||
|
(m.nzval, m.rowval .- 1, m.colptr .- 1),
|
||||||
|
shape = size(m),
|
||||||
|
).tocoo()
|
||||||
|
end
|
||||||
|
|
||||||
|
export H5File
|
@ -0,0 +1,28 @@
|
|||||||
|
global SetCoverData = PyNULL()
|
||||||
|
global SetCoverGenerator = PyNULL()
|
||||||
|
|
||||||
|
using JuMP
|
||||||
|
using HiGHS
|
||||||
|
|
||||||
|
function __init_problems_setcover__()
|
||||||
|
copy!(SetCoverData, pyimport("miplearn.problems.setcover").SetCoverData)
|
||||||
|
copy!(SetCoverGenerator, pyimport("miplearn.problems.setcover").SetCoverGenerator)
|
||||||
|
end
|
||||||
|
|
||||||
|
function build_setcover_model(data; optimizer = HiGHS.Optimizer)
|
||||||
|
model = Model(optimizer)
|
||||||
|
set_silent(model)
|
||||||
|
n_elements, n_sets = size(data.incidence_matrix)
|
||||||
|
E = 0:n_elements-1
|
||||||
|
S = 0:n_sets-1
|
||||||
|
@variable(model, x[S], Bin)
|
||||||
|
@objective(model, Min, sum(data.costs .* x))
|
||||||
|
@constraint(
|
||||||
|
model,
|
||||||
|
eqs[e in E],
|
||||||
|
sum(data.incidence_matrix[e+1, s+1] * x[s] for s in S) >= 1
|
||||||
|
)
|
||||||
|
return JumpModel(model)
|
||||||
|
end
|
||||||
|
|
||||||
|
export SetCoverData, SetCoverGenerator, build_setcover_model
|
@ -0,0 +1,290 @@
|
|||||||
|
using JuMP
|
||||||
|
using HiGHS
|
||||||
|
|
||||||
|
global JumpModel = PyNULL()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function _add_constrs(
|
||||||
|
model::JuMP.Model,
|
||||||
|
var_names,
|
||||||
|
constrs_lhs,
|
||||||
|
constrs_sense,
|
||||||
|
constrs_rhs,
|
||||||
|
stats,
|
||||||
|
) end
|
||||||
|
|
||||||
|
function _extract_after_load(model::JuMP.Model, h5)
|
||||||
|
if JuMP.objective_sense(model) == MOI.MIN_SENSE
|
||||||
|
h5.put_scalar("static_sense", "min")
|
||||||
|
else
|
||||||
|
h5.put_scalar("static_sense", "max")
|
||||||
|
end
|
||||||
|
_extract_after_load_vars(model, h5)
|
||||||
|
_extract_after_load_constrs(model, h5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _extract_after_load_vars(model::JuMP.Model, h5)
|
||||||
|
vars = JuMP.all_variables(model)
|
||||||
|
lb = [
|
||||||
|
JuMP.is_binary(v) ? 0.0 : JuMP.has_lower_bound(v) ? JuMP.lower_bound(v) : -Inf
|
||||||
|
for v in vars
|
||||||
|
]
|
||||||
|
ub = [
|
||||||
|
JuMP.is_binary(v) ? 1.0 : JuMP.has_upper_bound(v) ? JuMP.upper_bound(v) : Inf
|
||||||
|
for v in vars
|
||||||
|
]
|
||||||
|
types = [JuMP.is_binary(v) ? "B" : JuMP.is_integer(v) ? "I" : "C" for v in vars]
|
||||||
|
obj = objective_function(model, AffExpr)
|
||||||
|
obj_coeffs = [v ∈ keys(obj.terms) ? obj.terms[v] : 0.0 for v in vars]
|
||||||
|
h5.put_array("static_var_names", to_str_array(JuMP.name.(vars)))
|
||||||
|
h5.put_array("static_var_types", to_str_array(types))
|
||||||
|
h5.put_array("static_var_lower_bounds", lb)
|
||||||
|
h5.put_array("static_var_upper_bounds", ub)
|
||||||
|
h5.put_array("static_var_obj_coeffs", obj_coeffs)
|
||||||
|
h5.put_scalar("static_obj_offset", obj.constant)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _extract_after_load_constrs(model::JuMP.Model, h5)
|
||||||
|
names = String[]
|
||||||
|
senses, rhs = String[], Float64[]
|
||||||
|
lhs_rows, lhs_cols, lhs_values = Int[], Int[], Float64[]
|
||||||
|
|
||||||
|
constr_index = 1
|
||||||
|
for (ftype, stype) in JuMP.list_of_constraint_types(model)
|
||||||
|
for constr in JuMP.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)
|
||||||
|
|
||||||
|
name = JuMP.name(constr)
|
||||||
|
length(name) > 0 || continue
|
||||||
|
push!(names, name)
|
||||||
|
|
||||||
|
# LHS, RHS and sense
|
||||||
|
if ftype == VariableRef
|
||||||
|
# nop
|
||||||
|
elseif ftype == AffExpr
|
||||||
|
if stype == MOI.EqualTo{Float64}
|
||||||
|
rhs_c = cset.value
|
||||||
|
push!(senses, "=")
|
||||||
|
elseif stype == MOI.LessThan{Float64}
|
||||||
|
rhs_c = cset.upper
|
||||||
|
push!(senses, "<")
|
||||||
|
elseif stype == MOI.GreaterThan{Float64}
|
||||||
|
rhs_c = cset.lower
|
||||||
|
push!(senses, ">")
|
||||||
|
else
|
||||||
|
error("Unsupported set: $stype")
|
||||||
|
end
|
||||||
|
push!(rhs, rhs_c)
|
||||||
|
for term in cf.terms
|
||||||
|
push!(lhs_cols, term.variable.value)
|
||||||
|
push!(lhs_rows, constr_index)
|
||||||
|
push!(lhs_values, term.coefficient)
|
||||||
|
end
|
||||||
|
constr_index += 1
|
||||||
|
else
|
||||||
|
error("Unsupported constraint type: ($ftype, $stype)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
lhs = sparse(lhs_rows, lhs_cols, lhs_values, length(rhs), JuMP.num_variables(model))
|
||||||
|
h5.put_sparse("static_constr_lhs", lhs)
|
||||||
|
h5.put_array("static_constr_rhs", rhs)
|
||||||
|
h5.put_array("static_constr_sense", to_str_array(senses))
|
||||||
|
h5.put_array("static_constr_names", to_str_array(names))
|
||||||
|
end
|
||||||
|
|
||||||
|
function _extract_after_lp(model::JuMP.Model, h5)
|
||||||
|
h5.put_scalar("lp_wallclock_time", solve_time(model))
|
||||||
|
h5.put_scalar("lp_obj_value", objective_value(model))
|
||||||
|
_extract_after_lp_vars(model, h5)
|
||||||
|
_extract_after_lp_constrs(model, h5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _extract_after_lp_vars(model::JuMP.Model, h5)
|
||||||
|
# Values and reduced costs
|
||||||
|
vars = all_variables(model)
|
||||||
|
h5.put_array("lp_var_values", JuMP.value.(vars))
|
||||||
|
h5.put_array("lp_var_reduced_costs", reduced_cost.(vars))
|
||||||
|
|
||||||
|
# Basis status
|
||||||
|
basis_status = []
|
||||||
|
for var in vars
|
||||||
|
bstatus = MOI.get(model, MOI.VariableBasisStatus(), var)
|
||||||
|
if bstatus == MOI.BASIC
|
||||||
|
bstatus_v = "B"
|
||||||
|
elseif bstatus == MOI.NONBASIC_AT_LOWER
|
||||||
|
bstatus_v = "L"
|
||||||
|
elseif bstatus == MOI.NONBASIC_AT_UPPER
|
||||||
|
bstatus_v = "U"
|
||||||
|
else
|
||||||
|
error("Unknown basis status: $(bstatus)")
|
||||||
|
end
|
||||||
|
push!(basis_status, bstatus_v)
|
||||||
|
end
|
||||||
|
h5.put_array("lp_var_basis_status", to_str_array(basis_status))
|
||||||
|
|
||||||
|
# Sensitivity analysis
|
||||||
|
obj_coeffs = h5.get_array("static_var_obj_coeffs")
|
||||||
|
sensitivity_report = lp_sensitivity_report(model)
|
||||||
|
sa_obj_down, sa_obj_up = Float64[], Float64[]
|
||||||
|
sa_lb_down, sa_lb_up = Float64[], Float64[]
|
||||||
|
sa_ub_down, sa_ub_up = Float64[], Float64[]
|
||||||
|
for (i, v) in enumerate(vars)
|
||||||
|
# Objective function
|
||||||
|
(delta_down, delta_up) = sensitivity_report[v]
|
||||||
|
push!(sa_obj_down, delta_down + obj_coeffs[i])
|
||||||
|
push!(sa_obj_up, delta_up + obj_coeffs[i])
|
||||||
|
|
||||||
|
# Lower bound
|
||||||
|
if has_lower_bound(v)
|
||||||
|
constr = LowerBoundRef(v)
|
||||||
|
(delta_down, delta_up) = sensitivity_report[constr]
|
||||||
|
push!(sa_lb_down, lower_bound(v) + delta_down)
|
||||||
|
push!(sa_lb_up, lower_bound(v) + delta_up)
|
||||||
|
else
|
||||||
|
push!(sa_lb_down, -Inf)
|
||||||
|
push!(sa_lb_up, -Inf)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Upper bound
|
||||||
|
if has_upper_bound(v)
|
||||||
|
constr = JuMP.UpperBoundRef(v)
|
||||||
|
(delta_down, delta_up) = sensitivity_report[constr]
|
||||||
|
push!(sa_ub_down, upper_bound(v) + delta_down)
|
||||||
|
push!(sa_ub_up, upper_bound(v) + delta_up)
|
||||||
|
else
|
||||||
|
push!(sa_ub_down, Inf)
|
||||||
|
push!(sa_ub_up, Inf)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
h5.put_array("lp_var_sa_obj_up", sa_obj_up)
|
||||||
|
h5.put_array("lp_var_sa_obj_down", sa_obj_down)
|
||||||
|
h5.put_array("lp_var_sa_ub_up", sa_ub_up)
|
||||||
|
h5.put_array("lp_var_sa_ub_down", sa_ub_down)
|
||||||
|
h5.put_array("lp_var_sa_lb_up", sa_lb_up)
|
||||||
|
h5.put_array("lp_var_sa_lb_down", sa_lb_down)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function _extract_after_lp_constrs(model::JuMP.Model, h5)
|
||||||
|
# Slacks
|
||||||
|
lhs = h5.get_sparse("static_constr_lhs")
|
||||||
|
rhs = h5.get_array("static_constr_rhs")
|
||||||
|
x = h5.get_array("lp_var_values")
|
||||||
|
slacks = abs.(lhs * x - rhs)
|
||||||
|
h5.put_array("lp_constr_slacks", slacks)
|
||||||
|
|
||||||
|
sa_rhs_up, sa_rhs_down = Float64[], Float64[]
|
||||||
|
duals = Float64[]
|
||||||
|
basis_status = []
|
||||||
|
constr_idx = 1
|
||||||
|
sensitivity_report = lp_sensitivity_report(model)
|
||||||
|
for (ftype, stype) in JuMP.list_of_constraint_types(model)
|
||||||
|
for constr in JuMP.all_constraints(model, ftype, stype)
|
||||||
|
length(JuMP.name(constr)) > 0 || continue
|
||||||
|
|
||||||
|
# Duals
|
||||||
|
push!(duals, JuMP.dual(constr))
|
||||||
|
|
||||||
|
# Basis status
|
||||||
|
b = MOI.get(model, MOI.ConstraintBasisStatus(), constr)
|
||||||
|
if b == MOI.NONBASIC
|
||||||
|
push!(basis_status, "N")
|
||||||
|
elseif b == MOI.BASIC
|
||||||
|
push!(basis_status, "B")
|
||||||
|
else
|
||||||
|
error("Unknown basis status: $b")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sensitivity analysis
|
||||||
|
(delta_down, delta_up) = sensitivity_report[constr]
|
||||||
|
push!(sa_rhs_down, rhs[constr_idx] + delta_down)
|
||||||
|
push!(sa_rhs_up, rhs[constr_idx] + delta_up)
|
||||||
|
|
||||||
|
constr_idx += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
h5.put_array("lp_constr_dual_values", duals)
|
||||||
|
h5.put_array("lp_constr_basis_status", to_str_array(basis_status))
|
||||||
|
h5.put_array("lp_constr_sa_rhs_up", sa_rhs_up)
|
||||||
|
h5.put_array("lp_constr_sa_rhs_down", sa_rhs_down)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _extract_after_mip(model::JuMP.Model, h5)
|
||||||
|
h5.put_scalar("mip_obj_value", objective_value(model))
|
||||||
|
h5.put_scalar("mip_obj_bound", objective_bound(model))
|
||||||
|
h5.put_scalar("mip_wallclock_time", solve_time(model))
|
||||||
|
h5.put_scalar("mip_gap", relative_gap(model))
|
||||||
|
|
||||||
|
# Values
|
||||||
|
vars = all_variables(model)
|
||||||
|
x = JuMP.value.(vars)
|
||||||
|
h5.put_array("mip_var_values", x)
|
||||||
|
|
||||||
|
# Slacks
|
||||||
|
lhs = h5.get_sparse("static_constr_lhs")
|
||||||
|
rhs = h5.get_array("static_constr_rhs")
|
||||||
|
slacks = abs.(lhs * x - rhs)
|
||||||
|
h5.put_array("mip_constr_slacks", slacks)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _fix_variables(model::JuMP.Model, var_names, var_values, stats) end
|
||||||
|
|
||||||
|
function _optimize(model::JuMP.Model)
|
||||||
|
optimize!(model)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _relax(model::JuMP.Model)
|
||||||
|
relaxed, _ = copy_model(model)
|
||||||
|
relax_integrality(relaxed)
|
||||||
|
# FIXME: Remove hardcoded optimizer
|
||||||
|
set_optimizer(relaxed, HiGHS.Optimizer)
|
||||||
|
set_silent(relaxed)
|
||||||
|
return relaxed
|
||||||
|
end
|
||||||
|
|
||||||
|
function _set_warm_starts(model::JuMP.Model, var_names, var_values, stats) end
|
||||||
|
|
||||||
|
function _write(model::JuMP.Model, filename) end
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function __init_solvers_jump__()
|
||||||
|
@pydef mutable struct Class
|
||||||
|
|
||||||
|
function __init__(self, inner)
|
||||||
|
self.inner = inner
|
||||||
|
end
|
||||||
|
|
||||||
|
add_constrs(self, var_names, constrs_lhs, constrs_sense, constrs_rhs, stats) =
|
||||||
|
_add_constrs(
|
||||||
|
self.inner,
|
||||||
|
var_names,
|
||||||
|
constrs_lhs,
|
||||||
|
constrs_sense,
|
||||||
|
constrs_rhs,
|
||||||
|
stats,
|
||||||
|
)
|
||||||
|
|
||||||
|
extract_after_load(self, h5) = _extract_after_load(self.inner, h5)
|
||||||
|
|
||||||
|
extract_after_lp(self, h5) = _extract_after_lp(self.inner, h5)
|
||||||
|
|
||||||
|
extract_after_mip(self, h5) = _extract_after_mip(self.inner, h5)
|
||||||
|
|
||||||
|
fix_variables(self, var_names, var_values, stats) =
|
||||||
|
_fix_variables(self.inner, var_names, var_values, stats)
|
||||||
|
|
||||||
|
optimize(self) = _optimize(self.inner)
|
||||||
|
|
||||||
|
relax(self) = Class(_relax(self.inner))
|
||||||
|
|
||||||
|
set_warm_starts(self, var_names, var_values, stats) =
|
||||||
|
_set_warm_starts(self.inner, var_names, var_values, stats)
|
||||||
|
|
||||||
|
write(self, filename) = _write(self.inner, filename)
|
||||||
|
end
|
||||||
|
copy!(JumpModel, Class)
|
||||||
|
end
|
@ -1,6 +1,14 @@
|
|||||||
|
name = "MIPLearnT"
|
||||||
|
uuid = "92db8938-9c6a-4af6-8bcc-af424cd0e2d5"
|
||||||
|
authors = ["Alinson S. Xavier <git@axavier.org>"]
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
|
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
|
||||||
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
|
||||||
|
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
|
||||||
|
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||||
MIPLearn = "2b1277c3-b477-4c49-a15e-7ba350325c68"
|
MIPLearn = "2b1277c3-b477-4c49-a15e-7ba350325c68"
|
||||||
|
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
|
||||||
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
|
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
|
||||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
|
Binary file not shown.
@ -1,13 +0,0 @@
|
|||||||
# 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 Revise
|
|
||||||
using Test
|
|
||||||
using MIPLearn
|
|
||||||
|
|
||||||
includet("Cuts/BlackBox/test_cplex.jl")
|
|
||||||
|
|
||||||
function runtests()
|
|
||||||
test_cuts_blackbox_cplex()
|
|
||||||
end
|
|
@ -0,0 +1,34 @@
|
|||||||
|
module MIPLearnT
|
||||||
|
|
||||||
|
using Test
|
||||||
|
using Logging
|
||||||
|
using JuliaFormatter
|
||||||
|
using HiGHS
|
||||||
|
|
||||||
|
BASEDIR = dirname(@__FILE__)
|
||||||
|
FIXTURES = "$BASEDIR/../fixtures"
|
||||||
|
|
||||||
|
include("Cuts/BlackBox/test_cplex.jl")
|
||||||
|
include("problems/test_setcover.jl")
|
||||||
|
include("test_h5.jl")
|
||||||
|
include("solvers/test_jump.jl")
|
||||||
|
|
||||||
|
function runtests()
|
||||||
|
@testset "MIPLearn" begin
|
||||||
|
test_cuts_blackbox_cplex()
|
||||||
|
test_h5()
|
||||||
|
test_problems_setcover()
|
||||||
|
test_solvers_jump()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function format()
|
||||||
|
JuliaFormatter.format(BASEDIR, verbose = true)
|
||||||
|
JuliaFormatter.format("$BASEDIR/../../src", verbose = true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
export runtests, format
|
||||||
|
|
||||||
|
end # module MIPLearnT
|
@ -0,0 +1,56 @@
|
|||||||
|
using PyCall
|
||||||
|
|
||||||
|
function test_problems_setcover()
|
||||||
|
test_problems_setcover_generator()
|
||||||
|
test_problems_setcover_model()
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_problems_setcover_generator()
|
||||||
|
np = pyimport("numpy")
|
||||||
|
scipy_stats = pyimport("scipy.stats")
|
||||||
|
randint = scipy_stats.randint
|
||||||
|
uniform = scipy_stats.uniform
|
||||||
|
|
||||||
|
np.random.seed(42)
|
||||||
|
gen = SetCoverGenerator(
|
||||||
|
n_elements = randint(low = 3, high = 4),
|
||||||
|
n_sets = randint(low = 5, high = 6),
|
||||||
|
costs = uniform(loc = 0.0, scale = 100.0),
|
||||||
|
costs_jitter = uniform(loc = 0.95, scale = 0.10),
|
||||||
|
density = uniform(loc = 0.5, scale = 0),
|
||||||
|
K = uniform(loc = 25, scale = 0),
|
||||||
|
fix_sets = false,
|
||||||
|
)
|
||||||
|
data = gen.generate(2)
|
||||||
|
@test data[1].costs == [136.75, 86.17, 25.71, 27.31, 102.48]
|
||||||
|
@test data[1].incidence_matrix == [
|
||||||
|
1 0 1 0 1
|
||||||
|
1 1 0 0 0
|
||||||
|
1 0 0 1 1
|
||||||
|
]
|
||||||
|
@test data[2].costs == [63.54, 76.6, 48.09, 74.1, 93.33]
|
||||||
|
@test data[2].incidence_matrix == [
|
||||||
|
1 1 0 1 1
|
||||||
|
0 1 0 1 0
|
||||||
|
0 1 1 0 0
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_problems_setcover_model()
|
||||||
|
data = SetCoverData(
|
||||||
|
costs = [5, 10, 12, 6, 8],
|
||||||
|
incidence_matrix = [
|
||||||
|
1 0 0 1 0
|
||||||
|
1 1 0 0 0
|
||||||
|
0 0 1 1 1
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
h5 = H5File(tempname(), "w")
|
||||||
|
model = build_setcover_model(data)
|
||||||
|
model.extract_after_load(h5)
|
||||||
|
model.optimize()
|
||||||
|
model.extract_after_mip(h5)
|
||||||
|
@test h5.get_scalar("mip_obj_value") == 11.0
|
||||||
|
h5.close()
|
||||||
|
end
|
@ -0,0 +1,98 @@
|
|||||||
|
function build_model()
|
||||||
|
data = SetCoverData(
|
||||||
|
costs = [5, 10, 12, 6, 8],
|
||||||
|
incidence_matrix = [
|
||||||
|
1 0 0 1 0
|
||||||
|
1 1 0 0 0
|
||||||
|
0 0 1 1 1
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return build_setcover_model(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_solvers_jump()
|
||||||
|
test_solvers_jump_extract()
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_solvers_jump_extract()
|
||||||
|
h5 = H5File(tempname(), "w")
|
||||||
|
|
||||||
|
function test_scalar(key, expected)
|
||||||
|
actual = h5.get_scalar(key)
|
||||||
|
@test actual !== nothing
|
||||||
|
@test actual == expected
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_sparse(key, expected)
|
||||||
|
actual = h5.get_sparse(key)
|
||||||
|
@test actual !== nothing
|
||||||
|
@test all(actual == expected)
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_str_array(key, expected)
|
||||||
|
actual = MIPLearn.from_str_array(h5.get_array(key))
|
||||||
|
@debug actual, expected
|
||||||
|
@test actual !== nothing
|
||||||
|
@test all(actual .== expected)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function test_array(key, expected)
|
||||||
|
actual = h5.get_array(key)
|
||||||
|
@debug actual, expected
|
||||||
|
@test actual !== nothing
|
||||||
|
@test all(actual .≈ expected)
|
||||||
|
end
|
||||||
|
|
||||||
|
model = build_model()
|
||||||
|
model.extract_after_load(h5)
|
||||||
|
test_sparse(
|
||||||
|
"static_constr_lhs",
|
||||||
|
[
|
||||||
|
1 0 0 1 0
|
||||||
|
1 1 0 0 0
|
||||||
|
0 0 1 1 1
|
||||||
|
],
|
||||||
|
)
|
||||||
|
test_str_array("static_constr_names", ["eqs[0]", "eqs[1]", "eqs[2]"])
|
||||||
|
test_array("static_constr_rhs", [1, 1, 1])
|
||||||
|
test_str_array("static_constr_sense", [">", ">", ">"])
|
||||||
|
test_scalar("static_obj_offset", 0)
|
||||||
|
test_scalar("static_sense", "min")
|
||||||
|
test_array("static_var_lower_bounds", [0, 0, 0, 0, 0])
|
||||||
|
test_str_array("static_var_names", ["x[0]", "x[1]", "x[2]", "x[3]", "x[4]"])
|
||||||
|
test_array("static_var_obj_coeffs", [5, 10, 12, 6, 8])
|
||||||
|
test_str_array("static_var_types", ["B", "B", "B", "B", "B"])
|
||||||
|
test_array("static_var_upper_bounds", [1, 1, 1, 1, 1])
|
||||||
|
|
||||||
|
relaxed = model.relax()
|
||||||
|
relaxed.optimize()
|
||||||
|
relaxed.extract_after_lp(h5)
|
||||||
|
test_array("lp_constr_dual_values", [0, 10, 6])
|
||||||
|
test_array("lp_constr_slacks", [1, 0, 0])
|
||||||
|
test_scalar("lp_obj_value", 11)
|
||||||
|
test_array("lp_var_reduced_costs", [-5, 0, 6, 0, 2])
|
||||||
|
test_array("lp_var_values", [1, 0, 0, 1, 0])
|
||||||
|
test_str_array("lp_var_basis_status", ["U", "B", "L", "B", "L"])
|
||||||
|
test_str_array("lp_constr_basis_status", ["B","N","N"])
|
||||||
|
test_array("lp_constr_sa_rhs_up", [2, 2, 1])
|
||||||
|
test_array("lp_constr_sa_rhs_down", [-Inf, 1, 0])
|
||||||
|
test_array("lp_var_sa_obj_up", [10, Inf, Inf, 8, Inf])
|
||||||
|
test_array("lp_var_sa_obj_down", [-Inf, 5, 6, 0, 6])
|
||||||
|
test_array("lp_var_sa_ub_up", [1, Inf, Inf, Inf, Inf])
|
||||||
|
test_array("lp_var_sa_ub_down", [0, 0, 0, 1, 0])
|
||||||
|
test_array("lp_var_sa_lb_up", [1, 0, 1, 1, 1])
|
||||||
|
test_array("lp_var_sa_lb_down", [-Inf, -Inf, 0, -Inf, 0])
|
||||||
|
lp_wallclock_time = h5.get_scalar("lp_wallclock_time")
|
||||||
|
@test lp_wallclock_time >= 0
|
||||||
|
|
||||||
|
model.optimize()
|
||||||
|
model.extract_after_mip(h5)
|
||||||
|
test_array("mip_constr_slacks", [1, 0, 0])
|
||||||
|
test_array("mip_var_values", [1.0, 0.0, 0.0, 1.0, 0.0])
|
||||||
|
test_scalar("mip_gap", 0)
|
||||||
|
test_scalar("mip_obj_bound", 11.0)
|
||||||
|
test_scalar("mip_obj_value", 11.0)
|
||||||
|
mip_wallclock_time = h5.get_scalar("mip_wallclock_time")
|
||||||
|
@test mip_wallclock_time >= 0
|
||||||
|
end
|
@ -0,0 +1,37 @@
|
|||||||
|
using MIPLearn
|
||||||
|
|
||||||
|
function test_h5()
|
||||||
|
h5 = H5File(tempname(), "w")
|
||||||
|
_test_roundtrip_scalar(h5, "A")
|
||||||
|
_test_roundtrip_scalar(h5, true)
|
||||||
|
_test_roundtrip_scalar(h5, 1)
|
||||||
|
_test_roundtrip_scalar(h5, 1.0)
|
||||||
|
@test h5.get_scalar("unknown-key") === nothing
|
||||||
|
_test_roundtrip_array(h5, [true, false])
|
||||||
|
_test_roundtrip_array(h5, [1, 2, 3])
|
||||||
|
_test_roundtrip_array(h5, [1.0, 2.0, 3.0])
|
||||||
|
_test_roundtrip_str_array(h5, ["A", "BB", "CCC"])
|
||||||
|
@test h5.get_array("unknown-key") === nothing
|
||||||
|
h5.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function _test_roundtrip_scalar(h5, original)
|
||||||
|
h5.put_scalar("key", original)
|
||||||
|
recovered = h5.get_scalar("key")
|
||||||
|
@test recovered !== nothing
|
||||||
|
@test original == recovered
|
||||||
|
end
|
||||||
|
|
||||||
|
function _test_roundtrip_array(h5, original)
|
||||||
|
h5.put_array("key", original)
|
||||||
|
recovered = h5.get_array("key")
|
||||||
|
@test recovered !== nothing
|
||||||
|
@test all(original .== recovered)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _test_roundtrip_str_array(h5, original)
|
||||||
|
h5.put_array("key", MIPLearn.to_str_array(original))
|
||||||
|
recovered = MIPLearn.from_str_array(h5.get_array("key"))
|
||||||
|
@test recovered !== nothing
|
||||||
|
@test all(original .== recovered)
|
||||||
|
end
|
Loading…
Reference in new issue