Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make likelihood() work with <epichains_summary>, <epichains>, and <numeric> objects #213

Merged
merged 78 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
d53c4c3
Update doc of chains argument to accept epichains_tree and epichains_…
jamesmbaazam Mar 5, 2024
065c48c
Add examples for epichains_summary and epichains_tree objects
jamesmbaazam Mar 5, 2024
d68cfc6
Check that object is epichains_tree or epichains_summary
jamesmbaazam Mar 5, 2024
0afb10f
Find summary of object is epichains_tree
jamesmbaazam Mar 5, 2024
c93ca8c
Add tests
jamesmbaazam Mar 7, 2024
708d84e
Delete README.md to trigger render-readme CI
jamesmbaazam Mar 8, 2024
2100ea6
Rename epichains_tree to epichains
jamesmbaazam Mar 12, 2024
16025fd
Remove nsim_obs as unused b'cos analytical solution is used
jamesmbaazam Mar 20, 2024
4cd3d9e
Automatic readme update
actions-user Mar 20, 2024
a3ca4f8
Replace with likelihood Inf stat_max with finite one from the simulation
jamesmbaazam Mar 21, 2024
a6b6094
Fix comment
jamesmbaazam Mar 21, 2024
f0a0f90
Move input checking on nsim_obs where it is first needed
jamesmbaazam Mar 21, 2024
941d62e
Use new .is_epichains
jamesmbaazam Mar 22, 2024
403578f
Fix documentation
jamesmbaazam Mar 22, 2024
0aa941a
Check that chains is an epichains_summary and stat_max is not specified
jamesmbaazam May 9, 2024
8b965a1
Change default value of stat_max to NULL
jamesmbaazam May 14, 2024
7f8629e
Turn on cyclocomp linter
jamesmbaazam May 14, 2024
d69dbea
Fix comments
jamesmbaazam May 14, 2024
c825e13
Deal with the default, specified, and unspecified stat_max
jamesmbaazam May 14, 2024
9ac6389
Replace Inf values with stat_max
jamesmbaazam May 14, 2024
9194919
Throw error if numeric chains contain Infs
jamesmbaazam May 14, 2024
f6269bc
Add tests
jamesmbaazam May 14, 2024
ec1d0dc
Fix simulation function name in examples and tests
jamesmbaazam May 14, 2024
d4cb06b
Automatic readme update
actions-user May 14, 2024
61ccf12
Automatic readme update
actions-user May 15, 2024
83e43e9
Replace index_cases with n_chains
jamesmbaazam May 15, 2024
4552e27
Replace stat_max with stat_threshold
jamesmbaazam May 16, 2024
f01ad1e
Automatic readme update
actions-user May 16, 2024
2f6a337
Automatic readme update
actions-user May 17, 2024
399dc36
Check that nsim_obs is integerish instead of "number"
jamesmbaazam May 23, 2024
dfb8209
Automatic readme update
actions-user May 23, 2024
4d6b5e0
chains vector must be at most stat_threshold
jamesmbaazam May 23, 2024
891d8e5
Remove unused arguments to assert function
jamesmbaazam May 23, 2024
b79dfae
Automatic readme update
actions-user May 23, 2024
675f9da
Check that stat_threshold is an integer or infinite
jamesmbaazam May 23, 2024
23de55c
Remove default value of stat_threshold and move up argument list
jamesmbaazam May 23, 2024
a756b3b
Set stat_threshold to Inf
jamesmbaazam May 24, 2024
33509d9
Remove redundant check
jamesmbaazam May 24, 2024
a8a6c19
Remove redundant assignment and use default value
jamesmbaazam May 24, 2024
9836cb3
Improve comments
jamesmbaazam May 24, 2024
24b7fbb
Add comment explaining logic behind stat_threshold
jamesmbaazam May 24, 2024
2d4fe7f
Add check on stat_threshold to ensure not NULL
jamesmbaazam May 24, 2024
3005845
Remove check for NULL from assert
jamesmbaazam May 24, 2024
713ce24
Remove redundant argument
jamesmbaazam May 24, 2024
36cd129
Remove nested if statement
jamesmbaazam May 24, 2024
d8be604
Improve documentation of "chains"
jamesmbaazam May 24, 2024
5510303
Add James as author
jamesmbaazam May 24, 2024
d6c1b5a
Use better names in examples
jamesmbaazam May 24, 2024
2d7015e
Revert "Add James as author"
jamesmbaazam May 24, 2024
b91c5a0
Add James as author
jamesmbaazam May 24, 2024
2987267
Indent comment
jamesmbaazam May 28, 2024
eb68347
Simplify checks on stat_threshold
jamesmbaazam May 28, 2024
73cd4a1
Revert order of arguments
jamesmbaazam May 28, 2024
f0ab8a5
Regenerate docs
jamesmbaazam May 28, 2024
24f3a98
Fix test comment
jamesmbaazam May 28, 2024
dc75ccc
Improve comments
jamesmbaazam May 28, 2024
3cf6196
Add helper function to get stat_threshold attribute
jamesmbaazam May 28, 2024
86c9cd5
Remove outdated comments
jamesmbaazam May 28, 2024
b8cc232
Add controls to fix stat_threshold and apply censoring
jamesmbaazam May 28, 2024
c47171f
Move error earlier
jamesmbaazam May 28, 2024
d560000
Add stat_threshold to function calls to avoid warnings
jamesmbaazam May 28, 2024
02cd9fd
Update expected error message
jamesmbaazam May 28, 2024
af00b19
Style brackets
jamesmbaazam May 28, 2024
9a3c14e
Add tests for expected warnings
jamesmbaazam May 28, 2024
7f05ee2
Update expected error message
jamesmbaazam May 28, 2024
f3ae521
Remove trailing whitespace
jamesmbaazam May 28, 2024
35fb7a3
Fix style
jamesmbaazam May 28, 2024
aecbccc
Update expected warning message
jamesmbaazam May 28, 2024
b1d78ab
Improve comments in tests
jamesmbaazam May 28, 2024
b5bf496
Remove unused argument
jamesmbaazam May 28, 2024
26d526f
Removed unneeded tests (due to change in logic)
jamesmbaazam May 28, 2024
e7889a1
Remove warning before censoring epichains_summary objects internally
jamesmbaazam May 28, 2024
cd01dab
Change to only warn if stat_threshold is specified in case of epichai…
jamesmbaazam May 28, 2024
515527d
Document stat_threshold behaviour when object is epichains or epichai…
jamesmbaazam May 28, 2024
7e83bd9
Remove stat_threshold specification to remove warning
jamesmbaazam May 29, 2024
02f782f
Remove unnecessary function and replace with inline code
jamesmbaazam May 29, 2024
e239252
Improve comments
jamesmbaazam May 29, 2024
8bb0e64
Fix brackets
jamesmbaazam May 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions R/checks.R
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@
statistic,
choices = c("size", "length")
)
# check that stat_threshold is an integer or Inf.
checkmate::assert(
is.infinite(stat_threshold),
checkmate::anyInfinite(stat_threshold),
checkmate::check_integerish(
stat_threshold,
lower = 1,
null.ok = FALSE
lower = 1
),
combine = "or"
)
Expand Down
8 changes: 8 additions & 0 deletions R/helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,11 @@
next_gen[as.integer(names(next_gen_count))] <- unname(next_gen_count)
return(next_gen)
}

