From d6d4a34e34d2ba015c428dc941a2419ff6b42a91 Mon Sep 17 00:00:00 2001 From: seabbs-bot Date: Sat, 18 Jul 2026 18:22:05 +0100 Subject: [PATCH 1/7] feat: add DistributionsInference.jl to the ecosystem inventory Also refreshes LoweredDistributions.jl's description to its current phase-type/CTMC lowering scope (it previously undersold it as "nothing to install yet"), and bumps ConvolvedDistributions.jl and EpiAwareADTools.jl to the maturing status pill now both are registered in General. Co-authored-by: Sam Abbott --- packages/index.qmd | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/index.qmd b/packages/index.qmd index a3edefa..cf16e30 100644 --- a/packages/index.qmd +++ b/packages/index.qmd @@ -60,7 +60,7 @@ Status reflects where a package sits in its release cycle rather than a judgemen ConvolvedDistributions.jl - experimental + maturing Convolve probability distributions, with shared numerical quadrature. @@ -74,15 +74,23 @@ Status reflects where a package sits in its release cycle rather than a judgemen - LoweredDistributions.jl + LoweredDistributions.jl - in development + experimental + + Lower a distribution, or a whole composed event tree, onto a backend-agnostic dynamical-systems representation (a phase-type chain or a CTMC), then hand it to Catalyst, SciML, AlgebraicPetri, or JumpProcesses. + + + + DistributionsInference.jl + + experimental - Common components for differential equation infectious disease models. In development, so there is nothing to install yet. + A PPL-neutral fit protocol and log-density engine: any object that names its own parameters becomes fittable, with Turing.jl, Bijectors, and FlexiChains readback layered on as extensions rather than requirements. - EpiAwarePackageTools.jl + EpiAwarePackageTools.jl experimental @@ -90,9 +98,9 @@ Status reflects where a package sits in its release cycle rather than a judgemen - EpiAwareADTools.jl + EpiAwareADTools.jl - experimental + maturing Automatic differentiation safety machinery for the stack, tested across ForwardDiff, ReverseDiff, Mooncake, and Enzyme. Deliberately framed as fixes we host while we try to fix things upstream. From d7b73f2cbf189acca7e6d879c4ad3d64e276bb09 Mon Sep 17 00:00:00 2001 From: seabbs-bot Date: Sat, 18 Jul 2026 18:22:55 +0100 Subject: [PATCH 2/7] docs: move the composed-distributions approach page to current syntax Demonstrate @uncertain's natural syntax in the hero example, document uncertain(tree, ...) as the promotion verb over an existing tree, the update(tree, table)/update(tree, chain) split, and Distributions.jl's own truncated()/censored() working as ordinary leaves. Add LoweredDistributions.jl and DistributionsInference.jl to "Where next", since the page already discusses lowering and fitting without naming either package. Co-authored-by: Sam Abbott --- approaches/composed-distributions.qmd | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/approaches/composed-distributions.qmd b/approaches/composed-distributions.qmd index 12e70a6..c33e50f 100644 --- a/approaches/composed-distributions.qmd +++ b/approaches/composed-distributions.qmd @@ -79,9 +79,9 @@ using ComposedDistributions, Distributions # BDBV-inspired delay tree: parallel pathways # with sequential, resolve, and compete nested together -tree = compose(( +tree = @uncertain compose(( clinical = sequential( - :onset_admit => Gamma(1.2, 3.0), + :onset_admit => Gamma(Normal(1.2, 0.2), 3.0), :admit_resolve => resolve( :death => (Gamma(2.0, 3.5), 0.3), :discharge => (Gamma(1.0, 8.0), 0.7))), @@ -121,10 +121,10 @@ Both share one internal representation, so the model you build to simulate is th The approach is built in four layers, each resting on the one before. -- **Leaves** are any Distributions.jl `UnivariateDistribution`, used directly as the per-event delays. +- **Leaves** are any Distributions.jl `UnivariateDistribution`, used directly as the per-event delays, including Distributions.jl's own `truncated(...)` and `censored(...)` wrappers — a leaf can be truncated or censored, but wrapping a whole tree the same way errors by design, since the composers read a wrapper's structure to peel through to the free delay underneath. - **Composers** wire named leaves into an event tree. - **Combination and lowering** join or collapse whole delays with `convolved`, `difference`, and `observed_distribution`. -- **Parameters and edits** read and reshape an assembled tree with `params_table`, `param_priors`, `update`, `prune`, and `splice`. +- **Parameters and edits** read and reshape an assembled tree with `params_table`, `param_priors`, `uncertain`, `update`, `prune`, and `splice`. ### Five composers @@ -151,15 +151,17 @@ The model does not change, only the delays each case supplies. Real line-list delays are rarely clean, and the corrections are part of the model rather than a preprocessing step. [CensoredDistributions.jl](https://censoreddistributions.epiaware.org/) supplies censoring leaves for primary-event censoring, interval censoring, and truncation. -[ModifiedDistributions.jl](https://modifieddistributions.epiaware.org/) supplies modifier leaves that rescale, weight, or transform a delay. +[ModifiedDistributions.jl](https://modifieddistributions.epiaware.org/dev/) supplies modifier leaves that rescale, weight, or transform a delay. Both are leaves like any other, so they slot into the tree and the composers peel through them to reach the free delay underneath. ### Uncertainty in the parameters themselves A delay taken from the literature rarely comes with exact parameters. -`uncertain` wraps a leaf whose parameters are themselves distributions, nested to any depth. +`uncertain` wraps a leaf whose parameters are themselves distributions, nested to any depth, and the `@uncertain` macro reads a distribution passed as a constructor argument as that parameter's prior, so `@uncertain Gamma(Normal(2.0, 0.3), 1.0)` reads the same as writing it out by hand. The result is still a univariate distribution, so it composes as a leaf everywhere, and `has_uncertain` guards against forgetting to pin it down before fitting. +`uncertain` is also the verb for promoting parameters on a tree already built: `uncertain(tree; onset_admit = (shape = prior,))` targets one parameter, and bare `uncertain(tree)` promotes every free parameter with a default prior in one call. + ### One representation across the workflow The tree is not tied to one package or one use. @@ -169,7 +171,8 @@ There is no translation step between the delay model, the infection model, and t ### Read the posterior back through the same tree After fitting, the posterior is read back by applying the parameter draws to the same tree. -`update(template, chain)` walks each draw through the tree and returns a distribution per draw, so every delay mean, convolution, or contrast comes from the fitted object itself. +`update(template, chain)` reduces the chain to a point summary and returns one fitted tree; `update(template, table)` does the same from a Tables.jl table of values or priors, for a bulk edit outside a sampling loop. +Reading every draw rather than a point summary maps `update` over `param_draws(template, chain)`, so every delay mean, convolution, or contrast still comes from the fitted object itself, one draw at a time. Nothing is recomputed in separate post-processing that could drift from the model. ## Where next @@ -179,13 +182,20 @@ Nothing is recomputed in separate post-processing that could drift from the mode [composing distributions](https://composeddistributions.epiaware.org/dev/getting-started/tutorials/composing-distributions). - The [ComposedDistributions.jl documentation](https://composeddistributions.epiaware.org/dev/) covers the full API and worked examples. -- [ConvolvedDistributions.jl](https://convolveddistributions.epiaware.org/) handles +- [ConvolvedDistributions.jl](https://convolveddistributions.epiaware.org/dev/) handles the convolutions (sums) of delays that arise naturally in event trees. -- [ModifiedDistributions.jl](https://modifieddistributions.epiaware.org/) lets you +- [ModifiedDistributions.jl](https://modifieddistributions.epiaware.org/dev/) lets you rescale, weight, or otherwise transform individual leaves without changing the tree structure. - [CensoredDistributions.jl](https://censoreddistributions.epiaware.org/) adds the censoring layer for real line-list data. +- [LoweredDistributions.jl](https://lowereddistributions.epiaware.org/dev/) lowers + the same tree onto a phase-type chain or a CTMC, the bridge to compartmental + models. +- Fitting outside Turing, or fitting a plain struct alongside a tree? + [DistributionsInference.jl](https://github.com/EpiAware/DistributionsInference.jl) + hosts the PPL-neutral log-density and readback that `as_logdensity` and + `as_turing` build on. - Ready to fit a full model with infection dynamics? See the [composable Turing models approach](composable-turing-models.qmd). From 68621e64f1327797245290ec8f8c09bf5bd51d19 Mon Sep 17 00:00:00 2001 From: seabbs-bot Date: Sat, 18 Jul 2026 18:23:57 +0100 Subject: [PATCH 3/7] docs: show @uncertain's natural syntax in the homepage hero example Keeps the homepage's code block in step with the identical one on the composed-distributions approach page (they have always been updated together), so both now demonstrate the same current syntax rather than a tree with no uncertain leaves. Co-authored-by: Sam Abbott --- index.qmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.qmd b/index.qmd index 470da64..3e7a241 100644 --- a/index.qmd +++ b/index.qmd @@ -146,9 +146,9 @@ using ComposedDistributions, Distributions # BDBV-inspired delay tree: parallel pathways # with sequential, resolve, and compete nested together -tree = compose(( +tree = @uncertain compose(( clinical = sequential( - :onset_admit => Gamma(1.2, 3.0), + :onset_admit => Gamma(Normal(1.2, 0.2), 3.0), :admit_resolve => resolve( :death => (Gamma(2.0, 3.5), 0.3), :discharge => (Gamma(1.0, 8.0), 0.7))), From 376cceffda8e2a414f8874ad10b739eca68a0090 Mon Sep 17 00:00:00 2001 From: seabbs-bot Date: Sat, 18 Jul 2026 18:24:22 +0100 Subject: [PATCH 4/7] fix: route remaining GitHub Discussions mentions to the forum faq.qmd and get-involved.qmd still pointed at package GitHub Discussions, which the earlier help-routing fix (#21) missed since it only touched a different page. Discussions are disabled ecosystem-wide; support routes through the epinowcast forum and package issues. Also points EpiAwarePackageTools.jl's developer.qmd link at /dev/, matching how every other unreleased package is linked on this site. Co-authored-by: Sam Abbott --- developer.qmd | 2 +- faq.qmd | 2 +- get-involved.qmd | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/developer.qmd b/developer.qmd index 60920ee..3132ae0 100644 --- a/developer.qmd +++ b/developer.qmd @@ -41,7 +41,7 @@ Packages run a consistent set of quality tools: - [JET.jl](https://github.com/aviatesk/JET.jl) for static analysis (`@report_opt`, `@report_package`) - Doctests and formatting checks -[EpiAwarePackageTools.jl](https://epiawarepackagetools.epiaware.org/) provides the shared +[EpiAwarePackageTools.jl](https://epiawarepackagetools.epiaware.org/dev/) provides the shared harnesses, including automatic-differentiation tests across backends. ## Checking performance diff --git a/faq.qmd b/faq.qmd index 2b37afe..b96735c 100644 --- a/faq.qmd +++ b/faq.qmd @@ -54,7 +54,7 @@ version. Package-specific citation details live in each package's documentation. ## Where do I get help or report a bug? -- Questions: the [epinowcast forum](https://community.epinowcast.org/) or a package's GitHub Discussions +- Questions: the [epinowcast forum](https://community.epinowcast.org/) - Bugs and feature requests: open an issue on the relevant GitHub repository - General Julia help: [Julia Discourse](https://discourse.julialang.org/) and [Slack](https://julialang.org/slack/) diff --git a/get-involved.qmd b/get-involved.qmd index c7d4813..c826dca 100644 --- a/get-involved.qmd +++ b/get-involved.qmd @@ -12,7 +12,8 @@ ecosystem, there is a place for you here. - **Discuss.** Join the [epinowcast forum](https://community.epinowcast.org/) to ask questions and talk through ideas — the best first step. - **Try the packages.** Use them on a real problem and tell us what worked and - what didn't, via a package's GitHub Discussions or issues. + what didn't, via the [forum](https://community.epinowcast.org/) or a + package's GitHub issues. - **Contribute.** Fix a bug, improve documentation, or add a feature. See the [Contributing](contributing.qmd) guide. - **Bring a use case.** If you have a modelling problem that composable tools From 255b824110fae142076d167e4075047f833124f6 Mon Sep 17 00:00:00 2001 From: seabbs-bot Date: Sat, 18 Jul 2026 18:24:50 +0100 Subject: [PATCH 5/7] fix: point CensoredDistributions tutorial links at /stable/ CensoredDistributions.jl is released and its GitHub Pages deploys a stable/ folder (verified against the gh-pages branch directly, all four tutorial paths present), so these should not point at /dev/. Co-authored-by: Sam Abbott --- tutorials/index.qmd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/index.qmd b/tutorials/index.qmd index bfd0798..d5cd4bc 100644 --- a/tutorials/index.qmd +++ b/tutorials/index.qmd @@ -109,25 +109,25 @@ If you are new to an approach, start with the tutorial marked *start here*. - Fitting with Turing + Fitting with Turing Estimating a delay distribution from censored, truncated line-list data. CensoredDistributions - Analytical primary-censored CDFs + Analytical primary-censored CDFs Closed-form primary-event censoring, and when it beats numerical integration. CensoredDistributions - Exponentially tilted primary events + Exponentially tilted primary events Primary events that are not uniform within their window, as in a growing epidemic. CensoredDistributions - Automatic differentiation backends + Automatic differentiation backends Which AD backend to use for censored delay likelihoods, and what it costs. CensoredDistributions From 8b6e977cf2c3f4e9365215eca3bcac7242994c21 Mon Sep 17 00:00:00 2001 From: seabbs-bot Date: Sat, 18 Jul 2026 18:25:22 +0100 Subject: [PATCH 6/7] docs: fix README's stale description of the site structure packages/_template.qmd and docs.qmd/EPIAWARE_DOCS no longer exist (packages/index.qmd is now the single filterable inventory, and tutorials/index.qmd the single filterable tutorial listing), but the README still described the old per-package-page structure. Co-authored-by: Sam Abbott --- README.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index dc97f3b..cc439f0 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,10 @@ Source for the [EpiAware](https://github.com/EpiAware) organisation website — the meta-site for a composable Julia ecosystem for infectious disease modelling. -The site links out to the ecosystem's packages, wraps their documentation in a -single browser, and hosts org-level material (using Julia, FAQ, contributing, -developer, funding, getting involved) alongside a gallery and team page. +The site is the ecosystem's shared front door: an inventory of every package +linking out to its own documentation, the approaches that tie them together, +and org-level material (using Julia, FAQ, contributing, developer, funding, +getting involved) alongside a gallery and team page. ## Build @@ -22,19 +23,23 @@ Pushes to `main` deploy it to GitHub Pages. ## Adding a package -Copy `packages/_template.qmd` to `packages/.qmd`, fill in the front -matter and body, and open a pull request. The card on the -[Packages](https://epiaware.github.io/packages/) page is generated from the file, -and adding the package to the docs browser is a one-line entry in the -`EPIAWARE_DOCS` list in `docs.qmd`. +Add a row to the table in `packages/index.qmd`: package name linking to its +documentation, a GitHub icon linking to its source, a status pill, a +`data-category` (`modelling`, `distributions`, or `infrastructure`) so it +picks up the category filter, and a one-sentence description. Open a pull +request. ## Structure - `index.qmd` — home -- `packages/` — one page per package, plus the generated listing and a template -- `docs.qmd` — unified documentation browser -- `tutorials.qmd`, `gallery.qmd`, `team.qmd`, `funding.qmd`, `get-involved.qmd`, `community.qmd` +- `packages/index.qmd` — the ecosystem inventory (one filterable table) +- `approaches/` — the modelling approaches (composed distributions, composable + Turing models) and their shared overview +- `tutorials/index.qmd` — a filterable listing of runnable tutorials, each + living in its own package's documentation +- `gallery.qmd`, `team.qmd`, `funding.qmd`, `get-involved.qmd`, `community.qmd` - `using-julia.qmd`, `faq.qmd`, `contributing.qmd`, `developer.qmd` — org-level guides +- `news/` — blog posts - `theme-light.scss`, `theme-dark.scss`, `styles.css` — theme - `assets/img/logo.svg` — org logo From 838294b1c12ad73d9b5e2ea9306035ac3cc45aa4 Mon Sep 17 00:00:00 2001 From: seabbs-bot Date: Sat, 18 Jul 2026 18:28:46 +0100 Subject: [PATCH 7/7] docs: name distribution_priors/distribution_params in the DI mention The brief specifically asked for distribution_params to be shown as the chain-readback verb; the DistributionsInference bullet named as_logdensity/as_turing but not the two readback functions. Co-authored-by: Sam Abbott --- approaches/composed-distributions.qmd | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/approaches/composed-distributions.qmd b/approaches/composed-distributions.qmd index c33e50f..fb53133 100644 --- a/approaches/composed-distributions.qmd +++ b/approaches/composed-distributions.qmd @@ -195,7 +195,10 @@ Nothing is recomputed in separate post-processing that could drift from the mode - Fitting outside Turing, or fitting a plain struct alongside a tree? [DistributionsInference.jl](https://github.com/EpiAware/DistributionsInference.jl) hosts the PPL-neutral log-density and readback that `as_logdensity` and - `as_turing` build on. + `as_turing` build on: `distribution_priors` assembles priors the same way + `param_priors` does for a tree, and `distribution_params` reads a fitted + chain back to the estimated values by name, whatever fittable object it + came from. - Ready to fit a full model with infection dynamics? See the [composable Turing models approach](composable-turing-models.qmd).