polynomial-algebra-0.1: src/Math/Algebra/Polynomial/Monomial/Exterior/Indexed.hs
-- | Exterior monomials where the variable set
-- looks like @{x_1, x_2, ... , x_N}@
--
-- The internal representation is a bit vector
{-# LANGUAGE
CPP, BangPatterns, TypeFamilies, DataKinds, KindSignatures, ScopedTypeVariables,
FlexibleContexts
#-}
module Math.Algebra.Polynomial.Monomial.Exterior.Indexed where
--------------------------------------------------------------------------------
import Data.Maybe
import Data.List
import Data.Array.Unboxed
import Data.Ord
import Data.Bits
import Data.Typeable
import GHC.TypeLits
import Data.Proxy
import Data.Foldable as F
import qualified Data.Set as Set ; import Data.Set (Set) -- IntSet and
import qualified Data.Map as Map ; import Data.Map (Map) -- IntMap would be more efficient
import Math.Algebra.Polynomial.Class
import Math.Algebra.Polynomial.Pretty
import Math.Algebra.Polynomial.Misc
import Math.Algebra.Polynomial.FreeModule ( PartialMonoid(..) )
--------------------------------------------------------------------------------
-- * Exterior monomials
-- | Exterior monomials of the variables @x1,x2,...,xn@. The internal representation is
-- a bit vector encoded as an @Integer@
newtype Ext (var :: Symbol) (n :: Nat) = Ext Integer deriving (Eq,Ord,Show)
-- | Signed exterior monomial
data SgnExt (var :: Symbol) (n :: Nat) = SgnExt !Sign !(Ext var n) deriving (Eq,Ord,Show)
unExt :: Ext v n -> Integer
unExt (Ext k) = k
instance KnownNat n => PartialMonoid (SgnExt var n) where
pmUnit = emptySgnExt
pmAdd = mulSgnExt
instance KnownSymbol var => Pretty (Ext var n) where
pretty monom =
case [ showX i | i <- extToList monom ] of
[] -> "(1)"
xs -> intercalate "/\\" xs
where
v = extVar monom
showX !(Index i) = v ++ show i
instance KnownSymbol var => Pretty (SgnExt var n) where
pretty (SgnExt sgn ext) = case sgn of
Plus -> '+' : pretty ext
Minus -> '-' : pretty ext
-- | Name of the variables
extVar :: KnownSymbol var => Ext var n -> String
extVar = symbolVal . varProxy where
varProxy :: Ext var n -> Proxy var
varProxy _ = Proxy
-- | Number of variables
nOfExt :: KnownNat n => Ext var n -> Int
nOfExt = fromInteger . natVal . natProxy where
natProxy :: Ext var n -> Proxy n
natProxy _ = Proxy
nOfMbExt :: KnownNat n => Maybe (Ext var n) -> Int
nOfMbExt = fromInteger . natVal . natProxy where
natProxy :: Maybe (Ext var n) -> Proxy n
natProxy _ = Proxy
nOfSgnExt :: KnownNat n => SgnExt var n -> Int
nOfSgnExt = fromInteger . natVal . natProxy where
natProxy :: SgnExt var n -> Proxy n
natProxy _ = Proxy
nOfMbSgnExt :: KnownNat n => Maybe (SgnExt var n) -> Int
nOfMbSgnExt = fromInteger . natVal . natProxy where
natProxy :: Maybe (SgnExt var n) -> Proxy n
natProxy _ = Proxy
--------------------------------------------------------------------------------
-- * emptyness
emptyExt :: KnownNat n => Ext v n
emptyExt = Ext 0
emptySgnExt :: KnownNat n => SgnExt v n
emptySgnExt = SgnExt Plus (Ext 0)
isEmptyExt :: Ext v n -> Bool
isEmptyExt (Ext a) = (a==0)
--------------------------------------------------------------------------------
-- * normalization
isNormalExt :: KnownNat n => Ext v n -> Bool
isNormalExt ext@(Ext int) = shiftR int n == 0 where n = nOfExt ext
--------------------------------------------------------------------------------
-- * conversion
extFromList :: KnownNat n => [Index] -> Maybe (SgnExt v n)
extFromList list = result where
result
| Map.null multiset = Just emptySgnExt
| fst (Map.findMin multiset) < Index 1 = error "extFromList: index out of bounds (too small)"
| fst (Map.findMax multiset) > Index n = Nothing -- should this be an error ?
| any (>1) (Map.elems multiset) = Nothing
| otherwise = Just (SgnExt sgn $ Ext int)
n = nOfMbSgnExt result
multiset = Map.fromListWith (+) [ (idx,1) | idx <- list ]
perm = sortingPermutationAsc list
sgn = if odd (numberOfInversionsMerge perm) then Minus else Plus
int = sum' [ shiftL 1 (j-1) | Index j <- Map.keys multiset ]
extToList :: Ext v n -> [Index]
extToList (Ext int) = go 0 int where
go _ 0 = []
go !i !a = if testBit a 0
then Index (i+1) : go (i+1) (shiftR a 1)
else go (i+1) (shiftR a 1)
extFromSet :: KnownNat n => Set Index -> Maybe (Ext v n)
extFromSet set = result where
n = nOfMbExt result
result = case Set.lookupMax set of
Nothing -> Just emptyExt
Just (Index k) -> if k > n
then Nothing -- should this be an error ?
else if (Set.findMin set < Index 1)
then error "extFromSet: index smaller than 1"
else Just $ Ext $ sum' [ shiftL 1 (j-1) | Index j <- Set.toList set ]
extToSet :: Ext v n -> Set Index
extToSet = Set.fromList . extToList
--------------------------------------------------------------------------------
-- * creation
variableExt :: KnownNat n => Index -> Ext v n
variableExt (Index idx) = Ext $ shiftL 1 (idx-1)
--------------------------------------------------------------------------------
-- * multiplication
mulExt :: KnownNat n => Ext v n -> Ext v n -> Maybe (SgnExt v n)
mulExt x y = extFromList (extToList x ++ extToList y)
mulExtCoeff :: (Num c, KnownNat n) => Ext v n -> Ext v n -> Maybe (Ext v n, c)
mulExtCoeff x y = case mulExt x y of
Nothing -> Nothing
Just (SgnExt sgn z) -> case sgn of
Plus -> Just (z, 1)
Minus -> Just (z, -1)
mulSgnExt :: KnownNat n => SgnExt v n -> SgnExt v n -> Maybe (SgnExt v n)
mulSgnExt (SgnExt s x) (SgnExt t y) = case mulExt x y of
Nothing -> Nothing
Just (SgnExt u z) -> Just $ SgnExt (s `mappend` t `mappend` u) z
productExt :: (KnownNat n, Foldable f) => f (Ext v n) -> Maybe (SgnExt v n)
productExt t = go Plus (F.toList t) where
go !sgn list = case list of
[] -> Just (SgnExt sgn emptyExt)
[x] -> Just (SgnExt sgn x)
(x:y:rest) -> case mulExt x y of
Nothing -> Nothing
Just (SgnExt s z) -> go (sgn `mappend` s) (z:rest)
--------------------------------------------------------------------------------
[v1,v2,v3,v4,v5,v6] = [ variableExt (Index i) | i<-[1..6] ] :: [Ext "a" 7]
Just a = productExt [v1,v2,v3]
Just b = productExt [v4,v5,v6]
prop_graded_anticomm :: KnownNat n => Ext v n -> Ext v n -> Bool
prop_graded_anticomm x y
| isNothing mb1 && isNothing mb2 = True
| isJust mb1 && isJust mb2 = u == v && maybeFlip s == t
| otherwise = False
where
mb1 = x `mulExt` y
mb2 = y `mulExt` x
Just (SgnExt s u) = mb1
Just (SgnExt t v) = mb2
d1 = degreeExt x
d2 = degreeExt y
maybeFlip = if odd (d1*d2) then oppositeSign else id
prop_graded_anticomm_sgn :: KnownNat n => SgnExt v n -> SgnExt v n -> Bool
prop_graded_anticomm_sgn x y
| isNothing mb1 && isNothing mb2 = True
| isJust mb1 && isJust mb2 = u == v && maybeFlip s == t
| otherwise = False
where
mb1 = x `mulSgnExt` y
mb2 = y `mulSgnExt` x
Just (SgnExt s u) = mb1
Just (SgnExt t v) = mb2
d1 = degreeSgnExt x
d2 = degreeSgnExt y
maybeFlip = if odd (d1*d2) then oppositeSign else id
--------------------------------------------------------------------------------
-- * degree
degreeExt :: Ext v n -> Int
degreeExt (Ext k) = popCount k
degreeSgnExt :: SgnExt v n -> Int
degreeSgnExt (SgnExt sgn ext) = degreeExt ext
--------------------------------------------------------------------------------
-- * Permutations
--
newtype Permutation = Permutation (UArray Int Int) deriving (Eq,Ord)
toPermutationUnsafe :: [Int] -> Permutation
toPermutationUnsafe xs = Permutation perm where
n = length xs
perm = listArray (1,n) xs
sortingPermutationAsc :: Ord a => [a] -> Permutation
sortingPermutationAsc xs = toPermutationUnsafe (map fst sorted) where
sorted = sortBy (comparing snd) $ zip [1..] xs
{-
isEvenPermutation :: Permutation -> Bool
isEvenPermutation (Permutation perm) = res where
(1,n) = bounds perm
res = runST $ do
tag <- newArray (1,n) False
cycles <- unfoldM (step tag) 1
return $ even (sum cycles)
step :: STUArray s Int Bool -> Int -> ST s (Int,Maybe Int)
step tag k = do
cyclen <- worker tag k k 0
m <- next tag (k+1)
return (cyclen,m)
next :: STUArray s Int Bool -> Int -> ST s (Maybe Int)
next tag k = if k > n
then return Nothing
else readArray tag k >>= \b -> if b
then next tag (k+1)
else return (Just k)
worker :: STUArray s Int Bool -> Int -> Int -> Int -> ST s Int
worker tag k l cyclen = do
writeArray tag l True
let m = perm ! l
if m == k
then return cyclen
else worker tag k m (1+cyclen)
-}
isEvenPermutation :: Permutation -> Bool
isEvenPermutation = even . numberOfInversionsMerge
-- | Returns the number of inversions, using the merge-sort algorithm.
-- This should be @O(n*log(n))@
--
numberOfInversionsMerge :: Permutation -> Int
numberOfInversionsMerge (Permutation arr) = fst (sortCnt n $ elems arr) where
(_,n) = bounds arr
-- | First argument is length of the list.
-- Returns also the inversion count.
sortCnt :: Int -> [Int] -> (Int,[Int])
sortCnt 0 _ = (0,[] )
sortCnt 1 [x] = (0,[x])
sortCnt 2 [x,y] = if x>y then (1,[y,x]) else (0,[x,y])
sortCnt n xs = mergeCnt (sortCnt k us) (sortCnt l vs) where
k = div n 2
l = n - k
(us,vs) = splitAt k xs
mergeCnt :: (Int,[Int]) -> (Int,[Int]) -> (Int,[Int])
mergeCnt (!c,us) (!d,vs) = (c+d+e,ws) where
(e,ws) = go 0 us vs
go !k xs [] = ( k*length xs , xs )
go _ [] ys = ( 0 , ys)
go !k xxs@(x:xs) yys@(y:ys) = if x < y
then let (a,zs) = go k xs yys in (a+k, x:zs)
else let (a,zs) = go (k+1) xxs ys in (a , y:zs)
--------------------------------------------------------------------------------