Exemplo n.º 1
0
class TestSize(object):
    @given(reals())
    def test_infinite_lower_bound_greater_than_everything(self, f):
        b = Interval(None, 0)
        assert almostgte(b.size(), f)

    @given(reals())
    def test_infinite_upper_bound_greater_than_everything(self, f):
        b = Interval(0, None)
        assert almostgte(b.size(), f)

    @given(reals())
    def test_infinite_bounds_greater_than_everything(self, f):
        b = Interval(None, None)
        assert almostgte(b.size(), f)

    @given(intervals(allow_infinity=False, upper_bound=1e10),
           reals(allow_infinity=False, min_value=1.0, max_value=1e20))
    def test_finite_bounds(self, a, f):
        b = a * f
        assert almostlte(a.size(), b.size())

    @given(intervals(allow_infinity=False),
           reals(allow_infinity=False, min_value=0.0, max_value=1.0))
    def test_finite_bounds_1(self, a, f):
        b = a * f
        assert almostgte(a.size(), b.size())
Exemplo n.º 2
0
class TestPowConstantBase(object):
    def _result_with_base_expo(self, visitor, base, expo, mono_expo, bounds_expo):
        rule = PowerRule()
        mono = ComponentMap()
        mono[base] = M.Constant
        mono[expo] = mono_expo
        bounds = ComponentMap()
        bounds[base] = I(base, base)
        bounds[expo] = bounds_expo
        expr = base ** expo
        assume(isinstance(expr, PowExpression))
        matched, result = visitor.visit_expression(expr, mono, bounds)
        assert matched
        return result

    @pytest.mark.parametrize(
        'mono_expo,bounds_expo',
        itertools.product(
            [M.Nondecreasing, M.Nonincreasing, M.Unknown],
            [I(None, 0), I(0, None), I(None, None)]
        )
    )
    @given(
        base=reals(max_value=-0.01, allow_infinity=False),
        expo=expressions(),
    )
    def test_negative_base(self, visitor, base, expo, mono_expo, bounds_expo):
        mono = self._result_with_base_expo(visitor, base, expo, mono_expo, bounds_expo)
        assert mono == M.Unknown

    @pytest.mark.parametrize('mono_expo,bounds_expo,expected', [
        (M.Nondecreasing, I(None, 0), M.Nondecreasing),
        (M.Nondecreasing, I(0, None), M.Unknown),
        (M.Nonincreasing, I(0, None), M.Nondecreasing),
        (M.Nonincreasing, I(None, 0), M.Unknown),
    ])
    @given(
        base=reals(min_value=0.01, max_value=0.999),
        expo=expressions(),
    )
    def test_base_between_0_and_1(self, visitor, base, expo, mono_expo, bounds_expo, expected):
        mono = self._result_with_base_expo(visitor, base, expo, mono_expo, bounds_expo)
        assert mono == expected

    @pytest.mark.parametrize('mono_expo,bounds_expo,expected', [
        (M.Nondecreasing, I(0, None), M.Nondecreasing),
        (M.Nondecreasing, I(None, 0), M.Unknown),
        (M.Nonincreasing, I(None, 0), M.Nondecreasing),
        (M.Nonincreasing, I(0, None), M.Unknown),
    ])
    @given(
        base=reals(min_value=1.01, allow_infinity=False),
        expo=expressions(),
    )
    def test_base_gt_1(self, visitor, base, expo, mono_expo, bounds_expo, expected):
        mono = self._result_with_base_expo(visitor, base, expo, mono_expo, bounds_expo)
        assert mono == expected
