Reputation: 462
I was wondering how I would go about converting Sympy's "Mod()" to Python's default syntax of the percent sign (%). I cannot find any "built-in" function (in the Sympy package). I could write one myself, except I want the result to be simplified. In the function that I would create, I've realized that I don't know how to avoid adding extra parentheses. Since I want it in its most simplified form, I would need to use Sympy's simplification. And that would then undo the conversion of "Mod()" to "%". So if there isn't an easy way to convert Sympy's Mod to Python's "%", please tell me there's a way in Sympy to simplify only parentheses. I've looked, and I haven't been able to find that either. I at least want a way to remove redundant parentheses without converting "%" back to "Mod".
Edit:
Here's an example, I want:
Mod(x + 1, 3)
to become:
(x+1)%3
But I don't want:
Mod(x, 3)
to be:
(x)%3
I want it to be:
x%3
Upvotes: 0
Views: 579
Reputation: 21453
Now that I have done a bit of looking into sympy's options for custom printing it looks like it is quite easy to implement:
from sympy.printing.precedence import precedence
from sympy.printing.str import StrPrinter
class MyPrinter(StrPrinter):
def _print_Mod(self,expr):
own_level = precedence(expr)
args = (self.parenthesize(arg, own_level) for arg in expr.args)
return "{} % {}".format(*args)
The printer
's method parenthesize
takes an expression and a "level" where the level represents the precedence threshold that causes it to actually add brackets around the argument, so it will only put parentheses around operations that need them:
from sympy.abc import x
printer = MyPrinter()
def test(expr):
print(printer.doprint(expr))
>>> test(x%3)
x % 3
>>> test((x+1)%3)
(x + 1) % 3
From my (extremely limited) testing seems to suggest this works fine, also see the note below about own_level
.
as shown in the documentation for overriding printing, you can just override Basic.__str__
to use your custom printer when just printing out expressions:
from sympy import Basic
Basic.__str__ = lambda expr, printer=MyPrinter(): printer.doprint(expr)
>>> print(x%3)
x % 3
as a side note - the reason Mod
doesn't / can't do this by default is because it apparently inherits from Function
:
>>> issubclass(sympy.Mod, sympy.Function)
True
This is why it displays as a function call in pretty much every printing method there is, this also means that modulus has the same precedence as functions:
>>> precedence(x%3)
70
>>> f = sympy.Function("f")
>>> precedence(f(x))
70
>>> print( f(x)%x ) #so it thinks f(x) needs parens here
(f(x)) % x
if you find an appropriate precedence to use for own_level
please share it with me as I would love to update my answer.
Upvotes: 1