{-# LANGUAGE
CPP, BangPatterns,
ScopedTypeVariables, ExistentialQuantification,
DataKinds, KindSignatures, Rank2Types,
TypeOperators, TypeFamilies,
StandaloneDeriving #-}
module Math.Combinat.Groups.Braid where
import Data.Proxy
import GHC.TypeLits
import Control.Monad
import Data.List ( mapAccumL , foldl' )
import Data.Array.Unboxed
import Data.Array.ST
import Data.Array.IArray
import Data.Array.MArray
import Data.Array.Unsafe
import Data.Array.Base
import Control.Monad.ST
import System.Random
import Math.Combinat.ASCII
import Math.Combinat.Sign
import Math.Combinat.Helper
import Math.Combinat.TypeLevel
import Math.Combinat.Numbers.Series
import Math.Combinat.Permutations ( Permutation(..) )
import qualified Math.Combinat.Permutations as P
data BrGen
= Sigma !Int
| SigmaInv !Int
deriving (Eq,Ord,Show)
brGenIdx :: BrGen -> Int
brGenIdx g = case g of
Sigma i -> i
SigmaInv i -> i
brGenSign :: BrGen -> Sign
brGenSign g = case g of
Sigma _ -> Plus
SigmaInv _ -> Minus
brGenSignIdx :: BrGen -> (Sign,Int)
brGenSignIdx g = case g of
Sigma i -> (Plus ,i)
SigmaInv i -> (Minus,i)
invBrGen :: BrGen -> BrGen
invBrGen g = case g of
Sigma i -> SigmaInv i
SigmaInv i -> Sigma i
newtype Braid (n :: Nat) = Braid [BrGen] deriving (Show)
numberOfStrands :: KnownNat n => Braid n -> Int
numberOfStrands = fromInteger . natVal . braidProxy where
braidProxy :: Braid n -> Proxy n
braidProxy _ = Proxy
data SomeBraid = forall n. KnownNat n => SomeBraid (Braid n)
someBraid :: Int -> (forall (n :: Nat). KnownNat n => Braid n) -> SomeBraid
someBraid n polyBraid =
case snat of
SomeNat pxy -> SomeBraid (asProxyTypeOf1 polyBraid pxy)
where
snat = case someNatVal (fromIntegral n :: Integer) of
Just sn -> sn
Nothing -> error "someBraid: input is not a natural number"
withSomeBraid :: SomeBraid -> (forall n. KnownNat n => Braid n -> a) -> a
withSomeBraid sbraid f = case sbraid of SomeBraid braid -> f braid
mkBraid :: (forall n. KnownNat n => Braid n -> a) -> Int -> [BrGen] -> a
mkBraid f n w = y where
sb = someBraid n (Braid w)
y = withSomeBraid sb f
withBraid
:: Int
-> (forall (n :: Nat). KnownNat n => Braid n)
-> (forall (n :: Nat). KnownNat n => Braid n -> a)
-> a
withBraid n polyBraid f =
case snat of
SomeNat pxy -> f (asProxyTypeOf1 polyBraid pxy)
where
snat = case someNatVal (fromIntegral n :: Integer) of
Just sn -> sn
Nothing -> error "withBraid: input is not a natural number"
braidWord :: Braid n -> [BrGen]
braidWord (Braid gs) = gs
braidWordLength :: Braid n -> Int
braidWordLength (Braid gs) = length gs
extend :: (n1 <= n2) => Braid n1 -> Braid n2
extend (Braid gs) = Braid gs
freeReduceBraidWord :: Braid n -> Braid n
freeReduceBraidWord (Braid orig) = Braid (loop orig) where
loop w = case reduceStep w of
Nothing -> w
Just w' -> loop w'
reduceStep :: [BrGen] -> Maybe [BrGen]
reduceStep = go False where
go !changed w = case w of
(Sigma x : SigmaInv y : rest) | x==y -> go True rest
(SigmaInv x : Sigma y : rest) | x==y -> go True rest
(this : rest) -> liftM (this:) $ go changed rest
_ -> if changed then Just w else Nothing
sigma :: KnownNat n => Int -> Braid (n :: Nat)
sigma k = braid where
braid = if k > 0 && k < numberOfStrands braid
then Braid [Sigma k]
else error "sigma: braid generator index out of range"
sigmaInv :: KnownNat n => Int -> Braid (n :: Nat)
sigmaInv k = braid where
braid = if k > 0 && k < numberOfStrands braid
then Braid [SigmaInv k]
else error "sigma: braid generator index out of range"
doubleSigma :: KnownNat n => Int -> Int -> Braid (n :: Nat)
doubleSigma s t = braid where
n = numberOfStrands braid
braid
| s < 1 || s > n = error "doubleSigma: s index out of range"
| t < 1 || t > n = error "doubleSigma: t index out of range"
| s >= t = error "doubleSigma: s >= t"
| otherwise = Braid $
[ Sigma i | i<-[t-1,t-2..s] ] ++ [ SigmaInv i | i<-[s+1..t-1] ]
positiveWord :: KnownNat n => [Int] -> Braid (n :: Nat)
positiveWord idxs = braid where
braid = Braid (map gen idxs)
n = numberOfStrands braid
gen i = if i>0 && i<n then Sigma i else error "positiveWord: index out of range"
halfTwist :: KnownNat n => Braid n
halfTwist = braid where
braid = Braid $ map Sigma $ _halfTwist n
n = numberOfStrands braid
_halfTwist :: Int -> [Int]
_halfTwist n = gens where
gens = concat [ sub k | k<-[1..n-1] ]
sub k = [ j | j<-[n-1,n-2..k] ]
theGarsideBraid :: KnownNat n => Braid n
theGarsideBraid = halfTwist
tau :: KnownNat n => Braid n -> Braid n
tau braid@(Braid gens) = Braid (map f gens) where
n = numberOfStrands braid
f (Sigma i) = Sigma (n-i)
f (SigmaInv i) = SigmaInv (n-i)
tauPerm :: Permutation -> Permutation
tauPerm (Permutation arr) = Permutation $ listArray (1,n) [ (n+1) - arr!(n-i) | i<-[0..n-1] ] where
(1,n) = bounds arr
identity :: Braid n
identity = Braid []
inverse :: Braid n -> Braid n
inverse = Braid . reverse . map invBrGen . braidWord
compose :: Braid n -> Braid n -> Braid n
compose (Braid gs) (Braid hs) = freeReduceBraidWord $ Braid (gs++hs)
composeMany :: [Braid n] -> Braid n
composeMany = freeReduceBraidWord . Braid . concat . map braidWord
composeDontReduce :: Braid n -> Braid n -> Braid n
composeDontReduce (Braid gs) (Braid hs) = Braid (gs++hs)
isPureBraid :: KnownNat n => Braid n -> Bool
isPureBraid braid = (braidPermutation braid == P.identity n) where
n = numberOfStrands braid
braidPermutation :: KnownNat n => Braid n -> Permutation
braidPermutation braid@ (Braid gens) = perm where
n = numberOfStrands braid
perm = _braidPermutation n (map brGenIdx gens)
_braidPermutation :: Int -> [Int] -> Permutation
_braidPermutation n idxs = Permutation (runSTUArray action) where
action :: forall s. ST s (STUArray s Int Int)
action = do
arr <- newArray_ (1,n)
forM_ [1..n] $ \i -> writeArray arr i i
worker arr idxs
return arr
worker arr = go where
go [] = return arr
go (i:is) = do
a <- readArray arr i
b <- readArray arr (i+1)
writeArray arr i b
writeArray arr (i+1) a
go is
isPositiveBraidWord :: KnownNat n => Braid n -> Bool
isPositiveBraidWord (Braid gs) = all (isPlus . brGenSign) gs
isPermutationBraid :: KnownNat n => Braid n -> Bool
isPermutationBraid braid = isPositiveBraidWord braid && crosses where
crosses = and [ check i j | i<-[1..n-1], j<-[i+1..n] ]
check i j = zeroOrOne (lkMatrix ! (i,j))
zeroOrOne a = (a==1 || a==0)
lkMatrix = linkingMatrix braid
n = numberOfStrands braid
_isPermutationBraid :: Int -> [Int] -> Bool
_isPermutationBraid n gens = crosses where
crosses = and [ check i j | i<-[1..n-1], j<-[i+1..n] ]
check i j = zeroOrOne (lkMatrix ! (i,j))
zeroOrOne a = (a==1 || a==0)
lkMatrix = _linkingMatrix n $ map Sigma gens
permutationBraid :: KnownNat n => Permutation -> Braid n
permutationBraid perm = braid where
n1 = numberOfStrands braid
n2 = P.permutationSize perm
braid = if n1 == n2
then Braid (map Sigma $ _permutationBraid perm)
else error $ "permutationBraid: incompatible n: " ++ show n1 ++ " vs. " ++ show n2
_permutationBraid :: Permutation -> [Int]
_permutationBraid = concat . _permutationBraid'
_permutationBraid' :: Permutation -> [[Int]]
_permutationBraid' perm@(Permutation arr) = runST action where
(1,n) = bounds arr
action :: forall s. ST s [[Int]]
action = do
cfwd <- newArray_ (1,n) :: ST s (STUArray s Int Int)
cinv <- newArray_ (1,n) :: ST s (STUArray s Int Int)
forM_ [1..n] $ \j -> do
writeArray cfwd j j
writeArray cinv j j
let doSwap i = do
a <- readArray cinv i
b <- readArray cinv (i+1)
writeArray cinv i b
writeArray cinv (i+1) a
u <- readArray cfwd a
v <- readArray cfwd b
writeArray cfwd a v
writeArray cfwd b u
let worker phase
| phase >= n = return []
| otherwise = do
let tgt = (arr ! phase)
src <- readArray cfwd tgt
let this = [src-1,src-2..phase]
mapM_ doSwap $ this
rest <- worker (phase+1)
return (this:rest)
worker 1
linkingMatrix :: KnownNat n => Braid n -> UArray (Int,Int) Int
linkingMatrix braid@(Braid gens) = _linkingMatrix (numberOfStrands braid) gens where
_linkingMatrix :: Int -> [BrGen] -> UArray (Int,Int) Int
_linkingMatrix n gens = runSTUArray action where
action :: forall s. ST s (STUArray s (Int,Int) Int)
action = do
perm <- newArray_ (1,n) :: ST s (STUArray s Int Int)
forM_ [1..n] $ \i -> writeArray perm i i
let doSwap :: Int -> ST s ()
doSwap i = do
a <- readArray perm i
b <- readArray perm (i+1)
writeArray perm i b
writeArray perm (i+1) a
mat <- newArray ((1,1),(n,n)) 0 :: ST s (STUArray s (Int,Int) Int)
let doAdd :: Int -> Int -> Int -> ST s ()
doAdd i j pm1 = do
x <- readArray mat (i,j)
writeArray mat (i,j) (x+pm1)
writeArray mat (j,i) (x+pm1)
forM_ gens $ \g -> do
let (sgn,k) = brGenSignIdx g
u <- readArray perm k
v <- readArray perm (k+1)
doAdd u v (signValue sgn)
doSwap k
return mat
strandLinking :: KnownNat n => Braid n -> Int -> Int -> Int
strandLinking braid@(Braid gens) i0 j0
| i0 < 1 || i0 > n = error $ "strandLinkingNumber: invalid strand index i: " ++ show i0
| j0 < 1 || j0 > n = error $ "strandLinkingNumber: invalid strand index j: " ++ show j0
| i0 == j0 = 0
| otherwise = go i0 j0 gens
where
n = numberOfStrands braid
go !i !j [] = 0
go !i !j (g:gs)
| i == k && j == k+1 = s + go (i+1) (j-1) gs
| j == k && i == k+1 = s + go (i-1) (j+1) gs
| i == k = go (i+1) j gs
| i == k+1 = go (i-1) j gs
| j == k = go i (j+1) gs
| j == k+1 = go i (j-1) gs
| otherwise = go i j gs
where
(sgn,k) = brGenSignIdx g
s = signValue sgn
bronfmanH :: Int -> [Int]
bronfmanH n = bronfmanHsList !! n
bronfmanHsList :: [[Int]]
bronfmanHsList = list where
list = map go [0..]
go 0 = [1]
go n = sumSeries [ sgn i $ replicate (choose2 i) 0 ++ list !! (n-i) | i<-[1..n] ]
sgn i = if odd i then id else map negate
choose2 k = div (k*(k-1)) 2
expandBronfmanH :: Int -> [Int]
expandBronfmanH n = pseries' (convertPoly $ bronfmanH n) where
convertPoly (1:cs) = zip (map negate cs) [1..]
instance KnownNat n => DrawASCII (Braid n) where
ascii = horizBraidASCII
horizBraidASCII :: KnownNat n => Braid n -> ASCII
horizBraidASCII = horizBraidASCII' True
horizBraidASCII' :: KnownNat n => Bool -> Braid n -> ASCII
horizBraidASCII' flipped braid@(Braid gens) = final where
n = numberOfStrands braid
final = vExtendWith VTop 1 $ hCatTop allBlocks
allBlocks = prelude ++ middleBlocks ++ epilogue
prelude = [ numberBlock , spaceBlock , beginEndBlock ]
epilogue = [ beginEndBlock , spaceBlock , numberBlock' ]
middleBlocks = map block gens
block g = case g of
Sigma i -> block' i $ if flipped then over else under
SigmaInv i -> block' i $ if flipped then under else over
block' i middle = asciiFromLines $ drop 2 $ concat
$ replicate a horiz ++ [space3, middle] ++ replicate b horiz
where
(a,b) = if flipped then (n-i-1,i-1) else (i-1,n-i-1)
spaceBlock = transparentBox (1,n*3-2)
beginEndBlock = asciiFromLines $ drop 2 $ concat $ replicate n horiz
numberBlock = mkNumbers [1..n]
numberBlock' = mkNumbers $ P.fromPermutation $ braidPermutation braid
mkNumbers :: [Int] -> ASCII
mkNumbers list = vCatWith HRight (VSepSpaces 2) $ map asciiShow
$ (if flipped then reverse else id) $ list
under = [ "\\ /" , " / " , "/ \\" ]
over = [ "\\ /" , " \\ " , "/ \\" ]
horiz = [ " " , " " , "___" ]
space3 = [ " " , " " , " " ]
allPositiveBraidWords :: KnownNat n => Int -> [Braid n]
allPositiveBraidWords l = braids where
n = numberOfStrands (head braids)
braids = map Braid $ _allPositiveBraidWords n l
allBraidWords :: KnownNat n => Int -> [Braid n]
allBraidWords l = braids where
n = numberOfStrands (head braids)
braids = map Braid $ _allBraidWords n l
_allPositiveBraidWords :: Int -> Int -> [[BrGen]]
_allPositiveBraidWords n = go where
go 0 = [[]]
go k = [ Sigma i : rest | i<-[1..n-1] , rest <- go (k-1) ]
_allBraidWords :: Int -> Int -> [[BrGen]]
_allBraidWords n = go where
go 0 = [[]]
go k = [ gen : rest | gen <- gens , rest <- go (k-1) ]
gens = concat [ [ Sigma i , SigmaInv i ] | i<-[1..n-1] ]
randomBraidWord :: (RandomGen g, KnownNat n) => Int -> g -> (Braid n, g)
randomBraidWord len g = (braid, g') where
braid = Braid w
n = numberOfStrands braid
(w,g') = _randomBraidWord n len g
randomPositiveBraidWord :: (RandomGen g, KnownNat n) => Int -> g -> (Braid n, g)
randomPositiveBraidWord len g = (braid, g') where
braid = Braid w
n = numberOfStrands braid
(w,g') = _randomPositiveBraidWord n len g
randomPerturbBraidWord :: forall n g. (RandomGen g, KnownNat n) => Int -> Braid n -> g -> (Braid n, g)
randomPerturbBraidWord m braid@(Braid xs) g = (Braid word' , g') where
(word',g') = go m (length xs) xs g
n = numberOfStrands braid
rndE :: g -> ([BrGen],g)
rndE g = (e1,g'') where
(i , g' ) = randomR (1,n-1) g
(b , g'' ) = random g'
e0 = [SigmaInv i, Sigma i]
e1 = if b then reverse e0 else e0
brg s i = case s of { Plus -> Sigma i ; Minus -> SigmaInv i }
brginv s i = case s of { Plus -> SigmaInv i ; Minus -> Sigma i }
go :: Int -> Int -> [BrGen] -> g -> ([BrGen], g)
go !cnt !len !word !g
| cnt <= 0 = (word, g)
| len < 2 = let w' = if b1 then (e++word) else (word++e)
in continue g4 (len+2) w'
| abs (i-j) >= 2 = continue g4 len (as ++ v:u:bs)
| i == j && s/=t = continue g4 (len-2) (as ++ bs )
| abs (i-j) == 1 && s == t = let mid = if b1
then [ brg s j , brg s i , brg s j , brginv s i ]
else [ brginv s j , brg s i , brg s j , brg s i ]
in continue g4 (len+2) (as ++ mid ++ bs)
| otherwise = let mid = if b1
then (u : e ++ [v])
else if b2
then [u,v] ++ e
else e ++ [u,v]
in continue g4 (len+2) (as++(u:e)++[v]++bs)
where
(pos , g1 ) = randomR (0,len-2) g
(b1 :: Bool , g2 ) = random g1
(b2 :: Bool , g3 ) = random g2
(e , g4 ) = rndE g3
(as,u:v:bs) = splitAt pos word
(s,i) = brGenSignIdx u
(t,j) = brGenSignIdx v
continue g' len' word' = go (cnt-1) len' word' g'
withRandomBraidWord
:: RandomGen g
=> (forall n. KnownNat n => Braid n -> a)
-> Int
-> Int
-> g -> (a, g)
withRandomBraidWord f n len = runRand $ do
withSelectedM f (rand $ randomBraidWord len) n
withRandomPositiveBraidWord
:: RandomGen g
=> (forall n. KnownNat n => Braid n -> a)
-> Int
-> Int
-> g -> (a, g)
withRandomPositiveBraidWord f n len = runRand $ do
withSelectedM f (rand $ randomPositiveBraidWord len) n
_randomBraidWord
:: (RandomGen g)
=> Int
-> Int
-> g -> ([BrGen], g)
_randomBraidWord n len = runRand $ replicateM len $ do
k <- randChoose (1,n-1)
s <- randRoll
return $ case s of
Plus -> Sigma k
Minus -> SigmaInv k
_randomPositiveBraidWord
:: (RandomGen g)
=> Int
-> Int
-> g -> ([BrGen], g)
_randomPositiveBraidWord n len = runRand $ replicateM len $ do
liftM Sigma $ randChoose (1,n-1)