From c3c2b16ad78cb7542371bdc09f4ea8958006a462 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Mon, 18 May 2026 17:43:05 +0100 Subject: [PATCH] fix: Basis.image_2d_from and dPIEPotential.convergence_2d_from wrappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two profile-return-type bugs surfaced while writing the autolens_workspace mass / light profile guides: 1. Basis.image_2d_list_from returned a raw xp.zeros((N,)) ndarray as the placeholder for LightProfileLinear constituents, so a basis composed entirely of linear profiles produced an image_2d_from = sum([ndarray, ndarray, ...]) that was itself a raw ndarray, contradicting the -> aa.Array2D return annotation. Wrap the placeholder in aa.Array2D so the return type is uniform. 2. dPIEPotential.convergence_2d_from was decorated with @aa.decorators.to_vector_yx (the deflections decorator directly above it) instead of @aa.decorators.to_array — the scalar convergence was being wrapped as a VectorYX2D. The Sph variant below already uses the correct decorator; this brings the elliptical variant in line. Adds regression tests for both fixes asserting the correct wrapper type. Closes #424. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- autogalaxy/profiles/basis.py | 4 +++- .../total/dual_pseudo_isothermal_potential.py | 2 +- .../test_dual_pseudo_isothermal_potential.py | 13 +++++++++++++ test_autogalaxy/profiles/test_basis.py | 19 +++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/autogalaxy/profiles/basis.py b/autogalaxy/profiles/basis.py index ef8936fb..d48e0ebd 100644 --- a/autogalaxy/profiles/basis.py +++ b/autogalaxy/profiles/basis.py @@ -147,7 +147,9 @@ def image_2d_list_from( grid=grid, xp=xp, operated_only=operated_only ) if not isinstance(light_profile, lp_linear.LightProfileLinear) - else xp.zeros((grid.shape[0],)) + else aa.Array2D( + values=xp.zeros((grid.shape[0],)), mask=grid.mask + ) ) for light_profile in self.light_profile_list ] diff --git a/autogalaxy/profiles/mass/total/dual_pseudo_isothermal_potential.py b/autogalaxy/profiles/mass/total/dual_pseudo_isothermal_potential.py index e3da5af1..a7510f5a 100644 --- a/autogalaxy/profiles/mass/total/dual_pseudo_isothermal_potential.py +++ b/autogalaxy/profiles/mass/total/dual_pseudo_isothermal_potential.py @@ -118,7 +118,7 @@ def deflections_yx_2d_from(self, grid: aa.type.Grid2DLike, xp=np, **kwargs): return xp.vstack((deflection_y, deflection_x)).T - @aa.decorators.to_vector_yx + @aa.decorators.to_array @aa.decorators.transform def convergence_2d_from(self, grid: aa.type.Grid2DLike, xp=np, **kwargs): """ diff --git a/test_autogalaxy/profiles/mass/total/test_dual_pseudo_isothermal_potential.py b/test_autogalaxy/profiles/mass/total/test_dual_pseudo_isothermal_potential.py index 2ea341b4..dcf6bbc7 100644 --- a/test_autogalaxy/profiles/mass/total/test_dual_pseudo_isothermal_potential.py +++ b/test_autogalaxy/profiles/mass/total/test_dual_pseudo_isothermal_potential.py @@ -1,10 +1,23 @@ import pytest +import autoarray as aa import autogalaxy as ag grid = ag.Grid2DIrregular([[1.0, 1.0], [2.0, 2.0], [3.0, 3.0], [2.0, 4.0]]) +def test__convergence_2d_from__returns_array2d_not_vector(): + # Regression test for a copy-paste typo: convergence_2d_from used to be + # decorated with @aa.decorators.to_vector_yx (the deflections decorator + # directly above it) which wrapped the scalar convergence in a VectorYX2D. + # The correct decorator is @aa.decorators.to_array. + mp = ag.mp.dPIEPotential(centre=(0.0, 0.0), ell_comps=(0.05, 0.0), ra=0.2, rs=2.0, b0=1.0) + + convergence = mp.convergence_2d_from(grid=grid) + + assert isinstance(convergence, aa.ArrayIrregular) + + def test__deflections_yx_2d_from__sph_config_1(): mp = ag.mp.dPIEPotentialSph(centre=(-0.7, 0.5), b0=5.2, ra=2.0, rs=3.0) diff --git a/test_autogalaxy/profiles/test_basis.py b/test_autogalaxy/profiles/test_basis.py index f2ea3fce..81fa25a9 100644 --- a/test_autogalaxy/profiles/test_basis.py +++ b/test_autogalaxy/profiles/test_basis.py @@ -1,6 +1,7 @@ import numpy as np import pytest +import autoarray as aa import autogalaxy as ag @@ -42,6 +43,24 @@ def test__image_2d_from__does_not_include_linear_light_profiles(grid_2d_7x7): assert (image == lp_image).all() +def test__image_2d_from__returns_array2d_for_linear_only_basis(grid_2d_7x7): + # Regression test for the case where every constituent is a LightProfileLinear: + # the placeholder for the linear-intensity-unknown image used to be a raw + # ndarray of zeros, which made `sum(...)` return a raw ndarray rather than an + # `Array2D`. After the fix the placeholder is itself an `Array2D` so the + # summed return type is uniform regardless of the constituent mix. + basis = ag.lp_basis.Basis( + profile_list=[ + ag.lp_linear.Gaussian(sigma=0.5), + ag.lp_linear.Gaussian(sigma=1.0), + ] + ) + + image = basis.image_2d_from(grid=grid_2d_7x7) + + assert isinstance(image, aa.Array2D) + + def test__image_2d_from__operated_only_false__returns_only_non_operated_profile_image( grid_2d_7x7, lp_0, lp_operated_0 ):