Skip to content

refactor: one shared colour-norm helper (fixes inversion ignoring log10_min_value) - #489

Merged
Jammy2211 merged 1 commit into
mainfrom
claude/autoarray-shared-log-norm-c12ges
Aug 24, 2026
Merged

refactor: one shared colour-norm helper (fixes inversion ignoring log10_min_value)#489
Jammy2211 merged 1 commit into
mainfrom
claude/autoarray-shared-log-norm-c12ges

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Closes #488.

Building the matplotlib colour norm from use_log10 / vmin / vmax was written out three times — plot/array.py, plot/inversion.py, and (in PyAutoGalaxy) util/plot_utils.py::norm_from. The copies had diverged, and one divergence was a live behaviour bug: plot_inversion_reconstruction hardcoded the 1e-4 log floor and never consulted autonerves config, so a user who changed visualize.general.general.log10_min_value had it honoured on array plots and silently ignored on inversion plots.

norm_from now lives once in autoarray/plot/utils.py and both plot functions call it. The config read lands as _conf_log10_min_value, alongside the other _conf_* readers. Deduplicating is how the divergence stops recurring; fixing the floor is the point.

The companion PyAutoGalaxy PR makes its norm_from a delegate. Library-first: this merges first.

API Changes

New public helper autoarray.plot.utils.norm_from(array=None, use_log10=False, vmin=None, vmax=None) — the single implementation behind every PyAuto colour scale. Nothing is removed and no existing signature changes; plot_array and plot_inversion_reconstruction are unchanged from a caller's point of view.

Two deliberate behaviour changes, both on plot_inversion_reconstruction only (plot_array and the autogalaxy GUI path are untouched):

  1. The configured log10_min_value is now honoured, and values are clipped to it before vmax is derived. Inversion plots differ when the configured floor is not 1e-4. This is the fix the task exists for.
  2. A degenerate vmax <= vmin is now widened however it arose, not only in the derived branch — LogNorm(vmin=10, vmax=1) is unusable whoever supplied the numbers. Inversion plots differ when a caller passes vmax <= vmin explicitly.

A third difference was deliberately not merged into one behaviour: array.py derived vmax from the image, inversion.py from pixel_values. The helper's array argument means "the values being coloured", so the two call sites legitimately pass different data. array=None takes the same fallback as an all-NaN array — exactly what inversion's separate else branch did — so nothing changed there. All three decisions are recorded in the helper's docstring, per the task's requirement that they be decided rather than assumed.

Test Plan

  • test_autoarray/plot — 17 passed (11 new)
  • test_autoarray full suite — 1157 passed, 55 skipped
  • test_autogalaxy full suite against this branch — 1119 passed, 1 skipped
  • test_autogalaxy/gui/test_plot_norm.py (pins PyAutoGalaxy#586's behaviour) — 9 passed, file untouched
  • New tests verified to have teeth: reverting plot_inversion_reconstruction to the hardcoded floor makes test__plot_inversion_reconstruction fail with assert 0.0001 == 0.003

The new tests cover the configured floor at both call sites end-to-end (a spy wrapping the shared helper records the norm each plot function actually applied), plus explicit limits, derived vmax, clipping to the floor, the degenerate range, all-NaN values, and array=None.

Two caveats, stated rather than glossed:

  • 8 inversion/inversion/test_{abstract,factory}.py sparse-operator tests fail in the authoring environment. They fail identically on unmodified main (verified by stashing) and are unrelated to plotting.
  • Ran on Python 3.12 only — the authoring session had no 3.13 environment. CI covers 3.13.
Full API Changes (for automation & release notes)

Added

  • autoarray.plot.utils.norm_from(array=None, use_log10=False, vmin=None, vmax=None) — returns a LogNorm, a Normalize, or None. The single colour-norm implementation; its docstring records the three divergence decisions.
  • autoarray.plot.utils._conf_log10_min_value() — reads visualize.general.general.log10_min_value, falling back to 1.0e-4.

Removed

  • Nothing. The inline colour-norm blocks in autoarray/plot/array.py and autoarray/plot/inversion.py are replaced by calls to the helper; from matplotlib.colors import LogNorm, Normalize drops out of inversion.py's module imports (both remain lazily imported inside the helper).

Migration

  • None required for callers of plot_array / plot_inversion_reconstruction.
  • Downstream code building its own colour norm should call autoarray.plot.utils.norm_from rather than reimplementing the block — that reimplementation is what this PR exists to stop.

Generated by the PyAutoLabs agent workflow.


Generated by Claude Code

Building the matplotlib colour norm from use_log10/vmin/vmax was written out
three times — plot/array.py, plot/inversion.py, and (in PyAutoGalaxy)
util/plot_utils.py::norm_from. The copies had diverged, and one divergence was
a live behaviour bug.

Add `norm_from` to autoarray/plot/utils.py as the single implementation, and
call it from both plot call sites. Its docstring records which behaviour each
divergence resolved to:

1. The log floor is always the configured `log10_min_value` (fallback 1.0e-4),
   and the values are clipped to it before vmax is derived. plot_inversion_
   reconstruction hardcoded 1e-4 and never read config, so a user who changed
   the floor had it honoured on array plots and silently ignored on inversion
   plots. Fixing that is the point of the change; inversion plots differ when
   the configured floor is not 1e-4.
2. `array` means "the values being coloured" — the image for plot_array, the
   reconstruction's pixel_values for plot_inversion_reconstruction. The two
   call sites colour different data, so this was never a divergence to
   reconcile; `array=None` takes the same fallback the all-NaN case does,
   which is what inversion's separate `else` branch did. No behaviour change.
3. A degenerate `vmax <= vmin` is widened however it arose, not only in the
   derived branch. LogNorm(vmin=10, vmax=1) is unusable whoever supplied the
   numbers; inversion previously let an explicitly-passed degenerate pair
   through. Inversion plots differ when a caller passes vmax <= vmin.

The config read lands as `_conf_log10_min_value`, alongside the other `_conf_*`
readers.

Tests cover the configured floor at BOTH call sites end-to-end (a spy wrapping
the shared helper records the norm each plot function actually applied — the
inversion one fails against the old hardcoded floor), plus explicit limits,
derived vmax, clipping to the floor, the degenerate range, all-NaN values and
`array=None`.

Full suite: 1157 passed. The 8 sparse-operator inversion failures in this
environment are present on unmodified main and are unrelated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DamasCoRrENvgiRkU5WHHW
@Jammy2211
Jammy2211 merged commit b690c3e into main Aug 24, 2026
3 checks passed
@Jammy2211
Jammy2211 deleted the claude/autoarray-shared-log-norm-c12ges branch August 25, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: share the colour-norm helper; fix inversion log10 floor

1 participant