mirror of
https://github.com/ANL-CEEESA/MIPLearn.jl.git
synced 2025-12-07 00:48:50 -06:00
Merge branch 'dev' into feature/replay
This commit is contained in:
@@ -7,6 +7,7 @@ version = "0.1.0"
|
||||
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
|
||||
Clp = "e2554f3b-3117-50c0-817c-e040a3ddf72d"
|
||||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
||||
GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6"
|
||||
Glob = "c27321d9-0574-5035-807b-f59d2c89b15c"
|
||||
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
|
||||
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
|
||||
@@ -17,6 +18,8 @@ Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||
MIPLearn = "2b1277c3-b477-4c49-a15e-7ba350325c68"
|
||||
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
|
||||
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
|
||||
SCIP = "82193955-e24f-5292-bf16-6f2c5261a85f"
|
||||
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||
|
||||
[compat]
|
||||
|
||||
BIN
test/fixtures/bell5.h5
vendored
BIN
test/fixtures/bell5.h5
vendored
Binary file not shown.
BIN
test/fixtures/stab-n50-00000.h5
vendored
Normal file
BIN
test/fixtures/stab-n50-00000.h5
vendored
Normal file
Binary file not shown.
BIN
test/fixtures/stab-n50-00000.pkl.gz
vendored
Normal file
BIN
test/fixtures/stab-n50-00000.pkl.gz
vendored
Normal file
Binary file not shown.
BIN
test/fixtures/tsp-n20-00000.h5
vendored
Normal file
BIN
test/fixtures/tsp-n20-00000.h5
vendored
Normal file
Binary file not shown.
BIN
test/fixtures/tsp-n20-00000.mps.gz
vendored
Normal file
BIN
test/fixtures/tsp-n20-00000.mps.gz
vendored
Normal file
Binary file not shown.
BIN
test/fixtures/tsp-n20-00000.pkl.gz
vendored
Normal file
BIN
test/fixtures/tsp-n20-00000.pkl.gz
vendored
Normal file
Binary file not shown.
BIN
test/fixtures/vpm2.h5
vendored
BIN
test/fixtures/vpm2.h5
vendored
Binary file not shown.
@@ -89,11 +89,7 @@ function bb_run(optimizer_name, optimizer; large=true)
|
||||
BB.ReliabilityBranching(aggregation=:min, collect=true),
|
||||
]
|
||||
h5 = H5File("$FIXTURES/$instance.h5")
|
||||
mip_lower_bound = h5.get_scalar("mip_lower_bound")
|
||||
mip_upper_bound = h5.get_scalar("mip_upper_bound")
|
||||
mip_sense = h5.get_scalar("mip_sense")
|
||||
mip_primal_bound =
|
||||
mip_sense == "min" ? mip_upper_bound : mip_lower_bound
|
||||
mip_obj_bound = h5.get_scalar("mip_obj_bound")
|
||||
h5.file.close()
|
||||
|
||||
mip = BB.init(optimizer)
|
||||
@@ -101,10 +97,10 @@ function bb_run(optimizer_name, optimizer; large=true)
|
||||
@info optimizer_name, branch_rule, instance
|
||||
@time BB.solve!(
|
||||
mip,
|
||||
initial_primal_bound=mip_primal_bound,
|
||||
print_interval=1,
|
||||
node_limit=25,
|
||||
branch_rule=branch_rule,
|
||||
initial_primal_bound = mip_obj_bound,
|
||||
print_interval = 1,
|
||||
node_limit = 25,
|
||||
branch_rule = branch_rule,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
23
test/src/Cuts/tableau/test_gmi.jl
Normal file
23
test/src/Cuts/tableau/test_gmi.jl
Normal file
@@ -0,0 +1,23 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020-2024, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
using HiGHS
|
||||
|
||||
function test_cuts_tableau_gmi()
|
||||
mps_filename = "$BASEDIR/../fixtures/bell5.mps.gz"
|
||||
h5_filename = "$BASEDIR/../fixtures/bell5.h5"
|
||||
collect_gmi(mps_filename, optimizer = HiGHS.Optimizer)
|
||||
h5 = H5File(h5_filename, "r")
|
||||
try
|
||||
cuts_lb = h5.get_array("cuts_lb")
|
||||
cuts_ub = h5.get_array("cuts_ub")
|
||||
cuts_lhs = h5.get_sparse("cuts_lhs")
|
||||
n_cuts = length(cuts_lb)
|
||||
@test n_cuts > 0
|
||||
@test n_cuts == length(cuts_ub)
|
||||
@test cuts_lhs.shape[1] == n_cuts
|
||||
finally
|
||||
h5.close()
|
||||
end
|
||||
end
|
||||
70
test/src/Cuts/tableau/test_gmi_dual.jl
Normal file
70
test/src/Cuts/tableau/test_gmi_dual.jl
Normal file
@@ -0,0 +1,70 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020-2024, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
using SCIP
|
||||
using HiGHS
|
||||
using MIPLearn.Cuts
|
||||
|
||||
function test_cuts_tableau_gmi_dual_collect()
|
||||
mps_filename = "$BASEDIR/../fixtures/bell5.mps.gz"
|
||||
h5_filename = "$BASEDIR/../fixtures/bell5.h5"
|
||||
stats = collect_gmi_dual(mps_filename, optimizer = HiGHS.Optimizer)
|
||||
h5 = H5File(h5_filename, "r")
|
||||
try
|
||||
cuts_basis_vars = h5.get_array("cuts_basis_vars")
|
||||
cuts_basis_sizes = h5.get_array("cuts_basis_sizes")
|
||||
cuts_rows = h5.get_array("cuts_rows")
|
||||
@test size(cuts_basis_vars) == (15, 402)
|
||||
@test size(cuts_basis_sizes) == (15, 4)
|
||||
@test size(cuts_rows) == (15,)
|
||||
finally
|
||||
h5.close()
|
||||
end
|
||||
end
|
||||
|
||||
function test_cuts_tableau_gmi_dual_usage()
|
||||
function build_model(mps_filename)
|
||||
model = read_from_file(mps_filename)
|
||||
set_optimizer(model, SCIP.Optimizer)
|
||||
return JumpModel(model)
|
||||
end
|
||||
|
||||
mps_filename = "$BASEDIR/../fixtures/bell5.mps.gz"
|
||||
h5_filename = "$BASEDIR/../fixtures/bell5.h5"
|
||||
rm(h5_filename, force=true)
|
||||
|
||||
# Run basic collector
|
||||
bc = BasicCollector(write_mps = false, skip_lp = true)
|
||||
bc.collect([mps_filename], build_model)
|
||||
|
||||
# Run dual GMI collector
|
||||
@info "Running dual GMI collector..."
|
||||
collect_gmi_dual(mps_filename, optimizer = HiGHS.Optimizer)
|
||||
|
||||
# # Test expert component
|
||||
# solver = LearningSolver(
|
||||
# components = [
|
||||
# ExpertPrimalComponent(action = SetWarmStart()),
|
||||
# ExpertDualGmiComponent(),
|
||||
# ],
|
||||
# skip_lp = true,
|
||||
# )
|
||||
# solver.optimize(mps_filename, build_model)
|
||||
|
||||
# Test kNN component
|
||||
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)
|
||||
return
|
||||
end
|
||||
@@ -16,10 +16,16 @@ FIXTURES = "$BASEDIR/../fixtures"
|
||||
include("fixtures.jl")
|
||||
|
||||
include("BB/test_bb.jl")
|
||||
include("components/test_cuts.jl")
|
||||
include("components/test_lazy.jl")
|
||||
include("Cuts/BlackBox/test_cplex.jl")
|
||||
include("Cuts/tableau/test_gmi.jl")
|
||||
include("Cuts/tableau/test_gmi_dual.jl")
|
||||
include("problems/test_setcover.jl")
|
||||
include("test_io.jl")
|
||||
include("problems/test_stab.jl")
|
||||
include("problems/test_tsp.jl")
|
||||
include("solvers/test_jump.jl")
|
||||
include("test_io.jl")
|
||||
include("test_usage.jl")
|
||||
|
||||
function runtests()
|
||||
@@ -27,11 +33,14 @@ function runtests()
|
||||
@testset "BB" begin
|
||||
test_bb()
|
||||
end
|
||||
# test_cuts_blackbox_cplex()
|
||||
test_io()
|
||||
test_problems_setcover()
|
||||
test_problems_stab()
|
||||
test_problems_tsp()
|
||||
test_solvers_jump()
|
||||
test_usage()
|
||||
test_cuts()
|
||||
test_lazy()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
41
test/src/components/test_cuts.jl
Normal file
41
test/src/components/test_cuts.jl
Normal file
@@ -0,0 +1,41 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020-2024, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
using SCIP
|
||||
|
||||
function gen_stab()
|
||||
np = pyimport("numpy")
|
||||
uniform = pyimport("scipy.stats").uniform
|
||||
randint = pyimport("scipy.stats").randint
|
||||
np.random.seed(42)
|
||||
gen = MaxWeightStableSetGenerator(
|
||||
w = uniform(10.0, scale = 1.0),
|
||||
n = randint(low = 50, high = 51),
|
||||
p = uniform(loc = 0.5, scale = 0.0),
|
||||
fix_graph = true,
|
||||
)
|
||||
data = gen.generate(1)
|
||||
data_filenames = write_pkl_gz(data, "$BASEDIR/../fixtures", prefix = "stab-n50-")
|
||||
collector = BasicCollector()
|
||||
collector.collect(
|
||||
data_filenames,
|
||||
data -> build_stab_model_jump(data, optimizer = SCIP.Optimizer),
|
||||
progress = true,
|
||||
verbose = true,
|
||||
)
|
||||
end
|
||||
|
||||
function test_cuts()
|
||||
data_filenames = ["$BASEDIR/../fixtures/stab-n50-00000.pkl.gz"]
|
||||
clf = pyimport("sklearn.dummy").DummyClassifier()
|
||||
extractor = H5FieldsExtractor(instance_fields = ["static_var_obj_coeffs"])
|
||||
comp = MemorizingCutsComponent(clf = clf, extractor = extractor)
|
||||
solver = LearningSolver(components = [comp])
|
||||
solver.fit(data_filenames)
|
||||
model, stats = solver.optimize(
|
||||
data_filenames[1],
|
||||
data -> build_stab_model_jump(data, optimizer = SCIP.Optimizer),
|
||||
)
|
||||
@test stats["Cuts: AOT"] > 0
|
||||
end
|
||||
44
test/src/components/test_lazy.jl
Normal file
44
test/src/components/test_lazy.jl
Normal file
@@ -0,0 +1,44 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020-2024, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
using GLPK
|
||||
|
||||
function gen_tsp()
|
||||
np = pyimport("numpy")
|
||||
uniform = pyimport("scipy.stats").uniform
|
||||
randint = pyimport("scipy.stats").randint
|
||||
np.random.seed(42)
|
||||
|
||||
gen = TravelingSalesmanGenerator(
|
||||
x = uniform(loc = 0.0, scale = 1000.0),
|
||||
y = uniform(loc = 0.0, scale = 1000.0),
|
||||
n = randint(low = 20, high = 21),
|
||||
gamma = uniform(loc = 1.0, scale = 0.25),
|
||||
fix_cities = true,
|
||||
round = true,
|
||||
)
|
||||
data = gen.generate(1)
|
||||
data_filenames = write_pkl_gz(data, "$BASEDIR/../fixtures", prefix = "tsp-n20-")
|
||||
collector = BasicCollector()
|
||||
collector.collect(
|
||||
data_filenames,
|
||||
data -> build_tsp_model_jump(data, optimizer = GLPK.Optimizer),
|
||||
progress = true,
|
||||
verbose = true,
|
||||
)
|
||||
end
|
||||
|
||||
function test_lazy()
|
||||
data_filenames = ["$BASEDIR/../fixtures/tsp-n20-00000.pkl.gz"]
|
||||
clf = pyimport("sklearn.dummy").DummyClassifier()
|
||||
extractor = H5FieldsExtractor(instance_fields = ["static_var_obj_coeffs"])
|
||||
comp = MemorizingLazyComponent(clf = clf, extractor = extractor)
|
||||
solver = LearningSolver(components = [comp])
|
||||
solver.fit(data_filenames)
|
||||
model, stats = solver.optimize(
|
||||
data_filenames[1],
|
||||
data -> build_tsp_model_jump(data, optimizer = GLPK.Optimizer),
|
||||
)
|
||||
@test stats["Lazy Constraints: AOT"] > 0
|
||||
end
|
||||
@@ -14,5 +14,5 @@ function fixture_setcover_data()
|
||||
end
|
||||
|
||||
function fixture_setcover_model()
|
||||
return build_setcover_model(fixture_setcover_data())
|
||||
return build_setcover_model_jump(fixture_setcover_data())
|
||||
end
|
||||
|
||||
@@ -51,7 +51,7 @@ function test_problems_setcover_model()
|
||||
)
|
||||
|
||||
h5 = H5File(tempname(), "w")
|
||||
model = build_setcover_model(data)
|
||||
model = build_setcover_model_jump(data)
|
||||
model.extract_after_load(h5)
|
||||
model.optimize()
|
||||
model.extract_after_mip(h5)
|
||||
|
||||
22
test/src/problems/test_stab.jl
Normal file
22
test/src/problems/test_stab.jl
Normal file
@@ -0,0 +1,22 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020-2024, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
using PyCall
|
||||
using SCIP
|
||||
|
||||
function test_problems_stab()
|
||||
nx = pyimport("networkx")
|
||||
data = MaxWeightStableSetData(
|
||||
graph = nx.gnp_random_graph(25, 0.5, seed = 42),
|
||||
weights = repeat([1.0], 25),
|
||||
)
|
||||
h5 = H5File(tempname(), "w")
|
||||
model = build_stab_model_jump(data, optimizer = SCIP.Optimizer)
|
||||
model.extract_after_load(h5)
|
||||
model.optimize()
|
||||
model.extract_after_mip(h5)
|
||||
@test h5.get_scalar("mip_obj_value") == -6
|
||||
@test h5.get_scalar("mip_cuts")[1:20] == "[[0,8,11,13],[0,8,13"
|
||||
h5.close()
|
||||
end
|
||||
22
test/src/problems/test_tsp.jl
Normal file
22
test/src/problems/test_tsp.jl
Normal file
@@ -0,0 +1,22 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020-2024, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
using GLPK
|
||||
using JuMP
|
||||
|
||||
function test_problems_tsp()
|
||||
pdist = pyimport("scipy.spatial.distance").pdist
|
||||
squareform = pyimport("scipy.spatial.distance").squareform
|
||||
|
||||
data = TravelingSalesmanData(
|
||||
n_cities = 6,
|
||||
distances = squareform(
|
||||
pdist([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0], [3.0, 0.0], [0.0, 1.0], [3.0, 1.0]]),
|
||||
),
|
||||
)
|
||||
model = build_tsp_model_jump(data, optimizer = GLPK.Optimizer)
|
||||
model.optimize()
|
||||
@test objective_value(model.inner) == 8.0
|
||||
return
|
||||
end
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using MIPLearn
|
||||
using JLD2
|
||||
using SparseArrays
|
||||
|
||||
struct _TestStruct
|
||||
n::Int
|
||||
@@ -35,6 +36,8 @@ function test_h5()
|
||||
_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_roundtrip_sparse(h5, sparse([1; 2; 3], [1; 2; 3], [1; 2; 3]))
|
||||
# _test_roundtrip_sparse(h5, sparse([1; 2; 3], [1; 2; 3], [1; 2; 3], 4, 4))
|
||||
@test h5.get_array("unknown-key") === nothing
|
||||
h5.close()
|
||||
end
|
||||
@@ -46,7 +49,7 @@ function test_jld2()
|
||||
_TestStruct(2, [1.0, 2.0, 3.0]),
|
||||
_TestStruct(3, [3.0, 3.0, 3.0]),
|
||||
]
|
||||
filenames = write_jld2(data, dirname, prefix="obj")
|
||||
filenames = write_jld2(data, dirname, prefix = "obj")
|
||||
@test all(
|
||||
filenames .==
|
||||
["$dirname/obj00001.jld2", "$dirname/obj00002.jld2", "$dirname/obj00003.jld2"],
|
||||
@@ -79,3 +82,11 @@ function _test_roundtrip_str_array(h5, original)
|
||||
@test recovered !== nothing
|
||||
@test all(original .== recovered)
|
||||
end
|
||||
|
||||
function _test_roundtrip_sparse(h5, original)
|
||||
h5.put_sparse("key", original)
|
||||
recovered = MIPLearn.convert(SparseMatrixCSC, h5.get_sparse("key"))
|
||||
@test recovered !== nothing
|
||||
@test size(original) == size(recovered)
|
||||
@test all(original .== recovered)
|
||||
end
|
||||
|
||||
@@ -29,13 +29,13 @@ function test_usage()
|
||||
|
||||
@debug "Collecting training data..."
|
||||
bc = BasicCollector()
|
||||
bc.collect(data_filenames, build_setcover_model)
|
||||
bc.collect(data_filenames, build_setcover_model_jump)
|
||||
|
||||
@debug "Training models..."
|
||||
solver.fit(data_filenames)
|
||||
|
||||
@debug "Solving model..."
|
||||
solver.optimize(data_filenames[1], build_setcover_model)
|
||||
solver.optimize(data_filenames[1], build_setcover_model_jump)
|
||||
|
||||
@debug "Checking solution..."
|
||||
h5 = H5File(h5_filenames[1])
|
||||
|
||||
Reference in New Issue
Block a user