combinat 0.2.7.1 → 0.2.7.2
raw patch · 24 files changed
+2024/−1817 lines, 24 filesdep ~base
Dependency ranges changed: base
Files
- Math/Combinat/ASCII.hs +300/−296
- Math/Combinat/Compositions.hs +4/−4
- Math/Combinat/FreeGroups.hs +10/−1
- Math/Combinat/Helper.hs +0/−2
- Math/Combinat/LatticePaths.hs +379/−368
- Math/Combinat/Numbers/Series.hs +1/−1
- Math/Combinat/Partitions/Integer.hs +3/−0
- Math/Combinat/Partitions/Multiset.hs +23/−23
- Math/Combinat/Partitions/NonCrossing.hs +205/−205
- Math/Combinat/Partitions/Plane.hs +116/−116
- Math/Combinat/Partitions/Set.hs +99/−99
- Math/Combinat/Partitions/Skew.hs +82/−67
- Math/Combinat/Partitions/Vector.hs +82/−82
- Math/Combinat/Permutations.hs +3/−3
- Math/Combinat/Sign.hs +48/−48
- Math/Combinat/Tableaux.hs +28/−0
- Math/Combinat/Tableaux/GelfandTsetlin.hs +341/−343
- Math/Combinat/Tableaux/GelfandTsetlin/Cone.hs +7/−3
- Math/Combinat/Tableaux/LittlewoodRichardson.hs +29/−2
- Math/Combinat/Tableaux/Skew.hs +180/−78
- Math/Combinat/Trees/Binary.hs +12/−8
- Math/Combinat/Trees/Nary.hs +4/−0
- combinat.cabal +2/−2
- svg/src/gen_figures.hs +66/−66
Math/Combinat/ASCII.hs view
@@ -1,296 +1,300 @@- --- | A mini-DSL for ASCII drawing of structures. --- --- --- From some structures there is also Graphviz and\/or @diagrams@ --- (<http://projects.haskell.org/diagrams>) visualization support --- (the latter in the separate libray @combinat-diagrams@). --- - -module Math.Combinat.ASCII where - --------------------------------------------------------------------------------- - -import Data.List - -import Math.Combinat.Helper - --------------------------------------------------------------------------------- --- * The basic type - --- | The type of a (rectangular) ASCII figure. --- Internally it is a list of lines of the same length plus the size. --- --- Note: The Show instance is pretty-printing, so that it\'s convenient in ghci. --- -data ASCII = ASCII - { asciiSize :: (Int,Int) - , asciiLines :: [String] - } - -instance Show ASCII where - show = asciiString - --- | An empty (0x0) rectangle -emptyRect :: ASCII -emptyRect = ASCII (0,0) [] - -asciiXSize, asciiYSize :: ASCII -> Int -asciiXSize = fst . asciiSize -asciiYSize = snd . asciiSize - -asciiString :: ASCII -> String -asciiString (ASCII sz ls) = unlines ls - -printASCII :: ASCII -> IO () -printASCII = putStrLn . asciiString - -asciiFromLines :: [String] -> ASCII -asciiFromLines ls = ASCII (x,y) (map f ls) where - y = length ls - x = maximum (map length ls) - f l = l ++ replicate (x - length l) ' ' - -asciiFromString :: String -> ASCII -asciiFromString = asciiFromLines . lines - --------------------------------------------------------------------------------- --- * Alignment - --- | Horizontal alignment -data HAlign - = HLeft - | HCenter - | HRight - deriving (Eq,Show) - --- | Vertical alignment -data VAlign - = VTop - | VCenter - | VBottom - deriving (Eq,Show) - -data Alignment = Align HAlign VAlign - --------------------------------------------------------------------------------- --- * Extension - --- | Extends an ASCII figure with spaces horizontally to the given width -hExtendTo :: HAlign -> Int -> ASCII -> ASCII -hExtendTo halign n0 rect@(ASCII (x,y) ls) = hExtendWith halign (max n0 x - x) rect - --- | Extends an ASCII figure with spaces vertically to the given height -vExtendTo :: VAlign -> Int -> ASCII -> ASCII -vExtendTo valign n0 rect@(ASCII (x,y) ls) = vExtendWith valign (max n0 y - y) rect - --- | Extend horizontally with the given number of spaces -hExtendWith :: HAlign -> Int -> ASCII -> ASCII -hExtendWith alignment d (ASCII (x,y) ls) = ASCII (x+d,y) (map f ls) where - f l = case alignment of - HLeft -> l ++ replicate d ' ' - HRight -> replicate d ' ' ++ l - HCenter -> replicate a ' ' ++ l ++ replicate (d-a) ' ' - a = div d 2 - --- | Extend vertically with the given number of empty lines -vExtendWith :: VAlign -> Int -> ASCII -> ASCII -vExtendWith valign d (ASCII (x,y) ls) = ASCII (x,y+d) (f ls) where - f ls = case valign of - VTop -> ls ++ replicate d emptyline - VBottom -> replicate d emptyline ++ ls - VCenter -> replicate a emptyline ++ ls ++ replicate (d-a) emptyline - a = div d 2 - emptyline = replicate x ' ' - --- | Horizontal indentation -hIndent :: Int -> ASCII -> ASCII -hIndent d = hExtendWith HRight d - --- | Vertical indentation -vIndent :: Int -> ASCII -> ASCII -vIndent d = vExtendWith VBottom d - --------------------------------------------------------------------------------- --- * Separators - --- | Horizontal separator -data HSep - = HSepEmpty -- ^ empty separator - | HSepSpaces Int -- ^ @n@ spaces - | HSepString String -- ^ some custom string, eg. @\" | \"@ - deriving Show - -hSepSize :: HSep -> Int -hSepSize hsep = case hsep of - HSepEmpty -> 0 - HSepSpaces k -> k - HSepString s -> length s - -hSepString :: HSep -> String -hSepString hsep = case hsep of - HSepEmpty -> "" - HSepSpaces k -> replicate k ' ' - HSepString s -> s - --- | Vertical separator -data VSep - = VSepEmpty -- ^ empty separator - | VSepSpaces Int -- ^ @n@ spaces - | VSepString [Char] -- ^ some custom list of characters, eg. @\" - \"@ (the characters are interpreted as below each other) - deriving Show - -vSepSize :: VSep -> Int -vSepSize vsep = case vsep of - VSepEmpty -> 0 - VSepSpaces k -> k - VSepString s -> length s - -vSepString :: VSep -> [Char] -vSepString vsep = case vsep of - VSepEmpty -> [] - VSepSpaces k -> replicate k ' ' - VSepString s -> s - --------------------------------------------------------------------------------- --- * Padding - --- | Horizontally pads with the given number of spaces, on both sides -hPad :: Int -> ASCII -> ASCII -hPad k (ASCII (x,y) ls) = ASCII (x+2*k,y) (map f ls) where - f l = pad ++ l ++ pad - pad = replicate k ' ' - --- | Vertically pads with the given number of empty lines, on both sides -vPad :: Int -> ASCII -> ASCII -vPad k (ASCII (x,y) ls) = ASCII (x,y+2*k) (pad ++ ls ++ pad) where - pad = replicate k (replicate x ' ') - --- | Pads by single empty lines vertically and two spaces horizontally -pad :: ASCII -> ASCII -pad = vPad 1 . hPad 2 - --------------------------------------------------------------------------------- --- * Concatenation - --- | Horizontal concatenation -hCatWith :: VAlign -> HSep -> [ASCII] -> ASCII -hCatWith valign hsep rects = ASCII (x',maxy) final where - n = length rects - maxy = maximum [ y | ASCII (_,y) _ <- rects ] - xsz = [ x | ASCII (x,_) _ <- rects ] - sep = hSepString hsep - sepx = length sep - rects1 = map (vExtendTo valign maxy) rects - x' = sum' xsz + (n-1)*sepx - final = map (intercalate sep) $ transpose (map asciiLines rects1) - --- | Vertical concatenation -vCatWith :: HAlign -> VSep -> [ASCII] -> ASCII -vCatWith halign vsep rects = ASCII (maxx,y') final where - n = length rects - maxx = maximum [ x | ASCII (x,_) _ <- rects ] - ysz = [ y | ASCII (_,y) _ <- rects ] - sepy = vSepSize vsep - fullsep = transpose (replicate maxx $ vSepString vsep) :: [String] - rects1 = map (hExtendTo halign maxx) rects - y' = sum' ysz + (n-1)*sepy - final = intercalate fullsep $ map asciiLines rects1 - --------------------------------------------------------------------------------- --- * Tabulate - -tabulate :: (HAlign,VAlign) -> (HSep,VSep) -> [[ASCII]] -> ASCII -tabulate (halign,valign) (hsep,vsep) rects0 = final where - n = length rects0 - m = maximum (map length rects0) - rects1 = map (\rs -> rs ++ replicate (m - length rs) emptyRect) rects0 - ys = map (\rs -> maximum (map asciiYSize rs)) rects1 - xs = map (\rs -> maximum (map asciiXSize rs)) (transpose rects1) - rects2 = map (\rs -> [ hExtendTo halign x r | (x,r ) <- zip xs rs ]) rects1 - rects3 = [ map (vExtendTo valign y) rs | (y,rs) <- zip ys rects2 ] - final = vCatWith HLeft vsep - $ map (hCatWith VTop hsep) rects3 - --- | Order of elements in a matrix -data MatrixOrder - = RowMajor - | ColMajor - deriving (Eq,Ord,Show,Read) - --- | Automatically tabulates ASCII rectangles. --- -autoTabulate - :: MatrixOrder -- ^ whether to use row-major or column-major ordering of the elements - -> Either Int Int -- ^ @(Right x)@ creates x columns, while @(Left y)$ creates y rows - -> [ASCII] -- ^ list of ASCII rectangles - -> ASCII -autoTabulate mtxorder ei list = final where - - final = tabulate (HLeft,VBottom) (HSepSpaces 2,VSepSpaces 1) rects - - n = length list - - rects = case ei of - - Left y -> case mtxorder of - ColMajor -> transpose (parts y list) - RowMajor -> invparts y list - - Right x -> case mtxorder of - ColMajor -> transpose (invparts x list) - RowMajor -> parts x list - - transposeIf b = if b then transpose else id - - -- chops into parts (the last one can be smaller) - parts d = go where - go [] = [] - go xs = take d xs : go (drop d xs) - - invparts d xs = parts' ds xs where - (q,r) = divMod n d - ds = replicate r (q+1) ++ replicate (d-r) q - - parts' ds xs = go ds xs where - go _ [] = [] - go [] _ = [] - go (d:ds) xs = take d xs : go ds (drop d xs) - --------------------------------------------------------------------------------- --- * Captions - --- | Adds a caption to the bottom, with default settings. -caption :: String -> ASCII -> ASCII -caption = caption' False HLeft - --- | Adds a caption to the bottom. The @Bool@ flag specifies whether to add an empty between --- the caption and the figure -caption' :: Bool -> HAlign -> String -> ASCII -> ASCII -caption' emptyline halign str rect = vCatWith halign sep [rect,capt] where - sep = if emptyline then VSepSpaces 1 else VSepEmpty - capt = asciiFromString str - --------------------------------------------------------------------------------- --- * Testing \/ miscellanea - --- | An ASCII box of the given size -asciiBox :: (Int,Int) -> ASCII -asciiBox (x,y) = ASCII (max x 2, max y 2) (h : replicate (y-2) m ++ [h]) where - h = "+" ++ replicate (x-2) '-' ++ "+" - m = "|" ++ replicate (x-2) ' ' ++ "|" - --- | An \"rounded\" ASCII box of the given size -roundedAsciiBox :: (Int,Int) -> ASCII -roundedAsciiBox (x,y) = ASCII (max x 2, max y 2) (a : replicate (y-2) m ++ [b]) where - a = "/" ++ replicate (x-2) '-' ++ "\\" - m = "|" ++ replicate (x-2) ' ' ++ "|" - b = "\\" ++ replicate (x-2) '-' ++ "/" - -asciiNumber :: Int -> ASCII -asciiNumber = asciiShow - -asciiShow :: Show a => a -> ASCII -asciiShow = asciiFromLines . (:[]) . show - --------------------------------------------------------------------------------- ++-- | A mini-DSL for ASCII drawing of structures.+--+--+-- From some structures there is also Graphviz and\/or @diagrams@ +-- (<http://projects.haskell.org/diagrams>) visualization support +-- (the latter in the separate libray @combinat-diagrams@).+--++module Math.Combinat.ASCII where++--------------------------------------------------------------------------------++import Data.List++import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * The basic type++-- | The type of a (rectangular) ASCII figure. +-- Internally it is a list of lines of the same length plus the size.+--+-- Note: The Show instance is pretty-printing, so that it\'s convenient in ghci.+--+data ASCII = ASCII + { asciiSize :: (Int,Int) + , asciiLines :: [String]+ }++-- | A type class to have a simple way to draw things +class DrawASCII a where+ ascii :: a -> ASCII++instance Show ASCII where+ show = asciiString++-- | An empty (0x0) rectangle+emptyRect :: ASCII+emptyRect = ASCII (0,0) []++asciiXSize, asciiYSize :: ASCII -> Int+asciiXSize = fst . asciiSize+asciiYSize = snd . asciiSize++asciiString :: ASCII -> String+asciiString (ASCII sz ls) = unlines ls++printASCII :: ASCII -> IO ()+printASCII = putStrLn . asciiString++asciiFromLines :: [String] -> ASCII+asciiFromLines ls = ASCII (x,y) (map f ls) where+ y = length ls+ x = maximum (map length ls)+ f l = l ++ replicate (x - length l) ' '++asciiFromString :: String -> ASCII+asciiFromString = asciiFromLines . lines++--------------------------------------------------------------------------------+-- * Alignment++-- | Horizontal alignment+data HAlign + = HLeft + | HCenter + | HRight + deriving (Eq,Show)++-- | Vertical alignment+data VAlign + = VTop + | VCenter + | VBottom + deriving (Eq,Show)++data Alignment = Align HAlign VAlign+ +--------------------------------------------------------------------------------+-- * Extension++-- | Extends an ASCII figure with spaces horizontally to the given width +hExtendTo :: HAlign -> Int -> ASCII -> ASCII+hExtendTo halign n0 rect@(ASCII (x,y) ls) = hExtendWith halign (max n0 x - x) rect+ +-- | Extends an ASCII figure with spaces vertically to the given height+vExtendTo :: VAlign -> Int -> ASCII -> ASCII+vExtendTo valign n0 rect@(ASCII (x,y) ls) = vExtendWith valign (max n0 y - y) rect++-- | Extend horizontally with the given number of spaces+hExtendWith :: HAlign -> Int -> ASCII -> ASCII+hExtendWith alignment d (ASCII (x,y) ls) = ASCII (x+d,y) (map f ls) where+ f l = case alignment of+ HLeft -> l ++ replicate d ' ' + HRight -> replicate d ' ' ++ l+ HCenter -> replicate a ' ' ++ l ++ replicate (d-a) ' ' + a = div d 2++-- | Extend vertically with the given number of empty lines+vExtendWith :: VAlign -> Int -> ASCII -> ASCII+vExtendWith valign d (ASCII (x,y) ls) = ASCII (x,y+d) (f ls) where+ f ls = case valign of+ VTop -> ls ++ replicate d emptyline + VBottom -> replicate d emptyline ++ ls+ VCenter -> replicate a emptyline ++ ls ++ replicate (d-a) emptyline+ a = div d 2+ emptyline = replicate x ' '++-- | Horizontal indentation+hIndent :: Int -> ASCII -> ASCII+hIndent d = hExtendWith HRight d++-- | Vertical indentation+vIndent :: Int -> ASCII -> ASCII+vIndent d = vExtendWith VBottom d++--------------------------------------------------------------------------------+-- * Separators++-- | Horizontal separator+data HSep + = HSepEmpty -- ^ empty separator+ | HSepSpaces Int -- ^ @n@ spaces+ | HSepString String -- ^ some custom string, eg. @\" | \"@+ deriving Show++hSepSize :: HSep -> Int+hSepSize hsep = case hsep of+ HSepEmpty -> 0+ HSepSpaces k -> k+ HSepString s -> length s++hSepString :: HSep -> String+hSepString hsep = case hsep of+ HSepEmpty -> ""+ HSepSpaces k -> replicate k ' '+ HSepString s -> s++-- | Vertical separator+data VSep + = VSepEmpty -- ^ empty separator+ | VSepSpaces Int -- ^ @n@ spaces+ | VSepString [Char] -- ^ some custom list of characters, eg. @\" - \"@ (the characters are interpreted as below each other)+ deriving Show++vSepSize :: VSep -> Int+vSepSize vsep = case vsep of+ VSepEmpty -> 0+ VSepSpaces k -> k+ VSepString s -> length s++vSepString :: VSep -> [Char]+vSepString vsep = case vsep of+ VSepEmpty -> []+ VSepSpaces k -> replicate k ' '+ VSepString s -> s++--------------------------------------------------------------------------------+-- * Padding++-- | Horizontally pads with the given number of spaces, on both sides+hPad :: Int -> ASCII -> ASCII+hPad k (ASCII (x,y) ls) = ASCII (x+2*k,y) (map f ls) where+ f l = pad ++ l ++ pad + pad = replicate k ' '++-- | Vertically pads with the given number of empty lines, on both sides+vPad :: Int -> ASCII -> ASCII+vPad k (ASCII (x,y) ls) = ASCII (x,y+2*k) (pad ++ ls ++ pad) where+ pad = replicate k (replicate x ' ')++-- | Pads by single empty lines vertically and two spaces horizontally+pad :: ASCII -> ASCII+pad = vPad 1 . hPad 2 ++--------------------------------------------------------------------------------+-- * Concatenation++-- | Horizontal concatenation+hCatWith :: VAlign -> HSep -> [ASCII] -> ASCII+hCatWith valign hsep rects = ASCII (x',maxy) final where+ n = length rects+ maxy = maximum [ y | ASCII (_,y) _ <- rects ]+ xsz = [ x | ASCII (x,_) _ <- rects ]+ sep = hSepString hsep+ sepx = length sep+ rects1 = map (vExtendTo valign maxy) rects+ x' = sum' xsz + (n-1)*sepx+ final = map (intercalate sep) $ transpose (map asciiLines rects1)++-- | Vertical concatenation+vCatWith :: HAlign -> VSep -> [ASCII] -> ASCII+vCatWith halign vsep rects = ASCII (maxx,y') final where+ n = length rects+ maxx = maximum [ x | ASCII (x,_) _ <- rects ]+ ysz = [ y | ASCII (_,y) _ <- rects ]+ sepy = vSepSize vsep+ fullsep = transpose (replicate maxx $ vSepString vsep) :: [String]+ rects1 = map (hExtendTo halign maxx) rects+ y' = sum' ysz + (n-1)*sepy+ final = intercalate fullsep $ map asciiLines rects1++--------------------------------------------------------------------------------+-- * Tabulate++tabulate :: (HAlign,VAlign) -> (HSep,VSep) -> [[ASCII]] -> ASCII+tabulate (halign,valign) (hsep,vsep) rects0 = final where+ n = length rects0+ m = maximum (map length rects0)+ rects1 = map (\rs -> rs ++ replicate (m - length rs) emptyRect) rects0+ ys = map (\rs -> maximum (map asciiYSize rs)) rects1+ xs = map (\rs -> maximum (map asciiXSize rs)) (transpose rects1)+ rects2 = map (\rs -> [ hExtendTo halign x r | (x,r ) <- zip xs rs ]) rects1+ rects3 = [ map (vExtendTo valign y) rs | (y,rs) <- zip ys rects2 ] + final = vCatWith HLeft vsep + $ map (hCatWith VTop hsep) rects3++-- | Order of elements in a matrix+data MatrixOrder + = RowMajor+ | ColMajor+ deriving (Eq,Ord,Show,Read)++-- | Automatically tabulates ASCII rectangles.+--+autoTabulate + :: MatrixOrder -- ^ whether to use row-major or column-major ordering of the elements+ -> Either Int Int -- ^ @(Right x)@ creates x columns, while @(Left y)$ creates y rows+ -> [ASCII] -- ^ list of ASCII rectangles+ -> ASCII+autoTabulate mtxorder ei list = final where+ + final = tabulate (HLeft,VBottom) (HSepSpaces 2,VSepSpaces 1) rects ++ n = length list++ rects = case ei of++ Left y -> case mtxorder of+ ColMajor -> transpose (parts y list)+ RowMajor -> invparts y list++ Right x -> case mtxorder of+ ColMajor -> transpose (invparts x list)+ RowMajor -> parts x list++ transposeIf b = if b then transpose else id++ -- chops into parts (the last one can be smaller)+ parts d = go where+ go [] = []+ go xs = take d xs : go (drop d xs)++ invparts d xs = parts' ds xs where+ (q,r) = divMod n d+ ds = replicate r (q+1) ++ replicate (d-r) q++ parts' ds xs = go ds xs where+ go _ [] = [] + go [] _ = []+ go (d:ds) xs = take d xs : go ds (drop d xs)++--------------------------------------------------------------------------------+-- * Captions++-- | Adds a caption to the bottom, with default settings.+caption :: String -> ASCII -> ASCII+caption = caption' False HLeft++-- | Adds a caption to the bottom. The @Bool@ flag specifies whether to add an empty between +-- the caption and the figure+caption' :: Bool -> HAlign -> String -> ASCII -> ASCII+caption' emptyline halign str rect = vCatWith halign sep [rect,capt] where+ sep = if emptyline then VSepSpaces 1 else VSepEmpty + capt = asciiFromString str++--------------------------------------------------------------------------------+-- * Testing \/ miscellanea++-- | An ASCII box of the given size+asciiBox :: (Int,Int) -> ASCII+asciiBox (x,y) = ASCII (max x 2, max y 2) (h : replicate (y-2) m ++ [h]) where+ h = "+" ++ replicate (x-2) '-' ++ "+"+ m = "|" ++ replicate (x-2) ' ' ++ "|"++-- | An \"rounded\" ASCII box of the given size+roundedAsciiBox :: (Int,Int) -> ASCII+roundedAsciiBox (x,y) = ASCII (max x 2, max y 2) (a : replicate (y-2) m ++ [b]) where+ a = "/" ++ replicate (x-2) '-' ++ "\\"+ m = "|" ++ replicate (x-2) ' ' ++ "|"+ b = "\\" ++ replicate (x-2) '-' ++ "/"++asciiNumber :: Int -> ASCII+asciiNumber = asciiShow++asciiShow :: Show a => a -> ASCII+asciiShow = asciiFromLines . (:[]) . show++--------------------------------------------------------------------------------
Math/Combinat/Compositions.hs view
@@ -70,13 +70,13 @@ => a -- ^ length -> a -- ^ sum -> [[Int]]-compositions1 len' d' - | len > d = []+compositions1 len d + | len > d = [] | otherwise = map plus1 $ compositions len (d-len) where plus1 = map (+1)- len = fromIntegral len'- d = fromIntegral d'+ -- len = fromIntegral len'+ -- d = fromIntegral d' countCompositions1 :: Integral a => a -> a -> Integer countCompositions1 len d = countCompositions len (d-len)
Math/Combinat/FreeGroups.hs view
@@ -2,10 +2,19 @@ -- | Words in free groups (and free powers of cyclic groups). -- This module is not re-exported by "Math.Combinat" ---{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE CPP, PatternGuards #-} module Math.Combinat.FreeGroups where --------------------------------------------------------------------------------++-- new Base exports "Word" from Data.Word...+#ifdef MIN_VERSION_base+#if MIN_VERSION_base(4,7,1)+import Prelude hiding ( Word )+#endif+#elif __GLASGOW_HASKELL__ >= 709+import Prelude hiding ( Word )+#endif import Data.Char ( chr ) import Data.List ( mapAccumL )
Math/Combinat/Helper.hs view
@@ -24,8 +24,6 @@ -------------------------------------------------------------------------------- -- * pairs -{-# SPECIALIZE swap :: (a ,a ) -> (a ,a ) #-}-{-# SPECIALIZE swap :: (Int,Int) -> (Int,Int) #-} swap :: (a,b) -> (b,a) swap (x,y) = (y,x)
Math/Combinat/LatticePaths.hs view
@@ -1,368 +1,379 @@- --- | Dyck paths, lattice paths, etc --- --- For example, the following figure represents a Dyck path of height 5 with 3 zero-touches (not counting the starting point, --- but counting the endpoint) and 7 peaks: --- --- <<svg/dyck_path.svg>> --- - -{-# LANGUAGE BangPatterns #-} -module Math.Combinat.LatticePaths where - --------------------------------------------------------------------------------- - -import Data.List -import System.Random - -import Math.Combinat.Numbers -import Math.Combinat.Trees.Binary -import Math.Combinat.ASCII as ASCII - --------------------------------------------------------------------------------- --- * Types - --- | A step in a lattice path -data Step - = UpStep -- ^ the step @(1,1)@ - | DownStep -- ^ the step @(1,-1)@ - deriving (Eq,Ord,Show) - --- | A lattice path is a path using only the allowed steps, never going below the zero level line @y=0@. --- --- Note that if you rotate such a path by 45 degrees counterclockwise, --- you get a path which uses only the steps @(1,0)@ and @(0,1)@, and stays --- above the main diagonal (hence the name, we just use a different convention). --- -type LatticePath = [Step] - --------------------------------------------------------------------------------- --- * ascii drawing of paths - --- | Draws the path into a list of lines. For example try: --- --- > autotabulate RowMajor (Right 5) (map asciiPath $ dyckPaths 4) --- -asciiPath :: LatticePath -> ASCII -asciiPath p = asciiFromLines $ transpose (go 0 p) where - - go !h [] = [] - go !h (x:xs) = case x of - UpStep -> ee h x : go (h+1) xs - DownStep -> ee (h-1) x : go (h-1) xs - - maxh = pathHeight p - - ee h x = replicate (maxh-h-1) ' ' ++ [ch x] ++ replicate h ' ' - ch x = case x of - UpStep -> '/' - DownStep -> '\\' - --------------------------------------------------------------------------------- --- * elementary queries - --- | A lattice path is called \"valid\", if it never goes below the @y=0@ line. -isValidPath :: LatticePath -> Bool -isValidPath = go 0 where - go !y [] = y>=0 - go !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 } - in if y'<0 then False - else go y' ts - --- | A Dyck path is a lattice path whose last point lies on the @y=0@ line -isDyckPath :: LatticePath -> Bool -isDyckPath = go 0 where - go !y [] = y==0 - go !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 } - in if y'<0 then False - else go y' ts - --- | Maximal height of a lattice path -pathHeight :: LatticePath -> Int -pathHeight = go 0 0 where - go !h !y [] = h - go !h !y (t:ts) = case t of - UpStep -> go (max h (y+1)) (y+1) ts - DownStep -> go h (y-1) ts - --- | Endpoint of a lattice path, which starts from @(0,0)@. -pathEndpoint :: LatticePath -> (Int,Int) -pathEndpoint = go 0 0 where - go !x !y [] = (x,y) - go !x !y (t:ts) = case t of - UpStep -> go (x+1) (y+1) ts - DownStep -> go (x+1) (y-1) ts - --- | Returns the coordinates of the path (excluding the starting point @(0,0)@, but including --- the endpoint) -pathCoordinates :: LatticePath -> [(Int,Int)] -pathCoordinates = go 0 0 where - go _ _ [] = [] - go !x !y (t:ts) = let x' = x + 1 - y' = case t of { UpStep -> y+1 ; DownStep -> y-1 } - in (x',y') : go x' y' ts - --- | Counts the up-steps -pathNumberOfUpSteps :: LatticePath -> Int -pathNumberOfUpSteps = fst . pathNumberOfUpDownSteps - --- | Counts the down-steps -pathNumberOfDownSteps :: LatticePath -> Int -pathNumberOfDownSteps = snd . pathNumberOfUpDownSteps - --- | Counts both the up-steps and down-steps -pathNumberOfUpDownSteps :: LatticePath -> (Int,Int) -pathNumberOfUpDownSteps = go 0 0 where - go !u !d (p:ps) = case p of - UpStep -> go (u+1) d ps - DownStep -> go u (d+1) ps - go !u !d [] = (u,d) - --------------------------------------------------------------------------------- --- * path-specific queries - --- | Number of peaks of a path (excluding the endpoint) -pathNumberOfPeaks :: LatticePath -> Int -pathNumberOfPeaks = go 0 where - go !k (x:xs@(y:_)) = go (if x==UpStep && y==DownStep then k+1 else k) xs - go !k [x] = k - go !k [ ] = k - --- | Number of points on the path which touch the @y=0@ zero level line --- (excluding the starting point @(0,0)@, but including the endpoint; that is, for Dyck paths it this is always positive!). -pathNumberOfZeroTouches :: LatticePath -> Int -pathNumberOfZeroTouches = pathNumberOfTouches' 0 - --- | Number of points on the path which touch the level line at height @h@ --- (excluding the starting point @(0,0)@, but including the endpoint). -pathNumberOfTouches' - :: Int -- ^ @h@ = the touch level - -> LatticePath -> Int -pathNumberOfTouches' h = go 0 0 0 where - go !cnt _ _ [] = cnt - go !cnt !x !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 } - cnt' = if y'==h then cnt+1 else cnt - in go cnt' (x+1) y' ts - --------------------------------------------------------------------------------- --- * Dyck paths - --- | @dyckPaths m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@. --- --- Remark: Dyck paths are obviously in bijection with nested parentheses, and thus --- also with binary trees. --- --- Order is reverse lexicographical: --- --- > sort (dyckPaths m) == reverse (dyckPaths m) --- -dyckPaths :: Int -> [LatticePath] -dyckPaths = map nestedParensToDyckPath . nestedParentheses - --- | @dyckPaths m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@. --- --- > sort (dyckPathsNaive m) == sort (dyckPaths m) --- --- Naive recursive algorithm, order is ad-hoc --- -dyckPathsNaive :: Int -> [LatticePath] -dyckPathsNaive = worker where - worker 0 = [[]] - worker m = as ++ bs where - as = [ bracket p | p <- worker (m-1) ] - bs = [ bracket p ++ q | k <- [1..m-1] , p <- worker (k-1) , q <- worker (m-k) ] - bracket p = UpStep : p ++ [DownStep] - --- | The number of Dyck paths from @(0,0)@ to @(2m,0)@ is simply the m\'th Catalan number. -countDyckPaths :: Int -> Integer -countDyckPaths m = catalan m - --- | The trivial bijection -nestedParensToDyckPath :: [Paren] -> LatticePath -nestedParensToDyckPath = map f where - f p = case p of { LeftParen -> UpStep ; RightParen -> DownStep } - --- | The trivial bijection in the other direction -dyckPathToNestedParens :: LatticePath -> [Paren] -dyckPathToNestedParens = map g where - g s = case s of { UpStep -> LeftParen ; DownStep -> RightParen } - --------------------------------------------------------------------------------- --- * Bounded Dyck paths - --- | @boundedDyckPaths h m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ whose height is at most @h@. --- Synonym for 'boundedDyckPathsNaive'. --- -boundedDyckPaths - :: Int -- ^ @h@ = maximum height - -> Int -- ^ @m@ = half-length - -> [LatticePath] -boundedDyckPaths = boundedDyckPathsNaive - --- | @boundedDyckPathsNaive h m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ whose height is at most @h@. --- --- > sort (boundedDyckPaths h m) == sort [ p | p <- dyckPaths m , pathHeight p <= h ] --- > sort (boundedDyckPaths m m) == sort (dyckPaths m) --- --- Naive recursive algorithm, resulting order is pretty ad-hoc. --- -boundedDyckPathsNaive - :: Int -- ^ @h@ = maximum height - -> Int -- ^ @m@ = half-length - -> [LatticePath] -boundedDyckPathsNaive = worker where - worker !h !m - | h<0 = [] - | m<0 = [] - | m==0 = [[]] - | h<=0 = [] - | otherwise = as ++ bs - where - bracket p = UpStep : p ++ [DownStep] - as = [ bracket p | p <- boundedDyckPaths (h-1) (m-1) ] - bs = [ bracket p ++ q | k <- [1..m-1] , p <- boundedDyckPaths (h-1) (k-1) , q <- boundedDyckPaths h (m-k) ] - --------------------------------------------------------------------------------- --- * More general lattice paths - --- | All lattice paths from @(0,0)@ to @(x,y)@. Clearly empty unless @x-y@ is even. --- Synonym for 'latticePathsNaive' --- -latticePaths :: (Int,Int) -> [LatticePath] -latticePaths = latticePathsNaive - --- | All lattice paths from @(0,0)@ to @(x,y)@. Clearly empty unless @x-y@ is even. --- --- Note that --- --- > sort (dyckPaths n) == sort (latticePaths (0,2*n)) --- --- Naive recursive algorithm, resulting order is pretty ad-hoc. --- -latticePathsNaive :: (Int,Int) -> [LatticePath] -latticePathsNaive (x,y) = worker x y where - worker !x !y - | odd (x-y) = [] - | x<0 = [] - | y<0 = [] - | y==0 = dyckPaths (div x 2) - | x==1 && y==1 = [[UpStep]] - | otherwise = as ++ bs - where - bracket p = UpStep : p ++ [DownStep] - as = [ UpStep : p | p <- worker (x-1) (y-1) ] - bs = [ bracket p ++ q | k <- [1..(div x 2)] , p <- dyckPaths (k-1) , q <- worker (x-2*k) y ] - --- | Lattice paths are counted by the numbers in the Catalan triangle. -countLatticePaths :: (Int,Int) -> Integer -countLatticePaths (x,y) - | even (x+y) = catalanTriangle (div (x+y) 2) (div (x-y) 2) - | otherwise = 0 - --------------------------------------------------------------------------------- --- * Zero-level touches - --- | @touchingDyckPaths k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ which touch the --- zero level line @y=0@ exactly @k@ times (excluding the starting point, but including the endpoint; --- thus, @k@ should be positive). Synonym for 'touchingDyckPathsNaive'. -touchingDyckPaths - :: Int -- ^ @k@ = number of zero-touches - -> Int -- ^ @m@ = half-length - -> [LatticePath] -touchingDyckPaths = touchingDyckPathsNaive - - --- | @touchingDyckPathsNaive k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ which touch the --- zero level line @y=0@ exactly @k@ times (excluding the starting point, but including the endpoint; --- thus, @k@ should be positive). --- --- > sort (touchingDyckPathsNaive k m) == sort [ p | p <- dyckPaths m , pathNumberOfZeroTouches p == k ] --- --- Naive recursive algorithm, resulting order is pretty ad-hoc. --- -touchingDyckPathsNaive - :: Int -- ^ @k@ = number of zero-touches - -> Int -- ^ @m@ = half-length - -> [LatticePath] -touchingDyckPathsNaive = worker where - worker !k !m - | m == 0 = if k==0 then [[]] else [] - | k <= 0 = [] - | m < 0 = [] - | k == 1 = [ bracket p | p <- dyckPaths (m-1) ] - | otherwise = [ bracket p ++ q | l <- [1..m-1] , p <- dyckPaths (l-1) , q <- worker (k-1) (m-l) ] - where - bracket p = UpStep : p ++ [DownStep] - - --- | There is a bijection from the set of non-empty Dyck paths of length @2n@ which touch the zero lines @t@ times, --- to lattice paths from @(0,0)@ to @(2n-t-1,t-1)@ (just remove all the down-steps just before touching --- the zero line, and also the very first up-step). This gives us a counting formula. -countTouchingDyckPaths - :: Int -- ^ @k@ = number of zero-touches - -> Int -- ^ @m@ = half-length - -> Integer -countTouchingDyckPaths t n - | t==0 && n==0 = 1 - | otherwise = countLatticePaths (2*n-t-1,t-1) - --------------------------------------------------------------------------------- --- * Dyck paths with given number of peaks - --- | @peakingDyckPaths k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ with exactly @k@ peaks. --- --- Synonym for 'peakingDyckPathsNaive' --- -peakingDyckPaths - :: Int -- ^ @k@ = number of peaks - -> Int -- ^ @m@ = half-length - -> [LatticePath] -peakingDyckPaths = peakingDyckPathsNaive - --- | @peakingDyckPathsNaive k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ with exactly @k@ peaks. --- --- > sort (peakingDyckPathsNaive k m) = sort [ p | p <- dyckPaths m , pathNumberOfPeaks p == k ] --- --- Naive recursive algorithm, resulting order is pretty ad-hoc. --- -peakingDyckPathsNaive - :: Int -- ^ @k@ = number of peaks - -> Int -- ^ @m@ = half-length - -> [LatticePath] -peakingDyckPathsNaive = worker where - worker !k !m - | m == 0 = if k==0 then [[]] else [] - | k <= 0 = [] - | m < 0 = [] - | k == 1 = [ singlePeak m ] - | otherwise = as ++ bs ++ cs - where - as = [ bracket p | p <- worker k (m-1) ] - bs = [ smallHill ++ q | q <- worker (k-1) (m-1) ] - cs = [ bracket p ++ q | l <- [2..m-1] , a <- [1..k-1] , p <- worker a (l-1) , q <- worker (k-a) (m-l) ] - smallHill = [ UpStep , DownStep ] - singlePeak !m = replicate m UpStep ++ replicate m DownStep - bracket p = UpStep : p ++ [DownStep] - --- | Dyck paths of length @2m@ with @k@ peaks are counted by the Narayana numbers @N(m,k) = \binom{m}{k} \binom{m}{k-1} / m@ -countPeakingDyckPaths - :: Int -- ^ @k@ = number of peaks - -> Int -- ^ @m@ = half-length - -> Integer -countPeakingDyckPaths k m - | m == 0 = if k==0 then 1 else 0 - | k <= 0 = 0 - | m < 0 = 0 - | k == 1 = 1 - | otherwise = div (binomial m k * binomial m (k-1)) (fromIntegral m) - --------------------------------------------------------------------------------- --- * Random lattice paths - --- | A uniformly random Dyck path of length @2m@ -randomDyckPath :: RandomGen g => Int -> g -> (LatticePath,g) -randomDyckPath m g0 = (nestedParensToDyckPath parens, g1) where - (parens,g1) = randomNestedParentheses m g0 - --------------------------------------------------------------------------------- - ++-- | Dyck paths, lattice paths, etc+--+-- For example, the following figure represents a Dyck path of height 5 with 3 zero-touches (not counting the starting point,+-- but counting the endpoint) and 7 peaks:+--+-- <<svg/dyck_path.svg>>+--++{-# LANGUAGE BangPatterns, FlexibleInstances, TypeSynonymInstances #-}+module Math.Combinat.LatticePaths where++--------------------------------------------------------------------------------++import Data.List+import System.Random++import Math.Combinat.Numbers+import Math.Combinat.Trees.Binary+import Math.Combinat.ASCII as ASCII++--------------------------------------------------------------------------------+-- * Types++-- | A step in a lattice path+data Step + = UpStep -- ^ the step @(1,1)@+ | DownStep -- ^ the step @(1,-1)@+ deriving (Eq,Ord,Show)++-- | A lattice path is a path using only the allowed steps, never going below the zero level line @y=0@. +--+-- Note that if you rotate such a path by 45 degrees counterclockwise,+-- you get a path which uses only the steps @(1,0)@ and @(0,1)@, and stays+-- above the main diagonal (hence the name, we just use a different convention).+--+type LatticePath = [Step]++--------------------------------------------------------------------------------+-- * ascii drawing of paths++-- | Draws the path into a list of lines. For example try:+--+-- > autotabulate RowMajor (Right 5) (map asciiPath $ dyckPaths 4)+--+asciiPath :: LatticePath -> ASCII+asciiPath p = asciiFromLines $ transpose (go 0 p) where++ go !h [] = []+ go !h (x:xs) = case x of+ UpStep -> ee h x : go (h+1) xs+ DownStep -> ee (h-1) x : go (h-1) xs++ maxh = pathHeight p++ ee h x = replicate (maxh-h-1) ' ' ++ [ch x] ++ replicate h ' '+ ch x = case x of + UpStep -> '/' + DownStep -> '\\' ++instance DrawASCII LatticePath where + ascii = asciiPath++--------------------------------------------------------------------------------+-- * elementary queries++-- | A lattice path is called \"valid\", if it never goes below the @y=0@ line.+isValidPath :: LatticePath -> Bool+isValidPath = go 0 where+ go :: Int -> LatticePath -> Bool+ go !y [] = y>=0+ go !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }+ in if y'<0 then False + else go y' ts++-- | A Dyck path is a lattice path whose last point lies on the @y=0@ line+isDyckPath :: LatticePath -> Bool+isDyckPath = go 0 where+ go :: Int -> LatticePath -> Bool+ go !y [] = y==0+ go !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }+ in if y'<0 then False + else go y' ts++-- | Maximal height of a lattice path+pathHeight :: LatticePath -> Int+pathHeight = go 0 0 where+ go :: Int -> Int -> LatticePath -> Int+ go !h !y [] = h+ go !h !y (t:ts) = case t of+ UpStep -> go (max h (y+1)) (y+1) ts+ DownStep -> go h (y-1) ts++-- | Endpoint of a lattice path, which starts from @(0,0)@.+pathEndpoint :: LatticePath -> (Int,Int)+pathEndpoint = go 0 0 where+ go :: Int -> Int -> LatticePath -> (Int,Int)+ go !x !y [] = (x,y)+ go !x !y (t:ts) = case t of + UpStep -> go (x+1) (y+1) ts+ DownStep -> go (x+1) (y-1) ts++-- | Returns the coordinates of the path (excluding the starting point @(0,0)@, but including+-- the endpoint)+pathCoordinates :: LatticePath -> [(Int,Int)]+pathCoordinates = go 0 0 where+ go :: Int -> Int -> LatticePath -> [(Int,Int)]+ go _ _ [] = []+ go !x !y (t:ts) = let x' = x + 1+ y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }+ in (x',y') : go x' y' ts++-- | Counts the up-steps+pathNumberOfUpSteps :: LatticePath -> Int+pathNumberOfUpSteps = fst . pathNumberOfUpDownSteps++-- | Counts the down-steps+pathNumberOfDownSteps :: LatticePath -> Int+pathNumberOfDownSteps = snd . pathNumberOfUpDownSteps++-- | Counts both the up-steps and down-steps+pathNumberOfUpDownSteps :: LatticePath -> (Int,Int)+pathNumberOfUpDownSteps = go 0 0 where + go :: Int -> Int -> LatticePath -> (Int,Int)+ go !u !d (p:ps) = case p of + UpStep -> go (u+1) d ps + DownStep -> go u (d+1) ps + go !u !d [] = (u,d)++--------------------------------------------------------------------------------+-- * path-specific queries++-- | Number of peaks of a path (excluding the endpoint)+pathNumberOfPeaks :: LatticePath -> Int+pathNumberOfPeaks = go 0 where+ go :: Int -> LatticePath -> Int+ go !k (x:xs@(y:_)) = go (if x==UpStep && y==DownStep then k+1 else k) xs+ go !k [x] = k+ go !k [ ] = k++-- | Number of points on the path which touch the @y=0@ zero level line+-- (excluding the starting point @(0,0)@, but including the endpoint; that is, for Dyck paths it this is always positive!).+pathNumberOfZeroTouches :: LatticePath -> Int+pathNumberOfZeroTouches = pathNumberOfTouches' 0++-- | Number of points on the path which touch the level line at height @h@+-- (excluding the starting point @(0,0)@, but including the endpoint).+pathNumberOfTouches' + :: Int -- ^ @h@ = the touch level+ -> LatticePath -> Int+pathNumberOfTouches' h = go 0 0 0 where+ go :: Int -> Int -> Int -> LatticePath -> Int+ go !cnt _ _ [] = cnt+ go !cnt !x !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }+ cnt' = if y'==h then cnt+1 else cnt+ in go cnt' (x+1) y' ts++--------------------------------------------------------------------------------+-- * Dyck paths++-- | @dyckPaths m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@. +-- +-- Remark: Dyck paths are obviously in bijection with nested parentheses, and thus+-- also with binary trees.+--+-- Order is reverse lexicographical:+--+-- > sort (dyckPaths m) == reverse (dyckPaths m)+-- +dyckPaths :: Int -> [LatticePath]+dyckPaths = map nestedParensToDyckPath . nestedParentheses ++-- | @dyckPaths m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@. +--+-- > sort (dyckPathsNaive m) == sort (dyckPaths m) +-- +-- Naive recursive algorithm, order is ad-hoc+--+dyckPathsNaive :: Int -> [LatticePath]+dyckPathsNaive = worker where+ worker 0 = [[]]+ worker m = as ++ bs where+ as = [ bracket p | p <- worker (m-1) ] + bs = [ bracket p ++ q | k <- [1..m-1] , p <- worker (k-1) , q <- worker (m-k) ]+ bracket p = UpStep : p ++ [DownStep]++-- | The number of Dyck paths from @(0,0)@ to @(2m,0)@ is simply the m\'th Catalan number.+countDyckPaths :: Int -> Integer+countDyckPaths m = catalan m++-- | The trivial bijection+nestedParensToDyckPath :: [Paren] -> LatticePath+nestedParensToDyckPath = map f where+ f p = case p of { LeftParen -> UpStep ; RightParen -> DownStep }++-- | The trivial bijection in the other direction+dyckPathToNestedParens :: LatticePath -> [Paren]+dyckPathToNestedParens = map g where+ g s = case s of { UpStep -> LeftParen ; DownStep -> RightParen }++--------------------------------------------------------------------------------+-- * Bounded Dyck paths++-- | @boundedDyckPaths h m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ whose height is at most @h@.+-- Synonym for 'boundedDyckPathsNaive'.+--+boundedDyckPaths+ :: Int -- ^ @h@ = maximum height+ -> Int -- ^ @m@ = half-length+ -> [LatticePath]+boundedDyckPaths = boundedDyckPathsNaive ++-- | @boundedDyckPathsNaive h m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ whose height is at most @h@.+--+-- > sort (boundedDyckPaths h m) == sort [ p | p <- dyckPaths m , pathHeight p <= h ]+-- > sort (boundedDyckPaths m m) == sort (dyckPaths m) +--+-- Naive recursive algorithm, resulting order is pretty ad-hoc.+--+boundedDyckPathsNaive+ :: Int -- ^ @h@ = maximum height+ -> Int -- ^ @m@ = half-length+ -> [LatticePath]+boundedDyckPathsNaive = worker where+ worker !h !m + | h<0 = []+ | m<0 = []+ | m==0 = [[]]+ | h<=0 = []+ | otherwise = as ++ bs + where+ bracket p = UpStep : p ++ [DownStep]+ as = [ bracket p | p <- boundedDyckPaths (h-1) (m-1) ]+ bs = [ bracket p ++ q | k <- [1..m-1] , p <- boundedDyckPaths (h-1) (k-1) , q <- boundedDyckPaths h (m-k) ]++--------------------------------------------------------------------------------+-- * More general lattice paths++-- | All lattice paths from @(0,0)@ to @(x,y)@. Clearly empty unless @x-y@ is even.+-- Synonym for 'latticePathsNaive'+--+latticePaths :: (Int,Int) -> [LatticePath]+latticePaths = latticePathsNaive++-- | All lattice paths from @(0,0)@ to @(x,y)@. Clearly empty unless @x-y@ is even.+--+-- Note that+--+-- > sort (dyckPaths n) == sort (latticePaths (0,2*n))+--+-- Naive recursive algorithm, resulting order is pretty ad-hoc.+--+latticePathsNaive :: (Int,Int) -> [LatticePath]+latticePathsNaive (x,y) = worker x y where+ worker !x !y + | odd (x-y) = []+ | x<0 = []+ | y<0 = []+ | y==0 = dyckPaths (div x 2)+ | x==1 && y==1 = [[UpStep]]+ | otherwise = as ++ bs+ where+ bracket p = UpStep : p ++ [DownStep] + as = [ UpStep : p | p <- worker (x-1) (y-1) ]+ bs = [ bracket p ++ q | k <- [1..(div x 2)] , p <- dyckPaths (k-1) , q <- worker (x-2*k) y ]++-- | Lattice paths are counted by the numbers in the Catalan triangle.+countLatticePaths :: (Int,Int) -> Integer+countLatticePaths (x,y) + | even (x+y) = catalanTriangle (div (x+y) 2) (div (x-y) 2)+ | otherwise = 0++--------------------------------------------------------------------------------+-- * Zero-level touches++-- | @touchingDyckPaths k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ which touch the +-- zero level line @y=0@ exactly @k@ times (excluding the starting point, but including the endpoint;+-- thus, @k@ should be positive). Synonym for 'touchingDyckPathsNaive'.+touchingDyckPaths+ :: Int -- ^ @k@ = number of zero-touches+ -> Int -- ^ @m@ = half-length+ -> [LatticePath]+touchingDyckPaths = touchingDyckPathsNaive+++-- | @touchingDyckPathsNaive k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ which touch the +-- zero level line @y=0@ exactly @k@ times (excluding the starting point, but including the endpoint;+-- thus, @k@ should be positive).+--+-- > sort (touchingDyckPathsNaive k m) == sort [ p | p <- dyckPaths m , pathNumberOfZeroTouches p == k ]+-- +-- Naive recursive algorithm, resulting order is pretty ad-hoc.+--+touchingDyckPathsNaive+ :: Int -- ^ @k@ = number of zero-touches+ -> Int -- ^ @m@ = half-length+ -> [LatticePath]+touchingDyckPathsNaive = worker where+ worker !k !m + | m == 0 = if k==0 then [[]] else []+ | k <= 0 = []+ | m < 0 = []+ | k == 1 = [ bracket p | p <- dyckPaths (m-1) ]+ | otherwise = [ bracket p ++ q | l <- [1..m-1] , p <- dyckPaths (l-1) , q <- worker (k-1) (m-l) ]+ where+ bracket p = UpStep : p ++ [DownStep] +++-- | There is a bijection from the set of non-empty Dyck paths of length @2n@ which touch the zero lines @t@ times,+-- to lattice paths from @(0,0)@ to @(2n-t-1,t-1)@ (just remove all the down-steps just before touching+-- the zero line, and also the very first up-step). This gives us a counting formula.+countTouchingDyckPaths + :: Int -- ^ @k@ = number of zero-touches+ -> Int -- ^ @m@ = half-length+ -> Integer+countTouchingDyckPaths t n+ | t==0 && n==0 = 1+ | otherwise = countLatticePaths (2*n-t-1,t-1)++--------------------------------------------------------------------------------+-- * Dyck paths with given number of peaks++-- | @peakingDyckPaths k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ with exactly @k@ peaks.+--+-- Synonym for 'peakingDyckPathsNaive'+--+peakingDyckPaths+ :: Int -- ^ @k@ = number of peaks+ -> Int -- ^ @m@ = half-length+ -> [LatticePath]+peakingDyckPaths = peakingDyckPathsNaive ++-- | @peakingDyckPathsNaive k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ with exactly @k@ peaks.+--+-- > sort (peakingDyckPathsNaive k m) = sort [ p | p <- dyckPaths m , pathNumberOfPeaks p == k ]+-- +-- Naive recursive algorithm, resulting order is pretty ad-hoc.+--+peakingDyckPathsNaive + :: Int -- ^ @k@ = number of peaks+ -> Int -- ^ @m@ = half-length+ -> [LatticePath]+peakingDyckPathsNaive = worker where+ worker !k !m+ | m == 0 = if k==0 then [[]] else [] + | k <= 0 = []+ | m < 0 = []+ | k == 1 = [ singlePeak m ] + | otherwise = as ++ bs ++ cs+ where+ as = [ bracket p | p <- worker k (m-1) ]+ bs = [ smallHill ++ q | q <- worker (k-1) (m-1) ]+ cs = [ bracket p ++ q | l <- [2..m-1] , a <- [1..k-1] , p <- worker a (l-1) , q <- worker (k-a) (m-l) ]+ smallHill = [ UpStep , DownStep ]+ singlePeak !m = replicate m UpStep ++ replicate m DownStep + bracket p = UpStep : p ++ [DownStep] ++-- | Dyck paths of length @2m@ with @k@ peaks are counted by the Narayana numbers @N(m,k) = \binom{m}{k} \binom{m}{k-1} / m@+countPeakingDyckPaths+ :: Int -- ^ @k@ = number of peaks+ -> Int -- ^ @m@ = half-length+ -> Integer+countPeakingDyckPaths k m + | m == 0 = if k==0 then 1 else 0+ | k <= 0 = 0+ | m < 0 = 0+ | k == 1 = 1+ | otherwise = div (binomial m k * binomial m (k-1)) (fromIntegral m)++--------------------------------------------------------------------------------+-- * Random lattice paths++-- | A uniformly random Dyck path of length @2m@+randomDyckPath :: RandomGen g => Int -> g -> (LatticePath,g)+randomDyckPath m g0 = (nestedParensToDyckPath parens, g1) where+ (parens,g1) = randomNestedParentheses m g0++--------------------------------------------------------------------------------+
Math/Combinat/Numbers/Series.hs view
@@ -233,7 +233,7 @@ -- | Power series expansion of @(1-Sqrt[1-4x])/(2x)@ (the coefficients are the Catalan numbers) dyckSeries :: Num a => [a]-dyckSeries = [ fromInteger (catalan i) | i<-[0..] ]+dyckSeries = [ fromInteger (catalan i) | i<-[(0::Int)..] ] -------------------------------------------------------------------------------- -- * \"Coin\" series
Math/Combinat/Partitions/Integer.hs view
@@ -526,6 +526,9 @@ EnglishNotationCCW -> reverse $ fromPartition $ dualPartition part FrenchNotation -> reverse $ fromPartition $ part +instance DrawASCII Partition where+ ascii = asciiFerrersDiagram+ -------------------------------------------------------------------------------- {-
Math/Combinat/Partitions/Multiset.hs view
@@ -1,24 +1,24 @@- --- | Partitions of a multiset -module Math.Combinat.Partitions.Multiset where - --------------------------------------------------------------------------------- - -import Data.Array.Unboxed -import Data.List - -import Math.Combinat.Partitions.Vector - --------------------------------------------------------------------------------- - --- | Partitions of a multiset. Internally, this uses the vector partition algorithm -partitionMultiset :: (Eq a, Ord a) => [a] -> [[[a]]] -partitionMultiset xs = parts where - parts = (map . map) (f . elems) temp - f ns = concat (zipWith replicate ns zs) - temp = fasc3B_algorithm_M counts - counts = map length ys - ys = group (sort xs) - zs = map head ys - ++-- | Partitions of a multiset+module Math.Combinat.Partitions.Multiset where++--------------------------------------------------------------------------------++import Data.Array.Unboxed+import Data.List++import Math.Combinat.Partitions.Vector++--------------------------------------------------------------------------------+ +-- | Partitions of a multiset. Internally, this uses the vector partition algorithm+partitionMultiset :: (Eq a, Ord a) => [a] -> [[[a]]]+partitionMultiset xs = parts where+ parts = (map . map) (f . elems) temp+ f ns = concat (zipWith replicate ns zs)+ temp = fasc3B_algorithm_M counts+ counts = map length ys+ ys = group (sort xs) + zs = map head ys+ --------------------------------------------------------------------------------
Math/Combinat/Partitions/NonCrossing.hs view
@@ -1,205 +1,205 @@- --- | Non-crossing partitions. --- --- See eg. <http://en.wikipedia.org/wiki/Noncrossing_partition> --- --- Non-crossing partitions of the set @[1..n]@ are encoded as lists of lists --- in standard form: Entries decreasing in each block and blocks listed in increasing order of their first entries. --- For example the partition in the diagram --- --- <<svg/noncrossing.svg>> --- --- is represented as --- --- > NonCrossing [[3],[5,4,2],[7,6,1],[9,8]] --- - -{-# LANGUAGE BangPatterns #-} -module Math.Combinat.Partitions.NonCrossing where - --------------------------------------------------------------------------------- - -import Control.Applicative - -import Data.List -import Data.Ord - -import System.Random - -import Math.Combinat.Numbers -import Math.Combinat.LatticePaths -import Math.Combinat.Helper -import Math.Combinat.Partitions.Set -import Math.Combinat.Partitions ( HasNumberOfParts(..) ) - --------------------------------------------------------------------------------- --- * The type of non-crossing partitions - --- | A non-crossing partition of the set @[1..n]@ in standard form: --- entries decreasing in each block and blocks listed in increasing order of their first entries. -newtype NonCrossing = NonCrossing [[Int]] deriving (Eq,Ord,Show,Read) - --- | Checks whether a set partition is noncrossing. --- --- Implementation method: we convert to a Dyck path and then back again, and finally compare. --- Probably not very efficient, but should be better than a naive check for crosses...) --- -_isNonCrossing :: [[Int]] -> Bool -_isNonCrossing zzs0 = _isNonCrossingUnsafe (_standardizeNonCrossing zzs0) - --- | Warning: This function assumes the standard ordering! -_isNonCrossingUnsafe :: [[Int]] -> Bool -_isNonCrossingUnsafe zzs = - case _nonCrossingPartitionToDyckPathMaybe zzs of - Nothing -> False - Just dyck -> case dyckPathToNonCrossingPartitionMaybe dyck of - Nothing -> False - Just (NonCrossing yys) -> yys == zzs - --- | Convert to standard form: entries decreasing in each block --- and blocks listed in increasing order of their first entries. -_standardizeNonCrossing :: [[Int]] -> [[Int]] -_standardizeNonCrossing = sortBy (comparing myhead) . map reverseSort where - myhead xs = case xs of - (x:xs) -> x - [] -> error "_standardizeNonCrossing: empty subset" - -fromNonCrossing :: NonCrossing -> [[Int]] -fromNonCrossing (NonCrossing xs) = xs - -toNonCrossingUnsafe :: [[Int]] -> NonCrossing -toNonCrossingUnsafe = NonCrossing - --- | Throws an error if the input is not a non-crossing partition -toNonCrossing :: [[Int]] -> NonCrossing -toNonCrossing xxs = case toNonCrossingMaybe xxs of - Just nc -> nc - Nothing -> error "toNonCrossing: not a non-crossing partition" - -toNonCrossingMaybe :: [[Int]] -> Maybe NonCrossing -toNonCrossingMaybe xxs0 = - if _isNonCrossingUnsafe xxs - then Just $ NonCrossing xxs - else Nothing - where - xxs = _standardizeNonCrossing xxs0 - --- | If a set partition is actually non-crossing, then we can convert it -setPartitionToNonCrossing :: SetPartition -> Maybe NonCrossing -setPartitionToNonCrossing (SetPartition zzs0) = - if _isNonCrossingUnsafe zzs - then Just $ NonCrossing zzs - else Nothing - where - zzs = _standardizeNonCrossing zzs0 - -instance HasNumberOfParts NonCrossing where - numberOfParts (NonCrossing p) = length p - --------------------------------------------------------------------------------- --- * Bijection to Dyck paths - --- | Bijection between Dyck paths and noncrossing partitions --- --- Based on: David Callan: /Sets, Lists and Noncrossing Partitions/ --- --- Fails if the input is not a Dyck path. -dyckPathToNonCrossingPartition :: LatticePath -> NonCrossing -dyckPathToNonCrossingPartition = NonCrossing . go 0 [] [] [] where - go :: Int -> [Int] -> [Int] -> [[Int]] -> LatticePath -> [[Int]] - go !cnt stack small big path = - case path of - (x:xs) -> case x of - UpStep -> let cnt' = cnt + 1 in case xs of - (y:ys) -> case y of - UpStep -> go cnt' (cnt':stack) small big xs - DownStep -> go cnt' (cnt':stack) [] (reverse small : big) xs - [] -> error "dyckPathToNonCrossingPartition: last step is an UpStep (thus input was not a Dyck path)" - DownStep -> case stack of - (k:ks) -> go cnt ks (k:small) big xs - [] -> error "dyckPathToNonCrossingPartition: empty stack, shouldn't happen (thus input was not a Dyck path)" - [] -> tail $ reverse (reverse small : big) - --- | Safe version of 'dyckPathToNonCrossingPartition' -dyckPathToNonCrossingPartitionMaybe :: LatticePath -> Maybe NonCrossing -dyckPathToNonCrossingPartitionMaybe = fmap NonCrossing . go 0 [] [] [] where - go :: Int -> [Int] -> [Int] -> [[Int]] -> LatticePath -> Maybe [[Int]] - go !cnt stack small big path = - case path of - (x:xs) -> case x of - UpStep -> let cnt' = cnt + 1 in case xs of - (y:ys) -> case y of - UpStep -> go cnt' (cnt':stack) small big xs - DownStep -> go cnt' (cnt':stack) [] (reverse small : big) xs - [] -> Nothing - DownStep -> case stack of - (k:ks) -> go cnt ks (k:small) big xs - [] -> Nothing - [] -> Just $ tail $ reverse (reverse small : big) - --- | The inverse bijection (should never fail proper 'NonCrossing'-s) -nonCrossingPartitionToDyckPath :: NonCrossing -> LatticePath -nonCrossingPartitionToDyckPath (NonCrossing zzs) = go 0 zzs where - go !k (ys@(y:_):yys) = replicate (y-k) UpStep ++ replicate (length ys) DownStep ++ go y yys - go !k [] = [] - go _ _ = error "nonCrossingPartitionToDyckPath: shouldnt't happen" - --- | Safe version 'nonCrossingPartitionToDyckPath' -_nonCrossingPartitionToDyckPathMaybe :: [[Int]] -> Maybe LatticePath -_nonCrossingPartitionToDyckPathMaybe = go 0 where - go !k (ys@(y:_):yys) = fmap (\zs -> replicate (y-k) UpStep ++ replicate (length ys) DownStep ++ zs) (go y yys) - go !k [] = Just [] - go _ _ = Nothing - --------------------------------------------------------------------------------- - -{- --- this should be mapped to NonCrossing [[3],[5,4,2],[7,6,1],[9,8]] -testpath = [u,u,u,d,u,u,d,d,d,u,u,d,d,d,u,u,d,d] where - u = UpStep - d = DownStep - -testnc = NonCrossing [[3],[5,4,2],[7,6,1],[9,8]] --} - --------------------------------------------------------------------------------- --- * Generating non-crossing partitions - --- | Lists all non-crossing partitions of @[1..n]@ --- --- Equivalent to (but orders of magnitude faster than) filtering out the non-crossing ones: --- --- > (sort $ catMaybes $ map setPartitionToNonCrossing $ setPartitions n) == sort (nonCrossingPartitions n) --- -nonCrossingPartitions :: Int -> [NonCrossing] -nonCrossingPartitions = map dyckPathToNonCrossingPartition . dyckPaths - --- | Lists all non-crossing partitions of @[1..n]@ into @k@ parts. --- --- > sort (nonCrossingPartitionsWithKParts k n) == sort [ p | p <- nonCrossingPartitions n , numberOfParts p == k ] --- -nonCrossingPartitionsWithKParts - :: Int -- ^ @k@ = number of parts - -> Int -- ^ @n@ = size of the set - -> [NonCrossing] -nonCrossingPartitionsWithKParts k n = map dyckPathToNonCrossingPartition $ peakingDyckPaths k n - --- | Non-crossing partitions are counted by the Catalan numbers -countNonCrossingPartitions :: Int -> Integer -countNonCrossingPartitions = countDyckPaths - --- | Non-crossing partitions with @k@ parts are counted by the Naranaya numbers -countNonCrossingPartitionsWithKParts - :: Int -- ^ @k@ = number of parts - -> Int -- ^ @n@ = size of the set - -> Integer -countNonCrossingPartitionsWithKParts = countPeakingDyckPaths - --------------------------------------------------------------------------------- - --- | Uniformly random non-crossing partition -randomNonCrossingPartition :: RandomGen g => Int -> g -> (NonCrossing,g) -randomNonCrossingPartition n g0 = (dyckPathToNonCrossingPartition dyck, g1) where - (dyck,g1) = randomDyckPath n g0 - --------------------------------------------------------------------------------- ++-- | Non-crossing partitions.+--+-- See eg. <http://en.wikipedia.org/wiki/Noncrossing_partition>+--+-- Non-crossing partitions of the set @[1..n]@ are encoded as lists of lists+-- in standard form: Entries decreasing in each block and blocks listed in increasing order of their first entries.+-- For example the partition in the diagram+--+-- <<svg/noncrossing.svg>>+--+-- is represented as+--+-- > NonCrossing [[3],[5,4,2],[7,6,1],[9,8]]+--++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.NonCrossing where++--------------------------------------------------------------------------------++import Control.Applicative++import Data.List+import Data.Ord++import System.Random++import Math.Combinat.Numbers+import Math.Combinat.LatticePaths+import Math.Combinat.Helper+import Math.Combinat.Partitions.Set+import Math.Combinat.Partitions ( HasNumberOfParts(..) )++--------------------------------------------------------------------------------+-- * The type of non-crossing partitions++-- | A non-crossing partition of the set @[1..n]@ in standard form: +-- entries decreasing in each block and blocks listed in increasing order of their first entries.+newtype NonCrossing = NonCrossing [[Int]] deriving (Eq,Ord,Show,Read)++-- | Checks whether a set partition is noncrossing.+--+-- Implementation method: we convert to a Dyck path and then back again, and finally compare. +-- Probably not very efficient, but should be better than a naive check for crosses...)+--+_isNonCrossing :: [[Int]] -> Bool+_isNonCrossing zzs0 = _isNonCrossingUnsafe (_standardizeNonCrossing zzs0)++-- | Warning: This function assumes the standard ordering!+_isNonCrossingUnsafe :: [[Int]] -> Bool+_isNonCrossingUnsafe zzs = + case _nonCrossingPartitionToDyckPathMaybe zzs of+ Nothing -> False+ Just dyck -> case dyckPathToNonCrossingPartitionMaybe dyck of+ Nothing -> False+ Just (NonCrossing yys) -> yys == zzs++-- | Convert to standard form: entries decreasing in each block +-- and blocks listed in increasing order of their first entries.+_standardizeNonCrossing :: [[Int]] -> [[Int]]+_standardizeNonCrossing = sortBy (comparing myhead) . map reverseSort where+ myhead xs = case xs of+ (x:xs) -> x+ [] -> error "_standardizeNonCrossing: empty subset"++fromNonCrossing :: NonCrossing -> [[Int]]+fromNonCrossing (NonCrossing xs) = xs++toNonCrossingUnsafe :: [[Int]] -> NonCrossing+toNonCrossingUnsafe = NonCrossing++-- | Throws an error if the input is not a non-crossing partition+toNonCrossing :: [[Int]] -> NonCrossing+toNonCrossing xxs = case toNonCrossingMaybe xxs of+ Just nc -> nc+ Nothing -> error "toNonCrossing: not a non-crossing partition"++toNonCrossingMaybe :: [[Int]] -> Maybe NonCrossing+toNonCrossingMaybe xxs0 = + if _isNonCrossingUnsafe xxs+ then Just $ NonCrossing xxs+ else Nothing+ where + xxs = _standardizeNonCrossing xxs0++-- | If a set partition is actually non-crossing, then we can convert it+setPartitionToNonCrossing :: SetPartition -> Maybe NonCrossing+setPartitionToNonCrossing (SetPartition zzs0) =+ if _isNonCrossingUnsafe zzs+ then Just $ NonCrossing zzs+ else Nothing+ where+ zzs = _standardizeNonCrossing zzs0++instance HasNumberOfParts NonCrossing where+ numberOfParts (NonCrossing p) = length p++--------------------------------------------------------------------------------+-- * Bijection to Dyck paths++-- | Bijection between Dyck paths and noncrossing partitions+--+-- Based on: David Callan: /Sets, Lists and Noncrossing Partitions/+--+-- Fails if the input is not a Dyck path.+dyckPathToNonCrossingPartition :: LatticePath -> NonCrossing+dyckPathToNonCrossingPartition = NonCrossing . go 0 [] [] [] where+ go :: Int -> [Int] -> [Int] -> [[Int]] -> LatticePath -> [[Int]] + go !cnt stack small big path =+ case path of+ (x:xs) -> case x of + UpStep -> let cnt' = cnt + 1 in case xs of+ (y:ys) -> case y of+ UpStep -> go cnt' (cnt':stack) small big xs + DownStep -> go cnt' (cnt':stack) [] (reverse small : big) xs+ [] -> error "dyckPathToNonCrossingPartition: last step is an UpStep (thus input was not a Dyck path)"+ DownStep -> case stack of+ (k:ks) -> go cnt ks (k:small) big xs+ [] -> error "dyckPathToNonCrossingPartition: empty stack, shouldn't happen (thus input was not a Dyck path)"+ [] -> tail $ reverse (reverse small : big)++-- | Safe version of 'dyckPathToNonCrossingPartition'+dyckPathToNonCrossingPartitionMaybe :: LatticePath -> Maybe NonCrossing+dyckPathToNonCrossingPartitionMaybe = fmap NonCrossing . go 0 [] [] [] where+ go :: Int -> [Int] -> [Int] -> [[Int]] -> LatticePath -> Maybe [[Int]] + go !cnt stack small big path =+ case path of+ (x:xs) -> case x of + UpStep -> let cnt' = cnt + 1 in case xs of+ (y:ys) -> case y of+ UpStep -> go cnt' (cnt':stack) small big xs + DownStep -> go cnt' (cnt':stack) [] (reverse small : big) xs+ [] -> Nothing+ DownStep -> case stack of+ (k:ks) -> go cnt ks (k:small) big xs+ [] -> Nothing+ [] -> Just $ tail $ reverse (reverse small : big)++-- | The inverse bijection (should never fail proper 'NonCrossing'-s)+nonCrossingPartitionToDyckPath :: NonCrossing -> LatticePath+nonCrossingPartitionToDyckPath (NonCrossing zzs) = go 0 zzs where+ go !k (ys@(y:_):yys) = replicate (y-k) UpStep ++ replicate (length ys) DownStep ++ go y yys+ go !k [] = []+ go _ _ = error "nonCrossingPartitionToDyckPath: shouldnt't happen"++-- | Safe version 'nonCrossingPartitionToDyckPath'+_nonCrossingPartitionToDyckPathMaybe :: [[Int]] -> Maybe LatticePath+_nonCrossingPartitionToDyckPathMaybe = go 0 where+ go !k (ys@(y:_):yys) = fmap (\zs -> replicate (y-k) UpStep ++ replicate (length ys) DownStep ++ zs) (go y yys)+ go !k [] = Just []+ go _ _ = Nothing++--------------------------------------------------------------------------------++{- +-- this should be mapped to NonCrossing [[3],[5,4,2],[7,6,1],[9,8]]+testpath = [u,u,u,d,u,u,d,d,d,u,u,d,d,d,u,u,d,d] where+ u = UpStep+ d = DownStep++testnc = NonCrossing [[3],[5,4,2],[7,6,1],[9,8]]+-}++--------------------------------------------------------------------------------+-- * Generating non-crossing partitions++-- | Lists all non-crossing partitions of @[1..n]@+--+-- Equivalent to (but orders of magnitude faster than) filtering out the non-crossing ones:+--+-- > (sort $ catMaybes $ map setPartitionToNonCrossing $ setPartitions n) == sort (nonCrossingPartitions n)+--+nonCrossingPartitions :: Int -> [NonCrossing]+nonCrossingPartitions = map dyckPathToNonCrossingPartition . dyckPaths++-- | Lists all non-crossing partitions of @[1..n]@ into @k@ parts.+--+-- > sort (nonCrossingPartitionsWithKParts k n) == sort [ p | p <- nonCrossingPartitions n , numberOfParts p == k ]+--+nonCrossingPartitionsWithKParts + :: Int -- ^ @k@ = number of parts + -> Int -- ^ @n@ = size of the set+ -> [NonCrossing]+nonCrossingPartitionsWithKParts k n = map dyckPathToNonCrossingPartition $ peakingDyckPaths k n++-- | Non-crossing partitions are counted by the Catalan numbers+countNonCrossingPartitions :: Int -> Integer+countNonCrossingPartitions = countDyckPaths++-- | Non-crossing partitions with @k@ parts are counted by the Naranaya numbers+countNonCrossingPartitionsWithKParts + :: Int -- ^ @k@ = number of parts + -> Int -- ^ @n@ = size of the set+ -> Integer+countNonCrossingPartitionsWithKParts = countPeakingDyckPaths++--------------------------------------------------------------------------------++-- | Uniformly random non-crossing partition+randomNonCrossingPartition :: RandomGen g => Int -> g -> (NonCrossing,g)+randomNonCrossingPartition n g0 = (dyckPathToNonCrossingPartition dyck, g1) where+ (dyck,g1) = randomDyckPath n g0++--------------------------------------------------------------------------------
Math/Combinat/Partitions/Plane.hs view
@@ -1,116 +1,116 @@- --- | Plane partitions. See eg. <http://en.wikipedia.org/wiki/Plane_partition> --- --- Plane partitions are encoded as lists of lists of Z heights. For example the plane --- partition in the picture --- --- <<svg/plane_partition.svg>> --- --- is encoded as --- --- > PlanePart [ [5,4,3,3,1] --- > , [4,4,2,1] --- > , [3,2] --- > , [2,1] --- > , [1] --- > , [1] --- > ] --- -{-# LANGUAGE BangPatterns #-} -module Math.Combinat.Partitions.Plane where - --------------------------------------------------------------------------------- - -import Data.List -import Data.Array - -import Math.Combinat.Partitions -import Math.Combinat.Tableaux as Tableaux -import Math.Combinat.Helper - --------------------------------------------------------------------------------- --- * the type of plane partitions - --- | A plane partition encoded as a tablaeu (the \"Z\" heights are the numbers) -newtype PlanePart = PlanePart [[Int]] deriving (Eq,Ord,Show) - -fromPlanePart :: PlanePart -> [[Int]] -fromPlanePart (PlanePart xs) = xs - -isValidPlanePart :: [[Int]] -> Bool -isValidPlanePart pps = - and [ table!(i,j) >= table!(i ,j+1) && - table!(i,j) >= table!(i+1,j ) - | i<-[0..y-1] , j<-[0..x-1] - ] - where - table :: Array (Int,Int) Int - table = accumArray const 0 ((0,0),(y,x)) [ ((i,j),k) | (i,ps) <- zip [0..] pps , (j,k) <- zip [0..] ps ] - y = length pps - x = maximum (map length pps) - --- | Throws an exception if the input is not a plane partition -toPlanePart :: [[Int]] -> PlanePart -toPlanePart pps = if isValidPlanePart pps - then PlanePart $ filter (not . null) $ map (filter (>0)) $ pps - else error "toPlanePart: not a plane partition" - --- | The XY projected shape of a plane partition, as an integer partition -planePartShape :: PlanePart -> Partition -planePartShape = Tableaux.shape . fromPlanePart - --- | The Z height of a plane partition -planePartZHeight :: PlanePart -> Int -planePartZHeight (PlanePart xs) = - case xs of - ((h:_):_) -> h - _ -> 0 - -planePartWeight :: PlanePart -> Int -planePartWeight (PlanePart xs) = sum' (map sum' xs) - --------------------------------------------------------------------------------- --- * constructing plane partitions - -singleLayer :: Partition -> PlanePart -singleLayer = PlanePart . map (\k -> replicate k 1) . fromPartition - --- | Stacks layers of partitions into a plane partition. --- Throws an exception if they do not form a plane partition. -stackLayers :: [Partition] -> PlanePart -stackLayers layers = if and [ isSubPartitionOf p q | (q,p) <- pairs layers ] - then unsafeStackLayers layers - else error "stackLayers: the layers do not form a plane partition" - --- | Stacks layers of partitions into a plane partition. --- This is unsafe in the sense that we don't check that the partitions fit on the top of each other. -unsafeStackLayers :: [Partition] -> PlanePart -unsafeStackLayers [] = PlanePart [] -unsafeStackLayers (bottom:rest) = PlanePart $ foldl addLayer (fromPlanePart $ singleLayer bottom) rest where - addLayer :: [[Int]] -> Partition -> [[Int]] - addLayer xxs (Partition ps) = [ zipWith (+) xs (replicate p 1 ++ repeat 0) | (xs,p) <- zip xxs (ps ++ repeat 0) ] - --- | The \"layers\" of a plane partition (in direction @Z@). We should have --- --- > unsafeStackLayers (planePartLayers pp) == pp --- -planePartLayers :: PlanePart -> [Partition] -planePartLayers pp@(PlanePart xs) = [ layer h | h<-[1..planePartZHeight pp] ] where - layer h = Partition $ filter (>0) $ map sum' $ (map . map) (f h) xs - f h = \k -> if k>=h then 1 else 0 - --------------------------------------------------------------------------------- --- * generating plane partitions - --- | Plane partitions of a given weight -planePartitions :: Int -> [PlanePart] -planePartitions d - | d < 0 = [] - | d == 0 = [PlanePart []] - | otherwise = concat [ go (d-n) [p] | n<-[1..d] , p<-partitions n ] - where - go :: Int -> [Partition] -> [PlanePart] - go 0 acc = [unsafeStackLayers (reverse acc)] - go !rem acc@(h:_) = concat [ go (rem-k) (this:acc) | k<-[1..rem] , this <- subPartitions k h ] - --------------------------------------------------------------------------------- ++-- | Plane partitions. See eg. <http://en.wikipedia.org/wiki/Plane_partition>+--+-- Plane partitions are encoded as lists of lists of Z heights. For example the plane +-- partition in the picture+-- +-- <<svg/plane_partition.svg>>+--+-- is encoded as+--+-- > PlanePart [ [5,4,3,3,1]+-- > , [4,4,2,1]+-- > , [3,2]+-- > , [2,1]+-- > , [1]+-- > , [1]+-- > ]+-- +{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.Plane where++--------------------------------------------------------------------------------++import Data.List+import Data.Array++import Math.Combinat.Partitions+import Math.Combinat.Tableaux as Tableaux+import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * the type of plane partitions++-- | A plane partition encoded as a tablaeu (the \"Z\" heights are the numbers)+newtype PlanePart = PlanePart [[Int]] deriving (Eq,Ord,Show)++fromPlanePart :: PlanePart -> [[Int]]+fromPlanePart (PlanePart xs) = xs++isValidPlanePart :: [[Int]] -> Bool+isValidPlanePart pps = + and [ table!(i,j) >= table!(i ,j+1) &&+ table!(i,j) >= table!(i+1,j )+ | i<-[0..y-1] , j<-[0..x-1] + ]+ where+ table :: Array (Int,Int) Int+ table = accumArray const 0 ((0,0),(y,x)) [ ((i,j),k) | (i,ps) <- zip [0..] pps , (j,k) <- zip [0..] ps ]+ y = length pps+ x = maximum (map length pps)++-- | Throws an exception if the input is not a plane partition+toPlanePart :: [[Int]] -> PlanePart+toPlanePart pps = if isValidPlanePart pps+ then PlanePart $ filter (not . null) $ map (filter (>0)) $ pps+ else error "toPlanePart: not a plane partition"++-- | The XY projected shape of a plane partition, as an integer partition+planePartShape :: PlanePart -> Partition+planePartShape = Tableaux.shape . fromPlanePart++-- | The Z height of a plane partition+planePartZHeight :: PlanePart -> Int+planePartZHeight (PlanePart xs) = + case xs of+ ((h:_):_) -> h+ _ -> 0++planePartWeight :: PlanePart -> Int+planePartWeight (PlanePart xs) = sum' (map sum' xs)++--------------------------------------------------------------------------------+-- * constructing plane partitions++singleLayer :: Partition -> PlanePart+singleLayer = PlanePart . map (\k -> replicate k 1) . fromPartition ++-- | Stacks layers of partitions into a plane partition.+-- Throws an exception if they do not form a plane partition.+stackLayers :: [Partition] -> PlanePart+stackLayers layers = if and [ isSubPartitionOf p q | (q,p) <- pairs layers ]+ then unsafeStackLayers layers+ else error "stackLayers: the layers do not form a plane partition"++-- | Stacks layers of partitions into a plane partition.+-- This is unsafe in the sense that we don't check that the partitions fit on the top of each other.+unsafeStackLayers :: [Partition] -> PlanePart+unsafeStackLayers [] = PlanePart []+unsafeStackLayers (bottom:rest) = PlanePart $ foldl addLayer (fromPlanePart $ singleLayer bottom) rest where+ addLayer :: [[Int]] -> Partition -> [[Int]]+ addLayer xxs (Partition ps) = [ zipWith (+) xs (replicate p 1 ++ repeat 0) | (xs,p) <- zip xxs (ps ++ repeat 0) ] ++-- | The \"layers\" of a plane partition (in direction @Z@). We should have+--+-- > unsafeStackLayers (planePartLayers pp) == pp+-- +planePartLayers :: PlanePart -> [Partition]+planePartLayers pp@(PlanePart xs) = [ layer h | h<-[1..planePartZHeight pp] ] where+ layer h = Partition $ filter (>0) $ map sum' $ (map . map) (f h) xs+ f h = \k -> if k>=h then 1 else 0++--------------------------------------------------------------------------------+-- * generating plane partitions++-- | Plane partitions of a given weight+planePartitions :: Int -> [PlanePart]+planePartitions d + | d < 0 = []+ | d == 0 = [PlanePart []]+ | otherwise = concat [ go (d-n) [p] | n<-[1..d] , p<-partitions n ]+ where+ go :: Int -> [Partition] -> [PlanePart]+ go 0 acc = [unsafeStackLayers (reverse acc)]+ go !rem acc@(h:_) = concat [ go (rem-k) (this:acc) | k<-[1..rem] , this <- subPartitions k h ]++--------------------------------------------------------------------------------
Math/Combinat/Partitions/Set.hs view
@@ -1,99 +1,99 @@- --- | Set partitions. --- --- See eg. <http://en.wikipedia.org/wiki/Partition_of_a_set> --- - -{-# LANGUAGE BangPatterns #-} -module Math.Combinat.Partitions.Set where - --------------------------------------------------------------------------------- - -import Data.List -import Data.Ord - -import System.Random - -import Math.Combinat.Sets -import Math.Combinat.Numbers -import Math.Combinat.Helper -import Math.Combinat.Partitions ( HasNumberOfParts(..) ) - --------------------------------------------------------------------------------- --- * The type of set partitions - --- | A partition of the set @[1..n]@ (in standard order) -newtype SetPartition = SetPartition [[Int]] deriving (Eq,Ord,Show,Read) - -_standardizeSetPartition :: [[Int]] -> [[Int]] -_standardizeSetPartition = sortBy (comparing myhead) . map sort where - myhead xs = case xs of - (x:xs) -> x - [] -> error "_standardizeSetPartition: empty subset" - -fromSetPartition :: SetPartition -> [[Int]] -fromSetPartition (SetPartition zzs) = zzs - -toSetPartitionUnsafe :: [[Int]] -> SetPartition -toSetPartitionUnsafe = SetPartition - -toSetPartition :: [[Int]] -> SetPartition -toSetPartition zzs = if _isSetPartition zzs - then SetPartition (_standardizeSetPartition zzs) - else error "toSetPartition: not a set partition" - -_isSetPartition :: [[Int]] -> Bool -_isSetPartition zzs = sort (concat zzs) == [1..n] where - n = sum' (map length zzs) - -instance HasNumberOfParts SetPartition where - numberOfParts (SetPartition p) = length p - --------------------------------------------------------------------------------- --- * Generating set partitions - --- | Synonym for 'setPartitionsNaive' -setPartitions :: Int -> [SetPartition] -setPartitions = setPartitionsNaive - --- | Synonym for 'setPartitionsWithKPartsNaive' --- --- > sort (setPartitionsWithKParts k n) == sort [ p | p <- setPartitions n , numberOfParts p == k ] --- -setPartitionsWithKParts - :: Int -- ^ @k@ = number of parts - -> Int -- ^ @n@ = size of the set - -> [SetPartition] -setPartitionsWithKParts = setPartitionsWithKPartsNaive - --- | List all set partitions of @[1..n]@, naive algorithm -setPartitionsNaive :: Int -> [SetPartition] -setPartitionsNaive n = map (SetPartition . _standardizeSetPartition) $ go [1..n] where - go :: [Int] -> [[[Int]]] - go [] = [[]] - go (z:zs) = [ s : rest | k <- [1..n] , s0 <- choose (k-1) zs , let s = z:s0 , rest <- go (zs \\ s) ] - --- | Set partitions of the set @[1..n]@ into @k@ parts -setPartitionsWithKPartsNaive - :: Int -- ^ @k@ = number of parts - -> Int -- ^ @n@ = size of the set - -> [SetPartition] -setPartitionsWithKPartsNaive k n = map (SetPartition . _standardizeSetPartition) $ go k [1..n] where - go :: Int -> [Int] -> [[[Int]]] - go !k [] = if k==0 then [[]] else [] - go 1 zs = [[zs]] - go !k (z:zs) = [ s : rest | l <- [1..n] , s0 <- choose (l-1) zs , let s = z:s0 , rest <- go (k-1) (zs \\ s) ] - - --- | Set partitions are counted by the Bell numbers -countSetPartitions :: Int -> Integer -countSetPartitions = bellNumber - --- | Set partitions of size @k@ are counted by the Stirling numbers of second kind -countSetPartitionsWithKParts - :: Int -- ^ @k@ = number of parts - -> Int -- ^ @n@ = size of the set - -> Integer -countSetPartitionsWithKParts k n = stirling2nd n k - --------------------------------------------------------------------------------- ++-- | Set partitions.+--+-- See eg. <http://en.wikipedia.org/wiki/Partition_of_a_set>+-- ++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.Set where++--------------------------------------------------------------------------------++import Data.List+import Data.Ord++import System.Random++import Math.Combinat.Sets+import Math.Combinat.Numbers+import Math.Combinat.Helper+import Math.Combinat.Partitions ( HasNumberOfParts(..) )++--------------------------------------------------------------------------------+-- * The type of set partitions++-- | A partition of the set @[1..n]@ (in standard order)+newtype SetPartition = SetPartition [[Int]] deriving (Eq,Ord,Show,Read)++_standardizeSetPartition :: [[Int]] -> [[Int]]+_standardizeSetPartition = sortBy (comparing myhead) . map sort where+ myhead xs = case xs of+ (x:xs) -> x+ [] -> error "_standardizeSetPartition: empty subset"++fromSetPartition :: SetPartition -> [[Int]]+fromSetPartition (SetPartition zzs) = zzs++toSetPartitionUnsafe :: [[Int]] -> SetPartition+toSetPartitionUnsafe = SetPartition++toSetPartition :: [[Int]] -> SetPartition+toSetPartition zzs = if _isSetPartition zzs+ then SetPartition (_standardizeSetPartition zzs)+ else error "toSetPartition: not a set partition"++_isSetPartition :: [[Int]] -> Bool+_isSetPartition zzs = sort (concat zzs) == [1..n] where + n = sum' (map length zzs)++instance HasNumberOfParts SetPartition where+ numberOfParts (SetPartition p) = length p++--------------------------------------------------------------------------------+-- * Generating set partitions++-- | Synonym for 'setPartitionsNaive'+setPartitions :: Int -> [SetPartition]+setPartitions = setPartitionsNaive++-- | Synonym for 'setPartitionsWithKPartsNaive'+--+-- > sort (setPartitionsWithKParts k n) == sort [ p | p <- setPartitions n , numberOfParts p == k ]+-- +setPartitionsWithKParts + :: Int -- ^ @k@ = number of parts+ -> Int -- ^ @n@ = size of the set+ -> [SetPartition]+setPartitionsWithKParts = setPartitionsWithKPartsNaive++-- | List all set partitions of @[1..n]@, naive algorithm+setPartitionsNaive :: Int -> [SetPartition]+setPartitionsNaive n = map (SetPartition . _standardizeSetPartition) $ go [1..n] where+ go :: [Int] -> [[[Int]]]+ go [] = [[]]+ go (z:zs) = [ s : rest | k <- [1..n] , s0 <- choose (k-1) zs , let s = z:s0 , rest <- go (zs \\ s) ]++-- | Set partitions of the set @[1..n]@ into @k@ parts+setPartitionsWithKPartsNaive + :: Int -- ^ @k@ = number of parts+ -> Int -- ^ @n@ = size of the set+ -> [SetPartition]+setPartitionsWithKPartsNaive k n = map (SetPartition . _standardizeSetPartition) $ go k [1..n] where+ go :: Int -> [Int] -> [[[Int]]]+ go !k [] = if k==0 then [[]] else []+ go 1 zs = [[zs]]+ go !k (z:zs) = [ s : rest | l <- [1..n] , s0 <- choose (l-1) zs , let s = z:s0 , rest <- go (k-1) (zs \\ s) ]+++-- | Set partitions are counted by the Bell numbers+countSetPartitions :: Int -> Integer+countSetPartitions = bellNumber ++-- | Set partitions of size @k@ are counted by the Stirling numbers of second kind+countSetPartitionsWithKParts + :: Int -- ^ @k@ = number of parts+ -> Int -- ^ @n@ = size of the set+ -> Integer+countSetPartitionsWithKParts k n = stirling2nd n k++--------------------------------------------------------------------------------
Math/Combinat/Partitions/Skew.hs view
@@ -1,67 +1,82 @@- --- | Skew partitions. --- --- Skew partitions are the difference of two integer partitions, denoted by @lambda/mu@. --- - -{-# LANGUAGE BangPatterns #-} -module Math.Combinat.Partitions.Skew where - --------------------------------------------------------------------------------- - -import Data.List - -import Math.Combinat.Partitions.Integer -import Math.Combinat.ASCII - --------------------------------------------------------------------------------- - --- | A skew partition @lambda/mu@ is represented by the list @[ (mu_i , lambda_i-mu_i) | i<-[1..n] ]@ -newtype SkewPartition = SkewPartition [(Int,Int)] deriving (Eq,Ord,Show) - --- | @mkSkewPartition (lambda,mu)@ creates the skew partition @lambda/mu@ -mkSkewPartition :: (Partition,Partition) -> SkewPartition -mkSkewPartition ( lam@(Partition bs) , mu@(Partition as)) = if mu `isSubPartitionOf` lam - then SkewPartition $ zipWith (\b a -> (a,b-a)) bs (as ++ repeat 0) - else error "mkSkewPartition: mu should be a subpartition of lambda!" - --- | This function \"cuts off\" the \"uninteresting parts\" of a skew partition -normalizeSkewPartition :: SkewPartition -> SkewPartition -normalizeSkewPartition (SkewPartition abs) = SkewPartition abs' where - (as,bs) = unzip abs - a0 = minimum as - k = length (takeWhile (==0) bs) - abs' = zip [ a-a0 | a <- drop k as ] (drop k bs) - --- | Returns the outer and inner partition of a skew partition, respectively -fromSkewPartition :: SkewPartition -> (Partition,Partition) -fromSkewPartition (SkewPartition list) = (toPartition (zipWith (+) as bs) , toPartition (filter (>0) as)) where - (as,bs) = unzip list - -outerPartition :: SkewPartition -> Partition -outerPartition = fst . fromSkewPartition - -innerPartition :: SkewPartition -> Partition -innerPartition = snd . fromSkewPartition - -dualSkewPartition :: SkewPartition -> SkewPartition -dualSkewPartition = mkSkewPartition . f . fromSkewPartition where - f (lam,mu) = (dualPartition lam, dualPartition mu) - -asciiSkewFerrersDiagram :: SkewPartition -> ASCII -asciiSkewFerrersDiagram = asciiSkewFerrersDiagram' ('@','.') EnglishNotation - -asciiSkewFerrersDiagram' - :: (Char,Char) - -> PartitionConvention -- Orientation - -> SkewPartition - -> ASCII -asciiSkewFerrersDiagram' (outer,inner) orient (SkewPartition abs) = asciiFromLines stuff where - stuff = case orient of - EnglishNotation -> ls - EnglishNotationCCW -> reverse (transpose ls) - FrenchNotation -> reverse ls - ls = [ replicate a inner ++ replicate b outer | (a,b) <- abs ] - - --------------------------------------------------------------------------------- ++-- | Skew partitions.+--+-- Skew partitions are the difference of two integer partitions, denoted by @lambda/mu@.+--++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.Skew where++--------------------------------------------------------------------------------++import Data.List++import Math.Combinat.Partitions.Integer+import Math.Combinat.ASCII++--------------------------------------------------------------------------------++-- | A skew partition @lambda/mu@ is represented by the list @[ (mu_i , lambda_i-mu_i) | i<-[1..n] ]@+newtype SkewPartition = SkewPartition [(Int,Int)] deriving (Eq,Ord,Show)++-- | @mkSkewPartition (lambda,mu)@ creates the skew partition @lambda/mu@.+-- Throws an error if @mu@ is not a sub-partition of @lambda@.+mkSkewPartition :: (Partition,Partition) -> SkewPartition+mkSkewPartition ( lam@(Partition bs) , mu@(Partition as)) = if mu `isSubPartitionOf` lam + then SkewPartition $ zipWith (\b a -> (a,b-a)) bs (as ++ repeat 0)+ else error "mkSkewPartition: mu should be a subpartition of lambda!" ++-- | Returns 'Nothing' if @mu@ is not a sub-partition of @lambda@.+safeSkewPartition :: (Partition,Partition) -> Maybe SkewPartition+safeSkewPartition ( lam@(Partition bs) , mu@(Partition as)) = if mu `isSubPartitionOf` lam + then Just $ SkewPartition $ zipWith (\b a -> (a,b-a)) bs (as ++ repeat 0)+ else Nothing++skewPartitionWeight :: SkewPartition -> Int+skewPartitionWeight (SkewPartition abs) = foldl' (+) 0 (map snd abs)++-- | This function \"cuts off\" the \"uninteresting parts\" of a skew partition+normalizeSkewPartition :: SkewPartition -> SkewPartition+normalizeSkewPartition (SkewPartition abs) = SkewPartition abs' where+ (as,bs) = unzip abs+ a0 = minimum as+ k = length (takeWhile (==0) bs)+ abs' = zip [ a-a0 | a <- drop k as ] (drop k bs)+ +-- | Returns the outer and inner partition of a skew partition, respectively+fromSkewPartition :: SkewPartition -> (Partition,Partition)+fromSkewPartition (SkewPartition list) = (toPartition (zipWith (+) as bs) , toPartition (filter (>0) as)) where+ (as,bs) = unzip list++outerPartition :: SkewPartition -> Partition +outerPartition = fst . fromSkewPartition ++innerPartition :: SkewPartition -> Partition +innerPartition = snd . fromSkewPartition ++dualSkewPartition :: SkewPartition -> SkewPartition+dualSkewPartition = mkSkewPartition . f . fromSkewPartition where+ f (lam,mu) = (dualPartition lam, dualPartition mu)++--------------------------------------------------------------------------------++asciiSkewFerrersDiagram :: SkewPartition -> ASCII+asciiSkewFerrersDiagram = asciiSkewFerrersDiagram' ('@','.') EnglishNotation++asciiSkewFerrersDiagram' + :: (Char,Char) + -> PartitionConvention -- Orientation+ -> SkewPartition + -> ASCII+asciiSkewFerrersDiagram' (outer,inner) orient (SkewPartition abs) = asciiFromLines stuff where+ stuff = case orient of+ EnglishNotation -> ls+ EnglishNotationCCW -> reverse (transpose ls)+ FrenchNotation -> reverse ls+ ls = [ replicate a inner ++ replicate b outer | (a,b) <- abs ]++instance DrawASCII SkewPartition where+ ascii = asciiSkewFerrersDiagram ++--------------------------------------------------------------------------------+
Math/Combinat/Partitions/Vector.hs view
@@ -1,82 +1,82 @@- --- | Vector partitions. See: --- --- * Donald E. Knuth: The Art of Computer Programming, vol 4, pre-fascicle 3B. --- - -{-# LANGUAGE BangPatterns #-} -module Math.Combinat.Partitions.Vector where - --------------------------------------------------------------------------------- - -import Data.Array.Unboxed -import Data.List - --------------------------------------------------------------------------------- - --- | Integer vectors. The indexing starts from 1. -type IntVector = UArray Int Int - --- | Vector partitions. Basically a synonym for 'fasc3B_algorithm_M'. -vectorPartitions :: IntVector -> [[IntVector]] -vectorPartitions = fasc3B_algorithm_M . elems - -_vectorPartitions :: [Int] -> [[[Int]]] -_vectorPartitions = map (map elems) . fasc3B_algorithm_M - --- | Generates all vector partitions --- (\"algorithm M\" in Knuth). --- The order is decreasing lexicographic. -fasc3B_algorithm_M :: [Int] -> [[IntVector]] -{- note to self: Knuth's descriptions of algorithms are still totally unreadable -} -fasc3B_algorithm_M xs = worker [start] where - - -- n = sum xs - m = length xs - - start = [ (j,x,x) | (j,x) <- zip [1..] xs ] - - worker stack@(last:_) = - case decrease stack' of - Nothing -> [visited] - Just stack'' -> visited : worker stack'' - where - stack' = subtract_rec stack - visited = map to_vector stack' - - decrease (last:rest) = - case span (\(_,_,v) -> v==0) (reverse last) of - ( _ , [(_,_,1)] ) -> case rest of - [] -> Nothing - _ -> decrease rest - ( second , (c,u,v):first ) -> Just (modified:rest) where - modified = - reverse first ++ - (c,u,v-1) : - [ (c,u,u) | (c,u,_) <- reverse second ] - _ -> error "fasc3B_algorithm_M: should not happen" - - to_vector cuvs = - accumArray (flip const) 0 (1,m) - [ (c,v) | (c,_,v) <- cuvs ] - - subtract_rec all@(last:_) = - case subtract last of - [] -> all - new -> subtract_rec (new:all) - - subtract [] = [] - subtract full@((c,u,v):rest) = - if w >= v - then (c,w,v) : subtract rest - else subtract_b full - where w = u - v - - subtract_b [] = [] - subtract_b ((c,u,v):rest) = - if w /= 0 - then (c,w,w) : subtract_b rest - else subtract_b rest - where w = u - v - --------------------------------------------------------------------------------- ++-- | Vector partitions. See:+--+-- * Donald E. Knuth: The Art of Computer Programming, vol 4, pre-fascicle 3B.+--++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.Vector where++--------------------------------------------------------------------------------++import Data.Array.Unboxed+import Data.List++--------------------------------------------------------------------------------++-- | Integer vectors. The indexing starts from 1.+type IntVector = UArray Int Int++-- | Vector partitions. Basically a synonym for 'fasc3B_algorithm_M'.+vectorPartitions :: IntVector -> [[IntVector]]+vectorPartitions = fasc3B_algorithm_M . elems++_vectorPartitions :: [Int] -> [[[Int]]]+_vectorPartitions = map (map elems) . fasc3B_algorithm_M++-- | Generates all vector partitions +-- (\"algorithm M\" in Knuth). +-- The order is decreasing lexicographic. +fasc3B_algorithm_M :: [Int] -> [[IntVector]] +{- note to self: Knuth's descriptions of algorithms are still totally unreadable -}+fasc3B_algorithm_M xs = worker [start] where++ -- n = sum xs+ m = length xs++ start = [ (j,x,x) | (j,x) <- zip [1..] xs ] + + worker stack@(last:_) = + case decrease stack' of+ Nothing -> [visited]+ Just stack'' -> visited : worker stack''+ where+ stack' = subtract_rec stack+ visited = map to_vector stack'+ + decrease (last:rest) = + case span (\(_,_,v) -> v==0) (reverse last) of+ ( _ , [(_,_,1)] ) -> case rest of+ [] -> Nothing+ _ -> decrease rest+ ( second , (c,u,v):first ) -> Just (modified:rest) where + modified = + reverse first ++ + (c,u,v-1) : + [ (c,u,u) | (c,u,_) <- reverse second ] + _ -> error "fasc3B_algorithm_M: should not happen"+ + to_vector cuvs = + accumArray (flip const) 0 (1,m)+ [ (c,v) | (c,_,v) <- cuvs ] ++ subtract_rec all@(last:_) = + case subtract last of + [] -> all+ new -> subtract_rec (new:all) ++ subtract [] = []+ subtract full@((c,u,v):rest) = + if w >= v + then (c,w,v) : subtract rest+ else subtract_b full+ where w = u - v+ + subtract_b [] = []+ subtract_b ((c,u,v):rest) = + if w /= 0 + then (c,w,w) : subtract_b rest+ else subtract_b rest+ where w = u - v++--------------------------------------------------------------------------------
Math/Combinat/Permutations.hs view
@@ -274,9 +274,9 @@ then Permutation result else error "multiply: permutations of different sets" where- (_,n) = bounds perm1- (_,m) = bounds perm2 - result = permute pi1 perm2 + (_,n) = bounds perm1+ (_,m) = bounds perm2 + result = permute pi1 perm2 infixr 7 `multiply`
Math/Combinat/Sign.hs view
@@ -1,48 +1,48 @@- --- | Signs - -{-# LANGUAGE BangPatterns #-} -module Math.Combinat.Sign where - --------------------------------------------------------------------------------- - -import Data.Monoid - --------------------------------------------------------------------------------- - -data Sign - = Plus - | Minus - deriving (Eq,Ord,Show,Read) - -instance Monoid Sign where - mempty = Plus - mappend = mulSign - mconcat = productOfSigns - -signValue :: Num a => Sign -> a -signValue s = case s of - Plus -> 1 - Minus -> -1 - -paritySign :: Integral a => a -> Sign -paritySign x = if even x then Plus else Minus - -oppositeSign :: Sign -> Sign -oppositeSign s = case s of - Plus -> Minus - Minus -> Plus - -mulSign :: Sign -> Sign -> Sign -mulSign s1 s2 = case s1 of - Plus -> s2 - Minus -> oppositeSign s2 - -productOfSigns :: [Sign] -> Sign -productOfSigns = go Plus where - go !acc [] = acc - go !acc (x:xs) = case x of - Plus -> go acc xs - Minus -> go (oppositeSign acc) xs - --------------------------------------------------------------------------------- ++-- | Signs++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Sign where++--------------------------------------------------------------------------------++import Data.Monoid++--------------------------------------------------------------------------------++data Sign+ = Plus+ | Minus+ deriving (Eq,Ord,Show,Read)++instance Monoid Sign where+ mempty = Plus+ mappend = mulSign+ mconcat = productOfSigns++signValue :: Num a => Sign -> a+signValue s = case s of + Plus -> 1 + Minus -> -1 ++paritySign :: Integral a => a -> Sign+paritySign x = if even x then Plus else Minus ++oppositeSign :: Sign -> Sign+oppositeSign s = case s of+ Plus -> Minus+ Minus -> Plus++mulSign :: Sign -> Sign -> Sign+mulSign s1 s2 = case s1 of+ Plus -> s2+ Minus -> oppositeSign s2++productOfSigns :: [Sign] -> Sign+productOfSigns = go Plus where+ go !acc [] = acc+ go !acc (x:xs) = case x of+ Plus -> go acc xs+ Minus -> go (oppositeSign acc) xs++--------------------------------------------------------------------------------
Math/Combinat/Tableaux.hs view
@@ -22,8 +22,11 @@ -- > ] -- +{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} module Math.Combinat.Tableaux where +--------------------------------------------------------------------------------+ import Data.List import Math.Combinat.Helper@@ -31,6 +34,9 @@ import Math.Combinat.Partitions import Math.Combinat.ASCII +import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+ -------------------------------------------------------------------------------- -- * Basic stuff @@ -41,6 +47,9 @@ $ (map . map) asciiShow $ t +instance Show a => DrawASCII (Tableau a) where + ascii = asciiTableau+ _shape :: Tableau a -> [Int] _shape t = map length t @@ -93,6 +102,25 @@ columnWordToTableau :: Ord a => [a] -> Tableau a columnWordToTableau = transpose . rowWordToTableau++-- | Checks whether a sequence of positive integers is a /lattice word/, +-- which means that in every initial part of the sequence any number @i@+-- occurs at least as often as the number @i+1@+--+isLatticeWord :: [Int] -> Bool+isLatticeWord = go Map.empty where+ go :: Map Int Int -> [Int] -> Bool+ go _ [] = True+ go !table (i:is) =+ if check i+ then go table' is+ else False+ where+ table' = Map.insertWith (+) i 1 table+ check j = j==1 || cnt (j-1) >= cnt j+ cnt j = case Map.lookup j table' of+ Just k -> k+ Nothing -> 0 -------------------------------------------------------------------------------- -- * Standard Young tableaux
Math/Combinat/Tableaux/GelfandTsetlin.hs view
@@ -1,343 +1,341 @@- --- | Gelfand-Tsetlin patterns and Kostka numbers. --- --- Gelfand-Tsetlin patterns (or tableaux) are triangular arrays like --- --- > [ 3 ] --- > [ 3 , 2 ] --- > [ 3 , 1 , 0 ] --- > [ 2 , 0 , 0 , 0 ] --- --- with both rows and columns non-increasing non-negative integers. --- Note: these are in bijection with the semi-standard Young tableaux. --- --- If we add the further restriction that --- the top diagonal reads @lambda@, --- and the diagonal sums are partial sums of @mu@, where @lambda@ and @mu@ are two --- partitions (in this case @lambda=[3,2]@ and @mu=[2,1,1,1]@), --- then the number of the resulting patterns --- or tableaux is the Kostka number @K(lambda,mu)@. --- Actually @mu@ doesn't even need to the be non-increasing. --- - -{-# LANGUAGE BangPatterns, ScopedTypeVariables #-} -module Math.Combinat.Tableaux.GelfandTsetlin where - --------------------------------------------------------------------------------- - -import Data.List -import Data.Maybe -import Data.Monoid -import Data.Ord - -import Control.Monad -import Control.Monad.Trans.State - -import Data.Map (Map) -import qualified Data.Map as Map - -import Math.Combinat.Partitions.Integer -import Math.Combinat.Tableaux -import Math.Combinat.Helper -import Math.Combinat.ASCII - --------------------------------------------------------------------------------- --- * Kostka numbers - --- | Kostka numbers (via counting Gelfand-Tsetlin patterns). See for example <http://en.wikipedia.org/wiki/Kostka_number> --- --- @K(lambda,mu)==0@ unless @lambda@ dominates @mu@: --- --- > [ mu | mu <- partitions (weight lam) , kostkaNumber lam mu > 0 ] == dominatedPartitions lam --- -kostkaNumber :: Partition -> Partition -> Int -kostkaNumber = countKostkaGelfandTsetlinPatterns - --- | Very naive (and slow) implementation of Kostka numbers, for reference. -kostkaNumberReferenceNaive :: Partition -> Partition -> Int -kostkaNumberReferenceNaive plambda pmu@(Partition mu) = length stuff where - stuff = [ 1 | t <- semiStandardYoungTableaux k plambda , cond t ] - k = length mu - cond t = [ (head xs, length xs) | xs <- group (sort $ concat t) ] == zip [1..] mu - --------------------------------------------------------------------------------- - --- | Lists all (positive) Kostka numbers @K(lambda,mu)@ with the given @lambda@: --- --- > kostkaNumbersWithGivenLambda lambda == Map.fromList [ (mu , kostkaNumber lambda mu) | mu <- dominatedPartitions lambda ] --- --- It's much faster than computing the individual Kostka numbers, but not as fast --- as it could be. --- -{-# SPECIALIZE kostkaNumbersWithGivenLambda :: Partition -> Map Partition Int #-} -{-# SPECIALIZE kostkaNumbersWithGivenLambda :: Partition -> Map Partition Integer #-} -kostkaNumbersWithGivenLambda :: forall coeff. Num coeff => Partition -> Map Partition coeff -kostkaNumbersWithGivenLambda plambda@(Partition lam) = evalState (worker lam) Map.empty where - - worker :: [Int] -> State (Map Partition (Map Partition coeff)) (Map Partition coeff) - worker unlam = case unlam of - [] -> return $ Map.singleton (Partition []) 1 - _ -> do - cache <- get - case Map.lookup (Partition unlam) cache of - Just sol -> return sol - Nothing -> do - let s = foldl' (+) 0 unlam - subsols <- forM (prevLambdas0 unlam) $ \p -> do - sub <- worker p - let t = s - foldl' (+) 0 p - f (Partition xs , c) = case xs of - (y:_) -> if t >= y then Just (Partition (t:xs) , c) else Nothing - [] -> if t > 0 then Just (Partition [t] , c) else Nothing - if t > 0 - then return $ Map.fromList $ mapMaybe f $ Map.toList sub - else return $ Map.empty - - let sol = Map.unionsWith (+) subsols - put $! (Map.insert (Partition unlam) sol cache) - return sol - - -- needs decreasing sequence - prevLambdas0 :: [Int] -> [[Int]] - prevLambdas0 (l:ls) = go l ls where - go b [a] = [ [x] | x <- [a..b] ] ++ [ [x,y] | x <- [a..b] , y<-[1..a] ] - go b (a:as) = [ x:xs | x <- [a..b] , xs <- go a as ] - go b [] = [] : [ [j] | j <- [1..b] ] - prevLambdas0 [] = [] - --- | Lists all (positive) Kostka numbers @K(lambda,mu)@ with the given @mu@: --- --- > kostkaNumbersWithGivenMu mu == Map.fromList [ (lambda , kostkaNumber lambda mu) | lambda <- dominatingPartitions mu ] --- --- This function uses the iterated Pieri rule, and is relatively fast. --- -kostkaNumbersWithGivenMu :: Partition -> Map Partition Int -kostkaNumbersWithGivenMu (Partition mu) = iteratedPieriRule (reverse mu) - --------------------------------------------------------------------------------- --- * Gelfand-Tsetlin patterns - --- | A Gelfand-Tstetlin tableau -type GT = [[Int]] - - -asciiGT :: GT -> ASCII -asciiGT gt = tabulate (HRight,VTop) (HSepSpaces 1, VSepEmpty) - $ (map . map) asciiShow - $ gt - - -kostkaGelfandTsetlinPatterns :: Partition -> Partition -> [GT] -kostkaGelfandTsetlinPatterns lambda (Partition mu) = kostkaGelfandTsetlinPatterns' lambda mu - --- | Generates all Kostka-Gelfand-Tsetlin tableau, that is, triangular arrays like --- --- > [ 3 ] --- > [ 3 , 2 ] --- > [ 3 , 1 , 0 ] --- > [ 2 , 0 , 0 , 0 ] --- --- with both rows and column non-increasing such that --- the top diagonal read lambda (in this case @lambda=[3,2]@) and the diagonal sums --- are partial sums of mu (in this case @mu=[2,1,1,1]@) --- --- The number of such GT tableaux is the Kostka --- number K(lambda,mu). --- -kostkaGelfandTsetlinPatterns' :: Partition -> [Int] -> [GT] -kostkaGelfandTsetlinPatterns' plam@(Partition lambda0) mu0 - | minimum mu0 < 0 = [] - | wlam == 0 = if wmu == 0 then [ [] ] else [] - | wmu == wlam && plam `dominates` pmu = list - | otherwise = [] - where - - pmu = mkPartition mu0 - - nlam = length lambda0 - nmu = length mu0 - - n = max nlam nmu - - lambda = lambda0 ++ replicate (n - nlam) 0 - mu = mu0 ++ replicate (n - nmu ) 0 - - revlam = reverse lambda - - wmu = sum' mu - wlam = sum' lambda - - list = worker - revlam - (scanl1 (+) mu) - (replicate (n-1) 0) - (replicate (n ) 0) - [] - - worker - :: [Int] -- lambda_i in reverse order - -> [Int] -- partial sums of mu - -> [Int] -- sums of the tails of previous rows - -> [Int] -- last row - -> [[Int]] -- the lower part of GT tableau we accumulated so far (this is not needed if we only want to count) - -> [GT] - - worker (rl:rls) (smu:smus) (a:acc) (lastx0:lastrowt) table = stuff - where - x0 = smu - a - stuff = concat - [ worker rls smus (zipWith (+) acc (tail row)) (init row) (row:table) - | row <- boundedNonIncrSeqs' x0 (map (max rl) (max lastx0 x0 : lastrowt)) lambda - ] - worker [rl] _ _ _ table = [ [rl]:table ] - worker [] _ _ _ _ = [ [] ] - - boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]] - boundedNonIncrSeqs' = go where - go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ] - go _ [] _ = [[]] - go _ _ [] = [[]] - --------------------------------------------------------------------------------- - --- | This returns the corresponding Kostka number: --- --- > countKostkaGelfandTsetlinPatterns lambda mu == length (kostkaGelfandTsetlinPatterns lambda mu) --- -countKostkaGelfandTsetlinPatterns :: Partition -> Partition -> Int -countKostkaGelfandTsetlinPatterns plam@(Partition lambda0) pmu@(Partition mu0) - | wlam == 0 = if wmu == 0 then 1 else 0 - | wmu == wlam && plam `dominates` pmu = cnt - | otherwise = 0 - where - - nlam = length lambda0 - nmu = length mu0 - - n = max nlam nmu - - lambda = lambda0 ++ replicate (n - nlam) 0 - mu = mu0 ++ replicate (n - nmu ) 0 - - revlam = reverse lambda - - wmu = sum' mu - wlam = sum' lambda - - cnt = worker - revlam - (scanl1 (+) mu) - (replicate (n-1) 0) - (replicate (n ) 0) - - worker - :: [Int] -- lambda_i in reverse order - -> [Int] -- partial sums of mu - -> [Int] -- sums of the tails of previous rows - -> [Int] -- last row - -> Int - - worker (rl:rls) (smu:smus) (a:acc) (lastx0:lastrowt) = stuff - where - x0 = smu - a - stuff = sum' - [ worker rls smus (zipWith (+) acc (tail row)) (init row) - | row <- boundedNonIncrSeqs' x0 (map (max rl) (max lastx0 x0 : lastrowt)) lambda - ] - worker [rl] _ _ _ = 1 - worker [] _ _ _ = 1 - - boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]] - boundedNonIncrSeqs' = go where - go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ] - go _ [] _ = [[]] - go _ _ [] = [[]] - --------------------------------------------------------------------------------- - -{- - --- | All non-increasing sentences between a lower and an upper bound -boundedNonIncrSeqs :: [Int] -> [Int] -> [[Int]] -boundedNonIncrSeqs as bs = case bs of - (h0:_) -> boundedNonIncrSeqs' h0 as bs - [] -> [[]] - --- | All non-increasing sentences between a lower and an upper bound, and also less-or-equal than the given number -boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]] -boundedNonIncrSeqs' = go where - go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ] - go _ [] _ = [[]] - go _ _ [] = [[]] - --- | All non-decreasing sentences between a lower and an upper bound -boundedNonDecrSeqs :: [Int] -> [Int] -> [[Int]] -boundedNonDecrSeqs = boundedNonDecrSeqs' 0 - --- | All non-decreasing sentences between a lower and an upper bound, and also greator-or-equal then the given number -boundedNonDecrSeqs' :: Int -> [Int] -> [Int] -> [[Int]] -boundedNonDecrSeqs' h0 = go (max 0 h0) where - go h0 (a:as) (b:bs) = [ h:hs | h <- [(max h0 a)..b] , hs <- go h as bs ] - go _ [] _ = [[]] - go _ _ [] = [[]] - --} - --------------------------------------------------------------------------------- --- * The iterated Pieri rule - --- | Computes the Schur expansion of @h[n1]*h[n2]*h[n3]*...*h[nk]@ via iterating the Pieri rule. --- Note: the coefficients are actually the Kostka numbers; the following is true: --- --- > Map.toList (iteratedPieriRule (fromPartition mu)) == [ (lam, kostkaNumber lam mu) | lam <- dominatingPartitions mu ] --- --- This should be faster than individually computing all these Kostka numbers. --- -iteratedPieriRule :: Num coeff => [Int] -> Map Partition coeff -iteratedPieriRule = iteratedPieriRule' (Partition []) - --- | Iterating the Pieri rule, we can compute the Schur expansion of --- @h[lambda]*h[n1]*h[n2]*h[n3]*...*h[nk]@ -iteratedPieriRule' :: Num coeff => Partition -> [Int] -> Map Partition coeff -iteratedPieriRule' plambda ns = iteratedPieriRule'' (plambda,1) ns - -{-# SPECIALIZE iteratedPieriRule'' :: (Partition,Int ) -> [Int] -> Map Partition Int #-} -{-# SPECIALIZE iteratedPieriRule'' :: (Partition,Integer) -> [Int] -> Map Partition Integer #-} -iteratedPieriRule'' :: Num coeff => (Partition,coeff) -> [Int] -> Map Partition coeff -iteratedPieriRule'' (plambda,coeff0) ns = worker (Map.singleton plambda coeff0) ns where - worker old [] = old - worker old (n:ns) = worker new ns where - stuff = [ (coeff, pieriRule lam n) | (lam,coeff) <- Map.toList old ] - new = foldl' f Map.empty stuff - f t0 (c,ps) = foldl' (\t p -> Map.insertWith (+) p c t) t0 ps - --------------------------------------------------------------------------------- - --- | Computes the Schur expansion of @e[n1]*e[n2]*e[n3]*...*e[nk]@ via iterating the Pieri rule. --- Note: the coefficients are actually the Kostka numbers; the following is true: --- --- > Map.toList (iteratedDualPieriRule (fromPartition mu)) == --- > [ (dualPartition lam, kostkaNumber lam mu) | lam <- dominatingPartitions mu ] --- --- This should be faster than individually computing all these Kostka numbers. --- It is a tiny bit slower than 'iteratedPieriRule'. --- -iteratedDualPieriRule :: Num coeff => [Int] -> Map Partition coeff -iteratedDualPieriRule = iteratedDualPieriRule' (Partition []) - --- | Iterating the Pieri rule, we can compute the Schur expansion of --- @e[lambda]*e[n1]*e[n2]*e[n3]*...*e[nk]@ -iteratedDualPieriRule' :: Num coeff => Partition -> [Int] -> Map Partition coeff -iteratedDualPieriRule' plambda ns = iteratedDualPieriRule'' (plambda,1) ns - -{-# SPECIALIZE iteratedDualPieriRule'' :: (Partition,Int ) -> [Int] -> Map Partition Int #-} -{-# SPECIALIZE iteratedDualPieriRule'' :: (Partition,Integer) -> [Int] -> Map Partition Integer #-} -iteratedDualPieriRule'' :: Num coeff => (Partition,coeff) -> [Int] -> Map Partition coeff -iteratedDualPieriRule'' (plambda,coeff0) ns = worker (Map.singleton plambda coeff0) ns where - worker old [] = old - worker old (n:ns) = worker new ns where - stuff = [ (coeff, dualPieriRule lam n) | (lam,coeff) <- Map.toList old ] - new = foldl' f Map.empty stuff - f t0 (c,ps) = foldl' (\t p -> Map.insertWith (+) p c t) t0 ps - --------------------------------------------------------------------------------- ++-- | Gelfand-Tsetlin patterns and Kostka numbers.+--+-- Gelfand-Tsetlin patterns (or tableaux) are triangular arrays like+--+-- > [ 3 ]+-- > [ 3 , 2 ]+-- > [ 3 , 1 , 0 ]+-- > [ 2 , 0 , 0 , 0 ]+--+-- with both rows and columns non-increasing non-negative integers.+-- Note: these are in bijection with the semi-standard Young tableaux.+--+-- If we add the further restriction that+-- the top diagonal reads @lambda@, +-- and the diagonal sums are partial sums of @mu@, where @lambda@ and @mu@ are two+-- partitions (in this case @lambda=[3,2]@ and @mu=[2,1,1,1]@), +-- then the number of the resulting patterns +-- or tableaux is the Kostka number @K(lambda,mu)@.+-- Actually @mu@ doesn't even need to the be non-increasing.+--++{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}+module Math.Combinat.Tableaux.GelfandTsetlin where++--------------------------------------------------------------------------------++import Data.List+import Data.Maybe+import Data.Monoid+import Data.Ord++import Control.Monad+import Control.Monad.Trans.State++import Data.Map (Map)+import qualified Data.Map as Map++import Math.Combinat.Partitions.Integer+import Math.Combinat.Tableaux+import Math.Combinat.Helper+import Math.Combinat.ASCII++--------------------------------------------------------------------------------+-- * Kostka numbers++-- | Kostka numbers (via counting Gelfand-Tsetlin patterns). See for example <http://en.wikipedia.org/wiki/Kostka_number>+--+-- @K(lambda,mu)==0@ unless @lambda@ dominates @mu@:+--+-- > [ mu | mu <- partitions (weight lam) , kostkaNumber lam mu > 0 ] == dominatedPartitions lam+--+kostkaNumber :: Partition -> Partition -> Int+kostkaNumber = countKostkaGelfandTsetlinPatterns++-- | Very naive (and slow) implementation of Kostka numbers, for reference.+kostkaNumberReferenceNaive :: Partition -> Partition -> Int+kostkaNumberReferenceNaive plambda pmu@(Partition mu) = length stuff where+ stuff = [ (1::Int) | t <- semiStandardYoungTableaux k plambda , cond t ]+ k = length mu+ cond t = [ (head xs, length xs) | xs <- group (sort $ concat t) ] == zip [1..] mu ++--------------------------------------------------------------------------------++-- | Lists all (positive) Kostka numbers @K(lambda,mu)@ with the given @lambda@:+--+-- > kostkaNumbersWithGivenLambda lambda == Map.fromList [ (mu , kostkaNumber lambda mu) | mu <- dominatedPartitions lambda ]+--+-- It's much faster than computing the individual Kostka numbers, but not as fast+-- as it could be.+--+{-# SPECIALIZE kostkaNumbersWithGivenLambda :: Partition -> Map Partition Int #-}+{-# SPECIALIZE kostkaNumbersWithGivenLambda :: Partition -> Map Partition Integer #-}+kostkaNumbersWithGivenLambda :: forall coeff. Num coeff => Partition -> Map Partition coeff+kostkaNumbersWithGivenLambda plambda@(Partition lam) = evalState (worker lam) Map.empty where++ worker :: [Int] -> State (Map Partition (Map Partition coeff)) (Map Partition coeff)+ worker unlam = case unlam of+ [] -> return $ Map.singleton (Partition []) 1+ _ -> do+ cache <- get+ case Map.lookup (Partition unlam) cache of+ Just sol -> return sol+ Nothing -> do+ let s = foldl' (+) 0 unlam+ subsols <- forM (prevLambdas0 unlam) $ \p -> do+ sub <- worker p + let t = s - foldl' (+) 0 p + f (Partition xs , c) = case xs of+ (y:_) -> if t >= y then Just (Partition (t:xs) , c) else Nothing+ [] -> if t > 0 then Just (Partition [t] , c) else Nothing+ if t > 0+ then return $ Map.fromList $ mapMaybe f $ Map.toList sub+ else return $ Map.empty++ let sol = Map.unionsWith (+) subsols+ put $! (Map.insert (Partition unlam) sol cache)+ return sol++ -- needs decreasing sequence+ prevLambdas0 :: [Int] -> [[Int]]+ prevLambdas0 (l:ls) = go l ls where+ go b [a] = [ [x] | x <- [a..b] ] ++ [ [x,y] | x <- [a..b] , y<-[1..a] ]+ go b (a:as) = [ x:xs | x <- [a..b] , xs <- go a as ]+ go b [] = [] : [ [j] | j <- [1..b] ]+ prevLambdas0 [] = []++-- | Lists all (positive) Kostka numbers @K(lambda,mu)@ with the given @mu@:+--+-- > kostkaNumbersWithGivenMu mu == Map.fromList [ (lambda , kostkaNumber lambda mu) | lambda <- dominatingPartitions mu ]+--+-- This function uses the iterated Pieri rule, and is relatively fast.+--+kostkaNumbersWithGivenMu :: Partition -> Map Partition Int+kostkaNumbersWithGivenMu (Partition mu) = iteratedPieriRule (reverse mu)++--------------------------------------------------------------------------------+-- * Gelfand-Tsetlin patterns++-- | A Gelfand-Tstetlin tableau+type GT = [[Int]]++asciiGT :: GT -> ASCII+asciiGT gt = tabulate (HRight,VTop) (HSepSpaces 1, VSepEmpty) + $ (map . map) asciiShow+ $ gt++kostkaGelfandTsetlinPatterns :: Partition -> Partition -> [GT]+kostkaGelfandTsetlinPatterns lambda (Partition mu) = kostkaGelfandTsetlinPatterns' lambda mu++-- | Generates all Kostka-Gelfand-Tsetlin tableau, that is, triangular arrays like+--+-- > [ 3 ]+-- > [ 3 , 2 ]+-- > [ 3 , 1 , 0 ]+-- > [ 2 , 0 , 0 , 0 ]+--+-- with both rows and column non-increasing such that+-- the top diagonal read lambda (in this case @lambda=[3,2]@) and the diagonal sums+-- are partial sums of mu (in this case @mu=[2,1,1,1]@)+--+-- The number of such GT tableaux is the Kostka+-- number K(lambda,mu).+--+kostkaGelfandTsetlinPatterns' :: Partition -> [Int] -> [GT]+kostkaGelfandTsetlinPatterns' plam@(Partition lambda0) mu0+ | minimum mu0 < 0 = []+ | wlam == 0 = if wmu == 0 then [ [] ] else []+ | wmu == wlam && plam `dominates` pmu = list+ | otherwise = []+ where++ pmu = mkPartition mu0++ nlam = length lambda0+ nmu = length mu0++ n = max nlam nmu++ lambda = lambda0 ++ replicate (n - nlam) 0+ mu = mu0 ++ replicate (n - nmu ) 0++ revlam = reverse lambda++ wmu = sum' mu+ wlam = sum' lambda++ list = worker + revlam + (scanl1 (+) mu) + (replicate (n-1) 0) + (replicate (n ) 0) + []++ worker+ :: [Int] -- lambda_i in reverse order+ -> [Int] -- partial sums of mu+ -> [Int] -- sums of the tails of previous rows+ -> [Int] -- last row+ -> [[Int]] -- the lower part of GT tableau we accumulated so far (this is not needed if we only want to count)+ -> [GT] ++ worker (rl:rls) (smu:smus) (a:acc) (lastx0:lastrowt) table = stuff + where+ x0 = smu - a+ stuff = concat + [ worker rls smus (zipWith (+) acc (tail row)) (init row) (row:table)+ | row <- boundedNonIncrSeqs' x0 (map (max rl) (max lastx0 x0 : lastrowt)) lambda+ ]+ worker [rl] _ _ _ table = [ [rl]:table ] + worker [] _ _ _ _ = [ [] ]++ boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]]+ boundedNonIncrSeqs' = go where+ go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ]+ go _ [] _ = [[]]+ go _ _ [] = [[]]++--------------------------------------------------------------------------------++-- | This returns the corresponding Kostka number:+--+-- > countKostkaGelfandTsetlinPatterns lambda mu == length (kostkaGelfandTsetlinPatterns lambda mu)+-- +countKostkaGelfandTsetlinPatterns :: Partition -> Partition -> Int+countKostkaGelfandTsetlinPatterns plam@(Partition lambda0) pmu@(Partition mu0) + | wlam == 0 = if wmu == 0 then 1 else 0+ | wmu == wlam && plam `dominates` pmu = cnt+ | otherwise = 0+ where++ nlam = length lambda0+ nmu = length mu0++ n = max nlam nmu++ lambda = lambda0 ++ replicate (n - nlam) 0+ mu = mu0 ++ replicate (n - nmu ) 0++ revlam = reverse lambda++ wmu = sum' mu+ wlam = sum' lambda++ cnt = worker + revlam + (scanl1 (+) mu) + (replicate (n-1) 0) + (replicate (n ) 0) ++ worker+ :: [Int] -- lambda_i in reverse order+ -> [Int] -- partial sums of mu+ -> [Int] -- sums of the tails of previous rows+ -> [Int] -- last row+ -> Int++ worker (rl:rls) (smu:smus) (a:acc) (lastx0:lastrowt) = stuff + where+ x0 = smu - a+ stuff = sum'+ [ worker rls smus (zipWith (+) acc (tail row)) (init row) + | row <- boundedNonIncrSeqs' x0 (map (max rl) (max lastx0 x0 : lastrowt)) lambda+ ]+ worker [rl] _ _ _ = 1 + worker [] _ _ _ = 1++ boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]]+ boundedNonIncrSeqs' = go where+ go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ]+ go _ [] _ = [[]]+ go _ _ [] = [[]]++--------------------------------------------------------------------------------++{-++-- | All non-increasing sentences between a lower and an upper bound+boundedNonIncrSeqs :: [Int] -> [Int] -> [[Int]]+boundedNonIncrSeqs as bs = case bs of + (h0:_) -> boundedNonIncrSeqs' h0 as bs+ [] -> [[]]++-- | All non-increasing sentences between a lower and an upper bound, and also less-or-equal than the given number+boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]]+boundedNonIncrSeqs' = go where+ go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ]+ go _ [] _ = [[]]+ go _ _ [] = [[]]++-- | All non-decreasing sentences between a lower and an upper bound+boundedNonDecrSeqs :: [Int] -> [Int] -> [[Int]]+boundedNonDecrSeqs = boundedNonDecrSeqs' 0++-- | All non-decreasing sentences between a lower and an upper bound, and also greator-or-equal then the given number+boundedNonDecrSeqs' :: Int -> [Int] -> [Int] -> [[Int]]+boundedNonDecrSeqs' h0 = go (max 0 h0) where+ go h0 (a:as) (b:bs) = [ h:hs | h <- [(max h0 a)..b] , hs <- go h as bs ]+ go _ [] _ = [[]]+ go _ _ [] = [[]]++-}++--------------------------------------------------------------------------------+-- * The iterated Pieri rule ++-- | Computes the Schur expansion of @h[n1]*h[n2]*h[n3]*...*h[nk]@ via iterating the Pieri rule.+-- Note: the coefficients are actually the Kostka numbers; the following is true:+--+-- > Map.toList (iteratedPieriRule (fromPartition mu)) == [ (lam, kostkaNumber lam mu) | lam <- dominatingPartitions mu ]+-- +-- This should be faster than individually computing all these Kostka numbers.+--+iteratedPieriRule :: Num coeff => [Int] -> Map Partition coeff+iteratedPieriRule = iteratedPieriRule' (Partition [])++-- | Iterating the Pieri rule, we can compute the Schur expansion of+-- @h[lambda]*h[n1]*h[n2]*h[n3]*...*h[nk]@+iteratedPieriRule' :: Num coeff => Partition -> [Int] -> Map Partition coeff+iteratedPieriRule' plambda ns = iteratedPieriRule'' (plambda,1) ns++{-# SPECIALIZE iteratedPieriRule'' :: (Partition,Int ) -> [Int] -> Map Partition Int #-}+{-# SPECIALIZE iteratedPieriRule'' :: (Partition,Integer) -> [Int] -> Map Partition Integer #-}+iteratedPieriRule'' :: Num coeff => (Partition,coeff) -> [Int] -> Map Partition coeff+iteratedPieriRule'' (plambda,coeff0) ns = worker (Map.singleton plambda coeff0) ns where+ worker old [] = old+ worker old (n:ns) = worker new ns where+ stuff = [ (coeff, pieriRule lam n) | (lam,coeff) <- Map.toList old ] + new = foldl' f Map.empty stuff + f t0 (c,ps) = foldl' (\t p -> Map.insertWith (+) p c t) t0 ps ++--------------------------------------------------------------------------------++-- | Computes the Schur expansion of @e[n1]*e[n2]*e[n3]*...*e[nk]@ via iterating the Pieri rule.+-- Note: the coefficients are actually the Kostka numbers; the following is true:+--+-- > Map.toList (iteratedDualPieriRule (fromPartition mu)) == +-- > [ (dualPartition lam, kostkaNumber lam mu) | lam <- dominatingPartitions mu ]+-- +-- This should be faster than individually computing all these Kostka numbers.+-- It is a tiny bit slower than 'iteratedPieriRule'.+--+iteratedDualPieriRule :: Num coeff => [Int] -> Map Partition coeff+iteratedDualPieriRule = iteratedDualPieriRule' (Partition [])++-- | Iterating the Pieri rule, we can compute the Schur expansion of+-- @e[lambda]*e[n1]*e[n2]*e[n3]*...*e[nk]@+iteratedDualPieriRule' :: Num coeff => Partition -> [Int] -> Map Partition coeff+iteratedDualPieriRule' plambda ns = iteratedDualPieriRule'' (plambda,1) ns++{-# SPECIALIZE iteratedDualPieriRule'' :: (Partition,Int ) -> [Int] -> Map Partition Int #-}+{-# SPECIALIZE iteratedDualPieriRule'' :: (Partition,Integer) -> [Int] -> Map Partition Integer #-}+iteratedDualPieriRule'' :: Num coeff => (Partition,coeff) -> [Int] -> Map Partition coeff+iteratedDualPieriRule'' (plambda,coeff0) ns = worker (Map.singleton plambda coeff0) ns where+ worker old [] = old+ worker old (n:ns) = worker new ns where+ stuff = [ (coeff, dualPieriRule lam n) | (lam,coeff) <- Map.toList old ] + new = foldl' f Map.empty stuff + f t0 (c,ps) = foldl' (\t p -> Map.insertWith (+) p c t) t0 ps ++--------------------------------------------------------------------------------
Math/Combinat/Tableaux/GelfandTsetlin/Cone.hs view
@@ -47,6 +47,7 @@ -- to the dimension), which encode the combinatorics of Kostka numbers. -- +{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} module Math.Combinat.Tableaux.GelfandTsetlin.Cone ( -- * Types@@ -115,12 +116,10 @@ range (a,b) = map deIndex' [ index' a .. index' b ] rangeSize (a,b) = index' b - index' a + 1 -{-# SPECIALIZE triangularArrayUnsafe :: Tableau Int -> TriangularArray Int #-} triangularArrayUnsafe :: Tableau a -> TriangularArray a triangularArrayUnsafe tableau = listArray (Tri (1,1),Tri (k,k)) (concat tableau) where k = length tableau -{-# SPECIALIZE fromTriangularArray :: TriangularArray Int -> Tableau Int #-} fromTriangularArray :: TriangularArray a -> Tableau a fromTriangularArray arr = (map.map) snd $ groupBy (equating f) $ assocs arr where f = fst . unTri . fst@@ -135,6 +134,12 @@ $ (map . map) asciiShow $ xxs +instance Show a => DrawASCII (TriangularArray a) where+ ascii = asciiTriangularArray++-- instance Show a => DrawASCII (Tableau a) where+-- ascii = asciiTableau+ -------------------------------------------------------------------------------- -- "fractional fillings"@@ -149,7 +154,6 @@ nextHole :: Hole -> Hole nextHole (Hole k l) = Hole k (l+1) -{-# SPECIALIZE reverseTableau :: [[Int]] -> [[Int]] #-} reverseTableau :: [[a]] -> [[a]] reverseTableau = reverse . map reverse
Math/Combinat/Tableaux/LittlewoodRichardson.hs view
@@ -2,22 +2,38 @@ -- | The Littlewood-Richardson rule module Math.Combinat.Tableaux.LittlewoodRichardson - ( lrRule , _lrRule+ ( lrRule , _lrRule + , lrRuleNaive ) where -------------------------------------------------------------------------------- import Data.List+import Data.Maybe import Math.Combinat.Partitions.Integer import Math.Combinat.Partitions.Skew+import Math.Combinat.Tableaux+import Math.Combinat.Tableaux.Skew import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map -------------------------------------------------------------------------------- +-- | Naive, reference implementation of the Littlewood-Richardson rule, based on the definition+-- "count the semistandard skew tableaux whose row content is a lattice word"+--+lrRuleNaive :: SkewPartition -> Map Partition Int+lrRuleNaive skew = final where+ n = skewPartitionWeight skew+ ssst = semiStandardSkewTableaux n skew + final = foldl' f Map.empty $ catMaybes [ skewTableauRowContent skew | skew <- ssst ]+ f old nu = Map.insertWith (+) nu 1 old++--------------------------------------------------------------------------------+ -- | @lrRule@ computes the expansion of a skew Schur function -- @s[lambda/mu]@ via the Littlewood-Richardson rule. --@@ -35,7 +51,7 @@ {-# SPECIALIZE _lrRule :: Partition -> Partition -> Map Partition Integer #-} _lrRule :: Num coeff => Partition -> Partition -> Map Partition coeff _lrRule plam@(Partition lam) pmu@(Partition mu0) = - if not (plam `dominates` pmu) + if not (pmu `isSubPartitionOf` plam) then Map.empty else foldl' f Map.empty [ nu | (nu,_) <- fillings n diagram ] where@@ -107,8 +123,19 @@ ub = if upper>0 then min (length shape) (lpart !! (upper-1)) else length shape+ nlist = filter (>0) $ map f [lb+1..ub] f j = if j==1 || shape!!(j-2) > shape!!(j-1) then j else 0++{-+ -- another nlist implementation, but doesn't seem to be faster+ (h0:hs0) = drop lb (-666:shape)+ nlist = go h0 hs0 [lb+1..ub] where+ go !lasth (h:hs) (j:js) = if j==1 || lasth > h + then j : go h hs js + else go h hs js+ go _ _ [] = []+-} -- increments the i-th element (starting from 1) incr :: Int -> [Int] -> [Int]
Math/Combinat/Tableaux/Skew.hs view
@@ -1,78 +1,180 @@- --- | Skew tableaux are skew partitions filled with numbers. - -{-# LANGUAGE BangPatterns #-} - -module Math.Combinat.Tableaux.Skew where - --------------------------------------------------------------------------------- - -import Data.List - -import Math.Combinat.Partitions.Integer -import Math.Combinat.Partitions.Skew -import Math.Combinat.Tableaux -import Math.Combinat.ASCII - --------------------------------------------------------------------------------- - --- | A skew tableau is represented by a list of offsets and entries -newtype SkewTableau a = SkewTableau [(Int,[a])] deriving (Eq,Ord,Show) - -instance Functor SkewTableau where - fmap f (SkewTableau t) = SkewTableau [ (a, map f xs) | (a,xs) <- t ] - -skewShape :: SkewTableau a -> SkewPartition -skewShape (SkewTableau list) = SkewPartition [ (o,length xs) | (o,xs) <- list ] - --- | Semi-standard skew tableaux filled with numbers @[1..n]@ -semiStandardSkewTableaux :: Int -> SkewPartition -> [SkewTableau Int] -semiStandardSkewTableaux n (SkewPartition abs) = map SkewTableau stuff where - - stuff = worker as bs ds (repeat 1) - (as,bs) = unzip abs - ds = diffSequence as - - -- | @worker inner outerMinusInner innerdiffs lowerbound - worker :: [Int] -> [Int] -> [Int] -> [Int] -> [[(Int,[Int])]] - worker (a:as) (b:bs) (d:ds) lb = [ (a,this):rest - | this <- row b 1 lb - , let lb' = (replicate d 1 ++ map (+1) this) - , rest <- worker as bs ds lb' ] - worker [] _ _ _ = [ [] ] - - -- @row length minimum lowerbound@ - row 0 _ _ = [[]] - row _ _ [] = [] - row !k !m (!a:as) = [ x:xs | x <- [(max a m)..n] , xs <- row (k-1) x as ] - -{- --- | from a sequence @[a1,a2,..,an]@ computes the sequence of differences --- @[a1-a2,a2-a3,...,an-0]@ -diffSequence :: [Int] -> [Int] -diffSequence = go where - go (x:ys@(y:_)) = (x-y) : go ys - go [x] = [x] - go [] = [] --} - --------------------------------------------------------------------------------- - -asciiSkewTableau :: Show a => SkewTableau a -> ASCII -asciiSkewTableau = asciiSkewTableau' "." EnglishNotation - -asciiSkewTableau' - :: Show a - => String -- ^ string representing the elements of the inner (unfilled) partition - -> PartitionConvention -- Orientation - -> SkewTableau a - -> ASCII -asciiSkewTableau' innerstr orient (SkewTableau axs) = tabulate (HRight,VTop) (HSepSpaces 1, VSepEmpty) stuff where - stuff = case orient of - EnglishNotation -> es - EnglishNotationCCW -> reverse (transpose es) - FrenchNotation -> reverse es - inner = asciiFromString innerstr - es = [ replicate a inner ++ map asciiShow xs | (a,xs) <- axs ] - --------------------------------------------------------------------------------- ++-- | Skew tableaux are skew partitions filled with numbers.++{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}++module Math.Combinat.Tableaux.Skew where++--------------------------------------------------------------------------------++import Data.List++import Math.Combinat.Partitions.Integer+import Math.Combinat.Partitions.Skew+import Math.Combinat.Tableaux+import Math.Combinat.ASCII++import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map++--------------------------------------------------------------------------------++-- | A skew tableau is represented by a list of offsets and entries+newtype SkewTableau a = SkewTableau [(Int,[a])] deriving (Eq,Ord,Show)++-- unSkewTableau :: SkewTableau a -> [(Int,[a])]+-- unSkewTableau (SkewTableau a) = a++instance Functor SkewTableau where+ fmap f (SkewTableau t) = SkewTableau [ (a, map f xs) | (a,xs) <- t ]+ +skewShape :: SkewTableau a -> SkewPartition+skewShape (SkewTableau list) = SkewPartition [ (o,length xs) | (o,xs) <- list ]++--------------------------------------------------------------------------------++dualSkewTableau :: forall a. SkewTableau a -> SkewTableau a+dualSkewTableau (SkewTableau axs) = SkewTableau (go axs) where++ go [] = [] + go axs = case sub 0 axs of+ (0,[]) -> []+ this -> this : go (strip axs)++ strip :: [(Int,[a])] -> [(Int,[a])]+ strip [] = []+ strip ((a,xs):rest) = if a>0 + then (a-1,xs) : strip rest+ else case xs of+ [] -> []+ (z:zs) -> case zs of+ [] -> []+ _ -> (0,zs) : strip rest++ sub :: Int -> [(Int,[a])] -> (Int,[a])+ sub !b [] = (b,[])+ sub !b ((a,this):rest) = if a>0 + then sub (b+1) rest + else (b,ys) where + ys = map head $ takeWhile (not . null) (this : map snd rest)++{-+test_dualSkewTableau :: [SkewTableau Int]+test_dualSkewTableau = bad where + ps = allPartitions 11+ bad = [ st + | p<-ps , q<-ps + , (q `isSubPartitionOf` p) + , let sp = mkSkewPartition (p,q) + , let st = fillSkewPartitionWithRowWord sp [1..] + , dualSkewTableau (dualSkewTableau st) /= st+ ]+-}++--------------------------------------------------------------------------------++-- | Semi-standard skew tableaux filled with numbers @[1..n]@+semiStandardSkewTableaux :: Int -> SkewPartition -> [SkewTableau Int]+semiStandardSkewTableaux n (SkewPartition abs) = map SkewTableau stuff where++ stuff = worker as bs ds (repeat 1) + (as,bs) = unzip abs+ ds = diffSequence as+ + -- | @worker inner outerMinusInner innerdiffs lowerbound+ worker :: [Int] -> [Int] -> [Int] -> [Int] -> [[(Int,[Int])]]+ worker (a:as) (b:bs) (d:ds) lb = [ (a,this):rest + | this <- row b 1 lb + , let lb' = (replicate d 1 ++ map (+1) this) + , rest <- worker as bs ds lb' ] + worker [] _ _ _ = [ [] ]++ -- @row length minimum lowerbound@+ row 0 _ _ = [[]]+ row _ _ [] = []+ row !k !m (!a:as) = [ x:xs | x <- [(max a m)..n] , xs <- row (k-1) x as ] ++{-+-- | from a sequence @[a1,a2,..,an]@ computes the sequence of differences+-- @[a1-a2,a2-a3,...,an-0]@+diffSequence :: [Int] -> [Int]+diffSequence = go where+ go (x:ys@(y:_)) = (x-y) : go ys + go [x] = [x]+ go [] = []+-}++--------------------------------------------------------------------------------++asciiSkewTableau :: Show a => SkewTableau a -> ASCII+asciiSkewTableau = asciiSkewTableau' "." EnglishNotation++asciiSkewTableau' + :: Show a+ => String -- ^ string representing the elements of the inner (unfilled) partition+ -> PartitionConvention -- Orientation+ -> SkewTableau a + -> ASCII+asciiSkewTableau' innerstr orient (SkewTableau axs) = tabulate (HRight,VTop) (HSepSpaces 1, VSepEmpty) stuff where+ stuff = case orient of+ EnglishNotation -> es+ EnglishNotationCCW -> reverse (transpose es)+ FrenchNotation -> reverse es+ inner = asciiFromString innerstr+ es = [ replicate a inner ++ map asciiShow xs | (a,xs) <- axs ]++instance Show a => DrawASCII (SkewTableau a) where+ ascii = asciiSkewTableau++--------------------------------------------------------------------------------++-- | The reversed rows, concatenated+skewTableauRowWord :: SkewTableau a -> [a]+skewTableauRowWord (SkewTableau axs) = concatMap (reverse . snd) axs++-- | The reversed rows, concatenated+skewTableauColumnWord :: SkewTableau a -> [a]+skewTableauColumnWord = skewTableauRowWord . dualSkewTableau++-- | Fills a skew partition with content, in row word order +fillSkewPartitionWithRowWord :: SkewPartition -> [a] -> SkewTableau a+fillSkewPartitionWithRowWord (SkewPartition abs) xs = SkewTableau $ go abs xs where+ go ((b,a):rest) xs = let (ys,zs) = splitAt a xs in (b,reverse ys) : go rest zs+ go [] xs = []++-- | Fills a skew partition with content, in column word order +fillSkewPartitionWithColumnWord :: SkewPartition -> [a] -> SkewTableau a+fillSkewPartitionWithColumnWord shape content + = dualSkewTableau + $ fillSkewPartitionWithRowWord (dualSkewPartition shape) content++--------------------------------------------------------------------------------++-- | If the skew tableau's row word is a lattice word, we can make a partition from its content+skewTableauRowContent :: SkewTableau Int -> Maybe Partition+skewTableauRowContent (SkewTableau axs) = go Map.empty rowword where++ rowword = concatMap (reverse . snd) axs++ finish table = Partition (f 1) where+ f !i = case lkp i of+ 0 -> []+ y -> y : f (i+1) + lkp j = case Map.lookup j table of+ Just k -> k+ Nothing -> 0++ go :: Map Int Int -> [Int] -> Maybe Partition+ go !table [] = Just (finish table)+ go !table (i:is) =+ if check i+ then go table' is+ else Nothing+ where+ table' = Map.insertWith (+) i 1 table+ check j = j==1 || cnt (j-1) >= cnt j+ cnt j = case Map.lookup j table' of+ Just k -> k+ Nothing -> 0++--------------------------------------------------------------------------------
Math/Combinat/Trees/Binary.hs view
@@ -7,6 +7,7 @@ -- <<svg/bintrees.svg>> -- +{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} module Math.Combinat.Trees.Binary ( -- * Types BinTree(..)@@ -267,10 +268,10 @@ new = {- debug (reverse ls,l,r,rs) $ -} case l of - RightParen -> Just ( ls , LeftParen:RightParen:rs )- LeftParen -> - {- debug ("---",reverse ls,l,r,rs) $ -}- findj ( lls , [] ) ( reverse (RightParen:rs) , [] ) + RightParen -> Just ( ls , LeftParen:RightParen:rs )+ LeftParen -> + {- debug ("---",reverse ls,l,r,rs) $ -}+ findj ( lls , [] ) ( reverse (RightParen:rs) , [] ) next _ = error "fasc4A_algorithm_P: fatal error shouldn't happen" findj :: ([Paren],[Paren]) -> ([Paren],[Paren]) -> Maybe ([Paren],[Paren])@@ -278,10 +279,10 @@ findj ( lls@(l:ls) , rs) ( xs , ys ) = {- debug ((reverse ls,l,rs),(reverse xs,ys)) $ -} case l of- LeftParen -> case xs of- (a:_:as) -> findj ( ls, RightParen:rs ) ( as , LeftParen:a:ys )- _ -> findj ( lls, [] ) ( reverse rs ++ xs , ys) - RightParen -> Just ( reverse ys ++ xs ++ reverse (LeftParen:rs) ++ ls , [] )+ LeftParen -> case xs of+ (a:_:as) -> findj ( ls, RightParen:rs ) ( as , LeftParen:a:ys )+ _ -> findj ( lls, [] ) ( reverse rs ++ xs , ys) + RightParen -> Just ( reverse ys ++ xs ++ reverse (LeftParen:rs) ++ ls , [] ) findj _ _ = error "fasc4A_algorithm_P: fatal error shouldn't happen" -- | Generates a uniformly random sequence of nested parentheses of length 2n. @@ -409,5 +410,8 @@ blockWidth ls = case ls of (l:_) -> length l [] -> 0++instance DrawASCII (BinTree ()) where+ ascii = asciiBinaryTree_ --------------------------------------------------------------------------------
Math/Combinat/Trees/Nary.hs view
@@ -1,6 +1,7 @@ -- | N-ary trees. +{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} module Math.Combinat.Trees.Nary ( -- * Regular trees @@ -185,6 +186,9 @@ else if bf then "@-" else "+-" in (branch++l) : map (indent++) ls ++ gap++instance DrawASCII (Tree ()) where+ ascii = asciiTreeVertical_ -- | Prints all labels. Example: --
combinat.cabal view
@@ -1,5 +1,5 @@ Name: combinat-Version: 0.2.7.1+Version: 0.2.7.2 Synopsis: Generate and manipulate various combinatorial objects. Description: A collection of functions to generate, count and manipulate all kinds of combinatorial objects like partitions, @@ -83,5 +83,5 @@ if flag(withQuickCheck) cpp-options: -DQUICKCHECK - ghc-options: -Wall -fno-warn-unused-matches+ ghc-options: -Wall -fwarn-tabs -fno-warn-unused-matches -fno-warn-name-shadowing -fno-warn-unused-imports
svg/src/gen_figures.hs view
@@ -1,66 +1,66 @@- --- | A script to generate the SVG figures in the documentation. --- We use the @combinat-diagrams@ library for that. - -module Main where - --------------------------------------------------------------------------------- - -import Math.Combinat.Partitions.Integer -import Math.Combinat.Partitions.Plane -import Math.Combinat.Partitions.NonCrossing -import Math.Combinat.Tableaux -import Math.Combinat.LatticePaths -import Math.Combinat.Trees.Binary - -import Math.Combinat.Diagrams.Partitions.Integer -import Math.Combinat.Diagrams.Partitions.Plane -import Math.Combinat.Diagrams.Partitions.NonCrossing -import Math.Combinat.Diagrams.Tableaux -import Math.Combinat.Diagrams.LatticePaths -import Math.Combinat.Diagrams.Trees.Binary - -import Diagrams.Core -import Diagrams.Prelude -import Diagrams.Backend.SVG - --------------------------------------------------------------------------------- - -export fpath size what = renderSVG fpath size $ pad 1.10 what - -vcatSep = vcat' (with & sep .~ 1) -hcatSep = hcat' (with & sep .~ 1) - -boxSep m xs = pad 1.05 $ vcatSep $ map hcatSep $ yys where - yys = go xs where - go [] = [] - go zs = take m zs : go (drop m zs) - --------------------------------------------------------------------------------- - -main = do - - export "plane_partition.svg" (Width 320) $ drawPlanePartition3D $ - PlanePart [[5,4,3,3,1],[4,4,2,1],[3,2],[2,1],[1],[1]] - - export "noncrossing.svg" (Width 256) $ pad 1.10 $ drawNonCrossingCircleDiagram' orange True $ - NonCrossing [[3],[5,4,2],[7,6,1],[9,8]] - - export "young_tableau.svg" (Width 256) $ drawTableau $ - [ [ 1 , 3 , 4 , 6 , 7 ] - , [ 2 , 5 , 8 ,10 ] - , [ 9 ] - ] - - let u = UpStep - d = DownStep - path = [ u,u,d,u,u,u,d,u,d,d,u,d,u,u,u,d,d,d,d,d,u,d,u,u,d,d ] - export "dyck_path.svg" (Width 500) $ drawLatticePath $ path - -- print (pathHeight path, pathNumberOfZeroTouches path, pathNumberOfPeaks path) - - export "ferrers.svg" (Width 256) $ drawFerrersDiagram' EnglishNotation red True $ - Partition [8,6,3,3,1] - - export "bintrees.svg" (Width 750) $ boxSep 7 $ map drawBinTree_ (binaryTrees 4) - --------------------------------------------------------------------------------- ++-- | A script to generate the SVG figures in the documentation.+-- We use the @combinat-diagrams@ library for that.++module Main where++--------------------------------------------------------------------------------++import Math.Combinat.Partitions.Integer+import Math.Combinat.Partitions.Plane+import Math.Combinat.Partitions.NonCrossing+import Math.Combinat.Tableaux+import Math.Combinat.LatticePaths+import Math.Combinat.Trees.Binary++import Math.Combinat.Diagrams.Partitions.Integer+import Math.Combinat.Diagrams.Partitions.Plane+import Math.Combinat.Diagrams.Partitions.NonCrossing+import Math.Combinat.Diagrams.Tableaux+import Math.Combinat.Diagrams.LatticePaths+import Math.Combinat.Diagrams.Trees.Binary++import Diagrams.Core+import Diagrams.Prelude+import Diagrams.Backend.SVG++--------------------------------------------------------------------------------++export fpath size what = renderSVG fpath size $ pad 1.10 what++vcatSep = vcat' (with & sep .~ 1) +hcatSep = hcat' (with & sep .~ 1) ++boxSep m xs = pad 1.05 $ vcatSep $ map hcatSep $ yys where+ yys = go xs where+ go [] = []+ go zs = take m zs : go (drop m zs) ++--------------------------------------------------------------------------------++main = do ++ export "plane_partition.svg" (Width 320) $ drawPlanePartition3D $+ PlanePart [[5,4,3,3,1],[4,4,2,1],[3,2],[2,1],[1],[1]] ++ export "noncrossing.svg" (Width 256) $ pad 1.10 $ drawNonCrossingCircleDiagram' orange True $+ NonCrossing [[3],[5,4,2],[7,6,1],[9,8]]++ export "young_tableau.svg" (Width 256) $ drawTableau $ + [ [ 1 , 3 , 4 , 6 , 7 ]+ , [ 2 , 5 , 8 ,10 ]+ , [ 9 ]+ ]++ let u = UpStep+ d = DownStep+ path = [ u,u,d,u,u,u,d,u,d,d,u,d,u,u,u,d,d,d,d,d,u,d,u,u,d,d ] + export "dyck_path.svg" (Width 500) $ drawLatticePath $ path+ -- print (pathHeight path, pathNumberOfZeroTouches path, pathNumberOfPeaks path)++ export "ferrers.svg" (Width 256) $ drawFerrersDiagram' EnglishNotation red True $+ Partition [8,6,3,3,1]++ export "bintrees.svg" (Width 750) $ boxSep 7 $ map drawBinTree_ (binaryTrees 4)++--------------------------------------------------------------------------------