Lecture 12: RNLA Case Study

Jamie Haddock

Case study: randomness as a tool for linear algebra

Three randomized algorithms from randomized numerical linear algebra (RNLA):

  1. Randomized Kaczmarz – solve Ax=b by projecting onto one randomly chosen row at a time.
  2. Randomized trace estimation – estimate \mathrm{tr}(A) using only products z^\top A z with a random vector z.
  3. Randomized matrix multiplication – approximate AB using a few randomly chosen column/row outer products.

The common story

Each method returns a random answer whose expected value is either the exact, or an improved answer.

Part A: Randomized Kaczmarz

Randomized Kaczmarz (RK) solves a consistent linear system Ax=b, where A\in\mathbb{R}^{m\times n} has rows a_1^\top,\dots,a_m^\top and x^\star denotes a solution. Starting from an initial guess x_0 (for instance x_0=0), at each iteration k the algorithm:

  1. Samples a row. Draw a row index I_k\in\{1,\dots,m\} at random from a fixed PMF p=(p_1,\dots,p_m), independently of all previous draws (sampling with replacement).
  2. Projects onto that equation. Move the current iterate to the closest point on the hyperplane \{x : a_{I_k}^\top x=b_{I_k}\}: x_{k+1}=x_k+\frac{b_{I_k}-a_{I_k}^\top x_k}{\|a_{I_k}\|^2}\,a_{I_k}.

After the kth step the iterate satisfies equation I_k exactly, so sampling the same row twice in a row (a collision) makes no progress.

Two standard choices of PMF are the row-norm PMF p_i=\|a_i\|^2/\|A\|_F^2 and the uniform PMF p_i=1/m.

Notation: the Frobenius norm

For a matrix A\in\mathbb{R}^{m\times n} with entries A_{ij} and rows a_1^\top,\dots,a_m^\top, the Frobenius norm is \|A\|_F=\sqrt{\sum_{i=1}^m\sum_{j=1}^n A_{ij}^2}\quad\Longrightarrow\quad \|A\|_F^2=\sum_{i=1}^m\|a_i\|^2 .

Collision probability in randomized Kaczmarz

Suppose two consecutive iterations sample rows I_1,I_2 independently, each \sim p (with replacement). What’s the probability the algorithm samples the same row twice in a row – a collision?

Theorem: Collision probability

P(I_1=I_2) = \sum_{i=1}^m p_i^2.

Proof: Condition on I_1, applying the law of total probability with partitioning on the value of I_1: P(I_1=I_2) = \sum_{i=1}^m P(I_2=i\mid I_1=i)\,P(I_1=i) = \sum_{i=1}^m P(I_2=i)\,p_i = \sum_{i=1}^m p_i^2, using independence of I_1,I_2 for P(I_2=i\mid I_1=i)=P(I_2=i)=p_i. \blacksquare

Why we care: if row i is sampled twice in a row, the second projection doesn’t update the iterate – the iterate already satisfies a_i^\top x=b_i exactly after the first projection, so the second step makes zero progress toward x^\star. A high collision probability means wasted iterations.

Fact

The collision probability \sum_i p_i^2 is minimized by the uniform distribution p_i=1/m (giving \sum_i p_i^2=1/m, the smallest possible value: 1=(\sum_i p_i)^2 \le m\sum_i p_i^2 by Cauchy-Schwarz), and maximized at 1 when all the probability mass sits on a single row. So concentrating the sampling distribution on a subset of rows comes at the cost of more wasted iterations.

Exercise 1: Bayes’ rule on collisions

Sample I_1,I_2 independently from a PMF p on \{1,\dots,m\}, and let C=\{I_1=I_2\} be the collision event.

  1. Use Bayes’ rule and the collision theorem to show that P(I_1=i\mid C)=\frac{p_i^2}{\sum_{j=1}^m p_j^2}.

  2. Now take m=3 and p=(\tfrac12,\tfrac13,\tfrac16). Compute P(C), and compute P(I_1=i\mid C) for i=1,2,3. Compare each with the unconditional probability P(I_1=i)=p_i. For which rows is the conditional probability larger, and for which is it smaller? Explain in words why a collision makes the heavily weighted row even more likely.

LOTUS: why the row-norm PMF?

A popular distribution over the rows is the row-norm PMF p_I(i) = \|a_i\|^2/\|A\|_F^2. We’ll see why now!

Fix the current iterate x_k and let v = x_k - x^\star. LOTUS lets us compute the expected value of any function of I straight from p_I – for instance E\!\left[\frac{(a_I^\top v)^2}{\|a_I\|^2}\right] = \sum_{i=1}^m \frac{(a_i^\top v)^2}{\|a_i\|^2}\, p_I(i).

Plugging in p_I(i) = \|a_i\|^2/\|A\|_F^2, the \|a_i\|^2 cancels: \sum_{i=1}^m \frac{(a_i^\top v)^2}{\|a_i\|^2}\cdot\frac{\|a_i\|^2}{\|A\|_F^2} = \frac{1}{\|A\|_F^2}\sum_{i=1}^m (a_i^\top v)^2 = \frac{\|Av\|^2}{\|A\|_F^2}.

If I were sampled uniformly instead, the \|a_i\|^2 would not cancel, and the final form wouldn’t be so nice and simple!

Linearity + LOTUS: average one-step progress of RK

Recall randomized Kaczmarz’s update, and note that the Pythagorean theorem (the update is an orthogonal projection) provides, \|x_{k+1}-x^\star\|^2 = \|v\|^2 - \frac{(a_I^\top v)^2}{\|a_I\|^2}. where v = x_k-x^\star.

\|v\|^2 is a constant given x_k – only I is random. By linearity, E\big[\|x_{k+1}-x^\star\|^2\big] = \|v\|^2 - E\!\left[\frac{(a_I^\top v)^2}{\|a_I\|^2}\right] = \|v\|^2 - \frac{\|Av\|^2}{\|A\|_F^2}, reusing the LOTUS computation from the previous slide!

Since \|Av\| \ge \sigma_{\min}(A)\|v\|, E\big[\|x_{k+1}-x^\star\|^2\big] \;\le\; \left(1 - \frac{\sigma_{\min}(A)^2}{\|A\|_F^2}\right)\|x_k-x^\star\|^2. Every step shrinks the expected squared error by a fixed factor – this a very nice (and very well-cited) result from Thomas Strohmer and Roman Vershynin in 2009!

Watching the bound play out over many steps

The one-step bound says the expected squared error shrinks by a fixed factor every step. Let’s actually run RK and watch \|x_k-x^\star\|^2 decrease and compare it to the bound.

Code
import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(151)
m, n = 20, 8
A = rng.standard_normal((m, n))
x_star = rng.standard_normal(n)
b = A @ x_star
row_norm_sq = (A ** 2).sum(axis=1)
p_row_norm = row_norm_sq / row_norm_sq.sum()
sigma_min = np.linalg.svd(A, compute_uv=False)[-1]
frob_sq = row_norm_sq.sum()

num_steps = 60
num_runs = 300
errors_sq = np.zeros((num_runs, num_steps + 1))

for r in range(num_runs):
    x = np.zeros(n)
    errors_sq[r, 0] = np.linalg.norm(x - x_star) ** 2
    idx_sequence = rng.choice(m, size=num_steps, p=p_row_norm)
    for t, i in enumerate(idx_sequence, start=1):
        a_i, b_i = A[i], b[i]
        x = x + (b_i - a_i @ x) / row_norm_sq[i] * a_i
        errors_sq[r, t] = np.linalg.norm(x - x_star) ** 2

mean_error_sq = errors_sq.mean(axis=0)
bound_curve = errors_sq[:, 0].mean() * (1 - sigma_min**2 / frob_sq) ** np.arange(num_steps + 1)

fig, ax = plt.subplots(figsize=(6, 3.5))
ax.semilogy(mean_error_sq, label="average of $\\|x_k-x^\\star\\|^2$ over runs")
ax.semilogy(bound_curve, "--", label="one-step bound, applied repeatedly")
ax.set_xlabel("step $k$")
ax.set_ylabel("squared error (log scale)")
ax.legend()
plt.tight_layout()
plt.show()

Recursing the randomized Kaczmarz bound

Recall our previous one-step bound, valid conditional on the current iterate x_k: E\big[\|x_{k+1}-x^\star\|^2 \mid x_k\big] \le (1-c)\,\|x_k-x^\star\|^2, \qquad c = \frac{\sigma_{\min}(A)^2}{\|A\|_F^2}.

That’s only a statement about one step, conditional on where we happen to be. To turn it into an unconditional guarantee after k steps, apply the law of iterated expectation repeatedly: \begin{aligned} E\big[\|x_2-x^\star\|^2\big] &= E\Big[E\big[\|x_2-x^\star\|^2 \mid x_1\big]\Big] \le E\big[(1-c)\|x_1-x^\star\|^2\big] \\&= (1-c)\,E\big[\|x_1-x^\star\|^2\big] \le (1-c)^2\|x_0-x^\star\|^2. \end{aligned}

Repeating this argument (this is called the tower of iterated expectation) gives, for every k, E\big[\|x_k-x^\star\|^2\big] \le (1-c)^k \|x_0-x^\star\|^2, – exactly the compounded bound we plotted before. Now we know why “applying the one-step bound repeatedly” is legitimate: it’s the law of total expectation, used k times.

Uniform vs. row-norm sampling: the distribution controls the rate

One can prove a similar (but worse) bound for the error for uniform sampling. Let’s run both samplers on a matrix whose row norms vary by orders of magnitude, and compare each empirical curve with its bound.

Code
rng = np.random.default_rng(151)
m, n = 40, 8
A = rng.standard_normal((m, n)) * (10 ** rng.uniform(-1, 1, size=(m, 1)))  # very unequal row norms
x_star = rng.standard_normal(n)
b = A @ x_star
row_norm_sq = (A ** 2).sum(axis=1)
frob_sq = row_norm_sq.sum()
sigma_min = np.linalg.svd(A, compute_uv=False)[-1]

samplers = {
    "row-norm": row_norm_sq / frob_sq,
    "uniform": np.ones(m) / m,
}
rates = {
    "row-norm": sigma_min**2 / frob_sq,
    "uniform": sigma_min**2 / (m * row_norm_sq.max()),
}

num_steps, num_runs = 400, 300
mean_err = {}
for name, p in samplers.items():
    errs = np.zeros((num_runs, num_steps + 1))
    for r in range(num_runs):
        x = np.zeros(n)
        errs[r, 0] = np.linalg.norm(x - x_star) ** 2
        for t, i in enumerate(rng.choice(m, size=num_steps, p=p), start=1):
            x = x + (b[i] - A[i] @ x) / row_norm_sq[i] * A[i]
            errs[r, t] = np.linalg.norm(x - x_star) ** 2
    mean_err[name] = errs.mean(axis=0)

e0 = np.linalg.norm(x_star) ** 2
ks = np.arange(num_steps + 1)
fig, ax = plt.subplots(figsize=(7, 4))
for name, color in [("row-norm", "C0"), ("uniform", "C1")]:
    ax.semilogy(mean_err[name], color=color, label=f"{name}: empirical")
    ax.semilogy(e0 * (1 - rates[name]) ** ks, "--", color=color, label=f"{name}: bound")
ax.set_ylim(1e-20, 1e2)
ax.set_xlabel("step $k$")
ax.set_ylabel("squared error (log scale)")
ax.legend()
plt.tight_layout()
plt.show()

Takeaways

  • The sampling distribution controls the guarantee: row-norm sampling gives a much better bound here.
  • The bounds are only upper bounds. On this matrix uniform sampling actually converges faster in practice – row-norm sampling spends most of its draws on a few huge rows, while uniform sampling touches every row equally often. A bound being better does not mean the algorithm is better on a given matrix.

Exercise 2: distinct rows

