Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to GLPK configuration for MIPs #231

Merged
merged 2 commits into from
Mar 27, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions src/optlang/glpk_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


"""
Interface for the GNU Linear Programming Kit (GLPK)
Interface for the GNU Linear Programming Kit (GLPK).

Wraps the GLPK solver by subclassing and extending :class:`Model`,
:class:`Variable`, and :class:`Constraint` from :mod:`interface`.
Expand All @@ -26,16 +26,13 @@
"""
import logging

import os
import six

from optlang.util import inheritdocstring, TemporaryFilename
from optlang.expression_parsing import parse_optimization_expression
from optlang import interface
from optlang import symbolics

log = logging.getLogger(__name__)

from swiglpk import glp_find_col, glp_get_col_prim, glp_get_col_dual, GLP_CV, GLP_IV, GLP_BV, GLP_UNDEF, GLP_FEAS, \
GLP_INFEAS, GLP_NOFEAS, GLP_OPT, GLP_UNBND, \
glp_set_col_kind, glp_find_row, glp_get_row_prim, glp_get_row_dual, glp_get_obj_val, glp_set_obj_dir, glp_init_smcp, \
Expand All @@ -46,10 +43,9 @@
glp_set_col_name, intArray, glp_del_cols, glp_add_rows, glp_set_row_name, doubleArray, glp_write_lp, glp_write_prob, \
glp_set_mat_row, glp_set_col_bnds, glp_set_row_bnds, GLP_FR, GLP_UP, GLP_LO, GLP_FX, GLP_DB, glp_del_rows, \
glp_get_mat_row, glp_get_row_ub, glp_get_row_type, glp_get_row_lb, glp_get_row_name, glp_get_obj_coef, \
glp_get_obj_dir, glp_scale_prob, GLP_SF_AUTO, glp_get_num_int, glp_get_num_bin, glp_mip_col_val, \
glp_get_obj_dir, glp_scale_prob, GLP_SF_AUTO, glp_get_num_int, glp_mip_col_val, \
glp_mip_obj_val, glp_mip_status, GLP_ETMLIM, glp_adv_basis, glp_read_lp, glp_mip_row_val, \
get_col_primals, get_col_duals, get_row_primals, get_row_duals, glp_delete_prob

get_col_primals, get_col_duals, get_row_primals, get_row_duals


_GLPK_STATUS_TO_STATUS = {
Expand All @@ -71,6 +67,8 @@
[(val, key) for key, val in six.iteritems(_GLPK_VTYPE_TO_VTYPE)]
)

log = logging.getLogger(__name__)


def _glpk_validate_id(name):
if name is None:
Expand Down Expand Up @@ -152,7 +150,6 @@ def name(self, value):
glp_set_col_name(self.problem.problem, glp_find_col(self.problem.problem, old_name), str(value))



@six.add_metaclass(inheritdocstring)
class Constraint(interface.Constraint):
_INDICATOR_CONSTRAINT_SUPPORT = False
Expand Down Expand Up @@ -376,7 +373,6 @@ def get_linear_coefficients(self, variables):
raise Exception("Can't get coefficients from solver if objective is not in a model")



@six.add_metaclass(inheritdocstring)
class Configuration(interface.MathematicalProgrammingConfiguration):
def __init__(self, presolve="auto", verbosity=0, timeout=None, *args, **kwargs):
Expand Down Expand Up @@ -414,7 +410,7 @@ def __setstate__(self, state):

def _set_presolve(self, value):
self._smcp.presolve = {False: GLP_OFF, True: GLP_ON, "auto": GLP_OFF}[value]
self._iocp.presolve = {False: GLP_OFF, True: GLP_ON, "auto": GLP_OFF}[value]
self._iocp.presolve = {False: GLP_OFF, True: GLP_ON, "auto": GLP_ON}[value]

def _set_verbosity(self, value):
if value == 0:
Expand Down Expand Up @@ -453,10 +449,17 @@ def _get_feasibility(self):
def _set_feasibility(self, value):
return setattr(self._smcp, "tol_bnd", value)

def _get_integrality(self):
return getattr(self._iocp, "tol_int")

def _set_integrality(self, value):
return setattr(self._iocp, "tol_int", value)

def _tolerance_functions(self):
return {
"feasibility": (self._get_feasibility, self._set_feasibility)
}
"feasibility": (self._get_feasibility, self._set_feasibility),
"integrality": (self._get_integrality, self._set_integrality)
}

@property
def presolve(self):
Expand Down Expand Up @@ -599,14 +602,17 @@ def __setstate__(self, repr_dict):
if code != 0:
with open(tmp_file_name) as tmp_file:
invalid_problem = tmp_file.read()
raise Exception("The GLPK file " + tmp_file_name + " does not seem to contain a valid GLPK problem:\n\n" + invalid_problem)
raise Exception("The GLPK file " + tmp_file_name +
" does not seem to contain a valid GLPK problem:\n\n" +
invalid_problem)
self.__init__(problem=problem)
self.configuration = Configuration.clone(repr_dict['config'], problem=self)
if repr_dict['glpk_status'] == 'optimal':
self.optimize() # since the start is an optimal solution, nothing will happen here

# def __del__(self): # To make sure that the glpk problem is deleted when this is garbage collected
# Gotcha: When objects with a __del__ method are part of a referencing cycle, the entire cycle is never automatically garbage collected
# Gotcha: When objects with a __del__ method are part of a referencing cycle, the entire
# cycle is never automatically garbage collected
# glp_delete_prob(self.problem)

@property
Expand Down