diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 9a7542473..f2ec7b3bf 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.1","generation_timestamp":"2024-03-06T11:26:57","documenter_version":"1.3.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.2","generation_timestamp":"2024-03-06T13:50:54","documenter_version":"1.3.0"}} \ No newline at end of file diff --git a/dev/assets/documenter.js b/dev/assets/documenter.js index c6562b558..8a829d865 100644 --- a/dev/assets/documenter.js +++ b/dev/assets/documenter.js @@ -4,10 +4,8 @@ requirejs.config({ 'highlight-julia': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/julia.min', 'headroom': 'https://cdnjs.cloudflare.com/ajax/libs/headroom/0.12.0/headroom.min', 'jqueryui': 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min', - 'katex-auto-render': 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/contrib/auto-render.min', 'jquery': 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min', 'headroom-jquery': 'https://cdnjs.cloudflare.com/ajax/libs/headroom/0.12.0/jQuery.headroom.min', - 'katex': 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min', 'highlight': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min', 'highlight-julia-repl': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/julia-repl.min', }, @@ -17,11 +15,6 @@ requirejs.config({ "highlight" ] }, - "katex-auto-render": { - "deps": [ - "katex" - ] - }, "headroom-jquery": { "deps": [ "jquery", @@ -36,32 +29,39 @@ requirejs.config({ } }); //////////////////////////////////////////////////////////////////////////////// -require(['jquery', 'katex', 'katex-auto-render'], function($, katex, renderMathInElement) { -$(document).ready(function() { - renderMathInElement( - document.body, - { - "delimiters": [ - { - "left": "$", - "right": "$", - "display": false - }, - { - "left": "$$", - "right": "$$", - "display": true - }, - { - "left": "\\[", - "right": "\\]", - "display": true - } - ] +require([], function() { +window.MathJax = { + "tex": { + "packages": [ + "base", + "ams", + "autoload" + ], + "inlineMath": [ + [ + "$", + "$" + ], + [ + "\\(", + "\\)" + ] + ], + "tags": "ams" + }, + "options": { + "ignoreHtmlClass": "tex2jax_ignore", + "processHtmlClass": "tex2jax_process" + } } - - ); -}) +; + +(function () { + var script = document.createElement('script'); + script.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js'; + script.async = true; + document.head.appendChild(script); +})(); }) //////////////////////////////////////////////////////////////////////////////// diff --git a/dev/checklist/index.html b/dev/checklist/index.html index b10cb7e16..38e1b8fa3 100644 --- a/dev/checklist/index.html +++ b/dev/checklist/index.html @@ -1,5 +1,5 @@ -Release checklist · EpiAware.jl

Checklists

The purpose of this page is to collate a series of checklists for commonly performed changes to the source code of EpiAware. It has been adapted from Documenter.jl.

In each case, copy the checklist into the description of the pull request.

Making a release

In preparation for a release, use the following checklist. These steps should be performed on a branch with an open pull request, either for a topic branch, or for a new branch release-1.y.z ("Release version 1.y.z") if multiple changes have accumulated on the master branch since the last release.

## Pre-release
+Release checklist · EpiAware.jl

Checklists

The purpose of this page is to collate a series of checklists for commonly performed changes to the source code of EpiAware. It has been adapted from Documenter.jl.

In each case, copy the checklist into the description of the pull request.

Making a release

In preparation for a release, use the following checklist. These steps should be performed on a branch with an open pull request, either for a topic branch, or for a new branch release-1.y.z ("Release version 1.y.z") if multiple changes have accumulated on the master branch since the last release.

## Pre-release
 
  - [ ] Change the version number in `Project.toml`
    * If the release is breaking, increment MAJOR
@@ -20,4 +20,4 @@
 
    Either of those should automatically publish a new version to the Julia registry.
  - Once registered, the `TagBot.yml` workflow should create a tag, and rebuild the documentation for this tag.
- - These steps can take quite a bit of time (1 hour or more), so don't be surprised if the new documentation takes a while to appear.
+ - These steps can take quite a bit of time (1 hour or more), so don't be surprised if the new documentation takes a while to appear.
diff --git a/dev/contributing/index.html b/dev/contributing/index.html index 2b356f4f3..4026b1261 100644 --- a/dev/contributing/index.html +++ b/dev/contributing/index.html @@ -1,2 +1,2 @@ -Contributing · EpiAware.jl

Contributing

This page details the some of the guidelines that should be followed when contributing to this package. It is adapted from Documenter.jl.

Branches

release-* branches are used for tagged minor versions of this package. This follows the same approach used in the main Julia repository, albeit on a much more modest scale.

Please open pull requests against the master branch rather than any of the release-* branches whenever possible.

Backports

Bug fixes are backported to the release-* branches using git cherry-pick -x by a EpiAware member and will become available in point releases of that particular minor version of the package.

Feel free to nominate commits that should be backported by opening an issue. Requests for new point releases to be tagged in METADATA.jl can also be made in the same way.

release-* branches

  • Each new minor version x.y.0 gets a branch called release-x.y (a protected branch).
  • New versions are usually tagged only from the release-x.y branches.
  • For patch releases, changes get backported to the release-x.y branch via a single PR with the standard name "Backports for x.y.z" and label "Type: Backport". The PR message links to all the PRs that are providing commits to the backport. The PR gets merged as a merge commit (i.e. not squashed).
  • The old release-* branches may be removed once they have outlived their usefulness.
  • Patch version milestones are used to keep track of which PRs get backported etc.

Style Guide

Follow the style of the surrounding text when making changes. When adding new features please try to stick to the following points whenever applicable. This project follows the SciML style guide.

Tests

Unit tests

As is conventional for Julia packages, unit tests are located at test/*.jl with the entrypoint test/runtests.jl.

End to end testing

Tests that build example package docs from source and inspect the results (end to end tests) are located in /test/examples. The main entry points are test/examples/make.jl for building and test/examples/test.jl for doing some basic checks on the generated outputs.

+Contributing · EpiAware.jl

Contributing

This page details the some of the guidelines that should be followed when contributing to this package. It is adapted from Documenter.jl.

Branches

release-* branches are used for tagged minor versions of this package. This follows the same approach used in the main Julia repository, albeit on a much more modest scale.

Please open pull requests against the master branch rather than any of the release-* branches whenever possible.

Backports

Bug fixes are backported to the release-* branches using git cherry-pick -x by a EpiAware member and will become available in point releases of that particular minor version of the package.

Feel free to nominate commits that should be backported by opening an issue. Requests for new point releases to be tagged in METADATA.jl can also be made in the same way.

release-* branches

  • Each new minor version x.y.0 gets a branch called release-x.y (a protected branch).
  • New versions are usually tagged only from the release-x.y branches.
  • For patch releases, changes get backported to the release-x.y branch via a single PR with the standard name "Backports for x.y.z" and label "Type: Backport". The PR message links to all the PRs that are providing commits to the backport. The PR gets merged as a merge commit (i.e. not squashed).
  • The old release-* branches may be removed once they have outlived their usefulness.
  • Patch version milestones are used to keep track of which PRs get backported etc.

Style Guide

Follow the style of the surrounding text when making changes. When adding new features please try to stick to the following points whenever applicable. This project follows the SciML style guide.

Tests

Unit tests

As is conventional for Julia packages, unit tests are located at test/*.jl with the entrypoint test/runtests.jl.

End to end testing

Tests that build example package docs from source and inspect the results (end to end tests) are located in /test/examples. The main entry points are test/examples/make.jl for building and test/examples/test.jl for doing some basic checks on the generated outputs.

diff --git a/dev/examples/getting_started.jl b/dev/examples/getting_started.jl new file mode 100644 index 000000000..dafc99039 --- /dev/null +++ b/dev/examples/getting_started.jl @@ -0,0 +1,390 @@ +### A Pluto.jl notebook ### +# v0.19.40 + +using Markdown +using InteractiveUtils + +# ╔═╡ c593a2a0-d7f5-11ee-0931-d9f65ae84a72 +# hideall +let + docs_dir = dirname(dirname(@__DIR__)) + pkg_dir = dirname(docs_dir) + + using Pkg: Pkg + Pkg.activate(docs_dir) + Pkg.develop(; path = pkg_dir) + Pkg.instantiate() +end; + +# ╔═╡ da479d8d-1312-4b98-b0af-5be52dffaf3f +begin + using EpiAware + using Turing + using Distributions + using StatsPlots + using Random + using DynamicPPL + using Statistics + using DataFramesMeta + using LinearAlgebra +end + +# ╔═╡ 3ebc8384-f73d-4597-83a7-07a3744fed61 +md" +# Getting stated with `EpiAware` + +This tutorial introduces the basic functionality of `EpiAware`. `EpiAware` is a package for making inferences on epidemiological case/determined infection data using a model-based approach. + +## `EpiAware` models +The models we consider are discrete-time $t = 1,\dots, T$ with a latent random process, $Z_t$ generating stochasticity in the number of new infections $I_t$ at each time step. Observations are treated as downstream random variables determined by the actual infections and a model of infection to observation delay. + +#### Mathematical definition +```math +\begin{align} +Z_\cdot &\sim \mathcal{P}(\mathbb{R}^T) | \theta_Z, \\ +I_0 &\sim f_0(\theta_I), \\ +I_t &\sim g_I(\{I_s, Z_s\}_{s < t}, \theta_{I}), \\ +y_t &\sim f_O(\{I_s\}_{s \leq t}, \theta_{O}). +\end{align} +``` +Where, $\mathcal{P}(\mathbb{R}^T) | \theta_Z$ is a parametric process on $\mathbb{R}^T$. $f_0$ and $f_O$ are parametric distributions on, respectively, the initial number of infections and the observed case data conditional on underlying infections. $g_I$ is distribution of new infections conditional on infections and latent process in the past. Note that we assume that new infections are conditional on the strict past, whereas new observations can depend on infections on the same time step. + +#### Code structure outline + +An `EpiAware` model in code is created from three modular components: + +- A `LatentModel`: This defines the model for $Z_\cdot$. This chooses $\mathcal{P}(\mathbb{R}^T) | \theta_Z$. +- An `EpiModel`: This defines a generative process for infections conditional on the latent process. This chooses $f_0(\theta_I)$, and $g_I(\{I_s, Z_s\}_{s < t}, \theta_{I})$. +- An `ObservationModel`: This defines the observation model. This chooses $f_O({I_s}_{s \leq t}, \theta_{O})$ + +#### Reproductive number +`EpiAware` models do not need to specify a time-varying reproductive number $\mathcal{R}_t$ to generate $I_\cdot$, however, this is often a quantity of interest. When not directly used we will typically back-calculate $\mathcal{R}_t$ from the generated infections: + +```math +\mathcal{R}_t = {I_t \over \sum_{s \geq 1} g_s I_{t-s} }. +``` + +Where $g_s$ is a discrete generation interval. For this reason, even when not using a reproductive number approach directly, we ask for a generation interval. +" + +# ╔═╡ 5a0d5ab8-e985-4126-a1ac-58fe08beee38 +md" +## Random walk `LatentModel` + +As an example, we choose the latent process as a random walk with parameters $\theta_Z$: + +- ``Z_0``: Initial position. +- ``\sigma^2_{Z}``: The step-size variance. + +Conditional on the parameters the random walk is then generated by white noise: +```math +\begin{align} +Z_t &= Z_0 + \sigma_{RW} \sum_{t= 1}^T \epsilon_t, \\ +\epsilon_t &\sim \mathcal{N}(0,1). +\end{align} +``` + +In `EpiAware` we provide a constructor for random walk latent models with priors for $\theta_Z$. We choose priors, +```math +\begin{align} +Z_0 &\sim \mathcal{N}(0,1),\\ +\sigma^2_Z &\sim \text{HalfNormal}(0.01). +\end{align} +``` +" + +# ╔═╡ 56ae496b-0094-460b-89cb-526627991717 +rwp = EpiAware.RandomWalk(Normal(), + truncated(Normal(0.0, 0.02), 0.0, Inf)) + +# ╔═╡ 767beffd-1ef5-4e6c-9ac6-edb52e60fb44 +md" +## Direct infection `EpiModel` + +This is a simple model where the unobserved log-infections are directly generated by the latent process $Z$. +```math +\log I_t = \log I_0 + Z_t. +``` + +As discussed above, we still ask for a defined generation interval, which can be used to calculate $\mathcal{R}_t$. + +" + +# ╔═╡ 9e43cbe3-94de-44fc-a788-b9c7adb34218 +truth_GI = Gamma(2, 5) + +# ╔═╡ f067284f-a1a6-44a6-9b79-f8c2de447673 +md" +The `EpiData` constructor performs double interval censoring to convert our _continuous_ estimate of the generation interval into a discretized version. We also implement right truncation using the keyword `D_gen`. +" + +# ╔═╡ c0662d48-4b54-4b6d-8c91-ddf4b0e3aa43 +model_data = EpiData(truth_GI, D_gen = 10.0) + +# ╔═╡ fd72094f-1b95-4d07-a8b0-ef47dc560dfc +md" +We can supply a prior for the initial log_infections. +" + +# ╔═╡ 6639e66f-7725-4976-81b2-6472419d1a62 +log_I0_prior = Normal(log(100.0), 1.0) + +# ╔═╡ df5e59f8-3185-4bed-9cca-7c266df17cec +md" +And construct the `EpiModel`. +" + +# ╔═╡ 6fbdd8e6-2323-4352-9185-1f31a9cf9012 +epi_model = DirectInfections(model_data, log_I0_prior) + +# ╔═╡ 5e62a50a-71f4-4902-b1c9-fdf51fe145fa +md" + + +### Delayed Observations `ObservationModel` + +The observation model is a negative binomial distribution with mean `μ` and cluster factor `1 / r`. Delays are implemented +as the action of a sparse kernel on the infections $I(t)$. + +```math +\begin{align} +y_t &\sim \text{NegBinomial}(\mu = \sum_{s\geq 0} K[t, t-s] I(s), r), \\ +1 / r &\sim \text{Gamma}(3, 0.05/3). +\end{align} +``` +" + +# ╔═╡ e813d547-6100-4c43-b84c-8cebe306bda8 +md" +We also set up the inference to occur over 100 days. +" + +# ╔═╡ c7580ae6-0db5-448e-8b20-4dd6fcdb1ae0 +time_horizon = 100 + +# ╔═╡ 0aa3fcbd-0831-45b8-9a2c-7ffbabf5895f +md" +We choose a simple observation model where infections are observed 0, 1, 2, 3 days later with equal probability. +" + +# ╔═╡ 448669bc-99f4-4823-b15e-fcc9040ba31b +obs_model = EpiAware.DelayObservations( + fill(0.25, 4), + time_horizon, + truncated(Gamma(5, 0.05 / 5), 1e-3, 1.0) +) + +# ╔═╡ e49713e8-4840-4083-8e3f-fc52d791be7b +md" +## Generate cases from the `EpiAware` model + +Having chosen an `EpiModel`, `LatentModel` and `ObservationModel`, we can implement the model as a [`Turing`](https://turinglang.org/dev/) model using `make_epi_aware`. + +By giving `missing` to the first argument, we indicate that case data will be _generated_ from the model rather than treated as fixed. +" + +# ╔═╡ abeff860-58c3-4644-9325-66ffd4446b6d +full_epi_aware_mdl = make_epi_aware(missing, time_horizon; + epi_model = epi_model, + latent_model = rwp, + observation_model = obs_model) + +# ╔═╡ 821628fb-8044-48b0-aa4f-0b7b57a2f45a +md" +We choose some fixed parameters: +- Initial incidence is 100. +- In the direct infection model, the initial incidence and in the initial value of the random walk form a non-identifiable pair. Therefore, we fix $Z_0 = 0$. +" + +# ╔═╡ 36b34fd2-2891-42ca-b5dc-abb482e516ee +fixed_parameters = (rw_init = 0.0, init_incidence = log(100.0)) + +# ╔═╡ 0aadd9e3-7f91-4b45-9663-67d11335f0d0 +md" +We fix these parameters using `fix`, and generate a random epidemic. +" + +# ╔═╡ 7e0e6012-8648-4f84-a25a-8b0138c4b72a +cond_generative_model = fix(full_epi_aware_mdl, fixed_parameters) + +# ╔═╡ b20c28be-7b07-410c-a33b-ea5ad6828c12 +random_epidemic = rand(cond_generative_model) + +# ╔═╡ d073e63b-62da-4743-ace0-78ef7806bc0b +true_infections = generated_quantities(cond_generative_model, random_epidemic).I_t + +# ╔═╡ f68b4e41-ac5c-42cd-a8c2-8761d66f7543 +let + plot(true_infections, + label = "I_t", + xlabel = "Time", + ylabel = "Infections", + title = "Generated Infections") + scatter!(random_epidemic.y_t, lab = "generated cases") +end + +# ╔═╡ b5bc8f05-b538-4abf-aa84-450bf2dff3d9 +md" +## Inference +Fixing $Z_0 = 0$ for the random walk was based on inference principles; in this model $Z_0$ and $\log I_0$ are non-identifiable. + +However, we now treat the generated data as `truth_data` and make inference without fixing any other parameters. + +We do the inference by MCMC/NUTS using the `Turing` NUTS sampler with default warm-up steps. +" + +# ╔═╡ c8ce0d46-a160-4c40-a055-69b3d10d1770 +truth_data = random_epidemic.y_t + +# ╔═╡ b4033728-b321-4100-8194-1fd9fe2d268d +inference_mdl = fix( + make_epi_aware(truth_data, time_horizon; epi_model = epi_model, + latent_model = rwp, observation_model = obs_model), + (rw_init = 0.0,) +) + +# ╔═╡ 3eb5ec5e-aae7-478e-84fb-80f2e9f85b4c +chn = sample(inference_mdl, + NUTS(; adtype = AutoReverseDiff(true)), + MCMCThreads(), + 250, + 4; + drop_warmup = true) + +# ╔═╡ 30498cc7-16a5-441a-b8cd-c19b220c60c1 +md" +### Predictive plotting + +We can spaghetti plot generated case data from the version of the model _which hasn't conditioned on case data_ using posterior parameters inferred from the version conditioned on observed data. This is known as _posterior predictive checking_, and is a useful diagnostic tool for Bayesian inference (see [here](http://www.stat.columbia.edu/~gelman/book/BDA3.pdf)). + +Because we are using synthetic data we can also plot the model predictions for the _unobserved_ infections and check that (at least in this example) we were able to capture some unobserved/latent variables in the process accurate. +" + +# ╔═╡ e9df22b8-8e4d-4ab7-91ea-c01f2239b3e5 +let + post_check_mdl = fix(full_epi_aware_mdl, (rw_init = 0.0,)) + post_check_y_t = mapreduce(hcat, generated_quantities(post_check_mdl, chn)) do gen + gen.generated_y_t + end + + predicted_I_t = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen + gen.I_t + end + + p1 = plot(post_check_y_t, c = :grey, alpha = 0.05, lab = "") + scatter!(p1, truth_data, + lab = "Observed cases", + xlabel = "Time", + ylabel = "Cases", + title = "Post. predictive checking: cases", + ylims = (-0.5, maximum(truth_data) * 1.5), + c = :green) + + p2 = plot(predicted_I_t, c = :grey, alpha = 0.05, lab = "") + scatter!(p2, true_infections, + lab = "Actual infections", + xlabel = "Time", + ylabel = "Unobserved Infections", + title = "Post. predictions: infections", + ylims = (-0.5, maximum(true_infections) * 1.5), + c = :red) + + plot(p1, p2, + layout = (1, 2), + size = (700, 400)) +end + +# ╔═╡ fd6321b1-4c3a-4123-b0dc-c45b951e0b80 +md" +As well as checking the posterior predictions for latent infections, we can also check how well inference recovered unknown parameters, such as the random walk variance or the cluster factor of the negative binomial observations. +" + +# ╔═╡ 10d8fe24-83a6-47ac-97b7-a374481473d3 +let + parameters_to_plot = (:σ²_RW, :neg_bin_cluster_factor) + + plts = map(parameters_to_plot) do name + var_samples = chn[name] |> vec + histogram(var_samples, + bins = 50, + norm = :pdf, + lw = 0, + fillalpha = 0.5, + lab = "MCMC") + vline!([getfield(random_epidemic, name)], lab = "True value") + title!(string(name)) + end + plot(plts..., layout = (2, 1)) +end + +# ╔═╡ 81efe8ca-b753-4a12-bafc-a887a999377b +md" +## Reproductive number back-calculation + +As mentioned at the top, we _don't_ directly use the concept of reproductive numbers in this note. However, we can back-calculate the implied $\mathcal{R}(t)$ values, conditional on the specified generation interval being correct. + +Here we spaghetti plot posterior sampled time-varying reproductive numbers against the actual. +" + +# ╔═╡ 15b9f37f-8d5f-460d-8c28-d7f2271fd099 +let + n = epi_model.data.len_gen_int + Rt_denom = [dot(reverse(epi_model.data.gen_int), true_infections[(t - n):(t - 1)]) + for t in (n + 1):length(true_infections)] + true_Rt = true_infections[(n + 1):end] ./ Rt_denom + + predicted_Rt = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen + _It = gen.I_t + _Rt_denom = [dot(reverse(epi_model.data.gen_int), _It[(t - n):(t - 1)]) + for t in (n + 1):length(_It)] + Rt = _It[(n + 1):end] ./ _Rt_denom + end + + plt = plot((n + 1):time_horizon, predicted_Rt, c = :grey, alpha = 0.05, lab = "") + plot!(plt, (n + 1):time_horizon, true_Rt, + lab = "true Rt", + xlabel = "Time", + ylabel = "Rt", + title = "Post. predictions: reproductive number", + c = :red, + lw = 2) +end + +# ╔═╡ Cell order: +# ╟─c593a2a0-d7f5-11ee-0931-d9f65ae84a72 +# ╟─3ebc8384-f73d-4597-83a7-07a3744fed61 +# ╠═da479d8d-1312-4b98-b0af-5be52dffaf3f +# ╟─5a0d5ab8-e985-4126-a1ac-58fe08beee38 +# ╠═56ae496b-0094-460b-89cb-526627991717 +# ╟─767beffd-1ef5-4e6c-9ac6-edb52e60fb44 +# ╠═9e43cbe3-94de-44fc-a788-b9c7adb34218 +# ╟─f067284f-a1a6-44a6-9b79-f8c2de447673 +# ╠═c0662d48-4b54-4b6d-8c91-ddf4b0e3aa43 +# ╟─fd72094f-1b95-4d07-a8b0-ef47dc560dfc +# ╠═6639e66f-7725-4976-81b2-6472419d1a62 +# ╟─df5e59f8-3185-4bed-9cca-7c266df17cec +# ╠═6fbdd8e6-2323-4352-9185-1f31a9cf9012 +# ╟─5e62a50a-71f4-4902-b1c9-fdf51fe145fa +# ╟─e813d547-6100-4c43-b84c-8cebe306bda8 +# ╠═c7580ae6-0db5-448e-8b20-4dd6fcdb1ae0 +# ╟─0aa3fcbd-0831-45b8-9a2c-7ffbabf5895f +# ╠═448669bc-99f4-4823-b15e-fcc9040ba31b +# ╟─e49713e8-4840-4083-8e3f-fc52d791be7b +# ╠═abeff860-58c3-4644-9325-66ffd4446b6d +# ╟─821628fb-8044-48b0-aa4f-0b7b57a2f45a +# ╠═36b34fd2-2891-42ca-b5dc-abb482e516ee +# ╟─0aadd9e3-7f91-4b45-9663-67d11335f0d0 +# ╠═7e0e6012-8648-4f84-a25a-8b0138c4b72a +# ╠═b20c28be-7b07-410c-a33b-ea5ad6828c12 +# ╠═d073e63b-62da-4743-ace0-78ef7806bc0b +# ╟─f68b4e41-ac5c-42cd-a8c2-8761d66f7543 +# ╟─b5bc8f05-b538-4abf-aa84-450bf2dff3d9 +# ╠═c8ce0d46-a160-4c40-a055-69b3d10d1770 +# ╠═b4033728-b321-4100-8194-1fd9fe2d268d +# ╠═3eb5ec5e-aae7-478e-84fb-80f2e9f85b4c +# ╟─30498cc7-16a5-441a-b8cd-c19b220c60c1 +# ╠═e9df22b8-8e4d-4ab7-91ea-c01f2239b3e5 +# ╟─fd6321b1-4c3a-4123-b0dc-c45b951e0b80 +# ╠═10d8fe24-83a6-47ac-97b7-a374481473d3 +# ╟─81efe8ca-b753-4a12-bafc-a887a999377b +# ╠═15b9f37f-8d5f-460d-8c28-d7f2271fd099 diff --git a/dev/examples/getting_started/index.html b/dev/examples/getting_started/index.html new file mode 100644 index 000000000..d699727eb --- /dev/null +++ b/dev/examples/getting_started/index.html @@ -0,0 +1,278 @@ + +Getting started · EpiAware.jl
+ + + + + + + +

Getting stated with EpiAware

This tutorial introduces the basic functionality of EpiAware. EpiAware is a package for making inferences on epidemiological case/determined infection data using a model-based approach.

EpiAware models

The models we consider are discrete-time $t = 1,\dots, T$ with a latent random process, $Z_t$ generating stochasticity in the number of new infections $I_t$ at each time step. Observations are treated as downstream random variables determined by the actual infections and a model of infection to observation delay.

Mathematical definition

$$\begin{align} +Z_\cdot &\sim \mathcal{P}(\mathbb{R}^T) | \theta_Z, \\ +I_0 &\sim f_0(\theta_I), \\ +I_t &\sim g_I(\{I_s, Z_s\}_{s < t}, \theta_{I}), \\ +y_t &\sim f_O(\{I_s\}_{s \leq t}, \theta_{O}). +\end{align}$$

Where, $\mathcal{P}(\mathbb{R}^T) | \theta_Z$ is a parametric process on $\mathbb{R}^T$. $f_0$ and $f_O$ are parametric distributions on, respectively, the initial number of infections and the observed case data conditional on underlying infections. $g_I$ is distribution of new infections conditional on infections and latent process in the past. Note that we assume that new infections are conditional on the strict past, whereas new observations can depend on infections on the same time step.

Code structure outline

An EpiAware model in code is created from three modular components:

  • A LatentModel: This defines the model for $Z_\cdot$. This chooses $\mathcal{P}(\mathbb{R}^T) | \theta_Z$.

  • An EpiModel: This defines a generative process for infections conditional on the latent process. This chooses $f_0(\theta_I)$, and $g_I(\{I_s, Z_s\}_{s < t}, \theta_{I})$.

  • An ObservationModel: This defines the observation model. This chooses $f_O({I_s}_{s \leq t}, \theta_{O})$

Reproductive number

EpiAware models do not need to specify a time-varying reproductive number $\mathcal{R}_t$ to generate $I_\cdot$, however, this is often a quantity of interest. When not directly used we will typically back-calculate $\mathcal{R}_t$ from the generated infections:

$$\mathcal{R}_t = {I_t \over \sum_{s \geq 1} g_s I_{t-s} }.$$

Where $g_s$ is a discrete generation interval. For this reason, even when not using a reproductive number approach directly, we ask for a generation interval.

+ +
begin
+    using EpiAware
+    using Turing
+    using Distributions
+    using StatsPlots
+    using Random
+    using DynamicPPL
+    using Statistics
+    using DataFramesMeta
+    using LinearAlgebra
+end
+ + + +

Random walk LatentModel

As an example, we choose the latent process as a random walk with parameters $\theta_Z$:

  • $Z_0$: Initial position.

  • $\sigma^2_{Z}$: The step-size variance.

Conditional on the parameters the random walk is then generated by white noise:

$$\begin{align} +Z_t &= Z_0 + \sigma_{RW} \sum_{t= 1}^T \epsilon_t, \\ +\epsilon_t &\sim \mathcal{N}(0,1). +\end{align}$$

In EpiAware we provide a constructor for random walk latent models with priors for $\theta_Z$. We choose priors,

$$\begin{align} +Z_0 &\sim \mathcal{N}(0,1),\\ +\sigma^2_Z &\sim \text{HalfNormal}(0.01). +\end{align}$$

+ +
rwp = EpiAware.RandomWalk(Normal(),
+    truncated(Normal(0.0, 0.02), 0.0, Inf))
+
RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf))
+ + +

Direct infection EpiModel

This is a simple model where the unobserved log-infections are directly generated by the latent process $Z$.

$$\log I_t = \log I_0 + Z_t.$$

As discussed above, we still ask for a defined generation interval, which can be used to calculate $\mathcal{R}_t$.

+ +
truth_GI = Gamma(2, 5)
+
Distributions.Gamma{Float64}(α=2.0, θ=5.0)
+ + +

The EpiData constructor performs double interval censoring to convert our continuous estimate of the generation interval into a discretized version. We also implement right truncation using the keyword D_gen.

+ +
model_data = EpiData(truth_GI, D_gen = 10.0)
+
EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp)
+ + +

We can supply a prior for the initial log_infections.

+ +
log_I0_prior = Normal(log(100.0), 1.0)
+
Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)
+ + +

And construct the EpiModel.

+ +
epi_model = DirectInfections(model_data, log_I0_prior)
+
DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0))
+ + +

Delayed Observations ObservationModel

The observation model is a negative binomial distribution with mean μ and cluster factor 1 / r. Delays are implemented as the action of a sparse kernel on the infections $I(t)$.

$$\begin{align} +y_t &\sim \text{NegBinomial}(\mu = \sum_{s\geq 0} K[t, t-s] I(s), r), \\ +1 / r &\sim \text{Gamma}(3, 0.05/3). +\end{align}$$

+ + +

We also set up the inference to occur over 100 days.

+ +
time_horizon = 100
+
100
+ + +

We choose a simple observation model where infections are observed 0, 1, 2, 3 days later with equal probability.

+ +
obs_model = EpiAware.DelayObservations(
+    fill(0.25, 4),
+    time_horizon,
+    truncated(Gamma(5, 0.05 / 5), 1e-3, 1.0)
+)
+
DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0))
+ + +

Generate cases from the EpiAware model

Having chosen an EpiModel, LatentModel and ObservationModel, we can implement the model as a Turing model using make_epi_aware.

By giving missing to the first argument, we indicate that case data will be generated from the model rather than treated as fixed.

+ +
full_epi_aware_mdl = make_epi_aware(missing, time_horizon;
+    epi_model = epi_model,
+    latent_model = rwp,
+    observation_model = obs_model)
+
Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (:y_t,), Tuple{Missing, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DefaultContext}(EpiAware.make_epi_aware, (y_t = missing, time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), DefaultContext())
+ + +

We choose some fixed parameters:

  • Initial incidence is 100.

  • In the direct infection model, the initial incidence and in the initial value of the random walk form a non-identifiable pair. Therefore, we fix $Z_0 = 0$.

+ +
fixed_parameters = (rw_init = 0.0, init_incidence = log(100.0))
+
(rw_init = 0.0, init_incidence = 4.605170185988092)
+ + +

We fix these parameters using fix, and generate a random epidemic.

+ +
cond_generative_model = fix(full_epi_aware_mdl, fixed_parameters)
+
Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (:y_t,), Tuple{Missing, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DynamicPPL.FixedContext{@NamedTuple{rw_init::Float64, init_incidence::Float64}, DefaultContext}}(EpiAware.make_epi_aware, (y_t = missing, time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), FixedContext((rw_init = 0.0, init_incidence = 4.605170185988092), DynamicPPL.DefaultContext()))
+ +
random_epidemic = rand(cond_generative_model)
+
(ϵ_t = [-0.5583184135402424, -0.11335818151049779, -0.41840610994469457, -1.547563151811605, 0.6999363447850959, -0.3056255658036757, -1.535672236352086, 1.774399549687218, -1.4892356494555308, -1.5762432361371324  …  0.8092430706433104, 0.25054983992169017, -0.22299867432234016, 0.9535550868628223, 0.6972338651279294, -0.8825600469470198, 1.8024873865011717, -1.60481179576158, 0.3586762822392592, -0.3759702879979565], σ²_RW = 0.03212848549550727, neg_bin_cluster_factor = 0.07280717990510553, y_t = [26, 49, 90, 131, 78, 80, 74, 61, 47, 71  …  247, 188, 251, 148, 147, 181, 271, 194, 345, 258])
+ +
true_infections = generated_quantities(cond_generative_model, random_epidemic).I_t
+
100-element Vector{Float64}:
+  90.47692493358163
+  88.65709363937565
+  82.25129849708927
+  62.32654682907016
+  70.65769231857523
+  66.89106298063069
+  50.795360380484595
+   ⋮
+ 313.83867330836426
+ 267.91916167543263
+ 370.0990677393993
+ 277.5823105609579
+ 296.01441408488313
+ 276.7231792195055
+ + + + +

Inference

+

Fixing $Z_0 = 0$ for the random walk was based on inference principles; in this model $Z_0$ and $\log I_0$ are non-identifiable.

However, we now treat the generated data as truth_data and make inference without fixing any other parameters.

We do the inference by MCMC/NUTS using the Turing NUTS sampler with default warm-up steps.

+ +
truth_data = random_epidemic.y_t
+
100-element Vector{Int64}:
+  26
+  49
+  90
+ 131
+  78
+  80
+  74
+   ⋮
+ 147
+ 181
+ 271
+ 194
+ 345
+ 258
+ +
inference_mdl = fix(
+    make_epi_aware(truth_data, time_horizon; epi_model = epi_model,
+        latent_model = rwp, observation_model = obs_model),
+    (rw_init = 0.0,)
+)
+
Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (), Tuple{Vector{Int64}, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DynamicPPL.FixedContext{@NamedTuple{rw_init::Float64}, DefaultContext}}(EpiAware.make_epi_aware, (y_t = [26, 49, 90, 131, 78, 80, 74, 61, 47, 71  …  247, 188, 251, 148, 147, 181, 271, 194, 345, 258], time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), FixedContext((rw_init = 0.0,), DynamicPPL.DefaultContext()))
+ +
chn = sample(inference_mdl,
+    NUTS(; adtype = AutoReverseDiff(true)),
+    MCMCThreads(),
+    250,
+    4;
+    drop_warmup = true)
+
iterationchainϵ_t[1]ϵ_t[2]ϵ_t[3]ϵ_t[4]ϵ_t[5]ϵ_t[6]...
112610.8033010.450201-0.551514-0.299899-0.808481-1.17731
212710.8114870.270511.01452-0.280454-0.314382-0.820463
312810.4503360.24293-0.7822810.0228791-1.24283-0.952719
41291-1.10222-0.7561-0.41742-0.6293010.16572-0.905448
51301-0.85121-1.2367-1.08595-0.842347-0.0570876-0.646837
613110.247534-0.929693-1.0840.697031-0.913332-0.967544
71321-0.66134-0.392204-0.7195050.662858-1.912460.557281
81331-0.28067-0.332762-1.61992-0.134065-1.420081.68518
91341-0.4442020.574384-0.304614-0.919158-1.925020.511353
101351-0.4596890.7121420.000772433-1.11338-2.418430.703778
...
+ + +

Predictive plotting

We can spaghetti plot generated case data from the version of the model which hasn't conditioned on case data using posterior parameters inferred from the version conditioned on observed data. This is known as posterior predictive checking, and is a useful diagnostic tool for Bayesian inference (see here).

Because we are using synthetic data we can also plot the model predictions for the unobserved infections and check that (at least in this example) we were able to capture some unobserved/latent variables in the process accurate.

+ +
let
+    post_check_mdl = fix(full_epi_aware_mdl, (rw_init = 0.0,))
+    post_check_y_t = mapreduce(hcat, generated_quantities(post_check_mdl, chn)) do gen
+        gen.generated_y_t
+    end
+
+    predicted_I_t = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen
+        gen.I_t
+    end
+
+    p1 = plot(post_check_y_t, c = :grey, alpha = 0.05, lab = "")
+    scatter!(p1, truth_data,
+        lab = "Observed cases",
+        xlabel = "Time",
+        ylabel = "Cases",
+        title = "Post. predictive checking: cases",
+        ylims = (-0.5, maximum(truth_data) * 1.5),
+        c = :green)
+
+    p2 = plot(predicted_I_t, c = :grey, alpha = 0.05, lab = "")
+    scatter!(p2, true_infections,
+        lab = "Actual infections",
+        xlabel = "Time",
+        ylabel = "Unobserved Infections",
+        title = "Post. predictions: infections",
+        ylims = (-0.5, maximum(true_infections) * 1.5),
+        c = :red)
+
+    plot(p1, p2,
+        layout = (1, 2),
+        size = (700, 400))
+end
+ + + +

As well as checking the posterior predictions for latent infections, we can also check how well inference recovered unknown parameters, such as the random walk variance or the cluster factor of the negative binomial observations.

+ +
let
+    parameters_to_plot = (:σ²_RW, :neg_bin_cluster_factor)
+
+    plts = map(parameters_to_plot) do name
+        var_samples = chn[name] |> vec
+        histogram(var_samples,
+            bins = 50,
+            norm = :pdf,
+            lw = 0,
+            fillalpha = 0.5,
+            lab = "MCMC")
+        vline!([getfield(random_epidemic, name)], lab = "True value")
+        title!(string(name))
+    end
+    plot(plts..., layout = (2, 1))
+end
+ + +

Reproductive number back-calculation

+

As mentioned at the top, we don't directly use the concept of reproductive numbers in this note. However, we can back-calculate the implied $\mathcal{R}(t)$ values, conditional on the specified generation interval being correct.

Here we spaghetti plot posterior sampled time-varying reproductive numbers against the actual.

+ +
let
+    n = epi_model.data.len_gen_int
+    Rt_denom = [dot(reverse(epi_model.data.gen_int), true_infections[(t - n):(t - 1)])
+                for t in (n + 1):length(true_infections)]
+    true_Rt = true_infections[(n + 1):end] ./ Rt_denom
+
+    predicted_Rt = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen
+        _It = gen.I_t
+        _Rt_denom = [dot(reverse(epi_model.data.gen_int), _It[(t - n):(t - 1)])
+                     for t in (n + 1):length(_It)]
+        Rt = _It[(n + 1):end] ./ _Rt_denom
+    end
+
+    plt = plot((n + 1):time_horizon, predicted_Rt, c = :grey, alpha = 0.05, lab = "")
+    plot!(plt, (n + 1):time_horizon, true_Rt,
+        lab = "true Rt",
+        xlabel = "Time",
+        ylabel = "Rt",
+        title = "Post. predictions: reproductive number",
+        c = :red,
+        lw = 2)
+end
+ + +
diff --git a/dev/index.html b/dev/index.html index c22bf3dbb..27f238746 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -EpiAware.jl: Real-time epidemic monitoring · EpiAware.jl

EpiAware.jl

Infectious disease situational awareness modelling toolkit for Julia.

A package for building and fitting situational awareness models for infectious diseases. The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.

Package Features

  • Flexible: The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.
  • Modular: The package is designed to be modular, with a clear separation between the model and the data.
  • Extensible: The package is designed to be extensible, with a clear separation between the model and the data.
  • Consistent: The package is designed to provide a consistent interface for fitting and simulating models.
  • Efficient: The package is designed to be efficient, with a clear separation between the model and the data.

See the Index for the complete list of documented functions and types.

Manual Outline

    Library Outline

    Index

      +EpiAware.jl: Real-time epidemic monitoring · EpiAware.jl

      EpiAware.jl

      Infectious disease situational awareness modelling toolkit for Julia.

      A package for building and fitting situational awareness models for infectious diseases. The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.

      Package Features

      • Flexible: The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.
      • Modular: The package is designed to be modular, with a clear separation between the model and the data.
      • Extensible: The package is designed to be extensible, with a clear separation between the model and the data.
      • Consistent: The package is designed to provide a consistent interface for fitting and simulating models.
      • Efficient: The package is designed to be efficient, with a clear separation between the model and the data.

      See the Index for the complete list of documented functions and types.

      Manual Outline

        Library Outline

        Index

          diff --git a/dev/lib/internals/index.html b/dev/lib/internals/index.html index b8df86381..085c419a3 100644 --- a/dev/lib/internals/index.html +++ b/dev/lib/internals/index.html @@ -1,5 +1,5 @@ -Internals · EpiAware.jl

          internal Documentation

          Documentation for EpiAware.jl's internal interface.

          Contents

            Index

            Internal API

            EpiAware.generate_observation_kernelMethod

            generateobservationkernel generateobservationkernel(delayint, timehorizon)

            Generate an observation kernel matrix based on the given delay interval and time horizon.

            Arguments

            • delay_int::Vector{Float64}: The delay PMF vector.
            • time_horizon::Int: The number of time steps of the observation period.

            Returns

            • K::SparseMatrixCSC{Float64, Int}: The observation kernel matrix.

            Signatures

            generate_observation_kernel(delay_int, time_horizon) -> Any
            -

            Methods

            generate_observation_kernel(delay_int, time_horizon)

            defined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:229.

            source
            +Internals · EpiAware.jl

            internal Documentation

            Documentation for EpiAware.jl's internal interface.

            Contents

              Index

              Internal API

              EpiAware.generate_observation_kernelMethod

              generateobservationkernel generateobservationkernel(delayint, timehorizon)

              Generate an observation kernel matrix based on the given delay interval and time horizon.

              Arguments

              • delay_int::Vector{Float64}: The delay PMF vector.
              • time_horizon::Int: The number of time steps of the observation period.

              Returns

              • K::SparseMatrixCSC{Float64, Int}: The observation kernel matrix.

              Signatures

              generate_observation_kernel(delay_int, time_horizon) -> Any
              +

              Methods

              generate_observation_kernel(delay_int, time_horizon)

              defined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:229.

              source
              diff --git a/dev/lib/public/index.html b/dev/lib/public/index.html index 237c20bd3..3e8d41fbf 100644 --- a/dev/lib/public/index.html +++ b/dev/lib/public/index.html @@ -1,2 +1,2 @@ -Public API · EpiAware.jl
              +Public API · EpiAware.jl
              diff --git a/dev/man/getting-started/index.html b/dev/man/getting-started/index.html deleted file mode 100644 index b26bc4623..000000000 --- a/dev/man/getting-started/index.html +++ /dev/null @@ -1,2 +0,0 @@ - -Getting started · EpiAware.jl
              diff --git a/dev/man/guide/index.html b/dev/man/guide/index.html index 284ebf05b..ea3908f14 100644 --- a/dev/man/guide/index.html +++ b/dev/man/guide/index.html @@ -1,2 +1,2 @@ -Guide · EpiAware.jl
              +Guide · EpiAware.jl
              diff --git a/dev/objects.inv b/dev/objects.inv index b2ee9d914..4931fcb9e 100644 Binary files a/dev/objects.inv and b/dev/objects.inv differ diff --git a/dev/release-notes/index.html b/dev/release-notes/index.html index 2f41520d5..54a01412d 100644 --- a/dev/release-notes/index.html +++ b/dev/release-notes/index.html @@ -1,2 +1,2 @@ -Release notes · EpiAware.jl
              +Release notes · EpiAware.jl
              diff --git a/dev/search_index.js b/dev/search_index.js index 77e05ea3b..def289a16 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"checklist/#Checklists","page":"Release checklist","title":"Checklists","text":"","category":"section"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"The purpose of this page is to collate a series of checklists for commonly performed changes to the source code of EpiAware. It has been adapted from Documenter.jl.","category":"page"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"In each case, copy the checklist into the description of the pull request.","category":"page"},{"location":"checklist/#Making-a-release","page":"Release checklist","title":"Making a release","text":"","category":"section"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"In preparation for a release, use the following checklist. These steps should be performed on a branch with an open pull request, either for a topic branch, or for a new branch release-1.y.z (\"Release version 1.y.z\") if multiple changes have accumulated on the master branch since the last release.","category":"page"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"## Pre-release\n\n - [ ] Change the version number in `Project.toml`\n * If the release is breaking, increment MAJOR\n * If the release adds a new user-visible feature, increment MINOR\n * Otherwise (bug-fixes, documentation improvements), increment PATCH\n - [ ] Update `CHANGELOG.md`, following the existing style (in particular, make sure that the change log for this version has the correct version number and date).\n - [ ] Run `make changelog`, to make sure that all the issue references in `CHANGELOG.md` are up to date.\n - [ ] Check that the commit messages in this PR do not contain `[ci skip]`\n - [ ] Run https://github.com/JuliaDocs/Documenter.jl/actions/workflows/regression-tests.yml\n using a `workflow_dispatch` trigger to check for any changes that broke extensions.\n\n## The release\n\n - [ ] After merging the pull request, tag the release. There are two options for this:\n\n 1. [Comment `[at]JuliaRegistrator register` on the GitHub commit.](https://github.com/JuliaRegistries/Registrator.jl#via-the-github-app)\n 2. Use [JuliaHub's package registration feature](https://help.juliahub.com/juliahub/stable/contribute/#registrator) to trigger the registration.\n\n Either of those should automatically publish a new version to the Julia registry.\n - Once registered, the `TagBot.yml` workflow should create a tag, and rebuild the documentation for this tag.\n - These steps can take quite a bit of time (1 hour or more), so don't be surprised if the new documentation takes a while to appear.","category":"page"},{"location":"release-notes/","page":"Release notes","title":"Release notes","text":"EditURL = \"https://github.com/JuliaDocs/Documenter.jl/blob/master/CHANGELOG.md\"","category":"page"},{"location":"release-notes/#Release-notes","page":"Release notes","title":"Release notes","text":"","category":"section"},{"location":"release-notes/","page":"Release notes","title":"Release notes","text":"The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.","category":"page"},{"location":"release-notes/#Unreleased","page":"Release notes","title":"Unreleased","text":"","category":"section"},{"location":"release-notes/#Added","page":"Release notes","title":"Added","text":"","category":"section"},{"location":"release-notes/#Changed","page":"Release notes","title":"Changed","text":"","category":"section"},{"location":"release-notes/#Fixed","page":"Release notes","title":"Fixed","text":"","category":"section"},{"location":"lib/internals/#internal-Documentation","page":"Internals","title":"internal Documentation","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Documentation for EpiAware.jl's internal interface.","category":"page"},{"location":"lib/internals/#Contents","page":"Internals","title":"Contents","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Pages = [\"internal.md\"]\nDepth = 2:2","category":"page"},{"location":"lib/internals/#Index","page":"Internals","title":"Index","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Pages = [\"internals.md\"]","category":"page"},{"location":"lib/internals/#Internal-API","page":"Internals","title":"Internal API","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Modules = [EpiAware]\nPublic = false","category":"page"},{"location":"lib/internals/#EpiAware.generate_observation_kernel-Tuple{Any, Any}","page":"Internals","title":"EpiAware.generate_observation_kernel","text":"generateobservationkernel generateobservationkernel(delayint, timehorizon)\n\nGenerate an observation kernel matrix based on the given delay interval and time horizon.\n\nArguments\n\ndelay_int::Vector{Float64}: The delay PMF vector.\ntime_horizon::Int: The number of time steps of the observation period.\n\nReturns\n\nK::SparseMatrixCSC{Float64, Int}: The observation kernel matrix.\n\n\n\nSignatures\n\ngenerate_observation_kernel(delay_int, time_horizon) -> Any\n\n\n\n\nMethods\n\ngenerate_observation_kernel(delay_int, time_horizon)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:229.\n\n\n\n\n\n","category":"method"},{"location":"lib/internals/#EpiAware.mean_cc_neg_bin-Tuple{Any, Any}","page":"Internals","title":"EpiAware.mean_cc_neg_bin","text":"meanccnegbin meanccnegbin(μ, α)\n\nCompute the mean-cluster factor negative binomial distribution.\n\nArguments\n\nμ: The mean of the distribution.\nα: The clustering factor parameter.\n\nReturns\n\nA NegativeBinomial distribution object.\n\n\n\nSignatures\n\nmean_cc_neg_bin(μ, α) -> Distributions.NegativeBinomial\n\n\n\n\nMethods\n\nmean_cc_neg_bin(μ, α)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:210.\n\n\n\n\n\n","category":"method"},{"location":"lib/internals/#EpiAware.neg_MGF-Tuple{Any, AbstractVector}","page":"Internals","title":"EpiAware.neg_MGF","text":"negMGF negMGF(r, w::AbstractVector)\n\nCompute the negative moment generating function (MGF) for a given rate r and weights w.\n\nArguments\n\nr: The rate parameter.\nw: An abstract vector of weights.\n\nReturns\n\nThe value of the negative MGF.\n\n\n\nSignatures\n\nneg_MGF(r, w::AbstractVector) -> Any\n\n\n\n\nMethods\n\nneg_MGF(r, w)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:132.\n\n\n\n\n\n","category":"method"},{"location":"contributing/#Contributing","page":"Contributing","title":"Contributing","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"This page details the some of the guidelines that should be followed when contributing to this package. It is adapted from Documenter.jl.","category":"page"},{"location":"contributing/#Branches","page":"Contributing","title":"Branches","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"release-* branches are used for tagged minor versions of this package. This follows the same approach used in the main Julia repository, albeit on a much more modest scale.","category":"page"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Please open pull requests against the master branch rather than any of the release-* branches whenever possible.","category":"page"},{"location":"contributing/#Backports","page":"Contributing","title":"Backports","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Bug fixes are backported to the release-* branches using git cherry-pick -x by a EpiAware member and will become available in point releases of that particular minor version of the package.","category":"page"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Feel free to nominate commits that should be backported by opening an issue. Requests for new point releases to be tagged in METADATA.jl can also be made in the same way.","category":"page"},{"location":"contributing/#release-*-branches","page":"Contributing","title":"release-* branches","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Each new minor version x.y.0 gets a branch called release-x.y (a protected branch).\nNew versions are usually tagged only from the release-x.y branches.\nFor patch releases, changes get backported to the release-x.y branch via a single PR with the standard name \"Backports for x.y.z\" and label \"Type: Backport\". The PR message links to all the PRs that are providing commits to the backport. The PR gets merged as a merge commit (i.e. not squashed).\nThe old release-* branches may be removed once they have outlived their usefulness.\nPatch version milestones are used to keep track of which PRs get backported etc.","category":"page"},{"location":"contributing/#Style-Guide","page":"Contributing","title":"Style Guide","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Follow the style of the surrounding text when making changes. When adding new features please try to stick to the following points whenever applicable. This project follows the SciML style guide.","category":"page"},{"location":"contributing/#Tests","page":"Contributing","title":"Tests","text":"","category":"section"},{"location":"contributing/#Unit-tests","page":"Contributing","title":"Unit tests","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"As is conventional for Julia packages, unit tests are located at test/*.jl with the entrypoint test/runtests.jl.","category":"page"},{"location":"contributing/#End-to-end-testing","page":"Contributing","title":"End to end testing","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Tests that build example package docs from source and inspect the results (end to end tests) are located in /test/examples. The main entry points are test/examples/make.jl for building and test/examples/test.jl for doing some basic checks on the generated outputs.","category":"page"},{"location":"lib/public/#Public-Documentation","page":"Public API","title":"Public Documentation","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Documentation for EpiAware.jl's public interface.","category":"page"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"See the Internals section of the manual for internal package docs covering all submodules.","category":"page"},{"location":"lib/public/#Contents","page":"Public API","title":"Contents","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Pages = [\"public.md\"]\nDepth = 2:2","category":"page"},{"location":"lib/public/#Index","page":"Public API","title":"Index","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Pages = [\"public.md\"]","category":"page"},{"location":"lib/public/#Public-API","page":"Public API","title":"Public API","text":"","category":"section"},{"location":"#EpiAware.jl","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Infectious disease situational awareness modelling toolkit for Julia.","category":"page"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"A package for building and fitting situational awareness models for infectious diseases. The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.","category":"page"},{"location":"#Package-Features","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Package Features","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Flexible: The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.\nModular: The package is designed to be modular, with a clear separation between the model and the data.\nExtensible: The package is designed to be extensible, with a clear separation between the model and the data.\nConsistent: The package is designed to provide a consistent interface for fitting and simulating models.\nEfficient: The package is designed to be efficient, with a clear separation between the model and the data.","category":"page"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"See the Index for the complete list of documented functions and types.","category":"page"},{"location":"#Manual-Outline","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Manual Outline","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\n \"man/contributing.md\",\n]\nDepth = 1","category":"page"},{"location":"#Library-Outline","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Library Outline","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\"lib/public.md\", \"lib/internals.md\"]","category":"page"},{"location":"#main-index","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Index","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\"lib/public.md\"]","category":"page"}] +[{"location":"checklist/#Checklists","page":"Release checklist","title":"Checklists","text":"","category":"section"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"The purpose of this page is to collate a series of checklists for commonly performed changes to the source code of EpiAware. It has been adapted from Documenter.jl.","category":"page"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"In each case, copy the checklist into the description of the pull request.","category":"page"},{"location":"checklist/#Making-a-release","page":"Release checklist","title":"Making a release","text":"","category":"section"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"In preparation for a release, use the following checklist. These steps should be performed on a branch with an open pull request, either for a topic branch, or for a new branch release-1.y.z (\"Release version 1.y.z\") if multiple changes have accumulated on the master branch since the last release.","category":"page"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"## Pre-release\n\n - [ ] Change the version number in `Project.toml`\n * If the release is breaking, increment MAJOR\n * If the release adds a new user-visible feature, increment MINOR\n * Otherwise (bug-fixes, documentation improvements), increment PATCH\n - [ ] Update `CHANGELOG.md`, following the existing style (in particular, make sure that the change log for this version has the correct version number and date).\n - [ ] Run `make changelog`, to make sure that all the issue references in `CHANGELOG.md` are up to date.\n - [ ] Check that the commit messages in this PR do not contain `[ci skip]`\n - [ ] Run https://github.com/JuliaDocs/Documenter.jl/actions/workflows/regression-tests.yml\n using a `workflow_dispatch` trigger to check for any changes that broke extensions.\n\n## The release\n\n - [ ] After merging the pull request, tag the release. There are two options for this:\n\n 1. [Comment `[at]JuliaRegistrator register` on the GitHub commit.](https://github.com/JuliaRegistries/Registrator.jl#via-the-github-app)\n 2. Use [JuliaHub's package registration feature](https://help.juliahub.com/juliahub/stable/contribute/#registrator) to trigger the registration.\n\n Either of those should automatically publish a new version to the Julia registry.\n - Once registered, the `TagBot.yml` workflow should create a tag, and rebuild the documentation for this tag.\n - These steps can take quite a bit of time (1 hour or more), so don't be surprised if the new documentation takes a while to appear.","category":"page"},{"location":"release-notes/","page":"Release notes","title":"Release notes","text":"EditURL = \"https://github.com/JuliaDocs/Documenter.jl/blob/master/CHANGELOG.md\"","category":"page"},{"location":"release-notes/#Release-notes","page":"Release notes","title":"Release notes","text":"","category":"section"},{"location":"release-notes/","page":"Release notes","title":"Release notes","text":"The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.","category":"page"},{"location":"release-notes/#Unreleased","page":"Release notes","title":"Unreleased","text":"","category":"section"},{"location":"release-notes/#Added","page":"Release notes","title":"Added","text":"","category":"section"},{"location":"release-notes/#Changed","page":"Release notes","title":"Changed","text":"","category":"section"},{"location":"release-notes/#Fixed","page":"Release notes","title":"Fixed","text":"","category":"section"},{"location":"lib/internals/#internal-Documentation","page":"Internals","title":"internal Documentation","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Documentation for EpiAware.jl's internal interface.","category":"page"},{"location":"lib/internals/#Contents","page":"Internals","title":"Contents","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Pages = [\"internal.md\"]\nDepth = 2:2","category":"page"},{"location":"lib/internals/#Index","page":"Internals","title":"Index","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Pages = [\"internals.md\"]","category":"page"},{"location":"lib/internals/#Internal-API","page":"Internals","title":"Internal API","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Modules = [EpiAware]\nPublic = false","category":"page"},{"location":"lib/internals/#EpiAware.generate_observation_kernel-Tuple{Any, Any}","page":"Internals","title":"EpiAware.generate_observation_kernel","text":"generateobservationkernel generateobservationkernel(delayint, timehorizon)\n\nGenerate an observation kernel matrix based on the given delay interval and time horizon.\n\nArguments\n\ndelay_int::Vector{Float64}: The delay PMF vector.\ntime_horizon::Int: The number of time steps of the observation period.\n\nReturns\n\nK::SparseMatrixCSC{Float64, Int}: The observation kernel matrix.\n\n\n\nSignatures\n\ngenerate_observation_kernel(delay_int, time_horizon) -> Any\n\n\n\n\nMethods\n\ngenerate_observation_kernel(delay_int, time_horizon)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:229.\n\n\n\n\n\n","category":"method"},{"location":"lib/internals/#EpiAware.mean_cc_neg_bin-Tuple{Any, Any}","page":"Internals","title":"EpiAware.mean_cc_neg_bin","text":"meanccnegbin meanccnegbin(μ, α)\n\nCompute the mean-cluster factor negative binomial distribution.\n\nArguments\n\nμ: The mean of the distribution.\nα: The clustering factor parameter.\n\nReturns\n\nA NegativeBinomial distribution object.\n\n\n\nSignatures\n\nmean_cc_neg_bin(μ, α) -> Distributions.NegativeBinomial\n\n\n\n\nMethods\n\nmean_cc_neg_bin(μ, α)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:210.\n\n\n\n\n\n","category":"method"},{"location":"lib/internals/#EpiAware.neg_MGF-Tuple{Any, AbstractVector}","page":"Internals","title":"EpiAware.neg_MGF","text":"negMGF negMGF(r, w::AbstractVector)\n\nCompute the negative moment generating function (MGF) for a given rate r and weights w.\n\nArguments\n\nr: The rate parameter.\nw: An abstract vector of weights.\n\nReturns\n\nThe value of the negative MGF.\n\n\n\nSignatures\n\nneg_MGF(r, w::AbstractVector) -> Any\n\n\n\n\nMethods\n\nneg_MGF(r, w)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:132.\n\n\n\n\n\n","category":"method"},{"location":"contributing/#Contributing","page":"Contributing","title":"Contributing","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"This page details the some of the guidelines that should be followed when contributing to this package. It is adapted from Documenter.jl.","category":"page"},{"location":"contributing/#Branches","page":"Contributing","title":"Branches","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"release-* branches are used for tagged minor versions of this package. This follows the same approach used in the main Julia repository, albeit on a much more modest scale.","category":"page"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Please open pull requests against the master branch rather than any of the release-* branches whenever possible.","category":"page"},{"location":"contributing/#Backports","page":"Contributing","title":"Backports","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Bug fixes are backported to the release-* branches using git cherry-pick -x by a EpiAware member and will become available in point releases of that particular minor version of the package.","category":"page"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Feel free to nominate commits that should be backported by opening an issue. Requests for new point releases to be tagged in METADATA.jl can also be made in the same way.","category":"page"},{"location":"contributing/#release-*-branches","page":"Contributing","title":"release-* branches","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Each new minor version x.y.0 gets a branch called release-x.y (a protected branch).\nNew versions are usually tagged only from the release-x.y branches.\nFor patch releases, changes get backported to the release-x.y branch via a single PR with the standard name \"Backports for x.y.z\" and label \"Type: Backport\". The PR message links to all the PRs that are providing commits to the backport. The PR gets merged as a merge commit (i.e. not squashed).\nThe old release-* branches may be removed once they have outlived their usefulness.\nPatch version milestones are used to keep track of which PRs get backported etc.","category":"page"},{"location":"contributing/#Style-Guide","page":"Contributing","title":"Style Guide","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Follow the style of the surrounding text when making changes. When adding new features please try to stick to the following points whenever applicable. This project follows the SciML style guide.","category":"page"},{"location":"contributing/#Tests","page":"Contributing","title":"Tests","text":"","category":"section"},{"location":"contributing/#Unit-tests","page":"Contributing","title":"Unit tests","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"As is conventional for Julia packages, unit tests are located at test/*.jl with the entrypoint test/runtests.jl.","category":"page"},{"location":"contributing/#End-to-end-testing","page":"Contributing","title":"End to end testing","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Tests that build example package docs from source and inspect the results (end to end tests) are located in /test/examples. The main entry points are test/examples/make.jl for building and test/examples/test.jl for doing some basic checks on the generated outputs.","category":"page"},{"location":"examples/getting_started/","page":"Getting started","title":"Getting started","text":"\n\n\n\n\n\n\n\n

              Getting stated with EpiAware

              This tutorial introduces the basic functionality of EpiAware. EpiAware is a package for making inferences on epidemiological case/determined infection data using a model-based approach.

              EpiAware models

              The models we consider are discrete-time $t = 1,\\dots, T$ with a latent random process, $Z_t$ generating stochasticity in the number of new infections $I_t$ at each time step. Observations are treated as downstream random variables determined by the actual infections and a model of infection to observation delay.

              Mathematical definition

              $$\\begin{align}\nZ_\\cdot &\\sim \\mathcal{P}(\\mathbb{R}^T) | \\theta_Z, \\\\\nI_0 &\\sim f_0(\\theta_I), \\\\\nI_t &\\sim g_I(\\{I_s, Z_s\\}_{s < t}, \\theta_{I}), \\\\\ny_t &\\sim f_O(\\{I_s\\}_{s \\leq t}, \\theta_{O}).\n\\end{align}$$

              Where, $\\mathcal{P}(\\mathbb{R}^T) | \\theta_Z$ is a parametric process on $\\mathbb{R}^T$. $f_0$ and $f_O$ are parametric distributions on, respectively, the initial number of infections and the observed case data conditional on underlying infections. $g_I$ is distribution of new infections conditional on infections and latent process in the past. Note that we assume that new infections are conditional on the strict past, whereas new observations can depend on infections on the same time step.

              Code structure outline

              An EpiAware model in code is created from three modular components:

              Reproductive number

              EpiAware models do not need to specify a time-varying reproductive number $\\mathcal{R}_t$ to generate $I_\\cdot$, however, this is often a quantity of interest. When not directly used we will typically back-calculate $\\mathcal{R}_t$ from the generated infections:

              $$\\mathcal{R}_t = {I_t \\over \\sum_{s \\geq 1} g_s I_{t-s} }.$$

              Where $g_s$ is a discrete generation interval. For this reason, even when not using a reproductive number approach directly, we ask for a generation interval.

              \n\n
              begin\n    using EpiAware\n    using Turing\n    using Distributions\n    using StatsPlots\n    using Random\n    using DynamicPPL\n    using Statistics\n    using DataFramesMeta\n    using LinearAlgebra\nend
              \n\n\n\n

              Random walk LatentModel

              As an example, we choose the latent process as a random walk with parameters $\\theta_Z$:

              Conditional on the parameters the random walk is then generated by white noise:

              $$\\begin{align}\nZ_t &= Z_0 + \\sigma_{RW} \\sum_{t= 1}^T \\epsilon_t, \\\\\n\\epsilon_t &\\sim \\mathcal{N}(0,1).\n\\end{align}$$

              In EpiAware we provide a constructor for random walk latent models with priors for $\\theta_Z$. We choose priors,

              $$\\begin{align}\nZ_0 &\\sim \\mathcal{N}(0,1),\\\\\n\\sigma^2_Z &\\sim \\text{HalfNormal}(0.01).\n\\end{align}$$

              \n\n
              rwp = EpiAware.RandomWalk(Normal(),\n    truncated(Normal(0.0, 0.02), 0.0, Inf))
              \n
              RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf))
              \n\n\n

              Direct infection EpiModel

              This is a simple model where the unobserved log-infections are directly generated by the latent process $Z$.

              $$\\log I_t = \\log I_0 + Z_t.$$

              As discussed above, we still ask for a defined generation interval, which can be used to calculate $\\mathcal{R}_t$.

              \n\n
              truth_GI = Gamma(2, 5)
              \n
              Distributions.Gamma{Float64}(α=2.0, θ=5.0)
              \n\n\n

              The EpiData constructor performs double interval censoring to convert our continuous estimate of the generation interval into a discretized version. We also implement right truncation using the keyword D_gen.

              \n\n
              model_data = EpiData(truth_GI, D_gen = 10.0)
              \n
              EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp)
              \n\n\n

              We can supply a prior for the initial log_infections.

              \n\n
              log_I0_prior = Normal(log(100.0), 1.0)
              \n
              Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)
              \n\n\n

              And construct the EpiModel.

              \n\n
              epi_model = DirectInfections(model_data, log_I0_prior)
              \n
              DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0))
              \n\n\n

              Delayed Observations ObservationModel

              The observation model is a negative binomial distribution with mean μ and cluster factor 1 / r. Delays are implemented as the action of a sparse kernel on the infections $I(t)$.

              $$\\begin{align}\ny_t &\\sim \\text{NegBinomial}(\\mu = \\sum_{s\\geq 0} K[t, t-s] I(s), r), \\\\\n1 / r &\\sim \\text{Gamma}(3, 0.05/3).\n\\end{align}$$

              \n\n\n

              We also set up the inference to occur over 100 days.

              \n\n
              time_horizon = 100
              \n
              100
              \n\n\n

              We choose a simple observation model where infections are observed 0, 1, 2, 3 days later with equal probability.

              \n\n
              obs_model = EpiAware.DelayObservations(\n    fill(0.25, 4),\n    time_horizon,\n    truncated(Gamma(5, 0.05 / 5), 1e-3, 1.0)\n)
              \n
              DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0))
              \n\n\n

              Generate cases from the EpiAware model

              Having chosen an EpiModel, LatentModel and ObservationModel, we can implement the model as a Turing model using make_epi_aware.

              By giving missing to the first argument, we indicate that case data will be generated from the model rather than treated as fixed.

              \n\n
              full_epi_aware_mdl = make_epi_aware(missing, time_horizon;\n    epi_model = epi_model,\n    latent_model = rwp,\n    observation_model = obs_model)
              \n
              Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (:y_t,), Tuple{Missing, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DefaultContext}(EpiAware.make_epi_aware, (y_t = missing, time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), DefaultContext())
              \n\n\n

              We choose some fixed parameters:

              \n\n
              fixed_parameters = (rw_init = 0.0, init_incidence = log(100.0))
              \n
              (rw_init = 0.0, init_incidence = 4.605170185988092)
              \n\n\n

              We fix these parameters using fix, and generate a random epidemic.

              \n\n
              cond_generative_model = fix(full_epi_aware_mdl, fixed_parameters)
              \n
              Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (:y_t,), Tuple{Missing, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DynamicPPL.FixedContext{@NamedTuple{rw_init::Float64, init_incidence::Float64}, DefaultContext}}(EpiAware.make_epi_aware, (y_t = missing, time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), FixedContext((rw_init = 0.0, init_incidence = 4.605170185988092), DynamicPPL.DefaultContext()))
              \n\n
              random_epidemic = rand(cond_generative_model)
              \n
              (ϵ_t = [-0.5583184135402424, -0.11335818151049779, -0.41840610994469457, -1.547563151811605, 0.6999363447850959, -0.3056255658036757, -1.535672236352086, 1.774399549687218, -1.4892356494555308, -1.5762432361371324  …  0.8092430706433104, 0.25054983992169017, -0.22299867432234016, 0.9535550868628223, 0.6972338651279294, -0.8825600469470198, 1.8024873865011717, -1.60481179576158, 0.3586762822392592, -0.3759702879979565], σ²_RW = 0.03212848549550727, neg_bin_cluster_factor = 0.07280717990510553, y_t = [26, 49, 90, 131, 78, 80, 74, 61, 47, 71  …  247, 188, 251, 148, 147, 181, 271, 194, 345, 258])
              \n\n
              true_infections = generated_quantities(cond_generative_model, random_epidemic).I_t
              \n
              100-element Vector{Float64}:\n  90.47692493358163\n  88.65709363937565\n  82.25129849708927\n  62.32654682907016\n  70.65769231857523\n  66.89106298063069\n  50.795360380484595\n   ⋮\n 313.83867330836426\n 267.91916167543263\n 370.0990677393993\n 277.5823105609579\n 296.01441408488313\n 276.7231792195055
              \n\n\n\n\n","category":"page"},{"location":"examples/getting_started/#Inference","page":"Getting started","title":"Inference","text":"","category":"section"},{"location":"examples/getting_started/","page":"Getting started","title":"Getting started","text":"
              \n

              Fixing $Z_0 = 0$ for the random walk was based on inference principles; in this model $Z_0$ and $\\log I_0$ are non-identifiable.

              However, we now treat the generated data as truth_data and make inference without fixing any other parameters.

              We do the inference by MCMC/NUTS using the Turing NUTS sampler with default warm-up steps.

              \n\n
              truth_data = random_epidemic.y_t
              \n
              100-element Vector{Int64}:\n  26\n  49\n  90\n 131\n  78\n  80\n  74\n   ⋮\n 147\n 181\n 271\n 194\n 345\n 258
              \n\n
              inference_mdl = fix(\n    make_epi_aware(truth_data, time_horizon; epi_model = epi_model,\n        latent_model = rwp, observation_model = obs_model),\n    (rw_init = 0.0,)\n)
              \n
              Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (), Tuple{Vector{Int64}, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DynamicPPL.FixedContext{@NamedTuple{rw_init::Float64}, DefaultContext}}(EpiAware.make_epi_aware, (y_t = [26, 49, 90, 131, 78, 80, 74, 61, 47, 71  …  247, 188, 251, 148, 147, 181, 271, 194, 345, 258], time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), FixedContext((rw_init = 0.0,), DynamicPPL.DefaultContext()))
              \n\n
              chn = sample(inference_mdl,\n    NUTS(; adtype = AutoReverseDiff(true)),\n    MCMCThreads(),\n    250,\n    4;\n    drop_warmup = true)
              \n
              iterationchainϵ_t[1]ϵ_t[2]ϵ_t[3]ϵ_t[4]ϵ_t[5]ϵ_t[6]...
              112610.8033010.450201-0.551514-0.299899-0.808481-1.17731
              212710.8114870.270511.01452-0.280454-0.314382-0.820463
              312810.4503360.24293-0.7822810.0228791-1.24283-0.952719
              41291-1.10222-0.7561-0.41742-0.6293010.16572-0.905448
              51301-0.85121-1.2367-1.08595-0.842347-0.0570876-0.646837
              613110.247534-0.929693-1.0840.697031-0.913332-0.967544
              71321-0.66134-0.392204-0.7195050.662858-1.912460.557281
              81331-0.28067-0.332762-1.61992-0.134065-1.420081.68518
              91341-0.4442020.574384-0.304614-0.919158-1.925020.511353
              101351-0.4596890.7121420.000772433-1.11338-2.418430.703778
              ...
              \n\n\n

              Predictive plotting

              We can spaghetti plot generated case data from the version of the model which hasn't conditioned on case data using posterior parameters inferred from the version conditioned on observed data. This is known as posterior predictive checking, and is a useful diagnostic tool for Bayesian inference (see here).

              Because we are using synthetic data we can also plot the model predictions for the unobserved infections and check that (at least in this example) we were able to capture some unobserved/latent variables in the process accurate.

              \n\n
              let\n    post_check_mdl = fix(full_epi_aware_mdl, (rw_init = 0.0,))\n    post_check_y_t = mapreduce(hcat, generated_quantities(post_check_mdl, chn)) do gen\n        gen.generated_y_t\n    end\n\n    predicted_I_t = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen\n        gen.I_t\n    end\n\n    p1 = plot(post_check_y_t, c = :grey, alpha = 0.05, lab = \"\")\n    scatter!(p1, truth_data,\n        lab = \"Observed cases\",\n        xlabel = \"Time\",\n        ylabel = \"Cases\",\n        title = \"Post. predictive checking: cases\",\n        ylims = (-0.5, maximum(truth_data) * 1.5),\n        c = :green)\n\n    p2 = plot(predicted_I_t, c = :grey, alpha = 0.05, lab = \"\")\n    scatter!(p2, true_infections,\n        lab = \"Actual infections\",\n        xlabel = \"Time\",\n        ylabel = \"Unobserved Infections\",\n        title = \"Post. predictions: infections\",\n        ylims = (-0.5, maximum(true_infections) * 1.5),\n        c = :red)\n\n    plot(p1, p2,\n        layout = (1, 2),\n        size = (700, 400))\nend
              \n\n\n\n

              As well as checking the posterior predictions for latent infections, we can also check how well inference recovered unknown parameters, such as the random walk variance or the cluster factor of the negative binomial observations.

              \n\n
              let\n    parameters_to_plot = (:σ²_RW, :neg_bin_cluster_factor)\n\n    plts = map(parameters_to_plot) do name\n        var_samples = chn[name] |> vec\n        histogram(var_samples,\n            bins = 50,\n            norm = :pdf,\n            lw = 0,\n            fillalpha = 0.5,\n            lab = \"MCMC\")\n        vline!([getfield(random_epidemic, name)], lab = \"True value\")\n        title!(string(name))\n    end\n    plot(plts..., layout = (2, 1))\nend
              \n\n\n","category":"page"},{"location":"examples/getting_started/#Reproductive-number-back-calculation","page":"Getting started","title":"Reproductive number back-calculation","text":"","category":"section"},{"location":"examples/getting_started/","page":"Getting started","title":"Getting started","text":"
              \n

              As mentioned at the top, we don't directly use the concept of reproductive numbers in this note. However, we can back-calculate the implied $\\mathcal{R}(t)$ values, conditional on the specified generation interval being correct.

              Here we spaghetti plot posterior sampled time-varying reproductive numbers against the actual.

              \n\n
              let\n    n = epi_model.data.len_gen_int\n    Rt_denom = [dot(reverse(epi_model.data.gen_int), true_infections[(t - n):(t - 1)])\n                for t in (n + 1):length(true_infections)]\n    true_Rt = true_infections[(n + 1):end] ./ Rt_denom\n\n    predicted_Rt = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen\n        _It = gen.I_t\n        _Rt_denom = [dot(reverse(epi_model.data.gen_int), _It[(t - n):(t - 1)])\n                     for t in (n + 1):length(_It)]\n        Rt = _It[(n + 1):end] ./ _Rt_denom\n    end\n\n    plt = plot((n + 1):time_horizon, predicted_Rt, c = :grey, alpha = 0.05, lab = \"\")\n    plot!(plt, (n + 1):time_horizon, true_Rt,\n        lab = \"true Rt\",\n        xlabel = \"Time\",\n        ylabel = \"Rt\",\n        title = \"Post. predictions: reproductive number\",\n        c = :red,\n        lw = 2)\nend
              \n\n\n","category":"page"},{"location":"examples/getting_started/","page":"Getting started","title":"Getting started","text":"EditURL = \"https://github.com/CDCgov/Rt-without-renewal/blob/main/docs/src/examples/getting_started.jl\"","category":"page"},{"location":"lib/public/#Public-Documentation","page":"Public API","title":"Public Documentation","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Documentation for EpiAware.jl's public interface.","category":"page"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"See the Internals section of the manual for internal package docs covering all submodules.","category":"page"},{"location":"lib/public/#Contents","page":"Public API","title":"Contents","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Pages = [\"public.md\"]\nDepth = 2:2","category":"page"},{"location":"lib/public/#Index","page":"Public API","title":"Index","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Pages = [\"public.md\"]","category":"page"},{"location":"lib/public/#Public-API","page":"Public API","title":"Public API","text":"","category":"section"},{"location":"#EpiAware.jl","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Infectious disease situational awareness modelling toolkit for Julia.","category":"page"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"A package for building and fitting situational awareness models for infectious diseases. The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.","category":"page"},{"location":"#Package-Features","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Package Features","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Flexible: The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.\nModular: The package is designed to be modular, with a clear separation between the model and the data.\nExtensible: The package is designed to be extensible, with a clear separation between the model and the data.\nConsistent: The package is designed to provide a consistent interface for fitting and simulating models.\nEfficient: The package is designed to be efficient, with a clear separation between the model and the data.","category":"page"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"See the Index for the complete list of documented functions and types.","category":"page"},{"location":"#Manual-Outline","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Manual Outline","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\n \"man/contributing.md\",\n]\nDepth = 1","category":"page"},{"location":"#Library-Outline","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Library Outline","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\"lib/public.md\", \"lib/internals.md\"]","category":"page"},{"location":"#main-index","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Index","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\"lib/public.md\"]","category":"page"}] }