mirror of
https://github.com/ANL-CEEESA/MIPLearn.jl.git
synced 2025-12-06 08:28:52 -06:00
Accelerate KnnDualGmiComponent_before_mip; enable precompilation
This commit is contained in:
@@ -15,10 +15,12 @@ KLU = "ef3ab10e-7fda-4108-b977-705223b18434"
|
|||||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||||
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
||||||
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
||||||
|
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
|
||||||
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||||
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
|
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
|
||||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||||
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
|
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
|
||||||
|
SCIP = "82193955-e24f-5292-bf16-6f2c5261a85f"
|
||||||
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
||||||
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
|
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
|
||||||
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
|
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
|
||||||
@@ -30,7 +32,6 @@ HDF5 = "0.16"
|
|||||||
HiGHS = "1"
|
HiGHS = "1"
|
||||||
JLD2 = "0.4"
|
JLD2 = "0.4"
|
||||||
JSON = "0.21"
|
JSON = "0.21"
|
||||||
julia = "1"
|
|
||||||
JuMP = "1"
|
JuMP = "1"
|
||||||
KLU = "0.4"
|
KLU = "0.4"
|
||||||
MathOptInterface = "1"
|
MathOptInterface = "1"
|
||||||
@@ -39,3 +40,4 @@ PyCall = "1"
|
|||||||
Requires = "1"
|
Requires = "1"
|
||||||
Statistics = "1"
|
Statistics = "1"
|
||||||
TimerOutputs = "0.5"
|
TimerOutputs = "0.5"
|
||||||
|
julia = "1"
|
||||||
|
|||||||
@@ -442,34 +442,69 @@ function _dualgmi_features(h5_filename, extractor)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function _dualgmi_generate(train_h5, model)
|
function _dualgmi_generate(train_h5, model)
|
||||||
|
@timeit "Read problem data" begin
|
||||||
data = ProblemData(model)
|
data = ProblemData(model)
|
||||||
|
end
|
||||||
|
@timeit "Convert to standard form" begin
|
||||||
data_s, transforms = convert_to_standard_form(data)
|
data_s, transforms = convert_to_standard_form(data)
|
||||||
all_cuts = nothing
|
|
||||||
visited = Set()
|
|
||||||
for h5_filename in train_h5
|
|
||||||
h5 = H5File(h5_filename)
|
|
||||||
cut_basis_vars = h5.get_array("cuts_basis_vars")
|
|
||||||
cut_basis_sizes = h5.get_array("cuts_basis_sizes")
|
|
||||||
cut_rows = h5.get_array("cuts_rows")
|
|
||||||
h5.close()
|
|
||||||
current_basis = nothing
|
|
||||||
for (r, row) in enumerate(cut_rows)
|
|
||||||
if r == 1 || cut_basis_vars[r, :] != cut_basis_vars[r-1, :]
|
|
||||||
vbb, vnn, cbb, cnn = cut_basis_sizes[r, :]
|
|
||||||
current_basis = Basis(;
|
|
||||||
var_basic = cut_basis_vars[r, 1:vbb],
|
|
||||||
var_nonbasic = cut_basis_vars[r, vbb+1:vbb+vnn],
|
|
||||||
constr_basic = cut_basis_vars[r, vbb+vnn+1:vbb+vnn+cbb],
|
|
||||||
constr_nonbasic = cut_basis_vars[r, vbb+vnn+cbb+1:vbb+vnn+cbb+cnn],
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Detect and skip duplicated cuts
|
@timeit "Collect cuts from H5 files" begin
|
||||||
cut_id = (row, cut_basis_vars[r, :])
|
cut_basis_vars = nothing
|
||||||
cut_id ∉ visited || continue
|
cut_basis_sizes = nothing
|
||||||
push!(visited, cut_id)
|
cut_rows = nothing
|
||||||
|
for h5_filename in train_h5
|
||||||
|
h5 = H5File(h5_filename)
|
||||||
|
cut_basis_vars_sample = h5.get_array("cuts_basis_vars")
|
||||||
|
cut_basis_sizes_sample = h5.get_array("cuts_basis_sizes")
|
||||||
|
cut_rows_sample = h5.get_array("cuts_rows")
|
||||||
|
if cut_basis_vars === nothing
|
||||||
|
cut_basis_vars = cut_basis_vars_sample
|
||||||
|
cut_basis_sizes = cut_basis_sizes_sample
|
||||||
|
cut_rows = cut_rows_sample
|
||||||
|
else
|
||||||
|
cut_basis_vars = [cut_basis_vars; cut_basis_vars_sample]
|
||||||
|
cut_basis_sizes = [cut_basis_sizes; cut_basis_sizes_sample]
|
||||||
|
cut_rows = [cut_rows; cut_rows_sample]
|
||||||
|
end
|
||||||
|
h5.close()
|
||||||
|
end
|
||||||
|
ncuts, nvars = size(cut_basis_vars)
|
||||||
|
end
|
||||||
|
|
||||||
tableau = compute_tableau(data_s, current_basis, rows = [row])
|
@timeit "Group cuts by tableau basis" begin
|
||||||
|
vars_to_unique_basis_offset = Dict()
|
||||||
|
unique_basis_vars = Matrix{Int}(undef, 0, nvars)
|
||||||
|
unique_basis_sizes = Matrix{Int}(undef, 0, 4)
|
||||||
|
unique_basis_rows = Dict{Int,Set{Int}}()
|
||||||
|
for i in 1:ncuts
|
||||||
|
vars = cut_basis_vars[i, :]
|
||||||
|
sizes = cut_basis_sizes[i, :]
|
||||||
|
row = cut_rows[i]
|
||||||
|
if vars ∉ keys(vars_to_unique_basis_offset)
|
||||||
|
offset = size(unique_basis_vars)[1] + 1
|
||||||
|
vars_to_unique_basis_offset[vars] = offset
|
||||||
|
unique_basis_vars = [unique_basis_vars; vars']
|
||||||
|
unique_basis_sizes = [unique_basis_sizes; sizes']
|
||||||
|
unique_basis_rows[offset] = Set()
|
||||||
|
end
|
||||||
|
offset = vars_to_unique_basis_offset[vars]
|
||||||
|
push!(unique_basis_rows[offset], row)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@timeit "Compute tableaus and cuts" begin
|
||||||
|
all_cuts = nothing
|
||||||
|
for (offset, rows) in unique_basis_rows
|
||||||
|
vbb, vnn, cbb, cnn = unique_basis_sizes[offset, :]
|
||||||
|
current_basis = Basis(;
|
||||||
|
var_basic = unique_basis_vars[offset, 1:vbb],
|
||||||
|
var_nonbasic = unique_basis_vars[offset, vbb+1:vbb+vnn],
|
||||||
|
constr_basic = unique_basis_vars[offset, vbb+vnn+1:vbb+vnn+cbb],
|
||||||
|
constr_nonbasic = unique_basis_vars[offset, vbb+vnn+cbb+1:vbb+vnn+cbb+cnn],
|
||||||
|
)
|
||||||
|
|
||||||
|
tableau = compute_tableau(data_s, current_basis; rows=collect(rows))
|
||||||
cuts_s = compute_gmi(data_s, tableau)
|
cuts_s = compute_gmi(data_s, tableau)
|
||||||
cuts = backwards(transforms, cuts_s)
|
cuts = backwards(transforms, cuts_s)
|
||||||
|
|
||||||
@@ -509,8 +544,14 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function KnnDualGmiComponent_before_mip(data::_KnnDualGmiData, test_h5, model, _)
|
function KnnDualGmiComponent_before_mip(data::_KnnDualGmiData, test_h5, model, _)
|
||||||
|
reset_timer!()
|
||||||
|
|
||||||
|
@timeit "Extract features" begin
|
||||||
x = _dualgmi_features(test_h5, data.extractor)
|
x = _dualgmi_features(test_h5, data.extractor)
|
||||||
x = reshape(x, 1, length(x))
|
x = reshape(x, 1, length(x))
|
||||||
|
end
|
||||||
|
|
||||||
|
@timeit "Find neighbors" begin
|
||||||
neigh_dist, neigh_ind = data.model.kneighbors(x, return_distance = true)
|
neigh_dist, neigh_ind = data.model.kneighbors(x, return_distance = true)
|
||||||
neigh_ind = neigh_ind .+ 1
|
neigh_ind = neigh_ind .+ 1
|
||||||
N = length(neigh_dist)
|
N = length(neigh_dist)
|
||||||
@@ -533,14 +574,21 @@ function KnnDualGmiComponent_before_mip(data::_KnnDualGmiData, test_h5, model, _
|
|||||||
dist = neigh_dist[i]
|
dist = neigh_dist[i]
|
||||||
@info " $(h5_filename) dist=$(dist)"
|
@info " $(h5_filename) dist=$(dist)"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@info "Dual GMI: Generating cuts..."
|
@info "Dual GMI: Generating cuts..."
|
||||||
|
@timeit "Generate cuts" begin
|
||||||
time_generate = @elapsed begin
|
time_generate = @elapsed begin
|
||||||
cuts = _dualgmi_generate(data.train_h5[neigh_ind], model)
|
cuts = _dualgmi_generate(data.train_h5[neigh_ind], model)
|
||||||
end
|
end
|
||||||
@info "Dual GMI: Generated $(length(cuts.lb)) unique cuts in $(time_generate) seconds"
|
@info "Dual GMI: Generated $(length(cuts.lb)) unique cuts in $(time_generate) seconds"
|
||||||
|
end
|
||||||
|
|
||||||
|
@timeit "Set callback" begin
|
||||||
_dualgmi_set_callback(model, cuts)
|
_dualgmi_set_callback(model, cuts)
|
||||||
|
end
|
||||||
|
|
||||||
|
print_timer()
|
||||||
|
|
||||||
stats = Dict()
|
stats = Dict()
|
||||||
stats["KnnDualGmi: k"] = data.k
|
stats["KnnDualGmi: k"] = data.k
|
||||||
@@ -567,10 +615,11 @@ function __init_gmi_dual__()
|
|||||||
KnnDualGmiComponent_fit(self.data, train_h5)
|
KnnDualGmiComponent_fit(self.data, train_h5)
|
||||||
end
|
end
|
||||||
function before_mip(self, test_h5, model, stats)
|
function before_mip(self, test_h5, model, stats)
|
||||||
return KnnDualGmiComponent_before_mip(self.data, test_h5, model.inner, stats)
|
return @time KnnDualGmiComponent_before_mip(self.data, test_h5, model.inner, stats)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
copy!(KnnDualGmiComponent, Class2)
|
copy!(KnnDualGmiComponent, Class2)
|
||||||
end
|
end
|
||||||
|
|
||||||
export collect_gmi_dual, expert_gmi_dual, ExpertDualGmiComponent, KnnDualGmiComponent
|
export collect_gmi_dual, expert_gmi_dual, ExpertDualGmiComponent, KnnDualGmiComponent
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ module MIPLearn
|
|||||||
|
|
||||||
using PyCall
|
using PyCall
|
||||||
using SparseArrays
|
using SparseArrays
|
||||||
|
using PrecompileTools: @setup_workload, @compile_workload
|
||||||
|
|
||||||
|
|
||||||
include("collectors.jl")
|
include("collectors.jl")
|
||||||
include("components.jl")
|
include("components.jl")
|
||||||
@@ -32,4 +34,51 @@ end
|
|||||||
include("BB/BB.jl")
|
include("BB/BB.jl")
|
||||||
include("Cuts/Cuts.jl")
|
include("Cuts/Cuts.jl")
|
||||||
|
|
||||||
|
# Precompilation
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
function __precompile_cuts__()
|
||||||
|
function build_model(mps_filename)
|
||||||
|
model = read_from_file(mps_filename)
|
||||||
|
set_optimizer(model, SCIP.Optimizer)
|
||||||
|
return JumpModel(model)
|
||||||
|
end
|
||||||
|
BASEDIR = dirname(@__FILE__)
|
||||||
|
mps_filename = "$BASEDIR/../test/fixtures/bell5.mps.gz"
|
||||||
|
h5_filename = "$BASEDIR/../test/fixtures/bell5.h5"
|
||||||
|
collect_gmi_dual(
|
||||||
|
mps_filename;
|
||||||
|
optimizer=HiGHS.Optimizer,
|
||||||
|
max_rounds = 10,
|
||||||
|
max_cuts_per_round = 500,
|
||||||
|
)
|
||||||
|
knn = KnnDualGmiComponent(
|
||||||
|
extractor = H5FieldsExtractor(instance_fields = ["static_var_obj_coeffs"]),
|
||||||
|
k = 2,
|
||||||
|
)
|
||||||
|
knn.fit([h5_filename, h5_filename])
|
||||||
|
solver = LearningSolver(
|
||||||
|
components = [
|
||||||
|
ExpertPrimalComponent(action = SetWarmStart()),
|
||||||
|
knn,
|
||||||
|
],
|
||||||
|
skip_lp = true,
|
||||||
|
)
|
||||||
|
solver.optimize(mps_filename, build_model)
|
||||||
|
end
|
||||||
|
|
||||||
|
@setup_workload begin
|
||||||
|
using SCIP
|
||||||
|
using HiGHS
|
||||||
|
using MIPLearn.Cuts
|
||||||
|
using PrecompileTools: @setup_workload, @compile_workload
|
||||||
|
|
||||||
|
__init__()
|
||||||
|
Cuts.__init__()
|
||||||
|
|
||||||
|
@compile_workload begin
|
||||||
|
__precompile_cuts__()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end # module
|
end # module
|
||||||
|
|||||||
BIN
test/fixtures/bell5.h5
vendored
BIN
test/fixtures/bell5.h5
vendored
Binary file not shown.
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
using SCIP
|
using SCIP
|
||||||
using HiGHS
|
using HiGHS
|
||||||
|
using MIPLearn.Cuts
|
||||||
|
|
||||||
function test_cuts_tableau_gmi_dual_collect()
|
function test_cuts_tableau_gmi_dual_collect()
|
||||||
mps_filename = "$BASEDIR/../fixtures/bell5.mps.gz"
|
mps_filename = "$BASEDIR/../fixtures/bell5.mps.gz"
|
||||||
@@ -31,15 +32,15 @@ function test_cuts_tableau_gmi_dual_usage()
|
|||||||
|
|
||||||
mps_filename = "$BASEDIR/../fixtures/bell5.mps.gz"
|
mps_filename = "$BASEDIR/../fixtures/bell5.mps.gz"
|
||||||
h5_filename = "$BASEDIR/../fixtures/bell5.h5"
|
h5_filename = "$BASEDIR/../fixtures/bell5.h5"
|
||||||
# rm(h5_filename, force=true)
|
rm(h5_filename, force=true)
|
||||||
|
|
||||||
# # Run basic collector
|
# Run basic collector
|
||||||
# bc = BasicCollector(write_mps = false, skip_lp = true)
|
bc = BasicCollector(write_mps = false, skip_lp = true)
|
||||||
# bc.collect([mps_filename], build_model)
|
bc.collect([mps_filename], build_model)
|
||||||
|
|
||||||
# # Run dual GMI collector
|
# Run dual GMI collector
|
||||||
# @info "Running dual GMI collector..."
|
@info "Running dual GMI collector..."
|
||||||
# collect_gmi_dual(mps_filename, optimizer = HiGHS.Optimizer)
|
collect_gmi_dual(mps_filename, optimizer = HiGHS.Optimizer)
|
||||||
|
|
||||||
# # Test expert component
|
# # Test expert component
|
||||||
# solver = LearningSolver(
|
# solver = LearningSolver(
|
||||||
@@ -65,6 +66,5 @@ function test_cuts_tableau_gmi_dual_usage()
|
|||||||
skip_lp = true,
|
skip_lp = true,
|
||||||
)
|
)
|
||||||
solver.optimize(mps_filename, build_model)
|
solver.optimize(mps_filename, build_model)
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user