Qu

Math & linear algebra

QR & SVD

QR and SVD decompositions, and a least-squares solve.

catalog/qu_qr_svd.qu

`qr`/`svd` (like `ols_model`/`findpeaks`/`kalman_init`) return a `Value::Model` carrying their named results, rather than a MATLAB-style tuple -- Qu has no multi-return/tuple-unpacking assignment.

What it exercises

How it works

Setup — a small, hand-checkable matrix

A = [1, 1; 1, 2; 1, 3; 1, 4]     # (4,2) design matrix: column of ones + x
b = [6, 5, 7, 10]

QR decomposition

qrf = qr(A)
print("Q = {qrf.q}")
print("R = {qrf.r}")
recon = qrf.q * qrf.r
print("max |A - Q*R| = {max(abs(A - recon)):.2e}")

SVD

s = svd(A)
print("singular values = {s.s}")
rank_est = length(where(s.s > 1e-9 * max(s.s)))
print("estimated rank = {rank_est}")

Least squares via the QR factors

x = R \ (Q' * b) solves min ||A*x - b||^2

Qtb = qrf.q' * b
x = qrf.r \ Qtb
print("least-squares fit: intercept={x[0]:.4f}, slope={x[1]:.4f}")

resid = A * x - b
print("residual norm = {norm(resid):.4f}")

Plot the fit

xs = A[:, 1]
plot(xs, b, marker="o", label="data")
fit_line = A * x
plot(xs, fit_line, label="least-squares fit", color="orange")
xlabel("x")
ylabel("y")
title("QR-based least squares")
legend()
show plot

Results

printed
Q = [0.5, -0.67082; 0.5, -0.223607; 0.5, 0.223607; 0.5, 0.67082]
R = [2, 5; 0, 2.236068]
max |A - Q*R| = 2.44e-15
singular values = [5.779379, 0.773809]
estimated rank = 2
least-squares fit: intercept=3.5000, slope=1.4000
residual norm = 2.0494
5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 1.0 1.5 2.0 2.5 3.0 3.5 4.0 x=1, y=6 x=2, y=5 x=3, y=7 x=4, y=10 QR-based least squares x y data least-squares fit
Drawn by running the program above.

The whole program

Runnable as it stands: qu run catalog/qu_qr_svd.qu

# qu_qr_svd.qu — QR and SVD decompositions, and a least-squares solve.

#

# `qr`/`svd` (like `ols_model`/`findpeaks`/`kalman_init`) return a

# `Value::Model` carrying their named results, rather than a MATLAB-style

# tuple -- Qu has no multi-return/tuple-unpacking assignment.

#

# Constructs exercised (all real, verified-working builtins):

#   qr(A)  -> Model(.q, .r),    A = Q*R

#   svd(A) -> Model(.u, .s, .vt), A = U * diag(s) * V'

#   the `\` left-division (linear solve) operator for a least-squares fit


backend auto

#%% Setup — a small, hand-checkable matrix

A = [1, 1; 1, 2; 1, 3; 1, 4]     # (4,2) design matrix: column of ones + x

b = [6, 5, 7, 10]

#%% QR decomposition

qrf = qr(A)
print("Q = {qrf.q}")
print("R = {qrf.r}")
recon = qrf.q * qrf.r
print("max |A - Q*R| = {max(abs(A - recon)):.2e}")

#%% SVD

s = svd(A)
print("singular values = {s.s}")
rank_est = length(where(s.s > 1e-9 * max(s.s)))
print("estimated rank = {rank_est}")

#%% Least squares via the QR factors

# x = R \ (Q' * b)  solves min ||A*x - b||^2

Qtb = qrf.q' * b
x = qrf.r \ Qtb
print("least-squares fit: intercept={x[0]:.4f}, slope={x[1]:.4f}")

resid = A * x - b
print("residual norm = {norm(resid):.4f}")

#%% Plot the fit

xs = A[:, 1]
plot(xs, b, marker="o", label="data")
fit_line = A * x
plot(xs, fit_line, label="least-squares fit", color="orange")
xlabel("x")
ylabel("y")
title("QR-based least squares")
legend()
show plot