TAI BUI
Back to curriculum
LearnPython

Complex Numbers for AI

Every rotation, oscillation, and RoPE embedding in this curriculum runs on one idea: i is a 90-degree turn, not a philosophical trick.

TIME: ~60 minutesPREREQUISITES: Phase 1, Lessons 01-04 (linear algebra, calculus)

Learning Objectives

  • Perform complex arithmetic (add, multiply, divide, conjugate) in both rectangular and polar form
  • Apply Euler's formula to convert between complex exponentials and trigonometric functions
  • Implement the Discrete Fourier Transform using complex roots of unity
  • Explain how complex rotations underlie RoPE and sinusoidal positional encodings in transformers

The Problem

Open a paper on Fourier transforms and i is everywhere. Look at transformer positional encodings and you'll find sin and cos pairs at different frequencies: the real and imaginary parts of complex exponentials, written out in trig instead. Read about quantum computing and the whole framework lives in complex vector spaces.

Complex numbers look abstract because the square root of -1 sounds like a trick question. It isn't. It's the natural language of rotations and oscillations, and once that clicks, i stops looking mysterious and starts looking like the right tool for the job. Anything that spins, vibrates, or oscillates is fair game for complex numbers.

Skip this lesson and a few doors stay locked: the Discrete Fourier Transform, the FFT built on top of it, RoPE (Rotary Position Embedding) in modern language models, and the specific frequencies the original Transformer paper chose for its sinusoidal positional encodings. All four rest on the same handful of complex-number facts.

This lesson builds complex arithmetic from scratch, connects it to geometry, and shows exactly where it shows up in machine learning.

Warm-up0/3
1. What happens when you square any real number, positive or negative?Warm-up
A number line from -3 to 3 with four points marked (-3, -1, 2, 3), each feeding into a box asking for that number squared; no results are filled in.
2. You rotate a point 90 degrees counterclockwise, then rotate the result another 90 degrees in the same direction. What is the total rotation from where it started?Warm-up
A dashed circle with an arrow pointing right labelled start, a second arrow pointing straight up labelled after 1 turn of 90 degrees, and a question mark on the left side for where a second 90-degree turn in the same direction would land.
3. On the unit circle, a point at angle 0 sits at (cos(0), sin(0)). What are those two values?Warm-up
A unit circle with a point marked at angle 0 on the positive horizontal axis, with cos(0) and sin(0) both left as question marks.

The Concept

What is a complex number?

A complex number has two parts: a real part and an imaginary part.

z = a + bi

where:
  a is the real part
  b is the imaginary part
  i is the imaginary unit, defined by i^2 = -1

That's it. Extend the number line into a plane: real numbers on one axis, imaginary numbers on the other. Every complex number is a point in that plane.

Complex arithmetic

Addition. Add the real parts together, add the imaginary parts together.

(a + bi) + (c + di) = (a + c) + (b + d)i

Example: (3 + 2i) + (1 + 4i) = 4 + 6i

Multiplication. Use the distributive law and remember that i^2 = -1.

(a + bi)(c + di) = ac + adi + bci + bdi^2
                 = ac + adi + bci - bd
                 = (ac - bd) + (ad + bc)i

Example: (3 + 2i)(1 + 4i) = 3 + 12i + 2i + 8i^2
                            = 3 + 14i - 8
                            = -5 + 14i

Conjugate. Flip the sign of the imaginary part.

conjugate of (a + bi) = a - bi

The product of a complex number and its conjugate is always real:

(a + bi)(a - bi) = a^2 + b^2

Division. Multiply numerator and denominator by the conjugate of the denominator.

(a + bi) / (c + di) = (a + bi)(c - di) / (c^2 + d^2)

This eliminates the imaginary part from the denominator, giving you a clean complex number.

The complex plane

The complex plane maps every complex number to a 2D point. The horizontal axis is the real axis, the vertical axis is the imaginary axis.

z = 3 + 2i  corresponds to the point (3, 2)
z = -1 + 0i corresponds to the point (-1, 0) on the real axis
z = 0 + 4i  corresponds to the point (0, 4) on the imaginary axis

A complex number is simultaneously a point and a vector from the origin. This dual interpretation is what makes complex numbers useful for geometry.

