feat: AdaptPower regularization — power input + factor-2 scatter fix - #512
Merged
Merged
Conversation
…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
Collaborator
Author
|
Downstream companion PRs (merge after this one — their CI checks out the PyAutoArray
|
Collaborator
Author
|
Correction to the note above: the downstream PRs are not red. PyAutoHeart's reusable Library-first merge order still applies so that |
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
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
Adds four corrected sibling regularization classes —
AdaptPower,AdaptSplitPower,AdaptSplitZerothPower,MaternAdaptPowerKernel— alongside the existingAdaptfamily, which is left byte-for-byte unchanged so storedaf.Modelidentifiers,output/directories and aggregator reloads all stay valid.Two defects of the legacy family are fixed in the new classes:
Adaptsquares its coefficients twice — once inadapt_regularization_weights_from, once in the matrix builder — whileConstantsquares once. Under the sharedLogUniform(1e-6, 1e6)prior that drives the regularization matrix numerically non-positive-definite fromc ≈ 1e4instead ofc ≈ 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 acceptedlog_lup to3e+303, and a 6-hour GPU run never terminated. The new classes takepower: float = 1.0, so the coefficient enters at2 · power— theConstantλ² convention by default, andpower=2.0restores the legacy scaling.weighted_regularization_matrix_fromscatters every mesh edge in both directions, and the neighbour list already holds each unordered edge twice, so each edge lands four times whereConstantlands it twice.Adapt(inner=outer=c)is therefore exactly2 ×Constant(c), contradicting its own docstring. The newweighted_regularization_matrix_single_scatter_fromscatters each ordered pair once with the symmetric edge weight0.5·(w_i² + w_j²)— a weighted graph Laplacian, so still symmetric and PSD — makingAdaptPower(inner=outer=c)equalConstant(c)exactly.The split family never carried the factor-2 asymmetry (
AdaptSplitalready sharespixel_splitted_regularization_matrix_fromwithConstantSplit), soAdaptSplitPower(inner=outer=c)equalsConstantSplit(c)exactly with thepowerchange alone.New work should prefer the
*Powerclasses: the λ² scale is far more robust to the gradient / NaN pathologies that bite free-coefficient adaptive fits. Migration for an existing coefficient isc_new = c_old ** 2.API Changes
Four new regularization classes and one new matrix-builder utility, all additive.
adapt_regularization_weights_fromgains an optionalpowerkeyword 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 passedAdaptPower(inner=outer=c)matrix== Constant(c)matrix forc ∈ {0.5, 2.0, 7.0}(max abs diff ≤ 1.4e-14)AdaptSplitPower(inner=outer=c)matrix== ConstantSplit(c)matrix, bit-identicalpower=2.0reproduces the legacy classes (split / zeroth matrices bit-identical; non-split matrix is the legacy one halved, since the scatter fix applies regardless)power=1.0, squared atpower=2.0jax.jit+jax.gradfiniteness, and numpy/JAX parity of the split path (pytest.importorskip, so the suite still runs without JAX)1e-8floor, and has a positive minimum eigenvalue for strongly non-uniform weights-1) neighbour entries contribute nothingFull 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)—Adaptwith theConstantcoefficient convention and single-edge scatter. Exported asaa.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)—AdaptSplitwith theConstantSplitcoefficient 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)—MaternAdaptKernelwith 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 asaa.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
aa.reg.AdaptSplit(inner_coefficient=c, outer_coefficient=c)aa.reg.AdaptSplitPower(inner_coefficient=c**2, outer_coefficient=c**2)aa.reg.AdaptSplitPower(inner_coefficient=c, outer_coefficient=c, power=2.0)powerships as aConstantprior (value: 1.0) inautogalaxy/config/priors/regularization/*_power.yaml, so a non-linear search never samples it.Closes #511.
Generated by the PyAutoLabs agent workflow.