Skip to content

Commit 802c860

Browse files
committed
No (easy) optimisation for booleans possible. Now pass all ndarray_object tests!
1 parent 578d234 commit 802c860

3 files changed

Lines changed: 19 additions & 22 deletions

File tree

‎src/blosc2/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ def __array_namespace_info__() -> Info:
351351
get_expr_operands,
352352
validate_expr,
353353
evaluate,
354+
slices_eval,
354355
)
355356
from .proxy import Proxy, ProxySource, ProxyNDSource, ProxyNDField, SimpleProxy, jit
356357

‎src/blosc2/lazyexpr.py‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,11 +1506,8 @@ def slices_eval( # noqa: C901
15061506
if out is None:
15071507
shape_ = shape_slice if shape_slice is not None else shape
15081508
if where is not None and len(where) < 2:
1509-
# The result is a linear array in the first ndims
1510-
try: # call from NDArray.__getitem__
1511-
shape_ = (math.prod(operands["key"].shape),) + shape[operands["key"].ndim :]
1512-
except KeyError:
1513-
shape_ = math.prod(shape_)
1509+
# The result is a linear array
1510+
shape_ = math.prod(shape_)
15141511
if getitem or _order:
15151512
out = np.empty(shape_, dtype=dtype_)
15161513
if _order:

‎src/blosc2/ndarray.py‎

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,8 @@ def get_fselection_numpy(self, key: list | np.ndarray) -> np.ndarray:
15991599
return out.reshape(out_shape) # should have filled in correct order, just need to reshape
16001600

16011601
# Default when there are booleans
1602+
# TODO: for boolean indexing could be optimised by avoiding
1603+
# calculating out_shape prior to loop and keeping track on-the-fly (like in LazyExpr machinery)
16021604
return self._get_set_findex_default(_slice, out_shape)
16031605

16041606
def _get_set_findex_default(self, _slice, out_shape=None, updater=None):
@@ -1718,36 +1720,36 @@ def __getitem__( # noqa: C901
17181720
expr = blosc2.LazyExpr._new_expr(key, self.fields, guess=False)
17191721
return expr.where(self)
17201722

1721-
# key not iterable
1722-
key = key[()] if isinstance(key, NDArray) else key
1723+
key = key[()] if isinstance(key, NDArray) else key # key not iterable
17231724
key = tuple(k[()] if isinstance(k, NDArray) else k for k in key) if isinstance(key, tuple) else key
17241725

17251726
# decompress NDArrays
17261727
key_, mask = process_key(key, self.shape) # internally handles key an integer
17271728
key = key[()] if hasattr(key, "shape") and key.shape == () else key # convert to scalar
1729+
17281730
# fancy indexing
17291731
if isinstance(key_, (list, np.ndarray)) or builtins.any(
17301732
isinstance(k, (list, np.ndarray)) for k in key_
17311733
):
1732-
# check scalar booleans, which add 1 dim to beginning but which cause problems for ndindex.as_subindex
1734+
# check scalar booleans, which add 1 dim to beginning
17331735
if np.issubdtype(type(key), bool) and np.isscalar(key):
17341736
if key:
17351737
_slice = ndindex.ndindex(()).expand(self.shape) # just get whole array
17361738
out_shape = _slice.newshape(self.shape)
17371739
return np.expand_dims(self._get_set_findex_default(_slice, out_shape=out_shape), 0)
17381740
else: # do nothing
17391741
return np.empty((0,) + self.shape, dtype=self.dtype)
1740-
elif hasattr(key, "dtype") and np.issubdtype(key.dtype, np.bool_): # check ORIGINAL key
1741-
# This can be interpreted as a boolean expression
1742-
# elif key.shape == self.shape: # This is faster than the fancy indexing path
1743-
# expr = blosc2.lazyexpr(f"(key)")
1744-
# The next should be a bit faster
1745-
expr = blosc2.LazyExpr._new_expr("key", {"key": key}, guess=False)
1742+
elif (
1743+
hasattr(key, "dtype") and np.issubdtype(key.dtype, np.bool_) and key.shape == self.shape
1744+
): # check ORIGINAL key
1745+
# This can be interpreted as a boolean expression but only for key shape same as self shape
1746+
expr = blosc2.LazyExpr._new_expr("key", {"key": key}, guess=False).where(self)
17461747
# Decorate with where and force a getitem operation to return actual values.
17471748
# This behavior is consistent with NumPy, although different from e.g. ['expr']
17481749
# which returns a lazy expression.
1749-
return expr.where(self)[:]
1750-
return self.get_fselection_numpy(key_) # fancy index
1750+
# This is faster than the fancy indexing path
1751+
return expr[:]
1752+
return self.get_fselection_numpy(key) # fancy index default, can be quite slow
17511753

17521754
start, stop, step, none_mask = get_ndarray_start_stop(self.ndim, key_, self.shape)
17531755
for i, s in enumerate(step): # (start, stop, -1) => stop < start
@@ -4096,7 +4098,7 @@ def save(array: NDArray, urlpath: str, contiguous=True, **kwargs: Any) -> None:
40964098
array.save(urlpath, contiguous, **kwargs)
40974099

40984100

4099-
def asarray(array: Sequence | np.ndarray | blosc2.C2Array | NDArray, copy=True, **kwargs: Any) -> NDArray: # noqa: C901
4101+
def asarray(array: Sequence | np.ndarray | blosc2.C2Array | NDArray, copy=True, **kwargs: Any) -> NDArray:
41004102
"""Convert the `array` to an `NDArray`.
41014103
41024104
Parameters
@@ -4159,10 +4161,7 @@ def asarray(array: Sequence | np.ndarray | blosc2.C2Array | NDArray, copy=True,
41594161
# A getitem operation should be enough to get a numpy array
41604162
array = array[()]
41614163

4162-
if dtype != array.dtype:
4163-
array = array.astype(dtype=dtype, casting=casting)
4164-
if not array.flags.contiguous:
4165-
array = np.ascontiguousarray(array)
4164+
array = np.require(array, dtype=dtype, requirements="C") # require contiguous array
41664165

41674166
return blosc2_ext.asarray(array, chunks, blocks, **kwargs)
41684167

@@ -4182,7 +4181,7 @@ def asarray(array: Sequence | np.ndarray | blosc2.C2Array | NDArray, copy=True,
41824181
for i, (c, s) in enumerate(zip(coords, chunks, strict=True))
41834182
)
41844183
# Ensure the array slice is contiguous and of correct dtype
4185-
array_slice = np.ascontiguousarray(array[slice_], dtype=dtype)
4184+
array_slice = np.require(array[slice_], dtype=dtype, requirements="C")
41864185
if behaved:
41874186
# The whole chunk is to be updated, so this fastpath is safe
41884187
ndarr.schunk.update_data(nchunk, array_slice, copy=False)

0 commit comments

Comments
 (0)