From b27e7d659da29ff244c5273e8c2f88e3d1990981 Mon Sep 17 00:00:00 2001 From: Jesse Grabowski Date: Wed, 8 Jan 2025 22:40:13 +0800 Subject: [PATCH] Add example gallery --- doc/conf.py | 33 +++- .../introduction/what_is_pytensor.ipynb | 134 +++++++++++++ doc/index.rst | 1 + environment.yml | 4 + scripts/generate_gallery.py | 184 ++++++++++++++++++ 5 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 doc/gallery/introduction/what_is_pytensor.ipynb create mode 100644 scripts/generate_gallery.py diff --git a/doc/conf.py b/doc/conf.py index 5b2d0c71a4..100b7d6e6f 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -21,6 +21,8 @@ import sys import pytensor +sys.path.insert(0, os.path.abspath(os.path.join("..", "scripts"))) + # General configuration # --------------------- @@ -34,7 +36,9 @@ "sphinx.ext.linkcode", "sphinx.ext.mathjax", "sphinx_design", - "sphinx.ext.intersphinx" + "sphinx.ext.intersphinx", + "myst_nb", + "generate_gallery", ] intersphinx_mapping = { @@ -295,3 +299,30 @@ def find_source(): # If false, no module index is generated. # latex_use_modindex = True + + +# -- MyST config ------------------------------------------------- +myst_enable_extensions = [ + "colon_fence", + "deflist", + "dollarmath", + "amsmath", + "substitution", +] +myst_dmath_double_inline = True + +myst_substitutions = { + "pip_dependencies": "{{ extra_dependencies }}", + "conda_dependencies": "{{ extra_dependencies }}", + "extra_install_notes": "", +} + +nb_execution_mode = "off" +nbsphinx_execute = "never" +nbsphinx_allow_errors = True + + +# -- Bibtex config ------------------------------------------------- +bibtex_bibfiles = ["references.bib"] +bibtex_default_style = "unsrt" +bibtex_reference_style = "author_year" diff --git a/doc/gallery/introduction/what_is_pytensor.ipynb b/doc/gallery/introduction/what_is_pytensor.ipynb new file mode 100644 index 0000000000..e898070b5b --- /dev/null +++ b/doc/gallery/introduction/what_is_pytensor.ipynb @@ -0,0 +1,134 @@ +{ + "cells": [ + { + "metadata": {}, + "cell_type": "markdown", + "source": "# Overview of Pytensor", + "id": "a99a08673e9df0f1" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-08T14:14:08.142952Z", + "start_time": "2025-01-08T14:14:07.893626Z" + } + }, + "cell_type": "code", + "source": [ + "import pytensor\n", + "import pytensor.tensor as pt\n", + "import matplotlib.pyplot as plt" + ], + "id": "b65f256863d33326", + "outputs": [], + "execution_count": 7 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-08T14:13:49.219564Z", + "start_time": "2025-01-08T14:13:49.211832Z" + } + }, + "cell_type": "code", + "source": [ + "x = pt.tensor('x', shape=(None,))\n", + "sin_x = pt.sin(x)\n", + "\n", + "sin_x.dprint()" + ], + "id": "baac8d431d62249a", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sin [id A]\n", + " └─ x [id B]\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 4 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-08T14:13:50.913530Z", + "start_time": "2025-01-08T14:13:50.901519Z" + } + }, + "cell_type": "code", + "source": "f = pytensor.function([x], sin_x)", + "id": "9ae9fb5bfc619790", + "outputs": [], + "execution_count": 5 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-08T14:14:31.172872Z", + "start_time": "2025-01-08T14:14:31.040816Z" + } + }, + "cell_type": "code", + "source": [ + "fig, ax = plt.subplots()\n", + "ax.plot(range(-10, 10), f(range(-10, 10)))\n", + "plt.show()" + ], + "id": "df70d774ccaaed7a", + "outputs": [ + { + "data": { + "text/plain": [ + "
" + ], + "image/png": "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Muh9r732Wrz33nvCv00mkyhrDLQXXngBixcvFv4dHh4+6PUvv/wyXn31VaxatQoTJ07Er3/9a1xzzTU4efIkIiIixF7uiJ04cQIejwdvv/02xo8fjyNHjmDx4sXo6OjA7373u0HvK/ff7fr167F06VK88cYbuPTSS/H222/juuuuw7FjxzB27Ng+15eWluL666/H4sWLsXr1avz73//GkiVLEB8fj9tuu02CZzA027dvx89//nPMnDkTLpcLTz/9NAoLC3Hs2DGEhYUNet+TJ0/6db+Nj48Xe7kBMWXKFGzdulX492CvlUr9vQLAnj174Ha7hX8fOXIE11xzDe64445B76fU32vAMDIi7733HrNarX1u37RpE9PpdKyqqkq4be3atcxsNjObzdbvY7W0tDCj0cjWrVsn3FZVVcV0Oh3bvHlzwNceKA6HgyUkJLAXXnhh0OvuvfdedvPNNwdnUQGUnp7OXnvttSFf7/F4WFJSEvvtb38r3Nbd3c2sVit76623RFihuF5++WWWmZk56DVK+N3OmjWLPfjgg363ZWdns6eeeqrf63/xi1+w7Oxsv9t++tOfsjlz5oi2RjHU1dUxAGz79u0DXvPVV18xAKy5uTl4CwuQZ599lk2fPn3I16vl98oYY48++igbN24c83g8/X5fyb/XQKIjqgDbuXMncnNzkZKSItw2b9482O127Nu3r9/77Nu3D06nE4WFhcJtKSkpyM3NxY4dO0Rf80h9+umnaGhowH333XfBa7dt24aEhARMnDgRixcvRl1dnfgLDICXXnoJsbGxuOiii/Diiy8OemRTWlqKmpoav9+j2WzGD3/4Q1n/Hgdis9kQExNzwevk/Lt1OBzYt2+f3+8EAAoLCwf8nezcubPP9fPmzcPevXvhdDpFW2ug2Ww2ABjS73DGjBlITk7GVVddha+++krspQVMcXExUlJSkJmZiTvvvBMlJSUDXquW36vD4cDq1avxn//5nxccFq3U32ugUIATYDU1NUhMTPS7LTo6GiaTCTU1NQPex2QyITo62u/2xMTEAe8jBytXrsS8efOQlpY26HXXXXcd1qxZg3/961945ZVXsGfPHlx55ZWw2+1BWunIPProo1i3bh2++uorPPTQQ1ixYgWWLFky4PX87+r837/cf4/9OXPmDF5//XU8+OCDg14n999tQ0MD3G73sH4n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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 8 + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": "", + "id": "6e7ad00262c9c57c" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/index.rst b/doc/index.rst index ac5bc0876c..a70a28df82 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -80,6 +80,7 @@ Community introduction user_guide API + Examples Contributing .. _Theano: https://github.com/Theano/Theano diff --git a/environment.yml b/environment.yml index 4b213fd851..1571ae0d11 100644 --- a/environment.yml +++ b/environment.yml @@ -43,6 +43,10 @@ dependencies: - ipython - pymc-sphinx-theme - sphinx-design + - myst-nb + - matplotlib + - watermark + # code style - ruff # developer tools diff --git a/scripts/generate_gallery.py b/scripts/generate_gallery.py new file mode 100644 index 0000000000..b2a2764119 --- /dev/null +++ b/scripts/generate_gallery.py @@ -0,0 +1,184 @@ +""" +Sphinx plugin to run generate a gallery for notebooks + +Modified from the pymc project, which modified the seaborn project, which modified the mpld3 project. +""" + +import base64 +import json +import os +import shutil +from pathlib import Path + +import matplotlib + + +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import sphinx +from matplotlib import image + + +logger = sphinx.util.logging.getLogger(__name__) + +DOC_SRC = Path(__file__).resolve().parent +# DEFAULT_IMG_LOC = os.path.join(os.path.dirname(DOC_SRC), "_static", "PyMC.png") + +DEFAULT_IMG_LOC = None +external_nbs = {} + +HEAD = """ +Example Gallery +=============== + +.. toctree:: + :hidden: + +""" + +SECTION_TEMPLATE = """ +.. _{section_id}: + +{section_title} +{underlines} + +.. grid:: 1 2 3 3 + :gutter: 4 + +""" + +ITEM_TEMPLATE = """ + .. grid-item-card:: :doc:`{doc_name}` + :img-top: {image} + :link: {doc_reference} + :link-type: {link_type} + :shadow: none +""" + +folder_title_map = { + "introduction": "Introduction", +} + + +def create_thumbnail(infile, width=275, height=275, cx=0.5, cy=0.5, border=4): + """Overwrites `infile` with a new file of the given size""" + im = image.imread(infile) + rows, cols = im.shape[:2] + size = min(rows, cols) + if size == cols: + xslice = slice(0, size) + ymin = min(max(0, int(cx * rows - size // 2)), rows - size) + yslice = slice(ymin, ymin + size) + else: + yslice = slice(0, size) + xmin = min(max(0, int(cx * cols - size // 2)), cols - size) + xslice = slice(xmin, xmin + size) + thumb = im[yslice, xslice] + thumb[:border, :, :3] = thumb[-border:, :, :3] = 0 + thumb[:, :border, :3] = thumb[:, -border:, :3] = 0 + + dpi = 100 + fig = plt.figure(figsize=(width / dpi, height / dpi), dpi=dpi) + + ax = fig.add_axes([0, 0, 1, 1], aspect="auto", frameon=False, xticks=[], yticks=[]) + ax.imshow(thumb, aspect="auto", resample=True, interpolation="bilinear") + fig.savefig(infile, dpi=dpi) + plt.close(fig) + return fig + + +class NotebookGenerator: + """Tools for generating an example page from a file""" + + def __init__(self, filename, root_dir, folder): + self.folder = folder + + self.basename = Path(filename).name + self.stripped_name = Path(filename).stem + self.image_dir = Path(root_dir) / "_thumbnails" / folder + self.png_path = self.image_dir / f"{self.stripped_name}.png" + + with filename.open(encoding="utf-8") as fid: + self.json_source = json.load(fid) + self.default_image_loc = DEFAULT_IMG_LOC + + def extract_preview_pic(self): + """By default, just uses the last image in the notebook.""" + pic = None + for cell in self.json_source["cells"]: + for output in cell.get("outputs", []): + if "image/png" in output.get("data", []): + pic = output["data"]["image/png"] + if pic is not None: + return base64.b64decode(pic) + return None + + def gen_previews(self): + preview = self.extract_preview_pic() + if preview is not None: + with self.png_path.open("wb") as buff: + buff.write(preview) + else: + logger.warning( + f"Didn't find any pictures in {self.basename}", + type="thumbnail_extractor", + ) + shutil.copy(self.default_image_loc, self.png_path) + create_thumbnail(self.png_path) + + +def main(app): + logger.info("Starting thumbnail extractor.") + + working_dir = Path.getcwd() + os.chdir(app.builder.srcdir) + + file = [HEAD] + + for folder, title in folder_title_map.items(): + file.append( + SECTION_TEMPLATE.format( + section_title=title, section_id=folder, underlines="-" * len(title) + ) + ) + + thumbnail_dir = Path("..") / "_thumbnails" / folder + if not thumbnail_dir.exists(): + Path.mkdir(thumbnail_dir, parents=True) + + if folder in external_nbs.keys(): + file += [ + ITEM_TEMPLATE.format( + doc_name=descr["doc_name"], + image=descr["image"], + doc_reference=descr["doc_reference"], + link_type=descr["link_type"], + ) + for descr in external_nbs[folder] + ] + + nb_paths = sorted(Path.glob(f"gallery/{folder}/*.ipynb")) + + for nb_path in nb_paths: + nbg = NotebookGenerator( + filename=nb_path, root_dir=Path(".."), folder=folder + ) + nbg.gen_previews() + + file.append( + ITEM_TEMPLATE.format( + doc_name=Path(folder) / nbg.stripped_name, + image="/" + str(nbg.png_path), + doc_reference=Path(folder) / nbg.stripped_name, + link_type="doc", + ) + ) + + with Path("gallery", "gallery.rst").open("w", encoding="utf-8") as f: + f.write("\n".join(file)) + + os.chdir(working_dir) + + +def setup(app): + app.connect("builder-inited", main)