Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

The Problem: How Many Terms?

Recall that spectral methods approximate the solution to a boundary value problem as:

u(x)k=0n1ckTk(x)u(x) \approx \sum_{k=0}^{n-1} c_k T_k(x)

where TkT_k are Chebyshev polynomials and ckc_k are unknown coefficients we need to find.

The challenge: We don’t know nn in advance!

The Naive Approach: Double and Retry

The obvious strategy:

  1. Try n=16n = 16, solve the system, check if accurate enough

  2. If not, try n=32n = 32, solve again from scratch

  3. If not, try n=64n = 64, solve again from scratch

  4. ...

Problem: Each time we double nn, we throw away all the work we did before!

The Key Insight: Build the Solution Incrementally

Instead of solving separate problems for n=16n = 16, then n=32n = 32, etc., the adaptive QR algorithm:

  1. Starts solving with n=1n = 1 term

  2. Adds one term at a time, updating the solution

  3. Checks at each step: “Is this accurate enough?”

  4. Stops as soon as the answer is “yes”

The magic: Step 2 can be done efficiently using Givens rotations, and step 3 gives us the residual for free as a byproduct of the computation!

From ODE to Linear System

When we discretize a boundary value problem using spectral collocation, we get a linear system:

Ac=bA \mathbf{c} = \mathbf{b}

where:

A Special Structure: “Almost Banded”

Here’s the key observation that makes everything work. The matrix AA has a very special structure:

┌─────────────────────────────────────────┐
│   K rows from boundary conditions       │  ← Dense rows (usually 2)
├─────────────────────────────────────────┤
│                                         │
│        Banded part from the ODE         │  ← Only a few diagonals
│                                         │
└─────────────────────────────────────────┘

Why is this useful? Banded matrices can be factored in O(n)O(n) operations instead of O(n3)O(n^3). The “almost banded” structure preserves most of this efficiency!

QR Factorization: A Quick Review

Recall that any matrix AA can be factored as:

A=QRA = QR

where QQ is orthogonal (QTQ=IQ^T Q = I) and RR is upper triangular.

To solve Ac=bA\mathbf{c} = \mathbf{b}:

  1. Compute QR=AQR = A

  2. Compute QTbQ^T \mathbf{b} (easy, just matrix-vector multiply)

  3. Solve Rc=QTbR\mathbf{c} = Q^T \mathbf{b} by back-substitution

The key property of QQ: Since QQ is orthogonal, it preserves lengths:

Qx2=x2for any vector x\|Q\mathbf{x}\|_2 = \|\mathbf{x}\|_2 \quad \text{for any vector } \mathbf{x}

This will be crucial for understanding the residual!

Givens Rotations: Building QR One Column at a Time

A Givens rotation is a simple 2×22 \times 2 orthogonal matrix that zeros out one element:

G=(cssc)G = \begin{pmatrix} c & s \\ -s & c \end{pmatrix}

where c2+s2=1c^2 + s^2 = 1 (so c=cosθc = \cos\theta and s=sinθs = \sin\theta for some angle θ\theta).

When we apply GG to a 2-vector:

(cssc)(ab)=(a2+b20)\begin{pmatrix} c & s \\ -s & c \end{pmatrix} \begin{pmatrix} a \\ b \end{pmatrix} = \begin{pmatrix} \sqrt{a^2 + b^2} \\ 0 \end{pmatrix}

By choosing c=a/a2+b2c = a/\sqrt{a^2 + b^2} and s=b/a2+b2s = b/\sqrt{a^2 + b^2}, we can zero out bb while preserving the length a2+b2\sqrt{a^2 + b^2}.

Using Givens to Zero a Column

To create zeros below the diagonal in column jj of a matrix:

Before column j:          After applying Givens:
     column j                  column j
        ↓                         ↓
[  × × × × ×  ]           [  × × × × ×  ]
[  0 × × × ×  ]           [  0 × × × ×  ]
[  0 0 × × ×  ]    →      [  0 0 × × ×  ]   ← R[j,j]
[  0 0 a × ×  ]           [  0 0 0 × ×  ]   ← zeroed!
[  0 0 b × ×  ]           [  0 0 0 × ×  ]   ← zeroed!

We apply a sequence of Givens rotations, each zeroing one entry below the diagonal.

Important: We apply the same rotations to the right-hand side b\mathbf{b}!

The Adaptive QR Algorithm

Now we can explain the algorithm. Here’s the beautiful idea:

Step 1: Process Columns One at a Time

Instead of building the full matrix and then factoring it, we:

  1. Start with just column 1

  2. Apply Givens rotations to make it upper triangular

  3. Add column 2, apply more Givens rotations

  4. Add column 3, apply more Givens rotations

  5. ...continue...

At each step, we have a partial QR factorization of the columns processed so far.

Step 2: Check the Residual (For Free!)

Here’s the magic. After processing jj columns, the transformed system looks like:

QjA=(Rj0),Qjb=(r1:jrj+1:end)Q_j A = \begin{pmatrix} R_j & * \\ 0 & * \end{pmatrix}, \quad Q_j \mathbf{b} = \begin{pmatrix} \mathbf{r}_{1:j} \\ \mathbf{r}_{j+1:\text{end}} \end{pmatrix}

