Skip to content

Commit 485be6f

Browse files
committed
Add float64 preservation tests for convolve_2d (#1096)
- test_convolve_2d_preserves_float64_1096: float64 input at 1e7 magnitude retains precision across all 4 backends - test_convolve_2d_int_promotes_to_float32_1096: int32 input gets promoted to float, not left as integer
1 parent fe940d9 commit 485be6f

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

xrspatial/tests/test_focal.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,62 @@ def test_convolution_2d_boundary_no_nan(boundary):
840840
np_result.data, da_result.data.compute(), equal_nan=True, rtol=1e-5)
841841

842842

843+
# --- convolve_2d float64 preservation (issue-1096) ---
844+
845+
846+
@pytest.mark.parametrize("backend", ['numpy', 'dask+numpy', 'cupy', 'dask+cupy'])
847+
def test_convolve_2d_preserves_float64_1096(backend):
848+
"""Float64 input should produce float64 output, not float32.
849+
850+
Regression test for #1096: convolve_2d hardcoded .astype(float32)
851+
across all backends.
852+
"""
853+
from xrspatial.tests.general_checks import has_cuda_and_cupy
854+
if 'cupy' in backend and not has_cuda_and_cupy():
855+
pytest.skip("Requires CUDA and CuPy")
856+
if 'dask' in backend and da is None:
857+
pytest.skip("Requires Dask")
858+
859+
# Values near 1e7 where float32 loses the 0.0x differences
860+
data = np.array([[1e7 + 0.01, 1e7 + 0.02, 1e7 + 0.03],
861+
[1e7 + 0.04, 1e7 + 0.05, 1e7 + 0.06],
862+
[1e7 + 0.07, 1e7 + 0.08, 1e7 + 0.09],
863+
[1e7 + 0.10, 1e7 + 0.11, 1e7 + 0.12]], dtype=np.float64)
864+
kernel = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype=np.float64)
865+
866+
agg = create_test_raster(data, backend=backend, chunks=(4, 3))
867+
result = convolve_2d(agg.data, kernel)
868+
869+
if hasattr(result, 'compute'):
870+
result = result.compute()
871+
if hasattr(result, 'get'):
872+
result = result.get()
873+
result = np.asarray(result)
874+
875+
# Output must be float64
876+
assert result.dtype == np.float64, f"got {result.dtype}"
877+
878+
# Interior pixel (1,1): kernel cross hits [0.02, 0.04, 0.05, 0.06, 0.08]
879+
# Expected: sum = 5e7 + 0.25
880+
expected_center = 5e7 + 0.25
881+
assert abs(float(result[1, 1]) - expected_center) < 1e-8, (
882+
f"center={result[1, 1]}, expected={expected_center}"
883+
)
884+
885+
886+
def test_convolve_2d_int_promotes_to_float32_1096():
887+
"""Integer input should be promoted to float32 (not stay int)."""
888+
data = np.array([[1, 2, 3],
889+
[4, 5, 6],
890+
[7, 8, 9]], dtype=np.int32)
891+
kernel = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype=np.float64)
892+
893+
result = convolve_2d(data, kernel)
894+
assert np.issubdtype(result.dtype, np.floating), f"got {result.dtype}"
895+
# Interior pixel (1,1): cross sum = 2+4+5+6+8 = 25
896+
assert float(result[1, 1]) == 25.0
897+
898+
843899
# --- 3D (multi-band) focal tests ---
844900

845901

0 commit comments

Comments
 (0)