def _solve(self):
        if self._solved:
            return

        if self._maximize:
            glp.glp_set_obj_dir(self._lp, glp.GLP_MAX)
        else:
            glp.glp_set_obj_dir(self._lp, glp.GLP_MIN)

        result = glp.glp_simplex(self._lp, self._smcp)

        # If no solution within iteration limit, switch to dual method to find solution
        if result == glp.GLP_EITLIM:
            print(
                'Warning: could not find solution with primal method, switching to dual'
            )
            self.simplex_method = SimplexMethod.DUALP
            result = glp.glp_simplex(self._lp, self._smcp)
            self.simplex_method = SimplexMethod.PRIMAL

        if result != 0:
            raise RuntimeError(
                SIMPLEX_RETURN_CODE_TO_STRING.get(
                    result, "GLP_?: UNKNOWN SOLVER RETURN VALUE"))
        if self.status_code != glp.GLP_OPT:
            raise RuntimeError(self.status_string)

        self._solved = True
	def test_swiglpk(self):
		"""Test the underlying GLPK lib and its SWIG interface based on
		the example from https://github.com/biosustain/swiglpk
		"""
		ia = glp.intArray(1 + 1000)
		ja = glp.intArray(1 + 1000)
		ar = glp.doubleArray(1 + 1000)

		lp = glp.glp_create_prob()
		smcp = glp.glp_smcp()
		glp.glp_init_smcp(smcp)
		smcp.msg_lev = glp.GLP_MSG_ALL  # use GLP_MSG_ERR?

		glp.glp_set_prob_name(lp, "sample")
		glp.glp_set_obj_dir(lp, glp.GLP_MAX)

		glp.glp_add_rows(lp, 3)
		glp.glp_set_row_name(lp, 1, "p")
		glp.glp_set_row_bnds(lp, 1, glp.GLP_UP, 0.0, 100.0)
		glp.glp_set_row_name(lp, 2, "q")
		glp.glp_set_row_bnds(lp, 2, glp.GLP_UP, 0.0, 600.0)
		glp.glp_set_row_name(lp, 3, "r")
		glp.glp_set_row_bnds(lp, 3, glp.GLP_UP, 0.0, 300.0)
		glp.glp_add_cols(lp, 3)
		glp.glp_set_col_name(lp, 1, "x1")
		glp.glp_set_col_bnds(lp, 1, glp.GLP_LO, 0.0, 0.0)
		glp.glp_set_obj_coef(lp, 1, 10.0)
		glp.glp_set_col_name(lp, 2, "x2")
		glp.glp_set_col_bnds(lp, 2, glp.GLP_LO, 0.0, 0.0)
		glp.glp_set_obj_coef(lp, 2, 6.0)
		glp.glp_set_col_name(lp, 3, "x3")
		glp.glp_set_col_bnds(lp, 3, glp.GLP_LO, 0.0, 0.0)
		glp.glp_set_obj_coef(lp, 3, 4.0)

		ia[1] = 1; ja[1] = 1; ar[1] = 1.0  # a[1,1] = 1
		ia[2] = 1; ja[2] = 2; ar[2] = 1.0  # a[1,2] = 1
		ia[3] = 1; ja[3] = 3; ar[3] = 1.0  # a[1,3] = 1
		ia[4] = 2; ja[4] = 1; ar[4] = 10.0  # a[2,1] = 10
		ia[5] = 3; ja[5] = 1; ar[5] = 2.0  # a[3,1] = 2
		ia[6] = 2; ja[6] = 2; ar[6] = 4.0  # a[2,2] = 4
		ia[7] = 3; ja[7] = 2; ar[7] = 2.0  # a[3,2] = 2
		ia[8] = 2; ja[8] = 3; ar[8] = 5.0  # a[2,3] = 5
		ia[9] = 3; ja[9] = 3; ar[9] = 6.0  # a[3,3] = 6

		glp.glp_load_matrix(lp, 9, ia, ja, ar)
		glp.glp_simplex(lp, smcp)

		Z = glp.glp_get_obj_val(lp)
		x1 = glp.glp_get_col_prim(lp, 1)
		x2 = glp.glp_get_col_prim(lp, 2)
		x3 = glp.glp_get_col_prim(lp, 3)

		self.assertAlmostEqual(Z, 733.3333, 4)
		self.assertAlmostEqual(x1, 33.3333, 4)
		self.assertAlmostEqual(x2, 66.6667, 4)
		self.assertAlmostEqual(x3, 0)

		glp.glp_delete_prob(lp)
