Matrix chain multiplication
InductiveParenthesization#✓fully proved
A parenthesization of \(n\) matrices is a full binary tree whose leaves are labeled by indices in \(\{0, \dots, n - 1\}\): either a single leaf naming matrix \(i\), or a product node with a left and a right parenthesization as factors. Labels may be repeated, omitted, or out of order.
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
Used by 13
DefinitionLeaf indices of a parenthesization#✓fully proved
The leaf indices of a parenthesization of \(n\) matrices form the list of indices in \(\{0, \dots, n - 1\}\) obtained by reading its leaves from left to right: \([i]\) for a single leaf naming matrix \(i\), and the concatenation of the lists of the left and right factors for a product node.
@[expose] public def matrixIndices {n : Nat} : Parenthesization n → List (Fin n)
| .matrix index => [index]
| .multiply left right => matrixIndices left ++ matrixIndices right
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:35
Uses 1 · Used by 3
DefinitionRow count of a parenthesization#✓fully proved
Let \(p = (p_0, \dots, p_n)\) be a vector of natural numbers, regarding matrix \(i\) (for \(0 \le i < n\)) as having \(p_i\) rows and \(p_{i+1}\) columns. The row count of a parenthesization of \(n\) matrices with respect to \(p\) is \(p_i\) for a single leaf naming matrix \(i\), and the row count of the left factor for a product node.
@[expose] public def rows {n : Nat}
(dimensions : Vector Nat (n + 1)) : Parenthesization n → Nat
| .matrix index => dimensions.get index.castSucc
| .multiply left _ => rows dimensions left
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:35
Uses 1 · Used by 8
DefinitionColumn count of a parenthesization#✓fully proved
Let \(p = (p_0, \dots, p_n)\) be a vector of natural numbers, regarding matrix \(i\) (for \(0 \le i < n\)) as having \(p_i\) rows and \(p_{i+1}\) columns. The column count of a parenthesization of \(n\) matrices with respect to \(p\) is \(p_{i+1}\) for a single leaf naming matrix \(i\), and the column count of the right factor for a product node.
@[expose] public def cols {n : Nat}
(dimensions : Vector Nat (n + 1)) : Parenthesization n → Nat
| .matrix index => dimensions.get index.succ
| .multiply _ right => cols dimensions right
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:35
Uses 1 · Used by 8
DefinitionDimension-compatible parenthesization#✓fully proved
Let \(p = (p_0, \dots, p_n)\) be a vector of natural numbers, regarding matrix \(i\) (for \(0 \le i < n\)) as having \(p_i\) rows and \(p_{i+1}\) columns. A parenthesization of \(n\) matrices is dimension-compatible with \(p\) when, at every product node, the column count of the left factor equals the row count of the right factor, where row and column counts are taken with respect to \(p\). A single leaf is always dimension-compatible.
@[expose] public def DimensionCompatible {n : Nat} (dimensions : Vector Nat (n + 1)) :
Parenthesization n → Prop
| .matrix _ => True
| .multiply left right =>
DimensionCompatible dimensions left ∧ DimensionCompatible dimensions right ∧
cols dimensions left = rows dimensions right
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:35
@[expose] public def cols {n : Nat}
(dimensions : Vector Nat (n + 1)) : Parenthesization n → Nat
| .matrix index => dimensions.get index.succ
| .multiply _ right => cols dimensions right
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:56
@[expose] public def rows {n : Nat}
(dimensions : Vector Nat (n + 1)) : Parenthesization n → Nat
| .matrix index => dimensions.get index.castSucc
| .multiply left _ => rows dimensions left
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:50
Uses 3 · Used by 3
DefinitionValid parenthesization#✓fully proved
Let \(p = (p_0, \dots, p_n)\) be a vector of natural numbers, regarding matrix \(i\) (for \(0 \le i < n\)) as having \(p_i\) rows and \(p_{i+1}\) columns. A parenthesization of \(n\) matrices is valid for \(p\) when its leaf indices, read left to right, are exactly \(0, 1, \dots, n - 1\), and it is dimension-compatible with \(p\).
@[expose] public def IsValid {n : Nat} (dimensions : Vector Nat (n + 1))
(tree : Parenthesization n) : Prop :=
tree.matrixIndices = List.finRange n ∧ tree.DimensionCompatible dimensions
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:35
Uses 3 · Used by 2
DefinitionScalar multiplication count of a parenthesization#✓fully proved
Let \(p = (p_0, \dots, p_n)\) be a vector of natural numbers, regarding matrix \(i\) (for \(0 \le i < n\)) as having \(p_i\) rows and \(p_{i+1}\) columns, and let \(r(T)\) and \(c(T)\) be the row and column counts of a parenthesization \(T\) with respect to \(p\). The scalar multiplication count of a single leaf is \(0\), and that of a product node with factors \(L\) and \(R\) is the sum of the counts of \(L\) and \(R\) plus \(r(L)\,c(L)\,c(R)\).
@[expose] public def scalarMultiplicationCost {n : Nat}
(dimensions : Vector Nat (n + 1)) :
Parenthesization n → Nat
| .matrix _ => 0
| .multiply left right =>
scalarMultiplicationCost dimensions left + scalarMultiplicationCost dimensions right +
left.rows dimensions * left.cols dimensions * right.cols dimensions
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:35
Uses 3 · Used by 6
StructureMatrix-chain cost#✓fully proved
A matrix-chain cost is a pair of natural numbers counting the abstract events charged by matrix-chain ordering and reconstruction: the number of split candidates evaluated by the dynamic program, and the number of tree nodes emitted during reconstruction.
public structure Cost where
/-- Split candidates evaluated by the dynamic program. -/
candidateEvaluations : Nat
/-- Tree constructors emitted during reconstruction. -/
reconstructionNodes : Nat
deriving DecidableEq, Repr
Used by 9
DefinitionTotal matrix-chain cost#✓fully proved
The total of a matrix-chain cost is its number of candidate evaluations plus its number of reconstruction nodes.
@[expose] public def Cost.total (cost : Cost) : Nat :=
cost.candidateEvaluations + cost.reconstructionNodes
public structure Cost where
/-- Split candidates evaluated by the dynamic program. -/
candidateEvaluations : Nat
/-- Tree constructors emitted during reconstruction. -/
reconstructionNodes : Nat
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:86
Uses 1 · Used by 2
StructureMatrix-chain result#✓fully proved
A matrix-chain result for \(n\) matrices pairs a natural number, the reported optimal scalar multiplication count, with a parenthesization of \(n\) matrices, the reported optimal parenthesization. The structure itself does not require the count to be minimal or to be attained by the parenthesization.
public structure Result (n : Nat) where
/-- Minimum scalar-multiplication count. -/
optimalCost : Nat
/-- Reconstructed optimal parenthesization. -/
parenthesization : Parenthesization n
deriving DecidableEq, Repr
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:35
Uses 1 · Used by 8
DefinitionMatrix-chain ordering#✓fully proved
Given \(n \ge 1\) and natural numbers \(p_0, \dots, p_n\), where matrix \(i\) of the chain (\(0 \le i < n\)) has \(p_i\) rows and \(p_{i+1}\) columns, the algorithm computes, bottom-up by interval length, values \(m_{i,j}\) for \(0 \le i \le j < n\): \(m_{i,i} = 0\) and, for \(i < j\), \(m_{i,j}\) is the least value of \(m_{i,k} + m_{k+1,j} + p_i p_{k+1} p_{j+1}\) over \(i \le k < j\), and the first split point \(k\) attaining it is recorded. It then rebuilds a parenthesization of the whole chain from the recorded split points and returns \(m_{0,n-1}\) together with this parenthesization. One event is charged per evaluated split candidate and one per reconstructed tree node.
public def matrixChainOrder {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) : TimeM Cost (Result n) :=
let computation := matrixChainOrderRaw dimensions hn
have hSome : computation.ret.isSome := by
obtain ⟨cell, tree, hResult, hOptimal, hInterval, hCost⟩ :=
matrixChainOrderRaw_spec dimensions hn
change (matrixChainOrderRaw dimensions hn).ret.isSome
rw [hResult]
rfl
{ ret := computation.ret.get hSome, time := computation.time }
public structure Cost where
/-- Split candidates evaluated by the dynamic program. -/
candidateEvaluations : Nat
/-- Tree constructors emitted during reconstruction. -/
reconstructionNodes : Nat
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:86
public structure Result (n : Nat) where
/-- Minimum scalar-multiplication count. -/
optimalCost : Nat
/-- Reconstructed optimal parenthesization. -/
parenthesization : Parenthesization n
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:119
def matrixChainOrderRaw {n : Nat} (dimensions : Vector Nat (n + 1)) (hn : 0 < n) :
TimeM Cost (Option (Result n)) :=
let Cell := Nat × (Option (Fin n) × Parenthesization n)
let Table := Vector (Vector (Option Cell) n) n
let tableGet (table : Table) (i j : Fin n) : Option Cell :=
(table.get i).get j
let tableSet (table : Table) (i j : Fin n) (value : Option Cell) : Table :=
table.set i.val ((table.get i).set j.val value)
let initialTable : Table :=
Vector.ofFn fun i => Vector.ofFn fun j =>
if i = j then some (0, none, .matrix i) else none
let rec scanSplits (table : Table) (i j : Fin n) :
⋯
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:126
Uses 6 · Used by 7
TheoremMatrix-chain ordering returns a valid parenthesization#✓fully proved
For every \(n \ge 1\) and all natural numbers \(p_0, \dots, p_n\), the parenthesization returned by the matrix-chain ordering algorithm on the dimensions \(p_0, \dots, p_n\) is valid: its leaves are the matrices \(0, 1, \dots, n - 1\) in order, each exactly once, and at every multiplication node the column count of the left factor equals the row count of the right factor, where matrix \(i\) has \(p_i\) rows and \(p_{i+1}\) columns.
public theorem matrixChainOrder_parenthesization_valid {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) :
let result := (matrixChainOrder dimensions hn).ret
result.parenthesization.IsValid dimensions
public def matrixChainOrder {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) : TimeM Cost (Result n) :=
let computation := matrixChainOrderRaw dimensions hn
have hSome : computation.ret.isSome := by
obtain ⟨cell, tree, hResult, hOptimal, hInterval, hCost⟩ :=
matrixChainOrderRaw_spec dimensions hn
change (matrixChainOrderRaw dimensions hn).ret.isSome
rw [hResult]
rfl
{ ret := computation.ret.get hSome, time := computation.time }
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Correctness.lean:65
Uses 10
TheoremMatrix-chain ordering reports the cost of its parenthesization#✓fully proved
For every \(n \ge 1\) and all natural numbers \(p_0, \dots, p_n\), the scalar-multiplication cost of the parenthesization returned by the matrix-chain ordering algorithm on the dimensions \(p_0, \dots, p_n\) equals the optimal cost it reports.
public theorem matrixChainOrder_cost_eq {n : Nat} (dimensions : Vector Nat (n + 1))
(hn : 0 < n) :
let result := (matrixChainOrder dimensions hn).ret
scalarMultiplicationCost dimensions result.parenthesization = result.optimalCost
public def matrixChainOrder {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) : TimeM Cost (Result n) :=
let computation := matrixChainOrderRaw dimensions hn
have hSome : computation.ret.isSome := by
obtain ⟨cell, tree, hResult, hOptimal, hInterval, hCost⟩ :=
matrixChainOrderRaw_spec dimensions hn
change (matrixChainOrderRaw dimensions hn).ret.isSome
rw [hResult]
rfl
{ ret := computation.ret.get hSome, time := computation.time }
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Correctness.lean:65
@[expose] public def scalarMultiplicationCost {n : Nat}
(dimensions : Vector Nat (n + 1)) :
Parenthesization n → Nat
| .matrix _ => 0
| .multiply left right =>
scalarMultiplicationCost dimensions left + scalarMultiplicationCost dimensions right +
left.rows dimensions * left.cols dimensions * right.cols dimensions
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:77
Uses 7
TheoremReported matrix-chain cost is a lower bound#✓fully proved
For every \(n \ge 1\), all natural numbers \(p_0, \dots, p_n\) and every parenthesization \(T\) of \(n\) matrices that is valid for these dimensions, the optimal cost reported by the matrix-chain ordering algorithm on the dimensions \(p_0, \dots, p_n\) is at most the scalar-multiplication cost of \(T\).
public theorem matrixChainOrder_minimal {n : Nat} (dimensions : Vector Nat (n + 1))
(hn : 0 < n) (tree : Parenthesization n) (hTree : tree.IsValid dimensions) :
(matrixChainOrder dimensions hn).ret.optimalCost ≤
scalarMultiplicationCost dimensions tree
public inductive Parenthesization (n : Nat) where
/-- One matrix at its original bounded index. -/
| matrix (index : Fin n)
/-- Multiply two fully parenthesized subchains. -/
| multiply (left right : Parenthesization n)
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:35
public def matrixChainOrder {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) : TimeM Cost (Result n) :=
let computation := matrixChainOrderRaw dimensions hn
have hSome : computation.ret.isSome := by
obtain ⟨cell, tree, hResult, hOptimal, hInterval, hCost⟩ :=
matrixChainOrderRaw_spec dimensions hn
change (matrixChainOrderRaw dimensions hn).ret.isSome
rw [hResult]
rfl
{ ret := computation.ret.get hSome, time := computation.time }
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Correctness.lean:65
@[expose] public def scalarMultiplicationCost {n : Nat}
(dimensions : Vector Nat (n + 1)) :
Parenthesization n → Nat
| .matrix _ => 0
| .multiply left right =>
scalarMultiplicationCost dimensions left + scalarMultiplicationCost dimensions right +
left.rows dimensions * left.cols dimensions * right.cols dimensions
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Basic.lean:77
Uses 10
TheoremSplit candidates evaluated by matrix-chain ordering#✓fully proved
For every \(n \ge 1\) and all natural numbers \(p_0, \dots, p_n\), the matrix-chain ordering algorithm run on the dimensions \(p_0, \dots, p_n\) evaluates exactly \(\binom{n+1}{3}\) split candidates.
This is the number of triples \(i \le k < j < n\), so the dynamic program evaluates one candidate for every interval split.
public theorem matrixChainOrder_candidateEvaluations {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) :
(matrixChainOrder dimensions hn).time.candidateEvaluations = Nat.choose (n + 1) 3
public def matrixChainOrder {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) : TimeM Cost (Result n) :=
let computation := matrixChainOrderRaw dimensions hn
have hSome : computation.ret.isSome := by
obtain ⟨cell, tree, hResult, hOptimal, hInterval, hCost⟩ :=
matrixChainOrderRaw_spec dimensions hn
change (matrixChainOrderRaw dimensions hn).ret.isSome
rw [hResult]
rfl
{ ret := computation.ret.get hSome, time := computation.time }
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Correctness.lean:65
Uses 7 · Used by 1
TheoremReconstruction size in matrix-chain ordering#✓fully proved
For every \(n \ge 1\) and all natural numbers \(p_0, \dots, p_n\), the matrix-chain ordering algorithm run on the dimensions \(p_0, \dots, p_n\) charges exactly \(2n - 1\) reconstruction events.
This is the number of nodes of a full binary tree with \(n\) leaves.
public theorem matrixChainOrder_reconstructionNodes {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) :
(matrixChainOrder dimensions hn).time.reconstructionNodes = 2 * n - 1
public def matrixChainOrder {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) : TimeM Cost (Result n) :=
let computation := matrixChainOrderRaw dimensions hn
have hSome : computation.ret.isSome := by
obtain ⟨cell, tree, hResult, hOptimal, hInterval, hCost⟩ :=
matrixChainOrderRaw_spec dimensions hn
change (matrixChainOrderRaw dimensions hn).ret.isSome
rw [hResult]
rfl
{ ret := computation.ret.get hSome, time := computation.time }
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Correctness.lean:65
Uses 7 · Used by 1
TheoremExact event count of matrix-chain ordering#✓fully proved
For every \(n \ge 1\) and all natural numbers \(p_0, \dots, p_n\), the total number of events charged by the matrix-chain ordering algorithm run on the dimensions \(p_0, \dots, p_n\) is \(\binom{n+1}{3} + (2n - 1)\).
The first term counts evaluated split candidates and the second the nodes of the reconstructed tree.
public theorem matrixChainOrder_time {n : Nat} (dimensions : Vector Nat (n + 1))
(hn : 0 < n) :
(matrixChainOrder dimensions hn).time.total =
Nat.choose (n + 1) 3 + (2 * n - 1)
public def matrixChainOrder {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) : TimeM Cost (Result n) :=
let computation := matrixChainOrderRaw dimensions hn
have hSome : computation.ret.isSome := by
obtain ⟨cell, tree, hResult, hOptimal, hInterval, hCost⟩ :=
matrixChainOrderRaw_spec dimensions hn
change (matrixChainOrderRaw dimensions hn).ret.isSome
rw [hResult]
rfl
{ ret := computation.ret.get hSome, time := computation.time }
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Correctness.lean:65
Uses 6 · Used by 1
TheoremCubic bound for matrix-chain ordering#✓fully proved
For every \(n \ge 1\) and all natural numbers \(p_0, \dots, p_n\), the total number of events charged by the matrix-chain ordering algorithm run on the dimensions \(p_0, \dots, p_n\) is at most \(3n^3\).
public theorem matrixChainOrder_time_le_three_mul_cube {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) :
(matrixChainOrder dimensions hn).time.total ≤ 3 * n ^ 3
public def matrixChainOrder {n : Nat}
(dimensions : Vector Nat (n + 1)) (hn : 0 < n) : TimeM Cost (Result n) :=
let computation := matrixChainOrderRaw dimensions hn
have hSome : computation.ret.isSome := by
obtain ⟨cell, tree, hResult, hOptimal, hInterval, hCost⟩ :=
matrixChainOrderRaw_spec dimensions hn
change (matrixChainOrderRaw dimensions hn).ret.isSome
rw [hResult]
rfl
{ ret := computation.ret.get hSome, time := computation.time }
CSLibExt/Algorithms/Lean/DynamicProgramming/MatrixChain/Correctness.lean:65
Uses 5
Loading dependency explorer…
The dependency explorer needs JavaScript and its graph data.