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[^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))
+ ]
+```
-{.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:
-
+[
+ {.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.