import autolens as al
import numpy as np
import pytest
from autoarray.inversion import inversions
from test_autogalaxy.mock import MockLightProfile


class TestFitImaging:
    class TestFitProperties:
        def test__total_inversions(self, masked_imaging_7x7):

            g0 = al.Galaxy(redshift=0.5)

            g1 = al.Galaxy(redshift=1.0)

            g2 = al.Galaxy(redshift=2.0)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            assert fit.total_inversions == 0

            g2 = al.Galaxy(
                redshift=2.0,
                pixelization=al.pix.Rectangular(),
                regularization=al.reg.Constant(),
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            assert fit.total_inversions == 1

            g0 = al.Galaxy(
                redshift=0.5,
                pixelization=al.pix.Rectangular(),
                regularization=al.reg.Constant(),
            )

            g1 = al.Galaxy(
                redshift=1.0,
                pixelization=al.pix.Rectangular(),
                regularization=al.reg.Constant(),
            )

            g2 = al.Galaxy(
                redshift=2.0,
                pixelization=al.pix.Rectangular(),
                regularization=al.reg.Constant(),
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            assert fit.total_inversions == 3

    class TestLikelihood:
        def test__1x2_image__no_psf_blurring__tracing_fits_data_with_chi_sq_5(self):
            # The image plane image generated by the galaxy is [1.0, 1.0]

            # Thus the chi squared is 4.0**2.0 + 3.0**2.0 = 25.0

            psf = al.Kernel.manual_2d(
                array=(np.array([[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]])),
                pixel_scales=1.0,
            )

            imaging = al.Imaging(
                image=5.0 * al.Array.ones(shape_2d=(3, 4)),
                psf=psf,
                noise_map=al.Array.ones(shape_2d=(3, 4)),
            )
            imaging.image[6] = 4.0

            mask = al.Mask.manual(
                mask=np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                ),
                pixel_scales=1.0,
            )

            masked_imaging_7x7 = al.MaskedImaging(
                imaging=imaging,
                mask=mask,
                settings=al.SettingsMaskedImaging(sub_size=1),
            )

            # Setup as a ray trace instance, using a light profile for the lens

            g0 = al.Galaxy(
                redshift=0.5, light_profile=MockLightProfile(value=1.0, size=2)
            )
            tracer = al.Tracer.from_galaxies(galaxies=[g0])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            assert (
                fit.mask
                == np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                )
            ).all()

            assert (
                fit.image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 5.0, 4.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.noise_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.model_image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 4.0, 3.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.normalized_residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 4.0, 3.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.chi_squared_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 16.0, 9.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert fit.chi_squared == 25.0
            assert fit.reduced_chi_squared == 25.0 / 2.0
            assert fit.noise_normalization == (2.0 * np.log(2 * np.pi * 1.0 ** 2.0))
            assert fit.log_likelihood == -0.5 * (
                25.0 + 2.0 * np.log(2 * np.pi * 1.0 ** 2.0)
            )

        def test__1x2_image__include_psf_blurring__tracing_fits_data_with_chi_sq_4(
            self
        ):
            # This PSF changes the blurred image plane image from [1.0, 1.0] to [1.0, 5.0]

            # Thus, the chi squared is 4.0**2.0 + 0.0**2.0 = 16.0

            psf = al.Kernel.manual_2d(
                array=(np.array([[0.0, 0.0, 0.0], [0.0, 1.0, 3.0], [0.0, 0.0, 0.0]])),
                pixel_scales=1.0,
                renormalize=False,
            )

            imaging = al.Imaging(
                image=5.0 * al.Array.ones(shape_2d=(3, 4)),
                psf=psf,
                noise_map=al.Array.ones(shape_2d=(3, 4)),
            )
            imaging.image[6] = 4.0

            mask = al.Mask.manual(
                mask=np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                ),
                pixel_scales=1.0,
            )

            masked_imaging_7x7 = al.MaskedImaging(
                imaging=imaging,
                mask=mask,
                settings=al.SettingsMaskedImaging(sub_size=1, renormalize_psf=False),
            )

            # Setup as a ray trace instance, using a light profile for the lens

            g0 = al.Galaxy(
                redshift=0.5, light_profile=MockLightProfile(value=1.0, size=2)
            )
            tracer = al.Tracer.from_galaxies(galaxies=[g0])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            assert (
                fit.mask
                == np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                )
            ).all()

            assert (
                fit.image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 5.0, 4.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.noise_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.model_image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 4.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 4.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.normalized_residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 4.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.chi_squared_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 16.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert fit.chi_squared == 16.0
            assert fit.reduced_chi_squared == 16.0 / 2.0
            assert fit.noise_normalization == (2.0 * np.log(2 * np.pi * 1.0 ** 2.0))
            assert fit.log_likelihood == -0.5 * (
                16.0 + 2.0 * np.log(2 * np.pi * 1.0 ** 2.0)
            )

        def test_hyper_galaxy_changes_noise_above_from_1_to_2__reflected_in_likelihood(
            self
        ):
            # This PSF changes the blurred image plane image from [1.0, 1.0] to [1.0, 5.0]

            # Thus, the chi squared is 4.0**2.0 + 0.0**2.0 = 16.0

            # The hyper_galaxies galaxy increases the noise in both pixels by 1.0, to 2.0.

            # This reduces the chi squared to 2.0 instead of 4.0

            psf = al.Kernel.manual_2d(
                array=(np.array([[0.0, 0.0, 0.0], [0.0, 1.0, 3.0], [0.0, 0.0, 0.0]])),
                pixel_scales=1.0,
            )

            imaging = al.Imaging(
                image=5.0 * al.Array.ones(shape_2d=(3, 4)),
                psf=psf,
                noise_map=al.Array.ones(shape_2d=(3, 4)),
            )
            imaging.image[6] = 4.0

            mask = al.Mask.manual(
                mask=np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                ),
                pixel_scales=1.0,
            )

            masked_imaging_7x7 = al.MaskedImaging(
                imaging=imaging,
                mask=mask,
                settings=al.SettingsMaskedImaging(sub_size=1, renormalize_psf=False),
            )

            # Setup as a ray trace instance, using a light profile for the lens

            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=MockLightProfile(value=1.0, size=2),
                hyper_galaxy=al.HyperGalaxy(
                    contribution_factor=1.0, noise_factor=1.0, noise_power=1.0
                ),
                hyper_model_image=al.Array.ones(shape_2d=(1, 2)),
                hyper_galaxy_image=al.Array.ones(shape_2d=(1, 2)),
                hyper_minimum_value=0.0,
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            assert (
                fit.mask
                == np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                )
            ).all()

            assert (
                fit.image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 5.0, 4.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.noise_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 2.0, 2.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.model_image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 4.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 4.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.normalized_residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 2.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.chi_squared_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 4.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert fit.chi_squared == 4.0
            assert fit.reduced_chi_squared == 4.0 / 2.0
            assert fit.noise_normalization == (2.0 * np.log(2 * np.pi * 2.0 ** 2.0))
            assert fit.log_likelihood == -0.5 * (
                4.0 + 2.0 * np.log(2 * np.pi * 2.0 ** 2.0)
            )

        def test__hyper_image_changes_background_sky__reflected_in_likelihood(self):

            psf = al.Kernel.manual_2d(
                array=(np.array([[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]])),
                pixel_scales=1.0,
            )

            imaging = al.Imaging(
                image=al.Array.full(fill_value=4.0, shape_2d=(3, 4)),
                psf=psf,
                noise_map=al.Array.ones(shape_2d=(3, 4)),
            )
            imaging.image[5] = 5.0

            mask = al.Mask.manual(
                mask=np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                ),
                pixel_scales=1.0,
            )

            masked_imaging_7x7 = al.MaskedImaging(
                imaging=imaging,
                mask=mask,
                settings=al.SettingsMaskedImaging(sub_size=1),
            )

            # Setup as a ray trace instance, using a light profile for the lens

            g0 = al.Galaxy(
                redshift=0.5, light_profile=MockLightProfile(value=1.0, size=2)
            )
            tracer = al.Tracer.from_galaxies(galaxies=[g0])

            hyper_image_sky = al.hyper_data.HyperImageSky(sky_scale=1.0)

            fit = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                hyper_image_sky=hyper_image_sky,
            )

            assert (
                fit.mask
                == np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                )
            ).all()

            assert (
                fit.image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 6.0, 5.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.noise_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.model_image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 5.0, 4.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.normalized_residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 5.0, 4.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.chi_squared_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 25.0, 16.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert fit.chi_squared == 41.0
            assert fit.reduced_chi_squared == 41.0 / 2.0
            assert fit.noise_normalization == (2.0 * np.log(2 * np.pi * 1.0 ** 2.0))
            assert fit.log_likelihood == -0.5 * (
                41.0 + 2.0 * np.log(2 * np.pi * 1.0 ** 2.0)
            )

        def test__hyper_background_changes_background_noise_map__reflected_in_likelihood(
            self
        ):

            psf = al.Kernel.manual_2d(
                array=(np.array([[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]])),
                pixel_scales=1.0,
            )

            imaging = al.Imaging(
                image=5.0 * al.Array.ones(shape_2d=(3, 4)),
                psf=psf,
                noise_map=al.Array.ones(shape_2d=(3, 4)),
            )
            imaging.image[6] = 4.0

            mask = al.Mask.manual(
                mask=np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                ),
                pixel_scales=1.0,
            )

            masked_imaging_7x7 = al.MaskedImaging(
                imaging=imaging,
                mask=mask,
                settings=al.SettingsMaskedImaging(sub_size=1),
            )

            # Setup as a ray trace instance, using a light profile for the lens

            g0 = al.Galaxy(
                redshift=0.5, light_profile=MockLightProfile(value=1.0, size=2)
            )
            tracer = al.Tracer.from_galaxies(galaxies=[g0])

            hyper_background_noise = al.hyper_data.HyperBackgroundNoise(noise_scale=1.0)

            fit = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                hyper_background_noise=hyper_background_noise,
            )

            assert (
                fit.mask
                == np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                )
            ).all()

            assert (
                fit.image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 5.0, 4.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.noise_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 2.0, 2.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.model_image.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 4.0, 3.0, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.normalized_residual_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 2.0, 1.5, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert (
                fit.chi_squared_map.in_2d
                == np.array(
                    [[0.0, 0.0, 0.0, 0.0], [0.0, 4.0, 2.25, 0.0], [0.0, 0.0, 0.0, 0.0]]
                )
            ).all()

            assert fit.chi_squared == 6.25
            assert fit.reduced_chi_squared == 6.25 / 2.0
            assert fit.noise_normalization == (2.0 * np.log(2 * np.pi * 2.0 ** 2.0))
            assert fit.log_likelihood == -0.5 * (
                6.25 + 2.0 * np.log(2 * np.pi * 2.0 ** 2.0)
            )

    class TestCompareToManualProfilesOnly:
        def test___all_lens_fit_quantities__no_hyper_methods(self, masked_imaging_7x7):

            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                mass_profile=al.mp.SphericalIsothermal(einstein_radius=1.0),
            )

            g1 = al.Galaxy(
                redshift=1.0, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            assert masked_imaging_7x7.noise_map.in_2d == pytest.approx(
                fit.noise_map.in_2d
            )

            model_image = tracer.blurred_image_from_grid_and_convolver(
                grid=masked_imaging_7x7.grid,
                convolver=masked_imaging_7x7.convolver,
                blurring_grid=masked_imaging_7x7.blurring_grid,
            )

            assert model_image.in_2d == pytest.approx(fit.model_image.in_2d)

            residual_map = al.util.fit.residual_map_from(
                data=masked_imaging_7x7.image, model_data=model_image
            )

            assert residual_map.in_2d == pytest.approx(fit.residual_map.in_2d)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=masked_imaging_7x7.noise_map
            )

            assert normalized_residual_map.in_2d == pytest.approx(
                fit.normalized_residual_map.in_2d
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=masked_imaging_7x7.noise_map
            )

            assert chi_squared_map.in_2d == pytest.approx(fit.chi_squared_map.in_2d)

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=masked_imaging_7x7.noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)
            assert log_likelihood == fit.figure_of_merit

        def test___lens_fit_galaxy_model_image_dict__corresponds_to_blurred_galaxy_images(
            self, masked_imaging_7x7
        ):
            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                mass_profile=al.mp.SphericalIsothermal(einstein_radius=1.0),
            )
            g1 = al.Galaxy(
                redshift=1.0, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )
            g2 = al.Galaxy(redshift=1.0)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            traced_grids_of_planes = tracer.traced_grids_of_planes_from_grid(
                grid=masked_imaging_7x7.grid
            )
            traced_blurring_grids_of_planes = tracer.traced_grids_of_planes_from_grid(
                grid=masked_imaging_7x7.blurring_grid
            )

            g0_image = g0.image_from_grid(grid=traced_grids_of_planes[0])
            g0_blurring_image = g0.image_from_grid(
                grid=traced_blurring_grids_of_planes[0]
            )

            g0_blurred_image = masked_imaging_7x7.convolver.convolved_image_from_image_and_blurring_image(
                image=g0_image, blurring_image=g0_blurring_image
            )

            g1_image = g1.image_from_grid(grid=traced_grids_of_planes[1])
            g1_blurring_image = g1.image_from_grid(
                grid=traced_blurring_grids_of_planes[1]
            )

            g1_blurred_image = masked_imaging_7x7.convolver.convolved_image_from_image_and_blurring_image(
                image=g1_image, blurring_image=g1_blurring_image
            )

            assert fit.galaxy_model_image_dict[g0] == pytest.approx(
                g0_blurred_image, 1.0e-4
            )
            assert fit.galaxy_model_image_dict[g1] == pytest.approx(
                g1_blurred_image, 1.0e-4
            )
            assert (fit.galaxy_model_image_dict[g2].in_1d == np.zeros(9)).all()

            assert fit.model_image.in_2d == pytest.approx(
                fit.galaxy_model_image_dict[g0].in_2d
                + fit.galaxy_model_image_dict[g1].in_2d,
                1.0e-4,
            )

        def test___all_lens_fit_quantities__including_hyper_methods(
            self, masked_imaging_7x7
        ):

            hyper_image_sky = al.hyper_data.HyperImageSky(sky_scale=1.0)

            hyper_background_noise = al.hyper_data.HyperBackgroundNoise(noise_scale=1.0)

            image = hyper_image_sky.hyper_image_from_image(
                image=masked_imaging_7x7.image
            )

            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                mass_profile=al.mp.SphericalIsothermal(einstein_radius=1.0),
                hyper_galaxy=al.HyperGalaxy(
                    contribution_factor=1.0, noise_factor=1.0, noise_power=1.0
                ),
                hyper_model_image=np.ones(9),
                hyper_galaxy_image=np.ones(9),
                hyper_minimum_value=0.0,
            )
            g1 = al.Galaxy(
                redshift=1.0, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1])

            fit = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                hyper_image_sky=hyper_image_sky,
                hyper_background_noise=hyper_background_noise,
            )

            hyper_noise_map_background = hyper_background_noise.hyper_noise_map_from_noise_map(
                noise_map=masked_imaging_7x7.noise_map
            )

            hyper_noise = tracer.hyper_noise_map_from_noise_map(
                noise_map=masked_imaging_7x7.noise_map
            )

            hyper_noise_map = hyper_noise_map_background + hyper_noise

            assert hyper_noise_map.in_2d == pytest.approx(fit.noise_map.in_2d)

            model_image = tracer.blurred_image_from_grid_and_convolver(
                grid=masked_imaging_7x7.grid,
                convolver=masked_imaging_7x7.convolver,
                blurring_grid=masked_imaging_7x7.blurring_grid,
            )

            assert model_image.in_2d == pytest.approx(fit.model_image.in_2d)

            residual_map = al.util.fit.residual_map_from(
                data=image, model_data=model_image
            )

            assert residual_map.in_2d == pytest.approx(fit.residual_map.in_2d)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=hyper_noise_map
            )

            assert normalized_residual_map.in_2d == pytest.approx(
                fit.normalized_residual_map.in_2d
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=hyper_noise_map
            )

            assert chi_squared_map.in_2d == pytest.approx(fit.chi_squared_map.in_2d)

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=hyper_noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)
            assert log_likelihood == fit.figure_of_merit

        def test___blurred_and_model_images_of_planes_and_unmasked_blurred_image_properties(
            self, masked_imaging_7x7
        ):

            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                mass_profile=al.mp.SphericalIsothermal(einstein_radius=1.0),
            )

            g1 = al.Galaxy(
                redshift=1.0, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            blurred_images_of_planes = tracer.blurred_images_of_planes_from_grid_and_convolver(
                grid=masked_imaging_7x7.grid,
                convolver=masked_imaging_7x7.convolver,
                blurring_grid=masked_imaging_7x7.blurring_grid,
            )

            assert blurred_images_of_planes[0].in_2d == pytest.approx(
                fit.model_images_of_planes[0].in_2d, 1.0e-4
            )

            assert blurred_images_of_planes[1].in_2d == pytest.approx(
                fit.model_images_of_planes[1].in_2d, 1.0e-4
            )

            unmasked_blurred_image = tracer.unmasked_blurred_image_from_grid_and_psf(
                grid=masked_imaging_7x7.grid, psf=masked_imaging_7x7.psf
            )

            assert (unmasked_blurred_image == fit.unmasked_blurred_image).all()

            unmasked_blurred_image_of_planes = tracer.unmasked_blurred_image_of_planes_from_grid_and_psf(
                grid=masked_imaging_7x7.grid, psf=masked_imaging_7x7.psf
            )

            assert (
                unmasked_blurred_image_of_planes[0]
                == fit.unmasked_blurred_image_of_planes[0]
            ).all()
            assert (
                unmasked_blurred_image_of_planes[1]
                == fit.unmasked_blurred_image_of_planes[1]
            ).all()

            unmasked_blurred_image_of_galaxies = tracer.unmasked_blurred_image_of_planes_and_galaxies_from_grid_and_psf(
                grid=masked_imaging_7x7.grid, psf=masked_imaging_7x7.psf
            )

            assert (
                unmasked_blurred_image_of_galaxies[0][0]
                == fit.unmasked_blurred_image_of_planes_and_galaxies[0][0]
            ).all()
            assert (
                unmasked_blurred_image_of_galaxies[1][0]
                == fit.unmasked_blurred_image_of_planes_and_galaxies[1][0]
            ).all()

    class TestCompareToManualInversionOnly:
        def test___all_lens_fit_quantities__no_hyper_methods(self, masked_imaging_7x7):

            # Ensures the inversion grid is used, as this would cause the test to fail.
            masked_imaging_7x7.grid[0, 0] = -100.0

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)

            g0 = al.Galaxy(redshift=0.5, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[al.Galaxy(redshift=0.5), g0])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_imaging_7x7.grid_inversion, sparse_grid=None
            )
            inversion = inversions.InversionImagingMatrix.from_data_mapper_and_regularization(
                mapper=mapper,
                regularization=reg,
                image=masked_imaging_7x7.image,
                noise_map=masked_imaging_7x7.noise_map,
                convolver=masked_imaging_7x7.convolver,
            )

            assert inversion.mapped_reconstructed_image.in_2d == pytest.approx(
                fit.model_image.in_2d, 1.0e-4
            )

            residual_map = al.util.fit.residual_map_from(
                data=masked_imaging_7x7.image,
                model_data=inversion.mapped_reconstructed_image,
            )

            assert residual_map.in_2d == pytest.approx(fit.residual_map.in_2d, 1.0e-4)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=masked_imaging_7x7.noise_map
            )

            assert normalized_residual_map.in_2d == pytest.approx(
                fit.normalized_residual_map.in_2d, 1.0e-4
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=masked_imaging_7x7.noise_map
            )

            assert chi_squared_map.in_2d == pytest.approx(
                fit.chi_squared_map.in_2d, 1.0e-4
            )

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=masked_imaging_7x7.noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)

            log_likelihood_with_regularization = al.util.fit.log_likelihood_with_regularization_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                noise_normalization=noise_normalization,
            )

            assert log_likelihood_with_regularization == pytest.approx(
                fit.log_likelihood_with_regularization, 1e-4
            )

            log_evidence = al.util.fit.log_evidence_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                log_curvature_regularization_term=inversion.log_det_curvature_reg_matrix_term,
                log_regularization_term=inversion.log_det_regularization_matrix_term,
                noise_normalization=noise_normalization,
            )

            assert log_evidence == fit.log_evidence
            assert log_evidence == fit.figure_of_merit

        def test___lens_fit_galaxy_model_image_dict__has_inversion_mapped_reconstructed_image(
            self, masked_imaging_7x7
        ):
            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)

            g0 = al.Galaxy(redshift=0.5)
            g1 = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_imaging_7x7.grid, sparse_grid=None
            )

            inversion = inversions.InversionImagingMatrix.from_data_mapper_and_regularization(
                mapper=mapper,
                regularization=reg,
                image=masked_imaging_7x7.image,
                noise_map=masked_imaging_7x7.noise_map,
                convolver=masked_imaging_7x7.convolver,
            )

            assert (fit.galaxy_model_image_dict[g0] == np.zeros(9)).all()

            assert fit.galaxy_model_image_dict[g1].in_2d == pytest.approx(
                inversion.mapped_reconstructed_image.in_2d, 1.0e-4
            )

            assert fit.model_image.in_2d == pytest.approx(
                fit.galaxy_model_image_dict[g1].in_2d, 1.0e-4
            )

        def test___all_lens_fit_quantities__include_hyper_methods(
            self, masked_imaging_7x7
        ):

            hyper_image_sky = al.hyper_data.HyperImageSky(sky_scale=1.0)

            hyper_background_noise = al.hyper_data.HyperBackgroundNoise(noise_scale=1.0)

            image = hyper_image_sky.hyper_image_from_image(
                image=masked_imaging_7x7.image
            )

            hyper_noise_map_background = hyper_background_noise.hyper_noise_map_from_noise_map(
                noise_map=masked_imaging_7x7.noise_map
            )

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)

            g0 = al.Galaxy(
                redshift=0.5,
                pixelization=pix,
                regularization=reg,
                hyper_galaxy=al.HyperGalaxy(
                    contribution_factor=1.0, noise_factor=1.0, noise_power=1.0
                ),
                hyper_model_image=np.ones(9),
                hyper_galaxy_image=np.ones(9),
                hyper_minimum_value=0.0,
            )

            tracer = al.Tracer.from_galaxies(galaxies=[al.Galaxy(redshift=0.5), g0])

            fit = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                hyper_image_sky=hyper_image_sky,
                hyper_background_noise=hyper_background_noise,
            )

            hyper_noise = tracer.hyper_noise_map_from_noise_map(
                noise_map=masked_imaging_7x7.noise_map
            )
            hyper_noise_map = hyper_noise_map_background + hyper_noise

            assert hyper_noise_map.in_2d == pytest.approx(fit.noise_map.in_2d)

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_imaging_7x7.grid,
                settings=al.SettingsPixelization(use_border=False),
            )
            inversion = inversions.InversionImagingMatrix.from_data_mapper_and_regularization(
                mapper=mapper,
                regularization=reg,
                image=image,
                noise_map=hyper_noise_map,
                convolver=masked_imaging_7x7.convolver,
            )

            assert inversion.mapped_reconstructed_image.in_2d == pytest.approx(
                fit.model_image.in_2d, 1.0e-4
            )

            residual_map = al.util.fit.residual_map_from(
                data=image, model_data=inversion.mapped_reconstructed_image
            )

            assert residual_map.in_2d == pytest.approx(fit.residual_map.in_2d)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=hyper_noise_map
            )

            assert normalized_residual_map.in_2d == pytest.approx(
                fit.normalized_residual_map.in_2d
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=hyper_noise_map
            )

            assert chi_squared_map.in_2d == pytest.approx(fit.chi_squared_map.in_2d)

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=hyper_noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)

            log_likelihood_with_regularization = al.util.fit.log_likelihood_with_regularization_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                noise_normalization=noise_normalization,
            )

            assert log_likelihood_with_regularization == pytest.approx(
                fit.log_likelihood_with_regularization, 1e-4
            )

            log_evidence = al.util.fit.log_evidence_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                log_curvature_regularization_term=inversion.log_det_curvature_reg_matrix_term,
                log_regularization_term=inversion.log_det_regularization_matrix_term,
                noise_normalization=noise_normalization,
            )

            assert log_evidence == fit.log_evidence
            assert log_evidence == fit.figure_of_merit

        def test___blurred_and_model_images_of_planes_and_unmasked_blurred_image_properties(
            self, masked_imaging_7x7
        ):

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)

            g0 = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[al.Galaxy(redshift=0.5), g0])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_imaging_7x7.grid,
                settings=al.SettingsPixelization(use_border=False),
            )

            inversion = inversions.InversionImagingMatrix.from_data_mapper_and_regularization(
                mapper=mapper,
                regularization=reg,
                image=masked_imaging_7x7.image,
                noise_map=masked_imaging_7x7.noise_map,
                convolver=masked_imaging_7x7.convolver,
            )

            assert (fit.model_images_of_planes[0].in_2d == np.zeros((7, 7))).all()
            assert inversion.mapped_reconstructed_image.in_2d == pytest.approx(
                fit.model_images_of_planes[1].in_2d, 1.0e-4
            )

        def test___stochastic_mode_gives_different_log_likelihoods(
            self, masked_imaging_7x7
        ):

            # Ensures the inversion grid is used, as this would cause the test to fail.
            masked_imaging_7x7.grid[0, 0] = -100.0

            pix = al.pix.VoronoiBrightnessImage(pixels=9)
            reg = al.reg.Constant(coefficient=1.0)

            g0 = al.Galaxy(
                redshift=0.5,
                pixelization=pix,
                regularization=reg,
                hyper_model_image=al.Array.ones(shape_2d=(3, 3)),
                hyper_galaxy_image=al.Array.ones(shape_2d=(3, 3)),
            )

            tracer = al.Tracer.from_galaxies(galaxies=[al.Galaxy(redshift=0.5), g0])

            fit_0 = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                settings_pixelization=al.SettingsPixelization(is_stochastic=False),
            )
            fit_1 = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                settings_pixelization=al.SettingsPixelization(is_stochastic=False),
            )

            assert fit_0.log_evidence == fit_1.log_evidence

            fit_0 = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                settings_pixelization=al.SettingsPixelization(is_stochastic=True),
            )
            fit_1 = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                settings_pixelization=al.SettingsPixelization(is_stochastic=True),
            )

            assert fit_0.log_evidence != fit_1.log_evidence

    class TestCompareToManualProfilesAndInversion:
        def test___all_lens_fit_quantities__no_hyper_methods(self, masked_imaging_7x7):
            galaxy_light = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)
            galaxy_pix = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[galaxy_light, galaxy_pix])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            blurred_image = tracer.blurred_image_from_grid_and_convolver(
                grid=masked_imaging_7x7.grid,
                convolver=masked_imaging_7x7.convolver,
                blurring_grid=masked_imaging_7x7.blurring_grid,
            )

            assert blurred_image.in_2d == pytest.approx(fit.blurred_image.in_2d)

            profile_subtracted_image = masked_imaging_7x7.image - blurred_image

            assert profile_subtracted_image.in_2d == pytest.approx(
                fit.profile_subtracted_image.in_2d
            )

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_imaging_7x7.grid,
                settings=al.SettingsPixelization(use_border=False),
            )

            inversion = inversions.InversionImagingMatrix.from_data_mapper_and_regularization(
                image=profile_subtracted_image,
                noise_map=masked_imaging_7x7.noise_map,
                convolver=masked_imaging_7x7.convolver,
                mapper=mapper,
                regularization=reg,
            )

            model_image = blurred_image + inversion.mapped_reconstructed_image

            assert model_image.in_2d == pytest.approx(fit.model_image.in_2d)

            residual_map = al.util.fit.residual_map_from(
                data=masked_imaging_7x7.image, model_data=model_image
            )

            assert residual_map.in_2d == pytest.approx(fit.residual_map.in_2d)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=masked_imaging_7x7.noise_map
            )

            assert normalized_residual_map.in_2d == pytest.approx(
                fit.normalized_residual_map.in_2d
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=masked_imaging_7x7.noise_map
            )

            assert chi_squared_map.in_2d == pytest.approx(fit.chi_squared_map.in_2d)

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=masked_imaging_7x7.noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)

            log_likelihood_with_regularization = al.util.fit.log_likelihood_with_regularization_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                noise_normalization=noise_normalization,
            )

            assert log_likelihood_with_regularization == pytest.approx(
                fit.log_likelihood_with_regularization, 1e-4
            )

            log_evidence = al.util.fit.log_evidence_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                log_curvature_regularization_term=inversion.log_det_curvature_reg_matrix_term,
                log_regularization_term=inversion.log_det_regularization_matrix_term,
                noise_normalization=noise_normalization,
            )

            assert log_evidence == fit.log_evidence
            assert log_evidence == fit.figure_of_merit

        def test___lens_fit_galaxy_model_image_dict__has_blurred_images_and_inversion_mapped_reconstructed_image(
            self, masked_imaging_7x7
        ):

            g0 = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )
            g1 = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=2.0)
            )
            g2 = al.Galaxy(redshift=0.5)

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)
            galaxy_pix = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2, galaxy_pix])

            masked_imaging_7x7.image[0] = 3.0

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            traced_grids = tracer.traced_grids_of_planes_from_grid(
                grid=masked_imaging_7x7.grid
            )
            traced_blurring_grids = tracer.traced_grids_of_planes_from_grid(
                grid=masked_imaging_7x7.blurring_grid
            )

            g0_image = g0.image_from_grid(grid=traced_grids[0])
            g0_blurring_image = g0.image_from_grid(grid=traced_blurring_grids[0])

            g0_blurred_image = masked_imaging_7x7.convolver.convolved_image_from_image_and_blurring_image(
                image=g0_image, blurring_image=g0_blurring_image
            )

            g1_image = g1.image_from_grid(grid=traced_grids[1])
            g1_blurring_image = g1.image_from_grid(grid=traced_blurring_grids[1])

            g1_blurred_image = masked_imaging_7x7.convolver.convolved_image_from_image_and_blurring_image(
                image=g1_image, blurring_image=g1_blurring_image
            )

            blurred_image = g0_blurred_image + g1_blurred_image

            profile_subtracted_image = masked_imaging_7x7.image - blurred_image

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_imaging_7x7.grid,
                settings=al.SettingsPixelization(use_border=False),
            )

            inversion = inversions.InversionImagingMatrix.from_data_mapper_and_regularization(
                image=profile_subtracted_image,
                noise_map=masked_imaging_7x7.noise_map,
                convolver=masked_imaging_7x7.convolver,
                mapper=mapper,
                regularization=reg,
            )

            assert (fit.galaxy_model_image_dict[g2] == np.zeros(9)).all()

            assert fit.galaxy_model_image_dict[g0].in_2d == pytest.approx(
                g0_blurred_image.in_2d, 1.0e-4
            )
            assert fit.galaxy_model_image_dict[g1].in_2d == pytest.approx(
                g1_blurred_image.in_2d, 1.0e-4
            )
            assert fit.galaxy_model_image_dict[galaxy_pix].in_2d == pytest.approx(
                inversion.mapped_reconstructed_image.in_2d, 1.0e-4
            )

            assert fit.model_image.in_2d == pytest.approx(
                fit.galaxy_model_image_dict[g0].in_2d
                + fit.galaxy_model_image_dict[g1].in_2d
                + inversion.mapped_reconstructed_image.in_2d,
                1.0e-4,
            )

        def test___all_lens_fit_quantities__include_hyper_methods(
            self, masked_imaging_7x7
        ):

            hyper_image_sky = al.hyper_data.HyperImageSky(sky_scale=1.0)

            hyper_background_noise = al.hyper_data.HyperBackgroundNoise(noise_scale=1.0)

            image = hyper_image_sky.hyper_image_from_image(
                image=masked_imaging_7x7.image
            )

            hyper_noise_map_background = hyper_background_noise.hyper_noise_map_from_noise_map(
                noise_map=masked_imaging_7x7.noise_map
            )

            galaxy_light = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                hyper_galaxy=al.HyperGalaxy(
                    contribution_factor=1.0, noise_factor=1.0, noise_power=1.0
                ),
                hyper_model_image=al.Array.ones(shape_2d=(3, 3)),
                hyper_galaxy_image=al.Array.ones(shape_2d=(3, 3)),
                hyper_minimum_value=0.0,
            )

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)
            galaxy_pix = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[galaxy_light, galaxy_pix])

            fit = al.FitImaging(
                masked_imaging=masked_imaging_7x7,
                tracer=tracer,
                hyper_image_sky=hyper_image_sky,
                hyper_background_noise=hyper_background_noise,
            )

            hyper_noise = tracer.hyper_noise_map_from_noise_map(
                noise_map=masked_imaging_7x7.noise_map
            )
            hyper_noise_map = hyper_noise_map_background + hyper_noise

            assert hyper_noise_map.in_2d == pytest.approx(fit.noise_map.in_2d, 1.0e-4)

            blurred_image = tracer.blurred_image_from_grid_and_convolver(
                grid=masked_imaging_7x7.grid,
                convolver=masked_imaging_7x7.convolver,
                blurring_grid=masked_imaging_7x7.blurring_grid,
            )

            assert blurred_image.in_2d == pytest.approx(fit.blurred_image.in_2d)

            profile_subtracted_image = image - blurred_image

            assert profile_subtracted_image.in_2d == pytest.approx(
                fit.profile_subtracted_image.in_2d
            )

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_imaging_7x7.grid,
                settings=al.SettingsPixelization(use_border=False),
            )

            inversion = inversions.InversionImagingMatrix.from_data_mapper_and_regularization(
                image=profile_subtracted_image,
                noise_map=hyper_noise_map,
                convolver=masked_imaging_7x7.convolver,
                mapper=mapper,
                regularization=reg,
            )

            model_image = blurred_image + inversion.mapped_reconstructed_image

            assert model_image.in_2d == pytest.approx(fit.model_image.in_2d, 1.0e-4)

            residual_map = al.util.fit.residual_map_from(
                data=image, model_data=model_image
            )

            assert residual_map.in_2d == pytest.approx(fit.residual_map.in_2d, 1.0e-4)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=hyper_noise_map
            )

            assert normalized_residual_map.in_2d == pytest.approx(
                fit.normalized_residual_map.in_2d, 1.0e-4
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=hyper_noise_map
            )

            assert chi_squared_map.in_2d == pytest.approx(
                fit.chi_squared_map.in_2d, 1.0e-4
            )

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=hyper_noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)

            log_likelihood_with_regularization = al.util.fit.log_likelihood_with_regularization_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                noise_normalization=noise_normalization,
            )

            assert log_likelihood_with_regularization == pytest.approx(
                fit.log_likelihood_with_regularization, 1e-4
            )

            log_evidence = al.util.fit.log_evidence_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                log_curvature_regularization_term=inversion.log_det_curvature_reg_matrix_term,
                log_regularization_term=inversion.log_det_regularization_matrix_term,
                noise_normalization=noise_normalization,
            )

            assert log_evidence == fit.log_evidence
            assert log_evidence == fit.figure_of_merit

        def test___blurred_and_model_images_of_planes_and_unmasked_blurred_image_properties(
            self, masked_imaging_7x7
        ):
            galaxy_light = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)
            galaxy_pix = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[galaxy_light, galaxy_pix])

            fit = al.FitImaging(masked_imaging=masked_imaging_7x7, tracer=tracer)

            blurred_image = tracer.blurred_image_from_grid_and_convolver(
                grid=masked_imaging_7x7.grid,
                convolver=masked_imaging_7x7.convolver,
                blurring_grid=masked_imaging_7x7.blurring_grid,
            )

            profile_subtracted_image = masked_imaging_7x7.image - blurred_image

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_imaging_7x7.grid,
                settings=al.SettingsPixelization(use_border=False),
            )

            inversion = inversions.InversionImagingMatrix.from_data_mapper_and_regularization(
                image=profile_subtracted_image,
                noise_map=masked_imaging_7x7.noise_map,
                convolver=masked_imaging_7x7.convolver,
                mapper=mapper,
                regularization=reg,
            )

            assert blurred_image.in_2d == pytest.approx(
                fit.model_images_of_planes[0].in_2d, 1.0e-4
            )
            assert inversion.mapped_reconstructed_image.in_2d == pytest.approx(
                fit.model_images_of_planes[1].in_2d, 1.0e-4
            )


