tlynx-0.4.1: src/TLynx/Simulate/Options.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- |
-- Module : TLynx.Simulate.Options
-- Description : Argument parser for seq-ana
-- Copyright : (c) Dominik Schrempf 2020
-- License : GPL-3.0-or-later
--
-- Maintainer : dominik.schrempf@gmail.com
-- Stability : unstable
-- Portability : portable
--
-- Creation date: Fri May 3 11:51:07 2019.
module TLynx.Simulate.Options
( Process (..),
SimulateArguments (..),
simulateArguments,
reportSimulateArguments,
)
where
import Data.List
import Data.Maybe
import ELynx.Tools hiding (Random)
import ELynx.Tree.Simulate.PointProcess (TimeSpec (..))
import Options.Applicative
deriving instance Eq TimeSpec
deriving instance Generic TimeSpec
instance Show TimeSpec where
show Random = "Random"
show (Origin o) = "Condition on height of origin: " ++ show o
show (Mrca m) = "Condition on height of MRCA: " ++ show m
instance FromJSON TimeSpec
instance ToJSON TimeSpec
-- | Process to be used for simulation.
data Process
= BirthDeath
{ -- | Birth rate.
bdLambda :: Double,
-- | Death rate.
bdMu :: Double,
-- | Sampling rate.
bdRho :: Maybe Double,
-- | Condition on height?
bdHeight :: TimeSpec
}
| Coalescent
deriving (Eq, Show, Generic)
instance FromJSON Process
instance ToJSON Process
reportProcess :: Process -> String
reportProcess (BirthDeath l m mr ts) =
intercalate
"\n"
[ "Model: Birth and death process",
" Birth rate: " ++ show l,
" Death rate: " ++ show m,
" Sampling probability: " ++ maybe "1.0" show mr,
" Height specification: " ++ show ts
]
reportProcess Coalescent = "Model: Coalescent process"
-- | Arguments need to simulate phylogenetic trees using birth and death processes.
data SimulateArguments = SimulateArguments
{ -- | Simulated trees.
argsNTrees :: Int,
-- | Number of leaves.
argsNLeaves :: Int,
-- | Process.
argsProcess :: Process,
-- | Perform sub-sampling with given probability.
argsSubSample :: Maybe Double,
-- | Only print summary statistics?
argsSumStat :: Bool,
-- | Seed of NRG, random if 'Nothing'.
argsSeed :: Seed
}
deriving (Eq, Show, Generic)
instance Reproducible SimulateArguments where
inFiles _ = []
outSuffixes _ = [".tree"]
getSeed = Just . argsSeed
setSeed a s = a {argsSeed = Fixed s}
parser = simulateArguments
cmdName = "simulate"
cmdDsc = ["Simulate phylogenetic trees using a birth and death or coalescent process."]
cmdFtr = simulateFooter
instance FromJSON SimulateArguments
instance ToJSON SimulateArguments
-- | Print useful information about the provided arguments.
reportSimulateArguments :: SimulateArguments -> String
reportSimulateArguments a =
intercalate
"\n"
[ "Number of simulated trees: " ++ show (argsNTrees a),
"Number of leaves per tree: " ++ show (argsNLeaves a),
reportProcess (argsProcess a),
"Perform sub-sampling: " ++ ssStr,
"Summary statistics only: " ++ show (argsSumStat a)
]
where
ssStr = case argsSubSample a of
Nothing -> "No"
Just p -> "Yes, with probability " ++ show p
-- | Command line parser.
simulateArguments :: Parser SimulateArguments
simulateArguments =
SimulateArguments
<$> nTreeOpt
<*> nLeavesOpt
<*> process
<*> subSampleOpt
<*> sumStatOpt
<*> seedOpt
nTreeOpt :: Parser Int
nTreeOpt =
option auto $
long "nTrees"
<> short 't'
<> metavar "INT"
<> help "Number of trees"
nLeavesOpt :: Parser Int
nLeavesOpt =
option auto $
long "nLeaves"
<> short 'n'
<> metavar "INT"
<> help "Number of leaves per tree"
lambdaOpt :: Parser Double
lambdaOpt =
option auto $
long "lambda"
<> short 'l'
<> metavar "DOUBLE"
<> help "Birth rate lambda"
muOpt :: Parser Double
muOpt =
option auto $
long "mu"
<> short 'm'
<> metavar "DOUBLE"
<> help "Death rate mu"
rhoOpt :: Parser Double
rhoOpt =
option auto $
long "rho"
<> short 'r'
<> metavar "DOUBLE"
<> help "Sampling probability rho"
mrca :: Parser TimeSpec
mrca =
Mrca
<$> option
auto
( long "mrca"
<> metavar "DOUBLE"
<> help "Condition on height of most recent common ancestor"
)
origin :: Parser TimeSpec
origin =
Origin
<$> option
auto
( long "origin"
<> metavar "DOUBLE"
<> help "Condition on height of origin"
)
timeSpec :: Parser TimeSpec
timeSpec = fromMaybe Random <$> optional (mrca <|> origin)
birthDeath :: Parser Process
birthDeath = BirthDeath <$> lambdaOpt <*> muOpt <*> optional rhoOpt <*> timeSpec
coalescent :: Parser Process
coalescent = pure Coalescent
process :: Parser Process
process =
hsubparser $
( command
"birthdeath"
( info
birthDeath
( progDesc "Birth and death process"
<> footer "Height: If no tree height is given, the heights will be randomly drawn from the expected distribution given the number of leaves, the birth and the death rate assuming a uniform prior."
)
)
<> command
"coalescent"
(info coalescent (progDesc "Coalescent process"))
)
<> metavar "PROCESS"
<> commandGroup "Available processes:"
subSampleOpt :: Parser (Maybe Double)
subSampleOpt =
optional $
option auto $
long
"sub-sample"
<> short 'u'
<> metavar "DOUBLE"
<> showDefault
<> help "Perform sub-sampling; see below."
sumStatOpt :: Parser Bool
sumStatOpt =
switch $
long "summary-statistics" <> short 's' <> showDefault
<> help
"For each branch, print length and number of children"
-- citation :: String
-- citation =
-- "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"
-- | And a footer.
simulateFooter :: [String]
simulateFooter =
[ "See, for example, 'tlynx simulate birthdeath --help'.",
"Sub-sample with probability p:\n 1. Simulate one big tree with n'=round(n/p), n'>=n, leaves;\n 2. Randomly sample sub-trees with n leaves.\n (With p=1.0, the same tree is reported over and over again.)"
]