mirror of
https://github.com/ANL-CEEESA/MIPLearn.jl.git
synced 2025-12-06 00:18:51 -06:00
Allow module to be precompiled
This commit is contained in:
@@ -2,12 +2,21 @@
|
|||||||
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
|
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
|
||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
__precompile__(false)
|
|
||||||
module MIPLearn
|
module MIPLearn
|
||||||
|
|
||||||
using PyCall
|
using PyCall
|
||||||
global miplearn = pyimport("miplearn")
|
|
||||||
global traceback = pyimport("traceback")
|
global DynamicLazyConstraintsComponent = PyNULL()
|
||||||
|
global JuMPSolver = PyNULL()
|
||||||
|
global MinPrecisionThreshold = PyNULL()
|
||||||
|
global miplearn = PyNULL()
|
||||||
|
global ObjectiveValueComponent = PyNULL()
|
||||||
|
global PrimalSolutionComponent = PyNULL()
|
||||||
|
global PyFileInstance = PyNULL()
|
||||||
|
global PyJuMPInstance = PyNULL()
|
||||||
|
global StaticLazyConstraintsComponent = PyNULL()
|
||||||
|
global traceback = PyNULL()
|
||||||
|
global UserCutsComponent = PyNULL()
|
||||||
|
|
||||||
include("utils/log.jl")
|
include("utils/log.jl")
|
||||||
include("utils/exceptions.jl")
|
include("utils/exceptions.jl")
|
||||||
@@ -19,12 +28,19 @@ include("solvers/learning.jl")
|
|||||||
include("solvers/macros.jl")
|
include("solvers/macros.jl")
|
||||||
include("utils/benchmark.jl")
|
include("utils/benchmark.jl")
|
||||||
|
|
||||||
DynamicLazyConstraintsComponent = miplearn.DynamicLazyConstraintsComponent
|
function __init__()
|
||||||
UserCutsComponent = miplearn.UserCutsComponent
|
copy!(miplearn, pyimport("miplearn"))
|
||||||
ObjectiveValueComponent = miplearn.ObjectiveValueComponent
|
copy!(traceback, pyimport("traceback"))
|
||||||
PrimalSolutionComponent = miplearn.PrimalSolutionComponent
|
copy!(DynamicLazyConstraintsComponent, miplearn.DynamicLazyConstraintsComponent)
|
||||||
StaticLazyConstraintsComponent = miplearn.StaticLazyConstraintsComponent
|
copy!(UserCutsComponent, miplearn.UserCutsComponent)
|
||||||
MinPrecisionThreshold = miplearn.MinPrecisionThreshold
|
copy!(ObjectiveValueComponent, miplearn.ObjectiveValueComponent)
|
||||||
|
copy!(PrimalSolutionComponent, miplearn.PrimalSolutionComponent)
|
||||||
|
copy!(StaticLazyConstraintsComponent, miplearn.StaticLazyConstraintsComponent)
|
||||||
|
copy!(MinPrecisionThreshold, miplearn.MinPrecisionThreshold)
|
||||||
|
__init_PyFileInstance__()
|
||||||
|
__init_PyJuMPInstance__()
|
||||||
|
__init_JuMPSolver__()
|
||||||
|
end
|
||||||
|
|
||||||
export DynamicLazyConstraintsComponent,
|
export DynamicLazyConstraintsComponent,
|
||||||
UserCutsComponent,
|
UserCutsComponent,
|
||||||
|
|||||||
@@ -3,56 +3,58 @@
|
|||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
|
|
||||||
@pydef mutable struct PyFileInstance <: miplearn.Instance
|
function __init_PyFileInstance__()
|
||||||
function __init__(self, filename)
|
@pydef mutable struct Class <: miplearn.Instance
|
||||||
self.filename = filename
|
function __init__(self, filename)
|
||||||
self.loaded = nothing
|
self.filename = filename
|
||||||
self.samples = nothing
|
self.loaded = nothing
|
||||||
end
|
self.samples = nothing
|
||||||
|
end
|
||||||
|
|
||||||
function to_model(self)
|
function to_model(self)
|
||||||
return self.loaded.py.to_model()
|
return self.loaded.py.to_model()
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_instance_features(self)
|
function get_instance_features(self)
|
||||||
return self.loaded.py.get_instance_features()
|
return self.loaded.py.get_instance_features()
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_variable_features(self, var_name)
|
function get_variable_features(self, var_name)
|
||||||
return self.loaded.py.get_variable_features(var_name)
|
return self.loaded.py.get_variable_features(var_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_variable_category(self, var_name)
|
function get_variable_category(self, var_name)
|
||||||
return self.loaded.py.get_variable_category(var_name)
|
return self.loaded.py.get_variable_category(var_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_constraint_features(self, cname)
|
function get_constraint_features(self, cname)
|
||||||
return self.loaded.py.get_constraint_features(cname)
|
return self.loaded.py.get_constraint_features(cname)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_constraint_category(self, cname)
|
function get_constraint_category(self, cname)
|
||||||
return self.loaded.py.get_constraint_category(cname)
|
return self.loaded.py.get_constraint_category(cname)
|
||||||
end
|
end
|
||||||
|
|
||||||
function load(self)
|
function load(self)
|
||||||
if self.loaded === nothing
|
if self.loaded === nothing
|
||||||
self.loaded = load_instance(self.filename)
|
self.loaded = load_instance(self.filename)
|
||||||
self.samples = self.loaded.py.samples
|
self.samples = self.loaded.py.samples
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function free(self)
|
||||||
|
self.loaded = nothing
|
||||||
|
self.samples = nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
function flush(self)
|
||||||
|
self.loaded.py.samples = self.samples
|
||||||
|
save(self.filename, self.loaded)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
copy!(PyFileInstance, Class)
|
||||||
function free(self)
|
|
||||||
self.loaded = nothing
|
|
||||||
self.samples = nothing
|
|
||||||
end
|
|
||||||
|
|
||||||
function flush(self)
|
|
||||||
self.loaded.py.samples = self.samples
|
|
||||||
save(self.filename, self.loaded)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
struct FileInstance <: Instance
|
struct FileInstance <: Instance
|
||||||
py::PyCall.PyObject
|
py::PyCall.PyObject
|
||||||
filename::AbstractString
|
filename::AbstractString
|
||||||
|
|||||||
@@ -5,45 +5,47 @@
|
|||||||
using JuMP
|
using JuMP
|
||||||
using JLD2
|
using JLD2
|
||||||
|
|
||||||
|
function __init_PyJuMPInstance__()
|
||||||
|
@pydef mutable struct Class <: miplearn.Instance
|
||||||
|
function __init__(self, model)
|
||||||
|
init_miplearn_ext(model)
|
||||||
|
self.model = model
|
||||||
|
self.samples = []
|
||||||
|
end
|
||||||
|
|
||||||
@pydef mutable struct PyJuMPInstance <: miplearn.Instance
|
function to_model(self)
|
||||||
function __init__(self, model)
|
return self.model
|
||||||
init_miplearn_ext(model)
|
end
|
||||||
self.model = model
|
|
||||||
self.samples = []
|
|
||||||
end
|
|
||||||
|
|
||||||
function to_model(self)
|
function get_instance_features(self)
|
||||||
return self.model
|
return self.model.ext[:miplearn][:instance_features]
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_instance_features(self)
|
function get_variable_features(self, var_name)
|
||||||
return self.model.ext[:miplearn][:instance_features]
|
model = self.model
|
||||||
end
|
v = variable_by_name(model, var_name)
|
||||||
|
return get(model.ext[:miplearn][:variable_features], v, nothing)
|
||||||
|
end
|
||||||
|
|
||||||
function get_variable_features(self, var_name)
|
function get_variable_category(self, var_name)
|
||||||
model = self.model
|
model = self.model
|
||||||
v = variable_by_name(model, var_name)
|
v = variable_by_name(model, var_name)
|
||||||
return get(model.ext[:miplearn][:variable_features], v, nothing)
|
return get(model.ext[:miplearn][:variable_categories], v, nothing)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_variable_category(self, var_name)
|
function get_constraint_features(self, cname)
|
||||||
model = self.model
|
model = self.model
|
||||||
v = variable_by_name(model, var_name)
|
c = constraint_by_name(model, cname)
|
||||||
return get(model.ext[:miplearn][:variable_categories], v, nothing)
|
return get(model.ext[:miplearn][:constraint_features], c, nothing)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_constraint_features(self, cname)
|
function get_constraint_category(self, cname)
|
||||||
model = self.model
|
model = self.model
|
||||||
c = constraint_by_name(model, cname)
|
c = constraint_by_name(model, cname)
|
||||||
return get(model.ext[:miplearn][:constraint_features], c, nothing)
|
return get(model.ext[:miplearn][:constraint_categories], c, nothing)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_constraint_category(self, cname)
|
|
||||||
model = self.model
|
|
||||||
c = constraint_by_name(model, cname)
|
|
||||||
return get(model.ext[:miplearn][:constraint_categories], c, nothing)
|
|
||||||
end
|
end
|
||||||
|
copy!(PyJuMPInstance, Class)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -467,144 +467,147 @@ function get_constraints(
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@pydef mutable struct JuMPSolver <: miplearn.solvers.internal.InternalSolver
|
function __init_JuMPSolver__()
|
||||||
function __init__(self, optimizer_factory)
|
@pydef mutable struct Class <: miplearn.solvers.internal.InternalSolver
|
||||||
self.data = JuMPSolverData(
|
function __init__(self, optimizer_factory)
|
||||||
optimizer_factory,
|
self.data = JuMPSolverData(
|
||||||
Dict(), # varname_to_var
|
optimizer_factory,
|
||||||
Dict(), # cname_to_constr
|
Dict(), # varname_to_var
|
||||||
nothing, # instance
|
Dict(), # cname_to_constr
|
||||||
nothing, # model
|
nothing, # instance
|
||||||
[], # bin_vars
|
nothing, # model
|
||||||
Dict(), # solution
|
[], # bin_vars
|
||||||
[], # reduced_costs
|
Dict(), # solution
|
||||||
Dict(), # dual_values
|
[], # reduced_costs
|
||||||
)
|
Dict(), # dual_values
|
||||||
end
|
)
|
||||||
|
|
||||||
function add_constraints(self, cf)
|
|
||||||
lhs = cf.lhs
|
|
||||||
if lhs isa Matrix
|
|
||||||
# Undo incorrect automatic conversion performed by PyCall
|
|
||||||
lhs = [col[:] for col in eachcol(lhs)]
|
|
||||||
end
|
end
|
||||||
add_constraints(
|
|
||||||
self.data,
|
|
||||||
lhs=lhs,
|
|
||||||
rhs=cf.rhs,
|
|
||||||
senses=cf.senses,
|
|
||||||
names=cf.names,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function are_constraints_satisfied(self, cf; tol=1e-5)
|
function add_constraints(self, cf)
|
||||||
lhs = cf.lhs
|
lhs = cf.lhs
|
||||||
if lhs isa Matrix
|
if lhs isa Matrix
|
||||||
# Undo incorrect automatic conversion performed by PyCall
|
# Undo incorrect automatic conversion performed by PyCall
|
||||||
lhs = [col[:] for col in eachcol(lhs)]
|
lhs = [col[:] for col in eachcol(lhs)]
|
||||||
|
end
|
||||||
|
add_constraints(
|
||||||
|
self.data,
|
||||||
|
lhs=lhs,
|
||||||
|
rhs=cf.rhs,
|
||||||
|
senses=cf.senses,
|
||||||
|
names=cf.names,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
return are_constraints_satisfied(
|
|
||||||
|
function are_constraints_satisfied(self, cf; tol=1e-5)
|
||||||
|
lhs = cf.lhs
|
||||||
|
if lhs isa Matrix
|
||||||
|
# Undo incorrect automatic conversion performed by PyCall
|
||||||
|
lhs = [col[:] for col in eachcol(lhs)]
|
||||||
|
end
|
||||||
|
return are_constraints_satisfied(
|
||||||
|
self.data,
|
||||||
|
lhs=lhs,
|
||||||
|
rhs=cf.rhs,
|
||||||
|
senses=cf.senses,
|
||||||
|
tol=tol,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
build_test_instance_infeasible(self) =
|
||||||
|
build_test_instance_infeasible()
|
||||||
|
|
||||||
|
build_test_instance_knapsack(self) =
|
||||||
|
build_test_instance_knapsack()
|
||||||
|
|
||||||
|
clone(self) = JuMPSolver(self.data.optimizer_factory)
|
||||||
|
|
||||||
|
fix(self, solution) =
|
||||||
|
fix!(self.data, solution)
|
||||||
|
|
||||||
|
get_solution(self) =
|
||||||
|
isempty(self.data.solution) ? nothing : self.data.solution
|
||||||
|
|
||||||
|
get_constraints(
|
||||||
|
self;
|
||||||
|
with_static=true,
|
||||||
|
with_sa=true,
|
||||||
|
with_lhs=true,
|
||||||
|
) = get_constraints(
|
||||||
self.data,
|
self.data,
|
||||||
lhs=lhs,
|
with_static=with_static,
|
||||||
rhs=cf.rhs,
|
|
||||||
senses=cf.senses,
|
|
||||||
tol=tol,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
get_constraint_attrs(self) = [
|
||||||
|
# "basis_status",
|
||||||
|
"categories",
|
||||||
|
"dual_values",
|
||||||
|
"lazy",
|
||||||
|
"lhs",
|
||||||
|
"names",
|
||||||
|
"rhs",
|
||||||
|
# "sa_rhs_down",
|
||||||
|
# "sa_rhs_up",
|
||||||
|
"senses",
|
||||||
|
# "slacks",
|
||||||
|
"user_features",
|
||||||
|
]
|
||||||
|
|
||||||
|
get_variables(
|
||||||
|
self;
|
||||||
|
with_static=true,
|
||||||
|
with_sa=true,
|
||||||
|
) = get_variables(self.data; with_static=with_static)
|
||||||
|
|
||||||
|
get_variable_attrs(self) = [
|
||||||
|
"names",
|
||||||
|
# "basis_status",
|
||||||
|
"categories",
|
||||||
|
"lower_bounds",
|
||||||
|
"obj_coeffs",
|
||||||
|
"reduced_costs",
|
||||||
|
# "sa_lb_down",
|
||||||
|
# "sa_lb_up",
|
||||||
|
# "sa_obj_down",
|
||||||
|
# "sa_obj_up",
|
||||||
|
# "sa_ub_down",
|
||||||
|
# "sa_ub_up",
|
||||||
|
"types",
|
||||||
|
"upper_bounds",
|
||||||
|
"user_features",
|
||||||
|
"values",
|
||||||
|
]
|
||||||
|
|
||||||
|
is_infeasible(self) =
|
||||||
|
is_infeasible(self.data)
|
||||||
|
|
||||||
|
remove_constraints(self, names) =
|
||||||
|
remove_constraints(
|
||||||
|
self.data,
|
||||||
|
[n for n in names],
|
||||||
|
)
|
||||||
|
|
||||||
|
set_instance(self, instance, model=nothing) =
|
||||||
|
set_instance!(self.data, instance, model=model)
|
||||||
|
|
||||||
|
set_warm_start(self, solution) =
|
||||||
|
set_warm_start!(self.data, solution)
|
||||||
|
|
||||||
|
solve(
|
||||||
|
self;
|
||||||
|
tee=false,
|
||||||
|
iteration_cb=nothing,
|
||||||
|
lazy_cb=nothing,
|
||||||
|
user_cut_cb=nothing,
|
||||||
|
) = solve(
|
||||||
|
self.data,
|
||||||
|
tee=tee,
|
||||||
|
iteration_cb=iteration_cb,
|
||||||
|
)
|
||||||
|
|
||||||
|
solve_lp(self; tee=false) =
|
||||||
|
solve_lp(self.data, tee=tee)
|
||||||
end
|
end
|
||||||
|
copy!(JuMPSolver, Class)
|
||||||
build_test_instance_infeasible(self) =
|
|
||||||
build_test_instance_infeasible()
|
|
||||||
|
|
||||||
build_test_instance_knapsack(self) =
|
|
||||||
build_test_instance_knapsack()
|
|
||||||
|
|
||||||
clone(self) = JuMPSolver(self.data.optimizer_factory)
|
|
||||||
|
|
||||||
fix(self, solution) =
|
|
||||||
fix!(self.data, solution)
|
|
||||||
|
|
||||||
get_solution(self) =
|
|
||||||
isempty(self.data.solution) ? nothing : self.data.solution
|
|
||||||
|
|
||||||
get_constraints(
|
|
||||||
self;
|
|
||||||
with_static=true,
|
|
||||||
with_sa=true,
|
|
||||||
with_lhs=true,
|
|
||||||
) = get_constraints(
|
|
||||||
self.data,
|
|
||||||
with_static=with_static,
|
|
||||||
)
|
|
||||||
|
|
||||||
get_constraint_attrs(self) = [
|
|
||||||
# "basis_status",
|
|
||||||
"categories",
|
|
||||||
"dual_values",
|
|
||||||
"lazy",
|
|
||||||
"lhs",
|
|
||||||
"names",
|
|
||||||
"rhs",
|
|
||||||
# "sa_rhs_down",
|
|
||||||
# "sa_rhs_up",
|
|
||||||
"senses",
|
|
||||||
# "slacks",
|
|
||||||
"user_features",
|
|
||||||
]
|
|
||||||
|
|
||||||
get_variables(
|
|
||||||
self;
|
|
||||||
with_static=true,
|
|
||||||
with_sa=true,
|
|
||||||
) = get_variables(self.data; with_static=with_static)
|
|
||||||
|
|
||||||
get_variable_attrs(self) = [
|
|
||||||
"names",
|
|
||||||
# "basis_status",
|
|
||||||
"categories",
|
|
||||||
"lower_bounds",
|
|
||||||
"obj_coeffs",
|
|
||||||
"reduced_costs",
|
|
||||||
# "sa_lb_down",
|
|
||||||
# "sa_lb_up",
|
|
||||||
# "sa_obj_down",
|
|
||||||
# "sa_obj_up",
|
|
||||||
# "sa_ub_down",
|
|
||||||
# "sa_ub_up",
|
|
||||||
"types",
|
|
||||||
"upper_bounds",
|
|
||||||
"user_features",
|
|
||||||
"values",
|
|
||||||
]
|
|
||||||
|
|
||||||
is_infeasible(self) =
|
|
||||||
is_infeasible(self.data)
|
|
||||||
|
|
||||||
remove_constraints(self, names) =
|
|
||||||
remove_constraints(
|
|
||||||
self.data,
|
|
||||||
[n for n in names],
|
|
||||||
)
|
|
||||||
|
|
||||||
set_instance(self, instance, model=nothing) =
|
|
||||||
set_instance!(self.data, instance, model=model)
|
|
||||||
|
|
||||||
set_warm_start(self, solution) =
|
|
||||||
set_warm_start!(self.data, solution)
|
|
||||||
|
|
||||||
solve(
|
|
||||||
self;
|
|
||||||
tee=false,
|
|
||||||
iteration_cb=nothing,
|
|
||||||
lazy_cb=nothing,
|
|
||||||
user_cut_cb=nothing,
|
|
||||||
) = solve(
|
|
||||||
self.data,
|
|
||||||
tee=tee,
|
|
||||||
iteration_cb=iteration_cb,
|
|
||||||
)
|
|
||||||
|
|
||||||
solve_lp(self; tee=false) =
|
|
||||||
solve_lp(self.data, tee=tee)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user