Coverage for /usr/lib/python3/dist-packages/sympy/printing/mathematica.py: 37%

131 statements  

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

1""" 

2Mathematica code printer 

3""" 

4 

5from __future__ import annotations 

6from typing import Any 

7 

8from sympy.core import Basic, Expr, Float 

9from sympy.core.sorting import default_sort_key 

10 

11from sympy.printing.codeprinter import CodePrinter 

12from sympy.printing.precedence import precedence 

13 

14# Used in MCodePrinter._print_Function(self) 

15known_functions = { 

16 "exp": [(lambda x: True, "Exp")], 

17 "log": [(lambda x: True, "Log")], 

18 "sin": [(lambda x: True, "Sin")], 

19 "cos": [(lambda x: True, "Cos")], 

20 "tan": [(lambda x: True, "Tan")], 

21 "cot": [(lambda x: True, "Cot")], 

22 "sec": [(lambda x: True, "Sec")], 

23 "csc": [(lambda x: True, "Csc")], 

24 "asin": [(lambda x: True, "ArcSin")], 

25 "acos": [(lambda x: True, "ArcCos")], 

26 "atan": [(lambda x: True, "ArcTan")], 

27 "acot": [(lambda x: True, "ArcCot")], 

28 "asec": [(lambda x: True, "ArcSec")], 

29 "acsc": [(lambda x: True, "ArcCsc")], 

30 "atan2": [(lambda *x: True, "ArcTan")], 

31 "sinh": [(lambda x: True, "Sinh")], 

32 "cosh": [(lambda x: True, "Cosh")], 

33 "tanh": [(lambda x: True, "Tanh")], 

34 "coth": [(lambda x: True, "Coth")], 

35 "sech": [(lambda x: True, "Sech")], 

36 "csch": [(lambda x: True, "Csch")], 

37 "asinh": [(lambda x: True, "ArcSinh")], 

38 "acosh": [(lambda x: True, "ArcCosh")], 

39 "atanh": [(lambda x: True, "ArcTanh")], 

40 "acoth": [(lambda x: True, "ArcCoth")], 

41 "asech": [(lambda x: True, "ArcSech")], 

42 "acsch": [(lambda x: True, "ArcCsch")], 

43 "sinc": [(lambda x: True, "Sinc")], 

44 "conjugate": [(lambda x: True, "Conjugate")], 

45 "Max": [(lambda *x: True, "Max")], 

46 "Min": [(lambda *x: True, "Min")], 

47 "erf": [(lambda x: True, "Erf")], 

48 "erf2": [(lambda *x: True, "Erf")], 

49 "erfc": [(lambda x: True, "Erfc")], 

50 "erfi": [(lambda x: True, "Erfi")], 

51 "erfinv": [(lambda x: True, "InverseErf")], 

52 "erfcinv": [(lambda x: True, "InverseErfc")], 

53 "erf2inv": [(lambda *x: True, "InverseErf")], 

54 "expint": [(lambda *x: True, "ExpIntegralE")], 

55 "Ei": [(lambda x: True, "ExpIntegralEi")], 

56 "fresnelc": [(lambda x: True, "FresnelC")], 

57 "fresnels": [(lambda x: True, "FresnelS")], 

58 "gamma": [(lambda x: True, "Gamma")], 

59 "uppergamma": [(lambda *x: True, "Gamma")], 

60 "polygamma": [(lambda *x: True, "PolyGamma")], 

61 "loggamma": [(lambda x: True, "LogGamma")], 

62 "beta": [(lambda *x: True, "Beta")], 

63 "Ci": [(lambda x: True, "CosIntegral")], 

64 "Si": [(lambda x: True, "SinIntegral")], 

65 "Chi": [(lambda x: True, "CoshIntegral")], 

66 "Shi": [(lambda x: True, "SinhIntegral")], 

67 "li": [(lambda x: True, "LogIntegral")], 

68 "factorial": [(lambda x: True, "Factorial")], 

69 "factorial2": [(lambda x: True, "Factorial2")], 

70 "subfactorial": [(lambda x: True, "Subfactorial")], 

71 "catalan": [(lambda x: True, "CatalanNumber")], 

72 "harmonic": [(lambda *x: True, "HarmonicNumber")], 

73 "lucas": [(lambda x: True, "LucasL")], 

74 "RisingFactorial": [(lambda *x: True, "Pochhammer")], 

75 "FallingFactorial": [(lambda *x: True, "FactorialPower")], 

76 "laguerre": [(lambda *x: True, "LaguerreL")], 

77 "assoc_laguerre": [(lambda *x: True, "LaguerreL")], 

78 "hermite": [(lambda *x: True, "HermiteH")], 

79 "jacobi": [(lambda *x: True, "JacobiP")], 

80 "gegenbauer": [(lambda *x: True, "GegenbauerC")], 

81 "chebyshevt": [(lambda *x: True, "ChebyshevT")], 

82 "chebyshevu": [(lambda *x: True, "ChebyshevU")], 

83 "legendre": [(lambda *x: True, "LegendreP")], 

84 "assoc_legendre": [(lambda *x: True, "LegendreP")], 

85 "mathieuc": [(lambda *x: True, "MathieuC")], 

86 "mathieus": [(lambda *x: True, "MathieuS")], 

87 "mathieucprime": [(lambda *x: True, "MathieuCPrime")], 

88 "mathieusprime": [(lambda *x: True, "MathieuSPrime")], 

89 "stieltjes": [(lambda x: True, "StieltjesGamma")], 

90 "elliptic_e": [(lambda *x: True, "EllipticE")], 

91 "elliptic_f": [(lambda *x: True, "EllipticE")], 

92 "elliptic_k": [(lambda x: True, "EllipticK")], 

93 "elliptic_pi": [(lambda *x: True, "EllipticPi")], 

94 "zeta": [(lambda *x: True, "Zeta")], 

95 "dirichlet_eta": [(lambda x: True, "DirichletEta")], 

96 "riemann_xi": [(lambda x: True, "RiemannXi")], 

97 "besseli": [(lambda *x: True, "BesselI")], 

98 "besselj": [(lambda *x: True, "BesselJ")], 

99 "besselk": [(lambda *x: True, "BesselK")], 

100 "bessely": [(lambda *x: True, "BesselY")], 

101 "hankel1": [(lambda *x: True, "HankelH1")], 

102 "hankel2": [(lambda *x: True, "HankelH2")], 

103 "airyai": [(lambda x: True, "AiryAi")], 

104 "airybi": [(lambda x: True, "AiryBi")], 

105 "airyaiprime": [(lambda x: True, "AiryAiPrime")], 

106 "airybiprime": [(lambda x: True, "AiryBiPrime")], 

107 "polylog": [(lambda *x: True, "PolyLog")], 

108 "lerchphi": [(lambda *x: True, "LerchPhi")], 

109 "gcd": [(lambda *x: True, "GCD")], 

110 "lcm": [(lambda *x: True, "LCM")], 

111 "jn": [(lambda *x: True, "SphericalBesselJ")], 

112 "yn": [(lambda *x: True, "SphericalBesselY")], 

113 "hyper": [(lambda *x: True, "HypergeometricPFQ")], 

114 "meijerg": [(lambda *x: True, "MeijerG")], 

115 "appellf1": [(lambda *x: True, "AppellF1")], 

116 "DiracDelta": [(lambda x: True, "DiracDelta")], 

117 "Heaviside": [(lambda x: True, "HeavisideTheta")], 

118 "KroneckerDelta": [(lambda *x: True, "KroneckerDelta")], 

119 "sqrt": [(lambda x: True, "Sqrt")], # For automatic rewrites 

120} 

