From 44998cf75356c2c7d5633afa33b45b97957985a6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 17:10:17 +0000 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20rectangular=20mesh=20split=20?= =?UTF-8?q?=E2=80=94=20Bilinear=20default=20(imaging),=20RTU=20advanced/in?= =?UTF-8?q?terferometer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workspace follow-up to PyAutoArray#462 (issue PyAutoArray#461): - Imaging-family examples (imaging, multi_galaxy, group, multi_dataset, guides): RectangularAdaptDensity/AdaptImage -> the new fast-CPU default RectangularBilinearAdaptDensity/AdaptImage. - Interferometer examples: -> RectangularRTUAdaptDensity/AdaptImage (pure rename of the previous kernel-CDF behaviour; RTU is the required gradient mesh on the interferometer sparse path). - Mesh-variants docs added where the pixelization examples introduce the meshes (imaging + interferometer modeling.py, likelihood_function.py, adaptive.py, READMEs) incl. the Enzi et al. 2026 (arXiv:2606.30620) RTU citation — folds in the queued Mind Enzi-citation docs draft — and the gradient guidance (os_pix >= 4 or RTU; interferometer gradients need RTU). - searches.py gradient guide: names the RTU meshes explicitly. - Prior configs: rectangular_adapt_*.yaml renamed to rectangular_rtu_adapt_*.yaml and new rectangular_bilinear_adapt_*.yaml added (same priors). Scripts and configs only — notebooks and workspace_index.json regenerate at release. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WtMqU3JfmyJh8GvB7jT4Et --- ...> rectangular_bilinear_adapt_density.yaml} | 40 +++++----- ... => rectangular_bilinear_adapt_image.yaml} | 78 +++++++++---------- .../mesh/rectangular_rtu_adapt_density.yaml | 21 +++++ .../mesh/rectangular_rtu_adapt_image.yaml | 40 ++++++++++ .../advanced/double_source_plane_lens/slam.py | 6 +- .../advanced/mass_stellar_dark/slam.py | 4 +- .../advanced/subhalo/detect/start_here.py | 4 +- .../group/features/pixelization/adaptive.py | 10 +-- .../pixelization/cpu_fast_modeling.py | 4 +- scripts/group/features/pixelization/slam.py | 8 +- .../features/pixelization/source_science.py | 2 +- scripts/group/slam.py | 2 +- scripts/guides/modeling/searches.py | 11 ++- scripts/guides/modeling/slam_start_here.py | 8 +- scripts/guides/results/start_here.py | 2 +- .../double_source_plane_lens/chaining.py | 8 +- .../advanced/double_source_plane_lens/plot.py | 4 +- .../advanced/double_source_plane_lens/slam.py | 8 +- .../advanced/mass_stellar_dark/slam.py | 4 +- .../advanced/subhalo/detect/start_here.py | 4 +- .../sensitivity/slam_source_pixelized.py | 4 +- .../imaging/features/extra_galaxies/slam.py | 4 +- .../linear_light_profiles/modeling.py | 2 +- .../features/linear_light_profiles/slam.py | 4 +- .../imaging/features/no_lens_light/slam.py | 6 +- .../imaging/features/pixelization/README.md | 10 +++ .../imaging/features/pixelization/adaptive.py | 21 +++-- .../pixelization/cpu_fast_modeling.py | 8 +- scripts/imaging/features/pixelization/fit.py | 4 +- .../pixelization/likelihood_function.py | 16 +++- .../imaging/features/pixelization/modeling.py | 23 +++++- scripts/imaging/features/pixelization/plot.py | 4 +- scripts/imaging/features/pixelization/slam.py | 8 +- .../features/pixelization/source_science.py | 6 +- .../imaging/features/scaling_relation/slam.py | 4 +- .../advanced/subhalo/detect/start_here.py | 4 +- .../features/datacube/README.md | 2 +- .../features/datacube/delaunay.py | 2 +- .../features/datacube/likelihood_function.py | 18 ++--- .../features/datacube/modeling.py | 8 +- .../features/datacube/start_here.py | 8 +- .../features/extra_galaxies/slam.py | 4 +- .../linear_light_profiles/modeling.py | 2 +- .../features/linear_light_profiles/slam.py | 4 +- .../features/multi_gaussian_expansion/slam.py | 4 +- .../features/pixelization/README.md | 9 +++ .../features/pixelization/fit.py | 2 +- .../pixelization/likelihood_function.py | 12 +-- .../features/pixelization/modeling.py | 20 ++++- .../features/pixelization/slam.py | 4 +- .../features/pixelization/source_science.py | 6 +- .../features/scaling_relation/slam.py | 4 +- .../features/pixelization/modeling.py | 2 +- .../features/slam/independent.py | 10 +-- .../features/slam/simultaneous.py | 4 +- .../advanced/double_source_plane_lens/slam.py | 4 +- .../advanced/mass_stellar_dark/slam.py | 4 +- .../advanced/subhalo/detect/start_here.py | 4 +- .../features/extra_galaxies/slam.py | 4 +- .../features/linear_light_profiles/slam.py | 4 +- .../features/multi_gaussian_expansion/slam.py | 4 +- .../features/no_lens_light/slam.py | 4 +- .../features/pixelization/README.md | 2 +- .../features/pixelization/adaptive.py | 8 +- .../pixelization/cpu_fast_modeling.py | 4 +- .../features/pixelization/modeling.py | 6 +- .../features/pixelization/plot.py | 2 +- .../features/pixelization/slam.py | 16 ++-- .../features/pixelization/source_science.py | 2 +- .../features/scaling_relation/slam.py | 4 +- scripts/multi_galaxy/slam.py | 4 +- 71 files changed, 364 insertions(+), 233 deletions(-) rename config/priors/mesh/{rectangular_adapt_density.yaml => rectangular_bilinear_adapt_density.yaml} (88%) rename config/priors/mesh/{rectangular_adapt_image.yaml => rectangular_bilinear_adapt_image.yaml} (91%) create mode 100644 config/priors/mesh/rectangular_rtu_adapt_density.yaml create mode 100644 config/priors/mesh/rectangular_rtu_adapt_image.yaml diff --git a/config/priors/mesh/rectangular_adapt_density.yaml b/config/priors/mesh/rectangular_bilinear_adapt_density.yaml similarity index 88% rename from config/priors/mesh/rectangular_adapt_density.yaml rename to config/priors/mesh/rectangular_bilinear_adapt_density.yaml index e78308a33..903f96378 100644 --- a/config/priors/mesh/rectangular_adapt_density.yaml +++ b/config/priors/mesh/rectangular_bilinear_adapt_density.yaml @@ -1,21 +1,21 @@ -RectangularAdaptDensity: - shape_0: - type: Uniform - lower_limit: 20.0 - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 - limits: - lower: 3.0 - upper: inf - shape_1: - type: Uniform - lower_limit: 20.0 - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 - limits: - lower: 3.0 +RectangularBilinearAdaptDensity: + shape_0: + type: Uniform + lower_limit: 20.0 + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + limits: + lower: 3.0 + upper: inf + shape_1: + type: Uniform + lower_limit: 20.0 + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + limits: + lower: 3.0 upper: inf \ No newline at end of file diff --git a/config/priors/mesh/rectangular_adapt_image.yaml b/config/priors/mesh/rectangular_bilinear_adapt_image.yaml similarity index 91% rename from config/priors/mesh/rectangular_adapt_image.yaml rename to config/priors/mesh/rectangular_bilinear_adapt_image.yaml index e8878be7d..f147e0ecd 100644 --- a/config/priors/mesh/rectangular_adapt_image.yaml +++ b/config/priors/mesh/rectangular_bilinear_adapt_image.yaml @@ -1,40 +1,40 @@ -RectangularAdaptImage: - shape_0: - type: Uniform - lower_limit: 20.0 - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 - limits: - lower: 3.0 - upper: inf - shape_1: - type: Uniform - lower_limit: 20.0 - upper_limit: 45.0 - width_modifier: - type: Absolute - value: 8.0 - limits: - lower: 3.0 - upper: inf - weight_power: - type : Uniform - lower_limit: 0.0 - upper_limit: 10.0 - width_modifier: - type: Absolute - value: 2.0 - limits: - lower: -100.0 - upper: 100.0 - weight_floor: - type: LogUniform - lower_limit: 0.00001 - upper_limit: 1.0 - width_modifier: - type: Absolute - limits: - lower: 0.0 +RectangularBilinearAdaptImage: + shape_0: + type: Uniform + lower_limit: 20.0 + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + limits: + lower: 3.0 + upper: inf + shape_1: + type: Uniform + lower_limit: 20.0 + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + limits: + lower: 3.0 + upper: inf + weight_power: + type : Uniform + lower_limit: 0.0 + upper_limit: 10.0 + width_modifier: + type: Absolute + value: 2.0 + limits: + lower: -100.0 + upper: 100.0 + weight_floor: + type: LogUniform + lower_limit: 0.00001 + upper_limit: 1.0 + width_modifier: + type: Absolute + limits: + lower: 0.0 upper: inf \ No newline at end of file diff --git a/config/priors/mesh/rectangular_rtu_adapt_density.yaml b/config/priors/mesh/rectangular_rtu_adapt_density.yaml new file mode 100644 index 000000000..2bfdb4968 --- /dev/null +++ b/config/priors/mesh/rectangular_rtu_adapt_density.yaml @@ -0,0 +1,21 @@ +RectangularRTUAdaptDensity: + shape_0: + type: Uniform + lower_limit: 20.0 + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + limits: + lower: 3.0 + upper: inf + shape_1: + type: Uniform + lower_limit: 20.0 + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + limits: + lower: 3.0 + upper: inf \ No newline at end of file diff --git a/config/priors/mesh/rectangular_rtu_adapt_image.yaml b/config/priors/mesh/rectangular_rtu_adapt_image.yaml new file mode 100644 index 000000000..c8ae45d63 --- /dev/null +++ b/config/priors/mesh/rectangular_rtu_adapt_image.yaml @@ -0,0 +1,40 @@ +RectangularRTUAdaptImage: + shape_0: + type: Uniform + lower_limit: 20.0 + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + limits: + lower: 3.0 + upper: inf + shape_1: + type: Uniform + lower_limit: 20.0 + upper_limit: 45.0 + width_modifier: + type: Absolute + value: 8.0 + limits: + lower: 3.0 + upper: inf + weight_power: + type : Uniform + lower_limit: 0.0 + upper_limit: 10.0 + width_modifier: + type: Absolute + value: 2.0 + limits: + lower: -100.0 + upper: 100.0 + weight_floor: + type: LogUniform + lower_limit: 0.00001 + upper_limit: 1.0 + width_modifier: + type: Absolute + limits: + lower: 0.0 + upper: inf \ No newline at end of file diff --git a/scripts/group/features/advanced/double_source_plane_lens/slam.py b/scripts/group/features/advanced/double_source_plane_lens/slam.py index 2f85d87ad..9af569c40 100644 --- a/scripts/group/features/advanced/double_source_plane_lens/slam.py +++ b/scripts/group/features/advanced/double_source_plane_lens/slam.py @@ -696,7 +696,7 @@ def source_pix_2( source_lp_result_1=source_lp_result_1, source_lp_result_2=source_lp_result_2, redshift_source_1=redshift_source_1, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -705,7 +705,7 @@ def source_pix_2( dataset=dataset, source_lp_result_2=source_lp_result_2, source_pix_result_1_source_0=source_pix_result_1_source_0, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -715,6 +715,6 @@ def source_pix_2( source_lp_result_2=source_lp_result_2, source_pix_result_1_source_0=source_pix_result_1_source_0, source_pix_result_1_source_1=source_pix_result_1_source_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/group/features/advanced/mass_stellar_dark/slam.py b/scripts/group/features/advanced/mass_stellar_dark/slam.py index 9b1be30f9..bb65eec13 100644 --- a/scripts/group/features/advanced/mass_stellar_dark/slam.py +++ b/scripts/group/features/advanced/mass_stellar_dark/slam.py @@ -537,7 +537,7 @@ def mass_light_dark( settings_search=settings_search, dataset=dataset, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -556,7 +556,7 @@ def mass_light_dark( dataset=dataset, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/group/features/advanced/subhalo/detect/start_here.py b/scripts/group/features/advanced/subhalo/detect/start_here.py index 0db8d6cb8..688194ae5 100644 --- a/scripts/group/features/advanced/subhalo/detect/start_here.py +++ b/scripts/group/features/advanced/subhalo/detect/start_here.py @@ -808,7 +808,7 @@ def subhalo_refine( settings_search=settings_search, dataset=dataset, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -830,7 +830,7 @@ def subhalo_refine( dataset=dataset, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/group/features/pixelization/adaptive.py b/scripts/group/features/pixelization/adaptive.py index 27fb6de63..7675e62b8 100644 --- a/scripts/group/features/pixelization/adaptive.py +++ b/scripts/group/features/pixelization/adaptive.py @@ -28,7 +28,7 @@ Two key adaptive classes are used: - - `RectangularAdaptImage` mesh: adapts the rectangular source-pixel upsampling to the source's unlensed + - `RectangularBilinearAdaptImage` mesh: adapts the rectangular source-pixel upsampling to the source's unlensed morphology. More rectangular pixels are placed where the source is located, even in low magnification regions. @@ -232,7 +232,7 @@ pixelization = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant, ) @@ -266,7 +266,7 @@ Search 3 uses the adaptive pixelization classes: - - `RectangularAdaptImage` mesh: adapts pixel density to the source morphology. + - `RectangularBilinearAdaptImage` mesh: adapts pixel density to the source morphology. - `Adapt` regularization: adapts smoothing strength to the source brightness. The lens mass is fixed from search 2 to ensure the adaptation is performed quickly. @@ -290,7 +290,7 @@ pixelization_3 = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptImage(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptImage(shape=mesh_shape), regularization=al.reg.Adapt, ) @@ -399,7 +399,7 @@ Key points: - Adaptive pixelizations are set up via search chaining: parametric fit -> constant pixelization -> adaptive. - The adapt_data is the lens-subtracted image, so accurate light modeling of ALL group galaxies is essential. - - `RectangularAdaptImage` concentrates source pixels where the source is brightest. + - `RectangularBilinearAdaptImage` concentrates source pixels where the source is brightest. - `Adapt` regularization varies smoothing based on source brightness. - The SLaM pipeline (see `group/features/pixelization/slam.py`) automates this entire process. """ diff --git a/scripts/group/features/pixelization/cpu_fast_modeling.py b/scripts/group/features/pixelization/cpu_fast_modeling.py index 579730bcb..e9cedf752 100644 --- a/scripts/group/features/pixelization/cpu_fast_modeling.py +++ b/scripts/group/features/pixelization/cpu_fast_modeling.py @@ -196,7 +196,7 @@ ), ) -mesh = al.mesh.RectangularAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) @@ -284,7 +284,7 @@ pix = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant, ) diff --git a/scripts/group/features/pixelization/slam.py b/scripts/group/features/pixelization/slam.py index f956dabf1..8e64c7858 100644 --- a/scripts/group/features/pixelization/slam.py +++ b/scripts/group/features/pixelization/slam.py @@ -285,7 +285,7 @@ def source_lp_1( rectangular adaptive pixelization with `Adapt` regularization. Key pixelization choices: - - `al.mesh.RectangularAdaptDensity`: a rectangular mesh whose cell density adapts to the source brightness. + - `al.mesh.RectangularBilinearAdaptDensity`: a rectangular mesh whose cell density adapts to the source brightness. - Fixed `mesh_shape = (28, 28)` pixels, chosen to match data resolution. - `al.reg.Adapt`: adaptive regularization that varies smoothing based on source brightness. Rectangular meshes use `Adapt` (not `AdaptSplit`, which is reserved for irregular meshes). @@ -359,10 +359,10 @@ def source_pix_1( else None ) - # Source: RectangularAdaptDensity mesh with Adapt regularization. + # Source: RectangularBilinearAdaptDensity mesh with Adapt regularization. pixelization = af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=af.Model(al.reg.Adapt), ) @@ -435,7 +435,7 @@ def source_pix_2( pixelization = af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=af.Model(al.reg.Adapt), ) diff --git a/scripts/group/features/pixelization/source_science.py b/scripts/group/features/pixelization/source_science.py index 50d126b57..c60bd4404 100644 --- a/scripts/group/features/pixelization/source_science.py +++ b/scripts/group/features/pixelization/source_science.py @@ -153,7 +153,7 @@ mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh = al.mesh.RectangularAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) diff --git a/scripts/group/slam.py b/scripts/group/slam.py index 9dd3f9c86..eab96163c 100644 --- a/scripts/group/slam.py +++ b/scripts/group/slam.py @@ -424,7 +424,7 @@ def source_lp_1( __SOURCE PIX PIPELINE 1__ Equivalent to `source_pix_1` in `slam_start_here.py`, except a Hilbert image mesh is used -instead of `RectangularAdaptDensity`, with edge-point padding of 30 pixels. The Hilbert pixel +instead of `RectangularBilinearAdaptDensity`, with edge-point padding of 30 pixels. The Hilbert pixel count is set by `al.model_util.hilbert_pixels_from_pixel_scale`. Pixelization over-sampling is signal-adaptive: pixels above the S/N threshold use sub-size 4, diff --git a/scripts/guides/modeling/searches.py b/scripts/guides/modeling/searches.py index 47f3949ca..e138facc3 100644 --- a/scripts/guides/modeling/searches.py +++ b/scripts/guides/modeling/searches.py @@ -153,10 +153,13 @@ is often found late, and a long likelihood plateau is a regularization mode, not convergence. `batch_size` (e.g. 4) bounds the memory of the batched gradient, which pixelized models need. -For the rectangular kernel-CDF meshes one further setting matters: a sharp `bandwidth` (e.g. 0.1) gives the mesh -narrow gradient support that can stall descent, whereas the default `bandwidth=1.0` searches on a smoother -likelihood at only a small cost in final fit quality — if a rectangular-mesh search stalls, search at the default -bandwidth first and only sharpen afterwards. +Gradient-based searches on rectangular meshes must use the kernel-CDF `RectangularRTUAdaptDensity` / +`RectangularRTUAdaptImage` meshes (or set `over_sample_size_pixelization >= 4`): the default +`RectangularBilinearAdaptDensity` mesh's rank-CDF likelihood is exactly piecewise-constant in the mass model at +the default over-sampling, so its gradients are identically zero. For the RTU meshes one further setting +matters: a sharp `bandwidth` (e.g. 0.1) gives the mesh narrow gradient support that can stall descent, whereas +the default `bandwidth=1.0` searches on a smoother likelihood at only a small cost in final fit quality — if a +rectangular-mesh search stalls, search at the default bandwidth first and only sharpen afterwards. Because it manages its own broad starting points, this search does not use the start-point API described below. Like all optimizers it returns a single best-fit lens model, not a posterior with errors, so `Nautilus` above remains the diff --git a/scripts/guides/modeling/slam_start_here.py b/scripts/guides/modeling/slam_start_here.py index c906a106b..6ea4cf56f 100644 --- a/scripts/guides/modeling/slam_start_here.py +++ b/scripts/guides/modeling/slam_start_here.py @@ -66,7 +66,7 @@ The SLaM workflow is flexible—you can swap MGE light profiles for other light models if desired. Models set up in -earlier pipelines guide those used in later ones. For example, if the Source Pipeline uses a `RectangularAdaptDensity` +earlier pipelines guide those used in later ones. For example, if the Source Pipeline uses a `RectangularBilinearAdaptDensity` mesh, the same mesh type is carried into later pipelines for consistency. __Design Choices__ @@ -294,7 +294,7 @@ def source_pix_1( The second search of the SOURCE PIX PIPELINE fits the final pixelized source model using the improved adapt images computed from search 1's pixelized source reconstruction. -The `RectangularAdaptImage` mesh and `Adapt` regularization adapt the source pixels and regularization +The `RectangularBilinearAdaptImage` mesh and `Adapt` regularization adapt the source pixels and regularization weights to the source's morphology using the high-quality adapt images from search 1. """ @@ -608,7 +608,7 @@ def mass_total( settings_search=settings_search, dataset=dataset, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -617,7 +617,7 @@ def mass_total( dataset=dataset, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/guides/results/start_here.py b/scripts/guides/results/start_here.py index 8002f2742..654cf4e14 100644 --- a/scripts/guides/results/start_here.py +++ b/scripts/guides/results/start_here.py @@ -631,7 +631,7 @@ The example script `autolens_workspace/*/imaging/features/pixelization/fit.py` describes using pixelization results including: - - Producing source reconstructions using the Voronoi mesh, RectangularAdaptDensity triangulation or whichever mesh is used. + - Producing source reconstructions using the Voronoi mesh, RectangularBilinearAdaptDensity triangulation or whichever mesh is used. - Inspecting the evidence terms of the fit, which quantify how well the pixelization reconstructs fits the data whilst accounting for the complexity of the pixelization. - Estimating the magnification of the source galaxy's image using the pixelization. diff --git a/scripts/imaging/features/advanced/double_source_plane_lens/chaining.py b/scripts/imaging/features/advanced/double_source_plane_lens/chaining.py index dea7cbd4f..7317ff066 100644 --- a/scripts/imaging/features/advanced/double_source_plane_lens/chaining.py +++ b/scripts/imaging/features/advanced/double_source_plane_lens/chaining.py @@ -672,9 +672,9 @@ - The lens galaxy's light is an MGE bulge [Parameters fixed to results of search 4]. - The lens galaxy's total mass distribution is again an `Isothermal` [Parameters fixed to results of search 4]. - The first source galaxy's mass is a `IsothermalSph` [Parameters fixed to results of search 4]. - - The first source-galaxy's light uses an `Overlay` image-mesh, `RectangularAdaptDensity` mesh and `Constant` regularization + - The first source-galaxy's light uses an `Overlay` image-mesh, `RectangularBilinearAdaptDensity` mesh and `Constant` regularization scheme [3 parameters]. - - The second source-galaxy's light uses an `Overlay` image-mesh, `RectangularAdaptDensity` mesh and `Constant` regularization + - The second source-galaxy's light uses an `Overlay` image-mesh, `RectangularBilinearAdaptDensity` mesh and `Constant` regularization scheme [3 parameters]. The number of free parameters and therefore the dimensionality of non-linear parameter space is N=6. @@ -686,7 +686,7 @@ mass=result_4.instance.galaxies.source_0.mass, pixelization=af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant, ), ) @@ -695,7 +695,7 @@ redshift=2.0, pixelization=af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant, ), ) diff --git a/scripts/imaging/features/advanced/double_source_plane_lens/plot.py b/scripts/imaging/features/advanced/double_source_plane_lens/plot.py index 75379b864..ee4136afc 100644 --- a/scripts/imaging/features/advanced/double_source_plane_lens/plot.py +++ b/scripts/imaging/features/advanced/double_source_plane_lens/plot.py @@ -147,7 +147,7 @@ source_galaxy_0 = al.Galaxy( redshift=1.0, pixelization=al.Pixelization( - mesh=al.mesh.RectangularAdaptDensity(shape=(24, 24)), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=(24, 24)), regularization=al.reg.Constant(coefficient=1.0), ), ) @@ -155,7 +155,7 @@ source_galaxy_1 = al.Galaxy( redshift=2.0, pixelization=al.Pixelization( - mesh=al.mesh.RectangularAdaptDensity(shape=(24, 24)), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=(24, 24)), regularization=al.reg.Constant(coefficient=1.0), ), ) diff --git a/scripts/imaging/features/advanced/double_source_plane_lens/slam.py b/scripts/imaging/features/advanced/double_source_plane_lens/slam.py index edd46b1f0..484321777 100644 --- a/scripts/imaging/features/advanced/double_source_plane_lens/slam.py +++ b/scripts/imaging/features/advanced/double_source_plane_lens/slam.py @@ -378,7 +378,7 @@ def source_pix_1_source_1( Lens mass, shear and `source_0`'s mass are all fixed to the maximum-likelihood instances of the previous pixelized searches; only the pixelization regularization parameters are free. -The `RectangularAdaptImage` (or equivalent) mesh uses the high-quality adapt images built up over the earlier +The `RectangularBilinearAdaptImage` (or equivalent) mesh uses the high-quality adapt images built up over the earlier pipeline stages to adapt each source-plane pixelization to its reconstructed morphology. """ @@ -565,7 +565,7 @@ def source_pix_2( source_lp_result_1=source_lp_result_1, source_lp_result_2=source_lp_result_2, redshift_source_1=redshift_source_1, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -574,7 +574,7 @@ def source_pix_2( dataset=dataset, source_lp_result_2=source_lp_result_2, source_pix_result_1_source_0=source_pix_result_1_source_0, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -584,6 +584,6 @@ def source_pix_2( source_lp_result_2=source_lp_result_2, source_pix_result_1_source_0=source_pix_result_1_source_0, source_pix_result_1_source_1=source_pix_result_1_source_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/imaging/features/advanced/mass_stellar_dark/slam.py b/scripts/imaging/features/advanced/mass_stellar_dark/slam.py index 9ce12044f..b882f4f1b 100644 --- a/scripts/imaging/features/advanced/mass_stellar_dark/slam.py +++ b/scripts/imaging/features/advanced/mass_stellar_dark/slam.py @@ -507,7 +507,7 @@ def mass_light_dark( settings_search=settings_search, dataset=dataset, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -529,7 +529,7 @@ def mass_light_dark( dataset=dataset, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/imaging/features/advanced/subhalo/detect/start_here.py b/scripts/imaging/features/advanced/subhalo/detect/start_here.py index 4f3d8bb03..11c3bffd1 100644 --- a/scripts/imaging/features/advanced/subhalo/detect/start_here.py +++ b/scripts/imaging/features/advanced/subhalo/detect/start_here.py @@ -684,7 +684,7 @@ def subhalo_refine( settings_search=settings_search, dataset=dataset, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -706,7 +706,7 @@ def subhalo_refine( dataset=dataset, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/imaging/features/advanced/subhalo/sensitivity/slam_source_pixelized.py b/scripts/imaging/features/advanced/subhalo/sensitivity/slam_source_pixelized.py index a17e7d19c..c1fa4a144 100644 --- a/scripts/imaging/features/advanced/subhalo/sensitivity/slam_source_pixelized.py +++ b/scripts/imaging/features/advanced/subhalo/sensitivity/slam_source_pixelized.py @@ -175,7 +175,7 @@ def source_pix_1( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -241,7 +241,7 @@ def source_pix_2( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/scripts/imaging/features/extra_galaxies/slam.py b/scripts/imaging/features/extra_galaxies/slam.py index 4990cd7f3..aa8b7e1bc 100644 --- a/scripts/imaging/features/extra_galaxies/slam.py +++ b/scripts/imaging/features/extra_galaxies/slam.py @@ -220,7 +220,7 @@ def source_pix_1( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -281,7 +281,7 @@ def source_pix_2( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/scripts/imaging/features/linear_light_profiles/modeling.py b/scripts/imaging/features/linear_light_profiles/modeling.py index 843944554..9b41b33c3 100644 --- a/scripts/imaging/features/linear_light_profiles/modeling.py +++ b/scripts/imaging/features/linear_light_profiles/modeling.py @@ -365,7 +365,7 @@ many light profiles combined in a `Basis` (e.g. `lp_basis.Basis`). - `Mapper`: The linear object used by a `Pixelization` to reconstruct data via an `Inversion`. The `Mapper` - is specific to the `Pixelization`'s `Mesh` (e.g. a `RectangularMapper` is used for a `RectangularAdaptDensity` + is specific to the `Pixelization`'s `Mesh` (e.g. a `RectangularMapper` is used for a `RectangularBilinearAdaptDensity` mesh). In this example, the model uses one linear `Sersic` for the lens galaxy's bulge and one linear `SersicCore` diff --git a/scripts/imaging/features/linear_light_profiles/slam.py b/scripts/imaging/features/linear_light_profiles/slam.py index f4a115118..64ffe01a5 100644 --- a/scripts/imaging/features/linear_light_profiles/slam.py +++ b/scripts/imaging/features/linear_light_profiles/slam.py @@ -467,7 +467,7 @@ def mass_total( settings_search=settings_search, dataset=dataset, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -476,7 +476,7 @@ def mass_total( dataset=dataset, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/imaging/features/no_lens_light/slam.py b/scripts/imaging/features/no_lens_light/slam.py index cdcf4d932..785963e30 100644 --- a/scripts/imaging/features/no_lens_light/slam.py +++ b/scripts/imaging/features/no_lens_light/slam.py @@ -42,7 +42,7 @@ - The lens galaxy's light is omitted from the data and model. - The lens galaxy's total mass distribution is an `PowerLaw`. - - The source galaxy is reconstructed using a `RectangularAdaptImage` mesh and `Adapt` regularization scheme. + - The source galaxy is reconstructed using a `RectangularBilinearAdaptImage` mesh and `Adapt` regularization scheme. __Start Here Notebook__ @@ -382,7 +382,7 @@ def mass_total( settings_search=settings_search, dataset=dataset, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -391,7 +391,7 @@ def mass_total( dataset=dataset, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/imaging/features/pixelization/README.md b/scripts/imaging/features/pixelization/README.md index 99603fae7..d02a16a7d 100644 --- a/scripts/imaging/features/pixelization/README.md +++ b/scripts/imaging/features/pixelization/README.md @@ -14,6 +14,16 @@ The following example scripts illustrating lens modeling where: - `slam`: Using the Source, Light and Mass (SLAM) pipeline to perform lens modeling using pixelized source reconstruction. - `delaunay`: Using a Delaunay mesh (instead of a rectangular mesh) for the source reconstruction. +# Rectangular Mesh Variants + +The default adaptive rectangular mesh is `RectangularBilinearAdaptDensity` (with `RectangularBilinearAdaptImage` +its adapt-image counterpart): it warps the grid via the empirical rank CDF of the traced points — no extra +parameters and the fastest rectangular mesh on CPUs. The advanced `RectangularRTUAdaptDensity` / +`RectangularRTUAdaptImage` meshes use a smooth kernel-density CDF instead — the ray-guided transformed uniform +(RTU) grid formulation of Enzi et al. (2026), https://arxiv.org/abs/2606.30620, which should be cited when using +them. Use RTU on GPUs, for gradient-based (JAX) samplers (the Bilinear likelihood has zero gradients at the +default `over_sample_size_pixelization=1` — set it >= 4 or use RTU), and for interferometer gradient fitting. + # Results These scripts only give a brief overview of how to analyse and interpret the results a lens model fit. diff --git a/scripts/imaging/features/pixelization/adaptive.py b/scripts/imaging/features/pixelization/adaptive.py index 161403239..afeb539bd 100644 --- a/scripts/imaging/features/pixelization/adaptive.py +++ b/scripts/imaging/features/pixelization/adaptive.py @@ -15,13 +15,20 @@ calculate and pass the multiple image-plane positions of the lensed source to later searches, which resamples bad mass models removing demagnified source reconstructions. -This script illustrates using the `RectangularAdaptImage` mesh and `Adapt` regularization +This script illustrates using the `RectangularBilinearAdaptImage` mesh and `Adapt` regularization scheme to adapt the source reconstruction to the source galaxy's morphology (as opposed to the methods used in other examplesw hich adapt to the mass model magnification and apply a constant regularization scheme). This script illustrates the API used for adaptive pixelizations, but does not go into the details of how they work. This is described in chapter 3 of the **HowToLens** lectures (tutorials 11 and 12). +The `RectangularBilinear` meshes used here are the default (fast CPU) adaptive rectangular meshes, which warp +the grid via the empirical rank CDF of the traced points. Their advanced counterparts `RectangularRTUAdaptDensity` / +`RectangularRTUAdaptImage` use a smooth kernel-density CDF instead — the ray-guided transformed uniform (RTU) +grid formulation of Enzi et al. (2026), https://arxiv.org/abs/2606.30620, which should be cited when using them. +The RTU meshes are recommended on GPUs and for gradient-based (JAX) samplers (the Bilinear likelihood has zero +gradients at the default `over_sample_size_pixelization=1`; set it >= 4 or use RTU). + __Why Chain?__ There are a number of benefits of chaining a linear source model and a pixelized source, as opposed to fitting the @@ -49,7 +56,7 @@ - **Mesh Shape:** As discussed in the `features/pixelization/modeling` example, the mesh shape is fixed before modeling. - **Analysis + Position Likelihood:** We add a penalty term to the likelihood function, which penalizes models where the brightest multiple images of the lensed source galaxy do not trace close to one another in the source plane. - **Brief Description:** In this example we update the positions between searches, where the positions correspond to the (y,x) locations of the lensed source's multiple images. -- **Adaptive Pixelization:** Search 3 uses two adaptive pixelization classes: `RectangularAdaptImage` mesh and `Adapt` regularization, which adapt the source reconstruction to the source galaxy's morphology. +- **Adaptive Pixelization:** Search 3 uses two adaptive pixelization classes: `RectangularBilinearAdaptImage` mesh and `Adapt` regularization, which adapt the source reconstruction to the source galaxy's morphology. - **Adapt Images:** When we create the analysis, we pass it an `adapt_images`, which contains a dictionary mapping each galaxy name to the corresponding lens subtracted image of the source galaxy from the result of a previous search. - **SLaM Pipelines:** The API above allows you to write modeling code using adaptive features yourself, but it is recommended you use the Source, Light and Mass (SLaM) pipeline. @@ -231,7 +238,7 @@ - The source galaxy's light uses no image-mesh (only used for Delaunay meshes) [0 parameters]. - - The source-galaxy's light uses a 20 x 20 `RectangularAdaptDensity` mesh [0 parameters]. + - The source-galaxy's light uses a 20 x 20 `RectangularBilinearAdaptDensity` mesh [0 parameters]. - This pixelization is regularized using a `Constant` scheme [1 parameter]. @@ -245,7 +252,7 @@ pixelization = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant, ) @@ -319,7 +326,7 @@ Search 3 uses two adaptive pixelization classes that have not been used elsewhere in the workspace: - - `RectangularAdaptImage` mesh: adapts the rectangular source-pixel upsampling to the source's unlensed morphology. This + - `RectangularBilinearAdaptImage` mesh: adapts the rectangular source-pixel upsampling to the source's unlensed morphology. This means that more rectangular pixels will be used where the source is located, even if its far away from the caustic and therefore in lower magnification regions. @@ -351,7 +358,7 @@ - The source galaxy's light uses no image-mesh (only used for Delaunay meshes) [0 parameters]. - - The source-galaxy's light uses a 20 x 20 `RectangularAdaptImage` mesh [0 parameters]. + - The source-galaxy's light uses a 20 x 20 `RectangularBilinearAdaptImage` mesh [0 parameters]. - This pixelization is regularized using a `Adapt` scheme [2 parameter]. @@ -365,7 +372,7 @@ pixelization = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptImage(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptImage(shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/imaging/features/pixelization/cpu_fast_modeling.py b/scripts/imaging/features/pixelization/cpu_fast_modeling.py index b6e57de9f..3750afd47 100644 --- a/scripts/imaging/features/pixelization/cpu_fast_modeling.py +++ b/scripts/imaging/features/pixelization/cpu_fast_modeling.py @@ -177,7 +177,7 @@ Below, we perform a similar fit using the **same pixelization**, but this time accelerated on the **CPU** using `numba` and sparse operations. """ -mesh = al.mesh.RectangularAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) @@ -226,7 +226,7 @@ pixelization = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant, ) @@ -346,7 +346,7 @@ def source_pix_1( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -391,7 +391,7 @@ def source_pix_2( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/scripts/imaging/features/pixelization/fit.py b/scripts/imaging/features/pixelization/fit.py index 4d57eb5d1..015bb3417 100644 --- a/scripts/imaging/features/pixelization/fit.py +++ b/scripts/imaging/features/pixelization/fit.py @@ -208,7 +208,7 @@ reconstruction solution by penalizing solutions where neighboring pixels have large flux differences. """ -mesh = al.mesh.RectangularAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) @@ -460,7 +460,7 @@ __Simulated Imaging__ We load the source galaxy image from the pixelized inversion of a previous fit, which was performed on an irregular -RectangularAdaptDensity. +RectangularBilinearAdaptDensity. Since irregular meshes cannot be directly used to simulate lensed images, we interpolate the source onto a uniform grid with shape `interpolated_pixelized_shape`. This grid should have a high resolution (e.g., 1000 × 1000) to preserve diff --git a/scripts/imaging/features/pixelization/likelihood_function.py b/scripts/imaging/features/pixelization/likelihood_function.py index adb307b21..97759e412 100644 --- a/scripts/imaging/features/pixelization/likelihood_function.py +++ b/scripts/imaging/features/pixelization/likelihood_function.py @@ -45,7 +45,7 @@ __Simplifications__ This example uses a `RectangularUniform` mesh, where all rectangular source pixels have the same size. Most -pixelization examples use a `RectangularAdaptDensity` mesh, which adapts the size of source pixels to the +pixelization examples use a `RectangularBilinearAdaptDensity` mesh, which adapts the size of source pixels to the density of points in the source-plane (e.g. the caustic). The `RectangularUniform` mesh is used here because it is simpler to explain the likelihood function @@ -471,14 +471,24 @@ We can briefly consider how this step differs for other mesh types. Above, we simply overlaid a uniform rectangular grid to define the source pixel centres and then mapped image pixels to these source pixels. -The `RectangularAdaptDensity` mesh pretty much works exactly the same, its just that a calculation (which we don't +The `RectangularBilinearAdaptDensity` mesh pretty much works exactly the same, its just that a calculation (which we don't describe here) works out how to make a grid of rectangular pixels that adapt to the source-plane density and thus vary in size. -There is also a `RectangularAdaptImage` mesh which uses the image of the lensed source to adapt +There is also a `RectangularBilinearAdaptImage` mesh which uses the image of the lensed source to adapt the rectangular pixel sizes. This often puts even smaller pixels in the brightest regions of the source, even if it lies offset or away from the caustic. +The adaptive rectangular meshes come in two variants which differ only in the transform used to warp the +uniform grid: the default `RectangularBilinear` meshes use the empirical rank CDF of the traced points (a sort +and a cumulative sum — no extra parameters, fastest on CPUs), whereas the advanced `RectangularRTU` meshes use +a smooth kernel-density CDF — the ray-guided transformed uniform (RTU) grid formulation of Enzi et al. (2026), +https://arxiv.org/abs/2606.30620, which should be cited when using those meshes (the paper pairs the RTU grid +with a Gaussian-process source prior, whereas these examples use PyAutoLens's own regularization schemes such +as `reg.Constant` / `reg.Adapt`). The RTU meshes are recommended on GPUs and are required for gradient-based +(JAX) samplers at the default `over_sample_size_pixelization=1`, where the Bilinear likelihood is exactly +piecewise-constant (zero gradients) — gradient users set `over_sample_size_pixelization >= 4` or use RTU. + There is also a `Delaunay` mesh which uses a Delaunay triangulation to define an irregular grid of source pixels. This is described fully in the `delaunay` example including a likelihood function guide. diff --git a/scripts/imaging/features/pixelization/modeling.py b/scripts/imaging/features/pixelization/modeling.py index 3f4759d8e..b5dcd22b3 100644 --- a/scripts/imaging/features/pixelization/modeling.py +++ b/scripts/imaging/features/pixelization/modeling.py @@ -114,9 +114,26 @@ - The lens galaxy's light is omitted (and is not present in the simulated data). - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear`. - - The source galaxy's surface-brightness is reconstructed using a `RectangularAdaptDensity` mesh + - The source galaxy's surface-brightness is reconstructed using a `RectangularBilinearAdaptDensity` mesh and `Constant` regularization scheme. +__Rectangular Mesh Variants__ + +The `RectangularBilinearAdaptDensity` mesh used here is the default adaptive rectangular mesh: it adapts the +size of source pixels to the density of traced points via the empirical rank CDF of their coordinates — a +conceptually simple transform (a sort and a cumulative sum) with no extra parameters, which is why it is also +the fastest rectangular mesh on CPUs. + +An advanced alternative is the `RectangularRTUAdaptDensity` mesh (and its `RectangularRTUAdaptImage` +counterpart), which instead uses a smooth kernel-density CDF — the ray-guided transformed uniform (RTU) grid +formulation of Enzi et al. (2026), https://arxiv.org/abs/2606.30620, which should be cited when using those +meshes. Note the paper pairs the RTU grid with a Gaussian-process source prior, whereas these examples use +PyAutoLens's own regularization schemes (`reg.Constant` / `reg.Adapt`). The RTU meshes are recommended on GPUs +(where their kernel evaluation is not a bottleneck) and for gradient-based (JAX) samplers: the Bilinear mesh's +likelihood is exactly piecewise-constant in the mass model at the default `over_sample_size_pixelization=1` +(zero gradients), so gradient users must either set `over_sample_size_pixelization >= 4` or use the RTU meshes, +and interferometer gradient fitting must use the RTU meshes (interferometer datasets have no over-sampling). + __Start Here Notebook__ If any code in this script is unclear, refer to the `imaging/start_here.ipynb` notebook. @@ -246,7 +263,7 @@ - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear` [7 parameters]. - - The source-galaxy's light uses a 20 x 20 `RectangularAdaptDensity` mesh [0 parameters]. + - The source-galaxy's light uses a 20 x 20 `RectangularBilinearAdaptDensity` mesh [0 parameters]. - This pixelization is regularized using a `Constant` scheme which smooths every source pixel equally [1 parameter]. @@ -266,7 +283,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source: -mesh = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) diff --git a/scripts/imaging/features/pixelization/plot.py b/scripts/imaging/features/pixelization/plot.py index 52cc62edb..6a88a77c6 100644 --- a/scripts/imaging/features/pixelization/plot.py +++ b/scripts/imaging/features/pixelization/plot.py @@ -94,7 +94,7 @@ ) pixelization = al.Pixelization( - mesh=al.mesh.RectangularAdaptDensity(shape=(24, 24)), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=(24, 24)), regularization=al.reg.Constant(coefficient=1.0), ) @@ -238,7 +238,7 @@ ) pixelization = al.Pixelization( - mesh=al.mesh.RectangularAdaptDensity(shape=(24, 24)), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=(24, 24)), regularization=al.reg.Constant(coefficient=1.0), ) diff --git a/scripts/imaging/features/pixelization/slam.py b/scripts/imaging/features/pixelization/slam.py index 2704332ba..8c917efa3 100644 --- a/scripts/imaging/features/pixelization/slam.py +++ b/scripts/imaging/features/pixelization/slam.py @@ -17,7 +17,7 @@ (Note: `AdaptSplit` / `ConstantSplit` regularization is only valid on irregular meshes such as `Delaunay` or `Voronoi`, where the split-cross scheme is defined — see `features/pixelization/delaunay.py`. This example uses -a `RectangularAdaptImage` mesh, so it uses `Adapt` regularization, matching `slam_start_here.py`.) +a `RectangularBilinearAdaptImage` mesh, so it uses `Adapt` regularization, matching `slam_start_here.py`.) __Contents__ @@ -185,7 +185,7 @@ def source_pix_1( """ __SOURCE PIX PIPELINE 2__ -Identical to `slam_start_here.py`. It uses a `RectangularAdaptImage` mesh with `Adapt` regularization. +Identical to `slam_start_here.py`. It uses a `RectangularBilinearAdaptImage` mesh with `Adapt` regularization. `AdaptSplit` regularization (which splits every source pixel into a cross of four regularization points) is reserved for irregular meshes such as `Delaunay` or `Voronoi`, where that split-cross scheme is defined; see @@ -476,7 +476,7 @@ def mass_total( settings_search=settings_search, dataset=dataset, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, ) @@ -485,7 +485,7 @@ def mass_total( dataset=dataset, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/imaging/features/pixelization/source_science.py b/scripts/imaging/features/pixelization/source_science.py index 132d88d24..c793a388c 100644 --- a/scripts/imaging/features/pixelization/source_science.py +++ b/scripts/imaging/features/pixelization/source_science.py @@ -112,7 +112,7 @@ mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh = al.mesh.RectangularAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) @@ -394,7 +394,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source: -mesh = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) @@ -485,7 +485,7 @@ You can now use standard libraries to performed calculations with the reconstruction on the mesh, again avoiding the need to use autolens. -For example, we can create a RectangularAdaptDensity mesh using the scipy.spatial library, which is a triangulation +For example, we can create a RectangularBilinearAdaptDensity mesh using the scipy.spatial library, which is a triangulation of the y and x coordinates of the pixelization mesh. This is useful for visualizing the pixelization and performing calculations on the mesh. """ diff --git a/scripts/imaging/features/scaling_relation/slam.py b/scripts/imaging/features/scaling_relation/slam.py index 95eff69f7..74c22ddc9 100644 --- a/scripts/imaging/features/scaling_relation/slam.py +++ b/scripts/imaging/features/scaling_relation/slam.py @@ -431,7 +431,7 @@ def source_pix_1( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -492,7 +492,7 @@ def source_pix_2( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/scripts/interferometer/features/advanced/subhalo/detect/start_here.py b/scripts/interferometer/features/advanced/subhalo/detect/start_here.py index 652ccf335..2d1c92a92 100644 --- a/scripts/interferometer/features/advanced/subhalo/detect/start_here.py +++ b/scripts/interferometer/features/advanced/subhalo/detect/start_here.py @@ -675,7 +675,7 @@ def subhalo_refine( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -685,7 +685,7 @@ def subhalo_refine( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/datacube/README.md b/scripts/interferometer/features/datacube/README.md index 976323013..13662d8ff 100644 --- a/scripts/interferometer/features/datacube/README.md +++ b/scripts/interferometer/features/datacube/README.md @@ -7,7 +7,7 @@ The following example scripts illustrate datacube lens modeling where: - `start_here`: A first walkthrough — load a 4-channel cube, build the FactorGraph, fit with Nautilus. - `simulator`: Simulate a representative cube. Source `intensity` follows a Gaussian emission line; source `centre` shifts linearly across channels to mimic a kinematic gradient. Writes three layouts side by side: per-channel folders (`channel_NNN/{data,noise_map,uv_wavelengths}.fits`), a 3D-FITS cube (`{visibilities,noise_map,uv_wavelengths}_cube.fits`, `(n_chan, n_vis, 2)`), and a CASA-like 4D cube (`{visibilities,noise_map,uv_wavelengths}_4d_cube.fits`, `(n_pol, n_chan, n_vis, 2)`). Also writes `positions.json` and `cube_summary.json`. - `data_preparation`: How to bridge from CASA's 4D `(n_pol, n_chan, n_vis, 2)` visibilities to the autolens-canonical `(n_chan, n_vis, 2)` layout — polarisation handling (average vs concatenate), run on the simulator's actual 4D output rather than a synthetic example. Includes a self-contained `dataset_list_from_3d_fits()` loader function (copy that into your own script if you have a 3D cube on disk). -- `modeling`: Focused FactorGraph + `RectangularAdaptDensity` pixelization fit, ready to point at your own cube. +- `modeling`: Focused FactorGraph + `RectangularRTUAdaptDensity` pixelization fit, ready to point at your own cube. - `modeling_parametric`: Same wiring with a parametric `Sersic` source — shared morphology, per-channel intensity. Faster than the pixelization variant; appropriate when the source is well-described by a single Sersic. - `delaunay`: Same wiring with a Delaunay-pixelized source — image-plane `Overlay` mesh ray-traced and triangulated in the source plane, with `ConstantSplit` regularization. More flexible than the rectangular mesh; the canonical follow-up once the rectangular fit converges on a sensible lens model. - `likelihood_function`: Step-by-step JAX walkthrough of the per-channel log-evidence sum with an explicit eager-vs-JIT correctness check at `rtol=1e-4`. Useful for understanding what `af.FactorGraphModel` is doing under the hood. diff --git a/scripts/interferometer/features/datacube/delaunay.py b/scripts/interferometer/features/datacube/delaunay.py index 23dbccfbe..b97b262c6 100644 --- a/scripts/interferometer/features/datacube/delaunay.py +++ b/scripts/interferometer/features/datacube/delaunay.py @@ -4,7 +4,7 @@ This script fits a datacube — a list of `Interferometer` channels — with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which -fits the same cube with a `RectangularAdaptDensity` mesh. +fits the same cube with a `RectangularRTUAdaptDensity` mesh. A Delaunay mesh adapts the source-plane reconstruction to the lensed source's morphology more flexibly than a rectangular mesh: source pixels are placed via a triangulation of (y, x) image-plane points that are ray-traced diff --git a/scripts/interferometer/features/datacube/likelihood_function.py b/scripts/interferometer/features/datacube/likelihood_function.py index 33cd25533..fb7e7070e 100644 --- a/scripts/interferometer/features/datacube/likelihood_function.py +++ b/scripts/interferometer/features/datacube/likelihood_function.py @@ -3,7 +3,7 @@ This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** — a list of N per-channel `Interferometer` objects sharing a single lens model — with a per-channel -pixelized source reconstruction (specifically a `RectangularAdaptDensity` mesh and `Constant` regularization +pixelized source reconstruction (specifically a `RectangularRTUAdaptDensity` mesh and `Constant` regularization scheme). This script has the same aims as `interferometer/features/pixelization/likelihood_function.py`: @@ -45,11 +45,11 @@ __Simplifications__ -This example uses a `RectangularAdaptDensity` mesh + `Constant` regularization — the same combination used by +This example uses a `RectangularRTUAdaptDensity` mesh + `Constant` regularization — the same combination used by the rest of the `datacube/` tutorials (`modeling.py`, `start_here.py`). The `pixelization/likelihood_function.py` reference uses `RectangularUniform`, which is a thin subclass of -`RectangularAdaptDensity`; the linear algebra is identical and the construction code is the same. The single -behaviour difference is that `RectangularAdaptDensity` lets the mesh's pixel density adapt to the source-plane +`RectangularRTUAdaptDensity`; the linear algebra is identical and the construction code is the same. The single +behaviour difference is that `RectangularRTUAdaptDensity` lets the mesh's pixel density adapt to the source-plane magnification map, which gives slightly better resolution in highly-magnified regions but does not change the likelihood-function maths at all. @@ -67,7 +67,7 @@ __Contents__ - **Comparison:** datacube = N independent pixelization fits + shared lens; cube log-evidence is the sum. -- **Simplifications:** `RectangularAdaptDensity` mesh, `Constant` regularization. +- **Simplifications:** `RectangularRTUAdaptDensity` mesh, `Constant` regularization. - **Prerequisites:** read `pixelization/likelihood_function.py` first. - **Mesh Shape:** identical to the pixelization reference, sized to 14×14 to match `modeling.py`. - **Mask:** identical to the pixelization reference, sized to the datacube simulator's 256×256 / 0.1″ grid. @@ -211,15 +211,15 @@ __Source Galaxy Pixelization and Regularization__ Same as `pixelization/likelihood_function.py:__Source Galaxy Pixelization and Regularization__`, with -`RectangularAdaptDensity` substituted for `RectangularUniform`. The classes share the same construction -machinery — `RectangularUniform` is a subclass of `RectangularAdaptDensity` — so all of the mesh-grid and +`RectangularRTUAdaptDensity` substituted for `RectangularUniform`. The classes share the same construction +machinery — `RectangularUniform` is a subclass of `RectangularRTUAdaptDensity` — so all of the mesh-grid and mapper code below is unchanged. The same source pixelization is used for every channel. Each channel runs its own linear inversion against this shared pixelization (which is what gives each channel an independent source-plane reconstruction). """ pixelization = al.Pixelization( - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularRTUAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant(coefficient=1.0), ) source_galaxy = al.Galaxy(redshift=1.0, pixelization=pixelization) @@ -619,7 +619,7 @@ def per_channel_log_evidence(dataset): To fit a lens model to a datacube, this likelihood function is sampled across many candidate lens-model parameters using a non-linear search. For the user-facing modeling story see: - - `modeling.py` — `RectangularAdaptDensity` pixelization fit with `af.Nautilus`, the canonical entry point. + - `modeling.py` — `RectangularRTUAdaptDensity` pixelization fit with `af.Nautilus`, the canonical entry point. - `start_here.py` — narrative walkthrough wrapping the same fit. - `delaunay.py` — Delaunay-pixelized source variant. - `modeling_parametric.py` — parametric `Sersic` source variant (per-channel intensity). diff --git a/scripts/interferometer/features/datacube/modeling.py b/scripts/interferometer/features/datacube/modeling.py index 6ab111482..8c594c3ed 100644 --- a/scripts/interferometer/features/datacube/modeling.py +++ b/scripts/interferometer/features/datacube/modeling.py @@ -145,8 +145,8 @@ __Mesh Shape__ The pixelization mesh shape is fixed before modeling because JAX needs static array shapes. We use a -14 x 14 ``RectangularAdaptDensity`` mesh — small enough to keep the prototype cheap, large enough to capture the -emission-line source morphology produced by the simulator. `RectangularAdaptDensity` adapts the source-plane +14 x 14 ``RectangularRTUAdaptDensity`` mesh — small enough to keep the prototype cheap, large enough to capture the +emission-line source morphology produced by the simulator. `RectangularRTUAdaptDensity` adapts the source-plane pixel density to the lensing magnification map, giving more pixels to the highly-magnified source-plane regions where the lensed signal is concentrated. """ @@ -157,7 +157,7 @@ __Model__ The lens galaxy is a shared `Isothermal + ExternalShear`, identical across every channel. The source galaxy is -a `Pixelization` with a `RectangularAdaptDensity` mesh and `Constant` regularization — the inversion runs +a `Pixelization` with a `RectangularRTUAdaptDensity` mesh and `Constant` regularization — the inversion runs independently per channel inside each `AnalysisInterferometer`, giving each channel its own source-plane reconstruction without adding any model parameters. @@ -170,7 +170,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source (pixelization, no free priors): -mesh = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) source = af.Model(al.Galaxy, redshift=1.0, pixelization=pixelization) diff --git a/scripts/interferometer/features/datacube/start_here.py b/scripts/interferometer/features/datacube/start_here.py index 4b89a63c9..154d26a01 100644 --- a/scripts/interferometer/features/datacube/start_here.py +++ b/scripts/interferometer/features/datacube/start_here.py @@ -177,9 +177,9 @@ __Mesh Shape__ The pixelization mesh shape is fixed before modeling because JAX needs static-shape arrays for its source-plane -linear algebra. We use a 14 x 14 `RectangularAdaptDensity` mesh — small enough to make the prototype iteration +linear algebra. We use a 14 x 14 `RectangularRTUAdaptDensity` mesh — small enough to make the prototype iteration cheap, large enough to capture the emission-line source morphology produced by the simulator. -`RectangularAdaptDensity` adapts the source-plane pixel density to the lensing magnification map, giving more +`RectangularRTUAdaptDensity` adapts the source-plane pixel density to the lensing magnification map, giving more pixels to the highly-magnified regions of the source plane where the lensed signal is concentrated. """ mesh_pixels_yx = 14 @@ -193,7 +193,7 @@ - A shared `Isothermal + ExternalShear` lens. There are 7 free parameters (mass centre, ellipticity components, einstein radius, two shear components). The lens does not change with frequency, so a single set of priors is used for every channel. - - A pixelized source: a `RectangularAdaptDensity` mesh with `Constant` regularization (1 free parameter — the + - A pixelized source: a `RectangularRTUAdaptDensity` mesh with `Constant` regularization (1 free parameter — the regularization coefficient). The pixelization itself has no per-pixel priors; the source-plane fluxes are a linear inversion output computed by each channel's `AnalysisInterferometer` at fit time. That is what makes each channel an independent linear solve while sharing all of the non-linear parameters. @@ -206,7 +206,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source (pixelization, no per-pixel priors): -mesh = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) source = af.Model(al.Galaxy, redshift=1.0, pixelization=pixelization) diff --git a/scripts/interferometer/features/extra_galaxies/slam.py b/scripts/interferometer/features/extra_galaxies/slam.py index 8547188a2..d51bd2fa4 100644 --- a/scripts/interferometer/features/extra_galaxies/slam.py +++ b/scripts/interferometer/features/extra_galaxies/slam.py @@ -556,7 +556,7 @@ def mass_total( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -566,7 +566,7 @@ def mass_total( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/linear_light_profiles/modeling.py b/scripts/interferometer/features/linear_light_profiles/modeling.py index a0480fa33..418bae152 100644 --- a/scripts/interferometer/features/linear_light_profiles/modeling.py +++ b/scripts/interferometer/features/linear_light_profiles/modeling.py @@ -389,7 +389,7 @@ many light profiles combined in a `Basis` (e.g. `lp_basis.Basis`). - `Mapper`: The linear object used by a `Pixelization` to reconstruct data via an `Inversion`. The `Mapper` - is specific to the `Pixelization`'s `Mesh` (e.g. a `RectangularMapper` is used for a `RectangularAdaptDensity` + is specific to the `Pixelization`'s `Mesh` (e.g. a `RectangularMapper` is used for a `RectangularRTUAdaptDensity` mesh). In this example, the model has one linear `SersicCore` for the source galaxy's bulge and no lens-light diff --git a/scripts/interferometer/features/linear_light_profiles/slam.py b/scripts/interferometer/features/linear_light_profiles/slam.py index db32cf4df..b2d4325f9 100644 --- a/scripts/interferometer/features/linear_light_profiles/slam.py +++ b/scripts/interferometer/features/linear_light_profiles/slam.py @@ -525,7 +525,7 @@ def mass_total( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -535,7 +535,7 @@ def mass_total( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/multi_gaussian_expansion/slam.py b/scripts/interferometer/features/multi_gaussian_expansion/slam.py index 1b362e9f0..83e4f0458 100644 --- a/scripts/interferometer/features/multi_gaussian_expansion/slam.py +++ b/scripts/interferometer/features/multi_gaussian_expansion/slam.py @@ -526,7 +526,7 @@ def mass_total( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -536,7 +536,7 @@ def mass_total( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/pixelization/README.md b/scripts/interferometer/features/pixelization/README.md index be5d4c979..cab0f05b0 100644 --- a/scripts/interferometer/features/pixelization/README.md +++ b/scripts/interferometer/features/pixelization/README.md @@ -13,6 +13,15 @@ The following example scripts illustrating lens modeling where: - `delaunay`: Using a Delaunay mesh (instead of a rectangular mesh) for the source reconstruction. - `many_visibilities_preparation`: How to prepare the linear algbera for datasets with many visibilities for efficient memory usage and run times. +# Rectangular Mesh Variants + +Interferometer examples use the `RectangularRTUAdaptDensity` / `RectangularRTUAdaptImage` meshes — the +ray-guided transformed uniform (RTU) grid formulation of Enzi et al. (2026), https://arxiv.org/abs/2606.30620, +which should be cited when using them. RTU is required for gradient-based (JAX) interferometer fitting: the +`RectangularBilinear` meshes (the fast CPU default in the imaging examples) have zero likelihood gradients on +the interferometer sparse path, with no over-sampling setting available to fix it. For CPU-only fitting with +non-gradient samplers the Bilinear meshes can be swapped in for a substantial speed-up. + # Results These scripts only give a brief overview of how to analyse and interpret the results a lens model fit. diff --git a/scripts/interferometer/features/pixelization/fit.py b/scripts/interferometer/features/pixelization/fit.py index 7561a1476..f35e5da69 100644 --- a/scripts/interferometer/features/pixelization/fit.py +++ b/scripts/interferometer/features/pixelization/fit.py @@ -235,7 +235,7 @@ of the noise in the data and an unrealistically complex and structured source. Regularization smooths the source reconstruction solution by penalizing solutions where neighboring pixels have large flux differences. """ -mesh = al.mesh.RectangularAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularRTUAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) diff --git a/scripts/interferometer/features/pixelization/likelihood_function.py b/scripts/interferometer/features/pixelization/likelihood_function.py index bee76967d..22f43dc81 100644 --- a/scripts/interferometer/features/pixelization/likelihood_function.py +++ b/scripts/interferometer/features/pixelization/likelihood_function.py @@ -45,7 +45,7 @@ __Simplifications__ This example uses a `RectangularUniform` mesh, where all rectangular source pixels have the same size. Most -pixelization examples use a `RectangularAdaptDensity` mesh, which adapts the size of source pixels to the +pixelization examples use a `RectangularRTUAdaptDensity` mesh, which adapts the size of source pixels to the density of points in the source-plane (e.g. the caustic). The `RectangularUniform` mesh is used here because it is simpler to explain the likelihood function @@ -427,11 +427,11 @@ We can briefly consider how this step differs for other mesh types. Above, we simply overlaid a uniform rectangular grid to define the source pixel centres and then mapped image pixels to these source pixels. -The `RectangularAdaptDensity` mesh pretty much works exactly the same, its just that a calculation (which we don't +The `RectangularRTUAdaptDensity` mesh pretty much works exactly the same, its just that a calculation (which we don't describe here) works out how to make a grid of rectangular pixels that adapt to the source-plane density and thus vary in size. -There is also a `RectangularAdaptImage` mesh which uses the image of the lensed source to adapt +There is also a `RectangularRTUAdaptImage` mesh which uses the image of the lensed source to adapt the rectangular pixel sizes. This often puts even smaller pixels in the brightest regions of the source, even if it lies offset or away from the caustic. @@ -1006,10 +1006,10 @@ are described in additional notebooks found in this package. In brief, these describe: - **Over Sampling**: Oversampling the image grid into a finer grid of sub-pixels, which are all individually - paired fractionally with each `RectangularAdaptDensity` pixel. + paired fractionally with each `RectangularRTUAdaptDensity` pixel. - - **Source-plane Interpolation**: Using bilinear interpolation on the `RectangularAdaptDensity` pixelization to pair - each image (sub-)pixel to multiple `RectangularAdaptDensity` pixels with interpolation weights. + - **Source-plane Interpolation**: Using bilinear interpolation on the `RectangularRTUAdaptDensity` pixelization to pair + each image (sub-)pixel to multiple `RectangularRTUAdaptDensity` pixels with interpolation weights. - **Source Morphology Pixelization Adaption**: Adapting the pixelization such that is congregates source pixels around the source's brightest regions, as opposed to the magnification-based pixelization used here. diff --git a/scripts/interferometer/features/pixelization/modeling.py b/scripts/interferometer/features/pixelization/modeling.py index 01655fa70..a4ab92907 100644 --- a/scripts/interferometer/features/pixelization/modeling.py +++ b/scripts/interferometer/features/pixelization/modeling.py @@ -108,9 +108,23 @@ - The lens galaxy's light is omitted (and is not present in the simulated data). - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear`. - - The source galaxy's surface-brightness is reconstructed using a `RectangularAdaptDensity` mesh + - The source galaxy's surface-brightness is reconstructed using a `RectangularRTUAdaptDensity` mesh and `Constant` regularization scheme. +__Rectangular Mesh Variants__ + +Interferometer examples use the `RectangularRTUAdaptDensity` mesh: its smooth kernel-density CDF transform is +the ray-guided transformed uniform (RTU) grid formulation of Enzi et al. (2026), +https://arxiv.org/abs/2606.30620, which should be cited when using this mesh (the paper pairs the RTU grid +with a Gaussian-process source prior, whereas these examples use PyAutoLens's own regularization schemes such +as `reg.Constant` / `reg.Adapt`). + +RTU is the required choice for gradient-based (JAX) fitting of interferometer data: the alternative +`RectangularBilinearAdaptDensity` mesh (the fast CPU default in the imaging examples, which warps the grid via +the empirical rank CDF of the traced points) has an exactly piecewise-constant likelihood in the mass model on +the interferometer sparse path — zero gradients, with no over-sampling setting available to fix it. For +CPU-only fitting with non-gradient samplers, the Bilinear mesh can be swapped in for a substantial speed-up. + __Start Here Notebook__ If any code in this script is unclear, refer to the `interferometer/start_here.ipynb` notebook. @@ -267,7 +281,7 @@ - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear` [7 parameters]. - - The source-galaxy's light uses a 20 x 20 `RectangularAdaptDensity` mesh [0 parameters]. + - The source-galaxy's light uses a 20 x 20 `RectangularRTUAdaptDensity` mesh [0 parameters]. - This pixelization is regularized using a `Constant` scheme which smooths every source pixel equally [1 parameter]. @@ -287,7 +301,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source: -mesh = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) diff --git a/scripts/interferometer/features/pixelization/slam.py b/scripts/interferometer/features/pixelization/slam.py index b050bd5e8..8ac779da8 100644 --- a/scripts/interferometer/features/pixelization/slam.py +++ b/scripts/interferometer/features/pixelization/slam.py @@ -518,7 +518,7 @@ def mass_total( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -528,7 +528,7 @@ def mass_total( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/pixelization/source_science.py b/scripts/interferometer/features/pixelization/source_science.py index 73366c153..4ad0613e5 100644 --- a/scripts/interferometer/features/pixelization/source_science.py +++ b/scripts/interferometer/features/pixelization/source_science.py @@ -96,7 +96,7 @@ mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh = al.mesh.RectangularAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularRTUAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) @@ -382,7 +382,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source: -mesh = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) @@ -470,7 +470,7 @@ You can now use standard libraries to performed calculations with the reconstruction on the mesh, again avoiding the need to use autolens. -For example, we can create a RectangularAdaptDensity mesh using the scipy.spatial library, which is a triangulation +For example, we can create a RectangularRTUAdaptDensity mesh using the scipy.spatial library, which is a triangulation of the y and x coordinates of the pixelization mesh. This is useful for visualizing the pixelization and performing calculations on the mesh. """ diff --git a/scripts/interferometer/features/scaling_relation/slam.py b/scripts/interferometer/features/scaling_relation/slam.py index aac530e9b..e9de6000a 100644 --- a/scripts/interferometer/features/scaling_relation/slam.py +++ b/scripts/interferometer/features/scaling_relation/slam.py @@ -225,7 +225,7 @@ def source_pix_1( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -283,7 +283,7 @@ def source_pix_2( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/scripts/multi_dataset/features/pixelization/modeling.py b/scripts/multi_dataset/features/pixelization/modeling.py index 1b228cdbc..86d1aa08e 100644 --- a/scripts/multi_dataset/features/pixelization/modeling.py +++ b/scripts/multi_dataset/features/pixelization/modeling.py @@ -182,7 +182,7 @@ pixelization = af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Constant, ) diff --git a/scripts/multi_dataset/features/slam/independent.py b/scripts/multi_dataset/features/slam/independent.py index a43c0ed89..a3181cb02 100644 --- a/scripts/multi_dataset/features/slam/independent.py +++ b/scripts/multi_dataset/features/slam/independent.py @@ -17,7 +17,7 @@ to capture changes in the lens light over wavelength, but it does not update the Gaussian parameters (e.g. `centre`, `elliptical_comps`, `sigma`) themselves due to the lower resolution of the data. -- The source reconstruction (RectangularAdaptDensity adaptive mesh) is updated using linear algebra to reconstruct +- The source reconstruction (RectangularBilinearAdaptDensity adaptive mesh) is updated using linear algebra to reconstruct the source, but again fixes the source pixelization parameters themselves. - Sub-pixel offsets between the datasets are fully modeled as free parameters, because the precision of a lens model @@ -246,7 +246,7 @@ def source_pix_1( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -290,7 +290,7 @@ def source_pix_2( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -464,7 +464,7 @@ def source_pix_1_secondary( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -510,7 +510,7 @@ def source_pix_2_secondary( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/scripts/multi_dataset/features/slam/simultaneous.py b/scripts/multi_dataset/features/slam/simultaneous.py index b3ac9c80b..740dba12d 100644 --- a/scripts/multi_dataset/features/slam/simultaneous.py +++ b/scripts/multi_dataset/features/slam/simultaneous.py @@ -253,7 +253,7 @@ def source_pix_1( pixelization=af.Model( al.Pixelization, mesh=af.Model( - al.mesh.RectangularAdaptDensity, shape=mesh_shape + al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape ), regularization=al.reg.Adapt, ), @@ -308,7 +308,7 @@ def source_pix_2( redshift=source_lp_result[i].instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/scripts/multi_galaxy/features/advanced/double_source_plane_lens/slam.py b/scripts/multi_galaxy/features/advanced/double_source_plane_lens/slam.py index 2b14e7160..9bca0772f 100644 --- a/scripts/multi_galaxy/features/advanced/double_source_plane_lens/slam.py +++ b/scripts/multi_galaxy/features/advanced/double_source_plane_lens/slam.py @@ -617,10 +617,10 @@ def dataset_from(mask_radius, main_lens_centres): mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ diff --git a/scripts/multi_galaxy/features/advanced/mass_stellar_dark/slam.py b/scripts/multi_galaxy/features/advanced/mass_stellar_dark/slam.py index 19ccd6ee1..ab548224d 100644 --- a/scripts/multi_galaxy/features/advanced/mass_stellar_dark/slam.py +++ b/scripts/multi_galaxy/features/advanced/mass_stellar_dark/slam.py @@ -557,10 +557,10 @@ def mass_light_dark( mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ diff --git a/scripts/multi_galaxy/features/advanced/subhalo/detect/start_here.py b/scripts/multi_galaxy/features/advanced/subhalo/detect/start_here.py index 86ad56bc0..9f5ea59f7 100644 --- a/scripts/multi_galaxy/features/advanced/subhalo/detect/start_here.py +++ b/scripts/multi_galaxy/features/advanced/subhalo/detect/start_here.py @@ -763,10 +763,10 @@ def subhalo_refine( mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ diff --git a/scripts/multi_galaxy/features/extra_galaxies/slam.py b/scripts/multi_galaxy/features/extra_galaxies/slam.py index bfec491ed..bd7e4698a 100644 --- a/scripts/multi_galaxy/features/extra_galaxies/slam.py +++ b/scripts/multi_galaxy/features/extra_galaxies/slam.py @@ -618,10 +618,10 @@ def mass_total( mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ diff --git a/scripts/multi_galaxy/features/linear_light_profiles/slam.py b/scripts/multi_galaxy/features/linear_light_profiles/slam.py index 639e77675..5751d4fa2 100644 --- a/scripts/multi_galaxy/features/linear_light_profiles/slam.py +++ b/scripts/multi_galaxy/features/linear_light_profiles/slam.py @@ -544,10 +544,10 @@ def mass_total( mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ diff --git a/scripts/multi_galaxy/features/multi_gaussian_expansion/slam.py b/scripts/multi_galaxy/features/multi_gaussian_expansion/slam.py index 56850c09c..b6d05a926 100644 --- a/scripts/multi_galaxy/features/multi_gaussian_expansion/slam.py +++ b/scripts/multi_galaxy/features/multi_gaussian_expansion/slam.py @@ -523,10 +523,10 @@ def mass_total( """ mesh_shape = (28, 28) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ diff --git a/scripts/multi_galaxy/features/no_lens_light/slam.py b/scripts/multi_galaxy/features/no_lens_light/slam.py index 88d537843..c6fe06872 100644 --- a/scripts/multi_galaxy/features/no_lens_light/slam.py +++ b/scripts/multi_galaxy/features/no_lens_light/slam.py @@ -464,10 +464,10 @@ def mass_total( mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ diff --git a/scripts/multi_galaxy/features/pixelization/README.md b/scripts/multi_galaxy/features/pixelization/README.md index cb56c32c3..0945802a7 100644 --- a/scripts/multi_galaxy/features/pixelization/README.md +++ b/scripts/multi_galaxy/features/pixelization/README.md @@ -20,7 +20,7 @@ on every deflector rather than on a single lens. # Mesh and Regularization -The rectangular meshes (`RectangularUniform`, `RectangularAdaptDensity`, `RectangularAdaptImage`) are used by +The rectangular meshes (`RectangularUniform`, `RectangularBilinearAdaptDensity`, `RectangularBilinearAdaptImage`) are used by `modeling`, `fit`, `adaptive`, `cpu_fast_modeling` and `slam`; `delaunay` uses the `Delaunay` mesh. Two constraints govern which regularization pairs with which mesh, and both fail loudly rather than silently: diff --git a/scripts/multi_galaxy/features/pixelization/adaptive.py b/scripts/multi_galaxy/features/pixelization/adaptive.py index bf1cee807..e29fb9a52 100644 --- a/scripts/multi_galaxy/features/pixelization/adaptive.py +++ b/scripts/multi_galaxy/features/pixelization/adaptive.py @@ -26,10 +26,10 @@ __Adaptive Features__ -Two adaptive classes are used, replacing the `RectangularAdaptDensity` mesh and `Constant` regularization of +Two adaptive classes are used, replacing the `RectangularBilinearAdaptDensity` mesh and `Constant` regularization of `multi_galaxy/features/pixelization/modeling.py`: - - `RectangularAdaptImage` mesh: places more source pixels where the source's adapt image is brighter, so + - `RectangularBilinearAdaptImage` mesh: places more source pixels where the source's adapt image is brighter, so resolution follows the source's light rather than the magnification pattern. - `Adapt` regularization: varies the smoothing strength across the source, regularizing bright regions less @@ -241,7 +241,7 @@ pixelization_2 = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant, ) @@ -301,7 +301,7 @@ pixelization_3 = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptImage(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptImage(shape=mesh_shape), regularization=al.reg.Adapt, ) diff --git a/scripts/multi_galaxy/features/pixelization/cpu_fast_modeling.py b/scripts/multi_galaxy/features/pixelization/cpu_fast_modeling.py index f572949b3..e7aa4f5a3 100644 --- a/scripts/multi_galaxy/features/pixelization/cpu_fast_modeling.py +++ b/scripts/multi_galaxy/features/pixelization/cpu_fast_modeling.py @@ -207,7 +207,7 @@ ) pixelization = al.Pixelization( - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant(coefficient=1.0), ) @@ -269,7 +269,7 @@ pix = af.Model( al.Pixelization, - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant, ) diff --git a/scripts/multi_galaxy/features/pixelization/modeling.py b/scripts/multi_galaxy/features/pixelization/modeling.py index 4b8250ebf..eca49b449 100644 --- a/scripts/multi_galaxy/features/pixelization/modeling.py +++ b/scripts/multi_galaxy/features/pixelization/modeling.py @@ -57,7 +57,7 @@ - Each co-dominant deflector's light is an MGE, its mass an `Isothermal` with its centre fixed. - The system has a single overall `ExternalShear` at the system centre. - - The source is reconstructed on a `RectangularAdaptDensity` mesh with `Constant` regularization. + - The source is reconstructed on a `RectangularBilinearAdaptDensity` mesh with `Constant` regularization. __Start Here Notebook__ @@ -171,7 +171,7 @@ A `Pixelization` has two parts: - - a **mesh**, which decides where the source pixels are. `RectangularAdaptDensity` places more pixels where the + - a **mesh**, which decides where the source pixels are. `RectangularBilinearAdaptDensity` places more pixels where the source is brighter, so resolution follows the signal. - a **regularization**, which penalizes unsmooth reconstructions. `Constant` applies one smoothing strength everywhere, and its coefficient is the single sampled parameter the source contributes. @@ -225,7 +225,7 @@ pixelization = af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=(28, 28)), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=(28, 28)), regularization=af.Model(al.reg.Constant), ) diff --git a/scripts/multi_galaxy/features/pixelization/plot.py b/scripts/multi_galaxy/features/pixelization/plot.py index 6da2b2af5..d5d74b4d5 100644 --- a/scripts/multi_galaxy/features/pixelization/plot.py +++ b/scripts/multi_galaxy/features/pixelization/plot.py @@ -128,7 +128,7 @@ ) pixelization = al.Pixelization( - mesh=al.mesh.RectangularAdaptDensity(shape=(24, 24)), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=(24, 24)), regularization=al.reg.Constant(coefficient=1.0), ) diff --git a/scripts/multi_galaxy/features/pixelization/slam.py b/scripts/multi_galaxy/features/pixelization/slam.py index aed2b396d..d48f1b885 100644 --- a/scripts/multi_galaxy/features/pixelization/slam.py +++ b/scripts/multi_galaxy/features/pixelization/slam.py @@ -21,8 +21,8 @@ __Pixelization Choices__ - - **Two meshes, not one.** `source_pix[1]` uses `RectangularAdaptDensity`, which places pixels by the traced - grid's own density and so needs nothing from an earlier fit. `source_pix[2]` uses `RectangularAdaptImage`, + - **Two meshes, not one.** `source_pix[1]` uses `RectangularBilinearAdaptDensity`, which places pixels by the traced + grid's own density and so needs nothing from an earlier fit. `source_pix[2]` uses `RectangularBilinearAdaptImage`, which places them by the source's adapt image — which `source_pix[1]` has by then produced. The progression is the reason there are two SOURCE PIX searches rather than one. @@ -161,7 +161,7 @@ def source_lp( __Where The Mesh Comes In__ -`mesh_init` is passed in rather than hard-coded, and is a `RectangularAdaptDensity` — a mesh that needs no adapt +`mesh_init` is passed in rather than hard-coded, and is a `RectangularBilinearAdaptDensity` — a mesh that needs no adapt image of its own, because it places pixels by the density of the traced grid. That is what makes it usable at this point in the pipeline, before a reconstruction exists. @@ -255,7 +255,7 @@ def source_pix_1( __Where The Mesh Comes In__ -This is the search that uses `RectangularAdaptImage`. It can, because `source_pix[1]` has now produced a +This is the search that uses `RectangularBilinearAdaptImage`. It can, because `source_pix[1]` has now produced a reconstruction of the source, and `galaxy_name_image_dict_via_result_from` below reads its adapt images from that result rather than from the parametric SOURCE LP source. @@ -580,8 +580,8 @@ def mass_total( a finer source reconstruction and a slower fit. `mesh_init` and `regularization_init` go to `source_pix[1]`; `mesh` and `regularization` go to `source_pix[2]`. -The pair differ only in the mesh, for the reason given at the top of this script: `RectangularAdaptDensity` needs -no reconstruction to exist, `RectangularAdaptImage` does. +The pair differ only in the mesh, for the reason given at the top of this script: `RectangularBilinearAdaptDensity` needs +no reconstruction to exist, `RectangularBilinearAdaptImage` does. To use the Delaunay meshes and their split regularization schemes instead, see `multi_galaxy/features/pixelization/delaunay.py`, which builds the image-plane mesh grid those meshes require. @@ -589,10 +589,10 @@ def mass_total( mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ diff --git a/scripts/multi_galaxy/features/pixelization/source_science.py b/scripts/multi_galaxy/features/pixelization/source_science.py index 293505a0b..b16f94ee4 100644 --- a/scripts/multi_galaxy/features/pixelization/source_science.py +++ b/scripts/multi_galaxy/features/pixelization/source_science.py @@ -171,7 +171,7 @@ mesh_shape = (28, 28) pixelization = al.Pixelization( - mesh=al.mesh.RectangularAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant(coefficient=1.0), ) diff --git a/scripts/multi_galaxy/features/scaling_relation/slam.py b/scripts/multi_galaxy/features/scaling_relation/slam.py index 5594bd8c1..a2411b49e 100644 --- a/scripts/multi_galaxy/features/scaling_relation/slam.py +++ b/scripts/multi_galaxy/features/scaling_relation/slam.py @@ -507,7 +507,7 @@ def source_pix_1( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -582,7 +582,7 @@ def source_pix_2( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/scripts/multi_galaxy/slam.py b/scripts/multi_galaxy/slam.py index 6b881b890..5172c134f 100644 --- a/scripts/multi_galaxy/slam.py +++ b/scripts/multi_galaxy/slam.py @@ -650,10 +650,10 @@ def mass_total( mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh_init = af.Model(al.mesh.RectangularAdaptDensity, shape=mesh_shape) +mesh_init = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization_init = al.reg.Adapt -mesh = af.Model(al.mesh.RectangularAdaptImage, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape) regularization = al.reg.Adapt """ From 9a3f0acfcd73665a3a57fcc84b4e0e15a0f3ada6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 17:46:47 +0000 Subject: [PATCH 2/3] chore: regenerate navigator catalogue for the mesh rename llms-full.txt / workspace_index.json regenerated with autohands/regenerate_navigator.py autolens after the Bilinear/RTU class-name sweep (navigator staleness CI check). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WtMqU3JfmyJh8GvB7jT4Et --- llms-full.txt | 4 ++-- workspace_index.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/llms-full.txt b/llms-full.txt index fdcda60be..fe212db07 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -312,9 +312,9 @@ AUTO-GENERATED by PyAutoHands — do not edit by hand; regenerate with generate. - Contents: Dataset Paths, Simulate, Ray Tracing, Output, Visualize, Tracer json - [Data Preparation: Datacube](scripts/interferometer/features/datacube/data_preparation.py): Most users come to datacube modeling with a single 4D FITS file from CASA, with shape ``(n_pol, n_chan, n_vis, 2)`` — for example, an ALMA observation of a CO emission line might be ``(2, 34, 16984, 2)``: two polarisations, 34 spectral channels, ~17k visibilities per channel, real/imag. - Contents: What ALMA Gives You, Polarisation Handling, Canonical 3D Shape, Shared vs Per-Channel uv_wavelengths / noise_map, 3D-FITS Loader, Worked Example -- [Modeling: Datacube — Delaunay Source](scripts/interferometer/features/datacube/delaunay.py): This script fits a datacube — a list of `Interferometer` channels — with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which fits the same cube with a `RectangularAdaptDensity` mesh. +- [Modeling: Datacube — Delaunay Source](scripts/interferometer/features/datacube/delaunay.py): This script fits a datacube — a list of `Interferometer` channels — with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which fits the same cube with a `RectangularRTUAdaptDensity` mesh. - Contents: Mask, Dataset, Dataset Auto-Simulation, Dataset Loading, Sparse Operators, Positions, Settings, Image Mesh, Edge Zeroing, Adapt Images, Model, Per-Channel Analyses, FactorGraph, Search, Model Fit, Wrap Up -- [__Log Likelihood Function: Datacube__](scripts/interferometer/features/datacube/likelihood_function.py): This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** — a list of N per-channel `Interferometer` objects sharing a single lens model — with a per-channel pixelized source reconstruction (specifically a `RectangularAdaptDensity` mesh and `Constant` regularization scheme). +- [__Log Likelihood Function: Datacube__](scripts/interferometer/features/datacube/likelihood_function.py): This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** — a list of N per-channel `Interferometer` objects sharing a single lens model — with a per-channel pixelized source reconstruction (specifically a `RectangularRTUAdaptDensity` mesh and `Constant` regularization scheme). - Contents: Comparison, Simplifications, Prerequisites, Mesh Shape, Mask, Dataset, Lens Galaxy, Source Galaxy Pixelization and Regularization, One Channel Walkthrough, Across All Channels, Fit, Lens Modeling, Log Likelihood Function: Source Code Speed Up, Wrap Up - [Modeling: Datacube](scripts/interferometer/features/datacube/modeling.py): This script fits a list of `Interferometer` channels — a "datacube" — with a single shared lens model and a per-channel pixelized source reconstruction. Each channel is an independent `Interferometer` dataset; the `af.FactorGraphModel` ties them together by feeding the same lens parameters into every channel's `AnalysisInterferometer.log_likelihood_function` and summing the per-channel log-evidences. - Contents: Mask, Dataset, Dataset Auto-Simulation, Dataset Loading, Sparse Operators, Settings, Mesh Shape, Model, Per-Channel Analyses, FactorGraph, Search, Model Fit, Wrap Up diff --git a/workspace_index.json b/workspace_index.json index 82650c8e7..6177b2c5e 100644 --- a/workspace_index.json +++ b/workspace_index.json @@ -4747,7 +4747,7 @@ ], "notebook": "notebooks/interferometer/features/datacube/delaunay.ipynb", "path": "scripts/interferometer/features/datacube/delaunay.py", - "summary": "This script fits a datacube \u2014 a list of `Interferometer` channels \u2014 with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which fits the same cube with a `RectangularAdaptDensity` mesh.", + "summary": "This script fits a datacube \u2014 a list of `Interferometer` channels \u2014 with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which fits the same cube with a `RectangularRTUAdaptDensity` mesh.", "title": "Modeling: Datacube \u2014 Delaunay Source" }, { @@ -4781,7 +4781,7 @@ ], "notebook": "notebooks/interferometer/features/datacube/likelihood_function.ipynb", "path": "scripts/interferometer/features/datacube/likelihood_function.py", - "summary": "This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** \u2014 a list of N per-channel `Interferometer` objects sharing a single lens model \u2014 with a per-channel pixelized source reconstruction (specifically a `RectangularAdaptDensity` mesh and `Constant` regularization scheme).", + "summary": "This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** \u2014 a list of N per-channel `Interferometer` objects sharing a single lens model \u2014 with a per-channel pixelized source reconstruction (specifically a `RectangularRTUAdaptDensity` mesh and `Constant` regularization scheme).", "title": "__Log Likelihood Function: Datacube__" }, { From 3fbb02007ef755731087e42c5751abdbd58f5542 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 18:39:47 +0000 Subject: [PATCH 3/3] feat: interferometer examples also default to the Bilinear mesh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No normal-workspace example uses RTU (user decision): the interferometer examples switch from RectangularRTUAdaptDensity/AdaptImage to the RectangularBilinearAdaptDensity/AdaptImage default, matching imaging. RTU stays documentation-only — the advanced option, required for gradient-based (JAX) interferometer fitting (zero Bilinear gradients on the sparse path) and recommended on GPUs, with the Enzi et al. 2026 citation. Navigator catalogue regenerated. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WtMqU3JfmyJh8GvB7jT4Et --- llms-full.txt | 4 +-- .../advanced/subhalo/detect/start_here.py | 4 +-- .../features/datacube/README.md | 2 +- .../features/datacube/delaunay.py | 2 +- .../features/datacube/likelihood_function.py | 18 ++++++------- .../features/datacube/modeling.py | 8 +++--- .../features/datacube/start_here.py | 8 +++--- .../features/extra_galaxies/slam.py | 4 +-- .../linear_light_profiles/modeling.py | 2 +- .../features/linear_light_profiles/slam.py | 4 +-- .../features/multi_gaussian_expansion/slam.py | 4 +-- .../features/pixelization/README.md | 13 ++++----- .../features/pixelization/fit.py | 2 +- .../pixelization/likelihood_function.py | 12 ++++----- .../features/pixelization/modeling.py | 27 ++++++++++--------- .../features/pixelization/slam.py | 4 +-- .../features/pixelization/source_science.py | 6 ++--- .../features/scaling_relation/slam.py | 4 +-- workspace_index.json | 4 +-- 19 files changed, 67 insertions(+), 65 deletions(-) diff --git a/llms-full.txt b/llms-full.txt index fe212db07..0481ec342 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -312,9 +312,9 @@ AUTO-GENERATED by PyAutoHands — do not edit by hand; regenerate with generate. - Contents: Dataset Paths, Simulate, Ray Tracing, Output, Visualize, Tracer json - [Data Preparation: Datacube](scripts/interferometer/features/datacube/data_preparation.py): Most users come to datacube modeling with a single 4D FITS file from CASA, with shape ``(n_pol, n_chan, n_vis, 2)`` — for example, an ALMA observation of a CO emission line might be ``(2, 34, 16984, 2)``: two polarisations, 34 spectral channels, ~17k visibilities per channel, real/imag. - Contents: What ALMA Gives You, Polarisation Handling, Canonical 3D Shape, Shared vs Per-Channel uv_wavelengths / noise_map, 3D-FITS Loader, Worked Example -- [Modeling: Datacube — Delaunay Source](scripts/interferometer/features/datacube/delaunay.py): This script fits a datacube — a list of `Interferometer` channels — with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which fits the same cube with a `RectangularRTUAdaptDensity` mesh. +- [Modeling: Datacube — Delaunay Source](scripts/interferometer/features/datacube/delaunay.py): This script fits a datacube — a list of `Interferometer` channels — with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which fits the same cube with a `RectangularBilinearAdaptDensity` mesh. - Contents: Mask, Dataset, Dataset Auto-Simulation, Dataset Loading, Sparse Operators, Positions, Settings, Image Mesh, Edge Zeroing, Adapt Images, Model, Per-Channel Analyses, FactorGraph, Search, Model Fit, Wrap Up -- [__Log Likelihood Function: Datacube__](scripts/interferometer/features/datacube/likelihood_function.py): This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** — a list of N per-channel `Interferometer` objects sharing a single lens model — with a per-channel pixelized source reconstruction (specifically a `RectangularRTUAdaptDensity` mesh and `Constant` regularization scheme). +- [__Log Likelihood Function: Datacube__](scripts/interferometer/features/datacube/likelihood_function.py): This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** — a list of N per-channel `Interferometer` objects sharing a single lens model — with a per-channel pixelized source reconstruction (specifically a `RectangularBilinearAdaptDensity` mesh and `Constant` regularization scheme). - Contents: Comparison, Simplifications, Prerequisites, Mesh Shape, Mask, Dataset, Lens Galaxy, Source Galaxy Pixelization and Regularization, One Channel Walkthrough, Across All Channels, Fit, Lens Modeling, Log Likelihood Function: Source Code Speed Up, Wrap Up - [Modeling: Datacube](scripts/interferometer/features/datacube/modeling.py): This script fits a list of `Interferometer` channels — a "datacube" — with a single shared lens model and a per-channel pixelized source reconstruction. Each channel is an independent `Interferometer` dataset; the `af.FactorGraphModel` ties them together by feeding the same lens parameters into every channel's `AnalysisInterferometer.log_likelihood_function` and summing the per-channel log-evidences. - Contents: Mask, Dataset, Dataset Auto-Simulation, Dataset Loading, Sparse Operators, Settings, Mesh Shape, Model, Per-Channel Analyses, FactorGraph, Search, Model Fit, Wrap Up diff --git a/scripts/interferometer/features/advanced/subhalo/detect/start_here.py b/scripts/interferometer/features/advanced/subhalo/detect/start_here.py index 2d1c92a92..b84aae6bb 100644 --- a/scripts/interferometer/features/advanced/subhalo/detect/start_here.py +++ b/scripts/interferometer/features/advanced/subhalo/detect/start_here.py @@ -675,7 +675,7 @@ def subhalo_refine( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -685,7 +685,7 @@ def subhalo_refine( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/datacube/README.md b/scripts/interferometer/features/datacube/README.md index 13662d8ff..380ec9e32 100644 --- a/scripts/interferometer/features/datacube/README.md +++ b/scripts/interferometer/features/datacube/README.md @@ -7,7 +7,7 @@ The following example scripts illustrate datacube lens modeling where: - `start_here`: A first walkthrough — load a 4-channel cube, build the FactorGraph, fit with Nautilus. - `simulator`: Simulate a representative cube. Source `intensity` follows a Gaussian emission line; source `centre` shifts linearly across channels to mimic a kinematic gradient. Writes three layouts side by side: per-channel folders (`channel_NNN/{data,noise_map,uv_wavelengths}.fits`), a 3D-FITS cube (`{visibilities,noise_map,uv_wavelengths}_cube.fits`, `(n_chan, n_vis, 2)`), and a CASA-like 4D cube (`{visibilities,noise_map,uv_wavelengths}_4d_cube.fits`, `(n_pol, n_chan, n_vis, 2)`). Also writes `positions.json` and `cube_summary.json`. - `data_preparation`: How to bridge from CASA's 4D `(n_pol, n_chan, n_vis, 2)` visibilities to the autolens-canonical `(n_chan, n_vis, 2)` layout — polarisation handling (average vs concatenate), run on the simulator's actual 4D output rather than a synthetic example. Includes a self-contained `dataset_list_from_3d_fits()` loader function (copy that into your own script if you have a 3D cube on disk). -- `modeling`: Focused FactorGraph + `RectangularRTUAdaptDensity` pixelization fit, ready to point at your own cube. +- `modeling`: Focused FactorGraph + `RectangularBilinearAdaptDensity` pixelization fit, ready to point at your own cube. - `modeling_parametric`: Same wiring with a parametric `Sersic` source — shared morphology, per-channel intensity. Faster than the pixelization variant; appropriate when the source is well-described by a single Sersic. - `delaunay`: Same wiring with a Delaunay-pixelized source — image-plane `Overlay` mesh ray-traced and triangulated in the source plane, with `ConstantSplit` regularization. More flexible than the rectangular mesh; the canonical follow-up once the rectangular fit converges on a sensible lens model. - `likelihood_function`: Step-by-step JAX walkthrough of the per-channel log-evidence sum with an explicit eager-vs-JIT correctness check at `rtol=1e-4`. Useful for understanding what `af.FactorGraphModel` is doing under the hood. diff --git a/scripts/interferometer/features/datacube/delaunay.py b/scripts/interferometer/features/datacube/delaunay.py index b97b262c6..2448a8b10 100644 --- a/scripts/interferometer/features/datacube/delaunay.py +++ b/scripts/interferometer/features/datacube/delaunay.py @@ -4,7 +4,7 @@ This script fits a datacube — a list of `Interferometer` channels — with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which -fits the same cube with a `RectangularRTUAdaptDensity` mesh. +fits the same cube with a `RectangularBilinearAdaptDensity` mesh. A Delaunay mesh adapts the source-plane reconstruction to the lensed source's morphology more flexibly than a rectangular mesh: source pixels are placed via a triangulation of (y, x) image-plane points that are ray-traced diff --git a/scripts/interferometer/features/datacube/likelihood_function.py b/scripts/interferometer/features/datacube/likelihood_function.py index fb7e7070e..4f4891733 100644 --- a/scripts/interferometer/features/datacube/likelihood_function.py +++ b/scripts/interferometer/features/datacube/likelihood_function.py @@ -3,7 +3,7 @@ This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** — a list of N per-channel `Interferometer` objects sharing a single lens model — with a per-channel -pixelized source reconstruction (specifically a `RectangularRTUAdaptDensity` mesh and `Constant` regularization +pixelized source reconstruction (specifically a `RectangularBilinearAdaptDensity` mesh and `Constant` regularization scheme). This script has the same aims as `interferometer/features/pixelization/likelihood_function.py`: @@ -45,11 +45,11 @@ __Simplifications__ -This example uses a `RectangularRTUAdaptDensity` mesh + `Constant` regularization — the same combination used by +This example uses a `RectangularBilinearAdaptDensity` mesh + `Constant` regularization — the same combination used by the rest of the `datacube/` tutorials (`modeling.py`, `start_here.py`). The `pixelization/likelihood_function.py` reference uses `RectangularUniform`, which is a thin subclass of -`RectangularRTUAdaptDensity`; the linear algebra is identical and the construction code is the same. The single -behaviour difference is that `RectangularRTUAdaptDensity` lets the mesh's pixel density adapt to the source-plane +`RectangularBilinearAdaptDensity`; the linear algebra is identical and the construction code is the same. The single +behaviour difference is that `RectangularBilinearAdaptDensity` lets the mesh's pixel density adapt to the source-plane magnification map, which gives slightly better resolution in highly-magnified regions but does not change the likelihood-function maths at all. @@ -67,7 +67,7 @@ __Contents__ - **Comparison:** datacube = N independent pixelization fits + shared lens; cube log-evidence is the sum. -- **Simplifications:** `RectangularRTUAdaptDensity` mesh, `Constant` regularization. +- **Simplifications:** `RectangularBilinearAdaptDensity` mesh, `Constant` regularization. - **Prerequisites:** read `pixelization/likelihood_function.py` first. - **Mesh Shape:** identical to the pixelization reference, sized to 14×14 to match `modeling.py`. - **Mask:** identical to the pixelization reference, sized to the datacube simulator's 256×256 / 0.1″ grid. @@ -211,15 +211,15 @@ __Source Galaxy Pixelization and Regularization__ Same as `pixelization/likelihood_function.py:__Source Galaxy Pixelization and Regularization__`, with -`RectangularRTUAdaptDensity` substituted for `RectangularUniform`. The classes share the same construction -machinery — `RectangularUniform` is a subclass of `RectangularRTUAdaptDensity` — so all of the mesh-grid and +`RectangularBilinearAdaptDensity` substituted for `RectangularUniform`. The classes share the same construction +machinery — `RectangularUniform` is a subclass of `RectangularBilinearAdaptDensity` — so all of the mesh-grid and mapper code below is unchanged. The same source pixelization is used for every channel. Each channel runs its own linear inversion against this shared pixelization (which is what gives each channel an independent source-plane reconstruction). """ pixelization = al.Pixelization( - mesh=al.mesh.RectangularRTUAdaptDensity(shape=mesh_shape), + mesh=al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape), regularization=al.reg.Constant(coefficient=1.0), ) source_galaxy = al.Galaxy(redshift=1.0, pixelization=pixelization) @@ -619,7 +619,7 @@ def per_channel_log_evidence(dataset): To fit a lens model to a datacube, this likelihood function is sampled across many candidate lens-model parameters using a non-linear search. For the user-facing modeling story see: - - `modeling.py` — `RectangularRTUAdaptDensity` pixelization fit with `af.Nautilus`, the canonical entry point. + - `modeling.py` — `RectangularBilinearAdaptDensity` pixelization fit with `af.Nautilus`, the canonical entry point. - `start_here.py` — narrative walkthrough wrapping the same fit. - `delaunay.py` — Delaunay-pixelized source variant. - `modeling_parametric.py` — parametric `Sersic` source variant (per-channel intensity). diff --git a/scripts/interferometer/features/datacube/modeling.py b/scripts/interferometer/features/datacube/modeling.py index 8c594c3ed..167a0f372 100644 --- a/scripts/interferometer/features/datacube/modeling.py +++ b/scripts/interferometer/features/datacube/modeling.py @@ -145,8 +145,8 @@ __Mesh Shape__ The pixelization mesh shape is fixed before modeling because JAX needs static array shapes. We use a -14 x 14 ``RectangularRTUAdaptDensity`` mesh — small enough to keep the prototype cheap, large enough to capture the -emission-line source morphology produced by the simulator. `RectangularRTUAdaptDensity` adapts the source-plane +14 x 14 ``RectangularBilinearAdaptDensity`` mesh — small enough to keep the prototype cheap, large enough to capture the +emission-line source morphology produced by the simulator. `RectangularBilinearAdaptDensity` adapts the source-plane pixel density to the lensing magnification map, giving more pixels to the highly-magnified source-plane regions where the lensed signal is concentrated. """ @@ -157,7 +157,7 @@ __Model__ The lens galaxy is a shared `Isothermal + ExternalShear`, identical across every channel. The source galaxy is -a `Pixelization` with a `RectangularRTUAdaptDensity` mesh and `Constant` regularization — the inversion runs +a `Pixelization` with a `RectangularBilinearAdaptDensity` mesh and `Constant` regularization — the inversion runs independently per channel inside each `AnalysisInterferometer`, giving each channel its own source-plane reconstruction without adding any model parameters. @@ -170,7 +170,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source (pixelization, no free priors): -mesh = af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) source = af.Model(al.Galaxy, redshift=1.0, pixelization=pixelization) diff --git a/scripts/interferometer/features/datacube/start_here.py b/scripts/interferometer/features/datacube/start_here.py index 154d26a01..45cba0512 100644 --- a/scripts/interferometer/features/datacube/start_here.py +++ b/scripts/interferometer/features/datacube/start_here.py @@ -177,9 +177,9 @@ __Mesh Shape__ The pixelization mesh shape is fixed before modeling because JAX needs static-shape arrays for its source-plane -linear algebra. We use a 14 x 14 `RectangularRTUAdaptDensity` mesh — small enough to make the prototype iteration +linear algebra. We use a 14 x 14 `RectangularBilinearAdaptDensity` mesh — small enough to make the prototype iteration cheap, large enough to capture the emission-line source morphology produced by the simulator. -`RectangularRTUAdaptDensity` adapts the source-plane pixel density to the lensing magnification map, giving more +`RectangularBilinearAdaptDensity` adapts the source-plane pixel density to the lensing magnification map, giving more pixels to the highly-magnified regions of the source plane where the lensed signal is concentrated. """ mesh_pixels_yx = 14 @@ -193,7 +193,7 @@ - A shared `Isothermal + ExternalShear` lens. There are 7 free parameters (mass centre, ellipticity components, einstein radius, two shear components). The lens does not change with frequency, so a single set of priors is used for every channel. - - A pixelized source: a `RectangularRTUAdaptDensity` mesh with `Constant` regularization (1 free parameter — the + - A pixelized source: a `RectangularBilinearAdaptDensity` mesh with `Constant` regularization (1 free parameter — the regularization coefficient). The pixelization itself has no per-pixel priors; the source-plane fluxes are a linear inversion output computed by each channel's `AnalysisInterferometer` at fit time. That is what makes each channel an independent linear solve while sharing all of the non-linear parameters. @@ -206,7 +206,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source (pixelization, no per-pixel priors): -mesh = af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) source = af.Model(al.Galaxy, redshift=1.0, pixelization=pixelization) diff --git a/scripts/interferometer/features/extra_galaxies/slam.py b/scripts/interferometer/features/extra_galaxies/slam.py index d51bd2fa4..e63fdae80 100644 --- a/scripts/interferometer/features/extra_galaxies/slam.py +++ b/scripts/interferometer/features/extra_galaxies/slam.py @@ -556,7 +556,7 @@ def mass_total( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -566,7 +566,7 @@ def mass_total( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/linear_light_profiles/modeling.py b/scripts/interferometer/features/linear_light_profiles/modeling.py index 418bae152..c47b70fb0 100644 --- a/scripts/interferometer/features/linear_light_profiles/modeling.py +++ b/scripts/interferometer/features/linear_light_profiles/modeling.py @@ -389,7 +389,7 @@ many light profiles combined in a `Basis` (e.g. `lp_basis.Basis`). - `Mapper`: The linear object used by a `Pixelization` to reconstruct data via an `Inversion`. The `Mapper` - is specific to the `Pixelization`'s `Mesh` (e.g. a `RectangularMapper` is used for a `RectangularRTUAdaptDensity` + is specific to the `Pixelization`'s `Mesh` (e.g. a `RectangularMapper` is used for a `RectangularBilinearAdaptDensity` mesh). In this example, the model has one linear `SersicCore` for the source galaxy's bulge and no lens-light diff --git a/scripts/interferometer/features/linear_light_profiles/slam.py b/scripts/interferometer/features/linear_light_profiles/slam.py index b2d4325f9..525ff5ff4 100644 --- a/scripts/interferometer/features/linear_light_profiles/slam.py +++ b/scripts/interferometer/features/linear_light_profiles/slam.py @@ -525,7 +525,7 @@ def mass_total( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -535,7 +535,7 @@ def mass_total( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/multi_gaussian_expansion/slam.py b/scripts/interferometer/features/multi_gaussian_expansion/slam.py index 83e4f0458..cbd2d08c2 100644 --- a/scripts/interferometer/features/multi_gaussian_expansion/slam.py +++ b/scripts/interferometer/features/multi_gaussian_expansion/slam.py @@ -526,7 +526,7 @@ def mass_total( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -536,7 +536,7 @@ def mass_total( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/pixelization/README.md b/scripts/interferometer/features/pixelization/README.md index cab0f05b0..0e1e19bb6 100644 --- a/scripts/interferometer/features/pixelization/README.md +++ b/scripts/interferometer/features/pixelization/README.md @@ -15,12 +15,13 @@ The following example scripts illustrating lens modeling where: # Rectangular Mesh Variants -Interferometer examples use the `RectangularRTUAdaptDensity` / `RectangularRTUAdaptImage` meshes — the -ray-guided transformed uniform (RTU) grid formulation of Enzi et al. (2026), https://arxiv.org/abs/2606.30620, -which should be cited when using them. RTU is required for gradient-based (JAX) interferometer fitting: the -`RectangularBilinear` meshes (the fast CPU default in the imaging examples) have zero likelihood gradients on -the interferometer sparse path, with no over-sampling setting available to fix it. For CPU-only fitting with -non-gradient samplers the Bilinear meshes can be swapped in for a substantial speed-up. +Interferometer examples use the default `RectangularBilinearAdaptDensity` / `RectangularBilinearAdaptImage` +meshes (empirical rank-CDF transform — no extra parameters, fastest on CPUs), like the imaging examples. +Gradient-based (JAX) interferometer fitting must instead use the advanced `RectangularRTUAdaptDensity` / +`RectangularRTUAdaptImage` meshes — the ray-guided transformed uniform (RTU) grid formulation of Enzi et al. +(2026), https://arxiv.org/abs/2606.30620, which should be cited when using them: the Bilinear meshes have zero +likelihood gradients on the interferometer sparse path, with no over-sampling setting available to fix it. The +RTU meshes are also the recommended option on GPUs. # Results diff --git a/scripts/interferometer/features/pixelization/fit.py b/scripts/interferometer/features/pixelization/fit.py index f35e5da69..25d98aab3 100644 --- a/scripts/interferometer/features/pixelization/fit.py +++ b/scripts/interferometer/features/pixelization/fit.py @@ -235,7 +235,7 @@ of the noise in the data and an unrealistically complex and structured source. Regularization smooths the source reconstruction solution by penalizing solutions where neighboring pixels have large flux differences. """ -mesh = al.mesh.RectangularRTUAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) diff --git a/scripts/interferometer/features/pixelization/likelihood_function.py b/scripts/interferometer/features/pixelization/likelihood_function.py index 22f43dc81..4868d9178 100644 --- a/scripts/interferometer/features/pixelization/likelihood_function.py +++ b/scripts/interferometer/features/pixelization/likelihood_function.py @@ -45,7 +45,7 @@ __Simplifications__ This example uses a `RectangularUniform` mesh, where all rectangular source pixels have the same size. Most -pixelization examples use a `RectangularRTUAdaptDensity` mesh, which adapts the size of source pixels to the +pixelization examples use a `RectangularBilinearAdaptDensity` mesh, which adapts the size of source pixels to the density of points in the source-plane (e.g. the caustic). The `RectangularUniform` mesh is used here because it is simpler to explain the likelihood function @@ -427,11 +427,11 @@ We can briefly consider how this step differs for other mesh types. Above, we simply overlaid a uniform rectangular grid to define the source pixel centres and then mapped image pixels to these source pixels. -The `RectangularRTUAdaptDensity` mesh pretty much works exactly the same, its just that a calculation (which we don't +The `RectangularBilinearAdaptDensity` mesh pretty much works exactly the same, its just that a calculation (which we don't describe here) works out how to make a grid of rectangular pixels that adapt to the source-plane density and thus vary in size. -There is also a `RectangularRTUAdaptImage` mesh which uses the image of the lensed source to adapt +There is also a `RectangularBilinearAdaptImage` mesh which uses the image of the lensed source to adapt the rectangular pixel sizes. This often puts even smaller pixels in the brightest regions of the source, even if it lies offset or away from the caustic. @@ -1006,10 +1006,10 @@ are described in additional notebooks found in this package. In brief, these describe: - **Over Sampling**: Oversampling the image grid into a finer grid of sub-pixels, which are all individually - paired fractionally with each `RectangularRTUAdaptDensity` pixel. + paired fractionally with each `RectangularBilinearAdaptDensity` pixel. - - **Source-plane Interpolation**: Using bilinear interpolation on the `RectangularRTUAdaptDensity` pixelization to pair - each image (sub-)pixel to multiple `RectangularRTUAdaptDensity` pixels with interpolation weights. + - **Source-plane Interpolation**: Using bilinear interpolation on the `RectangularBilinearAdaptDensity` pixelization to pair + each image (sub-)pixel to multiple `RectangularBilinearAdaptDensity` pixels with interpolation weights. - **Source Morphology Pixelization Adaption**: Adapting the pixelization such that is congregates source pixels around the source's brightest regions, as opposed to the magnification-based pixelization used here. diff --git a/scripts/interferometer/features/pixelization/modeling.py b/scripts/interferometer/features/pixelization/modeling.py index a4ab92907..f8dbd9d24 100644 --- a/scripts/interferometer/features/pixelization/modeling.py +++ b/scripts/interferometer/features/pixelization/modeling.py @@ -108,22 +108,23 @@ - The lens galaxy's light is omitted (and is not present in the simulated data). - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear`. - - The source galaxy's surface-brightness is reconstructed using a `RectangularRTUAdaptDensity` mesh + - The source galaxy's surface-brightness is reconstructed using a `RectangularBilinearAdaptDensity` mesh and `Constant` regularization scheme. __Rectangular Mesh Variants__ -Interferometer examples use the `RectangularRTUAdaptDensity` mesh: its smooth kernel-density CDF transform is -the ray-guided transformed uniform (RTU) grid formulation of Enzi et al. (2026), -https://arxiv.org/abs/2606.30620, which should be cited when using this mesh (the paper pairs the RTU grid -with a Gaussian-process source prior, whereas these examples use PyAutoLens's own regularization schemes such -as `reg.Constant` / `reg.Adapt`). +Like the imaging examples, interferometer examples use the default `RectangularBilinearAdaptDensity` mesh: it +adapts the size of source pixels to the density of traced points via the empirical rank CDF of their +coordinates — no extra parameters and the fastest rectangular mesh on CPUs. -RTU is the required choice for gradient-based (JAX) fitting of interferometer data: the alternative -`RectangularBilinearAdaptDensity` mesh (the fast CPU default in the imaging examples, which warps the grid via -the empirical rank CDF of the traced points) has an exactly piecewise-constant likelihood in the mass model on -the interferometer sparse path — zero gradients, with no over-sampling setting available to fix it. For -CPU-only fitting with non-gradient samplers, the Bilinear mesh can be swapped in for a substantial speed-up. +Gradient-based (JAX) fitting of interferometer data must instead use the advanced `RectangularRTUAdaptDensity` +/ `RectangularRTUAdaptImage` meshes: the Bilinear mesh's likelihood is exactly piecewise-constant in the mass +model on the interferometer sparse path — zero gradients, with no over-sampling setting available to fix it — +whereas the RTU meshes' smooth kernel-density CDF (the ray-guided transformed uniform grid formulation of +Enzi et al. 2026, https://arxiv.org/abs/2606.30620, which should be cited when using them) carries correct +gradients everywhere. The RTU meshes are also the recommended option on GPUs. Note the Enzi et al. paper +pairs the RTU grid with a Gaussian-process source prior, whereas these examples use PyAutoLens's own +regularization schemes (`reg.Constant` / `reg.Adapt`). __Start Here Notebook__ @@ -281,7 +282,7 @@ - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear` [7 parameters]. - - The source-galaxy's light uses a 20 x 20 `RectangularRTUAdaptDensity` mesh [0 parameters]. + - The source-galaxy's light uses a 20 x 20 `RectangularBilinearAdaptDensity` mesh [0 parameters]. - This pixelization is regularized using a `Constant` scheme which smooths every source pixel equally [1 parameter]. @@ -301,7 +302,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source: -mesh = af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) diff --git a/scripts/interferometer/features/pixelization/slam.py b/scripts/interferometer/features/pixelization/slam.py index 8ac779da8..a908a9736 100644 --- a/scripts/interferometer/features/pixelization/slam.py +++ b/scripts/interferometer/features/pixelization/slam.py @@ -518,7 +518,7 @@ def mass_total( settings_search=settings_search, dataset=dataset_sparse, source_lp_result=source_lp_result, - mesh_init=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), + mesh_init=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization_init=al.reg.Adapt, settings=settings, ) @@ -528,7 +528,7 @@ def mass_total( dataset=dataset_sparse, source_lp_result=source_lp_result, source_pix_result_1=source_pix_result_1, - mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, settings=settings, ) diff --git a/scripts/interferometer/features/pixelization/source_science.py b/scripts/interferometer/features/pixelization/source_science.py index 4ad0613e5..3b42cc038 100644 --- a/scripts/interferometer/features/pixelization/source_science.py +++ b/scripts/interferometer/features/pixelization/source_science.py @@ -96,7 +96,7 @@ mesh_pixels_yx = 28 mesh_shape = (mesh_pixels_yx, mesh_pixels_yx) -mesh = al.mesh.RectangularRTUAdaptDensity(shape=mesh_shape) +mesh = al.mesh.RectangularBilinearAdaptDensity(shape=mesh_shape) regularization = al.reg.Constant(coefficient=1.0) pixelization = al.Pixelization(mesh=mesh, regularization=regularization) @@ -382,7 +382,7 @@ lens = af.Model(al.Galaxy, redshift=0.5, mass=mass, shear=shear) # Source: -mesh = af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape) +mesh = af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape) regularization = af.Model(al.reg.Constant) pixelization = af.Model(al.Pixelization, mesh=mesh, regularization=regularization) @@ -470,7 +470,7 @@ You can now use standard libraries to performed calculations with the reconstruction on the mesh, again avoiding the need to use autolens. -For example, we can create a RectangularRTUAdaptDensity mesh using the scipy.spatial library, which is a triangulation +For example, we can create a RectangularBilinearAdaptDensity mesh using the scipy.spatial library, which is a triangulation of the y and x coordinates of the pixelization mesh. This is useful for visualizing the pixelization and performing calculations on the mesh. """ diff --git a/scripts/interferometer/features/scaling_relation/slam.py b/scripts/interferometer/features/scaling_relation/slam.py index e9de6000a..8e031ad07 100644 --- a/scripts/interferometer/features/scaling_relation/slam.py +++ b/scripts/interferometer/features/scaling_relation/slam.py @@ -225,7 +225,7 @@ def source_pix_1( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularRTUAdaptDensity, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptDensity, shape=mesh_shape), regularization=al.reg.Adapt, ), ), @@ -283,7 +283,7 @@ def source_pix_2( redshift=source_lp_result.instance.galaxies.source.redshift, pixelization=af.Model( al.Pixelization, - mesh=af.Model(al.mesh.RectangularRTUAdaptImage, shape=mesh_shape), + mesh=af.Model(al.mesh.RectangularBilinearAdaptImage, shape=mesh_shape), regularization=al.reg.Adapt, ), ), diff --git a/workspace_index.json b/workspace_index.json index 6177b2c5e..a8d7479a0 100644 --- a/workspace_index.json +++ b/workspace_index.json @@ -4747,7 +4747,7 @@ ], "notebook": "notebooks/interferometer/features/datacube/delaunay.ipynb", "path": "scripts/interferometer/features/datacube/delaunay.py", - "summary": "This script fits a datacube \u2014 a list of `Interferometer` channels \u2014 with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which fits the same cube with a `RectangularRTUAdaptDensity` mesh.", + "summary": "This script fits a datacube \u2014 a list of `Interferometer` channels \u2014 with a single shared lens model and a per-channel **Delaunay-pixelized** source reconstruction. It is the Delaunay sibling of `modeling.py`, which fits the same cube with a `RectangularBilinearAdaptDensity` mesh.", "title": "Modeling: Datacube \u2014 Delaunay Source" }, { @@ -4781,7 +4781,7 @@ ], "notebook": "notebooks/interferometer/features/datacube/likelihood_function.ipynb", "path": "scripts/interferometer/features/datacube/likelihood_function.py", - "summary": "This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** \u2014 a list of N per-channel `Interferometer` objects sharing a single lens model \u2014 with a per-channel pixelized source reconstruction (specifically a `RectangularRTUAdaptDensity` mesh and `Constant` regularization scheme).", + "summary": "This script provides a step-by-step guide of the **PyAutoLens** `log_likelihood_function` used to fit a **datacube** \u2014 a list of N per-channel `Interferometer` objects sharing a single lens model \u2014 with a per-channel pixelized source reconstruction (specifically a `RectangularBilinearAdaptDensity` mesh and `Constant` regularization scheme).", "title": "__Log Likelihood Function: Datacube__" }, {