From 42d3104fa72d4c461c7817646501bdb4b9eb7e30 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Sat, 7 Oct 2023 13:18:30 +0200 Subject: [PATCH] Ensure mask calculation is correct also for multi-op ufunc with scalars --- astropy/utils/masked/core.py | 4 +++- astropy/utils/masked/tests/test_functions.py | 22 ++++++++++++++++++++ docs/changes/utils/15450.bugfix.rst | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 docs/changes/utils/15450.bugfix.rst diff --git a/astropy/utils/masked/core.py b/astropy/utils/masked/core.py index aa16e51354a..7cf2edcf62b 100644 --- a/astropy/utils/masked/core.py +++ b/astropy/utils/masked/core.py @@ -697,7 +697,9 @@ def _combine_masks(self, masks, out=None, where=True, copy=True): np.copyto(out, masks[0], where=where) return out - out = np.logical_or(masks[0], masks[1], out=out, where=where) + # [...] at the end to ensure we have an array, not a scalar, and + # thus can be used for in-place changes in the loop. + out = np.logical_or(masks[0], masks[1], out=out, where=where)[...] for mask in masks[2:]: np.logical_or(out, mask, out=out, where=where) return out diff --git a/astropy/utils/masked/tests/test_functions.py b/astropy/utils/masked/tests/test_functions.py index 9bdc3ccba67..9ace08958f6 100644 --- a/astropy/utils/masked/tests/test_functions.py +++ b/astropy/utils/masked/tests/test_functions.py @@ -5,6 +5,7 @@ coverage. Complete coverage of all numpy functions is done with less detailed tests in test_function_helpers. """ +import erfa.ufunc as erfa_ufunc import numpy as np import pytest from numpy.testing import assert_array_equal @@ -151,6 +152,27 @@ def test_3op_ufunc(self): assert_array_equal(ma_mb.unmasked, expected_data) assert_array_equal(ma_mb.mask, expected_mask) + def test_multi_op_ufunc(self): + mask = [True, False, False] + iy = Masked([2000, 2001, 2002], mask=mask) + im = Masked([1, 2, 3], mask=mask) + idy = Masked([10, 20, 25], mask=mask) + ihr = Masked([11, 12, 13], mask=[False, False, True]) + imn = np.array([50, 51, 52]) + isc = np.array([12.5, 13.6, 14.7]) + result = erfa_ufunc.dtf2d("utc", iy, im, idy, ihr, imn, isc) + # Also test scalar + result0 = erfa_ufunc.dtf2d("utc", iy[0], im[0], idy[0], ihr[0], imn[0], isc[0]) + expected = erfa_ufunc.dtf2d( + "utc", iy.unmasked, im.unmasked, idy.unmasked, ihr.unmasked, imn, isc + ) + expected_mask = np.array([True, False, True]) + for res, res0, exp in zip(result, result0, expected): + assert_array_equal(res.unmasked, exp) + assert_array_equal(res.mask, expected_mask) + assert res0.unmasked == exp[0] + assert res0.mask == expected_mask[0] + @pytest.mark.parametrize("axis", (0, 1, None)) def test_add_reduce(self, axis): ma_reduce = np.add.reduce(self.ma, axis=axis) diff --git a/docs/changes/utils/15450.bugfix.rst b/docs/changes/utils/15450.bugfix.rst new file mode 100644 index 00000000000..07e2e9f7b73 --- /dev/null +++ b/docs/changes/utils/15450.bugfix.rst @@ -0,0 +1,2 @@ +Ufuncs with more than 2 operands (such as ``erfa.dtf2d``) now work +also if all inputs are scalars and more than two inputs have masks.