Search code examples
pythonjupyter-notebooksympymathjaxpretty-print

How do I extend SymPy pretty printing for new structures in Jupyter notebook?


Note: This is a duplicate of this question in Math Stack Exchange. I had to first pose the question in Math StackExchange because StackOverflow doesn't have MathJax. However, almost all SymPy questions are on StackOverflow. Please refer to the Math Stack Exchange version for the typesetting of the desired output. An editor on Math StackExchange suggested I cross-post it here.

In Jupyter notebook if we execute this code:

import sympy as sp
sp.init_printing()
x,y=sp.symbols('x,y')
x**2+sp.sin(y)

We will get a nice output, with no further coding, due to SymPy's pretty printing process, that looks like

Pretty formula

Now suppose we do:

class MetricSpace:
    def __init__(self, M, d):
        self.M = M
        self.d = d
        
    def __repr__(self):
        return f"$({self.M}, {self.d})$ {self.__class__.__name__}"

Re,d=sp.symbols(r'\Re,d')

MetricSpace(Re,d)

Then the output we get is

ugly formula

If we do instead

from IPython.core.display import Markdown
Markdown(repr(MetricSpace(Re,d)))

then I get the desired output, which looks like

Desired formatting

How do we code the above so that SymPy's pretty printer provides the desired output in Jupyter notebook without having to wrap it in Markdown(repr(...))?


Solution

  • Here is a snippet that functions correctly. It might not do quite all the things you want yet, but hopefully it will start you off.

    import sympy as sp
    sp.init_printing()
    
    class MetricSpace(sp.Expr):
        def __init__(self, M, d):
            self.M = M
            self.d = d
    
        def _latex(self, printer=None):
            return f"({printer.doprint(self.M)}, {printer.doprint(self.d)})\\ \\text{{{self.__class__.__name__}}}"
    
    Re, d = sp.symbols(r'\Re,d')
    
    MetricSpace(Re, d)
    

    See here for more information on the Sympy printing system and how to hook into it. The key things to note is that you should subclass Expr if you want your object to come into the domain of Sympy's pretty printing, and that the latex printer calls the _latex method to get its LaTeX. It doesn't need to be wrapped in dollar signs, and you should use printer.doprint to get the LaTeX of nested expressions.

    Here is a screenshot of what this produces on my notebook:

    screenshot

    PS: I'm intrigued by a metric space with underlying set denoted by "\Re". If you mean the reals, might I suggest using sp.Reals?