Skip to content

Commit 87ca150

Browse files
brokkoli71dcherian
andauthored
support zero-sized chunks (#2434)
* support zero-sized chunks * fix imports * add min_side=0 to testing strategies * fix property tests --------- Co-authored-by: Deepak Cherian <deepak@cherian.net>
1 parent bc588a7 commit 87ca150

File tree

4 files changed

+27
-8
lines changed

4 files changed

+27
-8
lines changed

src/zarr/core/indexing.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def __iter__(self) -> Iterator[ChunkProjection]: ...
9494

9595

9696
def ceildiv(a: float, b: float) -> int:
97+
if a == 0:
98+
return 0
9799
return math.ceil(a / b)
98100

99101

@@ -374,7 +376,7 @@ def __init__(self, dim_sel: slice, dim_len: int, dim_chunk_len: int) -> None:
374376

375377
def __iter__(self) -> Iterator[ChunkDimProjection]:
376378
# figure out the range of chunks we need to visit
377-
dim_chunk_ix_from = self.start // self.dim_chunk_len
379+
dim_chunk_ix_from = 0 if self.start == 0 else self.start // self.dim_chunk_len
378380
dim_chunk_ix_to = ceildiv(self.stop, self.dim_chunk_len)
379381

380382
# iterate over chunks in range

src/zarr/testing/strategies.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def v2_dtypes() -> st.SearchStrategy[np.dtype]:
6565
stores = st.builds(MemoryStore, st.just({}), mode=st.just("w"))
6666
compressors = st.sampled_from([None, "default"])
6767
zarr_formats: st.SearchStrategy[Literal[2, 3]] = st.sampled_from([2, 3])
68-
array_shapes = npst.array_shapes(max_dims=4)
68+
array_shapes = npst.array_shapes(max_dims=4, min_side=0)
6969

7070

7171
@st.composite # type: ignore[misc]
@@ -85,17 +85,24 @@ def numpy_arrays(
8585
@st.composite # type: ignore[misc]
8686
def np_array_and_chunks(
8787
draw: st.DrawFn, *, arrays: st.SearchStrategy[np.ndarray] = numpy_arrays
88-
) -> tuple[np.ndarray, tuple[int]]: # type: ignore[type-arg]
88+
) -> tuple[np.ndarray, tuple[int, ...]]: # type: ignore[type-arg]
8989
"""A hypothesis strategy to generate small sized random arrays.
9090
9191
Returns: a tuple of the array and a suitable random chunking for it.
9292
"""
9393
array = draw(arrays)
9494
# We want this strategy to shrink towards arrays with smaller number of chunks
9595
# 1. st.integers() shrinks towards smaller values. So we use that to generate number of chunks
96-
numchunks = draw(st.tuples(*[st.integers(min_value=1, max_value=size) for size in array.shape]))
96+
numchunks = draw(
97+
st.tuples(
98+
*[st.integers(min_value=0 if size == 0 else 1, max_value=size) for size in array.shape]
99+
)
100+
)
97101
# 2. and now generate the chunks tuple
98-
chunks = tuple(size // nchunks for size, nchunks in zip(array.shape, numchunks, strict=True))
102+
chunks = tuple(
103+
size // nchunks if nchunks > 0 else 0
104+
for size, nchunks in zip(array.shape, numchunks, strict=True)
105+
)
99106
return (array, chunks)
100107

101108

tests/test_indexing.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from numpy.testing import assert_array_equal
1212

1313
import zarr
14+
from zarr import Array
1415
from zarr.core.buffer import BufferPrototype, default_buffer_prototype
1516
from zarr.core.indexing import (
1617
BasicSelection,
@@ -31,7 +32,6 @@
3132
if TYPE_CHECKING:
3233
from collections.abc import AsyncGenerator
3334

34-
from zarr.core.array import Array
3535
from zarr.core.buffer.core import Buffer
3636
from zarr.core.common import ChunkCoords
3737

@@ -1927,3 +1927,11 @@ def test_indexing_with_zarr_array(store: StorePath) -> None:
19271927

19281928
assert_array_equal(a[ii], za[zii])
19291929
assert_array_equal(a[ii], za.oindex[zii])
1930+
1931+
1932+
@pytest.mark.parametrize("store", ["local", "memory"], indirect=["store"])
1933+
@pytest.mark.parametrize("shape", [(0, 2, 3), (0), (3, 0)])
1934+
def test_zero_sized_chunks(store: StorePath, shape: list[int]) -> None:
1935+
z = Array.create(store=store, shape=shape, chunk_shape=shape, zarr_format=3, dtype="f8")
1936+
z[...] = 42
1937+
assert_array_equal(z[...], np.zeros(shape, dtype="f8"))

tests/test_properties.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import hypothesis.extra.numpy as npst # noqa: E402
88
import hypothesis.strategies as st # noqa: E402
9-
from hypothesis import given # noqa: E402
9+
from hypothesis import assume, given # noqa: E402
1010

1111
from zarr.testing.strategies import arrays, basic_indices, numpy_arrays, zarr_formats # noqa: E402
1212

@@ -35,11 +35,13 @@ def test_basic_indexing(data: st.DataObject) -> None:
3535
@given(data=st.data())
3636
def test_vindex(data: st.DataObject) -> None:
3737
zarray = data.draw(arrays())
38+
# integer_array_indices can't handle 0-size dimensions.
39+
assume(all(s > 0 for s in zarray.shape))
3840
nparray = zarray[:]
3941

4042
indexer = data.draw(
4143
npst.integer_array_indices(
42-
shape=nparray.shape, result_shape=npst.array_shapes(max_dims=None)
44+
shape=nparray.shape, result_shape=npst.array_shapes(min_side=1, max_dims=None)
4345
)
4446
)
4547
actual = zarray.vindex[indexer]

0 commit comments

Comments
 (0)