Core dense matrix definitions for hex-matrix.
This module models matrices as Vector (Vector R m) n and provides the
basic executable operations needed by later linear-algebra algorithms:
row/column accessors, zero and identity matrices, dot products,
matrix-vector multiplication, matrix-matrix multiplication, and norm-squared
helpers.
Dense n × m matrices over R. Opaque one-field structure wrapping the row
data; consumers go through rows/getRow/ofRows/ofFn and M[i] / M[(i,j)],
never the data projection, so the representation can change later.
- ofRows :: (
Implementation detail — use
Matrix.rows/getRow, never this projection.- )
Instances For
Equations
- Hex.instBEqMatrix = { beq := Hex.instBEqMatrix.beq }
Dot product of two vectors.
This List.finRange form is the reference definition the entry lemmas reason
about; crucially it kernel-reduces, so #guard/decide checks over
dotProduct (e.g. memLattice membership) stay evaluable — core Fin.foldl
does not reduce in the kernel. Compiled code runs the allocation-free
Fin.foldl loop dotProductImpl via the @[csimp] below.
TODO: once https://github.com/leanprover/lean4/pull/14267 (make Fin.foldl
reduce in the kernel) lands and this project's toolchain is bumped past it,
collapse this reference/compiled split: define
dotProduct directly as the native Fin.foldl loop and delete dotProductImpl
and dotProduct_eq_impl. The native form will then kernel-reduce on its own, so
the memLattice decide checks keep working without the List.finRange
reference allocation.
Equations
- u.dotProduct v = List.foldl (fun (acc : R) (i : Fin n) => acc + u[i] * v[i]) 0 (List.finRange n)
Instances For
Allocation-free implementation of dotProduct: a Fin.foldl loop that never
materializes the List.finRange n index list. Swapped in for compiled code by the
@[csimp] lemma; dotProduct remains the reference form for proofs.
Instances For
Row access M[i] for i : Fin n. Deliberately noncomputable: for a
future flat (Vector R (n*m)) representation, materializing a whole row just to
read it — and especially M[i][j] to read one entry — is the wrong cost model.
This instance exists only so proofs may write M[i] / M[i][j]; executable
code must use the computable getRow for rows and M[(i, j)] (O(1)) for single
entries. Any compiled definition that reaches for M[i] will fail to compile,
which is the intended guard.
Equations
- Hex.Matrix.instGetElemFinVectorTrue = { getElem := fun (M : Hex.Matrix R n m) (i : Fin n) (x : True) => M.getRow i }
Row access by a Nat index. Also noncomputable; see the Fin n instance.
Equations
- Hex.Matrix.instGetElemNatVectorLt = { getElem := fun (M : Hex.Matrix R n m) (i : Nat) (h : i < n) => M.getRow ⟨i, h⟩ }
The pair entry access (computable, O(1)) agrees with the nested row-then-element
form. The nested form is the simp-normal form the entry lemmas are stated in, so
proofs about the computable M[(i, j)] line up with the M[i][j] lemmas.
Build a matrix from an entry function.
Equations
- Hex.Matrix.ofFn f = { data := Vector.ofFn fun (i : Fin n) => Vector.ofFn fun (j : Fin m) => f i j }
Instances For
The all-zero matrix.
Equations
- Hex.Matrix.zero n m = Hex.Matrix.ofFn fun (x : Fin n) (x_1 : Fin m) => 0
Instances For
Equations
- Hex.Matrix.instZeroOfOfNatOfNatNat = { zero := Hex.Matrix.zero n m }
Every row of the zero matrix is the zero vector.
Every column of the zero matrix is the zero vector.
The identity matrix.
Equations
- Hex.Matrix.identity n = Hex.Matrix.ofFn fun (i j : Fin n) => if i = j then 1 else 0
Instances For
Equations
- Hex.Matrix.instAdd = { add := Hex.Matrix.add }
Equations
- Hex.Matrix.instNeg = { neg := Hex.Matrix.neg }
Equations
- Hex.Matrix.instSub = { sub := Hex.Matrix.sub }
Multiply two matrices, using the naive algorithm.
This reads each column col N j and is the reference definition the entry lemmas
reason about. Compiled code runs mulImpl, which transposes N once (via the
@[csimp] below) so each column is materialized a single time instead of being
rebuilt for every row of M.
We intend to provide Strassen-Winograd with a customizable algorithm for small sizes later.
Equations
- M.mul N = Hex.Matrix.ofFn fun (i : Fin n) (j : Fin k) => (M.row i).dotProduct (N.col j)
Instances For
Cache-friendly implementation of mul: transpose N once (turning its columns
into contiguous rows), then take row-by-row dot products, so each column is built a
single time rather than once per row of M. Swapped in for compiled code by the
@[csimp] lemma; mul stays the column-based reference form for proofs.
Equations
- M.mulImpl N = Hex.Matrix.ofFn fun (i : Fin n) (j : Fin k) => (M.row i).dotProduct (N.transpose.row j)
Instances For
Homogeneous multiplication on square matrices, agreeing with the
heterogeneous HMul. This is the Mul instance Mathlib's Semiring/Ring
structures build on; see HexMatrixMathlib.
Equations
Multiply a row vector by a matrix, v * M. Equal to transpose M * v; the
j-th entry is ∑ i, M[i][j] * v[i], the combination of the rows of M with
coefficients v.
Equations
- Hex.Matrix.vecMul v M = M.transpose * v
Instances For
The identity matrix is its own transpose.
In-place modification of row i. Linear in M: destructuring consumes M,
so when M is uniquely referenced the row is owned and Vector.modify updates
the backing store without copying.
Instances For
In-place indexed row map: replace row i by f i (row i) for every i,
threading M through a Fin.foldl of per-row modifyRows. No intermediate
index list is allocated, and each row update is in place when M is uniquely
referenced (Vector.modify frees the row slot before applying f). This is the
shared engine for the in-place column and diagonal scatters (setCol,
modifyCol).
Equations
- M.mapRowsIdx f = Fin.foldl n (fun (M : Hex.Matrix R n m) (i : Fin n) => M.modifyRow (↑i) (f i)) M
Instances For
The row data of mapRowsIdx is the corresponding Fin.foldl of Vector.modifys.
Row r of mapRowsIdx M f is f r applied to the original row r.
Replace column dst of M with the entry function v. In-place via
mapRowsIdx: each row's single dst entry is set, reusing the freed row slot,
rather than rebuilding the whole matrix with ofFn.
Instances For
Transposing a row replacement is a column replacement on the transpose:
setRow on M corresponds to setCol on Mᵀ. This is the bridge the
determinant row laws route through to reuse the column laws.
In-place per-entry column modify: replace each entry M[i][dst] by
g i M[i][dst], every other column unchanged. In-place via mapRowsIdx,
analogous to setCol.
Equations
Instances For
Scalar action on a matrix, delegated to the row data. The single sanctioned
SMul instance for matrices: the Mathlib bridge layer reuses it rather than
declaring its own, so there is no overlapping instance. Matches the action the
former abbrev inherited from Vector, so c • M keeps its previous meaning.
Equations
- Hex.Matrix.instSMul = { smul := fun (c : S) (M : Hex.Matrix R n m) => { data := c • M.data } }