Polar form

Any point in the plane can be described by its distance from the origin and its angle from the positive real axis.

z = r * (cos(theta) + i*sin(theta))

where:
  r = |z| = sqrt(a^2 + b^2)     (magnitude, or modulus)
  theta = atan2(b, a)             (phase, or argument)

Rectangular form (a + bi) is good for addition. Polar form (r, theta) is good for multiplication.

Multiplication in polar form. Multiply the magnitudes, add the angles.

z1 = r1 * e^(i*theta1)
z2 = r2 * e^(i*theta2)

z1 * z2 = (r1 * r2) * e^(i*(theta1 + theta2))

This is why complex numbers are perfect for rotations. Multiplying by a complex number with magnitude 1 is a pure rotation.

Euler's formula

Euler's formula is the bridge between complex exponentials and trigonometry:

e^(i*theta) = cos(theta) + i*sin(theta)

This is the most important formula in this lesson. When theta = pi:

e^(i*pi) = cos(pi) + i*sin(pi) = -1 + 0i = -1

Therefore: e^(i*pi) + 1 = 0

Five fundamental constants (e, i, pi, 1, 0) linked in one equation.

Why Euler's formula matters for ML

Euler's formula says that e^(i*theta) traces the unit circle as theta varies: (1, 0) at theta = 0, (0, 1) at theta = pi/2, (-1, 0) at theta = pi, (0, -1) at theta = 3pi/2. A full rotation closes the loop at theta = 2pi.

That's not a coincidence: complex exponentials are rotations, and rotations are everywhere in signal processing and ML.

Connection to 2D rotations

Multiplying the complex number (x + yi) by e^(i*theta) rotates the point (x, y) by angle theta around the origin.

Rotation via complex multiplication:
  (x + yi) * (cos(theta) + i*sin(theta))
  = (x*cos(theta) - y*sin(theta)) + (x*sin(theta) + y*cos(theta))i

Rotation via matrix multiplication:
  [cos(theta)  -sin(theta)] [x]   [x*cos(theta) - y*sin(theta)]
  [sin(theta)   cos(theta)] [y] = [x*sin(theta) + y*cos(theta)]

They produce identical results. Complex multiplication IS 2D rotation. The rotation matrix is just complex multiplication written in matrix notation.

Mermaid diagram

Phasors and rotating signals

A complex exponential e^(iomegat) is a point rotating around the unit circle at angular frequency omega. As t increases, the point traces the circle.

The real part of this rotating point is cos(omegat). The imaginary part is sin(omegat). A sinusoidal signal is the shadow of a rotating complex number.

e^(i*omega*t) = cos(omega*t) + i*sin(omega*t)

Real part:      cos(omega*t)    -- a cosine wave
Imaginary part: sin(omega*t)    -- a sine wave

This is the phasor representation. Instead of tracking a wiggly sine wave, you track a smoothly rotating arrow. Phase shifts become angle offsets. Amplitude changes become magnitude changes. Addition of signals becomes vector addition.

Roots of unity

The N-th roots of unity are N points equally spaced on the unit circle:

w_k = e^(2*pi*i*k/N)    for k = 0, 1, 2, ..., N-1

For N = 4, the roots are: 1, i, -1, -i (the four compass points). For N = 8, you get the four compass points plus the four diagonals.

Roots of unity are the foundation of the Discrete Fourier Transform. The DFT decomposes a signal into components at these N equally-spaced frequencies.

Connection to the DFT

The Discrete Fourier Transform of a signal x[0], x[1], ..., x[N-1] is:

X[k] = sum_{n=0}^{N-1} x[n] * e^(-2*pi*i*k*n/N)

Each X[k] measures how much the signal correlates with the k-th root of unity, a complex sinusoid at frequency k. The DFT breaks a signal into N rotating phasors and reports the amplitude and phase of each one.

Why i is not imaginary

i is a 90-degree rotation operator. Multiply a real number by i once and it swings 90 degrees onto the imaginary axis. Multiply by i again (i^2) and it swings another 90 degrees, landing on the negative real axis. That's why i^2 = -1: not a mystery, just a half-turn built from two quarter-turns.

