Skip to content

feat: first-class latent variable API in PyAutoGalaxy#441

Merged
Jammy2211 merged 3 commits into
mainfrom
feature/latent-module-autogalaxy
May 23, 2026
Merged

feat: first-class latent variable API in PyAutoGalaxy#441
Jammy2211 merged 3 commits into
mainfrom
feature/latent-module-autogalaxy

Conversation

@Jammy2211
Copy link
Copy Markdown
Collaborator

@Jammy2211 Jammy2211 commented May 23, 2026

Summary

PyAutoGalaxy now has a first-class latent variable API. Users no longer need to subclass AnalysisImaging and hand-write LATENT_KEYS + compute_latent_variables to get derived quantities into latent.csv. A new autogalaxy/imaging/model/latent.py module owns the registry of named latent computations, autogalaxy/config/latent.yaml lets users toggle individual latents on/off without editing Python, and AnalysisImaging overrides PyAutoFit's stubbed hook to dispatch through the registry.

Day-1 catalogue is one concrete latent — total_galaxy_0_flux_mujy (total integrated flux of the first galaxy in the fit, magzero-converted to microjanskies). It defaults OFF in autogalaxy/config/latent.yaml because it requires magzero to be passed via AnalysisImaging(..., magzero=<value>); existing fits that don't pass magzero would otherwise crash once compute_latent_samples ran. Users opt in by editing the yaml AND passing magzero.

This is the dependency root of the broader latent_refactor epic; PyAutoLens (next sub-prompt) will add the lensing-specific latents (magnification, effective Einstein radius, lensed source flux) on top of this infrastructure.

Closes #439.

API Changes

New public module autogalaxy.imaging.model.latent with helpers (ab_mag_via_flux_from, flux_mujy_via_ab_mag_from), the LATENT_FUNCTIONS registry, the latent_keys_enabled() reader, and one registered latent (total_galaxy_0_flux_mujy, disabled by default). AnalysisImaging gains a LATENT_KEYS @property and a compute_latent_variables(parameters, model) method — both new public surface. New config file autogalaxy/config/latent.yaml. Note: autoconf lowercases yaml keys, so the latent name uses lowercase mujy (not muJy) — this leaks through to the latent.csv column header.

See full details below.

Test Plan

  • pytest test_autogalaxy/imaging/model/test_latent.py -x -v — 9/9 pass
  • pytest test_autogalaxy/ — 917/917 pass (no regression)
  • Default-off check: existing AnalysisImaging fits without magzero continue to produce empty LATENT_KEYS, so compute_latent_variables returns an empty tuple — no crash.
  • End-to-end opt-in smoke (follow-up): enable the latent in a workspace config/latent.yaml, pass magzero via Analysis kwargs, run an autogalaxy_workspace modeling script, inspect output/.../latent/latent_summary.json.
Full API Changes (for automation & release notes)

Added

  • autogalaxy.imaging.model.latent — new module
  • autogalaxy.imaging.model.latent.ab_mag_via_flux_from(flux, magzero, xp=np) — flux → AB magnitude
  • autogalaxy.imaging.model.latent.flux_mujy_via_ab_mag_from(ab_mag, xp=np) — AB magnitude → flux in microjanskies
  • autogalaxy.imaging.model.latent.total_galaxy_0_flux_mujy(fit, magzero, xp=np) — concrete latent; returns NaN when galaxy 0 has no light profile; raises ValueError if magzero is None
  • autogalaxy.imaging.model.latent.LATENT_FUNCTIONS: Dict[str, Callable] — flat registry of name → function
  • autogalaxy.imaging.model.latent.latent_keys_enabled(yaml_config=None) -> List[str] — reads conf.instance["latent"] (or an explicit dict for tests); returns enabled keys filtered to those present in LATENT_FUNCTIONS
  • autogalaxy.AnalysisImaging.LATENT_KEYS — new @property overriding the inherited class-level []; reads from config at call time
  • autogalaxy.AnalysisImaging.compute_latent_variables(parameters, model) — overrides PyAutoFit's stubbed hook; returns a tuple positionally aligned with LATENT_KEYS
  • autogalaxy/config/latent.yaml — new config file with one toggle (total_galaxy_0_flux_mujy: false by default)

Changed Behaviour

  • No behaviour change for existing fits with the default config — the only registered latent ships off-by-default, so LATENT_KEYS is [] and compute_latent_variables returns (). The latent pipeline runs but produces no output, matching today's behaviour.
  • When the latent is enabled (user-edited yaml), AnalysisImaging instances produce latent.csv / latent_summary.json outputs containing total_galaxy_0_flux_mujy columns, provided the user also passes magzero via AnalysisImaging(..., magzero=<value>). If magzero is absent when the latent is enabled, the computation raises ValueError loudly (no silent NaN).

Migration

None required. Existing code continues to work unchanged. To opt into the new latent: set total_galaxy_0_flux_mujy: true in your workspace's config/latent.yaml and pass magzero when constructing AnalysisImaging.

For the follow-up workspace tutorial: workspaces should mirror autogalaxy/config/latent.yaml into <workspace>/config/latent.yaml so users have a clear opt-in surface.

🤖 Generated with Claude Code

Add autogalaxy/imaging/model/latent.py with a registry-driven catalogue
of latent variables, autogalaxy/config/latent.yaml as a user-toggle
layer, and AnalysisImaging overrides (LATENT_KEYS property +
compute_latent_variables method) so users get a curated default latent
set without subclassing.

Day-1 catalogue: total_galaxy_0_flux_mujy (total integrated flux of
the first galaxy in the fit, magzero-converted to microjanskies).
Helpers ab_mag_via_flux_from and flux_mujy_via_ab_mag_from are ported
from euclid_strong_lens_modeling_pipeline/util.py.

Refs: #439
@Jammy2211 Jammy2211 added the pending-release PR queued for the next release build label May 23, 2026
Jammy2211 added 2 commits May 23, 2026 12:20
Existing AnalysisImaging users don't pass magzero today, but
compute_latent_samples runs on every fit (latent_after_fit defaults
to true in output.yaml). If the latent stays on by default, every
existing fit would crash with ValueError once it tries to compute
total_galaxy_0_flux_mujy without magzero.

Default the latent off in the library config so existing fits remain
green. Users who want the latent must enable it in their workspace
config/latent.yaml AND pass magzero=<value> to AnalysisImaging.

Test config (test_autogalaxy/config/latent.yaml) stays on so the
existing tests continue to exercise the AnalysisImaging dispatch path.
Raise NotImplementedError when LATENT_KEYS is [] (the default-off case)
so PyAutoFit's existing `except NotImplementedError: return None` at
autofit/non_linear/analysis/analysis.py:304 cleanly skips the latent
pipeline. Otherwise the empty-tuple flow runs the batch loop, builds
Sample objects with empty kwargs, and calls simple_model_for_kwargs({})
— wasted work and fragile against future autofit changes.

Adds a test that confirms the explicit raise.
@Jammy2211 Jammy2211 merged commit a5cc42a into main May 23, 2026
6 checks passed
@Jammy2211 Jammy2211 deleted the feature/latent-module-autogalaxy branch May 23, 2026 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pending-release PR queued for the next release build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: first-class latent variable API in PyAutoGalaxy

1 participant