diff --git a/.gitignore b/.gitignore index a562a9918..90d8d908b 100644 --- a/.gitignore +++ b/.gitignore @@ -61,8 +61,6 @@ instance/ # Scrapy stuff: .scrapy -# Sphinx documentation -docs/_build/ # PyBuilder target/ @@ -81,6 +79,7 @@ celerybeat-schedule # virtualenv venv/ +.venv/ ENV/ # Spyder project settings @@ -99,3 +98,7 @@ benchmarks/results/ # vscode configuration .vscode/ + +# Zensical +_site/ +.notebook_cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2715666ef..e41c84989 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,4 +26,4 @@ repos: rev: "v1.15.0" hooks: - id: mypy - exclude: ipynb_filter.py|docs/source/conf.py + exclude: ipynb_filter.py diff --git a/AUTHORS.md b/AUTHORS.md index 1624101b1..69a2ba67e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,4 +1,4 @@ -## πŸ‘₯ Authors +## Authors The current maintainers of Adaptive are: diff --git a/CHANGELOG.md b/CHANGELOG.md index 339c9db0a..96c53655c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# πŸ—žοΈ Changelog +# Changelog ## [v1.4.0](https://github.com/python-adaptive/adaptive/tree/v1.3.0) (2025-05-13) diff --git a/README.md b/README.md index 23989bc53..906b33ea9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ![logo](https://adaptive.readthedocs.io/en/latest/_static/logo.png) *Adaptive*: Parallel Active Learning of Mathematical Functions - + [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) [![Conda](https://img.shields.io/badge/install%20with-conda-green.svg)](https://anaconda.org/conda-forge/adaptive) @@ -9,12 +9,12 @@ [![Documentation](https://readthedocs.org/projects/adaptive/badge/?version=latest)](https://adaptive.readthedocs.io/en/latest/?badge=latest) [![Downloads](https://img.shields.io/conda/dn/conda-forge/adaptive.svg)](https://anaconda.org/conda-forge/adaptive) [![GitHub](https://img.shields.io/github/stars/python-adaptive/adaptive.svg?style=social)](https://github.com/python-adaptive/adaptive/stargazers) -[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/python-adaptive/adaptive) [![PyPI](https://img.shields.io/pypi/v/adaptive.svg)](https://pypi.python.org/pypi/adaptive) +[![Python](https://img.shields.io/pypi/pyversions/adaptive.svg)](https://pypi.python.org/pypi/adaptive) - + - + Adaptive is an open-source Python library that streamlines adaptive parallel function evaluations. Rather than calculating all points on a dense grid, it intelligently selects the "best" points in the parameter space based on your provided function and bounds. @@ -22,11 +22,11 @@ With minimal code, you can perform evaluations on a computing cluster, display l Adaptive is most efficient for computations where each function evaluation takes at least β‰ˆ50ms due to the overhead of selecting potentially interesting points. -To see Adaptive in action, try the [example notebook on Binder](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) or explore the [tutorial on Read the Docs](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial). +To see Adaptive in action, try the [example notebook on Binder](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) or explore the [tutorial on Read the Docs](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial/). - + -
[ToC] πŸ“š +
Table of contents @@ -38,6 +38,7 @@ To see Adaptive in action, try the [example notebook on Binder](https://mybinder - [Installation](#installation) - [Faster triangulation (optional)](#faster-triangulation-optional) - [Development](#development) + - [Documentation](#documentation) - [Citing](#citing) - [Draft Paper](#draft-paper) - [Credits](#credits) @@ -46,20 +47,20 @@ To see Adaptive in action, try the [example notebook on Binder](https://mybinder
- + ## Key features -- 🎯 **Intelligent Adaptive Sampling**: Adaptive focuses on areas of interest within a function, ensuring better results with fewer evaluations, saving time, and computational resources. -- ⚑ **Parallel Execution**: The library leverages parallel processing for faster function evaluations, making optimal use of available computational resources. -- πŸ“Š **Live Plotting and Info Widgets**: When working in Jupyter notebooks, Adaptive offers real-time visualization of the learning process, making it easier to monitor progress and identify areas of improvement. -- πŸ”§ **Customizable Loss Functions**: Adaptive supports various loss functions and allows customization, enabling users to tailor the learning process according to their specific needs. -- πŸ“ˆ **Support for Multidimensional Functions**: The library can handle functions with scalar or vector outputs in one or multiple dimensions, providing flexibility for a wide range of problems. -- 🧩 **Seamless Integration**: Adaptive offers a simple and intuitive interface, making it easy to integrate with existing Python projects and workflows. -- πŸ’Ύ **Flexible Data Export**: The library provides options to export learned data as NumPy arrays or Pandas DataFrames, ensuring compatibility with various data processing tools. -- 🌐 **Open-Source and Community-Driven**: Adaptive is an open-source project, encouraging contributions from the community to continuously improve and expand the library's features and capabilities. +- **Intelligent Adaptive Sampling**: Adaptive focuses on areas of interest within a function, ensuring better results with fewer evaluations, saving time, and computational resources. +- **Parallel Execution**: The library leverages parallel processing for faster function evaluations, making optimal use of available computational resources. +- **Live Plotting and Info Widgets**: When working in Jupyter notebooks, Adaptive offers real-time visualization of the learning process, making it easier to monitor progress and identify areas of improvement. +- **Customizable Loss Functions**: Adaptive supports various loss functions and allows customization, enabling users to tailor the learning process according to their specific needs. +- **Support for Multidimensional Functions**: The library can handle functions with scalar or vector outputs in one or multiple dimensions, providing flexibility for a wide range of problems. +- **Seamless Integration**: Adaptive offers a simple and intuitive interface, making it easy to integrate with existing Python projects and workflows. +- **Flexible Data Export**: The library provides options to export learned data as NumPy arrays or Pandas DataFrames, ensuring compatibility with various data processing tools. +- **Open-Source and Community-Driven**: Adaptive is an open-source project, encouraging contributions from the community to continuously improve and expand the library's features and capabilities. - + ## Example usage @@ -81,7 +82,13 @@ runner.live_info() runner.live_plot() ``` - +With the same number of points, adaptive sampling resolves features that a homogeneous grid misses: + +

+ Learner1D: homogeneous grid versus adaptive sampling of a sharp peak + Learner2D: homogeneous grid versus adaptive triangulation of a ring + LearnerND: sampled points concentrate on a spherical shell +

### Exporting Data @@ -97,7 +104,7 @@ If you have Pandas installed, you can also export the data as a DataFrame: df = learner.to_dataframe() ``` - + ## Implemented Algorithms @@ -110,7 +117,7 @@ While `adaptive` provides sensible default choices, the adaptive sampling proces The following learners are implemented: - + - `Learner1D`: for 1D functions `f: ℝ β†’ ℝ^N`, - `Learner2D`: for 2D functions `f: ℝ^2 β†’ ℝ^N`, @@ -118,7 +125,7 @@ The following learners are implemented: - `AverageLearner`: for random variables, allowing averaging of results over multiple evaluations, - `AverageLearner1D`: for stochastic 1D functions, estimating the mean value at each point, - `IntegratorLearner`: for integrating a 1D function `f: ℝ β†’ ℝ`, -- `BalancingLearner`: for running multiple learners simultaneously and selecting the "best" one as more points are gathered. +- `SequenceLearner`: for evaluating a fixed sequence of points while still using the runners, parallelism, and data export of Adaptive. Meta-learners (to be used with other learners): @@ -132,11 +139,11 @@ In addition to learners, `adaptive` offers primitives for parallel sampling acro [ipyparallel](https://ipyparallel.readthedocs.io/en/latest/), and [distributed](https://distributed.readthedocs.io/en/latest/). - + ## Installation -`adaptive` works with Python 3.7 and higher on Linux, Windows, or Mac, and provides optional extensions for working with the Jupyter/IPython Notebook. +`adaptive` works with Python 3.11 and higher on Linux, Windows, or Mac, and provides optional extensions for working with Jupyter. The recommended way to install adaptive is using `conda`: @@ -150,7 +157,7 @@ conda install -c conda-forge adaptive pip install "adaptive[notebook]" ``` -The `[notebook]` above will also install the optional dependencies for running `adaptive` inside a Jupyter notebook. +The `[notebook]` above also installs the optional dependencies for live plotting and widgets in JupyterLab and Jupyter Notebook; no extra Jupyter extensions need to be installed. ### Faster triangulation (optional) @@ -163,16 +170,9 @@ pip install "adaptive[rust]" No code changes are needed β€” the Rust backend is detected and used automatically. To control the selection, pass `LearnerND(..., triangulation_backend="python" | "rust" | "auto")` per learner, or set the environment variable `ADAPTIVE_TRIANGULATION_BACKEND=python` to force the pure-Python implementation globally (or to `rust` to make a missing Rust backend an error instead of a silent fallback). -To use Adaptive in Jupyterlab, you need to install the following labextensions. - -```bash -jupyter labextension install @jupyter-widgets/jupyterlab-manager -jupyter labextension install @pyviz/jupyterlab_pyviz -``` - ## Development -Clone the repository and run `pip install -e ".[notebook,test,other]"` to add a link to the cloned repo into your Python path: +Clone the repository and install it in editable mode with the optional dependencies: ```bash git clone git@github.com:python-adaptive/adaptive.git @@ -180,7 +180,7 @@ cd adaptive pip install -e ".[notebook,test,other]" ``` -We recommend using a Conda environment or a virtualenv for package management during Adaptive development. +We recommend a virtual environment, for example with [uv](https://docs.astral.sh/uv/) (`uv sync --extra notebook --extra test --extra other`) or Conda. To avoid polluting the history with notebook output, set up the git filter by running: @@ -198,6 +198,19 @@ pre-commit install in the repository. +### Documentation + +The documentation is built with [Zensical](https://zensical.org). +The executable pages in `docs/notebooks/` are Jupyter notebooks in [jupytext's MyST Markdown format](https://jupytext.readthedocs.io/en/latest/formats-markdown.html). +`docs/build_notebooks.py` executes them (with caching) and renders them into `docs/`, after which Zensical builds the site: + +```bash +uv run --group docs python docs/build_notebooks.py +uv run --group docs zensical serve # preview on http://localhost:8000 +``` + +The animations in this README are rendered from real learner runs with `uv run --group docs python docs/make_animations.py` (requires `ffmpeg` and Chrome or Chromium). + ## Citing If you used Adaptive in a scientific work, please cite it as follows. @@ -224,7 +237,10 @@ We would like to give credits to the following people: - Pedro Gonnet for his implementation of [CQUAD](https://www.gnu.org/software/gsl/manual/html_node/CQUAD-doubly_002dadaptive-integration.html), β€œAlgorithm 4” as described in β€œIncreasing the Reliability of Adaptive Quadrature Using Explicit Interpolants”, P. Gonnet, ACM Transactions on Mathematical Software, 37 (3), art. no. 26, 2010. - Pauli Virtanen for his `AdaptiveTriSampling` script (no longer available online since SciPy Central went down) which served as inspiration for the `adaptive.Learner2D`. - + + For general discussion, we have a [Gitter chat channel](https://gitter.im/python-adaptive/adaptive). If you find any bugs or have any feature suggestions please file a GitHub [issue](https://github.com/python-adaptive/adaptive/issues/new) or submit a [pull request](https://github.com/python-adaptive/adaptive/pulls). + + diff --git a/docs/.gitignore b/docs/.gitignore index ece50848c..65a4396fb 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,2 +1,7 @@ -build/* -source/_static/holoviews.* +# Rendered from docs/notebooks/ by docs/build_notebooks.py +/algorithms_and_examples.md +/benchmarks.md +/tutorial/ +*.ipynb +# Written by notebooks during execution +/notebooks/tutorial/data/ diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 120000 index 000000000..04c99a55c --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1 @@ +../CHANGELOG.md \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 2c7e55394..000000000 --- a/docs/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -j auto -SPHINXBUILD = sphinx-build -SPHINXPROJ = adaptive -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -source/_static/logo_docs.png: logo.py - python logo.py - -# Fake target to avoid the catch-all target -logo.py: - @echo "never executed" - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile source/_static/logo_docs.png - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/source/_static/example_uses/battle_for_majoranas.jpeg b/docs/_static/example_uses/battle_for_majoranas.jpeg similarity index 100% rename from docs/source/_static/example_uses/battle_for_majoranas.jpeg rename to docs/_static/example_uses/battle_for_majoranas.jpeg diff --git a/docs/source/_static/example_uses/quasi_majorana_paper.jpeg b/docs/_static/example_uses/quasi_majorana_paper.jpeg similarity index 100% rename from docs/source/_static/example_uses/quasi_majorana_paper.jpeg rename to docs/_static/example_uses/quasi_majorana_paper.jpeg diff --git a/docs/source/_static/example_uses/spin_orbit_paper.jpeg b/docs/_static/example_uses/spin_orbit_paper.jpeg similarity index 100% rename from docs/source/_static/example_uses/spin_orbit_paper.jpeg rename to docs/_static/example_uses/spin_orbit_paper.jpeg diff --git a/docs/source/_static/example_uses/zigzag_paper.jpeg b/docs/_static/example_uses/zigzag_paper.jpeg similarity index 100% rename from docs/source/_static/example_uses/zigzag_paper.jpeg rename to docs/_static/example_uses/zigzag_paper.jpeg diff --git a/docs/_static/extra.css b/docs/_static/extra.css new file mode 100644 index 000000000..c75ad33be --- /dev/null +++ b/docs/_static/extra.css @@ -0,0 +1,89 @@ +/* Viridis-inspired palette, matching the logo */ +[data-md-color-primary="custom"] { + --md-primary-fg-color: #3b528b; + --md-primary-fg-color--light: #5a6fa8; + --md-primary-fg-color--dark: #2c3e6b; + --md-primary-bg-color: #ffffff; + --md-primary-bg-color--light: #ffffffb3; +} +[data-md-color-accent="custom"] { + --md-accent-fg-color: #21918c; + --md-accent-fg-color--transparent: #21918c1a; + --md-accent-bg-color: #ffffff; + --md-accent-bg-color--light: #ffffffb3; +} +[data-md-color-scheme="slate"][data-md-color-primary="custom"] { + --md-typeset-a-color: #7ea6e0; +} + +/* Animated logo on the home page */ +.adaptive-logo-video { + display: block; + width: 320px; + max-width: 100%; + margin: 1.5rem auto; +} + +/* Notebook outputs rendered by docs/build_notebooks.py */ +.md-typeset .nb-output { + overflow-x: auto; +} +/* Script-only outputs (e.g. the HoloViews extension loader) take no space */ +.md-typeset .nb-output:has(> :not(script, style, link, div:empty)) { + margin: 0 0 1.5em; +} +.md-typeset .highlight.nb-output-text pre { + border-left: 0.2rem solid var(--md-accent-fg-color); +} +.md-typeset .highlight.nb-output-text code { + background: transparent; +} + +/* Bokeh plots and widgets are designed for a light background */ +/* (empty roots are the HoloViews extension loader, which renders nothing) */ +[data-md-color-scheme="slate"] .md-typeset .nb-output:has([data-root-id]:not(:empty)), +[data-md-color-scheme="slate"] .md-typeset .nb-output:has(.plotly-graph-div), +[data-md-color-scheme="slate"] .md-typeset .nb-output:has(.jupyter-widgets) { + background: #ffffff; + color: #000000; + border-radius: 0.4rem; + padding: 0.5rem; +} + +/* `live_info` and `live_plot` cancel buttons do nothing on a static page */ +.md-typeset .nb-output .widget-button, +.md-typeset .nb-output:has(.widget-button):not(:has(.widget-html)) { + display: none !important; +} + +/* pandas DataFrames */ +.md-typeset .nb-output table.dataframe { + border: none; + border-collapse: collapse; + border-spacing: 0; + font-size: 0.7rem; + table-layout: fixed; +} +.md-typeset .nb-output table.dataframe thead { + border-bottom: 1px solid var(--md-default-fg-color--lighter); + vertical-align: bottom; +} +.md-typeset .nb-output table.dataframe :is(tr, th, td) { + text-align: right; + vertical-align: middle; + padding: 0.4em 0.6em; + line-height: normal; + white-space: normal; + border: none; +} +.md-typeset .nb-output table.dataframe tbody tr:nth-child(odd) { + background: var(--md-code-bg-color); +} +.md-typeset .nb-output table.dataframe tbody tr:hover { + background: var(--md-accent-fg-color--transparent); +} + +/* README animations on the home page */ +.md-typeset img[src*="_static/readme/"] { + border-radius: 0.4rem; +} diff --git a/docs/_static/katex.js b/docs/_static/katex.js new file mode 100644 index 000000000..861ed7592 --- /dev/null +++ b/docs/_static/katex.js @@ -0,0 +1,11 @@ +// Render the math that pymdownx.arithmatex wraps in `.arithmatex` elements. +document$.subscribe(({ body }) => { + for (const element of body.querySelectorAll(".arithmatex")) { + renderMathInElement(element, { + delimiters: [ + { left: "\\(", right: "\\)", display: false }, + { left: "\\[", right: "\\]", display: true }, + ], + }); + } +}); diff --git a/docs/source/_static/logo.png b/docs/_static/logo.png similarity index 100% rename from docs/source/_static/logo.png rename to docs/_static/logo.png diff --git a/docs/source/_static/logo_docs.png b/docs/_static/logo_docs.png similarity index 100% rename from docs/source/_static/logo_docs.png rename to docs/_static/logo_docs.png diff --git a/docs/_static/logo_docs.webm b/docs/_static/logo_docs.webm new file mode 100644 index 000000000..43f23a67e Binary files /dev/null and b/docs/_static/logo_docs.webm differ diff --git a/docs/_static/readme/learner1d.gif b/docs/_static/readme/learner1d.gif new file mode 100644 index 000000000..41c2b0b35 Binary files /dev/null and b/docs/_static/readme/learner1d.gif differ diff --git a/docs/_static/readme/learner2d.gif b/docs/_static/readme/learner2d.gif new file mode 100644 index 000000000..4988aecf7 Binary files /dev/null and b/docs/_static/readme/learner2d.gif differ diff --git a/docs/_static/readme/learnerND.gif b/docs/_static/readme/learnerND.gif new file mode 100644 index 000000000..df370d6c4 Binary files /dev/null and b/docs/_static/readme/learnerND.gif differ diff --git a/docs/build_notebooks.py b/docs/build_notebooks.py new file mode 100644 index 000000000..099894008 --- /dev/null +++ b/docs/build_notebooks.py @@ -0,0 +1,245 @@ +"""Execute the notebooks in docs/notebooks/ and render them for Zensical. + +Every ``docs/notebooks/.md`` (a jupytext MyST notebook) becomes +``docs/.md`` with its outputs embedded as raw HTML, plus a +``docs/.ipynb`` download without outputs. Executed notebooks are cached +in ``.notebook_cache/`` keyed by their source text and library versions. + +Usage: python docs/build_notebooks.py [docs/notebooks/.md ...] +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import sys +import time +from concurrent.futures import ThreadPoolExecutor +from importlib.metadata import version +from pathlib import Path + +import jupytext +import nbformat +from nbclient import NotebookClient + +DOCS_DIR = Path(__file__).parent +SOURCE_DIR = DOCS_DIR / "notebooks" +CACHE_DIR = DOCS_DIR.parent / ".notebook_cache" # Zensical owns .cache/ + +# Richest representation first, like nbconvert's HTML exporter. +MIME_PRIORITY = [ + "application/vnd.jupyter.widget-view+json", + "application/javascript", + "text/html", + "text/markdown", + "image/svg+xml", + "image/png", + "image/jpeg", + "text/latex", + "text/plain", +] +WIDGET_VIEW = "application/vnd.jupyter.widget-view+json" +WIDGET_STATE = "application/vnd.jupyter.widget-state+json" +WIDGET_LOADER = ( + '' +) +ANSI = re.compile(r"\x1b\[[0-9;]*[A-Za-z]") +# adaptive calls plotly.offline.init_notebook_mode(), which inlines plotly.js. +PLOTLY_BUNDLE = re.compile(r"\* plotly\.js v(\d+\.\d+\.\d+)") +HIDE_LABELS = { + "hide-cell": "Show code cell", + "hide-input": "Show code", + "hide-output": "Show output", +} + + +def _script_json(value: object) -> str: + # " element early; "<\\/" is the same JSON. + return json.dumps(value).replace(" str: + return f'
\n{html_text.strip()}\n
' + + +def _code_block(text: str, lang: str) -> str: + fence = "````" if "```" in text else "```" + return f"{fence}{lang}\n{text.rstrip()}\n{fence}" + + +def _text_output(text: str) -> str: + return _code_block(ANSI.sub("", text), " { .text .nb-output-text }") + + +def _details(label: str, body: str) -> str: + return ( + f'
\n{label}\n\n' + f"{body}\n\n
" + ) + + +def _render_data(data: dict, has_widget_state: bool) -> str: + for mime in MIME_PRIORITY: + if mime not in data or (mime == WIDGET_VIEW and not has_widget_state): + continue + value = data[mime] + if mime == WIDGET_VIEW: + return _raw(f'') + if mime == "application/javascript": + return _raw(f'') + if mime == "text/html": + if bundle := PLOTLY_BUNDLE.search(value): + url = f"https://cdn.plot.ly/plotly-{bundle.group(1)}.min.js" + value = f'' + return _raw(value) + if mime == "text/markdown": + return value + if mime == "image/svg+xml": + return _raw(value) + if mime in ("image/png", "image/jpeg"): + src = f"data:{mime};base64,{value.strip()}" + return _raw(f'output') + if mime == "text/latex": + return value + return _text_output(value) + return "" + + +def _render_output(output: dict, has_widget_state: bool) -> str: + kind = output["output_type"] + if kind == "stream": + return _text_output(output["text"]) + if kind == "error": + return _text_output("\n".join(output["traceback"])) + return _render_data(output.get("data", {}), has_widget_state) + + +def _render_code_cell(cell: dict, has_widget_state: bool) -> str: + tags = set(cell.get("metadata", {}).get("tags", [])) + if "remove-cell" in tags: + return "" + source = "" if "remove-input" in tags else _code_block(cell["source"], "python") + outputs = ( + [] + if "remove-output" in tags + else [_render_output(o, has_widget_state) for o in cell.get("outputs", [])] + ) + # Raw HTML is escaped inside a `markdown="1"`
, so hidden cells + # only hide their source and text outputs; raw outputs follow the block. + raw = "\n\n".join(o for o in outputs if o.startswith('
str: + return "\n\n".join(part for part in parts if part) + + +def _download_note(ipynb_name: str) -> str: + return ( + '!!! tip "Run this page yourself"\n' + f" Download this page as a Jupyter notebook: [{ipynb_name}]({ipynb_name})." + ) + + +def render_notebook(nb: nbformat.NotebookNode, ipynb_name: str) -> str: + """Render an (executed) notebook as Zensical Markdown.""" + widget_state = nb.metadata.get("widgets", {}).get(WIDGET_STATE) + has_widget_state = bool(widget_state and widget_state.get("state")) + parts = [] + for cell in nb.cells: + if cell.cell_type == "markdown": + parts.append(cell.source) + elif cell.cell_type == "code": + parts.append(_render_code_cell(cell, has_widget_state)) + md = "\n\n".join(p for p in parts if p.strip()) + + if any(cell.cell_type == "code" for cell in nb.cells): + note = _download_note(ipynb_name) + title = re.search(r"^# .*$", md, flags=re.MULTILINE) + if title: + md = f"{md[: title.end()]}\n\n{note}{md[title.end() :]}" + else: + md = f"{note}\n\n{md}" + + if has_widget_state: + state = _script_json(widget_state) + md += "\n\n" + _raw( + f'\n{WIDGET_LOADER}' + ) + return md + "\n" + + +def _execute(nb: nbformat.NotebookNode, cwd: Path) -> nbformat.NotebookNode: + client = NotebookClient( + nb, + timeout=600, + kernel_name="python3", + resources={"metadata": {"path": str(cwd)}}, + store_widget_state=True, + ) + return client.execute() + + +def _library_versions() -> str: + # Outputs embed these libraries' versions (e.g. BokehJS CDN URLs). + packages = ("adaptive", "bokeh", "holoviews", "ipywidgets", "panel", "plotly") + return " ".join(f"{name}=={version(name)}" for name in packages) + + +def execute_cached(source: Path) -> nbformat.NotebookNode: + """Execute a jupytext notebook, reusing the cached result if unchanged.""" + text = source.read_text() + key = hashlib.sha256(f"{_library_versions()}\n{text}".encode()).hexdigest() + cached = CACHE_DIR / f"{key}.ipynb" + if cached.exists(): + return nbformat.read(cached, as_version=4) + nb = _execute(jupytext.reads(text, fmt="md:myst"), source.parent) + CACHE_DIR.mkdir(parents=True, exist_ok=True) + nbformat.write(nb, cached) + return nb + + +def build(source: Path) -> Path: + """Execute and render one notebook; return the written Markdown path.""" + start = time.monotonic() + target = DOCS_DIR / source.relative_to(SOURCE_DIR) + target.parent.mkdir(parents=True, exist_ok=True) + ipynb = target.with_suffix(".ipynb") + nb = execute_cached(source) + # Read by docs/overrides/partials/actions.html to link "Edit" to the source. + front_matter = f"---\nnotebook_source: {source.relative_to(DOCS_DIR)}\n---\n\n" + target.write_text(front_matter + render_notebook(nb, ipynb.name)) + if any(cell.cell_type == "code" for cell in nb.cells): + jupytext.write(jupytext.read(source), ipynb) + print(f"{source.relative_to(DOCS_DIR)}: {time.monotonic() - start:.1f}s") + return target + + +def main(argv: list[str] | None = None) -> None: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("sources", nargs="*", type=Path) + parser.add_argument("-j", "--jobs", type=int, default=4) + args = parser.parse_args(argv) + sources = [p.resolve() for p in args.sources] or sorted(SOURCE_DIR.rglob("*.md")) + with ThreadPoolExecutor(args.jobs) as pool: + list(pool.map(build, sources)) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/docs/docs.md b/docs/docs.md new file mode 100644 index 000000000..07d0a4db8 --- /dev/null +++ b/docs/docs.md @@ -0,0 +1,7 @@ +# Installation and development + +--8<-- "README.md:rest" + +--8<-- "AUTHORS.md" + +--8<-- "README.md:community" diff --git a/docs/environment.yml b/docs/environment.yml deleted file mode 100644 index caa8badcf..000000000 --- a/docs/environment.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: adaptive-docs - -channels: - - conda-forge - -dependencies: - - python=3.11 - - sortedcollections=2.1.0 - - scipy=1.10.1 - - holoviews=1.18.3 - - bokeh=3.3.4 - - panel=1.3.8 - - pandas=2.0.0 - - plotly=5.14.1 - - ipywidgets=8.0.6 - - myst-nb=0.17.1 - - sphinx_fontawesome=0.0.6 - - sphinx=5.3.0 - - ffmpeg=5.1.2 - - cloudpickle=2.2.1 - - loky=3.3.0 - - furo=2023.3.27 - - myst-parser=0.18.1 - - dask=2024.2.0 - - emoji=2.2.0 - - versioningit=2.2.0 - - distributed=2024.2.0 - - param=2.0.2 diff --git a/docs/source/faq.md b/docs/faq.md similarity index 67% rename from docs/source/faq.md rename to docs/faq.md index cfb35d476..2695a3efd 100644 --- a/docs/source/faq.md +++ b/docs/faq.md @@ -1,8 +1,8 @@ -# ❓ FAQ: Frequently Asked Questions +# FAQ: Frequently Asked Questions ## Where can I learn more about the algorithm used? -Read our [draft paper](https://gitlab.kwant-project.org/qt/adaptive-paper/builds/artifacts/master/file/paper.pdf?job=make) or the source code on [GitHub](https://github.com/python-adaptive/adaptive/). +Read our [draft paper](https://github.com/python-adaptive/paper) or the source code on [GitHub](https://github.com/python-adaptive/adaptive/). ## How do I get the data? @@ -10,19 +10,19 @@ Check `learner.data`. ## How do I learn more than one value per point? -Use the {class}`adaptive.DataSaver`. +Use the [`DataSaver`][adaptive.DataSaver]. ## My runner failed, how do I get the error message? Check `runner.task.print_stack()`. -## How do I get a {class}`~adaptive.Learner2D`'s data on a grid? +## How do I get a `Learner2D`'s data on a grid? Use `learner.interpolated_on_grid()` optionally with a argument `n` to specify the the amount of points in `x` and `y`. ## Why can I not use a `lambda` with a learner? -When using the {class}`~adaptive.Runner` the learner's function is evaluated in different Python processes. +When using the [`Runner`][adaptive.Runner] the learner's function is evaluated in different Python processes. Therefore, the `function` needs to be serialized (pickled) and send to the other Python processes; `lambda`s cannot be pickled. Instead you can probably use `functools.partial` to accomplish what you want to do. @@ -56,32 +56,4 @@ Adaptive is much more simple-minded and it relies only on the local properties o We'd say that Bayesian modeling is good for really computationally expensive data, regular grids for really cheap data, and local adaptive algorithms are somewhere in the middle. -% I get "``concurrent.futures.process.BrokenProcessPool``: A process in the process pool was terminated abruptly while the future was running or pending." what does it mean? -% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -% -% XXX: add answer! -% -% What is the difference with Kriging? -% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -% -% XXX: add answer! -% -% -% What is the difference with adaptive meshing in CFD or computer graphics? -% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -% -% XXX: add answer! -% -% -% Can I use this to tune my hyper parameters for machine learning models? -% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -% -% XXX: add answer! -% -% -% How to use Adaptive with MATLAB? -% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -% -% XXX: add answer! - Missing a question that you think belongs here? Let us [know](https://github.com/python-adaptive/adaptive/issues/new). diff --git a/docs/source/gallery.md b/docs/gallery.md similarity index 70% rename from docs/source/gallery.md rename to docs/gallery.md index 360889db4..104a74096 100644 --- a/docs/source/gallery.md +++ b/docs/gallery.md @@ -1,4 +1,4 @@ -# πŸ–ΌοΈ Gallery +# Gallery Adaptive has been used in the following scientific publications: @@ -6,19 +6,13 @@ Adaptive has been used in the following scientific publications: by A. Vuik, B. Nijholt, A. R. Akhmerov, M. Wimmer [arXiv:1806.02801](https://arxiv.org/abs/1806.02801) and the [source-code](https://zenodo.org/record/1285177) -```{image} _static/example_uses/quasi_majorana_paper.jpeg -:alt: Reproducing topological properties with quasi-Majorana states -:width: 500 -``` +![Reproducing topological properties with quasi-Majorana states](_static/example_uses/quasi_majorana_paper.jpeg){ width="500" loading=lazy } **Enhanced proximity effect in zigzag-shaped Majorana Josephson junctions** by Tom Laeven, Bas Nijholt, Anton R. Akhmerov, Michael Wimmer [arXiv:1903.06168](https://arxiv.org/abs/1903.06168) and the [source-code](https://zenodo.org/record/2578027) -```{image} _static/example_uses/zigzag_paper.jpeg -:alt: Enhanced proximity effect in zigzag-shaped Majorana Josephson junctions -:width: 500 -``` +![Enhanced proximity effect in zigzag-shaped Majorana Josephson junctions](_static/example_uses/zigzag_paper.jpeg){ width="500" loading=lazy } **Spin-Orbit Protection of Induced Superconductivity in Majorana Nanowires** @@ -26,10 +20,7 @@ by Jouri D.S. Bommer, Hao Zhang, Γ–nder GΓΌl, Bas Nijholt, Michael Wimmer, Filip [arXiv:1807.01940](https://arxiv.org/abs/1807.01940) -```{image} _static/example_uses/spin_orbit_paper.jpeg -:alt: Spin-Orbit Protection of Induced Superconductivity in Majorana Nanowires -:width: 500 -``` +![Spin-Orbit Protection of Induced Superconductivity in Majorana Nanowires](_static/example_uses/spin_orbit_paper.jpeg){ width="500" loading=lazy } Other examples: @@ -41,9 +32,6 @@ by Accidentally leaving the cluster running over the weekend without a `runner.goal` and with a bug in the simulation parameters led to this beautiful mess. -```{image} _static/example_uses/battle_for_majoranas.jpeg -:alt: Battle for Majoranas -:width: 500 -``` +![Battle for Majoranas](_static/example_uses/battle_for_majoranas.jpeg){ width="500" loading=lazy } Did you use Adaptive for something cool? Let us [know](https://github.com/python-adaptive/adaptive/issues/new) and we will add it to this gallery. diff --git a/docs/griffe_sphinx_refs.py b/docs/griffe_sphinx_refs.py new file mode 100644 index 000000000..197304d40 --- /dev/null +++ b/docs/griffe_sphinx_refs.py @@ -0,0 +1,81 @@ +"""Griffe extension: link Sphinx-style references in docstrings. + +The docstrings were written for Sphinx with ``default_role = "autolink"``, so +`` `~adaptive.Runner` `` was a link. This rewrites such references into +optional autorefs, which link when the target is documented (here or in an +inventory) and render as plain code otherwise. +""" + +from __future__ import annotations + +import re +from typing import Any + +import griffe + +# `name`, `~dotted.name`, or ~`dotted.name`, but not ``literal``. +SPHINX_REF = re.compile(r"(? str: + """Rewrite Sphinx default-role references in ``text`` to optional autorefs. + + Unindented lines in numpy field sections are types, which mkdocstrings + renders verbatim, so there the markup is only stripped: griffe then parses + plain dotted names as expressions and links them itself. + """ + lines = re.sub(r"\\\n\s+", " ", text).split("\n") + section = None + for i, line in enumerate(lines): + if i + 1 < len(lines) and re.fullmatch(r"-{3,}", lines[i + 1].strip()): + section = line.strip() + elif section in FIELD_SECTIONS and line.strip() and not line[0].isspace(): + line = re.sub(r"``([^`]+)``", r"\1", line) + lines[i] = SPHINX_REF.sub(lambda m: m.group(2), line) + else: + lines[i] = _link_refs(line, public) + return "\n".join(lines) + + +def _link_refs(text: str, public: frozenset[str]) -> str: + def replace(match: re.Match) -> str: + tilde, target = match.groups() + label = target + if "." not in target: + if target not in public: + return match.group(0) + target = f"adaptive.{target}" + elif tilde or match.group(0).startswith("~"): + label = target.rsplit(".", 1)[-1] + return f'{label}' + + return SPHINX_REF.sub(replace, text) + + +class SphinxRefs(griffe.Extension): + """Apply `sphinx_refs_to_autorefs` to every docstring of the package.""" + + def on_package(self, *, pkg: griffe.Module, **kwargs: Any) -> None: + public = frozenset(str(name) for name in pkg.exports or ()) + self._rewrite(pkg, public, seen=set()) + + def _rewrite(self, obj: griffe.Object, public: frozenset[str], seen: set) -> None: + if obj.path in seen: + return + seen.add(obj.path) + if obj.docstring: + obj.docstring.value = sphinx_refs_to_autorefs(obj.docstring.value, public) + for member in obj.members.values(): + if not member.is_alias: + self._rewrite(member, public, seen) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..d8d64d9d1 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,26 @@ +--- +hide: + - toc +--- + +# *Adaptive*: Parallel Active Learning of Mathematical Functions + +--8<-- "README.md:badges" + + + +--8<-- "README.md:summary" + +

+ Learner1D: homogeneous grid versus adaptive sampling of a sharp peak + Learner2D: homogeneous grid versus adaptive triangulation of a ring + LearnerND: sampled points concentrate on a spherical shell +

+ +[Start the tutorial](tutorial/tutorial.Learner1D.md){ .md-button .md-button--primary } +[See the examples](algorithms_and_examples.md){ .md-button } +[API reference](reference/adaptive.md){ .md-button } + +--8<-- "README.md:key-features" diff --git a/docs/logo.py b/docs/logo.py deleted file mode 100644 index 384c71065..000000000 --- a/docs/logo.py +++ /dev/null @@ -1,66 +0,0 @@ -import os -import sys - -import holoviews -import matplotlib.pyplot as plt -import matplotlib.tri as mtri -import numpy as np -from PIL import Image, ImageDraw - -sys.path.insert(0, os.path.abspath("..")) # to get adaptive on the path - -import adaptive # noqa: E402, isort:skip - -holoviews.notebook_extension("matplotlib") - - -def create_and_run_learner(): - def ring(xy): - import numpy as np - - x, y = xy - a = 0.2 - return x + np.exp(-((x**2 + y**2 - 0.75**2) ** 2) / a**4) - - learner = adaptive.Learner2D(ring, bounds=[(-1, 1), (-1, 1)]) - adaptive.runner.simple(learner, loss_goal=0.01) - return learner - - -def plot_learner_and_save(learner, fname): - fig, ax = plt.subplots() - tri = learner.interpolator(scaled=True).tri - triang = mtri.Triangulation(*tri.points.T, triangles=tri.vertices) - ax.triplot(triang, c="k", lw=0.8) - data = learner.interpolated_on_grid() - ax.imshow(np.vstack(data), extent=(-0.5, 0.5, -0.5, 0.5)) - ax.set_xticks([]) - ax.set_yticks([]) - plt.savefig(fname, bbox_inches="tight", transparent=True, dpi=300, pad_inches=-0.1) - - -def add_rounded_corners(fname, rad): - im = Image.open(fname) - circle = Image.new("L", (rad * 2, rad * 2), 0) - draw = ImageDraw.Draw(circle) - draw.ellipse((0, 0, rad * 2, rad * 2), fill=255) - alpha = Image.new("L", im.size, 255) - w, h = im.size - alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0)) - alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad)) - alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0)) - alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad)) - im.putalpha(alpha) - return im - - -def main(fname="source/_static/logo_docs.png"): - learner = create_and_run_learner() - plot_learner_and_save(learner, fname) - im = add_rounded_corners(fname, rad=200) - im.thumbnail((200, 200), Image.ANTIALIAS) # resize - im.save(fname) - - -if __name__ == "__main__": - main() diff --git a/docs/make_animations.py b/docs/make_animations.py new file mode 100644 index 000000000..846e2c906 --- /dev/null +++ b/docs/make_animations.py @@ -0,0 +1,275 @@ +"""Render the README animations and the animated docs logo from real learner runs. + +Writes docs/_static/readme/{learner1d,learner2d,learnerND}.gif and +docs/_static/logo_docs.webm. Needs ffmpeg, and Chrome or Chromium for kaleido +(set BROWSER_PATH if it is not found). + +Usage: python docs/make_animations.py [learner1d learner2d learnerND logo] +""" + +from __future__ import annotations + +import subprocess +import sys +import tempfile +from collections.abc import Callable +from pathlib import Path + +import matplotlib + +matplotlib.use("Agg") + +import matplotlib.pyplot as plt # noqa: E402 +import matplotlib.tri as mtri # noqa: E402 +import numpy as np # noqa: E402 +from PIL import Image, ImageDraw # noqa: E402 + +import adaptive # noqa: E402 +from adaptive.learner.learner1D import uniform_loss # noqa: E402 +from adaptive.learner.learner2D import uniform_loss as uniform_loss_2d # noqa: E402 + +STATIC = Path(__file__).parent / "_static" +DARK, ACCENT, NEW = "#2c3e6b", "#21918c", "#e4572e" +plt.rcParams.update( + { + "font.family": "DejaVu Sans", + "font.size": 11, + "axes.titlesize": 13, + "axes.titleweight": "bold", + "axes.spines.top": False, + "axes.spines.right": False, + "axes.edgecolor": "#888888", + "xtick.color": "#555555", + "ytick.color": "#555555", + "figure.facecolor": "white", + } +) + + +def peak(x, a=0.01, x0=0.137): + # Off-center, so a homogeneous grid does not happen to hit the peak exactly. + return x + a**2 / (a**2 + (x - x0) ** 2) + + +def ring(xy, a=0.2): + x, y = xy + return x + np.exp(-((x**2 + y**2 - 0.75**2) ** 2) / a**4) + + +def shell(xyz, a=0.4): + x, y, z = xyz + return np.exp(-((x**2 + y**2 + z**2 - 0.75**2) ** 2) / a**4) + + +def sampled(learner, **goal) -> tuple[np.ndarray, np.ndarray]: + """Run ``learner`` and return its points and values in evaluation order.""" + adaptive.runner.simple(learner, **goal) + points, values = zip(*learner.data.items()) + return np.array(points, dtype=float), np.array(values, dtype=float) + + +def counts(start: int, stop: int, nframes: int, power: float = 2) -> np.ndarray: + """Number of points per frame, slow at first so the start is visible.""" + return (start + (stop - start) * np.linspace(0, 1, nframes) ** power).astype(int) + + +def write_frames( + draw: Callable[[plt.Figure, int], None], + nframes: int, + figsize: tuple[float, float], + folder: Path, + dpi: int = 100, + transparent: bool = False, +) -> None: + fig = plt.figure(figsize=figsize, dpi=dpi) + for i in range(nframes): + fig.clear() + draw(fig, i) + fig.savefig(folder / f"{i:04d}.png", transparent=transparent) + plt.close(fig) + + +def to_gif(folder: Path, target: Path, fps: int = 15, hold: float = 2.5) -> None: + graph = ( + f"tpad=stop_mode=clone:stop_duration={hold},split[a][b];" + "[a]palettegen=max_colors=128:stats_mode=diff[p];" + "[b][p]paletteuse=dither=none:diff_mode=rectangle" + ) + _ffmpeg( + "-framerate", fps, "-i", folder / "%04d.png", "-vf", graph, "-loop", 0, target + ) + + +def _ffmpeg(*args) -> None: + cmd = ["ffmpeg", "-loglevel", "error", "-y", *map(str, args)] + subprocess.run(cmd, check=True) + + +def learner1d(target: Path) -> None: + xs, _ = sampled(adaptive.Learner1D(peak, bounds=(-1, 1)), npoints_goal=100) + # Homogeneous sampling that keeps its earlier points, so nothing jumps. + uniform = adaptive.Learner1D(peak, bounds=(-1, 1), loss_per_interval=uniform_loss) + xs_uniform, _ = sampled(uniform, npoints_goal=100) + + def draw(fig, i): + n = frames[i] + axes = fig.subplots(1, 2, sharey=True) + for ax, x, title in [ + (axes[0], xs_uniform[:n], "Homogeneous"), + (axes[1], xs[:n], "Adaptive"), + ]: + order = np.sort(x) + ax.plot(order, peak(order), color=DARK, lw=1.6, zorder=2) + ax.scatter(x, peak(x), s=16, color=ACCENT, zorder=3, linewidths=0) + ax.set_title(title) + ax.set_xlim(-1.05, 1.05) + ax.set_ylim(-1.15, 1.35) + if n: # the points chosen last + for ax, x in [(axes[0], xs_uniform[n - 1]), (axes[1], xs[n - 1])]: + ax.scatter(x, peak(x), s=50, color=NEW, zorder=4) + fig.suptitle(f"{n} points", fontsize=12, color="#555555", y=0.06) + fig.subplots_adjust(left=0.06, right=0.98, top=0.88, bottom=0.2, wspace=0.08) + + # One point per frame at first, then speed up. + frames = np.concatenate( + [np.arange(0, 40), np.linspace(40, len(xs), 30).astype(int)] + ) + _render_gif(draw, len(frames), (8, 3.6), target, fps=12) + + +def _triangles(ax, points, values, vmin, vmax, edge_color): + tri = mtri.Triangulation(points[:, 0], points[:, 1]) + ax.tripcolor(tri, values, shading="gouraud", cmap="viridis", vmin=vmin, vmax=vmax) + ax.triplot(tri, color=edge_color, lw=0.35, alpha=0.7) + ax.set_xlim(-1, 1) + ax.set_ylim(-1, 1) + ax.set_aspect("equal") + ax.axis("off") + + +def learner2d(target: Path) -> None: + bounds = ((-1, 1), (-1, 1)) + points, values = sampled(adaptive.Learner2D(ring, bounds), npoints_goal=1600) + uniform = adaptive.Learner2D(ring, bounds, loss_per_triangle=uniform_loss_2d) + points_uniform, values_uniform = sampled(uniform, npoints_goal=1600) + vmin, vmax = values.min(), values.max() + + def draw(fig, i): + n = frames[i] + axes = fig.subplots(1, 2) + _triangles(axes[0], points_uniform[:n], values_uniform[:n], vmin, vmax, "white") + _triangles(axes[1], points[:n], values[:n], vmin, vmax, "white") + axes[0].set_title("Homogeneous") + axes[1].set_title("Adaptive") + fig.suptitle(f"{n} points", fontsize=12, color="#555555", y=0.05) + fig.subplots_adjust(left=0.02, right=0.98, top=0.9, bottom=0.1, wspace=0.05) + + frames = counts(4, len(points), 60, power=2.5) + _render_gif(draw, len(frames), (7, 3.75), target, dpi=90) + + +def learnerND(target: Path) -> None: + """Rotating version of the LearnerND plot on the "Algorithms and examples" page.""" + import plotly.graph_objects as go + import plotly.io as pio + + points, values = sampled( + adaptive.LearnerND(shell, bounds=[(-1, 1)] * 3), npoints_goal=5000 + ) + # Cut away the x > 0, y > 0 quarter to show the shell in cross-section. + keep = ~((points[:, 0] > 0) & (points[:, 1] > 0)) + marker = { + "size": 5, + "color": values[keep], + "colorscale": "Viridis", + "line": {"color": "rgb(50,50,50)", "width": 0.5}, + } # same style as LearnerND.plot_3D, with bigger markers + x, y, z = points[keep].T + fig = go.Figure(go.Scatter3d(x=x, y=y, z=z, mode="markers", marker=marker)) + hidden = {"visible": False} + fig.update_layout( + scene={ + "xaxis": hidden, "yaxis": hidden, "zaxis": hidden, "aspectmode": "cube", + "camera": {"projection": {"type": "orthographic"}}, + }, + margin={"l": 0, "r": 0, "t": 0, "b": 0}, + showlegend=False, + annotations=[ + { + "text": f"LearnerND, {len(points)} points", + "x": 0.5, "y": 0.02, "xref": "paper", "yref": "paper", + "showarrow": False, "font": {"size": 15, "color": "#555555"}, + } + ], + ) # fmt: skip + nframes = 90 + figures = [] + for i in range(nframes): + # Sway back and forth around the view into the cut. + angle = np.pi / 4 + np.radians(45) * np.sin(2 * np.pi * i / nframes) + eye = {"x": 2 * np.cos(angle), "y": 2 * np.sin(angle), "z": 0.25} + figures.append(go.Figure(fig).update_layout(scene_camera_eye=eye)) + with tempfile.TemporaryDirectory() as folder: + files = [Path(folder) / f"{i:04d}.png" for i in range(nframes)] + pio.write_images(figures, files, width=400, height=400) + to_gif(Path(folder), target, hold=0) + print(f"{target.relative_to(STATIC.parent)}: {target.stat().st_size / 1e6:.2f} MB") + + +def _render_gif(draw, nframes, figsize, target, hold=2.5, dpi=100, fps=15): + with tempfile.TemporaryDirectory() as folder: + write_frames(draw, nframes, figsize, Path(folder), dpi=dpi) + to_gif(Path(folder), target, fps=fps, hold=hold) + print(f"{target.relative_to(STATIC.parent)}: {target.stat().st_size / 1e6:.2f} MB") + + +def _rounded_alpha(size: int, radius: int) -> Image.Image: + mask = Image.new("L", (size, size), 0) + ImageDraw.Draw(mask).rounded_rectangle((0, 0, size - 1, size - 1), radius, fill=255) + return mask + + +def logo(target: Path) -> None: + points, values = sampled( + adaptive.Learner2D(ring, bounds=((-1, 1), (-1, 1))), loss_goal=0.005 + ) + vmin, vmax = values.min(), values.max() + + def draw(fig, i): + ax = fig.add_axes((0, 0, 1, 1)) + _triangles(ax, points[: frames[i]], values[: frames[i]], vmin, vmax, "black") + + frames = counts(9, len(points), 24 * 10) + size, dpi = 500, 100 + with tempfile.TemporaryDirectory() as tmp: + folder = Path(tmp) + write_frames(draw, len(frames), (size / dpi, size / dpi), folder, dpi=dpi) + alpha = _rounded_alpha(size, radius=int(size * 0.3)) + for frame in sorted(folder.glob("*.png")): + image = Image.open(frame).convert("RGBA") + image.putalpha(alpha) + image.save(frame) + _ffmpeg( + "-framerate", 24, "-i", folder / "%04d.png", "-vf", "tpad=stop_mode=clone:stop_duration=3", + "-c:v", "libvpx-vp9", "-pix_fmt", "yuva420p", "-crf", 30, "-b:v", 0, target, + ) # fmt: skip + print(f"{target.relative_to(STATIC.parent)}: {target.stat().st_size / 1e6:.2f} MB") + + +ANIMATIONS = { + "learner1d": (learner1d, STATIC / "readme" / "learner1d.gif"), + "learner2d": (learner2d, STATIC / "readme" / "learner2d.gif"), + "learnerND": (learnerND, STATIC / "readme" / "learnerND.gif"), + "logo": (logo, STATIC / "logo_docs.webm"), +} + + +def main(names: list[str]) -> None: + (STATIC / "readme").mkdir(exist_ok=True) + for name in names or ANIMATIONS: + function, target = ANIMATIONS[name] + function(target) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/docs/source/algorithms_and_examples.md b/docs/notebooks/algorithms_and_examples.md similarity index 82% rename from docs/source/algorithms_and_examples.md rename to docs/notebooks/algorithms_and_examples.md index 0aff9f1fa..fc785e30a 100644 --- a/docs/source/algorithms_and_examples.md +++ b/docs/notebooks/algorithms_and_examples.md @@ -10,25 +10,22 @@ kernelspec: name: python3 --- -```{include} ../README.md ---- -start-after: -end-before: ---- -``` +# Algorithms and examples + +--8<-- "README.md:implemented-algorithms" -- {class}`~adaptive.Learner1D`, for 1D functions `f: ℝ β†’ ℝ^N`, -- {class}`~adaptive.Learner2D`, for 2D functions `f: ℝ^2 β†’ ℝ^N`, -- {class}`~adaptive.LearnerND`, for ND functions `f: ℝ^N β†’ ℝ^M`, -- {class}`~adaptive.AverageLearner`, for random variables where you want to average the result over many evaluations, -- {class}`~adaptive.AverageLearner1D`, for stochastic 1D functions where you want to estimate the mean value of the function at each point, -- {class}`~adaptive.IntegratorLearner`, for when you want to intergrate a 1D function `f: ℝ β†’ ℝ`. -- {class}`~adaptive.BalancingLearner`, for when you want to run several learners at once, selecting the β€œbest” one each time you get more points. +- [`Learner1D`][adaptive.Learner1D], for 1D functions `f: ℝ β†’ ℝ^N`, +- [`Learner2D`][adaptive.Learner2D], for 2D functions `f: ℝ^2 β†’ ℝ^N`, +- [`LearnerND`][adaptive.LearnerND], for ND functions `f: ℝ^N β†’ ℝ^M`, +- [`AverageLearner`][adaptive.AverageLearner], for random variables where you want to average the result over many evaluations, +- [`AverageLearner1D`][adaptive.AverageLearner1D], for stochastic 1D functions where you want to estimate the mean value of the function at each point, +- [`IntegratorLearner`][adaptive.IntegratorLearner], for when you want to integrate a 1D function `f: ℝ β†’ ℝ`, +- [`SequenceLearner`][adaptive.SequenceLearner], for when you want to evaluate a fixed sequence of points while still using the runners and data export of Adaptive. Meta-learners (to be used with other learners): -- {class}`~adaptive.BalancingLearner`, for when you want to run several learners at once, selecting the β€œbest” one each time you get more points, -- {class}`~adaptive.DataSaver`, for when your function doesn't just return a scalar or a vector. +- [`BalancingLearner`][adaptive.BalancingLearner], for when you want to run several learners at once, selecting the β€œbest” one each time you get more points, +- [`DataSaver`][adaptive.DataSaver], for when your function doesn't just return a scalar or a vector. In addition to the learners, `adaptive` also provides primitives for running the sampling across several cores and even several machines, with built-in support for [concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html), @@ -37,10 +34,10 @@ In addition to the learners, `adaptive` also provides primitives for running the [ipyparallel](https://ipyparallel.readthedocs.io/en/latest/), and [distributed](https://distributed.readthedocs.io/en/latest/). -# πŸ’‘ Examples +## Examples Here are some examples of how Adaptive samples vs. homogeneous sampling. -Click on the *Play* {fa}`play` button or move the sliders. +Click on the *Play* :fontawesome-solid-play: button or move the sliders. ```{code-cell} ipython3 :tags: [hide-cell] @@ -53,11 +50,11 @@ import numpy as np import adaptive from adaptive.learner.learner1D import default_loss, uniform_loss -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) hv.output(holomap="scrubber") ``` -## {class}`adaptive.Learner1D` +### `adaptive.Learner1D` The `Learner1D` class is designed for adaptively learning 1D functions of the form `f: ℝ β†’ ℝ^N`. It focuses on sampling points where the function is less well understood to improve the overall approximation. This learner is well-suited for functions with localized features or varying degrees of complexity across the domain. @@ -126,7 +123,7 @@ layout = plot_homo + plot_adaptive layout.opts(hv.opts.Scatter(active_tools=["box_zoom", wheel_zoom])) ``` -## {class}`adaptive.Learner2D` +### `adaptive.Learner2D` The `Learner2D` class is tailored for adaptively learning 2D functions of the form `f: ℝ^2 β†’ ℝ^N`. Similar to `Learner1D`, it concentrates on sampling points with higher uncertainty to provide a better approximation. This learner is ideal for functions with complex features or varying behavior across a 2D domain. @@ -162,7 +159,7 @@ plot = hv.HoloMap(plots, kdims=["npoints"]).collate() plot.opts(hv.opts.Image(active_tools=[wheel_zoom])) ``` -## {class}`adaptive.AverageLearner` +### `adaptive.AverageLearner` The `AverageLearner` class is designed for situations where you want to average the result of a function over multiple evaluations. This is particularly useful when working with random variables or stochastic functions, as it helps to estimate the mean value of the function. @@ -191,7 +188,7 @@ hm = hv.HoloMap(plots, kdims=["npoints"]) hm.opts(hv.opts.Histogram(active_tools=[wheel_zoom])) ``` -## {class}`adaptive.LearnerND` +### `adaptive.LearnerND` The `LearnerND` class is intended for adaptively learning ND functions of the form `f: ℝ^N β†’ ℝ^M`. It extends the adaptive learning capabilities of the 1D and 2D learners to functions with more dimensions, allowing for efficient exploration of complex, high-dimensional spaces. @@ -226,4 +223,4 @@ scatter["x"], scatter["y"], scatter["z"], scatter.marker["color"] = zip(*coords_ fig ``` -see more in the {ref}`Tutorial Adaptive`. +see more in the [tutorial](tutorial/tutorial.md). diff --git a/docs/source/benchmarks.md b/docs/notebooks/benchmarks.md similarity index 89% rename from docs/source/benchmarks.md rename to docs/notebooks/benchmarks.md index 1e8cc4441..838089450 100644 --- a/docs/source/benchmarks.md +++ b/docs/notebooks/benchmarks.md @@ -13,10 +13,6 @@ kernelspec: # Benchmarks -```{tip} -This page is a Jupyter notebook that can be downloaded and run locally. [^download] -``` - Adaptive sampling is a powerful technique for approximating functions with varying degrees of complexity across their domain. This approach is particularly useful for functions with sharp features or rapid changes, as it focuses on calculating more points around those areas. By concentrating points where they are needed most, adaptive sampling can provide an accurate representation of the function with fewer points compared to uniform sampling. @@ -28,27 +24,21 @@ We will also present benchmarking results to highlight the advantages (and disad Look below where we demonstrate the use of the adaptive package to perform adaptive sampling and visualize the results. By the end of this benchmarking showcase, you should better understand of the benefits of adaptive sampling and in which cases you could to apply this technique to your own simulations or functions. -```{note} -> Note on error estimates +!!! note "Note on error estimates" + The error is estimated using the L1 norm of the difference between the true function values and the interpolated values. Here's a step-by-step explanation of how the error is calculated: -The error is estimated using the L1 norm of the difference between the true function values and the interpolated values. Here's a step-by-step explanation of how the error is calculated: + 1. For each benchmark function, two learners are created: the adaptive learner and a homogeneous learner. The adaptive learner uses adaptive sampling, while the homogeneous learner uses a uniform grid of points. -1. For each benchmark function, two learners are created: the adaptive learner and a homogeneous learner. The adaptive learner uses adaptive sampling, while the homogeneous learner uses a uniform grid of points. + 2. After the adaptive learning is complete, the error is calculated by comparing the interpolated values obtained from the adaptive learner to the true function values evaluated at the points used by the homogeneous learner. -2. After the adaptive learning is complete, the error is calculated by comparing the interpolated values obtained from the adaptive learner to the true function values evaluated at the points used by the homogeneous learner. + 3. To calculate the error, the L1 norm is used. The L1 norm represents the average of the absolute differences between the true function values and the interpolated values. Specifically, it is calculated as the square root of the mean of the squared differences between the true function values and the interpolated values. -3. To calculate the error, the L1 norm is used. The L1 norm represents the average of the absolute differences between the true function values and the interpolated values. Specifically, it is calculated as the square root of the mean of the squared differences between the true function values and the interpolated values. - -Note that the choice of the L1 norm is somewhat arbitrary. -**Please judge the results for yourself by looking at the plots** and observe the significantly better function approximation obtained by the adaptive learner. -``` + Note that the choice of the L1 norm is somewhat arbitrary. + **Please judge the results for yourself by looking at the plots** and observe the significantly better function approximation obtained by the adaptive learner. -```{warning} -> Note on benchmark functions - -The benchmark functions used in this tutorial are analytical and cheap to evaluate. -In real-world applications ([see the gallery](gallery)), adaptive sampling is often more beneficial for expensive simulations where function evaluations are computationally demanding or time-consuming. -``` +!!! warning "Note on benchmark functions" + The benchmark functions used in this tutorial are analytical and cheap to evaluate. + In real-world applications ([see the gallery](gallery.md)), adaptive sampling is often more beneficial for expensive simulations where function evaluations are computationally demanding or time-consuming. ## Benchmarks 1D @@ -66,7 +56,7 @@ from scipy.interpolate import interp1d import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) benchmarks = {} benchmarks_2d = {} @@ -511,5 +501,3 @@ df[["npoints", "error_ratio"]] ```{code-cell} ipython3 plot_benchmarks(df) ``` - -[^download]: This notebook can be downloaded as **{nb-download}`benchmarks.ipynb`** and {download}`benchmarks.md`. diff --git a/docs/source/tutorial/tutorial.AverageLearner.md b/docs/notebooks/tutorial/tutorial.AverageLearner.md similarity index 77% rename from docs/source/tutorial/tutorial.AverageLearner.md rename to docs/notebooks/tutorial/tutorial.AverageLearner.md index 2f53d396b..aae7e5396 100644 --- a/docs/source/tutorial/tutorial.AverageLearner.md +++ b/docs/notebooks/tutorial/tutorial.AverageLearner.md @@ -9,19 +9,18 @@ jupytext: format_version: '0.13' jupytext_version: 1.13.8 --- -# Tutorial {class}`~adaptive.AverageLearner` +# Tutorial `AverageLearner` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) ``` The next type of learner averages a function until the uncertainty in the average meets some condition. @@ -62,5 +61,3 @@ runner.live_info() ```{code-cell} ipython3 runner.live_plot(update_interval=0.1) ``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.AverageLearner.ipynb`** and {download}`tutorial.AverageLearner.md`. diff --git a/docs/source/tutorial/tutorial.AverageLearner1D.md b/docs/notebooks/tutorial/tutorial.AverageLearner1D.md similarity index 91% rename from docs/source/tutorial/tutorial.AverageLearner1D.md rename to docs/notebooks/tutorial/tutorial.AverageLearner1D.md index 38c3c6353..0fb1fa031 100644 --- a/docs/source/tutorial/tutorial.AverageLearner1D.md +++ b/docs/notebooks/tutorial/tutorial.AverageLearner1D.md @@ -10,19 +10,18 @@ kernelspec: name: python3 --- -# Tutorial {class}`~adaptive.AverageLearner1D` +# Tutorial `AverageLearner1D` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) from functools import partial @@ -98,7 +97,7 @@ In some cases, the default configuration of the 1D average learner can be sub-op One can then tune the internal parameters of the learner. The most relevant are: -- `loss_per_interval`: loss function (see {class}`~adaptive.Learner1D`). +- `loss_per_interval`: loss function (see [`Learner1D`][adaptive.Learner1D]). - `delta`: this parameter is the most relevant and controls the balance between resampling existing points (exploitation) and sampling new ones (exploration). Its value should remain between 0 and 1 (the default value is 0.2). Large values favor the "exploration" behavior, although this can make the learner to sample noise. Small values favor the "exploitation" behavior, leading the learner to thoroughly resample existing points. In general, the optimal value of `delta` is between 0.1 and 0.4. - `neighbor_sampling`: each new point is initially sampled a fraction `neighbor_sampling` of the number of samples of its nearest neighbor. We recommend to keep the value of `neighbor_sampling` below 1 to prevent oversampling. - `min_samples`: minimum number of samples that are initially taken at a new point. This parameter can prevent the learner from sampling noise in case we accidentally set a too large value of `delta`. @@ -144,5 +143,3 @@ await runner.task # This is not needed in a notebook environment! runner.live_info() runner.live_plot(update_interval=0.1) ``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.AverageLearner1D.ipynb`** and {download}`tutorial.AverageLearner1D.md`. diff --git a/docs/source/tutorial/tutorial.BalancingLearner.md b/docs/notebooks/tutorial/tutorial.BalancingLearner.md similarity index 77% rename from docs/source/tutorial/tutorial.BalancingLearner.md rename to docs/notebooks/tutorial/tutorial.BalancingLearner.md index 8e43259c3..9b677bb11 100644 --- a/docs/source/tutorial/tutorial.BalancingLearner.md +++ b/docs/notebooks/tutorial/tutorial.BalancingLearner.md @@ -10,19 +10,18 @@ kernelspec: name: python3 --- -# Tutorial {class}`~adaptive.BalancingLearner` +# Tutorial `BalancingLearner` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) import random from functools import partial @@ -34,7 +33,7 @@ import numpy as np The balancing learner is a β€œmeta-learner” that takes a list of learners. When you request a point from the balancing learner, it will query all of its β€œchildren” to figure out which one will give the most improvement. -The balancing learner can for example be used to implement a poor-man’s 2D learner by using the {class}`~adaptive.Learner1D`. +The balancing learner can for example be used to implement a poor-man’s 2D learner by using the [`Learner1D`][adaptive.Learner1D]. ```{code-cell} ipython3 def h(x, offset=0): @@ -70,7 +69,7 @@ runner.live_plot(plotter=plotter, update_interval=0.1) ``` Often one wants to create a set of `learner`s for a cartesian product of parameters. -For that particular case we’ve added a `classmethod` called {class}`~adaptive.BalancingLearner.from_product`. +For that particular case we’ve added a `classmethod` called [`from_product`][adaptive.BalancingLearner.from_product]. See how it works below ```{code-cell} ipython3 @@ -97,5 +96,3 @@ runner = adaptive.BlockingRunner(learner, loss_goal=0.01) # `plot()` will return a HoloMap with correctly labeled sliders. learner.plot().overlay("beta").grid().select(y=(-1, 3)) ``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.BalancingLearner.ipynb`** and {download}`tutorial.BalancingLearner.md`. diff --git a/docs/source/tutorial/tutorial.DataSaver.md b/docs/notebooks/tutorial/tutorial.DataSaver.md similarity index 79% rename from docs/source/tutorial/tutorial.DataSaver.md rename to docs/notebooks/tutorial/tutorial.DataSaver.md index 1eb35e707..880a1fa11 100644 --- a/docs/source/tutorial/tutorial.DataSaver.md +++ b/docs/notebooks/tutorial/tutorial.DataSaver.md @@ -9,22 +9,21 @@ jupytext: format_version: '0.13' jupytext_version: 1.13.8 --- -# Tutorial {class}`~adaptive.DataSaver` +# Tutorial `DataSaver` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) ``` -If the function that you want to learn returns a value along with some metadata, you can wrap your learner in an {class}`adaptive.DataSaver`. +If the function that you want to learn returns a value along with some metadata, you can wrap your learner in an [`adaptive.DataSaver`][adaptive.DataSaver]. In the following example the function to be learned returns its result and the execution time in a dictionary: @@ -77,5 +76,3 @@ Now the `DataSavingLearner` will have an dictionary attribute `extra_data` that ```{code-cell} ipython3 learner.extra_data ``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.DataSaver.ipynb`** and {download}`tutorial.DataSaver.md`. diff --git a/docs/source/tutorial/tutorial.IntegratorLearner.md b/docs/notebooks/tutorial/tutorial.IntegratorLearner.md similarity index 78% rename from docs/source/tutorial/tutorial.IntegratorLearner.md rename to docs/notebooks/tutorial/tutorial.IntegratorLearner.md index 12b86e090..731e6aeed 100644 --- a/docs/source/tutorial/tutorial.IntegratorLearner.md +++ b/docs/notebooks/tutorial/tutorial.IntegratorLearner.md @@ -10,19 +10,18 @@ kernelspec: name: python3 --- -# Tutorial {class}`~adaptive.IntegratorLearner` +# Tutorial `IntegratorLearner` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) import holoviews as hv import numpy as np @@ -51,7 +50,7 @@ scipy.integrate.quad(f24, 0, 3) ``` We initialize a learner again and pass the bounds and relative tolerance we want to reach. -Then in the {class}`~adaptive.Runner` we pass `goal=lambda l: l.done()` where `learner.done()` is `True` when the relative tolerance has been reached. +Then in the [`Runner`][adaptive.Runner] we pass `goal=lambda l: l.done()` where `learner.done()` is `True` when the relative tolerance has been reached. ```{code-cell} ipython3 from adaptive.runner import SequentialExecutor @@ -90,5 +89,3 @@ print( ) learner.plot() ``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.IntegratorLearner.ipynb`** and {download}`tutorial.IntegratorLearner.md`. diff --git a/docs/source/tutorial/tutorial.Learner1D.md b/docs/notebooks/tutorial/tutorial.Learner1D.md similarity index 87% rename from docs/source/tutorial/tutorial.Learner1D.md rename to docs/notebooks/tutorial/tutorial.Learner1D.md index e5ffe491a..12a964b8b 100644 --- a/docs/source/tutorial/tutorial.Learner1D.md +++ b/docs/notebooks/tutorial/tutorial.Learner1D.md @@ -10,20 +10,18 @@ kernelspec: name: python3 --- -(TutorialLearner1D)= -# Tutorial {class}`~adaptive.Learner1D` +# Tutorial `Learner1D` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) import random from functools import partial @@ -59,10 +57,10 @@ learner = adaptive.Learner1D(f, bounds=(-1, 1)) Next we create a β€œrunner” that will request points from the learner and evaluate β€˜f’ on them. -By default on Unix-like systems the runner will evaluate the points in parallel using local processes {class}`concurrent.futures.ProcessPoolExecutor`. +By default on Unix-like systems the runner will evaluate the points in parallel using local processes [`concurrent.futures.ProcessPoolExecutor`][concurrent.futures.ProcessPoolExecutor]. -On Windows systems the runner will use a {class}`loky.get_reusable_executor`. -A {class}`~concurrent.futures.ProcessPoolExecutor` cannot be used on Windows for reasons. +On Windows systems the runner will use a [`loky.get_reusable_executor`][loky.get_reusable_executor]. +A [`ProcessPoolExecutor`][concurrent.futures.ProcessPoolExecutor] cannot be used on Windows for reasons. ```{code-cell} ipython3 # The end condition is when the "loss" is less than 0.01. In the context of the @@ -205,7 +203,7 @@ adaptive.runner.simple(learner_2, npoints_goal=100) ).cols(2) ``` -More info about using custom loss functions can be found in {ref}`Custom adaptive logic for 1D and 2D`. +More info about using custom loss functions can be found in [Custom adaptive logic for 1D and 2D](tutorial.custom_loss.md). ## Exporting the data @@ -230,5 +228,3 @@ new_learner = adaptive.Learner1D(learner.function, (-1, 1)) # create an empty l new_learner.load_dataframe(df) # load the pandas.DataFrame's data new_learner.plot() ``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.Learner1D.ipynb`** and {download}`tutorial.Learner1D.md`. diff --git a/docs/source/tutorial/tutorial.Learner2D.md b/docs/notebooks/tutorial/tutorial.Learner2D.md similarity index 81% rename from docs/source/tutorial/tutorial.Learner2D.md rename to docs/notebooks/tutorial/tutorial.Learner2D.md index 43e5bdeff..1800a6738 100644 --- a/docs/source/tutorial/tutorial.Learner2D.md +++ b/docs/notebooks/tutorial/tutorial.Learner2D.md @@ -10,12 +10,11 @@ kernelspec: name: python3 --- -# Tutorial {class}`~adaptive.Learner2D` +# Tutorial `Learner2D` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] @@ -27,7 +26,7 @@ import numpy as np import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) ``` Besides 1D functions, we can also learn 2D functions: $f: ℝ^2 β†’ ℝ$. @@ -89,5 +88,3 @@ learner2.tell_many(xys, map(partial(ring, wait=False), xys)) + learner.plot(tri_alpha=0.4) ).cols(2).opts(hv.opts.EdgePaths(color="w")) ``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.Learner2D.ipynb`** and {download}`tutorial.Learner2D.md`. diff --git a/docs/source/tutorial/tutorial.LearnerND.md b/docs/notebooks/tutorial/tutorial.LearnerND.md similarity index 90% rename from docs/source/tutorial/tutorial.LearnerND.md rename to docs/notebooks/tutorial/tutorial.LearnerND.md index 37705b79d..1d767ca57 100644 --- a/docs/source/tutorial/tutorial.LearnerND.md +++ b/docs/notebooks/tutorial/tutorial.LearnerND.md @@ -10,21 +10,18 @@ kernelspec: name: python3 --- -# Tutorial {class}`~adaptive.LearnerND` +# Tutorial `LearnerND` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.LearnerND.ipynb`** and {download}`tutorial.LearnerND.md`. +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) import holoviews as hv import numpy as np diff --git a/docs/source/tutorial/tutorial.SequenceLearner.md b/docs/notebooks/tutorial/tutorial.SequenceLearner.md similarity index 78% rename from docs/source/tutorial/tutorial.SequenceLearner.md rename to docs/notebooks/tutorial/tutorial.SequenceLearner.md index 5a60fbcdd..0770ecb57 100644 --- a/docs/source/tutorial/tutorial.SequenceLearner.md +++ b/docs/notebooks/tutorial/tutorial.SequenceLearner.md @@ -9,19 +9,18 @@ jupytext: format_version: '0.13' jupytext_version: 1.13.8 --- -# Tutorial {class}`~adaptive.SequenceLearner` +# Tutorial `SequenceLearner` -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) import holoviews as hv import numpy as np @@ -73,5 +72,3 @@ To get the values in the same order as the input sequence (`learner.sequence`) u result = learner.result() print(result[:10]) # print the 10 first values ``` - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.SequenceLearner.ipynb`** and {download}`tutorial.SequenceLearner.md`. diff --git a/docs/source/tutorial/tutorial.advanced-topics.md b/docs/notebooks/tutorial/tutorial.advanced-topics.md similarity index 86% rename from docs/source/tutorial/tutorial.advanced-topics.md rename to docs/notebooks/tutorial/tutorial.advanced-topics.md index 8ba0caf40..99355b7e1 100644 --- a/docs/source/tutorial/tutorial.advanced-topics.md +++ b/docs/notebooks/tutorial/tutorial.advanced-topics.md @@ -9,20 +9,18 @@ kernelspec: display_name: python3 name: python3 --- -(TutorialAdvancedTopics)= # Advanced Topics -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) import asyncio import random @@ -37,11 +35,11 @@ def f(x, offset=offset): ## Saving and loading learners -Every learner has a {class}`~adaptive.BaseLearner.save` and {class}`~adaptive.BaseLearner.load` method that can be used to save and load **only** the data of a learner. +Every learner has a [`save`][adaptive.BaseLearner.save] and [`load`][adaptive.BaseLearner.load] method that can be used to save and load **only** the data of a learner. Use the `fname` argument in `learner.save(fname=...)`. -Or, when using a {class}`~adaptive.BalancingLearner` one can use either a callable that takes the child learner and returns a filename **or** a list of filenames. +Or, when using a [`BalancingLearner`][adaptive.BalancingLearner] one can use either a callable that takes the child learner and returns a filename **or** a list of filenames. By default the resulting pickle files are compressed, to turn this off use `learner.save(fname=..., compress=False)` @@ -79,7 +77,7 @@ control = adaptive.Learner1D(f, bounds=(-1, 1)) control.copy_from(learner) ``` -One can also periodically save the learner while running in a {class}`~adaptive.Runner`. Use it like: +One can also periodically save the learner while running in a [`Runner`][adaptive.Runner]. Use it like: ```{code-cell} ipython3 def slow_f(x): @@ -114,7 +112,7 @@ runner.live_info() # we cancelled it after 6 seconds ## A watched pot never boils! -The {class}`adaptive.Runner` does its work in an `asyncio` task that runs concurrently with the IPython kernel, when using `adaptive` from a Jupyter notebook. +The [`adaptive.Runner`][adaptive.Runner] does its work in an `asyncio` task that runs concurrently with the IPython kernel, when using `adaptive` from a Jupyter notebook. This is advantageous because it allows us to do things like live-updating plots, however it can trip you up if you’re not careful. Notably: **if you block the IPython kernel, the runner will not do any work**. @@ -130,7 +128,7 @@ If you do this then **the runner will never finish**. What to do if you don’t care about live plotting, and just want to run something until its done? -The simplest way to accomplish this is to use {class}`adaptive.BlockingRunner`: +The simplest way to accomplish this is to use [`adaptive.BlockingRunner`][adaptive.BlockingRunner]: ```{code-cell} ipython3 learner = adaptive.Learner1D(f, bounds=(-1, 1)) @@ -149,7 +147,7 @@ Adaptive makes this choice because in many cases the speedup from parallel execu Nevertheless it is still possible to run a learner in a deterministic way with adaptive. -The simplest way is to use {class}`adaptive.runner.simple` to run your learner: +The simplest way is to use [`adaptive.runner.simple`][adaptive.runner.simple] to run your learner: ```{code-cell} ipython3 learner = adaptive.Learner1D(f, bounds=(-1, 1)) @@ -160,9 +158,9 @@ adaptive.runner.simple(learner, loss_goal=0.01) learner.plot() ``` -Note that unlike {class}`adaptive.Runner`, {class}`adaptive.runner.simple` *blocks* until it is finished. +Note that unlike [`adaptive.Runner`][adaptive.Runner], [`adaptive.runner.simple`][adaptive.runner.simple] *blocks* until it is finished. -If you want to enable determinism, want to continue using the non-blocking {class}`adaptive.Runner`, you can use the {class}`adaptive.runner.SequentialExecutor`: +If you want to enable determinism, want to continue using the non-blocking [`adaptive.Runner`][adaptive.Runner], you can use the [`adaptive.runner.SequentialExecutor`][adaptive.runner.SequentialExecutor]: ```{code-cell} ipython3 from adaptive.runner import SequentialExecutor @@ -307,7 +305,7 @@ runner.live_info() This gives a the runner a `log` attribute, which is a list of the `learner` methods that were called, as well as their arguments. This is useful because executors typically execute their tasks in a non-deterministic order. -This can be used with {class}`adaptive.runner.replay_log` to perfom the same set of operations on another runner: +This can be used with [`adaptive.runner.replay_log`][adaptive.runner.replay_log] to perfom the same set of operations on another runner: ```{code-cell} ipython3 reconstructed_learner = adaptive.Learner1D(f, bounds=learner.bounds) @@ -320,7 +318,7 @@ learner.plot().Scatter.I.opts(size=6) * reconstructed_learner.plot() ## Adding coroutines -In the following example we'll add a {class}`~asyncio.Task` that times the runner. +In the following example we'll add a [`Task`][asyncio.Task] that times the runner. This is *only* for demonstration purposes because one can simply check `runner.elapsed_time()` or use the `runner.live_info()` widget to see the time since the runner has started. So let's get on with the example. To time the runner you **cannot** simply use @@ -365,14 +363,12 @@ await runner.task # This is not needed in a notebook environment! # The result will only be set when the runner is done. timer.result() ``` -(CustomParallelization)= ## Custom parallelization using coroutines Adaptive by itself does not implement a way of sharing partial results between function executions. Instead its implementation of parallel computation using executors is minimal by design. The appropriate way to implement custom parallelization is by using coroutines (asynchronous functions). - We illustrate this approach by using `dask.distributed` for parallel computations in part because it supports asynchronous operation out-of-the-box. We will focus on a function `f(x)` that consists of two distinct components: a slow part `g` that can be reused across multiple inputs and shared among various function evaluations, and a fast part `h` that is calculated for each `x` value. @@ -400,7 +396,7 @@ def h(x): ### Using `adaptive.utils.daskify` -To simplify the process of using coroutines and caching with dask and Adaptive, we provide the {func}`adaptive.utils.daskify` decorator. This decorator can be used to parallelize functions with caching as well as functions without caching, making it a powerful tool for custom parallelization in Adaptive. +To simplify the process of using coroutines and caching with dask and Adaptive, we provide the [`adaptive.utils.daskify`][adaptive.utils.daskify] decorator. This decorator can be used to parallelize functions with caching as well as functions without caching, making it a powerful tool for custom parallelization in Adaptive. ```{code-cell} ipython3 from dask.distributed import Client @@ -443,7 +439,7 @@ learner.plot() ### Step-by-step explanation of custom parallelization -Now let's dive into a detailed explanation of the process to understand how the {func}`adaptive.utils.daskify` decorator works. +Now let's dive into a detailed explanation of the process to understand how the [`adaptive.utils.daskify`][adaptive.utils.daskify] decorator works. In order to combine reuse of values of `g` with adaptive, we need to convert `f` into a dask graph by using `dask.delayed`. @@ -498,7 +494,7 @@ learner.plot() Runners can also be used from a Python script independently of the notebook. -The simplest way to accomplish this is simply to use the {class}`~adaptive.BlockingRunner`: +The simplest way to accomplish this is simply to use the [`BlockingRunner`][adaptive.BlockingRunner]: ```python import adaptive @@ -513,7 +509,7 @@ learner = adaptive.Learner1D(f, (-1, 1)) adaptive.BlockingRunner(learner, loss_goal=0.1) ``` -If you use `asyncio` already in your script and want to integrate `adaptive` into it, then you can use the default {class}`~adaptive.Runner` as you would from a notebook. +If you use `asyncio` already in your script and want to integrate `adaptive` into it, then you can use the default [`Runner`][adaptive.Runner] as you would from a notebook. If you want to wait for the runner to finish, then you can simply ```python @@ -521,5 +517,3 @@ await runner.task ``` from within a coroutine. - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.advanced-topics.ipynb`** and {download}`tutorial.advanced-topics.md`. diff --git a/docs/source/tutorial/tutorial.custom_loss.md b/docs/notebooks/tutorial/tutorial.custom_loss.md similarity index 63% rename from docs/source/tutorial/tutorial.custom_loss.md rename to docs/notebooks/tutorial/tutorial.custom_loss.md index f1d84cd3f..a0e866427 100644 --- a/docs/source/tutorial/tutorial.custom_loss.md +++ b/docs/notebooks/tutorial/tutorial.custom_loss.md @@ -12,45 +12,44 @@ kernelspec: # Custom adaptive logic for 1D and 2D -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebook in order to see the real behaviour. [^download] -``` +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebook in order to see the real behaviour. ```{code-cell} ipython3 :tags: [hide-cell] import adaptive -adaptive.notebook_extension() +adaptive.notebook_extension(_inline_js=False) # Import modules that are used in multiple cells import holoviews as hv import numpy as np ``` -{class}`~adaptive.Learner1D` and {class}`~adaptive.Learner2D` both work on the principle of subdividing their domain into subdomains, and assigning a property to each subdomain, which we call the *loss*. +[`Learner1D`][adaptive.Learner1D] and [`Learner2D`][adaptive.Learner2D] both work on the principle of subdividing their domain into subdomains, and assigning a property to each subdomain, which we call the *loss*. The algorithm for choosing the best place to evaluate our function is then simply *take the subdomain with the largest loss and add a point in the center, creating new subdomains around this point*. The *loss function* that defines the loss per subdomain is the canonical place to define what regions of the domain are β€œinteresting”. -The default loss function for {class}`~adaptive.Learner1D` and {class}`~adaptive.Learner2D` is sufficient for a wide range of common cases, but it is by no means a panacea. +The default loss function for [`Learner1D`][adaptive.Learner1D] and [`Learner2D`][adaptive.Learner2D] is sufficient for a wide range of common cases, but it is by no means a panacea. For example, the default loss function will tend to get stuck on divergences. -Both the {class}`~adaptive.Learner1D` and {class}`~adaptive.Learner2D` allow you to specify a *custom loss function*. +Both the [`Learner1D`][adaptive.Learner1D] and [`Learner2D`][adaptive.Learner2D] allow you to specify a *custom loss function*. Below we illustrate how you would go about writing your own loss function. -The documentation for {class}`~adaptive.Learner1D` and {class}`~adaptive.Learner2D` specifies the signature that your loss function needs to have in order for it to work with `adaptive`. +The documentation for [`Learner1D`][adaptive.Learner1D] and [`Learner2D`][adaptive.Learner2D] specifies the signature that your loss function needs to have in order for it to work with `adaptive`. tl;dr, one can use the following *loss functions* that **we** already implemented: -- {class}`adaptive.learner.learner1D.default_loss` -- {class}`adaptive.learner.learner1D.uniform_loss` -- {class}`adaptive.learner.learner1D.curvature_loss_function` -- {class}`adaptive.learner.learner1D.resolution_loss_function` -- {class}`adaptive.learner.learner1D.abs_min_log_loss` -- {class}`adaptive.learner.learner2D.default_loss` -- {class}`adaptive.learner.learner2D.uniform_loss` -- {class}`adaptive.learner.learner2D.minimize_triangle_surface_loss` -- {class}`adaptive.learner.learner2D.resolution_loss_function` +- [`adaptive.learner.learner1D.default_loss`][adaptive.learner.learner1D.default_loss] +- [`adaptive.learner.learner1D.uniform_loss`][adaptive.learner.learner1D.uniform_loss] +- [`adaptive.learner.learner1D.curvature_loss_function`][adaptive.learner.learner1D.curvature_loss_function] +- [`adaptive.learner.learner1D.resolution_loss_function`][adaptive.learner.learner1D.resolution_loss_function] +- [`adaptive.learner.learner1D.abs_min_log_loss`][adaptive.learner.learner1D.abs_min_log_loss] +- [`adaptive.learner.learner2D.default_loss`][adaptive.learner.learner2D.default_loss] +- [`adaptive.learner.learner2D.uniform_loss`][adaptive.learner.learner2D.uniform_loss] +- [`adaptive.learner.learner2D.minimize_triangle_surface_loss`][adaptive.learner.learner2D.minimize_triangle_surface_loss] +- [`adaptive.learner.learner2D.resolution_loss_function`][adaptive.learner.learner2D.resolution_loss_function] Whenever a loss function has `_function` appended to its name, it is a factory function that returns the loss function with certain settings. @@ -119,14 +118,14 @@ def plotter(lrn): runner.live_plot(update_interval=0.2, plotter=plotter) ``` -The uniform sampling strategy is a common case to benchmark against, so the 1D and 2D versions are included in `adaptive` as {class}`adaptive.learner.learner1D.uniform_loss` and {class}`adaptive.learner.learner2D.uniform_loss`. +The uniform sampling strategy is a common case to benchmark against, so the 1D and 2D versions are included in `adaptive` as [`adaptive.learner.learner1D.uniform_loss`][adaptive.learner.learner1D.uniform_loss] and [`adaptive.learner.learner2D.uniform_loss`][adaptive.learner.learner2D.uniform_loss]. ## Doing better Of course, using `adaptive` for uniform sampling is a bit of a waste! Let’s see if we can do a bit better. -Below we define a loss per subdomain that scales with the degree of nonlinearity of the function (this is very similar to the default loss function for {class}`~adaptive.Learner2D`), but which is 0 for subdomains smaller than a certain area, and infinite for subdomains larger than a certain area. +Below we define a loss per subdomain that scales with the degree of nonlinearity of the function (this is very similar to the default loss function for [`Learner2D`][adaptive.Learner2D]), but which is 0 for subdomains smaller than a certain area, and infinite for subdomains larger than a certain area. A loss defined in this way means that the adaptive algorithm will first prioritise subdomains that are too large (infinite loss). After all subdomains are appropriately small it will prioritise places where the function is very nonlinear, but will ignore subdomains that are too small (0 loss). @@ -164,6 +163,4 @@ learner.plot(tri_alpha=0.3).relabel("1 / (x^2 + y^2) in log scale").opts( Awesome! We zoom in on the singularity, but not at the expense of sampling the rest of the domain a reasonable amount. -The above strategy is available as {class}`adaptive.learner.learner2D.resolution_loss_function`. - -[^download]: This notebook can be downloaded as **{nb-download}`tutorial.custom_loss.ipynb`** and {download}`tutorial.custom_loss.md`. +The above strategy is available as [`adaptive.learner.learner2D.resolution_loss_function`][adaptive.learner.learner2D.resolution_loss_function]. diff --git a/docs/notebooks/tutorial/tutorial.md b/docs/notebooks/tutorial/tutorial.md new file mode 100644 index 000000000..75ee27979 --- /dev/null +++ b/docs/notebooks/tutorial/tutorial.md @@ -0,0 +1,24 @@ +--- +kernelspec: + name: python3 + display_name: python3 +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: '0.13' + jupytext_version: 1.13.8 +--- +# Tutorial + +[Adaptive](https://github.com/python-adaptive/adaptive) +is a package for adaptively sampling functions with support for parallel +evaluation. + +This is an introductory notebook that shows some basic use cases. + +We recommend to start with the [`Learner1D` tutorial](tutorial.Learner1D.md). + +!!! note + Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. + Download the notebooks in order to see the real behaviour. diff --git a/docs/source/tutorial/tutorial.parallelism.md b/docs/notebooks/tutorial/tutorial.parallelism.md similarity index 78% rename from docs/source/tutorial/tutorial.parallelism.md rename to docs/notebooks/tutorial/tutorial.parallelism.md index 9ca43ddf2..f6daeefd2 100644 --- a/docs/source/tutorial/tutorial.parallelism.md +++ b/docs/notebooks/tutorial/tutorial.parallelism.md @@ -16,8 +16,8 @@ Often you will want to evaluate the function on some remote computing resources. ## `concurrent.futures` -By default {class}`adaptive.Runner` creates a `loky.get_reusable_executor`, which serializes functions by value so it also works with functions defined interactively (e.g. in a notebook). -You can also pass a {class}`~concurrent.futures.ProcessPoolExecutor` explicitly, e.g. to limit the number of workers, but then the function must be importable from `__main__` by the worker processes: +By default [`adaptive.Runner`][adaptive.Runner] creates a [`loky.get_reusable_executor`][loky.get_reusable_executor], which serializes functions by value so it also works with functions defined interactively (e.g. in a notebook). +You can also pass a [`ProcessPoolExecutor`][concurrent.futures.ProcessPoolExecutor] explicitly, e.g. to limit the number of workers, but then the function must be importable from `__main__` by the worker processes: ```python from concurrent.futures import ProcessPoolExecutor @@ -45,7 +45,7 @@ runner.live_plot() ## `distributed.Client` -On Windows by default {class}`adaptive.Runner` uses a `distributed.Client`. +On Windows by default [`adaptive.Runner`][adaptive.Runner] uses a `distributed.Client`. ```python import distributed @@ -58,7 +58,7 @@ runner.live_info() runner.live_plot(update_interval=0.1) ``` -Also check out the {ref}`Custom parallelization` section in the {ref}`advanced topics tutorial` for more control over caching and parallelization. +Also check out the [Custom parallelization](tutorial.advanced-topics.md#custom-parallelization-using-coroutines) section in the [advanced topics tutorial](tutorial.advanced-topics.md) for more control over caching and parallelization. ## `mpi4py.futures.MPIPoolExecutor` @@ -123,7 +123,7 @@ srun -n $SLURM_NTASKS --mpi=pmi2 ~/miniconda3/envs/py37_min/bin/python -m mpi4py ## `loky.get_reusable_executor` -This executor is basically a powered-up version of {class}`~concurrent.futures.ProcessPoolExecutor`, check its [documentation](https://loky.readthedocs.io/). +This executor is basically a powered-up version of [`ProcessPoolExecutor`][concurrent.futures.ProcessPoolExecutor], check its [documentation](https://loky.readthedocs.io/). Among other things, it allows to *reuse* the executor and uses `cloudpickle` for serialization. This means you can even learn closures, lambdas, or other functions that are not picklable with `pickle`. diff --git a/docs/overrides/partials/actions.html b/docs/overrides/partials/actions.html new file mode 100644 index 000000000..14bbbfed2 --- /dev/null +++ b/docs/overrides/partials/actions.html @@ -0,0 +1,34 @@ +{#- + Zensical's partials/actions.html, but pages rendered by docs/build_notebooks.py + link "Edit" and "View" to their notebook source in docs/notebooks/. +-#} +{% set edit_url = page.edit_url %} +{% if page.meta and page.meta.notebook_source and edit_url %} + {% set edit_uri = (config.edit_uri ~ "/") | replace("//", "/") %} + {% set edit_url = config.repo_url ~ "/" ~ edit_uri ~ page.meta.notebook_source %} +{% endif %} +{% if "content.action.copy" in features and page.markdown_url %} + +{% endif %} +{% if edit_url %} + {% if "content.action.edit" in features %} + + {% set icon = config.theme.icon.edit or "material/file-edit-outline" %} + {% include ".icons/" ~ icon ~ ".svg" %} + + {% endif %} + {% if "content.action.view" in features %} + {% if "/blob/" in edit_url %} + {% set part = "/blob/" %} + {% else %} + {% set part = "/edit/" %} + {% endif %} + + {% set icon = config.theme.icon.view or "material/file-eye-outline" %} + {% include ".icons/" ~ icon ~ ".svg" %} + + {% endif %} +{% endif %} diff --git a/docs/reference/adaptive.learner.average_learner.md b/docs/reference/adaptive.learner.average_learner.md new file mode 100644 index 000000000..08bb52c88 --- /dev/null +++ b/docs/reference/adaptive.learner.average_learner.md @@ -0,0 +1,3 @@ +# adaptive.AverageLearner + +::: adaptive.AverageLearner diff --git a/docs/reference/adaptive.learner.average_learner1D.md b/docs/reference/adaptive.learner.average_learner1D.md new file mode 100644 index 000000000..89577de2f --- /dev/null +++ b/docs/reference/adaptive.learner.average_learner1D.md @@ -0,0 +1,3 @@ +# adaptive.AverageLearner1D + +::: adaptive.AverageLearner1D diff --git a/docs/reference/adaptive.learner.balancing_learner.md b/docs/reference/adaptive.learner.balancing_learner.md new file mode 100644 index 000000000..16e4971ca --- /dev/null +++ b/docs/reference/adaptive.learner.balancing_learner.md @@ -0,0 +1,3 @@ +# adaptive.BalancingLearner + +::: adaptive.BalancingLearner diff --git a/docs/reference/adaptive.learner.base_learner.md b/docs/reference/adaptive.learner.base_learner.md new file mode 100644 index 000000000..3ed98b40a --- /dev/null +++ b/docs/reference/adaptive.learner.base_learner.md @@ -0,0 +1,3 @@ +# adaptive.BaseLearner + +::: adaptive.BaseLearner diff --git a/docs/reference/adaptive.learner.data_saver.md b/docs/reference/adaptive.learner.data_saver.md new file mode 100644 index 000000000..cde932794 --- /dev/null +++ b/docs/reference/adaptive.learner.data_saver.md @@ -0,0 +1,13 @@ +# adaptive.DataSaver + +## The `DataSaver` class + +::: adaptive.DataSaver + options: + heading_level: 3 + +## The `make_datasaver` function + +::: adaptive.make_datasaver + options: + heading_level: 3 diff --git a/docs/reference/adaptive.learner.integrator_learner.md b/docs/reference/adaptive.learner.integrator_learner.md new file mode 100644 index 000000000..6a05e5c1f --- /dev/null +++ b/docs/reference/adaptive.learner.integrator_learner.md @@ -0,0 +1,3 @@ +# adaptive.IntegratorLearner + +::: adaptive.IntegratorLearner diff --git a/docs/reference/adaptive.learner.learner1D.md b/docs/reference/adaptive.learner.learner1D.md new file mode 100644 index 000000000..9383b997a --- /dev/null +++ b/docs/reference/adaptive.learner.learner1D.md @@ -0,0 +1,33 @@ +# adaptive.Learner1D + +::: adaptive.Learner1D + +## Custom loss functions + +::: adaptive.learner.learner1D.default_loss + options: + heading_level: 3 + +::: adaptive.learner.learner1D.uniform_loss + options: + heading_level: 3 + +::: adaptive.learner.learner1D.uses_nth_neighbors + options: + heading_level: 3 + +::: adaptive.learner.learner1D.triangle_loss + options: + heading_level: 3 + +::: adaptive.learner.learner1D.curvature_loss_function + options: + heading_level: 3 + +::: adaptive.learner.learner1D.abs_min_log_loss + options: + heading_level: 3 + +::: adaptive.learner.learner1D.resolution_loss_function + options: + heading_level: 3 diff --git a/docs/reference/adaptive.learner.learner2D.md b/docs/reference/adaptive.learner.learner2D.md new file mode 100644 index 000000000..4e629dbf0 --- /dev/null +++ b/docs/reference/adaptive.learner.learner2D.md @@ -0,0 +1,31 @@ +# adaptive.Learner2D + +::: adaptive.Learner2D + +## Custom loss functions + +::: adaptive.learner.learner2D.default_loss + options: + heading_level: 3 + +::: adaptive.learner.learner2D.minimize_triangle_surface_loss + options: + heading_level: 3 + +::: adaptive.learner.learner2D.uniform_loss + options: + heading_level: 3 + +::: adaptive.learner.learner2D.resolution_loss_function + options: + heading_level: 3 + +## Helper functions + +::: adaptive.learner.learner2D.areas + options: + heading_level: 3 + +::: adaptive.learner.learner2D.deviations + options: + heading_level: 3 diff --git a/docs/reference/adaptive.learner.learnerND.md b/docs/reference/adaptive.learner.learnerND.md new file mode 100644 index 000000000..57ec73be9 --- /dev/null +++ b/docs/reference/adaptive.learner.learnerND.md @@ -0,0 +1,17 @@ +# adaptive.LearnerND + +::: adaptive.LearnerND + +## Custom loss functions + +::: adaptive.learner.learnerND.default_loss + options: + heading_level: 3 + +::: adaptive.learner.learnerND.uniform_loss + options: + heading_level: 3 + +::: adaptive.learner.learnerND.std_loss + options: + heading_level: 3 diff --git a/docs/reference/adaptive.learner.sequence_learner.md b/docs/reference/adaptive.learner.sequence_learner.md new file mode 100644 index 000000000..f09c2cf6c --- /dev/null +++ b/docs/reference/adaptive.learner.sequence_learner.md @@ -0,0 +1,3 @@ +# adaptive.SequenceLearner + +::: adaptive.SequenceLearner diff --git a/docs/reference/adaptive.learner.triangulation.md b/docs/reference/adaptive.learner.triangulation.md new file mode 100644 index 000000000..3622c9d9a --- /dev/null +++ b/docs/reference/adaptive.learner.triangulation.md @@ -0,0 +1,5 @@ +# adaptive.learner.triangulation module + +::: adaptive.learner.triangulation + options: + show_root_heading: false diff --git a/docs/reference/adaptive.learner.triangulation_backend.md b/docs/reference/adaptive.learner.triangulation_backend.md new file mode 100644 index 000000000..7b2561cef --- /dev/null +++ b/docs/reference/adaptive.learner.triangulation_backend.md @@ -0,0 +1,5 @@ +# adaptive.learner.triangulation_backend module + +::: adaptive.learner.triangulation_backend + options: + show_root_heading: false diff --git a/docs/reference/adaptive.md b/docs/reference/adaptive.md new file mode 100644 index 000000000..a43f241d1 --- /dev/null +++ b/docs/reference/adaptive.md @@ -0,0 +1,35 @@ +# API reference + +## Learners + +| Page | Description | +| --- | --- | +| [`Learner1D`](adaptive.learner.learner1D.md) | 1D functions `f: ℝ β†’ ℝ^N`, and its loss functions | +| [`Learner2D`](adaptive.learner.learner2D.md) | 2D functions `f: ℝ^2 β†’ ℝ^N`, and its loss functions | +| [`LearnerND`](adaptive.learner.learnerND.md) | ND functions `f: ℝ^N β†’ ℝ^M`, and its loss functions | +| [`AverageLearner`](adaptive.learner.average_learner.md) | Averaging a random variable | +| [`AverageLearner1D`](adaptive.learner.average_learner1D.md) | Stochastic 1D functions | +| [`BalancingLearner`](adaptive.learner.balancing_learner.md) | Running several learners at once | +| [`DataSaver`](adaptive.learner.data_saver.md) | Functions that return more than a scalar or vector | +| [`IntegratorLearner`](adaptive.learner.integrator_learner.md) | Integrating 1D functions | +| [`SequenceLearner`](adaptive.learner.sequence_learner.md) | Evaluating a fixed sequence of points | +| [`BaseLearner`](adaptive.learner.base_learner.md) | Base class for all learners | + +## Runners + +| Page | Description | +| --- | --- | +| [`Runner`](adaptive.runner.Runner.md) | Alias of `AsyncRunner` | +| [`AsyncRunner`](adaptive.runner.AsyncRunner.md) | Runs a learner in the background on an asyncio event loop | +| [`BlockingRunner`](adaptive.runner.BlockingRunner.md) | Runs a learner and blocks until done | +| [`BaseRunner`](adaptive.runner.BaseRunner.md) | Base class for all runners | +| [Runner extras](adaptive.runner.extras.md) | Stopping criteria, `simple`, `SequentialExecutor`, and `replay_log` | + +## Other + +| Page | Description | +| --- | --- | +| [`adaptive.learner.triangulation`](adaptive.learner.triangulation.md) | Triangulation used by `LearnerND` | +| [`adaptive.learner.triangulation_backend`](adaptive.learner.triangulation_backend.md) | Selecting the Python or Rust triangulation for `LearnerND` | +| [`adaptive.notebook_integration`](adaptive.notebook_integration.md) | Live plotting and widgets in Jupyter | +| [`adaptive.utils`](adaptive.utils.md) | Utilities | diff --git a/docs/reference/adaptive.notebook_integration.md b/docs/reference/adaptive.notebook_integration.md new file mode 100644 index 000000000..31848388b --- /dev/null +++ b/docs/reference/adaptive.notebook_integration.md @@ -0,0 +1,5 @@ +# adaptive.notebook_integration module + +::: adaptive.notebook_integration + options: + show_root_heading: false diff --git a/docs/reference/adaptive.runner.AsyncRunner.md b/docs/reference/adaptive.runner.AsyncRunner.md new file mode 100644 index 000000000..6feb6baa1 --- /dev/null +++ b/docs/reference/adaptive.runner.AsyncRunner.md @@ -0,0 +1,3 @@ +# adaptive.AsyncRunner + +::: adaptive.runner.AsyncRunner diff --git a/docs/reference/adaptive.runner.BaseRunner.md b/docs/reference/adaptive.runner.BaseRunner.md new file mode 100644 index 000000000..b1170e167 --- /dev/null +++ b/docs/reference/adaptive.runner.BaseRunner.md @@ -0,0 +1,3 @@ +# adaptive.runner.BaseRunner + +::: adaptive.runner.BaseRunner diff --git a/docs/reference/adaptive.runner.BlockingRunner.md b/docs/reference/adaptive.runner.BlockingRunner.md new file mode 100644 index 000000000..7631065fb --- /dev/null +++ b/docs/reference/adaptive.runner.BlockingRunner.md @@ -0,0 +1,3 @@ +# adaptive.BlockingRunner + +::: adaptive.BlockingRunner diff --git a/docs/reference/adaptive.runner.Runner.md b/docs/reference/adaptive.runner.Runner.md new file mode 100644 index 000000000..7ad3495cd --- /dev/null +++ b/docs/reference/adaptive.runner.Runner.md @@ -0,0 +1,8 @@ +# adaptive.Runner + +`adaptive.Runner` is an alias of [`AsyncRunner`][adaptive.runner.AsyncRunner], +the runner that evaluates the learner's function in the background of a Jupyter +notebook or any other running asyncio event loop. +Use [`BlockingRunner`][adaptive.runner.BlockingRunner] to run from a script without an event loop. + +::: adaptive.Runner diff --git a/docs/source/reference/adaptive.runner.extras.md b/docs/reference/adaptive.runner.extras.md similarity index 56% rename from docs/source/reference/adaptive.runner.extras.md rename to docs/reference/adaptive.runner.extras.md index d2dfaa89c..f14659dc5 100644 --- a/docs/source/reference/adaptive.runner.extras.md +++ b/docs/reference/adaptive.runner.extras.md @@ -8,31 +8,30 @@ for "continue running" and `True` for "stop". This gives you a lot of flexibilit for defining your own stopping conditions, however we also provide some common stopping conditions as a convenience. For example, to continue until the loss is below a threshold `x`, you may specify `loss_goal=x`. Similarly, to continue until `n` points have been sampled, you may -specify `npoints_goal=n`. See the Runner docstring for details. +specify `npoints_goal=n`. See the [`Runner`][adaptive.runner.AsyncRunner] docstring for details. -```{eval-rst} -.. autofunction:: adaptive.runner.auto_goal -``` +::: adaptive.runner.auto_goal + options: + heading_level: 3 -```{eval-rst} -.. autofunction:: adaptive.runner.stop_after -``` +::: adaptive.runner.stop_after + options: + heading_level: 3 ## Simple executor -```{eval-rst} -.. autofunction:: adaptive.runner.simple -``` +::: adaptive.runner.simple + options: + heading_level: 3 -## Sequential excecutor +## Sequential executor -```{eval-rst} -.. autoclass:: adaptive.runner.SequentialExecutor - -``` +::: adaptive.runner.SequentialExecutor + options: + heading_level: 3 ## Replay log -```{eval-rst} -.. autofunction:: adaptive.runner.replay_log -``` +::: adaptive.runner.replay_log + options: + heading_level: 3 diff --git a/docs/reference/adaptive.utils.md b/docs/reference/adaptive.utils.md new file mode 100644 index 000000000..d90d6a408 --- /dev/null +++ b/docs/reference/adaptive.utils.md @@ -0,0 +1,5 @@ +# adaptive.utils module + +::: adaptive.utils + options: + show_root_heading: false diff --git a/docs/source/CHANGELOG.md b/docs/source/CHANGELOG.md deleted file mode 120000 index 03cb73106..000000000 --- a/docs/source/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -../../CHANGELOG.md diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css deleted file mode 100644 index 0c1e64483..000000000 --- a/docs/source/_static/custom.css +++ /dev/null @@ -1,62 +0,0 @@ -.ignore-css { - all: unset !important; -} - -/* https://gitter.im/pyviz/pyviz?at=5e8efb2b16f84f0461683446 */ -.panel-widget-box.bk form { - display: none !important; - margin-left: auto !important; - margin-right: auto !important; - width: 25%; -} - -/* Some additional styling taken form the Jupyter notebook CSS */ -/* but changed `rendered_html` to `text_html` */ - -div.text_html table { - border: none; - border-collapse: collapse; - border-spacing: 0; - /* color: black; */ - font-size: 12px; - table-layout: fixed; -} -div.text_html thead { - border-bottom: 1px solid; - vertical-align: bottom; -} -div.text_html tr, -div.text_html th, -div.text_html td { - text-align: right; - vertical-align: middle; - padding: 0.5em 0.5em; - line-height: normal; - white-space: normal; - max-width: none; - border: none; -} -div.text_html th { - font-weight: bold; -} - -/* alternating color light theme */ -body[data-theme="light"] div.text_html tbody tr:nth-child(odd) { - background: #f5f5f5; -} -body[data-theme="light"] div.text_html tbody tr:hover { - background: rgba(66, 165, 245, 0.2); -} - -/* alternating color dark theme */ -body[data-theme="dark"] div.text_html tbody tr:nth-child(odd) { - background: #696969; -} -body[data-theme="dark"] div.text_html tbody tr:hover { - background: rgba(175, 175, 175, 0.2); -} - -/* for rendered markdown on meta_data.md docs */ -body[data-theme="dark"] .widget-html-content { - color: #f5f5f5; -} diff --git a/docs/source/_static/js/theme.js b/docs/source/_static/js/theme.js deleted file mode 100644 index 7bf9f6a40..000000000 --- a/docs/source/_static/js/theme.js +++ /dev/null @@ -1,262 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2013-2018 Dave Snider, Read the Docs, Inc. & contributors - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// var jQuery = (typeof(window) != 'undefined') ? window.jQuery : require('jquery'); - -// Sphinx theme nav state -function ThemeNav () { - - var nav = { - navBar: null, - win: null, - winScroll: false, - winResize: false, - linkScroll: false, - winPosition: 0, - winHeight: null, - docHeight: null, - isRunning: false - }; - - nav.enable = function (withStickyNav) { - var self = this; - - // TODO this can likely be removed once the theme javascript is broken - // out from the RTD assets. This just ensures old projects that are - // calling `enable()` get the sticky menu on by default. All other cals - // to `enable` should include an argument for enabling the sticky menu. - if (typeof(withStickyNav) == 'undefined') { - withStickyNav = true; - } - - if (self.isRunning) { - // Only allow enabling nav logic once - return; - } - - self.isRunning = true; - jQuery(function ($) { - self.init($); - - self.reset(); - self.win.on('hashchange', self.reset); - - if (withStickyNav) { - // Set scroll monitor - self.win.on('scroll', function () { - if (!self.linkScroll) { - if (!self.winScroll) { - self.winScroll = true; - requestAnimationFrame(function() { self.onScroll(); }); - } - } - }); - } - - // Set resize monitor - self.win.on('resize', function () { - if (!self.winResize) { - self.winResize = true; - requestAnimationFrame(function() { self.onResize(); }); - } - }); - - self.onResize(); - }); - - }; - - // TODO remove this with a split in theme and Read the Docs JS logic as - // well, it's only here to support 0.3.0 installs of our theme. - nav.enableSticky = function() { - this.enable(true); - }; - - nav.init = function ($) { - var doc = $(document), - self = this; - - this.navBar = $('div.wy-side-scroll:first'); - this.win = $(window); - - // Set up javascript UX bits - $(document) - // Shift nav in mobile when clicking the menu. - .on('click', "[data-toggle='wy-nav-top']", function() { - $("[data-toggle='wy-nav-shift']").toggleClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); - }) - - // Nav menu link click operations - .on('click', ".wy-menu-vertical .current ul li a", function() { - var target = $(this); - // Close menu when you click a link. - $("[data-toggle='wy-nav-shift']").removeClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); - // Handle dynamic display of l3 and l4 nav lists - self.toggleCurrent(target); - self.hashChange(); - }) - .on('click', "[data-toggle='rst-current-version']", function() { - $("[data-toggle='rst-versions']").toggleClass("shift-up"); - }) - - // Make tables responsive - $("table.docutils:not(.field-list,.footnote,.citation)") - .wrap("
"); - - // Add extra class to responsive tables that contain - // footnotes or citations so that we can target them for styling - $("table.docutils.footnote") - .wrap("
"); - $("table.docutils.citation") - .wrap("
"); - - // Add expand links to all parents of nested ul - $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () { - var link = $(this); - expand = $(''); - expand.on('click', function (ev) { - self.toggleCurrent(link); - ev.stopPropagation(); - return false; - }); - link.prepend(expand); - }); - }; - - nav.reset = function () { - // Get anchor from URL and open up nested nav - var anchor = encodeURI(window.location.hash) || '#'; - - try { - var vmenu = $('.wy-menu-vertical'); - var link = vmenu.find('[href="' + anchor + '"]'); - if (link.length === 0) { - // this link was not found in the sidebar. - // Find associated id element, then its closest section - // in the document and try with that one. - var id_elt = $('.document [id="' + anchor.substring(1) + '"]'); - var closest_section = id_elt.closest('div.section'); - link = vmenu.find('[href="#' + closest_section.attr("id") + '"]'); - if (link.length === 0) { - // still not found in the sidebar. fall back to main section - link = vmenu.find('[href="#"]'); - } - } - // If we found a matching link then reset current and re-apply - // otherwise retain the existing match - if (link.length > 0) { - $('.wy-menu-vertical .current').removeClass('current'); - link.addClass('current'); - link.closest('li.toctree-l1').addClass('current'); - link.closest('li.toctree-l1').parent().addClass('current'); - link.closest('li.toctree-l1').addClass('current'); - link.closest('li.toctree-l2').addClass('current'); - link.closest('li.toctree-l3').addClass('current'); - link.closest('li.toctree-l4').addClass('current'); - link[0].scrollIntoView(); - } - } - catch (err) { - console.log("Error expanding nav for anchor", err); - } - - }; - - nav.onScroll = function () { - this.winScroll = false; - var newWinPosition = this.win.scrollTop(), - winBottom = newWinPosition + this.winHeight, - navPosition = this.navBar.scrollTop(), - newNavPosition = navPosition + (newWinPosition - this.winPosition); - if (newWinPosition < 0 || winBottom > this.docHeight) { - return; - } - this.navBar.scrollTop(newNavPosition); - this.winPosition = newWinPosition; - }; - - nav.onResize = function () { - this.winResize = false; - this.winHeight = this.win.height(); - this.docHeight = $(document).height(); - }; - - nav.hashChange = function () { - this.linkScroll = true; - this.win.one('hashchange', function () { - this.linkScroll = false; - }); - }; - - nav.toggleCurrent = function (elem) { - var parent_li = elem.closest('li'); - parent_li.siblings('li.current').removeClass('current'); - parent_li.siblings().find('li.current').removeClass('current'); - parent_li.find('> ul li.current').removeClass('current'); - parent_li.toggleClass('current'); - } - - return nav; -}; - -module.exports.ThemeNav = ThemeNav(); - -if (typeof(window) != 'undefined') { - window.SphinxRtdTheme = { - Navigation: module.exports.ThemeNav, - // TODO remove this once static assets are split up between the theme - // and Read the Docs. For now, this patches 0.3.0 to be backwards - // compatible with a pre-0.3.0 layout.html - StickyNav: module.exports.ThemeNav, - }; -} - - -// requestAnimationFrame polyfill by Erik MΓΆller. fixes from Paul Irish and Tino Zijdel -// https://gist.github.com/paulirish/1579671 -// MIT license - -(function() { - var lastTime = 0; - var vendors = ['ms', 'moz', 'webkit', 'o']; - for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { - window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; - window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] - || window[vendors[x]+'CancelRequestAnimationFrame']; - } - - if (!window.requestAnimationFrame) - window.requestAnimationFrame = function(callback, element) { - var currTime = new Date().getTime(); - var timeToCall = Math.max(0, 16 - (currTime - lastTime)); - var id = window.setTimeout(function() { callback(currTime + timeToCall); }, - timeToCall); - lastTime = currTime + timeToCall; - return id; - }; - - if (!window.cancelAnimationFrame) - window.cancelAnimationFrame = function(id) { - clearTimeout(id); - }; -}()); diff --git a/docs/source/_templates/base.html b/docs/source/_templates/base.html deleted file mode 100644 index 5423b63ba..000000000 --- a/docs/source/_templates/base.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - {%- block site_meta -%} - - - - - {%- if metatags %}{{ metatags }}{% endif -%} - - {%- block linktags %} - {%- if hasdoc('about') -%} - - {%- endif -%} - {%- if hasdoc('genindex') -%} - - {%- endif -%} - {%- if hasdoc('search') -%} - - {%- endif -%} - {%- if hasdoc('copyright') -%} - - {%- endif -%} - {%- if next -%} - - {%- endif -%} - {%- if prev -%} - - {%- endif -%} - {#- rel="canonical" (set by html_baseurl) -#} - {%- if pageurl %} - - {%- endif %} - {%- endblock linktags %} - - {# Favicon #} - {%- if favicon_url -%} - - {%- endif -%} - - {#- Generator banner -#} - - - {%- endblock site_meta -%} - - {#- Site title -#} - {%- block htmltitle -%} - {% if not docstitle %} - {{ title|striptags|e }} - {% elif pagename == master_doc %} - {{ docstitle|striptags|e }} - {% else %} - {{ title|striptags|e }} - {{ docstitle|striptags|e }} - {% endif %} - {%- endblock -%} - - {%- block styles -%} - - {# Custom stylesheets #} - {%- block regular_styles -%} - {%- for css in css_files -%} - {% if css|attr("filename") -%} - {{ css_tag(css) }} - {%- else -%} - - {%- endif %} - {% endfor -%} - {%- endblock regular_styles -%} - - {#- Theme-related stylesheets -#} - {%- block theme_styles %} - {% include "partials/_head_css_variables.html" with context %} - {%- endblock -%} - - {%- block extra_styles %} - {%- endblock -%} - - {%- endblock styles -%} - - {#- Custom front matter #} - {%- block extrahead -%}{%- endblock -%} - - {# Custom JS #} - {%- block regular_scripts -%} - {% for path in script_files -%} - {{ js_tag(path) }} - {% endfor -%} - {%- endblock regular_scripts -%} - - {# Theme-related JavaScript code #} - {%- block theme_scripts -%} - {%- endblock -%} - - - - {% block body %} - - {% endblock %} - - {%- block scripts -%} - {%- endblock scripts -%} - - diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 5ea9e6ef7..000000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,107 +0,0 @@ -# -- Path setup -------------------------------------------------------------- - -import os -import sys -from pathlib import Path - -package_path = Path("../..").resolve() -# Insert into sys.path so that we can import adaptive here -sys.path.insert(0, str(package_path)) -# Insert into PYTHONPATH so that jupyter-sphinx will pick it up -os.environ["PYTHONPATH"] = ":".join( - (str(package_path), os.environ.get("PYTHONPATH", "")), -) -# Insert `docs/` such that we can run the logo scripts -docs_path = Path("..").resolve() -sys.path.insert(1, str(docs_path)) - - -import adaptive # noqa: E402, isort:skip - -# -- Project information ----------------------------------------------------- - -project = "adaptive" -copyright = "2018-2023, Adaptive Authors" -author = "Adaptive Authors" - -# The short X.Y version -version = ".".join(adaptive.__version__.split(".")[:3]) -version = version -# The full version, including alpha/beta/rc tags -release = version - -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.autosummary", - "sphinx.ext.autosectionlabel", - "sphinx.ext.intersphinx", - "sphinx.ext.mathjax", - "sphinx.ext.viewcode", - "sphinx.ext.napoleon", - "myst_nb", - "sphinx_fontawesome", -] -source_parsers = {} -templates_path = ["_templates"] -source_suffix = [".rst", ".md"] -master_doc = "index" -language = None -exclude_patterns = [] -pygments_style = "sphinx" -# TODO: change to "furo" when https://github.com/executablebooks/MyST-NB/issues/54 is fixed (again) -html_theme = "furo" -html_static_path = ["_static"] -htmlhelp_basename = "adaptivedoc" - - -# -- Extension configuration ------------------------------------------------- - -default_role = "autolink" - -intersphinx_mapping = { - "python": ("https://docs.python.org/3", None), - "distributed": ("https://distributed.readthedocs.io/en/stable/", None), - "holoviews": ("https://holoviews.org/", None), - "ipyparallel": ("https://ipyparallel.readthedocs.io/en/stable/", None), - "scipy": ("https://docs.scipy.org/doc/scipy/reference", None), - "loky": ("https://loky.readthedocs.io/en/stable/", None), -} -html_js_files = [ - "https://cdn.bokeh.org/bokeh/release/bokeh-2.4.0.min.js", - "https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.0.min.js", - "https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.0.min.js", - "https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.0.min.js", - "https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.0.min.js", - "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js", -] -html_logo = "_static/logo_docs.png" - - -# myst-nb configuration -nb_execution_mode = "cache" -# Generating the logo animation (logo.md) takes ~3 minutes on Read the Docs -# builders, which intermittently exceeded the previous 180 s limit. -nb_execution_timeout = 600 -nb_execution_raise_on_error = True - - -def replace_named_emojis(input_file: Path, output_file: Path) -> None: - """Replace named emojis in a file with unicode emojis.""" - import emoji - - with input_file.open("r") as infile: - content = infile.read() - content_with_emojis = emoji.emojize(content, language="alias") - - with output_file.open("w") as outfile: - outfile.write(content_with_emojis) - - -# Call the function to replace emojis in the README.md file -input_file = package_path / "README.md" -output_file = docs_path / "README.md" -replace_named_emojis(input_file, output_file) - - -def setup(app): - app.add_css_file("custom.css") # For the `live_info` widget diff --git a/docs/source/docs.md b/docs/source/docs.md deleted file mode 100644 index 4d1fa479c..000000000 --- a/docs/source/docs.md +++ /dev/null @@ -1,15 +0,0 @@ -```{include} ../README.md ---- -start-after: -end-before: ---- -``` - -```{include} ../../AUTHORS.md -``` - -```{include} ../README.md ---- -start-after: ---- -``` diff --git a/docs/source/index.md b/docs/source/index.md deleted file mode 100644 index b1bfe8977..000000000 --- a/docs/source/index.md +++ /dev/null @@ -1,49 +0,0 @@ -# ![logo](https://adaptive.readthedocs.io/en/latest/_static/logo.png) *Adaptive*: Parallel Active Learning of Mathematical Functions - -```{include} ../README.md ---- -start-after: -end-before: ---- -``` - -```{include} logo.md -``` - -```{include} ../README.md ---- -start-after: -end-before: ---- -``` - -```{include} ../README.md ---- -start-after: -end-before: ---- -``` - -```{tip} -Start with the {ref}`1D function learning tutorial`. -``` - -```{toctree} -:hidden: true - -self -``` - -```{toctree} -:hidden: true -:maxdepth: 2 - -algorithms_and_examples -docs -tutorial/tutorial -benchmarks -gallery -reference/adaptive -CHANGELOG -faq -``` diff --git a/docs/source/logo.md b/docs/source/logo.md deleted file mode 100644 index 33939990b..000000000 --- a/docs/source/logo.md +++ /dev/null @@ -1,218 +0,0 @@ ---- -jupytext: - text_representation: - extension: .md - format_name: myst - format_version: 0.13 - jupytext_version: 1.19.1 -kernelspec: - display_name: Python 3 (ipykernel) - language: python - name: python3 ---- - -```{code-cell} ipython3 -:tags: [remove-input] - -import functools -import subprocess -import tempfile -from pathlib import Path - -import matplotlib.tri as mtri -import numpy as np -from matplotlib import animation -from matplotlib import pyplot as plt -from matplotlib.animation import FFMpegWriter -from PIL import Image, ImageDraw -from tqdm.auto import tqdm - -import adaptive - - -@functools.lru_cache -def make_cut(size, rad): - # Make new images - circle = Image.new("L", (rad * 2, rad * 2), color=1) - draw = ImageDraw.Draw(circle) - draw.ellipse((0, 0, rad * 2, rad * 2), fill=0) - alpha = Image.new("L", size, 0) - - # Crop circles - w, h = size - alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0)) - alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad)) - alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0)) - alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad)) - - # To array - return np.array(alpha) - - -@functools.lru_cache -def add_rounded_corners(size=(1000, 1000), rad=300): - cut = make_cut(size, rad) - cut = cut.reshape((*cut.shape, 1)).repeat(4, axis=2) - - # Set the corners to (252, 252, 252, 255) to match the RTD background #FCFCFC - cut[:, :, -1] *= 255 - cut[:, :, :-1] *= 252 - return cut - - -def remove_rounded_corners(fname): - im = Image.open(fname) - ar = np.array(im) - cut = make_cut(size=ar.shape[:-1], rad=round(ar.shape[0] * 0.3)).astype(bool) - ar[:, :, -1] = np.where(~cut, ar[:, :, -1], 0) - im_new = Image.fromarray(ar) - im_new.save(fname) - return im_new - - -def learner_till(till, learner, data): - new_learner = adaptive.Learner2D(None, bounds=learner.bounds) - new_learner.data = dict(data[:till]) - for x, y in learner._bounds_points: - # always include the bounds - new_learner.tell((x, y), learner.data[x, y]) - return new_learner - - -def plot_tri(learner, ax): - tri = learner.ip().tri - triang = mtri.Triangulation(*tri.points.T, triangles=tri.simplices) - return ax.triplot(triang, c="k", lw=0.8, alpha=0.8) - - -def get_new_artists(npoints, learner, data, rounded_corners, ax): - new_learner = learner_till(npoints, learner, data) - line1, line2 = plot_tri(new_learner, ax) - data = np.rot90(new_learner.interpolated_on_grid()[-1]) - im = ax.imshow(data, extent=(-0.5, 0.5, -0.5, 0.5), cmap="viridis") - if rounded_corners is None: - return im, line1, line2 - else: - im2 = ax.imshow(rounded_corners, extent=(-0.5, 0.5, -0.5, 0.5), zorder=10) - return im, line1, line2, im2 - - -@functools.lru_cache -def create_and_run_learner(): - def ring(xy): - import numpy as np - - x, y = xy - a = 0.2 - return x + np.exp(-((x**2 + y**2 - 0.75**2) ** 2) / a**4) - - learner = adaptive.Learner2D(ring, bounds=[(-1, 1), (-1, 1)]) - adaptive.runner.simple(learner, loss_goal=0.005) - return learner - - -def get_figure(): - fig, ax = plt.subplots(figsize=(5, 5)) - fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=None, hspace=None) - ax.set_xticks([]) - ax.set_yticks([]) - ax.spines["top"].set_visible(False) - ax.spines["right"].set_visible(False) - ax.spines["bottom"].set_visible(False) - ax.spines["left"].set_visible(False) - return fig, ax - - -def setup(nseconds=15): - learner = create_and_run_learner() - - data = list(learner.data.items()) - - fig, ax = get_figure() - - npoints = (len(data) * np.linspace(0, 1, 24 * nseconds) ** 2).astype(int) - rounded_corners = add_rounded_corners(size=(1000, 1000), rad=300) - return npoints, learner, data, rounded_corners, fig, ax - - -def animate_mp4(fname="source/_static/logo_docs.mp4", nseconds=15): - npoints, learner, data, rounded_corners, fig, ax = setup() - artists = [ - get_new_artists(n, learner, data, rounded_corners, ax) for n in tqdm(npoints) - ] - ani = animation.ArtistAnimation(fig, artists, blit=True) - ani.save(fname, writer=FFMpegWriter(fps=24)) - - -def animate_png(folder=None, nseconds=15): - npoints, learner, data, rounded_corners, fig, ax = setup(nseconds) - if folder is None: - folder = Path(tempfile.gettempdir()) / next(tempfile._get_candidate_names()) - folder = Path(folder) - folder.mkdir(parents=True, exist_ok=True) - fnames = [] - ims = [] - for i, n in tqdm(enumerate(npoints), total=len(npoints)): - fname = folder / f"logo_docs_{i:07d}.png" - fnames.append(fname) - npoints, learner, data, _, fig, ax = setup(nseconds) - get_new_artists(n, learner, data, None, ax) - fig.savefig(fname, transparent=True) - ax.cla() - plt.close(fig) - im = remove_rounded_corners(fname) - ims.append(im) - return fnames, ims - - -def save_webp(fname_webp, ims): - im, *_ims = ims - im.save( - fname_webp, - save_all=True, - append_images=_ims, - opimize=False, - durarion=2, - quality=70, - ) - - -def save_webm(fname, fnames): - args = [ - "ffmpeg", - "-framerate", - "24", - "-f", - "image2", - "-i", - str(fnames[0]).replace("0000000", "%07d"), - "-c:v", - "libvpx-vp9", - "-pix_fmt", - "yuva420p", - "-crf", - "23", # 0 is lossless 51 is worst - "-y", - fname, - ] - return subprocess.run(args, capture_output=True) - - -if __name__ == "__main__": - fname_mp4 = Path("_static/logo_docs.mp4") - # if not fname_mp4.exists(): - # animate_mp4(fname_mp4) - fname_webm = fname_mp4.with_suffix(".webm") - if not fname_webm.exists(): - fnames, ims = animate_png() - save_webm(fname_webm, fnames) -``` - -```{eval-rst} -.. raw:: html - -
-``` diff --git a/docs/source/reference/adaptive.learner.average_learner.md b/docs/source/reference/adaptive.learner.average_learner.md deleted file mode 100644 index a299797b6..000000000 --- a/docs/source/reference/adaptive.learner.average_learner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.AverageLearner - -```{eval-rst} -.. autoclass:: adaptive.AverageLearner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.learner.average_learner1D.md b/docs/source/reference/adaptive.learner.average_learner1D.md deleted file mode 100644 index baf2780e8..000000000 --- a/docs/source/reference/adaptive.learner.average_learner1D.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.AverageLearner1D - -```{eval-rst} -.. autoclass:: adaptive.AverageLearner1D - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.learner.balancing_learner.md b/docs/source/reference/adaptive.learner.balancing_learner.md deleted file mode 100644 index f977a0aa8..000000000 --- a/docs/source/reference/adaptive.learner.balancing_learner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.BalancingLearner - -```{eval-rst} -.. autoclass:: adaptive.BalancingLearner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.learner.base_learner.md b/docs/source/reference/adaptive.learner.base_learner.md deleted file mode 100644 index 1b3845ebd..000000000 --- a/docs/source/reference/adaptive.learner.base_learner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.BaseLearner - -```{eval-rst} -.. autoclass:: adaptive.learner.BaseLearner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.learner.data_saver.md b/docs/source/reference/adaptive.learner.data_saver.md deleted file mode 100644 index 38f573601..000000000 --- a/docs/source/reference/adaptive.learner.data_saver.md +++ /dev/null @@ -1,17 +0,0 @@ -# adaptive.DataSaver - -## The `DataSaver` class - -```{eval-rst} -.. autoclass:: adaptive.DataSaver - :members: - :undoc-members: - :show-inheritance: - -``` - -## The `make_datasaver` function - -```{eval-rst} -.. autofunction:: adaptive.make_datasaver -``` diff --git a/docs/source/reference/adaptive.learner.integrator_learner.md b/docs/source/reference/adaptive.learner.integrator_learner.md deleted file mode 100644 index f659f75a6..000000000 --- a/docs/source/reference/adaptive.learner.integrator_learner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.IntegratorLearner - -```{eval-rst} -.. autoclass:: adaptive.IntegratorLearner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.learner.learner1D.md b/docs/source/reference/adaptive.learner.learner1D.md deleted file mode 100644 index 3d7b78c66..000000000 --- a/docs/source/reference/adaptive.learner.learner1D.md +++ /dev/null @@ -1,39 +0,0 @@ -# adaptive.Learner1D - -```{eval-rst} -.. autoclass:: adaptive.Learner1D - :members: - :undoc-members: - :show-inheritance: - -``` - -## Custom loss functions - -```{eval-rst} -.. autofunction:: adaptive.learner.learner1D.default_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner1D.uniform_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner1D.uses_nth_neighbors -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner1D.triangle_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner1D.curvature_loss_function -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner1D.abs_min_log_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner1D.resolution_loss_function -``` diff --git a/docs/source/reference/adaptive.learner.learner2D.md b/docs/source/reference/adaptive.learner.learner2D.md deleted file mode 100644 index 270e0ac06..000000000 --- a/docs/source/reference/adaptive.learner.learner2D.md +++ /dev/null @@ -1,38 +0,0 @@ -# adaptive.Learner2D - -```{eval-rst} -.. autoclass:: adaptive.Learner2D - :members: - :undoc-members: - :show-inheritance: - -``` - -## Custom loss functions - -```{eval-rst} -.. autofunction:: adaptive.learner.learner2D.default_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner2D.minimize_triangle_surface_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner2D.uniform_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner2D.resolution_loss_function - -``` - -## Helper functions - -```{eval-rst} -.. autofunction:: adaptive.learner.learner2D.areas -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learner2D.deviations -``` diff --git a/docs/source/reference/adaptive.learner.learnerND.md b/docs/source/reference/adaptive.learner.learnerND.md deleted file mode 100644 index f2e36e1cf..000000000 --- a/docs/source/reference/adaptive.learner.learnerND.md +++ /dev/null @@ -1,22 +0,0 @@ -# adaptive.LearnerND - -```{eval-rst} -.. autoclass:: adaptive.LearnerND - :members: - :undoc-members: - :show-inheritance: -``` - -## Custom loss functions - -```{eval-rst} -.. autofunction:: adaptive.learner.learnerND.default_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learnerND.uniform_loss -``` - -```{eval-rst} -.. autofunction:: adaptive.learner.learnerND.std_loss -``` diff --git a/docs/source/reference/adaptive.learner.sequence_learner.md b/docs/source/reference/adaptive.learner.sequence_learner.md deleted file mode 100644 index 91be8372d..000000000 --- a/docs/source/reference/adaptive.learner.sequence_learner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.SequenceLearner - -```{eval-rst} -.. autoclass:: adaptive.SequenceLearner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.learner.triangulation.md b/docs/source/reference/adaptive.learner.triangulation.md deleted file mode 100644 index e5f8012f3..000000000 --- a/docs/source/reference/adaptive.learner.triangulation.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.learner.triangulation module - -```{eval-rst} -.. automodule:: adaptive.learner.triangulation - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.learner.triangulation_backend.md b/docs/source/reference/adaptive.learner.triangulation_backend.md deleted file mode 100644 index 7e3407d83..000000000 --- a/docs/source/reference/adaptive.learner.triangulation_backend.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.learner.triangulation_backend module - -```{eval-rst} -.. automodule:: adaptive.learner.triangulation_backend - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.md b/docs/source/reference/adaptive.md deleted file mode 100644 index fb26c5837..000000000 --- a/docs/source/reference/adaptive.md +++ /dev/null @@ -1,36 +0,0 @@ -# πŸ“œ API Documentation - -## Learners - -```{toctree} -adaptive.learner.average_learner -adaptive.learner.average_learner1D -adaptive.learner.base_learner -adaptive.learner.balancing_learner -adaptive.learner.data_saver -adaptive.learner.integrator_learner -adaptive.learner.learner1D -adaptive.learner.learner2D -adaptive.learner.learnerND -adaptive.learner.sequence_learner -adaptive.learner.skopt_learner -``` - -## Runners - -```{toctree} -adaptive.runner.Runner -adaptive.runner.AsyncRunner -adaptive.runner.BlockingRunner -adaptive.runner.BaseRunner -adaptive.runner.extras -``` - -## Other - -```{toctree} -adaptive.learner.triangulation -adaptive.learner.triangulation_backend -adaptive.utils -adaptive.notebook_integration -``` diff --git a/docs/source/reference/adaptive.notebook_integration.md b/docs/source/reference/adaptive.notebook_integration.md deleted file mode 100644 index 0a8114b67..000000000 --- a/docs/source/reference/adaptive.notebook_integration.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.notebook_integration module - -```{eval-rst} -.. automodule:: adaptive.notebook_integration - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.runner.AsyncRunner.md b/docs/source/reference/adaptive.runner.AsyncRunner.md deleted file mode 100644 index 1cc1a6f77..000000000 --- a/docs/source/reference/adaptive.runner.AsyncRunner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.AsyncRunner - -```{eval-rst} -.. autoclass:: adaptive.runner.AsyncRunner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.runner.BaseRunner.md b/docs/source/reference/adaptive.runner.BaseRunner.md deleted file mode 100644 index d92054af3..000000000 --- a/docs/source/reference/adaptive.runner.BaseRunner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.runner.BaseRunner - -```{eval-rst} -.. autoclass:: adaptive.runner.BaseRunner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.runner.BlockingRunner.md b/docs/source/reference/adaptive.runner.BlockingRunner.md deleted file mode 100644 index 0777f7b79..000000000 --- a/docs/source/reference/adaptive.runner.BlockingRunner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.BlockingRunner - -```{eval-rst} -.. autoclass:: adaptive.BlockingRunner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.runner.Runner.md b/docs/source/reference/adaptive.runner.Runner.md deleted file mode 100644 index 132705429..000000000 --- a/docs/source/reference/adaptive.runner.Runner.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.Runner - -```{eval-rst} -.. autoclass:: adaptive.Runner - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/reference/adaptive.utils.md b/docs/source/reference/adaptive.utils.md deleted file mode 100644 index 937298a2f..000000000 --- a/docs/source/reference/adaptive.utils.md +++ /dev/null @@ -1,8 +0,0 @@ -# adaptive.utils module - -```{eval-rst} -.. automodule:: adaptive.utils - :members: - :undoc-members: - :show-inheritance: -``` diff --git a/docs/source/tutorial/tutorial.md b/docs/source/tutorial/tutorial.md deleted file mode 100644 index a3f79970d..000000000 --- a/docs/source/tutorial/tutorial.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -kernelspec: - name: python3 - display_name: python3 -jupytext: - text_representation: - extension: .md - format_name: myst - format_version: '0.13' - jupytext_version: 1.13.8 ---- -# πŸŽ“ Tutorial Adaptive - -[Adaptive](https://github.com/python-adaptive/adaptive) -is a package for adaptively sampling functions with support for parallel -evaluation. - -This is an introductory notebook that shows some basic use cases. - -We recommend to start with the {ref}`adaptive.Learner1D tutorial`. - -```{note} -Because this documentation consists of static html, the `live_plot` and `live_info` widget is not live. -Download the notebooks in order to see the real behaviour. -``` - -```{toctree} -:hidden: true - -tutorial.Learner1D -tutorial.Learner2D -tutorial.custom_loss -tutorial.AverageLearner -tutorial.BalancingLearner -tutorial.DataSaver -tutorial.IntegratorLearner -tutorial.LearnerND -tutorial.AverageLearner1D -tutorial.SequenceLearner -tutorial.parallelism -tutorial.advanced-topics -``` diff --git a/docs/test_build_notebooks.py b/docs/test_build_notebooks.py new file mode 100644 index 000000000..635f69128 --- /dev/null +++ b/docs/test_build_notebooks.py @@ -0,0 +1,185 @@ +"""Tests for docs/build_notebooks.py. + +Run with: uv run --group docs pytest docs/test_build_notebooks.py -o addopts="" +""" + +import json +import sys +from pathlib import Path + +import markdown # type: ignore[import-untyped] +from nbformat.v4 import new_code_cell, new_markdown_cell, new_notebook, new_output + +sys.path.insert(0, str(Path(__file__).parent)) + +import build_notebooks as bn # noqa: E402 + +WIDGET_VIEW = {"model_id": "abc", "version_major": 2, "version_minor": 0} + + +def render(*cells, metadata=None, ipynb_name="page.ipynb"): + nb = new_notebook(cells=list(cells), metadata=metadata or {}) + return bn.render_notebook(nb, ipynb_name) + + +def test_hide_cell_wraps_in_details(): + cell = new_code_cell("import adaptive", metadata={"tags": ["hide-cell"]}) + md = render(cell) + assert '
' in md + assert "```python\nimport adaptive\n```" in md + assert md.index("") + + +def test_remove_input_keeps_output(): + out = new_output("stream", name="stdout", text="hi there\n") + cell = new_code_cell("print('hi there')", outputs=[out]) + cell.metadata["tags"] = ["remove-input"] + md = render(cell) + assert "print(" not in md + assert "hi there" in md + + +def test_html_output_wrapped_raw(): + out = new_output("display_data", data={"text/html": "x", "text/plain": "X"}) + md = render(new_code_cell("x", outputs=[out])) + assert '
\nx\n
' in md + assert "X" not in md.replace("x", "") + + +def test_widget_without_state_falls_back(): + data = { + "application/vnd.jupyter.widget-view+json": WIDGET_VIEW, + "text/plain": "VBox()", + } + md = render(new_code_cell("w", outputs=[new_output("display_data", data=data)])) + assert "widget-view+json" not in md + assert "VBox()" in md + + +def test_widget_with_state_embeds_view_and_state(): + data = { + "application/vnd.jupyter.widget-view+json": WIDGET_VIEW, + "text/plain": "VBox()", + } + state = {"state": {"abc": {}}, "version_major": 2, "version_minor": 0} + metadata = {"widgets": {"application/vnd.jupyter.widget-state+json": state}} + cell = new_code_cell("w", outputs=[new_output("display_data", data=data)]) + md = render(cell, metadata=metadata) + assert '" + data = { + "application/vnd.jupyter.widget-view+json": WIDGET_VIEW, + "text/plain": "HTML()", + } + state = {"state": {"abc": {"state": {"value": value}}}, "version_major": 2} + metadata = {"widgets": {"application/vnd.jupyter.widget-state+json": state}} + md = render( + new_code_cell("w", outputs=[new_output("display_data", data=data)]), + metadata=metadata, + ) + start = md.index('", start)] + assert json.loads(content) == state + + +def test_inline_plotly_bundle_replaced_by_cdn(): + bundle = ( + "\n" + ) + out = new_output("display_data", data={"text/html": bundle, "text/plain": ""}) + md = render(new_code_cell("learner.plot_3D()", outputs=[out])) + assert '