Exemplo n.º 3
0
class TestQuadratic:
    def _rule_result(self, A):
        n = A.shape[0]
        var = [PE(ET.Variable) for _ in range(n)]
        terms = []
        for i in range(n):
            for j in range(i + 1):
                terms.append(BilinearTerm(var[i], var[j], A[i, j]))

        expr = PE(ET.Quadratic, terms=terms, children=var)
        rule = QuadraticRule()
        return rule.apply(expr, None)

    @given(coefs=st.lists(reals(min_value=0, allow_infinity=False),
                          min_size=1))
    def test_sum_of_squares_is_convex_with_positive_coefficients(self, coefs):
        assume(any([c > 0 and not np.isclose(c, 0) for c in coefs]))
        A = np.eye(len(coefs)) * coefs
        cvx = self._rule_result(A)
        assert cvx == C.Convex

    @given(coefs=st.lists(reals(max_value=0, allow_infinity=False),
                          min_size=1))
    def test_sum_of_squares_is_concave_with_negative_coefficients(self, coefs):
        assume(any([c < 0 and not np.isclose(c, 0) for c in coefs]))
        A = np.eye(len(coefs)) * coefs
        cvx = self._rule_result(A)
        assert cvx == C.Concave

    @given(neg_coefs=st.lists(reals(max_value=0.0, allow_infinity=False),
                              min_size=1),
           pos_coefs=st.lists(reals(min_value=0.0, allow_infinity=False),
                              min_size=1))
    def test_sum_of_squares_is_unknown_otherwise(self, neg_coefs, pos_coefs):
        assume(any([n < 0 and not np.isclose(n, 0) for n in neg_coefs]))
        assume(any([p > 0 and not np.isclose(p, 0) for p in pos_coefs]))
        coefs = neg_coefs + pos_coefs
        A = np.eye(len(coefs)) * coefs
        cvx = self._rule_result(A)
        assert cvx == C.Unknown

    @given(st.integers(min_value=1, max_value=100))
    def test_positive_definite_is_convex(self, n):
        B = np.random.randn(n, n)
        A = np.eye(n) * n + 0.5 * (B + B.T)
        cvx = self._rule_result(A)
        assert cvx == C.Convex

    @given(st.integers(min_value=1, max_value=100))
    def test_negative_definite_is_concave(self, n):
        B = np.random.randn(n, n)
        A = -np.eye(n) * n - 0.5 * (B + B.T)
        cvx = self._rule_result(A)
        assert cvx == C.Concave
Exemplo n.º 4
0
def intervals(draw, allow_infinity=True, lower_bound=None, upper_bound=None):
    if lower_bound is None and allow_infinity:
        lower = draw(
            st.one_of(st.none(),
                      reals(max_value=upper_bound, allow_infinity=False)))
    else:
        lower = draw(
            reals(min_value=lower_bound,
                  max_value=upper_bound,
                  allow_infinity=False))

    if upper_bound is None and allow_infinity:
        upper = draw(
            st.one_of(st.none(), reals(min_value=lower, allow_infinity=False)))
    else:
        upper = draw(
            reals(
                min_value=lower,
                max_value=upper_bound,
                allow_infinity=False,
            ))
    return Interval(lower, upper)
Exemplo n.º 5
0
class TestContains(object):
    @given(intervals())
    def test_bound_contains_itself(self, a):
        assert a in a

    @given(intervals(allow_infinity=False))
    def test_bound_contains_midpoint(self, a):
        assume((a.upper_bound - a.lower_bound) != inf)
        m = a.lower_bound + (a.upper_bound - a.lower_bound) / 2.0
        assert m in a

    @given(reals())
    def test_infinity_bound_contains_everything(self, n):
        a = Interval(None, None)
        assert n in a
Exemplo n.º 6
0
# We test with denominator/denominator linear, constant and variable.


class FractionalContext:
    def __init__(self, bounds=None):
        if bounds is None:
            bounds = {}
        self._b = bounds

    def bounds(self, expr):
        return self._b[expr]


@pytest.mark.skip('Not update')
# (a_1 x) / x is linear (constant)
@given(coef=reals(allow_infinity=False))
def test_linear_over_variable(coef):
    rule = FractionalRule()
    x = PE(ET.Variable)
    num = PE(ET.Linear, [x], coefficients=[coef], constant_term=0.0)
    ctx = FractionalContext({
        x: I(0, None),
    })
    result = rule.apply(PE(ET.Division, [num, x]), ctx)
    assert result == Convexity.Linear


