numpy.polynomial
package¶Polynomial
objects¶For both Python 2 and Python 3, numpy has a very nice module to work with polynomials: numpy.polynomial.
If you are not familiar with it, I highly recommend you to read this tutorial.
- Note: this small tutorial was inspired by this question asked on StackOverflow (and by my answer).
- This Jupyter Notebook was written by Lilian Besson.
Now, let assume you already know everything about Python, and the numpy.polynomial
package.
So you know that it should be imported like this:
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}}$:
X = P([0, 1])
print("We defined the monome X =", X)
X
We defined the monome X = poly([ 0. 1.])
Polynomial([ 0., 1.], [-1., 1.], [-1., 1.])
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 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
.
Q = 1 + 17 * X ** 3
print("Q(X) =", Q)
Q
Q(X) = poly([ 1. 0. 0. 17.])
Polynomial([ 1., 0., 0., 17.], [-1., 1.], [-1., 1.])
Our first task will be to implement a small function, or an overload of the numpy.polynomial.Polynomial class to be able to pretty-print a Polynomial
object nicely.
Note: more details, along with a documentation, some examples and a fully pylint-compatible code are available on this Bitbucket snippet: bitbucket.org/snippets/lbesson/j6dpz.
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:
.0
if one coefficient is an integer,(
and )
if it is negative,1
, no need to display it.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:
print("X =", prettyprintPolynomial(X))
print("Q(X) =", prettyprintPolynomial(Q))
X = X Q(X) = 1 + 17 * X**3
Q3 = -1 - 2*X - 17*X**3
print("Q_3(X) =", prettyprintPolynomial(Q3))
print("- Q_3(X) =", prettyprintPolynomial(-Q3))
Q_3(X) = (-1) + (-2) * X + (-17) * X**3 - Q_3(X) = 1 + 2 * X + 17 * X**3
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:
Q4 = (1 + 2*X + 17*X**3) ** 12
print("Q_4(X) =", prettyprintPolynomial(Q4))
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
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 **
:
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:
print("X =", Polynomial_to_LaTeX(X))
print("Q(X) =", Polynomial_to_LaTeX(Q))
X = $X$ Q(X) = $1 + 17 \;X^{3}$
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:
from IPython.display import Latex
print("X =")
Latex(Polynomial_to_LaTeX(X))
X =
print("Q(X) =")
Latex(Polynomial_to_LaTeX(Q))
Q(X) =
Allright! It starts to look like what we wanted! Let's try with a bigger polynomial, as we did before:
Q4 = (1 + 2*X + 17*X**3) ** 12
print("Q_4(X) =")
Latex(Polynomial_to_LaTeX(Q4))
Q_4(X) =
Way nicer! Yay!
This manipulation is showed in IPython's documentation.
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:
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):
X
One a last example:
P = ((1 + X**2)**16)**16 % (1 - X**16)
print("str(P) =", str(P))
print("repr(P) =", repr(P))
P
str(P) = poly([ 1.44740112e+76 0.00000000e+00 1.44740112e+76 0.00000000e+00 1.44740112e+76 0.00000000e+00 1.44740111e+76 0.00000000e+00 1.44740111e+76 0.00000000e+00 1.44740111e+76 0.00000000e+00 1.44740112e+76 0.00000000e+00 1.44740112e+76]) repr(P) = Polynomial([ 1.44740112e+76, 0.00000000e+00, 1.44740112e+76, 0.00000000e+00, 1.44740112e+76, 0.00000000e+00, 1.44740111e+76, 0.00000000e+00, 1.44740111e+76, 0.00000000e+00, 1.44740111e+76, 0.00000000e+00, 1.44740112e+76, 0.00000000e+00, 1.44740112e+76], [-1., 1.], [-1., 1.])
That's all for today, folks!