From 529e6289ecd05151b212f68d4e431fd0539ca202 Mon Sep 17 00:00:00 2001 From: Alinson S Xavier Date: Thu, 17 Jun 2021 12:41:48 -0500 Subject: [PATCH] Allow module to be precompiled --- src/MIPLearn.jl | 34 ++++-- src/instance/file.jl | 76 ++++++------- src/instance/jump.jl | 66 +++++------ src/solvers/jump.jl | 255 ++++++++++++++++++++++--------------------- 4 files changed, 227 insertions(+), 204 deletions(-) diff --git a/src/MIPLearn.jl b/src/MIPLearn.jl index 17319b2..1fc4694 100644 --- a/src/MIPLearn.jl +++ b/src/MIPLearn.jl @@ -2,12 +2,21 @@ # Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved. # Released under the modified BSD license. See COPYING.md for more details. -__precompile__(false) module MIPLearn 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/exceptions.jl") @@ -19,12 +28,19 @@ include("solvers/learning.jl") include("solvers/macros.jl") include("utils/benchmark.jl") -DynamicLazyConstraintsComponent = miplearn.DynamicLazyConstraintsComponent -UserCutsComponent = miplearn.UserCutsComponent -ObjectiveValueComponent = miplearn.ObjectiveValueComponent -PrimalSolutionComponent = miplearn.PrimalSolutionComponent -StaticLazyConstraintsComponent = miplearn.StaticLazyConstraintsComponent -MinPrecisionThreshold = miplearn.MinPrecisionThreshold +function __init__() + copy!(miplearn, pyimport("miplearn")) + copy!(traceback, pyimport("traceback")) + copy!(DynamicLazyConstraintsComponent, miplearn.DynamicLazyConstraintsComponent) + copy!(UserCutsComponent, miplearn.UserCutsComponent) + 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, UserCutsComponent, diff --git a/src/instance/file.jl b/src/instance/file.jl index 32a3fd3..e01043b 100644 --- a/src/instance/file.jl +++ b/src/instance/file.jl @@ -3,56 +3,58 @@ # Released under the modified BSD license. See COPYING.md for more details. -@pydef mutable struct PyFileInstance <: miplearn.Instance - function __init__(self, filename) - self.filename = filename - self.loaded = nothing - self.samples = nothing - end +function __init_PyFileInstance__() + @pydef mutable struct Class <: miplearn.Instance + function __init__(self, filename) + self.filename = filename + self.loaded = nothing + self.samples = nothing + end - function to_model(self) - return self.loaded.py.to_model() - end + function to_model(self) + return self.loaded.py.to_model() + end - function get_instance_features(self) - return self.loaded.py.get_instance_features() - end + function get_instance_features(self) + return self.loaded.py.get_instance_features() + end - function get_variable_features(self, var_name) - return self.loaded.py.get_variable_features(var_name) - end + function get_variable_features(self, var_name) + return self.loaded.py.get_variable_features(var_name) + end - function get_variable_category(self, var_name) - return self.loaded.py.get_variable_category(var_name) - end + function get_variable_category(self, var_name) + return self.loaded.py.get_variable_category(var_name) + end - function get_constraint_features(self, cname) - return self.loaded.py.get_constraint_features(cname) - end + function get_constraint_features(self, cname) + return self.loaded.py.get_constraint_features(cname) + end - function get_constraint_category(self, cname) - return self.loaded.py.get_constraint_category(cname) - end + function get_constraint_category(self, cname) + return self.loaded.py.get_constraint_category(cname) + end - function load(self) - if self.loaded === nothing - self.loaded = load_instance(self.filename) - self.samples = self.loaded.py.samples + function load(self) + if self.loaded === nothing + self.loaded = load_instance(self.filename) + self.samples = self.loaded.py.samples + end end - end - function free(self) - self.loaded = nothing - self.samples = nothing - 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) + function flush(self) + self.loaded.py.samples = self.samples + save(self.filename, self.loaded) + end end + copy!(PyFileInstance, Class) end - struct FileInstance <: Instance py::PyCall.PyObject filename::AbstractString diff --git a/src/instance/jump.jl b/src/instance/jump.jl index e80cd87..71bbf3e 100644 --- a/src/instance/jump.jl +++ b/src/instance/jump.jl @@ -5,45 +5,47 @@ using JuMP 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 __init__(self, model) - init_miplearn_ext(model) - self.model = model - self.samples = [] - end - - function to_model(self) - return self.model - end + function to_model(self) + return self.model + end - function get_instance_features(self) - return self.model.ext[:miplearn][:instance_features] - end + function get_instance_features(self) + return self.model.ext[:miplearn][:instance_features] + end - function get_variable_features(self, var_name) - model = self.model - v = variable_by_name(model, var_name) - return get(model.ext[:miplearn][:variable_features], v, nothing) - end + function get_variable_features(self, var_name) + model = self.model + v = variable_by_name(model, var_name) + return get(model.ext[:miplearn][:variable_features], v, nothing) + end - function get_variable_category(self, var_name) - model = self.model - v = variable_by_name(model, var_name) - return get(model.ext[:miplearn][:variable_categories], v, nothing) - end + function get_variable_category(self, var_name) + model = self.model + v = variable_by_name(model, var_name) + return get(model.ext[:miplearn][:variable_categories], v, nothing) + end - function get_constraint_features(self, cname) - model = self.model - c = constraint_by_name(model, cname) - return get(model.ext[:miplearn][:constraint_features], c, nothing) - end + function get_constraint_features(self, cname) + model = self.model + c = constraint_by_name(model, cname) + return get(model.ext[:miplearn][:constraint_features], c, nothing) + 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) + 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 diff --git a/src/solvers/jump.jl b/src/solvers/jump.jl index b0ccddb..643b0f5 100644 --- a/src/solvers/jump.jl +++ b/src/solvers/jump.jl @@ -467,144 +467,147 @@ function get_constraints( end -@pydef mutable struct JuMPSolver <: miplearn.solvers.internal.InternalSolver - function __init__(self, optimizer_factory) - self.data = JuMPSolverData( - optimizer_factory, - Dict(), # varname_to_var - Dict(), # cname_to_constr - nothing, # instance - nothing, # model - [], # bin_vars - Dict(), # solution - [], # reduced_costs - Dict(), # dual_values - ) - end +function __init_JuMPSolver__() + @pydef mutable struct Class <: miplearn.solvers.internal.InternalSolver + function __init__(self, optimizer_factory) + self.data = JuMPSolverData( + optimizer_factory, + Dict(), # varname_to_var + Dict(), # cname_to_constr + nothing, # instance + nothing, # model + [], # bin_vars + Dict(), # solution + [], # 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)] + 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 + add_constraints( + self.data, + lhs=lhs, + rhs=cf.rhs, + senses=cf.senses, + names=cf.names, + ) 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) - lhs = cf.lhs - if lhs isa Matrix - # Undo incorrect automatic conversion performed by PyCall - lhs = [col[:] for col in eachcol(lhs)] + 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 - return are_constraints_satisfied( + + 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, - lhs=lhs, - rhs=cf.rhs, - senses=cf.senses, - tol=tol, + with_static=with_static, ) - 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, - 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", + ] - 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) - is_infeasible(self) = - is_infeasible(self.data) + remove_constraints(self, names) = + remove_constraints( + self.data, + [n for n in names], + ) - remove_constraints(self, names) = - remove_constraints( + 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, - [n for n in names], + tee=tee, + iteration_cb=iteration_cb, ) - - 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) + + solve_lp(self; tee=false) = + solve_lp(self.data, tee=tee) + end + copy!(JuMPSolver, Class) end