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_standard_repn(constr.body) # only take the variables with nonzero coefficients vars_ = [v for i, v in enumerate(repn.linear_vars) if repn.linear_coefs[i]] if (len(vars_) == 2 and sorted(l for l in repn.linear_coefs 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 canonical_form(self, compute_values=True): """Build a canonical representation of the body of this constraints""" from pyomo.repn.standard_repn import \ StandardRepn variables = [] coefficients = [] constant = 0 for v, c in self.terms: if v.is_expression_type(): v = v.expr if not v.fixed: variables.append(v) if compute_values: coefficients.append(value(c)) else: coefficients.append(c) else: if compute_values: constant += value(c) * v() else: constant += c * v repn = StandardRepn() repn.linear_vars = tuple(variables) repn.linear_coefs = tuple(coefficients) repn.constant = constant return repn
def visiting_potential_leaf(self, node): if node.__class__ in nonpyomo_leaf_types: self.bnds_dict[node] = (node, node) return True, None if node.is_variable_type(): if node.is_fixed(): lb = value(node.value) ub = lb else: lb = value(node.lb) ub = value(node.ub) if lb is None: lb = -math.inf if ub is None: ub = math.inf self.bnds_dict[node] = (lb, ub) return True, None if not node.is_expression_type(): assert is_fixed(node) val = value(node) self.bnds_dict[node] = (val, val) return True, None return False, None
def _get_equality_linked_variables(constraint): """Return the two variables linked by an equality constraint x == y. If the constraint does not match this form, skip it. """ if value(constraint.lower) != 0 or value(constraint.upper) != 0: # LB and UB on constraint must be zero; otherwise, return empty tuple. return () if constraint.body.polynomial_degree() != 1: # must be a linear constraint; otherwise, return empty tuple. return () # Generate the standard linear representation repn = generate_standard_repn(constraint.body) nonzero_coef_vars = tuple(v for i, v in enumerate(repn.linear_vars) # if coefficient on variable is nonzero if repn.linear_coefs[i] != 0) if len(nonzero_coef_vars) != 2: # Expect two variables with nonzero cofficient in constraint; # otherwise, return empty tuple. return () if sorted(coef for coef in repn.linear_coefs if coef != 0) != [-1, 1]: # Expect a constraint of form x == y --> 0 == -1 * x + 1 * y; # otherwise, return empty tuple. return () # Above checks are satisifed. Return the variables. return nonzero_coef_vars
def visiting_potential_leaf(self, node): """ Visiting a potential leaf. Return True if the node is not expanded. """ if node.__class__ in nonpyomo_leaf_types: return True, node if node.is_parameter_type(): if node._component()._mutable: raise FixedExpressionError() return True, value(node) if node.is_variable_type(): if node.fixed: raise FixedExpressionError() else: raise NonConstantExpressionError() if not node.is_expression_type(): return True, value(node) return False, None
def _fix_equality_fixed_variables(model, scaling_tolerance=1E-10): """Detects variables fixed by a constraint: ax=b. Fixes the variable to the constant value (b/a) and deactivates the relevant constraint. This sub-transformation is different than contrib.detect_fixed_vars because it looks for x = const rather than x.lb = x.ub. """ for constraint in model.component_data_objects( ctype=Constraint, active=True, descend_into=True ): if not (constraint.has_lb() and constraint.has_ub()): # Constraint is not an equality. Skip. continue if value(constraint.lower) != value(constraint.upper): # Constraint is not an equality. Skip. continue if constraint.body.polynomial_degree() != 1: # Constraint is not linear. Skip. continue # Generate the standard linear representation repn = generate_standard_repn(constraint.body) # Generator of tuples with the coefficient and variable object for # nonzero coefficients. nonzero_coef_vars = ( (repn.linear_coefs[i], v) for i, v in enumerate(repn.linear_vars) # if coefficient on variable is nonzero if repn.linear_coefs[i] != 0) # get the coefficient and variable object coef, var = next(nonzero_coef_vars) if next(nonzero_coef_vars, None) is not None: # Expect one variable with nonzero cofficient in constraint; # otherwise, skip. continue # Constant term on the constraint body const = repn.constant if repn.constant is not None else 0 if abs(coef) <= scaling_tolerance: logger.warn( "Skipping fixed variable processing for constraint %s: " "%s * %s + %s = %s because coefficient %s is below " "tolerance of %s. Check your problem scaling." % (constraint.name, coef, var.name, const, value(constraint.lower), coef, scaling_tolerance)) continue # Constraint has form lower <= coef * var + const <= upper. We know that # lower = upper, so coef * var + const = lower. var_value = (value(constraint.lower) - const) / coef var.fix(var_value) constraint.deactivate()
def is_binary(self): """Returns :const:`True` when the domain type is :class:`IntegerSet` and the bounds are within [0,1].""" lb, ub = self.bounds return self.is_integer() and \ (lb is not None) and \ (ub is not None) and \ (value(lb) >= 0) and \ (value(ub) <= 1)
def _apply_to(self, instance, **kwargs): config = self.CONFIG(kwargs) if config.tmp and not hasattr(instance, '_tmp_trivial_deactivated_constrs'): instance._tmp_trivial_deactivated_constrs = ComponentSet() elif config.tmp: logger.warning( 'Deactivating trivial constraints on the block {} for which ' 'trivial constraints were previously deactivated. ' 'Reversion will affect all deactivated constraints.'.format( instance.name)) # Trivial constraints are those that do not contain any variables, ie. # the polynomial degree is 0 trivial_constraints = ( constr for constr in instance.component_data_objects( ctype=Constraint, active=True, descend_into=True) if constr.body.polynomial_degree() == 0) for constr in trivial_constraints: # We need to check each constraint to sure that it is not violated. constr_lb = value( constr.lower) if constr.has_lb() else float('-inf') constr_ub = value( constr.upper) if constr.has_ub() else float('inf') constr_value = value(constr.body) # Check if the lower bound is violated outside a given tolerance if (constr_value + config.tolerance <= constr_lb): if config.ignore_infeasible: continue else: raise ValueError( 'Trivial constraint {} violates LB {} ≤ BODY {}.' .format(constr.name, constr_lb, constr_value)) # Check if the upper bound is violated outside a given tolerance if (constr_value >= constr_ub + config.tolerance): if config.ignore_infeasible: continue else: raise ValueError( 'Trivial constraint {} violates BODY {} ≤ UB {}.' .format(constr.name, constr_value, constr_ub)) # Constraint is not infeasible. Deactivate it. if config.tmp: instance._tmp_trivial_deactivated_constrs.add(constr) config.return_trivial.append(constr) constr.deactivate()
def construct(self, values=None): """ Initialize set data """ if self._constructed: return timer = ConstructionTimer(self) self._constructed=True # # We call value() here for cases like Expressions, mutable # Params and the like # self._start_val = value(self._start) self._end_val = value(self._end) self._step_val = value(self._step) # # The set generates integer values if the starting value, # step and end value are all integers. Otherwise, the set # generates real values. # if type(self._start_val) is int and type(self._step) is int and type(self._end_val) is int: self.domain = Integers else: self.domain = Reals # # Compute the set length and upper bound # if self.filter is None and self.validate is None: # # Directly compute the number of elements in the set, from # which the upper-bound is computed. # self._len = int(math.floor((self._end_val-self._start_val+self._step_val+1e-7)//self._step_val)) ub = self._start_val + (self._len-1)*self._step_val else: # # Iterate through the set to compute the upper bound # and number of elements. # ub = self._start_val ctr=0 for i in self: ub = i ctr += 1 self._len = ctr # # Set the bounds information # self._bounds = (self._start_val, ub) timer.report()
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_standard_repn(constr.body) if len(repn.linear_vars) == 1 and repn.linear_coefs[0]: var = repn.linear_vars[0] coef = float(repn.linear_coefs[0]) const = repn.constant var_val = (value(constr.lower) - value(const)) / coef var.fix(var_val) new_fixed_vars.add(var) return new_fixed_vars
def visiting_potential_leaf(self, node): """ Visiting a potential leaf. Return True if the node is not expanded. """ if node.__class__ in nonpyomo_leaf_types: return True, node if node.is_variable_type(): return True, value(node) if not node.is_expression_type(): return True, value(node) return False, None
def _apply_to(self, instance, **kwargs): config = self.CONFIG(kwargs) if config.tmp: instance._xfrm_detect_fixed_vars_old_values = ComponentMap() for var in instance.component_data_objects( ctype=Var, descend_into=True): if var.fixed or var.lb is None or var.ub is None: # if the variable is already fixed, or if it is missing a # bound, we skip it. continue if fabs(value(var.lb) - value(var.ub)) <= config.tolerance: if config.tmp: instance._xfrm_detect_fixed_vars_old_values[var] = \ var.value var.fix(var.lb)
def _apply_to(self, instance, **kwds): config = self.CONFIG(kwds) if config.tmp and not hasattr(instance, '_tmp_propagate_original_bounds'): instance._tmp_propagate_original_bounds = Suffix( direction=Suffix.LOCAL) eq_var_map, relevant_vars = _build_equality_set(instance) processed = ComponentSet() # Go through each variable in an equality set to propagate the variable # bounds to all equality-linked variables. for var in relevant_vars: # If we have already processed the variable, skip it. if var in processed: continue var_equality_set = eq_var_map.get(var, ComponentSet([var])) #: variable lower bounds in the equality set lbs = [v.lb for v in var_equality_set if v.has_lb()] max_lb = max(lbs) if len(lbs) > 0 else None #: variable upper bounds in the equality set ubs = [v.ub for v in var_equality_set if v.has_ub()] min_ub = min(ubs) if len(ubs) > 0 else None # Check for error due to bound cross-over if max_lb is not None and min_ub is not None and max_lb > min_ub: # the lower bound is above the upper bound. Raise a ValueError. # get variable with the highest lower bound v1 = next(v for v in var_equality_set if v.lb == max_lb) # get variable with the lowest upper bound v2 = next(v for v in var_equality_set if v.ub == min_ub) raise ValueError( 'Variable {} has a lower bound {} ' ' > the upper bound {} of variable {}, ' 'but they are linked by equality constraints.' .format(v1.name, value(v1.lb), value(v2.ub), v2.name)) for v in var_equality_set: if config.tmp: # TODO warn if overwriting instance._tmp_propagate_original_bounds[v] = ( v.lb, v.ub) v.setlb(max_lb) v.setub(min_ub) processed.update(var_equality_set)
def finalize(self, ans): if isinstance(ans, cmodel.Node): return ans else: if len(self._constant_pool) == 0: self._constant_pool = set(cmodel.create_constants(100)) const = self._constant_pool.pop() const.value = value(ans) return const
def __call__(self, exception=True): try: return sum(value(c, exception=exception) * \ v(exception=exception) for v,c in self.terms) except (ValueError, TypeError): if exception: raise ValueError("one or more terms " "could not be evaluated") return None
def slack(self): """ Returns the smaller of lslack and uslack values """ if self.lower is None: return value(self.upper) - value(self.body) elif self.upper is None: return value(self.body) - value(self.lower) return min( value(self.upper) - value(self.body), value(self.body) - value(self.lower))
def validate(self, equal_slopes_tolerance=1e-6): """ Validate this piecewise linear function by verifying various properties of the breakpoints and values lists (e.g., that the list of breakpoints is nondecreasing). Args: equal_slopes_tolerance (float): Tolerance used check if consecutive slopes are nearly equal. If any are found, validation will fail. Default is 1e-6. Returns: int: a function characterization code (see \ :func:`util.characterize_function`) Raises: PiecewiseValidationError: if validation fails """ breakpoints = [value(x) for x in self._breakpoints] values = [value(x) for x in self._values] if not is_nondecreasing(breakpoints): raise PiecewiseValidationError( "The list of breakpoints is not nondecreasing: %s" % (str(breakpoints))) ftype, slopes = characterize_function(breakpoints, values) for i in xrange(1, len(slopes)): if (slopes[i-1] is not None) and \ (slopes[i] is not None) and \ (abs(slopes[i-1] - slopes[i]) <= equal_slopes_tolerance): raise PiecewiseValidationError( "Piecewise function validation detected slopes " "of consecutive line segments to be within %s " "of one another. This may cause numerical issues. " "To avoid this error, set the 'equal_slopes_tolerance' " "keyword to a smaller value or disable validation." % (equal_slopes_tolerance)) return ftype
def visiting_potential_leaf(self, node): if node.__class__ in nonpyomo_leaf_types: return True, None if node.is_variable_type(): lb, ub = self.bnds_dict[node] if node.is_binary() or node.is_integer(): """ This bit of code has two purposes: 1) Improve the bounds on binary and integer variables with the fact that they are integer. 2) Account for roundoff error. If the lower bound of a binary variable comes back as 1e-16, the lower bound may actually be 0. This could potentially cause problems when handing the problem to a MIP solver. Some solvers are robust to this, but some may not be and may give the wrong solution. Even if the correct solution is found, this could introduce numerical problems. """ lb = max(math.floor(lb), math.ceil(lb - self.integer_tol)) ub = min(math.ceil(ub), math.floor(ub + self.integer_tol)) if lb < value(node.lb): lb = value(node.lb) # don't make the bounds worse than the original bounds if ub > value(node.ub): ub = value(node.ub) # don't make the bounds worse than the original bounds self.bnds_dict[node] = (lb, ub) lb, ub = self.bnds_dict[node] if lb != -math.inf: node.setlb(lb) if ub != math.inf: node.setub(ub) return True, None if not node.is_expression_type(): return True, None if node.__class__ in _prop_bnds_root_to_leaf_map: _prop_bnds_root_to_leaf_map[node.__class__](node, self.bnds_dict) else: logger.warning('Unsupported expression type for FBBT: {0}. Bounds will not be improved in this part of ' 'the tree.' ''.format(str(type(node)))) return False, None
def _apply_to(self, instance, overwrite=False): """Apply the transformation. Kwargs: overwrite: if False, transformation will not overwrite existing variable values. """ for var in instance.component_data_objects(ctype=Var, descend_into=True): if var.fixed: continue if var.value is not None and not overwrite: continue if var.lb is not None and value(var.lb) > 0: var.set_value(value(var.lb)) elif var.ub is not None and value(var.ub) < 0: var.set_value(value(var.ub)) else: var.set_value(0)
def _pprint(self): """Print component information.""" headers = [ ("Size", len(self)), ("Index", self._index if self.is_indexed() else None), ] if self._units is not None: headers.append(('Units', str(self._units))) return ( headers, self._data.items(), ( "Lower","Value","Upper","Fixed","Stale","Domain"), lambda k, v: [ value(v.lb), v.value, value(v.ub), v.fixed, v.stale, v.domain ] )
def visiting_potential_leaf(self, node): """ Visiting a potential leaf. Return True if the node is not expanded. """ #print("ISLEAF") #print(node.__class__) if node is None: return True, None if node.__class__ in native_types: return True, _ftoa(node) if node.is_expression_type(): # we will descend into this, so type checking will happen later if node.is_component_type(): self.treechecker(node) return False, None if node.is_component_type(): if isinstance(node, ICategorizedObject): _ctype = node.ctype else: _ctype = node.type() if _ctype not in valid_expr_ctypes_minlp: # Make sure all components in active constraints # are basic ctypes we know how to deal with. raise RuntimeError( "Unallowable component '%s' of type %s found in an active " "constraint or objective.\nThe GAMS writer cannot export " "expressions with this component type." % (node.name, _ctype.__name__)) if node.is_variable_type(): if node.fixed: return True, _ftoa(value(node)) else: self.variables.add(id(node)) label = self.smap.getSymbol(node) return True, label return True, _ftoa(value(node))
def _apply_operation(self, result): obj = result[0].__getitem__(tuple(result[1:])) if obj.__class__ in nonpyomo_leaf_types: return obj # Note that because it is possible (likely) that the result # could be an IndexedComponent_slice object, must test "is # True", as the slice will return a list of values. if obj.is_numeric_type() is True: obj = value(obj) return obj
def _add_variables(self, variables: List[_GeneralVarData]): cvars = cmodel.create_vars(len(variables)) for ndx, v in enumerate(variables): cv = cvars[ndx] cv.name = self._symbol_map.getSymbol(v, self._var_labeler) if not v.is_continuous(): raise NotImplementedError('NLWriter currently only supports continuous variables') lb = value(v.lb) ub = value(v.ub) if lb is not None: cv.lb = lb if ub is not None: cv.ub = ub if v.value is not None: cv.value = v.value if v.is_fixed(): cv.fixed = True self._pyomo_var_to_solver_var_map[id(v)] = cv self._solver_var_to_pyomo_var_map[cv] = v
def _apply_to(self, instance, overwrite=False): """Apply the transformation. Kwargs: overwrite: if False, transformation will not overwrite existing variable values. """ for var in instance.component_data_objects( ctype=Var, descend_into=True): if var.fixed: continue if var.value is not None and not overwrite: continue if var.lb is not None and value(var.lb) > 0: var.set_value(value(var.lb)) elif var.ub is not None and value(var.ub) < 0: var.set_value(value(var.ub)) else: var.set_value(0)
def _initialize_members(self, init_set): """Initialize variable data for all indices in a set.""" # # Initialize values # if self._value_init_rule is not None: # # Initialize values with a rule # if self.is_indexed(): for key in init_set: vardata = self._data[key] val = apply_indexed_rule(self, self._value_init_rule, self._parent(), key) val = value(val) vardata.set_value(val) else: val = self._value_init_rule(self._parent()) val = value(val) self.set_value(val) elif self._value_init_value is not None: # # Initialize values with a value if self._value_init_value.__class__ is dict: for key in init_set: # Skip indices that are not in the # dictionary. This arises when # initializing VarList objects with a # dictionary. # What does this continue do here? if not key in self._value_init_value: continue val = self._value_init_value[key] vardata = self._data[key] vardata.set_value(val) else: val = value(self._value_init_value) for key in init_set: vardata = self._data[key] vardata.set_value(val)
def uslack(self): """ Returns the value of U-f(x) for constraints of the form: (L <=) f(x) <= U U >= f(x) (>= L) """ ub = self.ub if ub is None: return _inf else: return ub - value(self.body)
def lslack(self): """ Returns the value of f(x)-L for constraints of the form: L <= f(x) (<= U) (U >=) f(x) >= L """ lb = self.lb if lb is None: return _inf else: return value(self.body) - lb
def _transformContainer(self, obj): """Find all disjuncts in the container and transform them.""" for disjunct in obj.component_data_objects(ctype=Disjunct, active=True, descend_into=True): _binary = disjunct.binary_indicator_var if fabs(value(_binary) - 1) <= self.config.integer_tolerance: disjunct.indicator_var.fix(True) self._transformContainer(disjunct) elif fabs(value(_binary)) <= self.config.integer_tolerance: disjunct.deactivate() else: raise ValueError( 'Non-binary indicator variable value %s for disjunct %s' % (disjunct.name, value(_binary))) for disjunction in obj.component_data_objects(ctype=Disjunction, active=True, descend_into=True): self._transformDisjunctionData(disjunction)
def ub(self): """Access the value of the upper bound of a constraint expression.""" bound = value(self._ub()) if bound is not None and not math.isfinite(bound): if bound == _inf: bound = None else: raise ValueError( "Constraint '%s' created with an invalid non-finite " "upper bound (%s)." % (self.name, bound)) return bound
def __init__(self, var_list, symbol_map): self.binary = [] self.ints = [] self.positive = [] self.reals = [] # categorize variables for var in var_list: v = symbol_map.getObject(var) if v.is_binary(): self.binary.append(var) elif v.is_integer(): if (v.has_lb() and (value(v.lb) >= 0)) and \ (v.has_ub() and (value(v.ub) <= 1)): self.binary.append(var) else: self.ints.append(var) elif value(v.lb) == 0: self.positive.append(var) else: self.reals.append(var)
def lslack(self): """Lower slack (value - lb). Returns :const:`None` if the variable value is :const:`None`.""" val = self.value if val is None: return None lb = self.lb if lb is None: lb = _neg_inf else: lb = value(lb) return val - lb
def uslack(self): """Upper slack (ub - value). Returns :const:`None` if the variable value is :const:`None`.""" val = self.value if val is None: return None ub = self.ub if ub is None: ub = _pos_inf else: ub = value(ub) return ub - val
def slack(self): """ Returns the smaller of lslack and uslack values """ lb = self.lb ub = self.ub body = value(self.body) if lb is None: return ub - body elif ub is None: return body - lb return min(ub - body, body - lb)
def uslack(self): """Upper slack (ub - value). Returns :const:`None` if the variable value is :const:`None`.""" val = self.value if val is None: return None ub = self.ub if ub is None: ub = _pos_inf else: ub = value(ub) return ub - val
def _update_variables(self, variables: List[_GeneralVarData]): for v in variables: cv = self._pyomo_var_to_solver_var_map[id(v)] if not v.is_continuous(): raise NotImplementedError('NLWriter currently only supports continuous variables') lb = value(v.lb) ub = value(v.ub) if lb is None: cv.lb = -cmodel.inf else: cv.lb = lb if ub is None: cv.ub = cmodel.inf else: cv.ub = ub if v.value is not None: cv.value = v.value if v.is_fixed(): cv.fixed = True else: cv.fixed = False
def _add_var(self, var): varname = self._symbol_map.getSymbol(var, self._labeler) vtype = self._gurobi_vtype_from_var(var) if var.has_lb(): lb = value(var.lb) else: lb = -self._gurobipy.GRB.INFINITY if var.has_ub(): ub = value(var.ub) else: ub = self._gurobipy.GRB.INFINITY gurobipy_var = self._solver_model.addVar(lb=lb, ub=ub, vtype=vtype, name=varname) self._pyomo_var_to_solver_var_map[var] = gurobipy_var self._solver_var_to_pyomo_var_map[gurobipy_var] = var self._referenced_variables[var] = 0 if var.is_fixed(): gurobipy_var.setAttr('lb', var.value) gurobipy_var.setAttr('ub', var.value)
def __init__(self, var_list, symbol_map): self.binary = [] self.ints = [] self.positive = [] self.reals = [] # categorize variables for var in var_list: v = symbol_map.getObject(var) if v.is_binary(): self.binary.append(var) elif v.is_integer(): if (v.has_lb() and (value(v.lb) >= 0)) and \ (v.has_ub() and (value(v.ub) <= 1)): self.binary.append(var) else: self.ints.append(var) elif value(v.lb) == 0: self.positive.append(var) else: self.reals.append(var)
def lslack(self): """Lower slack (value - lb). Returns :const:`None` if the variable value is :const:`None`.""" val = self.value if val is None: return None lb = self.lb if lb is None: lb = _neg_inf else: lb = value(lb) return val - lb
def _apply_to(self, model, **kwds): config = self.CONFIG(kwds) for constr in model.component_data_objects(ctype=Constraint, active=True, descend_into=True): # Check if the constraint is k * x + c1 <= c2 or c2 <= k * x + c1 repn = generate_standard_repn(constr.body) if not repn.is_linear() or len(repn.linear_vars) != 1: # Skip nonlinear constraints, trivial constraints, and those # that involve more than one variable. continue else: var = repn.linear_vars[0] const = repn.constant coef = float(repn.linear_coefs[0]) if coef == 0: # Skip trivial constraints continue elif coef > 0: if constr.has_ub(): new_ub = (value(constr.upper) - const) / coef var_ub = float('inf') if var.ub is None else var.ub var.setub(min(var_ub, new_ub)) if constr.has_lb(): new_lb = (value(constr.lower) - const) / coef var_lb = float('-inf') if var.lb is None else var.lb var.setlb(max(var_lb, new_lb)) elif coef < 0: if constr.has_ub(): new_lb = (value(constr.upper) - const) / coef var_lb = float('-inf') if var.lb is None else var.lb var.setlb(max(var_lb, new_lb)) if constr.has_lb(): new_ub = (value(constr.lower) - const) / coef var_ub = float('inf') if var.ub is None else var.ub var.setub(min(var_ub, new_ub)) if var.is_integer() or var.is_binary(): # Make sure that the lb and ub are integral. Use safe construction if near to integer. if var.has_lb(): var.setlb( int( min(math.ceil(var.lb - config.tolerance), math.ceil(var.lb)))) if var.has_ub(): var.setub( int( max(math.floor(var.ub + config.tolerance), math.floor(var.ub)))) if var is not None and var.value is not None: _adjust_var_value_if_not_feasible(var) if (config.detect_fixed and var.has_lb() and var.has_ub() and fabs(value(var.lb) - value(var.ub)) <= config.tolerance): var.fix(var.lb) constr.deactivate()
def visiting_potential_leaf(self, node): """ Visiting a potential leaf. Return True if the node is not expanded. """ if node is None: return True, None if node.__class__ in native_types: return True, ftoa(node) if node.is_expression_type(): # we will descend into this, so type checking will happen later if node.is_component_type(): self.treechecker(node) return False, None if node.is_component_type(): if node.ctype not in valid_expr_ctypes_minlp: # Make sure all components in active constraints # are basic ctypes we know how to deal with. raise RuntimeError( "Unallowable component '%s' of type %s found in an active " "constraint or objective.\nThe GAMS writer cannot export " "expressions with this component type." % (node.name, node.ctype.__name__)) if node.ctype is not Var: # For these, make sure it's on the right model. We can check # Vars later since they don't disappear from the expressions self.treechecker(node) if node.is_variable_type(): if node.fixed: return True, ftoa(value(node)) else: label = self.smap.getSymbol(node) return True, label return True, ftoa(value(node))
def visiting_potential_leaf(self, node): """ Visiting a potential leaf. Return True if the node is not expanded. """ #print("ISLEAF") #print(node.__class__) if node.__class__ in native_types: return True, ftoa(node) if node.is_expression_type(): # Special handling if NPV and semi-NPV types: if not node.is_potentially_variable(): return True, ftoa(value(node)) if node.__class__ is EXPR.MonomialTermExpression: return True, self._monomial_to_string(node) if node.__class__ is EXPR.LinearExpression: return True, self._linear_to_string(node) # we will descend into this, so type checking will happen later return False, None if node.is_component_type(): if node.ctype not in valid_expr_ctypes_minlp: # Make sure all components in active constraints # are basic ctypes we know how to deal with. raise RuntimeError( "Unallowable component '%s' of type %s found in an active " "constraint or objective.\nThe GAMS writer cannot export " "expressions with this component type." % (node.name, node.ctype.__name__)) if node.is_fixed(): return True, ftoa(value(node)) else: assert node.is_variable_type() self.variables.add(id(node)) return True, self.smap.getSymbol(node)
def _transformDisjunctData(self, obj): """Fix the disjunct either to a Block or a deactivated Block.""" if fabs(value(obj.indicator_var) - 1) <= self.config.integer_tolerance: # Disjunct is active. Convert to Block. obj.parent_block().reclassify_component_type(obj, Block) obj.indicator_var.fix(1) # Process the components attached to this disjunct. self._transformContainer(obj) elif fabs(value(obj.indicator_var)) <= self.config.integer_tolerance: obj.parent_block().reclassify_component_type(obj, Block) obj.indicator_var.fix(0) # Deactivate all constituent constraints and disjunctions # HACK I do not deactivate the whole block because some writers # do not look for variables in deactivated blocks. for constr in obj.component_objects( ctype=(Constraint, Disjunction), active=True, descend_into=True): constr.deactivate() else: raise ValueError( 'Non-binary indicator variable value %s for disjunct %s' % (obj.name, value(obj.indicator_var)))
def _apply_to(self, instance): 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_standard_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.linear_vars[i].has_lb() and value(repn.linear_vars[i].lb) >= 0 and coef >= 0) or # variable is non-positive and has non-positive coefficient (repn.linear_vars[i].has_ub() and value(repn.linear_vars[i].ub) <= 0 and coef <= 0) for i, coef in enumerate(repn.linear_coefs)): for i, coef in enumerate(repn.linear_coefs): if not coef == 0: repn.linear_vars[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.linear_vars[i].has_lb() and value(repn.linear_vars[i].lb) >= 0 and coef <= 0) or # variable is non-positive and has non-negative coefficient (repn.linear_vars[i].has_ub() and value(repn.linear_vars[i].ub) <= 0 and coef >= 0) for i, coef in enumerate(repn.linear_coefs)): for i, coef in enumerate(repn.linear_coefs): if not coef == 0: repn.linear_vars[i].fix(0)
def _add_var(self, var): varname = self._symbol_map.getSymbol(var, self._labeler) vtype = self._mosek_vtype_from_var(var) if var.has_lb(): lb = value(var.lb) else: lb = '0' if var.has_ub(): ub = value(var.ub) else: ub = '0' bound_type = self.set_var_boundtype(var, ub, lb) self._solver_model.appendvars(1) index = self._solver_model.getnumvar() - 1 self._solver_model.putvarbound(index, bound_type, float(lb), float(ub)) self._solver_model.putvartype(index, vtype) self._solver_model.putvarname(index, varname) self._pyomo_var_to_solver_var_map[var] = index self._solver_var_to_pyomo_var_map[index] = var self._referenced_variables[var] = 0
def _apply_to(self, instance): 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_standard_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.linear_vars[i].has_lb() and value(repn.linear_vars[i].lb) >= 0 and coef >= 0) or # variable is non-positive and has non-positive coefficient (repn.linear_vars[i].has_ub() and value(repn.linear_vars[i].ub) <= 0 and coef <= 0) for i, coef in enumerate(repn.linear_coefs)): for i, coef in enumerate(repn.linear_coefs): if not coef == 0: repn.linear_vars[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.linear_vars[i].has_lb() and value(repn.linear_vars[i].lb) >= 0 and coef <= 0) or # variable is non-positive and has non-negative coefficient (repn.linear_vars[i].has_ub() and value(repn.linear_vars[i].ub) <= 0 and coef >= 0) for i, coef in enumerate(repn.linear_coefs)): for i, coef in enumerate(repn.linear_coefs): if not coef == 0: repn.linear_vars[i].fix(0)
def visiting_potential_leaf(self, node): """ Visiting a potential leaf. Return True if the node is not expanded. """ if node is None: return True, None if node.__class__ in native_types: return True, str(node) if node.is_variable_type(): if node.fixed: return True, str(value(node)) label = self.smap.getSymbol(node) return True, label if not node.is_expression_type(): return True, str(value(node)) return False, None
def display(self, prefix="", ostream=None): """ Print component state information This duplicates logic in Component.pprint() """ if not self.active: return if ostream is None: ostream = sys.stdout tab=" " ostream.write(prefix+self.local_name+" : ") ostream.write("Size="+str(len(self))) ostream.write("\n") tabular_writer( ostream, prefix+tab, ((k,v) for k,v in iteritems(self._data) if v.active), ( "Lower","Body","Upper" ), lambda k, v: [ value(v.lower), v.body(), value(v.upper), ] )
def display(self, prefix="", ostream=None): """ Print component state information This duplicates logic in Component.pprint() """ if not self.active: return if ostream is None: ostream = sys.stdout tab=" " ostream.write(prefix+self.local_name+" : ") ostream.write("Size="+str(len(self))) ostream.write("\n") tabular_writer( ostream, prefix+tab, ((k,v) for k,v in iteritems(self._data) if v.active), ( "Lower","Body","Upper" ), lambda k, v: [ value(v.lower), v.body(), value(v.upper), ] )
def _add_var(self, var): varname = self._symbol_map.getSymbol(var, self._labeler) vtype = self._gurobi_vtype_from_var(var) if var.has_lb(): lb = value(var.lb) else: lb = -self._gurobipy.GRB.INFINITY if var.has_ub(): ub = value(var.ub) else: ub = self._gurobipy.GRB.INFINITY if var.is_fixed(): lb = value(var.value) ub = value(var.value) gurobipy_var = self._solver_model.addVar(lb=lb, ub=ub, vtype=vtype, name=varname) self._pyomo_var_to_solver_var_map[var] = gurobipy_var self._solver_var_to_pyomo_var_map[gurobipy_var] = var self._referenced_variables[var] = 0 self._needs_updated = True
def update_var(self, var): """Update a single variable in the solver's model. This will update bounds, fix/unfix the variable as needed, and update the variable type. Parameters ---------- var: Var (scalar Var or single _VarData) """ # see PR #366 for discussion about handling indexed # objects and keeping compatibility with the # pyomo.kernel objects #if var.is_indexed(): # for child_var in var.values(): # self.update_var(child_var) # return if var not in self._pyomo_var_to_solver_var_map: raise ValueError( 'The Var provided to update_var needs to be added first: {0}'. format(var)) gurobipy_var = self._pyomo_var_to_solver_var_map[var] vtype = self._gurobi_vtype_from_var(var) if var.is_fixed(): lb = var.value ub = var.value else: lb = -self._gurobipy.GRB.INFINITY ub = self._gurobipy.GRB.INFINITY if var.has_lb(): lb = value(var.lb) if var.has_ub(): ub = value(var.ub) gurobipy_var.setAttr('lb', lb) gurobipy_var.setAttr('ub', ub) gurobipy_var.setAttr('vtype', vtype) self._needs_updated = True
def _add_var(self, var): varname = self._symbol_map.getSymbol(var, self._labeler) vtype = self._cplex_vtype_from_var(var) if var.has_lb(): lb = value(var.lb) else: lb = -self._cplex.infinity if var.has_ub(): ub = value(var.ub) else: ub = self._cplex.infinity self._solver_model.variables.add(lb=[lb], ub=[ub], types=[vtype], names=[varname]) self._pyomo_var_to_solver_var_map[var] = varname self._solver_var_to_pyomo_var_map[varname] = var self._pyomo_var_to_ndx_map[var] = self._ndx_count self._ndx_count += 1 self._referenced_variables[var] = 0 if var.is_fixed(): self._solver_model.variables.set_lower_bounds(varname, var.value) self._solver_model.variables.set_upper_bounds(varname, var.value)
def _add_var(self, var): varname = self._symbol_map.getSymbol(var, self._labeler) vtype = self._cplex_vtype_from_var(var) if var.has_lb(): lb = value(var.lb) else: lb = -self._cplex.infinity if var.has_ub(): ub = value(var.ub) else: ub = self._cplex.infinity self._solver_model.variables.add(lb=[lb], ub=[ub], types=[vtype], names=[varname]) self._pyomo_var_to_solver_var_map[var] = varname self._solver_var_to_pyomo_var_map[varname] = var self._pyomo_var_to_ndx_map[var] = self._ndx_count self._ndx_count += 1 self._referenced_variables[var] = 0 if var.is_fixed(): self._solver_model.variables.set_lower_bounds(varname, var.value) self._solver_model.variables.set_upper_bounds(varname, var.value)
def _apply_to(self, instance, **kwds): config = self.CONFIG(kwds) if config.tmp and not hasattr(instance, '_tmp_propagate_fixed'): instance._tmp_propagate_fixed = ComponentSet() eq_var_map, relevant_vars = _build_equality_set(instance) #: ComponentSet: The set of all fixed variables fixed_vars = ComponentSet((v for v in relevant_vars if v.fixed)) newly_fixed = _detect_fixed_variables(instance) if config.tmp: instance._tmp_propagate_fixed.update(newly_fixed) fixed_vars.update(newly_fixed) processed = ComponentSet() # Go through each fixed variable to propagate the 'fixed' status to all # equality-linked variabes. for v1 in fixed_vars: # If we have already processed the variable, skip it. if v1 in processed: continue eq_set = eq_var_map.get(v1, ComponentSet([v1])) for v2 in eq_set: if (v2.fixed and value(v1) != value(v2)): raise ValueError( 'Variables {} and {} have conflicting fixed ' 'values of {} and {}, but are linked by ' 'equality constraints.' .format(v1.name, v2.name, value(v1), value(v2))) elif not v2.fixed: v2.fix(value(v1)) if config.tmp: instance._tmp_propagate_fixed.add(v2) # Add all variables in the equality set to the set of processed # variables. processed.update(eq_set)
def visiting_potential_leaf(self, node): if node.__class__ in nonpyomo_leaf_types: self.val_dict[node] = node if node not in self.der_dict: self.der_dict[node] = 0 return True, node if not node.is_expression_type(): val = value(node) self.val_dict[node] = val if node not in self.der_dict: self.der_dict[node] = 0 return True, val return False, None
def test_pickle(self): v = variable() e = noclone(v) self.assertEqual(type(e), noclone) self.assertIs(type(e.expr), variable) eup = pickle.loads( pickle.dumps(e)) self.assertEqual(type(eup), noclone) self.assertTrue(e is not eup) self.assertIs(type(eup.expr), variable) self.assertIs(type(e.expr), variable) self.assertTrue(eup.expr is not e.expr) del e del v v = variable(value=1) b = block() b.v = v eraw = b.v + 1 b.e = 1 + noclone(eraw) bup = pickle.loads( pickle.dumps(b)) self.assertTrue(isinstance(bup.e, NumericValue)) self.assertEqual(value(bup.e), 3.0) b.v.value = 2 self.assertEqual(value(b.e), 4.0) self.assertEqual(value(bup.e), 3.0) bup.v.value = -1 self.assertEqual(value(b.e), 4.0) self.assertEqual(value(bup.e), 1.0) self.assertIs(b.v.parent, b) self.assertIs(bup.v.parent, bup) del b.v
def _apply_to(self, instance, overwrite=False): """Apply the transformation. Kwargs: overwrite: if False, transformation will not overwrite existing variable values. """ for var in instance.component_data_objects( ctype=Var, descend_into=True): if var.fixed: continue if var.value is not None and not overwrite: continue if var.lb is None and var.ub is None: # If LB and UB do not exist, set variable value to 0 var.set_value(0) elif var.lb is None: # if one bound does not exist, set variable value to the other var.set_value(value(var.ub)) elif var.ub is None: # if one bound does not exist, set variable value to the other var.set_value(value(var.lb)) else: var.set_value((value(var.lb) + value(var.ub)) / 2.)
def uslack(self): """Upper slack (ub - body). Returns :const:`None` if a value for the body can not be computed.""" # this method is written so that constraint # types that build the body expression on the # fly do not have to here body = self(exception=False) if body is None: return None ub = self.ub if ub is None: ub = _pos_inf else: ub = value(ub) return ub - body
def lslack(self): """Lower slack (body - lb). Returns :const:`None` if a value for the body can not be computed.""" # this method is written so that constraint # types that build the body expression on the # fly do not have to here body = self(exception=False) if body is None: return None lb = self.lb if lb is None: lb = _neg_inf else: lb = value(lb) return body - lb
def __call__(self, exception=False): """Compute the value of this expression. Args: exception (bool): Indicates if an exception should be raised when instances of NumericValue fail to evaluate due to one or more objects not being initialized to a numeric value (e.g, one or more variables in an algebraic expression having the value None). Default is :const:`True`. Returns: numeric value or None """ return value(self._expr, exception=exception)