The word "imaginary" is a historical accident. Descartes coined it dismissively, and the name stuck. But i is no more imaginary than negative numbers were when people first rejected them. Negative numbers answer "what do you subtract 5 from 3 to get?" The imaginary unit answers "what do you square to get -1?" Neither question is nonsense, and neither answer is fictional.

It's the rotation, not the label, that explains why complex numbers are everywhere in engineering. Anything that rotates (electromagnetic waves, quantum states, signal oscillations, positional encodings) is naturally described by complex numbers.

Complex exponentials vs trigonometric functions

Before Euler's formula, engineers wrote signals as Acos(omegat + phi): amplitude A, frequency omega, phase phi. This works but makes arithmetic painful. Adding two cosines with different phases requires trigonometric identities.

With complex exponentials, the same signal is Ae^(i(omega*t + phi)). Adding two signals is just adding two complex numbers. Multiplying (modulating) is just multiplying magnitudes and adding angles. Phase shifts become angle additions. Frequency shifts become multiplications by phasors.

The entire field of signal processing switched to complex exponential notation because the math is cleaner. The "real signal" is always just the real part of the complex representation. The imaginary part is carried along as bookkeeping, making all the algebra work out naturally.

Connection to transformers

Sinusoidal positional encodings (original Transformer paper):

PE(pos, 2i) = sin(pos / 10000^(2i/d))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d))

The sin and cos pairs are the real and imaginary parts of complex exponentials at different frequencies. Each frequency provides a different "resolution" for encoding position. Low frequencies change slowly (coarse position). High frequencies change quickly (fine position). Together they give each position a unique frequency fingerprint.

RoPE (Rotary Position Embedding) takes this further. It explicitly multiplies query and key vectors by complex rotation matrices. The relative position between two tokens becomes a rotation angle. Attention is computed using these rotated vectors, making the model sensitive to relative position through complex multiplication.

OperationAlgebraic FormGeometric Meaning
Addition(a+c) + (b+d)iVector addition in the plane
Multiplication(ac-bd) + (ad+bc)iRotate and scale
Conjugatea - biReflect over real axis
Magnitudesqrt(a^2 + b^2)Distance from origin
Phaseatan2(b, a)Angle from positive real axis
Divisionmultiply by conjugateReverse rotation and rescale
Powerr^n * e^(intheta)Rotate n times, scale by r^n

Mermaid diagram

