from itertools import product import math import inspect import mpmath from sympy.testing.pytest import raises, warns_deprecated_sympy from sympy.concrete.summations import Sum from sympy.core.function import (Function, Lambda, diff) from sympy.core.numbers import (E, Float, I, Rational, all_close, oo, pi) from sympy.core.relational import Eq from sympy.core.singleton import S from sympy.core.symbol import (Dummy, symbols) from sympy.functions.combinatorial.factorials import (RisingFactorial, factorial) from sympy.functions.combinatorial.numbers import bernoulli, harmonic from sympy.functions.elementary.complexes import Abs from sympy.functions.elementary.exponential import exp, log from sympy.functions.elementary.hyperbolic import acosh from sympy.functions.elementary.integers import floor from sympy.functions.elementary.miscellaneous import (Max, Min, sqrt) from sympy.functions.elementary.piecewise import Piecewise from sympy.functions.elementary.trigonometric import (acos, cos, cot, sin, sinc, tan) from sympy.functions.special.bessel import (besseli, besselj, besselk, bessely, jn, yn) from sympy.functions.special.beta_functions import (beta, betainc, betainc_regularized) from sympy.functions.special.delta_functions import (Heaviside) from sympy.functions.special.error_functions import (Ei, erf, erfc, fresnelc, fresnels, Si, Ci) from sympy.functions.special.gamma_functions import (digamma, gamma, loggamma, polygamma) from sympy.integrals.integrals import Integral from sympy.logic.boolalg import (And, false, ITE, Not, Or, true) from sympy.matrices.expressions.dotproduct import DotProduct from sympy.simplify.cse_main import cse from sympy.tensor.array import derive_by_array, Array from sympy.tensor.indexed import IndexedBase from sympy.utilities.lambdify import lambdify from sympy.utilities.iterables import numbered_symbols from sympy.vector import CoordSys3D from sympy.core.expr import UnevaluatedExpr from sympy.codegen.cfunctions import expm1, log1p, exp2, log2, log10, hypot from sympy.codegen.numpy_nodes import logaddexp, logaddexp2 from sympy.codegen.scipy_nodes import cosm1, powm1 from sympy.functions.elementary.complexes import re, im, arg from sympy.functions.special.polynomials import \ chebyshevt, chebyshevu, legendre, hermite, laguerre, gegenbauer, \ assoc_legendre, assoc_laguerre, jacobi from sympy.matrices import Matrix, MatrixSymbol, SparseMatrix from sympy.printing.lambdarepr import LambdaPrinter from sympy.printing.numpy import NumPyPrinter from sympy.utilities.lambdify import implemented_function, lambdastr from sympy.testing.pytest import skip from sympy.utilities.decorator import conserve_mpmath_dps from sympy.utilities.exceptions import ignore_warnings from sympy.external import import_module from sympy.functions.special.gamma_functions import uppergamma, lowergamma import sympy MutableDenseMatrix = Matrix numpy = import_module('numpy') scipy = import_module('scipy', import_kwargs={'fromlist': ['sparse']}) numexpr = import_module('numexpr') tensorflow = import_module('tensorflow') cupy = import_module('cupy') jax = import_module('jax') numba = import_module('numba') if tensorflow: # Hide Tensorflow warnings import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' w, x, y, z = symbols('w,x,y,z') #================== Test different arguments ======================= def test_no_args(): f = lambdify([], 1) raises(TypeError, lambda: f(-1)) assert f() == 1 def test_single_arg(): f = lambdify(x, 2*x) assert f(1) == 2 def test_list_args(): f = lambdify([x, y], x + y) assert f(1, 2) == 3 def test_nested_args(): f1 = lambdify([[w, x]], [w, x]) assert f1([91, 2]) == [91, 2] raises(TypeError, lambda: f1(1, 2)) f2 = lambdify([(w, x), (y, z)], [w, x, y, z]) assert f2((18, 12), (73, 4)) == [18, 12, 73, 4] raises(TypeError, lambda: f2(3, 4)) f3 = lambdify([w, [[[x]], y], z], [w, x, y, z]) assert f3(10, [[[52]], 31], 44) == [10, 52, 31, 44] def test_str_args(): f = lambdify('x,y,z', 'z,y,x') assert f(3, 2, 1) == (1, 2, 3) assert f(1.0, 2.0, 3.0) == (3.0, 2.0, 1.0) # make sure correct number of args required raises(TypeError, lambda: f(0)) def test_own_namespace_1(): myfunc = lambda x: 1 f = lambdify(x, sin(x), {"sin": myfunc}) assert f(0.1) == 1 assert f(100) == 1 def test_own_namespace_2(): def myfunc(x): return 1 f = lambdify(x, sin(x), {'sin': myfunc}) assert f(0.1) == 1 assert f(100) == 1 def test_own_module(): f = lambdify(x, sin(x), math) assert f(0) == 0.0 p, q, r = symbols("p q r", real=True) ae = abs(exp(p+UnevaluatedExpr(q+r))) f = lambdify([p, q, r], [ae, ae], modules=math) results = f(1.0, 1e18, -1e18) refvals = [math.exp(1.0)]*2 for res, ref in zip(results, refvals): assert abs((res-ref)/ref) < 1e-15 def test_bad_args(): # no vargs given raises(TypeError, lambda: lambdify(1)) # same with vector exprs raises(TypeError, lambda: lambdify([1, 2])) def test_atoms(): # Non-Symbol atoms should not be pulled out from the expression namespace f = lambdify(x, pi + x, {"pi": 3.14}) assert f(0) == 3.14 f = lambdify(x, I + x, {"I": 1j}) assert f(1) == 1 + 1j #================== Test different modules ========================= # high precision output of sin(0.2*pi) is used to detect if precision is lost unwanted @conserve_mpmath_dps def test_sympy_lambda(): mpmath.mp.dps = 50 sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "sympy") assert f(x) == sin(x) prec = 1e-15 assert -prec < f(Rational(1, 5)).evalf() - Float(str(sin02)) < prec # arctan is in numpy module and should not be available # The arctan below gives NameError. What is this supposed to test? # raises(NameError, lambda: lambdify(x, arctan(x), "sympy")) @conserve_mpmath_dps def test_math_lambda(): mpmath.mp.dps = 50 sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "math") prec = 1e-15 assert -prec < f(0.2) - sin02 < prec raises(TypeError, lambda: f(x)) # if this succeeds, it can't be a Python math function @conserve_mpmath_dps def test_mpmath_lambda(): mpmath.mp.dps = 50 sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "mpmath") prec = 1e-49 # mpmath precision is around 50 decimal places assert -prec < f(mpmath.mpf("0.2")) - sin02 < prec raises(TypeError, lambda: f(x)) # if this succeeds, it can't be a mpmath function ref2 = (mpmath.mpf("1e-30") - mpmath.mpf("1e-45")/2 + 5*mpmath.mpf("1e-60")/6 - 3*mpmath.mpf("1e-75")/4 + 33*mpmath.mpf("1e-90")/40 ) f2a = lambdify((x, y), x**y - 1, "mpmath") f2b = lambdify((x, y), powm1(x, y), "mpmath") f2c = lambdify((x,), expm1(x*log1p(x)), "mpmath") ans2a = f2a(mpmath.mpf("1")+mpmath.mpf("1e-15"), mpmath.mpf("1e-15")) ans2b = f2b(mpmath.mpf("1")+mpmath.mpf("1e-15"), mpmath.mpf("1e-15")) ans2c = f2c(mpmath.mpf("1e-15")) assert abs(ans2a - ref2) < 1e-51 assert abs(ans2b - ref2) < 1e-67 assert abs(ans2c - ref2) < 1e-80 @conserve_mpmath_dps def test_number_precision(): mpmath.mp.dps = 50 sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin02, "mpmath") prec = 1e-49 # mpmath precision is around 50 decimal places assert -prec < f(0) - sin02 < prec @conserve_mpmath_dps def test_mpmath_precision(): mpmath.mp.dps = 100 assert str(lambdify((), pi.evalf(100), 'mpmath')()) == str(pi.evalf(100)) #================== Test Translations ============================== # We can only check if all translated functions are valid. It has to be checked # by hand if they are complete. def test_math_transl(): from sympy.utilities.lambdify import MATH_TRANSLATIONS for sym, mat in MATH_TRANSLATIONS.items(): assert sym in sympy.__dict__ assert mat in math.__dict__ def test_mpmath_transl(): from sympy.utilities.lambdify import MPMATH_TRANSLATIONS for sym, mat in MPMATH_TRANSLATIONS.items(): assert sym in sympy.__dict__ or sym == 'Matrix' assert mat in mpmath.__dict__ def test_numpy_transl(): if not numpy: skip("numpy not installed.") from sympy.utilities.lambdify import NUMPY_TRANSLATIONS for sym, nump in NUMPY_TRANSLATIONS.items(): assert sym in sympy.__dict__ assert nump in numpy.__dict__ def test_scipy_transl(): if not scipy: skip("scipy not installed.") from sympy.utilities.lambdify import SCIPY_TRANSLATIONS for sym, scip in SCIPY_TRANSLATIONS.items(): assert sym in sympy.__dict__ assert scip in scipy.__dict__ or scip in scipy.special.__dict__ def test_numpy_translation_abs(): if not numpy: skip("numpy not installed.") f = lambdify(x, Abs(x), "numpy") assert f(-1) == 1 assert f(1) == 1 def test_numexpr_printer(): if not numexpr: skip("numexpr not installed.") # if translation/printing is done incorrectly then evaluating # a lambdified numexpr expression will throw an exception from sympy.printing.lambdarepr import NumExprPrinter blacklist = ('where', 'complex', 'contains') arg_tuple = (x, y, z) # some functions take more than one argument for sym in NumExprPrinter._numexpr_functions.keys(): if sym in blacklist: continue ssym = S(sym) if hasattr(ssym, '_nargs'): nargs = ssym._nargs[0] else: nargs = 1 args = arg_tuple[:nargs] f = lambdify(args, ssym(*args), modules='numexpr') assert f(*(1, )*nargs) is not None def test_issue_9334(): if not numexpr: skip("numexpr not installed.") if not numpy: skip("numpy not installed.") expr = S('b*a - sqrt(a**2)') a, b = sorted(expr.free_symbols, key=lambda s: s.name) func_numexpr = lambdify((a,b), expr, modules=[numexpr], dummify=False) foo, bar = numpy.random.random((2, 4)) func_numexpr(foo, bar) def test_issue_12984(): if not numexpr: skip("numexpr not installed.") func_numexpr = lambdify((x,y,z), Piecewise((y, x >= 0), (z, x > -1)), numexpr) with ignore_warnings(RuntimeWarning): assert func_numexpr(1, 24, 42) == 24 assert str(func_numexpr(-1, 24, 42)) == 'nan' def test_empty_modules(): x, y = symbols('x y') expr = -(x % y) no_modules = lambdify([x, y], expr) empty_modules = lambdify([x, y], expr, modules=[]) assert no_modules(3, 7) == empty_modules(3, 7) assert no_modules(3, 7) == -3 def test_exponentiation(): f = lambdify(x, x**2) assert f(-1) == 1 assert f(0) == 0 assert f(1) == 1 assert f(-2) == 4 assert f(2) == 4 assert f(2.5) == 6.25 def test_sqrt(): f = lambdify(x, sqrt(x)) assert f(0) == 0.0 assert f(1) == 1.0 assert f(4) == 2.0 assert abs(f(2) - 1.414) < 0.001 assert f(6.25) == 2.5 def test_trig(): f = lambdify([x], [cos(x), sin(x)], 'math') d = f(pi) prec = 1e-11 assert -prec < d[0] + 1 < prec assert -prec < d[1] < prec d = f(3.14159) prec = 1e-5 assert -prec < d[0] + 1 < prec assert -prec < d[1] < prec def test_integral(): if numpy and not scipy: skip("scipy not installed.") f = Lambda(x, exp(-x**2)) l = lambdify(y, Integral(f(x), (x, y, oo))) d = l(-oo) assert 1.77245385 < d < 1.772453851 def test_double_integral(): if numpy and not scipy: skip("scipy not installed.") # example from http://mpmath.org/doc/current/calculus/integration.html i = Integral(1/(1 - x**2*y**2), (x, 0, 1), (y, 0, z)) l = lambdify([z], i) d = l(1) assert 1.23370055 < d < 1.233700551 def test_spherical_bessel(): if numpy and not scipy: skip("scipy not installed.") test_point = 4.2 #randomly selected x = symbols("x") jtest = jn(2, x) assert abs(lambdify(x,jtest)(test_point) - jtest.subs(x,test_point).evalf()) < 1e-8 ytest = yn(2, x) assert abs(lambdify(x,ytest)(test_point) - ytest.subs(x,test_point).evalf()) < 1e-8 #================== Test vectors =================================== def test_vector_simple(): f = lambdify((x, y, z), (z, y, x)) assert f(3, 2, 1) == (1, 2, 3) assert f(1.0, 2.0, 3.0) == (3.0, 2.0, 1.0) # make sure correct number of args required raises(TypeError, lambda: f(0)) def test_vector_discontinuous(): f = lambdify(x, (-1/x, 1/x)) raises(ZeroDivisionError, lambda: f(0)) assert f(1) == (-1.0, 1.0) assert f(2) == (-0.5, 0.5) assert f(-2) == (0.5, -0.5) def test_trig_symbolic(): f = lambdify([x], [cos(x), sin(x)], 'math') d = f(pi) assert abs(d[0] + 1) < 0.0001 assert abs(d[1] - 0) < 0.0001 def test_trig_float(): f = lambdify([x], [cos(x), sin(x)]) d = f(3.14159) assert abs(d[0] + 1) < 0.0001 assert abs(d[1] - 0) < 0.0001 def test_docs(): f = lambdify(x, x**2) assert f(2) == 4 f = lambdify([x, y, z], [z, y, x]) assert f(1, 2, 3) == [3, 2, 1] f = lambdify(x, sqrt(x)) assert f(4) == 2.0 f = lambdify((x, y), sin(x*y)**2) assert f(0, 5) == 0 def test_math(): f = lambdify((x, y), sin(x), modules="math") assert f(0, 5) == 0 def test_sin(): f = lambdify(x, sin(x)**2) assert isinstance(f(2), float) f = lambdify(x, sin(x)**2, modules="math") assert isinstance(f(2), float) def test_matrix(): A = Matrix([[x, x*y], [sin(z) + 4, x**z]]) sol = Matrix([[1, 2], [sin(3) + 4, 1]]) f = lambdify((x, y, z), A, modules="sympy") assert f(1, 2, 3) == sol f = lambdify((x, y, z), (A, [A]), modules="sympy") assert f(1, 2, 3) == (sol, [sol]) J = Matrix((x, x + y)).jacobian((x, y)) v = Matrix((x, y)) sol = Matrix([[1, 0], [1, 1]]) assert lambdify(v, J, modules='sympy')(1, 2) == sol assert lambdify(v.T, J, modules='sympy')(1, 2) == sol def test_numpy_matrix(): if not numpy: skip("numpy not installed.") A = Matrix([[x, x*y], [sin(z) + 4, x**z]]) sol_arr = numpy.array([[1, 2], [numpy.sin(3) + 4, 1]]) #Lambdify array first, to ensure return to array as default f = lambdify((x, y, z), A, ['numpy']) numpy.testing.assert_allclose(f(1, 2, 3), sol_arr) #Check that the types are arrays and matrices assert isinstance(f(1, 2, 3), numpy.ndarray) # gh-15071 class dot(Function): pass x_dot_mtx = dot(x, Matrix([[2], [1], [0]])) f_dot1 = lambdify(x, x_dot_mtx) inp = numpy.zeros((17, 3)) assert numpy.all(f_dot1(inp) == 0) strict_kw = {"allow_unknown_functions": False, "inline": True, "fully_qualified_modules": False} p2 = NumPyPrinter(dict(user_functions={'dot': 'dot'}, **strict_kw)) f_dot2 = lambdify(x, x_dot_mtx, printer=p2) assert numpy.all(f_dot2(inp) == 0) p3 = NumPyPrinter(strict_kw) # The line below should probably fail upon construction (before calling with "(inp)"): raises(Exception, lambda: lambdify(x, x_dot_mtx, printer=p3)(inp)) def test_numpy_transpose(): if not numpy: skip("numpy not installed.") A = Matrix([[1, x], [0, 1]]) f = lambdify((x), A.T, modules="numpy") numpy.testing.assert_array_equal(f(2), numpy.array([[1, 0], [2, 1]])) def test_numpy_dotproduct(): if not numpy: skip("numpy not installed") A = Matrix([x, y, z]) f1 = lambdify([x, y, z], DotProduct(A, A), modules='numpy') f2 = lambdify([x, y, z], DotProduct(A, A.T), modules='numpy') f3 = lambdify([x, y, z], DotProduct(A.T, A), modules='numpy') f4 = lambdify([x, y, z], DotProduct(A, A.T), modules='numpy') assert f1(1, 2, 3) == \ f2(1, 2, 3) == \ f3(1, 2, 3) == \ f4(1, 2, 3) == \ numpy.array([14]) def test_numpy_inverse(): if not numpy: skip("numpy not installed.") A = Matrix([[1, x], [0, 1]]) f = lambdify((x), A**-1, modules="numpy") numpy.testing.assert_array_equal(f(2), numpy.array([[1, -2], [0, 1]])) def test_numpy_old_matrix(): if not numpy: skip("numpy not installed.") A = Matrix([[x, x*y], [sin(z) + 4, x**z]]) sol_arr = numpy.array([[1, 2], [numpy.sin(3) + 4, 1]]) f = lambdify((x, y, z), A, [{'ImmutableDenseMatrix': numpy.matrix}, 'numpy']) with ignore_warnings(PendingDeprecationWarning): numpy.testing.assert_allclose(f(1, 2, 3), sol_arr) assert isinstance(f(1, 2, 3), numpy.matrix) def test_scipy_sparse_matrix(): if not scipy: skip("scipy not installed.") A = SparseMatrix([[x, 0], [0, y]]) f = lambdify((x, y), A, modules="scipy") B = f(1, 2) assert isinstance(B, scipy.sparse.coo_matrix) def test_python_div_zero_issue_11306(): if not numpy: skip("numpy not installed.") p = Piecewise((1 / x, y < -1), (x, y < 1), (1 / x, True)) f = lambdify([x, y], p, modules='numpy') with numpy.errstate(divide='ignore'): assert float(f(numpy.array(0), numpy.array(0.5))) == 0 assert float(f(numpy.array(0), numpy.array(1))) == float('inf') def test_issue9474(): mods = [None, 'math'] if numpy: mods.append('numpy') if mpmath: mods.append('mpmath') for mod in mods: f = lambdify(x, S.One/x, modules=mod) assert f(2) == 0.5 f = lambdify(x, floor(S.One/x), modules=mod) assert f(2) == 0 for absfunc, modules in product([Abs, abs], mods): f = lambdify(x, absfunc(x), modules=modules) assert f(-1) == 1 assert f(1) == 1 assert f(3+4j) == 5 def test_issue_9871(): if not numexpr: skip("numexpr not installed.") if not numpy: skip("numpy not installed.") r = sqrt(x**2 + y**2) expr = diff(1/r, x) xn = yn = numpy.linspace(1, 10, 16) # expr(xn, xn) = -xn/(sqrt(2)*xn)^3 fv_exact = -numpy.sqrt(2.)**-3 * xn**-2 fv_numpy = lambdify((x, y), expr, modules='numpy')(xn, yn) fv_numexpr = lambdify((x, y), expr, modules='numexpr')(xn, yn) numpy.testing.assert_allclose(fv_numpy, fv_exact, rtol=1e-10) numpy.testing.assert_allclose(fv_numexpr, fv_exact, rtol=1e-10) def test_numpy_piecewise(): if not numpy: skip("numpy not installed.") pieces = Piecewise((x, x < 3), (x**2, x > 5), (0, True)) f = lambdify(x, pieces, modules="numpy") numpy.testing.assert_array_equal(f(numpy.arange(10)), numpy.array([0, 1, 2, 0, 0, 0, 36, 49, 64, 81])) # If we evaluate somewhere all conditions are False, we should get back NaN nodef_func = lambdify(x, Piecewise((x, x > 0), (-x, x < 0))) numpy.testing.assert_array_equal(nodef_func(numpy.array([-1, 0, 1])), numpy.array([1, numpy.nan, 1])) def test_numpy_logical_ops(): if not numpy: skip("numpy not installed.") and_func = lambdify((x, y), And(x, y), modules="numpy") and_func_3 = lambdify((x, y, z), And(x, y, z), modules="numpy") or_func = lambdify((x, y), Or(x, y), modules="numpy") or_func_3 = lambdify((x, y, z), Or(x, y, z), modules="numpy") not_func = lambdify((x), Not(x), modules="numpy") arr1 = numpy.array([True, True]) arr2 = numpy.array([False, True]) arr3 = numpy.array([True, False]) numpy.testing.assert_array_equal(and_func(arr1, arr2), numpy.array([False, True])) numpy.testing.assert_array_equal(and_func_3(arr1, arr2, arr3), numpy.array([False, False])) numpy.testing.assert_array_equal(or_func(arr1, arr2), numpy.array([True, True])) numpy.testing.assert_array_equal(or_func_3(arr1, arr2, arr3), numpy.array([True, True])) numpy.testing.assert_array_equal(not_func(arr2), numpy.array([True, False])) def test_numpy_matmul(): if not numpy: skip("numpy not installed.") xmat = Matrix([[x, y], [z, 1+z]]) ymat = Matrix([[x**2], [Abs(x)]]) mat_func = lambdify((x, y, z), xmat*ymat, modules="numpy") numpy.testing.assert_array_equal(mat_func(0.5, 3, 4), numpy.array([[1.625], [3.5]])) numpy.testing.assert_array_equal(mat_func(-0.5, 3, 4), numpy.array([[1.375], [3.5]])) # Multiple matrices chained together in multiplication f = lambdify((x, y, z), xmat*xmat*xmat, modules="numpy") numpy.testing.assert_array_equal(f(0.5, 3, 4), numpy.array([[72.125, 119.25], [159, 251]])) def test_numpy_numexpr(): if not numpy: skip("numpy not installed.") if not numexpr: skip("numexpr not installed.") a, b, c = numpy.random.randn(3, 128, 128) # ensure that numpy and numexpr return same value for complicated expression expr = sin(x) + cos(y) + tan(z)**2 + Abs(z-y)*acos(sin(y*z)) + \ Abs(y-z)*acosh(2+exp(y-x))- sqrt(x**2+I*y**2) npfunc = lambdify((x, y, z), expr, modules='numpy') nefunc = lambdify((x, y, z), expr, modules='numexpr') assert numpy.allclose(npfunc(a, b, c), nefunc(a, b, c)) def test_numexpr_userfunctions(): if not numpy: skip("numpy not installed.") if not numexpr: skip("numexpr not installed.") 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) def test_tensorflow_basic_math(): if not tensorflow: skip("tensorflow not installed.") expr = Max(sin(x), Abs(1/(x+2))) func = lambdify(x, expr, modules="tensorflow") with tensorflow.compat.v1.Session() as s: a = tensorflow.constant(0, dtype=tensorflow.float32) assert func(a).eval(session=s) == 0.5 def test_tensorflow_placeholders(): if not tensorflow: skip("tensorflow not installed.") expr = Max(sin(x), Abs(1/(x+2))) func = lambdify(x, expr, modules="tensorflow") with tensorflow.compat.v1.Session() as s: a = tensorflow.compat.v1.placeholder(dtype=tensorflow.float32) assert func(a).eval(session=s, feed_dict={a: 0}) == 0.5 def test_tensorflow_variables(): if not tensorflow: skip("tensorflow not installed.") expr = Max(sin(x), Abs(1/(x+2))) func = lambdify(x, expr, modules="tensorflow") with tensorflow.compat.v1.Session() as s: a = tensorflow.Variable(0, dtype=tensorflow.float32) s.run(a.initializer) assert func(a).eval(session=s, feed_dict={a: 0}) == 0.5 def test_tensorflow_logical_operations(): if not tensorflow: skip("tensorflow not installed.") expr = Not(And(Or(x, y), y)) func = lambdify([x, y], expr, modules="tensorflow") with tensorflow.compat.v1.Session() as s: assert func(False, True).eval(session=s) == False def test_tensorflow_piecewise(): if not tensorflow: skip("tensorflow not installed.") expr = Piecewise((0, Eq(x,0)), (-1, x < 0), (1, x > 0)) func = lambdify(x, expr, modules="tensorflow") with tensorflow.compat.v1.Session() as s: assert func(-1).eval(session=s) == -1 assert func(0).eval(session=s) == 0 assert func(1).eval(session=s) == 1 def test_tensorflow_multi_max(): if not tensorflow: skip("tensorflow not installed.") expr = Max(x, -x, x**2) func = lambdify(x, expr, modules="tensorflow") with tensorflow.compat.v1.Session() as s: assert func(-2).eval(session=s) == 4 def test_tensorflow_multi_min(): if not tensorflow: skip("tensorflow not installed.") expr = Min(x, -x, x**2) func = lambdify(x, expr, modules="tensorflow") with tensorflow.compat.v1.Session() as s: assert func(-2).eval(session=s) == -2 def test_tensorflow_relational(): if not tensorflow: skip("tensorflow not installed.") expr = x >= 0 func = lambdify(x, expr, modules="tensorflow") with tensorflow.compat.v1.Session() as s: assert func(1).eval(session=s) == True def test_tensorflow_complexes(): if not tensorflow: skip("tensorflow not installed") func1 = lambdify(x, re(x), modules="tensorflow") func2 = lambdify(x, im(x), modules="tensorflow") func3 = lambdify(x, Abs(x), modules="tensorflow") func4 = lambdify(x, arg(x), modules="tensorflow") with tensorflow.compat.v1.Session() as s: # For versions before # https://github.com/tensorflow/tensorflow/issues/30029 # resolved, using Python numeric types may not work a = tensorflow.constant(1+2j) assert func1(a).eval(session=s) == 1 assert func2(a).eval(session=s) == 2 tensorflow_result = func3(a).eval(session=s) sympy_result = Abs(1 + 2j).evalf() assert abs(tensorflow_result-sympy_result) < 10**-6 tensorflow_result = func4(a).eval(session=s) sympy_result = arg(1 + 2j).evalf() assert abs(tensorflow_result-sympy_result) < 10**-6 def test_tensorflow_array_arg(): # Test for issue 14655 (tensorflow part) if not tensorflow: skip("tensorflow not installed.") f = lambdify([[x, y]], x*x + y, 'tensorflow') with tensorflow.compat.v1.Session() as s: fcall = f(tensorflow.constant([2.0, 1.0])) assert fcall.eval(session=s) == 5.0 #================== Test symbolic ================================== def test_sym_single_arg(): f = lambdify(x, x * y) assert f(z) == z * y def test_sym_list_args(): f = lambdify([x, y], x + y + z) assert f(1, 2) == 3 + z def test_sym_integral(): f = Lambda(x, exp(-x**2)) l = lambdify(x, Integral(f(x), (x, -oo, oo)), modules="sympy") assert l(y) == Integral(exp(-y**2), (y, -oo, oo)) assert l(y).doit() == sqrt(pi) def test_namespace_order(): # lambdify had a bug, such that module dictionaries or cached module # dictionaries would pull earlier namespaces into themselves. # Because the module dictionaries form the namespace of the # generated lambda, this meant that the behavior of a previously # generated lambda function could change as a result of later calls # to lambdify. n1 = {'f': lambda x: 'first f'} n2 = {'f': lambda x: 'second f', 'g': lambda x: 'function g'} f = sympy.Function('f') g = sympy.Function('g') if1 = lambdify(x, f(x), modules=(n1, "sympy")) assert if1(1) == 'first f' if2 = lambdify(x, g(x), modules=(n2, "sympy")) # previously gave 'second f' assert if1(1) == 'first f' assert if2(1) == 'function g' 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 = sympy.Function('myfunc') assert not hasattr(func, '_imp_') my_f = implemented_function(func, lambda x: 2*x) assert hasattr(my_f, '_imp_') # Error for functions with same name and different implementation f2 = implemented_function("f", lambda x: x + 101) raises(ValueError, lambda: lambdify(x, f(f2(x)))) def test_imps_errors(): # Test errors that implemented functions can return, and still be able to # form expressions. # See: https://github.com/sympy/sympy/issues/10810 # # XXX: Removed AttributeError here. This test was added due to issue 10810 # but that issue was about ValueError. It doesn't seem reasonable to # "support" catching AttributeError in the same context... for val, error_class in product((0, 0., 2, 2.0), (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) def test_imps_wrong_args(): raises(ValueError, lambda: implemented_function(sin, lambda x: x)) def test_lambdify_imps(): # Test lambdify with implemented functions # first test basic (sympy) lambdify f = sympy.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 def test_dummification(): t = symbols('t') F = Function('F') G = Function('G') #"\alpha" is not a valid Python variable name #lambdify should sub in a dummy for it, and return #without a syntax error alpha = symbols(r'\alpha') some_expr = 2 * F(t)**2 / G(t) lam = lambdify((F(t), G(t)), some_expr) assert lam(3, 9) == 2 lam = lambdify(sin(t), 2 * sin(t)**2) assert lam(F(t)) == 2 * F(t)**2 #Test that \alpha was properly dummified lam = lambdify((alpha, t), 2*alpha + t) assert lam(2, 1) == 5 raises(SyntaxError, lambda: lambdify(F(t) * G(t), F(t) * G(t) + 5)) raises(SyntaxError, lambda: lambdify(2 * F(t), 2 * F(t) + 5)) raises(SyntaxError, lambda: lambdify(2 * F(t), 4 * F(t) + 5)) def test_lambdify__arguments_with_invalid_python_identifiers(): # see sympy/sympy#26690 N = CoordSys3D('N') xn, yn, zn = N.base_scalars() expr = xn + yn f = lambdify([xn, yn], expr) res = f(0.2, 0.3) ref = 0.2 + 0.3 assert abs(res-ref) < 1e-15 def test_curly_matrix_symbol(): # Issue #15009 curlyv = sympy.MatrixSymbol("{v}", 2, 1) lam = lambdify(curlyv, curlyv) assert lam(1)==1 lam = lambdify(curlyv, curlyv, dummify=True) assert lam(1)==1 def test_python_keywords(): # Test for issue 7452. The automatic dummification should ensure use of # Python reserved keywords as symbol names will create valid lambda # functions. This is an additional regression test. python_if = symbols('if') expr = python_if / 2 f = lambdify(python_if, expr) assert f(4.0) == 2.0 def test_lambdify_docstring(): func = lambdify((w, x, y, z), w + x + y + z) ref = ( "Created with lambdify. Signature:\n\n" "func(w, x, y, z)\n\n" "Expression:\n\n" "w + x + y + z" ).splitlines() assert func.__doc__.splitlines()[:len(ref)] == ref syms = symbols('a1:26') func = lambdify(syms, sum(syms)) ref = ( "Created with lambdify. Signature:\n\n" "func(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,\n" " a16, a17, a18, a19, a20, a21, a22, a23, a24, a25)\n\n" "Expression:\n\n" "a1 + a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + a19 + a2 + a20 +..." ).splitlines() assert func.__doc__.splitlines()[:len(ref)] == ref #================== Test special printers ========================== def test_special_printers(): from sympy.printing.lambdarepr import IntervalPrinter def intervalrepr(expr): return IntervalPrinter().doprint(expr) expr = sqrt(sqrt(2) + sqrt(3)) + S.Half func0 = lambdify((), expr, modules="mpmath", printer=intervalrepr) func1 = lambdify((), expr, modules="mpmath", printer=IntervalPrinter) func2 = lambdify((), expr, modules="mpmath", printer=IntervalPrinter()) mpi = type(mpmath.mpi(1, 2)) assert isinstance(func0(), mpi) assert isinstance(func1(), mpi) assert isinstance(func2(), mpi) # To check Is lambdify loggamma works for mpmath or not exp1 = lambdify(x, loggamma(x), 'mpmath')(5) exp2 = lambdify(x, loggamma(x), 'mpmath')(1.8) exp3 = lambdify(x, loggamma(x), 'mpmath')(15) exp_ls = [exp1, exp2, exp3] sol1 = mpmath.loggamma(5) sol2 = mpmath.loggamma(1.8) sol3 = mpmath.loggamma(15) sol_ls = [sol1, sol2, sol3] assert exp_ls == sol_ls def test_true_false(): # We want exact is comparison here, not just == assert lambdify([], true)() is True assert lambdify([], false)() is False def test_issue_2790(): assert lambdify((x, (y, z)), x + y)(1, (2, 4)) == 3 assert lambdify((x, (y, (w, z))), w + x + y + z)(1, (2, (3, 4))) == 10 assert lambdify(x, x + 1, dummify=False)(1) == 2 def test_issue_12092(): f = implemented_function('f', lambda x: x**2) assert f(f(2)).evalf() == Float(16) def test_issue_14911(): class Variable(sympy.Symbol): def _sympystr(self, printer): return printer.doprint(self.name) _lambdacode = _sympystr _numpycode = _sympystr x = Variable('x') y = 2 * x code = LambdaPrinter().doprint(y) assert code.replace(' ', '') == '2*x' def test_ITE(): assert lambdify((x, y, z), ITE(x, y, z))(True, 5, 3) == 5 assert lambdify((x, y, z), ITE(x, y, z))(False, 5, 3) == 3 def test_Min_Max(): # see gh-10375 assert lambdify((x, y, z), Min(x, y, z))(1, 2, 3) == 1 assert lambdify((x, y, z), Max(x, y, z))(1, 2, 3) == 3 def test_Indexed(): # Issue #10934 if not numpy: skip("numpy not installed") a = IndexedBase('a') i, j = symbols('i j') b = numpy.array([[1, 2], [3, 4]]) assert lambdify(a, Sum(a[x, y], (x, 0, 1), (y, 0, 1)))(b) == 10 def test_issue_12173(): #test for issue 12173 expr1 = lambdify((x, y), uppergamma(x, y),"mpmath")(1, 2) expr2 = lambdify((x, y), lowergamma(x, y),"mpmath")(1, 2) assert expr1 == uppergamma(1, 2).evalf() assert expr2 == lowergamma(1, 2).evalf() def test_issue_13642(): if not numpy: skip("numpy not installed") f = lambdify(x, sinc(x)) assert Abs(f(1) - sinc(1)).n() < 1e-15 def test_sinc_mpmath(): f = lambdify(x, sinc(x), "mpmath") assert Abs(f(1) - sinc(1)).n() < 1e-15 def test_lambdify_dummy_arg(): d1 = Dummy() f1 = lambdify(d1, d1 + 1, dummify=False) assert f1(2) == 3 f1b = lambdify(d1, d1 + 1) assert f1b(2) == 3 d2 = Dummy('x') f2 = lambdify(d2, d2 + 1) assert f2(2) == 3 f3 = lambdify([[d2]], d2 + 1) assert f3([2]) == 3 def test_lambdify_mixed_symbol_dummy_args(): d = Dummy() # Contrived example of name clash dsym = symbols(str(d)) f = lambdify([d, dsym], d - dsym) assert f(4, 1) == 3 def test_numpy_array_arg(): # Test for issue 14655 (numpy part) if not numpy: skip("numpy not installed") f = lambdify([[x, y]], x*x + y, 'numpy') assert f(numpy.array([2.0, 1.0])) == 5 def test_scipy_fns(): if not scipy: skip("scipy not installed") single_arg_sympy_fns = [Ei, erf, erfc, factorial, gamma, loggamma, digamma, Si, Ci] single_arg_scipy_fns = [scipy.special.expi, scipy.special.erf, scipy.special.erfc, scipy.special.factorial, scipy.special.gamma, scipy.special.gammaln, scipy.special.psi, scipy.special.sici, scipy.special.sici] numpy.random.seed(0) for (sympy_fn, scipy_fn) in zip(single_arg_sympy_fns, single_arg_scipy_fns): f = lambdify(x, sympy_fn(x), modules="scipy") for i in range(20): tv = numpy.random.uniform(-10, 10) + 1j*numpy.random.uniform(-5, 5) # SciPy thinks that factorial(z) is 0 when re(z) < 0 and # does not support complex numbers. # SymPy does not think so. if sympy_fn == factorial: tv = numpy.abs(tv) # SciPy supports gammaln for real arguments only, # and there is also a branch cut along the negative real axis if sympy_fn == loggamma: tv = numpy.abs(tv) # SymPy's digamma evaluates as polygamma(0, z) # which SciPy supports for real arguments only if sympy_fn == digamma: tv = numpy.real(tv) sympy_result = sympy_fn(tv).evalf() scipy_result = scipy_fn(tv) # SciPy's sici returns a tuple with both Si and Ci present in it # which needs to be unpacked if sympy_fn == Si: scipy_result = scipy_fn(tv)[0] if sympy_fn == Ci: scipy_result = scipy_fn(tv)[1] assert abs(f(tv) - sympy_result) < 1e-13*(1 + abs(sympy_result)) assert abs(f(tv) - scipy_result) < 1e-13*(1 + abs(sympy_result)) double_arg_sympy_fns = [RisingFactorial, besselj, bessely, besseli, besselk, polygamma] double_arg_scipy_fns = [scipy.special.poch, scipy.special.jv, scipy.special.yv, scipy.special.iv, scipy.special.kv, scipy.special.polygamma] for (sympy_fn, scipy_fn) in zip(double_arg_sympy_fns, double_arg_scipy_fns): f = lambdify((x, y), sympy_fn(x, y), modules="scipy") for i in range(20): # SciPy supports only real orders of Bessel functions tv1 = numpy.random.uniform(-10, 10) tv2 = numpy.random.uniform(-10, 10) + 1j*numpy.random.uniform(-5, 5) # SciPy requires a real valued 2nd argument for: poch, polygamma if sympy_fn in (RisingFactorial, polygamma): tv2 = numpy.real(tv2) if sympy_fn == polygamma: tv1 = abs(int(tv1)) # first argument to polygamma must be a non-negative integer. sympy_result = sympy_fn(tv1, tv2).evalf() assert abs(f(tv1, tv2) - sympy_result) < 1e-13*(1 + abs(sympy_result)) assert abs(f(tv1, tv2) - scipy_fn(tv1, tv2)) < 1e-13*(1 + abs(sympy_result)) def test_scipy_polys(): if not scipy: skip("scipy not installed") numpy.random.seed(0) params = symbols('n k a b') # list polynomials with the number of parameters polys = [ (chebyshevt, 1), (chebyshevu, 1), (legendre, 1), (hermite, 1), (laguerre, 1), (gegenbauer, 2), (assoc_legendre, 2), (assoc_laguerre, 2), (jacobi, 3) ] msg = \ "The random test of the function {func} with the arguments " \ "{args} had failed because the SymPy result {sympy_result} " \ "and SciPy result {scipy_result} had failed to converge " \ "within the tolerance {tol} " \ "(Actual absolute difference : {diff})" for sympy_fn, num_params in polys: args = params[:num_params] + (x,) f = lambdify(args, sympy_fn(*args)) for _ in range(10): tn = numpy.random.randint(3, 10) tparams = tuple(numpy.random.uniform(0, 5, size=num_params-1)) tv = numpy.random.uniform(-10, 10) + 1j*numpy.random.uniform(-5, 5) # SciPy supports hermite for real arguments only if sympy_fn == hermite: tv = numpy.real(tv) # assoc_legendre needs x in (-1, 1) and integer param at most n if sympy_fn == assoc_legendre: tv = numpy.random.uniform(-1, 1) tparams = tuple(numpy.random.randint(1, tn, size=1)) vals = (tn,) + tparams + (tv,) scipy_result = f(*vals) sympy_result = sympy_fn(*vals).evalf() atol = 1e-9*(1 + abs(sympy_result)) diff = abs(scipy_result - sympy_result) try: assert diff < atol except TypeError: raise AssertionError( msg.format( func=repr(sympy_fn), args=repr(vals), sympy_result=repr(sympy_result), scipy_result=repr(scipy_result), diff=diff, tol=atol) ) def test_lambdify_inspect(): f = lambdify(x, x**2) # Test that inspect.getsource works but don't hard-code implementation # details assert 'x**2' in inspect.getsource(f) def test_issue_14941(): x, y = Dummy(), Dummy() # test dict f1 = lambdify([x, y], {x: 3, y: 3}, 'sympy') assert f1(2, 3) == {2: 3, 3: 3} # test tuple f2 = lambdify([x, y], (y, x), 'sympy') assert f2(2, 3) == (3, 2) f2b = lambdify([], (1,)) # gh-23224 assert f2b() == (1,) # test list f3 = lambdify([x, y], [y, x], 'sympy') assert f3(2, 3) == [3, 2] def test_lambdify_Derivative_arg_issue_16468(): f = Function('f')(x) fx = f.diff() assert lambdify((f, fx), f + fx)(10, 5) == 15 assert eval(lambdastr((f, fx), f/fx))(10, 5) == 2 raises(Exception, lambda: eval(lambdastr((f, fx), f/fx, dummify=False))) assert eval(lambdastr((f, fx), f/fx, dummify=True))(10, 5) == 2 assert eval(lambdastr((fx, f), f/fx, dummify=True))(S(10), 5) == S.Half assert lambdify(fx, 1 + fx)(41) == 42 assert eval(lambdastr(fx, 1 + fx, dummify=True))(41) == 42 def test_imag_real(): f_re = lambdify([z], sympy.re(z)) val = 3+2j assert f_re(val) == val.real f_im = lambdify([z], sympy.im(z)) # see #15400 assert f_im(val) == val.imag def test_MatrixSymbol_issue_15578(): if not numpy: skip("numpy not installed") A = MatrixSymbol('A', 2, 2) A0 = numpy.array([[1, 2], [3, 4]]) f = lambdify(A, A**(-1)) assert numpy.allclose(f(A0), numpy.array([[-2., 1.], [1.5, -0.5]])) g = lambdify(A, A**3) assert numpy.allclose(g(A0), numpy.array([[37, 54], [81, 118]])) def test_issue_15654(): if not scipy: skip("scipy not installed") from sympy.abc import n, l, r, Z from sympy.physics import hydrogen nv, lv, rv, Zv = 1, 0, 3, 1 sympy_value = hydrogen.R_nl(nv, lv, rv, Zv).evalf() f = lambdify((n, l, r, Z), hydrogen.R_nl(n, l, r, Z)) scipy_value = f(nv, lv, rv, Zv) assert abs(sympy_value - scipy_value) < 1e-15 def test_issue_15827(): if not numpy: skip("numpy not installed") A = MatrixSymbol("A", 3, 3) B = MatrixSymbol("B", 2, 3) C = MatrixSymbol("C", 3, 4) D = MatrixSymbol("D", 4, 5) k=symbols("k") f = lambdify(A, (2*k)*A) g = lambdify(A, (2+k)*A) h = lambdify(A, 2*A) i = lambdify((B, C, D), 2*B*C*D) assert numpy.array_equal(f(numpy.array([[1, 2, 3], [1, 2, 3], [1, 2, 3]])), \ numpy.array([[2*k, 4*k, 6*k], [2*k, 4*k, 6*k], [2*k, 4*k, 6*k]], dtype=object)) assert numpy.array_equal(g(numpy.array([[1, 2, 3], [1, 2, 3], [1, 2, 3]])), \ numpy.array([[k + 2, 2*k + 4, 3*k + 6], [k + 2, 2*k + 4, 3*k + 6], \ [k + 2, 2*k + 4, 3*k + 6]], dtype=object)) assert numpy.array_equal(h(numpy.array([[1, 2, 3], [1, 2, 3], [1, 2, 3]])), \ numpy.array([[2, 4, 6], [2, 4, 6], [2, 4, 6]])) assert numpy.array_equal(i(numpy.array([[1, 2, 3], [1, 2, 3]]), numpy.array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]), \ numpy.array([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]])), numpy.array([[ 120, 240, 360, 480, 600], \ [ 120, 240, 360, 480, 600]])) def test_issue_16930(): if not scipy: skip("scipy not installed") x = symbols("x") f = lambda x: S.GoldenRatio * x**2 f_ = lambdify(x, f(x), modules='scipy') assert f_(1) == scipy.constants.golden_ratio def test_issue_17898(): if not scipy: skip("scipy not installed") x = symbols("x") f_ = lambdify([x], sympy.LambertW(x,-1), modules='scipy') assert f_(0.1) == mpmath.lambertw(0.1, -1) def test_issue_13167_21411(): if not numpy: skip("numpy not installed") f1 = lambdify(x, sympy.Heaviside(x)) f2 = lambdify(x, sympy.Heaviside(x, 1)) res1 = f1([-1, 0, 1]) res2 = f2([-1, 0, 1]) assert Abs(res1[0]).n() < 1e-15 # First functionality: only one argument passed assert Abs(res1[1] - 1/2).n() < 1e-15 assert Abs(res1[2] - 1).n() < 1e-15 assert Abs(res2[0]).n() < 1e-15 # Second functionality: two arguments passed assert Abs(res2[1] - 1).n() < 1e-15 assert Abs(res2[2] - 1).n() < 1e-15 def test_single_e(): f = lambdify(x, E) assert f(23) == exp(1.0) def test_issue_16536(): if not scipy: skip("scipy not installed") a = symbols('a') f1 = lowergamma(a, x) F = lambdify((a, x), f1, modules='scipy') assert abs(lowergamma(1, 3) - F(1, 3)) <= 1e-10 f2 = uppergamma(a, x) F = lambdify((a, x), f2, modules='scipy') assert abs(uppergamma(1, 3) - F(1, 3)) <= 1e-10 def test_issue_22726(): if not numpy: skip("numpy not installed") x1, x2 = symbols('x1 x2') f = Max(S.Zero, Min(x1, x2)) g = derive_by_array(f, (x1, x2)) G = lambdify((x1, x2), g, modules='numpy') point = {x1: 1, x2: 2} assert (abs(g.subs(point) - G(*point.values())) <= 1e-10).all() def test_issue_22739(): if not numpy: skip("numpy not installed") x1, x2 = symbols('x1 x2') f = Heaviside(Min(x1, x2)) F = lambdify((x1, x2), f, modules='numpy') point = {x1: 1, x2: 2} assert abs(f.subs(point) - F(*point.values())) <= 1e-10 def test_issue_22992(): if not numpy: skip("numpy not installed") a, t = symbols('a t') expr = a*(log(cot(t/2)) - cos(t)) F = lambdify([a, t], expr, 'numpy') point = {a: 10, t: 2} assert abs(expr.subs(point) - F(*point.values())) <= 1e-10 # Standard math F = lambdify([a, t], expr) assert abs(expr.subs(point) - F(*point.values())) <= 1e-10 def test_issue_19764(): if not numpy: skip("numpy not installed") expr = Array([x, x**2]) f = lambdify(x, expr, 'numpy') assert f(1).__class__ == numpy.ndarray def test_issue_20070(): if not numba: skip("numba not installed") f = lambdify(x, sin(x), 'numpy') assert numba.jit(f, nopython=True)(1)==0.8414709848078965 def test_fresnel_integrals_scipy(): if not scipy: skip("scipy not installed") f1 = fresnelc(x) f2 = fresnels(x) F1 = lambdify(x, f1, modules='scipy') F2 = lambdify(x, f2, modules='scipy') assert abs(fresnelc(1.3) - F1(1.3)) <= 1e-10 assert abs(fresnels(1.3) - F2(1.3)) <= 1e-10 def test_beta_scipy(): if not scipy: skip("scipy not installed") f = beta(x, y) F = lambdify((x, y), f, modules='scipy') assert abs(beta(1.3, 2.3) - F(1.3, 2.3)) <= 1e-10 def test_beta_math(): f = beta(x, y) F = lambdify((x, y), f, modules='math') assert abs(beta(1.3, 2.3) - F(1.3, 2.3)) <= 1e-10 def test_betainc_scipy(): if not scipy: skip("scipy not installed") f = betainc(w, x, y, z) F = lambdify((w, x, y, z), f, modules='scipy') assert abs(betainc(1.4, 3.1, 0.1, 0.5) - F(1.4, 3.1, 0.1, 0.5)) <= 1e-10 def test_betainc_regularized_scipy(): if not scipy: skip("scipy not installed") f = betainc_regularized(w, x, y, z) F = lambdify((w, x, y, z), f, modules='scipy') assert abs(betainc_regularized(0.2, 3.5, 0.1, 1) - F(0.2, 3.5, 0.1, 1)) <= 1e-10 def test_numpy_special_math(): if not numpy: skip("numpy not installed") funcs = [expm1, log1p, exp2, log2, log10, hypot, logaddexp, logaddexp2] for func in funcs: if 2 in func.nargs: expr = func(x, y) args = (x, y) num_args = (0.3, 0.4) elif 1 in func.nargs: expr = func(x) args = (x,) num_args = (0.3,) else: raise NotImplementedError("Need to handle other than unary & binary functions in test") f = lambdify(args, expr) result = f(*num_args) reference = expr.subs(dict(zip(args, num_args))).evalf() assert numpy.allclose(result, float(reference)) lae2 = lambdify((x, y), logaddexp2(log2(x), log2(y))) assert abs(2.0**lae2(1e-50, 2.5e-50) - 3.5e-50) < 1e-62 # from NumPy's docstring def test_scipy_special_math(): if not scipy: skip("scipy not installed") cm1 = lambdify((x,), cosm1(x), modules='scipy') assert abs(cm1(1e-20) + 5e-41) < 1e-200 have_scipy_1_10plus = tuple(map(int, scipy.version.version.split('.')[:2])) >= (1, 10) if have_scipy_1_10plus: cm2 = lambdify((x, y), powm1(x, y), modules='scipy') assert abs(cm2(1.2, 1e-9) - 1.82321557e-10) < 1e-17 def test_scipy_bernoulli(): if not scipy: skip("scipy not installed") bern = lambdify((x,), bernoulli(x), modules='scipy') assert bern(1) == 0.5 def test_scipy_harmonic(): if not scipy: skip("scipy not installed") hn = lambdify((x,), harmonic(x), modules='scipy') assert hn(2) == 1.5 hnm = lambdify((x, y), harmonic(x, y), modules='scipy') assert hnm(2, 2) == 1.25 def test_cupy_array_arg(): if not cupy: skip("CuPy not installed") f = lambdify([[x, y]], x*x + y, 'cupy') result = f(cupy.array([2.0, 1.0])) assert result == 5 assert "cupy" in str(type(result)) def test_cupy_array_arg_using_numpy(): # numpy functions can be run on cupy arrays # unclear if we can "officially" support this, # depends on numpy __array_function__ support if not cupy: skip("CuPy not installed") f = lambdify([[x, y]], x*x + y, 'numpy') result = f(cupy.array([2.0, 1.0])) assert result == 5 assert "cupy" in str(type(result)) def test_cupy_dotproduct(): if not cupy: skip("CuPy not installed") A = Matrix([x, y, z]) f1 = lambdify([x, y, z], DotProduct(A, A), modules='cupy') f2 = lambdify([x, y, z], DotProduct(A, A.T), modules='cupy') f3 = lambdify([x, y, z], DotProduct(A.T, A), modules='cupy') f4 = lambdify([x, y, z], DotProduct(A, A.T), modules='cupy') assert f1(1, 2, 3) == \ f2(1, 2, 3) == \ f3(1, 2, 3) == \ f4(1, 2, 3) == \ cupy.array([14]) def test_jax_array_arg(): if not jax: skip("JAX not installed") f = lambdify([[x, y]], x*x + y, 'jax') result = f(jax.numpy.array([2.0, 1.0])) assert result == 5 assert "jax" in str(type(result)) def test_jax_array_arg_using_numpy(): if not jax: skip("JAX not installed") f = lambdify([[x, y]], x*x + y, 'numpy') result = f(jax.numpy.array([2.0, 1.0])) assert result == 5 assert "jax" in str(type(result)) def test_jax_dotproduct(): if not jax: skip("JAX not installed") A = Matrix([x, y, z]) f1 = lambdify([x, y, z], DotProduct(A, A), modules='jax') f2 = lambdify([x, y, z], DotProduct(A, A.T), modules='jax') f3 = lambdify([x, y, z], DotProduct(A.T, A), modules='jax') f4 = lambdify([x, y, z], DotProduct(A, A.T), modules='jax') assert f1(1, 2, 3) == \ f2(1, 2, 3) == \ f3(1, 2, 3) == \ f4(1, 2, 3) == \ jax.numpy.array([14]) def test_lambdify_cse(): def no_op_cse(exprs): return (), exprs def dummy_cse(exprs): from sympy.simplify.cse_main import cse return cse(exprs, symbols=numbered_symbols(cls=Dummy)) def minmem(exprs): from sympy.simplify.cse_main import cse_release_variables, cse return cse(exprs, postprocess=cse_release_variables) class Case: def __init__(self, *, args, exprs, num_args, requires_numpy=False): self.args = args self.exprs = exprs self.num_args = num_args subs_dict = dict(zip(self.args, self.num_args)) self.ref = [e.subs(subs_dict).evalf() for e in exprs] self.requires_numpy = requires_numpy def lambdify(self, *, cse): return lambdify(self.args, self.exprs, cse=cse) def assertAllClose(self, result, *, abstol=1e-15, reltol=1e-15): if self.requires_numpy: assert all(numpy.allclose(result[i], numpy.asarray(r, dtype=float), rtol=reltol, atol=abstol) for i, r in enumerate(self.ref)) return for i, r in enumerate(self.ref): abs_err = abs(result[i] - r) if r == 0: assert abs_err < abstol else: assert abs_err/abs(r) < reltol cases = [ Case( args=(x, y, z), exprs=[ x + y + z, x + y - z, 2*x + 2*y - z, (x+y)**2 + (y+z)**2, ], num_args=(2., 3., 4.) ), Case( args=(x, y, z), exprs=[ x + sympy.Heaviside(x), y + sympy.Heaviside(x), z + sympy.Heaviside(x, 1), z/sympy.Heaviside(x, 1) ], num_args=(0., 3., 4.) ), Case( args=(x, y, z), exprs=[ x + sinc(y), y + sinc(y), z - sinc(y) ], num_args=(0.1, 0.2, 0.3) ), Case( args=(x, y, z), exprs=[ Matrix([[x, x*y], [sin(z) + 4, x**z]]), x*y+sin(z)-x**z, Matrix([x*x, sin(z), x**z]) ], num_args=(1.,2.,3.), requires_numpy=True ), Case( args=(x, y), exprs=[(x + y - 1)**2, x, x + y, (x + y)/(2*x + 1) + (x + y - 1)**2, (2*x + 1)**(x + y)], num_args=(1,2) ) ] for case in cases: if not numpy and case.requires_numpy: continue for _cse in [False, True, minmem, no_op_cse, dummy_cse]: f = case.lambdify(cse=_cse) result = f(*case.num_args) case.assertAllClose(result) def test_issue_25288(): syms = numbered_symbols(cls=Dummy) ok = lambdify(x, [x**2, sin(x**2)], cse=lambda e: cse(e, symbols=syms))(2) assert ok def test_deprecated_set(): with warns_deprecated_sympy(): lambdify({x, y}, x + y) def test_issue_13881(): if not numpy: skip("numpy not installed.") X = MatrixSymbol('X', 3, 1) f = lambdify(X, X.T*X, 'numpy') assert f(numpy.array([1, 2, 3])) == 14 assert f(numpy.array([3, 2, 1])) == 14 f = lambdify(X, X*X.T, 'numpy') assert f(numpy.array([1, 2, 3])) == 14 assert f(numpy.array([3, 2, 1])) == 14 f = lambdify(X, (X*X.T)*X, 'numpy') arr1 = numpy.array([[1], [2], [3]]) arr2 = numpy.array([[14],[28],[42]]) assert numpy.array_equal(f(arr1), arr2) def test_23536_lambdify_cse_dummy(): f = Function('x')(y) g = Function('w')(y) expr = z + (f**4 + g**5)*(f**3 + (g*f)**3) expr = expr.expand() eval_expr = lambdify(((f, g), z), expr, cse=True) ans = eval_expr((1.0, 2.0), 3.0) # shouldn't raise NameError assert ans == 300.0 # not a list and value is 300 class LambdifyDocstringTestCase: SIGNATURE = None EXPR = None SRC = None def __init__(self, docstring_limit, expected_redacted): self.docstring_limit = docstring_limit self.expected_redacted = expected_redacted @property def expected_expr(self): expr_redacted_msg = "EXPRESSION REDACTED DUE TO LENGTH, (see lambdify's `docstring_limit`)" return self.EXPR if not self.expected_redacted else expr_redacted_msg @property def expected_src(self): src_redacted_msg = "SOURCE CODE REDACTED DUE TO LENGTH, (see lambdify's `docstring_limit`)" return self.SRC if not self.expected_redacted else src_redacted_msg @property def expected_docstring(self): expected_docstring = ( f'Created with lambdify. Signature:\n\n' f'func({self.SIGNATURE})\n\n' f'Expression:\n\n' f'{self.expected_expr}\n\n' f'Source code:\n\n' f'{self.expected_src}\n\n' f'Imported modules:\n\n' ) return expected_docstring def __len__(self): return len(self.expected_docstring) def __repr__(self): return ( f'{self.__class__.__name__}(' f'docstring_limit={self.docstring_limit}, ' f'expected_redacted={self.expected_redacted})' ) def test_lambdify_docstring_size_limit_simple_symbol(): class SimpleSymbolTestCase(LambdifyDocstringTestCase): SIGNATURE = 'x' EXPR = 'x' SRC = ( 'def _lambdifygenerated(x):\n' ' return x\n' ) x = symbols('x') test_cases = ( SimpleSymbolTestCase(docstring_limit=None, expected_redacted=False), SimpleSymbolTestCase(docstring_limit=100, expected_redacted=False), SimpleSymbolTestCase(docstring_limit=1, expected_redacted=False), SimpleSymbolTestCase(docstring_limit=0, expected_redacted=True), SimpleSymbolTestCase(docstring_limit=-1, expected_redacted=True), ) for test_case in test_cases: lambdified_expr = lambdify( [x], x, 'sympy', docstring_limit=test_case.docstring_limit, ) assert lambdified_expr.__doc__ == test_case.expected_docstring def test_lambdify_docstring_size_limit_nested_expr(): class ExprListTestCase(LambdifyDocstringTestCase): SIGNATURE = 'x, y, z' EXPR = ( '[x, [y], z, x**3 + 3*x**2*y + 3*x**2*z + 3*x*y**2 + 6*x*y*z ' '+ 3*x*z**2 +...' ) SRC = ( 'def _lambdifygenerated(x, y, z):\n' ' return [x, [y], z, x**3 + 3*x**2*y + 3*x**2*z + 3*x*y**2 ' '+ 6*x*y*z + 3*x*z**2 + y**3 + 3*y**2*z + 3*y*z**2 + z**3]\n' ) x, y, z = symbols('x, y, z') expr = [x, [y], z, ((x + y + z)**3).expand()] test_cases = ( ExprListTestCase(docstring_limit=None, expected_redacted=False), ExprListTestCase(docstring_limit=200, expected_redacted=False), ExprListTestCase(docstring_limit=50, expected_redacted=True), ExprListTestCase(docstring_limit=0, expected_redacted=True), ExprListTestCase(docstring_limit=-1, expected_redacted=True), ) for test_case in test_cases: lambdified_expr = lambdify( [x, y, z], expr, 'sympy', docstring_limit=test_case.docstring_limit, ) assert lambdified_expr.__doc__ == test_case.expected_docstring def test_lambdify_docstring_size_limit_matrix(): class MatrixTestCase(LambdifyDocstringTestCase): SIGNATURE = 'x, y, z' EXPR = ( 'Matrix([[0, x], [x + y + z, x**3 + 3*x**2*y + 3*x**2*z + 3*x*y**2 ' '+ 6*x*y*z...' ) SRC = ( 'def _lambdifygenerated(x, y, z):\n' ' return ImmutableDenseMatrix([[0, x], [x + y + z, x**3 ' '+ 3*x**2*y + 3*x**2*z + 3*x*y**2 + 6*x*y*z + 3*x*z**2 + y**3 ' '+ 3*y**2*z + 3*y*z**2 + z**3]])\n' ) x, y, z = symbols('x, y, z') expr = Matrix([[S.Zero, x], [x + y + z, ((x + y + z)**3).expand()]]) test_cases = ( MatrixTestCase(docstring_limit=None, expected_redacted=False), MatrixTestCase(docstring_limit=200, expected_redacted=False), MatrixTestCase(docstring_limit=50, expected_redacted=True), MatrixTestCase(docstring_limit=0, expected_redacted=True), MatrixTestCase(docstring_limit=-1, expected_redacted=True), ) for test_case in test_cases: lambdified_expr = lambdify( [x, y, z], expr, 'sympy', docstring_limit=test_case.docstring_limit, ) assert lambdified_expr.__doc__ == test_case.expected_docstring def test_lambdify_empty_tuple(): a = symbols("a") expr = ((), (a,)) f = lambdify(a, expr) result = f(1) assert result == ((), (1,)), "Lambdify did not handle the empty tuple correctly." def test_assoc_legendre_numerical_evaluation(): tol = 1e-10 sympy_result_integer = assoc_legendre(1, 1/2, 0.1).evalf() sympy_result_complex = assoc_legendre(2, 1, 3).evalf() mpmath_result_integer = -0.474572528387641 mpmath_result_complex = -25.45584412271571*I assert all_close(sympy_result_integer, mpmath_result_integer, tol) assert all_close(sympy_result_complex, mpmath_result_complex, tol)