def setFlowMaterialCoeff(self, flow, material, coefficient): if self._eqConstBuilt: if material not in self._materialIdxLookup: raise Exception("Invalid material") if flow not in self._flows: raise Exception("Invalid flow") materialIdx = self._materialIdxLookup[material] flowIdx = self._flows[flow] coeffs, flowIdxs = zip(*self._materialCoeffs[material]) coeffs = list(coeffs) flowLoc = flowIdxs.index(flowIdx) coeffs[flowLoc] = coefficient self._materialCoeffs[material] = zip(coeffs, flowIdxs) rowIdx = int(materialIdx + 1) length = len(flowIdxs) if length != len(coeffs): raise ValueError("Array sizes must match") colIdxs = _toIndexArray(flowIdxs) data = _toDoubleArray(coeffs) glp.glp_set_mat_row(self._lp, rowIdx, length, colIdxs, data) else: idx = self._getVar(flow) self._materialCoeffs[material].append((coefficient, idx)) self._solved = False
def set_linear_coefficients(self, coefficients): if self.problem is not None: problem = self.problem.problem self.problem.update() num_cols = glp_get_num_cols(problem) ia = intArray(num_cols + 1) va = doubleArray(num_cols + 1) num_rows = glp_get_mat_row(self.problem.problem, self._index, ia, va) variables_and_coefficients = {var.name: coeff for var, coeff in six.iteritems(coefficients)} final_variables_and_coefficients = { glp_get_col_name(problem, ia[i]): va[i] for i in range(1, num_rows + 1) } final_variables_and_coefficients.update(variables_and_coefficients) ia = intArray(num_cols + 1) va = doubleArray(num_cols + 1) for i, (name, coeff) in enumerate(six.iteritems(final_variables_and_coefficients)): ia[i + 1] = self.problem._variables[name]._index va[i + 1] = float(coeff) glp_set_mat_row(problem, self._index, len(final_variables_and_coefficients), ia, va) else: raise Exception("Can't change coefficients if constraint is not associated with a model.")
def set_constraints_csr(self, data, glpk_indices, indptr, shape): ''' set the constrains row by row to be equal to the passed-in csr matrix attribues glpk_indices is already offset by one ''' Timers.tic('set_constraints_csr') assert shape[0] <= self.get_num_rows() assert shape[1] <= self.get_num_cols() # actually set the constraints row by row assert isinstance(data, list), "data was not a list" for row in range(shape[0]): # we must copy the indices since glpk is offset by 1 :( count = int(indptr[row + 1] - indptr[row]) #indices_list = glpk_indices[indptr[row]:indptr[row+1]] #indices_vec = SwigArray.as_int_array(indices_list) indices_vec = SwigArray.as_int_array( glpk_indices[indptr[row]:indptr[row + 1]], count) #data_row_list = [float(d) for d in data[indptr[row]:indptr[row+1]]] #data_vec = SwigArray.as_double_array(data_row_list) data_vec = SwigArray.as_double_array( data[indptr[row]:indptr[row + 1]], count) glpk.glp_set_mat_row(self.lp, 1 + row, count, indices_vec, data_vec) Timers.toc('set_constraints_csr')
def set_linear_coefficients(self, coefficients): if self.problem is not None: problem = self.problem.problem num_cols = glp_get_num_cols(problem) ia = intArray(num_cols + 1) va = doubleArray(num_cols + 1) num_rows = glp_get_mat_row(self.problem.problem, self._index, ia, va) variables_and_coefficients = {var.name: coeff for var, coeff in six.iteritems(coefficients)} final_variables_and_coefficients = { glp_get_col_name(problem, ia[i]): va[i] for i in range(1, num_rows + 1) } final_variables_and_coefficients.update(variables_and_coefficients) ia = intArray(num_cols + 1) va = doubleArray(num_cols + 1) for i, (name, coeff) in enumerate(six.iteritems(final_variables_and_coefficients)): ia[i + 1] = self.problem._variables[name]._index va[i + 1] = coeff glp_set_mat_row(problem, self._index, len(final_variables_and_coefficients), ia, va) else: raise Exception("Can't change coefficients if constraint is not associated with a model.")
def set_constraints_swigvec_rows(self, data_vec_list, indices_vec_list, count_list, row_offset): '''An optimized / lower level way to set row constraints compared with set_constraints_csr The passed in fields are a list of swig data vector and indices vectors (one for each row), as well as a row offset. ''' Timers.tic('set_constraints_swigvec_rows') for row, (data, indices, count) in enumerate(zip(data_vec_list, indices_vec_list, count_list)): glpk.glp_set_mat_row(self.lp, 1 + row_offset + row, count, indices, data) Timers.toc('set_constraints_swigvec_rows')
def _add_constraints(self, constraints, sloppy=False): super(Model, self)._add_constraints(constraints, sloppy=sloppy) for constraint in constraints: constraint._problem = None # This needs to be dones in order to not trigger constraint._get_expression() glp_add_rows(self.problem, 1) index = glp_get_num_rows(self.problem) glp_set_row_name(self.problem, index, str(constraint.name)) num_cols = glp_get_num_cols(self.problem) index_array = intArray(num_cols + 1) value_array = doubleArray(num_cols + 1) num_vars = 0 # constraint.variables is too expensive for large problems if constraint.expression.is_Atom and constraint.expression.is_Symbol: var = constraint.expression index_array[1] = var.index value_array[1] = 1 num_vars += 1 elif constraint.expression.is_Mul: args = constraint.expression.args if len(args) > 2: raise Exception( "Term(s) %s from constraint %s is not a proper linear term." % (args, constraint)) coeff = float(args[0]) var = args[1] index_array[1] = var.index value_array[1] = coeff num_vars += 1 else: for i, term in enumerate(constraint.expression.args): args = term.args if args == (): assert term.is_Symbol coeff = 1 var = term elif len(args) == 2: assert args[0].is_Number assert args[1].is_Symbol var = args[1] coeff = float(args[0]) elif len(args) > 2: raise Exception( "Term %s from constraint %s is not a proper linear term." % (term, constraint)) index_array[i + 1] = var.index value_array[i + 1] = coeff num_vars += 1 glp_set_mat_row(self.problem, index, num_vars, index_array, value_array) constraint._problem = self self._glpk_set_row_bounds(constraint)
def set_constraints_csr(self, csr_mat, offset=None): '''set the constrains row by row to be equal to the passed-in csr matrix offset is an optional tuple (num_rows, num_cols) which tells you the top-left offset for the assignment ''' assert isinstance(csr_mat, csr_matrix) assert csr_mat.dtype == float if offset is None: offset = (0, 0) assert len( offset) == 2, "offset should be a 2-tuple (num_rows, num_cols)" # check that the matrix is in bounds lp_rows = self.get_num_rows() lp_cols = self.get_num_cols() if offset[0] < 0 or offset[1] < 0 or \ offset[0] + csr_mat.shape[0] > lp_rows or offset[1] + csr_mat.shape[1] > lp_cols: raise RuntimeError("Error: set constraints matrix out of bounds (offset was " + \ "{}, matrix size was {}), but lp size was ({}, {})".format( offset, csr_mat.shape, lp_rows, lp_cols)) # actually set the constraints row by row indptr = csr_mat.indptr indices = csr_mat.indices data_list = csr_mat.data.tolist() for row in range(csr_mat.shape[0]): # we must copy the indices since glpk is offset by 1 :( count = int(indptr[row + 1] - indptr[row]) indices_list = [ 1 + offset[1] + int(indices[index]) for index in range(indptr[row], indptr[row + 1]) ] indices_vec = SwigArray.as_int_array(indices_list) data_row_list = data_list[indptr[row]:indptr[row + 1]] data_vec = SwigArray.as_double_array(data_row_list) glpk.glp_set_mat_row(self.lp, offset[0] + row + 1, count, indices_vec, data_vec)
def _set_coefficients_low_level(self, variables_coefficients_dict): if self.problem is not None: problem = self.problem.problem indices_coefficients_dict = dict( [(variable.index, coefficient) for variable, coefficient in six.iteritems(variables_coefficients_dict)]) num_cols = glp_get_num_cols(problem) ia = intArray(num_cols + 1) da = doubleArray(num_cols + 1) index = self.index num = glp_get_mat_row(self.problem.problem, index, ia, da) for i in range(1, num + 1): try: da[i] = indices_coefficients_dict[ia[i]] except KeyError: pass glp_set_mat_row(self.problem.problem, index, num, ia, da) else: raise Exception( '_set_coefficients_low_level works only if a constraint is associated with a solver instance.')
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 _add_constraints(self, constraints, sloppy=False): super(Model, self)._add_constraints(constraints, sloppy=sloppy) for constraint in constraints: constraint._problem = None # This needs to be done in order to not trigger constraint._get_expression() glp_add_rows(self.problem, 1) index = glp_get_num_rows(self.problem) glp_set_row_name(self.problem, index, str(constraint.name)) num_cols = glp_get_num_cols(self.problem) index_array = intArray(num_cols + 1) value_array = doubleArray(num_cols + 1) num_vars = 0 # constraint.variables is too expensive for large problems offset, coef_dict, _ = parse_optimization_expression(constraint, linear=True) num_vars = len(coef_dict) for i, (var, coef) in enumerate(coef_dict.items()): index_array[i + 1] = var._index value_array[i + 1] = float(coef) glp_set_mat_row(self.problem, index, num_vars, index_array, value_array) constraint._problem = self self._glpk_set_row_bounds(constraint)
def add_dense_row(self, vec, rhs): ''' add a row from a dense nd.array, row <= rhs ''' Timers.tic('add_dense_row') assert isinstance(vec, np.ndarray) assert len(vec.shape) == 1 or vec.shape[0] == 1 assert len(vec) == self.get_num_cols( ), f"vec had {len(vec)} values, but lpi has {self.get_num_cols()} cols" rows_before = self.get_num_rows() self.add_rows_less_equal([rhs]) data_vec = SwigArray.as_double_array(vec, vec.size) indices_vec = SwigArray.get_sequential_int_array(vec.size) glpk.glp_set_mat_row(self.lp, rows_before + 1, vec.size, indices_vec, data_vec) Timers.toc('add_dense_row')
def _add_constraints(self, constraints, sloppy=False): super(Model, self)._add_constraints(constraints, sloppy=sloppy) for constraint in constraints: constraint._problem = None # This needs to be done in order to not trigger constraint._get_expression() glp_add_rows(self.problem, 1) index = glp_get_num_rows(self.problem) glp_set_row_name(self.problem, index, str(constraint.name)) num_cols = glp_get_num_cols(self.problem) index_array = intArray(num_cols + 1) value_array = doubleArray(num_cols + 1) num_vars = 0 # constraint.variables is too expensive for large problems coef_dict, _ = parse_optimization_expression(constraint, linear=True) num_vars = len(coef_dict) for i, (var, coef) in enumerate(coef_dict.items()): index_array[i + 1] = var._index value_array[i + 1] = float(coef) glp_set_mat_row(self.problem, index, num_vars, index_array, value_array) constraint._problem = self self._glpk_set_row_bounds(constraint)
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