epi-sim-0.7.0: src/Epidemic/Model/BDSCOD.hs
{-# LANGUAGE MultiParamTypeClasses #-}
module Epidemic.Model.BDSCOD
( configuration
, randomEvent
, BDSCODParameters(..)
, BDSCODPopulation(..)
) where
import qualified Data.Vector as V
import qualified Data.Vector.Generic as G
import Epidemic
import Epidemic.Types.Events (EpidemicEvent(..))
import Epidemic.Types.Parameter
import Epidemic.Types.Population
import Epidemic.Types.Simulation
( SimulationConfiguration(..)
, SimulationRandEvent(..), TerminationHandler(..)
)
import Epidemic.Types.Time
( AbsoluteTime(..)
, TimeDelta(..)
, Timed(..)
, asTimed
, maybeNextTimed
, timeAfterDelta
)
import Epidemic.Utility
import System.Random.MWC
import System.Random.MWC.Distributions (bernoulli, categorical, exponential)
-- | birth rate, death rate, sampling rate, catastrophe specification, occurrence rate and disaster specification
data BDSCODParameters =
BDSCODParameters Rate Rate Rate (Timed Probability) Rate (Timed Probability)
data BDSCODPopulation =
BDSCODPopulation People
deriving (Show)
instance ModelParameters BDSCODParameters BDSCODPopulation where
rNaught _ (BDSCODParameters br dr sRate _ occRate _) _ =
Just $ br / (dr + sRate + occRate)
eventRate _ (BDSCODParameters br dr sRate _ occRate _) _ =
Just $ br + dr + sRate + occRate
birthProb _ (BDSCODParameters br dr sRate _ occRate _) _ =
Just $ br / (br + dr + sRate + occRate)
eventWeights _ (BDSCODParameters br dr sRate _ occRate _) _ =
Just $ V.fromList [br, dr, sRate, occRate]
instance Population BDSCODPopulation where
susceptiblePeople _ = Nothing
infectiousPeople (BDSCODPopulation people) = Just people
removedPeople _ = Nothing
isInfected (BDSCODPopulation (People people)) = not $ V.null people
-- | Configuration of a birth-death-sampling-occurrence-disaster simulation
configuration ::
TimeDelta -- ^ Duration of the simulation
-> Bool -- ^ condition upon at least two sequenced samples.
-> Maybe (BDSCODPopulation -> Bool, [EpidemicEvent] -> s) -- ^ values for termination handling.
-> ( Rate
, Rate
, Rate
, [(AbsoluteTime, Probability)]
, Rate
, [(AbsoluteTime, Probability)]) -- ^ Birth, Death, Sampling, Catastrophe probability, Occurrence rates and Disaster probabilities
-> Maybe (SimulationConfiguration BDSCODParameters BDSCODPopulation s)
configuration maxTime atLeastCherry maybeTHFuncs (birthRate, deathRate, samplingRate, catastropheSpec, occurrenceRate, disasterSpec) = do
catastropheSpec' <- asTimed catastropheSpec
disasterSpec' <- asTimed disasterSpec
let bdscodParams =
BDSCODParameters
birthRate
deathRate
samplingRate
catastropheSpec'
occurrenceRate
disasterSpec'
(seedPerson, newId) = newPerson initialIdentifier
bdscodPop = BDSCODPopulation (People $ V.singleton seedPerson)
termHandler = do (f1, f2) <- maybeTHFuncs
return $ TerminationHandler f1 f2
in return $
SimulationConfiguration
bdscodParams
bdscodPop
newId
(AbsoluteTime 0)
maxTime
termHandler
atLeastCherry
-- | The way in which random events are generated in this model.
randomEvent :: SimulationRandEvent BDSCODParameters BDSCODPopulation
randomEvent = SimulationRandEvent randomEvent'
-- | Return a random event from the BDSCOD-process given the current state of the process.
randomEvent' ::
BDSCODParameters -- ^ Parameters of the process
-> AbsoluteTime -- ^ The current time within the process
-> BDSCODPopulation -- ^ The current state of the populaion
-> Identifier -- ^ The current state of the identifier generator
-> GenIO -- ^ The current state of the PRNG
-> IO (AbsoluteTime, EpidemicEvent, BDSCODPopulation, Identifier)
randomEvent' params@(BDSCODParameters _ _ _ catastInfo _ disastInfo) currTime currPop@(BDSCODPopulation currPeople) currId gen =
let (Just netEventRate) = eventRate currPop params currTime
(Just weightVec) = eventWeights currPop params currTime
in do delay <-
exponential (fromIntegral (numPeople currPeople) * netEventRate) gen
let newEventTime = timeAfterDelta currTime (TimeDelta delay)
if noScheduledEvent currTime newEventTime (catastInfo <> disastInfo)
then do
eventIx <- categorical weightVec gen
(selectedPerson, unselectedPeople) <- randomPerson currPeople gen
return $
case eventIx of
0 ->
let (birthedPerson, newId) = newPerson currId
infEvent =
Infection newEventTime selectedPerson birthedPerson
in ( newEventTime
, infEvent
, BDSCODPopulation (addPerson birthedPerson currPeople)
, newId)
1 ->
( newEventTime
, Removal newEventTime selectedPerson
, BDSCODPopulation unselectedPeople
, currId)
2 ->
( newEventTime
, IndividualSample newEventTime selectedPerson True
, BDSCODPopulation unselectedPeople
, currId)
3 ->
( newEventTime
, IndividualSample newEventTime selectedPerson False
, BDSCODPopulation unselectedPeople
, currId)
_ ->
error "no birth, death, sampling, occurrence event selected."
else case maybeNextTimed catastInfo disastInfo currTime of
Just (disastTime, Right disastProb) ->
do (disastEvent, postDisastPop) <-
randomDisasterEvent
(disastTime, disastProb)
currPop
gen
return (disastTime, disastEvent, postDisastPop, currId)
Just (catastTime, Left catastProb) ->
do (catastEvent, postCatastPop) <-
randomCatastropheEvent
(catastTime, catastProb)
currPop
gen
return (catastTime, catastEvent, postCatastPop, currId)
Nothing -> error "Missing a next scheduled event when there should be one."
-- | Return a randomly sampled Catastrophe event
randomCatastropheEvent ::
(AbsoluteTime, Probability) -- ^ Time and probability of sampling in the catastrophe
-> BDSCODPopulation -- ^ The state of the population prior to the catastrophe
-> GenIO
-> IO (EpidemicEvent, BDSCODPopulation)
randomCatastropheEvent (catastTime, rhoProb) (BDSCODPopulation (People currPeople)) gen = do
rhoBernoullis <- G.replicateM (V.length currPeople) (bernoulli rhoProb gen)
let filterZip predicate a b = fst . V.unzip . V.filter predicate $ V.zip a b
sampledPeople = filterZip snd currPeople rhoBernoullis
unsampledPeople = filterZip (not . snd) currPeople rhoBernoullis
in return
( PopulationSample catastTime (People sampledPeople) True
, BDSCODPopulation (People unsampledPeople))
-- | Return a randomly sampled Disaster event
-- TODO Move this into the epidemic module to keep things DRY.
randomDisasterEvent ::
(AbsoluteTime, Probability) -- ^ Time and probability of sampling in the disaster
-> BDSCODPopulation -- ^ The state of the population prior to the disaster
-> GenIO
-> IO (EpidemicEvent, BDSCODPopulation)
randomDisasterEvent (disastTime, nuProb) (BDSCODPopulation (People currPeople)) gen = do
nuBernoullis <- G.replicateM (V.length currPeople) (bernoulli nuProb gen)
let filterZip predicate a b = fst . V.unzip . V.filter predicate $ V.zip a b
sampledPeople = filterZip snd currPeople nuBernoullis
unsampledPeople = filterZip (not . snd) currPeople nuBernoullis
in return
( PopulationSample disastTime (People sampledPeople) False
, BDSCODPopulation (People unsampledPeople))