Beispiel #1
0
def test_implemented_function_evalf():
    f = Function('f')
    f = implemented_function(f, lambda x: x + 1)
    assert str(f(x)) == "f(x)"
    assert str(f(2)) == "f(2)"
    assert f(2).evalf(strict=False) == 3
    assert f(x).evalf(strict=False) == f(x)
    f = implemented_function(Function('sin'), lambda x: x + 1)
    assert f(2).evalf() != sin(2)
    del f._imp_  # XXX: due to caching _imp_ would influence all other tests
Beispiel #2
0
def test_implemented_function_evalf():
    f = Function('f')
    f = implemented_function(f, lambda x: x + 1)
    assert str(f(x)) == "f(x)"
    assert str(f(2)) == "f(2)"
    assert f(2).evalf(strict=False) == 3
    assert f(x).evalf(strict=False) == f(x)
    f = implemented_function(Function('sin'), lambda x: x + 1)
    assert f(2).evalf() != sin(2)
    del f._imp_     # XXX: due to caching _imp_ would influence all other tests
Beispiel #3
0
def test_ccode_inline_function():
    g = implemented_function('g', Lambda(x, 2 * x))
    assert ccode(g(x)) == '2*x'
    g = implemented_function('g', Lambda(x, 2 * x / Catalan))
    assert ccode(
        g(x)) == 'double const Catalan = %s;\n2*x/Catalan' % Catalan.evalf()
    A = IndexedBase('A')
    i = Idx('i', symbols('n', integer=True))
    g = implemented_function('g', Lambda(x, x * (1 + x) * (2 + x)))
    assert ccode(g(A[i]),
                 assign_to=A[i]) == ('for (int i=0; i<n; i++){\n'
                                     '   A[i] = (A[i] + 1)*(A[i] + 2)*A[i];\n'
                                     '}')
Beispiel #4
0
def test_jscode_inline_function():
    x = symbols('x')
    g = implemented_function('g', Lambda(x, 2*x))
    assert jscode(g(x)) == "2*x"
    g = implemented_function('g', Lambda(x, 2*x/Catalan))
    assert jscode(g(x)) == "var Catalan = %s;\n2*x/Catalan" % Catalan.n()
    A = IndexedBase('A')
    i = Idx('i', symbols('n', integer=True))
    g = implemented_function('g', Lambda(x, x*(1 + x)*(2 + x)))
    assert jscode(g(A[i]), assign_to=A[i]) == (
        "for (var i=0; i<n; i++){\n"
        "   A[i] = (A[i] + 1)*(A[i] + 2)*A[i];\n"
        "}"
    )
Beispiel #5
0
def test_inline_function():
    g = implemented_function('g', Lambda(x, 2 * x))
    assert fcode(g(x)) == '      2*x'
    g = implemented_function('g', Lambda(x, 2 * pi / x))
    assert fcode(g(x)) == ('      parameter (pi = 3.14159265358979d0)\n'
                           '      2*pi/x')
    A = IndexedBase('A')
    i = Idx('i', symbols('n', integer=True))
    g = implemented_function('g', Lambda(x, x * (1 + x) * (2 + x)))
    assert fcode(
        g(A[i]),
        assign_to=A[i]) == ('      do i = 1, n\n'
                            '         A(i) = (A(i) + 1)*(A(i) + 2)*A(i)\n'
                            '      end do')
Beispiel #6
0
def test_ccode_inline_function():
    x = symbols('x')
    g = implemented_function('g', Lambda(x, 2*x))
    assert ccode(g(x)) == "2*x"
    g = implemented_function('g', Lambda(x, 2*x/Catalan))
    assert ccode(
        g(x)) == "double const Catalan = %s;\n2*x/Catalan" % Catalan.evalf()
    A = IndexedBase('A')
    i = Idx('i', symbols('n', integer=True))
    g = implemented_function('g', Lambda(x, x*(1 + x)*(2 + x)))
    assert ccode(g(A[i]), assign_to=A[i]) == (
        "for (int i=0; i<n; i++){\n"
        "   A[i] = (A[i] + 1)*(A[i] + 2)*A[i];\n"
        "}"
    )
Beispiel #7
0
def test_jscode_Pow():
    g = implemented_function('g', Lambda(x, 2*x))
    assert jscode(x**3) == "Math.pow(x, 3)"
    assert jscode(x**(y**3)) == "Math.pow(x, Math.pow(y, 3))"
    assert jscode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \
        "Math.pow(3.5*2*x, -x + Math.pow(y, x))/(Math.pow(x, 2) + y)"
    assert jscode(x**-1.0) == '1/x'
Beispiel #8
0
def test_inline_function():
    g = implemented_function('g', Lambda(x, 2*x))
    assert fcode(g(x)) == "      2*x"
    g = implemented_function('g', Lambda(x, 2*pi/x))
    assert fcode(g(x)) == (
        "      parameter (pi = 3.14159265358979d0)\n"
        "      2*pi/x"
    )
    A = IndexedBase('A')
    i = Idx('i', symbols('n', integer=True))
    g = implemented_function('g', Lambda(x, x*(1 + x)*(2 + x)))
    assert fcode(g(A[i]), assign_to=A[i]) == (
        "      do i = 1, n\n"
        "         A(i) = (A(i) + 1)*(A(i) + 2)*A(i)\n"
        "      end do"
    )
Beispiel #9
0
def test_lambdify_imps():
    # Test lambdify with implemented functions
    # first test basic (diofant) lambdify
    f = diofant.cos
    assert lambdify(x, f(x))(0) == 1
    assert lambdify(x, 1 + f(x))(0) == 2
    assert lambdify((x, y), y + f(x))(0, 1) == 2
    # make an implemented function and test
    f = implemented_function("f", lambda x: x + 100)
    assert lambdify(x, f(x))(0) == 100
    assert lambdify(x, 1 + f(x))(0) == 101
    assert lambdify((x, y), y + f(x))(0, 1) == 101
    # Can also handle tuples, lists, dicts as expressions
    lam = lambdify(x, (f(x), x))
    assert lam(3) == (103, 3)
    lam = lambdify(x, [f(x), x])
    assert lam(3) == [103, 3]
    lam = lambdify(x, [f(x), (f(x), x)])
    assert lam(3) == [103, (103, 3)]
    lam = lambdify(x, {f(x): x})
    assert lam(3) == {103: 3}
    lam = lambdify(x, {f(x): x})
    assert lam(3) == {103: 3}
    lam = lambdify(x, {x: f(x)})
    assert lam(3) == {3: 103}
    # Check that imp preferred to other namespaces by default
    d = {'f': lambda x: x + 99}
    lam = lambdify(x, f(x), d)
    assert lam(3) == 103
    # Unless flag passed
    lam = lambdify(x, f(x), d, use_imps=False)
    assert lam(3) == 102
Beispiel #10
0
def test_lambdify_imps():
    # Test lambdify with implemented functions
    # first test basic (diofant) lambdify
    f = diofant.cos
    assert lambdify(x, f(x))(0) == 1
    assert lambdify(x, 1 + f(x))(0) == 2
    assert lambdify((x, y), y + f(x))(0, 1) == 2
    # make an implemented function and test
    f = implemented_function("f", lambda x: x + 100)
    assert lambdify(x, f(x))(0) == 100
    assert lambdify(x, 1 + f(x))(0) == 101
    assert lambdify((x, y), y + f(x))(0, 1) == 101
    # Can also handle tuples, lists, dicts as expressions
    lam = lambdify(x, (f(x), x))
    assert lam(3) == (103, 3)
    lam = lambdify(x, [f(x), x])
    assert lam(3) == [103, 3]
    lam = lambdify(x, [f(x), (f(x), x)])
    assert lam(3) == [103, (103, 3)]
    lam = lambdify(x, {f(x): x})
    assert lam(3) == {103: 3}
    lam = lambdify(x, {f(x): x})
    assert lam(3) == {103: 3}
    lam = lambdify(x, {x: f(x)})
    assert lam(3) == {3: 103}
    # Check that imp preferred to other namespaces by default
    d = {'f': lambda x: x + 99}
    lam = lambdify(x, f(x), d)
    assert lam(3) == 103
    # Unless flag passed
    lam = lambdify(x, f(x), d, use_imps=False)
    assert lam(3) == 102
