def _glpk_set_row_bounds(self, constraint): if constraint.lb is None and constraint.ub is None: # 0.'s are ignored glp_set_row_bnds(self.problem, constraint.index, GLP_FR, 0., 0.) elif constraint.lb is None: # 0. is ignored glp_set_row_bnds(self.problem, constraint.index, GLP_UP, 0., float(constraint.ub)) elif constraint.ub is None: # 0. is ignored glp_set_row_bnds(self.problem, constraint.index, GLP_LO, float(constraint.lb), 0.) elif constraint.lb == constraint.ub: glp_set_row_bnds(self.problem, constraint.index, GLP_FX, float(constraint.lb), float(constraint.lb)) elif constraint.lb < constraint.ub: glp_set_row_bnds(self.problem, constraint.index, GLP_DB, float(constraint.lb), float(constraint.ub)) elif constraint.lb > constraint.ub: raise ValueError( "Lower bound %f is larger than upper bound %f in constraint %s" % (constraint.lb, constraint.ub, constraint)) else: raise Exception( "Something is wrong with the provided bounds %f and %f in constraint %s" % (constraint.lb, constraint.ub, constraint))
def set_row_equal(self, row_num, rhs): 'set row equality constraint constraints[row_num] == rhs' assert 0 <= row_num < self.get_num_rows() glpk.glp_set_row_bnds(self.lp, row_num + 1, glpk.GLP_FX, rhs, rhs) # '== rhs' constraint
def buildEqConst(self): if self._eqConstBuilt: raise Exception("Equality constraints already built.") n_coeffs = len(self._materialCoeffs) n_flows = len(self._flows) self._add_rows(n_coeffs) A = np.zeros((n_coeffs, n_flows)) # avoid creating duplicate constraints self._materialIdxLookup = {} for materialIdx, (material, pairs) in enumerate( sorted(self._materialCoeffs.viewitems())): self._materialIdxLookup[material] = materialIdx for pair in pairs: A[materialIdx, pair[1]] = pair[0] A_coo = coo_matrix(A) rowIdxs = _toIndexArray(A_coo.row) colIdxs = _toIndexArray(A_coo.col) data = _toDoubleArray(A_coo.data) n_elems = len(A_coo.row) for row in xrange(1, self._n_eq_constraints + 1): glp.glp_set_row_bnds(self._lp, row, glp.GLP_FX, 0.0, 0.0) glp.glp_load_matrix(self._lp, n_elems, rowIdxs, colIdxs, data) self._eqConstBuilt = True
def _glpk_set_row_bounds(self, constraint): if constraint.lb is None and constraint.ub is None: # 0.'s are ignored glp_set_row_bnds(self.problem, constraint._index, GLP_FR, 0., 0.) elif constraint.lb is None: # 0. is ignored glp_set_row_bnds(self.problem, constraint._index, GLP_UP, 0., float(constraint.ub)) elif constraint.ub is None: # 0. is ignored glp_set_row_bnds(self.problem, constraint._index, GLP_LO, float(constraint.lb), 0.) elif constraint.lb == constraint.ub: glp_set_row_bnds(self.problem, constraint._index, GLP_FX, float(constraint.lb), float(constraint.lb)) elif constraint.lb < constraint.ub: glp_set_row_bnds(self.problem, constraint._index, GLP_DB, float(constraint.lb), float(constraint.ub)) elif constraint.lb > constraint.ub: raise ValueError( "Lower bound %f is larger than upper bound %f in constraint %s" % (constraint.lb, constraint.ub, constraint)) else: raise Exception( "Something is wrong with the provided bounds %f and %f in constraint %s" % (constraint.lb, constraint.ub, constraint))
def set_rows(lp, d, signs): rows_count = len(d) bnd_types = get_bnd_types(signs, rows_count) glpk.glp_add_rows(lp, rows_count) for idx, (bnd_type, bnd) in enumerate(zip(bnd_types, d)): glpk.glp_set_row_bnds(lp, idx + 1, bnd_type, bnd, bnd) return rows_count
def set_rhs(self, rhs_vec): 'set (overwrite) the rhs for exising rows' assert rhs_vec.size == self.get_num_rows() for i, rhs in enumerate(rhs_vec): glpk.glp_set_row_bnds(self.lp, i + 1, glpk.GLP_UP, 0, rhs) # '<=' constraint
def add_rows_equal_zero(self, num): '''add rows to the LP with == 0 constraints''' if num > 0: num_rows = glpk.glp_get_num_rows(self.lp) # create new row for each constraint glpk.glp_add_rows(self.lp, num) for i in range(num): glpk.glp_set_row_bnds(self.lp, num_rows + i + 1, glpk.GLP_FX, 0, 0) # '== 0' constraints
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)
def add_rows_equal(self, rhs_vec): '''add rows to the LP with == rhs[i] constraints''' num = len(rhs_vec) if num > 0: num_rows = glpk.glp_get_num_rows(self.lp) # create new row for each constraint glpk.glp_add_rows(self.lp, num) for i, val in enumerate(rhs_vec): glpk.glp_set_row_bnds(self.lp, num_rows + i + 1, glpk.GLP_FX, val, val) # '== val' constraints
def setup_row( problem: SwigPyObject, i: int, resource: str, min_rate: float, ): lp.glp_set_row_name(problem, i, resource) # Every resource rate must be 0 or greater for sustainability lp.glp_set_row_bnds( problem, i, lp.GLP_LO, 100 * min_rate, float('inf'), # Lower and upper boundaries )
def add_rows_less_equal(self, rhs_vec): '''add rows to the LP with <= constraints rhs_vector is the right-hand-side values of the constriants ''' if isinstance(rhs_vec, list): rhs_vec = np.array(rhs_vec, dtype=float) assert isinstance(rhs_vec, np.ndarray) and len(rhs_vec.shape) == 1, "expected 1-d right-hand-side vector" if rhs_vec.shape[0] > 0: num_rows = glpk.glp_get_num_rows(self.lp) # create new row for each constraint glpk.glp_add_rows(self.lp, len(rhs_vec)) for i, rhs in enumerate(rhs_vec): glpk.glp_set_row_bnds(self.lp, num_rows + i + 1, glpk.GLP_UP, 0, rhs) # '<=' constraint
def add_rows_with_types(self, types, rhs_vec): '''add rows to the LP with the given types types is a vector of types: swiglpk.GLP_FX, swiglpk.GLP_UP, or swiglpk.GLP_LO rhs_vector is the right-hand-side values of the constriants ''' assert len(types) == len(rhs_vec) if isinstance(rhs_vec, list): rhs_vec = np.array(rhs_vec, dtype=float) assert isinstance(rhs_vec, np.ndarray) and len(rhs_vec.shape) == 1, "expected 1-d right-hand-side vector" if rhs_vec.shape[0] > 0: num_rows = glpk.glp_get_num_rows(self.lp) # create new row for each constraint glpk.glp_add_rows(self.lp, len(rhs_vec)) for i, pair in enumerate(zip(rhs_vec, types)): rhs, ty = pair if ty == glpk.GLP_UP: glpk.glp_set_row_bnds(self.lp, num_rows + i + 1, glpk.GLP_UP, 0, rhs) # '<=' constraint elif ty == glpk.GLP_LO: glpk.glp_set_row_bnds(self.lp, num_rows + i + 1, glpk.GLP_LO, rhs, 0) # '>=' constraint else: assert ty == glpk.GLP_FX glpk.glp_set_row_bnds(self.lp, num_rows + i + 1, glpk.GLP_FX, rhs, rhs) # '>=' constraint
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)
def set_constraint_rhs(self, row_index, rhs): '''change an existing constraint's right hand side''' rows = glpk.glp_get_num_rows(self.lp) assert 0 <= row_index < rows, "Invalid row ({}) in set_constraint_rhs() (lp has {})".format( row_index, rows) row_type = glpk.glp_get_row_type(self.lp, row_index + 1) if row_type == glpk.GLP_UP: glpk.glp_set_row_bnds(self.lp, row_index + 1, glpk.GLP_UP, 0, rhs) elif row_type == glpk.GLP_LO: glpk.glp_set_row_bnds(self.lp, row_index + 1, glpk.GLP_LO, rhs, 0) elif row_type == glpk.GLP_FX: glpk.glp_set_row_bnds(self.lp, row_index + 1, glpk.GLP_FX, rhs, rhs) else: raise RuntimeError("Invalid constraint type {} in row {} in set_constraint_rhs()".format( row_type, row_index))
def _add_constraints(self, relation): """Add the given relation as one or more constraints. Return a list of the names of the constraints added. """ expression = relation.expression constr_count = sum(True for _ in expression.value_sets()) if constr_count == 0: return [] row_indices = count(swiglpk.glp_add_rows(self._p, constr_count)) names = [] for i, value_set in zip(row_indices, expression.value_sets()): value_set = list(value_set) var_indices = swiglpk.intArray(1 + len(value_set)) var_values = swiglpk.doubleArray(1 + len(value_set)) for j, (variable, coeff) in enumerate(value_set): var_indices[1 + j] = self._variables[variable] var_values[1 + j] = float(coeff) swiglpk.glp_set_mat_row( self._p, i, len(value_set), var_indices, var_values) if relation.sense == RelationSense.Greater: swiglpk.glp_set_row_bnds( self._p, i, swiglpk.GLP_LO, -float(expression.offset), 0) elif relation.sense == RelationSense.Less: swiglpk.glp_set_row_bnds( self._p, i, swiglpk.GLP_UP, 0, -float(expression.offset)) else: swiglpk.glp_set_row_bnds( self._p, i, swiglpk.GLP_FX, -float(expression.offset), 0) names.append(i) self._do_presolve = True return names
def _add_constraints(self, relation): """Add the given relation as one or more constraints. Return a list of the names of the constraints added. """ expression = relation.expression constr_count = sum(True for _ in expression.value_sets()) if constr_count == 0: return [] row_indices = count(swiglpk.glp_add_rows(self._p, constr_count)) names = [] for i, value_set in zip(row_indices, expression.value_sets()): value_set = list(value_set) var_indices = swiglpk.intArray(1 + len(value_set)) var_values = swiglpk.doubleArray(1 + len(value_set)) for j, (variable, coeff) in enumerate(value_set): var_indices[1 + j] = self._variables[variable] var_values[1 + j] = float(coeff) swiglpk.glp_set_mat_row(self._p, i, len(value_set), var_indices, var_values) if relation.sense == RelationSense.Greater: swiglpk.glp_set_row_bnds(self._p, i, swiglpk.GLP_LO, -float(expression.offset), 0) elif relation.sense == RelationSense.Less: swiglpk.glp_set_row_bnds(self._p, i, swiglpk.GLP_UP, 0, -float(expression.offset)) else: swiglpk.glp_set_row_bnds(self._p, i, swiglpk.GLP_FX, -float(expression.offset), 0) names.append(i) self._do_presolve = True return names
def delete(self): if self._name is not None: swiglpk.glp_set_row_bnds( self._prob._p, self._name, swiglpk.GLP_FR, 0, 0)
def _import_problem(self): import swiglpk as glpk if self.verbosity() >= 1: glpk.glp_term_out(glpk.GLP_ON) else: glpk.glp_term_out(glpk.GLP_OFF) # Create a problem instance. p = self.int = glpk.glp_create_prob(); # Set the objective. if self.ext.objective[0] in ("find", "min"): glpk.glp_set_obj_dir(p, glpk.GLP_MIN) elif self.ext.objective[0] is "max": glpk.glp_set_obj_dir(p, glpk.GLP_MAX) else: raise NotImplementedError("Objective '{0}' not supported by GLPK." .format(self.ext.objective[0])) # Set objective function shift if self.ext.objective[1] is not None \ and self.ext.objective[1].constant is not None: if not isinstance(self.ext.objective[1], AffinExp): raise NotImplementedError("Non-linear objective function not " "supported by GLPK.") if self.ext.objective[1].constant.size != (1,1): raise NotImplementedError("Non-scalar objective function not " "supported by GLPK.") glpk.glp_set_obj_coef(p, 0, self.ext.objective[1].constant[0]) # Add variables. # Multideminsional variables are split into multiple scalar variables # represented as matrix columns within GLPK. for varName in self.ext.varNames: var = self.ext.variables[varName] # Add a column for every scalar variable. numCols = var.size[0] * var.size[1] glpk.glp_add_cols(p, numCols) for localIndex, picosIndex \ in enumerate(range(var.startIndex, var.endIndex)): glpkIndex = self._picos2glpk_variable_index(picosIndex) # Assign a name to the scalar variable. scalarName = varName if numCols > 1: x = localIndex // var.size[0] y = localIndex % var.size[0] scalarName += "_{:d}_{:d}".format(x + 1, y + 1) glpk.glp_set_col_name(p, glpkIndex, scalarName) # Assign bounds to the scalar variable. lower, upper = var.bnd.get(localIndex, (None, None)) if lower is not None and upper is not None: if lower == upper: glpk.glp_set_col_bnds( p, glpkIndex, glpk.GLP_FX, lower, upper) else: glpk.glp_set_col_bnds( p, glpkIndex, glpk.GLP_DB, lower, upper) elif lower is not None and upper is None: glpk.glp_set_col_bnds(p, glpkIndex, glpk.GLP_LO, lower, 0) elif lower is None and upper is not None: glpk.glp_set_col_bnds(p, glpkIndex, glpk.GLP_UP, 0, upper) else: glpk.glp_set_col_bnds(p, glpkIndex, glpk.GLP_FR, 0, 0) # Assign a type to the scalar variable. if var.vtype in ("continuous", "symmetric"): glpk.glp_set_col_kind(p, glpkIndex, glpk.GLP_CV) elif var.vtype == "integer": glpk.glp_set_col_kind(p, glpkIndex, glpk.GLP_IV) elif var.vtype == "binary": glpk.glp_set_col_kind(p, glpkIndex, glpk.GLP_BV) else: raise NotImplementedError("Variable type '{0}' not " "supported by GLPK.".format(var.vtype())) # Set objective function coefficient of the scalar variable. if self.ext.objective[1] is not None \ and var in self.ext.objective[1].factors: glpk.glp_set_obj_coef(p, glpkIndex, self.ext.objective[1].factors[var][localIndex]) # Add constraints. # Multideminsional constraints are split into multiple scalar # constraints represented as matrix rows within GLPK. rowOffset = 1 for constraintNum, constraint in enumerate(self.ext.constraints): if not isinstance(constraint, AffineConstraint): raise NotImplementedError( "Non-linear constraints not supported by GLPK.") # Add a row for every scalar constraint. # Internally, GLPK uses an auxiliary variable for every such row, # bounded by the right hand side of the scalar constraint in a # canonical form. numRows = len(constraint) glpk.glp_add_rows(p, numRows) self._debug("Handling PICOS Constraint: " + str(constraint)) # Split multidimensional constraints into multiple scalar ones. for localConIndex, (glpkVarIndices, coefficients, rhs) in \ enumerate(constraint.sparse_Ab_rows( None, indexFunction = lambda picosVar, i: self._picos2glpk_variable_index(picosVar.startIndex + i))): # Determine GLPK's row index of the scalar constraint. glpkConIndex = rowOffset + localConIndex numColumns = len(glpkVarIndices) # Name the auxiliary variable associated with the current row. if constraint.name: name = constraint.name else: name = "rhs_{:d}".format(constraintNum) if numRows > 1: x = localConIndex // constraint.size[0] y = localConIndex % constraint.size[0] name += "_{:d}_{:d}".format(x + 1, y + 1) glpk.glp_set_row_name(p, glpkConIndex, name) # Assign bounds to the auxiliary variable. if constraint.is_equality(): glpk.glp_set_row_bnds(p, glpkConIndex, glpk.GLP_FX, rhs,rhs) elif constraint.is_increasing(): glpk.glp_set_row_bnds(p, glpkConIndex, glpk.GLP_UP, 0, rhs) elif constraint.is_decreasing(): glpk.glp_set_row_bnds(p, glpkConIndex, glpk.GLP_LO, rhs, 0) else: assert False, "Unexpected constraint relation." # Set coefficients for current row. # Note that GLPK requires a glpk.intArray containing column # indices and a glpk.doubleArray of same size containing the # coefficients for the listed column index. The first element # of both arrays (with index 0) is skipped by GLPK. glpkVarIndicesArray = glpk.intArray(numColumns + 1) for i in range(numColumns): glpkVarIndicesArray[i + 1] = glpkVarIndices[i] coefficientsArray = glpk.doubleArray(numColumns + 1) for i in range(numColumns): coefficientsArray[i + 1] = coefficients[i] glpk.glp_set_mat_row(p, glpkConIndex, numColumns, glpkVarIndicesArray, coefficientsArray) rowOffset += numRows
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)
def delete(self): if self._name is not None: swiglpk.glp_set_row_bnds(self._prob._p, self._name, swiglpk.GLP_FR, 0, 0)