121 

122 

123class MCodePrinter(CodePrinter): 

124 """A printer to convert Python expressions to 

125 strings of the Wolfram's Mathematica code 

126 """ 

127 printmethod = "_mcode" 

128 language = "Wolfram Language" 

129 

130 _default_settings: dict[str, Any] = { 

131 'order': None, 

132 'full_prec': 'auto', 

133 'precision': 15, 

134 'user_functions': {}, 

135 'human': True, 

136 'allow_unknown_functions': False, 

137 } 

138 

139 _number_symbols: set[tuple[Expr, Float]] = set() 

140 _not_supported: set[Basic] = set() 

141 

142 def __init__(self, settings={}): 

143 """Register function mappings supplied by user""" 

144 CodePrinter.__init__(self, settings) 

145 self.known_functions = dict(known_functions) 

146 userfuncs = settings.get('user_functions', {}).copy() 

147 for k, v in userfuncs.items(): 

148 if not isinstance(v, list): 

149 userfuncs[k] = [(lambda *x: True, v)] 

150 self.known_functions.update(userfuncs) 

151 

152 def _format_code(self, lines): 

153 return lines 

154 

155 def _print_Pow(self, expr): 

156 PREC = precedence(expr) 

157 return '%s^%s' % (self.parenthesize(expr.base, PREC), 

158 self.parenthesize(expr.exp, PREC)) 

159 

160 def _print_Mul(self, expr): 

161 PREC = precedence(expr) 

162 c, nc = expr.args_cnc() 

163 res = super()._print_Mul(expr.func(*c)) 

164 if nc: 

165 res += '*' 

166 res += '**'.join(self.parenthesize(a, PREC) for a in nc) 

167 return res 

168 

169 def _print_Relational(self, expr): 

170 lhs_code = self._print(expr.lhs) 

171 rhs_code = self._print(expr.rhs) 

172 op = expr.rel_op 

173 return "{} {} {}".format(lhs_code, op, rhs_code) 

174 

175 # Primitive numbers 

176 def _print_Zero(self, expr): 

177 return '0' 

178 

179 def _print_One(self, expr): 

180 return '1' 

181 

182 def _print_NegativeOne(self, expr): 

183 return '-1' 

184 

185 def _print_Half(self, expr): 

186 return '1/2' 

187 

188 def _print_ImaginaryUnit(self, expr): 

189 return 'I' 

190 

191 

192 # Infinity and invalid numbers 

193 def _print_Infinity(self, expr): 

194 return 'Infinity' 

195 

196 def _print_NegativeInfinity(self, expr): 

197 return '-Infinity' 

198 

199 def _print_ComplexInfinity(self, expr): 

200 return 'ComplexInfinity' 

201 

202 def _print_NaN(self, expr): 

203 return 'Indeterminate' 

204 

205 

206 # Mathematical constants 

207 def _print_Exp1(self, expr): 

208 return 'E' 

209 

210 def _print_Pi(self, expr): 

211 return 'Pi' 

212 

213 def _print_GoldenRatio(self, expr): 

214 return 'GoldenRatio' 

215 

216 def _print_TribonacciConstant(self, expr): 

217 expanded = expr.expand(func=True) 

218 PREC = precedence(expr) 

219 return self.parenthesize(expanded, PREC) 

220 

221 def _print_EulerGamma(self, expr): 

222 return 'EulerGamma' 

223 

224 def _print_Catalan(self, expr): 

225 return 'Catalan' 

226 

227 

228 def _print_list(self, expr): 

229 return '{' + ', '.join(self.doprint(a) for a in expr) + '}' 

230 _print_tuple = _print_list 

231 _print_Tuple = _print_list 

232 

233 def _print_ImmutableDenseMatrix(self, expr): 

234 return self.doprint(expr.tolist()) 

235 

236 def _print_ImmutableSparseMatrix(self, expr): 

237 

238 def print_rule(pos, val): 

239 return '{} -> {}'.format( 

240 self.doprint((pos[0]+1, pos[1]+1)), self.doprint(val)) 

241 

242 def print_data(): 

243 items = sorted(expr.todok().items(), key=default_sort_key) 

244 return '{' + \ 

245 ', '.join(print_rule(k, v) for k, v in items) + \ 

246 '}' 

247 

248 def print_dims(): 

249 return self.doprint(expr.shape) 

250 

251 return 'SparseArray[{}, {}]'.format(print_data(), print_dims()) 

252 

253 def _print_ImmutableDenseNDimArray(self, expr): 

254 return self.doprint(expr.tolist()) 

255 

256 def _print_ImmutableSparseNDimArray(self, expr): 