@pytest.mark.skip('Not update')
# (a_1 x + b_1) / x
@given(coef=reals(allow_infinity=False), const=reals(allow_infinity=False))
def test_linear_with_constant_over_variable(coef, const):
Exemplo n.º 7
0
class TestPowConstantExponent(object):
    def _rule_result(self, visitor, base, cvx_base, mono_base, bounds_base,
                     expo):
        convexity = ComponentMap()
        convexity[base] = cvx_base
        convexity[expo] = C.Linear
        mono = ComponentMap()
        mono[base] = mono_base
        mono[expo] = M.Constant
        bounds = ComponentMap()
        bounds[base] = bounds_base
        bounds[expo] = I(expo, expo)
        expr = PowExpression([base, expo])
        matched, result = visitor.visit_expression(expr, convexity, mono,
                                                   bounds)
        assert matched
        return result

    @pytest.mark.parametrize(
        'cvx_base,mono_base,bounds_base',
        itertools.product(
            [C.Convex, C.Concave, C.Linear, C.Unknown],
            [M.Nondecreasing, M.Nonincreasing, M.Unknown],
            [I(None, 0), I(0, None), I(None, None)],
        ))
    @given(base=expressions())
    def test_exponent_equals_0(self, visitor, base, cvx_base, mono_base,
                               bounds_base):
        cvx = self._rule_result(visitor, base, cvx_base, mono_base,
                                bounds_base, 0.0)
        assert cvx == C.Linear

    @pytest.mark.parametrize(
        'cvx_base,mono_base,bounds_base',
        itertools.product(
            [C.Convex, C.Concave, C.Linear, C.Unknown],
            [M.Nondecreasing, M.Nonincreasing, M.Unknown],
            [I(None, 0), I(0, None), I(None, None)],
        ))
    @given(base=expressions())
    def test_exponent_equals_1(self, visitor, base, cvx_base, mono_base,
                               bounds_base):
        cvx = self._rule_result(visitor, base, cvx_base, mono_base,
                                bounds_base, 1.0)
        assert cvx == cvx_base

    @pytest.mark.parametrize('cvx_base,mono_base,bounds_base,expected', [
        (C.Linear, M.Nondecreasing, I(None, None), C.Convex),
        (C.Convex, M.Unknown, I(0, None), C.Convex),
        (C.Convex, M.Unknown, I(None, 0), C.Unknown),
        (C.Concave, M.Unknown, I(0, None), C.Unknown),
        (C.Concave, M.Unknown, I(None, 0), C.Convex),
    ])
    @given(
        base=expressions(),
        expo=st.integers(min_value=1),
    )
    def test_positive_even_integer(self, visitor, base, expo, cvx_base,
                                   mono_base, bounds_base, expected):
        cvx = self._rule_result(visitor, base, cvx_base, mono_base,
                                bounds_base, 2 * expo)
        assert cvx == expected

    @pytest.mark.parametrize('cvx_base,mono_base,bounds_base,expected', [
        (C.Convex, M.Unknown, I(None, 0), C.Convex),
        (C.Convex, M.Unknown, I(0, None), C.Concave),
        (C.Concave, M.Unknown, I(0, None), C.Convex),
        (C.Concave, M.Unknown, I(None, 0), C.Concave),
    ])
    @given(
        base=expressions(),
        expo=st.integers(min_value=1),
    )
    def test_negative_even_integer(self, visitor, base, expo, cvx_base,
                                   mono_base, bounds_base, expected):
        cvx = self._rule_result(visitor, base, cvx_base, mono_base,
                                bounds_base, -2 * expo)
        assert cvx == expected

    @pytest.mark.parametrize('cvx_base,mono_base,bounds_base,expected', [
        (C.Convex, M.Unknown, I(0, None), C.Convex),
        (C.Convex, M.Unknown, I(None, 0), C.Unknown),
        (C.Concave, M.Unknown, I(None, 0), C.Concave),
        (C.Concave, M.Unknown, I(0, None), C.Unknown),
    ])
    @given(
        base=expressions(),
        expo=st.integers(min_value=1),
    )
    def test_positive_odd_integer(self, visitor, base, expo, cvx_base,
                                  mono_base, bounds_base, expected):
        cvx = self._rule_result(visitor, base, cvx_base, mono_base,
                                bounds_base, 2 * expo + 1)
        assert cvx == expected

    @pytest.mark.parametrize('cvx_base,mono_base,bounds_base,expected', [
        (C.Concave, M.Unknown, I(0, None), C.Convex),
        (C.Concave, M.Unknown, I(None, 0), C.Unknown),
        (C.Convex, M.Unknown, I(None, 0), C.Concave),
        (C.Convex, M.Unknown, I(0, None), C.Unknown),
    ])
    @given(
        base=expressions(),
        expo=st.integers(min_value=1),
    )
    def test_negative_odd_integer(self, visitor, base, expo, cvx_base,
                                  mono_base, bounds_base, expected):
        cvx = self._rule_result(visitor, base, cvx_base, mono_base,
                                bounds_base, -2 * expo + 1)
        assert cvx == expected

    @given(
        base=expressions(),
        expo=reals(min_value=1, allow_infinity=False),
    )
    def test_positive_gt_1_non_integer_negative_base(self, visitor, base,
                                                     expo):
        expo = expo + 1e-6
        assume(expo != int(expo))
        cvx = self._rule_result(visitor, base, C.Convex, M.Unknown,
                                I(None, -1), expo)
        assert cvx == C.Unknown

    @given(
        base=expressions(),
        expo=reals(min_value=1, allow_infinity=False),
    )
    def test_positive_gt_1_non_integer(self, visitor, base, expo):
        expo = expo + 1e-5  # make it positive
        assume(expo != int(expo))
        cvx = self._rule_result(visitor, base, C.Convex, M.Unknown, I(0, None),
                                expo)
        assert cvx == C.Convex

    @pytest.mark.parametrize('cvx,expected', [(C.Convex, C.Concave),
                                              (C.Concave, C.Convex)])
    @given(
        base=expressions(),
        expo=reals(max_value=0, allow_infinity=False),
    )
    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

    @given(
        base=expressions(),
        expo=reals(min_value=0, max_value=1, allow_infinity=False),
    )
    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
