<commit_before># distutils: libraries=glpk
# cython: embedsignature=True

from glpk cimport *
from libc.stdlib cimport malloc, free
from cpython cimport bool
from cpython.version cimport PY_MAJOR_VERSION

from tempfile import NamedTemporaryFile as _NamedTemporaryFile  # for pickling
from os import unlink as _unlink
from warnings import warn as _warn

__glpk_version__ = str(glp_version())
_SUPPORTS_MILP = True
solver_name = "cglpk"

__doc__ = """Bindings to GLPK

The GNU Linear Programming Kit (GLPK) is released under the GPL.
The source can be downloaded from http://www.gnu.org/software/glpk/

The GNU Multiple Precision Arithmetic Library (GMP) is released under the GPL.
The source can be downloaded from https://gmplib.org/

"""

cdef dict ERROR_CODES = {
    GLP_EBADB: "GLP_EBADB",
    GLP_ESING: "GLP_ESING",
    GLP_ECOND: "GLP_ECOND",
    GLP_EBOUND: "GLP_EBOUND",
    GLP_EFAIL: "GLP_EFAIL",
    GLP_EOBJLL: "GLP_EOBJLL",
    GLP_EOBJUL: "GLP_EOBJUL",
    GLP_EITLIM: "GLP_EITLIM",
    GLP_ETMLIM: "GLP_ETMLIM",
    GLP_ENOPFS: "GLP_ENOPFS",
    GLP_ENODFS: "GLP_ENODFS",
    GLP_EROOT: "GLP_EROOT",
    GLP_ESTOP: "GLP_ESTOP",
    GLP_EMIPGAP: "GLP_EMIPGAP",
    GLP_ENOFEAS: "GLP_ENOFEAS",
    GLP_ENOCVG: "GLP_ENOCVG",
    GLP_EINSTAB: "GLP_EINSTAB",
    GLP_EDATA: "GLP_EDATA",
    GLP_ERANGE: "GLP_ERANGE"
}

cdef dict ERROR_MESSAGES = {
    GLP_EBADB: "invalid basis",
    GLP_ESING: "singular matrix",
    GLP_ECOND: "ill-conditioned matrix",
    GLP_EBOUND: "invalid bounds",
    GLP_EFAIL: "solver failed",
    GLP_EOBJLL: "objective lower limit reached",
    GLP_EOBJUL: "objective upper limit reached",
    GLP_EITLIM: "iteration limit exceeded",
    GLP_ETMLIM: "time limit exceeded",
    GLP_ENOPFS: "no primal feasible solution",
    GLP_ENODFS: "no dual feasible solution",
    GLP_EROOT: "root LP optimum not provided",
    GLP_ESTOP: "search terminated by application",
    GLP_EMIPGAP: "relative mip gap tolerance reached",
    GLP_ENOFEAS: "no primal/dual feasible solution",
    GLP_ENOCVG: "no convergence",
    GLP_EINSTAB: "numerical instability",
    GLP_EDATA: "invalid data",
    GLP_ERANGE: "result out of range"
}

cdef dict METHODS = {
    "auto": GLP_DUALP,
    "primal": GLP_PRIMAL,
    "dual": GLP_DUAL
}


cdef int downcast_pos_size(Py_ssize_t size):
    if size > INT_MAX:
        raise ValueError("Integer overflow %d > %d" % (size, INT_MAX))
    else:
        return <int> size


cdef check_error(int result):
    if result == 0:
        return
    if result not in ERROR_CODES:
        raise RuntimeError("glp_simplex failed with unknown error code 0x%x" %
                           result)
    raise RuntimeError("glp_simplex failed with error code %s: %s" %
                       (ERROR_CODES[result], ERROR_MESSAGES[result]))


# Do not want to print out to terminal. Even when not verbose, output
# will be redirected through the hook.
glp_term_out(GLP_OFF)


cdef int hook(void *info, const char *s):
    """function to redirect sdout to python stdout"""
    print(s)
    return 1


