From 432870604e400c6b977ad9e8b3c4155f00622d75 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Mon, 24 Jun 2024 16:57:13 +0000 Subject: [PATCH] build based on ce00d76 --- dev/.documenter-siteinfo.json | 2 +- dev/api/index.html | 18 +- dev/assets/documenter.js | 923 +++++++++++++++---------- dev/assets/themes/documenter-dark.css | 2 +- dev/assets/themes/documenter-light.css | 2 +- dev/design/index.html | 2 +- dev/index.html | 2 +- dev/objects.inv | 9 + 8 files changed, 565 insertions(+), 395 deletions(-) create mode 100644 dev/objects.inv diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 3f022a23..80e9301f 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.1","generation_timestamp":"2024-02-26T22:59:53","documenter_version":"1.2.1"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-06-24T16:57:10","documenter_version":"1.4.1"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index 487a2647..02231381 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,17 +1,17 @@ -API · AbstractMCMC

API

AbstractMCMC defines an interface for sampling Markov chains.

Model

AbstractMCMC.LogDensityModelType
LogDensityModel <: AbstractMCMC.AbstractModel

Wrapper around something that implements the LogDensityProblem.jl interface.

Note that this does not implement the LogDensityProblems.jl interface itself, but it simply useful for indicating to the sample and other AbstractMCMC methods that the wrapped object implements the LogDensityProblems.jl interface.

Fields

  • logdensity: The object that implements the LogDensityProblems.jl interface.
source

Sampler

AbstractMCMC.AbstractSamplerType
AbstractSampler

The AbstractSampler type is intended to be inherited from when implementing a custom sampler. Any persistent state information should be saved in a subtype of AbstractSampler.

When defining a new sampler, you should also overload the function transition_type, which tells the sample function what type of parameter it should expect to receive.

source

Sampling a single chain

