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
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""
2Mathematica code printer
3"""
5from __future__ import annotations
6from typing import Any
8from sympy.core import Basic, Expr, Float
9from sympy.core.sorting import default_sort_key
11from sympy.printing.codeprinter import CodePrinter
12from sympy.printing.precedence import precedence
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}
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"
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 }
139 _number_symbols: set[tuple[Expr, Float]] = set()
140 _not_supported: set[Basic] = set()
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)
152 def _format_code(self, lines):
153 return lines
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))
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
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)
175 # Primitive numbers
176 def _print_Zero(self, expr):
177 return '0'
179 def _print_One(self, expr):
180 return '1'
182 def _print_NegativeOne(self, expr):
183 return '-1'
185 def _print_Half(self, expr):
186 return '1/2'
188 def _print_ImaginaryUnit(self, expr):
189 return 'I'
192 # Infinity and invalid numbers
193 def _print_Infinity(self, expr):
194 return 'Infinity'
196 def _print_NegativeInfinity(self, expr):
197 return '-Infinity'
199 def _print_ComplexInfinity(self, expr):
200 return 'ComplexInfinity'
202 def _print_NaN(self, expr):
203 return 'Indeterminate'
206 # Mathematical constants
207 def _print_Exp1(self, expr):
208 return 'E'
210 def _print_Pi(self, expr):
211 return 'Pi'
213 def _print_GoldenRatio(self, expr):
214 return 'GoldenRatio'
216 def _print_TribonacciConstant(self, expr):
217 expanded = expr.expand(func=True)
218 PREC = precedence(expr)
219 return self.parenthesize(expanded, PREC)
221 def _print_EulerGamma(self, expr):
222 return 'EulerGamma'
224 def _print_Catalan(self, expr):
225 return 'Catalan'
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
233 def _print_ImmutableDenseMatrix(self, expr):
234 return self.doprint(expr.tolist())
236 def _print_ImmutableSparseMatrix(self, expr):
238 def print_rule(pos, val):
239 return '{} -> {}'.format(
240 self.doprint((pos[0]+1, pos[1]+1)), self.doprint(val))
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 '}'
248 def print_dims():
249 return self.doprint(expr.shape)
251 return 'SparseArray[{}, {}]'.format(print_data(), print_dims())
253 def _print_ImmutableDenseNDimArray(self, expr):
254 return self.doprint(expr.tolist())
256 def _print_ImmutableSparseNDimArray(self, expr):
257 def print_string_list(string_list):
258 return '{' + ', '.join(a for a in string_list) + '}'
260 def to_mathematica_index(*args):
261 """Helper function to change Python style indexing to
262 Pathematica indexing.
264 Python indexing (0, 1 ... n-1)
265 -> Mathematica indexing (1, 2 ... n)
266 """
267 return tuple(i + 1 for i in args)
269 def print_rule(pos, val):
270 """Helper function to print a rule of Mathematica"""
271 return '{} -> {}'.format(self.doprint(pos), self.doprint(val))
273 def print_data():
274 """Helper function to print data part of Mathematica
275 sparse array.
277 It uses the fourth notation ``SparseArray[data,{d1,d2,...}]``
278 from
279 https://reference.wolfram.com/language/ref/SparseArray.html
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 )
290 def print_dims():
291 """Helper function to print dimensions part of Mathematica
292 sparse array.
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)
300 return 'SparseArray[{}, {}]'.format(print_data(), print_dims())
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, ", ")
315 _print_MinMaxBase = _print_Function
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]))
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) + "]]"
330 def _print_Sum(self, expr):
331 return "Hold[Sum[" + ', '.join(self.doprint(a) for a in expr.args) + "]]"
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) + "]]"
339 def _get_comment(self, text):
340 return "(* {} *)".format(text)
343def mathematica_code(expr, **settings):
344 r"""Converts an expr to a string of the Wolfram Mathematica code
346 Examples
347 ========
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)