packages feed

HaskellForMaths 0.1.8 → 0.1.9

raw patch · 10 files changed

+324/−317 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Math.Algebra.Group.PermutationGroup: (//) :: (Ord a, Show a) => [Permutation a] -> [Permutation a] -> [Permutation Int]
+ Math.Algebra.Group.PermutationGroup: isNormal :: (Ord a, Show a) => [Permutation a] -> [Permutation a] -> Bool
+ Math.Algebra.Group.PermutationGroup: normalSubgps :: (Ord a, Show a) => [Permutation a] -> [[Permutation a]]
+ Math.Algebra.Group.PermutationGroup: orderSGS :: (Ord a) => [Permutation a] -> Integer
+ Math.Algebra.Group.PermutationGroup: quotientGp :: (Ord a, Show a) => [Permutation a] -> [Permutation a] -> [Permutation Int]
+ Math.Algebra.Group.PermutationGroup: subgps :: (Ord a, Show a) => [Permutation a] -> [[Permutation a]]

Files

HaskellForMaths.cabal view
@@ -1,7 +1,7 @@    Name:                HaskellForMaths
-   Version:             0.1.8
+   Version:             0.1.9
    Category:            Math
-   Description:         Math library - combinatorics, group theory, commutative algebra, non-commutative algebra
+   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
    License:             BSD3
    License-file:        license.txt
Math/Algebra/Group/PermutationGroup.hs view
@@ -1,4 +1,4 @@--- Copyright (c) David Amos, 2008. All rights reserved.
+-- Copyright (c) David Amos, 2008-2009. All rights reserved.
 
 module Math.Algebra.Group.PermutationGroup where
 
@@ -126,18 +126,7 @@ orbitB gs b = orbit (-^) b gs
 orbitE gs b = orbit (-^) b gs
 
-{-
--- orbit of a vertex / point
-x .^^ gs = closure [x] [ .^g | g <- gs]
-orbitV gs x = closure [x] [ .^g | g <- gs]
-orbitP gs x = closure [x] [ .^g | g <- gs]
 
--- orbit of an edge / block
-b -^^ gs = closure [b] [ -^g | g <- gs]
-orbitE gs b = closure [b] [ -^g | g <- gs]
-orbitB gs b = closure [b] [ -^g | g <- gs]
--}
-
 action xs f = fromPairs [(x, f x) | x <- xs]
 
 
@@ -285,6 +274,8 @@ -- For example, sgs (_A 5) == [[[1,2,3]],[[2,4,5]],[[3,4,5]]]
 -- So we need all three to generate the first transversal, then the last two to generate the second transversal, etc
 
+-- |Given a strong generating set, return the order of the group it generates
+orderSGS :: (Ord a) => [Permutation a] -> Integer
 orderSGS sgs = product $ map (L.genericLength . fundamentalOrbit) bs where
     bs = toListSet $ map minsupp sgs
     fundamentalOrbit b = b .^^ filter ( (b <=) . minsupp ) sgs
@@ -323,6 +314,45 @@           conjClasses' (h:hs) = let c = conjClass gs h in c : conjClasses' (hs L.\\ c)
 -}
 
+
+-- given list of generators, try to find a shorter list
+reduceGens (1:gs) = reduceGens gs
+reduceGens (g:gs) = reduceGens' ([g], eltsS [g]) gs where
+    reduceGens' (gs,eltsgs) (h:hs) =
+        if h `S.member` eltsgs
+        then reduceGens' (gs,eltsgs) hs
+        else reduceGens' (h:gs, eltsS $ h:gs) hs
+    reduceGens' (gs,_) [] = reverse gs
+
+
+-- SUBGROUPS
+
+isSubgp hs gs = all (`S.member` gs') hs
+    where gs' = eltsS gs
+
+-- The following is similar to the "cyclic extension" method - Holt p385
+-- However, Holt only looks at normal cyclic extensions (ie, by an elt of prime order), and so only finds solvable subgps
+
+-- |Return the subgroups of a group. Only suitable for use on small groups (eg < 100 elts)
+subgps :: (Ord a, Show a) => [Permutation a] -> [[Permutation a]]
+subgps gs = [] : subgps' S.empty [] (map (:[]) hs) where
+    hs = filter isMinimal $ elts gs
+    subgps' found ls (r:rs) =
+        let ks = elts r in
+        if ks `S.member` found
+        then subgps' found ls rs
+        else r : subgps' (S.insert ks found) (r:ls) rs
+    subgps' found [] [] = []
+    subgps' found ls [] = subgps' found [] [l ++ [h] | l <- reverse ls, h <- hs, last l < h]
+
+-- g is the minimal elt in the cyclic subgp it generates
+isMinimal 1 = False
+isMinimal g = all (g <=) primitives -- g == minimum primitives
+    where powers = takeWhile (/=1) $ tail $ iterate (*g) 1
+          n = orderElt g -- == length powers + 1
+          primitives = filter (\h -> orderElt h == n) powers
+
+
 -- centralizer of a subgroup or a set of elts
 -- the centralizer of H in G is the set of elts of G which commute with all elts of H
 centralizer gs hs = [k | k <- elts gs, all (\h -> h*k == k*h) hs]
@@ -344,16 +374,6 @@ -- setwise stabiliser of a set
 setStab gs xs = [g | g <- elts gs, xs -^ g == xs]
 
-
--- given list of generators, try to find a shorter list
-reduceGens (1:gs) = reduceGens gs
-reduceGens (g:gs) = reduceGens' ([g], eltsS [g]) gs where
-    reduceGens' (gs,eltsgs) (h:hs) =
-        if h `S.member` eltsgs
-        then reduceGens' (gs,eltsgs) hs
-        else reduceGens' (h:gs, eltsS $ h:gs) hs
-    reduceGens' (gs,_) [] = reverse gs
-
 -- normal closure of H in G
 normalClosure gs hs = reduceGens $ hs ++ [h ~^ g | h <- hs, g <- gs ++ map inverse gs]
 
@@ -368,87 +388,54 @@ derivedSubgp gs = commutatorGp gs gs
 
 
--- ACTIONS ON COSETS AND SUBGROUPS (QUOTIENT GROUPS)
-
-isSubgp hs gs = all (isMember gs) hs
+-- ACTION ON COSETS, QUOTIENT GROUPS
 
-isNormal hs gs = isSubgp hs gs && all (isMember hs) [h~^g | h <- hs, g <- gs]
+xs -*- ys = toListSet [x*y | x <- xs, y <- ys]
 
+xs -*  y  = L.sort [x*y | x <- xs] -- == xs -*- [y]
+x   *- ys = L.sort [x*y | y <- ys] -- == [x] -*- ys
 
--- action of a group on cosets by right multiplication
--- (hs should be all elts, not just generators)
-hs **^ g = L.sort [h*g | h <- hs]
+-- |isNormal gs ks returns True if \<ks\> is normal in \<gs\>.
+-- Note, it is caller's responsibility to ensure that \<ks\> is a subgroup of \<gs\> (ie that each k is in \<gs\>).
+isNormal :: (Ord a, Show a) => [Permutation a] -> [Permutation a] -> Bool
+isNormal gs ks = all (== ks') [ (g^-1) *- ks' -* g | g <- gs]
+    where ks' = elts ks
 
--- Cosets are disjoint, which leads to Lagrange's theorem
+-- |Return the normal subgroups of a group. Only suitable for use on small groups (eg < 100 elts)
+normalSubgps :: (Ord a, Show a) => [Permutation a] -> [[Permutation a]]
+normalSubgps gs = filter (isNormal gs) (subgps gs)
 
--- cosets gs hs = closure [hs] [ **^ g | g <- gs]
-cosets gs hs = orbit (**^) hs gs
--- the group acts transitively on cosets of a subgp, so this gives all cosets
--- hs #^^ gs = orbit (#^) gs hs
+isSimple gs = length (normalSubgps gs) == 2
 
-cosetAction gs hs =
-    let _H = elts hs
-        cosets_H = cosets gs _H
-    in toSn [action cosets_H (**^ g) | g <- gs]
-    -- in toSn $ map (induced (**^) cosets_H) gs
+-- Note: caller must ensure that hs is a subgp of gs
+cosets gs hs = orbit (-*) hs' gs
+    where hs' = elts hs
 
--- if H normal in G, then each element within a given coset gives rise to the same action on other cosets,
--- and we get a well defined multiplication Hx * Hy = Hxy (where it doesn't depend on which coset rep we chose)
-quotientGp gs hs
-    | hs `isNormal` gs = gens $ cosetAction gs hs
-    | otherwise = error "quotientGp: not well defined unless H normal in G"
--- the call to gens removes identity and duplicates
+-- |quotientGp gs ks returns \<gs\> / \<ks\>
+quotientGp :: (Ord a, Show a) => [Permutation a] -> [Permutation a] -> [Permutation Int]
+quotientGp gs ks
+    | ks `isNormal` gs = gens $ toSn [action cosetsK (-* g) | g <- gs]
+    | otherwise = error "quotientGp: not well defined unless ks normal in gs"
+    where cosetsK = cosets gs ks
 
-gs // hs = quotientGp gs hs
+-- |Synonym for quotientGp
+(//) :: (Ord a, Show a) => [Permutation a] -> [Permutation a] -> [Permutation Int]
+gs // ks = quotientGp gs ks
 
 
--- action of group on a subgroup by conjugation
--- (hs should be all elts, not just generators)
-hs ~~^ g = L.sort [h ~^ g | h <- hs]
+-- action of group element on a subset by conjugation
+xs ~~^ g = L.sort [x ~^ g | x <- xs]
 
--- don't think that this is necessarily transitive on isomorphic subgps
--- conjugateSubgps gs hs = closure [hs] [ ~~^ g | g <- gs]
-conjugateSubgps gs hs = orbit (~~^) hs gs
--- hs ~~^^ gs = orbit (~~^) gs hs
+conjugateSubgps gs hs = orbit (~~^) hs' gs
+    where hs' = elts hs
+-- not necessarily transitive on isomorphic subgps - eg a gp with an outer aut
 
 subgpAction gs hs =
-    let _H = elts hs
-        conjugates_H = conjugateSubgps gs _H
-    in toSn [action conjugates_H (~~^ g) | g <- gs]
-    -- in toSn $ map (induced (~~^) conjugates_H) gs
+    let conjugatesH = conjugateSubgps gs hs
+    in toSn [action conjugatesH (~~^ g) | g <- gs]
 
 
 -- in cube gp, the subgps all appear to correspond to stabilisers of subsets, or of blocks
 
 
-
-{-
-OLDER VERSIONS
-
-
--- the orbit of a point or block under the action of a set of permutations
-orbit action x gs = S.toList $ orbitS action x gs
-
-orbitS action x gs = orbit' S.empty (S.singleton x) where
-    orbit' interior boundary
-        | S.null boundary = interior
-        | otherwise =
-            let interior' = S.union interior boundary
-                boundary' = S.fromList [p `action` g | g <- gs, p <- S.toList boundary] S.\\ interior'
-            in orbit' interior' boundary'
-
--- orbit of a point
--}
-{-
--- the induced action of g on a set of blocks
--- Note: the set of blocks must be closed under the action of g, otherwise we will get an error in fromPairs
--- To ensure that it is closed, generate the blocks as the orbit of a starting block
-inducedAction bs g = fromPairs [(b, b -^ g) | b <- bs]
-
-induced action bs g = fromPairs [(b, b `action` g) | b <- bs]
-
-inducedB bs g = induced (-^) bs g
--}
--- elts gs = orbit (*) 1 gs
--- eltsS gs = orbitS (*) 1 gs
 
Math/Algebra/LinearAlgebra.hs view
@@ -9,7 +9,7 @@ --
 -- The mnemonic for many of the arithmetic operations is that the number of angle brackets
 -- on each side indicates the dimension of the argument on that side. For example,
--- v <*>> m is multiplication of a vector on the left by a matrix on the right.
+-- v \<*\>\> m is multiplication of a vector on the left by a matrix on the right.
 module Math.Algebra.LinearAlgebra where
 
 import qualified Data.List as L
@@ -26,50 +26,50 @@ 
 -- vector operations
 
--- |u <+> v returns the sum u+v of vectors
+-- |u \<+\> v returns the sum u+v of vectors
 (<+>) :: (Num a) => [a] -> [a] -> [a]
 u <+> v = zipWith (+) u v
 
--- |u <-> v returns the difference u-v of vectors
+-- |u \<-\> v returns the difference u-v of vectors
 (<->) :: (Num a) => [a] -> [a] -> [a]
 u <-> v = zipWith (-) u v
 
--- |k *> v returns the product k*v of the scalar k and the vector v
+-- |k *\> v returns the product k*v of the scalar k and the vector v
 (*>) :: (Num a) => a -> [a] -> [a]
 k *> v = map (k*) v
 
--- |u <.> v returns the dot product of vectors (also called inner or scalar product)
+-- |u \<.\> v returns the dot product of vectors (also called inner or scalar product)
 (<.>) :: (Num a) => [a] -> [a] -> a
 u <.> v = sum (zipWith (*) u v)
 
--- |u <*> v returns the tensor product of vectors (also called outer or matrix product)
+-- |u \<*\> v returns the tensor product of vectors (also called outer or matrix product)
 (<*>) :: (Num a) => [a] -> [a] -> [[a]]
 u <*> v = [ [a*b | b <- v] | a <- u]
 
 
 -- matrix operations
 
--- |a <<+>> b returns the sum a+b of matrices
+-- |a \<\<+\>\> b returns the sum a+b of matrices
 (<<+>>) :: (Num a) => [[a]] -> [[a]] -> [[a]]
 a <<+>> b = (zipWith . zipWith) (+) a b
 
--- |a <<->> b returns the difference a-b of matrices
+-- |a \<\<-\>\> b returns the difference a-b of matrices
 (<<->>) :: (Num a) => [[a]] -> [[a]] -> [[a]]
 a <<->> b = (zipWith . zipWith) (-) a b
 
--- |a <<*>> b returns the product a*b of matrices
+-- |a \<\<*\>\> b returns the product a*b of matrices
 (<<*>>) :: (Num a) => [[a]] -> [[a]] -> [[a]]
 a <<*>> b = [ [u <.> v | v <- L.transpose b] | u <- a]
  
--- |k *> m returns the product k*m of the scalar k and the matrix m
+-- |k *\> m returns the product k*m of the scalar k and the matrix m
 (*>>) :: (Num a) => a -> [[a]] -> [[a]]
 k *>> m = (map . map) (k*) m
 
--- |m <<*> v is multiplication of a vector by a matrix on the left
+-- |m \<\<*\> v is multiplication of a vector by a matrix on the left
 (<<*>) :: (Num a) => [[a]] -> [a] -> [a]
 m <<*> v = map (<.> v) m
 
--- |v <*>> m is multiplication of a vector by a matrix on the right
+-- |v \<*\>\> m is multiplication of a vector by a matrix on the right
 (<*>>) :: (Num a) => [a] -> [[a]] -> [a]
 v <*>> m = map (v <.>) (L.transpose m)
 
Math/Combinatorics/Design.hs view
@@ -7,13 +7,13 @@ import qualified Data.Map as M
 import qualified Data.Set as S
 
-import Math.Common.ListSet (symDiff)
+import Math.Common.ListSet (intersect, symDiff)
 import Math.Algebra.Field.Base
 import Math.Algebra.Field.Extension
 import Math.Algebra.Group.PermutationGroup hiding (elts, order, isMember)
 import Math.Algebra.Group.SchreierSims as SS
 import Math.Combinatorics.Graph as G hiding (to1n)
-import Math.Combinatorics.GraphAuts (refine, isSingleton, graphAuts, incidenceAuts, removeGens)
+import Math.Combinatorics.GraphAuts (refine', isSingleton, graphAuts, incidenceAuts) -- , removeGens)
 import Math.Combinatorics.FiniteGeometry
 
 -- Cameron & van Lint, Designs, Graphs, Codes and their Links
@@ -77,7 +77,7 @@         case reverse (takeWhile (isJust . snd) [(t, findlambda t d) | t <- [0..k] ]) of
         [] -> Nothing
         (t,Just lambda):_ -> Just (t,(v,k,lambda))
-
+-- Note that a 0-(v,k,lambda) design just means that there are lambda blocks, all of size k, with no other regularity
 
 isStructure t d = isJust $ tDesignParams t d
 
@@ -213,106 +213,6 @@ -- The incidence graph is a bipartite graph, so the distance function naturally partitions points from blocks
 
 
--- !! Not strictly correct to stop when a level is empty
--- For example, Fano plane, then if you fix 1,2, you can't move 3, but you can move 4
--- However, the reason it doesn't seem to cause any problem is that in fact it's almost certainly okay to just find the first level
--- (since we know the group is transitive on the points)
--- (In the graph case, non-transitive graphs like kb 2 3 require us to continue after null levels)
-
--- !! No, not okay to stop at first level
--- The strong generators of one level don't necessarily generate the whole group
--- For example, suppose the group is Sn. We might by chance find a transversal consisting entirely of elts of An
--- We might only finally get Sn when looking for transversal for n-1, and finding (n-1 n)
-
--- The distance partition of the incidence graph of a design will always look like this:
--- [x], [b | x `elem` b], xs \\ [x], [b | x `notElem` b],
--- When we refine it, we're crossing it with a similar partition but with a different x
--- This won't have much effect on the point cell, but will hopefully split the block cells in half
-
-
--- !! superceded by Math.Combinatorics.GraphAuts.incidenceAuts, leading to designAuts above
--- based on graphAuts as applied to the incidence graph, but modified to avoid point-block crossover auts
-designAuts2 d@(D xs bs) = map points (designAuts' [] [vs]) where
-    n = length xs
-    g@(G vs es) = incidenceGraph d
-    points h = fromPairs [(x,y) | (Left x, Left y) <- toPairs h] -- filtering out the action on blocks
-    designAuts' us p@((x@(Left _):ys):pt) =
-        let p' = L.sort $ filter (not . null) $ refine (ys:pt) (dps M.! x)
-        in level us p x ys []
-        ++ designAuts' (x:us) p'
-    designAuts' us ([]:pt) = designAuts' us pt
-    designAuts' _ (((Right _):_):_) = [] -- if we fix all the points, then the blocks must be fixed too 
-    -- designAuts' _ [] = []
-    level us p@(ph:pt) x (y@(Left _):ys) hs =
-        let px = refine (L.delete x ph : pt) (dps M.! x)
-            py = refine (L.delete y ph : pt) (dps M.! y)
-            uus = zip us us
-        in case dfs ((x,y):uus) px py of
-           []  -> level us p x ys hs
-           h:_ -> let hs' = h:hs in h : level us p x (ys L.\\ (x .^^ hs')) hs'
-    -- level _ _ _ [] _ = []
-    level _ _ _ _ _ = [] -- includes the case where y matches Right _, which can only occur on first level, before we've distance partitioned
-    dfs xys p1 p2
-        | map length p1 /= map length p2 = []
-        | otherwise =
-            let p1' = filter (not . null) p1
-                p2' = filter (not . null) p2
-            in if all isSingleton p1'
-               then let xys' = xys ++ zip (concat p1') (concat p2')
-                    in if isCompatible xys' then [fromPairs' xys'] else []
-               else let (x:xs):p1'' = p1'
-                        ys:p2'' = p2'
-                    in concat [dfs ((x,y):xys)
-                                   (refine (xs : p1'') (dps M.! x))
-                                   (refine ((L.delete y ys):p2'') (dps M.! y))
-                                   | y <- ys]
-    isCompatible xys = and [([x,x'] `S.member` es') == (L.sort [y,y'] `S.member` es') | (x,y) <- xys, (x',y') <- xys, x < x']
-    dps = M.fromList [(v, G.distancePartition g v) | v <- vs]
-    es' = S.fromList es
-
-
-designAutsNew d@(D xs bs) = map points (designAuts' [] [] [vs]) where
-    n = length xs
-    g@(G vs es) = incidenceGraph d
-    -- xs' = take n vs -- the points of the design as vertices of the incidence graph
-    points (P m) = P $ M.fromList [(k,v) | (Left k, Left v) <- M.toList m]
-    designAuts' us hs p@((x@(Left _):ys):pt) =
-        let p' = L.sort $ filter (not . null) $ refine (ys:pt) (dps M.! x)
-            hs' = level us p x (ys L.\\ (x .^^ hs)) []
-            reps = cosetRepsGx (hs'++hs) x
-            schreierGens = removeGens x $ schreierGeneratorsGx (x,reps) (hs'++hs)
-        in hs' ++ designAuts' (x:us) schreierGens p'
-    designAuts' us hs ([]:pt) = designAuts' us hs pt
-    designAuts' us hs p@((x@(Right _):ys):pt) = []
-    designAuts' _ _ [] = []
-    level us p@(ph:pt) x (y@(Left _):ys) hs =
-        let px = refine (L.delete x ph : pt) (dps M.! x)
-            py = refine (L.delete y ph : pt) (dps M.! y)
-            uus = zip us us
-        in case dfs ((x,y):uus) px py of
-                []  -> level us p x ys hs
-                h:_ -> let hs' = h:hs in h : level us p x (ys L.\\ (x .^^ hs')) hs'
-    -- level _ _ _ [] _ = []
-    level _ _ _ _ _ = [] -- includes the case where y matches Right _, which can only occur on first level, before we've distance partitioned
-    dfs xys p1 p2
-        | map length p1 /= map length p2 = []
-        | otherwise =
-            let p1' = filter (not . null) p1
-                p2' = filter (not . null) p2
-            in if all isSingleton p1'
-               then let xys' = xys ++ zip (concat p1') (concat p2')
-                    in if isCompatible xys' then [fromPairs' xys'] else []
-               else let (x:xs):p1'' = p1'
-                        ys:p2'' = p2'
-                    in concat [dfs ((x,y):xys)
-                                   (refine (xs : p1'') (dps M.! x))
-                                   (refine ((L.delete y ys):p2'') (dps M.! y))
-                                   | y <- ys]
-    isCompatible xys = and [([x,x'] `S.member` es') == (L.sort [y,y'] `S.member` es') | (x,y) <- xys, (x',y') <- xys, x < x']
-    dps = M.fromList [(v, G.distancePartition g v) | v <- vs]
-    es' = S.fromList es
-
-
 -- MATHIEU GROUPS AND WITT DESIGNS
 
 alphaL2_23 = p [[-1],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]]                      -- t -> t+1
@@ -340,7 +240,7 @@ m22sgs = filter (\g -> 0.^g == 0) m23sgs
 -- order 443520
 
--- !! The above assume that the base is [-1,0,..], which isn't guaranteed
+-- sgs uses the base implied by the Ord instance, which will be [-1,0,..]
 
 
 -- Steiner system S(5,8,24)
@@ -352,27 +252,63 @@ s_5_8_24 = design ([-1..22], octad -^^ l2_23)
 -- S(5,8,24) constructed as the image of a single octad under the action of PSL(2,23)
 -- 759 blocks ( (24 `choose` 5) `div` (8 `choose` 5) )
+-- Automorphism group is M24
 
 s_4_7_23 = derivedDesign s_5_8_24 (-1)
 -- 253 blocks ( (23 `choose` 4) `div` (7 `choose` 4) )
+-- Automorphism group is M23
 
 s_3_6_22 = derivedDesign s_4_7_23 0
 -- 77 blocks
+-- Automorphism group is M22
 
+-- Derived design of s_3_6_22 is PG(2,F4)
+
+
+-- An alternative construction
+s_5_8_24' = D xs bs where
+    xs = [1..24]
+    bs = sift [] (combinationsOf 8 xs)
+    sift ls (r:rs) = if all ((<=4) . length) [r `intersect` l | l <- ls]
+                     then r : sift (r:ls) rs 
+                     else sift ls rs
+    sift ls [] = []
+
+
 -- Could test that m22sgs are all designAuts of s_3_6_22
 
-{-
-s_5_8_24' = octad -^^ m24 -- [alphaL2_23, betaL2_23, gammaL2_23]
--- S(5,8,24) constructed as the image of a single octad under the action of PSL(2,23)
--- 759 blocks ( (24 `choose` 5) `div` (8 `choose` 5) )
 
+-- S(5,6,12) and M12
 
-s_4_7_23' = [xs | (-1):xs <- s_5_8_24]
--- 253 blocks ( (23 `choose` 4) `div` (7 `choose` 4) )
+alphaL2_11 = p [[-1],[0,1,2,3,4,5,6,7,8,9,10]]          -- t -> t+1
+betaL2_11  = p [[-1],[0],[1,3,9,5,4],[2,6,7,10,8]]      -- t -> 3*t
+gammaL2_11 = p [[-1,0],[1,10],[2,5],[3,7],[4,8],[6,9]]  -- t -> -1/t
 
-s_3_6_22' = [xs | 0:xs <- s_4_7_23]
--- 77 blocks
--}
+l2_11 = [alphaL2_11, betaL2_11, gammaL2_11]
+
+deltaM12 = p [[-1],[0],[1],[2,10],[3,4],[5,9],[6,7],[8]]
+-- Conway&Sloane p271, 327
+
+hexad = [0,1,3,4,5,9]
+-- the squares (quadratic residues) in F11
+-- http://en.wikipedia.org/wiki/Steiner_system
+
+s_5_6_12 = design ([-1..10], hexad -^^ l2_11)
+-- S(5,6,12) constructed as the image of a single hexad under the action of PSL(2,11)
+-- 132 blocks ( (12 `choose` 5) `div` (6 `choose` 5) )
+-- Automorphism group is M12
+
+s_4_5_11 = derivedDesign s_5_6_12 (-1)
+-- 66 blocks
+-- Automorphism group is M11
+
+m12 = [alphaL2_11, betaL2_11, gammaL2_11, deltaM12]
+
+m12sgs = sgs m12
+-- order 95040
+
+m11sgs = filter (\g -> (-1).^g == -1) m12sgs
+-- order 7920
 
 
 {-
Math/Combinatorics/FiniteGeometry.hs view
@@ -7,6 +7,8 @@ import Data.List as L
 import qualified Data.Set as S
 
+import Math.Common.ListSet (toListSet)
+
 import Math.Algebra.Field.Base
 import Math.Algebra.Field.Extension hiding ( (<+>) ) -- , (*>) )
 import Math.Algebra.LinearAlgebra -- hiding ( det )
@@ -14,6 +16,7 @@ import Math.Combinatorics.Graph
 import Math.Combinatorics.GraphAuts -- for use in GHCi
 import Math.Algebra.Group.PermutationGroup -- for use in GHCi
+import Math.Algebra.Group.SchreierSims as SS -- for use in GHCi
 
 -- |ptsAG n fq returns the points of the affine geometry AG(n,Fq), where fq are the elements of Fq
 ptsAG :: (FiniteField a) => Int -> [a] -> [[a]]
@@ -36,14 +39,25 @@ ispnf _ = False
 
 -- closure of points in AG(n,Fq)
--- result is sorted
+-- given p1, .., pk, we're looking for all a1 p1 + ... + ak pk, s.t. a1 + ... + ak = 1
+-- if m is the matrix with p1, .., pk as rows, and vs are the vectors [a1, .., ak]
+-- then this is the same as [v <*>> m | v <- vs] == [m' <<*> v | v <- vs]
 closureAG ps =
-    let multipliers = [ (1 - sum xs) : xs | xs <- ptsAG (k-1) fq ] -- k-vectors over fq whose sum is 1
-    in S.toList $ S.fromList [foldl1 (<+>) $ zipWith (*>) m ps | m <- multipliers]
-    where n = length $ head ps -- the dimension of the space we're working in
-          k = length ps        -- the dimension of the flat
+    let vs = [ (1 - sum xs) : xs | xs <- ptsAG (k-1) fq ] -- k-vectors over fq whose sum is 1
+    in toListSet [m' <<*> v | v <- vs]
+    where k = length ps -- the dimension of the flat (assuming ps are independent)
+          m' = L.transpose ps
           fq = eltsFq undefined
+-- toListSet call sorts the result, and also removes duplicates in case the points weren't independent
 
+{-
+closureAG ps =
+    let vs = [ (1 - sum xs) : xs | xs <- ptsAG (k-1) fq ] -- k-vectors over fq whose sum is 1
+    in toListSet [foldl1 (<+>) $ zipWith (*>) v ps | v <- vs]
+    where k = length ps        -- the dimension of the flat
+          fq = eltsFq undefined
+-}
+
 lineAG [p1,p2] = L.sort [ p1 <+> (c *> dp) | c <- fq ] where
     dp = p2 <-> p1
     fq = eltsFq undefined
@@ -51,11 +65,13 @@ -- closure of points in PG(n,Fq)
 -- take all linear combinations of the points (ie the subspace generated by the points, considered as points in Fq ^(n+1) )
 -- then discard all which aren't in PNF (thus dropping back into PG(n,Fq))
-closurePG ps = L.sort $ filter ispnf $ map (<*>> ps) $ ptsAG k fq where
+closurePG ps = toListSet $ filter ispnf $ map (<*>> ps) $ ptsAG k fq where
     k = length ps
     fq = eltsFq undefined
-
+-- toListSet call sorts the result, and also removes duplicates in case the points weren't independent
 
+linePG [p1,p2] = toListSet $ filter ispnf [(a *> p1) <+> (b *> p2) | a <- fq, b <- fq]
+    where fq = eltsFq undefined
 
 -- van Lint & Wilson, p325, 332
 qtorial n q | n >= 0 = product [(q^i - 1) `div` (q-1) | i <- [1..n]]
@@ -136,13 +152,12 @@ -- among all pairs of distinct points, select those which are the first two in the line they generate
 linesAG1 n fq = [ [x,y] | [x,y] <- combinationsOf 2 (ptsAG n fq),
                           [x,y] == take 2 (lineAG [x,y]) ]
-
+-- the point of the condition at the end is to avoid listing the same line more than once
 
 -- almost certainly not as efficient as linesAG, because requires lineAG/closureAG call
 -- a line in AG(n,fq) is a translation (x) of a line through the origin (y)
 linesAG2 n fq = [ [x,z] | x <- ptsAG n fq, y <- ptsPG (n-1) fq,
                           z <- [x <+> y], [x,z] == take 2 (closureAG [x,z]) ]
--- the point of the condition at the end is to avoid listing the same line more than once
 
 
 -- INCIDENCE GRAPH
@@ -164,7 +179,6 @@ -- The full group is called PGammaL(3,f4)
 
 
-
 -- |Incidence graph of AG(n,fq), considered as an incidence structure between points and lines
 incidenceGraphAG :: (Ord a, FiniteField a) => Int -> [a] -> Graph (Either [a] [[a]])
 incidenceGraphAG n fq = G vs es where
@@ -185,8 +199,13 @@ 
 orderAff n q = q^n * orderGL n q
 
+orderPGL n q = orderGL n q `div` (q-1)
 
 -- NOTE:
 -- AG(n,F2) is degenerate:
 -- Every pair of points is a line, so it is the complete graph on 2^n points
 -- And as such has aut group S(2^n)
+
+
+-- Heawood graph = incidence graph of Fano plane
+heawood = to1n $ incidenceGraphPG 2 f2
Math/Combinatorics/Graph.hs view
@@ -211,6 +211,8 @@ 
 valencies g@(G vs es) = map (head &&& length) $ L.group $ L.sort $ map (valency g) vs
 
+valencyPartition g@(G vs es) = map (map snd) $ L.groupBy (\x y -> fst x == fst y) [(valency g v, v) | v <- vs]
+
 regularParam g =
     case valencies g of
     [(v,_)] -> Just v
Math/Combinatorics/GraphAuts.hs view
@@ -5,6 +5,7 @@ import qualified Data.List as L
 import qualified Data.Map as M
 import qualified Data.Set as S
+import Data.Maybe
 
 import Math.Common.ListSet
 import Math.Combinatorics.Graph
@@ -87,14 +88,22 @@           stab = dropWhile (\p -> v .^ p /= v) auts -- we know that graphAuts are returned in this order
           stabOrbits = let os = orbits stab in os ++ map (:[]) ((v:vs) L.\\ concat os) -- include fixed point orbits
 
+
 -- GRAPH AUTOMORPHISMS
 
+-- !! Note, in the literature the following is just called the intersection of two partitions
+-- !! Refinement actually refers to the process of refining to an equitable partition
+
 -- refine one partition by another
-refine p1 p2 = concat [ [c1 `intersect` c2 | c2 <- p2] | c1 <- p1]
+refine p1 p2 = filter (not . null) $ refine' p1 p2
 -- Refinement preserves ordering within cells but not between cells
 -- eg the cell [1,2,3,4] could be refined to [2,4],[1,3]
 
+-- refine, but leaving null cells in
+-- we use this in the graphAuts functions when comparing two refinements to check that they split in the same way
+refine' p1 p2 = concat [ [c1 `intersect` c2 | c2 <- p2] | c1 <- p1]
 
+
 isGraphAut (G vs es) h = all (`S.member` es') [e -^ h | e <- es]
     where es' = S.fromList es
 -- this works best on sparse graphs, where p(edge) < 1/2
@@ -130,8 +139,8 @@ -- Now using distance partitions
 graphAuts3 g@(G vs es) = graphAuts' [] [vs] where
     graphAuts' us ((x:ys):pt) =
-        let px = refine (ys : pt) (dps M.! x)
-            p y = refine ((x : L.delete y ys) : pt) (dps M.! y)
+        let px = refine' (ys : pt) (dps M.! x)
+            p y = refine' ((x : L.delete y ys) : pt) (dps M.! y)
             uus = zip us us
             p' = L.sort $ filter (not . null) $ px
         in concat [take 1 $ dfs ((x,y):uus) px (p y) | y <- ys]
@@ -150,8 +159,8 @@                else let (x:xs):p1'' = p1'
                         ys:p2'' = p2'
                     in concat [dfs ((x,y):xys)
-                                   (refine (xs : p1'') (dps M.! x))
-                                   (refine ((L.delete y ys):p2'') (dps M.! y))
+                                   (refine' (xs : p1'') (dps M.! x))
+                                   (refine' ((L.delete y ys):p2'') (dps M.! y))
                                    | y <- ys]
     isCompatible xys = and [([x,x'] `S.member` es') == (L.sort [y,y'] `S.member` es') | (x,y) <- xys, (x',y') <- xys, x < x']
     dps = M.fromList [(v, distancePartition g v) | v <- vs]
@@ -163,18 +172,17 @@ 
 -- Now we try to use generators we've already found at a given level to save us having to look for others
 -- For example, if we have found (1 2)(3 4) and (1 3 2), then we don't need to look for something taking 1 -> 4
--- |Given a graph g, graphAuts g returns a strong generating set for the automorphism group of g.
-graphAuts :: (Ord a) => Graph a -> [Permutation a]
-graphAuts g@(G vs es) = graphAuts' [] [vs] where
+graphAuts4 g@(G vs es) = graphAuts' [] [vs] where
     graphAuts' us p@((x:ys):pt) =
-        let p' = L.sort $ filter (not . null) $ refine (ys:pt) (dps M.! x)
+        -- let p' = L.sort $ filter (not . null) $ refine' (ys:pt) (dps M.! x)
+        let p' = L.sort $ refine (ys:pt) (dps M.! x)
         in level us p x ys []
         ++ graphAuts' (x:us) p'
     graphAuts' us ([]:pt) = graphAuts' us pt
     graphAuts' _ [] = []
     level us p@(ph:pt) x (y:ys) hs =
-        let px = refine (L.delete x ph : pt) (dps M.! x)
-            py = refine (L.delete y ph : pt) (dps M.! y)
+        let px = refine' (L.delete x ph : pt) (dps M.! x)
+            py = refine' (L.delete y ph : pt) (dps M.! y)
             uus = zip us us
         in case dfs ((x,y):uus) px py of
            []  -> level us p x ys hs
@@ -191,8 +199,8 @@                else let (x:xs):p1'' = p1'
                         ys:p2'' = p2'
                     in concat [dfs ((x,y):xys)
-                                   (refine (xs : p1'') (dps M.! x))
-                                   (refine ((L.delete y ys):p2'') (dps M.! y))
+                                   (refine' (xs : p1'') (dps M.! x))
+                                   (refine' ((L.delete y ys):p2'') (dps M.! y))
                                    | y <- ys]
     isCompatible xys = and [([x,x'] `S.member` es') == (L.sort [y,y'] `S.member` es') | (x,y) <- xys, (x',y') <- xys, x < x']
     dps = M.fromList [(v, distancePartition g v) | v <- vs]
@@ -201,27 +209,129 @@ -- contrary to first thought, you can't stop when a level is null - eg kb 2 3, the third level is null, but the fourth isn't
 
 
+
+-- an example for equitable partitions
+-- this is a graph whose distance partition (from any vertex) can be refined to an equitable partition
+eqgraph = G vs es where
+    vs = [1..14]
+    es = L.sort $ [[1,14],[2,13]] ++ [ [v1,v2] | [v1,v2] <- combinationsOf 2 vs, v1+1 == v2 || v1+3 == v2 && even v2]
+
+-- refine a partition to give an equitable partition
+toEquitable g cells = L.sort $ toEquitable' [] cells where
+    toEquitable' ls (r:rs) =
+        let (lls,lrs) = L.partition isSingleton $ map (splitNumNbrs r) ls
+            -- so the lrs split, and the lls didn't
+            rs' = concatMap (splitNumNbrs r) rs
+        in if isSingleton r -- then we know it won't split further, so can remove it from further processing
+           then r : toEquitable' (concat lls) (concat lrs ++ rs')
+           else toEquitable' (r : concat lls) (concat lrs ++ rs')
+    toEquitable' ls [] = ls
+    splitNumNbrs t c = map (map snd) $ L.groupBy (\x y -> fst x == fst y) $ L.sort
+                    [ (length ((nbrs_g M.! v) `intersect` t), v) | v <- c]
+    nbrs_g = M.fromList [(v, nbrs g v) | v <- vertices g]
+
+
+-- try to refine two partitions in parallel, failing if they become mismatched
+toEquitable2 nbrs_g psrc ptrg = unzip $ L.sort $ toEquitable' [] (zip psrc ptrg) where
+    toEquitable' ls (r:rs) =
+        let ls' = map (splitNumNbrs nbrs_g r) ls
+            (lls,lrs) = L.partition isSingleton $ map fromJust ls'
+            rs' = map (splitNumNbrs nbrs_g r) rs
+        in if any isNothing ls' || any isNothing rs'
+           then []
+           else
+               {- if (isSingleton . fst) r
+               then r : toEquitable' (concat lls) (concat lrs ++ concatMap fromJust rs')
+               else -} toEquitable' (r : concat lls) (concat lrs ++ concatMap fromJust rs')
+    toEquitable' ls [] = ls
+
+splitNumNbrs nbrs_g (t_src,t_trg) (c_src,c_trg) =
+    let src_split = L.groupBy (\x y -> fst x == fst y) $ L.sort
+                    [ (length ((nbrs_g M.! v) `intersect` t_src), v) | v <- c_src]
+        trg_split = L.groupBy (\x y -> fst x == fst y) $ L.sort
+                    [ (length ((nbrs_g M.! v) `intersect` t_trg), v) | v <- c_trg]
+    in if map length src_split == map length trg_split
+       && map (fst . head) src_split == map (fst . head) trg_split
+       then Just $ zip (map (map snd) src_split) (map (map snd) trg_split)
+       else Nothing
+       -- else error (show (src_split, trg_split)) -- for debugging
+
+-- Now, every time we intersect two partitions, refine to an equitable partition
+-- |Given a graph g, graphAuts g returns a strong generating set for the automorphism group of g.
+graphAuts :: (Ord a) => Graph a -> [Permutation a]
+graphAuts g@(G vs es) = graphAuts' [] (toEquitable g $ valencyPartition g) where
+    graphAuts' us p@((x:ys):pt) =
+        let p' = L.sort $ filter (not . null) $ refine' (ys:pt) (dps M.! x)
+        in level us p x ys []
+        ++ graphAuts' (x:us) p'
+    graphAuts' us ([]:pt) = graphAuts' us pt
+    graphAuts' _ [] = []
+    level us p@(ph:pt) x (y:ys) hs =
+        let px = refine' (L.delete x ph : pt) (dps M.! x)
+            py = refine' (L.delete y ph : pt) (dps M.! y)
+            uus = zip us us
+        in case dfsEquitable (dps,es',nbrs_g) ((x,y):uus) px py of
+           []  -> level us p x ys hs
+           h:_ -> let hs' = h:hs in h : level us p x (ys L.\\ (x .^^ hs')) hs'
+    level _ _ _ [] _ = []
+    dps = M.fromList [(v, distancePartition g v) | v <- vs]
+    es' = S.fromList es
+    nbrs_g = M.fromList [(v, nbrs g v) | v <- vs]
+
+dfsEquitable (dps,es',nbrs_g) xys p1 p2 = dfs xys p1 p2 where
+    dfs xys p1 p2
+        | map length p1 /= map length p2 = []
+        | otherwise =
+            let p1' = filter (not . null) p1
+                p2' = filter (not . null) p2
+                (p1e,p2e) = toEquitable2 nbrs_g p1' p2'
+            in if null p1e
+               then []
+               else
+                   if all isSingleton p1e
+                   then let xys' = xys ++ zip (concat p1e) (concat p2e)
+                        in if isCompatible xys' then [fromPairs' xys'] else []
+                   else let (x:xs):p1'' = p1e
+                            ys:p2'' = p2e
+                        in concat [dfs ((x,y):xys)
+                                       (refine' (xs : p1'') (dps M.! x))
+                                       (refine' ((L.delete y ys):p2'') (dps M.! y))
+                                       | y <- ys]
+    isCompatible xys = and [([x,x'] `S.member` es') == (L.sort [y,y'] `S.member` es') | (x,y) <- xys, (x',y') <- xys, x < x']
+
+
 -- AUTS OF INCIDENCE STRUCTURE VIA INCIDENCE GRAPH
 
 -- based on graphAuts as applied to the incidence graph, but modified to avoid point-block crossover auts
 incidenceAuts g@(G vs es) = map points (incidenceAuts' [] [vs]) where
     points h = fromPairs [(x,y) | (Left x, Left y) <- toPairs h] -- filtering out the action on blocks
-    -- points (P m) = P $ M.fromList [(k,v) | (Left k, Left v) <- M.toList m]
     incidenceAuts' us p@((x@(Left _):ys):pt) =
-        let p' = L.sort $ filter (not . null) $ refine (ys:pt) (dps M.! x)
+        -- let p' = L.sort $ filter (not . null) $ refine' (ys:pt) (dps M.! x)
+        let p' = L.sort $ refine (ys:pt) (dps M.! x)
         in level us p x ys []
         ++ incidenceAuts' (x:us) p'
     incidenceAuts' us ([]:pt) = incidenceAuts' us pt
     incidenceAuts' _ (((Right _):_):_) = [] -- if we fix all the points, then the blocks must be fixed too 
     -- incidenceAuts' _ [] = []
     level us p@(ph:pt) x (y@(Left _):ys) hs =
-        let px = refine (L.delete x ph : pt) (dps M.! x)
-            py = refine (L.delete y ph : pt) (dps M.! y)
+        let px = refine' (L.delete x ph : pt) (dps M.! x)
+            py = refine' (L.delete y ph : pt) (dps M.! y)
             uus = zip us us
-        in case dfs ((x,y):uus) px py of
+        in case dfsEquitable (dps,es',nbrs_g) ((x,y):uus) px py of
            []  -> level us p x ys hs
            h:_ -> let hs' = h:hs in h : level us p x (ys L.\\ (x .^^ hs')) hs'
     level _ _ _ _ _ = [] -- includes the case where y matches Right _, which can only occur on first level, before we've distance partitioned
+    dps = M.fromList [(v, distancePartition g v) | v <- vs]
+    es' = S.fromList es
+    nbrs_g = M.fromList [(v, nbrs g v) | v <- vs]
+
+
+-- GRAPH ISOMORPHISMS
+
+-- !! not yet using equitable partitions, so could probably be more efficient
+
+graphIsos g1 g2 = concat [dfs [] (distancePartition g1 v1) (distancePartition g2 v2) | v2 <- vertices g2] where
+    v1 = head $ vertices g1
     dfs xys p1 p2
         | map length p1 /= map length p2 = []
         | otherwise =
@@ -229,21 +339,23 @@                 p2' = filter (not . null) p2
             in if all isSingleton p1'
                then let xys' = xys ++ zip (concat p1') (concat p2')
-                    in if isCompatible xys' then [fromPairs' xys'] else []
+                    in if isCompatible xys' then [xys'] else []
                else let (x:xs):p1'' = p1'
                         ys:p2'' = p2'
                     in concat [dfs ((x,y):xys)
-                                   (refine (xs : p1'') (dps M.! x))
-                                   (refine ((L.delete y ys):p2'') (dps M.! y))
+                                   (refine' (xs : p1'') (dps1 M.! x))
+                                   (refine' ((L.delete y ys):p2'') (dps2 M.! y))
                                    | y <- ys]
-    isCompatible xys = and [([x,x'] `S.member` es') == (L.sort [y,y'] `S.member` es') | (x,y) <- xys, (x',y') <- xys, x < x']
-    dps = M.fromList [(v, distancePartition g v) | v <- vs]
-    es' = S.fromList es
-
-
+    isCompatible xys = and [([x,x'] `S.member` es1) == (L.sort [y,y'] `S.member` es2) | (x,y) <- xys, (x',y') <- xys, x < x']
+    dps1 = M.fromList [(v, distancePartition g1 v) | v <- vertices g1]
+    dps2 = M.fromList [(v, distancePartition g2 v) | v <- vertices g2]
+    es1 = S.fromList $ edges g1
+    es2 = S.fromList $ edges g2
 
+isIso g1 g2 = (not . null) (graphIsos g1 g2)
 
 
+{-
 removeGens x gs = removeGens' [] gs where
     baseOrbit = x .^^ gs
     removeGens' ls (r:rs) =
@@ -266,15 +378,15 @@     graphAuts' us hs p@((x:ys):pt) =
         let ys' = ys L.\\ (x .^^ hs) -- don't need to consider points which can already be reached from Schreier generators
             hs' = level us p x ys' []
-            p' = L.sort $ filter (not . null) $ refine (ys:pt) (dps M.! x)
+            p' = L.sort $ filter (not . null) $ refine' (ys:pt) (dps M.! x)
             reps = cosetRepsGx (hs'++hs) x
             schreierGens = removeGens x $ schreierGeneratorsGx (x,reps) (hs'++hs)
         in hs' ++ graphAuts' (x:us) schreierGens p'
     graphAuts' us hs ([]:pt) = graphAuts' us hs pt
     graphAuts' _ _ [] = []
     level us p@(ph:pt) x (y:ys) hs =
-        let px = refine (L.delete x ph : pt) (dps M.! x)
-            py = refine (L.delete y ph : pt) (dps M.! y)
+        let px = refine' (L.delete x ph : pt) (dps M.! x)
+            py = refine' (L.delete y ph : pt) (dps M.! y)
             uus = zip us us
         in if map length px /= map length py
            then level us p x ys hs
@@ -294,66 +406,12 @@                else let (x:xs):p1'' = p1'
                         ys:p2'' = p2'
                     in concat [dfs ((x,y):xys)
-                                   (refine (xs : p1'') (dps M.! x))
-                                   (refine ((L.delete y ys):p2'') (dps M.! y))
+                                   (refine' (xs : p1'') (dps M.! x))
+                                   (refine' ((L.delete y ys):p2'') (dps M.! y))
                                    | y <- ys]
     isCompatible xys = and [([x,x'] `S.member` es') == (L.sort [y,y'] `S.member` es') | (x,y) <- xys, (x',y') <- xys, x < x']
     dps = M.fromList [(v, distancePartition g v) | v <- vs]
     es' = S.fromList es
-
-
--- GRAPH ISOMORPHISMS
-
-graphIsos g1 g2 = concat [dfs [] (distancePartition g1 v1) (distancePartition g2 v2) | v2 <- vertices g2] where
-    v1 = head $ vertices g1
-    dfs xys p1 p2
-        | map length p1 /= map length p2 = []
-        | otherwise =
-            let p1' = filter (not . null) p1
-                p2' = filter (not . null) p2
-            in if all isSingleton p1'
-               then let xys' = xys ++ zip (concat p1') (concat p2')
-                    in if isCompatible xys' then [xys'] else []
-               else let (x:xs):p1'' = p1'
-                        ys:p2'' = p2'
-                    in concat [dfs ((x,y):xys)
-                                   (refine (xs : p1'') (dps1 M.! x))
-                                   (refine ((L.delete y ys):p2'') (dps2 M.! y))
-                                   | y <- ys]
-    isCompatible xys = and [([x,x'] `S.member` es1) == (L.sort [y,y'] `S.member` es2) | (x,y) <- xys, (x',y') <- xys, x < x']
-    dps1 = M.fromList [(v, distancePartition g1 v) | v <- vertices g1]
-    dps2 = M.fromList [(v, distancePartition g2 v) | v <- vertices g2]
-    es1 = S.fromList $ edges g1
-    es2 = S.fromList $ edges g2
-
-isIso g1 g2 = (not . null) (graphIsos g1 g2)
-
--- graphAuts3 g = map fromPairs $ graphIsos g g
-
-
-
-
-{-
-graphAuts2 (G vs es) = graphAuts' [] 1 (bsgsSym vs) where
-    graphAuts' bs g ((b,t):bts) = concat [graphAuts' (b:bs) (h*g) bts | h <- M.elems t, isCompatible (b:bs) (h*g)]
-    -- has to be h*g not g*h - not quite sure why
-    graphAuts' _ g [] = [g]
-    isCompatible (b:bs) g = and [(e `S.member` es') == ((e -^ g) `S.member` es') | e <- [ [b',b] | b' <- bs] ] -- if bs ordered then b' < b
-    es' = S.fromList es
-
-graphAutsSGS2 (G vs es) = transversals [] (bsgsSym vs) where
-    transversals bs ((b,t):bts) = let t' = concat [take 1 $ dfs (b:bs) h bts | h <- tail (M.elems t), isCompatible (b:bs) h]
-                                  in t' ++ transversals (b:bs) bts
-    transversals _ [] = []
-    dfs bs g ((b,t):bts) = concat [dfs (b:bs) (h*g) bts | h <- M.elems t, isCompatible (b:bs) (h*g)]
-    dfs _ g [] = [g]
-    isCompatible (b:bs) g = and [(e `S.member` es') == ((e -^ g) `S.member` es') | e <- [ [b',b] | b' <- bs] ] -- if bs ordered then b' < b
-    es' = S.fromList es
-
--- base and strong generating set for Sym(xs)
-bsgsSym xs = [(x, t x) | x <- init xs]
-    where t x = M.fromList $ (x,p []) : [(y, p [[x,y]]) | y <- dropWhile (<= x) xs]
-
-bsgs_S n = bsgsSym [1..n]
 -}
+
 
Math/Test/TDesign.hs view
@@ -6,14 +6,10 @@ 
 import qualified Data.List as L
 
--- import PermutationGroup as P
 import Math.Algebra.Field.Base
 import Math.Algebra.Field.Extension
--- import Graph as G
--- import StronglyRegularGraph as SRG
 import Math.Combinatorics.Design as D
 import Math.Algebra.Group.SchreierSims as SS
--- import LinearAlgebra hiding ( (^-) )
 
 
 factorial n = product [1..n]
@@ -30,7 +26,12 @@     ,designParams (pg2 f2) == Just (2,(7,3,1))
     ,designParams (pg2 f3) == Just (2,(13,4,1))
     ,designParams (pg2 f4) == Just (2,(21,5,1))
+    ,designParams s_3_6_22 == Just (3,(22,6,1))
+    ,designParams (derivedDesign s_3_6_22 1) == Just (2,(21,5,1)) -- it is PG(2,F4)
+    ,designParams s_4_5_11 == Just (4,(11,5,1))
+    ,designParams (derivedDesign (derivedDesign s_4_5_11 0) 1) == Just (2,(9,3,1)) -- it is AG(2,F3)
     ]
+
 
 designAutTest = all (uncurry (==)) designAutTests
 
Math/Test/TFiniteGeometry.hs view
@@ -23,7 +23,10 @@     ,numFlatsPG 3 4 1 == length (flatsPG 3 f4 1)
     ,numFlatsPG 3 4 2 == length (flatsPG 3 f4 2)
     ,numFlatsPG 3 4 3 == length (flatsPG 3 f4 3)
-    ,(orderSGS $ incidenceAuts $ incidenceGraphAG 2 f2) == orderAff 2 2 * degree f2 
-    ,(orderSGS $ incidenceAuts $ incidenceGraphAG 2 f3) == orderAff 2 3 * degree f3 
-    ,(orderSGS $ incidenceAuts $ incidenceGraphAG 2 f4) == orderAff 2 4 * degree f4 
+    ,(orderSGS $ incidenceAuts $ incidenceGraphAG 2 f2) == orderAff 2 2 * toInteger (degree f2) 
+    ,(orderSGS $ incidenceAuts $ incidenceGraphAG 2 f3) == orderAff 2 3 * toInteger (degree f3)
+    ,(orderSGS $ incidenceAuts $ incidenceGraphAG 2 f4) == orderAff 2 4 * toInteger (degree f4)
+    ,(orderSGS $ incidenceAuts $ incidenceGraphPG 2 f2) == orderPGL 3 2 * toInteger (degree f2)
+    ,(orderSGS $ incidenceAuts $ incidenceGraphPG 2 f3) == orderPGL 3 3 * toInteger (degree f3)
+    ,(orderSGS $ incidenceAuts $ incidenceGraphPG 2 f4) == orderPGL 3 4 * toInteger (degree f4)
     ]
Math/Test/TGraph.hs view
@@ -60,6 +60,7 @@ -- by virtue of not even being vertex- or edge-transitive
 -- It is actually rather hard to find graphs which are vertex- and edge-transitive but not arc-transitive, but here is one
 -- Doyle, "A 27-vertex graph that is vertex-transitive and edge-transitive but not 1-transitive"
+-- http://en.wikipedia.org/wiki/Holt_graph
 doyleGraph = G gs es where
     relations = knuthBendix [("aaaaaaaaa",""), ("ccccccccc",""), ("aaaaaa","ccc"), ("cccccc","aaa"), ("ccccccccac","aaaa"), ("aaaaaaaaca","cccc")]
     gs = L.sort $ nfs ("ac",relations) -- all elements of the group, reduced to normal form