import itertools from sympy.core import S from sympy.core.add import Add from sympy.core.containers import Tuple from sympy.core.function import Function from sympy.core.mul import Mul from sympy.core.numbers import Number, Rational from sympy.core.power import Pow from sympy.core.sorting import default_sort_key from sympy.core.symbol import Symbol from sympy.core.sympify import SympifyError from sympy.printing.conventions import requires_partial from sympy.printing.precedence import PRECEDENCE, precedence, precedence_traditional from sympy.printing.printer import Printer, print_function from sympy.printing.str import sstr from sympy.utilities.iterables import has_variety from sympy.utilities.exceptions import sympy_deprecation_warning from sympy.printing.pretty.stringpict import prettyForm, stringPict from sympy.printing.pretty.pretty_symbology import hobj, vobj, xobj, \ xsym, pretty_symbol, pretty_atom, pretty_use_unicode, greek_unicode, U, \ pretty_try_use_unicode, annotated # rename for usage from outside pprint_use_unicode = pretty_use_unicode pprint_try_use_unicode = pretty_try_use_unicode class PrettyPrinter(Printer): """Printer, which converts an expression into 2D ASCII-art figure.""" printmethod = "_pretty" _default_settings = { "order": None, "full_prec": "auto", "use_unicode": None, "wrap_line": True, "num_columns": None, "use_unicode_sqrt_char": True, "root_notation": True, "mat_symbol_style": "plain", "imaginary_unit": "i", "perm_cyclic": True } def __init__(self, settings=None): Printer.__init__(self, settings) if not isinstance(self._settings['imaginary_unit'], str): raise TypeError("'imaginary_unit' must a string, not {}".format(self._settings['imaginary_unit'])) elif self._settings['imaginary_unit'] not in ("i", "j"): raise ValueError("'imaginary_unit' must be either 'i' or 'j', not '{}'".format(self._settings['imaginary_unit'])) def emptyPrinter(self, expr): return prettyForm(str(expr)) @property def _use_unicode(self): if self._settings['use_unicode']: return True else: return pretty_use_unicode() def doprint(self, expr): return self._print(expr).render(**self._settings) # empty op so _print(stringPict) returns the same def _print_stringPict(self, e): return e def _print_basestring(self, e): return prettyForm(e) def _print_atan2(self, e): pform = prettyForm(*self._print_seq(e.args).parens()) pform = prettyForm(*pform.left('atan2')) return pform def _print_Symbol(self, e, bold_name=False): symb = pretty_symbol(e.name, bold_name) return prettyForm(symb) _print_RandomSymbol = _print_Symbol def _print_MatrixSymbol(self, e): return self._print_Symbol(e, self._settings['mat_symbol_style'] == "bold") def _print_Float(self, e): # we will use StrPrinter's Float printer, but we need to handle the # full_prec ourselves, according to the self._print_level full_prec = self._settings["full_prec"] if full_prec == "auto": full_prec = self._print_level == 1 return prettyForm(sstr(e, full_prec=full_prec)) def _print_Cross(self, e): vec1 = e._expr1 vec2 = e._expr2 pform = self._print(vec2) pform = prettyForm(*pform.left('(')) pform = prettyForm(*pform.right(')')) pform = prettyForm(*pform.left(self._print(U('MULTIPLICATION SIGN')))) pform = prettyForm(*pform.left(')')) pform = prettyForm(*pform.left(self._print(vec1))) pform = prettyForm(*pform.left('(')) return pform def _print_Curl(self, e): vec = e._expr pform = self._print(vec) pform = prettyForm(*pform.left('(')) pform = prettyForm(*pform.right(')')) pform = prettyForm(*pform.left(self._print(U('MULTIPLICATION SIGN')))) pform = prettyForm(*pform.left(self._print(U('NABLA')))) return pform def _print_Divergence(self, e): vec = e._expr pform = self._print(vec) pform = prettyForm(*pform.left('(')) pform = prettyForm(*pform.right(')')) pform = prettyForm(*pform.left(self._print(U('DOT OPERATOR')))) pform = prettyForm(*pform.left(self._print(U('NABLA')))) return pform def _print_Dot(self, e): vec1 = e._expr1 vec2 = e._expr2 pform = self._print(vec2) pform = prettyForm(*pform.left('(')) pform = prettyForm(*pform.right(')')) pform = prettyForm(*pform.left(self._print(U('DOT OPERATOR')))) pform = prettyForm(*pform.left(')')) pform = prettyForm(*pform.left(self._print(vec1))) pform = prettyForm(*pform.left('(')) return pform def _print_Gradient(self, e): func = e._expr pform = self._print(func) pform = prettyForm(*pform.left('(')) pform = prettyForm(*pform.right(')')) pform = prettyForm(*pform.left(self._print(U('NABLA')))) return pform def _print_Laplacian(self, e): func = e._expr pform = self._print(func) pform = prettyForm(*pform.left('(')) pform = prettyForm(*pform.right(')')) pform = prettyForm(*pform.left(self._print(U('INCREMENT')))) return pform def _print_Atom(self, e): try: # print atoms like Exp1 or Pi return prettyForm(pretty_atom(e.__class__.__name__, printer=self)) except KeyError: return self.emptyPrinter(e) # Infinity inherits from Number, so we have to override _print_XXX order _print_Infinity = _print_Atom _print_NegativeInfinity = _print_Atom _print_EmptySet = _print_Atom _print_Naturals = _print_Atom _print_Naturals0 = _print_Atom _print_Integers = _print_Atom _print_Rationals = _print_Atom _print_Complexes = _print_Atom _print_EmptySequence = _print_Atom def _print_Reals(self, e): if self._use_unicode: return self._print_Atom(e) else: inf_list = ['-oo', 'oo'] return self._print_seq(inf_list, '(', ')') def _print_subfactorial(self, e): x = e.args[0] pform = self._print(x) # Add parentheses if needed if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol): pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left('!')) return pform def _print_factorial(self, e): x = e.args[0] pform = self._print(x) # Add parentheses if needed if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol): pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.right('!')) return pform def _print_factorial2(self, e): x = e.args[0] pform = self._print(x) # Add parentheses if needed if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol): pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.right('!!')) return pform def _print_binomial(self, e): n, k = e.args n_pform = self._print(n) k_pform = self._print(k) bar = ' '*max(n_pform.width(), k_pform.width()) pform = prettyForm(*k_pform.above(bar)) pform = prettyForm(*pform.above(n_pform)) pform = prettyForm(*pform.parens('(', ')')) pform.baseline = (pform.baseline + 1)//2 return pform def _print_Relational(self, e): op = prettyForm(' ' + xsym(e.rel_op) + ' ') l = self._print(e.lhs) r = self._print(e.rhs) pform = prettyForm(*stringPict.next(l, op, r), binding=prettyForm.OPEN) return pform def _print_Not(self, e): from sympy.logic.boolalg import (Equivalent, Implies) if self._use_unicode: arg = e.args[0] pform = self._print(arg) if isinstance(arg, Equivalent): return self._print_Equivalent(arg, altchar="\N{LEFT RIGHT DOUBLE ARROW WITH STROKE}") if isinstance(arg, Implies): return self._print_Implies(arg, altchar="\N{RIGHTWARDS ARROW WITH STROKE}") if arg.is_Boolean and not arg.is_Not: pform = prettyForm(*pform.parens()) return prettyForm(*pform.left("\N{NOT SIGN}")) else: return self._print_Function(e) def __print_Boolean(self, e, char, sort=True): args = e.args if sort: args = sorted(e.args, key=default_sort_key) arg = args[0] pform = self._print(arg) if arg.is_Boolean and not arg.is_Not: pform = prettyForm(*pform.parens()) for arg in args[1:]: pform_arg = self._print(arg) if arg.is_Boolean and not arg.is_Not: pform_arg = prettyForm(*pform_arg.parens()) pform = prettyForm(*pform.right(' %s ' % char)) pform = prettyForm(*pform.right(pform_arg)) return pform def _print_And(self, e): if self._use_unicode: return self.__print_Boolean(e, "\N{LOGICAL AND}") else: return self._print_Function(e, sort=True) def _print_Or(self, e): if self._use_unicode: return self.__print_Boolean(e, "\N{LOGICAL OR}") else: return self._print_Function(e, sort=True) def _print_Xor(self, e): if self._use_unicode: return self.__print_Boolean(e, "\N{XOR}") else: return self._print_Function(e, sort=True) def _print_Nand(self, e): if self._use_unicode: return self.__print_Boolean(e, "\N{NAND}") else: return self._print_Function(e, sort=True) def _print_Nor(self, e): if self._use_unicode: return self.__print_Boolean(e, "\N{NOR}") else: return self._print_Function(e, sort=True) def _print_Implies(self, e, altchar=None): if self._use_unicode: return self.__print_Boolean(e, altchar or "\N{RIGHTWARDS ARROW}", sort=False) else: return self._print_Function(e) def _print_Equivalent(self, e, altchar=None): if self._use_unicode: return self.__print_Boolean(e, altchar or "\N{LEFT RIGHT DOUBLE ARROW}") else: return self._print_Function(e, sort=True) def _print_conjugate(self, e): pform = self._print(e.args[0]) return prettyForm( *pform.above( hobj('_', pform.width())) ) def _print_Abs(self, e): pform = self._print(e.args[0]) pform = prettyForm(*pform.parens('|', '|')) return pform def _print_floor(self, e): if self._use_unicode: pform = self._print(e.args[0]) pform = prettyForm(*pform.parens('lfloor', 'rfloor')) return pform else: return self._print_Function(e) def _print_ceiling(self, e): if self._use_unicode: pform = self._print(e.args[0]) pform = prettyForm(*pform.parens('lceil', 'rceil')) return pform else: return self._print_Function(e) def _print_Derivative(self, deriv): if requires_partial(deriv.expr) and self._use_unicode: deriv_symbol = U('PARTIAL DIFFERENTIAL') else: deriv_symbol = r'd' x = None count_total_deriv = 0 for sym, num in reversed(deriv.variable_count): s = self._print(sym) ds = prettyForm(*s.left(deriv_symbol)) count_total_deriv += num if (not num.is_Integer) or (num > 1): ds = ds**prettyForm(str(num)) if x is None: x = ds else: x = prettyForm(*x.right(' ')) x = prettyForm(*x.right(ds)) f = prettyForm( binding=prettyForm.FUNC, *self._print(deriv.expr).parens()) pform = prettyForm(deriv_symbol) if (count_total_deriv > 1) != False: pform = pform**prettyForm(str(count_total_deriv)) pform = prettyForm(*pform.below(stringPict.LINE, x)) pform.baseline = pform.baseline + 1 pform = prettyForm(*stringPict.next(pform, f)) pform.binding = prettyForm.MUL return pform def _print_Cycle(self, dc): from sympy.combinatorics.permutations import Permutation, Cycle # for Empty Cycle if dc == Cycle(): cyc = stringPict('') return prettyForm(*cyc.parens()) dc_list = Permutation(dc.list()).cyclic_form # for Identity Cycle if dc_list == []: cyc = self._print(dc.size - 1) return prettyForm(*cyc.parens()) cyc = stringPict('') for i in dc_list: l = self._print(str(tuple(i)).replace(',', '')) cyc = prettyForm(*cyc.right(l)) return cyc def _print_Permutation(self, expr): from sympy.combinatorics.permutations import Permutation, Cycle perm_cyclic = Permutation.print_cyclic if perm_cyclic is not None: sympy_deprecation_warning( f""" Setting Permutation.print_cyclic is deprecated. Instead use init_printing(perm_cyclic={perm_cyclic}). """, deprecated_since_version="1.6", active_deprecations_target="deprecated-permutation-print_cyclic", stacklevel=7, ) else: perm_cyclic = self._settings.get("perm_cyclic", True) if perm_cyclic: return self._print_Cycle(Cycle(expr)) lower = expr.array_form upper = list(range(len(lower))) result = stringPict('') first = True for u, l in zip(upper, lower): s1 = self._print(u) s2 = self._print(l) col = prettyForm(*s1.below(s2)) if first: first = False else: col = prettyForm(*col.left(" ")) result = prettyForm(*result.right(col)) return prettyForm(*result.parens()) def _print_Integral(self, integral): f = integral.function # Add parentheses if arg involves addition of terms and # create a pretty form for the argument prettyF = self._print(f) # XXX generalize parens if f.is_Add: prettyF = prettyForm(*prettyF.parens()) # dx dy dz ... arg = prettyF for x in integral.limits: prettyArg = self._print(x[0]) # XXX qparens (parens if needs-parens) if prettyArg.width() > 1: prettyArg = prettyForm(*prettyArg.parens()) arg = prettyForm(*arg.right(' d', prettyArg)) # \int \int \int ... firstterm = True s = None for lim in integral.limits: # Create bar based on the height of the argument h = arg.height() H = h + 2 # XXX hack! ascii_mode = not self._use_unicode if ascii_mode: H += 2 vint = vobj('int', H) # Construct the pretty form with the integral sign and the argument pform = prettyForm(vint) pform.baseline = arg.baseline + ( H - h)//2 # covering the whole argument if len(lim) > 1: # Create pretty forms for endpoints, if definite integral. # Do not print empty endpoints. if len(lim) == 2: prettyA = prettyForm("") prettyB = self._print(lim[1]) if len(lim) == 3: prettyA = self._print(lim[1]) prettyB = self._print(lim[2]) if ascii_mode: # XXX hack # Add spacing so that endpoint can more easily be # identified with the correct integral sign spc = max(1, 3 - prettyB.width()) prettyB = prettyForm(*prettyB.left(' ' * spc)) spc = max(1, 4 - prettyA.width()) prettyA = prettyForm(*prettyA.right(' ' * spc)) pform = prettyForm(*pform.above(prettyB)) pform = prettyForm(*pform.below(prettyA)) if not ascii_mode: # XXX hack pform = prettyForm(*pform.right(' ')) if firstterm: s = pform # first term firstterm = False else: s = prettyForm(*s.left(pform)) pform = prettyForm(*arg.left(s)) pform.binding = prettyForm.MUL return pform def _print_Product(self, expr): func = expr.term pretty_func = self._print(func) horizontal_chr = xobj('_', 1) corner_chr = xobj('_', 1) vertical_chr = xobj('|', 1) if self._use_unicode: # use unicode corners horizontal_chr = xobj('-', 1) corner_chr = '\N{BOX DRAWINGS LIGHT DOWN AND HORIZONTAL}' func_height = pretty_func.height() first = True max_upper = 0 sign_height = 0 for lim in expr.limits: pretty_lower, pretty_upper = self.__print_SumProduct_Limits(lim) width = (func_height + 2) * 5 // 3 - 2 sign_lines = [horizontal_chr + corner_chr + (horizontal_chr * (width-2)) + corner_chr + horizontal_chr] for _ in range(func_height + 1): sign_lines.append(' ' + vertical_chr + (' ' * (width-2)) + vertical_chr + ' ') pretty_sign = stringPict('') pretty_sign = prettyForm(*pretty_sign.stack(*sign_lines)) max_upper = max(max_upper, pretty_upper.height()) if first: sign_height = pretty_sign.height() pretty_sign = prettyForm(*pretty_sign.above(pretty_upper)) pretty_sign = prettyForm(*pretty_sign.below(pretty_lower)) if first: pretty_func.baseline = 0 first = False height = pretty_sign.height() padding = stringPict('') padding = prettyForm(*padding.stack(*[' ']*(height - 1))) pretty_sign = prettyForm(*pretty_sign.right(padding)) pretty_func = prettyForm(*pretty_sign.right(pretty_func)) pretty_func.baseline = max_upper + sign_height//2 pretty_func.binding = prettyForm.MUL return pretty_func def __print_SumProduct_Limits(self, lim): def print_start(lhs, rhs): op = prettyForm(' ' + xsym("==") + ' ') l = self._print(lhs) r = self._print(rhs) pform = prettyForm(*stringPict.next(l, op, r)) return pform prettyUpper = self._print(lim[2]) prettyLower = print_start(lim[0], lim[1]) return prettyLower, prettyUpper def _print_Sum(self, expr): ascii_mode = not self._use_unicode def asum(hrequired, lower, upper, use_ascii): def adjust(s, wid=None, how='<^>'): if not wid or len(s) > wid: return s need = wid - len(s) if how in ('<^>', "<") or how not in list('<^>'): return s + ' '*need half = need//2 lead = ' '*half if how == ">": return " "*need + s return lead + s + ' '*(need - len(lead)) h = max(hrequired, 2) d = h//2 w = d + 1 more = hrequired % 2 lines = [] if use_ascii: lines.append("_"*(w) + ' ') lines.append(r"\%s`" % (' '*(w - 1))) for i in range(1, d): lines.append('%s\\%s' % (' '*i, ' '*(w - i))) if more: lines.append('%s)%s' % (' '*(d), ' '*(w - d))) for i in reversed(range(1, d)): lines.append('%s/%s' % (' '*i, ' '*(w - i))) lines.append("/" + "_"*(w - 1) + ',') return d, h + more, lines, more else: w = w + more d = d + more vsum = vobj('sum', 4) lines.append("_"*(w)) for i in range(0, d): lines.append('%s%s%s' % (' '*i, vsum[2], ' '*(w - i - 1))) for i in reversed(range(0, d)): lines.append('%s%s%s' % (' '*i, vsum[4], ' '*(w - i - 1))) lines.append(vsum[8]*(w)) return d, h + 2*more, lines, more f = expr.function prettyF = self._print(f) if f.is_Add: # add parens prettyF = prettyForm(*prettyF.parens()) H = prettyF.height() + 2 # \sum \sum \sum ... first = True max_upper = 0 sign_height = 0 for lim in expr.limits: prettyLower, prettyUpper = self.__print_SumProduct_Limits(lim) max_upper = max(max_upper, prettyUpper.height()) # Create sum sign based on the height of the argument d, h, slines, adjustment = asum( H, prettyLower.width(), prettyUpper.width(), ascii_mode) prettySign = stringPict('') prettySign = prettyForm(*prettySign.stack(*slines)) if first: sign_height = prettySign.height() prettySign = prettyForm(*prettySign.above(prettyUpper)) prettySign = prettyForm(*prettySign.below(prettyLower)) if first: # change F baseline so it centers on the sign prettyF.baseline -= d - (prettyF.height()//2 - prettyF.baseline) first = False # put padding to the right pad = stringPict('') pad = prettyForm(*pad.stack(*[' ']*h)) prettySign = prettyForm(*prettySign.right(pad)) # put the present prettyF to the right prettyF = prettyForm(*prettySign.right(prettyF)) # adjust baseline of ascii mode sigma with an odd height so that it is # exactly through the center ascii_adjustment = ascii_mode if not adjustment else 0 prettyF.baseline = max_upper + sign_height//2 + ascii_adjustment prettyF.binding = prettyForm.MUL return prettyF def _print_Limit(self, l): e, z, z0, dir = l.args E = self._print(e) if precedence(e) <= PRECEDENCE["Mul"]: E = prettyForm(*E.parens('(', ')')) Lim = prettyForm('lim') LimArg = self._print(z) if self._use_unicode: LimArg = prettyForm(*LimArg.right('\N{BOX DRAWINGS LIGHT HORIZONTAL}\N{RIGHTWARDS ARROW}')) else: LimArg = prettyForm(*LimArg.right('->')) LimArg = prettyForm(*LimArg.right(self._print(z0))) if str(dir) == '+-' or z0 in (S.Infinity, S.NegativeInfinity): dir = "" else: if self._use_unicode: dir = '\N{SUPERSCRIPT PLUS SIGN}' if str(dir) == "+" else '\N{SUPERSCRIPT MINUS}' LimArg = prettyForm(*LimArg.right(self._print(dir))) Lim = prettyForm(*Lim.below(LimArg)) Lim = prettyForm(*Lim.right(E), binding=prettyForm.MUL) return Lim def _print_matrix_contents(self, e): """ This method factors out what is essentially grid printing. """ M = e # matrix Ms = {} # i,j -> pretty(M[i,j]) for i in range(M.rows): for j in range(M.cols): Ms[i, j] = self._print(M[i, j]) # h- and v- spacers hsep = 2 vsep = 1 # max width for columns maxw = [-1] * M.cols for j in range(M.cols): maxw[j] = max([Ms[i, j].width() for i in range(M.rows)] or [0]) # drawing result D = None for i in range(M.rows): D_row = None for j in range(M.cols): s = Ms[i, j] # reshape s to maxw # XXX this should be generalized, and go to stringPict.reshape ? assert s.width() <= maxw[j] # hcenter it, +0.5 to the right 2 # ( it's better to align formula starts for say 0 and r ) # XXX this is not good in all cases -- maybe introduce vbaseline? wdelta = maxw[j] - s.width() wleft = wdelta // 2 wright = wdelta - wleft s = prettyForm(*s.right(' '*wright)) s = prettyForm(*s.left(' '*wleft)) # we don't need vcenter cells -- this is automatically done in # a pretty way because when their baselines are taking into # account in .right() if D_row is None: D_row = s # first box in a row continue D_row = prettyForm(*D_row.right(' '*hsep)) # h-spacer D_row = prettyForm(*D_row.right(s)) if D is None: D = D_row # first row in a picture continue # v-spacer for _ in range(vsep): D = prettyForm(*D.below(' ')) D = prettyForm(*D.below(D_row)) if D is None: D = prettyForm('') # Empty Matrix return D def _print_MatrixBase(self, e, lparens='[', rparens=']'): D = self._print_matrix_contents(e) D.baseline = D.height()//2 D = prettyForm(*D.parens(lparens, rparens)) return D def _print_Determinant(self, e): mat = e.arg if mat.is_MatrixExpr: from sympy.matrices.expressions.blockmatrix import BlockMatrix if isinstance(mat, BlockMatrix): return self._print_MatrixBase(mat.blocks, lparens='|', rparens='|') D = self._print(mat) D.baseline = D.height()//2 return prettyForm(*D.parens('|', '|')) else: return self._print_MatrixBase(mat, lparens='|', rparens='|') def _print_TensorProduct(self, expr): # This should somehow share the code with _print_WedgeProduct: if self._use_unicode: circled_times = "\u2297" else: circled_times = ".*" return self._print_seq(expr.args, None, None, circled_times, parenthesize=lambda x: precedence_traditional(x) <= PRECEDENCE["Mul"]) def _print_WedgeProduct(self, expr): # This should somehow share the code with _print_TensorProduct: if self._use_unicode: wedge_symbol = "\u2227" else: wedge_symbol = '/\\' return self._print_seq(expr.args, None, None, wedge_symbol, parenthesize=lambda x: precedence_traditional(x) <= PRECEDENCE["Mul"]) def _print_Trace(self, e): D = self._print(e.arg) D = prettyForm(*D.parens('(',')')) D.baseline = D.height()//2 D = prettyForm(*D.left('\n'*(0) + 'tr')) return D def _print_MatrixElement(self, expr): from sympy.matrices import MatrixSymbol if (isinstance(expr.parent, MatrixSymbol) and expr.i.is_number and expr.j.is_number): return self._print( Symbol(expr.parent.name + '_%d%d' % (expr.i, expr.j))) else: prettyFunc = self._print(expr.parent) prettyFunc = prettyForm(*prettyFunc.parens()) prettyIndices = self._print_seq((expr.i, expr.j), delimiter=', ' ).parens(left='[', right=']')[0] pform = prettyForm(binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyIndices)) # store pform parts so it can be reassembled e.g. when powered pform.prettyFunc = prettyFunc pform.prettyArgs = prettyIndices return pform def _print_MatrixSlice(self, m): # XXX works only for applied functions from sympy.matrices import MatrixSymbol prettyFunc = self._print(m.parent) if not isinstance(m.parent, MatrixSymbol): prettyFunc = prettyForm(*prettyFunc.parens()) def ppslice(x, dim): x = list(x) if x[2] == 1: del x[2] if x[0] == 0: x[0] = '' if x[1] == dim: x[1] = '' return prettyForm(*self._print_seq(x, delimiter=':')) prettyArgs = self._print_seq((ppslice(m.rowslice, m.parent.rows), ppslice(m.colslice, m.parent.cols)), delimiter=', ').parens(left='[', right=']')[0] pform = prettyForm( binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) # store pform parts so it can be reassembled e.g. when powered pform.prettyFunc = prettyFunc pform.prettyArgs = prettyArgs return pform def _print_Transpose(self, expr): mat = expr.arg pform = self._print(mat) from sympy.matrices import MatrixSymbol, BlockMatrix if (not isinstance(mat, MatrixSymbol) and not isinstance(mat, BlockMatrix) and mat.is_MatrixExpr): pform = prettyForm(*pform.parens()) pform = pform**(prettyForm('T')) return pform def _print_Adjoint(self, expr): mat = expr.arg pform = self._print(mat) if self._use_unicode: dag = prettyForm('\N{DAGGER}') else: dag = prettyForm('+') from sympy.matrices import MatrixSymbol, BlockMatrix if (not isinstance(mat, MatrixSymbol) and not isinstance(mat, BlockMatrix) and mat.is_MatrixExpr): pform = prettyForm(*pform.parens()) pform = pform**dag return pform def _print_BlockMatrix(self, B): if B.blocks.shape == (1, 1): return self._print(B.blocks[0, 0]) return self._print(B.blocks) def _print_MatAdd(self, expr): s = None for item in expr.args: pform = self._print(item) if s is None: s = pform # First element else: coeff = item.as_coeff_mmul()[0] if S(coeff).could_extract_minus_sign(): s = prettyForm(*stringPict.next(s, ' ')) pform = self._print(item) else: s = prettyForm(*stringPict.next(s, ' + ')) s = prettyForm(*stringPict.next(s, pform)) return s def _print_MatMul(self, expr): args = list(expr.args) from sympy.matrices.expressions.hadamard import HadamardProduct from sympy.matrices.expressions.kronecker import KroneckerProduct from sympy.matrices.expressions.matadd import MatAdd for i, a in enumerate(args): if (isinstance(a, (Add, MatAdd, HadamardProduct, KroneckerProduct)) and len(expr.args) > 1): args[i] = prettyForm(*self._print(a).parens()) else: args[i] = self._print(a) return prettyForm.__mul__(*args) def _print_Identity(self, expr): if self._use_unicode: return prettyForm('\N{MATHEMATICAL DOUBLE-STRUCK CAPITAL I}') else: return prettyForm('I') def _print_ZeroMatrix(self, expr): if self._use_unicode: return prettyForm('\N{MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO}') else: return prettyForm('0') def _print_OneMatrix(self, expr): if self._use_unicode: return prettyForm('\N{MATHEMATICAL DOUBLE-STRUCK DIGIT ONE}') else: return prettyForm('1') def _print_DotProduct(self, expr): args = list(expr.args) for i, a in enumerate(args): args[i] = self._print(a) return prettyForm.__mul__(*args) def _print_MatPow(self, expr): pform = self._print(expr.base) from sympy.matrices import MatrixSymbol if not isinstance(expr.base, MatrixSymbol) and expr.base.is_MatrixExpr: pform = prettyForm(*pform.parens()) pform = pform**(self._print(expr.exp)) return pform def _print_HadamardProduct(self, expr): from sympy.matrices.expressions.hadamard import HadamardProduct from sympy.matrices.expressions.matadd import MatAdd from sympy.matrices.expressions.matmul import MatMul if self._use_unicode: delim = pretty_atom('Ring') else: delim = '.*' return self._print_seq(expr.args, None, None, delim, parenthesize=lambda x: isinstance(x, (MatAdd, MatMul, HadamardProduct))) def _print_HadamardPower(self, expr): # from sympy import MatAdd, MatMul if self._use_unicode: circ = pretty_atom('Ring') else: circ = self._print('.') pretty_base = self._print(expr.base) pretty_exp = self._print(expr.exp) if precedence(expr.exp) < PRECEDENCE["Mul"]: pretty_exp = prettyForm(*pretty_exp.parens()) pretty_circ_exp = prettyForm( binding=prettyForm.LINE, *stringPict.next(circ, pretty_exp) ) return pretty_base**pretty_circ_exp def _print_KroneckerProduct(self, expr): from sympy.matrices.expressions.matadd import MatAdd from sympy.matrices.expressions.matmul import MatMul if self._use_unicode: delim = ' \N{N-ARY CIRCLED TIMES OPERATOR} ' else: delim = ' x ' return self._print_seq(expr.args, None, None, delim, parenthesize=lambda x: isinstance(x, (MatAdd, MatMul))) def _print_FunctionMatrix(self, X): D = self._print(X.lamda.expr) D = prettyForm(*D.parens('[', ']')) return D def _print_TransferFunction(self, expr): if not expr.num == 1: num, den = expr.num, expr.den res = Mul(num, Pow(den, -1, evaluate=False), evaluate=False) return self._print_Mul(res) else: return self._print(1)/self._print(expr.den) def _print_Series(self, expr): args = list(expr.args) for i, a in enumerate(expr.args): args[i] = prettyForm(*self._print(a).parens()) return prettyForm.__mul__(*args) def _print_MIMOSeries(self, expr): from sympy.physics.control.lti import MIMOParallel args = list(expr.args) pretty_args = [] for i, a in enumerate(reversed(args)): if (isinstance(a, MIMOParallel) and len(expr.args) > 1): expression = self._print(a) expression.baseline = expression.height()//2 pretty_args.append(prettyForm(*expression.parens())) else: expression = self._print(a) expression.baseline = expression.height()//2 pretty_args.append(expression) return prettyForm.__mul__(*pretty_args) def _print_Parallel(self, expr): s = None for item in expr.args: pform = self._print(item) if s is None: s = pform # First element else: s = prettyForm(*stringPict.next(s)) s.baseline = s.height()//2 s = prettyForm(*stringPict.next(s, ' + ')) s = prettyForm(*stringPict.next(s, pform)) return s def _print_MIMOParallel(self, expr): from sympy.physics.control.lti import TransferFunctionMatrix s = None for item in expr.args: pform = self._print(item) if s is None: s = pform # First element else: s = prettyForm(*stringPict.next(s)) s.baseline = s.height()//2 s = prettyForm(*stringPict.next(s, ' + ')) if isinstance(item, TransferFunctionMatrix): s.baseline = s.height() - 1 s = prettyForm(*stringPict.next(s, pform)) # s.baseline = s.height()//2 return s def _print_Feedback(self, expr): from sympy.physics.control import TransferFunction, Series num, tf = expr.sys1, TransferFunction(1, 1, expr.var) num_arg_list = list(num.args) if isinstance(num, Series) else [num] den_arg_list = list(expr.sys2.args) if \ isinstance(expr.sys2, Series) else [expr.sys2] if isinstance(num, Series) and isinstance(expr.sys2, Series): den = Series(*num_arg_list, *den_arg_list) elif isinstance(num, Series) and isinstance(expr.sys2, TransferFunction): if expr.sys2 == tf: den = Series(*num_arg_list) else: den = Series(*num_arg_list, expr.sys2) elif isinstance(num, TransferFunction) and isinstance(expr.sys2, Series): if num == tf: den = Series(*den_arg_list) else: den = Series(num, *den_arg_list) else: if num == tf: den = Series(*den_arg_list) elif expr.sys2 == tf: den = Series(*num_arg_list) else: den = Series(*num_arg_list, *den_arg_list) denom = prettyForm(*stringPict.next(self._print(tf))) denom.baseline = denom.height()//2 denom = prettyForm(*stringPict.next(denom, ' + ')) if expr.sign == -1 \ else prettyForm(*stringPict.next(denom, ' - ')) denom = prettyForm(*stringPict.next(denom, self._print(den))) return self._print(num)/denom def _print_MIMOFeedback(self, expr): from sympy.physics.control import MIMOSeries, TransferFunctionMatrix inv_mat = self._print(MIMOSeries(expr.sys2, expr.sys1)) plant = self._print(expr.sys1) _feedback = prettyForm(*stringPict.next(inv_mat)) _feedback = prettyForm(*stringPict.right("I + ", _feedback)) if expr.sign == -1 \ else prettyForm(*stringPict.right("I - ", _feedback)) _feedback = prettyForm(*stringPict.parens(_feedback)) _feedback.baseline = 0 _feedback = prettyForm(*stringPict.right(_feedback, '-1 ')) _feedback.baseline = _feedback.height()//2 _feedback = prettyForm.__mul__(_feedback, prettyForm(" ")) if isinstance(expr.sys1, TransferFunctionMatrix): _feedback.baseline = _feedback.height() - 1 _feedback = prettyForm(*stringPict.next(_feedback, plant)) return _feedback def _print_TransferFunctionMatrix(self, expr): mat = self._print(expr._expr_mat) mat.baseline = mat.height() - 1 subscript = greek_unicode['tau'] if self._use_unicode else r'{t}' mat = prettyForm(*mat.right(subscript)) return mat def _print_BasisDependent(self, expr): from sympy.vector import Vector if not self._use_unicode: raise NotImplementedError("ASCII pretty printing of BasisDependent is not implemented") if expr == expr.zero: return prettyForm(expr.zero._pretty_form) o1 = [] vectstrs = [] 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 the coef of the basis vector is 1 #we skip the 1 if v == 1: o1.append("" + k._pretty_form) #Same for -1 elif v == -1: o1.append("(-1) " + k._pretty_form) #For a general expr else: #We always wrap the measure numbers in #parentheses arg_str = self._print( v).parens()[0] o1.append(arg_str + ' ' + k._pretty_form) vectstrs.append(k._pretty_form) #outstr = u("").join(o1) if o1[0].startswith(" + "): o1[0] = o1[0][3:] elif o1[0].startswith(" "): o1[0] = o1[0][1:] #Fixing the newlines lengths = [] strs = [''] flag = [] for i, partstr in enumerate(o1): flag.append(0) # XXX: What is this hack? if '\n' in partstr: tempstr = partstr tempstr = tempstr.replace(vectstrs[i], '') if '\N{RIGHT PARENTHESIS EXTENSION}' in tempstr: # If scalar is a fraction for paren in range(len(tempstr)): flag[i] = 1 if tempstr[paren] == '\N{RIGHT PARENTHESIS EXTENSION}' and tempstr[paren + 1] == '\n': # We want to place the vector string after all the right parentheses, because # otherwise, the vector will be in the middle of the string tempstr = tempstr[:paren] + '\N{RIGHT PARENTHESIS EXTENSION}'\ + ' ' + vectstrs[i] + tempstr[paren + 1:] break elif '\N{RIGHT PARENTHESIS LOWER HOOK}' in tempstr: # We want to place the vector string after all the right parentheses, because # otherwise, the vector will be in the middle of the string. For this reason, # we insert the vector string at the rightmost index. index = tempstr.rfind('\N{RIGHT PARENTHESIS LOWER HOOK}') if index != -1: # then this character was found in this string flag[i] = 1 tempstr = tempstr[:index] + '\N{RIGHT PARENTHESIS LOWER HOOK}'\ + ' ' + vectstrs[i] + tempstr[index + 1:] o1[i] = tempstr o1 = [x.split('\n') for x in o1] n_newlines = max([len(x) for x in o1]) # Width of part in its pretty form if 1 in flag: # If there was a fractional scalar for i, parts in enumerate(o1): if len(parts) == 1: # If part has no newline parts.insert(0, ' ' * (len(parts[0]))) flag[i] = 1 for i, parts in enumerate(o1): lengths.append(len(parts[flag[i]])) for j in range(n_newlines): if j+1 <= len(parts): if j >= len(strs): strs.append(' ' * (sum(lengths[:-1]) + 3*(len(lengths)-1))) if j == flag[i]: strs[flag[i]] += parts[flag[i]] + ' + ' else: strs[j] += parts[j] + ' '*(lengths[-1] - len(parts[j])+ 3) else: if j >= len(strs): strs.append(' ' * (sum(lengths[:-1]) + 3*(len(lengths)-1))) strs[j] += ' '*(lengths[-1]+3) return prettyForm('\n'.join([s[:-3] for s in strs])) def _print_NDimArray(self, expr): from sympy.matrices.immutable import ImmutableMatrix if expr.rank() == 0: return self._print(expr[()]) level_str = [[]] + [[] for i in range(expr.rank())] shape_ranges = [list(range(i)) for i in expr.shape] # leave eventual matrix elements unflattened mat = lambda x: ImmutableMatrix(x, evaluate=False) for outer_i in itertools.product(*shape_ranges): level_str[-1].append(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(level_str[back_outer_i+1]) else: level_str[back_outer_i].append(mat( level_str[back_outer_i+1])) if len(level_str[back_outer_i + 1]) == 1: level_str[back_outer_i][-1] = mat( [[level_str[back_outer_i][-1]]]) even = not even level_str[back_outer_i+1] = [] out_expr = level_str[0][0] if expr.rank() % 2 == 1: out_expr = mat([out_expr]) return self._print(out_expr) def _printer_tensor_indices(self, name, indices, index_map={}): center = stringPict(name) top = stringPict(" "*center.width()) bot = stringPict(" "*center.width()) last_valence = None prev_map = None for i, index in enumerate(indices): indpic = self._print(index.args[0]) if ((index in index_map) or prev_map) and last_valence == index.is_up: if index.is_up: top = prettyForm(*stringPict.next(top, ",")) else: bot = prettyForm(*stringPict.next(bot, ",")) if index in index_map: indpic = prettyForm(*stringPict.next(indpic, "=")) indpic = prettyForm(*stringPict.next(indpic, self._print(index_map[index]))) prev_map = True else: prev_map = False if index.is_up: top = stringPict(*top.right(indpic)) center = stringPict(*center.right(" "*indpic.width())) bot = stringPict(*bot.right(" "*indpic.width())) else: bot = stringPict(*bot.right(indpic)) center = stringPict(*center.right(" "*indpic.width())) top = stringPict(*top.right(" "*indpic.width())) last_valence = index.is_up pict = prettyForm(*center.above(top)) pict = prettyForm(*pict.below(bot)) return pict def _print_Tensor(self, expr): name = expr.args[0].name indices = expr.get_indices() return self._printer_tensor_indices(name, indices) def _print_TensorElement(self, expr): name = expr.expr.args[0].name indices = expr.expr.get_indices() index_map = expr.index_map return self._printer_tensor_indices(name, indices, index_map) def _print_TensMul(self, expr): sign, args = expr._get_args_for_traditional_printer() args = [ prettyForm(*self._print(i).parens()) if precedence_traditional(i) < PRECEDENCE["Mul"] else self._print(i) for i in args ] pform = prettyForm.__mul__(*args) if sign: return prettyForm(*pform.left(sign)) else: return pform def _print_TensAdd(self, expr): args = [ prettyForm(*self._print(i).parens()) if precedence_traditional(i) < PRECEDENCE["Mul"] else self._print(i) for i in expr.args ] return prettyForm.__add__(*args) def _print_TensorIndex(self, expr): sym = expr.args[0] if not expr.is_up: sym = -sym return self._print(sym) def _print_PartialDerivative(self, deriv): if self._use_unicode: deriv_symbol = U('PARTIAL DIFFERENTIAL') else: deriv_symbol = r'd' x = None for variable in reversed(deriv.variables): s = self._print(variable) ds = prettyForm(*s.left(deriv_symbol)) if x is None: x = ds else: x = prettyForm(*x.right(' ')) x = prettyForm(*x.right(ds)) f = prettyForm( binding=prettyForm.FUNC, *self._print(deriv.expr).parens()) pform = prettyForm(deriv_symbol) if len(deriv.variables) > 1: pform = pform**self._print(len(deriv.variables)) pform = prettyForm(*pform.below(stringPict.LINE, x)) pform.baseline = pform.baseline + 1 pform = prettyForm(*stringPict.next(pform, f)) pform.binding = prettyForm.MUL return pform def _print_Piecewise(self, pexpr): P = {} for n, ec in enumerate(pexpr.args): P[n, 0] = self._print(ec.expr) if ec.cond == True: P[n, 1] = prettyForm('otherwise') else: P[n, 1] = prettyForm( *prettyForm('for ').right(self._print(ec.cond))) hsep = 2 vsep = 1 len_args = len(pexpr.args) # max widths maxw = [max([P[i, j].width() for i in range(len_args)]) for j in range(2)] # FIXME: Refactor this code and matrix into some tabular environment. # drawing result D = None for i in range(len_args): D_row = None for j in range(2): p = P[i, j] assert p.width() <= maxw[j] wdelta = maxw[j] - p.width() wleft = wdelta // 2 wright = wdelta - wleft p = prettyForm(*p.right(' '*wright)) p = prettyForm(*p.left(' '*wleft)) if D_row is None: D_row = p continue D_row = prettyForm(*D_row.right(' '*hsep)) # h-spacer D_row = prettyForm(*D_row.right(p)) if D is None: D = D_row # first row in a picture continue # v-spacer for _ in range(vsep): D = prettyForm(*D.below(' ')) D = prettyForm(*D.below(D_row)) D = prettyForm(*D.parens('{', '')) D.baseline = D.height()//2 D.binding = prettyForm.OPEN return D def _print_ITE(self, ite): from sympy.functions.elementary.piecewise import Piecewise return self._print(ite.rewrite(Piecewise)) def _hprint_vec(self, v): D = None for a in v: p = a if D is None: D = p else: D = prettyForm(*D.right(', ')) D = prettyForm(*D.right(p)) if D is None: D = stringPict(' ') return D def _hprint_vseparator(self, p1, p2, left=None, right=None, delimiter='', ifascii_nougly=False): if ifascii_nougly and not self._use_unicode: return self._print_seq((p1, '|', p2), left=left, right=right, delimiter=delimiter, ifascii_nougly=True) tmp = self._print_seq((p1, p2,), left=left, right=right, delimiter=delimiter) sep = stringPict(vobj('|', tmp.height()), baseline=tmp.baseline) return self._print_seq((p1, sep, p2), left=left, right=right, delimiter=delimiter) def _print_hyper(self, e): # FIXME refactor Matrix, Piecewise, and this into a tabular environment ap = [self._print(a) for a in e.ap] bq = [self._print(b) for b in e.bq] P = self._print(e.argument) P.baseline = P.height()//2 # Drawing result - first create the ap, bq vectors D = None for v in [ap, bq]: D_row = self._hprint_vec(v) if D is None: D = D_row # first row in a picture else: D = prettyForm(*D.below(' ')) D = prettyForm(*D.below(D_row)) # make sure that the argument `z' is centred vertically D.baseline = D.height()//2 # insert horizontal separator P = prettyForm(*P.left(' ')) D = prettyForm(*D.right(' ')) # insert separating `|` D = self._hprint_vseparator(D, P) # add parens D = prettyForm(*D.parens('(', ')')) # create the F symbol above = D.height()//2 - 1 below = D.height() - above - 1 sz, t, b, add, img = annotated('F') F = prettyForm('\n' * (above - t) + img + '\n' * (below - b), baseline=above + sz) add = (sz + 1)//2 F = prettyForm(*F.left(self._print(len(e.ap)))) F = prettyForm(*F.right(self._print(len(e.bq)))) F.baseline = above + add D = prettyForm(*F.right(' ', D)) return D def _print_meijerg(self, e): # FIXME refactor Matrix, Piecewise, and this into a tabular environment v = {} v[(0, 0)] = [self._print(a) for a in e.an] v[(0, 1)] = [self._print(a) for a in e.aother] v[(1, 0)] = [self._print(b) for b in e.bm] v[(1, 1)] = [self._print(b) for b in e.bother] P = self._print(e.argument) P.baseline = P.height()//2 vp = {} for idx in v: vp[idx] = self._hprint_vec(v[idx]) for i in range(2): maxw = max(vp[(0, i)].width(), vp[(1, i)].width()) for j in range(2): s = vp[(j, i)] left = (maxw - s.width()) // 2 right = maxw - left - s.width() s = prettyForm(*s.left(' ' * left)) s = prettyForm(*s.right(' ' * right)) vp[(j, i)] = s D1 = prettyForm(*vp[(0, 0)].right(' ', vp[(0, 1)])) D1 = prettyForm(*D1.below(' ')) D2 = prettyForm(*vp[(1, 0)].right(' ', vp[(1, 1)])) D = prettyForm(*D1.below(D2)) # make sure that the argument `z' is centred vertically D.baseline = D.height()//2 # insert horizontal separator P = prettyForm(*P.left(' ')) D = prettyForm(*D.right(' ')) # insert separating `|` D = self._hprint_vseparator(D, P) # add parens D = prettyForm(*D.parens('(', ')')) # create the G symbol above = D.height()//2 - 1 below = D.height() - above - 1 sz, t, b, add, img = annotated('G') F = prettyForm('\n' * (above - t) + img + '\n' * (below - b), baseline=above + sz) pp = self._print(len(e.ap)) pq = self._print(len(e.bq)) pm = self._print(len(e.bm)) pn = self._print(len(e.an)) def adjust(p1, p2): diff = p1.width() - p2.width() if diff == 0: return p1, p2 elif diff > 0: return p1, prettyForm(*p2.left(' '*diff)) else: return prettyForm(*p1.left(' '*-diff)), p2 pp, pm = adjust(pp, pm) pq, pn = adjust(pq, pn) pu = prettyForm(*pm.right(', ', pn)) pl = prettyForm(*pp.right(', ', pq)) ht = F.baseline - above - 2 if ht > 0: pu = prettyForm(*pu.below('\n'*ht)) p = prettyForm(*pu.below(pl)) F.baseline = above F = prettyForm(*F.right(p)) F.baseline = above + add D = prettyForm(*F.right(' ', D)) return D def _print_ExpBase(self, e): # TODO should exp_polar be printed differently? # what about exp_polar(0), exp_polar(1)? base = prettyForm(pretty_atom('Exp1', 'e')) return base ** self._print(e.args[0]) def _print_Exp1(self, e): return prettyForm(pretty_atom('Exp1', 'e')) def _print_Function(self, e, sort=False, func_name=None, left='(', right=')'): # optional argument func_name for supplying custom names # XXX works only for applied functions return self._helper_print_function(e.func, e.args, sort=sort, func_name=func_name, left=left, right=right) def _print_mathieuc(self, e): return self._print_Function(e, func_name='C') def _print_mathieus(self, e): return self._print_Function(e, func_name='S') def _print_mathieucprime(self, e): return self._print_Function(e, func_name="C'") def _print_mathieusprime(self, e): return self._print_Function(e, func_name="S'") def _helper_print_function(self, func, args, sort=False, func_name=None, delimiter=', ', elementwise=False, left='(', right=')'): if sort: args = sorted(args, key=default_sort_key) if not func_name and hasattr(func, "__name__"): func_name = func.__name__ if func_name: prettyFunc = self._print(Symbol(func_name)) else: prettyFunc = prettyForm(*self._print(func).parens()) if elementwise: if self._use_unicode: circ = pretty_atom('Modifier Letter Low Ring') else: circ = '.' circ = self._print(circ) prettyFunc = prettyForm( binding=prettyForm.LINE, *stringPict.next(prettyFunc, circ) ) prettyArgs = prettyForm(*self._print_seq(args, delimiter=delimiter).parens( left=left, right=right)) pform = prettyForm( binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) # store pform parts so it can be reassembled e.g. when powered pform.prettyFunc = prettyFunc pform.prettyArgs = prettyArgs return pform def _print_ElementwiseApplyFunction(self, e): func = e.function arg = e.expr args = [arg] return self._helper_print_function(func, args, delimiter="", elementwise=True) @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.zeta_functions import lerchphi 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: [greek_unicode['delta'], 'delta'], gamma: [greek_unicode['Gamma'], 'Gamma'], lerchphi: [greek_unicode['Phi'], 'lerchphi'], lowergamma: [greek_unicode['gamma'], 'gamma'], beta: [greek_unicode['Beta'], 'B'], DiracDelta: [greek_unicode['delta'], 'delta'], Chi: ['Chi', 'Chi']} def _print_FunctionClass(self, expr): for cls in self._special_function_classes: if issubclass(expr, cls) and expr.__name__ == cls.__name__: if self._use_unicode: return prettyForm(self._special_function_classes[cls][0]) else: return prettyForm(self._special_function_classes[cls][1]) func_name = expr.__name__ return prettyForm(pretty_symbol(func_name)) def _print_GeometryEntity(self, expr): # GeometryEntity is based on Tuple but should not print like a Tuple return self.emptyPrinter(expr) def _print_lerchphi(self, e): func_name = greek_unicode['Phi'] if self._use_unicode else 'lerchphi' return self._print_Function(e, func_name=func_name) def _print_dirichlet_eta(self, e): func_name = greek_unicode['eta'] if self._use_unicode else 'dirichlet_eta' return self._print_Function(e, func_name=func_name) def _print_Heaviside(self, e): func_name = greek_unicode['theta'] if self._use_unicode else 'Heaviside' if e.args[1] is S.Half: pform = prettyForm(*self._print(e.args[0]).parens()) pform = prettyForm(*pform.left(func_name)) return pform else: return self._print_Function(e, func_name=func_name) def _print_fresnels(self, e): return self._print_Function(e, func_name="S") def _print_fresnelc(self, e): return self._print_Function(e, func_name="C") def _print_airyai(self, e): return self._print_Function(e, func_name="Ai") def _print_airybi(self, e): return self._print_Function(e, func_name="Bi") def _print_airyaiprime(self, e): return self._print_Function(e, func_name="Ai'") def _print_airybiprime(self, e): return self._print_Function(e, func_name="Bi'") def _print_LambertW(self, e): return self._print_Function(e, func_name="W") def _print_Covariance(self, e): return self._print_Function(e, func_name="Cov") def _print_Variance(self, e): return self._print_Function(e, func_name="Var") def _print_Probability(self, e): return self._print_Function(e, func_name="P") def _print_Expectation(self, e): return self._print_Function(e, func_name="E", left='[', right=']') def _print_Lambda(self, e): expr = e.expr sig = e.signature if self._use_unicode: arrow = " \N{RIGHTWARDS ARROW FROM BAR} " else: arrow = " -> " if len(sig) == 1 and sig[0].is_symbol: sig = sig[0] var_form = self._print(sig) return prettyForm(*stringPict.next(var_form, arrow, self._print(expr)), binding=8) def _print_Order(self, expr): pform = self._print(expr.expr) if (expr.point and any(p != S.Zero for p in expr.point)) or \ len(expr.variables) > 1: pform = prettyForm(*pform.right("; ")) if len(expr.variables) > 1: pform = prettyForm(*pform.right(self._print(expr.variables))) elif len(expr.variables): pform = prettyForm(*pform.right(self._print(expr.variables[0]))) if self._use_unicode: pform = prettyForm(*pform.right(" \N{RIGHTWARDS ARROW} ")) else: pform = prettyForm(*pform.right(" -> ")) if len(expr.point) > 1: pform = prettyForm(*pform.right(self._print(expr.point))) else: pform = prettyForm(*pform.right(self._print(expr.point[0]))) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left("O")) return pform def _print_SingularityFunction(self, e): if self._use_unicode: shift = self._print(e.args[0]-e.args[1]) n = self._print(e.args[2]) base = prettyForm("<") base = prettyForm(*base.right(shift)) base = prettyForm(*base.right(">")) pform = base**n return pform else: n = self._print(e.args[2]) shift = self._print(e.args[0]-e.args[1]) base = self._print_seq(shift, "<", ">", ' ') return base**n def _print_beta(self, e): func_name = greek_unicode['Beta'] if self._use_unicode else 'B' return self._print_Function(e, func_name=func_name) def _print_betainc(self, e): func_name = "B'" return self._print_Function(e, func_name=func_name) def _print_betainc_regularized(self, e): func_name = 'I' return self._print_Function(e, func_name=func_name) def _print_gamma(self, e): func_name = greek_unicode['Gamma'] if self._use_unicode else 'Gamma' return self._print_Function(e, func_name=func_name) def _print_uppergamma(self, e): func_name = greek_unicode['Gamma'] if self._use_unicode else 'Gamma' return self._print_Function(e, func_name=func_name) def _print_lowergamma(self, e): func_name = greek_unicode['gamma'] if self._use_unicode else 'lowergamma' return self._print_Function(e, func_name=func_name) def _print_DiracDelta(self, e): if self._use_unicode: if len(e.args) == 2: a = prettyForm(greek_unicode['delta']) b = self._print(e.args[1]) b = prettyForm(*b.parens()) c = self._print(e.args[0]) c = prettyForm(*c.parens()) pform = a**b pform = prettyForm(*pform.right(' ')) pform = prettyForm(*pform.right(c)) return pform pform = self._print(e.args[0]) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left(greek_unicode['delta'])) return pform else: return self._print_Function(e) def _print_expint(self, e): if e.args[0].is_Integer and self._use_unicode: return self._print_Function(Function('E_%s' % e.args[0])(e.args[1])) return self._print_Function(e) def _print_Chi(self, e): # This needs a special case since otherwise it comes out as greek # letter chi... prettyFunc = prettyForm("Chi") prettyArgs = prettyForm(*self._print_seq(e.args).parens()) pform = prettyForm( binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) # store pform parts so it can be reassembled e.g. when powered pform.prettyFunc = prettyFunc pform.prettyArgs = prettyArgs return pform def _print_elliptic_e(self, e): pforma0 = self._print(e.args[0]) if len(e.args) == 1: pform = pforma0 else: pforma1 = self._print(e.args[1]) pform = self._hprint_vseparator(pforma0, pforma1) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left('E')) return pform def _print_elliptic_k(self, e): pform = self._print(e.args[0]) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left('K')) return pform def _print_elliptic_f(self, e): pforma0 = self._print(e.args[0]) pforma1 = self._print(e.args[1]) pform = self._hprint_vseparator(pforma0, pforma1) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left('F')) return pform def _print_elliptic_pi(self, e): name = greek_unicode['Pi'] if self._use_unicode else 'Pi' pforma0 = self._print(e.args[0]) pforma1 = self._print(e.args[1]) if len(e.args) == 2: pform = self._hprint_vseparator(pforma0, pforma1) else: pforma2 = self._print(e.args[2]) pforma = self._hprint_vseparator(pforma1, pforma2, ifascii_nougly=False) pforma = prettyForm(*pforma.left('; ')) pform = prettyForm(*pforma.left(pforma0)) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left(name)) return pform def _print_GoldenRatio(self, expr): if self._use_unicode: return prettyForm(pretty_symbol('phi')) return self._print(Symbol("GoldenRatio")) def _print_EulerGamma(self, expr): if self._use_unicode: return prettyForm(pretty_symbol('gamma')) return self._print(Symbol("EulerGamma")) def _print_Catalan(self, expr): return self._print(Symbol("G")) def _print_Mod(self, expr): pform = self._print(expr.args[0]) if pform.binding > prettyForm.MUL: pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.right(' mod ')) pform = prettyForm(*pform.right(self._print(expr.args[1]))) pform.binding = prettyForm.OPEN return pform def _print_Add(self, expr, order=None): terms = self._as_ordered_terms(expr, order=order) pforms, indices = [], [] def pretty_negative(pform, index): """Prepend a minus sign to a pretty form. """ #TODO: Move this code to prettyForm if index == 0: if pform.height() > 1: pform_neg = '- ' else: pform_neg = '-' else: pform_neg = ' - ' if (pform.binding > prettyForm.NEG or pform.binding == prettyForm.ADD): p = stringPict(*pform.parens()) else: p = pform p = stringPict.next(pform_neg, p) # Lower the binding to NEG, even if it was higher. Otherwise, it # will print as a + ( - (b)), instead of a - (b). return prettyForm(binding=prettyForm.NEG, *p) for i, term in enumerate(terms): if term.is_Mul and term.could_extract_minus_sign(): coeff, other = term.as_coeff_mul(rational=False) if coeff == -1: negterm = Mul(*other, evaluate=False) else: negterm = Mul(-coeff, *other, evaluate=False) pform = self._print(negterm) pforms.append(pretty_negative(pform, i)) elif term.is_Rational and term.q > 1: pforms.append(None) indices.append(i) elif term.is_Number and term < 0: pform = self._print(-term) pforms.append(pretty_negative(pform, i)) elif term.is_Relational: pforms.append(prettyForm(*self._print(term).parens())) else: pforms.append(self._print(term)) if indices: large = True for pform in pforms: if pform is not None and pform.height() > 1: break else: large = False for i in indices: term, negative = terms[i], False if term < 0: term, negative = -term, True if large: pform = prettyForm(str(term.p))/prettyForm(str(term.q)) else: pform = self._print(term) if negative: pform = pretty_negative(pform, i) pforms[i] = pform return prettyForm.__add__(*pforms) def _print_Mul(self, product): from sympy.physics.units import Quantity # Check for unevaluated Mul. In this case we need to make sure the # identities are visible, multiple Rational factors are not combined # etc so we display in a straight-forward form that fully preserves all # args and their order. args = product.args if args[0] is S.One or any(isinstance(arg, Number) for arg in args[1:]): strargs = list(map(self._print, args)) # XXX: This is a hack to work around the fact that # prettyForm.__mul__ absorbs a leading -1 in the args. Probably it # would be better to fix this in prettyForm.__mul__ instead. negone = strargs[0] == '-1' if negone: strargs[0] = prettyForm('1', 0, 0) obj = prettyForm.__mul__(*strargs) if negone: obj = prettyForm('-' + obj.s, obj.baseline, obj.binding) return obj a = [] # items in the numerator b = [] # items that are in the denominator (if any) if self.order not in ('old', 'none'): args = product.as_ordered_factors() else: args = list(product.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))) # Gather terms for numerator/denominator for item in args: if item.is_commutative and item.is_Pow and item.exp.is_Rational and item.exp.is_negative: if item.exp != -1: b.append(Pow(item.base, -item.exp, evaluate=False)) else: b.append(Pow(item.base, -item.exp)) elif item.is_Rational and item is not S.Infinity: if item.p != 1: a.append( Rational(item.p) ) if item.q != 1: b.append( Rational(item.q) ) else: a.append(item) # Convert to pretty forms. Parentheses are added by `__mul__`. a = [self._print(ai) for ai in a] b = [self._print(bi) for bi in b] # Construct a pretty form if len(b) == 0: return prettyForm.__mul__(*a) else: if len(a) == 0: a.append( self._print(S.One) ) return prettyForm.__mul__(*a)/prettyForm.__mul__(*b) # A helper function for _print_Pow to print x**(1/n) def _print_nth_root(self, base, root): bpretty = self._print(base) # In very simple cases, use a single-char root sign if (self._settings['use_unicode_sqrt_char'] and self._use_unicode and root == 2 and bpretty.height() == 1 and (bpretty.width() == 1 or (base.is_Integer and base.is_nonnegative))): return prettyForm(*bpretty.left('\N{SQUARE ROOT}')) # Construct root sign, start with the \/ shape _zZ = xobj('/', 1) rootsign = xobj('\\', 1) + _zZ # Constructing the number to put on root rpretty = self._print(root) # roots look bad if they are not a single line if rpretty.height() != 1: return self._print(base)**self._print(1/root) # If power is half, no number should appear on top of root sign exp = '' if root == 2 else str(rpretty).ljust(2) if len(exp) > 2: rootsign = ' '*(len(exp) - 2) + rootsign # Stack the exponent rootsign = stringPict(exp + '\n' + rootsign) rootsign.baseline = 0 # Diagonal: length is one less than height of base linelength = bpretty.height() - 1 diagonal = stringPict('\n'.join( ' '*(linelength - i - 1) + _zZ + ' '*i for i in range(linelength) )) # Put baseline just below lowest line: next to exp diagonal.baseline = linelength - 1 # Make the root symbol rootsign = prettyForm(*rootsign.right(diagonal)) # Det the baseline to match contents to fix the height # but if the height of bpretty is one, the rootsign must be one higher rootsign.baseline = max(1, bpretty.baseline) #build result s = prettyForm(hobj('_', 2 + bpretty.width())) s = prettyForm(*bpretty.above(s)) s = prettyForm(*s.left(rootsign)) return s def _print_Pow(self, power): from sympy.simplify.simplify import fraction b, e = power.as_base_exp() if power.is_commutative: if e is S.NegativeOne: return prettyForm("1")/self._print(b) n, d = fraction(e) if n is S.One and d.is_Atom and not e.is_Integer and (e.is_Rational or d.is_Symbol) \ and self._settings['root_notation']: return self._print_nth_root(b, d) if e.is_Rational and e < 0: return prettyForm("1")/self._print(Pow(b, -e, evaluate=False)) if b.is_Relational: return prettyForm(*self._print(b).parens()).__pow__(self._print(e)) return self._print(b)**self._print(e) def _print_UnevaluatedExpr(self, expr): return self._print(expr.args[0]) def __print_numer_denom(self, p, q): if q == 1: if p < 0: return prettyForm(str(p), binding=prettyForm.NEG) else: return prettyForm(str(p)) elif abs(p) >= 10 and abs(q) >= 10: # If more than one digit in numer and denom, print larger fraction if p < 0: return prettyForm(str(p), binding=prettyForm.NEG)/prettyForm(str(q)) # Old printing method: #pform = prettyForm(str(-p))/prettyForm(str(q)) #return prettyForm(binding=prettyForm.NEG, *pform.left('- ')) else: return prettyForm(str(p))/prettyForm(str(q)) else: return None def _print_Rational(self, expr): result = self.__print_numer_denom(expr.p, expr.q) if result is not None: return result else: return self.emptyPrinter(expr) def _print_Fraction(self, expr): result = self.__print_numer_denom(expr.numerator, expr.denominator) if result is not None: return result else: return self.emptyPrinter(expr) def _print_ProductSet(self, p): if len(p.sets) >= 1 and not has_variety(p.sets): return self._print(p.sets[0]) ** self._print(len(p.sets)) else: prod_char = "\N{MULTIPLICATION SIGN}" if self._use_unicode else 'x' return self._print_seq(p.sets, None, None, ' %s ' % prod_char, parenthesize=lambda set: set.is_Union or set.is_Intersection or set.is_ProductSet) def _print_FiniteSet(self, s): items = sorted(s.args, key=default_sort_key) return self._print_seq(items, '{', '}', ', ' ) def _print_Range(self, s): if self._use_unicode: dots = "\N{HORIZONTAL ELLIPSIS}" else: dots = '...' if s.start.is_infinite and s.stop.is_infinite: if s.step.is_positive: printset = dots, -1, 0, 1, dots else: printset = dots, 1, 0, -1, dots elif 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 self._print_seq(printset, '{', '}', ', ' ) def _print_Interval(self, i): if i.start == i.end: return self._print_seq(i.args[:1], '{', '}') else: if i.left_open: left = '(' else: left = '[' if i.right_open: right = ')' else: right = ']' return self._print_seq(i.args[:2], left, right) def _print_AccumulationBounds(self, i): left = '<' right = '>' return self._print_seq(i.args[:2], left, right) def _print_Intersection(self, u): delimiter = ' %s ' % pretty_atom('Intersection', 'n') return self._print_seq(u.args, None, None, delimiter, parenthesize=lambda set: set.is_ProductSet or set.is_Union or set.is_Complement) def _print_Union(self, u): union_delimiter = ' %s ' % pretty_atom('Union', 'U') return self._print_seq(u.args, None, None, union_delimiter, parenthesize=lambda set: set.is_ProductSet or set.is_Intersection or set.is_Complement) def _print_SymmetricDifference(self, u): if not self._use_unicode: raise NotImplementedError("ASCII pretty printing of SymmetricDifference is not implemented") sym_delimeter = ' %s ' % pretty_atom('SymmetricDifference') return self._print_seq(u.args, None, None, sym_delimeter) def _print_Complement(self, u): delimiter = r' \ ' return self._print_seq(u.args, None, None, delimiter, parenthesize=lambda set: set.is_ProductSet or set.is_Intersection or set.is_Union) def _print_ImageSet(self, ts): if self._use_unicode: inn = "\N{SMALL ELEMENT OF}" else: inn = 'in' fun = ts.lamda sets = ts.base_sets signature = fun.signature expr = self._print(fun.expr) # TODO: the stuff to the left of the | and the stuff to the right of # the | should have independent baselines, that way something like # ImageSet(Lambda(x, 1/x**2), S.Naturals) prints the "x in N" part # centered on the right instead of aligned with the fraction bar on # the left. The same also applies to ConditionSet and ComplexRegion if len(signature) == 1: S = self._print_seq((signature[0], inn, sets[0]), delimiter=' ') return self._hprint_vseparator(expr, S, left='{', right='}', ifascii_nougly=True, delimiter=' ') else: pargs = tuple(j for var, setv in zip(signature, sets) for j in (var, ' ', inn, ' ', setv, ", ")) S = self._print_seq(pargs[:-1], delimiter='') return self._hprint_vseparator(expr, S, left='{', right='}', ifascii_nougly=True, delimiter=' ') def _print_ConditionSet(self, ts): if self._use_unicode: inn = "\N{SMALL ELEMENT OF}" # using _and because and is a keyword and it is bad practice to # overwrite them _and = "\N{LOGICAL AND}" else: inn = 'in' _and = 'and' variables = self._print_seq(Tuple(ts.sym)) as_expr = getattr(ts.condition, 'as_expr', None) if as_expr is not None: cond = self._print(ts.condition.as_expr()) else: cond = self._print(ts.condition) if self._use_unicode: cond = self._print(cond) cond = prettyForm(*cond.parens()) if ts.base_set is S.UniversalSet: return self._hprint_vseparator(variables, cond, left="{", right="}", ifascii_nougly=True, delimiter=' ') base = self._print(ts.base_set) C = self._print_seq((variables, inn, base, _and, cond), delimiter=' ') return self._hprint_vseparator(variables, C, left="{", right="}", ifascii_nougly=True, delimiter=' ') def _print_ComplexRegion(self, ts): if self._use_unicode: inn = "\N{SMALL ELEMENT OF}" else: inn = 'in' variables = self._print_seq(ts.variables) expr = self._print(ts.expr) prodsets = self._print(ts.sets) C = self._print_seq((variables, inn, prodsets), delimiter=' ') return self._hprint_vseparator(expr, C, left="{", right="}", ifascii_nougly=True, delimiter=' ') def _print_Contains(self, e): var, set = e.args if self._use_unicode: el = " \N{ELEMENT OF} " return prettyForm(*stringPict.next(self._print(var), el, self._print(set)), binding=8) else: return prettyForm(sstr(e)) def _print_FourierSeries(self, s): if s.an.formula is S.Zero and s.bn.formula is S.Zero: return self._print(s.a0) if self._use_unicode: dots = "\N{HORIZONTAL ELLIPSIS}" else: dots = '...' return self._print_Add(s.truncate()) + self._print(dots) def _print_FormalPowerSeries(self, s): return self._print_Add(s.infinite) def _print_SetExpr(self, se): pretty_set = prettyForm(*self._print(se.set).parens()) pretty_name = self._print(Symbol("SetExpr")) return prettyForm(*pretty_name.right(pretty_set)) def _print_SeqFormula(self, s): if self._use_unicode: dots = "\N{HORIZONTAL ELLIPSIS}" else: dots = '...' if len(s.start.free_symbols) > 0 or len(s.stop.free_symbols) > 0: raise NotImplementedError("Pretty printing of sequences with symbolic bound not implemented") if s.start is S.NegativeInfinity: stop = s.stop printset = (dots, 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(dots) printset = tuple(printset) else: printset = tuple(s) return self._print_list(printset) _print_SeqPer = _print_SeqFormula _print_SeqAdd = _print_SeqFormula _print_SeqMul = _print_SeqFormula def _print_seq(self, seq, left=None, right=None, delimiter=', ', parenthesize=lambda x: False, ifascii_nougly=True): try: pforms = [] for item in seq: pform = self._print(item) if parenthesize(item): pform = prettyForm(*pform.parens()) if pforms: pforms.append(delimiter) pforms.append(pform) if not pforms: s = stringPict('') else: s = prettyForm(*stringPict.next(*pforms)) # XXX: Under the tests from #15686 the above raises: # AttributeError: 'Fake' object has no attribute 'baseline' # This is caught below but that is not the right way to # fix it. except AttributeError: s = None for item in seq: pform = self.doprint(item) if parenthesize(item): pform = prettyForm(*pform.parens()) if s is None: # first element s = pform else : s = prettyForm(*stringPict.next(s, delimiter)) s = prettyForm(*stringPict.next(s, pform)) if s is None: s = stringPict('') s = prettyForm(*s.parens(left, right, ifascii_nougly=ifascii_nougly)) return s def join(self, delimiter, args): pform = None for arg in args: if pform is None: pform = arg else: pform = prettyForm(*pform.right(delimiter)) pform = prettyForm(*pform.right(arg)) if pform is None: return prettyForm("") else: return pform def _print_list(self, l): return self._print_seq(l, '[', ']') def _print_tuple(self, t): if len(t) == 1: ptuple = prettyForm(*stringPict.next(self._print(t[0]), ',')) return prettyForm(*ptuple.parens('(', ')', ifascii_nougly=True)) else: return self._print_seq(t, '(', ')') def _print_Tuple(self, expr): return self._print_tuple(expr) def _print_dict(self, d): keys = sorted(d.keys(), key=default_sort_key) items = [] for k in keys: K = self._print(k) V = self._print(d[k]) s = prettyForm(*stringPict.next(K, ': ', V)) items.append(s) return self._print_seq(items, '{', '}') def _print_Dict(self, d): return self._print_dict(d) def _print_set(self, s): if not s: return prettyForm('set()') items = sorted(s, key=default_sort_key) pretty = self._print_seq(items) pretty = prettyForm(*pretty.parens('{', '}', ifascii_nougly=True)) return pretty def _print_frozenset(self, s): if not s: return prettyForm('frozenset()') items = sorted(s, key=default_sort_key) pretty = self._print_seq(items) pretty = prettyForm(*pretty.parens('{', '}', ifascii_nougly=True)) pretty = prettyForm(*pretty.parens('(', ')', ifascii_nougly=True)) pretty = prettyForm(*stringPict.next(type(s).__name__, pretty)) return pretty def _print_UniversalSet(self, s): if self._use_unicode: return prettyForm("\N{MATHEMATICAL DOUBLE-STRUCK CAPITAL U}") else: return prettyForm('UniversalSet') def _print_PolyRing(self, ring): return prettyForm(sstr(ring)) def _print_FracField(self, field): return prettyForm(sstr(field)) def _print_FreeGroupElement(self, elm): return prettyForm(str(elm)) def _print_PolyElement(self, poly): return prettyForm(sstr(poly)) def _print_FracElement(self, frac): return prettyForm(sstr(frac)) def _print_AlgebraicNumber(self, expr): if expr.is_aliased: return self._print(expr.as_poly().as_expr()) else: return self._print(expr.as_expr()) def _print_ComplexRootOf(self, expr): args = [self._print_Add(expr.expr, order='lex'), expr.index] pform = prettyForm(*self._print_seq(args).parens()) pform = prettyForm(*pform.left('CRootOf')) return pform def _print_RootSum(self, expr): args = [self._print_Add(expr.expr, order='lex')] if expr.fun is not S.IdentityFunction: args.append(self._print(expr.fun)) pform = prettyForm(*self._print_seq(args).parens()) pform = prettyForm(*pform.left('RootSum')) return pform def _print_FiniteField(self, expr): if self._use_unicode: form = '\N{DOUBLE-STRUCK CAPITAL Z}_%d' else: form = 'GF(%d)' return prettyForm(pretty_symbol(form % expr.mod)) def _print_IntegerRing(self, expr): if self._use_unicode: return prettyForm('\N{DOUBLE-STRUCK CAPITAL Z}') else: return prettyForm('ZZ') def _print_RationalField(self, expr): if self._use_unicode: return prettyForm('\N{DOUBLE-STRUCK CAPITAL Q}') else: return prettyForm('QQ') def _print_RealField(self, domain): if self._use_unicode: prefix = '\N{DOUBLE-STRUCK CAPITAL R}' else: prefix = 'RR' if domain.has_default_precision: return prettyForm(prefix) else: return self._print(pretty_symbol(prefix + "_" + str(domain.precision))) def _print_ComplexField(self, domain): if self._use_unicode: prefix = '\N{DOUBLE-STRUCK CAPITAL C}' else: prefix = 'CC' if domain.has_default_precision: return prettyForm(prefix) else: return self._print(pretty_symbol(prefix + "_" + str(domain.precision))) def _print_PolynomialRing(self, expr): args = list(expr.symbols) if not expr.order.is_default: order = prettyForm(*prettyForm("order=").right(self._print(expr.order))) args.append(order) pform = self._print_seq(args, '[', ']') pform = prettyForm(*pform.left(self._print(expr.domain))) return pform def _print_FractionField(self, expr): args = list(expr.symbols) if not expr.order.is_default: order = prettyForm(*prettyForm("order=").right(self._print(expr.order))) args.append(order) pform = self._print_seq(args, '(', ')') pform = prettyForm(*pform.left(self._print(expr.domain))) return pform def _print_PolynomialRingBase(self, expr): g = expr.symbols if str(expr.order) != str(expr.default_order): g = g + ("order=" + str(expr.order),) pform = self._print_seq(g, '[', ']') pform = prettyForm(*pform.left(self._print(expr.domain))) return pform def _print_GroebnerBasis(self, basis): exprs = [ self._print_Add(arg, order=basis.order) for arg in basis.exprs ] exprs = prettyForm(*self.join(", ", exprs).parens(left="[", right="]")) gens = [ self._print(gen) for gen in basis.gens ] domain = prettyForm( *prettyForm("domain=").right(self._print(basis.domain))) order = prettyForm( *prettyForm("order=").right(self._print(basis.order))) pform = self.join(", ", [exprs] + gens + [domain, order]) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left(basis.__class__.__name__)) return pform def _print_Subs(self, e): pform = self._print(e.expr) pform = prettyForm(*pform.parens()) h = pform.height() if pform.height() > 1 else 2 rvert = stringPict(vobj('|', h), baseline=pform.baseline) pform = prettyForm(*pform.right(rvert)) b = pform.baseline pform.baseline = pform.height() - 1 pform = prettyForm(*pform.right(self._print_seq([ self._print_seq((self._print(v[0]), xsym('=='), self._print(v[1])), delimiter='') for v in zip(e.variables, e.point) ]))) pform.baseline = b return pform def _print_number_function(self, e, name): # Print name_arg[0] for one argument or name_arg[0](arg[1]) # for more than one argument pform = prettyForm(name) arg = self._print(e.args[0]) pform_arg = prettyForm(" "*arg.width()) pform_arg = prettyForm(*pform_arg.below(arg)) pform = prettyForm(*pform.right(pform_arg)) if len(e.args) == 1: return pform m, x = e.args # TODO: copy-pasted from _print_Function: can we do better? prettyFunc = pform prettyArgs = prettyForm(*self._print_seq([x]).parens()) pform = prettyForm( binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) pform.prettyFunc = prettyFunc pform.prettyArgs = prettyArgs return pform def _print_euler(self, e): return self._print_number_function(e, "E") def _print_catalan(self, e): return self._print_number_function(e, "C") def _print_bernoulli(self, e): return self._print_number_function(e, "B") _print_bell = _print_bernoulli def _print_lucas(self, e): return self._print_number_function(e, "L") def _print_fibonacci(self, e): return self._print_number_function(e, "F") def _print_tribonacci(self, e): return self._print_number_function(e, "T") def _print_stieltjes(self, e): if self._use_unicode: return self._print_number_function(e, '\N{GREEK SMALL LETTER GAMMA}') else: return self._print_number_function(e, "stieltjes") def _print_KroneckerDelta(self, e): pform = self._print(e.args[0]) pform = prettyForm(*pform.right(prettyForm(','))) pform = prettyForm(*pform.right(self._print(e.args[1]))) if self._use_unicode: a = stringPict(pretty_symbol('delta')) else: a = stringPict('d') b = pform top = stringPict(*b.left(' '*a.width())) bot = stringPict(*a.right(' '*b.width())) return prettyForm(binding=prettyForm.POW, *bot.below(top)) def _print_RandomDomain(self, d): if hasattr(d, 'as_boolean'): pform = self._print('Domain: ') pform = prettyForm(*pform.right(self._print(d.as_boolean()))) return pform elif hasattr(d, 'set'): pform = self._print('Domain: ') pform = prettyForm(*pform.right(self._print(d.symbols))) pform = prettyForm(*pform.right(self._print(' in '))) pform = prettyForm(*pform.right(self._print(d.set))) return pform elif hasattr(d, 'symbols'): pform = self._print('Domain on ') pform = prettyForm(*pform.right(self._print(d.symbols))) return pform else: return self._print(None) 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(pretty_symbol(object.name)) def _print_Morphism(self, morphism): arrow = xsym("-->") domain = self._print(morphism.domain) codomain = self._print(morphism.codomain) tail = domain.right(arrow, codomain)[0] return prettyForm(tail) def _print_NamedMorphism(self, morphism): pretty_name = self._print(pretty_symbol(morphism.name)) pretty_morphism = self._print_Morphism(morphism) return prettyForm(pretty_name.right(":", pretty_morphism)[0]) 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): circle = xsym(".") # All components of the morphism have names and it is thus # possible to build the name of the composite. component_names_list = [pretty_symbol(component.name) for component in morphism.components] component_names_list.reverse() component_names = circle.join(component_names_list) + ":" pretty_name = self._print(component_names) pretty_morphism = self._print_Morphism(morphism) return prettyForm(pretty_name.right(pretty_morphism)[0]) def _print_Category(self, category): return self._print(pretty_symbol(category.name)) def _print_Diagram(self, diagram): if not diagram.premises: # This is an empty diagram. return self._print(S.EmptySet) pretty_result = self._print(diagram.premises) if diagram.conclusions: results_arrow = " %s " % xsym("==>") pretty_conclusions = self._print(diagram.conclusions)[0] pretty_result = pretty_result.right( results_arrow, pretty_conclusions) return prettyForm(pretty_result[0]) def _print_DiagramGrid(self, grid): from sympy.matrices import Matrix matrix = Matrix([[grid[i, j] if grid[i, j] else Symbol(" ") for j in range(grid.width)] for i in range(grid.height)]) return self._print_matrix_contents(matrix) def _print_FreeModuleElement(self, m): # Print as row vector for convenience, for now. return self._print_seq(m, '[', ']') def _print_SubModule(self, M): return self._print_seq(M.gens, '<', '>') def _print_FreeModule(self, M): return self._print(M.ring)**self._print(M.rank) def _print_ModuleImplementedIdeal(self, M): return self._print_seq([x for [x] in M._module.gens], '<', '>') def _print_QuotientRing(self, R): return self._print(R.ring) / self._print(R.base_ideal) def _print_QuotientRingElement(self, R): return self._print(R.data) + self._print(R.ring.base_ideal) def _print_QuotientModuleElement(self, m): return self._print(m.data) + self._print(m.module.killed_module) def _print_QuotientModule(self, M): return self._print(M.base) / self._print(M.killed_module) def _print_MatrixHomomorphism(self, h): matrix = self._print(h._sympy_matrix()) matrix.baseline = matrix.height() // 2 pform = prettyForm(*matrix.right(' : ', self._print(h.domain), ' %s> ' % hobj('-', 2), self._print(h.codomain))) return pform def _print_Manifold(self, manifold): return self._print(manifold.name) def _print_Patch(self, patch): return self._print(patch.name) def _print_CoordSystem(self, coords): return self._print(coords.name) def _print_BaseScalarField(self, field): string = field._coord_sys.symbols[field._index].name return self._print(pretty_symbol(string)) def _print_BaseVectorField(self, field): s = U('PARTIAL DIFFERENTIAL') + '_' + field._coord_sys.symbols[field._index].name return self._print(pretty_symbol(s)) def _print_Differential(self, diff): if self._use_unicode: d = '\N{DOUBLE-STRUCK ITALIC SMALL D}' else: d = 'd' field = diff._form_field if hasattr(field, '_coord_sys'): string = field._coord_sys.symbols[field._index].name return self._print(d + ' ' + pretty_symbol(string)) else: pform = self._print(field) pform = prettyForm(*pform.parens()) return prettyForm(*pform.left(d)) def _print_Tr(self, p): #TODO: Handle indices pform = self._print(p.args[0]) pform = prettyForm(*pform.left('%s(' % (p.__class__.__name__))) pform = prettyForm(*pform.right(')')) return pform def _print_primenu(self, e): pform = self._print(e.args[0]) pform = prettyForm(*pform.parens()) if self._use_unicode: pform = prettyForm(*pform.left(greek_unicode['nu'])) else: pform = prettyForm(*pform.left('nu')) return pform def _print_primeomega(self, e): pform = self._print(e.args[0]) pform = prettyForm(*pform.parens()) if self._use_unicode: pform = prettyForm(*pform.left(greek_unicode['Omega'])) else: pform = prettyForm(*pform.left('Omega')) return pform def _print_Quantity(self, e): if e.name.name == 'degree': pform = self._print("\N{DEGREE SIGN}") return pform else: return self.emptyPrinter(e) def _print_AssignmentBase(self, e): op = prettyForm(' ' + xsym(e.op) + ' ') l = self._print(e.lhs) r = self._print(e.rhs) pform = prettyForm(*stringPict.next(l, op, r)) return pform def _print_Str(self, s): return self._print(s.name) @print_function(PrettyPrinter) def pretty(expr, **settings): """Returns a string containing the prettified form of expr. For information on keyword arguments see pretty_print function. """ pp = PrettyPrinter(settings) # XXX: this is an ugly hack, but at least it works use_unicode = pp._settings['use_unicode'] uflag = pretty_use_unicode(use_unicode) try: return pp.doprint(expr) finally: pretty_use_unicode(uflag) def pretty_print(expr, **kwargs): """Prints expr in pretty form. pprint is just a shortcut for this function. Parameters ========== expr : expression The expression to print. wrap_line : bool, optional (default=True) Line wrapping enabled/disabled. num_columns : int or None, optional (default=None) Number of columns before line breaking (default to None which reads the terminal width), useful when using SymPy without terminal. use_unicode : bool or None, optional (default=None) Use unicode characters, such as the Greek letter pi instead of the string pi. full_prec : bool or string, optional (default="auto") Use full precision. order : bool or string, optional (default=None) Set to 'none' for long expressions if slow; default is None. use_unicode_sqrt_char : bool, optional (default=True) Use compact single-character square root symbol (when unambiguous). root_notation : bool, optional (default=True) Set to 'False' for printing exponents of the form 1/n in fractional form. By default exponent is printed in root form. mat_symbol_style : string, optional (default="plain") Set to "bold" for printing MatrixSymbols using a bold mathematical symbol face. By default the standard face is used. imaginary_unit : string, optional (default="i") Letter to use for imaginary unit when use_unicode is True. Can be "i" (default) or "j". """ print(pretty(expr, **kwargs)) pprint = pretty_print def pager_print(expr, **settings): """Prints expr using the pager, in pretty form. This invokes a pager command using pydoc. Lines are not wrapped automatically. This routine is meant to be used with a pager that allows sideways scrolling, like ``less -S``. Parameters are the same as for ``pretty_print``. If you wish to wrap lines, pass ``num_columns=None`` to auto-detect the width of the terminal. """ from pydoc import pager from locale import getpreferredencoding if 'num_columns' not in settings: settings['num_columns'] = 500000 # disable line wrap pager(pretty(expr, **settings).encode(getpreferredencoding()))