zarr_indexing.lazy_array
LazyArray.lazy[...] is metadata-only: every derived view keeps the same
reader and composes its transform without reading data. result() allocates
owned system memory, then calls that reader once for each projected part.
Rectangular parts write directly into their final slices; advanced placement
may first use an owned dense temporary. LazyArray(source) assumes only basic
indexing, while LazyArray.from_numpy(array) selects numpy_reader. Both
currently use the same slab-and-gather implementation.
The built-in readers lower through NumPy system memory and support sources whose basic reads can be converted there. A device source that refuses NumPy conversion needs a custom reader that transfers into the output buffer. Derived views and parts share their reader and part views may be materialized concurrently, so stateful readers must synchronize their own mutable state.
Every public Partition.view.transform directly maps that view's zero-origin
coordinates into its raw Partition.view.array, including for non-first
partitions. Partition.projection.chunk_transform intentionally stays local to
the selected chunk. During parent materialization (view.result(parts=parts)) the reader receives
both frames in one ReadContext: the public global transform in context.transform and the
same local plan in context.projection. Direct part.view.result() calls
provide no projection; readers requiring it must use parent assembly.
zarr_indexing.lazy_array ¶
LazyArray — TensorStore-style lazy indexing over array-like sources.
LazyArray wraps a source with shape, dtype, and basic integer/slice
__getitem__, whose reads can be lowered through NumPy system memory. It adds
a .lazy accessor whose indexing operations build up an
IndexTransform instead of reading data:
view = LazyArray(source).lazy[10:50, ::2].lazy.oindex[[3, 1, 1], :]
view.shape # known without touching the data
values = view.result()
Selection construction does not read source values: result(), __array__,
and eager __getitem__ perform reads. Tokenization hashes plain NumPy source
data or delegates to an explicit source hook. .lazy operations inspect
selection metadata and may copy or process supplied index arrays. Composition
does not accumulate wrapper layers: a view of a view is still a single transform
and retains its reader.
Parts
A LazyArray carries a partitioning of the array it wraps: a grid of boxes
that a read is broken into. parts() walks those boxes as they fall through the
view, yielding a Partition per box. Its
paired projection describes the chunk-local read and where its cells land in the
request; view carries that partition's transform. result() allocates one
fresh output buffer, then reads each partition once through the selected reader
into the final buffer or an owned temporary for fancy placement.
The part view's transform directly addresses its raw wrapped array. The paired
projection deliberately retains the chunk-local frame. Parent materialization
passes both in ReadContext; calling part.view.result() directly uses an
unpartitioned context with projection=None. Readers that require the projection
should be used through the parent result(parts=parts) path.
The partitioning is discovered from the wrapped array at construction — first
read_chunk_sizes (zarr's clipped per-axis sizes, sharding-aware), then
chunks, read as per-axis sizes if its entries are sequences and as a uniform
box shape if they are integers. Those attribute names belong to the wrapped
array; this API refers only to parts. An array that advertises neither gets a
single whole-array part, and resolving it reads the whole view through its
selected reader in one pass.
with_parts replaces the partitioning without touching the data or the view:
view.with_parts((64, 64)) # uniform boxes, tail clipped
view.with_parts_per_axis(((3, 3, 1),)) # explicit per-axis sizes
view.unpartitioned() # one whole-array part; resolve in one shot
Repartitioning changes how the read is divided, not what result() returns.
Parts that do not align with the source's own boxes are permitted and can be
useful for controlling per-read sizes or batching small reads. They can change
I/O costs; the intended selected values remain the same for an unchanged source
and a conforming reader. The full result buffer is still allocated.
Readers
Every wrapper carries a reader that owns the backend-specific request. The
transform answers which values? and is independent of the backend; the
reader answers how does this backend obtain them? and must preserve the
complete transform exactly. Readers do not define indexing semantics,
partitioning, scheduling, or result ownership. The conservative
LazyArray(source) uses basic_reader, which needs only basic slicing.
LazyArray.from_numpy(array) explicitly opts into numpy_reader for direct
NumPy indexing. with_reader() replaces the reader without reading or changing
the view metadata. The reader object is shared by all derived views and their
parts. Consumers may materialize part views concurrently; LazyArray does not
serialize calls, so a stateful reader must synchronize its own mutable state.
The built-in readers lower through NumPy system memory using NumPy conversion of source slices. Device arrays that refuse implicit conversion need a custom reader that explicitly transfers values into the output buffer.
Boxes and queries
is_box reports whether the current transform contains only constant and affine
output maps. An index-array gather commonly produces a query, but singleton
indices and later selections can remove its index-array maps and make it a box.
bounding_box() gives a coordinate hull, and strides() gives stride magnitudes
for a box. These describe storage coordinates, not the full result layout or
traversal order. A query can fill its hull, and a singleton box can fill its hull
even when its recorded stride exceeds one.
The positional dialect
Selections on LazyArray are positional, NumPy-style: index 0 is the first
element of the current view, -1 is the last, boolean masks must match the
view's shape, integer coordinates are bounds-checked, and slices are clipped
to the view's extent.
This differs from the low-level IndexTransform literal-coordinate dialect:
a transform can retain a nonzero domain origin, while LazyArray re-zeroes
positions on each derived view. The current main Zarr Array does not expose
this wrapper as an Array.lazy attribute; use LazyArray(array) explicitly.
Scalar integers drop axes. Non-boolean objects implementing SupportsIndex
are accepted as scalar indices and in slice bounds; __int__ alone is not enough.
For orthogonal and vectorized modes this wrapper applies scalar indices first,
then the remaining advanced selection. Orthogonal indices form an outer product.
Vectorized indexing accepts coordinate arrays or a shape-matching boolean mask.
An ellipsis can retain unindexed axes ([..., i, j]), but explicit slice entries
in vectorized selections are currently rejected. Scalar-first processing can
also differ from NumPy's advanced-axis placement: for shape (2, 3, 4),
lazy.vindex[0, ..., [1, 2]] has shape (3, 2), whereas NumPy's same selection
has shape (2, 3). These modes do not implement every NumPy indexing form.
Materializing on fallback
LazyArray implements __array__ but deliberately implements neither
__array_ufunc__ nor __array_function__. Many NumPy operations therefore materialize through __array__ and work on
the resulting array: numpy.sum(view),
numpy.add(view, 1) and numpy.stack([view, view]) all do, and so does
numpy.ones(view.shape) + view, where the ndarray on the left dispatches.
Metadata queries such as numpy.shape(view) and numpy.ndim(view) can use the
exposed attributes without materializing; other unsupported operations may fail.
Python's arithmetic operators do not: view + 1 raises TypeError, because
the wrapper defines no arithmetic dunders and an int has nothing to dispatch
to. Both facts follow from the same intent — laziness here applies to indexing,
not to building a deferred compute graph — and a LazyArray is not a drop-in
for arithmetic on a large array either way. Use .lazy[...] to narrow the view
first, or pass the wrapper to dask.array.from_array so that dask owns the
compute graph.
Ownership
result() always allocates fresh system memory before reading through the
selected reader. A numpy.ma source keeps its mask by receiving a masked
output buffer; other source-specific array types do not survive materializing.
ArrayLike ¶
Bases: Protocol
The surface LazyArray needs from the array it wraps.
Source code in src/zarr_indexing/lazy_array.py
LazyArray ¶
A lazily-indexable view over a system-memory/basic-indexing source.
Wrapping neither copies nor reads the wrapped array at construction time.
Indexing through .lazy composes an IndexTransform and returns another
LazyArray; result() materializes.
Selections use the positional NumPy dialect and reads are broken up along a partitioning discovered from the wrapped array. Every derived view retains its reader; that reader receives the complete projected transform once per part. See the module docstring, which also covers how the dialect differs from low-level literal transforms and which NumPy operations materialize the view.
This wrapper describes reads. It defines no __setitem__, so
assigning into a view raises TypeError. Writing belongs to the
consumer: plan the selection with
plan_chunks and own the
read-modify-write, since chunk atomicity and concurrent-writer policy are
the backend's to decide, not an indexing plan's.
Parameters:
-
array(_WrappedArray) –The array to wrap. It must expose
shape,dtype, and__getitem__with basic (integer/slice) indexing;__setitem__is not required, so a read-only source wraps as well as a writable one. Its partitioning, if it advertises one, is discovered here; usewith_partsto choose a different one. This conservative constructor selectsbasic_reader; usefrom_numpyfor a NumPy array orwith_readerto select another backend adapter.
Examples:
>>> import numpy as np
>>> source = np.arange(12).reshape(3, 4)
>>> view = LazyArray.from_numpy(source).with_parts((2, 2)).lazy[1:, ::2]
>>> view.shape
(2, 2)
>>> view.result()
array([[ 4, 6],
[ 8, 10]])
Source code in src/zarr_indexing/lazy_array.py
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 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 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 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 | |
__slots__
class-attribute
instance-attribute
¶
base_shape
property
¶
The shape the partitioning is expressed in — not this view's shape.
with_parts and with_parts_per_axis describe boxes of the array being
read, not of the view reading it, so a narrowed view still partitions
the extents named here. For a part's own array, this is the part's
box, which is why the same call means different sizes there. Without
somewhere to read it, the frame in force could only be inferred from an
error message.
is_box
property
¶
is_box: bool
Whether this view selects a rectangular region rather than a point list.
True exactly when the composed transform has no ArrayMap output maps.
Basic indexing of a box stays a box. Advanced indexing can introduce
index arrays, but singleton gathers and later selections may remove them.
bounding_box() and strides() describe the touched coordinate region
and stride magnitudes; use the transform for order, result axes, and
repetitions. Neither this flag nor the hull alone proves dense coverage.
Examples:
lazy
property
¶
Lazy indexing: lazy[...], lazy.oindex[...], lazy.vindex[...].
Each returns a new LazyArray view; no data is read.
shape
property
¶
The shape of this view — the transform's input domain, not the source's.
transform
property
¶
transform: IndexTransform
The composed transform from this view's coordinates to storage.
__array__ ¶
Materialize the view as a NumPy array.
The result never shares memory with the wrapped array, whatever copy
asks for: result() already allocates, so copy=True gets an array the
caller owns. copy=None need not copy again when the dtype already
matches; requesting another dtype can allocate a conversion buffer. copy=False is refused, because materializing means reading
— this API always materializes into its own result buffer.
Source code in src/zarr_indexing/lazy_array.py
__dask_tokenize__ ¶
__dask_tokenize__() -> Any
Tokenize the source and serialized transform.
The transform contributes a digest of JSON produced by to_json(), so
its JSON is not embedded in the returned token, but is allocated while
computing the digest. Equal serialized transforms and equal source
tokens produce equal tokens; arbitrary semantically equivalent mappings
are not guaranteed to serialize identically.
Plain NumPy arrays without object fields are hashed in full on each
call, with time and temporary memory proportional to their byte size.
Known NumPy/mmap backing is rejected through ndarray base and
memoryview object chains; arbitrary buffer provenance is not inferred.
Array subclasses, object arrays, and foreign sources require a source __dask_tokenize__ hook; unsupported sources
raise TypeError. Hooks own determinism, versioning, and any I/O, and
hook exceptions propagate. Installing Dask does not change this policy.
The reader and partitioning are omitted under the contract that they preserve values. A token describes the source at tokenization time; mutation after graph construction does not invalidate existing Dask keys. Concurrent mutation during hashing is unsupported. This method does not provide a persistent cache identity or a source snapshot.
Source code in src/zarr_indexing/lazy_array.py
__getitem__ ¶
Read a basic selection eagerly, like numpy.ndarray.__getitem__.
Reads here are eager, not lazy, so that a LazyArray works as a duck
array for consumers (dask's from_array, numpy.asarray) that expect
indexing to produce data. Use .lazy[...] for the lazy form.
Source code in src/zarr_indexing/lazy_array.py
__init__ ¶
Wrap array without reading it; parameters are documented on the class.
Reject numpy.matrix, normalize the shape, and inspect partition metadata.
Shape conversion and transform construction can also reject invalid input.
Source code in src/zarr_indexing/lazy_array.py
__iter__ ¶
Iterate eagerly over the first axis, like a NumPy array.
The rank check happens in __iter__ itself rather than in the
generator, so iter(view) on a zero-rank view raises immediately as
NumPy's does, instead of waiting for the first next.
Source code in src/zarr_indexing/lazy_array.py
__len__ ¶
__len__() -> int
The length of the first axis, as for a NumPy array; TypeError on a 0-d view.
bounding_box ¶
The storage region this view touches, one interval per storage dimension.
Defined for any selection, box or not, as the hull: the smallest
[inclusive_min, exclusive_max) interval per dimension of the array
this view reads from that contains every coordinate the selection
reaches.
A strided box can leave gaps, and a query hull can be arbitrarily loose:
oindex[[0, 999]] spans a 1000-wide hull over two selected rows. Conversely,
a query may cover every coordinate in its hull. Inspect the transform
when coverage or traversal order matters.
Returns:
-
tuple of (int, int), or None–One interval per storage dimension, or
Nonewhen the view is empty (size == 0) and so touches no coordinate at all, leaving no interval to report.
Notes
The coordinates directly address the raw array this view exposes.
Consequently, partition views report source-global hulls;
Partition.box separately gives
the whole global partition cell rather than only the selected hull.
Examples:
>>> import numpy as np
>>> array = LazyArray.from_numpy(np.arange(12).reshape(3, 4))
>>> array.lazy[1:, ::2].bounding_box()
((1, 3), (0, 3))
>>> array.lazy.oindex[[2, 0], :].bounding_box()
((0, 3), (0, 4))
>>> array.lazy[1:1].bounding_box() is None
True
Source code in src/zarr_indexing/lazy_array.py
from_numpy
classmethod
¶
Wrap a NumPy array with its explicitly selected NumPy reader.
Source code in src/zarr_indexing/lazy_array.py
parts ¶
Iterate the base partitioning, projected through this view.
Single-use: this is a generator, so it is consumed by the first walk and
a second for over the same object yields nothing. Call parts() again
for a fresh walk, or keep a list of it if you need to revisit.
Yields one Partition per box the
view actually touches. The parts tile the view exactly and disjointly,
and each carries a LazyArray that can be resolved on its own: in
another thread, in another order, or not at all. Those views share this
view's reader, and LazyArray does not serialize calls, so a stateful
reader must synchronize its own mutable state.
A wrapper with no partitioning (see with_parts) yields a single part
covering the whole array.
Yields:
-
Partition–One per touched box, in the resolver's own order.
Examples:
>>> import numpy as np
>>> view = LazyArray.from_numpy(np.arange(12).reshape(3, 4)).with_parts((2, 2))
>>> part = next(view.lazy[:, 1:].parts())
>>> (part.base_coords, part.view.shape, part.is_complete)
((0, 0), (2, 1), False)
Source code in src/zarr_indexing/lazy_array.py
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 | |
result ¶
Materialize this view.
Every result starts as a fresh system-memory buffer. Each touched partition is read through the selected reader directly into its rectangular destination, or into an owned dense temporary before fancy placement. Empty views allocate without reading the source.
Parameters:
-
parts(Sequence[Partition] | None, default:None) –A reusable sequence previously returned by this exact view's
parts()method. Supplying it reuses that partition plan instead of constructing another one. The parts must tile the view exactly.
Returns:
-
ndarray–An array of shape
self.shape, identical whatever partitioning is in force, always in fresh system memory. A view with a zero-rank domain returns a zero-dimensional array, not a scalar.
Raises:
-
ValueError–If supplied parts were prepared by another view, or do not tile this view exactly. The output buffer is uninitialized where nothing was written, so a bad plan is reported rather than returned.
-
AssertionError–If this library's own partition walk fails to cover the view — a bug in zarr-indexing, never a consequence of the caller's input.
Source code in src/zarr_indexing/lazy_array.py
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 | |
strides ¶
The step between selected coordinates, one per storage dimension.
Together with bounding_box(), this describes a box's per-axis coordinates:
bounding_box() gives the interval per dimension, strides() gives the
step per dimension. A stride of 1 means every cell of the hull along
that dimension is selected; k means every k-th. Dimensions fixed by
an integer index report 1 — they span a single coordinate.
Returns:
-
tuple of int, or None–One positive stride per storage dimension, or
Nonewhenis_boxis false: a query's coordinates are a lookup table and have no step. An empty box still reports its strides even thoughbounding_boxreturnsNone, because the step is a property of the selection's shape, not of the (empty) region it touches.
Notes
Magnitudes only. A reversing view (lazy[::-1]) selects the same set of
coordinates as the equivalent forward view, so it reports the same
bounding box and the same strides. The traversal direction is recorded
in the transform, not in this description of the region touched. A
consumer that needs the order reads the transform, or reverses the block
it gets back.
Examples:
>>> import numpy as np
>>> array = LazyArray.from_numpy(np.arange(24).reshape(4, 6))
>>> (array.lazy[1:, ::2].bounding_box(), array.lazy[1:, ::2].strides())
(((1, 4), (0, 5)), (1, 2))
>>> array.lazy[2, ::3].strides()
(1, 3)
>>> array.lazy.oindex[[2, 0], :].strides() is None
True
Source code in src/zarr_indexing/lazy_array.py
unpartitioned ¶
unpartitioned() -> LazyArray
Return the same view, read in one pass.
result() still allocates its owned output buffer first, then calls the
reader once with the whole projected transform. parts() still yields a
single part covering everything.
Returns:
-
LazyArray–The same view with no partitioning.
Source code in src/zarr_indexing/lazy_array.py
with_parts ¶
Return the same view, read in uniform boxes of shape parts.
One integer per dimension of base_shape, with the trailing box in each
dimension clipped to the extent. The transform, the wrapped array, and
therefore result() are all unchanged; only the boxes the read is
broken into differ. Nothing is copied and nothing is read.
For per-axis sizes see
with_parts_per_axis,
and to read in one pass see
unpartitioned.
Parameters:
Returns:
-
LazyArray–The same view with a new partitioning.
Raises:
-
ValueError–If
partshas the wrong length or contains a non-positive extent. Uniform part sizes must remain positive even for a zero-length axis; usewith_parts_per_axisfor the accepted explicit zero-axis spellings.
Examples:
>>> import numpy as np
>>> view = LazyArray.from_numpy(np.arange(12).reshape(3, 4))
>>> [part.base_coords for part in view.with_parts((2, 3)).parts()]
[(0, 0), (0, 1), (1, 0), (1, 1)]
Source code in src/zarr_indexing/lazy_array.py
with_parts_per_axis ¶
Return the same view, read in boxes of explicitly listed sizes.
The dask convention: one sequence of box extents per dimension of
base_shape, each summing to that dimension's extent. Use it when the
boxes are not uniform — a partitioning discovered from a store, or one
whose last box differs by more than clipping.
Parameters:
Returns:
-
LazyArray–The same view with a new partitioning.
Raises:
-
ValueError–If
sizeshas the wrong length, contains a negative extent, uses a zero extent on a nonempty axis, or declares sizes that do not sum tobase_shape. On a zero-length axis,(),(0,), and repeated zeros all describe no chunks.
Examples:
>>> import numpy as np
>>> view = LazyArray.from_numpy(np.arange(12).reshape(3, 4))
>>> [part.box for part in view.with_parts_per_axis(((1, 2), (4,))).parts()]
[((0, 1), (0, 4)), ((1, 3), (0, 4))]
Source code in src/zarr_indexing/lazy_array.py
with_reader ¶
Return the same metadata view resolved through reader.
Source code in src/zarr_indexing/lazy_array.py
Partition
dataclass
¶
One box of a LazyArray's partitioning, as it falls through the view.
Yielded by LazyArray.parts.
The parts of a view tile it exactly and disjointly: assembling every
view.result() at its out_selection reproduces the whole view's
result() for readers supporting contexts without projections. Parts can be
resolved concurrently when the source and reader permit it.
Derived parts retain the same reader object; a shared stateful reader owns
synchronization for concurrent calls.
A consumer that needs the plan before materialization can prepare it once and reuse the same part records for both scheduling and assembly. The frozen records do not snapshot the mutable source or reader:
parts = tuple(view.parts())
schedule(part.base_coords for part in parts)
values = view.result(parts=parts)
Prepared parts are owned by the exact view that created them and must tile it completely. Passing parts from another view, even an equivalent one, is rejected without reading; omitting a part is likewise rejected rather than returning a partly initialized result.
Attributes:
-
projection(ChunkProjection) –The source-independent description of this part. Its paired
chunk_transformandcell_transformshare one compact synthetic domain, mapping each selected cell to chunk-local storage and request coordinates respectively. This is the authoritative placement model;base_coordsandis_completeare conveniences derived from it. -
base_coords(tuple[int, ...]) –Which box of the base partitioning this is, one coordinate per dimension of the wrapped array.
-
box(tuple[tuple[int, int], ...]) –The box itself, in the global storage coordinates of the wrapped array: one
[inclusive_min, exclusive_max)interval per dimension. It describes the whole partition cell, whileview.bounding_box()is the global hull of only the selected values in that cell. For a nested or repartitioned view this box may be narrower thanprojection.chunk_domain. -
view(LazyArray) –A
LazyArraycovering exactly the cells of the view that live in this box. Its transform directly addresses its raw wrappedarray; only the projection'schunk_transformis chunk-local. Resolving the view reads the box once through its selected reader. Namedviewrather thanarraybecauseLazyArray.arrayis the opposite thing — the raw wrapped source — and the two sat next to each other meaning inverses. -
out_selection(tuple[Any, ...]) –Where
view.result()belongs in an array of the whole view's shape — a NumPy index tuple with one entry per dimension of the view, usable directly asout[part.out_selection] = .... -
is_complete(bool) –Whether the view covers the whole box. Useful to a writer deciding whether coverage is complete; it does not by itself establish buffer order, storage-unit alignment, or concurrent-write safety. Fancy projections report
Falsebecause their coverage is deliberatelyunknownuntil duplicate-aware proof is added.
Examples:
Assembling every part's result at its out_selection reproduces the view:
>>> import numpy as np
>>> source = np.arange(12).reshape(3, 4)
>>> view = LazyArray.from_numpy(source).with_parts((2, 2))
>>> out = np.empty(view.shape, dtype=view.dtype)
>>> for part in view.parts():
... out[part.out_selection] = part.view.result()
>>> bool((out == source).all())
True
Source code in src/zarr_indexing/lazy_array.py
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 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 | |