Esempio n. 1
0
    def __init__(self,
                 substitute=None,
                 descend_into_named_expressions=True,
                 remove_named_expressions=True):
        if substitute is None:
            substitute = {}
        # Note: preserving the attribute names from the previous
        # implementation of the expression walker.
        self.substitute = substitute
        self.enter_named_expr = descend_into_named_expressions
        self.rm_named_expr = remove_named_expressions

        kwds = {}
        if hasattr(self, 'visiting_potential_leaf'):
            deprecation_warning(
                "ExpressionReplacementVisitor: this walker has been ported "
                "to derive from StreamBasedExpressionVisitor.  "
                "visiting_potential_leaf() has been replaced by beforeChild()"
                "(note to implementers: the sense of the bool return value "
                "has been inverted).",
                version='6.2')

            def beforeChild(node, child, child_idx):
                is_leaf, ans = self.visiting_potential_leaf(child)
                return not is_leaf, ans

            kwds['beforeChild'] = beforeChild

        if hasattr(self, 'visit'):
            raise DeveloperError(
                "ExpressionReplacementVisitor: this walker has been ported "
                "to derive from StreamBasedExpressionVisitor.  "
                "overriding visit() has no effect (and is likely to generate "
                "invalid expression trees)")
        super().__init__(**kwds)
Esempio n. 2
0
def _deprecation_docstring(obj, msg, version, remove_in):
    if version is None: # or version in ('','tbd','TBD'):
        raise DeveloperError("@deprecated missing initial version")
    return (
        '%s %s\n   %s\n'
        % (_doc_flag, version, _default_msg(obj, msg, None, remove_in))
    )
Esempio n. 3
0
def deprecated(msg=None, logger='pyomo.core', version=None, remove_in=None):
    """Indicate that a function, method or class is deprecated.

    This decorator will cause a warning to be logged when the wrapped
    function or method is called, or when the deprecated class is
    constructed.  This decorator also updates the target object's
    docstring to indicate that it is deprecated.

    Args:
        msg (str): a custom deprecation message (default: "This
            {function|class} has been deprecated and may be
            removed in a future release.")

        logger (str): the logger to use for emitting the warning
            (default: "pyomo.core")

        version (str): [required] the version in which the decorated
            object was deprecated.  General practice is to set version
            to '' or 'TBD' during development and update it to the
            actual release as part of the release process.

        remove_in (str): the version in which the decorated object will be
            removed from the code.

    """
    if version is None:  # or version in ('','tbd','TBD'):
        raise DeveloperError("@deprecated missing initial version")

    def wrap(func):
        message = _default_msg(msg, version, remove_in, func)

        @functools.wraps(func, assigned=('__module__', '__name__'))
        def wrapper(*args, **kwargs):
            deprecation_warning(message, logger)
            return func(*args, **kwargs)

        if func.__doc__ is None:
            wrapper.__doc__ = textwrap.fill('DEPRECATION WARNING: %s' %
                                            (message, ),
                                            width=70)
        else:
            wrapper.__doc__ = textwrap.fill(
                'DEPRECATION WARNING: %s' % (message,), width=70) + '\n\n' + \
                textwrap.fill(textwrap.dedent(func.__doc__.strip()))
        return wrapper

    return wrap
Esempio n. 4
0
    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))
Esempio n. 5
0
def power(xl, xu, yl, yu):
    """
    Compute bounds on x**y.
    """
    if xl > 0:
        """
        If x is always positive, things are simple. We only need to worry about the sign of y.
        """
        if yl < 0 and yu > 0:
            lb = min(xu**yl, xl**yu)
            ub = max(xl**yl, xu**yu)
        elif yl >= 0:
            lb = xl**yl
            ub = xu**yu
        elif yu <= 0:
            lb = xu**yl
            ub = xl**yu
        else:
            raise DeveloperError()
    elif xl == 0:
        # this section is only needed so we do not encounter math domain errors;
        # The logic is essentially the same as above (xl > 0)
        if xu == 0 and yl < 0:
            _lba = math.inf
        else:
            _lba = xu**yl
        if yu < 0:
            _lbb = math.inf
        else:
            _lbb = xl**yu
        lb = min(_lba, _lbb)

        if yl < 0:
            _uba = math.inf
        else:
            _uba = xl**yl
        if xu == 0 and yu < 0:
            _ubb = math.inf
        else:
            _ubb = xu**yu
        ub = max(_uba, _ubb)
    elif yl == yu and yl == round(yl):
        # the exponent is an integer, so x can be negative
        """
        The logic here depends on several things:
        1) The sign of x
        2) The sign of y
        3) Whether y is even or odd.
        
        There are also special cases to avoid math domain errors.
        """
        y = yl
        if xu <= 0:
            if y < 0:
                if y % 2 == 0:
                    lb = xl**y
                    if xu == 0:
                        ub = math.inf
                    else:
                        ub = xu**y
                else:
                    if xu == 0:
                        lb = -math.inf
                    else:
                        lb = xu**y
                    ub = xl**y
            else:
                if y % 2 == 0:
                    lb = xu**y
                    ub = xl**y
                else:
                    lb = xl**y
                    ub = xu**y
        else:
            if y < 0:
                if y % 2 == 0:
                    lb = min(xl**y, xu**y)
                    ub = math.inf
                else:
                    lb = -math.inf
                    ub = math.inf
            else:
                if y % 2 == 0:
                    lb = 0
                    ub = max(xl**y, xu**y)
                else:
                    lb = xl**y
                    ub = xu**y
    elif yl == yu:
        # the exponent is allowed to be fractional, so x must be positive
        xl = 0
        lb, ub = power(xl, xu, yl, yu)
    else:
        msg = 'encountered an exponent where the base is allowed to be negative '
        msg += 'and the exponent is allowed to be fractional and is not fixed. '
        msg += 'Assuming the lower bound of the base to be 0.'
        warnings.warn(msg)
        logger.warning(msg)
        xl = 0
        lb, ub = power(xl, xu, yl, yu)

    return lb, ub
