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
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
def _run_glp_mip(self): return_value = glp_intopt(self.problem, self.configuration._iocp) glpk_status = glp_mip_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_intopt was %d" % return_value) return status
def solve_IP(self): #clear solution file open("lp.sol", "w").close() #create the LP lp = glpk.glp_create_prob() #create the model translator tran = glpk.glp_mpl_alloc_wksp() #read the model intro translator glpk.glp_mpl_read_model(tran, "lp.mod", 0) #generate the model glpk.glp_mpl_generate(tran, None) #build the LP from the model glpk.glp_mpl_build_prob(tran, lp) #create and init params for MIP solver params = glpk.glp_iocp() glpk.glp_init_iocp(params) params.presolve = glpk.GLP_ON #solve the MIP glpk.glp_intopt(lp, params) #save solution #glpk.glp_write_sol(lp,"lp2.sol") glpk.glp_mpl_postsolve(tran, lp, glpk.GLP_MIP) #free resources glpk.glp_mpl_free_wksp(tran) glpk.glp_delete_prob(lp) #read solution from model self.read_solution() #delete model and solution files os.remove("lp.mod") os.remove("lp.sol")
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')
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
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)
def solve(nutrition_target, foods): ''' Calculate food amounts to reach the nutrition target Parameters ---------- nutrition_target : soylent_recipes.nutrition_target.NormalizedNutritionTarget The desired nutrition foods : np.array The foods to use to achieve the nutrition target. Contains exactly the nutrients required by the nutrition target in the exact same order. Rows represent foods, columns represent nutrients. Returns ------- amounts : np.array(int) or None The amounts of each food to use to optimally achieve the nutrition target. ``amounts[i]`` is the amount of the i-th food to use. If the nutrition target cannot be achieved, returns None. ''' # Implementation: using the GLPK C library via ecyglpki Python library binding # GLPK documentation: download it and look inside the package (http://ftp.gnu.org/gnu/glpk/) # GLPK wikibook: https://en.wikibooks.org/wiki/GLPK # # GPLK lingo: rows and columns refer to Ax=b where b_i are auxiliary # variables, x_i are structural variables. Setting constraints on rows, set # constraints on b_i, while column constraints are applied to x_i. # Note: glpk is powerful. We're using mostly the default settings. # Performance likely can be improved by tinkering with the settings; or even # by providing the solution to the least squares equivalent, with amounts # rounded afterwards, as starting point could improve performance. nutrition_target = nutrition_target.values problem = glp.glp_create_prob() try: glp.glp_add_rows(problem, len(nutrition_target)) glp.glp_add_cols(problem, len(foods)) # Configure columns/amounts for i in range(len(foods)): glp.glp_set_col_kind(problem, i+1, glp.GLP_IV) # int glp.glp_set_col_bnds(problem, i+1, glp.GLP_LO, 0.0, np.nan) # >=0 # Configure rows/nutrients for i, extrema in enumerate(nutrition_target): if np.isnan(extrema[0]): bounds_type = glp.GLP_UP elif np.isnan(extrema[1]): bounds_type = glp.GLP_LO else: # Note: a nutrition target has either min, max or both and min!=max bounds_type = glp.GLP_DB glp.glp_set_row_bnds(problem, i+1, bounds_type, *extrema) # Load A of our Ax=b non_zero_count = foods.size row_indices = glp.intArray(non_zero_count+1) # +1 because (insane) 1-indexing column_indices = glp.intArray(non_zero_count+1) values = glp.doubleArray(non_zero_count+1) for i, ((row, column), value) in enumerate(np.ndenumerate(foods.transpose())): row_indices[i+1] = row+1 column_indices[i+1] = column+1 values[i+1] = value glp.glp_load_matrix(problem, non_zero_count, row_indices, column_indices, values) # Solve int_opt_args = glp.glp_iocp() glp.glp_init_iocp(int_opt_args) int_opt_args.presolve = glp.GLP_ON # without this, you have to provide an LP relaxation basis int_opt_args.msg_lev = glp.GLP_MSG_OFF # be quiet, no stdout glp.glp_intopt(problem, int_opt_args) # returns an error code; can safely ignore # Check we've got a valid solution # # Note: glp_intopt returns whether the algorithm completed successfully. # This does not imply you've got a good solution, it could even be # infeasible. glp_mip_status returns whether the solution is optimal, # feasible, infeasible or undefined. An optimal/feasible solution is not # necessarily a good solution. An optimal solution may even violate # bounds constraints. The thing you actually need to use is # glp_check_kkt and check that the solution satisfies KKT.PB (all within # bounds) max_error = glp.doubleArray(1) glp.glp_check_kkt(problem, glp.GLP_MIP, glp.GLP_KKT_PB, max_error, None, None, None) if not np.isclose(max_error[0], 0.0): # A row/column value exceeds its bounds return None # Return solution amounts = np.fromiter((glp.glp_mip_col_val(problem, i+1) for i in range(len(foods))), int) return amounts finally: glp.glp_delete_prob(problem)