#' Extract the `stat_threshold` attribute from an `<epichains>` object.
#'
#' @param obj An <epichains> or <epichains_summary> object.
#' @keywords internal
.get_stat_threshold <- function(obj) {
attr(obj, "stat_threshold")
}
jamesmbaazam marked this conversation as resolved.
Show resolved Hide resolved
134 changes: 120 additions & 14 deletions R/likelihood.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
#'
#' @inheritParams .offspring_ll
#' @inheritParams simulate_chain_stats
#' @param chains A numeric vector of chain summaries (sizes/lengths).
#' @param chains Vector of chain summaries (sizes/lengths). Can be a
#' `<numeric>` vector or an object of class `<epichains>` or
#' `<epichains_summary>` (obtained from [simulate_chains()] or
#' [simulate_chain_stats()]). See examples below.
#' @param nsim_obs Number of simulations to be used to approximate the
#' log-likelihood/likelihood if `obs_prob < 1` (imperfect observation). If
#' `obs_prob == 1`, this argument is ignored.
Expand All @@ -11,6 +14,14 @@
#' simulations will be used to approximate the (log)likelihood. This will
#' also require specifying `nsim_obs`. In the simulation, the observation
#' process is assumed to be constant.
#' @param stat_threshold A stopping criterion for individual chain simulations;
#' a positive number coercible to integer. When any chain's cumulative statistic
#' reaches or surpasses `stat_threshold`, that chain ends. It also serves as a
#' censoring limit so that results above the specified value, are set to `Inf`.
#' Defaults to `Inf`. NOTE: For objects of class `<epichains>` or
#' `<epichains_summary>`, the `stat_threshold` used in the simulation is
#' extracted and used here so if this argument is specified, it is ignored and
#' a warning is thrown.
#' @param log Should the log-likelihoods be transformed to
#' likelihoods? Logical. Defaults to `TRUE`.
#' @param exclude A vector of indices of the sizes/lengths to exclude from the
Expand All @@ -34,31 +45,82 @@
#' except that likelihoods, instead of log-likelihoods, are calculated in all
#' cases. Moreover, the joint likelihoods are the product, instead of the sum,
#' of the individual likelihoods.
#' @author Sebastian Funk
#' @author Sebastian Funk, James M. Azam
#' @examples
#' # example of observed chain sizes
#' set.seed(121)
#' # randomly generate 20 chains of size 1 to 10
#' chain_sizes <- sample(1:10, 20, replace = TRUE)
#' likelihood(
#' chains = chain_sizes, statistic = "size",
#' offspring_dist = rpois, nsim_obs = 100, lambda = 0.5
#' chains = chain_sizes,
#' statistic = "size",
#' offspring_dist = rpois,
#' nsim_obs = 100,
#' lambda = 0.5
#' )
#' # Example using an <epichains> object
#' set.seed(32)
#' epichains_obj_eg <- simulate_chains(
#' n_chains = 10,
#' pop = 100,
#' percent_immune = 0,
#' statistic = "size",
#' offspring_dist = rnbinom,
#' stat_threshold = 10,
#' generation_time = function(n) rep(3, n),
#' mu = 2,
#' size = 0.2
#')
#'
#' epichains_obj_eg_lik <- likelihood(
#' chains = epichains_obj_eg,
#' statistic = "size",
#' offspring_dist = rnbinom,
#' mu = 2,
#' size = 0.2,
#' stat_threshold = 10
#' )
#' epichains_obj_eg_lik
#'
#' # Example using a <epichains_summary> object
#' set.seed(32)
#' epichains_summary_eg <- simulate_chain_stats(
#' n_chains = 10,
#' pop = 100,
#' percent_immune = 0,
#' statistic = "size",
#' offspring_dist = rnbinom,
#' stat_threshold = 10,
#' mu = 2,
#' size = 0.2
#' )
#' epichains_summary_eg_lik <- likelihood(
#' chains = epichains_summary_eg,
#' statistic = "size",
#' offspring_dist = rnbinom,
#' mu = 2,
#' size = 0.2,
#' stat_threshold = 10
#' )
#' epichains_summary_eg_lik
#' @export
#nolint start: cyclocomp_linter
likelihood <- function(chains, statistic = c("size", "length"), offspring_dist,
nsim_obs, obs_prob = 1, log = TRUE, stat_threshold = Inf,
nsim_obs, obs_prob = 1, stat_threshold = Inf, log = TRUE,
exclude = NULL, individual = FALSE, ...) {
statistic <- match.arg(statistic)

## Input checking
## Check nsim_obs when specified
if (!missing(nsim_obs)) {
checkmate::assert_number(
nsim_obs, lower = 1, finite = TRUE, na.ok = FALSE
checkmate::assert(
checkmate::check_numeric(
chains, lower = 0, upper = Inf, any.missing = FALSE
),
checkmate::check_class(
chains, "epichains"
),
checkmate::check_class(
chains, "epichains_summary"
)
}
checkmate::assert_numeric(
chains, lower = 0, upper = Inf, any.missing = FALSE
)
# check that arguments related to the statistic are valid
.check_statistic_args(
Expand All @@ -78,9 +140,52 @@ likelihood <- function(chains, statistic = c("size", "length"), offspring_dist,
checkmate::assert_numeric(
exclude, null.ok = TRUE
)
# likelihood cannot work with an <epichains> object so convert to
# <epichains_summary>
if (.is_epichains(chains)) {
chains <- summary(chains)
}

# Logic:
# * If the object is an <epichains>/<epichains_summary>, we'll give
# preference to the stat_threshold used in the simulation.
if (.is_epichains_summary(chains)) {
if (!missing(stat_threshold)) {
warning(
"`stat_threshold` specified but will be ignored. ",
"Using `stat_threshold` = ",
.get_stat_threshold(chains),
" as used in the simulation.",
call. = FALSE
)
}
stat_threshold <- .get_stat_threshold(chains)
}

if (is.finite(stat_threshold)) {
# censor the chains to be at most stat_threshold
chains <- pmin(chains, stat_threshold)
} else if (any(is.infinite(chains)) && !.is_epichains_summary(chains)) {
# Developer note: <epichains_summary> objects also pass the`is.numeric()`
# check, so we need to check if the object is not an <epichains_summary>
# object.
# Numeric chains can only contain Inf values based on human error/decision
# to censor it with infinite values, so we ask the user to fix that.
stop(
"`chains` must be censored with a finite value. ",
"Replace the `Inf` values with a finite value.",
call. = FALSE
)
}

# Apply the observation process
if (obs_prob < 1) {
if (missing(nsim_obs)) {
stop("'nsim_obs' must be specified if 'obs_prob' is < 1")
} else {
checkmate::assert_integerish(
nsim_obs, lower = 1
)
}

statistic_func <- .get_statistic_func(statistic)
Expand All @@ -102,7 +207,7 @@ likelihood <- function(chains, statistic = c("size", "length"), offspring_dist,
stat_rep_list <- list(chains)
}

## determine for which sizes to calculate the log-likelihood
## Determine for which sizes to calculate the log-likelihood
## (for true chain size)
if (any(stat_rep_vect == stat_threshold)) {
calc_sizes <- seq_len(stat_threshold - 1)
Expand Down Expand Up @@ -160,7 +265,7 @@ likelihood <- function(chains, statistic = c("size", "length"), offspring_dist,
## assign likelihoods
chains_likelihood <- lapply(stat_rep_list, function(sx) {
likelihoods[sx[!(sx %in% exclude)]]
}
}
)
jamesmbaazam marked this conversation as resolved.
Show resolved Hide resolved

## if individual == FALSE, return the joint log-likelihood
Expand All @@ -180,3 +285,4 @@ likelihood <- function(chains, statistic = c("size", "length"), offspring_dist,

return(chains_likelihood)
}
#nolint end
15 changes: 15 additions & 0 deletions man/dot-get_stat_threshold.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 63 additions & 9 deletions man/likelihood.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading