Skip to content

feat: AdaptPower regularization — power input + factor-2 scatter fix - #512

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/adapt-linear-regularization
Aug 29, 2026
Merged

Jammy2211 merged 1 commit into
mainfrom
feature/adapt-linear-regularization

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

Adds four corrected sibling regularization classes — AdaptPower, AdaptSplitPower, AdaptSplitZerothPower, MaternAdaptPowerKernel — alongside the existing Adapt family, which is left byte-for-byte unchanged so stored af.Model identifiers, output/ directories and aggregator reloads all stay valid.

Two defects of the legacy family are fixed in the new classes:

  1. λ⁴ coefficient scaling. Adapt squares its coefficients twice — once in adapt_regularization_weights_from, once in the matrix builder — while Constant squares once. Under the shared LogUniform(1e-6, 1e6) prior that drives the regularization matrix numerically non-positive-definite from c ≈ 1e4 instead of c ≈ 1e6. That is the mechanism behind the likelihood-overflow flood diagnosed on RAL pilot 341908_5 (slam_source_pix_nn): fp64 Cholesky returned finite garbage, Nautilus accepted log_l up to 3e+303, and a 6-hour GPU run never terminated. The new classes take power: float = 1.0, so the coefficient enters at 2 · power — the Constant λ² convention by default, and power=2.0 restores the legacy scaling.
  2. A factor-2 scatter asymmetry. weighted_regularization_matrix_from scatters every mesh edge in both directions, and the neighbour list already holds each unordered edge twice, so each edge lands four times where Constant lands it twice. Adapt(inner=outer=c) is therefore exactly 2 × Constant(c), contradicting its own docstring. The new weighted_regularization_matrix_single_scatter_from scatters each ordered pair once with the symmetric edge weight 0.5·(w_i² + w_j²) — a weighted graph Laplacian, so still symmetric and PSD — making AdaptPower(inner=outer=c) equal Constant(c) exactly.

The split family never carried the factor-2 asymmetry (AdaptSplit already shares pixel_splitted_regularization_matrix_from with ConstantSplit), so AdaptSplitPower(inner=outer=c) equals ConstantSplit(c) exactly with the power change alone.

New work should prefer the *Power classes: the λ² scale is far more robust to the gradient / NaN pathologies that bite free-coefficient adaptive fits. Migration for an existing coefficient is c_new = c_old ** 2.

API Changes

Four new regularization classes and one new matrix-builder utility, all additive. adapt_regularization_weights_from gains an optional power keyword whose default reproduces its current behaviour exactly. Nothing existing changes numerically, and no existing symbol is removed, renamed or re-signed.
See full details below.

Test Plan

  • pytest test_autoarray/inversion/regularizations/ — 102 passed (existing pins untouched)
  • pytest test_autoarray/ — 1358 passed
  • AdaptPower(inner=outer=c) matrix == Constant(c) matrix for c ∈ {0.5, 2.0, 7.0} (max abs diff ≤ 1.4e-14)
  • AdaptSplitPower(inner=outer=c) matrix == ConstantSplit(c) matrix, bit-identical
  • power=2.0 reproduces the legacy classes (split / zeroth matrices bit-identical; non-split matrix is the legacy one halved, since the scatter fix applies regardless)
  • Weights are unsquared at power=1.0, squared at power=2.0
  • numpy / JAX parity of the new builder, plus jax.jit + jax.grad finiteness, and numpy/JAX parity of the split path (pytest.importorskip, so the suite still runs without JAX)
  • New builder is symmetric, row-sums to the 1e-8 floor, and has a positive minimum eigenvalue for strongly non-uniform weights
  • Padded (-1) neighbour entries contribute nothing
  • Downstream: PyAutoGalaxy 1152 passed, PyAutoLens 572 passed / 1 xfailed
Full API Changes (for automation & release notes)

