Algorithm Search⌘K

Proofs: Divide-and-conquer matrix multiplication

These are the human proofs of Divide-and-conquer matrix multiplication, at full length.

The eight block products add up to the matrix product full

This proof shows: correctness, termination.

Let RR be a ring, or a semiring (commutativity of the multiplication is not used). A view is a block of an array given by a row range and a column range. For a call DC-MULTIPLY-ADD(A,B,C)(A, B, C) with AA of shape n×mn \times m, BB of shape m×pm \times p and CC of shape n×pn \times p, write ColdC^{\text{old}} for CC at the start of the call. Let T={(i,j,k):0≤i<n, 0≤j<p, 0≤k<m}\mathcal{T} = \{(i, j, k) : 0 \le i < n,\ 0 \le j < p,\ 0 \le k < m\} be its set of index triples.

Claim. The call stops. When it returns, C[i][j]=Cold[i][j]+∑k=0m−1A[i][k]B[k][j]C[i][j] = C^{\text{old}}[i][j] + \sum_{k=0}^{m-1} A[i][k] B[k][j] for all i,ji, j, where the products are added one at a time in increasing order of kk. No entry of the arrays outside the view CC changes, and AA and BB do not change. Line L3 runs exactly ∣T∣=nmp|\mathcal{T}| = nmp times.

Termination. Let μ=n+m+p\mu = n + m + p. If the call reaches line L5, then it did not return at line L1 or L4, so n,m,p≥1n, m, p \ge 1 and at least one side is at least 2. A side d≥2d \ge 2 splits into ⌈d/2⌉<d\lceil d/2 \rceil < d and ⌊d/2⌋<d\lfloor d/2 \rfloor < d. A side d=1d = 1 splits into 11 and 00. So each of the eight calls of lines L8 and L9 has its three sides at most the old ones, and the side that was at least 2 is strictly smaller. So μ\mu drops by at least 1 in each call, and μ≥0\mu \ge 0, so the recursion is finite.

Proof of the claim. By strong induction on μ\mu.

Base cases. If n=0n = 0 or p=0p = 0, there are no entries of CC and line L1 returns, so the claim holds. If m=0m = 0, each sum is empty and line L1 returns with C=ColdC = C^{\text{old}}. If n=m=p=1n = m = p = 1, line L3 adds A[0][0]B[0][0]A[0][0] B[0][0] to C[0][0]C[0][0], which is the claim with one term, and ∣T∣=1|\mathcal{T}| = 1.

Step. Let n,m,p≥1n, m, p \ge 1 with one side at least 2, and assume the claim for all calls with a smaller μ\mu. Line L5 splits the row range [0,n)[0, n) into I1=[0,n1)I_1 = [0, n_1) and I2=[n1,n)I_2 = [n_1, n) with n1=⌈n/2⌉n_1 = \lceil n/2 \rceil, the inner range into K1=[0,m1)K_1 = [0, m_1) and K2=[m1,m)K_2 = [m_1, m), and the column range into J1J_1 and J2J_2. The view ArtA_{rt} has rows IrI_r and columns KtK_t, the view BtsB_{ts} has rows KtK_t and columns JsJ_s, and the view CrsC_{rs} has rows IrI_r and columns JsJ_s. The views C11,C12,C21,C22C_{11}, C_{12}, C_{21}, C_{22} are disjoint and cover CC.

Fix (r,s)(r, s) and an entry (i,j)(i, j) with i∈Iri \in I_r, j∈Jsj \in J_s. Only the two calls with this (r,s)(r, s) change CrsC_{rs}, because each call changes only its own view of CC by the hypothesis. The call of line L8 has a smaller μ\mu, so by the hypothesis it adds ∑k∈K1A[i][k]B[k][j]\sum_{k \in K_1} A[i][k] B[k][j], in increasing order of kk. Then the call of line L9 adds ∑k∈K2A[i][k]B[k][j]\sum_{k \in K_2} A[i][k] B[k][j], in increasing order of kk. Every element of K1K_1 is less than every element of K2K_2, and K1∪K2=[0,m)K_1 \cup K_2 = [0, m). So after both calls,

C[i][j]=Cold[i][j]+∑k=0m−1A[i][k]B[k][j],C[i][j] = C^{\text{old}}[i][j] + \sum_{k=0}^{m-1} A[i][k] B[k][j],

with the terms added in increasing order of kk. The calls for other pairs (r′,s′)(r', s') do not touch CrsC_{rs}. This holds for each entry, so the claim holds for CC. The calls read AA and BB and never write them.

Count. The eight sets Ir×Js×KtI_r \times J_s \times K_t for r,s,t∈{1,2}r, s, t \in \{1, 2\} are disjoint and their union is T\mathcal{T}. By the hypothesis, the call for (r,s,t)(r, s, t) runs line L3 exactly ∣Ir∣∣Js∣∣Kt∣|I_r| |J_s| |K_t| times. The sum over the eight calls is ∣T∣|\mathcal{T}|. ■\blacksquare

Corollary. DC-MULTIPLY-ADD(A,B,0)(A, B, 0) gives ABAB, with each entry computed by the same sequence of additions as the triple loop with ss starting at 00.

Back to Divide-and-conquer matrix multiplication