Zeros of the Exponential Function
Tags: Complex Functions , Asymptotics
(Español: Ceros de la función exponencial)
Here is a cool animation I made in 2012.
The animation shows the complex zeros of the Taylor polynomials of the exponential function $e^z$. In this post I will explain what this means, and why it is interesting. I will also show the code in Sage to recreate the animation (which I originaly created in Mathematica in 2012). Unlike Mathematica, which is not free or open source, Sage is a Python-based open source mathematics software system that builds on top of many existing open-source packages.
So, let’s unpack what the animation is and how it is done!
Visualizing complex numbers
Complex numbers are numbers of the form $$2+3i,\quad\text{or}\quad -1+\sqrt{2}i,\quad\text{or}\quad 1-5i$$ where $i=\sqrt{-1}$. One way to think about complex numbers is to think of them as “2D numbers”. The piece without the $i$ tells you the horizontal coordinate (real part) and the piece with the $i$ tells you the vertical coordinate (imaginary part):
(Figure created with TikZ. Source code here.)
The complex exponential function
The complex exponential function $e^z$ is the extension of the exponential function $e^x$, where one allows the input to be a complex number.
For example, with some rounding, $$e^{2-3i}=-7.315-1.0427i.$$
Another example is $$e^{\pi i}=-1.$$ This equality is so surprising that we still call it “Euler’s identity” after the mathematician who discovered it around 1740. Leonard Euler also developed the notation to actually allow us to write it down! You can read more about it here.
To understand the animation, the only thing we need to know about $e^z$ is that it can be written as an infinite sum (technically known as a Taylor series): $$ e^z=1+z+\frac{z^2}{2!}+\frac{z^3}{3!}+\frac{z^4}{4!}+\frac{z^5}{5!}+\ldots $$ Here:
- The $!$ means multiplying by all smaller numbers. For example, $4!=4\times3\times2\times 1$.
- The infinite sum means adding more and more terms to get closer to a value. That value is the sum. Sometimes it works, like for $e^z$ independent of what the value of $z$ is, and sometimes it does not, like for $1+2+3+4+\ldots$.
For example, for $e^z$ when $z=2$, we add more and more terms to get closer and closer to $$e^2=7.3890561\ldots,$$ like this: $$ \begin{aligned} 1 & = 1\\ 1+2 & = 3\\ 1+2+\frac{2^2}{2!} & = 5\\ 1+2+\frac{2^2}{2!}+\frac{2^3}{3!} & = 6.333333…\\ 1+2+\frac{2^2}{2!}+\frac{2^3}{3!}+\frac{2^4}{4!} & = 7\\ 1+2+\frac{2^2}{2!}+\frac{2^3}{3!}+\frac{2^4}{4!}+\frac{2^5}{5!} & = 7.266666666….\\ 1+2+\frac{2^2}{2!}+\frac{2^3}{3!}+\frac{2^4}{4!}+\frac{2^5}{5!} +\ldots+ \frac{2^8}{8!} & = 7.38095238095238…\\ 1+2+\frac{2^2}{2!}+\frac{2^3}{3!}+\frac{2^4}{4!}+\frac{2^5}{5!} +\ldots+ \frac{2^{12}}{12!} & = 7.38904601571268…\\ 1+2+\frac{2^2}{2!}+\frac{2^3}{3!}+\frac{2^4}{4!}+\frac{2^5}{5!} +\ldots+ \frac{2^{20}}{20!} & = 7.38905609893017… \end{aligned} $$
For more details about $e^z$, you can look at the Wikipedia article on Euler’s formula.
The Taylor polynomials of $e^z$ and their zeros
The $n$-th order Taylor polynomial of $e^z$ is given by only summing up to the $z^n$ term in the Taylor series:
$$p_n(z) = 1+z+\frac{z^2}{2!}+\frac{z^3}{3!}+\ldots\frac{z^n}{n!}.$$
By what I explained above, the polynomials $p_n(z)$ become better and better approximations of $e^z$ as $n$ increases.
The polynomial $p_n(z)$ has $n$ complex zeros, which are the solutions of the equation $p_n(z) = 0$. (Any polynomial of degree $n$ has exactly $n$ complex zeros. This fact is so important that it is called “the Fundamental Theorem of Algebra”.)
Here are the zeros for $n=3$ (as expected, we see 3 points – the 3 complex numbers which satisfy $p_3(z)=0$)
And here for $n=5$ (as expected, we see 5 points)
And here for $n=20$!
Interesting! They seem so organized!
How the above figures were made
Here is the Sage code that produces one of these figures for a specific value of $n$.
from sage.plot.colors import blue
# Value of n
n=20
# build p_n(z). Gets stored in f
f=0
for i in range(n+1):
f=f+x^i/factorial(i)
# Find roots of f
roots=f.solve(x,to_poly_solve=True) # to_poly_solve makes Sage use Maxima's solver package to find ALL the roots
numRoots=len(roots) # should be equal to n!
myRoots= [roots[i].right() for i in range(numRoots)]
# Plot the roots of f
myPlot=list_plot(
[(real(i), imag(i)) for i in myRoots],
xmin=-Integer(20),
xmax=Integer(20),
aspect_ratio=Integer(1),
color=blue
)
show(myPlot)
Sage uses Maxima in the background to estimate the roots, which is an open source symbolic manipulator from MIT that has been in development since the 1960s. Finding the $n$ complex zeroes of a polynomial with precision and speed for large values of $n$ requires some clever work!
Saving the image as a png
is simple with the save
command:
myPlot.save("myFilename.png", dpi=200)
The animation
Here is an animation of the zeros of $p_n(z)$ as $n$ increases.
We made it! This is the animation from the beginning of the post, but this time created with Sage (and at a higher resolution).
Why the animation is interesting: $e^z$ has no zeros in the complex plane, but its Taylor polynomials have $n$ zeros.
- How can that be?
- What happens to the zeros?
The animation shows how the zeros escape to infinity in a very orderly manner as $n$ grows.
Producing the animation frames with Sage
The following sage code creates images sagePngs/frame001.png
, sagePngs/frame002.png
, up to sagePngs/frame050.png
, each one showing the zeros of the corresponding Taylor polynomial. These will be the frames of the animation.
from sage.plot.colors import blue
f=0
n=50
folder="sagePngs"
myPlot=Graphics()
for i in range(n+1):
f=f+x^i/factorial(i)
roots=f.solve(x,to_poly_solve=True) # to_poly_solve makes Sage use Maxima's solver package to find ALL the roots
numRoots=len(roots)
myRoots= [roots[i].right() for i in range(numRoots)]
filename=folder+"/"+"frame"+Integer(i).str().zfill(3)+".png"
show("saved: "+filename)
p=list_plot(
[(real(root), imag(root)) for root in myRoots],
xmin=-Integer(20),
xmax=Integer(20),
ymin=-Integer(20),
ymax=Integer(20),
aspect_ratio=Integer(1),
color=blue
)
p.save(filename, dpi=200)
Note that this Sage code to create the frames for the animation is similar to the code for plotting the zeros for a single $n$. We just added code to create a plot and save it as a png in each step of the loop used to build $p_n$.
With those png’s one can produce an animation using Image Magick.
Creating gif with Image Magick
These are instructions to be able to run Image Magick on the MacOS command line.
- Install the fantastic Homebrew if you don’t have it already (from the terminal.app). Instructions at their website.
- Install the also fantastic ImageMagick from the terminal using Homebrew by executing the command:
brew install imagemagick
Once you have Image Magick, it is very simple to create the animation. Just run in the terminal:
convert frame*.png movie.gif
To control the speed, you need to add -delay X
before the input images and -loop 0
before the output gif to tell it how many times to loop (0 means to never stop looping).
convert -delay 20 frame*.png -loop 0 movie.gif
Digging deeper - Szegő curve
There are plenty of interesting things to consider regarding the animation. Let’s look at the animation once more:
The zeros seem to form a “wave” that grows away from the origin. Can we say something about its shape?
This question was answered by Gábor Szegő in 1924, no computers involved!
Szegő discovered that the right way to stop the zeros from trailing off to infinity is to scale them down by a factor of $n$. When one does this, the zeros converge to a very specific “tear-shaped” curve.
Here is the animation with the scaling, with $n$ going from $1$ to $60$ past $n=60$ Sage has trouble finding all the zeros numerically). So much hidden order!
Szegő even found a formula for the limiting curve. It is: $$ \left|ze^{1-z}\right|=1,\quad\text{for } |z|\leq 1 $$
and here is it’s graph (cartesian equation is $x^{2}+y^{2}=e^{2(x-1)}$)
Sage code for the scaled version to see the Szegő curve
This is the Sage code that makes the pgns that make up the frames of the animation with the scaling. It is barely any different from the previous code. The only difference (besides the pngs being stored in a different folder), is that it plots [(real(root)/i, imag(root)/i)]
instead of [(real(root), imag(root))]
.
from sage.plot.colors import red, blue
f=0
n=50
folder="sagePngsScaled"
myPlot=Graphics()
for i in range(n+1):
f=f+x^i/factorial(i)
roots=f.solve(x,to_poly_solve=True) # to_poly_solve makes Sage use Maxima's solver package to find ALL the roots
numRoots=len(roots)
myRoots= [roots[i].right() for i in range(numRoots)]
filename=folder+"/"+"frame"+Integer(i).str().zfill(3)+".png"
show("saved: "+ filename)
p=list_plot(
[(real(root)/i, imag(root)/i) for root in myRoots],
xmin=-Integer(1),
xmax=Integer(1),
ymin=-Integer(1),
ymax=Integer(1),
aspect_ratio=Integer(1),
color=blue
)
p.save(filename, dpi=200)
More about the Szegő curve
You can read more about the Szegő curve here:
-
Zemyan, S. M. (2005). On the Zeroes of the Nth Partial Sum of the Exponential Series. The American Mathematical Monthly, 112(10), 891–909. https://doi.org/10.2307/30037629
-
(Check out the Table of zeros and plot in this one!) K.E. Iverson, the zeros of the partial sums of $e^z$. https://www.ams.org/journals/mcom/1953-07-043/S0025-5718-1953-0057013-0/S0025-5718-1953-0057013-0.pdf
-
D.J Newman, T.J Rivlin, The zeros of the partial sums of the exponential function. https://doi.org/10.1016/0021-9045(72)90007-X
Some other cool images
Once one has working code to generate a mathematical image, making all sorts of interesting variations/extensions is easy!
Here is the plot of all the zeros for $n=1$, $n=2$, …, $n=40$ at the same time with some cool coloring.
This is the code that produces that image:
from sage.plot.colors import red, blue
f=0
myPlot=Graphics()
n=40
for i in range(n+1):
f=f+x^i/factorial(i)
roots=f.solve(x,to_poly_solve=True) # to_poly_solve makes Sage use Maxima's solver package to find ALL the roots
numRoots=len(roots)
myRoots= [roots[i].right() for i in range(numRoots)]
show(i)
myPlot+=list_plot(
[(real(i), imag(i)) for i in myRoots],
xmin=-Integer(20),
xmax=Integer(20),
aspect_ratio=Integer(1),
color=red.blend(blue, fraction=i/10)
)
show(myPlot)
And here it is with rainbow colors (each color corresponds to one $n$)
Sage code:
from sage.plot.colors import red, blue
f=0
myPlot=Graphics()
n=40
myColors=rainbow(n)
for i in range(n+1):
f=f+x^i/factorial(i)
roots=f.solve(x,to_poly_solve=True) # to_poly_solve makes Sage use Maxima's solver package to find ALL the roots
numRoots=len(roots)
myRoots= [roots[i].right() for i in range(numRoots)]
show(i)
myPlot+=list_plot(
[(real(i), imag(i)) for i in myRoots],
xmin=-Integer(20),
xmax=Integer(20),
aspect_ratio=Integer(1),
color=Color(myColors[i]).darker(fraction=0.2)
)
show(myPlot)