Added

  • autoarray.inversion.regularization.AdaptPower(inner_coefficient=1.0, outer_coefficient=1.0, signal_scale=1.0, power=1.0) — Adapt with the Constant coefficient convention and single-edge scatter. Exported as aa.reg.AdaptPower / ag.reg.AdaptPower / al.reg.AdaptPower.
  • autoarray.inversion.regularization.AdaptSplitPower(inner_coefficient=1.0, outer_coefficient=1.0, signal_scale=1.0, power=1.0) — AdaptSplit with the ConstantSplit coefficient convention.
  • autoarray.inversion.regularization.AdaptSplitZerothPower(zeroth_coefficient=1.0, zeroth_signal_scale=1.0, inner_coefficient=1.0, outer_coefficient=1.0, signal_scale=1.0, power=1.0) — as above; the zeroth leg (BrightnessZeroth) is already squared once and is unchanged.
  • autoarray.inversion.regularization.MaternAdaptPowerKernel(scale=1.0, nu=0.5, inner_coefficient=1.0, outer_coefficient=1.0, signal_scale=1.0, jitter=None, jitter_relative=False, power=1.0) — MaternAdaptKernel with the λ² convention.
  • autoarray.inversion.regularization.adapt.weighted_regularization_matrix_single_scatter_from(regularization_weights, neighbors, xp=np) — single-scatter weighted graph Laplacian builder; also re-exported as aa.util.regularization.weighted_regularization_matrix_single_scatter_from.
  • adapt_regularization_weights_from(..., power=2.0) — new optional keyword. The default is the existing behaviour, bit-for-bit; only the new classes pass anything else.

Migration

  • Before: aa.reg.AdaptSplit(inner_coefficient=c, outer_coefficient=c)
  • After: aa.reg.AdaptSplitPower(inner_coefficient=c**2, outer_coefficient=c**2)
  • Or, to keep the legacy coefficient scale on the new class: aa.reg.AdaptSplitPower(inner_coefficient=c, outer_coefficient=c, power=2.0)
  • The legacy classes are unchanged — no migration is required; this is guidance for new work.
  • power ships as a Constant prior (value: 1.0) in autogalaxy/config/priors/regularization/*_power.yaml, so a non-linear search never samples it.

Closes #511.

Generated by the PyAutoLabs agent workflow.

…er fix

Adds AdaptPower, AdaptSplitPower, AdaptSplitZerothPower and
MaternAdaptPowerKernel as corrected siblings of the Adapt family. The legacy
classes and their util functions are untouched, so stored af.Model identifiers,
output directories and aggregator reloads stay valid.

Two things separate the new classes from the legacy ones:

1. The coefficient enters the regularization matrix at 2 * power (default
   power=1.0, the Constant lambda^2 convention) rather than always at lambda^4.
   adapt_regularization_weights_from gains a power keyword defaulting to 2.0,
   so the legacy classes keep their exact numerics.
2. weighted_regularization_matrix_single_scatter_from scatters each mesh edge
   once, so AdaptPower(inner=outer=c) equals Constant(c) exactly instead of
   being 2x it. The split family already shared its builder with ConstantSplit
   and needed no change.

Motivation: under the shared LogUniform(1e-6, 1e6) prior, the lambda^4 scale
drives the regularization matrix non-positive-definite from c ~ 1e4 rather than
c ~ 1e6, which is the mechanism behind the likelihood-overflow flood seen on
RAL pilot 341908_5.

Migration: c_new = c_old ** 2; power=2.0 restores the legacy coefficient
scaling.

Refs #511
@Jammy2211

Copy link
Copy Markdown
Collaborator Author

Downstream companion PRs (merge after this one — their CI checks out the PyAutoArray main, so they stay red on AdaptPower until this merges):

@Jammy2211

Copy link
Copy Markdown
Collaborator Author

Correction to the note above: the downstream PRs are not red. PyAutoHeart's reusable lib-tests.yml clones each dependency library at the matching branch if it exists, and all three PRs share the branch name feature/adapt-linear-regularization, so PyAutoGalaxy#592 and PyAutoLens#716 were tested against this branch and are green on all four legs.

Library-first merge order still applies so that main never references AdaptPower before the symbol exists: #512 → PyAutoGalaxy#592 → PyAutoLens#716.

@Jammy2211
Jammy2211 merged commit 302d5df into main Aug 29, 2026
3 checks passed
@Jammy2211
Jammy2211 deleted the feature/adapt-linear-regularization branch August 29, 2026 20:33
palacios22c pushed a commit to palacios22c/PyAutoLens that referenced this pull request Sep 3, 2026
…ings

Mirrors the four *Power regularization classes introduced in PyAutoArray
(PyAutoLabs/PyAutoArray#512) into the test_autolens prior config, adds
AdaptPower / AdaptSplitPower to the docs/api pixelization autosummary, and adds
a composition test proving the al.reg re-export chain and the prior-config
resolution.

Depends on PyAutoLabs/PyAutoArray#512. Refs PyAutoLabs/PyAutoArray#511
@Jammy2211 Jammy2211 removed the pending-release PR queued for the next release build label Sep 4, 2026
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.

feat: AdaptPower regularization — power input + factor-2 scatter fix

1 participant