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

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 

7 

8 

9__all__ = [ 

10 'PythonCodePrinter', 

11 'MpmathPrinter', # MpmathPrinter is published for backward compatibility 

12 'NumPyPrinter', 

13 'LambdaPrinter', 

14 'NumPyPrinter', 

15 'IntervalPrinter', 

16 'lambdarepr', 

17] 

18 

19 

20class LambdaPrinter(PythonCodePrinter): 

21 """ 

22 This printer converts expressions into strings that can be used by 

23 lambdify. 

24 """ 

25 printmethod = "_lambdacode" 

26 

27 

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) 

36 

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) 

45 

46 def _print_Not(self, expr): 

47 result = ['(', 'not (', self._print(expr.args[0]), '))'] 

48 return ''.join(result) 

49 

50 def _print_BooleanTrue(self, expr): 

51 return "True" 

52 

53 def _print_BooleanFalse(self, expr): 

54 return "False" 

55 

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) 

63 

64 def _print_NumberSymbol(self, expr): 

65 return str(expr) 

66 

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) 

71 

72 

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" 

79 

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 } 

106 

107 module = 'numexpr' 

108 

109 def _print_ImaginaryUnit(self, expr): 

110 return '1j' 

111 

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 "" 

119 

120 def _print_Function(self, e): 

121 func_name = e.func.__name__ 

122 

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)) 

132 

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 

169 

170 def _print_ITE(self, expr): 

171 from sympy.functions.elementary.piecewise import Piecewise 

172 return self._print(expr.rewrite(Piecewise)) 

173 

174 def blacklisted(self, expr): 

175 raise TypeError("numexpr cannot be used with %s" % 

176 expr.__class__.__name__) 

177 

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 

195 

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)) 

199 

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) 

206 

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) 

213 

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) 

220 

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)) 

226 

227 

228class IntervalPrinter(MpmathPrinter, LambdaPrinter): 

229 """Use ``lambda`` printer but print numbers as ``mpi`` intervals. """ 

230 

231 def _print_Integer(self, expr): 

232 return "mpi('%s')" % super(PythonCodePrinter, self)._print_Integer(expr) 

233 

234 def _print_Rational(self, expr): 

235 return "mpi('%s')" % super(PythonCodePrinter, self)._print_Rational(expr) 

236 

237 def _print_Half(self, expr): 

238 return "mpi('%s')" % super(PythonCodePrinter, self)._print_Rational(expr) 

239 

240 def _print_Pow(self, expr): 

241 return super(MpmathPrinter, self)._print_Pow(expr, rational=True) 

242 

243 

244for k in NumExprPrinter._numexpr_functions: 

245 setattr(NumExprPrinter, '_print_%s' % k, NumExprPrinter._print_Function) 

246 

247def lambdarepr(expr, **settings): 

248 """ 

249 Returns a string usable for lambdifying. 

250 """ 

251 return LambdaPrinter(settings).doprint(expr)