2025-08-08 04:22:49 -05:00

12 lines
39 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": "4461ec622cfdbf3190ece10ef3b6c9d2",
"result": {
"engine": "jupyter",
"markdown": "---\ntitle: \"Exploring Finite Fields, Part 2: Matrix Boogaloo\"\ndescription: |\n How do we extend a field non-symbolically?\nformat:\n html:\n html-math-method: katex\ndate: \"2024-01-15\"\ndate-modified: \"2025-08-03\"\ncategories:\n - algebra\n - finite field\n - haskell\n---\n\n\n\nIn the [last post](../1/), we discussed finite fields, polynomials and matrices over them, and the typical,\n symbolic way of extending fields with polynomials.\nThis post will will focus on circumventing symbolic means with numeric ones.\n\n\nMore about Matrices (and Polynomials)\n-------------------------------------\n\nRecall the definition of polynomial evaluation.\nSince a polynomial is defined with respect to a field or ring, we expect only to be able to evaluate the\n polynomial at values *in* that field or ring.\n\n$$\n\\begin{gather*}\n K[x] \\times K \\overset{\\text{eval}}{\\longrightarrow} K\n \\\\\n (p(x), n) \\overset{\\text{eval}}{\\mapsto} p(n)\n\\end{gather*}\n$$\n\nHowever, there's nothing wrong with evaluating polynomials with another polynomial,\n as long as they're defined over the same structure.\nAfter all, we can take powers of polynomials, scalar-multiply them with coefficients from *K*,\n and add them together.\nThe same holds for matrices, or any \"collection\" structure *F* over *K* which has those properties.\n\n$$\n\\begin{align*}\n K[x] \\times K[x]\n &\\overset{\\text{eval}_{poly}}{\\longrightarrow} K[x]\n \\\\\n (p(x), q(x)) \\mapsto p(q(x))\n \\\\[10pt]\n K[x] \\times K^{n \\times n}\n &\\overset{\\text{eval}_{mat}}{\\longrightarrow} K^{n \\times n}\n (p(x), A) \\overset{?}{\\mapsto} p(A)\n \\\\[10pt]\n K[x] \\times F(K)\n &\\overset{\\text{eval}_F}{\\longrightarrow} F(K)\n\\end{align*}\n$$\n\n\n### Cayley-Hamilton Theorem\n\nWhen evaluating the characteristic polynomial of a matrix *with* that matrix,\n something strange happens.\nContinuing from the previous article, using $x^2 + x + 1$ and its companion matrix, we have:\n\n$$\n\\begin{gather*}\n p(x) = x^2 + x + 1 \\qquad C_{p} = C\n = \\left( \\begin{matrix}\n 0 & 1 \\\\\n -1 & -1\n \\end{matrix} \\right)\n \\\\ \\\\\n \\begin{align*}\n \\text{eval}_{mat}(p, C) &= C^2 + C + (1 \\cdot I)\n \\\\\n &= \\left( \\begin{matrix}\n -1 & -1 \\\\\n 1 & 0\n \\end{matrix} \\right)\n + \\left( \\begin{matrix}\n 0 & 1 \\\\\n -1 & -1\n \\end{matrix} \\right)\n + \\left( \\begin{matrix}\n 1 & 0 \\\\\n 0 & 1\n \\end{matrix} \\right)\n \\\\\n &= \\left( \\begin{matrix}\n 0 & 0 \\\\\n 0 & 0\n \\end{matrix} \\right)\n \\end{align*}\n\\end{gather*}\n$$\n\nThe result is the zero matrix.\nThis tells us that, at least in this case, the matrix *C* is a root of its own characteristic polynomial.\nBy the [Cayley-Hamilton theorem](https://en.wikipedia.org/wiki/Cayley%E2%80%93Hamilton_theorem),\n this is true in general, no matter the degree of *p*, no matter its coefficients,\n and importantly, no matter the choice of field.\n\nIn addition to this, we can also note the following:\n\n- Irreducible polynomials cannot have a constant term 0, otherwise *x* could be factored out.\n The constant term is equal to the determinant of the companion matrix (up to sign),\n so *C*~*p*~ must be invertible.\n- All powers of *C*~*p*~ are guaranteed to commute over multiplication,\n since this follows from associativity.\n\nBoth of these facts narrow the ring of matrices to a full-on field.\nThis absolves us of needing to adjoin roots symbolically using α.\nInstead, we can take the companion matrix of an irreducible polynomial *p*\n and work with its powers in the same way we would a typical root[^1].\n\n[^1]: For finite fields, it might make sense to do the following procedure\n to generate every possible element:\n\n - Take all powers of a companion matrix *C*\n - Add all powers of *C* with prior elements of the field (times identity matrices)\n - Repeat until no new elements are generated\n\n In fact, we can usually do a little better, as we'll see.\n\n\nGF(8)\n-----\n\nThis is all rather abstract, so let's look at an example before we proceed any further.\nThe next smallest field of characteristic 2 is GF(8).\nWe can construct this field from the two irreducible polynomials of degree 3 over GF(2):\n\n::: {#9211524b .cell execution_count=3}\n``` {.haskell .cell-code code-fold=\"true\"}\nirrsOfDegree d n = map fst $ takeWhile ((==d) . snd) $ dropWhile ((/=d) . snd) $\n map ((,) <*> (+(-1)) . length . coeffs) $ irreducibles n\n\n-- First (and only) two polynomials of degree 3\nqPoly:rPoly:_ = irrsOfDegree 3 2\n\n\n-- Display a polynomial in positional notation, base x\ntexPolyAsPositional (Poly xs) = (++ \"_{x}\") $\n reverse xs >>= (\\x -> if x < 0 then \"\\\\bar{\" ++ show (-x) ++ \"}\" else show x)\n-- Display a polynomial as its encoding in base b\ntexPolyAsNumeric b p = ((\"{}_{\" ++ show b ++ \"} \") ++) $ show $ evalPoly b p\n\n-- Display a polynomial and equivalent notations\ntexPolyPosNum b p = texifyPoly p ++ \" = \" ++\n texPolyAsPositional p ++ \"\\\\sim\" ++\n texPolyAsNumeric b p\n\n-- Display a polynomial and its companion matrix\ntexPolyAndMatrix b p name = name ++ \"(x) = \" ++ texPolyPosNum b p ++\n \"\\\\qquad C_{\" ++ name ++ \"} = \" ++ texifyMatrix ((`mod` b) <$> companion p)\n\nmarkdown $ \"$$\\\\begin{gather*}\" ++\n texPolyAndMatrix 2 qPoly \"q\" ++\n \"\\\\\\\\\" ++ texPolyAndMatrix 2 rPoly \"r\" ++\n \"\\\\end{gather*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{gather*}q(x) = 1 + x + x^{3} = 1011_{x}\\sim{}_{2} 11\\qquad C_{q} = \\left( \\begin{matrix}0 & 1 & 0 \\\\ 0 & 0 & 1 \\\\ 1 & 1 & 0\\end{matrix} \\right)\\\\r(x) = 1 + x^{2} + x^{3} = 1101_{x}\\sim{}_{2} 13\\qquad C_{r} = \\left( \\begin{matrix}0 & 1 & 0 \\\\ 0 & 0 & 1 \\\\ 1 & 0 & 1\\end{matrix} \\right)\\end{gather*}$$\n:::\n:::\n\n\nNotice how the bit strings for either of these polynomials is the other, reversed.\nArbitrarily, let's work with C~r~.\nThe powers of this matrix (mod 2) are as follows:\n\n::: {#2dda47b9 .cell execution_count=4}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Compute all powers of a matrix, starting with the first\nmatrixPowersMod b mat = iterate (((`mod` b) <$>) . (mat*)) mat\n\n-- Show a matrix power\ntexMatrixPower n b mat name = \"(\" ++ name ++ \")^{\" ++ show n ++ \"} = \" ++\n texifyMatrix ((`mod` b) <$> mat)\n\n-- Show all matrix powers\ntexPows b mat name = [texMatrixPower n b matPow name |\n (n, matPow) <- zip [1..] (matrixPowersMod b mat)]\n\nlet pows = texPows 2 (companion rPoly) \"C_r\" in\n markdown $ \"$$\\\\begin{gather*}\" ++\n concat (take 3 pows) ++ \"\\\\\\\\\" ++\n concat (take 3 $ drop 3 pows) ++ \"\\\\\\\\\" ++\n (pows !! (7 - 1)) ++ \" = I = (C_r)^0 \\\\quad\" ++\n (pows !! (8 - 1)) ++ \" = C_r\" ++\n \"\\\\end{gather*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{gather*}(C_r)^{1} = \\left( \\begin{matrix}0 & 1 & 0 \\\\ 0 & 0 & 1 \\\\ 1 & 0 & 1\\end{matrix} \\right)(C_r)^{2} = \\left( \\begin{matrix}0 & 0 & 1 \\\\ 1 & 0 & 1 \\\\ 1 & 1 & 1\\end{matrix} \\right)(C_r)^{3} = \\left( \\begin{matrix}1 & 0 & 1 \\\\ 1 & 1 & 1 \\\\ 1 & 1 & 0\\end{matrix} \\right)\\\\(C_r)^{4} = \\left( \\begin{matrix}1 & 1 & 1 \\\\ 1 & 1 & 0 \\\\ 0 & 1 & 1\\end{matrix} \\right)(C_r)^{5} = \\left( \\begin{matrix}1 & 1 & 0 \\\\ 0 & 1 & 1 \\\\ 1 & 0 & 0\\end{matrix} \\right)(C_r)^{6} = \\left( \\begin{matrix}0 & 1 & 1 \\\\ 1 & 0 & 0 \\\\ 0 & 1 & 0\\end{matrix} \\right)\\\\(C_r)^{7} = \\left( \\begin{matrix}1 & 0 & 0 \\\\ 0 & 1 & 0 \\\\ 0 & 0 & 1\\end{matrix} \\right) = I = (C_r)^0 \\quad(C_r)^{8} = \\left( \\begin{matrix}0 & 1 & 0 \\\\ 0 & 0 & 1 \\\\ 1 & 0 & 1\\end{matrix} \\right) = C_r\\end{gather*}$$\n:::\n:::\n\n\nAs a reminder, these matrices are taken mod 2, so the elements can only be 0 or 1.\nThe seventh power of *C*~*r*~ is just the identity matrix,\n meaning that the eighth power is the original matrix.\nThis means that *C*~*r*~ is cyclic of order 7 with respect to self-multiplication mod 2.\nAlong with the zero matrix, this fully characterizes GF(8).\n\nIf we picked *C*~*q*~ instead, we would have gotten different matrices.\nI'll omit writing them here, but we get the same result: *C*~*q*~ is also cyclic of order 7.\nSince every nonzero element of the field can be written as a power of the root,\n the root (as well as the polynomial) is termed\n [primitive](https://en.wikipedia.org/wiki/Primitive_polynomial_%28field_theory%29).\n\n\n### Condensing\n\nWorking with matrices directly, as a human, is very cumbersome.\nWhile it makes computation explicit, it makes presentation difficult.\nOne of the things in which we know we should be interested is the characteristic polynomial,\n since it is central to the definition and behavior of the matrices.\nLet's focus only on the characteristic polynomial for successive powers of *C*~*r*~\n\n::: {#53c8b922 .cell execution_count=5}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Create sequence of charpolys (mod b) from the powers of its companion matrix\ncharPolyPows b = map (((`mod` b) <$>) . charpoly) . matrixPowersMod b . companion\n\n-- Display row of charpoly array\ntexCharPolyRow b poly name extra = \"\\\\text{charpoly}(\" ++ name ++ \")\" ++ \"&=&\" ++\n fst (extra poly) ++ texifyPoly poly ++ \"&=&\" ++\n fst (extra poly) ++ texPolyAsPositional poly ++ \"\\\\sim\" ++\n texPolyAsNumeric b poly ++ \" = \" ++ snd (extra poly)\n\nmarkdown $ \"$$\\\\begin{array}{}\" ++\n intercalate \" \\\\\\\\ \" [\n texCharPolyRow 2 mat (\"(C_r)^{\" ++ show n ++ \"}\") (\\x ->\n if x == rPoly then (\"\\\\color{blue}\", \"r\")\n else if x == qPoly then (\"\\\\color{red}\", \"q\")\n else (\"\", \"(x + 1)^3\")\n )\n | (n, mat) <- zip [1..7] (charPolyPows 2 rPoly)\n ] ++\n \"\\\\end{array}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{array}{}\\text{charpoly}((C_r)^{1})&=&\\color{blue}1 + x^{2} + x^{3}&=&\\color{blue}1101_{x}\\sim{}_{2} 13 = r \\\\ \\text{charpoly}((C_r)^{2})&=&\\color{blue}1 + x^{2} + x^{3}&=&\\color{blue}1101_{x}\\sim{}_{2} 13 = r \\\\ \\text{charpoly}((C_r)^{3})&=&\\color{red}1 + x + x^{3}&=&\\color{red}1011_{x}\\sim{}_{2} 11 = q \\\\ \\text{charpoly}((C_r)^{4})&=&\\color{blue}1 + x^{2} + x^{3}&=&\\color{blue}1101_{x}\\sim{}_{2} 13 = r \\\\ \\text{charpoly}((C_r)^{5})&=&\\color{red}1 + x + x^{3}&=&\\color{red}1011_{x}\\sim{}_{2} 11 = q \\\\ \\text{charpoly}((C_r)^{6})&=&\\color{red}1 + x + x^{3}&=&\\color{red}1011_{x}\\sim{}_{2} 11 = q \\\\ \\text{charpoly}((C_r)^{7})&=&1 + x + x^{2} + x^{3}&=&1111_{x}\\sim{}_{2} 15 = (x + 1)^3\\end{array}$$\n:::\n:::\n\n\nSomehow, even though we start with one characteristic polynomial, the other manages to work its way in here.\nBoth polynomials are of degree 3 and have 3 matrix roots (distinguished in red and blue).\n\nIf we chose to use *C*~*q*~, we'd actually get the same sequence backwards (starting with ~2~11).\nIt's beneficial to remember that 6, 5, and 3 can also be written as 7 - 1, 7 - 2, and 7 - 4.\nThis makes it clear that the powers of 2 (the field characteristic) less than the 8 (the order of the field)\n play a role with respect to both the initial and terminal items.\n\n\n### Factoring\n\nIntuitively, you may try using the roots to factor the matrix into powers of *C*~*r*~.\nThis turns out to work:\n\n::: {#1157c0af .cell execution_count=6}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Convert a list of roots to a polynomial with those as its roots\nrootsToPoly :: (Num a, Eq a) => [a] -> Polynomial a\nrootsToPoly xs = Poly $ reverse $ zipWith (*) (cycle [1,-1]) vieta where\n -- Group by degree of subsequence\n elemSyms = groupBy ((==) `on` length) . sortOn length . subsequences\n -- Vieta's formulas over xs\n vieta = map (sum . map product) $ elemSyms xs\n\n-- Make a polynomial from the powers of the companion matrix of p (mod b)\ncompanionPowerPoly b p = fmap (fmap (`mod` b)) . rootsToPoly .\n map ((matrixPowersMod b (companion p) !!) . (+(-1)))\n\n\n-- Show a polynomial over matrices\nshowPolyMat :: (Show a, Num a, Eq a) => Polynomial (Matrix a) -> String\nshowPolyMat = intercalate \" + \" . showCoeffs where\n showCoeffs = zipWith showCoeff [0..] . map showMatrix . coeffs\n -- Show the indeterminate as \"X\"\n showCoeff 0 x = x\n showCoeff 1 x = x ++ \"X\"\n showCoeff n x = x ++ \"X^{\" ++ show n ++ \"}\"\n -- Show identity matrices (but not their multiples) as \"I\"\n showMatrix x\n | x `elem` [eye 1, eye $ mSize x] = \"I\"\n | x `elem` [zero 1, zero $ mSize x] = \"0\"\n | otherwise = texifyMatrix x\n mSize = (+1) . snd . snd . bounds . unMat\n\nmarkdown $ \"$$\\\\begin{align*}\" ++\n \"\\\\hat{R}(X) &= (X - (C_r)^1)(X - (C_r)^2)(X - (C_r)^4)\" ++\n \" \\\\\\\\ \" ++\n \" &= \" ++ showPolyMat (companionPowerPoly 2 rPoly [1,2,4]) ++\n \" \\\\\\\\[10pt] \" ++\n \"\\\\hat{Q}(X) &= (X - (C_r)^3)(X - (C_r)^5)(X - (C_r)^6)\" ++\n \" \\\\\\\\ \" ++\n \" &= \" ++ showPolyMat (companionPowerPoly 2 rPoly [3,5,6]) ++\n \"\\\\end{align*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{align*}\\hat{R}(X) &= (X - (C_r)^1)(X - (C_r)^2)(X - (C_r)^4) \\\\ &= I + 0X + IX^{2} + IX^{3} \\\\[10pt] \\hat{Q}(X) &= (X - (C_r)^3)(X - (C_r)^5)(X - (C_r)^6) \\\\ &= I + IX + 0X^{2} + IX^{3}\\end{align*}$$\n:::\n:::\n\n\nWe could have factored our polynomials differently if we used *C*~*q*~ instead.\nHowever, the effect of splitting both polynomials into monomial factors is the same.\n\n\nGF(16)\n------\n\nGF(8) is simple to study, but too simple to study the sequence of characteristic polynomials alone.\nLet's widen our scope to GF(16).\nThere are three irreducible polynomials of degree 4 over GF(2).\n\n::: {#7f0fb15f .cell execution_count=7}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- First (and only) three polynomials of degree 4\nsPoly:tPoly:uPoly:_ = irrsOfDegree 4 2\n\nmarkdown $ \"$$\\\\begin{gather*}\" ++\n texPolyAndMatrix 2 sPoly \"s\" ++ \"\\\\\\\\\" ++\n texPolyAndMatrix 2 tPoly \"t\" ++ \"\\\\\\\\\" ++\n texPolyAndMatrix 2 uPoly \"u\" ++\n \"\\\\end{gather*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{gather*}s(x) = 1 + x + x^{4} = 10011_{x}\\sim{}_{2} 19\\qquad C_{s} = \\left( \\begin{matrix}0 & 1 & 0 & 0 \\\\ 0 & 0 & 1 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 1 & 1 & 0 & 0\\end{matrix} \\right)\\\\t(x) = 1 + x^{3} + x^{4} = 11001_{x}\\sim{}_{2} 25\\qquad C_{t} = \\left( \\begin{matrix}0 & 1 & 0 & 0 \\\\ 0 & 0 & 1 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 1 & 0 & 0 & 1\\end{matrix} \\right)\\\\u(x) = 1 + x + x^{2} + x^{3} + x^{4} = 11111_{x}\\sim{}_{2} 31\\qquad C_{u} = \\left( \\begin{matrix}0 & 1 & 0 & 0 \\\\ 0 & 0 & 1 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 1 & 1 & 1 & 1\\end{matrix} \\right)\\end{gather*}$$\n:::\n:::\n\n\nAgain, *s* and *t* form a pair under the reversal of their bit strings, while *u* is palindromic.\nBoth *C*~*s*~ and *C*~*t*~ are cyclic of order 15, so *s* and *t* are primitive polynomials.\nUsing *s* = ~2~19 to generate the field, the powers of its companion matrix *C*~*s*~\n have the following characteristic polynomials:\n\n::: {#0b44f871 .cell .plain execution_count=8}\n``` {.haskell .cell-code code-fold=\"true\"}\nsPolyCharPowers = charPolyPows 2 sPoly\n\n-- Horizontal table of entries\nfromIndices ns = columns (\\(_, f) r -> f r) (\\(c, _) -> Headed c) $\n map (\\i -> (show i, (!! i))) ns\nfromIndices' = (singleton (Headed \"m\") head <>) . fromIndices\n\n-- Symbolic representation of a power of a companion matrix (in Markdown)\ncompPowSymbolic \"\" m = \"*f*(*C*^*\" ++ m ++ \"*^)\"\ncompPowSymbolic x m = \"*f*((*C*~*\" ++ x ++ \"*~)^*\" ++ m ++ \"*^)\"\n\n-- Spans of a given color\nspanColor color = ((\"<span style=\\\"color: \" ++ color ++ \"\\\">\") ++) . (++ \"</span>\")\n\nmarkdown $ markdownTable (fromIndices' [1..15]) [\n compPowSymbolic \"s\" \"m\":\n map ((\n \\x -> if x == 19 then spanColor \"blue\" (show x)\n else if x == 25 then spanColor \"red\" (show x)\n else show x\n ) . evalPoly 2) sPolyCharPowers\n ]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n| m | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |\n|---|---|---|---|---|---|---|---|---|---|----|----|----|----|----|----|\n| *f*((*C*~*s*~)^*m*^) | <span style=\"color: blue\">19</span> | <span style=\"color: blue\">19</span> | 31 | <span style=\"color: blue\">19</span> | 21 | 31 | <span style=\"color: red\">25</span> | <span style=\"color: blue\">19</span> | 31 | 21 | <span style=\"color: red\">25</span> | 31 | <span style=\"color: red\">25</span> | <span style=\"color: red\">25</span> | 17 |\n\n:::\n:::\n\n\nThe polynomial ~2~19 occurs at positions 1, 2, 4, and 8.\nThese are obviously powers of 2, the characteristic of the field.\nSimilarly, the polynomial *t* = ~2~25 occurs at positions 14 (= 15 - 1), 13 (= 15 - 2),\n 11 (= 15 - 4), and 7 (= 15 - 8).\nWe'd get the same sequence backwards if we used *C*~*t*~ instead, just like in GF(8).\n\n\n### Non-primitive\n\nThe polynomial *u* = ~2~31 occurs at positions 3, 6, 9, and 12\n -- multiples of 3, which is a factor of *15*.\nIt follows that the roots of *u* are cyclic of order 5, so this polynomial is irreducible,\n but *not* primitive.\n\nNaturally, *u* (or a polynomial isomorphic to it) can be factored as powers of (*C*~*s*~)^3^.\nWe can also factor it more naively as powers of *C*~*u*~.\nEither way, we get the same sequence.\n\n:::: {layout-ncol=\"2\" layout-valign=\"center\"}\n::: {}\n\n::: {#a9ad689c .cell .plain execution_count=9}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Get every entry of an (infinite) list which is a multiple of n\nentriesEvery n = map head . unfoldr (Just . splitAt n)\n\nmarkdown $ markdownTable (fromIndices' [1..5]) [\n compPowSymbolic \"s\" \"3m\":\n map (show . evalPoly 2)\n (entriesEvery 3 $ drop 2 sPolyCharPowers),\n compPowSymbolic \"u\" \"m\":\n map (show . evalPoly 2) (charPolyPows 2 uPoly)\n ]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n| m | 1 | 2 | 3 | 4 | 5 |\n|---|---|---|---|---|---|\n| *f*((*C*~*s*~)^*3m*^) | 31 | 31 | 31 | 31 | 17 |\n| *f*((*C*~*u*~)^*m*^) | 31 | 31 | 31 | 31 | 17 |\n\n:::\n:::\n\n\nBoth of the matrices in column 5 happen to be the identity matrix.\nIt follows that this root is only cyclic of order 5.\n\nThe polynomials ~2~19 and ~2~25 are reversals of one another, and the sequences that their companion matrices\n generate end one with another -- in this regard, they are dual.\nHowever, ${}_2 31 = 11111_x$ is a palindrome and its sequence ends where it begins, so it is self-dual.\n:::\n\n::: {width = \"33%\"}\n$$\n\\begin{gather*}\n (C_u)^1 =\\left( \\begin{matrix}\n 0 & 1 & 0 & 0 \\\\\n 0 & 0 & 1 & 0 \\\\\n 0 & 0 & 0 & 1 \\\\\n 1 & 1 & 1 & 1\n \\end{matrix} \\right)\n \\\\ \\\\\n (C_u)^2 =\\left( \\begin{matrix}\n 0 & 0 & 1 & 0 \\\\\n 0 & 0 & 0 & 1 \\\\\n 1 & 1 & 1 & 1 \\\\\n 1 & 0 & 0 & 0\n \\end{matrix} \\right)\n \\\\ \\\\\n (C_u)^3 =\\left( \\begin{matrix}\n 0 & 0 & 0 & 1 \\\\\n 1 & 1 & 1 & 1 \\\\\n 1 & 0 & 0 & 0 \\\\\n 0 & 1 & 0 & 0\n \\end{matrix} \\right)\n \\\\ \\\\\n (C_u)^4 =\\left( \\begin{matrix}\n 1 & 1 & 1 & 1 \\\\\n 1 & 0 & 0 & 0 \\\\\n 0 & 1 & 0 & 0 \\\\\n 0 & 0 & 1 & 0 \\\\\n \\end{matrix} \\right)\n \\\\ \\\\\n (C_u)^5 =\\left( \\begin{matrix}\n 1 & 0 & 0 & 0 \\\\\n 0 & 1 & 0 & 0 \\\\\n 0 & 0 & 1 & 0 \\\\\n 0 & 0 & 0 & 1 \\\\\n \\end{matrix} \\right)\n \\\\\n = I = (C_u)^0\n\\end{gather*}\n$$\n:::\n::::\n\n\n### Non-irreducible\n\nIn addition to the three irreducibles, a fourth polynomial, ${}_2 21 \\sim 10101_x$,\n also appears in the sequence on entries 5 and 10 -- multiples of 5, which is also a factor of 15.\nLike ~2~31, this polynomial is palindromic.\nThis polynomial is *not* irreducible mod 2, and factors as:\n\n::: {#a38aa9dd .cell execution_count=10}\n``` {.haskell .cell-code code-fold=\"true\"}\npoly21 = asPoly 2 21\n\nmarkdown $ \"$$\\\\begin{gather*}\" ++\n texPolyAsPositional poly21 ++ \" = \" ++ texifyPoly poly21 ++\n \" = \\\\left( 1 + x + x^2 \\\\right)^2 \\\\mod 2\" ++\n \" \\\\\\\\[10pt] \" ++\n \"(X - (C_s)^5)(X - (C_s)^{10}) = \" ++\n showPolyMat (companionPowerPoly 2 sPoly [5,10]) ++\n \"\\\\end{gather*}$$\"\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n$$\\begin{gather*}10101_{x} = 1 + x^{2} + x^{4} = \\left( 1 + x + x^2 \\right)^2 \\mod 2 \\\\[10pt] (X - (C_s)^5)(X - (C_s)^{10}) = I + IX + IX^{2}\\end{gather*}$$\n:::\n:::\n\n\nJust like how the fields we construct are powers of a prime, this extra element is a power\n of a smaller irreducible.\nThis is unexpected, but perhaps not surprising.\n\nSomething a little more surprising is that the companion matrix is cyclic of degree *6*,\n rather than of degree 3 like the matrices encountered in GF(8).\nThe powers of its companion matrix are:\n\n::: {#571a51be .cell .plain execution_count=11}\n``` {.haskell .cell-code code-fold=\"true\"}\ncompanion21Pows = matrixPowersMod 2 (companion poly21)\n\nmarkdown $ markdownTable (fromIndices' [1..6]) [\n compPowSymbolic \"s\" \"5m\":\n map (show . evalPoly 2) (entriesEvery 5 $ drop 4 sPolyCharPowers),\n compPowSymbolic \"21\" \"m\":\n map (\\x -> let\n p = (`mod` 2) <$> charpoly x\n p' = evalPoly 2 p\n comp21 = head companion21Pows\n in\n -- x shares its characteristic polynomial with the identity\n if p' == 17 then\n show p' ++\n (if x == eye 4 then \" (identity)\" else \" (*Not* the identity)\")\n -- x is either the companion matrix of the polynomial 21 or its inverse\n else if x == comp21 ||\n eye 4 == ((`mod` 2) <$> x * comp21) then\n spanColor \"red\" $ show p'\n else spanColor \"blue\" $ show p')\n companion21Pows\n ]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n| m | 1 | 2 | 3 | 4 | 5 | 6 |\n|---|---|---|---|---|---|---|\n| *f*((*C*~*s*~)^*5m*^) | 21 | 21 | 17 | 21 | 21 | 17 |\n| *f*((*C*~*21*~)^*m*^) | <span style=\"color: red\">21</span> | <span style=\"color: blue\">21</span> | 17 (*Not* the identity) | <span style=\"color: blue\">21</span> | <span style=\"color: red\">21</span> | 17 (identity) |\n\n:::\n:::\n\n\nWe can think of the repeated sequence as ensuring that there are enough roots of ~2~21.\nThe Fundamental Theorem of Algebra states that there must be 4 roots.\nFor *numbers*, we'd allow duplicate roots with multiplicities greater than 1,\n but the matrix roots are all distinct.\n\nBasic group theory tells us that as a cyclic group, the matrix's first and fifth powers\n (in red) are pairs of inverses.\nThe constant term of the characteristic polynomial is the product of all four roots and,\n as a polynomial over matrices, must be some nonzero multiple of the identity matrix.\nSince the red roots are a pair of inverses, the blue roots are, too.\n\n\nGF(32)\n------\n\nGF(32) turns out to be special.\nThere are six irreducible polynomials of degree 5 over GF(2).\nPicking the \"smallest\" at random, ~2~37, and looking at the polynomial sequence it generates, we see:\n\n::: {#08b6c8ad .cell .plain execution_count=12}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Get all degree 5 polynomials over GF(2)\ndeg5Char2Polys = irrsOfDegree 5 2\nleastDeg5Char2Poly = head deg5Char2Polys\n\ncolorByEval b ps x = (maybe show (. show) . getColor <*> id) $ evalPoly b x where\n getColor = flip lookup $ map (fmap spanColor) ps\n\ncolorDeg5Char2 = colorByEval 2 [\n (37, \"red\"),\n (47, \"orange\"),\n (55, \"yellow\"),\n (41, \"green\"),\n (61, \"blue\"),\n (59, \"purple\")\n ]\n\nmarkdown $ markdownTable (fromIndices' [1..16]) [\n compPowSymbolic \"\" \"m\":\n map colorDeg5Char2 (charPolyPows 2 leastDeg5Char2Poly)\n ]\nmarkdown $ markdownTable (fromIndices' [17..31]) [\n compPowSymbolic \"\" \"m\":\n map colorDeg5Char2 (charPolyPows 2 leastDeg5Char2Poly)\n ]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n| m | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |\n|---|---|---|---|---|---|---|---|---|---|----|----|----|----|----|----|----|\n| *f*(*C*^*m*^) | <span style=\"color: red\">37</span> | <span style=\"color: red\">37</span> | <span style=\"color: blue\">61</span> | <span style=\"color: red\">37</span> | <span style=\"color: yellow\">55</span> | <span style=\"color: blue\">61</span> | <span style=\"color: orange\">47</span> | <span style=\"color: red\">37</span> | <span style=\"color: yellow\">55</span> | <span style=\"color: yellow\">55</span> | <span style=\"color: purple\">59</span> | <span style=\"color: blue\">61</span> | <span style=\"color: purple\">59</span> | <span style=\"color: orange\">47</span> | <span style=\"color: green\">41</span> | <span style=\"color: red\">37</span> |\n\n:::\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n| m | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |\n|---|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|\n| *f*(*C*^*m*^) | <span style=\"color: blue\">61</span> | <span style=\"color: yellow\">55</span> | <span style=\"color: orange\">47</span> | <span style=\"color: yellow\">55</span> | <span style=\"color: purple\">59</span> | <span style=\"color: purple\">59</span> | <span style=\"color: green\">41</span> | <span style=\"color: blue\">61</span> | <span style=\"color: orange\">47</span> | <span style=\"color: purple\">59</span> | <span style=\"color: green\">41</span> | <span style=\"color: orange\">47</span> | <span style=\"color: green\">41</span> | <span style=\"color: green\">41</span> | 51 |\n\n:::\n:::\n\n\n31 is prime, so we don't have any sub-patterns that appear on multiples of factors.\nIn fact, all six irreducible polynomials are present in this table.\nThe pairs in complementary colors form pairs under reversing the polynomials:\n <span style=\"color: red\">~2~37</span> and <span style=\"color: green\">~2~41</span>,\n <span style=\"color: blue\">~2~61</span> and <span style=\"color: orange\">~2~47</span>,\n and <span style=\"color: yellow\">~2~55</span> and <span style=\"color: purple\">~2~59</span>.\n\nSince their roots have order 31, these polynomials are actually\n the distinct factors of *x*^31^ - 1 mod 2:\n\n$$\n\\begin{gather*}\n x^{31} -1 = (x-1)(x^{30} +x^{29} + ... + x + 1)\n \\\\\n (x^{30} +x^{29} + ... + x + 1) =\n \\left\\{ \\begin{align*}\n &\\phantom\\cdot (x^5 + x^2 + 1) &\\sim \\quad {}_2 37\n \\\\\n &\\cdot (x^5 + x^3 + 1) &\\sim \\quad {}_2 41 \\\\\n &\\cdot (x^5 + x^4 + x^3 + x^2 + 1) &\\sim \\quad {}_2 61\n \\\\\n &\\cdot (x^5 + x^3 + x^2 + x + 1) &\\sim \\quad {}_2 47\n \\\\\n &\\cdot (x^5 + x^4 + x^2 + x + 1) &\\sim \\quad {}_2 55\n \\\\\n &\\cdot (x^5 + x^4 + x^3 + x + 1) &\\sim \\quad {}_2 59\n \\end{align*} \\right.\n\\end{gather*}\n$$\n\nThis is a feature special to fields of characteristic 2.\n2 is the only prime number whose powers can be one more than another prime,\n since all other prime powers are one more than even numbers.\n31 is a [Mersenne prime](https://en.wikipedia.org/wiki/Mersenne_prime),\n so all integers less than 31 are coprime to it.\nThus, there is no room for the \"extra\" entries we observed in GF(16) which occurred\n on factors of 15 = 16 - 1.\nNo entry can be irreducible (but not primitive) or the power of an irreducible of lower degree.\nIn other words, *only primitive polynomials exist of degree* p *if 2^p^ - 1 is a Mersenne prime*.\n\n\n### Counting Irreducibles\n\nThe remark about coprimes to 31 may inspire you to think of the\n [totient function](https://en.wikipedia.org/wiki/Euler%27s_totient_function).\nWe have *φ*(2^5^ - 1) = 30 = 5⋅6, where 5 is the degree and 6 is the number of primitive polynomials.\nWe also have *φ*(24 - 1) = 8 = 4⋅2 and *φ*(23 - 1) = 6 = 3⋅2.\nIn general, it is true that there are *φ*(*pm* - 1) / *m* primitive polynomials of degree m over GF(p).\n\n\nPolynomial Reversal\n-------------------\n\nWe've only been looking at fields of characteristic 2, where the meaning of\n \"palindrome\" and \"reversed polynomial\" is intuitive.\nLet's look at an example over characteristic 3.\nOne primitive of degree 2 is ~3~14, which gives rise to the following sequence over GF(9):\n\n::: {#940af06a .cell .plain execution_count=13}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Get all degree 2 polynomials over GF(3)\ndeg2Char3Polys = map fst $ takeWhile ((==3) . snd) $ dropWhile ((/=3) . snd) $\n map ((,) <*> length . coeffs) $ irreducibles 3\nleastDeg2Char3Poly = deg2Char3Polys !! 1\n\ncolorDeg3Char3 = colorByEval 3 [(14, \"red\"), (17, \"blue\")]\n\nmarkdown $ markdownTable (fromIndices' [1..8]) [\n compPowSymbolic \"14\" \"m\":\n map colorDeg3Char3 (charPolyPows 3 leastDeg2Char3Poly)\n ]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n| m | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |\n|---|---|---|---|---|---|---|---|---|\n| *f*((*C*~*14*~)^*m*^) | <span style=\"color: red\">14</span> | 10 | <span style=\"color: red\">14</span> | 16 | <span style=\"color: blue\">17</span> | 10 | <span style=\"color: blue\">17</span> | 13 |\n\n:::\n:::\n\n\nThe table suggests that ${}_3 14 = 112_x = x^2 + x + 2$ and ${}_3 17 = 122_x = x^2 + 2x + 2$\n are reversals of one another.\nMore naturally, you'd think that 112~*x*~ reversed is 211~*x*~.\nBut remember that we prefer to work with monic polynomials.\nBy multiplying the polynomial by the multiplicative inverse of the leading coefficient (in this case, 2),\n we get $422_x \\equiv 122_x \\mod 3$.\nThis is a rule that applies over larger characteristics in general.\n\nNote that ${}_3 16 \\sim 121_x = x^2 + 2x + 1$ and ${}_3 13 \\sim 111_x = x^2 + x + 1 = x^2 - 2x + 1$,\n both of which have factors over GF(3).\n\n\nIrreducible Graphs\n------------------\n\nWe can study the interplay of primitives, irreducibles, and their powers by converting\n our sequences into (directed) graphs.\nEach node in the graph will represent an irreducible polynomial over the field.\nCall the one under consideration *a*.\nIf the sequence of characteristic polynomials generated by powers of *C*~*a*~ contains\n contains another polynomial *b*, then there is an edge from *a* to *b*.\n\n::: {#afbd9183 .cell execution_count=14}\n``` {.haskell .cell-code}\n-- Convert a polynomial to the integer representing it in characteristic n\nasPolyNum n = evalPoly n . fmap (`mod` n)\n\nirreducibleGraph d n = concatMap (\\(x:xs) -> map (x,) xs) polyKinClasses where\n -- All irreducible polynomials of degree d in characteristic n\n irrsOfDegree' = irrsOfDegree d n\n -- Get \"kin\" polynomials as integers -- all those who appear as characteristic\n -- polynomials in the powers of its companion matrix\n getKinPolys = map (asPolyNum n . charpoly) . matrixPowersMod n . companion\n -- Kin classes corresponding to each irreducible polynomial,\n -- which is the first entry\n polyKinClasses = map (nub . take (n^d) . getKinPolys) irrsOfDegree'\n```\n:::\n\n\nWe can do this for every GF(*p*^*m*^).\nLet's start with the first few fields of characteristic 2.\nWe get the following graphs:\n\n![](./char_2_irreducibles_graphs.png)\n\nAll nodes connect to the node corresponding to the identity matrix, since all roots are cyclic.\nAlso, since all primitive polynomials are interchangeable with one another,\n they are all interconnected and form a [complete](https://en.wikipedia.org/wiki/Complete_graph) clique.\nThis means that, excluding the identity node, the graphs for fields of order one more\n than a Mersenne prime are just the complete graphs.\n\nSince all of the graphs share the identity node as a feature\n -- a node with incoming edges from every other node -- its convenient to omit it.\nHere are a few more of these graphs after doing so, over fields of other characteristics:\n\n::: {layout=\"[[1,1], [1,1], [1,1,1]]\"}\n\n::: {#5e1dcbce .cell execution_count=15}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Characteristic polynomial of the identity matrix\neyePoly d n = asPolyNum n $ charpoly $ eye d\n-- Remove edges directed toward the characteristic polynomial of the identity\nirreducibleGraphNoEye d n = filter ((/=eyePoly d n) . snd) $ irreducibleGraph d n\n\n-- Only plot the graph for GF(9), since the others take too long to render\nplotDigraph $ irreducibleGraphNoEye 2 3\n```\n\n::: {.cell-output .cell-output-display}\n![GF(9)](index_files/figure-html/cell-15-output-1.svg){}\n:::\n:::\n\n\n![\n GF(25)\n](./irreducibles-graphs/irred_25.png)\n\n![\n GF(49)\n](./irreducibles-graphs/irred_49.png)\n\n![\n GF(121)\n](./irreducibles-graphs/irred_121.png)\n\n![\n GF(27)\n](./irreducibles-graphs/irred_27.png)\n\n![\n GF(125)\n](./irreducibles-graphs/irred_125.png)\n\n![\n GF(343)\n](./irreducibles-graphs/irred_343.png)\n:::\n\n<!-- This code cell is not evaluated because it takes too long -->\n\n\n\n### Spectra\n\nAgain, since visually interpreting graphs is difficult, we can study an invariant.\nFrom these graphs of polynomials, we can compute *their* characteristic polynomials\n (to add another layer to this algebraic cake) and look at their spectra.\n\nIt turns out that a removing a fully-connected node (like the one for the identity matrix)\n has a simple effect on characteristic polynomial of a graph: it just removes a factor of *x*.\nHere are a few of the (identity-reduced) spectra, arranged into a table.\n\n::: {#cf7d8108 .cell .plain execution_count=17}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Not technically correct, but enough for this example\nedgesToAdjacency [] = toMatrix [[0]]\nedgesToAdjacency es = Mat asArray where\n -- Vertices from the edge list\n vs = nub $ es >>= (\\(x,y) -> [x,y])\n -- Lookup table for new vertices\n vs' = zip vs [0..]\n -- Largest vertex index for array bounds\n b = maximum $ map snd vs'\n -- Lookup function for new edge reindexing\n lookupVs = fromJust . flip lookup vs'\n -- List of reindexed edges\n reindexed = map (bimap lookupVs lookupVs) es\n -- Use a list of 2-tuples to set addresses in a matrix to 1\n asArray = listArray ((0,0),(b,b)) (repeat 0) // map (, 1) reindexed\n\n-- Find roots of `p` by trial dividing the entries of `xs`\nfindRootsFrom xs p = fmap head $ partitionEithers $ recurse p xs where\n -- We only need to test roots we haven't failed to divide\n tails [] = []\n tails x@(_:xs) = x:tails xs\n -- Try dividing `p` by every remaining 'integer' root from xs\n trialDivisions p xs = map (\\x -> (x, p `synthDiv` Poly [-head x, 1])) $ tails xs\n -- Find the first root which has a zero remainder, or Nothing if none exists\n firstRoot p xs = listToMaybe $ dropWhile ((/= 0) . snd . snd) $\n trialDivisions p xs\n -- We either found a root (r) and need to recurse with the quotient (q)\n -- Or we couldn't find a root, and terminate with the number of unfound roots\n recurse p xs = case firstRoot p xs of\n (Just (next@(r:_), (q,_))) -> Left r:recurse q next\n _ -> [Right $ length (coeffs p) - 1]\n\n-- Show the spectrum\nshowSpectrum (xs, y) = intercalate \", \" showMults ++ showMissing where\n showMults = map showMult (rle Nothing xs)\n -- Markdown notation for a root x repeated y times\n showMult (x,y) = show x ++ \"^\" ++ show y ++ \"^\"\n showMissing = if y == 0 then \"\" else \" \" ++ show y ++ \" other roots\"\n -- Run-length encode a list to a list containing (original entry, count)\n rle Nothing [] = []\n rle (Just x) [] = [x]\n rle Nothing (x:xs) = rle (Just (x, 1)) xs\n rle (Just (y, c)) (x:xs)\n | x == y = rle (Just (y, c+1)) xs\n | otherwise = (y, c):rle (Just (x, 1)) xs\n\n-- Characteristic, degree, remark\ndata CharGraphRow = CGR {\n cgrCharacteristic :: Int,\n cgrDegree :: Int,\n cgrRemark :: String\n }\n\ncharGraphTable = columns (\\(_, f) r -> f r) (\\(c, _) -> Headed c) [\n (\"Characteristic\", \\(CGR n d _) -> if d == 2 then show n else \"\"),\n (\"Order\", show . \\(CGR n d _) -> n^d),\n (\"Spectrum\", \\(CGR n d _) -> showSpectrum $ findRootsFrom [-1..35] $\n charpoly $ fmap fromIntegral $ edgesToAdjacency $\n irreducibleGraphNoEye d n),\n (\"Remark\", cgrRemark)\n ]\n\nmarkdown $ markdownTable charGraphTable [\n CGR 2 2 \"\",\n CGR 2 3 \"Mersenne\",\n CGR 2 4 \"\",\n CGR 2 5 \"Mersenne\",\n CGR 3 2 \"\",\n CGR 3 3 \"Pseudo-Mersenne?\",\n CGR 5 2 \"\",\n CGR 5 3 \"Prime power in spectrum\",\n CGR 7 2 \"\",\n CGR 7 3 \"Composite in spectrum\",\n CGR 11 2 \"Composite in spectrum\"\n ]\n```\n\n::: {.cell-output .cell-output-display .cell-output-markdown}\n| Characteristic | Order | Spectrum | Remark |\n|----------------|-------|----------|--------|\n| 2 | 4 | 0^1^ | |\n| | 8 | -1^1^, 1^1^ | Mersenne |\n| | 16 | -1^1^, 0^2^, 1^1^ | |\n| | 32 | -1^5^, 5^1^ | Mersenne |\n| 3 | 9 | -1^1^, 0^2^, 1^1^ | |\n| | 27 | -1^6^, 0^1^, 3^2^ | Pseudo-Mersenne? |\n| 5 | 25 | -1^5^, 0^5^, 1^2^, 3^1^ | |\n| | 125 | -1^37^, 0^3^, 9^2^, 19^1^ | Prime power in spectrum |\n| 7 | 49 | -1^15^, 0^6^, 1^2^, 3^2^, 7^1^ | |\n| | 343 | -1^104^, 0^5^, 1^2^, 5^2^, 11^2^, 35^2^ | Composite in spectrum |\n| 11 | 121 | -1^43^, 0^12^, 1^2^, 3^4^, 7^2^, 15^1^ | Composite in spectrum |\n\n:::\n:::\n\n\nIncredibly, all spectra shown are composed exclusively of integers, and thus,\n each of these graphs are integral graphs.\nMoreover, it does not appear that any integer sequences that one may try extracting from this table\n (for example, the multiplicity of -1) can be found in the\n [Online Encyclopedia of Integer Sequences](https://oeis.org/).\n\nFrom what I was able to tell, the following subgraphs were *also* integral over the range I tested:\n\n- the induced subgraph of vertices corresponding to non-primitives\n- the complement of the previous graph with respect to the whole graph\n- the induced subgraph of vertices corresponding only to irreducibles\n\nUnfortunately, proving any such relationship is out of the scope of this post (and my abilities).\n\n\nClosing\n-------\n\nThis concludes the first foray into using matrices as elements of prime power fields.\nIt is a subject which, using the tools of linear algebra, makes certain aspects of field theory\n more palatable and constructs some objects with fairly interesting properties.\n\nOne of the most intriguing parts to me is the sequence of polynomials generated by a companion matrix.\nThough I haven't proven it, I suspect that it suffices to study only the sequence generated\n by a primitive polynomial.\nIt seems to be possible to get the non-primitive sequences by looking at the subsequences\n where the indices are multiples of a factor of the length of the sequence.\nBut this means that the entire story about polynomials and finite fields can be foregone entirely,\n and the problem instead becomes one of number theory.\n\nThis post has [an addendum](./extra/) to it which discusses some additional notes about matrix roots and the\n Cayley-Hamilton theorem.\nThe [next post](../3/) will focus on an \"application\" of matrix roots to other areas of abstract algebra.\nDiagrams made with Geogebra and NetworkX (GraphViz).\n\n",
"supporting": [
"index_files"
],
"filters": [],
"includes": {}
}
}