Complex numbers

If your background includes mathematics or physics, you’ve no doubt encountered complex numbers. The basic unit used in complex numbers is the square root of -1, denoted in mathematics and physics books by a lowercase i, and in engineering by j. Any number that is a multiple of i is called an imaginary number. A complex number is the sum of a real number and an imaginary number, as in 3+4i or -7.2-9.3i. [As my background is in physics, I tend to prefer i over j, but we’ll have to overcome that predilection when we program in Python, as we’ll see.]

Python uses an upper or lowercase ‘J’ or ‘j’ to denote \sqrt{-1}. Thus we can define the above complex numbers in Python as:

z1 = 3 + 4j
z2 = -7.2 - 9.3j

If the real or imaginary part of a complex number contains a decimal point, it is stored as a float, so the usual problems with roundoff error can occur with complex numbers as well. Note that if you want to use the quantity j on its own, you must prefix it with 1, as in z = 1j. The letter ‘j’ on its own is interpreted as a variable name.

All the basic arithmetic operations are defined for complex numbers. As a reminder, the arithmetic operations are defined as follows for complex numbers z_1=a+bj and z_2=c+dj:

    \begin{align*} z_{1}+z_{2} & =\left(a+c\right)+\left(b+d\right)j\\ z_{1}-z_{2} & =\left(a-c\right)+\left(b-d\right)j\\ z_{1}\times z_{2} & =\left(a+bj\right)\left(c+dj\right)\\ & =ac-bd+\left(ad+bc\right)j\\ \frac{z_{1}}{z_{2}} & =\frac{a+bj}{c+dj}\\ & =\frac{\left(a+bj\right)\left(c-dj\right)}{\left(c+dj\right)\left(c-dj\right)}\\ & =\frac{\left(ac+bd\right)+\left(bc-ad\right)j}{c^{2}+d^{2}} \end{align*}

Try these to see what you get:

z1 + z2
z1 - z2
z1 * z2
z1 / z2
See answers
(-4.2-5.300000000000001j)
(10.2+13.3j)
(15.600000000000001-56.7j)
(-0.42507048362611144-0.0065061808718282306j)

Note that the integer division operator // is not defined for complex numbers.

To do more advanced operations on complex numbers, you’ll need the cmath module. This allows you to express a complex number z in polar form as z=re^{j\theta}, where r is the modulus or amplitude of z (written |z|=r) and \theta is the phase. You can also apply standard functions such as logarithms, trigonometric and hyperbolic functions and their inverses. Here are a few examples to try:

from cmath import *
z = 3 + 2j
polar(z)
abs(z)
phase(z)
log(z)
See answers
(3.605551275463989, 0.5880026035475675)
3.605551275463989
0.5880026035475675
(1.2824746787307684+0.5880026035475675j)

The polar(z) function converts z to polar form, which returns a tuple, with the first element being the amplitude of z and the second argument the phase. The abs(z) function returns the amplitude (or absolute value) of z and phase(z) returns the phase (in radians).

The log(z) function returns the log (to base e). Since z=re^{j\theta}, applying the usual rules for the logarithm of a product and an exponential we get \log\left(re^{j\theta}\right)=\log r+j\theta. We see from the answer above that log(3.605551275463989) = 1.2824746787307684, and the imaginary part is just j times the phase, as expected.

Readers familiar with complex variable theory will realize that the logarithm of a complex number is not unique. Since e^{j\theta}=e^{j\left(\theta+2n\pi\right)} for any integer n, we can in principle add any multiple of 2\pi to the imaginary part of any logarithm. The log function in cmath always returns a value with a phase in the range -\pi<\theta\le\pi. If you want a different branch of the logarithm, you’ll need to account for it in the surrounding code.

Similar problems arise when calculating roots of complex numbers. If we calculate the square root of a positive real number, there are two possible answers, differing in the sign. In general, when calculating the nth root of a complex number, there are n different possible results. The answer returned by Python is the one obtained from taking the nth root of the form of the complex number with the smallest phase. That is, if z=re^{j\theta}, then if we calculate z ** (1/n), we get z^{\left(1/n\right)}=r^{\left(1/n\right)}e^{j\theta/n}. Other roots would have to be calculated from this one.

Other functions of complex numbers have similar peculiarities, so you’ll need to be familiar with the mathematics if you want to use them properly. I don’t want to get too bogged down with details, but perhaps one more example might be useful.

To calculate the cosine of a complex number z we can use the formula \cos z=\frac{e^{jz}+e^{-jz}}{2}. For z=3+2j, for example, this formula gives us

    \begin{align*} \cos\left(3+2j\right) & =\frac{1}{2}\left(e^{j\left(3+2j\right)}+e^{-j\left(3+2j\right)}\right)\\ & =\frac{1}{2}\left(e^{-2}e^{3j}+e^{2}e^{-3j}\right) \end{align*}

If you enter this formula into the Python console and compare the result with cos(z), you’ll see that they are the same (well, up to the penultimate digit, anyway).

To get a complete list of functions in cmath, in a fresh console type the commands:

import cmath
help(cmath)

Exercises

Write Python code to calculate the three cube roots (one real, two complex) of +1. Verify your answer by cubing the results and showing that they do indeed give +1.

See answers
The integer +1 can be written as e^{0}, e^{2\pi j} and e^{4\pi j}, so its three cube roots are +1, e^{2\pi j/3} and e^{4\pi j/3}.

Code to calculate these three roots is:

from cmath import *
z1 = 1
z2 = exp(2j * pi / 3)
z3 = z2 * z2
# Verify the results
z1 ** 3
z2 ** 3
z3 ** 3

When you calculated the cubes in the above exercise, you found that the answers were only approximately equal to +1 due to roundoff error. Try to find a way of expressing the cubes so that they give exactly +1.

See answers

You can modify the code as follows:

from cmath import *
z1 = 1
z2 = exp(2j * pi / 3)
z3 = z2 * z2
# Verify the results
z1cube = z1 ** 3
z2cube = z2 ** 3
z3cube = z3 ** 3
# Use the round() function on real & imaginary parts separately
z1cube = round(z1cube.real) + round(z1cube.imag) * 1j
z2cube = round(z2cube.real) + round(z2cube.imag) * 1j
z3cube = round(z3cube.real) + round(z3cube.imag) * 1j

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.