Coverage for /usr/lib/python3/dist-packages/sympy/printing/latex.py: 18%
1878 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"""
2A Printer which converts an expression into its LaTeX equivalent.
3"""
4from __future__ import annotations
5from typing import Any, Callable, TYPE_CHECKING
7import itertools
9from sympy.core import Add, Float, Mod, Mul, Number, S, Symbol, Expr
10from sympy.core.alphabets import greeks
11from sympy.core.containers import Tuple
12from sympy.core.function import Function, AppliedUndef, Derivative
13from sympy.core.operations import AssocOp
14from sympy.core.power import Pow
15from sympy.core.sorting import default_sort_key
16from sympy.core.sympify import SympifyError
17from sympy.logic.boolalg import true, BooleanTrue, BooleanFalse
18from sympy.tensor.array import NDimArray
20# sympy.printing imports
21from sympy.printing.precedence import precedence_traditional
22from sympy.printing.printer import Printer, print_function
23from sympy.printing.conventions import split_super_sub, requires_partial
24from sympy.printing.precedence import precedence, PRECEDENCE
26from mpmath.libmp.libmpf import prec_to_dps, to_str as mlib_to_str
28from sympy.utilities.iterables import has_variety, sift
30import re
32if TYPE_CHECKING:
33 from sympy.vector.basisdependent import BasisDependent
35# Hand-picked functions which can be used directly in both LaTeX and MathJax
36# Complete list at
37# https://docs.mathjax.org/en/latest/tex.html#supported-latex-commands
38# This variable only contains those functions which SymPy uses.
39accepted_latex_functions = ['arcsin', 'arccos', 'arctan', 'sin', 'cos', 'tan',
40 'sinh', 'cosh', 'tanh', 'sqrt', 'ln', 'log', 'sec',
41 'csc', 'cot', 'coth', 're', 'im', 'frac', 'root',
42 'arg',
43 ]
45tex_greek_dictionary = {
46 'Alpha': r'\mathrm{A}',
47 'Beta': r'\mathrm{B}',
48 'Gamma': r'\Gamma',
49 'Delta': r'\Delta',
50 'Epsilon': r'\mathrm{E}',
51 'Zeta': r'\mathrm{Z}',
52 'Eta': r'\mathrm{H}',
53 'Theta': r'\Theta',
54 'Iota': r'\mathrm{I}',
55 'Kappa': r'\mathrm{K}',
56 'Lambda': r'\Lambda',
57 'Mu': r'\mathrm{M}',
58 'Nu': r'\mathrm{N}',
59 'Xi': r'\Xi',
60 'omicron': 'o',
61 'Omicron': r'\mathrm{O}',
62 'Pi': r'\Pi',
63 'Rho': r'\mathrm{P}',
64 'Sigma': r'\Sigma',
65 'Tau': r'\mathrm{T}',
66 'Upsilon': r'\Upsilon',
67 'Phi': r'\Phi',
68 'Chi': r'\mathrm{X}',
69 'Psi': r'\Psi',
70 'Omega': r'\Omega',
71 'lamda': r'\lambda',
72 'Lamda': r'\Lambda',
73 'khi': r'\chi',
74 'Khi': r'\mathrm{X}',
75 'varepsilon': r'\varepsilon',
76 'varkappa': r'\varkappa',
77 'varphi': r'\varphi',
78 'varpi': r'\varpi',
79 'varrho': r'\varrho',
80 'varsigma': r'\varsigma',
81 'vartheta': r'\vartheta',
82}
84other_symbols = {'aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', 'hbar',
85 'hslash', 'mho', 'wp'}
87# Variable name modifiers
88modifier_dict: dict[str, Callable[[str], str]] = {
89 # Accents
90 'mathring': lambda s: r'\mathring{'+s+r'}',
91 'ddddot': lambda s: r'\ddddot{'+s+r'}',
92 'dddot': lambda s: r'\dddot{'+s+r'}',
93 'ddot': lambda s: r'\ddot{'+s+r'}',
94 'dot': lambda s: r'\dot{'+s+r'}',
95 'check': lambda s: r'\check{'+s+r'}',
96 'breve': lambda s: r'\breve{'+s+r'}',
97 'acute': lambda s: r'\acute{'+s+r'}',
98 'grave': lambda s: r'\grave{'+s+r'}',
99 'tilde': lambda s: r'\tilde{'+s+r'}',
100 'hat': lambda s: r'\hat{'+s+r'}',
101 'bar': lambda s: r'\bar{'+s+r'}',
102 'vec': lambda s: r'\vec{'+s+r'}',
103 'prime': lambda s: "{"+s+"}'",
104 'prm': lambda s: "{"+s+"}'",
105 # Faces
106 'bold': lambda s: r'\boldsymbol{'+s+r'}',
107 'bm': lambda s: r'\boldsymbol{'+s+r'}',
108 'cal': lambda s: r'\mathcal{'+s+r'}',
109 'scr': lambda s: r'\mathscr{'+s+r'}',
110 'frak': lambda s: r'\mathfrak{'+s+r'}',
111 # Brackets
112 'norm': lambda s: r'\left\|{'+s+r'}\right\|',
113 'avg': lambda s: r'\left\langle{'+s+r'}\right\rangle',
114 'abs': lambda s: r'\left|{'+s+r'}\right|',
115 'mag': lambda s: r'\left|{'+s+r'}\right|',
116}
118greek_letters_set = frozenset(greeks)
120_between_two_numbers_p = (
121 re.compile(r'[0-9][} ]*$'), # search
122 re.compile(r'[0-9]'), # match
123)
126def latex_escape(s: str) -> str:
127 """
128 Escape a string such that latex interprets it as plaintext.
130 We cannot use verbatim easily with mathjax, so escaping is easier.
131 Rules from https://tex.stackexchange.com/a/34586/41112.
132 """
133 s = s.replace('\\', r'\textbackslash')
134 for c in '&%$#_{}':
135 s = s.replace(c, '\\' + c)
136 s = s.replace('~', r'\textasciitilde')
137 s = s.replace('^', r'\textasciicircum')
138 return s
141class LatexPrinter(Printer):
142 printmethod = "_latex"
144 _default_settings: dict[str, Any] = {
145 "full_prec": False,
146 "fold_frac_powers": False,
147 "fold_func_brackets": False,
148 "fold_short_frac": None,
149 "inv_trig_style": "abbreviated",
150 "itex": False,
151 "ln_notation": False,
152 "long_frac_ratio": None,
153 "mat_delim": "[",
154 "mat_str": None,
155 "mode": "plain",
156 "mul_symbol": None,
157 "order": None,
158 "symbol_names": {},
159 "root_notation": True,
160 "mat_symbol_style": "plain",
161 "imaginary_unit": "i",
162 "gothic_re_im": False,
163 "decimal_separator": "period",
164 "perm_cyclic": True,
165 "parenthesize_super": True,
166 "min": None,
167 "max": None,
168 "diff_operator": "d",
169 }
171 def __init__(self, settings=None):
172 Printer.__init__(self, settings)
174 if 'mode' in self._settings:
175 valid_modes = ['inline', 'plain', 'equation',
176 'equation*']
177 if self._settings['mode'] not in valid_modes:
178 raise ValueError("'mode' must be one of 'inline', 'plain', "
179 "'equation' or 'equation*'")
181 if self._settings['fold_short_frac'] is None and \
182 self._settings['mode'] == 'inline':
183 self._settings['fold_short_frac'] = True
185 mul_symbol_table = {
186 None: r" ",
187 "ldot": r" \,.\, ",
188 "dot": r" \cdot ",
189 "times": r" \times "
190 }
191 try:
192 self._settings['mul_symbol_latex'] = \
193 mul_symbol_table[self._settings['mul_symbol']]
194 except KeyError:
195 self._settings['mul_symbol_latex'] = \
196 self._settings['mul_symbol']
197 try:
198 self._settings['mul_symbol_latex_numbers'] = \
199 mul_symbol_table[self._settings['mul_symbol'] or 'dot']
200 except KeyError:
201 if (self._settings['mul_symbol'].strip() in
202 ['', ' ', '\\', '\\,', '\\:', '\\;', '\\quad']):
203 self._settings['mul_symbol_latex_numbers'] = \
204 mul_symbol_table['dot']
205 else:
206 self._settings['mul_symbol_latex_numbers'] = \
207 self._settings['mul_symbol']
209 self._delim_dict = {'(': ')', '[': ']'}
211 imaginary_unit_table = {
212 None: r"i",
213 "i": r"i",
214 "ri": r"\mathrm{i}",
215 "ti": r"\text{i}",
216 "j": r"j",
217 "rj": r"\mathrm{j}",
218 "tj": r"\text{j}",
219 }
220 imag_unit = self._settings['imaginary_unit']
221 self._settings['imaginary_unit_latex'] = imaginary_unit_table.get(imag_unit, imag_unit)
223 diff_operator_table = {
224 None: r"d",
225 "d": r"d",
226 "rd": r"\mathrm{d}",
227 "td": r"\text{d}",
228 }
229 diff_operator = self._settings['diff_operator']
230 self._settings["diff_operator_latex"] = diff_operator_table.get(diff_operator, diff_operator)
232 def _add_parens(self, s) -> str:
233 return r"\left({}\right)".format(s)
235 # TODO: merge this with the above, which requires a lot of test changes
236 def _add_parens_lspace(self, s) -> str:
237 return r"\left( {}\right)".format(s)
239 def parenthesize(self, item, level, is_neg=False, strict=False) -> str:
240 prec_val = precedence_traditional(item)
241 if is_neg and strict:
242 return self._add_parens(self._print(item))
244 if (prec_val < level) or ((not strict) and prec_val <= level):
245 return self._add_parens(self._print(item))
246 else:
247 return self._print(item)
249 def parenthesize_super(self, s):
250 """
251 Protect superscripts in s
253 If the parenthesize_super option is set, protect with parentheses, else
254 wrap in braces.
255 """
256 if "^" in s:
257 if self._settings['parenthesize_super']:
258 return self._add_parens(s)
259 else:
260 return "{{{}}}".format(s)
261 return s
263 def doprint(self, expr) -> str:
264 tex = Printer.doprint(self, expr)
266 if self._settings['mode'] == 'plain':
267 return tex
268 elif self._settings['mode'] == 'inline':
269 return r"$%s$" % tex
270 elif self._settings['itex']:
271 return r"$$%s$$" % tex
272 else:
273 env_str = self._settings['mode']
274 return r"\begin{%s}%s\end{%s}" % (env_str, tex, env_str)
276 def _needs_brackets(self, expr) -> bool:
277 """
278 Returns True if the expression needs to be wrapped in brackets when
279 printed, False otherwise. For example: a + b => True; a => False;
280 10 => False; -10 => True.
281 """
282 return not ((expr.is_Integer and expr.is_nonnegative)
283 or (expr.is_Atom and (expr is not S.NegativeOne
284 and expr.is_Rational is False)))
286 def _needs_function_brackets(self, expr) -> bool:
287 """
288 Returns True if the expression needs to be wrapped in brackets when
289 passed as an argument to a function, False otherwise. This is a more
290 liberal version of _needs_brackets, in that many expressions which need
291 to be wrapped in brackets when added/subtracted/raised to a power do
292 not need them when passed to a function. Such an example is a*b.
293 """
294 if not self._needs_brackets(expr):
295 return False
296 else:
297 # Muls of the form a*b*c... can be folded
298 if expr.is_Mul and not self._mul_is_clean(expr):
299 return True
300 # Pows which don't need brackets can be folded
301 elif expr.is_Pow and not self._pow_is_clean(expr):
302 return True
303 # Add and Function always need brackets
304 elif expr.is_Add or expr.is_Function:
305 return True
306 else:
307 return False
309 def _needs_mul_brackets(self, expr, first=False, last=False) -> bool:
310 """
311 Returns True if the expression needs to be wrapped in brackets when
312 printed as part of a Mul, False otherwise. This is True for Add,
313 but also for some container objects that would not need brackets
314 when appearing last in a Mul, e.g. an Integral. ``last=True``
315 specifies that this expr is the last to appear in a Mul.
316 ``first=True`` specifies that this expr is the first to appear in
317 a Mul.
318 """
319 from sympy.concrete.products import Product
320 from sympy.concrete.summations import Sum
321 from sympy.integrals.integrals import Integral
323 if expr.is_Mul:
324 if not first and expr.could_extract_minus_sign():
325 return True
326 elif precedence_traditional(expr) < PRECEDENCE["Mul"]:
327 return True
328 elif expr.is_Relational:
329 return True
330 if expr.is_Piecewise:
331 return True
332 if any(expr.has(x) for x in (Mod,)):
333 return True
334 if (not last and
335 any(expr.has(x) for x in (Integral, Product, Sum))):
336 return True
338 return False
340 def _needs_add_brackets(self, expr) -> bool:
341 """
342 Returns True if the expression needs to be wrapped in brackets when
343 printed as part of an Add, False otherwise. This is False for most
344 things.
345 """
346 if expr.is_Relational:
347 return True
348 if any(expr.has(x) for x in (Mod,)):
349 return True
350 if expr.is_Add:
351 return True
352 return False
354 def _mul_is_clean(self, expr) -> bool:
355 for arg in expr.args:
356 if arg.is_Function:
357 return False
358 return True
360 def _pow_is_clean(self, expr) -> bool:
361 return not self._needs_brackets(expr.base)
363 def _do_exponent(self, expr: str, exp):
364 if exp is not None:
365 return r"\left(%s\right)^{%s}" % (expr, exp)
366 else:
367 return expr
369 def _print_Basic(self, expr):
370 name = self._deal_with_super_sub(expr.__class__.__name__)
371 if expr.args:
372 ls = [self._print(o) for o in expr.args]
373 s = r"\operatorname{{{}}}\left({}\right)"
374 return s.format(name, ", ".join(ls))
375 else:
376 return r"\text{{{}}}".format(name)
378 def _print_bool(self, e: bool | BooleanTrue | BooleanFalse):
379 return r"\text{%s}" % e
381 _print_BooleanTrue = _print_bool
382 _print_BooleanFalse = _print_bool
384 def _print_NoneType(self, e):
385 return r"\text{%s}" % e
387 def _print_Add(self, expr, order=None):
388 terms = self._as_ordered_terms(expr, order=order)
390 tex = ""
391 for i, term in enumerate(terms):
392 if i == 0:
393 pass
394 elif term.could_extract_minus_sign():
395 tex += " - "
396 term = -term
397 else:
398 tex += " + "
399 term_tex = self._print(term)
400 if self._needs_add_brackets(term):
401 term_tex = r"\left(%s\right)" % term_tex
402 tex += term_tex
404 return tex
406 def _print_Cycle(self, expr):
407 from sympy.combinatorics.permutations import Permutation
408 if expr.size == 0:
409 return r"\left( \right)"
410 expr = Permutation(expr)
411 expr_perm = expr.cyclic_form
412 siz = expr.size
413 if expr.array_form[-1] == siz - 1:
414 expr_perm = expr_perm + [[siz - 1]]
415 term_tex = ''
416 for i in expr_perm:
417 term_tex += str(i).replace(',', r"\;")
418 term_tex = term_tex.replace('[', r"\left( ")
419 term_tex = term_tex.replace(']', r"\right)")
420 return term_tex
422 def _print_Permutation(self, expr):
423 from sympy.combinatorics.permutations import Permutation
424 from sympy.utilities.exceptions import sympy_deprecation_warning
426 perm_cyclic = Permutation.print_cyclic
427 if perm_cyclic is not None:
428 sympy_deprecation_warning(
429 f"""
430 Setting Permutation.print_cyclic is deprecated. Instead use
431 init_printing(perm_cyclic={perm_cyclic}).
432 """,
433 deprecated_since_version="1.6",
434 active_deprecations_target="deprecated-permutation-print_cyclic",
435 stacklevel=8,
436 )
437 else:
438 perm_cyclic = self._settings.get("perm_cyclic", True)
440 if perm_cyclic:
441 return self._print_Cycle(expr)
443 if expr.size == 0:
444 return r"\left( \right)"
446 lower = [self._print(arg) for arg in expr.array_form]
447 upper = [self._print(arg) for arg in range(len(lower))]
449 row1 = " & ".join(upper)
450 row2 = " & ".join(lower)
451 mat = r" \\ ".join((row1, row2))
452 return r"\begin{pmatrix} %s \end{pmatrix}" % mat
455 def _print_AppliedPermutation(self, expr):
456 perm, var = expr.args
457 return r"\sigma_{%s}(%s)" % (self._print(perm), self._print(var))
459 def _print_Float(self, expr):
460 # Based off of that in StrPrinter
461 dps = prec_to_dps(expr._prec)
462 strip = False if self._settings['full_prec'] else True
463 low = self._settings["min"] if "min" in self._settings else None
464 high = self._settings["max"] if "max" in self._settings else None
465 str_real = mlib_to_str(expr._mpf_, dps, strip_zeros=strip, min_fixed=low, max_fixed=high)
467 # Must always have a mul symbol (as 2.5 10^{20} just looks odd)
468 # thus we use the number separator
469 separator = self._settings['mul_symbol_latex_numbers']
471 if 'e' in str_real:
472 (mant, exp) = str_real.split('e')
474 if exp[0] == '+':
475 exp = exp[1:]
476 if self._settings['decimal_separator'] == 'comma':
477 mant = mant.replace('.','{,}')
479 return r"%s%s10^{%s}" % (mant, separator, exp)
480 elif str_real == "+inf":
481 return r"\infty"
482 elif str_real == "-inf":
483 return r"- \infty"
484 else:
485 if self._settings['decimal_separator'] == 'comma':
486 str_real = str_real.replace('.','{,}')
487 return str_real
489 def _print_Cross(self, expr):
490 vec1 = expr._expr1
491 vec2 = expr._expr2
492 return r"%s \times %s" % (self.parenthesize(vec1, PRECEDENCE['Mul']),
493 self.parenthesize(vec2, PRECEDENCE['Mul']))
495 def _print_Curl(self, expr):
496 vec = expr._expr
497 return r"\nabla\times %s" % self.parenthesize(vec, PRECEDENCE['Mul'])
499 def _print_Divergence(self, expr):
500 vec = expr._expr
501 return r"\nabla\cdot %s" % self.parenthesize(vec, PRECEDENCE['Mul'])
503 def _print_Dot(self, expr):
504 vec1 = expr._expr1
505 vec2 = expr._expr2
506 return r"%s \cdot %s" % (self.parenthesize(vec1, PRECEDENCE['Mul']),
507 self.parenthesize(vec2, PRECEDENCE['Mul']))
509 def _print_Gradient(self, expr):
510 func = expr._expr
511 return r"\nabla %s" % self.parenthesize(func, PRECEDENCE['Mul'])
513 def _print_Laplacian(self, expr):
514 func = expr._expr
515 return r"\Delta %s" % self.parenthesize(func, PRECEDENCE['Mul'])
517 def _print_Mul(self, expr: Expr):
518 from sympy.simplify import fraction
519 separator: str = self._settings['mul_symbol_latex']
520 numbersep: str = self._settings['mul_symbol_latex_numbers']
522 def convert(expr) -> str:
523 if not expr.is_Mul:
524 return str(self._print(expr))
525 else:
526 if self.order not in ('old', 'none'):
527 args = expr.as_ordered_factors()
528 else:
529 args = list(expr.args)
531 # If there are quantities or prefixes, append them at the back.
532 units, nonunits = sift(args, lambda x: (hasattr(x, "_scale_factor") or hasattr(x, "is_physical_constant")) or
533 (isinstance(x, Pow) and
534 hasattr(x.base, "is_physical_constant")), binary=True)
535 prefixes, units = sift(units, lambda x: hasattr(x, "_scale_factor"), binary=True)
536 return convert_args(nonunits + prefixes + units)
538 def convert_args(args) -> str:
539 _tex = last_term_tex = ""
541 for i, term in enumerate(args):
542 term_tex = self._print(term)
543 if not (hasattr(term, "_scale_factor") or hasattr(term, "is_physical_constant")):
544 if self._needs_mul_brackets(term, first=(i == 0),
545 last=(i == len(args) - 1)):
546 term_tex = r"\left(%s\right)" % term_tex
548 if _between_two_numbers_p[0].search(last_term_tex) and \
549 _between_two_numbers_p[1].match(str(term)):
550 # between two numbers
551 _tex += numbersep
552 elif _tex:
553 _tex += separator
554 elif _tex:
555 _tex += separator
557 _tex += term_tex
558 last_term_tex = term_tex
559 return _tex
561 # Check for unevaluated Mul. In this case we need to make sure the
562 # identities are visible, multiple Rational factors are not combined
563 # etc so we display in a straight-forward form that fully preserves all
564 # args and their order.
565 # XXX: _print_Pow calls this routine with instances of Pow...
566 if isinstance(expr, Mul):
567 args = expr.args
568 if args[0] is S.One or any(isinstance(arg, Number) for arg in args[1:]):
569 return convert_args(args)
571 include_parens = False
572 if expr.could_extract_minus_sign():
573 expr = -expr
574 tex = "- "
575 if expr.is_Add:
576 tex += "("
577 include_parens = True
578 else:
579 tex = ""
581 numer, denom = fraction(expr, exact=True)
583 if denom is S.One and Pow(1, -1, evaluate=False) not in expr.args:
584 # use the original expression here, since fraction() may have
585 # altered it when producing numer and denom
586 tex += convert(expr)
588 else:
589 snumer = convert(numer)
590 sdenom = convert(denom)
591 ldenom = len(sdenom.split())
592 ratio = self._settings['long_frac_ratio']
593 if self._settings['fold_short_frac'] and ldenom <= 2 and \
594 "^" not in sdenom:
595 # handle short fractions
596 if self._needs_mul_brackets(numer, last=False):
597 tex += r"\left(%s\right) / %s" % (snumer, sdenom)
598 else:
599 tex += r"%s / %s" % (snumer, sdenom)
600 elif ratio is not None and \
601 len(snumer.split()) > ratio*ldenom:
602 # handle long fractions
603 if self._needs_mul_brackets(numer, last=True):
604 tex += r"\frac{1}{%s}%s\left(%s\right)" \
605 % (sdenom, separator, snumer)
606 elif numer.is_Mul:
607 # split a long numerator
608 a = S.One
609 b = S.One
610 for x in numer.args:
611 if self._needs_mul_brackets(x, last=False) or \
612 len(convert(a*x).split()) > ratio*ldenom or \
613 (b.is_commutative is x.is_commutative is False):
614 b *= x
615 else:
616 a *= x
617 if self._needs_mul_brackets(b, last=True):
618 tex += r"\frac{%s}{%s}%s\left(%s\right)" \
619 % (convert(a), sdenom, separator, convert(b))
620 else:
621 tex += r"\frac{%s}{%s}%s%s" \
622 % (convert(a), sdenom, separator, convert(b))
623 else:
624 tex += r"\frac{1}{%s}%s%s" % (sdenom, separator, snumer)
625 else:
626 tex += r"\frac{%s}{%s}" % (snumer, sdenom)
628 if include_parens:
629 tex += ")"
630 return tex
632 def _print_AlgebraicNumber(self, expr):
633 if expr.is_aliased:
634 return self._print(expr.as_poly().as_expr())
635 else:
636 return self._print(expr.as_expr())
638 def _print_PrimeIdeal(self, expr):
639 p = self._print(expr.p)
640 if expr.is_inert:
641 return rf'\left({p}\right)'
642 alpha = self._print(expr.alpha.as_expr())
643 return rf'\left({p}, {alpha}\right)'
645 def _print_Pow(self, expr: Pow):
646 # Treat x**Rational(1,n) as special case
647 if expr.exp.is_Rational:
648 p: int = expr.exp.p # type: ignore
649 q: int = expr.exp.q # type: ignore
650 if abs(p) == 1 and q != 1 and self._settings['root_notation']:
651 base = self._print(expr.base)
652 if q == 2:
653 tex = r"\sqrt{%s}" % base
654 elif self._settings['itex']:
655 tex = r"\root{%d}{%s}" % (q, base)
656 else:
657 tex = r"\sqrt[%d]{%s}" % (q, base)
658 if expr.exp.is_negative:
659 return r"\frac{1}{%s}" % tex
660 else:
661 return tex
662 elif self._settings['fold_frac_powers'] and q != 1:
663 base = self.parenthesize(expr.base, PRECEDENCE['Pow'])
664 # issue #12886: add parentheses for superscripts raised to powers
665 if expr.base.is_Symbol:
666 base = self.parenthesize_super(base)
667 if expr.base.is_Function:
668 return self._print(expr.base, exp="%s/%s" % (p, q))
669 return r"%s^{%s/%s}" % (base, p, q)
670 elif expr.exp.is_negative and expr.base.is_commutative:
671 # special case for 1^(-x), issue 9216
672 if expr.base == 1:
673 return r"%s^{%s}" % (expr.base, expr.exp)
674 # special case for (1/x)^(-y) and (-1/-x)^(-y), issue 20252
675 if expr.base.is_Rational:
676 base_p: int = expr.base.p # type: ignore
677 base_q: int = expr.base.q # type: ignore
678 if base_p * base_q == abs(base_q):
679 if expr.exp == -1:
680 return r"\frac{1}{\frac{%s}{%s}}" % (base_p, base_q)
681 else:
682 return r"\frac{1}{(\frac{%s}{%s})^{%s}}" % (base_p, base_q, abs(expr.exp))
683 # things like 1/x
684 return self._print_Mul(expr)
685 if expr.base.is_Function:
686 return self._print(expr.base, exp=self._print(expr.exp))
687 tex = r"%s^{%s}"
688 return self._helper_print_standard_power(expr, tex)
690 def _helper_print_standard_power(self, expr, template: str) -> str:
691 exp = self._print(expr.exp)
692 # issue #12886: add parentheses around superscripts raised
693 # to powers
694 base = self.parenthesize(expr.base, PRECEDENCE['Pow'])
695 if expr.base.is_Symbol:
696 base = self.parenthesize_super(base)
697 elif (isinstance(expr.base, Derivative)
698 and base.startswith(r'\left(')
699 and re.match(r'\\left\(\\d?d?dot', base)
700 and base.endswith(r'\right)')):
701 # don't use parentheses around dotted derivative
702 base = base[6: -7] # remove outermost added parens
703 return template % (base, exp)
705 def _print_UnevaluatedExpr(self, expr):
706 return self._print(expr.args[0])
708 def _print_Sum(self, expr):
709 if len(expr.limits) == 1:
710 tex = r"\sum_{%s=%s}^{%s} " % \
711 tuple([self._print(i) for i in expr.limits[0]])
712 else:
713 def _format_ineq(l):
714 return r"%s \leq %s \leq %s" % \
715 tuple([self._print(s) for s in (l[1], l[0], l[2])])
717 tex = r"\sum_{\substack{%s}} " % \
718 str.join('\\\\', [_format_ineq(l) for l in expr.limits])
720 if isinstance(expr.function, Add):
721 tex += r"\left(%s\right)" % self._print(expr.function)
722 else:
723 tex += self._print(expr.function)
725 return tex
727 def _print_Product(self, expr):
728 if len(expr.limits) == 1:
729 tex = r"\prod_{%s=%s}^{%s} " % \
730 tuple([self._print(i) for i in expr.limits[0]])
731 else:
732 def _format_ineq(l):
733 return r"%s \leq %s \leq %s" % \
734 tuple([self._print(s) for s in (l[1], l[0], l[2])])
736 tex = r"\prod_{\substack{%s}} " % \
737 str.join('\\\\', [_format_ineq(l) for l in expr.limits])
739 if isinstance(expr.function, Add):
740 tex += r"\left(%s\right)" % self._print(expr.function)
741 else:
742 tex += self._print(expr.function)
744 return tex
746 def _print_BasisDependent(self, expr: 'BasisDependent'):
747 from sympy.vector import Vector
749 o1: list[str] = []
750 if expr == expr.zero:
751 return expr.zero._latex_form
752 if isinstance(expr, Vector):
753 items = expr.separate().items()
754 else:
755 items = [(0, expr)]
757 for system, vect in items:
758 inneritems = list(vect.components.items())
759 inneritems.sort(key=lambda x: x[0].__str__())
760 for k, v in inneritems:
761 if v == 1:
762 o1.append(' + ' + k._latex_form)
763 elif v == -1:
764 o1.append(' - ' + k._latex_form)
765 else:
766 arg_str = r'\left(' + self._print(v) + r'\right)'
767 o1.append(' + ' + arg_str + k._latex_form)
769 outstr = (''.join(o1))
770 if outstr[1] != '-':
771 outstr = outstr[3:]
772 else:
773 outstr = outstr[1:]
774 return outstr
776 def _print_Indexed(self, expr):
777 tex_base = self._print(expr.base)
778 tex = '{'+tex_base+'}'+'_{%s}' % ','.join(
779 map(self._print, expr.indices))
780 return tex
782 def _print_IndexedBase(self, expr):
783 return self._print(expr.label)
785 def _print_Idx(self, expr):
786 label = self._print(expr.label)
787 if expr.upper is not None:
788 upper = self._print(expr.upper)
789 if expr.lower is not None:
790 lower = self._print(expr.lower)
791 else:
792 lower = self._print(S.Zero)
793 interval = '{lower}\\mathrel{{..}}\\nobreak {upper}'.format(
794 lower = lower, upper = upper)
795 return '{{{label}}}_{{{interval}}}'.format(
796 label = label, interval = interval)
797 #if no bounds are defined this just prints the label
798 return label
800 def _print_Derivative(self, expr):
801 if requires_partial(expr.expr):
802 diff_symbol = r'\partial'
803 else:
804 diff_symbol = self._settings["diff_operator_latex"]
806 tex = ""
807 dim = 0
808 for x, num in reversed(expr.variable_count):
809 dim += num
810 if num == 1:
811 tex += r"%s %s" % (diff_symbol, self._print(x))
812 else:
813 tex += r"%s %s^{%s}" % (diff_symbol,
814 self.parenthesize_super(self._print(x)),
815 self._print(num))
817 if dim == 1:
818 tex = r"\frac{%s}{%s}" % (diff_symbol, tex)
819 else:
820 tex = r"\frac{%s^{%s}}{%s}" % (diff_symbol, self._print(dim), tex)
822 if any(i.could_extract_minus_sign() for i in expr.args):
823 return r"%s %s" % (tex, self.parenthesize(expr.expr,
824 PRECEDENCE["Mul"],
825 is_neg=True,
826 strict=True))
828 return r"%s %s" % (tex, self.parenthesize(expr.expr,
829 PRECEDENCE["Mul"],
830 is_neg=False,
831 strict=True))
833 def _print_Subs(self, subs):
834 expr, old, new = subs.args
835 latex_expr = self._print(expr)
836 latex_old = (self._print(e) for e in old)
837 latex_new = (self._print(e) for e in new)
838 latex_subs = r'\\ '.join(
839 e[0] + '=' + e[1] for e in zip(latex_old, latex_new))
840 return r'\left. %s \right|_{\substack{ %s }}' % (latex_expr,
841 latex_subs)
843 def _print_Integral(self, expr):
844 tex, symbols = "", []
845 diff_symbol = self._settings["diff_operator_latex"]
847 # Only up to \iiiint exists
848 if len(expr.limits) <= 4 and all(len(lim) == 1 for lim in expr.limits):
849 # Use len(expr.limits)-1 so that syntax highlighters don't think
850 # \" is an escaped quote
851 tex = r"\i" + "i"*(len(expr.limits) - 1) + "nt"
852 symbols = [r"\, %s%s" % (diff_symbol, self._print(symbol[0]))
853 for symbol in expr.limits]
855 else:
856 for lim in reversed(expr.limits):
857 symbol = lim[0]
858 tex += r"\int"
860 if len(lim) > 1:
861 if self._settings['mode'] != 'inline' \
862 and not self._settings['itex']:
863 tex += r"\limits"
865 if len(lim) == 3:
866 tex += "_{%s}^{%s}" % (self._print(lim[1]),
867 self._print(lim[2]))
868 if len(lim) == 2:
869 tex += "^{%s}" % (self._print(lim[1]))
871 symbols.insert(0, r"\, %s%s" % (diff_symbol, self._print(symbol)))
873 return r"%s %s%s" % (tex, self.parenthesize(expr.function,
874 PRECEDENCE["Mul"],
875 is_neg=any(i.could_extract_minus_sign() for i in expr.args),
876 strict=True),
877 "".join(symbols))
879 def _print_Limit(self, expr):
880 e, z, z0, dir = expr.args
882 tex = r"\lim_{%s \to " % self._print(z)
883 if str(dir) == '+-' or z0 in (S.Infinity, S.NegativeInfinity):
884 tex += r"%s}" % self._print(z0)
885 else:
886 tex += r"%s^%s}" % (self._print(z0), self._print(dir))
888 if isinstance(e, AssocOp):
889 return r"%s\left(%s\right)" % (tex, self._print(e))
890 else:
891 return r"%s %s" % (tex, self._print(e))
893 def _hprint_Function(self, func: str) -> str:
894 r'''
895 Logic to decide how to render a function to latex
896 - if it is a recognized latex name, use the appropriate latex command
897 - if it is a single letter, excluding sub- and superscripts, just use that letter
898 - if it is a longer name, then put \operatorname{} around it and be
899 mindful of undercores in the name
900 '''
901 func = self._deal_with_super_sub(func)
902 superscriptidx = func.find("^")
903 subscriptidx = func.find("_")
904 if func in accepted_latex_functions:
905 name = r"\%s" % func
906 elif len(func) == 1 or func.startswith('\\') or subscriptidx == 1 or superscriptidx == 1:
907 name = func
908 else:
909 if superscriptidx > 0 and subscriptidx > 0:
910 name = r"\operatorname{%s}%s" %(
911 func[:min(subscriptidx,superscriptidx)],
912 func[min(subscriptidx,superscriptidx):])
913 elif superscriptidx > 0:
914 name = r"\operatorname{%s}%s" %(
915 func[:superscriptidx],
916 func[superscriptidx:])
917 elif subscriptidx > 0:
918 name = r"\operatorname{%s}%s" %(
919 func[:subscriptidx],
920 func[subscriptidx:])
921 else:
922 name = r"\operatorname{%s}" % func
923 return name
925 def _print_Function(self, expr: Function, exp=None) -> str:
926 r'''
927 Render functions to LaTeX, handling functions that LaTeX knows about
928 e.g., sin, cos, ... by using the proper LaTeX command (\sin, \cos, ...).
929 For single-letter function names, render them as regular LaTeX math
930 symbols. For multi-letter function names that LaTeX does not know
931 about, (e.g., Li, sech) use \operatorname{} so that the function name
932 is rendered in Roman font and LaTeX handles spacing properly.
934 expr is the expression involving the function
935 exp is an exponent
936 '''
937 func = expr.func.__name__
938 if hasattr(self, '_print_' + func) and \
939 not isinstance(expr, AppliedUndef):
940 return getattr(self, '_print_' + func)(expr, exp)
941 else:
942 args = [str(self._print(arg)) for arg in expr.args]
943 # How inverse trig functions should be displayed, formats are:
944 # abbreviated: asin, full: arcsin, power: sin^-1
945 inv_trig_style = self._settings['inv_trig_style']
946 # If we are dealing with a power-style inverse trig function
947 inv_trig_power_case = False
948 # If it is applicable to fold the argument brackets
949 can_fold_brackets = self._settings['fold_func_brackets'] and \
950 len(args) == 1 and \
951 not self._needs_function_brackets(expr.args[0])
953 inv_trig_table = [
954 "asin", "acos", "atan",
955 "acsc", "asec", "acot",
956 "asinh", "acosh", "atanh",
957 "acsch", "asech", "acoth",
958 ]
960 # If the function is an inverse trig function, handle the style
961 if func in inv_trig_table:
962 if inv_trig_style == "abbreviated":
963 pass
964 elif inv_trig_style == "full":
965 func = ("ar" if func[-1] == "h" else "arc") + func[1:]
966 elif inv_trig_style == "power":
967 func = func[1:]
968 inv_trig_power_case = True
970 # Can never fold brackets if we're raised to a power
971 if exp is not None:
972 can_fold_brackets = False
974 if inv_trig_power_case:
975 if func in accepted_latex_functions:
976 name = r"\%s^{-1}" % func
977 else:
978 name = r"\operatorname{%s}^{-1}" % func
979 elif exp is not None:
980 func_tex = self._hprint_Function(func)
981 func_tex = self.parenthesize_super(func_tex)
982 name = r'%s^{%s}' % (func_tex, exp)
983 else:
984 name = self._hprint_Function(func)
986 if can_fold_brackets:
987 if func in accepted_latex_functions:
988 # Wrap argument safely to avoid parse-time conflicts
989 # with the function name itself
990 name += r" {%s}"
991 else:
992 name += r"%s"
993 else:
994 name += r"{\left(%s \right)}"
996 if inv_trig_power_case and exp is not None:
997 name += r"^{%s}" % exp
999 return name % ",".join(args)
1001 def _print_UndefinedFunction(self, expr):
1002 return self._hprint_Function(str(expr))
1004 def _print_ElementwiseApplyFunction(self, expr):
1005 return r"{%s}_{\circ}\left({%s}\right)" % (
1006 self._print(expr.function),
1007 self._print(expr.expr),
1008 )
1010 @property
1011 def _special_function_classes(self):
1012 from sympy.functions.special.tensor_functions import KroneckerDelta
1013 from sympy.functions.special.gamma_functions import gamma, lowergamma
1014 from sympy.functions.special.beta_functions import beta
1015 from sympy.functions.special.delta_functions import DiracDelta
1016 from sympy.functions.special.error_functions import Chi
1017 return {KroneckerDelta: r'\delta',
1018 gamma: r'\Gamma',
1019 lowergamma: r'\gamma',
1020 beta: r'\operatorname{B}',
1021 DiracDelta: r'\delta',
1022 Chi: r'\operatorname{Chi}'}
1024 def _print_FunctionClass(self, expr):
1025 for cls in self._special_function_classes:
1026 if issubclass(expr, cls) and expr.__name__ == cls.__name__:
1027 return self._special_function_classes[cls]
1028 return self._hprint_Function(str(expr))
1030 def _print_Lambda(self, expr):
1031 symbols, expr = expr.args
1033 if len(symbols) == 1:
1034 symbols = self._print(symbols[0])
1035 else:
1036 symbols = self._print(tuple(symbols))
1038 tex = r"\left( %s \mapsto %s \right)" % (symbols, self._print(expr))
1040 return tex
1042 def _print_IdentityFunction(self, expr):
1043 return r"\left( x \mapsto x \right)"
1045 def _hprint_variadic_function(self, expr, exp=None) -> str:
1046 args = sorted(expr.args, key=default_sort_key)
1047 texargs = [r"%s" % self._print(symbol) for symbol in args]
1048 tex = r"\%s\left(%s\right)" % (str(expr.func).lower(),
1049 ", ".join(texargs))
1050 if exp is not None:
1051 return r"%s^{%s}" % (tex, exp)
1052 else:
1053 return tex
1055 _print_Min = _print_Max = _hprint_variadic_function
1057 def _print_floor(self, expr, exp=None):
1058 tex = r"\left\lfloor{%s}\right\rfloor" % self._print(expr.args[0])
1060 if exp is not None:
1061 return r"%s^{%s}" % (tex, exp)
1062 else:
1063 return tex
1065 def _print_ceiling(self, expr, exp=None):
1066 tex = r"\left\lceil{%s}\right\rceil" % self._print(expr.args[0])
1068 if exp is not None:
1069 return r"%s^{%s}" % (tex, exp)
1070 else:
1071 return tex
1073 def _print_log(self, expr, exp=None):
1074 if not self._settings["ln_notation"]:
1075 tex = r"\log{\left(%s \right)}" % self._print(expr.args[0])
1076 else:
1077 tex = r"\ln{\left(%s \right)}" % self._print(expr.args[0])
1079 if exp is not None:
1080 return r"%s^{%s}" % (tex, exp)
1081 else:
1082 return tex
1084 def _print_Abs(self, expr, exp=None):
1085 tex = r"\left|{%s}\right|" % self._print(expr.args[0])
1087 if exp is not None:
1088 return r"%s^{%s}" % (tex, exp)
1089 else:
1090 return tex
1092 def _print_re(self, expr, exp=None):
1093 if self._settings['gothic_re_im']:
1094 tex = r"\Re{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Atom'])
1095 else:
1096 tex = r"\operatorname{{re}}{{{}}}".format(self.parenthesize(expr.args[0], PRECEDENCE['Atom']))
1098 return self._do_exponent(tex, exp)
1100 def _print_im(self, expr, exp=None):
1101 if self._settings['gothic_re_im']:
1102 tex = r"\Im{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Atom'])
1103 else:
1104 tex = r"\operatorname{{im}}{{{}}}".format(self.parenthesize(expr.args[0], PRECEDENCE['Atom']))
1106 return self._do_exponent(tex, exp)
1108 def _print_Not(self, e):
1109 from sympy.logic.boolalg import (Equivalent, Implies)
1110 if isinstance(e.args[0], Equivalent):
1111 return self._print_Equivalent(e.args[0], r"\not\Leftrightarrow")
1112 if isinstance(e.args[0], Implies):
1113 return self._print_Implies(e.args[0], r"\not\Rightarrow")
1114 if (e.args[0].is_Boolean):
1115 return r"\neg \left(%s\right)" % self._print(e.args[0])
1116 else:
1117 return r"\neg %s" % self._print(e.args[0])
1119 def _print_LogOp(self, args, char):
1120 arg = args[0]
1121 if arg.is_Boolean and not arg.is_Not:
1122 tex = r"\left(%s\right)" % self._print(arg)
1123 else:
1124 tex = r"%s" % self._print(arg)
1126 for arg in args[1:]:
1127 if arg.is_Boolean and not arg.is_Not:
1128 tex += r" %s \left(%s\right)" % (char, self._print(arg))
1129 else:
1130 tex += r" %s %s" % (char, self._print(arg))
1132 return tex
1134 def _print_And(self, e):
1135 args = sorted(e.args, key=default_sort_key)
1136 return self._print_LogOp(args, r"\wedge")
1138 def _print_Or(self, e):
1139 args = sorted(e.args, key=default_sort_key)
1140 return self._print_LogOp(args, r"\vee")
1142 def _print_Xor(self, e):
1143 args = sorted(e.args, key=default_sort_key)
1144 return self._print_LogOp(args, r"\veebar")
1146 def _print_Implies(self, e, altchar=None):
1147 return self._print_LogOp(e.args, altchar or r"\Rightarrow")
1149 def _print_Equivalent(self, e, altchar=None):
1150 args = sorted(e.args, key=default_sort_key)
1151 return self._print_LogOp(args, altchar or r"\Leftrightarrow")
1153 def _print_conjugate(self, expr, exp=None):
1154 tex = r"\overline{%s}" % self._print(expr.args[0])
1156 if exp is not None:
1157 return r"%s^{%s}" % (tex, exp)
1158 else:
1159 return tex
1161 def _print_polar_lift(self, expr, exp=None):
1162 func = r"\operatorname{polar\_lift}"
1163 arg = r"{\left(%s \right)}" % self._print(expr.args[0])
1165 if exp is not None:
1166 return r"%s^{%s}%s" % (func, exp, arg)
1167 else:
1168 return r"%s%s" % (func, arg)
1170 def _print_ExpBase(self, expr, exp=None):
1171 # TODO should exp_polar be printed differently?
1172 # what about exp_polar(0), exp_polar(1)?
1173 tex = r"e^{%s}" % self._print(expr.args[0])
1174 return self._do_exponent(tex, exp)
1176 def _print_Exp1(self, expr, exp=None):
1177 return "e"
1179 def _print_elliptic_k(self, expr, exp=None):
1180 tex = r"\left(%s\right)" % self._print(expr.args[0])
1181 if exp is not None:
1182 return r"K^{%s}%s" % (exp, tex)
1183 else:
1184 return r"K%s" % tex
1186 def _print_elliptic_f(self, expr, exp=None):
1187 tex = r"\left(%s\middle| %s\right)" % \
1188 (self._print(expr.args[0]), self._print(expr.args[1]))
1189 if exp is not None:
1190 return r"F^{%s}%s" % (exp, tex)
1191 else:
1192 return r"F%s" % tex
1194 def _print_elliptic_e(self, expr, exp=None):
1195 if len(expr.args) == 2:
1196 tex = r"\left(%s\middle| %s\right)" % \
1197 (self._print(expr.args[0]), self._print(expr.args[1]))
1198 else:
1199 tex = r"\left(%s\right)" % self._print(expr.args[0])
1200 if exp is not None:
1201 return r"E^{%s}%s" % (exp, tex)
1202 else:
1203 return r"E%s" % tex
1205 def _print_elliptic_pi(self, expr, exp=None):
1206 if len(expr.args) == 3:
1207 tex = r"\left(%s; %s\middle| %s\right)" % \
1208 (self._print(expr.args[0]), self._print(expr.args[1]),
1209 self._print(expr.args[2]))
1210 else:
1211 tex = r"\left(%s\middle| %s\right)" % \
1212 (self._print(expr.args[0]), self._print(expr.args[1]))
1213 if exp is not None:
1214 return r"\Pi^{%s}%s" % (exp, tex)
1215 else:
1216 return r"\Pi%s" % tex
1218 def _print_beta(self, expr, exp=None):
1219 x = expr.args[0]
1220 # Deal with unevaluated single argument beta
1221 y = expr.args[0] if len(expr.args) == 1 else expr.args[1]
1222 tex = rf"\left({x}, {y}\right)"
1224 if exp is not None:
1225 return r"\operatorname{B}^{%s}%s" % (exp, tex)
1226 else:
1227 return r"\operatorname{B}%s" % tex
1229 def _print_betainc(self, expr, exp=None, operator='B'):
1230 largs = [self._print(arg) for arg in expr.args]
1231 tex = r"\left(%s, %s\right)" % (largs[0], largs[1])
1233 if exp is not None:
1234 return r"\operatorname{%s}_{(%s, %s)}^{%s}%s" % (operator, largs[2], largs[3], exp, tex)
1235 else:
1236 return r"\operatorname{%s}_{(%s, %s)}%s" % (operator, largs[2], largs[3], tex)
1238 def _print_betainc_regularized(self, expr, exp=None):
1239 return self._print_betainc(expr, exp, operator='I')
1241 def _print_uppergamma(self, expr, exp=None):
1242 tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]),
1243 self._print(expr.args[1]))
1245 if exp is not None:
1246 return r"\Gamma^{%s}%s" % (exp, tex)
1247 else:
1248 return r"\Gamma%s" % tex
1250 def _print_lowergamma(self, expr, exp=None):
1251 tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]),
1252 self._print(expr.args[1]))
1254 if exp is not None:
1255 return r"\gamma^{%s}%s" % (exp, tex)
1256 else:
1257 return r"\gamma%s" % tex
1259 def _hprint_one_arg_func(self, expr, exp=None) -> str:
1260 tex = r"\left(%s\right)" % self._print(expr.args[0])
1262 if exp is not None:
1263 return r"%s^{%s}%s" % (self._print(expr.func), exp, tex)
1264 else:
1265 return r"%s%s" % (self._print(expr.func), tex)
1267 _print_gamma = _hprint_one_arg_func
1269 def _print_Chi(self, expr, exp=None):
1270 tex = r"\left(%s\right)" % self._print(expr.args[0])
1272 if exp is not None:
1273 return r"\operatorname{Chi}^{%s}%s" % (exp, tex)
1274 else:
1275 return r"\operatorname{Chi}%s" % tex
1277 def _print_expint(self, expr, exp=None):
1278 tex = r"\left(%s\right)" % self._print(expr.args[1])
1279 nu = self._print(expr.args[0])
1281 if exp is not None:
1282 return r"\operatorname{E}_{%s}^{%s}%s" % (nu, exp, tex)
1283 else:
1284 return r"\operatorname{E}_{%s}%s" % (nu, tex)
1286 def _print_fresnels(self, expr, exp=None):
1287 tex = r"\left(%s\right)" % self._print(expr.args[0])
1289 if exp is not None:
1290 return r"S^{%s}%s" % (exp, tex)
1291 else:
1292 return r"S%s" % tex
1294 def _print_fresnelc(self, expr, exp=None):
1295 tex = r"\left(%s\right)" % self._print(expr.args[0])
1297 if exp is not None:
1298 return r"C^{%s}%s" % (exp, tex)
1299 else:
1300 return r"C%s" % tex
1302 def _print_subfactorial(self, expr, exp=None):
1303 tex = r"!%s" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
1305 if exp is not None:
1306 return r"\left(%s\right)^{%s}" % (tex, exp)
1307 else:
1308 return tex
1310 def _print_factorial(self, expr, exp=None):
1311 tex = r"%s!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
1313 if exp is not None:
1314 return r"%s^{%s}" % (tex, exp)
1315 else:
1316 return tex
1318 def _print_factorial2(self, expr, exp=None):
1319 tex = r"%s!!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
1321 if exp is not None:
1322 return r"%s^{%s}" % (tex, exp)
1323 else:
1324 return tex
1326 def _print_binomial(self, expr, exp=None):
1327 tex = r"{\binom{%s}{%s}}" % (self._print(expr.args[0]),
1328 self._print(expr.args[1]))
1330 if exp is not None:
1331 return r"%s^{%s}" % (tex, exp)
1332 else:
1333 return tex
1335 def _print_RisingFactorial(self, expr, exp=None):
1336 n, k = expr.args
1337 base = r"%s" % self.parenthesize(n, PRECEDENCE['Func'])
1339 tex = r"{%s}^{\left(%s\right)}" % (base, self._print(k))
1341 return self._do_exponent(tex, exp)
1343 def _print_FallingFactorial(self, expr, exp=None):
1344 n, k = expr.args
1345 sub = r"%s" % self.parenthesize(k, PRECEDENCE['Func'])
1347 tex = r"{\left(%s\right)}_{%s}" % (self._print(n), sub)
1349 return self._do_exponent(tex, exp)
1351 def _hprint_BesselBase(self, expr, exp, sym: str) -> str:
1352 tex = r"%s" % (sym)
1354 need_exp = False
1355 if exp is not None:
1356 if tex.find('^') == -1:
1357 tex = r"%s^{%s}" % (tex, exp)
1358 else:
1359 need_exp = True
1361 tex = r"%s_{%s}\left(%s\right)" % (tex, self._print(expr.order),
1362 self._print(expr.argument))
1364 if need_exp:
1365 tex = self._do_exponent(tex, exp)
1366 return tex
1368 def _hprint_vec(self, vec) -> str:
1369 if not vec:
1370 return ""
1371 s = ""
1372 for i in vec[:-1]:
1373 s += "%s, " % self._print(i)
1374 s += self._print(vec[-1])
1375 return s
1377 def _print_besselj(self, expr, exp=None):
1378 return self._hprint_BesselBase(expr, exp, 'J')
1380 def _print_besseli(self, expr, exp=None):
1381 return self._hprint_BesselBase(expr, exp, 'I')
1383 def _print_besselk(self, expr, exp=None):
1384 return self._hprint_BesselBase(expr, exp, 'K')
1386 def _print_bessely(self, expr, exp=None):
1387 return self._hprint_BesselBase(expr, exp, 'Y')
1389 def _print_yn(self, expr, exp=None):
1390 return self._hprint_BesselBase(expr, exp, 'y')
1392 def _print_jn(self, expr, exp=None):
1393 return self._hprint_BesselBase(expr, exp, 'j')
1395 def _print_hankel1(self, expr, exp=None):
1396 return self._hprint_BesselBase(expr, exp, 'H^{(1)}')
1398 def _print_hankel2(self, expr, exp=None):
1399 return self._hprint_BesselBase(expr, exp, 'H^{(2)}')
1401 def _print_hn1(self, expr, exp=None):
1402 return self._hprint_BesselBase(expr, exp, 'h^{(1)}')
1404 def _print_hn2(self, expr, exp=None):
1405 return self._hprint_BesselBase(expr, exp, 'h^{(2)}')
1407 def _hprint_airy(self, expr, exp=None, notation="") -> str:
1408 tex = r"\left(%s\right)" % self._print(expr.args[0])
1410 if exp is not None:
1411 return r"%s^{%s}%s" % (notation, exp, tex)
1412 else:
1413 return r"%s%s" % (notation, tex)
1415 def _hprint_airy_prime(self, expr, exp=None, notation="") -> str:
1416 tex = r"\left(%s\right)" % self._print(expr.args[0])
1418 if exp is not None:
1419 return r"{%s^\prime}^{%s}%s" % (notation, exp, tex)
1420 else:
1421 return r"%s^\prime%s" % (notation, tex)
1423 def _print_airyai(self, expr, exp=None):
1424 return self._hprint_airy(expr, exp, 'Ai')
1426 def _print_airybi(self, expr, exp=None):
1427 return self._hprint_airy(expr, exp, 'Bi')
1429 def _print_airyaiprime(self, expr, exp=None):
1430 return self._hprint_airy_prime(expr, exp, 'Ai')
1432 def _print_airybiprime(self, expr, exp=None):
1433 return self._hprint_airy_prime(expr, exp, 'Bi')
1435 def _print_hyper(self, expr, exp=None):
1436 tex = r"{{}_{%s}F_{%s}\left(\begin{matrix} %s \\ %s \end{matrix}" \
1437 r"\middle| {%s} \right)}" % \
1438 (self._print(len(expr.ap)), self._print(len(expr.bq)),
1439 self._hprint_vec(expr.ap), self._hprint_vec(expr.bq),
1440 self._print(expr.argument))
1442 if exp is not None:
1443 tex = r"{%s}^{%s}" % (tex, exp)
1444 return tex
1446 def _print_meijerg(self, expr, exp=None):
1447 tex = r"{G_{%s, %s}^{%s, %s}\left(\begin{matrix} %s & %s \\" \
1448 r"%s & %s \end{matrix} \middle| {%s} \right)}" % \
1449 (self._print(len(expr.ap)), self._print(len(expr.bq)),
1450 self._print(len(expr.bm)), self._print(len(expr.an)),
1451 self._hprint_vec(expr.an), self._hprint_vec(expr.aother),
1452 self._hprint_vec(expr.bm), self._hprint_vec(expr.bother),
1453 self._print(expr.argument))
1455 if exp is not None:
1456 tex = r"{%s}^{%s}" % (tex, exp)
1457 return tex
1459 def _print_dirichlet_eta(self, expr, exp=None):
1460 tex = r"\left(%s\right)" % self._print(expr.args[0])
1461 if exp is not None:
1462 return r"\eta^{%s}%s" % (exp, tex)
1463 return r"\eta%s" % tex
1465 def _print_zeta(self, expr, exp=None):
1466 if len(expr.args) == 2:
1467 tex = r"\left(%s, %s\right)" % tuple(map(self._print, expr.args))
1468 else:
1469 tex = r"\left(%s\right)" % self._print(expr.args[0])
1470 if exp is not None:
1471 return r"\zeta^{%s}%s" % (exp, tex)
1472 return r"\zeta%s" % tex
1474 def _print_stieltjes(self, expr, exp=None):
1475 if len(expr.args) == 2:
1476 tex = r"_{%s}\left(%s\right)" % tuple(map(self._print, expr.args))
1477 else:
1478 tex = r"_{%s}" % self._print(expr.args[0])
1479 if exp is not None:
1480 return r"\gamma%s^{%s}" % (tex, exp)
1481 return r"\gamma%s" % tex
1483 def _print_lerchphi(self, expr, exp=None):
1484 tex = r"\left(%s, %s, %s\right)" % tuple(map(self._print, expr.args))
1485 if exp is None:
1486 return r"\Phi%s" % tex
1487 return r"\Phi^{%s}%s" % (exp, tex)
1489 def _print_polylog(self, expr, exp=None):
1490 s, z = map(self._print, expr.args)
1491 tex = r"\left(%s\right)" % z
1492 if exp is None:
1493 return r"\operatorname{Li}_{%s}%s" % (s, tex)
1494 return r"\operatorname{Li}_{%s}^{%s}%s" % (s, exp, tex)
1496 def _print_jacobi(self, expr, exp=None):
1497 n, a, b, x = map(self._print, expr.args)
1498 tex = r"P_{%s}^{\left(%s,%s\right)}\left(%s\right)" % (n, a, b, x)
1499 if exp is not None:
1500 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1501 return tex
1503 def _print_gegenbauer(self, expr, exp=None):
1504 n, a, x = map(self._print, expr.args)
1505 tex = r"C_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x)
1506 if exp is not None:
1507 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1508 return tex
1510 def _print_chebyshevt(self, expr, exp=None):
1511 n, x = map(self._print, expr.args)
1512 tex = r"T_{%s}\left(%s\right)" % (n, x)
1513 if exp is not None:
1514 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1515 return tex
1517 def _print_chebyshevu(self, expr, exp=None):
1518 n, x = map(self._print, expr.args)
1519 tex = r"U_{%s}\left(%s\right)" % (n, x)
1520 if exp is not None:
1521 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1522 return tex
1524 def _print_legendre(self, expr, exp=None):
1525 n, x = map(self._print, expr.args)
1526 tex = r"P_{%s}\left(%s\right)" % (n, x)
1527 if exp is not None:
1528 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1529 return tex
1531 def _print_assoc_legendre(self, expr, exp=None):
1532 n, a, x = map(self._print, expr.args)
1533 tex = r"P_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x)
1534 if exp is not None:
1535 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1536 return tex
1538 def _print_hermite(self, expr, exp=None):
1539 n, x = map(self._print, expr.args)
1540 tex = r"H_{%s}\left(%s\right)" % (n, x)
1541 if exp is not None:
1542 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1543 return tex
1545 def _print_laguerre(self, expr, exp=None):
1546 n, x = map(self._print, expr.args)
1547 tex = r"L_{%s}\left(%s\right)" % (n, x)
1548 if exp is not None:
1549 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1550 return tex
1552 def _print_assoc_laguerre(self, expr, exp=None):
1553 n, a, x = map(self._print, expr.args)
1554 tex = r"L_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x)
1555 if exp is not None:
1556 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1557 return tex
1559 def _print_Ynm(self, expr, exp=None):
1560 n, m, theta, phi = map(self._print, expr.args)
1561 tex = r"Y_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi)
1562 if exp is not None:
1563 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1564 return tex
1566 def _print_Znm(self, expr, exp=None):
1567 n, m, theta, phi = map(self._print, expr.args)
1568 tex = r"Z_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi)
1569 if exp is not None:
1570 tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
1571 return tex
1573 def __print_mathieu_functions(self, character, args, prime=False, exp=None):
1574 a, q, z = map(self._print, args)
1575 sup = r"^{\prime}" if prime else ""
1576 exp = "" if not exp else "^{%s}" % exp
1577 return r"%s%s\left(%s, %s, %s\right)%s" % (character, sup, a, q, z, exp)
1579 def _print_mathieuc(self, expr, exp=None):
1580 return self.__print_mathieu_functions("C", expr.args, exp=exp)
1582 def _print_mathieus(self, expr, exp=None):
1583 return self.__print_mathieu_functions("S", expr.args, exp=exp)
1585 def _print_mathieucprime(self, expr, exp=None):
1586 return self.__print_mathieu_functions("C", expr.args, prime=True, exp=exp)
1588 def _print_mathieusprime(self, expr, exp=None):
1589 return self.__print_mathieu_functions("S", expr.args, prime=True, exp=exp)
1591 def _print_Rational(self, expr):
1592 if expr.q != 1:
1593 sign = ""
1594 p = expr.p
1595 if expr.p < 0:
1596 sign = "- "
1597 p = -p
1598 if self._settings['fold_short_frac']:
1599 return r"%s%d / %d" % (sign, p, expr.q)
1600 return r"%s\frac{%d}{%d}" % (sign, p, expr.q)
1601 else:
1602 return self._print(expr.p)
1604 def _print_Order(self, expr):
1605 s = self._print(expr.expr)
1606 if expr.point and any(p != S.Zero for p in expr.point) or \
1607 len(expr.variables) > 1:
1608 s += '; '
1609 if len(expr.variables) > 1:
1610 s += self._print(expr.variables)
1611 elif expr.variables:
1612 s += self._print(expr.variables[0])
1613 s += r'\rightarrow '
1614 if len(expr.point) > 1:
1615 s += self._print(expr.point)
1616 else:
1617 s += self._print(expr.point[0])
1618 return r"O\left(%s\right)" % s
1620 def _print_Symbol(self, expr: Symbol, style='plain'):
1621 name: str = self._settings['symbol_names'].get(expr)
1622 if name is not None:
1623 return name
1625 return self._deal_with_super_sub(expr.name, style=style)
1627 _print_RandomSymbol = _print_Symbol
1629 def _deal_with_super_sub(self, string: str, style='plain') -> str:
1630 if '{' in string:
1631 name, supers, subs = string, [], []
1632 else:
1633 name, supers, subs = split_super_sub(string)
1635 name = translate(name)
1636 supers = [translate(sup) for sup in supers]
1637 subs = [translate(sub) for sub in subs]
1639 # apply the style only to the name
1640 if style == 'bold':
1641 name = "\\mathbf{{{}}}".format(name)
1643 # glue all items together:
1644 if supers:
1645 name += "^{%s}" % " ".join(supers)
1646 if subs:
1647 name += "_{%s}" % " ".join(subs)
1649 return name
1651 def _print_Relational(self, expr):
1652 if self._settings['itex']:
1653 gt = r"\gt"
1654 lt = r"\lt"
1655 else:
1656 gt = ">"
1657 lt = "<"
1659 charmap = {
1660 "==": "=",
1661 ">": gt,
1662 "<": lt,
1663 ">=": r"\geq",
1664 "<=": r"\leq",
1665 "!=": r"\neq",
1666 }
1668 return "%s %s %s" % (self._print(expr.lhs),
1669 charmap[expr.rel_op], self._print(expr.rhs))
1671 def _print_Piecewise(self, expr):
1672 ecpairs = [r"%s & \text{for}\: %s" % (self._print(e), self._print(c))
1673 for e, c in expr.args[:-1]]
1674 if expr.args[-1].cond == true:
1675 ecpairs.append(r"%s & \text{otherwise}" %
1676 self._print(expr.args[-1].expr))
1677 else:
1678 ecpairs.append(r"%s & \text{for}\: %s" %
1679 (self._print(expr.args[-1].expr),
1680 self._print(expr.args[-1].cond)))
1681 tex = r"\begin{cases} %s \end{cases}"
1682 return tex % r" \\".join(ecpairs)
1684 def _print_matrix_contents(self, expr):
1685 lines = []
1687 for line in range(expr.rows): # horrible, should be 'rows'
1688 lines.append(" & ".join([self._print(i) for i in expr[line, :]]))
1690 mat_str = self._settings['mat_str']
1691 if mat_str is None:
1692 if self._settings['mode'] == 'inline':
1693 mat_str = 'smallmatrix'
1694 else:
1695 if (expr.cols <= 10) is True:
1696 mat_str = 'matrix'
1697 else:
1698 mat_str = 'array'
1700 out_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}'
1701 out_str = out_str.replace('%MATSTR%', mat_str)
1702 if mat_str == 'array':
1703 out_str = out_str.replace('%s', '{' + 'c'*expr.cols + '}%s')
1704 return out_str % r"\\".join(lines)
1706 def _print_MatrixBase(self, expr):
1707 out_str = self._print_matrix_contents(expr)
1708 if self._settings['mat_delim']:
1709 left_delim = self._settings['mat_delim']
1710 right_delim = self._delim_dict[left_delim]
1711 out_str = r'\left' + left_delim + out_str + \
1712 r'\right' + right_delim
1713 return out_str
1715 def _print_MatrixElement(self, expr):
1716 return self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True)\
1717 + '_{%s, %s}' % (self._print(expr.i), self._print(expr.j))
1719 def _print_MatrixSlice(self, expr):
1720 def latexslice(x, dim):
1721 x = list(x)
1722 if x[2] == 1:
1723 del x[2]
1724 if x[0] == 0:
1725 x[0] = None
1726 if x[1] == dim:
1727 x[1] = None
1728 return ':'.join(self._print(xi) if xi is not None else '' for xi in x)
1729 return (self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) + r'\left[' +
1730 latexslice(expr.rowslice, expr.parent.rows) + ', ' +
1731 latexslice(expr.colslice, expr.parent.cols) + r'\right]')
1733 def _print_BlockMatrix(self, expr):
1734 return self._print(expr.blocks)
1736 def _print_Transpose(self, expr):
1737 mat = expr.arg
1738 from sympy.matrices import MatrixSymbol, BlockMatrix
1739 if (not isinstance(mat, MatrixSymbol) and
1740 not isinstance(mat, BlockMatrix) and mat.is_MatrixExpr):
1741 return r"\left(%s\right)^{T}" % self._print(mat)
1742 else:
1743 s = self.parenthesize(mat, precedence_traditional(expr), True)
1744 if '^' in s:
1745 return r"\left(%s\right)^{T}" % s
1746 else:
1747 return "%s^{T}" % s
1749 def _print_Trace(self, expr):
1750 mat = expr.arg
1751 return r"\operatorname{tr}\left(%s \right)" % self._print(mat)
1753 def _print_Adjoint(self, expr):
1754 mat = expr.arg
1755 from sympy.matrices import MatrixSymbol, BlockMatrix
1756 if (not isinstance(mat, MatrixSymbol) and
1757 not isinstance(mat, BlockMatrix) and mat.is_MatrixExpr):
1758 return r"\left(%s\right)^{\dagger}" % self._print(mat)
1759 else:
1760 s = self.parenthesize(mat, precedence_traditional(expr), True)
1761 if '^' in s:
1762 return r"\left(%s\right)^{\dagger}" % s
1763 else:
1764 return r"%s^{\dagger}" % s
1766 def _print_MatMul(self, expr):
1767 from sympy import MatMul
1769 # Parenthesize nested MatMul but not other types of Mul objects:
1770 parens = lambda x: self._print(x) if isinstance(x, Mul) and not isinstance(x, MatMul) else \
1771 self.parenthesize(x, precedence_traditional(expr), False)
1773 args = list(expr.args)
1774 if expr.could_extract_minus_sign():
1775 if args[0] == -1:
1776 args = args[1:]
1777 else:
1778 args[0] = -args[0]
1779 return '- ' + ' '.join(map(parens, args))
1780 else:
1781 return ' '.join(map(parens, args))
1783 def _print_Determinant(self, expr):
1784 mat = expr.arg
1785 if mat.is_MatrixExpr:
1786 from sympy.matrices.expressions.blockmatrix import BlockMatrix
1787 if isinstance(mat, BlockMatrix):
1788 return r"\left|{%s}\right|" % self._print_matrix_contents(mat.blocks)
1789 return r"\left|{%s}\right|" % self._print(mat)
1790 return r"\left|{%s}\right|" % self._print_matrix_contents(mat)
1793 def _print_Mod(self, expr, exp=None):
1794 if exp is not None:
1795 return r'\left(%s \bmod %s\right)^{%s}' % \
1796 (self.parenthesize(expr.args[0], PRECEDENCE['Mul'],
1797 strict=True),
1798 self.parenthesize(expr.args[1], PRECEDENCE['Mul'],
1799 strict=True),
1800 exp)
1801 return r'%s \bmod %s' % (self.parenthesize(expr.args[0],
1802 PRECEDENCE['Mul'],
1803 strict=True),
1804 self.parenthesize(expr.args[1],
1805 PRECEDENCE['Mul'],
1806 strict=True))
1808 def _print_HadamardProduct(self, expr):
1809 args = expr.args
1810 prec = PRECEDENCE['Pow']
1811 parens = self.parenthesize
1813 return r' \circ '.join(
1814 (parens(arg, prec, strict=True) for arg in args))
1816 def _print_HadamardPower(self, expr):
1817 if precedence_traditional(expr.exp) < PRECEDENCE["Mul"]:
1818 template = r"%s^{\circ \left({%s}\right)}"
1819 else:
1820 template = r"%s^{\circ {%s}}"
1821 return self._helper_print_standard_power(expr, template)
1823 def _print_KroneckerProduct(self, expr):
1824 args = expr.args
1825 prec = PRECEDENCE['Pow']
1826 parens = self.parenthesize
1828 return r' \otimes '.join(
1829 (parens(arg, prec, strict=True) for arg in args))
1831 def _print_MatPow(self, expr):
1832 base, exp = expr.base, expr.exp
1833 from sympy.matrices import MatrixSymbol
1834 if not isinstance(base, MatrixSymbol) and base.is_MatrixExpr:
1835 return "\\left(%s\\right)^{%s}" % (self._print(base),
1836 self._print(exp))
1837 else:
1838 base_str = self._print(base)
1839 if '^' in base_str:
1840 return r"\left(%s\right)^{%s}" % (base_str, self._print(exp))
1841 else:
1842 return "%s^{%s}" % (base_str, self._print(exp))
1844 def _print_MatrixSymbol(self, expr):
1845 return self._print_Symbol(expr, style=self._settings[
1846 'mat_symbol_style'])
1848 def _print_ZeroMatrix(self, Z):
1849 return "0" if self._settings[
1850 'mat_symbol_style'] == 'plain' else r"\mathbf{0}"
1852 def _print_OneMatrix(self, O):
1853 return "1" if self._settings[
1854 'mat_symbol_style'] == 'plain' else r"\mathbf{1}"
1856 def _print_Identity(self, I):
1857 return r"\mathbb{I}" if self._settings[
1858 'mat_symbol_style'] == 'plain' else r"\mathbf{I}"
1860 def _print_PermutationMatrix(self, P):
1861 perm_str = self._print(P.args[0])
1862 return "P_{%s}" % perm_str
1864 def _print_NDimArray(self, expr: NDimArray):
1866 if expr.rank() == 0:
1867 return self._print(expr[()])
1869 mat_str = self._settings['mat_str']
1870 if mat_str is None:
1871 if self._settings['mode'] == 'inline':
1872 mat_str = 'smallmatrix'
1873 else:
1874 if (expr.rank() == 0) or (expr.shape[-1] <= 10):
1875 mat_str = 'matrix'
1876 else:
1877 mat_str = 'array'
1878 block_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}'
1879 block_str = block_str.replace('%MATSTR%', mat_str)
1880 if mat_str == 'array':
1881 block_str= block_str.replace('%s','{}%s')
1882 if self._settings['mat_delim']:
1883 left_delim: str = self._settings['mat_delim']
1884 right_delim = self._delim_dict[left_delim]
1885 block_str = r'\left' + left_delim + block_str + \
1886 r'\right' + right_delim
1888 if expr.rank() == 0:
1889 return block_str % ""
1891 level_str: list[list[str]] = [[] for i in range(expr.rank() + 1)]
1892 shape_ranges = [list(range(i)) for i in expr.shape]
1893 for outer_i in itertools.product(*shape_ranges):
1894 level_str[-1].append(self._print(expr[outer_i]))
1895 even = True
1896 for back_outer_i in range(expr.rank()-1, -1, -1):
1897 if len(level_str[back_outer_i+1]) < expr.shape[back_outer_i]:
1898 break
1899 if even:
1900 level_str[back_outer_i].append(
1901 r" & ".join(level_str[back_outer_i+1]))
1902 else:
1903 level_str[back_outer_i].append(
1904 block_str % (r"\\".join(level_str[back_outer_i+1])))
1905 if len(level_str[back_outer_i+1]) == 1:
1906 level_str[back_outer_i][-1] = r"\left[" + \
1907 level_str[back_outer_i][-1] + r"\right]"
1908 even = not even
1909 level_str[back_outer_i+1] = []
1911 out_str = level_str[0][0]
1913 if expr.rank() % 2 == 1:
1914 out_str = block_str % out_str
1916 return out_str
1918 def _printer_tensor_indices(self, name, indices, index_map: dict):
1919 out_str = self._print(name)
1920 last_valence = None
1921 prev_map = None
1922 for index in indices:
1923 new_valence = index.is_up
1924 if ((index in index_map) or prev_map) and \
1925 last_valence == new_valence:
1926 out_str += ","
1927 if last_valence != new_valence:
1928 if last_valence is not None:
1929 out_str += "}"
1930 if index.is_up:
1931 out_str += "{}^{"
1932 else:
1933 out_str += "{}_{"
1934 out_str += self._print(index.args[0])
1935 if index in index_map:
1936 out_str += "="
1937 out_str += self._print(index_map[index])
1938 prev_map = True
1939 else:
1940 prev_map = False
1941 last_valence = new_valence
1942 if last_valence is not None:
1943 out_str += "}"
1944 return out_str
1946 def _print_Tensor(self, expr):
1947 name = expr.args[0].args[0]
1948 indices = expr.get_indices()
1949 return self._printer_tensor_indices(name, indices, {})
1951 def _print_TensorElement(self, expr):
1952 name = expr.expr.args[0].args[0]
1953 indices = expr.expr.get_indices()
1954 index_map = expr.index_map
1955 return self._printer_tensor_indices(name, indices, index_map)
1957 def _print_TensMul(self, expr):
1958 # prints expressions like "A(a)", "3*A(a)", "(1+x)*A(a)"
1959 sign, args = expr._get_args_for_traditional_printer()
1960 return sign + "".join(
1961 [self.parenthesize(arg, precedence(expr)) for arg in args]
1962 )
1964 def _print_TensAdd(self, expr):
1965 a = []
1966 args = expr.args
1967 for x in args:
1968 a.append(self.parenthesize(x, precedence(expr)))
1969 a.sort()
1970 s = ' + '.join(a)
1971 s = s.replace('+ -', '- ')
1972 return s
1974 def _print_TensorIndex(self, expr):
1975 return "{}%s{%s}" % (
1976 "^" if expr.is_up else "_",
1977 self._print(expr.args[0])
1978 )
1980 def _print_PartialDerivative(self, expr):
1981 if len(expr.variables) == 1:
1982 return r"\frac{\partial}{\partial {%s}}{%s}" % (
1983 self._print(expr.variables[0]),
1984 self.parenthesize(expr.expr, PRECEDENCE["Mul"], False)
1985 )
1986 else:
1987 return r"\frac{\partial^{%s}}{%s}{%s}" % (
1988 len(expr.variables),
1989 " ".join([r"\partial {%s}" % self._print(i) for i in expr.variables]),
1990 self.parenthesize(expr.expr, PRECEDENCE["Mul"], False)
1991 )
1993 def _print_ArraySymbol(self, expr):
1994 return self._print(expr.name)
1996 def _print_ArrayElement(self, expr):
1997 return "{{%s}_{%s}}" % (
1998 self.parenthesize(expr.name, PRECEDENCE["Func"], True),
1999 ", ".join([f"{self._print(i)}" for i in expr.indices]))
2001 def _print_UniversalSet(self, expr):
2002 return r"\mathbb{U}"
2004 def _print_frac(self, expr, exp=None):
2005 if exp is None:
2006 return r"\operatorname{frac}{\left(%s\right)}" % self._print(expr.args[0])
2007 else:
2008 return r"\operatorname{frac}{\left(%s\right)}^{%s}" % (
2009 self._print(expr.args[0]), exp)
2011 def _print_tuple(self, expr):
2012 if self._settings['decimal_separator'] == 'comma':
2013 sep = ";"
2014 elif self._settings['decimal_separator'] == 'period':
2015 sep = ","
2016 else:
2017 raise ValueError('Unknown Decimal Separator')
2019 if len(expr) == 1:
2020 # 1-tuple needs a trailing separator
2021 return self._add_parens_lspace(self._print(expr[0]) + sep)
2022 else:
2023 return self._add_parens_lspace(
2024 (sep + r" \ ").join([self._print(i) for i in expr]))
2026 def _print_TensorProduct(self, expr):
2027 elements = [self._print(a) for a in expr.args]
2028 return r' \otimes '.join(elements)
2030 def _print_WedgeProduct(self, expr):
2031 elements = [self._print(a) for a in expr.args]
2032 return r' \wedge '.join(elements)
2034 def _print_Tuple(self, expr):
2035 return self._print_tuple(expr)
2037 def _print_list(self, expr):
2038 if self._settings['decimal_separator'] == 'comma':
2039 return r"\left[ %s\right]" % \
2040 r"; \ ".join([self._print(i) for i in expr])
2041 elif self._settings['decimal_separator'] == 'period':
2042 return r"\left[ %s\right]" % \
2043 r", \ ".join([self._print(i) for i in expr])
2044 else:
2045 raise ValueError('Unknown Decimal Separator')
2048 def _print_dict(self, d):
2049 keys = sorted(d.keys(), key=default_sort_key)
2050 items = []
2052 for key in keys:
2053 val = d[key]
2054 items.append("%s : %s" % (self._print(key), self._print(val)))
2056 return r"\left\{ %s\right\}" % r", \ ".join(items)
2058 def _print_Dict(self, expr):
2059 return self._print_dict(expr)
2061 def _print_DiracDelta(self, expr, exp=None):
2062 if len(expr.args) == 1 or expr.args[1] == 0:
2063 tex = r"\delta\left(%s\right)" % self._print(expr.args[0])
2064 else:
2065 tex = r"\delta^{\left( %s \right)}\left( %s \right)" % (
2066 self._print(expr.args[1]), self._print(expr.args[0]))
2067 if exp:
2068 tex = r"\left(%s\right)^{%s}" % (tex, exp)
2069 return tex
2071 def _print_SingularityFunction(self, expr, exp=None):
2072 shift = self._print(expr.args[0] - expr.args[1])
2073 power = self._print(expr.args[2])
2074 tex = r"{\left\langle %s \right\rangle}^{%s}" % (shift, power)
2075 if exp is not None:
2076 tex = r"{\left({\langle %s \rangle}^{%s}\right)}^{%s}" % (shift, power, exp)
2077 return tex
2079 def _print_Heaviside(self, expr, exp=None):
2080 pargs = ', '.join(self._print(arg) for arg in expr.pargs)
2081 tex = r"\theta\left(%s\right)" % pargs
2082 if exp:
2083 tex = r"\left(%s\right)^{%s}" % (tex, exp)
2084 return tex
2086 def _print_KroneckerDelta(self, expr, exp=None):
2087 i = self._print(expr.args[0])
2088 j = self._print(expr.args[1])
2089 if expr.args[0].is_Atom and expr.args[1].is_Atom:
2090 tex = r'\delta_{%s %s}' % (i, j)
2091 else:
2092 tex = r'\delta_{%s, %s}' % (i, j)
2093 if exp is not None:
2094 tex = r'\left(%s\right)^{%s}' % (tex, exp)
2095 return tex
2097 def _print_LeviCivita(self, expr, exp=None):
2098 indices = map(self._print, expr.args)
2099 if all(x.is_Atom for x in expr.args):
2100 tex = r'\varepsilon_{%s}' % " ".join(indices)
2101 else:
2102 tex = r'\varepsilon_{%s}' % ", ".join(indices)
2103 if exp:
2104 tex = r'\left(%s\right)^{%s}' % (tex, exp)
2105 return tex
2107 def _print_RandomDomain(self, d):
2108 if hasattr(d, 'as_boolean'):
2109 return '\\text{Domain: }' + self._print(d.as_boolean())
2110 elif hasattr(d, 'set'):
2111 return ('\\text{Domain: }' + self._print(d.symbols) + ' \\in ' +
2112 self._print(d.set))
2113 elif hasattr(d, 'symbols'):
2114 return '\\text{Domain on }' + self._print(d.symbols)
2115 else:
2116 return self._print(None)
2118 def _print_FiniteSet(self, s):
2119 items = sorted(s.args, key=default_sort_key)
2120 return self._print_set(items)
2122 def _print_set(self, s):
2123 items = sorted(s, key=default_sort_key)
2124 if self._settings['decimal_separator'] == 'comma':
2125 items = "; ".join(map(self._print, items))
2126 elif self._settings['decimal_separator'] == 'period':
2127 items = ", ".join(map(self._print, items))
2128 else:
2129 raise ValueError('Unknown Decimal Separator')
2130 return r"\left\{%s\right\}" % items
2133 _print_frozenset = _print_set
2135 def _print_Range(self, s):
2136 def _print_symbolic_range():
2137 # Symbolic Range that cannot be resolved
2138 if s.args[0] == 0:
2139 if s.args[2] == 1:
2140 cont = self._print(s.args[1])
2141 else:
2142 cont = ", ".join(self._print(arg) for arg in s.args)
2143 else:
2144 if s.args[2] == 1:
2145 cont = ", ".join(self._print(arg) for arg in s.args[:2])
2146 else:
2147 cont = ", ".join(self._print(arg) for arg in s.args)
2149 return(f"\\text{{Range}}\\left({cont}\\right)")
2151 dots = object()
2153 if s.start.is_infinite and s.stop.is_infinite:
2154 if s.step.is_positive:
2155 printset = dots, -1, 0, 1, dots
2156 else:
2157 printset = dots, 1, 0, -1, dots
2158 elif s.start.is_infinite:
2159 printset = dots, s[-1] - s.step, s[-1]
2160 elif s.stop.is_infinite:
2161 it = iter(s)
2162 printset = next(it), next(it), dots
2163 elif s.is_empty is not None:
2164 if (s.size < 4) == True:
2165 printset = tuple(s)
2166 elif s.is_iterable:
2167 it = iter(s)
2168 printset = next(it), next(it), dots, s[-1]
2169 else:
2170 return _print_symbolic_range()
2171 else:
2172 return _print_symbolic_range()
2173 return (r"\left\{" +
2174 r", ".join(self._print(el) if el is not dots else r'\ldots' for el in printset) +
2175 r"\right\}")
2177 def __print_number_polynomial(self, expr, letter, exp=None):
2178 if len(expr.args) == 2:
2179 if exp is not None:
2180 return r"%s_{%s}^{%s}\left(%s\right)" % (letter,
2181 self._print(expr.args[0]), exp,
2182 self._print(expr.args[1]))
2183 return r"%s_{%s}\left(%s\right)" % (letter,
2184 self._print(expr.args[0]), self._print(expr.args[1]))
2186 tex = r"%s_{%s}" % (letter, self._print(expr.args[0]))
2187 if exp is not None:
2188 tex = r"%s^{%s}" % (tex, exp)
2189 return tex
2191 def _print_bernoulli(self, expr, exp=None):
2192 return self.__print_number_polynomial(expr, "B", exp)
2194 def _print_genocchi(self, expr, exp=None):
2195 return self.__print_number_polynomial(expr, "G", exp)
2197 def _print_bell(self, expr, exp=None):
2198 if len(expr.args) == 3:
2199 tex1 = r"B_{%s, %s}" % (self._print(expr.args[0]),
2200 self._print(expr.args[1]))
2201 tex2 = r"\left(%s\right)" % r", ".join(self._print(el) for
2202 el in expr.args[2])
2203 if exp is not None:
2204 tex = r"%s^{%s}%s" % (tex1, exp, tex2)
2205 else:
2206 tex = tex1 + tex2
2207 return tex
2208 return self.__print_number_polynomial(expr, "B", exp)
2210 def _print_fibonacci(self, expr, exp=None):
2211 return self.__print_number_polynomial(expr, "F", exp)
2213 def _print_lucas(self, expr, exp=None):
2214 tex = r"L_{%s}" % self._print(expr.args[0])
2215 if exp is not None:
2216 tex = r"%s^{%s}" % (tex, exp)
2217 return tex
2219 def _print_tribonacci(self, expr, exp=None):
2220 return self.__print_number_polynomial(expr, "T", exp)
2222 def _print_SeqFormula(self, s):
2223 dots = object()
2224 if len(s.start.free_symbols) > 0 or len(s.stop.free_symbols) > 0:
2225 return r"\left\{%s\right\}_{%s=%s}^{%s}" % (
2226 self._print(s.formula),
2227 self._print(s.variables[0]),
2228 self._print(s.start),
2229 self._print(s.stop)
2230 )
2231 if s.start is S.NegativeInfinity:
2232 stop = s.stop
2233 printset = (dots, s.coeff(stop - 3), s.coeff(stop - 2),
2234 s.coeff(stop - 1), s.coeff(stop))
2235 elif s.stop is S.Infinity or s.length > 4:
2236 printset = s[:4]
2237 printset.append(dots)
2238 else:
2239 printset = tuple(s)
2241 return (r"\left[" +
2242 r", ".join(self._print(el) if el is not dots else r'\ldots' for el in printset) +
2243 r"\right]")
2245 _print_SeqPer = _print_SeqFormula
2246 _print_SeqAdd = _print_SeqFormula
2247 _print_SeqMul = _print_SeqFormula
2249 def _print_Interval(self, i):
2250 if i.start == i.end:
2251 return r"\left\{%s\right\}" % self._print(i.start)
2253 else:
2254 if i.left_open:
2255 left = '('
2256 else:
2257 left = '['
2259 if i.right_open:
2260 right = ')'
2261 else:
2262 right = ']'
2264 return r"\left%s%s, %s\right%s" % \
2265 (left, self._print(i.start), self._print(i.end), right)
2267 def _print_AccumulationBounds(self, i):
2268 return r"\left\langle %s, %s\right\rangle" % \
2269 (self._print(i.min), self._print(i.max))
2271 def _print_Union(self, u):
2272 prec = precedence_traditional(u)
2273 args_str = [self.parenthesize(i, prec) for i in u.args]
2274 return r" \cup ".join(args_str)
2276 def _print_Complement(self, u):
2277 prec = precedence_traditional(u)
2278 args_str = [self.parenthesize(i, prec) for i in u.args]
2279 return r" \setminus ".join(args_str)
2281 def _print_Intersection(self, u):
2282 prec = precedence_traditional(u)
2283 args_str = [self.parenthesize(i, prec) for i in u.args]
2284 return r" \cap ".join(args_str)
2286 def _print_SymmetricDifference(self, u):
2287 prec = precedence_traditional(u)
2288 args_str = [self.parenthesize(i, prec) for i in u.args]
2289 return r" \triangle ".join(args_str)
2291 def _print_ProductSet(self, p):
2292 prec = precedence_traditional(p)
2293 if len(p.sets) >= 1 and not has_variety(p.sets):
2294 return self.parenthesize(p.sets[0], prec) + "^{%d}" % len(p.sets)
2295 return r" \times ".join(
2296 self.parenthesize(set, prec) for set in p.sets)
2298 def _print_EmptySet(self, e):
2299 return r"\emptyset"
2301 def _print_Naturals(self, n):
2302 return r"\mathbb{N}"
2304 def _print_Naturals0(self, n):
2305 return r"\mathbb{N}_0"
2307 def _print_Integers(self, i):
2308 return r"\mathbb{Z}"
2310 def _print_Rationals(self, i):
2311 return r"\mathbb{Q}"
2313 def _print_Reals(self, i):
2314 return r"\mathbb{R}"
2316 def _print_Complexes(self, i):
2317 return r"\mathbb{C}"
2319 def _print_ImageSet(self, s):
2320 expr = s.lamda.expr
2321 sig = s.lamda.signature
2322 xys = ((self._print(x), self._print(y)) for x, y in zip(sig, s.base_sets))
2323 xinys = r", ".join(r"%s \in %s" % xy for xy in xys)
2324 return r"\left\{%s\; \middle|\; %s\right\}" % (self._print(expr), xinys)
2326 def _print_ConditionSet(self, s):
2327 vars_print = ', '.join([self._print(var) for var in Tuple(s.sym)])
2328 if s.base_set is S.UniversalSet:
2329 return r"\left\{%s\; \middle|\; %s \right\}" % \
2330 (vars_print, self._print(s.condition))
2332 return r"\left\{%s\; \middle|\; %s \in %s \wedge %s \right\}" % (
2333 vars_print,
2334 vars_print,
2335 self._print(s.base_set),
2336 self._print(s.condition))
2338 def _print_PowerSet(self, expr):
2339 arg_print = self._print(expr.args[0])
2340 return r"\mathcal{{P}}\left({}\right)".format(arg_print)
2342 def _print_ComplexRegion(self, s):
2343 vars_print = ', '.join([self._print(var) for var in s.variables])
2344 return r"\left\{%s\; \middle|\; %s \in %s \right\}" % (
2345 self._print(s.expr),
2346 vars_print,
2347 self._print(s.sets))
2349 def _print_Contains(self, e):
2350 return r"%s \in %s" % tuple(self._print(a) for a in e.args)
2352 def _print_FourierSeries(self, s):
2353 if s.an.formula is S.Zero and s.bn.formula is S.Zero:
2354 return self._print(s.a0)
2355 return self._print_Add(s.truncate()) + r' + \ldots'
2357 def _print_FormalPowerSeries(self, s):
2358 return self._print_Add(s.infinite)
2360 def _print_FiniteField(self, expr):
2361 return r"\mathbb{F}_{%s}" % expr.mod
2363 def _print_IntegerRing(self, expr):
2364 return r"\mathbb{Z}"
2366 def _print_RationalField(self, expr):
2367 return r"\mathbb{Q}"
2369 def _print_RealField(self, expr):
2370 return r"\mathbb{R}"
2372 def _print_ComplexField(self, expr):
2373 return r"\mathbb{C}"
2375 def _print_PolynomialRing(self, expr):
2376 domain = self._print(expr.domain)
2377 symbols = ", ".join(map(self._print, expr.symbols))
2378 return r"%s\left[%s\right]" % (domain, symbols)
2380 def _print_FractionField(self, expr):
2381 domain = self._print(expr.domain)
2382 symbols = ", ".join(map(self._print, expr.symbols))
2383 return r"%s\left(%s\right)" % (domain, symbols)
2385 def _print_PolynomialRingBase(self, expr):
2386 domain = self._print(expr.domain)
2387 symbols = ", ".join(map(self._print, expr.symbols))
2388 inv = ""
2389 if not expr.is_Poly:
2390 inv = r"S_<^{-1}"
2391 return r"%s%s\left[%s\right]" % (inv, domain, symbols)
2393 def _print_Poly(self, poly):
2394 cls = poly.__class__.__name__
2395 terms = []
2396 for monom, coeff in poly.terms():
2397 s_monom = ''
2398 for i, exp in enumerate(monom):
2399 if exp > 0:
2400 if exp == 1:
2401 s_monom += self._print(poly.gens[i])
2402 else:
2403 s_monom += self._print(pow(poly.gens[i], exp))
2405 if coeff.is_Add:
2406 if s_monom:
2407 s_coeff = r"\left(%s\right)" % self._print(coeff)
2408 else:
2409 s_coeff = self._print(coeff)
2410 else:
2411 if s_monom:
2412 if coeff is S.One:
2413 terms.extend(['+', s_monom])
2414 continue
2416 if coeff is S.NegativeOne:
2417 terms.extend(['-', s_monom])
2418 continue
2420 s_coeff = self._print(coeff)
2422 if not s_monom:
2423 s_term = s_coeff
2424 else:
2425 s_term = s_coeff + " " + s_monom
2427 if s_term.startswith('-'):
2428 terms.extend(['-', s_term[1:]])
2429 else:
2430 terms.extend(['+', s_term])
2432 if terms[0] in ('-', '+'):
2433 modifier = terms.pop(0)
2435 if modifier == '-':
2436 terms[0] = '-' + terms[0]
2438 expr = ' '.join(terms)
2439 gens = list(map(self._print, poly.gens))
2440 domain = "domain=%s" % self._print(poly.get_domain())
2442 args = ", ".join([expr] + gens + [domain])
2443 if cls in accepted_latex_functions:
2444 tex = r"\%s {\left(%s \right)}" % (cls, args)
2445 else:
2446 tex = r"\operatorname{%s}{\left( %s \right)}" % (cls, args)
2448 return tex
2450 def _print_ComplexRootOf(self, root):
2451 cls = root.__class__.__name__
2452 if cls == "ComplexRootOf":
2453 cls = "CRootOf"
2454 expr = self._print(root.expr)
2455 index = root.index
2456 if cls in accepted_latex_functions:
2457 return r"\%s {\left(%s, %d\right)}" % (cls, expr, index)
2458 else:
2459 return r"\operatorname{%s} {\left(%s, %d\right)}" % (cls, expr,
2460 index)
2462 def _print_RootSum(self, expr):
2463 cls = expr.__class__.__name__
2464 args = [self._print(expr.expr)]
2466 if expr.fun is not S.IdentityFunction:
2467 args.append(self._print(expr.fun))
2469 if cls in accepted_latex_functions:
2470 return r"\%s {\left(%s\right)}" % (cls, ", ".join(args))
2471 else:
2472 return r"\operatorname{%s} {\left(%s\right)}" % (cls,
2473 ", ".join(args))
2475 def _print_OrdinalOmega(self, expr):
2476 return r"\omega"
2478 def _print_OmegaPower(self, expr):
2479 exp, mul = expr.args
2480 if mul != 1:
2481 if exp != 1:
2482 return r"{} \omega^{{{}}}".format(mul, exp)
2483 else:
2484 return r"{} \omega".format(mul)
2485 else:
2486 if exp != 1:
2487 return r"\omega^{{{}}}".format(exp)
2488 else:
2489 return r"\omega"
2491 def _print_Ordinal(self, expr):
2492 return " + ".join([self._print(arg) for arg in expr.args])
2494 def _print_PolyElement(self, poly):
2495 mul_symbol = self._settings['mul_symbol_latex']
2496 return poly.str(self, PRECEDENCE, "{%s}^{%d}", mul_symbol)
2498 def _print_FracElement(self, frac):
2499 if frac.denom == 1:
2500 return self._print(frac.numer)
2501 else:
2502 numer = self._print(frac.numer)
2503 denom = self._print(frac.denom)
2504 return r"\frac{%s}{%s}" % (numer, denom)
2506 def _print_euler(self, expr, exp=None):
2507 m, x = (expr.args[0], None) if len(expr.args) == 1 else expr.args
2508 tex = r"E_{%s}" % self._print(m)
2509 if exp is not None:
2510 tex = r"%s^{%s}" % (tex, exp)
2511 if x is not None:
2512 tex = r"%s\left(%s\right)" % (tex, self._print(x))
2513 return tex
2515 def _print_catalan(self, expr, exp=None):
2516 tex = r"C_{%s}" % self._print(expr.args[0])
2517 if exp is not None:
2518 tex = r"%s^{%s}" % (tex, exp)
2519 return tex
2521 def _print_UnifiedTransform(self, expr, s, inverse=False):
2522 return r"\mathcal{{{}}}{}_{{{}}}\left[{}\right]\left({}\right)".format(s, '^{-1}' if inverse else '', self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2]))
2524 def _print_MellinTransform(self, expr):
2525 return self._print_UnifiedTransform(expr, 'M')
2527 def _print_InverseMellinTransform(self, expr):
2528 return self._print_UnifiedTransform(expr, 'M', True)
2530 def _print_LaplaceTransform(self, expr):
2531 return self._print_UnifiedTransform(expr, 'L')
2533 def _print_InverseLaplaceTransform(self, expr):
2534 return self._print_UnifiedTransform(expr, 'L', True)
2536 def _print_FourierTransform(self, expr):
2537 return self._print_UnifiedTransform(expr, 'F')
2539 def _print_InverseFourierTransform(self, expr):
2540 return self._print_UnifiedTransform(expr, 'F', True)
2542 def _print_SineTransform(self, expr):
2543 return self._print_UnifiedTransform(expr, 'SIN')
2545 def _print_InverseSineTransform(self, expr):
2546 return self._print_UnifiedTransform(expr, 'SIN', True)
2548 def _print_CosineTransform(self, expr):
2549 return self._print_UnifiedTransform(expr, 'COS')
2551 def _print_InverseCosineTransform(self, expr):
2552 return self._print_UnifiedTransform(expr, 'COS', True)
2554 def _print_DMP(self, p):
2555 try:
2556 if p.ring is not None:
2557 # TODO incorporate order
2558 return self._print(p.ring.to_sympy(p))
2559 except SympifyError:
2560 pass
2561 return self._print(repr(p))
2563 def _print_DMF(self, p):
2564 return self._print_DMP(p)
2566 def _print_Object(self, object):
2567 return self._print(Symbol(object.name))
2569 def _print_LambertW(self, expr, exp=None):
2570 arg0 = self._print(expr.args[0])
2571 exp = r"^{%s}" % (exp,) if exp is not None else ""
2572 if len(expr.args) == 1:
2573 result = r"W%s\left(%s\right)" % (exp, arg0)
2574 else:
2575 arg1 = self._print(expr.args[1])
2576 result = "W{0}_{{{1}}}\\left({2}\\right)".format(exp, arg1, arg0)
2577 return result
2579 def _print_Expectation(self, expr):
2580 return r"\operatorname{{E}}\left[{}\right]".format(self._print(expr.args[0]))
2582 def _print_Variance(self, expr):
2583 return r"\operatorname{{Var}}\left({}\right)".format(self._print(expr.args[0]))
2585 def _print_Covariance(self, expr):
2586 return r"\operatorname{{Cov}}\left({}\right)".format(", ".join(self._print(arg) for arg in expr.args))
2588 def _print_Probability(self, expr):
2589 return r"\operatorname{{P}}\left({}\right)".format(self._print(expr.args[0]))
2591 def _print_Morphism(self, morphism):
2592 domain = self._print(morphism.domain)
2593 codomain = self._print(morphism.codomain)
2594 return "%s\\rightarrow %s" % (domain, codomain)
2596 def _print_TransferFunction(self, expr):
2597 num, den = self._print(expr.num), self._print(expr.den)
2598 return r"\frac{%s}{%s}" % (num, den)
2600 def _print_Series(self, expr):
2601 args = list(expr.args)
2602 parens = lambda x: self.parenthesize(x, precedence_traditional(expr),
2603 False)
2604 return ' '.join(map(parens, args))
2606 def _print_MIMOSeries(self, expr):
2607 from sympy.physics.control.lti import MIMOParallel
2608 args = list(expr.args)[::-1]
2609 parens = lambda x: self.parenthesize(x, precedence_traditional(expr),
2610 False) if isinstance(x, MIMOParallel) else self._print(x)
2611 return r"\cdot".join(map(parens, args))
2613 def _print_Parallel(self, expr):
2614 return ' + '.join(map(self._print, expr.args))
2616 def _print_MIMOParallel(self, expr):
2617 return ' + '.join(map(self._print, expr.args))
2619 def _print_Feedback(self, expr):
2620 from sympy.physics.control import TransferFunction, Series
2622 num, tf = expr.sys1, TransferFunction(1, 1, expr.var)
2623 num_arg_list = list(num.args) if isinstance(num, Series) else [num]
2624 den_arg_list = list(expr.sys2.args) if \
2625 isinstance(expr.sys2, Series) else [expr.sys2]
2626 den_term_1 = tf
2628 if isinstance(num, Series) and isinstance(expr.sys2, Series):
2629 den_term_2 = Series(*num_arg_list, *den_arg_list)
2630 elif isinstance(num, Series) and isinstance(expr.sys2, TransferFunction):
2631 if expr.sys2 == tf:
2632 den_term_2 = Series(*num_arg_list)
2633 else:
2634 den_term_2 = tf, Series(*num_arg_list, expr.sys2)
2635 elif isinstance(num, TransferFunction) and isinstance(expr.sys2, Series):
2636 if num == tf:
2637 den_term_2 = Series(*den_arg_list)
2638 else:
2639 den_term_2 = Series(num, *den_arg_list)
2640 else:
2641 if num == tf:
2642 den_term_2 = Series(*den_arg_list)
2643 elif expr.sys2 == tf:
2644 den_term_2 = Series(*num_arg_list)
2645 else:
2646 den_term_2 = Series(*num_arg_list, *den_arg_list)
2648 numer = self._print(num)
2649 denom_1 = self._print(den_term_1)
2650 denom_2 = self._print(den_term_2)
2651 _sign = "+" if expr.sign == -1 else "-"
2653 return r"\frac{%s}{%s %s %s}" % (numer, denom_1, _sign, denom_2)
2655 def _print_MIMOFeedback(self, expr):
2656 from sympy.physics.control import MIMOSeries
2657 inv_mat = self._print(MIMOSeries(expr.sys2, expr.sys1))
2658 sys1 = self._print(expr.sys1)
2659 _sign = "+" if expr.sign == -1 else "-"
2660 return r"\left(I_{\tau} %s %s\right)^{-1} \cdot %s" % (_sign, inv_mat, sys1)
2662 def _print_TransferFunctionMatrix(self, expr):
2663 mat = self._print(expr._expr_mat)
2664 return r"%s_\tau" % mat
2666 def _print_DFT(self, expr):
2667 return r"\text{{{}}}_{{{}}}".format(expr.__class__.__name__, expr.n)
2668 _print_IDFT = _print_DFT
2670 def _print_NamedMorphism(self, morphism):
2671 pretty_name = self._print(Symbol(morphism.name))
2672 pretty_morphism = self._print_Morphism(morphism)
2673 return "%s:%s" % (pretty_name, pretty_morphism)
2675 def _print_IdentityMorphism(self, morphism):
2676 from sympy.categories import NamedMorphism
2677 return self._print_NamedMorphism(NamedMorphism(
2678 morphism.domain, morphism.codomain, "id"))
2680 def _print_CompositeMorphism(self, morphism):
2681 # All components of the morphism have names and it is thus
2682 # possible to build the name of the composite.
2683 component_names_list = [self._print(Symbol(component.name)) for
2684 component in morphism.components]
2685 component_names_list.reverse()
2686 component_names = "\\circ ".join(component_names_list) + ":"
2688 pretty_morphism = self._print_Morphism(morphism)
2689 return component_names + pretty_morphism
2691 def _print_Category(self, morphism):
2692 return r"\mathbf{{{}}}".format(self._print(Symbol(morphism.name)))
2694 def _print_Diagram(self, diagram):
2695 if not diagram.premises:
2696 # This is an empty diagram.
2697 return self._print(S.EmptySet)
2699 latex_result = self._print(diagram.premises)
2700 if diagram.conclusions:
2701 latex_result += "\\Longrightarrow %s" % \
2702 self._print(diagram.conclusions)
2704 return latex_result
2706 def _print_DiagramGrid(self, grid):
2707 latex_result = "\\begin{array}{%s}\n" % ("c" * grid.width)
2709 for i in range(grid.height):
2710 for j in range(grid.width):
2711 if grid[i, j]:
2712 latex_result += latex(grid[i, j])
2713 latex_result += " "
2714 if j != grid.width - 1:
2715 latex_result += "& "
2717 if i != grid.height - 1:
2718 latex_result += "\\\\"
2719 latex_result += "\n"
2721 latex_result += "\\end{array}\n"
2722 return latex_result
2724 def _print_FreeModule(self, M):
2725 return '{{{}}}^{{{}}}'.format(self._print(M.ring), self._print(M.rank))
2727 def _print_FreeModuleElement(self, m):
2728 # Print as row vector for convenience, for now.
2729 return r"\left[ {} \right]".format(",".join(
2730 '{' + self._print(x) + '}' for x in m))
2732 def _print_SubModule(self, m):
2733 return r"\left\langle {} \right\rangle".format(",".join(
2734 '{' + self._print(x) + '}' for x in m.gens))
2736 def _print_ModuleImplementedIdeal(self, m):
2737 return r"\left\langle {} \right\rangle".format(",".join(
2738 '{' + self._print(x) + '}' for [x] in m._module.gens))
2740 def _print_Quaternion(self, expr):
2741 # TODO: This expression is potentially confusing,
2742 # shall we print it as `Quaternion( ... )`?
2743 s = [self.parenthesize(i, PRECEDENCE["Mul"], strict=True)
2744 for i in expr.args]
2745 a = [s[0]] + [i+" "+j for i, j in zip(s[1:], "ijk")]
2746 return " + ".join(a)
2748 def _print_QuotientRing(self, R):
2749 # TODO nicer fractions for few generators...
2750 return r"\frac{{{}}}{{{}}}".format(self._print(R.ring),
2751 self._print(R.base_ideal))
2753 def _print_QuotientRingElement(self, x):
2754 return r"{{{}}} + {{{}}}".format(self._print(x.data),
2755 self._print(x.ring.base_ideal))
2757 def _print_QuotientModuleElement(self, m):
2758 return r"{{{}}} + {{{}}}".format(self._print(m.data),
2759 self._print(m.module.killed_module))
2761 def _print_QuotientModule(self, M):
2762 # TODO nicer fractions for few generators...
2763 return r"\frac{{{}}}{{{}}}".format(self._print(M.base),
2764 self._print(M.killed_module))
2766 def _print_MatrixHomomorphism(self, h):
2767 return r"{{{}}} : {{{}}} \to {{{}}}".format(self._print(h._sympy_matrix()),
2768 self._print(h.domain), self._print(h.codomain))
2770 def _print_Manifold(self, manifold):
2771 string = manifold.name.name
2772 if '{' in string:
2773 name, supers, subs = string, [], []
2774 else:
2775 name, supers, subs = split_super_sub(string)
2777 name = translate(name)
2778 supers = [translate(sup) for sup in supers]
2779 subs = [translate(sub) for sub in subs]
2781 name = r'\text{%s}' % name
2782 if supers:
2783 name += "^{%s}" % " ".join(supers)
2784 if subs:
2785 name += "_{%s}" % " ".join(subs)
2787 return name
2789 def _print_Patch(self, patch):
2790 return r'\text{%s}_{%s}' % (self._print(patch.name), self._print(patch.manifold))
2792 def _print_CoordSystem(self, coordsys):
2793 return r'\text{%s}^{\text{%s}}_{%s}' % (
2794 self._print(coordsys.name), self._print(coordsys.patch.name), self._print(coordsys.manifold)
2795 )
2797 def _print_CovarDerivativeOp(self, cvd):
2798 return r'\mathbb{\nabla}_{%s}' % self._print(cvd._wrt)
2800 def _print_BaseScalarField(self, field):
2801 string = field._coord_sys.symbols[field._index].name
2802 return r'\mathbf{{{}}}'.format(self._print(Symbol(string)))
2804 def _print_BaseVectorField(self, field):
2805 string = field._coord_sys.symbols[field._index].name
2806 return r'\partial_{{{}}}'.format(self._print(Symbol(string)))
2808 def _print_Differential(self, diff):
2809 field = diff._form_field
2810 if hasattr(field, '_coord_sys'):
2811 string = field._coord_sys.symbols[field._index].name
2812 return r'\operatorname{{d}}{}'.format(self._print(Symbol(string)))
2813 else:
2814 string = self._print(field)
2815 return r'\operatorname{{d}}\left({}\right)'.format(string)
2817 def _print_Tr(self, p):
2818 # TODO: Handle indices
2819 contents = self._print(p.args[0])
2820 return r'\operatorname{{tr}}\left({}\right)'.format(contents)
2822 def _print_totient(self, expr, exp=None):
2823 if exp is not None:
2824 return r'\left(\phi\left(%s\right)\right)^{%s}' % \
2825 (self._print(expr.args[0]), exp)
2826 return r'\phi\left(%s\right)' % self._print(expr.args[0])
2828 def _print_reduced_totient(self, expr, exp=None):
2829 if exp is not None:
2830 return r'\left(\lambda\left(%s\right)\right)^{%s}' % \
2831 (self._print(expr.args[0]), exp)
2832 return r'\lambda\left(%s\right)' % self._print(expr.args[0])
2834 def _print_divisor_sigma(self, expr, exp=None):
2835 if len(expr.args) == 2:
2836 tex = r"_%s\left(%s\right)" % tuple(map(self._print,
2837 (expr.args[1], expr.args[0])))
2838 else:
2839 tex = r"\left(%s\right)" % self._print(expr.args[0])
2840 if exp is not None:
2841 return r"\sigma^{%s}%s" % (exp, tex)
2842 return r"\sigma%s" % tex
2844 def _print_udivisor_sigma(self, expr, exp=None):
2845 if len(expr.args) == 2:
2846 tex = r"_%s\left(%s\right)" % tuple(map(self._print,
2847 (expr.args[1], expr.args[0])))
2848 else:
2849 tex = r"\left(%s\right)" % self._print(expr.args[0])
2850 if exp is not None:
2851 return r"\sigma^*^{%s}%s" % (exp, tex)
2852 return r"\sigma^*%s" % tex
2854 def _print_primenu(self, expr, exp=None):
2855 if exp is not None:
2856 return r'\left(\nu\left(%s\right)\right)^{%s}' % \
2857 (self._print(expr.args[0]), exp)
2858 return r'\nu\left(%s\right)' % self._print(expr.args[0])
2860 def _print_primeomega(self, expr, exp=None):
2861 if exp is not None:
2862 return r'\left(\Omega\left(%s\right)\right)^{%s}' % \
2863 (self._print(expr.args[0]), exp)
2864 return r'\Omega\left(%s\right)' % self._print(expr.args[0])
2866 def _print_Str(self, s):
2867 return str(s.name)
2869 def _print_float(self, expr):
2870 return self._print(Float(expr))
2872 def _print_int(self, expr):
2873 return str(expr)
2875 def _print_mpz(self, expr):
2876 return str(expr)
2878 def _print_mpq(self, expr):
2879 return str(expr)
2881 def _print_Predicate(self, expr):
2882 return r"\operatorname{{Q}}_{{\text{{{}}}}}".format(latex_escape(str(expr.name)))
2884 def _print_AppliedPredicate(self, expr):
2885 pred = expr.function
2886 args = expr.arguments
2887 pred_latex = self._print(pred)
2888 args_latex = ', '.join([self._print(a) for a in args])
2889 return '%s(%s)' % (pred_latex, args_latex)
2891 def emptyPrinter(self, expr):
2892 # default to just printing as monospace, like would normally be shown
2893 s = super().emptyPrinter(expr)
2895 return r"\mathtt{\text{%s}}" % latex_escape(s)
2898def translate(s: str) -> str:
2899 r'''
2900 Check for a modifier ending the string. If present, convert the
2901 modifier to latex and translate the rest recursively.
2903 Given a description of a Greek letter or other special character,
2904 return the appropriate latex.
2906 Let everything else pass as given.
2908 >>> from sympy.printing.latex import translate
2909 >>> translate('alphahatdotprime')
2910 "{\\dot{\\hat{\\alpha}}}'"
2911 '''
2912 # Process the rest
2913 tex = tex_greek_dictionary.get(s)
2914 if tex:
2915 return tex
2916 elif s.lower() in greek_letters_set:
2917 return "\\" + s.lower()
2918 elif s in other_symbols:
2919 return "\\" + s
2920 else:
2921 # Process modifiers, if any, and recurse
2922 for key in sorted(modifier_dict.keys(), key=len, reverse=True):
2923 if s.lower().endswith(key) and len(s) > len(key):
2924 return modifier_dict[key](translate(s[:-len(key)]))
2925 return s
2929@print_function(LatexPrinter)
2930def latex(expr, **settings):
2931 r"""Convert the given expression to LaTeX string representation.
2933 Parameters
2934 ==========
2935 full_prec: boolean, optional
2936 If set to True, a floating point number is printed with full precision.
2937 fold_frac_powers : boolean, optional
2938 Emit ``^{p/q}`` instead of ``^{\frac{p}{q}}`` for fractional powers.
2939 fold_func_brackets : boolean, optional
2940 Fold function brackets where applicable.
2941 fold_short_frac : boolean, optional
2942 Emit ``p / q`` instead of ``\frac{p}{q}`` when the denominator is
2943 simple enough (at most two terms and no powers). The default value is
2944 ``True`` for inline mode, ``False`` otherwise.
2945 inv_trig_style : string, optional
2946 How inverse trig functions should be displayed. Can be one of
2947 ``'abbreviated'``, ``'full'``, or ``'power'``. Defaults to
2948 ``'abbreviated'``.
2949 itex : boolean, optional
2950 Specifies if itex-specific syntax is used, including emitting
2951 ``$$...$$``.
2952 ln_notation : boolean, optional
2953 If set to ``True``, ``\ln`` is used instead of default ``\log``.
2954 long_frac_ratio : float or None, optional
2955 The allowed ratio of the width of the numerator to the width of the
2956 denominator before the printer breaks off long fractions. If ``None``
2957 (the default value), long fractions are not broken up.
2958 mat_delim : string, optional
2959 The delimiter to wrap around matrices. Can be one of ``'['``, ``'('``,
2960 or the empty string ``''``. Defaults to ``'['``.
2961 mat_str : string, optional
2962 Which matrix environment string to emit. ``'smallmatrix'``,
2963 ``'matrix'``, ``'array'``, etc. Defaults to ``'smallmatrix'`` for
2964 inline mode, ``'matrix'`` for matrices of no more than 10 columns, and
2965 ``'array'`` otherwise.
2966 mode: string, optional
2967 Specifies how the generated code will be delimited. ``mode`` can be one
2968 of ``'plain'``, ``'inline'``, ``'equation'`` or ``'equation*'``. If
2969 ``mode`` is set to ``'plain'``, then the resulting code will not be
2970 delimited at all (this is the default). If ``mode`` is set to
2971 ``'inline'`` then inline LaTeX ``$...$`` will be used. If ``mode`` is
2972 set to ``'equation'`` or ``'equation*'``, the resulting code will be
2973 enclosed in the ``equation`` or ``equation*`` environment (remember to
2974 import ``amsmath`` for ``equation*``), unless the ``itex`` option is
2975 set. In the latter case, the ``$$...$$`` syntax is used.
2976 mul_symbol : string or None, optional
2977 The symbol to use for multiplication. Can be one of ``None``,
2978 ``'ldot'``, ``'dot'``, or ``'times'``.
2979 order: string, optional
2980 Any of the supported monomial orderings (currently ``'lex'``,
2981 ``'grlex'``, or ``'grevlex'``), ``'old'``, and ``'none'``. This
2982 parameter does nothing for `~.Mul` objects. Setting order to ``'old'``
2983 uses the compatibility ordering for ``~.Add`` defined in Printer. For
2984 very large expressions, set the ``order`` keyword to ``'none'`` if
2985 speed is a concern.
2986 symbol_names : dictionary of strings mapped to symbols, optional
2987 Dictionary of symbols and the custom strings they should be emitted as.
2988 root_notation : boolean, optional
2989 If set to ``False``, exponents of the form 1/n are printed in fractonal
2990 form. Default is ``True``, to print exponent in root form.
2991 mat_symbol_style : string, optional
2992 Can be either ``'plain'`` (default) or ``'bold'``. If set to
2993 ``'bold'``, a `~.MatrixSymbol` A will be printed as ``\mathbf{A}``,
2994 otherwise as ``A``.
2995 imaginary_unit : string, optional
2996 String to use for the imaginary unit. Defined options are ``'i'``
2997 (default) and ``'j'``. Adding ``r`` or ``t`` in front gives ``\mathrm``
2998 or ``\text``, so ``'ri'`` leads to ``\mathrm{i}`` which gives
2999 `\mathrm{i}`.
3000 gothic_re_im : boolean, optional
3001 If set to ``True``, `\Re` and `\Im` is used for ``re`` and ``im``, respectively.
3002 The default is ``False`` leading to `\operatorname{re}` and `\operatorname{im}`.
3003 decimal_separator : string, optional
3004 Specifies what separator to use to separate the whole and fractional parts of a
3005 floating point number as in `2.5` for the default, ``period`` or `2{,}5`
3006 when ``comma`` is specified. Lists, sets, and tuple are printed with semicolon
3007 separating the elements when ``comma`` is chosen. For example, [1; 2; 3] when
3008 ``comma`` is chosen and [1,2,3] for when ``period`` is chosen.
3009 parenthesize_super : boolean, optional
3010 If set to ``False``, superscripted expressions will not be parenthesized when
3011 powered. Default is ``True``, which parenthesizes the expression when powered.
3012 min: Integer or None, optional
3013 Sets the lower bound for the exponent to print floating point numbers in
3014 fixed-point format.
3015 max: Integer or None, optional
3016 Sets the upper bound for the exponent to print floating point numbers in
3017 fixed-point format.
3018 diff_operator: string, optional
3019 String to use for differential operator. Default is ``'d'``, to print in italic
3020 form. ``'rd'``, ``'td'`` are shortcuts for ``\mathrm{d}`` and ``\text{d}``.
3022 Notes
3023 =====
3025 Not using a print statement for printing, results in double backslashes for
3026 latex commands since that's the way Python escapes backslashes in strings.
3028 >>> from sympy import latex, Rational
3029 >>> from sympy.abc import tau
3030 >>> latex((2*tau)**Rational(7,2))
3031 '8 \\sqrt{2} \\tau^{\\frac{7}{2}}'
3032 >>> print(latex((2*tau)**Rational(7,2)))
3033 8 \sqrt{2} \tau^{\frac{7}{2}}
3035 Examples
3036 ========
3038 >>> from sympy import latex, pi, sin, asin, Integral, Matrix, Rational, log
3039 >>> from sympy.abc import x, y, mu, r, tau
3041 Basic usage:
3043 >>> print(latex((2*tau)**Rational(7,2)))
3044 8 \sqrt{2} \tau^{\frac{7}{2}}
3046 ``mode`` and ``itex`` options:
3048 >>> print(latex((2*mu)**Rational(7,2), mode='plain'))
3049 8 \sqrt{2} \mu^{\frac{7}{2}}
3050 >>> print(latex((2*tau)**Rational(7,2), mode='inline'))
3051 $8 \sqrt{2} \tau^{7 / 2}$
3052 >>> print(latex((2*mu)**Rational(7,2), mode='equation*'))
3053 \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*}
3054 >>> print(latex((2*mu)**Rational(7,2), mode='equation'))
3055 \begin{equation}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation}
3056 >>> print(latex((2*mu)**Rational(7,2), mode='equation', itex=True))
3057 $$8 \sqrt{2} \mu^{\frac{7}{2}}$$
3058 >>> print(latex((2*mu)**Rational(7,2), mode='plain'))
3059 8 \sqrt{2} \mu^{\frac{7}{2}}
3060 >>> print(latex((2*tau)**Rational(7,2), mode='inline'))
3061 $8 \sqrt{2} \tau^{7 / 2}$
3062 >>> print(latex((2*mu)**Rational(7,2), mode='equation*'))
3063 \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*}
3064 >>> print(latex((2*mu)**Rational(7,2), mode='equation'))
3065 \begin{equation}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation}
3066 >>> print(latex((2*mu)**Rational(7,2), mode='equation', itex=True))
3067 $$8 \sqrt{2} \mu^{\frac{7}{2}}$$
3069 Fraction options:
3071 >>> print(latex((2*tau)**Rational(7,2), fold_frac_powers=True))
3072 8 \sqrt{2} \tau^{7/2}
3073 >>> print(latex((2*tau)**sin(Rational(7,2))))
3074 \left(2 \tau\right)^{\sin{\left(\frac{7}{2} \right)}}
3075 >>> print(latex((2*tau)**sin(Rational(7,2)), fold_func_brackets=True))
3076 \left(2 \tau\right)^{\sin {\frac{7}{2}}}
3077 >>> print(latex(3*x**2/y))
3078 \frac{3 x^{2}}{y}
3079 >>> print(latex(3*x**2/y, fold_short_frac=True))
3080 3 x^{2} / y
3081 >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=2))
3082 \frac{\int r\, dr}{2 \pi}
3083 >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=0))
3084 \frac{1}{2 \pi} \int r\, dr
3086 Multiplication options:
3088 >>> print(latex((2*tau)**sin(Rational(7,2)), mul_symbol="times"))
3089 \left(2 \times \tau\right)^{\sin{\left(\frac{7}{2} \right)}}
3091 Trig options:
3093 >>> print(latex(asin(Rational(7,2))))
3094 \operatorname{asin}{\left(\frac{7}{2} \right)}
3095 >>> print(latex(asin(Rational(7,2)), inv_trig_style="full"))
3096 \arcsin{\left(\frac{7}{2} \right)}
3097 >>> print(latex(asin(Rational(7,2)), inv_trig_style="power"))
3098 \sin^{-1}{\left(\frac{7}{2} \right)}
3100 Matrix options:
3102 >>> print(latex(Matrix(2, 1, [x, y])))
3103 \left[\begin{matrix}x\\y\end{matrix}\right]
3104 >>> print(latex(Matrix(2, 1, [x, y]), mat_str = "array"))
3105 \left[\begin{array}{c}x\\y\end{array}\right]
3106 >>> print(latex(Matrix(2, 1, [x, y]), mat_delim="("))
3107 \left(\begin{matrix}x\\y\end{matrix}\right)
3109 Custom printing of symbols:
3111 >>> print(latex(x**2, symbol_names={x: 'x_i'}))
3112 x_i^{2}
3114 Logarithms:
3116 >>> print(latex(log(10)))
3117 \log{\left(10 \right)}
3118 >>> print(latex(log(10), ln_notation=True))
3119 \ln{\left(10 \right)}
3121 ``latex()`` also supports the builtin container types :class:`list`,
3122 :class:`tuple`, and :class:`dict`:
3124 >>> print(latex([2/x, y], mode='inline'))
3125 $\left[ 2 / x, \ y\right]$
3127 Unsupported types are rendered as monospaced plaintext:
3129 >>> print(latex(int))
3130 \mathtt{\text{<class 'int'>}}
3131 >>> print(latex("plain % text"))
3132 \mathtt{\text{plain \% text}}
3134 See :ref:`printer_method_example` for an example of how to override
3135 this behavior for your own types by implementing ``_latex``.
3137 .. versionchanged:: 1.7.0
3138 Unsupported types no longer have their ``str`` representation treated as valid latex.
3140 """
3141 return LatexPrinter(settings).doprint(expr)
3144def print_latex(expr, **settings):
3145 """Prints LaTeX representation of the given expression. Takes the same
3146 settings as ``latex()``."""
3148 print(latex(expr, **settings))
3151def multiline_latex(lhs, rhs, terms_per_line=1, environment="align*", use_dots=False, **settings):
3152 r"""
3153 This function generates a LaTeX equation with a multiline right-hand side
3154 in an ``align*``, ``eqnarray`` or ``IEEEeqnarray`` environment.
3156 Parameters
3157 ==========
3159 lhs : Expr
3160 Left-hand side of equation
3162 rhs : Expr
3163 Right-hand side of equation
3165 terms_per_line : integer, optional
3166 Number of terms per line to print. Default is 1.
3168 environment : "string", optional
3169 Which LaTeX wnvironment to use for the output. Options are "align*"
3170 (default), "eqnarray", and "IEEEeqnarray".
3172 use_dots : boolean, optional
3173 If ``True``, ``\\dots`` is added to the end of each line. Default is ``False``.
3175 Examples
3176 ========
3178 >>> from sympy import multiline_latex, symbols, sin, cos, exp, log, I
3179 >>> x, y, alpha = symbols('x y alpha')
3180 >>> expr = sin(alpha*y) + exp(I*alpha) - cos(log(y))
3181 >>> print(multiline_latex(x, expr))
3182 \begin{align*}
3183 x = & e^{i \alpha} \\
3184 & + \sin{\left(\alpha y \right)} \\
3185 & - \cos{\left(\log{\left(y \right)} \right)}
3186 \end{align*}
3188 Using at most two terms per line:
3189 >>> print(multiline_latex(x, expr, 2))
3190 \begin{align*}
3191 x = & e^{i \alpha} + \sin{\left(\alpha y \right)} \\
3192 & - \cos{\left(\log{\left(y \right)} \right)}
3193 \end{align*}
3195 Using ``eqnarray`` and dots:
3196 >>> print(multiline_latex(x, expr, terms_per_line=2, environment="eqnarray", use_dots=True))
3197 \begin{eqnarray}
3198 x & = & e^{i \alpha} + \sin{\left(\alpha y \right)} \dots\nonumber\\
3199 & & - \cos{\left(\log{\left(y \right)} \right)}
3200 \end{eqnarray}
3202 Using ``IEEEeqnarray``:
3203 >>> print(multiline_latex(x, expr, environment="IEEEeqnarray"))
3204 \begin{IEEEeqnarray}{rCl}
3205 x & = & e^{i \alpha} \nonumber\\
3206 & & + \sin{\left(\alpha y \right)} \nonumber\\
3207 & & - \cos{\left(\log{\left(y \right)} \right)}
3208 \end{IEEEeqnarray}
3210 Notes
3211 =====
3213 All optional parameters from ``latex`` can also be used.
3215 """
3217 # Based on code from https://github.com/sympy/sympy/issues/3001
3218 l = LatexPrinter(**settings)
3219 if environment == "eqnarray":
3220 result = r'\begin{eqnarray}' + '\n'
3221 first_term = '& = &'
3222 nonumber = r'\nonumber'
3223 end_term = '\n\\end{eqnarray}'
3224 doubleet = True
3225 elif environment == "IEEEeqnarray":
3226 result = r'\begin{IEEEeqnarray}{rCl}' + '\n'
3227 first_term = '& = &'
3228 nonumber = r'\nonumber'
3229 end_term = '\n\\end{IEEEeqnarray}'
3230 doubleet = True
3231 elif environment == "align*":
3232 result = r'\begin{align*}' + '\n'
3233 first_term = '= &'
3234 nonumber = ''
3235 end_term = '\n\\end{align*}'
3236 doubleet = False
3237 else:
3238 raise ValueError("Unknown environment: {}".format(environment))
3239 dots = ''
3240 if use_dots:
3241 dots=r'\dots'
3242 terms = rhs.as_ordered_terms()
3243 n_terms = len(terms)
3244 term_count = 1
3245 for i in range(n_terms):
3246 term = terms[i]
3247 term_start = ''
3248 term_end = ''
3249 sign = '+'
3250 if term_count > terms_per_line:
3251 if doubleet:
3252 term_start = '& & '
3253 else:
3254 term_start = '& '
3255 term_count = 1
3256 if term_count == terms_per_line:
3257 # End of line
3258 if i < n_terms-1:
3259 # There are terms remaining
3260 term_end = dots + nonumber + r'\\' + '\n'
3261 else:
3262 term_end = ''
3264 if term.as_ordered_factors()[0] == -1:
3265 term = -1*term
3266 sign = r'-'
3267 if i == 0: # beginning
3268 if sign == '+':
3269 sign = ''
3270 result += r'{:s} {:s}{:s} {:s} {:s}'.format(l.doprint(lhs),
3271 first_term, sign, l.doprint(term), term_end)
3272 else:
3273 result += r'{:s}{:s} {:s} {:s}'.format(term_start, sign,
3274 l.doprint(term), term_end)
3275 term_count += 1
3276 result += end_term
3277 return result