Example #3
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 #4
0
def _linprog(c, A, b, obj):

    lp = glpk.glp_create_prob()
    glpk.glp_set_obj_dir(lp, obj)

    params = glpk.glp_smcp()
    glpk.glp_init_smcp(params)
    params.msg_lev = glpk.GLP_MSG_OFF  #Only print error messages from GLPK

    num_rows = A.shape[0]
    num_cols = A.shape[1]
    mat_size = num_rows * num_cols

    glpk.glp_add_rows(lp, num_rows)

    for row_ind in range(num_rows):
        glpk.glp_set_row_bnds(lp, row_ind + 1, glpk.GLP_UP, 0.0,
                              float(b[row_ind]))

    glpk.glp_add_cols(lp, num_cols)

    for col_ind in range(num_cols):
        glpk.glp_set_col_bnds(lp, col_ind + 1, glpk.GLP_FR, 0.0, 0.0)
        glpk.glp_set_obj_coef(lp, col_ind + 1, c[col_ind])

    'Swig arrays are used for feeding constraints in GLPK'

    ia, ja, ar = [], [], []
    for i, j in product(range(num_rows), range(num_cols)):
        ia.append(i + 1)
        ja.append(j + 1)
        ar.append(float(A[i][j]))

    ia = glpk.as_intArray(ia)
    ja = glpk.as_intArray(ja)
    ar = glpk.as_doubleArray(ar)

    glpk.glp_load_matrix(lp, mat_size, ia, ja, ar)
    glpk.glp_simplex(lp, params)

    fun = glpk.glp_get_obj_val(lp)
    x = [
        i for i in map(lambda x: glpk.glp_get_col_prim(lp, x + 1),
                       range(num_cols))
    ]

    glpk.glp_delete_prob(lp)
    glpk.glp_free_env()

    return LPSolution(x, fun)
Example #5
0
    def solve(self, sense=None):
        """Solve problem."""
        if sense is not None:
            self.set_objective_sense(sense)

        parm = swiglpk.glp_smcp()
        swiglpk.glp_init_smcp(parm)
        parm.presolve = swiglpk.GLP_ON

        logger.debug('Solving using glp_simplex()')
        r = swiglpk.glp_simplex(self._p, parm)
        if r in (swiglpk.GLP_ENOPFS, swiglpk.GLP_ENODFS):
            self._result = Result(self, r)
            return self._result
        elif r != 0:
            raise GLPKError('glp_simplex: {}'.format(r))

        if swiglpk.glp_get_num_int(self._p) == 0:
            self._result = Result(self)
        else:
            # The intopt MILP solver need an optimal solution to the LP
            # relaxation. Therefore, glp_simplex has to run before glp_intopt
            # for MILP problems.
            logger.debug('Solving using glp_intopt()')
            r = swiglpk.glp_intopt(self._p, None)
            if r != 0:
                raise GLPKError('glp_intopt: {}'.format(r))

            self._result = MIPResult(self)

        return self._result