Beispiel #11
0
def test_Pow():
    assert octave_code(x**3) == 'x.^3'
    assert octave_code(x**(y**3)) == 'x.^(y.^3)'
    assert octave_code(x**Rational(2, 3)) == 'x.^(2/3)'
    g = implemented_function('g', Lambda(x, 2 * x))
    assert octave_code(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \
        '(3.5*2*x).^(-x + y.^x)./(x.^2 + y)'
Beispiel #12
0
def test_Pow():
    assert mcode(x**3) == "x.^3"
    assert mcode(x**(y**3)) == "x.^(y.^3)"
    assert mcode(x**Rational(2, 3)) == 'x.^(2/3)'
    g = implemented_function('g', Lambda(x, 2*x))
    assert mcode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \
        "(3.5*2*x).^(-x + y.^x)./(x.^2 + y)"
Beispiel #13
0
def test_imps():
    # Here we check if the default returned functions are anonymous - in
    # the sense that we can have more than one function with the same name
    f = implemented_function('f', lambda x: 2*x)
    g = implemented_function('f', lambda x: math.sqrt(x))
    l1 = lambdify(x, f(x))
    l2 = lambdify(x, g(x))
    assert str(f(x)) == str(g(x))
    assert l1(3) == 6
    assert l2(3) == math.sqrt(3)
    # check that we can pass in a Function as input
    func = diofant.Function('myfunc')
    assert not hasattr(func, '_imp_')
    my_f = implemented_function(func, lambda x: 2*x)
    assert hasattr(func, '_imp_') and hasattr(my_f, '_imp_')
    # Error for functions with same name and different implementation
    f2 = implemented_function("f", lambda x: x + 101)
    pytest.raises(ValueError, lambda: lambdify(x, f(f2(x))))
Beispiel #14
0
def test_implemented_function_evalf():
    from diofant.utilities.lambdify import implemented_function
    f = Function('f')
    f = implemented_function(f, lambda x: x + 1)
    assert str(f(x)) == "f(x)"
    assert str(f(2)) == "f(2)"
    assert f(2).evalf() == 3
    assert f(x).evalf() == f(x)
    del f._imp_     # XXX: due to caching _imp_ would influence all other tests
Beispiel #15
0
def test_numexpr_userfunctions():
    a, b = numpy.random.randn(2, 10)
    uf = type('uf', (Function, ), {'eval': classmethod(lambda x, y: y**2 + 1)})
    func = lambdify(x, 1 - uf(x), modules='numexpr')
    assert numpy.allclose(func(a), -(a**2))

    uf = implemented_function(Function('uf'), lambda x, y: 2 * x * y + 1)
    func = lambdify((x, y), uf(x, y), modules='numexpr')
    assert numpy.allclose(func(a, b), 2 * a * b + 1)
Beispiel #16
0
def test_imps():
    # Here we check if the default returned functions are anonymous - in
    # the sense that we can have more than one function with the same name
    f = implemented_function('f', lambda x: 2*x)
    g = implemented_function('f', lambda x: math.sqrt(x))
    l1 = lambdify(x, f(x))
    l2 = lambdify(x, g(x))
    assert str(f(x)) == str(g(x))
    assert l1(3) == 6
    assert l2(3) == math.sqrt(3)
    # check that we can pass in a Function as input
    func = diofant.Function('myfunc')
    assert not hasattr(func, '_imp_')
    my_f = implemented_function(func, lambda x: 2*x)
    assert hasattr(func, '_imp_')
    # Error for functions with same name and different implementation
    f2 = implemented_function("f", lambda x: x + 101)
    pytest.raises(ValueError, lambda: lambdify(x, f(f2(x))))
Beispiel #17
0
def test_numexpr_userfunctions():
    a, b = numpy.random.randn(2, 10)
    uf = type('uf', (Function, ),
              {'eval': classmethod(lambda x, y: y**2+1)})
    func = lambdify(x, 1-uf(x), modules='numexpr')
    assert numpy.allclose(func(a), -(a**2))

    uf = implemented_function(Function('uf'), lambda x, y: 2*x*y+1)
    func = lambdify((x, y), uf(x, y), modules='numexpr')
    assert numpy.allclose(func(a, b), 2*a*b+1)
Beispiel #18
0
def test_ccode_Pow():
    assert ccode(x**3) == "pow(x, 3)"
    assert ccode(x**(y**3)) == "pow(x, pow(y, 3))"
    g = implemented_function('g', Lambda(x, 2 * x))
    assert ccode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \
        "pow(3.5*2*x, -x + pow(y, x))/(pow(x, 2) + y)"
    assert ccode(x**-1.0) == '1.0/x'
    assert ccode(x**Rational(2, 3)) == 'pow(x, 2.0L/3.0L)'
    _cond_cfunc = [(lambda base, exp: exp.is_integer, "dpowi"),
                   (lambda base, exp: not exp.is_integer, "pow")]
    assert ccode(x**3, user_functions={'Pow': _cond_cfunc}) == 'dpowi(x, 3)'
    assert ccode(x**3.2, user_functions={'Pow': _cond_cfunc}) == 'pow(x, 3.2)'
Beispiel #19
0
def test_imps_errors():
    # Test errors that implemented functions can return, and still be
    # able to form expressions.  See issue sympy/sympy#10810.
    for val, error_class in product((0, 0., 2, 2.0),
                                    (AttributeError, TypeError, ValueError)):

        def myfunc(a):
            if a == 0:
                raise error_class
            return 1

        f = implemented_function('f', myfunc)
        expr = f(val)
        assert expr == f(val)
Beispiel #20
0
def test_imps_errors():
    # Test errors that implemented functions can return, and still be
    # able to form expressions.  See issue sympy/sympy#10810.
    for val, error_class in product((0, 0., 2, 2.0),
                                    (AttributeError, TypeError, ValueError)):

        def myfunc(a):
            if a == 0:
                raise error_class
            return 1

        f = implemented_function('f', myfunc)
        expr = f(val)
        assert expr == f(val)
Beispiel #21
0
def test_numexprprinter():
    p = NumExprPrinter()
    M = MatrixSymbol('M', 1, 2)

    pytest.raises(TypeError, lambda: p.doprint(M))
    pytest.raises(TypeError, lambda: p.doprint([x, y]))

    assert p.doprint(I) == "evaluate('1j')"
    assert p.doprint(sin(x)) == "evaluate('sin(x)')"
    assert p.doprint(asinh(x)) == "evaluate('arcsinh(x)')"

    f = implemented_function('f', lambda x: 2 * x)
    assert p.doprint(f(x)) == "evaluate('(2*x)')"

    g = Function('g')
    pytest.raises(TypeError, lambda: p.doprint(g(x)))
Beispiel #22
0
def test_numexprprinter():
    p = NumExprPrinter()
    M = MatrixSymbol('M', 1, 2)

    pytest.raises(TypeError, lambda: p.doprint(M))
    pytest.raises(TypeError, lambda: p.doprint([x, y]))

    assert p.doprint(I) == "evaluate('1j')"
    assert p.doprint(sin(x)) == "evaluate('sin(x)')"
    assert p.doprint(asinh(x)) == "evaluate('arcsinh(x)')"

    f = implemented_function('f', lambda x: 2*x)
    assert p.doprint(f(x)) == "evaluate('(2*x)')"

    g = Function('g')
    pytest.raises(TypeError, lambda: p.doprint(g(x)))
Beispiel #23
0
def test_ccode_Pow():
    assert ccode(x**3) == 'pow(x, 3)'
    assert ccode(x**(y**3)) == 'pow(x, pow(y, 3))'
    g = implemented_function('g', Lambda(x, 2*x))
    assert ccode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \
        'pow(3.5*2*x, -x + pow(y, x))/(pow(x, 2) + y)'
    assert ccode(x**-1.0) == '1.0/x'
    assert ccode(x**Rational(2, 3)) == 'pow(x, 2.0L/3.0L)'
    _cond_cfunc = [(lambda base, exp: exp.is_integer, 'dpowi'),
                   (lambda base, exp: not exp.is_integer, 'pow')]
    assert ccode(x**3, user_functions={'Pow': _cond_cfunc}) == 'dpowi(x, 3)'
    assert ccode(x**3.2, user_functions={'Pow': _cond_cfunc}) == 'pow(x, 3.2)'

    _cond_cfunc2 = [(lambda base, exp: base == 2, lambda base, exp: f'exp2({exp})'),
                    (lambda base, exp: base != 2, 'pow')]
    # Related to sympy/sympy#11353
    assert ccode(2**x, user_functions={'Pow': _cond_cfunc2}) == 'exp2(x)'
    assert ccode(x**2, user_functions={'Pow': _cond_cfunc2}) == 'pow(x, 2)'
Beispiel #24
0
def test_ccode_Pow():
    assert ccode(x**3) == "pow(x, 3)"
    assert ccode(x**(y**3)) == "pow(x, pow(y, 3))"
    g = implemented_function('g', Lambda(x, 2*x))
    assert ccode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \
        "pow(3.5*2*x, -x + pow(y, x))/(pow(x, 2) + y)"
    assert ccode(x**-1.0) == '1.0/x'
    assert ccode(x**Rational(2, 3)) == 'pow(x, 2.0L/3.0L)'
    _cond_cfunc = [(lambda base, exp: exp.is_integer, "dpowi"),
                   (lambda base, exp: not exp.is_integer, "pow")]
    assert ccode(x**3, user_functions={'Pow': _cond_cfunc}) == 'dpowi(x, 3)'
    assert ccode(x**3.2, user_functions={'Pow': _cond_cfunc}) == 'pow(x, 3.2)'

    _cond_cfunc2 = [(lambda base, exp: base == 2, lambda base, exp: 'exp2(%s)' % exp),
                    (lambda base, exp: base != 2, 'pow')]
    # Related to sympy/sympy#11353
    assert ccode(2**x, user_functions={'Pow': _cond_cfunc2}) == 'exp2(x)'
    assert ccode(x**2, user_functions={'Pow': _cond_cfunc2}) == 'pow(x, 2)'
Beispiel #25
0
def binary_function(symfunc, expr, **kwargs):
    """Returns a diofant function with expr as binary implementation

    This is a convenience function that automates the steps needed to
    autowrap the Diofant expression and attaching it to a Function object
    with implemented_function().

    >>> from diofant.abc import x, y
    >>> from diofant.utilities.autowrap import binary_function
    >>> expr = ((x - y)**(25)).expand()
    >>> f = binary_function('f', expr)
    >>> type(f)
    <class 'diofant.core.function.UndefinedFunction'>
    >>> 2*f(x, y)
    2*f(x, y)
    >>> f(x, y).evalf(2, subs={x: 1, y: 2})
    -1.0
    """
    binary = autowrap(expr, **kwargs)
    return implemented_function(symfunc, binary)
Beispiel #26
0
def test_inline_function():
    n, m = symbols('n m', integer=True)
    A, x, y = map(IndexedBase, 'Axy')
    i = Idx('i', m)
    p = FCodeGen()
    func = implemented_function('func', Lambda(n, n*(n + 1)))
    routine = make_routine('test_inline', Eq(y[i], func(x[i])))
    code = get_string(p.dump_f95, [routine])
    expected = (
        'subroutine test_inline(m, x, y)\n'
        'implicit none\n'
        'INTEGER*4, intent(in) :: m\n'
        'REAL*8, intent(in), dimension(1:m) :: x\n'
        'REAL*8, intent(out), dimension(1:m) :: y\n'
        'INTEGER*4 :: i\n'
        'do i = 1, m\n'
        '   y(i) = %s*%s\n'
        'end do\n'
        'end subroutine\n'
    )
    args = ('x(i)', '(x(i) + 1)')
    assert code == expected % args or\
        code == expected % args[::-1]
Beispiel #27
0
def test_imps_wrong_args():
    pytest.raises(ValueError, lambda: implemented_function(sin, lambda x: x))
Beispiel #28
0
def test_imps_wrong_args():
    pytest.raises(ValueError, lambda: implemented_function(sin, lambda x: x))
Beispiel #29
0
def ufuncify(args,
             expr,
             language=None,
             backend='numpy',
             tempdir=None,
             flags=None,
             verbose=False,
             helpers=None):
    """
    Generates a binary function that supports broadcasting on numpy arrays.

    Parameters
    ----------

    args : iterable
        Either a Symbol or an iterable of symbols. Specifies the argument
        sequence for the function.
    expr
        A Diofant expression that defines the element wise operation.
    language : string, optional
        If supplied, (options: 'C' or 'F95'), specifies the language of the
        generated code. If ``None`` [default], the language is inferred based
        upon the specified backend.
    backend : string, optional
        Backend used to wrap the generated code. Either 'numpy' [default],
        'cython', or 'f2py'.
    tempdir : string, optional
        Path to directory for temporary files. If this argument is supplied,
        the generated code and the wrapper input files are left intact in the
        specified path.
    flags : iterable, optional
        Additional option flags that will be passed to the backend
    verbose : bool, optional
        If True, autowrap will not mute the command line backends. This can be
        helpful for debugging.
    helpers : iterable, optional
        Used to define auxillary expressions needed for the main expr. If the
        main expression needs to call a specialized function it should be put
        in the ``helpers`` iterable. Autowrap will then make sure that the
        compiled main expression can link to the helper routine. Items should
        be tuples with (<funtion_name>, <diofant_expression>, <arguments>). It
        is mandatory to supply an argument sequence to helper routines.

    Notes
    -----

    The default backend ('numpy') will create actual instances of
    ``numpy.ufunc``. These support ndimensional broadcasting, and implicit type
    conversion. Use of the other backends will result in a "ufunc-like"
    function, which requires equal length 1-dimensional arrays for all
    arguments, and will not perform any type conversions.

    References
    ----------

    .. [1] http://docs.scipy.org/doc/numpy/reference/ufuncs.html

    Examples
    --------

    >>> from diofant.utilities.autowrap import ufuncify
    >>> from diofant.abc import x, y
    >>> import numpy as np
    >>> f = ufuncify((x, y), y + x**2)
    >>> type(f) is np.ufunc
    True
    >>> f([1, 2, 3], 2)
    array([ 3.,  6.,  11.])
    >>> f(np.arange(5), 3)
    array([ 3.,  4.,  7.,  12.,  19.])

    For the F2Py and Cython backends, inputs are required to be equal length
    1-dimensional arrays. The F2Py backend will perform type conversion, but
    the Cython backend will error if the inputs are not of the expected type.

    >>> f_fortran = ufuncify((x, y), y + x**2, backend='F2Py')
    >>> f_fortran(1, 2)
    3
    >>> f_fortran(numpy.array([1, 2, 3]), numpy.array([1.0, 2.0, 3.0]))
    array([2.,  6.,  12.])
    >>> f_cython = ufuncify((x, y), y + x**2, backend='Cython')
    >>> f_cython(1, 2)
    Traceback (most recent call last):
    ...
    TypeError: Argument '_x' has incorrect type (expected numpy.ndarray, got int)
    >>> f_cython(numpy.array([1.0]), numpy.array([2.0]))
    array([ 3.])
    """

    if isinstance(args, (Dummy, Symbol)):
        args = (args, )
    else:
        args = tuple(args)

    if language:
        _validate_backend_language(backend, language)
    else:
        language = _infer_language(backend)

    helpers = helpers if helpers else ()
    flags = flags if flags else ()

    if backend.upper() == 'NUMPY':
        routine = make_routine('autofunc', expr, args)
        helps = []
        for name, expr, args in helpers:
            helps.append(make_routine(name, expr, args))
        code_wrapper = UfuncifyCodeWrapper(CCodeGen("ufuncify"), tempdir,
                                           flags, verbose)
        return code_wrapper.wrap_code(routine, helpers=helps)
    else:
        # Dummies are used for all added expressions to prevent name clashes
        # within the original expression.
        y = IndexedBase(Dummy())
        m = Dummy(integer=True)
        i = Idx(Dummy(integer=True), m)
        f = implemented_function(Dummy().name, Lambda(args, expr))
        # For each of the args create an indexed version.
        indexed_args = [IndexedBase(Dummy(str(a))) for a in args]
        # Order the arguments (out, args, dim)
        args = [y] + indexed_args + [m]
        args_with_indices = [a[i] for a in indexed_args]
        return autowrap(Eq(y[i], f(*args_with_indices)), language, backend,
                        tempdir, tuple(args), flags, verbose, helpers)
Beispiel #30
0
def test_sympyissue_12092():
    f = implemented_function('f', lambda x: x**2)
    assert f(f(2)).evalf() == Float(16)