class TestFitInterferometer:
    class TestFitProperties:
        def test__total_inversions(self, masked_interferometer_7):
            g0 = al.Galaxy(redshift=0.5)

            g1 = al.Galaxy(redshift=1.0)

            g2 = al.Galaxy(redshift=2.0)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7, tracer=tracer
            )

            assert fit.total_inversions == 0

            g2 = al.Galaxy(
                redshift=2.0,
                pixelization=al.pix.Rectangular(),
                regularization=al.reg.Constant(),
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7, tracer=tracer
            )

            assert fit.total_inversions == 1

            g0 = al.Galaxy(
                redshift=0.5,
                pixelization=al.pix.Rectangular(),
                regularization=al.reg.Constant(),
            )

            g1 = al.Galaxy(
                redshift=1.0,
                pixelization=al.pix.Rectangular(),
                regularization=al.reg.Constant(),
            )

            g2 = al.Galaxy(
                redshift=2.0,
                pixelization=al.pix.Rectangular(),
                regularization=al.reg.Constant(),
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7, tracer=tracer
            )

            assert fit.total_inversions == 3

    class TestLikelihood:
        def test__1x2_image__1x2_visibilities__simple_fourier_transform(self):
            # The image plane image generated by the galaxy is [1.0, 1.0]

            # Thus the chi squared is 4.0**2.0 + 3.0**2.0 = 25.0

            uv_wavelengths = np.array([[0.0, 0.0]])

            interferometer = al.Interferometer(
                visibilities=al.Visibilities.full(fill_value=5.0, shape_1d=(1,)),
                noise_map=al.Visibilities.ones(shape_1d=(1,)),
                uv_wavelengths=uv_wavelengths,
            )

            interferometer.visibilities[0, 1] = 4.0

            visibilities_mask = np.full(fill_value=False, shape=(1, 2))

            real_space_mask = al.Mask.manual(
                mask=np.array(
                    [
                        [True, True, True, True],
                        [True, False, False, True],
                        [True, True, True, True],
                    ]
                ),
                pixel_scales=1.0,
            )

            masked_interferometer = al.MaskedInterferometer(
                interferometer=interferometer,
                visibilities_mask=visibilities_mask,
                real_space_mask=real_space_mask,
                settings=al.SettingsMaskedInterferometer(
                    sub_size=1, transformer_class=al.TransformerDFT
                ),
            )

            # Setup as a ray trace instance, using a light profile for the lens

            g0 = al.Galaxy(
                redshift=0.5, light_profile=MockLightProfile(value=1.0, size=2)
            )
            tracer = al.Tracer.from_galaxies(galaxies=[g0])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer, tracer=tracer
            )

            assert (fit.visibilities_mask == np.array([False, False])).all()

            assert (fit.visibilities.in_1d == np.array([[5.0, 4.0]])).all()
            assert (fit.noise_map.in_1d == np.array([[1.0, 1.0]])).all()
            assert (fit.model_visibilities.in_1d == np.array([2.0, 0.0])).all()
            assert (fit.residual_map.in_1d == np.array([3.0, 4.0])).all()
            assert (fit.normalized_residual_map.in_1d == np.array([3.0, 4.0])).all()
            assert (fit.chi_squared_map.in_1d == np.array([9.0, 16.0])).all()

            assert fit.chi_squared == 25.0
            assert fit.reduced_chi_squared == 25.0 / 2.0
            assert fit.noise_normalization == (2.0 * np.log(2 * np.pi * 1.0 ** 2.0))
            assert fit.log_likelihood == -0.5 * (
                25.0 + 2.0 * np.log(2 * np.pi * 1.0 ** 2.0)
            )

        def test__3x2_image__2x2_visibilities__use_transformer_for_likelihood(self):

            uv_wavelengths = np.array([[1.0, 0.0], [1.0, 1.0], [2.0, 2.0]])

            interferometer = al.Interferometer(
                visibilities=al.Visibilities.full(fill_value=5.0, shape_1d=(3,)),
                noise_map=al.Visibilities.full(fill_value=2.0, shape_1d=(3,)),
                uv_wavelengths=uv_wavelengths,
            )

            real_space_mask = al.Mask.unmasked(shape_2d=(1, 3), pixel_scales=1.0)

            transformer = al.TransformerDFT(
                uv_wavelengths=uv_wavelengths, real_space_mask=real_space_mask
            )

            visibilities_mask = np.full(fill_value=False, shape=(3, 2))

            real_space_mask = al.Mask.manual(
                mask=np.array(
                    [
                        [True, True, True, True, True],
                        [True, False, False, False, True],
                        [True, True, True, True, True],
                    ]
                ),
                pixel_scales=1.0,
            )

            masked_interferometer = al.MaskedInterferometer(
                interferometer=interferometer,
                visibilities_mask=visibilities_mask,
                real_space_mask=real_space_mask,
                settings=al.SettingsMaskedInterferometer(
                    sub_size=1, transformer_class=al.TransformerDFT
                ),
            )

            # Setup as a ray trace instance, using a light profile for the lens

            g0 = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=0.001)
            )

            image = g0.image_from_grid(grid=masked_interferometer.grid)

            model_visibilities_manual = transformer.visibilities_from_image(image=image)

            tracer = al.Tracer.from_galaxies(galaxies=[g0])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer, tracer=tracer
            )

            assert (fit.visibilities_mask == np.array([False, False])).all()

            assert (
                fit.visibilities.in_1d
                == np.array([[[5.0, 5.0], [5.0, 5.0], [5.0, 5.0]]])
            ).all()
            assert (
                fit.noise_map.in_1d == np.array([[[2.0, 2.0], [2.0, 2.0], [2.0, 2.0]]])
            ).all()

            assert fit.model_visibilities.in_1d == pytest.approx(
                model_visibilities_manual, 1.0e-4
            )

            # moddel visibilities are all [1.94805, 0.0]

            assert fit.residual_map.in_1d == pytest.approx(
                np.array([[3.0519, 5.0], [3.0519, 5.0], [3.0519, 5.0]]), 1.0e-4
            )

            assert fit.normalized_residual_map.in_1d == pytest.approx(
                np.array(
                    [
                        [3.0519 / 2.0, 5.0 / 2.0],
                        [3.0519 / 2.0, 5.0 / 2.0],
                        [3.0519 / 2.0, 5.0 / 2.0],
                    ]
                ),
                1.0e-4,
            )

            assert fit.chi_squared_map.in_1d == pytest.approx(
                np.array(
                    [
                        [(3.0519 / 2.0) ** 2.0, (5.0 / 2.0) ** 2.0],
                        [(3.0519 / 2.0) ** 2.0, (5.0 / 2.0) ** 2.0],
                        [(3.0519 / 2.0) ** 2.0, (5.0 / 2.0) ** 2.0],
                    ]
                ),
                1.0e-4,
            )

            assert fit.chi_squared == pytest.approx(25.73579, 1.0e-4)
            assert fit.reduced_chi_squared == pytest.approx(25.73579 / 6.0)
            assert fit.noise_normalization == pytest.approx(
                (6.0 * np.log(2 * np.pi * 2.0 ** 2.0)), 1.0e-4
            )
            assert fit.log_likelihood == pytest.approx(
                -0.5 * (25.73579 + 6.0 * np.log(2 * np.pi * 2.0 ** 2.0)), 1.0e-4
            )

        def test__hyper_background_changes_background_sky__reflected_in_likelihood(
            self
        ):

            uv_wavelengths = np.array([[1.0, 0.0], [1.0, 1.0], [2.0, 2.0]])

            interferometer = al.Interferometer(
                visibilities=al.Visibilities.full(fill_value=5.0, shape_1d=(3,)),
                noise_map=al.Visibilities.full(fill_value=2.0, shape_1d=(3,)),
                uv_wavelengths=uv_wavelengths,
            )

            visibilities_mask = np.full(fill_value=False, shape=(1, 2))

            real_space_mask = al.Mask.manual(
                mask=np.array(
                    [
                        [True, True, True, True, True],
                        [True, False, False, False, True],
                        [True, True, True, True, True],
                    ]
                ),
                pixel_scales=1.0,
            )

            masked_interferometer = al.MaskedInterferometer(
                interferometer=interferometer,
                visibilities_mask=visibilities_mask,
                real_space_mask=real_space_mask,
                settings=al.SettingsMaskedInterferometer(sub_size=1),
            )

            # Setup as a ray trace instance, using a light profile for the lens

            g0 = al.Galaxy(
                redshift=0.5, light_profile=MockLightProfile(value=1.0, size=2)
            )
            tracer = al.Tracer.from_galaxies(galaxies=[g0])

            hyper_background_noise = al.hyper_data.HyperBackgroundNoise(noise_scale=1.0)

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer,
                tracer=tracer,
                hyper_background_noise=hyper_background_noise,
            )

            assert (
                fit.visibilities.in_1d == np.full(fill_value=5.0, shape=(3, 2))
            ).all()

            assert (fit.noise_map.in_1d == np.full(fill_value=3.0, shape=(3, 2))).all()

    class TestCompareToManualProfilesOnly:
        def test___all_lens_fit_quantities__no_hyper_methods(
            self, masked_interferometer_7
        ):
            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                mass_profile=al.mp.SphericalIsothermal(einstein_radius=1.0),
            )

            g1 = al.Galaxy(
                redshift=1.0, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7, tracer=tracer
            )

            assert masked_interferometer_7.noise_map == pytest.approx(fit.noise_map)

            model_visibilities = tracer.profile_visibilities_from_grid_and_transformer(
                grid=masked_interferometer_7.grid,
                transformer=masked_interferometer_7.transformer,
            )

            assert model_visibilities == pytest.approx(fit.model_visibilities, 1e-4)

            residual_map = al.util.fit.residual_map_from(
                data=masked_interferometer_7.visibilities, model_data=model_visibilities
            )

            assert residual_map == pytest.approx(fit.residual_map, 1e-4)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=masked_interferometer_7.noise_map
            )

            assert normalized_residual_map == pytest.approx(
                fit.normalized_residual_map, 1e-4
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=masked_interferometer_7.noise_map
            )

            assert chi_squared_map == pytest.approx(fit.chi_squared_map, 1e-4)

            chi_squared = al.util.fit.chi_squared_from(
                chi_squared_map=fit.chi_squared_map
            )

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=masked_interferometer_7.noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)
            assert log_likelihood == fit.figure_of_merit

        def test___lens_fit_galaxy_model_image_dict__corresponds_to_profile_galaxy_images(
            self, masked_interferometer_7_grid
        ):
            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                mass_profile=al.mp.SphericalIsothermal(einstein_radius=1.0),
            )
            g1 = al.Galaxy(
                redshift=1.0, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )
            g2 = al.Galaxy(redshift=1.0)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7_grid, tracer=tracer
            )

            traced_grids_of_planes = tracer.traced_grids_of_planes_from_grid(
                grid=masked_interferometer_7_grid.grid
            )

            g0_image = g0.image_from_grid(grid=traced_grids_of_planes[0])

            g1_image = g1.image_from_grid(grid=traced_grids_of_planes[1])

            assert fit.galaxy_model_image_dict[g0].in_1d == pytest.approx(
                g0_image, 1.0e-4
            )
            assert fit.galaxy_model_image_dict[g1].in_1d == pytest.approx(
                g1_image, 1.0e-4
            )

        def test___lens_fit_galaxy_visibilities_dict__corresponds_to_galaxy_visibilities(
            self, masked_interferometer_7_grid
        ):
            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                mass_profile=al.mp.SphericalIsothermal(einstein_radius=1.0),
            )
            g1 = al.Galaxy(
                redshift=1.0, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )
            g2 = al.Galaxy(redshift=1.0)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7_grid, tracer=tracer
            )

            traced_grids_of_planes = tracer.traced_grids_of_planes_from_grid(
                grid=masked_interferometer_7_grid.grid
            )

            g0_profile_visibilities = g0.profile_visibilities_from_grid_and_transformer(
                grid=traced_grids_of_planes[0],
                transformer=masked_interferometer_7_grid.transformer,
            )

            g1_profile_visibilities = g1.profile_visibilities_from_grid_and_transformer(
                grid=traced_grids_of_planes[1],
                transformer=masked_interferometer_7_grid.transformer,
            )

            assert fit.galaxy_model_visibilities_dict[g0].in_1d == pytest.approx(
                g0_profile_visibilities, 1.0e-4
            )
            assert fit.galaxy_model_visibilities_dict[g1].in_1d == pytest.approx(
                g1_profile_visibilities, 1.0e-4
            )
            assert (
                fit.galaxy_model_visibilities_dict[g2].in_1d == np.zeros((7, 2))
            ).all()

            assert fit.model_visibilities.in_1d == pytest.approx(
                fit.galaxy_model_visibilities_dict[g0].in_1d
                + fit.galaxy_model_visibilities_dict[g1].in_1d,
                1.0e-4,
            )

        def test___all_lens_fit_quantities__hyper_background_noise(
            self, masked_interferometer_7
        ):

            hyper_background_noise = al.hyper_data.HyperBackgroundNoise(noise_scale=1.0)

            hyper_noise_map = hyper_background_noise.hyper_noise_map_from_noise_map(
                noise_map=masked_interferometer_7.noise_map
            )

            g0 = al.Galaxy(
                redshift=0.5,
                light_profile=al.lp.EllipticalSersic(intensity=1.0),
                mass_profile=al.mp.SphericalIsothermal(einstein_radius=1.0),
            )

            g1 = al.Galaxy(
                redshift=1.0, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7,
                tracer=tracer,
                hyper_background_noise=hyper_background_noise,
            )

            assert hyper_noise_map.in_1d == pytest.approx(fit.noise_map.in_1d)

    class TestCompareToManualInversionOnly:
        def test___all_lens_fit_quantities__no_hyper_methods(
            self, masked_interferometer_7
        ):

            # Ensures the inversion grid is used, as this would cause the test to fail.
            masked_interferometer_7.grid[0, 0] = -100.0

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=0.01)

            g0 = al.Galaxy(redshift=0.5, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[al.Galaxy(redshift=0.5), g0])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7, tracer=tracer
            )

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_interferometer_7.grid_inversion, sparse_grid=None
            )

            inversion = inversions.InversionInterferometerMatrix.from_data_mapper_and_regularization(
                mapper=mapper,
                regularization=reg,
                visibilities=masked_interferometer_7.visibilities,
                noise_map=masked_interferometer_7.noise_map,
                transformer=masked_interferometer_7.transformer,
            )

            assert inversion.mapped_reconstructed_visibilities == pytest.approx(
                fit.model_visibilities, 1.0e-4
            )

            residual_map = al.util.fit.residual_map_from(
                data=masked_interferometer_7.visibilities,
                model_data=inversion.mapped_reconstructed_visibilities,
            )

            assert residual_map.in_1d == pytest.approx(fit.residual_map.in_1d, 1.0e-4)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=masked_interferometer_7.noise_map
            )

            assert normalized_residual_map.in_1d == pytest.approx(
                fit.normalized_residual_map.in_1d, 1.0e-4
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=masked_interferometer_7.noise_map
            )

            assert chi_squared_map.in_1d == pytest.approx(
                fit.chi_squared_map.in_1d, 1.0e-4
            )

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=masked_interferometer_7.noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)

            log_likelihood_with_regularization = al.util.fit.log_likelihood_with_regularization_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                noise_normalization=noise_normalization,
            )

            assert log_likelihood_with_regularization == pytest.approx(
                fit.log_likelihood_with_regularization, 1e-4
            )

            log_evidence = al.util.fit.log_evidence_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                log_curvature_regularization_term=inversion.log_det_curvature_reg_matrix_term,
                log_regularization_term=inversion.log_det_regularization_matrix_term,
                noise_normalization=noise_normalization,
            )

            assert log_evidence == fit.log_evidence
            assert log_evidence == fit.figure_of_merit

            mapped_reconstructed_image = al.util.inversion.mapped_reconstructed_data_from(
                mapping_matrix=fit.inversion.mapper.mapping_matrix,
                reconstruction=fit.inversion.reconstruction,
            )

            assert (
                fit.inversion.mapped_reconstructed_image.in_1d
                == mapped_reconstructed_image
            ).all()

        def test___lens_fit_galaxy_model_image_dict__images_and_inversion_mapped_reconstructed_image(
            self, masked_interferometer_7
        ):
            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)

            g0 = al.Galaxy(redshift=0.5)
            g1 = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7, tracer=tracer
            )

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_interferometer_7.grid, sparse_grid=None
            )

            inversion = inversions.InversionInterferometerMatrix.from_data_mapper_and_regularization(
                mapper=mapper,
                regularization=reg,
                visibilities=masked_interferometer_7.visibilities,
                noise_map=masked_interferometer_7.noise_map,
                transformer=masked_interferometer_7.transformer,
            )

            assert (fit.galaxy_model_image_dict[g0].in_2d == np.zeros((7, 7))).all()

            assert fit.galaxy_model_image_dict[g1].in_1d == pytest.approx(
                inversion.mapped_reconstructed_image.in_1d, 1.0e-4
            )

        def test___lens_fit_galaxy_model_visibilities_dict__has_inversion_mapped_reconstructed_visibilities(
            self, masked_interferometer_7
        ):
            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)

            g0 = al.Galaxy(redshift=0.5)
            g1 = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7, tracer=tracer
            )

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_interferometer_7.grid, sparse_grid=None
            )

            inversion = inversions.InversionInterferometerMatrix.from_data_mapper_and_regularization(
                mapper=mapper,
                regularization=reg,
                visibilities=masked_interferometer_7.visibilities,
                noise_map=masked_interferometer_7.noise_map,
                transformer=masked_interferometer_7.transformer,
            )

            assert (fit.galaxy_model_visibilities_dict[g0] == np.zeros((7, 2))).all()

            assert fit.galaxy_model_visibilities_dict[g1].in_1d == pytest.approx(
                inversion.mapped_reconstructed_visibilities.in_1d, 1.0e-4
            )

            assert fit.model_visibilities.in_1d == pytest.approx(
                fit.galaxy_model_visibilities_dict[g1].in_1d, 1.0e-4
            )

        def test___all_lens_fit_quantities__hyper_background_noise(
            self, masked_interferometer_7
        ):

            hyper_background_noise = al.hyper_data.HyperBackgroundNoise(noise_scale=1.0)

            hyper_noise_map = hyper_background_noise.hyper_noise_map_from_noise_map(
                noise_map=masked_interferometer_7.noise_map
            )

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=0.01)

            g0 = al.Galaxy(redshift=0.5, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[al.Galaxy(redshift=0.5), g0])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7,
                tracer=tracer,
                hyper_background_noise=hyper_background_noise,
            )

            assert hyper_noise_map.in_1d == pytest.approx(
                fit.inversion.noise_map, 1.0e-4
            )

            assert hyper_noise_map.in_1d == pytest.approx(fit.noise_map.in_1d)

        def test___stochastic_mode_gives_different_log_likelihoods(
            self, masked_interferometer_7
        ):

            # Ensures the inversion grid is used, as this would cause the test to fail.
            masked_interferometer_7.grid[0, 0] = -100.0

            pix = al.pix.VoronoiBrightnessImage(pixels=9)
            reg = al.reg.Constant(coefficient=1.0)

            g0 = al.Galaxy(
                redshift=0.5,
                pixelization=pix,
                regularization=reg,
                hyper_model_image=al.Array.ones(shape_2d=(3, 3)),
                hyper_galaxy_image=al.Array.ones(shape_2d=(3, 3)),
            )

            tracer = al.Tracer.from_galaxies(galaxies=[al.Galaxy(redshift=0.5), g0])

            fit_0 = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7,
                tracer=tracer,
                settings_pixelization=al.SettingsPixelization(is_stochastic=False),
            )
            fit_1 = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7,
                tracer=tracer,
                settings_pixelization=al.SettingsPixelization(is_stochastic=False),
            )

            assert fit_0.log_evidence == fit_1.log_evidence

            fit_0 = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7,
                tracer=tracer,
                settings_pixelization=al.SettingsPixelization(is_stochastic=True),
            )
            fit_1 = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7,
                tracer=tracer,
                settings_pixelization=al.SettingsPixelization(is_stochastic=True),
            )

            assert fit_0.log_evidence != fit_1.log_evidence

        def test___all_lens_fit_quantities__linear_operator(
            self, masked_interferometer_7
        ):

            # Ensures the inversion grid is used, as this would cause the test to fail.
            masked_interferometer_7.grid[0, 0] = -100.0

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=0.01)

            g0 = al.Galaxy(redshift=0.5, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[al.Galaxy(redshift=0.5), g0])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7,
                tracer=tracer,
                settings_inversion=al.SettingsInversion(use_linear_operators=True),
            )

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_interferometer_7.grid_inversion, sparse_grid=None
            )

            inversion = inversions.InversionInterferometerLinearOperator.from_data_mapper_and_regularization(
                mapper=mapper,
                regularization=reg,
                visibilities=masked_interferometer_7.visibilities,
                noise_map=masked_interferometer_7.noise_map,
                transformer=masked_interferometer_7.transformer,
                settings=al.SettingsInversion(use_linear_operators=True),
            )

            assert inversion.mapped_reconstructed_visibilities == pytest.approx(
                fit.model_visibilities, 1.0e-4
            )

            residual_map = al.util.fit.residual_map_from(
                data=masked_interferometer_7.visibilities,
                model_data=inversion.mapped_reconstructed_visibilities,
            )

            assert residual_map.in_1d == pytest.approx(fit.residual_map.in_1d, 1.0e-4)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=masked_interferometer_7.noise_map
            )

            assert normalized_residual_map.in_1d == pytest.approx(
                fit.normalized_residual_map.in_1d, 1.0e-4
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=masked_interferometer_7.noise_map
            )

            assert chi_squared_map.in_1d == pytest.approx(
                fit.chi_squared_map.in_1d, 1.0e-4
            )

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=masked_interferometer_7.noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)

            log_likelihood_with_regularization = al.util.fit.log_likelihood_with_regularization_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                noise_normalization=noise_normalization,
            )

            assert log_likelihood_with_regularization == pytest.approx(
                fit.log_likelihood_with_regularization, 1e-4
            )

            log_evidence = al.util.fit.log_evidence_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                log_curvature_regularization_term=inversion.log_det_curvature_reg_matrix_term,
                log_regularization_term=inversion.log_det_regularization_matrix_term,
                noise_normalization=noise_normalization,
            )

            assert log_evidence == fit.log_evidence
            assert log_evidence == fit.figure_of_merit

            mapped_reconstructed_image = al.util.inversion.mapped_reconstructed_data_from(
                mapping_matrix=fit.inversion.mapper.mapping_matrix,
                reconstruction=fit.inversion.reconstruction,
            )

            assert (
                fit.inversion.mapped_reconstructed_image.in_1d
                == mapped_reconstructed_image
            ).all()

    class TestCompareToManualProfilesAndInversion:
        def test___all_lens_fit_quantities__no_hyper_methods(
            self, masked_interferometer_7
        ):
            galaxy_light = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)
            galaxy_pix = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[galaxy_light, galaxy_pix])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7, tracer=tracer
            )

            profile_visibilities = tracer.profile_visibilities_from_grid_and_transformer(
                grid=masked_interferometer_7.grid,
                transformer=masked_interferometer_7.transformer,
            )

            assert profile_visibilities.in_1d == pytest.approx(
                fit.profile_visibilities.in_1d
            )

            profile_subtracted_visibilities = (
                masked_interferometer_7.visibilities - profile_visibilities
            )

            assert profile_subtracted_visibilities.in_1d == pytest.approx(
                fit.profile_subtracted_visibilities.in_1d
            )

            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_interferometer_7.grid,
                settings=al.SettingsPixelization(use_border=False),
            )

            inversion = inversions.InversionInterferometerMatrix.from_data_mapper_and_regularization(
                visibilities=profile_subtracted_visibilities,
                noise_map=masked_interferometer_7.noise_map,
                transformer=masked_interferometer_7.transformer,
                mapper=mapper,
                regularization=reg,
            )

            model_visibilities = (
                profile_visibilities + inversion.mapped_reconstructed_visibilities
            )

            assert model_visibilities.in_1d == pytest.approx(
                fit.model_visibilities.in_1d
            )

            residual_map = al.util.fit.residual_map_from(
                data=masked_interferometer_7.visibilities, model_data=model_visibilities
            )

            assert residual_map.in_1d == pytest.approx(fit.residual_map.in_1d)

            normalized_residual_map = al.util.fit.normalized_residual_map_from(
                residual_map=residual_map, noise_map=masked_interferometer_7.noise_map
            )

            assert normalized_residual_map.in_1d == pytest.approx(
                fit.normalized_residual_map.in_1d
            )

            chi_squared_map = al.util.fit.chi_squared_map_from(
                residual_map=residual_map, noise_map=masked_interferometer_7.noise_map
            )

            assert chi_squared_map.in_1d == pytest.approx(fit.chi_squared_map.in_1d)

            chi_squared = al.util.fit.chi_squared_from(chi_squared_map=chi_squared_map)

            noise_normalization = al.util.fit.noise_normalization_from(
                noise_map=masked_interferometer_7.noise_map
            )

            log_likelihood = al.util.fit.log_likelihood_from(
                chi_squared=chi_squared, noise_normalization=noise_normalization
            )

            assert log_likelihood == pytest.approx(fit.log_likelihood, 1e-4)

            log_likelihood_with_regularization = al.util.fit.log_likelihood_with_regularization_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                noise_normalization=noise_normalization,
            )

            assert log_likelihood_with_regularization == pytest.approx(
                fit.log_likelihood_with_regularization, 1e-4
            )

            log_evidence = al.util.fit.log_evidence_from(
                chi_squared=chi_squared,
                regularization_term=inversion.regularization_term,
                log_curvature_regularization_term=inversion.log_det_curvature_reg_matrix_term,
                log_regularization_term=inversion.log_det_regularization_matrix_term,
                noise_normalization=noise_normalization,
            )

            assert log_evidence == fit.log_evidence
            assert log_evidence == fit.figure_of_merit

            mapped_reconstructed_image = al.util.inversion.mapped_reconstructed_data_from(
                mapping_matrix=fit.inversion.mapper.mapping_matrix,
                reconstruction=fit.inversion.reconstruction,
            )

            assert (
                fit.inversion.mapped_reconstructed_image.in_1d
                == mapped_reconstructed_image
            ).all()

        def test___lens_fit_galaxy_model_visibilities_dict__has_image_and_inversion_mapped_reconstructed_image(
            self, masked_interferometer_7_grid
        ):

            g0 = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )
            g1 = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=2.0)
            )
            g2 = al.Galaxy(redshift=0.5)

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)
            galaxy_pix = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2, galaxy_pix])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7_grid, tracer=tracer
            )

            traced_grids = tracer.traced_grids_of_planes_from_grid(
                grid=masked_interferometer_7_grid.grid
            )

            g0_visibilities = g0.profile_visibilities_from_grid_and_transformer(
                grid=traced_grids[0],
                transformer=masked_interferometer_7_grid.transformer,
            )

            g1_visibilities = g1.profile_visibilities_from_grid_and_transformer(
                grid=traced_grids[1],
                transformer=masked_interferometer_7_grid.transformer,
            )

            profile_visibilities = g0_visibilities + g1_visibilities

            profile_subtracted_visibilities = (
                masked_interferometer_7_grid.visibilities - profile_visibilities
            )
            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_interferometer_7_grid.grid,
                settings=al.SettingsPixelization(use_border=False),
            )

            inversion = inversions.InversionInterferometerMatrix.from_data_mapper_and_regularization(
                visibilities=profile_subtracted_visibilities,
                noise_map=masked_interferometer_7_grid.noise_map,
                transformer=masked_interferometer_7_grid.transformer,
                mapper=mapper,
                regularization=reg,
            )

            g0_image = g0.image_from_grid(grid=traced_grids[0])

            g1_image = g1.image_from_grid(grid=traced_grids[1])

            assert (fit.galaxy_model_image_dict[g2].in_2d == np.zeros((7, 7))).all()

            assert fit.galaxy_model_image_dict[g0].in_1d == pytest.approx(
                g0_image.in_1d, 1.0e-4
            )
            assert fit.galaxy_model_image_dict[g1].in_1d == pytest.approx(
                g1_image.in_1d, 1.0e-4
            )
            assert fit.galaxy_model_image_dict[galaxy_pix].in_1d == pytest.approx(
                inversion.mapped_reconstructed_image.in_1d, 1.0e-4
            )

        def test___lens_fit_galaxy_model_visibilities_dict__has_profile_visibilitiess_and_inversion_mapped_reconstructed_visibilities(
            self, masked_interferometer_7_grid
        ):

            g0 = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )
            g1 = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=2.0)
            )
            g2 = al.Galaxy(redshift=0.5)

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)
            galaxy_pix = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[g0, g1, g2, galaxy_pix])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7_grid, tracer=tracer
            )

            traced_grids = tracer.traced_grids_of_planes_from_grid(
                grid=masked_interferometer_7_grid.grid
            )

            g0_visibilities = g0.profile_visibilities_from_grid_and_transformer(
                grid=traced_grids[0],
                transformer=masked_interferometer_7_grid.transformer,
            )

            g1_visibilities = g1.profile_visibilities_from_grid_and_transformer(
                grid=traced_grids[1],
                transformer=masked_interferometer_7_grid.transformer,
            )

            profile_visibilities = g0_visibilities + g1_visibilities

            profile_subtracted_visibilities = (
                masked_interferometer_7_grid.visibilities - profile_visibilities
            )
            mapper = pix.mapper_from_grid_and_sparse_grid(
                grid=masked_interferometer_7_grid.grid,
                settings=al.SettingsPixelization(use_border=False),
            )

            inversion = inversions.InversionInterferometerMatrix.from_data_mapper_and_regularization(
                visibilities=profile_subtracted_visibilities,
                noise_map=masked_interferometer_7_grid.noise_map,
                transformer=masked_interferometer_7_grid.transformer,
                mapper=mapper,
                regularization=reg,
            )

            assert (fit.galaxy_model_visibilities_dict[g2] == np.zeros((7, 2))).all()

            assert fit.galaxy_model_visibilities_dict[g0].in_1d == pytest.approx(
                g0_visibilities.in_1d, 1.0e-4
            )
            assert fit.galaxy_model_visibilities_dict[g1].in_1d == pytest.approx(
                g1_visibilities.in_1d, 1.0e-4
            )
            assert fit.galaxy_model_visibilities_dict[
                galaxy_pix
            ].in_1d == pytest.approx(
                inversion.mapped_reconstructed_visibilities.in_1d, 1.0e-4
            )

            assert fit.model_visibilities.in_1d == pytest.approx(
                fit.galaxy_model_visibilities_dict[g0].in_1d
                + fit.galaxy_model_visibilities_dict[g1].in_1d
                + inversion.mapped_reconstructed_visibilities.in_1d,
                1.0e-4,
            )

        def test___all_lens_fit_quantities__hyper_background_noise(
            self, masked_interferometer_7
        ):

            hyper_background_noise = al.hyper_data.HyperBackgroundNoise(noise_scale=1.0)

            hyper_noise_map = hyper_background_noise.hyper_noise_map_from_noise_map(
                noise_map=masked_interferometer_7.noise_map
            )

            galaxy_light = al.Galaxy(
                redshift=0.5, light_profile=al.lp.EllipticalSersic(intensity=1.0)
            )

            pix = al.pix.Rectangular(shape=(3, 3))
            reg = al.reg.Constant(coefficient=1.0)
            galaxy_pix = al.Galaxy(redshift=1.0, pixelization=pix, regularization=reg)

            tracer = al.Tracer.from_galaxies(galaxies=[galaxy_light, galaxy_pix])

            fit = al.FitInterferometer(
                masked_interferometer=masked_interferometer_7,
                tracer=tracer,
                hyper_background_noise=hyper_background_noise,
            )

            assert hyper_noise_map.in_1d == pytest.approx(
                fit.inversion.noise_map, 1.0e-4
            )

            assert hyper_noise_map.in_1d == pytest.approx(fit.noise_map.in_1d)