Exemplo n.º 8
0
class TestPowConstantBase:
    def _rule_result(self, visitor, base, expo, cvx_expo, mono_expo,
                     bounds_expo):
        convexity = ComponentMap()
        convexity[expo] = cvx_expo
        convexity[base] = C.Linear
        mono = ComponentMap()
        mono[expo] = mono_expo
        mono[base] = M.Constant
        bounds = ComponentMap()
        bounds[base] = I(base, base)
        bounds[expo] = bounds_expo

        expr = PowExpression([base, expo])
        matched, result = visitor.visit_expression(expr, convexity, mono,
                                                   bounds)
        assert matched
        return result

    @pytest.mark.parametrize(
        'cvx_expo,mono_expo,bounds_expo',
        itertools.product(
            [C.Convex, C.Concave, C.Linear, C.Unknown],
            [M.Nondecreasing, M.Nonincreasing, M.Unknown],
            [I(None, 0), I(0, None), I(None, None)],
        ))
    @given(
        base=reals(max_value=-0.01, allow_infinity=False),
        expo=expressions(),
    )
    def test_negative_base(self, visitor, base, expo, cvx_expo, mono_expo,
                           bounds_expo):
        cvx = self._rule_result(visitor, base, expo, cvx_expo, mono_expo,
                                bounds_expo)
        assert cvx == C.Unknown

    @pytest.mark.parametrize(
        'cvx_expo,mono_expo,bounds_expo',
        itertools.product(
            [C.Convex, C.Concave, C.Linear, C.Unknown],
            [M.Nondecreasing, M.Nonincreasing, M.Unknown],
            [I(None, 0), I(0, None), I(None, None)],
        ))
    @given(
        base=reals(min_value=0.001, max_value=0.999),
        expo=expressions(),
    )
    def test_base_between_0_and_1(self, visitor, base, expo, cvx_expo,
                                  mono_expo, bounds_expo):
        if cvx_expo == C.Concave or cvx_expo == C.Linear:
            expected = C.Convex
        else:
            expected = C.Unknown
        cvx = self._rule_result(visitor, base, expo, cvx_expo, mono_expo,
                                bounds_expo)
        assert cvx == expected

    @pytest.mark.parametrize(
        'cvx_expo,mono_expo,bounds_expo',
        itertools.product(
            [C.Convex, C.Concave, C.Linear, C.Unknown],
            [M.Nondecreasing, M.Nonincreasing, M.Unknown],
            [I(None, 0), I(0, None), I(None, None)],
        ))
    @given(
        base=reals(min_value=1, allow_infinity=False),
        expo=expressions(),
    )
    def test_base_gt_1(self, visitor, base, expo, cvx_expo, mono_expo,
                       bounds_expo):
        if cvx_expo == C.Convex or cvx_expo == C.Linear:
            expected = C.Convex
        else:
            expected = C.Unknown
        cvx = self._rule_result(visitor, base, expo, cvx_expo, mono_expo,
                                bounds_expo)
        assert cvx == expected
Exemplo n.º 9
0
class TestProduct:
    def _rule_result(self, visitor, f, g, cvx_f, cvx_g, mono_f, mono_g,
                     bounds_f, bounds_g):
        convexity = ComponentMap()
        convexity[f] = cvx_f
        convexity[g] = cvx_g
        mono = ComponentMap()
        mono[f] = mono_f
        mono[g] = mono_g
        bounds = ComponentMap()
        bounds[f] = bounds_f
        bounds[g] = bounds_g
        expr = f * g
        assume(isinstance(expr, ProductExpression))
        matched, result = visitor.visit_expression(expr, convexity, mono,
                                                   bounds)
        assert matched
        return result

    @pytest.mark.parametrize('cvx_f,bounds_g,expected', [
        (C.Convex, I(0, None), C.Convex),
        (C.Convex, I(None, 0), C.Concave),
        (C.Concave, I(0, None), C.Concave),
        (C.Concave, I(None, 0), C.Convex),
        (C.Linear, I(0, None), C.Linear),
        (C.Linear, I(None, 0), C.Linear),
        (C.Unknown, I(0, None), C.Unknown),
        (C.Unknown, I(None, 0), C.Unknown),
    ])
    @given(f=expressions(), g=constants())
    def test_product_with_constant(self, visitor, f, g, cvx_f, bounds_g,
                                   expected):
        assume(f.is_expression_type()
               and not isinstance(f, MonomialTermExpression))
        cvx_g = C.Linear  # g is constant
        mono_f = M.Unknown
        mono_g = M.Constant
        bounds_f = I(None, None)
        assert self._rule_result(visitor, f, g, cvx_f, cvx_g, mono_f, mono_g,
                                 bounds_f, bounds_g) == expected
        assert self._rule_result(visitor, g, f, cvx_g, cvx_f, mono_g, mono_f,
                                 bounds_g, bounds_f) == expected

    @pytest.mark.parametrize('cvx_f,bounds_f,expected', [
        (C.Linear, I(None, None), C.Convex),
        (C.Linear, I(0, None), C.Convex),
        (C.Linear, I(None, 0), C.Convex),
        (C.Convex, I(None, None), C.Unknown),
        (C.Convex, I(0, None), C.Convex),
        (C.Convex, I(None, 0), C.Unknown),
        (C.Concave, I(None, None), C.Unknown),
        (C.Concave, I(0, None), C.Unknown),
        (C.Concave, I(None, 0), C.Convex),
    ])
    @given(f=expressions())
    def test_product_with_itself(self, visitor, f, cvx_f, bounds_f, expected):
        convexity = ComponentMap()
        convexity[f] = cvx_f
        bounds = ComponentMap()
        bounds[f] = bounds_f
        expr = f * f
        matched, result = visitor.visit_expression(expr, convexity, None,
                                                   bounds)
        assert matched
        assert result == expected

    @given(var=variables(), coef=reals())
    def test_product_with_itself_with_coeff(self, visitor, var, coef):
        if coef > 0:
            expected = C.Convex
        else:
            expected = C.Concave
        rule = ProductRule()
        g = coef * var
        assume(isinstance(g, ProductExpression))
        matched, result = visitor.visit_expression(ProductExpression([var, g]),
                                                   None, None, None)
        assert result == expected
        matched, result = visitor.visit_expression(ProductExpression([g, var]),
                                                   None, None, None)
        assert result == expected

    @given(var=variables(),
           vars_with_coef=st.lists(st.tuples(
               variables(),
               constants(),
           ), ))
    def test_product_linear_by_var(self, visitor, var, vars_with_coef):
        rule = ProductRule()
        mono = ComponentMap()
        lin = sum(v * c for v, c in vars_with_coef)
        mono[var] = M.Nondecreasing
        mono[lin] = M.Nondecreasing
        matched, result = visitor.visit_expression(
            ProductExpression([var, lin]), None, mono, None)
        assert result == C.Unknown
        matched, result = visitor.visit_expression(
            ProductExpression([lin, var]), None, mono, None)
        assert result == C.Unknown
