Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 46 additions & 37 deletions src/boost_histogram/_internal/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
16 changes: 16 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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))