zarr_indexing.chunk_resolution
zarr_indexing.chunk_resolution ¶
Chunk resolution — mapping transforms to chunk-level I/O.
Given an IndexTransform (which coordinates a request reads) and one grid per
storage dimension (how storage is divided into chunks), chunk resolution
answers:
For each chunk, which storage coordinates does this transform touch,
and where do those values land in the request?
The public result is a lazy, reusable ChunkPlan whose rows are
ChunkProjections. Each identifies a chunk and pairs a chunk-local transform
with a transform back to the request's cells, over one shared zero-origin
cell domain, without assuming NumPy selectors, a codec pipeline, or a
scheduler.
The plan is computed in factored form, the GridPartition. Restricting a
transform to a chunk box distributes over output dimensions whenever each
output map reads its own input axis — every basic and orthogonal selection —
so each axis is resolved once against its grid into a table:
StridedSet— aConstantMaporDimensionMapaxis: one row per touched chunk, holding the chunk-local start, the extent, the request position of the first cell, and whether the row covers its chunk exactly once.IndexedSet— an orthogonalArrayMapaxis: its coordinates grouped by chunk in CSR form, with the request positions they fill.JointSet— one connected index-array component. Arrays sharing input axes are grouped together; independent components remain separate tables.
A projection is one row of each table combined. Building the tables costs the
sum of the touched chunks per axis rather than their product, rows are
materialized only on request, and a consumer may read the tables directly
instead. Two output maps that read one input axis through a DimensionMap
(a diagonal, which no selection produces) have no factored form and are
rejected with ValueError; a correlated index array varying over an axis a
DimensionMap also reads is rejected with NotImplementedError.
ChunkPlan
dataclass
¶
A reusable, lazy partition of an index transform over a chunk grid.
Construct plans with plan_chunks. The plan's factored form, a
GridPartition, is built on first use and memoized; iterating the plan
or projections() materializes fresh ChunkProjection rows from it.
Examples:
Row 1 of a (3, 4) array with (2, 2) chunks crosses two chunks, and
the plan can be walked again after it is exhausted:
>>> from zarr_indexing import IndexTransform
>>> from zarr_indexing.grid import dimension_grids_from_chunks
>>> grids = dimension_grids_from_chunks((2, 2), shape=(3, 4))
>>> plan = plan_chunks(IndexTransform.from_shape((3, 4))[1, :], grids)
>>> [p.chunk_coords for p in plan]
[(0, 0), (0, 1)]
>>> [p.chunk_coords for p in plan.projections()]
[(0, 0), (0, 1)]
Source code in src/zarr_indexing/chunk_resolution.py
dimension_grids
instance-attribute
¶
dimension_grids: tuple[DimensionGridLike, ...]
One grid per storage dimension, defining the chunk layout the plan walks.
__init__ ¶
__init__(
transform: IndexTransform,
dimension_grids: tuple[DimensionGridLike, ...],
) -> None
__iter__ ¶
__iter__() -> Iterator[ChunkProjection]
partition ¶
partition() -> GridPartition
The plan in factored, columnar form: one table per axis plus connected index-array tables.
Built once per plan and memoized. Raises ValueError if two output
maps read one input axis through a DimensionMap (a diagonal, which
no selection produces): its tables are per output dimension, and a
diagonal needs a strided set spanning several.
Source code in src/zarr_indexing/chunk_resolution.py
projections ¶
projections() -> Iterator[ChunkProjection]
ChunkProjection
dataclass
¶
One source-independent projection of a request through a chunk.
Both transforms share a synthetic input domain. chunk_transform maps
that domain to chunk-local storage coordinates; cell_transform maps it
to the original request domain.
Attributes:
-
chunk_coords(tuple[int, ...]) –Coordinates of the selected cell in the caller's grid.
-
chunk_domain(IndexDomain) –Bounds of that grid cell in global storage coordinates.
-
chunk_transform(IndexTransform) –Mapping from the shared synthetic domain to chunk-local storage.
-
cell_transform(IndexTransform) –Mapping from the shared synthetic domain to request coordinates.
-
coverage(ChunkCoverage) –Whether the request is proven to cover the whole grid cell exactly once. Fancy selections are conservatively
"unknown".
Examples:
Row 1 of a (3, 4) array with (2, 2) chunks touches only part of the
first chunk, whose domain spans rows [0, 2) and columns [0, 2):
>>> from zarr_indexing import IndexTransform
>>> from zarr_indexing.grid import dimension_grids_from_chunks
>>> grids = dimension_grids_from_chunks((2, 2), shape=(3, 4))
>>> plan = plan_chunks(IndexTransform.from_shape((3, 4))[1, :], grids)
>>> first = next(iter(plan))
>>> first.chunk_coords
(0, 0)
>>> first.chunk_domain.shape
(2, 2)
>>> first.coverage
'partial'
Source code in src/zarr_indexing/chunk_resolution.py
__init__ ¶
__init__(
chunk_coords: tuple[int, ...],
chunk_domain: IndexDomain,
chunk_transform: IndexTransform,
cell_transform: IndexTransform,
coverage: ChunkCoverage,
) -> None
__post_init__ ¶
Source code in src/zarr_indexing/chunk_resolution.py
GridPartition
dataclass
¶
A plan in factored form: per-axis tables whose product is the chunk walk.
sets holds one StridedSet or IndexedSet per output dimension the
transform reads independently, in output-dimension order; joint_sets
holds the connected index-array components. A projection is one row of each
table, so the partition has prod(row_shape) rows, walked
in row-major order over row_shape (the component tables after sets). Rows are materialized into
ChunkProjection objects only on request; a vectorized consumer can read
the tables directly.
Take one from ChunkPlan.partition.
Examples:
arr[1:6:2, 5:] on a (7, 9) array with (3, 4) chunks touches two
chunks along each axis, so the partition has four rows:
>>> from zarr_indexing import IndexTransform, plan_chunks
>>> from zarr_indexing.grid import dimension_grids_from_chunks
>>> grids = dimension_grids_from_chunks((3, 4), shape=(7, 9))
>>> partition = plan_chunks(IndexTransform.from_shape((7, 9))[1:6:2, 5:], grids).partition()
>>> partition.row_shape, len(partition)
((2, 2), 4)
>>> partition.chunk_coords().tolist()
[[0, 1], [0, 2], [1, 1], [1, 2]]
>>> [projection.chunk_transform.selection_repr for projection in partition][3]
'{ [0, 4) step 2, [0, 1) }'
Source code in src/zarr_indexing/chunk_resolution.py
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 | |
dimension_grids
instance-attribute
¶
dimension_grids: tuple[DimensionGridLike, ...]
One grid per storage dimension.
joint_sets
instance-attribute
¶
Connected index-array components, ordered by their first output dimension.
row_shape
instance-attribute
¶
Rows per table: sets followed by joint_sets, in row-major iteration order.
sets
instance-attribute
¶
sets: tuple[StridedSet | IndexedSet, ...]
Independent per-axis tables, in output-dimension order.
__init__ ¶
__init__(
transform: IndexTransform,
dimension_grids: tuple[DimensionGridLike, ...],
sets: tuple[StridedSet | IndexedSet, ...],
joint_sets: tuple[JointSet, ...],
row_shape: tuple[int, ...],
) -> None
__iter__ ¶
__iter__() -> Iterator[ChunkProjection]
Materialize every row in order.
Source code in src/zarr_indexing/chunk_resolution.py
chunk_coords ¶
Chunk coordinates of every row, shape (len(self), output rank), without materializing rows.
Source code in src/zarr_indexing/chunk_resolution.py
IndexedSet
dataclass
¶
One output dimension read through an orthogonal ArrayMap, one row per chunk.
The map's coordinates are grouped by chunk in CSR form: row i owns
index[pointer[i]:pointer[i + 1]] (the index-array values, in request
order) and positions[pointer[i]:pointer[i + 1]] (their positions along
the request axis, ascending). local gives the same values as chunk-local
storage coordinates. Columns are read-only NumPy arrays.
Examples:
>>> import numpy as np
>>> from zarr_indexing import IndexTransform, plan_chunks
>>> from zarr_indexing.grid import dimension_grids_from_chunks
>>> grids = dimension_grids_from_chunks((4,), shape=(10,))
>>> transform = IndexTransform.from_shape((10,)).oindex[np.array([9, 1, 2, 8])]
>>> (axis,) = plan_chunks(transform, grids).partition().sets
>>> axis.chunk.tolist(), axis.pointer.tolist()
([0, 2], [0, 2, 4])
>>> axis.local.tolist(), axis.positions.tolist()
([1, 2, 1, 0], [1, 2, 0, 3])
Source code in src/zarr_indexing/chunk_resolution.py
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | |
chunk
instance-attribute
¶
Chunk index along the axis, one per row, ascending.
chunk_extent
instance-attribute
¶
Data extent of each chunk.
chunk_start
instance-attribute
¶
Storage origin of each chunk.
input_dimension
instance-attribute
¶
input_dimension: int
The request axis the index array varies over.
local
property
¶
Memoized, read-only chunk-local coordinates, grouped like index.
offset
instance-attribute
¶
offset: int
The map's affine offset: storage is offset + stride * index.
pointer
instance-attribute
¶
CSR row pointer: row i owns entries pointer[i] to pointer[i + 1].
positions
instance-attribute
¶
Positions along the request axis, grouped by chunk, ascending within a row.
__init__ ¶
__init__(
output_dimension: int,
input_dimension: int,
offset: int,
stride: int,
chunk: ndarray[Any, dtype[intp]],
chunk_start: ndarray[Any, dtype[intp]],
chunk_extent: ndarray[Any, dtype[intp]],
pointer: ndarray[Any, dtype[intp]],
index: ndarray[Any, dtype[intp]],
positions: ndarray[Any, dtype[intp]],
) -> None
__post_init__ ¶
JointSet
dataclass
¶
One connected component of index arrays, grouped by the chunk each point lands in.
Arrays connected by shared input axes, possibly transitively, constrain
each other and are sorted into chunks together.
Row i is one touched chunk, chunk[i] its coordinates on the
output_dimensions, and CSR range pointer[i]:pointer[i + 1] its
points: index holds their index-array values per output dimension,
positions their flat positions in this component's input block, and
block_coordinates those positions unravelled over the block. Columns are
read-only NumPy arrays.
Examples:
>>> import numpy as np
>>> from zarr_indexing import IndexTransform, plan_chunks
>>> from zarr_indexing.grid import dimension_grids_from_chunks
>>> grids = dimension_grids_from_chunks((3, 4), shape=(7, 9))
>>> transform = IndexTransform.from_shape((7, 9)).vindex[
... np.array([0, 6, 6, 1]), np.array([8, 0, 1, 8])
... ]
>>> (joint,) = plan_chunks(transform, grids).partition().joint_sets
>>> joint.chunk.tolist(), joint.pointer.tolist(), joint.positions.tolist()
([[0, 2], [2, 0]], [0, 2, 4], [0, 3, 1, 2])
Source code in src/zarr_indexing/chunk_resolution.py
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | |
block_coordinates
property
¶
Memoized positions unravelled over broadcast_shape, shape (points, len(broadcast_axes)).
broadcast_axes
instance-attribute
¶
The request axes the arrays broadcast over.
chunk
instance-attribute
¶
Chunk coordinates on output_dimensions, shape (rows, k), lexicographic.
chunk_extent
instance-attribute
¶
Data extent of each chunk on output_dimensions, shape (rows, k).
chunk_start
instance-attribute
¶
Storage origin of each chunk on output_dimensions, shape (rows, k).
index
instance-attribute
¶
Index-array values per point and output dimension, shape (points, k).
local
property
¶
Memoized, read-only chunk-local coordinates, shape (points, k).
offsets
instance-attribute
¶
Affine offset of each array's map, aligned with output_dimensions.
output_dimensions
instance-attribute
¶
The storage axes read by correlated index arrays.
pointer
instance-attribute
¶
CSR row pointer into index, positions and block_coordinates.
positions
instance-attribute
¶
Flat block position of each point, ascending within a row.
__init__ ¶
__init__(
output_dimensions: tuple[int, ...],
offsets: tuple[int, ...],
strides: tuple[int, ...],
broadcast_axes: tuple[int, ...],
broadcast_shape: tuple[int, ...],
chunk: ndarray[Any, dtype[intp]],
chunk_start: ndarray[Any, dtype[intp]],
chunk_extent: ndarray[Any, dtype[intp]],
pointer: ndarray[Any, dtype[intp]],
index: ndarray[Any, dtype[intp]],
positions: ndarray[Any, dtype[intp]],
) -> None
__post_init__ ¶
StridedSet
dataclass
¶
One output dimension read through a ConstantMap or DimensionMap, one row per chunk.
Row i is the map restricted to chunk chunk[i] and re-based to
chunk-local, zero-origin coordinates: the chunk-local map is
DimensionMap(input_dimension, offset=local_start[i], stride=stride) over
[0, extent[i]) (a ConstantMap(local_start[i]) for a constant), and
its cells are request positions [origin[i], origin[i] + extent[i])
along the input axis, counted from the request domain's lower bound.
Columns are read-only NumPy arrays. extent and origin are measured
along the request axis, whose bounds are arbitrary Python ints; they are
intp unless a value does not fit, in which case they hold exact ints
(dtype=object).
Examples:
>>> from zarr_indexing import IndexTransform, plan_chunks
>>> from zarr_indexing.grid import dimension_grids_from_chunks
>>> grids = dimension_grids_from_chunks((4,), shape=(10,))
>>> (axis,) = plan_chunks(IndexTransform.from_shape((10,))[1:9:2], grids).partition().sets
>>> axis.chunk.tolist(), axis.local_start.tolist(), axis.extent.tolist(), axis.origin.tolist()
([0, 1], [1, 1], [2, 2], [0, 2])
Source code in src/zarr_indexing/chunk_resolution.py
chunk
instance-attribute
¶
Chunk index along the axis, one per row, ascending.
chunk_extent
instance-attribute
¶
Data extent of each chunk (clipped at the array boundary).
chunk_start
instance-attribute
¶
Storage origin of each chunk.
extent
instance-attribute
¶
Cells the row selects along the request axis (1 for a constant).
full
instance-attribute
¶
Whether the row covers its chunk's data extent exactly once (in either direction).
input_dimension
instance-attribute
¶
input_dimension: int | None
The request axis the map reads, or None for a constant.
local_start
instance-attribute
¶
Chunk-local storage coordinate of the row's first cell.
origin
instance-attribute
¶
Position of the row's first cell along the request axis (0 for a constant).
__init__ ¶
__init__(
output_dimension: int,
input_dimension: int | None,
stride: int,
chunk: ndarray[Any, dtype[intp]],
chunk_start: ndarray[Any, dtype[intp]],
chunk_extent: ndarray[Any, dtype[intp]],
local_start: ndarray[Any, dtype[intp]],
extent: ndarray[Any, dtype[intp]],
origin: ndarray[Any, dtype[intp]],
full: ndarray[Any, dtype[bool_]],
) -> None
plan_chunks ¶
plan_chunks(
transform: IndexTransform,
dimension_grids: Sequence[DimensionGridLike],
) -> ChunkPlan
Plan a transform against a caller-selected chunk grid.
Parameters:
-
transform(IndexTransform) –Mapping from the request domain to storage coordinates.
-
dimension_grids(Sequence[DimensionGridLike]) –One storage grid per transform output dimension.
Returns:
-
ChunkPlan–A reusable plan whose projections are computed lazily; its
partition()is the factored form they are derived from.
Examples:
Row 1 of a (3, 4) array with (2, 2) chunks touches the two chunks in
the top grid row, each contributing a (2, 2) chunk domain:
>>> from zarr_indexing import IndexTransform
>>> from zarr_indexing.grid import dimension_grids_from_chunks
>>> grids = dimension_grids_from_chunks((2, 2), shape=(3, 4))
>>> plan = plan_chunks(IndexTransform.from_shape((3, 4))[1, :], grids)
>>> [p.chunk_coords for p in plan]
[(0, 0), (0, 1)]
>>> [p.chunk_domain.shape for p in plan]
[(2, 2), (2, 2)]