Parallel prefix sums
DefinitionWindow product#✓fully proved
For a sequence \(x : \mathbb{N} \to M\) in a monoid \(M\) and \(a, b \in \mathbb{N}\), the window product is the ordered product \(x_a x_{a+1} \cdots x_{b-1}\) of the terms with indices \(a \le j < b\); it is the empty product \(1\) when \(b \le a\).
def seg (x : ℕ → M) (a b : ℕ) : M := ((List.range' a (b - a)).map x).prod
DefinitionPrefix product#✓fully proved
For a sequence \(x_0, x_1, \dots\) in a monoid \(M\) and a natural number \(i\), the prefix product at \(i\) is \(x_0 x_1 \cdots x_i\), the factors taken in increasing order of index.
def prefixProd (x : ℕ → M) (i : ℕ) : M := seg x 0 (i + 1)
def seg (x : ℕ → M) (a b : ℕ) : M := ((List.range' a (b - a)).map x).prod
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:56
Uses 1 · Used by 3
TheoremProducts of adjacent windows#✓fully proved
Let \(M\) be a monoid and \(x : \mathbb{N} \to M\) a sequence, and for \(a, b \in \mathbb{N}\) let \(x_{[a, b)}\) be the ordered product \(x_a x_{a+1} \cdots x_{b-1}\) (equal to \(1\) if \(b \le a\)). For all \(a \le b \le c\), \(x_{[a, b)} \, x_{[b, c)} = x_{[a, c)}\).
theorem seg_mul_seg (x : ℕ → M) {a b c : ℕ} (hab : a ≤ b) (hbc : b ≤ c) :
seg x a b * seg x b c = seg x a c
def seg (x : ℕ → M) (a b : ℕ) : M := ((List.range' a (b - a)).map x).prod
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:56
Uses 1 · Used by 2
DefinitionThe Kogge–Stone scan#✓fully proved
Given a sequence \(x_0, x_1, \dots\) in a monoid \(M\), the value \(s_k(i)\) at position \(i\) after \(k\) rounds is defined by \(s_0(i) = x_i\) and, for each \(k\), \(s_{k+1}(i) = s_k(i - 2^k) \, s_k(i)\) if \(2^k \le i\) and \(s_{k+1}(i) = s_k(i)\) otherwise.
def koggeStone (x : ℕ → M) : ℕ → ℕ → M
| 0, i => x i
| k + 1, i => if 2 ^ k ≤ i then koggeStone x k (i - 2 ^ k) * koggeStone x k i
else koggeStone x k i
TheoremWhat the scan holds after each round#✓fully proved
Let \(x_0, x_1, \dots\) be a sequence in a monoid \(M\), let \(k\) and \(i\) be natural numbers, and let \(m = \min(i + 1, 2^k)\). After \(k\) rounds of the Kogge–Stone scan, position \(i\) holds the product \(x_{i+1-m} \cdots x_i\) of the \(m\) consecutive inputs ending at \(i\), taken in increasing order of index.
theorem koggeStone_eq_seg (x : ℕ → M) :
∀ k i, koggeStone x k i = seg x (i + 1 - min (i + 1) (2 ^ k)) (i + 1)
def koggeStone (x : ℕ → M) : ℕ → ℕ → M
| 0, i => x i
| k + 1, i => if 2 ^ k ≤ i then koggeStone x k (i - 2 ^ k) * koggeStone x k i
else koggeStone x k i
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:74
def seg (x : ℕ → M) (a b : ℕ) : M := ((List.range' a (b - a)).map x).prod
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:56
Uses 3 · Used by 1
TheoremThe scan computes all prefix products#✓fully proved
Let \(x_0, x_1, \dots\) be a sequence in a monoid \(M\) and let \(n\) and \(K\) be natural numbers with \(n \le 2^K\). After \(K\) rounds of the Kogge–Stone scan, every position \(i < n\) holds the prefix product \(x_0 x_1 \cdots x_i\).
theorem koggeStone_eq_prefix (x : ℕ → M) {n K : ℕ} (hn : n ≤ 2 ^ K) {i : ℕ} (hi : i < n) :
koggeStone x K i = prefixProd x i
def koggeStone (x : ℕ → M) : ℕ → ℕ → M
| 0, i => x i
| k + 1, i => if 2 ^ k ≤ i then koggeStone x k (i - 2 ^ k) * koggeStone x k i
else koggeStone x k i
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:74
def prefixProd (x : ℕ → M) (i : ℕ) : M := seg x 0 (i + 1)
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:59
Uses 4 · Used by 2
DefinitionWork of a scan round#✓fully proved
The work of round \(k\) on \(n\) positions is \(\max(n - 2^k, 0)\), the number of positions \(i < n\) with \(i \ge 2^k\), each of which performs one multiplication.
def roundWork (n k : ℕ) : ℕ := n - 2 ^ k
TheoremThe work of a round counts its combining positions#✓fully proved
For natural numbers \(n\) and \(k\), the work of round \(k\) on \(n\) positions equals the number of positions \(i\) with \(2^k \le i < n\).
theorem roundWork_eq_card (n k : ℕ) :
roundWork n k = ((Finset.range n).filter fun i => 2 ^ k ≤ i).card
def roundWork (n k : ℕ) : ℕ := n - 2 ^ k
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:109
Uses 1
DefinitionWork of the Kogge–Stone scan#✓fully proved
The total work of \(K\) rounds on \(n\) positions is \(\sum_{k=0}^{K-1} \max(n - 2^k, 0)\), the sum over the rounds \(k < K\) of the work \(\max(n - 2^k, 0)\) of round \(k\).
def work (n K : ℕ) : ℕ := ∑ k ∈ Finset.range K, roundWork n k
def roundWork (n k : ℕ) : ℕ := n - 2 ^ k
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:109
Uses 1 · Used by 1
TheoremWork bound for the Kogge–Stone scan#✓fully proved
For all \(n, K \in \mathbb{N}\), the total work of \(K\) rounds of the scan on \(n\) positions is at most \(nK\).
theorem work_le (n K : ℕ) : work n K ≤ n * K
def work (n K : ℕ) : ℕ := ∑ k ∈ Finset.range K, roundWork n k
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:120
Uses 2
DefinitionSpan of the Kogge–Stone scan#✓fully proved
The span of \(K\) rounds of the scan is \(K\): each round is one parallel step.
def span (K : ℕ) : ℕ := K
TheoremThe scan computes all prefixes in logarithmically many rounds#✓fully proved
Let \(x_0, x_1, \dots\) be a sequence in a monoid \(M\) and let \(n\) be a natural number. After \(\lceil \log_2 n \rceil\) rounds of the Kogge–Stone scan (the least \(K\) with \(n \le 2^K\)), every position \(i < n\) holds the prefix product \(x_0 x_1 \cdots x_i\).
theorem koggeStone_clog_eq_prefix (x : ℕ → M) {n i : ℕ} (hi : i < n) :
koggeStone x (Nat.clog 2 n) i = prefixProd x i
def koggeStone (x : ℕ → M) : ℕ → ℕ → M
| 0, i => x i
| k + 1, i => if 2 ^ k ≤ i then koggeStone x k (i - 2 ^ k) * koggeStone x k i
else koggeStone x k i
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:74
def prefixProd (x : ℕ → M) (i : ℕ) : M := seg x 0 (i + 1)
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:59
Uses 3
DefinitionOne round of the scan on a list#✓fully proved
Given a list \(y_0, \dots, y_{n-1}\) in a monoid \(M\) and a natural number \(k\), one round produces the list of length \(n\) whose entry \(i\) is \(y_{i - 2^k} \, y_i\) when \(2^k \le i\) and \(y_i\) when \(i < 2^k\).
The list is traversed in lockstep with its own suffix starting at position \(2^k\), so a round costs \(O(n)\).
def roundList (k : ℕ) (ys : List M) : List M :=
ys.take (2 ^ k) ++ List.zipWith (· * ·) ys (ys.drop (2 ^ k))
Used by 2
DefinitionTabulated Kogge–Stone scan#✓fully proved
For a list \(y = (y_0, \dots, y_{n-1})\) in a monoid \(M\), the list after \(K\) rounds is defined by recursion on \(K\): after \(0\) rounds it is \(y\), and after \(K + 1\) rounds it is round \(K\) applied to the list after \(K\) rounds. Here round \(k\) sends a list \((z_0, \dots, z_{n-1})\) to the list of length \(n\) whose entry \(i\) is \(z_i\) for \(i < 2^k\) and \(z_{i - 2^k} z_i\) for \(2^k \le i < n\).
def scanList : ℕ → List M → List M
| 0, ys => ys
| K + 1, ys => roundList K (scanList K ys)
def roundList (k : ℕ) (ys : List M) : List M :=
ys.take (2 ^ k) ++ List.zipWith (· * ·) ys (ys.drop (2 ^ k))
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:141
Uses 1 · Used by 2
TheoremThe tabulated scan agrees with the round recursion#✓fully proved
Let \(x_0, \dots, x_{n-1}\) be a list of elements of a monoid \(M\), extended to a sequence by \(x_j = 1\) for \(j \ge n\). For every number of rounds \(K\) and every position \(i < n\), entry \(i\) of the list produced by \(K\) rounds of the tabulated scan equals the value at position \(i\) after \(K\) rounds of the Kogge–Stone scan applied to the sequence \(x\).
theorem getD_scanList (xs : List M) : ∀ (K i : ℕ), i < xs.length →
(scanList K xs).getD i 1 = koggeStone (fun j => xs.getD j 1) K i
def scanList : ℕ → List M → List M
| 0, ys => ys
| K + 1, ys => roundList K (scanList K ys)
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:145
def koggeStone (x : ℕ → M) : ℕ → ℕ → M
| 0, i => x i
| k + 1, i => if 2 ^ k ≤ i then koggeStone x k (i - 2 ^ k) * koggeStone x k i
else koggeStone x k i
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:74
Uses 3 · Used by 1
TheoremThe tabulated scan computes all prefix products#✓fully proved
Let \(x_0, \dots, x_{n-1}\) be a list of elements of a monoid \(M\) and let \(K\) be a natural number with \(n \le 2^K\). Then for every \(i < n\), entry \(i\) of the list produced by \(K\) rounds of the tabulated scan equals the prefix product \(x_0 x_1 \cdots x_i\).
theorem getD_scanList_eq_prefix (xs : List M) {K : ℕ} (hK : xs.length ≤ 2 ^ K) {i : ℕ}
(hi : i < xs.length) : (scanList K xs).getD i 1 = prefixProd (fun j => xs.getD j 1) i
def scanList : ℕ → List M → List M
| 0, ys => ys
| K + 1, ys => roundList K (scanList K ys)
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:145
def prefixProd (x : ℕ → M) (i : ℕ) : M := seg x 0 (i + 1)
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:59
Uses 5
DefinitionAdjacent pair products of a sequence#✓fully proved
For a sequence \(x \colon \mathbb{N} \to M\) in a monoid \(M\), the sequence of adjacent pair products is the sequence whose term at \(i\) is \(x(2i) \cdot x(2i+1)\).
def pairUp (x : ℕ → M) : ℕ → M := fun i => x (2 * i) * x (2 * i + 1)
DefinitionBlelloch's scan#✓fully proved
Let \(M\) be a monoid. For a natural number \(k\) and a sequence \(x = (x_0, x_1, \dots)\) in \(M\), the scan \(B_k(x)\) is the sequence in \(M\) defined by recursion on \(k\): \(B_0(x)\) is constantly \(1\), and \(B_{k+1}(x)\) is obtained from the scan \(y = B_k(p)\) of the pair products \(p_i = x_{2i} x_{2i+1}\) by setting \(B_{k+1}(x)_{2i} = y_i\) and \(B_{k+1}(x)_{2i+1} = y_i x_{2i}\) for every \(i\).
TheoremBlelloch's scan computes the exclusive prefix products#✓fully proved
Let \(M\) be a monoid, \(k\) a natural number and \(x = (x_0, x_1, \dots)\) a sequence in \(M\). For every \(j < 2^k\), the value at position \(j\) of Blelloch's scan of \(x\) on \(2^k\) positions is the exclusive prefix product \(x_0 x_1 \cdots x_{j-1}\) (equal to \(1\) when \(j = 0\)).
theorem blelloch_eq_seg : ∀ (k : ℕ) (x : ℕ → M) (j : ℕ), j < 2 ^ k → blelloch k x j = seg x 0 j
def blelloch : ℕ → (ℕ → M) → ℕ → M
| 0, _ => fun _ => 1
| k + 1, x => fun j =>
if j % 2 = 0 then blelloch k (pairUp x) (j / 2) else blelloch k (pairUp x) (j / 2) * x (j - 1)
CSLibExt/Algorithms/Lean/Parallel/Blelloch.lean:54
def seg (x : ℕ → M) (a b : ℕ) : M := ((List.range' a (b - a)).map x).prod
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:56
Uses 4 · Used by 1
DefinitionWork of Blelloch's scan#✓fully proved
The number \(W(k)\) of monoid multiplications Blelloch's scan performs on \(2^k\) positions is defined by \(W(0) = 0\) and \(W(k+1) = 2^k + W(k) + 2^k\): on \(2^{k+1}\) positions, \(2^k\) pair products, the recursive scan on the \(2^k\) pair products, and \(2^k\) products for the odd positions.
def blellochWork : ℕ → ℕ
| 0 => 0
| k + 1 => 2 ^ k + blellochWork k + 2 ^ k
Used by 1
DefinitionParallel steps of Blelloch's scan#✓fully proved
The span \(S(k)\) of Blelloch's scan on \(2^k\) positions is defined by \(S(0) = 0\) and \(S(k+1) = S(k) + 2\): one parallel step for the pair products, the recursive scan, and one parallel step for the odd positions.
def blellochSpan : ℕ → ℕ
| 0 => 0
| k + 1 => blellochSpan k + 2
Used by 1
TheoremBlelloch's scan is work-efficient#✓fully proved
For every natural number \(k\), the number \(W(k)\) of monoid multiplications Blelloch's scan performs on \(n = 2^k\) positions satisfies \(W(k) + 2 = 2^{k+1}\), so \(W(k) = 2^{k+1} - 2 < 2n\).
theorem blellochWork_eq : ∀ k, blellochWork k + 2 = 2 ^ (k + 1)
def blellochWork : ℕ → ℕ
| 0 => 0
| k + 1 => 2 ^ k + blellochWork k + 2 ^ k
CSLibExt/Algorithms/Lean/Parallel/Blelloch.lean:89
Uses 1
TheoremBlelloch's scan has logarithmic span#✓fully proved
For every natural number \(k\), the span of Blelloch's scan on \(n = 2^k\) positions is \(2k = 2 \log_2 n\).
theorem blellochSpan_eq : ∀ k, blellochSpan k = 2 * k
def blellochSpan : ℕ → ℕ
| 0 => 0
| k + 1 => blellochSpan k + 2
CSLibExt/Algorithms/Lean/Parallel/Blelloch.lean:95
Uses 1
DefinitionAdjacent pair products of a list#✓fully proved
For a list \([a_0, a_1, \ldots, a_{n-1}]\) of elements of a monoid \(M\), the list of adjacent pair products is \([a_0 a_1, a_2 a_3, \ldots]\): it has length \(\lfloor n/2 \rfloor\), its \(i\)-th entry is \(a_{2i} a_{2i+1}\), and when \(n\) is odd the last element \(a_{n-1}\) is dropped.
Used by 2
DefinitionInterleaving step of Blelloch's list scan#✓fully proved
Let \(M\) be a monoid. For lists \(z = (z_0, z_1, \dots)\) and \(x = (x_0, x_1, \dots)\) in \(M\), the expansion of \(z\) along \(x\) is the list \((z_0, z_0 x_0, z_1, z_1 x_2, z_2, z_2 x_4, \dots)\): each \(z_i\) is followed by \(z_i x_{2i}\), and the list stops at the first \(i\) for which \(z_i\) or \(x_{2i+1}\) does not exist.
def expand : List M → List M → List M
| z :: zs, a :: _ :: xs => z :: (z * a) :: expand zs xs
| _, _ => []
Used by 2
DefinitionBlelloch's scan on lists#✓fully proved
Let \(M\) be a monoid. For a natural number \(k\) and a list \(xs = (x_0, x_1, \dots)\) in \(M\), the list scan \(L_k(xs)\) is defined by recursion on \(k\): \(L_0(xs)\) is the one-element list \((1)\), and \(L_{k+1}(xs)\) is obtained from the list scan \(z = L_k(p)\) of the pair products \(p = (x_0 x_1, x_2 x_3, \dots)\) of \(xs\) (a trailing unpaired entry is dropped) by interleaving: \(L_{k+1}(xs) = (z_0, z_0 x_0, z_1, z_1 x_2, \dots)\), where each \(z_i\) is followed by \(z_i x_{2i}\) and the list stops at the first \(i\) for which \(z_i\) or \(x_{2i+1}\) does not exist.
On a list of length \(2^k\) this takes \(O(2^k)\) time.
def blellochList : ℕ → List M → List M
| 0, _ => [1]
| k + 1, xs => expand (blellochList k (pairList xs)) xs
def expand : List M → List M → List M
| z :: zs, a :: _ :: xs => z :: (z * a) :: expand zs xs
| _, _ => []
CSLibExt/Algorithms/Lean/Parallel/Blelloch.lean:121
def pairList : List M → List M
| a :: b :: xs => (a * b) :: pairList xs
| _ => []
CSLibExt/Algorithms/Lean/Parallel/Blelloch.lean:116
Uses 2 · Used by 2
TheoremThe list implementation computes Blelloch's scan#✓fully proved
Let \(M\) be a monoid, \(k\) a natural number and \(xs = (x_0, \dots, x_{2^k - 1})\) a list in \(M\) of length \(2^k\). For every \(j < 2^k\), the entry at position \(j\) of Blelloch's list scan of \(xs\) in \(k\) rounds equals the value at position \(j\) of Blelloch's scan on \(2^k\) positions of the sequence \(x_0, \dots, x_{2^k - 1}, 1, 1, \dots\) obtained by extending \(xs\) by \(1\).
theorem getD_blellochList : ∀ (k : ℕ) (xs : List M), xs.length = 2 ^ k → ∀ j, j < 2 ^ k →
(blellochList k xs).getD j 1 = blelloch k (fun i => xs.getD i 1) j
def blellochList : ℕ → List M → List M
| 0, _ => [1]
| k + 1, xs => expand (blellochList k (pairList xs)) xs
CSLibExt/Algorithms/Lean/Parallel/Blelloch.lean:126
def blelloch : ℕ → (ℕ → M) → ℕ → M
| 0, _ => fun _ => 1
| k + 1, x => fun j =>
if j % 2 = 0 then blelloch k (pairUp x) (j / 2) else blelloch k (pairUp x) (j / 2) * x (j - 1)
CSLibExt/Algorithms/Lean/Parallel/Blelloch.lean:54
Uses 5 · Used by 1
TheoremThe list implementation computes the exclusive prefix products#✓fully proved
Let \(M\) be a monoid, \(k\) a natural number and \(xs = (x_0, \dots, x_{2^k - 1})\) a list in \(M\) of length \(2^k\). For every \(j < 2^k\), the entry at position \(j\) of Blelloch's list scan of \(xs\) in \(k\) rounds is the exclusive prefix product \(x_0 x_1 \cdots x_{j-1}\) (equal to \(1\) when \(j = 0\)).
theorem getD_blellochList_eq_seg (k : ℕ) (xs : List M) (h : xs.length = 2 ^ k) (j : ℕ)
(hj : j < 2 ^ k) : (blellochList k xs).getD j 1 = seg (fun i => xs.getD i 1) 0 j
def blellochList : ℕ → List M → List M
| 0, _ => [1]
| k + 1, xs => expand (blellochList k (pairList xs)) xs
CSLibExt/Algorithms/Lean/Parallel/Blelloch.lean:126
def seg (x : ℕ → M) (a b : ℕ) : M := ((List.range' a (b - a)).map x).prod
CSLibExt/Algorithms/Lean/Parallel/PrefixSum.lean:56
Uses 5
Loading dependency explorer…
The dependency explorer needs JavaScript and its graph data.