packages feed

elynx-tree 0.3.4 → 0.4.0

raw patch · 82 files changed

+4556/−4554 lines, 82 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- ELynx.Data.Topology.Phylogeny: outgroup :: Ord a => Set a -> Topology a -> Either String (Topology a)
- ELynx.Data.Topology.Phylogeny: rootAt :: Ord a => Bipartition a -> Topology a -> Either String (Forest a)
- ELynx.Data.Topology.Phylogeny: roots :: Topology a -> Either String (Forest a)
- ELynx.Data.Topology.Rooted: Leaf :: a -> Topology a
- ELynx.Data.Topology.Rooted: Node :: Forest a -> Topology a
- ELynx.Data.Topology.Rooted: [forest] :: Topology a -> Forest a
- ELynx.Data.Topology.Rooted: [label] :: Topology a -> a
- ELynx.Data.Topology.Rooted: data Topology a
- ELynx.Data.Topology.Rooted: degree :: Topology a -> Int
- ELynx.Data.Topology.Rooted: dropLeavesWith :: (a -> Bool) -> Topology a -> Maybe (Topology a)
- ELynx.Data.Topology.Rooted: duplicateLeaves :: Ord a => Topology a -> Bool
- ELynx.Data.Topology.Rooted: flatten :: Topology a -> [a]
- ELynx.Data.Topology.Rooted: fromLabeledTree :: Tree e a -> Topology a
- ELynx.Data.Topology.Rooted: fromTree :: Tree a -> Topology a
- ELynx.Data.Topology.Rooted: identify :: Traversable t => t a -> t Int
- ELynx.Data.Topology.Rooted: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (ELynx.Data.Topology.Rooted.Topology a)
- ELynx.Data.Topology.Rooted: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (ELynx.Data.Topology.Rooted.Topology a)
- ELynx.Data.Topology.Rooted: instance Data.Aeson.Types.ToJSON.ToJSON a => Data.Aeson.Types.ToJSON.ToJSON (ELynx.Data.Topology.Rooted.Topology a)
- ELynx.Data.Topology.Rooted: instance Data.Data.Data a => Data.Data.Data (ELynx.Data.Topology.Rooted.Topology a)
- ELynx.Data.Topology.Rooted: instance Data.Foldable.Foldable ELynx.Data.Topology.Rooted.Topology
- ELynx.Data.Topology.Rooted: instance Data.Traversable.Traversable ELynx.Data.Topology.Rooted.Topology
- ELynx.Data.Topology.Rooted: instance GHC.Base.Applicative ELynx.Data.Topology.Rooted.Topology
- ELynx.Data.Topology.Rooted: instance GHC.Base.Functor ELynx.Data.Topology.Rooted.Topology
- ELynx.Data.Topology.Rooted: instance GHC.Base.Monad ELynx.Data.Topology.Rooted.Topology
- ELynx.Data.Topology.Rooted: instance GHC.Classes.Eq a => GHC.Classes.Eq (ELynx.Data.Topology.Rooted.Topology a)
- ELynx.Data.Topology.Rooted: instance GHC.Generics.Generic (ELynx.Data.Topology.Rooted.Topology a)
- ELynx.Data.Topology.Rooted: instance GHC.Read.Read a => GHC.Read.Read (ELynx.Data.Topology.Rooted.Topology a)
- ELynx.Data.Topology.Rooted: instance GHC.Show.Show a => GHC.Show.Show (ELynx.Data.Topology.Rooted.Topology a)
- ELynx.Data.Topology.Rooted: leaves :: Ord a => Topology a -> [a]
- ELynx.Data.Topology.Rooted: prune :: Topology a -> Topology a
- ELynx.Data.Topology.Rooted: type Forest a = NonEmpty (Topology a)
- ELynx.Data.Topology.Rooted: zipTrees :: Topology a1 -> Topology a2 -> Maybe (Topology (a1, a2))
- ELynx.Data.Topology.Rooted: zipTreesWith :: (a1 -> a2 -> a) -> Topology a1 -> Topology a2 -> Maybe (Topology a)
- ELynx.Data.Tree.Bipartition: bipartition :: Ord a => Tree e a -> Either String (Bipartition a)
- ELynx.Data.Tree.Bipartition: bipartitionToBranch :: (Semigroup e, Ord a) => Tree e a -> Either String (Map (Bipartition a) e)
- ELynx.Data.Tree.Bipartition: bipartitions :: Ord a => Tree e a -> Either String (Set (Bipartition a))
- ELynx.Data.Tree.Bipartition: bp :: Ord a => Set a -> Set a -> Either String (Bipartition a)
- ELynx.Data.Tree.Bipartition: bpHuman :: Show a => Bipartition a -> String
- ELynx.Data.Tree.Bipartition: bpUnsafe :: Ord a => Set a -> Set a -> Bipartition a
- ELynx.Data.Tree.Bipartition: data Bipartition a
- ELynx.Data.Tree.Bipartition: getComplementaryLeaves :: Ord a => Set a -> Tree e (Set a) -> [Set a]
- ELynx.Data.Tree.Bipartition: groups :: Tree e a -> Tree e [a]
- ELynx.Data.Tree.Bipartition: instance (GHC.Read.Read a, GHC.Classes.Ord a) => GHC.Read.Read (ELynx.Data.Tree.Bipartition.Bipartition a)
- ELynx.Data.Tree.Bipartition: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (ELynx.Data.Tree.Bipartition.Bipartition a)
- ELynx.Data.Tree.Bipartition: instance GHC.Classes.Eq a => GHC.Classes.Eq (ELynx.Data.Tree.Bipartition.Bipartition a)
- ELynx.Data.Tree.Bipartition: instance GHC.Classes.Ord a => GHC.Classes.Ord (ELynx.Data.Tree.Bipartition.Bipartition a)
- ELynx.Data.Tree.Bipartition: instance GHC.Show.Show a => GHC.Show.Show (ELynx.Data.Tree.Bipartition.Bipartition a)
- ELynx.Data.Tree.Bipartition: toSet :: Ord a => Bipartition a -> Set a
- ELynx.Data.Tree.Distance: branchScore :: (Measurable e1, Measurable e2, Ord a) => Tree e1 a -> Tree e2 a -> Either String Double
- ELynx.Data.Tree.Distance: incompatibleSplits :: (Show a, Ord a) => Tree e1 a -> Tree e2 a -> Either String Int
- ELynx.Data.Tree.Distance: symmetric :: Ord a => Tree e1 a -> Tree e2 a -> Either String Int
- ELynx.Data.Tree.Measurable: applyStem :: Measurable e => (BranchLength -> BranchLength) -> Tree e a -> Tree e a
- ELynx.Data.Tree.Measurable: class Measurable e
- ELynx.Data.Tree.Measurable: distancesOriginLeaves :: Measurable e => Tree e a -> [BranchLength]
- ELynx.Data.Tree.Measurable: getLen :: Measurable e => e -> BranchLength
- ELynx.Data.Tree.Measurable: getStem :: Measurable e => Tree e a -> BranchLength
- ELynx.Data.Tree.Measurable: height :: Measurable e => Tree e a -> BranchLength
- ELynx.Data.Tree.Measurable: instance ELynx.Data.Tree.Measurable.Measurable GHC.Types.Double
- ELynx.Data.Tree.Measurable: makeUltrametric :: Measurable e => Tree e a -> Tree e a
- ELynx.Data.Tree.Measurable: normalizeBranchLengths :: Measurable e => Tree e a -> Tree e a
- ELynx.Data.Tree.Measurable: normalizeHeight :: Measurable e => Tree e a -> Tree e a
- ELynx.Data.Tree.Measurable: rootHeight :: Measurable e => Tree e a -> BranchLength
- ELynx.Data.Tree.Measurable: setLen :: Measurable e => BranchLength -> e -> e
- ELynx.Data.Tree.Measurable: setStem :: Measurable e => BranchLength -> Tree e a -> Tree e a
- ELynx.Data.Tree.Measurable: totalBranchLength :: Measurable e => Tree e a -> BranchLength
- ELynx.Data.Tree.Measurable: type BranchLength = Double
- ELynx.Data.Tree.Measurable: ultrametric :: Measurable e => Tree e a -> Bool
- ELynx.Data.Tree.Named: class Named a
- ELynx.Data.Tree.Named: getName :: Named a => a -> ByteString
- ELynx.Data.Tree.Named: instance ELynx.Data.Tree.Named.Named ()
- ELynx.Data.Tree.Named: instance ELynx.Data.Tree.Named.Named Data.ByteString.Internal.ByteString
- ELynx.Data.Tree.Named: instance ELynx.Data.Tree.Named.Named Data.ByteString.Lazy.Internal.ByteString
- ELynx.Data.Tree.Named: instance ELynx.Data.Tree.Named.Named GHC.Types.Char
- ELynx.Data.Tree.Named: instance ELynx.Data.Tree.Named.Named GHC.Types.Double
- ELynx.Data.Tree.Named: instance ELynx.Data.Tree.Named.Named GHC.Types.Int
- ELynx.Data.Tree.Partition: bpToMp :: Ord a => Bipartition a -> Partition a
- ELynx.Data.Tree.Partition: compatible :: (Show a, Ord a) => Partition a -> Partition a -> Bool
- ELynx.Data.Tree.Partition: data Partition a
- ELynx.Data.Tree.Partition: instance (GHC.Read.Read a, GHC.Classes.Ord a) => GHC.Read.Read (ELynx.Data.Tree.Partition.Partition a)
- ELynx.Data.Tree.Partition: instance GHC.Classes.Eq a => GHC.Classes.Eq (ELynx.Data.Tree.Partition.Partition a)
- ELynx.Data.Tree.Partition: instance GHC.Classes.Ord a => GHC.Classes.Ord (ELynx.Data.Tree.Partition.Partition a)
- ELynx.Data.Tree.Partition: instance GHC.Show.Show a => GHC.Show.Show (ELynx.Data.Tree.Partition.Partition a)
- ELynx.Data.Tree.Partition: mp :: Ord a => [Set a] -> Either String (Partition a)
- ELynx.Data.Tree.Partition: mpHuman :: Show a => Partition a -> String
- ELynx.Data.Tree.Partition: mpUnsafe :: Ord a => [Set a] -> Partition a
- ELynx.Data.Tree.Partition: partition :: Ord a => Tree e a -> Either String (Partition a)
- ELynx.Data.Tree.Partition: partitions :: Ord a => Tree e a -> Either String (Set (Partition a))
- ELynx.Data.Tree.Phylogeny: Length :: BranchLength -> Length
- ELynx.Data.Tree.Phylogeny: Phylo :: Maybe BranchLength -> Maybe BranchSupport -> Phylo
- ELynx.Data.Tree.Phylogeny: PhyloExplicit :: BranchLength -> BranchSupport -> PhyloExplicit
- ELynx.Data.Tree.Phylogeny: Support :: BranchSupport -> Support
- ELynx.Data.Tree.Phylogeny: [brLen] :: Phylo -> Maybe BranchLength
- ELynx.Data.Tree.Phylogeny: [brSup] :: Phylo -> Maybe BranchSupport
- ELynx.Data.Tree.Phylogeny: [fromLength] :: Length -> BranchLength
- ELynx.Data.Tree.Phylogeny: [fromSupport] :: Support -> BranchSupport
- ELynx.Data.Tree.Phylogeny: [sBrLen] :: PhyloExplicit -> BranchLength
- ELynx.Data.Tree.Phylogeny: [sBrSup] :: PhyloExplicit -> BranchSupport
- ELynx.Data.Tree.Phylogeny: bifurcating :: Tree e a -> Bool
- ELynx.Data.Tree.Phylogeny: data Phylo
- ELynx.Data.Tree.Phylogeny: data PhyloExplicit
- ELynx.Data.Tree.Phylogeny: equal :: (Eq e, Eq a) => Tree e a -> Tree e a -> Bool
- ELynx.Data.Tree.Phylogeny: instance Control.DeepSeq.NFData ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance Control.DeepSeq.NFData ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance Control.DeepSeq.NFData ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance ELynx.Data.Tree.Measurable.Measurable ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance ELynx.Data.Tree.Measurable.Measurable ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance ELynx.Data.Tree.Splittable.Splittable ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance ELynx.Data.Tree.Splittable.Splittable ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance ELynx.Data.Tree.Splittable.Splittable ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance ELynx.Data.Tree.Supported.Supported ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance ELynx.Data.Tree.Supported.Supported ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Base.Monoid ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Base.Semigroup ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Base.Semigroup ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance GHC.Base.Semigroup ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance GHC.Base.Semigroup ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Classes.Eq ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Classes.Eq ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance GHC.Classes.Eq ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance GHC.Classes.Eq ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Classes.Ord ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Classes.Ord ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance GHC.Classes.Ord ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance GHC.Classes.Ord ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Float.Floating ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Float.Floating ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Generics.Generic ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Generics.Generic ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance GHC.Generics.Generic ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance GHC.Generics.Generic ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Num.Num ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Num.Num ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Read.Read ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Read.Read ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance GHC.Read.Read ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance GHC.Read.Read ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Real.Fractional ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Real.Fractional ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: instance GHC.Show.Show ELynx.Data.Tree.Phylogeny.Length
- ELynx.Data.Tree.Phylogeny: instance GHC.Show.Show ELynx.Data.Tree.Phylogeny.Phylo
- ELynx.Data.Tree.Phylogeny: instance GHC.Show.Show ELynx.Data.Tree.Phylogeny.PhyloExplicit
- ELynx.Data.Tree.Phylogeny: instance GHC.Show.Show ELynx.Data.Tree.Phylogeny.Support
- ELynx.Data.Tree.Phylogeny: intersect :: (Semigroup e, Eq e, Ord a) => Forest e a -> Either String (Forest e a)
- ELynx.Data.Tree.Phylogeny: measurableToPhyloTree :: Measurable e => Tree e a -> Tree Phylo a
- ELynx.Data.Tree.Phylogeny: midpoint :: (Semigroup e, Splittable e, Measurable e) => Tree e a -> Either String (Tree e a)
- ELynx.Data.Tree.Phylogeny: newtype Length
- ELynx.Data.Tree.Phylogeny: newtype Support
- ELynx.Data.Tree.Phylogeny: outgroup :: (Semigroup e, Splittable e, Ord a) => Set a -> a -> Tree e a -> Either String (Tree e a)
- ELynx.Data.Tree.Phylogeny: phyloToLengthTree :: Tree Phylo a -> Either String (Tree Length a)
- ELynx.Data.Tree.Phylogeny: phyloToSupportTree :: Tree Phylo a -> Either String (Tree Support a)
- ELynx.Data.Tree.Phylogeny: phyloToSupportTreeUnsafe :: Tree Phylo a -> Tree Support a
- ELynx.Data.Tree.Phylogeny: rootAt :: (Semigroup e, Splittable e, Eq a, Ord a) => Bipartition a -> Tree e a -> Either String (Tree e a)
- ELynx.Data.Tree.Phylogeny: roots :: (Semigroup e, Splittable e) => Tree e a -> Either String (Forest e a)
- ELynx.Data.Tree.Phylogeny: supportedToPhyloTree :: Supported e => Tree e a -> Tree Phylo a
- ELynx.Data.Tree.Phylogeny: toExplicitTree :: Tree Phylo a -> Either String (Tree PhyloExplicit a)
- ELynx.Data.Tree.Phylogeny: toPhyloTree :: (Measurable e, Supported e) => Tree e a -> Tree Phylo a
- ELynx.Data.Tree.Rooted: Node :: e -> a -> Forest e a -> Tree e a
- ELynx.Data.Tree.Rooted: [branch] :: Tree e a -> e
- ELynx.Data.Tree.Rooted: [forest] :: Tree e a -> Forest e a
- ELynx.Data.Tree.Rooted: [label] :: Tree e a -> a
- ELynx.Data.Tree.Rooted: branches :: Tree e a -> [e]
- ELynx.Data.Tree.Rooted: data Tree e a
- ELynx.Data.Tree.Rooted: degree :: Tree e a -> Int
- ELynx.Data.Tree.Rooted: dropLeavesWith :: (a -> Bool) -> Tree e a -> Maybe (Tree e a)
- ELynx.Data.Tree.Rooted: dropNodesWith :: (a -> Bool) -> Tree e a -> Maybe (Tree e a)
- ELynx.Data.Tree.Rooted: duplicateLeaves :: Ord a => Tree e a -> Bool
- ELynx.Data.Tree.Rooted: identify :: Traversable t => t a -> t Int
- ELynx.Data.Tree.Rooted: instance (Control.DeepSeq.NFData e, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (ELynx.Data.Tree.Rooted.Tree e a)
- ELynx.Data.Tree.Rooted: instance (Data.Aeson.Types.FromJSON.FromJSON e, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (ELynx.Data.Tree.Rooted.Tree e a)
- ELynx.Data.Tree.Rooted: instance (Data.Aeson.Types.ToJSON.ToJSON e, Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (ELynx.Data.Tree.Rooted.Tree e a)
- ELynx.Data.Tree.Rooted: instance (Data.Data.Data e, Data.Data.Data a) => Data.Data.Data (ELynx.Data.Tree.Rooted.Tree e a)
- ELynx.Data.Tree.Rooted: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (ELynx.Data.Tree.Rooted.Tree e a)
- ELynx.Data.Tree.Rooted: instance (GHC.Read.Read e, GHC.Read.Read a) => GHC.Read.Read (ELynx.Data.Tree.Rooted.Tree e a)
- ELynx.Data.Tree.Rooted: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (ELynx.Data.Tree.Rooted.Tree e a)
- ELynx.Data.Tree.Rooted: instance Control.Comonad.Comonad (ELynx.Data.Tree.Rooted.Tree e)
- ELynx.Data.Tree.Rooted: instance Data.Bifoldable.Bifoldable ELynx.Data.Tree.Rooted.Tree
- ELynx.Data.Tree.Rooted: instance Data.Bifunctor.Bifunctor ELynx.Data.Tree.Rooted.Tree
- ELynx.Data.Tree.Rooted: instance Data.Bitraversable.Bitraversable ELynx.Data.Tree.Rooted.Tree
- ELynx.Data.Tree.Rooted: instance Data.Foldable.Foldable (ELynx.Data.Tree.Rooted.Tree e)
- ELynx.Data.Tree.Rooted: instance Data.Traversable.Traversable (ELynx.Data.Tree.Rooted.Tree e)
- ELynx.Data.Tree.Rooted: instance GHC.Base.Functor (ELynx.Data.Tree.Rooted.Tree e)
- ELynx.Data.Tree.Rooted: instance GHC.Base.Monoid e => Control.Monad.Fix.MonadFix (ELynx.Data.Tree.Rooted.Tree e)
- ELynx.Data.Tree.Rooted: instance GHC.Base.Monoid e => GHC.Base.Applicative (ELynx.Data.Tree.Rooted.Tree e)
- ELynx.Data.Tree.Rooted: instance GHC.Base.Monoid e => GHC.Base.Monad (ELynx.Data.Tree.Rooted.Tree e)
- ELynx.Data.Tree.Rooted: instance GHC.Generics.Generic (ELynx.Data.Tree.Rooted.Tree e a)
- ELynx.Data.Tree.Rooted: labels :: Tree e a -> [a]
- ELynx.Data.Tree.Rooted: leaves :: Tree e a -> [a]
- ELynx.Data.Tree.Rooted: prune :: Semigroup e => Tree e a -> Tree e a
- ELynx.Data.Tree.Rooted: setBranches :: Bitraversable t => [f] -> t e a -> Maybe (t f a)
- ELynx.Data.Tree.Rooted: setLabels :: Traversable t => [b] -> t a -> Maybe (t b)
- ELynx.Data.Tree.Rooted: toTreeBranchLabels :: Tree e a -> Tree e
- ELynx.Data.Tree.Rooted: toTreeNodeLabels :: Tree e a -> Tree a
- ELynx.Data.Tree.Rooted: type Forest e a = [Tree e a]
- ELynx.Data.Tree.Rooted: zipTrees :: Tree e1 a1 -> Tree e2 a2 -> Maybe (Tree (e1, e2) (a1, a2))
- ELynx.Data.Tree.Rooted: zipTreesWith :: (e1 -> e2 -> e) -> (a1 -> a2 -> a) -> Tree e1 a1 -> Tree e2 a2 -> Maybe (Tree e a)
- ELynx.Data.Tree.Splittable: class Splittable e
- ELynx.Data.Tree.Splittable: instance ELynx.Data.Tree.Splittable.Splittable GHC.Types.Double
- ELynx.Data.Tree.Splittable: split :: Splittable e => e -> e
- ELynx.Data.Tree.Supported: class Supported e
- ELynx.Data.Tree.Supported: collapse :: (Eq e, Eq a, Supported e) => BranchSupport -> Tree e a -> Tree e a
- ELynx.Data.Tree.Supported: getSup :: Supported e => e -> BranchSupport
- ELynx.Data.Tree.Supported: normalizeBranchSupport :: Supported e => Tree e a -> Tree e a
- ELynx.Data.Tree.Supported: setSup :: Supported e => BranchSupport -> e -> e
- ELynx.Data.Tree.Supported: type BranchSupport = Double
- ELynx.Data.Tree.Zipper: Pos :: Tree e a -> Forest e a -> Forest e a -> [([Tree e a], e, a, [Tree e a])] -> TreePos e a
- ELynx.Data.Tree.Zipper: [after] :: TreePos e a -> Forest e a
- ELynx.Data.Tree.Zipper: [before] :: TreePos e a -> Forest e a
- ELynx.Data.Tree.Zipper: [current] :: TreePos e a -> Tree e a
- ELynx.Data.Tree.Zipper: [parents] :: TreePos e a -> [([Tree e a], e, a, [Tree e a])]
- ELynx.Data.Tree.Zipper: data TreePos e a
- ELynx.Data.Tree.Zipper: fromTree :: Tree e a -> TreePos e a
- ELynx.Data.Tree.Zipper: goChild :: Int -> TreePos e a -> Maybe (TreePos e a)
- ELynx.Data.Tree.Zipper: goLeft :: TreePos e a -> Maybe (TreePos e a)
- ELynx.Data.Tree.Zipper: goPath :: [Int] -> TreePos e a -> Maybe (TreePos e a)
- ELynx.Data.Tree.Zipper: goRight :: TreePos e a -> Maybe (TreePos e a)
- ELynx.Data.Tree.Zipper: goRoot :: TreePos e a -> TreePos e a
- ELynx.Data.Tree.Zipper: goUp :: TreePos e a -> Maybe (TreePos e a)
- ELynx.Data.Tree.Zipper: insertBranch :: e -> TreePos e a -> TreePos e a
- ELynx.Data.Tree.Zipper: insertLabel :: a -> TreePos e a -> TreePos e a
- ELynx.Data.Tree.Zipper: insertTree :: Tree e a -> TreePos e a -> TreePos e a
- ELynx.Data.Tree.Zipper: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (ELynx.Data.Tree.Zipper.TreePos e a)
- ELynx.Data.Tree.Zipper: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (ELynx.Data.Tree.Zipper.TreePos e a)
- ELynx.Data.Tree.Zipper: toTree :: TreePos e a -> Tree e a
- ELynx.Data.Tree.Zipper: unsafeGoPath :: [Int] -> TreePos e a -> TreePos e a
- ELynx.Distribution.BirthDeath: BDD :: Time -> Rate -> Rate -> BirthDeathDistribution
- ELynx.Distribution.BirthDeath: [bddLa] :: BirthDeathDistribution -> Rate
- ELynx.Distribution.BirthDeath: [bddMu] :: BirthDeathDistribution -> Rate
- ELynx.Distribution.BirthDeath: [bddTOr] :: BirthDeathDistribution -> Time
- ELynx.Distribution.BirthDeath: cumulative :: BirthDeathDistribution -> Time -> Double
- ELynx.Distribution.BirthDeath: data BirthDeathDistribution
- ELynx.Distribution.BirthDeath: density :: BirthDeathDistribution -> Time -> Double
- ELynx.Distribution.BirthDeath: instance Data.Data.Data ELynx.Distribution.BirthDeath.BirthDeathDistribution
- ELynx.Distribution.BirthDeath: instance GHC.Classes.Eq ELynx.Distribution.BirthDeath.BirthDeathDistribution
- ELynx.Distribution.BirthDeath: instance GHC.Generics.Generic ELynx.Distribution.BirthDeath.BirthDeathDistribution
- ELynx.Distribution.BirthDeath: instance Statistics.Distribution.ContDistr ELynx.Distribution.BirthDeath.BirthDeathDistribution
- ELynx.Distribution.BirthDeath: instance Statistics.Distribution.ContGen ELynx.Distribution.BirthDeath.BirthDeathDistribution
- ELynx.Distribution.BirthDeath: instance Statistics.Distribution.Distribution ELynx.Distribution.BirthDeath.BirthDeathDistribution
- ELynx.Distribution.BirthDeath: quantile :: BirthDeathDistribution -> Double -> Time
- ELynx.Distribution.BirthDeathCritical: BDCD :: Time -> Rate -> BirthDeathCriticalDistribution
- ELynx.Distribution.BirthDeathCritical: [bdcdLa] :: BirthDeathCriticalDistribution -> Rate
- ELynx.Distribution.BirthDeathCritical: [bdcdTOr] :: BirthDeathCriticalDistribution -> Time
- ELynx.Distribution.BirthDeathCritical: cumulative :: BirthDeathCriticalDistribution -> Time -> Double
- ELynx.Distribution.BirthDeathCritical: data BirthDeathCriticalDistribution
- ELynx.Distribution.BirthDeathCritical: density :: BirthDeathCriticalDistribution -> Time -> Double
- ELynx.Distribution.BirthDeathCritical: instance Data.Data.Data ELynx.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
- ELynx.Distribution.BirthDeathCritical: instance GHC.Classes.Eq ELynx.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
- ELynx.Distribution.BirthDeathCritical: instance GHC.Generics.Generic ELynx.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
- ELynx.Distribution.BirthDeathCritical: instance Statistics.Distribution.ContDistr ELynx.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
- ELynx.Distribution.BirthDeathCritical: instance Statistics.Distribution.ContGen ELynx.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
- ELynx.Distribution.BirthDeathCritical: instance Statistics.Distribution.Distribution ELynx.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
- ELynx.Distribution.BirthDeathCritical: quantile :: BirthDeathCriticalDistribution -> Double -> Time
- ELynx.Distribution.BirthDeathCriticalNoTime: BDCNTD :: Rate -> BirthDeathCriticalNoTimeDistribution
- ELynx.Distribution.BirthDeathCriticalNoTime: [bdcntdLa] :: BirthDeathCriticalNoTimeDistribution -> Rate
- ELynx.Distribution.BirthDeathCriticalNoTime: cumulative :: BirthDeathCriticalNoTimeDistribution -> Time -> Double
- ELynx.Distribution.BirthDeathCriticalNoTime: density :: BirthDeathCriticalNoTimeDistribution -> Time -> Double
- ELynx.Distribution.BirthDeathCriticalNoTime: instance Data.Data.Data ELynx.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
- ELynx.Distribution.BirthDeathCriticalNoTime: instance GHC.Classes.Eq ELynx.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
- ELynx.Distribution.BirthDeathCriticalNoTime: instance GHC.Generics.Generic ELynx.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
- ELynx.Distribution.BirthDeathCriticalNoTime: instance Statistics.Distribution.ContDistr ELynx.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
- ELynx.Distribution.BirthDeathCriticalNoTime: instance Statistics.Distribution.ContGen ELynx.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
- ELynx.Distribution.BirthDeathCriticalNoTime: instance Statistics.Distribution.Distribution ELynx.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
- ELynx.Distribution.BirthDeathCriticalNoTime: newtype BirthDeathCriticalNoTimeDistribution
- ELynx.Distribution.BirthDeathCriticalNoTime: quantile :: BirthDeathCriticalNoTimeDistribution -> Double -> Time
- ELynx.Distribution.BirthDeathNearlyCritical: BDNCD :: Time -> Rate -> Rate -> BirthDeathNearlyCriticalDistribution
- ELynx.Distribution.BirthDeathNearlyCritical: [bdncdLa] :: BirthDeathNearlyCriticalDistribution -> Rate
- ELynx.Distribution.BirthDeathNearlyCritical: [bdncdMu] :: BirthDeathNearlyCriticalDistribution -> Rate
- ELynx.Distribution.BirthDeathNearlyCritical: [bdncdTOr] :: BirthDeathNearlyCriticalDistribution -> Time
- ELynx.Distribution.BirthDeathNearlyCritical: cumulative :: BirthDeathNearlyCriticalDistribution -> Time -> Double
- ELynx.Distribution.BirthDeathNearlyCritical: data BirthDeathNearlyCriticalDistribution
- ELynx.Distribution.BirthDeathNearlyCritical: density :: BirthDeathNearlyCriticalDistribution -> Time -> Double
- ELynx.Distribution.BirthDeathNearlyCritical: instance Data.Data.Data ELynx.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
- ELynx.Distribution.BirthDeathNearlyCritical: instance GHC.Classes.Eq ELynx.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
- ELynx.Distribution.BirthDeathNearlyCritical: instance GHC.Generics.Generic ELynx.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
- ELynx.Distribution.BirthDeathNearlyCritical: instance Statistics.Distribution.ContDistr ELynx.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
- ELynx.Distribution.BirthDeathNearlyCritical: instance Statistics.Distribution.ContGen ELynx.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
- ELynx.Distribution.BirthDeathNearlyCritical: instance Statistics.Distribution.Distribution ELynx.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
- ELynx.Distribution.BirthDeathNearlyCritical: quantile :: BirthDeathNearlyCriticalDistribution -> Double -> Time
- ELynx.Distribution.CoalescentContinuous: coalescentDistributionCont :: Int -> ExponentialDistribution
- ELynx.Distribution.TimeOfOrigin: TOD :: Int -> Rate -> Rate -> TimeOfOriginDistribution
- ELynx.Distribution.TimeOfOrigin: [todLa] :: TimeOfOriginDistribution -> Rate
- ELynx.Distribution.TimeOfOrigin: [todMu] :: TimeOfOriginDistribution -> Rate
- ELynx.Distribution.TimeOfOrigin: [todTN] :: TimeOfOriginDistribution -> Int
- ELynx.Distribution.TimeOfOrigin: cumulative :: TimeOfOriginDistribution -> Time -> Double
- ELynx.Distribution.TimeOfOrigin: data TimeOfOriginDistribution
- ELynx.Distribution.TimeOfOrigin: density :: TimeOfOriginDistribution -> Time -> Double
- ELynx.Distribution.TimeOfOrigin: instance Data.Data.Data ELynx.Distribution.TimeOfOrigin.TimeOfOriginDistribution
- ELynx.Distribution.TimeOfOrigin: instance GHC.Classes.Eq ELynx.Distribution.TimeOfOrigin.TimeOfOriginDistribution
- ELynx.Distribution.TimeOfOrigin: instance GHC.Generics.Generic ELynx.Distribution.TimeOfOrigin.TimeOfOriginDistribution
- ELynx.Distribution.TimeOfOrigin: instance Statistics.Distribution.ContDistr ELynx.Distribution.TimeOfOrigin.TimeOfOriginDistribution
- ELynx.Distribution.TimeOfOrigin: instance Statistics.Distribution.ContGen ELynx.Distribution.TimeOfOrigin.TimeOfOriginDistribution
- ELynx.Distribution.TimeOfOrigin: instance Statistics.Distribution.Distribution ELynx.Distribution.TimeOfOrigin.TimeOfOriginDistribution
- ELynx.Distribution.TimeOfOrigin: quantile :: TimeOfOriginDistribution -> Double -> Time
- ELynx.Distribution.TimeOfOriginNearCritical: TONCD :: Int -> Rate -> Rate -> TimeOfOriginNearCriticalDistribution
- ELynx.Distribution.TimeOfOriginNearCritical: [todLa] :: TimeOfOriginNearCriticalDistribution -> Rate
- ELynx.Distribution.TimeOfOriginNearCritical: [todMu] :: TimeOfOriginNearCriticalDistribution -> Rate
- ELynx.Distribution.TimeOfOriginNearCritical: [todTN] :: TimeOfOriginNearCriticalDistribution -> Int
- ELynx.Distribution.TimeOfOriginNearCritical: cumulative :: TimeOfOriginNearCriticalDistribution -> Time -> Double
- ELynx.Distribution.TimeOfOriginNearCritical: data TimeOfOriginNearCriticalDistribution
- ELynx.Distribution.TimeOfOriginNearCritical: density :: TimeOfOriginNearCriticalDistribution -> Time -> Double
- ELynx.Distribution.TimeOfOriginNearCritical: instance Data.Data.Data ELynx.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
- ELynx.Distribution.TimeOfOriginNearCritical: instance GHC.Classes.Eq ELynx.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
- ELynx.Distribution.TimeOfOriginNearCritical: instance GHC.Generics.Generic ELynx.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
- ELynx.Distribution.TimeOfOriginNearCritical: instance Statistics.Distribution.ContDistr ELynx.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
- ELynx.Distribution.TimeOfOriginNearCritical: instance Statistics.Distribution.ContGen ELynx.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
- ELynx.Distribution.TimeOfOriginNearCritical: instance Statistics.Distribution.Distribution ELynx.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
- ELynx.Distribution.TimeOfOriginNearCritical: quantile :: TimeOfOriginNearCriticalDistribution -> Double -> Time
- ELynx.Distribution.Types: type Rate = Double
- ELynx.Distribution.Types: type Time = Double
- ELynx.Export.Tree.Newick: toNewick :: Named a => Tree Phylo a -> ByteString
- ELynx.Export.Tree.Newick: toNewickBuilder :: Named a => Tree Phylo a -> Builder
- ELynx.Export.Tree.Nexus: toNexusTrees :: Named a => [(ByteString, Tree Phylo a)] -> ByteString
- ELynx.Import.Tree.Newick: IqTree :: NewickFormat
- ELynx.Import.Tree.Newick: RevBayes :: NewickFormat
- ELynx.Import.Tree.Newick: Standard :: NewickFormat
- ELynx.Import.Tree.Newick: data NewickFormat
- ELynx.Import.Tree.Newick: description :: NewickFormat -> String
- ELynx.Import.Tree.Newick: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Import.Tree.Newick.NewickFormat
- ELynx.Import.Tree.Newick: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Import.Tree.Newick.NewickFormat
- ELynx.Import.Tree.Newick: instance GHC.Classes.Eq ELynx.Import.Tree.Newick.NewickFormat
- ELynx.Import.Tree.Newick: instance GHC.Enum.Bounded ELynx.Import.Tree.Newick.NewickFormat
- ELynx.Import.Tree.Newick: instance GHC.Enum.Enum ELynx.Import.Tree.Newick.NewickFormat
- ELynx.Import.Tree.Newick: instance GHC.Generics.Generic ELynx.Import.Tree.Newick.NewickFormat
- ELynx.Import.Tree.Newick: instance GHC.Read.Read ELynx.Import.Tree.Newick.NewickFormat
- ELynx.Import.Tree.Newick: instance GHC.Show.Show ELynx.Import.Tree.Newick.NewickFormat
- ELynx.Import.Tree.Newick: newick :: NewickFormat -> Parser (Tree Phylo ByteString)
- ELynx.Import.Tree.Newick: oneNewick :: NewickFormat -> Parser (Tree Phylo ByteString)
- ELynx.Import.Tree.Newick: someNewick :: NewickFormat -> Parser (Forest Phylo ByteString)
- ELynx.Import.Tree.Nexus: nexusTrees :: NewickFormat -> Parser [(ByteString, Tree Phylo ByteString)]
- ELynx.Simulate.Coalescent: simulate :: PrimMonad m => Int -> Gen (PrimState m) -> m (Tree Length Int)
- ELynx.Simulate.PointProcess: PointProcess :: ![a] -> ![b] -> !b -> PointProcess a b
- ELynx.Simulate.PointProcess: [origin] :: PointProcess a b -> !b
- ELynx.Simulate.PointProcess: [points] :: PointProcess a b -> ![a]
- ELynx.Simulate.PointProcess: [values] :: PointProcess a b -> ![b]
- ELynx.Simulate.PointProcess: data PointProcess a b
- ELynx.Simulate.PointProcess: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (ELynx.Simulate.PointProcess.PointProcess a b)
- ELynx.Simulate.PointProcess: instance (GHC.Read.Read a, GHC.Read.Read b) => GHC.Read.Read (ELynx.Simulate.PointProcess.PointProcess a b)
- ELynx.Simulate.PointProcess: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (ELynx.Simulate.PointProcess.PointProcess a b)
- ELynx.Simulate.PointProcess: simulate :: PrimMonad m => Int -> TimeSpec -> Rate -> Rate -> Gen (PrimState m) -> m (PointProcess Int Double)
- ELynx.Simulate.PointProcess: simulateNReconstructedTrees :: PrimMonad m => Int -> Int -> TimeSpec -> Rate -> Rate -> Gen (PrimState m) -> m (Forest Length Int)
- ELynx.Simulate.PointProcess: simulateReconstructedTree :: PrimMonad m => Int -> TimeSpec -> Rate -> Rate -> Gen (PrimState m) -> m (Tree Length Int)
- ELynx.Simulate.PointProcess: toReconstructedTree :: a -> PointProcess a Double -> Tree Length a
- ELynx.Simulate.PointProcess: type TimeSpec = Maybe (Time, Bool)
+ 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: Leaf :: a -> Topology a
+ ELynx.Topology.Rooted: Node :: Forest a -> Topology a
+ ELynx.Topology.Rooted: [forest] :: Topology a -> Forest a
+ ELynx.Topology.Rooted: [label] :: Topology a -> a
+ ELynx.Topology.Rooted: data Topology a
+ ELynx.Topology.Rooted: degree :: Topology a -> Int
+ ELynx.Topology.Rooted: dropLeavesWith :: (a -> Bool) -> Topology a -> Maybe (Topology a)
+ ELynx.Topology.Rooted: duplicateLeaves :: Ord a => Topology a -> Bool
+ ELynx.Topology.Rooted: flatten :: Topology a -> [a]
+ ELynx.Topology.Rooted: fromLabeledTree :: Tree e a -> Topology a
+ ELynx.Topology.Rooted: fromTree :: Tree a -> Topology a
+ ELynx.Topology.Rooted: identify :: Traversable t => t a -> t Int
+ ELynx.Topology.Rooted: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (ELynx.Topology.Rooted.Topology a)
+ ELynx.Topology.Rooted: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (ELynx.Topology.Rooted.Topology a)
+ ELynx.Topology.Rooted: instance Data.Aeson.Types.ToJSON.ToJSON a => Data.Aeson.Types.ToJSON.ToJSON (ELynx.Topology.Rooted.Topology a)
+ ELynx.Topology.Rooted: instance Data.Data.Data a => Data.Data.Data (ELynx.Topology.Rooted.Topology a)
+ ELynx.Topology.Rooted: instance Data.Foldable.Foldable ELynx.Topology.Rooted.Topology
+ ELynx.Topology.Rooted: instance Data.Traversable.Traversable ELynx.Topology.Rooted.Topology
+ ELynx.Topology.Rooted: instance GHC.Base.Applicative ELynx.Topology.Rooted.Topology
+ ELynx.Topology.Rooted: instance GHC.Base.Functor ELynx.Topology.Rooted.Topology
+ ELynx.Topology.Rooted: instance GHC.Base.Monad ELynx.Topology.Rooted.Topology
+ ELynx.Topology.Rooted: instance GHC.Classes.Eq a => GHC.Classes.Eq (ELynx.Topology.Rooted.Topology a)
+ ELynx.Topology.Rooted: instance GHC.Generics.Generic (ELynx.Topology.Rooted.Topology a)
+ ELynx.Topology.Rooted: instance GHC.Read.Read a => GHC.Read.Read (ELynx.Topology.Rooted.Topology a)
+ ELynx.Topology.Rooted: instance GHC.Show.Show a => GHC.Show.Show (ELynx.Topology.Rooted.Topology a)
+ ELynx.Topology.Rooted: leaves :: Ord a => Topology a -> [a]
+ ELynx.Topology.Rooted: prune :: Topology a -> Topology a
+ ELynx.Topology.Rooted: type Forest a = NonEmpty (Topology a)
+ ELynx.Topology.Rooted: zipTrees :: Topology a1 -> Topology a2 -> Maybe (Topology (a1, a2))
+ ELynx.Topology.Rooted: zipTreesWith :: (a1 -> a2 -> a) -> Topology a1 -> Topology a2 -> Maybe (Topology a)
+ ELynx.Tree.Bipartition: bipartition :: Ord a => Tree e a -> Either String (Bipartition a)
+ ELynx.Tree.Bipartition: bipartitionToBranch :: (Semigroup e, Ord a) => Tree e a -> Either String (Map (Bipartition a) e)
+ ELynx.Tree.Bipartition: bipartitions :: Ord a => Tree e a -> Either String (Set (Bipartition a))
+ ELynx.Tree.Bipartition: bp :: Ord a => Set a -> Set a -> Either String (Bipartition a)
+ ELynx.Tree.Bipartition: bpHuman :: Show a => Bipartition a -> String
+ ELynx.Tree.Bipartition: bpUnsafe :: Ord a => Set a -> Set a -> Bipartition a
+ ELynx.Tree.Bipartition: data Bipartition a
+ ELynx.Tree.Bipartition: getComplementaryLeaves :: Ord a => Set a -> Tree e (Set a) -> [Set a]
+ ELynx.Tree.Bipartition: groups :: Tree e a -> Tree e [a]
+ ELynx.Tree.Bipartition: instance (GHC.Read.Read a, GHC.Classes.Ord a) => GHC.Read.Read (ELynx.Tree.Bipartition.Bipartition a)
+ ELynx.Tree.Bipartition: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (ELynx.Tree.Bipartition.Bipartition a)
+ ELynx.Tree.Bipartition: instance GHC.Classes.Eq a => GHC.Classes.Eq (ELynx.Tree.Bipartition.Bipartition a)
+ ELynx.Tree.Bipartition: instance GHC.Classes.Ord a => GHC.Classes.Ord (ELynx.Tree.Bipartition.Bipartition a)
+ ELynx.Tree.Bipartition: instance GHC.Show.Show a => GHC.Show.Show (ELynx.Tree.Bipartition.Bipartition a)
+ ELynx.Tree.Bipartition: toSet :: Ord a => Bipartition a -> Set a
+ ELynx.Tree.Distance: branchScore :: (Measurable e1, Measurable e2, Ord a) => Tree e1 a -> Tree e2 a -> Either String Double
+ ELynx.Tree.Distance: incompatibleSplits :: (Show a, Ord a) => Tree e1 a -> Tree e2 a -> Either String Int
+ ELynx.Tree.Distance: symmetric :: Ord a => Tree e1 a -> Tree e2 a -> Either String Int
+ ELynx.Tree.Distribution.BirthDeath: BDD :: Time -> Rate -> Rate -> BirthDeathDistribution
+ ELynx.Tree.Distribution.BirthDeath: [bddLa] :: BirthDeathDistribution -> Rate
+ ELynx.Tree.Distribution.BirthDeath: [bddMu] :: BirthDeathDistribution -> Rate
+ ELynx.Tree.Distribution.BirthDeath: [bddTOr] :: BirthDeathDistribution -> Time
+ ELynx.Tree.Distribution.BirthDeath: cumulative :: BirthDeathDistribution -> Time -> Double
+ ELynx.Tree.Distribution.BirthDeath: data BirthDeathDistribution
+ ELynx.Tree.Distribution.BirthDeath: density :: BirthDeathDistribution -> Time -> Double
+ ELynx.Tree.Distribution.BirthDeath: instance Data.Data.Data ELynx.Tree.Distribution.BirthDeath.BirthDeathDistribution
+ ELynx.Tree.Distribution.BirthDeath: instance GHC.Classes.Eq ELynx.Tree.Distribution.BirthDeath.BirthDeathDistribution
+ ELynx.Tree.Distribution.BirthDeath: instance GHC.Generics.Generic ELynx.Tree.Distribution.BirthDeath.BirthDeathDistribution
+ ELynx.Tree.Distribution.BirthDeath: instance Statistics.Distribution.ContDistr ELynx.Tree.Distribution.BirthDeath.BirthDeathDistribution
+ ELynx.Tree.Distribution.BirthDeath: instance Statistics.Distribution.ContGen ELynx.Tree.Distribution.BirthDeath.BirthDeathDistribution
+ ELynx.Tree.Distribution.BirthDeath: instance Statistics.Distribution.Distribution ELynx.Tree.Distribution.BirthDeath.BirthDeathDistribution
+ ELynx.Tree.Distribution.BirthDeath: quantile :: BirthDeathDistribution -> Double -> Time
+ ELynx.Tree.Distribution.BirthDeathCritical: BDCD :: Time -> Rate -> BirthDeathCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathCritical: [bdcdLa] :: BirthDeathCriticalDistribution -> Rate
+ ELynx.Tree.Distribution.BirthDeathCritical: [bdcdTOr] :: BirthDeathCriticalDistribution -> Time
+ ELynx.Tree.Distribution.BirthDeathCritical: cumulative :: BirthDeathCriticalDistribution -> Time -> Double
+ ELynx.Tree.Distribution.BirthDeathCritical: data BirthDeathCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathCritical: density :: BirthDeathCriticalDistribution -> Time -> Double
+ ELynx.Tree.Distribution.BirthDeathCritical: instance Data.Data.Data ELynx.Tree.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathCritical: instance GHC.Classes.Eq ELynx.Tree.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathCritical: instance GHC.Generics.Generic ELynx.Tree.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathCritical: instance Statistics.Distribution.ContDistr ELynx.Tree.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathCritical: instance Statistics.Distribution.ContGen ELynx.Tree.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathCritical: instance Statistics.Distribution.Distribution ELynx.Tree.Distribution.BirthDeathCritical.BirthDeathCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathCritical: quantile :: BirthDeathCriticalDistribution -> Double -> Time
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: BDCNTD :: Rate -> BirthDeathCriticalNoTimeDistribution
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: [bdcntdLa] :: BirthDeathCriticalNoTimeDistribution -> Rate
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: cumulative :: BirthDeathCriticalNoTimeDistribution -> Time -> Double
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: density :: BirthDeathCriticalNoTimeDistribution -> Time -> Double
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: instance Data.Data.Data ELynx.Tree.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: instance GHC.Classes.Eq ELynx.Tree.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: instance GHC.Generics.Generic ELynx.Tree.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: instance Statistics.Distribution.ContDistr ELynx.Tree.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: instance Statistics.Distribution.ContGen ELynx.Tree.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: instance Statistics.Distribution.Distribution ELynx.Tree.Distribution.BirthDeathCriticalNoTime.BirthDeathCriticalNoTimeDistribution
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: newtype BirthDeathCriticalNoTimeDistribution
+ ELynx.Tree.Distribution.BirthDeathCriticalNoTime: quantile :: BirthDeathCriticalNoTimeDistribution -> Double -> Time
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: BDNCD :: Time -> Rate -> Rate -> BirthDeathNearlyCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: [bdncdLa] :: BirthDeathNearlyCriticalDistribution -> Rate
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: [bdncdMu] :: BirthDeathNearlyCriticalDistribution -> Rate
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: [bdncdTOr] :: BirthDeathNearlyCriticalDistribution -> Time
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: cumulative :: BirthDeathNearlyCriticalDistribution -> Time -> Double
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: data BirthDeathNearlyCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: density :: BirthDeathNearlyCriticalDistribution -> Time -> Double
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: instance Data.Data.Data ELynx.Tree.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: instance GHC.Classes.Eq ELynx.Tree.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: instance GHC.Generics.Generic ELynx.Tree.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: instance Statistics.Distribution.ContDistr ELynx.Tree.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: instance Statistics.Distribution.ContGen ELynx.Tree.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: instance Statistics.Distribution.Distribution ELynx.Tree.Distribution.BirthDeathNearlyCritical.BirthDeathNearlyCriticalDistribution
+ ELynx.Tree.Distribution.BirthDeathNearlyCritical: quantile :: BirthDeathNearlyCriticalDistribution -> Double -> Time
+ ELynx.Tree.Distribution.CoalescentContinuous: coalescentDistributionCont :: Int -> ExponentialDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: TOD :: Int -> Rate -> Rate -> TimeOfOriginDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: [todLa] :: TimeOfOriginDistribution -> Rate
+ ELynx.Tree.Distribution.TimeOfOrigin: [todMu] :: TimeOfOriginDistribution -> Rate
+ ELynx.Tree.Distribution.TimeOfOrigin: [todTN] :: TimeOfOriginDistribution -> Int
+ ELynx.Tree.Distribution.TimeOfOrigin: cumulative :: TimeOfOriginDistribution -> Time -> Double
+ ELynx.Tree.Distribution.TimeOfOrigin: data TimeOfOriginDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: density :: TimeOfOriginDistribution -> Time -> Double
+ ELynx.Tree.Distribution.TimeOfOrigin: instance Data.Data.Data ELynx.Tree.Distribution.TimeOfOrigin.TimeOfOriginDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: instance GHC.Classes.Eq ELynx.Tree.Distribution.TimeOfOrigin.TimeOfOriginDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: instance GHC.Generics.Generic ELynx.Tree.Distribution.TimeOfOrigin.TimeOfOriginDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: instance Statistics.Distribution.ContDistr ELynx.Tree.Distribution.TimeOfOrigin.TimeOfOriginDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: instance Statistics.Distribution.ContGen ELynx.Tree.Distribution.TimeOfOrigin.TimeOfOriginDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: instance Statistics.Distribution.Distribution ELynx.Tree.Distribution.TimeOfOrigin.TimeOfOriginDistribution
+ ELynx.Tree.Distribution.TimeOfOrigin: quantile :: TimeOfOriginDistribution -> Double -> Time
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: TONCD :: Int -> Rate -> Rate -> TimeOfOriginNearCriticalDistribution
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: [todLa] :: TimeOfOriginNearCriticalDistribution -> Rate
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: [todMu] :: TimeOfOriginNearCriticalDistribution -> Rate
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: [todTN] :: TimeOfOriginNearCriticalDistribution -> Int
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: cumulative :: TimeOfOriginNearCriticalDistribution -> Time -> Double
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: data TimeOfOriginNearCriticalDistribution
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: density :: TimeOfOriginNearCriticalDistribution -> Time -> Double
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: instance Data.Data.Data ELynx.Tree.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: instance GHC.Classes.Eq ELynx.Tree.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: instance GHC.Generics.Generic ELynx.Tree.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: instance Statistics.Distribution.ContDistr ELynx.Tree.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: instance Statistics.Distribution.ContGen ELynx.Tree.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: instance Statistics.Distribution.Distribution ELynx.Tree.Distribution.TimeOfOriginNearCritical.TimeOfOriginNearCriticalDistribution
+ ELynx.Tree.Distribution.TimeOfOriginNearCritical: quantile :: TimeOfOriginNearCriticalDistribution -> Double -> Time
+ ELynx.Tree.Distribution.Types: type Rate = Double
+ ELynx.Tree.Distribution.Types: type Time = Double
+ ELynx.Tree.Export.Newick: toNewick :: Named a => Tree Phylo a -> ByteString
+ ELynx.Tree.Export.Newick: toNewickBuilder :: Named a => Tree Phylo a -> Builder
+ ELynx.Tree.Export.Nexus: toNexusTrees :: Named a => [(ByteString, Tree Phylo a)] -> ByteString
+ ELynx.Tree.Import.Newick: IqTree :: NewickFormat
+ ELynx.Tree.Import.Newick: RevBayes :: NewickFormat
+ ELynx.Tree.Import.Newick: Standard :: NewickFormat
+ ELynx.Tree.Import.Newick: data NewickFormat
+ ELynx.Tree.Import.Newick: description :: NewickFormat -> String
+ ELynx.Tree.Import.Newick: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Import.Newick.NewickFormat
+ ELynx.Tree.Import.Newick: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Import.Newick.NewickFormat
+ ELynx.Tree.Import.Newick: instance GHC.Classes.Eq ELynx.Tree.Import.Newick.NewickFormat
+ ELynx.Tree.Import.Newick: instance GHC.Enum.Bounded ELynx.Tree.Import.Newick.NewickFormat
+ ELynx.Tree.Import.Newick: instance GHC.Enum.Enum ELynx.Tree.Import.Newick.NewickFormat
+ ELynx.Tree.Import.Newick: instance GHC.Generics.Generic ELynx.Tree.Import.Newick.NewickFormat
+ ELynx.Tree.Import.Newick: instance GHC.Read.Read ELynx.Tree.Import.Newick.NewickFormat
+ ELynx.Tree.Import.Newick: instance GHC.Show.Show ELynx.Tree.Import.Newick.NewickFormat
+ ELynx.Tree.Import.Newick: newick :: NewickFormat -> Parser (Tree Phylo ByteString)
+ ELynx.Tree.Import.Newick: oneNewick :: NewickFormat -> Parser (Tree Phylo ByteString)
+ ELynx.Tree.Import.Newick: someNewick :: NewickFormat -> Parser (Forest Phylo ByteString)
+ ELynx.Tree.Import.Nexus: nexusTrees :: NewickFormat -> Parser [(ByteString, Tree Phylo ByteString)]
+ ELynx.Tree.Measurable: applyStem :: Measurable e => (BranchLength -> BranchLength) -> Tree e a -> Tree e a
+ ELynx.Tree.Measurable: class Measurable e
+ ELynx.Tree.Measurable: distancesOriginLeaves :: Measurable e => Tree e a -> [BranchLength]
+ ELynx.Tree.Measurable: getLen :: Measurable e => e -> BranchLength
+ ELynx.Tree.Measurable: getStem :: Measurable e => Tree e a -> BranchLength
+ ELynx.Tree.Measurable: height :: Measurable e => Tree e a -> BranchLength
+ ELynx.Tree.Measurable: instance ELynx.Tree.Measurable.Measurable GHC.Types.Double
+ ELynx.Tree.Measurable: makeUltrametric :: Measurable e => Tree e a -> Tree e a
+ 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 -> BranchLength
+ ELynx.Tree.Measurable: setLen :: Measurable e => BranchLength -> e -> e
+ ELynx.Tree.Measurable: setStem :: Measurable e => BranchLength -> Tree e a -> Tree e a
+ ELynx.Tree.Measurable: totalBranchLength :: Measurable e => Tree e a -> BranchLength
+ ELynx.Tree.Measurable: type BranchLength = Double
+ ELynx.Tree.Measurable: ultrametric :: Measurable e => Tree e a -> Bool
+ ELynx.Tree.Named: class Named a
+ ELynx.Tree.Named: getName :: Named a => a -> ByteString
+ 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 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.Partition: bpToMp :: Ord a => Bipartition a -> Partition a
+ ELynx.Tree.Partition: compatible :: (Show a, Ord a) => Partition a -> Partition a -> Bool
+ ELynx.Tree.Partition: data Partition a
+ ELynx.Tree.Partition: instance (GHC.Read.Read a, GHC.Classes.Ord a) => GHC.Read.Read (ELynx.Tree.Partition.Partition a)
+ ELynx.Tree.Partition: instance GHC.Classes.Eq a => GHC.Classes.Eq (ELynx.Tree.Partition.Partition a)
+ ELynx.Tree.Partition: instance GHC.Classes.Ord a => GHC.Classes.Ord (ELynx.Tree.Partition.Partition a)
+ ELynx.Tree.Partition: instance GHC.Show.Show a => GHC.Show.Show (ELynx.Tree.Partition.Partition a)
+ ELynx.Tree.Partition: mp :: Ord a => [Set a] -> Either String (Partition a)
+ ELynx.Tree.Partition: mpHuman :: Show a => Partition a -> String
+ ELynx.Tree.Partition: mpUnsafe :: Ord a => [Set a] -> Partition a
+ ELynx.Tree.Partition: partition :: Ord a => Tree e a -> Either String (Partition a)
+ ELynx.Tree.Partition: partitions :: Ord a => Tree e a -> Either String (Set (Partition a))
+ ELynx.Tree.Phylogeny: Length :: BranchLength -> Length
+ ELynx.Tree.Phylogeny: Phylo :: Maybe BranchLength -> Maybe BranchSupport -> Phylo
+ ELynx.Tree.Phylogeny: PhyloExplicit :: BranchLength -> BranchSupport -> PhyloExplicit
+ ELynx.Tree.Phylogeny: Support :: BranchSupport -> Support
+ ELynx.Tree.Phylogeny: [brLen] :: Phylo -> Maybe BranchLength
+ ELynx.Tree.Phylogeny: [brSup] :: Phylo -> Maybe BranchSupport
+ ELynx.Tree.Phylogeny: [fromLength] :: Length -> BranchLength
+ ELynx.Tree.Phylogeny: [fromSupport] :: Support -> BranchSupport
+ ELynx.Tree.Phylogeny: [sBrLen] :: PhyloExplicit -> BranchLength
+ ELynx.Tree.Phylogeny: [sBrSup] :: PhyloExplicit -> BranchSupport
+ ELynx.Tree.Phylogeny: bifurcating :: Tree e a -> Bool
+ ELynx.Tree.Phylogeny: data Phylo
+ ELynx.Tree.Phylogeny: data PhyloExplicit
+ ELynx.Tree.Phylogeny: equal :: (Eq e, Eq a) => Tree e a -> Tree e a -> Bool
+ ELynx.Tree.Phylogeny: instance Control.DeepSeq.NFData ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance Control.DeepSeq.NFData ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance Control.DeepSeq.NFData ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance Data.Aeson.Types.FromJSON.FromJSON ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance Data.Aeson.Types.ToJSON.ToJSON ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Measurable.Measurable ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Measurable.Measurable ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Splittable.Splittable ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Splittable.Splittable ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Splittable.Splittable ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Supported.Supported ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance ELynx.Tree.Supported.Supported ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Base.Monoid ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Base.Semigroup ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Base.Semigroup ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance GHC.Base.Semigroup ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance GHC.Base.Semigroup ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Classes.Eq ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Classes.Eq ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance GHC.Classes.Eq ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance GHC.Classes.Eq ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Classes.Ord ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Classes.Ord ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance GHC.Classes.Ord ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance GHC.Classes.Ord ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Float.Floating ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Float.Floating ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Generics.Generic ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Generics.Generic ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance GHC.Generics.Generic ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance GHC.Generics.Generic ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Num.Num ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Num.Num ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Read.Read ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Read.Read ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance GHC.Read.Read ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance GHC.Read.Read ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Real.Fractional ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Real.Fractional ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: instance GHC.Show.Show ELynx.Tree.Phylogeny.Length
+ ELynx.Tree.Phylogeny: instance GHC.Show.Show ELynx.Tree.Phylogeny.Phylo
+ ELynx.Tree.Phylogeny: instance GHC.Show.Show ELynx.Tree.Phylogeny.PhyloExplicit
+ ELynx.Tree.Phylogeny: instance GHC.Show.Show ELynx.Tree.Phylogeny.Support
+ ELynx.Tree.Phylogeny: intersect :: (Semigroup e, Eq e, Ord a) => Forest e a -> Either String (Forest e a)
+ ELynx.Tree.Phylogeny: measurableToPhyloTree :: Measurable 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: newtype Length
+ ELynx.Tree.Phylogeny: newtype Support
+ ELynx.Tree.Phylogeny: outgroup :: (Semigroup e, Splittable e, Ord a) => Set a -> a -> Tree e a -> Either String (Tree e a)
+ ELynx.Tree.Phylogeny: phyloToLengthTree :: Tree Phylo a -> Either String (Tree Length a)
+ ELynx.Tree.Phylogeny: phyloToSupportTree :: Tree Phylo a -> Either String (Tree Support a)
+ ELynx.Tree.Phylogeny: phyloToSupportTreeUnsafe :: Tree Phylo a -> Tree Support a
+ ELynx.Tree.Phylogeny: rootAt :: (Semigroup e, Splittable e, Eq a, Ord a) => Bipartition a -> Tree e a -> Either String (Tree e a)
+ ELynx.Tree.Phylogeny: roots :: (Semigroup e, Splittable e) => Tree e a -> Either String (Forest e a)
+ ELynx.Tree.Phylogeny: supportedToPhyloTree :: Supported e => Tree e a -> Tree Phylo a
+ ELynx.Tree.Phylogeny: toExplicitTree :: Tree Phylo a -> Either String (Tree PhyloExplicit a)
+ ELynx.Tree.Phylogeny: toPhyloTree :: (Measurable e, Supported e) => Tree e a -> Tree Phylo a
+ ELynx.Tree.Rooted: Node :: e -> a -> Forest e a -> Tree e a
+ ELynx.Tree.Rooted: [branch] :: Tree e a -> e
+ ELynx.Tree.Rooted: [forest] :: Tree e a -> Forest e a
+ ELynx.Tree.Rooted: [label] :: Tree e a -> a
+ ELynx.Tree.Rooted: branches :: Tree e a -> [e]
+ ELynx.Tree.Rooted: data Tree e a
+ ELynx.Tree.Rooted: degree :: Tree e a -> Int
+ ELynx.Tree.Rooted: dropLeavesWith :: (a -> Bool) -> Tree e a -> Maybe (Tree e a)
+ ELynx.Tree.Rooted: dropNodesWith :: (a -> Bool) -> Tree e a -> Maybe (Tree e a)
+ ELynx.Tree.Rooted: duplicateLeaves :: Ord a => Tree e a -> Bool
+ ELynx.Tree.Rooted: identify :: Traversable t => t a -> t Int
+ ELynx.Tree.Rooted: instance (Control.DeepSeq.NFData e, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (ELynx.Tree.Rooted.Tree e a)
+ ELynx.Tree.Rooted: instance (Data.Aeson.Types.FromJSON.FromJSON e, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (ELynx.Tree.Rooted.Tree e a)
+ ELynx.Tree.Rooted: instance (Data.Aeson.Types.ToJSON.ToJSON e, Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (ELynx.Tree.Rooted.Tree e a)
+ ELynx.Tree.Rooted: instance (Data.Data.Data e, Data.Data.Data a) => Data.Data.Data (ELynx.Tree.Rooted.Tree e a)
+ ELynx.Tree.Rooted: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (ELynx.Tree.Rooted.Tree e a)
+ ELynx.Tree.Rooted: instance (GHC.Read.Read e, GHC.Read.Read a) => GHC.Read.Read (ELynx.Tree.Rooted.Tree e a)
+ ELynx.Tree.Rooted: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (ELynx.Tree.Rooted.Tree e a)
+ ELynx.Tree.Rooted: instance Control.Comonad.Comonad (ELynx.Tree.Rooted.Tree e)
+ ELynx.Tree.Rooted: instance Data.Bifoldable.Bifoldable ELynx.Tree.Rooted.Tree
+ ELynx.Tree.Rooted: instance Data.Bifunctor.Bifunctor ELynx.Tree.Rooted.Tree
+ ELynx.Tree.Rooted: instance Data.Bitraversable.Bitraversable ELynx.Tree.Rooted.Tree
+ ELynx.Tree.Rooted: instance Data.Foldable.Foldable (ELynx.Tree.Rooted.Tree e)
+ ELynx.Tree.Rooted: instance Data.Traversable.Traversable (ELynx.Tree.Rooted.Tree e)
+ ELynx.Tree.Rooted: instance GHC.Base.Functor (ELynx.Tree.Rooted.Tree e)
+ ELynx.Tree.Rooted: instance GHC.Base.Monoid e => Control.Monad.Fix.MonadFix (ELynx.Tree.Rooted.Tree e)
+ 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.Rooted: instance GHC.Generics.Generic (ELynx.Tree.Rooted.Tree e a)
+ ELynx.Tree.Rooted: labels :: Tree e a -> [a]
+ ELynx.Tree.Rooted: leaves :: Tree e a -> [a]
+ ELynx.Tree.Rooted: prune :: Semigroup e => Tree e a -> Tree e a
+ ELynx.Tree.Rooted: setBranches :: Bitraversable t => [f] -> t e a -> Maybe (t f a)
+ ELynx.Tree.Rooted: setLabels :: Traversable t => [b] -> t a -> Maybe (t b)
+ ELynx.Tree.Rooted: toTreeBranchLabels :: Tree e a -> Tree e
+ ELynx.Tree.Rooted: toTreeNodeLabels :: Tree e a -> Tree a
+ ELynx.Tree.Rooted: type Forest e a = [Tree e a]
+ ELynx.Tree.Rooted: zipTrees :: Tree e1 a1 -> Tree e2 a2 -> Maybe (Tree (e1, e2) (a1, a2))
+ ELynx.Tree.Rooted: zipTreesWith :: (e1 -> e2 -> e) -> (a1 -> a2 -> a) -> Tree e1 a1 -> Tree e2 a2 -> Maybe (Tree e a)
+ ELynx.Tree.Simulate.Coalescent: simulate :: PrimMonad m => Int -> Gen (PrimState m) -> m (Tree Length Int)
+ ELynx.Tree.Simulate.PointProcess: PointProcess :: ![a] -> ![b] -> !b -> PointProcess a b
+ ELynx.Tree.Simulate.PointProcess: [origin] :: PointProcess a b -> !b
+ ELynx.Tree.Simulate.PointProcess: [points] :: PointProcess a b -> ![a]
+ ELynx.Tree.Simulate.PointProcess: [values] :: PointProcess a b -> ![b]
+ ELynx.Tree.Simulate.PointProcess: data PointProcess a b
+ ELynx.Tree.Simulate.PointProcess: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (ELynx.Tree.Simulate.PointProcess.PointProcess a b)
+ ELynx.Tree.Simulate.PointProcess: instance (GHC.Read.Read a, GHC.Read.Read b) => GHC.Read.Read (ELynx.Tree.Simulate.PointProcess.PointProcess a b)
+ ELynx.Tree.Simulate.PointProcess: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (ELynx.Tree.Simulate.PointProcess.PointProcess a b)
+ ELynx.Tree.Simulate.PointProcess: simulate :: PrimMonad m => Int -> TimeSpec -> Rate -> Rate -> Gen (PrimState m) -> m (PointProcess Int Double)
+ ELynx.Tree.Simulate.PointProcess: simulateNReconstructedTrees :: PrimMonad m => Int -> Int -> TimeSpec -> Rate -> Rate -> Gen (PrimState m) -> m (Forest Length Int)
+ ELynx.Tree.Simulate.PointProcess: simulateReconstructedTree :: PrimMonad m => Int -> TimeSpec -> Rate -> Rate -> Gen (PrimState m) -> m (Tree Length Int)
+ ELynx.Tree.Simulate.PointProcess: toReconstructedTree :: a -> PointProcess a Double -> Tree Length a
+ ELynx.Tree.Simulate.PointProcess: type TimeSpec = Maybe (Time, Bool)
+ ELynx.Tree.Splittable: class Splittable e
+ ELynx.Tree.Splittable: instance ELynx.Tree.Splittable.Splittable GHC.Types.Double
+ ELynx.Tree.Splittable: split :: Splittable e => e -> e
+ ELynx.Tree.Supported: class Supported e
+ ELynx.Tree.Supported: collapse :: (Eq e, Eq a, Supported e) => BranchSupport -> Tree e a -> Tree e a
+ ELynx.Tree.Supported: getSup :: Supported e => e -> BranchSupport
+ ELynx.Tree.Supported: normalizeBranchSupport :: Supported e => Tree e a -> Tree e a
+ ELynx.Tree.Supported: setSup :: Supported e => BranchSupport -> e -> e
+ ELynx.Tree.Supported: type BranchSupport = Double
+ ELynx.Tree.Zipper: Pos :: Tree e a -> Forest e a -> Forest e a -> [([Tree e a], e, a, [Tree e a])] -> TreePos e a
+ ELynx.Tree.Zipper: [after] :: TreePos e a -> Forest e a
+ ELynx.Tree.Zipper: [before] :: TreePos e a -> Forest e a
+ ELynx.Tree.Zipper: [current] :: TreePos e a -> Tree e a
+ ELynx.Tree.Zipper: [parents] :: TreePos e a -> [([Tree e a], e, a, [Tree e a])]
+ ELynx.Tree.Zipper: data TreePos e a
+ ELynx.Tree.Zipper: fromTree :: Tree e a -> TreePos e a
+ ELynx.Tree.Zipper: goChild :: Int -> TreePos e a -> Maybe (TreePos e a)
+ ELynx.Tree.Zipper: goLeft :: TreePos e a -> Maybe (TreePos e a)
+ ELynx.Tree.Zipper: goPath :: [Int] -> TreePos e a -> Maybe (TreePos e a)
+ ELynx.Tree.Zipper: goRight :: TreePos e a -> Maybe (TreePos e a)
+ ELynx.Tree.Zipper: goRoot :: TreePos e a -> TreePos e a
+ ELynx.Tree.Zipper: goUp :: TreePos e a -> Maybe (TreePos e a)
+ ELynx.Tree.Zipper: insertBranch :: e -> TreePos e a -> TreePos e a
+ ELynx.Tree.Zipper: insertLabel :: a -> TreePos e a -> TreePos e a
+ ELynx.Tree.Zipper: insertTree :: Tree e a -> TreePos e a -> TreePos e a
+ ELynx.Tree.Zipper: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (ELynx.Tree.Zipper.TreePos e a)
+ ELynx.Tree.Zipper: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (ELynx.Tree.Zipper.TreePos e a)
+ ELynx.Tree.Zipper: toTree :: TreePos e a -> Tree e a
+ ELynx.Tree.Zipper: unsafeGoPath :: [Int] -> TreePos e a -> TreePos e a

Files

ChangeLog.md view
@@ -5,6 +5,12 @@ ## Unreleased changes  +## Version 0.4.0++-   Major refactor of `elynx-tree`. All required function can now conveniently+    reexported by `ELynx.Tree`.++ ## Version 0.3.4  -   Improve `slynx examine`; show hamming distance; show constant sites.
README.md view
@@ -2,7 +2,7 @@  # The ELynx Suite -Version: 0.3.4.+Version: 0.4.0. Reproducible evolution made easy.  <p align="center"><img src="https://travis-ci.org/dschrempf/elynx.svg?branch=master"/></p>@@ -90,9 +90,9 @@      slynx --help | head -n -16 -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: slynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME]                   [-f|--force] COMMAND@@ -136,9 +136,9 @@      slynx concatenate --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: slynx concatenate (-a|--alphabet NAME) INPUT-FILE       Concatenate sequences found in input files.@@ -157,9 +157,9 @@      slynx examine --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: slynx examine (-a|--alphabet NAME) INPUT-FILE [--per-site]       Examine sequences. If data is a multi sequence alignment, additionally analyze columns.@@ -179,9 +179,9 @@      slynx filter-rows --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: slynx filter-rows (-a|--alphabet NAME) INPUT-FILE [--longer-than LENGTH]                               [--shorter-than LENGTH] [--standard-characters]@@ -202,9 +202,9 @@      slynx filter-columns --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: slynx filter-columns (-a|--alphabet NAME) INPUT-FILE                                  [--standard-chars DOUBLE]@@ -226,9 +226,9 @@      slynx simulate --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: slynx simulate (-t|--tree-file Name) [-s|--substitution-model MODEL]                            [-m|--mixture-model MODEL] [-e|--edm-file NAME] @@ -305,9 +305,9 @@      slynx sub-sample --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: slynx sub-sample (-a|--alphabet NAME) INPUT-FILE                             (-n|--number-of-sites INT)@@ -335,9 +335,9 @@      slynx translate --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: slynx translate (-a|--alphabet NAME) INPUT-FILE (-r|--reading-frame INT)                            (-u|--universal-code CODE)@@ -360,9 +360,9 @@      tlynx --help | head -n -16 -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: tlynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME]                   [-f|--force] COMMAND@@ -398,9 +398,9 @@      tlynx compare --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: tlynx compare [-n|--normalize] [-b|--bipartitions] [-t|--intersect]                           [-f|--newick-format FORMAT] NAMES@@ -427,9 +427,9 @@      tlynx examine --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: tlynx examine INPUT-FILE [-f|--newick-format FORMAT]       Compute summary statistics of phylogenetic trees.@@ -451,9 +451,9 @@      tlynx simulate --help -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: tlynx simulate (-t|--nTrees INT) (-n|--nLeaves INT) PROCESS                            [-u|--sub-sample DOUBLE] [-s|--summary-statistics] @@ -488,9 +488,9 @@      elynx --help | head -n -16 -    ELynx Suite version 0.3.4.+    ELynx Suite version 0.4.0.     Developed by Dominik Schrempf.-    Compiled on August 21, 2020, at 09:40 am, UTC.+    Compiled on September 4, 2020, at 13:37 pm, UTC.          Usage: elynx COMMAND       Validate and redo past ELynx analyses
bench/Bench.hs view
@@ -13,9 +13,9 @@  import Criterion.Main import qualified Data.ByteString.Char8 as BS-import ELynx.Data.Tree-import ELynx.Import.Tree.Newick import ELynx.Tools+import ELynx.Tree+import ELynx.Tree.Import.Newick  treeFileMany :: FilePath treeFileMany = "data/Many.trees"
elynx-tree.cabal view
@@ -1,6 +1,6 @@ cabal-version:  2.2 name:           elynx-tree-version:        0.3.4+version:        0.4.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@@ -40,33 +40,33 @@  library   exposed-modules:-      ELynx.Data.Topology.Phylogeny-      ELynx.Data.Topology.Rooted-      ELynx.Data.Tree-      ELynx.Data.Tree.Bipartition-      ELynx.Data.Tree.Distance-      ELynx.Data.Tree.Measurable-      ELynx.Data.Tree.Named-      ELynx.Data.Tree.Partition-      ELynx.Data.Tree.Phylogeny-      ELynx.Data.Tree.Rooted-      ELynx.Data.Tree.Splittable-      ELynx.Data.Tree.Supported-      ELynx.Data.Tree.Zipper-      ELynx.Distribution.BirthDeath-      ELynx.Distribution.BirthDeathCritical-      ELynx.Distribution.BirthDeathCriticalNoTime-      ELynx.Distribution.BirthDeathNearlyCritical-      ELynx.Distribution.CoalescentContinuous-      ELynx.Distribution.TimeOfOrigin-      ELynx.Distribution.TimeOfOriginNearCritical-      ELynx.Distribution.Types-      ELynx.Export.Tree.Newick-      ELynx.Export.Tree.Nexus-      ELynx.Import.Tree.Newick-      ELynx.Import.Tree.Nexus-      ELynx.Simulate.Coalescent-      ELynx.Simulate.PointProcess+      ELynx.Topology.Phylogeny+      ELynx.Topology.Rooted+      ELynx.Tree+      ELynx.Tree.Bipartition+      ELynx.Tree.Distance+      ELynx.Tree.Measurable+      ELynx.Tree.Named+      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:@@ -94,18 +94,18 @@   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:-      ELynx.Data.Topology.RootedSpec-      ELynx.Data.Tree.Arbitrary-      ELynx.Data.Tree.BipartitionSpec-      ELynx.Data.Tree.DistanceSpec-      ELynx.Data.Tree.PartitionSpec-      ELynx.Data.Tree.PhylogenySpec-      ELynx.Data.Tree.RootedSpec-      ELynx.Data.Tree.SupportedSpec-      ELynx.Export.Tree.NewickSpec-      ELynx.Export.Tree.NexusSpec-      ELynx.Import.Tree.NewickSpec-      ELynx.Import.Tree.NexusSpec+      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
− src/ELynx/Data/Topology/Phylogeny.hs
@@ -1,115 +0,0 @@--- |--- Module      :  ELynx.Data.Topology.Phylogeny--- Description :  Phylogenetic topologies--- Copyright   :  (c) Dominik Schrempf, 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Sat Jul 18 13:15:49 2020.------ 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.------ 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.------ Also, the uniqueness of the leaves is not ensured by the data type, but has--- to be checked at runtime. Functions relying on the tree to have unique leaves--- 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.------ The bifurcating root of a topology can be changed with 'roots', or 'rootAt'.------ Topologies with multifurcating root nodes can be properly rooted using--- 'outgroup'.-module ELynx.Data.Topology.Phylogeny-  ( outgroup,-    roots,-    rootAt,-  )-where--import Data.Set (Set)-import ELynx.Data.Topology.Rooted-import ELynx.Data.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."---- | For a rooted topology with a bifurcating root node, get all possible rooted--- topologies.-roots :: Topology a -> Either String (Forest a)-roots = undefined---- -- | 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---- | Root a tree at a specific position.-rootAt :: Ord a => Bipartition a -> Topology a -> Either String (Forest a)-rootAt = undefined
− src/ELynx/Data/Topology/Rooted.hs
@@ -1,207 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- |--- Module      :  ELynx.Data.Topology.Rooted--- Description :  Topologies--- Copyright   :  (c) Dominik Schrempf, 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ 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.------ For rooted trees, please see 'ELynx.Data.Tree.Rooted'.------ 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.-module ELynx.Data.Topology.Rooted-  ( -- * Data type-    Topology (..),-    Forest,-    fromTree,-    fromLabeledTree,--    -- * Functions-    degree,-    leaves,-    flatten,-    identify,-    prune,-    dropLeavesWith,-    zipTreesWith,-    zipTrees,-    duplicateLeaves,-  )-where--import Control.Applicative-import Control.DeepSeq-import Control.Monad-import Data.Aeson-import Data.Data-import Data.Foldable-import Data.List.NonEmpty (NonEmpty)-import qualified Data.List.NonEmpty as N-import Data.Maybe-import qualified Data.Set as S-import Data.Traversable-import qualified Data.Tree as T-import qualified ELynx.Data.Tree.Rooted as R-import GHC.Generics--singleton :: NonEmpty a -> Bool-singleton xs = 1 == length (N.take 2 xs)---- | Rooted topologies with leaf labels.-data Topology a-  = Node {forest :: Forest a}-  | Leaf {label :: a}-  deriving (Eq, Read, Show, Data, Generic)---- | A shortcut.-type Forest a = NonEmpty (Topology a)--instance Functor Topology where-  fmap f (Node ts) = Node $ fmap (fmap f) ts-  fmap f (Leaf lb) = Leaf $ f lb--instance Foldable Topology where-  foldMap f (Node ts) = foldMap (foldMap f) ts-  foldMap f (Leaf lb) = f lb--  null _ = False-  {-# INLINE null #-}--  toList = flatten-  {-# INLINE toList #-}--instance Traversable Topology where-  traverse g (Node ts) = Node <$> traverse (traverse g) ts-  traverse g (Leaf lb) = Leaf <$> g lb---- TODO: This type checks, but I doubt the implementation is bug-free.-instance Applicative Topology where-  pure = Leaf--  (Node tsF) <*> tx = Node $ fmap (<*> tx) tsF-  (Leaf lbF) <*> tx = lbF <$> tx--  liftA2 f (Node tsX) ty = Node $ fmap (\tx -> liftA2 f tx ty) tsX-  liftA2 f (Leaf lbX) (Node tsY) = Node $ fmap (f lbX <$>) tsY-  liftA2 f (Leaf lbX) (Leaf lbY) = Leaf $ f lbX lbY--  (Node tsX) *> ty@(Node tsY) = Node $ tsY <> fmap (*> ty) tsX-  (Leaf _) *> (Node tsY) = Node tsY-  _ *> (Leaf lbY) = Leaf lbY--  (Node tsX) <* ty = Node $ fmap (<* ty) tsX-  (Leaf lbX) <* _ = Leaf lbX---- TODO: This type checks, but I doubt the implementation is bug-free.-instance Monad Topology where-  (Node ts) >>= f = Node $ fmap (>>= f) ts-  (Leaf lb) >>= f = case f lb of-    Node ts' -> Node ts'-    Leaf lb' -> Leaf lb'--instance NFData a => NFData (Topology a) where-  rnf (Node ts) = rnf ts-  rnf (Leaf lb) = rnf lb--instance ToJSON a => ToJSON (Topology a)--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.-fromTree :: T.Tree a -> Topology a-fromTree (T.Node lb []) = Leaf lb-fromTree (T.Node _ xs) = Node $ fromTree <$> N.fromList xs---- | Convert a rooted, labeled rose tree to a rooted topology. Branch labels and--- internal node labels are lost.-fromLabeledTree :: R.Tree e a -> Topology a-fromLabeledTree (R.Node _ lb []) = Leaf lb-fromLabeledTree (R.Node _ _ xs) = Node $ fromLabeledTree <$> N.fromList xs---- | 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)---- | Prune degree two nodes.-prune :: Topology a -> Topology a-prune (Node ts)-  | singleton ts = Node $ fmap prune $ forest $ N.head ts-  | otherwise = Node $ fmap prune ts-prune (Leaf lb) = Leaf lb---- | Drop leaves satisfying predicate.------ Degree two nodes may arise.------ Return 'Nothing' if all leaves satisfy the predicate.-dropLeavesWith :: (a -> Bool) -> Topology a -> Maybe (Topology a)-dropLeavesWith p (Leaf lb)-  | p lb = Nothing-  | otherwise = Just $ Leaf lb-dropLeavesWith p (Node ts) =-  if null ts'-    then Nothing-    else -- XXX: May be slow, unnecessary conversion to and from list.-      Just $ Node $ N.fromList ts'-  where-    ts' = catMaybes $ N.toList $ fmap (dropLeavesWith p) ts---- | Zip leaves of two equal topologies.------ Return 'Nothing' if the topologies are different.-zipTreesWith :: (a1 -> a2 -> a) -> Topology a1 -> Topology a2 -> Maybe (Topology a)-zipTreesWith f (Node tsL) (Node tsR) =-  if N.length tsL == N.length tsR-    then -- XXX: May be slow, unnecessary conversion to and from list.-      zipWithM (zipTreesWith f) (N.toList tsL) (N.toList tsR) >>= Just . Node . N.fromList-    else Nothing-zipTreesWith f (Leaf lbL) (Leaf lbR) = Just $ Leaf $ f lbL lbR-zipTreesWith _ _ _ = Nothing---- | Zip leaves of two equal topologies.------ 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/Data/Tree.hs
@@ -1,72 +0,0 @@--- TODO: Topology data type.--- data Topology a = Node (NonEmptySet (Topology a)) | Leaf a---- |--- Module      :  ELynx.Data.Tree--- Description :  Phylogenetic trees--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Sat Mar 21 16:27:20 2020.-module ELynx.Data.Tree-  ( -- * Rooted trees-    module ELynx.Data.Tree.Rooted,-    module ELynx.Data.Tree.Zipper,--    -- * Branch label classes-    module ELynx.Data.Tree.Measurable,-    module ELynx.Data.Tree.Splittable,-    module ELynx.Data.Tree.Supported,--    -- * Node label classes-    module ELynx.Data.Tree.Named,--    -- * Phylogenies-    module ELynx.Data.Tree.Phylogeny,--    -- * Partitions and distances-    module ELynx.Data.Tree.Bipartition,-    module ELynx.Data.Tree.Partition,-    module ELynx.Data.Tree.Distance,-  )-where--import ELynx.Data.Tree.Bipartition-import ELynx.Data.Tree.Distance-import ELynx.Data.Tree.Measurable-import ELynx.Data.Tree.Named-import ELynx.Data.Tree.Partition-import ELynx.Data.Tree.Phylogeny-import ELynx.Data.Tree.Rooted-import ELynx.Data.Tree.Splittable-import ELynx.Data.Tree.Supported-import ELynx.Data.Tree.Zipper---- -- | An evolutionary label has some information about where the corresponding--- -- node is on the tree, and if the node is 'extant', 'extinct', 'internal', or--- -- 'external'. The latter two could also be determined from the tree. This could--- -- be species, genes or individuals; probably more.--- class EvoLabel n where---   extant          :: n -> Bool---   extinct         :: n -> Bool----   internal        :: n -> Bool---   internal n = not $ extant n || extinct n----   external        :: n -> Bool---   external   = not . internal---- -- -- | Glue branches together, so that one new tree emerges. It's root node is--- -- -- new, the sub-forest has to be given (a list of trees).--- -- glue :: (NodeType c)--- --      => PhyloLabel a b c       -- ^ New root node.--- --      -> [PhyloTree a b c]      -- ^ Sub-forest.--- --      -> PhyloTree a b c--- -- glue s@(PhyloLabel _ _ n) ts--- --   | extant n  = error "Root node cannot be of type 'Exant'."--- --   | extinct n = error "Root node cannot be of type 'Extinct'."--- --   | otherwise = Node s ts
− src/ELynx/Data/Tree/Bipartition.hs
@@ -1,198 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}---- |--- Module      :  ELynx.Data.Tree.Bipartition--- Description :  Bipartitions on trees--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Fri Aug 30 15:28:17 2019.------ 'Bipartition's are weird in that--- > Bipartition x y == Bipartition y x--- is True.------ Also,--- > Bipartition x y > Bipartition y x--- is False, even when @x > y@.------ That's why we have to make sure that for--- > Bipartition x y--- we always have @x >= y@.-module ELynx.Data.Tree.Bipartition-  ( groups,--    -- * Data type-    Bipartition (fromBipartition),-    bp,-    bpUnsafe,-    toSet,-    bpHuman,--    -- * Work with 'Bipartition's-    bipartition,-    bipartitions,-    getComplementaryLeaves,-    bipartitionToBranch,-  )-where--import Control.Comonad-import Control.DeepSeq-import Data.List hiding (partition)-import Data.Map (Map)-import qualified Data.Map as M-import Data.Set (Set)-import qualified Data.Set as S-import ELynx.Data.Tree.Rooted---- | Each node of a tree is root of an induced subtree. Set the node labels to--- the leaves of the induced subtrees.-groups :: Tree e a -> Tree e [a]--- I am proud of this awesome 'Comonad' usage here :).-groups = extend leaves---- | Each branch of a tree partitions the leaves of the tree into two subsets,--- or a bipartition.------ The order of the two subsets of a 'Bipartition' is meaningless. We ensure by--- construction that the smaller subset comes first, and hence, that equality--- checks are meaningful.-newtype Bipartition a = Bipartition-  { fromBipartition :: (Set a, Set a)-  }-  deriving (Eq, Ord, Show, Read, NFData)---- | Create a bipartition from two sets.------ Ensure that the smaller set comes first.------ Return 'Left' if one set is empty.-bp :: Ord a => Set a -> Set a -> Either String (Bipartition a)-bp xs ys-  | S.null xs = Left "bp: Left set empty."-  | S.null ys = Left "bp: Right set empty."-  | otherwise = Right $ bpUnsafe xs ys---- | Create a bipartition from two sets.------ Ensure that the smaller set comes first.-bpUnsafe :: Ord a => Set a -> Set a -> Bipartition a-bpUnsafe xs ys = if xs >= ys then Bipartition (xs, ys) else Bipartition (ys, xs)---- | Conversion to a set containing both partitions.-toSet :: Ord a => Bipartition a -> Set a-toSet (Bipartition (x, y)) = S.union x y---- I decided not to provide a human readable show instance because I need the--- following identity to hold:------ > read . show = id------ This identity is met by the derived instance anyways. A more human readable--- instance would most likely violate the identity.---- | Show a bipartition in a human readable format. Use a provided function to--- extract information of interest.-bpHuman :: Show a => Bipartition a -> String-bpHuman (Bipartition (x, y)) = "(" ++ setShow x ++ "|" ++ setShow y ++ ")"---- Show the elements of a set in a human readable format.-setShow :: Show a => Set a -> String-setShow = intercalate "," . map show . S.toList---- -- | Map a function over all elements in the 'Bipartition'.--- bpMap :: Ord b => (a -> b) -> Bipartition a -> Bipartition b--- bpMap f (Bipartition (x, y)) = bp (S.map f x) (S.map f y)---- | For a bifurcating root, get the bipartition induced by the root node.------ Return 'Left' if--- - the root node is not bifurcating;--- - a leave set is empty.-bipartition :: Ord a => Tree e a -> Either String (Bipartition a)-bipartition (Node _ _ [x, y]) = bp (S.fromList $ leaves x) (S.fromList $ leaves y)-bipartition _ = Left "bipartition: Root node is not bifurcating."---- | Get all bipartitions of the tree.------ Return 'Left' if the tree contains duplicate leaves.-bipartitions :: Ord a => Tree e a -> Either String (Set (Bipartition a))-bipartitions t-  | duplicateLeaves t = Left "bipartitions: Tree contains duplicate leaves."-  | otherwise = Right $ bipartitions' S.empty $ S.fromList <$> groups t---- | Report the complementary leaves for each child.-getComplementaryLeaves ::-  (Ord a) =>-  -- Complementary leaves.-  Set a ->-  -- Tree with node labels storing leaves.-  Tree e (Set a) ->-  [Set a]-getComplementaryLeaves p (Node _ _ ts) =-  [ S.unions $ p : take i lvsChildren ++ drop (i + 1) lvsChildren-    | i <- [0 .. (n -1)]-  ]-  where-    n = length ts-    lvsChildren = map label ts---- See 'bipartitions', but do not check if leaves are unique, nor if--- bipartitions are valid.-bipartitions' :: Ord a => Set a -> Tree e (Set a) -> Set (Bipartition a)-bipartitions' p (Node _ p' []) = either (const S.empty) S.singleton $ bp p p'-bipartitions' p t@(Node _ p' ts) =-  S.unions $-    either (const S.empty) S.singleton (bp p p') :-      [bipartitions' c s | (c, s) <- zip cs ts]-  where-    cs = getComplementaryLeaves p t---- TODO: Unrooted? See module comment of Distance.hs.---- | Convert a tree into a 'Map' from each 'Bipartition' to the branch inducing--- the respective 'Bipartition'.------ Since the induced bipartitions of the daughter branches of a bifurcating root--- node are equal, the branches leading to the root have to be combined in this--- case. See http://evolution.genetics.washington.edu/phylip/doc/treedist.html--- and how unrooted trees should be handled.------ Further, branches connected to degree two nodes also induce the same--- bipartitions and have to be combined.------ For combining branches, a binary function is required. This requirement is--- encoded in the 'Semigroup' type class constraint (see 'prune').------ Return 'Left' if the tree contains duplicate leaves.-bipartitionToBranch ::-  (Semigroup e, Ord a) =>-  Tree e a ->-  Either String (Map (Bipartition a) e)-bipartitionToBranch t-  | duplicateLeaves t = Left "bipartitionToBranch: Tree contains duplicate leaves."-  | otherwise = Right $ bipartitionToBranch' S.empty pTree-  where-    pTree = S.fromList <$> groups t---- When calculating the map, branches separated by various degree two nodes have--- to be combined. Hence, not only the complementary leaves, but also the branch--- label itself have to be passed along.-bipartitionToBranch' ::-  (Semigroup e, Ord a) =>-  -- Complementary leaves.-  Set a ->-  -- Partition tree.-  Tree e (Set a) ->-  Map (Bipartition a) e-bipartitionToBranch' p t@(Node b p' ts) =-  M.unionsWith (<>) $-    either (const M.empty) (`M.singleton` b) (bp p p') :-      [bipartitionToBranch' c s | (c, s) <- zip cs ts]-  where-    cs = getComplementaryLeaves p t
− src/ELynx/Data/Tree/Distance.hs
@@ -1,139 +0,0 @@--- |--- Module      :  ELynx.Data.Tree.Distance--- Description :  Compute distances between trees--- 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 17:15:54 2019.------ Various distance functions for phylogenetic trees (and trees with branch--- lengths in general).------ The functions provided in this module return distances for __unrooted__--- trees. See comments of 'symmetric', 'branchScore', and 'bipartitionToBranch',--- as well as the documentation of--- [treedist](http://evolution.genetics.washington.edu/phylip/doc/treedist.html).------ It is a little unfortunate that 'Tree' data type, which represents rooted--- trees, is also used in this module. However, rooted trees are much easier to--- handle. In the future, a separate data type for unrooted trees may be--- introduced. In theory, this is quite straight forward, for example, using--- algebraic graphs. Difficulties may arise because the branches of an unrooted--- tree are undirected.-module ELynx.Data.Tree.Distance-  ( symmetric,-    incompatibleSplits,-    branchScore,-  )-where---- adjacent,--import Data.Bifunctor-import Data.List-import qualified Data.Map as M-import Data.Monoid-import Data.Set (Set)-import qualified Data.Set as S-import ELynx.Data.Tree.Bipartition-import ELynx.Data.Tree.Measurable-import ELynx.Data.Tree.Partition-import ELynx.Data.Tree.Rooted---- Symmetric difference between two 'Set's.-symmetricDifference :: Ord a => Set a -> Set a -> Set a-symmetricDifference xs ys = S.difference xs ys `S.union` S.difference ys xs---- | Symmetric (Robinson-Foulds) distance between two trees.------ Although a rooted tree data type is used, the distance between the unrooted--- trees is returned.------ Return 'Nothing' if the trees contain different leaves.------ XXX: Comparing a list of trees may recompute bipartitions.-symmetric :: Ord a => Tree e1 a -> Tree e2 a -> Either String Int-symmetric t1 t2-  | S.fromList (leaves t1) /= S.fromList (leaves t2) = Left "symmetric: Trees contain different leaves."-  | otherwise = do-    bps1 <- bipartitions t1-    bps2 <- bipartitions t2-    return $ length $ symmetricDifference bps1 bps2--countIncompatibilities :: (Show a, Ord a) => Set (Bipartition a) -> Set (Partition a) -> Int-countIncompatibilities bs ms =-  foldl' (\i b -> if any (compatible $ bpToMp b) ms then i else i + 1) 0 bs---- | Number of incompatible splits.------ Similar to 'symmetric' but all bipartitions induced by multifurcations are--- considered. For a detailed description of how the distance is calculated, see--- 'ELynx.Data.Tree.Bipartition.bipartitionCompatible'.------ A multifurcation on a tree may (but not necessarily does) represent missing--- information about the order of bifurcations. In this case, it is interesting--- to get a set of compatible bifurcations of the tree. For example, the star tree------ > (A,B,C,D);------ induces the following bipartitions:------ > A|BCD--- > B|ACD--- > C|ABD--- > D|ABC------ However, the tree is additionally compatible with the following hidden--- bipartitions:------ > AB|CD--- > AC|BD--- > AD|BC------ For an explanation of how compatibility of partitions is checked, see--- 'compatible'. Before using 'compatible', bipartitions are simply converted to--- partitions with two subsets.------ A bipartition is incompatible with a tree if it is incompatible with all--- induced multifurcations of the tree.------ XXX: Comparing a list of trees with this function recomputes bipartitions.-incompatibleSplits :: (Show a, Ord a) => Tree e1 a -> Tree e2 a -> Either String Int-incompatibleSplits t1 t2-  | S.fromList (leaves t1) /= S.fromList (leaves t2) =-    Left "incompatibleSplits: Trees do not have equal leaf sets."-  | otherwise = do-    -- Bipartitions.-    bs1 <- bipartitions t1-    bs2 <- bipartitions t2-    -- traceShowM $ "bs1" ++ show (S.map bpHuman bs1)-    -- traceShowM $ "bs2" ++ show (S.map bpHuman bs2)-    let -- Putative incompatible bipartitions of trees one and two, respectively.-        putIncBs1 = bs1 S.\\ bs2-        putIncBs2 = bs2 S.\\ bs1-    -- Partitions.-    ms1 <- partitions t1-    ms2 <- partitions t2-    -- traceShowM $ "putIncBs1 " ++ show (S.map bpHuman putIncBs1)-    -- traceShowM $ "putIncBs2 " ++ show (S.map bpHuman putIncBs2)-    return $ countIncompatibilities putIncBs1 ms2 + countIncompatibilities putIncBs2 ms1---- | Compute branch score distance between two trees.------ Although a rooted tree data type is used, the distance between the unrooted--- trees is returned.------ XXX: Comparing a list of trees with this function recomputes bipartitions.-branchScore :: (Measurable e1, Measurable 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-    bpToBr1 <- bipartitionToBranch $ first (Sum . getLen) t1-    bpToBr2 <- bipartitionToBranch $ first (Sum . getLen) t2-    let dBs = M.unionWith (-) bpToBr1 bpToBr2-        dsSquared = foldl' (\acc e -> acc + e * e) 0 dBs-    return $ sqrt $ getSum dsSquared
− src/ELynx/Data/Tree/Measurable.hs
@@ -1,124 +0,0 @@--- |--- Module      :  ELynx.Data.Tree.Measurable--- Description :  Measurable branch 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 branch lengths is not (yet) ensured. To ensure--- non-negativity, a newtype wrapper could be used, but this would be a major--- refactor.-module ELynx.Data.Tree.Measurable-  ( BranchLength,-    Measurable (..),-    applyStem,-    getStem,-    setStem,-    height,-    rootHeight,-    distancesOriginLeaves,-    totalBranchLength,-    normalizeBranchLengths,-    normalizeHeight,-    ultrametric,-    makeUltrametric,-  )-where--import Data.Bifoldable-import Data.Bifunctor-import ELynx.Data.Tree.Rooted---- | Branch length.-type BranchLength = Double---- | A branch label with measurable and modifiable branch length.-class Measurable e where-  -- | Length of attached branch.-  getLen :: e -> BranchLength--  -- | Set attached branch length.-  setLen :: BranchLength -> e -> e--instance Measurable Double where-  getLen = id-  setLen = const---- Apply a function to a branch support label.-apply :: Measurable e => (BranchLength -> BranchLength) -> e -> e-apply f l = setLen (f s) l where s = getLen l---- | Lengthen the stem of a tree.-applyStem :: Measurable e => (BranchLength -> BranchLength) -> Tree e a -> Tree e a-applyStem f t = t {branch = apply f b}-  where-    b = branch t---- | Get the length of the stem of a tree.-getStem :: Measurable e => Tree e a -> BranchLength-getStem (Node br _ _) = getLen br---- | Set the length of the stem of a tree.-setStem :: Measurable e => BranchLength -> Tree e a -> Tree e a-setStem x = applyStem (const x)---- | The maximum distance between origin and leaves.------ The height includes the length of the stem.-height :: Measurable e => Tree e a -> BranchLength-height = maximum . distancesOriginLeaves---- | The maximum distance between root node and leaves.-rootHeight :: Measurable e => Tree e a -> BranchLength-rootHeight (Node _ _ []) = 0-rootHeight t = maximum $ concatMap distancesOriginLeaves (forest t)---- | Distances from the origin of a tree to the leaves.------ The distances include the length of the stem.-distancesOriginLeaves :: Measurable e => Tree e a -> [BranchLength]-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 -> BranchLength-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 (apply (/ 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 (apply (/ h)) t-  where-    h = height t--eps :: Double-eps = 1e-12--allNearlyEqual :: [Double] -> Bool-allNearlyEqual [] = True-allNearlyEqual xs = all (\y -> eps > abs (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 => BranchLength -> Tree e a -> Tree e a-    go h' (Node br lb []) = let dh = h - h' - getLen br in Node (apply (+ dh) br) lb []-    go h' (Node br lb ts) = let h'' = h' + getLen br in Node br lb $ map (go h'') ts
− src/ELynx/Data/Tree/Named.hs
@@ -1,43 +0,0 @@--- |--- Module      :  ELynx.Data.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.Data.Tree.Named-  ( Named (..),-  )-where--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---- | Data types with names.-class Named a where-  -- Use lazy byte strings because Newick strings are built using chunks.-  getName :: a -> BL.ByteString--instance Named () where-  getName = const BL.empty--instance Named Int where-  getName = BB.toLazyByteString . BB.intDec--instance Named Double where-  getName = BL.fromStrict . toShortest--instance Named Char where-  getName = BB.toLazyByteString . BB.char8--instance Named BL.ByteString where-  getName = id--instance Named BS.ByteString where-  getName = BL.fromStrict
− src/ELynx/Data/Tree/Partition.hs
@@ -1,137 +0,0 @@--- |--- Module      :  ELynx.Data.Tree.Partition--- Description :  Partitions on rose trees--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Thu Dec 12 12:58:49 2019.------ A multifurcation induces a 'Partition', similar to branches inducing--- 'ELynx.Data.Tree.Bipartition's.-module ELynx.Data.Tree.Partition-  ( -- * Data type-    Partition (fromPartition),-    mp,-    mpUnsafe,-    bpToMp,-    mpHuman,--    -- * Work with 'Partition's-    partition,-    partitions,-    compatible,-  )-where--import Data.List hiding (partition)-import Data.Set (Set)-import qualified Data.Set as S-import ELynx.Data.Tree.Bipartition-import ELynx.Data.Tree.Rooted---- | Each branch of a tree partitions the leaves of the tree into two subsets--- (see 'ELynx.Data.Tree.Bipartition'). In a similar way, each internal node--- (excluding the root node) partitions the leaves into three (or more) subsets--- which is called 'Partition'. If the tree is multifurcating, and a--- specific node has more than two children, the number of subsets induced by--- this node is larger than three. Partitions are interesting in that we--- can use them for calculating incompatible splits, see--- 'ELynx.Data.Tree.Distance'.------ The order of the subsets of a 'Partition' is meaningless. We ensure by--- construction that the subsets are ordered, and hence, that equality checks--- are meaningful.-newtype Partition a = Partition-  { fromPartition :: Set (Set a)-  }-  deriving (Eq, Ord, Show, Read)---- TODO: Check that list is not empty after filtering.---- TODO: Rename these functions; don't use 'multi'.---- | Create a partition.-mp :: Ord a => [Set a] -> Either String (Partition a)-mp xs = case filter (not . S.null) xs of-  [] -> Left "mp: Empty list."-  xs' -> Right $ mpUnsafe xs'---- | Create a partition.-mpUnsafe :: Ord a => [Set a] -> Partition a-mpUnsafe xs = Partition (S.fromList xs)---- | Convert a bipartition to a partition.-bpToMp :: Ord a => Bipartition a -> Partition a-bpToMp = mpUnsafe . tupleToList . fromBipartition-  where-    -- Be careful with tuples, because 'toList' does something very weird. It only-    -- takes the second element of the tuple!-    ---    -- toList :: Foldable t => t a -> [a]-    tupleToList (x, y) = [x, y]---- | Show a partition in a human readable form. Use a provided function to--- extract the valuable information.-mpHuman :: Show a => Partition a -> String-mpHuman (Partition xs) =-  "(" ++ intercalate "|" (map setShow (S.toList xs)) ++ ")"---- Show the elements of a set in a human readable format.-setShow :: Show a => Set a -> String-setShow = intercalate "," . map show . S.toList---- | Get partition defined by the root of the tree.------ Return 'Left' if:--- - the tree is a leaf;--- - the tree contains duplicate leaves.-partition :: Ord a => Tree e a -> Either String (Partition a)-partition (Node _ _ []) = Left "partition: Encountered a leaf."-partition t@(Node _ _ ts)-  | duplicateLeaves t = Left "partition: Tree contains duplicate leaves."-  | otherwise = mp $ map (S.fromList . leaves) ts---- | Get all 'Partition's of a tree.------ Return 'Left' if tree contains duplicate leaves.-partitions :: Ord a => Tree e a -> Either String (Set (Partition a))-partitions t-  | duplicateLeaves t = Left "partitions: Tree contains duplicate leaves."-  | otherwise = Right $ partitions' S.empty $ S.fromList <$> groups t---- See 'partitions', but do not check if leaves are unique.-partitions' :: Ord a => Set a -> Tree e (Set a) -> Set (Partition a)-partitions' _ (Node _ _ []) = S.empty-partitions' p t@(Node _ _ ts) =-  S.unions $-    either (const S.empty) S.singleton (mp (p : map label ts)) :-    zipWith partitions' cs ts-  where-    cs = getComplementaryLeaves p t---- | 'Partition's are compatible if they do not contain conflicting--- information. This function checks if two partitions are compatible with--- each other. Thereby, a variation of the following algorithm is used:------ @--- mp1 `compatible` mp2--- for set1 in mp1:---   for set2 in mp2:---     if set1 `S.isSubSetOf` set2:---       remove set1 from mp1---     if set2 `S.isSubSetOf` set1:---       remove set2 from mp2--- if either mp2 or mp2 is empty, they are compatible--- @-compatible :: (Show a, Ord a) => Partition a -> Partition a -> Bool-compatible l r = S.null (S.filter (`remove` rs) ls) || S.null (S.filter (`remove` ls) rs)-  where-    ls = fromPartition l-    rs = fromPartition r--remove :: Ord a => Set a -> Set (Set a) -> Bool-remove s = not . any (s `S.isSubsetOf`)
− src/ELynx/Data/Tree/Phylogeny.hs
@@ -1,487 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingVia #-}---- |--- Module      :  ELynx.Data.Tree.Phylogeny--- Description :  Phylogenetic trees--- 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 16:08:54 2019.------ A phylogeny is a 'Tree' with unique leaf labels, and the order of the trees--- in the sub-forest is considered to be meaningless.------ Internally, however, the underlying 'Tree' data structure stores the--- sub-forest as a list, which has a specific order. Hence, we have to do some--- tricks when comparing trees, and tree comparison is slow.------ Also, the uniqueness of the leaves is not ensured by the data type, but has--- to be checked at runtime. Functions relying on the tree to have unique leaves--- do perform this check, and return 'Left' with an error message, if the tree--- has duplicate leaves.------ Note: 'Tree's are rooted.------ 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.------ The bifurcating root of a tree can be changed with 'rootAt' or 'midpoint'; a--- list of all rooted trees is returned by 'roots'.------ Trees with multifurcating root nodes can be rooted using 'outgroup'.-module ELynx.Data.Tree.Phylogeny-  ( -- * Functions-    equal,-    intersect,-    bifurcating,-    outgroup,-    midpoint,-    roots,-    rootAt,--    -- * Branch labels-    Phylo (..),-    toPhyloTree,-    measurableToPhyloTree,-    supportedToPhyloTree,-    Length (..),-    phyloToLengthTree,-    Support (..),-    phyloToSupportTree,-    phyloToSupportTreeUnsafe,-    PhyloExplicit (..),-    toExplicitTree,-  )-where--import Control.DeepSeq-import Data.Aeson-import Data.Bifoldable-import Data.Bifunctor-import Data.Bitraversable-import Data.List hiding (intersect)-import Data.Maybe-import Data.Monoid-import Data.Semigroup-import Data.Set (Set)-import qualified Data.Set as S-import ELynx.Data.Tree.Bipartition-import ELynx.Data.Tree.Measurable-import ELynx.Data.Tree.Rooted-import ELynx.Data.Tree.Splittable-import ELynx.Data.Tree.Supported-import GHC.Generics---- | 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) =-  (brL == brR)-    && (lbL == lbR)-    && (length tsL == length tsR)-    && all (`elem` tsR) tsL---- | Compute the intersection of trees.------ The intersections are the largest subtrees sharing the same leaf set.------ 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)-intersect ts-  | S.null lvsCommon = Left "intersect: Intersection of leaves is empty."-  | otherwise = case sequence [dropLeavesWith (predicate ls) t | (ls, t) <- zip leavesToDrop ts] of-    Nothing -> Left "intersect: A tree is empty."-    Just ts' -> Right ts'-  where-    -- Leaf sets.-    lvss = map (S.fromList . leaves) ts-    -- Common leaf set.-    lvsCommon = foldl1' S.intersection lvss-    -- Leaves to drop for each tree in the forest.-    leavesToDrop = map (S.\\ lvsCommon) lvss-    -- Predicate.-    predicate lvsToDr l = l `S.member` lvsToDr---- | Check if a tree is bifurcating.------ A Bifurcating tree only contains degree one (leaves) and degree three nodes--- (internal bifurcating nodes).-bifurcating :: Tree e a -> Bool-bifurcating (Node _ _ []) = True-bifurcating (Node _ _ [x, y]) = bifurcating x && bifurcating y-bifurcating _ = False---- I believe that manual treatment with 'outgroup' is preferable.---- -- | Remove multifurcations.--- ----- -- A caterpillar like bifurcating structure is used to resolve all--- -- multifurcations on a tree.--- ----- -- Multifurcating nodes are copied and branches are 'split'.--- resolve :: Splittable e => Tree e a -> Tree e a--- resolve t@(Node _ _ []) = t--- resolve (Node br lb [x]) = Node br lb [resolve x]--- resolve (Node br lb [x, y]) = Node br lb $ map resolve [x, y]--- 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.------ A bifurcating root node with the provided label is introduced. The affected--- branch is 'split'.------ Note, the degree of the former root node is decreased by one.------ If the root node is bifurcating, use 'rootAt'.------ Return 'Left' if--- - the tree has duplicate leaves;--- - the root node is not multifurcating;--- - 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'-  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)]---- The 'midpoint' algorithm is pretty stupid because it calculates all rooted--- trees and then finds the one minimizing the difference between the heights of--- the left and right sub tree. Actually, one just needs to move left or right,--- with the aim to minimize the height difference between the left and right sub--- tree.---- | 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 (Node _ _ []) = Left "midpoint: Root node is a leaf."-midpoint (Node _ _ [_]) = Left "midpoint: Root node has degree two."-midpoint t@(Node _ _ [_, _]) = getMidpoint <$> roots t-midpoint _ = Left "midpoint: Root node is multifurcating."--findMinIndex :: Ord a => [a] -> Int-findMinIndex (x : xs) = go (0, x) 1 xs-  where-    go (i, _) _ [] = i-    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 ts = case t of-  (Node br lb [l, r]) ->-    let hl = height l-        hr = height r-        dh = (hl - hr) / 2-     in Node br lb [applyStem (subtract dh) l, applyStem (+ dh) r]-  -- Explicitly use 'error' here, because roots is supposed to return trees with-  -- bifurcating root nodes.-  _ -> error "getMidpoint: Root node is not bifurcating."-  where-    dhs = map getDeltaHeight ts-    i = findMinIndex dhs-    t = ts !! i---- 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 -> Double-getDeltaHeight (Node _ _ [l, r]) = abs $ height l - height r--- Explicitly use 'error' here, because roots is supposed to return trees with--- bifurcating root nodes.-getDeltaHeight _ = error "getDeltaHeight: Root node is not bifurcating."---- | 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 degree--- of the root node. Hence, a bifurcating root is required. 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.------ Return 'Left' if the root node is not 'bifurcating'.-roots :: (Semigroup e, Splittable e) => Tree e a -> Either String (Forest e a)-roots (Node _ _ []) = Left "roots: Root node is a leaf."-roots (Node _ _ [_]) = Left "roots: Root node has degree two."-roots t@(Node b c [tL, tR]) = Right $ t : descend b c tR tL ++ descend b c tL tR-roots _ = Left "roots: Root node is multifurcating."--complementaryForests :: Tree e a -> Forest e a -> [Forest e a]-complementaryForests t ts = [t : take i ts ++ drop (i + 1) ts | i <- [0 .. (n -1)]]-  where-    n = length ts---- From the bifurcating root, descend into one of the two pits.------ descend splitFunction rootBranch rootLabel complementaryTree downwardsTree-descend :: (Semigroup e, Splittable e) => e -> a -> Tree e a -> Tree e a -> Forest e a-descend _ _ _ (Node _ _ []) = []-descend brR lbR tC (Node brD lbD tsD) =-  [ Node brR lbR [Node (split brDd) lbD f, Node (split brDd) lbDd tsDd]-    | (Node brDd lbDd tsDd, f) <- zip tsD cfs-  ]-    ++ concat-      [ descend brR lbR (Node (split brDd) lbD f) (Node (split brDd) lbDd tsDd)-        | (Node brDd lbDd tsDd, f) <- zip tsD cfs-      ]-  where-    brC' = branch tC <> brD-    tC' = tC {branch = brC'}-    cfs = complementaryForests tC' tsD---- | 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' and 'outgroup').------ Connect branches according to the provided 'Semigroup' instance.------ Upon insertion of the root, split the affected branch according to the--- 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) =>-  Bipartition a ->-  Tree e a ->-  Either String (Tree e a)-rootAt b t-  -- Tree is checked for being bifurcating in 'roots'.-  ---  -- Do not use 'duplicateLeaves' here, because we also need to compare the leaf-  -- set with the bipartition.-  | length lvLst /= S.size lvSet = Left "rootAt: Tree has duplicate leaves."-  | toSet b /= lvSet = Left "rootAt: Bipartition does not match leaves of tree."-  | otherwise = rootAt' b t-  where-    lvLst = leaves t-    lvSet = S.fromList $ leaves t---- Assume the leaves of the tree are unique.-rootAt' ::-  (Semigroup e, Splittable e, Ord a) =>-  Bipartition a ->-  Tree e a ->-  Either String (Tree e a)-rootAt' b t = do-  ts <- roots t-  case find (\x -> Right b == bipartition x) ts of-    Nothing -> Left "rootAt': Bipartition not found on tree."-    Just t' -> Right t'---- | Branch label for phylogenetic trees.------ Branches may have a length and a support value.-data Phylo = Phylo-  { brLen :: Maybe BranchLength,-    brSup :: Maybe BranchSupport-  }-  deriving (Read, Show, Eq, Ord, Generic, NFData)--instance Semigroup Phylo where-  Phylo mBL mSL <> Phylo mBR mSR =-    Phylo-      (getSum <$> (Sum <$> mBL) <> (Sum <$> mBR))-      (getMin <$> (Min <$> mSL) <> (Min <$> mSR))--instance ToJSON Phylo--instance FromJSON Phylo---- | Set all branch length 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 = first toPhyloLabel--toPhyloLabel :: (Measurable e, Supported e) => e -> Phylo-toPhyloLabel x = Phylo (Just $ getLen x) (Just $ getSup x)---- | Set all branch support values to 'Nothing'.------ Useful to export a tree with branch lengths to Newick format.-measurableToPhyloTree :: Measurable e => Tree e a -> Tree Phylo a-measurableToPhyloTree = first measurableToPhyloLabel--measurableToPhyloLabel :: Measurable e => e -> Phylo-measurableToPhyloLabel x = Phylo (Just $ getLen x) Nothing---- | Set all branch lengths to 'Nothing'.------ Useful to export a tree with branch support to Newick format.-supportedToPhyloTree :: Supported e => Tree e a -> Tree Phylo a-supportedToPhyloTree = first supportedToPhyloLabel--supportedToPhyloLabel :: Supported e => e -> Phylo-supportedToPhyloLabel x = Phylo Nothing (Just $ getSup x)---- | Branch length label.------ For conversion, see 'phyloToLengthTree' and 'lengthToPhyloTree'.-newtype Length = Length {fromLength :: BranchLength}-  deriving (Read, Show, Eq, Ord, Generic, NFData)-  deriving (Num, Fractional, Floating) via Double-  deriving (Semigroup, Monoid) via Sum Double--instance Measurable Length where-  getLen = fromLength-  setLen b _ = Length b--instance Splittable Length where-  split = Length . (/ 2.0) . fromLength--instance ToJSON Length--instance FromJSON Length---- | If root branch length is not available, set it to 0.------ Return 'Left' if any other branch length is unavailable.-phyloToLengthTree :: Tree Phylo a -> Either String (Tree Length a)-phyloToLengthTree =-  maybe (Left "phyloToLengthTree: Length unavailable for some branches.") Right-    . bitraverse toLength pure-    . cleanRootLength--cleanRootLength :: Tree Phylo a -> Tree Phylo a-cleanRootLength (Node (Phylo Nothing s) l f) = Node (Phylo (Just 0) s) l f-cleanRootLength t = t--toLength :: Phylo -> Maybe Length-toLength p = Length <$> brLen p---- | Branch support label.------ For conversion, see 'phyloToSupportTree'.-newtype Support = Support {fromSupport :: BranchSupport}-  deriving (Read, Show, Eq, Ord, Generic, NFData)-  deriving (Num, Fractional, Floating) via Double-  deriving (Semigroup) via Min Double--instance Supported Support where-  getSup = fromSupport-  setSup s _ = Support s--instance Splittable Support where-  split = id--instance ToJSON Support--instance FromJSON Support---- | Set branch support values of branches leading to the leaves and of the root--- branch to maximum support.------ Return 'Left' if any other branch has no available support value.-phyloToSupportTree :: Tree Phylo a -> Either String (Tree Support a)-phyloToSupportTree t =-  maybe-    (Left "phyloToSupportTree: Support unavailable for some branches.")-    Right-    $ bitraverse toSupport pure $-      cleanLeafSupport m $-        cleanRootSupport m t-  where-    m = getMaxSupport t---- | Set all unavailable branch support values to maximum support.-phyloToSupportTreeUnsafe :: Tree Phylo a -> Tree Support a-phyloToSupportTreeUnsafe t = cleanSupport m t-  where-    m = getMaxSupport t---- If all branch support values are below 1.0, set the max support to 1.0.-getMaxSupport :: Tree Phylo a -> BranchSupport-getMaxSupport = fromJust . max (Just 1.0) . bimaximum . bimap brSup (const Nothing)--cleanRootSupport :: BranchSupport -> Tree Phylo a -> Tree Phylo a-cleanRootSupport maxSup (Node (Phylo b Nothing) l xs) = Node (Phylo b (Just maxSup)) l xs-cleanRootSupport _ t = t--cleanLeafSupport :: BranchSupport -> Tree Phylo a -> Tree Phylo a-cleanLeafSupport s (Node (Phylo b Nothing) l []) = Node (Phylo b (Just s)) l []-cleanLeafSupport s (Node b l xs) = Node b l $ map (cleanLeafSupport s) xs--toSupport :: Phylo -> Maybe Support-toSupport (Phylo _ Nothing) = Nothing-toSupport (Phylo _ (Just s)) = Just $ Support s--cleanSupport :: BranchSupport -> Tree Phylo a -> Tree Support a-cleanSupport maxSup (Node (Phylo _ s) l xs) = Node (Support $ fromMaybe maxSup s) l $ map (cleanSupport maxSup) xs---- | Explicit branch label for phylogenetic trees.-data PhyloExplicit = PhyloExplicit-  { sBrLen :: BranchLength,-    sBrSup :: BranchSupport-  }-  deriving (Read, Show, Eq, Ord, Generic)--instance Semigroup PhyloExplicit where-  PhyloExplicit bL sL <> PhyloExplicit bR sR = PhyloExplicit (bL + bR) (min sL sR)--instance Measurable PhyloExplicit where-  getLen = sBrLen-  setLen b l = l {sBrLen = b}--instance Splittable PhyloExplicit where-  split l = l {sBrLen = b'}-    where-      b' = sBrLen l / 2.0--instance Supported PhyloExplicit where-  getSup = sBrSup-  setSup s l = l {sBrSup = s}--instance ToJSON PhyloExplicit--instance FromJSON PhyloExplicit---- | Conversion to a 'PhyloExplicit' tree.------ See 'phyloToLengthTree' and 'phyloToSupportTree'.-toExplicitTree :: Tree Phylo a -> Either String (Tree PhyloExplicit a)-toExplicitTree t = do-  lt <- first fromLength <$> phyloToLengthTree t-  st <- first fromSupport <$> phyloToSupportTree t-  case zipTreesWith PhyloExplicit const lt st of-    Nothing -> error "toExplicitTree: This is a bug. Can not zip two trees with the same topology."-    Just zt -> return zt
− src/ELynx/Data/Tree/Rooted.hs
@@ -1,355 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- |--- Module      :  ELynx.Data.Tree.Rooted--- Description :  Rooted trees with labeled branches--- 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 09:57:29 2019.------ Rooted 'Tree's differes from a classical rose 'Data.Tree.Tree' in that it has--- labeled branches.------ For rooted topologies, please see 'ELynx.Data.Topology.Rooted'.------ A 'Tree' is defined as:------ @--- data Tree e a = Node---   { branch :: e,---     label :: a,---     forest :: Forest e a---   }--- @------ where------ @--- type Forest e a = [Tree e a]--- @------ This means, that the word 'Node' is reserved for the constructor of a tree,--- and that a 'Node' has an attached 'branch', a 'label', and a sub-'forest'.--- The value constructor /Node/ and the record function /label/ are not to be--- confused. The elements of the sub-forest are often called /children/.------ With respect to phylogenetic analyses, using the 'Tree' data type has some--- disadvantages:------ 1. All trees are rooted. Unrooted trees can be treated with a rooted data--- structure, as it is used here. However, some functions may be meaningless.------ 2. Changing branch labels, node labels, or the topology of the tree are slow--- operations, especially, when the changes are close to the leaves of the tree.------ In mathematical terms: A 'Tree' is a directed acyclic graph without loops,--- with vertex labels, with edge labels. Let me know if this definition is--- incomplete.-module ELynx.Data.Tree.Rooted-  ( -- * Data type-    Tree (..),-    Forest,-    toTreeBranchLabels,-    toTreeNodeLabels,--    -- * Access leaves, branches and labels-    leaves,-    duplicateLeaves,-    branches,-    setBranches,-    labels,-    setLabels,-    identify,--    -- * Change structure-    degree,-    prune,-    dropNodesWith,-    dropLeavesWith,-    zipTreesWith,-    zipTrees,-  )-where--import Control.Applicative-import Control.Comonad-import Control.DeepSeq-import Control.Monad-import Control.Monad.Fix-import Data.Aeson-import Data.Bifoldable-import Data.Bifunctor-import Data.Bitraversable-import Data.Data-import Data.Foldable-import Data.List-import Data.Maybe-import qualified Data.Set as S-import qualified Data.Tree as T-import GHC.Generics---- | Rooted rose trees with branch labels.------ Unary instances such as 'Functor' act on node labels, and not on branch--- labels. Binary instances such as 'Bifunctor' act on both labels.------ Lifted instances are not provided.-data Tree e a = Node-  { branch :: e,-    label :: a,-    forest :: Forest e a-  }-  deriving (Eq, Read, Show, Data, Generic)---- | A shorthand.-type Forest e a = [Tree e a]---- | Map over node labels.-instance Functor (Tree e) where-  fmap f ~(Node br lb ts) = Node br (f lb) $ map (fmap f) ts-  x <$ ~(Node br _ ts) = Node br x (map (x <$) ts)--instance Bifunctor Tree where-  bimap f g ~(Node br lb ts) = Node (f br) (g lb) $ map (bimap f g) ts-  first f ~(Node br lb ts) = Node (f br) lb $ map (first f) ts-  second g ~(Node br lb ts) = Node br (g lb) $ map (second g) ts---- | Combine node labels in pre-order.-instance Foldable (Tree e) where-  foldMap f ~(Node _ lb ts) = f lb <> foldMap (foldMap f) ts-  null _ = False-  {-# INLINE null #-}-  toList = labels-  {-# INLINE toList #-}--instance Bifoldable Tree where-  bifoldMap f g ~(Node br lb ts) = f br <> g lb <> foldMap (bifoldMap f g) ts--instance Traversable (Tree e) where-  traverse g ~(Node br lb ts) = Node br <$> g lb <*> traverse (traverse g) ts--instance Bitraversable Tree where-  bitraverse f g ~(Node br lb ts) = Node <$> f br <*> g lb <*> traverse (bitraverse f g) ts---- The following code provides a zip-like applicative instance. However,--- the zip-like instance makes the Monad instance meaningless. So, either we--- provide only 'Applicative' in zip-like form, or we use the classic instance--- for 'Applicative' and 'Monad'.---- -- | Note: The 'Applicative' instance of 'Tree' is similar to the one of--- -- 'Control.Applicative.ZipList', and differs from the instance of--- -- 'Data.Tree.Tree'!--- ----- -- >>> let t = Node "" 0 [Node "" 1 [], Node "" 2 []] :: Tree String Int--- -- >>> let f = Node "+3" (+3) [Node "*5" (*5) [], Node "+10" (+10) []] :: Tree String (Int -> Int)--- -- >>> f <*> t--- -- Node {branch = "+3", label = 3, forest = [Node {branch = "*5", label = 5, forest = []},Node {branch = "+10", label = 12, forest = []}]}--- ----- -- Note: The 'Monoid' instance of the branch labels determines how the branches--- -- are combined. For example, distances can be summed using the--- -- 'Data.Monoid.Sum' monoid.--- instance Monoid e => Applicative (Tree e) where---   pure lb = Node mempty lb []---   ~(Node brF lbF tsF) <*> ~(Node brX lbX tsX) =---     Node (brF <> brX) (lbF lbX) (zipWith (<*>) tsF tsX)---   liftA2 f ~(Node brX lbX tsX) ~(Node brY lbY tsY) =---     Node (brX <> brY) (f lbX lbY) (zipWith (liftA2 f) tsX tsY)---   ~(Node brX _ tsX) *> ~(Node brY lbY tsY) =---     Node (brX <> brY) lbY (zipWith (*>) tsX tsY)---   ~(Node brX lbX tsX) <* ~(Node brY _ tsY) =---     Node (brX <> brY) lbX (zipWith (<*) tsX tsY)---- | The 'Semigroup' instance of the branch labels determines how the--- branches are combined. For example, distances can be summed using--- 'Data.Semigroup.Sum'.------ The 'Monoid' instance of the branch labels determines the default branch--- label when using 'pure'.-instance 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)-  liftA2 f ~(Node brX lbX tsX) ~ty@(Node brY lbY tsY) =-    Node (brX <> brY) (f lbX lbY) (map (f lbX <$>) tsY ++ map (\tx -> liftA2 f tx ty) tsX)-  ~(Node brX _ tsX) *> ~ty@(Node brY lbY tsY) =-    Node (brX <> brY) lbY (tsY ++ map (*> ty) tsX)-  ~(Node brX lbX tsX) <* ~ty@(Node brY _ tsY) =-    Node (brX <> brY) lbX (map (lbX <$) tsY ++ map (<* ty) tsX)---- | 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-  ~(Node br lb ts) >>= f = case f lb of-    Node br' lb' ts' -> Node (br <> br') lb' (ts' ++ map (>>= f) ts)---- -- Cannot provide MonadZip instance because branch labels cannot be--- -- recovered from combined label.--- instance Monoid e => MonadZip (Tree e) where---   mzipWith f (Node brL lbL tsL) (Node brR lbR tsR) =---     Node (brL <> brR) (f lbL lbR) (mzipWith (mzipWith f) tsL tsR)------   munzip (Node br (lbL, lbR) ts) = (Node ? lbL tsL, Node ? lbR tsR)---     where---       (tsL, tsR) = munzip (map munzip ts)--instance Monoid e => MonadFix (Tree e) where-  mfix = mfixTree--mfixTree :: (a -> Tree e a) -> Tree e a-mfixTree f-  | Node br lb ts <- fix (f . label) =-    Node-      br-      lb-      ( zipWith-          (\i _ -> mfixTree ((!! i) . forest . f))-          [0 ..]-          ts-      )--instance Comonad (Tree e) where-  duplicate t@(Node br _ ts) = Node br t (map duplicate ts)-  extract (Node _ lb _) = lb-  {-# INLINE extract #-}--instance (NFData e, NFData a) => NFData (Tree e a) where-  rnf (Node br lb ts) = rnf br `seq` rnf lb `seq` rnf ts--instance (ToJSON e, ToJSON a) => ToJSON (Tree e a)--instance (FromJSON e, FromJSON a) => FromJSON (Tree e a)---- | Conversion to 'T.Tree' using branch labels.-toTreeBranchLabels :: Tree e a -> T.Tree e-toTreeBranchLabels (Node br _ ts) = T.Node br (map toTreeBranchLabels ts)---- | Conversion to 'T.Tree' using node labels.-toTreeNodeLabels :: Tree e a -> T.Tree a-toTreeNodeLabels (Node _ lb ts) = T.Node lb (map toTreeNodeLabels ts)---- | Get leaves.-leaves :: Tree e a -> [a]-leaves (Node _ lb []) = [lb]-leaves (Node _ _ ts) = concatMap leaves ts---- | Check if a tree has duplicate leaves.-duplicateLeaves :: Ord a => Tree e a -> Bool-duplicateLeaves = duplicates . leaves---- | Get branch labels in pre-order.-branches :: Tree e a -> [e]-branches t = squish t []-  where-    squish (Node br _ ts) xs = br : foldr squish xs ts---- | Set branch labels in pre-order.------ Return 'Nothing' if the provided list of branch labels is too short.-setBranches :: Bitraversable t => [f] -> t e a -> Maybe (t f a)-setBranches xs = bisequenceA . snd . bimapAccumL setBranch noChange xs-  where-    setBranch [] _ = ([], Nothing)-    setBranch (y : ys) _ = (ys, Just y)-    noChange ys z = (ys, Just z)---- | Return node labels in pre-order.-labels :: Tree e a -> [a]-labels t = squish t []-  where-    squish (Node _ lb ts) xs = lb : foldr squish xs ts---- | Set node labels in pre-order.------ Return 'Nothing' if the provided list of node labels is too short.-setLabels :: Traversable t => [b] -> t a -> Maybe (t b)-setLabels xs = sequenceA . snd . mapAccumL setLabel xs-  where-    setLabel [] _ = ([], Nothing)-    setLabel (y : ys) _ = (ys, Just y)---- | Label the nodes with unique integers starting at the root with 0.-identify :: Traversable t => t a -> t Int-identify = snd . mapAccumL (\i _ -> (i + 1, i)) (0 :: Int)---- | The degree of the root node.-degree :: Tree e a -> Int-degree = (+ 1) . length . forest---- | Prune degree two nodes.------ The information stored in a pruned node is lost. The branches are combined--- according to their 'Semigroup' instance of the form @\daughterBranch--- parentBranch -> combinedBranch@.-prune :: Semigroup e => Tree e a -> Tree e a-prune t@(Node _ _ []) = t-prune (Node paBr _ [Node daBr daLb daTs]) = Node (daBr <> paBr) daLb daTs-prune (Node paBr paLb paTs) = Node paBr paLb $ map prune paTs---- | Drop nodes satisfying predicate.------ Degree two nodes may arise.------ Also drop parent nodes of which all daughter nodes are dropped.------ Return 'Nothing' if the root node satisfies the predicate.-dropNodesWith :: (a -> Bool) -> Tree e a -> Maybe (Tree e a)-dropNodesWith p (Node br lb ts)-  | p lb = Nothing-  | otherwise =-    if null ts'-      then Nothing-      else Just $ Node br lb ts'-  where-    ts' = mapMaybe (dropNodesWith p) ts---- | Drop leaves satisfying predicate.------ Degree two nodes may arise.------ Also drop parent nodes of which all leaves are dropped.------ Return 'Nothing' if all leaves satisfy the predicate.-dropLeavesWith :: (a -> Bool) -> Tree e a -> Maybe (Tree e a)-dropLeavesWith p (Node br lb [])-  | p lb = Nothing-  | otherwise = Just $ Node br lb []-dropLeavesWith p (Node br lb ts) =-  if null ts'-    then Nothing-    else Just $ Node br lb ts'-  where-    ts' = mapMaybe (dropLeavesWith p) ts---- | Zip two trees with the same topology.------ Return 'Nothing' if the topologies are different.-zipTreesWith ::-  (e1 -> e2 -> e) ->-  (a1 -> a2 -> a) ->-  Tree e1 a1 ->-  Tree e2 a2 ->-  Maybe (Tree e a)-zipTreesWith f g (Node brL lbL tsL) (Node brR lbR tsR) =-  if length tsL == length tsR-    then -- I am proud of that :)).-      zipWithM (zipTreesWith f g) tsL tsR >>= Just . Node (f brL brR) (g lbL lbR)-    else Nothing---- | Zip two trees with the same topology.------ 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/Data/Tree/Splittable.hs
@@ -1,29 +0,0 @@--- |--- Module      :  ELynx.Data.Tree.Splittable--- Description :  Splittable branch labels--- Copyright   :  (c) Dominik Schrempf, 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Sat Jul 18 13:52:22 2020.-module ELynx.Data.Tree.Splittable-  ( Splittable (..),-  )-where---- | A data type that can be combined using '<>' and split into one out of two--- equal entities.------ The following equality should hold:------ @--- split x <> split x = x--- @-class Splittable e where-  split :: e -> e--instance Splittable Double where-  split = (/ 2)
− src/ELynx/Data/Tree/Supported.hs
@@ -1,66 +0,0 @@--- |--- Module      :  ELynx.Data.Tree.Supported--- Description :  Branch label with support value--- 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.------ Non-negativity of branch support values is not (yet) ensured. To ensure--- non-negativity, a newtype wrapper could be used, but this would be a major--- refactor.-module ELynx.Data.Tree.Supported-  ( BranchSupport,-    Supported (..),-    normalizeBranchSupport,-    collapse,-  )-where--import Data.Bifoldable-import Data.Bifunctor-import Data.List-import ELynx.Data.Tree.Rooted---- | Branch support.-type BranchSupport = Double---- | A branch label that supports extraction and setting of branch support values.-class Supported e where-  getSup :: e -> BranchSupport-  setSup :: BranchSupport -> e -> e---- Apply a function to a branch support label.-apply :: Supported e => (BranchSupport -> BranchSupport) -> e -> e-apply f l = setSup (f s) l where s = getSup l---- | 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 (apply (/ 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) => BranchSupport -> 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 => Double -> Tree e a -> Bool-highP _  (Node _ _ []) = True-highP th (Node br _ _) = getSup br >= th---- See 'collapse'.-collapse' :: Supported e => BranchSupport -> 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/Data/Tree/Zipper.hs
@@ -1,160 +0,0 @@--- |--- Module      :  ELynx.Data.Tree.Zipper--- Description :  Zippers on rooted rose trees with branch labels--- Copyright   :  (c) Dominik Schrempf, 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Thu Jul 23 08:42:37 2020.-module ELynx.Data.Tree.Zipper-  ( -- * Data type-    TreePos (..),--    -- * Conversion-    fromTree,-    toTree,--    -- * Movement-    goUp,-    goRoot,-    goLeft,-    goRight,-    goChild,-    goPath,-    unsafeGoPath,--    -- * Modification-    insertTree,-    insertBranch,-    insertLabel,-  )-where--import Data.Foldable-import ELynx.Data.Tree.Rooted---- | Tree zipper. For reference, please see http://hackage.haskell.org/package/rosezipper.-data TreePos e a = Pos-  { -- | The currently selected tree.-    current :: Tree e a,-    -- | Forest to the left in reversed order.-    before :: Forest e a,-    -- | Forest to the right-    after :: Forest e a,-    -- | Finger to the selected tree-    parents :: [([Tree e a], e, a, [Tree e a])]-  }-  deriving (Show, Eq)---- | Get a zipper pointing to the root.-fromTree :: Tree e a -> TreePos e a-fromTree t = Pos {current = t, before = [], after = [], parents = []}---- | Get the complete tree of the zipper.-toTree :: TreePos e a -> Tree e a-toTree = current . goRoot--getForest :: TreePos e a -> Forest e a-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-  (ls, br, lb, rs) : ps ->-    Just-      Pos-        { current = Node br lb $ getForest pos,-          before = ls,-          after = rs,-          parents = ps-        }-  [] -> Nothing---- | Go to root.-goRoot :: TreePos e a -> TreePos e a-goRoot pos = maybe pos goRoot (goUp pos)---- | Go to left sibling in current forest.-goLeft :: TreePos e a -> Maybe (TreePos e a)-goLeft pos =-  case before pos of-    t : ts ->-      Just-        pos-          { current = t,-            before = ts,-            after = current pos : after pos-          }-    [] -> Nothing---- | Go to right sibling in current forest.-goRight :: TreePos e a -> Maybe (TreePos e a)-goRight pos =-  case after pos of-    t : ts ->-      Just-        pos-          { current = t,-            before = current pos : before pos,-            after = ts-          }-    [] -> Nothing---- | Go to child with given index in forest.-goChild :: Int -> TreePos e a -> Maybe (TreePos e a)-goChild n pos = case current pos of-  (Node br lb ts)-    | null ts -> Nothing-    | length ts <= n -> Nothing-    | otherwise ->-      Just $-        Pos-          { current = head rs',-            before = reverse ls',-            after = tail rs',-            parents = (before pos, br, lb, after pos) : parents pos-          }-    where-      (ls', rs') = splitAt n ts---- | Go to node with given path.-goPath :: [Int] -> 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.-unsafeGoChild :: Int -> TreePos e a -> TreePos e a-unsafeGoChild n pos = case current pos of-  (Node br lb ts)-    | null ts -> error "unsafeGoChild: Forest is empty."-    | length ts <= n -> error "unsafeGoChild: Forest is too short."-    | otherwise ->-      Pos-        { current = head rs',-          before = reverse ls',-          after = tail rs',-          parents = (before pos, br, lb, after pos) : parents pos-        }-    where-      (ls', rs') = splitAt n ts---- | Got to node with given path. Call 'error' if path is invalid.-unsafeGoPath :: [Int] -> TreePos e a -> TreePos e a-unsafeGoPath pos pth = foldl (flip unsafeGoChild) pth pos---- | 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}---- | Insert a new branch label into the current focus of the zipper.-insertBranch :: e -> TreePos e a -> TreePos e a-insertBranch br pos = case current pos of-  Node _ lb ts -> pos {current = Node br lb ts}---- | Insert a new node label into the current focus of the zipper.-insertLabel :: a -> TreePos e a -> TreePos e a-insertLabel lb pos = case current pos of-  Node br _ ts -> pos {current = Node br lb ts}
− src/ELynx/Distribution/BirthDeath.hs
@@ -1,95 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- |---   Module      :  ELynx.Distribution.BirthDeath---   Description :  Birth and death distribution---   Copyright   :  (c) Dominik Schrempf 2018---   License     :  GPL-3.0-or-later------   Maintainer  :  dominik.schrempf@gmail.com---   Stability   :  unstable---   Portability :  portable------ Creation date: Tue Feb 13 13:16:18 2018.------ See Gernhard, T. (2008). The conditioned reconstructed process. Journal of--- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.------ Distribution of the values of the point process such that it corresponds to--- reconstructed trees under the birth and death process.-module ELynx.Distribution.BirthDeath-  ( BirthDeathDistribution (..),-    cumulative,-    density,-    quantile,-  )-where--import Data.Data-  ( Data,-    Typeable,-  )-import ELynx.Distribution.Types-import GHC.Generics (Generic)-import qualified Statistics.Distribution as D---- | Distribution of the values of the point process such that it corresponds to--- a reconstructed tree of the birth and death process.-data BirthDeathDistribution = BDD-  { -- | Time to origin of the tree.-    bddTOr :: Time,-    -- | Birth rate.-    bddLa :: Rate,-    -- | Death rate.-    bddMu :: Rate-  }-  deriving (Eq, Typeable, Data, Generic)--instance D.Distribution BirthDeathDistribution where-  cumulative = cumulative---- | Cumulative distribution function Eq. (3).-cumulative :: BirthDeathDistribution -> Time -> Double-cumulative (BDD t l m) x-  | x <= 0 = 0-  | x > t = 1-  | otherwise = t1 * t2-  where-    d = l - m-    t1 = (1.0 - exp (- d * x)) / (l - m * exp (- d * x))-    t2 = (l - m * exp (- d * t)) / (1.0 - exp (- d * t))--instance D.ContDistr BirthDeathDistribution where-  density = density-  quantile = quantile---- | Density function Eq. (2).-density :: BirthDeathDistribution -> Time -> Double-density (BDD t l m) x-  | x < 0 = 0-  | x > t = 0-  | otherwise = d ** 2 * t1 * t2-  where-    d = l - m-    t1 = exp (- d * x) / ((l - m * exp (- d * x)) ** 2)-    t2 = (l - m * exp (- d * t)) / (1.0 - exp (- d * t))---- | Inverted cumulative probability distribution 'cumulative'. See also--- 'D.ContDistr'.-quantile :: BirthDeathDistribution -> Double -> Time-quantile (BDD t l m) p-  | p >= 0 && p <= 1 =-    res-  | otherwise =-    error $-      "PointProcess.quantile: p must be in range [0,1] but got "-        ++ show p-        ++ "."-  where-    d = l - m-    t2 = (l - m * exp (- d * t)) / (1.0 - exp (- d * t))-    res = (-1.0 / d) * log ((1.0 - p * l / t2) / (1.0 - p * m / t2))--instance D.ContGen BirthDeathDistribution where-  genContVar = D.genContinuous
− src/ELynx/Distribution/BirthDeathCritical.hs
@@ -1,84 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- |---   Module      :  ELynx.Distribution.BirthDeathCritical---   Description :  Birth and death distribution---   Copyright   :  (c) Dominik Schrempf 2018---   License     :  GPL-3.0-or-later------   Maintainer  :  dominik.schrempf@gmail.com---   Stability   :  unstable---   Portability :  portable------ Creation date: Tue Feb 13 13:16:18 2018.------ See Gernhard, T. (2008). The conditioned reconstructed process. Journal of--- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.------ Distribution of the values of the point process such that it corresponds to--- reconstructed trees under the birth and death process; critical birth and death--- process with lambda=mu.-module ELynx.Distribution.BirthDeathCritical-  ( BirthDeathCriticalDistribution (..),-    cumulative,-    density,-    quantile,-  )-where--import Data.Data-  ( Data,-    Typeable,-  )-import ELynx.Distribution.Types-import GHC.Generics (Generic)-import qualified Statistics.Distribution as D---- | Distribution of the values of the point process such that it corresponds to--- a reconstructed tree of the birth and death process.-data BirthDeathCriticalDistribution = BDCD-  { -- | Time to origin of the tree.-    bdcdTOr :: Time,-    -- | Birth and death rate.-    bdcdLa :: Rate-  }-  deriving (Eq, Typeable, Data, Generic)--instance D.Distribution BirthDeathCriticalDistribution where-  cumulative = cumulative---- | Cumulative distribution function section 2.1.2, second formula.-cumulative :: BirthDeathCriticalDistribution -> Time -> Double-cumulative (BDCD t l) x-  | x <= 0 = 0-  | x > t = 1-  | otherwise = x / (1.0 + l * x) * (1.0 + l * t) / t--instance D.ContDistr BirthDeathCriticalDistribution where-  density = density-  quantile = quantile---- | Density function section 2.1.2, first formula.-density :: BirthDeathCriticalDistribution -> Time -> Double-density (BDCD t l) x-  | x < 0 = 0-  | x > t = 0-  | otherwise = (1.0 + l * t) / (t * (1.0 + l * x) ** 2)---- | Inverted cumulative probability distribution 'cumulative'. See also--- 'D.ContDistr'.-quantile :: BirthDeathCriticalDistribution -> Double -> Time-quantile (BDCD t l) p-  | p >= 0 && p <= 1 =-    res-  | otherwise =-    error $-      "PointProcess.quantile: p must be in [0,1] range. Got: "-        ++ show p-        ++ "."-  where-    res = p * t / (1 + l * t - l * p * t)--instance D.ContGen BirthDeathCriticalDistribution where-  genContVar = D.genContinuous
− src/ELynx/Distribution/BirthDeathCriticalNoTime.hs
@@ -1,76 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- |---   Module      :  ELynx.Distribution.BirthDeathCriticalNoTime---   Description :  Birth and death distribution---   Copyright   :  (c) Dominik Schrempf 2018---   License     :  GPL-3.0-or-later------   Maintainer  :  dominik.schrempf@gmail.com---   Stability   :  unstable---   Portability :  portable------ Creation date: Tue Feb 13 13:16:18 2018.------ See Gernhard, T. (2008). The conditioned reconstructed process. Journal of--- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.------ Distribution of the values of the point process such that it corresponds to--- reconstructed trees under the birth and death process; critical birth and death--- process with lambda=mu.-module ELynx.Distribution.BirthDeathCriticalNoTime-  ( BirthDeathCriticalNoTimeDistribution (..),-    cumulative,-    density,-    quantile,-  )-where--import Data.Data-  ( Data,-    Typeable,-  )-import ELynx.Distribution.Types-import GHC.Generics (Generic)-import qualified Statistics.Distribution as D---- | Distribution of the values of the point process such that it corresponds to--- a reconstructed tree of the birth and death process.-newtype BirthDeathCriticalNoTimeDistribution = BDCNTD-  { -- | Birth and death rate.-    bdcntdLa :: Rate-  }-  deriving (Eq, Typeable, Data, Generic)--instance D.Distribution BirthDeathCriticalNoTimeDistribution where-  cumulative = cumulative---- | Cumulative distribution function section 2.1.2, second formula.-cumulative :: BirthDeathCriticalNoTimeDistribution -> Time -> Double-cumulative (BDCNTD l) x-  | x <= 0 = 0-  | otherwise = x * l / (1.0 + x * l)--instance D.ContDistr BirthDeathCriticalNoTimeDistribution where-  density = density-  quantile = quantile---- | Density function section 2.1.2, first formula; t cancels out because it is--- expected to be much larger than 1.0; because t \in [0, \infty].-density :: BirthDeathCriticalNoTimeDistribution -> Time -> Double-density (BDCNTD l) x-  | x < 0 = 0-  | otherwise = l / ((1.0 + x * l) ** 2)---- | Inverted cumulative probability distribution 'cumulative'. See also--- 'D.ContDistr'.-quantile :: BirthDeathCriticalNoTimeDistribution -> Double -> Time-quantile (BDCNTD l) p-  | p >= 0 && p <= 1 =-    p / (l - l * p)-  | otherwise =-    error $ "PointProcess.quantile: p must be in [0,1]. Got: " ++ show p ++ "."--instance D.ContGen BirthDeathCriticalNoTimeDistribution where-  genContVar = D.genContinuous
− src/ELynx/Distribution/BirthDeathNearlyCritical.hs
@@ -1,109 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- |---   Module      :  ELynx.Distribution.BirthDeathNearlyCritical---   Description :  Birth and death distribution---   Copyright   :  (c) Dominik Schrempf 2018---   License     :  GPL-3.0-or-later------   Maintainer  :  dominik.schrempf@gmail.com---   Stability   :  unstable---   Portability :  portable------ Creation date: Tue Feb 13 13:16:18 2018.------ See Gernhard, T. (2008). The conditioned reconstructed process. Journal of--- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.------ Distribution of the values of the point process such that it corresponds to--- reconstructed trees under the birth and death process; nearly critical birth and--- death process with lambda~mu.------ Basically, this is a Taylor expansion of Eq. (2) and Eq. (3).-module ELynx.Distribution.BirthDeathNearlyCritical-  ( BirthDeathNearlyCriticalDistribution (..),-    cumulative,-    density,-    quantile,-  )-where--import Data.Data-  ( Data,-    Typeable,-  )-import ELynx.Distribution.Types-import GHC.Generics (Generic)-import qualified Statistics.Distribution as D---- | Distribution of the values of the point process such that it corresponds to--- a reconstructed tree of the birth and death process.-data BirthDeathNearlyCriticalDistribution = BDNCD-  { -- | Time to origin of the tree.-    bdncdTOr :: Time,-    -- | Birth and death rate.-    bdncdLa :: Rate,-    -- | Birth and death rate.-    bdncdMu :: Rate-  }-  deriving (Eq, Typeable, Data, Generic)--instance D.Distribution BirthDeathNearlyCriticalDistribution where-  cumulative = cumulative---- | Cumulative distribution function section 2.1.2, second formula.-cumulative :: BirthDeathNearlyCriticalDistribution -> Time -> Double-cumulative (BDNCD t l m) s-  | s <= 0 = 0-  | s > t = 1-  | otherwise = o0 + o1-  where-    o0 = s * (1.0 + t * l) / t / (1.0 + s * l)-    o1 = (- s * s + s * t) * (m - l) / (2.0 * t * (1.0 + s * l) ** 2)--instance D.ContDistr BirthDeathNearlyCriticalDistribution where-  density = density-  quantile = quantile---- | Density function section 2.1.2, first formula.-density :: BirthDeathNearlyCriticalDistribution -> Time -> Double-density (BDNCD t l m) s-  | s < 0 = 0-  | s > t = 0-  | otherwise = o0 + o1-  where-    o0 = (1.0 + t * l) / (t * (1.0 + s * l) ** 2)-    o1 = (-2.0 * s + t - s * t * l) * (m - l) / (2.0 * t * (1.0 + s * l) ** 3)---- | Inverted cumulative probability distribution 'cumulative'. See also--- 'D.ContDistr'.-quantile :: BirthDeathNearlyCriticalDistribution -> Double -> Time-quantile (BDNCD t l m) p-  | p >= 0 && p <= 1 =-    res-  | otherwise =-    error $-      "PointProcess.quantile: p must be in [0,1] range. Got: "-        ++ show p-        ++ "."-  where-    den = l * (-3.0 + 2.0 * t * (-1.0 + p) * l) + m-    t1 = (2.0 + t * (l - 4.0 * p * l + m)) / den-    t2Nom =-      4.0-        + t-        * ( l-              * (4.0 + t * l + 8.0 * p * (1.0 + t * l))-              + 2.0-              * (2.0 + t * l - 4.0 * p * (1.0 + t * l))-              * m-              + t-              * m-              * m-          )-    t2 = t2Nom / (den ** 2)-    res = 0.5 * (t1 + sqrt t2)--instance D.ContGen BirthDeathNearlyCriticalDistribution where-  genContVar = D.genContinuous
− src/ELynx/Distribution/CoalescentContinuous.hs
@@ -1,26 +0,0 @@--- |--- Module      :  ELynx.Distribution.CoalescentContinuous--- Description :  Distribution of coalescent times--- Copyright   :  (c) Dominik Schrempf 2018--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Wed May 16 12:40:45 2018.-module ELynx.Distribution.CoalescentContinuous-  ( coalescentDistributionCont,-  )-where--import Numeric.SpecFunctions (choose)-import Statistics.Distribution.Exponential---- | Distribution of the next coalescent event for a number of samples @n@. The--- time is measured in units of effective number of population size.-coalescentDistributionCont ::-  -- | Sample size.-  Int ->-  ExponentialDistribution-coalescentDistributionCont n = exponential (choose n 2)
− src/ELynx/Distribution/TimeOfOrigin.hs
@@ -1,95 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- |---   Module      :  ELynx.Distribution.TimeOfOrigin---   Description :  Distribution of time of origin for birth and death trees---   Copyright   :  (c) Dominik Schrempf 2018---   License     :  GPL-3.0-or-later------   Maintainer  :  dominik.schrempf@gmail.com---   Stability   :  unstable---   Portability :  portable------ Creation date: Tue Feb 13 13:16:18 2018.------ See Gernhard, T. (2008). The conditioned reconstructed process. Journal of--- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.------ Distribution of the time of origin for birth and death trees. See corollary 3.3--- in the paper cited above.-module ELynx.Distribution.TimeOfOrigin-  ( TimeOfOriginDistribution (..),-    cumulative,-    density,-    quantile,-  )-where--import Data.Data-  ( Data,-    Typeable,-  )-import ELynx.Distribution.Types-import GHC.Generics (Generic)-import qualified Statistics.Distribution as D---- | Distribution of the time of origin for a phylogenetic tree evolving under--- the birth and death process and conditioned on observing n leaves today.-data TimeOfOriginDistribution = TOD-  { -- | Number of leaves of the tree.-    todTN :: Int,-    -- | Birth rate.-    todLa :: Rate,-    -- | Death rate.-    todMu :: Rate-  }-  deriving (Eq, Typeable, Data, Generic)--instance D.Distribution TimeOfOriginDistribution where-  cumulative = cumulative---- | Cumulative distribution function Corollary 3.3.-cumulative :: TimeOfOriginDistribution -> Time -> Double-cumulative (TOD n l m) x-  | x <= 0 = 0-  | otherwise = te ** fromIntegral n-  where-    d = l - m-    te = l * (1.0 - exp (- d * x)) / (l - m * exp (- d * x))--instance D.ContDistr TimeOfOriginDistribution where-  density = density-  quantile = quantile---- | The density function Eq. (5).-density :: TimeOfOriginDistribution -> Time -> Double-density (TOD nn l m) x-  | x < 0 = 0-  | otherwise = n * l ** n * d ** 2 * t1 ** (n - 1.0) * ex / (t2 ** (n + 1.0))-  where-    d = l - m-    n = fromIntegral nn-    ex = exp (- d * x)-    t1 = 1.0 - ex-    t2 = l - m * ex---- | The inverted cumulative probability distribution 'cumulative'. See also--- 'D.ContDistr'.-quantile :: TimeOfOriginDistribution -> Double -> Time-quantile (TOD n' l m) p-  | p >= 0 && p <= 1 =-    -1.0 / d * log (t1 / t2)-  | otherwise =-    error $-      "PointProcess.quantile: p must be in [0,1] range. Got: "-        ++ show p-        ++ "."-  where-    d = l - m-    n = fromIntegral n'-    t1 = l * (1.0 - p ** (1.0 / n))-    t2 = l - p ** (1.0 / n) * m--instance D.ContGen TimeOfOriginDistribution where-  genContVar = D.genContinuous
− src/ELynx/Distribution/TimeOfOriginNearCritical.hs
@@ -1,96 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- |---   Module      :  ELynx.Distribution.TimeOfOriginNearCritical---   Description :  Distribution of time of origin for birth and death trees---   Copyright   :  (c) Dominik Schrempf 2018---   License     :  GPL-3.0-or-later------   Maintainer  :  dominik.schrempf@gmail.com---   Stability   :  unstable---   Portability :  portable------ Creation date: Tue Feb 13 13:16:18 2018.------ See Gernhard, T. (2008). The conditioned reconstructed process. Journal of--- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.------ Distribution of the time of origin for birth and death trees. See corollary 3.3--- in the paper cited above.-module ELynx.Distribution.TimeOfOriginNearCritical-  ( TimeOfOriginNearCriticalDistribution (..),-    cumulative,-    density,-    quantile,-  )-where--import Data.Data-  ( Data,-    Typeable,-  )-import ELynx.Distribution.Types-import GHC.Generics (Generic)-import qualified Statistics.Distribution as D---- | Distribution of the time of origin for a phylogenetic tree evolving under--- the birth and death process and conditioned on observing n leaves today.-data TimeOfOriginNearCriticalDistribution = TONCD-  { -- | Number of leaves of the tree.-    todTN :: Int,-    -- | Birth rate.-    todLa :: Rate,-    -- | Death rate.-    todMu :: Rate-  }-  deriving (Eq, Typeable, Data, Generic)--instance D.Distribution TimeOfOriginNearCriticalDistribution where-  cumulative = cumulative---- | Cumulative distribution function; see Mathematica notebook.-cumulative :: TimeOfOriginNearCriticalDistribution -> Time -> Double-cumulative (TONCD n' l m) t-  | t <= 0 = 0-  | otherwise = t1 + t2-  where-    d = l - m-    n = fromIntegral n'-    t1 = (t * l / (1.0 + t * l)) ** n-    t2 = (n * t * t1) * d / (2.0 * (1.0 + t * l))--instance D.ContDistr TimeOfOriginNearCriticalDistribution where-  density = density-  quantile = quantile---- | The density function Eq. (5).-density :: TimeOfOriginNearCriticalDistribution -> Time -> Double-density (TONCD n' l m) t-  | t < 0 = 0-  | otherwise = nom / den-  where-    n = fromIntegral n'-    nom =-      n * (t * l / (1 + t * l)) ** n * (2 + (3 + n) * t * l - (1 + n) * t * m)-    den = 2 * t * (1 + t * l) ** 2---- | The inverted cumulative probability distribution 'cumulative'. See also--- 'D.ContDistr'.-quantile :: TimeOfOriginNearCriticalDistribution -> Double -> Time-quantile (TONCD n' l m) p-  | p >= 0 && p <= 1 =-    t1 + t2nom / t2den-  | otherwise =-    error $-      "PointProcess.quantile: p must be in [0,1] range. Got: "-        ++ show p-        ++ "."-  where-    n = fromIntegral n'-    t1 = - p ** (1 / n) / ((-1 + p ** (1 / n)) * l)-    t2nom = p ** (2 / n) * (m - l)-    t2den = 2 * (-1 + p ** (1 / n)) ** 2 * l ** 2--instance D.ContGen TimeOfOriginNearCriticalDistribution where-  genContVar = D.genContinuous
− src/ELynx/Distribution/Types.hs
@@ -1,22 +0,0 @@--- |--- Module      :  ELynx.Distribution.Types--- Description :  Data types for distributions on trees--- Copyright   :  (c) Dominik Schrempf 2018--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Wed May 16 12:21:57 2018.-module ELynx.Distribution.Types-  ( Time,-    Rate,-  )-where---- | Branch lengths are measured in time.-type Time = Double---- | Birth or death rates.-type Rate = Double
− src/ELynx/Export/Tree/Newick.hs
@@ -1,53 +0,0 @@--- |--- Module      :  ELynx.Export.Tree.Newick--- Description :  Export tree objects to Newick format--- 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 13:51:47 2019.------ Some functions are inspired by--- [Biobase.Newick.Import](https://hackage.haskell.org/package/BiobaseNewick).------ See nomenclature in 'ELynx.Data.Tree.Tree'.-module ELynx.Export.Tree.Newick-  ( toNewick,-    toNewickBuilder,-  )-where--import qualified Data.ByteString.Builder as BB-import qualified Data.ByteString.Lazy.Char8 as BL-import Data.List (intersperse)-import ELynx.Data.Tree.Named-import ELynx.Data.Tree.Phylogeny-import ELynx.Data.Tree.Rooted---- | See 'toNewick'.-toNewickBuilder :: Named a => Tree Phylo a -> BB.Builder-toNewickBuilder t = go t <> BB.char8 ';'-  where-    go (Node b l []) = lbl b l-    go (Node b l ts) =-      BB.char8 '('-        <> mconcat (intersperse (BB.char8 ',') $ map go ts)-        <> BB.char8 ')'-        <> lbl b l-    mBrSupBuilder x = maybe mempty (\bs -> BB.char8 '[' <> BB.doubleDec bs <> BB.char8 ']') (brSup x)-    mBrLenBuilder x = maybe mempty (\bl -> BB.char8 ':' <> BB.doubleDec bl) (brLen x)-    lbl x y =-      BB.lazyByteString (getName y)-        <> mBrLenBuilder x-        -- After reading several discussion, I go for the "more semantical-        -- form" with branch support values in square brackets.-        <> mBrSupBuilder x---- | General conversion of a tree into a Newick 'BL.Bytestring'. Use provided--- functions to extract node labels and branch lengths builder objects. See also--- Biobase.Newick.Export.-toNewick :: Named a => Tree Phylo a -> BL.ByteString-toNewick = BB.toLazyByteString . toNewickBuilder
− src/ELynx/Export/Tree/Nexus.hs
@@ -1,31 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Export.Tree.Nexus--- Description :  Export trees to Nexus files--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Tue Apr 28 20:24:19 2020.-module ELynx.Export.Tree.Nexus-  ( toNexusTrees,-  )-where--import qualified Data.ByteString.Lazy.Char8 as BL-import ELynx.Data.Tree.Named-import ELynx.Data.Tree.Phylogeny-import ELynx.Data.Tree.Rooted-import ELynx.Export.Nexus-import ELynx.Export.Tree.Newick---- | Export a list of (NAME, TREE) to a Nexus file.-toNexusTrees :: Named 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 (n, t) = "  TREE " <> n <> " = " <> toNewick t
− src/ELynx/Import/Tree/Newick.hs
@@ -1,235 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}---- Module      :  ELynx.Import.Tree.Newick--- Description :  Import Newick trees--- 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:56:27 2019.------ Some functions are inspired by--- [Biobase.Newick.Import](https://hackage.haskell.org/package/BiobaseNewick).------ [Specifications](http://evolution.genetics.washington.edu/phylip/newicktree.html)------ In particular, no conversion from _ to (space) is done right now.------ For a description of rooted 'Tree's, please see the 'ELynx.Data.Tree.Rooted'---- |--- module header.-module ELynx.Import.Tree.Newick-  ( NewickFormat (..),-    description,-    newick,-    oneNewick,-    someNewick,-  )-where--import Control.Applicative-import Data.Aeson (FromJSON, ToJSON)-import Data.Attoparsec.ByteString.Char8-import qualified Data.ByteString.Char8 as BS-import ELynx.Data.Tree.Measurable-import ELynx.Data.Tree.Phylogeny-import ELynx.Data.Tree.Rooted hiding (forest, label)-import ELynx.Data.Tree.Supported-import GHC.Generics-import Prelude hiding (takeWhile)---- | Newick tree format.------ >>> unlines $ map (("- " <>) . description) (allValues :: [NewickFormat])--- - Standard: Branch support values are stored in square brackets after branch lengths.--- - IqTree:   Branch support values are stored as node names after the closing bracket of forests.--- - 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.-data NewickFormat = Standard | IqTree | RevBayes-  deriving (Eq, Show, Read, Bounded, Enum, Generic)--instance FromJSON NewickFormat--instance ToJSON NewickFormat---- | Short description of the supported Newick formats.-description :: NewickFormat -> String-description Standard =-  "Standard: Branch support values are stored in square brackets after branch lengths."-description IqTree =-  "IqTree:   Branch support values are stored as node names after the closing bracket of forests."-description RevBayes =-  "RevBayes: Key-value pairs is provided in square brackets after node names as well as branch lengths. XXX: Key value pairs are ignored at the moment."---- | Parse a single Newick tree. Also succeeds when more trees follow.-newick :: NewickFormat -> Parser (Tree Phylo BS.ByteString)-newick Standard = newickStandard-newick IqTree = newickIqTree-newick RevBayes = newickRevBayes---- | Parse a single Newick tree. Fails when end of file is not reached.-oneNewick :: NewickFormat -> Parser (Tree Phylo BS.ByteString)-oneNewick Standard = oneNewickStandard-oneNewick IqTree = oneNewickIqTree-oneNewick RevBayes = oneNewickRevBayes---- | Parse one or more Newick trees until end of file.-someNewick :: NewickFormat -> Parser (Forest Phylo BS.ByteString)-someNewick Standard = someNewickStandard-someNewick IqTree = someNewickIqTree-someNewick RevBayes = someNewickRevBayes---- Parse a single Newick tree. Also succeeds when more trees follow.-newickStandard :: Parser (Tree Phylo BS.ByteString)-newickStandard = skipWhile isSpace *> tree <* char ';' <* skipWhile isSpace <?> "newickStandard"---- Parse a single Newick tree. Fails when end of file is not reached.-oneNewickStandard :: Parser (Tree Phylo BS.ByteString)-oneNewickStandard = newickStandard <* endOfInput <?> "oneNewickStandard"---- Parse one ore more Newick trees until end of file.-someNewickStandard :: Parser (Forest Phylo BS.ByteString)-someNewickStandard = some newickStandard <* endOfInput <?> "someNewickStandard"--tree :: Parser (Tree Phylo BS.ByteString)-tree = branched <|> leaf <?> "tree"--branched :: Parser (Tree Phylo BS.ByteString)-branched = (<?> "branched") $ do-  f <- forest-  n <- name-  p <- phylo-  return $ Node p n f---- A 'forest' is a set of trees separated by @,@ and enclosed by parentheses.-forest :: Parser (Forest Phylo BS.ByteString)-forest = char '(' *> (tree `sepBy1` char ',') <* char ')' <?> "forest"---- A 'leaf' has a 'name' and a 'phylo' branch.-leaf :: Parser (Tree Phylo BS.ByteString)-leaf = (<?> "leaf") $ do-  n <- name-  p <- phylo-  return $ Node p n []--nameChar :: Char -> Bool-nameChar c = c `notElem` " :;()[],"---- A name can be any string of printable characters except blanks, colons,--- semicolons, parentheses, and square brackets (and commas).-name :: Parser BS.ByteString-name = takeWhile nameChar <?> "name"--phylo :: Parser Phylo-phylo = Phylo <$> optional branchLength <*> optional branchSupport <?> "phylo"---- Branch length.-branchLength :: Parser BranchLength-branchLength = char ':' *> double <?> "branchLength"--branchSupport :: Parser BranchSupport-branchSupport = (<?> "branchSupport") $-  do-    _ <- char '['-    s <- double-    _ <- char ']'-    return s------------------------------------------------------------------------------------- IQ-TREE.---- IQ-TREE stores the branch support as node names after the closing bracket of--- a forest. Parse a single Newick tree. Also succeeds when more trees follow.-newickIqTree :: Parser (Tree Phylo BS.ByteString)-newickIqTree = skipWhile isSpace *> treeIqTree <* char ';' <* skipWhile isSpace <?> "newickIqTree"---- See 'newickIqTree'. Parse a single Newick tree. Fails when end of file is not--- reached.-oneNewickIqTree :: Parser (Tree Phylo BS.ByteString)-oneNewickIqTree = newickIqTree <* endOfInput <?> "oneNewickIqTree"---- See 'newickIqTree'. Parse one ore more Newick trees until end of file.-someNewickIqTree :: Parser (Forest Phylo BS.ByteString)-someNewickIqTree = some newickIqTree <* endOfInput <?> "someNewickIqTree"---- IQ-TREE stores the branch support as node names after the closing bracket of a forest.-treeIqTree :: Parser (Tree Phylo BS.ByteString)-treeIqTree = branchedIqTree <|> leaf <?> "treeIqTree"---- IQ-TREE stores the branch support as node names after the closing bracket of a forest.-branchedIqTree :: Parser (Tree Phylo BS.ByteString)-branchedIqTree = (<?> "branchedIqTree") $ do-  f <- forestIqTree-  s <- optional double-  n <- name-  b <- optional branchLength-  return $ Node (Phylo b s) n f---- IQ-TREE stores the branch support as node names after the closing bracket of a forest.-forestIqTree :: Parser (Forest Phylo BS.ByteString)-forestIqTree = (<?> "forestIqTree") $ do-  _ <- char '('-  f <- treeIqTree `sepBy1` char ','-  _ <- char ')'-  return f------------------------------------------------------------------------------------- RevBayes.---- RevBayes uses square brackets and key-value pairs to define information--- about nodes and branches. Parse a single Newick tree. Also succeeds when more--- trees follow.------ XXX: Key value pairs are ignored at the moment.-newickRevBayes :: Parser (Tree Phylo BS.ByteString)-newickRevBayes =-  skipWhile isSpace *> optional brackets *> treeRevBayes <* char ';' <* skipWhile isSpace <?> "newickRevBayes"---- See 'newickRevBayes'. Parse a single Newick tree. Fails when end of file is--- not reached.-oneNewickRevBayes :: Parser (Tree Phylo BS.ByteString)-oneNewickRevBayes = newickRevBayes <* endOfInput <?> "oneNewickRevBayes"---- See 'newickRevBayes'. Parse one ore more Newick trees until end of file.-someNewickRevBayes :: Parser (Forest Phylo BS.ByteString)-someNewickRevBayes = some newickRevBayes <* endOfInput <?> "someNewickRevBayes"--treeRevBayes :: Parser (Tree Phylo BS.ByteString)-treeRevBayes = branchedRevBayes <|> leafRevBayes <?> "treeRevBayes"--branchedRevBayes :: Parser (Tree Phylo BS.ByteString)-branchedRevBayes = (<?> "branchedRevgBayes") $ do-  f <- forestRevBayes-  n <- nameRevBayes-  b <- optional branchLengthRevBayes-  return $ Node (Phylo b Nothing) n f--forestRevBayes :: Parser (Forest Phylo BS.ByteString)-forestRevBayes = (<?> "forestRevBayes") $ do-  _ <- char '('-  f <- treeRevBayes `sepBy1` char ','-  _ <- char ')'-  return f--nameRevBayes :: Parser BS.ByteString-nameRevBayes = name <* optional brackets <?> "nameRevBayes"--branchLengthRevBayes :: Parser BranchLength-branchLengthRevBayes = branchLength <* optional brackets <?> "branchLengthRevBayes"--leafRevBayes :: Parser (Tree Phylo BS.ByteString)-leafRevBayes = (<?> "leafRevBayes") $ do-  n <- nameRevBayes-  b <- optional branchLengthRevBayes-  return $ Node (Phylo b Nothing) n []---- Drop anything between brackets.-brackets :: Parser ()-brackets = (<?> "brackets") $ do-  _ <- char '['-  _ <- takeWhile (/= ']')-  _ <- char ']'-  return ()
− src/ELynx/Import/Tree/Nexus.hs
@@ -1,45 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Import.Tree.Nexus--- Description :  Import trees from Nexus files--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Tue Apr 28 17:44:13 2020.-module ELynx.Import.Tree.Nexus-  ( nexusTrees,-  )-where--import Control.Applicative-import Data.Attoparsec.ByteString.Char8-import qualified Data.ByteString.Char8 as BS-import ELynx.Data.Tree.Phylogeny-import ELynx.Data.Tree.Rooted-import ELynx.Import.Nexus-import ELynx.Import.Tree.Newick-import Prelude hiding (takeWhile)---- | Parse a Nexus files with a TREES block.-nexusTrees :: NewickFormat -> Parser [(BS.ByteString, Tree Phylo BS.ByteString)]-nexusTrees = nexus . trees--trees :: NewickFormat -> Block [(BS.ByteString, Tree Phylo BS.ByteString)]-trees f = Block "TREES" (some $ namedNewick f)--namedNewick :: NewickFormat -> Parser (BS.ByteString, Tree Phylo BS.ByteString)-namedNewick f = do-  _ <- skipWhile isSpace-  _ <- string "TREE"-  _ <- skipWhile isSpace-  n <- takeWhile1 (\x -> isAlpha_ascii x || isDigit x)-  _ <- skipWhile isSpace-  _ <- char '='-  _ <- skipWhile isSpace-  t <- newick f-  return (n, t)
− src/ELynx/Simulate/Coalescent.hs
@@ -1,60 +0,0 @@--- |--- Module      :  ELynx.Simulate.Coalescent--- Description :  Generate coalescent trees--- Copyright   :  (c) Dominik Schrempf 2018--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Wed May 16 13:13:11 2018.-module ELynx.Simulate.Coalescent-  ( simulate,-  )-where--import Control.Monad.Primitive-import ELynx.Data.Tree.Measurable-import ELynx.Data.Tree.Phylogeny-import ELynx.Data.Tree.Rooted-import ELynx.Distribution.CoalescentContinuous-import Statistics.Distribution-import System.Random.MWC---- | Simulate a coalescent tree with @n@ leaves. The branch lengths are in units--- of effective population size.-simulate ::-  (PrimMonad m) =>-  -- | Number of leaves.-  Int ->-  Gen (PrimState m) ->-  m (Tree Length Int)-simulate n = simulate' n 0 trs-  where-    trs = [Node (Length 0) i [] | i <- [0 .. n - 1]]--simulate' ::-  (PrimMonad m) =>-  Int ->-  Int ->-  Forest Length Int ->-  Gen (PrimState m) ->-  m (Tree Length Int)-simulate' n a trs g-  | n <= 0 = error "Cannot construct trees without leaves."-  | n == 1 && length trs /= 1 = error "Too many trees provided."-  | n == 1 && length trs == 1 = return $ head trs-  | otherwise = do-    -- Indices of the leaves to join will be i-1 and i.-    i <- uniformR (1, n - 1) g-    -- The time of the coalescent event.-    t <- genContVar (coalescentDistributionCont n) g-    let trs' = map (applyStem (+ t)) trs -- Move time 't' up on the tree.-        tl = trs' !! (i - 1)-        tr = trs' !! i-        -- Join the two chosen trees.-        tm = Node (Length 0) a [tl, tr]-        -- Take the trees on the left, the merged tree, and the trees on the right.-        trs'' = take (i - 1) trs' ++ [tm] ++ drop (i + 1) trs'-    simulate' (n - 1) a trs'' g
− src/ELynx/Simulate/PointProcess.hs
@@ -1,289 +0,0 @@-{-# LANGUAGE BangPatterns #-}---- |---   Module      :  ELynx.Simulate.PointProcess---   Description :  Point process and functions---   Copyright   :  (c) Dominik Schrempf 2018---   License     :  GPL-3.0-or-later------   Maintainer  :  dominik.schrempf@gmail.com---   Stability   :  unstable---   Portability :  portable------ Creation date: Tue Feb 13 13:16:18 2018.------ See Gernhard, T. (2008). The conditioned reconstructed process. Journal of--- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.------ The point process can be used to simulate reconstructed trees under the birth--- and death process.-module ELynx.Simulate.PointProcess-  ( PointProcess (..),-    TimeSpec,-    simulate,-    toReconstructedTree,-    simulateReconstructedTree,-    simulateNReconstructedTrees,-  )-where--import Control.Monad-import Control.Monad.Primitive-import Data.Function-import Data.List-import Data.Sequence (Seq)-import qualified Data.Sequence as S-import ELynx.Data.Tree.Measurable-import ELynx.Data.Tree.Phylogeny-import ELynx.Data.Tree.Rooted-import ELynx.Distribution.BirthDeath-import ELynx.Distribution.BirthDeathCritical-import ELynx.Distribution.BirthDeathCriticalNoTime-import ELynx.Distribution.BirthDeathNearlyCritical-import ELynx.Distribution.TimeOfOrigin-import ELynx.Distribution.TimeOfOriginNearCritical-import ELynx.Distribution.Types-import qualified Statistics.Distribution as D-  ( genContVar,-  )-import System.Random.MWC---- Require near critical process if birth and death rates are closer than this value.-epsNearCriticalPointProcess :: Double-epsNearCriticalPointProcess = 1e-5---- Also the distribution of origins needs a Tailor expansion for near critical values.------ TODO: Check why the two epsilons are chosen differently.-epsNearCriticalTimeOfOrigin :: Double-epsNearCriticalTimeOfOrigin = 1e-8---- Require critical process if birth and death rates are closer than this value.-eps :: Double-eps = 1e-12--(=~=) :: Double -> Double -> Bool-x =~= y = eps > abs (x - y)---- Sort a list and also return original indices.-sortListWithIndices :: Ord a => [a] -> [(a, Int)]-sortListWithIndices xs = sortBy (compare `on` fst) $ zip xs ([0 ..] :: [Int])---- Insert element into random position of list.-randomInsertList :: PrimMonad m => a -> [a] -> Gen (PrimState m) -> m [a]-randomInsertList e v g = do-  let l = length v-  i <- uniformR (0, l) g-  return $ take i v ++ [e] ++ drop i v---- | A __point process__ for \(n\) points and of age \(t_{or}\) is defined as--- follows. Draw $n$ points on the horizontal axis at \(1,2,\ldots,n\). Pick--- \(n-1\) points at locations \((i+1/2, s_i)\), \(i=1,2,\ldots,n-1\);--- \(0 < s_i < t_{or}\). There is a bijection between (ranked) oriented trees--- and the point process. Usually, a will be 'String' (or 'Int') and b will be--- 'Double'.-data PointProcess a b = PointProcess-  { points :: ![a],-    values :: ![b],-    origin :: !b-  }-  deriving (Read, Show, Eq)---- | If nothing, sample time of origin from respective distribution. If time is--- given, we need to know if we condition on the time of origin, or the time of--- the most recent common ancestor (MRCA).-type TimeSpec = Maybe (Time, Bool)---- | Sample a point process using the 'BirthDeathDistribution'. The names of the--- points will be integers.-simulate ::-  (PrimMonad m) =>-  -- | Number of points (samples)-  Int ->-  -- | Time of origin or MRCA-  TimeSpec ->-  -- | Birth rate-  Rate ->-  -- | Death rate-  Rate ->-  -- | Generator (see 'System.Random.MWC')-  Gen (PrimState m) ->-  m (PointProcess Int Double)--- No time of origin given. We also don't need to take care of the conditioning--- (origin or MRCA).-simulate n Nothing l m g-  | -- XXX. There is no formula for the over-critical process.-    m > l =-    error-      "Time of origin distribution formula not available when mu > lambda. Please specify height for the moment."-  | -- For the critical process, we have no idea about the time of origin, but can-    -- use a specially derived distribution.-    m =~= l =-    do-      !vs <- replicateM (n - 1) (D.genContVar (BDCNTD l) g)-      -- XXX: The length of the root branch will be 0.-      let t = maximum vs-      return $ PointProcess [0 .. (n - 1)] vs t-  | -- For the near critical process, we use a special distribution.-    abs (m - l) <= epsNearCriticalTimeOfOrigin =-    do-      t <- D.genContVar (TONCD n l m) g-      simulate n (Just (t, False)) l m g-  | -- For a sub-critical branching process, we can use the formula from Tanja Stadler.-    otherwise =-    do-      t <- D.genContVar (TOD n l m) g-      simulate n (Just (t, False)) l m g--- Time of origin is given.-simulate n (Just (t, c)) l m g-  | n < 1 = error "Number of samples needs to be one or larger."-  | t < 0.0 = error "Time of origin needs to be positive."-  | l < 0.0 = error "Birth rate needs to be positive."-  | -- See Stadler, T., & Steel, M. (2019). Swapping birth and death: symmetries-    -- and transformations in phylodynamic models. , (), .-    -- http://dx.doi.org/10.1101/494583. Should be possible now.-    -- -- | m < 0.0   = error "Death rate needs to be positive."-    -- Now, we have three different cases.-    -- 1. The critical branching process.-    -- 2. The near critical branching process.-    -- 3. Normal values :).-    (m =~= l) && not c = do-    !vs <- replicateM (n - 1) (D.genContVar (BDCD t l) g)-    return $ PointProcess [0 .. (n - 1)] vs t-  | (abs (m - l) <= epsNearCriticalPointProcess) && not c = do-    !vs <- replicateM (n - 1) (D.genContVar (BDNCD t l m) g)-    return $ PointProcess [0 .. (n - 1)] vs t-  | not c = do-    !vs <- replicateM (n - 1) (D.genContVar (BDD t l m) g)-    return $ PointProcess [0 .. (n - 1)] vs t-  | (m =~= l) && c = do-    !vs <- replicateM (n - 2) (D.genContVar (BDCD t l) g)-    vs' <- randomInsertList t vs g-    return $ PointProcess [0 .. (n - 1)] vs' t-  | (abs (m - l) <= epsNearCriticalPointProcess) && c = do-    !vs <- replicateM (n - 2) (D.genContVar (BDNCD t l m) g)-    vs' <- randomInsertList t vs g-    return $ PointProcess [0 .. (n - 1)] vs' t-  | c = do-    !vs <- replicateM (n - 2) (D.genContVar (BDD t l m) g)-    vs' <- randomInsertList t vs g-    return $ PointProcess [0 .. (n - 1)] vs' t-  | otherwise = error "simulate: Fell through guard, this should never happen."---- Sort the values of a point process and their indices to be (the indices--- that they will have while creating the tree).-sortPP :: (Ord b) => PointProcess a b -> ([b], [Int])-sortPP (PointProcess _ vs _) = (vsSorted, isSorted)-  where-    vsIsSorted = sortListWithIndices vs-    vsSorted = map fst vsIsSorted-    isSorted = flattenIndices $ map snd vsIsSorted---- Decrement indices that are above the one that is merged.-flattenIndices :: [Int] -> [Int]-flattenIndices is = snd $ mapAccumL fAcc [] is---- TODO: This is the bottleneck for simulating large trees.------ The accumulating function. Count the number of indices which are before the--- current index and lower than the current index.-fAcc :: [Int] -> Int -> ([Int], Int)-fAcc is i = (i : is, i') where i' = i - length (filter (< i) is)---- | See 'simulateReconstructedTree', but n times.-simulateNReconstructedTrees ::-  (PrimMonad m) =>-  -- | Number of trees-  Int ->-  -- | Number of points (samples)-  Int ->-  -- | Time of origin or MRCA-  TimeSpec ->-  -- | Birth rate-  Rate ->-  -- | Death rate-  Rate ->-  -- | Generator (see 'System.Random.MWC')-  Gen (PrimState m) ->-  m (Forest Length Int)-simulateNReconstructedTrees nT nP t l m g-  | nT <= 0 = return []-  | otherwise = replicateM nT $ simulateReconstructedTree nP t l m g---- | Use the point process to simulate a reconstructed tree (see--- 'toReconstructedTree') possibly with specific height and a fixed number of--- leaves according to the birth and death process.-simulateReconstructedTree ::-  (PrimMonad m) =>-  -- | Number of points (samples)-  Int ->-  -- | Time of origin or MRCA-  TimeSpec ->-  -- | Birth rate-  Rate ->-  -- | Death rate-  Rate ->-  -- | Generator (see 'System.Random.MWC')-  Gen (PrimState m) ->-  m (Tree Length Int)-simulateReconstructedTree n t l m g =-  toReconstructedTree 0 <$> simulate n t l m g---- | Convert a point process to a reconstructed tree. See Lemma 2.2.---- Of course, I decided to only use one tree structure with extinct and extant--- leaves (actually a complete tree). So a tree created here just does not--- contain extinct leaves. A function 'isReconstructed' is provided to test if a--- tree is reconstructed (and not complete) in this sense. However, a complete--- tree might show up as "reconstructed", just because, by chance, it does not--- contain extinct leaves. I wanted to use a Monoid constraint to get the unit--- element, but this fails for classical 'Int's. So, I rather have another--- (useless) argument.-toReconstructedTree ::-  a -> -- Default node label.-  PointProcess a Double ->-  Tree Length a-toReconstructedTree l pp@(PointProcess ps vs o)-  | length ps /= length vs + 1 = error "Too few or too many points."-  | length vs <= 1 = error "Too few values."-  | -- -- Test is deactivated.-    -- -- | otherwise = if isReconstructed treeOrigin then treeOrigin else error "Error in algorithm."-    otherwise =-    treeOrigin-  where-    (vsSorted, isSorted) = sortPP pp-    !lvs = S.fromList [Node (Length 0) p [] | p <- ps]-    !heights = S.replicate (length ps) 0-    !treeRoot = toReconstructedTree' isSorted vsSorted l lvs heights-    !h = last vsSorted-    !treeOrigin = applyStem (+ (o - h)) treeRoot---- Move up the tree, connect nodes when they join according to the point process.-toReconstructedTree' ::-  [Int] -> -- Sorted indices, see 'sort'.-  [Double] -> -- Sorted merge values.-  a -> -- Default node label.-  Seq (Tree Length a) -> -- Leaves with accumulated root branch lengths.-  Seq Double -> -- Accumulated heights of the leaves.-  Tree Length a-toReconstructedTree' [] [] _ trs _ = trs `S.index` 0-toReconstructedTree' is vs l trs hs = toReconstructedTree' is' vs' l trs'' hs'-  where-    -- For the algorithm, see 'ELynx.Coalescent.simulate', but index starts-    -- at zero.--    !i = head is-    !is' = tail is-    !v = head vs-    !vs' = tail vs-    -- Left: l, right: r.-    !hl = hs `S.index` i-    !hr = hs `S.index` (i + 1)-    !dvl = v - hl-    !dvr = v - hr-    !tl = applyStem (+ dvl) $ trs `S.index` i-    !tr = applyStem (+ dvr) $ trs `S.index` (i + 1)-    !h' = hl + dvl -- Should be the same as 'hr + dvr'.-    !tm = Node (Length 0) l [tl, tr]-    !trs'' = (S.take i trs S.|> tm) S.>< S.drop (i + 2) trs-    !hs' = (S.take i hs S.|> h') S.>< S.drop (i + 2) hs
+ src/ELynx/Topology/Phylogeny.hs view
@@ -0,0 +1,115 @@+-- |+-- Module      :  ELynx.Topology.Phylogeny+-- Description :  Phylogenetic topologies+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Sat Jul 18 13:15:49 2020.+--+-- 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.+--+-- 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.+--+-- Also, the uniqueness of the leaves is not ensured by the data type, but has+-- to be checked at runtime. Functions relying on the tree to have unique leaves+-- 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.+--+-- The bifurcating root of a topology can be changed with 'roots', or 'rootAt'.+--+-- Topologies with multifurcating root nodes can be properly rooted using+-- 'outgroup'.+module ELynx.Topology.Phylogeny+  ( outgroup,+    roots,+    rootAt,+  )+where++import Data.Set (Set)+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."++-- | For a rooted topology with a bifurcating root node, get all possible rooted+-- topologies.+roots :: Topology a -> Either String (Forest a)+roots = undefined++-- -- | 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++-- | Root a tree at a specific position.+rootAt :: Ord a => Bipartition a -> Topology a -> Either String (Forest a)+rootAt = undefined
+ src/ELynx/Topology/Rooted.hs view
@@ -0,0 +1,207 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+-- Module      :  ELynx.Topology.Rooted+-- Description :  Topologies+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- 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.+--+-- For rooted trees, please see 'ELynx.Tree.Rooted'.+--+-- 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.+module ELynx.Topology.Rooted+  ( -- * Data type+    Topology (..),+    Forest,+    fromTree,+    fromLabeledTree,++    -- * Functions+    degree,+    leaves,+    flatten,+    identify,+    prune,+    dropLeavesWith,+    zipTreesWith,+    zipTrees,+    duplicateLeaves,+  )+where++import Control.Applicative+import Control.DeepSeq+import Control.Monad+import Data.Aeson+import Data.Data+import Data.Foldable+import Data.List.NonEmpty (NonEmpty)+import qualified Data.List.NonEmpty as N+import Data.Maybe+import qualified Data.Set as S+import Data.Traversable+import qualified Data.Tree as T+import qualified ELynx.Tree.Rooted as R+import GHC.Generics++singleton :: NonEmpty a -> Bool+singleton xs = 1 == length (N.take 2 xs)++-- | Rooted topologies with leaf labels.+data Topology a+  = Node {forest :: Forest a}+  | Leaf {label :: a}+  deriving (Eq, Read, Show, Data, Generic)++-- | A shortcut.+type Forest a = NonEmpty (Topology a)++instance Functor Topology where+  fmap f (Node ts) = Node $ fmap (fmap f) ts+  fmap f (Leaf lb) = Leaf $ f lb++instance Foldable Topology where+  foldMap f (Node ts) = foldMap (foldMap f) ts+  foldMap f (Leaf lb) = f lb++  null _ = False+  {-# INLINE null #-}++  toList = flatten+  {-# INLINE toList #-}++instance Traversable Topology where+  traverse g (Node ts) = Node <$> traverse (traverse g) ts+  traverse g (Leaf lb) = Leaf <$> g lb++-- TODO: This type checks, but I doubt the implementation is bug-free.+instance Applicative Topology where+  pure = Leaf++  (Node tsF) <*> tx = Node $ fmap (<*> tx) tsF+  (Leaf lbF) <*> tx = lbF <$> tx++  liftA2 f (Node tsX) ty = Node $ fmap (\tx -> liftA2 f tx ty) tsX+  liftA2 f (Leaf lbX) (Node tsY) = Node $ fmap (f lbX <$>) tsY+  liftA2 f (Leaf lbX) (Leaf lbY) = Leaf $ f lbX lbY++  (Node tsX) *> ty@(Node tsY) = Node $ tsY <> fmap (*> ty) tsX+  (Leaf _) *> (Node tsY) = Node tsY+  _ *> (Leaf lbY) = Leaf lbY++  (Node tsX) <* ty = Node $ fmap (<* ty) tsX+  (Leaf lbX) <* _ = Leaf lbX++-- TODO: This type checks, but I doubt the implementation is bug-free.+instance Monad Topology where+  (Node ts) >>= f = Node $ fmap (>>= f) ts+  (Leaf lb) >>= f = case f lb of+    Node ts' -> Node ts'+    Leaf lb' -> Leaf lb'++instance NFData a => NFData (Topology a) where+  rnf (Node ts) = rnf ts+  rnf (Leaf lb) = rnf lb++instance ToJSON a => ToJSON (Topology a)++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.+fromTree :: T.Tree a -> Topology a+fromTree (T.Node lb []) = Leaf lb+fromTree (T.Node _ xs) = Node $ fromTree <$> N.fromList xs++-- | Convert a rooted, labeled rose tree to a rooted topology. Branch labels and+-- internal node labels are lost.+fromLabeledTree :: R.Tree e a -> Topology a+fromLabeledTree (R.Node _ lb []) = Leaf lb+fromLabeledTree (R.Node _ _ xs) = Node $ fromLabeledTree <$> N.fromList xs++-- | 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)++-- | Prune degree two nodes.+prune :: Topology a -> Topology a+prune (Node ts)+  | singleton ts = Node $ fmap prune $ forest $ N.head ts+  | otherwise = Node $ fmap prune ts+prune (Leaf lb) = Leaf lb++-- | Drop leaves satisfying predicate.+--+-- Degree two nodes may arise.+--+-- Return 'Nothing' if all leaves satisfy the predicate.+dropLeavesWith :: (a -> Bool) -> Topology a -> Maybe (Topology a)+dropLeavesWith p (Leaf lb)+  | p lb = Nothing+  | otherwise = Just $ Leaf lb+dropLeavesWith p (Node ts) =+  if null ts'+    then Nothing+    else -- XXX: May be slow, unnecessary conversion to and from list.+      Just $ Node $ N.fromList ts'+  where+    ts' = catMaybes $ N.toList $ fmap (dropLeavesWith p) ts++-- | Zip leaves of two equal topologies.+--+-- Return 'Nothing' if the topologies are different.+zipTreesWith :: (a1 -> a2 -> a) -> Topology a1 -> Topology a2 -> Maybe (Topology a)+zipTreesWith f (Node tsL) (Node tsR) =+  if N.length tsL == N.length tsR+    then -- XXX: May be slow, unnecessary conversion to and from list.+      zipWithM (zipTreesWith f) (N.toList tsL) (N.toList tsR) >>= Just . Node . N.fromList+    else Nothing+zipTreesWith f (Leaf lbL) (Leaf lbR) = Just $ Leaf $ f lbL lbR+zipTreesWith _ _ _ = Nothing++-- | Zip leaves of two equal topologies.+--+-- 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
@@ -0,0 +1,82 @@+-- TODO: Topology data type.+-- data Topology a = Node (NonEmptySet (Topology a)) | Leaf a++-- |+-- Module      :  ELynx.Tree+-- Description :  Phylogenetic trees+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Sat Mar 21 16:27:20 2020.+module ELynx.Tree+  ( -- * Rooted trees+    module ELynx.Tree.Rooted,+    module ELynx.Tree.Zipper,++    -- * Branch label classes+    module ELynx.Tree.Measurable,+    module ELynx.Tree.Splittable,+    module ELynx.Tree.Supported,++    -- * Node label classes+    module ELynx.Tree.Named,++    -- * Phylogenies+    module ELynx.Tree.Phylogeny,++    -- * Partitions and distances+    module ELynx.Tree.Bipartition,+    module ELynx.Tree.Partition,+    module ELynx.Tree.Distance,++    -- * Import and Export+    module ELynx.Tree.Export.Newick,+    module ELynx.Tree.Export.Nexus,+    module ELynx.Tree.Import.Newick,+    module ELynx.Tree.Import.Nexus,+  )+where++import ELynx.Tree.Bipartition+import ELynx.Tree.Distance+import ELynx.Tree.Export.Newick+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.Partition+import ELynx.Tree.Phylogeny+import ELynx.Tree.Rooted+import ELynx.Tree.Splittable+import ELynx.Tree.Supported+import ELynx.Tree.Zipper++-- -- | An evolutionary label has some information about where the corresponding+-- -- node is on the tree, and if the node is 'extant', 'extinct', 'internal', or+-- -- 'external'. The latter two could also be determined from the tree. This could+-- -- be species, genes or individuals; probably more.+-- class EvoLabel n where+--   extant          :: n -> Bool+--   extinct         :: n -> Bool++--   internal        :: n -> Bool+--   internal n = not $ extant n || extinct n++--   external        :: n -> Bool+--   external   = not . internal++-- -- -- | Glue branches together, so that one new tree emerges. It's root node is+-- -- -- new, the sub-forest has to be given (a list of trees).+-- -- glue :: (NodeType c)+-- --      => PhyloLabel a b c       -- ^ New root node.+-- --      -> [PhyloTree a b c]      -- ^ Sub-forest.+-- --      -> PhyloTree a b c+-- -- glue s@(PhyloLabel _ _ n) ts+-- --   | extant n  = error "Root node cannot be of type 'Exant'."+-- --   | extinct n = error "Root node cannot be of type 'Extinct'."+-- --   | otherwise = Node s ts
+ src/ELynx/Tree/Bipartition.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-- |+-- Module      :  ELynx.Tree.Bipartition+-- Description :  Bipartitions on trees+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Fri Aug 30 15:28:17 2019.+--+-- 'Bipartition's are weird in that+-- > Bipartition x y == Bipartition y x+-- is True.+--+-- Also,+-- > Bipartition x y > Bipartition y x+-- is False, even when @x > y@.+--+-- That's why we have to make sure that for+-- > Bipartition x y+-- we always have @x >= y@.+module ELynx.Tree.Bipartition+  ( groups,++    -- * Data type+    Bipartition (fromBipartition),+    bp,+    bpUnsafe,+    toSet,+    bpHuman,++    -- * Work with 'Bipartition's+    bipartition,+    bipartitions,+    getComplementaryLeaves,+    bipartitionToBranch,+  )+where++import Control.Comonad+import Control.DeepSeq+import Data.List hiding (partition)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Set (Set)+import qualified Data.Set as S+import ELynx.Tree.Rooted++-- | Each node of a tree is root of an induced subtree. Set the node labels to+-- the leaves of the induced subtrees.+groups :: Tree e a -> Tree e [a]+-- I am proud of this awesome 'Comonad' usage here :).+groups = extend leaves++-- | Each branch of a tree partitions the leaves of the tree into two subsets,+-- or a bipartition.+--+-- The order of the two subsets of a 'Bipartition' is meaningless. We ensure by+-- construction that the smaller subset comes first, and hence, that equality+-- checks are meaningful.+newtype Bipartition a = Bipartition+  { fromBipartition :: (Set a, Set a)+  }+  deriving (Eq, Ord, Show, Read, NFData)++-- | Create a bipartition from two sets.+--+-- Ensure that the smaller set comes first.+--+-- Return 'Left' if one set is empty.+bp :: Ord a => Set a -> Set a -> Either String (Bipartition a)+bp xs ys+  | S.null xs = Left "bp: Left set empty."+  | S.null ys = Left "bp: Right set empty."+  | otherwise = Right $ bpUnsafe xs ys++-- | Create a bipartition from two sets.+--+-- Ensure that the smaller set comes first.+bpUnsafe :: Ord a => Set a -> Set a -> Bipartition a+bpUnsafe xs ys = if xs >= ys then Bipartition (xs, ys) else Bipartition (ys, xs)++-- | Conversion to a set containing both partitions.+toSet :: Ord a => Bipartition a -> Set a+toSet (Bipartition (x, y)) = S.union x y++-- I decided not to provide a human readable show instance because I need the+-- following identity to hold:+--+-- > read . show = id+--+-- This identity is met by the derived instance anyways. A more human readable+-- instance would most likely violate the identity.++-- | Show a bipartition in a human readable format. Use a provided function to+-- extract information of interest.+bpHuman :: Show a => Bipartition a -> String+bpHuman (Bipartition (x, y)) = "(" ++ setShow x ++ "|" ++ setShow y ++ ")"++-- Show the elements of a set in a human readable format.+setShow :: Show a => Set a -> String+setShow = intercalate "," . map show . S.toList++-- -- | Map a function over all elements in the 'Bipartition'.+-- bpMap :: Ord b => (a -> b) -> Bipartition a -> Bipartition b+-- bpMap f (Bipartition (x, y)) = bp (S.map f x) (S.map f y)++-- | For a bifurcating root, get the bipartition induced by the root node.+--+-- Return 'Left' if+-- - the root node is not bifurcating;+-- - a leave set is empty.+bipartition :: Ord a => Tree e a -> Either String (Bipartition a)+bipartition (Node _ _ [x, y]) = bp (S.fromList $ leaves x) (S.fromList $ leaves y)+bipartition _ = Left "bipartition: Root node is not bifurcating."++-- | Get all bipartitions of the tree.+--+-- Return 'Left' if the tree contains duplicate leaves.+bipartitions :: Ord a => Tree e a -> Either String (Set (Bipartition a))+bipartitions t+  | duplicateLeaves t = Left "bipartitions: Tree contains duplicate leaves."+  | otherwise = Right $ bipartitions' S.empty $ S.fromList <$> groups t++-- | Report the complementary leaves for each child.+getComplementaryLeaves ::+  (Ord a) =>+  -- Complementary leaves.+  Set a ->+  -- Tree with node labels storing leaves.+  Tree e (Set a) ->+  [Set a]+getComplementaryLeaves p (Node _ _ ts) =+  [ S.unions $ p : take i lvsChildren ++ drop (i + 1) lvsChildren+    | i <- [0 .. (n -1)]+  ]+  where+    n = length ts+    lvsChildren = map label ts++-- See 'bipartitions', but do not check if leaves are unique, nor if+-- bipartitions are valid.+bipartitions' :: Ord a => Set a -> Tree e (Set a) -> Set (Bipartition a)+bipartitions' p (Node _ p' []) = either (const S.empty) S.singleton $ bp p p'+bipartitions' p t@(Node _ p' ts) =+  S.unions $+    either (const S.empty) S.singleton (bp p p') :+      [bipartitions' c s | (c, s) <- zip cs ts]+  where+    cs = getComplementaryLeaves p t++-- TODO: Unrooted? See module comment of Distance.hs.++-- | Convert a tree into a 'Map' from each 'Bipartition' to the branch inducing+-- the respective 'Bipartition'.+--+-- Since the induced bipartitions of the daughter branches of a bifurcating root+-- node are equal, the branches leading to the root have to be combined in this+-- case. See http://evolution.genetics.washington.edu/phylip/doc/treedist.html+-- and how unrooted trees should be handled.+--+-- Further, branches connected to degree two nodes also induce the same+-- bipartitions and have to be combined.+--+-- For combining branches, a binary function is required. This requirement is+-- encoded in the 'Semigroup' type class constraint (see 'prune').+--+-- Return 'Left' if the tree contains duplicate leaves.+bipartitionToBranch ::+  (Semigroup e, Ord a) =>+  Tree e a ->+  Either String (Map (Bipartition a) e)+bipartitionToBranch t+  | duplicateLeaves t = Left "bipartitionToBranch: Tree contains duplicate leaves."+  | otherwise = Right $ bipartitionToBranch' S.empty pTree+  where+    pTree = S.fromList <$> groups t++-- When calculating the map, branches separated by various degree two nodes have+-- to be combined. Hence, not only the complementary leaves, but also the branch+-- label itself have to be passed along.+bipartitionToBranch' ::+  (Semigroup e, Ord a) =>+  -- Complementary leaves.+  Set a ->+  -- Partition tree.+  Tree e (Set a) ->+  Map (Bipartition a) e+bipartitionToBranch' p t@(Node b p' ts) =+  M.unionsWith (<>) $+    either (const M.empty) (`M.singleton` b) (bp p p') :+      [bipartitionToBranch' c s | (c, s) <- zip cs ts]+  where+    cs = getComplementaryLeaves p t
+ src/ELynx/Tree/Distance.hs view
@@ -0,0 +1,139 @@+-- |+-- Module      :  ELynx.Tree.Distance+-- Description :  Compute distances between trees+-- 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 17:15:54 2019.+--+-- Various distance functions for phylogenetic trees (and trees with branch+-- lengths in general).+--+-- The functions provided in this module return distances for __unrooted__+-- trees. See comments of 'symmetric', 'branchScore', and 'bipartitionToBranch',+-- as well as the documentation of+-- [treedist](http://evolution.genetics.washington.edu/phylip/doc/treedist.html).+--+-- It is a little unfortunate that 'Tree' data type, which represents rooted+-- trees, is also used in this module. However, rooted trees are much easier to+-- handle. In the future, a separate data type for unrooted trees may be+-- introduced. In theory, this is quite straight forward, for example, using+-- algebraic graphs. Difficulties may arise because the branches of an unrooted+-- tree are undirected.+module ELynx.Tree.Distance+  ( symmetric,+    incompatibleSplits,+    branchScore,+  )+where++-- adjacent,++import Data.Bifunctor+import Data.List+import qualified Data.Map as M+import Data.Monoid+import Data.Set (Set)+import qualified Data.Set as S+import ELynx.Tree.Bipartition+import ELynx.Tree.Measurable+import ELynx.Tree.Partition+import ELynx.Tree.Rooted++-- Symmetric difference between two 'Set's.+symmetricDifference :: Ord a => Set a -> Set a -> Set a+symmetricDifference xs ys = S.difference xs ys `S.union` S.difference ys xs++-- | Symmetric (Robinson-Foulds) distance between two trees.+--+-- Although a rooted tree data type is used, the distance between the unrooted+-- trees is returned.+--+-- Return 'Nothing' if the trees contain different leaves.+--+-- XXX: Comparing a list of trees may recompute bipartitions.+symmetric :: Ord a => Tree e1 a -> Tree e2 a -> Either String Int+symmetric t1 t2+  | S.fromList (leaves t1) /= S.fromList (leaves t2) = Left "symmetric: Trees contain different leaves."+  | otherwise = do+    bps1 <- bipartitions t1+    bps2 <- bipartitions t2+    return $ length $ symmetricDifference bps1 bps2++countIncompatibilities :: (Show a, Ord a) => Set (Bipartition a) -> Set (Partition a) -> Int+countIncompatibilities bs ms =+  foldl' (\i b -> if any (compatible $ bpToMp b) ms then i else i + 1) 0 bs++-- | Number of incompatible splits.+--+-- Similar to 'symmetric' but all bipartitions induced by multifurcations are+-- considered. For a detailed description of how the distance is calculated, see+-- 'ELynx.Tree.Bipartition.bipartitionCompatible'.+--+-- A multifurcation on a tree may (but not necessarily does) represent missing+-- information about the order of bifurcations. In this case, it is interesting+-- to get a set of compatible bifurcations of the tree. For example, the star tree+--+-- > (A,B,C,D);+--+-- induces the following bipartitions:+--+-- > A|BCD+-- > B|ACD+-- > C|ABD+-- > D|ABC+--+-- However, the tree is additionally compatible with the following hidden+-- bipartitions:+--+-- > AB|CD+-- > AC|BD+-- > AD|BC+--+-- For an explanation of how compatibility of partitions is checked, see+-- 'compatible'. Before using 'compatible', bipartitions are simply converted to+-- partitions with two subsets.+--+-- A bipartition is incompatible with a tree if it is incompatible with all+-- induced multifurcations of the tree.+--+-- XXX: Comparing a list of trees with this function recomputes bipartitions.+incompatibleSplits :: (Show a, Ord a) => Tree e1 a -> Tree e2 a -> Either String Int+incompatibleSplits t1 t2+  | S.fromList (leaves t1) /= S.fromList (leaves t2) =+    Left "incompatibleSplits: Trees do not have equal leaf sets."+  | otherwise = do+    -- Bipartitions.+    bs1 <- bipartitions t1+    bs2 <- bipartitions t2+    -- traceShowM $ "bs1" ++ show (S.map bpHuman bs1)+    -- traceShowM $ "bs2" ++ show (S.map bpHuman bs2)+    let -- Putative incompatible bipartitions of trees one and two, respectively.+        putIncBs1 = bs1 S.\\ bs2+        putIncBs2 = bs2 S.\\ bs1+    -- Partitions.+    ms1 <- partitions t1+    ms2 <- partitions t2+    -- traceShowM $ "putIncBs1 " ++ show (S.map bpHuman putIncBs1)+    -- traceShowM $ "putIncBs2 " ++ show (S.map bpHuman putIncBs2)+    return $ countIncompatibilities putIncBs1 ms2 + countIncompatibilities putIncBs2 ms1++-- | Compute branch score distance between two trees.+--+-- Although a rooted tree data type is used, the distance between the unrooted+-- trees is returned.+--+-- XXX: Comparing a list of trees with this function recomputes bipartitions.+branchScore :: (Measurable e1, Measurable 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+    bpToBr1 <- bipartitionToBranch $ first (Sum . getLen) t1+    bpToBr2 <- bipartitionToBranch $ first (Sum . getLen) t2+    let dBs = M.unionWith (-) bpToBr1 bpToBr2+        dsSquared = foldl' (\acc e -> acc + e * e) 0 dBs+    return $ sqrt $ getSum dsSquared
+ src/ELynx/Tree/Distribution/BirthDeath.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+--   Module      :  ELynx.Tree.Distribution.BirthDeath+--   Description :  Birth and death distribution+--   Copyright   :  (c) Dominik Schrempf 2018+--   License     :  GPL-3.0-or-later+--+--   Maintainer  :  dominik.schrempf@gmail.com+--   Stability   :  unstable+--   Portability :  portable+--+-- Creation date: Tue Feb 13 13:16:18 2018.+--+-- See Gernhard, T. (2008). The conditioned reconstructed process. Journal of+-- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.+--+-- Distribution of the values of the point process such that it corresponds to+-- reconstructed trees under the birth and death process.+module ELynx.Tree.Distribution.BirthDeath+  ( BirthDeathDistribution (..),+    cumulative,+    density,+    quantile,+  )+where++import Data.Data+  ( Data,+    Typeable,+  )+import ELynx.Tree.Distribution.Types+import GHC.Generics (Generic)+import qualified Statistics.Distribution as D++-- | Distribution of the values of the point process such that it corresponds to+-- a reconstructed tree of the birth and death process.+data BirthDeathDistribution = BDD+  { -- | Time to origin of the tree.+    bddTOr :: Time,+    -- | Birth rate.+    bddLa :: Rate,+    -- | Death rate.+    bddMu :: Rate+  }+  deriving (Eq, Typeable, Data, Generic)++instance D.Distribution BirthDeathDistribution where+  cumulative = cumulative++-- | Cumulative distribution function Eq. (3).+cumulative :: BirthDeathDistribution -> Time -> Double+cumulative (BDD t l m) x+  | x <= 0 = 0+  | x > t = 1+  | otherwise = t1 * t2+  where+    d = l - m+    t1 = (1.0 - exp (- d * x)) / (l - m * exp (- d * x))+    t2 = (l - m * exp (- d * t)) / (1.0 - exp (- d * t))++instance D.ContDistr BirthDeathDistribution where+  density = density+  quantile = quantile++-- | Density function Eq. (2).+density :: BirthDeathDistribution -> Time -> Double+density (BDD t l m) x+  | x < 0 = 0+  | x > t = 0+  | otherwise = d ** 2 * t1 * t2+  where+    d = l - m+    t1 = exp (- d * x) / ((l - m * exp (- d * x)) ** 2)+    t2 = (l - m * exp (- d * t)) / (1.0 - exp (- d * t))++-- | Inverted cumulative probability distribution 'cumulative'. See also+-- 'D.ContDistr'.+quantile :: BirthDeathDistribution -> Double -> Time+quantile (BDD t l m) p+  | p >= 0 && p <= 1 =+    res+  | otherwise =+    error $+      "PointProcess.quantile: p must be in range [0,1] but got "+        ++ show p+        ++ "."+  where+    d = l - m+    t2 = (l - m * exp (- d * t)) / (1.0 - exp (- d * t))+    res = (-1.0 / d) * log ((1.0 - p * l / t2) / (1.0 - p * m / t2))++instance D.ContGen BirthDeathDistribution where+  genContVar = D.genContinuous
+ src/ELynx/Tree/Distribution/BirthDeathCritical.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+--   Module      :  ELynx.Tree.Distribution.BirthDeathCritical+--   Description :  Birth and death distribution+--   Copyright   :  (c) Dominik Schrempf 2018+--   License     :  GPL-3.0-or-later+--+--   Maintainer  :  dominik.schrempf@gmail.com+--   Stability   :  unstable+--   Portability :  portable+--+-- Creation date: Tue Feb 13 13:16:18 2018.+--+-- See Gernhard, T. (2008). The conditioned reconstructed process. Journal of+-- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.+--+-- Distribution of the values of the point process such that it corresponds to+-- reconstructed trees under the birth and death process; critical birth and death+-- process with lambda=mu.+module ELynx.Tree.Distribution.BirthDeathCritical+  ( BirthDeathCriticalDistribution (..),+    cumulative,+    density,+    quantile,+  )+where++import Data.Data+  ( Data,+    Typeable,+  )+import ELynx.Tree.Distribution.Types+import GHC.Generics (Generic)+import qualified Statistics.Distribution as D++-- | Distribution of the values of the point process such that it corresponds to+-- a reconstructed tree of the birth and death process.+data BirthDeathCriticalDistribution = BDCD+  { -- | Time to origin of the tree.+    bdcdTOr :: Time,+    -- | Birth and death rate.+    bdcdLa :: Rate+  }+  deriving (Eq, Typeable, Data, Generic)++instance D.Distribution BirthDeathCriticalDistribution where+  cumulative = cumulative++-- | Cumulative distribution function section 2.1.2, second formula.+cumulative :: BirthDeathCriticalDistribution -> Time -> Double+cumulative (BDCD t l) x+  | x <= 0 = 0+  | x > t = 1+  | otherwise = x / (1.0 + l * x) * (1.0 + l * t) / t++instance D.ContDistr BirthDeathCriticalDistribution where+  density = density+  quantile = quantile++-- | Density function section 2.1.2, first formula.+density :: BirthDeathCriticalDistribution -> Time -> Double+density (BDCD t l) x+  | x < 0 = 0+  | x > t = 0+  | otherwise = (1.0 + l * t) / (t * (1.0 + l * x) ** 2)++-- | Inverted cumulative probability distribution 'cumulative'. See also+-- 'D.ContDistr'.+quantile :: BirthDeathCriticalDistribution -> Double -> Time+quantile (BDCD t l) p+  | p >= 0 && p <= 1 =+    res+  | otherwise =+    error $+      "PointProcess.quantile: p must be in [0,1] range. Got: "+        ++ show p+        ++ "."+  where+    res = p * t / (1 + l * t - l * p * t)++instance D.ContGen BirthDeathCriticalDistribution where+  genContVar = D.genContinuous
+ src/ELynx/Tree/Distribution/BirthDeathCriticalNoTime.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+--   Module      :  ELynx.Tree.Distribution.BirthDeathCriticalNoTime+--   Description :  Birth and death distribution+--   Copyright   :  (c) Dominik Schrempf 2018+--   License     :  GPL-3.0-or-later+--+--   Maintainer  :  dominik.schrempf@gmail.com+--   Stability   :  unstable+--   Portability :  portable+--+-- Creation date: Tue Feb 13 13:16:18 2018.+--+-- See Gernhard, T. (2008). The conditioned reconstructed process. Journal of+-- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.+--+-- Distribution of the values of the point process such that it corresponds to+-- reconstructed trees under the birth and death process; critical birth and death+-- process with lambda=mu.+module ELynx.Tree.Distribution.BirthDeathCriticalNoTime+  ( BirthDeathCriticalNoTimeDistribution (..),+    cumulative,+    density,+    quantile,+  )+where++import Data.Data+  ( Data,+    Typeable,+  )+import ELynx.Tree.Distribution.Types+import GHC.Generics (Generic)+import qualified Statistics.Distribution as D++-- | Distribution of the values of the point process such that it corresponds to+-- a reconstructed tree of the birth and death process.+newtype BirthDeathCriticalNoTimeDistribution = BDCNTD+  { -- | Birth and death rate.+    bdcntdLa :: Rate+  }+  deriving (Eq, Typeable, Data, Generic)++instance D.Distribution BirthDeathCriticalNoTimeDistribution where+  cumulative = cumulative++-- | Cumulative distribution function section 2.1.2, second formula.+cumulative :: BirthDeathCriticalNoTimeDistribution -> Time -> Double+cumulative (BDCNTD l) x+  | x <= 0 = 0+  | otherwise = x * l / (1.0 + x * l)++instance D.ContDistr BirthDeathCriticalNoTimeDistribution where+  density = density+  quantile = quantile++-- | Density function section 2.1.2, first formula; t cancels out because it is+-- expected to be much larger than 1.0; because t \in [0, \infty].+density :: BirthDeathCriticalNoTimeDistribution -> Time -> Double+density (BDCNTD l) x+  | x < 0 = 0+  | otherwise = l / ((1.0 + x * l) ** 2)++-- | Inverted cumulative probability distribution 'cumulative'. See also+-- 'D.ContDistr'.+quantile :: BirthDeathCriticalNoTimeDistribution -> Double -> Time+quantile (BDCNTD l) p+  | p >= 0 && p <= 1 =+    p / (l - l * p)+  | otherwise =+    error $ "PointProcess.quantile: p must be in [0,1]. Got: " ++ show p ++ "."++instance D.ContGen BirthDeathCriticalNoTimeDistribution where+  genContVar = D.genContinuous
+ src/ELynx/Tree/Distribution/BirthDeathNearlyCritical.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+--   Module      :  ELynx.Tree.Distribution.BirthDeathNearlyCritical+--   Description :  Birth and death distribution+--   Copyright   :  (c) Dominik Schrempf 2018+--   License     :  GPL-3.0-or-later+--+--   Maintainer  :  dominik.schrempf@gmail.com+--   Stability   :  unstable+--   Portability :  portable+--+-- Creation date: Tue Feb 13 13:16:18 2018.+--+-- See Gernhard, T. (2008). The conditioned reconstructed process. Journal of+-- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.+--+-- Distribution of the values of the point process such that it corresponds to+-- reconstructed trees under the birth and death process; nearly critical birth and+-- death process with lambda~mu.+--+-- Basically, this is a Taylor expansion of Eq. (2) and Eq. (3).+module ELynx.Tree.Distribution.BirthDeathNearlyCritical+  ( BirthDeathNearlyCriticalDistribution (..),+    cumulative,+    density,+    quantile,+  )+where++import Data.Data+  ( Data,+    Typeable,+  )+import ELynx.Tree.Distribution.Types+import GHC.Generics (Generic)+import qualified Statistics.Distribution as D++-- | Distribution of the values of the point process such that it corresponds to+-- a reconstructed tree of the birth and death process.+data BirthDeathNearlyCriticalDistribution = BDNCD+  { -- | Time to origin of the tree.+    bdncdTOr :: Time,+    -- | Birth and death rate.+    bdncdLa :: Rate,+    -- | Birth and death rate.+    bdncdMu :: Rate+  }+  deriving (Eq, Typeable, Data, Generic)++instance D.Distribution BirthDeathNearlyCriticalDistribution where+  cumulative = cumulative++-- | Cumulative distribution function section 2.1.2, second formula.+cumulative :: BirthDeathNearlyCriticalDistribution -> Time -> Double+cumulative (BDNCD t l m) s+  | s <= 0 = 0+  | s > t = 1+  | otherwise = o0 + o1+  where+    o0 = s * (1.0 + t * l) / t / (1.0 + s * l)+    o1 = (- s * s + s * t) * (m - l) / (2.0 * t * (1.0 + s * l) ** 2)++instance D.ContDistr BirthDeathNearlyCriticalDistribution where+  density = density+  quantile = quantile++-- | Density function section 2.1.2, first formula.+density :: BirthDeathNearlyCriticalDistribution -> Time -> Double+density (BDNCD t l m) s+  | s < 0 = 0+  | s > t = 0+  | otherwise = o0 + o1+  where+    o0 = (1.0 + t * l) / (t * (1.0 + s * l) ** 2)+    o1 = (-2.0 * s + t - s * t * l) * (m - l) / (2.0 * t * (1.0 + s * l) ** 3)++-- | Inverted cumulative probability distribution 'cumulative'. See also+-- 'D.ContDistr'.+quantile :: BirthDeathNearlyCriticalDistribution -> Double -> Time+quantile (BDNCD t l m) p+  | p >= 0 && p <= 1 =+    res+  | otherwise =+    error $+      "PointProcess.quantile: p must be in [0,1] range. Got: "+        ++ show p+        ++ "."+  where+    den = l * (-3.0 + 2.0 * t * (-1.0 + p) * l) + m+    t1 = (2.0 + t * (l - 4.0 * p * l + m)) / den+    t2Nom =+      4.0+        + t+        * ( l+              * (4.0 + t * l + 8.0 * p * (1.0 + t * l))+              + 2.0+              * (2.0 + t * l - 4.0 * p * (1.0 + t * l))+              * m+              + t+              * m+              * m+          )+    t2 = t2Nom / (den ** 2)+    res = 0.5 * (t1 + sqrt t2)++instance D.ContGen BirthDeathNearlyCriticalDistribution where+  genContVar = D.genContinuous
+ src/ELynx/Tree/Distribution/CoalescentContinuous.hs view
@@ -0,0 +1,26 @@+-- |+-- Module      :  ELynx.Tree.Distribution.CoalescentContinuous+-- Description :  Distribution of coalescent times+-- Copyright   :  (c) Dominik Schrempf 2018+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Wed May 16 12:40:45 2018.+module ELynx.Tree.Distribution.CoalescentContinuous+  ( coalescentDistributionCont,+  )+where++import Numeric.SpecFunctions (choose)+import Statistics.Distribution.Exponential++-- | Distribution of the next coalescent event for a number of samples @n@. The+-- time is measured in units of effective number of population size.+coalescentDistributionCont ::+  -- | Sample size.+  Int ->+  ExponentialDistribution+coalescentDistributionCont n = exponential (choose n 2)
+ src/ELynx/Tree/Distribution/TimeOfOrigin.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+--   Module      :  ELynx.Tree.Distribution.TimeOfOrigin+--   Description :  Distribution of time of origin for birth and death trees+--   Copyright   :  (c) Dominik Schrempf 2018+--   License     :  GPL-3.0-or-later+--+--   Maintainer  :  dominik.schrempf@gmail.com+--   Stability   :  unstable+--   Portability :  portable+--+-- Creation date: Tue Feb 13 13:16:18 2018.+--+-- See Gernhard, T. (2008). The conditioned reconstructed process. Journal of+-- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.+--+-- Distribution of the time of origin for birth and death trees. See corollary 3.3+-- in the paper cited above.+module ELynx.Tree.Distribution.TimeOfOrigin+  ( TimeOfOriginDistribution (..),+    cumulative,+    density,+    quantile,+  )+where++import Data.Data+  ( Data,+    Typeable,+  )+import ELynx.Tree.Distribution.Types+import GHC.Generics (Generic)+import qualified Statistics.Distribution as D++-- | Distribution of the time of origin for a phylogenetic tree evolving under+-- the birth and death process and conditioned on observing n leaves today.+data TimeOfOriginDistribution = TOD+  { -- | Number of leaves of the tree.+    todTN :: Int,+    -- | Birth rate.+    todLa :: Rate,+    -- | Death rate.+    todMu :: Rate+  }+  deriving (Eq, Typeable, Data, Generic)++instance D.Distribution TimeOfOriginDistribution where+  cumulative = cumulative++-- | Cumulative distribution function Corollary 3.3.+cumulative :: TimeOfOriginDistribution -> Time -> Double+cumulative (TOD n l m) x+  | x <= 0 = 0+  | otherwise = te ** fromIntegral n+  where+    d = l - m+    te = l * (1.0 - exp (- d * x)) / (l - m * exp (- d * x))++instance D.ContDistr TimeOfOriginDistribution where+  density = density+  quantile = quantile++-- | The density function Eq. (5).+density :: TimeOfOriginDistribution -> Time -> Double+density (TOD nn l m) x+  | x < 0 = 0+  | otherwise = n * l ** n * d ** 2 * t1 ** (n - 1.0) * ex / (t2 ** (n + 1.0))+  where+    d = l - m+    n = fromIntegral nn+    ex = exp (- d * x)+    t1 = 1.0 - ex+    t2 = l - m * ex++-- | The inverted cumulative probability distribution 'cumulative'. See also+-- 'D.ContDistr'.+quantile :: TimeOfOriginDistribution -> Double -> Time+quantile (TOD n' l m) p+  | p >= 0 && p <= 1 =+    -1.0 / d * log (t1 / t2)+  | otherwise =+    error $+      "PointProcess.quantile: p must be in [0,1] range. Got: "+        ++ show p+        ++ "."+  where+    d = l - m+    n = fromIntegral n'+    t1 = l * (1.0 - p ** (1.0 / n))+    t2 = l - p ** (1.0 / n) * m++instance D.ContGen TimeOfOriginDistribution where+  genContVar = D.genContinuous
+ src/ELynx/Tree/Distribution/TimeOfOriginNearCritical.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+--   Module      :  ELynx.Tree.Distribution.TimeOfOriginNearCritical+--   Description :  Distribution of time of origin for birth and death trees+--   Copyright   :  (c) Dominik Schrempf 2018+--   License     :  GPL-3.0-or-later+--+--   Maintainer  :  dominik.schrempf@gmail.com+--   Stability   :  unstable+--   Portability :  portable+--+-- Creation date: Tue Feb 13 13:16:18 2018.+--+-- See Gernhard, T. (2008). The conditioned reconstructed process. Journal of+-- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.+--+-- Distribution of the time of origin for birth and death trees. See corollary 3.3+-- in the paper cited above.+module ELynx.Tree.Distribution.TimeOfOriginNearCritical+  ( TimeOfOriginNearCriticalDistribution (..),+    cumulative,+    density,+    quantile,+  )+where++import Data.Data+  ( Data,+    Typeable,+  )+import ELynx.Tree.Distribution.Types+import GHC.Generics (Generic)+import qualified Statistics.Distribution as D++-- | Distribution of the time of origin for a phylogenetic tree evolving under+-- the birth and death process and conditioned on observing n leaves today.+data TimeOfOriginNearCriticalDistribution = TONCD+  { -- | Number of leaves of the tree.+    todTN :: Int,+    -- | Birth rate.+    todLa :: Rate,+    -- | Death rate.+    todMu :: Rate+  }+  deriving (Eq, Typeable, Data, Generic)++instance D.Distribution TimeOfOriginNearCriticalDistribution where+  cumulative = cumulative++-- | Cumulative distribution function; see Mathematica notebook.+cumulative :: TimeOfOriginNearCriticalDistribution -> Time -> Double+cumulative (TONCD n' l m) t+  | t <= 0 = 0+  | otherwise = t1 + t2+  where+    d = l - m+    n = fromIntegral n'+    t1 = (t * l / (1.0 + t * l)) ** n+    t2 = (n * t * t1) * d / (2.0 * (1.0 + t * l))++instance D.ContDistr TimeOfOriginNearCriticalDistribution where+  density = density+  quantile = quantile++-- | The density function Eq. (5).+density :: TimeOfOriginNearCriticalDistribution -> Time -> Double+density (TONCD n' l m) t+  | t < 0 = 0+  | otherwise = nom / den+  where+    n = fromIntegral n'+    nom =+      n * (t * l / (1 + t * l)) ** n * (2 + (3 + n) * t * l - (1 + n) * t * m)+    den = 2 * t * (1 + t * l) ** 2++-- | The inverted cumulative probability distribution 'cumulative'. See also+-- 'D.ContDistr'.+quantile :: TimeOfOriginNearCriticalDistribution -> Double -> Time+quantile (TONCD n' l m) p+  | p >= 0 && p <= 1 =+    t1 + t2nom / t2den+  | otherwise =+    error $+      "PointProcess.quantile: p must be in [0,1] range. Got: "+        ++ show p+        ++ "."+  where+    n = fromIntegral n'+    t1 = - p ** (1 / n) / ((-1 + p ** (1 / n)) * l)+    t2nom = p ** (2 / n) * (m - l)+    t2den = 2 * (-1 + p ** (1 / n)) ** 2 * l ** 2++instance D.ContGen TimeOfOriginNearCriticalDistribution where+  genContVar = D.genContinuous
+ src/ELynx/Tree/Distribution/Types.hs view
@@ -0,0 +1,22 @@+-- |+-- Module      :  ELynx.Tree.Distribution.Types+-- Description :  Data types for distributions on trees+-- Copyright   :  (c) Dominik Schrempf 2018+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Wed May 16 12:21:57 2018.+module ELynx.Tree.Distribution.Types+  ( Time,+    Rate,+  )+where++-- | Branch lengths are measured in time.+type Time = Double++-- | Birth or death rates.+type Rate = Double
+ src/ELynx/Tree/Export/Newick.hs view
@@ -0,0 +1,53 @@+-- |+-- Module      :  ELynx.Tree.Export.Newick+-- Description :  Export tree objects to Newick format+-- 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 13:51:47 2019.+--+-- Some functions are inspired by+-- [Biobase.Newick.Import](https://hackage.haskell.org/package/BiobaseNewick).+--+-- See nomenclature in 'ELynx.Tree'.+module ELynx.Tree.Export.Newick+  ( toNewick,+    toNewickBuilder,+  )+where++import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Lazy.Char8 as BL+import Data.List (intersperse)+import ELynx.Tree.Named+import ELynx.Tree.Phylogeny+import ELynx.Tree.Rooted++-- | See 'toNewick'.+toNewickBuilder :: Named a => Tree Phylo a -> BB.Builder+toNewickBuilder t = go t <> BB.char8 ';'+  where+    go (Node b l []) = lbl b l+    go (Node b l ts) =+      BB.char8 '('+        <> mconcat (intersperse (BB.char8 ',') $ map go ts)+        <> BB.char8 ')'+        <> lbl b l+    mBrSupBuilder x = maybe mempty (\bs -> BB.char8 '[' <> BB.doubleDec bs <> BB.char8 ']') (brSup x)+    mBrLenBuilder x = maybe mempty (\bl -> BB.char8 ':' <> BB.doubleDec bl) (brLen x)+    lbl x y =+      BB.lazyByteString (getName y)+        <> mBrLenBuilder x+        -- After reading several discussion, I go for the "more semantical+        -- form" with branch support values in square brackets.+        <> mBrSupBuilder x++-- | General conversion of a tree into a Newick 'BL.Bytestring'. Use provided+-- functions to extract node labels and branch lengths builder objects. See also+-- Biobase.Newick.Export.+toNewick :: Named a => Tree Phylo a -> BL.ByteString+toNewick = BB.toLazyByteString . toNewickBuilder
+ src/ELynx/Tree/Export/Nexus.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.Export.Nexus+-- Description :  Export trees to Nexus files+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Tue Apr 28 20:24:19 2020.+module ELynx.Tree.Export.Nexus+  ( toNexusTrees,+  )+where++import qualified Data.ByteString.Lazy.Char8 as BL+import ELynx.Export.Nexus+import ELynx.Tree.Export.Newick+import ELynx.Tree.Named+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 ts = toNexus "TREES" (map tree ts)++tree :: Named a => (BL.ByteString, Tree Phylo a) -> BL.ByteString+tree (n, t) = "  TREE " <> n <> " = " <> toNewick t
+ src/ELynx/Tree/Import/Newick.hs view
@@ -0,0 +1,235 @@+{-# LANGUAGE DeriveGeneric #-}++-- Module      :  ELynx.Tree.Import.Newick+-- Description :  Import Newick trees+-- 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:56:27 2019.+--+-- Some functions are inspired by+-- [Biobase.Newick.Import](https://hackage.haskell.org/package/BiobaseNewick).+--+-- [Specifications](http://evolution.genetics.washington.edu/phylip/newicktree.html)+--+-- In particular, no conversion from _ to (space) is done right now.+--+-- For a description of rooted 'Tree's, please see the 'ELynx.Tree.Rooted'++-- |+-- module header.+module ELynx.Tree.Import.Newick+  ( NewickFormat (..),+    description,+    newick,+    oneNewick,+    someNewick,+  )+where++import Control.Applicative+import Data.Aeson (FromJSON, ToJSON)+import Data.Attoparsec.ByteString.Char8+import qualified Data.ByteString.Char8 as BS+import ELynx.Tree.Measurable+import ELynx.Tree.Phylogeny+import ELynx.Tree.Rooted hiding (forest, label)+import ELynx.Tree.Supported+import GHC.Generics+import Prelude hiding (takeWhile)++-- | Newick tree format.+--+-- >>> unlines $ map (("- " <>) . description) (allValues :: [NewickFormat])+-- - Standard: Branch support values are stored in square brackets after branch lengths.+-- - IqTree:   Branch support values are stored as node names after the closing bracket of forests.+-- - 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.+data NewickFormat = Standard | IqTree | RevBayes+  deriving (Eq, Show, Read, Bounded, Enum, Generic)++instance FromJSON NewickFormat++instance ToJSON NewickFormat++-- | Short description of the supported Newick formats.+description :: NewickFormat -> String+description Standard =+  "Standard: Branch support values are stored in square brackets after branch lengths."+description IqTree =+  "IqTree:   Branch support values are stored as node names after the closing bracket of forests."+description RevBayes =+  "RevBayes: Key-value pairs is provided in square brackets after node names as well as branch lengths. XXX: Key value pairs are ignored at the moment."++-- | Parse a single Newick tree. Also succeeds when more trees follow.+newick :: NewickFormat -> Parser (Tree Phylo BS.ByteString)+newick Standard = newickStandard+newick IqTree = newickIqTree+newick RevBayes = newickRevBayes++-- | Parse a single Newick tree. Fails when end of file is not reached.+oneNewick :: NewickFormat -> Parser (Tree Phylo BS.ByteString)+oneNewick Standard = oneNewickStandard+oneNewick IqTree = oneNewickIqTree+oneNewick RevBayes = oneNewickRevBayes++-- | Parse one or more Newick trees until end of file.+someNewick :: NewickFormat -> Parser (Forest Phylo BS.ByteString)+someNewick Standard = someNewickStandard+someNewick IqTree = someNewickIqTree+someNewick RevBayes = someNewickRevBayes++-- Parse a single Newick tree. Also succeeds when more trees follow.+newickStandard :: Parser (Tree Phylo BS.ByteString)+newickStandard = skipWhile isSpace *> tree <* char ';' <* skipWhile isSpace <?> "newickStandard"++-- Parse a single Newick tree. Fails when end of file is not reached.+oneNewickStandard :: Parser (Tree Phylo BS.ByteString)+oneNewickStandard = newickStandard <* endOfInput <?> "oneNewickStandard"++-- Parse one ore more Newick trees until end of file.+someNewickStandard :: Parser (Forest Phylo BS.ByteString)+someNewickStandard = some newickStandard <* endOfInput <?> "someNewickStandard"++tree :: Parser (Tree Phylo BS.ByteString)+tree = branched <|> leaf <?> "tree"++branched :: Parser (Tree Phylo BS.ByteString)+branched = (<?> "branched") $ do+  f <- forest+  n <- name+  p <- phylo+  return $ Node p n f++-- A 'forest' is a set of trees separated by @,@ and enclosed by parentheses.+forest :: Parser (Forest Phylo BS.ByteString)+forest = char '(' *> (tree `sepBy1` char ',') <* char ')' <?> "forest"++-- A 'leaf' has a 'name' and a 'phylo' branch.+leaf :: Parser (Tree Phylo BS.ByteString)+leaf = (<?> "leaf") $ do+  n <- name+  p <- phylo+  return $ Node p n []++nameChar :: Char -> Bool+nameChar c = c `notElem` " :;()[],"++-- A name can be any string of printable characters except blanks, colons,+-- semicolons, parentheses, and square brackets (and commas).+name :: Parser BS.ByteString+name = takeWhile nameChar <?> "name"++phylo :: Parser Phylo+phylo = Phylo <$> optional branchLength <*> optional branchSupport <?> "phylo"++-- Branch length.+branchLength :: Parser BranchLength+branchLength = char ':' *> double <?> "branchLength"++branchSupport :: Parser BranchSupport+branchSupport = (<?> "branchSupport") $+  do+    _ <- char '['+    s <- double+    _ <- char ']'+    return s++--------------------------------------------------------------------------------+-- IQ-TREE.++-- IQ-TREE stores the branch support as node names after the closing bracket of+-- a forest. Parse a single Newick tree. Also succeeds when more trees follow.+newickIqTree :: Parser (Tree Phylo BS.ByteString)+newickIqTree = skipWhile isSpace *> treeIqTree <* char ';' <* skipWhile isSpace <?> "newickIqTree"++-- See 'newickIqTree'. Parse a single Newick tree. Fails when end of file is not+-- reached.+oneNewickIqTree :: Parser (Tree Phylo BS.ByteString)+oneNewickIqTree = newickIqTree <* endOfInput <?> "oneNewickIqTree"++-- See 'newickIqTree'. Parse one ore more Newick trees until end of file.+someNewickIqTree :: Parser (Forest Phylo BS.ByteString)+someNewickIqTree = some newickIqTree <* endOfInput <?> "someNewickIqTree"++-- IQ-TREE stores the branch support as node names after the closing bracket of a forest.+treeIqTree :: Parser (Tree Phylo BS.ByteString)+treeIqTree = branchedIqTree <|> leaf <?> "treeIqTree"++-- IQ-TREE stores the branch support as node names after the closing bracket of a forest.+branchedIqTree :: Parser (Tree Phylo BS.ByteString)+branchedIqTree = (<?> "branchedIqTree") $ do+  f <- forestIqTree+  s <- optional double+  n <- name+  b <- optional branchLength+  return $ Node (Phylo b s) n f++-- IQ-TREE stores the branch support as node names after the closing bracket of a forest.+forestIqTree :: Parser (Forest Phylo BS.ByteString)+forestIqTree = (<?> "forestIqTree") $ do+  _ <- char '('+  f <- treeIqTree `sepBy1` char ','+  _ <- char ')'+  return f++--------------------------------------------------------------------------------+-- RevBayes.++-- RevBayes uses square brackets and key-value pairs to define information+-- about nodes and branches. Parse a single Newick tree. Also succeeds when more+-- trees follow.+--+-- XXX: Key value pairs are ignored at the moment.+newickRevBayes :: Parser (Tree Phylo BS.ByteString)+newickRevBayes =+  skipWhile isSpace *> optional brackets *> treeRevBayes <* char ';' <* skipWhile isSpace <?> "newickRevBayes"++-- See 'newickRevBayes'. Parse a single Newick tree. Fails when end of file is+-- not reached.+oneNewickRevBayes :: Parser (Tree Phylo BS.ByteString)+oneNewickRevBayes = newickRevBayes <* endOfInput <?> "oneNewickRevBayes"++-- See 'newickRevBayes'. Parse one ore more Newick trees until end of file.+someNewickRevBayes :: Parser (Forest Phylo BS.ByteString)+someNewickRevBayes = some newickRevBayes <* endOfInput <?> "someNewickRevBayes"++treeRevBayes :: Parser (Tree Phylo BS.ByteString)+treeRevBayes = branchedRevBayes <|> leafRevBayes <?> "treeRevBayes"++branchedRevBayes :: Parser (Tree Phylo BS.ByteString)+branchedRevBayes = (<?> "branchedRevgBayes") $ do+  f <- forestRevBayes+  n <- nameRevBayes+  b <- optional branchLengthRevBayes+  return $ Node (Phylo b Nothing) n f++forestRevBayes :: Parser (Forest Phylo BS.ByteString)+forestRevBayes = (<?> "forestRevBayes") $ do+  _ <- char '('+  f <- treeRevBayes `sepBy1` char ','+  _ <- char ')'+  return f++nameRevBayes :: Parser BS.ByteString+nameRevBayes = name <* optional brackets <?> "nameRevBayes"++branchLengthRevBayes :: Parser BranchLength+branchLengthRevBayes = branchLength <* optional brackets <?> "branchLengthRevBayes"++leafRevBayes :: Parser (Tree Phylo BS.ByteString)+leafRevBayes = (<?> "leafRevBayes") $ do+  n <- nameRevBayes+  b <- optional branchLengthRevBayes+  return $ Node (Phylo b Nothing) n []++-- Drop anything between brackets.+brackets :: Parser ()+brackets = (<?> "brackets") $ do+  _ <- char '['+  _ <- takeWhile (/= ']')+  _ <- char ']'+  return ()
+ src/ELynx/Tree/Import/Nexus.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.Import.Nexus+-- Description :  Import trees from Nexus files+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Tue Apr 28 17:44:13 2020.+module ELynx.Tree.Import.Nexus+  ( nexusTrees,+  )+where++import Control.Applicative+import Data.Attoparsec.ByteString.Char8+import qualified Data.ByteString.Char8 as BS+import ELynx.Import.Nexus+import ELynx.Tree.Import.Newick+import ELynx.Tree.Phylogeny+import ELynx.Tree.Rooted+import Prelude hiding (takeWhile)++-- | Parse a Nexus files with a TREES block.+nexusTrees :: NewickFormat -> Parser [(BS.ByteString, Tree Phylo BS.ByteString)]+nexusTrees = nexus . trees++trees :: NewickFormat -> Block [(BS.ByteString, Tree Phylo BS.ByteString)]+trees f = Block "TREES" (some $ namedNewick f)++namedNewick :: NewickFormat -> Parser (BS.ByteString, Tree Phylo BS.ByteString)+namedNewick f = do+  _ <- skipWhile isSpace+  _ <- string "TREE"+  _ <- skipWhile isSpace+  n <- takeWhile1 (\x -> isAlpha_ascii x || isDigit x)+  _ <- skipWhile isSpace+  _ <- char '='+  _ <- skipWhile isSpace+  t <- newick f+  return (n, t)
+ src/ELynx/Tree/Measurable.hs view
@@ -0,0 +1,124 @@+-- |+-- Module      :  ELynx.Tree.Measurable+-- Description :  Measurable branch 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 branch lengths is not (yet) ensured. To ensure+-- non-negativity, a newtype wrapper could be used, but this would be a major+-- refactor.+module ELynx.Tree.Measurable+  ( BranchLength,+    Measurable (..),+    applyStem,+    getStem,+    setStem,+    height,+    rootHeight,+    distancesOriginLeaves,+    totalBranchLength,+    normalizeBranchLengths,+    normalizeHeight,+    ultrametric,+    makeUltrametric,+  )+where++import Data.Bifoldable+import Data.Bifunctor+import ELynx.Tree.Rooted++-- | Branch length.+type BranchLength = Double++-- | A branch label with measurable and modifiable branch length.+class Measurable e where+  -- | Length of attached branch.+  getLen :: e -> BranchLength++  -- | Set attached branch length.+  setLen :: BranchLength -> e -> e++instance Measurable Double where+  getLen = id+  setLen = const++-- Apply a function to a branch support label.+apply :: Measurable e => (BranchLength -> BranchLength) -> e -> e+apply f l = setLen (f s) l where s = getLen l++-- | Lengthen the stem of a tree.+applyStem :: Measurable e => (BranchLength -> BranchLength) -> Tree e a -> Tree e a+applyStem f t = t {branch = apply f b}+  where+    b = branch t++-- | Get the length of the stem of a tree.+getStem :: Measurable e => Tree e a -> BranchLength+getStem (Node br _ _) = getLen br++-- | Set the length of the stem of a tree.+setStem :: Measurable e => BranchLength -> Tree e a -> Tree e a+setStem x = applyStem (const x)++-- | The maximum distance between origin and leaves.+--+-- The height includes the length of the stem.+height :: Measurable e => Tree e a -> BranchLength+height = maximum . distancesOriginLeaves++-- | The maximum distance between root node and leaves.+rootHeight :: Measurable e => Tree e a -> BranchLength+rootHeight (Node _ _ []) = 0+rootHeight t = maximum $ concatMap distancesOriginLeaves (forest t)++-- | Distances from the origin of a tree to the leaves.+--+-- The distances include the length of the stem.+distancesOriginLeaves :: Measurable e => Tree e a -> [BranchLength]+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 -> BranchLength+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 (apply (/ 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 (apply (/ h)) t+  where+    h = height t++eps :: Double+eps = 1e-12++allNearlyEqual :: [Double] -> Bool+allNearlyEqual [] = True+allNearlyEqual xs = all (\y -> eps > abs (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 => BranchLength -> Tree e a -> Tree e a+    go h' (Node br lb []) = let dh = h - h' - getLen br in Node (apply (+ 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/Named.hs view
@@ -0,0 +1,43 @@+-- |+-- 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+  ( Named (..),+  )+where++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++-- | Data types with names.+class Named a where+  -- Use lazy byte strings because Newick strings are built using chunks.+  getName :: a -> BL.ByteString++instance Named () where+  getName = const BL.empty++instance Named Int where+  getName = BB.toLazyByteString . BB.intDec++instance Named Double where+  getName = BL.fromStrict . toShortest++instance Named Char where+  getName = BB.toLazyByteString . BB.char8++instance Named BL.ByteString where+  getName = id++instance Named BS.ByteString where+  getName = BL.fromStrict
+ src/ELynx/Tree/Partition.hs view
@@ -0,0 +1,137 @@+-- |+-- Module      :  ELynx.Tree.Partition+-- Description :  Partitions on rose trees+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Thu Dec 12 12:58:49 2019.+--+-- A multifurcation induces a 'Partition', similar to branches inducing+-- 'ELynx.Tree.Bipartition's.+module ELynx.Tree.Partition+  ( -- * Data type+    Partition (fromPartition),+    mp,+    mpUnsafe,+    bpToMp,+    mpHuman,++    -- * Work with 'Partition's+    partition,+    partitions,+    compatible,+  )+where++import Data.List hiding (partition)+import Data.Set (Set)+import qualified Data.Set as S+import ELynx.Tree.Bipartition+import ELynx.Tree.Rooted++-- | Each branch of a tree partitions the leaves of the tree into two subsets+-- (see 'ELynx.Tree.Bipartition'). In a similar way, each internal node+-- (excluding the root node) partitions the leaves into three (or more) subsets+-- which is called 'Partition'. If the tree is multifurcating, and a+-- specific node has more than two children, the number of subsets induced by+-- this node is larger than three. Partitions are interesting in that we+-- can use them for calculating incompatible splits, see+-- 'ELynx.Tree.Distance'.+--+-- The order of the subsets of a 'Partition' is meaningless. We ensure by+-- construction that the subsets are ordered, and hence, that equality checks+-- are meaningful.+newtype Partition a = Partition+  { fromPartition :: Set (Set a)+  }+  deriving (Eq, Ord, Show, Read)++-- TODO: Check that list is not empty after filtering.++-- TODO: Rename these functions; don't use 'multi'.++-- | Create a partition.+mp :: Ord a => [Set a] -> Either String (Partition a)+mp xs = case filter (not . S.null) xs of+  [] -> Left "mp: Empty list."+  xs' -> Right $ mpUnsafe xs'++-- | Create a partition.+mpUnsafe :: Ord a => [Set a] -> Partition a+mpUnsafe xs = Partition (S.fromList xs)++-- | Convert a bipartition to a partition.+bpToMp :: Ord a => Bipartition a -> Partition a+bpToMp = mpUnsafe . tupleToList . fromBipartition+  where+    -- Be careful with tuples, because 'toList' does something very weird. It only+    -- takes the second element of the tuple!+    --+    -- toList :: Foldable t => t a -> [a]+    tupleToList (x, y) = [x, y]++-- | Show a partition in a human readable form. Use a provided function to+-- extract the valuable information.+mpHuman :: Show a => Partition a -> String+mpHuman (Partition xs) =+  "(" ++ intercalate "|" (map setShow (S.toList xs)) ++ ")"++-- Show the elements of a set in a human readable format.+setShow :: Show a => Set a -> String+setShow = intercalate "," . map show . S.toList++-- | Get partition defined by the root of the tree.+--+-- Return 'Left' if:+-- - the tree is a leaf;+-- - the tree contains duplicate leaves.+partition :: Ord a => Tree e a -> Either String (Partition a)+partition (Node _ _ []) = Left "partition: Encountered a leaf."+partition t@(Node _ _ ts)+  | duplicateLeaves t = Left "partition: Tree contains duplicate leaves."+  | otherwise = mp $ map (S.fromList . leaves) ts++-- | Get all 'Partition's of a tree.+--+-- Return 'Left' if tree contains duplicate leaves.+partitions :: Ord a => Tree e a -> Either String (Set (Partition a))+partitions t+  | duplicateLeaves t = Left "partitions: Tree contains duplicate leaves."+  | otherwise = Right $ partitions' S.empty $ S.fromList <$> groups t++-- See 'partitions', but do not check if leaves are unique.+partitions' :: Ord a => Set a -> Tree e (Set a) -> Set (Partition a)+partitions' _ (Node _ _ []) = S.empty+partitions' p t@(Node _ _ ts) =+  S.unions $+    either (const S.empty) S.singleton (mp (p : map label ts)) :+    zipWith partitions' cs ts+  where+    cs = getComplementaryLeaves p t++-- | 'Partition's are compatible if they do not contain conflicting+-- information. This function checks if two partitions are compatible with+-- each other. Thereby, a variation of the following algorithm is used:+--+-- @+-- mp1 `compatible` mp2+-- for set1 in mp1:+--   for set2 in mp2:+--     if set1 `S.isSubSetOf` set2:+--       remove set1 from mp1+--     if set2 `S.isSubSetOf` set1:+--       remove set2 from mp2+-- if either mp2 or mp2 is empty, they are compatible+-- @+compatible :: (Show a, Ord a) => Partition a -> Partition a -> Bool+compatible l r = S.null (S.filter (`remove` rs) ls) || S.null (S.filter (`remove` ls) rs)+  where+    ls = fromPartition l+    rs = fromPartition r++remove :: Ord a => Set a -> Set (Set a) -> Bool+remove s = not . any (s `S.isSubsetOf`)
+ src/ELynx/Tree/Phylogeny.hs view
@@ -0,0 +1,487 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}++-- |+-- Module      :  ELynx.Tree.Phylogeny+-- Description :  Phylogenetic trees+-- 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 16:08:54 2019.+--+-- A phylogeny is a 'Tree' with unique leaf labels, and the order of the trees+-- in the sub-forest is considered to be meaningless.+--+-- Internally, however, the underlying 'Tree' data structure stores the+-- sub-forest as a list, which has a specific order. Hence, we have to do some+-- tricks when comparing trees, and tree comparison is slow.+--+-- Also, the uniqueness of the leaves is not ensured by the data type, but has+-- to be checked at runtime. Functions relying on the tree to have unique leaves+-- do perform this check, and return 'Left' with an error message, if the tree+-- has duplicate leaves.+--+-- Note: 'Tree's are rooted.+--+-- 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.+--+-- The bifurcating root of a tree can be changed with 'rootAt' or 'midpoint'; a+-- list of all rooted trees is returned by 'roots'.+--+-- Trees with multifurcating root nodes can be rooted using 'outgroup'.+module ELynx.Tree.Phylogeny+  ( -- * Functions+    equal,+    intersect,+    bifurcating,+    outgroup,+    midpoint,+    roots,+    rootAt,++    -- * Branch labels+    Phylo (..),+    toPhyloTree,+    measurableToPhyloTree,+    supportedToPhyloTree,+    Length (..),+    phyloToLengthTree,+    Support (..),+    phyloToSupportTree,+    phyloToSupportTreeUnsafe,+    PhyloExplicit (..),+    toExplicitTree,+  )+where++import Control.DeepSeq+import Data.Aeson+import Data.Bifoldable+import Data.Bifunctor+import Data.Bitraversable+import Data.List hiding (intersect)+import Data.Maybe+import Data.Monoid+import Data.Semigroup+import Data.Set (Set)+import qualified Data.Set as S+import ELynx.Tree.Bipartition+import ELynx.Tree.Measurable+import ELynx.Tree.Rooted+import ELynx.Tree.Splittable+import ELynx.Tree.Supported+import GHC.Generics++-- | 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) =+  (brL == brR)+    && (lbL == lbR)+    && (length tsL == length tsR)+    && all (`elem` tsR) tsL++-- | Compute the intersection of trees.+--+-- The intersections are the largest subtrees sharing the same leaf set.+--+-- 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)+intersect ts+  | S.null lvsCommon = Left "intersect: Intersection of leaves is empty."+  | otherwise = case sequence [dropLeavesWith (predicate ls) t | (ls, t) <- zip leavesToDrop ts] of+    Nothing -> Left "intersect: A tree is empty."+    Just ts' -> Right ts'+  where+    -- Leaf sets.+    lvss = map (S.fromList . leaves) ts+    -- Common leaf set.+    lvsCommon = foldl1' S.intersection lvss+    -- Leaves to drop for each tree in the forest.+    leavesToDrop = map (S.\\ lvsCommon) lvss+    -- Predicate.+    predicate lvsToDr l = l `S.member` lvsToDr++-- | Check if a tree is bifurcating.+--+-- A Bifurcating tree only contains degree one (leaves) and degree three nodes+-- (internal bifurcating nodes).+bifurcating :: Tree e a -> Bool+bifurcating (Node _ _ []) = True+bifurcating (Node _ _ [x, y]) = bifurcating x && bifurcating y+bifurcating _ = False++-- I believe that manual treatment with 'outgroup' is preferable.++-- -- | Remove multifurcations.+-- --+-- -- A caterpillar like bifurcating structure is used to resolve all+-- -- multifurcations on a tree.+-- --+-- -- Multifurcating nodes are copied and branches are 'split'.+-- resolve :: Splittable e => Tree e a -> Tree e a+-- resolve t@(Node _ _ []) = t+-- resolve (Node br lb [x]) = Node br lb [resolve x]+-- resolve (Node br lb [x, y]) = Node br lb $ map resolve [x, y]+-- 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.+--+-- A bifurcating root node with the provided label is introduced. The affected+-- branch is 'split'.+--+-- Note, the degree of the former root node is decreased by one.+--+-- If the root node is bifurcating, use 'rootAt'.+--+-- Return 'Left' if+-- - the tree has duplicate leaves;+-- - the root node is not multifurcating;+-- - 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'+  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)]++-- The 'midpoint' algorithm is pretty stupid because it calculates all rooted+-- trees and then finds the one minimizing the difference between the heights of+-- the left and right sub tree. Actually, one just needs to move left or right,+-- with the aim to minimize the height difference between the left and right sub+-- tree.++-- | 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 (Node _ _ []) = Left "midpoint: Root node is a leaf."+midpoint (Node _ _ [_]) = Left "midpoint: Root node has degree two."+midpoint t@(Node _ _ [_, _]) = getMidpoint <$> roots t+midpoint _ = Left "midpoint: Root node is multifurcating."++findMinIndex :: Ord a => [a] -> Int+findMinIndex (x : xs) = go (0, x) 1 xs+  where+    go (i, _) _ [] = i+    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 ts = case t of+  (Node br lb [l, r]) ->+    let hl = height l+        hr = height r+        dh = (hl - hr) / 2+     in Node br lb [applyStem (subtract dh) l, applyStem (+ dh) r]+  -- Explicitly use 'error' here, because roots is supposed to return trees with+  -- bifurcating root nodes.+  _ -> error "getMidpoint: Root node is not bifurcating."+  where+    dhs = map getDeltaHeight ts+    i = findMinIndex dhs+    t = ts !! i++-- 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 -> Double+getDeltaHeight (Node _ _ [l, r]) = abs $ height l - height r+-- Explicitly use 'error' here, because roots is supposed to return trees with+-- bifurcating root nodes.+getDeltaHeight _ = error "getDeltaHeight: Root node is not bifurcating."++-- | 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 degree+-- of the root node. Hence, a bifurcating root is required. 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.+--+-- Return 'Left' if the root node is not 'bifurcating'.+roots :: (Semigroup e, Splittable e) => Tree e a -> Either String (Forest e a)+roots (Node _ _ []) = Left "roots: Root node is a leaf."+roots (Node _ _ [_]) = Left "roots: Root node has degree two."+roots t@(Node b c [tL, tR]) = Right $ t : descend b c tR tL ++ descend b c tL tR+roots _ = Left "roots: Root node is multifurcating."++complementaryForests :: Tree e a -> Forest e a -> [Forest e a]+complementaryForests t ts = [t : take i ts ++ drop (i + 1) ts | i <- [0 .. (n -1)]]+  where+    n = length ts++-- From the bifurcating root, descend into one of the two pits.+--+-- descend splitFunction rootBranch rootLabel complementaryTree downwardsTree+descend :: (Semigroup e, Splittable e) => e -> a -> Tree e a -> Tree e a -> Forest e a+descend _ _ _ (Node _ _ []) = []+descend brR lbR tC (Node brD lbD tsD) =+  [ Node brR lbR [Node (split brDd) lbD f, Node (split brDd) lbDd tsDd]+    | (Node brDd lbDd tsDd, f) <- zip tsD cfs+  ]+    ++ concat+      [ descend brR lbR (Node (split brDd) lbD f) (Node (split brDd) lbDd tsDd)+        | (Node brDd lbDd tsDd, f) <- zip tsD cfs+      ]+  where+    brC' = branch tC <> brD+    tC' = tC {branch = brC'}+    cfs = complementaryForests tC' tsD++-- | 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' and 'outgroup').+--+-- Connect branches according to the provided 'Semigroup' instance.+--+-- Upon insertion of the root, split the affected branch according to the+-- 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) =>+  Bipartition a ->+  Tree e a ->+  Either String (Tree e a)+rootAt b t+  -- Tree is checked for being bifurcating in 'roots'.+  --+  -- Do not use 'duplicateLeaves' here, because we also need to compare the leaf+  -- set with the bipartition.+  | length lvLst /= S.size lvSet = Left "rootAt: Tree has duplicate leaves."+  | toSet b /= lvSet = Left "rootAt: Bipartition does not match leaves of tree."+  | otherwise = rootAt' b t+  where+    lvLst = leaves t+    lvSet = S.fromList $ leaves t++-- Assume the leaves of the tree are unique.+rootAt' ::+  (Semigroup e, Splittable e, Ord a) =>+  Bipartition a ->+  Tree e a ->+  Either String (Tree e a)+rootAt' b t = do+  ts <- roots t+  case find (\x -> Right b == bipartition x) ts of+    Nothing -> Left "rootAt': Bipartition not found on tree."+    Just t' -> Right t'++-- | Branch label for phylogenetic trees.+--+-- Branches may have a length and a support value.+data Phylo = Phylo+  { brLen :: Maybe BranchLength,+    brSup :: Maybe BranchSupport+  }+  deriving (Read, Show, Eq, Ord, Generic, NFData)++instance Semigroup Phylo where+  Phylo mBL mSL <> Phylo mBR mSR =+    Phylo+      (getSum <$> (Sum <$> mBL) <> (Sum <$> mBR))+      (getMin <$> (Min <$> mSL) <> (Min <$> mSR))++instance ToJSON Phylo++instance FromJSON Phylo++-- | Set all branch length 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 = first toPhyloLabel++toPhyloLabel :: (Measurable e, Supported e) => e -> Phylo+toPhyloLabel x = Phylo (Just $ getLen x) (Just $ getSup x)++-- | Set all branch support values to 'Nothing'.+--+-- Useful to export a tree with branch lengths to Newick format.+measurableToPhyloTree :: Measurable e => Tree e a -> Tree Phylo a+measurableToPhyloTree = first measurableToPhyloLabel++measurableToPhyloLabel :: Measurable e => e -> Phylo+measurableToPhyloLabel x = Phylo (Just $ getLen x) Nothing++-- | Set all branch lengths to 'Nothing'.+--+-- Useful to export a tree with branch support to Newick format.+supportedToPhyloTree :: Supported e => Tree e a -> Tree Phylo a+supportedToPhyloTree = first supportedToPhyloLabel++supportedToPhyloLabel :: Supported e => e -> Phylo+supportedToPhyloLabel x = Phylo Nothing (Just $ getSup x)++-- | Branch length label.+--+-- For conversion, see 'phyloToLengthTree'.+newtype Length = Length {fromLength :: BranchLength}+  deriving (Read, Show, Eq, Ord, Generic, NFData)+  deriving (Num, Fractional, Floating) via Double+  deriving (Semigroup, Monoid) via Sum Double++instance Measurable Length where+  getLen = fromLength+  setLen b _ = Length b++instance Splittable Length where+  split = Length . (/ 2.0) . fromLength++instance ToJSON Length++instance FromJSON Length++-- | If root branch length is not available, set it to 0.+--+-- Return 'Left' if any other branch length is unavailable.+phyloToLengthTree :: Tree Phylo a -> Either String (Tree Length a)+phyloToLengthTree =+  maybe (Left "phyloToLengthTree: Length unavailable for some branches.") Right+    . bitraverse toLength pure+    . cleanRootLength++cleanRootLength :: Tree Phylo a -> Tree Phylo a+cleanRootLength (Node (Phylo Nothing s) l f) = Node (Phylo (Just 0) s) l f+cleanRootLength t = t++toLength :: Phylo -> Maybe Length+toLength p = Length <$> brLen p++-- | Branch support label.+--+-- For conversion, see 'phyloToSupportTree'.+newtype Support = Support {fromSupport :: BranchSupport}+  deriving (Read, Show, Eq, Ord, Generic, NFData)+  deriving (Num, Fractional, Floating) via Double+  deriving (Semigroup) via Min Double++instance Supported Support where+  getSup = fromSupport+  setSup s _ = Support s++instance Splittable Support where+  split = id++instance ToJSON Support++instance FromJSON Support++-- | Set branch support values of branches leading to the leaves and of the root+-- branch to maximum support.+--+-- Return 'Left' if any other branch has no available support value.+phyloToSupportTree :: Tree Phylo a -> Either String (Tree Support a)+phyloToSupportTree t =+  maybe+    (Left "phyloToSupportTree: Support unavailable for some branches.")+    Right+    $ bitraverse toSupport pure $+      cleanLeafSupport m $+        cleanRootSupport m t+  where+    m = getMaxSupport t++-- | Set all unavailable branch support values to maximum support.+phyloToSupportTreeUnsafe :: Tree Phylo a -> Tree Support a+phyloToSupportTreeUnsafe t = cleanSupport m t+  where+    m = getMaxSupport t++-- If all branch support values are below 1.0, set the max support to 1.0.+getMaxSupport :: Tree Phylo a -> BranchSupport+getMaxSupport = fromJust . max (Just 1.0) . bimaximum . bimap brSup (const Nothing)++cleanRootSupport :: BranchSupport -> Tree Phylo a -> Tree Phylo a+cleanRootSupport maxSup (Node (Phylo b Nothing) l xs) = Node (Phylo b (Just maxSup)) l xs+cleanRootSupport _ t = t++cleanLeafSupport :: BranchSupport -> Tree Phylo a -> Tree Phylo a+cleanLeafSupport s (Node (Phylo b Nothing) l []) = Node (Phylo b (Just s)) l []+cleanLeafSupport s (Node b l xs) = Node b l $ map (cleanLeafSupport s) xs++toSupport :: Phylo -> Maybe Support+toSupport (Phylo _ Nothing) = Nothing+toSupport (Phylo _ (Just s)) = Just $ Support s++cleanSupport :: BranchSupport -> Tree Phylo a -> Tree Support a+cleanSupport maxSup (Node (Phylo _ s) l xs) = Node (Support $ fromMaybe maxSup s) l $ map (cleanSupport maxSup) xs++-- | Explicit branch label for phylogenetic trees.+data PhyloExplicit = PhyloExplicit+  { sBrLen :: BranchLength,+    sBrSup :: BranchSupport+  }+  deriving (Read, Show, Eq, Ord, Generic)++instance Semigroup PhyloExplicit where+  PhyloExplicit bL sL <> PhyloExplicit bR sR = PhyloExplicit (bL + bR) (min sL sR)++instance Measurable PhyloExplicit where+  getLen = sBrLen+  setLen b l = l {sBrLen = b}++instance Splittable PhyloExplicit where+  split l = l {sBrLen = b'}+    where+      b' = sBrLen l / 2.0++instance Supported PhyloExplicit where+  getSup = sBrSup+  setSup s l = l {sBrSup = s}++instance ToJSON PhyloExplicit++instance FromJSON PhyloExplicit++-- | Conversion to a 'PhyloExplicit' tree.+--+-- See 'phyloToLengthTree' and 'phyloToSupportTree'.+toExplicitTree :: Tree Phylo a -> Either String (Tree PhyloExplicit a)+toExplicitTree t = do+  lt <- first fromLength <$> phyloToLengthTree t+  st <- first fromSupport <$> phyloToSupportTree t+  case zipTreesWith PhyloExplicit const lt st of+    Nothing -> error "toExplicitTree: This is a bug. Can not zip two trees with the same topology."+    Just zt -> return zt
+ src/ELynx/Tree/Rooted.hs view
@@ -0,0 +1,355 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- |+-- Module      :  ELynx.Tree.Rooted+-- Description :  Rooted trees with labeled branches+-- 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 09:57:29 2019.+--+-- Rooted 'Tree's differes from a classical rose 'Data.Tree.Tree' in that it has+-- labeled branches.+--+-- For rooted topologies, please see 'ELynx.Topology.Rooted'.+--+-- A 'Tree' is defined as:+--+-- @+-- data Tree e a = Node+--   { branch :: e,+--     label :: a,+--     forest :: Forest e a+--   }+-- @+--+-- where+--+-- @+-- type Forest e a = [Tree e a]+-- @+--+-- This means, that the word 'Node' is reserved for the constructor of a tree,+-- and that a 'Node' has an attached 'branch', a 'label', and a sub-'forest'.+-- The value constructor /Node/ and the record function /label/ are not to be+-- confused. The elements of the sub-forest are often called /children/.+--+-- With respect to phylogenetic analyses, using the 'Tree' data type has some+-- disadvantages:+--+-- 1. All trees are rooted. Unrooted trees can be treated with a rooted data+-- structure, as it is used here. However, some functions may be meaningless.+--+-- 2. Changing branch labels, node labels, or the topology of the tree are slow+-- operations, especially, when the changes are close to the leaves of the tree.+--+-- In mathematical terms: A 'Tree' is a directed acyclic graph without loops,+-- with vertex labels, with edge labels. Let me know if this definition is+-- incomplete.+module ELynx.Tree.Rooted+  ( -- * Data type+    Tree (..),+    Forest,+    toTreeBranchLabels,+    toTreeNodeLabels,++    -- * Access leaves, branches and labels+    leaves,+    duplicateLeaves,+    branches,+    setBranches,+    labels,+    setLabels,+    identify,++    -- * Change structure+    degree,+    prune,+    dropNodesWith,+    dropLeavesWith,+    zipTreesWith,+    zipTrees,+  )+where++import Control.Applicative+import Control.Comonad+import Control.DeepSeq+import Control.Monad+import Control.Monad.Fix+import Data.Aeson+import Data.Bifoldable+import Data.Bifunctor+import Data.Bitraversable+import Data.Data+import Data.Foldable+import Data.List+import Data.Maybe+import qualified Data.Set as S+import qualified Data.Tree as T+import GHC.Generics++-- | Rooted rose trees with branch labels.+--+-- Unary instances such as 'Functor' act on node labels, and not on branch+-- labels. Binary instances such as 'Bifunctor' act on both labels.+--+-- Lifted instances are not provided.+data Tree e a = Node+  { branch :: e,+    label :: a,+    forest :: Forest e a+  }+  deriving (Eq, Read, Show, Data, Generic)++-- | A shorthand.+type Forest e a = [Tree e a]++-- | Map over node labels.+instance Functor (Tree e) where+  fmap f ~(Node br lb ts) = Node br (f lb) $ map (fmap f) ts+  x <$ ~(Node br _ ts) = Node br x (map (x <$) ts)++instance Bifunctor Tree where+  bimap f g ~(Node br lb ts) = Node (f br) (g lb) $ map (bimap f g) ts+  first f ~(Node br lb ts) = Node (f br) lb $ map (first f) ts+  second g ~(Node br lb ts) = Node br (g lb) $ map (second g) ts++-- | Combine node labels in pre-order.+instance Foldable (Tree e) where+  foldMap f ~(Node _ lb ts) = f lb <> foldMap (foldMap f) ts+  null _ = False+  {-# INLINE null #-}+  toList = labels+  {-# INLINE toList #-}++instance Bifoldable Tree where+  bifoldMap f g ~(Node br lb ts) = f br <> g lb <> foldMap (bifoldMap f g) ts++instance Traversable (Tree e) where+  traverse g ~(Node br lb ts) = Node br <$> g lb <*> traverse (traverse g) ts++instance Bitraversable Tree where+  bitraverse f g ~(Node br lb ts) = Node <$> f br <*> g lb <*> traverse (bitraverse f g) ts++-- The following code provides a zip-like applicative instance. However,+-- the zip-like instance makes the Monad instance meaningless. So, either we+-- provide only 'Applicative' in zip-like form, or we use the classic instance+-- for 'Applicative' and 'Monad'.++-- -- | Note: The 'Applicative' instance of 'Tree' is similar to the one of+-- -- 'Control.Applicative.ZipList', and differs from the instance of+-- -- 'Data.Tree.Tree'!+-- --+-- -- >>> let t = Node "" 0 [Node "" 1 [], Node "" 2 []] :: Tree String Int+-- -- >>> let f = Node "+3" (+3) [Node "*5" (*5) [], Node "+10" (+10) []] :: Tree String (Int -> Int)+-- -- >>> f <*> t+-- -- Node {branch = "+3", label = 3, forest = [Node {branch = "*5", label = 5, forest = []},Node {branch = "+10", label = 12, forest = []}]}+-- --+-- -- Note: The 'Monoid' instance of the branch labels determines how the branches+-- -- are combined. For example, distances can be summed using the+-- -- 'Data.Monoid.Sum' monoid.+-- instance Monoid e => Applicative (Tree e) where+--   pure lb = Node mempty lb []+--   ~(Node brF lbF tsF) <*> ~(Node brX lbX tsX) =+--     Node (brF <> brX) (lbF lbX) (zipWith (<*>) tsF tsX)+--   liftA2 f ~(Node brX lbX tsX) ~(Node brY lbY tsY) =+--     Node (brX <> brY) (f lbX lbY) (zipWith (liftA2 f) tsX tsY)+--   ~(Node brX _ tsX) *> ~(Node brY lbY tsY) =+--     Node (brX <> brY) lbY (zipWith (*>) tsX tsY)+--   ~(Node brX lbX tsX) <* ~(Node brY _ tsY) =+--     Node (brX <> brY) lbX (zipWith (<*) tsX tsY)++-- | The 'Semigroup' instance of the branch labels determines how the+-- branches are combined. For example, distances can be summed using+-- 'Data.Semigroup.Sum'.+--+-- The 'Monoid' instance of the branch labels determines the default branch+-- label when using 'pure'.+instance 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)+  liftA2 f ~(Node brX lbX tsX) ~ty@(Node brY lbY tsY) =+    Node (brX <> brY) (f lbX lbY) (map (f lbX <$>) tsY ++ map (\tx -> liftA2 f tx ty) tsX)+  ~(Node brX _ tsX) *> ~ty@(Node brY lbY tsY) =+    Node (brX <> brY) lbY (tsY ++ map (*> ty) tsX)+  ~(Node brX lbX tsX) <* ~ty@(Node brY _ tsY) =+    Node (brX <> brY) lbX (map (lbX <$) tsY ++ map (<* ty) tsX)++-- | 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+  ~(Node br lb ts) >>= f = case f lb of+    Node br' lb' ts' -> Node (br <> br') lb' (ts' ++ map (>>= f) ts)++-- -- Cannot provide MonadZip instance because branch labels cannot be+-- -- recovered from combined label.+-- instance Monoid e => MonadZip (Tree e) where+--   mzipWith f (Node brL lbL tsL) (Node brR lbR tsR) =+--     Node (brL <> brR) (f lbL lbR) (mzipWith (mzipWith f) tsL tsR)+--+--   munzip (Node br (lbL, lbR) ts) = (Node ? lbL tsL, Node ? lbR tsR)+--     where+--       (tsL, tsR) = munzip (map munzip ts)++instance Monoid e => MonadFix (Tree e) where+  mfix = mfixTree++mfixTree :: (a -> Tree e a) -> Tree e a+mfixTree f+  | Node br lb ts <- fix (f . label) =+    Node+      br+      lb+      ( zipWith+          (\i _ -> mfixTree ((!! i) . forest . f))+          [0 ..]+          ts+      )++instance Comonad (Tree e) where+  duplicate t@(Node br _ ts) = Node br t (map duplicate ts)+  extract (Node _ lb _) = lb+  {-# INLINE extract #-}++instance (NFData e, NFData a) => NFData (Tree e a) where+  rnf (Node br lb ts) = rnf br `seq` rnf lb `seq` rnf ts++instance (ToJSON e, ToJSON a) => ToJSON (Tree e a)++instance (FromJSON e, FromJSON a) => FromJSON (Tree e a)++-- | Conversion to 'T.Tree' using branch labels.+toTreeBranchLabels :: Tree e a -> T.Tree e+toTreeBranchLabels (Node br _ ts) = T.Node br (map toTreeBranchLabels ts)++-- | Conversion to 'T.Tree' using node labels.+toTreeNodeLabels :: Tree e a -> T.Tree a+toTreeNodeLabels (Node _ lb ts) = T.Node lb (map toTreeNodeLabels ts)++-- | Get leaves.+leaves :: Tree e a -> [a]+leaves (Node _ lb []) = [lb]+leaves (Node _ _ ts) = concatMap leaves ts++-- | Check if a tree has duplicate leaves.+duplicateLeaves :: Ord a => Tree e a -> Bool+duplicateLeaves = duplicates . leaves++-- | Get branch labels in pre-order.+branches :: Tree e a -> [e]+branches t = squish t []+  where+    squish (Node br _ ts) xs = br : foldr squish xs ts++-- | Set branch labels in pre-order.+--+-- Return 'Nothing' if the provided list of branch labels is too short.+setBranches :: Bitraversable t => [f] -> t e a -> Maybe (t f a)+setBranches xs = bisequenceA . snd . bimapAccumL setBranch noChange xs+  where+    setBranch [] _ = ([], Nothing)+    setBranch (y : ys) _ = (ys, Just y)+    noChange ys z = (ys, Just z)++-- | Return node labels in pre-order.+labels :: Tree e a -> [a]+labels t = squish t []+  where+    squish (Node _ lb ts) xs = lb : foldr squish xs ts++-- | Set node labels in pre-order.+--+-- Return 'Nothing' if the provided list of node labels is too short.+setLabels :: Traversable t => [b] -> t a -> Maybe (t b)+setLabels xs = sequenceA . snd . mapAccumL setLabel xs+  where+    setLabel [] _ = ([], Nothing)+    setLabel (y : ys) _ = (ys, Just y)++-- | Label the nodes with unique integers starting at the root with 0.+identify :: Traversable t => t a -> t Int+identify = snd . mapAccumL (\i _ -> (i + 1, i)) (0 :: Int)++-- | The degree of the root node.+degree :: Tree e a -> Int+degree = (+ 1) . length . forest++-- | Prune degree two nodes.+--+-- The information stored in a pruned node is lost. The branches are combined+-- according to their 'Semigroup' instance of the form @\daughterBranch+-- parentBranch -> combinedBranch@.+prune :: Semigroup e => Tree e a -> Tree e a+prune t@(Node _ _ []) = t+prune (Node paBr _ [Node daBr daLb daTs]) = Node (daBr <> paBr) daLb daTs+prune (Node paBr paLb paTs) = Node paBr paLb $ map prune paTs++-- | Drop nodes satisfying predicate.+--+-- Degree two nodes may arise.+--+-- Also drop parent nodes of which all daughter nodes are dropped.+--+-- Return 'Nothing' if the root node satisfies the predicate.+dropNodesWith :: (a -> Bool) -> Tree e a -> Maybe (Tree e a)+dropNodesWith p (Node br lb ts)+  | p lb = Nothing+  | otherwise =+    if null ts'+      then Nothing+      else Just $ Node br lb ts'+  where+    ts' = mapMaybe (dropNodesWith p) ts++-- | Drop leaves satisfying predicate.+--+-- Degree two nodes may arise.+--+-- Also drop parent nodes of which all leaves are dropped.+--+-- Return 'Nothing' if all leaves satisfy the predicate.+dropLeavesWith :: (a -> Bool) -> Tree e a -> Maybe (Tree e a)+dropLeavesWith p (Node br lb [])+  | p lb = Nothing+  | otherwise = Just $ Node br lb []+dropLeavesWith p (Node br lb ts) =+  if null ts'+    then Nothing+    else Just $ Node br lb ts'+  where+    ts' = mapMaybe (dropLeavesWith p) ts++-- | Zip two trees with the same topology.+--+-- Return 'Nothing' if the topologies are different.+zipTreesWith ::+  (e1 -> e2 -> e) ->+  (a1 -> a2 -> a) ->+  Tree e1 a1 ->+  Tree e2 a2 ->+  Maybe (Tree e a)+zipTreesWith f g (Node brL lbL tsL) (Node brR lbR tsR) =+  if length tsL == length tsR+    then -- I am proud of that :)).+      zipWithM (zipTreesWith f g) tsL tsR >>= Just . Node (f brL brR) (g lbL lbR)+    else Nothing++-- | Zip two trees with the same topology.+--+-- 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
@@ -0,0 +1,60 @@+-- |+-- Module      :  ELynx.Tree.Simulate.Coalescent+-- Description :  Generate coalescent trees+-- Copyright   :  (c) Dominik Schrempf 2018+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Wed May 16 13:13:11 2018.+module ELynx.Tree.Simulate.Coalescent+  ( simulate,+  )+where++import Control.Monad.Primitive+import ELynx.Tree.Measurable+import ELynx.Tree.Phylogeny+import ELynx.Tree.Rooted+import ELynx.Tree.Distribution.CoalescentContinuous+import Statistics.Distribution+import System.Random.MWC++-- | Simulate a coalescent tree with @n@ leaves. The branch lengths are in units+-- of effective population size.+simulate ::+  (PrimMonad m) =>+  -- | Number of leaves.+  Int ->+  Gen (PrimState m) ->+  m (Tree Length Int)+simulate n = simulate' n 0 trs+  where+    trs = [Node (Length 0) i [] | i <- [0 .. n - 1]]++simulate' ::+  (PrimMonad m) =>+  Int ->+  Int ->+  Forest Length Int ->+  Gen (PrimState m) ->+  m (Tree Length Int)+simulate' n a trs g+  | n <= 0 = error "Cannot construct trees without leaves."+  | n == 1 && length trs /= 1 = error "Too many trees provided."+  | n == 1 && length trs == 1 = return $ head trs+  | otherwise = do+    -- Indices of the leaves to join will be i-1 and i.+    i <- uniformR (1, n - 1) g+    -- The time of the coalescent event.+    t <- genContVar (coalescentDistributionCont n) g+    let trs' = map (applyStem (+ t)) trs -- Move time 't' up on the tree.+        tl = trs' !! (i - 1)+        tr = trs' !! i+        -- Join the two chosen trees.+        tm = Node (Length 0) a [tl, tr]+        -- Take the trees on the left, the merged tree, and the trees on the right.+        trs'' = take (i - 1) trs' ++ [tm] ++ drop (i + 1) trs'+    simulate' (n - 1) a trs'' g
+ src/ELynx/Tree/Simulate/PointProcess.hs view
@@ -0,0 +1,288 @@+{-# LANGUAGE BangPatterns #-}++-- |+--   Module      :  ELynx.Tree.Simulate.PointProcess+--   Description :  Point process and functions+--   Copyright   :  (c) Dominik Schrempf 2018+--   License     :  GPL-3.0-or-later+--+--   Maintainer  :  dominik.schrempf@gmail.com+--   Stability   :  unstable+--   Portability :  portable+--+-- Creation date: Tue Feb 13 13:16:18 2018.+--+-- See Gernhard, T. (2008). The conditioned reconstructed process. Journal of+-- Theoretical Biology, 253(4), 769–778. http://doi.org/10.1016/j.jtbi.2008.04.005.+--+-- The point process can be used to simulate reconstructed trees under the birth+-- and death process.+module ELynx.Tree.Simulate.PointProcess+  ( PointProcess (..),+    TimeSpec,+    simulate,+    toReconstructedTree,+    simulateReconstructedTree,+    simulateNReconstructedTrees,+  )+where++import Control.Monad+import Control.Monad.Primitive+import Data.Function+import Data.List+import Data.Sequence (Seq)+import qualified Data.Sequence as S+import ELynx.Tree.Distribution.BirthDeath+import ELynx.Tree.Distribution.BirthDeathCritical+import ELynx.Tree.Distribution.BirthDeathCriticalNoTime+import ELynx.Tree.Distribution.BirthDeathNearlyCritical+import ELynx.Tree.Distribution.TimeOfOrigin+import ELynx.Tree.Distribution.TimeOfOriginNearCritical+import ELynx.Tree.Distribution.Types+import ELynx.Tree.Measurable+import ELynx.Tree.Phylogeny+import ELynx.Tree.Rooted+import qualified Statistics.Distribution as D+  ( genContVar,+  )+import System.Random.MWC++-- Require near critical process if birth and death rates are closer than this value.+epsNearCriticalPointProcess :: Double+epsNearCriticalPointProcess = 1e-5++-- Also the distribution of origins needs a Tailor expansion for near critical values.+--+-- TODO: Check why the two epsilons are chosen differently.+epsNearCriticalTimeOfOrigin :: Double+epsNearCriticalTimeOfOrigin = 1e-8++-- Require critical process if birth and death rates are closer than this value.+eps :: Double+eps = 1e-12++(=~=) :: Double -> Double -> Bool+x =~= y = eps > abs (x - y)++-- Sort a list and also return original indices.+sortListWithIndices :: Ord a => [a] -> [(a, Int)]+sortListWithIndices xs = sortBy (compare `on` fst) $ zip xs ([0 ..] :: [Int])++-- Insert element into random position of list.+randomInsertList :: PrimMonad m => a -> [a] -> Gen (PrimState m) -> m [a]+randomInsertList e v g = do+  let l = length v+  i <- uniformR (0, l) g+  return $ take i v ++ [e] ++ drop i v++-- | A __point process__ for \(n\) points and of age \(t_{or}\) is defined as+-- follows. Draw $n$ points on the horizontal axis at \(1,2,\ldots,n\). Pick+-- \(n-1\) points at locations \((i+1/2, s_i)\), \(i=1,2,\ldots,n-1\);+-- \(0 < s_i < t_{or}\). There is a bijection between (ranked) oriented trees+-- and the point process. Usually, a will be 'String' (or 'Int') and b will be+-- 'Double'.+data PointProcess a b = PointProcess+  { points :: ![a],+    values :: ![b],+    origin :: !b+  }+  deriving (Read, Show, Eq)++-- | If nothing, sample time of origin from respective distribution. If time is+-- given, we need to know if we condition on the time of origin, or the time of+-- the most recent common ancestor (MRCA).+type TimeSpec = Maybe (Time, Bool)++-- | Sample a point process using the 'BirthDeathDistribution'. The names of the+-- points will be integers.+simulate ::+  (PrimMonad m) =>+  -- | Number of points (samples)+  Int ->+  -- | Time of origin or MRCA+  TimeSpec ->+  -- | Birth rate+  Rate ->+  -- | Death rate+  Rate ->+  -- | Generator (see 'System.Random.MWC')+  Gen (PrimState m) ->+  m (PointProcess Int Double)+-- No time of origin given. We also don't need to take care of the conditioning+-- (origin or MRCA).+simulate n Nothing l m g+  | -- XXX. There is no formula for the over-critical process.+    m > l =+    error+      "Time of origin distribution formula not available when mu > lambda. Please specify height for the moment."+  | -- For the critical process, we have no idea about the time of origin, but can+    -- use a specially derived distribution.+    m =~= l =+    do+      !vs <- replicateM (n - 1) (D.genContVar (BDCNTD l) g)+      -- XXX: The length of the root branch will be 0.+      let t = maximum vs+      return $ PointProcess [0 .. (n - 1)] vs t+  | -- For the near critical process, we use a special distribution.+    abs (m - l) <= epsNearCriticalTimeOfOrigin =+    do+      t <- D.genContVar (TONCD n l m) g+      simulate n (Just (t, False)) l m g+  | -- For a sub-critical branching process, we can use the formula from Tanja Stadler.+    otherwise =+    do+      t <- D.genContVar (TOD n l m) g+      simulate n (Just (t, False)) l m g+-- Time of origin is given.+simulate n (Just (t, c)) l m g+  | n < 1 = error "Number of samples needs to be one or larger."+  | t < 0.0 = error "Time of origin needs to be positive."+  | l < 0.0 = error "Birth rate needs to be positive."+  | -- See Stadler, T., & Steel, M. (2019). Swapping birth and death: symmetries+    -- and transformations in phylodynamic models. , (), .+    -- http://dx.doi.org/10.1101/494583. Should be possible now.+    -- -- | m < 0.0   = error "Death rate needs to be positive."+    -- Now, we have three different cases.+    -- 1. The critical branching process.+    -- 2. The near critical branching process.+    -- 3. Normal values :).+    (m =~= l) && not c = do+    !vs <- replicateM (n - 1) (D.genContVar (BDCD t l) g)+    return $ PointProcess [0 .. (n - 1)] vs t+  | (abs (m - l) <= epsNearCriticalPointProcess) && not c = do+    !vs <- replicateM (n - 1) (D.genContVar (BDNCD t l m) g)+    return $ PointProcess [0 .. (n - 1)] vs t+  | not c = do+    !vs <- replicateM (n - 1) (D.genContVar (BDD t l m) g)+    return $ PointProcess [0 .. (n - 1)] vs t+  | (m =~= l) && c = do+    !vs <- replicateM (n - 2) (D.genContVar (BDCD t l) g)+    vs' <- randomInsertList t vs g+    return $ PointProcess [0 .. (n - 1)] vs' t+  | (abs (m - l) <= epsNearCriticalPointProcess) && c = do+    !vs <- replicateM (n - 2) (D.genContVar (BDNCD t l m) g)+    vs' <- randomInsertList t vs g+    return $ PointProcess [0 .. (n - 1)] vs' t+  | c = do+    !vs <- replicateM (n - 2) (D.genContVar (BDD t l m) g)+    vs' <- randomInsertList t vs g+    return $ PointProcess [0 .. (n - 1)] vs' t+  | otherwise = error "simulate: Fell through guard, this should never happen."++-- Sort the values of a point process and their indices to be (the indices+-- that they will have while creating the tree).+sortPP :: (Ord b) => PointProcess a b -> ([b], [Int])+sortPP (PointProcess _ vs _) = (vsSorted, isSorted)+  where+    vsIsSorted = sortListWithIndices vs+    vsSorted = map fst vsIsSorted+    isSorted = flattenIndices $ map snd vsIsSorted++-- Decrement indices that are above the one that is merged.+flattenIndices :: [Int] -> [Int]+flattenIndices is = snd $ mapAccumL fAcc [] is++-- TODO: This is the bottleneck for simulating large trees.+--+-- The accumulating function. Count the number of indices which are before the+-- current index and lower than the current index.+fAcc :: [Int] -> Int -> ([Int], Int)+fAcc is i = (i : is, i') where i' = i - length (filter (< i) is)++-- | See 'simulateReconstructedTree', but n times.+simulateNReconstructedTrees ::+  (PrimMonad m) =>+  -- | Number of trees+  Int ->+  -- | Number of points (samples)+  Int ->+  -- | Time of origin or MRCA+  TimeSpec ->+  -- | Birth rate+  Rate ->+  -- | Death rate+  Rate ->+  -- | Generator (see 'System.Random.MWC')+  Gen (PrimState m) ->+  m (Forest Length Int)+simulateNReconstructedTrees nT nP t l m g+  | nT <= 0 = return []+  | otherwise = replicateM nT $ simulateReconstructedTree nP t l m g++-- | Use the point process to simulate a reconstructed tree (see+-- 'toReconstructedTree') possibly with specific height and a fixed number of+-- leaves according to the birth and death process.+simulateReconstructedTree ::+  (PrimMonad m) =>+  -- | Number of points (samples)+  Int ->+  -- | Time of origin or MRCA+  TimeSpec ->+  -- | Birth rate+  Rate ->+  -- | Death rate+  Rate ->+  -- | Generator (see 'System.Random.MWC')+  Gen (PrimState m) ->+  m (Tree Length Int)+simulateReconstructedTree n t l m g =+  toReconstructedTree 0 <$> simulate n t l m g++-- | Convert a point process to a reconstructed tree. See Lemma 2.2.++-- Of course, I decided to only use one tree structure with extinct and extant+-- leaves (actually a complete tree). So a tree created here just does not+-- contain extinct leaves. A function 'isReconstructed' is provided to test if a+-- tree is reconstructed (and not complete) in this sense. However, a complete+-- tree might show up as "reconstructed", just because, by chance, it does not+-- contain extinct leaves. I wanted to use a Monoid constraint to get the unit+-- element, but this fails for classical 'Int's. So, I rather have another+-- (useless) argument.+toReconstructedTree ::+  a -> -- Default node label.+  PointProcess a Double ->+  Tree Length a+toReconstructedTree l pp@(PointProcess ps vs o)+  | length ps /= length vs + 1 = error "Too few or too many points."+  | length vs <= 1 = error "Too few values."+  | -- -- Test is deactivated.+    -- -- | otherwise = if isReconstructed treeOrigin then treeOrigin else error "Error in algorithm."+    otherwise =+    treeOrigin+  where+    (vsSorted, isSorted) = sortPP pp+    !lvs = S.fromList [Node (Length 0) p [] | p <- ps]+    !heights = S.replicate (length ps) 0+    !treeRoot = toReconstructedTree' isSorted vsSorted l lvs heights+    !h = last vsSorted+    !treeOrigin = applyStem (+ (o - h)) treeRoot++-- Move up the tree, connect nodes when they join according to the point process.+toReconstructedTree' ::+  [Int] -> -- Sorted indices, see 'sort'.+  [Double] -> -- Sorted merge values.+  a -> -- Default node label.+  Seq (Tree Length a) -> -- Leaves with accumulated root branch lengths.+  Seq Double -> -- Accumulated heights of the leaves.+  Tree Length a+toReconstructedTree' [] [] _ trs _ = trs `S.index` 0+toReconstructedTree' is vs l trs hs = toReconstructedTree' is' vs' l trs'' hs'+  where+    -- For the algorithm, see 'simulate' but index starts at zero.++    !i = head is+    !is' = tail is+    !v = head vs+    !vs' = tail vs+    -- Left: l, right: r.+    !hl = hs `S.index` i+    !hr = hs `S.index` (i + 1)+    !dvl = v - hl+    !dvr = v - hr+    !tl = applyStem (+ dvl) $ trs `S.index` i+    !tr = applyStem (+ dvr) $ trs `S.index` (i + 1)+    !h' = hl + dvl -- Should be the same as 'hr + dvr'.+    !tm = Node (Length 0) l [tl, tr]+    !trs'' = (S.take i trs S.|> tm) S.>< S.drop (i + 2) trs+    !hs' = (S.take i hs S.|> h') S.>< S.drop (i + 2) hs
+ src/ELynx/Tree/Splittable.hs view
@@ -0,0 +1,29 @@+-- |+-- Module      :  ELynx.Tree.Splittable+-- Description :  Splittable branch labels+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Sat Jul 18 13:52:22 2020.+module ELynx.Tree.Splittable+  ( Splittable (..),+  )+where++-- | A data type that can be combined using '<>' and split into one out of two+-- equal entities.+--+-- The following equality should hold:+--+-- @+-- split x <> split x = x+-- @+class Splittable e where+  split :: e -> e++instance Splittable Double where+  split = (/ 2)
+ src/ELynx/Tree/Supported.hs view
@@ -0,0 +1,66 @@+-- |+-- Module      :  ELynx.Tree.Supported+-- Description :  Branch label with support value+-- 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.+--+-- Non-negativity of branch support values is not (yet) ensured. To ensure+-- non-negativity, a newtype wrapper could be used, but this would be a major+-- refactor.+module ELynx.Tree.Supported+  ( BranchSupport,+    Supported (..),+    normalizeBranchSupport,+    collapse,+  )+where++import Data.Bifoldable+import Data.Bifunctor+import Data.List+import ELynx.Tree.Rooted++-- | Branch support.+type BranchSupport = Double++-- | A branch label that supports extraction and setting of branch support values.+class Supported e where+  getSup :: e -> BranchSupport+  setSup :: BranchSupport -> e -> e++-- Apply a function to a branch support label.+apply :: Supported e => (BranchSupport -> BranchSupport) -> e -> e+apply f l = setSup (f s) l where s = getSup l++-- | 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 (apply (/ 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) => BranchSupport -> 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 => Double -> Tree e a -> Bool+highP _  (Node _ _ []) = True+highP th (Node br _ _) = getSup br >= th++-- See 'collapse'.+collapse' :: Supported e => BranchSupport -> 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
@@ -0,0 +1,160 @@+-- |+-- Module      :  ELynx.Tree.Zipper+-- Description :  Zippers on rooted rose trees with branch labels+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Thu Jul 23 08:42:37 2020.+module ELynx.Tree.Zipper+  ( -- * Data type+    TreePos (..),++    -- * Conversion+    fromTree,+    toTree,++    -- * Movement+    goUp,+    goRoot,+    goLeft,+    goRight,+    goChild,+    goPath,+    unsafeGoPath,++    -- * Modification+    insertTree,+    insertBranch,+    insertLabel,+  )+where++import Data.Foldable+import ELynx.Tree.Rooted++-- | Tree zipper. For reference, please see http://hackage.haskell.org/package/rosezipper.+data TreePos e a = Pos+  { -- | The currently selected tree.+    current :: Tree e a,+    -- | Forest to the left in reversed order.+    before :: Forest e a,+    -- | Forest to the right+    after :: Forest e a,+    -- | Finger to the selected tree+    parents :: [([Tree e a], e, a, [Tree e a])]+  }+  deriving (Show, Eq)++-- | Get a zipper pointing to the root.+fromTree :: Tree e a -> TreePos e a+fromTree t = Pos {current = t, before = [], after = [], parents = []}++-- | Get the complete tree of the zipper.+toTree :: TreePos e a -> Tree e a+toTree = current . goRoot++getForest :: TreePos e a -> Forest e a+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+  (ls, br, lb, rs) : ps ->+    Just+      Pos+        { current = Node br lb $ getForest pos,+          before = ls,+          after = rs,+          parents = ps+        }+  [] -> Nothing++-- | Go to root.+goRoot :: TreePos e a -> TreePos e a+goRoot pos = maybe pos goRoot (goUp pos)++-- | Go to left sibling in current forest.+goLeft :: TreePos e a -> Maybe (TreePos e a)+goLeft pos =+  case before pos of+    t : ts ->+      Just+        pos+          { current = t,+            before = ts,+            after = current pos : after pos+          }+    [] -> Nothing++-- | Go to right sibling in current forest.+goRight :: TreePos e a -> Maybe (TreePos e a)+goRight pos =+  case after pos of+    t : ts ->+      Just+        pos+          { current = t,+            before = current pos : before pos,+            after = ts+          }+    [] -> Nothing++-- | Go to child with given index in forest.+goChild :: Int -> TreePos e a -> Maybe (TreePos e a)+goChild n pos = case current pos of+  (Node br lb ts)+    | null ts -> Nothing+    | length ts <= n -> Nothing+    | otherwise ->+      Just $+        Pos+          { current = head rs',+            before = reverse ls',+            after = tail rs',+            parents = (before pos, br, lb, after pos) : parents pos+          }+    where+      (ls', rs') = splitAt n ts++-- | Go to node with given path.+goPath :: [Int] -> 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.+unsafeGoChild :: Int -> TreePos e a -> TreePos e a+unsafeGoChild n pos = case current pos of+  (Node br lb ts)+    | null ts -> error "unsafeGoChild: Forest is empty."+    | length ts <= n -> error "unsafeGoChild: Forest is too short."+    | otherwise ->+      Pos+        { current = head rs',+          before = reverse ls',+          after = tail rs',+          parents = (before pos, br, lb, after pos) : parents pos+        }+    where+      (ls', rs') = splitAt n ts++-- | Got to node with given path. Call 'error' if path is invalid.+unsafeGoPath :: [Int] -> TreePos e a -> TreePos e a+unsafeGoPath pos pth = foldl (flip unsafeGoChild) pth pos++-- | 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}++-- | Insert a new branch label into the current focus of the zipper.+insertBranch :: e -> TreePos e a -> TreePos e a+insertBranch br pos = case current pos of+  Node _ lb ts -> pos {current = Node br lb ts}++-- | Insert a new node label into the current focus of the zipper.+insertLabel :: a -> TreePos e a -> TreePos e a+insertLabel lb pos = case current pos of+  Node br _ ts -> pos {current = Node br lb ts}
− test/ELynx/Data/Topology/RootedSpec.hs
@@ -1,23 +0,0 @@--- |--- Module      :  ELynx.Data.Topology.RootedSpec--- Description :  Unit tests for ELynx.Data.Topology.Rooted--- Copyright   :  (c) Dominik Schrempf, 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Sat Jul 18 13:58:16 2020.-module ELynx.Data.Topology.RootedSpec-  ( spec,-  )-where--import Test.Hspec--spec :: Spec-spec =-  describe "TODO" $-    it "returns the original number when given a positive input" $-      (1 :: Int) `shouldBe` 1
− test/ELynx/Data/Tree/Arbitrary.hs
@@ -1,66 +0,0 @@-{-# OPTIONS_GHC -Wno-orphans #-}---- |--- Module      :  ELynx.Data.Tree.Arbitrary--- Description :  Arbitrary instance, needed for QuickCheck--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Tue Apr 21 17:13:23 2020.-module ELynx.Data.Tree.Arbitrary-  (-  )-where--import Data.Traversable-import ELynx.Data.Tree-import Test.QuickCheck---- Of course, the boundaries for branch support and length have been chosen--- pretty arbitrarily :).------ XXX: This instance does not produce values without branch lengths nor branch--- supports.-instance Arbitrary Phylo where-  arbitrary = Phylo <$> (Just <$> choose (1, 100)) <*> (Just <$> choose (0.001, 10))--instance Arbitrary2 Tree where-  liftArbitrary2 arbB arbN = go-    where-      go = sized $ \n -> do-        -- Sized is the size of the trees.-        br <- arbB-        val <- arbN-        pars <- frequency [(1, pure [1, 1]), (3, arbPartition (n - 1))] -- can go negative!-        frst <- for pars $ \i -> resize i go-        return $ Node br val frst-      arbPartition :: Int -> Gen [Int]-      arbPartition k = case compare k 1 of-        LT -> pure []-        EQ -> pure [1]-        GT -> do-          first <- elements [1 .. k]-          rest <- arbPartition $ k - first-          return $ first : rest--  liftShrink2 _ shrN = go-    where-      go (Node br val frst) =-        frst-          ++ [ Node br e fs-               | (e, fs) <- liftShrink2 shrN (liftShrink go) (val, frst)-             ]--instance (Arbitrary e, Arbitrary a) => Arbitrary (Tree e a) where-  arbitrary = arbitrary2--instance (CoArbitrary e, CoArbitrary a) => CoArbitrary (Tree e a) where-  coarbitrary (Node br val frst) =-    coarbitrary br . coarbitrary val . coarbitrary frst--instance Arbitrary Length where-  arbitrary = Length . getPositive <$> arbitrary
− test/ELynx/Data/Tree/BipartitionSpec.hs
@@ -1,88 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Data.Tree.BipartitionSpec--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Fri Aug 30 09:38:50 2019.-module ELynx.Data.Tree.BipartitionSpec-  ( spec,-  )-where--import qualified Data.ByteString.Char8 as BS-import Data.Map (Map)-import qualified Data.Map as M-import Data.Set (Set)-import qualified Data.Set as S-import ELynx.Data.Tree-import ELynx.Import.Tree.Newick-import ELynx.Tools-import Test.Hspec--sfrom :: [BS.ByteString] -> S.Set BS.ByteString-sfrom = S.fromList--treeFileSimple :: FilePath-treeFileSimple = "data/TreeDist.trees"--getSimpleTrees :: IO (Forest Phylo BS.ByteString)-getSimpleTrees = parseFileWith (someNewick Standard) treeFileSimple--bipartitionToBranchAnswer :: Map (Bipartition BS.ByteString) Length-bipartitionToBranchAnswer =-  M.fromList-    [ (bpUnsafe (sfrom ["B"]) (sfrom ["A", "C", "D", "E"]), 0.3),-      (bpUnsafe (sfrom ["B", "C", "D", "E"]) (sfrom ["A"]), 0.1),-      (bpUnsafe (sfrom ["B", "C", "E"]) (sfrom ["A", "D"]), 5.0e-2),-      (bpUnsafe (sfrom ["B", "E"]) (sfrom ["A", "C", "D"]), 0.4),-      (bpUnsafe (sfrom ["C"]) (sfrom ["A", "B", "D", "E"]), 1.0e-2),-      (bpUnsafe (sfrom ["D"]) (sfrom ["A", "B", "C", "E"]), 0.25),-      (bpUnsafe (sfrom ["E"]) (sfrom ["A", "B", "C", "D"]), 0.8)-    ]--bipartitionsFirstTree :: Set (Bipartition BS.ByteString)-bipartitionsFirstTree =-  S.fromList-    [ bpUnsafe (sfrom ["B"]) (sfrom ["A", "C", "D", "E"]),-      bpUnsafe (sfrom ["B", "C", "D", "E"]) (sfrom ["A"]),-      bpUnsafe (sfrom ["B", "D", "E"]) (sfrom ["A", "C"]),-      bpUnsafe (sfrom ["B", "E"]) (sfrom ["A", "C", "D"]),-      bpUnsafe (sfrom ["C"]) (sfrom ["A", "B", "D", "E"]),-      bpUnsafe (sfrom ["D"]) (sfrom ["A", "B", "C", "E"]),-      bpUnsafe (sfrom ["E"]) (sfrom ["A", "B", "C", "D"])-    ]--bipartitionsSecondTree :: Set (Bipartition BS.ByteString)-bipartitionsSecondTree =-  S.fromList-    [ bpUnsafe (sfrom ["B"]) (sfrom ["A", "C", "D", "E"]),-      bpUnsafe (sfrom ["B", "C", "D", "E"]) (sfrom ["A"]),-      bpUnsafe (sfrom ["B", "C", "E"]) (sfrom ["A", "D"]),-      bpUnsafe (sfrom ["B", "E"]) (sfrom ["A", "C", "D"]),-      bpUnsafe (sfrom ["C"]) (sfrom ["A", "B", "D", "E"]),-      bpUnsafe (sfrom ["D"]) (sfrom ["A", "B", "C", "E"]),-      bpUnsafe (sfrom ["E"]) (sfrom ["A", "B", "C", "D"])-    ]--spec :: Spec-spec = do-  describe "bipartitions" $-    it "calculates correct bipartitions for sample trees" $-      do-        simpleTrees <- getSimpleTrees-        let t1 = head simpleTrees-            t2 = simpleTrees !! 1-        bipartitions t1 `shouldBe` Right bipartitionsFirstTree-        bipartitions t2 `shouldBe` Right bipartitionsSecondTree-  describe "bipartitionToBranch" $-    it "creates a map from bipartitions to branch lengths" $-      do-        simpleTrees <- getSimpleTrees-        (phyloToLengthTree (simpleTrees !! 2) >>= bipartitionToBranch)-          `shouldBe` Right bipartitionToBranchAnswer
− test/ELynx/Data/Tree/DistanceSpec.hs
@@ -1,259 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Data.Tree.DistanceSpec--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Fri Aug 30 09:38:50 2019.-module ELynx.Data.Tree.DistanceSpec-  ( spec,-  )-where--import qualified Data.ByteString.Char8 as BS-import ELynx.Data.Tree-import ELynx.Data.Tree.Arbitrary ()-import ELynx.Import.Tree.Newick-import ELynx.Tools-import Test.Hspec-import Test.QuickCheck--treeFileSimple :: FilePath-treeFileSimple = "data/TreeDist.trees"--getSimpleTrees :: IO (Forest Phylo BS.ByteString)-getSimpleTrees = parseFileWith (someNewick Standard) treeFileSimple--treeFileMany :: FilePath-treeFileMany = "data/Many.trees"--getManyTrees :: IO (Forest Phylo BS.ByteString)-getManyTrees = parseFileWith (someNewick Standard) treeFileMany---- I used treedist from Phylip to get the correct results.--- See http://evolution.genetics.washington.edu/phylip/doc/treedist.html.-symmetricAnswers :: [Int]-symmetricAnswers =-  [ 6,-    8,-    0,-    0,-    12,-    20,-    18,-    20,-    10,-    2,-    10,-    4,-    4,-    4,-    4,-    4,-    10,-    16,-    8,-    2,-    4,-    0,-    0,-    0,-    10,-    4,-    0,-    0,-    2,-    2,-    0,-    0,-    4,-    0,-    2,-    0,-    8,-    6,-    2,-    6,-    4,-    4,-    8,-    0,-    0,-    4,-    2,-    0,-    10,-    0,-    0,-    10-  ]--branchScoreAnswers :: [Double]-branchScoreAnswers =-  [ 8.567916e-02,-    9.570577e-02,-    1.704571e-02,-    7.603990e-03,-    6.149761e-01,-    3.557070e-01,-    2.329811e-01,-    3.820208e-01,-    1.895421e-02,-    6.302364e-03,-    2.083286e-02,-    1.023777e-03,-    2.138244e-02,-    1.444380e-02,-    1.958628e-02,-    6.089461e-03,-    2.551873e-02,-    8.041220e-02,-    4.123102e-02,-    8.241811e-03,-    2.623805e-02,-    2.109278e-02,-    1.953769e-02,-    4.459926e-03,-    6.594537e-02,-    7.040703e-02,-    8.603133e-03,-    3.878009e-03,-    2.969969e-02,-    2.505262e-02,-    2.095988e-02,-    8.461041e-03,-    5.228005e-02,-    6.001320e-02,-    8.276652e-03,-    6.966115e-03,-    7.701581e-02,-    4.946339e-02,-    2.548024e-02,-    5.800598e-03,-    3.875927e-02,-    2.836737e-02,-    9.059706e-02,-    1.333325e-02,-    5.071356e-02,-    7.433056e-02,-    3.854717e-02,-    3.255993e-02,-    1.581909e-01,-    6.813096e-02,-    8.210513e-02,-    7.664642e-02-  ]--prop_dist_same_tree :: (Num b, Eq b) => (Tree e a -> Tree e a -> Either String b) -> Tree e a -> Bool-prop_dist_same_tree distanceMeasure t = distanceMeasure t t == Right 0--each :: Int -> [a] -> [a]-each n = map head . takeWhile (not . null) . iterate (drop n)--multifurcating :: Tree () Char-multifurcating =-  Node-    ()-    ' '-    [Node () 'A' [], Node () 'B' [], Node () ' ' [Node () 'C' [], Node () 'D' [], Node () 'E' []]]--bifurcatingComp :: Tree () Char-bifurcatingComp =-  Node-    ()-    ' '-    [ Node () ' ' [Node () 'A' [], Node () 'B' []],-      Node () ' ' [Node () 'C' [], Node () ' ' [Node () 'D' [], Node () 'E' []]]-    ]--bifurcatingIncomp :: Tree () Char-bifurcatingIncomp =-  Node-    ()-    ' '-    [ Node () ' ' [Node () 'A' [], Node () 'C' []],-      Node () ' ' [Node () 'B' [], Node () ' ' [Node () 'D' [], Node () 'E' []]]-    ]--incSplitTree1a :: Tree Phylo BS.ByteString-incSplitTree1a = parseByteStringWith (oneNewick IqTree) "((a,b)0.7,(c,d));"--incSplitTree1b :: Tree Phylo BS.ByteString-incSplitTree1b = parseByteStringWith (oneNewick IqTree) "((a,b)0.7,c,d);"--incSplitTree2 :: Tree Phylo BS.ByteString-incSplitTree2 = parseByteStringWith (oneNewick IqTree) "((a,c),(b,d));"--incSplitTree3 :: Tree Phylo BS.ByteString-incSplitTree3 = parseByteStringWith (oneNewick IqTree) "(((a,b)0.7,c),(d,e));"--incSplitTree4 :: Tree Phylo BS.ByteString-incSplitTree4 = parseByteStringWith (oneNewick IqTree) "(((a,c),b),(d,e));"---- Compute distances between adjacent pairs of a list of input trees. Use given--- distance measure.-adjacent :: (a -> a -> b) -> [a] -> [b]-adjacent dist trs = [dist x y | (x, y) <- zip trs (tail trs)]---- noPL :: Phylo--- noPL = Phylo Nothing Nothing--spec :: Spec-spec = do-  describe "symmetric" $ do-    it "calculates correct distances for sample trees" $ do-      simpleTrees <- getSimpleTrees-      symmetric (head simpleTrees) (simpleTrees !! 1) `shouldBe` Right 2-      manyTrees <- getManyTrees-      -- Since treedist computes the distance between adjacent pairs, in the-      -- following manner: [tr0, tr1, tr2, tr3] -> [dist tr0 tr1, dist tr2 tr3],-      -- we have to skip some distances.-      each 2 (adjacent symmetric manyTrees)-        `shouldBe` map Right symmetricAnswers-    it "is zero for a collection of random trees" $-      property $-        prop_dist_same_tree-          (symmetric :: Tree Phylo Double -> Tree Phylo Double -> Either String Int)-  describe "incompatibleSplit" $ do-    it "calculates correct distances for sample trees" $ do-      incompatibleSplits multifurcating bifurcatingComp `shouldBe` Right 0-      incompatibleSplits bifurcatingComp multifurcating `shouldBe` Right 0-      -- print $ S.map bpHuman <$> bipartitions bifurcatingIncomp-      -- print $ S.map bpHuman <$> bipartitions multifurcating-      -- print $ S.map mpHuman <$> partitions bifurcatingIncomp-      -- print $ S.map mpHuman <$> partitions multifurcating-      -- print $ toNewick $ first (const noPL) bifurcatingIncomp-      -- print $ toNewick $ first (const noPL) multifurcating-      incompatibleSplits bifurcatingIncomp multifurcating `shouldBe` Right 2-      incompatibleSplits multifurcating bifurcatingIncomp `shouldBe` Right 2-    it "calculates correct distances for sample trees with branch support" $ do-      incompatibleSplits incSplitTree1a incSplitTree2 `shouldBe` Right 2-      incompatibleSplits incSplitTree1b incSplitTree2 `shouldBe` Right 2-      let t1a = phyloToSupportTreeUnsafe incSplitTree1a-          t1b = phyloToSupportTreeUnsafe incSplitTree1b-          tr2 = phyloToSupportTreeUnsafe incSplitTree2-          tr3 = phyloToSupportTreeUnsafe incSplitTree3-          tr4 = phyloToSupportTreeUnsafe incSplitTree4-      incompatibleSplits (collapse 0.7 t1a) tr2 `shouldBe` Right 2-      incompatibleSplits (collapse 0.71 t1b) tr2 `shouldBe` Right 0-      incompatibleSplits (collapse 0.71 tr3) tr4 `shouldBe` Right 0-    it "is zero for a collection of random trees" $-      property $-        prop_dist_same_tree-          (incompatibleSplits :: Tree Phylo Double -> Tree Phylo Double -> Either String Int)-  describe "branchScore" $ do-    it "calculates correct distances for sample trees" $ do-      manyTrees <- getManyTrees-      let ts = map (either error id . phyloToLengthTree) manyTrees-      let ds = map (either error id) $ each 2 $ adjacent branchScore ts-      ds `shouldSatisfy` nearlyEqListWith 1e-5 branchScoreAnswers-    it "is zero for a collection of random trees" $-      property $-        prop_dist_same_tree-          (branchScore :: Tree Length Double -> Tree Length Double -> Either String Double)
− test/ELynx/Data/Tree/PartitionSpec.hs
@@ -1,53 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Data.Tree.PartitionSpec--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Fri Aug 30 09:38:50 2019.-module ELynx.Data.Tree.PartitionSpec-  ( spec,-  )-where--import Data.Set (Set, fromList)-import ELynx.Data.Tree-import ELynx.Data.Tree.Arbitrary ()-import Test.Hspec--ex1 :: Tree () Int-ex1 = Node () 0 [Node () 1 [], Node () 2 [Node () 4 [], Node () 5 [], Node () 6 []], Node () 3 []]--sol1 :: Set (Partition Int)-sol1 =-  fromList-    [ mpUnsafe [fromList [1], fromList [3], fromList [4, 5, 6]],-      mpUnsafe [fromList [1, 3], fromList [4], fromList [5], fromList [6]]-    ]--ex2 :: Tree () Int-ex2 = Node () 0 [Node () 1 [], Node () 2 [], Node () 0 [Node () 3 [], Node () 4 []], Node () 5 []]--sol2 :: Set (Partition Int)-sol2 =-  fromList-    [ mpUnsafe [fromList [1], fromList [2], fromList [3, 4], fromList [5]],-      mpUnsafe [fromList [1, 2, 5], fromList [3], fromList [4]]-    ]--spec :: Spec-spec = describe "partitions" $-  it "calculates correct partitions for a sample tree" $-    do-      -- t <- removeBrInfo <$> getMultifurcatingTree-      -- print $ partitions ex1-      partitions ex1 `shouldBe` Right sol1-      partitions ex2 `shouldBe` Right sol2---- it "is empty for a collection of random bifurcating trees"---   $ property (prop_bifurcating_tree :: Tree (PhyloLabel Double) -> Bool)
− test/ELynx/Data/Tree/PhylogenySpec.hs
@@ -1,155 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}---- |--- Module      :  ELynx.Data.Tree.PhylogenySpec--- Description :  Unit tests for ELynx.Data.Tree.Phylogeny--- Copyright   :  (c) Dominik Schrempf, 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Wed Jul 15 11:05:32 2020.-module ELynx.Data.Tree.PhylogenySpec-  ( spec,-  )-where--import Data.Either-import qualified Data.Set as S-import ELynx.Data.Tree-import ELynx.Data.Tree.Arbitrary ()-import Test.Hspec-import Test.Hspec.QuickCheck-import Test.QuickCheck hiding (labels)--simpleTree :: Tree () String-simpleTree = Node () "i" [Node () "j" [Node () "x" [], Node () "y" []], Node () "z" []]--simpleSol :: Forest () String-simpleSol =-  [ Node () "i" [Node () "j" [Node () "x" [], Node () "y" []], Node () "z" []],-    Node () "i" [Node () "j" [Node () "z" [], Node () "y" []], Node () "x" []],-    Node () "i" [Node () "j" [Node () "z" [], Node () "x" []], Node () "y" []]-  ]--instance Splittable () where-  split = id---- Skip leaves and trees with multifurcating root nodes.-prop_roots :: Tree () a -> Bool-prop_roots t@(Node _ _ [_, _])-  | length (leaves t) == 2 = (length <$> roots t) == Right 1-  | otherwise = (length <$> roots t) == (Right $ length (labels t) - 2)-prop_roots _ = True---- -- Skip leaves and trees with multifurcating root nodes.--- prop_connect :: a -> Tree () a -> Tree () a -> Bool--- prop_connect n l@(Node _ _ [_, _]) r@(Node _ _ [_, _])---   | length (leaves l) < 3 = (length <$> connect n l r) == Right (length (flatten r) - 2)---   | length (leaves r) < 3 = (length <$> connect n l r) == Right (length (flatten l) - 2)---   | otherwise =---     (length <$> connect n l r)---       == (Right $ (length (flatten l) - 2) * (length (flatten r) - 2))--- prop_connect _ _ _ = True---- -- | Determine compatibility between a bipartition and a set.--- ----- -- If both subsets of the bipartition share elements with the given set, the--- -- bipartition is incompatible with this subset. If all elements of the subset--- -- are either not in the bipartition or mapping to one of the two subsets of the--- -- bipartition, the bipartition and the subset are compatible.--- ----- -- See also 'ELynx.Data.Tree.Partition.compatible'.--- bipartitionCompatible :: (Show a, Ord a) => Either String (Bipartition a) -> Set a -> Bool--- -- compatible (Bipartition (l, r)) ss = sintersection l ss `sdisjoint` sintersection r ss--- bipartitionCompatible (Left _) _ = False--- bipartitionCompatible (Right p) s = S.null lOverlap || S.null rOverlap---   where---     (l, r) = fromBipartition p---     lOverlap = S.intersection l s---     rOverlap = S.intersection r s---- compatibleAll :: (Show a, Ord a) => Tree e a -> [Set a] -> Bool--- compatibleAll (Node _ _ [l, r]) cs =---   all (bipartitionCompatible (bipartition l)) cs && all (bipartitionCompatible (bipartition r)) cs--- compatibleAll _ _ = error "Tree is not bifurcating."---- compatibleWith ::---   (Show b, Ord b) => (a -> b) -> [Set a] -> Tree e a -> Bool--- compatibleWith f cs t = compatibleAll (fmap f t) (map (S.map f) cs)---- -- Get groups induced by multifurcations. Collect the leaves of all trees--- -- induced by multifurcations.--- multifurcatingGroups :: Tree e a -> [[a]]--- multifurcatingGroups (Node _ _ []) = []--- multifurcatingGroups (Node _ _ [x]) = multifurcatingGroups x--- multifurcatingGroups (Node _ _ [x, y]) = multifurcatingGroups x ++ multifurcatingGroups y--- multifurcatingGroups t = leaves t : concatMap multifurcatingGroups (forest t)---- -- TODO.--- prop_bifurcating_tree---   :: (Ord a, Measurable a, Named a, BranchSupported 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-10) $-    map totalBranchLength $-      either error id $-        roots t-  where-    l = totalBranchLength t-prop_roots_total_length _ = True--spec :: Spec-spec = do-  -- TODO: describe "Resolve"--  describe "roots" $ do-    it "correctly handles leaves and cherries" $ do-      let tleaf = Node () 0 [] :: Tree () Int-          tcherry = Node () 0 [Node () 1 [], Node () 2 []] :: Tree () Int-      roots tleaf `shouldSatisfy` isLeft-      roots tcherry `shouldBe` Right [tcherry]-    it "correctly handles simple trees" $-      either error id (roots simpleTree) `shouldBe` simpleSol-    modifyMaxSize (* 100) $-      it "returns the correct number of rooted trees for arbitrary trees" $-        property (prop_roots :: (Tree () Int -> Bool))-  describe "rootAt" $-    modifyMaxSize (* 100) $-      it "correctly handles simple trees" $-        do-          let p = either error id $ bipartition simpleTree-          rootAt p simpleTree `shouldBe` Right simpleTree-          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))-  describe "rootsWithBranch" $-    modifyMaxSize (* 100) $-      it "does not change the tree height" $-        property (prop_roots_total_length :: Tree Length Int -> Bool)---- -- TODO: Move this test to the executable.--- describe "connect" $---   modifyMaxSize (* 100) $ do---     it "returns the correct number of rooted trees for arbitrary trees" $---       property (prop_connect :: Int -> Tree () Int -> Tree () Int -> Bool)---     it "correctly connects sample trees without and with constraints" $ do---       a <- parseFileWith (oneNewick Standard) "data/ConnectA.tree"---       b <- parseFileWith (oneNewick Standard) "data/ConnectB.tree"---       c <- parseFileWith (someNewick Standard) "data/ConnectConstraints.tree"---       let ts =---             either error id $---               connect "ROOT" (first (const ()) a) (first (const ()) b)---           cs =---             map S.fromList $---               concatMap (multifurcatingGroups . first (const ())) c ::---               [Set ByteString]---           ts' = filter (compatibleWith getName cs) ts---       length ts `shouldBe` 63---       length ts' `shouldBe` 15
− test/ELynx/Data/Tree/RootedSpec.hs
@@ -1,68 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Data.Tree.RootedSpec--- Description :  Unit tests for ELynx.Data.Tree.Rooted--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Mon May  6 14:04:05 2019.-module ELynx.Data.Tree.RootedSpec-  ( spec,-  )-where--import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BL-import Data.Maybe-import ELynx.Data.Tree-import ELynx.Import.Tree.Newick-import ELynx.Tools-import Test.Hspec--node :: Int -> Tree () Int-node n = Node () n []--smallTree :: Tree () Int-smallTree = Node () 0 [node 1, node 2]--smallSubTree :: Tree () Int-smallSubTree = Node () 0 [node 1]--smallSubTreePruned :: Tree () Int-smallSubTreePruned = node 1--sampleTreeBS :: BL.ByteString-sampleTreeBS = "(Aeropyrum0:0.5478645225,(((((((((Arabidopsi:0.0701001024,Oryza_sati:0.0765988261):0.0309636193,Gymnosperm:0.0520325624):0.0338982245,Physcomitr:0.0768008916):0.0895714685,(Chlamydomo:0.1136227755,Dunaliella:0.1406347323):0.1117340620):0.0818876186,Rhodophyta:0.3405656487):0.0363527066,((((((Babesia_bo:0.1646969208,Theileria0:0.1519889486):0.1908081096,Plasmodium:0.3250696762):0.0637865908,(Toxoplasma:0.1153570425,Eimeria000:0.1671916078):0.0980136930):0.0518956330,Cryptospor:0.3175062809):0.1607708388,Ciliophora:0.5687502950):0.0624078848,(Phytophtho:0.2016424948,((Thalassios:0.1202730781,Phaeodacty:0.1290341329):0.1772775509,Phaeophyce:0.1989260715):0.0312359673):0.1154768302):0.0311952864):0.0149160316,(((((((((Candida_al:0.1027755272,Saccharomy:0.1190206560):0.1333487870,Neurospora:0.1977309079):0.0522926266,Schizosacc:0.2019603227):0.0567441011,(Cryptococc:0.1948614959,Ustilago_m:0.1564451295):0.0775729694):0.0323959951,Glomus_int:0.1573670796):0.0194701292,Chytridiom:0.2228415254):0.0384370601,Encephalit:1.4622174644):0.0416231688,(((Drosophila:0.2160627753,(Mammalians:0.1080484094,Tunicates0:0.1739253014):0.0289624371):0.0346633757,Hydrozoa00:0.2058137032):0.0480963050,Monosiga_b:0.3020637584):0.0654894239):0.0380915725,(Dictyostel:0.3453588998,Mastigamoe:0.3844779231):0.0478795653):0.0129578395):1.7592083381,((Archaeoglo:0.5402784445,Methanococ:0.4088567459):0.0993669265,Pyrococcus:0.4058713829):0.1734405968):0.2193511807,Pyrobaculu:0.7507718047):0.1646616482,Sulfolobus:0.5404967897);"--largeTree :: Tree Phylo BS.ByteString-largeTree = parseByteStringWith (newick Standard) sampleTreeBS--subSampleLargeTree :: Tree Phylo BS.ByteString-subSampleLargeTree = fromJust $ dropLeavesWith ((/= 'P') . BS.head) largeTree--spec :: Spec-spec = do-  describe "prune" $ do-    it "leaves a normal tree untouched" $-      prune largeTree `shouldBe` largeTree-    it "correctly prunes a small example" $-      prune smallSubTree `shouldBe` smallSubTreePruned-    it "leaves height constant for Measurable trees" $ do-      let t' =-            either error id $-              phyloToLengthTree subSampleLargeTree-      height (prune t') `shouldBe` height t'-  describe "dropLeavesWith" $ do-    it "returns the same tree if no leaves satisfy predicate" $-      dropLeavesWith (const False) smallTree `shouldBe` Just smallTree-    it "returns nothing if all leaves satisfy predicate" $-      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/Data/Tree/SupportedSpec.hs
@@ -1,42 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Data.Tree.SupportedSpec--- Description :  Unit tests for ELynx.Data.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.Data.Tree.SupportedSpec-  ( spec,-  )-where--import qualified Data.ByteString.Char8 as BS-import ELynx.Data.Tree-import ELynx.Import.Tree.Newick--- import ELynx.Export.Tree.Newick-import ELynx.Tools-import Test.Hspec--collapseTree :: Tree Phylo BS.ByteString-collapseTree = parseByteStringWith (oneNewick IqTree) "((a,b),(c,d));"--collapseStarTree :: Tree Phylo BS.ByteString-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/Export/Tree/NewickSpec.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Export.Tree.NewickSpec--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Wed Feb 13 11:01:53 2019.-module ELynx.Export.Tree.NewickSpec-  ( spec,-  )-where--import Data.Bifunctor-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BL-import ELynx.Data.Tree-import ELynx.Data.Tree.Arbitrary ()-import ELynx.Export.Tree.Newick-import ELynx.Import.Tree.Newick-import ELynx.Tools-import Test.Hspec-import Test.QuickCheck hiding (label)--samplePhyloByteStringTree :: Tree Phylo BS.ByteString-samplePhyloByteStringTree = parseByteStringWith (newick Standard) sampleNewickByteString1--sampleNewickByteString1 :: BL.ByteString-sampleNewickByteString1 = "(Aeropyrum0:0.5478645225,(((((((((Arabidopsi:0.0701001024,Oryza_sati:0.0765988261):0.0309636193,Gymnosperm:0.0520325624):0.0338982245,Physcomitr:0.0768008916):0.0895714685,(Chlamydomo:0.1136227755,Dunaliella:0.1406347323):0.1117340620):0.0818876186,Rhodophyta:0.3405656487):0.0363527066,((((((Babesia_bo:0.1646969208,Theileria0:0.1519889486):0.1908081096,Plasmodium:0.3250696762):0.0637865908,(Toxoplasma:0.1153570425,Eimeria000:0.1671916078):0.0980136930):0.0518956330,Cryptospor:0.3175062809):0.1607708388,Ciliophora:0.5687502950):0.0624078848,(Phytophtho:0.2016424948,((Thalassios:0.1202730781,Phaeodacty:0.1290341329):0.1772775509,Phaeophyce:0.1989260715):0.0312359673):0.1154768302):0.0311952864):0.0149160316,(((((((((Candida_al:0.1027755272,Saccharomy:0.1190206560):0.1333487870,Neurospora:0.1977309079):0.0522926266,Schizosacc:0.2019603227):0.0567441011,(Cryptococc:0.1948614959,Ustilago_m:0.1564451295):0.0775729694):0.0323959951,Glomus_int:0.1573670796):0.0194701292,Chytridiom:0.2228415254):0.0384370601,Encephalit:1.4622174644):0.0416231688,(((Drosophila:0.2160627753,(Mammalians:0.1080484094,Tunicates0:0.1739253014):0.0289624371):0.0346633757,Hydrozoa00:0.2058137032):0.0480963050,Monosiga_b:0.3020637584):0.0654894239):0.0380915725,(Dictyostel:0.3453588998,Mastigamoe:0.3844779231):0.0478795653):0.0129578395):1.7592083381,((Archaeoglo:0.5402784445,Methanococ:0.4088567459):0.0993669265,Pyrococcus:0.4058713829):0.1734405968):0.2193511807,Pyrobaculu:0.7507718047):0.1646616482,Sulfolobus:0.5404967897);"--prop_invariant :: Tree Phylo Int -> Bool-prop_invariant t =-  parseByteStringWith (newick Standard) (toNewick t)-    ==-    -- We have to convert Int to ByteString, but we cannot use-    -- arbitrary instances with ByteStrings because many-    -- characters are disallowed and break the Newick string.-    second (BL.toStrict . getName) t--spec :: Spec-spec = describe "parseByteStringWith newick $ toNewickPhyloByteString" $ do-  it "should be an invariant for a sample tree" $ do-    let bs = toNewick samplePhyloByteStringTree-    parseByteStringWith-      (newick Standard)-      bs-      `shouldBe` samplePhyloByteStringTree-  it "should be an invariant for a sample tree" $ property prop_invariant
− test/ELynx/Export/Tree/NexusSpec.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Export.Tree.NexusSpec--- Description :  Test export of trees in Nexus files--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Tue Apr 28 18:08:14 2020.-module ELynx.Export.Tree.NexusSpec-  ( spec,-  )-where--import qualified Data.ByteString.Char8 as BS-import ELynx.Data.Tree-import ELynx.Export.Tree.Nexus-import ELynx.Import.Tree.Newick (NewickFormat (..))-import ELynx.Import.Tree.Nexus-import ELynx.Tools-import Test.Hspec--tree :: Tree Phylo BS.ByteString-tree =-  Node-    (Phylo Nothing Nothing)-    ""-    [ Node-        (Phylo Nothing Nothing)-        ""-        [ Node (Phylo Nothing Nothing) "A" [],-          Node (Phylo Nothing Nothing) "B" []-        ],-      Node (Phylo Nothing Nothing) "C" []-    ]--spec :: Spec-spec = describe "toNexusTrees" $-  it "exports a nexus file with a TREES block" $-    do-      let ts = parseByteStringWith (nexusTrees Standard) (toNexusTrees [("tree1", tree)])-      head ts `shouldBe` ("tree1", tree)
− test/ELynx/Import/Tree/NewickSpec.hs
@@ -1,134 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Import.Tree.NewickSpec--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3.0-or-later------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Fri Jan 18 10:14:04 2019.-module ELynx.Import.Tree.NewickSpec-  ( spec,-  )-where--import Data.Attoparsec.ByteString.Char8-import qualified Data.ByteString.Char8 as BS-import Data.Either-import ELynx.Data.Tree-import ELynx.Import.Tree.Newick-import ELynx.Tools-import Test.Hspec---- sampleLabelByteString :: ByteString--- sampleLabelByteString = "name:0.3"---- sampleLeaf :: Tree Phylo ByteString--- sampleLeaf = Node (Phylo (Just 0.3) Nothing) "name" []---- sampleForestByteString :: ByteString--- sampleForestByteString = "(l,l,(a,b))"--noPL :: Phylo-noPL = Phylo Nothing Nothing---- sampleForest :: Forest Phylo ByteString--- sampleForest =---   [ Node noPL "l" [],---     Node noPL "l" [],---     Node---       noPL---       ""---       [ Node noPL "a" [],---         Node noPL "b" []---       ]---   ]--sampleNewickByteString1 :: BS.ByteString-sampleNewickByteString1 = "(Aeropyrum0:0.5478645225,(((((((((Arabidopsi:0.0701001024,Oryza_sati:0.0765988261):0.0309636193,Gymnosperm:0.0520325624):0.0338982245,Physcomitr:0.0768008916):0.0895714685,(Chlamydomo:0.1136227755,Dunaliella:0.1406347323):0.1117340620):0.0818876186,Rhodophyta:0.3405656487):0.0363527066,((((((Babesia_bo:0.1646969208,Theileria0:0.1519889486):0.1908081096,Plasmodium:0.3250696762):0.0637865908,(Toxoplasma:0.1153570425,Eimeria000:0.1671916078):0.0980136930):0.0518956330,Cryptospor:0.3175062809):0.1607708388,Ciliophora:0.5687502950):0.0624078848,(Phytophtho:0.2016424948,((Thalassios:0.1202730781,Phaeodacty:0.1290341329):0.1772775509,Phaeophyce:0.1989260715):0.0312359673):0.1154768302):0.0311952864):0.0149160316,(((((((((Candida_al:0.1027755272,Saccharomy:0.1190206560):0.1333487870,Neurospora:0.1977309079):0.0522926266,Schizosacc:0.2019603227):0.0567441011,(Cryptococc:0.1948614959,Ustilago_m:0.1564451295):0.0775729694):0.0323959951,Glomus_int:0.1573670796):0.0194701292,Chytridiom:0.2228415254):0.0384370601,Encephalit:1.4622174644):0.0416231688,(((Drosophila:0.2160627753,(Mammalians:0.1080484094,Tunicates0:0.1739253014):0.0289624371):0.0346633757,Hydrozoa00:0.2058137032):0.0480963050,Monosiga_b:0.3020637584):0.0654894239):0.0380915725,(Dictyostel:0.3453588998,Mastigamoe:0.3844779231):0.0478795653):0.0129578395):1.7592083381,((Archaeoglo:0.5402784445,Methanococ:0.4088567459):0.0993669265,Pyrococcus:0.4058713829):0.1734405968):0.2193511807,Pyrobaculu:0.7507718047):0.1646616482,Sulfolobus:0.5404967897);"--sampleNewickByteString2 :: BS.ByteString-sampleNewickByteString2 =-  "(Caenorhabd:0.0176707431,C0briggsae:0.0142817073,(Ancylostom:0.0711440844,(Pristionch:0.1301309005,((Brugia_mal:0.0757534325,Ascaris0su:0.0482660407)1:0.0563924634,(((Meloidogyn:0.1239621893,Heteroderi:0.0987968800)1:0.1136879428,Strongyloi:0.2483437292)1:0.0252467381,(Trichoceph:0.2985037612,((((((Coleoptera:0.0907850846,(Apis0melli:0.0754058285,Hemiptera0:0.1675359618)0.93:0.0085703192)1:0.0146980945,(Siphonapte:0.0556805916,Bombyx0mor:0.0968983509)1:0.0127867903)1:0.0167360185,((Drosophila:0.0492149086,Glossina0m:0.0534390467)1:0.0583462602,Anopheles0:0.0968919941)1:0.0431343553)1:0.0535616453,Crustacea0:0.2247268999)1:0.0252755187,Chelicerat:0.1537491558)1:0.0212497286,((Echinoderm:0.1803896615,(Cephalocho:0.1492264574,(Urochordat:0.2194747834,(Mammalia00:0.0393008407,Actinopter:0.0491700096):0.0858550024)1:0.0157515969)1:0.0132516777)1:0.0203423736,((((((((Neurospora:0.0721607581,Magnaporth:0.0814182810)1:0.0198940548,Gibberella:0.0858192964)1:0.0533872590,Eurotiomyc:0.1058840539)1:0.1266302603,(Candida0al:0.1349957509,Saccharomy:0.1553464572)1:0.1791344287)1:0.0529664967,Schizosacc:0.2550087905)1:0.0723650615,(Ustilago0m:0.2031812772,(Homobasidi:0.1473391802,Cryptococc:0.2070743149)1:0.0347868586)1:0.0790327507)1:0.0727415175,Glomales00:0.1779430068)1:0.0169066667,Chytridiom:0.3028920870)1:0.3311420273)1:0.0278566156)1:0.1049569161)1:0.1366217350)1:0.0171168289)1:0.0345725378)1:0.0542036935)1:0.0879337167)1;"--sampleNewickEmptyByteString :: BS.ByteString-sampleNewickEmptyByteString = "(,(,,),);"--sampleNewickEmpty :: Tree Phylo BS.ByteString-sampleNewickEmpty =-  Node-    noPL-    ""-    [ Node noPL "" [],-      Node-        noPL-        ""-        [ Node noPL "" [],-          Node noPL "" [],-          Node noPL "" []-        ],-      Node noPL "" []-    ]--sampleTreeNewickRevbayes :: BS.ByteString-sampleTreeNewickRevbayes = "[&R](l[IDL]:0.3[KEYVALPAIRS],r[IDR]:0.4[KEYVALPARIS])[ID]:0.3;"--sampleNewickRevBayesFile :: String-sampleNewickRevBayesFile = "data/NewickRevBayes.tree"--spec :: Spec-spec = do-  -- describe "branchLength" $ do-  --   it "parses a colon and a branch length" $-  --     parse branchLength "" ":13.2"-  --       `shouldParse` Just 13.2--  --   it "returns Nothing if no branch length is given" $-  --     parse branchLength "" ""-  --       `shouldParse` Nothing--  -- describe "name" $ do-  --   it "parses a string of printable characters" $-  --     parse name "" "aName"-  --       `shouldParse` "aName"--  --   it "parses blanks, colons, semicolons, parentheses, and sequare brackets" $-  --     parse name "" "aName bla"-  --       `shouldParse` "aName"--  --   it "allows empty names" $ parse name "" "" `shouldParse` BL.pack ""--  -- describe "node" $ do-  --   it "parses a tree node" $-  --     parse node "" sampleLabelByteString-  --       `shouldParse` sampleLabel--  --   it "parses tree nodes with empty names and branch lengths" $-  --     parse node "" ""-  --       `shouldParse` PhyloLabelSoft "" Nothing Nothing--  -- describe "leaf" $-  --   it "parses a leaf of a tree" $-  --     parse leaf "" sampleLabelByteString-  --       `shouldParse` sampleLeaf--  -- describe "forestP" $-  --   it "parses a set of trees within brackets" $-  --     parse forestP "" sampleForestByteString-  --       `shouldParse` sampleForest--  describe "newick" $ do-    it "parses newick trees" $ do-      parseOnly (newick Standard) sampleNewickByteString1 `shouldSatisfy` isRight-      parseOnly (newick Standard) sampleNewickByteString2 `shouldSatisfy` isRight-    it "parses a weird newick tree without node labels nor branch lengths" $-      parseOnly (newick Standard) sampleNewickEmptyByteString `shouldBe` Right sampleNewickEmpty-  describe "newickRevBayes" $-    it "parses newick trees in RevBayes format" $-      do-        parseOnly (newick RevBayes) sampleTreeNewickRevbayes `shouldSatisfy` isRight-        t2 <- parseFileWith (newick RevBayes) sampleNewickRevBayesFile-        length (leaves t2) `shouldBe` 102
− test/ELynx/Import/Tree/NexusSpec.hs
@@ -1,51 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- |--- Module      :  ELynx.Import.Tree.NexusSpec--- Description :  Test import of trees in Nexus files--- Copyright   :  (c) Dominik Schrempf 2020--- License     :  GPL-3------ Maintainer  :  dominik.schrempf@gmail.com--- Stability   :  unstable--- Portability :  portable------ Creation date: Tue Apr 28 18:08:14 2020.-module ELynx.Import.Tree.NexusSpec-  ( spec,-  )-where--import qualified Data.ByteString.Char8 as BS-import ELynx.Data.Tree-import ELynx.Import.Tree.Newick (NewickFormat (..))-import ELynx.Import.Tree.Nexus-import ELynx.Tools-import Test.Hspec--file :: FilePath-file = "data/SimpleTree.nex"--noPL :: Phylo-noPL = Phylo Nothing Nothing--res :: Tree Phylo BS.ByteString-res =-  Node-    noPL-    ""-    [ Node-        noPL-        ""-        [ Node noPL "A" [],-          Node noPL "B" []-        ],-      Node noPL "C" []-    ]--spec :: Spec-spec = describe "trees" $-  it "parses a nexus file with a TREES block" $-    do-      ts <- parseFileWith (nexusTrees Standard) file-      head ts `shouldBe` ("tree1", res)
+ test/ELynx/Topology/RootedSpec.hs view
@@ -0,0 +1,23 @@+-- |+-- Module      :  ELynx.Topology.RootedSpec+-- Description :  Unit tests for ELynx.Topology.Rooted+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Sat Jul 18 13:58:16 2020.+module ELynx.Topology.RootedSpec+  ( spec,+  )+where++import Test.Hspec++spec :: Spec+spec =+  describe "TODO" $+    it "returns the original number when given a positive input" $+      (1 :: Int) `shouldBe` 1
+ test/ELynx/Tree/Arbitrary.hs view
@@ -0,0 +1,66 @@+{-# OPTIONS_GHC -Wno-orphans #-}++-- |+-- Module      :  ELynx.Tree.Arbitrary+-- Description :  Arbitrary instance, needed for QuickCheck+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Tue Apr 21 17:13:23 2020.+module ELynx.Tree.Arbitrary+  (+  )+where++import Data.Traversable+import ELynx.Tree+import Test.QuickCheck++-- Of course, the boundaries for branch support and length have been chosen+-- pretty arbitrarily :).+--+-- XXX: This instance does not produce values without branch lengths nor branch+-- supports.+instance Arbitrary Phylo where+  arbitrary = Phylo <$> (Just <$> choose (1, 100)) <*> (Just <$> choose (0.001, 10))++instance Arbitrary2 Tree where+  liftArbitrary2 arbB arbN = go+    where+      go = sized $ \n -> do+        -- Sized is the size of the trees.+        br <- arbB+        val <- arbN+        pars <- frequency [(1, pure [1, 1]), (3, arbPartition (n - 1))] -- can go negative!+        frst <- for pars $ \i -> resize i go+        return $ Node br val frst+      arbPartition :: Int -> Gen [Int]+      arbPartition k = case compare k 1 of+        LT -> pure []+        EQ -> pure [1]+        GT -> do+          first <- elements [1 .. k]+          rest <- arbPartition $ k - first+          return $ first : rest++  liftShrink2 _ shrN = go+    where+      go (Node br val frst) =+        frst+          ++ [ Node br e fs+               | (e, fs) <- liftShrink2 shrN (liftShrink go) (val, frst)+             ]++instance (Arbitrary e, Arbitrary a) => Arbitrary (Tree e a) where+  arbitrary = arbitrary2++instance (CoArbitrary e, CoArbitrary a) => CoArbitrary (Tree e a) where+  coarbitrary (Node br val frst) =+    coarbitrary br . coarbitrary val . coarbitrary frst++instance Arbitrary Length where+  arbitrary = Length . getPositive <$> arbitrary
+ test/ELynx/Tree/BipartitionSpec.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.BipartitionSpec+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Fri Aug 30 09:38:50 2019.+module ELynx.Tree.BipartitionSpec+  ( spec,+  )+where++import qualified Data.ByteString.Char8 as BS+import Data.Map (Map)+import qualified Data.Map as M+import Data.Set (Set)+import qualified Data.Set as S+import ELynx.Tree+import ELynx.Tools+import Test.Hspec++sfrom :: [BS.ByteString] -> S.Set BS.ByteString+sfrom = S.fromList++treeFileSimple :: FilePath+treeFileSimple = "data/TreeDist.trees"++getSimpleTrees :: IO (Forest Phylo BS.ByteString)+getSimpleTrees = parseFileWith (someNewick Standard) treeFileSimple++bipartitionToBranchAnswer :: Map (Bipartition BS.ByteString) Length+bipartitionToBranchAnswer =+  M.fromList+    [ (bpUnsafe (sfrom ["B"]) (sfrom ["A", "C", "D", "E"]), 0.3),+      (bpUnsafe (sfrom ["B", "C", "D", "E"]) (sfrom ["A"]), 0.1),+      (bpUnsafe (sfrom ["B", "C", "E"]) (sfrom ["A", "D"]), 5.0e-2),+      (bpUnsafe (sfrom ["B", "E"]) (sfrom ["A", "C", "D"]), 0.4),+      (bpUnsafe (sfrom ["C"]) (sfrom ["A", "B", "D", "E"]), 1.0e-2),+      (bpUnsafe (sfrom ["D"]) (sfrom ["A", "B", "C", "E"]), 0.25),+      (bpUnsafe (sfrom ["E"]) (sfrom ["A", "B", "C", "D"]), 0.8)+    ]++bipartitionsFirstTree :: Set (Bipartition BS.ByteString)+bipartitionsFirstTree =+  S.fromList+    [ bpUnsafe (sfrom ["B"]) (sfrom ["A", "C", "D", "E"]),+      bpUnsafe (sfrom ["B", "C", "D", "E"]) (sfrom ["A"]),+      bpUnsafe (sfrom ["B", "D", "E"]) (sfrom ["A", "C"]),+      bpUnsafe (sfrom ["B", "E"]) (sfrom ["A", "C", "D"]),+      bpUnsafe (sfrom ["C"]) (sfrom ["A", "B", "D", "E"]),+      bpUnsafe (sfrom ["D"]) (sfrom ["A", "B", "C", "E"]),+      bpUnsafe (sfrom ["E"]) (sfrom ["A", "B", "C", "D"])+    ]++bipartitionsSecondTree :: Set (Bipartition BS.ByteString)+bipartitionsSecondTree =+  S.fromList+    [ bpUnsafe (sfrom ["B"]) (sfrom ["A", "C", "D", "E"]),+      bpUnsafe (sfrom ["B", "C", "D", "E"]) (sfrom ["A"]),+      bpUnsafe (sfrom ["B", "C", "E"]) (sfrom ["A", "D"]),+      bpUnsafe (sfrom ["B", "E"]) (sfrom ["A", "C", "D"]),+      bpUnsafe (sfrom ["C"]) (sfrom ["A", "B", "D", "E"]),+      bpUnsafe (sfrom ["D"]) (sfrom ["A", "B", "C", "E"]),+      bpUnsafe (sfrom ["E"]) (sfrom ["A", "B", "C", "D"])+    ]++spec :: Spec+spec = do+  describe "bipartitions" $+    it "calculates correct bipartitions for sample trees" $+      do+        simpleTrees <- getSimpleTrees+        let t1 = head simpleTrees+            t2 = simpleTrees !! 1+        bipartitions t1 `shouldBe` Right bipartitionsFirstTree+        bipartitions t2 `shouldBe` Right bipartitionsSecondTree+  describe "bipartitionToBranch" $+    it "creates a map from bipartitions to branch lengths" $+      do+        simpleTrees <- getSimpleTrees+        (phyloToLengthTree (simpleTrees !! 2) >>= bipartitionToBranch)+          `shouldBe` Right bipartitionToBranchAnswer
+ test/ELynx/Tree/DistanceSpec.hs view
@@ -0,0 +1,258 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.DistanceSpec+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Fri Aug 30 09:38:50 2019.+module ELynx.Tree.DistanceSpec+  ( spec,+  )+where++import qualified Data.ByteString.Char8 as BS+import ELynx.Tree+import ELynx.Tree.Arbitrary ()+import ELynx.Tools+import Test.Hspec+import Test.QuickCheck++treeFileSimple :: FilePath+treeFileSimple = "data/TreeDist.trees"++getSimpleTrees :: IO (Forest Phylo BS.ByteString)+getSimpleTrees = parseFileWith (someNewick Standard) treeFileSimple++treeFileMany :: FilePath+treeFileMany = "data/Many.trees"++getManyTrees :: IO (Forest Phylo BS.ByteString)+getManyTrees = parseFileWith (someNewick Standard) treeFileMany++-- I used treedist from Phylip to get the correct results.+-- See http://evolution.genetics.washington.edu/phylip/doc/treedist.html.+symmetricAnswers :: [Int]+symmetricAnswers =+  [ 6,+    8,+    0,+    0,+    12,+    20,+    18,+    20,+    10,+    2,+    10,+    4,+    4,+    4,+    4,+    4,+    10,+    16,+    8,+    2,+    4,+    0,+    0,+    0,+    10,+    4,+    0,+    0,+    2,+    2,+    0,+    0,+    4,+    0,+    2,+    0,+    8,+    6,+    2,+    6,+    4,+    4,+    8,+    0,+    0,+    4,+    2,+    0,+    10,+    0,+    0,+    10+  ]++branchScoreAnswers :: [Double]+branchScoreAnswers =+  [ 8.567916e-02,+    9.570577e-02,+    1.704571e-02,+    7.603990e-03,+    6.149761e-01,+    3.557070e-01,+    2.329811e-01,+    3.820208e-01,+    1.895421e-02,+    6.302364e-03,+    2.083286e-02,+    1.023777e-03,+    2.138244e-02,+    1.444380e-02,+    1.958628e-02,+    6.089461e-03,+    2.551873e-02,+    8.041220e-02,+    4.123102e-02,+    8.241811e-03,+    2.623805e-02,+    2.109278e-02,+    1.953769e-02,+    4.459926e-03,+    6.594537e-02,+    7.040703e-02,+    8.603133e-03,+    3.878009e-03,+    2.969969e-02,+    2.505262e-02,+    2.095988e-02,+    8.461041e-03,+    5.228005e-02,+    6.001320e-02,+    8.276652e-03,+    6.966115e-03,+    7.701581e-02,+    4.946339e-02,+    2.548024e-02,+    5.800598e-03,+    3.875927e-02,+    2.836737e-02,+    9.059706e-02,+    1.333325e-02,+    5.071356e-02,+    7.433056e-02,+    3.854717e-02,+    3.255993e-02,+    1.581909e-01,+    6.813096e-02,+    8.210513e-02,+    7.664642e-02+  ]++prop_dist_same_tree :: (Num b, Eq b) => (Tree e a -> Tree e a -> Either String b) -> Tree e a -> Bool+prop_dist_same_tree distanceMeasure t = distanceMeasure t t == Right 0++each :: Int -> [a] -> [a]+each n = map head . takeWhile (not . null) . iterate (drop n)++multifurcating :: Tree () Char+multifurcating =+  Node+    ()+    ' '+    [Node () 'A' [], Node () 'B' [], Node () ' ' [Node () 'C' [], Node () 'D' [], Node () 'E' []]]++bifurcatingComp :: Tree () Char+bifurcatingComp =+  Node+    ()+    ' '+    [ Node () ' ' [Node () 'A' [], Node () 'B' []],+      Node () ' ' [Node () 'C' [], Node () ' ' [Node () 'D' [], Node () 'E' []]]+    ]++bifurcatingIncomp :: Tree () Char+bifurcatingIncomp =+  Node+    ()+    ' '+    [ Node () ' ' [Node () 'A' [], Node () 'C' []],+      Node () ' ' [Node () 'B' [], Node () ' ' [Node () 'D' [], Node () 'E' []]]+    ]++incSplitTree1a :: Tree Phylo BS.ByteString+incSplitTree1a = parseByteStringWith (oneNewick IqTree) "((a,b)0.7,(c,d));"++incSplitTree1b :: Tree Phylo BS.ByteString+incSplitTree1b = parseByteStringWith (oneNewick IqTree) "((a,b)0.7,c,d);"++incSplitTree2 :: Tree Phylo BS.ByteString+incSplitTree2 = parseByteStringWith (oneNewick IqTree) "((a,c),(b,d));"++incSplitTree3 :: Tree Phylo BS.ByteString+incSplitTree3 = parseByteStringWith (oneNewick IqTree) "(((a,b)0.7,c),(d,e));"++incSplitTree4 :: Tree Phylo BS.ByteString+incSplitTree4 = parseByteStringWith (oneNewick IqTree) "(((a,c),b),(d,e));"++-- Compute distances between adjacent pairs of a list of input trees. Use given+-- distance measure.+adjacent :: (a -> a -> b) -> [a] -> [b]+adjacent dist trs = [dist x y | (x, y) <- zip trs (tail trs)]++-- noPL :: Phylo+-- noPL = Phylo Nothing Nothing++spec :: Spec+spec = do+  describe "symmetric" $ do+    it "calculates correct distances for sample trees" $ do+      simpleTrees <- getSimpleTrees+      symmetric (head simpleTrees) (simpleTrees !! 1) `shouldBe` Right 2+      manyTrees <- getManyTrees+      -- Since treedist computes the distance between adjacent pairs, in the+      -- following manner: [tr0, tr1, tr2, tr3] -> [dist tr0 tr1, dist tr2 tr3],+      -- we have to skip some distances.+      each 2 (adjacent symmetric manyTrees)+        `shouldBe` map Right symmetricAnswers+    it "is zero for a collection of random trees" $+      property $+        prop_dist_same_tree+          (symmetric :: Tree Phylo Double -> Tree Phylo Double -> Either String Int)+  describe "incompatibleSplit" $ do+    it "calculates correct distances for sample trees" $ do+      incompatibleSplits multifurcating bifurcatingComp `shouldBe` Right 0+      incompatibleSplits bifurcatingComp multifurcating `shouldBe` Right 0+      -- print $ S.map bpHuman <$> bipartitions bifurcatingIncomp+      -- print $ S.map bpHuman <$> bipartitions multifurcating+      -- print $ S.map mpHuman <$> partitions bifurcatingIncomp+      -- print $ S.map mpHuman <$> partitions multifurcating+      -- print $ toNewick $ first (const noPL) bifurcatingIncomp+      -- print $ toNewick $ first (const noPL) multifurcating+      incompatibleSplits bifurcatingIncomp multifurcating `shouldBe` Right 2+      incompatibleSplits multifurcating bifurcatingIncomp `shouldBe` Right 2+    it "calculates correct distances for sample trees with branch support" $ do+      incompatibleSplits incSplitTree1a incSplitTree2 `shouldBe` Right 2+      incompatibleSplits incSplitTree1b incSplitTree2 `shouldBe` Right 2+      let t1a = phyloToSupportTreeUnsafe incSplitTree1a+          t1b = phyloToSupportTreeUnsafe incSplitTree1b+          tr2 = phyloToSupportTreeUnsafe incSplitTree2+          tr3 = phyloToSupportTreeUnsafe incSplitTree3+          tr4 = phyloToSupportTreeUnsafe incSplitTree4+      incompatibleSplits (collapse 0.7 t1a) tr2 `shouldBe` Right 2+      incompatibleSplits (collapse 0.71 t1b) tr2 `shouldBe` Right 0+      incompatibleSplits (collapse 0.71 tr3) tr4 `shouldBe` Right 0+    it "is zero for a collection of random trees" $+      property $+        prop_dist_same_tree+          (incompatibleSplits :: Tree Phylo Double -> Tree Phylo Double -> Either String Int)+  describe "branchScore" $ do+    it "calculates correct distances for sample trees" $ do+      manyTrees <- getManyTrees+      let ts = map (either error id . phyloToLengthTree) manyTrees+      let ds = map (either error id) $ each 2 $ adjacent branchScore ts+      ds `shouldSatisfy` nearlyEqListWith 1e-5 branchScoreAnswers+    it "is zero for a collection of random trees" $+      property $+        prop_dist_same_tree+          (branchScore :: Tree Length Double -> Tree Length Double -> Either String Double)
+ test/ELynx/Tree/Export/NewickSpec.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.Export.NewickSpec+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Wed Feb 13 11:01:53 2019.+module ELynx.Tree.Export.NewickSpec+  ( spec,+  )+where++import Data.Bifunctor+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as BL+import ELynx.Tree+import ELynx.Tree.Arbitrary ()+import ELynx.Tools+import Test.Hspec+import Test.QuickCheck hiding (label)++samplePhyloByteStringTree :: Tree Phylo BS.ByteString+samplePhyloByteStringTree = parseByteStringWith (newick Standard) sampleNewickByteString1++sampleNewickByteString1 :: BL.ByteString+sampleNewickByteString1 = "(Aeropyrum0:0.5478645225,(((((((((Arabidopsi:0.0701001024,Oryza_sati:0.0765988261):0.0309636193,Gymnosperm:0.0520325624):0.0338982245,Physcomitr:0.0768008916):0.0895714685,(Chlamydomo:0.1136227755,Dunaliella:0.1406347323):0.1117340620):0.0818876186,Rhodophyta:0.3405656487):0.0363527066,((((((Babesia_bo:0.1646969208,Theileria0:0.1519889486):0.1908081096,Plasmodium:0.3250696762):0.0637865908,(Toxoplasma:0.1153570425,Eimeria000:0.1671916078):0.0980136930):0.0518956330,Cryptospor:0.3175062809):0.1607708388,Ciliophora:0.5687502950):0.0624078848,(Phytophtho:0.2016424948,((Thalassios:0.1202730781,Phaeodacty:0.1290341329):0.1772775509,Phaeophyce:0.1989260715):0.0312359673):0.1154768302):0.0311952864):0.0149160316,(((((((((Candida_al:0.1027755272,Saccharomy:0.1190206560):0.1333487870,Neurospora:0.1977309079):0.0522926266,Schizosacc:0.2019603227):0.0567441011,(Cryptococc:0.1948614959,Ustilago_m:0.1564451295):0.0775729694):0.0323959951,Glomus_int:0.1573670796):0.0194701292,Chytridiom:0.2228415254):0.0384370601,Encephalit:1.4622174644):0.0416231688,(((Drosophila:0.2160627753,(Mammalians:0.1080484094,Tunicates0:0.1739253014):0.0289624371):0.0346633757,Hydrozoa00:0.2058137032):0.0480963050,Monosiga_b:0.3020637584):0.0654894239):0.0380915725,(Dictyostel:0.3453588998,Mastigamoe:0.3844779231):0.0478795653):0.0129578395):1.7592083381,((Archaeoglo:0.5402784445,Methanococ:0.4088567459):0.0993669265,Pyrococcus:0.4058713829):0.1734405968):0.2193511807,Pyrobaculu:0.7507718047):0.1646616482,Sulfolobus:0.5404967897);"++prop_invariant :: Tree Phylo Int -> Bool+prop_invariant t =+  parseByteStringWith (newick Standard) (toNewick t)+    ==+    -- We have to convert Int to ByteString, but we cannot use+    -- arbitrary instances with ByteStrings because many+    -- characters are disallowed and break the Newick string.+    second (BL.toStrict . getName) t++spec :: Spec+spec = describe "parseByteStringWith newick $ toNewickPhyloByteString" $ do+  it "should be an invariant for a sample tree" $ do+    let bs = toNewick samplePhyloByteStringTree+    parseByteStringWith+      (newick Standard)+      bs+      `shouldBe` samplePhyloByteStringTree+  it "should be an invariant for a sample tree" $ property prop_invariant
+ test/ELynx/Tree/Export/NexusSpec.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.Export.NexusSpec+-- Description :  Test export of trees in Nexus files+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Tue Apr 28 18:08:14 2020.+module ELynx.Tree.Export.NexusSpec+  ( spec,+  )+where++import qualified Data.ByteString.Char8 as BS+import ELynx.Tree+import ELynx.Tools+import Test.Hspec++tree :: Tree Phylo BS.ByteString+tree =+  Node+    (Phylo Nothing Nothing)+    ""+    [ Node+        (Phylo Nothing Nothing)+        ""+        [ Node (Phylo Nothing Nothing) "A" [],+          Node (Phylo Nothing Nothing) "B" []+        ],+      Node (Phylo Nothing Nothing) "C" []+    ]++spec :: Spec+spec = describe "toNexusTrees" $+  it "exports a nexus file with a TREES block" $+    do+      let ts = parseByteStringWith (nexusTrees Standard) (toNexusTrees [("tree1", tree)])+      head ts `shouldBe` ("tree1", tree)
+ test/ELynx/Tree/Import/NewickSpec.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.Import.NewickSpec+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Fri Jan 18 10:14:04 2019.+module ELynx.Tree.Import.NewickSpec+  ( spec,+  )+where++import Data.Attoparsec.ByteString.Char8+import qualified Data.ByteString.Char8 as BS+import Data.Either+import ELynx.Tree+import ELynx.Tools+import Test.Hspec++-- sampleLabelByteString :: ByteString+-- sampleLabelByteString = "name:0.3"++-- sampleLeaf :: Tree Phylo ByteString+-- sampleLeaf = Node (Phylo (Just 0.3) Nothing) "name" []++-- sampleForestByteString :: ByteString+-- sampleForestByteString = "(l,l,(a,b))"++noPL :: Phylo+noPL = Phylo Nothing Nothing++-- sampleForest :: Forest Phylo ByteString+-- sampleForest =+--   [ Node noPL "l" [],+--     Node noPL "l" [],+--     Node+--       noPL+--       ""+--       [ Node noPL "a" [],+--         Node noPL "b" []+--       ]+--   ]++sampleNewickByteString1 :: BS.ByteString+sampleNewickByteString1 = "(Aeropyrum0:0.5478645225,(((((((((Arabidopsi:0.0701001024,Oryza_sati:0.0765988261):0.0309636193,Gymnosperm:0.0520325624):0.0338982245,Physcomitr:0.0768008916):0.0895714685,(Chlamydomo:0.1136227755,Dunaliella:0.1406347323):0.1117340620):0.0818876186,Rhodophyta:0.3405656487):0.0363527066,((((((Babesia_bo:0.1646969208,Theileria0:0.1519889486):0.1908081096,Plasmodium:0.3250696762):0.0637865908,(Toxoplasma:0.1153570425,Eimeria000:0.1671916078):0.0980136930):0.0518956330,Cryptospor:0.3175062809):0.1607708388,Ciliophora:0.5687502950):0.0624078848,(Phytophtho:0.2016424948,((Thalassios:0.1202730781,Phaeodacty:0.1290341329):0.1772775509,Phaeophyce:0.1989260715):0.0312359673):0.1154768302):0.0311952864):0.0149160316,(((((((((Candida_al:0.1027755272,Saccharomy:0.1190206560):0.1333487870,Neurospora:0.1977309079):0.0522926266,Schizosacc:0.2019603227):0.0567441011,(Cryptococc:0.1948614959,Ustilago_m:0.1564451295):0.0775729694):0.0323959951,Glomus_int:0.1573670796):0.0194701292,Chytridiom:0.2228415254):0.0384370601,Encephalit:1.4622174644):0.0416231688,(((Drosophila:0.2160627753,(Mammalians:0.1080484094,Tunicates0:0.1739253014):0.0289624371):0.0346633757,Hydrozoa00:0.2058137032):0.0480963050,Monosiga_b:0.3020637584):0.0654894239):0.0380915725,(Dictyostel:0.3453588998,Mastigamoe:0.3844779231):0.0478795653):0.0129578395):1.7592083381,((Archaeoglo:0.5402784445,Methanococ:0.4088567459):0.0993669265,Pyrococcus:0.4058713829):0.1734405968):0.2193511807,Pyrobaculu:0.7507718047):0.1646616482,Sulfolobus:0.5404967897);"++sampleNewickByteString2 :: BS.ByteString+sampleNewickByteString2 =+  "(Caenorhabd:0.0176707431,C0briggsae:0.0142817073,(Ancylostom:0.0711440844,(Pristionch:0.1301309005,((Brugia_mal:0.0757534325,Ascaris0su:0.0482660407)1:0.0563924634,(((Meloidogyn:0.1239621893,Heteroderi:0.0987968800)1:0.1136879428,Strongyloi:0.2483437292)1:0.0252467381,(Trichoceph:0.2985037612,((((((Coleoptera:0.0907850846,(Apis0melli:0.0754058285,Hemiptera0:0.1675359618)0.93:0.0085703192)1:0.0146980945,(Siphonapte:0.0556805916,Bombyx0mor:0.0968983509)1:0.0127867903)1:0.0167360185,((Drosophila:0.0492149086,Glossina0m:0.0534390467)1:0.0583462602,Anopheles0:0.0968919941)1:0.0431343553)1:0.0535616453,Crustacea0:0.2247268999)1:0.0252755187,Chelicerat:0.1537491558)1:0.0212497286,((Echinoderm:0.1803896615,(Cephalocho:0.1492264574,(Urochordat:0.2194747834,(Mammalia00:0.0393008407,Actinopter:0.0491700096):0.0858550024)1:0.0157515969)1:0.0132516777)1:0.0203423736,((((((((Neurospora:0.0721607581,Magnaporth:0.0814182810)1:0.0198940548,Gibberella:0.0858192964)1:0.0533872590,Eurotiomyc:0.1058840539)1:0.1266302603,(Candida0al:0.1349957509,Saccharomy:0.1553464572)1:0.1791344287)1:0.0529664967,Schizosacc:0.2550087905)1:0.0723650615,(Ustilago0m:0.2031812772,(Homobasidi:0.1473391802,Cryptococc:0.2070743149)1:0.0347868586)1:0.0790327507)1:0.0727415175,Glomales00:0.1779430068)1:0.0169066667,Chytridiom:0.3028920870)1:0.3311420273)1:0.0278566156)1:0.1049569161)1:0.1366217350)1:0.0171168289)1:0.0345725378)1:0.0542036935)1:0.0879337167)1;"++sampleNewickEmptyByteString :: BS.ByteString+sampleNewickEmptyByteString = "(,(,,),);"++sampleNewickEmpty :: Tree Phylo BS.ByteString+sampleNewickEmpty =+  Node+    noPL+    ""+    [ Node noPL "" [],+      Node+        noPL+        ""+        [ Node noPL "" [],+          Node noPL "" [],+          Node noPL "" []+        ],+      Node noPL "" []+    ]++sampleTreeNewickRevbayes :: BS.ByteString+sampleTreeNewickRevbayes = "[&R](l[IDL]:0.3[KEYVALPAIRS],r[IDR]:0.4[KEYVALPARIS])[ID]:0.3;"++sampleNewickRevBayesFile :: String+sampleNewickRevBayesFile = "data/NewickRevBayes.tree"++spec :: Spec+spec = do+  -- describe "branchLength" $ do+  --   it "parses a colon and a branch length" $+  --     parse branchLength "" ":13.2"+  --       `shouldParse` Just 13.2++  --   it "returns Nothing if no branch length is given" $+  --     parse branchLength "" ""+  --       `shouldParse` Nothing++  -- describe "name" $ do+  --   it "parses a string of printable characters" $+  --     parse name "" "aName"+  --       `shouldParse` "aName"++  --   it "parses blanks, colons, semicolons, parentheses, and sequare brackets" $+  --     parse name "" "aName bla"+  --       `shouldParse` "aName"++  --   it "allows empty names" $ parse name "" "" `shouldParse` BL.pack ""++  -- describe "node" $ do+  --   it "parses a tree node" $+  --     parse node "" sampleLabelByteString+  --       `shouldParse` sampleLabel++  --   it "parses tree nodes with empty names and branch lengths" $+  --     parse node "" ""+  --       `shouldParse` PhyloLabelSoft "" Nothing Nothing++  -- describe "leaf" $+  --   it "parses a leaf of a tree" $+  --     parse leaf "" sampleLabelByteString+  --       `shouldParse` sampleLeaf++  -- describe "forestP" $+  --   it "parses a set of trees within brackets" $+  --     parse forestP "" sampleForestByteString+  --       `shouldParse` sampleForest++  describe "newick" $ do+    it "parses newick trees" $ do+      parseOnly (newick Standard) sampleNewickByteString1 `shouldSatisfy` isRight+      parseOnly (newick Standard) sampleNewickByteString2 `shouldSatisfy` isRight+    it "parses a weird newick tree without node labels nor branch lengths" $+      parseOnly (newick Standard) sampleNewickEmptyByteString `shouldBe` Right sampleNewickEmpty+  describe "newickRevBayes" $+    it "parses newick trees in RevBayes format" $+      do+        parseOnly (newick RevBayes) sampleTreeNewickRevbayes `shouldSatisfy` isRight+        t2 <- parseFileWith (newick RevBayes) sampleNewickRevBayesFile+        length (leaves t2) `shouldBe` 102
+ test/ELynx/Tree/Import/NexusSpec.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.Import.NexusSpec+-- Description :  Test import of trees in Nexus files+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Tue Apr 28 18:08:14 2020.+module ELynx.Tree.Import.NexusSpec+  ( spec,+  )+where++import qualified Data.ByteString.Char8 as BS+import ELynx.Tree+import ELynx.Tools+import Test.Hspec++file :: FilePath+file = "data/SimpleTree.nex"++noPL :: Phylo+noPL = Phylo Nothing Nothing++res :: Tree Phylo BS.ByteString+res =+  Node+    noPL+    ""+    [ Node+        noPL+        ""+        [ Node noPL "A" [],+          Node noPL "B" []+        ],+      Node noPL "C" []+    ]++spec :: Spec+spec = describe "trees" $+  it "parses a nexus file with a TREES block" $+    do+      ts <- parseFileWith (nexusTrees Standard) file+      head ts `shouldBe` ("tree1", res)
+ test/ELynx/Tree/PartitionSpec.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.PartitionSpec+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Fri Aug 30 09:38:50 2019.+module ELynx.Tree.PartitionSpec+  ( spec,+  )+where++import Data.Set (Set, fromList)+import ELynx.Tree+import ELynx.Tree.Arbitrary ()+import Test.Hspec++ex1 :: Tree () Int+ex1 = Node () 0 [Node () 1 [], Node () 2 [Node () 4 [], Node () 5 [], Node () 6 []], Node () 3 []]++sol1 :: Set (Partition Int)+sol1 =+  fromList+    [ mpUnsafe [fromList [1], fromList [3], fromList [4, 5, 6]],+      mpUnsafe [fromList [1, 3], fromList [4], fromList [5], fromList [6]]+    ]++ex2 :: Tree () Int+ex2 = Node () 0 [Node () 1 [], Node () 2 [], Node () 0 [Node () 3 [], Node () 4 []], Node () 5 []]++sol2 :: Set (Partition Int)+sol2 =+  fromList+    [ mpUnsafe [fromList [1], fromList [2], fromList [3, 4], fromList [5]],+      mpUnsafe [fromList [1, 2, 5], fromList [3], fromList [4]]+    ]++spec :: Spec+spec = describe "partitions" $+  it "calculates correct partitions for a sample tree" $+    do+      -- t <- removeBrInfo <$> getMultifurcatingTree+      -- print $ partitions ex1+      partitions ex1 `shouldBe` Right sol1+      partitions ex2 `shouldBe` Right sol2++-- it "is empty for a collection of random bifurcating trees"+--   $ property (prop_bifurcating_tree :: Tree (PhyloLabel Double) -> Bool)
+ test/ELynx/Tree/PhylogenySpec.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- |+-- Module      :  ELynx.Tree.PhylogenySpec+-- Description :  Unit tests for ELynx.Tree.Phylogeny+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Wed Jul 15 11:05:32 2020.+module ELynx.Tree.PhylogenySpec+  ( spec,+  )+where++import Data.Either+import qualified Data.Set as S+import ELynx.Tree+import ELynx.Tree.Arbitrary ()+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck hiding (labels)++simpleTree :: Tree () String+simpleTree = Node () "i" [Node () "j" [Node () "x" [], Node () "y" []], Node () "z" []]++simpleSol :: Forest () String+simpleSol =+  [ Node () "i" [Node () "j" [Node () "x" [], Node () "y" []], Node () "z" []],+    Node () "i" [Node () "j" [Node () "z" [], Node () "y" []], Node () "x" []],+    Node () "i" [Node () "j" [Node () "z" [], Node () "x" []], Node () "y" []]+  ]++instance Splittable () where+  split = id++-- Skip leaves and trees with multifurcating root nodes.+prop_roots :: Tree () a -> Bool+prop_roots t@(Node _ _ [_, _])+  | length (leaves t) == 2 = (length <$> roots t) == Right 1+  | otherwise = (length <$> roots t) == (Right $ length (labels t) - 2)+prop_roots _ = True++-- -- Skip leaves and trees with multifurcating root nodes.+-- prop_connect :: a -> Tree () a -> Tree () a -> Bool+-- prop_connect n l@(Node _ _ [_, _]) r@(Node _ _ [_, _])+--   | length (leaves l) < 3 = (length <$> connect n l r) == Right (length (flatten r) - 2)+--   | length (leaves r) < 3 = (length <$> connect n l r) == Right (length (flatten l) - 2)+--   | otherwise =+--     (length <$> connect n l r)+--       == (Right $ (length (flatten l) - 2) * (length (flatten r) - 2))+-- prop_connect _ _ _ = True++-- -- | Determine compatibility between a bipartition and a set.+-- --+-- -- If both subsets of the bipartition share elements with the given set, the+-- -- bipartition is incompatible with this subset. If all elements of the subset+-- -- are either not in the bipartition or mapping to one of the two subsets of the+-- -- bipartition, the bipartition and the subset are compatible.+-- --+-- -- See also 'ELynx.Tree.Partition.compatible'.+-- bipartitionCompatible :: (Show a, Ord a) => Either String (Bipartition a) -> Set a -> Bool+-- -- compatible (Bipartition (l, r)) ss = sintersection l ss `sdisjoint` sintersection r ss+-- bipartitionCompatible (Left _) _ = False+-- bipartitionCompatible (Right p) s = S.null lOverlap || S.null rOverlap+--   where+--     (l, r) = fromBipartition p+--     lOverlap = S.intersection l s+--     rOverlap = S.intersection r s++-- compatibleAll :: (Show a, Ord a) => Tree e a -> [Set a] -> Bool+-- compatibleAll (Node _ _ [l, r]) cs =+--   all (bipartitionCompatible (bipartition l)) cs && all (bipartitionCompatible (bipartition r)) cs+-- compatibleAll _ _ = error "Tree is not bifurcating."++-- compatibleWith ::+--   (Show b, Ord b) => (a -> b) -> [Set a] -> Tree e a -> Bool+-- compatibleWith f cs t = compatibleAll (fmap f t) (map (S.map f) cs)++-- -- Get groups induced by multifurcations. Collect the leaves of all trees+-- -- induced by multifurcations.+-- multifurcatingGroups :: Tree e a -> [[a]]+-- multifurcatingGroups (Node _ _ []) = []+-- multifurcatingGroups (Node _ _ [x]) = multifurcatingGroups x+-- multifurcatingGroups (Node _ _ [x, y]) = multifurcatingGroups x ++ multifurcatingGroups y+-- multifurcatingGroups t = leaves t : concatMap multifurcatingGroups (forest t)++-- -- TODO.+-- prop_bifurcating_tree+--   :: (Ord a, Measurable a, Named a, BranchSupported 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+  where+    l = totalBranchLength t+prop_roots_total_length _ = True++spec :: Spec+spec = do+  -- TODO: describe "Resolve"++  describe "roots" $ do+    it "correctly handles leaves and cherries" $ do+      let tleaf = Node () 0 [] :: Tree () Int+          tcherry = Node () 0 [Node () 1 [], Node () 2 []] :: Tree () Int+      roots tleaf `shouldSatisfy` isLeft+      roots tcherry `shouldBe` Right [tcherry]+    it "correctly handles simple trees" $+      either error id (roots simpleTree) `shouldBe` simpleSol+    modifyMaxSize (* 100) $+      it "returns the correct number of rooted trees for arbitrary trees" $+        property (prop_roots :: (Tree () Int -> Bool))+  describe "rootAt" $+    modifyMaxSize (* 100) $+      it "correctly handles simple trees" $+        do+          let p = either error id $ bipartition simpleTree+          rootAt p simpleTree `shouldBe` Right simpleTree+          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))+  describe "rootsWithBranch" $+    modifyMaxSize (* 100) $+      it "does not change the tree height" $+        property (prop_roots_total_length :: Tree Length Int -> Bool)++-- -- TODO: Move this test to the executable.+-- describe "connect" $+--   modifyMaxSize (* 100) $ do+--     it "returns the correct number of rooted trees for arbitrary trees" $+--       property (prop_connect :: Int -> Tree () Int -> Tree () Int -> Bool)+--     it "correctly connects sample trees without and with constraints" $ do+--       a <- parseFileWith (oneNewick Standard) "data/ConnectA.tree"+--       b <- parseFileWith (oneNewick Standard) "data/ConnectB.tree"+--       c <- parseFileWith (someNewick Standard) "data/ConnectConstraints.tree"+--       let ts =+--             either error id $+--               connect "ROOT" (first (const ()) a) (first (const ()) b)+--           cs =+--             map S.fromList $+--               concatMap (multifurcatingGroups . first (const ())) c ::+--               [Set ByteString]+--           ts' = filter (compatibleWith getName cs) ts+--       length ts `shouldBe` 63+--       length ts' `shouldBe` 15
+ test/ELynx/Tree/RootedSpec.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  ELynx.Tree.RootedSpec+-- Description :  Unit tests for ELynx.Tree.Rooted+-- Copyright   :  (c) Dominik Schrempf 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Mon May  6 14:04:05 2019.+module ELynx.Tree.RootedSpec+  ( spec,+  )+where++import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as BL+import Data.Maybe+import ELynx.Tree+import ELynx.Tools+import Test.Hspec++node :: Int -> Tree () Int+node n = Node () n []++smallTree :: Tree () Int+smallTree = Node () 0 [node 1, node 2]++smallSubTree :: Tree () Int+smallSubTree = Node () 0 [node 1]++smallSubTreePruned :: Tree () Int+smallSubTreePruned = node 1++sampleTreeBS :: BL.ByteString+sampleTreeBS = "(Aeropyrum0:0.5478645225,(((((((((Arabidopsi:0.0701001024,Oryza_sati:0.0765988261):0.0309636193,Gymnosperm:0.0520325624):0.0338982245,Physcomitr:0.0768008916):0.0895714685,(Chlamydomo:0.1136227755,Dunaliella:0.1406347323):0.1117340620):0.0818876186,Rhodophyta:0.3405656487):0.0363527066,((((((Babesia_bo:0.1646969208,Theileria0:0.1519889486):0.1908081096,Plasmodium:0.3250696762):0.0637865908,(Toxoplasma:0.1153570425,Eimeria000:0.1671916078):0.0980136930):0.0518956330,Cryptospor:0.3175062809):0.1607708388,Ciliophora:0.5687502950):0.0624078848,(Phytophtho:0.2016424948,((Thalassios:0.1202730781,Phaeodacty:0.1290341329):0.1772775509,Phaeophyce:0.1989260715):0.0312359673):0.1154768302):0.0311952864):0.0149160316,(((((((((Candida_al:0.1027755272,Saccharomy:0.1190206560):0.1333487870,Neurospora:0.1977309079):0.0522926266,Schizosacc:0.2019603227):0.0567441011,(Cryptococc:0.1948614959,Ustilago_m:0.1564451295):0.0775729694):0.0323959951,Glomus_int:0.1573670796):0.0194701292,Chytridiom:0.2228415254):0.0384370601,Encephalit:1.4622174644):0.0416231688,(((Drosophila:0.2160627753,(Mammalians:0.1080484094,Tunicates0:0.1739253014):0.0289624371):0.0346633757,Hydrozoa00:0.2058137032):0.0480963050,Monosiga_b:0.3020637584):0.0654894239):0.0380915725,(Dictyostel:0.3453588998,Mastigamoe:0.3844779231):0.0478795653):0.0129578395):1.7592083381,((Archaeoglo:0.5402784445,Methanococ:0.4088567459):0.0993669265,Pyrococcus:0.4058713829):0.1734405968):0.2193511807,Pyrobaculu:0.7507718047):0.1646616482,Sulfolobus:0.5404967897);"++largeTree :: Tree Phylo BS.ByteString+largeTree = parseByteStringWith (newick Standard) sampleTreeBS++subSampleLargeTree :: Tree Phylo BS.ByteString+subSampleLargeTree = fromJust $ dropLeavesWith ((/= 'P') . BS.head) largeTree++spec :: Spec+spec = do+  describe "prune" $ do+    it "leaves a normal tree untouched" $+      prune largeTree `shouldBe` largeTree+    it "correctly prunes a small example" $+      prune smallSubTree `shouldBe` smallSubTreePruned+    it "leaves height constant for Measurable trees" $ do+      let t' =+            either error id $+              phyloToLengthTree subSampleLargeTree+      height (prune t') `shouldBe` height t'+  describe "dropLeavesWith" $ do+    it "returns the same tree if no leaves satisfy predicate" $+      dropLeavesWith (const False) smallTree `shouldBe` Just smallTree+    it "returns nothing if all leaves satisfy predicate" $+      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/SupportedSpec.hs view
@@ -0,0 +1,40 @@+{-# 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 qualified Data.ByteString.Char8 as BS+import ELynx.Tree+import ELynx.Tools+import Test.Hspec++collapseTree :: Tree Phylo BS.ByteString+collapseTree = parseByteStringWith (oneNewick IqTree) "((a,b),(c,d));"++collapseStarTree :: Tree Phylo BS.ByteString+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