cdef class GLP:
    cdef glp_prob *glp
    cdef glp_smcp parameters
    cdef glp_iocp integer_parameters
    cdef public bool exact

    # cython related allocation/dellocation functions
    def __cinit__(self):
        self.glp = glp_create_prob()
        glp_set_obj_dir(self.glp, GLP_MAX)  # default is maximize
        glp_init_smcp(&self.parameters)
        glp_init_iocp(&self.integer_parameters)
        self.exact = False

    def __dealloc__(self):
        glp_delete_prob(self.glp)

    def __init__(self, cobra_model=None):
        cdef int bound_type, index, n, n_values
        cdef glp_prob *glp
        cdef int *c_rows
        cdef int *c_cols
        cdef double *c_values
        
        # initialize parameters
        self.parameters.msg_lev = GLP_MSG_OFF
        glp_term_hook(NULL, NULL)

        if cobra_model is None:
            return
        glp = self.glp
        glp_add_rows(glp, downcast_pos_size(len(cobra_model.metabolites)))
        glp_add_cols(glp, downcast_pos_size(len(cobra_model.reactions)))

        metabolite_id_to_index = {r.id: index for index, r
                                  in enumerate(cobra_model.metabolites, 1)}

        linear_constraint_rows = []
        linear_constraint_cols = []
        linear_constraint_values = []

        # set metabolite/consraint bounds
        for index, metabolite in enumerate(cobra_model.metabolites, 1):
            b = float(metabolite._bound)
            c = metabolite._constraint_sense
            if c == 'E':
                bound_type = GLP_FX  # Set metabolite to steady state levels
            elif c == 'L':
                bound_type = GLP_UP  # x < 2 <==> x has an upper bound of 2
            elif c == 'G':
                bound_type = GLP_LO  # x > 2 <==> x has a lower bound of 2
            else:
                raise ValueError("unsupported bound type: %s" % c)
            glp_set_row_bnds(glp, index, bound_type, b, b)
        
        # set reaction/varaiable bounds
        for index, reaction in enumerate(cobra_model.reactions, 1):
            if reaction.variable_kind == "integer":
                if reaction.lower_bound == 0 and reaction.upper_bound == 1:
                    glp_set_col_kind(self.glp, index, GLP_BV)  # binary
                else:
                    glp_set_col_kind(self.glp, index, GLP_IV)
            if reaction.lower_bound == reaction.upper_bound:
                bound_type = GLP_FX
            else:
                bound_type = GLP_DB
            glp_set_col_bnds(glp, index, bound_type,
                             float(reaction.lower_bound),
                             float(reaction.upper_bound))
            glp_set_obj_coef(glp, index, float(reaction.objective_coefficient))

            for metabolite, coefficient in reaction._metabolites.iteritems():
                metabolite_index = metabolite_id_to_index[metabolite.id]
                linear_constraint_rows.append(metabolite_index)
                linear_constraint_cols.append(index)
                linear_constraint_values.append(coefficient)
        
        # set constraint marix
        # first copy the python lists to c arrays
        n_values = downcast_pos_size(len(linear_constraint_rows))
        c_cols = <int *> malloc((n_values + 1) * sizeof(int))
        c_rows = <int *> malloc((n_values + 1) * sizeof(int))
        c_values = <double *> malloc((n_values + 1) * sizeof(double))
        if c_rows is NULL or c_rows is NULL or c_values is NULL:
            raise MemoryError()
        for index in range(n_values):
            c_rows[index + 1] = linear_constraint_rows[index]
            c_cols[index + 1] = linear_constraint_cols[index]
            c_values[index + 1] = float(linear_constraint_values[index])
        # actually set the values
        glp_load_matrix(glp, n_values, c_rows, c_cols, c_values)
        # free the c arrays
        free(c_rows)
        free(c_cols)
        free(c_values)

    # problem creation and modification
    @classmethod
    def create_problem(cls, cobra_model, objective_sense="maximize"):
        problem = cls(cobra_model)
        problem.set_objective_sense(objective_sense)
        return problem

    cpdef change_variable_bounds(self, int index, double lower_bound,
                                 double upper_bound):
        cdef int bound_type = GLP_FX if lower_bound == upper_bound else GLP_DB
        assert index >= 0
        glp_set_col_bnds(self.glp, index + 1, bound_type, lower_bound, upper_bound)

    def change_coefficient(self, int met_index, int rxn_index, double value):
        cdef int col_length, i
        cdef int *indexes
        cdef double *values<commit_msg>skip symbolic assignments in cglpk lp creation<commit_after># distutils: libraries=glpk
# cython: embedsignature=True

from glpk cimport *
from libc.stdlib cimport malloc, free
from cpython cimport bool
from cpython.version cimport PY_MAJOR_VERSION

from tempfile import NamedTemporaryFile as _NamedTemporaryFile  # for pickling
from os import unlink as _unlink
from warnings import warn as _warn

try:
    from sympy import Basic, Number
except:
    class Basic:
        pass
    Number = Basic

__glpk_version__ = str(glp_version())
_SUPPORTS_MILP = True
solver_name = "cglpk"

__doc__ = """Bindings to GLPK

The GNU Linear Programming Kit (GLPK) is released under the GPL.
The source can be downloaded from http://www.gnu.org/software/glpk/

The GNU Multiple Precision Arithmetic Library (GMP) is released under the GPL.
The source can be downloaded from https://gmplib.org/

"""

cdef dict ERROR_CODES = {
    GLP_EBADB: "GLP_EBADB",
    GLP_ESING: "GLP_ESING",
    GLP_ECOND: "GLP_ECOND",
    GLP_EBOUND: "GLP_EBOUND",
    GLP_EFAIL: "GLP_EFAIL",
    GLP_EOBJLL: "GLP_EOBJLL",
    GLP_EOBJUL: "GLP_EOBJUL",
    GLP_EITLIM: "GLP_EITLIM",
    GLP_ETMLIM: "GLP_ETMLIM",
    GLP_ENOPFS: "GLP_ENOPFS",
    GLP_ENODFS: "GLP_ENODFS",
    GLP_EROOT: "GLP_EROOT",
    GLP_ESTOP: "GLP_ESTOP",
    GLP_EMIPGAP: "GLP_EMIPGAP",
    GLP_ENOFEAS: "GLP_ENOFEAS",
    GLP_ENOCVG: "GLP_ENOCVG",
    GLP_EINSTAB: "GLP_EINSTAB",
    GLP_EDATA: "GLP_EDATA",
    GLP_ERANGE: "GLP_ERANGE"
}

cdef dict ERROR_MESSAGES = {
    GLP_EBADB: "invalid basis",
    GLP_ESING: "singular matrix",
    GLP_ECOND: "ill-conditioned matrix",
    GLP_EBOUND: "invalid bounds",
    GLP_EFAIL: "solver failed",
    GLP_EOBJLL: "objective lower limit reached",
    GLP_EOBJUL: "objective upper limit reached",
    GLP_EITLIM: "iteration limit exceeded",
    GLP_ETMLIM: "time limit exceeded",
    GLP_ENOPFS: "no primal feasible solution",
    GLP_ENODFS: "no dual feasible solution",
    GLP_EROOT: "root LP optimum not provided",
    GLP_ESTOP: "search terminated by application",
    GLP_EMIPGAP: "relative mip gap tolerance reached",
    GLP_ENOFEAS: "no primal/dual feasible solution",
    GLP_ENOCVG: "no convergence",
    GLP_EINSTAB: "numerical instability",
    GLP_EDATA: "invalid data",
    GLP_ERANGE: "result out of range"
}

cdef dict METHODS = {
    "auto": GLP_DUALP,
    "primal": GLP_PRIMAL,
    "dual": GLP_DUAL
}


cdef int downcast_pos_size(Py_ssize_t size):
    if size > INT_MAX:
        raise ValueError("Integer overflow %d > %d" % (size, INT_MAX))
    else:
        return <int> size


cdef check_error(int result):
    if result == 0:
        return
    if result not in ERROR_CODES:
        raise RuntimeError("glp_simplex failed with unknown error code 0x%x" %
                           result)
    raise RuntimeError("glp_simplex failed with error code %s: %s" %
                       (ERROR_CODES[result], ERROR_MESSAGES[result]))


# Do not want to print out to terminal. Even when not verbose, output
# will be redirected through the hook.
glp_term_out(GLP_OFF)


cdef int hook(void *info, const char *s):
    """function to redirect sdout to python stdout"""
    print(s)
    return 1

cdef double _to_double(value):
    if isinstance(value, Basic) and not isinstance(value, Number):
        return 0.
    else:
        return <double>value


