parent
1ea432fb57
commit
d69c4bbfa7
@ -0,0 +1,60 @@
|
|||||||
|
# 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 JuMP
|
||||||
|
using HiGHS
|
||||||
|
|
||||||
|
global MaxWeightStableSetData = PyNULL()
|
||||||
|
global MaxWeightStableSetGenerator = PyNULL()
|
||||||
|
|
||||||
|
function __init_problems_stab__()
|
||||||
|
copy!(MaxWeightStableSetData, pyimport("miplearn.problems.stab").MaxWeightStableSetData)
|
||||||
|
copy!(MaxWeightStableSetGenerator, pyimport("miplearn.problems.stab").MaxWeightStableSetGenerator)
|
||||||
|
end
|
||||||
|
|
||||||
|
function build_stab_model_jump(data::Any; optimizer=HiGHS.Optimizer)
|
||||||
|
nx = pyimport("networkx")
|
||||||
|
|
||||||
|
if data isa String
|
||||||
|
data = read_pkl_gz(data)
|
||||||
|
end
|
||||||
|
model = Model(optimizer)
|
||||||
|
|
||||||
|
# Variables and objective function
|
||||||
|
nodes = data.graph.nodes
|
||||||
|
x = @variable(model, x[nodes], Bin)
|
||||||
|
@objective(model, Min, sum(-data.weights[i+1] * x[i] for i in nodes))
|
||||||
|
|
||||||
|
# Edge inequalities
|
||||||
|
for (i1, i2) in data.graph.edges
|
||||||
|
@constraint(model, x[i1] + x[i2] <= 1, base_name = "eq_edge[$i1,$i2]")
|
||||||
|
end
|
||||||
|
|
||||||
|
function cuts_separate(cb_data)
|
||||||
|
x_val = callback_value.(Ref(cb_data), x)
|
||||||
|
violations = []
|
||||||
|
for clique in nx.find_cliques(data.graph)
|
||||||
|
if sum(x_val[i] for i in clique) > 1.0001
|
||||||
|
push!(violations, sort(clique))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return violations
|
||||||
|
end
|
||||||
|
|
||||||
|
function cuts_enforce(violations)
|
||||||
|
@info "Adding $(length(violations)) clique cuts..."
|
||||||
|
for clique in violations
|
||||||
|
constr = @build_constraint(sum(x[i] for i in clique) <= 1)
|
||||||
|
submit(model, constr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return JumpModel(
|
||||||
|
model,
|
||||||
|
cuts_separate=cuts_separate,
|
||||||
|
cuts_enforce=cuts_enforce,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
export MaxWeightStableSetData, MaxWeightStableSetGenerator, build_stab_model_jump
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,45 @@
|
|||||||
|
# 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(write_mps=false)
|
||||||
|
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-0000$i.pkl.gz" for i in 0:0]
|
||||||
|
clf = pyimport("sklearn.neighbors").KNeighborsClassifier(n_neighbors=1)
|
||||||
|
extractor = H5FieldsExtractor(
|
||||||
|
instance_fields=["static_var_obj_coeffs"],
|
||||||
|
)
|
||||||
|
comp = MemorizingCutsComponent(clf=clf, extractor=extractor)
|
||||||
|
solver = LearningSolver(components=[comp])
|
||||||
|
solver.fit(data_filenames)
|
||||||
|
@show comp.n_features_
|
||||||
|
@show comp.n_targets_
|
||||||
|
stats = solver.optimize(
|
||||||
|
data_filenames[1],
|
||||||
|
data -> build_stab_model_jump(data, optimizer=SCIP.Optimizer),
|
||||||
|
)
|
||||||
|
@test stats["Cuts: AOT"] > 0
|
||||||
|
end
|
@ -0,0 +1,27 @@
|
|||||||
|
# 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()
|
||||||
|
test_problems_stab_1()
|
||||||
|
test_problems_stab_2()
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_problems_stab_1()
|
||||||
|
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
|
Loading…
Reference in new issue