From 7b249047c6c309e2b3a089df0463542890f4668d Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Thu, 21 May 2026 21:30:06 +0100 Subject: [PATCH] docs(cookbooks/analysis): __Live Quick-Update Visualization__ section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new section to the analysis cookbook covering quick-update visualization, which previously had zero coverage despite being a core search-time feature. Covers: - iterations_per_quick_update — what it does, how perform_quick_update is called every N likelihood evals with the current best-fit instance. - background_quick_update=True — daemon-thread rendering with latest-only drop policy so the sampler is never blocked while matplotlib saves PNGs. - New in PyAutoFit #1290: when running in a Jupyter / Colab kernel, the cell that ran search.fit(...) auto-updates in place via IPython.display.update_display with a stable display_id. No code change needed by the user. - Script-mode fallback unchanged — PNGs still land on disk under the search's output folder. - PYAUTO_DISABLE_IPYTHON_DISPLAY=1 opt-out for papermill / nbconvert pipelines. - Small commented API-shape example showing the relevant Nautilus kwargs. Companion to PyAutoFit #1290 (merged). Closes the workspace half of rhayes777/PyAutoFit#1289. notebooks/cookbooks/analysis.ipynb regenerated to match. Co-Authored-By: Claude Opus 4.7 (1M context) --- notebooks/cookbooks/analysis.ipynb | 61 ++++++++++++++++++++++++++++++ scripts/cookbooks/analysis.py | 52 +++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/notebooks/cookbooks/analysis.ipynb b/notebooks/cookbooks/analysis.ipynb index 811f3c63..d5e75839 100644 --- a/notebooks/cookbooks/analysis.ipynb +++ b/notebooks/cookbooks/analysis.ipynb @@ -437,6 +437,67 @@ "outputs": [], "execution_count": null }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Live Quick-Update Visualization__\n", + "\n", + "Non-linear searches in **PyAutoFit** can produce on-the-fly visualization while the fit is still running, so you can\n", + "monitor whether the model is behaving sensibly long before the search finishes.\n", + "\n", + "This is controlled by two parameters on the search:\n", + "\n", + "- `iterations_per_quick_update=N` \u2014 every `N` likelihood evaluations the search calls\n", + " `analysis.perform_quick_update(paths, instance)` with the current maximum-likelihood `instance`. The default\n", + " `perform_quick_update` implementation (on subclasses like `AnalysisImaging` in PyAutoGalaxy / PyAutoLens) renders\n", + " the relevant subplot (`subplot_fit.png` for imaging, `subplot_tracer.png` for tracers, etc.) into the output\n", + " folder so users can refresh their file browser and see how the fit is progressing.\n", + "- `background_quick_update=True` \u2014 runs `perform_quick_update` on a background daemon thread so the sampler is\n", + " never blocked while matplotlib renders and saves PNGs. A latest-only drop policy applies: if a new best-fit\n", + " arrives before the previous render finishes, the older request is silently replaced (we only ever care about the\n", + " most recent state).\n", + "\n", + "**Live in-cell rendering in Jupyter / Colab.** When the search runs inside a Jupyter or Colab notebook kernel, the\n", + "background worker additionally pushes each freshly-written `subplot_fit.png` into the active cell via\n", + "`IPython.display.update_display` with a stable `display_id`. The result: the cell that ran `search.fit(...)`\n", + "shows a **single self-updating image** during the fit, refreshing every `iterations_per_quick_update` evaluations,\n", + "rather than appending stacked frames or requiring you to open PNGs externally. No code change needed by the user\n", + "\u2014 the worker auto-detects the kernel.\n", + "\n", + "**Script mode is unchanged.** When running outside a kernel (e.g. `python my_fit.py` from a terminal), no\n", + "IPython side effects fire. The PNGs still land on disk under the search's output folder as before. `IPython` is\n", + "only imported when actually running inside a kernel.\n", + "\n", + "**Opt-out.** Set `PYAUTO_DISABLE_IPYTHON_DISPLAY=1` to skip the in-cell display step even inside a kernel \u2014\n", + "useful for `papermill` / automated `nbconvert` pipelines that want PNGs on disk but no display side effects.\n", + "\n", + "The API shape looks like this (commented out \u2014 see the workspace `start_here.py` for a runnable end-to-end\n", + "example):\n", + "\n", + "```python\n", + "# search = af.Nautilus(\n", + "# path_prefix=\"cookbooks/quick_update\",\n", + "# name=\"example\",\n", + "# iterations_per_quick_update=50, # call perform_quick_update every 50 likelihood evals\n", + "# background_quick_update=True, # run rendering on a daemon thread\n", + "# number_of_cores=1,\n", + "# )\n", + "# result = search.fit(model=model, analysis=analysis)\n", + "```\n", + "\n", + "When this runs inside a Jupyter cell, the cell output is a single image that refreshes ~once per 50 evaluations\n", + "until the search converges. When run as `python my_fit.py`, the PNGs land at\n", + "`output/...//image/subplot_fit.png` and update on disk on the same cadence." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [], + "outputs": [], + "execution_count": null + }, { "cell_type": "markdown", "metadata": {}, diff --git a/scripts/cookbooks/analysis.py b/scripts/cookbooks/analysis.py index bf9a53c8..85ca99ce 100644 --- a/scripts/cookbooks/analysis.py +++ b/scripts/cookbooks/analysis.py @@ -351,6 +351,58 @@ def log_likelihood_function(self, instance): return log_likelihood +""" +__Live Quick-Update Visualization__ + +Non-linear searches in **PyAutoFit** can produce on-the-fly visualization while the fit is still running, so you can +monitor whether the model is behaving sensibly long before the search finishes. + +This is controlled by two parameters on the search: + +- `iterations_per_quick_update=N` — every `N` likelihood evaluations the search calls + `analysis.perform_quick_update(paths, instance)` with the current maximum-likelihood `instance`. The default + `perform_quick_update` implementation (on subclasses like `AnalysisImaging` in PyAutoGalaxy / PyAutoLens) renders + the relevant subplot (`subplot_fit.png` for imaging, `subplot_tracer.png` for tracers, etc.) into the output + folder so users can refresh their file browser and see how the fit is progressing. +- `background_quick_update=True` — runs `perform_quick_update` on a background daemon thread so the sampler is + never blocked while matplotlib renders and saves PNGs. A latest-only drop policy applies: if a new best-fit + arrives before the previous render finishes, the older request is silently replaced (we only ever care about the + most recent state). + +**Live in-cell rendering in Jupyter / Colab.** When the search runs inside a Jupyter or Colab notebook kernel, the +background worker additionally pushes each freshly-written `subplot_fit.png` into the active cell via +`IPython.display.update_display` with a stable `display_id`. The result: the cell that ran `search.fit(...)` +shows a **single self-updating image** during the fit, refreshing every `iterations_per_quick_update` evaluations, +rather than appending stacked frames or requiring you to open PNGs externally. No code change needed by the user +— the worker auto-detects the kernel. + +**Script mode is unchanged.** When running outside a kernel (e.g. `python my_fit.py` from a terminal), no +IPython side effects fire. The PNGs still land on disk under the search's output folder as before. `IPython` is +only imported when actually running inside a kernel. + +**Opt-out.** Set `PYAUTO_DISABLE_IPYTHON_DISPLAY=1` to skip the in-cell display step even inside a kernel — +useful for `papermill` / automated `nbconvert` pipelines that want PNGs on disk but no display side effects. + +The API shape looks like this (commented out — see the workspace `start_here.py` for a runnable end-to-end +example): + +```python +# search = af.Nautilus( +# path_prefix="cookbooks/quick_update", +# name="example", +# iterations_per_quick_update=50, # call perform_quick_update every 50 likelihood evals +# background_quick_update=True, # run rendering on a daemon thread +# number_of_cores=1, +# ) +# result = search.fit(model=model, analysis=analysis) +``` + +When this runs inside a Jupyter cell, the cell output is a single image that refreshes ~once per 50 evaluations +until the search converges. When run as `python my_fit.py`, the PNGs land at +`output/...//image/subplot_fit.png` and update on disk on the same cadence. +""" + + """ __Custom Result__