Example #1
0
File: glpk.py Project: jonls/psamm
 def status(self):
     """Return string indicating the error encountered on failure."""
     self._check_valid()
     if self._ret_val == swiglpk.GLP_ENOPFS:
         return 'No primal feasible solution'
     elif self._ret_val == swiglpk.GLP_ENODFS:
         return 'No dual feasible solution'
     return str(swiglpk.glp_get_status(self._problem._p))
Example #2
0
 def status(self):
     """Return string indicating the error encountered on failure."""
     self._check_valid()
     if self._ret_val == swiglpk.GLP_ENOPFS:
         return 'No primal feasible solution'
     elif self._ret_val == swiglpk.GLP_ENODFS:
         return 'No dual feasible solution'
     return str(swiglpk.glp_get_status(self._problem._p))
Example #3
0
    def _process_simplex_result(self, simplex_res, columns):
        '''process the result of a glp_simplex call

        returns None on UNSAT, otherwise the optimization result with the requested columns
        if columns is None, will return full result
        '''

        rv = None

        if simplex_res == glpk.GLP_ENOPFS:  # no primal feasible w/ presolver
            rv = None
        elif simplex_res != 0: # simplex failed, report the error
            raise RuntimeError("glp_simplex returned nonzero status ({}): {}".format(
                simplex_res, LpInstance.get_simplex_error_string(simplex_res)))
        else:
            status = glpk.glp_get_status(self.lp)

            if status == glpk.GLP_NOFEAS: # infeasible
                rv = None
            elif status == glpk.GLP_OPT: # optimal
                lp_cols = self.get_num_cols()
                
                if columns is None:
                    rv = np.zeros(lp_cols)
                else:
                    rv = np.zeros(len(columns))

                # copy the output vars
                rv_len = len(rv)
                
                for i in range(rv_len):
                    col = i if columns is None else columns[i]

                    assert 0 <= col < lp_cols, "out of bounds column requested in LP solution: {}".format(col)

                    rv[i] = glpk.glp_get_col_prim(self.lp, int(col + 1))

            else: # neither infeasible nor optimal (for example, unbounded)
                codes = [glpk.GLP_OPT, glpk.GLP_FEAS, glpk.GLP_INFEAS, glpk.GLP_NOFEAS, glpk.GLP_UNBND, glpk.GLP_UNDEF]
                msgs = ["solution is optimal",
                        "solution is feasible",
                        "solution is infeasible",
                        "problem has no feasible solution",
                        "problem has unbounded solution",
                        "solution is undefined"]

                if status == glpk.GLP_UNBND:
                    ray = glpk.glp_get_unbnd_ray(self.lp)

                    raise RuntimeError(f"LP had unbounded solution in minimize(). Unbounded ray was variable #{ray}")

                for code, message in zip(codes, msgs):
                    if status == code:
                        raise RuntimeError("LP status after solving in minimize() was '{}': {}".format(message, code))

                raise RuntimeError("LP status after solving in minimize() was <Unknown>: {}".format(status))

        return rv
Example #4
0
    def _process_simplex_result(self, simplex_res):
        '''process the result of a glp_simplex call

        returns None on UNSAT, otherwise the optimization result with the requested columns
        if columns is None, will return full result
        '''

        rv = None

        if simplex_res != glpk.GLP_ENOPFS:  # skip if no primal feasible w/ presolver

            if simplex_res != 0:  # simplex failed, report the error
                raise RuntimeError(
                    "glp_simplex returned nonzero status ({}): {}".format(
                        simplex_res,
                        LpInstance.get_simplex_error_string(simplex_res)))

            status = glpk.glp_get_status(self.lp)

            if status == glpk.GLP_NOFEAS:  # infeasible
                rv = None
            elif status == glpk.GLP_OPT:  # optimal
                lp_cols = self.get_num_cols()
                rv = np.zeros(lp_cols)

                for col in range(lp_cols):
                    rv[col] = glpk.glp_get_col_prim(self.lp, int(1 + col))

            else:  # neither infeasible nor optimal (for example, unbounded)
                error_msg = "<Unknown Status>"

                codes = [
                    glpk.GLP_OPT, glpk.GLP_FEAS, glpk.GLP_INFEAS,
                    glpk.GLP_NOFEAS, glpk.GLP_UNBND, glpk.GLP_UNDEF
                ]
                msgs = [
                    "solution is optimal", "solution is feasible",
                    "solution is infeasible",
                    "problem has no feasible solution",
                    "problem has unbounded solution", "solution is undefined"
                ]

                for code, message in zip(codes, msgs):
                    if status == code:
                        error_msg = message
                        break

                if status == glpk.GLP_UNBND:
                    ray = glpk.glp_get_unbnd_ray(self.lp)
                    error_msg += f"; unbounded ray was variable #{ray}"

                raise RuntimeError(
                    f"LP status after minimize() was {status}: {error_msg}")

        return rv
Example #5
0
 def _run_glp_simplex(self):
     return_value = glp_simplex(self.problem, self.configuration._smcp)
     glpk_status = glp_get_status(self.problem)
     if return_value == 0:
         status = _GLPK_STATUS_TO_STATUS[glpk_status]
     elif return_value == GLP_ETMLIM:
         status = interface.TIME_LIMIT
     else:
         status = _GLPK_STATUS_TO_STATUS[glpk_status]
         if status == interface.UNDEFINED:
             log.debug("Status undefined. GLPK status code returned by glp_simplex was %d" % return_value)
     return status
Example #6
0
 def _run_glp_simplex(self):
     return_value = glp_simplex(self.problem, self.configuration._smcp)
     glpk_status = glp_get_status(self.problem)
     if return_value == 0:
         status = _GLPK_STATUS_TO_STATUS[glpk_status]
     elif return_value == GLP_ETMLIM:
         status = interface.TIME_LIMIT
     else:
         status = _GLPK_STATUS_TO_STATUS[glpk_status]
         if status == interface.UNDEFINED:
             log.debug("Status undefined. GLPK status code returned by glp_simplex was %d" % return_value)
     return status
Example #7
0
 def check_objval(glpk_problem, model_objval):
     """
     Check that ...
     """
     smcp = glp_smcp()
     smcp.presolve = True
     glp_simplex(glpk_problem, None)
     status = glp_get_status(glpk_problem)
     if status == GLP_OPT:
         glpk_problem_objval = glp_get_obj_val(glpk_problem)
     else:
         glpk_problem_objval = None
     nose.tools.assert_almost_equal(glpk_problem_objval, model_objval, places=4)
Example #8
0
    def _solve(self):
        import swiglpk as glpk

        # An alias to the internal problem instance.
        p = self.int

        continuous = self.ext.is_continuous()

        # Select LP solver (Simplex or Interior Point Method).
        if continuous:
            if self.ext.options["lp_root_method"] == "interior":
                interior = True
            else:
                # Default to Simplex.
                interior = False
            simplex = not interior
        else:
            simplex = interior = False

        # Select appropriate options container.
        if simplex:
            options = glpk.glp_smcp()
            glpk.glp_init_smcp(options)
        elif interior:
            options = glpk.glp_iptcp()
            glpk.glp_init_iptcp(options)
        else:
            options = glpk.glp_iocp()
            glpk.glp_init_iocp(options)

        # Handle "verbose" option.
        verbosity = self.verbosity()
        if verbosity < 0:
            options.msg_lev = glpk.GLP_MSG_OFF
        elif verbosity == 0:
            options.msg_lev = glpk.GLP_MSG_ERR
        elif verbosity == 1:
            options.msg_lev = glpk.GLP_MSG_ON
        elif verbosity >= 2:
            options.msg_lev = glpk.GLP_MSG_ALL

        # Handle "tol" option.
        # Note that GLPK knows three different tolerances for Simplex but none
        # for the Interior Point Method, while PICOS states that "tol" is meant
        # only for the IPM.
        # XXX: The option is unsupported but does not default to None, so we
        #      cannot warn the user.
        pass

        # Handle "maxit" option.
        if not simplex:
            self._handle_unsupported_option("maxit",
                "GLPK supports the 'maxit' option only with Simplex.")
        elif self.ext.options["maxit"] is not None:
                options.it_lim = int(self.ext.options["maxit"])

        # Handle "lp_root_method" option.
        # Note that the PICOS option is explicitly also meant for the MIP
        # preprocessing step but GLPK does not support it in that scenario.
        if not continuous:
            self._handle_unsupported_option("lp_root_method",
                "GLPK supports the 'lp_root_method' option only for LPs.")
        elif self.ext.options["lp_root_method"] is not None:
            if self.ext.options["lp_root_method"] == "interior":
                # Handled above.
                pass
            elif self.ext.options["lp_root_method"] == "psimplex":
                assert simplex
                options.meth = glpk.GLP_PRIMAL
            elif self.ext.options["lp_root_method"] == "dsimplex":
                assert simplex
                options.meth = glpk.GLP_DUAL
            else:
                self._handle_bad_option_value("lp_root_method")

        # Handle "timelimit" option.
        if interior:
            self._handle_unsupported_option("timelimit",
                "GLPK does not support the 'timelimit' option with the "
                "Interior Point Method.")
        elif self.ext.options["timelimit"] is not None:
            options.tm_lim = 1000 * int(self.ext.options["timelimit"])

        # Handle "gaplim" option.
        # Note that the option is silently ignored if passed alongside an LP;
        # while the solver does not allow us to pass the option in that case, it
        # is still technically a valid option as every LP is also a MIP.
        # TODO: Find out if "mip_gap" is really equivalent to "gaplim".
        if self.ext.options["gaplim"] is not None:
            if not continuous:
                options.mip_gap = float(self.ext.options["gaplim"])

        # Handle unsupported options.
        self._handle_unsupported_options(
            "lp_node_method", "treememory", "nbsol", "hotstart")

        # TODO: Add GLPK-sepcific options. Candidates are:
        #       For both Simplex and MIPs:
        #           tol_*, out_*
        #       For Simplex:
        #           pricing, r_test, obj_*
        #       For the Interior Point Method:
        #           ord_alg
        #       For MIPs:
        #           *_tech, *_heur, ps_tm_lim, *_cuts, cb_size, binarize

        # Attempt to solve the problem.
        with self._header():
            with self._stopwatch():
                if simplex:
                    # TODO: Support glp_exact.
                    error = glpk.glp_simplex(p, options)
                elif interior:
                    error = glpk.glp_interior(p, options)
                else:
                    options.presolve = glpk.GLP_ON
                    error = glpk.glp_intopt(p, options)

            # Conert error codes to text output.
            # Note that by printing it above the footer, this output is made to
            # look like it's coming from GLPK, which is technically wrong but
            # semantically correct.
            if error == glpk.GLP_EBADB:
                self._warn("Unable to start the search, because the initial "
                    "basis specified in the problem object is invalid.")
            elif error == glpk.GLP_ESING:
                self._warn("Unable to start the search, because the basis "
                    "matrix corresponding to the initial basis is singular "
                    "within the working precision.")
            elif error == glpk.GLP_ECOND:
                self._warn("Unable to start the search, because the basis "
                    "matrix corresponding to the initial basis is "
                    "ill-conditioned.")
            elif error == glpk.GLP_EBOUND:
                self._warn("Unable to start the search, because some double-"
                    "bounded variables have incorrect bounds.")
            elif error == glpk.GLP_EFAIL:
                self._warn("The search was prematurely terminated due to a "
                    "solver failure.")
            elif error == glpk.GLP_EOBJLL:
                self._warn("The search was prematurely terminated, because the "
                    "objective function being maximized has reached its lower "
                    "limit and continues decreasing.")
            elif error == glpk.GLP_EOBJUL:
                self._warn("The search was prematurely terminated, because the "
                    "objective function being minimized has reached its upper "
                    "limit and continues increasing.")
            elif error == glpk.GLP_EITLIM:
                self._warn("The search was prematurely terminated, because the "
                    "simplex iteration limit has been exceeded.")
            elif error == glpk.GLP_ETMLIM:
                self._warn("The search was prematurely terminated, because the "
                    "time limit has been exceeded.")
            elif error == glpk.GLP_ENOPFS:
                self._verbose("The LP has no primal feasible solution.")
            elif error == glpk.GLP_ENODFS:
                self._verbose("The LP has no dual feasible solution.")
            elif error != 0:
                self._warn("GLPK error {:d}.".format(error))

        # Retrieve primals.
        if self.ext.options["noprimals"]:
            primals = None
        else:
            primals = {}

            for variable in self.ext.variables.values():
                value = []

                for localIndex, picosIndex \
                in enumerate(range(variable.startIndex, variable.endIndex)):
                    glpkIndex = self._picos2glpk_variable_index(picosIndex)

                    if simplex:
                        localValue = glpk.glp_get_col_prim(p, glpkIndex);
                    elif interior:
                        localValue = glpk.glp_ipt_col_prim(p, glpkIndex);
                    else:
                        localValue = glpk.glp_mip_col_val(p, glpkIndex);

                    value.append(localValue)

                primals[variable.name] = value

        # Retrieve duals.
        # XXX: Returns the duals as a flat cvx.matrix to be consistent with
        #      other solvers. This feels incorrect when the constraint was given
        #      as a proper two dimensional matrix.
        if self.ext.options["noduals"] or not continuous:
            duals = None
        else:
            duals = []
            rowOffset = 1
            for constraintNum, constraint in enumerate(self.ext.constraints):
                assert isinstance(constraint, AffineConstraint)
                numRows = len(constraint)
                values = []
                for localConIndex in range(numRows):
                    glpkConIndex = rowOffset + localConIndex
                    if simplex:
                        localValue = glpk.glp_get_row_dual(p, glpkConIndex);
                    elif interior:
                        localValue = glpk.glp_ipt_row_dual(p, glpkConIndex);
                    else:
                        assert False
                    values.append(localValue)
                if constraint.is_decreasing():
                    duals.append(-cvxopt.matrix(values))
                else:
                    duals.append(cvxopt.matrix(values))
                rowOffset += numRows
            if glpk.glp_get_obj_dir(p) == glpk.GLP_MIN:
                duals = [-d for d in duals]

        # Retrieve objective value.
        if simplex:
            objectiveValue = glpk.glp_get_obj_val(p)
        elif interior:
            objectiveValue = glpk.glp_ipt_obj_val(p)
        else:
            objectiveValue = glpk.glp_mip_obj_val(p)

        # Retrieve solution metadata.
        meta = {}

        if simplex:
            # Set common entry "status".
            status = glpk.glp_get_status(p)
            if status is glpk.GLP_OPT:
                meta["status"] = "optimal"
            elif status is glpk.GLP_FEAS:
                meta["status"] = "feasible"
            elif status in (glpk.GLP_INFEAS, glpk.GLP_NOFEAS):
                meta["status"] = "infeasible"
            elif status is glpk.GLP_UNBND:
                meta["status"] = "unbounded"
            elif status is glpk.GLP_UNDEF:
                meta["status"] = "undefined"
            else:
                meta["status"] = "unknown"

            # Set GLPK-specific entry "primal_status".
            primalStatus = glpk.glp_get_prim_stat(p)
            if primalStatus is glpk.GLP_FEAS:
                meta["primal_status"] = "feasible"
            elif primalStatus in (glpk.GLP_INFEAS, glpk.GLP_NOFEAS):
                meta["primal_status"] = "infeasible"
            elif primalStatus is glpk.GLP_UNDEF:
                meta["primal_status"] = "undefined"
            else:
                meta["primal_status"] = "unknown"

            # Set GLPK-specific entry "dual_status".
            dualStatus = glpk.glp_get_dual_stat(p)
            if dualStatus is glpk.GLP_FEAS:
                meta["dual_status"] = "feasible"
            elif dualStatus in (glpk.GLP_INFEAS, glpk.GLP_NOFEAS):
                meta["dual_status"] = "infeasible"
            elif dualStatus is glpk.GLP_UNDEF:
                meta["dual_status"] = "undefined"
            else:
                meta["dual_status"] = "unknown"
        elif interior:
            # Set common entry "status".
            status = glpk.glp_ipt_status(p)
            if status is glpk.GLP_OPT:
                meta["status"] = "optimal"
            elif status in (glpk.GLP_INFEAS, glpk.GLP_NOFEAS):
                meta["status"] = "infeasible"
            elif status is glpk.GLP_UNDEF:
                meta["status"] = "undefined"
            else:
                meta["status"] = "unknown"
        else:
            # Set common entry "status".
            status = glpk.glp_mip_status(p)
            if status is glpk.GLP_OPT:
                meta["status"] = "optimal"
            elif status is glpk.GLP_FEAS:
                meta["status"] = "feasible"
            elif status is glpk.GLP_NOFEAS:
                meta["status"] = "infeasible"
            elif status is glpk.GLP_UNDEF:
                meta["status"] = "undefined"
            else:
                meta["status"] = "unknown"

        return (primals, duals, objectiveValue, meta)
Example #9
0
 def unbounded(self):
     """Whether solution is unbounded"""
     self._check_valid()
     if self._ret_val != 0:
         return self._ret_val == swiglpk.GLP_ENODFS
     return swiglpk.glp_get_status(self._problem._p) == swiglpk.GLP_UNBND
Example #10
0
 def success(self):
     """Return boolean indicating whether a solution was found."""
     self._check_valid()
     if self._ret_val != 0:
         return False
     return swiglpk.glp_get_status(self._problem._p) == swiglpk.GLP_OPT
Example #11
0
 def status_code(self):
     """The generic status code for the current basic solution."""
     return glp.glp_get_status(self._lp)
Example #12
0
File: glpk.py Project: jonls/psamm
 def unbounded(self):
     """Whether solution is unbounded"""
     self._check_valid()
     if self._ret_val != 0:
         return self._ret_val == swiglpk.GLP_ENODFS
     return swiglpk.glp_get_status(self._problem._p) == swiglpk.GLP_UNBND
Example #13
0
File: glpk.py Project: jonls/psamm
 def success(self):
     """Return boolean indicating whether a solution was found."""
     self._check_valid()
     if self._ret_val != 0:
         return False
     return swiglpk.glp_get_status(self._problem._p) == swiglpk.GLP_OPT