# Chapter 7: Complex Analysis

> *"Complex numbers are not complicated—they're just 2D. Euler's formula connects exponentials, trigonometry, and rotation in one beautiful equation."*

## Why Complex Numbers for ML?

You might wonder: why do ML practitioners need complex numbers? Here's why:

1. **Fourier transforms** use complex exponentials
2. **Signal processing** is naturally complex
3. **Quantum computing** is built on complex numbers
4. **Some optimizations** are easier in the complex plane
5. **Eigenvalues** of real matrices can be complex

This chapter gives you the intuition you need—not a full course in complex analysis.

## Complex Numbers as 2D Vectors

### The Basic Idea

A complex number z = a + bi is just a point in 2D:
- **Real part**: a (x-coordinate)
- **Imaginary part**: b (y-coordinate)
- **i**: The "imaginary unit" where i² = -1

```python
import numpy as np

# Complex numbers in Python
z = 3 + 4j  # a = 3, b = 4

print(f"Real part: {z.real}")       # 3.0
print(f"Imaginary part: {z.imag}")  # 4.0
print(f"Magnitude: {abs(z)}")       # 5.0 (distance from origin)
print(f"Angle: {np.angle(z)}")      # 0.927 radians
```

### Polar Form

Any complex number can be written as:

$$z = r(\cos\theta + i\sin\theta) = re^{i\theta}$$

where:
- r = |z| is the **magnitude** (distance from origin)
- θ is the **argument** (angle from positive real axis)

```python
r = abs(z)
theta = np.angle(z)
z_polar = r * np.exp(1j * theta)
print(f"z = {z}, z_polar = {z_polar}")  # Same number!
```

## Euler's Formula: The Most Beautiful Equation

$$e^{i\theta} = \cos\theta + i\sin\theta$$

This single equation connects:
- **Exponentials** (e)
- **Trigonometry** (sin, cos)
- **Complex numbers** (i)
- **Rotation** (θ)

### Special Case: Euler's Identity

When θ = π:

$$e^{i\pi} + 1 = 0$$

This equation contains the five most important constants in mathematics: e, i, π, 1, and 0.

### Why It Matters

Euler's formula means:
- **Multiplication by e^(iθ)** = rotation by angle θ
- **Sinusoids are complex exponentials**: cos(θ) = Re(e^(iθ))
- **Fourier analysis** becomes multiplication

## Complex Differentiation

### Analytic Functions

A function f: ℂ → ℂ is **analytic** (or holomorphic) if it's differentiable in the complex sense:

$$f'(z) = \lim_{h \to 0} \frac{f(z+h) - f(z)}{h}$$

where h can approach 0 from any direction in the complex plane.

### The Cauchy-Riemann Equations

If f(z) = u(x,y) + iv(x,y) is analytic, then:

$$\frac{\partial u}{\partial x} = \frac{\partial v}{\partial y}, \quad \frac{\partial u}{\partial y} = -\frac{\partial v}{\partial x}$$

These constraints are very restrictive—analytic functions are extremely smooth.

### Why This Matters

Analytic functions have remarkable properties:
- **Infinitely differentiable** (unlike real functions)
- **Equal to their Taylor series** (no approximation error!)
- **Determined by values on boundary** (holographic principle)

## The Fourier Transform

### From Time to Frequency

The Fourier transform converts a signal from time domain to frequency domain:

$$\hat{f}(\omega) = \int_{-\infty}^{\infty} f(t) e^{-i\omega t} dt$$

The inverse:

$$f(t) = \frac{1}{2\pi} \int_{-\infty}^{\infty} \hat{f}(\omega) e^{i\omega t} d\omega$$

### Intuition

- e^(iωt) is a rotating complex number at frequency ω
- The integral measures how much f(t) "correlates" with each frequency
- The result is a complex number encoding amplitude and phase

### In Code

```python
import numpy as np
from numpy.fft import fft, fftfreq

# Create a signal: sum of two frequencies
t = np.linspace(0, 1, 1000)
signal = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 25 * t)

# Compute FFT
spectrum = fft(signal)
frequencies = fftfreq(len(t), t[1] - t[0])

# The spectrum shows peaks at 10 Hz and 25 Hz
import matplotlib.pyplot as plt
plt.plot(frequencies[:500], np.abs(spectrum[:500]))
plt.xlabel('Frequency (Hz)')
plt.ylabel('Magnitude')
plt.title('Frequency Spectrum')
plt.show()
```

### ML Applications

- **Audio processing**: Speech recognition, music generation
- **Image processing**: Convolution in frequency domain
- **Time series**: Spectral analysis, periodicity detection
- **Positional encodings**: Transformers use sinusoidal embeddings

## Convolution and the Fourier Transform

### The Convolution Theorem

Convolution in time domain = multiplication in frequency domain:

$$\mathcal{F}(f * g) = \mathcal{F}(f) \cdot \mathcal{F}(g)$$

This is why FFT-based convolution is fast:
1. FFT both signals: O(n log n)
2. Multiply: O(n)
3. Inverse FFT: O(n log n)

Total: O(n log n) instead of O(n²) for direct convolution.

### ML Connection

CNNs use convolution extensively. For large kernels, FFT-based convolution can be faster.

## Residues and Contour Integration

### The Residue Theorem

For analytic functions with isolated singularities:

$$\oint_C f(z) dz = 2\pi i \sum \text{Res}(f, z_k)$$

The integral around a closed curve equals 2πi times the sum of residues inside.

### Why It Matters

This lets you compute difficult real integrals by:
1. Extending to complex plane
2. Choosing a clever contour
3. Using residues instead of direct integration

### Example Application

The integral ∫₀^∞ sin(x)/x dx = π/2 is hard to compute directly but easy with contour integration.

## Complex Numbers in Neural Networks

### Complex-Valued Networks

Some applications benefit from complex-valued neural networks:

```python
# Complex linear layer (conceptual)
def complex_linear(z, W_real, W_imag, b_real, b_imag):
    """
    Complex matrix multiplication:
    (W_r + iW_i)(z_r + iz_i) = (W_r z_r - W_i z_i) + i(W_r z_i + W_i z_r)
    """
    z_real, z_imag = z.real, z.imag
    out_real = W_real @ z_real - W_imag @ z_imag + b_real
    out_imag = W_real @ z_imag + W_imag @ z_real + b_imag
    return out_real + 1j * out_imag
```

### Applications

- **MRI reconstruction**: Data is inherently complex
- **Radar/sonar**: Phase information matters
- **Audio synthesis**: Complex spectrograms
- **Quantum ML**: Quantum states are complex

## Conformal Maps

### Angle-Preserving Transformations

Analytic functions preserve angles—they're **conformal maps**. This means:
- Local shapes are preserved (circles map to circles, infinitesimally)
- Useful for solving PDEs by mapping to simpler domains

### Example: The Joukowski Transform

$$w = z + \frac{1}{z}$$

This maps a circle to an airfoil shape—used in aerodynamics!

## Connecting to PyDelt

While PyDelt primarily works with real-valued functions, complex analysis concepts appear in:

1. **Fourier-based smoothing**: Some interpolation methods use frequency domain
2. **Spectral analysis**: Analyzing periodic components in time series
3. **Signal processing**: Derivatives of complex signals

```python
import numpy as np
from pydelt.interpolation import SplineInterpolator

# Complex signal (e.g., from FFT)
t = np.linspace(0, 2*np.pi, 100)
signal_complex = np.exp(1j * t)  # Unit circle in complex plane

# Work with real and imaginary parts separately
real_part = signal_complex.real
imag_part = signal_complex.imag

# Differentiate each part
interp_real = SplineInterpolator(smoothing=0.01)
interp_real.fit(t, real_part)
deriv_real = interp_real.differentiate(order=1)(t)

interp_imag = SplineInterpolator(smoothing=0.01)
interp_imag.fit(t, imag_part)
deriv_imag = interp_imag.differentiate(order=1)(t)

# Combine back
deriv_complex = deriv_real + 1j * deriv_imag

# For e^(it), derivative should be i*e^(it)
expected = 1j * np.exp(1j * t)
print(f"Max error: {np.max(np.abs(deriv_complex - expected)):.6f}")
```

## Key Takeaways

1. **Complex numbers are 2D vectors** with special multiplication
2. **Euler's formula** connects exponentials, trig, and rotation
3. **Fourier transform** converts time ↔ frequency using complex exponentials
4. **Analytic functions** are extremely smooth and well-behaved
5. **Convolution theorem** makes FFT-based convolution fast
6. **Complex networks** are useful for inherently complex data

## Exercises

1. **Euler's formula**: Verify that e^(iπ/4) = (1+i)/√2 using Python.

2. **FFT analysis**: Create a signal with 3 frequency components. Use FFT to identify them.

3. **Complex derivative**: For f(z) = z², verify the Cauchy-Riemann equations.

4. **Rotation**: Show that multiplying by e^(iπ/2) rotates a complex number by 90°.

---

*Previous: [← Multivariate Calculus](06_multivariate_calculus.md) | Next: [Applications to ML →](08_applications_to_ml.md)*
