parent
4d5b7e971c
commit
190c288203
@ -0,0 +1,71 @@
|
||||
# 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
|
||||
|
||||
global TravelingSalesmanData = PyNULL()
|
||||
global TravelingSalesmanGenerator = PyNULL()
|
||||
|
||||
function __init_problems_tsp__()
|
||||
copy!(TravelingSalesmanData, pyimport("miplearn.problems.tsp").TravelingSalesmanData)
|
||||
copy!(TravelingSalesmanGenerator, pyimport("miplearn.problems.tsp").TravelingSalesmanGenerator)
|
||||
end
|
||||
|
||||
function build_tsp_model_jump(data::Any; optimizer=HiGHS.Optimizer)
|
||||
nx = pyimport("networkx")
|
||||
|
||||
if data isa String
|
||||
data = read_pkl_gz(data)
|
||||
end
|
||||
model = Model(optimizer)
|
||||
edges = [(i, j) for i in 1:data.n_cities for j in (i+1):data.n_cities]
|
||||
x = @variable(model, x[edges], Bin)
|
||||
@objective(model, Min, sum(
|
||||
x[(i, j)] * data.distances[i, j] for (i, j) in edges
|
||||
))
|
||||
|
||||
# Eq: Must choose two edges adjacent to each node
|
||||
@constraint(
|
||||
model,
|
||||
eq_degree[i in 1:data.n_cities],
|
||||
sum(x[(min(i, j), max(i, j))] for j in 1:data.n_cities if i != j) == 2
|
||||
)
|
||||
|
||||
function lazy_separate(cb_data)
|
||||
x_val = callback_value.(Ref(cb_data), x)
|
||||
violations = []
|
||||
selected_edges = [e for e in edges if x_val[e] > 0.5]
|
||||
graph = nx.Graph()
|
||||
graph.add_edges_from(selected_edges)
|
||||
for component in nx.connected_components(graph)
|
||||
if length(component) < data.n_cities
|
||||
cut_edges = [
|
||||
[e[1], e[2]]
|
||||
for e in edges
|
||||
if (e[1] ∈ component && e[2] ∉ component)
|
||||
||
|
||||
(e[1] ∉ component && e[2] ∈ component)
|
||||
]
|
||||
push!(violations, cut_edges)
|
||||
end
|
||||
end
|
||||
return violations
|
||||
end
|
||||
|
||||
function lazy_enforce(violations)
|
||||
@info "Adding $(length(violations)) subtour elimination eqs..."
|
||||
for violation in violations
|
||||
constr = @build_constraint(sum(x[(e[1], e[2])] for e in violation) >= 2)
|
||||
submit(model, constr)
|
||||
end
|
||||
end
|
||||
|
||||
return JumpModel(
|
||||
model,
|
||||
lazy_enforce=lazy_enforce,
|
||||
lazy_separate=lazy_separate,
|
||||
)
|
||||
end
|
||||
|
||||
export TravelingSalesmanData, TravelingSalesmanGenerator, build_tsp_model_jump
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,46 @@
|
||||
# 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(write_mps=false)
|
||||
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)
|
||||
stats = solver.optimize(
|
||||
data_filenames[1],
|
||||
data -> build_tsp_model_jump(data, optimizer=GLPK.Optimizer),
|
||||
)
|
||||
@test stats["Lazy Constraints: 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 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
|
Loading…
Reference in new issue