@@ -598,41 +598,58 @@ def block_indices(
598598 """
599599 Strategy for block-selection indexers over a *regular* chunk grid.
600600
601- Block indexing addresses whole chunks on the block grid rather than
602- individual elements. It only supports integers and step-1 slices over the
603- grid (strided block slices are rejected), so neither newaxis, ellipsis, nor
604- a step is generated here. The array-space translation below assumes a
605- regular (uniform) chunk grid, so ``shape`` must be evenly tiled by
606- ``chunks`` up to a possibly-smaller last chunk per dimension. Every
607- dimension must have at least one chunk (``size >= 1``).
601+ Block indexing is basic indexing applied to the block grid (the grid of
602+ chunks), so each axis is drawn with ``basic_indices`` over that axis's chunk
603+ count -- mirroring how ``orthogonal_indices`` reuses ``basic_indices`` per
604+ axis. Block indexing only supports integers and step-1 slices whose start
605+ references an existing chunk, so strided slices and slices starting at the
606+ grid edge are filtered out. The array-space translation assumes a regular
607+ (uniform) chunk grid, so ``shape`` must be evenly tiled by ``chunks`` up to a
608+ possibly-smaller last chunk per dimension, and every dimension must have at
609+ least one chunk (``size >= 1``).
608610
609611 Returns
610612 -------
611613 block_indexer
612- A tuple of ints / step-1 slices addressing whole chunks, suitable for
613- ``Array.blocks`` / ``Array. get_block_selection`` / ``set_block_selection``.
614+ A per-axis tuple of ints / step-1 slices addressing whole chunks,
615+ suitable for ``Array.blocks`` / ``get_block_selection`` / ``set_block_selection``.
614616 array_indexer
615617 The equivalent array-space selection (a tuple of slices) for indexing
616618 the corresponding numpy array, used as the comparison oracle.
617619 """
618620 grid_shape = tuple (- (- s // c ) for s , c in zip (shape , chunks , strict = True )) # ceil division
621+
622+ def supported (nchunks : int ) -> Callable [[tuple [Any , ...]], bool ]:
623+ # Block indexing only accepts step-1 slices whose start references an
624+ # existing chunk (a slice starting at nchunks raises, unlike numpy).
625+ def predicate (value : tuple [Any , ...]) -> bool :
626+ dim_sel = value [0 ]
627+ if isinstance (dim_sel , slice ):
628+ if dim_sel .step not in (None , 1 ):
629+ return False
630+ start = dim_sel .start or 0
631+ return 0 <= (start + nchunks if start < 0 else start ) < nchunks
632+ return True
633+
634+ return predicate
635+
619636 block_indexer : list [int | slice ] = []
620637 array_indexer : list [slice ] = []
621638 for size , chunk , nchunks in zip (shape , chunks , grid_shape , strict = True ):
622- if draw (st .booleans ()):
623- # a single block, sometimes addressed from the end with a negative index
624- block = draw (st .integers (min_value = - nchunks , max_value = nchunks - 1 ))
625- block_indexer .append (block )
626- start = (block % nchunks ) * chunk
627- array_indexer .append (slice (start , min (start + chunk , size )))
639+ (dim_sel ,) = draw (
640+ basic_indices (min_dims = 1 , shape = (nchunks ,), allow_ellipsis = False )
641+ # normalize bare ints / slices to a 1-tuple, skip the empty tuple
642+ .map (lambda x : (x ,) if not isinstance (x , tuple ) else x )
643+ .filter (bool )
644+ .filter (supported (nchunks ))
645+ )
646+ block_indexer .append (dim_sel )
647+ if isinstance (dim_sel , slice ):
648+ start , stop , _ = dim_sel .indices (nchunks )
649+ array_indexer .append (slice (start * chunk , min (stop * chunk , size )))
628650 else :
629- # a contiguous run of whole blocks (possibly empty). The start must
630- # reference an existing chunk: block indexing rejects a slice that
631- # starts at nchunks, unlike numpy which treats arr[len:len] as empty.
632- start_block = draw (st .integers (min_value = 0 , max_value = nchunks - 1 ))
633- stop_block = draw (st .integers (min_value = start_block , max_value = nchunks ))
634- block_indexer .append (slice (start_block , stop_block ))
635- array_indexer .append (slice (start_block * chunk , min (stop_block * chunk , size )))
651+ block = dim_sel % nchunks
652+ array_indexer .append (slice (block * chunk , min ((block + 1 ) * chunk , size )))
636653 return tuple (block_indexer ), tuple (array_indexer )
637654
638655
0 commit comments