def _apply_to(self, instance): """Apply the transformation. Args: instance (Block): the block on which to search for x == sum(var) constraints. Note that variables may be located anywhere in the model. Returns: None """ for constr in instance.component_data_objects(ctype=Constraint, active=True, descend_into=True): if not constr.body.polynomial_degree() == 1: continue # constraint not linear. Skip. repn = generate_canonical_repn(constr.body) if (constr.has_ub() and ( (repn.constant is None and value(constr.upper) == 0) or repn.constant == value(constr.upper) )): # term1 + term2 + term3 + ... <= 0 # all var terms need to be non-negative if all( # variable has 0 coefficient coef == 0 or # variable is non-negative and has non-negative coefficient (repn.variables[i].has_lb() and value(repn.variables[i].lb) >= 0 and coef >= 0) or # variable is non-positive and has non-positive coefficient (repn.variables[i].has_ub() and value(repn.variables[i].ub) <= 0 and coef <= 0) for i, coef in enumerate(repn.linear)): for i, coef in enumerate(repn.linear): if not coef == 0: repn.variables[i].fix(0) continue if (constr.has_lb() and ( (repn.constant is None and value(constr.lower) == 0) or repn.constant == value(constr.lower) )): # term1 + term2 + term3 + ... >= 0 # all var terms need to be non-positive if all( # variable has 0 coefficient coef == 0 or # variable is non-negative and has non-positive coefficient (repn.variables[i].has_lb() and value(repn.variables[i].lb) >= 0 and coef <= 0) or # variable is non-positive and has non-negative coefficient (repn.variables[i].has_ub() and value(repn.variables[i].ub) <= 0 and coef >= 0) for i, coef in enumerate(repn.linear)): for i, coef in enumerate(repn.linear): if not coef == 0: repn.variables[i].fix(0)
def _apply_to(self, model): """Apply the transformation.""" m = model for constr in m.component_data_objects(ctype=Constraint, active=True, descend_into=True): if not constr.body.polynomial_degree() == 1: continue # we currently only process linear constraints repn = generate_canonical_repn(constr.body) # get the index of all nonzero coefficient variables nonzero_vars_indx = [ i for i, _ in enumerate(repn.variables) if not repn.linear[i] == 0 ] const = repn.constant if repn.constant is not None else 0 # reconstitute the constraint, including only variable terms with # nonzero coefficients constr_body = sum(repn.linear[i] * repn.variables[i] for i in nonzero_vars_indx) + const if constr.equality: constr.set_value(constr_body == constr.upper) elif constr.has_lb() and not constr.has_ub(): constr.set_value(constr_body >= constr.lower) elif constr.has_ub() and not constr.has_lb(): constr.set_value(constr_body <= constr.upper) else: # constraint is a bounded inequality of form a <= x <= b. # I don't think this is a great idea, but ¯\_(ツ)_/¯ constr.set_value(constr.lower <= constr_body <= constr.upper)
def _build_equality_set(m): """Construct an equality set map. Maps all variables to the set of variables that are linked to them by equality. Mapping takes place using id(). That is, if you have x = y, then you would have id(x) -> ComponentSet([x, y]) and id(y) -> ComponentSet([x, y]) in the mapping. """ #: dict: map of var UID to the set of all equality-linked var UIDs eq_var_map = ComponentMap() relevant_vars = ComponentSet() for constr in m.component_data_objects(ctype=Constraint, active=True, descend_into=True): # Check to make sure the constraint is of form v1 - v2 == 0 if (value(constr.lower) == 0 and value(constr.upper) == 0 and constr.body.polynomial_degree() == 1): repn = generate_canonical_repn(constr.body) # only take the variables with nonzero coefficients vars_ = [v for i, v in enumerate(repn.variables) if repn.linear[i]] if (len(vars_) == 2 and sorted(l for l in repn.linear if l) == [-1, 1]): # this is an a == b constraint. v1 = vars_[0] v2 = vars_[1] set1 = eq_var_map.get(v1, ComponentSet([v1])) set2 = eq_var_map.get(v2, ComponentSet([v2])) relevant_vars.update([v1, v2]) set1.update(set2) # set1 is now the union for v in set1: eq_var_map[v] = set1 return eq_var_map, relevant_vars
def generate_ampl_repn(exp, idMap=None): # We need to do this not at the global scope in case someone changed # the mode after importing the environment. _using_pyomo4_trees = expr_common.mode == expr_common.Mode.pyomo4_trees if idMap is None: idMap = {} if exp is None: return AmplRepn() degree = exp.polynomial_degree() if (degree is None) or (degree > 1): repn = _generate_ampl_repn(exp) repn.compress() elif degree == 0: repn = AmplRepn() repn._constant = value(exp) # compress repn._linear_vars = tuple() repn._linear_terms_coef = tuple() repn._nonlinear_vars = tuple() else: # degree == 1 repn = AmplRepn() if _using_pyomo4_trees: canonical_repn = generate_canonical_repn(exp, idMap=idMap) # compress repn._nonlinear_vars = tuple() repn._constant = value(canonical_repn.constant) repn._linear_vars = tuple(canonical_repn.variables) repn._linear_terms_coef = tuple( value(_v) for _v in canonical_repn.linear) else: # compress repn._linear_vars = tuple() repn._linear_terms_coef = tuple() repn._nonlinear_vars = tuple() coef, varmap = collect_linear_canonical_repn(exp, idMap=idMap) if None in coef: val = coef.pop(None) if val: repn._constant = val # the six module is inefficient in terms of wrapping # iterkeys and itervalues, in the context of Python # 2.7. use the native dictionary methods where # possible. if using_py3 is False: repn._linear_terms_coef = tuple(val for val in coef.itervalues() if val) repn._linear_vars = tuple( (varmap[var_hash] for var_hash, val in coef.iteritems() if val)) else: repn._linear_terms_coef = tuple(val for val in coef.values() if val) repn._linear_vars = tuple((varmap[var_hash] for var_hash, val in coef.items() if val)) return repn
def generate_ampl_repn(exp, idMap=None): # We need to do this not at the global scope in case someone changed # the mode after importing the environment. _using_pyomo4_trees = expr_common.mode == expr_common.Mode.pyomo4_trees _using_pyomo5_trees = expr_common.mode == expr_common.Mode.pyomo5_trees if idMap is None: idMap = {} if _using_pyomo5_trees: from pyomo.repn.standard_repn import generate_standard_repn return generate_standard_repn(exp, quadratic=False) if exp is None: return AmplRepn() degree = exp.polynomial_degree() if (degree is None) or (degree > 1): repn = _generate_ampl_repn(exp) repn.compress() elif degree == 0: repn = AmplRepn() repn._constant = value(exp) # compress repn._linear_vars = tuple() repn._linear_terms_coef = tuple() repn._nonlinear_vars = tuple() else: # degree == 1 repn = AmplRepn() if _using_pyomo4_trees: canonical_repn = generate_canonical_repn(exp, idMap=idMap) # compress repn._nonlinear_vars = tuple() repn._constant = value(canonical_repn.constant) repn._linear_vars = tuple(canonical_repn.variables) repn._linear_terms_coef = tuple(value(_v) for _v in canonical_repn.linear) else: # compress repn._linear_vars = tuple() repn._linear_terms_coef = tuple() repn._nonlinear_vars = tuple() coef, varmap = collect_linear_canonical_repn(exp, idMap=idMap) if None in coef: val = coef.pop(None) if val: repn._constant = val # the six module is inefficient in terms of wrapping # iterkeys and itervalues, in the context of Python # 2.7. use the native dictionary methods where # possible. if using_py3 is False: repn._linear_terms_coef = tuple(val for val in coef.itervalues() if val) repn._linear_vars = tuple((varmap[var_hash] for var_hash,val in coef.iteritems() if val)) else: repn._linear_terms_coef = tuple(val for val in coef.values() if val) repn._linear_vars = tuple((varmap[var_hash] for var_hash,val in coef.items() if val)) return repn
def _apply_to(self, model): """Apply the transformation to the given model.""" m = model for constr in m.component_data_objects(ctype=Constraint, active=True, descend_into=True): # Check if the constraint is k * x + c1 <= c2 or c2 <= k * x + c1 if constr.body.polynomial_degree() == 1: repn = generate_canonical_repn(constr.body) if repn.variables is not None and len(repn.variables) == 1: var = repn.variables[0] const = repn.constant if repn.constant is not None else 0 coef = float(repn.linear[0]) if coef == 0: # This can happen when a one element of a bilinear term # is fixed to zero. Obviously, do not divide by zero. pass else: if constr.upper is not None: newbound = (value(constr.upper) - const) / coef if coef > 0: var.setub( min(var.ub, newbound) if var. ub is not None else newbound) elif coef < 0: var.setlb( max(var.lb, newbound) if var. lb is not None else newbound) if constr.lower is not None: newbound = (value(constr.lower) - const) / coef if coef > 0: var.setlb( max(var.lb, newbound) if var. lb is not None else newbound) elif coef < 0: var.setub( min(var.ub, newbound) if var. ub is not None else newbound) constr.deactivate() # Sometimes deactivating the constraint will remove a # variable from all active constraints, so that it won't be # updated during the optimization. Therefore, we need to # shift the value of var as necessary in order to keep it # within its implied bounds, as the constraint we are # deactivating is not an invalid constraint, but rather we # are moving its implied bound directly onto the variable. if (var.has_lb() and var.value is not None and var.value < var.lb): var.set_value(var.lb) if (var.has_ub() and var.value is not None and var.value > var.ub): var.set_value(var.ub)
def _detect_fixed_variables(m): """Detect fixed variables due to constraints of form var = const.""" new_fixed_vars = ComponentSet() for constr in m.component_data_objects(ctype=Constraint, active=True, descend_into=True): if constr.equality and constr.body.polynomial_degree() == 1: repn = generate_canonical_repn(constr.body) if len(repn.variables) == 1 and repn.linear[0]: var = repn.variables[0] coef = float(repn.linear[0]) const = repn.constant if repn.constant is not None else 0 var_val = (value(constr.lower) - value(const)) / coef var.fix(var_val) new_fixed_vars.add(var) return new_fixed_vars
def generate_ampl_repn(exp, idMap=None): if idMap is None: idMap = {} degree = exp.polynomial_degree() if (degree is None) or (degree > 1): repn = _generate_ampl_repn(exp) repn.compress() elif degree == 0: repn = AmplRepn() repn._constant = value(exp) # compress repn._linear_vars = tuple() repn._linear_terms_coef = tuple() repn._nonlinear_vars = tuple() else: # degree == 1 repn = AmplRepn() if _using_pyomo4_trees: canonical_repn = generate_canonical_repn(exp, idMap=idMap) # compress repn._nonlinear_vars = tuple() repn._constant = value(canonical_repn.constant) repn._linear_vars = tuple(canonical_repn.variables) repn._linear_terms_coef = tuple(value(_v) for _v in canonical_repn.linear) else: # compress repn._linear_vars = tuple() repn._linear_terms_coef = tuple() repn._nonlinear_vars = tuple() coef, varmap = collect_linear_canonical_repn(exp, idMap=idMap) if None in coef: val = coef.pop(None) if val: repn._constant = val # the six module is inefficient in terms of wrapping # iterkeys and itervalues, in the context of Python # 2.7. use the native dictionary methods where # possible. if using_py3 is False: repn._linear_terms_coef = tuple(val for val in coef.itervalues() if val) repn._linear_vars = tuple((varmap[var_hash] for var_hash,val in coef.iteritems() if val)) else: repn._linear_terms_coef = tuple(val for val in coef.values() if val) repn._linear_vars = tuple((varmap[var_hash] for var_hash,val in coef.items() if val)) return repn
def compile_block_linear_constraints(parent_block, constraint_name, skip_trivial_constraints=False, single_precision_storage=False, verbose=False, descend_into=True): if verbose: print("") print("Compiling linear constraints on block with name: %s" % (parent_block.name)) if not parent_block.is_constructed(): raise RuntimeError( "Attempting to compile block '%s' with unconstructed " "component(s)" % (parent_block.name)) # # Linear MatrixConstraint in CSR format # SparseMat_pRows = [] SparseMat_jCols = [] SparseMat_Vals = [] Ranges = [] RangeTypes = [] def _get_bound(exp): if exp is None: return None if is_fixed(exp): return value(exp) raise ValueError("non-fixed bound: " + str(exp)) start_time = time.time() if verbose: print("Sorting active blocks...") sortOrder = SortComponents.indices | SortComponents.alphabetical all_blocks = [ _b for _b in parent_block.block_data_objects( active=True, sort=sortOrder, descend_into=descend_into) ] stop_time = time.time() if verbose: print("Time to sort active blocks: %.2f seconds" % (stop_time - start_time)) start_time = time.time() if verbose: print("Collecting variables on active blocks...") # # First Pass: assign each variable a deterministic id # (an index in a list) # VarSymbolToVarObject = [] for block in all_blocks: VarSymbolToVarObject.extend( block.component_data_objects(Var, sort=sortOrder, descend_into=False)) VarIDToVarSymbol = \ dict((id(vardata), index) for index, vardata in enumerate(VarSymbolToVarObject)) stop_time = time.time() if verbose: print("Time to collect variables on active blocks: %.2f seconds" % (stop_time - start_time)) start_time = time.time() if verbose: print("Compiling active linear constraints...") # # Second Pass: collect and remove active linear constraints # constraint_data_to_remove = [] empty_constraint_containers_to_remove = [] constraint_containers_to_remove = [] constraint_containers_to_check = set() referenced_variable_symbols = set() nnz = 0 nrows = 0 SparseMat_pRows = [0] for block in all_blocks: if hasattr(block, '_canonical_repn'): del block._canonical_repn if hasattr(block, '_ampl_repn'): del block._ampl_repn for constraint in block.component_objects(Constraint, active=True, sort=sortOrder, descend_into=False): assert not isinstance(constraint, MatrixConstraint) if len(constraint) == 0: empty_constraint_containers_to_remove.append( (block, constraint)) else: singleton = isinstance(constraint, SimpleConstraint) for index, constraint_data in iteritems(constraint): if constraint_data.body.polynomial_degree() <= 1: # collect for removal if singleton: constraint_containers_to_remove.append( (block, constraint)) else: constraint_data_to_remove.append( (constraint, index)) constraint_containers_to_check.add( (block, constraint)) canonical_repn = generate_canonical_repn( constraint_data.body) assert isinstance(canonical_repn, LinearCanonicalRepn) row_variable_symbols = [] row_coefficients = [] if canonical_repn.variables is None: if skip_trivial_constraints: continue else: row_variable_symbols = \ [VarIDToVarSymbol[id(vardata)] for vardata in canonical_repn.variables] referenced_variable_symbols.update( row_variable_symbols) assert canonical_repn.linear is not None row_coefficients = canonical_repn.linear SparseMat_pRows.append(SparseMat_pRows[-1] + \ len(row_variable_symbols)) SparseMat_jCols.extend(row_variable_symbols) SparseMat_Vals.extend(row_coefficients) nnz += len(row_variable_symbols) nrows += 1 L = _get_bound(constraint_data.lower) U = _get_bound(constraint_data.upper) constant = value(canonical_repn.constant) if constant is None: constant = 0 Ranges.append(L - constant if (L is not None) else 0) Ranges.append(U - constant if (U is not None) else 0) if (L is not None) and \ (U is not None) and \ (not constraint_data.equality): RangeTypes.append(MatrixConstraint.LowerBound | MatrixConstraint.UpperBound) elif constraint_data.equality: RangeTypes.append(MatrixConstraint.Equality) elif L is not None: assert U is None RangeTypes.append(MatrixConstraint.LowerBound) else: assert U is not None RangeTypes.append(MatrixConstraint.UpperBound) # Start freeing up memory constraint_data.set_value(None) ncols = len(referenced_variable_symbols) stop_time = time.time() if verbose: print("Time to compile active linear constraints: %.2f seconds" % (stop_time - start_time)) start_time = time.time() if verbose: print("Removing compiled constraint objects...") # # Remove compiled constraints # constraints_removed = 0 constraint_containers_removed = 0 for block, constraint in empty_constraint_containers_to_remove: block.del_component(constraint) constraint_containers_removed += 1 for constraint, index in constraint_data_to_remove: del constraint[index] constraints_removed += 1 for block, constraint in constraint_containers_to_remove: block.del_component(constraint) constraints_removed += 1 constraint_containers_removed += 1 for block, constraint in constraint_containers_to_check: if len(constraint) == 0: block.del_component(constraint) constraint_containers_removed += 1 stop_time = time.time() if verbose: print("Eliminated %s constraints and %s Constraint container objects" % (constraints_removed, constraint_containers_removed)) print("Time to remove compiled constraint objects: %.2f seconds" % (stop_time - start_time)) start_time = time.time() if verbose: print("Assigning variable column indices...") # # Assign a column index to the set of referenced variables # ColumnIndexToVarSymbol = sorted(referenced_variable_symbols) VarSymbolToColumnIndex = dict( (symbol, column) for column, symbol in enumerate(ColumnIndexToVarSymbol)) SparseMat_jCols = [ VarSymbolToColumnIndex[symbol] for symbol in SparseMat_jCols ] del VarSymbolToColumnIndex ColumnIndexToVarObject = [ VarSymbolToVarObject[var_symbol] for var_symbol in ColumnIndexToVarSymbol ] stop_time = time.time() if verbose: print("Time to assign variable column indices: %.2f seconds" % (stop_time - start_time)) start_time = time.time() if verbose: print("Converting compiled constraint data to array storage...") print(" - Using %s precision for numeric values" % ('single' if single_precision_storage else 'double')) # # Convert to array storage # number_storage = 'f' if single_precision_storage else 'd' SparseMat_pRows = array.array('L', SparseMat_pRows) SparseMat_jCols = array.array('L', SparseMat_jCols) SparseMat_Vals = array.array(number_storage, SparseMat_Vals) Ranges = array.array(number_storage, Ranges) RangeTypes = array.array('B', RangeTypes) stop_time = time.time() if verbose: storage_bytes = \ SparseMat_pRows.buffer_info()[1] * SparseMat_pRows.itemsize + \ SparseMat_jCols.buffer_info()[1] * SparseMat_jCols.itemsize + \ SparseMat_Vals.buffer_info()[1] * SparseMat_Vals.itemsize + \ Ranges.buffer_info()[1] * Ranges.itemsize + \ RangeTypes.buffer_info()[1] * RangeTypes.itemsize print("Sparse Matrix Dimension:") print(" - Rows: " + str(nrows)) print(" - Cols: " + str(ncols)) print(" - Nonzeros: " + str(nnz)) print("Compiled Data Storage: " + str(_label_bytes(storage_bytes))) print("Time to convert compiled constraint data to " "array storage: %.2f seconds" % (stop_time - start_time)) parent_block.add_component( constraint_name, MatrixConstraint(nrows, ncols, nnz, SparseMat_pRows, SparseMat_jCols, SparseMat_Vals, Ranges, RangeTypes, ColumnIndexToVarObject))
def compile_block_linear_constraints(parent_block, constraint_name, skip_trivial_constraints=False, single_precision_storage=False, verbose=False, descend_into=True): if verbose: print("") print("Compiling linear constraints on block with name: %s" % (parent_block.name)) if not parent_block.is_constructed(): raise RuntimeError( "Attempting to compile block '%s' with unconstructed " "component(s)" % (parent_block.name)) # # Linear MatrixConstraint in CSR format # SparseMat_pRows = [] SparseMat_jCols = [] SparseMat_Vals = [] Ranges = [] RangeTypes = [] def _get_bound(exp): if exp is None: return None if is_fixed(exp): return value(exp) raise ValueError("non-fixed bound: " + str(exp)) start_time = time.time() if verbose: print("Sorting active blocks...") sortOrder = SortComponents.indices | SortComponents.alphabetical all_blocks = [_b for _b in parent_block.block_data_objects( active=True, sort=sortOrder, descend_into=descend_into)] stop_time = time.time() if verbose: print("Time to sort active blocks: %.2f seconds" % (stop_time-start_time)) start_time = time.time() if verbose: print("Collecting variables on active blocks...") # # First Pass: assign each variable a deterministic id # (an index in a list) # VarSymbolToVarObject = [] for block in all_blocks: VarSymbolToVarObject.extend( block.component_data_objects(Var, sort=sortOrder, descend_into=False)) VarIDToVarSymbol = \ dict((id(vardata), index) for index, vardata in enumerate(VarSymbolToVarObject)) stop_time = time.time() if verbose: print("Time to collect variables on active blocks: %.2f seconds" % (stop_time-start_time)) start_time = time.time() if verbose: print("Compiling active linear constraints...") # # Second Pass: collect and remove active linear constraints # constraint_data_to_remove = [] empty_constraint_containers_to_remove = [] constraint_containers_to_remove = [] constraint_containers_to_check = set() referenced_variable_symbols = set() nnz = 0 nrows = 0 SparseMat_pRows = [0] for block in all_blocks: if hasattr(block, '_canonical_repn'): del block._canonical_repn if hasattr(block, '_ampl_repn'): del block._ampl_repn for constraint in block.component_objects(Constraint, active=True, sort=sortOrder, descend_into=False): assert not isinstance(constraint, MatrixConstraint) if len(constraint) == 0: empty_constraint_containers_to_remove.append((block, constraint)) else: singleton = isinstance(constraint, SimpleConstraint) for index, constraint_data in iteritems(constraint): if constraint_data.body.polynomial_degree() <= 1: # collect for removal if singleton: constraint_containers_to_remove.append((block, constraint)) else: constraint_data_to_remove.append((constraint, index)) constraint_containers_to_check.add((block, constraint)) canonical_repn = generate_canonical_repn(constraint_data.body) assert isinstance(canonical_repn, LinearCanonicalRepn) row_variable_symbols = [] row_coefficients = [] if canonical_repn.variables is None: if skip_trivial_constraints: continue else: row_variable_symbols = \ [VarIDToVarSymbol[id(vardata)] for vardata in canonical_repn.variables] referenced_variable_symbols.update( row_variable_symbols) assert canonical_repn.linear is not None row_coefficients = canonical_repn.linear SparseMat_pRows.append(SparseMat_pRows[-1] + \ len(row_variable_symbols)) SparseMat_jCols.extend(row_variable_symbols) SparseMat_Vals.extend(row_coefficients) nnz += len(row_variable_symbols) nrows += 1 L = _get_bound(constraint_data.lower) U = _get_bound(constraint_data.upper) constant = value(canonical_repn.constant) if constant is None: constant = 0 Ranges.append(L - constant if (L is not None) else 0) Ranges.append(U - constant if (U is not None) else 0) if (L is not None) and \ (U is not None) and \ (not constraint_data.equality): RangeTypes.append(MatrixConstraint.LowerBound | MatrixConstraint.UpperBound) elif constraint_data.equality: RangeTypes.append(MatrixConstraint.Equality) elif L is not None: assert U is None RangeTypes.append(MatrixConstraint.LowerBound) else: assert U is not None RangeTypes.append(MatrixConstraint.UpperBound) # Start freeing up memory constraint_data.set_value(None) ncols = len(referenced_variable_symbols) stop_time = time.time() if verbose: print("Time to compile active linear constraints: %.2f seconds" % (stop_time-start_time)) start_time = time.time() if verbose: print("Removing compiled constraint objects...") # # Remove compiled constraints # constraints_removed = 0 constraint_containers_removed = 0 for block, constraint in empty_constraint_containers_to_remove: block.del_component(constraint) constraint_containers_removed += 1 for constraint, index in constraint_data_to_remove: del constraint[index] constraints_removed += 1 for block, constraint in constraint_containers_to_remove: block.del_component(constraint) constraints_removed += 1 constraint_containers_removed += 1 for block, constraint in constraint_containers_to_check: if len(constraint) == 0: block.del_component(constraint) constraint_containers_removed += 1 stop_time = time.time() if verbose: print("Eliminated %s constraints and %s Constraint container objects" % (constraints_removed, constraint_containers_removed)) print("Time to remove compiled constraint objects: %.2f seconds" % (stop_time-start_time)) start_time = time.time() if verbose: print("Assigning variable column indices...") # # Assign a column index to the set of referenced variables # ColumnIndexToVarSymbol = sorted(referenced_variable_symbols) VarSymbolToColumnIndex = dict((symbol, column) for column, symbol in enumerate(ColumnIndexToVarSymbol)) SparseMat_jCols = [VarSymbolToColumnIndex[symbol] for symbol in SparseMat_jCols] del VarSymbolToColumnIndex ColumnIndexToVarObject = [VarSymbolToVarObject[var_symbol] for var_symbol in ColumnIndexToVarSymbol] stop_time = time.time() if verbose: print("Time to assign variable column indices: %.2f seconds" % (stop_time-start_time)) start_time = time.time() if verbose: print("Converting compiled constraint data to array storage...") print(" - Using %s precision for numeric values" % ('single' if single_precision_storage else 'double')) # # Convert to array storage # number_storage = 'f' if single_precision_storage else 'd' SparseMat_pRows = array.array('L', SparseMat_pRows) SparseMat_jCols = array.array('L', SparseMat_jCols) SparseMat_Vals = array.array(number_storage, SparseMat_Vals) Ranges = array.array(number_storage, Ranges) RangeTypes = array.array('B', RangeTypes) stop_time = time.time() if verbose: storage_bytes = \ SparseMat_pRows.buffer_info()[1] * SparseMat_pRows.itemsize + \ SparseMat_jCols.buffer_info()[1] * SparseMat_jCols.itemsize + \ SparseMat_Vals.buffer_info()[1] * SparseMat_Vals.itemsize + \ Ranges.buffer_info()[1] * Ranges.itemsize + \ RangeTypes.buffer_info()[1] * RangeTypes.itemsize print("Sparse Matrix Dimension:") print(" - Rows: "+str(nrows)) print(" - Cols: "+str(ncols)) print(" - Nonzeros: "+str(nnz)) print("Compiled Data Storage: "+str(_label_bytes(storage_bytes))) print("Time to convert compiled constraint data to " "array storage: %.2f seconds" % (stop_time-start_time)) parent_block.add_component(constraint_name, MatrixConstraint(nrows, ncols, nnz, SparseMat_pRows, SparseMat_jCols, SparseMat_Vals, Ranges, RangeTypes, ColumnIndexToVarObject))
def to_matrix_form(model): """ Converts a concrete Pyomo model with a linear objective and linear constraints into matrix form. Args: model: A concrete Pyomo model. Returns: Objects that define the following LP representation: min(max) c0 + c^T x s.t. bL <= Ax <= bU xL <= x <= xU where, c0: scalar representing the aggregation of all constants found in the objective expression c: nvars-length list of objective coefficients. bL: ncons-length list of constraint upper bounds bU: ncons-length list of constraint lower bounds A: 3-tuple consisting of list objects (data, indices, indptr) defining a sparse matrix in Compressed Sparse Row format xL: nvars-length list of variable lower bounds xU: nvars-length list of variable upper bounds In addition, the following mapping objects are returned: vartocol: maps model variable objects to their integer column index in the A matrix. E.g., vartocol[model.x[5]] # -> 0 contorow: maps model constraint objects to their integer row index in the A matrix. E.g., contorow[model.c] # -> 19 All variable and constraint bound vectors will contain values of float('-inf') and float('inf') where the corresponding bound does not exist. """ # Assign each variable a deterministic symbol (an index # in a list) so that we can guarantee the same matrix # ordering for a given Pyomo model. We can not assign a # column index until after collecting the list of # variables that are actually used. sortOrder = (SortComponents.indices | SortComponents.alphabetical) all_blocks = [_b for _b in model.block_data_objects( active=True, sort=sortOrder)] VarSymbolToVarObject = [] for block in all_blocks: VarSymbolToVarObject.extend( block.component_data_objects( Var, sort=sortOrder, descend_into=False)) VarIDToVarSymbol = \ dict((id(var), index) for index, var in enumerate(VarSymbolToVarObject)) # Loop over objective and constraints to generate the # cost vector and matrix rows. Raise an exception if any # nonlinear expressions are encountered. negative_infinity = float('-inf') positive_infinity = float('inf') nobjs = 0 referenced_var_symbols = set() A_indptr = [0] A_indices = [] A_data = [] bL = [] bU = [] c_sparse = {} c0 = 0.0 RowIndexToConstraintObject = [] for block in all_blocks: for objective in block.component_data_objects( Objective, active=True, sort=sortOrder, descend_into=False): nobjs += 1 if nobjs > 1: raise ValueError( "This function does not support " "multiple objectives") polynomial_degree = \ objective.expr.polynomial_degree() if (polynomial_degree != 0) and \ (polynomial_degree != 1): raise ValueError( "This function does not support " "nonlinear objectives") canonical_repn = \ generate_canonical_repn(objective.expr) variables = canonical_repn.variables coefficients = canonical_repn.linear if variables is not None: for var, coef in zip(variables, coefficients): var_symbol = VarIDToVarSymbol[id(var)] c_sparse[var_symbol] = coef referenced_var_symbols.add(var_symbol) if canonical_repn.constant is not None: c0 = value(canonical_repn.constant) for sosconstraint in block.component_data_objects( SOSConstraint, active=True, sort=sortOrder, descend_into=False): raise ValueError("This function does not " "support SOSConstraints") for constraint in block.component_data_objects( Constraint, active=True, sort=sortOrder, descend_into=False): polynomial_degree = \ constraint.body.polynomial_degree() if (polynomial_degree != 0) and \ (polynomial_degree != 1): raise ValueError( "This function does not support " "nonlinear constraints") RowIndexToConstraintObject.append(constraint) canonical_repn = \ generate_canonical_repn(constraint.body) variables = canonical_repn.variables coefficients = canonical_repn.linear row_variable_symbols = [] row_coefficients = [] if variables is not None: row_variable_symbols = \ [VarIDToVarSymbol[id(var)] for var in variables] referenced_var_symbols.\ update(row_variable_symbols) row_coefficients = coefficients A_indptr.append(A_indptr[-1] + len(row_variable_symbols)) A_indices.extend(row_variable_symbols) A_data.extend(row_coefficients) L = negative_infinity U = positive_infinity constant = 0.0 if constraint.lower is not None: L = value(constraint.lower) if constraint.upper is not None: U = value(constraint.upper) if canonical_repn.constant is not None: constant = value(canonical_repn.constant) bL.append(L - constant) bU.append(U - constant) ncols = len(referenced_var_symbols) # Assign a column index to the set of referenced # variables ColumnIndexToVarSymbol = sorted(referenced_var_symbols) VarSymbolToColumnIndex = \ dict((symbol, col) for col, symbol in enumerate(ColumnIndexToVarSymbol)) A_indices = [VarSymbolToColumnIndex[symbol] for symbol in A_indices] ColumnIndexToVarObject = \ [VarSymbolToVarObject[var_symbol] for var_symbol in ColumnIndexToVarSymbol] # Convert the sparse cost vector into a dense list based # on the variable column id assignments. c = [0.0 for j in range(ncols)] for var_symbol, coef in c_sparse.items(): c[VarSymbolToColumnIndex[var_symbol]] = coef # Generate dense xL and xU variable bound lists based on # the variable column id assignments xL = [negative_infinity for j in range(ncols)] xU = [positive_infinity for j in range(ncols)] for j, var in enumerate(ColumnIndexToVarObject): if var.lb is not None: xL[j] = value(var.lb) if var.ub is not None: xU[j] = value(var.ub) # Generate the component maps that allow one to recover # the row/column index from a constraint/variable # object. The reverse maps are easy enough to generate # from these two maps if needed. vartocol = ComponentMap( (var, j) for j, var in enumerate(ColumnIndexToVarObject)) contorow = ComponentMap( (con, i) for i, con in enumerate(RowIndexToConstraintObject)) return (c0, c, bL, bU, (A_data, A_indices, A_indptr), xL, xU, vartocol, contorow)
def to_matrix_form(model): """ Converts a concrete Pyomo model with a linear objective and linear constraints into matrix form. Args: model: A concrete Pyomo model. Returns: Objects that define the following LP representation: min(max) c0 + c^T x s.t. bL <= Ax <= bU xL <= x <= xU where, c0: scalar representing the aggregation of all constants found in the objective expression c: nvars-length list of objective coefficients. bL: ncons-length list of constraint upper bounds bU: ncons-length list of constraint lower bounds A: 3-tuple consisting of list objects (data, indices, indptr) defining a sparse matrix in Compressed Sparse Row format xL: nvars-length list of variable lower bounds xU: nvars-length list of variable upper bounds In addition, the following mapping objects are returned: vartocol: maps model variable objects to their integer column index in the A matrix. E.g., vartocol[model.x[5]] # -> 0 contorow: maps model constraint objects to their integer row index in the A matrix. E.g., contorow[model.c] # -> 19 All variable and constraint bound vectors will contain values of float('-inf') and float('inf') where the corresponding bound does not exist. """ # Assign each variable a deterministic symbol (an index # in a list) so that we can guarantee the same matrix # ordering for a given Pyomo model. We can not assign a # column index until after collecting the list of # variables that are actually used. sortOrder = (SortComponents.indices | SortComponents.alphabetical) all_blocks = [ _b for _b in model.block_data_objects(active=True, sort=sortOrder) ] VarSymbolToVarObject = [] for block in all_blocks: VarSymbolToVarObject.extend( block.component_data_objects(Var, sort=sortOrder, descend_into=False)) VarIDToVarSymbol = \ dict((id(var), index) for index, var in enumerate(VarSymbolToVarObject)) # Loop over objective and constraints to generate the # cost vector and matrix rows. Raise an exception if any # nonlinear expressions are encountered. negative_infinity = float('-inf') positive_infinity = float('inf') nobjs = 0 referenced_var_symbols = set() A_indptr = [0] A_indices = [] A_data = [] bL = [] bU = [] c_sparse = {} c0 = 0.0 RowIndexToConstraintObject = [] for block in all_blocks: for objective in block.component_data_objects(Objective, active=True, sort=sortOrder, descend_into=False): nobjs += 1 if nobjs > 1: raise ValueError("This function does not support " "multiple objectives") polynomial_degree = \ objective.expr.polynomial_degree() if (polynomial_degree != 0) and \ (polynomial_degree != 1): raise ValueError("This function does not support " "nonlinear objectives") canonical_repn = \ generate_canonical_repn(objective.expr) variables = canonical_repn.variables coefficients = canonical_repn.linear if variables is not None: for var, coef in zip(variables, coefficients): var_symbol = VarIDToVarSymbol[id(var)] c_sparse[var_symbol] = coef referenced_var_symbols.add(var_symbol) if canonical_repn.constant is not None: c0 = value(canonical_repn.constant) for sosconstraint in block.component_data_objects(SOSConstraint, active=True, sort=sortOrder, descend_into=False): raise ValueError("This function does not " "support SOSConstraints") for constraint in block.component_data_objects(Constraint, active=True, sort=sortOrder, descend_into=False): polynomial_degree = \ constraint.body.polynomial_degree() if (polynomial_degree != 0) and \ (polynomial_degree != 1): raise ValueError("This function does not support " "nonlinear constraints") RowIndexToConstraintObject.append(constraint) canonical_repn = \ generate_canonical_repn(constraint.body) variables = canonical_repn.variables coefficients = canonical_repn.linear row_variable_symbols = [] row_coefficients = [] if variables is not None: row_variable_symbols = \ [VarIDToVarSymbol[id(var)] for var in variables] referenced_var_symbols.\ update(row_variable_symbols) row_coefficients = coefficients A_indptr.append(A_indptr[-1] + len(row_variable_symbols)) A_indices.extend(row_variable_symbols) A_data.extend(row_coefficients) L = negative_infinity U = positive_infinity constant = 0.0 if constraint.lower is not None: L = value(constraint.lower) if constraint.upper is not None: U = value(constraint.upper) if canonical_repn.constant is not None: constant = value(canonical_repn.constant) bL.append(L - constant) bU.append(U - constant) ncols = len(referenced_var_symbols) # Assign a column index to the set of referenced # variables ColumnIndexToVarSymbol = sorted(referenced_var_symbols) VarSymbolToColumnIndex = \ dict((symbol, col) for col, symbol in enumerate(ColumnIndexToVarSymbol)) A_indices = [VarSymbolToColumnIndex[symbol] for symbol in A_indices] ColumnIndexToVarObject = \ [VarSymbolToVarObject[var_symbol] for var_symbol in ColumnIndexToVarSymbol] # Convert the sparse cost vector into a dense list based # on the variable column id assignments. c = [0.0 for j in range(ncols)] for var_symbol, coef in c_sparse.items(): c[VarSymbolToColumnIndex[var_symbol]] = coef # Generate dense xL and xU variable bound lists based on # the variable column id assignments xL = [negative_infinity for j in range(ncols)] xU = [positive_infinity for j in range(ncols)] for j, var in enumerate(ColumnIndexToVarObject): if var.lb is not None: xL[j] = value(var.lb) if var.ub is not None: xU[j] = value(var.ub) # Generate the component maps that allow one to recover # the row/column index from a constraint/variable # object. The reverse maps are easy enough to generate # from these two maps if needed. vartocol = ComponentMap( (var, j) for j, var in enumerate(ColumnIndexToVarObject)) contorow = ComponentMap( (con, i) for i, con in enumerate(RowIndexToConstraintObject)) return (c0, c, bL, bU, (A_data, A_indices, A_indptr), xL, xU, vartocol, contorow)