File: C:/Users/fred/anaconda3/Lib/site-packages/astropy/convolution/tests/test_discretize.py
# Licensed under a 3-clause BSD style license - see LICENSE.rst
import itertools
import numpy as np
import pytest
from numpy.testing import assert_allclose
from astropy.convolution.utils import discretize_model
from astropy.modeling.functional_models import (
Box1D,
Box2D,
Gaussian1D,
Gaussian2D,
RickerWavelet1D,
RickerWavelet2D,
)
from astropy.modeling.tests.example_models import models_1D, models_2D
from astropy.modeling.tests.test_models import create_model
from astropy.utils.compat.optional_deps import HAS_SCIPY
modes = ["center", "linear_interp", "oversample"]
test_models_1D = [Gaussian1D, Box1D, RickerWavelet1D]
test_models_2D = [Gaussian2D, Box2D, RickerWavelet2D]
@pytest.mark.parametrize(
("model_class", "mode"), list(itertools.product(test_models_1D, modes))
)
def test_pixel_sum_1D(model_class, mode):
"""
Test if the sum of all pixels corresponds nearly to the integral.
"""
if model_class == Box1D and mode == "center":
pytest.skip("Non integrating mode. Skip integral test.")
parameters = models_1D[model_class]
model = create_model(model_class, parameters)
values = discretize_model(model, models_1D[model_class]["x_lim"], mode=mode)
assert_allclose(values.sum(), models_1D[model_class]["integral"], atol=0.0001)
@pytest.mark.parametrize("mode", modes)
def test_gaussian_eval_1D(mode):
"""
Discretize Gaussian with different modes and check
if result is at least similar to Gaussian1D.eval().
"""
model = Gaussian1D(1, 0, 20)
x = np.arange(-100, 101)
values = model(x)
disc_values = discretize_model(model, (-100, 101), mode=mode)
assert_allclose(values, disc_values, atol=0.001)
@pytest.mark.parametrize(
("model_class", "mode"), list(itertools.product(test_models_2D, modes))
)
def test_pixel_sum_2D(model_class, mode):
"""
Test if the sum of all pixels corresponds nearly to the integral.
"""
if model_class == Box2D and mode == "center":
pytest.skip("Non integrating mode. Skip integral test.")
parameters = models_2D[model_class]
model = create_model(model_class, parameters)
values = discretize_model(
model,
models_2D[model_class]["x_lim"],
models_2D[model_class]["y_lim"],
mode=mode,
)
assert_allclose(values.sum(), models_2D[model_class]["integral"], atol=0.0001)
@pytest.mark.parametrize(
("model_class", "mode"), list(itertools.product(test_models_2D, modes))
)
def test_pixel_sum_compound_2D(model_class, mode):
"""
Test if the sum of all pixels of a compound model corresponds nearly to the integral.
"""
if model_class == Box2D and mode == "center":
pytest.skip("Non integrating mode. Skip integral test.")
parameters = models_2D[model_class]
model = create_model(model_class, parameters)
values = discretize_model(
model + model,
models_2D[model_class]["x_lim"],
models_2D[model_class]["y_lim"],
mode=mode,
)
model_integral = 2 * models_2D[model_class]["integral"]
assert_allclose(values.sum(), model_integral, atol=0.0001)
@pytest.mark.parametrize("mode", modes)
def test_gaussian_eval_2D(mode):
"""
Discretize Gaussian with different modes and check
if result is at least similar to Gaussian2D.eval()
"""
model = Gaussian2D(0.01, 0, 0, 1, 1)
x = np.arange(-2, 3)
y = np.arange(-2, 3)
x, y = np.meshgrid(x, y)
values = model(x, y)
disc_values = discretize_model(model, (-2, 3), (-2, 3), mode=mode)
assert_allclose(values, disc_values, atol=1e-2)
@pytest.mark.skipif(not HAS_SCIPY, reason="Requires scipy")
@pytest.mark.slow
def test_gaussian_eval_2D_integrate_mode():
"""
Discretize Gaussian with integrate mode
"""
model_list = [
Gaussian2D(0.01, 0, 0, 2, 2),
Gaussian2D(0.01, 0, 0, 1, 2),
Gaussian2D(0.01, 0, 0, 2, 1),
]
x = np.arange(-2, 3)
y = np.arange(-2, 3)
x, y = np.meshgrid(x, y)
for model in model_list:
values = model(x, y)
disc_values = discretize_model(model, (-2, 3), (-2, 3), mode="integrate")
assert_allclose(values, disc_values, atol=1e-2)
@pytest.mark.skipif(not HAS_SCIPY, reason="Requires scipy")
def test_subpixel_gauss_1D():
"""
Test subpixel accuracy of the integrate mode with gaussian 1D model.
"""
gauss_1D = Gaussian1D(1, 0, 0.1)
values = discretize_model(gauss_1D, (-1, 2), mode="integrate", factor=100)
assert_allclose(values.sum(), np.sqrt(2 * np.pi) * 0.1, atol=0.00001)
@pytest.mark.skipif(not HAS_SCIPY, reason="Requires scipy")
def test_subpixel_gauss_2D():
"""
Test subpixel accuracy of the integrate mode with gaussian 2D model.
"""
gauss_2D = Gaussian2D(1, 0, 0, 0.1, 0.1)
values = discretize_model(gauss_2D, (-1, 2), (-1, 2), mode="integrate", factor=100)
assert_allclose(values.sum(), 2 * np.pi * 0.01, atol=0.00001)
def test_discretize_callable_1d():
"""
Test discretize when a 1d function is passed.
"""
def f(x):
return x**2
y = discretize_model(f, (-5, 6))
assert_allclose(y, np.arange(-5, 6) ** 2)
def test_discretize_callable_2d():
"""
Test discretize when a 2d function is passed.
"""
def f(x, y):
return x**2 + y**2
actual = discretize_model(f, (-5, 6), (-5, 6))
y, x = np.indices((11, 11)) - 5
desired = x**2 + y**2
assert_allclose(actual, desired)
def test_type_exception():
"""
Test type exception.
"""
with pytest.raises(TypeError, match=r"Model must be callable\."):
discretize_model(float(0), (-10, 11))
def test_dim_exception_1d():
"""
Test dimension exception 1d.
"""
def f(x):
return x**2
with pytest.raises(ValueError, match=r"y_range should not be input for a 1D model"):
discretize_model(f, (-10, 11), (-10, 11))
def test_dim_exception_2d():
"""
Test dimension exception 2d.
"""
def f(x, y):
return x**2 + y**2
with pytest.raises(ValueError, match=r"y_range must be specified for a 2D model"):
discretize_model(f, (-10, 11))
def test_float_x_range_exception():
def f(x, y):
return x**2 + y**2
with pytest.raises(
ValueError,
match=(
r"The difference between the upper and lower limit of 'x_range' must be a"
r" whole number\."
),
):
discretize_model(f, (-10.002, 11.23))
def test_float_y_range_exception():
def f(x, y):
return x**2 + y**2
with pytest.raises(
ValueError,
match=(
r"The difference between the upper and lower limit of 'y_range' must be a"
r" whole number\."
),
):
discretize_model(f, (-10, 11), (-10.002, 11.23))
def test_discretize_oversample():
gauss_2D = Gaussian2D(
amplitude=1.0, x_mean=5.0, y_mean=125.0, x_stddev=0.75, y_stddev=3
)
values = discretize_model(
gauss_2D, x_range=[0, 10], y_range=[100, 135], mode="oversample", factor=10
)
vmax = np.max(values)
vmax_yx = np.unravel_index(values.argmax(), values.shape)
values_osf1 = discretize_model(
gauss_2D, x_range=[0, 10], y_range=[100, 135], mode="oversample", factor=1
)
values_center = discretize_model(
gauss_2D, x_range=[0, 10], y_range=[100, 135], mode="center"
)
assert values.shape == (35, 10)
assert_allclose(vmax, 0.927, atol=1e-3)
assert vmax_yx == (25, 5)
assert_allclose(values_center, values_osf1)
def test_oversample_factor():
gauss_1D = Gaussian1D(1, 0, 0.1)
msg = "factor must have an integer value"
with pytest.raises(ValueError, match=msg):
discretize_model(gauss_1D, (-1, 2), mode="oversample", factor=1.2)