Skip to content

Commit 1b1c6c5

Browse files
Horopterkingaa
authored andcommitted
Add BDD(r) sampling to LBDP model
1 parent 7592ec5 commit 1b1c6c5

File tree

9 files changed

+99
-23
lines changed

9 files changed

+99
-23
lines changed

R/lbdp.R

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
##' @param lambda per capita birth rate
1111
##' @param mu per capita death rate
1212
##' @param psi per capita sampling rate
13+
##' @param r probability that a sampled lineage is removed (must be between 0 and 1; r=0 is non-destructive; r=1 is destructive)
1314
##' @param n0 population size at time t0
1415
##' @param time final time
1516
##' @param t0 initial time
@@ -27,13 +28,15 @@ NULL
2728
##' @export
2829
runLBDP <- function (
2930
time, t0 = 0,
30-
lambda = 2, mu = 1, psi = 1,
31+
lambda = 2, mu = 1, psi = 1, r = 0,
3132
n0 = 5
3233
) {
34+
if (!is.numeric(r) || length(r) != 1L || !is.finite(r) || r < 0 || r > 1)
35+
pStop(sQuote("r")," must be between 0 and 1.")
3336
n0 <- round(n0)
3437
if (n0 < 0)
3538
pStop(sQuote("n0")," must be a nonnegative integer.")
36-
params <- c(lambda=lambda,mu=mu,psi=psi)
39+
params <- c(lambda=lambda,mu=mu,psi=psi,r=r)
3740
ivps <- c(n0=n0)
3841
x <- .Call(P_makeLBDP,params,ivps,t0)
3942
.Call(P_runLBDP,x,time) |>
@@ -44,9 +47,13 @@ runLBDP <- function (
4447
##' @inheritParams simulate
4548
##' @export
4649
continueLBDP <- function (
47-
object, time, lambda = NA, mu = NA, psi = NA
50+
object, time, lambda = NA, mu = NA, psi = NA, r = NA
4851
) {
49-
params <- c(lambda=lambda,mu=mu,psi=psi)
52+
if (!isTRUE(is.na(r))) {
53+
if (!is.numeric(r) || length(r) != 1L || !is.finite(r) || r < 0 || r > 1)
54+
pStop(sQuote("r")," must be between 0 and 1.")
55+
}
56+
params <- c(lambda=lambda,mu=mu,psi=psi,r=r)
5057
x <- .Call(P_reviveLBDP,object,params)
5158
.Call(P_runLBDP,x,time) |>
5259
structure(model="LBDP",class=c("gpsim","gpgen"))

R/lbdp_exact.R

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
##' @return \code{lbdp_exact} returns the log likelihood of the genealogy.
66
##' Note that the time since the most recent sample is informative.
77
##' @param x genealogy in \pkg{phylopomp} format (i.e., an object that inherits from \sQuote{gpgen}).
8+
##' @param r probability that a sampled lineage is removed (must be between 0 and 1; must be 0 for \code{lbdp_exact})
89
##' @references
910
##' \Stadler2010
1011
##'
1112
##' \King2024
1213
##' @export
13-
lbdp_exact <- function (x, lambda, mu, psi, n0 = 1) {
14+
lbdp_exact <- function (x, lambda, mu, psi, r = 0, n0 = 1) {
15+
if (!is.numeric(r) || length(r) != 1L || !is.finite(r) || r < 0 || r > 1)
16+
pStop(sQuote("r")," must be between 0 and 1.")
17+
if (!isTRUE(all.equal(r, 0)))
18+
pStop(sQuote("r")," must be 0 for ",sQuote("lbdp_exact"),".")
1419
x |>
1520
lineages(prune=TRUE,obscure=TRUE) |>
1621
encode_data() -> data

R/lbdp_pomp.R

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
##' @importFrom pomp pomp onestep covariate_table
66
##' @inheritParams lbdp_exact
77
##' @export
8-
lbdp_pomp <- function (x, lambda, mu, psi, n0 = 1, t0 = 0)
8+
lbdp_pomp <- function (x, lambda, mu, psi, r = 0, n0 = 1, t0 = 0)
99
{
10+
if (!is.numeric(r) || length(r) != 1L || !is.finite(r) || r < 0 || r > 1)
11+
pStop(sQuote("r")," must be between 0 and 1.")
12+
if (!isTRUE(all.equal(r, 0)))
13+
pStop(sQuote("r")," must be 0 for ",sQuote("lbdp_pomp"),".")
1014
x |> gendat() -> gi
1115
n0 <- round(n0)
1216
if (n0 < 0)

man/lbdp.Rd

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lbdp.cc

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ typedef struct {
1616
double lambda;
1717
double mu;
1818
double psi;
19+
double r;
1920
int n0;
2021
} lbdp_parameters_t;
2122

22-
using lbdp_proc_t = popul_proc_t<lbdp_state_t,lbdp_parameters_t,3>;
23+
using lbdp_proc_t = popul_proc_t<lbdp_state_t,lbdp_parameters_t,4>;
2324
using lbdp_genealogy_t = master_t<lbdp_proc_t,1>;
2425

2526
template<>
@@ -29,6 +30,7 @@ std::string lbdp_proc_t::yaml (std::string tab) const {
2930
+ YAML_PARAM(lambda)
3031
+ YAML_PARAM(mu)
3132
+ YAML_PARAM(psi)
33+
+ YAML_PARAM(r)
3234
+ YAML_PARAM(n0);
3335
std::string s = tab + "state:\n"
3436
+ YAML_STATE(n);
@@ -41,6 +43,9 @@ void lbdp_proc_t::update_params (double *p, int n) {
4143
PARAM_SET(lambda);
4244
PARAM_SET(mu);
4345
PARAM_SET(psi);
46+
PARAM_SET(r);
47+
if (!R_FINITE(params.r) || params.r < 0 || params.r > 1)
48+
err("r must be between 0 and 1.");
4449
if (m != n) err("wrong number of parameters!");
4550
}
4651

@@ -57,7 +62,8 @@ double lbdp_proc_t::event_rates (double *rate, int n) const {
5762
double total = 0;
5863
RATE_CALC(params.lambda * state.n);
5964
RATE_CALC(params.mu * state.n);
60-
RATE_CALC(params.psi * state.n);
65+
RATE_CALC(params.psi * params.r * state.n);
66+
RATE_CALC(params.psi * (1 - params.r) * state.n);
6167
if (m != n) err("wrong number of events!");
6268
return total;
6369
}
@@ -78,6 +84,9 @@ void lbdp_genealogy_t::jump (int event) {
7884
state.n -= 1; death();
7985
break;
8086
case 2:
87+
state.n -= 1; sample_death();
88+
break;
89+
case 3:
8190
sample();
8291
break;
8392
default: // #nocov

tests/lbdp_bddr.R

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
options(digits=3)
2+
suppressPackageStartupMessages({
3+
library(phylopomp)
4+
})
5+
6+
check_sampling <- function(r_value, expected_sat) {
7+
set.seed(10101)
8+
runLBDP(time=1,lambda=0,mu=0,psi=10,r=r_value,n0=5) |>
9+
gendat() -> gi
10+
sample_idx <- gi$nodetype == 1L
11+
if (!any(sample_idx))
12+
stop("no sample nodes observed for r=", r_value)
13+
if (!all(gi$saturation[sample_idx] == expected_sat))
14+
stop("unexpected sample saturation for r=", r_value)
15+
}
16+
17+
check_sampling(0, 1)
18+
check_sampling(1, 0)
19+
20+
check_bad_r <- function(r_value) {
21+
ok <- FALSE
22+
tryCatch(
23+
runLBDP(time=1,lambda=0,mu=0,psi=1,r=r_value,n0=1),
24+
error = function(e) ok <<- TRUE
25+
)
26+
if (!ok)
27+
stop("expected error for r=", r_value)
28+
}
29+
30+
check_bad_r(-0.1)
31+
check_bad_r(1.1)

yaml/R/lbdp.R

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
##' @param lambda per capita birth rate
1010
##' @param mu per capita death rate
1111
##' @param psi per capita sampling rate
12-
##' @param n0 initial population size
12+
##' @param r probability that a sampled lineage is removed (must be between 0 and 1; r=0 is non-destructive; r=1 is destructive)
13+
##' @param n0 population size at time t0
1314
##' @inheritParams sir
1415
##' @return \code{runLBDP} and \code{continueLBDP} return objects of class \sQuote{gpsim} with \sQuote{model} attribute \dQuote{LBDP}.
1516
##'
@@ -19,9 +20,9 @@ NULL
1920
##' @export
2021
runLBDP <- function (
2122
time, t0 = 0,
22-
lambda = 1.3, mu = 1, psi = 1, n0 = 10
23+
lambda = 2, mu = 1, psi = 1, r = 0, n0 = 5
2324
) {
24-
params <- c(lambda=lambda,mu=mu,psi=psi)
25+
params <- c(lambda=lambda,mu=mu,psi=psi,r=r)
2526
ivps <- c(n0=n0)
2627
x <- .Call(P_makeLBDP,params,ivps,t0)
2728
.Call(P_runLBDP,x,time) |>
@@ -32,10 +33,10 @@ runLBDP <- function (
3233
##' @export
3334
continueLBDP <- function (
3435
object, time,
35-
lambda = NA, mu = NA, psi = NA
36+
lambda = NA, mu = NA, psi = NA, r = NA
3637
) {
3738
params <- c(
38-
lambda=lambda,mu=mu,psi=psi
39+
lambda=lambda,mu=mu,psi=psi,r=r
3940
)
4041
x <- .Call(P_reviveLBDP,object,params)
4142
.Call(P_runLBDP,x,time) |>

yaml/lbdp.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ parameter:
88
name: lambda
99
description: per capita birth rate
1010
type: double
11-
default: 1.3
11+
default: 2
1212
mu:
1313
name: mu
1414
description: per capita death rate
@@ -19,11 +19,16 @@ parameter:
1919
description: per capita sampling rate
2020
default: 1.0
2121
type: double
22+
r:
23+
name: r
24+
description: probability that a sampled lineage is removed (must be between 0 and 1; r=0 is non-destructive; r=1 is destructive)
25+
default: 0.0
26+
type: double
2227
ivp:
2328
n0:
2429
name: n0
25-
description: initial population size
26-
default: 10
30+
description: population size at time t0
31+
default: 5
2732
type: int
2833
state:
2934
'n':
@@ -39,6 +44,9 @@ events:
3944
death:
4045
jump: state.n -= 1; death();
4146
rate: params.mu * state.n
47+
sample_death:
48+
rate: params.psi * params.r * state.n
49+
jump: state.n -= 1; sample_death();
4250
sample:
43-
rate: params.psi * state.n
51+
rate: params.psi * (1 - params.r) * state.n
4452
jump: sample();

yaml/src/lbdp.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ typedef struct {
1616
double lambda;
1717
double mu;
1818
double psi;
19+
double r;
1920
int n0;
2021
} lbdp_parameters_t;
2122

22-
using lbdp_proc_t = popul_proc_t<lbdp_state_t,lbdp_parameters_t,3>;
23+
using lbdp_proc_t = popul_proc_t<lbdp_state_t,lbdp_parameters_t,4>;
2324
using lbdp_genealogy_t = master_t<lbdp_proc_t,1>;
2425

2526
template<>
@@ -29,6 +30,7 @@ std::string lbdp_proc_t::yaml (std::string tab) const {
2930
+ YAML_PARAM(lambda)
3031
+ YAML_PARAM(mu)
3132
+ YAML_PARAM(psi)
33+
+ YAML_PARAM(r)
3234
+ YAML_PARAM(n0);
3335
std::string s = tab + "state:\n"
3436
+ YAML_STATE(n);
@@ -41,6 +43,7 @@ void lbdp_proc_t::update_params (double *p, int n) {
4143
PARAM_SET(lambda);
4244
PARAM_SET(mu);
4345
PARAM_SET(psi);
46+
PARAM_SET(r);
4447
if (m != n) err("wrong number of parameters!");
4548
}
4649

@@ -57,7 +60,8 @@ double lbdp_proc_t::event_rates (double *rate, int n) const {
5760
double total = 0;
5861
RATE_CALC(params.lambda * state.n);
5962
RATE_CALC(params.mu * state.n);
60-
RATE_CALC(params.psi * state.n);
63+
RATE_CALC(params.psi * params.r * state.n);
64+
RATE_CALC(params.psi * (1 - params.r) * state.n);
6165
if (m != n) err("wrong number of events!");
6266
return total;
6367
}
@@ -78,6 +82,9 @@ void lbdp_genealogy_t::jump (int event) {
7882
state.n -= 1; death();
7983
break;
8084
case 2:
85+
state.n -= 1; sample_death();
86+
break;
87+
case 3:
8188
sample();
8289
break;
8390
default: // #nocov

0 commit comments

Comments
 (0)