packages feed

HaskellForMaths 0.1.9 → 0.2.0

raw patch · 10 files changed

+668/−22 lines, 10 filesdep +QuickCheckdep +arraydep +randomPVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, array, random

API changes (from Hackage documentation)

+ Math.Algebra.Group.RandomSchreierSims: initProdRepl :: (Ord a, Show a) => [Permutation a] -> IO (Int, IOArray Int (Permutation a))
+ Math.Algebra.Group.RandomSchreierSims: isMemberSGS :: (Ord a, Show a) => [Permutation a] -> Permutation a -> Bool
+ Math.Algebra.Group.RandomSchreierSims: nextProdRepl :: (Ord a, Show a) => (Int, IOArray Int (Permutation a)) -> IO (Maybe (Permutation a))
+ Math.Algebra.Group.RandomSchreierSims: sgs :: (Ord a, Show a) => [Permutation a] -> [Permutation a]
+ Math.Algebra.Group.SchreierSims: sgs :: (Ord a, Show a) => [Permutation a] -> [Permutation a]
+ Math.Projects.MiniquaternionGeometry: F9 :: F3 -> F3 -> F9
+ Math.Projects.MiniquaternionGeometry: J9 :: F9 -> J9
+ Math.Projects.MiniquaternionGeometry: data F9
+ Math.Projects.MiniquaternionGeometry: data J9
+ Math.Projects.MiniquaternionGeometry: instance Arbitrary F9
+ Math.Projects.MiniquaternionGeometry: instance Arbitrary J9
+ Math.Projects.MiniquaternionGeometry: instance Eq F9
+ Math.Projects.MiniquaternionGeometry: instance Eq J9
+ Math.Projects.MiniquaternionGeometry: instance FiniteField F9
+ Math.Projects.MiniquaternionGeometry: instance FiniteField J9
+ Math.Projects.MiniquaternionGeometry: instance Fractional F9
+ Math.Projects.MiniquaternionGeometry: instance Fractional J9
+ Math.Projects.MiniquaternionGeometry: instance Num F9
+ Math.Projects.MiniquaternionGeometry: instance Num J9
+ Math.Projects.MiniquaternionGeometry: instance Ord F9
+ Math.Projects.MiniquaternionGeometry: instance Ord J9
+ Math.Projects.MiniquaternionGeometry: instance Show F9
+ Math.Projects.MiniquaternionGeometry: instance Show J9

Files

HaskellForMaths.cabal view
@@ -1,5 +1,5 @@    Name:                HaskellForMaths
-   Version:             0.1.9
+   Version:             0.2.0
    Category:            Math
    Description:         A library of maths code in the areas of combinatorics, group theory, commutative algebra, and non-commutative algebra. The library is mainly intended for educational purposes, but does have efficient implementations of several fundamental algorithms.
    Synopsis:            Combinatorics, group theory, commutative algebra, non-commutative algebra
@@ -22,19 +22,21 @@         Math/Test/TestAll.hs
 
    Library
-     Build-Depends:     base >=3 && < 4, containers
+     Build-Depends:     base >=3 && < 4, containers, array, random, QuickCheck
      Exposed-modules:
         Math.Algebra.LinearAlgebra,
         Math.Algebra.Commutative.Monomial,
         Math.Algebra.Commutative.MPoly, Math.Algebra.Commutative.GBasis,
         Math.Algebra.Field.Base, Math.Algebra.Field.Extension,
-        Math.Algebra.Group.PermutationGroup, Math.Algebra.Group.SchreierSims, Math.Algebra.Group.StringRewriting,
+        Math.Algebra.Group.PermutationGroup, Math.Algebra.Group.SchreierSims,
+        Math.Algebra.Group.RandomSchreierSims, Math.Algebra.Group.StringRewriting,
         Math.Algebra.NonCommutative.NCPoly, Math.Algebra.NonCommutative.GSBasis, Math.Algebra.NonCommutative.TensorAlgebra,
         Math.Combinatorics.Graph, Math.Combinatorics.GraphAuts, Math.Combinatorics.StronglyRegularGraph,
         Math.Combinatorics.Design, Math.Combinatorics.FiniteGeometry, Math.Combinatorics.Hypergraph,
+        Math.Combinatorics.LatinSquares,
         Math.Common.IntegerAsType, Math.Common.ListSet,
         Math.Projects.RootSystem,
-        Math.Projects.Rubik,
+        Math.Projects.Rubik, Math.Projects.MiniquaternionGeometry,
         Math.Projects.ChevalleyGroup.Classical, Math.Projects.ChevalleyGroup.Exceptional,
         Math.Projects.KnotTheory.Braid,
         Math.Projects.KnotTheory.LaurentMPoly, Math.Projects.KnotTheory.TemperleyLieb, Math.Projects.KnotTheory.IwahoriHecke
+ Math/Algebra/Group/RandomSchreierSims.hs view
@@ -0,0 +1,135 @@+++module Math.Algebra.Group.RandomSchreierSims where++import System.Random+import Data.List as L+import qualified Data.Map as M+import Data.Maybe++import Control.Monad+import Data.Array.MArray+import Data.Array.IO+import System.IO.Unsafe++import Math.Common.ListSet (toListSet)+import Math.Algebra.Group.PermutationGroup+import Math.Algebra.Group.SchreierSims (sift, cosetRepsGx, ss')++{-+-- all the imports below used only for testing+import Math.Algebra.Field.Base+import Math.Algebra.Field.Extension++import Math.Projects.ChevalleyGroup.Classical+import Math.Projects.ChevalleyGroup.Exceptional+-}+++testProdRepl = do (r,xs) <- initProdRepl $ _D 10+                  hs <- replicateM 20 $ nextProdRepl (r,xs)+                  mapM_ print hs++-- Holt p69-71+-- Product replacement algorithm for generating uniformly distributed random elts of a black box group++initProdRepl :: (Ord a, Show a) => [Permutation a] -> IO (Int, IOArray Int (Permutation a))+initProdRepl gs =+    let n = length gs+        r = max 10 n+        xs = (1:) $ take r $ concat $ repeat gs +    in do xs' <- newListArray (0,r) xs+          replicateM_ 60 $ nextProdRepl (r,xs') -- perform initial mixing+          return (r,xs')++nextProdRepl :: (Ord a, Show a) => (Int, IOArray Int (Permutation a)) -> IO (Maybe (Permutation a))+nextProdRepl (r,xs) =+    do s <- randomRIO (1,r)+       t <- randomRIO (1,r)+       u <- randomRIO (0,3 :: Int)+       out <- updateArray xs s t u+       return out++updateArray xs s t u =+    let (swap,invert) = quotRem u 2 in+    if s == t+    then return Nothing+    else do+        x_0 <- readArray xs 0+        x_s <- readArray xs s+        x_t <- readArray xs t+        let x_s' = mult (swap,invert) x_s x_t+            x_0' = mult (swap,0) x_0 x_s'+        writeArray xs 0 x_0'+        writeArray xs s x_s'+        return (Just x_0')+    where mult (swap,invert) a b = case (swap,invert) of+                                   (0,0) -> a * b+                                   (0,1) -> a * b^-1+                                   (1,0) -> b * a+                                   (1,1) -> b^-1 * a+++-- Holt p97-8+-- Random Schreier-Sims algorithm, for finding strong generating set of permutation group++-- It's possible that the following code can be improved by introducing levels only as we need them?++-- |Given generators for a permutation group, return a strong generating set.+-- The result is calculated using random Schreier-Sims algorithm, so has a small (\<10^-6) chance of being incomplete.+-- The sgs is relative to the base implied by the Ord instance.+sgs :: (Ord a, Show a) => [Permutation a] -> [Permutation a]+sgs gs = toListSet $ concatMap snd $ rss gs++rss gs = unsafePerformIO $+    do (r,xs) <- initProdRepl gs+       rss' (r,xs) (initLevels gs) 0++rss' (r,xs) levels i+    | i == 25 = return levels -- stop if we've had 25 successful sifts in a row+    | otherwise = do g <- nextProdRepl (r,xs)+                     let (changed,levels') = updateLevels levels g+                     rss' (r,xs) levels' (if changed then 0 else i+1)+-- if we currently have an sgs for a subgroup of the group, then it must have index >= 2+-- so the chance of a random elt sifting to identity is <= 1/2++initLevels gs = [((b,M.singleton b 1),[]) | b <- bs]+    where bs = toListSet $ concatMap supp gs++updateLevels levels Nothing = (False,levels) -- not strictly correct to increment count on a Nothing+updateLevels levels (Just g) =+    case sift (map fst levels) g of+    Nothing -> (False, levels)+    -- Just 1 -> error "Just 1"+    Just g' -> (True, updateLevels' [] levels g' (minsupp g'))++updateLevels' ls (r@((b,t),s):rs) h b' =+    if b == b'+    then reverse ls ++ ((b, cosetRepsGx (h:s) b), h:s) : rs+    else updateLevels' (r:ls) rs h b'+-- updateLevels' ls [] h b' = error $ "updateLevels: " ++ show (ls,[],h,b')++-- used the following in debugging+-- orderLevels levels = product $ [if M.null t then 1 else toInteger (M.size t) | ((b,t),s) <- levels]+++-- recover the base tranversals from the sgs. gs must be an sgs+baseTransversalsSGS gs = [let hs = [h | h <- gs, b <= minsupp h] in (b, cosetRepsGx hs b) | b <- bs]+    where bs = toListSet $ concatMap supp gs++-- |Given a strong generating set gs, isMemberSGS gs is a membership test for the group+isMemberSGS :: (Ord a, Show a) => [Permutation a] -> Permutation a -> Bool+isMemberSGS gs h = let bts = baseTransversalsSGS gs in isNothing $ sift bts h+++{-+-- Alternative where we carry on with Schreier-Sims when we finish Random Schreier-Sims, just to make sure+-- !! Unfortunately, doesn't appear to work - perhaps ss' doesn't like finding empty levels+sgs2 gs = toListSet $ concatMap snd $ rss2 gs++rss2 gs = unsafePerformIO $+    do (r,xs) <- initProdRepl gs+       levels <- rss' (r,xs) (initLevels gs) 0+       return $ ss' bs (reverse levels) []+    where bs = toListSet $ concatMap supp gs+-}
Math/Algebra/Group/SchreierSims.hs view
@@ -41,10 +41,12 @@ -- SCHREIER-SIMS ALGORITHM
 
 sift bts g = sift' bts g where
-    sift' [] g = if g == 1 then Nothing else Just g
+    sift' _ 1 = Nothing
     sift' ((b,t):bts) g = case M.lookup (b .^ g) t of
                           Nothing -> Just g
+                          -- Nothing -> sift' bts g -- if we allow empty levels
                           Just h -> sift' bts (g * inverse h)
+    sift' [] g = if g == 1 then Nothing else Just g
 
 findBase gs = minimum $ concatMap supp gs
 {-
@@ -72,7 +74,9 @@ -}
 
 
--- strong generating set, with implied base from the Ord instance
+-- |Given generators for a permutation group, return a strong generating set.
+-- The result is calculated using Schreier-Sims algorithm, and is relative to the base implied by the Ord instance
+sgs :: (Ord a, Show a) => [Permutation a] -> [Permutation a]
 sgs gs = toListSet $ concatMap snd $ ss bs gs
     where bs = toListSet $ concatMap supp gs
 
Math/Combinatorics/Design.hs view
@@ -118,7 +118,7 @@ -- the projective plane PG(2,Fq) - a square 2-(q^2+q+1,q+1,1) design
 pg2 fq = design (points, lines) where
     points = ptsPG 2 fq
-    lines = map line points
+    lines = L.sort $ map line points
     line u = [v | v <- points, u <.> v == 0]
     u <.> v = sum (zipWith (*) u v)
 -- Remember that the points and lines of PG(2,Fp) are really the lines and planes of AG(3,Fp).
+ Math/Combinatorics/LatinSquares.hs view
@@ -0,0 +1,122 @@+++module Math.Combinatorics.LatinSquares where++import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.Map as M++-- import Math.Combinatorics.FiniteGeometry+import Math.Combinatorics.Design+import Math.Algebra.Field.Base+import Math.Algebra.Field.Extension+import Math.Algebra.LinearAlgebra (fMatrix')+import Math.Combinatorics.Graph+import Math.Combinatorics.GraphAuts+import Math.Combinatorics.StronglyRegularGraph+++-- LATIN SQUARES++findLatinSqs xs = findLatinSqs' 1 [xs] where+    n = length xs+    findLatinSqs' i rows+        | i == n    = [reverse rows]+        | otherwise = concat [findLatinSqs' (i+1) (row:rows)+                             | row <- findRows (L.transpose rows) [] xs]+    findRows (col:cols) ls rs = concat [findRows cols (r:ls) (L.delete r rs)+                                    | r <- rs, r `notElem` col]+    findRows [] ls _ = [reverse ls]++isLatinSq rows = all isOneOfEach rows && all isOneOfEach cols where+    cols = L.transpose rows++isOneOfEach xs = length xs == S.size (S.fromList xs)+++-- The incidence graph of a latin square+-- It is distance-regular+-- Godsil & Royle p69+incidenceGraphLS l = graph (vs,es) where+    n = length l -- the order of the latin square+    vs = [ (i, j, l ! (i,j)) | i <- [1..n], j <- [1..n] ]+    es = [ [v1,v2] | [v1@(i,j,lij), v2@(i',j',lij')] <- combinationsOf 2 vs, i == i' || j == j' || lij == lij' ]+    m ! (i,j) = m !! (i-1) !! (j-1)++incidenceGraphLS' l = graph (vs,es) where+    n = length l -- the order of the latin square+    vs = [ (i, j) | i <- [1..n], j <- [1..n] ]+    es = [ [v1,v2] | [v1@(i,j), v2@(i',j')] <- combinationsOf 2 vs, i == i' || j == j' || l' M.! (i,j) == l' M.! (i',j') ]+    l' = M.fromList [ ( (i,j), l !! (i-1) !! (j-1) ) | i <- [1..n], j <- [1..n] ]+-- vertices are grid positions+-- adjacent if they're in the same row, same column, or have the same entry+++-- ORTHOGONAL AND MUTUALLY ORTHOGONAL LATINS SQUARES++isOrthogonal greeks latins = isOneOfEach pairs+    where pairs = zip (concat greeks) (concat latins)++findMOLS k lsqs = findMOLS' k [] lsqs where+    findMOLS' 0 ls _ = [reverse ls]+    findMOLS' i ls (r:rs) =+        if all (isOrthogonal r) ls+        then findMOLS' (i-1) (r:ls) rs ++ findMOLS' i ls rs+        else findMOLS' i ls rs+    findMOLS' _ _ [] = []++isMOLS (greek:latins) = all (isOrthogonal greek) latins && isMOLS latins+isMOLS [] = True++-- MOLS from a projective plane+fromProjectivePlane (D xs bs) = map toLS parallelClasses where+    k = [x | [0,1,x] <- xs] -- the field we're working over+    n = length k            -- the order of the projective plane+    parallelClasses = drop 2 $ L.groupBy (\l1 l2 -> head l1 == head l2) bs+    -- The classes of parallel lines+    -- Each line has its ideal point at its head+    -- The first two classes have [0,0,1] and [0,1,0] as ideal points, and hence consist of horizontal and vertical lines+    toLS ls = let grid = M.fromList [ ((x,y),i) | (i, [0,1,mu]:ps) <- zip [1..] ls, [1,x,y] <- ps]+              in fMatrix' n (\i j -> grid M.! (k !! i, k !! j))+++-- ORTHOGONAL ARRAYS+-- Godsil & Royle p224++isOA (k,n) rows =+    length rows == k &&+    all ( (== n^2) . length ) rows &&+    all isOneOfEach [zip ri rj | [ri,rj] <- combinationsOf 2 rows ]++-- An OA(3,n) from a latin square+fromLS l =+    [ concat [replicate n i | i <- [1..n] ] -- row numbers+    , concat (replicate n [1..n])           -- column numbers+    , concat l                              -- entries+    ]+    where n = length l -- the order of the latin square++fromMOLS mols =+    (concat [replicate n i | i <- [1..n] ]) : -- row numbers+    (concat (replicate n [1..n]) ) :          -- column numbers+    map concat mols                           -- entries for each lsq+    where n = length $ head mols -- the order of the latin squares++-- The graph defined by an OA(k,n)+-- It is strongly regular with parameters ( n^2, (n-1)k, n-2+(k-1)(k-2), k(k-1) )+-- Godsil & Royle p225+graphOA rows = graph (vs,es) where+    vs = L.transpose rows -- the vertices are the columns of the OA+    es = [ [v1,v2] | [v1,v2] <- combinationsOf 2 vs, or (zipWith (==) v1 v2) ]+    -- two vertices are adjacent if they agree in any position++-- Expected SRG parameters+srgParamsOA (k,n) =  Just ( n^2, (n-1)*k, n-2+(k-1)*(k-2), k*(k-1) )++-- eg srgParams (4,4) == srgParams $ graphOA $ init $ fromMOLS $ fromProjectivePlane $ pg2 f4+++-- Todo:+-- Would like a way to find out to what extent two sets of MOLS are really the same,+-- eg can one be obtained from the other by row or column reordering (with renumbering)+-- This might provide a proof of the distinctness of phi, omega, omegaD, psi
Math/Combinatorics/StronglyRegularGraph.hs view
@@ -26,18 +26,22 @@ -- STRONGLY REGULAR GRAPHS
 
 -- strongly regular graphs
-srgParams g =
-    let vs = vertices g
-        n = length vs
-        es = edges g
-        es' = combinationsOf 2 vs \\ es -- the non-edges
-        k:ks = map (valency g) vs
-        lambda:ls = map (length . commonNbrs) es  -- common neighbours of adjacent vertices
-        mu:ms = map (length . commonNbrs) es' -- common neighbours of non-adjacent vertices
-        commonNbrs [v1,v2] = nbrs g v1 `intersect` nbrs g v2
-    in if all (==k) ks && all (==lambda) ls && all (==mu) ms
-       then Just (n,k,lambda,mu)
-       else Nothing
+srgParams g
+    | null es = error "srgParams: not defined for null graph"
+    | null es' = error "srgParams: not defined for complete graph"
+    | otherwise =
+        if all (==k) ks && all (==lambda) ls && all (==mu) ms
+        then Just (n,k,lambda,mu)
+        else Nothing
+    where vs = vertices g
+          n = length vs
+          es = edges g
+          es' = combinationsOf 2 vs \\ es -- the non-edges
+          k:ks = map (valency g) vs
+          lambda:ls = map (length . commonNbrs) es  -- common neighbours of adjacent vertices
+          mu:ms = map (length . commonNbrs) es' -- common neighbours of non-adjacent vertices
+          commonNbrs [v1,v2] = (nbrs_g M.! v1) `intersect` (nbrs_g M.! v2)
+          nbrs_g = M.fromList [ (v, nbrs g v) | v <- vs ]
 
 isSRG g = isJust $ srgParams g
 
Math/Projects/ChevalleyGroup/Classical.hs view
@@ -21,6 +21,7 @@ -- LINEAR GROUPS
 
 -- SL(n,Fq) is generated by elementary transvections
+-- sl :: FiniteField k => Int -> k -> [[[k]]]
 sl n fq = [elemTransvection n (r,c) l | r <- [1..n], c <- [1..n], r /= c, l <- fq']
     where fq' = basisFq undefined -- tail fq
     -- Carter p68 - x_r(t1) x_r(t2) == x_r(t1+t2) - this is true in general, not just in this case
+ Math/Projects/MiniquaternionGeometry.hs view
@@ -0,0 +1,372 @@+++module Math.Projects.MiniquaternionGeometry where++import qualified Data.List as L++import Math.Common.ListSet as LS++import Math.Algebra.Field.Base+import Math.Combinatorics.FiniteGeometry (pnf, ispnf, orderPGL)+import Math.Combinatorics.Graph (combinationsOf)+import Math.Combinatorics.GraphAuts+import Math.Algebra.Group.PermutationGroup hiding (order)+import qualified Math.Algebra.Group.SchreierSims as SS+import Math.Algebra.Group.RandomSchreierSims+import Math.Combinatorics.Design as D+import Math.Algebra.LinearAlgebra -- ( (<.>), (<+>) )++import Math.Projects.ChevalleyGroup.Classical++import Test.QuickCheck++++-- Sources:+-- Miniquaternion Geometry, Room & Kirkpatrick+-- Survey of Non-Desarguesian Planes, Charles Weibel+++-- F9, defined by adding sqrt of -1 to F3. (The Conway poly for F9 is not so convenient for us here)+data F9 = F9 F3 F3 deriving (Eq,Ord)++instance Show F9 where+    show (F9 0 0) = "0"+    show (F9 0 1) = "e"+    show (F9 0 2) = "-e"+    show (F9 1 0) = "1"+    show (F9 1 1) = "1+e"+    show (F9 1 2) = "1-e"+    show (F9 2 0) = "-1"+    show (F9 2 1) = "-1+e"+    show (F9 2 2) = "-1-e"++e = F9 0 1 -- sqrt of -1++instance Num F9 where+    F9 a1 b1 + F9 a2 b2 = F9 (a1+a2) (b1+b2)+    F9 a1 b1 * F9 a2 b2 = F9 (a1*a2-b1*b2) (a1*b2+a2*b1)+    negate (F9 a b) = F9 (negate a) (negate b)+    fromInteger n = F9 (fromInteger n) 0++f9 = [F9 a b | a <- f3, b <- f3]++w = 1-e -- a primitive element - generates the multiplicative group++conj (F9 a b) = F9 a (-b)+-- This is just the Frobenius aut x -> x^3++norm (F9 a b) = a^2 + b^2+-- == x * conj x++instance Fractional F9 where+    recip x@(F9 a b) = F9 (a/n) (-b/n) where n = norm x++instance FiniteField F9 where+    basisFq _ = [1,e]+++-- J9, or Q, defined by modifying the multiplication in F9+data J9 = J9 F9 deriving (Eq,Ord)++instance Show J9 where+    show (J9 (F9 0 0)) = "0"+    show (J9 (F9 0 1)) = "-j"+    show (J9 (F9 0 2)) = "j"+    show (J9 (F9 1 0)) = "1"+    show (J9 (F9 1 1)) = "-k"+    show (J9 (F9 1 2)) = "i"+    show (J9 (F9 2 0)) = "-1"+    show (J9 (F9 2 1)) = "-i"+    show (J9 (F9 2 2)) = "k"++squaresF9 = [1,w^2,w^4,w^6] -- and 0, but not needed here++instance Num J9 where+    J9 x + J9 y = J9 (x+y)+    0 * _ = 0+    _ * 0 = 0+    J9 x * J9 y =+        if y `elem` squaresF9+        then J9 (x*y)+        else J9 (conj x * y)+    negate (J9 x) = J9 (negate x)+    fromInteger n = J9 (fromInteger n)++i = J9 w+j = J9 (w^6) -- == i-1+k = J9 (w^7) -- == i+1++j9 = [J9 x | x <- f9]+++-- the aut of J9 that sends i to x+autJ9 x = fromPairs [ (a+b*i, a+b*x) | a <- [0,1,-1], b <- [1,-1] ]++autA = autJ9 (-i) -- sends i -> -i+autB = autJ9 (-k) -- sends j -> -j+autC = autJ9 (-j) -- sends k -> -k++autsJ9 = [autA, autC]+-- these two auts generate the group, which is isomorphic to S3+-- indeed, the auts permute the pairs {i,-i}, {j,-j}, {k,-k}+++conj' (J9 x) = J9 (conj x)+-- Note that conj' x == x .^ autB+++isAut k sigma = and [sigma x + sigma y == sigma (x+y) | x <- k, y <- k]+             && and [sigma x * sigma y == sigma (x*y) | x <- k, y <- k]+++isReal x = x `elem` [0,1,-1]+isComplex = not . isReal++instance Fractional J9 where+    recip 0 = error "J9.recip: 0"+    recip x | isReal x  = x+            | otherwise = -x++instance FiniteField J9 where+    basisFq _ = [1,i,j,k]+    eltsFq _ = j9+++-- Near fields++prop_NearField (a,b,c) =+    a+(b+c) == (a+b)+c   &&  -- addition is associative+    a+b == b+a           &&  -- addition is commutative+    a+0 == a             &&  -- additive identity+    a+(-a) == 0          &&  -- additive inverse+    a*(b*c) == (a*b)*c   &&  -- multiplication is associative+    a*1 == a && 1*a == a &&  -- multiplicative identity+    (a+b)*c == a*c + b*c &&  -- right-distributivity+    a*0 == 0++instance Arbitrary F9 where+    arbitrary = do x <- arbitrary :: Gen Int+                   return (f9 !! (x `mod` 9))+    coarbitrary = undefined -- !! only required if we want to test functions over the type++instance Arbitrary J9 where+    arbitrary = do x <- arbitrary :: Gen Int+                   return (j9 !! (x `mod` 9))+    coarbitrary = undefined -- !! only required if we want to test functions over the type++prop_NearFieldF9 (a,b,c) = prop_NearField (a,b,c) where+    types = (a,b,c) :: (F9,F9,F9)++prop_NearFieldJ9 (a,b,c) = prop_NearField (a,b,c) where+    types = (a,b,c) :: (J9,J9,J9)++++-- PROJECTIVE PLANES++ptsPG2 r =  [ [0,0,1] ] ++ [ [0,1,x] | x <- r ] ++ [ [1,x,y] | x <- r, y <- r ]+-- if r is sorted, then so is the result++orthogonalLinesPG2 xs = L.sort [ [x | x <- xs, u <.> x == 0] | u <- xs ]++rightLinesPG2 r =+    [ [0,0,1] : [ [0,1,x] | x <- r] ] ++ -- line at infinity+    [ [0,0,1] : [ [1,x,y] | y <- r] | x <- r ] ++ -- vertical lines+    [ [0,1,a] : [ [1,x,y] | x <- r, y <- [x*a+b] ] | a <- r, b <- r ] -- slope multiplies on the right+-- if r is sorted, then so is the result, and each line in the result++leftLinesPG2 r =+    [ [0,0,1] : [ [0,1,x] | x <- r] ] ++ -- line at infinity+    [ [0,0,1] : [ [1,x,y] | y <- r] | x <- r ] ++ -- vertical lines+    [ [0,1,a] : [ [1,x,y] | x <- r, y <- [a*x+b] ] | a <- r, b <- r ] -- slope multiplies on the left+++-- Projective plane PG2(F9)+phi = design (xs,bs) where+    xs = ptsPG2 f9+    bs = orthogonalLinesPG2 xs -- L.sort [ [x | x <- xs, u <.> x == 0] | u <- xs ]++-- Then the collineations of phi consist of projective transformations,+-- together with a conjugacy collineation induced by the Frobenius aut++-- alternative construction of PG2(F9) - gives same result+phi' = design (xs,bs) where+    xs = ptsPG2 f9+    bs = rightLinesPG2 f9+++collineationsPhi = l 3 f9 ++ [fieldAut] where+    D xs bs = phi+    fieldAut = fromPairs [ (x , map conj x) | x <- xs ]+-- in general, this would be PSigmaL(n,Fq), whereas we want PGammaL(n,Fq). However, for F9 they coincide.+-- order 84913920+++liftToGraph (D xs bs) g = fromPairs $ [(Left x, Left (x .^ g)) | x <- xs] ++ [(Right b, Right (b -^ g)) | b <- bs]++++-- This construction appears to produce a projective plane+-- (However, Room & Kirkpatrick point out that it's not really well-defined+-- - if we had chosen different quasi-homogeneous coords, we would have got different results)+-- However, it's not the same as either omega or omegaD below+omega0 = design (xs,bs) where+    xs = ptsPG2 j9+    bs = orthogonalLinesPG2 xs -- L.sort [ [x | x <- xs, u <.> x == 0] | u <- xs ]+++-- Room & Kirkpatrick, p103+omega = design (xs,bs) where+    xs = ptsPG2 j9+    bs = rightLinesPG2 j9++-- another construction that produces same result (but slower)+omega2 = design (xs,bs) where+    xs = ptsPG2 j9+    bs =  [ l | [p,q] <- combinationsOf 2 xs, l <- [line p q], [p,q] == take 2 l]+    line p q = toListSet $ filter ispnf [(a *> p) <+> (b *> q) | a <- j9, b <- j9]+++-- Room & Kirkpatrick, p107, p114+collineationsOmega =+    [r]+ ++ [s rho sigma | rho <- j9 \\ [0], sigma <- j9 \\ [0], rho == 1 || sigma == 1]+ ++ [t delta epsilon | delta <- j9, epsilon <- j9, delta * epsilon == 0] -- for generators sufficient to have only one non-zero+ ++ [u]+ ++ [a lambda | lambda <- autsJ9] where+    D xs bs = omega+    fromMatrix m = fromPairs [ (x, pnf (x <*>> m)) | x <- xs]+    r = fromMatrix [[1,0,0],[0,0,1],[0,1,0]] -- reflect in the line x = y in the affine subplane+    s rho sigma = fromPairs $ [([1,x,y], [1,x*rho,y*sigma]) | x <- j9, y <- j9]+                           ++ [([0,1,mu],[0,1,(recip rho)*mu*sigma]) | mu <- j9]+                           ++ [([0,0,1],[0,0,1])] -- leaves "Y" fixed+    -- fromMatrix [[1,0,0],[0,rho,0],[0,0,sigma]] -- scale x,y -> rho x, sigma y+    t delta epsilon = fromMatrix [[1,delta,epsilon],[0,1,0],[0,0,1]] -- translation x,y -> x+delta, y+epsilon+    u = fromPairs $ [([1,x,y], [1,x+y,x-y]) | x <- j9, y <- j9]+                           ++ [([0,1,mu],[0,1,-mu]) | mu <- filter isComplex j9]+                           ++ [([0,1,0],[0,1,1]), ([0,1,1],[0,1,0]), ([0,1,-1],[0,0,1]), ([0,0,1],[0,1,-1])]+    -- fromMatrix [[1,0,0],[0,1,-1],[0,1,1]]+    a lambda = fromPairs [ (x, map (.^ lambda) x) | x <- xs]+-- order 311040+-- (which means this is also the plane constructed in Weibel?)+++-- dual plane of omega+omegaD = design (xs,bs) where+    xs = ptsPG2 j9+    bs = leftLinesPG2 j9++omegaD1 = D.to1n $ dual omega+-- need proof omega /~= omegaD++omegaD2 = design (xs,bs) where+    xs = ptsPG2 j9+    bs =  [ l | [p,q] <- combinationsOf 2 xs, l <- [line p q], [p,q] == take 2 l]+    line p q = toListSet $ filter ispnf [(p <* a) <+> (q <* b) | a <- j9, b <- j9]++us <* x = map (*x) us+++-- Room and Kirkpatrick p130+psi = design (xs,bs) where+    xs = ptsPG2 j9+    isReal x = all (`elem` [0,1,-1]) x+    xrs = ptsPG2 [0,1,-1] -- the thirteen real points, a copy of PG2(F3) within psi+    bs = toListSet [line p q | p <- xrs, q <- xs, q /= p]+    line p q = L.sort $ p : [pnf ( (p <* a) <+> q) | a <- j9]+++-- Room & Kirkpatrick p137+psi2 = design (xs,bs) where+    xs = ptsPG2 j9+    bs = L.sort $+         [ [0,0,1] : [ [0,1,x] | x <- j9] ] ++ -- line at infinity, z=0+         [ [0,0,1] : [ [1,kappa,y] | y <- j9] | kappa <- j9 ] ++ -- vertical lines x = kappa+         [ [0,1,m] : [ [1,x,m*x+kappa] | x <- j9 ] | m <- [0,1,-1], kappa <- j9 ] ++ -- lines with real slope+         [ [0,1,kappa] : [ [1,x,kappa*(x-r)+s] | x <- j9 ] | r <- [0,1,-1], s <- [0,1,-1], kappa <- j9 \\ [0,1,-1] ]+         -- lines with complex slope++-- Room & Kirkpatrick p134-6+collineationsPsi = realProjectivities -- real transvections, generating real projectivities+                ++ [a lambda | lambda <- autsJ9] where+    D xs bs = psi+    n = 3+    realTransvections = [elemTransvection n (r,c) l | r <- [1..n], c <- [1..n], r /= c, l <- [1]]+    realProjectivities = [fromPairs $ [(x, pnf (x <*>> m)) | x <- xs] | m <- realTransvections]+    a lambda = fromPairs [ (x, map (.^ lambda) x) | x <- xs]+-- order 33696+++-- The order of a projective plane+order (D xs bs) = length (head bs) - 1++isProjectivePlane pi = designParams pi == Just (2,(q^2+q+1,q,1))+    where q = order pi+++collinear (D xs bs) ys = (not . null) [b | b <- bs, all (`elem` b) ys]++-- assume p1..4 are distinct+isQuadrangle plane ps@[p1,p2,p3,p4] =+    all (not . collinear plane) (combinationsOf 3 ps)+++concurrent (D xs bs) ls = (not . null) [x | x <- xs, all (x `elem`) ls]++isQuadrilateral plane ls@[l1,l2,l3,l4] =+    all (not . concurrent plane) (combinationsOf 3 ls)+++isOval pi ps = length ps == order pi+1+            && all (not . collinear pi) (combinationsOf 3 ps)++findOvals1 pi = findOvals' 0 ([], points pi) where+    n = order pi+    findOvals' i (ls,rs)+        | i == n+1 = [reverse ls]+        | otherwise = concatMap (findOvals' (i+1))+                      [ (r:ls, rs') | r:rs' <- L.tails rs, all (not . collinear pi) (map (r:) (combinationsOf 2 ls)) ]+-- if we have a function to quickly generate the line through two points,+-- then we just need to see whether the third point is on it, which is much faster than testing collinearity++findQuadrangles pi = findQuadrangles' 0 ([], points pi) where+    findQuadrangles' i (ls,rs)+        | i == 4 = [reverse ls]+        | otherwise = concatMap (findQuadrangles' (i+1))+                      [ (r:ls, rs') | r:rs' <- L.tails rs, all (not . collinear pi) (map (r:) (combinationsOf 2 ls)) ]+++findOvals pi@(D xs bs) = findOvals' 0 ([],xs) bs where+    n = order pi+    findOvals' i (ls,rs) bs+        | i == n+1 = [reverse ls]+        | otherwise = concat+                      [let rls = reverse (r:ls)+                           (notchords, chords) = L.partition (\b -> length (rls `LS.intersect` b) < 2) bs+                           rs'' = foldl (\\) rs' chords+                           -- if any line is already a chord, remove remaining points on it from further consideration+                       in findOvals' (i+1) (r:ls, rs'') notchords+                       | r:rs' <- L.tails rs]++-- Todo:+-- Code that shows that phi is Desarguesian, and omega, omegaD and psi are not+{-+-- !! NOT WORKING+-- finds apparent counterexamples in phi too+findNonDesarguesian pi@(D xs bs) =+    [ [p,x,y,z,x',y',z',k,l,m] | p <- xs,+                                 x <- xs \\ [p],+                                 y <- xs \\ [p,x],+                                 z <- xs \\ [p,x,y],+                                 (not . collinear pi) [x,y,z],+                                 x' <- line p x \\ L.sort [p,x],+                                 y' <- line p y \\ L.sort [p,y],+                                 z' <- line p z \\ L.sort [p,z],+                                 (not . collinear pi) [x',y',z'],+                                 k <- line x y `intersect` line x' y', -- will only have one element+                                 l <- line x z `intersect` line x' z',+                                 m <- line y z `intersect` line y' z',+                                 (not . collinear pi) [k,l,m]  ]+    where line p q = head [b | b <- bs, p `elem` b, q `elem` b]+-}
Math/Projects/Rubik.hs view
@@ -3,7 +3,8 @@ module Math.Projects.Rubik where
 
 import Math.Algebra.Group.PermutationGroup
-import Math.Algebra.Group.SchreierSims
+import Math.Algebra.Group.SchreierSims as SS
+import Math.Algebra.Group.RandomSchreierSims as RSS
 
 
 --           11 12 13
@@ -22,5 +23,7 @@ r = p [[41,43,49,47],[42,46,48,44],[ 3,13,57,33],[ 6,16,54,36],[ 9,19,51,39]]
 u = p [[11,13,19,17],[12,16,18,14],[ 1,21,51,41],[ 2,22,52,42],[ 3,23,53,43]]
 d = p [[31,33,39,37],[32,36,38,34],[ 7,47,57,27],[ 8,48,58,28],[ 9,49,59,29]]
+
+rubikCube = [f,b,l,r,u,d]
 
 -- In Singmaster notation these would be capital letters.
Math/Test/TPermutationGroup.hs view
@@ -8,8 +8,10 @@ 
 import Math.Algebra.Group.PermutationGroup as P
 import Math.Algebra.Group.SchreierSims as SS
+import Math.Algebra.Group.RandomSchreierSims as RSS
 import Math.Combinatorics.Graph
 import Math.Combinatorics.GraphAuts
+import Math.Projects.Rubik
 
 import Test.QuickCheck hiding (choose)
 
@@ -74,7 +76,7 @@ 
 choose n m | m <= n = product [m+1..n] `div` product [1..n-m]
 
-test = and [sgsTest, ssTest, ccTest]
+test = and [sgsTest, ssTest, ccTest, rubikTest]
 
 
 sgsTest = all (uncurry (==)) sgsTests
@@ -86,8 +88,9 @@     [let _G = toSn (_S 3 `dp` _S 3) in (sgsOrder _G, SS.order _G) ] ++
     [let _G = toSn (_C 3 `wr` _S 3) in (sgsOrder _G, SS.order _G) ] ++
     [let _G = toSn (_S 3 `wr` _C 3) in (sgsOrder _G, SS.order _G) ]
-    where sgsOrder = orderTGS . tgsFromSgs . sgs
+    where sgsOrder = orderTGS . tgsFromSgs . SS.sgs
 
+rubikTest = orderSGS (RSS.sgs rubikCube) == 43252003274489856000
 
 ssTest = all (uncurry (==)) ssTests