haskellify finite-field.4
This commit is contained in:
parent
91cfd2397a
commit
3581c804cf
@ -19,7 +19,7 @@ zipAdd (c:cs) (d:ds) = (c + d):zipAdd cs ds
|
||||
------------------------------------------------------------------------------}
|
||||
|
||||
-- A polynomial is its ascending list of coefficients (of type a)
|
||||
newtype Polynomial a = Poly { coeffs :: [a] } deriving Functor
|
||||
newtype Polynomial a = Poly { coeffs :: [a] } deriving (Functor)
|
||||
|
||||
instance (Eq a, Num a) => Num (Polynomial a) where
|
||||
(+) x@(Poly as) y@(Poly bs) = Poly $ zipAdd as bs
|
||||
@ -47,9 +47,9 @@ instance (Eq a, Num a) => Eq (Polynomial a) where
|
||||
|
||||
|
||||
-- Interpret a number's base-b expansion as a polynomial
|
||||
asPoly :: Int -> Int -> Polynomial Int
|
||||
-- Build a list with f, which returns either Nothing
|
||||
-- or Just (next element of list, next argument to f)
|
||||
asPoly :: Int -> Int -> Polynomial Int
|
||||
asPoly b = Poly . unfoldr f where
|
||||
-- Divide x by b. Emit the remainder and recurse with the quotient.
|
||||
f x | x /= 0 = Just $ swap $ divMod x b
|
||||
@ -57,10 +57,9 @@ asPoly b = Poly . unfoldr f where
|
||||
| otherwise = Nothing
|
||||
|
||||
-- Horner evaluation of a polynomial at the integer b
|
||||
evalPoly :: Int -> Polynomial Int -> Int
|
||||
-- Start with the highest coefficient
|
||||
-- Multiply by b at each step and add the coefficient of the next term
|
||||
evalPoly b (Poly p) = foldr (\y acc -> acc*b + y) 0 p
|
||||
evalPoly b (Poly p) = foldr (\y acc -> b*acc + y) 0 p
|
||||
|
||||
-- Divide the polynomial ps by qs (coefficients in descending degree order)
|
||||
synthDiv' :: (Eq a, Num a) => [a] -> [a] -> ([a], [a])
|
||||
@ -122,7 +121,7 @@ irreducibles n = go [] $ allMonics n where
|
||||
-
|
||||
------------------------------------------------------------------------------}
|
||||
|
||||
newtype Matrix a = Mat { unMat :: Array (Int, Int) a } deriving (Functor, Eq)
|
||||
newtype Matrix a = Mat { unMat :: Array (Int, Int) a } deriving (Functor)
|
||||
|
||||
asScalar = Mat . listArray ((0,0),(0,0)) . pure
|
||||
|
||||
@ -148,12 +147,22 @@ zipWithArr f a b
|
||||
mapRange :: Ix i => (i -> e) -> (i, i) -> [(i, e)]
|
||||
mapRange g r = map (\x -> (x, g x)) $ range r
|
||||
|
||||
instance (Num a, Eq a) => Eq (Matrix a) where
|
||||
(==) (Mat x) (Mat y)
|
||||
| (0,0) == (snd $ bounds x) = and [x!(0,0) == if m == n then u else 0 | ((m,n), u) <- assocs y]
|
||||
| (0,0) == (snd $ bounds y) = and [x!(0,0) == if m == n then u else 0 | ((m,n), u) <- assocs x]
|
||||
| otherwise = x == y
|
||||
|
||||
|
||||
instance Num a => Num (Matrix a) where
|
||||
(+) (Mat x) (Mat y)
|
||||
| (0,0) == (snd $ bounds x) = Mat $ fmap ((x!(0,0))+) y --adding scalars
|
||||
| (0,0) == (snd $ bounds x) && (0,0) == (snd $ bounds y) = Mat $ zipWithArr (+) x y
|
||||
| (0,0) == (snd $ bounds x) = Mat y + (((x!(0,0)) *) <$> eye ((+1) $ snd $ snd $ bounds y)) --adding scalars
|
||||
| (0,0) == (snd $ bounds y) = Mat x + (((y!(0,0)) *) <$> eye ((+1) $ snd $ snd $ bounds x)) --adding scalars
|
||||
| otherwise = Mat $ zipWithArr (+) x y
|
||||
(*) x y
|
||||
| (0,0) == (snd $ bounds $ unMat x) = fmap (((unMat x)!(0,0))*) y --multiplying scalars
|
||||
| (0,0) == (snd $ bounds $ unMat y) = fmap (((unMat y)!(0,0))*) x --multiplying scalars
|
||||
| otherwise = toMatrix $ matMul' (fromMatrix x) (fromMatrix $ transposeM y)
|
||||
abs = fmap abs
|
||||
negate = fmap negate
|
||||
@ -183,7 +192,7 @@ eye :: Num a => Int -> Matrix a
|
||||
eye n = Mat $ unMat (zero n) // take n [((i,i), 1) | i <- [0..]]
|
||||
|
||||
-- Companion matrix
|
||||
companion :: Polynomial Int -> Matrix Int
|
||||
companion :: (Eq a, Num a) => Polynomial a -> Matrix a
|
||||
companion (Poly ps)
|
||||
| last ps' /= 1 = error "Cannot find companion matrix of non-monic polynomial"
|
||||
| otherwise = Mat $ array ((0,0), (n-1,n-1)) $ lastRow ++ shiftI where
|
||||
|
||||
1
posts/finite-field/4/Previous.hs
Symbolic link
1
posts/finite-field/4/Previous.hs
Symbolic link
@ -0,0 +1 @@
|
||||
../2/Previous.hs
|
||||
@ -5,15 +5,102 @@ description: |
|
||||
format:
|
||||
html:
|
||||
html-math-method: katex
|
||||
jupyter: python3
|
||||
date: "2024-02-03"
|
||||
date-modified: "2025-07-20"
|
||||
date-modified: "2025-08-05"
|
||||
categories:
|
||||
- algebra
|
||||
- finite field
|
||||
- haskell
|
||||
---
|
||||
|
||||
```{haskell}
|
||||
--| echo: false
|
||||
|
||||
:l Previous.hs
|
||||
|
||||
import Data.Array (ixmap, bounds, (!), array, range)
|
||||
import Data.List (intercalate)
|
||||
import IHaskell.Display (markdown)
|
||||
|
||||
import Previous (
|
||||
Matrix(Mat, unMat), Polynomial(Poly, coeffs),
|
||||
asPoly, evalPoly, synthDiv,
|
||||
companion, zero, eye, fromMatrix, toMatrix
|
||||
)
|
||||
|
||||
-- Explicit Matrix operations
|
||||
(|+|) :: Num a => Matrix a -> Matrix a -> Matrix a
|
||||
(|+|) = (+)
|
||||
|
||||
(|*|) :: Num a => Matrix a -> Matrix a -> Matrix a
|
||||
(|*|) = (*)
|
||||
|
||||
-- Original determinant implementation, instead of the imported one based on Faddeev-Leverrier
|
||||
determinant :: (Num a, Eq a) => Matrix a -> a
|
||||
determinant (Mat xs) = determinant' xs where
|
||||
-- Evaluate (-1)^i without repeated multiplication
|
||||
parity i = if even i then 1 else -1
|
||||
-- Map old array addresses to new ones when eliminating row 0, column i
|
||||
rowMap i (x,y) = (x+1, if y >= i then y+1 else y)
|
||||
-- Recursive determinant Array
|
||||
determinant' xs
|
||||
-- Base case: 1x1 matrix
|
||||
| n == 0 = xs!(0,0)
|
||||
-- Sum of cofactor expansions
|
||||
| otherwise = sum $ map cofactor [0..n] where
|
||||
-- Produce the cofactor of row 0, column i
|
||||
cofactor i
|
||||
| xs!(0,i) == 0 = 0
|
||||
| otherwise = (parity i) * xs!(0,i) * determinant' (minor i)
|
||||
-- Furthest extent of the bounds, i.e., the size of the matrix
|
||||
(_,(n,_)) = bounds xs
|
||||
-- Build a new Array by eliminating row 0 and column i
|
||||
minor i = ixmap ((0,0),(n-1,n-1)) (rowMap i) xs
|
||||
|
||||
-- Characteristic Polynomial
|
||||
charpoly :: (Num a, Eq a) => Matrix a -> Polynomial a
|
||||
charpoly xs = determinant $ eyeLambda |+| negPolyXs where
|
||||
-- Furthest extent of the bounds, i.e., the size of the matrix
|
||||
(_,(n,_)) = bounds $ unMat xs
|
||||
-- Negative of input matrix, after being converted to polynomials
|
||||
negPolyXs = fmap (\x -> Poly [-x]) xs
|
||||
-- Identity matrix times lambda (encoded as Poly [0, 1])
|
||||
eyeLambda = (\x -> Poly [x] * Poly [0, 1]) <$> eye (n+1)
|
||||
|
||||
-- Convert Polynomial to LaTeX string
|
||||
texifyPoly' :: (Num a, Eq a) => String -> (a -> String) -> Polynomial a -> String
|
||||
texifyPoly' var f (Poly xs) = texify' $ zip xs [0..] where
|
||||
texify' [] = "0"
|
||||
texify' ((c, n):xs)
|
||||
| all ((==0) . fst) xs = showPow c n
|
||||
| c == 0 = texify' xs
|
||||
| otherwise = showPow c n ++ " + " ++ texify' xs
|
||||
showPow c 0 = f c
|
||||
showPow 1 1 = var
|
||||
showPow c 1 = f c ++ showPow 1 1
|
||||
showPow 1 n = var ++ "^{" ++ show n ++ "}"
|
||||
showPow c n = f c ++ showPow 1 n
|
||||
|
||||
-- Convert Polynomial to LaTeX string
|
||||
texifyPoly :: (Num a, Eq a, Show a) => Polynomial a -> String
|
||||
texifyPoly = texifyPoly' "x" show
|
||||
|
||||
texPolyAsPositional' x (Poly xs) = (++ "_{" ++ x ++ "}") $
|
||||
reverse xs >>= (\x -> if x < 0 then "\\bar{" ++ show (-x) ++ "}" else show x)
|
||||
|
||||
texPolyAsPositional = texPolyAsPositional' "x"
|
||||
|
||||
-- Convert matrix to LaTeX string
|
||||
texifyMatrix' :: (a -> String) -> Matrix a -> String
|
||||
texifyMatrix' f mat = surround mat' where
|
||||
mat' = intercalate " \\\\ " $ map (intercalate " & " . map f) $
|
||||
fromMatrix mat
|
||||
surround = ("\\left( \\begin{matrix}" ++) . (++ "\\end{matrix} \\right)")
|
||||
|
||||
texifyMatrix :: Show a => Matrix a -> String
|
||||
texifyMatrix = texifyMatrix' show
|
||||
```
|
||||
|
||||
|
||||
The [last post](../3) in this series focused on understanding some small linear groups
|
||||
and implementing them on the computer over both a prime field and prime power field.
|
||||
@ -45,6 +132,53 @@ $$
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| code-summary: "Equivalent Haskell"
|
||||
|
||||
data F4 = ZeroF4 | OneF4 | AlphaF4 | Alpha2F4 deriving Eq
|
||||
field4 = [ZeroF4, OneF4, AlphaF4, Alpha2F4]
|
||||
|
||||
instance Show F4 where
|
||||
show ZeroF4 = "0"
|
||||
show OneF4 = "1"
|
||||
show AlphaF4 = "α"
|
||||
show Alpha2F4 = "α^2"
|
||||
|
||||
-- Addition and multiplication over F4
|
||||
instance Num F4 where
|
||||
(+) ZeroF4 x = x
|
||||
(+) OneF4 AlphaF4 = Alpha2F4
|
||||
(+) OneF4 Alpha2F4 = AlphaF4
|
||||
(+) AlphaF4 Alpha2F4 = OneF4
|
||||
(+) x y = if x == y then ZeroF4 else y + x
|
||||
|
||||
(*) ZeroF4 x = ZeroF4
|
||||
(*) x ZeroF4 = ZeroF4
|
||||
(*) OneF4 x = x
|
||||
(*) AlphaF4 AlphaF4 = Alpha2F4
|
||||
(*) Alpha2F4 Alpha2F4 = AlphaF4
|
||||
(*) AlphaF4 Alpha2F4 = OneF4
|
||||
(*) x y = y * x
|
||||
|
||||
abs = id
|
||||
negate = id
|
||||
signum = id
|
||||
fromInteger = (cycle field4 !!) . fromInteger
|
||||
|
||||
|
||||
-- Companion matrix of `p`, an irreducible polynomial of degree 2 over GF(2)
|
||||
cP :: (Num a, Eq a, Integral a) => Matrix a
|
||||
cP = companion $ Poly [1, 1, 1]
|
||||
|
||||
f ZeroF4 = zero 2
|
||||
f OneF4 = eye 2
|
||||
f AlphaF4 = cP
|
||||
f Alpha2F4 = (`mod` 2) <$> cP |*| cP
|
||||
|
||||
field4M = map f field4
|
||||
```
|
||||
|
||||
Finally, we constructed GL(2, 4) using matrices of matrices
|
||||
-- not [block matrices](https://en.wikipedia.org/wiki/Block_matrix)!
|
||||
This post will focus on studying this method in slightly more detail.
|
||||
@ -69,31 +203,34 @@ More explicitly, we looked at a matrix *B* in SL(2, 4) which had the property
|
||||
that it was cyclic of order five.
|
||||
Then, to work with it without relying on symbols, we simply applied *f* over the contents of the matrix.
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
f^* : \mathbb{F}_4 {}^{2 \times 2}
|
||||
\longrightarrow
|
||||
(\mathbb{F}_2 {}^{2 \times 2})^{2 \times 2}
|
||||
\\[10pt]
|
||||
B = \left(\begin{matrix}
|
||||
0 & \alpha \\
|
||||
\alpha^2 & \alpha^2
|
||||
\end{matrix} \right)
|
||||
\\
|
||||
B^* = f^*(B)
|
||||
= \left(\begin{matrix}
|
||||
f(0) & f(\alpha) \\
|
||||
f(\alpha^2) & f(\alpha^2)
|
||||
\end{matrix} \right)
|
||||
= \left(\begin{matrix}
|
||||
\left(\begin{matrix} 0 & 0 \\ 0 & 0 \end{matrix} \right)
|
||||
& \left(\begin{matrix} 0 & 1 \\ 1 & 1 \end{matrix} \right)
|
||||
\\
|
||||
\left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
& \left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
\end{matrix} \right)
|
||||
\end{gather*}
|
||||
$$
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
-- Starred maps are instances of fmap composed with modding out
|
||||
-- by the characteristic
|
||||
|
||||
fStar :: (Eq a, Num a, Integral a) => Matrix F4 -> Matrix (Matrix a)
|
||||
fStar = fmap (fmap (`mod` 2) . f)
|
||||
|
||||
mBOrig = toMatrix [[ZeroF4, AlphaF4], [Alpha2F4, Alpha2F4]]
|
||||
mBStar = fStar mBOrig
|
||||
|
||||
markdown $ "$$\\begin{gather*}" ++ concat [
|
||||
-- First row, type of fStar
|
||||
"f^* : \\mathbb{F}_4 {}^{2 \\times 2}" ++
|
||||
"\\longrightarrow" ++
|
||||
"(\\mathbb{F}_2 {}^{2 \\times 2})^{2 \\times 2}" ++
|
||||
"\\\\[10pt]",
|
||||
-- Second row, B
|
||||
"B = " ++ texifyMatrix' show mBOrig ++
|
||||
"\\\\",
|
||||
-- Third row, B*
|
||||
"B^* = f^*(B) = " ++
|
||||
texifyMatrix' (\x -> "f(" ++ show x ++ ")") mBOrig ++ " = " ++
|
||||
texifyMatrix' (texifyMatrix' show) mBStar
|
||||
] ++
|
||||
"\\end{gather*}$$"
|
||||
```
|
||||
|
||||
We can do this because a matrix contains values in the domain of *f*, thus uniquely determining
|
||||
a way to change the internal structure (what Haskell calls
|
||||
@ -121,31 +258,27 @@ $$
|
||||
|
||||
It should be noted that the determinant strips off the *outer* matrix.
|
||||
We could also consider the map **det**\* , where we apply the determinant
|
||||
to the internal matrices (in Haskell terms, `fmap det`).
|
||||
to the internal matrices (in Haskell terms, `fmap determinant`).
|
||||
This map isn't as nice though, since:
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
\det {}^*(B^*)
|
||||
&= \left(\begin{matrix}
|
||||
\det \left(\begin{matrix} 0 & 0 \\ 0 & 0 \end{matrix} \right)
|
||||
& \det \left(\begin{matrix} 0 & 1 \\ 1 & 1 \end{matrix} \right)
|
||||
\\
|
||||
\det \left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
& \det \left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
\end{matrix} \right)
|
||||
= \left(\begin{matrix}
|
||||
0 & 1 \\
|
||||
1 & 1
|
||||
\end{matrix} \right)
|
||||
\\ \\
|
||||
&\neq \left(\begin{matrix}
|
||||
1 & 0 \\
|
||||
0 & 1
|
||||
\end{matrix} \right)
|
||||
= \det(B^*)
|
||||
\end{align*}
|
||||
$$
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
markdown $ "$$\\begin{align*}" ++ concat [
|
||||
-- First row, det* of B
|
||||
"\\det {}^*(B^*) &= " ++
|
||||
texifyMatrix' (("\\det" ++) . texifyMatrix' show) mBStar ++ " = " ++
|
||||
texifyMatrix ((`mod` 2) . determinant <$> mBStar) ++
|
||||
"\\\\ \\\\",
|
||||
-- Second row, determinant of B*
|
||||
-- Note how the commutation between `determinant` and <$> fails
|
||||
"&\\neq" ++
|
||||
texifyMatrix ((`mod` 2) <$> determinant mBStar) ++ " = " ++
|
||||
"\\det(B^*)",
|
||||
""
|
||||
] ++
|
||||
"\\end{align*}$$"
|
||||
```
|
||||
|
||||
|
||||
### Polynomial Map
|
||||
@ -155,6 +288,15 @@ For the purposes of demonstration, we'll work with $b = \lambda^2 + \alpha^2 \la
|
||||
the characteristic polynomial of *B*, since it has coefficients in the domain of *f*.
|
||||
We define the extended map $f^\bullet$ as:
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
-- Bulleted maps are also just instances of fmap, like the starred maps
|
||||
|
||||
fBullet :: (Eq a, Num a, Integral a) => Polynomial F4 -> Polynomial (Matrix a)
|
||||
fBullet = fmap (fmap (`mod` 2) . f)
|
||||
```
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
f^{\bullet} : \mathbb{F}_4[\lambda] \longrightarrow
|
||||
@ -184,38 +326,39 @@ We already looked at the determinant of this matrix, which is the constant term
|
||||
Therefore, it's probably not surprising that $f^\bullet$ and the characteristic polynomial commute
|
||||
in a similar fashion to the determinant.
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
bStar = fmap (fmap (`mod` 2)) $ charpoly $ fStar mBOrig
|
||||
bBullet = fmap (fmap (`mod` 2)) $ fBullet $ charpoly mBOrig
|
||||
|
||||
if bStar /= bBullet then
|
||||
markdown "$b^\\star$ and $b^\\bullet$ are not equal!"
|
||||
else
|
||||
markdown $ "$$\\begin{align*}" ++ concat [
|
||||
"b^* &= \\text{charpoly}(f^*(B)) = \\text{charpoly} " ++
|
||||
texifyMatrix' (texifyMatrix' show) mBStar ++
|
||||
"\\\\",
|
||||
"&= " ++
|
||||
texifyPoly' "\\Lambda" (texifyMatrix' show) bStar ++ " = " ++
|
||||
"f^\\bullet(\\text{charpoly}(B)) = b^\\bullet",
|
||||
""
|
||||
] ++
|
||||
"\\end{align*}$$"
|
||||
```
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
\begin{align*}
|
||||
b^*
|
||||
&= \text{charpoly}(f^*(B))
|
||||
= \text{charpoly}
|
||||
\left(\begin{matrix}
|
||||
\left(\begin{matrix} 0 & 0 \\ 0 & 0 \end{matrix} \right) &
|
||||
\left(\begin{matrix} 0 & 1 \\ 1 & 1 \end{matrix} \right) \\
|
||||
\left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right) &
|
||||
\left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
\end{matrix} \right)
|
||||
\\
|
||||
&= \Lambda^2 +
|
||||
\left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right) \Lambda +
|
||||
\left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
= f^{\bullet}(\text{charpoly}(B))
|
||||
= b^\bullet
|
||||
\end{align*}
|
||||
\\ \\
|
||||
\begin{CD}
|
||||
\mathbb{F}_4 {}^{2 \times 2}
|
||||
@>{\text{charpoly}}>>
|
||||
\mathbb{F}_4[\lambda]
|
||||
\\
|
||||
@V{f^*}VV ~ @VV{f^\bullet}V
|
||||
\\
|
||||
(\mathbb{F}_2 {}^{2 \times 2})^{2 \times 2}
|
||||
@>>{\text{charpoly}}>
|
||||
(\mathbb{F}_2 {}^{2 \times 2})[\Lambda]
|
||||
\end{CD}
|
||||
\end{gather*}
|
||||
\begin{CD}
|
||||
\mathbb{F}_4 {}^{2 \times 2}
|
||||
@>{\text{charpoly}}>>
|
||||
\mathbb{F}_4[\lambda]
|
||||
\\
|
||||
@V{f^*}VV ~ @VV{f^\bullet}V
|
||||
\\
|
||||
(\mathbb{F}_2 {}^{2 \times 2})^{2 \times 2}
|
||||
@>>{\text{charpoly}}>
|
||||
(\mathbb{F}_2 {}^{2 \times 2})[\Lambda]
|
||||
\end{CD}
|
||||
$$
|
||||
|
||||
It should also be mentioned that **charpoly**\*, taking the characteristic polynomials
|
||||
@ -228,29 +371,30 @@ There *does* happen to be an isomorphism between the two structures
|
||||
But even by converting to the proper type, we already have a counterexample in the constant term
|
||||
from taking **det**\* earlier.
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
\text{charpoly}^*(B^*)
|
||||
&= \left(\begin{matrix}
|
||||
\text{charpoly} \left(\begin{matrix} 0 & 0 \\ 0 & 0 \end{matrix} \right) &
|
||||
\text{charpoly} \left(\begin{matrix} 0 & 1 \\ 1 & 1 \end{matrix} \right) \\
|
||||
\text{charpoly} \left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right) &
|
||||
\text{charpoly} \left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
\end{matrix} \right)
|
||||
\\
|
||||
&= \left(\begin{matrix}
|
||||
\lambda^2 & \lambda^2 + \lambda + 1 \\
|
||||
\lambda^2 + \lambda + 1 & \lambda^2 + \lambda + 1
|
||||
\end{matrix} \right)
|
||||
\\
|
||||
&\cong
|
||||
\left(\begin{matrix} 1 & 1 \\ 1 & 1 \end{matrix} \right) \Lambda^2
|
||||
+ \left(\begin{matrix} 0 & 1 \\ 1 & 1 \end{matrix} \right) \Lambda
|
||||
+ \left(\begin{matrix} 0 & 1 \\ 1 & 1 \end{matrix} \right)
|
||||
\\ \\
|
||||
&\neq f^{\bullet}(\text{charpoly}(B))
|
||||
\end{align*}
|
||||
$$
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
markdown $ "$$\\begin{align*}" ++ concat [
|
||||
"\\text{charpoly}^*(B^*) &= " ++
|
||||
texifyMatrix' (("\\text{charpoly}" ++) . texifyMatrix' show) mBStar ++
|
||||
"\\\\",
|
||||
"&= " ++
|
||||
texifyMatrix' (texifyPoly' "\\lambda" show)
|
||||
(fmap (fmap (`mod` 2) . charpoly) mBStar) ++
|
||||
"\\\\",
|
||||
"&\\cong " ++
|
||||
-- Not constructing this by isomorphism yet
|
||||
texifyPoly' "\\Lambda" texifyMatrix
|
||||
(Poly [
|
||||
toMatrix [[0,1], [1,1]],
|
||||
toMatrix [[0,1], [1,1]],
|
||||
toMatrix [[1,1], [1,1]]
|
||||
]) ++
|
||||
"\\\\ \\\\",
|
||||
"&\\neq f^\\bullet(\\text{charpoly}(B))"
|
||||
] ++
|
||||
"\\end{align*}$$"
|
||||
```
|
||||
|
||||
|
||||
Forgetting
|
||||
@ -259,56 +403,45 @@ Forgetting
|
||||
Clearly, layering matrices has several advantages over how we usually interpret block matrices.
|
||||
But what happens if we *do* "forget" about the internal structure?
|
||||
|
||||
$$
|
||||
\begin{gather*}
|
||||
\text{forget} : (\mathbb{F}_2 {}^{2 \times 2})^{2 \times 2}
|
||||
\longrightarrow \mathbb{F}_2 {}^{4 \times 4}
|
||||
\\ \\
|
||||
\hat B = \text{forget}(B^*)
|
||||
= \text{forget}\left(\begin{matrix}
|
||||
\left(\begin{matrix} 0 & 0 \\ 0 & 0 \end{matrix} \right)
|
||||
& \left(\begin{matrix} 0 & 1 \\ 1 & 1 \end{matrix} \right)
|
||||
\\
|
||||
\left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
& \left(\begin{matrix} 1 & 1 \\ 1 & 0 \end{matrix} \right)
|
||||
\end{matrix} \right)
|
||||
= \left(\begin{matrix}
|
||||
0 & 0 & 0 & 1 \\
|
||||
0 & 0 & 1 & 1 \\
|
||||
1 & 1 & 1 & 1 \\
|
||||
1 & 0 & 1 & 0
|
||||
\end{matrix} \right)
|
||||
\end{gather*}
|
||||
$$
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| code-summary: "Haskell implementation of `forget`"
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell implementation of `forget`
|
||||
</summary>
|
||||
import Data.List (transpose)
|
||||
|
||||
<!-- TODO: run in jupyter -->
|
||||
```{.haskell}
|
||||
forget :: Matrix (Matrix a) -> Matrix a
|
||||
-- Massively complicated point-free way to forget matrices:
|
||||
-- Massively complicated point-free way to forget double matrices:
|
||||
-- 1. Convert internal matrices to lists of lists
|
||||
-- 2. Convert the external matrix to a list of lists
|
||||
-- 3. There are now four layers of lists. Transpose the second and third.
|
||||
-- 4. Concat the new third and fourth layers together
|
||||
-- 5. Concat the first and second layers together
|
||||
-- 6. Convert the list of lists back to a matrix
|
||||
forget = toMatrix . concat . fmap (fmap concat . transpose) .
|
||||
forget :: Matrix (Matrix a) -> Matrix a
|
||||
forget = toMatrix . concatMap (fmap concat . transpose) .
|
||||
fromMatrix . fmap fromMatrix
|
||||
```
|
||||
|
||||
To see why this is the structure, remember that we need to work with rows
|
||||
of the external matrix at the same time.
|
||||
We'd like to read across the whole row, but this involves descending into two matrices.
|
||||
The `fmap transpose` allows us to collect rows in the way we expect.
|
||||
For example, for the above matrix, We get `[[[0,0],[0,1]], [[0,0],[1,1]]]` after the transposition,
|
||||
which are the first two rows, grouped by the matrix they belonged to.
|
||||
Then, we can finally get the desired row by `fmap (fmap concat)`ing the rows together.
|
||||
Finally, we `concat` once more to undo the column grouping.
|
||||
</details>
|
||||
-- To see why this is the structure, remember that we need to work with rows
|
||||
-- of the external matrix at the same time.
|
||||
-- We'd like to read across the whole row, but this involves descending into two matrices.
|
||||
-- The `fmap transpose` allows us to collect rows in the way we expect.
|
||||
-- For example, for the above matrix, We get `[[[0,0],[0,1]], [[0,0],[1,1]]]` after the transposition,
|
||||
-- which are the first two rows, grouped by the matrix they belonged to.
|
||||
-- Then, we can finally get the desired row by `fmap (fmap concat)`ing the rows together.
|
||||
-- Finally, we `concat` once more to undo the column grouping.
|
||||
|
||||
mBHat = forget mBStar
|
||||
|
||||
markdown $ "$$\\begin{gather*}" ++ concat [
|
||||
"\\text{forget} : (\\mathbb{F}_2 {}^{2 \\times 2})^{2 \\times 2}" ++
|
||||
"\\longrightarrow \\mathbb{F}_2 {}^{4 \\times 4}" ++
|
||||
"\\\\[10pt]",
|
||||
"\\hat B = \\text{forget}(B^*) = \\text{forget}" ++
|
||||
texifyMatrix' (texifyMatrix' show) mBStar ++ " = " ++
|
||||
texifyMatrix mBHat,
|
||||
""
|
||||
] ++
|
||||
"\\end{gather*}$$"
|
||||
```
|
||||
|
||||
Like *f*, `forget` preserves addition and multiplication, a fact already appreciated by block matrices.
|
||||
Further, by *f*, the internal matrices multiply the same as elements of GF(4).
|
||||
@ -317,26 +450,28 @@ Hence, this shows us directly that GL(2, 4) is a subgroup of GL(4, 2).
|
||||
However, an obvious difference between layered and "forgotten" matrices is
|
||||
the determinant and characteristic polynomial:
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
\det {B^*} &= \left(\begin{matrix}1 & 0 \\ 0 & 1\end{matrix}\right)
|
||||
\\ \\
|
||||
\det {\hat B} &= 1
|
||||
\end{align*}
|
||||
\qquad
|
||||
\begin{align*}
|
||||
\text{charpoly}(B^*)
|
||||
&= \Lambda^2 +
|
||||
\left(\begin{matrix}1 & 1 \\ 1 & 0 \end{matrix}\right)\Lambda +
|
||||
\left(\begin{matrix}1 & 0 \\ 0 & 1\end{matrix}\right)
|
||||
\\ \\
|
||||
\text{charpoly}(\hat B)
|
||||
&= \lambda^4 + \lambda^3 + \lambda^2 + \lambda + 1\\
|
||||
\end{align*}
|
||||
$$
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
markdown $ "$$\\begin{align*}" ++ intercalate " \\\\ \\\\ " (
|
||||
map (intercalate " & ") [
|
||||
[
|
||||
"\\det B^* &= " ++
|
||||
texifyMatrix ((`mod` 2) <$> determinant mBStar),
|
||||
"\\text{charpoly} B^* &= " ++
|
||||
texifyPoly' "\\Lambda" texifyMatrix (fmap (`mod` 2) <$> charpoly mBStar)
|
||||
], [
|
||||
"\\det \\hat B &= " ++
|
||||
show ((`mod` 2) $ determinant mBHat),
|
||||
"\\text{charpoly} \\hat B &= " ++
|
||||
texifyPoly' "\\lambda" show ((`mod` 2) <$> charpoly mBHat)
|
||||
]
|
||||
]) ++
|
||||
"\\end{align*}$$"
|
||||
```
|
||||
|
||||
|
||||
### Another Path to the Forgotten
|
||||
### Another Forgotten Path
|
||||
|
||||
It's a relatively simple matter to move between determinants, since it's straightforward
|
||||
to identify 1 and the identity matrix.
|
||||
@ -346,7 +481,8 @@ However, a natural question to ask is whether there's a way to reconcile or coer
|
||||
<!-- TODO: reorganize parts of second post? -->
|
||||
First, let's formally establish a path from matrix polynomials to a matrix of polynomials.
|
||||
We need only use our friend from the [second post](../2) -- polynomial evaluation.
|
||||
Simply evaluating a matrix polynomial at *λI* converts our matrix indeterminate (*Λ*) into a scalar one (*λ*).
|
||||
Simply evaluating a matrix polynomial *r* at *λI* converts our matrix indeterminate (*Λ*)
|
||||
into a scalar one (*λ*).
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
@ -356,32 +492,51 @@ $$
|
||||
\\
|
||||
&:: \quad
|
||||
r(\Lambda) \mapsto r(\lambda I)
|
||||
\\ \\
|
||||
\text{eval}_{\Lambda \mapsto \lambda I}(\text{charpoly}(B^*))
|
||||
&= (\lambda I)^2
|
||||
+ \left(\begin{matrix}1 & 1 \\ 1 & 0 \end{matrix}\right)(\lambda I)
|
||||
+ \left(\begin{matrix}1 & 0 \\ 0 & 1\end{matrix}\right)
|
||||
\\
|
||||
&= \left(\begin{matrix}
|
||||
\lambda^2 + \lambda + 1 & \lambda \\
|
||||
\lambda & \lambda^2 + 1
|
||||
\end{matrix}\right)
|
||||
\end{align*}
|
||||
$$
|
||||
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
-- Function following from the evaluation definition above
|
||||
-- Note that `Poly . pure` is used to transform matrices of `a`
|
||||
-- into matrices of polynomials.
|
||||
|
||||
toMatrixPolynomial :: (Eq a, Num a) =>
|
||||
Polynomial (Matrix a) -> Matrix (Polynomial a)
|
||||
toMatrixPolynomial xs = evalPoly eyeLambda $ fmap (fmap (Poly . pure)) xs where
|
||||
-- First dimensions of the coefficients
|
||||
(is, _) = unzip $ map (snd . bounds . unMat) $ coeffs xs
|
||||
-- Properly-sized identity matrix times a scalar lambda
|
||||
eyeLambda = eye (1 + maximum is) * toMatrix [[Poly [0, 1]]]
|
||||
|
||||
|
||||
markdown $ "$$\\begin{align*}" ++
|
||||
"\\text{eval}_{\\Lambda \\mapsto \\lambda I}(\\text{charpoly}(B^*)) &=" ++
|
||||
texifyPoly' "(\\lambda I)" texifyMatrix
|
||||
(fmap (`mod` 2) <$> charpoly mBStar) ++
|
||||
"\\\\ &= " ++
|
||||
texifyMatrix' (texifyPoly' "\\lambda" show)
|
||||
(toMatrixPolynomial $ fmap (`mod` 2) <$> charpoly mBStar) ++
|
||||
"\\end{align*}$$"
|
||||
```
|
||||
|
||||
Since a matrix containing polynomials is still a matrix, we can then take its determinant.
|
||||
What pops out is exactly what we were after...
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
\det(\text{eval}_{\Lambda \mapsto \lambda I}(\text{charpoly}(B^*)))
|
||||
&= (\lambda^2 + \lambda + 1)(\lambda^2 + 1) - \lambda^2
|
||||
\\
|
||||
&= \lambda^4 + \lambda^3 + \lambda^2 + \lambda + 1
|
||||
\\
|
||||
&= \text{charpoly}(\hat B)
|
||||
\end{align*}
|
||||
$$
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
markdown $ "$$\\begin{align*}" ++
|
||||
"\\det(\\text{eval}_{\\Lambda \\mapsto \\lambda I}(" ++
|
||||
"\\text{charpoly}(B^*))) &=" ++
|
||||
"(1 + \\lambda + \\lambda^2)(1 + \\lambda^2) - \\lambda^2" ++
|
||||
"\\\\ &=" ++
|
||||
texifyPoly' "\\lambda" show
|
||||
(fmap (`mod` 2) <$> determinant $ toMatrixPolynomial $ charpoly mBStar) ++
|
||||
"\\\\ &= \\text{charpoly}{\\hat B}" ++
|
||||
"\\end{align*}$$"
|
||||
```
|
||||
|
||||
...and we can arrange our maps into another diagram:
|
||||
|
||||
@ -409,66 +564,24 @@ $$
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Haskell demonstration of this commutation
|
||||
</summary>
|
||||
Fortunately, the implementation of `charpoly` using Laplace expansion already works with numeric matrices.
|
||||
Therefore, we need only define the special eval:
|
||||
|
||||
```{.haskell}
|
||||
toMatrixPolynomial :: Num a => Polynomial (Matrix a) -> Matrix (Polynomial a)
|
||||
-- Collect our coefficient matrices into a single matrix of polynomials
|
||||
toMatrixPolynomial (Poly ps) = Mat $ array rs values where
|
||||
-- Technically, we're always working with square matrices, but we should
|
||||
-- always use the largest bounds available.
|
||||
(is,js) = unzip $ map mDims ps
|
||||
rs = ((0,0),(maximum is - 1,maximum js - 1))
|
||||
-- Address a matrix. This needs defaulting to zero to be fully correct
|
||||
-- with respect to the range given by `rs`
|
||||
access b (Mat m) = m!b
|
||||
-- Build the value at an address by addressing over the coefficients
|
||||
-- ps is already in rising coefficient order, so our values are too.
|
||||
values = map (\r -> (r, Poly $ map (access r) $ ps)) (range rs)
|
||||
```
|
||||
|
||||
Now we can simply observe:
|
||||
|
||||
<!-- TODO: run in jupyter -->
|
||||
```{.haskell}
|
||||
field4 = [zero 2, eye 2, toMatrix [[0,1],[1,1]], toMatrix [[1,1],[1,0]]]
|
||||
|
||||
mB = toMatrix $ [[field4!!0, field4!!2], [field4!!3, field4!!3]]
|
||||
|
||||
-- >>> mapM_ print $ fromMatrix $ forget mB
|
||||
-- -- [0,0,0,1]
|
||||
-- -- [0,0,1,1]
|
||||
-- -- [1,1,1,1]
|
||||
-- -- [1,0,1,0]
|
||||
|
||||
-- >>> fmap (`mod` 2) $ charpoly $ forget mB
|
||||
-- -- 1x^4 + 1x^3 + 1x^2 + 1x + 1
|
||||
-- >>> fmap (`mod` 2) $ determinant $ toMatrixPolynomial $ charpoly mB
|
||||
-- -- 1x^4 + 1x^3 + 1x^2 + 1x + 1
|
||||
```
|
||||
</details>
|
||||
|
||||
It should be noted that we do *not* get the same results by taking the determinant after
|
||||
applying **charpoly**\*, indicating that the above method is "correct".
|
||||
|
||||
$$
|
||||
\begin{align*}
|
||||
\text{charpoly}^*(B^*) &= \left(\begin{matrix}
|
||||
\lambda^2 & \lambda^2 + \lambda + 1 \\
|
||||
\lambda^2 + \lambda + 1 & \lambda^2 + \lambda + 1
|
||||
\end{matrix}\right)
|
||||
\\ ~ \\
|
||||
\det( \text{charpoly}^*(B^*))
|
||||
&= \lambda^2(\lambda^2 + \lambda + 1) - (\lambda^2 + \lambda + 1)^2
|
||||
\\
|
||||
&= \lambda^3 + 1 \mod 2
|
||||
\end{align*}
|
||||
$$
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
|
||||
markdown $ "$$\\begin{align*}" ++
|
||||
"\\text{charpoly}^*(B^*) &=" ++
|
||||
texifyMatrix' (texifyPoly' "\\lambda" show)
|
||||
(fmap (`mod` 2) <$> fmap charpoly mBStar) ++
|
||||
"\\\\ \\\\" ++
|
||||
"\\det(\\text{charpoly}^*(B^*)) &=" ++
|
||||
"\\lambda^2(1 + \\lambda + \\lambda^2) - (1 + \\lambda + \\lambda^2)^2" ++
|
||||
"\\\\ &= " ++
|
||||
texifyPoly' "\\lambda" show
|
||||
(fmap (`mod` 2) <$> determinant $ fmap charpoly mBStar) ++
|
||||
"\\end{align*}$$"
|
||||
```
|
||||
|
||||
|
||||
### Cycles and Cycles
|
||||
@ -484,7 +597,6 @@ However, the reason we see it is more obvious if we look at the powers of scalar
|
||||
First, recall that *f*\* takes us from a matrix over GF(4) to a matrix of matrices of GF(2).
|
||||
Then define a map *g* that gives us degree 4 polynomials:
|
||||
|
||||
::: {layout="[[1],[1,1,1]]"}
|
||||
$$
|
||||
\begin{gather*}
|
||||
g : \mathbb{F}_4^{2 \times 2} \rightarrow \mathbb{F}_2[\lambda]
|
||||
@ -493,78 +605,29 @@ $$
|
||||
\end{gather*}
|
||||
$$
|
||||
|
||||
$$
|
||||
\begin{array}{}
|
||||
& \scriptsize \left(\begin{matrix}
|
||||
0 & \alpha \\
|
||||
\alpha^2 & \alpha^2
|
||||
\end{matrix}\right)
|
||||
\\
|
||||
B & \overset{g}{\mapsto}
|
||||
& 11111_\lambda
|
||||
\\
|
||||
B^2 & \overset{g}{\mapsto}
|
||||
& 11111_\lambda
|
||||
\\
|
||||
B^3 & \overset{g}{\mapsto}
|
||||
& 11111_\lambda
|
||||
\\
|
||||
B^4 & \overset{g}{\mapsto}
|
||||
& 11111_\lambda
|
||||
\\
|
||||
B^5 & \overset{g}{\mapsto}
|
||||
& 10001_\lambda
|
||||
\end{array}
|
||||
$$
|
||||
```{haskell}
|
||||
--| code-fold: true
|
||||
--| layout-ncol: 3
|
||||
|
||||
$$
|
||||
\begin{array}{}
|
||||
& \scriptsize \left(\begin{matrix}
|
||||
0 & \alpha^2 \\
|
||||
1 & 1
|
||||
\end{matrix}\right)
|
||||
\\
|
||||
\alpha B & \overset{g}{\mapsto}
|
||||
& 10011_\lambda
|
||||
\\
|
||||
(\alpha B)^2 & \overset{g}{\mapsto}
|
||||
& 10011_\lambda
|
||||
\\
|
||||
(\alpha B)^3 & \overset{g}{\mapsto}
|
||||
& 11111_\lambda
|
||||
\\
|
||||
(\alpha B)^4 & \overset{g}{\mapsto}
|
||||
& 10011_\lambda
|
||||
\\
|
||||
(\alpha B)^5 & \overset{g}{\mapsto}
|
||||
& 10101_\lambda
|
||||
\end{array}
|
||||
$$
|
||||
g = fmap (`mod` 2) . charpoly . forget . fStar
|
||||
|
||||
$$
|
||||
\begin{array}{}
|
||||
& \scriptsize \left(\begin{matrix}
|
||||
0 & 1 \\
|
||||
\alpha & \alpha
|
||||
\end{matrix}\right)
|
||||
\\
|
||||
\alpha^2 B & \overset{g}{\mapsto}
|
||||
& 11001_\lambda
|
||||
\\
|
||||
(\alpha^2 B)^2 & \overset{g}{\mapsto}
|
||||
& 11001_\lambda
|
||||
\\
|
||||
(\alpha^2 B)^3 & \overset{g}{\mapsto}
|
||||
& 11111_\lambda
|
||||
\\
|
||||
(\alpha^2 B)^4 & \overset{g}{\mapsto}
|
||||
& 11001_\lambda
|
||||
\\
|
||||
(\alpha^2 B)^5 & \overset{g}{\mapsto}
|
||||
& 10101_\lambda
|
||||
\end{array}
|
||||
$$
|
||||
:::
|
||||
showSeries varName var = "$$\\begin{array}{}" ++
|
||||
" & \\scriptsize " ++
|
||||
texifyMatrix var ++
|
||||
"\\\\" ++
|
||||
intercalate " \\\\ " [
|
||||
(if n == 1 then varName' else varName' ++ "^{" ++ show n ++ "}") ++
|
||||
"& \\overset{g}{\\mapsto} &" ++
|
||||
texPolyAsPositional' "\\lambda" (g $ var^n)
|
||||
| n <- [1..5]
|
||||
] ++
|
||||
"\\end{array}$$" where
|
||||
varName' = if length varName == 1 then varName else "(" ++ varName ++ ")"
|
||||
|
||||
markdown $ showSeries "B" mBOrig
|
||||
markdown $ showSeries "αB" (fmap (AlphaF4*) mBOrig)
|
||||
markdown $ showSeries "α^2 B" (fmap (Alpha2F4*) mBOrig)
|
||||
```
|
||||
|
||||
The matrices in the middle and rightmost columns both have order 15 inside GL(2, 4).
|
||||
Correspondingly, both 10011~λ~ = ~2~19 and 11001~λ~ = ~2~25 are primitive,
|
||||
@ -586,33 +649,23 @@ Haskell demonstration of the field-like-ness of these matrices
|
||||
|
||||
All we really need to do is test additive closure, since the powers trivially commute and include the identity matrix.
|
||||
|
||||
<!-- TODO: run in jupyter -->
|
||||
```{.haskell}
|
||||
hasAdditiveClosure :: Integral a => Int -> a -> [Matrix a] -> bool
|
||||
```{haskell}
|
||||
-- Check whether n x n matrices (mod p) have additive closure
|
||||
-- Supplement the identity, even if it is not already present
|
||||
hasAdditiveClosure :: Integral a => Int -> a -> [Matrix a] -> Bool
|
||||
hasAdditiveClosure n p xs = all (`elem` xs') sums where
|
||||
-- Add in the zero matrix
|
||||
xs' = zero n:xs
|
||||
-- Calculate all possible sums of pairs (mod p)
|
||||
sums = map (fmap (`mod` p)) $ (+) <$> xs' <*> xs'
|
||||
|
||||
|
||||
generatesField :: Integral a => Int -> a -> Matrix a -> bool
|
||||
-- Generate the powers of x, then test if they form a field (mod p)
|
||||
generatesField :: Integral a => Int -> a -> Matrix a -> Bool
|
||||
generatesField n p x = hasAdditiveClosure n p xs where
|
||||
xs = map (fmap (`mod` p) . (x^)) [1..p^n-1]
|
||||
|
||||
alphaB = toMatrix [[zero 2, field4!!3],[eye 2, eye 2]]
|
||||
|
||||
-- >>> mapM_ $ print $ fromMatrix $ forget alphaB
|
||||
-- -- [0,0,1,1]
|
||||
-- -- [0,0,1,0]
|
||||
-- -- [1,0,1,0]
|
||||
-- -- [0,1,0,1]
|
||||
--
|
||||
-- >>> generatesField 4 2 $ forget $ alphaB
|
||||
-- -- True
|
||||
print $ generatesField 4 2 $ forget $ fStar $ fmap (AlphaF4*) mBOrig
|
||||
```
|
||||
</details>
|
||||
|
||||
@ -693,8 +746,8 @@ This concern about prime dimensions is unique to characteristic 2.
|
||||
For any other prime *p*, *p*^*m*^ - 1 is composite since it is at the very least even.
|
||||
All other remarks about the above diagram should still hold for any other prime *p*.
|
||||
|
||||
In addition, our earlier diagram where we correspond the order of an element in GL(2, 2^2^)
|
||||
with the order of an element in GF(2^2×2^) via the characteristic polynomial also generalizes.
|
||||
In addition, the diagram where we found a correspondence between the orders of elements in
|
||||
GL(2, 2^2^) and GF(2^2×2^) via the characteristic polynomial also generalizes.
|
||||
Though I have not proven it, I strongly suspect the following diagram commutes,
|
||||
at least in the case where *K* is a finite field:
|
||||
|
||||
@ -727,12 +780,12 @@ If the above diagram is true, then the prior statement follows.
|
||||
The action of forgetting the internal structure may sound somewhat familiar if you know your Haskell.
|
||||
Remember that for lists, we can do something similar
|
||||
-- converting `[[1,2,3],[4,5,6]]` to `[1,2,3,4,5,6]` is just a matter of applying `concat`.
|
||||
But this is an instance in which we know lists to behave like a [monad](https://wiki.haskell.org/Monad).
|
||||
This is an instance in which we know lists to behave like a [monad](https://wiki.haskell.org/Monad).
|
||||
Despite being an indecipherable bit of jargon to newcomers, it just means we:
|
||||
|
||||
1. can apply functions inside the structure (for example, to the elements of a list),
|
||||
2. have a sensible injection into the structure (creating singleton lists, called `return`), and
|
||||
3. can reduce two layers to one (concat, or join for monads in general).
|
||||
3. can reduce two layers to one (`concat`, or `join` for monads in general).
|
||||
- Monads are traditionally defined using the operator `>>=`, but `join = (>>= id)`
|
||||
|
||||
Just comparing the types of `join :: Monad m => m (m a) -> m a`
|
||||
@ -742,9 +795,9 @@ Of course, **this is only true when our internal matrices are all the same size*
|
||||
In the above diagrams, this restriction has applied, but should be stated explicitly
|
||||
since no dimension is specified by `Matrix a`.
|
||||
|
||||
However, we run into difficulty at condition 2.
|
||||
For one, only "numbers" (elements of a ring) can go inside matrices.
|
||||
This restricts where monadicity can hold.
|
||||
Condition 2 gives us some trouble, though.
|
||||
For one, only "numbers" (elements of a ring) can go inside matrices, which restricts
|
||||
where monadicity can hold.
|
||||
More importantly, we have a *lot* of freedom in what dimension we choose to inject into.
|
||||
For example, we might pick a `return` that uses 1×1 matrices (which add no additional structure).
|
||||
We might also pick `return2`, which scalar-multiplies its argument to a 2×2 identity matrix instead.
|
||||
@ -799,7 +852,7 @@ $$
|
||||
& \begin{matrix} | \\ \downarrow \end{matrix}
|
||||
& \searrow & & \swarrow
|
||||
& \begin{matrix} | \\ \downarrow \end{matrix}
|
||||
& f_2^*
|
||||
& \small f_2^*
|
||||
\\ \\
|
||||
& (K^{r \times r})^{n \times n}
|
||||
& \underset{\text{forget}} {\longrightarrow}
|
||||
@ -809,7 +862,7 @@ $$
|
||||
\end{matrix}
|
||||
$$
|
||||
|
||||
Or in English, whether "rebracketing" certain *nr*×*nr* matrices can be traced back to
|
||||
Or in English, whether "rebracketing" certain *nr* × *nr* matrices can be traced back to
|
||||
not only a degree *r* field extension, but also one of degree *n*.
|
||||
|
||||
The mathematician in me tells me to believe in well-defined structures.
|
||||
@ -819,7 +872,7 @@ However, the computer scientist in me laments that the application of these stru
|
||||
There is clear utility and interest in doing so, otherwise the diagrams shown above would not exist.
|
||||
|
||||
Of course, there's plenty of reason *not* to go down this route.
|
||||
For one, it's plainly inefficient -- GPUs are *built* on matrix operations being as efficient as possible,
|
||||
i.e., without the layering.
|
||||
For one, it's plainly inefficient -- GPUs are *built* on matrix operations being
|
||||
as efficient as possible, i.e., without the layering.
|
||||
It's also inefficient to learn for people *just* learning matrices.
|
||||
I'd still argue that the method is efficient for learning about more complex topics, like field extensions.
|
||||
I'd still argue that the method is useful for learning about more complex topics, like field extensions.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user