The key theorem:

Proof 1

Let c^\hat{\mathbf{c}} be the solution with only jj nonzero coefficients, found by solving Rjc^1:j=r1:jR_j \hat{\mathbf{c}}_{1:j} = \mathbf{r}_{1:j}.

The residual is:

residual=bAc^\text{residual} = \mathbf{b} - A\hat{\mathbf{c}}

Multiply both sides by QjQ_j (which preserves length since QjQ_j is orthogonal):

Qjresidual=QjbQjAc^Q_j \cdot \text{residual} = Q_j \mathbf{b} - Q_j A \hat{\mathbf{c}}

The right-hand side is:

(r1:jrj+1:end)(Rj0)(c^1:j0)=(r1:jRjc^1:jrj+1:end)=(0rj+1:end)\begin{pmatrix} \mathbf{r}_{1:j} \\ \mathbf{r}_{j+1:\text{end}} \end{pmatrix} - \begin{pmatrix} R_j & * \\ 0 & * \end{pmatrix} \begin{pmatrix} \hat{\mathbf{c}}_{1:j} \\ 0 \end{pmatrix} = \begin{pmatrix} \mathbf{r}_{1:j} - R_j \hat{\mathbf{c}}_{1:j} \\ \mathbf{r}_{j+1:\text{end}} \end{pmatrix} = \begin{pmatrix} \mathbf{0} \\ \mathbf{r}_{j+1:\text{end}} \end{pmatrix}

The top part is zero because c^1:j\hat{\mathbf{c}}_{1:j} was chosen to satisfy Rjc^1:j=r1:jR_j \hat{\mathbf{c}}_{1:j} = \mathbf{r}_{1:j}.

Since QjQ_j preserves lengths:

residual2=Qjresidual2=rj+1:end2\|\text{residual}\|_2 = \|Q_j \cdot \text{residual}\|_2 = \|\mathbf{r}_{j+1:\text{end}}\|_2

Step 3: Stop When Accurate Enough

The algorithm becomes:

for j = 1, 2, 3, ...
    1. Add column j to the system
    2. Apply Givens rotations to zero entries below diagonal
    3. Apply same rotations to right-hand side b
    4. Check: is ||r_{j+1:end}|| < tolerance?
       - If YES: stop, n_opt = j
       - If NO: continue to j+1

Solve R_{n_opt} c = r_{1:n_opt} by back-substitution

We get the optimal nn and the solution simultaneously!

Why “Almost Banded” Matters

Remember the matrix structure:

┌─────────────────────────────────────────┐
│   K dense boundary rows                 │
├─────────────────────────────────────────┤
│        Banded part (bandwidth m)        │
└─────────────────────────────────────────┘

When we apply Givens rotations:

The dense boundary rows cause some “fill-in” (new nonzeros created), but this is limited and can be tracked efficiently.

Example: Second-Order BVP

Consider the problem:

u(x)+u(x)=f(x),u(1)=α,u(1)=βu''(x) + u(x) = f(x), \quad u(-1) = \alpha, \quad u(1) = \beta

The almost-banded structure arises because:

  1. Boundary rows: u(1)=αu(-1) = \alpha and u(1)=βu(1) = \beta involve all Chebyshev coefficients (dense)

  2. Interior rows: The relation between Chebyshev coefficients of uu'' and uu is local (banded)

For the ultraspherical spectral method (a sophisticated variant), the bandwidth is small:

This means we can solve BVPs with spectral accuracy in just O(n)O(n) operations!

Putting It All Together

The adaptive QR algorithm combines several ideas we’ve seen:

ConceptRole in Algorithm
Chebyshev polynomialsBasis functions with exponential convergence
Spectral collocationTurns ODE into linear system
Almost-banded structureEnables O(n)O(n) factorization
QR factorizationStable way to solve the system
Givens rotationsBuilds QR incrementally
Orthogonal transformations preserve normsGives us the residual for free

The result: An algorithm that automatically finds how many Chebyshev coefficients are needed, solves the system, and verifies the accuracy—all in a single pass through the columns!

Practical Impact

This algorithm (from Olver & Townsend, 2013) is implemented in software like Chebfun and enables:

Example 1 (Automatic Accuracy)

Consider solving u=e4xu'' = e^{4x} on [1,1][-1, 1] with u(±1)=0u(\pm 1) = 0.

With traditional spectral methods, you might:

  1. Try n=16n = 16, check residual: too big

  2. Try n=32n = 32, check residual: okay!

With adaptive QR:

  1. Process columns 1, 2, 3, ...

  2. At column 24, residual drops below 10-14

  3. Stop and return the solution

No wasted work, and the residual check was free!

Looking Ahead

This algorithm illustrates a powerful principle in scientific computing: structure-exploiting algorithms. By understanding the mathematical structure of the problem (almost-banded matrices, orthogonal transformations, coefficient decay), we can design algorithms that are both:

This same philosophy—understanding structure to design better algorithms—appears throughout numerical analysis and is what makes scientific computing both an art and a science.

References