From e447e3f027439dcad27db40041fd3b1715067d36 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Tue, 10 Mar 2026 08:07:16 +0000 Subject: [PATCH] refactor: split fit tests into granular focused tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split each monolithic test function into individual focused tests, one per property or behaviour being verified. Tests now fail with precise names that identify exactly which quantity is wrong (e.g. chi_squared, residual_map, log_evidence) rather than a single large function covering a dozen assertions. Changes per file: - test_fit_imaging.py: 3 tests → 21 tests; helper builders extracted to avoid repetition; dirty-image block was left as fixture-based tests - test_fit_interferometer.py: 4 tests → 27 tests; dirty quantities split into one test per map type - test_fit_dataset.py: 3 tests → 6 tests; figure_of_merit split by inversion regularization branch; chi_squared covariance tests separated - test_fit_util.py: 12 tests → 30 tests; each util function now has one test per scenario; parametrize not needed as inputs differ meaningfully Co-Authored-By: Claude Sonnet 4.6 --- test_autoarray/fit/test_fit_dataset.py | 49 +++- test_autoarray/fit/test_fit_imaging.py | 193 ++++++++++--- test_autoarray/fit/test_fit_interferometer.py | 257 +++++++++++++----- test_autoarray/fit/test_fit_util.py | 252 +++++++++-------- 4 files changed, 528 insertions(+), 223 deletions(-) diff --git a/test_autoarray/fit/test_fit_dataset.py b/test_autoarray/fit/test_fit_dataset.py index 674f25555..a9f24f490 100644 --- a/test_autoarray/fit/test_fit_dataset.py +++ b/test_autoarray/fit/test_fit_dataset.py @@ -3,7 +3,9 @@ import autoarray as aa -def test__figure_of_merit__with_inversion(masked_imaging_7x7, model_image_7x7): +def test__figure_of_merit__with_inversion_and_regularization__equals_log_evidence( + masked_imaging_7x7, model_image_7x7 +): inversion = aa.m.MockInversion( linear_obj_list=[aa.m.MockMapper(regularization=aa.m.MockRegularization())], data_vector=1, @@ -21,6 +23,10 @@ def test__figure_of_merit__with_inversion(masked_imaging_7x7, model_image_7x7): assert fit.figure_of_merit == fit.log_evidence + +def test__figure_of_merit__with_inversion_and_no_regularization__equals_log_likelihood( + masked_imaging_7x7, model_image_7x7 +): inversion = aa.m.MockInversion( linear_obj_list=[aa.m.MockLinearObj(regularization=None)], data_vector=1 ) @@ -35,8 +41,8 @@ def test__figure_of_merit__with_inversion(masked_imaging_7x7, model_image_7x7): assert fit.figure_of_merit == fit.log_likelihood -def test__figure_of_merit__with_noise_covariance_matrix_in_dataset( - masked_imaging_covariance_7x7, model_image_7x7, masked_imaging_7x7 +def test__chi_squared__with_noise_covariance_matrix__uses_covariance_weighted_formula( + masked_imaging_covariance_7x7, model_image_7x7 ): fit = aa.m.MockFitImaging( dataset=masked_imaging_covariance_7x7, @@ -51,21 +57,49 @@ def test__figure_of_merit__with_noise_covariance_matrix_in_dataset( assert fit.chi_squared == chi_squared + +def test__figure_of_merit__with_noise_covariance_matrix__equals_negative_half_chi_squared_plus_noise_normalization( + masked_imaging_covariance_7x7, model_image_7x7 +): + fit = aa.m.MockFitImaging( + dataset=masked_imaging_covariance_7x7, + use_mask_in_fit=False, + model_data=model_image_7x7, + ) + assert fit.figure_of_merit == pytest.approx( -0.5 * (fit.chi_squared + fit.noise_normalization), 1.0e-4 ) - fit = aa.m.MockFitImaging( - dataset=masked_imaging_7x7, + +def test__chi_squared__without_noise_covariance_matrix__differs_from_covariance_weighted_result( + masked_imaging_covariance_7x7, masked_imaging_7x7, model_image_7x7 +): + fit_with_covariance = aa.m.MockFitImaging( + dataset=masked_imaging_covariance_7x7, use_mask_in_fit=False, model_data=model_image_7x7, ) - assert fit.chi_squared != pytest.approx(chi_squared, 1.0e-4) + chi_squared_covariance = aa.util.fit.chi_squared_with_noise_covariance_from( + residual_map=fit_with_covariance.residual_map, + noise_covariance_matrix_inv=masked_imaging_covariance_7x7.noise_covariance_matrix_inv, + ) + fit_without_covariance = aa.m.MockFitImaging( + dataset=masked_imaging_7x7, + use_mask_in_fit=False, + model_data=model_image_7x7, + ) + + assert fit_without_covariance.chi_squared != pytest.approx( + chi_squared_covariance, 1.0e-4 + ) -def test__grid_offset_via_data_model(imaging_7x7, mask_2d_7x7, model_image_7x7): +def test__grids__with_dataset_model_grid_offset__lp_and_pixelization_grids_offset_correctly( + imaging_7x7, mask_2d_7x7, model_image_7x7 +): masked_imaging_7x7 = imaging_7x7.apply_mask(mask=mask_2d_7x7) fit = aa.m.MockFitImaging( @@ -76,6 +110,5 @@ def test__grid_offset_via_data_model(imaging_7x7, mask_2d_7x7, model_image_7x7): ) assert fit.dataset_model.grid_offset == (1.0, 2.0) - assert fit.grids.lp[0] == pytest.approx((0.0, -3.0), 1.0e-4) assert fit.grids.pixelization[0] == pytest.approx((0.0, -3.0), 1.0e-4) diff --git a/test_autoarray/fit/test_fit_imaging.py b/test_autoarray/fit/test_fit_imaging.py index c90a453ac..411330d46 100644 --- a/test_autoarray/fit/test_fit_imaging.py +++ b/test_autoarray/fit/test_fit_imaging.py @@ -3,102 +3,205 @@ import autoarray as aa -def test__data_and_model_are_identical__no_masking__check_values_are_correct(): - mask = aa.Mask2D(mask=[[False, False], [False, False]], pixel_scales=(1.0, 1.0)) +# --------------------------------------------------------------------------- +# Helper: build the "identical model, no masking" fit used by multiple tests +# --------------------------------------------------------------------------- +def _make_identical_fit_no_mask(): + mask = aa.Mask2D(mask=[[False, False], [False, False]], pixel_scales=(1.0, 1.0)) data = aa.Array2D(values=[1.0, 2.0, 3.0, 4.0], mask=mask) noise_map = aa.Array2D(values=[2.0, 2.0, 2.0, 2.0], mask=mask) - dataset = aa.Imaging(data=data, noise_map=noise_map) - dataset = dataset.apply_mask(mask=mask) - model_data = aa.Array2D(values=[1.0, 2.0, 3.0, 4.0], mask=mask) + fit = aa.m.MockFitImaging( + dataset=dataset, use_mask_in_fit=False, model_data=model_data + ) + return fit, noise_map + +def _make_different_fit_with_mask(): + mask = aa.Mask2D(mask=[[False, False], [True, False]], pixel_scales=(1.0, 1.0)) + data = aa.Array2D(values=[1.0, 2.0, 4.0], mask=mask) + noise_map = aa.Array2D(values=[2.0, 2.0, 2.0], mask=mask) + dataset = aa.Imaging(data=data, noise_map=noise_map) + model_data = aa.Array2D(values=[1.0, 2.0, 3.0], mask=mask) fit = aa.m.MockFitImaging( dataset=dataset, use_mask_in_fit=False, model_data=model_data ) + return fit, noise_map + + +def _make_identical_fit_with_inversion(): + mask = aa.Mask2D(mask=[[False, False], [False, False]], pixel_scales=(1.0, 1.0)) + data = aa.Array2D(values=[1.0, 2.0, 3.0, 4.0], mask=mask) + noise_map = aa.Array2D(values=[2.0, 2.0, 2.0, 2.0], mask=mask) + dataset = aa.Imaging(data=data, noise_map=noise_map) + dataset = dataset.apply_mask(mask=mask) + model_data = aa.Array2D(values=[1.0, 2.0, 3.0, 4.0], mask=mask) + inversion = aa.m.MockInversion( + linear_obj_list=[aa.m.MockMapper()], + data_vector=1, + regularization_term=2.0, + log_det_curvature_reg_matrix_term=3.0, + log_det_regularization_matrix_term=4.0, + ) + fit = aa.m.MockFitImaging( + dataset=dataset, + use_mask_in_fit=False, + model_data=model_data, + inversion=inversion, + ) + return fit, noise_map + + +# --------------------------------------------------------------------------- +# Tests: identical data and model, no masking +# --------------------------------------------------------------------------- + + +def test__mask__no_masking__returns_2x2_all_false_mask(): + fit, _ = _make_identical_fit_no_mask() assert (fit.mask == np.array([[False, False], [False, False]])).all() + + +def test__data__no_masking__returns_correct_data_values(): + fit, _ = _make_identical_fit_no_mask() + assert (fit.data == np.array([1.0, 2.0, 3.0, 4.0])).all() + + +def test__noise_map__no_masking__returns_correct_noise_map_values(): + fit, _ = _make_identical_fit_no_mask() + assert (fit.noise_map == np.array([2.0, 2.0, 2.0, 2.0])).all() + + +def test__signal_to_noise_map__no_masking__returns_correct_signal_to_noise_values(): + fit, _ = _make_identical_fit_no_mask() + assert (fit.signal_to_noise_map == np.array([0.5, 1.0, 1.5, 2.0])).all() - assert (fit.model_data == np.array([1.0, 2.0, 3.0, 4.0])).all() + + +def test__residual_map__identical_data_and_model__all_zero_residuals(): + fit, _ = _make_identical_fit_no_mask() + assert (fit.residual_map == np.array([0.0, 0.0, 0.0, 0.0])).all() + + +def test__normalized_residual_map__identical_data_and_model__all_zero_normalized_residuals(): + fit, _ = _make_identical_fit_no_mask() + assert (fit.normalized_residual_map == np.array([0.0, 0.0, 0.0, 0.0])).all() + + +def test__chi_squared_map__identical_data_and_model__all_zero_chi_squared_map(): + fit, _ = _make_identical_fit_no_mask() + assert (fit.chi_squared_map == np.array([0.0, 0.0, 0.0, 0.0])).all() + +def test__chi_squared__identical_data_and_model__is_zero(): + fit, _ = _make_identical_fit_no_mask() + assert fit.chi_squared == 0.0 + + +def test__reduced_chi_squared__identical_data_and_model__is_zero(): + fit, _ = _make_identical_fit_no_mask() + assert fit.reduced_chi_squared == 0.0 + + +def test__noise_normalization__uniform_noise_map__correct_log_sum_formula(): + fit, noise_map = _make_identical_fit_no_mask() + assert fit.noise_normalization == np.sum(np.log(2 * np.pi * noise_map.array**2.0)) - assert fit.log_likelihood == -0.5 * (fit.chi_squared + fit.noise_normalization) -def test__data_and_model_are_different__include_masking__check_values_are_correct(): - mask = aa.Mask2D(mask=[[False, False], [True, False]], pixel_scales=(1.0, 1.0)) +def test__log_likelihood__identical_data_and_model__negative_half_noise_normalization(): + fit, _ = _make_identical_fit_no_mask() - data = aa.Array2D(values=[1.0, 2.0, 4.0], mask=mask) - noise_map = aa.Array2D(values=[2.0, 2.0, 2.0], mask=mask) + assert fit.log_likelihood == -0.5 * (fit.chi_squared + fit.noise_normalization) - dataset = aa.Imaging(data=data, noise_map=noise_map) - model_data = aa.Array2D(values=[1.0, 2.0, 3.0], mask=mask) +# --------------------------------------------------------------------------- +# Tests: different data and model with partial mask +# --------------------------------------------------------------------------- - fit = aa.m.MockFitImaging( - dataset=dataset, use_mask_in_fit=False, model_data=model_data - ) - assert (fit.mask == np.array([[False, False], [True, False]])).all() - assert (fit.data.slim == np.array([1.0, 2.0, 4.0])).all() - assert (fit.noise_map.slim == np.array([2.0, 2.0, 2.0])).all() - assert (fit.signal_to_noise_map.slim == np.array([0.5, 1.0, 2.0])).all() - assert (fit.model_data.slim == np.array([1.0, 2.0, 3.0])).all() +def test__residual_map__different_data_and_model_with_partial_mask__correct_slim_residuals(): + fit, _ = _make_different_fit_with_mask() + assert (fit.residual_map.slim == np.array([0.0, 0.0, 1.0])).all() + + +def test__normalized_residual_map__different_data_and_model_with_partial_mask__correct_slim_values(): + fit, _ = _make_different_fit_with_mask() + assert (fit.normalized_residual_map.slim == np.array([0.0, 0.0, 0.5])).all() + + +def test__chi_squared_map__different_data_and_model_with_partial_mask__correct_slim_chi_squared(): + fit, _ = _make_different_fit_with_mask() + assert (fit.chi_squared_map.slim == np.array([0.0, 0.0, 0.25])).all() + +def test__chi_squared__different_model_with_masked_data__correct_value(): + fit, _ = _make_different_fit_with_mask() + assert fit.chi_squared == 0.25 + + +def test__reduced_chi_squared__different_model_with_masked_data__divided_by_unmasked_pixel_count(): + fit, _ = _make_different_fit_with_mask() + assert fit.reduced_chi_squared == 0.25 / 3.0 + + +def test__log_likelihood__different_model_with_masked_data__correct_value(): + fit, noise_map = _make_different_fit_with_mask() + assert fit.noise_normalization == np.sum(np.log(2 * np.pi * noise_map.array**2.0)) assert fit.log_likelihood == -0.5 * (fit.chi_squared + fit.noise_normalization) -def test__data_and_model_are_identical__inversion_included__changes_certain_properties(): - mask = aa.Mask2D(mask=[[False, False], [False, False]], pixel_scales=(1.0, 1.0)) +# --------------------------------------------------------------------------- +# Tests: identical data and model with inversion +# --------------------------------------------------------------------------- - data = aa.Array2D(values=[1.0, 2.0, 3.0, 4.0], mask=mask) - noise_map = aa.Array2D(values=[2.0, 2.0, 2.0, 2.0], mask=mask) - dataset = aa.Imaging(data=data, noise_map=noise_map) +def test__chi_squared__identical_data_and_model_with_inversion__is_zero(): + fit, _ = _make_identical_fit_with_inversion() - dataset = dataset.apply_mask(mask=mask) + assert fit.chi_squared == 0.0 - model_data = aa.Array2D(values=[1.0, 2.0, 3.0, 4.0], mask=mask) - inversion = aa.m.MockInversion( - linear_obj_list=[aa.m.MockMapper()], - data_vector=1, - regularization_term=2.0, - log_det_curvature_reg_matrix_term=3.0, - log_det_regularization_matrix_term=4.0, - ) - - fit = aa.m.MockFitImaging( - dataset=dataset, - use_mask_in_fit=False, - model_data=model_data, - inversion=inversion, - ) +def test__reduced_chi_squared__identical_data_and_model_with_inversion__is_zero(): + fit, _ = _make_identical_fit_with_inversion() - assert fit.chi_squared == 0.0 assert fit.reduced_chi_squared == 0.0 - assert fit.noise_normalization == np.sum(np.log(2 * np.pi * noise_map.array**2.0)) - assert fit.log_likelihood == -0.5 * (fit.chi_squared + fit.noise_normalization) + + +def test__log_likelihood_with_regularization__with_inversion__adds_regularization_term(): + fit, noise_map = _make_identical_fit_with_inversion() assert fit.log_likelihood_with_regularization == -0.5 * ( fit.chi_squared + 2.0 + fit.noise_normalization ) + + +def test__log_evidence__with_inversion__uses_chi_squared_reg_and_determinant_terms(): + fit, noise_map = _make_identical_fit_with_inversion() + assert fit.log_evidence == -0.5 * ( fit.chi_squared + 2.0 + 3.0 - 4.0 + fit.noise_normalization ) + + +def test__figure_of_merit__with_inversion__equals_log_evidence(): + fit, _ = _make_identical_fit_with_inversion() + assert fit.figure_of_merit == fit.log_evidence diff --git a/test_autoarray/fit/test_fit_interferometer.py b/test_autoarray/fit/test_fit_interferometer.py index e22d9de19..97e7edf09 100644 --- a/test_autoarray/fit/test_fit_interferometer.py +++ b/test_autoarray/fit/test_fit_interferometer.py @@ -4,177 +4,306 @@ import autoarray as aa -def test__data_and_model_are_identical__no_masking__check_values_are_correct(): +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _make_dataset(): real_space_mask = aa.Mask2D( mask=[[False, False], [False, False]], pixel_scales=(1.0, 1.0) ) - data = aa.Visibilities(visibilities=[1.0 + 2.0j, 3.0 + 4.0j]) noise_map = aa.VisibilitiesNoiseMap(visibilities=[2.0 + 2.0j, 2.0 + 2.0j]) - dataset = aa.Interferometer( data=data, noise_map=noise_map, uv_wavelengths=np.ones(shape=(2, 2)), real_space_mask=real_space_mask, ) + return dataset, noise_map + +def _make_identical_fit(): + dataset, noise_map = _make_dataset() model_data = aa.Visibilities(visibilities=[1.0 + 2.0j, 3.0 + 4.0j]) + fit = aa.m.MockFitInterferometer( + dataset=dataset, use_mask_in_fit=False, model_data=model_data + ) + return fit, noise_map + +def _make_different_fit(): + dataset, noise_map = _make_dataset() + model_data = aa.Visibilities(visibilities=[1.0 + 2.0j, 3.0 + 3.0j]) fit = aa.m.MockFitInterferometer( dataset=dataset, use_mask_in_fit=False, model_data=model_data ) + return fit, noise_map + + +def _make_identical_fit_with_inversion(): + dataset, noise_map = _make_dataset() + data = dataset.data + model_data = aa.Visibilities(visibilities=[1.0 + 2.0j, 3.0 + 4.0j]) + chi_squared = data - model_data + chi_squared = np.sum( + (chi_squared.real**2.0 / dataset.noise_map.real**2.0) + + (chi_squared.imag**2.0 / dataset.noise_map.imag**2.0) + ) + inversion = aa.m.MockInversion( + linear_obj_list=[aa.m.MockMapper()], + data_vector=1, + regularization_term=2.0, + log_det_curvature_reg_matrix_term=3.0, + log_det_regularization_matrix_term=4.0, + fast_chi_squared=chi_squared, + ) + fit = aa.m.MockFitInterferometer( + dataset=dataset, + use_mask_in_fit=False, + model_data=model_data, + inversion=inversion, + ) + return fit, noise_map + + +# --------------------------------------------------------------------------- +# Tests: identical visibilities, no masking +# --------------------------------------------------------------------------- + + +def test__data__identical_visibilities__returns_correct_complex_data(): + fit, _ = _make_identical_fit() assert (fit.data == np.array([1.0 + 2.0j, 3.0 + 4.0j])).all() + +def test__noise_map__uniform_complex_noise__returns_correct_noise_map(): + fit, _ = _make_identical_fit() + assert (fit.noise_map == np.array([2.0 + 2.0j, 2.0 + 2.0j])).all() + +def test__signal_to_noise_map__identical_visibilities__returns_correct_complex_snr(): + fit, _ = _make_identical_fit() + assert (fit.signal_to_noise_map == np.array([0.5 + 1.0j, 1.5 + 2.0j])).all() - assert (fit.model_data == np.array([1.0 + 2.0j, 3.0 + 4.0j])).all() + +def test__residual_map__identical_visibilities__all_zero_residuals(): + fit, _ = _make_identical_fit() assert (fit.residual_map == np.array([0.0 + 0.0j, 0.0 + 0.0j])).all() + +def test__normalized_residual_map__identical_visibilities__all_zero_normalized_residuals(): + fit, _ = _make_identical_fit() + assert (fit.normalized_residual_map == np.array([0.0 + 0.0j, 0.0 + 0.0j])).all() + +def test__chi_squared_map__identical_visibilities__all_zero_chi_squared(): + fit, _ = _make_identical_fit() + assert (fit.chi_squared_map == np.array([0.0 + 0.0j, 0.0 + 0.0j])).all() + +def test__chi_squared__identical_visibilities__is_zero(): + fit, _ = _make_identical_fit() + assert fit.chi_squared == 0.0 + + +def test__reduced_chi_squared__identical_visibilities__is_zero(): + fit, _ = _make_identical_fit() + assert fit.reduced_chi_squared == 0.0 + + +def test__noise_normalization__uniform_complex_noise__correct_log_formula(): + fit, _ = _make_identical_fit() + assert fit.noise_normalization == pytest.approx( 4.0 * np.log(2 * np.pi * 2.0**2.0), 1.0e-4 ) - assert fit.log_likelihood == -0.5 * (fit.chi_squared + fit.noise_normalization) -def test__data_and_model_are_different__no_masking__check_values_are_correct(): - real_space_mask = aa.Mask2D( - mask=[[False, False], [False, False]], pixel_scales=(1.0, 1.0) - ) +def test__log_likelihood__identical_visibilities__negative_half_noise_normalization(): + fit, _ = _make_identical_fit() - data = aa.Visibilities(visibilities=[1.0 + 2.0j, 3.0 + 4.0j]) - noise_map = aa.VisibilitiesNoiseMap(visibilities=[2.0 + 2.0j, 2.0 + 2.0j]) + assert fit.log_likelihood == -0.5 * (fit.chi_squared + fit.noise_normalization) - dataset = aa.Interferometer( - data=data, - noise_map=noise_map, - uv_wavelengths=np.ones(shape=(2, 2)), - real_space_mask=real_space_mask, - ) - model_data = aa.Visibilities(visibilities=[1.0 + 2.0j, 3.0 + 3.0j]) +# --------------------------------------------------------------------------- +# Tests: different visibilities, no masking +# --------------------------------------------------------------------------- - fit = aa.m.MockFitInterferometer( - dataset=dataset, use_mask_in_fit=False, model_data=model_data - ) - assert (fit.data == np.array([1.0 + 2.0j, 3.0 + 4.0j])).all() - assert (fit.noise_map == np.array([2.0 + 2.0j, 2.0 + 2.0j])).all() - assert (fit.signal_to_noise_map == np.array([0.5 + 1.0j, 1.5 + 2.0j])).all() - assert (fit.model_data == np.array([1.0 + 2.0j, 3.0 + 3.0j])).all() +def test__residual_map__different_visibilities__correct_complex_residuals(): + fit, _ = _make_different_fit() + assert (fit.residual_map == np.array([0.0 + 0.0j, 0.0 + 1.0j])).all() + + +def test__normalized_residual_map__different_visibilities__correct_complex_normalized_residuals(): + fit, _ = _make_different_fit() + assert (fit.normalized_residual_map == np.array([0.0 + 0.0j, 0.0 + 0.5j])).all() + + +def test__chi_squared_map__different_visibilities__correct_complex_chi_squared_map(): + fit, _ = _make_different_fit() + assert (fit.chi_squared_map == np.array([0.0 + 0.0j, 0.0 + 0.25j])).all() + + +def test__chi_squared__different_visibilities__correct_value(): + fit, _ = _make_different_fit() + assert fit.chi_squared == 0.25 + + +def test__reduced_chi_squared__different_visibilities__divided_by_visibility_count(): + fit, _ = _make_different_fit() + assert fit.reduced_chi_squared == 0.25 / 2.0 + + +def test__log_likelihood__different_visibilities__correct_value(): + fit, _ = _make_different_fit() + assert fit.noise_normalization == pytest.approx( 4.0 * np.log(2 * np.pi * 2.0**2.0), 1.0e-4 ) assert fit.log_likelihood == -0.5 * (fit.chi_squared + fit.noise_normalization) -def test__data_and_model_are_identical__inversion_included__changes_certain_properties(): - real_space_mask = aa.Mask2D( - mask=[[False, False], [False, False]], pixel_scales=(1.0, 1.0) - ) - - data = aa.Visibilities(visibilities=[1.0 + 2.0j, 3.0 + 4.0j]) - noise_map = aa.VisibilitiesNoiseMap(visibilities=[2.0 + 2.0j, 2.0 + 2.0j]) +# --------------------------------------------------------------------------- +# Tests: identical visibilities with inversion +# --------------------------------------------------------------------------- - dataset = aa.Interferometer( - data=data, - noise_map=noise_map, - uv_wavelengths=np.ones(shape=(2, 2)), - real_space_mask=real_space_mask, - ) - model_data = aa.Visibilities(visibilities=[1.0 + 2.0j, 3.0 + 4.0j]) +def test__chi_squared__identical_visibilities_with_inversion__is_zero(): + fit, _ = _make_identical_fit_with_inversion() - chi_squared = data - model_data - chi_squared = np.sum( - (chi_squared.real**2.0 / noise_map.real**2.0) - + (chi_squared.imag**2.0 / noise_map.imag**2.0) - ) + assert fit.chi_squared == 0.0 - inversion = aa.m.MockInversion( - linear_obj_list=[aa.m.MockMapper()], - data_vector=1, - regularization_term=2.0, - log_det_curvature_reg_matrix_term=3.0, - log_det_regularization_matrix_term=4.0, - fast_chi_squared=chi_squared, - ) - fit = aa.m.MockFitInterferometer( - dataset=dataset, - use_mask_in_fit=False, - model_data=model_data, - inversion=inversion, - ) +def test__reduced_chi_squared__identical_visibilities_with_inversion__is_zero(): + fit, _ = _make_identical_fit_with_inversion() - assert fit.chi_squared == 0.0 assert fit.reduced_chi_squared == 0.0 - assert fit.noise_normalization == pytest.approx( - 4.0 * np.log(2 * np.pi * 2.0**2.0), 1.0e-4 - ) - assert fit.log_likelihood == -0.5 * (fit.chi_squared + fit.noise_normalization) + + +def test__log_likelihood_with_regularization__interferometer_with_inversion__adds_regularization_term(): + fit, _ = _make_identical_fit_with_inversion() assert fit.log_likelihood_with_regularization == -0.5 * ( fit.chi_squared + 2.0 + fit.noise_normalization ) + + +def test__log_evidence__interferometer_with_inversion__uses_chi_squared_reg_and_determinant_terms(): + fit, _ = _make_identical_fit_with_inversion() + assert fit.log_evidence == -0.5 * ( fit.chi_squared + 2.0 + 3.0 - 4.0 + fit.noise_normalization ) + + +def test__figure_of_merit__interferometer_with_inversion__equals_log_evidence(): + fit, _ = _make_identical_fit_with_inversion() + assert fit.figure_of_merit == fit.log_evidence -def test__dirty_quantities(transformer_7x7_7, interferometer_7, fit_interferometer_7): +# --------------------------------------------------------------------------- +# Tests: dirty image quantities (transformer applied to fit quantities) +# --------------------------------------------------------------------------- + + +def test__dirty_image__equals_transformer_image_from_interferometer_data( + transformer_7x7_7, interferometer_7, fit_interferometer_7 +): fit_interferometer_7.dataset.transformer = transformer_7x7_7 dirty_image = transformer_7x7_7.image_from(visibilities=interferometer_7.data) + assert (fit_interferometer_7.dirty_image == dirty_image).all() + +def test__dirty_noise_map__equals_transformer_image_from_noise_map( + transformer_7x7_7, interferometer_7, fit_interferometer_7 +): + fit_interferometer_7.dataset.transformer = transformer_7x7_7 + dirty_noise_map = transformer_7x7_7.image_from( visibilities=interferometer_7.noise_map ) + assert (fit_interferometer_7.dirty_noise_map == dirty_noise_map).all() + +def test__dirty_signal_to_noise_map__equals_transformer_image_from_signal_to_noise_map( + transformer_7x7_7, interferometer_7, fit_interferometer_7 +): + fit_interferometer_7.dataset.transformer = transformer_7x7_7 + dirty_signal_to_noise_map = transformer_7x7_7.image_from( visibilities=interferometer_7.signal_to_noise_map ) + assert ( fit_interferometer_7.dirty_signal_to_noise_map == dirty_signal_to_noise_map ).all() + +def test__dirty_model_image__equals_transformer_image_from_model_data( + transformer_7x7_7, interferometer_7, fit_interferometer_7 +): + fit_interferometer_7.dataset.transformer = transformer_7x7_7 + dirty_model_image = transformer_7x7_7.image_from( visibilities=fit_interferometer_7.model_data ) + assert (fit_interferometer_7.dirty_model_image == dirty_model_image).all() + +def test__dirty_residual_map__equals_transformer_image_from_residual_map( + transformer_7x7_7, interferometer_7, fit_interferometer_7 +): + fit_interferometer_7.dataset.transformer = transformer_7x7_7 + dirty_residual_map = transformer_7x7_7.image_from( visibilities=fit_interferometer_7.residual_map ) + assert (fit_interferometer_7.dirty_residual_map == dirty_residual_map).all() + +def test__dirty_normalized_residual_map__equals_transformer_image_from_normalized_residual_map( + transformer_7x7_7, interferometer_7, fit_interferometer_7 +): + fit_interferometer_7.dataset.transformer = transformer_7x7_7 + dirty_normalized_residual_map = transformer_7x7_7.image_from( visibilities=fit_interferometer_7.normalized_residual_map ) + assert ( fit_interferometer_7.dirty_normalized_residual_map == dirty_normalized_residual_map ).all() + +def test__dirty_chi_squared_map__equals_transformer_image_from_chi_squared_map( + transformer_7x7_7, interferometer_7, fit_interferometer_7 +): + fit_interferometer_7.dataset.transformer = transformer_7x7_7 + dirty_chi_squared_map = transformer_7x7_7.image_from( visibilities=fit_interferometer_7.chi_squared_map ) + assert (fit_interferometer_7.dirty_chi_squared_map == dirty_chi_squared_map).all() diff --git a/test_autoarray/fit/test_fit_util.py b/test_autoarray/fit/test_fit_util.py index 0702dc95a..c38c572f2 100644 --- a/test_autoarray/fit/test_fit_util.py +++ b/test_autoarray/fit/test_fit_util.py @@ -4,7 +4,12 @@ import autoarray as aa -def test__residual_map_from(): +# --------------------------------------------------------------------------- +# residual_map_from +# --------------------------------------------------------------------------- + + +def test__residual_map_from__identical_data_and_model__all_zero_residuals(): data = np.array([10.0, 10.0, 10.0, 10.0]) model_data = np.array([10.0, 10.0, 10.0, 10.0]) @@ -12,6 +17,8 @@ def test__residual_map_from(): assert (residual_map == np.array([0.0, 0.0, 0.0, 0.0])).all() + +def test__residual_map_from__different_data_and_model__correct_signed_residuals(): data = np.array([10.0, 10.0, 10.0, 10.0]) model_data = np.array([11.0, 10.0, 9.0, 8.0]) @@ -20,7 +27,12 @@ def test__residual_map_from(): assert (residual_map == np.array([-1.0, 0.0, 1.0, 2.0])).all() -def test__residual_map_with_mask_from(): +# --------------------------------------------------------------------------- +# residual_map_with_mask_from +# --------------------------------------------------------------------------- + + +def test__residual_map_with_mask_from__masked_pixels__zero_at_masked_locations(): data = np.array([10.0, 10.0, 10.0, 10.0]) mask = np.array([True, False, False, True]) model_data = np.array([11.0, 10.0, 9.0, 8.0]) @@ -32,13 +44,17 @@ def test__residual_map_with_mask_from(): assert (residual_map == np.array([0.0, 0.0, 1.0, 0.0])).all() -def test__normalized_residual_map_from(): +# --------------------------------------------------------------------------- +# normalized_residual_map_from +# --------------------------------------------------------------------------- + + +def test__normalized_residual_map_from__identical_data_and_model__all_zero(): data = np.array([10.0, 10.0, 10.0, 10.0]) noise_map = np.array([2.0, 2.0, 2.0, 2.0]) model_data = np.array([10.0, 10.0, 10.0, 10.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - normalized_residual_map = aa.util.fit.normalized_residual_map_from( residual_map=residual_map, noise_map=noise_map ) @@ -47,10 +63,13 @@ def test__normalized_residual_map_from(): np.array([0.0, 0.0, 0.0, 0.0]), 1.0e-4 ) + +def test__normalized_residual_map_from__different_data_and_model__divided_by_noise_map(): + data = np.array([10.0, 10.0, 10.0, 10.0]) + noise_map = np.array([2.0, 2.0, 2.0, 2.0]) model_data = np.array([11.0, 10.0, 9.0, 8.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - normalized_residual_map = aa.util.fit.normalized_residual_map_from( residual_map=residual_map, noise_map=noise_map ) @@ -60,7 +79,12 @@ def test__normalized_residual_map_from(): ) -def test__normalized_residual_map_with_mask_from(): +# --------------------------------------------------------------------------- +# normalized_residual_map_with_mask_from +# --------------------------------------------------------------------------- + + +def test__normalized_residual_map_with_mask_from__masked_pixels__zero_at_masked_locations(): data = np.array([10.0, 10.0, 10.0, 10.0]) mask = np.array([True, False, False, True]) noise_map = np.array([2.0, 2.0, 2.0, 2.0]) @@ -69,7 +93,6 @@ def test__normalized_residual_map_with_mask_from(): residual_map = aa.util.fit.residual_map_with_mask_from( data=data, mask=mask, model_data=model_data ) - normalized_residual_map = aa.util.fit.normalized_residual_map_with_mask_from( residual_map=residual_map, mask=mask, noise_map=noise_map ) @@ -79,13 +102,17 @@ def test__normalized_residual_map_with_mask_from(): ) -def test__normalized_residual_map_complex_from(): +# --------------------------------------------------------------------------- +# normalized_residual_map_complex_from +# --------------------------------------------------------------------------- + + +def test__normalized_residual_map_complex_from__complex_data__correct_real_and_imaginary_parts(): data = np.array([10.0 + 10.0j, 10.0 + 10.0j]) noise_map = np.array([2.0 + 2.0j, 2.0 + 2.0j]) model_data = np.array([9.0 + 12.0j, 9.0 + 12.0j]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - normalized_residual_map = aa.util.fit.normalized_residual_map_complex_from( residual_map=residual_map, noise_map=noise_map ) @@ -93,23 +120,30 @@ def test__normalized_residual_map_complex_from(): assert (normalized_residual_map == np.array([0.5 - 1.0j, 0.5 - 1.0j])).all() -def test__chi_squared_map_from(): +# --------------------------------------------------------------------------- +# chi_squared_map_from +# --------------------------------------------------------------------------- + + +def test__chi_squared_map_from__identical_data_and_model__all_zero(): data = np.array([10.0, 10.0, 10.0, 10.0]) noise_map = np.array([2.0, 2.0, 2.0, 2.0]) model_data = np.array([10.0, 10.0, 10.0, 10.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - chi_squared_map = aa.util.fit.chi_squared_map_from( residual_map=residual_map, noise_map=noise_map ) assert (chi_squared_map == np.array([0.0, 0.0, 0.0, 0.0])).all() + +def test__chi_squared_map_from__different_data_and_model__squared_normalized_residuals(): + data = np.array([10.0, 10.0, 10.0, 10.0]) + noise_map = np.array([2.0, 2.0, 2.0, 2.0]) model_data = np.array([11.0, 10.0, 9.0, 8.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - chi_squared_map = aa.util.fit.chi_squared_map_from( residual_map=residual_map, noise_map=noise_map ) @@ -120,7 +154,12 @@ def test__chi_squared_map_from(): ).all() -def test__chi_squared_map_with_mask_from(): +# --------------------------------------------------------------------------- +# chi_squared_map_with_mask_from +# --------------------------------------------------------------------------- + + +def test__chi_squared_map_with_mask_from__masked_pixels__zero_at_masked_locations(): data = np.array([10.0, 10.0, 10.0, 10.0]) mask = np.array([True, False, False, True]) noise_map = np.array([2.0, 2.0, 2.0, 2.0]) @@ -129,33 +168,24 @@ def test__chi_squared_map_with_mask_from(): residual_map = aa.util.fit.residual_map_with_mask_from( data=data, mask=mask, model_data=model_data ) - chi_squared_map = aa.util.fit.chi_squared_map_with_mask_from( residual_map=residual_map, mask=mask, noise_map=noise_map ) assert (chi_squared_map == np.array([0.0, 0.0, (1.0 / 2.0) ** 2.0, 0.0])).all() - model_data = np.array([11.0, 10.0, 9.0, 8.0]) - - residual_map = aa.util.fit.residual_map_with_mask_from( - data=data, mask=mask, model_data=model_data - ) - - chi_squared_map = aa.util.fit.chi_squared_map_with_mask_from( - residual_map=residual_map, mask=mask, noise_map=noise_map - ) - assert (chi_squared_map == np.array([0.0, 0.0, (1.0 / 2.0) ** 2.0, 0.0])).all() +# --------------------------------------------------------------------------- +# chi_squared_map_complex_from +# --------------------------------------------------------------------------- -def test__chi_squared_map_complex_from(): +def test__chi_squared_map_complex_from__complex_data__correct_real_and_imaginary_chi_squared(): data = np.array([10.0 + 10.0j, 10.0 + 10.0j]) noise_map = np.array([2.0 + 2.0j, 2.0 + 2.0j]) model_data = np.array([9.0 + 12.0j, 9.0 + 12.0j]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - chi_squared_map = aa.util.fit.chi_squared_map_complex_from( residual_map=residual_map, noise_map=noise_map ) @@ -163,8 +193,13 @@ def test__chi_squared_map_complex_from(): assert (chi_squared_map == np.array([0.25 + 1.0j, 0.25 + 1.0j])).all() -def test__chi_squared_with_noise_covariance_from(): - resdiual_map = aa.Array2D.no_mask([[1.0, 1.0], [2.0, 2.0]], pixel_scales=1.0) +# --------------------------------------------------------------------------- +# chi_squared_with_noise_covariance_from +# --------------------------------------------------------------------------- + + +def test__chi_squared_with_noise_covariance_from__known_residual_and_inverse__correct_chi_squared(): + residual_map = aa.Array2D.no_mask([[1.0, 1.0], [2.0, 2.0]], pixel_scales=1.0) noise_covariance_matrix_inv = np.array( [ @@ -176,14 +211,19 @@ def test__chi_squared_with_noise_covariance_from(): ) chi_squared = aa.util.fit.chi_squared_with_noise_covariance_from( - residual_map=resdiual_map, + residual_map=residual_map, noise_covariance_matrix_inv=noise_covariance_matrix_inv, ) assert chi_squared == 43 -def test__chi_squared_with_mask_fast_from(): +# --------------------------------------------------------------------------- +# chi_squared_with_mask_fast_from +# --------------------------------------------------------------------------- + + +def test__chi_squared_with_mask_fast_from__1d_data_with_mask__equals_standard_computation(): data = np.array([10.0, 10.0, 10.0, 10.0]) mask = np.array([True, False, False, True]) noise_map = np.array([1.0, 2.0, 3.0, 4.0]) @@ -192,15 +232,12 @@ def test__chi_squared_with_mask_fast_from(): residual_map = aa.util.fit.residual_map_with_mask_from( data=data, mask=mask, model_data=model_data ) - chi_squared_map = aa.util.fit.chi_squared_map_with_mask_from( residual_map=residual_map, mask=mask, noise_map=noise_map ) - chi_squared = aa.util.fit.chi_squared_with_mask_from( mask=mask, chi_squared_map=chi_squared_map ) - chi_squared_fast = aa.util.fit.chi_squared_with_mask_fast_from( data=data, noise_map=noise_map, @@ -210,6 +247,8 @@ def test__chi_squared_with_mask_fast_from(): assert chi_squared == pytest.approx(chi_squared_fast, 1.0e-4) + +def test__chi_squared_with_mask_fast_from__2d_data_with_mask__equals_standard_computation(): data = np.array([[10.0, 10.0], [10.0, 10.0]]) mask = np.array([[True, False], [False, True]]) noise_map = np.array([[1.0, 2.0], [3.0, 4.0]]) @@ -218,15 +257,12 @@ def test__chi_squared_with_mask_fast_from(): residual_map = aa.util.fit.residual_map_with_mask_from( data=data, mask=mask, model_data=model_data ) - chi_squared_map = aa.util.fit.chi_squared_map_with_mask_from( residual_map=residual_map, mask=mask, noise_map=noise_map ) - chi_squared = aa.util.fit.chi_squared_with_mask_from( mask=mask, chi_squared_map=chi_squared_map ) - chi_squared_fast = aa.util.fit.chi_squared_with_mask_fast_from( data=data, noise_map=noise_map, @@ -237,90 +273,78 @@ def test__chi_squared_with_mask_fast_from(): assert chi_squared == pytest.approx(chi_squared_fast, 1.0e-4) -def test__log_likelihood_from(): +# --------------------------------------------------------------------------- +# log_likelihood_from +# --------------------------------------------------------------------------- + + +def test__log_likelihood_from__identical_data_and_model__correct_value_from_noise_normalization(): data = np.array([10.0, 10.0, 10.0, 10.0]) noise_map = np.array([2.0, 2.0, 2.0, 2.0]) model_data = np.array([10.0, 10.0, 10.0, 10.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - chi_squared_map = aa.util.fit.chi_squared_map_from( residual_map=residual_map, noise_map=noise_map ) - chi_squared = aa.util.fit.chi_squared_from(chi_squared_map=chi_squared_map) - noise_normalization = aa.util.fit.noise_normalization_from(noise_map=noise_map) - log_likelihood = aa.util.fit.log_likelihood_from( chi_squared=chi_squared, noise_normalization=noise_normalization ) - chi_squared = 0.0 - noise_normalization = ( - np.log(2.0 * np.pi * (2.0**2.0)) - + np.log(2.0 * np.pi * (2.0**2.0)) - + np.log(2.0 * np.pi * (2.0**2.0)) - + np.log(2.0 * np.pi * (2.0**2.0)) - ) + expected_chi_squared = 0.0 + expected_noise_normalization = 4 * np.log(2.0 * np.pi * (2.0**2.0)) assert log_likelihood == pytest.approx( - -0.5 * (chi_squared + noise_normalization), 1.0e-4 + -0.5 * (expected_chi_squared + expected_noise_normalization), 1.0e-4 ) + +def test__log_likelihood_from__different_data_and_model_uniform_noise__correct_value(): + data = np.array([10.0, 10.0, 10.0, 10.0]) + noise_map = np.array([2.0, 2.0, 2.0, 2.0]) model_data = np.array([11.0, 10.0, 9.0, 8.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - chi_squared_map = aa.util.fit.chi_squared_map_from( residual_map=residual_map, noise_map=noise_map ) - chi_squared = aa.util.fit.chi_squared_from(chi_squared_map=chi_squared_map) - noise_normalization = aa.util.fit.noise_normalization_from(noise_map=noise_map) - log_likelihood = aa.util.fit.log_likelihood_from( chi_squared=chi_squared, noise_normalization=noise_normalization ) # chi squared = 0.25, 0, 0.25, 1.0 - # log_likelihood = -0.5*(0.25+0+0.25+1.0) - - chi_squared = ( + expected_chi_squared = ( ((1.0 / 2.0) ** 2.0) + 0.0 + ((1.0 / 2.0) ** 2.0) + ((2.0 / 2.0) ** 2.0) ) - noise_normalization = ( - np.log(2.0 * np.pi * (2.0**2.0)) - + np.log(2.0 * np.pi * (2.0**2.0)) - + np.log(2.0 * np.pi * (2.0**2.0)) - + np.log(2.0 * np.pi * (2.0**2.0)) - ) + expected_noise_normalization = 4 * np.log(2.0 * np.pi * (2.0**2.0)) assert log_likelihood == pytest.approx( - -0.5 * (chi_squared + noise_normalization), 1.0e-4 + -0.5 * (expected_chi_squared + expected_noise_normalization), 1.0e-4 ) + +def test__log_likelihood_from__different_data_and_model_varied_noise__correct_value(): + data = np.array([10.0, 10.0, 10.0, 10.0]) noise_map = np.array([1.0, 2.0, 3.0, 4.0]) + model_data = np.array([11.0, 10.0, 9.0, 8.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - chi_squared_map = aa.util.fit.chi_squared_map_from( residual_map=residual_map, noise_map=noise_map ) - chi_squared = aa.util.fit.chi_squared_from(chi_squared_map=chi_squared_map) - noise_normalization = aa.util.fit.noise_normalization_from(noise_map=noise_map) - log_likelihood = aa.util.fit.log_likelihood_from( chi_squared=chi_squared, noise_normalization=noise_normalization ) # chi squared = (1.0/1.0)**2, (0.0), (-1.0/3.0)**2.0, (2.0/4.0)**2.0 - - chi_squared = 1.0 + (1.0 / (3.0**2.0)) + 0.25 - noise_normalization = ( + expected_chi_squared = 1.0 + (1.0 / (3.0**2.0)) + 0.25 + expected_noise_normalization = ( np.log(2 * np.pi * (1.0**2.0)) + np.log(2 * np.pi * (2.0**2.0)) + np.log(2 * np.pi * (3.0**2.0)) @@ -328,11 +352,16 @@ def test__log_likelihood_from(): ) assert log_likelihood == pytest.approx( - -0.5 * (chi_squared + noise_normalization), 1e-4 + -0.5 * (expected_chi_squared + expected_noise_normalization), 1e-4 ) -def test__log_likelihood_from__with_mask(): +# --------------------------------------------------------------------------- +# log_likelihood_from with mask +# --------------------------------------------------------------------------- + + +def test__log_likelihood_from__with_1d_mask__excludes_masked_pixels_from_chi_squared_and_noise_normalization(): data = np.array([10.0, 10.0, 10.0, 10.0]) mask = np.array([True, False, False, True]) noise_map = np.array([1.0, 2.0, 3.0, 4.0]) @@ -341,34 +370,31 @@ def test__log_likelihood_from__with_mask(): residual_map = aa.util.fit.residual_map_with_mask_from( data=data, mask=mask, model_data=model_data ) - chi_squared_map = aa.util.fit.chi_squared_map_with_mask_from( residual_map=residual_map, mask=mask, noise_map=noise_map ) - chi_squared = aa.util.fit.chi_squared_with_mask_from( mask=mask, chi_squared_map=chi_squared_map ) - noise_normalization = aa.util.fit.noise_normalization_with_mask_from( mask=mask, noise_map=noise_map ) - log_likelihood = aa.util.fit.log_likelihood_from( chi_squared=chi_squared, noise_normalization=noise_normalization ) - # chi squared = 0, 0.25, (0.25 and 1.0 are masked) - - chi_squared = 0.0 + (1.0 / 3.0) ** 2.0 - noise_normalization = np.log(2 * np.pi * (2.0**2.0)) + np.log( + # chi squared = 0, (1/3)**2 (pixels at idx 0 and 3 are masked) + expected_chi_squared = 0.0 + (1.0 / 3.0) ** 2.0 + expected_noise_normalization = np.log(2 * np.pi * (2.0**2.0)) + np.log( 2 * np.pi * (3.0**2.0) ) assert log_likelihood == pytest.approx( - -0.5 * (chi_squared + noise_normalization), 1e-4 + -0.5 * (expected_chi_squared + expected_noise_normalization), 1e-4 ) + +def test__log_likelihood_from__with_2d_mask__excludes_masked_pixels_from_chi_squared_and_noise_normalization(): data = np.array([[10.0, 10.0], [10.0, 10.0]]) mask = np.array([[True, False], [False, True]]) noise_map = np.array([[1.0, 2.0], [3.0, 4.0]]) @@ -377,70 +403,69 @@ def test__log_likelihood_from__with_mask(): residual_map = aa.util.fit.residual_map_with_mask_from( data=data, mask=mask, model_data=model_data ) - chi_squared_map = aa.util.fit.chi_squared_map_with_mask_from( residual_map=residual_map, mask=mask, noise_map=noise_map ) - chi_squared = aa.util.fit.chi_squared_with_mask_from( mask=mask, chi_squared_map=chi_squared_map ) - noise_normalization = aa.util.fit.noise_normalization_with_mask_from( mask=mask, noise_map=noise_map ) - log_likelihood = aa.util.fit.log_likelihood_from( chi_squared=chi_squared, noise_normalization=noise_normalization ) - # chi squared = 0, 0.25, (0.25 and 1.0 are masked) - - chi_squared = 0.0 + (1.0 / 3.0) ** 2.0 - noise_normalization = np.log(2 * np.pi * (2.0**2.0)) + np.log( + # chi squared = 0, (1/3)**2 (corner pixels are masked) + expected_chi_squared = 0.0 + (1.0 / 3.0) ** 2.0 + expected_noise_normalization = np.log(2 * np.pi * (2.0**2.0)) + np.log( 2 * np.pi * (3.0**2.0) ) assert log_likelihood == pytest.approx( - -0.5 * (chi_squared + noise_normalization), 1e-4 + -0.5 * (expected_chi_squared + expected_noise_normalization), 1e-4 ) -def test__log_likelihood_from__complex_data(): +# --------------------------------------------------------------------------- +# log_likelihood_from with complex data +# --------------------------------------------------------------------------- + +def test__log_likelihood_from__complex_data__chi_squared_sums_real_and_imaginary_components(): data = np.array([10.0 + 10.0j, 10.0 + 10.0j]) noise_map = np.array([2.0 + 1.0j, 2.0 + 1.0j]) model_data = np.array([9.0 + 12.0j, 9.0 + 12.0j]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - chi_squared_map = aa.util.fit.chi_squared_map_complex_from( residual_map=residual_map, noise_map=noise_map ) - chi_squared = aa.util.fit.chi_squared_complex_from(chi_squared_map=chi_squared_map) - noise_normalization = aa.util.fit.noise_normalization_complex_from( noise_map=noise_map ) - log_likelihood = aa.util.fit.log_likelihood_from( chi_squared=chi_squared, noise_normalization=noise_normalization ) # chi squared = 0.25 and 4.0 - - chi_squared = 4.25 - noise_normalization = np.log(2 * np.pi * (2.0**2.0)) + np.log( + expected_chi_squared = 4.25 + expected_noise_normalization = np.log(2 * np.pi * (2.0**2.0)) + np.log( 2 * np.pi * (1.0**2.0) ) assert log_likelihood == pytest.approx( - -0.5 * 2.0 * (chi_squared + noise_normalization), 1e-4 + -0.5 * 2.0 * (expected_chi_squared + expected_noise_normalization), 1e-4 ) -def test__log_evidence_from(): +# --------------------------------------------------------------------------- +# log_likelihood_with_regularization_from / log_evidence_from +# --------------------------------------------------------------------------- + + +def test__log_likelihood_with_regularization_from__adds_regularization_to_chi_squared_and_noise(): likelihood_with_regularization_terms = ( aa.util.fit.log_likelihood_with_regularization_from( chi_squared=3.0, regularization_term=6.0, noise_normalization=2.0 @@ -449,6 +474,8 @@ def test__log_evidence_from(): assert likelihood_with_regularization_terms == -0.5 * (3.0 + 6.0 + 2.0) + +def test__log_evidence_from__includes_curvature_and_regularization_determinant_terms(): evidences = aa.util.fit.log_evidence_from( chi_squared=3.0, regularization_term=6.0, @@ -460,22 +487,28 @@ def test__log_evidence_from(): assert evidences == -0.5 * (3.0 + 6.0 + 9.0 - 10.0 + 30.0) -def test__residual_flux_fraction_map_from(): +# --------------------------------------------------------------------------- +# residual_flux_fraction_map_from +# --------------------------------------------------------------------------- + + +def test__residual_flux_fraction_map_from__identical_data_and_model__all_zero(): data = np.array([10.0, 10.0, 10.0, 10.0]) model_data = np.array([10.0, 10.0, 10.0, 10.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - residual_flux_fraction_map = aa.util.fit.residual_flux_fraction_map_from( residual_map=residual_map, data=data ) assert (residual_flux_fraction_map == np.array([0.0, 0.0, 0.0, 0.0])).all() + +def test__residual_flux_fraction_map_from__different_data_and_model__correct_fractional_residuals(): + data = np.array([10.0, 10.0, 10.0, 10.0]) model_data = np.array([11.0, 10.0, 9.0, 8.0]) residual_map = aa.util.fit.residual_map_from(data=data, model_data=model_data) - residual_flux_fraction_map = aa.util.fit.residual_flux_fraction_map_from( residual_map=residual_map, data=data ) @@ -483,7 +516,12 @@ def test__residual_flux_fraction_map_from(): assert (residual_flux_fraction_map == np.array([-0.1, 0.0, 0.1, 0.2])).all() -def test__residual_flux_fraction_map_with_mask_from(): +# --------------------------------------------------------------------------- +# residual_flux_fraction_map_with_mask_from +# --------------------------------------------------------------------------- + + +def test__residual_flux_fraction_map_with_mask_from__masked_data__zero_at_masked_locations(): data = np.array([10.0, 10.0, 10.0, 10.0]) mask = np.array([True, False, False, True]) model_data = np.array([11.0, 10.0, 9.0, 8.0]) @@ -491,19 +529,21 @@ def test__residual_flux_fraction_map_with_mask_from(): residual_map = aa.util.fit.residual_map_with_mask_from( data=data, mask=mask, model_data=model_data ) - residual_flux_fraction_map = aa.util.fit.residual_flux_fraction_map_with_mask_from( residual_map=residual_map, mask=mask, data=data ) assert (residual_flux_fraction_map == np.array([0.0, 0.0, 0.1, 0.0])).all() + +def test__residual_flux_fraction_map_with_mask_from__different_model__correct_unmasked_fractions(): + data = np.array([10.0, 10.0, 10.0, 10.0]) + mask = np.array([True, False, False, True]) model_data = np.array([11.0, 9.0, 8.0, 8.0]) residual_map = aa.util.fit.residual_map_with_mask_from( data=data, mask=mask, model_data=model_data ) - residual_flux_fraction_map = aa.util.fit.residual_flux_fraction_map_with_mask_from( residual_map=residual_map, mask=mask, data=data )