docs: adopt the flat fields=<MassField> model form across the workspace examples [draft until release] - #562
Merged
Merged
Conversation
Every single-entry `fields=af.Collection(field=X)` in `scripts/` now writes
`fields=X`, matching the merged euclid reference and the library's flat
`fields` slot. 143 model-side construction sites moved across 130 files
(imaging 37, group + interferometer 50, multi_galaxy + point_source 30,
guides + multi_dataset 26), together with the prose, `__Model__` bullets and
result-path docstrings that described the old container ("in its own `fields`
collection" -> "in the model's `fields` slot",
`result.instance.fields.field.shear` -> `result.instance.fields.shear`).
Deliberately unchanged:
* 170 chained `fields=<result>.model/.instance.fields` passes — shape
transparent, the free-vs-fixed distinction rides on `.model` vs `.instance`.
* 112 tracer-side `al.Tracer(..., fields=[field])` lists — tracers take a list.
* light-only `af.Collection(galaxies=...)` stages that carry no `fields=` —
none gained one.
* `markdown/` mirrors — out of scope for this change.
Reader family migrated in lockstep with the builders, so the latent keys, the
CSV column and the aggregator paths still name a real path:
`guides/results/_quick_fit.py` (latent keys `fields.shear.{magnitude,angle}`,
`instance.fields.shear.gamma_{1,2}`), `guides/results/workflow/csv_make.py`
(`add_variable(argument="fields.shear.magnitude")`) and
`guides/results/aggregator/samples.py` (tuple path `("fields","shear","gamma_1")`
and its string form).
Both subhalo sensitivity prior-reset guards in `base_model_narrow_priors_from()`
(`imaging/features/advanced/subhalo/sensitivity/slam_source_{parametric,pixelized}.py`)
moved from `hasattr(base_model.fields.field, "shear")` to
`hasattr(base_model.fields, "shear")`. Against a flat model the old guard was
False, so the re-centring silently never ran; under the new guard it fires:
gamma_1 UniformPrior(-0.3, 0.3) -> UniformPrior(0.0734, 0.1734) and
gamma_2 UniformPrior(-0.3, 0.3) -> UniformPrior(-0.1067, -0.0067) for a
max-likelihood gamma_1=0.1234, gamma_2=-0.0567 at b=0.05. The guard stays
defensive: no `fields` slot, or a field with no `shear`, still does nothing.
SEPARATELY — a pre-existing bug fix, not part of the migration:
`scripts/multi_dataset/features/one_by_one/modeling.py` "Second Dataset Offset"
re-collected only `galaxies=result.instance.galaxies`, dropping the external
shear the first fit had constrained, although its stated intent is to hold all
lens model parameters fixed to that fit (the commented-out "Mass Model Fixed"
block above it already carried `fields=result.instance.fields`). That stage now
carries `fields=result.instance.fields,`. This is a behaviour change on main,
independent of the flat form.
Equivalence witness (8 representative models, one per folder, composed from the
worktree with a byte-identical `config/`): `prior_count` unchanged everywhere
(21/14/33/20/15/12/11/29) and the prior-path sets are equal after normalising
`fields.field.` -> `fields.`; the same instance gives a bit-identical
log_likelihood under both forms (imaging: -37796.54513586828, 0xc0e2749171c0c6c4).
The `model.identifier` DOES change (e.g. imaging modeling
68b52dd173952931699fb677e93b9489 -> 38854f8a9d9cbde4fd1d27d2b54b7dc0) — that is
the intended consequence of the flat path. Existing `output/` trees are
orphaned: these scripts start new runs and do not resume the old ones.
`notebooks/`, `llms-full.txt` and `workspace_index.json` regenerated from
`scripts/` with PyAutoHands `generate.py autolens` (356 notebooks); notebooks
were never hand-edited. An AST walk of the notebook code cells matches the
scripts exactly (0 single-entry collections, 170 attr-chain / 128 bare-name /
25 flat-model / 112 list-literal on both sides, 0 per-file mismatches).
Issue: #561
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 18, 2026
Closed
Jammy2211
marked this pull request as ready for review
September 19, 2026 09:34
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.
Adopts the flat
fields=model form acrossautolens_workspace, following PyAutoLens #744 (478213e78), which made the model'sfields=slot accept a bareal.MassFieldalongside a collection.Prior paths become
fields.shear.gamma_1instead offields.field.shear.gamma_1.Companion PR: autolens_workspace_test #322 (draft), which carries the same sweep for the test workspace.
What changed
autolens_workspace(this PR) — 143 model-side construction sites across 120 files (imaging 37, group + interferometer 50, multi_galaxy + point_source 30, guides + multi_dataset 26), plus:__Model__bullets and result-path docstrings that described the old container: "in its ownfieldscollection" → "in the model'sfieldsslot";result.instance.fields.field.shear→result.instance.fields.shear.guides/results/_quick_fit.py— latent keysfields.shear.{magnitude,angle},instance.fields.shear.gamma_{1,2}guides/results/workflow/csv_make.py—add_variable(argument="fields.shear.magnitude")guides/results/aggregator/samples.py— tuple path("fields","shear","gamma_1")and its string formllms-full.txtandworkspace_index.json, rebuilt fromscripts/with PyAutoHandsgenerate.py autolens. Notebooks were never hand-edited.autolens_workspace_test(PR #322) — 66 model-side collection sites found by an AST walk; 65 migrated, 1 deliberately retained (see below).What was deliberately NOT changed
fields=<result>.{model,instance}.fieldsstages — 170 inautolens_workspace, 19 inautolens_workspace_test. Chaining is shape-transparent: it carries whatever form the parent model had, and the free-vs-fixed distinction rides on.modelvs.instance, not on the container. These work unchanged.al.Tracer(..., fields=[field])list literals — 112 and 16 respectively. Tracers take a list; this stays a list, mirroring the merged euclid pipeline reference implementation (euclid_strong_lens_modeling_pipeline#90,9cdee7b).all_fieldsin the test workspace) — a collection there is not a single-entry wrapper.af.Collection(galaxies=...)stages that carry nofields=. None gained one.misc/interop/coolest_round_trip.py— its.fields[0]reads are tracer-side list indexing, not model composition.misc/mass/galaxy_attached_legacy.py(test workspace, second model at :189). With every other model in both workspaces now flat, this is the workspaces' last regression witness that the library still accepts a collection in that slot. Only a docstring saying so was added.markdown/mirrors — out of scope for this change.The flat form intentionally changes prior paths (
fields.field.shear.*→fields.shear.*) and therefore PyAutoFit result identifiers.output/directories.Example (imaging modeling):
68b52dd173952931699fb677e93b9489→38854f8a9d9cbde4fd1d27d2b54b7dc0.Two pins in
autolens_workspace_test(PR #322):scripts/multi_galaxy/composition_mge.pyb99831e66dd27eee314113e8e58235b629f82bd3de24984b94657c328b64c3bescripts/misc/mass/galaxy_attached_legacy.py35ebe9353118bcc0c7b2d577ce2639eeThe
composition_mge.pypin was recomputed, not adjusted, from theautolens_workspace_testrepository root — the same cwd every script there runs from. An identifier is a function of the model and the active PyAutoFit configuration, so it is config- and cwd-dependent; the script's docstring now records that.Evidence
config/):prior_countunchanged everywhere (21/14/33/20/15/12/11/29); prior-path sets equal after normalisingfields.field.→fields.;model.identifierchanged (the intended consequence).-37796.54513586828(0xc0e2749171c0c6c4).autolens_workspacesmoke: 38/38 scripts + 2/2 notebooks PASS.autolens_workspace_testsmoke: 32/32 PASS (979 s,profile_smoke.yaml), includingcomposition_mge.pyand both latent integration smokes.slam_start_here.pyhand-run PASS — all 5 stages carryfields.shear, free/fixed split intact.slam_general_jax.pyhand-run — FREE stages (source_lp[1]seed,source_pix[1]andmass_total[1]via.model.fields) showfields MassField (N=2)with the seed's own prior ids 25/26 propagated; FIXED stages (source_pix[2],light[1]via.instance.fields) showfields MassField (N=0)with gamma 0.0.scripts/exactly (0 single-entry collections; 170 attr-chain / 128 bare-name / 25 flat-model / 112 list-literal on both sides).autolens_workspace_test: 103fields=sites overall (66 → 65 flat + 1 retained, 19 attr-chain, 16 tracer list-literals, 2 tracerall_fields); no file mixes the two forms;fields.fieldno longer appears anywhere, docstrings included.Local stack: PyAutoLens
478213e78, PyAutoGalaxy90e757d3, PyAutoFit7c0e79aab.The subhalo sensitivity fix
Both
hasattrguards inbase_model_narrow_priors_from()—imaging/features/advanced/subhalo/sensitivity/slam_source_{parametric,pixelized}.py— moved fromhasattr(base_model.fields.field, "shear")tohasattr(base_model.fields, "shear").Under the flat form the OLD guard silently skipped a deliberate shear prior re-centring.
base_model.fields.fieldraisesAttributeErrorinsidehasattr, which returnsFalse— no error, no log, the re-centring simply never ran.Proven to fire now, for a max-likelihood
gamma_1=0.1234, gamma_2=-0.0567atb=0.05:gamma_1:UniformPrior(-0.3, 0.3)→UniformPrior(0.0734, 0.1734)gamma_2:UniformPrior(-0.3, 0.3)→UniformPrior(-0.1067, -0.0067)The guard stays defensive: no
fieldsslot, or a field with noshear, still does nothing. These scripts remain parked inno_run.yamland out ofsmoke_tests.txt.SEPARATELY — a pre-existing bug fix, not part of the migration
This is called out in its own section so it is not read as part of the flat-form sweep.
scripts/multi_dataset/features/one_by_one/modeling.py, the "Second Dataset Offset" stage, re-collected:dropping
fieldsentirely — so a stage documented as holding all lens model parameters fixed to the first fit silently fitted with no external shear at all. The author's own commented-out "Mass Model Fixed" block directly above it already uses the correctfields=result.instance.fields.One line added:
fields=result.instance.fields,.This is a pre-existing bug on
main, independent of the flat form, and the fix was approved by the human.PyAutoHeart ack
Heart was RED at ship time. Verdict, verbatim:
Both conditions are pre-existing and unrelated to these two workspace repos.
The human was asked:
and answered:
That authorization covers push and PR-open/update only. Merge and release remain human-only and are NOT authorized by it.
This PR is therefore opened as DRAFT with
pending-release: the flat form requires PyAutoLens #744 (478213e78), which is merged but not yet on PyPI.PR #322's red CI leg is NOT this sweep
The red leg on the companion draft PR was bisected to a JAX 0.11.2 × unfixed-PyAutoGalaxy regression, not to the field migration:
raised in the zero-contour solver via the
effective_einstein_radiuslatent. The unmodifiedmainversion of the script fails identically in a CI-matching venv. It is fixed by PyAutoGalaxy90e757d3, which merged roughly 10.5 hours after that CI run, so a CI re-run should clear it. The flat form itself passes under JAX 0.11.2 on currentmains —misc/latent/latent_integration_smoke_jax.py, the very script whose leg was red, PASSES under the flat form (56.4 s), writing exactly onefiles/latent/latent_summary.jsonwith all 8 latents finite and non-zero.Cross-references
c79c8d3) — the galaxy-attached → collection migration this builds on478213e78) — the library support for the flat slot9cdee7b) — the flat-form reference implementationCloses #561
🤖 Generated with Claude Code