Coverage for /usr/lib/python3/dist-packages/sympy/printing/lambdarepr.py: 33%
117 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from .pycode import (
2 PythonCodePrinter,
3 MpmathPrinter,
4)
5from .numpy import NumPyPrinter # NumPyPrinter is imported for backward compatibility
6from sympy.core.sorting import default_sort_key
9__all__ = [
10 'PythonCodePrinter',
11 'MpmathPrinter', # MpmathPrinter is published for backward compatibility
12 'NumPyPrinter',
13 'LambdaPrinter',
14 'NumPyPrinter',
15 'IntervalPrinter',
16 'lambdarepr',
17]
20class LambdaPrinter(PythonCodePrinter):
21 """
22 This printer converts expressions into strings that can be used by
23 lambdify.
24 """
25 printmethod = "_lambdacode"
28 def _print_And(self, expr):
29 result = ['(']
30 for arg in sorted(expr.args, key=default_sort_key):
31 result.extend(['(', self._print(arg), ')'])
32 result.append(' and ')
33 result = result[:-1]
34 result.append(')')
35 return ''.join(result)
37 def _print_Or(self, expr):
38 result = ['(']
39 for arg in sorted(expr.args, key=default_sort_key):
40 result.extend(['(', self._print(arg), ')'])
41 result.append(' or ')
42 result = result[:-1]
43 result.append(')')
44 return ''.join(result)
46 def _print_Not(self, expr):
47 result = ['(', 'not (', self._print(expr.args[0]), '))']
48 return ''.join(result)
50 def _print_BooleanTrue(self, expr):
51 return "True"
53 def _print_BooleanFalse(self, expr):
54 return "False"
56 def _print_ITE(self, expr):
57 result = [
58 '((', self._print(expr.args[1]),
59 ') if (', self._print(expr.args[0]),
60 ') else (', self._print(expr.args[2]), '))'
61 ]
62 return ''.join(result)
64 def _print_NumberSymbol(self, expr):
65 return str(expr)
67 def _print_Pow(self, expr, **kwargs):
68 # XXX Temporary workaround. Should Python math printer be
69 # isolated from PythonCodePrinter?
70 return super(PythonCodePrinter, self)._print_Pow(expr, **kwargs)
73# numexpr works by altering the string passed to numexpr.evaluate
74# rather than by populating a namespace. Thus a special printer...
75class NumExprPrinter(LambdaPrinter):
76 # key, value pairs correspond to SymPy name and numexpr name
77 # functions not appearing in this dict will raise a TypeError
78 printmethod = "_numexprcode"
80 _numexpr_functions = {
81 'sin' : 'sin',
82 'cos' : 'cos',
83 'tan' : 'tan',
84 'asin': 'arcsin',
85 'acos': 'arccos',
86 'atan': 'arctan',
87 'atan2' : 'arctan2',
88 'sinh' : 'sinh',
89 'cosh' : 'cosh',
90 'tanh' : 'tanh',
91 'asinh': 'arcsinh',
92 'acosh': 'arccosh',
93 'atanh': 'arctanh',
94 'ln' : 'log',
95 'log': 'log',
96 'exp': 'exp',
97 'sqrt' : 'sqrt',
98 'Abs' : 'abs',
99 'conjugate' : 'conj',
100 'im' : 'imag',
101 're' : 'real',
102 'where' : 'where',
103 'complex' : 'complex',
104 'contains' : 'contains',
105 }
107 module = 'numexpr'
109 def _print_ImaginaryUnit(self, expr):
110 return '1j'
112 def _print_seq(self, seq, delimiter=', '):
113 # simplified _print_seq taken from pretty.py
114 s = [self._print(item) for item in seq]
115 if s:
116 return delimiter.join(s)
117 else:
118 return ""
120 def _print_Function(self, e):
121 func_name = e.func.__name__
123 nstr = self._numexpr_functions.get(func_name, None)
124 if nstr is None:
125 # check for implemented_function
126 if hasattr(e, '_imp_'):
127 return "(%s)" % self._print(e._imp_(*e.args))
128 else:
129 raise TypeError("numexpr does not support function '%s'" %
130 func_name)
131 return "%s(%s)" % (nstr, self._print_seq(e.args))
133 def _print_Piecewise(self, expr):
134 "Piecewise function printer"
135 exprs = [self._print(arg.expr) for arg in expr.args]
136 conds = [self._print(arg.cond) for arg in expr.args]
137 # If [default_value, True] is a (expr, cond) sequence in a Piecewise object
138 # it will behave the same as passing the 'default' kwarg to select()
139 # *as long as* it is the last element in expr.args.
140 # If this is not the case, it may be triggered prematurely.
141 ans = []
142 parenthesis_count = 0
143 is_last_cond_True = False
144 for cond, expr in zip(conds, exprs):
145 if cond == 'True':
146 ans.append(expr)
147 is_last_cond_True = True
148 break
149 else:
150 ans.append('where(%s, %s, ' % (cond, expr))
151 parenthesis_count += 1
152 if not is_last_cond_True:
153 # See https://github.com/pydata/numexpr/issues/298
154 #
155 # simplest way to put a nan but raises
156 # 'RuntimeWarning: invalid value encountered in log'
157 #
158 # There are other ways to do this such as
159 #
160 # >>> import numexpr as ne
161 # >>> nan = float('nan')
162 # >>> ne.evaluate('where(x < 0, -1, nan)', {'x': [-1, 2, 3], 'nan':nan})
163 # array([-1., nan, nan])
164 #
165 # That needs to be handled in the lambdified function though rather
166 # than here in the printer.
167 ans.append('log(-1)')
168 return ''.join(ans) + ')' * parenthesis_count
170 def _print_ITE(self, expr):
171 from sympy.functions.elementary.piecewise import Piecewise
172 return self._print(expr.rewrite(Piecewise))
174 def blacklisted(self, expr):
175 raise TypeError("numexpr cannot be used with %s" %
176 expr.__class__.__name__)
178 # blacklist all Matrix printing
179 _print_SparseRepMatrix = \
180 _print_MutableSparseMatrix = \
181 _print_ImmutableSparseMatrix = \
182 _print_Matrix = \
183 _print_DenseMatrix = \
184 _print_MutableDenseMatrix = \
185 _print_ImmutableMatrix = \
186 _print_ImmutableDenseMatrix = \
187 blacklisted
188 # blacklist some Python expressions
189 _print_list = \
190 _print_tuple = \
191 _print_Tuple = \
192 _print_dict = \
193 _print_Dict = \
194 blacklisted
196 def _print_NumExprEvaluate(self, expr):
197 evaluate = self._module_format(self.module +".evaluate")
198 return "%s('%s', truediv=True)" % (evaluate, self._print(expr.expr))
200 def doprint(self, expr):
201 from sympy.codegen.ast import CodegenAST
202 from sympy.codegen.pynodes import NumExprEvaluate
203 if not isinstance(expr, CodegenAST):
204 expr = NumExprEvaluate(expr)
205 return super().doprint(expr)
207 def _print_Return(self, expr):
208 from sympy.codegen.pynodes import NumExprEvaluate
209 r, = expr.args
210 if not isinstance(r, NumExprEvaluate):
211 expr = expr.func(NumExprEvaluate(r))
212 return super()._print_Return(expr)
214 def _print_Assignment(self, expr):
215 from sympy.codegen.pynodes import NumExprEvaluate
216 lhs, rhs, *args = expr.args
217 if not isinstance(rhs, NumExprEvaluate):
218 expr = expr.func(lhs, NumExprEvaluate(rhs), *args)
219 return super()._print_Assignment(expr)
221 def _print_CodeBlock(self, expr):
222 from sympy.codegen.ast import CodegenAST
223 from sympy.codegen.pynodes import NumExprEvaluate
224 args = [ arg if isinstance(arg, CodegenAST) else NumExprEvaluate(arg) for arg in expr.args ]
225 return super()._print_CodeBlock(self, expr.func(*args))
228class IntervalPrinter(MpmathPrinter, LambdaPrinter):
229 """Use ``lambda`` printer but print numbers as ``mpi`` intervals. """
231 def _print_Integer(self, expr):
232 return "mpi('%s')" % super(PythonCodePrinter, self)._print_Integer(expr)
234 def _print_Rational(self, expr):
235 return "mpi('%s')" % super(PythonCodePrinter, self)._print_Rational(expr)
237 def _print_Half(self, expr):
238 return "mpi('%s')" % super(PythonCodePrinter, self)._print_Rational(expr)
240 def _print_Pow(self, expr):
241 return super(MpmathPrinter, self)._print_Pow(expr, rational=True)
244for k in NumExprPrinter._numexpr_functions:
245 setattr(NumExprPrinter, '_print_%s' % k, NumExprPrinter._print_Function)
247def lambdarepr(expr, **settings):
248 """
249 Returns a string usable for lambdifying.
250 """
251 return LambdaPrinter(settings).doprint(expr)