Exemple #1
0
def _monotonicity_constant_exponent(base, expo, mono_base, mono_expo,
                                    bounds_base, bounds_expo):
    # pylint: disable=too-many-return-statements, too-many-arguments
    if almosteq(expo, 1):
        return mono_base
    elif almosteq(expo, 0):
        return Monotonicity.Constant
    is_integer = almosteq(expo, int(expo))
    is_even = almosteq(expo % 2, 0)
    if is_integer and is_even:
        return _monotonicity_even_exponent(
            base,
            expo,
            mono_base,
            mono_expo,
            bounds_base,
            bounds_expo,
        )
    elif is_integer:  # is odd
        return _monotonicity_odd_exponent(
            base,
            expo,
            mono_base,
            mono_expo,
            bounds_base,
            bounds_expo,
        )
    return _monotonicity_noninteger_exponent(
        base,
        expo,
        mono_base,
        mono_expo,
        bounds_base,
        bounds_expo,
    )
Exemple #2
0
 def test_noninteger_negative_base(self, visitor, base, expo, mono_base):
     assume(not almosteq(expo,  0))
     assume(not almosteq(expo, int(expo)))
     mono = self._result_with_base_expo(
         visitor, base, mono_base, I(None, 0), expo
     )
     assert mono == M.Unknown
Exemple #3
0
    def __eq__(self, other):
        if isinstance(other, (int, float)):
            return self.__eq__(Interval(other, other))

        if not isinstance(other, Interval):
            return False
        # pylint: disable=protected-access
        return (almosteq(self._lower, other._lower)
                and almosteq(self._upper, other._upper))
Exemple #4
0
def _constant_expo_pow_convexity(expo, cvx_base, bounds_base):
    is_integer = almosteq(expo, int(expo))
    is_even = almosteq(expo % 2, 0)

    if almosteq(expo, 0):
        return Convexity.Linear
    elif almosteq(expo, 1):
        return cvx_base
    elif is_integer and is_even:
        return _integer_even_expo_pow_convexity(expo, cvx_base, bounds_base)
    elif is_integer:
        return _integer_odd_expo_pow_convexity(expo, cvx_base, bounds_base)
    return _noninteger_expo_pow_convexity(expo, cvx_base, bounds_base)
Exemple #5
0
def expr_equal(expr1, expr2):
    stack = [(expr1, expr2)]
    while len(stack) > 0:
        e1, e2 = stack.pop()

        if type(e1) != type(e2):
            return False

        if _is_leaf_node(e1):
            if isinstance(e1, Number):
                if not almosteq(e1, e2):
                    return False

            if isinstance(e1, NumericConstant):
                if not almosteq(e1.value, e2.value):
                    return False

            if isinstance(e1, _VarData):
                if id(e1) != id(e2):
                    return False
        else:
            if len(e1._args_) != len(e2._args_):
                return False

            if isinstance(e1, LinearExpression):
                if len(e1.linear_vars) != len(e2.linear_vars):
                    return False

                for v1, v2 in zip(e1.linear_vars, e2.linear_vars):
                    if id(v1) != id(v2):
                        return False

                for c1, c2 in zip(e2.linear_coefs, e2.linear_coefs):
                    if not almosteq(c1, c2):
                        return False

                if not almosteq(e1.constant, e2.constant):
                    return False

            if isinstance(e1, UnaryFunctionExpression):
                if e1.name != e2.name:
                    return False

            # all checks passed, check args
            for a1, a2 in zip(e1._args_, e2._args_):
                stack.append((a1, a2))

    return True
Exemple #6
0
 def test_positive_lt_0_non_integer(self, visitor, base, expo, cvx,
                                    expected):
     expo = expo - 1e-5  # make it negative
     assume(not almosteq(expo, int(expo)))
     cvx = self._rule_result(visitor, base, cvx, M.Unknown, I(0, None),
                             expo)
     assert cvx == expected
Exemple #7
0
    def apply(self, expr, ctx):
        num, den = expr.children
        if not self._valid_expression(num, den):
            return None

        a1, x, b1 = self._linear_components(num)
        a2, y, b2 = self._linear_components(den)

        if x is not None and y is not None:
            if x is not y:
                return None

        if x is not None:
            x_bound = ctx.bounds(x)
        elif y is not None:
            x_bound = ctx.bounds(y)
        else:
            raise RuntimeError('no variable in numerator or denonimator')

        dd_num = -2 * a2 * (a1 * b2 - a2 * b1)
        dd_den = a2 * x_bound + b2

        if almosteq(dd_num, 0):
            return Convexity.Linear

        dd_bound = dd_den * (1 / dd_num)

        if dd_bound.is_nonpositive():
            return Convexity.Concave
        elif dd_bound.is_nonnegative():
            return Convexity.Convex
        # inconclusive
        return None
Exemple #8
0
def _integer_positive_odd_expo_pow_convexity(expo, cvx_base, bounds_base):
    if almosteq(expo, 1):  # pragma: no cover
        return cvx_base  # we won't reach this because of the check in apply.
    elif cvx_base.is_convex() and bounds_base.is_nonnegative():
        return Convexity.Convex
    elif cvx_base.is_concave() and bounds_base.is_nonpositive():
        return Convexity.Concave
    return Convexity.Unknown
Exemple #9
0
    def _tan(self):
        if almostgte(self.size(), pi):
            return Interval(None, None)
        lower = self._lower % pi
        upper = lower + (self._upper - self._lower)
        new_lower = min_(tan(lower, RM.RD), tan(upper, RM.RD))
        new_upper = max_(tan(lower, RM.RU), tan(upper, RM.RU))

        if almosteq(lower, 0.5 * pi):
            new_lower = None

        if almosteq(upper, 0.5 * pi):
            new_upper = None

        if new_lower is not None and new_upper is not None:
            new = Interval(new_lower, new_upper)
            if 0.5 * pi in new or pi in new:
                return Interval(None, None)
        return Interval(new_lower, new_upper)
Exemple #10
0
def _collect_expression_types(quadratic, linear):
    """Collect different expression types from quadratic and linear expression.

    Given an expression

    a0 x0^2 + a1 x1^2  + ...+ b0 x0 + b1 x1 + b2 x2 + ...

    Returns the quadratic univariate expressions like `a0 x0^2`, the bilinear
    expressions `c x1 x2`, and the linear expressions `bn xn`.

    :param quadratic: the quadratic expression
    :param linear: the linear expression
    :return:
    A tuple (univariate_expr, bilinear_terms, linear_terms)
    """
    variables_coef = ComponentMap()

    for coef, var in zip(linear.linear_coefs, linear.linear_vars):
        variables_coef[var] = \
            _VariableCoefficients(quadratic=0.0, linear=coef)

    non_univariate_terms = []
    for term in quadratic.terms:
        if term.var1 is term.var2:
            var_coef = variables_coef.get(term.var1, None)
            if var_coef is None:
                non_univariate_terms.append(term)
            else:
                linear_coef = var_coef.linear
                var_coef = _VariableCoefficients(quadratic=term.coefficient,
                                                 linear=linear_coef)
                variables_coef[term.var1] = var_coef
        else:
            non_univariate_terms.append(term)

    linear_terms = [(v, vc.linear) for v, vc in variables_coef.items()
                    if almosteq(vc.quadratic, 0.0)]
    univariate_terms = [(v, vc.quadratic, vc.linear)
                        for v, vc in variables_coef.items()
                        if not almosteq(vc.quadratic, 0.0)]

    return univariate_terms, non_univariate_terms, linear_terms
Exemple #11
0
    def __pow__(self, pow_):
        # Rules and tests from Boost::interval
        if isinstance(pow_, Interval):
            if not almosteq(pow_.lower_bound, pow_.upper_bound):
                return Interval(None, None)
            pow_ = pow_.lower_bound
            return self.__pow__(pow_)
        if not almosteq(pow_, int(pow_)):
            return Interval(None, None)
        pow_ = int(pow_)

        if almosteq(pow_, 0.0):
            return Interval(1, 1)

        if pow_ < 0:
            return 1.0 / self.__pow__(-pow_)

        is_odd = (abs(pow_) % 2) == 1
        if self.upper_bound < 0:
            # [-2, -1]
            yl = power(-self.upper_bound, pow_, RM.RD)
            yu = power(-self.lower_bound, pow_, RM.RU)
            if is_odd:
                return Interval(-yu, -yl)
            else:
                return Interval(yl, yu)
        elif self.lower_bound < 0:
            # [-1, 1]
            if is_odd:
                yl = power(-self.lower_bound, pow_, RM.RD)
                yu = power(self.upper_bound, pow_, RM.RU)
                return Interval(-yl, yu)
            else:
                return Interval(
                    0,
                    power(max(-self.lower_bound, self.upper_bound), pow_,
                          RM.RU))
        else:
            # [1, 2]
            return Interval(power(self.lower_bound, pow_, RM.RD),
                            power(self.upper_bound, pow_, RM.RU))
Exemple #12
0
    def apply(self, expr, bounds):
        assert len(expr.args) == 2
        _, expo = expr.args
        if type(expo) not in nonpyomo_leaf_types:
            if not expo.is_constant():
                return Interval(None, None)
            expo = expo.value

        is_even = almosteq(expo % 2, 0)
        is_positive = expo > 0
        if is_even and is_positive:
            return Interval(0, None)
        return Interval(None, None)
Exemple #13
0
    def intersect(self, other, rel_eps=None, abs_eps=None):
        """Intersect this interval with another."""
        if not isinstance(other, Interval):
            raise ValueError('intersect with non Interval value')

        new_lower = max(self._lower, other._lower)  # pylint: disable=protected-access
        new_upper = min(self._upper, other._upper)  # pylint: disable=protected-access

        if new_upper < new_lower:
            if almosteq(new_lower, new_upper, rel_eps=rel_eps,
                        abs_eps=abs_eps):
                new_lower = new_upper
            else:
                raise EmptyIntervalError()
        return Interval(new_lower, new_upper)
def test_linear_with_constant_over_variable(coef, const):
    assume(coef != 0.0)
    assume(coef < MAX_NUM and const < MAX_NUM)

    rule = FractionalRule()
    x = PE(ET.Variable)
    num = PE(ET.Linear, [x], coefficients=[coef], constant_term=const)
    ctx = FractionalContext({
        x: I(0, None),
    })
    result = rule.apply(PE(ET.Division, [num, x]), ctx)
    if almosteq(const, 0):
        expected = Convexity.Linear
    elif const > 0:
        expected = Convexity.Convex
    elif const < 0:
        expected = Convexity.Concave
Exemple #15
0
def _monotonicity_constant_base(base, _expo, _mono_base, mono_expo,
                                _bounds_base, bounds_expo):
    # pylint: disable=too-many-return-statements, too-many-arguments
    if base < 0:
        return Monotonicity.Unknown
    elif almosteq(base, 0):
        return Monotonicity.Constant
    elif 0 < base < 1:
        if mono_expo.is_nondecreasing() and bounds_expo.is_nonpositive():
            return Monotonicity.Nondecreasing
        elif mono_expo.is_nonincreasing() and bounds_expo.is_nonnegative():
            return Monotonicity.Nondecreasing
        return Monotonicity.Unknown
    elif almostgte(base, 1):
        if mono_expo.is_nondecreasing() and bounds_expo.is_nonnegative():
            return Monotonicity.Nondecreasing
        elif mono_expo.is_nonincreasing() and bounds_expo.is_nonpositive():
            return Monotonicity.Nondecreasing
        return Monotonicity.Unknown
    return Monotonicity.Unknown  # pragma: no cover
Exemple #16
0
    def apply(self, expr, bounds):
        base, expo = expr.args
        if type(expo) not in nonpyomo_leaf_types:
            if not expo.is_constant():
                return None
            expo = expo.value

        if not almosteq(expo, 2):
            return None

        expr_bound = bounds[expr]
        # the bound of a square number is never negative, but check anyway to
        # avoid unexpected crashes.
        if not expr_bound.is_nonnegative():
            return None

        sqrt_bound = expr_bound.sqrt()
        return [
            Interval(-sqrt_bound.upper_bound, sqrt_bound.upper_bound),
            None,
        ]
Exemple #17
0
    def hash(self, f):
        f = make_number(f)
        if self.root is None:
            self.root = self._make_node(f)
            return self.root.hash

        curr_node = self.root
        while True:
            if almosteq(f, curr_node.num):
                return curr_node.hash
            elif almostlte(f, curr_node.num):
                if curr_node.left is None:
                    new_node = self._make_node(f)
                    curr_node.left = new_node
                    return new_node.hash
                else:
                    curr_node = curr_node.left
            else:
                if curr_node.right is None:
                    new_node = self._make_node(f)
                    curr_node.right = new_node
                    return new_node.hash
                else:
                    curr_node = curr_node.right
Exemple #18
0
 def is_zero(self):
     """Check if the interval is [0, 0]."""
     if self._is_zero is None:
         self._is_zero = \
             almosteq(self._lower, 0) and almosteq(self._upper, 0)
     return self._is_zero
Exemple #19
0
 def test_positive_0_1_non_integer(self, visitor, base, expo):
     assume(not almosteq(expo, int(expo)))
     cvx = self._rule_result(visitor, base, C.Concave, M.Unknown,
                             I(0, None), expo)
     assert cvx == C.Concave