Check: The Concept0/28
1. What is a complex number?Define
The point z = 3 + 4i plotted as an arrow from the origin on the complex plane, with the Re axis horizontal and the Im axis vertical.
2. What is the imaginary unit i?Define
The complex plane with the real number 1 marked on the horizontal axis and the point i marked on the vertical axis, one unit from the origin in each case.
3. What is the complex plane?Define
An empty 2D grid with a horizontal axis labelled Re and a vertical axis labelled Im, both passing through the origin; no point is plotted.
4. What does the magnitude |z| of z = a + bi measure, and how is it computed?Define
The point z = 3 + 4i with a dashed line from the origin to it labelled distance equals question mark; no length is given.
5. What is the phase (argument) of a complex number?Define
The point z = 1 + 1i with an arc from the positive real axis to the arrow, labelled theta equals question mark; no distance is marked.
6. What is the conjugate of the complex number 3 + 2i, and what is special about the product of a complex number and its conjugate?Define
z = 3 + 2i plotted above the real axis, with a question mark at its mirror position below the axis; the conjugate's value and the product are both left unanswered.
7. What is polar form?Define
A single point on the complex plane with two boxes pointing to it, one labelled a plus b i rectangular and the other labelled r comma theta polar.
8. What does Euler's formula e^(i*theta) equal?Define
A point on the unit circle at an unlabelled angle theta from the positive real axis, marked e to the i theta, with its real and imaginary parts both left as question marks.
9. What is a phasor?Define
A unit circle with four arrows at increasing angles, numbered 1 through 4, tracing a path counterclockwise; no wave graph is shown.
10. What are the N-th roots of unity?Define
A unit circle with the point w_0 = 1 marked on the positive real axis and three question marks placed elsewhere on the circle, asking where the remaining roots sit.
11. What does the Discrete Fourier Transform formula X[k] = sum_n x[n] * e^(-2*pi*i*k*n/N) measure for each k?Define
The DFT formula, taking in a signal x[0] through x[N-1] and producing X[k] left as a question mark; no interpretation is given.
12. What is RoPE (Rotary Position Embedding)?Define
Two token boxes, one holding a query vector at position m and the other a key vector at position n, with a question mark between them and no mechanism drawn.
13. How do you divide (a+bi) by (c+di)?Explain
The general expression (a plus b i) divided by (c plus d i) equals a question mark; no procedure or numbers are shown.
14. Why is polar form the better choice when multiplying two complex numbers?Explain
Two polar complex numbers, r1 theta1 and r2 theta2, with a multiplication sign between them and a question mark for the product; no rule is shown.
15. Why does multiplying a point by e^(i*theta) change its angle but not its distance from the origin?Explain
A dashed arrow z of some length and a separate point e to the i theta sitting on the unit circle, with a note asking whether multiplying changes z's distance from the origin.
16. Under the rotation view of i, why does i^2 equal -1?Explain
A chain of three boxes connected by arrows labelled times i: the first box holds 1, the next two are both left as question marks.
17. How does rotating a point by multiplying by e^(i*theta) compare to rotating it with the 2D rotation matrix?Explain
Two boxes, one for rotating by complex multiplication and one for rotating with the matrix, with a question mark between them and no verdict given.
18. Why are complex numbers used in Rotary Position Embedding (RoPE) for transformers?Explain
A query vector rotated by an angle tied to its position m and a key vector rotated by an angle tied to its position n, both drawn on the unit circle, with a question asking why this helps attention reflect token distance.
19. In the original Transformer's sinusoidal positional encoding, what does each sin and cos pair correspond to?Explain
The sinusoidal positional encoding pair at one position and one frequency, giving a sine value of 0.1987 and a cosine value of 0.9801.
20. In the original Transformer's sinusoidal positional encoding, what do low frequencies and high frequencies encode?Explain
Two sine curves over position index, one labelled A changing slowly and one labelled B changing quickly, with a question asking which is coarse and which is fine.
21. What is the result of (3 + 2i)(1 + 4i)?Apply
The expression (3 plus 2i) times (1 plus 4i) equals a question mark; nothing is expanded.
22. Start at the point 1 on the real axis and multiply by i three times. Where do you land?Apply
A chain of four boxes connected by arrows labelled times i: the first holds 1, the remaining three are all question marks.
23. Take z1 = 2 * e^(i*pi/6) and z2 = 3 * e^(i*pi/3). What is z1 * z2?Apply
z1 equal to 2 e to the i pi over 6, and z2 equal to 3 e to the i pi over 3, with their product left as a question mark.
24. A loop multiplies a point by 1.02 * e^(i*pi/6) twelve times, expecting to return to the start. It ends farther from the origin. Which ingredient broke the pure rotation?Analyze
A spiral path of twelve points, each the previous one multiplied by 1.02 times e to the i pi over 6, starting at distance 2.00 from the origin and ending at distance 2.54, farther out each step.
25. A colleague adds two numbers in polar form by adding their magnitudes and adding their angles. What is the wrong assumption?Analyze
A colleague's proposed sum of two polar numbers, formed by adding their magnitudes and adding their angles directly, with a question asking whether this is correct.
26. A student multiplies 3 + 2i by itself hoping for a real result, and gets 5 + 12i. Which change gives a real product?Analyze
The square of 3 plus 2i computed as 5 plus 12i, still carrying an imaginary part, with a question asking what to multiply by instead to get a real result.
27. You want attention scores between two tokens to reflect how far apart they are rather than their absolute positions, using ideas from this lesson. Which design fits?Design
Two token boxes, A at position m and B at position n, with a note that the design goal is for attention to depend on their distance m minus n rather than on m and n separately; no mechanism is drawn.
28. You must add two sinusoids with different amplitudes and phases, then phase-shift one of them, with as little trig-identity algebra as possible. Which representation fits?Design
Two cosine curves with different amplitudes and phase offsets plotted against time, with a question about adding them using minimal trigonometric identities.

Build It

Step 1: Complex class

Build a Complex number class that supports arithmetic, magnitude, phase, and conversion between rectangular and polar forms.

python
import math
 