cdef class GLP:
    cdef glp_prob *glp
    cdef glp_smcp parameters
    cdef glp_iocp integer_parameters
    cdef public bool exact

    # cython related allocation/dellocation functions
    def __cinit__(self):
        self.glp = glp_create_prob()
        glp_set_obj_dir(self.glp, GLP_MAX)  # default is maximize
        glp_init_smcp(&self.parameters)
        glp_init_iocp(&self.integer_parameters)
        self.exact = False

    def __dealloc__(self):
        glp_delete_prob(self.glp)

    def __init__(self, cobra_model=None):
        cdef int bound_type, index, n, n_values
        cdef glp_prob *glp
        cdef int *c_rows
        cdef int *c_cols
        cdef double *c_values
        cdef double b
        
        # initialize parameters
        self.parameters.msg_lev = GLP_MSG_OFF
        glp_term_hook(NULL, NULL)

        if cobra_model is None:
            return
        glp = self.glp
        glp_add_rows(glp, downcast_pos_size(len(cobra_model.metabolites)))
        glp_add_cols(glp, downcast_pos_size(len(cobra_model.reactions)))

        metabolite_id_to_index = {r.id: index for index, r
                                  in enumerate(cobra_model.metabolites, 1)}

        linear_constraint_rows = []
        linear_constraint_cols = []
        linear_constraint_values = []

        # set metabolite/consraint bounds
        for index, metabolite in enumerate(cobra_model.metabolites, 1):
            b = _to_double(metabolite._bound)
            c = metabolite._constraint_sense
            if c == 'E':
                bound_type = GLP_FX  # Set metabolite to steady state levels
            elif c == 'L':
                bound_type = GLP_UP  # x < 2 <==> x has an upper bound of 2
            elif c == 'G':
                bound_type = GLP_LO  # x > 2 <==> x has a lower bound of 2
            else:
                raise ValueError("unsupported bound type: %s" % c)
            glp_set_row_bnds(glp, index, bound_type, b, b)
        
        # set reaction/varaiable bounds
        for index, reaction in enumerate(cobra_model.reactions, 1):
            if reaction.variable_kind == "integer":
                if reaction.lower_bound == 0 and reaction.upper_bound == 1:
                    glp_set_col_kind(self.glp, index, GLP_BV)  # binary
                else:
                    glp_set_col_kind(self.glp, index, GLP_IV)
            if reaction.lower_bound == reaction.upper_bound:
                bound_type = GLP_FX
            else:
                bound_type = GLP_DB
            glp_set_col_bnds(glp, index, bound_type,
                             _to_double(reaction.lower_bound),
                             _to_double(reaction.upper_bound))
            glp_set_obj_coef(glp, index,
                             _to_double(reaction.objective_coefficient))

            for metabolite, coefficient in reaction._metabolites.iteritems():
                linear_constraint_rows.append(
                    metabolite_id_to_index[metabolite.id])
                linear_constraint_cols.append(index)
                linear_constraint_values.append(coefficient)
        
        # set constraint marix
        # first copy the python lists to c arrays
        n_values = downcast_pos_size(len(linear_constraint_rows))
        c_cols = <int *> malloc((n_values + 1) * sizeof(int))
        c_rows = <int *> malloc((n_values + 1) * sizeof(int))
        c_values = <double *> malloc((n_values + 1) * sizeof(double))
        if c_rows is NULL or c_rows is NULL or c_values is NULL:
            raise MemoryError()
        for index in range(n_values):
            c_rows[index + 1] = linear_constraint_rows[index]
            c_cols[index + 1] = linear_constraint_cols[index]
            c_values[index + 1] = _to_double(linear_constraint_values[index])
        # actually set the values
        glp_load_matrix(glp, n_values, c_rows, c_cols, c_values)
        # free the c arrays
        free(c_rows)
        free(c_cols)
        free(c_values)

    # problem creation and modification
    @classmethod
    def create_problem(cls, cobra_model, objective_sense="maximize"):
        problem = cls(cobra_model)
        problem.set_objective_sense(objective_sense)
        return problem

    cpdef change_variable_bounds(self, int index, double lower_bound,
                                 double upper_bound):
        cdef int bound_type = GLP_FX if lower_bound == upper_bound else GLP_DB
        assert index >= 0
        glp_set_col_bnds(self.glp, index + 1, bound_type, lower_bound, upper_bound)

    def change_coefficient(self, int met_index, int rxn_index, double value):
        cdef int col_length, i
        cdef int *indexes
        cdef double *values
{'fraction_non_alphanumeric': 0.08278839438765792, 'fraction_numerical': 0.0026030093327407784, 'mean_word_length': 2.5670289855072466, 'pattern_counts': {'":': 8, '<': 18, '<?xml version=': 0, '>': 24, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5488497', 'n_tokens_mistral': 5399, 'n_tokens_neox': 5168, 'n_words': 1308}