Frontmatter

If you are publishing this notebook on the web, you can set the parameters below to provide HTML metadata. This is useful for search engines and social media.

HW 4 for CS 4220

You may (and probably should) talk about problems with the each other, with the TAs, and with me, providing attribution for any good ideas you might get. Your final write-up should be your own.

md"""
# HW 4 for CS 4220

You may (and probably should) talk about problems with the each other, with the TAs, and with me, providing attribution for any good ideas you might get. Your final write-up should be your own.
"""
178 μs
using LinearAlgebra
378 μs
using Plots
4.1 s

1. Dear colleagues

A smooth function on $[-1, 1]$ can be approximated by a Chebyshev series of up to degree $d$ in $O(d^2)$ time using point values on a Chebyshev mesh (we can actually do this in $O(d \log d)$ using FFT tricks, but won't bother here):

$$\begin{align*} \hat{f}(x) &= \sum_{j=0}^d a_j T_j(x) \\ a_j &= \frac{2}{d+1} \sum_{j=0}^d f(x_j) T(x_j) \\ x_j &= \cos\left( \frac{\pi (j+1/2)}{d+1} \right) \end{align*}$$

md"""
#### 1. Dear colleagues

A smooth function on $[-1, 1]$ can be approximated by a Chebyshev series of up to degree $d$ in $O(d^2)$ time using point values on a Chebyshev mesh (we can actually do this in $O(d \log d)$ using FFT tricks, but won't bother here):

$$\begin{align*}
\hat{f}(x) &= \sum_{j=0}^d a_j T_j(x) \\
a_j &= \frac{2}{d+1} \sum_{j=0}^d f(x_j) T(x_j) \\
x_j &= \cos\left( \frac{\pi (j+1/2)}{d+1} \right)
\end{align*}$$
"""
215 μs
fit_cheb_series (generic function with 1 method)
function fit_cheb_series(d, f)

# Mesh points and function values
xj = [cos(π*(j+0.5)/(d+1)) for j = 0:d]
fj = f.(xj)

# Space for Chebyshev evals on mesh points
T = zeros(d+1,3)
T[:,1] .= 1.0
T[:,2] = xj

# Compute coefficients
a = zeros(d+1)
if d >= 0 a[1] = sum(fj) end
if d >= 1 a[2] = xj'*fj end
for j = 2:d
j0 = mod(j, 3) + 1
jm1 = mod(j+2,3) + 1
jm2 = mod(j+1,3) + 1
T[:,j0] = 2*xj .* T[:,jm1] - T[:,jm2]
a[j+1] = T[:,j0]'*fj
end
a .*= 2/(d+1)
a[1] /= 2

a
end
2.2 ms

It is convenient to have a corresponding function to evaluate a Chebyshev series, too.

md"""
It is convenient to have a corresponding function to evaluate a Chebyshev series, too.
"""
138 μs
eval_cheb_series (generic function with 1 method)
function eval_cheb_series(a, x)
d = length(a)-1
px = 0.0
T = [1.0; x; 0.0]
if d >= 0 px = a[1] end
if d >= 1 px += a[2]*x end
for j = 2:d
j0 = mod(j, 3) + 1
jm1 = mod(j+2,3) + 1
jm2 = mod(j+1,3) + 1
T[j0] = 2*x*T[jm1] - T[jm2]
px += a[j+1]*T[j0]
end
end
1.1 ms

A nice visual check is to compare $\hat{f}(x)$ and $f(x)$ for some smooth test function.

md"""
A nice visual check is to compare $\hat{f}(x)$ and $f(x)$ for some smooth test function.
"""
120 μs
let
f(x) = sin(5*exp(x))
a = fit_cheb_series(15, f)
xx = range(-1.0, 1.0, length=100)
pxx = [eval_cheb_series(a, x) for x in xx]
fxx = f.(xx)
plot(xx, pxx, label="f")
plot!(xx, fxx, linestyle=:dash, label="approx")
end
968 ms

The colleague matrix associated with $\hat{f}(x)$ is the $d$-by-$d$ real matrix

$$C_T = \frac{1}{2} (B - e_1 v^T)$$

where

$$B = \begin{bmatrix} 0 & 1 \\ 1 & 0 & 1 \\ & 1 & 0 & 1 \\ & & \ddots & \ddots & \ddots \\ & & & 1 & 0 & 1 \\ & & & & 2 & 0 \end{bmatrix}$$

and $v^T = \begin{bmatrix} a_{d-1} & a_{d-2} & \ldots & a_0 \end{bmatrix}/a_d$ is the row vector of coefficients for $\hat{f}(x)/a_d$, save for the highest-order coefficient which has been scaled to 1 – we will assume $a_d \neq 0$.

Let $T(x)$ denote the length $d$ vector with entries $T_{d-1}(x)$ through $T_{0}(x)$. We claim that $\hat{f}(x) = 0$ iff $(C_T - xI) T(x) = 0$, i.e. $T(x)$ is an eigenvector of $C_T$ associated with the eigenvalue $x$. If $f(x)$ is well approximated by $\hat{f}(x)$ on $[-1,1]$, this gives a good way of estimating the zeros of $f(x)$ on $[-1,1]$ as well.

Questions
  1. Why is $(C_T - xI) T(x) = 0$ when $\hat{f}(x) = 0$?

  2. Write a function root_cheb_series(a) that computes all the real zeros of $\hat{f}(x)$ between $[-1, 1]$ by solving an eigenvalue problem with the colleague matrix. Note that the Julia eigvals routine computes the eigenvalues of a matrix.

Hint for 1: Note that $(B/2-xI) T(x) = -e_1 T_d(x)/2$ and $v^T T(x) = \hat{f}(x)/a_d - T_d(x)$. Conclude that in general $(C_T-xI) T(x) = -e_1 \hat{f}(x)/(2a_d)$.

Hint for 2: You may want to test using the example function above, which has known zeros at $x_j = \log(j\pi/5)$ for $j = 1, 2, 3, 4$. Assuming that the real zeros are returned in sorted order, you should get relative errors below $10^{-6}$ for all the computed roots reported with the disabled code snippet below.

md"""
The *colleague* matrix associated with $\hat{f}(x)$ is the $d$-by-$d$ real matrix

$$C_T = \frac{1}{2} (B - e_1 v^T)$$

where

$$B = \begin{bmatrix}
0 & 1 \\
1 & 0 & 1 \\
& 1 & 0 & 1 \\
& & \ddots & \ddots & \ddots \\
& & & 1 & 0 & 1 \\
& & & & 2 & 0
39.4 ms
let
f(x) = sin(5*exp(x))
a = fit_cheb_series(20, f)
x_cheb = root_cheb_series(a)
x_true = [log(j*π/5) for j = 1:4]
abs.(x_cheb-x_true)./abs.(x_true)
end
---

2. Twirling, twirling

Suppose $A$ is a real matrix with a complex conjugate pair of eigenvalues $\lambda_{\pm} = \alpha \pm i\beta$ and corresponding eigenvectors $z_{\pm} = x \pm iy$.

  1. Show that

$$A \begin{bmatrix} x & y \end{bmatrix} = \begin{bmatrix} x & y \end{bmatrix} \begin{bmatrix} \alpha & \beta \\ -\beta & \alpha \end{bmatrix}$$

  1. Writing the polar form $\alpha + i \beta = \rho \exp(i\theta)$, observe that

$$A^k \begin{bmatrix} x & y \end{bmatrix} \begin{bmatrix} c_1 \\ c_2 \end{bmatrix} = \rho^k \begin{bmatrix} x & y \end{bmatrix} \begin{bmatrix} \cos(k\theta) & \sin(k\theta) \\ -\sin(k\theta) & \cos(k\theta) \end{bmatrix} \begin{bmatrix} c_1 \\ c_2 \end{bmatrix}$$

Note: This shows that if $A$ has a complex conjugate pair of eigenvalues of largest magnitude (and all other eigenvalues are smaller in magnitude), then iterates of the power method will asymptotically "spin around" by an angle of $\theta$ at each step in a two-dimensional space spanned by the real and imaginary parts of the associated eigenvector.

md"""
#### 2. Twirling, twirling

Suppose $A$ is a real matrix with a complex conjugate pair of eigenvalues $\lambda_{\pm} = \alpha \pm i\beta$ and corresponding eigenvectors $z_{\pm} = x \pm iy$.

1. Show that

$$A \begin{bmatrix} x & y \end{bmatrix} = \begin{bmatrix} x & y \end{bmatrix} \begin{bmatrix} \alpha & \beta \\ -\beta & \alpha \end{bmatrix}$$

2. Writing the polar form $\alpha + i \beta = \rho \exp(i\theta)$, observe that

$$A^k \begin{bmatrix} x & y \end{bmatrix} \begin{bmatrix} c_1 \\ c_2 \end{bmatrix} =
\rho^k \begin{bmatrix} x & y \end{bmatrix} \begin{bmatrix} \cos(k\theta) & \sin(k\theta) \\ -\sin(k\theta) & \cos(k\theta) \end{bmatrix} \begin{bmatrix} c_1 \\ c_2 \end{bmatrix}$$
3.6 ms

3. Bidiagonal Tikhonov

In the lecture notes, we claimed that solving an upper bidiagonal system with Tikhonov regularization in $O(n)$ was "left as an exercise for the student." This is perhaps an overly difficult exercise, so here we provide a routine tik_bd_solve that solves the system $(B^T B + \sigma^2 I) x = B^T f$ where $B$ is a square upper bidiagonal matrix with diagonal entries $d_1, \ldots, d_n$ and superdiagonal entries $e_1, \ldots, e_{n-1}$, i.e.

$$B = \begin{bmatrix} d_1 & e_1 \\ & d_2 & e_2 \\ && \ddots & \ddots \\ &&& d_{n-1} & e_{n-1} \\ &&&& d_n \end{bmatrix}$$

md"""
#### 3. Bidiagonal Tikhonov

In the lecture notes, we claimed that solving an upper bidiagonal system with Tikhonov regularization in $O(n)$ was "left as an exercise for the student." This is perhaps an overly difficult exercise, so here we provide a routine `tik_bd_solve` that solves the system $(B^T B + \sigma^2 I) x = B^T f$ where $B$ is a square upper bidiagonal matrix with diagonal entries $d_1, \ldots, d_n$ and superdiagonal entries $e_1, \ldots, e_{n-1}$, i.e.

$$B = \begin{bmatrix} d_1 & e_1 \\ & d_2 & e_2 \\ && \ddots & \ddots \\ &&& d_{n-1} & e_{n-1} \\ &&&& d_n \end{bmatrix}$$
"""
195 μs
tik_bd_solve (generic function with 1 method)
function tik_bd_solve(d, e, σ, f)
n = length(d)
dnew = copy(d)
enew = copy(e)
fnew = zeros(2*n)
fnew[n+1:2*n] = f

eprev = 0.0
for j = 1:n
# First reflection to zero superdiagonal in second block
dnewj = sqrt(σ^2 + eprev^2)
if j > 1
# Apply [σ; eprev] reflector to rhs
u1 = -eprev^2/(σ + dnewj) # = σ - dnewj
ν = u1^2 + eprev^2
d = 2.0*(u1*fnew[j] + eprev*fnew[n+j-1])/ν
fnew[j] -= d*u1
fnew[n+j-1] -= d*eprev
end

# Second reflection for diagonal
v1 = dnewj
v2 = dnew[j]
ρ = sqrt(v1^2 + v2^2)
v1 += ρ
ν = v1^2 + v2^2
dnew[j] = -ρ

# Apply second reflector to rhs
d = 2.0*(v1*fnew[j] + v2*fnew[n+j])/ν
2.1 ms

We can illustrate the behavior of the solver with a small example:

md"""
We can illustrate the behavior of the solver with a small example:
"""
107 μs
let
d = randn(4)
e = randn(3)
f = randn(4)
B0 = Bidiagonal(d, e, :U)
x = tik_bd_solve(d, e, 5e-1, f)
[B0; 5e-1*I]\[f; 0*f]-x
end
110 ms

Julia provides a wrapper to the LAPACK routine dgebrd (or the single precision equivalent), which computes a bidiagonal reduction in packed form. We provide a slightly gentler wrapper that computes $Q^T A P = B$ and provides routines to apply $Q$, $Q^T$, $P$, and $P^T$ as well as the diagonal and superdiagonal elements of $B$ packed as a bidiagonal matrix.

md"""
Julia provides a wrapper to the LAPACK routine [`dgebrd`](https://netlib.org/lapack/explore-html/dd/d9a/group__double_g_ecomputational_ga9c735b94f840f927f8085fd23f3ee2e6.html) (or the single precision equivalent), which computes a bidiagonal reduction in packed form. We provide a slightly gentler wrapper that computes $Q^T A P = B$ and provides routines to apply $Q$, $Q^T$, $P$, and $P^T$ as well as the diagonal and superdiagonal elements of $B$ packed as a bidiagonal matrix.
"""
171 μs
bidiagonal_reduction (generic function with 1 method)
function bidiagonal_reduction(A)
m, n = size(A)
d = zeros(n)
e = zeros(n-1)
tauq = zeros(n)
taup = zeros(n)
A, d, e, tauq, taup = LAPACK.gebrd!(copy(A))

function houseQ!(k, v)
w = v[k] + A[k+1:m,k]'*v[k+1:m]
v[k] -= tauq[k]*w
v[k+1:m] -= tauq[k]*w*A[k+1:m,k]
end

function houseP!(k, v)
w = v[k+1] + A[k,k+2:n]'*v[k+2:n]
v[k+1] -= taup[k]*w
v[k+2:n] -= taup[k]*w*A[k,k+2:n]
end

function mulQ(v)
v = copy(v)
for k = n:-1:1
end
v
end

function mulP(v)
v = copy(v)
for k = n-1:-1:1
end
v
end

function mulQT(v)
6.0 ms

It's always good to have a sanity check; here's one for our routine above.

md"""
It's always good to have a sanity check; here's one for our routine above.
"""
110 μs
2.945754565036118e-15
let
A = rand(20,10)
x = rand(10)
d, e, mulQ, mulQT, mulP, mulPT = bidiagonal_reduction(A)
mulB(x) = [Bidiagonal(d, e, :U)*x; zeros(10)]

norm(A*x-mulQ(mulB(mulPT(x))))
end
219 ms

Your job is to

  1. Write a routine bd_solve(A, b, σ) that solves the Tikhonov regularized least squares problem by bidiagonal reduction followed by a call to tik_bd_solve.

  2. Write a routine lcurve(A, b, σs) that computes the solution norm $\|x(\sigma)\|$ and the residual norm $\|r(\sigma)\|$ for each $\sigma$ in the vector σs. Your code will take $O(mn^2)$ time for the initial bidiagonal reduction, but then should take $O(n)$ time per $\sigma$.

You may use the following tester codes to check your work.

md"""
Your job is to

1. Write a routine `bd_solve(A, b, σ)` that solves the Tikhonov regularized least squares problem by bidiagonal reduction followed by a call to `tik_bd_solve`.

2. Write a routine `lcurve(A, b, σs)` that computes the solution norm $\|x(\sigma)\|$ and the residual norm $\|r(\sigma)\|$ for each $\sigma$ in the vector `σs`. Your code will take $O(mn^2)$ time for the initial bidiagonal reduction, but then should take $O(n)$ time per $\sigma$.

You may use the following tester codes to check your work.
"""
316 μs
let
σ = 1e-1
A = rand(20,10)
b = rand(20)
xref = [A; σ*I]\[b; zeros(10)]
x = bd_solve(A, b, σ)
norm(x-xref)/norm(xref)
end
---
let
σs = 10.0.^(0.0:-0.1:-10)
A = rand(20,10)
A[:,10] = A[:,9] + 1e-6*randn(20)
b = A*rand(10) + 1e-3*randn(20)
xnorms, rnorms = lcurve(A, b, σs)
xnorms_ref = 0.0*σs
rnorms_ref = 0.0*σs
for j = 1:length(σs)
x = [A; σs[j]*I]\[b; zeros(10)]
xnorms_ref[j] = norm(x)
rnorms_ref[j] = norm(A*x-b)
end
norm((xnorms-xnorms_ref)./xnorms_ref, Inf),
end
---