Coverage for /usr/lib/python3/dist-packages/sympy/utilities/lambdify.py: 52%

427 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1""" 

2This module provides convenient functions to transform SymPy expressions to 

3lambda functions which can be used to calculate numerical values very fast. 

4""" 

5 

6from __future__ import annotations 

7from typing import Any 

8 

9import builtins 

10import inspect 

11import keyword 

12import textwrap 

13import linecache 

14 

15# Required despite static analysis claiming it is not used 

16from sympy.external import import_module # noqa:F401 

17from sympy.utilities.exceptions import sympy_deprecation_warning 

18from sympy.utilities.decorator import doctest_depends_on 

19from sympy.utilities.iterables import (is_sequence, iterable, 

20 NotIterable, flatten) 

21from sympy.utilities.misc import filldedent 

22 

23__doctest_requires__ = {('lambdify',): ['numpy', 'tensorflow']} 

24 

25# Default namespaces, letting us define translations that can't be defined 

26# by simple variable maps, like I => 1j 

27MATH_DEFAULT: dict[str, Any] = {} 

28MPMATH_DEFAULT: dict[str, Any] = {} 

29NUMPY_DEFAULT: dict[str, Any] = {"I": 1j} 

30SCIPY_DEFAULT: dict[str, Any] = {"I": 1j} 

31CUPY_DEFAULT: dict[str, Any] = {"I": 1j} 

32JAX_DEFAULT: dict[str, Any] = {"I": 1j} 

33TENSORFLOW_DEFAULT: dict[str, Any] = {} 

34SYMPY_DEFAULT: dict[str, Any] = {} 

35NUMEXPR_DEFAULT: dict[str, Any] = {} 

36 

37# These are the namespaces the lambda functions will use. 

38# These are separate from the names above because they are modified 

39# throughout this file, whereas the defaults should remain unmodified. 

40 

41MATH = MATH_DEFAULT.copy() 

42MPMATH = MPMATH_DEFAULT.copy() 

43NUMPY = NUMPY_DEFAULT.copy() 

44SCIPY = SCIPY_DEFAULT.copy() 

45CUPY = CUPY_DEFAULT.copy() 

46JAX = JAX_DEFAULT.copy() 

47TENSORFLOW = TENSORFLOW_DEFAULT.copy() 

48SYMPY = SYMPY_DEFAULT.copy() 

49NUMEXPR = NUMEXPR_DEFAULT.copy() 

50 

51 

52# Mappings between SymPy and other modules function names. 

53MATH_TRANSLATIONS = { 

54 "ceiling": "ceil", 

55 "E": "e", 

56 "ln": "log", 

57} 

58 

59# NOTE: This dictionary is reused in Function._eval_evalf to allow subclasses 

60# of Function to automatically evalf. 

61MPMATH_TRANSLATIONS = { 

62 "Abs": "fabs", 

63 "elliptic_k": "ellipk", 

64 "elliptic_f": "ellipf", 

65 "elliptic_e": "ellipe", 

66 "elliptic_pi": "ellippi", 

67 "ceiling": "ceil", 

68 "chebyshevt": "chebyt", 

69 "chebyshevu": "chebyu", 

70 "E": "e", 

71 "I": "j", 

72 "ln": "log", 

73 #"lowergamma":"lower_gamma", 

74 "oo": "inf", 

75 #"uppergamma":"upper_gamma", 

76 "LambertW": "lambertw", 

77 "MutableDenseMatrix": "matrix", 

78 "ImmutableDenseMatrix": "matrix", 

79 "conjugate": "conj", 

80 "dirichlet_eta": "altzeta", 

81 "Ei": "ei", 

82 "Shi": "shi", 

83 "Chi": "chi", 

84 "Si": "si", 

85 "Ci": "ci", 

86 "RisingFactorial": "rf", 

87 "FallingFactorial": "ff", 

88 "betainc_regularized": "betainc", 

89} 

90 

91NUMPY_TRANSLATIONS: dict[str, str] = { 

92 "Heaviside": "heaviside", 

93} 

94SCIPY_TRANSLATIONS: dict[str, str] = {} 

95CUPY_TRANSLATIONS: dict[str, str] = {} 

96JAX_TRANSLATIONS: dict[str, str] = {} 

97 

98TENSORFLOW_TRANSLATIONS: dict[str, str] = {} 

99 

100NUMEXPR_TRANSLATIONS: dict[str, str] = {} 

101 

102# Available modules: 

103MODULES = { 

104 "math": (MATH, MATH_DEFAULT, MATH_TRANSLATIONS, ("from math import *",)), 

105 "mpmath": (MPMATH, MPMATH_DEFAULT, MPMATH_TRANSLATIONS, ("from mpmath import *",)), 

106 "numpy": (NUMPY, NUMPY_DEFAULT, NUMPY_TRANSLATIONS, ("import numpy; from numpy import *; from numpy.linalg import *",)), 

107 "scipy": (SCIPY, SCIPY_DEFAULT, SCIPY_TRANSLATIONS, ("import scipy; import numpy; from scipy.special import *",)), 

108 "cupy": (CUPY, CUPY_DEFAULT, CUPY_TRANSLATIONS, ("import cupy",)), 

109 "jax": (JAX, JAX_DEFAULT, JAX_TRANSLATIONS, ("import jax",)), 

110 "tensorflow": (TENSORFLOW, TENSORFLOW_DEFAULT, TENSORFLOW_TRANSLATIONS, ("import tensorflow",)), 

111 "sympy": (SYMPY, SYMPY_DEFAULT, {}, ( 

112 "from sympy.functions import *", 

113 "from sympy.matrices import *", 

114 "from sympy import Integral, pi, oo, nan, zoo, E, I",)), 

115 "numexpr" : (NUMEXPR, NUMEXPR_DEFAULT, NUMEXPR_TRANSLATIONS, 

116 ("import_module('numexpr')", )), 

117} 

118 

119 

120def _import(module, reload=False): 

121 """ 

122 Creates a global translation dictionary for module. 

123 

124 The argument module has to be one of the following strings: "math", 

125 "mpmath", "numpy", "sympy", "tensorflow", "jax". 

126 These dictionaries map names of Python functions to their equivalent in 

127 other modules. 

128 """ 

129 try: 

130 namespace, namespace_default, translations, import_commands = MODULES[ 

131 module] 

132 except KeyError: 

133 raise NameError( 

134 "'%s' module cannot be used for lambdification" % module) 

135 

136 # Clear namespace or exit 

137 if namespace != namespace_default: 

138 # The namespace was already generated, don't do it again if not forced. 

139 if reload: 

140 namespace.clear() 

141 namespace.update(namespace_default) 

142 else: 

143 return 

144 

145 for import_command in import_commands: 

146 if import_command.startswith('import_module'): 

147 module = eval(import_command) 

148 

149 if module is not None: 

150 namespace.update(module.__dict__) 

151 continue 

152 else: 

153 try: 

154 exec(import_command, {}, namespace) 

155 continue 

156 except ImportError: 

157 pass 

158 

159 raise ImportError( 

160 "Cannot import '%s' with '%s' command" % (module, import_command)) 

161 

162 # Add translated names to namespace 

163 for sympyname, translation in translations.items(): 

164 namespace[sympyname] = namespace[translation] 

165 

166 # For computing the modulus of a SymPy expression we use the builtin abs 

167 # function, instead of the previously used fabs function for all 

168 # translation modules. This is because the fabs function in the math 

169 # module does not accept complex valued arguments. (see issue 9474). The 

170 # only exception, where we don't use the builtin abs function is the 

171 # mpmath translation module, because mpmath.fabs returns mpf objects in 

172 # contrast to abs(). 

173 if 'Abs' not in namespace: 

174 namespace['Abs'] = abs 

175 

176# Used for dynamically generated filenames that are inserted into the 

177# linecache. 

178_lambdify_generated_counter = 1 