Esempio n. 6
0
def power(xl, xu, yl, yu):
    """
    Compute bounds on x**y.
    """
    if xl > 0:
        """
        If x is always positive, things are simple. We only need to worry about the sign of y.
        """
        if yl < 0 and yu > 0:
            lb = min(xu**yl, xl**yu)
            ub = max(xl**yl, xu**yu)
        elif yl >= 0:
            lb = min(xl**yl, xl**yu)
            ub = max(xu**yl, xu**yu)
        elif yu <= 0:
            lb = min(xu**yl, xu**yu)
            ub = max(xl**yl, xl**yu)
        else:
            raise DeveloperError()
    elif xl == 0:
        if yl >= 0:
            lb = min(xl**yl, xl**yu)
            ub = max(xu**yl, xu**yu)
        else:
            lb = -inf
            ub = inf
    elif yl == yu and yl == round(yl):
        # the exponent is an integer, so x can be negative
        """
        The logic here depends on several things:
        1) The sign of x
        2) The sign of y
        3) Whether y is even or odd.
        
        There are also special cases to avoid math domain errors.
        """
        y = yl
        if xu <= 0:
            if y < 0:
                if y % 2 == 0:
                    lb = xl**y
                    if xu == 0:
                        ub = inf
                    else:
                        ub = xu**y
                else:
                    if xu == 0:
                        lb = -inf
                        ub = inf
                    else:
                        lb = xu**y
                        ub = xl**y
            else:
                if y % 2 == 0:
                    lb = xu**y
                    ub = xl**y
                else:
                    lb = xl**y
                    ub = xu**y
        else:
            if y < 0:
                if y % 2 == 0:
                    lb = min(xl**y, xu**y)
                    ub = inf
                else:
                    lb = -inf
                    ub = inf
            else:
                if y % 2 == 0:
                    lb = 0
                    ub = max(xl**y, xu**y)
                else:
                    lb = xl**y
                    ub = xu**y
    elif yl == yu:
        # the exponent has to be fractional, so x must be positive
        if xu < 0:
            msg = 'Cannot raise a negative number to the power of {0}.\n'.format(
                yl)
            msg += 'The upper bound of a variable raised to the power of {0} is {1}'.format(
                yl, xu)
            raise InfeasibleConstraintException(msg)
        xl = 0
        lb, ub = power(xl, xu, yl, yu)
    else:
        lb = -inf
        ub = inf

    return lb, ub
Esempio n. 7
0
def _generate_relational_expression(etype, lhs, rhs):
    rhs_is_relational = False
    lhs_is_relational = False

    constant_lhs = True
    constant_rhs = True

    if lhs is not None and lhs.__class__ not in native_numeric_types:
        lhs = _process_arg(lhs)
        # Note: _process_arg can return a native type
        if lhs is not None and lhs.__class__ not in native_numeric_types:
            lhs_is_relational = lhs.is_relational()
            constant_lhs = False
    if rhs is not None and rhs.__class__ not in native_numeric_types:
        rhs = _process_arg(rhs)
        # Note: _process_arg can return a native type
        if rhs is not None and rhs.__class__ not in native_numeric_types:
            rhs_is_relational = rhs.is_relational()
            constant_rhs = False

    if constant_lhs and constant_rhs:
        if etype == _eq:
            return lhs == rhs
        elif etype == _le:
            return lhs <= rhs
        elif etype == _lt:
            return lhs < rhs
        else:
            raise ValueError("Unknown relational expression type '%s'" % etype)

    if etype == _eq:
        if lhs_is_relational or rhs_is_relational:
            raise TypeError(
                "Cannot create an EqualityExpression where one of the "
                "sub-expressions is a relational expression:\n"
                "    %s\n    {==}\n    %s" % (
                    lhs,
                    rhs,
                ))
        return EqualityExpression((lhs, rhs))
    else:
        if etype == _le:
            strict = False
        elif etype == _lt:
            strict = True
        else:
            raise DeveloperError("Unknown relational expression type '%s'" %
                                 (etype, ))
        if lhs_is_relational:
            if lhs.__class__ is InequalityExpression:
                if rhs_is_relational:
                    raise TypeError(
                        "Cannot create an InequalityExpression where both "
                        "sub-expressions are relational expressions:\n"
                        "    %s\n    {%s}\n    %s" % (
                            lhs,
                            "<" if strict else "<=",
                            rhs,
                        ))
                return RangedExpression(lhs._args_ + (rhs, ),
                                        (lhs._strict, strict))
            else:
                raise TypeError(
                    "Cannot create an InequalityExpression where one of the "
                    "sub-expressions is an equality or ranged expression:\n"
                    "    %s\n    {%s}\n    %s" % (
                        lhs,
                        "<" if strict else "<=",
                        rhs,
                    ))
        elif rhs_is_relational:
            if rhs.__class__ is InequalityExpression:
                return RangedExpression((lhs, ) + rhs._args_,
                                        (strict, rhs._strict))
            else:
                raise TypeError(
                    "Cannot create an InequalityExpression where one of the "
                    "sub-expressions is an equality or ranged expression:\n"
                    "    %s\n    {%s}\n    %s" % (
                        lhs,
                        "<" if strict else "<=",
                        rhs,
                    ))
        else:
            return InequalityExpression((lhs, rhs), strict)