Public Documentation
Documentation for ConvolvedDistributions's public interface.
ConvolvedDistributions.ConvolvedDistributions Module
ConvolvedDistributionsRaw-distribution convolution and the shared numeric quadrature machinery for the EpiAware distribution-operations stack. Provides Convolved (the sum of independent components), Difference (the X - Y dual), Product (the X * Y Mellin convolution for non-negative components), Ratio (the X / Y quotient), the pluggable Gauss-Legendre integrate/gl_integrate layer, the solver-method types AnalyticalSolver/NumericSolver selecting the analytic-vs-numeric backend, and, for discrete distributions, the probability generating function primitive pgf. Operates on any Distributions.UnivariateDistribution; no censoring. A combination whose components are all integer-lattice discrete distributions is itself discrete and evaluates exactly (an integer-lattice fold replaces quadrature; see is_exact).
Examples
using ConvolvedDistributions, Distributions
# Sum of two independent delays
d = convolved(Gamma(2.0, 1.0), LogNormal(1.5, 0.5))
cdf(d, 5.0)
# Signed gap between two events
z = difference(Normal(5.0, 1.0), Normal(2.0, 1.0))
mean(z)
# A delay scaled by an independent multiplicative factor
w = product(Gamma(3.0, 1.0), LogNormal(0.0, 0.3))
mean(w)
# A rate: an independent count over an independent exposure time
r = ratio(Gamma(3.0, 1.0), Gamma(2.0, 1.0))
mean(r)Contents
Index
ConvolvedDistributions.ConvolvedDistributionsConvolvedDistributions.AbstractConvolvedDistributionConvolvedDistributions.AbstractSolverMethodConvolvedDistributions.AnalyticalSolverConvolvedDistributions.ConvolvedConvolvedDistributions.DifferenceConvolvedDistributions.GaussLegendreConvolvedDistributions.NumericSolverConvolvedDistributions.ProductConvolvedDistributions.RatioConvolvedDistributions.convolve_pairConvolvedDistributions.convolve_powerConvolvedDistributions.convolve_seriesConvolvedDistributions.convolvedConvolvedDistributions.convolved_ccdfConvolvedDistributions.convolved_cdfConvolvedDistributions.convolved_logccdfConvolvedDistributions.convolved_logcdfConvolvedDistributions.convolved_logpdfConvolvedDistributions.convolved_minimumConvolvedDistributions.convolved_pdfConvolvedDistributions.convolved_quantileConvolvedDistributions.delay_massesConvolvedDistributions.differenceConvolvedDistributions.difference_ccdfConvolvedDistributions.difference_cdfConvolvedDistributions.difference_logccdfConvolvedDistributions.difference_logcdfConvolvedDistributions.difference_logpdfConvolvedDistributions.difference_pairConvolvedDistributions.difference_pdfConvolvedDistributions.difference_quantileConvolvedDistributions.evaluation_pathConvolvedDistributions.gl_integrateConvolvedDistributions.has_closed_formConvolvedDistributions.integrateConvolvedDistributions.is_exactConvolvedDistributions.partial_expectationConvolvedDistributions.pgfConvolvedDistributions.productConvolvedDistributions.product_ccdfConvolvedDistributions.product_cdfConvolvedDistributions.product_logccdfConvolvedDistributions.product_logcdfConvolvedDistributions.product_logpdfConvolvedDistributions.product_pairConvolvedDistributions.product_pdfConvolvedDistributions.product_powerConvolvedDistributions.product_quantileConvolvedDistributions.quantile_by_optimizationConvolvedDistributions.quantile_initial_guessConvolvedDistributions.ratioConvolvedDistributions.ratio_ccdfConvolvedDistributions.ratio_cdfConvolvedDistributions.ratio_logccdfConvolvedDistributions.ratio_logcdfConvolvedDistributions.ratio_logpdfConvolvedDistributions.ratio_pairConvolvedDistributions.ratio_pdfConvolvedDistributions.ratio_quantileConvolvedDistributions.uniform_window_ccdfConvolvedDistributions.uniform_window_cdfConvolvedDistributions.upper_partial_expectation
Public API
ConvolvedDistributions.AbstractConvolvedDistribution Type
abstract type AbstractConvolvedDistribution{F<:Distributions.VariateForm, S<:Distributions.ValueSupport} <: Distributions.Distribution{F<:Distributions.VariateForm, S<:Distributions.ValueSupport}Supertype of the distributions of X op Y for independent components — the generalised convolutions. Convolved is the classical sum, Difference the reflected form (Z = X - Y), Product the Mellin form (Z = X * Y), and Ratio the Mellin-quotient form (Z = X / Y); further operations (order statistics) fit the same family.
Parametric on variate form and value support (Distribution{F, S}), so the univariate members stay UnivariateDistributions and existing dispatch is unchanged. S is DERIVED from a member's own components via ConvolvedDistributions._components_support: Discrete when every component is an integer-lattice discrete distribution (discrete with eltype <: Integer), Continuous otherwise — never hardcoded. A member typed Discrete MUST provide an exact route for its density and CDF, because is_exact reports exactness from the S type parameter alone; a Discrete-typed member with no exact route makes that report a lie.
Required of a concrete subtype:
params(d);logpdf(d, x)finite on its support;Base.show(io, d).
Verify a subtype with ConvolvedDistributions.TestUtils.test_convolved_interface, and family membership with ConvolvedDistributions.TestUtils.test_abstract_membership.
See also
Convolved,Difference,Product,Ratio: the concrete members.ConvolvedDistributions.TestUtils: the interface verifiers for a new subtype.
Fields
sourceConvolvedDistributions.AbstractSolverMethod Type
abstract type AbstractSolverMethodAbstract type for solver methods used in CDF/PDF computation.
Subtypes determine whether analytical solutions are preferred or numerical integration is forced.
See also
AnalyticalSolver: prefer closed forms, fall back to quadrature.NumericSolver: always use quadrature.
Fields
sourceConvolvedDistributions.AnalyticalSolver Type
struct AnalyticalSolver{S} <: ConvolvedDistributions.AbstractSolverMethodSolver that attempts analytical solutions when available, falling back to numerical integration.
Stores a numerical integration solver for use when no analytical solution exists for a given distribution pair. When the numeric path is reached the stored payload is honoured: the default GaussLegendre(; n = 64) keeps the native quantile-panelled quadrature, a custom GaussLegendre(n) raises the nodal accuracy, and an Integrals.jl algorithm (with the extension loaded) routes the integration window through IntegralProblem/solve.
See also
NumericSolver: force the quadrature path.GaussLegendre: the default fallback quadrature solver.
Fields
solver::Any: Fallback solver for when no analytical solution exists.
ConvolvedDistributions.Convolved Type
struct Convolved{C<:Tuple, M<:ConvolvedDistributions.AbstractSolverMethod, S<:Distributions.ValueSupport} <: ConvolvedDistributions.AbstractConvolvedDistribution{Distributions.Univariate, S<:Distributions.ValueSupport}Distribution of a sum of independent random variables (a convolution).
Convolved represents
Components may have negative support (for example a Normal capturing pre-symptomatic transmission timing); minimum and maximum are the sums of the component supports, taking the value
Value support
The value support is derived from the components, not hardcoded: when every component is an integer-lattice discrete distribution (discrete with eltype <: Integer), Convolved is itself Discrete and its density/CDF are computed by an exact fold over the integer lattice instead of quadrature (see is_exact). Otherwise it is Continuous, as before. The lattice fold costs O(window) lattice points per evaluation (and multiplies across nested components), so evaluating at a very large point is correspondingly expensive; no cap is imposed.
A two-component Convolved with exactly one integer-lattice discrete side and one non-integer-lattice side also evaluates exactly, by summing the other side's density/CDF over the discrete component's own lattice window rather than falling back to quadrature, which cannot integrate a component whose density is a comb of point masses. Three or more components with a mixed discrete/continuous split do not get this treatment yet and fall back to ordinary quadrature over the discrete side's near-zero-everywhere density – tracked separately, not attempted here.
CDF computation
The CDF is computed by integrating one component out against the CDF of the others:
where
Where an analytical convolution is available (Distributions.convolve applies, e.g. Normal+Normal, equal-scale Gamma, equal-rate Exponential, Poisson+Poisson, equal-p Binomial/ NegativeBinomial) the two-component result is taken directly from the convolved distribution unless a NumericSolver method is set. All other Continuous-typed cases use AD-safe fixed-node Gauss-Legendre quadrature: the integral is mapped from the fixed reference domain gl_integrate), which lets every AD backend specialise on the integrand's own type so component Duals and tangents propagate. A Discrete-typed Convolved never reaches quadrature at all — see "Value support" above.
The method field selects the CDF/PDF backend: an AnalyticalSolver (the default) uses the analytic convolution when one exists and falls back to the numeric path otherwise, while a NumericSolver forces that numeric path even when an analytic convolution exists; the latter is useful for validation and debugging. For a Continuous-typed Convolved the numeric path is quadrature; for a Discrete-typed one it is the exact integer-lattice fold, never quadrature — NumericSolver means "skip the closed form", not "run Gauss-Legendre".
The _closed_form field caches evaluation_path's per-quantity answer, resolved once here at construction rather than on every pdf/cdf call (#92); it is private and carries no public meaning of its own.
See also
convolved: Constructor function
Fields
components::Tuple: Tuple of independent component distributions to be summed.method::ConvolvedDistributions.AbstractSolverMethod: Solver method choosing the analytic vs numeric quadrature backend._closed_form::@NamedTuple{pdf::Bool, logpdf::Bool, cdf::Bool, logcdf::Bool, ccdf::Bool, logccdf::Bool}: Per-quantity closed-form answer, resolved once here (#92, review B).
ConvolvedDistributions.Difference Type
struct Difference{X<:(Distributions.UnivariateDistribution), Y<:(Distributions.UnivariateDistribution), M<:ConvolvedDistributions.AbstractSolverMethod, S<:Distributions.ValueSupport} <: ConvolvedDistributions.AbstractConvolvedDistribution{Distributions.Univariate, S<:Distributions.ValueSupport}Distribution of a difference of two independent random variables.
Difference represents X and Y are independent univariate distributions. It is the dual of Convolved (the sum X with the reflection of Y. Where a convolution gathers two delays into one longer gap, a difference is the signed gap between two events, so it arises as a derived observation (for example the offset between two independently timed measurements) rather than as a delay leaf.
Because the subtraction reflects Y, the support of Z is in general two-sided and can be negative: it runs from Z is therefore not a non-negative delay distribution; treat a Difference as an observation or derived quantity, not as a delay leaf.
Value support
Derived from the components, not hardcoded: a Difference of two integer-lattice discrete distributions is itself Discrete, with a two-sided integer support, and its density/CDF are computed by an exact fold over the integer lattice instead of quadrature (see is_exact). Otherwise it is Continuous, as before.
A Difference with exactly one integer-lattice discrete side (x or y, not both) also evaluates exactly, folding the other side's density/CDF over the discrete component's own lattice window (#115); the two argument orders reflect differently since subtraction is not commutative (see the mixed fold in src/Difference.jl).
Independence
The construction assumes X and Y are independent. The density, CDF, mean and variance below all rely on this; they are not correct for dependent components.
Density and CDF computation
The density is the cross-correlation of the two component densities:
and the CDF integrates X's CDF against Y's density:
For a Normal-Normal pair the closed form NumericSolver method is set. All other Continuous-typed cases use AD-safe fixed-node Gauss-Legendre quadrature (gl_integrate), the same construction Convolved uses: the integral is mapped from the fixed reference domain Duals and tangents propagate. A Discrete-typed Difference never reaches quadrature at all — see "Value support" above.
The method field selects the backend: an AnalyticalSolver (the default) uses the analytic difference where one exists and falls back to the numeric path otherwise, while a NumericSolver forces that numeric path even for a Normal-Normal pair (useful for validation). For a Continuous-typed Difference the numeric path is quadrature; for a Discrete-typed one it is the exact integer-lattice fold, never quadrature — NumericSolver means "skip the closed form", not "run Gauss-Legendre".
See also
difference: Constructor functionConvolved: The dual sum
Fields
x::Distributions.UnivariateDistribution: The minuend component (theXinZ = X - Y).y::Distributions.UnivariateDistribution: The subtrahend component (theYinZ = X - Y).method::ConvolvedDistributions.AbstractSolverMethod: Solver method choosing the analytic vs numeric quadrature backend.
ConvolvedDistributions.GaussLegendre Type
struct GaussLegendre{R}Fixed-node Gauss-Legendre quadrature solver (the package default).
Integrates with a Gauss-Legendre rule of n nodes, evaluated as a bare weighted dot product (see gl_integrate). The constant control flow and the accumulator type being seeded from the integrand make this the AD-safe default: every supported AD backend can differentiate through it, unlike adaptive schemes whose node count depends on integrand values.
The reference nodes and weights are built once at construction and held on the solver, so the differentiated hot path is a pure weighted sum with no shared mutable state. This matters for trace-based reverse-mode backends (Enzyme, ReverseDiff): resolving the rule through a mutated global cache inside the integrand crashes Enzyme reverse, so the rule travels with the solver instead.
n = 64 is accurate to about 1e-13 on the smooth, density-weighted integrands used in this package. This is the core default and needs no heavy dependency. Load Integrals.jl and pass an Integrals.jl algorithm (e.g. QuadGKJL()) when adaptive accuracy matters more than differentiability. AD support for an Integrals.jl-backed solver is backend-dependent: ForwardDiff, ReverseDiff, and Mooncake's forward mode differentiate through it correctly, but Mooncake's reverse mode and Enzyme (both modes) cannot build a derivative rule through the adaptive control flow and raise a clear error rather than returning a wrong gradient.
Examples
using ConvolvedDistributions
# A 64-node rule; integrate x^2 over [0, 1] (exact value 1/3)
solver = ConvolvedDistributions.GaussLegendre(; n = 64)
approx = ConvolvedDistributions.integrate(solver, x -> x^2, 0.0, 1.0)See also
gl_integrate: The underlying quadrature reduction.integrate: The pluggable entry point dispatching on a solver.
Fields
n::Int64: Number of Gauss-Legendre nodes.rule::Any: The reference Gauss-Legendre rule (nodes/weights on[-1, 1]).
ConvolvedDistributions.NumericSolver Type
struct NumericSolver{S} <: ConvolvedDistributions.AbstractSolverMethodSolver that always uses numerical integration.
Forces numerical computation even when analytical solutions are available, useful for testing and validation.
The solver field contains the numerical integration solver to use, and it is honoured by the numeric path: the default payload GaussLegendre(; n = 64) keeps the native quantile-panelled quadrature, a custom GaussLegendre(n) raises the nodal accuracy, and an Integrals.jl algorithm (with the extension loaded) routes the integration window through IntegralProblem/solve.
See also
AnalyticalSolver: the default, preferring closed forms.GaussLegendre: the default quadrature solver.
Fields
solver::Any: Numerical integration solver to use.
ConvolvedDistributions.Product Type
struct Product{X<:(Distributions.UnivariateDistribution), Y<:(Distributions.UnivariateDistribution), M<:ConvolvedDistributions.AbstractSolverMethod, S<:Distributions.ValueSupport} <: ConvolvedDistributions.AbstractConvolvedDistribution{Distributions.Univariate, S<:Distributions.ValueSupport}Distribution of a product of two independent random variables.
Product represents X and Y are independent univariate distributions with non-negative support. It is the multiplicative member of the family: where Convolved gathers two delays into one longer gap and Difference takes the signed gap between two events, a product scales one variable by another, as when a delay is stretched by an independent multiplicative factor.
Both components must satisfy minimum(d) >= 0; the constructor throws an ArgumentError otherwise. Sign-crossing supports are future work (they split the Mellin quadrature into positive and negative branches). The support of Z runs from
Value support
Derived from the components, not hardcoded: a Product of two integer-lattice discrete distributions is itself Discrete, with its density computed by exact divisor enumeration and its CDF by an exact conditioning sum, both replacing quadrature (see is_exact). Otherwise it is Continuous, as before.
A Product with exactly one integer-lattice discrete side (x or y, not both) also evaluates exactly, folding the other side's density/CDF over the discrete component's own positive lattice window (#115). A discrete factor with mass at 0 puts an atom at 0 in Z mixed with a continuous density elsewhere – a measure no pdf can represent – so product(...) rejects that combination with an ArgumentError at construction rather than silently dropping the atom.
Independence
The construction assumes X and Y are independent. The density, CDF, mean and variance below all rely on this; they are not correct for dependent components.
Density and CDF computation
This section describes the Continuous-typed path (see "Value support" above for the Discrete case: an exact divisor enumeration for the density, and an exact conditioning sum, O(z) mass evaluations, for the CDF).
The density is the Mellin convolution of the two component densities:
and the CDF integrates X's CDF against Y's density:
evaluated numerically in the equivalent survival form Y's density diverges at zero (Gamma or Weibull shape below one).
For a LogNormal-LogNormal pair the closed form NumericSolver method is set. All other Continuous-typed cases use AD-safe fixed-node Gauss-Legendre quadrature (gl_integrate), the same construction Convolved and Difference use: the integral is mapped from the fixed reference domain Duals and tangents propagate. A Discrete-typed Product never reaches quadrature at all — see "Value support" above.
The method field selects the backend: an AnalyticalSolver (the default) uses the analytic product where one exists and falls back to the numeric path otherwise, while a NumericSolver forces that numeric path even for a LogNormal-LogNormal pair (useful for validation). For a Continuous-typed Product the numeric path is quadrature; for a Discrete-typed one it is the exact divisor/ conditioning-sum fold, never quadrature — NumericSolver means "skip the closed form", not "run Gauss-Legendre".
See also
product: Constructor functionConvolved: The sumDifference: The signed gap
Fields
x::Distributions.UnivariateDistribution: The multiplicand component (theXinZ = X * Y).y::Distributions.UnivariateDistribution: The multiplier component (theYinZ = X * Y).method::ConvolvedDistributions.AbstractSolverMethod: Solver method choosing the analytic vs numeric quadrature backend.
ConvolvedDistributions.Ratio Type
struct Ratio{X<:(Distributions.UnivariateDistribution), Y<:(Distributions.UnivariateDistribution), M<:ConvolvedDistributions.AbstractSolverMethod} <: ConvolvedDistributions.AbstractConvolvedDistribution{Distributions.Univariate, Distributions.Continuous}Distribution of a ratio of two independent random variables.
Ratio represents X (the numerator) and Y (the denominator) are independent univariate distributions. It is the quotient member of the family: where Convolved sums two delays, Difference takes their signed gap, and Product scales one by the other, a ratio divides one by the other, as when a rate, a proportion, or a normalised measurement is formed from two independent uncertain quantities.
Y may not carry probability mass at zero: the constructor throws an ArgumentError naming the denominator's family otherwise. Subject to that, either component may have two-sided support — unlike Product, a Ratio accepts a sign-crossing numerator or denominator directly, splitting the quadrature at zero rather than rejecting it (see Density and CDF computation below). The support of Z is the interval quotient of the component supports (division by an interval straddling zero gives all of
Value support is Continuous unconditionally, including when both components are discrete: the ratio of two integer-valued variables is supported on the rationals, which is not a lattice, so a Discrete declaration could not honour the pdf-as-probability-mass contract.
Independence
The construction assumes X and Y are independent. The density, CDF, mean and variance below all rely on this; they are not correct for dependent components.
Density and CDF computation
Conditioning on Y = y and differentiating gives
Unlike Product's Mellin form, there is no 1/y factor: the |y| weight suppresses mass near y = 0 instead of amplifying it, so the density stays bounded even when Y's own density diverges there (Gamma/Weibull shape below one). The y-integration splits at zero because |y| has a kink there and the map y \mapsto z y flips orientation across it. The density window is intersected with the effective (quantile-clamped) support of X, since the integrand's mass sits near y \approx (\text{mass of } X) / z and shrinks like 1/z as z grows — a window tied only to Y's own mass would miss it in the tails. The CDF uses a saturated constant plus a transition integral (as Convolved does), with a control variate anchored at the window endpoint nearest zero; this is exact algebra, applied unconditionally, and is what keeps the CDF accurate when Y's density diverges at zero and F_X(0) > 0.
Three pairs have closed forms: Normal(0, \sigma_X) / Normal(0, \sigma_Y) gives Cauchy(0, \sigma_X / \sigma_Y) (zero means only — the general case has no elementary form); Gamma / Gamma gives a scaled BetaPrime; Chisq / Chisq gives a scaled FDist. All other cases use the same AD-safe fixed-node Gauss-Legendre quadrature (gl_integrate) the rest of the package uses.
Differentiating the Normal/Normal pair exactly at zero means omits the \partial/\partial\mu term (the true density does depend on \mu there; the branch only checks iszero(\mu)), the same hazard the package's equal-scale Gamma+Gamma convolution already has. Pass method = NumericSolver() to differentiate through a mean at zero.
The method field selects the backend: an AnalyticalSolver (the default) uses the analytic ratio where one exists and falls back to quadrature otherwise, while a NumericSolver forces the numeric path even for an analytic pair (useful for validation).
Nesting
A Ratio can be a component of another combination's numeric quadrature only when both the numerator and the denominator are non-negative (this includes the Gamma/Gamma and Chisq/Chisq analytic pairs and any non-negative numeric pair, but not the two-sided Normal/Normal pair). Outside that regime the ratio's tails are Cauchy-like, so no cheap effective-support bound is conservative and nesting throws an ArgumentError naming the offending component, rather than silently narrowing the outer window. Used as the outermost distribution, a Ratio has no such restriction.
See also
ratio: Constructor functionConvolved: The sumDifference: The signed gapProduct: The product
Fields
x::Distributions.UnivariateDistribution: The numerator component (theXinZ = X / Y).y::Distributions.UnivariateDistribution: The denominator component (theYinZ = X / Y).method::ConvolvedDistributions.AbstractSolverMethod: Solver method choosing the analytic vs numeric quadrature backend.
ConvolvedDistributions.convolve_pair Function
convolve_pair(
a::Distributions.UnivariateDistribution,
b::Distributions.UnivariateDistribution
) -> Distributions.Normalconvolve_pair(a, b)The analytic sum distribution for a + b, or nothing when no closed form is registered for the pair. This is the extension point a downstream package adds a method to, to teach convolved a closed form for its own distribution type: dispatch (not try/catch) keeps the path differentiable under every AD backend, and returning nothing (rather than throwing) is what tells the caller to fall back to pairwise collapse or numeric quadrature instead.
For the built-in families, Gamma and Exponential additionally need matching scale/rate, Binomial and NegativeBinomial matching success probability, else Distributions.convolve throws.
Examples
using ConvolvedDistributions, Distributions
struct MyPairDelay <: ContinuousUnivariateDistribution end
function ConvolvedDistributions.convolve_pair(::MyPairDelay, ::MyPairDelay)
return Exponential(2.0)
endSee also: convolve_power
ConvolvedDistributions.convolve_power Function
convolve_power(
d::Distributions.UnivariateDistribution,
k::Integer
) -> Distributions.Normalconvolve_power(d, k)The analytic distribution of the sum of k iid copies of d, or nothing when no closed form is registered for the family. This is the extension point convolved(d, k) calls first, before falling back to k - 1 applications of convolve_pair or numeric quadrature; a downstream package adds a method here to give its own distribution type an O(1) repeat instead of the built k-component fold.
For the built-in families this is registered directly rather than routed through repeated convolve_pair collapse, since a family closed under addition has a direct k-fold formula (e.g. Gamma(shape, scale) becomes Gamma(k * shape, scale)). Exponential is the one family whose repeat is not itself an Exponential: k iid Exponential(θ) sum to Gamma(k, θ).
Examples
using ConvolvedDistributions, Distributions
struct MyPowerDelay <: ContinuousUnivariateDistribution end
function ConvolvedDistributions.convolve_power(::MyPowerDelay, k::Integer)
return Exponential(2.0 * k)
endSee also: convolve_pair
ConvolvedDistributions.convolve_series Function
convolve_series(
delay::Distributions.Distribution{Distributions.Univariate, Distributions.Discrete},
series::AbstractVector{<:Real};
mask
) -> AnyConvolve a timeseries with the PMF of a discrete delay distribution.
convolve_series(delay, series) for a DiscreteUnivariateDistribution delay reads the delay PMF directly off the integer lag grid — the lag-k mass IS pdf(delay, k) — and returns the causal discrete convolution of series with that PMF, truncated to the series window. With series the expected events at times 0, 1, ..., t (e.g. infections), the result is the expected downstream event counts at the same times (the EpiNow2-style latent / renewal observation layer).
The masses are [pdf(delay, k) for k in 0:(length(series) - 1)], used as given: no renormalisation, so any delay mass beyond the series window is truncated. pdf(delay, k) is differentiable in the delay parameters for the standard discrete families, so gradients flow under the supported AD backends.
Direct PMF evaluation, NOT a CDF difference: for an integer-support delay, pdf(delay, k) rather than a CDF-difference mass.
Only the integer lags 0, 1, 2, ... are read. A delay with atoms off the integer grid is out of scope (a lag grid means masses at the integers), and mass at negative lags cannot enter a causal convolution, so lags below 0 are not read (consistent with the causal kernel).
A Convolved/Difference/Product of integer-lattice discrete components is itself a DiscreteUnivariateDistribution (#85) and flows straight through this method, reading its exact masses. A CONTINUOUS delay has no mass on the integer grid until it is discretised, and discretisation is an explicit modelling choice this package does not make; it matches no method here, so convolve_series(a_continuous_delay, series) is a MethodError naming what is actually missing, rather than a pre-emptive gate (#95) — see convolve_series(pmf, series) below for the caller-owned discretisation path.
Unlike convolved, which combines distributions into a single Convolved distribution, this returns a numeric series; the separate verb keeps convolved strictly for distribution construction.
Arguments
delay: aDiscreteUnivariateDistribution(e.g.Poisson,DiscreteUniform, a shifted count delay).series: the input timeseries (expected events at unit-spaced times from 0).mask: optional. ABoolvector the same length asseries; when given, only the output positions wheremaskistrueare computed and the rest holdzero(eltype(result)). Masked-out positions are genuinely skipped, not computed and discarded, so a mask selecting a few positions out of a long series is cheap —pdf(delay, k)is only evaluated for the lags a requested position can actually read, so a mask restricted to an early window also skips evaluating the delay'spdfat the later lags. Omitted (the default), every position is computed.
Returns
- A numeric vector of expected downstream counts, the same length as
series.
Examples
using ConvolvedDistributions, Distributions
delay = Poisson(2.0)
infections = [0.0, 1.0, 3.0, 6.0, 8.0, 5.0, 2.0]
expected_counts = convolve_series(delay, infections)See also
convolved: the distribution-level convolutionconvolve_series(pmf, series): the PMF-vector form for caller-owned discretisation
convolve_series(
pmf::AbstractVector{<:Real},
series::AbstractVector{<:Real};
mask
) -> AnyConvolve a timeseries with a caller-supplied discretised delay PMF.
convolve_series(pmf, series) returns the causal discrete convolution of series with the probability masses pmf, truncated to the series window: out[i] = sum(pmf[k + 1] * series[i - k] for k in 0:(min(length(pmf), i) - 1)). pmf[k + 1] is read as the delay mass at integer lag k on the same unit grid as series.
The masses are used exactly as given: no renormalisation, no validation that they sum to one, and no tail correction — mass at lags beyond the series window (including any pmf entries past length(series)) is simply never used, so sub-normalised or window-truncated PMFs stay truncated. This is the decoupled form of convolve_series(delay, series): the caller owns the discretisation (e.g. double-interval-censored masses from CensoredDistributions.jl), and this method only convolves. The convolution is linear, so gradients flow through both pmf and series under the supported AD backends.
Arguments
pmf: the discretised delay probability masses at integer lags0, 1, 2, ...(used as given).series: the input timeseries (expected events at unit-spaced times from 0).mask: optional output-position mask, as inconvolve_series(delay, series).
Returns
- A numeric vector of expected downstream counts, the same length as
series.
Examples
using ConvolvedDistributions
pmf = [0.5, 0.3, 0.2]
infections = [0.0, 1.0, 3.0, 6.0, 8.0, 5.0, 2.0]
expected_counts = convolve_series(pmf, infections)See also
convolve_series(pmf::DiscreteNonParametric, series): the non-unit-grid form
convolve_series(
pmf::Distributions.DiscreteNonParametric,
series::AbstractVector{<:Real};
mask
) -> AnyConvolve a timeseries with a delay's DiscreteNonParametric PMF.
convolve_series(pmf, series) for a DiscreteNonParametric pmf reads its support as the delay's lag grid and its probabilities as the masses at those lags, then convolves as convolve_series(probs(pmf), series). The support must start at 0 and be regularly spaced (a constant gap between consecutive support points); an irregular or offset grid throws an ArgumentError, since convolve_series has no separate argument to carry a grid width or starting lag.
This is the non-unit-grid caller-supplied form: a DiscreteNonParametric built on a coarser grid (e.g. DiscreteNonParametric(0:7:28, weekly_masses) for weekly bins) convolves correctly, whereas a plain AbstractVector PMF only ever reads as the unit grid.
Unlike the AbstractVector form, which uses masses exactly as given (no renormalisation, so a window-truncated tail stays sub-normalised), DiscreteNonParametric enforces a genuine probability vector at construction (sum(probs(pmf)) ≈ 1, or Distributions.jl throws a DomainError). A window-truncated or otherwise sub-normalised PMF therefore needs the plain vector form instead.
Arguments
pmf: aDiscreteNonParametricwhose support is the delay's lag grid (regularly spaced, starting at0) and whose probabilities are the masses at those lags.series: the input timeseries, sampled at the same grid steps aspmf's support, from time 0.mask: optional output-position mask, as inconvolve_series(delay, series).
Examples
using ConvolvedDistributions, Distributions
pmf = DiscreteNonParametric([0.0, 7.0, 14.0], [0.6, 0.3, 0.1])
infections = [0.0, 1.0, 3.0, 6.0, 8.0, 5.0, 2.0]
expected_counts = convolve_series(pmf, infections)See also
convolve_series(pmf::AbstractVector, series): the unit-grid vector form
convolve_series(
delays::AbstractVector,
series::AbstractVector{<:Real};
indexed_by,
mask,
kwargs...
) -> AnyConvolve a timeseries with a time-varying delay: one delay per time point.
convolve_series(delays, series) takes one delay per entry of series and returns the convolution, truncated to the series window. Each delay's lag masses come from its own single-delay convolve_series(delay, series) method, so the elements may be of any, and of mixed, types. A type whose masses are not what that method gives specialises delay_masses instead.
Identical delays share one set of masses, however often they recur, so a delay is only ever built once. With a mask, a delay is also built no larger than the requested output positions can actually read — a delay at a time point a mask excludes entirely is never built beyond a single placeholder lag.
indexed_by names which time the delay belongs to:
:primary(the default): the delay belongs to the events, so the cohort at timesspreads forward throughdelays[s]—out[i] = Σ_s series[s] * pmf_s[i - s + 1]. Conserves mass up to the truncated tail.:secondary: the delay belongs to the observation time, so everything landing at timeiis read throughdelays[i]—out[i] = Σ_k pmf_i[k + 1] * series[i - k]. Not mass-conserving.
Any other keyword is forwarded to each distinct delay's delay_masses call, so a vector of continuous delays needing a non-default discretisation (e.g. interval) agrees with the same keywords passed to the single-delay convolve_series(delay, series) form.
Arguments
delays: one delay per time point, inseriesorder.series: the input timeseries (expected events at unit-spaced times from 0).indexed_by::primary(default) or:secondary.mask: optional output-position mask, as inconvolve_series(delay, series).kwargs...: discretisation keywords, forwarded todelay_massesfor each distinct delay.
Returns
- A numeric vector of expected downstream counts, the same length as
series.
Examples
using ConvolvedDistributions, Distributions
infections = [0.0, 1.0, 3.0, 6.0, 8.0, 5.0, 2.0]
delays = [Poisson(λ) for λ in range(3.0, 1.0; length = length(infections))]
expected_counts = convolve_series(delays, infections)See also
convolve_series(delay, series): the static single-delay formconvolve_series(pmfs::AbstractMatrix, series): the caller-supplied-PMF formdelay_masses: how one delay's masses are read, and where to specialise it
convolve_series(
runs::AbstractVector{<:Pair},
series::AbstractVector{<:Real};
indexed_by,
mask
) -> AnyConvolve a timeseries with a delay that changes less often than the series.
convolve_series(runs, series) takes delay => length pairs, each holding for that many consecutive time points, and expands them before convolving — so the delays (or mass vectors) are given once per regime rather than once per time point. The lengths must sum to length(series).
indexed_by is as in the one-delay-per-time-point form.
Arguments
runs:delay => lengthpairs, inseriesorder. Eachdelayis anything the one-per-time-point form accepts, including a mass vector.series: the input timeseries (expected events at unit-spaced times from 0).indexed_by::primary(default) or:secondary.mask: optional output-position mask, as inconvolve_series(delay, series).
Returns
- A numeric vector of expected downstream counts, the same length as
series.
Examples
using ConvolvedDistributions, Distributions
infections = [0.0, 1.0, 3.0, 6.0, 8.0, 5.0, 2.0]
expected_counts = convolve_series([Poisson(3.0) => 3, Poisson(1.0) => 4],
infections)See also
convolve_series(delays::AbstractVector, series): one delay per time point
convolve_series(
pmfs::AbstractMatrix{<:Real},
series::AbstractVector{<:Real};
indexed_by,
mask
) -> AnyConvolve a timeseries with time-varying caller-supplied delay PMFs held in a matrix.
convolve_series(pmfs, series) reads an AbstractMatrix as one delay PMF per time point, lags down columns: pmfs[k + 1, j] is the mass at lag k for time point j, so size(pmfs, 2) must equal length(series). The lag count size(pmfs, 1) is free. A time-by-lag matrix M is passed as transpose(M).
indexed_by is as in the vector-of-delays form. Masses are used exactly as given: no renormalisation, no sum-to-one check and no tail correction.
Arguments
pmfs: delay masses, lags down columns, one column per time point.series: the input timeseries (expected events at unit-spaced times from 0).indexed_by::primary(default) or:secondary.mask: optional output-position mask, as inconvolve_series(delay, series).
Returns
- A numeric vector of expected downstream counts, the same length as
series.
Examples
using ConvolvedDistributions
infections = [0.0, 1.0, 3.0, 6.0]
pmfs = [0.5 0.5 0.6 0.7
0.3 0.3 0.3 0.2
0.2 0.2 0.1 0.1]
expected_counts = convolve_series(pmfs, infections)See also
convolve_series(pmf::AbstractVector, series): the static unit-grid vector formconvolve_series(delays::AbstractVector, series): the vector-of-distributions form
convolve_series(
pmfs::AbstractVector{<:AbstractVector{<:Real}},
series::AbstractVector{<:Real};
indexed_by,
mask
) -> AnyConvolve a timeseries with time-varying caller-supplied delay PMFs held in a vector of vectors.
The ragged counterpart of the matrix form: pmfs[j] is the delay PMF for time point j on the unit lag grid, so length(pmfs) must equal length(series) while each PMF may carry its own number of lags. indexed_by and the masses-as-given contract are as in the matrix form.
Arguments
pmfs: one vector of delay masses per time point, each from lag 0.series: the input timeseries (expected events at unit-spaced times from 0).indexed_by::primary(default) or:secondary.mask: optional output-position mask, as inconvolve_series(delay, series).
Returns
- A numeric vector of expected downstream counts, the same length as
series.
Examples
using ConvolvedDistributions
infections = [0.0, 1.0, 3.0, 6.0]
pmfs = [[0.5, 0.3, 0.2], [0.5, 0.5], [1.0], [0.4, 0.4, 0.1, 0.1]]
expected_counts = convolve_series(pmfs, infections)See also
convolve_series(pmfs::AbstractMatrix, series): the rectangular matrix form
ConvolvedDistributions.convolved Function
convolved(
components::AbstractVector{<:Distributions.UnivariateDistribution};
method,
strict
) -> Union{ConvolvedDistributions.Convolved{C, AnalyticalSolver{ConvolvedDistributions.GaussLegendre{ConvolvedDistributions._GL{Vector{Float64}, Vector{Float64}}}}, Distributions.Continuous} where C<:Tuple, ConvolvedDistributions.Convolved{C, AnalyticalSolver{ConvolvedDistributions.GaussLegendre{ConvolvedDistributions._GL{Vector{Float64}, Vector{Float64}}}}, Distributions.Discrete} where C<:Tuple}Create the distribution of a sum of independent delays (a convolution).
Accepts either two or more positional component distributions, or a single vector/tuple of components. Returns a Convolved distribution.
Arguments
components: Two or moreUnivariateDistributions, or a vector/tuple of them.
Keyword Arguments
method: The solver method, anAnalyticalSolver(the default) orNumericSolver.NumericSolverforces numeric quadrature even when an analytic convolution is available.strict: Whentrue, error (naming the component families) rather than silently return an object whose density/CDF would fall back to quadrature.falseby default. Seeevaluation_pathto check the route after construction instead of asserting it up front.
Returns
- A
Convolveddistribution of the sum of the components.
Examples
using ConvolvedDistributions, Distributions
# Sum of two delays
d = convolved(Gamma(2.0, 1.0), LogNormal(1.5, 0.5))
cdf_at_5 = cdf(d, 5.0)
# Sum of three delays from a vector
d3 = convolved([Gamma(2.0, 1.0), Gamma(1.0, 1.0), Normal(0.0, 1.0)])
mean_sample = rand(d3)
# Force numeric quadrature even for an analytic pair
dn = convolved(Normal(0.0, 1.0), Normal(1.0, 2.0);
method = NumericSolver())
cdf_numeric = cdf(dn, 2.0)See also
Convolved: The distribution typeevaluation_path: Check the route without asserting it.
convolved(
d::Distributions.UnivariateDistribution,
k::Integer
) -> Anyconvolved(d::UnivariateDistribution, k::Integer)
convolved(d::UnivariateDistribution, k::Val)Create the distribution of the sum of k independent copies of d.
Returns d itself for k == 1. For a family closed under addition (Normal, Gamma, Poisson, Binomial, NegativeBinomial, and Exponential, which sums to a Gamma) the closed form is returned directly, in O(1) time regardless of k – no k-length tuple is built. Otherwise a Convolved of k copies of d is built and returned, equivalent to convolved(d, d, ..., d) (k times).
Inference
The Convolved component-tuple type carries the component count, so the returned type depends on the VALUE of k, not just its type: with a runtime k::Integer, convolved(d, k) is not type-inferable for a family with no closed form (it is for a closed-form family, since the result type there depends only on d's type). Pass a literal k or convolved(d, Val(k)) for an inferable result on any family.
Arguments
d: The component distribution repeatedktimes.k: The repeat count, a positiveIntegerorVal.
Examples
using ConvolvedDistributions, Distributions
# Closed form: exact, O(1).
convolved(Gamma(2.0, 1.5), 5) # == Gamma(10.0, 1.5)
# No closed form: builds the 4-component `Convolved`.
convolved(LogNormal(0.0, 0.5), 4)
# Inference-stable path for a runtime k on any family.
convolved(LogNormal(0.0, 0.5), Val(4))See also
sourceConvolvedDistributions.convolved_ccdf Function
convolved_ccdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
x::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyconvolved_ccdf(d, components, x, method)The complementary CDF of the sum of components at x. See convolved_cdf.
ConvolvedDistributions.convolved_cdf Function
convolved_cdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
x::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyconvolved_cdf(d, components, x, method)The CDF of the sum of components at x, dispatched on the solver method method. d is the Convolved components came from (used by the NumericSolver arm directly, instead of rebuilding it). Public, alongside its logcdf/ccdf/logccdf/pdf/logpdf/quantile siblings, so a downstream package adds its own analytic pair by defining a method on a two-element tuple TYPE more specific than (Convolved, Tuple, Real, AnalyticalSolver) – no registration call needed, plain dispatch picks it up.
Examples
using ConvolvedDistributions, Distributions
d = convolved(Gamma(2.0, 1.5), Uniform(0.0, 2.0))
ConvolvedDistributions.convolved_cdf(
d, (Gamma(2.0, 1.5), Uniform(0.0, 2.0)), 3.0, AnalyticalSolver())See also: Convolved
ConvolvedDistributions.convolved_logccdf Function
convolved_logccdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
x::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyconvolved_logccdf(d, components, x, method)The log complementary CDF of the sum of components at x. See convolved_cdf.
ConvolvedDistributions.convolved_logcdf Function
convolved_logcdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
x::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyconvolved_logcdf(d, components, x, method)The log CDF of the sum of components at x. See convolved_cdf.
ConvolvedDistributions.convolved_logpdf Function
convolved_logpdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
x::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyconvolved_logpdf(d, components, x, method)The log density of the sum of components at x. See convolved_cdf.
ConvolvedDistributions.convolved_minimum Function
convolved_minimum(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
method::ConvolvedDistributions.AbstractSolverMethod
)convolved_minimum(d, components, method)The minimum of the sum of components. A quantity with no evaluation point takes no x/p argument at all. Not wired into minimum, which is already exact by summation; only the unimplemented-method-type error exists, demonstrating the shape compiles for a future zero-argument quantity.
ConvolvedDistributions.convolved_pdf Function
convolved_pdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
x::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyconvolved_pdf(d, components, x, method)The density of the sum of components at x. See convolved_cdf.
ConvolvedDistributions.convolved_quantile Function
convolved_quantile(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
p::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyconvolved_quantile(d, components, p, method)The quantile of the sum of components at probability p. Skeleton and AnalyticalSolver arm only: the NumericSolver arm needs a nonlinear solve and lives in the ConvolvedDistributionsOptimizationExt extension, so a non-analytic fold's quantile is unavailable until Optimization.jl is loaded, while a fully analytic one (_collapse_analytic_pair) works without it.
See also: convolved_cdf
ConvolvedDistributions.delay_masses Function
delay_masses(
d::Distributions.Distribution{Distributions.Univariate, Distributions.Discrete},
n::Int64
) -> AnyA delay's probability masses at lags 0:(n - 1).
The extension point for the time-varying convolve_series(delays, series): it reads each delay's masses through this function. The default convolves a unit impulse at time 0 through the delay's own single-delay convolve_series(delay, series) method, which returns exactly that delay's kernel, so a delay type needs no method here at all. Discrete delays take the pdf read that method makes anyway, at O(n) rather than O(n²).
Add a method when a delay's masses are NOT what its single-delay method would give — a discrete-support type whose lag masses are not pdf(delay, k), say.
Keyword arguments (e.g. a continuous delay's discretisation interval) are forwarded to the single-delay convolve_series call whenever any are given, so this default agrees with the scalar convolve_series(delay, series) path under the same keywords. A call with no keywords dispatches exactly as a plain two-argument call would, so a delay type whose own delay_masses or convolve_series method takes no keywords at all keeps working unchanged as long as no keywords are asked of it. Asking such a delay for keywords it cannot honour raises an ArgumentError naming delay_masses, not a bare keyword-sorter MethodError.
Arguments
delay: the delay.n: how many lags to return, from lag 0.kwargs...: discretisation keywords, forwarded to the delay's single-delayconvolve_seriesmethod.
Returns
- A vector of
nprobability masses.
Examples
using ConvolvedDistributions, Distributions
ConvolvedDistributions.delay_masses(Poisson(2.0), 4)ConvolvedDistributions.difference Function
difference(
x::Distributions.UnivariateDistribution,
y::Distributions.UnivariateDistribution;
method,
strict
) -> Union{Difference{X, Y, AnalyticalSolver{ConvolvedDistributions.GaussLegendre{ConvolvedDistributions._GL{Vector{Float64}, Vector{Float64}}}}, Distributions.Continuous} where {X<:(Distributions.UnivariateDistribution), Y<:(Distributions.UnivariateDistribution)}, Difference{X, Y, AnalyticalSolver{ConvolvedDistributions.GaussLegendre{ConvolvedDistributions._GL{Vector{Float64}, Vector{Float64}}}}, Distributions.Discrete} where {X<:(Distributions.UnivariateDistribution), Y<:(Distributions.UnivariateDistribution)}}Create the distribution of a difference of two independent variables.
Returns a Difference representing convolved: a convolution forms the sum of two delays, a difference the signed gap between two events. The support of Z can be negative, so Z is an observation or derived quantity rather than a non-negative delay leaf (see Difference).
X and Y are assumed independent.
Arguments
x: The minuend distribution (theXinZ = X - Y), aUnivariateDistribution.y: The subtrahend distribution (theYinZ = X - Y), aUnivariateDistribution.
Keyword Arguments
method: The solver method, anAnalyticalSolver(the default) orNumericSolver.NumericSolverforces numeric quadrature even for aNormal-Normalpair, mirroringconvolved.strict: Whentrue, error (naming the component families) rather than silently return an object whose density/CDF would fall back to quadrature.falseby default. Seeevaluation_pathto check the route after construction instead of asserting it up front.
Returns
- A
Differencedistribution of the signed gapZ = X - Y.
Examples
using ConvolvedDistributions, Distributions
# Difference of two delays; mean is the difference of the means (≈ 3)
d = difference(Normal(5.0, 1.0), Normal(2.0, 1.0))
mean(d)See also
Difference: The distribution typeconvolved: The dual sumevaluation_path: Check the route without asserting it.
ConvolvedDistributions.difference_ccdf Function
difference_ccdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anydifference_ccdf(d, components, z, method)The complementary CDF of components[1] - components[2] at z. See difference_cdf.
ConvolvedDistributions.difference_cdf Function
difference_cdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anydifference_cdf(d, components, z, method)The CDF of components[1] - components[2] at z, dispatched on the solver method method. Mirrors convolved_cdf: a downstream package adds its own analytic pair by defining a method on a two-element tuple TYPE more specific than (Difference, Tuple, Real, AnalyticalSolver).
See also: Difference
ConvolvedDistributions.difference_logccdf Function
difference_logccdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anydifference_logccdf(d, components, z, method)The log complementary CDF of components[1] - components[2] at z. See difference_cdf.
ConvolvedDistributions.difference_logcdf Function
difference_logcdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anydifference_logcdf(d, components, z, method)The log CDF of components[1] - components[2] at z. See difference_cdf.
ConvolvedDistributions.difference_logpdf Function
difference_logpdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anydifference_logpdf(d, components, z, method)The log density of components[1] - components[2] at z. See difference_cdf.
ConvolvedDistributions.difference_pair Function
difference_pair(
x::Distributions.UnivariateDistribution,
y::Distributions.UnivariateDistribution
) -> Distributions.Normaldifference_pair(x, y)The analytic distribution for x - y, or nothing when no closed form is registered for the pair. This is the extension point a downstream package adds a method to, to teach difference a closed form for its own distribution type: dispatch (not try/catch) keeps the path differentiable under every AD backend, and returning nothing (rather than throwing) is what tells the caller to fall back to numeric quadrature instead.
Examples
using ConvolvedDistributions, Distributions
struct MyDiffDelay <: ContinuousUnivariateDistribution end
function ConvolvedDistributions.difference_pair(::MyDiffDelay, ::MyDiffDelay)
return Normal(0.0, 1.0)
endSee also: convolve_pair
ConvolvedDistributions.difference_pdf Function
difference_pdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anydifference_pdf(d, components, z, method)The density of components[1] - components[2] at z. See difference_cdf.
ConvolvedDistributions.difference_quantile Function
difference_quantile(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
p::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anydifference_quantile(d, components, p, method)The quantile of components[1] - components[2] at probability p. Skeleton and AnalyticalSolver arm only, mirroring convolved_quantile: the NumericSolver arm needs a nonlinear solve and lives in the ConvolvedDistributionsOptimizationExt extension.
See also: difference_cdf
ConvolvedDistributions.evaluation_path Function
evaluation_path(
d::ConvolvedDistributions.AbstractConvolvedDistribution
) -> Symbol
evaluation_path(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
quantities
) -> SymbolReport which route d will take for quantities, without evaluating any of them: :analytic when every quantity in quantities has an exact closed form, :numeric otherwise. quantities is a single Distributions.jl generic (e.g. cdf) or a tuple of them, defaulting to (pdf, cdf) — density and CDF both exact, the route strict = true demands.
:numeric covers two different underlying routes: Gauss-Legendre quadrature for a continuous combination, and the exact discrete fold (src/lattice.jl, or the Product divisor fold) for an all-discrete integer-lattice combination. evaluation_path does not distinguish them — widening its two-valued contract would break downstream code that branches on it (#92) — so use is_exact when the distinction matters (whether evaluating d carries any quadrature error at all).
Recurses through nesting: a combination with any non-analytic component (including a nested Convolved/Difference/Product/Ratio using NumericSolver, or one with no matching closed form) reports :numeric, since evaluating it falls back to quadrature somewhere in the recursion.
Arguments
d: The combination to report the route for.quantities: A quantity (e.g.cdf) or tuple of them; default(pdf, cdf).
Examples
using ConvolvedDistributions, Distributions
# Normal + Normal has an analytic convolution
d = convolved(Normal(0.0, 1.0), Normal(1.0, 2.0))
ConvolvedDistributions.evaluation_path(d)
# Gamma + Uniform has an exact cdf and pdf but no analytic convolution
dg = convolved(Gamma(2.0, 1.0), Uniform(0.0, 2.0))
ConvolvedDistributions.evaluation_path(dg, cdf)
# Gamma + LogNormal has no closed form for either quantity
dn = convolved(Gamma(2.0, 1.0), LogNormal(1.5, 0.5))
ConvolvedDistributions.evaluation_path(dn)See also
has_closed_form: the boolean convenience form.is_exact: whether evaluation carries any quadrature error.
ConvolvedDistributions.gl_integrate Function
gl_integrate(f, lo, hi) -> Any
gl_integrate(
f,
lo,
hi,
rule::ConvolvedDistributions._GL
) -> AnyIntegrate a scalar function f over [lo, hi] by fixed-node Gauss-Legendre quadrature.
The reference domain [-1, 1] is mapped onto [lo, hi] inside the integrand and the result reduced as a weighted dot product, so the accumulator's element type is taken from the integrand and AD Duals and tangents propagate. Returns a typed zero when hi <= lo.
Arguments
f: The scalar integrand.lo: Lower integration bound.hi: Upper integration bound.rule: Gauss-Legendre rule to use (default: the 192-node convolution rule).
Returns
- The quadrature approximation of the integral, typed by the integrand (a typed zero when
hi <= lo).
Examples
using ConvolvedDistributions
# Integrate x^2 over [0, 1] (exact value 1/3)
approx = ConvolvedDistributions.gl_integrate(x -> x^2, 0.0, 1.0)See also
GaussLegendre: The default solver wrapping this rule.integrate: The pluggable entry point.
ConvolvedDistributions.has_closed_form Function
has_closed_form(
d::ConvolvedDistributions.AbstractConvolvedDistribution
) -> Bool
has_closed_form(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
quantities
) -> BoolWhether d has an exact closed form for quantities — evaluation_path(d, quantities) === :analytic. quantities defaults to (pdf, cdf), as for evaluation_path.
Arguments
d: The combination to report the route for.quantities: A quantity (e.g.cdf) or tuple of them; default(pdf, cdf).
Examples
using ConvolvedDistributions, Distributions
d = convolved(Normal(0.0, 1.0), Normal(1.0, 2.0))
ConvolvedDistributions.has_closed_form(d)See also
evaluation_path: the full:analytic/:numericpredicate.is_exact: true for a closed form OR an exact discrete fold.
ConvolvedDistributions.integrate Function
integrate(
solver::ConvolvedDistributions.GaussLegendre,
f,
lower,
upper
) -> AnyIntegrate a scalar function f over [lower, upper] using solver.
This is the pluggable integration entry point: every integral in the package calls integrate, dispatching on the solver type. The default GaussLegendre solver routes to gl_integrate. Loading the optional Integrals.jl extension adds a method for Integrals.jl algorithms (e.g. QuadGKJL()), which builds an IntegralProblem and calls solve.
Arguments
solver: The integration backend.GaussLegendreby default; any Integrals.jl algorithm when that extension is loaded.f: The scalar integrand.lower: Lower integration bound.upper: Upper integration bound.
Returns
- The integral approximation computed by the chosen backend, typed by the integrand.
Examples
using ConvolvedDistributions
solver = ConvolvedDistributions.GaussLegendre(; n = 64)
approx = ConvolvedDistributions.integrate(solver, x -> x^2, 0.0, 1.0)See also
GaussLegendre: The default solver.gl_integrate: The default quadrature reduction.
ConvolvedDistributions.is_exact Function
is_exact(
d::ConvolvedDistributions.AbstractConvolvedDistribution
) -> AnyWhether evaluating d's density and CDF carries no quadrature error.
evaluation_path and is_exact answer orthogonal questions — which route, and whether that route is exact — so together they form a 2x2:
| exact | inexact | |
|---|---|---|
:analytic | closed form | — |
:numeric | discrete fold | Gauss-Legendre |
is_exact(d) = has_closed_form(d) || [exact discrete route], where the exact-discrete-route predicate is the SAME one the Convolved/ Difference/Product route functions dispatch on (d's value-support type parameter is Discrete), so the reported exactness cannot drift from what evaluation actually does — the same no-drift discipline evaluation_path keeps.
Tail clamping does not count as inexact. When a component is unbounded (e.g. a Difference of two count distributions, unbounded below), the discrete fold's window is clamped at the _CONVOLVED_TAIL quantile, trimming roughly 1e-8 of mass — identical to the clamp the continuous quadrature paths already apply. is_exact means "no quadrature error", not "no approximation whatsoever"; window clamping is a separate, documented approximation that applies equally on both routes, so it does not flip is_exact to false.
Examples
using ConvolvedDistributions, Distributions
# Closed form: exact.
is_exact(convolved(Normal(0.0, 1.0), Normal(1.0, 2.0)))
# No closed form, but both components are integer-lattice discrete: the
# exact lattice fold replaces quadrature, so this is exact too.
is_exact(convolved(Poisson(1.0), Geometric(0.3)))
# No closed form and a continuous component: Gauss-Legendre, inexact.
is_exact(convolved(Gamma(2.0, 1.0), LogNormal(1.5, 0.5)))See also
evaluation_path: which route (:analytic/:numeric).has_closed_form: the closed-form-only predicate.
ConvolvedDistributions.partial_expectation Function
partial_expectation(component)The partial first moment component, as a closure over its parameters. The extension point uniform_window_cdf needs: a downstream family adds its own Uniform-window closed form by defining a method here and calling uniform_window_cdf(component, window, x, partial_expectation(component)) from a convolved_cdf method. A family that also wants convolved_ccdf/convolved_logccdf closed forms adds the survival-side companion, upper_partial_expectation, too.
ConvolvedDistributions.pgf Function
Probability generating function, d.
Mirrors the Distributions.mgf/Distributions.cf calling convention (pgf(d::UnivariateDistribution, s)). Closed forms are registered for the standard count families (Poisson, Bernoulli, Binomial, Geometric, NegativeBinomial); any other DiscreteUnivariateDistribution falls back to a truncated series. There is no method for a continuous distribution, and no fallback silently truncates a series it cannot bound — both raise rather than return an approximate or wrong answer.
Arguments
d: A discrete univariate distribution.s: The generating-function argument.|s| <= 1is always accepted; a specific method may accept a wider domain where its series converges (seeGeometric/NegativeBinomialbelow).
Examples
using ConvolvedDistributions, Distributions
ConvolvedDistributions.pgf(Poisson(2.0), 0.5)
ConvolvedDistributions.pgf(Binomial(5, 0.3), -1.0)ConvolvedDistributions.product Function
product(
x::Distributions.UnivariateDistribution,
y::Distributions.UnivariateDistribution;
method,
strict
) -> Union{ConvolvedDistributions.Product{X, Y, AnalyticalSolver{ConvolvedDistributions.GaussLegendre{ConvolvedDistributions._GL{Vector{Float64}, Vector{Float64}}}}, Distributions.Continuous} where {X<:(Distributions.UnivariateDistribution), Y<:(Distributions.UnivariateDistribution)}, ConvolvedDistributions.Product{X, Y, AnalyticalSolver{ConvolvedDistributions.GaussLegendre{ConvolvedDistributions._GL{Vector{Float64}, Vector{Float64}}}}, Distributions.Discrete} where {X<:(Distributions.UnivariateDistribution), Y<:(Distributions.UnivariateDistribution)}}Create the distribution of a product of two independent variables.
Returns a Product representing convolved sums two delays and difference takes their signed gap, a product scales one variable by an independent multiplicative factor. Both components must have non-negative support (minimum(d) >= 0); sign-crossing supports throw an ArgumentError and are future work.
X and Y are assumed independent.
Arguments
x: The multiplicand distribution (theXinZ = X * Y), aUnivariateDistributionwith non-negative support.y: The multiplier distribution (theYinZ = X * Y), aUnivariateDistributionwith non-negative support.
Keyword Arguments
method: The solver method, anAnalyticalSolver(the default) orNumericSolver.NumericSolverforces numeric quadrature even for aLogNormal-LogNormalpair, mirroringconvolved.strict: Whentrue, error (naming the component families) rather than silently return an object whose density/CDF would fall back to quadrature.falseby default. Seeevaluation_pathto check the route after construction instead of asserting it up front.
Returns
- A
Productdistribution of the productZ = X * Y.
Examples
using ConvolvedDistributions, Distributions
# A delay stretched by an independent multiplicative factor;
# the mean is the product of the means (≈ 3.3).
d = product(Gamma(3.0, 1.0), LogNormal(0.0, 0.3))
mean(d)See also
Product: The distribution typeconvolved: The sumdifference: The signed gapevaluation_path: Check the route without asserting it.
product(
d::Distributions.UnivariateDistribution,
k::Integer
) -> Distributions.UnivariateDistributionproduct(d::UnivariateDistribution, k::Integer)
product(d::UnivariateDistribution, k::Val)Create the distribution of the product of k independent copies of d.
Returns d itself for k == 1. For a family closed under multiplication (LogNormal) the closed form is returned directly, in O(1) time regardless of k. Otherwise a Product nesting is built and returned, equivalent to folding product over k copies of d left to right.
Inference
Product always holds exactly two components, so a k-fold product with no closed form nests k - 1 Product levels, and the nesting depth is part of the returned type: with a runtime k::Integer, product(d, k) is not type-inferable for such a family (it is for a closed-form family, since the result type there depends only on d's type). Pass a literal k or product(d, Val(k)) for an inferable result on any family; the Val path compiles a distinct method per k, so reserve it for small, compile-time-known counts.
Arguments
d: The component distribution repeatedktimes.k: The repeat count, a positiveIntegerorVal.
Examples
using ConvolvedDistributions, Distributions
# Closed form: exact, O(1).
product(LogNormal(0.0, 0.3), 4)
# No closed form: nests three `Product` levels.
product(Gamma(2.0, 1.0), 4)See also
sourceConvolvedDistributions.product_ccdf Function
product_ccdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyproduct_ccdf(d, components, z, method)The complementary CDF of components[1] * components[2] at z. See product_cdf.
ConvolvedDistributions.product_cdf Function
product_cdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyproduct_cdf(d, components, z, method)The CDF of components[1] * components[2] at z, dispatched on the solver method method. Mirrors difference_cdf: a downstream package adds its own analytic pair by defining a method on a two-element tuple TYPE more specific than (Product, Tuple, Real, AnalyticalSolver).
See also: Product
ConvolvedDistributions.product_logccdf Function
product_logccdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyproduct_logccdf(d, components, z, method)The log complementary CDF of components[1] * components[2] at z. See product_cdf.
ConvolvedDistributions.product_logcdf Function
product_logcdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyproduct_logcdf(d, components, z, method)The log CDF of components[1] * components[2] at z. See product_cdf.
ConvolvedDistributions.product_logpdf Function
product_logpdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyproduct_logpdf(d, components, z, method)The log density of components[1] * components[2] at z. See product_cdf.
ConvolvedDistributions.product_pair Function
product_pair(
x::Distributions.UnivariateDistribution,
y::Distributions.UnivariateDistribution
) -> Distributions.LogNormalproduct_pair(x, y)The analytic distribution for x * y, or nothing when no closed form is registered for the pair. This is the extension point a downstream package adds a method to, to teach product a closed form for its own distribution type: dispatch (not try/catch) keeps the path differentiable under every AD backend, and returning nothing (rather than throwing) is what tells the caller to fall back to numeric quadrature instead.
Examples
using ConvolvedDistributions, Distributions
struct MyProdDelay <: ContinuousUnivariateDistribution end
function ConvolvedDistributions.product_pair(::MyProdDelay, ::MyProdDelay)
return LogNormal(0.0, 1.0)
endSee also: convolve_pair
ConvolvedDistributions.product_pdf Function
product_pdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyproduct_pdf(d, components, z, method)The density of components[1] * components[2] at z. See product_cdf.
ConvolvedDistributions.product_power Function
product_power(
d::Distributions.UnivariateDistribution,
k::Integer
) -> Distributions.LogNormalproduct_power(d, k)The analytic distribution of the product of k iid copies of d, or nothing when no closed form is registered for the family. This is the extension point product(d, k) calls first, before falling back to the k - 1-level Product nesting; a downstream package adds a method here to give its own distribution type an O(1) repeat.
For the built-in LogNormal family, on the log scale a LogNormal(μ, σ) product is a sum of normals, so k copies give LogNormal(k * μ, sqrt(k) * σ).
Examples
using ConvolvedDistributions, Distributions
struct MyFactor <: ContinuousUnivariateDistribution end
function ConvolvedDistributions.product_power(::MyFactor, k::Integer)
return LogNormal(0.0, sqrt(k))
endConvolvedDistributions.product_quantile Function
product_quantile(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
p::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyproduct_quantile(d, components, p, method)The quantile of components[1] * components[2] at probability p. Skeleton and AnalyticalSolver arm only, mirroring difference_quantile: the NumericSolver arm needs a nonlinear solve and lives in the ConvolvedDistributionsOptimizationExt extension.
See also: product_cdf
ConvolvedDistributions.quantile_by_optimization Function
Numerically invert cdf(d, ·) = p for the quantile of d at p.
No closed form is assumed for d: the quantile is found by minimising a clamped-logit-transformed residual between cdf(d, q) and p with a Nelder-Mead solve, started from the caller-supplied initial_guess. The logit transform keeps the objective steep in the far tails, where a plain squared residual on cdf is nearly flat in q and lets the solve stop early (issue #48).
Arguments
d: The distribution to invert.p: The target probability; must lie in[0, 1].initial_guess: A length-1 vector giving the Nelder-Mead starting point.
Keyword Arguments
postprocess: Applied to the solved quantile before it is returned (e.g. to snap it onto a discrete grid). Defaults toidentity.check_nan: Reject aNaNpwith anArgumentErrorwhentrue(the default). Withcheck_nan = falseaNaNpskips validation and the solve errors at the convergence check instead; it never returns a value for aNaNtarget.solver: The Optim.jl solver passed tosolve. Defaults toNelderMead().solve_kwargs...: Passed through tosolve, merged over the defaultsreltol = 1e-8,abstol = 1e-8,maxiters = 10000(explicit values win).
Returns
- The quantile
qsuch thatcdf(d, q) ≈ p, afterpostprocess.
Requires Optimization.jl and OptimizationOptimJL.jl to be loaded (this method lives in the ConvolvedDistributionsOptimizationExt extension).
Examples
using ConvolvedDistributions, Distributions
using Optimization, OptimizationOptimJL
d = Normal(0.0, 1.0)
ConvolvedDistributions.quantile_by_optimization(d, 0.5, [0.0])See also: cdf
ConvolvedDistributions.quantile_initial_guess Function
Called as quantile_initial_guess(d, p), gives the initial_guess for quantile_by_optimization's Nelder-Mead solve, as a length-1 vector.
Each of Convolved, Difference, Product, and Ratio ships a default method. A downstream package overrides it for a more specific type to supply a domain-specific starting guess without forking the quantile machinery. The override needs the concrete parametrised component type and an explicit p::Real: Convolved's type parameters are invariant, and an untyped p is ambiguous with the generic fallback.
Validates p and throws ArgumentError for an out-of-range or NaN p before building any guess; an override should preserve that behaviour.
Examples
using ConvolvedDistributions, Distributions
d = convolved(Gamma(2.0, 1.5), Uniform(0.0, 2.0))
ConvolvedDistributions.quantile_initial_guess(
d::ConvolvedDistributions.Convolved{
Tuple{Gamma{Float64}, Uniform{Float64}}},
p::Real) = [3.0]
ConvolvedDistributions.quantile_initial_guess(d, 0.3)See also: quantile_by_optimization
ConvolvedDistributions.ratio Function
ratio(
x::Distributions.UnivariateDistribution,
y::Distributions.UnivariateDistribution;
method,
strict
) -> Ratio{X, Y, AnalyticalSolver{ConvolvedDistributions.GaussLegendre{ConvolvedDistributions._GL{Vector{Float64}, Vector{Float64}}}}} where {X<:(Distributions.UnivariateDistribution), Y<:(Distributions.UnivariateDistribution)}Create the distribution of a ratio of two independent variables.
Returns a Ratio representing convolved sums two delays, difference takes their signed gap, and product scales one by the other, a ratio divides one by the other. Y must not carry probability mass at zero (ArgumentError, naming the family, otherwise); either component may otherwise have two-sided support.
X and Y are assumed independent.
Arguments
x: The numerator distribution (theXinZ = X / Y), aUnivariateDistribution.y: The denominator distribution (theYinZ = X / Y), aUnivariateDistributionwith no probability mass at zero.
Keyword Arguments
method: The solver method, anAnalyticalSolver(the default) orNumericSolver.NumericSolverforces numeric quadrature even for an analytic pair, mirroringconvolved.strict: Whentrue, error (naming the component families) rather than silently return an object whose density/CDF would fall back to quadrature.falseby default. Seeevaluation_pathto check the route after construction instead of asserting it up front.
Returns
- A
Ratiodistribution of the quotientZ = X / Y.
Examples
using ConvolvedDistributions, Distributions
# A rate: an independent count divided by an independent exposure time.
d = ratio(Gamma(3.0, 1.0), Gamma(2.0, 1.0))
mean(d)See also
Ratio: The distribution typeconvolved: The sumdifference: The signed gapproduct: The productevaluation_path: Check the route without asserting it.
ConvolvedDistributions.ratio_ccdf Function
ratio_ccdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyratio_ccdf(d, components, z, method)The complementary CDF of components[1] / components[2] at z. See ratio_cdf.
ConvolvedDistributions.ratio_cdf Function
ratio_cdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyratio_cdf(d, components, z, method)The CDF of components[1] / components[2] at z, dispatched on the solver method method. Mirrors difference_cdf: a downstream package adds its own analytic pair by defining a method on a two-element tuple TYPE more specific than (Ratio, Tuple, Real, AnalyticalSolver).
See also: Ratio
ConvolvedDistributions.ratio_logccdf Function
ratio_logccdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyratio_logccdf(d, components, z, method)The log complementary CDF of components[1] / components[2] at z. See ratio_cdf.
ConvolvedDistributions.ratio_logcdf Function
ratio_logcdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyratio_logcdf(d, components, z, method)The log CDF of components[1] / components[2] at z. See ratio_cdf.
ConvolvedDistributions.ratio_logpdf Function
ratio_logpdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyratio_logpdf(d, components, z, method)The log density of components[1] / components[2] at z. See ratio_cdf.
ConvolvedDistributions.ratio_pair Function
ratio_pair(
x::Distributions.UnivariateDistribution,
y::Distributions.UnivariateDistribution
) -> Union{Nothing, Distributions.Cauchy}ratio_pair(x, y)The analytic distribution for x / y, or nothing when no closed form is registered for the pair. This is the extension point a downstream package adds a method to, to teach ratio a closed form for its own distribution type: dispatch (not try/catch) keeps the path differentiable under every AD backend, and returning nothing (rather than throwing) is what tells the caller to fall back to numeric quadrature instead.
Examples
using ConvolvedDistributions, Distributions
struct MyRatioDelay <: ContinuousUnivariateDistribution end
function ConvolvedDistributions.ratio_pair(::MyRatioDelay, ::MyRatioDelay)
return Cauchy(0.0, 1.0)
endSee also: convolve_pair
ConvolvedDistributions.ratio_pdf Function
ratio_pdf(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
z::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyratio_pdf(d, components, z, method)The density of components[1] / components[2] at z. See ratio_cdf.
ConvolvedDistributions.ratio_quantile Function
ratio_quantile(
d::ConvolvedDistributions.AbstractConvolvedDistribution,
components::Tuple,
p::Real,
method::ConvolvedDistributions.AbstractSolverMethod
) -> Anyratio_quantile(d, components, p, method)The quantile of components[1] / components[2] at probability p. Skeleton and AnalyticalSolver arm only, mirroring difference_quantile: the NumericSolver arm needs a nonlinear solve and lives in the ConvolvedDistributionsOptimizationExt extension.
See also: ratio_cdf
ConvolvedDistributions.uniform_window_ccdf Function
uniform_window_ccdf(
component::Distributions.UnivariateDistribution,
window::Distributions.Uniform,
x::Real,
partial_expectation,
upper_partial_expectation
) -> Anyuniform_window_ccdf(component, window, x, partial_expectation,
upper_partial_expectation)Complementary CDF of component + window at x for a Uniform window: the survival mirror of uniform_window_cdf, w the window width, and upper_partial_expectation. Adding the two forms telescopes to
Below the component's median the complement of uniform_window_cdf is returned instead: exact there, and it avoids dividing a mean-sized rounding error by a narrow w. Above it, the survival form is what keeps an upper tail meaningful — 1 - cdf cancels to zero once the survival drops below eps.
Public for the same reason uniform_window_cdf is: a downstream family supplies the two expectation closures and calls this from a convolved_ccdf method.
Arguments
component: The non-Uniformdistribution.window: TheUniformwindow distribution.x: Evaluation point.partial_expectation:t -> ∫₀ᵗ u f(u) duforcomponent.upper_partial_expectation:t -> ∫ₜ^∞ u f(u) duforcomponent.
Examples
using ConvolvedDistributions, Distributions
component = Gamma(2.0, 1.5)
ConvolvedDistributions.uniform_window_ccdf(
component, Uniform(0.0, 2.0), 30.0,
ConvolvedDistributions.partial_expectation(component),
ConvolvedDistributions.upper_partial_expectation(component))ConvolvedDistributions.uniform_window_cdf Function
uniform_window_cdf(
component::Distributions.UnivariateDistribution,
window::Distributions.Uniform,
x::Real,
partial_expectation
) -> Anyuniform_window_cdf(component, window, x, partial_expectation)CDF of component + window at x for a Uniform window, given partial_expectation(t) = ∫₀ᵗ u f(u) du for component. Derived by integration by parts: w the window width.
Public so a downstream package can add a family without re-deriving the algebra: supply partial_expectation and call this from a convolved_cdf method.
Arguments
component: The non-Uniformdistribution.window: TheUniformwindow distribution.x: Evaluation point.partial_expectation:t -> ∫₀ᵗ u f(u) duforcomponent.
Examples
using ConvolvedDistributions, Distributions
component = Gamma(2.0, 1.5)
k, θ = shape(component), scale(component)
partial_expectation = t -> k * θ * cdf(Gamma(k + 1, θ), t)
ConvolvedDistributions.uniform_window_cdf(
component, Uniform(0.0, 2.0), 3.0, partial_expectation)ConvolvedDistributions.upper_partial_expectation Function
upper_partial_expectation(component)The upper partial first moment component, as a closure over its parameters. The survival-side companion to partial_expectation that uniform_window_ccdf needs: computing it as mean(component) - partial_expectation(component)(t) would cancel to nothing in the upper tail, where both terms approach the mean.