Exemplo n.º 10
0
    assert matched
    assert not result.is_polynomial()


@given(expressions(), expressions(), polynomial_degrees())
def test_power_with_non_polynomial_base(visitor, base, expo, expo_poly):
    poly = ComponentMap()
    poly[base] = PolynomialDegree(None)
    poly[expo] = expo_poly
    expr = base**expo
    matched, result = visitor.visit_expression(expr, poly)
    assert matched
    assert not result.is_polynomial()


@given(variables(), reals(min_value=1.1))
def test_power_constant_power_constant(visitor, v, c):
    expr = v**c
    poly = ComponentMap()
    poly[v] = PolynomialDegree(0)
    poly[c] = PolynomialDegree(0)
    matched, result = visitor.visit_expression(expr, poly)
    assert matched
    assert result.is_polynomial() and result.degree == 0


@given(variables(), reals(allow_infinity=False))
def test_power_non_constant(visitor, v, c):
    assume(c != int(c))
    expr = v**c
    poly = ComponentMap()
Exemplo n.º 11
0
from hypothesis.strategies import integers, lists
from pyomo.core.kernel.component_map import ComponentMap

from suspect.fbbt.propagation.rules import *
from suspect.fbbt.propagation.visitor import BoundsPropagationVisitor
from suspect.interval import Interval
from suspect.pyomo.expressions import *
from tests.strategies import expressions, reals, intervals


@pytest.fixture
def visitor():
    return BoundsPropagationVisitor()


@given(reals(), reals())
def test_variable_bound(visitor, a, b):
    lb = min(a, b)
    ub = max(a, b)
    var = pe.Var(bounds=(lb, ub))
    var.construct()
    matched, result = visitor.visit_expression(var, None)
    assert matched
    assert result == Interval(lb, ub)


@given(reals())
def test_constant_bound(visitor, c):
    const = NumericConstant(c)
    matched, result = visitor.visit_expression(const, None)
    assert matched
Exemplo n.º 12
0
class TestPowConstantExponent(object):
    def _result_with_base_expo(self, visitor, base, mono_base, bounds_base, expo):
        mono = ComponentMap()
        mono[base] = mono_base
        mono[expo] = M.Constant
        bounds = ComponentMap()
        bounds[base] = bounds_base
        bounds[expo] = I(expo, expo)
        expr = PowExpression([base, expo])
        matched, result = visitor.visit_expression(expr, mono, bounds)
        assert matched
        return result

    @pytest.mark.parametrize(
        'mono_base,bounds_base',
        itertools.product([M.Nonincreasing, M.Nondecreasing],
                          [I(None, 0), I(0, None), I(None, None)])
    )
    @given(base=expressions())
    def test_exponent_equals_1(self, visitor, base, mono_base, bounds_base):
        mono = self._result_with_base_expo(visitor, base, mono_base, bounds_base, 1.0)
        assert mono == mono_base

    @pytest.mark.parametrize(
        'mono_base,bounds_base',
        itertools.product([M.Nonincreasing, M.Nondecreasing],
                          [I(None, 0), I(0, None), I(None, None)])
    )
    @given(base=expressions())
    def test_exponent_equals_0(self, visitor, base, mono_base, bounds_base):
        mono = self._result_with_base_expo(visitor, base, mono_base, bounds_base, 0.0)
        assert mono == M.Constant

    @pytest.mark.parametrize('mono_base,bounds_base,expected', [
        (M.Nondecreasing, I(0, None), M.Nondecreasing),
        (M.Nonincreasing, I(None, 0), M.Nondecreasing),

        (M.Nondecreasing, I(None, 0), M.Nonincreasing),
        (M.Nonincreasing, I(0, None), M.Nonincreasing),
    ])
    @given(
        base=expressions(),
        expo=st.integers(min_value=1, max_value=1000),
    )
    def test_positive_even_integer(self, visitor, base, expo, mono_base, bounds_base, expected):
        mono = self._result_with_base_expo(visitor, base, mono_base, bounds_base, 2*expo)
        assert mono == expected

    @pytest.mark.parametrize('mono_base,bounds_base,expected', [
        (M.Nondecreasing, I(0, None), M.Nonincreasing),
        (M.Nonincreasing, I(None, 0), M.Nonincreasing),

        (M.Nondecreasing, I(None, 0), M.Nondecreasing),
        (M.Nonincreasing, I(0, None), M.Nondecreasing),
    ])
    @given(
        base=expressions(),
        expo=st.integers(min_value=1, max_value=1000)
    )
    def test_negative_even_integer(self, visitor, base, expo, mono_base, bounds_base, expected):
        mono = self._result_with_base_expo(visitor, base, mono_base, bounds_base, -2*expo)
        assert mono == expected

    @pytest.mark.parametrize('mono_base,expected', [
        (M.Nondecreasing, M.Nondecreasing),
        (M.Nonincreasing, M.Nonincreasing),
    ])
    @given(
        base=expressions(),
        expo=st.integers(min_value=1, max_value=1000)
    )
    def test_positive_odd_integer(self, visitor, base, expo, mono_base, expected):
        mono = self._result_with_base_expo(visitor, base, mono_base, I(None, None), 2*expo+1)
        assert mono == expected

    @pytest.mark.parametrize('mono_base,expected', [
        (M.Nondecreasing, M.Nonincreasing),
        (M.Nonincreasing, M.Nondecreasing),
    ])
    @given(
        base=expressions(),
        expo=st.integers(min_value=1, max_value=1000)
    )
    def test_negative_odd_integer(self, visitor, base, expo, mono_base, expected):
        mono = self._result_with_base_expo(
            visitor, base, mono_base, I(None, None), -2*expo+1
        )
        assert mono == expected

    @pytest.mark.parametrize('mono_base', [M.Nondecreasing, M.Nondecreasing])
    @given(
        base=expressions(),
        expo=reals(allow_infinity=False, min_value=-1e5, max_value=1e5),
    )
    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

    @pytest.mark.parametrize('mono_base,expected', [
        (M.Nondecreasing, M.Nondecreasing),
        (M.Nonincreasing, M.Nonincreasing)
    ])
    @given(
        base=expressions(),
        expo=reals(allow_infinity=False, min_value=1e-5, max_value=1e-5)
    )
    def test_positive_noninteger(self, visitor, base, expo, mono_base, expected):
        mono = self._result_with_base_expo(visitor, base, mono_base, I(0, None), expo)
        assert mono == expected

    @pytest.mark.parametrize('mono_base,expected', [
        (M.Nonincreasing, M.Nondecreasing),
        (M.Nondecreasing, M.Nonincreasing)
    ])
    @given(
        base=expressions(),
        expo=reals(allow_infinity=False, min_value=-1e5, max_value=-1e-5)
    )
    def test_negative_noninteger(self, visitor, base, expo, mono_base, expected):
        mono = self._result_with_base_expo(visitor, base, mono_base, I(0, None), expo)
        assert mono == expected