diff --git a/README.rst b/README.rst index d3a2116..a1e5195 100644 --- a/README.rst +++ b/README.rst @@ -84,8 +84,3 @@ Since the underlying API is quite simple and written in C and only C, `ctypes` i GLPK is packaged but I may want to make it so the user can optionally specify where the installation is on a user's computer (i.e., path to the shared library) so GLPK is not packaged with `scikit-glpk` and/or scipy. `linprog` could then presumably route the problem to the GLPK backend instead of HiGHS or the existing native python solvers. The `ctypes` wrapper is required for integrating GLPK into the Python runtime. Instead of using MPS files to communicate problems and reading solutions from files, `scipy.sparse.coo_matrix` and `numpy` arrays can be passed directly to the library. More information can be extracted from GLPK this way as well (For example, there is no way to get iteration count except by reading directly from the underlying structs. It is only ever printed to stdout, no other way to get it). - -TODO ----- - -- Several GLPK solver options (notably tolerances) not wrapped yet diff --git a/glpk/_glpk.py b/glpk/_glpk.py index c775016..3fe3137 100644 --- a/glpk/_glpk.py +++ b/glpk/_glpk.py @@ -116,13 +116,38 @@ def glpk( - ``norelax`` : standard "textbook" ratio test - ``flip`` : long-step ratio test + - tol_bnd : double + Tolerance used to check if the basic solution is primal + feasible. (Default: 1e-7). + + - tol_dj : double + Tolerance used to check if the basic solution is dual + feasible. (Default: 1e-7). + + - tol_piv : double + Tolerance used to choose eligble pivotal elements of + the simplex table. (Default: 1e-10). + + - obj_ll : double + Lower limit of the objective function. If the objective + function reaches this limit and continues decreasing, + the solver terminates the search. Used in the dual simplex + only. (Default: -DBL_MAX -- the largest finite float64). + + - obj_ul : double + Upper limit of the objective function. If the objective + function reaches this limit and continues increasing, + the solver terminates the search. Used in the dual simplex + only. (Default: +DBL_MAX -- the largest finite float64). + - presolve : bool Use presolver (assumes ``scale=True`` and ``init_basis='adv'``. Default is ``True``. - exact : bool Use simplex method based on exact arithmetic. - Default is ``False``. + Default is ``False``. If ``True``, all other + ``simplex_option`` fields are ignored. ip_options : dict Options specific to interior-pooint solver. @@ -208,13 +233,31 @@ def glpk( - ``clique`` : clique cuts - ``all`` : generate all cuts above - - gap_tol : float - Relative mip gap tolerance. - + - tol_int : float + Absolute tolerance used to check if optimal solution to the + current LP relaxation is integer feasible. + (Default: 1e-5). + - tol_obj : float + Relative tolerance used to check if the objective value in + optimal solution to the current LP relaxation is not better + than in the best known integer feasible solution. + (Default: 1e-7). + - mip_gap : float + Relative mip gap tolerance. If the relative mip gap for + currently known best integer feasiblesolution falls below + this tolerance, the solver terminates the search. This allows + obtaining suboptimal integer feasible solutions if solving the + problem to optimality takes too long time. + (Default: 0.0). - bound : float add inequality obj <= bound (minimization) or obj >= bound (maximization) to integer feasibility problem (assumes ``minisat=True``). + + Notes + ----- + In general, don't change tolerances without a detailed understanding + of their purposes. ''' # Housekeeping @@ -375,6 +418,13 @@ def glpk( 'norelax': GLPK.GLP_RT_STD, 'flip': GLPK.GLP_RT_FLIP, }[simplex_options.get('ratio', 'relax')] + smcp.tol_bnd = simplex_options.get('tol_bnd', 1e-7) + smcp.tol_dj = simplex_options.get('tol_dj', 1e-7) + smcp.tol_piv = simplex_options.get('tol_piv', 1e-10) + if simplex_options.get('obj_ll', False): + smcp.obj_ll = simplex_options['obj_ll'] + if simplex_options.get('obj_ul', False): + smcp.obj_ul = simplex_options['obj_ul'] smcp.it_lim = maxit smcp.tm_lim = timeout smcp.presolve = { @@ -528,7 +578,9 @@ def glpk( if 'clique' in cuts: iocp.clq_cuts = GLPK.GLP_ON - iocp.mip_gap = mip_options.get('gap_tol', 0.0) + iocp.tol_int = mip_options.get('tol_int', 1e-5) + iocp.tol_obj = mip_options.get('tol_obj', 1e-7) + iocp.mip_gap = mip_options.get('mip_gap', 0.0) iocp.tm_lim = timeout iocp.presolve = { True: GLPK.GLP_ON, diff --git a/setup.py b/setup.py index 840d64f..fbf51ae 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ def get_export_symbols(self, ext): setup( name='scikit-glpk', - version='0.2.0', + version='0.2.1', author='Nicholas McKibben', author_email='nicholas.bgp@gmail.com', url='https://github.com/mckib2/scikit-glpk',