class Complex:
    def __init__(self, real, imag=0.0):
        self.real = real
        self.imag = imag
 
    def __add__(self, other):
        return Complex(self.real + other.real, self.imag + other.imag)
 
    def __mul__(self, other):
        r = self.real * other.real - self.imag * other.imag
        i = self.real * other.imag + self.imag * other.real
        return Complex(r, i)
 
    def __truediv__(self, other):
        denom = other.real ** 2 + other.imag ** 2
        r = (self.real * other.real + self.imag * other.imag) / denom
        i = (self.imag * other.real - self.real * other.imag) / denom
        return Complex(r, i)
 
    def magnitude(self):
        return math.sqrt(self.real ** 2 + self.imag ** 2)
 
    def phase(self):
        return math.atan2(self.imag, self.real)
 
    def conjugate(self):
        return Complex(self.real, -self.imag)

Step 2: Polar conversion and Euler's formula

Rectangular and polar form both describe the same point, so you need a fast way to move between them. Euler's formula makes that a one-line function:

python
def to_polar(z):
    return z.magnitude(), z.phase()
 
def from_polar(r, theta):
    return Complex(r * math.cos(theta), r * math.sin(theta))
 
def euler(theta):
    return Complex(math.cos(theta), math.sin(theta))

Verify: euler(theta).magnitude() should always be 1.0. euler(0) should give (1, 0). euler(pi) should give (-1, 0).

Step 3: Rotation

Rotating a point (x, y) by angle theta is one complex multiplication:

python
point = Complex(3, 4)
rotated = point * euler(math.pi / 4)

The magnitude stays the same. Only the angle changes.

Step 4: DFT from complex arithmetic

The DFT formula from the Concept section translates directly into code: for each frequency k, multiply every sample by the matching root of unity and add up the results.

python
def dft(signal):
    N = len(signal)
    result = []
    for k in range(N):
        total = Complex(0, 0)
        for n in range(N):
            angle = -2 * math.pi * k * n / N
            total = total + Complex(signal[n], 0) * euler(angle)
        result.append(total)
    return result

This is the O(N^2) DFT. Each output X[k] is the sum of the signal samples multiplied by roots of unity.

Step 5: Inverse DFT

The inverse DFT reconstructs the original signal from its spectrum. The only changes from the forward DFT: flip the sign in the exponent and divide by N.

python
def idft(spectrum):
    N = len(spectrum)
    result = []
    for n in range(N):
        total = Complex(0, 0)
        for k in range(N):
            angle = 2 * math.pi * k * n / N
            total = total + spectrum[k] * euler(angle)
        result.append(Complex(total.real / N, total.imag / N))
    return result

This gives you perfect reconstruction. Apply DFT, then IDFT, and you get back the original signal to machine precision. No information is lost.

Step 6: Roots of unity

The DFT and IDFT above both call euler() at multiples of 2*pi/N. Those calls are exactly the roots of unity, so it's worth generating them directly instead of recomputing them inline:

python
def roots_of_unity(N):
    return [euler(2 * math.pi * k / N) for k in range(N)]

Verify two properties:

  • Every root has magnitude exactly 1.
  • The sum of all N roots is zero (they cancel out by symmetry).

These properties are what make the DFT invertible. The roots of unity form an orthogonal basis for the frequency domain.

Check: Build It0/6
1. The N-th roots of unity are N equally spaced points on the unit circle. What is their sum?Explain
The five 5th roots of unity drawn as arrows from the origin, equally spaced around the unit circle, with a note asking for their vector sum.
2. What changes when you turn the forward DFT into the inverse DFT?Explain
The forward DFT formula feeding into a box labelled inverse DFT equals question mark; no change is shown.
3. With the lesson's Complex class, you run point = Complex(3, 4) and rotated = point * euler(math.pi / 4). What is rotated.magnitude()?Apply
The lesson's own code: point equals Complex(3, 4), rotated equals point times euler of pi over 4, with rotated.magnitude() left as a question mark.
4. With the lesson's Complex class, what does Complex(1, 2) / Complex(3, 4) return?Apply
The expression Complex(1, 2) divided by Complex(3, 4), left as a question mark.
5. With the lesson's euler function, what does euler(math.pi) return?Apply
The lesson's own euler function definition, followed by euler of math.pi left as a question mark.
6. After running dft and then idft, every value comes back exactly N times too large. Which step of the inverse is missing?Analyze
A signal run through the forward DFT and then an inverse, with every reconstructed value exactly 4 times the original, and a question asking which step is missing.