179 

180 

181@doctest_depends_on(modules=('numpy', 'scipy', 'tensorflow',), python_version=(3,)) 

182def lambdify(args, expr, modules=None, printer=None, use_imps=True, 

183 dummify=False, cse=False, docstring_limit=1000): 

184 """Convert a SymPy expression into a function that allows for fast 

185 numeric evaluation. 

186 

187 .. warning:: 

188 This function uses ``exec``, and thus should not be used on 

189 unsanitized input. 

190 

191 .. deprecated:: 1.7 

192 Passing a set for the *args* parameter is deprecated as sets are 

193 unordered. Use an ordered iterable such as a list or tuple. 

194 

195 Explanation 

196 =========== 

197 

198 For example, to convert the SymPy expression ``sin(x) + cos(x)`` to an 

199 equivalent NumPy function that numerically evaluates it: 

200 

201 >>> from sympy import sin, cos, symbols, lambdify 

202 >>> import numpy as np 

203 >>> x = symbols('x') 

204 >>> expr = sin(x) + cos(x) 

205 >>> expr 

206 sin(x) + cos(x) 

207 >>> f = lambdify(x, expr, 'numpy') 

208 >>> a = np.array([1, 2]) 

209 >>> f(a) 

210 [1.38177329 0.49315059] 

211 

212 The primary purpose of this function is to provide a bridge from SymPy 

213 expressions to numerical libraries such as NumPy, SciPy, NumExpr, mpmath, 

214 and tensorflow. In general, SymPy functions do not work with objects from 

215 other libraries, such as NumPy arrays, and functions from numeric 

216 libraries like NumPy or mpmath do not work on SymPy expressions. 

217 ``lambdify`` bridges the two by converting a SymPy expression to an 

218 equivalent numeric function. 

219 

220 The basic workflow with ``lambdify`` is to first create a SymPy expression 

221 representing whatever mathematical function you wish to evaluate. This 

222 should be done using only SymPy functions and expressions. Then, use 

223 ``lambdify`` to convert this to an equivalent function for numerical 

224 evaluation. For instance, above we created ``expr`` using the SymPy symbol 

225 ``x`` and SymPy functions ``sin`` and ``cos``, then converted it to an 

226 equivalent NumPy function ``f``, and called it on a NumPy array ``a``. 

227 

228 Parameters 

229 ========== 

230 

231 args : List[Symbol] 

232 A variable or a list of variables whose nesting represents the 

233 nesting of the arguments that will be passed to the function. 

234 

235 Variables can be symbols, undefined functions, or matrix symbols. 

236 

237 >>> from sympy import Eq 

238 >>> from sympy.abc import x, y, z 

239 

240 The list of variables should match the structure of how the 

241 arguments will be passed to the function. Simply enclose the 

242 parameters as they will be passed in a list. 

243 

244 To call a function like ``f(x)`` then ``[x]`` 

245 should be the first argument to ``lambdify``; for this 

246 case a single ``x`` can also be used: 

247 

248 >>> f = lambdify(x, x + 1) 

249 >>> f(1) 

250 2 

251 >>> f = lambdify([x], x + 1) 

252 >>> f(1) 

253 2 

254 

255 To call a function like ``f(x, y)`` then ``[x, y]`` will 

256 be the first argument of the ``lambdify``: 

257 

258 >>> f = lambdify([x, y], x + y) 

259 >>> f(1, 1) 

260 2 

261 

262 To call a function with a single 3-element tuple like 

263 ``f((x, y, z))`` then ``[(x, y, z)]`` will be the first 

264 argument of the ``lambdify``: 

265 

266 >>> f = lambdify([(x, y, z)], Eq(z**2, x**2 + y**2)) 

267 >>> f((3, 4, 5)) 

268 True 

269 

270 If two args will be passed and the first is a scalar but 

271 the second is a tuple with two arguments then the items 

272 in the list should match that structure: 

273 

274 >>> f = lambdify([x, (y, z)], x + y + z) 

275 >>> f(1, (2, 3)) 

276 6 

277 

278 expr : Expr 

279 An expression, list of expressions, or matrix to be evaluated. 

280 

281 Lists may be nested. 

282 If the expression is a list, the output will also be a list. 

283 

284 >>> f = lambdify(x, [x, [x + 1, x + 2]]) 

285 >>> f(1) 

286 [1, [2, 3]] 

287 

288 If it is a matrix, an array will be returned (for the NumPy module). 

289 

290 >>> from sympy import Matrix 

291 >>> f = lambdify(x, Matrix([x, x + 1])) 

292 >>> f(1) 

293 [[1] 

294 [2]] 

295 

296 Note that the argument order here (variables then expression) is used 

297 to emulate the Python ``lambda`` keyword. ``lambdify(x, expr)`` works 

298 (roughly) like ``lambda x: expr`` 

299 (see :ref:`lambdify-how-it-works` below). 

300 

301 modules : str, optional 

302 Specifies the numeric library to use. 

303 

304 If not specified, *modules* defaults to: 

305 

306 - ``["scipy", "numpy"]`` if SciPy is installed 

307 - ``["numpy"]`` if only NumPy is installed 

308 - ``["math", "mpmath", "sympy"]`` if neither is installed. 

309 

310 That is, SymPy functions are replaced as far as possible by 

311 either ``scipy`` or ``numpy`` functions if available, and Python's 

312 standard library ``math``, or ``mpmath`` functions otherwise. 

313 

314 *modules* can be one of the following types: 

315 

316 - The strings ``"math"``, ``"mpmath"``, ``"numpy"``, ``"numexpr"``, 

317 ``"scipy"``, ``"sympy"``, or ``"tensorflow"`` or ``"jax"``. This uses the 

318 corresponding printer and namespace mapping for that module. 

319 - A module (e.g., ``math``). This uses the global namespace of the 

320 module. If the module is one of the above known modules, it will 

321 also use the corresponding printer and namespace mapping 

322 (i.e., ``modules=numpy`` is equivalent to ``modules="numpy"``). 

323 - A dictionary that maps names of SymPy functions to arbitrary 

324 functions 

325 (e.g., ``{'sin': custom_sin}``). 

326 - A list that contains a mix of the arguments above, with higher 

327 priority given to entries appearing first 

328 (e.g., to use the NumPy module but override the ``sin`` function 

329 with a custom version, you can use 

330 ``[{'sin': custom_sin}, 'numpy']``). 

331 

332 dummify : bool, optional 

333 Whether or not the variables in the provided expression that are not 

334 valid Python identifiers are substituted with dummy symbols. 

335 

336 This allows for undefined functions like ``Function('f')(t)`` to be 

337 supplied as arguments. By default, the variables are only dummified 

338 if they are not valid Python identifiers. 

339 

340 Set ``dummify=True`` to replace all arguments with dummy symbols 

341 (if ``args`` is not a string) - for example, to ensure that the 

342 arguments do not redefine any built-in names. 

343 

344 cse : bool, or callable, optional 

345 Large expressions can be computed more efficiently when 

346 common subexpressions are identified and precomputed before 

347 being used multiple time. Finding the subexpressions will make 

348 creation of the 'lambdify' function slower, however. 

349 

350 When ``True``, ``sympy.simplify.cse`` is used, otherwise (the default) 

351 the user may pass a function matching the ``cse`` signature. 

352 

353 docstring_limit : int or None 

354 When lambdifying large expressions, a significant proportion of the time 

355 spent inside ``lambdify`` is spent producing a string representation of 

356 the expression for use in the automatically generated docstring of the 

357 returned function. For expressions containing hundreds or more nodes the 

358 resulting docstring often becomes so long and dense that it is difficult 

359 to read. To reduce the runtime of lambdify, the rendering of the full 

360 expression inside the docstring can be disabled. 

361 

362 When ``None``, the full expression is rendered in the docstring. When 

363 ``0`` or a negative ``int``, an ellipsis is rendering in the docstring 

364 instead of the expression. When a strictly positive ``int``, if the 

365 number of nodes in the expression exceeds ``docstring_limit`` an 

366 ellipsis is rendered in the docstring, otherwise a string representation 

367 of the expression is rendered as normal. The default is ``1000``. 

368 

369 Examples 

370 ======== 

371 

372 >>> from sympy.utilities.lambdify import implemented_function 

373 >>> from sympy import sqrt, sin, Matrix 

374 >>> from sympy import Function 

375 >>> from sympy.abc import w, x, y, z 

376 

377 >>> f = lambdify(x, x**2) 

378 >>> f(2) 

379 4 

380 >>> f = lambdify((x, y, z), [z, y, x]) 

381 >>> f(1,2,3) 

382 [3, 2, 1] 

383 >>> f = lambdify(x, sqrt(x)) 

384 >>> f(4) 

385 2.0 

386 >>> f = lambdify((x, y), sin(x*y)**2) 

387 >>> f(0, 5) 

388 0.0 

389 >>> row = lambdify((x, y), Matrix((x, x + y)).T, modules='sympy') 

390 >>> row(1, 2) 

391 Matrix([[1, 3]]) 

392 

393 ``lambdify`` can be used to translate SymPy expressions into mpmath 

394 functions. This may be preferable to using ``evalf`` (which uses mpmath on 

395 the backend) in some cases. 

396 

397 >>> f = lambdify(x, sin(x), 'mpmath') 

398 >>> f(1) 

399 0.8414709848078965 

400 

401 Tuple arguments are handled and the lambdified function should 

402 be called with the same type of arguments as were used to create 

403 the function: 

404 

405 >>> f = lambdify((x, (y, z)), x + y) 

406 >>> f(1, (2, 4)) 

407 3 

408 

409 The ``flatten`` function can be used to always work with flattened 

410 arguments: 

411 

412 >>> from sympy.utilities.iterables import flatten 

413 >>> args = w, (x, (y, z)) 

414 >>> vals = 1, (2, (3, 4)) 

415 >>> f = lambdify(flatten(args), w + x + y + z) 

416 >>> f(*flatten(vals)) 

417 10 

418 

419 Functions present in ``expr`` can also carry their own numerical 

420 implementations, in a callable attached to the ``_imp_`` attribute. This 

421 can be used with undefined functions using the ``implemented_function`` 

422 factory: 

423 

424 >>> f = implemented_function(Function('f'), lambda x: x+1) 

425 >>> func = lambdify(x, f(x)) 

426 >>> func(4) 

427 5 

428 

429 ``lambdify`` always prefers ``_imp_`` implementations to implementations 

430 in other namespaces, unless the ``use_imps`` input parameter is False. 

431 

432 Usage with Tensorflow: 

433 

434 >>> import tensorflow as tf 

435 >>> from sympy import Max, sin, lambdify 

436 >>> from sympy.abc import x 

437 

438 >>> f = Max(x, sin(x)) 

439 >>> func = lambdify(x, f, 'tensorflow') 

440 

441 After tensorflow v2, eager execution is enabled by default. 

442 If you want to get the compatible result across tensorflow v1 and v2 

443 as same as this tutorial, run this line. 

444 

445 >>> tf.compat.v1.enable_eager_execution() 

446 

447 If you have eager execution enabled, you can get the result out 

448 immediately as you can use numpy. 

449 

450 If you pass tensorflow objects, you may get an ``EagerTensor`` 

451 object instead of value. 

452 

453 >>> result = func(tf.constant(1.0)) 

454 >>> print(result) 

455 tf.Tensor(1.0, shape=(), dtype=float32) 

456 >>> print(result.__class__) 

457 <class 'tensorflow.python.framework.ops.EagerTensor'> 

458 

459 You can use ``.numpy()`` to get the numpy value of the tensor. 

460 

461 >>> result.numpy() 

462 1.0 

463 

464 >>> var = tf.Variable(2.0) 

465 >>> result = func(var) # also works for tf.Variable and tf.Placeholder 

466 >>> result.numpy() 

467 2.0 

468 

469 And it works with any shape array. 

470 

471 >>> tensor = tf.constant([[1.0, 2.0], [3.0, 4.0]]) 

472 >>> result = func(tensor) 

473 >>> result.numpy() 

474 [[1. 2.] 

475 [3. 4.]] 

476 

477 Notes 

478 ===== 

479 

480 - For functions involving large array calculations, numexpr can provide a 

481 significant speedup over numpy. Please note that the available functions 

482 for numexpr are more limited than numpy but can be expanded with 

483 ``implemented_function`` and user defined subclasses of Function. If 

484 specified, numexpr may be the only option in modules. The official list 

485 of numexpr functions can be found at: 

486 https://numexpr.readthedocs.io/projects/NumExpr3/en/latest/user_guide.html#supported-functions 

487 

488 - In the above examples, the generated functions can accept scalar 

489 values or numpy arrays as arguments. However, in some cases 

490 the generated function relies on the input being a numpy array: 

491 

492 >>> import numpy 

493 >>> from sympy import Piecewise 

494 >>> from sympy.testing.pytest import ignore_warnings 

495 >>> f = lambdify(x, Piecewise((x, x <= 1), (1/x, x > 1)), "numpy") 

496 

497 >>> with ignore_warnings(RuntimeWarning): 

498 ... f(numpy.array([-1, 0, 1, 2])) 

499 [-1. 0. 1. 0.5] 

500 

501 >>> f(0) 

502 Traceback (most recent call last): 

503 ... 

504 ZeroDivisionError: division by zero 

505 

506 In such cases, the input should be wrapped in a numpy array: 

507 

508 >>> with ignore_warnings(RuntimeWarning): 

509 ... float(f(numpy.array([0]))) 

510 0.0 

511 

512 Or if numpy functionality is not required another module can be used: 

513 

514 >>> f = lambdify(x, Piecewise((x, x <= 1), (1/x, x > 1)), "math") 

515 >>> f(0) 

516 0 

517 

518 .. _lambdify-how-it-works: 

519 

520 How it works 

521 ============ 

522 

523 When using this function, it helps a great deal to have an idea of what it 

524 is doing. At its core, lambdify is nothing more than a namespace 

525 translation, on top of a special printer that makes some corner cases work 

526 properly. 

527 

528 To understand lambdify, first we must properly understand how Python 

529 namespaces work. Say we had two files. One called ``sin_cos_sympy.py``, 

530 with 

531 

532 .. code:: python 

533 

534 # sin_cos_sympy.py 

535 

536 from sympy.functions.elementary.trigonometric import (cos, sin) 

537 

538 def sin_cos(x): 

539 return sin(x) + cos(x) 

540 

541 

542 and one called ``sin_cos_numpy.py`` with 

543 

544 .. code:: python 

545 

546 # sin_cos_numpy.py 

547 

548 from numpy import sin, cos 

549 

550 def sin_cos(x): 

551 return sin(x) + cos(x) 

552 

553 The two files define an identical function ``sin_cos``. However, in the 

554 first file, ``sin`` and ``cos`` are defined as the SymPy ``sin`` and 

555 ``cos``. In the second, they are defined as the NumPy versions. 

556 

557 If we were to import the first file and use the ``sin_cos`` function, we 

558 would get something like 

559 

560 >>> from sin_cos_sympy import sin_cos # doctest: +SKIP 

561 >>> sin_cos(1) # doctest: +SKIP 

562 cos(1) + sin(1) 

563 

564 On the other hand, if we imported ``sin_cos`` from the second file, we 

565 would get 

566 

567 >>> from sin_cos_numpy import sin_cos # doctest: +SKIP 

568 >>> sin_cos(1) # doctest: +SKIP 

569 1.38177329068 

570 

571 In the first case we got a symbolic output, because it used the symbolic 

572 ``sin`` and ``cos`` functions from SymPy. In the second, we got a numeric 

573 result, because ``sin_cos`` used the numeric ``sin`` and ``cos`` functions 

574 from NumPy. But notice that the versions of ``sin`` and ``cos`` that were 

575 used was not inherent to the ``sin_cos`` function definition. Both 

576 ``sin_cos`` definitions are exactly the same. Rather, it was based on the 

577 names defined at the module where the ``sin_cos`` function was defined. 

578 

579 The key point here is that when function in Python references a name that 

580 is not defined in the function, that name is looked up in the "global" 

581 namespace of the module where that function is defined. 

582 

583 Now, in Python, we can emulate this behavior without actually writing a 

584 file to disk using the ``exec`` function. ``exec`` takes a string 

585 containing a block of Python code, and a dictionary that should contain 

586 the global variables of the module. It then executes the code "in" that 

587 dictionary, as if it were the module globals. The following is equivalent 

588 to the ``sin_cos`` defined in ``sin_cos_sympy.py``: 

589 

590 >>> import sympy 

591 >>> module_dictionary = {'sin': sympy.sin, 'cos': sympy.cos} 

592 >>> exec(''' 

593 ... def sin_cos(x): 

594 ... return sin(x) + cos(x) 

595 ... ''', module_dictionary) 

596 >>> sin_cos = module_dictionary['sin_cos'] 

597 >>> sin_cos(1) 

598 cos(1) + sin(1) 

599 

600 and similarly with ``sin_cos_numpy``: 

601 

602 >>> import numpy 

603 >>> module_dictionary = {'sin': numpy.sin, 'cos': numpy.cos} 

604 >>> exec(''' 

605 ... def sin_cos(x): 

606 ... return sin(x) + cos(x) 

607 ... ''', module_dictionary) 

608 >>> sin_cos = module_dictionary['sin_cos'] 

609 >>> sin_cos(1) 

610 1.38177329068 

611 

612 So now we can get an idea of how ``lambdify`` works. The name "lambdify" 

613 comes from the fact that we can think of something like ``lambdify(x, 

614 sin(x) + cos(x), 'numpy')`` as ``lambda x: sin(x) + cos(x)``, where 

615 ``sin`` and ``cos`` come from the ``numpy`` namespace. This is also why 

616 the symbols argument is first in ``lambdify``, as opposed to most SymPy 

617 functions where it comes after the expression: to better mimic the 

618 ``lambda`` keyword. 

619 

620 ``lambdify`` takes the input expression (like ``sin(x) + cos(x)``) and 

621 

622 1. Converts it to a string 

623 2. Creates a module globals dictionary based on the modules that are 

624 passed in (by default, it uses the NumPy module) 

625 3. Creates the string ``"def func({vars}): return {expr}"``, where ``{vars}`` is the 

626 list of variables separated by commas, and ``{expr}`` is the string 

627 created in step 1., then ``exec``s that string with the module globals 

628 namespace and returns ``func``. 

629 

630 In fact, functions returned by ``lambdify`` support inspection. So you can 

631 see exactly how they are defined by using ``inspect.getsource``, or ``??`` if you 

632 are using IPython or the Jupyter notebook. 

633 

634 >>> f = lambdify(x, sin(x) + cos(x)) 

635 >>> import inspect 

636 >>> print(inspect.getsource(f)) 

637 def _lambdifygenerated(x): 

638 return sin(x) + cos(x) 

639 

640 This shows us the source code of the function, but not the namespace it 

641 was defined in. We can inspect that by looking at the ``__globals__`` 

642 attribute of ``f``: 

643 

644 >>> f.__globals__['sin'] 

645 <ufunc 'sin'> 

646 >>> f.__globals__['cos'] 

647 <ufunc 'cos'> 

648 >>> f.__globals__['sin'] is numpy.sin 

649 True 

650 

651 This shows us that ``sin`` and ``cos`` in the namespace of ``f`` will be 

652 ``numpy.sin`` and ``numpy.cos``. 

653 

654 Note that there are some convenience layers in each of these steps, but at 

655 the core, this is how ``lambdify`` works. Step 1 is done using the 

656 ``LambdaPrinter`` printers defined in the printing module (see 

657 :mod:`sympy.printing.lambdarepr`). This allows different SymPy expressions 

658 to define how they should be converted to a string for different modules. 

659 You can change which printer ``lambdify`` uses by passing a custom printer 

660 in to the ``printer`` argument. 

661 

662 Step 2 is augmented by certain translations. There are default 

663 translations for each module, but you can provide your own by passing a 

664 list to the ``modules`` argument. For instance, 

665 

666 >>> def mysin(x): 

667 ... print('taking the sin of', x) 

668 ... return numpy.sin(x) 

669 ... 

670 >>> f = lambdify(x, sin(x), [{'sin': mysin}, 'numpy']) 

671 >>> f(1) 

672 taking the sin of 1 

673 0.8414709848078965 

674 

675 The globals dictionary is generated from the list by merging the 

676 dictionary ``{'sin': mysin}`` and the module dictionary for NumPy. The 

677 merging is done so that earlier items take precedence, which is why 

678 ``mysin`` is used above instead of ``numpy.sin``. 

679 

680 If you want to modify the way ``lambdify`` works for a given function, it 

681 is usually easiest to do so by modifying the globals dictionary as such. 

682 In more complicated cases, it may be necessary to create and pass in a 

683 custom printer. 

684 

685 Finally, step 3 is augmented with certain convenience operations, such as 

686 the addition of a docstring. 

687 

688 Understanding how ``lambdify`` works can make it easier to avoid certain 

689 gotchas when using it. For instance, a common mistake is to create a 

690 lambdified function for one module (say, NumPy), and pass it objects from 

691 another (say, a SymPy expression). 

692 

693 For instance, say we create 

694 

695 >>> from sympy.abc import x 

696 >>> f = lambdify(x, x + 1, 'numpy') 

697 

698 Now if we pass in a NumPy array, we get that array plus 1 

699 

700 >>> import numpy 

701 >>> a = numpy.array([1, 2]) 

702 >>> f(a) 

703 [2 3] 

704 

705 But what happens if you make the mistake of passing in a SymPy expression 

706 instead of a NumPy array: 

707 

708 >>> f(x + 1) 

709 x + 2 

710 

711 This worked, but it was only by accident. Now take a different lambdified 

712 function: 

713 

714 >>> from sympy import sin 

715 >>> g = lambdify(x, x + sin(x), 'numpy') 

716 

717 This works as expected on NumPy arrays: 

718 

719 >>> g(a) 

720 [1.84147098 2.90929743] 

721 

722 But if we try to pass in a SymPy expression, it fails 

723 

724 >>> try: 

725 ... g(x + 1) 

726 ... # NumPy release after 1.17 raises TypeError instead of 

727 ... # AttributeError 

728 ... except (AttributeError, TypeError): 

729 ... raise AttributeError() # doctest: +IGNORE_EXCEPTION_DETAIL 

730 Traceback (most recent call last): 

731 ... 

732 AttributeError: 

733 

734 Now, let's look at what happened. The reason this fails is that ``g`` 

735 calls ``numpy.sin`` on the input expression, and ``numpy.sin`` does not 

736 know how to operate on a SymPy object. **As a general rule, NumPy 

737 functions do not know how to operate on SymPy expressions, and SymPy 

738 functions do not know how to operate on NumPy arrays. This is why lambdify 

739 exists: to provide a bridge between SymPy and NumPy.** 

740 

741 However, why is it that ``f`` did work? That's because ``f`` does not call 

742 any functions, it only adds 1. So the resulting function that is created, 

743 ``def _lambdifygenerated(x): return x + 1`` does not depend on the globals 

744 namespace it is defined in. Thus it works, but only by accident. A future 

745 version of ``lambdify`` may remove this behavior. 

746 

747 Be aware that certain implementation details described here may change in 

748 future versions of SymPy. The API of passing in custom modules and 

749 printers will not change, but the details of how a lambda function is 

750 created may change. However, the basic idea will remain the same, and 

751 understanding it will be helpful to understanding the behavior of 

752 lambdify. 

753 

754 **In general: you should create lambdified functions for one module (say, 

755 NumPy), and only pass it input types that are compatible with that module 

756 (say, NumPy arrays).** Remember that by default, if the ``module`` 

757 argument is not provided, ``lambdify`` creates functions using the NumPy 

758 and SciPy namespaces. 

759 """ 