257 def print_string_list(string_list): 

258 return '{' + ', '.join(a for a in string_list) + '}' 

259 

260 def to_mathematica_index(*args): 

261 """Helper function to change Python style indexing to 

262 Pathematica indexing. 

263 

264 Python indexing (0, 1 ... n-1) 

265 -> Mathematica indexing (1, 2 ... n) 

266 """ 

267 return tuple(i + 1 for i in args) 

268 

269 def print_rule(pos, val): 

270 """Helper function to print a rule of Mathematica""" 

271 return '{} -> {}'.format(self.doprint(pos), self.doprint(val)) 

272 

273 def print_data(): 

274 """Helper function to print data part of Mathematica 

275 sparse array. 

276 

277 It uses the fourth notation ``SparseArray[data,{d1,d2,...}]`` 

278 from 

279 https://reference.wolfram.com/language/ref/SparseArray.html 

280 

281 ``data`` must be formatted with rule. 

282 """ 

283 return print_string_list( 

284 [print_rule( 

285 to_mathematica_index(*(expr._get_tuple_index(key))), 

286 value) 

287 for key, value in sorted(expr._sparse_array.items())] 

288 ) 

289 

290 def print_dims(): 

291 """Helper function to print dimensions part of Mathematica 

292 sparse array. 

293 

294 It uses the fourth notation ``SparseArray[data,{d1,d2,...}]`` 

295 from 

296 https://reference.wolfram.com/language/ref/SparseArray.html 

297 """ 

298 return self.doprint(expr.shape) 

299 

300 return 'SparseArray[{}, {}]'.format(print_data(), print_dims()) 

301 

302 def _print_Function(self, expr): 

303 if expr.func.__name__ in self.known_functions: 

304 cond_mfunc = self.known_functions[expr.func.__name__] 

305 for cond, mfunc in cond_mfunc: 

306 if cond(*expr.args): 

307 return "%s[%s]" % (mfunc, self.stringify(expr.args, ", ")) 

308 elif expr.func.__name__ in self._rewriteable_functions: 

309 # Simple rewrite to supported function possible 

310 target_f, required_fs = self._rewriteable_functions[expr.func.__name__] 

311 if self._can_print(target_f) and all(self._can_print(f) for f in required_fs): 

312 return self._print(expr.rewrite(target_f)) 

313 return expr.func.__name__ + "[%s]" % self.stringify(expr.args, ", ") 

314 

315 _print_MinMaxBase = _print_Function 

316 

317 def _print_LambertW(self, expr): 

318 if len(expr.args) == 1: 

319 return "ProductLog[{}]".format(self._print(expr.args[0])) 

320 return "ProductLog[{}, {}]".format( 

321 self._print(expr.args[1]), self._print(expr.args[0])) 

322 

323 def _print_Integral(self, expr): 

324 if len(expr.variables) == 1 and not expr.limits[0][1:]: 

325 args = [expr.args[0], expr.variables[0]] 

326 else: 

327 args = expr.args 

328 return "Hold[Integrate[" + ', '.join(self.doprint(a) for a in args) + "]]" 

329 

330 def _print_Sum(self, expr): 

331 return "Hold[Sum[" + ', '.join(self.doprint(a) for a in expr.args) + "]]" 

332 

333 def _print_Derivative(self, expr): 

334 dexpr = expr.expr 

335 dvars = [i[0] if i[1] == 1 else i for i in expr.variable_count] 

336 return "Hold[D[" + ', '.join(self.doprint(a) for a in [dexpr] + dvars) + "]]" 

337 

338 

339 def _get_comment(self, text): 

340 return "(* {} *)".format(text) 

341 

342 

343def mathematica_code(expr, **settings): 

344 r"""Converts an expr to a string of the Wolfram Mathematica code 

345 

346 Examples 

347 ======== 

348 

349 >>> from sympy import mathematica_code as mcode, symbols, sin 

350 >>> x = symbols('x') 

351 >>> mcode(sin(x).series(x).removeO()) 

352 '(1/120)*x^5 - 1/6*x^3 + x' 

353 """ 

354 return MCodePrinter(settings).doprint(expr)