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_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_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_mul_repr(): assert repr(-x) == '-x' assert repr(2 * x) == '2*x' assert repr(-2 * x) == '-2*x' assert repr((x + y) * z) == '(x + y)*z' assert repr((x + y) / z) == '(x + y) / z' assert repr(Mul([1 / x, 1 / y])) == '1 / (x*y)' assert repr(Mul([])) == '1' assert repr(Mul([x])) == 'x' assert repr(Mul([1 / x])) == repr(1 / x) == '1 / x' assert repr(Mul([Pow(x / y, -2)])) == '1 / (x**2 / y**2)'
def test_automagic_gentle_simplify(): assert (Thing() + x).objects[0].gentle assert (Thing() * x).objects[0].gentle assert (Thing()**x).base.gentle assert (x**Thing()).exponent.gentle assert not Add([Thing(), x]).objects[0].gentle assert not Mul([Thing(), x]).objects[0].gentle assert not Pow(Thing(), x).base.gentle assert not Pow(x, Thing()).exponent.gentle
def test_operators(): pairs = [ (2 * x, y), (2 * ln(4), y), (x, x), (x, 2 * x), (y, -2 * x), # not all things are MathObjects, they must be mathified (1, -2 * x), (-10, -x), ] pairs.extend([(b, a) for (a, b) in pairs]) for a, b in pairs: if not isinstance(b, int): assert 0 - b == -b assert 1 / b == pow(b, -1) assert a + b == Add([a, b]).gentle_simplify() assert a - b == Add([a, -b]).gentle_simplify() assert a * b == Mul([a, b]).gentle_simplify() assert a / b == Mul([a, Pow(b, -1)]).gentle_simplify() assert a**b == Pow(a, b).gentle_simplify()
def test_add_partial_replaces(): # this checks .objects to make sure the order is correct assert Add([x, y, z]).replace(Add([x, y]), a).objects == [a, z] assert Add([x, y, z]).replace(Add([y, x]), a).objects == [a, z] assert Add([x, y, z]).replace(Add([y, z]), a).objects == [x, a] assert Add([x, y, z]).replace(Add([z, y]), a).objects == [x, a] assert Add([x, y, z]).replace(Add([x, z]), a).objects == [y, a] assert Add([x, y, z]).replace(Add([z, x]), a).objects == [a, y] assert Mul([x, y, z]).replace(Mul([x, y]), a).objects == [a, z] assert Mul([x, y, z]).replace(Mul([y, x]), a).objects == [a, z] assert Mul([x, y, z]).replace(Mul([y, z]), a).objects == [x, a] assert Mul([x, y, z]).replace(Mul([z, y]), a).objects == [x, a] assert Mul([x, y, z]).replace(Mul([x, z]), a).objects == [y, a] assert Mul([x, y, z]).replace(Mul([z, x]), a).objects == [a, y] for klass, name, instead in [(Add, 'Add', 'zeros'), (Mul, 'Mul', 'ones')]: with pytest.raises(ValueError, match=((r"cannot replace %s\(\[\]\) by something, " r"maybe use gentle_simplify\(\) to turn " r"%s\(\[\]\)'s into %s\?$") % (name, name, instead))): klass([x, y]).replace(klass([]), z) # make sure that gentle_simplify() is not called assert not Add([x, y, z]).replace(x + y, Thing()).objects[0].gentle assert not Mul([x, y, z]).replace(x * y, Thing()).objects[0].gentle # even -x which is Mul([-1, x]) doesn't turn into Integer(-1), instead it # turns into Mul([-1, 1]) which looks a lot like Integer(-1) ... (lol) assert Add([x, -x]).replace(x, 1) == Add([1, Mul([-1, 1])]) assert Mul([x, -x]).replace(x, 1) == Mul([1, Mul([-1, 1])])
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)