def test_mathify_pythonify(): math2python = { Integer(1): 1, Integer(1) / 2: fractions.Fraction(1, 2), Integer(3) / (-5): fractions.Fraction(-3, 5), Pow(4, 2): fractions.Fraction(4)**2, Pow(5, 3): 5**3, } # keys() and values() are guaranteed to be in the same order, although a # different order shouldn't matter math_sum = Add(math2python.keys()) # not gentle_simplify()ied python_sum = sum(math2python.values()) math2python[math_sum] = python_sum math_product = Mul(math2python.keys()) python_product = functools.reduce(operator.mul, math2python.values()) math2python[math_product] = python_product for math, python in math2python.items(): assert mathify(math) == math # returned as is assert mathify(python) == math.gentle_simplify() assert pythonify(math) == python assert pythonify(python) == python # make sure that fractions aren't returned when not needed assert type(mathify(fractions.Fraction(2, 1))) is Integer assert type(pythonify(Mul([4, half]))) is int with pytest.raises(TypeError, match="don't know how to pythonify <Toot>$"): pythonify(Toot()) with pytest.raises(TypeError, match="don't know how to mathify 'lol'$"): mathify('lol')
def test_pow_gentle_simplify(): # these simplify to 0 and 1 weird0 = Add([x, -x]) weird1 = Mul([x, 1 / x]) assert weird0 != mathify(0) assert weird1 != mathify(1) assert weird0.gentle_simplify() == mathify(0) assert weird1.gentle_simplify() == mathify(1) assert Pow(x, Thing()).gentle_simplify() == Pow(x, Thing(True)) assert Pow(Thing(), y).gentle_simplify() == Pow(Thing(True), y) assert Pow(Pow(x, y), z).gentle_simplify() == Pow(x, y * z) assert Pow(x * y, z).gentle_simplify() == Mul([Pow(x, z), Pow(y, z)]) assert Pow(weird0, x).gentle_simplify() == mathify(0) assert Pow(weird1, x).gentle_simplify() == mathify(1) assert Pow(weird0, weird0).gentle_simplify() == mathify(0**0) assert Pow(x, weird0).gentle_simplify() == mathify(1) assert Pow(x, weird1).gentle_simplify() == x assert Pow(2, 3).gentle_simplify() == mathify(8) assert isinstance(Pow(2, -3).gentle_simplify(), Pow) for base in range(-10, 10): for exponent in range(-10, 10): try: value = base**exponent except ZeroDivisionError: continue # TODO: derivater should also raise an error :( simplified = Pow(base, exponent).gentle_simplify() if abs(value - int(value)) > 1e-12: # not an integer assert isinstance(simplified, Pow) else: assert simplified == mathify(int(value))
def test_derivatives(): with pytest.raises(ValueError, match="negative derivative_count is not supported$"): f(x, derivative_count=-1) assert x.derivative(x) == mathify(1) assert x.derivative(y) == mathify(0) assert f(x).derivative(x) == f_(x) assert f(x).derivative(x).derivative(x) == f__(x) assert f(x).derivative(y) == mathify(0) assert f( g(x)).derivative(x) == f_(g(x)) * g_(x), "u forgot chain rule n00b"
def test_minus(): assert isinstance(-x, Mul) assert (-x).objects == [mathify(-1), x] assert -(-x) == x assert isinstance(x - y, Add) assert (x - y).objects == [x, -y]
def test_mul_gentle_simplify(): assert Mul([x, y, Thing()]).gentle_simplify() == Mul([x, y, Thing(True)]) assert (Mul([x, y, Mul([z, Thing()]) ]).gentle_simplify() == Mul([x, y, z, Thing(True)])) assert Mul([1, x, 2, y, 3]).gentle_simplify() == Mul([6, x, y]) assert Mul([1, x, 2, y, 3]).gentle_simplify().objects[0] == mathify(6) assert Mul([1, x, y]).gentle_simplify() == Mul([x, y]) assert (Mul([x, y, x**a, y, x**b]).gentle_simplify() == Mul([x**(a + b + 1), y**2])) assert Mul([x]).gentle_simplify() == x assert Mul([x, x]).gentle_simplify() == x**2 assert Mul([]).gentle_simplify() == mathify(1) assert Mul([x, 1 / x]).gentle_simplify() == mathify(1) # these were broken in old derivater versions assert Mul([x, y, 1 / x]).gentle_simplify() == y assert Mul([x, 1 / x]).gentle_simplify() == mathify(1)
def test_add_gentle_simplify(): assert Add([x, y, Thing()]).gentle_simplify() == Add([x, y, Thing(True)]) assert (Add([x, y, Add([z, Thing()]) ]).gentle_simplify() == Add([x, y, z, Thing(True)])) assert Add([1, x, 2, y, 3]).gentle_simplify() == Add([x, y, 6]) assert Add([1, x, 2, y, 3]).gentle_simplify().objects[-1] == mathify(6) assert Add([x, x, y]).gentle_simplify() == Add([2 * x, y]) assert Add([2 * x, 3 * y, 4 * x]).gentle_simplify() == Add([6 * x, 3 * y]) assert Add([x, x]).gentle_simplify() == 2 * x assert Add([y]).gentle_simplify() == y assert Add([2 * x, -2 * x]).gentle_simplify() == mathify(0) assert Add([]).gentle_simplify() == mathify(0) assert Add([2, x, half]).gentle_simplify().objects == [x, half * 5] assert Add([3 * x, half * x]).gentle_simplify() == half * 7 * x # these were broken in old derivater versions assert Add([x, y, -x]).gentle_simplify() == y assert Add([x, -x]).gentle_simplify() == mathify(0)
def test_default_may_depend_on_and_derivative(): assert not MathObject().may_depend_on(x) # must be overrided if depdends assert not Toot().may_depend_on(x) assert Toot().may_depend_on(y) assert Toot().derivative(x) == mathify(0) with pytest.raises(TypeError, match=(r"cannot take derivative of <Toot> " r"with respect to y$")): Toot().derivative(y)
def test_with_fraction_coeff(): assert Pow(3, 4).with_fraction_coeff() == (Pow(3, 4), mathify(1)) assert Pow(3, -4).with_fraction_coeff() == (Pow(3, -4), mathify(1)) assert Mul([2, Pow(3, -4)]).with_fraction_coeff() == (mathify(2) / 81, mathify(1)) assert Mul([half, x, 3]).with_fraction_coeff() == (half * 3, x) assert Add([2 * x / 3, 4 * y / 5 ]).with_fraction_coeff() == (mathify(2) / 15, 5 * x + 6 * y) assert Add([]).with_fraction_coeff() == (mathify(1), Add([]))
def test_automagic_mathify(): assert f(2).arg == mathify(2)
def __init__(self, content): self.content = mathify(content)
def test_integers(): for n in [-10, -1, 0, 1, 10]: assert mathify(n) == Integer(n) assert mathify(n).python_int == n assert not mathify(n).may_depend_on(x) assert mathify(n).with_fraction_coeff() == (mathify(n), mathify(1)) assert not repr(mathify(n)).startswith('(') assert not repr(mathify(n)).endswith(')') if n < 0: assert mathify(n).add_parenthesize().startswith('(-') assert mathify(n).add_parenthesize().endswith(')') else: assert mathify(n).add_parenthesize() == repr(mathify(n)) == repr(n) with pytest.raises(TypeError, match=r"cannot create Integer of 'lol'$"): Integer('lol') # "cannot create Integer of 2" would be a very confusing error message with pytest.raises(TypeError, match=r"cannot create a new Integer of an Integer$"): Integer(Integer(2))