Skip to content

EpiAware/ConvolvedDistributions.jl

Repository files navigation

ConvolvedDistributions ConvolvedDistributions logo

Documentation Build Status Code Quality License & DOI Downloads
Stable Dev Test codecov AD SciML Code Style Aqua QA JET License: MIT Downloads Downloads
ForwardDiff ReverseDiff (tape) Enzyme forward Enzyme reverse Mooncake reverse Mooncake forward
cov ForwardDiff cov ReverseDiff cov Enzyme forward cov Enzyme reverse cov Mooncake reverse cov Mooncake forward

Raw-distribution convolution and shared numeric quadrature for any Distributions.jl distribution.

Why ConvolvedDistributions?

  • Convolution of any pair: convolved builds the distribution of a sum of independent delays (a convolution) from any two or more Distributions.jl distributions, not just pairs with a closed form.
  • Analytic fast path: closed-form convolutions (Normal + Normal, equal-scale Gamma, equal-rate Exponential) are used where they exist, with an AD-safe Gauss-Legendre quadrature fallback for every other pair.
  • Differences and products as well as sums: difference builds the X - Y dual, the signed gap between two independent events, and product the X * Y Mellin convolution for non-negative components, a delay scaled by an independent multiplicative factor (LogNormal * LogNormal closed form, quadrature otherwise).
  • Timeseries convolution: convolve_series convolves a numeric series (e.g. expected infections over time) with a delay PMF on the unit lag grid to give expected downstream counts, the renewal-style observation layer. A discrete delay is read straight off its own PMF; a continuous delay is discretised first with discretise_pmf (interval-censored secondary, exact primary) or CensoredDistributions.jl's double-interval-censored masses. Gradients flow through the delay parameters.
  • Pluggable integration: a shared integrate / gl_integrate layer with a lightweight fixed-node GaussLegendre default and an optional Integrals.jl backend.
  • Gradients everywhere: gradients flow through the component parameters on every supported AD backend (ForwardDiff, ReverseDiff, Enzyme, Mooncake), so convolved distributions can be fitted with gradient-based samplers and optimisers.

Getting started

See the Getting started documentation for a full walkthrough.

The following example convolves two delays, an incubation period and a reporting delay, and evaluates the resulting distribution:

using ConvolvedDistributions, Distributions

# Sum of two independent delays
incubation = Gamma(2.0, 1.0)
reporting = LogNormal(1.0, 0.5)
d = convolved(incubation, reporting)

(cdf(d, 5.0), pdf(d, 5.0))

difference gives the signed gap between two independent events, for example the delay between two reporting streams:

z = difference(Normal(5.0, 1.0), Normal(2.0, 1.0))

(mean(z), cdf(z, 0.0))

A Convolved distribution is a UnivariateDistribution, so it composes with Distributions.truncated. Right truncation is useful when scoring against data observed only up to a cutoff:

d_trunc = truncated(d; upper = 10.0)

cdf(d_trunc, 5.0)

Loading the Optimization extension adds quantile support by numerically inverting the CDF:

using Optimization, OptimizationOptimJL

quantile(d, 0.5)

The components and their sum can be compared visually, here with AlgebraOfGraphics.jl:

using CairoMakie, AlgebraOfGraphics, DataFramesMeta

CairoMakie.activate!(type = "png", px_per_unit = 2)

x = 0.0:0.1:15.0
df = vcat(
    DataFrame(x = x, density = pdf.(incubation, x),
        Distribution = "Incubation (Gamma)"),
    DataFrame(x = x, density = pdf.(reporting, x),
        Distribution = "Reporting (LogNormal)"),
    DataFrame(x = x, density = pdf(d, collect(x)),
        Distribution = "Convolved sum")
)
draw(
    data(df) *
    mapping(:x, :density, color = :Distribution) *
    visual(Lines, linewidth = 2)
)

Relationship to Distributions.jl

Distributions.jl ships a convolve function, but it only covers pairs with a closed-form result:

Aspect Distributions.jl convolve ConvolvedDistributions.jl convolved
Coverage Closed-form, same-family pairs only (e.g. Normal + Normal, equal-scale Gamma); errors otherwise Any pair of univariate distributions
Method Returns the closed-form distribution Analytic fast path where a closed form exists, AD-safe Gauss-Legendre quadrature fallback otherwise
Forms Two positional arguments Nested, vector, tuple, and varargs forms for sums of many delays
Differences Not supported difference builds the X - Y dual
Evaluation Whatever the returned distribution supports Batched cdf / pdf / logpdf over vectors of points
Gradients Depend on the returned distribution Flow through the component parameters on all supported AD backends

For example, Distributions.convolve(Gamma(2, 1), LogNormal(0, 1)) throws a MethodError and Distributions.convolve(Gamma(2, 1), Gamma(3, 2)) throws an ArgumentError because the scales differ, whereas convolved handles both via quadrature. When a closed form does exist, convolved uses it, so there is no cost to reaching for the more general function.

What packages work well with ConvolvedDistributions.jl?

  • Distributions.jl provides the base functionality for working with distributions; every component and every result is a UnivariateDistribution.
  • CensoredDistributions.jl adds censoring and truncation layers for epidemiological observation processes; this package was split out of it and the two compose.
  • Turing.jl for Bayesian inference; the cdf / pdf / logpdf methods are AD-safe, so convolved distributions can be fitted with its samplers.
  • Integrals.jl as an optional quadrature backend via the package extension.

Where to learn more

Contributing

We welcome contributions and new contributors! This package follows ColPrac and the SciML style.

Supporting and citing

If you would like to support ConvolvedDistributions, please star the repository — such metrics help secure future funding.

If you use ConvolvedDistributions in your work, please cite it:

@software{ConvolvedDistributions_jl,
  author       = {Sam Abbott and EpiAware contributors},
  title        = {ConvolvedDistributions.jl},
  year         = {2026},
  url          = {https://github.com/EpiAware/ConvolvedDistributions.jl}
}

A citable DOI will be added with the first tagged release.

Code of conduct

Please note that the ConvolvedDistributions project is released with a Contributor Code of Conduct. By contributing, you agree to abide by its terms.

About

Resources

License

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages