Skip to content

epic: @rhayes777 API audit — 16 verified findings across 3 repos #415

Description

@Jammy2211

Overview

@rhayes777 filed five issues on 2026-05-23 while auditing released 2026.5.21.1
against the documented public API: PyAutoArray#332, PyAutoArray#333,
PyAutoGalaxy#440, PyAutoLens#531, PyAutoLens#532. They received zero replies for
66 days
. All five were answered on 2026-07-28, and every one of the 16 findings
was re-verified against current main first — all 16 still reproduce.

This epic tracks the campaign that fixes them. It exists because all five replies
promise @rhayes777 a single place to watch.

Decision (2026-07-28): we implement in-house. His "Happy to PR if useful" offer
was declined warmly — the affected constructors sit on JAX-traced hot paths and
guards that stay correct under tracing are not a fair hand-off.

Verified against main

PyAutoArray@616e8b4c · PyAutoGalaxy@f0b65f39 · PyAutoLens@6567b3b1c — all clean,
zero ahead/behind.

Issue Findings Verified
#332 ConstantSplit×RectangularUniform; adaptive-mesh precondition error 3/3
#333 B5, B6, B7, B8, B13 — input validation 5/5
PyAutoGalaxy#440 B9, B10, B11, B12 — profile validation 4/4
PyAutoLens#531 PointSolver AxisError + IndexError 2/2
PyAutoLens#532 B4 + redshift ordering 2/2

Correction to #332's headline

#332 reports that Delaunay and KNNBarycentric are "unusable in FitImaging".
They are not. They require an image-plane mesh grid via adapt_images, and with
it both fit cleanly:

Delaunay(pixels=100)       + Constant + adapt_images  ->  log_evidence 5084.7513
KNNBarycentric(pixels=100) + Constant + adapt_images  ->  log_evidence 5211.7226
Delaunay(pixels=100)       + Constant, NO adapt       ->  AttributeError 'NoneType'
RectangularUniform(15,15)  + ConstantSplit            ->  AttributeError  (Split unsupported)
RectangularAdaptDensity    + ConstantSplit            ->  IndexError      (Split unsupported)
Delaunay(pixels=100)       + ConstantSplit + adapt    ->  log_evidence 5096.4420

The real defect is that a missing precondition surfaces as
AttributeError: 'NoneType' object has no attribute 'array' from inside
border_relocator.py, naming nothing the caller controls.

This inverts the fix direction. A regression test must assert that bare
construction fails clearly, naming adapt_imagesnot that it succeeds.

Phases

Phase 1 — genuine crash bugs (real user impact, no workaround) — #416, in progress

  • Split regularization on rectangular meshes. Rectangular does not support
    Split — the fix is a clear "unsupported" exception, not the missing capability.
    Verified scope is 9 combinations in 2 failure modes (3 rectangular meshes x 3
    Split regularizations): RectangularUniformAttributeError (property absent);
    RectangularAdaptDensity / RectangularAdaptImageIndexError (the pass-through
    at rectangular.py:460 claims split "reuses the same mappings", which crashes one
    frame later in reg_split_from). Delaunay + ConstantSplit works, so this is
    specific to the rectangular family.
  • PointSolver.solve on a source outside the caustic → numpy AxisError
  • PointSolver.solve with loose pixel_scale_precisionIndexError
    (same source at 0.001 returns 4 images)

Phase 2 — constructor validation sweep (9 findings: B4–B13)
One shared _validate_* helper. Where it lives must be decided before writing
it
— PyAutoArray is the natural floor, since Galaxy and Lens both depend on it —
or the three repos ship inconsistent messages.

Phase 3 — error legibility
adapt_images precondition fails fast and legibly; regression test asserts the
clear failure. Plus B10 (2.357e-06 Isothermal/IsothermalSph difference at the
degenerate point) pinned at an explicit tolerance — not chased to bit-identity.

Phase 4 — HELD, awaiting @rhayes777
z_lens > z_source should warn rather than raise (multi-plane genuinely supports
geometries that look wrong under two-plane naming); negative redshift can raise
outright. The #532 reply asks him whether even a warning would be noise in a real
multi-plane setup. Held until he answers — everything else is unblocked.

Constraint carried into every phase

These constructors are JAX-traced. Guards must stay correct under tracing and
cost nothing when traced — no Python if on values that may be tracers.

Sharpened finding on B13

constant.py:43 squares the coefficient, which is why log_evidence is identical
for +1.0 and -1.0. But regularization_weights_from (constant.py:127) returns
the raw un-squared value, so a negative coefficient does leak negative
regularization weights into every consumer of that method. Not inert.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions