diff --git a/src/boost_histogram/_internal/view.py b/src/boost_histogram/_internal/view.py index 88e247766..f9f717fc2 100644 --- a/src/boost_histogram/_internal/view.py +++ b/src/boost_histogram/_internal/view.py @@ -144,52 +144,61 @@ def __array_ufunc__( ) return result.view(self.__class__) # type: ignore[no-any-return] + # If unsupported, just pass through (will return not implemented) + return super().__array_ufunc__(ufunc, method, *inputs, **kwargs) # type: ignore[misc, no-any-return] + # View with normal value or array - else: - if ufunc in {np.add, np.subtract}: - if self.dtype == input_0.dtype: - ufunc(input_0["value"], input_1, out=result["value"], **kwargs) - np.add( - input_0["variance"], - input_1**2, - out=result["variance"], - **kwargs, - ) - else: - ufunc(input_0, input_1["value"], out=result["value"], **kwargs) - np.add( - input_0**2, - input_1["variance"], - out=result["variance"], - **kwargs, - ) - return result.view(self.__class__) # type: ignore[no-any-return] + if ufunc in {np.add, np.subtract}: + if self.dtype == input_0.dtype: + ufunc(input_0["value"], input_1, out=result["value"], **kwargs) + np.add( + input_0["variance"], + input_1**2, + out=result["variance"], + **kwargs, + ) + else: + ufunc(input_0, input_1["value"], out=result["value"], **kwargs) + np.add( + input_0**2, + input_1["variance"], + out=result["variance"], + **kwargs, + ) + return result.view(self.__class__) # type: ignore[no-any-return] - if ufunc in {np.multiply, np.divide, np.true_divide, np.floor_divide}: - if self.dtype == input_0.dtype: - ufunc(input_0["value"], input_1, out=result["value"], **kwargs) - ufunc( - input_0["variance"], - input_1**2, - out=result["variance"], - **kwargs, - ) - else: - ufunc(input_0, input_1["value"], out=result["value"], **kwargs) - ufunc( - input_0**2, - input_1["variance"], - out=result["variance"], - **kwargs, - ) + if ufunc in {np.multiply, np.divide, np.true_divide, np.floor_divide}: + if self.dtype == input_0.dtype: + ufunc(input_0["value"], input_1, out=result["value"], **kwargs) + ufunc( + input_0["variance"], + input_1**2, + out=result["variance"], + **kwargs, + ) + else: + ufunc(input_0, input_1["value"], out=result["value"], **kwargs) + ufunc( + input_0**2, + input_1["variance"], + out=result["variance"], + **kwargs, + ) - return result.view(self.__class__) # type: ignore[no-any-return] + return result.view(self.__class__) # type: ignore[no-any-return] # ufuncs that are allowed to reduce if ufunc in {np.add} and method == "reduce" and len(inputs) == 1: results = (ufunc.reduce(self[field], **kwargs) for field in self._FIELDS) return self._PARENT._make(*results) # type: ignore[no-any-return] + # ufuncs that are allowed to accumulate + if ufunc in {np.add} and method == "accumulate" and len(inputs) == 1: + (result,) = kwargs.pop("out", [np.empty(self.shape, self.dtype)]) + for field in self._FIELDS: + ufunc.accumulate(self[field], out=result[field], **kwargs) + return result.view(self.__class__) # type: ignore[no-any-return] + # If unsupported, just pass through (will return not implemented) return super().__array_ufunc__(ufunc, method, *inputs, **kwargs) # type: ignore[misc, no-any-return] diff --git a/tests/test_views.py b/tests/test_views.py index 95c6cb954..e70b63942 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,5 +1,7 @@ +import numpy as np import pytest from numpy.testing import assert_allclose +from pytest import approx import boost_histogram as bh @@ -183,3 +185,17 @@ def test_view_assign_wmean(): assert w[0].sum_of_weights_squared == 1 assert w.value[0] == 2 assert w[0].variance == 3 + + +# Issue #696 +def test_view_cumsum(): + h = bh.Histogram( + bh.axis.Integer(1, 10, underflow=True, overflow=False), + storage=bh.storage.Weight(), + ) + h.fill([2, 3], weight=[1.5, 2.5]) + + view = h.view() + c_view = np.cumsum(view) + assert c_view.value == approx(np.cumsum(view.value)) + assert c_view.variance == approx(np.cumsum(view.variance))