tlynx-0.2.1: src/TLynx/Simulate/Simulate.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{- |
Description : Simulate reconstructed 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 27 17:27:16 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.
Mon Feb 4 14:26:11 CET 2019: Adding sampling probability rho. See Article
(Stadler2009) Stadler, T. On incomplete sampling under birth–death models and
connections to the sampling-based coalescent Journal of Theoretical Biology,
Elsevier BV, 2009, 261, 58-66
-}
module TLynx.Simulate.Simulate
( simulate
)
where
import Control.Monad.Trans.Reader ( ask )
import Control.Concurrent ( getNumCapabilities )
import Control.Concurrent.Async.Lifted.Safe
( mapConcurrently )
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Logger
import Control.Parallel.Strategies
import qualified Data.ByteString.Lazy.Char8 as L
import Data.Maybe
import qualified Data.Sequence as Seq
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.Encoding as LT
import Data.Tree
import System.Random.MWC ( GenIO
, initialize
)
import TLynx.Simulate.Options
import ELynx.Data.Tree
import ELynx.Export.Tree.Newick ( toNewick )
import ELynx.Simulate.PointProcess ( TimeSpec
, simulateNReconstructedTrees
, simulateReconstructedTree
)
import ELynx.Tools
-- | Simulate phylogenetic trees.
simulate :: ELynx SimulateArguments ()
simulate = do
l <- local <$> ask
let
SimulateArguments nTrees nLeaves tHeight mrca lambda mu rho subS sumS (Fixed s)
= l
-- error "simulate: seed not available; please contact maintainer."
when (isNothing tHeight && mrca)
$ error "Cannot condition on MRCA (-M) when height is not given (-H)."
c <- liftIO getNumCapabilities
logNewSection "Arguments"
$(logInfo) $ T.pack $ reportSimulateArguments l
logNewSection "Simulation"
$(logInfo) $ T.pack $ "Number of used cores: " <> show c
gs <- liftIO $ initialize s >>= \gen -> splitGen c gen
let chunks = getChunks c nTrees
timeSpec = fmap (, mrca) tHeight
trs <- if subS
then simulateAndSubSampleNTreesConcurrently nLeaves
lambda
mu
rho
timeSpec
chunks
gs
else simulateNTreesConcurrently nLeaves lambda mu rho timeSpec chunks gs
let ls = if sumS
then parMap rpar (formatNChildSumStat . toNChildSumStat) trs
else parMap rpar toNewick trs
let res = L.unlines ls
out "simulated trees" res ".tree"
simulateNTreesConcurrently
:: Int
-> Double
-> Double
-> Double
-> TimeSpec
-> [Int]
-> [GenIO]
-> ELynx SimulateArguments [Tree (PhyloLabel Int)]
simulateNTreesConcurrently nLeaves l m r timeSpec chunks gs = do
let l' = l * r
m' = m - l * (1.0 - r)
trss <- liftIO $ mapConcurrently
(\(n, g) -> simulateNReconstructedTrees n nLeaves timeSpec l' m' g)
(zip chunks gs)
return $ concat trss
simulateAndSubSampleNTreesConcurrently
:: Int
-> Double
-> Double
-> Double
-> TimeSpec
-> [Int]
-> [GenIO]
-> ELynx SimulateArguments [Tree (PhyloLabel Int)]
simulateAndSubSampleNTreesConcurrently nLeaves l m r timeSpec chunks gs = do
let nLeavesBigTree = (round $ fromIntegral nLeaves / r) :: Int
logNewSection
$ T.pack
$ "Simulate one big tree with "
<> show nLeavesBigTree
<> " leaves."
tr <- liftIO $ simulateReconstructedTree nLeavesBigTree timeSpec l m (head gs)
-- Log the base tree.
$(logInfo) $ LT.toStrict $ LT.decodeUtf8 $ toNewick tr
logNewSection
$ T.pack
$ "Sub sample "
<> show (sum chunks)
<> " trees with "
<> show nLeaves
<> " leaves."
let lvs = Seq.fromList $ leaves tr
trss <- liftIO $ mapConcurrently
(\(nSamples, g) -> nSubSamples nSamples lvs nLeaves tr g)
(zip chunks gs)
let trs = catMaybes $ concat trss
return $ map prune trs