Skip to content

Commit

Permalink
Merge pull request #147 from Jammy2211/feature/disable_noise
Browse files Browse the repository at this point in the history
Feature/disable noise
  • Loading branch information
Jammy2211 authored Nov 18, 2024
2 parents 588df3a + a1e6bcc commit fbf65b0
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 43 deletions.
49 changes: 37 additions & 12 deletions autoarray/dataset/imaging/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ def __init__(
subtract_background_sky: bool = True,
psf: Kernel2D = None,
normalize_psf: bool = True,
add_poisson_noise: bool = True,
add_poisson_noise_to_data: bool = True,
include_poisson_noise_in_noise_map: bool = True,
noise_if_add_noise_false: float = 0.1,
noise_seed: int = -1,
):
Expand All @@ -35,13 +36,28 @@ def __init__(
3) Use input values of the background sky level in every pixel of the image to add the background sky to
the PSF convolved image.
4) Add Poisson noise to the image, which represents noise due to whether photons hits the CCD and are converted
to photo-electrons which are succcessfully detected by the CCD and converted to counts.
to photo-electrons which are successfully detected by the CCD and converted to counts.
5) Subtract the background sky from the image, so that the returned simulated dataset is background sky
subtracted.
The inputs of the `SimulatorImaging` object can toggle these steps on and off, for example if `psf=None` the
PSF convolution step is omitted.
The inputs `add_poisson_noise_to_data` and `include_poisson_noise_in_noise_map` provide flexibility in
handling Poisson noise during simulations:
- `add_poisson_noise_to_data` determines whether Poisson noise is added to the simulated data.
- `include_poisson_noise_in_noise_map` decides if Poisson noise is included in the `noise_map` of the dataset.
These options allow you to create a dataset without adding Poisson noise to the data while still using
a `noise_map` that represents the noise levels expected if Poisson noise had been included.
This approach is particularly useful for diagnostic purposes. By simulating noise-free data but retaining
realistic noise levels in the `noise_map`, you can test whether the model-fitting process accurately recovers
the input parameters. Unlike noisy data, where random Poisson noise introduces offsets in each simulation,
this method ensures the posterior peaks precisely around the input values. This is a powerful way to identify
potential systematic biases in a model-fitting analysis.
Parameters
----------
exposure_time
Expand All @@ -54,8 +70,11 @@ def __init__(
An array describing the PSF kernel of the image.
normalize_psf
If `True`, the PSF kernel is normalized so all values sum to 1.0.
add_poisson_noise
add_poisson_noise_to_data
Whether Poisson noise corresponding to photon count statistics on the imaging observation is added.
include_poisson_noise_in_noise_map
Whether Poisson noise is included in the noise-map of the dataset. If `False` the noise-map is filled with
the value `noise_if_add_noise_false`.
noise_if_add_noise_false
If noise is not added to the simulated dataset a `noise_map` must still be returned. This value gives
the value of noise assigned to every pixel in the noise-map.
Expand All @@ -73,7 +92,8 @@ def __init__(
self.exposure_time = exposure_time
self.background_sky_level = background_sky_level
self.subtract_background_sky = subtract_background_sky
self.add_poisson_noise = add_poisson_noise
self.add_poisson_noise_to_data = add_poisson_noise_to_data
self.include_poisson_noise_in_noise_map = include_poisson_noise_in_noise_map
self.noise_if_add_noise_false = noise_if_add_noise_false
self.noise_seed = noise_seed

Expand Down Expand Up @@ -106,15 +126,18 @@ def via_image_from(self, image: Array2D) -> Imaging:

image = image + background_sky_map

if self.add_poisson_noise is True:
image = preprocess.data_eps_with_poisson_noise_added(
data_eps=image,
exposure_time_map=exposure_time_map,
seed=self.noise_seed,
)
image_with_poisson_noise = preprocess.data_eps_with_poisson_noise_added(
data_eps=image,
exposure_time_map=exposure_time_map,
seed=self.noise_seed,
)

if self.add_poisson_noise_to_data:
image = image_with_poisson_noise

if self.include_poisson_noise_in_noise_map:
noise_map = preprocess.noise_map_via_data_eps_and_exposure_time_map_from(
data_eps=image, exposure_time_map=exposure_time_map
data_eps=image_with_poisson_noise, exposure_time_map=exposure_time_map
)

else:
Expand All @@ -139,4 +162,6 @@ def via_image_from(self, image: Array2D) -> Imaging:

image = Array2D(values=image, mask=mask)

return Imaging(data=image, psf=self.psf, noise_map=noise_map)
return Imaging(
data=image, psf=self.psf, noise_map=noise_map, check_noise_map=False
)
2 changes: 1 addition & 1 deletion autoarray/dataset/interferometer/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def via_image_from(self, image):
An array representing the effective exposure time of each pixel.
psf: PSF
An array describing the PSF the simulated image is blurred with.
add_poisson_noise: Bool
add_poisson_noise_to_data: Bool
If `True` poisson noise_maps is simulated and added to the image, based on the total counts in each image
pixel
noise_seed: int
Expand Down
110 changes: 80 additions & 30 deletions test_autoarray/dataset/imaging/test_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ def make_array_2d_7x7():


def test__via_image_from__all_features_off(image_central_delta_3x3):
simulator = aa.SimulatorImaging(exposure_time=1.0, add_poisson_noise=False)
simulator = aa.SimulatorImaging(
exposure_time=1.0,
add_poisson_noise_to_data=False,
include_poisson_noise_in_noise_map=False,
)

dataset = simulator.via_image_from(image=image_central_delta_3x3)

Expand All @@ -24,10 +28,37 @@ def test__via_image_from__all_features_off(image_central_delta_3x3):
assert dataset.pixel_scales == (0.1, 0.1)


def test__via_image_from__noise_off__noise_map_is_noise_value(image_central_delta_3x3):
def test__via_image_from__psf_off__include_poisson_noise_in_noise_map(
image_central_delta_3x3,
):
image = image_central_delta_3x3 + 1.0

simulator = aa.SimulatorImaging(
exposure_time=20.0,
add_poisson_noise_to_data=True,
include_poisson_noise_in_noise_map=True,
noise_seed=1,
)

dataset = simulator.via_image_from(image=image)

assert dataset.data.native == pytest.approx(
np.array([[1.05, 1.3, 1.25], [1.05, 2.1, 1.2], [1.05, 1.3, 1.15]]), 1e-2
)

assert dataset.noise_map.native == pytest.approx(
np.array([[0.229, 0.255, 0.25], [0.229, 0.324, 0.245], [0.229, 0.255, 0.240]]),
1e-2,
)


def test__via_image_from__psf_off__noise_off_value_is_noise_value(
image_central_delta_3x3,
):
simulator = aa.SimulatorImaging(
exposure_time=1.0,
add_poisson_noise=False,
add_poisson_noise_to_data=False,
include_poisson_noise_in_noise_map=False,
noise_if_add_noise_false=0.2,
noise_seed=1,
)
Expand All @@ -42,62 +73,81 @@ def test__via_image_from__noise_off__noise_map_is_noise_value(image_central_delt
assert np.allclose(dataset.noise_map.native, 0.2 * np.ones((3, 3)))


def test__via_image_from__psf_blurs_image_with_edge_trimming(image_central_delta_3x3):
psf = aa.Kernel2D.no_mask(
values=np.array([[0.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 0.0]]),
pixel_scales=1.0,
)

def test__via_image_from__psf_off__background_sky_on(image_central_delta_3x3):
simulator = aa.SimulatorImaging(
exposure_time=1.0, psf=psf, add_poisson_noise=False, normalize_psf=False
exposure_time=1.0,
background_sky_level=16.0,
add_poisson_noise_to_data=True,
noise_seed=1,
)

dataset = simulator.via_image_from(image=image_central_delta_3x3)

assert (
dataset.data.native
== np.array([[0.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 0.0]])
== np.array([[1.0, 5.0, 4.0], [1.0, 2.0, 1.0], [5.0, 2.0, 7.0]])
).all()

assert dataset.noise_map.native[0, 0] == pytest.approx(4.12310, 1.0e-4)


def test__via_image_from__setup_with_noise(image_central_delta_3x3):
image = image_central_delta_3x3 + 1.0
def test__via_image_from__psf_on__psf_blurs_image_with_edge_trimming(
image_central_delta_3x3,
):
psf = aa.Kernel2D.no_mask(
values=np.array([[0.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 0.0]]),
pixel_scales=1.0,
)

simulator = aa.SimulatorImaging(
exposure_time=20.0, add_poisson_noise=True, noise_seed=1
exposure_time=1.0,
psf=psf,
add_poisson_noise_to_data=False,
include_poisson_noise_in_noise_map=False,
normalize_psf=False,
)

dataset = simulator.via_image_from(image=image)
dataset = simulator.via_image_from(image=image_central_delta_3x3)

assert dataset.data.native == pytest.approx(
np.array([[1.05, 1.3, 1.25], [1.05, 2.1, 1.2], [1.05, 1.3, 1.15]]), 1e-2
)
assert (
dataset.data.native
== np.array([[0.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 0.0]])
).all()

assert dataset.noise_map.native == pytest.approx(
np.array([[0.229, 0.255, 0.25], [0.229, 0.324, 0.245], [0.229, 0.255, 0.240]]),
1e-2,
)

def test__via_image_from__psf_on__disable_poisson_noise_in_data(
image_central_delta_3x3,
):
psf = aa.Kernel2D.no_mask(
values=np.array([[0.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 0.0]]),
pixel_scales=1.0,
)

def test__via_image_from__background_sky_on(image_central_delta_3x3):
simulator = aa.SimulatorImaging(
exposure_time=1.0,
background_sky_level=16.0,
add_poisson_noise=True,
exposure_time=20.0,
psf=psf,
normalize_psf=False,
add_poisson_noise_to_data=False,
include_poisson_noise_in_noise_map=True,
noise_seed=1,
)

dataset = simulator.via_image_from(image=image_central_delta_3x3)

assert (
dataset.data.native
== np.array([[1.0, 5.0, 4.0], [1.0, 2.0, 1.0], [5.0, 2.0, 7.0]])
== np.array([[0.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 0.0]])
).all()

assert dataset.noise_map.native[0, 0] == pytest.approx(4.12310, 1.0e-4)
assert dataset.noise_map.native == pytest.approx(
np.array(
[[0.0, 0.22912, 0.0], [0.25495, 0.34278, 0.22912], [0.0, 0.229128, 0.0]]
),
1e-2,
)


def test__via_image_from__psf_and_noise_both_on(image_central_delta_3x3):
def test__via_image_from__psf_on__psf_and_noise_both_on(image_central_delta_3x3):
image = image_central_delta_3x3 + 1.0

psf = aa.Kernel2D.no_mask(
Expand All @@ -108,7 +158,7 @@ def test__via_image_from__psf_and_noise_both_on(image_central_delta_3x3):
simulator = aa.SimulatorImaging(
exposure_time=20.0,
psf=psf,
add_poisson_noise=True,
add_poisson_noise_to_data=True,
noise_seed=1,
normalize_psf=False,
)
Expand Down

0 comments on commit fbf65b0

Please sign in to comment.