dist_cdf() and added dist_quantile(), so packages built on distspec can look up a distribution's CDF and quantile functions directly instead of reimplementing per-family dispatch.quantile.dist_spec() and cdf(), truncation-aware quantile and cumulative distribution functions for a <dist_spec> with fixed parameters; both respect a max/cdf_max bound set with bound_dist() and give every quantile/CDF value of a Fixed() distribution its point-mass value. Each raises an informative error for a distribution with uncertain parameters, or one with no analytic CDF (e.g. NonParametric()). A composite (multi-component) distribution returns one set of values per component, in keeping with mean()/sd().sample_dist() now respects a distribution's max and cdf_max bounds instead of sampling from the unbounded distribution; samples are drawn exactly via inverse-CDF sampling on the truncated range, computed on the log scale so that a bound deep in the lower tail (where the CDF underflows to zero in double precision) still samples correctly. This also fixes bounded sampling of a component within a composite (multi-dist_spec) distribution and lets cdf_max apply to Beta(). Sampling a composite that has a max/cdf_max of its own now raises an informative error, since such a bound constrains the sum of the components, which has no closed-form distribution to sample from.sample_dist() of a composite distribution now returns an n by k matrix for n = 1 as documented; previously it returned a length-k vector, so the documented rowSums() idiom errored.as_dist_spec(), a generic for converting other representations of a probability distribution to a <dist_spec>: a <dist_spec> is returned unchanged, a numeric PMF vector becomes a NonParametric() and a single number a Fixed(). Other packages can register methods for their own classes without a dependency in either direction.cdf_cutoff argument of bound_dist() and new_dist_spec() (and of
the distribution constructors, which pass it on) has been renamed to
cdf_max: the CDF level up to which the distribution is kept, mirroring
max on the probability axis (max = 10 truncates at the value 10,
cdf_max = 0.999 at the 99.9% quantile). The semantics are unchanged.
Supplying cdf_cutoff warns and interprets the value as cdf_max; a value
in the historical EpiNow2 convention (the tail probability to drop) is
caught by the existing guard against values below 0.5, which points to the
1 - x conversion. The internal attribute carrying the bound is renamed
accordingly.Fixed() now forwards its ... to bound_dist() as documented; previously limits such as Fixed(3, max = 5) were silently dropped.Fixed() value (e.g. NA or Inf) is now rejected with an informative error; previously it raised a bare condition error from the internal bound check.Fixed(6, max = 5)) is now rejected, and discretising a partially truncated fixed distribution renormalises the PMF to sum to one; previously both silently produced an invalid PMF.NonParametric() help page now documents that a distribution constructed with a Dirichlet() prior has no concrete PMF until fix_parameters() resolves it, and how get_pmf() and has_uncertainty() behave in the meantime. has_uncertainty() now appears in the reference index to match.distspec 0.1.0 splits the <dist_spec> interface out of EpiNow2. The entries
below are changes relative to that code as it stood in EpiNow2 1.9.0.
max/cdf_cutoff attributes) and raise an
informative error if it is malformed.Beta() distribution (shape1/shape2, or mean/sd).Exponential() and Weibull() distributions.Dirichlet() and support for uncertain nonparametric distributions
specified via a Dirichlet prior (NonParametric(pmf = Dirichlet(...))).sample_dist() to draw random samples from a distribution with fixed
parameters. A composite distribution is sampled per component, returning an
n by k matrix (rowSums() gives samples of the combined distribution).
Distributions with uncertain (prior) parameters cannot be sampled and raise
an error.has_uncertainty(), a predicate for whether a <dist_spec> (or a
component of a composite) carries a prior, so dependent packages and internal
code can test for uncertainty in one place.Gamma(mean = Normal(4, 0.5), sd = 1)) is now propagated to the natural
parameters with a first-order delta-method approximation. This replaces an
ad-hoc rule that understated the natural-parameter standard deviations several
times over.discretise() gains a remove_trailing_zeros argument (default TRUE).sd(), ndist(), natural_params() and
lower_bounds() so that dependent packages can reuse them.natural_params() and lower_bounds() accept a distribution type given by
name (e.g. natural_params("gamma")), as well as a <dist_spec>, so dependent
packages can query type metadata without constructing an instance.dist.spec to distspec.<dist_spec>
(e.g. c("gamma", "dist_spec")), so per-type behaviour dispatches directly and
each distribution's methods live in one place. The internal distribution
dispatch class and new_dist() have been removed. The internal helpers
natural_params() and lower_bounds() now take a <dist_spec> rather than a
distribution-name string.get_parameters() is now an S3 generic.cdf_cutoff argument (on the distribution constructors and bound_dist())
is the cumulative probability to keep up to: cdf_cutoff = 0.999 truncates at
the 99.9th percentile, and the default 1 keeps the full distribution. A value
below 0.5 is rejected, as it is almost certainly the tail probability to drop
(use 1 - x).Exp() is deprecated in favour of Exponential().NonParametric() and Dirichlet(prior = ) now reject a numeric PMF or weight
vector that contains negative or non-finite values, or is all zero, with an
informative error, instead of silently producing an invalid distribution.
Un-normalised non-negative weights are still accepted and normalised, but now
warn when they do not sum to one.Normal(x, 0), which collapses to Fixed(x)) is now resolved to its
point value at construction, so it behaves exactly like passing the number.
Previously such a parameter left the distribution marked uncertain, so
mean() and sd() returned NA for an otherwise fully-fixed distribution
(e.g. Gamma(shape = Normal(3, 0), rate = 2)).sd() of a nonparametric distribution now returns the standard deviation
rather than the variance (a missing square root). This also affects sd() of
any discretised distribution, since discretise() produces a nonparametric
distribution.collapse() now correctly convolves runs of three or more consecutive
nonparametric distributions, and runs that do not begin at the first
component, rather than erroring or convolving the wrong component.collapse() now uses a numerically stable implementation.bound_dist() now truncates a fixed nonparametric PMF at max when the PMF
is longer than max + 1, renormalising the result, and leaves it untouched
when max reaches beyond the support. Previously the condition was inverted,
so the bound never applied when requested and produced an all-NA PMF when
max exceeded the support.== (or !=) no longer errors when a
parameter is a numeric vector of length greater than one; such parameters are
now compared as whole vectors.fix_parameters() and discretise() now forward strategy and
remove_trailing_zeros to the components of a composite distribution, so these
arguments are no longer silently ignored for composites.Fixed() distributions may now take a value of 0; the lower bound for the
value parameter has been corrected accordingly, and a value below that
bound is now rejected with an informative error instead of silently
producing an invalid probability mass function.dist_spec for a
parameter. It has no PMF until resolved with fix_parameters(): get_pmf()
errors on such a distribution, mean() returns NA (or the prior mean with
ignore_uncertainty = TRUE), and it prints with its prior nested like any
other uncertain distribution.max or cdf_cutoff to an uncertain (Dirichlet-backed)
nonparametric distribution now raises an informative error, since its support
is fixed by the Dirichlet prior and the bound would otherwise be silently
ignored.plot() gives an actionable error when asked to plot a distribution with no
finite range (no finite max and no cdf_cutoff), pointing to bound_dist(),
rather than a cryptic message or a silently chosen default range.mean() and sd() now emit an informative message when they return NA
because a distribution has uncertain parameters, pointing to
mean(x, ignore_uncertainty = TRUE) and fix_parameters().get_element() and get_parameters(): an
out-of-range id now reports the offending value and valid range, and the
nonparametric error no longer implies that Weibull, Beta and Exponential
distributions lack parameters.Gamma(), LogNormal(),
...) rather than a single combined page, so each shows only its own
parameters. The reference index covers the full exported API, and the
discretise() help page documents how discretisation works, including the
fixed point-mass special case.get_pmf(collapse(discretise(d1 + d2))) pipeline for combining two
delays into a single PMF, stale EpiNow2 and Stan references have been removed
from the roxygen, and the bound_dist(), discretise(), fix_parameters()
and sd() help pages have clearer descriptions and runnable examples.primarycensored package to compute double
censored probability mass functions.natural_params() and lower_bounds() are now S3 generics, with each
distribution's behaviour defined alongside its type (in its own R/ file)
rather than in scattered switch()/if statements. Gamma(), Normal(),
LogNormal(), Exp(), Weibull(), Beta(), Fixed(), the Dirichlet()
prior and the nonparametric distribution now define their per-type behaviour
(parameter metadata, and mean()/sd()/max() where applicable) this way.
The internal per-distribution switch() statements have been collapsed to
direct S3 dispatch; attempting to discretise a distribution that has no CDF
now reports this directly.has_uncertainty() predicate (removing a near-duplicate helper),
threaded pre-computed parameter means through to_natural() to avoid
redundant lapply(x$parameters, mean) calls in every method, vectorised the
attribute-copy loop in discretise(), used %||% for null-default attribute
guards, and extracted repeated get_parameters() calls and sum(convolutions)
into local variables.data.table, checkmate and purrr, and
moved ggplot2 to Suggests. plot() now prompts to install ggplot2 if it
is missing, so it is no longer a hard dependency of the package.