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?

  • A convolution is usually only available in closed form for a few matching distribution families; convolved builds the distribution of a sum of independent delays from any two or more Distributions.jl distributions, not just those pairs.
  • 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.
  • Sums are not the only combination we support: difference builds the X - Y signed gap between two independent events, and product the X * Y Mellin convolution for a delay scaled by an independent multiplicative factor.
  • Turning expected events into expected downstream counts is usually a hand-rolled discrete convolution; convolve_series does it directly from a numeric series and a delay's PMF.

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

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 gap between two independent events:

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.

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,

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

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.

Related packages

  • ComposedDistributions.jl re-exports this package directly, so a composed chain collapses to its convolved total via observed_distribution.
  • ModifiedDistributions.jl applies its forward-series transforms (thin, cumulative) to a convolved series, and lets modified distributions serve as convolution components.
  • LoweredDistributions.jl lowers a convolved sum to one combined dynamical-systems representation, folding the components' phase-types in series rather than lowering each separately.
  • CensoredDistributions.jl adds censoring and truncation layers for epidemiological observation processes; this package was split out of it, and convolve_series reads its double-interval-censored masses directly.
  • DistributionsInference.jl is the emerging fit-protocol and PPL-integration layer across the EpiAware distribution packages.

Where to learn more

Getting help

For usage questions, ask on the Julia Discourse or the epinowcast community forum, our home for epidemiological modelling questions. Please use GitHub issues for bug reports and feature requests only.

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

Packages

 
 
 

Contributors

Languages