Source code for sympy.printing.latex

"""
A Printer which converts an expression into its LaTeX equivalent.
"""

from __future__ import print_function, division

import itertools

from sympy.core import S, Add, Symbol, Mod
from sympy.core.alphabets import greeks
from sympy.core.containers import Tuple
from sympy.core.function import _coeff_isneg, AppliedUndef, Derivative
from sympy.core.operations import AssocOp
from sympy.core.sympify import SympifyError
from sympy.logic.boolalg import true

# sympy.printing imports
from sympy.printing.precedence import precedence_traditional
from sympy.printing.printer import Printer
from sympy.printing.conventions import split_super_sub, requires_partial
from sympy.printing.precedence import precedence, PRECEDENCE

import mpmath.libmp as mlib
from mpmath.libmp import prec_to_dps

from sympy.core.compatibility import default_sort_key, range
from sympy.utilities.iterables import has_variety

import re

# Hand-picked functions which can be used directly in both LaTeX and MathJax
# Complete list at
# https://docs.mathjax.org/en/latest/tex.html#supported-latex-commands
# This variable only contains those functions which sympy uses.
accepted_latex_functions = ['arcsin', 'arccos', 'arctan', 'sin', 'cos', 'tan',
                            'sinh', 'cosh', 'tanh', 'sqrt', 'ln', 'log', 'sec',
                            'csc', 'cot', 'coth', 're', 'im', 'frac', 'root',
                            'arg',
                            ]

tex_greek_dictionary = {
    'Alpha': 'A',
    'Beta': 'B',
    'Gamma': r'\Gamma',
    'Delta': r'\Delta',
    'Epsilon': 'E',
    'Zeta': 'Z',
    'Eta': 'H',
    'Theta': r'\Theta',
    'Iota': 'I',
    'Kappa': 'K',
    'Lambda': r'\Lambda',
    'Mu': 'M',
    'Nu': 'N',
    'Xi': r'\Xi',
    'omicron': 'o',
    'Omicron': 'O',
    'Pi': r'\Pi',
    'Rho': 'P',
    'Sigma': r'\Sigma',
    'Tau': 'T',
    'Upsilon': r'\Upsilon',
    'Phi': r'\Phi',
    'Chi': 'X',
    'Psi': r'\Psi',
    'Omega': r'\Omega',
    'lamda': r'\lambda',
    'Lamda': r'\Lambda',
    'khi': r'\chi',
    'Khi': r'X',
    'varepsilon': r'\varepsilon',
    'varkappa': r'\varkappa',
    'varphi': r'\varphi',
    'varpi': r'\varpi',
    'varrho': r'\varrho',
    'varsigma': r'\varsigma',
    'vartheta': r'\vartheta',
}

other_symbols = set(['aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', 'hbar',
                     'hslash', 'mho', 'wp', ])

# Variable name modifiers
modifier_dict = {
    # Accents
    'mathring': lambda s: r'\mathring{'+s+r'}',
    'ddddot': lambda s: r'\ddddot{'+s+r'}',
    'dddot': lambda s: r'\dddot{'+s+r'}',
    'ddot': lambda s: r'\ddot{'+s+r'}',
    'dot': lambda s: r'\dot{'+s+r'}',
    'check': lambda s: r'\check{'+s+r'}',
    'breve': lambda s: r'\breve{'+s+r'}',
    'acute': lambda s: r'\acute{'+s+r'}',
    'grave': lambda s: r'\grave{'+s+r'}',
    'tilde': lambda s: r'\tilde{'+s+r'}',
    'hat': lambda s: r'\hat{'+s+r'}',
    'bar': lambda s: r'\bar{'+s+r'}',
    'vec': lambda s: r'\vec{'+s+r'}',
    'prime': lambda s: "{"+s+"}'",
    'prm': lambda s: "{"+s+"}'",
    # Faces
    'bold': lambda s: r'\boldsymbol{'+s+r'}',
    'bm': lambda s: r'\boldsymbol{'+s+r'}',
    'cal': lambda s: r'\mathcal{'+s+r'}',
    'scr': lambda s: r'\mathscr{'+s+r'}',
    'frak': lambda s: r'\mathfrak{'+s+r'}',
    # Brackets
    'norm': lambda s: r'\left\|{'+s+r'}\right\|',
    'avg': lambda s: r'\left\langle{'+s+r'}\right\rangle',
    'abs': lambda s: r'\left|{'+s+r'}\right|',
    'mag': lambda s: r'\left|{'+s+r'}\right|',
}

greek_letters_set = frozenset(greeks)

_between_two_numbers_p = (
    re.compile(r'[0-9][} ]*$'),  # search
    re.compile(r'[{ ]*[-+0-9]'),  # match
)


[docs]class LatexPrinter(Printer): printmethod = "_latex" _default_settings = { "fold_frac_powers": False, "fold_func_brackets": False, "fold_short_frac": None, "inv_trig_style": "abbreviated", "itex": False, "ln_notation": False, "long_frac_ratio": None, "mat_delim": "[", "mat_str": None, "mode": "plain", "mul_symbol": None, "order": None, "symbol_names": {}, "root_notation": True, "mat_symbol_style": "plain", "imaginary_unit": "i", "gothic_re_im": False, } def __init__(self, settings=None): Printer.__init__(self, settings) if 'mode' in self._settings: valid_modes = ['inline', 'plain', 'equation', 'equation*'] if self._settings['mode'] not in valid_modes: raise ValueError("'mode' must be one of 'inline', 'plain', " "'equation' or 'equation*'") if self._settings['fold_short_frac'] is None and \ self._settings['mode'] == 'inline': self._settings['fold_short_frac'] = True mul_symbol_table = { None: r" ", "ldot": r" \,.\, ", "dot": r" \cdot ", "times": r" \times " } try: self._settings['mul_symbol_latex'] = \ mul_symbol_table[self._settings['mul_symbol']] except KeyError: self._settings['mul_symbol_latex'] = \ self._settings['mul_symbol'] try: self._settings['mul_symbol_latex_numbers'] = \ mul_symbol_table[self._settings['mul_symbol'] or 'dot'] except KeyError: if (self._settings['mul_symbol'].strip() in ['', ' ', '\\', '\\,', '\\:', '\\;', '\\quad']): self._settings['mul_symbol_latex_numbers'] = \ mul_symbol_table['dot'] else: self._settings['mul_symbol_latex_numbers'] = \ self._settings['mul_symbol'] self._delim_dict = {'(': ')', '[': ']'} imaginary_unit_table = { None: r"i", "i": r"i", "ri": r"\mathrm{i}", "ti": r"\text{i}", "j": r"j", "rj": r"\mathrm{j}", "tj": r"\text{j}", } try: self._settings['imaginary_unit_latex'] = \ imaginary_unit_table[self._settings['imaginary_unit']] except KeyError: self._settings['imaginary_unit_latex'] = \ self._settings['imaginary_unit'] def parenthesize(self, item, level, strict=False): prec_val = precedence_traditional(item) if (prec_val < level) or ((not strict) and prec_val <= level): return r"\left({}\right)".format(self._print(item)) else: return self._print(item) def doprint(self, expr): tex = Printer.doprint(self, expr) if self._settings['mode'] == 'plain': return tex elif self._settings['mode'] == 'inline': return r"$%s$" % tex elif self._settings['itex']: return r"$$%s$$" % tex else: env_str = self._settings['mode'] return r"\begin{%s}%s\end{%s}" % (env_str, tex, env_str) def _needs_brackets(self, expr): """ Returns True if the expression needs to be wrapped in brackets when printed, False otherwise. For example: a + b => True; a => False; 10 => False; -10 => True. """ return not ((expr.is_Integer and expr.is_nonnegative) or (expr.is_Atom and (expr is not S.NegativeOne and expr.is_Rational is False))) def _needs_function_brackets(self, expr): """ Returns True if the expression needs to be wrapped in brackets when passed as an argument to a function, False otherwise. This is a more liberal version of _needs_brackets, in that many expressions which need to be wrapped in brackets when added/subtracted/raised to a power do not need them when passed to a function. Such an example is a*b. """ if not self._needs_brackets(expr): return False else: # Muls of the form a*b*c... can be folded if expr.is_Mul and not self._mul_is_clean(expr): return True # Pows which don't need brackets can be folded elif expr.is_Pow and not self._pow_is_clean(expr): return True # Add and Function always need brackets elif expr.is_Add or expr.is_Function: return True else: return False def _needs_mul_brackets(self, expr, first=False, last=False): """ Returns True if the expression needs to be wrapped in brackets when printed as part of a Mul, False otherwise. This is True for Add, but also for some container objects that would not need brackets when appearing last in a Mul, e.g. an Integral. ``last=True`` specifies that this expr is the last to appear in a Mul. ``first=True`` specifies that this expr is the first to appear in a Mul. """ from sympy import Integral, Product, Sum if expr.is_Mul: if not first and _coeff_isneg(expr): return True elif precedence_traditional(expr) < PRECEDENCE["Mul"]: return True elif expr.is_Relational: return True if expr.is_Piecewise: return True if any([expr.has(x) for x in (Mod,)]): return True if (not last and any([expr.has(x) for x in (Integral, Product, Sum)])): return True return False def _needs_add_brackets(self, expr): """ Returns True if the expression needs to be wrapped in brackets when printed as part of an Add, False otherwise. This is False for most things. """ if expr.is_Relational: return True if any([expr.has(x) for x in (Mod,)]): return True if expr.is_Add: return True return False def _mul_is_clean(self, expr): for arg in expr.args: if arg.is_Function: return False return True def _pow_is_clean(self, expr): return not self._needs_brackets(expr.base) def _do_exponent(self, expr, exp): if exp is not None: return r"\left(%s\right)^{%s}" % (expr, exp) else: return expr def _print_Basic(self, expr): ls = [self._print(o) for o in expr.args] return self._deal_with_super_sub(expr.__class__.__name__) + \ r"\left(%s\right)" % ", ".join(ls) def _print_bool(self, e): return r"\text{%s}" % e _print_BooleanTrue = _print_bool _print_BooleanFalse = _print_bool def _print_NoneType(self, e): return r"\text{%s}" % e def _print_Add(self, expr, order=None): if self.order == 'none': terms = list(expr.args) else: terms = self._as_ordered_terms(expr, order=order) tex = "" for i, term in enumerate(terms): if i == 0: pass elif _coeff_isneg(term): tex += " - " term = -term else: tex += " + " term_tex = self._print(term) if self._needs_add_brackets(term): term_tex = r"\left(%s\right)" % term_tex tex += term_tex return tex def _print_Cycle(self, expr): from sympy.combinatorics.permutations import Permutation if expr.size == 0: return r"\left( \right)" expr = Permutation(expr) expr_perm = expr.cyclic_form siz = expr.size if expr.array_form[-1] == siz - 1: expr_perm = expr_perm + [[siz - 1]] term_tex = '' for i in expr_perm: term_tex += str(i).replace(',', r"\;") term_tex = term_tex.replace('[', r"\left( ") term_tex = term_tex.replace(']', r"\right)") return term_tex _print_Permutation = _print_Cycle def _print_Float(self, expr): # Based off of that in StrPrinter dps = prec_to_dps(expr._prec) str_real = mlib.to_str(expr._mpf_, dps, strip_zeros=True) # Must always have a mul symbol (as 2.5 10^{20} just looks odd) # thus we use the number separator separator = self._settings['mul_symbol_latex_numbers'] if 'e' in str_real: (mant, exp) = str_real.split('e') if exp[0] == '+': exp = exp[1:] return r"%s%s10^{%s}" % (mant, separator, exp) elif str_real == "+inf": return r"\infty" elif str_real == "-inf": return r"- \infty" else: return str_real def _print_Cross(self, expr): vec1 = expr._expr1 vec2 = expr._expr2 return r"%s \times %s" % (self.parenthesize(vec1, PRECEDENCE['Mul']), self.parenthesize(vec2, PRECEDENCE['Mul'])) def _print_Curl(self, expr): vec = expr._expr return r"\nabla\times %s" % self.parenthesize(vec, PRECEDENCE['Mul']) def _print_Divergence(self, expr): vec = expr._expr return r"\nabla\cdot %s" % self.parenthesize(vec, PRECEDENCE['Mul']) def _print_Dot(self, expr): vec1 = expr._expr1 vec2 = expr._expr2 return r"%s \cdot %s" % (self.parenthesize(vec1, PRECEDENCE['Mul']), self.parenthesize(vec2, PRECEDENCE['Mul'])) def _print_Gradient(self, expr): func = expr._expr return r"\nabla %s" % self.parenthesize(func, PRECEDENCE['Mul']) def _print_Laplacian(self, expr): func = expr._expr return r"\triangle %s" % self.parenthesize(func, PRECEDENCE['Mul']) def _print_Mul(self, expr): from sympy.core.power import Pow from sympy.physics.units import Quantity include_parens = False if _coeff_isneg(expr): expr = -expr tex = "- " if expr.is_Add: tex += "(" include_parens = True else: tex = "" from sympy.simplify import fraction numer, denom = fraction(expr, exact=True) separator = self._settings['mul_symbol_latex'] numbersep = self._settings['mul_symbol_latex_numbers'] def convert(expr): if not expr.is_Mul: return str(self._print(expr)) else: _tex = last_term_tex = "" if self.order not in ('old', 'none'): args = expr.as_ordered_factors() else: args = list(expr.args) # If quantities are present append them at the back args = sorted(args, key=lambda x: isinstance(x, Quantity) or (isinstance(x, Pow) and isinstance(x.base, Quantity))) for i, term in enumerate(args): term_tex = self._print(term) if self._needs_mul_brackets(term, first=(i == 0), last=(i == len(args) - 1)): term_tex = r"\left(%s\right)" % term_tex if _between_two_numbers_p[0].search(last_term_tex) and \ _between_two_numbers_p[1].match(term_tex): # between two numbers _tex += numbersep elif _tex: _tex += separator _tex += term_tex last_term_tex = term_tex return _tex if denom is S.One and Pow(1, -1, evaluate=False) not in expr.args: # use the original expression here, since fraction() may have # altered it when producing numer and denom tex += convert(expr) else: snumer = convert(numer) sdenom = convert(denom) ldenom = len(sdenom.split()) ratio = self._settings['long_frac_ratio'] if self._settings['fold_short_frac'] and ldenom <= 2 and \ "^" not in sdenom: # handle short fractions if self._needs_mul_brackets(numer, last=False): tex += r"\left(%s\right) / %s" % (snumer, sdenom) else: tex += r"%s / %s" % (snumer, sdenom) elif ratio is not None and \ len(snumer.split()) > ratio*ldenom: # handle long fractions if self._needs_mul_brackets(numer, last=True): tex += r"\frac{1}{%s}%s\left(%s\right)" \ % (sdenom, separator, snumer) elif numer.is_Mul: # split a long numerator a = S.One b = S.One for x in numer.args: if self._needs_mul_brackets(x, last=False) or \ len(convert(a*x).split()) > ratio*ldenom or \ (b.is_commutative is x.is_commutative is False): b *= x else: a *= x if self._needs_mul_brackets(b, last=True): tex += r"\frac{%s}{%s}%s\left(%s\right)" \ % (convert(a), sdenom, separator, convert(b)) else: tex += r"\frac{%s}{%s}%s%s" \ % (convert(a), sdenom, separator, convert(b)) else: tex += r"\frac{1}{%s}%s%s" % (sdenom, separator, snumer) else: tex += r"\frac{%s}{%s}" % (snumer, sdenom) if include_parens: tex += ")" return tex def _print_Pow(self, expr): # Treat x**Rational(1,n) as special case if expr.exp.is_Rational and abs(expr.exp.p) == 1 and expr.exp.q != 1 \ and self._settings['root_notation']: base = self._print(expr.base) expq = expr.exp.q if expq == 2: tex = r"\sqrt{%s}" % base elif self._settings['itex']: tex = r"\root{%d}{%s}" % (expq, base) else: tex = r"\sqrt[%d]{%s}" % (expq, base) if expr.exp.is_negative: return r"\frac{1}{%s}" % tex else: return tex elif self._settings['fold_frac_powers'] \ and expr.exp.is_Rational \ and expr.exp.q != 1: base = self.parenthesize(expr.base, PRECEDENCE['Pow']) p, q = expr.exp.p, expr.exp.q # issue #12886: add parentheses for superscripts raised to powers if '^' in base and expr.base.is_Symbol: base = r"\left(%s\right)" % base if expr.base.is_Function: return self._print(expr.base, exp="%s/%s" % (p, q)) return r"%s^{%s/%s}" % (base, p, q) elif expr.exp.is_Rational and expr.exp.is_negative and \ expr.base.is_commutative: # special case for 1^(-x), issue 9216 if expr.base == 1: return r"%s^{%s}" % (expr.base, expr.exp) # things like 1/x return self._print_Mul(expr) else: if expr.base.is_Function: return self._print(expr.base, exp=self._print(expr.exp)) else: tex = r"%s^{%s}" exp = self._print(expr.exp) # issue #12886: add parentheses around superscripts raised # to powers base = self.parenthesize(expr.base, PRECEDENCE['Pow']) if '^' in base and expr.base.is_Symbol: base = r"\left(%s\right)" % base elif (isinstance(expr.base, Derivative) and base.startswith(r'\left(') and re.match(r'\\left\(\\d?d?dot', base) and base.endswith(r'\right)')): # don't use parentheses around dotted derivative base = base[6: -7] # remove outermost added parens return tex % (base, exp) def _print_UnevaluatedExpr(self, expr): return self._print(expr.args[0]) def _print_Sum(self, expr): if len(expr.limits) == 1: tex = r"\sum_{%s=%s}^{%s} " % \ tuple([self._print(i) for i in expr.limits[0]]) else: def _format_ineq(l): return r"%s \leq %s \leq %s" % \ tuple([self._print(s) for s in (l[1], l[0], l[2])]) tex = r"\sum_{\substack{%s}} " % \ str.join('\\\\', [_format_ineq(l) for l in expr.limits]) if isinstance(expr.function, Add): tex += r"\left(%s\right)" % self._print(expr.function) else: tex += self._print(expr.function) return tex def _print_Product(self, expr): if len(expr.limits) == 1: tex = r"\prod_{%s=%s}^{%s} " % \ tuple([self._print(i) for i in expr.limits[0]]) else: def _format_ineq(l): return r"%s \leq %s \leq %s" % \ tuple([self._print(s) for s in (l[1], l[0], l[2])]) tex = r"\prod_{\substack{%s}} " % \ str.join('\\\\', [_format_ineq(l) for l in expr.limits]) if isinstance(expr.function, Add): tex += r"\left(%s\right)" % self._print(expr.function) else: tex += self._print(expr.function) return tex def _print_BasisDependent(self, expr): from sympy.vector import Vector o1 = [] if expr == expr.zero: return expr.zero._latex_form if isinstance(expr, Vector): items = expr.separate().items() else: items = [(0, expr)] for system, vect in items: inneritems = list(vect.components.items()) inneritems.sort(key=lambda x: x[0].__str__()) for k, v in inneritems: if v == 1: o1.append(' + ' + k._latex_form) elif v == -1: o1.append(' - ' + k._latex_form) else: arg_str = '(' + LatexPrinter().doprint(v) + ')' o1.append(' + ' + arg_str + k._latex_form) outstr = (''.join(o1)) if outstr[1] != '-': outstr = outstr[3:] else: outstr = outstr[1:] return outstr def _print_Indexed(self, expr): tex_base = self._print(expr.base) tex = '{'+tex_base+'}'+'_{%s}' % ','.join( map(self._print, expr.indices)) return tex def _print_IndexedBase(self, expr): return self._print(expr.label) def _print_Derivative(self, expr): if requires_partial(expr): diff_symbol = r'\partial' else: diff_symbol = r'd' tex = "" dim = 0 for x, num in reversed(expr.variable_count): dim += num if num == 1: tex += r"%s %s" % (diff_symbol, self._print(x)) else: tex += r"%s %s^{%s}" % (diff_symbol, self._print(x), num) if dim == 1: tex = r"\frac{%s}{%s}" % (diff_symbol, tex) else: tex = r"\frac{%s^{%s}}{%s}" % (diff_symbol, dim, tex) return r"%s %s" % (tex, self.parenthesize(expr.expr, PRECEDENCE["Mul"], strict=True)) def _print_Subs(self, subs): expr, old, new = subs.args latex_expr = self._print(expr) latex_old = (self._print(e) for e in old) latex_new = (self._print(e) for e in new) latex_subs = r'\\ '.join( e[0] + '=' + e[1] for e in zip(latex_old, latex_new)) return r'\left. %s \right|_{\substack{ %s }}' % (latex_expr, latex_subs) def _print_Integral(self, expr): tex, symbols = "", [] # Only up to \iiiint exists if len(expr.limits) <= 4 and all(len(lim) == 1 for lim in expr.limits): # Use len(expr.limits)-1 so that syntax highlighters don't think # \" is an escaped quote tex = r"\i" + "i"*(len(expr.limits) - 1) + "nt" symbols = [r"\, d%s" % self._print(symbol[0]) for symbol in expr.limits] else: for lim in reversed(expr.limits): symbol = lim[0] tex += r"\int" if len(lim) > 1: if self._settings['mode'] != 'inline' \ and not self._settings['itex']: tex += r"\limits" if len(lim) == 3: tex += "_{%s}^{%s}" % (self._print(lim[1]), self._print(lim[2])) if len(lim) == 2: tex += "^{%s}" % (self._print(lim[1])) symbols.insert(0, r"\, d%s" % self._print(symbol)) return r"%s %s%s" % (tex, self.parenthesize(expr.function, PRECEDENCE["Mul"], strict=True), "".join(symbols)) def _print_Limit(self, expr): e, z, z0, dir = expr.args tex = r"\lim_{%s \to " % self._print(z) if str(dir) == '+-' or z0 in (S.Infinity, S.NegativeInfinity): tex += r"%s}" % self._print(z0) else: tex += r"%s^%s}" % (self._print(z0), self._print(dir)) if isinstance(e, AssocOp): return r"%s\left(%s\right)" % (tex, self._print(e)) else: return r"%s %s" % (tex, self._print(e)) def _hprint_Function(self, func): r''' Logic to decide how to render a function to latex - if it is a recognized latex name, use the appropriate latex command - if it is a single letter, just use that letter - if it is a longer name, then put \operatorname{} around it and be mindful of undercores in the name ''' func = self._deal_with_super_sub(func) if func in accepted_latex_functions: name = r"\%s" % func elif len(func) == 1 or func.startswith('\\'): name = func else: name = r"\operatorname{%s}" % func return name def _print_Function(self, expr, exp=None): r''' Render functions to LaTeX, handling functions that LaTeX knows about e.g., sin, cos, ... by using the proper LaTeX command (\sin, \cos, ...). For single-letter function names, render them as regular LaTeX math symbols. For multi-letter function names that LaTeX does not know about, (e.g., Li, sech) use \operatorname{} so that the function name is rendered in Roman font and LaTeX handles spacing properly. expr is the expression involving the function exp is an exponent ''' func = expr.func.__name__ if hasattr(self, '_print_' + func) and \ not isinstance(expr, AppliedUndef): return getattr(self, '_print_' + func)(expr, exp) else: args = [str(self._print(arg)) for arg in expr.args] # How inverse trig functions should be displayed, formats are: # abbreviated: asin, full: arcsin, power: sin^-1 inv_trig_style = self._settings['inv_trig_style'] # If we are dealing with a power-style inverse trig function inv_trig_power_case = False # If it is applicable to fold the argument brackets can_fold_brackets = self._settings['fold_func_brackets'] and \ len(args) == 1 and \ not self._needs_function_brackets(expr.args[0]) inv_trig_table = ["asin", "acos", "atan", "acsc", "asec", "acot"] # If the function is an inverse trig function, handle the style if func in inv_trig_table: if inv_trig_style == "abbreviated": pass elif inv_trig_style == "full": func = "arc" + func[1:] elif inv_trig_style == "power": func = func[1:] inv_trig_power_case = True # Can never fold brackets if we're raised to a power if exp is not None: can_fold_brackets = False if inv_trig_power_case: if func in accepted_latex_functions: name = r"\%s^{-1}" % func else: name = r"\operatorname{%s}^{-1}" % func elif exp is not None: name = r'%s^{%s}' % (self._hprint_Function(func), exp) else: name = self._hprint_Function(func) if can_fold_brackets: if func in accepted_latex_functions: # Wrap argument safely to avoid parse-time conflicts # with the function name itself name += r" {%s}" else: name += r"%s" else: name += r"{\left(%s \right)}" if inv_trig_power_case and exp is not None: name += r"^{%s}" % exp return name % ",".join(args) def _print_UndefinedFunction(self, expr): return self._hprint_Function(str(expr)) @property def _special_function_classes(self): from sympy.functions.special.tensor_functions import KroneckerDelta from sympy.functions.special.gamma_functions import gamma, lowergamma from sympy.functions.special.beta_functions import beta from sympy.functions.special.delta_functions import DiracDelta from sympy.functions.special.error_functions import Chi return {KroneckerDelta: r'\delta', gamma: r'\Gamma', lowergamma: r'\gamma', beta: r'\operatorname{B}', DiracDelta: r'\delta', Chi: r'\operatorname{Chi}'} def _print_FunctionClass(self, expr): for cls in self._special_function_classes: if issubclass(expr, cls) and expr.__name__ == cls.__name__: return self._special_function_classes[cls] return self._hprint_Function(str(expr)) def _print_Lambda(self, expr): symbols, expr = expr.args if len(symbols) == 1: symbols = self._print(symbols[0]) else: symbols = self._print(tuple(symbols)) tex = r"\left( %s \mapsto %s \right)" % (symbols, self._print(expr)) return tex def _hprint_variadic_function(self, expr, exp=None): args = sorted(expr.args, key=default_sort_key) texargs = [r"%s" % self._print(symbol) for symbol in args] tex = r"\%s\left(%s\right)" % (self._print((str(expr.func)).lower()), ", ".join(texargs)) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex _print_Min = _print_Max = _hprint_variadic_function def _print_floor(self, expr, exp=None): tex = r"\left\lfloor{%s}\right\rfloor" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_ceiling(self, expr, exp=None): tex = r"\left\lceil{%s}\right\rceil" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_log(self, expr, exp=None): if not self._settings["ln_notation"]: tex = r"\log{\left(%s \right)}" % self._print(expr.args[0]) else: tex = r"\ln{\left(%s \right)}" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_Abs(self, expr, exp=None): tex = r"\left|{%s}\right|" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex _print_Determinant = _print_Abs def _print_re(self, expr, exp=None): if self._settings['gothic_re_im']: tex = r"\Re{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Atom']) else: tex = r"\operatorname{{re}}{{{}}}".format(self.parenthesize(expr.args[0], PRECEDENCE['Atom'])) return self._do_exponent(tex, exp) def _print_im(self, expr, exp=None): if self._settings['gothic_re_im']: tex = r"\Im{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Atom']) else: tex = r"\operatorname{{im}}{{{}}}".format(self.parenthesize(expr.args[0], PRECEDENCE['Atom'])) return self._do_exponent(tex, exp) def _print_Not(self, e): from sympy import Equivalent, Implies if isinstance(e.args[0], Equivalent): return self._print_Equivalent(e.args[0], r"\not\Leftrightarrow") if isinstance(e.args[0], Implies): return self._print_Implies(e.args[0], r"\not\Rightarrow") if (e.args[0].is_Boolean): return r"\neg (%s)" % self._print(e.args[0]) else: return r"\neg %s" % self._print(e.args[0]) def _print_LogOp(self, args, char): arg = args[0] if arg.is_Boolean and not arg.is_Not: tex = r"\left(%s\right)" % self._print(arg) else: tex = r"%s" % self._print(arg) for arg in args[1:]: if arg.is_Boolean and not arg.is_Not: tex += r" %s \left(%s\right)" % (char, self._print(arg)) else: tex += r" %s %s" % (char, self._print(arg)) return tex def _print_And(self, e): args = sorted(e.args, key=default_sort_key) return self._print_LogOp(args, r"\wedge") def _print_Or(self, e): args = sorted(e.args, key=default_sort_key) return self._print_LogOp(args, r"\vee") def _print_Xor(self, e): args = sorted(e.args, key=default_sort_key) return self._print_LogOp(args, r"\veebar") def _print_Implies(self, e, altchar=None): return self._print_LogOp(e.args, altchar or r"\Rightarrow") def _print_Equivalent(self, e, altchar=None): args = sorted(e.args, key=default_sort_key) return self._print_LogOp(args, altchar or r"\Leftrightarrow") def _print_conjugate(self, expr, exp=None): tex = r"\overline{%s}" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_polar_lift(self, expr, exp=None): func = r"\operatorname{polar\_lift}" arg = r"{\left(%s \right)}" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}%s" % (func, exp, arg) else: return r"%s%s" % (func, arg) def _print_ExpBase(self, expr, exp=None): # TODO should exp_polar be printed differently? # what about exp_polar(0), exp_polar(1)? tex = r"e^{%s}" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_elliptic_k(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"K^{%s}%s" % (exp, tex) else: return r"K%s" % tex def _print_elliptic_f(self, expr, exp=None): tex = r"\left(%s\middle| %s\right)" % \ (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"F^{%s}%s" % (exp, tex) else: return r"F%s" % tex def _print_elliptic_e(self, expr, exp=None): if len(expr.args) == 2: tex = r"\left(%s\middle| %s\right)" % \ (self._print(expr.args[0]), self._print(expr.args[1])) else: tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"E^{%s}%s" % (exp, tex) else: return r"E%s" % tex def _print_elliptic_pi(self, expr, exp=None): if len(expr.args) == 3: tex = r"\left(%s; %s\middle| %s\right)" % \ (self._print(expr.args[0]), self._print(expr.args[1]), self._print(expr.args[2])) else: tex = r"\left(%s\middle| %s\right)" % \ (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"\Pi^{%s}%s" % (exp, tex) else: return r"\Pi%s" % tex def _print_beta(self, expr, exp=None): tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"\operatorname{B}^{%s}%s" % (exp, tex) else: return r"\operatorname{B}%s" % tex def _print_uppergamma(self, expr, exp=None): tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"\Gamma^{%s}%s" % (exp, tex) else: return r"\Gamma%s" % tex def _print_lowergamma(self, expr, exp=None): tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"\gamma^{%s}%s" % (exp, tex) else: return r"\gamma%s" % tex def _hprint_one_arg_func(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}%s" % (self._print(expr.func), exp, tex) else: return r"%s%s" % (self._print(expr.func), tex) _print_gamma = _hprint_one_arg_func def _print_Chi(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"\operatorname{Chi}^{%s}%s" % (exp, tex) else: return r"\operatorname{Chi}%s" % tex def _print_expint(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[1]) nu = self._print(expr.args[0]) if exp is not None: return r"\operatorname{E}_{%s}^{%s}%s" % (nu, exp, tex) else: return r"\operatorname{E}_{%s}%s" % (nu, tex) def _print_fresnels(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"S^{%s}%s" % (exp, tex) else: return r"S%s" % tex def _print_fresnelc(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"C^{%s}%s" % (exp, tex) else: return r"C%s" % tex def _print_subfactorial(self, expr, exp=None): tex = r"!%s" % self.parenthesize(expr.args[0], PRECEDENCE["Func"]) if exp is not None: return r"\left(%s\right)^{%s}" % (tex, exp) else: return tex def _print_factorial(self, expr, exp=None): tex = r"%s!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_factorial2(self, expr, exp=None): tex = r"%s!!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_binomial(self, expr, exp=None): tex = r"{\binom{%s}{%s}}" % (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_RisingFactorial(self, expr, exp=None): n, k = expr.args base = r"%s" % self.parenthesize(n, PRECEDENCE['Func']) tex = r"{%s}^{\left(%s\right)}" % (base, self._print(k)) return self._do_exponent(tex, exp) def _print_FallingFactorial(self, expr, exp=None): n, k = expr.args sub = r"%s" % self.parenthesize(k, PRECEDENCE['Func']) tex = r"{\left(%s\right)}_{%s}" % (self._print(n), sub) return self._do_exponent(tex, exp) def _hprint_BesselBase(self, expr, exp, sym): tex = r"%s" % (sym) need_exp = False if exp is not None: if tex.find('^') == -1: tex = r"%s^{%s}" % (tex, self._print(exp)) else: need_exp = True tex = r"%s_{%s}\left(%s\right)" % (tex, self._print(expr.order), self._print(expr.argument)) if need_exp: tex = self._do_exponent(tex, exp) return tex def _hprint_vec(self, vec): if not vec: return "" s = "" for i in vec[:-1]: s += "%s, " % self._print(i) s += self._print(vec[-1]) return s def _print_besselj(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'J') def _print_besseli(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'I') def _print_besselk(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'K') def _print_bessely(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'Y') def _print_yn(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'y') def _print_jn(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'j') def _print_hankel1(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'H^{(1)}') def _print_hankel2(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'H^{(2)}') def _print_hn1(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'h^{(1)}') def _print_hn2(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'h^{(2)}') def _hprint_airy(self, expr, exp=None, notation=""): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}%s" % (notation, exp, tex) else: return r"%s%s" % (notation, tex) def _hprint_airy_prime(self, expr, exp=None, notation=""): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"{%s^\prime}^{%s}%s" % (notation, exp, tex) else: return r"%s^\prime%s" % (notation, tex) def _print_airyai(self, expr, exp=None): return self._hprint_airy(expr, exp, 'Ai') def _print_airybi(self, expr, exp=None): return self._hprint_airy(expr, exp, 'Bi') def _print_airyaiprime(self, expr, exp=None): return self._hprint_airy_prime(expr, exp, 'Ai') def _print_airybiprime(self, expr, exp=None): return self._hprint_airy_prime(expr, exp, 'Bi') def _print_hyper(self, expr, exp=None): tex = r"{{}_{%s}F_{%s}\left(\begin{matrix} %s \\ %s \end{matrix}" \ r"\middle| {%s} \right)}" % \ (self._print(len(expr.ap)), self._print(len(expr.bq)), self._hprint_vec(expr.ap), self._hprint_vec(expr.bq), self._print(expr.argument)) if exp is not None: tex = r"{%s}^{%s}" % (tex, self._print(exp)) return tex def _print_meijerg(self, expr, exp=None): tex = r"{G_{%s, %s}^{%s, %s}\left(\begin{matrix} %s & %s \\" \ r"%s & %s \end{matrix} \middle| {%s} \right)}" % \ (self._print(len(expr.ap)), self._print(len(expr.bq)), self._print(len(expr.bm)), self._print(len(expr.an)), self._hprint_vec(expr.an), self._hprint_vec(expr.aother), self._hprint_vec(expr.bm), self._hprint_vec(expr.bother), self._print(expr.argument)) if exp is not None: tex = r"{%s}^{%s}" % (tex, self._print(exp)) return tex def _print_dirichlet_eta(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"\eta^{%s}%s" % (self._print(exp), tex) return r"\eta%s" % tex def _print_zeta(self, expr, exp=None): if len(expr.args) == 2: tex = r"\left(%s, %s\right)" % tuple(map(self._print, expr.args)) else: tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"\zeta^{%s}%s" % (self._print(exp), tex) return r"\zeta%s" % tex def _print_lerchphi(self, expr, exp=None): tex = r"\left(%s, %s, %s\right)" % tuple(map(self._print, expr.args)) if exp is None: return r"\Phi%s" % tex return r"\Phi^{%s}%s" % (self._print(exp), tex) def _print_polylog(self, expr, exp=None): s, z = map(self._print, expr.args) tex = r"\left(%s\right)" % z if exp is None: return r"\operatorname{Li}_{%s}%s" % (s, tex) return r"\operatorname{Li}_{%s}^{%s}%s" % (s, self._print(exp), tex) def _print_jacobi(self, expr, exp=None): n, a, b, x = map(self._print, expr.args) tex = r"P_{%s}^{\left(%s,%s\right)}\left(%s\right)" % (n, a, b, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_gegenbauer(self, expr, exp=None): n, a, x = map(self._print, expr.args) tex = r"C_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_chebyshevt(self, expr, exp=None): n, x = map(self._print, expr.args) tex = r"T_{%s}\left(%s\right)" % (n, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_chebyshevu(self, expr, exp=None): n, x = map(self._print, expr.args) tex = r"U_{%s}\left(%s\right)" % (n, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_legendre(self, expr, exp=None): n, x = map(self._print, expr.args) tex = r"P_{%s}\left(%s\right)" % (n, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_assoc_legendre(self, expr, exp=None): n, a, x = map(self._print, expr.args) tex = r"P_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_hermite(self, expr, exp=None): n, x = map(self._print, expr.args) tex = r"H_{%s}\left(%s\right)" % (n, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_laguerre(self, expr, exp=None): n, x = map(self._print, expr.args) tex = r"L_{%s}\left(%s\right)" % (n, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_assoc_laguerre(self, expr, exp=None): n, a, x = map(self._print, expr.args) tex = r"L_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_Ynm(self, expr, exp=None): n, m, theta, phi = map(self._print, expr.args) tex = r"Y_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_Znm(self, expr, exp=None): n, m, theta, phi = map(self._print, expr.args) tex = r"Z_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_Rational(self, expr): if expr.q != 1: sign = "" p = expr.p if expr.p < 0: sign = "- " p = -p if self._settings['fold_short_frac']: return r"%s%d / %d" % (sign, p, expr.q) return r"%s\frac{%d}{%d}" % (sign, p, expr.q) else: return self._print(expr.p) def _print_Order(self, expr): s = self._print(expr.expr) if expr.point and any(p != S.Zero for p in expr.point) or \ len(expr.variables) > 1: s += '; ' if len(expr.variables) > 1: s += self._print(expr.variables) elif expr.variables: s += self._print(expr.variables[0]) s += r'\rightarrow ' if len(expr.point) > 1: s += self._print(expr.point) else: s += self._print(expr.point[0]) return r"O\left(%s\right)" % s def _print_Symbol(self, expr, style='plain'): if expr in self._settings['symbol_names']: return self._settings['symbol_names'][expr] result = self._deal_with_super_sub(expr.name) if \ '\\' not in expr.name else expr.name if style == 'bold': result = r"\mathbf{{{}}}".format(result) return result _print_RandomSymbol = _print_Symbol def _print_MatrixSymbol(self, expr): return self._print_Symbol(expr, style=self._settings['mat_symbol_style']) def _deal_with_super_sub(self, string): if '{' in string: return string name, supers, subs = split_super_sub(string) name = translate(name) supers = [translate(sup) for sup in supers] subs = [translate(sub) for sub in subs] # glue all items together: if supers: name += "^{%s}" % " ".join(supers) if subs: name += "_{%s}" % " ".join(subs) return name def _print_Relational(self, expr): if self._settings['itex']: gt = r"\gt" lt = r"\lt" else: gt = ">" lt = "<" charmap = { "==": "=", ">": gt, "<": lt, ">=": r"\geq", "<=": r"\leq", "!=": r"\neq", } return "%s %s %s" % (self._print(expr.lhs), charmap[expr.rel_op], self._print(expr.rhs)) def _print_Piecewise(self, expr): ecpairs = [r"%s & \text{for}\: %s" % (self._print(e), self._print(c)) for e, c in expr.args[:-1]] if expr.args[-1].cond == true: ecpairs.append(r"%s & \text{otherwise}" % self._print(expr.args[-1].expr)) else: ecpairs.append(r"%s & \text{for}\: %s" % (self._print(expr.args[-1].expr), self._print(expr.args[-1].cond))) tex = r"\begin{cases} %s \end{cases}" return tex % r" \\".join(ecpairs) def _print_MatrixBase(self, expr): lines = [] for line in range(expr.rows): # horrible, should be 'rows' lines.append(" & ".join([self._print(i) for i in expr[line, :]])) mat_str = self._settings['mat_str'] if mat_str is None: if self._settings['mode'] == 'inline': mat_str = 'smallmatrix' else: if (expr.cols <= 10) is True: mat_str = 'matrix' else: mat_str = 'array' out_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}' out_str = out_str.replace('%MATSTR%', mat_str) if mat_str == 'array': out_str = out_str.replace('%s', '{' + 'c'*expr.cols + '}%s') if self._settings['mat_delim']: left_delim = self._settings['mat_delim'] right_delim = self._delim_dict[left_delim] out_str = r'\left' + left_delim + out_str + \ r'\right' + right_delim return out_str % r"\\".join(lines) _print_ImmutableMatrix = _print_ImmutableDenseMatrix \ = _print_Matrix \ = _print_MatrixBase def _print_MatrixElement(self, expr): return self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True)\ + '_{%s, %s}' % (self._print(expr.i), self._print(expr.j)) def _print_MatrixSlice(self, expr): def latexslice(x): x = list(x) if x[2] == 1: del x[2] if x[1] == x[0] + 1: del x[1] if x[0] == 0: x[0] = '' return ':'.join(map(self._print, x)) return (self._print(expr.parent) + r'\left[' + latexslice(expr.rowslice) + ', ' + latexslice(expr.colslice) + r'\right]') def _print_BlockMatrix(self, expr): return self._print(expr.blocks) def _print_Transpose(self, expr): mat = expr.arg from sympy.matrices import MatrixSymbol if not isinstance(mat, MatrixSymbol): return r"\left(%s\right)^{T}" % self._print(mat) else: return "%s^{T}" % self._print(mat) def _print_Trace(self, expr): mat = expr.arg return r"\operatorname{tr}\left(%s \right)" % self._print(mat) def _print_Adjoint(self, expr): mat = expr.arg from sympy.matrices import MatrixSymbol if not isinstance(mat, MatrixSymbol): return r"\left(%s\right)^{\dagger}" % self._print(mat) else: return r"%s^{\dagger}" % self._print(mat) def _print_MatMul(self, expr): from sympy import MatMul, Mul parens = lambda x: self.parenthesize(x, precedence_traditional(expr), False) args = expr.args if isinstance(args[0], Mul): args = args[0].as_ordered_factors() + list(args[1:]) else: args = list(args) if isinstance(expr, MatMul) and _coeff_isneg(expr): if args[0] == -1: args = args[1:] else: args[0] = -args[0] return '- ' + ' '.join(map(parens, args)) else: return ' '.join(map(parens, args)) def _print_Mod(self, expr, exp=None): if exp is not None: return r'\left(%s\bmod{%s}\right)^{%s}' % \ (self.parenthesize(expr.args[0], PRECEDENCE['Mul'], strict=True), self._print(expr.args[1]), self._print(exp)) return r'%s\bmod{%s}' % (self.parenthesize(expr.args[0], PRECEDENCE['Mul'], strict=True), self._print(expr.args[1])) def _print_HadamardProduct(self, expr): from sympy import Add, MatAdd, MatMul def parens(x): if isinstance(x, (Add, MatAdd, MatMul)): return r"\left(%s\right)" % self._print(x) return self._print(x) return r' \circ '.join(map(parens, expr.args)) def _print_KroneckerProduct(self, expr): from sympy import Add, MatAdd, MatMul def parens(x): if isinstance(x, (Add, MatAdd, MatMul)): return r"\left(%s\right)" % self._print(x) return self._print(x) return r' \otimes '.join(map(parens, expr.args)) def _print_MatPow(self, expr): base, exp = expr.base, expr.exp from sympy.matrices import MatrixSymbol if not isinstance(base, MatrixSymbol): return r"\left(%s\right)^{%s}" % (self._print(base), self._print(exp)) else: return "%s^{%s}" % (self._print(base), self._print(exp)) def _print_ZeroMatrix(self, Z): return r"\mathbb{0}" def _print_Identity(self, I): return r"\mathbb{I}" def _print_NDimArray(self, expr): if expr.rank() == 0: return self._print(expr[()]) mat_str = self._settings['mat_str'] if mat_str is None: if self._settings['mode'] == 'inline': mat_str = 'smallmatrix' else: if (expr.rank() == 0) or (expr.shape[-1] <= 10): mat_str = 'matrix' else: mat_str = 'array' block_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}' block_str = block_str.replace('%MATSTR%', mat_str) if self._settings['mat_delim']: left_delim = self._settings['mat_delim'] right_delim = self._delim_dict[left_delim] block_str = r'\left' + left_delim + block_str + \ r'\right' + right_delim if expr.rank() == 0: return block_str % "" level_str = [[]] + [[] for i in range(expr.rank())] shape_ranges = [list(range(i)) for i in expr.shape] for outer_i in itertools.product(*shape_ranges): level_str[-1].append(self._print(expr[outer_i])) even = True for back_outer_i in range(expr.rank()-1, -1, -1): if len(level_str[back_outer_i+1]) < expr.shape[back_outer_i]: break if even: level_str[back_outer_i].append( r" & ".join(level_str[back_outer_i+1])) else: level_str[back_outer_i].append( block_str % (r"\\".join(level_str[back_outer_i+1]))) if len(level_str[back_outer_i+1]) == 1: level_str[back_outer_i][-1] = r"\left[" + \ level_str[back_outer_i][-1] + r"\right]" even = not even level_str[back_outer_i+1] = [] out_str = level_str[0][0] if expr.rank() % 2 == 1: out_str = block_str % out_str return out_str _print_ImmutableDenseNDimArray = _print_NDimArray _print_ImmutableSparseNDimArray = _print_NDimArray _print_MutableDenseNDimArray = _print_NDimArray _print_MutableSparseNDimArray = _print_NDimArray def _printer_tensor_indices(self, name, indices, index_map={}): out_str = self._print(name) last_valence = None prev_map = None for index in indices: new_valence = index.is_up if ((index in index_map) or prev_map) and \ last_valence == new_valence: out_str += "," if last_valence != new_valence: if last_valence is not None: out_str += "}" if index.is_up: out_str += "{}^{" else: out_str += "{}_{" out_str += self._print(index.args[0]) if index in index_map: out_str += "=" out_str += self._print(index_map[index]) prev_map = True else: prev_map = False last_valence = new_valence if last_valence is not None: out_str += "}" return out_str def _print_Tensor(self, expr): name = expr.args[0].args[0] indices = expr.get_indices() return self._printer_tensor_indices(name, indices) def _print_TensorElement(self, expr): name = expr.expr.args[0].args[0] indices = expr.expr.get_indices() index_map = expr.index_map return self._printer_tensor_indices(name, indices, index_map) def _print_TensMul(self, expr): # prints expressions like "A(a)", "3*A(a)", "(1+x)*A(a)" sign, args = expr._get_args_for_traditional_printer() return sign + "".join( [self.parenthesize(arg, precedence(expr)) for arg in args] ) def _print_TensAdd(self, expr): a = [] args = expr.args for x in args: a.append(self.parenthesize(x, precedence(expr))) a.sort() s = ' + '.join(a) s = s.replace('+ -', '- ') return s def _print_TensorIndex(self, expr): return "{}%s{%s}" % ( "^" if expr.is_up else "_", self._print(expr.args[0]) ) def _print_tuple(self, expr): return r"\left( %s\right)" % \ r", \ ".join([self._print(i) for i in expr]) def _print_TensorProduct(self, expr): elements = [self._print(a) for a in expr.args] return r' \otimes '.join(elements) def _print_WedgeProduct(self, expr): elements = [self._print(a) for a in expr.args] return r' \wedge '.join(elements) def _print_Tuple(self, expr): return self._print_tuple(expr) def _print_list(self, expr): return r"\left[ %s\right]" % \ r", \ ".join([self._print(i) for i in expr]) def _print_dict(self, d): keys = sorted(d.keys(), key=default_sort_key) items = [] for key in keys: val = d[key] items.append("%s : %s" % (self._print(key), self._print(val))) return r"\left\{ %s\right\}" % r", \ ".join(items) def _print_Dict(self, expr): return self._print_dict(expr) def _print_DiracDelta(self, expr, exp=None): if len(expr.args) == 1 or expr.args[1] == 0: tex = r"\delta\left(%s\right)" % self._print(expr.args[0]) else: tex = r"\delta^{\left( %s \right)}\left( %s \right)" % ( self._print(expr.args[1]), self._print(expr.args[0])) if exp: tex = r"\left(%s\right)^{%s}" % (tex, exp) return tex def _print_SingularityFunction(self, expr): shift = self._print(expr.args[0] - expr.args[1]) power = self._print(expr.args[2]) tex = r"{\left\langle %s \right\rangle}^{%s}" % (shift, power) return tex def _print_Heaviside(self, expr, exp=None): tex = r"\theta\left(%s\right)" % self._print(expr.args[0]) if exp: tex = r"\left(%s\right)^{%s}" % (tex, exp) return tex def _print_KroneckerDelta(self, expr, exp=None): i = self._print(expr.args[0]) j = self._print(expr.args[1]) if expr.args[0].is_Atom and expr.args[1].is_Atom: tex = r'\delta_{%s %s}' % (i, j) else: tex = r'\delta_{%s, %s}' % (i, j) if exp is not None: tex = r'\left(%s\right)^{%s}' % (tex, exp) return tex def _print_LeviCivita(self, expr, exp=None): indices = map(self._print, expr.args) if all(x.is_Atom for x in expr.args): tex = r'\varepsilon_{%s}' % " ".join(indices) else: tex = r'\varepsilon_{%s}' % ", ".join(indices) if exp: tex = r'\left(%s\right)^{%s}' % (tex, exp) return tex def _print_ProductSet(self, p): if len(p.sets) > 1 and not has_variety(p.sets): return self._print(p.sets[0]) + "^{%d}" % len(p.sets) else: return r" \times ".join(self._print(set) for set in p.sets) def _print_RandomDomain(self, d): if hasattr(d, 'as_boolean'): return '\\text{Domain: }' + self._print(d.as_boolean()) elif hasattr(d, 'set'): return ('\\text{Domain: }' + self._print(d.symbols) + '\\text{ in }' + self._print(d.set)) elif hasattr(d, 'symbols'): return '\\text{Domain on }' + self._print(d.symbols) else: return self._print(None) def _print_FiniteSet(self, s): items = sorted(s.args, key=default_sort_key) return self._print_set(items) def _print_set(self, s): items = sorted(s, key=default_sort_key) items = ", ".join(map(self._print, items)) return r"\left\{%s\right\}" % items _print_frozenset = _print_set def _print_Range(self, s): dots = r'\ldots' if s.start.is_infinite: printset = dots, s[-1] - s.step, s[-1] elif s.stop.is_infinite: it = iter(s) printset = next(it), next(it), dots elif len(s) > 4: it = iter(s) printset = next(it), next(it), dots, s[-1] else: printset = tuple(s) return (r"\left\{" + r", ".join(self._print(el) for el in printset) + r"\right\}") def _print_bernoulli(self, expr, exp=None): tex = r"B_{%s}" % self._print(expr.args[0]) if exp is not None: tex = r"%s^{%s}" % (tex, self._print(exp)) return tex _print_bell = _print_bernoulli def _print_fibonacci(self, expr, exp=None): tex = r"F_{%s}" % self._print(expr.args[0]) if exp is not None: tex = r"%s^{%s}" % (tex, self._print(exp)) return tex def _print_lucas(self, expr, exp=None): tex = r"L_{%s}" % self._print(expr.args[0]) if exp is not None: tex = r"%s^{%s}" % (tex, self._print(exp)) return tex def _print_tribonacci(self, expr, exp=None): tex = r"T_{%s}" % self._print(expr.args[0]) if exp is not None: tex = r"%s^{%s}" % (tex, self._print(exp)) return tex def _print_SeqFormula(self, s): if len(s.start.free_symbols) > 0 or len(s.stop.free_symbols) > 0: return r"\left\{%s\right\}_{%s=%s}^{%s}" % ( self._print(s.formula), self._print(s.variables[0]), self._print(s.start), self._print(s.stop) ) if s.start is S.NegativeInfinity: stop = s.stop printset = (r'\ldots', s.coeff(stop - 3), s.coeff(stop - 2), s.coeff(stop - 1), s.coeff(stop)) elif s.stop is S.Infinity or s.length > 4: printset = s[:4] printset.append(r'\ldots') else: printset = tuple(s) return (r"\left[" + r", ".join(self._print(el) for el in printset) + r"\right]") _print_SeqPer = _print_SeqFormula _print_SeqAdd = _print_SeqFormula _print_SeqMul = _print_SeqFormula def _print_Interval(self, i): if i.start == i.end: return r"\left\{%s\right\}" % self._print(i.start) else: if i.left_open: left = '(' else: left = '[' if i.right_open: right = ')' else: right = ']' return r"\left%s%s, %s\right%s" % \ (left, self._print(i.start), self._print(i.end), right) def _print_AccumulationBounds(self, i): return r"\left\langle %s, %s\right\rangle" % \ (self._print(i.min), self._print(i.max)) def _print_Union(self, u): return r" \cup ".join([self._print(i) for i in u.args]) def _print_Complement(self, u): return r" \setminus ".join([self._print(i) for i in u.args]) def _print_Intersection(self, u): return r" \cap ".join([self._print(i) for i in u.args]) def _print_SymmetricDifference(self, u): return r" \triangle ".join([self._print(i) for i in u.args]) def _print_EmptySet(self, e): return r"\emptyset" def _print_Naturals(self, n): return r"\mathbb{N}" def _print_Naturals0(self, n): return r"\mathbb{N}_0" def _print_Integers(self, i): return r"\mathbb{Z}" def _print_Reals(self, i): return r"\mathbb{R}" def _print_Complexes(self, i): return r"\mathbb{C}" def _print_ImageSet(self, s): sets = s.args[1:] varsets = [r"%s \in %s" % (self._print(var), self._print(setv)) for var, setv in zip(s.lamda.variables, sets)] return r"\left\{%s\; |\; %s\right\}" % ( self._print(s.lamda.expr), ', '.join(varsets)) def _print_ConditionSet(self, s): vars_print = ', '.join([self._print(var) for var in Tuple(s.sym)]) if s.base_set is S.UniversalSet: return r"\left\{%s \mid %s \right\}" % \ (vars_print, self._print(s.condition.as_expr())) return r"\left\{%s \mid %s \in %s \wedge %s \right\}" % ( vars_print, vars_print, self._print(s.base_set), self._print(s.condition)) def _print_ComplexRegion(self, s): vars_print = ', '.join([self._print(var) for var in s.variables]) return r"\left\{%s\; |\; %s \in %s \right\}" % ( self._print(s.expr), vars_print, self._print(s.sets)) def _print_Contains(self, e): return r"%s \in %s" % tuple(self._print(a) for a in e.args) def _print_FourierSeries(self, s): return self._print_Add(s.truncate()) + self._print(r' + \ldots') def _print_FormalPowerSeries(self, s): return self._print_Add(s.infinite) def _print_FiniteField(self, expr): return r"\mathbb{F}_{%s}" % expr.mod def _print_IntegerRing(self, expr): return r"\mathbb{Z}" def _print_RationalField(self, expr): return r"\mathbb{Q}" def _print_RealField(self, expr): return r"\mathbb{R}" def _print_ComplexField(self, expr): return r"\mathbb{C}" def _print_PolynomialRing(self, expr): domain = self._print(expr.domain) symbols = ", ".join(map(self._print, expr.symbols)) return r"%s\left[%s\right]" % (domain, symbols) def _print_FractionField(self, expr): domain = self._print(expr.domain) symbols = ", ".join(map(self._print, expr.symbols)) return r"%s\left(%s\right)" % (domain, symbols) def _print_PolynomialRingBase(self, expr): domain = self._print(expr.domain) symbols = ", ".join(map(self._print, expr.symbols)) inv = "" if not expr.is_Poly: inv = r"S_<^{-1}" return r"%s%s\left[%s\right]" % (inv, domain, symbols) def _print_Poly(self, poly): cls = poly.__class__.__name__ terms = [] for monom, coeff in poly.terms(): s_monom = '' for i, exp in enumerate(monom): if exp > 0: if exp == 1: s_monom += self._print(poly.gens[i]) else: s_monom += self._print(pow(poly.gens[i], exp)) if coeff.is_Add: if s_monom: s_coeff = r"\left(%s\right)" % self._print(coeff) else: s_coeff = self._print(coeff) else: if s_monom: if coeff is S.One: terms.extend(['+', s_monom]) continue if coeff is S.NegativeOne: terms.extend(['-', s_monom]) continue s_coeff = self._print(coeff) if not s_monom: s_term = s_coeff else: s_term = s_coeff + " " + s_monom if s_term.startswith('-'): terms.extend(['-', s_term[1:]]) else: terms.extend(['+', s_term]) if terms[0] in ['-', '+']: modifier = terms.pop(0) if modifier == '-': terms[0] = '-' + terms[0] expr = ' '.join(terms) gens = list(map(self._print, poly.gens)) domain = "domain=%s" % self._print(poly.get_domain()) args = ", ".join([expr] + gens + [domain]) if cls in accepted_latex_functions: tex = r"\%s {\left(%s \right)}" % (cls, args) else: tex = r"\operatorname{%s}{\left( %s \right)}" % (cls, args) return tex def _print_ComplexRootOf(self, root): cls = root.__class__.__name__ if cls == "ComplexRootOf": cls = "CRootOf" expr = self._print(root.expr) index = root.index if cls in accepted_latex_functions: return r"\%s {\left(%s, %d\right)}" % (cls, expr, index) else: return r"\operatorname{%s} {\left(%s, %d\right)}" % (cls, expr, index) def _print_RootSum(self, expr): cls = expr.__class__.__name__ args = [self._print(expr.expr)] if expr.fun is not S.IdentityFunction: args.append(self._print(expr.fun)) if cls in accepted_latex_functions: return r"\%s {\left(%s\right)}" % (cls, ", ".join(args)) else: return r"\operatorname{%s} {\left(%s\right)}" % (cls, ", ".join(args)) def _print_PolyElement(self, poly): mul_symbol = self._settings['mul_symbol_latex'] return poly.str(self, PRECEDENCE, "{%s}^{%d}", mul_symbol) def _print_FracElement(self, frac): if frac.denom == 1: return self._print(frac.numer) else: numer = self._print(frac.numer) denom = self._print(frac.denom) return r"\frac{%s}{%s}" % (numer, denom) def _print_euler(self, expr, exp=None): m, x = (expr.args[0], None) if len(expr.args) == 1 else expr.args tex = r"E_{%s}" % self._print(m) if exp is not None: tex = r"%s^{%s}" % (tex, self._print(exp)) if x is not None: tex = r"%s\left(%s\right)" % (tex, self._print(x)) return tex def _print_catalan(self, expr, exp=None): tex = r"C_{%s}" % self._print(expr.args[0]) if exp is not None: tex = r"%s^{%s}" % (tex, self._print(exp)) return tex def _print_UnifiedTransform(self, expr, s, inverse=False): 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])) def _print_MellinTransform(self, expr): return self._print_UnifiedTransform(expr, 'M') def _print_InverseMellinTransform(self, expr): return self._print_UnifiedTransform(expr, 'M', True) def _print_LaplaceTransform(self, expr): return self._print_UnifiedTransform(expr, 'L') def _print_InverseLaplaceTransform(self, expr): return self._print_UnifiedTransform(expr, 'L', True) def _print_FourierTransform(self, expr): return self._print_UnifiedTransform(expr, 'F') def _print_InverseFourierTransform(self, expr): return self._print_UnifiedTransform(expr, 'F', True) def _print_SineTransform(self, expr): return self._print_UnifiedTransform(expr, 'SIN') def _print_InverseSineTransform(self, expr): return self._print_UnifiedTransform(expr, 'SIN', True) def _print_CosineTransform(self, expr): return self._print_UnifiedTransform(expr, 'COS') def _print_InverseCosineTransform(self, expr): return self._print_UnifiedTransform(expr, 'COS', True) def _print_DMP(self, p): try: if p.ring is not None: # TODO incorporate order return self._print(p.ring.to_sympy(p)) except SympifyError: pass return self._print(repr(p)) def _print_DMF(self, p): return self._print_DMP(p) def _print_Object(self, object): return self._print(Symbol(object.name)) def _print_Morphism(self, morphism): domain = self._print(morphism.domain) codomain = self._print(morphism.codomain) return "%s\\rightarrow %s" % (domain, codomain) def _print_NamedMorphism(self, morphism): pretty_name = self._print(Symbol(morphism.name)) pretty_morphism = self._print_Morphism(morphism) return "%s:%s" % (pretty_name, pretty_morphism) def _print_IdentityMorphism(self, morphism): from sympy.categories import NamedMorphism return self._print_NamedMorphism(NamedMorphism( morphism.domain, morphism.codomain, "id")) def _print_CompositeMorphism(self, morphism): # All components of the morphism have names and it is thus # possible to build the name of the composite. component_names_list = [self._print(Symbol(component.name)) for component in morphism.components] component_names_list.reverse() component_names = "\\circ ".join(component_names_list) + ":" pretty_morphism = self._print_Morphism(morphism) return component_names + pretty_morphism def _print_Category(self, morphism): return r"\mathbf{{{}}}".format(self._print(Symbol(morphism.name))) def _print_Diagram(self, diagram): if not diagram.premises: # This is an empty diagram. return self._print(S.EmptySet) latex_result = self._print(diagram.premises) if diagram.conclusions: latex_result += "\\Longrightarrow %s" % \ self._print(diagram.conclusions) return latex_result def _print_DiagramGrid(self, grid): latex_result = "\\begin{array}{%s}\n" % ("c" * grid.width) for i in range(grid.height): for j in range(grid.width): if grid[i, j]: latex_result += latex(grid[i, j]) latex_result += " " if j != grid.width - 1: latex_result += "& " if i != grid.height - 1: latex_result += "\\\\" latex_result += "\n" latex_result += "\\end{array}\n" return latex_result def _print_FreeModule(self, M): return '{{{}}}^{{{}}}'.format(self._print(M.ring), self._print(M.rank)) def _print_FreeModuleElement(self, m): # Print as row vector for convenience, for now. return r"\left[ {} \right]".format(",".join( '{' + self._print(x) + '}' for x in m)) def _print_SubModule(self, m): return r"\left\langle {} \right\rangle".format(",".join( '{' + self._print(x) + '}' for x in m.gens)) def _print_ModuleImplementedIdeal(self, m): return r"\left\langle {} \right\rangle".format(",".join( '{' + self._print(x) + '}' for [x] in m._module.gens)) def _print_Quaternion(self, expr): # TODO: This expression is potentially confusing, # shall we print it as `Quaternion( ... )`? s = [self.parenthesize(i, PRECEDENCE["Mul"], strict=True) for i in expr.args] a = [s[0]] + [i+" "+j for i, j in zip(s[1:], "ijk")] return " + ".join(a) def _print_QuotientRing(self, R): # TODO nicer fractions for few generators... return r"\frac{{{}}}{{{}}}".format(self._print(R.ring), self._print(R.base_ideal)) def _print_QuotientRingElement(self, x): return r"{{{}}} + {{{}}}".format(self._print(x.data), self._print(x.ring.base_ideal)) def _print_QuotientModuleElement(self, m): return r"{{{}}} + {{{}}}".format(self._print(m.data), self._print(m.module.killed_module)) def _print_QuotientModule(self, M): # TODO nicer fractions for few generators... return r"\frac{{{}}}{{{}}}".format(self._print(M.base), self._print(M.killed_module)) def _print_MatrixHomomorphism(self, h): return r"{{{}}} : {{{}}} \to {{{}}}".format(self._print(h._sympy_matrix()), self._print(h.domain), self._print(h.codomain)) def _print_BaseScalarField(self, field): string = field._coord_sys._names[field._index] return r'\mathbf{{{}}}'.format(self._print(Symbol(string))) def _print_BaseVectorField(self, field): string = field._coord_sys._names[field._index] return r'\partial_{{{}}}'.format(self._print(Symbol(string))) def _print_Differential(self, diff): field = diff._form_field if hasattr(field, '_coord_sys'): string = field._coord_sys._names[field._index] return r'\operatorname{{d}}{}'.format(self._print(Symbol(string))) else: string = self._print(field) return r'\operatorname{{d}}\left({}\right)'.format(string) def _print_Tr(self, p): # TODO: Handle indices contents = self._print(p.args[0]) return r'\operatorname{{tr}}\left({}\right)'.format(contents) def _print_totient(self, expr, exp=None): if exp is not None: return r'\left(\phi\left(%s\right)\right)^{%s}' % \ (self._print(expr.args[0]), self._print(exp)) return r'\phi\left(%s\right)' % self._print(expr.args[0]) def _print_reduced_totient(self, expr, exp=None): if exp is not None: return r'\left(\lambda\left(%s\right)\right)^{%s}' % \ (self._print(expr.args[0]), self._print(exp)) return r'\lambda\left(%s\right)' % self._print(expr.args[0]) def _print_divisor_sigma(self, expr, exp=None): if len(expr.args) == 2: tex = r"_%s\left(%s\right)" % tuple(map(self._print, (expr.args[1], expr.args[0]))) else: tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"\sigma^{%s}%s" % (self._print(exp), tex) return r"\sigma%s" % tex def _print_udivisor_sigma(self, expr, exp=None): if len(expr.args) == 2: tex = r"_%s\left(%s\right)" % tuple(map(self._print, (expr.args[1], expr.args[0]))) else: tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"\sigma^*^{%s}%s" % (self._print(exp), tex) return r"\sigma^*%s" % tex def _print_primenu(self, expr, exp=None): if exp is not None: return r'\left(\nu\left(%s\right)\right)^{%s}' % \ (self._print(expr.args[0]), self._print(exp)) return r'\nu\left(%s\right)' % self._print(expr.args[0]) def _print_primeomega(self, expr, exp=None): if exp is not None: return r'\left(\Omega\left(%s\right)\right)^{%s}' % \ (self._print(expr.args[0]), self._print(exp)) return r'\Omega\left(%s\right)' % self._print(expr.args[0])
def translate(s): r''' Check for a modifier ending the string. If present, convert the modifier to latex and translate the rest recursively. Given a description of a Greek letter or other special character, return the appropriate latex. Let everything else pass as given. >>> from sympy.printing.latex import translate >>> translate('alphahatdotprime') "{\\dot{\\hat{\\alpha}}}'" ''' # Process the rest tex = tex_greek_dictionary.get(s) if tex: return tex elif s.lower() in greek_letters_set: return "\\" + s.lower() elif s in other_symbols: return "\\" + s else: # Process modifiers, if any, and recurse for key in sorted(modifier_dict.keys(), key=lambda k:len(k), reverse=True): if s.lower().endswith(key) and len(s) > len(key): return modifier_dict[key](translate(s[:-len(key)])) return s
[docs]def latex(expr, fold_frac_powers=False, fold_func_brackets=False, fold_short_frac=None, inv_trig_style="abbreviated", itex=False, ln_notation=False, long_frac_ratio=None, mat_delim="[", mat_str=None, mode="plain", mul_symbol=None, order=None, symbol_names=None, root_notation=True, mat_symbol_style="plain", imaginary_unit="i", gothic_re_im=False): r"""Convert the given expression to LaTeX string representation. Parameters ========== fold_frac_powers : boolean, optional Emit ``^{p/q}`` instead of ``^{\frac{p}{q}}`` for fractional powers. fold_func_brackets : boolean, optional Fold function brackets where applicable. fold_short_frac : boolean, optional Emit ``p / q`` instead of ``\frac{p}{q}`` when the denominator is simple enough (at most two terms and no powers). The default value is ``True`` for inline mode, ``False`` otherwise. inv_trig_style : string, optional How inverse trig functions should be displayed. Can be one of ``abbreviated``, ``full``, or ``power``. Defaults to ``abbreviated``. itex : boolean, optional Specifies if itex-specific syntax is used, including emitting ``$$...$$``. ln_notation : boolean, optional If set to ``True``, ``\ln`` is used instead of default ``\log``. long_frac_ratio : float or None, optional The allowed ratio of the width of the numerator to the width of the denominator before the printer breaks off long fractions. If ``None`` (the default value), long fractions are not broken up. mat_delim : string, optional The delimiter to wrap around matrices. Can be one of ``[``, ``(``, or the empty string. Defaults to ``[``. mat_str : string, optional Which matrix environment string to emit. ``smallmatrix``, ``matrix``, ``array``, etc. Defaults to ``smallmatrix`` for inline mode, ``matrix`` for matrices of no more than 10 columns, and ``array`` otherwise. mode: string, optional Specifies how the generated code will be delimited. ``mode`` can be one of ``plain``, ``inline``, ``equation`` or ``equation*``. If ``mode`` is set to ``plain``, then the resulting code will not be delimited at all (this is the default). If ``mode`` is set to ``inline`` then inline LaTeX ``$...$`` will be used. If ``mode`` is set to ``equation`` or ``equation*``, the resulting code will be enclosed in the ``equation`` or ``equation*`` environment (remember to import ``amsmath`` for ``equation*``), unless the ``itex`` option is set. In the latter case, the ``$$...$$`` syntax is used. mul_symbol : string or None, optional The symbol to use for multiplication. Can be one of ``None``, ``ldot``, ``dot``, or ``times``. order: string, optional Any of the supported monomial orderings (currently ``lex``, ``grlex``, or ``grevlex``), ``old``, and ``none``. This parameter does nothing for Mul objects. Setting order to ``old`` uses the compatibility ordering for Add defined in Printer. For very large expressions, set the ``order`` keyword to ``none`` if speed is a concern. symbol_names : dictionary of strings mapped to symbols, optional Dictionary of symbols and the custom strings they should be emitted as. root_notation : boolean, optional If set to ``False``, exponents of the form 1/n are printed in fractonal form. Default is ``True``, to print exponent in root form. mat_symbol_style : string, optional Can be either ``plain`` (default) or ``bold``. If set to ``bold``, a MatrixSymbol A will be printed as ``\mathbf{A}``, otherwise as ``A``. imaginary_unit : string, optional String to use for the imaginary unit. Defined options are "i" (default) and "j". Adding "r" or "t" in front gives ``\mathrm`` or ``\text``, so "ri" leads to ``\mathrm{i}`` which gives `\mathrm{i}`. gothic_re_im : boolean, optional If set to ``True``, `\Re` and `\Im` is used for ``re`` and ``im``, respectively. The default is ``False`` leading to `\operatorname{re}` and `\operatorname{im}`. Notes ===== Not using a print statement for printing, results in double backslashes for latex commands since that's the way Python escapes backslashes in strings. >>> from sympy import latex, Rational >>> from sympy.abc import tau >>> latex((2*tau)**Rational(7,2)) '8 \\sqrt{2} \\tau^{\\frac{7}{2}}' >>> print(latex((2*tau)**Rational(7,2))) 8 \sqrt{2} \tau^{\frac{7}{2}} Examples ======== >>> from sympy import latex, pi, sin, asin, Integral, Matrix, Rational, log >>> from sympy.abc import x, y, mu, r, tau Basic usage: >>> print(latex((2*tau)**Rational(7,2))) 8 \sqrt{2} \tau^{\frac{7}{2}} ``mode`` and ``itex`` options: >>> print(latex((2*mu)**Rational(7,2), mode='plain')) 8 \sqrt{2} \mu^{\frac{7}{2}} >>> print(latex((2*tau)**Rational(7,2), mode='inline')) $8 \sqrt{2} \tau^{7 / 2}$ >>> print(latex((2*mu)**Rational(7,2), mode='equation*')) \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*} >>> print(latex((2*mu)**Rational(7,2), mode='equation')) \begin{equation}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation} >>> print(latex((2*mu)**Rational(7,2), mode='equation', itex=True)) $$8 \sqrt{2} \mu^{\frac{7}{2}}$$ >>> print(latex((2*mu)**Rational(7,2), mode='plain')) 8 \sqrt{2} \mu^{\frac{7}{2}} >>> print(latex((2*tau)**Rational(7,2), mode='inline')) $8 \sqrt{2} \tau^{7 / 2}$ >>> print(latex((2*mu)**Rational(7,2), mode='equation*')) \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*} >>> print(latex((2*mu)**Rational(7,2), mode='equation')) \begin{equation}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation} >>> print(latex((2*mu)**Rational(7,2), mode='equation', itex=True)) $$8 \sqrt{2} \mu^{\frac{7}{2}}$$ Fraction options: >>> print(latex((2*tau)**Rational(7,2), fold_frac_powers=True)) 8 \sqrt{2} \tau^{7/2} >>> print(latex((2*tau)**sin(Rational(7,2)))) \left(2 \tau\right)^{\sin{\left(\frac{7}{2} \right)}} >>> print(latex((2*tau)**sin(Rational(7,2)), fold_func_brackets=True)) \left(2 \tau\right)^{\sin {\frac{7}{2}}} >>> print(latex(3*x**2/y)) \frac{3 x^{2}}{y} >>> print(latex(3*x**2/y, fold_short_frac=True)) 3 x^{2} / y >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=2)) \frac{\int r\, dr}{2 \pi} >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=0)) \frac{1}{2 \pi} \int r\, dr Multiplication options: >>> print(latex((2*tau)**sin(Rational(7,2)), mul_symbol="times")) \left(2 \times \tau\right)^{\sin{\left(\frac{7}{2} \right)}} Trig options: >>> print(latex(asin(Rational(7,2)))) \operatorname{asin}{\left(\frac{7}{2} \right)} >>> print(latex(asin(Rational(7,2)), inv_trig_style="full")) \arcsin{\left(\frac{7}{2} \right)} >>> print(latex(asin(Rational(7,2)), inv_trig_style="power")) \sin^{-1}{\left(\frac{7}{2} \right)} Matrix options: >>> print(latex(Matrix(2, 1, [x, y]))) \left[\begin{matrix}x\\y\end{matrix}\right] >>> print(latex(Matrix(2, 1, [x, y]), mat_str = "array")) \left[\begin{array}{c}x\\y\end{array}\right] >>> print(latex(Matrix(2, 1, [x, y]), mat_delim="(")) \left(\begin{matrix}x\\y\end{matrix}\right) Custom printing of symbols: >>> print(latex(x**2, symbol_names={x: 'x_i'})) x_i^{2} Logarithms: >>> print(latex(log(10))) \log{\left(10 \right)} >>> print(latex(log(10), ln_notation=True)) \ln{\left(10 \right)} ``latex()`` also supports the builtin container types list, tuple, and dictionary. >>> print(latex([2/x, y], mode='inline')) $\left[ 2 / x, \ y\right]$ """ if symbol_names is None: symbol_names = {} settings = { 'fold_frac_powers': fold_frac_powers, 'fold_func_brackets': fold_func_brackets, 'fold_short_frac': fold_short_frac, 'inv_trig_style': inv_trig_style, 'itex': itex, 'ln_notation': ln_notation, 'long_frac_ratio': long_frac_ratio, 'mat_delim': mat_delim, 'mat_str': mat_str, 'mode': mode, 'mul_symbol': mul_symbol, 'order': order, 'symbol_names': symbol_names, 'root_notation': root_notation, 'mat_symbol_style': mat_symbol_style, 'imaginary_unit': imaginary_unit, 'gothic_re_im': gothic_re_im, } return LatexPrinter(settings).doprint(expr)