From 2ffd3b4d739d929c3ab4cc49732f261c001f9408 Mon Sep 17 00:00:00 2001 From: mckib2 Date: Mon, 18 May 2020 14:59:33 -0600 Subject: [PATCH] Support for reading mps files into matrices or glp_prob structs --- glpk/__init__.py | 2 +- glpk/_glpk_defines.py | 86 ++++++++++++++++++++++++++++++++++++++++--- test_mps.py | 26 +++++++++++++ 3 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 test_mps.py diff --git a/glpk/__init__.py b/glpk/__init__.py index 1306243..0874561 100644 --- a/glpk/__init__.py +++ b/glpk/__init__.py @@ -1,4 +1,4 @@ from ._glpk import glpk +from ._utils import mpsread, mpswrite from ._glpk_defines import GLPK -from ._set_env import glpk_set_env diff --git a/glpk/_glpk_defines.py b/glpk/_glpk_defines.py index d201101..51c954f 100644 --- a/glpk/_glpk_defines.py +++ b/glpk/_glpk_defines.py @@ -1,5 +1,7 @@ import ctypes +import os +import pathlib from numpy.ctypeslib import ndpointer @@ -33,6 +35,9 @@ class glp_iptcp(ctypes.Structure): ('foo_bar', ctypes.c_double*48), ] +class glp_mpscp(ctypes.Structure): + _fields_ = [] + # LP problem structure class glp_prob(ctypes.Structure): class DMP(ctypes.Structure): @@ -191,14 +196,20 @@ class GLPK: GLP_ORD_AMD = 2 GLP_ORD_SYMAMD = 3 - def __init__(self, libpath): + # MPS formats + GLP_MPS_DECK = 1 # fixed (ancient) + GLP_MPS_FILE = 2 # free (modern) - #glp_prob = self.glp_prob - #glp_smcp = self.glp_smcp + def __init__(self, libpath): - # Load the shared library - #so = '/home/nicholas/Downloads/glpk-4.65/src/.libs/libglpk.so' + # Load the shared library; + # Windows Octave installs require that we load from + # the same directory as libglpk.dll is found + cwd = os.getcwd() + os.chdir(pathlib.Path(libpath).parent) _lib = ctypes.cdll.LoadLibrary(libpath) + os.chdir(cwd) + _lib.glp_version.restype = ctypes.c_char_p _lib.glp_create_prob.restype = ctypes.POINTER(glp_prob) @@ -277,6 +288,9 @@ def __init__(self, libpath): _lib.glp_get_obj_coef.restype = ctypes.c_double _lib.glp_get_obj_coef.argtypes = [ctypes.POINTER(glp_prob), ctypes.c_int] + _lib.glp_get_num_cols.restype = ctypes.c_int + _lib.glp_get_num_cols.argtypes = [ctypes.POINTER(glp_prob)] + _lib.glp_get_col_name.restype = ctypes.c_char_p _lib.glp_get_col_name.argtypes = [ctypes.POINTER(glp_prob), ctypes.c_int] @@ -286,6 +300,50 @@ def __init__(self, libpath): _lib.glp_get_num_nz.restype = ctypes.c_int _lib.glp_get_num_nz.argtypes = [ctypes.POINTER(glp_prob)] + _lib.glp_get_mat_row.restype = ctypes.c_int # len of col indices and values + _lib.glp_get_mat_row.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # ith row + ctypes.POINTER(ctypes.c_int), # col indices + ctypes.POINTER(ctypes.c_double), # values + ] + + _lib.glp_get_row_type.restype = ctypes.c_int + _lib.glp_get_row_type.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # ith row + ] + + _lib.glp_get_row_lb.restype = ctypes.c_double + _lib.glp_get_row_lb.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # ith row + ] + + _lib.glp_get_row_ub.restype = ctypes.c_double + _lib.glp_get_row_ub.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # ith row + ] + + _lib.glp_get_col_type.restype = ctypes.c_int + _lib.glp_get_col_type.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # jth col + ] + + _lib.glp_get_col_lb.restype = ctypes.c_double + _lib.glp_get_col_lb.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # jth col + ] + + _lib.glp_get_col_ub.restype = ctypes.c_double + _lib.glp_get_col_ub.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # jth col + ] + _lib.glp_get_status.restype = ctypes.c_int _lib.glp_get_status.argtypes = [ctypes.POINTER(glp_prob)] @@ -346,5 +404,23 @@ def __init__(self, libpath): _lib.glp_delete_prob.restype = None _lib.glp_delete_prob.argtypes = [ctypes.POINTER(glp_prob)] + # Utility + _lib.glp_read_mps.restype = ctypes.c_int + _lib.glp_read_mps.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # format (GLP_MPS_DECK or GLP_MPS_FILE) + ctypes.POINTER(glp_mpscp), # should be NULL + ctypes.c_char_p, # filename + ] + + _lib.glp_write_mps.restype = ctypes.c_int + _lib.glp_write_mps.argtypes = [ + ctypes.POINTER(glp_prob), + ctypes.c_int, # format (GLP_MPS_DECK or GLP_MPS_FILE) + ctypes.POINTER(glp_mpscp), # should be NULL + ctypes.c_char_p, # filename + ] + + # Make accessible for front-end interface self._lib = _lib diff --git a/test_mps.py b/test_mps.py new file mode 100644 index 0000000..c1a31eb --- /dev/null +++ b/test_mps.py @@ -0,0 +1,26 @@ +'''Example showing how to use mpsread/write.''' + +from scipy.optimize import linprog + +from glpk import mpsread + +if __name__ == '__main__': + + libpath = '/home/nicholas/Downloads/glpk-4.65/src/.libs/libglpk.so' + + filename = '~/Documents/HiGHS/check/instances/25fv47.mps' + + lp = mpsread(filename, libpath=libpath) + c, A_ub, b_ub, A_eq, b_eq, bounds = lp + + if A_ub is not None: + print(A_ub.nnz) + A_ub = A_ub.todense() + print(A_ub) + print(len(b_ub), c.shape) + print(A_ub.shape) + if A_eq is not None: + A_eq = A_eq.todense() + + res = linprog(c, A_ub, b_ub, A_eq, b_eq, bounds) + print(res)