def expr(self): """Get the expression on this constraint.""" body_expr = self.body if body_expr is None: return None if self.equality: return body_expr == self.rhs else: if self.lb is None: return body_expr <= self.ub elif self.ub is None: return self.lb <= body_expr return logical_expr.RangedExpression((self.lb, body_expr, self.ub), (False, False))
def set_value(self, expr): """Set the expression on this constraint.""" # Clear any previously-cached normalized constraint self._lower = self._upper = self._body = self._expr = None _expr_type = expr.__class__ if hasattr(expr, 'is_relational'): if not expr.is_relational(): raise ValueError("Constraint '%s' does not have a proper " "value. Found '%s'\nExpecting a tuple or " "equation. Examples:" "\n sum(model.costs) == model.income" "\n (0, model.price[item], 50)" % (self.name, str(expr))) self._expr = expr elif _expr_type is tuple: # or expr_type is list: for arg in expr: if arg is None or arg.__class__ in native_numeric_types \ or isinstance(arg, NumericValue): continue raise ValueError( "Constraint '%s' does not have a proper value. " "Constraint expressions expressed as tuples must " "contain native numeric types or Pyomo NumericValue " "objects. Tuple %s contained invalid type, %s" % (self.name, expr, arg.__class__.__name__)) if len(expr) == 2: # # Form equality expression # if expr[0] is None or expr[1] is None: raise ValueError( "Constraint '%s' does not have a proper value. " "Equality Constraints expressed as 2-tuples " "cannot contain None [received %s]" % ( self.name, expr, )) self._expr = logical_expr.EqualityExpression(expr) elif len(expr) == 3: # # Form (ranged) inequality expression # if expr[0] is None: self._expr = logical_expr.InequalityExpression( expr[1:], False) elif expr[2] is None: self._expr = logical_expr.InequalityExpression( expr[:2], False) else: self._expr = logical_expr.RangedExpression(expr, False) else: raise ValueError( "Constraint '%s' does not have a proper value. " "Found a tuple of length %d. Expecting a tuple of " "length 2 or 3:\n" " Equality: (left, right)\n" " Inequality: (lower, expression, upper)" % (self.name, len(expr))) # # Ignore an 'empty' constraint # elif _expr_type is type: del self.parent_component()[self.index()] if expr is Constraint.Skip: return elif expr is Constraint.Infeasible: # TODO: create a trivial infeasible constraint. This # could be useful in the case of GDP where certain # disjuncts are trivially infeasible, but we would still # like to express the disjunction. #del self.parent_component()[self.index()] raise ValueError("Constraint '%s' is always infeasible" % (self.name, )) else: raise ValueError("Constraint '%s' does not have a proper " "value. Found '%s'\nExpecting a tuple or " "equation. Examples:" "\n sum(model.costs) == model.income" "\n (0, model.price[item], 50)" % (self.name, str(expr))) elif expr is None: raise ValueError(_rule_returned_none_error % (self.name, )) elif _expr_type is bool: raise ValueError( "Invalid constraint expression. The constraint " "expression resolved to a trivial Boolean (%s) " "instead of a Pyomo object. Please modify your " "rule to return Constraint.%s instead of %s." "\n\nError thrown for Constraint '%s'" % (expr, "Feasible" if expr else "Infeasible", expr, self.name)) else: msg = ("Constraint '%s' does not have a proper " "value. Found '%s'\nExpecting a tuple or " "equation. Examples:" "\n sum(model.costs) == model.income" "\n (0, model.price[item], 50)" % (self.name, str(expr))) raise ValueError(msg) # # Normalize the incoming expressions, if we can # args = self._expr.args if self._expr.__class__ is logical_expr.InequalityExpression: if self._expr.strict: raise ValueError("Constraint '%s' encountered a strict " "inequality expression ('>' or '< '). All" " constraints must be formulated using " "using '<=', '>=', or '=='." % (self.name, )) if args[1] is None or args[1].__class__ in native_numeric_types \ or not args[1].is_potentially_variable(): self._body = args[0] self._upper = args[1] elif args[0] is None or args[0].__class__ in native_numeric_types \ or not args[0].is_potentially_variable(): self._lower = args[0] self._body = args[1] else: self._body = args[0] - args[1] self._upper = 0 elif self._expr.__class__ is logical_expr.EqualityExpression: if args[0] is None or args[1] is None: # Error check: ensure equality does not have infinite RHS raise ValueError("Equality constraint '%s' defined with " "non-finite term (%sHS == None)." % (self.name, 'L' if args[0] is None else 'R')) if args[0].__class__ in native_numeric_types or \ not args[0].is_potentially_variable(): self._lower = self._upper = args[0] self._body = args[1] elif args[1].__class__ in native_numeric_types or \ not args[1].is_potentially_variable(): self._lower = self._upper = args[1] self._body = args[0] else: self._lower = self._upper = 0 self._body = args[0] - args[1] # The following logic is caught below when checking for # invalid non-finite bounds: # # if self._lower.__class__ in native_numeric_types and \ # not math.isfinite(self._lower): # raise ValueError( # "Equality constraint '%s' defined with " # "non-finite term." % (self.name)) elif self._expr.__class__ is logical_expr.RangedExpression: if any(self._expr.strict): raise ValueError("Constraint '%s' encountered a strict " "inequality expression ('>' or '< '). All" " constraints must be formulated using " "using '<=', '>=', or '=='." % (self.name, )) if all((arg is None or arg.__class__ in native_numeric_types or not arg.is_potentially_variable()) for arg in (args[0], args[2])): self._lower, self._body, self._upper = args else: # Defensive programming: we currently only support three # relational expression types. This will only be hit if # someone defines a fourth... raise DeveloperError( "Unrecognized relational expression type: %s" % (self._expr.__class__.__name__, )) # We have historically mapped incoming inf to None if self._lower.__class__ in native_numeric_types: if self._lower == -_inf: self._lower = None elif not math.isfinite(self._lower): raise ValueError( "Constraint '%s' created with an invalid non-finite " "lower bound (%s)." % (self.name, self._lower)) if self._upper.__class__ in native_numeric_types: if self._upper == _inf: self._upper = None elif not math.isfinite(self._upper): raise ValueError( "Constraint '%s' created with an invalid non-finite " "upper bound (%s)." % (self.name, self._upper))