refactor: one shared colour-norm helper (fixes inversion ignoring log10_min_value) - #489
Merged
Merged
Conversation
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
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #488.
Building the matplotlib colour norm from
use_log10/vmin/vmaxwas 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_reconstructionhardcoded the1e-4log floor and never consultedautonervesconfig, so a user who changedvisualize.general.general.log10_min_valuehad it honoured on array plots and silently ignored on inversion plots.norm_fromnow lives once inautoarray/plot/utils.pyand 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_froma 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_arrayandplot_inversion_reconstructionare unchanged from a caller's point of view.Two deliberate behaviour changes, both on
plot_inversion_reconstructiononly (plot_arrayand the autogalaxy GUI path are untouched):log10_min_valueis now honoured, and values are clipped to it beforevmaxis derived. Inversion plots differ when the configured floor is not1e-4. This is the fix the task exists for.vmax <= vminis 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 passesvmax <= vminexplicitly.A third difference was deliberately not merged into one behaviour:
array.pyderivedvmaxfrom the image,inversion.pyfrompixel_values. The helper'sarrayargument means "the values being coloured", so the two call sites legitimately pass different data.array=Nonetakes the same fallback as an all-NaNarray — exactly what inversion's separateelsebranch 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_autoarrayfull suite — 1157 passed, 55 skippedtest_autogalaxyfull suite against this branch — 1119 passed, 1 skippedtest_autogalaxy/gui/test_plot_norm.py(pins PyAutoGalaxy#586's behaviour) — 9 passed, file untouchedplot_inversion_reconstructionto the hardcoded floor makestest__plot_inversion_reconstructionfail withassert 0.0001 == 0.003The 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-NaNvalues, andarray=None.Two caveats, stated rather than glossed:
inversion/inversion/test_{abstract,factory}.pysparse-operator tests fail in the authoring environment. They fail identically on unmodifiedmain(verified by stashing) and are unrelated to plotting.Full API Changes (for automation & release notes)
Added
autoarray.plot.utils.norm_from(array=None, use_log10=False, vmin=None, vmax=None)— returns aLogNorm, aNormalize, orNone. The single colour-norm implementation; its docstring records the three divergence decisions.autoarray.plot.utils._conf_log10_min_value()— readsvisualize.general.general.log10_min_value, falling back to1.0e-4.Removed
autoarray/plot/array.pyandautoarray/plot/inversion.pyare replaced by calls to the helper;from matplotlib.colors import LogNorm, Normalizedrops out ofinversion.py's module imports (both remain lazily imported inside the helper).Migration
plot_array/plot_inversion_reconstruction.autoarray.plot.utils.norm_fromrather than reimplementing the block — that reimplementation is what this PR exists to stop.Generated by the PyAutoLabs agent workflow.
Generated by Claude Code