StatsBase.sampleMethod
sample(
+API · AbstractMCMC

API

AbstractMCMC defines an interface for sampling Markov chains.

Model

AbstractMCMC.LogDensityModelType
LogDensityModel <: AbstractMCMC.AbstractModel

Wrapper around something that implements the LogDensityProblem.jl interface.

Note that this does not implement the LogDensityProblems.jl interface itself, but it simply useful for indicating to the sample and other AbstractMCMC methods that the wrapped object implements the LogDensityProblems.jl interface.

Fields

  • logdensity: The object that implements the LogDensityProblems.jl interface.
source

Sampler

AbstractMCMC.AbstractSamplerType
AbstractSampler

The AbstractSampler type is intended to be inherited from when implementing a custom sampler. Any persistent state information should be saved in a subtype of AbstractSampler.

When defining a new sampler, you should also overload the function transition_type, which tells the sample function what type of parameter it should expect to receive.

source

Sampling a single chain

StatsBase.sampleMethod
sample(
     rng::Random.AbatractRNG=Random.default_rng(),
     model::AbstractModel,
     sampler::AbstractSampler,
     N_or_isdone;
     kwargs...,
-)

Sample from the model with the Markov chain Monte Carlo sampler and return the samples.

If N_or_isdone is an Integer, exactly N_or_isdone samples are returned.

Otherwise, sampling is performed until a convergence criterion N_or_isdone returns true. The convergence criterion has to be a function with the signature

isdone(rng, model, sampler, samples, state, iteration; kwargs...)

where state and iteration are the current state and iteration of the sampler, respectively. It should return true when sampling should end, and false otherwise.

source
StatsBase.sampleMethod
sample(
+)

Sample from the model with the Markov chain Monte Carlo sampler and return the samples.

If N_or_isdone is an Integer, exactly N_or_isdone samples are returned.

Otherwise, sampling is performed until a convergence criterion N_or_isdone returns true. The convergence criterion has to be a function with the signature

isdone(rng, model, sampler, samples, state, iteration; kwargs...)

where state and iteration are the current state and iteration of the sampler, respectively. It should return true when sampling should end, and false otherwise.

source
StatsBase.sampleMethod
sample(
     rng::Random.AbstractRNG=Random.default_rng(),
     logdensity,
     sampler::AbstractSampler,
     N_or_isdone;
     kwargs...,
-)

Wrap the logdensity function in a LogDensityModel, and call sample with the resulting model instead of logdensity.

The logdensity function has to support the LogDensityProblems.jl interface.

source

Iterator

Iterator

AbstractMCMC.stepsMethod
steps(
     rng::Random.AbstractRNG=Random.default_rng(),
     model::AbstractModel,
     sampler::AbstractSampler;
@@ -28,12 +28,12 @@
 julia> iterator = steps(MyModel(), MySampler());
 
 julia> collect(Iterators.take(iterator, 10)) == zeros(10)
-true
source
AbstractMCMC.stepsMethod
steps(
     rng::Random.AbstractRNG=Random.default_rng(),
     logdensity,
     sampler::AbstractSampler;
     kwargs...,
-)

Wrap the logdensity function in a LogDensityModel, and call steps with the resulting model instead of logdensity.

The logdensity function has to support the LogDensityProblems.jl interface.

source

Transducer

Transducer

AbstractMCMC.SampleMethod
Sample(
     rng::Random.AbstractRNG=Random.default_rng(),
     model::AbstractModel,
     sampler::AbstractSampler;
@@ -50,12 +50,12 @@
 julia> transducer = Sample(MyModel(), MySampler());
 
 julia> collect(transducer(1:10)) == zeros(10)
-true
source
AbstractMCMC.SampleMethod
Sample(
     rng::Random.AbstractRNG=Random.default_rng(),
     logdensity,
     sampler::AbstractSampler;
     kwargs...,
-)

Wrap the logdensity function in a LogDensityModel, and call Sample with the resulting model instead of logdensity.

The logdensity function has to support the LogDensityProblems.jl interface.

source

Sampling multiple chains in parallel

Sampling multiple chains in parallel

StatsBase.sampleMethod
sample(
     rng::Random.AbstractRNG=Random.default_rng(),
     model::AbstractModel,
     sampler::AbstractSampler,
@@ -63,7 +63,7 @@
     N::Integer,
     nchains::Integer;
     kwargs...,
-)

Sample nchains Monte Carlo Markov chains from the model with the sampler in parallel using the parallel algorithm, and combine them into a single chain.

source
StatsBase.sampleMethod
sample(
+)

Sample nchains Monte Carlo Markov chains from the model with the sampler in parallel using the parallel algorithm, and combine them into a single chain.

source
StatsBase.sampleMethod
sample(
     rng::Random.AbstractRNG=Random.default_rng(),
     logdensity,
     sampler::AbstractSampler,
@@ -71,4 +71,4 @@
     N::Integer,
     nchains::Integer;
     kwargs...,
-)

Wrap the logdensity function in a LogDensityModel, and call sample with the resulting model instead of logdensity.

The logdensity function has to support the LogDensityProblems.jl interface.

source

Two algorithms are provided for parallel sampling with multiple threads and multiple processes, and one allows for the user to sample multiple chains in serial (no parallelization):

Common keyword arguments

Common keyword arguments for regular and parallel sampling are:

  • progress (default: AbstractMCMC.PROGRESS[] which is true initially): toggles progress logging
  • chain_type (default: Any): determines the type of the returned chain
  • callback (default: nothing): if callback !== nothing, then callback(rng, model, sampler, sample, state, iteration) is called after every sampling step, where sample is the most recent sample of the Markov chain and state and iteration are the current state and iteration of the sampler
  • discard_initial (default: 0): number of initial samples that are discarded
  • thinning (default: 1): factor by which to thin samples.
  • initial_state (default: nothing): if initial_state !== nothing, the first call to AbstractMCMC.step is passed initial_state as the state argument.
Info

The common keyword arguments progress, chain_type, and callback are not supported by the iterator AbstractMCMC.steps and the transducer AbstractMCMC.Sample.

There is no "official" way for providing initial parameter values yet. However, multiple packages such as EllipticalSliceSampling.jl and AdvancedMH.jl support an initial_params keyword argument for setting the initial values when sampling a single chain. To ensure that sampling multiple chains "just works" when sampling of a single chain is implemented, we decided to support initial_params in the default implementations of the ensemble methods:

  • initial_params (default: nothing): if initial_params isa AbstractArray, then the ith element of initial_params is used as initial parameters of the ith chain. If one wants to use the same initial parameters x for every chain, one can specify e.g. initial_params = FillArrays.Fill(x, N).

Progress logging can be enabled and disabled globally with AbstractMCMC.setprogress!(progress).

AbstractMCMC.setprogress!Function
setprogress!(progress::Bool; silent::Bool=false)

Enable progress logging globally if progress is true, and disable it otherwise. Optionally disable informational message if silent is true.

source

Chains

The chain_type keyword argument allows to set the type of the returned chain. A common choice is to return chains of type Chains from MCMCChains.jl.

AbstractMCMC defines the abstract type AbstractChains for Markov chains.

AbstractMCMC.AbstractChainsType
AbstractChains

AbstractChains is an abstract type for an object that stores parameter samples generated through a MCMC process.

source

For chains of this type, AbstractMCMC defines the following two methods.

AbstractMCMC.chainscatFunction
chainscat(c::AbstractChains...)

Concatenate multiple chains.

By default, the chains are concatenated along the third dimension by calling cat(c...; dims=3).

source
AbstractMCMC.chainsstackFunction
chainsstack(c::AbstractVector)

Stack chains in c.

By default, the vector of chains is returned unmodified. If eltype(c) <: AbstractChains, then reduce(chainscat, c) is called.

source
+)

Wrap the logdensity function in a LogDensityModel, and call sample with the resulting model instead of logdensity.

The logdensity function has to support the LogDensityProblems.jl interface.

source

Two algorithms are provided for parallel sampling with multiple threads and multiple processes, and one allows for the user to sample multiple chains in serial (no parallelization):

Common keyword arguments

Common keyword arguments for regular and parallel sampling are:

  • progress (default: AbstractMCMC.PROGRESS[] which is true initially): toggles progress logging
  • chain_type (default: Any): determines the type of the returned chain
  • callback (default: nothing): if callback !== nothing, then callback(rng, model, sampler, sample, state, iteration) is called after every sampling step, where sample is the most recent sample of the Markov chain and state and iteration are the current state and iteration of the sampler
  • discard_initial (default: 0): number of initial samples that are discarded
  • thinning (default: 1): factor by which to thin samples.
  • initial_state (default: nothing): if initial_state !== nothing, the first call to AbstractMCMC.step is passed initial_state as the state argument.
Info

The common keyword arguments progress, chain_type, and callback are not supported by the iterator AbstractMCMC.steps and the transducer AbstractMCMC.Sample.

There is no "official" way for providing initial parameter values yet. However, multiple packages such as EllipticalSliceSampling.jl and AdvancedMH.jl support an initial_params keyword argument for setting the initial values when sampling a single chain. To ensure that sampling multiple chains "just works" when sampling of a single chain is implemented, we decided to support initial_params in the default implementations of the ensemble methods:

  • initial_params (default: nothing): if initial_params isa AbstractArray, then the ith element of initial_params is used as initial parameters of the ith chain. If one wants to use the same initial parameters x for every chain, one can specify e.g. initial_params = FillArrays.Fill(x, N).

Progress logging can be enabled and disabled globally with AbstractMCMC.setprogress!(progress).

AbstractMCMC.setprogress!Function
setprogress!(progress::Bool; silent::Bool=false)

Enable progress logging globally if progress is true, and disable it otherwise. Optionally disable informational message if silent is true.

source

Chains

The chain_type keyword argument allows to set the type of the returned chain. A common choice is to return chains of type Chains from MCMCChains.jl.

AbstractMCMC defines the abstract type AbstractChains for Markov chains.

AbstractMCMC.AbstractChainsType
AbstractChains

AbstractChains is an abstract type for an object that stores parameter samples generated through a MCMC process.

source

For chains of this type, AbstractMCMC defines the following two methods.

AbstractMCMC.chainscatFunction
chainscat(c::AbstractChains...)

Concatenate multiple chains.

By default, the chains are concatenated along the third dimension by calling cat(c...; dims=3).

source
AbstractMCMC.chainsstackFunction
chainsstack(c::AbstractVector)

Stack chains in c.

By default, the vector of chains is returned unmodified. If eltype(c) <: AbstractChains, then reduce(chainscat, c) is called.

source
diff --git a/dev/assets/documenter.js b/dev/assets/documenter.js index f5311607..c6562b55 100644 --- a/dev/assets/documenter.js +++ b/dev/assets/documenter.js @@ -4,7 +4,6 @@ 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', - 'minisearch': 'https://cdn.jsdelivr.net/npm/minisearch@6.1.0/dist/umd/index.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', @@ -103,9 +102,10 @@ $(document).on("click", ".docstring header", function () { }); }); -$(document).on("click", ".docs-article-toggle-button", function () { +$(document).on("click", ".docs-article-toggle-button", function (event) { let articleToggleTitle = "Expand docstring"; let navArticleToggleTitle = "Expand all docstrings"; + let animationSpeed = event.noToggleAnimation ? 0 : 400; debounce(() => { if (isExpanded) { @@ -116,7 +116,7 @@ $(document).on("click", ".docs-article-toggle-button", function () { isExpanded = false; - $(".docstring section").slideUp(); + $(".docstring section").slideUp(animationSpeed); } else { $(this).removeClass("fa-chevron-down").addClass("fa-chevron-up"); $(".docstring-article-toggle-button") @@ -127,7 +127,7 @@ $(document).on("click", ".docs-article-toggle-button", function () { articleToggleTitle = "Collapse docstring"; navArticleToggleTitle = "Collapse all docstrings"; - $(".docstring section").slideDown(); + $(".docstring section").slideDown(animationSpeed); } $(this).prop("title", navArticleToggleTitle); @@ -224,224 +224,465 @@ $(document).ready(function () { }) //////////////////////////////////////////////////////////////////////////////// -require(['jquery', 'minisearch'], function($, minisearch) { - -// In general, most search related things will have "search" as a prefix. -// To get an in-depth about the thought process you can refer: https://hetarth02.hashnode.dev/series/gsoc +require(['jquery'], function($) { -let results = []; -let timer = undefined; +$(document).ready(function () { + let meta = $("div[data-docstringscollapsed]").data(); -let data = documenterSearchIndex["docs"].map((x, key) => { - x["id"] = key; // minisearch requires a unique for each object - return x; + if (meta?.docstringscollapsed) { + $("#documenter-article-toggle-button").trigger({ + type: "click", + noToggleAnimation: true, + }); + } }); -// list below is the lunr 2.1.3 list minus the intersect with names(Base) -// (all, any, get, in, is, only, which) and (do, else, for, let, where, while, with) -// ideally we'd just filter the original list but it's not available as a variable -const stopWords = new Set([ - "a", - "able", - "about", - "across", - "after", - "almost", - "also", - "am", - "among", - "an", - "and", - "are", - "as", - "at", - "be", - "because", - "been", - "but", - "by", - "can", - "cannot", - "could", - "dear", - "did", - "does", - "either", - "ever", - "every", - "from", - "got", - "had", - "has", - "have", - "he", - "her", - "hers", - "him", - "his", - "how", - "however", - "i", - "if", - "into", - "it", - "its", - "just", - "least", - "like", - "likely", - "may", - "me", - "might", - "most", - "must", - "my", - "neither", - "no", - "nor", - "not", - "of", - "off", - "often", - "on", - "or", - "other", - "our", - "own", - "rather", - "said", - "say", - "says", - "she", - "should", - "since", - "so", - "some", - "than", - "that", - "the", - "their", - "them", - "then", - "there", - "these", - "they", - "this", - "tis", - "to", - "too", - "twas", - "us", - "wants", - "was", - "we", - "were", - "what", - "when", - "who", - "whom", - "why", - "will", - "would", - "yet", - "you", - "your", -]); - -let index = new minisearch({ - fields: ["title", "text"], // fields to index for full-text search - storeFields: ["location", "title", "text", "category", "page"], // fields to return with search results - processTerm: (term) => { - let word = stopWords.has(term) ? null : term; - if (word) { - // custom trimmer that doesn't strip @ and !, which are used in julia macro and function names - word = word - .replace(/^[^a-zA-Z0-9@!]+/, "") - .replace(/[^a-zA-Z0-9@!]+$/, ""); - } +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { - return word ?? null; - }, - // add . as a separator, because otherwise "title": "Documenter.Anchors.add!", would not find anything if searching for "add!", only for the entire qualification - tokenize: (string) => string.split(/[\s\-\.]+/), - // options which will be applied during the search - searchOptions: { - boost: { title: 100 }, - fuzzy: 2, +/* +To get an in-depth about the thought process you can refer: https://hetarth02.hashnode.dev/series/gsoc + +PSEUDOCODE: + +Searching happens automatically as the user types or adjusts the selected filters. +To preserve responsiveness, as much as possible of the slow parts of the search are done +in a web worker. Searching and result generation are done in the worker, and filtering and +DOM updates are done in the main thread. The filters are in the main thread as they should +be very quick to apply. This lets filters be changed without re-searching with minisearch +(which is possible even if filtering is on the worker thread) and also lets filters be +changed _while_ the worker is searching and without message passing (neither of which are +possible if filtering is on the worker thread) + +SEARCH WORKER: + +Import minisearch + +Build index + +On message from main thread + run search + find the first 200 unique results from each category, and compute their divs for display + note that this is necessary and sufficient information for the main thread to find the + first 200 unique results from any given filter set + post results to main thread + +MAIN: + +Launch worker + +Declare nonconstant globals (worker_is_running, last_search_text, unfiltered_results) + +On text update + if worker is not running, launch_search() + +launch_search + set worker_is_running to true, set last_search_text to the search text + post the search query to worker + +on message from worker + if last_search_text is not the same as the text in the search field, + the latest search result is not reflective of the latest search query, so update again + launch_search() + otherwise + set worker_is_running to false + + regardless, display the new search results to the user + save the unfiltered_results as a global + update_search() + +on filter click + adjust the filter selection + update_search() + +update_search + apply search filters by looping through the unfiltered_results and finding the first 200 + unique results that match the filters + + Update the DOM +*/ + +/////// SEARCH WORKER /////// + +function worker_function(documenterSearchIndex, documenterBaseURL, filters) { + importScripts( + "https://cdn.jsdelivr.net/npm/minisearch@6.1.0/dist/umd/index.min.js" + ); + + let data = documenterSearchIndex.map((x, key) => { + x["id"] = key; // minisearch requires a unique for each object + return x; + }); + + // list below is the lunr 2.1.3 list minus the intersect with names(Base) + // (all, any, get, in, is, only, which) and (do, else, for, let, where, while, with) + // ideally we'd just filter the original list but it's not available as a variable + const stopWords = new Set([ + "a", + "able", + "about", + "across", + "after", + "almost", + "also", + "am", + "among", + "an", + "and", + "are", + "as", + "at", + "be", + "because", + "been", + "but", + "by", + "can", + "cannot", + "could", + "dear", + "did", + "does", + "either", + "ever", + "every", + "from", + "got", + "had", + "has", + "have", + "he", + "her", + "hers", + "him", + "his", + "how", + "however", + "i", + "if", + "into", + "it", + "its", + "just", + "least", + "like", + "likely", + "may", + "me", + "might", + "most", + "must", + "my", + "neither", + "no", + "nor", + "not", + "of", + "off", + "often", + "on", + "or", + "other", + "our", + "own", + "rather", + "said", + "say", + "says", + "she", + "should", + "since", + "so", + "some", + "than", + "that", + "the", + "their", + "them", + "then", + "there", + "these", + "they", + "this", + "tis", + "to", + "too", + "twas", + "us", + "wants", + "was", + "we", + "were", + "what", + "when", + "who", + "whom", + "why", + "will", + "would", + "yet", + "you", + "your", + ]); + + let index = new MiniSearch({ + fields: ["title", "text"], // fields to index for full-text search + storeFields: ["location", "title", "text", "category", "page"], // fields to return with results processTerm: (term) => { let word = stopWords.has(term) ? null : term; if (word) { + // custom trimmer that doesn't strip @ and !, which are used in julia macro and function names word = word .replace(/^[^a-zA-Z0-9@!]+/, "") .replace(/[^a-zA-Z0-9@!]+$/, ""); + + word = word.toLowerCase(); } return word ?? null; }, + // add . as a separator, because otherwise "title": "Documenter.Anchors.add!", would not + // find anything if searching for "add!", only for the entire qualification tokenize: (string) => string.split(/[\s\-\.]+/), - }, -}); + // options which will be applied during the search + searchOptions: { + prefix: true, + boost: { title: 100 }, + fuzzy: 2, + }, + }); -index.addAll(data); + index.addAll(data); + + /** + * Used to map characters to HTML entities. + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + const htmlEscapes = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + }; + + /** + * Used to match HTML entities and HTML characters. + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + const reUnescapedHtml = /[&<>"']/g; + const reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** + * Escape function from lodash + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + function escape(string) { + return string && reHasUnescapedHtml.test(string) + ? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr]) + : string || ""; + } -let filters = [...new Set(data.map((x) => x.category))]; -var modal_filters = make_modal_body_filters(filters); -var filter_results = []; + /** + * Make the result component given a minisearch result data object and the value + * of the search input as queryString. To view the result object structure, refer: + * https://lucaong.github.io/minisearch/modules/_minisearch_.html#searchresult + * + * @param {object} result + * @param {string} querystring + * @returns string + */ + function make_search_result(result, querystring) { + let search_divider = `
`; + let display_link = + result.location.slice(Math.max(0), Math.min(50, result.location.length)) + + (result.location.length > 30 ? "..." : ""); // To cut-off the link because it messes with the overflow of the whole div + + if (result.page !== "") { + display_link += ` (${result.page})`; + } -$(document).on("keyup", ".documenter-search-input", function (event) { - // Adding a debounce to prevent disruptions from super-speed typing! - debounce(() => update_search(filter_results), 300); + let textindex = new RegExp(`${querystring}`, "i").exec(result.text); + let text = + textindex !== null + ? result.text.slice( + Math.max(textindex.index - 100, 0), + Math.min( + textindex.index + querystring.length + 100, + result.text.length + ) + ) + : ""; // cut-off text before and after from the match + + text = text.length ? escape(text) : ""; + + let display_result = text.length + ? "..." + + text.replace( + new RegExp(`${escape(querystring)}`, "i"), // For first occurrence + '$&' + ) + + "..." + : ""; // highlights the match + + let in_code = false; + if (!["page", "section"].includes(result.category.toLowerCase())) { + in_code = true; + } + + // We encode the full url to escape some special characters which can lead to broken links + let result_div = ` + +
+
${escape(result.title)}
+
${result.category}
+
+

+ ${display_result} +

+
+ ${display_link} +
+
+ ${search_divider} + `; + + return result_div; + } + + self.onmessage = function (e) { + let query = e.data; + let results = index.search(query, { + filter: (result) => { + // Only return relevant results + return result.score >= 1; + }, + }); + + // Pre-filter to deduplicate and limit to 200 per category to the extent + // possible without knowing what the filters are. + let filtered_results = []; + let counts = {}; + for (let filter of filters) { + counts[filter] = 0; + } + let present = {}; + + for (let result of results) { + cat = result.category; + cnt = counts[cat]; + if (cnt < 200) { + id = cat + "---" + result.location; + if (present[id]) { + continue; + } + present[id] = true; + filtered_results.push({ + location: result.location, + category: cat, + div: make_search_result(result, query), + }); + } + } + + postMessage(filtered_results); + }; +} + +// `worker = Threads.@spawn worker_function(documenterSearchIndex)`, but in JavaScript! +const filters = [ + ...new Set(documenterSearchIndex["docs"].map((x) => x.category)), +]; +const worker_str = + "(" + + worker_function.toString() + + ")(" + + JSON.stringify(documenterSearchIndex["docs"]) + + "," + + JSON.stringify(documenterBaseURL) + + "," + + JSON.stringify(filters) + + ")"; +const worker_blob = new Blob([worker_str], { type: "text/javascript" }); +const worker = new Worker(URL.createObjectURL(worker_blob)); + +/////// SEARCH MAIN /////// + +// Whether the worker is currently handling a search. This is a boolean +// as the worker only ever handles 1 or 0 searches at a time. +var worker_is_running = false; + +// The last search text that was sent to the worker. This is used to determine +// if the worker should be launched again when it reports back results. +var last_search_text = ""; + +// The results of the last search. This, in combination with the state of the filters +// in the DOM, is used compute the results to display on calls to update_search. +var unfiltered_results = []; + +// Which filter is currently selected +var selected_filter = ""; + +$(document).on("input", ".documenter-search-input", function (event) { + if (!worker_is_running) { + launch_search(); + } }); +function launch_search() { + worker_is_running = true; + last_search_text = $(".documenter-search-input").val(); + worker.postMessage(last_search_text); +} + +worker.onmessage = function (e) { + if (last_search_text !== $(".documenter-search-input").val()) { + launch_search(); + } else { + worker_is_running = false; + } + + unfiltered_results = e.data; + update_search(); +}; + $(document).on("click", ".search-filter", function () { if ($(this).hasClass("search-filter-selected")) { - $(this).removeClass("search-filter-selected"); + selected_filter = ""; } else { - $(this).addClass("search-filter-selected"); + selected_filter = $(this).text().toLowerCase(); } - // Adding a debounce to prevent disruptions from crazy clicking! - debounce(() => get_filters(), 300); + // This updates search results and toggles classes for UI: + update_search(); }); -/** - * A debounce function, takes a function and an optional timeout in milliseconds - * - * @function callback - * @param {number} timeout - */ -function debounce(callback, timeout = 300) { - clearTimeout(timer); - timer = setTimeout(callback, timeout); -} - /** * Make/Update the search component - * - * @param {string[]} selected_filters */ -function update_search(selected_filters = []) { - let initial_search_body = ` -
Type something to get started!
- `; - +function update_search() { let querystring = $(".documenter-search-input").val(); if (querystring.trim()) { - results = index.search(querystring, { - filter: (result) => { - // Filtering results - if (selected_filters.length === 0) { - return result.score >= 1; - } else { - return ( - result.score >= 1 && selected_filters.includes(result.category) - ); - } - }, - }); + if (selected_filter == "") { + results = unfiltered_results; + } else { + results = unfiltered_results.filter((result) => { + return selected_filter == result.category.toLowerCase(); + }); + } let search_result_container = ``; + let modal_filters = make_modal_body_filters(); let search_divider = `
`; if (results.length) { @@ -449,19 +690,23 @@ function update_search(selected_filters = []) { let count = 0; let search_results = ""; - results.forEach(function (result) { - if (result.location) { - // Checking for duplication of results for the same page - if (!links.includes(result.location)) { - search_results += make_search_result(result, querystring); - count++; - } - + for (var i = 0, n = results.length; i < n && count < 200; ++i) { + let result = results[i]; + if (result.location && !links.includes(result.location)) { + search_results += result.div; + count++; links.push(result.location); } - }); + } - let result_count = `
${count} result(s)
`; + if (count == 1) { + count_str = "1 result"; + } else if (count == 200) { + count_str = "200+ results"; + } else { + count_str = count + " results"; + } + let result_count = `
${count_str}
`; search_result_container = `
@@ -490,125 +735,37 @@ function update_search(selected_filters = []) { $(".search-modal-card-body").html(search_result_container); } else { - filter_results = []; - modal_filters = make_modal_body_filters(filters, filter_results); - if (!$(".search-modal-card-body").hasClass("is-justify-content-center")) { $(".search-modal-card-body").addClass("is-justify-content-center"); } - $(".search-modal-card-body").html(initial_search_body); + $(".search-modal-card-body").html(` +
Type something to get started!
+ `); } } /** * Make the modal filter html * - * @param {string[]} filters - * @param {string[]} selected_filters * @returns string */ -function make_modal_body_filters(filters, selected_filters = []) { - let str = ``; - - filters.forEach((val) => { - if (selected_filters.includes(val)) { - str += `${val}`; - } else { - str += `${val}`; - } - }); +function make_modal_body_filters() { + let str = filters + .map((val) => { + if (selected_filter == val.toLowerCase()) { + return `${val}`; + } else { + return `${val}`; + } + }) + .join(""); - let filter_html = ` + return `
Filters: ${str} -
- `; - - return filter_html; -} - -/** - * Make the result component given a minisearch result data object and the value of the search input as queryString. - * To view the result object structure, refer: https://lucaong.github.io/minisearch/modules/_minisearch_.html#searchresult - * - * @param {object} result - * @param {string} querystring - * @returns string - */ -function make_search_result(result, querystring) { - let search_divider = `
`; - let display_link = - result.location.slice(Math.max(0), Math.min(50, result.location.length)) + - (result.location.length > 30 ? "..." : ""); // To cut-off the link because it messes with the overflow of the whole div - - if (result.page !== "") { - display_link += ` (${result.page})`; - } - - let textindex = new RegExp(`\\b${querystring}\\b`, "i").exec(result.text); - let text = - textindex !== null - ? result.text.slice( - Math.max(textindex.index - 100, 0), - Math.min( - textindex.index + querystring.length + 100, - result.text.length - ) - ) - : ""; // cut-off text before and after from the match - - let display_result = text.length - ? "..." + - text.replace( - new RegExp(`\\b${querystring}\\b`, "i"), // For first occurrence - '$&' - ) + - "..." - : ""; // highlights the match - - let in_code = false; - if (!["page", "section"].includes(result.category.toLowerCase())) { - in_code = true; - } - - // We encode the full url to escape some special characters which can lead to broken links - let result_div = ` - -
-
${result.title}
-
${result.category}
-
-

- ${display_result} -

-
- ${display_link} -
-
- ${search_divider} - `; - - return result_div; -} - -/** - * Get selected filters, remake the filter html and lastly update the search modal - */ -function get_filters() { - let ele = $(".search-filters .search-filter-selected").get(); - filter_results = ele.map((x) => $(x).text().toLowerCase()); - modal_filters = make_modal_body_filters(filters, filter_results); - update_search(filter_results); +
`; } }) @@ -635,103 +792,107 @@ $(document).ready(function () { //////////////////////////////////////////////////////////////////////////////// require(['jquery'], function($) { -let search_modal_header = ` - -`; - -let initial_search_body = ` -
Type something to get started!
-`; - -let search_modal_footer = ` - -`; - -$(document.body).append( - ` - +end

All other default implementations make use of the same structure and in particular call the same methods.

Sampling step

The only method for which no default implementation is provided (and hence which downstream packages have to implement) is AbstractMCMC.step. It defines the sampling step of the inference method.

AbstractMCMC.stepFunction
step(rng, model, sampler[, state; kwargs...])

Return a 2-tuple of the next sample and the next state of the MCMC sampler for model.

Samples describe the results of a single step of the sampler. As an example, a sample might include a vector of parameters sampled from a prior distribution.

When sampling using sample, every step call after the first has access to the current state of the sampler.

source

Collecting samples

Note

This section does not apply to the iterator and transducer interface.

After the initial sample is obtained, the default implementations for regular and parallel sampling (not for the iterator and the transducer since it is not needed there) create a container for all samples (the initial one and all subsequent samples) using AbstractMCMC.samples.

AbstractMCMC.samplesFunction
samples(sample, model, sampler[, N; kwargs...])

Generate a container for the samples of the MCMC sampler for the model, whose first sample is sample.

The method can be called with and without a predefined number N of samples.

source

In each step, the sample is saved in the container by AbstractMCMC.save!!. The notation !! follows the convention of the package BangBang.jl which is used in the default implementation of AbstractMCMC.save!!. It indicates that the sample is pushed to the container but a "widening" fallback is used if the container type does not allow to save the sample. Therefore AbstractMCMC.save!! always has to return the container.

AbstractMCMC.save!!Function
save!!(samples, sample, iteration, model, sampler[, N; kwargs...])

Save the sample of the MCMC sampler at the current iteration in the container of samples.

The function can be called with and without a predefined number N of samples. By default, AbstractMCMC uses push!! from the Julia package BangBang to append to the container, and widen its type if needed.

source

For most use cases the default implementation of AbstractMCMC.samples and AbstractMCMC.save!! should work out of the box and hence need not to be overloaded in downstream code.

Creating chains

Note

This section does not apply to the iterator and transducer interface.

At the end of the sampling procedure for regular and paralle sampling we transform the collection of samples to the desired output type by calling AbstractMCMC.bundle_samples.

AbstractMCMC.bundle_samplesFunction
bundle_samples(samples, model, sampler, state, chain_type[; kwargs...])

Bundle all samples that were sampled from the model with the given sampler in a chain.

The final state of the sampler can be included in the chain. The type of the chain can be specified with the chain_type argument.

By default, this method returns samples.

source

The default implementation should be fine in most use cases, but downstream packages could, e.g., save the final state of the sampler as well if they overload AbstractMCMC.bundle_samples.

diff --git a/dev/index.html b/dev/index.html index 7481f7d5..6e6e2b91 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Home · AbstractMCMC

AbstractMCMC.jl

Abstract types and interfaces for Markov chain Monte Carlo methods.

AbstractMCMC defines an interface for sampling and combining Markov chains. It comes with a default sampling algorithm that provides support of progress bars, parallel sampling (multithreaded and multicore), and user-provided callbacks out of the box. Typically developers only have to define the sampling step of their inference method in an iterator-like fashion to make use of this functionality. Additionally, the package defines an iterator and a transducer for sampling Markov chains based on the interface.

+Home · AbstractMCMC

AbstractMCMC.jl

Abstract types and interfaces for Markov chain Monte Carlo methods.

AbstractMCMC defines an interface for sampling and combining Markov chains. It comes with a default sampling algorithm that provides support of progress bars, parallel sampling (multithreaded and multicore), and user-provided callbacks out of the box. Typically developers only have to define the sampling step of their inference method in an iterator-like fashion to make use of this functionality. Additionally, the package defines an iterator and a transducer for sampling Markov chains based on the interface.

diff --git a/dev/objects.inv b/dev/objects.inv new file mode 100644 index 00000000..347171d7 --- /dev/null +++ b/dev/objects.inv @@ -0,0 +1,9 @@ +# Sphinx inventory version 2 +# Project: AbstractMCMC +# Version: 5.2.0 +# The remainder of this file is compressed using zlib. +xŖAo0| +W[n]s[-ҲM#oĭ16i}`hl= DR;'(AcA }hu4ݿ'(2 Y@50- +ye9a bp%x F-D{mL#2[K%pAC,rL Q4둾$jȺ&TAዳ`%0LuksP0`ʝtE lAl)< 0_I}JhaɐZm`U"6hD f&5X<ǒ&z{DR/`jfN +@mNڞ!*N+GɵY%] ++zIpU5\˦%nU &'vw}'d:LǑim4Vwsξ"^h:}צMeYXAӇN5.qF{.![3D vՐk݈+įnDy*@ xgP{|ݢqK=Nl4.ڍ.#}Pp-}@j:.9a}mr \ No newline at end of file