760 from sympy.core.symbol import Symbol 

761 from sympy.core.expr import Expr 

762 

763 # If the user hasn't specified any modules, use what is available. 

764 if modules is None: 

765 try: 

766 _import("scipy") 

767 except ImportError: 

768 try: 

769 _import("numpy") 

770 except ImportError: 

771 # Use either numpy (if available) or python.math where possible. 

772 # XXX: This leads to different behaviour on different systems and 

773 # might be the reason for irreproducible errors. 

774 modules = ["math", "mpmath", "sympy"] 

775 else: 

776 modules = ["numpy"] 

777 else: 

778 modules = ["numpy", "scipy"] 

779 

780 # Get the needed namespaces. 

781 namespaces = [] 

782 # First find any function implementations 

783 if use_imps: 

784 namespaces.append(_imp_namespace(expr)) 

785 # Check for dict before iterating 

786 if isinstance(modules, (dict, str)) or not hasattr(modules, '__iter__'): 

787 namespaces.append(modules) 

788 else: 

789 # consistency check 

790 if _module_present('numexpr', modules) and len(modules) > 1: 

791 raise TypeError("numexpr must be the only item in 'modules'") 

792 namespaces += list(modules) 

793 # fill namespace with first having highest priority 

794 namespace = {} 

795 for m in namespaces[::-1]: 

