| Safe Haskell | None |
|---|---|
| Language | GHC2024 |
Circuit.Mat
Description
Matrices over a semiring as a category with biproducts.
Overview
Matrices over a semiring are not a metaphor for category theory — they
are the morphisms of a category with biproducts. Objects are finite
index types; a morphism i -> j is one scalar per input/output pair.
Either gives block matrices and is simultaneously product and coproduct
— a biproduct. This is the matrix calculus, and it is a categorical fact,
not an analogy.
The encoding is initial: Mat is the fully-applied leaf, Id / Fun /
Par / Comp are symbolic constructors, and runMat pays only boundary
evidence to collect results.
Evaluation is by pushing a sparse formal sum through the term. Fun needs
no finite evidence; Mat carries its own dictionary; Par is structural
recursion; Comp is composition of formal sums. This is total and
terminating by construction — the free-semimodule semantics of the
category.
Monoidal structure
TensorEither: tensor places two matrices on disjoint wires (block
diagonal). unitl / unitr witness that Void (the uninhabited type)
is the tensor unit, reflecting the fact that a 0 × n or m × 0 matrix
has no entries. Unit laws hold: tensor (unitl . unitl') id = id.
ActionEither: braid permutes index positions — the symmetry of the
biproduct. The slide slide :: Either a (Either b c) -> Either b
(Either a c) is derivable as assoc . tensor braid id . assoc'.
Biproduct structure and the 2-category
The additive monoid on each hom-set gives matrices over a semiring the structure of a locally posetal 2-category when the semiring is idempotent:
- 0-cells: finite types (the index sets).
- 1-cells: matrices
i -> j, elements of the free semimodule. - 2-cells: for idempotent semirings (
Bool,Tropical), the additive monoid+induces a partial order:
m ≤ n iff m + n = n
This is the natural 2-categorical structure: matrix inequality is a 2-cell, and composition is monotone in both arguments.
The biproduct structure means Either is simultaneously product and
coproduct. For any finite types a, b there exist:
- projections
p1 :: a ⊕ b -> a,p2 :: a ⊕ b -> b(implemented as pattern-matching onLeft/Right); - injections
i1 :: a -> a ⊕ b,i2 :: b -> a ⊕ b(theLeftandRightconstructors).
satisfying the biproduct equations:
p1 . i1 = id, p2 . i2 = id, p1 . i2 = 0, p2 . i1 = 0, i1 . p1 + i2 . p2 = id
where 0 is the zero matrix and + is pointwise addition. Every matrix
decomposes into its components via these projections and injections.
Kleene star as closure
The Kleene star starM computes the reflexive-transitive closure of a
square matrix in the 2-category. For idempotent semirings, this is the
least fixed point of X ↦ I + A · X, i.e. the closure operator of the
locally posetal 2-category:
Bool: transitive closure of a graph (reachability).Tropical(min-plus): all-pairs shortest paths (Floyd-Warshall).StarSemiring String: regular expression for all paths.
These carriers are not merely semirings; the canonical ones are
quantales — complete join-semilattices with monoidal multiplication
distributing over arbitrary joins. See NumHask.Algebra.Quantale. In
that setting matrix addition is pointwise join and the Kleene star is the
least fixed point I + A + A² + …. starM uses the StarSemiring
fragment, which is exactly the iterative-join part of a quantale.
Kleene's algorithm enumerates intermediate indices: O(|V|³) in the
number of elements of the Finite type.
Trace as Schur-complement
traceMat computes the trace (feedback loop) of a block matrix:
┌ ┐ │ a c │ : a ⊕ b → a ⊕ c │ b d │ └ ┘ traceMat → d + c · a* · b : b → c
This is exactly the Schur-complement formula for biproduct categories. The trace satisfies the standard traced-monoidal laws (naturality, sliding, vanishing, yanking) — see the docspec examples.
Traced instance
With local Category carrying Ob (Mat s) a = Finite a, the feedback
channel of trace carries Finite evidence. traceMat is therefore a
lawful Traced Either instance (Schur-complement / star-closure).
Relation to harpie
harpie provides statically-shaped arrays (Harpie.Fixed.Array) with
type-level dimensions. Where Finite enumerates a type's inhabitants at
runtime, a harpie Array [n] s knows its shape at compile time via
KnownNat n. A matrix i -> j could be a harpie array of shape [i, j]
with indices drawn from Fin n@ (harpie's type-level finite set). The
trace formula would still be the Schur-complement; the difference is that
the index set is a Nat rather than a type-class dictionary.
Benchmarking (Finite enumeration vs matrix ops) shows that universe
enumeration cost is linear in the number of inhabitants and typically
dominated by the O(|V|³) star-closure computation for non-trivial
matrices. See the enumeration spike section below for details.
Synopsis
- class Eq a => Finite a where
- universe :: [a]
- data Mat s i j where
- runMat :: (Additive s, Multiplicative s, Eq j) => Mat s i j -> i -> j -> s
- starM :: (StarSemiring s, Additive s, Multiplicative s, Finite a) => Mat s a a -> Mat s a a
- traceMat :: (StarSemiring s, Additive s, Multiplicative s, Finite a, Finite b, Finite c) => Mat s (Either a b) (Either a c) -> Mat s b c
- dot :: (StarSemiring s, Additive s, Multiplicative s, Eq a, Finite a) => (a -> s) -> (a -> s) -> s
- evalMat :: (Additive s, Multiplicative s, Eq a, Eq b, Finite a, Finite b) => Mat s (a, (a, b)) b
- curryMat :: (Additive s, Multiplicative s, Eq c, Finite a, Finite b, Finite c) => Mat s (a, b) c -> Mat s a (b, c)
- uncurryMat :: (Additive s, Multiplicative s, Eq b, Finite a, Finite b, Finite c) => Mat s a (b, c) -> Mat s (a, b) c
- newtype Dual a = Dual a
- transposeMat :: (Additive s, Multiplicative s, Finite i, Finite j, Eq i, Eq j) => Mat s i j -> Mat s j i
- dualMat :: (Additive s, Multiplicative s, Finite i, Finite j, Eq i, Eq j) => Mat s i j -> Mat s (Dual j) (Dual i)
- class Conjugate s where
- conjugateS :: s -> s
- conjugateMat :: (Conjugate s, Additive s, Multiplicative s, Finite i, Finite j, Eq j) => Mat s i j -> Mat s i j
- type MatrixM (n :: Nat) a = Matrix n n a
- cholM :: forall (n :: Nat) a. (KnownNat n, KnownNats '[n, n], ExpField a) => MatrixM n a -> MatrixM n a
- invtriM :: forall (n :: Nat) a. (KnownNat n, KnownNats '[n, n], Subtractive a, Divisive a) => MatrixM n a -> MatrixM n a
- inverseM :: forall (n :: Nat) a. (KnownNat n, KnownNats '[n, n], ExpField a) => MatrixM n a -> MatrixM n a
- newtype MatField (n :: Nat) a = MatField {
- unMatField :: MatrixM n a
Finite index types
class Eq a => Finite a where Source #
A finite index type: enumerable and comparable.
Instances
| Finite Void Source # | |
Defined in Circuit.Mat | |
| Finite () Source # | |
Defined in Circuit.Mat | |
| Finite Bool Source # | |
Defined in Circuit.Mat | |
| Finite a => Finite (Dual a) Source # | |
Defined in Circuit.Mat | |
| KnownNat n => Finite (F n) Source # | Enumerate all
|
Defined in Circuit.Mat.Harpie | |
| (Finite a, Finite b) => Finite (Either a b) Source # | |
Defined in Circuit.Mat | |
| KnownNats s => Finite (Fins s) Source # | |
Defined in Circuit.Mat.Array | |
| (Finite a, Finite b) => Finite (a, b) Source # | |
Defined in Circuit.Mat | |
Matrices
Matrix over a semiring s, indexed by i (rows) and j (columns).
Matis the concrete, fully-applied leaf; it carriesFinitedictionaries so thatpushcan enumerate indices when needed.Id,Fun,Par, andCompare symbolic; they are evaluated bypush.
Note: Par does not require Finite — it is a structural constructor
whose Finite evidence is supplied by the subterms or by Mat leaves.
The Tensor instance uses Par for tensor.
Constructors
| Mat :: forall i j s. (Finite i, Finite j) => (i -> j -> s) -> Mat s i j | |
| Id :: forall s i. Mat s i i | |
| Fun :: forall i j s. (i -> j) -> Mat s i j | |
| Par :: forall s a b c d. Mat s a b -> Mat s c d -> Mat s (Either a c) (Either b d) | |
| Comp :: forall s j1 j i. Mat s j1 j -> Mat s i j1 -> Mat s i j |
Instances
| (Additive s, Multiplicative s) => Channel Either (Mat s :: Type -> Type -> Type) Source # | |
| (Additive s, Multiplicative s) => Strength Either (Mat s :: Type -> Type -> Type) Source # | |
| Action Either (Mat s :: Type -> Type -> Type) Source # | Coproduct symmetry on
|
| Tensor Either (Mat s :: Type -> Type -> Type) Source # | |
| Unital Either (Mat s :: Type -> Type -> Type) Source # | Coproduct tensor action on
|
| Category (Mat s :: Type -> Type -> Type) Source # | |
| LiftFun (Mat r) Source # | |
Defined in Circuit.Mat.Array | |
| Tabular (Mat r) Source # | |
Defined in Circuit.Mat.Array Methods tabulateArr :: TabulateOb (Mat r) i j => (i -> j) -> Mat r i j Source # indexArr :: TabulateOb (Mat r) i j => Mat r i j -> i -> j Source # | |
| type TabulateOb (Mat r) i j Source # | |
Defined in Circuit.Mat.Array | |
runMat :: (Additive s, Multiplicative s, Eq j) => Mat s i j -> i -> j -> s Source #
Run a matrix at a single input/output pair.
Equality evidence is required only at the boundary (the output index).
Closure
starM :: (StarSemiring s, Additive s, Multiplicative s, Finite a) => Mat s a a -> Mat s a a Source #
Reflexive-transitive closure of a square matrix.
Implements Kleene's algorithm: for each intermediate index k, update all
entries m(i,j) := m(i,j) + m(i,k) * star(m(k,k)) * m(k,j). The closure is
the original matrix with the loop applied, plus the identity at the end,
so starM m = I + m + m² + ... for arbitrary star semirings.
For idempotent semirings, this is the closure operator of the locally
posetal 2-category: starM computes the least fixed point of
X ↦ I + A · X.
>>>let g = Mat (\i j -> case (i, j) of (A, B) -> True; (B, C) -> True; _ -> False) :: Mat Bool Node Node>>>runMat (starM g) A CTrue
>>>let w = Mat (\i j -> case (i, j) of (A, B) -> MinPlus 1; (B, C) -> MinPlus 2; (A, C) -> MinPlus 10; _ -> zero) :: Mat (MinPlus Double) Node Node>>>getMinPlus (runMat (starM w) A C)3.0
Trace (explicit finite-channel form)
traceMat :: (StarSemiring s, Additive s, Multiplicative s, Finite a, Finite b, Finite c) => Mat s (Either a b) (Either a c) -> Mat s b c Source #
Trace over an explicit finite feedback channel a.
For a block matrix [[a, a->c], [b->a, b->c]], the trace is the
Schur-complement:
traceMat [[a, c], [b, d]] = d + c · a* · b
This is the standard trace for biproduct categories, satisfying the traced-monoidal laws.
Naturality
Post-composing the data output with g before tracing equals tracing then
post-composing with g. The blocks are coupled so the loop is live.
>>>let fFun (Left ()) (Left ()) = False; fFun (Left ()) (Right B) = True; fFun (Right A) (Left ()) = True; fFun _ _ = False>>>let f = Mat fFun :: Mat Bool (Either () Node) (Either () Node)>>>let g = Mat (\i j -> case (i, j) of (B, C) -> True; _ -> False) :: Mat Bool Node Node>>>sameMat (traceMat (Comp (Par Id g) f)) (Comp g (traceMat f))True
Sliding
Moving g through the feedback wire. Feedback channel is Bool, g is
the swap permutation, and the blocks are coupled so the loop is live.
>>>let gSwap = Mat (\i j -> i /= j) :: Mat Bool Bool Bool>>>let fSlideFun (Left False) (Left True) = True; fSlideFun (Left True) (Right B) = True; fSlideFun (Right A) (Left False) = True; fSlideFun (Right B) (Right C) = True; fSlideFun _ _ = False>>>let fSlide = Mat fSlideFun :: Mat Bool (Either Bool Node) (Either Bool Node)>>>sameMat (traceMat (Comp (Par gSwap Id) fSlide)) (traceMat (Comp fSlide (Par gSwap Id)))True
Vanishing
Tracing over the unit channel () collapses to the Schur-complement formula
mbc + mac * star maa * mba.
>>>let blockFun (Left ()) (Left ()) = False; blockFun (Left ()) (Right B) = True; blockFun (Right A) (Left ()) = True; blockFun _ _ = False>>>let block = Mat blockFun :: Mat Bool (Either () Node) (Either () Node)>>>let expected = Mat (\i j -> case (i, j) of (A, B) -> True; _ -> False) :: Mat Bool Node Node>>>sameMat (traceMat block) expectedTrue
Regression: nested Comp terminates
The old reduceComp fallthrough looped on m . m . m.
>>>let m = Mat (\i j -> case (i, j) of (A, A) -> True; (A, B) -> True; (B, B) -> True; _ -> False) :: Mat Bool Node Node>>>runMat (Comp m (Comp m m)) A BTrue>>>runMat (Comp m (Comp m m)) B AFalse
Contraction
dot :: (StarSemiring s, Additive s, Multiplicative s, Eq a, Finite a) => (a -> s) -> (a -> s) -> s Source #
Categorical dot product of two vectors over the same finite index set.
Both vectors are given as functions from the index set to the semiring. The
construction uses only Par, the coproduct swap, and traceMat — no
row/column naming, no dimension indices.
The feedback channel in the trace is the shared index set a; because the
block on that channel is zero, star reduces to one and the Schur
complement becomes the sum of products.
Linear implication (explicit finite-channel form)
evalMat :: (Additive s, Multiplicative s, Eq a, Eq b, Finite a, Finite b) => Mat s (a, (a, b)) b Source #
Compact-closed implication on index pairs: A ⊸ B is the space of
matrix entries (a, b). curry/uncurry reshape; eval contracts
the repeated index (transpose + product).
These are named constrained combinators rather than Lolli class methods
because the unconstrained tower no longer carries object evidence.
curryMat :: (Additive s, Multiplicative s, Eq c, Finite a, Finite b, Finite c) => Mat s (a, b) c -> Mat s a (b, c) Source #
uncurryMat :: (Additive s, Multiplicative s, Eq b, Finite a, Finite b, Finite c) => Mat s a (b, c) -> Mat s (a, b) c Source #
Involution structure (Ex6: reversibility, duality, conjugation)
Object-level duality marker. Dual i is the dual index set of i;
it carries the same inhabitants but is a distinct type, so duality is
visible at the object level.
Constructors
| Dual a |
transposeMat :: (Additive s, Multiplicative s, Finite i, Finite j, Eq i, Eq j) => Mat s i j -> Mat s j i Source #
Reversibility: matrix transpose.
This is the dagger/reversibility involution. It is contravariant and
identity-on-objects: a morphism i -> j becomes j -> i, but the object
labels themselves do not change.
dualMat :: (Additive s, Multiplicative s, Finite i, Finite j, Eq i, Eq j) => Mat s i j -> Mat s (Dual j) (Dual i) Source #
Duality: the dual of a linear map f :: i -> j is f* :: j* -> i*.
This is not identity-on-objects: it tags both source and target with
Dual. Over a field it is the transpose carried by the dual basis.
class Conjugate s where Source #
Methods
conjugateS :: s -> s Source #
Instances
| Conjugate Double Source # | |
Defined in Circuit.Mat Methods conjugateS :: Double -> Double Source # | |
| (Distributive s, Subtractive s) => Conjugate (Complex s) Source # | |
Defined in Circuit.Mat Methods conjugateS :: Complex s -> Complex s Source # | |
conjugateMat :: (Conjugate s, Additive s, Multiplicative s, Finite i, Finite j, Eq j) => Mat s i j -> Mat s i j Source #
Conjugation: the composite of duality and reversibility, expressed with the standard identification of a finite-dimensional space with its dual.
It is a covariant endofunctor on objects and, over Complex, is entrywise
complex conjugation. It is distinct from both transposeMat
(identity-on-objects contravariant) and dualMat (not identity-on-objects).
Dense field calculus (re-exported from Circuit.Mat.Field)
cholM :: forall (n :: Nat) a. (KnownNat n, KnownNats '[n, n], ExpField a) => MatrixM n a -> MatrixM n a Source #
Cholesky decomposition using the Cholesky-Crout algorithm.
>>>e = F.array @[3,3] @Double [4,12,-16,12,37,-43,-16,-43,98]>>>pretty (cholM e)[[2.0,0.0,0.0], [6.0,1.0,0.0], [-8.0,5.0,3.0]]>>>F.mult (cholM e) (F.transpose (cholM e)) == eTrue
invtriM :: forall (n :: Nat) a. (KnownNat n, KnownNats '[n, n], Subtractive a, Divisive a) => MatrixM n a -> MatrixM n a Source #
Inversion of a triangular matrix.
>>>t = F.array @[3,3] @Double [1,0,1,0,1,2,0,0,1]>>>pretty (invtriM t)[[1.0,0.0,-1.0], [0.0,1.0,-2.0], [0.0,0.0,1.0]]>>>F.ident @[3,3] == F.mult t (invtriM t)True
inverseM :: forall (n :: Nat) a. (KnownNat n, KnownNats '[n, n], ExpField a) => MatrixM n a -> MatrixM n a Source #
Inverse of a square matrix via Cholesky decomposition.
>>>e = F.array @[3,3] @Double [4,12,-16,12,37,-43,-16,-43,98]>>>pretty (inverseM e)[[49.36111111111111,-13.555555555555554,2.1111111111111107], [-13.555555555555554,3.7777777777777772,-0.5555555555555555], [2.1111111111111107,-0.5555555555555555,0.1111111111111111]]
newtype MatField (n :: Nat) a Source #
Newtype wrapper that gives square matrices a multiplicative (and, over a field, divisible) structure.
The unit is the identity matrix; multiplication is the usual matrix product. Division uses Cholesky-based inversion.
Constructors
| MatField | |
Fields
| |