Overview
test__mge_model_from__default_sigma_list_is_bitwise_unchanged and
test__mge_point_model_from__default_sigma_list_is_bitwise_unchanged
(test_autogalaxy/analysis/test_model_util.py, added with #549) assert exact
equality between two different numpy code paths. The implementation builds each
sigma with a per-element scalar power (gaussian.sigma = 10 ** log10_sigma_list[i],
autogalaxy/analysis/model_util.py:190 and :271), while the tests build their
expectation with a vectorised 10 ** np.linspace(...). numpy does not guarantee the
scalar and SIMD power loops agree bit for bit, so on AVX-512 hardware they differ by
1 ULP and the tests fail; on GitHub's runners they agree and the tests pass.
The effect is cosmetic, not functional — the PyAutoFit identifier quantises floats at
RESOLUTION = 1e-8 and this drift is ~1e-16 relative, so no identifier moves and no
archived fit is orphaned. But the regression guard added with #549 is not portable:
green in CI, red on an AVX-512 developer machine.
Confirmed reproducible on an AVX-512 host with numpy 2.4.6 — both tests fail, at
mask_radius=3.0, total_gaussians=20 index 18 (the index pytest reports) and at
pixel_scales=0.1, total_gaussians=10 index 4.
Plan
- Keep the exact-equality guarantee.
pytest.approx(rel=1e-8) is explicitly ruled out
by the test docstring and its reasoning still holds — it would only fail once the
ladder has already moved past the identifier's quantisation.
- Build the expected ladder element by element, the way the implementation does, so
both sides take the same numpy scalar-power path. The comparison stays bitwise; only
the code path used to produce the expectation changes.
- Keep the existing docstring reasoning intact and add a note recording the portability
trap, so a future tidy-up does not re-vectorise the expectation.
- Verify: both tests fail before the change and pass after, on the same AVX-512 host;
full test_autogalaxy/ suite stays green.
Detailed implementation plan
Work Classification
Library
Affected Repositories
- PyAutoGalaxy (primary) — test-only change, no library source touched
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoGalaxy |
claude/pyautogalaxy-mge-sigma-test-3neq07 (at origin/main = 13d3023) |
clean |
Branch: claude/pyautogalaxy-mge-sigma-test-3neq07 (session-mandated)
Worktree root: n/a — cloud session, worked in the canonical /home/user/PyAutoGalaxy checkout
Implementation Steps
-
test_autogalaxy/analysis/test_model_util.py,
test__mge_model_from__default_sigma_list_is_bitwise_unchanged (~L169): replace
assert sigma_list == list(
10 ** np.linspace(-4, np.log10(mask_radius), total_gaussians)
)
with a named log10_sigma_list = np.linspace(-4, np.log10(mask_radius), total_gaussians)
and an element-wise expectation [10 ** log10_sigma_list[i] for i in range(total_gaussians)].
-
Same transformation in
test__mge_point_model_from__default_sigma_list_is_bitwise_unchanged (~L220) for the
np.linspace(-2.0, np.log10(max_sigma), total_gaussians) ladder.
-
Extend both docstrings: keep the existing "why not pytest.approx(rel=1e-8)" reasoning
verbatim, and add the portability note (scalar vs SIMD power loops are not bit-identical;
the expectation must be built element-wise).
Key Files
test_autogalaxy/analysis/test_model_util.py — the two exact-equality guards (only file changed)
autogalaxy/analysis/model_util.py:190, :271 — the implementation's per-element
10 ** log10_sigma_list[i], unchanged; it defines the code path the test must mirror
Notes / non-goals
- The two neighbouring
pytest.approx(..., 1.0e-8) assertions (L129, L237) are tolerance-based
by design and are left alone.
np.log10(1e-4) == -4.0 and np.log10(0.01) == -2.0 exactly, so the test's literal
endpoints remain a faithful stand-in for the implementation's np.log10(sigma_min).
- No library source changes, so no downstream workspace impact.
Original Prompt
Click to expand starting prompt
# Bug in PyAutoGalaxy: the MGE bitwise sigma-ladder tests fail on
Type: test
Target: PyAutoGalaxy
Repos:
- PyAutoGalaxy
Difficulty: medium
Autonomy: safe
Priority: high
Status: formalised
Bug in PyAutoGalaxy: the MGE bitwise sigma-ladder tests fail on AVX-512 hardware. In PyAutoGalaxy, test__mge_model_from__default_sigma_list_is_bitwise_unchanged and test__mge_point_model_from__default_sigma_list_is_bitwise_unchanged in test_autogalaxy/analysis/test_model_util.py assert exact equality between two different numpy code paths. The PyAutoGalaxy implementation builds each sigma with a per-element scalar power, gaussian.sigma = 10 ** log10_sigma_list[i] at autogalaxy/analysis/model_util.py line 190, while the test builds its expectation with a vectorised 10 ** np.linspace(...). numpy does not guarantee the scalar and SIMD power loops agree bit for bit; on an x86-64-v4 / AVX-512 CPU they differ by 1 ULP at index 18, exactly the index pytest reports. Reproduced in isolation with numpy 2.4.6. The effect is cosmetic, not functional: the run identifier quantizes at RESOLUTION 1e-8 and this drift is ~1e-16 relative, so no identifier moves and no archived fit is orphaned. But the tests are green on GitHub runners and red on AVX-512 developer machines, so the regression guard added with PyAutoGalaxy#549 is not portable. Fix in PyAutoGalaxy by building the expected ladder element by element the way the implementation does, or by comparing with a tolerance far below the 1e-8 identifier resolution.
Session context added by the human at /start_dev:
Do NOT weaken this into pytest.approx(rel=1e-8) — the test docstring rules that out
explicitly and gives the reason. The guarantee worth keeping is that the ladder stays
stable well inside the identifier's RESOLUTION = 1e-8 quantisation. Preferred fix is to
build the expected ladder element-wise so both sides take the same numpy path; keep the
docstring's reasoning intact and note the portability trap in it.
Overview
test__mge_model_from__default_sigma_list_is_bitwise_unchangedandtest__mge_point_model_from__default_sigma_list_is_bitwise_unchanged(
test_autogalaxy/analysis/test_model_util.py, added with #549) assert exactequality between two different numpy code paths. The implementation builds each
sigma with a per-element scalar power (
gaussian.sigma = 10 ** log10_sigma_list[i],autogalaxy/analysis/model_util.py:190and:271), while the tests build theirexpectation with a vectorised
10 ** np.linspace(...). numpy does not guarantee thescalar and SIMD power loops agree bit for bit, so on AVX-512 hardware they differ by
1 ULP and the tests fail; on GitHub's runners they agree and the tests pass.
The effect is cosmetic, not functional — the PyAutoFit identifier quantises floats at
RESOLUTION = 1e-8and this drift is ~1e-16 relative, so no identifier moves and noarchived fit is orphaned. But the regression guard added with #549 is not portable:
green in CI, red on an AVX-512 developer machine.
Confirmed reproducible on an AVX-512 host with numpy 2.4.6 — both tests fail, at
mask_radius=3.0, total_gaussians=20index 18 (the index pytest reports) and atpixel_scales=0.1, total_gaussians=10index 4.Plan
pytest.approx(rel=1e-8)is explicitly ruled outby the test docstring and its reasoning still holds — it would only fail once the
ladder has already moved past the identifier's quantisation.
both sides take the same numpy scalar-power path. The comparison stays bitwise; only
the code path used to produce the expectation changes.
trap, so a future tidy-up does not re-vectorise the expectation.
full
test_autogalaxy/suite stays green.Detailed implementation plan
Work Classification
Library
Affected Repositories
Branch Survey
claude/pyautogalaxy-mge-sigma-test-3neq07(atorigin/main= 13d3023)Branch:
claude/pyautogalaxy-mge-sigma-test-3neq07(session-mandated)Worktree root: n/a — cloud session, worked in the canonical
/home/user/PyAutoGalaxycheckoutImplementation Steps
test_autogalaxy/analysis/test_model_util.py,test__mge_model_from__default_sigma_list_is_bitwise_unchanged(~L169): replacewith a named
log10_sigma_list = np.linspace(-4, np.log10(mask_radius), total_gaussians)and an element-wise expectation
[10 ** log10_sigma_list[i] for i in range(total_gaussians)].Same transformation in
test__mge_point_model_from__default_sigma_list_is_bitwise_unchanged(~L220) for thenp.linspace(-2.0, np.log10(max_sigma), total_gaussians)ladder.Extend both docstrings: keep the existing "why not
pytest.approx(rel=1e-8)" reasoningverbatim, and add the portability note (scalar vs SIMD power loops are not bit-identical;
the expectation must be built element-wise).
Key Files
test_autogalaxy/analysis/test_model_util.py— the two exact-equality guards (only file changed)autogalaxy/analysis/model_util.py:190,:271— the implementation's per-element10 ** log10_sigma_list[i], unchanged; it defines the code path the test must mirrorNotes / non-goals
pytest.approx(..., 1.0e-8)assertions (L129, L237) are tolerance-basedby design and are left alone.
np.log10(1e-4) == -4.0andnp.log10(0.01) == -2.0exactly, so the test's literalendpoints remain a faithful stand-in for the implementation's
np.log10(sigma_min).Original Prompt
Click to expand starting prompt
Session context added by the human at
/start_dev: