maple.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. """
  2. Maple code printer
  3. The MapleCodePrinter converts single SymPy expressions into single
  4. Maple expressions, using the functions defined in the Maple objects where possible.
  5. FIXME: This module is still under actively developed. Some functions may be not completed.
  6. """
  7. from sympy.core import S
  8. from sympy.core.numbers import Integer, IntegerConstant
  9. from sympy.printing.codeprinter import CodePrinter
  10. from sympy.printing.precedence import precedence, PRECEDENCE
  11. import sympy
  12. _known_func_same_name = (
  13. 'sin', 'cos', 'tan', 'sec', 'csc', 'cot', 'sinh', 'cosh', 'tanh', 'sech',
  14. 'csch', 'coth', 'exp', 'floor', 'factorial', 'bernoulli', 'euler',
  15. 'fibonacci', 'gcd', 'lcm', 'conjugate', 'Ci', 'Chi', 'Ei', 'Li', 'Si', 'Shi',
  16. 'erf', 'erfc', 'harmonic', 'LambertW',
  17. 'sqrt', # For automatic rewrites
  18. )
  19. known_functions = {
  20. # SymPy -> Maple
  21. 'Abs': 'abs',
  22. 'log': 'ln',
  23. 'asin': 'arcsin',
  24. 'acos': 'arccos',
  25. 'atan': 'arctan',
  26. 'asec': 'arcsec',
  27. 'acsc': 'arccsc',
  28. 'acot': 'arccot',
  29. 'asinh': 'arcsinh',
  30. 'acosh': 'arccosh',
  31. 'atanh': 'arctanh',
  32. 'asech': 'arcsech',
  33. 'acsch': 'arccsch',
  34. 'acoth': 'arccoth',
  35. 'ceiling': 'ceil',
  36. 'Max' : 'max',
  37. 'Min' : 'min',
  38. 'factorial2': 'doublefactorial',
  39. 'RisingFactorial': 'pochhammer',
  40. 'besseli': 'BesselI',
  41. 'besselj': 'BesselJ',
  42. 'besselk': 'BesselK',
  43. 'bessely': 'BesselY',
  44. 'hankelh1': 'HankelH1',
  45. 'hankelh2': 'HankelH2',
  46. 'airyai': 'AiryAi',
  47. 'airybi': 'AiryBi',
  48. 'appellf1': 'AppellF1',
  49. 'fresnelc': 'FresnelC',
  50. 'fresnels': 'FresnelS',
  51. 'lerchphi' : 'LerchPhi',
  52. }
  53. for _func in _known_func_same_name:
  54. known_functions[_func] = _func
  55. number_symbols = {
  56. # SymPy -> Maple
  57. S.Pi: 'Pi',
  58. S.Exp1: 'exp(1)',
  59. S.Catalan: 'Catalan',
  60. S.EulerGamma: 'gamma',
  61. S.GoldenRatio: '(1/2 + (1/2)*sqrt(5))'
  62. }
  63. spec_relational_ops = {
  64. # SymPy -> Maple
  65. '==': '=',
  66. '!=': '<>'
  67. }
  68. not_supported_symbol = [
  69. S.ComplexInfinity
  70. ]
  71. class MapleCodePrinter(CodePrinter):
  72. """
  73. Printer which converts a SymPy expression into a maple code.
  74. """
  75. printmethod = "_maple"
  76. language = "maple"
  77. _default_settings = {
  78. 'order': None,
  79. 'full_prec': 'auto',
  80. 'human': True,
  81. 'inline': True,
  82. 'allow_unknown_functions': True,
  83. }
  84. def __init__(self, settings=None):
  85. if settings is None:
  86. settings = dict()
  87. super().__init__(settings)
  88. self.known_functions = dict(known_functions)
  89. userfuncs = settings.get('user_functions', {})
  90. self.known_functions.update(userfuncs)
  91. def _get_statement(self, codestring):
  92. return "%s;" % codestring
  93. def _get_comment(self, text):
  94. return "# {}".format(text)
  95. def _declare_number_const(self, name, value):
  96. return "{} := {};".format(name,
  97. value.evalf(self._settings['precision']))
  98. def _format_code(self, lines):
  99. return lines
  100. def _print_tuple(self, expr):
  101. return self._print(list(expr))
  102. def _print_Tuple(self, expr):
  103. return self._print(list(expr))
  104. def _print_Assignment(self, expr):
  105. lhs = self._print(expr.lhs)
  106. rhs = self._print(expr.rhs)
  107. return "{lhs} := {rhs}".format(lhs=lhs, rhs=rhs)
  108. def _print_Pow(self, expr, **kwargs):
  109. PREC = precedence(expr)
  110. if expr.exp == -1:
  111. return '1/%s' % (self.parenthesize(expr.base, PREC))
  112. elif expr.exp in (0.5, S.Half):
  113. return 'sqrt(%s)' % self._print(expr.base)
  114. elif expr.exp in (-0.5, -S.Half):
  115. return '1/sqrt(%s)' % self._print(expr.base)
  116. else:
  117. return '{base}^{exp}'.format(
  118. base=self.parenthesize(expr.base, PREC),
  119. exp=self.parenthesize(expr.exp, PREC))
  120. def _print_Piecewise(self, expr):
  121. if (expr.args[-1].cond is not True) and (expr.args[-1].cond != S.BooleanTrue):
  122. # We need the last conditional to be a True, otherwise the resulting
  123. # function may not return a result.
  124. raise ValueError("All Piecewise expressions must contain an "
  125. "(expr, True) statement to be used as a default "
  126. "condition. Without one, the generated "
  127. "expression may not evaluate to anything under "
  128. "some condition.")
  129. _coup_list = [
  130. ("{c}, {e}".format(c=self._print(c),
  131. e=self._print(e)) if c is not True and c is not S.BooleanTrue else "{e}".format(
  132. e=self._print(e)))
  133. for e, c in expr.args]
  134. _inbrace = ', '.join(_coup_list)
  135. return 'piecewise({_inbrace})'.format(_inbrace=_inbrace)
  136. def _print_Rational(self, expr):
  137. p, q = int(expr.p), int(expr.q)
  138. return "{p}/{q}".format(p=str(p), q=str(q))
  139. def _print_Relational(self, expr):
  140. PREC=precedence(expr)
  141. lhs_code = self.parenthesize(expr.lhs, PREC)
  142. rhs_code = self.parenthesize(expr.rhs, PREC)
  143. op = expr.rel_op
  144. if op in spec_relational_ops:
  145. op = spec_relational_ops[op]
  146. return "{lhs} {rel_op} {rhs}".format(lhs=lhs_code, rel_op=op, rhs=rhs_code)
  147. def _print_NumberSymbol(self, expr):
  148. return number_symbols[expr]
  149. def _print_NegativeInfinity(self, expr):
  150. return '-infinity'
  151. def _print_Infinity(self, expr):
  152. return 'infinity'
  153. def _print_Idx(self, expr):
  154. return self._print(expr.label)
  155. def _print_BooleanTrue(self, expr):
  156. return "true"
  157. def _print_BooleanFalse(self, expr):
  158. return "false"
  159. def _print_bool(self, expr):
  160. return 'true' if expr else 'false'
  161. def _print_NaN(self, expr):
  162. return 'undefined'
  163. def _get_matrix(self, expr, sparse=False):
  164. if S.Zero in expr.shape:
  165. _strM = 'Matrix([], storage = {storage})'.format(
  166. storage='sparse' if sparse else 'rectangular')
  167. else:
  168. _strM = 'Matrix({list}, storage = {storage})'.format(
  169. list=self._print(expr.tolist()),
  170. storage='sparse' if sparse else 'rectangular')
  171. return _strM
  172. def _print_MatrixElement(self, expr):
  173. return "{parent}[{i_maple}, {j_maple}]".format(
  174. parent=self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True),
  175. i_maple=self._print(expr.i + 1),
  176. j_maple=self._print(expr.j + 1))
  177. def _print_MatrixBase(self, expr):
  178. return self._get_matrix(expr, sparse=False)
  179. def _print_SparseRepMatrix(self, expr):
  180. return self._get_matrix(expr, sparse=True)
  181. def _print_Identity(self, expr):
  182. if isinstance(expr.rows, (Integer, IntegerConstant)):
  183. return self._print(sympy.SparseMatrix(expr))
  184. else:
  185. return "Matrix({var_size}, shape = identity)".format(var_size=self._print(expr.rows))
  186. def _print_MatMul(self, expr):
  187. PREC=precedence(expr)
  188. _fact_list = list(expr.args)
  189. _const = None
  190. if not isinstance(_fact_list[0], (sympy.MatrixBase, sympy.MatrixExpr,
  191. sympy.MatrixSlice, sympy.MatrixSymbol)):
  192. _const, _fact_list = _fact_list[0], _fact_list[1:]
  193. if _const is None or _const == 1:
  194. return '.'.join(self.parenthesize(_m, PREC) for _m in _fact_list)
  195. else:
  196. return '{c}*{m}'.format(c=_const, m='.'.join(self.parenthesize(_m, PREC) for _m in _fact_list))
  197. def _print_MatPow(self, expr):
  198. # This function requires LinearAlgebra Function in Maple
  199. return 'MatrixPower({A}, {n})'.format(A=self._print(expr.base), n=self._print(expr.exp))
  200. def _print_HadamardProduct(self, expr):
  201. PREC = precedence(expr)
  202. _fact_list = list(expr.args)
  203. return '*'.join(self.parenthesize(_m, PREC) for _m in _fact_list)
  204. def _print_Derivative(self, expr):
  205. _f, (_var, _order) = expr.args
  206. if _order != 1:
  207. _second_arg = '{var}${order}'.format(var=self._print(_var),
  208. order=self._print(_order))
  209. else:
  210. _second_arg = '{var}'.format(var=self._print(_var))
  211. return 'diff({func_expr}, {sec_arg})'.format(func_expr=self._print(_f), sec_arg=_second_arg)
  212. def maple_code(expr, assign_to=None, **settings):
  213. r"""Converts ``expr`` to a string of Maple code.
  214. Parameters
  215. ==========
  216. expr : Expr
  217. A SymPy expression to be converted.
  218. assign_to : optional
  219. When given, the argument is used as the name of the variable to which
  220. the expression is assigned. Can be a string, ``Symbol``,
  221. ``MatrixSymbol``, or ``Indexed`` type. This can be helpful for
  222. expressions that generate multi-line statements.
  223. precision : integer, optional
  224. The precision for numbers such as pi [default=16].
  225. user_functions : dict, optional
  226. A dictionary where keys are ``FunctionClass`` instances and values are
  227. their string representations. Alternatively, the dictionary value can
  228. be a list of tuples i.e. [(argument_test, cfunction_string)]. See
  229. below for examples.
  230. human : bool, optional
  231. If True, the result is a single string that may contain some constant
  232. declarations for the number symbols. If False, the same information is
  233. returned in a tuple of (symbols_to_declare, not_supported_functions,
  234. code_text). [default=True].
  235. contract: bool, optional
  236. If True, ``Indexed`` instances are assumed to obey tensor contraction
  237. rules and the corresponding nested loops over indices are generated.
  238. Setting contract=False will not generate loops, instead the user is
  239. responsible to provide values for the indices in the code.
  240. [default=True].
  241. inline: bool, optional
  242. If True, we try to create single-statement code instead of multiple
  243. statements. [default=True].
  244. """
  245. return MapleCodePrinter(settings).doprint(expr, assign_to)
  246. def print_maple_code(expr, **settings):
  247. """Prints the Maple representation of the given expression.
  248. See :func:`maple_code` for the meaning of the optional arguments.
  249. Examples
  250. ========
  251. >>> from sympy import print_maple_code, symbols
  252. >>> x, y = symbols('x y')
  253. >>> print_maple_code(x, assign_to=y)
  254. y := x
  255. """
  256. print(maple_code(expr, **settings))