Skip to content

fix: repair ConstantZeroth dead code (eye(P) shape bug + missing neighbors_sizes) - #449

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/constant-zeroth-repair
Aug 18, 2026
Merged

fix: repair ConstantZeroth dead code (eye(P) shape bug + missing neighbors_sizes)#449
Jammy2211 merged 1 commit into
mainfrom
feature/constant-zeroth-repair

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Closes #448. Phase 8C of the inference-methods programme (autolens_profiling#134, results/notes/inference/PROGRAMME.md on its PR #135).

What was broken

al.reg.ConstantZeroth was dead code presenting as a feature — two independent defects, both reproduced on clean main (7d1906e) before any fix (tracebacks pasted on #448):

  1. eye(P) shape bugconstant_zeroth_regularization_matrix_from built the zeroth-order term as xp.eye(P) where P = neighbors.shape[1] is the neighbor-column count (e.g. 4), not S the mesh pixel count, so const + zeroth raised ValueError: operands could not be broadcast together with shapes (9,9) (4,4) whenever S != P.
  2. Missing neighbors_sizes at the class APIConstantZeroth.regularization_matrix_from omitted the required argument, raising TypeError before the shape bug was even reached.

The fix

  • Zeroth term is now the full S x S scaled identity (xp.eye(S) * coefficient_zeroth**2), matching zeroth.py's semantics (eye(pixels)).
  • The class API threads neighbors_sizes=linear_obj.neighbors.sizes, mirroring Constant.regularization_matrix_from.
  • Sibling check: adapt_split_zeroth.py and brightness_zeroth.py do not carry the copy-paste pattern — their zeroth terms are xp.diag over per-pixel weights; no change made there.
  • Untouched by design: Constant/Adapt/AdaptSplit numerics, autoarray/__init__.py (version-stamp branch owns it). Nothing else bundled.

Null-mode-lift verification (spectrum before/after)

On the 9-pixel rectangular MockMapper fixture with lambda_neighbor = lambda_zeroth = 1.0, through the class API:

Scheme min eigenvalue max eigenvalue condition number
Constant 1.000e-08 (the jitter floor — graph-Laplacian null mode) 6.000e+00 6.0e+08
ConstantZeroth (fixed) 1.000e+00 = lambda_z^2 7.000e+00 7.0e+00

The zeroth-order term lifts the null mode exactly as intended: max |(CZ − C) − lambda_z^2·I| = 0.0 element-wise, and the smallest eigenvalue rises from the 1e-8 conditioning floor to lambda_z^2. This fix makes the scheme WORK — it does not recommend it over AdaptSplit (ConstantZeroth is an alternative regularization model; target-changing relative to AdaptSplit).

Tests

New test_autoarray/inversion/regularizations/test_constant_zeroth.py (numpy-only per repo policy):

  • matrix shape (S, S) on a fixture with S != P (the shape that previously raised),
  • no null mode: min(eigvalsh) >= lambda_z^2,
  • reduction to Constant's matrix + lambda_z^2 * I where the schemes overlap,
  • class-API path returns without raising (diagonal = Constant's 8.0000001 + lambda_z^2 = 9).

Full suite: pytest test_autoarray/988 passed, 59 skipped, 3 failed — the 3 failures are test_transformer.py::test__nufft_pynufft__* (scipy.linalg.pinv2 removed in modern scipy, inside pynufft), identical on clean main in this environment and unrelated to this change.

Merge stays with @Jammy2211.


Generated by Claude Code

…hbors_sizes)

Two independent defects made al.reg.ConstantZeroth unusable through its
class API (issue #448, found in the wsdev#104 reg-logdet investigation):

- constant_zeroth_regularization_matrix_from built the zeroth-order term
  as eye(P) where P is the neighbor-column count, not S the mesh pixel
  count, so `const + zeroth` raised a broadcast ValueError whenever
  S != P. The zeroth term is now the full S x S scaled identity,
  matching zeroth.py's semantics.
- ConstantZeroth.regularization_matrix_from omitted the required
  neighbors_sizes argument, raising TypeError before the shape bug was
  even reached. It now threads linear_obj.neighbors.sizes, mirroring
  Constant.

Siblings adapt_split_zeroth.py and brightness_zeroth.py were checked and
do not carry the pattern (their zeroth terms are diag over per-pixel
weights).

Adds numpy-only unit tests: matrix shape (S, S) with S != P, no null
mode (smallest eigenvalue >= lambda_z^2), reduction to Constant's
matrix + lambda_z^2 * I, and the class-API path returning without
raising. Constant/Adapt/AdaptSplit numerics untouched.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013frRFAHyKN1EpE8sQHTogB
@Jammy2211
Jammy2211 merged commit 74cf5a0 into main Aug 18, 2026
2 checks passed
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.

fix: ConstantZeroth regularization is dead code (eye(P) shape bug + missing neighbors_sizes)

2 participants