packages feed

elynx-tree 0.4.0 → 0.4.1

raw patch · 26 files changed

+336/−151 lines, 26 filesdep +parallelPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: parallel

API changes (from Hackage documentation)

- ELynx.Tree.Partition: bpToMp :: Ord a => Bipartition a -> Partition a
- ELynx.Tree.Partition: mp :: Ord a => [Set a] -> Either String (Partition a)
- ELynx.Tree.Partition: mpHuman :: Show a => Partition a -> String
- ELynx.Tree.Partition: mpUnsafe :: Ord a => [Set a] -> Partition a
- ELynx.Tree.Simulate.PointProcess: type TimeSpec = Maybe (Time, Bool)
+ ELynx.Tree.Import.Newick: parseNewick :: NewickFormat -> ByteString -> Tree Phylo ByteString
+ ELynx.Tree.Import.Newick: parseOneNewick :: NewickFormat -> ByteString -> Tree Phylo ByteString
+ ELynx.Tree.Import.Newick: parseSomeNewick :: NewickFormat -> ByteString -> [Tree Phylo ByteString]
+ ELynx.Tree.Parallel: parBranchFoldMap :: NFData f => Int -> (e -> f) -> (f -> f -> f) -> Tree e a -> f
+ ELynx.Tree.Parallel: parNodeFoldMap :: NFData b => Int -> (a -> b) -> (b -> b -> b) -> Tree e a -> b
+ ELynx.Tree.Parallel: parTree :: (NFData e, NFData a) => Int -> Strategy (Tree e a)
+ ELynx.Tree.Partition: bpToPt :: Ord a => Bipartition a -> Partition a
+ ELynx.Tree.Partition: pt :: Ord a => [Set a] -> Either String (Partition a)
+ ELynx.Tree.Partition: ptHuman :: Show a => Partition a -> String
+ ELynx.Tree.Partition: ptUnsafe :: Ord a => [Set a] -> Partition a
+ ELynx.Tree.Simulate.PointProcess: Mrca :: Time -> TimeSpec
+ ELynx.Tree.Simulate.PointProcess: Origin :: Time -> TimeSpec
+ ELynx.Tree.Simulate.PointProcess: Random :: TimeSpec
+ ELynx.Tree.Simulate.PointProcess: data TimeSpec

Files

ChangeLog.md view
@@ -5,6 +5,20 @@ ## Unreleased changes  +## Version 0.4.1++-   Improve `TimeSpec` (Point process).+-   Parallel evaluation strategies.+-   Change names of some functions involving partitions. For example, `mp` was+    renamed to `pt`.+-   Improve documentation for (bi)partitions.+-   Bugfix `tlynx compare`; do not throw error when branch support values are not+    set.+-   Add `no-elynx-file` option.+-   Also parse Nexus files with `tlynx` commands.+-   Bugfix `subSample`; the sub sample was reversed.++ ## Version 0.4.0  -   Major refactor of `elynx-tree`. All required function can now conveniently
README.md view
@@ -2,7 +2,7 @@  # The ELynx Suite -Version: 0.4.0.+Version: 0.4.1. Reproducible evolution made easy.  <p align="center"><img src="https://travis-ci.org/dschrempf/elynx.svg?branch=master"/></p>@@ -90,12 +90,12 @@      slynx --help | head -n -16 -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: slynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME] -                 [-f|--force] COMMAND+                 [-f|--force] [--no-elynx-file] COMMAND       Analyze, and simulate multi sequence alignments.          Available options:@@ -107,6 +107,7 @@                                Specify base name of output file       -f,--force               Ignore previous analysis and overwrite existing                                output files.+      --no-elynx-file          Do not write files for needed for reproducibility.          Available commands:       concatenate              Concatenate sequences found in input files.@@ -136,9 +137,9 @@      slynx concatenate --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: slynx concatenate (-a|--alphabet NAME) INPUT-FILE       Concatenate sequences found in input files.@@ -157,9 +158,9 @@      slynx examine --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: slynx examine (-a|--alphabet NAME) INPUT-FILE [--per-site]       Examine sequences. If data is a multi sequence alignment, additionally analyze columns.@@ -179,9 +180,9 @@      slynx filter-rows --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: slynx filter-rows (-a|--alphabet NAME) INPUT-FILE [--longer-than LENGTH]                               [--shorter-than LENGTH] [--standard-characters]@@ -202,9 +203,9 @@      slynx filter-columns --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: slynx filter-columns (-a|--alphabet NAME) INPUT-FILE                                  [--standard-chars DOUBLE]@@ -226,9 +227,9 @@      slynx simulate --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: slynx simulate (-t|--tree-file Name) [-s|--substitution-model MODEL]                            [-m|--mixture-model MODEL] [-e|--edm-file NAME] @@ -305,9 +306,9 @@      slynx sub-sample --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: slynx sub-sample (-a|--alphabet NAME) INPUT-FILE                             (-n|--number-of-sites INT)@@ -335,9 +336,9 @@      slynx translate --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: slynx translate (-a|--alphabet NAME) INPUT-FILE (-r|--reading-frame INT)                            (-u|--universal-code CODE)@@ -360,12 +361,12 @@      tlynx --help | head -n -16 -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: tlynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME] -                 [-f|--force] COMMAND+                 [-f|--force] [--no-elynx-file] COMMAND       Compare, examine, and simulate phylogenetic trees.          Available options:@@ -377,6 +378,7 @@                                Specify base name of output file       -f,--force               Ignore previous analysis and overwrite existing                                output files.+      --no-elynx-file          Do not write files for needed for reproducibility.          Available commands:       compare                  Compare two phylogenetic trees (compute distances and branch-wise differences).@@ -390,6 +392,7 @@     Available tree file formats:       - Newick Standard: Branch support values are stored in square brackets after branch lengths.       - Newick IqTree:   Branch support values are stored as node names after the closing bracket of forests.+      - Newick RevBayes: Key-value pairs is provided in square brackets after node names as well as branch lengths. XXX: Key value pairs are ignored at the moment.   ## Compare@@ -398,9 +401,9 @@      tlynx compare --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: tlynx compare [-n|--normalize] [-b|--bipartitions] [-t|--intersect]                           [-f|--newick-format FORMAT] NAMES@@ -427,9 +430,9 @@      tlynx examine --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: tlynx examine INPUT-FILE [-f|--newick-format FORMAT]       Compute summary statistics of phylogenetic trees.@@ -451,9 +454,9 @@      tlynx simulate --help -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: tlynx simulate (-t|--nTrees INT) (-n|--nLeaves INT) PROCESS                            [-u|--sub-sample DOUBLE] [-s|--summary-statistics] @@ -488,9 +491,9 @@      elynx --help | head -n -16 -    ELynx Suite version 0.4.0.+    ELynx Suite version 0.4.1.     Developed by Dominik Schrempf.-    Compiled on September 4, 2020, at 13:37 pm, UTC.+    Compiled on October 16, 2020, at 12:46 pm, UTC.          Usage: elynx COMMAND       Validate and redo past ELynx analyses
bench/Bench.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE BangPatterns #-}+ -- | -- Module      :  Bench -- Description :  Various benchmarks@@ -11,11 +13,15 @@ -- Creation date: Mon Dec 16 13:33:27 2019. module Main where +import Control.Parallel.Strategies import Criterion.Main+import Data.Bifunctor import qualified Data.ByteString.Char8 as BS-import ELynx.Tools+import Data.Foldable+import ELynx.Tools hiding (Random) import ELynx.Tree-import ELynx.Tree.Import.Newick+import ELynx.Tree.Simulate.PointProcess+import System.Random.MWC  treeFileMany :: FilePath treeFileMany = "data/Many.trees"@@ -23,8 +29,48 @@ getManyTrees :: IO (Forest Phylo BS.ByteString) getManyTrees = parseFileWith (someNewick Standard) treeFileMany +hugeTree :: IO (Tree Length Int)+hugeTree = create >>= simulateReconstructedTree 50000 Random 1.0 0.9++sinN :: Int -> Double -> Double+sinN n x = iterate sin x !! n++func :: Double -> Double+func = sinN 200++hugeTreeCalcPar :: Int -> Tree Double Int -> Tree Double Int+hugeTreeCalcPar n t = first func t `using` parTree n+ main :: IO () main = do-  ts <- getManyTrees+  !ts <- getManyTrees+  !ht <- first getLen <$> hugeTree+  let mr1 = hugeTreeCalcPar 0 ht+      mr2 = hugeTreeCalcPar 1 ht+  if mr1 == mr2+    then putStrLn "Map OK."+    else do+      print mr1+      print mr2+      error "Map wrong."+  let fr1 = (foldl' (+) 0 . branches) ht+      fr2 = parBranchFoldMap 1 id (+) ht+  if 1e-8 > abs (fr1 - fr2)+    then putStrLn "Fold OK."+    else do+      print fr1+      print fr2+      error "Fold wrong."   defaultMain-    [bgroup "bipartition" [bench "manyTrees" $ nf (map bipartitions) ts]]+    [ bgroup "bipartition" [bench "manyTrees" $ nf (map bipartitions) ts],+      bgroup+        "map strategies"+        [ bench "sequential" $ nf (hugeTreeCalcPar 0) ht,+          bench "parallel 3" $ nf (hugeTreeCalcPar 3) ht+        ],+      bgroup+        "fold strategies"+        [ bench "sequential" $ nf (parBranchFoldMap 0 func (+)) ht,+          bench "parallel 3" $ nf (parBranchFoldMap 3 func (+)) ht+        ]+    ]
− data/UltraMetric.log
@@ -1,22 +0,0 @@------------------------------------------------------------------- Simulate phylogenetic trees using birth and death processes.-ELynx Suite version 0.0.1.-Developed by Dominik Schrempf.-Compiled on January 13, 2020, at 16:42 pm, UTC.-Start time: January 14, 2020, at 09:35 am, UTC.-Command line: tlynx -o UltraMetric simulate -t 1 -n 10 -H 1.0--- Arguments-Number of simulated trees: 1-Number of leaves per tree: 10-Height of trees: 1.0, conditioned on origin-Birth rate: 1.0-Death rate: 0.9-Sampling probability: 1.0-Perform sub-sampling: False-Summary statistics only: False-Seed: Random--- Simulation-Number of used cores: 4-Write simulated trees to file 'UltraMetric.tree'.--- End time: January 14, 2020, at 09:35 am, UTC.-------------------------------------------------
elynx-tree.cabal view
@@ -1,6 +1,6 @@ cabal-version:  2.2 name:           elynx-tree-version:        0.4.0+version:        0.4.1 synopsis:       Handle phylogenetic trees description:    Examine, compare, and simulate phylogenetic trees in a reproducible way. Please see the README on GitHub at <https://github.com/dschrempf/elynx>. category:       Bioinformatics@@ -31,7 +31,6 @@     data/RevBayes.trees     data/SimpleTree.nex     data/TreeDist.trees-    data/UltraMetric.log     data/UltraMetric.tree  source-repository head@@ -47,6 +46,7 @@       ELynx.Tree.Distance       ELynx.Tree.Measurable       ELynx.Tree.Named+      ELynx.Tree.Parallel       ELynx.Tree.Partition       ELynx.Tree.Phylogeny       ELynx.Tree.Rooted@@ -86,6 +86,7 @@     , elynx-nexus     , math-functions     , mwc-random+    , parallel     , primitive     , statistics   default-language: Haskell2010@@ -128,11 +129,15 @@       Paths_elynx_tree   hs-source-dirs:       bench-  ghc-options: -Wall+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N+  -- ghc-options: -Wall   build-depends:       base >=4.7 && <5     , bytestring     , criterion+    , deepseq     , elynx-tools     , elynx-tree+    , mwc-random+    , parallel   default-language: Haskell2010
src/ELynx/Tree.hs view
@@ -15,8 +15,13 @@ module ELynx.Tree   ( -- * Rooted trees     module ELynx.Tree.Rooted,++    -- * Tree zipper     module ELynx.Tree.Zipper, +    -- * Parallel evaluation+    module ELynx.Tree.Parallel,+     -- * Branch label classes     module ELynx.Tree.Measurable,     module ELynx.Tree.Splittable,@@ -49,6 +54,7 @@ import ELynx.Tree.Import.Nexus import ELynx.Tree.Measurable import ELynx.Tree.Named+import ELynx.Tree.Parallel import ELynx.Tree.Partition import ELynx.Tree.Phylogeny import ELynx.Tree.Rooted
src/ELynx/Tree/Bipartition.hs view
@@ -56,9 +56,13 @@ -- I am proud of this awesome 'Comonad' usage here :). groups = extend leaves --- | Each branch of a tree partitions the leaves of the tree into two subsets,--- or a bipartition.+-- | A bipartition of a tree is a grouping of the leaves of the tree into two+-- non-overlapping, non-empty sub sets. --+-- For example, each branch of a tree partitions the leaves of the tree into two+-- subsets, or a bipartition. Also, a bifurcating root induces a bipartition;+-- see 'bipartition'.+-- -- The order of the two subsets of a 'Bipartition' is meaningless. We ensure by -- construction that the smaller subset comes first, and hence, that equality -- checks are meaningful.@@ -152,8 +156,6 @@       [bipartitions' c s | (c, s) <- zip cs ts]   where     cs = getComplementaryLeaves p t---- TODO: Unrooted? See module comment of Distance.hs.  -- | Convert a tree into a 'Map' from each 'Bipartition' to the branch inducing -- the respective 'Bipartition'.
src/ELynx/Tree/Distance.hs view
@@ -10,20 +10,18 @@ -- -- Creation date: Thu Jun 13 17:15:54 2019. ----- Various distance functions for phylogenetic trees (and trees with branch--- lengths in general).+-- Various distance functions for trees. -- -- The functions provided in this module return distances for __unrooted__ -- trees. See comments of 'symmetric', 'branchScore', and 'bipartitionToBranch', -- as well as the documentation of -- [treedist](http://evolution.genetics.washington.edu/phylip/doc/treedist.html). ----- It is a little unfortunate that 'Tree' data type, which represents rooted--- trees, is also used in this module. However, rooted trees are much easier to--- handle. In the future, a separate data type for unrooted trees may be--- introduced. In theory, this is quite straight forward, for example, using--- algebraic graphs. Difficulties may arise because the branches of an unrooted--- tree are undirected.+-- It is a little unfortunate that the 'Tree' data type represents rooted trees.+-- However, rooted trees are much easier to handle computationally. In the+-- future, a separate data type for unrooted trees may be introduced, for+-- example, using algebraic graphs. Difficulties may arise because the branches+-- of an unrooted tree are undirected. module ELynx.Tree.Distance   ( symmetric,     incompatibleSplits,@@ -66,7 +64,7 @@  countIncompatibilities :: (Show a, Ord a) => Set (Bipartition a) -> Set (Partition a) -> Int countIncompatibilities bs ms =-  foldl' (\i b -> if any (compatible $ bpToMp b) ms then i else i + 1) 0 bs+  foldl' (\i b -> if any (compatible $ bpToPt b) ms then i else i + 1) 0 bs  -- | Number of incompatible splits. --
src/ELynx/Tree/Export/Newick.hs view
@@ -27,6 +27,12 @@ import ELynx.Tree.Phylogeny import ELynx.Tree.Rooted +buildBrSup :: Double -> BB.Builder+buildBrSup bs = BB.char8 '[' <> BB.doubleDec bs <> BB.char8 ']'++buildBrLen :: Double -> BB.Builder+buildBrLen bl = BB.char8 ':' <> BB.doubleDec bl+ -- | See 'toNewick'. toNewickBuilder :: Named a => Tree Phylo a -> BB.Builder toNewickBuilder t = go t <> BB.char8 ';'@@ -37,8 +43,8 @@         <> mconcat (intersperse (BB.char8 ',') $ map go ts)         <> BB.char8 ')'         <> lbl b l-    mBrSupBuilder x = maybe mempty (\bs -> BB.char8 '[' <> BB.doubleDec bs <> BB.char8 ']') (brSup x)-    mBrLenBuilder x = maybe mempty (\bl -> BB.char8 ':' <> BB.doubleDec bl) (brLen x)+    mBrSupBuilder x = maybe mempty buildBrSup (brSup x)+    mBrLenBuilder x = maybe mempty buildBrLen (brLen x)     lbl x y =       BB.lazyByteString (getName y)         <> mBrLenBuilder x
src/ELynx/Tree/Import/Newick.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveGeneric #-} +-- | -- Module      :  ELynx.Tree.Import.Newick -- Description :  Import Newick trees -- Copyright   :  (c) Dominik Schrempf 2020@@ -19,15 +20,24 @@ -- In particular, no conversion from _ to (space) is done right now. -- -- For a description of rooted 'Tree's, please see the 'ELynx.Tree.Rooted'---- |--- module header.+--+-- Code snippet:+--+-- @+-- import Data.Attoparsec.ByteString+-- import ELynx.Tree+--+-- getOneNewick = either error id . parseOnly (oneNewick Standard)+-- @ module ELynx.Tree.Import.Newick   ( NewickFormat (..),     description,     newick,+    parseNewick,     oneNewick,+    parseOneNewick,     someNewick,+    parseSomeNewick,   ) where @@ -64,24 +74,36 @@ description RevBayes =   "RevBayes: Key-value pairs is provided in square brackets after node names as well as branch lengths. XXX: Key value pairs are ignored at the moment." --- | Parse a single Newick tree. Also succeeds when more trees follow.+-- | Newick tree parser. Also succeeds when more trees follow. newick :: NewickFormat -> Parser (Tree Phylo BS.ByteString) newick Standard = newickStandard newick IqTree = newickIqTree newick RevBayes = newickRevBayes --- | Parse a single Newick tree. Fails when end of file is not reached.+-- | See 'newick'.+parseNewick :: NewickFormat -> BS.ByteString -> Tree Phylo BS.ByteString+parseNewick f = either error id . parseOnly (newick f)++-- | One Newick tree parser. Fails when end of input is not reached. oneNewick :: NewickFormat -> Parser (Tree Phylo BS.ByteString) oneNewick Standard = oneNewickStandard oneNewick IqTree = oneNewickIqTree oneNewick RevBayes = oneNewickRevBayes --- | Parse one or more Newick trees until end of file.+-- | See 'oneNewick'.+parseOneNewick :: NewickFormat -> BS.ByteString -> Tree Phylo BS.ByteString+parseOneNewick f = either error id . parseOnly (oneNewick f)++-- | One or more Newick trees parser. someNewick :: NewickFormat -> Parser (Forest Phylo BS.ByteString) someNewick Standard = someNewickStandard someNewick IqTree = someNewickIqTree someNewick RevBayes = someNewickRevBayes +-- | See 'someNewick'.+parseSomeNewick :: NewickFormat -> BS.ByteString -> [Tree Phylo BS.ByteString]+parseSomeNewick f = either error id . parseOnly (someNewick f)+ -- Parse a single Newick tree. Also succeeds when more trees follow. newickStandard :: Parser (Tree Phylo BS.ByteString) newickStandard = skipWhile isSpace *> tree <* char ';' <* skipWhile isSpace <?> "newickStandard"@@ -183,10 +205,14 @@ -- about nodes and branches. Parse a single Newick tree. Also succeeds when more -- trees follow. ----- XXX: Key value pairs are ignored at the moment.+-- TODO: Key value pairs are ignored at the moment. newickRevBayes :: Parser (Tree Phylo BS.ByteString) newickRevBayes =-  skipWhile isSpace *> optional brackets *> treeRevBayes <* char ';' <* skipWhile isSpace <?> "newickRevBayes"+  skipWhile isSpace+    *> optional brackets+    *> treeRevBayes+    <* char ';'+    <* skipWhile isSpace <?> "newickRevBayes"  -- See 'newickRevBayes'. Parse a single Newick tree. Fails when end of file is -- not reached.
src/ELynx/Tree/Import/Nexus.hs view
@@ -27,7 +27,7 @@  -- | Parse a Nexus files with a TREES block. nexusTrees :: NewickFormat -> Parser [(BS.ByteString, Tree Phylo BS.ByteString)]-nexusTrees = nexus . trees+nexusTrees = nexusBlock . trees  trees :: NewickFormat -> Block [(BS.ByteString, Tree Phylo BS.ByteString)] trees f = Block "TREES" (some $ namedNewick f)@@ -35,11 +35,11 @@ namedNewick :: NewickFormat -> Parser (BS.ByteString, Tree Phylo BS.ByteString) namedNewick f = do   _ <- skipWhile isSpace-  _ <- string "TREE"+  _ <- stringCI "TREE" <?> "namedNewickTreeStart"   _ <- skipWhile isSpace-  n <- takeWhile1 (\x -> isAlpha_ascii x || isDigit x)+  n <- takeWhile1 (\x -> isAlpha_ascii x || isDigit x) <?> "namedNewickTreeName"   _ <- skipWhile isSpace-  _ <- char '='+  _ <- char '=' <?> "namedNewickEqual"   _ <- skipWhile isSpace-  t <- newick f+  t <- newick f <?> "namedNewickTree"   return (n, t)
+ src/ELynx/Tree/Parallel.hs view
@@ -0,0 +1,62 @@+-- |+-- Module      :  ELynx.Tree.Parallel+-- Description :  Evaluation strategies for trees+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Mon Sep  7 13:36:45 2020.+module ELynx.Tree.Parallel+  ( parTree,+    parBranchFoldMap,+    parNodeFoldMap,+  )+where++import Control.Parallel.Strategies+import Data.Foldable+import ELynx.Tree.Rooted++myParList :: Strategy a -> Strategy [a]+myParList _ [] = return []+myParList s xs = do+  ys <- parList s $ tail xs+  y <- s $ head xs+  return $ y : ys++-- | Parallel evaluation strategy for a tree into normal form.+--+-- Evaluate the sub trees up to given layer in parallel.+parTree :: (NFData e, NFData a) => Int -> Strategy (Tree e a)+parTree n t@(Node br lb ts)+  | n == 0 = rdeepseq t+  | n == 1 = do+    ts' <- myParList rdeepseq ts+    return $ Node br lb ts'+  | n >= 2 = do+    ts' <- myParList (parTree (n -1)) ts+    return $ Node br lb ts'+  | otherwise = error "parTree: n is negative."++branchFoldMap :: (e -> f) -> (f -> f -> f) -> Tree e a -> f+branchFoldMap f op (Node br _ ts) = foldl' op (f br) $ map (branchFoldMap f op) ts++-- | Map and fold over branches. Evaluate the sub trees up to given layer in parallel.+parBranchFoldMap :: NFData f => Int -> (e -> f) -> (f -> f -> f) -> Tree e a -> f+parBranchFoldMap 0 f op t = branchFoldMap f op t+parBranchFoldMap n f op (Node br _ ts)+  | n >= 1 = foldl' op (f br) (map (parBranchFoldMap (n - 1) f op) ts `using` myParList rdeepseq)+  | otherwise = error "parBranchFoldMap: n is negative."++nodeFoldMap :: (a -> b) -> (b -> b -> b) -> Tree e a -> b+nodeFoldMap f op (Node _ lb ts) = foldl' op (f lb) $ map (nodeFoldMap f op) ts++-- | Map and fold over nodes. Evaluate the sub trees up to given layer in parallel.+parNodeFoldMap :: NFData b => Int -> (a -> b) -> (b -> b -> b) -> Tree e a -> b+parNodeFoldMap 0 f op t = nodeFoldMap f op t+parNodeFoldMap n f op (Node _ lb ts)+  | n >= 1 = foldl' op (f lb) (map (parNodeFoldMap (n - 1) f op) ts `using` myParList rdeepseq)+  | otherwise = error "parNodeFoldMap: n is negative."
src/ELynx/Tree/Partition.hs view
@@ -15,10 +15,10 @@ module ELynx.Tree.Partition   ( -- * Data type     Partition (fromPartition),-    mp,-    mpUnsafe,-    bpToMp,-    mpHuman,+    pt,+    ptUnsafe,+    bpToPt,+    ptHuman,      -- * Work with 'Partition's     partition,@@ -33,15 +33,17 @@ import ELynx.Tree.Bipartition import ELynx.Tree.Rooted --- | Each branch of a tree partitions the leaves of the tree into two subsets--- (see 'ELynx.Tree.Bipartition'). In a similar way, each internal node--- (excluding the root node) partitions the leaves into three (or more) subsets--- which is called 'Partition'. If the tree is multifurcating, and a--- specific node has more than two children, the number of subsets induced by--- this node is larger than three. Partitions are interesting in that we--- can use them for calculating incompatible splits, see--- 'ELynx.Tree.Distance'.+-- | A partition of a tree is a grouping of the leaves of the tree into+-- non-overlapping, non-empty sub sets. --+-- For example, each branch of a tree partitions the leaves of the tree into two+-- subsets (see 'ELynx.Tree.Bipartition'). In a similar way, each bifurcating+-- internal node (excluding the root node) partitions the leaves into three+-- subsets called a 'Partition'. If the tree is multifurcating, and a specific+-- node has more than two children, the number of subsets induced by this node+-- is larger than three. Partitions are interesting in that we can use them for+-- calculating incompatible splits, see 'ELynx.Tree.Distance'.+-- -- The order of the subsets of a 'Partition' is meaningless. We ensure by -- construction that the subsets are ordered, and hence, that equality checks -- are meaningful.@@ -52,21 +54,19 @@  -- TODO: Check that list is not empty after filtering. --- TODO: Rename these functions; don't use 'multi'.- -- | Create a partition.-mp :: Ord a => [Set a] -> Either String (Partition a)-mp xs = case filter (not . S.null) xs of+pt :: Ord a => [Set a] -> Either String (Partition a)+pt xs = case filter (not . S.null) xs of   [] -> Left "mp: Empty list."-  xs' -> Right $ mpUnsafe xs'+  xs' -> Right $ ptUnsafe xs'  -- | Create a partition.-mpUnsafe :: Ord a => [Set a] -> Partition a-mpUnsafe xs = Partition (S.fromList xs)+ptUnsafe :: Ord a => [Set a] -> Partition a+ptUnsafe xs = Partition (S.fromList xs)  -- | Convert a bipartition to a partition.-bpToMp :: Ord a => Bipartition a -> Partition a-bpToMp = mpUnsafe . tupleToList . fromBipartition+bpToPt :: Ord a => Bipartition a -> Partition a+bpToPt = ptUnsafe . tupleToList . fromBipartition   where     -- Be careful with tuples, because 'toList' does something very weird. It only     -- takes the second element of the tuple!@@ -76,8 +76,8 @@  -- | Show a partition in a human readable form. Use a provided function to -- extract the valuable information.-mpHuman :: Show a => Partition a -> String-mpHuman (Partition xs) =+ptHuman :: Show a => Partition a -> String+ptHuman (Partition xs) =   "(" ++ intercalate "|" (map setShow (S.toList xs)) ++ ")"  -- Show the elements of a set in a human readable format.@@ -93,7 +93,7 @@ partition (Node _ _ []) = Left "partition: Encountered a leaf." partition t@(Node _ _ ts)   | duplicateLeaves t = Left "partition: Tree contains duplicate leaves."-  | otherwise = mp $ map (S.fromList . leaves) ts+  | otherwise = pt $ map (S.fromList . leaves) ts  -- | Get all 'Partition's of a tree. --@@ -108,7 +108,7 @@ partitions' _ (Node _ _ []) = S.empty partitions' p t@(Node _ _ ts) =   S.unions $-    either (const S.empty) S.singleton (mp (p : map label ts)) :+    either (const S.empty) S.singleton (pt (p : map label ts)) :     zipWith partitions' cs ts   where     cs = getComplementaryLeaves p t
src/ELynx/Tree/Rooted.hs view
@@ -97,7 +97,8 @@ -- | Rooted rose trees with branch labels. -- -- Unary instances such as 'Functor' act on node labels, and not on branch--- labels. Binary instances such as 'Bifunctor' act on both labels.+-- labels. Binary instances such as 'Bifunctor' act on both labels (`first` acts+-- on branches, `second` on node labels). -- -- Lifted instances are not provided. data Tree e a = Node@@ -115,6 +116,7 @@   fmap f ~(Node br lb ts) = Node br (f lb) $ map (fmap f) ts   x <$ ~(Node br _ ts) = Node br x (map (x <$) ts) +-- | The function 'first' acts on branch labels, 'second' on node labels. instance Bifunctor Tree where   bimap f g ~(Node br lb ts) = Node (f br) (g lb) $ map (bimap f g) ts   first f ~(Node br lb ts) = Node (f br) lb $ map (first f) ts
src/ELynx/Tree/Simulate/Coalescent.hs view
@@ -15,10 +15,10 @@ where  import Control.Monad.Primitive+import ELynx.Tree.Distribution.CoalescentContinuous import ELynx.Tree.Measurable import ELynx.Tree.Phylogeny import ELynx.Tree.Rooted-import ELynx.Tree.Distribution.CoalescentContinuous import Statistics.Distribution import System.Random.MWC 
src/ELynx/Tree/Simulate/PointProcess.hs view
@@ -19,7 +19,7 @@ -- and death process. module ELynx.Tree.Simulate.PointProcess   ( PointProcess (..),-    TimeSpec,+    TimeSpec (..),     simulate,     toReconstructedTree,     simulateReconstructedTree,@@ -89,33 +89,52 @@   }   deriving (Read, Show, Eq) --- | If nothing, sample time of origin from respective distribution. If time is--- given, we need to know if we condition on the time of origin, or the time of--- the most recent common ancestor (MRCA).-type TimeSpec = Maybe (Time, Bool)+-- | Tree height specification.+data TimeSpec+  = -- | Sample time of origin from respective distribution.+    Random+  | -- | Condition on time of origin.+    Origin Time+  | -- | Condition on time of most recent common ancestor (MRCA).+    Mrca Time  -- | Sample a point process using the 'BirthDeathDistribution'. The names of the -- points will be integers. simulate ::   (PrimMonad m) =>-  -- | Number of points (samples)+  -- | Number of points (samples).   Int ->-  -- | Time of origin or MRCA+  -- | Time of origin or MRCA.   TimeSpec ->-  -- | Birth rate+  -- | Birth rate.   Rate ->-  -- | Death rate+  -- | Death rate.   Rate ->-  -- | Generator (see 'System.Random.MWC')+  -- | Generator.   Gen (PrimState m) ->   m (PointProcess Int Double)+simulate n ts l m g+  | n < 1 = error "Number of samples needs to be one or larger."+  | l < 0.0 = error "Birth rate needs to be positive."+  | otherwise = case ts of+    Random -> simulateRandom n l m g+    Origin t -> simulateOrigin n t l m g+    Mrca t -> simulateMrca n t l m g+ -- No time of origin given. We also don't need to take care of the conditioning -- (origin or MRCA).-simulate n Nothing l m g+simulateRandom ::+  PrimMonad m =>+  Int ->+  Double ->+  Double ->+  Gen (PrimState m) ->+  m (PointProcess Int Double)+simulateRandom n l m g   | -- XXX. There is no formula for the over-critical process.     m > l =     error-      "Time of origin distribution formula not available when mu > lambda. Please specify height for the moment."+      "simulateRandom: Please specify height if mu > lambda."   | -- For the critical process, we have no idea about the time of origin, but can     -- use a specially derived distribution.     m =~= l =@@ -128,17 +147,24 @@     abs (m - l) <= epsNearCriticalTimeOfOrigin =     do       t <- D.genContVar (TONCD n l m) g-      simulate n (Just (t, False)) l m g+      simulateOrigin n t l m g   | -- For a sub-critical branching process, we can use the formula from Tanja Stadler.     otherwise =     do       t <- D.genContVar (TOD n l m) g-      simulate n (Just (t, False)) l m g+      simulateOrigin n t l m g+ -- Time of origin is given.-simulate n (Just (t, c)) l m g-  | n < 1 = error "Number of samples needs to be one or larger."-  | t < 0.0 = error "Time of origin needs to be positive."-  | l < 0.0 = error "Birth rate needs to be positive."+simulateOrigin ::+  PrimMonad m =>+  Int ->+  Time ->+  Double ->+  Double ->+  Gen (PrimState m) ->+  m (PointProcess Int Double)+simulateOrigin n t l m g+  | t < 0.0 = error "simulateOrigin: Time of origin needs to be positive."   | -- See Stadler, T., & Steel, M. (2019). Swapping birth and death: symmetries     -- and transformations in phylodynamic models. , (), .     -- http://dx.doi.org/10.1101/494583. Should be possible now.@@ -147,28 +173,39 @@     -- 1. The critical branching process.     -- 2. The near critical branching process.     -- 3. Normal values :).-    (m =~= l) && not c = do+    m =~= l = do     !vs <- replicateM (n - 1) (D.genContVar (BDCD t l) g)     return $ PointProcess [0 .. (n - 1)] vs t-  | (abs (m - l) <= epsNearCriticalPointProcess) && not c = do+  | abs (m - l) <= epsNearCriticalPointProcess = do     !vs <- replicateM (n - 1) (D.genContVar (BDNCD t l m) g)     return $ PointProcess [0 .. (n - 1)] vs t-  | not c = do+  | otherwise = do     !vs <- replicateM (n - 1) (D.genContVar (BDD t l m) g)     return $ PointProcess [0 .. (n - 1)] vs t-  | (m =~= l) && c = do++-- Time of Mrca is given.+simulateMrca ::+  PrimMonad m =>+  Int ->+  Time ->+  Double ->+  Double ->+  Gen (PrimState m) ->+  m (PointProcess Int Double)+simulateMrca n t l m g+  | t < 0.0 = error "simulateMrca: Time of MRCA needs to be positive."+  | m =~= l = do     !vs <- replicateM (n - 2) (D.genContVar (BDCD t l) g)     vs' <- randomInsertList t vs g     return $ PointProcess [0 .. (n - 1)] vs' t-  | (abs (m - l) <= epsNearCriticalPointProcess) && c = do+  | abs (m - l) <= epsNearCriticalPointProcess = do     !vs <- replicateM (n - 2) (D.genContVar (BDNCD t l m) g)     vs' <- randomInsertList t vs g     return $ PointProcess [0 .. (n - 1)] vs' t-  | c = do+  | otherwise = do     !vs <- replicateM (n - 2) (D.genContVar (BDD t l m) g)     vs' <- randomInsertList t vs g     return $ PointProcess [0 .. (n - 1)] vs' t-  | otherwise = error "simulate: Fell through guard, this should never happen."  -- Sort the values of a point process and their indices to be (the indices -- that they will have while creating the tree).
src/ELynx/Tree/Supported.hs view
@@ -55,7 +55,7 @@  -- A leaf has full support. highP :: Supported e => Double -> Tree e a -> Bool-highP _  (Node _ _ []) = True+highP _ (Node _ _ []) = True highP th (Node br _ _) = getSup br >= th  -- See 'collapse'.
test/ELynx/Tree/BipartitionSpec.hs view
@@ -20,8 +20,8 @@ import qualified Data.Map as M import Data.Set (Set) import qualified Data.Set as S-import ELynx.Tree import ELynx.Tools+import ELynx.Tree import Test.Hspec  sfrom :: [BS.ByteString] -> S.Set BS.ByteString
test/ELynx/Tree/DistanceSpec.hs view
@@ -17,9 +17,9 @@ where  import qualified Data.ByteString.Char8 as BS+import ELynx.Tools import ELynx.Tree import ELynx.Tree.Arbitrary ()-import ELynx.Tools import Test.Hspec import Test.QuickCheck 
test/ELynx/Tree/Export/NewickSpec.hs view
@@ -18,9 +18,9 @@ import Data.Bifunctor import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL+import ELynx.Tools import ELynx.Tree import ELynx.Tree.Arbitrary ()-import ELynx.Tools import Test.Hspec import Test.QuickCheck hiding (label) 
test/ELynx/Tree/Export/NexusSpec.hs view
@@ -17,8 +17,8 @@ where  import qualified Data.ByteString.Char8 as BS-import ELynx.Tree import ELynx.Tools+import ELynx.Tree import Test.Hspec  tree :: Tree Phylo BS.ByteString
test/ELynx/Tree/Import/NewickSpec.hs view
@@ -18,8 +18,8 @@ import Data.Attoparsec.ByteString.Char8 import qualified Data.ByteString.Char8 as BS import Data.Either-import ELynx.Tree import ELynx.Tools+import ELynx.Tree import Test.Hspec  -- sampleLabelByteString :: ByteString
test/ELynx/Tree/Import/NexusSpec.hs view
@@ -17,8 +17,8 @@ where  import qualified Data.ByteString.Char8 as BS-import ELynx.Tree import ELynx.Tools+import ELynx.Tree import Test.Hspec  file :: FilePath
test/ELynx/Tree/PartitionSpec.hs view
@@ -26,8 +26,8 @@ sol1 :: Set (Partition Int) sol1 =   fromList-    [ mpUnsafe [fromList [1], fromList [3], fromList [4, 5, 6]],-      mpUnsafe [fromList [1, 3], fromList [4], fromList [5], fromList [6]]+    [ ptUnsafe [fromList [1], fromList [3], fromList [4, 5, 6]],+      ptUnsafe [fromList [1, 3], fromList [4], fromList [5], fromList [6]]     ]  ex2 :: Tree () Int@@ -36,8 +36,8 @@ sol2 :: Set (Partition Int) sol2 =   fromList-    [ mpUnsafe [fromList [1], fromList [2], fromList [3, 4], fromList [5]],-      mpUnsafe [fromList [1, 2, 5], fromList [3], fromList [4]]+    [ ptUnsafe [fromList [1], fromList [2], fromList [3, 4], fromList [5]],+      ptUnsafe [fromList [1, 2, 5], fromList [3], fromList [4]]     ]  spec :: Spec
test/ELynx/Tree/RootedSpec.hs view
@@ -19,8 +19,8 @@ import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL import Data.Maybe-import ELynx.Tree import ELynx.Tools+import ELynx.Tree import Test.Hspec  node :: Int -> Tree () Int
test/ELynx/Tree/SupportedSpec.hs view
@@ -17,8 +17,8 @@ where  import qualified Data.ByteString.Char8 as BS-import ELynx.Tree import ELynx.Tools+import ELynx.Tree import Test.Hspec  collapseTree :: Tree Phylo BS.ByteString