#!/usr/bin/env python # coding: utf-8 # # Table of Contents # * [1. Demonstration of the ``numpy.polynomial`` package](#1.-Demonstration-of-the-numpy.polynomial-package) # * [1.1 And especially a small hand-made pretty printing function for ``Polynomial`` objects](#1.1-And-especially-a-small-hand-made-pretty-printing-function-for-Polynomial-objects) # * [1.2 First goal: pretty print in ASCII text](#1.2-First-goal:-pretty-print-in-ASCII-text) # * [1.3 Second goal: pretty-print in $\LaTeX{}$ code](#1.3-Second-goal:-pretty-print-in-$\LaTeX{}$-code) # * [1.4 A bonus for the end: add this pretty-printer as the default one in IPython:](#1.4-A-bonus-for-the-end:-add-this-pretty-printer-as-the-default-one-in-IPython:) # # # 1. Demonstration of the ``numpy.polynomial`` package # ## 1.1 And especially a small hand-made pretty printing function for ``Polynomial`` objects # For both [Python 2](https://docs.python.org/2/) and [Python 3](https://docs.python.org/3/), [numpy](https://numpy.org) has a very nice module to work with [polynomials](https://en.wikipedia.org/wiki/Polynomial): [numpy.polynomial](https://docs.scipy.org/doc/numpy/reference/routines.polynomials.classes.html). # # If you are not familiar with it, I **highly** recommend you to read [this tutorial](https://docs.scipy.org/doc/numpy/reference/routines.polynomials.classes.html#basics). # # > - Note: this small tutorial was inspired by [this question asked on StackOverflow](https://stackoverflow.com/questions/28646336/pretty-printing-polynomials-in-ipython-notebook/37510036#37510036) (and by my answer). # > - This [Jupyter Notebook](http://jupyter.org/) was written by [Lilian Besson](https://github.com/Naereen/). # ---- # # Now, let assume you already know everything about Python, and the ``numpy.polynomial`` package. # So you know that it should be imported like this: # In[1]: from numpy.polynomial import Polynomial as P # And we can then define the monome $X$ as ``P([0, 1])``, defined by the list of its coefficients in the *canonical* basis $(X_i)_{i \in \mathbb{N}}$: # In[2]: X = P([0, 1]) print("We defined the monome X =", X) X # The main issue with this output (either ``poly([ 0. 1.])`` or ``Polynomial([ 0., 1.], [-1, 1], [-1, 1])``) is its lack of *sexyness*: it gives the required information (coefficients, domain, window etc, see [here](https://docs.scipy.org/doc/numpy/reference/generated/numpy.polynomial.polynomial.Polynomial.html#numpy.polynomial.polynomial.Polynomial) for more details) but it is too far from the mathematical notation. # # If we define $Q(X) = 1 + 17 X^3$, we would like Python (or IPython, or in this case, the Jupyter Notebook) to display this polynomial nicely, either as ASCII text: ``1 + 17 * X**3`` (valid Python code), or as a nice $\LaTeX{}$ code: ``1 + 17 X^3``. # In[3]: Q = 1 + 17 * X ** 3 print("Q(X) =", Q) Q # ---- # ## 1.2 First goal: pretty print in ASCII text # Our first task will be to implement a small function, or an overload of the [numpy.polynomial.Polynomial](https://docs.scipy.org/doc/numpy/reference/generated/numpy.polynomial.polynomial.Polynomial.html) class to be able to pretty-print a ``Polynomial`` object nicely. # # > Note: more details, along with a documentation, some examples and a fully [pylint](https://www.pylint.org/)-compatible code are available on this [Bitbucket snippet](https://bitbucket.org/snippets/): [bitbucket.org/snippets/lbesson/j6dpz](https://bitbucket.org/snippets/lbesson/j6dpz#file-nicedisplay_numpy_polynomial_Polynomial.py). # The function ``prettyprintPolynomial`` defined below implements a natural strategy to print a polynomial: it prints the coefficients, as ``a_i * X**i``, in increasing order. # # This function takes care of all the special cases: # # - it removes a trailing ``.0`` if one coefficient is an integer, # - it displays the coefficient between ``(`` and ``)`` if it is negative, # - it only displays the non-zero coefficients, # - if one coefficient is ``1``, no need to display it. # In[4]: def prettyprintPolynomial(p): """ Small function to print nicely the polynomial p as we write it in maths, in ASCII text.""" coefs = p.coef # List of coefficient, sorted by increasing degrees res = "" # The resulting string for i, a in enumerate(coefs): if int(a) == a: # Remove the trailing .0 a = int(a) if i == 0: # First coefficient, no need for X if a > 0: res += "{a} + ".format(a=a) elif a < 0: # Negative a is printed like (a) res += "({a}) + ".format(a=a) # a = 0 is not displayed elif i == 1: # Second coefficient, only X and not X**i if a == 1: # a = 1 does not need to be displayed res += "X + " elif a > 0: res += "{a} * X + ".format(a=a) elif a < 0: res += "({a}) * X + ".format(a=a) else: if a == 1: res += "X**{i} + ".format(i=i) elif a > 0: res += "{a} * X**{i} + ".format(a=a, i=i) elif a < 0: res += "({a}) * X**{i} + ".format(a=a, i=i) return res[:-3] if res else "" # We can check its behavior on some small polynomials: # In[5]: print("X =", prettyprintPolynomial(X)) print("Q(X) =", prettyprintPolynomial(Q)) # In[6]: Q3 = -1 - 2*X - 17*X**3 print("Q_3(X) =", prettyprintPolynomial(Q3)) print("- Q_3(X) =", prettyprintPolynomial(-Q3)) # We can create a more complicated polynomial $Q_4(X) = (1 + 2 X + 17 X^3) ^ {12}$ and check that it is also nicely printed: # In[7]: Q4 = (1 + 2*X + 17*X**3) ** 12 print("Q_4(X) =", prettyprintPolynomial(Q4)) # ---- # ## 1.3 Second goal: pretty-print in $\LaTeX{}$ code # We will simply modify the function ``prettyprintPolynomial`` to use $\LaTeX{}$ code instead of ASCII text: the string has to be between ``$``, the multiplications are without symbols (e.g., $17 X$ for ``17 * X``), and the powers are with the ``^`` symbol instead of ``**`` : # In[8]: def Polynomial_to_LaTeX(p): """ Small function to print nicely the polynomial p as we write it in maths, in LaTeX code.""" coefs = p.coef # List of coefficient, sorted by increasing degrees res = "" # The resulting string for i, a in enumerate(coefs): if int(a) == a: # Remove the trailing .0 a = int(a) if i == 0: # First coefficient, no need for X if a > 0: res += "{a} + ".format(a=a) elif a < 0: # Negative a is printed like (a) res += "({a}) + ".format(a=a) # a = 0 is not displayed elif i == 1: # Second coefficient, only X and not X**i if a == 1: # a = 1 does not need to be displayed res += "X + " elif a > 0: res += "{a} \;X + ".format(a=a) elif a < 0: res += "({a}) \;X + ".format(a=a) else: if a == 1: # A special care needs to be addressed to put the exponent in {..} in LaTeX res += "X^{i} + ".format(i="{%d}" % i) elif a > 0: res += "{a} \;X^{i} + ".format(a=a, i="{%d}" % i) elif a < 0: res += "({a}) \;X^{i} + ".format(a=a, i="{%d}" % i) return "$" + res[:-3] + "$" if res else "" # We can quickly try the same examples as before: # In[9]: print("X =", Polynomial_to_LaTeX(X)) print("Q(X) =", Polynomial_to_LaTeX(Q)) # But we want the $\LaTeX{}$ code to be pretty-printed by IPython, not just displayed like this. # For this, the internal function ``Latex`` from the module ``IPython.display`` is required: # In[10]: from IPython.display import Latex # In[11]: print("X =") Latex(Polynomial_to_LaTeX(X)) # In[12]: print("Q(X) =") Latex(Polynomial_to_LaTeX(Q)) # Allright! It starts to look like what we wanted! # Let's try with a bigger polynomial, as we did before: # In[13]: Q4 = (1 + 2*X + 17*X**3) ** 12 print("Q_4(X) =") Latex(Polynomial_to_LaTeX(Q4)) # Way nicer! Yay! # ---- # ## 1.4 A bonus for the end: add this pretty-printer as the default one in IPython: # This manipulation is showed in [IPython's documentation](https://nbviewer.jupyter.org/github/ipython/ipython/blob/3607712653c66d63e0d7f13f073bde8c0f209ba8/docs/examples/notebooks/display_protocol.ipynb). # # But we can configure IPython to do this automatically for us as follows. # We hook into the IPython display system and instruct it to use ``Polynomial_to_LaTeX`` for the ``latex`` mimetype, when encountering objects of the ``Polynomial`` type defined in the ``numpy.polynomial.polynomial`` module: # In[14]: ip = get_ipython() latex_formatter = ip.display_formatter.formatters['text/latex'] latex_formatter.for_type_by_name('numpy.polynomial.polynomial', 'Polynomial', Polynomial_to_LaTeX) # Once our special printer has been loaded, all polynomials will be represented by their mathematical form instead (as $\LaTeX{}$ code displayed with [MathJax](https://www.mathjax.org/)): # In[15]: X # One a last example: # In[23]: P = ((1 + X**2)**16)**16 % (1 - X**16) print("str(P) =", str(P)) print("repr(P) =", repr(P)) P # *That's all for today, folks!*