796 buf = _get_namespace(m) 

797 namespace.update(buf) 

798 

799 if hasattr(expr, "atoms"): 

800 #Try if you can extract symbols from the expression. 

801 #Move on if expr.atoms in not implemented. 

802 syms = expr.atoms(Symbol) 

803 for term in syms: 

804 namespace.update({str(term): term}) 

805 

806 if printer is None: 

807 if _module_present('mpmath', namespaces): 

808 from sympy.printing.pycode import MpmathPrinter as Printer # type: ignore 

809 elif _module_present('scipy', namespaces): 

810 from sympy.printing.numpy import SciPyPrinter as Printer # type: ignore 

811 elif _module_present('numpy', namespaces): 

812 from sympy.printing.numpy import NumPyPrinter as Printer # type: ignore 

813 elif _module_present('cupy', namespaces): 

814 from sympy.printing.numpy import CuPyPrinter as Printer # type: ignore 

815 elif _module_present('jax', namespaces): 

816 from sympy.printing.numpy import JaxPrinter as Printer # type: ignore 

817 elif _module_present('numexpr', namespaces): 

818 from sympy.printing.lambdarepr import NumExprPrinter as Printer # type: ignore 

819 elif _module_present('tensorflow', namespaces): 

820 from sympy.printing.tensorflow import TensorflowPrinter as Printer # type: ignore 

821 elif _module_present('sympy', namespaces): 

822 from sympy.printing.pycode import SymPyPrinter as Printer # type: ignore 

823 else: 

824 from sympy.printing.pycode import PythonCodePrinter as Printer # type: ignore 

825 user_functions = {} 

826 for m in namespaces[::-1]: 

827 if isinstance(m, dict): 

828 for k in m: 

829 user_functions[k] = k 

830 printer = Printer({'fully_qualified_modules': False, 'inline': True, 

831 'allow_unknown_functions': True, 

832 'user_functions': user_functions}) 

833 

834 if isinstance(args, set): 

835 sympy_deprecation_warning( 

836 """ 

837Passing the function arguments to lambdify() as a set is deprecated. This 

838leads to unpredictable results since sets are unordered. Instead, use a list 

839or tuple for the function arguments. 

840 """, 

841 deprecated_since_version="1.6.3", 

842 active_deprecations_target="deprecated-lambdify-arguments-set", 

843 ) 

844 

845 # Get the names of the args, for creating a docstring 

846 iterable_args = (args,) if isinstance(args, Expr) else args 

847 names = [] 

848 

849 # Grab the callers frame, for getting the names by inspection (if needed) 

850 callers_local_vars = inspect.currentframe().f_back.f_locals.items() # type: ignore 

851 for n, var in enumerate(iterable_args): 

852 if hasattr(var, 'name'): 

853 names.append(var.name) 

854 else: 

855 # It's an iterable. Try to get name by inspection of calling frame. 

856 name_list = [var_name for var_name, var_val in callers_local_vars 

857 if var_val is var] 

858 if len(name_list) == 1: 

859 names.append(name_list[0]) 

860 else: 

861 # Cannot infer name with certainty. arg_# will have to do. 

862 names.append('arg_' + str(n)) 

863 

864 # Create the function definition code and execute it 

865 funcname = '_lambdifygenerated' 

866 if _module_present('tensorflow', namespaces): 

867 funcprinter = _TensorflowEvaluatorPrinter(printer, dummify) 

868 else: 

869 funcprinter = _EvaluatorPrinter(printer, dummify) 

870 

871 if cse == True: 

872 from sympy.simplify.cse_main import cse as _cse 

873 cses, _expr = _cse(expr, list=False) 

874 elif callable(cse): 

875 cses, _expr = cse(expr) 

876 else: 

877 cses, _expr = (), expr 

878 funcstr = funcprinter.doprint(funcname, iterable_args, _expr, cses=cses) 

879 

880 # Collect the module imports from the code printers. 

881 imp_mod_lines = [] 

882 for mod, keys in (getattr(printer, 'module_imports', None) or {}).items(): 

883 for k in keys: 

884 if k not in namespace: 

885 ln = "from %s import %s" % (mod, k) 

886 try: 

887 exec(ln, {}, namespace) 

888 except ImportError: 

889 # Tensorflow 2.0 has issues with importing a specific 

890 # function from its submodule. 

891 # https://github.com/tensorflow/tensorflow/issues/33022 

892 ln = "%s = %s.%s" % (k, mod, k) 

893 exec(ln, {}, namespace) 

894 imp_mod_lines.append(ln) 

895 

896 # Provide lambda expression with builtins, and compatible implementation of range 

897 namespace.update({'builtins':builtins, 'range':range}) 

898 

899 funclocals = {} 

900 global _lambdify_generated_counter 

901 filename = '<lambdifygenerated-%s>' % _lambdify_generated_counter 

902 _lambdify_generated_counter += 1 

903 c = compile(funcstr, filename, 'exec') 

904 exec(c, namespace, funclocals) 

905 # mtime has to be None or else linecache.checkcache will remove it 

906 linecache.cache[filename] = (len(funcstr), None, funcstr.splitlines(True), filename) # type: ignore 

907 

908 func = funclocals[funcname] 

909 

910 # Apply the docstring 

911 sig = "func({})".format(", ".join(str(i) for i in names)) 

912 sig = textwrap.fill(sig, subsequent_indent=' '*8) 

913 if _too_large_for_docstring(expr, docstring_limit): 

914 expr_str = 'EXPRESSION REDACTED DUE TO LENGTH' 

915 src_str = 'SOURCE CODE REDACTED DUE TO LENGTH' 

916 else: 

917 expr_str = str(expr) 

918 if len(expr_str) > 78: 

919 expr_str = textwrap.wrap(expr_str, 75)[0] + '...' 

920 src_str = funcstr 

921 func.__doc__ = ( 

922 "Created with lambdify. Signature:\n\n" 

923 "{sig}\n\n" 

924 "Expression:\n\n" 

925 "{expr}\n\n" 

926 "Source code:\n\n" 

927 "{src}\n\n" 

928 "Imported modules:\n\n" 

929 "{imp_mods}" 

930 ).format(sig=sig, expr=expr_str, src=src_str, imp_mods='\n'.join(imp_mod_lines)) 

931 return func 

932 

933def _module_present(modname, modlist): 

934 if modname in modlist: 

935 return True 

936 for m in modlist: 

937 if hasattr(m, '__name__') and m.__name__ == modname: 

938 return True 

939 return False 

940 

941def _get_namespace(m): 

942 """ 

943 This is used by _lambdify to parse its arguments. 

944 """ 

945 if isinstance(m, str): 

946 _import(m) 

947 return MODULES[m][0] 

948 elif isinstance(m, dict): 

949 return m 

950 elif hasattr(m, "__dict__"): 

951 return m.__dict__ 

952 else: 

953 raise TypeError("Argument must be either a string, dict or module but it is: %s" % m) 

954 

955 

956def _recursive_to_string(doprint, arg): 

957 """Functions in lambdify accept both SymPy types and non-SymPy types such as python 

958 lists and tuples. This method ensures that we only call the doprint method of the 

959 printer with SymPy types (so that the printer safely can use SymPy-methods).""" 

960 from sympy.matrices.common import MatrixOperations 

961 from sympy.core.basic import Basic 

962 

963 if isinstance(arg, (Basic, MatrixOperations)): 

964 return doprint(arg) 

965 elif iterable(arg): 

966 if isinstance(arg, list): 

967 left, right = "[", "]" 

968 elif isinstance(arg, tuple): 

969 left, right = "(", ",)" 

970 else: 

971 raise NotImplementedError("unhandled type: %s, %s" % (type(arg), arg)) 

972 return left +', '.join(_recursive_to_string(doprint, e) for e in arg) + right 

973 elif isinstance(arg, str): 

974 return arg 

975 else: 

976 return doprint(arg) 

977 

978 

979def lambdastr(args, expr, printer=None, dummify=None): 

980 """ 

981 Returns a string that can be evaluated to a lambda function. 

982 

983 Examples 

984 ======== 

985 

986 >>> from sympy.abc import x, y, z 

987 >>> from sympy.utilities.lambdify import lambdastr 

988 >>> lambdastr(x, x**2) 

989 'lambda x: (x**2)' 

990 >>> lambdastr((x,y,z), [z,y,x]) 

991 'lambda x,y,z: ([z, y, x])' 

992 

993 Although tuples may not appear as arguments to lambda in Python 3, 

994 lambdastr will create a lambda function that will unpack the original 

995 arguments so that nested arguments can be handled: 

996 

997 >>> lambdastr((x, (y, z)), x + y) 

998 'lambda _0,_1: (lambda x,y,z: (x + y))(_0,_1[0],_1[1])' 

999 """ 

1000 # Transforming everything to strings. 

1001 from sympy.matrices import DeferredVector 

1002 from sympy.core.basic import Basic 

1003 from sympy.core.function import (Derivative, Function) 

1004 from sympy.core.symbol import (Dummy, Symbol) 

1005 from sympy.core.sympify import sympify 

1006 

1007 if printer is not None: 

1008 if inspect.isfunction(printer): 

1009 lambdarepr = printer 

1010 else: 

1011 if inspect.isclass(printer): 

1012 lambdarepr = lambda expr: printer().doprint(expr) 

1013 else: 

1014 lambdarepr = lambda expr: printer.doprint(expr) 

1015 else: 

1016 #XXX: This has to be done here because of circular imports 

1017 from sympy.printing.lambdarepr import lambdarepr 

1018 

1019 def sub_args(args, dummies_dict): 

1020 if isinstance(args, str): 

1021 return args 

1022 elif isinstance(args, DeferredVector): 

1023 return str(args) 

1024 elif iterable(args): 

1025 dummies = flatten([sub_args(a, dummies_dict) for a in args]) 

1026 return ",".join(str(a) for a in dummies) 

1027 else: 

1028 # replace these with Dummy symbols 

1029 if isinstance(args, (Function, Symbol, Derivative)): 

1030 dummies = Dummy() 

1031 dummies_dict.update({args : dummies}) 

1032 return str(dummies) 

1033 else: 

1034 return str(args) 

1035 

1036 def sub_expr(expr, dummies_dict): 

1037 expr = sympify(expr) 

1038 # dict/tuple are sympified to Basic 

1039 if isinstance(expr, Basic): 

1040 expr = expr.xreplace(dummies_dict) 

1041 # list is not sympified to Basic 

1042 elif isinstance(expr, list): 

1043 expr = [sub_expr(a, dummies_dict) for a in expr] 

1044 return expr 

1045 

1046 # Transform args 

1047 def isiter(l): 

1048 return iterable(l, exclude=(str, DeferredVector, NotIterable)) 

1049 

1050 def flat_indexes(iterable): 

1051 n = 0 

1052 

1053 for el in iterable: 

1054 if isiter(el): 

1055 for ndeep in flat_indexes(el): 

1056 yield (n,) + ndeep 

1057 else: 

1058 yield (n,) 

1059 

1060 n += 1 

1061 

1062 if dummify is None: 

1063 dummify = any(isinstance(a, Basic) and 

1064 a.atoms(Function, Derivative) for a in ( 

1065 args if isiter(args) else [args])) 

1066 

1067 if isiter(args) and any(isiter(i) for i in args): 

1068 dum_args = [str(Dummy(str(i))) for i in range(len(args))] 

1069 

1070 indexed_args = ','.join([ 

1071 dum_args[ind[0]] + ''.join(["[%s]" % k for k in ind[1:]]) 

1072 for ind in flat_indexes(args)]) 

1073 

1074 lstr = lambdastr(flatten(args), expr, printer=printer, dummify=dummify) 

1075 

1076 return 'lambda %s: (%s)(%s)' % (','.join(dum_args), lstr, indexed_args) 

1077 

1078 dummies_dict = {} 

1079 if dummify: 

1080 args = sub_args(args, dummies_dict) 

1081 else: 

1082 if isinstance(args, str): 

1083 pass 

1084 elif iterable(args, exclude=DeferredVector): 

1085 args = ",".join(str(a) for a in args) 

1086 

1087 # Transform expr 

1088 if dummify: 

1089 if isinstance(expr, str): 

1090 pass 

1091 else: 

1092 expr = sub_expr(expr, dummies_dict) 

1093 expr = _recursive_to_string(lambdarepr, expr) 

1094 return "lambda %s: (%s)" % (args, expr) 

1095 

1096class _EvaluatorPrinter: 

1097 def __init__(self, printer=None, dummify=False): 

1098 self._dummify = dummify 

1099 

1100 #XXX: This has to be done here because of circular imports 

1101 from sympy.printing.lambdarepr import LambdaPrinter 

1102 

1103 if printer is None: 

1104 printer = LambdaPrinter() 

1105 

1106 if inspect.isfunction(printer): 

1107 self._exprrepr = printer 

1108 else: 

1109 if inspect.isclass(printer): 

1110 printer = printer() 

1111 

1112 self._exprrepr = printer.doprint 

1113 

1114 #if hasattr(printer, '_print_Symbol'): 

1115 # symbolrepr = printer._print_Symbol 

1116 

1117 #if hasattr(printer, '_print_Dummy'): 

1118 # dummyrepr = printer._print_Dummy 

1119 

1120 # Used to print the generated function arguments in a standard way 

1121 self._argrepr = LambdaPrinter().doprint 

1122 

1123 def doprint(self, funcname, args, expr, *, cses=()): 

1124 """ 

1125 Returns the function definition code as a string. 

1126 """ 

1127 from sympy.core.symbol import Dummy 

1128 

1129 funcbody = [] 

1130 

1131 if not iterable(args): 

1132 args = [args] 

1133 

1134 if cses: 

1135 subvars, subexprs = zip(*cses) 

1136 exprs = [expr] + list(subexprs) 

1137 argstrs, exprs = self._preprocess(args, exprs) 

1138 expr, subexprs = exprs[0], exprs[1:] 

1139 cses = zip(subvars, subexprs) 

1140 else: 

1141 argstrs, expr = self._preprocess(args, expr) 

1142 

1143 # Generate argument unpacking and final argument list 

1144 funcargs = [] 

1145 unpackings = [] 

1146 

1147 for argstr in argstrs: 

1148 if iterable(argstr): 

1149 funcargs.append(self._argrepr(Dummy())) 

1150 unpackings.extend(self._print_unpacking(argstr, funcargs[-1])) 

1151 else: 

1152 funcargs.append(argstr) 

1153 

1154 funcsig = 'def {}({}):'.format(funcname, ', '.join(funcargs)) 

1155 

1156 # Wrap input arguments before unpacking 

1157 funcbody.extend(self._print_funcargwrapping(funcargs)) 

1158 

1159 funcbody.extend(unpackings) 

1160 

1161 for s, e in cses: 

1162 if e is None: 

1163 funcbody.append('del {}'.format(s)) 

1164 else: 

1165 funcbody.append('{} = {}'.format(s, self._exprrepr(e))) 

1166 

1167 str_expr = _recursive_to_string(self._exprrepr, expr) 

1168 

1169 if '\n' in str_expr: 

1170 str_expr = '({})'.format(str_expr) 

1171 funcbody.append('return {}'.format(str_expr)) 

1172 

1173 funclines = [funcsig] 

1174 funclines.extend([' ' + line for line in funcbody]) 

1175 

1176 return '\n'.join(funclines) + '\n' 

1177 

1178 @classmethod 

1179 def _is_safe_ident(cls, ident): 

1180 return isinstance(ident, str) and ident.isidentifier() \ 

1181 and not keyword.iskeyword(ident) 

1182 

1183 def _preprocess(self, args, expr): 

1184 """Preprocess args, expr to replace arguments that do not map 

1185 to valid Python identifiers. 

1186 

1187 Returns string form of args, and updated expr. 

1188 """ 

1189 from sympy.core.basic import Basic 

1190 from sympy.core.sorting import ordered 

1191 from sympy.core.function import (Derivative, Function) 

1192 from sympy.core.symbol import Dummy, uniquely_named_symbol 

1193 from sympy.matrices import DeferredVector 

1194 from sympy.core.expr import Expr 

1195 

1196 # Args of type Dummy can cause name collisions with args 

1197 # of type Symbol. Force dummify of everything in this 

1198 # situation. 

1199 dummify = self._dummify or any( 

1200 isinstance(arg, Dummy) for arg in flatten(args)) 

1201 

1202 argstrs = [None]*len(args) 

1203 for arg, i in reversed(list(ordered(zip(args, range(len(args)))))): 

1204 if iterable(arg): 

1205 s, expr = self._preprocess(arg, expr) 

1206 elif isinstance(arg, DeferredVector): 

1207 s = str(arg) 

1208 elif isinstance(arg, Basic) and arg.is_symbol: 

1209 s = self._argrepr(arg) 

1210 if dummify or not self._is_safe_ident(s): 

1211 dummy = Dummy() 

1212 if isinstance(expr, Expr): 

1213 dummy = uniquely_named_symbol( 

1214 dummy.name, expr, modify=lambda s: '_' + s) 

1215 s = self._argrepr(dummy) 

1216 expr = self._subexpr(expr, {arg: dummy}) 

1217 elif dummify or isinstance(arg, (Function, Derivative)): 

1218 dummy = Dummy() 

1219 s = self._argrepr(dummy) 

1220 expr = self._subexpr(expr, {arg: dummy}) 

1221 else: 

1222 s = str(arg) 

1223 argstrs[i] = s 

1224 return argstrs, expr 

1225 

1226 def _subexpr(self, expr, dummies_dict): 

1227 from sympy.matrices import DeferredVector 

1228 from sympy.core.sympify import sympify 

1229 

1230 expr = sympify(expr) 

1231 xreplace = getattr(expr, 'xreplace', None) 

1232 if xreplace is not None: 

1233 expr = xreplace(dummies_dict) 

1234 else: 

1235 if isinstance(expr, DeferredVector): 

1236 pass 

1237 elif isinstance(expr, dict): 

1238 k = [self._subexpr(sympify(a), dummies_dict) for a in expr.keys()] 

1239 v = [self._subexpr(sympify(a), dummies_dict) for a in expr.values()] 

1240 expr = dict(zip(k, v)) 

1241 elif isinstance(expr, tuple): 

1242 expr = tuple(self._subexpr(sympify(a), dummies_dict) for a in expr) 

1243 elif isinstance(expr, list): 

1244 expr = [self._subexpr(sympify(a), dummies_dict) for a in expr] 

1245 return expr 

1246 

1247 def _print_funcargwrapping(self, args): 

1248 """Generate argument wrapping code. 

1249 

1250 args is the argument list of the generated function (strings). 

1251 

1252 Return value is a list of lines of code that will be inserted at 

1253 the beginning of the function definition. 

1254 """ 

1255 return [] 

1256 

1257 def _print_unpacking(self, unpackto, arg): 

1258 """Generate argument unpacking code. 

1259 

1260 arg is the function argument to be unpacked (a string), and 

1261 unpackto is a list or nested lists of the variable names (strings) to 

1262 unpack to. 

1263 """ 

1264 def unpack_lhs(lvalues): 

1265 return '[{}]'.format(', '.join( 

1266 unpack_lhs(val) if iterable(val) else val for val in lvalues)) 

1267 

1268 return ['{} = {}'.format(unpack_lhs(unpackto), arg)] 

1269 

1270class _TensorflowEvaluatorPrinter(_EvaluatorPrinter): 

1271 def _print_unpacking(self, lvalues, rvalue): 

1272 """Generate argument unpacking code. 

1273 

1274 This method is used when the input value is not interable, 

1275 but can be indexed (see issue #14655). 

1276 """ 

1277 

1278 def flat_indexes(elems): 

1279 n = 0 

1280 

1281 for el in elems: 

1282 if iterable(el): 

1283 for ndeep in flat_indexes(el): 

1284 yield (n,) + ndeep 

1285 else: 

1286 yield (n,) 

1287 

1288 n += 1 

1289 

1290 indexed = ', '.join('{}[{}]'.format(rvalue, ']['.join(map(str, ind))) 

1291 for ind in flat_indexes(lvalues)) 

1292 

1293 return ['[{}] = [{}]'.format(', '.join(flatten(lvalues)), indexed)] 

1294 

1295def _imp_namespace(expr, namespace=None): 

1296 """ Return namespace dict with function implementations 

1297 

1298 We need to search for functions in anything that can be thrown at 

1299 us - that is - anything that could be passed as ``expr``. Examples 

1300 include SymPy expressions, as well as tuples, lists and dicts that may 

1301 contain SymPy expressions. 

1302 

1303 Parameters 

1304 ---------- 

1305 expr : object 

1306 Something passed to lambdify, that will generate valid code from 

1307 ``str(expr)``. 

1308 namespace : None or mapping 

1309 Namespace to fill. None results in new empty dict 

1310 

1311 Returns 

1312 ------- 

1313 namespace : dict 

1314 dict with keys of implemented function names within ``expr`` and 

1315 corresponding values being the numerical implementation of 

1316 function 

1317 

1318 Examples 

1319 ======== 

1320 

1321 >>> from sympy.abc import x 

1322 >>> from sympy.utilities.lambdify import implemented_function, _imp_namespace 

1323 >>> from sympy import Function 

1324 >>> f = implemented_function(Function('f'), lambda x: x+1) 

1325 >>> g = implemented_function(Function('g'), lambda x: x*10) 

1326 >>> namespace = _imp_namespace(f(g(x))) 

1327 >>> sorted(namespace.keys()) 

1328 ['f', 'g'] 

1329 """ 

1330 # Delayed import to avoid circular imports 

1331 from sympy.core.function import FunctionClass 

1332 if namespace is None: 

1333 namespace = {} 

1334 # tuples, lists, dicts are valid expressions 

1335 if is_sequence(expr): 

1336 for arg in expr: 

1337 _imp_namespace(arg, namespace) 

1338 return namespace 

1339 elif isinstance(expr, dict): 

1340 for key, val in expr.items(): 

1341 # functions can be in dictionary keys 

1342 _imp_namespace(key, namespace) 

1343 _imp_namespace(val, namespace) 

1344 return namespace 

1345 # SymPy expressions may be Functions themselves 

1346 func = getattr(expr, 'func', None) 

1347 if isinstance(func, FunctionClass): 

1348 imp = getattr(func, '_imp_', None) 

1349 if imp is not None: 

1350 name = expr.func.__name__ 

1351 if name in namespace and namespace[name] != imp: 

1352 raise ValueError('We found more than one ' 

1353 'implementation with name ' 

1354 '"%s"' % name) 

1355 namespace[name] = imp 

1356 # and / or they may take Functions as arguments 

1357 if hasattr(expr, 'args'): 

1358 for arg in expr.args: 

1359 _imp_namespace(arg, namespace) 

1360 return namespace 

1361 

1362 

1363def implemented_function(symfunc, implementation): 

1364 """ Add numerical ``implementation`` to function ``symfunc``. 

1365 

1366 ``symfunc`` can be an ``UndefinedFunction`` instance, or a name string. 

1367 In the latter case we create an ``UndefinedFunction`` instance with that 

1368 name. 

1369 

1370 Be aware that this is a quick workaround, not a general method to create 

1371 special symbolic functions. If you want to create a symbolic function to be 

1372 used by all the machinery of SymPy you should subclass the ``Function`` 

1373 class. 

1374 

1375 Parameters 

1376 ---------- 

1377 symfunc : ``str`` or ``UndefinedFunction`` instance 

1378 If ``str``, then create new ``UndefinedFunction`` with this as 

1379 name. If ``symfunc`` is an Undefined function, create a new function 

1380 with the same name and the implemented function attached. 

1381 implementation : callable 

1382 numerical implementation to be called by ``evalf()`` or ``lambdify`` 

1383 

1384 Returns 

1385 ------- 

1386 afunc : sympy.FunctionClass instance 

1387 function with attached implementation 

1388 

1389 Examples 

1390 ======== 

1391 

1392 >>> from sympy.abc import x 

1393 >>> from sympy.utilities.lambdify import implemented_function 

1394 >>> from sympy import lambdify 

1395 >>> f = implemented_function('f', lambda x: x+1) 

1396 >>> lam_f = lambdify(x, f(x)) 

1397 >>> lam_f(4) 

1398 5 

1399 """ 

1400 # Delayed import to avoid circular imports 

1401 from sympy.core.function import UndefinedFunction 

1402 # if name, create function to hold implementation 

1403 kwargs = {} 

1404 if isinstance(symfunc, UndefinedFunction): 

1405 kwargs = symfunc._kwargs 

1406 symfunc = symfunc.__name__ 

1407 if isinstance(symfunc, str): 

1408 # Keyword arguments to UndefinedFunction are added as attributes to 

1409 # the created class. 

1410 symfunc = UndefinedFunction( 

1411 symfunc, _imp_=staticmethod(implementation), **kwargs) 

1412 elif not isinstance(symfunc, UndefinedFunction): 

1413 raise ValueError(filldedent(''' 

1414 symfunc should be either a string or 

1415 an UndefinedFunction instance.''')) 

1416 return symfunc 

1417 

1418 

1419def _too_large_for_docstring(expr, limit): 

1420 """Decide whether an ``Expr`` is too large to be fully rendered in a 

1421 ``lambdify`` docstring. 

1422 

1423 This is a fast alternative to ``count_ops``, which can become prohibitively 

1424 slow for large expressions, because in this instance we only care whether 

1425 ``limit`` is exceeded rather than counting the exact number of nodes in the 

1426 expression. 

1427 

1428 Parameters 

1429 ========== 

1430 expr : ``Expr``, (nested) ``list`` of ``Expr``, or ``Matrix`` 

1431 The same objects that can be passed to the ``expr`` argument of 

1432 ``lambdify``. 

1433 limit : ``int`` or ``None`` 

1434 The threshold above which an expression contains too many nodes to be 

1435 usefully rendered in the docstring. If ``None`` then there is no limit. 

1436 

1437 Returns 

1438 ======= 

1439 bool 

1440 ``True`` if the number of nodes in the expression exceeds the limit, 

1441 ``False`` otherwise. 

1442 

1443 Examples 

1444 ======== 

1445 

1446 >>> from sympy.abc import x, y, z 

1447 >>> from sympy.utilities.lambdify import _too_large_for_docstring 

1448 >>> expr = x 

1449 >>> _too_large_for_docstring(expr, None) 

1450 False 

1451 >>> _too_large_for_docstring(expr, 100) 

1452 False 

1453 >>> _too_large_for_docstring(expr, 1) 

1454 False 

1455 >>> _too_large_for_docstring(expr, 0) 

1456 True 

1457 >>> _too_large_for_docstring(expr, -1) 

1458 True 

1459 

1460 Does this split it? 

1461 

1462 >>> expr = [x, y, z] 

1463 >>> _too_large_for_docstring(expr, None) 

1464 False 

1465 >>> _too_large_for_docstring(expr, 100) 

1466 False 

1467 >>> _too_large_for_docstring(expr, 1) 

1468 True 

1469 >>> _too_large_for_docstring(expr, 0) 

1470 True 

1471 >>> _too_large_for_docstring(expr, -1) 

1472 True 

1473 

1474 >>> expr = [x, [y], z, [[x+y], [x*y*z, [x+y+z]]]] 

1475 >>> _too_large_for_docstring(expr, None) 

1476 False 

1477 >>> _too_large_for_docstring(expr, 100) 

1478 False 

1479 >>> _too_large_for_docstring(expr, 1) 

1480 True 

1481 >>> _too_large_for_docstring(expr, 0) 

1482 True 

1483 >>> _too_large_for_docstring(expr, -1) 

1484 True 

1485 

1486 >>> expr = ((x + y + z)**5).expand() 

1487 >>> _too_large_for_docstring(expr, None) 

1488 False 

1489 >>> _too_large_for_docstring(expr, 100) 

1490 True 

1491 >>> _too_large_for_docstring(expr, 1) 

1492 True 

1493 >>> _too_large_for_docstring(expr, 0) 

1494 True 

1495 >>> _too_large_for_docstring(expr, -1) 

1496 True 

1497 

1498 >>> from sympy import Matrix 

1499 >>> expr = Matrix([[(x + y + z), ((x + y + z)**2).expand(), 

1500 ... ((x + y + z)**3).expand(), ((x + y + z)**4).expand()]]) 

1501 >>> _too_large_for_docstring(expr, None) 

1502 False 

1503 >>> _too_large_for_docstring(expr, 1000) 

1504 False 

1505 >>> _too_large_for_docstring(expr, 100) 

1506 True 

1507 >>> _too_large_for_docstring(expr, 1) 

1508 True 

1509 >>> _too_large_for_docstring(expr, 0) 

1510 True 

1511 >>> _too_large_for_docstring(expr, -1) 

1512 True 

1513 

1514 """ 

1515 # Must be imported here to avoid a circular import error 

1516 from sympy.core.traversal import postorder_traversal 

1517 

1518 if limit is None: 

1519 return False 

1520 

1521 i = 0 

1522 for _ in postorder_traversal(expr): 

1523 i += 1 

1524 if i > limit: 

1525 return True 

1526 return False