Example #6
0
File: glpk.py Project: jonls/psamm
    def solve(self, sense=None):
        """Solve problem."""
        if sense is not None:
            self.set_objective_sense(sense)

        parm = swiglpk.glp_smcp()
        swiglpk.glp_init_smcp(parm)
        if self._do_presolve:
            parm.presolve = swiglpk.GLP_ON
            self._do_presolve = False
        else:
            parm.presolve = swiglpk.GLP_OFF

        logger.debug('Solving using glp_simplex()')
        r = swiglpk.glp_simplex(self._p, parm)
        if r in (swiglpk.GLP_ENOPFS, swiglpk.GLP_ENODFS):
            self._result = Result(self, r)
            return self._result
        elif r != 0:
            raise GLPKError('glp_simplex: {}'.format(r))

        if swiglpk.glp_get_num_int(self._p) == 0:
            self._result = Result(self)
        else:
            # The intopt MILP solver need an optimal solution to the LP
            # relaxation. Therefore, glp_simplex has to run before glp_intopt
            # for MILP problems.
            logger.debug('Solving using glp_intopt()')
            r = swiglpk.glp_intopt(self._p, None)
            if r != 0:
                raise GLPKError('glp_intopt: {}'.format(r))

            self._result = MIPResult(self)

        return self._result
Example #7
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 #8
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 #9
0
    def _solve(self):
        if self._solved:
            return

        if self._maximize:
            glp.glp_set_obj_dir(self._lp, glp.GLP_MAX)
        else:
            glp.glp_set_obj_dir(self._lp, glp.GLP_MIN)

        result = glp.glp_simplex(self._lp, self._smcp)

        # Adjust solver options for robustness
        ## If no solution within iteration limit, switch to dual method to find solution
        if result == glp.GLP_EITLIM:
            print(
                'Warning: could not find solution with primal method, switching to dual'
            )
            self.simplex_method = SimplexMethod.DUALP
            result = glp.glp_simplex(self._lp, self._smcp)
            self.simplex_method = SimplexMethod.PRIMAL

            ## If still no solution, presolve the problem to reduce complexity
            ## (also prevents warm start)
            if self.status_code != glp.GLP_OPT:
                print(
                    'Warning: could not find solution with dual method, using primal with presolve'
                )
                self._smcp.presolve = glp.GLP_ON
                result = glp.glp_simplex(self._lp, self._smcp)
                self._smcp.presolve = glp.GLP_OFF

        if result != 0:
            raise RuntimeError(
                SIMPLEX_RETURN_CODE_TO_STRING.get(
                    result, "GLP_?: UNKNOWN SOLVER RETURN VALUE"))
        if self.status_code != glp.GLP_OPT:
            raise RuntimeError(self.status_string)

        self._solved = True
Example #10
0
def solve_linprog(problem: SwigPyObject):
    level = level_for_parm()

    parm = lp.glp_smcp()
    lp.glp_init_smcp(parm)
    parm.msg_lev = level
    check_lp(problem, lp.glp_simplex(problem, parm))
    log_soln(problem, 'sol')

    parm = lp.glp_iocp()
    lp.glp_init_iocp(parm)
    parm.msg_lev = level
    check_lp(problem, lp.glp_intopt(problem, parm))
    log_soln(problem, 'mip')
Example #11
0
    def minimize(self, direction_vec=None, columns=None, fail_on_unsat=True):
        '''minimize the lp, returning a list of assigments to each of the variables

        if direction_vec is not None, this will first assign the optimization direction (note: relative to cur_vars)
        if columns is not None, will only return the requested columns (default: all columns)
        if fail_on_unsat is True and the LP is infeasible, an UnsatError is raised
        unsat (sometimes happens in GLPK due to likely bug, see space station model)

        returns None if UNSAT, otherwise the optimization result. Use columns=[] if you're not interested in the result
        '''

        if direction_vec is not None:
            self.set_minimize_direction(direction_vec)

        Timers.tic('setup')
        # setup lp params
        params = glpk.glp_smcp()
        glpk.glp_init_smcp(params)
        params.meth = glpk.GLP_DUALP  # use dual simplex since we're reoptimizing often
        params.msg_lev = glpk.GLP_MSG_OFF
        params.tm_lim = 1000  # 1000 ms time limit
        Timers.toc('setup')

        Timers.tic('glp_simplex')
        simplex_res = glpk.glp_simplex(self.lp, params)
        Timers.toc('glp_simplex')

        # process simplex result
        Timers.tic('process_simplex_result')
        rv = self._process_simplex_result(simplex_res, columns)
        Timers.toc('process_simplex_result')

        if rv is None and fail_on_unsat:
            print(
                "Note: minimize failed with fail_on_unsat was true, resetting and retrying..."
            )

            glpk.glp_cpx_basis(self.lp)  # resets the initial basis

            rv = self.minimize(direction_vec, columns, False)

            #LpInstance.print_verbose("Note: LP was infeasible, but then feasible after resetting statuses")

        if rv is None and fail_on_unsat:
            raise UnsatError(
                "minimize returned UNSAT and fail_on_unsafe was True")

        return rv