Use It

Python has built-in complex number support. The literal j represents the imaginary unit.

python
z = 3 + 2j
w = 1 + 4j
 
print(z + w)
print(z * w)
print(abs(z))
 
import cmath
print(cmath.phase(z))
print(cmath.exp(1j * cmath.pi))

For arrays, numpy handles complex numbers natively:

python
import numpy as np
 
z = np.array([1+2j, 3+4j, 5+6j])
print(np.abs(z))
print(np.angle(z))
print(np.conj(z))
print(np.real(z))
print(np.imag(z))
 
signal = np.sin(2 * np.pi * 5 * np.linspace(0, 1, 128))
spectrum = np.fft.fft(signal)
freqs = np.fft.fftfreq(128, d=1/128)

Ship It

Run code/complex_numbers.py. It generates outputs/skill-complex-arithmetic.md, a quick reference for complex-number operations in ML and signal-processing contexts.

Author's Note

People treat complex numbers as an algebra trick you memorize for the FFT and then never touch again. If you can multiply (a+bi)(c+di) by hand but can't say why multiplying by e^(i*theta) rotates a point, you've learned the mechanics and skipped the reason any of this matters. Go back to the rotation picture before you ever call NumPy's fft.

RoPE is where the geometry pays off directly. Read the RoFormer paper cold and the rotation matrices look like a trick invented specifically for transformers. They are not. They are the same e^(i*theta) multiplication from this lesson, applied to query and key vectors instead of a point in the plane. Once i is a 90-degree turn instead of a philosophical puzzle, RoPE stops looking like a special case and starts looking like the obvious thing to try.

Exercises

  1. Complex arithmetic by hand. Compute (2 + 3i) * (4 - i) and verify with the code. Then compute (5 + 2i) / (1 - 3i). Draw both results on the complex plane and check that multiplication rotated and scaled the first number.

  2. Rotation sequence. Start with the point (1, 0). Multiply by e^(i*pi/6) twelve times. Verify that you return to (1, 0) after 12 multiplications. Print the coordinates at each step and confirm they trace a regular 12-gon.

  3. DFT of a known signal. Create a signal that is the sum of sin(2pi3t) and 0.5sin(2pi7*t) sampled at 32 points. Run your DFT. Verify that the magnitude spectrum has peaks at frequencies 3 and 7, with the peak at 7 being half the height of the peak at 3.

  4. Roots of unity visualization. Compute the 8th roots of unity. Verify that they sum to zero. Verify that multiplying any root by the primitive root e^(2pii/8) gives the next root.

  5. Rotation matrix equivalence. For 10 random angles and 10 random points, verify that complex multiplication gives the same result as matrix-vector multiplication with the 2x2 rotation matrix. Print the maximum numerical difference.

Key Terms

TermWhat it means
Complex numberA number a + bi where a is the real part, b is the imaginary part, and i^2 = -1
Imaginary unitThe number i, defined by i^2 = -1: a rotation operator, not a philosophical mystery
Complex planeThe 2D plane where the x-axis is real and the y-axis is imaginary. Also called the Argand plane
Magnitude (modulus)The distance from the origin: sqrt(a^2 + b^2). Written as |z|
Phase (argument)The angle from the positive real axis: atan2(b, a). Written as arg(z)
ConjugateThe mirror image across the real axis: conjugate of a + bi is a - bi
Polar formExpressing z as r * e^(i*theta) instead of a + bi. Makes multiplication easy
Euler's formulae^(itheta) = cos(theta) + isin(theta). Connects exponentials to trigonometry
PhasorA rotating complex number e^(iomegat) representing a sinusoidal signal
Roots of unityThe N complex numbers e^(2pii*k/N) for k = 0 to N-1. N equally spaced points on the unit circle
DFTDiscrete Fourier Transform. Decomposes a signal into complex sinusoidal components using roots of unity
RoPERotary Position Embedding. Uses complex multiplication to encode relative position in transformer attention

Further Reading

Code

Ships With