From 91cfd2397add67e40e8e9b48b1243a7b84b7b2c1 Mon Sep 17 00:00:00 2001 From: queue-miscreant Date: Mon, 4 Aug 2025 05:33:30 -0500 Subject: [PATCH] haskellify finite-field.3 --- posts/finite-field/3/Previous.hs | 1 + posts/finite-field/3/index.qmd | 476 ++++++++++++++----------------- 2 files changed, 218 insertions(+), 259 deletions(-) create mode 120000 posts/finite-field/3/Previous.hs diff --git a/posts/finite-field/3/Previous.hs b/posts/finite-field/3/Previous.hs new file mode 120000 index 0000000..fbcffd3 --- /dev/null +++ b/posts/finite-field/3/Previous.hs @@ -0,0 +1 @@ +../2/Previous.hs \ No newline at end of file diff --git a/posts/finite-field/3/index.qmd b/posts/finite-field/3/index.qmd index 3fffaab..19fdf5a 100644 --- a/posts/finite-field/3/index.qmd +++ b/posts/finite-field/3/index.qmd @@ -5,9 +5,8 @@ description: | format: html: html-math-method: katex -jupyter: python3 date: "2024-02-03" -date-modified: "2025-07-16" +date-modified: "2025-08-04" categories: - algebra - finite field @@ -15,26 +14,64 @@ categories: --- +```{haskell} +--| echo: false + +:l Previous.hs + +import Data.Array (ixmap, bounds, (!)) +import Data.List (intercalate) +import IHaskell.Display (markdown) + +import Previous ( + Matrix(Mat, unMat), Polynomial(Poly, coeffs), + asPoly, evalPoly, synthDiv, + companion, zero, eye, determinant + ) + +-- 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 +laplaceDet :: (Num a, Eq a) => Matrix a -> a +laplaceDet (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 + +-- Convert matrix to LaTeX string +texifyMatrix' f mat = surround mat' where + mat' = intercalate " \\\\ " $ map (intercalate " & " . map f) $ + fromMatrix mat + surround = ("\\left( \\begin{matrix}" ++) . (++ "\\end{matrix} \\right)") + +texifyMatrix = texifyMatrix' show +``` + In the [previous post](../2), we focused on constructing finite fields using *n*×*n* matrices. These matrices came from from primitive polynomials of degree *n* over GF(*p*), @@ -59,12 +96,12 @@ $$ 0 & 0 \\ 0 & 0 \end{matrix}\right) - \quad + ~~ 1 \mapsto \left(\begin{matrix} 1 & 0 \\ 0 & 1 \end{matrix}\right) = I - \quad + ~~ \alpha \mapsto \left(\begin{matrix} 0 & 1 \\ 1 & 1 @@ -93,9 +130,9 @@ $$ In the images of *f*, the zero matrix has determinant 0 and all other elements have determinant 1. Therefore, the product of any two nonzero matrices always has determinant 1, and a nonzero determinant means the matrix is invertible. -This means that the non-zero elements of the field form their own group with respect to multiplication. +Per the definition of the field, the non-zero elements form a group with respect to multiplication. Here, they form a cyclic group of order 3, since *C*~*p*~^3^ = *I* mod 2. -This is also true using symbols, and we've already agreed that *α*^3^ = 1. +This is also true using symbols, since *α*^3^ = 1. ### Other Matrices @@ -109,47 +146,51 @@ $$ \#\{a_{ij} = 1\} & \det = 0 & \det = 1 \\ \hline 1 & + \scriptsize \left(\begin{matrix} 0 & 1 \\ 0 & 0 \end{matrix}\right) - \quad + ~~ \left(\begin{matrix} 1 & 0 \\ 0 & 0 \end{matrix}\right) - \quad + ~~ \left(\begin{matrix} 0 & 0 \\ 0 & 1 \end{matrix}\right) - \quad + ~~ \left(\begin{matrix} 0 & 0 \\ 1 & 0 \end{matrix}\right) \\ - 2 & + 2 & + \scriptsize \left(\begin{matrix} 1 & 1 \\ 0 & 0 \end{matrix}\right) - \quad + ~~ \left(\begin{matrix} 0 & 0 \\ 1 & 1 \end{matrix}\right) - \quad + ~~ \left(\begin{matrix} 0 & 1 \\ 0 & 1 \end{matrix}\right) - \quad + ~~ \left(\begin{matrix} 1 & 1 \\ 0 & 0 \end{matrix}\right) - & \textcolor{red}{ + & + \scriptsize + \textcolor{red}{ \left(\begin{matrix} 0 & 1 \\ 1 & 0 @@ -157,13 +198,14 @@ $$ } \\ 3 & & + \scriptsize \textcolor{red}{ \left(\begin{matrix} 1 & 1 \\ 0 & 1 \end{matrix}\right) } - \quad + ~~ \textcolor{red}{ \left(\begin{matrix} 1 & 0 \\ @@ -172,6 +214,7 @@ $$ } \\ 4 & + \scriptsize \left(\begin{matrix} 1 & 1 \\ 1 & 1 @@ -184,12 +227,15 @@ The matrices in the right column (in red) have determinant 1, which means they c This forms a larger group, of which our field's multiplication group is a subgroup. However, it is *not* commutative, since matrix multiplication is not commutative in general. -The group of all six matrices of determinant 1 is called the +The group of all six matrices with nonzero determinant is called the [*general linear group*](https://en.wikipedia.org/wiki/General_linear_group) - of degree 2 over GF(2), written GL(2, 2). + of degree 2 over GF(2), written[^1] GL(2, 2). We can sort the elements into classes by their order, or the number of times we have to multiply them before getting to the identity matrix (mod 2): +[^1]: Unfortunately, it's rather easy to confuse "GF" with "GL". + Remember that "F" is for "field", with the former standing for "Galois field". + $$ \begin{array}{} \text{Order 1} & \text{Order 2} & \text{Order 3} @@ -232,10 +278,10 @@ $$ If you've studied enough group theory, you know that there are two groups of order 6: the cyclic group of order 6, *C*~6~, and the symmetric group on three elements, *S*~3~. -Since the former group has order-6 elements, this group must be isomorphic to the latter. +Since the former group has order-6 elements, but none of these matrices are of order 6, + the matrix group must be isomorphic to the latter. Since the group is small, it's not too difficult to construct an isomorphism between the two. -Writing the elements of *S*~3~ in [cycle notation](), we have: - +Writing the elements of *S*~3~ in [cycle notation](/posts/permutations/1/), we have: $$ \begin{gather*} @@ -293,11 +339,9 @@ Haskell implementation of GL and SL for prime fields This implementation will be based on the `Matrix` type from the first post. Assume we have already defined matrix multiplication and addition. -```{.haskell} -data Matrix a = Mat { unMat :: Array (Int, Int) a } - --- instance Functor Matrix --- instance Num a => Num (Matrix a) +```{haskell} +import Data.Array (listArray, bounds, elems) +import Data.List (unfoldr) -- Partition a list into lists of length n reshape :: Int -> [a] -> [[a]] @@ -316,27 +360,29 @@ fromMatrix :: Matrix a -> [[a]] fromMatrix (Mat m) = let (_,(_,n)) = bounds m in reshape (n+1) $ elems m ``` -With helper functions out of the way, we can move on to generating all matrices (mod *n*) - before filtering for matrices with nonzero determinant (in the case of GL) and determinant 1 +With helper functions out of the way, we can move on to generating all matrices (mod *n*). +Then, we filter for matrices with nonzero determinant (in the case of GL) and determinant 1 (in the case of SL). -```{.haskell} -allMatrices :: Int -> Int -> Matrix Int +```{haskell} +import Control.Monad (replicateM) + -- All m x m matrices (mod n) -allMatrices m n = map toMatrix $ sequence $ replicate m vectors where +allMatrices :: Int -> Int -> [Matrix Int] +allMatrices m n = map toMatrix $ replicateM m vectors where -- Construct all vectors mod n using base-n expansions and padding vectors = [pad $ coeffs $ asPoly n l | l <- [1..n^m-1]] -- Pad xs to length m with zero - pad xs = xs ++ replicate 0 (m - length xs) + pad xs = xs ++ replicate (m - length xs) 0 -- All matrices, but paired with their determinants matsWithDets :: Int -> Int -> [(Matrix Int, Int)] matsWithDets m n = map (\x -> (x, determinant x `mod` n)) $ allMatrices m n -- Nonzero determinants -mGL m n = map fst $ filter (\(x,d) -> d /= 0) $ matsWithDets' m n +mGL m n = map fst $ filter (\(x,d) -> d /= 0) $ matsWithDets m n -- Determinant is 1 -mSL m n = map fst $ filter (\(x,d) -> d == 1) $ matsWithDets' m n +mSL m n = map fst $ filter (\(x,d) -> d == 1) $ matsWithDets m n ``` @@ -346,13 +392,14 @@ mSL m n = map fst $ filter (\(x,d) -> d == 1) $ matsWithDets' m n Another important matrix group is the [*projective general linear group*](https://en.wikipedia.org/wiki/Projective_linear_group), PGL(*n*, *K*). -In this group, two matrices are considered equal if one is a scalar multiple of the other. -Equivalently, the elements *are* these equivalence classes, and the product of two classes is - the set of all possible products of items from one class with items from the other. - +In this group, two matrices are considered equal if one is a scalar multiple of the other[^2]. Both this and the determinant 1 constraint can apply at the same time, forming the *projective special linear group*, PSL(*n*, *K*). +[^2]: Equivalently, the elements *are* these equivalence classes. + The product of two classes is the set of all possible products between the two classes, + which is another class. + For GF(2), all of these groups are the same, since the only nonzero determinant and scalar multiple is 1. Therefore, it's beneficial to contrast SL and PGL with another example. @@ -369,23 +416,27 @@ $$ \large \text{GL}(2, 5) \\ \underset{\det = 4}{ + \scriptsize \left(\begin{matrix} 0 & 1 \\ 1 & 1 \end{matrix} \right) }, \textcolor{red}{ \underset{\det = 1}{ + \scriptsize \left(\begin{matrix} 0 & 2 \\ 2 & 2 \end{matrix} \right) }}, \underset{\det = 2}{ + \scriptsize \left(\begin{matrix} 1 & 0 \\ 0 & 2 \end{matrix} \right) }, \underset{\det = 3}{ + \scriptsize \left(\begin{matrix} 2 & 0 \\ 0 & 4 @@ -398,6 +449,7 @@ $$ \large \text{PGL}(2,5) \\ \underset{\det = 1, ~4}{ + \scriptsize \textcolor{red}{\left\{ \left(\begin{matrix} 0 & 1 \\ @@ -412,6 +464,7 @@ $$ }} \\ \underset{\det = 2, ~ 3}{ + \scriptsize \left\{ \left(\begin{matrix} 1 & 0 \\ 0 & 2 @@ -432,11 +485,13 @@ $$ \large \text{SL}(2,5) \\ \textcolor{red}{ + \scriptsize \left(\begin{matrix} 0 & 2 \\ 2 & 2 \end{matrix} \right) }, + \scriptsize \left(\begin{matrix} 0 & 3 \\ 3 & 3 @@ -448,6 +503,7 @@ $$ \large \text{PSL}(2,5) \\ \textcolor{red}{ \left\{ + \scriptsize \left(\begin{matrix} 0 & 2 \\ 2 & 2 @@ -463,32 +519,30 @@ $$ \end{matrix} $$ -
- -Haskell implementation of PGL and PSL for prime fields - -PGL and PSL require special equality. -It's certainly possible to write a definition which makes the classes explicit, as its own new type. -We could then define equality on this type through `Eq`. -This is rather inefficient, though, so I'll choose to work with the representatives instead. +```{haskell} +--| code-fold: true +--| code-summary: "Haskell implementation of PGL and PSL for prime fields" -```{.haskell} import Data.List (nubBy) -scalarTimes :: Int -> Int -> Matrix Int -> Matrix Int +-- PGL and PSL require special equality. +-- It's certainly possible to write a definition which makes the classes explicit, as its own new type. +-- We could then define equality on this type through `Eq`. +-- This is rather inefficient, though, so I'll choose to work with the representatives instead. + -- Scalar-multiply a matrix (mod p) +scalarTimes :: Int -> Int -> Matrix Int -> Matrix Int scalarTimes n k = fmap ((`mod` n) . (*k)) -projEq :: Int -> Matrix Int -> Matrix Int -> Bool -- Construct all scalar multiples mod n, then check if ys is any of them. -- This is ludicrously inefficient, and only works for fields. +projEq :: Int -> Matrix Int -> Matrix Int -> Bool projEq n xs ys = ys `elem` [scalarTimes n k xs | k <- [1..n-1]] -- Strip out duplicates in GL and SL with projective equality mPGL m n = nubBy (projEq n) $ mGL m n mPSL m n = nubBy (projEq n) $ mSL m n ``` -
### Exceptional Isomorphisms @@ -526,10 +580,10 @@ Within the group, there are three kinds of elements: (or more precisely, 1/2 of a turn) about an edge - 5-cycles, such as *b* = (1 2 3 4 5) - This corresponds to a 72 degree rotation (1/5 of a turn) - around the center of a face + around a vertex - 3-cycles, such as *ab* = (2 4 5) - This corresponds to a 120 degree rotation (1/3 of a turn) - around a vertex + around the center of a face It happens to be the case that all elements of the group can be expressed as a product between *a* and *b* -- they generate the group. @@ -541,137 +595,82 @@ To create a correspondence with PSL(2, 5), we need to identify permutations with Obviously, the identity permutation goes to the identity matrix. Then, since *a* and *b* generate the group, we can search for two matrices which obey the same relations (under projective equality, since we're working in PSL). -One such correspondence is: -$$ -\begin{array}{} - \begin{gather*} - A = \left(\begin{matrix} - 1 & 1 \\ - 3 & 4 - \end{matrix} \right) - \qquad - A^2 = \left(\begin{matrix} - 4 & 0 \\ - 0 & 4 - \end{matrix}\right) = - 4 \left(\begin{matrix} - 1 & 0 \\ - 0 & 1 - \end{matrix}\right) - \qquad - \end{gather*} - \\ ~ \\ \hline \\ - \begin{gather*} - B = \left(\begin{matrix} - 0 & 2 \\ - 2 & 2 - \end{matrix} \right) - \qquad - B^2 = \left(\begin{matrix} - 4 & 4 \\ - 4 & 3 - \end{matrix}\right) - \qquad - B^3 = \left(\begin{matrix} - 3 & 1 \\ - 1 & 4 - \end{matrix}\right) - \\ - B^4 = \left(\begin{matrix} - 2 & 3 \\ - 3 & 0 - \end{matrix}\right) - \qquad - B^5 = \left(\begin{matrix} - 1 & 0 \\ - 0 & 1 - \end{matrix}\right) - \end{gather*} - \\ ~ \\ \hline \\ - \begin{gather*} - (AB) = \left(\begin{matrix} - 2 & 4 \\ - 3 & 4 - \end{matrix} \right) - \qquad - (AB)^2 = \left(\begin{matrix} - 1 & 4 \\ - 3 & 3 - \end{matrix}\right) - \qquad - (AB)^3 = \left(\begin{matrix} - 4 & 0 \\ - 0 & 4 - \end{matrix}\right) - \end{gather*} = - 4 \left(\begin{matrix} - 1 & 0 \\ - 0 & 1 - \end{matrix}\right) -\end{array} -$$ +Fortunately, we have a computer, so we can search for candidates rather quickly. +First, let's note a matrix *B* which is cyclic of order 5 to correspond with *b*: +```{haskell} +--| code-fold: true +--| code-summary: "Haskell implementation of finding candidates for B" -
- -Haskell implementation using B as a generator to find candidates for A - - -```{.haskell} -orderWith :: Eq a => (a -> a -> a) -> (a -> Bool) -> a -> Int -- Repeatedly apply f to p, until the predicate z -- (usually equality to some quantity) becomes True. -- Get the length of the resulting list +orderWith :: Eq a => (a -> a -> a) -> (a -> Bool) -> a -> Int orderWith f z p = (+1) $ length $ takeWhile (not . z) $ iterate (f p) p -- Order with respect to PSL(2, 5): using matrix multiplication (mod 5) -- and projective equality to the identity matrix -orderPSL25 = orderWith (\x -> fmap (`mod` 5) . (x |*|))) (projEq 5 $ eye 2) +orderPSL25 = orderWith (\x -> fmap (`mod` 5) . (x |*|)) (projEq 5 $ eye 2) --- Only order 2 elements of PSL(2, 5) -psl25_order2 = filter ((==2) . orderPSL25) $ mPSL 2 5 +-- Only order 5 elements of PSL(2, 5) +psl25_order5 = filter ((==5) . orderPSL25) $ mPSL 2 5 + +markdown $ ("$$B = " ++) $ (++ "... $$") $ intercalate " ~,~ " $ + take 5 $ map texifyMatrix psl25_order5 +``` + +Arbitrarily, let's pick the last entry on this list. +Now, we can search for order-2 elements in PSL(2, 5) whose product with *B* has order 3. +This matrix (*A*) matches exactly with *a* in *A*~5~. + +```{haskell} +--| code-fold: true +--| code-summary: "Haskell implementation using B as a generator to find candidates for A" -- Start with B as a generator psl25_gen_B = toMatrix [[0,2],[2,2]] --- Find an order 2 element whose product with `psl25_gen_B` has order 3 -psl25_gen_A_candidates = filter ((==3) . orderPSL25 . (psl25_gen_B |*|)) \ - psl25_order2 +-- Only order 2 elements of PSL(2, 5) +psl25_order2 = filter ((==2) . orderPSL25) $ mPSL 2 5 --- Candidate matrices: --- --- [1,1] --- [3,4] --- --- [1,3] --- [1,4] --- --- [2,0] --- [0,3] --- --- [2,0] --- [4,3] --- --- [2,4] --- [0,3] +-- Find an order 2 element whose product with `psl25_gen_B` has order 3 +psl25_gen_A_candidates = filter ((==3) . orderPSL25 . (psl25_gen_B |*|)) + psl25_order2 + +markdown $ ("$$A = " ++) $ (++ "$$") $ intercalate " ~,~ " $ + map texifyMatrix psl25_gen_A_candidates ``` -If you're unsatisfied with starting from *B*, realize that we could have filtered out - only the order 5 elements of PSL(2, 5) (`filter ((==5) . psl25Order) $ mPSL 2 5`), - and picked any element from this list to start. -
+Again, arbitrarily, we'll pick the last entry from this list. +Let's also peek at what the matrix *AB* looks like. +```{haskell} +--| code-fold: true + +psl25_gen_AB = (`mod` 5) <$> (psl25_gen_A_candidates !! 4) |*| psl25_gen_B + +markdown $ ("$$" ++) $ (++ "$$") $ intercalate " \\quad " [ + "(AB) = " ++ texifyMatrix psl25_gen_AB, + "(AB)^3 = " ++ texifyMatrix ((`mod` 5) <$> (psl25_gen_AB^3)) + ] +``` We now have a correspondence between three elements of *A*~5~ and PSL(2, 5). We can "run" both sets of the generators until we associate all elements to one another. This is most visually appealing to see as a Cayley graph: -![ - Cayley graph showing an isomorphism between A5 and PSL(2, 5).
- Order-2 elements are red, order-3 elements are green, and order-5 elements are blue.
- Purple arrows are order-5 generators, orange arrows are order-2 generators. -](./a5_psl25_cayley.png) +[ + ![ + Cayley graph showing an isomorphism between A5 and PSL(2, 5).
+ Order-2 elements are red, order-3 elements are green, and order-5 elements are blue. + Purple arrows are order-5 generators, orange arrows are order-2 generators[^4]. + ](./a5_psl25_cayley.png){.narrow} +](./a5_psl24_cayley.png) + +[^4]: Different generators appear to be used for *A* and *B* in the above image + due to some self-imposed turbulence when writing the original post. + Under projective equality, both are the same as our choices of *A* and *B*. PSL(2, 4) @@ -737,7 +736,7 @@ $$ Scalar multiplication by *α* multiplies the determinant by *α*^2^; by *α*^2^ multiplies the determinant by *α*^4^ = *α*. -Thus, SL(2, 4) is also PSL(2, 4), since no scalar multiple has determinant 1. +Thus, SL(2, 4) is also PSL(2, 4), since a scalar multiple has determinant 1. Let's start by looking at an order-5 matrix over PSL(2, 4). We'll call this matrix *B*' to correspond with our order-5 generator in PSL(2, 5). @@ -777,8 +776,8 @@ $$ We need to be able to do three things over GL(2, 4) on a computer: - multiply matrices over GF(4), -- compare those matrices, -- compute their determinant, and +- compute their determinant, +- visually distinguish between each of them, and - be able to systematically write down all of them It would then follow for us to repeat what we did with with SL(2, 5). @@ -826,7 +825,7 @@ $$ f(1) & f(1) \\ f(\alpha) & f(\alpha^2) \end{matrix} \right) = - f^*((\bar B')^2) + f^*((B')^2) \end{align*} \end{gather*} $$ @@ -877,14 +876,11 @@ All we need to do is find all possible 4-tuples of **0**, *I*, *C*~*p*~, and *C* then arrange each into a 2x2 matrix. Multiplication follows from the typical definition and the multiplicative identity is just *f*\*(*I*). +```{haskell} +--| code-fold: true +--| code-summary: "Haskell implementation of PSL(2, 4)" -
- -Haskell implementation of PSL(2, 4) - - -```{.haskell} -import Data.List (findIndex) +import Data.List (elemIndex) -- Matrices which obey the same relations as the elements of GF(4) zero_f4 = zero 2 @@ -896,7 +892,7 @@ alpha2_f4 = toMatrix [[1,1],[1,0]] field4 = [zero_f4, one_f4, alpha_f4, alpha2_f4] -- Convenient show function for these matrices -showF4 x = case findIndex (==x) field4 of +showF4 x = case elemIndex x field4 of Just 0 -> "0" Just 1 -> "1" Just 2 -> "α" @@ -909,101 +905,59 @@ psl_24_identity = toMatrix [[one_f4, zero_f4], [zero_f4, one_f4]] -- All possible matrices over GF(4) -- Create a list of 4-lists of elements from GF(4), then -- Shape them into 2x2 matrices -f4_matrices = map (toMatrix . reshape 2) $ sequence $ replicate 4 field4 +f4_matrices = map (toMatrix . reshape 2) $ replicateM 4 field4 -- Sieve out those which have a determinant of 1 in the field -mPSL24 = filter ((==one_f4) . (fmap (`mod` 2)) . laplaceDet) $ f4_matrices +mPSL24 = filter ((==one_f4) . fmap (`mod` 2) . laplaceDet) f4_matrices ``` -
Now that we can generate the group, we can finally repeat what we did with PSL(2, 5). All we have to do is filter out order-2 elements, then further filter for those which have an order-3 product with *B*'. -
- -Haskell implementation using *B*' as a generator to find candidates for *A*' - +```{haskell} +--| code-fold: true +--| code-summary: "Haskell implementation using *B*' as a generator to find candidates for *A*'" -```{.haskell} -- Order with respect to PSL(2, 4): using matrix multiplication (mod 2) -- and projective equality to the identity matrix -orderPSL24 = orderWith (\x -> fmap (fmap (`mod` 2)) . (x*))) (== psl_24_identity) +orderPSL24 = orderWith (\x -> fmap (fmap (`mod` 2)) . (x*)) (== psl_24_identity) -- Only order 2 elements of PSL(2, 4) -psl24_order2 = filter ((==2) . orderPSL24) $ mPSL24 +psl24_order2 = filter ((==2) . orderPSL24) mPSL24 -- Start with B as a generator psl24_gen_B = toMatrix [[zero_f4, alpha_f4], [alpha2_f4, alpha2_f4]] -- Find an order 2 element whose product with `psl24_gen_B` has order 3 -psl24_gen_A_candidates = filter ((==3) . orderPSL24 . (psl24_gen_B*)) +psl24_gen_A_candidates = filter ((==3) . orderPSL24 . (psl24_gen_B |*|)) psl24_order2 --- Candidate matrices: --- --- ["0","1"] --- ["1","0"] --- --- ["0","α^2"] --- ["α","0"] --- --- ["1","0"] --- ["1","1"] --- --- ["1","α^2"] --- ["0","1"] --- --- ["α","1"] --- ["α","α"] +markdown $ ("$$ A' = " ++) $ (++ "$$") $ intercalate " ~,~ " $ + map (texifyMatrix' showF4) psl24_gen_A_candidates ``` -
-Finally, we can decide on an *A*', the order-2 generator with the properties we wanted. +We'll pick the second entry as our choice of *A*'. +We note that the product *A'B'*, does indeed have order 3. -$$ -\begin{array}{} - \begin{gather*} - A' = \left(\begin{matrix} - 0 & \alpha^2 \\ - \alpha & 0 - \end{matrix} \right) - \qquad - (A')^2 = \left(\begin{matrix} - 1 & 0 \\ - 0 & 1 - \end{matrix}\right) - \end{gather*} - \\ \\ \hline \\ - \begin{gather*} - A'B' = - \left(\begin{matrix} - \alpha & \alpha \\ - 0 & \alpha^2 - \end{matrix} \right) - \qquad - (A'B')^2 = - \left(\begin{matrix} - \alpha^2 & \alpha \\ - 0 & \alpha - \end{matrix} \right) - \qquad - (A'B')^3 = - \left(\begin{matrix} - 1 & 0 \\ - 0 & 1 - \end{matrix} \right) - \qquad - \end{gather*} -\end{array} -$$ +```{haskell} +--| code-fold: true -Then, we can arrange them on a Cayley graph in the same way as PSL(2, 5): +psl24_gen_AB = fmap (`mod` 2) <$> (psl24_gen_A_candidates !! 1) |*| psl24_gen_B +markdown $ ("$$" ++) $ (++ "$$") $ intercalate " \\quad " [ + "(A'B') = " ++ texifyMatrix' showF4 psl24_gen_AB, + "(A'B')^3 = " ++ texifyMatrix' showF4 (fmap (`mod` 2) <$> (psl24_gen_AB^3)) + ] +``` -![ - Cayley graph showing an isomorphism between *A*^5^ and PSL(2, 4).
- Colors indicate the same thing as in the previous diagram. +Finally, we can arrange these matrices on a Cayley graph in the same way as PSL(2, 5): + +[ + ![ + Cayley graph showing an isomorphism between *A*~5~ and PSL(2, 4).
+ Colors indicate the same thing as in the previous diagram. + ](./a5_psl24_cayley.png){.narrow} ](./a5_psl24_cayley.png) @@ -1011,16 +965,20 @@ Closing ------- This post addresses my original goal in implementing finite fields, - namely computationally finding an explicit map between *A*^5^ and PSL(2, 4). + namely computationally finding an explicit map between *A*~5~ and PSL(2, 4). I believe the results are a little more satisfying than attempting to wrap your head around group-theoretic proofs. -That's not to discount the power and astounding amount of work that goes into the latter method. +That's not to discount the power and incredible logic that goes into the latter method. It does tend to leave things rather opaque, however. If you'd prefer a more interactive diagram showing the above isomorphisms, I've gone to the liberty of creating a hoverable SVG: -![](./a5_psl24_psl25_isomorphism.svg) +[ + ![ + Click to open interactive version + ](./a5_psl24_psl25_isomorphism.svg){.narrow} +](./a5_psl24_psl25_isomorphism.svg) This post slightly diverts our course from the previous one's focus on fields. The [next one](../4) will focus on more results regarding the treatment of layered matrices.