12 lines
35 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"hash": "6d14e6ba9847880e6db4924484d7e689",
"result": {
"engine": "jupyter",
"markdown": "---\ntitle: \"Exploring Finite Fields, Part 4: The Power of Forgetting\"\ndescription: |\n Or: how I stopped learned to worrying and appreciate the Monad.\nformat:\n html:\n html-math-method: katex\ndate: \"2024-02-20\"\ndate-modified: \"2025-08-05\"\ncategories:\n - algebra\n - finite field\n - haskell\n---\n\n\n\nThe [last post](../3) in this series focused on understanding some small linear groups\n and implementing them on the computer over both a prime field and prime power field.\n\nThe prime power case was particularly interesting.\nFirst, we adjoined the roots of a polynomial to the base field, GF(2).\nRather than the traditional means of adding new symbols like *α*, we used companion matrices,\n which behave the same arithmetically.\nFor example, for the smallest prime power field, GF(4), we use the polynomial $p(x) = x^2 + x + 1$,\n and map its symbolic roots (*α* and *α*^2^), to matrices over GF(2):\n\n$$\n\\begin{gather*}\n f : \\mathbb{F}_4 \\longrightarrow \\mathbb{F}_2 {}^{2 \\times 2}\n \\\\ \\\\\n \\begin{gather*}\n f(0) = {\\bf 0} =\n \\left(\\begin{matrix} 0 & 0 \\\\ 0 & 0 \\end{matrix}\\right)\n & f(1) = I\n = \\left(\\begin{matrix} 1 & 0 \\\\ 0 & 1 \\end{matrix}\\right)\n \\\\\n f(\\alpha) = C_p\n = \\left(\\begin{matrix} 0 & 1 \\\\ 1 & 1 \\end{matrix}\\right)\n & f(\\alpha^2) = C_p {}^2\n = \\left(\\begin{matrix} 1 & 1 \\\\ 1 & 0 \\end{matrix}\\right)\n \\end{gather*}\n \\\\ \\\\\n f(a + b)= f(a) + f(b), \\quad f(ab) = f(a)f(b)\n\\end{gather*}\n$$\n\n::: {#1249570e .cell execution_count=3}\n``` {.haskell .cell-code code-fold=\"true\" code-summary=\"Equivalent Haskell\"}\ndata F4 = ZeroF4 | OneF4 | AlphaF4 | Alpha2F4 deriving Eq\nfield4 = [ZeroF4, OneF4, AlphaF4, Alpha2F4]\n\ninstance Show F4 where\n show ZeroF4 = \"0\"\n show OneF4 = \"1\"\n show AlphaF4 = \"α\"\n show Alpha2F4 = \"α^2\"\n\n-- Addition and multiplication over F4\ninstance Num F4 where\n (+) ZeroF4 x = x\n (+) OneF4 AlphaF4 = Alpha2F4\n (+) OneF4 Alpha2F4 = AlphaF4\n (+) AlphaF4 Alpha2F4 = OneF4\n (+) x y = if x == y then ZeroF4 else y + x\n\n (*) ZeroF4 x = ZeroF4\n (*) x ZeroF4 = ZeroF4\n (*) OneF4 x = x\n (*) AlphaF4 AlphaF4 = Alpha2F4\n (*) Alpha2F4 Alpha2F4 = AlphaF4\n (*) AlphaF4 Alpha2F4 = OneF4\n (*) x y = y * x\n\n abs = id\n negate = id\n signum = id\n fromInteger = (cycle field4 !!) . fromInteger\n\n\n-- Companion matrix of `p`, an irreducible polynomial of degree 2 over GF(2)\ncP :: (Num a, Eq a, Integral a) => Matrix a\ncP = companion $ Poly [1, 1, 1]\n\nf ZeroF4 = zero 2\nf OneF4 = eye 2\nf AlphaF4 = cP\nf Alpha2F4 = (`mod` 2) <$> cP |*| cP\n\nfield4M = map f field4\n```\n:::\n\n\nFinally, we constructed GL(2, 4) using matrices of matrices\n -- not [block matrices](https://en.wikipedia.org/wiki/Block_matrix)!\nThis post will focus on studying this method in slightly more detail.\n\n\nReframing the Path Until Now\n----------------------------\n\nIn the above description, we already mentioned larger structures over GF(2),\n namely polynomials and matrices.\nSince GF(4) can itself be described with matrices over GF(2),\n we can generalize *f* to give us two more maps:\n\n- $f^*$, which converts matrices over GF(4) to double-layered matrices over GF(2), and\n- $f^\\bullet$, which converts polynomials over GF(4) to polynomials of matrices over GF(2)\n\n\n### Matrix Map\n\nWe examined the former map briefly in the previous post.\nMore explicitly, we looked at a matrix *B* in SL(2, 4) which had the property\n that it was cyclic of order five.\nThen, to work with it without relying on symbols, we simply applied *f* over the contents of the matrix.\n\n::: {#0554e224 .cell execution_count=4}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Starred maps are instances of fmap composed with modding out\n-- by the characteristic\n\nfStar :: (Eq a, Num a, Integral a) => Matrix F4 -> Matrix (Matrix a)\nfStar = fmap (fmap (`mod` 2) . f)\n\nmBOrig = toMatrix [[ZeroF4, AlphaF4], [Alpha2F4, Alpha2F4]]\nmBStar = fStar mBOrig\n\nmarkdown $ \"$$\\\\begin{gather*}\" ++ concat [\n -- First row, type of fStar\n \"f^* : \\\\mathbb{F}_4 {}^{2 \\\\times 2}\" ++\n \"\\\\longrightarrow\" ++\n \"(\\\\mathbb{F}_2 {}^{2 \\\\times 2})^{2 \\\\times 2}\" ++\n \"\\\\\\\\[10pt]\",\n -- Second row, B\n \"B = \" ++ texifyMatrix' show mBOrig ++\n \"\\\\\\\\\",\n -- Third row, B*\n \"B^* = f^*(B) = \" ++\n texifyMatrix' (\\x -> \"f(\" ++ show x ++ \")\") mBOrig ++ \" = \" ++\n texifyMatrix' (texifyMatrix' show) mBStar\n ] ++\n \"\\\\end{gather*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\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 & α \\\\ α^2 & α^2\\end{matrix} \\right)\\\\B^* = f^*(B) = \\left( \\begin{matrix}f(0) & f(α) \\\\ f(α^2) & f(α^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*}$$\n:::\n:::\n\n\nWe can do this because a matrix contains values in the domain of *f*, thus uniquely determining\n a way to change the internal structure (what Haskell calls\n a [functor](https://wiki.haskell.org/Functor)).\nFurthermore, due to the properties of *f*, it and *f*\\* commute with the determinant,\n as shown by the following diagram:\n\n$$\n\\begin{gather*}\n f(\\det(B)) = f(1) = I =\\det(B^*)= \\det(f^*(B))\n \\\\[10pt]\n \\begin{CD}\n \\mathbb{F}_4 {}^{2 \\times 2}\n @>{\\det}>>\n \\mathbb{F}_4\n \\\\\n @V{f^*}VV ~ @VV{f}V\n \\\\\n (\\mathbb{F}_2 {}^{2 \\times 2})^{2 \\times 2}\n @>>{\\det}>\n \\mathbb{F}_2 {}^{2 \\times 2}\n \\end{CD}\n\\end{gather*}\n$$\n\nIt should be noted that the determinant strips off the *outer* matrix.\nWe could also consider the map **det**\\* , where we apply the determinant\n to the internal matrices (in Haskell terms, `fmap determinant`).\nThis map isn't as nice though, since:\n\n::: {#f2977c19 .cell execution_count=5}\n``` {.haskell .cell-code code-fold=\"true\"}\nmarkdown $ \"$$\\\\begin{align*}\" ++ concat [\n -- First row, det* of B\n \"\\\\det {}^*(B^*) &= \" ++\n texifyMatrix' ((\"\\\\det\" ++) . texifyMatrix' show) mBStar ++ \" = \" ++\n texifyMatrix ((`mod` 2) . determinant <$> mBStar) ++\n \"\\\\\\\\ \\\\\\\\\",\n -- Second row, determinant of B*\n -- Note how the commutation between `determinant` and <$> fails\n \"&\\\\neq\" ++\n texifyMatrix ((`mod` 2) <$> determinant mBStar) ++ \" = \" ++\n \"\\\\det(B^*)\",\n \"\"\n ] ++\n \"\\\\end{align*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\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*}$$\n:::\n:::\n\n\n### Polynomial Map\n\nMuch like how we can change the internal structure of matrices, we can do the same for polynomials.\nFor the purposes of demonstration, we'll work with $b = \\lambda^2 + \\alpha^2 \\lambda + 1$,\n the characteristic polynomial of *B*, since it has coefficients in the domain of *f*.\nWe define the extended map $f^\\bullet$ as:\n\n::: {#58a36854 .cell execution_count=6}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Bulleted maps are also just instances of fmap, like the starred maps\n\nfBullet :: (Eq a, Num a, Integral a) => Polynomial F4 -> Polynomial (Matrix a)\nfBullet = fmap (fmap (`mod` 2) . f)\n```\n:::\n\n\n$$\n\\begin{gather*}\n f^{\\bullet} : \\mathbb{F}_4[\\lambda] \\longrightarrow\n \\mathbb{F}_2 {}^{2 \\times 2}[\\Lambda]\n \\\\\n f^{\\bullet} (\\lambda) = \\Lambda \\qquad\n f^{\\bullet}(a) = f(a), \\quad a \\in \\mathbb{F}_4\n \\\\ \\\\\n \\begin{align*}\n b^{\\bullet}\n = f^{\\bullet}(b)\n &= f^{\\bullet}(\\lambda^2)\n &&+&& f^{\\bullet}(\\alpha^2)f^{\\bullet}(\\lambda)\n &&+&& f^{\\bullet}(1)\n \\\\\n &= \\Lambda^2\n &&+&& \\left(\\begin{matrix} 1 & 1 \\\\ 1 & 0\\end{matrix}\\right) \\Lambda\n &&+&& \\left(\\begin{matrix} 1 & 0 \\\\ 0 & 1 \\end{matrix}\\right)\n \\end{align*}\n\\end{gather*}\n$$\n\nSince we're looking at the characteristic polynomial of *B*, we might as well also look\n at the characteristic polynomial of *B*\\*, its image under $f^*$.\nWe already looked at the determinant of this matrix, which is the constant term\n of the characteristic polynomial (up to sign).\nTherefore, it's probably not surprising that $f^\\bullet$ and the characteristic polynomial commute\n in a similar fashion to the determinant.\n\n::: {#9126ada7 .cell execution_count=7}\n``` {.haskell .cell-code code-fold=\"true\"}\nbStar = fmap (fmap (`mod` 2)) $ charpoly $ fStar mBOrig\nbBullet = fmap (fmap (`mod` 2)) $ fBullet $ charpoly mBOrig\n\nif bStar /= bBullet then\n markdown \"$b^\\\\star$ and $b^\\\\bullet$ are not equal!\"\n else\n markdown $ \"$$\\\\begin{align*}\" ++ concat [\n \"b^* &= \\\\text{charpoly}(f^*(B)) = \\\\text{charpoly} \" ++\n texifyMatrix' (texifyMatrix' show) mBStar ++\n \"\\\\\\\\\",\n \"&= \" ++\n texifyPoly' \"\\\\Lambda\" (texifyMatrix' show) bStar ++ \" = \" ++\n \"f^\\\\bullet(\\\\text{charpoly}(B)) = b^\\\\bullet\",\n \"\"\n ] ++\n \"\\\\end{align*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\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)\\\\&= \\left( \\begin{matrix}1 & 0 \\\\ 0 & 1\\end{matrix} \\right) + \\left( \\begin{matrix}1 & 1 \\\\ 1 & 0\\end{matrix} \\right)\\Lambda + \\Lambda^{2} = f^\\bullet(\\text{charpoly}(B)) = b^\\bullet\\end{align*}$$\n:::\n:::\n\n\n$$\n\\begin{CD}\n \\mathbb{F}_4 {}^{2 \\times 2}\n @>{\\text{charpoly}}>>\n \\mathbb{F}_4[\\lambda]\n \\\\\n @V{f^*}VV ~ @VV{f^\\bullet}V\n \\\\\n (\\mathbb{F}_2 {}^{2 \\times 2})^{2 \\times 2}\n @>>{\\text{charpoly}}>\n (\\mathbb{F}_2 {}^{2 \\times 2})[\\Lambda]\n\\end{CD}\n$$\n\nIt should also be mentioned that **charpoly**\\*, taking the characteristic polynomials\n of the internal matrices, does *not* obey the same relationship.\nFor one, the type is wrong: the codomain is a matrix *containing* polynomials,\n rather than a polynomial over matrices.\n\nThere *does* happen to be an isomorphism between the two structures\n (a direction of which we'll discuss momentarily).\nBut even by converting to the proper type, we already have a counterexample in the constant term\n from taking **det**\\* earlier.\n\n::: {#80572089 .cell execution_count=8}\n``` {.haskell .cell-code code-fold=\"true\"}\nmarkdown $ \"$$\\\\begin{align*}\" ++ concat [\n \"\\\\text{charpoly}^*(B^*) &= \" ++\n texifyMatrix' ((\"\\\\text{charpoly}\" ++) . texifyMatrix' show) mBStar ++\n \"\\\\\\\\\",\n \"&= \" ++\n texifyMatrix' (texifyPoly' \"\\\\lambda\" show)\n (fmap (fmap (`mod` 2) . charpoly) mBStar) ++\n \"\\\\\\\\\",\n \"&\\\\cong \" ++\n -- Not constructing this by isomorphism yet\n texifyPoly' \"\\\\Lambda\" texifyMatrix\n (Poly [\n toMatrix [[0,1], [1,1]],\n toMatrix [[0,1], [1,1]],\n toMatrix [[1,1], [1,1]]\n ]) ++\n \"\\\\\\\\ \\\\\\\\\",\n \"&\\\\neq f^\\\\bullet(\\\\text{charpoly}(B))\"\n ] ++\n \"\\\\end{align*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\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} & 1 + \\lambda + \\lambda^{2} \\\\ 1 + \\lambda + \\lambda^{2} & 1 + \\lambda + \\lambda^{2}\\end{matrix} \\right)\\\\&\\cong \\left( \\begin{matrix}0 & 1 \\\\ 1 & 1\\end{matrix} \\right) + \\left( \\begin{matrix}0 & 1 \\\\ 1 & 1\\end{matrix} \\right)\\Lambda + \\left( \\begin{matrix}1 & 1 \\\\ 1 & 1\\end{matrix} \\right)\\Lambda^{2}\\\\ \\\\&\\neq f^\\bullet(\\text{charpoly}(B))\\end{align*}$$\n:::\n:::\n\n\nForgetting\n----------\n\nClearly, layering matrices has several advantages over how we usually interpret block matrices.\nBut what happens if we *do* \"forget\" about the internal structure?\n\n::: {#fd9b9f12 .cell execution_count=9}\n``` {.haskell .cell-code code-fold=\"true\" code-summary=\"Haskell implementation of `forget`\"}\nimport Data.List (transpose)\n\n-- Massively complicated point-free way to forget double matrices:\n-- 1. Convert internal matrices to lists of lists\n-- 2. Convert the external matrix to a list of lists\n-- 3. There are now four layers of lists. Transpose the second and third.\n-- 4. Concat the new third and fourth layers together\n-- 5. Concat the first and second layers together\n-- 6. Convert the list of lists back to a matrix\nforget :: Matrix (Matrix a) -> Matrix a\nforget = toMatrix . concatMap (fmap concat . transpose) .\n fromMatrix . fmap fromMatrix\n\n-- To see why this is the structure, remember that we need to work with rows\n-- of the external matrix at the same time.\n-- We'd like to read across the whole row, but this involves descending into two matrices.\n-- The `fmap transpose` allows us to collect rows in the way we expect.\n-- For example, for the above matrix, We get `[[[0,0],[0,1]], [[0,0],[1,1]]]` after the transposition,\n-- which are the first two rows, grouped by the matrix they belonged to.\n-- Then, we can finally get the desired row by `fmap (fmap concat)`ing the rows together.\n-- Finally, we `concat` once more to undo the column grouping.\n\nmBHat = forget mBStar\n\nmarkdown $ \"$$\\\\begin{gather*}\" ++ concat [\n \"\\\\text{forget} : (\\\\mathbb{F}_2 {}^{2 \\\\times 2})^{2 \\\\times 2}\" ++\n \"\\\\longrightarrow \\\\mathbb{F}_2 {}^{4 \\\\times 4}\" ++\n \"\\\\\\\\[10pt]\",\n \"\\\\hat B = \\\\text{forget}(B^*) = \\\\text{forget}\" ++\n texifyMatrix' (texifyMatrix' show) mBStar ++ \" = \" ++\n texifyMatrix mBHat,\n \"\"\n ] ++\n \"\\\\end{gather*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{gather*}\\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}\\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*}$$\n:::\n:::\n\n\nLike *f*, `forget` preserves addition and multiplication, a fact already appreciated by block matrices.\nFurther, by *f*, the internal matrices multiply the same as elements of GF(4).\nHence, this shows us directly that GL(2, 4) is a subgroup of GL(4, 2).\n\nHowever, an obvious difference between layered and \"forgotten\" matrices is\n the determinant and characteristic polynomial:\n\n::: {#623d4a04 .cell execution_count=10}\n``` {.haskell .cell-code code-fold=\"true\"}\nmarkdown $ \"$$\\\\begin{align*}\" ++ intercalate \" \\\\\\\\ \\\\\\\\ \" (\n map (intercalate \" & \") [\n [\n \"\\\\det B^* &= \" ++\n texifyMatrix ((`mod` 2) <$> determinant mBStar),\n \"\\\\text{charpoly} B^* &= \" ++\n texifyPoly' \"\\\\Lambda\" texifyMatrix (fmap (`mod` 2) <$> charpoly mBStar)\n ], [\n \"\\\\det \\\\hat B &= \" ++\n show ((`mod` 2) $ determinant mBHat),\n \"\\\\text{charpoly} \\\\hat B &= \" ++\n texifyPoly' \"\\\\lambda\" show ((`mod` 2) <$> charpoly mBHat)\n ]\n ]) ++\n \"\\\\end{align*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{align*}\\det B^* &= \\left( \\begin{matrix}1 & 0 \\\\ 0 & 1\\end{matrix} \\right) & \\text{charpoly} B^* &= \\left( \\begin{matrix}1 & 0 \\\\ 0 & 1\\end{matrix} \\right) + \\left( \\begin{matrix}1 & 1 \\\\ 1 & 0\\end{matrix} \\right)\\Lambda + \\Lambda^{2} \\\\ \\\\ \\det \\hat B &= 1 & \\text{charpoly} \\hat B &= 1 + \\lambda + \\lambda^{2} + \\lambda^{3} + \\lambda^{4}\\end{align*}$$\n:::\n:::\n\n\n### Another Forgotten Path\n\nIt's a relatively simple matter to move between determinants, since it's straightforward\n to identify 1 and the identity matrix.\nHowever, a natural question to ask is whether there's a way to reconcile or coerce\n the matrix polynomial into the \"forgotten\" one.\n\nFirst, let's formally establish a path from matrix polynomials to a matrix of polynomials.\nWe need only use our friend from the [second post](../2) -- polynomial evaluation.\nSimply evaluating a matrix polynomial *r* at *λI* converts our matrix indeterminate (*Λ*)\n into a scalar one (*λ*).\n\n$$\n\\begin{align*}\n \\text{eval}_{\\Lambda \\mapsto \\lambda I}\n &: (\\mathbb{F}_2 {}^{2 \\times 2})[\\Lambda]\n \\rightarrow (\\mathbb{F}_2[\\lambda]) {}^{2 \\times 2}\n \\\\\n &:: \\quad\n r(\\Lambda) \\mapsto r(\\lambda I)\n\\end{align*}\n$$\n\n::: {#8e5ab00c .cell execution_count=11}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Function following from the evaluation definition above\n-- Note that `Poly . pure` is used to transform matrices of `a`\n-- into matrices of polynomials.\n\ntoMatrixPolynomial :: (Eq a, Num a) =>\n Polynomial (Matrix a) -> Matrix (Polynomial a)\ntoMatrixPolynomial xs = evalPoly eyeLambda $ fmap (fmap (Poly . pure)) xs where\n -- First dimensions of the coefficients\n (is, _) = unzip $ map (snd . bounds . unMat) $ coeffs xs\n -- Properly-sized identity matrix times a scalar lambda\n eyeLambda = eye (1 + maximum is) * toMatrix [[Poly [0, 1]]]\n\n\nmarkdown $ \"$$\\\\begin{align*}\" ++\n \"\\\\text{eval}_{\\\\Lambda \\\\mapsto \\\\lambda I}(\\\\text{charpoly}(B^*)) &=\" ++\n texifyPoly' \"(\\\\lambda I)\" texifyMatrix\n (fmap (`mod` 2) <$> charpoly mBStar) ++\n \"\\\\\\\\ &= \" ++\n texifyMatrix' (texifyPoly' \"\\\\lambda\" show)\n (toMatrixPolynomial $ fmap (`mod` 2) <$> charpoly mBStar) ++\n \"\\\\end{align*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{align*}\\text{eval}_{\\Lambda \\mapsto \\lambda I}(\\text{charpoly}(B^*)) &=\\left( \\begin{matrix}1 & 0 \\\\ 0 & 1\\end{matrix} \\right) + \\left( \\begin{matrix}1 & 1 \\\\ 1 & 0\\end{matrix} \\right)(\\lambda I) + (\\lambda I)^{2}\\\\ &= \\left( \\begin{matrix}1 + \\lambda + \\lambda^{2} & \\lambda \\\\ \\lambda & 1 + \\lambda^{2}\\end{matrix} \\right)\\end{align*}$$\n:::\n:::\n\n\nSince a matrix containing polynomials is still a matrix, we can then take its determinant.\nWhat pops out is exactly what we were after...\n\n::: {#a9a302d9 .cell execution_count=12}\n``` {.haskell .cell-code code-fold=\"true\"}\nmarkdown $ \"$$\\\\begin{align*}\" ++\n \"\\\\det(\\\\text{eval}_{\\\\Lambda \\\\mapsto \\\\lambda I}(\" ++\n \"\\\\text{charpoly}(B^*))) &=\" ++\n \"(1 + \\\\lambda + \\\\lambda^2)(1 + \\\\lambda^2) - \\\\lambda^2\" ++\n \"\\\\\\\\ &=\" ++\n texifyPoly' \"\\\\lambda\" show\n (fmap (`mod` 2) <$> determinant $ toMatrixPolynomial $ charpoly mBStar) ++\n \"\\\\\\\\ &= \\\\text{charpoly}{\\\\hat B}\" ++\n \"\\\\end{align*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{align*}\\det(\\text{eval}_{\\Lambda \\mapsto \\lambda I}(\\text{charpoly}(B^*))) &=(1 + \\lambda + \\lambda^2)(1 + \\lambda^2) - \\lambda^2\\\\ &=1 + \\lambda + \\lambda^{2} + \\lambda^{3} + \\lambda^{4}\\\\ &= \\text{charpoly}{\\hat B}\\end{align*}$$\n:::\n:::\n\n\n...and we can arrange our maps into another diagram:\n\n$$\n\\begin{gather*}\n \\begin{CD}\n (\\mathbb{F}_2 {}^{2 \\times 2})^{2 \\times 2}\n @>{\\text{charpoly}}>>\n (\\mathbb{F}_2 {}^{2 \\times 2})[\\Lambda]\n \\\\\n @V{\\text{id}}VV ~ @VV{\\text{eval}_{\\Lambda \\mapsto \\lambda I}}V\n \\\\\n -\n @. (\\mathbb{F}_2 [\\lambda])^{2 \\times 2}\n \\\\\n @V{\\text{forget}}VV ~ @VV{\\det}V\n \\\\\n \\mathbb{F}_2 {}^{4 \\times 4}\n @>>{\\text{charpoly}}>\n \\mathbb{F}_2[\\lambda]\n \\end{CD}\n \\\\ \\\\\n \\text{charpoly} \\circ \\text{forget}\n = \\det \\circ ~\\text{eval}_{\\Lambda \\mapsto \\lambda I} \\circ\\text{charpoly}\n\\end{gather*}\n$$\n\nIt should be noted that we do *not* get the same results by taking the determinant after\n applying **charpoly**\\*, indicating that the above method is \"correct\".\n\n::: {#c9d284c9 .cell execution_count=13}\n``` {.haskell .cell-code code-fold=\"true\"}\nmarkdown $ \"$$\\\\begin{align*}\" ++\n \"\\\\text{charpoly}^*(B^*) &=\" ++\n texifyMatrix' (texifyPoly' \"\\\\lambda\" show)\n (fmap (`mod` 2) <$> fmap charpoly mBStar) ++\n \"\\\\\\\\ \\\\\\\\\" ++\n \"\\\\det(\\\\text{charpoly}^*(B^*)) &=\" ++\n \"\\\\lambda^2(1 + \\\\lambda + \\\\lambda^2) - (1 + \\\\lambda + \\\\lambda^2)^2\" ++\n \"\\\\\\\\ &= \" ++\n texifyPoly' \"\\\\lambda\" show\n (fmap (`mod` 2) <$> determinant $ fmap charpoly mBStar) ++\n \"\\\\end{align*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{align*}\\text{charpoly}^*(B^*) &=\\left( \\begin{matrix}\\lambda^{2} & 1 + \\lambda + \\lambda^{2} \\\\ 1 + \\lambda + \\lambda^{2} & 1 + \\lambda + \\lambda^{2}\\end{matrix} \\right)\\\\ \\\\\\det(\\text{charpoly}^*(B^*)) &=\\lambda^2(1 + \\lambda + \\lambda^2) - (1 + \\lambda + \\lambda^2)^2\\\\ &= 1 + \\lambda^{3}\\end{align*}$$\n:::\n:::\n\n\n### Cycles and Cycles\n\nSince we can get $\\lambda^4 + \\lambda^3 + \\lambda^2 + \\lambda + 1$ in two ways,\n it's natural to assume this polynomial is significant in some way.\nIn the language of the the second post, the polynomial can also be written as ~2~31,\n whose root we determined was cyclic of order 5.\nThis happens to match the order of *B* in GL(2, 4).\n\nPerhaps this is unsurprising, since there are only so many polynomials of degree 4 over GF(2).\nHowever, the reason we see it is more obvious if we look at the powers of scalar multiples of *B*.\nFirst, recall that *f*\\* takes us from a matrix over GF(4) to a matrix of matrices of GF(2).\nThen define a map *g* that gives us degree 4 polynomials:\n\n$$\n\\begin{gather*}\n g : \\mathbb{F}_4^{2 \\times 2} \\rightarrow \\mathbb{F}_2[\\lambda]\n \\\\\n g = \\text{charpoly} \\circ \\text{forget} \\circ f^*\n\\end{gather*}\n$$\n\n::: {#5e05ff31 .cell layout-ncol='3' execution_count=14}\n``` {.haskell .cell-code code-fold=\"true\"}\ng = fmap (`mod` 2) . charpoly . forget . fStar\n\nshowSeries varName var = \"$$\\\\begin{array}{}\" ++\n \" & \\\\scriptsize \" ++\n texifyMatrix var ++\n \"\\\\\\\\\" ++\n intercalate \" \\\\\\\\ \" [\n (if n == 1 then varName' else varName' ++ \"^{\" ++ show n ++ \"}\") ++\n \"& \\\\overset{g}{\\\\mapsto} &\" ++\n texPolyAsPositional' \"\\\\lambda\" (g $ var^n)\n | n <- [1..5]\n ] ++\n \"\\\\end{array}$$\" where\n varName' = if length varName == 1 then varName else \"(\" ++ varName ++ \")\"\n\nmarkdown $ showSeries \"B\" mBOrig\nmarkdown $ showSeries \"αB\" (fmap (AlphaF4*) mBOrig)\nmarkdown $ showSeries \"α^2 B\" (fmap (Alpha2F4*) mBOrig)\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{array}{} & \\scriptsize \\left( \\begin{matrix}0 & α \\\\ α^2 & α^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}$$\n:::\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{array}{} & \\scriptsize \\left( \\begin{matrix}0 & α^2 \\\\ 1 & 1\\end{matrix} \\right)\\\\(αB)& \\overset{g}{\\mapsto} &10011_{\\lambda} \\\\ (αB)^{2}& \\overset{g}{\\mapsto} &10011_{\\lambda} \\\\ (αB)^{3}& \\overset{g}{\\mapsto} &11111_{\\lambda} \\\\ (αB)^{4}& \\overset{g}{\\mapsto} &10011_{\\lambda} \\\\ (αB)^{5}& \\overset{g}{\\mapsto} &10101_{\\lambda}\\end{array}$$\n:::\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{array}{} & \\scriptsize \\left( \\begin{matrix}0 & 1 \\\\ α & α\\end{matrix} \\right)\\\\(α^2 B)& \\overset{g}{\\mapsto} &11001_{\\lambda} \\\\ (α^2 B)^{2}& \\overset{g}{\\mapsto} &11001_{\\lambda} \\\\ (α^2 B)^{3}& \\overset{g}{\\mapsto} &11111_{\\lambda} \\\\ (α^2 B)^{4}& \\overset{g}{\\mapsto} &11001_{\\lambda} \\\\ (α^2 B)^{5}& \\overset{g}{\\mapsto} &10101_{\\lambda}\\end{array}$$\n:::\n:::\n\n\nThe matrices in the middle and rightmost columns both have order 15 inside GL(2, 4).\nCorrespondingly, both 10011~λ~ = ~2~19 and 11001~λ~ = ~2~25 are primitive,\n and so have roots of order 15 over GF(2).\n\n\n### A Field?\n\nSince we have 15 matrices generated by the powers of one, you might wonder whether or not\n they can correspond to the nonzero elements of GF(16).\nAnd they can!\nIn a sense, we've \"borrowed\" the order 15 elements from this \"field\" within GL(4, 2).\nHowever, none of the powers of this matrix are the companion matrix of either ~2~19 or ~2~25.\n\n<details>\n<summary>\nHaskell demonstration of the field-like-ness of these matrices\n</summary>\n\nAll we really need to do is test additive closure, since the powers trivially commute and include the identity matrix.\n\n::: {#027fb2e4 .cell execution_count=15}\n``` {.haskell .cell-code}\n-- Check whether n x n matrices (mod p) have additive closure\n-- Supplement the identity, even if it is not already present\nhasAdditiveClosure :: Integral a => Int -> a -> [Matrix a] -> Bool\nhasAdditiveClosure n p xs = all (`elem` xs') sums where\n -- Add in the zero matrix\n xs' = zero n:xs\n -- Calculate all possible sums of pairs (mod p)\n sums = map (fmap (`mod` p)) $ (+) <$> xs' <*> xs'\n\n-- Generate the powers of x, then test if they form a field (mod p)\ngeneratesField :: Integral a => Int -> a -> Matrix a -> Bool\ngeneratesField n p x = hasAdditiveClosure n p xs where\n xs = map (fmap (`mod` p) . (x^)) [1..p^n-1]\n\n\nprint $ generatesField 4 2 $ forget $ fStar $ fmap (AlphaF4*) mBOrig\n```\n\n::: {.cell-output .cell-output-display}\n```\nTrue\n```\n:::\n:::\n\n\n</details>\n\nMore directly, we might also observe that *α*^2^*B* is the companion matrix of\n an irreducible polynomial over GF(4), namely $q(x) = x^2 - \\alpha x - \\alpha$.\n\nBoth the \"forgotten\" matrices and the aforementioned companion matrices lie within GL(4, 2).\nA natural question to ask is whether we can make fields by the following process:\n\n1. Filter out all order-15 elements of GL(4, 2)\n2. Partition the elements and their powers into their respective order-15 subgroups\n3. Add the zero matrix into each class\n4. Check whether all classes are additively closed (and are therefore fields)\n\nIn this case, it happens to be true, but proving this in general is difficult, and I haven't done so.\n\n\nExpanding Dimensions\n--------------------\n\nOf course, we need not only focus on GF(4) -- we can just as easily work over GL(2, 2*r*) for other *r* than 2.\nIn this case, the internal matrices will be *r*×*r* while the external one remains 2×2.\nBut neither do we have to work exclusively with 2×2 matrices -- we can work over GL(*n*, 2^*r*^).\nIn either circumstance, the \"borrowing\" of elements of larger order still occurs.\nThis is summarized by the following diagram:\n\n$$\n\\begin{CD}\n \\underset{\n \\scriptsize S \\text{ (order $k$)}\n }{\n \\text{SL}(n,2^r)\n }\n @>>>\n \\underset{\n \\scriptsize\n \\begin{matrix}\n S \\text{ (order $k$)} \\\\\n T \\text{ (order $2^{nr}-1$)}\n \\end{matrix}\n }{\n \\text{GL}(n, 2^r)\n }\n @>{\\text{forget} \\circ f_{r}^*}>>\n {\\text{GL}(nr, 2)}\n @<{f_{nr}}<<\n \\underset{\n \\scriptsize\n \\begin{matrix}\n s \\text{ (order $k$)} \\\\\n t \\text{ (order $2^{nr}-1$)}\n \\end{matrix}\n }{\n \\mathbb{F}_{2^{nr}}\n }\n\\end{CD}\n$$\n\nHere, *f*~*r*~ is our map from GF(2^*r*^) to *r*×*r* matrices and *f*~*nr*~ is a similar map.\n*r* must greater than 1 for us to properly make use of matrix arithmetic.\nSimilarly, *n* must be greater than 1 for the leftmost GL.\nThus, *nr* is a composite number.\nHere, *k* is a proper factor of 2^*nr*^ - 1.\nIn the prior discussion, *k* was 5 and 2^*nr*^ - 1 was 15.\n\nRecall that primitive polynomials over GF(2^*nr*^) have roots with order 2^*nr*^ - 1.\nThis number can *never* be prime, since the only primes of the form\n 2^*p*^ - 1 are Mersenne primes -- *p* itself must be prime.\nThus, in GL of prime dimensions, we can never loan to a GL over a field\n of larger order with the same characteristic.\nConversely, GL(*nr* + 1, 2) trivially contains GL(*nr*, 2) by fixing a subspace.\nSo we do eventually see elements of order 2^*m*^ - 1 for either prime or composite *m*.\n\n\n### Other Primes\n\nThis concern about prime dimensions is unique to characteristic 2.\nFor any other prime *p*, *p*^*m*^ - 1 is composite since it is at the very least even.\nAll other remarks about the above diagram should still hold for any other prime *p*.\n\nIn addition, the diagram where we found a correspondence between the orders of elements in\n GL(2, 2^2^) and GF(2^2×2^) via the characteristic polynomial also generalizes.\nThough I have not proven it, I strongly suspect the following diagram commutes,\n at least in the case where *K* is a finite field:\n\n$$\n\\begin{CD}\n (K^{r \\times r})^{n \\times n}\n @>{\\text{charpoly}}>>\n (K^{r \\times r})[\\Lambda]\n \\\\\n @V{\\text{id}}VV ~ @VV{\\text{eval}_{\\Lambda \\mapsto \\lambda I}}V\n \\\\\n -\n @. (K [\\lambda])^{r \\times r}\n \\\\\n @V{\\text{forget}}VV ~ @VV{\\det}V\n \\\\\n K^{nr \\times nr}\n @>>{\\text{charpoly}}>\n K[\\lambda]\n\\end{CD}\n$$\n\nOver larger primes, the gap between GL and SL may grow ever larger,\n but SL over a prime power field seems to inject into SL over a prime field.\nIf the above diagram is true, then the prior statement follows.\n\n\n### Monadicity and Injections\n\nThe action of forgetting the internal structure may sound somewhat familiar if you know your Haskell.\nRemember that for lists, we can do something similar\n -- converting `[[1,2,3],[4,5,6]]` to `[1,2,3,4,5,6]` is just a matter of applying `concat`.\nThis is an instance in which we know lists to behave like a [monad](https://wiki.haskell.org/Monad).\nDespite being an indecipherable bit of jargon to newcomers, it just means we:\n\n1. can apply functions inside the structure (for example, to the elements of a list),\n2. have a sensible injection into the structure (creating singleton lists, called `return`), and\n3. can reduce two layers to one (`concat`, or `join` for monads in general).\n - Monads are traditionally defined using the operator `>>=`, but `join = (>>= id)`\n\nJust comparing the types of `join :: Monad m => m (m a) -> m a`\n and `forget :: Matrix (Matrix a) -> Matrix a` suggests that `Matrix` (meaning square matrices)\n could be a monad, and further, one which respects addition and multiplication.\nOf course, **this is only true when our internal matrices are all the same size**.\nIn the above diagrams, this restriction has applied, but should be stated explicitly\n since no dimension is specified by `Matrix a`.\n\nCondition 2 gives us some trouble, though.\nFor one, only \"numbers\" (elements of a ring) can go inside matrices, which restricts\n where monadicity can hold.\nMore importantly, we have a *lot* of freedom in what dimension we choose to inject into.\nFor example, we might pick a `return` that uses 1×1 matrices (which add no additional structure).\nWe might also pick `return2`, which scalar-multiplies its argument to a 2×2 identity matrix instead.\n\nUnfortunately, there's no good answer.\nAt the very least, we can close our eyes and pretend that we have a nice diagram:\n\n$$\n\\begin{gather*}\n \\begin{matrix}\n & L\\underset{\\text{degree } r}{/} K\n \\\\ \\\\\n \\small f\n & \\begin{matrix} | \\\\ \\downarrow \\end{matrix}\n \\\\ \\\\\n & K^{r \\times r}\n \\end{matrix}\n & \\quad & \\quad\n & \\begin{matrix}\n & (L\\underset{\\text{degree } r}{/} K)^{n \\times n}\n \\\\ \\\\\n \\small f^* &\n \\begin{matrix} | \\\\ \\downarrow \\end{matrix}\n & \\searrow & \\small \\texttt{>>=} ~ f \\qquad\n \\\\ \\\\\n & (K^{r \\times r})^{n \\times n}\n & \\underset{\\text{forget}} {\\longrightarrow}\n & K {}^{nr \\times nr}\n \\end{matrix}\n\\end{gather*}\n$$\n\nAs one last note on the monadicity of matrices, I *have* played around with an alternative `Matrix`\n type which includes scalars alongside proper matrices, which would allow for\n a simple canonical injection.\nUnfortunately, it complicates `join` -- we just place the responsibility of sizing the internal matrices\n front-and-center since we can correspond internal scalars with identity matrices.\n\n\nClosing\n-------\n\nAt this point, I've gone on far too long about algebra.\nOne nagging curiosity makes me wonder whether the there are any diagrams like the following:\n\n$$\n\\begin{matrix}\n & (L\\underset{\\text{degree } r}{/} K)^{n \\times n}\n & & & & (L\\underset{\\text{degree } n}{/} K)^{r \\times r}\n \\\\ \\\\\n \\small f_1^*\n & \\begin{matrix} | \\\\ \\downarrow \\end{matrix}\n & \\searrow & & \\swarrow\n & \\begin{matrix} | \\\\ \\downarrow \\end{matrix}\n & \\small f_2^*\n \\\\ \\\\\n & (K^{r \\times r})^{n \\times n}\n & \\underset{\\text{forget}} {\\longrightarrow}\n & K {}^{nr \\times nr}\n & \\underset{\\text{forget}}{\\longleftarrow}\n & (K^{n \\times n})^{r \\times r}\n\\end{matrix}\n$$\n\nOr in English, whether \"rebracketing\" certain *nr* × *nr* matrices can be traced back to\n not only a degree *r* field extension, but also one of degree *n*.\n\nThe mathematician in me tells me to believe in well-defined structures.\nMatrices are one such structure, with myriad applications.\nHowever, the computer scientist in me laments that the application of these structures is\n buried in symbols and that layering them is at most glossed over.\nThere is clear utility and interest in doing so, otherwise the diagrams shown above would not exist.\n\nOf course, there's plenty of reason *not* to go down this route.\nFor one, it's plainly inefficient -- GPUs are *built* on matrix operations being\n as efficient as possible, i.e., without the layering.\nIt's also inefficient to learn for people *just* learning matrices.\nI'd still argue that the method is useful for learning about more complex topics, like field extensions.\n\n",
"supporting": [
"index_files/figure-html"
],
"filters": [],
"includes": {}
}
}