|
| 1 | +import math |
1 | 2 | import sys |
2 | 3 | from typing import Any, Literal |
3 | 4 |
|
4 | 5 | import hypothesis.extra.numpy as npst |
5 | 6 | import hypothesis.strategies as st |
6 | 7 | import numpy as np |
7 | | -from hypothesis import given, settings # noqa: F401 |
| 8 | +from hypothesis import event, given, settings # noqa: F401 |
8 | 9 | from hypothesis.strategies import SearchStrategy |
9 | 10 |
|
10 | 11 | import zarr |
@@ -97,7 +98,8 @@ def clear_store(x: Store) -> Store: |
97 | 98 | stores = st.builds(MemoryStore, st.just({})).map(clear_store) |
98 | 99 | compressors = st.sampled_from([None, "default"]) |
99 | 100 | zarr_formats: st.SearchStrategy[ZarrFormat] = st.sampled_from([3, 2]) |
100 | | -array_shapes = npst.array_shapes(max_dims=4, min_side=0) |
| 101 | +# We de-prioritize arrays having dim sizes 0, 1, 2 |
| 102 | +array_shapes = npst.array_shapes(max_dims=4, min_side=3) | npst.array_shapes(max_dims=4, min_side=0) |
101 | 103 |
|
102 | 104 |
|
103 | 105 | @st.composite # type: ignore[misc] |
@@ -174,11 +176,19 @@ def chunk_shapes(draw: st.DrawFn, *, shape: tuple[int, ...]) -> tuple[int, ...]: |
174 | 176 | st.tuples(*[st.integers(min_value=0 if size == 0 else 1, max_value=size) for size in shape]) |
175 | 177 | ) |
176 | 178 | # 2. and now generate the chunks tuple |
177 | | - return tuple( |
| 179 | + chunks = tuple( |
178 | 180 | size // nchunks if nchunks > 0 else 0 |
179 | 181 | for size, nchunks in zip(shape, numchunks, strict=True) |
180 | 182 | ) |
181 | 183 |
|
| 184 | + for c in chunks: |
| 185 | + event("chunk size", c) |
| 186 | + |
| 187 | + if any((c != 0 and s % c != 0) for s, c in zip(shape, chunks, strict=True)): |
| 188 | + event("smaller last chunk") |
| 189 | + |
| 190 | + return chunks |
| 191 | + |
182 | 192 |
|
183 | 193 | @st.composite # type: ignore[misc] |
184 | 194 | def shard_shapes( |
@@ -267,23 +277,55 @@ def arrays( |
267 | 277 | return a |
268 | 278 |
|
269 | 279 |
|
| 280 | +@st.composite # type: ignore[misc] |
| 281 | +def simple_arrays( |
| 282 | + draw: st.DrawFn, |
| 283 | + *, |
| 284 | + shapes: st.SearchStrategy[tuple[int, ...]] = array_shapes, |
| 285 | +) -> Any: |
| 286 | + return draw( |
| 287 | + arrays( |
| 288 | + shapes=shapes, |
| 289 | + attrs=st.none(), |
| 290 | + paths=paths(max_num_nodes=2), |
| 291 | + compressors=st.sampled_from([None, "default"]), |
| 292 | + ) |
| 293 | + ) |
| 294 | + |
| 295 | + |
270 | 296 | def is_negative_slice(idx: Any) -> bool: |
271 | 297 | return isinstance(idx, slice) and idx.step is not None and idx.step < 0 |
272 | 298 |
|
273 | 299 |
|
| 300 | +@st.composite # type: ignore[misc] |
| 301 | +def end_slices(draw: st.DrawFn, *, shape: tuple[int]) -> Any: |
| 302 | + """ |
| 303 | + A strategy that slices ranges that include the last chunk. |
| 304 | + This is intended to stress-test handling of a possibly smaller last chunk. |
| 305 | + """ |
| 306 | + slicers = [] |
| 307 | + for size in shape: |
| 308 | + start = draw(st.integers(min_value=size // 2, max_value=size - 1)) |
| 309 | + length = draw(st.integers(min_value=0, max_value=size - start)) |
| 310 | + slicers.append(slice(start, start + length)) |
| 311 | + event("drawing end slice") |
| 312 | + return tuple(slicers) |
| 313 | + |
| 314 | + |
274 | 315 | @st.composite # type: ignore[misc] |
275 | 316 | def basic_indices(draw: st.DrawFn, *, shape: tuple[int], **kwargs: Any) -> Any: |
276 | 317 | """Basic indices without unsupported negative slices.""" |
277 | | - return draw( |
278 | | - npst.basic_indices(shape=shape, **kwargs).filter( |
279 | | - lambda idxr: ( |
280 | | - not ( |
281 | | - is_negative_slice(idxr) |
282 | | - or (isinstance(idxr, tuple) and any(is_negative_slice(idx) for idx in idxr)) |
283 | | - ) |
| 318 | + strategy = npst.basic_indices(shape=shape, **kwargs).filter( |
| 319 | + lambda idxr: ( |
| 320 | + not ( |
| 321 | + is_negative_slice(idxr) |
| 322 | + or (isinstance(idxr, tuple) and any(is_negative_slice(idx) for idx in idxr)) |
284 | 323 | ) |
285 | 324 | ) |
286 | 325 | ) |
| 326 | + if math.prod(shape) >= 3: |
| 327 | + strategy = end_slices(shape=shape) | strategy |
| 328 | + return draw(strategy) |
287 | 329 |
|
288 | 330 |
|
289 | 331 | @st.composite # type: ignore[misc] |
|
0 commit comments