diff --git a/.gitignore b/.gitignore index 6f6156ff..9a050fb8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,9 +14,9 @@ deps/usr/ deps/src/ # Build artifacts for creating documentation generated by the Documenter package -docs/build/ -docs/site/ -**/docs/src/examples/ +**/**/docs/build/ +**/**/docs/site/ +**/**/docs/src/examples/ # File generated by Pkg, the package manager, based on a corresponding Project.toml # It records a fixed state of all packages used by the project. As such, it should not be diff --git a/GeneralisedFilters/docs/literate.jl b/GeneralisedFilters/docs/literate.jl index feb1e47b..9e8fff11 100644 --- a/GeneralisedFilters/docs/literate.jl +++ b/GeneralisedFilters/docs/literate.jl @@ -14,6 +14,142 @@ Pkg.activate(EXAMPLEPATH) Pkg.instantiate() using Literate: Literate -# Convert to markdown and notebook -const SCRIPTJL = joinpath(EXAMPLEPATH, "script.jl") -Literate.markdown(SCRIPTJL, OUTDIR; name=EXAMPLE, execute=true) +# Determine the package name (GeneralisedFilters or SSMProblems) +const PKG_NAME = if occursin("GeneralisedFilters", abspath(EXAMPLEPATH)) + "GeneralisedFilters" +else + "SSMProblems" +end + +# Git revision for Pkg.add — use the current commit/branch on CI so notebooks match the docs build +const GIT_REV = if haskey(ENV, "GITHUB_ACTIONS") + ref = get(ENV, "GITHUB_REF", "") + if startswith(ref, "refs/heads/") + String(match(r"refs\/heads\/(.*)", ref).captures[1]) + elseif startswith(ref, "refs/tags/") + String(match(r"refs\/tags\/(.*)", ref).captures[1]) + else + get(ENV, "GITHUB_SHA", "main") + end +else + "main" +end + +const REPO_URL = "https://github.com/TuringLang/SSMProblems.jl" + +# Compute a version/PR-aware Colab root URL, mirroring Literate.jl's deploy folder logic. +# On CI this produces URLs like: +# .../blob/gh-pages/GeneralisedFilters/dev/... +# .../blob/gh-pages/SSMProblems/v1.2.0/... +# .../blob/gh-pages/GeneralisedFilters/previews/PR42/... +function colab_root_url() + repo = get(ENV, "GITHUB_REPOSITORY", "TuringLang/SSMProblems.jl") + deploy_folder = if haskey(ENV, "GITHUB_ACTIONS") + if get(ENV, "GITHUB_EVENT_NAME", nothing) == "push" + ref = get(ENV, "GITHUB_REF", "") + m = match(r"^refs\/tags\/(.*)$", ref) + m !== nothing ? String(m.captures[1]) : "dev" + elseif (m = match(r"refs\/pull\/(\d+)\/merge", get(ENV, "GITHUB_REF", ""))) !== + nothing + "previews/PR$(m.captures[1])" + else + "dev" + end + else + "dev" + end + return "https://colab.research.google.com/github/$(repo)/blob/gh-pages/$(PKG_NAME)/$(deploy_folder)" +end + +const COLAB_ROOT_URL = colab_root_url() + +# Preprocess for markdown: replace @__COLAB_ROOT_URL__ placeholder +function replace_colab_url(content) + return replace(content, "@__COLAB_ROOT_URL__" => COLAB_ROOT_URL) +end + +# Preprocess for notebooks: prepend a Colab-friendly setup cell that installs packages +# and downloads auxiliary files. Uses #nb so these lines only appear in notebooks. +function insert_colab_preamble(content) + lines = String[] + + push!(lines, "#nb # ## Environment Setup") + push!(lines, "#nb #") + push!( + lines, "#nb # Install required packages (for Google Colab or fresh environments)." + ) + push!(lines, "#nb import Pkg") + push!( + lines, + "#nb Pkg.add(url=\"$(REPO_URL)\", subdir=\"SSMProblems\", rev=\"$(GIT_REV)\")", + ) + + if PKG_NAME == "GeneralisedFilters" + push!( + lines, + "#nb Pkg.add(url=\"$(REPO_URL)\", subdir=\"GeneralisedFilters\", rev=\"$(GIT_REV)\")", + ) + end + + # Parse extra dependencies from Project.toml + project_toml_path = joinpath(EXAMPLEPATH, "Project.toml") + if isfile(project_toml_path) + deps = String[] + in_deps = false + for line in readlines(project_toml_path) + if startswith(line, "[deps]") + in_deps = true + continue + elseif startswith(line, "[") && in_deps + break + end + if in_deps && occursin("=", line) + pkg = strip(split(line, "=")[1]) + if pkg ∉ ("SSMProblems", "GeneralisedFilters", "Literate") + push!(deps, "\"$pkg\"") + end + end + end + if !isempty(deps) + push!(lines, "#nb Pkg.add([$(join(deps, ", "))])") + end + end + + # Download auxiliary files (data, utility scripts) for Colab + for file in readdir(EXAMPLEPATH) + if !endswith(file, ".toml") && + !isdir(joinpath(EXAMPLEPATH, file)) && + !startswith(file, "script") + url = "https://raw.githubusercontent.com/TuringLang/SSMProblems.jl/$(GIT_REV)/$(PKG_NAME)/examples/$(EXAMPLE)/$(file)" + push!(lines, "#nb download(\"$(url)\", \"$(file)\")") + end + end + + push!(lines, "") # blank line before original content + return join(lines, "\n") * "\n" * content +end + +# Postprocess for notebooks: fix kernelspec to be "julia" instead of "julia-1.x" for Colab support +function fix_kernelspec(nb) + if haskey(nb, "metadata") && haskey(nb["metadata"], "kernelspec") + nb["metadata"]["kernelspec"]["name"] = "julia" + end + return nb +end + +# Process the Literate-formatted script in the example directory +let scriptjl = joinpath(EXAMPLEPATH, "script.jl") + # Generate executed markdown for Documenter (with Colab URL replacement) + Literate.markdown( + scriptjl, OUTDIR; name=EXAMPLE, execute=true, preprocess=replace_colab_url + ) + # Generate notebook with Colab preamble + Literate.notebook( + scriptjl, + OUTDIR; + name=EXAMPLE, + execute=true, + preprocess=insert_colab_preamble, + postprocess=fix_kernelspec, + ) +end diff --git a/GeneralisedFilters/examples/trend-inflation/script.jl b/GeneralisedFilters/examples/trend-inflation/script.jl index 126db1db..f2d7970b 100644 --- a/GeneralisedFilters/examples/trend-inflation/script.jl +++ b/GeneralisedFilters/examples/trend-inflation/script.jl @@ -1,5 +1,7 @@ # # Trend Inflation # +#md # [![](https://colab.research.google.com/assets/colab-badge.svg)](@__COLAB_ROOT_URL__/examples/trend-inflation.ipynb) +# # This example is a replication of the univariate state space model suggested by (Stock & # Watson, 2016) using GeneralisedFilters to define a heirarchical model for use in Rao- # Blackwellised particle filtering. @@ -15,8 +17,10 @@ using PDMats const GF = GeneralisedFilters INFL_PATH = joinpath(@__DIR__, "..", "..", "..", "examples", "trend-inflation"); #hide +#nb INFL_PATH = @__DIR__ # INFL_PATH = joinpath(@__DIR__) include(joinpath(INFL_PATH, "utilities.jl")); #hide +#nb include(joinpath(INFL_PATH, "utilities.jl")) # ## Model Definition diff --git a/README.md b/README.md index 5ac01fb4..3ac1263f 100644 --- a/README.md +++ b/README.md @@ -100,3 +100,13 @@ function distribution( return Normal(state, sig_v) end ``` + +## Examples + +You can run our examples directly in your browser using Google Colab: + +| Example | Colab Link | +| :------ | :--------: | +| Trend Inflation (GeneralisedFilters) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/TuringLang/SSMProblems.jl/blob/gh-pages/GeneralisedFilters/dev/examples/trend-inflation.ipynb) | +| Kalman Filter (SSMProblems) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/TuringLang/SSMProblems.jl/blob/gh-pages/SSMProblems/dev/examples/kalman-filter.ipynb) | + diff --git a/SSMProblems/docs/Project.toml b/SSMProblems/docs/Project.toml index cf645b52..c8faa358 100644 --- a/SSMProblems/docs/Project.toml +++ b/SSMProblems/docs/Project.toml @@ -1,3 +1,4 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" +SSMProblems = "26aad666-b158-4e64-9d35-0e672562fa48" diff --git a/SSMProblems/docs/literate.jl b/SSMProblems/docs/literate.jl index 879ec107..d432c24b 100644 --- a/SSMProblems/docs/literate.jl +++ b/SSMProblems/docs/literate.jl @@ -13,6 +13,142 @@ Pkg.activate(EXAMPLEPATH) Pkg.instantiate() using Literate: Literate -# Convert to markdown and notebook -const SCRIPTJL = joinpath(EXAMPLEPATH, "script.jl") -Literate.markdown(SCRIPTJL, OUTDIR; name=EXAMPLE, execute=true) +# Determine the package name (GeneralisedFilters or SSMProblems) +const PKG_NAME = if occursin("GeneralisedFilters", abspath(EXAMPLEPATH)) + "GeneralisedFilters" +else + "SSMProblems" +end + +# Git revision for Pkg.add — use the current commit/branch on CI so notebooks match the docs build +const GIT_REV = if haskey(ENV, "GITHUB_ACTIONS") + ref = get(ENV, "GITHUB_REF", "") + if startswith(ref, "refs/heads/") + String(match(r"refs\/heads\/(.*)", ref).captures[1]) + elseif startswith(ref, "refs/tags/") + String(match(r"refs\/tags\/(.*)", ref).captures[1]) + else + get(ENV, "GITHUB_SHA", "main") + end +else + "main" +end + +const REPO_URL = "https://github.com/TuringLang/SSMProblems.jl" + +# Compute a version/PR-aware Colab root URL, mirroring Literate.jl's deploy folder logic. +# On CI this produces URLs like: +# .../blob/gh-pages/SSMProblems/dev/... +# .../blob/gh-pages/SSMProblems/v1.2.0/... +# .../blob/gh-pages/SSMProblems/previews/PR42/... +function colab_root_url() + repo = get(ENV, "GITHUB_REPOSITORY", "TuringLang/SSMProblems.jl") + deploy_folder = if haskey(ENV, "GITHUB_ACTIONS") + if get(ENV, "GITHUB_EVENT_NAME", nothing) == "push" + ref = get(ENV, "GITHUB_REF", "") + m = match(r"^refs\/tags\/(.*)$", ref) + m !== nothing ? String(m.captures[1]) : "dev" + elseif (m = match(r"refs\/pull\/(\d+)\/merge", get(ENV, "GITHUB_REF", ""))) !== + nothing + "previews/PR$(m.captures[1])" + else + "dev" + end + else + "dev" + end + return "https://colab.research.google.com/github/$(repo)/blob/gh-pages/$(PKG_NAME)/$(deploy_folder)" +end + +const COLAB_ROOT_URL = colab_root_url() + +# Preprocess for markdown: replace @__COLAB_ROOT_URL__ placeholder +function replace_colab_url(content) + return replace(content, "@__COLAB_ROOT_URL__" => COLAB_ROOT_URL) +end + +# Preprocess for notebooks: prepend a Colab-friendly setup cell that installs packages +# and downloads auxiliary files. Uses #nb so these lines only appear in notebooks. +function insert_colab_preamble(content) + lines = String[] + + push!(lines, "#nb # ## Environment Setup") + push!(lines, "#nb #") + push!( + lines, "#nb # Install required packages (for Google Colab or fresh environments)." + ) + push!(lines, "#nb import Pkg") + push!( + lines, + "#nb Pkg.add(url=\"$(REPO_URL)\", subdir=\"SSMProblems\", rev=\"$(GIT_REV)\")", + ) + + if PKG_NAME == "GeneralisedFilters" + push!( + lines, + "#nb Pkg.add(url=\"$(REPO_URL)\", subdir=\"GeneralisedFilters\", rev=\"$(GIT_REV)\")", + ) + end + + # Parse extra dependencies from Project.toml + project_toml_path = joinpath(EXAMPLEPATH, "Project.toml") + if isfile(project_toml_path) + deps = String[] + in_deps = false + for line in readlines(project_toml_path) + if startswith(line, "[deps]") + in_deps = true + continue + elseif startswith(line, "[") && in_deps + break + end + if in_deps && occursin("=", line) + pkg = strip(split(line, "=")[1]) + if pkg ∉ ("SSMProblems", "GeneralisedFilters", "Literate") + push!(deps, "\"$pkg\"") + end + end + end + if !isempty(deps) + push!(lines, "#nb Pkg.add([$(join(deps, ", "))])") + end + end + + # Download auxiliary files (data, utility scripts) for Colab + for file in readdir(EXAMPLEPATH) + if !endswith(file, ".toml") && + !isdir(joinpath(EXAMPLEPATH, file)) && + !startswith(file, "script") + url = "https://raw.githubusercontent.com/TuringLang/SSMProblems.jl/$(GIT_REV)/$(PKG_NAME)/examples/$(EXAMPLE)/$(file)" + push!(lines, "#nb download(\"$(url)\", \"$(file)\")") + end + end + + push!(lines, "") # blank line before original content + return join(lines, "\n") * "\n" * content +end + +# Postprocess for notebooks: fix kernelspec to be "julia" instead of "julia-1.x" for Colab support +function fix_kernelspec(nb) + if haskey(nb, "metadata") && haskey(nb["metadata"], "kernelspec") + nb["metadata"]["kernelspec"]["name"] = "julia" + end + return nb +end + +# Process the Literate-formatted script in the example directory +let scriptjl = joinpath(EXAMPLEPATH, "script.jl") + # Generate executed markdown for Documenter (with Colab URL replacement) + Literate.markdown( + scriptjl, OUTDIR; name=EXAMPLE, execute=true, preprocess=replace_colab_url + ) + # Generate notebook with Colab preamble + Literate.notebook( + scriptjl, + OUTDIR; + name=EXAMPLE, + execute=true, + preprocess=insert_colab_preamble, + postprocess=fix_kernelspec, + ) +end diff --git a/SSMProblems/examples/kalman-filter/script.jl b/SSMProblems/examples/kalman-filter/script.jl index d924f69a..943e38a7 100644 --- a/SSMProblems/examples/kalman-filter/script.jl +++ b/SSMProblems/examples/kalman-filter/script.jl @@ -1,5 +1,7 @@ # # Kalman Filter # +#md # [![](https://colab.research.google.com/assets/colab-badge.svg)](@__COLAB_ROOT_URL__/examples/kalman-filter.ipynb) +# # This example implements a Kalman filter for a linear Gaussian state space model using the # SSMProblems interface.