Example #12
0
    def solve_unchecked(self, sense=None):
        """Solve problem and return result.

        The user must manually check the status of the result to determine
        whether an optimal solution was found. A :class:`SolverError` may still
        be raised if the underlying solver raises an exception.
        """
        if sense is not None:
            self.set_objective_sense(sense)

        parm = swiglpk.glp_smcp()
        swiglpk.glp_init_smcp(parm)
        if self._do_presolve:
            parm.presolve = swiglpk.GLP_ON
            self._do_presolve = False
        else:
            parm.presolve = swiglpk.GLP_OFF

        parm.tol_bnd = self._feasibility_tolerance
        parm.tol_dj = self._optimality_tolerance

        logger.debug('Solving using glp_simplex()')
        r = swiglpk.glp_simplex(self._p, parm)
        if r in (swiglpk.GLP_ENOPFS, swiglpk.GLP_ENODFS):
            self._result = Result(self, r)
            return self._result
        elif r != 0:
            raise GLPKError('glp_simplex: {}'.format(r))

        if swiglpk.glp_get_num_int(self._p) == 0:
            self._result = Result(self)
        else:
            # The intopt MILP solver needs an optimal solution to the LP
            # relaxation. Therefore, glp_simplex has to run before glp_intopt
            # for MILP problems.
            logger.debug('Solving using glp_intopt()')
            parm = swiglpk.glp_iocp()
            swiglpk.glp_init_iocp(parm)
            parm.tol_int = self._integrality_tolerance
            r = swiglpk.glp_intopt(self._p, parm)
            if r != 0:
                raise GLPKError('glp_intopt: {}'.format(r))

            self._result = MIPResult(self)

        return self._result
Example #13
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 #14
0
    def minimize(self, direction_vec, fail_on_unsat=True):
        '''minimize the lp, returning a list of assigments to each of the variables

        if direction_vec is not None, this will first assign the optimization direction

        returns None if UNSAT, otherwise the optimization result.
        '''

        assert not isinstance(
            self.lp,
            tuple), "self.lp was tuple. Did you call lpi.deserialize()?"

        if direction_vec is None:
            direction_vec = [0] * self.get_num_cols()

        self.set_minimize_direction(direction_vec)

        if Settings.GLPK_RESET_BEFORE_MINIMIZE:
            self.reset_basis()

        start = time.perf_counter()
        simplex_res = glpk.glp_simplex(self.lp, get_lp_params())

        if simplex_res != 0:  # solver failure (possibly timeout)
            r = self.get_num_rows()
            c = self.get_num_cols()

            diff = time.perf_counter() - start
            print(f"GLPK timed out / failed ({simplex_res}) after {round(diff, 3)} sec with primary " + \
                  f"settings with {r} rows and {c} cols")

            print("Retrying with reset")
            self.reset_basis()
            start = time.perf_counter()
            simplex_res = glpk.glp_simplex(self.lp, get_lp_params())
            diff = time.perf_counter() - start
            print(f"result with reset  ({simplex_res}) {round(diff, 3)} sec")

            print("Retrying with reset + alternate GLPK settings")

            # retry with alternate params
            params = get_lp_params(alternate_lp_params=True)
            self.reset_basis()
            start = time.perf_counter()
            simplex_res = glpk.glp_simplex(self.lp, params)
            diff = time.perf_counter() - start
            print(
                f"result with reset & alternate settings ({simplex_res}) {round(diff, 3)} sec"
            )

        rv = self._process_simplex_result(simplex_res)

        if rv is None and fail_on_unsat:
            # extra logic to try harder if fail_on_unsafe is True
            # glpk can sometimes be cajoled into providing a solution

            print(
                "Note: minimize failed with fail_on_unsat was true, trying to reset basis..."
            )

            self.reset_basis()
            rv = self.minimize(direction_vec, fail_on_unsat=False)

            if rv is None:
                print(
                    "still unsat after reset basis, trying no-dir optimization"
                )
                self.reset_basis()

                result_nodir = self.minimize(None, fail_on_unsat=False)

                # lp became infeasible when I picked an optimization direction
                if result_nodir is not None:
                    print("Using result from no-direction optimization")
                    rv = result_nodir
                else:
                    print("Error: No-dir result was also infeasible!")
            else:
                print(
                    "Using result after reset basis (soltion was now feasible)"
                )

        if rv is None and fail_on_unsat:
            raise UnsatError(
                "minimize returned UNSAT and fail_on_unsat was True")

        return rv
