mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Improve JuMPSolver performance
This commit is contained in:
@@ -164,6 +164,7 @@ uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
|
|||||||
version = "0.7.13"
|
version = "0.7.13"
|
||||||
|
|
||||||
[[LibGit2]]
|
[[LibGit2]]
|
||||||
|
deps = ["Printf"]
|
||||||
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
||||||
|
|
||||||
[[Libdl]]
|
[[Libdl]]
|
||||||
@@ -212,9 +213,9 @@ version = "1.0.1"
|
|||||||
|
|
||||||
[[MbedTLS_jll]]
|
[[MbedTLS_jll]]
|
||||||
deps = ["Libdl", "Pkg"]
|
deps = ["Libdl", "Pkg"]
|
||||||
git-tree-sha1 = "066a4467008745eed36dad973ceb66405785a621"
|
git-tree-sha1 = "c83f5a1d038f034ad0549f9ee4d5fac3fb429e33"
|
||||||
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
|
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
|
||||||
version = "2.16.0+1"
|
version = "2.16.0+2"
|
||||||
|
|
||||||
[[Mmap]]
|
[[Mmap]]
|
||||||
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
|
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
|
||||||
@@ -249,7 +250,7 @@ uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
|
|||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
||||||
[[Pkg]]
|
[[Pkg]]
|
||||||
deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Test", "UUIDs"]
|
deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"]
|
||||||
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||||
|
|
||||||
[[Printf]]
|
[[Printf]]
|
||||||
@@ -309,6 +310,12 @@ uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
|
|||||||
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
|
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
|
||||||
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
|
|
||||||
|
[[TimerOutputs]]
|
||||||
|
deps = ["Printf"]
|
||||||
|
git-tree-sha1 = "0cc8db57cb537191b02948d4fabdc09eb7f31f98"
|
||||||
|
uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
|
||||||
|
version = "0.5.5"
|
||||||
|
|
||||||
[[TinyBnB]]
|
[[TinyBnB]]
|
||||||
deps = ["CPLEXW", "Printf", "Random", "Revise", "Test"]
|
deps = ["CPLEXW", "Printf", "Random", "Revise", "Test"]
|
||||||
git-tree-sha1 = "921ad42ac9dd4d44e42941eb63c59bcb50f30b6f"
|
git-tree-sha1 = "921ad42ac9dd4d44e42941eb63c59bcb50f30b6f"
|
||||||
|
|||||||
@@ -12,4 +12,9 @@ MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
|||||||
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
|
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"
|
||||||
|
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
|
||||||
TinyBnB = "1b2a1171-e557-4eeb-a4d6-6c23d7e94fcd"
|
TinyBnB = "1b2a1171-e557-4eeb-a4d6-6c23d7e94fcd"
|
||||||
|
|
||||||
|
[compat]
|
||||||
|
CPLEX = "0.6"
|
||||||
|
JuMP = "0.21"
|
||||||
|
|||||||
@@ -3,17 +3,39 @@
|
|||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
using JuMP
|
using JuMP
|
||||||
|
using CPLEX
|
||||||
using MathOptInterface
|
using MathOptInterface
|
||||||
const MOI = MathOptInterface
|
const MOI = MathOptInterface
|
||||||
|
using TimerOutputs
|
||||||
|
|
||||||
|
mutable struct JuMPSolverData
|
||||||
|
basename_idx_to_var
|
||||||
|
var_to_basename_idx
|
||||||
|
optimizer
|
||||||
|
instance
|
||||||
|
model
|
||||||
|
bin_vars
|
||||||
|
solution
|
||||||
|
end
|
||||||
|
|
||||||
function varname_split(varname::String)
|
function varname_split(varname::String)
|
||||||
m = match(r"([^[]*)\[(.*)\]", varname)
|
m = match(r"([^[]*)\[(.*)\]", varname)
|
||||||
|
if m == nothing
|
||||||
|
return varname, ""
|
||||||
|
end
|
||||||
return m.captures[1], m.captures[2]
|
return m.captures[1], m.captures[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
@pydef mutable struct JuMPSolver <: InternalSolver
|
@pydef mutable struct JuMPSolver <: InternalSolver
|
||||||
function __init__(self; optimizer=CPLEX.Optimizer)
|
function __init__(self; optimizer=nothing)
|
||||||
self.optimizer = optimizer
|
self.data = JuMPSolverData(nothing, # basename_idx_to_var
|
||||||
|
nothing, # var_to_basename_idx
|
||||||
|
optimizer,
|
||||||
|
nothing, # instance
|
||||||
|
nothing, # model
|
||||||
|
nothing, # bin_vars
|
||||||
|
nothing, # solution
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function add_constraint(self, constraint)
|
function add_constraint(self, constraint)
|
||||||
@@ -21,10 +43,11 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
function set_warm_start(self, solution)
|
function set_warm_start(self, solution)
|
||||||
|
basename_idx_to_var = self.data.basename_idx_to_var
|
||||||
for (basename, subsolution) in solution
|
for (basename, subsolution) in solution
|
||||||
for (idx, value) in subsolution
|
for (idx, value) in subsolution
|
||||||
value != nothing || continue
|
value != nothing || continue
|
||||||
var = self.basename_idx_to_var[basename, idx]
|
var = basename_idx_to_var[basename, idx]
|
||||||
JuMP.set_start_value(var, value)
|
JuMP.set_start_value(var, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -35,88 +58,126 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
function fix(self, solution)
|
function fix(self, solution)
|
||||||
for (basename, subsolution) in solution
|
@timeit "fix" begin
|
||||||
for (idx, value) in subsolution
|
basename_idx_to_var = self.data.basename_idx_to_var
|
||||||
value != nothing || continue
|
for (basename, subsolution) in solution
|
||||||
var = self.basename_idx_to_var[basename, idx]
|
for (idx, value) in subsolution
|
||||||
JuMP.fix(var, value, force=true)
|
value != nothing || continue
|
||||||
|
var = basename_idx_to_var[basename, idx]
|
||||||
|
JuMP.fix(var, value, force=true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_instance(self, instance, model)
|
function set_instance(self, instance, model)
|
||||||
self.instance = instance
|
@timeit "set_instance" begin
|
||||||
self.model = model
|
self.data.instance = instance
|
||||||
self.var_to_basename_idx = Dict(var => varname_split(JuMP.name(var))
|
self.data.model = model
|
||||||
for var in JuMP.all_variables(self.model))
|
self.data.var_to_basename_idx = Dict(var => varname_split(JuMP.name(var))
|
||||||
self.basename_idx_to_var = Dict(varname_split(JuMP.name(var)) => var
|
for var in JuMP.all_variables(model))
|
||||||
for var in JuMP.all_variables(self.model))
|
self.data.basename_idx_to_var = Dict(varname_split(JuMP.name(var)) => var
|
||||||
self.bin_vars = [var
|
for var in JuMP.all_variables(model))
|
||||||
for var in JuMP.all_variables(self.model)
|
self.data.bin_vars = [var
|
||||||
if JuMP.is_binary(var)]
|
for var in JuMP.all_variables(model)
|
||||||
JuMP.set_optimizer(self.model, self.optimizer)
|
if JuMP.is_binary(var)]
|
||||||
|
if self.data.optimizer != nothing
|
||||||
|
JuMP.set_optimizer(model, self.data.optimizer)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function solve(self; tee=false)
|
function solve(self; tee=false)
|
||||||
JuMP.optimize!(self.model)
|
@timeit "solve" begin
|
||||||
self._update_solution()
|
instance, model = self.data.instance, self.data.model
|
||||||
|
wallclock_time = 0
|
||||||
primal_bound = JuMP.objective_value(self.model)
|
found_violations = []
|
||||||
dual_bound = JuMP.objective_bound(self.model)
|
|
||||||
|
while true
|
||||||
if JuMP.objective_sense(self.model) == MOI.MIN_SENSE
|
@timeit "optimize!" begin
|
||||||
sense = "min"
|
JuMP.optimize!(model)
|
||||||
lower_bound = dual_bound
|
end
|
||||||
upper_bound = primal_bound
|
wallclock_time += JuMP.solve_time(model)
|
||||||
else
|
@timeit "find_violations" begin
|
||||||
sense = "max"
|
violations = instance.find_violations(model)
|
||||||
lower_bound = primal_bound
|
end
|
||||||
upper_bound = dual_bound
|
@info "$(length(violations)) violations found"
|
||||||
|
if length(violations) == 0
|
||||||
|
break
|
||||||
|
end
|
||||||
|
append!(found_violations, violations)
|
||||||
|
for v in violations
|
||||||
|
instance.build_lazy_constraint(self.data.model, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@timeit "update solution" begin
|
||||||
|
self._update_solution()
|
||||||
|
self.data.instance.found_violations = found_violations
|
||||||
|
end
|
||||||
|
primal_bound = JuMP.objective_value(model)
|
||||||
|
dual_bound = JuMP.objective_bound(model)
|
||||||
|
if JuMP.objective_sense(model) == MOI.MIN_SENSE
|
||||||
|
sense = "min"
|
||||||
|
lower_bound = dual_bound
|
||||||
|
upper_bound = primal_bound
|
||||||
|
else
|
||||||
|
sense = "max"
|
||||||
|
lower_bound = primal_bound
|
||||||
|
upper_bound = dual_bound
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@show primal_bound, dual_bound
|
|
||||||
|
|
||||||
return Dict("Lower bound" => lower_bound,
|
return Dict("Lower bound" => lower_bound,
|
||||||
"Upper bound" => upper_bound,
|
"Upper bound" => upper_bound,
|
||||||
"Sense" => sense,
|
"Sense" => sense,
|
||||||
"Wallclock time" => JuMP.solve_time(self.model),
|
"Wallclock time" => wallclock_time,
|
||||||
"Nodes" => 1,
|
"Nodes" => 1,
|
||||||
"Log" => nothing,
|
"Log" => nothing,
|
||||||
"Warm start value" => nothing)
|
"Warm start value" => nothing)
|
||||||
end
|
end
|
||||||
|
|
||||||
function solve_lp(self; tee=false)
|
function solve_lp(self; tee=false)
|
||||||
for var in self.bin_vars
|
@timeit "solve_lp" begin
|
||||||
JuMP.unset_binary(var)
|
model = self.data.model
|
||||||
JuMP.set_upper_bound(var, 1.0)
|
bin_vars = self.data.bin_vars
|
||||||
JuMP.set_lower_bound(var, 0.0)
|
@timeit "unset_binary" begin
|
||||||
|
for var in bin_vars
|
||||||
|
JuMP.unset_binary(var)
|
||||||
|
JuMP.set_upper_bound(var, 1.0)
|
||||||
|
JuMP.set_lower_bound(var, 0.0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@timeit "optimize" begin
|
||||||
|
JuMP.optimize!(model)
|
||||||
|
end
|
||||||
|
@timeit "update solution" begin
|
||||||
|
self._update_solution()
|
||||||
|
end
|
||||||
|
obj_value = JuMP.objective_value(model)
|
||||||
|
@timeit "set_binary" begin
|
||||||
|
for var in bin_vars
|
||||||
|
JuMP.set_binary(var)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
JuMP.optimize!(self.model)
|
|
||||||
obj_value = JuMP.objective_value(self.model)
|
|
||||||
self._update_solution()
|
|
||||||
|
|
||||||
for var in self.bin_vars
|
|
||||||
JuMP.set_binary(var)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Dict("Optimal value" => obj_value)
|
return Dict("Optimal value" => obj_value)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_solution(self)
|
function get_solution(self)
|
||||||
return self.solution
|
return self.data.solution
|
||||||
end
|
end
|
||||||
|
|
||||||
function _update_solution(self)
|
function _update_solution(self)
|
||||||
|
var_to_basename_idx, model = self.data.var_to_basename_idx, self.data.model
|
||||||
solution = Dict()
|
solution = Dict()
|
||||||
for var in JuMP.all_variables(self.model)
|
for var in JuMP.all_variables(model)
|
||||||
basename, idx = self.var_to_basename_idx[var]
|
var in keys(var_to_basename_idx) || continue
|
||||||
|
basename, idx = var_to_basename_idx[var]
|
||||||
if !haskey(solution, basename)
|
if !haskey(solution, basename)
|
||||||
solution[basename] = Dict()
|
solution[basename] = Dict()
|
||||||
end
|
end
|
||||||
solution[basename][idx] = JuMP.value(var)
|
solution[basename][idx] = JuMP.value(var)
|
||||||
end
|
end
|
||||||
self.solution = solution
|
self.data.solution = solution
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_gap_tolerance(self, gap_tolerance)
|
function set_gap_tolerance(self, gap_tolerance)
|
||||||
@@ -136,6 +197,6 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
function set_time_limit(self, time_limit)
|
function set_time_limit(self, time_limit)
|
||||||
JuMP.set_time_limit_sec(self.model, time_limit)
|
JuMP.set_time_limit_sec(self.data.model, time_limit)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user