Page Not Found
-The page you requested cannot be found (perhaps it was moved or renamed).
-You may want to visit Get-Started Guide or Tutorials.
- - - - Back to topdiff --git a/pr-previews/109/404.html b/pr-previews/109/404.html deleted file mode 100644 index 53b4d64d3..000000000 --- a/pr-previews/109/404.html +++ /dev/null @@ -1,767 +0,0 @@ - -
- - - - - - - -If you are interested in exploring any of these projects or have any questions, please reach out to the listed project mentors. You can find their contact information at turinglang.org/team.
-Mentors: Kai Xu, Tor E. Fjelde, Hong Ge
-Project difficulty: Medium
-Project length: 175 hrs or 350 hrs
-Description: There are many real-world Bayesian models out there, and they deserve a Turing / Julia implementation.
-Examples include but not limited to - Forecasting (Prophet, datasets) - Recommender system (probabilistic matrix factorisation, dataset) - Ranking (TrueSkill, dataset) - Bayesian revenue estimation (example) - Political forecasting model (example) - Topic mining (latent Dirichlet allocation and new variants) - Multiple Annotators/Combining Unreliable Observations (Dawid and Skene, 1979)
-For each model, we would consider the following tasks as part of a GSoC project: - Correctness test: correctness of the implementation can be tested by doing inference for prior samples, for which we know the ground truth latent variables. - Performance benchmark: this includes (i) time per MCMC step and (ii) time per effective sample; if the model is differentiable, a further break-down of (i) into (i.1) time per forward pass and (i.2) time per gradient pass are needed. - Real-world results: if available, the final step is to apply the model to a real-world dataset; if such an experiment has been done in the literature, consistency of inference results needs to be checked
-Mentors: Cameron Pfiffer, Mohamed Tarek, David Widmann
-Project difficulty: Easy
-Project length: 175 hrs
-Description: Turing.jl is based on a set of inference packages maintained by the TuringLang group. This project is about making use of improvements in DynamicPPL to create a generic integration between Turing.jl and the AbstractMCMC.jl sampling API. The ultimate goal is to remove or substantially reduce algorithm-specific glue code inside Turing.jl. The project would also involve improving data structures for storing model parameters in DynamicPPL.
-Mentors: Philipp Gabler, Hong Ge
-Project difficulty: Hard
-Project length: 350 hrs
-Description: We want to have a very light-weight representation of probabilistic models of static graphs (similar to BUGS), which can serve as a representation target of other front-end DSLs or be dynamically built. The representation should consist of the model and node representations (stochastic and deterministic, perhaps hyperparameters) and conform to the AbstractPPL model interface, with basic functions (evaluation of density, sampling, conditioning; at later stages some static analysis like extraction of Markov blankets). The model should also contain the state of the variables and implement the AbstractPPL trace interface (dictionary functions, querying of variable names). The result should be able to work with existing sampling packages through the abstract interfaces.
-Mentors: Qingliang Zhuo, Mohamed Tarek
-Project difficulty: Medium
-Project length: 175 hrs
-Description: Tape caching often leads to significant performance improvements for gradient-based sampling algorithms (e.g. HMC/NUTS). Tape caching is only possible at the complete computational level for ReverseDiff at the moment. This project is about implementing a more modular, i.e. function-as-a-caching-barrier, tape caching mechanism for ReverseDiff.jl.
-Mentors: Theo Galy-Fajou, Will Tebbutt, ST John
-Project difficulty: Medium
-Project length: 350 hrs
-Description: Although KernelFunctions.jl has extensive correctness testing, our performance testing is lacking. This project aims to resolve this, and resolve performance issues wherever they are found. The student would first need to extend our existing benchmarking coverage, and debug any obvious performance problems. The next phase of the work would be to construct end-to-end examples of KernelFunctions being used in practice, profile them to determine where performance problems lie, and fix them.
-Mentors: Will Tebbutt, S. T. John, Ross Viljoen
-Project difficulty: Medium
-Project length: 175 hrs
-Description: There has recently been quite a bit of work on inference methods for GPs that use iterative methods rather than the Cholesky factorisation. They look quite promising, but no one has implemented any of these within the Julia GP ecosystem yet, but they should fit nicely within the AbstractGPs framework. If you’re interested in improving the GP ecosystem in Julia, this project might be for you!
-Mentors: S. T. John, Ross Viljoen, Theo Galy-Fajou
-Project difficulty: Hard
-Project length: 350 hrs
-Description: Adding approximate inference methods for non-Gaussian likelihoods which are available in other GP packages but not yet within JuliaGPs. The project would start by determining which approximate inference method(s) to implement—there’s lots to do, and we’re happy to work with a student on whichever method they are most interested in, or to suggest one if they have no strong preference.
-Mentors: Ross Viljoen, Theo Galy-Fajou, Will Tebbutt
-Project difficulty: Medium
-Project length: 350 hrs
-Description: This would involve first ensuring that common models are able to run fully on the GPU, then identifying and improving GPU-specific performance bottlenecks. This would begin by implementing a limited end-to-end example involving a GP with a standard kernel, and profiling it to debug any substantial performance bottlenecks. From there, support for a wider range of the functionality available in KernelFunctions.jl and AbstractGPs.jl can be added. Stretch goal: extension of GPU support to some functionality in ApproximateGPs.jl.
- - -If you are interested in exploring any of these projects, please reach out to the listed project mentors. You can find their contact information at turinglang.org/team.
-Mentors: Cameron Pfiffer, Hong Ge
-Project difficulty: Easy
-Description: MCMCChains is a key component of the Turing.jl ecosystem. It is the package that determines how to analyze and store MCMC samples provided by packages like Turing. It’s also used outside of Turing.
-For this project, a student might improve the performance of the various statistical functions provided by MCMCChains, changing the back end to use a data storage format that maintains the shape of parameter samples, or improve the general plotting functionality of the package.
-There’s lots of fun little things to do for MCMCChains. Check out this meta-issue for more details and discussions.
-Mentors: Hong Ge, Cameron Pfiffer
-Project difficulty: Medium
-Description: Turing’s support for particle sampling methods is slowing being improved with the addition of AdvancedPS.jl. If you’re interested in implementing or improving particle sampling methods, this is a great project for you!
-Mentors: Miles Lucas, Cameron Pfiffer, Hong Ge
-Project difficulty: Hard
-Description: NestedSamplers.jl is an excellent package which implements nested sampling methods. As of yet, it is not connected to Turing.jl. For this project, a student would connect the NestedSamplers.jl library to Turing.jl.
-Mentors: Mohamed Tarek, Hong Ge, Kai Xu, Tor Fjelde
-Project difficulty: Medium
-Description: Turing’s native GPU support is limited in that the Metropolis-Hastings and HMC samplers do not implement GPU sampling methods. This can and should be done – GPU methods are awesome! If you are interested with working on parallelism and GPUs, this project is for you.
-Students will work with the code at AdvancedMH or AdvancedHMC, depending on their interests.
-Mentors: Cameron Pfiffer, Martin Trapp
-Project difficulty: Easy
-Description: Turing’s documentation and tutorials need a bit of an overhaul. Turing has changed significantly since the last time the documentation was written, and it’s beginning to show. Students would use their knowledge of probabilistic programming languages and Turing to shore-up or rewrite documentation and tutorials.
-Mentors: Will Tebbutt, S. T. John, Theo Galy-Fajou
-Project difficulty: Medium
-Description: There has recently been quite a bit of work on inference methods for GPs that use iterative methods rather than the Cholesky factorisation. They look quite promising, but no one has implemented any of these within the Julia GP ecosystem yet, but they should fit nicely within the AbstractGPs framework. If you’re interested in improving the GP ecosystem in Julia, this project might be for you!
-Mentors: ST John, Will Tebbutt, Theo Galy-Fajou
-Project difficulty: Easy to Medium
-Description: Sparse variational Gaussian process models provide the flexibility to scale to large datasets, handle arbitrary (non-conjugate) likelihoods, and to be used as building blocks for composite models such as deep GPs. This project is about making such models more readily available within the Julia GP ecosystem - depending on your interests you can focus on making it easier for end users and providing good tutorials, or on the implementations of these models to give us the same or better performance as with established Python packages such as GPflow, integrating with Flux.jl, etc.
- - -Saranjeet Kaur’s project focused primarily on expanding NestedSamplers.jl. NestedSamplers.jl now supports PolyChord-style nested sampling natively, which is an absolute delight. Saranjeet wrote about this here. She also provided a good tutorial on how to use NestedSamplers.jl here. The NestedSamplers.jl integration with Turing is still on-going – integrating new samplers with Turing is one of the more difficult tasks. If you are interested to see the progress on this, check out the relevant pull request.
-Arthur Lui’s project was to provide a much-needed set of benchmarks of Bayesian nonparametric models between Turing and other PPLs. Arthur’s work spawned a GitHub repository with good practices for benchmarking, as well as three blog posts with some (very cool!) statistics on Turing’s performance:
-Finally, Sharan Yalburgi (a returning GSoC student) completed an epic amount of work Turing’s growing suite of Gaussian process tools. In particular, the GitHub organization JuliaGaussianProcesses was founded, and serves as an effort to build a robust Gaussian process framework for the Julia ecosystem. The framework consists of multiple GP related Julia packages:
-Special thanks to our three GSoC students for this summer, who all did excellent work. Additional thanks to Google for supporting open source software development and the Julia language!
- - - - Back to top ]]>In summary, we replicated the Imperial COVID-19 model using Turing.jl. Subsequently, we compared the inference results between Turing and Stan, and our comparison indicates that results are reproducible with two different implementations. In particular, we performed 4 sets of simulations using the Imperial COVID-19 model. The resulting estimates of the expected real number of cases, in contrast to the recorded number of cases, the reproduction number \(R_t\), and the expected number of deaths as a function of time and non-pharmaceutical interventions (NPIs) for each Simulation are shown below.
- -Simulation (a): hypothetical Simulation from the model without data (prior predictive) or non-pharmaceutical interventions. Under the prior assumptions of the Imperial Covid-19 model, there is a very wide range of epidemic progressions with expected cases from almost 0 to 100% of the population over time. The black bar corresponds to the date of the last observation. Note that \(R_t\) has a different time-range than the other plots; following the original report, this shows the 100 days following the country-specific epidemic_start
which is defined to be 31 days prior to the first date of 10 cumulative deaths, while the other plots show the last 60 days.
Simulation (b): future Simulation with non-pharmaceutical interventions kept in place (posterior predictive). After incorporating the observed infection data, we can see a substantially more refined range of epidemic progression. The reproduction rate estimate lies in the range of 3.5-5.6 before any intervention is introduced. The dotted lines correspond to observations, and the black bar corresponds to the date of the last observation.
-Simulation (c): future Simulation with non-pharmaceutical interventions removed. Now we see the hypothetical scenarios after incorporating infection data, but with non-pharmaceutical interventions removed. This plot looks similar to Simulation (a), but with a more rapid progression of the pandemic since the estimated reproduction rate is bigger than the prior assumptions. The dotted lines correspond to observations, and the black bar corresponds to the date of the last observation.
-Simulation (d): future Simulation with when lockdown
is lifted two weeks before the last observation (predictive posterior). As a result there is a clear, rapid rebound of the reproduction rate. Comparing with Simulation (b) we do not observe an immediate increase in the number of expected cases and deaths upon lifting lockdown, but there is a significant difference in the number of cases and deaths in the last few days in the plot: Simulation (d) results in both greater number of cases and deaths, as expected. This demonstrates how the effects of lifting an intervention might not become apparent in the measurable variables, e.g. deaths, until several weeks later. The dotted lines correspond to observations, the black bar corresponds to the date of the last observation, and the red bar indicates when lockdown
was lifted.
Overall, Simulation (a) shows the prior modelling assumptions, and how these prior assumptions determine the predicted number of cases, etc. before seeing any data. Simulation (b) predicts the trend of the number of cases, etc. using estimated parameters and by keeping all the non-pharmaceutical interventions in place. Simulation (c) shows the estimate in the case where none of the intervention measures are ever put in place. Simulation (d) shows the estimates in the case when the lockdown was lifted two weeks prior to the last observation while keeping all the other non-pharmaceutical interventions in place.
-We want to emphasise that we do not provide additional analysis of the Imperial model yet, nor are we aiming to make any claims about the validity or the implications of the model. Instead we refer to Imperial Report 13 for more details and analysis. The purpose of this post is solely to add validation to the inference performed in the paper by obtaining the same results using a different probabilistic programming language (PPL) and by exploring whether or not Turing.jl can be useful for researchers working on these problems.
-For our next steps, we’re looking at collaboration with other researchers and further developments of this and similar models. There are some immediate directions to explore:
-Such model refinement can be potentially valuable given the high impact of this pandemic and the uncertainty and debates in the potential outcomes.
-Acknowledgement We would like to thank the Julia community for creating such an excellent platform for scientific computing, and for the continuous feedback that we have received. We also thank researchers from Computational and Biological Laboratory at Cambridge University for their feedback on an early version of the post.
- - - - Back to top ]]>If you are not aware, Google provides funds to students around the world to develop a project of their choice over the summer. Students receive funds from Google and spend three months on any open source project.
-The Turing development team has prepared a list of possible projects that we have deemed valuable to the project and easy enough that it could feasibly be created in the three-month limit. This list is not exclusive – if you have a good idea, you can write it up in your proposal, though it is recommend that you reach out to any of the Turing team on Julia’s Slack (you can get an invite here) or Discourse. Messages on Discourse should be posted to the “Probabilistic programming” category – we’ll find you!
-Possible project ideas:
-mle(model)
or map(model)
would be very useful for many of Turing’s users who want to see what the MLE or MAP estimates look like, and it may be valuable to allow for functionality that allows MCMC sampling to begin from the MLE or MAP estimates. Students working on this project will work with optimization packages such as Optim.jl to make MLE and MAP estimation straightforward for Turing models.Other projects are welcome, but we do strongly recommend discussing any potential projects with members of the Turing team, as they will end up mentoring GSoC students for the duration of the project.
-We’re looking forward to what people are interested in!
- - - - Back to top ]]>Stay tuned!
- - - - Back to top ]]>${missingFields[0]}
field.`,
- message: `The items being returned for this search do not include all the required fields. Please ensure that your index items include the ${missingFields[0]}
field or use index-fields
in your _quarto.yml
file to specify the field names.`,
- };
- } else if (missingFields.length > 1) {
- const missingFieldList = missingFields
- .map((field) => {
- return `${field}
`;
- })
- .join(", ");
-
- throw {
- name: `Error: Search index is missing the following fields: ${missingFieldList}.`,
- message: `The items being returned for this search do not include all the required fields. Please ensure that your index items includes the following fields: ${missingFieldList}, or use index-fields
in your _quarto.yml
file to specify the field names.`,
- };
- }
- }
-}
-
-let lastQuery = null;
-function showCopyLink(query, options) {
- const language = options.language;
- lastQuery = query;
- // Insert share icon
- const inputSuffixEl = window.document.body.querySelector(
- ".aa-Form .aa-InputWrapperSuffix"
- );
-
- if (inputSuffixEl) {
- let copyButtonEl = window.document.body.querySelector(
- ".aa-Form .aa-InputWrapperSuffix .aa-CopyButton"
- );
-
- if (copyButtonEl === null) {
- copyButtonEl = window.document.createElement("button");
- copyButtonEl.setAttribute("class", "aa-CopyButton");
- copyButtonEl.setAttribute("type", "button");
- copyButtonEl.setAttribute("title", language["search-copy-link-title"]);
- copyButtonEl.onmousedown = (e) => {
- e.preventDefault();
- e.stopPropagation();
- };
-
- const linkIcon = "bi-clipboard";
- const checkIcon = "bi-check2";
-
- const shareIconEl = window.document.createElement("i");
- shareIconEl.setAttribute("class", `bi ${linkIcon}`);
- copyButtonEl.appendChild(shareIconEl);
- inputSuffixEl.prepend(copyButtonEl);
-
- const clipboard = new window.ClipboardJS(".aa-CopyButton", {
- text: function (_trigger) {
- const copyUrl = new URL(window.location);
- copyUrl.searchParams.set(kQueryArg, lastQuery);
- copyUrl.searchParams.set(kResultsArg, "1");
- return copyUrl.toString();
- },
- });
- clipboard.on("success", function (e) {
- // Focus the input
-
- // button target
- const button = e.trigger;
- const icon = button.querySelector("i.bi");
-
- // flash "checked"
- icon.classList.add(checkIcon);
- icon.classList.remove(linkIcon);
- setTimeout(function () {
- icon.classList.remove(checkIcon);
- icon.classList.add(linkIcon);
- }, 1000);
- });
- }
-
- // If there is a query, show the link icon
- if (copyButtonEl) {
- if (lastQuery && options["copy-button"]) {
- copyButtonEl.style.display = "flex";
- } else {
- copyButtonEl.style.display = "none";
- }
- }
- }
-}
-
-/* Search Index Handling */
-// create the index
-var fuseIndex = undefined;
-var shownWarning = false;
-
-// fuse index options
-const kFuseIndexOptions = {
- keys: [
- { name: "title", weight: 20 },
- { name: "section", weight: 20 },
- { name: "text", weight: 10 },
- ],
- ignoreLocation: true,
- threshold: 0.1,
-};
-
-async function readSearchData() {
- // Initialize the search index on demand
- if (fuseIndex === undefined) {
- if (window.location.protocol === "file:" && !shownWarning) {
- window.alert(
- "Search requires JavaScript features disabled when running in file://... URLs. In order to use search, please run this document in a web server."
- );
- shownWarning = true;
- return;
- }
- const fuse = new window.Fuse([], kFuseIndexOptions);
-
- // fetch the main search.json
- const response = await fetch(offsetURL("search.json"));
- if (response.status == 200) {
- return response.json().then(function (searchDocs) {
- searchDocs.forEach(function (searchDoc) {
- fuse.add(searchDoc);
- });
- fuseIndex = fuse;
- return fuseIndex;
- });
- } else {
- return Promise.reject(
- new Error(
- "Unexpected status from search index request: " + response.status
- )
- );
- }
- }
-
- return fuseIndex;
-}
-
-function inputElement() {
- return window.document.body.querySelector(".aa-Form .aa-Input");
-}
-
-function focusSearchInput() {
- setTimeout(() => {
- const inputEl = inputElement();
- if (inputEl) {
- inputEl.focus();
- }
- }, 50);
-}
-
-/* Panels */
-const kItemTypeDoc = "document";
-const kItemTypeMore = "document-more";
-const kItemTypeItem = "document-item";
-const kItemTypeError = "error";
-
-function renderItem(
- item,
- createElement,
- state,
- setActiveItemId,
- setContext,
- refresh,
- quartoSearchOptions
-) {
- switch (item.type) {
- case kItemTypeDoc:
- return createDocumentCard(
- createElement,
- "file-richtext",
- item.title,
- item.section,
- item.text,
- item.href,
- item.crumbs,
- quartoSearchOptions
- );
- case kItemTypeMore:
- return createMoreCard(
- createElement,
- item,
- state,
- setActiveItemId,
- setContext,
- refresh
- );
- case kItemTypeItem:
- return createSectionCard(
- createElement,
- item.section,
- item.text,
- item.href
- );
- case kItemTypeError:
- return createErrorCard(createElement, item.title, item.text);
- default:
- return undefined;
- }
-}
-
-function createDocumentCard(
- createElement,
- icon,
- title,
- section,
- text,
- href,
- crumbs,
- quartoSearchOptions
-) {
- const iconEl = createElement("i", {
- class: `bi bi-${icon} search-result-icon`,
- });
- const titleEl = createElement("p", { class: "search-result-title" }, title);
- const titleContents = [iconEl, titleEl];
- const showParent = quartoSearchOptions["show-item-context"];
- if (crumbs && showParent) {
- let crumbsOut = undefined;
- const crumbClz = ["search-result-crumbs"];
- if (showParent === "root") {
- crumbsOut = crumbs.length > 1 ? crumbs[0] : undefined;
- } else if (showParent === "parent") {
- crumbsOut = crumbs.length > 1 ? crumbs[crumbs.length - 2] : undefined;
- } else {
- crumbsOut = crumbs.length > 1 ? crumbs.join(" > ") : undefined;
- crumbClz.push("search-result-crumbs-wrap");
- }
-
- const crumbEl = createElement(
- "p",
- { class: crumbClz.join(" ") },
- crumbsOut
- );
- titleContents.push(crumbEl);
- }
-
- const titleContainerEl = createElement(
- "div",
- { class: "search-result-title-container" },
- titleContents
- );
-
- const textEls = [];
- if (section) {
- const sectionEl = createElement(
- "p",
- { class: "search-result-section" },
- section
- );
- textEls.push(sectionEl);
- }
- const descEl = createElement("p", {
- class: "search-result-text",
- dangerouslySetInnerHTML: {
- __html: text,
- },
- });
- textEls.push(descEl);
-
- const textContainerEl = createElement(
- "div",
- { class: "search-result-text-container" },
- textEls
- );
-
- const containerEl = createElement(
- "div",
- {
- class: "search-result-container",
- },
- [titleContainerEl, textContainerEl]
- );
-
- const linkEl = createElement(
- "a",
- {
- href: offsetURL(href),
- class: "search-result-link",
- },
- containerEl
- );
-
- const classes = ["search-result-doc", "search-item"];
- if (!section) {
- classes.push("document-selectable");
- }
-
- return createElement(
- "div",
- {
- class: classes.join(" "),
- },
- linkEl
- );
-}
-
-function createMoreCard(
- createElement,
- item,
- state,
- setActiveItemId,
- setContext,
- refresh
-) {
- const moreCardEl = createElement(
- "div",
- {
- class: "search-result-more search-item",
- onClick: (e) => {
- // Handle expanding the sections by adding the expanded
- // section to the list of expanded sections
- toggleExpanded(item, state, setContext, setActiveItemId, refresh);
- e.stopPropagation();
- },
- },
- item.title
- );
-
- return moreCardEl;
-}
-
-function toggleExpanded(item, state, setContext, setActiveItemId, refresh) {
- const expanded = state.context.expanded || [];
- if (expanded.includes(item.target)) {
- setContext({
- expanded: expanded.filter((target) => target !== item.target),
- });
- } else {
- setContext({ expanded: [...expanded, item.target] });
- }
-
- refresh();
- setActiveItemId(item.__autocomplete_id);
-}
-
-function createSectionCard(createElement, section, text, href) {
- const sectionEl = createSection(createElement, section, text, href);
- return createElement(
- "div",
- {
- class: "search-result-doc-section search-item",
- },
- sectionEl
- );
-}
-
-function createSection(createElement, title, text, href) {
- const descEl = createElement("p", {
- class: "search-result-text",
- dangerouslySetInnerHTML: {
- __html: text,
- },
- });
-
- const titleEl = createElement("p", { class: "search-result-section" }, title);
- const linkEl = createElement(
- "a",
- {
- href: offsetURL(href),
- class: "search-result-link",
- },
- [titleEl, descEl]
- );
- return linkEl;
-}
-
-function createErrorCard(createElement, title, text) {
- const descEl = createElement("p", {
- class: "search-error-text",
- dangerouslySetInnerHTML: {
- __html: text,
- },
- });
-
- const titleEl = createElement("p", {
- class: "search-error-title",
- dangerouslySetInnerHTML: {
- __html: ` ${title}`,
- },
- });
- const errorEl = createElement("div", { class: "search-error" }, [
- titleEl,
- descEl,
- ]);
- return errorEl;
-}
-
-function positionPanel(pos) {
- const panelEl = window.document.querySelector(
- "#quarto-search-results .aa-Panel"
- );
- const inputEl = window.document.querySelector(
- "#quarto-search .aa-Autocomplete"
- );
-
- if (panelEl && inputEl) {
- panelEl.style.top = `${Math.round(panelEl.offsetTop)}px`;
- if (pos === "start") {
- panelEl.style.left = `${Math.round(inputEl.left)}px`;
- } else {
- panelEl.style.right = `${Math.round(inputEl.offsetRight)}px`;
- }
- }
-}
-
-/* Highlighting */
-// highlighting functions
-function highlightMatch(query, text) {
- if (text) {
- const start = text.toLowerCase().indexOf(query.toLowerCase());
- if (start !== -1) {
- const startMark = "";
- const endMark = "";
-
- const end = start + query.length;
- text =
- text.slice(0, start) +
- startMark +
- text.slice(start, end) +
- endMark +
- text.slice(end);
- const startInfo = clipStart(text, start);
- const endInfo = clipEnd(
- text,
- startInfo.position + startMark.length + endMark.length
- );
- text =
- startInfo.prefix +
- text.slice(startInfo.position, endInfo.position) +
- endInfo.suffix;
-
- return text;
- } else {
- return text;
- }
- } else {
- return text;
- }
-}
-
-function clipStart(text, pos) {
- const clipStart = pos - 50;
- if (clipStart < 0) {
- // This will just return the start of the string
- return {
- position: 0,
- prefix: "",
- };
- } else {
- // We're clipping before the start of the string, walk backwards to the first space.
- const spacePos = findSpace(text, pos, -1);
- return {
- position: spacePos.position,
- prefix: "",
- };
- }
-}
-
-function clipEnd(text, pos) {
- const clipEnd = pos + 200;
- if (clipEnd > text.length) {
- return {
- position: text.length,
- suffix: "",
- };
- } else {
- const spacePos = findSpace(text, clipEnd, 1);
- return {
- position: spacePos.position,
- suffix: spacePos.clipped ? "…" : "",
- };
- }
-}
-
-function findSpace(text, start, step) {
- let stepPos = start;
- while (stepPos > -1 && stepPos < text.length) {
- const char = text[stepPos];
- if (char === " " || char === "," || char === ":") {
- return {
- position: step === 1 ? stepPos : stepPos - step,
- clipped: stepPos > 1 && stepPos < text.length,
- };
- }
- stepPos = stepPos + step;
- }
-
- return {
- position: stepPos - step,
- clipped: false,
- };
-}
-
-// removes highlighting as implemented by the mark tag
-function clearHighlight(searchterm, el) {
- const childNodes = el.childNodes;
- for (let i = childNodes.length - 1; i >= 0; i--) {
- const node = childNodes[i];
- if (node.nodeType === Node.ELEMENT_NODE) {
- if (
- node.tagName === "MARK" &&
- node.innerText.toLowerCase() === searchterm.toLowerCase()
- ) {
- el.replaceChild(document.createTextNode(node.innerText), node);
- } else {
- clearHighlight(searchterm, node);
- }
- }
- }
-}
-
-function escapeRegExp(string) {
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
-}
-
-// highlight matches
-function highlight(term, el) {
- const termRegex = new RegExp(term, "ig");
- const childNodes = el.childNodes;
-
- // walk back to front avoid mutating elements in front of us
- for (let i = childNodes.length - 1; i >= 0; i--) {
- const node = childNodes[i];
-
- if (node.nodeType === Node.TEXT_NODE) {
- // Search text nodes for text to highlight
- const text = node.nodeValue;
-
- let startIndex = 0;
- let matchIndex = text.search(termRegex);
- if (matchIndex > -1) {
- const markFragment = document.createDocumentFragment();
- while (matchIndex > -1) {
- const prefix = text.slice(startIndex, matchIndex);
- markFragment.appendChild(document.createTextNode(prefix));
-
- const mark = document.createElement("mark");
- mark.appendChild(
- document.createTextNode(
- text.slice(matchIndex, matchIndex + term.length)
- )
- );
- markFragment.appendChild(mark);
-
- startIndex = matchIndex + term.length;
- matchIndex = text.slice(startIndex).search(new RegExp(term, "ig"));
- if (matchIndex > -1) {
- matchIndex = startIndex + matchIndex;
- }
- }
- if (startIndex < text.length) {
- markFragment.appendChild(
- document.createTextNode(text.slice(startIndex, text.length))
- );
- }
-
- el.replaceChild(markFragment, node);
- }
- } else if (node.nodeType === Node.ELEMENT_NODE) {
- // recurse through elements
- highlight(term, node);
- }
- }
-}
-
-/* Link Handling */
-// get the offset from this page for a given site root relative url
-function offsetURL(url) {
- var offset = getMeta("quarto:offset");
- return offset ? offset + url : url;
-}
-
-// read a meta tag value
-function getMeta(metaName) {
- var metas = window.document.getElementsByTagName("meta");
- for (let i = 0; i < metas.length; i++) {
- if (metas[i].getAttribute("name") === metaName) {
- return metas[i].getAttribute("content");
- }
- }
- return "";
-}
-
-function algoliaSearch(query, limit, algoliaOptions) {
- const { getAlgoliaResults } = window["@algolia/autocomplete-preset-algolia"];
-
- const applicationId = algoliaOptions["application-id"];
- const searchOnlyApiKey = algoliaOptions["search-only-api-key"];
- const indexName = algoliaOptions["index-name"];
- const indexFields = algoliaOptions["index-fields"];
- const searchClient = window.algoliasearch(applicationId, searchOnlyApiKey);
- const searchParams = algoliaOptions["params"];
- const searchAnalytics = !!algoliaOptions["analytics-events"];
-
- return getAlgoliaResults({
- searchClient,
- queries: [
- {
- indexName: indexName,
- query,
- params: {
- hitsPerPage: limit,
- clickAnalytics: searchAnalytics,
- ...searchParams,
- },
- },
- ],
- transformResponse: (response) => {
- if (!indexFields) {
- return response.hits.map((hit) => {
- return hit.map((item) => {
- return {
- ...item,
- text: highlightMatch(query, item.text),
- };
- });
- });
- } else {
- const remappedHits = response.hits.map((hit) => {
- return hit.map((item) => {
- const newItem = { ...item };
- ["href", "section", "title", "text", "crumbs"].forEach(
- (keyName) => {
- const mappedName = indexFields[keyName];
- if (
- mappedName &&
- item[mappedName] !== undefined &&
- mappedName !== keyName
- ) {
- newItem[keyName] = item[mappedName];
- delete newItem[mappedName];
- }
- }
- );
- newItem.text = highlightMatch(query, newItem.text);
- return newItem;
- });
- });
- return remappedHits;
- }
- },
- });
-}
-
-let subSearchTerm = undefined;
-let subSearchFuse = undefined;
-const kFuseMaxWait = 125;
-
-async function fuseSearch(query, fuse, fuseOptions) {
- let index = fuse;
- // Fuse.js using the Bitap algorithm for text matching which runs in
- // O(nm) time (no matter the structure of the text). In our case this
- // means that long search terms mixed with large index gets very slow
- //
- // This injects a subIndex that will be used once the terms get long enough
- // Usually making this subindex is cheap since there will typically be
- // a subset of results matching the existing query
- if (subSearchFuse !== undefined && query.startsWith(subSearchTerm)) {
- // Use the existing subSearchFuse
- index = subSearchFuse;
- } else if (subSearchFuse !== undefined) {
- // The term changed, discard the existing fuse
- subSearchFuse = undefined;
- subSearchTerm = undefined;
- }
-
- // Search using the active fuse
- const then = performance.now();
- const resultsRaw = await index.search(query, fuseOptions);
- const now = performance.now();
-
- const results = resultsRaw.map((result) => {
- const addParam = (url, name, value) => {
- const anchorParts = url.split("#");
- const baseUrl = anchorParts[0];
- const sep = baseUrl.search("\\?") > 0 ? "&" : "?";
- anchorParts[0] = baseUrl + sep + name + "=" + value;
- return anchorParts.join("#");
- };
-
- return {
- title: result.item.title,
- section: result.item.section,
- href: addParam(result.item.href, kQueryArg, query),
- text: highlightMatch(query, result.item.text),
- crumbs: result.item.crumbs,
- };
- });
-
- // If we don't have a subfuse and the query is long enough, go ahead
- // and create a subfuse to use for subsequent queries
- if (
- now - then > kFuseMaxWait &&
- subSearchFuse === undefined &&
- resultsRaw.length < fuseOptions.limit
- ) {
- subSearchTerm = query;
- subSearchFuse = new window.Fuse([], kFuseIndexOptions);
- resultsRaw.forEach((rr) => {
- subSearchFuse.add(rr.item);
- });
- }
- return results;
-}
diff --git a/pr-previews/109/sitemap.xml b/pr-previews/109/sitemap.xml
deleted file mode 100644
index d74809784..000000000
--- a/pr-previews/109/sitemap.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-