QR Decomposition as a Recurrence System
To provide a recurrence-equation formulation for QR decomposition in the same style as the matrix multiplication (matmul) example, we need to express the QR decomposition algorithm in a way that captures its data dependencies and computations as a system of equations. Let’s derive this step-by-step, ensuring the formulation aligns with the Domain Flow Architecture (DFA) methodology and matches the provided matmul structure.
Understanding QR Decomposition
Section titled “Understanding QR Decomposition”QR decomposition factorizes a matrix (of size , where ) into a matrix with orthonormal columns and an upper triangular matrix , such that . Modified Gram-Schmidt (MGS) — the numerically stable Gram-Schmidt variant used here — computes the reduced (thin) factorization: (the orthonormal vectors ) and upper triangular. (Completing to a full orthogonal matrix is a separate step MGS does not perform.) We focus on MGS for an matrix, as it lends itself to a regular computational structure.
Modified Gram-Schmidt Overview
Section titled “Modified Gram-Schmidt Overview”For an input matrix with columns , MGS iteratively computes:
- Orthogonal vectors (columns of ).
- Upper triangular entries (elements of ).
The process can be summarized as:
- For each column to :
- Initialize .
- For each to :
- Compute (projection coefficient).
- Update (orthogonalization).
- Compute .
- Normalize .
Formulating a Recurrence System for QR Decomposition
Section titled “Formulating a Recurrence System for QR Decomposition”To express this as a recurrence system, we need:
- Index space: Define the iteration domain over indices, similar to in matmul.
- Variables: Define arrays for inputs (), intermediate results (), and outputs (, ).
- Equations: Express computations as recurrence equations. (Note: some MGS dependencies are affine, not uniform — the reduction broadcasts and the previous-column read — as the caution above explains.)
- Domain constraints: Specify bounds for indices, e.g., , .
Step 1: Define the Index Space
Section titled “Step 1: Define the Index Space”The MGS algorithm involves:
- Iterating over columns to (for each column of ).
- For each , iterating over rows to (for vector elements).
- For orthogonalization, iterating over previous columns to (for projections).
The primary computation involves three indices:
- : Row index (0 to ).
- : Column index being processed (0 to ).
- : Index for orthogonalization steps (0 to ).
This suggests a 3D index space , similar to the matmul SURE, but we’ll adjust the role of .
Step 2: Define Variables
Section titled “Step 2: Define Variables”We define the following arrays:
- Input: , the input matrix of size .
- Intermediates:
- : Intermediate vectors during orthogonalization.
- : Coefficients for .
- Outputs:
- : Orthogonal matrix (columns ).
- : Upper triangular matrix .
Step 3: Express Computations as Recurrence Equations
Section titled “Step 3: Express Computations as Recurrence Equations”We break down the MGS steps into recurrence equations (with the affine dependencies noted above).
-
Initialize : For each column , the initial vector is the column of .
Here, indicates the start of orthogonalization.
-
Compute : For each , and for each to , compute the projection:
This is a reduction over , which we’ll handle by introducing an auxiliary array to compute the sum incrementally.
-
Update : Update the vector after each projection:
For to .
-
Compute : After orthogonalization (at ):
Another reduction over .
-
Normalize :
Step 4: Handle Reductions
Section titled “Step 4: Handle Reductions”The reductions (sums for and ) require iterating over . To make dependencies uniform, we introduce partial sum arrays:
- : Partial sum for .
- : Partial sum for the norm computation.
For :
For :
Step 5: Define the Domain
Section titled “Step 5: Define the Domain”The index space is:
- : Rows of the matrix ().
- : Columns of the matrix ().
- : Orthogonalization step (), since we process up to projections, and is used for the norm and normalization.
Final Formulation (v2 confluence DSL)
Section titled “Final Formulation (v2 confluence DSL)”The derivation above sketches the algorithm with case splits (if i = 0),
partial-domain equations (v(i,j,0) = a(i,j), r(k,j,k) = ...), and
mixed-rank outputs (q(i,j) is 2D while v(i,j,k) is 3D). None of that
survives contact with the Domain Flow discipline: every equation must be a
total recurrence over the shared domain, base cases must enter through
oriented input confluences on the halo, and results must leave through
oriented output confluences on faces of the domain. The refined,
executable formulation (qr.sure):
M = 3; N = 3;
system ((i,j,k) | 0 <= i < M, 0 <= j < N, 0 <= k < N, k <= j) { v(i,j,k) = v(i,j,k-1) - srp(M-1,j,k-1) * qhat(i,k-1,k-1); // orthogonalize srp(i,j,k) = srp(i-1,j,k) + qhat(i,k,k) * v(i,j,k); // r_kj reduction snorm(i,j,k) = snorm(i-1,j,k) + v(i,j,k) * v(i,j,k); // ||v_j||^2 reduction qhat(i,j,k) = v(i,j,k) / sqrt(snorm(M-1,j,k)); // normalize}
input A[M][N] ((i,j,k) | 0 <= i < M, 0 <= j < N, k = -1) : v(i,j,k) = A[i][j];input ((i,j,k) | M-1 <= i < M, 0 <= j < N, k = -1) : srp(i,j,k) = 0;input ((i,j,k) | 0 <= j < N, 0 <= k < N, k <= j, i = -1) : srp(i,j,k) = 0;input ((i,j,k) | 0 <= j < N, 0 <= k < N, k <= j, i = -1) : snorm(i,j,k) = 0;input ((i,j,k) | 0 <= i < M, -1 <= k < 0, j = -1) : qhat(i,j,k) = 0;
output Q[M][N] ((i,j,k) | 0 <= i < M, 0 <= j < N, 0 <= k < N, k - j = 0) : Q[i][j] = qhat(i,j,k);output Rdiag[N] ((i,j,k) | M-1 <= i < M, 0 <= j < N, 0 <= k < N, k - j = 0) : Rdiag[j] = sqrt(snorm(i,j,k));output Roff[N][N] ((i,j,k) | 0 <= j < N, 0 <= k < N, k < j, i = M-1) : Roff[k][j] = srp(i,j,k);Run it:
$ dfactl --sure docs/SURE/qr.sure input A -> v normal (0,0,-1) ... output Q <- qhat normal (0,-1,1) output Rdiag <- snorm normal (0,-1,1) output Roff <- srp normal (1,0,0) Q[0][0] = 0.857143 ... Rdiag[0] = 14 ... Roff[0][1] = 21 ...The refinements, one by one
Section titled “The refinements, one by one”-
Case splits become input confluences.
v(i,j,0) = a(i,j)is not a separate equation: the recurrencev(i,j,k) = v(i,j,k-1) - ...simply escapes the domain atk = 0, and the input confluence on thek = -1halo face suppliesA[i][j]there. The subtractive term vanishes on that first step because its own taps escape to zero-valued faces:srpon thek = -1face, andqhatat the corner points(i,-1,-1). -
The corner escape and the one-equality rule. A face has exactly one equality (codimension 1). The
qhatescape lands on the codimension-2 set(i,-1,-1)— the trick is to pin the second coordinate with inequalities:((i,j,k) | 0 <= i < M, -1 <= k < 0, j = -1)has the single equalityj = -1while-1 <= k < 0confineskexactly. -
Reductions are first-class recurrence variables. The derivation’s
s_rands_normpartial sums becomesrpandsnorm, seeded to zero through theiri = -1faces and read where the reduction completes, on thei = M-1face. The completed value is fetched with an affine (broadcast) tapsrp(M-1, j, k-1)— a constant row index, which is what makes this a SARE rather than a pure uniform system. -
qis uniformized asqhatover the whole domain. The 2D outputq(i,j)of the sketch cannot live in a 3D system. Insteadqhatnormalizesvby the running column norm at every point; the only points other equations ever tap are the diagonal(i,k,k), whereqhat(i,k,k) = v_k / ||v_k|| = q(i,k)exactly. The off-diagonal values are uniformization slack: computed, never consumed. This is the classic space-time trade of embedding a lower-dimensional value in the full domain. -
Rleaves through two oriented faces. The sketch’sr(k,j,k)conflates two different results. The off-diagonal projection coefficientsr_kj(k < j) complete where the reduction ends and exit through thei = M-1face with outward normal(1,0,0); the diagonal normsr_jjexit through the diagonal facek = j(outward normal(0,-1,1)), the same faceQleaves through. Non-axis-aligned output faces are exactly why face regions are equality-pinned constraint sets rather than named box sides. -
No global linear schedule. The broadcast taps (
srp(M-1,...),snorm(M-1,...)) and the diagonal taps (qhat(i,k,k)) give dependence vectors that vary with the point, so no singletausatisfiestau.theta >= 1everywhere —qr.suredeclares notauand runs under the free schedule. Index-set splitting (piecewise schedules) is the standard remedy and remains future work.
Verification
Section titled “Verification”qr.sure binds the classic 3x3 example A = [[12,-51,4],[6,167,-68],[-4,24,-41]]
with the exact factorization R = [[14,21,-14],[0,175,-70],[0,0,35]]. The
test suite (src/dfa/tests/sim/sure_parser.cpp) checks R exactly,
Q^T Q = I and Q R = A to machine precision (both residuals < 1e-15),
and the free-schedule legality of the parsed system.
Watch the schedule
Section titled “Watch the schedule”QR is a far harder animation than matmul, and a good stress test of the
approach. It is a genuine SARE: affine (broadcast) taps — srp(M-1,j,k-1),
the completed reduction, and qhat(i,k-1,k-1), the previous column’s q — mean no
global linear schedule exists, so this is the free (data-flow-earliest)
schedule. The domain is a triangular prism (k ≤ j), and the four flowing
variables (v orthogonalization, srp and snorm reductions, qhat
normalization) light up as the wavefront threads the sequential Gram-Schmidt
dependencies — column j cannot start until the earlier columns’ qs exist — so
the front is markedly less regular than matmul’s flat diagonal plane. Shown at
M = N = 8 (latency 144). Press play, or scrub; drag to orbit.
- The reductions are linear chains along
i(O(m) depth); a balanced reduction tree would shorten the critical path but needs log-indexed dependencies outside the affine form — a representation question for the DFA, not the DSL. - The formulation assumes
m >= n(thin QR) and a full-rankA(the normalization divides by the running column norm). - Givens-rotation QR (the Gentleman-Kung array) admits a fully uniform system with a linear schedule; expressing it in the DSL is a natural companion exercise.