{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Table of Contents\n", "* [1. Demonstration of the ``numpy.polynomial`` package](#1.-Demonstration-of-the-numpy.polynomial-package)\n", "\t* [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)\n", "\t* [1.2 First goal: pretty print in ASCII text](#1.2-First-goal:-pretty-print-in-ASCII-text)\n", "\t* [1.3 Second goal: pretty-print in $\\LaTeX{}$ code](#1.3-Second-goal:-pretty-print-in-$\\LaTeX{}$-code)\n", "\t* [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:)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Demonstration of the ``numpy.polynomial`` package" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.1 And especially a small hand-made pretty printing function for ``Polynomial`` objects" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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).\n", "\n", "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).\n", "\n", "> - 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).\n", "> - This [Jupyter Notebook](http://jupyter.org/) was written by [Lilian Besson](https://github.com/Naereen/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "\n", "Now, let assume you already know everything about Python, and the ``numpy.polynomial`` package.\n", "So you know that it should be imported like this:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from numpy.polynomial import Polynomial as P" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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}}$:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "We defined the monome X = poly([ 0. 1.])\n" ] }, { "data": { "text/plain": [ "Polynomial([ 0., 1.], [-1., 1.], [-1., 1.])" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X = P([0, 1])\n", "print(\"We defined the monome X =\", X)\n", "X" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "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``." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Q(X) = poly([ 1. 0. 0. 17.])\n" ] }, { "data": { "text/plain": [ "Polynomial([ 1., 0., 0., 17.], [-1., 1.], [-1., 1.])" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q = 1 + 17 * X ** 3\n", "print(\"Q(X) =\", Q)\n", "Q" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.2 First goal: pretty print in ASCII text" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "> 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)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "This function takes care of all the special cases:\n", "\n", "- it removes a trailing ``.0`` if one coefficient is an integer,\n", "- it displays the coefficient between ``(`` and ``)`` if it is negative,\n", "- it only displays the non-zero coefficients,\n", "- if one coefficient is ``1``, no need to display it." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def prettyprintPolynomial(p):\n", " \"\"\" Small function to print nicely the polynomial p as we write it in maths, in ASCII text.\"\"\"\n", " coefs = p.coef # List of coefficient, sorted by increasing degrees\n", " res = \"\" # The resulting string\n", " for i, a in enumerate(coefs):\n", " if int(a) == a: # Remove the trailing .0\n", " a = int(a)\n", " if i == 0: # First coefficient, no need for X\n", " if a > 0:\n", " res += \"{a} + \".format(a=a)\n", " elif a < 0: # Negative a is printed like (a)\n", " res += \"({a}) + \".format(a=a)\n", " # a = 0 is not displayed \n", " elif i == 1: # Second coefficient, only X and not X**i\n", " if a == 1: # a = 1 does not need to be displayed\n", " res += \"X + \"\n", " elif a > 0:\n", " res += \"{a} * X + \".format(a=a)\n", " elif a < 0:\n", " res += \"({a}) * X + \".format(a=a)\n", " else:\n", " if a == 1:\n", " res += \"X**{i} + \".format(i=i)\n", " elif a > 0:\n", " res += \"{a} * X**{i} + \".format(a=a, i=i)\n", " elif a < 0:\n", " res += \"({a}) * X**{i} + \".format(a=a, i=i)\n", " return res[:-3] if res else \"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can check its behavior on some small polynomials:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X = X\n", "Q(X) = 1 + 17 * X**3\n" ] } ], "source": [ "print(\"X =\", prettyprintPolynomial(X))\n", "print(\"Q(X) =\", prettyprintPolynomial(Q))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Q_3(X) = (-1) + (-2) * X + (-17) * X**3\n", "- Q_3(X) = 1 + 2 * X + 17 * X**3\n" ] } ], "source": [ "Q3 = -1 - 2*X - 17*X**3\n", "print(\"Q_3(X) =\", prettyprintPolynomial(Q3))\n", "print(\"- Q_3(X) =\", prettyprintPolynomial(-Q3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Q_4(X) = 1 + 24 * X + 264 * X**2 + 1964 * X**3 + 12408 * X**4 + 70224 * X**5 + 347490 * X**6 + 1559976 * X**7 + 6575976 * X**8 + 25536412 * X**9 + 92228664 * X**10 + 318098112 * X**11 + 1029784111 * X**12 + 3135774576 * X**13 + 9208582032 * X**14 + 25554505944 * X**15 + 67047551472 * X**16 + 171037015776 * X**17 + 411810470236 * X**18 + 939707466192 * X**19 + 2104452578448 * X**20 + 4397274411288 * X**21 + 8746569014832 * X**22 + 17281727001792 * X**23 + 30879457534959 * X**24 + 53623057787640 * X**25 + 93271621727592 * X**26 + 136585330694780 * X**27 + 211783995908760 * X**28 + 313071993952080 * X**29 + 341770260064354 * X**30 + 532222389718536 * X**31 + 532222389718536 * X**32 + 411262755691596 * X**33 + 822525511383192 * X**34 + 582622237229761 * X**36\n" ] } ], "source": [ "Q4 = (1 + 2*X + 17*X**3) ** 12\n", "print(\"Q_4(X) =\", prettyprintPolynomial(Q4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.3 Second goal: pretty-print in $\\LaTeX{}$ code" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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 ``**`` :" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def Polynomial_to_LaTeX(p):\n", " \"\"\" Small function to print nicely the polynomial p as we write it in maths, in LaTeX code.\"\"\"\n", " coefs = p.coef # List of coefficient, sorted by increasing degrees\n", " res = \"\" # The resulting string\n", " for i, a in enumerate(coefs):\n", " if int(a) == a: # Remove the trailing .0\n", " a = int(a)\n", " if i == 0: # First coefficient, no need for X\n", " if a > 0:\n", " res += \"{a} + \".format(a=a)\n", " elif a < 0: # Negative a is printed like (a)\n", " res += \"({a}) + \".format(a=a)\n", " # a = 0 is not displayed \n", " elif i == 1: # Second coefficient, only X and not X**i\n", " if a == 1: # a = 1 does not need to be displayed\n", " res += \"X + \"\n", " elif a > 0:\n", " res += \"{a} \\;X + \".format(a=a)\n", " elif a < 0:\n", " res += \"({a}) \\;X + \".format(a=a)\n", " else:\n", " if a == 1:\n", " # A special care needs to be addressed to put the exponent in {..} in LaTeX\n", " res += \"X^{i} + \".format(i=\"{%d}\" % i)\n", " elif a > 0:\n", " res += \"{a} \\;X^{i} + \".format(a=a, i=\"{%d}\" % i)\n", " elif a < 0:\n", " res += \"({a}) \\;X^{i} + \".format(a=a, i=\"{%d}\" % i)\n", " return \"$\" + res[:-3] + \"$\" if res else \"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can quickly try the same examples as before:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X = $X$\n", "Q(X) = $1 + 17 \\;X^{3}$\n" ] } ], "source": [ "print(\"X =\", Polynomial_to_LaTeX(X))\n", "print(\"Q(X) =\", Polynomial_to_LaTeX(Q))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But we want the $\\LaTeX{}$ code to be pretty-printed by IPython, not just displayed like this.\n", "For this, the internal function ``Latex`` from the module ``IPython.display`` is required:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from IPython.display import Latex" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X =\n" ] }, { "data": { "text/latex": [ "$X$" ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(\"X =\")\n", "Latex(Polynomial_to_LaTeX(X))" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Q(X) =\n" ] }, { "data": { "text/latex": [ "$1 + 17 \\;X^{3}$" ], "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(\"Q(X) =\")\n", "Latex(Polynomial_to_LaTeX(Q))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Allright! It starts to look like what we wanted!\n", "Let's try with a bigger polynomial, as we did before:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Q_4(X) =\n" ] }, { "data": { "text/latex": [ "$1 + 24 \\;X + 264 \\;X^{2} + 1964 \\;X^{3} + 12408 \\;X^{4} + 70224 \\;X^{5} + 347490 \\;X^{6} + 1559976 \\;X^{7} + 6575976 \\;X^{8} + 25536412 \\;X^{9} + 92228664 \\;X^{10} + 318098112 \\;X^{11} + 1029784111 \\;X^{12} + 3135774576 \\;X^{13} + 9208582032 \\;X^{14} + 25554505944 \\;X^{15} + 67047551472 \\;X^{16} + 171037015776 \\;X^{17} + 411810470236 \\;X^{18} + 939707466192 \\;X^{19} + 2104452578448 \\;X^{20} + 4397274411288 \\;X^{21} + 8746569014832 \\;X^{22} + 17281727001792 \\;X^{23} + 30879457534959 \\;X^{24} + 53623057787640 \\;X^{25} + 93271621727592 \\;X^{26} + 136585330694780 \\;X^{27} + 211783995908760 \\;X^{28} + 313071993952080 \\;X^{29} + 341770260064354 \\;X^{30} + 532222389718536 \\;X^{31} + 532222389718536 \\;X^{32} + 411262755691596 \\;X^{33} + 822525511383192 \\;X^{34} + 582622237229761 \\;X^{36}$" ], "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Q4 = (1 + 2*X + 17*X**3) ** 12\n", "print(\"Q_4(X) =\")\n", "Latex(Polynomial_to_LaTeX(Q4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Way nicer! Yay!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.4 A bonus for the end: add this pretty-printer as the default one in IPython:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This manipulation is showed in [IPython's documentation](https://nbviewer.jupyter.org/github/ipython/ipython/blob/3607712653c66d63e0d7f13f073bde8c0f209ba8/docs/examples/notebooks/display_protocol.ipynb).\n", "\n", "But we can configure IPython to do this automatically for us as follows.\n", "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:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ip = get_ipython()\n", "latex_formatter = ip.display_formatter.formatters['text/latex']\n", "latex_formatter.for_type_by_name('numpy.polynomial.polynomial',\n", " 'Polynomial', Polynomial_to_LaTeX)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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/)):" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$X$" ], "text/plain": [ "Polynomial([ 0., 1.], [-1., 1.], [-1., 1.])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One a last example:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "str(P) = poly([ 1.44740112e+76 0.00000000e+00 1.44740112e+76 0.00000000e+00\n", " 1.44740112e+76 0.00000000e+00 1.44740111e+76 0.00000000e+00\n", " 1.44740111e+76 0.00000000e+00 1.44740111e+76 0.00000000e+00\n", " 1.44740112e+76 0.00000000e+00 1.44740112e+76])\n", "repr(P) = Polynomial([ 1.44740112e+76, 0.00000000e+00, 1.44740112e+76,\n", " 0.00000000e+00, 1.44740112e+76, 0.00000000e+00,\n", " 1.44740111e+76, 0.00000000e+00, 1.44740111e+76,\n", " 0.00000000e+00, 1.44740111e+76, 0.00000000e+00,\n", " 1.44740112e+76, 0.00000000e+00, 1.44740112e+76], [-1., 1.], [-1., 1.])\n" ] }, { "data": { "text/latex": [ "$14474011200283702161601714015574787453529301631345998719884624998391009509376 + 14474011186922156948012193183568171432300845296008107824850143024215245193216 \\;X^{2} + 14474011154664527641822461644066539565582932765530275549338185566574811807744 \\;X^{4} + 14474011122406890300942508809613529989054558529239430662811259194970201915392 \\;X^{6} + 14474011109045346694291032236597189509788194535064142289979771003587272900608 \\;X^{8} + 14474011122406895121756641586584356614940835552727238229420240543348707819520 \\;X^{10} + 14474011154664527641822461644066539565582932765530275549338185566574811807744 \\;X^{12} + 14474011186922156948012193183568171432300845296008107824850143024215245193216 \\;X^{14}$" ], "text/plain": [ "Polynomial([ 1.44740112e+76, 0.00000000e+00, 1.44740112e+76,\n", " 0.00000000e+00, 1.44740112e+76, 0.00000000e+00,\n", " 1.44740111e+76, 0.00000000e+00, 1.44740111e+76,\n", " 0.00000000e+00, 1.44740111e+76, 0.00000000e+00,\n", " 1.44740112e+76, 0.00000000e+00, 1.44740112e+76], [-1., 1.], [-1., 1.])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "P = ((1 + X**2)**16)**16 % (1 - X**16)\n", "print(\"str(P) =\", str(P))\n", "print(\"repr(P) =\", repr(P))\n", "P" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*That's all for today, folks!*" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.4.3+" } }, "nbformat": 4, "nbformat_minor": 0 }