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 diff --git a/approaches/composed-distributions.qmd b/approaches/composed-distributions.qmd index 12e70a6..fb53133 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,23 @@ 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: `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). 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 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))), 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. 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