packages feed

elynx-tree 0.5.0 → 0.5.0.1

raw patch · 28 files changed

+858/−810 lines, 28 filesdep ~QuickCheckdep ~aesondep ~attoparsecPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: QuickCheck, aeson, attoparsec, base, bytestring, comonad, containers, criterion, deepseq, double-conversion, elynx-nexus, elynx-tools, hspec, math-functions, microlens, mwc-random, parallel, primitive, statistics

API changes (from Hackage documentation)

- ELynx.Topology.Phylogeny: outgroup :: Ord a => Set a -> Topology a -> Either String (Topology a)
- ELynx.Topology.Phylogeny: rootAt :: Ord a => Bipartition a -> Topology a -> Either String (Forest a)
- ELynx.Topology.Phylogeny: roots :: Topology a -> Either String (Forest a)
- ELynx.Topology.Rooted: flatten :: Topology a -> [a]
- ELynx.Tree.Import.Newick: description :: NewickFormat -> String
- ELynx.Tree.Measurable: checkLength :: Length -> Either String Length
- ELynx.Tree.Measurable: class Measurable e
- ELynx.Tree.Measurable: data Length
- ELynx.Tree.Measurable: distancesOriginLeaves :: Measurable e => Tree e a -> [Length]
- ELynx.Tree.Measurable: getLen :: Measurable e => e -> Length
- ELynx.Tree.Measurable: height :: Measurable e => Tree e a -> Length
- ELynx.Tree.Measurable: instance Control.DeepSeq.NFData ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance ELynx.Tree.Measurable.Measurable ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance ELynx.Tree.Splittable.Splittable ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Base.Monoid ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Base.Semigroup ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Classes.Eq ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Classes.Ord ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Enum.Enum ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Float.Floating ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Float.RealFloat ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Generics.Generic ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Num.Num ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Read.Read ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Real.Fractional ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Real.Real ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Real.RealFrac ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: instance GHC.Show.Show ELynx.Tree.Measurable.Length
- ELynx.Tree.Measurable: makeUltrametric :: Measurable e => Tree e a -> Tree e a
- ELynx.Tree.Measurable: modLen :: Measurable e => (Length -> Length) -> e -> e
- ELynx.Tree.Measurable: normalizeBranchLengths :: Measurable e => Tree e a -> Tree e a
- ELynx.Tree.Measurable: normalizeHeight :: Measurable e => Tree e a -> Tree e a
- ELynx.Tree.Measurable: rootHeight :: Measurable e => Tree e a -> Length
- ELynx.Tree.Measurable: setLen :: Measurable e => Length -> e -> e
- ELynx.Tree.Measurable: toLength :: Double -> Either String Length
- ELynx.Tree.Measurable: toLengthUnsafe :: Double -> Length
- ELynx.Tree.Measurable: totalBranchLength :: Measurable e => Tree e a -> Length
- ELynx.Tree.Measurable: ultrametric :: Measurable e => Tree e a -> Bool
- ELynx.Tree.Named: Name :: ByteString -> Name
- ELynx.Tree.Named: [fromName] :: Name -> ByteString
- ELynx.Tree.Named: class Named a
- ELynx.Tree.Named: getName :: Named a => a -> Name
- ELynx.Tree.Named: instance Control.DeepSeq.NFData ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance Data.String.IsString ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance ELynx.Tree.Named.Named ()
- ELynx.Tree.Named: instance ELynx.Tree.Named.Named Data.ByteString.Internal.ByteString
- ELynx.Tree.Named: instance ELynx.Tree.Named.Named Data.ByteString.Lazy.Internal.ByteString
- ELynx.Tree.Named: instance ELynx.Tree.Named.Named ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance ELynx.Tree.Named.Named GHC.Types.Char
- ELynx.Tree.Named: instance ELynx.Tree.Named.Named GHC.Types.Double
- ELynx.Tree.Named: instance ELynx.Tree.Named.Named GHC.Types.Int
- ELynx.Tree.Named: instance ELynx.Tree.Named.Named a => ELynx.Tree.Named.Named [a]
- ELynx.Tree.Named: instance GHC.Base.Monoid ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance GHC.Base.Semigroup ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance GHC.Classes.Eq ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance GHC.Classes.Ord ELynx.Tree.Named.Name
- ELynx.Tree.Named: instance GHC.Show.Show ELynx.Tree.Named.Name
- ELynx.Tree.Named: newtype Name
- ELynx.Tree.Phylogeny: instance ELynx.Tree.Measurable.Measurable ELynx.Tree.Phylogeny.PhyloExplicit
- ELynx.Tree.Phylogeny: instance ELynx.Tree.Supported.Supported ELynx.Tree.Phylogeny.PhyloExplicit
- ELynx.Tree.Phylogeny: rootAt :: (Semigroup e, Splittable e, Eq a, Ord a) => Bipartition a -> Tree e a -> Either String (Tree e a)
- ELynx.Tree.Rooted: instance GHC.Base.Monoid e => GHC.Base.Applicative (ELynx.Tree.Rooted.Tree e)
- ELynx.Tree.Rooted: instance GHC.Base.Monoid e => GHC.Base.Monad (ELynx.Tree.Rooted.Tree e)
- ELynx.Tree.Supported: class Supported e
- ELynx.Tree.Supported: collapse :: (Eq e, Eq a, Supported e) => Support -> Tree e a -> Tree e a
- ELynx.Tree.Supported: data Support
- ELynx.Tree.Supported: getSup :: Supported e => e -> Support
- ELynx.Tree.Supported: instance Control.DeepSeq.NFData ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance ELynx.Tree.Splittable.Splittable ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance ELynx.Tree.Supported.Supported ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Base.Semigroup ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Classes.Eq ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Classes.Ord ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Enum.Enum ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Float.Floating ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Float.RealFloat ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Generics.Generic ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Num.Num ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Read.Read ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Real.Fractional ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Real.Real ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Real.RealFrac ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: instance GHC.Show.Show ELynx.Tree.Supported.Support
- ELynx.Tree.Supported: modSup :: Supported e => (Support -> Support) -> e -> e
- ELynx.Tree.Supported: normalizeBranchSupport :: Supported e => Tree e a -> Tree e a
- ELynx.Tree.Supported: setSup :: Supported e => Support -> e -> e
- ELynx.Tree.Supported: toSupport :: Double -> Either String Support
- ELynx.Tree.Supported: toSupportUnsafe :: Double -> Support
- ELynx.Tree.Zipper: goUp :: TreePos e a -> Maybe (TreePos e a)
+ ELynx.Topology.Phylogeny: equal :: (Eq a, Ord a) => Topology a -> Topology a -> Either String Bool
+ ELynx.Topology.Phylogeny: equal' :: Eq a => Topology a -> Topology a -> Bool
+ ELynx.Tree.Import.Newick: describeNewickFormat :: NewickFormat -> String
+ ELynx.Tree.Length: checkLength :: String -> Length -> Length
+ ELynx.Tree.Length: class HasLength e
+ ELynx.Tree.Length: data Length
+ ELynx.Tree.Length: distancesOriginLeaves :: HasLength e => Tree e a -> [Length]
+ ELynx.Tree.Length: getLen :: HasLength e => e -> Length
+ ELynx.Tree.Length: height :: HasLength e => Tree e a -> Length
+ ELynx.Tree.Length: instance Control.DeepSeq.NFData ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance ELynx.Tree.Length.HasLength ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance ELynx.Tree.Splittable.Splittable ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Base.Monoid ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Base.Semigroup ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Classes.Eq ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Classes.Ord ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Enum.Enum ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Float.Floating ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Float.RealFloat ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Generics.Generic ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Num.Num ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Read.Read ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Real.Fractional ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Real.Real ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Real.RealFrac ELynx.Tree.Length.Length
+ ELynx.Tree.Length: instance GHC.Show.Show ELynx.Tree.Length.Length
+ ELynx.Tree.Length: makeUltrametric :: HasLength e => Tree e a -> Tree e a
+ ELynx.Tree.Length: modLen :: HasLength e => (Length -> Length) -> e -> e
+ ELynx.Tree.Length: normalizeBranchLengths :: HasLength e => Tree e a -> Tree e a
+ ELynx.Tree.Length: normalizeHeight :: HasLength e => Tree e a -> Tree e a
+ ELynx.Tree.Length: rootHeight :: HasLength e => Tree e a -> Length
+ ELynx.Tree.Length: setLen :: HasLength e => Length -> e -> e
+ ELynx.Tree.Length: toLength :: String -> Double -> Length
+ ELynx.Tree.Length: toLengthUnsafe :: Double -> Length
+ ELynx.Tree.Length: totalBranchLength :: HasLength e => Tree e a -> Length
+ ELynx.Tree.Length: ultrametric :: HasLength e => Tree e a -> Bool
+ ELynx.Tree.Name: Name :: ByteString -> Name
+ ELynx.Tree.Name: [fromName] :: Name -> ByteString
+ ELynx.Tree.Name: class HasName a
+ ELynx.Tree.Name: getName :: HasName a => a -> Name
+ ELynx.Tree.Name: instance Control.DeepSeq.NFData ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance Data.String.IsString ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance ELynx.Tree.Name.HasName ()
+ ELynx.Tree.Name: instance ELynx.Tree.Name.HasName Data.ByteString.Internal.ByteString
+ ELynx.Tree.Name: instance ELynx.Tree.Name.HasName Data.ByteString.Lazy.Internal.ByteString
+ ELynx.Tree.Name: instance ELynx.Tree.Name.HasName ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance ELynx.Tree.Name.HasName GHC.Types.Char
+ ELynx.Tree.Name: instance ELynx.Tree.Name.HasName GHC.Types.Double
+ ELynx.Tree.Name: instance ELynx.Tree.Name.HasName GHC.Types.Int
+ ELynx.Tree.Name: instance ELynx.Tree.Name.HasName a => ELynx.Tree.Name.HasName [a]
+ ELynx.Tree.Name: instance GHC.Base.Monoid ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance GHC.Base.Semigroup ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance GHC.Classes.Eq ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance GHC.Classes.Ord ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance GHC.Read.Read ELynx.Tree.Name.Name
+ ELynx.Tree.Name: instance GHC.Show.Show ELynx.Tree.Name.Name
+ ELynx.Tree.Name: newtype Name
+ ELynx.Tree.Phylogeny: equal' :: (Eq e, Eq a) => Tree e a -> Tree e a -> Bool
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Length.HasLength ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Support.HasSupport ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Rooted: instance (GHC.Base.Semigroup e, GHC.Base.Monoid e) => GHC.Base.Applicative (ELynx.Tree.Rooted.Tree e)
+ ELynx.Tree.Rooted: instance (GHC.Base.Semigroup e, GHC.Base.Monoid e) => GHC.Base.Monad (ELynx.Tree.Rooted.Tree e)
+ ELynx.Tree.Support: class HasSupport e
+ ELynx.Tree.Support: collapse :: (Eq e, Eq a, HasSupport e) => Support -> Tree e a -> Tree e a
+ ELynx.Tree.Support: data Support
+ ELynx.Tree.Support: getSup :: HasSupport e => e -> Support
+ ELynx.Tree.Support: instance Control.DeepSeq.NFData ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance ELynx.Tree.Splittable.Splittable ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance ELynx.Tree.Support.HasSupport ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Base.Semigroup ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Classes.Eq ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Classes.Ord ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Enum.Enum ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Float.Floating ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Float.RealFloat ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Generics.Generic ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Num.Num ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Read.Read ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Real.Fractional ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Real.Real ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Real.RealFrac ELynx.Tree.Support.Support
+ ELynx.Tree.Support: instance GHC.Show.Show ELynx.Tree.Support.Support
+ ELynx.Tree.Support: modSup :: HasSupport e => (Support -> Support) -> e -> e
+ ELynx.Tree.Support: normalizeBranchSupport :: HasSupport e => Tree e a -> Tree e a
+ ELynx.Tree.Support: setSup :: HasSupport e => Support -> e -> e
+ ELynx.Tree.Support: toSupport :: String -> Double -> Support
+ ELynx.Tree.Support: toSupportUnsafe :: Double -> Support
+ ELynx.Tree.Zipper: goChildUnsafe :: Int -> TreePos e a -> TreePos e a
+ ELynx.Tree.Zipper: goParent :: TreePos e a -> Maybe (TreePos e a)
+ ELynx.Tree.Zipper: goParentUnsafe :: TreePos e a -> TreePos e a
+ ELynx.Tree.Zipper: modifyTree :: (Tree e a -> Tree e a) -> TreePos e a -> TreePos e a
+ ELynx.Tree.Zipper: validPath :: Tree e a -> Path -> Either String Path
- ELynx.Topology.Rooted: leaves :: Ord a => Topology a -> [a]
+ ELynx.Topology.Rooted: leaves :: Topology a -> [a]
- ELynx.Tree.Distance: branchScore :: (Measurable e1, Measurable e2, Ord a) => Tree e1 a -> Tree e2 a -> Either String Double
+ ELynx.Tree.Distance: branchScore :: (HasLength e1, HasLength e2, Ord a) => Tree e1 a -> Tree e2 a -> Either String Double
- ELynx.Tree.Export.Newick: toNewick :: Named a => Tree Phylo a -> ByteString
+ ELynx.Tree.Export.Newick: toNewick :: HasName a => Tree Phylo a -> ByteString
- ELynx.Tree.Export.Newick: toNewickBuilder :: Named a => Tree Phylo a -> Builder
+ ELynx.Tree.Export.Newick: toNewickBuilder :: HasName a => Tree Phylo a -> Builder
- ELynx.Tree.Export.Nexus: toNexusTrees :: Named a => [(ByteString, Tree Phylo a)] -> ByteString
+ ELynx.Tree.Export.Nexus: toNexusTrees :: HasName a => [(ByteString, Tree Phylo a)] -> ByteString
- ELynx.Tree.Phylogeny: equal :: (Eq e, Eq a) => Tree e a -> Tree e a -> Bool
+ ELynx.Tree.Phylogeny: equal :: (Eq e, Eq a, Ord a) => Tree e a -> Tree e a -> Either String Bool
- ELynx.Tree.Phylogeny: measurableToPhyloTree :: Measurable e => Tree e a -> Tree Phylo a
+ ELynx.Tree.Phylogeny: measurableToPhyloTree :: HasLength e => Tree e a -> Tree Phylo a
- ELynx.Tree.Phylogeny: midpoint :: (Semigroup e, Splittable e, Measurable e) => Tree e a -> Either String (Tree e a)
+ ELynx.Tree.Phylogeny: midpoint :: (Semigroup e, Splittable e, HasLength e) => Tree e a -> Either String (Tree e a)
- ELynx.Tree.Phylogeny: outgroup :: (Semigroup e, Splittable e, Ord a) => Set a -> a -> Tree e a -> Either String (Tree e a)
+ ELynx.Tree.Phylogeny: outgroup :: (Semigroup e, Splittable e, Monoid a, Ord a) => Set a -> Tree e a -> Either String (Tree e a)
- ELynx.Tree.Phylogeny: supportedToPhyloTree :: Supported e => Tree e a -> Tree Phylo a
+ ELynx.Tree.Phylogeny: supportedToPhyloTree :: HasSupport e => Tree e a -> Tree Phylo a
- ELynx.Tree.Phylogeny: toPhyloTree :: (Measurable e, Supported e) => Tree e a -> Tree Phylo a
+ ELynx.Tree.Phylogeny: toPhyloTree :: (HasLength e, HasSupport e) => Tree e a -> Tree Phylo a

Files

ChangeLog.md view
@@ -1,11 +1,18 @@ -# Changelog for ELynx+# Revision history for ELynx   ## Unreleased changes +-   Improve rooting functions.+-   Improve `Topology` data type (but still a lot to do).+-   Various additions to the documentation.+-   Rename `Measurable` to `HasLength`, `Supported` to `HasSupport`, and `Named`+    to `HasLength`.+-   Cabal and stack file changes. -## Version 0.5.0++## Version 0.5.0.1  -   `modLen`, `modSup`. -   Newtype wrappers for branch length, branch support, and node name. Those data
README.md view
@@ -2,7 +2,7 @@  # The ELynx Suite -Version: 0.5.0.+Version: 0.5.0.1. Reproducible evolution made easy.  <p align="center"><img src="https://travis-ci.org/dschrempf/elynx.svg?branch=master"/></p>@@ -19,18 +19,20 @@  The library packages are: --   **elynx-nexus:** Nexus file support.--   **elynx-markov:** Simulate multi sequence alignments along phylogenetic trees.--   **elynx-seq:** Handle evolutionary sequences and multi sequence alignments.--   **elynx-tools:** Tools for the provided executables.--   **elynx-tree:** Handle phylogenetic trees.+-   **[elynx-nexus](https://hackage.haskell.org/package/elynx-nexus):** Nexus file support.+-   **[elynx-markov](https://hackage.haskell.org/package/elynx-markov):** Simulate multi sequence alignments along phylogenetic trees.+-   **[elynx-seq](https://hackage.haskell.org/package/elynx-seq):** Handle evolutionary sequences and multi sequence alignments.+-   **[elynx-tools](https://hackage.haskell.org/package/elynx-tools):** Tools for the provided executables.+-   **[elynx-tree](https://hackage.haskell.org/package/elynx-tree):** Handle phylogenetic trees.  The executables are: --   **slynx:** Analyze, modify, and simulate evolutionary sequences.--   **tlynx:** Analyze, modify, and simulate phylogenetic trees.--   **elynx:** Validate and redo past analyses.+-   **[slynx](https://hackage.haskell.org/package/slynx):** Analyze, modify, and simulate evolutionary sequences.+-   **[tlynx](https://hackage.haskell.org/package/tlynx):** Analyze, modify, and simulate phylogenetic trees.+-   **[elynx](https://hackage.haskell.org/package/elynx):** Validate and redo past analyses. +Documentation is available on [Hackage](https://hackage.haskell.org/) (use direct links above).+ **ELynx is actively developed. We happily receive comments, ideas, feature requests, and pull requests!** @@ -65,34 +67,15 @@     [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) environment variable. Then, they can be used directly.  -# Documentation--Documentation is available on [Hackage](https://hackage.haskell.org/).--Libraries:---   [elynx-nexus](https://hackage.haskell.org/package/elynx-nexus)--   [elynx-markov](https://hackage.haskell.org/package/elynx-markov)--   [elynx-seq](https://hackage.haskell.org/package/elynx-seq)--   [elynx-tools](https://hackage.haskell.org/package/elynx-tools)--   [elynx-tree](https://hackage.haskell.org/package/elynx-tree)--Executables:---   [elynx](https://hackage.haskell.org/package/elynx)--   [slynx](https://hackage.haskell.org/package/slynx)--   [tlynx](https://hackage.haskell.org/package/tlynx)-- # SLynx  Handle evolutionary sequences.      slynx --help | head -n -16 -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: slynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME]                   [-f|--force] [--no-elynx-file] COMMAND@@ -137,9 +120,9 @@      slynx concatenate --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: slynx concatenate (-a|--alphabet NAME) INPUT-FILE       Concatenate sequences found in input files.@@ -158,9 +141,9 @@      slynx examine --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: slynx examine (-a|--alphabet NAME) INPUT-FILE [--per-site]       Examine sequences. If data is a multi sequence alignment, additionally analyze columns.@@ -180,9 +163,9 @@      slynx filter-rows --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: slynx filter-rows (-a|--alphabet NAME) INPUT-FILE [--longer-than LENGTH]                               [--shorter-than LENGTH] [--standard-characters]@@ -203,9 +186,9 @@      slynx filter-columns --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: slynx filter-columns (-a|--alphabet NAME) INPUT-FILE                                  [--standard-chars DOUBLE]@@ -227,9 +210,9 @@      slynx simulate --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: slynx simulate (-t|--tree-file Name) [-s|--substitution-model MODEL]                            [-m|--mixture-model MODEL] [-e|--edm-file NAME] @@ -306,9 +289,9 @@      slynx sub-sample --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: slynx sub-sample (-a|--alphabet NAME) INPUT-FILE                             (-n|--number-of-sites INT)@@ -336,9 +319,9 @@      slynx translate --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: slynx translate (-a|--alphabet NAME) INPUT-FILE (-r|--reading-frame INT)                            (-u|--universal-code CODE)@@ -361,9 +344,9 @@      tlynx --help | head -n -16 -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: tlynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME]                   [-f|--force] [--no-elynx-file] COMMAND@@ -401,9 +384,9 @@      tlynx compare --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: tlynx compare [-n|--normalize] [-b|--bipartitions] [-t|--intersect]                           [-f|--newick-format FORMAT] NAMES@@ -430,9 +413,9 @@      tlynx examine --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: tlynx examine INPUT-FILE [-f|--newick-format FORMAT]       Compute summary statistics of phylogenetic trees.@@ -454,9 +437,9 @@      tlynx simulate --help -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: tlynx simulate (-t|--nTrees INT) (-n|--nLeaves INT) PROCESS                            [-u|--sub-sample DOUBLE] [-s|--summary-statistics] @@ -491,9 +474,9 @@      elynx --help | head -n -16 -    ELynx Suite version 0.5.0.+    ELynx Suite version 0.5.0.1.     Developed by Dominik Schrempf.-    Compiled on November 10, 2020, at 14:29 pm, UTC.+    Compiled on December 18, 2020, at 10:14 am, UTC.          Usage: elynx COMMAND       Validate and redo past ELynx analyses
bench/Length.hs view
@@ -19,10 +19,10 @@ where  import Data.Foldable-import ELynx.Tree.Measurable+import ELynx.Tree.Length  lengthSumFoldl' :: [Length] -> Length-lengthSumFoldl' = foldl' (\x y -> either error id $ toLength $ fromLength x + fromLength y) 0+lengthSumFoldl' = foldl' (\x y -> toLength "lengthSumFoldl'" $ fromLength x + fromLength y) 0  lengthSumFoldl'Unsafe :: [Length] -> Length lengthSumFoldl'Unsafe = foldl' (\x y -> toLengthUnsafe $ fromLength x + fromLength y) 0
bench/Lens.hs view
@@ -18,20 +18,20 @@ where  import Data.Foldable-import ELynx.Tree.Measurable+import ELynx.Tree.Length import Lens.Micro -len :: Measurable a => Lens' a Length+len :: HasLength a => Lens' a Length len = lens getLen (flip setLen) -sumWithGetter :: Measurable a => [a] -> Length+sumWithGetter :: HasLength a => [a] -> Length sumWithGetter = foldl' (\x y -> x ^. len + y ^. len) 0 -sumWithSetter :: Measurable a => [a] -> Length+sumWithSetter :: HasLength a => [a] -> Length sumWithSetter = sumWithGetter . map (\x -> x & len %~ (+ 10)) -sumWithAccessorFunction :: Measurable a => [a] -> Length+sumWithAccessorFunction :: HasLength a => [a] -> Length sumWithAccessorFunction = foldl' (\x y -> getLen x + getLen y) 0 -sumWithModifyFunction :: Measurable a => [a] -> Length+sumWithModifyFunction :: HasLength a => [a] -> Length sumWithModifyFunction = sumWithAccessorFunction . map (modLen (+ 10))
elynx-tree.cabal view
@@ -1,18 +1,19 @@-cabal-version:  2.2-name:           elynx-tree-version:        0.5.0-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-homepage:       https://github.com/dschrempf/elynx#readme-bug-reports:    https://github.com/dschrempf/elynx/issues-author:         Dominik Schrempf-maintainer:     dominik.schrempf@gmail.com-copyright:      Dominik Schrempf (2020)-license:        GPL-3.0-or-later-license-file:   LICENSE-build-type:     Simple+cabal-version:      2.2+name:               elynx-tree+version:            0.5.0.1+license:            GPL-3.0-or-later+license-file:       LICENSE+copyright:          Dominik Schrempf (2020)+maintainer:         dominik.schrempf@gmail.com+author:             Dominik Schrempf+homepage:           https://github.com/dschrempf/elynx#readme+bug-reports:        https://github.com/dschrempf/elynx/issues+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+build-type:         Simple extra-source-files:     README.md     ChangeLog.md@@ -34,114 +35,111 @@     data/UltraMetric.tree  source-repository head-  type: git-  location: https://github.com/dschrempf/elynx+    type:     git+    location: https://github.com/dschrempf/elynx  library-  exposed-modules:-      ELynx.Topology-      ELynx.Topology.Phylogeny-      ELynx.Topology.Rooted-      ELynx.Tree-      ELynx.Tree.Bipartition-      ELynx.Tree.Distance-      ELynx.Tree.Measurable-      ELynx.Tree.Named-      ELynx.Tree.Parallel-      ELynx.Tree.Partition-      ELynx.Tree.Phylogeny-      ELynx.Tree.Rooted-      ELynx.Tree.Splittable-      ELynx.Tree.Supported-      ELynx.Tree.Zipper-      ELynx.Tree.Distribution.BirthDeath-      ELynx.Tree.Distribution.BirthDeathCritical-      ELynx.Tree.Distribution.BirthDeathCriticalNoTime-      ELynx.Tree.Distribution.BirthDeathNearlyCritical-      ELynx.Tree.Distribution.CoalescentContinuous-      ELynx.Tree.Distribution.TimeOfOrigin-      ELynx.Tree.Distribution.TimeOfOriginNearCritical-      ELynx.Tree.Distribution.Types-      ELynx.Tree.Export.Newick-      ELynx.Tree.Export.Nexus-      ELynx.Tree.Import.Newick-      ELynx.Tree.Import.Nexus-      ELynx.Tree.Simulate.Coalescent-      ELynx.Tree.Simulate.PointProcess-  other-modules:-      Paths_elynx_tree-  autogen-modules:-      Paths_elynx_tree-  hs-source-dirs:-      src-  ghc-options: -Wall-  build-depends:-      aeson-    , attoparsec-    , base >=4.7 && <5-    , bytestring-    , comonad-    , containers-    , deepseq-    , double-conversion-    , elynx-nexus-    , math-functions-    , mwc-random-    , parallel-    , primitive-    , statistics-  default-language: Haskell2010+    exposed-modules:+        ELynx.Topology+        ELynx.Topology.Phylogeny+        ELynx.Topology.Rooted+        ELynx.Tree+        ELynx.Tree.Bipartition+        ELynx.Tree.Distance+        ELynx.Tree.Length+        ELynx.Tree.Name+        ELynx.Tree.Parallel+        ELynx.Tree.Partition+        ELynx.Tree.Phylogeny+        ELynx.Tree.Rooted+        ELynx.Tree.Splittable+        ELynx.Tree.Support+        ELynx.Tree.Zipper+        ELynx.Tree.Distribution.BirthDeath+        ELynx.Tree.Distribution.BirthDeathCritical+        ELynx.Tree.Distribution.BirthDeathCriticalNoTime+        ELynx.Tree.Distribution.BirthDeathNearlyCritical+        ELynx.Tree.Distribution.CoalescentContinuous+        ELynx.Tree.Distribution.TimeOfOrigin+        ELynx.Tree.Distribution.TimeOfOriginNearCritical+        ELynx.Tree.Distribution.Types+        ELynx.Tree.Export.Newick+        ELynx.Tree.Export.Nexus+        ELynx.Tree.Import.Newick+        ELynx.Tree.Import.Nexus+        ELynx.Tree.Simulate.Coalescent+        ELynx.Tree.Simulate.PointProcess +    hs-source-dirs:   src+    other-modules:    Paths_elynx_tree+    autogen-modules:  Paths_elynx_tree+    default-language: Haskell2010+    ghc-options:      -Wall -Wunused-packages+    build-depends:+        aeson >=1.5.4.1 && <1.6,+        attoparsec >=0.13.2.4 && <0.14,+        base >=4.14.1.0 && <4.15,+        bytestring >=0.10.10.0 && <0.11,+        comonad >=5.0.6 && <5.1,+        containers >=0.6.2.1 && <0.7,+        deepseq >=1.4.4.0 && <1.5,+        double-conversion >=2.0.2.0 && <2.1,+        elynx-nexus >=0.5.0.1 && <0.6,+        math-functions >=0.3.4.1 && <0.4,+        mwc-random >=0.14.0.0 && <0.15,+        parallel >=3.2.2.0 && <3.3,+        primitive >=0.7.1.0 && <0.8,+        statistics >=0.15.2.0 && <0.16+ test-suite tree-test-  type: exitcode-stdio-1.0-  main-is: Spec.hs-  other-modules:-      ELynx.Topology.RootedSpec-      ELynx.Tree.Arbitrary-      ELynx.Tree.BipartitionSpec-      ELynx.Tree.DistanceSpec-      ELynx.Tree.PartitionSpec-      ELynx.Tree.PhylogenySpec-      ELynx.Tree.RootedSpec-      ELynx.Tree.SupportedSpec-      ELynx.Tree.Export.NewickSpec-      ELynx.Tree.Export.NexusSpec-      ELynx.Tree.Import.NewickSpec-      ELynx.Tree.Import.NexusSpec-      Paths_elynx_tree-  hs-source-dirs:-      test-  ghc-options: -Wall-  build-depends:-      QuickCheck-    , attoparsec-    , base >=4.7 && <5-    , bytestring-    , containers-    , elynx-tools-    , elynx-tree-    , hspec-  default-language: Haskell2010+    type:             exitcode-stdio-1.0+    main-is:          Spec.hs+    hs-source-dirs:   test+    other-modules:+        ELynx.Topology.RootedSpec+        ELynx.Tree.Arbitrary+        ELynx.Tree.BipartitionSpec+        ELynx.Tree.DistanceSpec+        ELynx.Tree.PartitionSpec+        ELynx.Tree.PhylogenySpec+        ELynx.Tree.RootedSpec+        ELynx.Tree.SupportSpec+        ELynx.Tree.Export.NewickSpec+        ELynx.Tree.Export.NexusSpec+        ELynx.Tree.Import.NewickSpec+        ELynx.Tree.Import.NexusSpec+        Paths_elynx_tree +    default-language: Haskell2010+    ghc-options:      -Wall -Wunused-packages+    build-depends:+        QuickCheck >=2.13.2 && <2.14,+        attoparsec >=0.13.2.4 && <0.14,+        base >=4.14.1.0 && <4.15,+        bytestring >=0.10.10.0 && <0.11,+        containers >=0.6.2.1 && <0.7,+        elynx-tools >=0.5.0.1 && <0.6,+        elynx-tree -any,+        hspec >=2.7.4 && <2.8+ benchmark tree-bench-  type: exitcode-stdio-1.0-  main-is: Bench.hs-  other-modules:-      Length-      Lens-      Paths_elynx_tree-  hs-source-dirs:-      bench-  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N-  -- ghc-options: -Wall-  build-depends:-      base >=4.7 && <5-    , bytestring-    , criterion-    , deepseq-    , elynx-tools-    , elynx-tree-    , microlens-    , mwc-random-    , parallel-  default-language: Haskell2010+    type:             exitcode-stdio-1.0+    main-is:          Bench.hs+    hs-source-dirs:   bench+    other-modules:+        Length+        Lens+        Paths_elynx_tree++    default-language: Haskell2010+    ghc-options:+        -Wall -Wunused-packages -threaded -rtsopts -with-rtsopts=-N++    build-depends:+        base >=4.7 && <5,+        criterion >=1.5.7.0 && <1.6,+        elynx-tools >=0.5.0.1 && <0.6,+        elynx-tree -any,+        microlens >=0.4.11.2 && <0.5,+        mwc-random >=0.14.0.0 && <0.15,+        parallel >=3.2.2.0 && <3.3
src/ELynx/Topology/Phylogeny.hs view
@@ -10,6 +10,8 @@ -- -- Creation date: Sat Jul 18 13:15:49 2020. --+-- THIS MODULE IS INCOMPLETE.+-- -- A topology, as it is used in phylogenetics is a 'Topology' with unique leaf -- labels, and the order of the topologies in the sub-forest is considered to be -- meaningless.@@ -23,93 +25,54 @@ -- do perform this check, and return 'Left' with an error message, if the tree -- has duplicate leaves. ----- Note: Topologies are rooted.------ Note: Topologies encoded in Newick format correspond to rooted topologies. By--- convention only, a topology parsed from Newick format is usually thought to--- be unrooted, when the root node is multifurcating and has three children.--- This convention is not enforced here. Newick topologies are just parsed as--- they are, and a rooted topology is returned.+-- Note: 'Topology's are rooted. ----- The bifurcating root of a topology can be changed with 'roots', or 'rootAt'.+-- Note: 'Topology's encoded in Newick format correspond to rooted topologies.+-- By convention only, a topology parsed from Newick format is usually thought+-- to be unrooted, when the root node is multifurcating and has three or more+-- children. This convention is not enforced here. Newick topologies are just+-- parsed as they are, and a rooted topology is returned. ----- Topologies with multifurcating root nodes can be properly rooted using--- 'outgroup'.+-- THIS MODULE IS INCOMPLETE. module ELynx.Topology.Phylogeny-  ( outgroup,-    roots,-    rootAt,+  ( equal,+    equal',   ) where -import Data.Set (Set)+import Data.List+import Data.Maybe import ELynx.Topology.Rooted-import ELynx.Tree.Bipartition --- TODO.---- -- | Remove multifurcations.--- ----- -- A caterpillar like bifurcating tree is used to resolve all multifurcations on--- -- a tree. The multifurcating nodes are copied.--- ----- -- Branch labels are not handled.--- resolve :: Tree () a -> Tree () a--- resolve t@(Node _ _ []) = t--- resolve (Node _ l [x]) = Node () l [resolve x]--- resolve (Node _ l [x, y]) = Node () l $ map resolve [x, y]--- resolve (Node _ l (x : xs)) = Node () l $ map resolve [x, Node () l xs]---- | Resolve a multifurcation at the root using an outgroup.-outgroup :: Ord a => Set a -> Topology a -> Either String (Topology a)-outgroup = undefined---- -- | For a rooted tree with a bifurcating root node, get all possible rooted--- -- trees.--- ----- -- The root node is moved.--- ----- -- For a tree with @l=2@ leaves, there is one rooted tree. For a bifurcating--- -- tree with @l>2@ leaves, there are @(2l-3)@ rooted trees. For a general tree--- -- with a bifurcating root node, and a total number of @n>2@ nodes, there are--- -- (n-2) rooted trees.--- ----- -- Moving a multifurcating root node to another branch would change the--- -- topology, and so, a bifurcating root is required. To resolve a multifurcating--- -- root, please see and use TODO.--- ----- -- Branch labels are not handled, but see 'rootsBranch'.--- ----- -- 'rootAt' roots the tree at a specific position.--- ----- -- Return 'Left' if the root node is not 'bifurcating'.--- roots :: Tree () a -> Either String (Forest () a)--- roots (Node _ _ []) = Left "roots: Root node is a leaf."--- roots (Node _ _ [_]) = Left "roots: Root node has degree two."--- roots t@(Node _ c [tL, tR]) = Right $ t : descend id () c tR tL ++ descend id () c tL tR--- roots _ = Left "roots: Root node is multifurcating."+-- | The equality check is slow because the order of children is considered to+-- be arbitrary.+--+-- NOTE: The equality check is only meaningful if the topologies have unique+-- leaves.+--+-- Return 'Left' if a topology does not have unique leaves.+equal :: (Eq a, Ord a) => Topology a -> Topology a -> Either String Bool+equal tL tR+  | duplicateLeaves tL = Left "equal: Left topology has duplicate leaves."+  | duplicateLeaves tR = Left "equal: Right topology has duplicate leaves."+  | otherwise = Right $ equal' tL tR --- | For a rooted topology with a bifurcating root node, get all possible rooted--- topologies.-roots :: Topology a -> Either String (Forest a)-roots = undefined+-- | Same as 'equal', but assume that leaves are unique.+equal' :: Eq a => Topology a -> Topology a -> Bool+equal' (Leaf lbL) (Leaf lbR) = lbL == lbR+equal' (Node tsL) (Node tsR) =+  (length tsL == length tsR)+    && all (`elem'` tsR) tsL+  where+    elem' t ts = isJust $ find (equal' t) ts+equal' _ _ = False --- -- | Root a tree at a specific position.--- ----- -- Root the tree at the branch defined by the given bipartition. The original--- -- root node is moved to the new position.--- ----- -- The root node must be bifurcating (see 'roots').--- ----- -- Branch labels are not handled, but see 'rootAtBranch'.--- ----- -- Return 'Left', if:--- -- - the root node is not bifurcating;--- -- - the tree has duplicate leaves;--- -- - the bipartition does not match the leaves of the tree.--- rootAt :: Ord a => Bipartition a -> Tree () a -> Either String (Tree () a)--- rootAt = rootAtBranch id+-- TODO. --- | Root a tree at a specific position.-rootAt :: Ord a => Bipartition a -> Topology a -> Either String (Forest a)-rootAt = undefined+-- A multifurcating root node can be resolved to a bifurcating root node with+-- 'outgroup'.+--+-- The bifurcating root node can be changed with 'outgroup' or 'midpoint'.+--+-- For a given topology with bifurcating root node, a list of all rooted+-- topologies is returned by 'roots'.
src/ELynx/Topology/Rooted.hs view
@@ -13,15 +13,15 @@ -- -- Creation date: Sat Jul 11 10:28:28 2020. ----- A 'Topology' differs from a classical rose 'Data.Tree.Tree' in that it does--- not have internal node labels. The leaves have labels.+-- THIS MODULE IS INCOMPLETE. ----- For rooted trees, please see 'ELynx.Tree.Rooted'.+-- A rooted 'Topology' differs from a classical rooted rose 'Data.Tree.Tree' in+-- that it does not have internal node labels. The leaves have labels. ----- In phylogenetics, the order of children of a topology node is arbitrary.--- Internally, however, the underlying 'Topology' data structure stores the--- sub-forest as a (non-empty) list, which has a specific order. Hence, we have--- to do some tricks when comparing topologies, and topology comparison is slow.+-- For rooted trees with branch labels, please see "ELynx.Tree.Rooted". Please+-- also see the note about tree traversals therein.+--+-- THIS MODULE IS INCOMPLETE. module ELynx.Topology.Rooted   ( -- * Data type     Topology (..),@@ -29,16 +29,17 @@     fromTree,     fromLabeledTree, -    -- * Functions-    degree,+    -- * Access leaves, branches and labels     leaves,-    flatten,+    duplicateLeaves,     identify,++    -- * Structure+    degree,     prune,     dropLeavesWith,     zipTreesWith,     zipTrees,-    duplicateLeaves,   ) where @@ -80,7 +81,7 @@   null _ = False   {-# INLINE null #-} -  toList = flatten+  toList = leaves   {-# INLINE toList #-}  instance Traversable Topology where@@ -120,23 +121,6 @@  instance FromJSON a => FromJSON (Topology a) --- | The degree of the root node.-degree :: Topology a -> Int-degree (Node ts) = (+ 1) $ length ts-degree (Leaf _) = 1---- | Set of leaves.-leaves :: Ord a => Topology a -> [a]-leaves (Leaf lb) = [lb]-leaves (Node ts) = concatMap leaves ts---- | Return leaf labels in pre-order.-flatten :: Topology a -> [a]-flatten t = squish t []-  where-    squish (Node ts) xs = foldr squish xs ts-    squish (Leaf lb) xs = lb : xs- -- TODO: Provide and fix tests, provide arbitrary instances.  -- | Convert a rooted rose tree to a rooted topology. Internal node labels are lost.@@ -150,10 +134,40 @@ fromLabeledTree (R.Node _ lb []) = Leaf lb fromLabeledTree (R.Node _ _ xs) = Node $ fromLabeledTree <$> N.fromList xs +-- TODO: Maybe use foldr similar to 'flatten'.+-- | Set of leaves.+leaves :: Topology a -> [a]+leaves (Leaf lb) = [lb]+leaves (Node ts) = concatMap leaves ts++-- -- TODO: Check if this implementation of 'leaves' is faster.+-- -- | Return leaf labels in pre-order.+-- flatten :: Topology a -> [a]+-- flatten t = squish t []+--   where+--     squish (Node ts) xs = foldr squish xs ts+--     squish (Leaf lb) xs = lb : xs++duplicates :: Ord a => [a] -> Bool+duplicates = go S.empty+  where+    go _ [] = False+    go seen (x : xs) = x `S.member` seen || go (S.insert x seen) xs++-- | Check if a topology has duplicate leaves.+duplicateLeaves :: Ord a => Topology a -> Bool+duplicateLeaves = duplicates . leaves++-- TODO: This is the same as in ELynx.Tree.Rooted. -- | Label the leaves with unique integers starting at 0. identify :: Traversable t => t a -> t Int identify = snd . mapAccumL (\i _ -> (i + 1, i)) (0 :: Int) +-- | The degree of the root node.+degree :: Topology a -> Int+degree (Node ts) = (+ 1) $ length ts+degree (Leaf _) = 1+ -- | Prune degree two nodes. prune :: Topology a -> Topology a prune (Node ts)@@ -195,13 +209,3 @@ -- Return 'Nothing' if the topologies are different. zipTrees :: Topology a1 -> Topology a2 -> Maybe (Topology (a1, a2)) zipTrees = zipTreesWith (,)--duplicates :: Ord a => [a] -> Bool-duplicates = go S.empty-  where-    go _ [] = False-    go seen (x : xs) = x `S.member` seen || go (S.insert x seen) xs---- | Check if a topology has duplicate leaves.-duplicateLeaves :: Ord a => Topology a -> Bool-duplicateLeaves = duplicates . leaves
src/ELynx/Tree.hs view
@@ -22,12 +22,12 @@     module ELynx.Tree.Parallel,      -- * Branch label classes-    module ELynx.Tree.Measurable,+    module ELynx.Tree.Length,     module ELynx.Tree.Splittable,-    module ELynx.Tree.Supported,+    module ELynx.Tree.Support,      -- * Node label classes-    module ELynx.Tree.Named,+    module ELynx.Tree.Name,      -- * Phylogenies     module ELynx.Tree.Phylogeny,@@ -51,14 +51,14 @@ import ELynx.Tree.Export.Nexus import ELynx.Tree.Import.Newick import ELynx.Tree.Import.Nexus-import ELynx.Tree.Measurable-import ELynx.Tree.Named+import ELynx.Tree.Length+import ELynx.Tree.Name import ELynx.Tree.Parallel import ELynx.Tree.Partition import ELynx.Tree.Phylogeny import ELynx.Tree.Rooted import ELynx.Tree.Splittable-import ELynx.Tree.Supported+import ELynx.Tree.Support import ELynx.Tree.Zipper  -- -- | An evolutionary label has some information about where the corresponding
src/ELynx/Tree/Distance.hs view
@@ -36,7 +36,7 @@ import Data.Set (Set) import qualified Data.Set as S import ELynx.Tree.Bipartition-import ELynx.Tree.Measurable+import ELynx.Tree.Length import ELynx.Tree.Partition import ELynx.Tree.Rooted @@ -124,7 +124,7 @@ -- trees is returned. -- -- XXX: Comparing a list of trees recomputes bipartitions.-branchScore :: (Measurable e1, Measurable e2, Ord a) => Tree e1 a -> Tree e2 a -> Either String Double+branchScore :: (HasLength e1, HasLength e2, Ord a) => Tree e1 a -> Tree e2 a -> Either String Double branchScore t1 t2   | S.fromList (leaves t1) /= S.fromList (leaves t2) = Left "branchScoreWith: Trees do not have equal leaf sets."   | otherwise = do
src/ELynx/Tree/Export/Newick.hs view
@@ -23,11 +23,11 @@ import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as BL import Data.List (intersperse)-import ELynx.Tree.Measurable-import ELynx.Tree.Named+import ELynx.Tree.Length+import ELynx.Tree.Name import ELynx.Tree.Phylogeny import ELynx.Tree.Rooted-import ELynx.Tree.Supported+import ELynx.Tree.Support  buildBrLen :: Length -> BB.Builder buildBrLen bl = BB.char8 ':' <> BB.doubleDec (fromLength bl)@@ -36,7 +36,7 @@ buildBrSup bs = BB.char8 '[' <> BB.doubleDec (fromSupport bs) <> BB.char8 ']'  -- | See 'toNewick'.-toNewickBuilder :: Named a => Tree Phylo a -> BB.Builder+toNewickBuilder :: HasName a => Tree Phylo a -> BB.Builder toNewickBuilder t = go t <> BB.char8 ';'   where     go (Node b l []) = lbl b l@@ -65,5 +65,5 @@ -- @ -- "ACTUALNAME[posterior=-2839.2,age_95%_HPD={4.80804,31.6041}]" -- @-toNewick :: Named a => Tree Phylo a -> BL.ByteString+toNewick :: HasName a => Tree Phylo a -> BL.ByteString toNewick = BB.toLazyByteString . toNewickBuilder
src/ELynx/Tree/Export/Nexus.hs view
@@ -19,13 +19,13 @@ import qualified Data.ByteString.Lazy.Char8 as BL import ELynx.Export.Nexus import ELynx.Tree.Export.Newick-import ELynx.Tree.Named+import ELynx.Tree.Name import ELynx.Tree.Phylogeny import ELynx.Tree.Rooted  -- | Export a list of (NAME, TREE) to a Nexus file.-toNexusTrees :: Named a => [(BL.ByteString, Tree Phylo a)] -> BL.ByteString+toNexusTrees :: HasName a => [(BL.ByteString, Tree Phylo a)] -> BL.ByteString toNexusTrees ts = toNexus "TREES" (map tree ts) -tree :: Named a => (BL.ByteString, Tree Phylo a) -> BL.ByteString+tree :: HasName a => (BL.ByteString, Tree Phylo a) -> BL.ByteString tree (n, t) = "  TREE " <> n <> " = " <> toNewick t
src/ELynx/Tree/Import/Newick.hs view
@@ -31,7 +31,7 @@ -- @ module ELynx.Tree.Import.Newick   ( NewickFormat (..),-    description,+    describeNewickFormat,     newick,     parseNewick,     oneNewick,@@ -46,11 +46,11 @@ import Data.Attoparsec.ByteString.Char8 import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL-import ELynx.Tree.Measurable-import ELynx.Tree.Named+import ELynx.Tree.Length+import ELynx.Tree.Name import ELynx.Tree.Phylogeny import ELynx.Tree.Rooted hiding (forest, label)-import ELynx.Tree.Supported+import ELynx.Tree.Support import GHC.Generics import Prelude hiding (takeWhile) @@ -68,12 +68,12 @@ instance ToJSON NewickFormat  -- | Short description of the supported Newick formats.-description :: NewickFormat -> String-description Standard =+describeNewickFormat :: NewickFormat -> String+describeNewickFormat Standard =   "Standard: Branch support values are stored in square brackets after branch lengths."-description IqTree =+describeNewickFormat IqTree =   "IqTree:   Branch support values are stored as node names after the closing bracket of forests."-description RevBayes =+describeNewickFormat 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."  -- | Newick tree parser. Also succeeds when more trees follow.@@ -155,7 +155,7 @@ branchLength = do   _ <- char ':' <?> "branchLengthDelimiter"   l <- double <?> "branchLength"-  return $ either error id $ toLength l+  return $ toLength "branchLength" l  branchSupport :: Parser Support branchSupport =@@ -163,7 +163,7 @@     _ <- char '[' <?> "branchSupportBegin"     s <- double <?> "branchSupport"     _ <- char ']' <?> "branchSupportEnd"-    return $ either error id $ toSupport s+    return $ toSupport "branchSupport" s  -------------------------------------------------------------------------------- -- IQ-TREE.@@ -191,7 +191,7 @@ branchedIqTree = (<?> "branchedIqTree") $ do   f <- forestIqTree   ms <- optional double-  let s = either error id . toSupport <$> ms+  let s = toSupport "branchedIqTree" <$> ms   n <- name   b <- optional branchLength   return $ Node (Phylo b s) n f
src/ELynx/Tree/Import/Nexus.hs view
@@ -21,7 +21,7 @@ import qualified Data.ByteString.Char8 as BS import ELynx.Import.Nexus import ELynx.Tree.Import.Newick-import ELynx.Tree.Named+import ELynx.Tree.Name import ELynx.Tree.Phylogeny import ELynx.Tree.Rooted import Prelude hiding (takeWhile)
+ src/ELynx/Tree/Length.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}++-- |+-- Module      :  ELynx.Tree.Length+-- Description :  Measurable labels+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Thu Jan 17 14:16:34 2019.+--+-- Non-negativity of lengths is not completely ensured. See the documentation of+-- 'Length'.+module ELynx.Tree.Length+  ( -- * Non-negative length+    Length (fromLength),+    toLength,+    toLengthUnsafe,+    checkLength,+    HasLength (..),+    height,+    rootHeight,++    -- * Functions on trees+    distancesOriginLeaves,+    totalBranchLength,+    normalizeBranchLengths,+    normalizeHeight,+    ultrametric,+    makeUltrametric,+  )+where++import Control.DeepSeq+import Data.Aeson+import Data.Bifoldable+import Data.Bifunctor+import Data.Semigroup+import ELynx.Tree.Rooted+import ELynx.Tree.Splittable+import GHC.Generics++-- | Non-negative length.+--+-- However, non-negativity is only checked with 'toLength', and negative values+-- can be obtained using the 'Num' and related instances.+--+-- Safe conversion is roughly 50 percent slower.+--+-- @+-- benchmarking length/length sum foldl' with safe conversion+-- time                 110.4 ms   (109.8 ms .. 111.0 ms)+--                      1.000 R²   (1.000 R² .. 1.000 R²)+-- mean                 110.2 ms   (110.0 ms .. 110.6 ms)+-- std dev              501.8 μs   (359.1 μs .. 730.0 μs)+--+-- benchmarking length/length sum foldl' num instance+-- time                 89.37 ms   (85.13 ms .. 94.27 ms)+--                      0.996 R²   (0.992 R² .. 1.000 R²)+-- mean                 86.53 ms   (85.63 ms .. 88.52 ms)+-- std dev              2.239 ms   (1.069 ms .. 3.421 ms)+--+-- benchmarking length/double sum foldl'+-- time                 85.47 ms   (84.88 ms .. 86.42 ms)+--                      1.000 R²   (0.999 R² .. 1.000 R²)+-- mean                 85.56 ms   (85.26 ms .. 86.02 ms)+-- std dev              611.9 μs   (101.5 μs .. 851.7 μs)+-- @+newtype Length = Length {fromLength :: Double}+  deriving (Read, Show, Generic, NFData)+  deriving (Enum, Eq, Floating, Fractional, Num, Ord, Real, RealFloat, RealFrac) via Double+  deriving (Semigroup, Monoid) via Sum Double++instance Splittable Length where+  split = (/ 2.0)++instance ToJSON Length++instance FromJSON Length++instance HasLength Length where+  getLen = id+  setLen = const+  modLen f = f++-- | If negative, call 'error' indicating the calling function name.+toLength :: String -> Double -> Length+toLength s x+  | x < 0 = error $ s ++ ": Length is negative: " ++ show x ++ "."+  | otherwise = Length x++-- | Do not check if value is negative.+toLengthUnsafe :: Double -> Length+toLengthUnsafe = Length++-- | If negative, call 'error' with given calling function name.+checkLength :: String -> Length -> Length+checkLength s = toLength s . fromLength++-- | A data type with measurable and modifiable values.+class HasLength e where+  -- | Get length.+  getLen :: e -> Length++  -- | Set length.+  setLen :: Length -> e -> e++  -- For computational efficiency.++  -- | Modify length.+  modLen :: (Length -> Length) -> e -> e++-- | The maximum distance between origin and leaves.+--+-- The height includes the branch length of the stem.+height :: HasLength e => Tree e a -> Length+height = maximum . distancesOriginLeaves++-- | The maximum distance between root node and leaves.+rootHeight :: HasLength e => Tree e a -> Length+rootHeight (Node _ _ []) = 0+rootHeight t = maximum $ concatMap distancesOriginLeaves (forest t)++-- | Distances from the origin of a tree to the leaves.+--+-- The distances include the branch length of the stem.+distancesOriginLeaves :: HasLength e => Tree e a -> [Length]+distancesOriginLeaves (Node br _ []) = [getLen br]+distancesOriginLeaves (Node br _ ts) = map (getLen br +) (concatMap distancesOriginLeaves ts)++-- | Total branch length of a tree.+totalBranchLength :: HasLength e => Tree e a -> Length+totalBranchLength = bifoldl' (+) const 0 . first getLen++-- | Normalize branch lengths so that the sum is 1.0.+normalizeBranchLengths :: HasLength e => Tree e a -> Tree e a+normalizeBranchLengths t = first (modLen (/ s)) t+  where+    s = totalBranchLength t++-- | Normalize height of tree to 1.0.+normalizeHeight :: HasLength e => Tree e a -> Tree e a+normalizeHeight t = first (modLen (/ h)) t+  where+    h = height t++eps :: Double+eps = 1e-12++allNearlyEqual :: [Length] -> Bool+allNearlyEqual [] = True+allNearlyEqual xs = all (\y -> eps > abs (fromLength $ x - y)) (tail xs)+  where+    x = head xs++-- | Check if a tree is ultrametric.+ultrametric :: HasLength e => Tree e a -> Bool+ultrametric = allNearlyEqual . distancesOriginLeaves++-- | Elongate terminal branches such that the tree becomes ultrametric.+makeUltrametric :: HasLength e => Tree e a -> Tree e a+makeUltrametric t = go 0 t+  where+    h = height t+    go :: HasLength e => Length -> Tree e a -> Tree e a+    go h' (Node br lb []) = let dh = h - h' - getLen br in Node (modLen (+ dh) br) lb []+    go h' (Node br lb ts) = let h'' = h' + getLen br in Node br lb $ map (go h'') ts
− src/ELynx/Tree/Measurable.hs
@@ -1,173 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingVia #-}---- |--- Module      :  ELynx.Tree.Measurable--- Description :  Measurable labels--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Thu Jan 17 14:16:34 2019.------ Non-negativity of lengths is not completely ensured. See the documentation of--- 'Length'.-module ELynx.Tree.Measurable-  ( -- * Non-negative length-    Length (fromLength),-    toLength,-    toLengthUnsafe,-    checkLength,-    Measurable (..),-    height,-    rootHeight,--    -- * Functions on trees-    distancesOriginLeaves,-    totalBranchLength,-    normalizeBranchLengths,-    normalizeHeight,-    ultrametric,-    makeUltrametric,-  )-where--import Control.DeepSeq-import Data.Aeson-import Data.Bifoldable-import Data.Bifunctor-import Data.Semigroup-import ELynx.Tree.Rooted-import ELynx.Tree.Splittable-import GHC.Generics---- | Non-negative length.------ However, non-negativity is only checked with 'toLength', and negative values--- can be obtained using the 'Num' and related instances.------ Safe operations with conversion from and to length are roughly 50 percent--- slower.------ @--- benchmarking length/length sum foldl' with safe conversion--- time                 110.4 ms   (109.8 ms .. 111.0 ms)---                      1.000 R²   (1.000 R² .. 1.000 R²)--- mean                 110.2 ms   (110.0 ms .. 110.6 ms)--- std dev              501.8 μs   (359.1 μs .. 730.0 μs)------ benchmarking length/length sum foldl' num instance--- time                 89.37 ms   (85.13 ms .. 94.27 ms)---                      0.996 R²   (0.992 R² .. 1.000 R²)--- mean                 86.53 ms   (85.63 ms .. 88.52 ms)--- std dev              2.239 ms   (1.069 ms .. 3.421 ms)------ benchmarking length/double sum foldl'--- time                 85.47 ms   (84.88 ms .. 86.42 ms)---                      1.000 R²   (0.999 R² .. 1.000 R²)--- mean                 85.56 ms   (85.26 ms .. 86.02 ms)--- std dev              611.9 μs   (101.5 μs .. 851.7 μs)--- @-newtype Length = Length {fromLength :: Double}-  deriving (Read, Show, Generic, NFData)-  deriving (Enum, Eq, Floating, Fractional, Num, Ord, Real, RealFloat, RealFrac) via Double-  deriving (Semigroup, Monoid) via Sum Double--instance Splittable Length where-  split = (/ 2.0)--instance ToJSON Length--instance FromJSON Length--instance Measurable Length where-  getLen = id-  setLen = const-  modLen f = f---- | 'Nothing' if length is negative.-toLength :: Double -> Either String Length-toLength x-  | x < 0 = Left $ "length: Length is negative: " ++ show x ++ "."-  | otherwise = Right $ Length x---- | Do not check if support value is negative.-toLengthUnsafe :: Double -> Length-toLengthUnsafe = Length---- | 'Nothing' if length is negative.-checkLength :: Length -> Either String Length-checkLength = toLength . fromLength---- | A data type with measurable and modifiable length.-class Measurable e where-  -- | Get length.-  getLen :: e -> Length--  -- | Set length.-  setLen :: Length -> e -> e--  -- For computational efficiency.--  -- | Modify length.-  modLen :: (Length -> Length) -> e -> e---- | The maximum distance between origin and leaves.------ The height includes the branch length of the stem.-height :: Measurable e => Tree e a -> Length-height = maximum . distancesOriginLeaves---- | The maximum distance between root node and leaves.-rootHeight :: Measurable e => Tree e a -> Length-rootHeight (Node _ _ []) = 0-rootHeight t = maximum $ concatMap distancesOriginLeaves (forest t)---- | Distances from the origin of a tree to the leaves.------ The distances include the branch length of the stem.-distancesOriginLeaves :: Measurable e => Tree e a -> [Length]-distancesOriginLeaves (Node br _ []) = [getLen br]-distancesOriginLeaves (Node br _ ts) = map (getLen br +) (concatMap distancesOriginLeaves ts)---- | Total branch length of a tree.-totalBranchLength :: Measurable e => Tree e a -> Length-totalBranchLength = bifoldl' (+) const 0 . first getLen---- | Normalize branch lengths so that the sum is 1.0.-normalizeBranchLengths :: Measurable e => Tree e a -> Tree e a-normalizeBranchLengths t = first (modLen (/ s)) t-  where-    s = totalBranchLength t---- | Normalize height of tree to 1.0.-normalizeHeight :: Measurable e => Tree e a -> Tree e a-normalizeHeight t = first (modLen (/ h)) t-  where-    h = height t--eps :: Double-eps = 1e-12--allNearlyEqual :: [Length] -> Bool-allNearlyEqual [] = True-allNearlyEqual xs = all (\y -> eps > abs (fromLength $ x - y)) (tail xs)-  where-    x = head xs---- | Check if a tree is ultrametric.-ultrametric :: Measurable e => Tree e a -> Bool-ultrametric = allNearlyEqual . distancesOriginLeaves---- | Elongate terminal branches such that the tree becomes ultrametric.-makeUltrametric :: Measurable e => Tree e a -> Tree e a-makeUltrametric t = go 0 t-  where-    h = height t-    go :: Measurable e => Length -> Tree e a -> Tree e a-    go h' (Node br lb []) = let dh = h - h' - getLen br in Node (modLen (+ dh) br) lb []-    go h' (Node br lb ts) = let h'' = h' + getLen br in Node br lb $ map (go h'') ts
+ src/ELynx/Tree/Name.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE TypeSynonymInstances #-}++-- |+-- Module      :  ELynx.Tree.Name+-- Description :  Trees with named nodes+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Thu Jan 24 20:09:20 2019.+module ELynx.Tree.Name+  ( Name (..),+    HasName (..),+  )+where++import Control.DeepSeq+import Data.Aeson+import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as BL+import Data.Double.Conversion.ByteString as BC+import Data.String++-- | Node name.+--+-- Use lazy byte strings because Newick strings are built using chunks.+newtype Name = Name {fromName :: BL.ByteString}+  deriving (Show, Read, Eq)+  deriving (Ord, Monoid, Semigroup, IsString, NFData) via BL.ByteString++-- XXX: This is pretty lame, but I need those instances. At the moment, I just+-- go via 'String', but this is certainly not the best solution.++instance ToJSON Name where+  toJSON = toJSON . BL.unpack . fromName+  toEncoding = toEncoding . BL.unpack . fromName++instance FromJSON Name where+  parseJSON = fmap (Name . BL.pack) . parseJSON++-- | Class of types having a name.+class HasName a where+  getName :: a -> Name++instance HasName Name where+  getName = id++instance HasName () where+  getName = const (Name BL.empty)++instance HasName Int where+  getName = Name . BB.toLazyByteString . BB.intDec++instance HasName Double where+  getName = Name . BL.fromStrict . toShortest++instance HasName Char where+  getName = Name . BB.toLazyByteString . BB.char8++instance (HasName a) => HasName [a] where+  getName = Name . BL.concat . map (fromName . getName)++instance HasName BL.ByteString where+  getName = Name++instance HasName BS.ByteString where+  getName = Name . BL.fromStrict
− src/ELynx/Tree/Named.hs
@@ -1,72 +0,0 @@-{-# LANGUAGE DerivingVia #-}-{-# LANGUAGE TypeSynonymInstances #-}---- |--- Module      :  ELynx.Tree.Named--- Description :  Trees with named nodes--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Thu Jan 24 20:09:20 2019.-module ELynx.Tree.Named-  ( Name (..),-    Named (..),-  )-where--import Control.DeepSeq-import Data.Aeson-import qualified Data.ByteString.Builder as BB-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BL-import Data.Double.Conversion.ByteString as BC-import Data.String---- | Node name.------ Use lazy byte strings because Newick strings are built using chunks.-newtype Name = Name {fromName :: BL.ByteString}-  deriving (Show, Eq)-  deriving (Ord, Monoid, Semigroup, IsString, NFData) via BL.ByteString---- XXX: This is pretty lame, but I need those instances. At the moment, I just--- go via 'String', but this is certainly not the best solution.--instance ToJSON Name where-  toJSON = toJSON . BL.unpack . fromName-  toEncoding = toEncoding . BL.unpack . fromName--instance FromJSON Name where-  parseJSON = fmap (Name . BL.pack) . parseJSON--instance Named Name where-  getName = id---- | Data types with names.-class Named a where-  getName :: a -> Name--instance Named () where-  getName = const (Name BL.empty)--instance Named Int where-  getName = Name . BB.toLazyByteString . BB.intDec--instance Named Double where-  getName = Name . BL.fromStrict . toShortest--instance Named Char where-  getName = Name . BB.toLazyByteString . BB.char8--instance (Named a) => Named [a] where-  getName = Name . BL.concat . map (fromName . getName)--instance Named BL.ByteString where-  getName = Name--instance Named BS.ByteString where-  getName = Name . BL.fromStrict
src/ELynx/Tree/Phylogeny.hs view
@@ -39,23 +39,26 @@ -- -- Note: 'Tree's encoded in Newick format correspond to rooted trees. By -- convention only, a tree parsed from Newick format is usually thought to be--- unrooted, when the root node is multifurcating and has three children. This--- convention is not used here. Newick trees are just parsed as they are, and a--- rooted tree is returned.+-- unrooted, when the root node is multifurcating and has three or more+-- children. This convention is not used here. Newick trees are just parsed as+-- they are, and a rooted tree is returned. ----- Trees with multifurcating root nodes can be rooted using 'outgroup'.+-- A multifurcating root node can be resolved to a bifurcating root node with+-- 'outgroup'. ----- Trees with bifurcating root nodes can be changed with 'rootAt' or 'midpoint';--- a list of all rooted trees is returned by 'roots'.+-- The bifurcating root node can be changed with 'outgroup' or 'midpoint'.+--+-- For a given tree with bifurcating root node, a list of all rooted trees is+-- returned by 'roots'. module ELynx.Tree.Phylogeny   ( -- * Functions     equal,+    equal',     intersect,     bifurcating,     outgroup,     midpoint,     roots,-    rootAt,      -- * Branch labels     Phylo (..),@@ -82,22 +85,33 @@ import Data.Set (Set) import qualified Data.Set as S import ELynx.Tree.Bipartition-import ELynx.Tree.Measurable+import ELynx.Tree.Length import ELynx.Tree.Rooted import ELynx.Tree.Splittable-import ELynx.Tree.Supported+import ELynx.Tree.Support import GHC.Generics --- A faster check could probably be done using 'Ord' and sets. The leave set--- could be precomputed. -- | The equality check is slow because the order of children is considered to -- be arbitrary.-equal :: (Eq e, Eq a) => Tree e a -> Tree e a -> Bool-equal ~(Node brL lbL tsL) ~(Node brR lbR tsR) =+--+-- NOTE: The equality check is only meaningful if the trees have unique leaves.+--+-- Return 'Left' if a tree does not have unique leaves.+equal :: (Eq e, Eq a, Ord a) => Tree e a -> Tree e a -> Either String Bool+equal tL tR+  | duplicateLeaves tL = Left "equal: Left tree has duplicate leaves."+  | duplicateLeaves tR = Left "equal: Right tree has duplicate leaves."+  | otherwise = Right $ equal' tL tR++-- | Same as 'equal', but assume that leaves are unique.+equal' :: (Eq e, Eq a) => Tree e a -> Tree e a -> Bool+equal' ~(Node brL lbL tsL) ~(Node brR lbR tsR) =   (brL == brR)     && (lbL == lbR)     && (length tsL == length tsR)-    && all (`elem` tsR) tsL+    && all (elem' tsR) tsL+  where+    elem' ts t = isJust $ find (equal' t) ts  -- | Compute the intersection of trees. --@@ -106,6 +120,7 @@ -- Degree two nodes are pruned with 'prune'. -- -- Return 'Left' if:+-- -- - the intersection of leaves is empty. intersect ::   (Semigroup e, Eq e, Ord a) => Forest e a -> Either String (Forest e a)@@ -148,33 +163,40 @@ -- resolve (Node br lb (Node brL lbL xsL : xs)) = Node br lb [Node brL' lbL (map resolve xsL), Node brL' lb (map resolve xs)] --   where brL' = split brL --- | Resolve a multifurcating root using an outgroup.+-- | Root the tree using an outgroup. ----- A bifurcating root node with the provided label is introduced. The affected--- branch is 'split'.+-- If the current root node is multifurcating, a bifurcating root node with the+-- empty label is introduced by 'split'ting the leftmost branch. The 'Monoid'+-- instance of the node label and the 'Splittable' instance of the branch length+-- are used. Note that in this case, the degree of the former root node is+-- decreased by one! ----- Note, the degree of the former root node is decreased by one.+-- Given that the root note is bifurcating, the root node is moved to the+-- required position specified by the outgroup. ----- If the root node is bifurcating, use 'rootAt'.+-- Branches are connected according to the provided 'Semigroup' instance. --+-- Upon insertion of the root node at the required position, the affected branch+-- is 'split' according to the provided 'Splittable' instance.+-- -- Return 'Left' if+-- -- - the root node is not multifurcating;+-- -- - the tree has duplicate leaves;+-- -- - the provided outgroup is not found on the tree or is polyphyletic.-outgroup :: (Semigroup e, Splittable e, Ord a) => Set a -> a -> Tree e a -> Either String (Tree e a)-outgroup _ _ (Node _ _ []) = Left "outgroup: Root node is a leaf."-outgroup _ _ (Node _ _ [_]) = Left "outgroup: Root node has degree two."-outgroup _ _ (Node _ _ [_, _]) = Left "outgroup: Root node is bifurcating."-outgroup o r t@(Node b l ts)-  | duplicateLeaves t = Left "outgroup: Tree has duplicate leaves."-  | otherwise = do-    bip <- bp o (S.fromList lvs S.\\ o)-    rootAt bip t'+outgroup :: (Semigroup e, Splittable e, Monoid a, Ord a) => Set a -> Tree e a -> Either String (Tree e a)+outgroup _ (Node _ _ []) = Left "outgroup: Root node is a leaf."+outgroup _ (Node _ _ [_]) = Left "outgroup: Root node has degree two."+outgroup o t@(Node _ _ [_, _]) = do+  bip <- bp o (S.fromList (leaves t) S.\\ o)+  rootAt bip t+outgroup o (Node b l ts) = outgroup o t'   where-    lvs = leaves t     (Node brO lbO tsO) = head ts     -- Introduce a bifurcating root node.-    t' = Node b r [Node (split brO) lbO tsO, Node (split brO) l (tail ts)]+    t' = Node b mempty [Node (split brO) lbO tsO, Node (split brO) l (tail ts)]  -- The 'midpoint' algorithm is pretty stupid because it calculates all rooted -- trees and then finds the one minimizing the difference between the heights of@@ -185,8 +207,9 @@ -- | Root tree at the midpoint. -- -- Return 'Left' if+-- -- - the root node is not bifurcating.-midpoint :: (Semigroup e, Splittable e, Measurable e) => Tree e a -> Either String (Tree e a)+midpoint :: (Semigroup e, Splittable e, HasLength e) => Tree e a -> Either String (Tree e a) midpoint (Node _ _ []) = Left "midpoint: Root node is a leaf." midpoint (Node _ _ [_]) = Left "midpoint: Root node has degree two." midpoint t@(Node _ _ [_, _]) = getMidpoint <$> roots t@@ -199,7 +222,7 @@     go (i, z) j (y : ys) = if z < y then go (i, z) (j + 1) ys else go (j, y) (j + 1) ys findMinIndex [] = error "findMinIndex: Empty list." -getMidpoint :: Measurable e => [Tree e a] -> Tree e a+getMidpoint :: HasLength e => [Tree e a] -> Tree e a getMidpoint ts = case t of   (Node br lb [l, r]) ->     let hl = height l@@ -222,7 +245,7 @@ -- find index of minimum; take this tree and move root to the midpoint of the branch  -- Get delta height of left and right sub tree.-getDeltaHeight :: Measurable e => Tree e a -> Length+getDeltaHeight :: HasLength e => Tree e a -> Length getDeltaHeight (Node _ _ [l, r]) = abs $ height l - height r -- Explicitly use 'error' here, because roots is supposed to return trees with -- bifurcating root nodes.@@ -238,14 +261,14 @@ -- with a bifurcating root node, and a total number of @n>2@ nodes, there are -- (n-2) rooted trees. ----- Moving a multifurcating root node to another branch would change the degree--- of the root node. Hence, a bifurcating root is required. To resolve a+-- A bifurcating root is required because moving a multifurcating root node to+-- another branch would change the degree of the root node. To resolve a -- multifurcating root, please use 'outgroup'. -- -- Connect branches according to the provided 'Semigroup' instance. ----- Upon insertion of the root, split the affected branch into one out of two--- equal entities according to a given function.+-- Split the affected branch into one out of two equal entities according the+-- provided 'Splittable' instance. -- -- Return 'Left' if the root node is not 'bifurcating'. roots :: (Semigroup e, Splittable e) => Tree e a -> Either String (Forest e a)@@ -277,7 +300,7 @@     tC' = tC {branch = brC'}     cfs = complementaryForests tC' tsD --- | Root a tree at a specific position.+-- Root a tree at a specific position. -- -- Root the tree at the branch defined by the given bipartition. The original -- root node is moved to the new position.@@ -290,8 +313,11 @@ -- provided 'Splittable' instance. -- -- Return 'Left', if:+-- -- - the root node is not bifurcating;+-- -- - the tree has duplicate leaves;+-- -- - the bipartition does not match the leaves of the tree. rootAt ::   (Semigroup e, Splittable e, Eq a, Ord a) =>@@ -318,7 +344,7 @@   Either String (Tree e a) rootAt' b t = do   ts <- roots t-  case find (\x -> Right b == bipartition x) ts of+  case find (\x -> bipartition x == Right b) ts of     Nothing -> Left "rootAt': Bipartition not found on tree."     Just t' -> Right t' @@ -344,10 +370,10 @@ -- | Set all branch lengths and support values to 'Just' the value. -- -- Useful to export a tree with branch lengths in Newick format.-toPhyloTree :: (Measurable e, Supported e) => Tree e a -> Tree Phylo a+toPhyloTree :: (HasLength e, HasSupport e) => Tree e a -> Tree Phylo a toPhyloTree = first toPhyloLabel -toPhyloLabel :: (Measurable e, Supported e) => e -> Phylo+toPhyloLabel :: (HasLength e, HasSupport e) => e -> Phylo toPhyloLabel x = Phylo (Just $ getLen x) (Just $ getSup x)  -- | Set all branch lengths to 'Just' the values, and all support values to@@ -355,10 +381,10 @@ -- -- Useful to export a tree with branch lengths but without branch support values -- to Newick format.-measurableToPhyloTree :: Measurable e => Tree e a -> Tree Phylo a+measurableToPhyloTree :: HasLength e => Tree e a -> Tree Phylo a measurableToPhyloTree = first measurableToPhyloLabel -measurableToPhyloLabel :: Measurable e => e -> Phylo+measurableToPhyloLabel :: HasLength e => e -> Phylo measurableToPhyloLabel x = Phylo (Just $ getLen x) Nothing  -- | Set all branch lengths to 'Nothing', and all support values to 'Just' the@@ -366,10 +392,10 @@ -- -- Useful to export a tree with branch support values but without branch lengths -- to Newick format.-supportedToPhyloTree :: Supported e => Tree e a -> Tree Phylo a+supportedToPhyloTree :: HasSupport e => Tree e a -> Tree Phylo a supportedToPhyloTree = first supportedToPhyloLabel -supportedToPhyloLabel :: Supported e => e -> Phylo+supportedToPhyloLabel :: HasSupport e => e -> Phylo supportedToPhyloLabel x = Phylo Nothing (Just $ getSup x)  -- | If root branch length is not available, set it to 0.@@ -431,7 +457,7 @@ instance Semigroup PhyloExplicit where   PhyloExplicit bL sL <> PhyloExplicit bR sR = PhyloExplicit (bL + bR) (min sL sR) -instance Measurable PhyloExplicit where+instance HasLength PhyloExplicit where   getLen = sBrLen   setLen b pl = pl {sBrLen = b}   modLen f (PhyloExplicit l s) = PhyloExplicit (f l) s@@ -441,7 +467,7 @@     where       b' = sBrLen l / 2.0 -instance Supported PhyloExplicit where+instance HasSupport PhyloExplicit where   getSup = sBrSup   setSup s pl = pl {sBrSup = s}   modSup f (PhyloExplicit l s) = PhyloExplicit l (f s)
src/ELynx/Tree/Rooted.hs view
@@ -41,6 +41,21 @@ -- -- In mathematical terms: A 'Tree' is a directed acyclic graph without loops, -- with vertex labels, and with edge labels.+--+-- A short recap of recursive tree traversals:+--+-- - Pre-order: Root first, then sub trees from left to right. Also called depth+--   first.+--+-- - In-order: Only valid for bifurcating trees. Left sub tree first, then root,+--   then right sub tree.+--+-- - Post-order: Sub trees from left to right, then the root. Also called+--   breadth first.+--+-- Here, pre-order traversals are used exclusively, for example, by accessor+-- functions such as 'branches', or 'labels' which is the same as 'toList'.+-- Please let me know, if post-order algorithms are required. module ELynx.Tree.Rooted   ( -- * Data type     Tree (..),@@ -168,7 +183,7 @@ -- -- The 'Monoid' instance of the branch labels determines the default branch -- label when using 'pure'.-instance Monoid e => Applicative (Tree e) where+instance (Semigroup e, Monoid e) => Applicative (Tree e) where   pure lb = Node mempty lb []   ~(Node brF lbF tsF) <*> ~tx@(Node brX lbX tsX) =     Node (brF <> brX) (lbF lbX) (map (lbF <$>) tsX ++ map (<*> tx) tsF)@@ -182,7 +197,10 @@ -- | The 'Semigroup' instance of the branch labels determines how the branches -- are combined. For example, distances can be summed using -- 'Data.Semigroup.Sum'.-instance Monoid e => Monad (Tree e) where+--+-- The 'Monoid' instance of the branch labels determines the default branch+-- label when using 'return'.+instance (Semigroup e, Monoid e) => Monad (Tree e) where   ~(Node br lb ts) >>= f = case f lb of     Node br' lb' ts' -> Node (br <> br') lb' (ts' ++ map (>>= f) ts) @@ -231,11 +249,18 @@ toTreeNodeLabels :: Tree e a -> T.Tree a toTreeNodeLabels (Node _ lb ts) = T.Node lb (map toTreeNodeLabels ts) +-- TODO: Maybe use foldr similar to 'labels'. -- | Get leaves. leaves :: Tree e a -> [a] leaves (Node _ lb []) = [lb] leaves (Node _ _ ts) = concatMap leaves ts +duplicates :: Ord a => [a] -> Bool+duplicates = go S.empty+  where+    go _ [] = False+    go seen (x : xs) = x `S.member` seen || go (S.insert x seen) xs+ -- | Check if a tree has duplicate leaves. duplicateLeaves :: Ord a => Tree e a -> Bool duplicateLeaves = duplicates . leaves@@ -364,9 +389,3 @@ -- Return 'Nothing' if the topologies are different. zipTrees :: Tree e1 a1 -> Tree e2 a2 -> Maybe (Tree (e1, e2) (a1, a2)) zipTrees = zipTreesWith (,) (,)--duplicates :: Ord a => [a] -> Bool-duplicates = go S.empty-  where-    go _ [] = False-    go seen (x : xs) = x `S.member` seen || go (S.insert x seen) xs
src/ELynx/Tree/Simulate/Coalescent.hs view
@@ -16,7 +16,7 @@  import Control.Monad.Primitive import ELynx.Tree.Distribution.CoalescentContinuous-import ELynx.Tree.Measurable+import ELynx.Tree.Length import ELynx.Tree.Rooted import Statistics.Distribution import System.Random.MWC
src/ELynx/Tree/Simulate/PointProcess.hs view
@@ -40,7 +40,7 @@ import ELynx.Tree.Distribution.TimeOfOrigin import ELynx.Tree.Distribution.TimeOfOriginNearCritical import ELynx.Tree.Distribution.Types-import ELynx.Tree.Measurable+import ELynx.Tree.Length import ELynx.Tree.Rooted import qualified Statistics.Distribution as D   ( genContVar,
+ src/ELynx/Tree/Support.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}++-- |+-- Module      :  ELynx.Tree.Support+-- Description :  Labels with support values+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Thu Jun 13 14:06:45 2019.+module ELynx.Tree.Support+  ( -- * Non-negative support value+    Support (fromSupport),+    toSupport,+    toSupportUnsafe,+    HasSupport (..),++    -- * Functions on trees+    normalizeBranchSupport,+    collapse,+  )+where++import Control.DeepSeq+import Data.Aeson+import Data.Bifoldable+import Data.Bifunctor+import Data.List+import Data.Semigroup+import ELynx.Tree.Rooted+import ELynx.Tree.Splittable+import GHC.Generics++-- | Non-negative support value.+--+-- However, non-negativity is only checked with 'toSupport', and negative values+-- can be obtained using the 'Num' and related instances.+--+-- See also the documentation of 'ELynx.Tree.Length.Length'.+newtype Support = Support {fromSupport :: Double}+  deriving (Read, Show, Generic, NFData)+  deriving (Enum, Eq, Floating, Fractional, Num, Ord, Real, RealFloat, RealFrac) via Double+  deriving (Semigroup) via Min Double++instance Splittable Support where+  split = id++instance ToJSON Support++instance FromJSON Support++instance HasSupport Support where+  getSup = id+  setSup = const+  modSup f = f++-- | If negative, call 'error' indicating the calling function name.+toSupport :: String -> Double -> Support+toSupport s x+  | x < 0 = error $ s ++ ": Support is negative: " ++ show x ++ "."+  | otherwise = Support x++-- | Do not check if value is negative.+toSupportUnsafe :: Double -> Support+toSupportUnsafe = Support++-- | A data type with measurable and modifiable values.+class HasSupport e where+  getSup :: e -> Support+  setSup :: Support -> e -> e++  -- For computational efficiency.+  modSup :: (Support -> Support) -> e -> e++-- | Normalize branch support values. The maximum branch support value will be+-- set to 1.0.+normalizeBranchSupport :: HasSupport e => Tree e a -> Tree e a+normalizeBranchSupport t = first (modSup (/ m)) t+  where+    m = bimaximum $ bimap getSup (const 0) t++-- | Collapse branches with support lower than given value.+--+-- The branch and node labels of the collapsed branches are discarded.+collapse :: (Eq e, Eq a, HasSupport e) => Support -> Tree e a -> Tree e a+collapse th tr =+  let tr' = collapse' th tr+   in if tr == tr' then tr else collapse th tr'++-- A leaf has full support.+highP :: HasSupport e => Support -> Tree e a -> Bool+highP _ (Node _ _ []) = True+highP th (Node br _ _) = getSup br >= th++-- See 'collapse'.+collapse' :: HasSupport e => Support -> Tree e a -> Tree e a+collapse' th (Node br lb ts) = Node br lb $ map (collapse' th) (highSupport ++ lowSupportForest)+  where+    (highSupport, lowSupport) = partition (highP th) ts+    lowSupportForest = concatMap forest lowSupport
− src/ELynx/Tree/Supported.hs
@@ -1,106 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingVia #-}---- |--- Module      :  ELynx.Tree.Supported--- Description :  Labels with support values--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Thu Jun 13 14:06:45 2019.-module ELynx.Tree.Supported-  ( -- * Non-negative support value-    Support (fromSupport),-    toSupport,-    toSupportUnsafe,-    Supported (..),--    -- * Functions on trees-    normalizeBranchSupport,-    collapse,-  )-where--import Control.DeepSeq-import Data.Aeson-import Data.Bifoldable-import Data.Bifunctor-import Data.List-import Data.Semigroup-import ELynx.Tree.Rooted-import ELynx.Tree.Splittable-import GHC.Generics---- | Non-negative support value.------ However, non-negativity is only checked with 'toSupport', and negative values--- can be obtained using the 'Num' and related instances.------ See also the documentation of 'ELynx.Tree.Measurable.Length'.-newtype Support = Support {fromSupport :: Double}-  deriving (Read, Show, Generic, NFData)-  deriving (Enum, Eq, Floating, Fractional, Num, Ord, Real, RealFloat, RealFrac) via Double-  deriving (Semigroup) via Min Double--instance Splittable Support where-  split = id--instance ToJSON Support--instance FromJSON Support--instance Supported Support where-  getSup = id-  setSup = const-  modSup f = f---- | Nothing if support is negative.-toSupport :: Double -> Either String Support-toSupport x-  | x < 0 = Left $ "toSupport: Support is negative: " ++ show x ++ "."-  | otherwise = Right $ Support x---- | Do not check if support value is negative.-toSupportUnsafe :: Double -> Support-toSupportUnsafe = Support---- | A data type that supports extraction, setting and modifying of support--- values.-class Supported e where-  getSup :: e -> Support-  setSup :: Support -> e -> e--  -- For computational efficiency.-  modSup :: (Support -> Support) -> e -> e---- | Normalize branch support values. The maximum branch support value will be--- set to 1.0.-normalizeBranchSupport :: Supported e => Tree e a -> Tree e a-normalizeBranchSupport t = first (modSup (/ m)) t-  where-    m = bimaximum $ bimap getSup (const 0) t---- | Collapse branches with support lower than given value.------ The branch and node labels of the collapsed branches are discarded.-collapse :: (Eq e, Eq a, Supported e) => Support -> Tree e a -> Tree e a-collapse th tr =-  let tr' = collapse' th tr-   in if tr == tr' then tr else collapse th tr'---- A leaf has full support.-highP :: Supported e => Support -> Tree e a -> Bool-highP _ (Node _ _ []) = True-highP th (Node br _ _) = getSup br >= th---- See 'collapse'.-collapse' :: Supported e => Support -> Tree e a -> Tree e a-collapse' th (Node br lb ts) = Node br lb $ map (collapse' th) (highSupport ++ lowSupportForest)-  where-    (highSupport, lowSupport) = partition (highP th) ts-    lowSupportForest = concatMap forest lowSupport
src/ELynx/Tree/Zipper.hs view
@@ -18,18 +18,24 @@     toTree,      -- * Movement-    goUp,+    goParent,+    goParentUnsafe,     goRoot,     goLeft,     goRight,     goChild,+    goChildUnsafe,++    -- * Paths     Path,     goPath,+    validPath,     goPathUnsafe,     getSubTreeUnsafe,      -- * Modification     insertTree,+    modifyTree,     insertBranch,     insertLabel,   )@@ -63,8 +69,8 @@ getForest pos = foldl (flip (:)) (current pos : after pos) (before pos)  -- | Go to parent.-goUp :: TreePos e a -> Maybe (TreePos e a)-goUp pos = case parents pos of+goParent :: TreePos e a -> Maybe (TreePos e a)+goParent pos = case parents pos of   (ls, br, lb, rs) : ps ->     Just       Pos@@ -75,9 +81,23 @@         }   [] -> Nothing +-- | Go to parent.+--+-- Call 'error' if no parent is found.+goParentUnsafe :: TreePos e a -> TreePos e a+goParentUnsafe pos = case parents pos of+  (ls, br, lb, rs) : ps ->+    Pos+      { current = Node br lb $ getForest pos,+        before = ls,+        after = rs,+        parents = ps+      }+  [] -> error "goUpUnsafe: No parent found."+ -- | Go to root. goRoot :: TreePos e a -> TreePos e a-goRoot pos = maybe pos goRoot (goUp pos)+goRoot pos = maybe pos goRoot (goParent pos)  -- | Go to left sibling in current forest. goLeft :: TreePos e a -> Maybe (TreePos e a)@@ -122,16 +142,6 @@     where       (ls', rs') = splitAt n ts --- | Path from the root of a tree to the node of the tree.------ The position is specific to a tree topology. If the topology changes, the--- position becomes invalid.-type Path = [Int]---- | Go to node with given path.-goPath :: Path -> TreePos e a -> Maybe (TreePos e a)-goPath pos pth = foldlM (flip goChild) pth pos- -- | Go to child with given index in forest. Call 'error' if child does not -- exist. goChildUnsafe :: Int -> TreePos e a -> TreePos e a@@ -149,11 +159,29 @@     where       (ls', rs') = splitAt n ts +-- | Path from the root of a tree to the node of the tree.+--+-- The position is specific to a tree topology. If the topology changes, the+-- position becomes invalid.+type Path = [Int]++-- | Go to node with given path.+goPath :: Path -> TreePos e a -> Maybe (TreePos e a)+goPath pos pth = foldlM (flip goChild) pth pos++-- | Check if a path is valid in that it leads to a node on a tree.+validPath :: Tree e a -> Path -> Either String Path+validPath t p = case goPath p (fromTree t) of+  Nothing -> Left "validPath: Path is invalid."+  Just _ -> Right p+ -- | Got to node with given path. -- -- Call 'error' if path is invalid. goPathUnsafe :: Path -> TreePos e a -> TreePos e a-goPathUnsafe pos pth = foldl (flip goChildUnsafe) pth pos+goPathUnsafe pos pth =+  {-# SCC "goPathUnsafe" #-}+  foldl (flip goChildUnsafe) pth pos  -- | Get the sub tree at path. --@@ -164,6 +192,12 @@ -- | Insert a new tree into the current focus of the zipper. insertTree :: Tree e a -> TreePos e a -> TreePos e a insertTree t pos = pos {current = t}++-- | Modify the tree at the current focus of the zipper.+modifyTree :: (Tree e a -> Tree e a) -> TreePos e a -> TreePos e a+modifyTree f pos = pos {current = f t}+  where+    t = current pos  -- | Insert a new branch label into the current focus of the zipper. insertBranch :: e -> TreePos e a -> TreePos e a
test/ELynx/Tree/PhylogenySpec.hs view
@@ -25,9 +25,24 @@ import Test.Hspec.QuickCheck import Test.QuickCheck hiding (labels) -simpleTree :: Tree () String-simpleTree = Node () "i" [Node () "j" [Node () "x" [], Node () "y" []], Node () "z" []]+simpleTree1 :: Tree () String+simpleTree1 = Node () "i" [Node () "j" [Node () "x" [], Node () "y" []], Node () "z" []] +simpleTree2 :: Tree () String+simpleTree2 = Node () "i" [Node () "j" [Node () "y" [], Node () "x" []], Node () "z" []]++simpleTree3 :: Tree () String+simpleTree3 = Node () "i" [Node () "j" [Node () "x" [], Node () "z" []], Node () "y" []]++prop_commutative :: (Eq a, Ord a) => Tree () a -> Tree () a -> Bool+prop_commutative t1 t2 = case (s1, s2) of+  (Left _, Left _) -> True+  (Right x1, Right x2) -> x1 == x2+  _ -> False+  where+    s1 = t1 `equal` t2+    s2 = t2 `equal` t1+ simpleSol :: Forest () String simpleSol =   [ Node () "i" [Node () "j" [Node () "x" [], Node () "y" []], Node () "z" []],@@ -91,23 +106,27 @@  -- -- TODO. -- prop_bifurcating_tree---   :: (Ord a, Measurable a, Named a, Supported a) => Tree a -> Bool+--   :: (Ord a, HasLength a, HasName a, HasSupport a) => Tree a -> Bool -- prop_bifurcating_tree t = partitions (resolve t) == empty  prop_roots_total_length :: Tree Length a -> Bool prop_roots_total_length t@(Node _ _ [_, _]) =-  all (\b -> abs (b - l) < 1e-8) $-    map totalBranchLength $-      either error id $-        roots t+  all (\x -> abs (totalBranchLength x - l) < 1e-8) $+    either error id $+      roots t   where     l = totalBranchLength t prop_roots_total_length _ = True  spec :: Spec spec = do-  -- TODO: describe "Resolve"-+  describe "equal" $ do+    it "correctly handles some test cases" $ do+      simpleTree1 `equal` simpleTree2 `shouldBe` Right True+      simpleTree1 `equal` simpleTree3 `shouldBe` Right False+      simpleTree2 `equal` simpleTree3 `shouldBe` Right False+    it "is commutative" $+      property (prop_commutative :: Tree () Int -> Tree () Int -> Bool)   describe "roots" $ do     it "correctly handles leaves and cherries" $ do       let tleaf = Node () 0 [] :: Tree () Int@@ -115,20 +134,19 @@       roots tleaf `shouldSatisfy` isLeft       roots tcherry `shouldBe` Right [tcherry]     it "correctly handles simple trees" $-      either error id (roots simpleTree) `shouldBe` simpleSol+      either error id (roots simpleTree1) `shouldBe` simpleSol     modifyMaxSize (* 100) $       it "returns the correct number of rooted trees for arbitrary trees" $         property (prop_roots :: (Tree () Int -> Bool))-  describe "rootAt" $+  describe "outgroup" $     modifyMaxSize (* 100) $       it "correctly handles simple trees" $         do-          let p = either error id $ bipartition simpleTree-          rootAt p simpleTree `shouldBe` Right simpleTree+          let p = fst $ fromBipartition $ either error id $ bipartition simpleTree1+          outgroup p simpleTree1 `shouldBe` Right simpleTree1           let l = S.singleton "x"-              r = S.fromList ["y", "z"]-              p' = either error id $ bp l r-          either error id (rootAt p' simpleTree) `shouldSatisfy` (`equal` (simpleSol !! 1))+          either error id (outgroup l simpleTree1) `equal` (simpleSol !! 1)+            `shouldBe` Right True   describe "rootsWithBranch" $     modifyMaxSize (* 100) $       it "does not change the tree height" $
test/ELynx/Tree/RootedSpec.hs view
@@ -50,7 +50,7 @@       prune largeTree `shouldBe` largeTree     it "correctly prunes a small example" $       prune smallSubTree `shouldBe` smallSubTreePruned-    it "leaves height constant for Measurable trees" $ do+    it "leaves height constant for trees with branch lengths" $ do       let t' =             either error id $               phyloToLengthTree subSampleLargeTree@@ -62,5 +62,3 @@       dropLeavesWith (const True) smallTree `shouldBe` Nothing     it "returns the correct subtree for a small example" $       dropLeavesWith (== 2) smallTree `shouldBe` Just smallSubTree---- TODO: intersect.
+ test/ELynx/Tree/SupportSpec.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.SupportSpec+-- Description :  Unit tests for ELynx.Tree.SupportSpec+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Fri Aug 21 14:20:09 2020.+module ELynx.Tree.SupportSpec+  ( spec,+  )+where++import ELynx.Tools+import ELynx.Tree+import Test.Hspec++collapseTree :: Tree Phylo Name+collapseTree = parseByteStringWith (oneNewick IqTree) "((a,b),(c,d));"++collapseStarTree :: Tree Phylo Name+collapseStarTree = parseByteStringWith (oneNewick Standard) "(a[1.0],b[1.0],c[1.0],d[1.0])[1.0];"++spec :: Spec+spec = do+  describe "collapse" $ do+    it "creates a star tree for 1.0" $ do+      let t = phyloToSupportTreeUnsafe collapseTree+          s = phyloToSupportTreeUnsafe collapseStarTree+      collapse 0 t `shouldBe` t+      collapse 0.01 t `shouldBe` t+      collapse 0.99 t `shouldBe` t+      collapse 1.0 t `shouldBe` t+      collapse 1.1 t `shouldBe` s
− test/ELynx/Tree/SupportedSpec.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Tree.SupportedSpec--- Description :  Unit tests for ELynx.Tree.SupportedSpec--- Copyright   :  (c) Dominik Schrempf, 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Fri Aug 21 14:20:09 2020.-module ELynx.Tree.SupportedSpec-  ( spec,-  )-where--import ELynx.Tools-import ELynx.Tree-import Test.Hspec--collapseTree :: Tree Phylo Name-collapseTree = parseByteStringWith (oneNewick IqTree) "((a,b),(c,d));"--collapseStarTree :: Tree Phylo Name-collapseStarTree = parseByteStringWith (oneNewick Standard) "(a[1.0],b[1.0],c[1.0],d[1.0])[1.0];"--spec :: Spec-spec = do-  describe "collapse" $ do-    it "creates a star tree for 1.0" $ do-      let t = phyloToSupportTreeUnsafe collapseTree-          s = phyloToSupportTreeUnsafe collapseStarTree-      collapse 0 t `shouldBe` t-      collapse 0.01 t `shouldBe` t-      collapse 0.99 t `shouldBe` t-      collapse 1.0 t `shouldBe` t-      collapse 1.1 t `shouldBe` s