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— the correlated (vindex) index arrays, which read the same input axes and so do not distribute: their points are sorted into chunks together, once.
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 DimensionMaps (a
diagonal, which no selection produces) have no factored form and are
rejected with ValueError; an 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 a joint table.
Built once per plan and memoized. Raises ValueError if two
DimensionMaps read one input axis (a diagonal, which no selection
produces), the one shape with no factored form.
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 holds
the correlated index arrays, if any. A projection is one row of each
table, so the partition has n_rows == prod(row_shape) rows, walked
in row-major order over row_shape (the joint table last). 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
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 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 | |
dimension_grids
instance-attribute
¶
dimension_grids: tuple[DimensionGridLike, ...]
One grid per storage dimension.
joint
instance-attribute
¶
joint: JointSet | None
The correlated index arrays' table, or None when there are none.
n_rows
property
¶
n_rows: int
The number of projections, as an exact integer (len raises above the platform limit).
row_shape
instance-attribute
¶
Rows per table, joint last; the partition is walked in row-major order over it.
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: JointSet | None,
row_shape: tuple[int, ...],
) -> None
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
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 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 | |
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
¶
Chunk-local storage coordinate of every entry, 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
¶
The correlated index arrays of a transform, grouped by the chunk each point lands in.
Correlated (vindex) arrays read the same input axes, so a chunk
constrains all of them at once; they 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 the request's broadcast 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
>>> 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
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 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 | |
block_coordinates
instance-attribute
¶
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
¶
Chunk-local storage coordinates of every point, 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]],
block_coordinates: 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 order.
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)]