Run RK with replacement for k steps, and let D_k be the number of distinct rows sampled.

  1. Write D_k=\sum_{i=1}^m Y_i for indicator RVs Y_i, and say what event Y_i indicates.

  2. Use independence of the draws to compute P(Y_i=1), then use linearity to show E[D_k]=\sum_{i=1}^m\big(1-(1-p_i)^k\big). Check the case k=1.

Part B: Randomized trace estimation

Recall the randomized trace estimator: for A\in\mathbb R^{n\times n}, draw z\in\{-1,+1\}^n with i.i.d. Rademacher entries and set \tau = z^\top A z = \sum_{i,j} z_iz_j A_{ij}.

Example: unbiasedness of randomized trace estimation

We’ll now prove E[\tau]=\mathrm{tr}(A) – recall this is called unbiasedness.

By linearity, E[\tau] = \sum_{i,j}A_{ij}\,E[z_iz_j]. Split into diagonal and off-diagonal terms:

  • Diagonal (i=j): z_i^2=1 always, so E[z_i^2]=1.
  • Off-diagonal (i\ne j): z_i,z_j independent, so by the fact above, E[z_iz_j]=E[z_i]E[z_j] = 0\cdot 0=0 (Rademacher has mean 0).

E[\tau] = \sum_i A_{ii}\cdot 1 + \sum_{i\ne j} A_{ij}\cdot 0 = \sum_i A_{ii} = \mathrm{tr}(A).

Exercise 3: breaking the proof

Suppose that instead of independent entries, we use a single random sign s\in\{-1,+1\} (each with probability 1/2) and set z=s\mathbf 1=(s,s,\dots,s).

  1. Check that each z_i still has mean 0 and variance 1.

  2. Compute E[z^\top Az]. What do you get instead of \mathrm{tr}(A)?

  3. Which step of the unbiasedness proof fails, and why?

Example: continuous RV in randomized trace estimation

The unbiasedness proof only ever used E[z_i^2] = 1 \qquad\text{and}\qquad E[z_iz_j] = 0 \ (i\ne j), which followed from mean 0, variance 1, and independence – nothing about z_i being discrete. So a continuous z_i works exactly as well.

A continuous alternative

Instead of Rademacher entries z_i=\pm1, draw z_i i.i.d. from a continuous distribution with mean 0 and variance 1 – the standard normal N(0,1) (the Gaussian). The same proof works exactly, just a different distribution!

Code
rng = np.random.default_rng(151)
d = 50
A = rng.standard_normal((d, d)) + 5 * np.eye(d)
true_trace = np.trace(A)

num_samples = 20_000
Z_rad = rng.choice([-1.0, 1.0], size=(num_samples, d))  # discrete: Rademacher
Z_gauss = rng.standard_normal((num_samples, d))          # continuous: standard Gaussian

tau_rad = np.einsum('si,ij,sj->s', Z_rad, A, Z_rad)
tau_gauss = np.einsum('si,ij,sj->s', Z_gauss, A, Z_gauss)

print(f"true trace(A)                        = {true_trace:.4f}")
print(f"mean of tau, Rademacher z (discrete)  = {tau_rad.mean():.4f}")
print(f"mean of tau, Gaussian z (continuous)  = {tau_gauss.mean():.4f}")
true trace(A)                        = 250.0783
mean of tau, Rademacher z (discrete)  = 249.9342
mean of tau, Gaussian z (continuous)  = 249.5031

How much variance? The Rademacher estimator

Unbiased is not enough – a single sample of \tau could be far from \mathrm{tr}(A). Let A be symmetric, so A_{ij}=A_{ji} and \tau=\sum_i A_{ii}z_i^2+2\sum_{i<j}A_{ij}z_iz_j=\mathrm{tr}(A)+2\sum_{i<j}A_{ij}z_iz_j for Rademacher z (since z_i^2=1). The first term is a constant, so only the cross terms contribute to the variance.

Variance of the Rademacher estimator. For symmetric A, we have \mathrm{Var}(\tau)=4\,\mathrm{Var}\Big(\sum_{i<j}A_{ij}\,z_iz_j\Big).

Let W_{ij}=z_iz_j for i<j. We check that these are uncorrelated with variance 1:

  • Mean zero. E[W_{ij}]=E[z_i]E[z_j]=0 by independence.
  • Variance one. \mathrm{Var}(W_{ij})=E[z_i^2z_j^2]-0=1, since z_i^2=z_j^2=1.
  • Pairwise uncorrelated. For distinct pairs \{i,j\}\ne\{k,l\}, the product W_{ij}W_{kl} has at least one index appearing exactly once. On the other hand, if j=k then W_{ij}W_{jl}=z_iz_j^2z_l=z_iz_l with i\neq l. So E[W_{ij}W_{kl}] is a product of terms including E[z_m]=0, and \mathrm{Cov}(W_{ij},W_{kl})=0.

(They are not mutually independent: W_{12}W_{13}W_{23}=1 always. We only need uncorrelated, since variances add for uncorrelated summands.)

So variances of the sum add: \mathrm{Var}(\tau)=4\sum_{i<j}A_{ij}^2\,\mathrm{Var}(W_{ij})=4\sum_{i<j}A_{ij}^2=2\sum_{i\ne j}A_{ij}^2, where the last step uses \sum_{i\ne j}A_{ij}^2=2\sum_{i<j}A_{ij}^2 by symmetry.

Diagonal matrices

If A is diagonal, \mathrm{Var}(\tau)=0: every draw of z gives exactly \mathrm{tr}(A). The variance only measures the off-diagonal mass.

Exercise 4: variance of the Gaussian estimator

Let A be symmetric and z have i.i.d. N(0,1) entries. You may use E[z_i^4]=3.

  1. Show \mathrm{Var}(z_i^2)=2.

  2. Write \tau=\sum_i A_{ii}z_i^2+2\sum_{i<j}A_{ij}z_iz_j. Argue that all terms are pairwise uncorrelated, and conclude that \mathrm{Var}(\tau_{\text{Gauss}})=2\|A\|_F^2.

Comparing the two estimators

Since \|A\|_F^2=\sum_i A_{ii}^2+\sum_{i\ne j}A_{ij}^2, the two variances differ by \mathrm{Var}(\tau_{\text{Gauss}})-\mathrm{Var}(\tau_{\text{Rad}})=2\sum_i A_{ii}^2\;\ge 0. Rademacher always has smaller variance, and it wins by the most when A has a heavy diagonal. If A has zero diagonal, the two are identical. Let’s check on a diagonal-dominant matrix and on one with zero diagonal.

Code
rng = np.random.default_rng(151)
d = 30
G = rng.standard_normal((d, d))
S = (G + G.T) / 2                                   # symmetric, generic diagonal
A_diag_heavy = S + 5 * np.eye(d)                    # diagonal-dominant
A_zero_diag = S - np.diag(np.diag(S))               # zero diagonal

num_samples = 200_000
for name, M in [("diagonal-dominant", A_diag_heavy), ("zero diagonal", A_zero_diag)]:
    Z_rad = rng.choice([-1.0, 1.0], size=(num_samples, d))
    Z_gauss = rng.standard_normal((num_samples, d))
    tau_rad = np.einsum('si,ij,sj->s', Z_rad, M, Z_rad)
    tau_gauss = np.einsum('si,ij,sj->s', Z_gauss, M, Z_gauss)
    off = (M ** 2).sum() - (np.diag(M) ** 2).sum()
    print(name)
    print(f"  Rademacher: empirical var {tau_rad.var():9.2f}   formula {2*off:9.2f}")
    print(f"  Gaussian:   empirical var {tau_gauss.var():9.2f}   formula {2*(M**2).sum():9.2f}")
diagonal-dominant
  Rademacher: empirical var    978.64   formula    983.70
  Gaussian:   empirical var   2578.98   formula   2595.05
zero diagonal
  Rademacher: empirical var    984.84   formula    983.70
  Gaussian:   empirical var    981.17   formula    983.70

Part C: Randomized matrix multiplication

Let A\in\mathbb R^{m\times n} and B\in\mathbb R^{n\times q}. Write a_j for the j-th column of A and b_j^\top for the j-th row of B. Then AB=\sum_{j=1}^n a_j b_j^\top, a sum of n rank-one outer products.

Computing all of them is expensive, so sample:

  • draw J\sim p on \{1,\dots,n\},
  • form X=\dfrac{1}{p_J}\,a_Jb_J^\top,
  • average c i.i.d. copies: \hat C=\dfrac1c\sum_{t=1}^c X^{(t)}.

The factor 1/p_J is importance weighting: it compensates for rows that are sampled more often.

Unbiasedness of randomized matrix multiplication

X is a matrix-valued function h(J) of the discrete RV J, and by LOTUS: E[X]=\sum_{j=1}^n \frac{1}{p_j}\,a_jb_j^\top\;p_j=\sum_{j=1}^n a_jb_j^\top=AB.

By linearity, E[\hat C]=\frac1c\sum_t E[X^{(t)}]=AB as well.

  • The p_j cancels – the same cancellation trick as for the row-norm PMF in Kaczmarz.
  • We need p_j>0 wherever a_jb_j^\top\ne0; otherwise those terms are never sampled and the estimator is biased.
  • Unbiasedness holds for every valid p, so the choice of p will only affect the variance.

(Not on HW) Exercise 5: variance of randomized matrix multiplication

Let X=\frac1{p_J}a_Jb_J^\top as above, with X_{ik}=\frac{a_{ij}b_{jk}}{p_j} conditional on J=j (here a_{ij} is the i-th entry of column a_j and b_{jk} the k-th entry of row b_j^\top).

  1. Use LOTUS and law of total expectation to show E[X_{ik}^2]=\sum_{j}\dfrac{a_{ij}^2b_{jk}^2}{p_j}.

  2. Deduce the variance of the (i,k) entry of the averaged estimator \hat C: \mathrm{Var}(\hat C_{ik})=\frac1c\left(\sum_{j}\frac{a_{ij}^2b_{jk}^2}{p_j}-(AB)_{ik}^2\right).

The best sampling distribution

Part (b) of the exercise above can be summed to show: E\|AB-\hat C\|_F^2=\frac1c\left(\sum_{j}\frac{w_j^2}{p_j}-\|AB\|_F^2\right), where w_j=\|a_j\|\|b_j\|. Only the sum depends on p, so minimize \sum_j w_j^2/p_j.

Apply Cauchy-Schwarz, the same trick as in the collision fact: \Big(\sum_j w_j\Big)^2=\Big(\sum_j \tfrac{w_j}{\sqrt{p_j}}\cdot\sqrt{p_j}\Big)^2 \le\Big(\sum_j\tfrac{w_j^2}{p_j}\Big)\Big(\sum_j p_j\Big)=\sum_j\frac{w_j^2}{p_j}. Equality holds exactly when \frac{w_j}{\sqrt{p_j}}\propto\sqrt{p_j}, i.e. when \boxed{\,p_j=\frac{\|a_j\|\|b_j\|}{\sum_l\|a_l\|\|b_l\|}\,}, \qquad\text{giving}\qquad E\|AB-\hat C\|_F^2=\frac1c\Big(\big(\textstyle\sum_j w_j\big)^2-\|AB\|_F^2\Big).

Compared with uniform sampling

For p_j=1/n the sum is n\sum_j w_j^2\ \ge\ (\sum_j w_j)^2 (Cauchy-Schwarz again), with equality only when all w_j are equal. When the w_j are very unequal, uniform sampling wastes most draws on unimportant terms, and optimal sampling can be far better.

Connection to Kaczmarz. With B=A^\top we get b_j=a_j, so w_j=\|a_j\|^2 and the optimal PMF is p_j\propto\|a_j\|^2: the same squared-norm weighting as the row-norm PMF in RK. (Here a_j is a column of A, i.e. a row of A^\top.) In both algorithms, sample in proportion to how much each piece contributes.