Example #15
0
    def minimize(self, direction_vec=None, columns=None, fail_on_unsat=True, print_on=False):
        '''minimize the lp, returning a list of assigments to each of the variables

        if direction_vec is not None, this will first assign the optimization direction (note: relative to cur_vars)
        if columns is not None, will only return the requested columns (default: all columns)
        if fail_on_unsat is True and the LP is infeasible, an UnsatError is raised
        unsat (sometimes happens in GLPK due to likely bug, see space station model)

        returns None if UNSAT, otherwise the optimization result. Use columns=[] if you're not interested in the result
        '''

        Timers.tic('minimize')

        if direction_vec is not None:
            self.set_minimize_direction(direction_vec)

        # setup lp params
        params = glpk.glp_smcp()
        glpk.glp_init_smcp(params)
        params.meth = glpk.GLP_DUALP # use dual simplex since we're reoptimizing often
        params.msg_lev = glpk.GLP_MSG_ALL if print_on else glpk.GLP_MSG_OFF
        params.tm_lim = 1000 # 1000 ms time limit

        Timers.tic('glp_simplex')
        simplex_res = glpk.glp_simplex(self.lp, params)
        Timers.toc('glp_simplex')

        if simplex_res != 0:
            # this can happen when you replace constraints after already solving once
            LpInstance.print_normal('Note: glp_simplex() failed ({}: {}), resetting and retrying'.format(
                simplex_res, LpInstance.get_simplex_error_string(simplex_res)))

            if simplex_res == glpk.GLP_ESING: # singular matrix, can happen after replacing constraints
                glpk.glp_std_basis(self.lp)
                simplex_res = glpk.glp_simplex(self.lp, params)

            if simplex_res != 0:
                glpk.glp_cpx_basis(self.lp) # resets the initial basis
                params.msg_lev = glpk.GLP_MSG_ON # turn printing on
                params.tm_lim = 30 * 1000 # second try: 30 second time limit
                simplex_res = glpk.glp_simplex(self.lp, params)

        # process simplex result
        rv = self._process_simplex_result(simplex_res, columns)

        Timers.toc('minimize')

        if rv is None and fail_on_unsat:
            LpInstance.print_normal("Note: minimize failed with fail_on_unsat was true, resetting and retrying...")

            glpk.glp_cpx_basis(self.lp) # resets the initial basis

            rv = self.minimize(direction_vec, columns, False, print_on=True)

            if rv is not None:
                LpInstance.print_verbose("Note: LP was infeasible, but then feasible after resetting statuses")

        if rv is None and fail_on_unsat:
            raise UnsatError("minimize returned UNSAT and fail_on_unsafe was True")

        return rv