autogalaxy.Settings#

class Settings[source]#

Bases: object

The settings of an Inversion, customizing how a linear set of equations are solved for.

An Inversion is used to reconstruct a dataset, for example the luminous emission of a galaxy.

Parameters:
  • use_mixed_precision (bool) –

    If True, a targeted subset of the inversion’s linear algebra runs in single precision (float32 / complex64) instead of double precision (float64 / complex128). This is intended to reduce VRAM use and speed up the FFT-heavy and bandwidth-bound steps on GPU and CPU; only the JAX (xp=jnp) paths honor the flag — the NumPy backend always runs in fp64.

    Paths that honor the flag:

    • PSF FFT convolution in Convolver.convolved_image_from (the light-profile blurring path, used by linear MGE bases and similar): the input image, kernel multiply and inverse FFT all run in complex64 / float32 end to end. This is the headline GPU win for MGE imaging pipelines.

    • PSF FFT convolution in Convolver.convolved_mapping_matrix_from (the pixelization mapping matrix path): the input cube is fp32 and the forward rfft2 runs in complex64, but the kernel multiply intentionally upcasts back to complex128 so the inverse FFT and downstream linear algebra stay fp64. Pixelization meshes with K ≫ 40 source pixels accumulate enough fp32 round-off through NNLS / log-determinant to shift figure_of_merit by O(1) units; the upcast preserves precision while the cheaper fp32 scatter and forward FFT are kept.

    • The mapping matrix allocation in autoarray.inversion.mappers.mapper_util.mapping_matrix_from and the native cube in Convolver.mapping_matrix_native_from — output dtype becomes fp32 on the JAX backend only; under xp=np both stay fp64, so a NumPy fit is a true fp64 reference for a mixed-precision JAX fit (PyAutoArray#552).

    Empirical platform notes:

    • GPU: full pipeline single-JIT roughly matches the fp64 baseline; vmap-batched evaluation (the production sampler hot path) shows 25–30% speedup on RTX 2060-class hardware.

    • CPU: the per-call FFT itself is ~1.6× faster in fp32, but JAX/XLA’s CPU FFT lowering does not always re-compose well across ~40-call MGE-basis pipelines, so the single-JIT measurement can be neutral or slightly slower than fp64. vmap remains comparable to or slightly faster than fp64. The flag is most beneficial for GPU users.

    Paths that intentionally stay in fp64:

    • The noise weighting of the curvature matrix in autoarray.inversion.inversion.inversion_util.curvature_matrix_via_mapping_matrix_from: F and the data vector D are both formed with fp64 1 / noise_map on every backend, so the linear system is weighted consistently (an earlier fp32 1 / noise_map on the JAX branch never produced an fp32 accumulation, because the blurred mapping matrix is already fp64 — it only biased F against D; PyAutoArray#552).

    • The NNLS reconstruction (jaxnnls / Cholesky factor + cho_solve) in autoarray.inversion.inversion.inversion_util.reconstruction_positive_only_from. Active-set and PDIP solvers are sensitive to fp32 noise on ill-conditioned source meshes.

    • The log-determinant of the curvature regularization matrix used by figure_of_merit: condition numbers can exceed 1e6 on fine pixelizations and fp32 silently loses 1+ digit there.

    • Light profile evaluation on the (over-)sampled grid; only the resulting mapping matrix is downcast.

    Empirical numerical impact on the MGE imaging regression (HST-shaped, 15k masked pixels, 40 linear Gaussians): Δlog-likelihood ≈ 1e-4 absolute at log-likelihood ≈ 27,400. Well below the natural χ² sampling noise floor (σ ≈ √(2N) ≈ 175). On a 316-pixel RectangularBilinearAdaptImage + Adapt inversion (17×17 mesh) the whole mixed-precision effect on the JAX path is Δlog-likelihood ≈ 1e-4 nats against the fp64 path (PyAutoArray#552); pixelization paths with K ≫ 40 source pixels are more sensitive — verify on representative integration tests before turning on for production fits.

    If False (default), all paths run in fp64.

  • use_positive_only_solver (Optional[bool]) – Whether to use a positive-only linear system solver, which requires that every reconstructed value is positive but is computationally much slower than the default solver (which allows for positive and negative values).

  • use_border_relocator (Optional[bool]) – If True, all coordinates of all pixelization source mesh grids have pixels outside their border relocated to their edge.

  • no_regularization_add_to_curvature_diag_value (float) – If a linear func object does not have a corresponding regularization, this value is added to its diagonal entries of the curvature regularization matrix to ensure the matrix is positive-definite. The packaged configuration defaults to 1.0e-3; workspaces may override it. The addition is absolute, so its effect depends on the scale of the curvature matrix.

  • nnls_solver_tol (Optional[float]) – Convergence tolerance (infinity-norm KKT residual) of the JAX positive-only (NNLS) interior-point solve. None (default) uses jaxnnls’s own tolerance min(n * eps * 5e3, 1e-2) (~1.7e-9 at n=1500 fp64) — behaviour is identical to not having this setting. Each solver iteration is a fresh dense Cholesky of the (n, n) system, so looser tolerances buy real speed: 1e-6 saves ~15-20% of solve time with a log-evidence shift of order 1e-8 on production HST pixelization+MGE fits (measured in https://github.com/PyAutoLabs/PyAutoArray/issues/369). Only the JAX (xp=jnp) path honors this; the NumPy fnnls path is unaffected.

  • nnls_max_iter (Optional[int]) – Iteration cap of the JAX positive-only (NNLS) interior-point solve. None (default) uses jaxnnls’s own hard-coded cap of 50 (production HST pixelization+MGE systems converge in ~19-21 iterations). Under vmap the solve runs until the slowest lane in the batch converges, so this also caps the worst-case batched cost. Only the JAX (xp=jnp) path honors this.

  • nnls_warm_start_memo (Optional[bool]) – Whether the NumPy / numba positive-only (NNLS) solve warm-starts its active-set iteration from the passive set of the previous likelihood evaluation, held in a small process-local memo (autoarray.inversion.inversion.nnls_memo). Successive sampler evaluations sit close together in parameter space and share most of their passive set, so the seeded start removes active-set iterations from the solve, which is ~70% of a numba CPU likelihood evaluation on production Delaunay meshes. The NNLS optimum is unique, so the reconstruction is unchanged. None (default) reads the packaged value (true); setting AUTOARRAY_NNLS_WARM_START=0 is the process-wide kill-switch that disables the memo whatever this setting says. Only the NumPy (xp=np) fnnls path honors this; the JAX path ignores it.

  • nnls_warm_start_error_tolerance (Optional[float]) – The relative guard on a memo seed’s quality. Each memo entry carries a reference: the error fraction (warm-start errors / solve size) of the most recent solve for that key which started from the dense-sign guess. A memo-seeded solve whose own error fraction exceeds tolerance * reference is judged worse than simply starting dense, so its entry is discarded and the next solve for that key restarts from the dense-sign start, refreshing the reference. The guard is relative because the absolute error fraction does not separate helpful from unhelpful seeds (the two populations overlap at 0.048-0.138 in the PyAutoArray#498 32-cell robustness matrix) whereas the ratio to the dense-sign start does (helpful cells top out at 0.89, the worst seed measured reaches 1.42). None (default) reads the packaged value (1.5), chosen above that worst observed ratio so the guard is protective against regimes far outside the matrix rather than flapping inside it. The guard is disabled by any value that is not finite and positive – float("inf") is the idiomatic choice, and 0 or a negative value disables it too rather than meaning “drop everything”. Only the NumPy (xp=np) fnnls path honors this; the JAX path ignores it.

  • log_det_method (Optional[str]) –

    Which computation is used for the two Bayesian-evidence log-determinant terms (log_det_curvature_reg_matrix_term and log_det_regularization_matrix_term). None (default) reads the packaged value "cholesky".

    • "cholesky" (default) — 2 * sum(log(diag(cholesky(M)))), the historical computation. On a non-positive-definite matrix the NumPy backend raises and the JAX backend returns NaN.

    • "slogdet" — the logabsdet of xp.linalg.slogdet(M). Where M is positive-definite this equals the Cholesky value exactly; where the Cholesky would NaN it returns a finite, differentiable value instead, so it never stalls a gradient-based search (autolens_workspace_developer#104). This is an opt-in, non-default alternative intended for gradient-based work and for comparison against the Cholesky evidence — it is not a replacement for the default and the default evidence path is unchanged. See PyAutoArray#391.

      Under "slogdet", the kernel regularization schemes (MaternKernel, GaussianKernel, ExponentialKernel, MaternAdaptKernel) additionally compute log_det_regularization_matrix_term analytically from a single Cholesky of their kernel covariance C (log det H = pixels * log(coeff) - log det C) instead of factorizing the explicitly formed inverse — more accurate (the formed C^-1 carries round-off amplified by cond(C), ~1e-6 absolute in the evidence on clustered traced mesh vertices) and finite at any regularization coefficient (C’s conditioning does not depend on it). See AbstractRegularization.log_det_regularization_matrix_term_from.

  • regularization_term_method (Optional[str]) –

    How the Bayesian-evidence regularization term s^T H s (AbstractInversion.regularization_term) is computed. None (default) reads the packaged value "matmul".

    • "matmul" (default) — s @ (H @ s) against the explicitly formed regularization matrix, the historical computation.

    • "cho_solve" — for the kernel regularization schemes (MaternKernel, GaussianKernel, ExponentialKernel, MaternAdaptKernel), whose H = coefficient * C^-1, evaluate coefficient * s^T C^-1 s by solving C x = s through one Cholesky of the kernel covariance C instead of forming C^-1 and contracting it. More accurate — the explicit inverse’s round-off is amplified by cond(C), which reaches ~1e9 on the clustered traced vertices of the kNN mesh families — and cheaper, being one triangular solve rather than N. This is an opt-in, non-default alternative; schemes with no such factorization (Constant, Adapt, the split families) have no shortcut and fall back to the formed matrix, so mixed inversions stay correct. See AbstractRegularization.regularization_term_from.

      This is deliberately separate from log_det_method: the two terms can be moved onto their exact factorizations independently, which is what makes it possible to attribute an evidence shift to one term rather than both.

      Note neither option removes the explicit inverse from the inversion as a whole — curvature_reg_matrix is a dense F + H feeding the dense solve for the reconstruction, so H is still formed there regardless.

  • interferometer_numba_nnz_per_source_max (Optional[float]) – How a NumPy single-mapper interferometer inversion chooses between the numba direct_conv curvature path and the FFT one. None (default) reads the packaged value, which is itself None: a cost model predicts both routes’ time from the source pixels, the mapping operator’s non-zeros and the extent rectangle, and picks the faster (PyAutoArray#628). A positive value is a hard gate in mean non-zeros per source column (mapper.pix_sizes_for_sub_slim_index.sum() / mapper.params): numba at or below it, FFT above. 0 disables the numba path. See the property of the same name.

  • interferometer_route (Optional[str]) – Forces the interferometer curvature route, for benchmarking. None (default) reads the packaged value, which is None: route by interferometer_numba_nnz_per_source_max. "fft" always uses the FFT route (InversionInterferometerSparse); "numba" uses the numba direct_conv route (InversionInterferometerSparseNumba) whenever it can represent the inversion (NumPy backend, one mapper, no linear functions, no over-sampling, numba installed), overriding the geometry gate and the 0 kill switch. "matrix_free" uses InversionInterferometerMatrixFree, which never forms the dense curvature matrix F or regularization matrix H: F s is applied as an FFT convolution, the reconstruction is a preconditioned CG (or interior-point positive) solve and ln det(F + H) a stochastic Lanczos quadrature estimate. It requires the NumPy backend, one RectangularUniform mapper with aa.reg.MaternSparse regularization, no linear functions and, with the positive-only solver, use_edge_zeroed_pixels=False; it raises on anything else rather than falling back, and dense-only attributes (e.g. curvature_matrix, reconstruction_noise_map) are unavailable.

  • interferometer_direct_conv_symmetric_max_work_fraction (Optional[float]) – When the serial numba direct_conv route runs, the largest geometry work fraction at which it uses the symmetric, row-restricted kernel (curvature_direct_conv_symmetric) instead of the full one. None (default) reads the packaged value (0.7); 0 always uses the full kernel. The result is the same either way (to round-off); only the time differs. See the property of the same name.

  • positive_only_solver (Optional[str]) –

    Which solver the JAX (xp=jnp) positive-only reconstruction uses. None (default) reads the packaged value "pdip".

    • "pdip" (default) — the jaxnnls primal-dual interior-point solve, unchanged.

    • "certified" — the certified active-set solve (autoarray.util.jax_active_set): a budgeted lax.while_loop of masked Cholesky solves that stops once the iterate satisfies the primal and dual (KKT) conditions, with the PDIP solve as a fallback when the budget is exhausted. Its gradient is the exact implicit active-set derivative. Measured 1.2-2.6x faster than PDIP on source-only inversions returning the same constrained optimum (PyAutoArray#566).

    "certified" is applied only on the JAX backend to mapper-only inversions (no linear light-profile / MGE coefficients, which converge poorly under the active-set scheme); every other inversion, and the whole NumPy path, keeps its existing solver (AbstractInversion.positive_only_solver_used records the decision). Opt-in until the batched (vmap) policy is measured.

  • certified_pass_budget (Optional[int]) – Maximum number of restricted active-set passes of the "certified" solver. None (default) reads the packaged value (16). Measured passes to certification: rectangular <= 11, Delaunay <= 7; the loop exits at certification, so unused budget costs nothing.

  • certified_fallback (Optional[str]) – What the "certified" solver returns when it exhausts its budget uncertified. None (default) reads the packaged value "pdip" (run the PDIP solve instead, via lax.cond; under vmap that cond executes both solvers for every lane). "none" returns the last, uncertified iterate.

  • certified_tau_rel (Optional[float]) – Relative KKT tolerance of the "certified" solver’s certificate: primal violations are x < -tau_rel * max|x|, dual violations g < -tau_rel * max|q|. None (default) reads the packaged value (1.0e-9).

  • nnls_preconditioning_no_mapper (Optional[str]) –

    How the JAX positive-only PDIP solve scales an inversion with no `Mapper` (linear light profiles / MGE only). None (default) reads the packaged value "raw".

    • "raw" (default) – the forward PDIP solve runs on the un-preconditioned system with a data-scaled KKT tolerance (1e-2 * n * eps_pdip * max(1, max|data_vector|), or nnls_solver_tol if set), polished by at most 10 tight warm-started PDIP iterations on the Jacobi-scaled system; the polished iterate is the returned reconstruction (unpolished, the loose tolerance left 11.5 % of the fnnls flux on inactive columns of the euclid vis_lp system, PyAutoArray#594) and the start of the gradient, the same Jacobi-space relaxed-KKT pass as "jacobi". On the SLaM source_lp[1] MGE model (2 x 20 lens + 20 source Gaussians) Jacobi scaling made 14/48 near-truth points hit the 50-iteration cap with wrong log-likelihoods (signal-free Gaussian columns, whose diagonal is only no_regularization_add_to_curvature_diag_value, become degenerate coordinates on which the PDIP dual diverges); the raw solve converges on all of them in 16-19 iterations (PyAutoArray#571).

    • "jacobi" – the Jacobi-preconditioned solve, as for mapper inversions.

    Inversions containing a Mapper always use "jacobi" (AbstractInversion.positive_only_preconditioning_used records the decision). The NumPy path always runs fnnls and ignores this.

Methods

Attributes

certified_fallback

"pdip" (the PDIP solve) or "none".

certified_pass_budget

Maximum number of restricted active-set passes of the "certified" solver.

certified_tau_rel

Relative KKT tolerance of the "certified" solver's certificate.

interferometer_direct_conv_symmetric_max_work_fraction

0.7 (packaged default), 0 for the kill switch (always the full kernel).

interferometer_numba_nnz_per_source_max

How the factory chooses between the numba direct_conv interferometer curvature path and the FFT one: None (packaged default) for the cost model, a positive value for a hard non-zeros-per-source-column gate, 0 for never numba.

interferometer_route

None (route by interferometer_numba_nnz_per_source_max), "numba", "fft" or "matrix_free".

log_det_method

nnls_preconditioning_no_mapper

"raw" or "jacobi".

nnls_warm_start_error_tolerance

How much worse than the dense-sign start a memo seed may be before its entry is dropped.

nnls_warm_start_memo

Whether the NumPy fnnls solve is warm-started from the previous evaluation's passive set.

no_regularization_add_to_curvature_diag_value

positive_only_solver

"pdip" or "certified".

regularization_term_method

use_border_relocator

use_edge_zeroed_pixels

Whether a mesh's edge pixels are excluded from the inversion and fixed to zero.

use_positive_only_solver

property use_positive_only_solver#
property use_edge_zeroed_pixels#

Whether a mesh’s edge pixels are excluded from the inversion and fixed to zero.

This is consulted only when `use_positive_only_solver` is `True`. Under the positive-negative solver the full system is solved and this setting has no effect, so the two are not independent switches despite reading that way in config/general.yaml.

That scoping is deliberate. It is called out here because the nesting is not visible from the config, and has been mistaken for a bug (a setting “silently ignored”) by someone reading the control flow in AbstractInversion.reconstruction without it.

property use_border_relocator#
property no_regularization_add_to_curvature_diag_value#
property nnls_warm_start_memo: bool#

Whether the NumPy fnnls solve is warm-started from the previous evaluation’s passive set.

property nnls_warm_start_error_tolerance: float#

How much worse than the dense-sign start a memo seed may be before its entry is dropped.

A seeded solve whose error fraction exceeds this multiple of the entry’s dense-sign reference fraction is discarded, so the next solve for that key restarts dense. Any value that is not finite and positive disables the guard.

property log_det_method#
property regularization_term_method#
property interferometer_numba_nnz_per_source_max: Optional[float]#

How the factory chooses between the numba direct_conv interferometer curvature path and the FFT one: None (packaged default) for the cost model, a positive value for a hard non-zeros-per-source-column gate, 0 for never numba.

InversionInterferometerSparseNumba convolves each non-zero of the mapping operator over the extent rectangle, so its cost scales with the total non-zeros times the extent area and is flat in the number of source pixels; the FFT route (InversionInterferometerSparse) transforms the padded extent once per source column, so its cost scales with the source pixels times the padded extent’s transform cost. At a fixed extent the two cross at a fixed mean non-zeros per source column, mapper.pix_sizes_for_sub_slim_index.sum() / mapper.params – but that crossover moves with the extent: ~60 (Delaunay) / ~77 (rectangular) at the autolens_profiling#226 geometries (extents 70^2-280^2), between 106 and 188 on the 512x512 Aris mask (extent 341^2, model crossover ~166; PyAutoArray#628), so no single constant is right for both.

  • None – the cost model (inversion_interferometer_numba_util.curvature_route_seconds_from, constants calibrated in that module) predicts both routes’ time and the factory routes to numba when direct_conv is predicted no slower.

  • positive – the pre-#628 hard gate: numba at or below this many non-zeros per source column, FFT above, whatever the extent. Honoured as given, so A/B scripts that set it keep their meaning.

  • 0 – the kill switch: never numba.

The cost model’s constants are single-thread measurements on one CPU; the ratio that decides a route depends on the ratio of scalar AXPY throughput to FFT throughput, so a very different machine may cross elsewhere. interferometer_route forces a route for benchmarking.

property interferometer_route: Optional[str]#

None (route by interferometer_numba_nnz_per_source_max), "numba", "fft" or "matrix_free".

See the constructor docstring; "numba" still requires an inversion the direct_conv kernel can represent, and "matrix_free" raises on an inversion InversionInterferometerMatrixFree cannot represent.

Type:

A forced interferometer curvature route

property interferometer_direct_conv_symmetric_max_work_fraction: float#

0.7 (packaged default), 0 for the kill switch (always the full kernel).

curvature_direct_conv_symmetric exploits F’s symmetry (source columns ranked by their top extent row, each convolving only the rows later-ranked columns span) and each row’s mask x-range. Its work_fraction (inversion_interferometer_numba_util.symmetric_restriction_inputs_from) is the fraction of the full kernel’s convolution work it still does (PyAutoArray#635):

  • the synthetic Aris-512 geometry (compact 3-NN columns): f ~ 0.41, 1.81-1.90x faster single-threaded on RAL;

  • Aris’s real 512x512 Delaunay mesh (multiply imaged, tall columns): f ~ 0.51, measured ~1.46x, as the cost model’s 1 / (kappa f) predicts.

The cost model (DIRECT_CONV_SYMMETRIC_WORK_FACTOR, kappa = 1.32) says the kernel wins while kappa f < 1, i.e. f < 0.76; the default 0.7 keeps a predicted gain of at least 1.08x. With serial numba and work_fraction <= this value (value > 0) the symmetric kernel is used, and the route cost model is told so; the parallel path always uses the full kernel. Must be non-negative.

Type:

The work-fraction gate of the serial symmetric direct_conv kernel

property positive_only_solver: str#

"pdip" or "certified".

See the constructor docstring; "certified" is only applied to mapper-only JAX inversions.

Type:

Which solver the JAX positive-only reconstruction uses

property certified_pass_budget: int#

Maximum number of restricted active-set passes of the "certified" solver.

property certified_fallback: str#

"pdip" (the PDIP solve) or "none".

Type:

What an exhausted "certified" solve returns

property certified_tau_rel: float#

Relative KKT tolerance of the "certified" solver’s certificate.

property nnls_preconditioning_no_mapper: str#

"raw" or "jacobi".

See the constructor docstring; inversions with a Mapper always use "jacobi".

Type:

How the JAX PDIP solve scales an inversion with no Mapper