Clayton Geist
Clayton Geist

Reputation: 462

How do you Convert Sympy's "Mod()" to Python's Syntax of "%"?

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

Answers (1)

Tadhg McDonald-Jensen
Tadhg McDonald-Jensen

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

Related Questions