combinat-0.2.10.0: Math/Combinat/RootSystems.hs
-- | Naive (very inefficient) algorithm to generate the irreducible (Dynkin) root systems
--
-- Based on <https://en.wikipedia.org/wiki/Root_system>
{-# LANGUAGE BangPatterns, FlexibleInstances, TypeSynonymInstances, FlexibleContexts #-}
module Math.Combinat.RootSystems where
--------------------------------------------------------------------------------
import Control.Monad
import Data.Array
import Data.Set (Set)
import qualified Data.Set as Set
import Data.List
import Data.Ord
import Math.Combinat.Numbers.Primes
import Math.Combinat.Sets
--------------------------------------------------------------------------------
-- * Half-integers
-- | The type of half-integers (internally represented by their double)
--
-- TODO: refactor this into its own module
newtype HalfInt
= HalfInt Int
deriving (Eq,Ord)
half :: HalfInt
half = HalfInt 1
divByTwo :: Int -> HalfInt
divByTwo n = HalfInt n
mulByTwo :: HalfInt -> Int
mulByTwo (HalfInt n) = n
scaleBy :: Int -> HalfInt -> HalfInt
scaleBy k (HalfInt n) = HalfInt (k*n)
instance Show HalfInt where
show (HalfInt n) = case divMod n 2 of
(k,0) -> show k
(_,1) -> show n ++ "/2"
instance Num HalfInt where
fromInteger = HalfInt . (*2) . fromInteger
a + b = divByTwo $ mulByTwo a + mulByTwo b
a - b = divByTwo $ mulByTwo a - mulByTwo b
a * b = case divMod (mulByTwo a * mulByTwo b) 4 of
(k,0) -> HalfInt (2*k)
(k,2) -> HalfInt (2*k+1)
_ -> error "the result of multiplication is not a half-integer"
negate = divByTwo . negate . mulByTwo
signum = divByTwo . signum . mulByTwo
abs = divByTwo . abs . mulByTwo
--------------------------------------------------------------------------------
-- * Vectors of half-integers
type HalfVec = [HalfInt]
instance Num HalfVec where
fromInteger = error "HalfVec/fromInteger"
(+) = safeZip (+)
(-) = safeZip (-)
(*) = safeZip (*)
negate = map negate
abs = map abs
signum = map signum
scaleVec :: Int -> HalfVec -> HalfVec
scaleVec k = map (scaleBy k)
negateVec :: HalfVec -> HalfVec
negateVec = map negate
-- dotProd :: HalfVec -> HalfVec
-- dotProd xs ys = foldl' (+) 0 $ safeZip (*) xs ys
safeZip :: (a -> b -> c) -> [a] -> [b] -> [c]
safeZip f = go where
go (x:xs) (y:ys) = f x y : go xs ys
go [] [] = []
go _ _ = error "safeZip: the lists do not have equal length"
--------------------------------------------------------------------------------
-- * Dynkin diagrams
data Dynkin
= A !Int
| B !Int
| C !Int
| D !Int
| E6 | E7 | E8
| F4
| G2
deriving (Eq,Show)
--------------------------------------------------------------------------------
-- * The roots of root systems
-- | The ambient dimension of (our representation of the) system (length of the vector)
ambientDim :: Dynkin -> Int
ambientDim d = case d of
A n -> n+1 -- it's an n dimensional subspace of (n+1) dimensions
B n -> n
C n -> n
D n -> n
E6 -> 6
E7 -> 8 -- sublattice of E8 ?
E8 -> 8
F4 -> 4
G2 -> 3 -- it's a 2 dimensional subspace of 3 dimensions
simpleRootsOf :: Dynkin -> [HalfVec]
simpleRootsOf d =
case d of
A n -> [ e i - e (i+1) | i <- [1..n] ]
B n -> [ e i - e (i+1) | i <- [1..n-1] ] ++ [e n]
C n -> [ e i - e (i+1) | i <- [1..n-1] ] ++ [scaleVec 2 (e n)]
D n -> [ e i - e (i+1) | i <- [1..n-1] ] ++ [e (n-1) + e n]
E6 -> simpleRootsE6_123
E7 -> simpleRootsE7_12
E8 -> simpleRootsE8_even
F4 -> [ [ 1,-1, 0, 0]
, [ 0, 1,-1, 0]
, [ 0, 0, 1, 0]
, [-h,-h,-h,-h]
]
G2 -> [ [ 1,-1, 0]
, [-1, 2,-1]
]
where
h = half
n = ambientDim d
e :: Int -> HalfVec
e i = replicate (i-1) 0 ++ [1] ++ replicate (n-i) 0
positiveRootsOf :: Dynkin -> Set HalfVec
positiveRootsOf = positiveRoots . simpleRootsOf
negativeRootsOf :: Dynkin -> Set HalfVec
negativeRootsOf = Set.map negate . positiveRootsOf
allRootsOf :: Dynkin -> Set HalfVec
allRootsOf dynkin = Set.unions [ pos , neg ] where
simple = simpleRootsOf dynkin
pos = positiveRoots simple
neg = Set.map negate pos
--------------------------------------------------------------------------------
-- * Positive roots
-- | Finds a vector, which is hopefully not orthognal to any root
-- (generated by the given simple roots), and has positive dot product with each of them.
findPositiveHyperplane :: [HalfVec] -> [Double]
findPositiveHyperplane vs = w where
n = length (head vs)
w0 = map (fromIntegral . mulByTwo) (foldl1 (+) vs) :: [Double]
w = zipWith (+) w0 perturb
perturb = map small $ map fromIntegral $ take n primes
small :: Double -> Double
small x = x / (10**10)
positiveRoots :: [HalfVec] -> Set HalfVec
positiveRoots simples = Set.fromList pos where
roots = mirrorClosure simples
w = findPositiveHyperplane simples
pos = [ r | r <- Set.toList roots , dot4 r > 0 ] where
dot4 :: HalfVec -> Double
dot4 a = foldl' (+) 0 $ safeZip (*) w $ map (fromIntegral . mulByTwo) a
basisOfPositives :: Set HalfVec -> [HalfVec]
basisOfPositives set = Set.toList (Set.difference set set2) where
set2 = Set.fromList [ a + b | [a,b] <- choose 2 (Set.toList set) ]
--------------------------------------------------------------------------------
-- * Operations on half-integer vectors
-- | bracket b a = (a,b)/(a,a)
bracket :: HalfVec -> HalfVec -> HalfInt
bracket b a =
case divMod (2*a_dot_b) (a_dot_a) of
(n,0) -> divByTwo n
_ -> error "bracket: result is not a half-integer"
where
a_dot_b = foldl' (+) 0 $ safeZip (*) (map mulByTwo a) (map mulByTwo b)
a_dot_a = foldl' (+) 0 $ safeZip (*) (map mulByTwo a) (map mulByTwo a)
-- | mirror b a = b - 2*(a,b)/(a,a) * a
mirror :: HalfVec -> HalfVec -> HalfVec
mirror b a = b - scaleVec (mulByTwo $ bracket b a) a
-- | Cartan matrix of a list of (simple) roots
cartanMatrix :: [HalfVec] -> Array (Int,Int) Int
cartanMatrix list = array ((1,1),(n,n)) [ ((i,j), f i j) | i<-[1..n] , j<-[1..n] ] where
n = length list
arr = listArray (1,n) list
f !i !j = mulByTwo $ bracket (arr!j) (arr!i)
printMatrix :: Show a => Array (Int,Int) a -> IO ()
printMatrix arr = do
let ((1,1),(n,m)) = bounds arr
arr' = fmap show arr
let ks = [ 1 + maximum [ length (arr'!(i,j)) | i<-[1..n] ] | j<-[1..m] ]
forM_ [1..n] $ \i -> do
putStrLn $ flip concatMap [1..m] $ \j -> extendTo (ks!!(j-1)) $ arr' ! (i,j)
where
extendTo n s = replicate (n-length s) ' ' ++ s
--------------------------------------------------------------------------------
-- * Mirroring
-- | We mirror stuff until there is no more things happening
-- (very naive algorithm, but seems to work)
mirrorClosure :: [HalfVec] -> Set HalfVec
mirrorClosure = go . Set.fromList where
go set
| n' > n = go set'
| n'' > n = go set''
| otherwise = set
where
n = Set.size set
n' = Set.size set'
n'' = Set.size set''
set' = mirrorStep set
set'' = Set.union set (Set.map negateVec set)
mirrorStep :: Set HalfVec -> Set HalfVec
mirrorStep old = Set.union old new where
new = Set.fromList [ mirror b a | [a,b] <- choose 2 $ Set.toList old ]
--------------------------------------------------------------------------------
-- * E6, E7 and E8
-- | This is a basis of E6 as the subset of the even E8 root system
-- where the first three coordinates agree (they are consolidated
-- into the first coordinate here)
simpleRootsE6_123:: [HalfVec]
simpleRootsE6_123 = roots where
h = half
roots =
[ [-h,-h,-h,-h,-h,-h,-h,-h]
, [ h, h, h, h, h, h,-h,-h]
, [ 0, 0, 0, 0,-1, 0, 1, 0]
, [ 0, 0, 0, 0, 0, 0,-1, 1]
, [-h,-h,-h, h, h, h, h,-h]
, [ 0, 0, 0,-1, 1, 0, 0, 0]
]
-- | This is a basis of E8 as the subset of the even E8 root system
-- where the first two coordinates agree (they are consolidated
-- into the first coordinate here)
simpleRootsE7_12:: [HalfVec]
simpleRootsE7_12 = roots where
h = half
roots =
[ [-h,-h,-h,-h,-h,-h,-h,-h]
, [ h, h, h, h, h, h,-h,-h]
, [ h, h,-h,-h,-h,-h, h, h]
, [-h,-h, h, h,-h, h, h,-h]
, [ 0, 0, 0,-1, 1, 0, 0, 0]
, [ 0, 0,-1, 1, 0, 0, 0, 0]
, [ 0, 0, 0, 0, 0, 0,-1, 1]
]
-- | This is a basis of E7 as the subset of the even E8 root system
-- for which the sum of coordinates sum to zero
simpleRootsE7_diag :: [HalfVec]
simpleRootsE7_diag = roots where
roots = [ e i - e (i+1) | i <-[2..7] ] ++ [[h,h,h,h,-h,-h,-h,-h]]
h = half
n = 8
e :: Int -> HalfVec
e i = replicate (i-1) 0 ++ [1] ++ replicate (n-i) 0
simpleRootsE8_even :: [HalfVec]
simpleRootsE8_even = roots where
roots = [v1,v2,v3,v4,v5,v7,v8,v6]
[v1,v2,v3,v4,v5,v6,v7,v8] = roots0
roots0 = [ e i - e (i+1) | i <-[1..6] ] ++ [ e 6 + e 7 , replicate 8 (-h) ]
h = half
n = 8
e :: Int -> HalfVec
e i = replicate (i-1) 0 ++ [1] ++ replicate (n-i) 0
simpleRootsE8_odd :: [HalfVec]
simpleRootsE8_odd = roots where
roots = [ e i - e (i+1) | i <-[1..7] ] ++ [[-h,-h,-h,-h,-h , h,h,h]]
h = half
n = 8
e :: Int -> HalfVec
e i = replicate (i-1) 0 ++ [1] ++ replicate (n-i) 0
--------------------------------------------------------------------------------