Skip to content

operated_mapping_matrix_override is silently ignored for Interferometer #459

Description

@HRSAstro

Description

Imaging checks the override, interferometer doesn't:

e.g. in operated_mapping_matrix_list:
self.psf.convolved_mapping_matrix_from(...)
if linear_obj.operated_mapping_matrix_override is None
else self.linear_func_operated_mapping_matrix_dict[linear_obj]
(same pattern again in linear_func_operated_mapping_matrix_dict).

autoarray/inversion/inversion/interferometer/abstract.py:63-78 has no such branch:
@Property
def operated_mapping_matrix_list(self) -> List[np.ndarray]:
return [
self.transformer.transform_mapping_matrix(
mapping_matrix=linear_obj.mapping_matrix, xp=self._xp
)
for linear_obj in self.linear_obj_list
]
It always pulls linear_obj.mapping_matrix and pushes it through the NUFFT — linear_obj.operated_mapping_matrix_override is never read on this path.

So if you set operated_mapping_matrix_override on a LinearObj/LinearObjFuncList, it works for imaging fits and is silently ignored for interferometer fits — the raw mapping_matrix is transformed regardless.

Steps to Reproduce

Define a LinearObjFuncList subclass whose operated_mapping_matrix_override returns a distinguishable sentinel matrix, then build an Inversion from it against both an Imaging dataset and an Interferometer dataset and compare inversion.operated_mapping_matrix_list[0] to the sentinel in each case:

import numpy as np
import autoarray as aa
from autoarray.inversion.linear_obj.func_list import AbstractLinearObjFuncList

class OverridingLinearObjFuncList(AbstractLinearObjFuncList):
def init(self, grid, n_data_points):
super().init(grid=grid, regularization=None)
self._n_data_points = n_data_points

@property
def params(self):
    return 1

@property
def mapping_matrix(self):
    return np.ones((self._n_data_points, 1))

@property
def operated_mapping_matrix_override(self):
    return 999.0 * np.ones((self._n_data_points, 1))  # sentinel

def pixel_signals_from(self, signal_scale):
    return np.ones(self.params)

mask = aa.Mask2D.circular(shape_native=(9, 9), radius=3.0, pixel_scales=0.1)
grid = aa.Grid2D.from_mask(mask=mask)

--- Imaging: override IS respected ---

imaging = aa.Imaging(
data=aa.Array2D.full(1.0, shape_native=(9, 9), pixel_scales=0.1).apply_mask(mask=mask),
noise_map=aa.Array2D.full(1.0, shape_native=(9, 9), pixel_scales=0.1).apply_mask(mask=mask),
psf=aa.Convolver.no_blur(pixel_scales=(0.1, 0.1)),
)
inv_imaging = aa.Inversion(
dataset=imaging,
linear_obj_list=[OverridingLinearObjFuncList(grid=grid, n_data_points=mask.pixels_in_mask)],
)
print(np.allclose(inv_imaging.operated_mapping_matrix_list[0], 999.0)) # True

--- Interferometer: override is IGNORED ---

n_vis = 5
interferometer = aa.Interferometer(
data=aa.Visibilities.full(1.0, shape_slim=(n_vis,)),
noise_map=aa.VisibilitiesNoiseMap.full(1.0, shape_slim=(n_vis,)),
uv_wavelengths=np.ones((n_vis, 2)),
real_space_mask=mask,
transformer_class=aa.TransformerDFT,
)
inv_interferometer = aa.Inversion(
dataset=interferometer,
linear_obj_list=[OverridingLinearObjFuncList(grid=grid, n_data_points=mask.pixels_in_mask)],
)
print(np.allclose(inv_interferometer.operated_mapping_matrix_list[0], 999.0)) # False — bug

Expected Behavior

The second print should be True — operated_mapping_matrix_override should either be honored consistently across both dataset types, or the property/docstring on LinearObj should say explicitly that it is imaging-only.

Actual Behavior

Imaging operated_mapping_matrix matches override: True
Interferometer operated_mapping_matrix matches override: False
Interferometer operated_mapping_matrix (actual, first row): [80.99999999-4.1e-20j]

Environment

Python -- 3.11.15
OS -- macOS 26.6.1, arm64 
autoarray -- 2026.7.29.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions