1028 lines
32 KiB
Python
1028 lines
32 KiB
Python
|
"""
|
||
|
A Printer for generating readable representation of most SymPy classes.
|
||
|
"""
|
||
|
|
||
|
from __future__ import annotations
|
||
|
from typing import Any
|
||
|
|
||
|
from sympy.core import S, Rational, Pow, Basic, Mul, Number
|
||
|
from sympy.core.mul import _keep_coeff
|
||
|
from sympy.core.relational import Relational
|
||
|
from sympy.core.sorting import default_sort_key
|
||
|
from sympy.core.sympify import SympifyError
|
||
|
from sympy.utilities.iterables import sift
|
||
|
from .precedence import precedence, PRECEDENCE
|
||
|
from .printer import Printer, print_function
|
||
|
|
||
|
from mpmath.libmp import prec_to_dps, to_str as mlib_to_str
|
||
|
|
||
|
|
||
|
class StrPrinter(Printer):
|
||
|
printmethod = "_sympystr"
|
||
|
_default_settings: dict[str, Any] = {
|
||
|
"order": None,
|
||
|
"full_prec": "auto",
|
||
|
"sympy_integers": False,
|
||
|
"abbrev": False,
|
||
|
"perm_cyclic": True,
|
||
|
"min": None,
|
||
|
"max": None,
|
||
|
}
|
||
|
|
||
|
_relationals: dict[str, str] = {}
|
||
|
|
||
|
def parenthesize(self, item, level, strict=False):
|
||
|
if (precedence(item) < level) or ((not strict) and precedence(item) <= level):
|
||
|
return "(%s)" % self._print(item)
|
||
|
else:
|
||
|
return self._print(item)
|
||
|
|
||
|
def stringify(self, args, sep, level=0):
|
||
|
return sep.join([self.parenthesize(item, level) for item in args])
|
||
|
|
||
|
def emptyPrinter(self, expr):
|
||
|
if isinstance(expr, str):
|
||
|
return expr
|
||
|
elif isinstance(expr, Basic):
|
||
|
return repr(expr)
|
||
|
else:
|
||
|
return str(expr)
|
||
|
|
||
|
def _print_Add(self, expr, order=None):
|
||
|
terms = self._as_ordered_terms(expr, order=order)
|
||
|
|
||
|
prec = precedence(expr)
|
||
|
l = []
|
||
|
for term in terms:
|
||
|
t = self._print(term)
|
||
|
if t.startswith('-') and not term.is_Add:
|
||
|
sign = "-"
|
||
|
t = t[1:]
|
||
|
else:
|
||
|
sign = "+"
|
||
|
if precedence(term) < prec or term.is_Add:
|
||
|
l.extend([sign, "(%s)" % t])
|
||
|
else:
|
||
|
l.extend([sign, t])
|
||
|
sign = l.pop(0)
|
||
|
if sign == '+':
|
||
|
sign = ""
|
||
|
return sign + ' '.join(l)
|
||
|
|
||
|
def _print_BooleanTrue(self, expr):
|
||
|
return "True"
|
||
|
|
||
|
def _print_BooleanFalse(self, expr):
|
||
|
return "False"
|
||
|
|
||
|
def _print_Not(self, expr):
|
||
|
return '~%s' %(self.parenthesize(expr.args[0],PRECEDENCE["Not"]))
|
||
|
|
||
|
def _print_And(self, expr):
|
||
|
args = list(expr.args)
|
||
|
for j, i in enumerate(args):
|
||
|
if isinstance(i, Relational) and (
|
||
|
i.canonical.rhs is S.NegativeInfinity):
|
||
|
args.insert(0, args.pop(j))
|
||
|
return self.stringify(args, " & ", PRECEDENCE["BitwiseAnd"])
|
||
|
|
||
|
def _print_Or(self, expr):
|
||
|
return self.stringify(expr.args, " | ", PRECEDENCE["BitwiseOr"])
|
||
|
|
||
|
def _print_Xor(self, expr):
|
||
|
return self.stringify(expr.args, " ^ ", PRECEDENCE["BitwiseXor"])
|
||
|
|
||
|
def _print_AppliedPredicate(self, expr):
|
||
|
return '%s(%s)' % (
|
||
|
self._print(expr.function), self.stringify(expr.arguments, ", "))
|
||
|
|
||
|
def _print_Basic(self, expr):
|
||
|
l = [self._print(o) for o in expr.args]
|
||
|
return expr.__class__.__name__ + "(%s)" % ", ".join(l)
|
||
|
|
||
|
def _print_BlockMatrix(self, B):
|
||
|
if B.blocks.shape == (1, 1):
|
||
|
self._print(B.blocks[0, 0])
|
||
|
return self._print(B.blocks)
|
||
|
|
||
|
def _print_Catalan(self, expr):
|
||
|
return 'Catalan'
|
||
|
|
||
|
def _print_ComplexInfinity(self, expr):
|
||
|
return 'zoo'
|
||
|
|
||
|
def _print_ConditionSet(self, s):
|
||
|
args = tuple([self._print(i) for i in (s.sym, s.condition)])
|
||
|
if s.base_set is S.UniversalSet:
|
||
|
return 'ConditionSet(%s, %s)' % args
|
||
|
args += (self._print(s.base_set),)
|
||
|
return 'ConditionSet(%s, %s, %s)' % args
|
||
|
|
||
|
def _print_Derivative(self, expr):
|
||
|
dexpr = expr.expr
|
||
|
dvars = [i[0] if i[1] == 1 else i for i in expr.variable_count]
|
||
|
return 'Derivative(%s)' % ", ".join((self._print(arg) for arg in [dexpr] + dvars))
|
||
|
|
||
|
def _print_dict(self, d):
|
||
|
keys = sorted(d.keys(), key=default_sort_key)
|
||
|
items = []
|
||
|
|
||
|
for key in keys:
|
||
|
item = "%s: %s" % (self._print(key), self._print(d[key]))
|
||
|
items.append(item)
|
||
|
|
||
|
return "{%s}" % ", ".join(items)
|
||
|
|
||
|
def _print_Dict(self, expr):
|
||
|
return self._print_dict(expr)
|
||
|
|
||
|
def _print_RandomDomain(self, d):
|
||
|
if hasattr(d, 'as_boolean'):
|
||
|
return 'Domain: ' + self._print(d.as_boolean())
|
||
|
elif hasattr(d, 'set'):
|
||
|
return ('Domain: ' + self._print(d.symbols) + ' in ' +
|
||
|
self._print(d.set))
|
||
|
else:
|
||
|
return 'Domain on ' + self._print(d.symbols)
|
||
|
|
||
|
def _print_Dummy(self, expr):
|
||
|
return '_' + expr.name
|
||
|
|
||
|
def _print_EulerGamma(self, expr):
|
||
|
return 'EulerGamma'
|
||
|
|
||
|
def _print_Exp1(self, expr):
|
||
|
return 'E'
|
||
|
|
||
|
def _print_ExprCondPair(self, expr):
|
||
|
return '(%s, %s)' % (self._print(expr.expr), self._print(expr.cond))
|
||
|
|
||
|
def _print_Function(self, expr):
|
||
|
return expr.func.__name__ + "(%s)" % self.stringify(expr.args, ", ")
|
||
|
|
||
|
def _print_GoldenRatio(self, expr):
|
||
|
return 'GoldenRatio'
|
||
|
|
||
|
def _print_Heaviside(self, expr):
|
||
|
# Same as _print_Function but uses pargs to suppress default 1/2 for
|
||
|
# 2nd args
|
||
|
return expr.func.__name__ + "(%s)" % self.stringify(expr.pargs, ", ")
|
||
|
|
||
|
def _print_TribonacciConstant(self, expr):
|
||
|
return 'TribonacciConstant'
|
||
|
|
||
|
def _print_ImaginaryUnit(self, expr):
|
||
|
return 'I'
|
||
|
|
||
|
def _print_Infinity(self, expr):
|
||
|
return 'oo'
|
||
|
|
||
|
def _print_Integral(self, expr):
|
||
|
def _xab_tostr(xab):
|
||
|
if len(xab) == 1:
|
||
|
return self._print(xab[0])
|
||
|
else:
|
||
|
return self._print((xab[0],) + tuple(xab[1:]))
|
||
|
L = ', '.join([_xab_tostr(l) for l in expr.limits])
|
||
|
return 'Integral(%s, %s)' % (self._print(expr.function), L)
|
||
|
|
||
|
def _print_Interval(self, i):
|
||
|
fin = 'Interval{m}({a}, {b})'
|
||
|
a, b, l, r = i.args
|
||
|
if a.is_infinite and b.is_infinite:
|
||
|
m = ''
|
||
|
elif a.is_infinite and not r:
|
||
|
m = ''
|
||
|
elif b.is_infinite and not l:
|
||
|
m = ''
|
||
|
elif not l and not r:
|
||
|
m = ''
|
||
|
elif l and r:
|
||
|
m = '.open'
|
||
|
elif l:
|
||
|
m = '.Lopen'
|
||
|
else:
|
||
|
m = '.Ropen'
|
||
|
return fin.format(**{'a': a, 'b': b, 'm': m})
|
||
|
|
||
|
def _print_AccumulationBounds(self, i):
|
||
|
return "AccumBounds(%s, %s)" % (self._print(i.min),
|
||
|
self._print(i.max))
|
||
|
|
||
|
def _print_Inverse(self, I):
|
||
|
return "%s**(-1)" % self.parenthesize(I.arg, PRECEDENCE["Pow"])
|
||
|
|
||
|
def _print_Lambda(self, obj):
|
||
|
expr = obj.expr
|
||
|
sig = obj.signature
|
||
|
if len(sig) == 1 and sig[0].is_symbol:
|
||
|
sig = sig[0]
|
||
|
return "Lambda(%s, %s)" % (self._print(sig), self._print(expr))
|
||
|
|
||
|
def _print_LatticeOp(self, expr):
|
||
|
args = sorted(expr.args, key=default_sort_key)
|
||
|
return expr.func.__name__ + "(%s)" % ", ".join(self._print(arg) for arg in args)
|
||
|
|
||
|
def _print_Limit(self, expr):
|
||
|
e, z, z0, dir = expr.args
|
||
|
return "Limit(%s, %s, %s, dir='%s')" % tuple(map(self._print, (e, z, z0, dir)))
|
||
|
|
||
|
|
||
|
def _print_list(self, expr):
|
||
|
return "[%s]" % self.stringify(expr, ", ")
|
||
|
|
||
|
def _print_List(self, expr):
|
||
|
return self._print_list(expr)
|
||
|
|
||
|
def _print_MatrixBase(self, expr):
|
||
|
return expr._format_str(self)
|
||
|
|
||
|
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 strslice(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 ':'.join((self._print(arg) for arg in x))
|
||
|
return (self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) + '[' +
|
||
|
strslice(expr.rowslice, expr.parent.rows) + ', ' +
|
||
|
strslice(expr.colslice, expr.parent.cols) + ']')
|
||
|
|
||
|
def _print_DeferredVector(self, expr):
|
||
|
return expr.name
|
||
|
|
||
|
def _print_Mul(self, expr):
|
||
|
|
||
|
prec = precedence(expr)
|
||
|
|
||
|
# 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 = expr.args
|
||
|
if args[0] is S.One or any(
|
||
|
isinstance(a, Number) or
|
||
|
a.is_Pow and all(ai.is_Integer for ai in a.args)
|
||
|
for a in args[1:]):
|
||
|
d, n = sift(args, lambda x:
|
||
|
isinstance(x, Pow) and bool(x.exp.as_coeff_Mul()[0] < 0),
|
||
|
binary=True)
|
||
|
for i, di in enumerate(d):
|
||
|
if di.exp.is_Number:
|
||
|
e = -di.exp
|
||
|
else:
|
||
|
dargs = list(di.exp.args)
|
||
|
dargs[0] = -dargs[0]
|
||
|
e = Mul._from_args(dargs)
|
||
|
d[i] = Pow(di.base, e, evaluate=False) if e - 1 else di.base
|
||
|
|
||
|
pre = []
|
||
|
# don't parenthesize first factor if negative
|
||
|
if n and not n[0].is_Add and n[0].could_extract_minus_sign():
|
||
|
pre = [self._print(n.pop(0))]
|
||
|
|
||
|
nfactors = pre + [self.parenthesize(a, prec, strict=False)
|
||
|
for a in n]
|
||
|
if not nfactors:
|
||
|
nfactors = ['1']
|
||
|
|
||
|
# don't parenthesize first of denominator unless singleton
|
||
|
if len(d) > 1 and d[0].could_extract_minus_sign():
|
||
|
pre = [self._print(d.pop(0))]
|
||
|
else:
|
||
|
pre = []
|
||
|
dfactors = pre + [self.parenthesize(a, prec, strict=False)
|
||
|
for a in d]
|
||
|
|
||
|
n = '*'.join(nfactors)
|
||
|
d = '*'.join(dfactors)
|
||
|
if len(dfactors) > 1:
|
||
|
return '%s/(%s)' % (n, d)
|
||
|
elif dfactors:
|
||
|
return '%s/%s' % (n, d)
|
||
|
return n
|
||
|
|
||
|
c, e = expr.as_coeff_Mul()
|
||
|
if c < 0:
|
||
|
expr = _keep_coeff(-c, e)
|
||
|
sign = "-"
|
||
|
else:
|
||
|
sign = ""
|
||
|
|
||
|
a = [] # items in the numerator
|
||
|
b = [] # items that are in the denominator (if any)
|
||
|
|
||
|
pow_paren = [] # Will collect all pow with more than one base element and exp = -1
|
||
|
|
||
|
if self.order not in ('old', 'none'):
|
||
|
args = expr.as_ordered_factors()
|
||
|
else:
|
||
|
# use make_args in case expr was something like -x -> x
|
||
|
args = Mul.make_args(expr)
|
||
|
|
||
|
# Gather args for numerator/denominator
|
||
|
def apow(i):
|
||
|
b, e = i.as_base_exp()
|
||
|
eargs = list(Mul.make_args(e))
|
||
|
if eargs[0] is S.NegativeOne:
|
||
|
eargs = eargs[1:]
|
||
|
else:
|
||
|
eargs[0] = -eargs[0]
|
||
|
e = Mul._from_args(eargs)
|
||
|
if isinstance(i, Pow):
|
||
|
return i.func(b, e, evaluate=False)
|
||
|
return i.func(e, evaluate=False)
|
||
|
for item in args:
|
||
|
if (item.is_commutative and
|
||
|
isinstance(item, Pow) and
|
||
|
bool(item.exp.as_coeff_Mul()[0] < 0)):
|
||
|
if item.exp is not S.NegativeOne:
|
||
|
b.append(apow(item))
|
||
|
else:
|
||
|
if (len(item.args[0].args) != 1 and
|
||
|
isinstance(item.base, (Mul, Pow))):
|
||
|
# To avoid situations like #14160
|
||
|
pow_paren.append(item)
|
||
|
b.append(item.base)
|
||
|
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)
|
||
|
|
||
|
a = a or [S.One]
|
||
|
|
||
|
a_str = [self.parenthesize(x, prec, strict=False) for x in a]
|
||
|
b_str = [self.parenthesize(x, prec, strict=False) for x in b]
|
||
|
|
||
|
# To parenthesize Pow with exp = -1 and having more than one Symbol
|
||
|
for item in pow_paren:
|
||
|
if item.base in b:
|
||
|
b_str[b.index(item.base)] = "(%s)" % b_str[b.index(item.base)]
|
||
|
|
||
|
if not b:
|
||
|
return sign + '*'.join(a_str)
|
||
|
elif len(b) == 1:
|
||
|
return sign + '*'.join(a_str) + "/" + b_str[0]
|
||
|
else:
|
||
|
return sign + '*'.join(a_str) + "/(%s)" % '*'.join(b_str)
|
||
|
|
||
|
def _print_MatMul(self, expr):
|
||
|
c, m = expr.as_coeff_mmul()
|
||
|
|
||
|
sign = ""
|
||
|
if c.is_number:
|
||
|
re, im = c.as_real_imag()
|
||
|
if im.is_zero and re.is_negative:
|
||
|
expr = _keep_coeff(-c, m)
|
||
|
sign = "-"
|
||
|
elif re.is_zero and im.is_negative:
|
||
|
expr = _keep_coeff(-c, m)
|
||
|
sign = "-"
|
||
|
|
||
|
return sign + '*'.join(
|
||
|
[self.parenthesize(arg, precedence(expr)) for arg in expr.args]
|
||
|
)
|
||
|
|
||
|
def _print_ElementwiseApplyFunction(self, expr):
|
||
|
return "{}.({})".format(
|
||
|
expr.function,
|
||
|
self._print(expr.expr),
|
||
|
)
|
||
|
|
||
|
def _print_NaN(self, expr):
|
||
|
return 'nan'
|
||
|
|
||
|
def _print_NegativeInfinity(self, expr):
|
||
|
return '-oo'
|
||
|
|
||
|
def _print_Order(self, expr):
|
||
|
if not expr.variables or all(p is S.Zero for p in expr.point):
|
||
|
if len(expr.variables) <= 1:
|
||
|
return 'O(%s)' % self._print(expr.expr)
|
||
|
else:
|
||
|
return 'O(%s)' % self.stringify((expr.expr,) + expr.variables, ', ', 0)
|
||
|
else:
|
||
|
return 'O(%s)' % self.stringify(expr.args, ', ', 0)
|
||
|
|
||
|
def _print_Ordinal(self, expr):
|
||
|
return expr.__str__()
|
||
|
|
||
|
def _print_Cycle(self, expr):
|
||
|
return expr.__str__()
|
||
|
|
||
|
def _print_Permutation(self, expr):
|
||
|
from sympy.combinatorics.permutations import Permutation, Cycle
|
||
|
from sympy.utilities.exceptions import sympy_deprecation_warning
|
||
|
|
||
|
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:
|
||
|
if not expr.size:
|
||
|
return '()'
|
||
|
# before taking Cycle notation, see if the last element is
|
||
|
# a singleton and move it to the head of the string
|
||
|
s = Cycle(expr)(expr.size - 1).__repr__()[len('Cycle'):]
|
||
|
last = s.rfind('(')
|
||
|
if not last == 0 and ',' not in s[last:]:
|
||
|
s = s[last:] + s[:last]
|
||
|
s = s.replace(',', '')
|
||
|
return s
|
||
|
else:
|
||
|
s = expr.support()
|
||
|
if not s:
|
||
|
if expr.size < 5:
|
||
|
return 'Permutation(%s)' % self._print(expr.array_form)
|
||
|
return 'Permutation([], size=%s)' % self._print(expr.size)
|
||
|
trim = self._print(expr.array_form[:s[-1] + 1]) + ', size=%s' % self._print(expr.size)
|
||
|
use = full = self._print(expr.array_form)
|
||
|
if len(trim) < len(full):
|
||
|
use = trim
|
||
|
return 'Permutation(%s)' % use
|
||
|
|
||
|
def _print_Subs(self, obj):
|
||
|
expr, old, new = obj.args
|
||
|
if len(obj.point) == 1:
|
||
|
old = old[0]
|
||
|
new = new[0]
|
||
|
return "Subs(%s, %s, %s)" % (
|
||
|
self._print(expr), self._print(old), self._print(new))
|
||
|
|
||
|
def _print_TensorIndex(self, expr):
|
||
|
return expr._print()
|
||
|
|
||
|
def _print_TensorHead(self, expr):
|
||
|
return expr._print()
|
||
|
|
||
|
def _print_Tensor(self, expr):
|
||
|
return expr._print()
|
||
|
|
||
|
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):
|
||
|
return expr._print()
|
||
|
|
||
|
def _print_ArraySymbol(self, expr):
|
||
|
return self._print(expr.name)
|
||
|
|
||
|
def _print_ArrayElement(self, expr):
|
||
|
return "%s[%s]" % (
|
||
|
self.parenthesize(expr.name, PRECEDENCE["Func"], True), ", ".join([self._print(i) for i in expr.indices]))
|
||
|
|
||
|
def _print_PermutationGroup(self, expr):
|
||
|
p = [' %s' % self._print(a) for a in expr.args]
|
||
|
return 'PermutationGroup([\n%s])' % ',\n'.join(p)
|
||
|
|
||
|
def _print_Pi(self, expr):
|
||
|
return 'pi'
|
||
|
|
||
|
def _print_PolyRing(self, ring):
|
||
|
return "Polynomial ring in %s over %s with %s order" % \
|
||
|
(", ".join((self._print(rs) for rs in ring.symbols)),
|
||
|
self._print(ring.domain), self._print(ring.order))
|
||
|
|
||
|
def _print_FracField(self, field):
|
||
|
return "Rational function field in %s over %s with %s order" % \
|
||
|
(", ".join((self._print(fs) for fs in field.symbols)),
|
||
|
self._print(field.domain), self._print(field.order))
|
||
|
|
||
|
def _print_FreeGroupElement(self, elm):
|
||
|
return elm.__str__()
|
||
|
|
||
|
def _print_GaussianElement(self, poly):
|
||
|
return "(%s + %s*I)" % (poly.x, poly.y)
|
||
|
|
||
|
def _print_PolyElement(self, poly):
|
||
|
return poly.str(self, PRECEDENCE, "%s**%s", "*")
|
||
|
|
||
|
def _print_FracElement(self, frac):
|
||
|
if frac.denom == 1:
|
||
|
return self._print(frac.numer)
|
||
|
else:
|
||
|
numer = self.parenthesize(frac.numer, PRECEDENCE["Mul"], strict=True)
|
||
|
denom = self.parenthesize(frac.denom, PRECEDENCE["Atom"], strict=True)
|
||
|
return numer + "/" + denom
|
||
|
|
||
|
def _print_Poly(self, expr):
|
||
|
ATOM_PREC = PRECEDENCE["Atom"] - 1
|
||
|
terms, gens = [], [ self.parenthesize(s, ATOM_PREC) for s in expr.gens ]
|
||
|
|
||
|
for monom, coeff in expr.terms():
|
||
|
s_monom = []
|
||
|
|
||
|
for i, e in enumerate(monom):
|
||
|
if e > 0:
|
||
|
if e == 1:
|
||
|
s_monom.append(gens[i])
|
||
|
else:
|
||
|
s_monom.append(gens[i] + "**%d" % e)
|
||
|
|
||
|
s_monom = "*".join(s_monom)
|
||
|
|
||
|
if coeff.is_Add:
|
||
|
if s_monom:
|
||
|
s_coeff = "(" + 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]
|
||
|
|
||
|
format = expr.__class__.__name__ + "(%s, %s"
|
||
|
|
||
|
from sympy.polys.polyerrors import PolynomialError
|
||
|
|
||
|
try:
|
||
|
format += ", modulus=%s" % expr.get_modulus()
|
||
|
except PolynomialError:
|
||
|
format += ", domain='%s'" % expr.get_domain()
|
||
|
|
||
|
format += ")"
|
||
|
|
||
|
for index, item in enumerate(gens):
|
||
|
if len(item) > 2 and (item[:1] == "(" and item[len(item) - 1:] == ")"):
|
||
|
gens[index] = item[1:len(item) - 1]
|
||
|
|
||
|
return format % (' '.join(terms), ', '.join(gens))
|
||
|
|
||
|
def _print_UniversalSet(self, p):
|
||
|
return 'UniversalSet'
|
||
|
|
||
|
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_Pow(self, expr, rational=False):
|
||
|
"""Printing helper function for ``Pow``
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
rational : bool, optional
|
||
|
If ``True``, it will not attempt printing ``sqrt(x)`` or
|
||
|
``x**S.Half`` as ``sqrt``, and will use ``x**(1/2)``
|
||
|
instead.
|
||
|
|
||
|
See examples for additional details
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import sqrt, StrPrinter
|
||
|
>>> from sympy.abc import x
|
||
|
|
||
|
How ``rational`` keyword works with ``sqrt``:
|
||
|
|
||
|
>>> printer = StrPrinter()
|
||
|
>>> printer._print_Pow(sqrt(x), rational=True)
|
||
|
'x**(1/2)'
|
||
|
>>> printer._print_Pow(sqrt(x), rational=False)
|
||
|
'sqrt(x)'
|
||
|
>>> printer._print_Pow(1/sqrt(x), rational=True)
|
||
|
'x**(-1/2)'
|
||
|
>>> printer._print_Pow(1/sqrt(x), rational=False)
|
||
|
'1/sqrt(x)'
|
||
|
|
||
|
Notes
|
||
|
=====
|
||
|
|
||
|
``sqrt(x)`` is canonicalized as ``Pow(x, S.Half)`` in SymPy,
|
||
|
so there is no need of defining a separate printer for ``sqrt``.
|
||
|
Instead, it should be handled here as well.
|
||
|
"""
|
||
|
PREC = precedence(expr)
|
||
|
|
||
|
if expr.exp is S.Half and not rational:
|
||
|
return "sqrt(%s)" % self._print(expr.base)
|
||
|
|
||
|
if expr.is_commutative:
|
||
|
if -expr.exp is S.Half and not rational:
|
||
|
# Note: Don't test "expr.exp == -S.Half" here, because that will
|
||
|
# match -0.5, which we don't want.
|
||
|
return "%s/sqrt(%s)" % tuple((self._print(arg) for arg in (S.One, expr.base)))
|
||
|
if expr.exp is -S.One:
|
||
|
# Similarly to the S.Half case, don't test with "==" here.
|
||
|
return '%s/%s' % (self._print(S.One),
|
||
|
self.parenthesize(expr.base, PREC, strict=False))
|
||
|
|
||
|
e = self.parenthesize(expr.exp, PREC, strict=False)
|
||
|
if self.printmethod == '_sympyrepr' and expr.exp.is_Rational and expr.exp.q != 1:
|
||
|
# the parenthesized exp should be '(Rational(a, b))' so strip parens,
|
||
|
# but just check to be sure.
|
||
|
if e.startswith('(Rational'):
|
||
|
return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False), e[1:-1])
|
||
|
return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False), e)
|
||
|
|
||
|
def _print_UnevaluatedExpr(self, expr):
|
||
|
return self._print(expr.args[0])
|
||
|
|
||
|
def _print_MatPow(self, expr):
|
||
|
PREC = precedence(expr)
|
||
|
return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False),
|
||
|
self.parenthesize(expr.exp, PREC, strict=False))
|
||
|
|
||
|
def _print_Integer(self, expr):
|
||
|
if self._settings.get("sympy_integers", False):
|
||
|
return "S(%s)" % (expr)
|
||
|
return str(expr.p)
|
||
|
|
||
|
def _print_Integers(self, expr):
|
||
|
return 'Integers'
|
||
|
|
||
|
def _print_Naturals(self, expr):
|
||
|
return 'Naturals'
|
||
|
|
||
|
def _print_Naturals0(self, expr):
|
||
|
return 'Naturals0'
|
||
|
|
||
|
def _print_Rationals(self, expr):
|
||
|
return 'Rationals'
|
||
|
|
||
|
def _print_Reals(self, expr):
|
||
|
return 'Reals'
|
||
|
|
||
|
def _print_Complexes(self, expr):
|
||
|
return 'Complexes'
|
||
|
|
||
|
def _print_EmptySet(self, expr):
|
||
|
return 'EmptySet'
|
||
|
|
||
|
def _print_EmptySequence(self, expr):
|
||
|
return 'EmptySequence'
|
||
|
|
||
|
def _print_int(self, expr):
|
||
|
return str(expr)
|
||
|
|
||
|
def _print_mpz(self, expr):
|
||
|
return str(expr)
|
||
|
|
||
|
def _print_Rational(self, expr):
|
||
|
if expr.q == 1:
|
||
|
return str(expr.p)
|
||
|
else:
|
||
|
if self._settings.get("sympy_integers", False):
|
||
|
return "S(%s)/%s" % (expr.p, expr.q)
|
||
|
return "%s/%s" % (expr.p, expr.q)
|
||
|
|
||
|
def _print_PythonRational(self, expr):
|
||
|
if expr.q == 1:
|
||
|
return str(expr.p)
|
||
|
else:
|
||
|
return "%d/%d" % (expr.p, expr.q)
|
||
|
|
||
|
def _print_Fraction(self, expr):
|
||
|
if expr.denominator == 1:
|
||
|
return str(expr.numerator)
|
||
|
else:
|
||
|
return "%s/%s" % (expr.numerator, expr.denominator)
|
||
|
|
||
|
def _print_mpq(self, expr):
|
||
|
if expr.denominator == 1:
|
||
|
return str(expr.numerator)
|
||
|
else:
|
||
|
return "%s/%s" % (expr.numerator, expr.denominator)
|
||
|
|
||
|
def _print_Float(self, expr):
|
||
|
prec = expr._prec
|
||
|
if prec < 5:
|
||
|
dps = 0
|
||
|
else:
|
||
|
dps = prec_to_dps(expr._prec)
|
||
|
if self._settings["full_prec"] is True:
|
||
|
strip = False
|
||
|
elif self._settings["full_prec"] is False:
|
||
|
strip = True
|
||
|
elif self._settings["full_prec"] == "auto":
|
||
|
strip = self._print_level > 1
|
||
|
low = self._settings["min"] if "min" in self._settings else None
|
||
|
high = self._settings["max"] if "max" in self._settings else None
|
||
|
rv = mlib_to_str(expr._mpf_, dps, strip_zeros=strip, min_fixed=low, max_fixed=high)
|
||
|
if rv.startswith('-.0'):
|
||
|
rv = '-0.' + rv[3:]
|
||
|
elif rv.startswith('.0'):
|
||
|
rv = '0.' + rv[2:]
|
||
|
if rv.startswith('+'):
|
||
|
# e.g., +inf -> inf
|
||
|
rv = rv[1:]
|
||
|
return rv
|
||
|
|
||
|
def _print_Relational(self, expr):
|
||
|
|
||
|
charmap = {
|
||
|
"==": "Eq",
|
||
|
"!=": "Ne",
|
||
|
":=": "Assignment",
|
||
|
'+=': "AddAugmentedAssignment",
|
||
|
"-=": "SubAugmentedAssignment",
|
||
|
"*=": "MulAugmentedAssignment",
|
||
|
"/=": "DivAugmentedAssignment",
|
||
|
"%=": "ModAugmentedAssignment",
|
||
|
}
|
||
|
|
||
|
if expr.rel_op in charmap:
|
||
|
return '%s(%s, %s)' % (charmap[expr.rel_op], self._print(expr.lhs),
|
||
|
self._print(expr.rhs))
|
||
|
|
||
|
return '%s %s %s' % (self.parenthesize(expr.lhs, precedence(expr)),
|
||
|
self._relationals.get(expr.rel_op) or expr.rel_op,
|
||
|
self.parenthesize(expr.rhs, precedence(expr)))
|
||
|
|
||
|
def _print_ComplexRootOf(self, expr):
|
||
|
return "CRootOf(%s, %d)" % (self._print_Add(expr.expr, order='lex'),
|
||
|
expr.index)
|
||
|
|
||
|
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))
|
||
|
|
||
|
return "RootSum(%s)" % ", ".join(args)
|
||
|
|
||
|
def _print_GroebnerBasis(self, basis):
|
||
|
cls = basis.__class__.__name__
|
||
|
|
||
|
exprs = [self._print_Add(arg, order=basis.order) for arg in basis.exprs]
|
||
|
exprs = "[%s]" % ", ".join(exprs)
|
||
|
|
||
|
gens = [ self._print(gen) for gen in basis.gens ]
|
||
|
domain = "domain='%s'" % self._print(basis.domain)
|
||
|
order = "order='%s'" % self._print(basis.order)
|
||
|
|
||
|
args = [exprs] + gens + [domain, order]
|
||
|
|
||
|
return "%s(%s)" % (cls, ", ".join(args))
|
||
|
|
||
|
def _print_set(self, s):
|
||
|
items = sorted(s, key=default_sort_key)
|
||
|
|
||
|
args = ', '.join(self._print(item) for item in items)
|
||
|
if not args:
|
||
|
return "set()"
|
||
|
return '{%s}' % args
|
||
|
|
||
|
def _print_FiniteSet(self, s):
|
||
|
from sympy.sets.sets import FiniteSet
|
||
|
items = sorted(s, key=default_sort_key)
|
||
|
|
||
|
args = ', '.join(self._print(item) for item in items)
|
||
|
if any(item.has(FiniteSet) for item in items):
|
||
|
return 'FiniteSet({})'.format(args)
|
||
|
return '{{{}}}'.format(args)
|
||
|
|
||
|
def _print_Partition(self, s):
|
||
|
items = sorted(s, key=default_sort_key)
|
||
|
|
||
|
args = ', '.join(self._print(arg) for arg in items)
|
||
|
return 'Partition({})'.format(args)
|
||
|
|
||
|
def _print_frozenset(self, s):
|
||
|
if not s:
|
||
|
return "frozenset()"
|
||
|
return "frozenset(%s)" % self._print_set(s)
|
||
|
|
||
|
def _print_Sum(self, expr):
|
||
|
def _xab_tostr(xab):
|
||
|
if len(xab) == 1:
|
||
|
return self._print(xab[0])
|
||
|
else:
|
||
|
return self._print((xab[0],) + tuple(xab[1:]))
|
||
|
L = ', '.join([_xab_tostr(l) for l in expr.limits])
|
||
|
return 'Sum(%s, %s)' % (self._print(expr.function), L)
|
||
|
|
||
|
def _print_Symbol(self, expr):
|
||
|
return expr.name
|
||
|
_print_MatrixSymbol = _print_Symbol
|
||
|
_print_RandomSymbol = _print_Symbol
|
||
|
|
||
|
def _print_Identity(self, expr):
|
||
|
return "I"
|
||
|
|
||
|
def _print_ZeroMatrix(self, expr):
|
||
|
return "0"
|
||
|
|
||
|
def _print_OneMatrix(self, expr):
|
||
|
return "1"
|
||
|
|
||
|
def _print_Predicate(self, expr):
|
||
|
return "Q.%s" % expr.name
|
||
|
|
||
|
def _print_str(self, expr):
|
||
|
return str(expr)
|
||
|
|
||
|
def _print_tuple(self, expr):
|
||
|
if len(expr) == 1:
|
||
|
return "(%s,)" % self._print(expr[0])
|
||
|
else:
|
||
|
return "(%s)" % self.stringify(expr, ", ")
|
||
|
|
||
|
def _print_Tuple(self, expr):
|
||
|
return self._print_tuple(expr)
|
||
|
|
||
|
def _print_Transpose(self, T):
|
||
|
return "%s.T" % self.parenthesize(T.arg, PRECEDENCE["Pow"])
|
||
|
|
||
|
def _print_Uniform(self, expr):
|
||
|
return "Uniform(%s, %s)" % (self._print(expr.a), self._print(expr.b))
|
||
|
|
||
|
def _print_Quantity(self, expr):
|
||
|
if self._settings.get("abbrev", False):
|
||
|
return "%s" % expr.abbrev
|
||
|
return "%s" % expr.name
|
||
|
|
||
|
def _print_Quaternion(self, expr):
|
||
|
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_Dimension(self, expr):
|
||
|
return str(expr)
|
||
|
|
||
|
def _print_Wild(self, expr):
|
||
|
return expr.name + '_'
|
||
|
|
||
|
def _print_WildFunction(self, expr):
|
||
|
return expr.name + '_'
|
||
|
|
||
|
def _print_WildDot(self, expr):
|
||
|
return expr.name
|
||
|
|
||
|
def _print_WildPlus(self, expr):
|
||
|
return expr.name
|
||
|
|
||
|
def _print_WildStar(self, expr):
|
||
|
return expr.name
|
||
|
|
||
|
def _print_Zero(self, expr):
|
||
|
if self._settings.get("sympy_integers", False):
|
||
|
return "S(0)"
|
||
|
return "0"
|
||
|
|
||
|
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
|
||
|
|
||
|
cls = p.__class__.__name__
|
||
|
rep = self._print(p.rep)
|
||
|
dom = self._print(p.dom)
|
||
|
ring = self._print(p.ring)
|
||
|
|
||
|
return "%s(%s, %s, %s)" % (cls, rep, dom, ring)
|
||
|
|
||
|
def _print_DMF(self, expr):
|
||
|
return self._print_DMP(expr)
|
||
|
|
||
|
def _print_Object(self, obj):
|
||
|
return 'Object("%s")' % obj.name
|
||
|
|
||
|
def _print_IdentityMorphism(self, morphism):
|
||
|
return 'IdentityMorphism(%s)' % morphism.domain
|
||
|
|
||
|
def _print_NamedMorphism(self, morphism):
|
||
|
return 'NamedMorphism(%s, %s, "%s")' % \
|
||
|
(morphism.domain, morphism.codomain, morphism.name)
|
||
|
|
||
|
def _print_Category(self, category):
|
||
|
return 'Category("%s")' % category.name
|
||
|
|
||
|
def _print_Manifold(self, manifold):
|
||
|
return manifold.name.name
|
||
|
|
||
|
def _print_Patch(self, patch):
|
||
|
return patch.name.name
|
||
|
|
||
|
def _print_CoordSystem(self, coords):
|
||
|
return coords.name.name
|
||
|
|
||
|
def _print_BaseScalarField(self, field):
|
||
|
return field._coord_sys.symbols[field._index].name
|
||
|
|
||
|
def _print_BaseVectorField(self, field):
|
||
|
return 'e_%s' % field._coord_sys.symbols[field._index].name
|
||
|
|
||
|
def _print_Differential(self, diff):
|
||
|
field = diff._form_field
|
||
|
if hasattr(field, '_coord_sys'):
|
||
|
return 'd%s' % field._coord_sys.symbols[field._index].name
|
||
|
else:
|
||
|
return 'd(%s)' % self._print(field)
|
||
|
|
||
|
def _print_Tr(self, expr):
|
||
|
#TODO : Handle indices
|
||
|
return "%s(%s)" % ("Tr", self._print(expr.args[0]))
|
||
|
|
||
|
def _print_Str(self, s):
|
||
|
return self._print(s.name)
|
||
|
|
||
|
def _print_AppliedBinaryRelation(self, expr):
|
||
|
rel = expr.function
|
||
|
return '%s(%s, %s)' % (self._print(rel),
|
||
|
self._print(expr.lhs),
|
||
|
self._print(expr.rhs))
|
||
|
|
||
|
|
||
|
@print_function(StrPrinter)
|
||
|
def sstr(expr, **settings):
|
||
|
"""Returns the expression as a string.
|
||
|
|
||
|
For large expressions where speed is a concern, use the setting
|
||
|
order='none'. If abbrev=True setting is used then units are printed in
|
||
|
abbreviated form.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy import symbols, Eq, sstr
|
||
|
>>> a, b = symbols('a b')
|
||
|
>>> sstr(Eq(a + b, 0))
|
||
|
'Eq(a + b, 0)'
|
||
|
"""
|
||
|
|
||
|
p = StrPrinter(settings)
|
||
|
s = p.doprint(expr)
|
||
|
|
||
|
return s
|
||
|
|
||
|
|
||
|
class StrReprPrinter(StrPrinter):
|
||
|
"""(internal) -- see sstrrepr"""
|
||
|
|
||
|
def _print_str(self, s):
|
||
|
return repr(s)
|
||
|
|
||
|
def _print_Str(self, s):
|
||
|
# Str does not to be printed same as str here
|
||
|
return "%s(%s)" % (s.__class__.__name__, self._print(s.name))
|
||
|
|
||
|
|
||
|
@print_function(StrReprPrinter)
|
||
|
def sstrrepr(expr, **settings):
|
||
|
"""return expr in mixed str/repr form
|
||
|
|
||
|
i.e. strings are returned in repr form with quotes, and everything else
|
||
|
is returned in str form.
|
||
|
|
||
|
This function could be useful for hooking into sys.displayhook
|
||
|
"""
|
||
|
|
||
|
p = StrReprPrinter(settings)
|
||
|
s = p.doprint(expr)
|
||
|
|
||
|
return s
|