|
| 1 | +"""Integration tests for RectilinearChunkGrid with array creation.""" |
| 2 | + |
| 3 | +from typing import Literal |
| 4 | + |
| 5 | +import numpy as np |
| 6 | +import pytest |
| 7 | + |
| 8 | +import zarr |
| 9 | +from zarr.core.chunk_grids import RectilinearChunkGrid |
| 10 | +from zarr.storage import MemoryStore |
| 11 | + |
| 12 | + |
| 13 | +@pytest.mark.parametrize("zarr_format", [3]) |
| 14 | +async def test_create_array_with_nested_chunks(zarr_format: Literal[2, 3]) -> None: |
| 15 | + """ |
| 16 | + Test creating an array with nested chunk specification (RectilinearChunkGrid). |
| 17 | + This is an end-to-end test for the feature. |
| 18 | + """ |
| 19 | + store = MemoryStore() |
| 20 | + arr = await zarr.api.asynchronous.create_array( |
| 21 | + store=store, |
| 22 | + shape=(60, 100), |
| 23 | + chunks=[[10, 20, 30], [25, 25, 25, 25]], |
| 24 | + dtype="i4", |
| 25 | + zarr_format=zarr_format, |
| 26 | + ) |
| 27 | + |
| 28 | + # Verify metadata has RectilinearChunkGrid |
| 29 | + assert isinstance(arr.metadata.chunk_grid, RectilinearChunkGrid) |
| 30 | + assert arr.metadata.chunk_grid.chunk_shapes == ((10, 20, 30), (25, 25, 25, 25)) |
| 31 | + |
| 32 | + # Verify array is functional - can write and read data |
| 33 | + data = np.arange(60 * 100, dtype="i4").reshape(60, 100) |
| 34 | + await arr.setitem(slice(None), data) |
| 35 | + result = await arr.getitem(slice(None)) |
| 36 | + np.testing.assert_array_equal(result, data) |
| 37 | + |
| 38 | + |
| 39 | +async def test_create_array_nested_chunks_read_write() -> None: |
| 40 | + """ |
| 41 | + Test that arrays with RectilinearChunkGrid support standard read/write operations. |
| 42 | + """ |
| 43 | + store = MemoryStore() |
| 44 | + arr = await zarr.api.asynchronous.create_array( |
| 45 | + store=store, |
| 46 | + shape=(30, 40), |
| 47 | + chunks=[[10, 10, 10], [10, 10, 10, 10]], |
| 48 | + dtype="f4", |
| 49 | + zarr_format=3, |
| 50 | + ) |
| 51 | + |
| 52 | + # Write data to different chunks |
| 53 | + arr_data = np.random.random((30, 40)).astype("f4") |
| 54 | + await arr.setitem(slice(None), arr_data) |
| 55 | + |
| 56 | + # Read full array |
| 57 | + result = await arr.getitem(slice(None)) |
| 58 | + np.testing.assert_array_almost_equal(np.asarray(result), arr_data) |
| 59 | + |
| 60 | + # Read partial slices |
| 61 | + partial = await arr.getitem((slice(5, 25), slice(10, 30))) |
| 62 | + np.testing.assert_array_almost_equal(np.asarray(partial), arr_data[5:25, 10:30]) |
| 63 | + |
| 64 | + |
| 65 | +async def test_rectilinear_chunk_grid_roundtrip() -> None: |
| 66 | + """ |
| 67 | + Test that RectilinearChunkGrid persists correctly through save/load. |
| 68 | + """ |
| 69 | + store = MemoryStore() |
| 70 | + |
| 71 | + # Create array with nested chunks |
| 72 | + arr1 = await zarr.api.asynchronous.create_array( |
| 73 | + store=store, |
| 74 | + name="test_array", |
| 75 | + shape=(60, 80), |
| 76 | + chunks=[[10, 20, 30], [20, 20, 20, 20]], |
| 77 | + dtype="u1", |
| 78 | + zarr_format=3, |
| 79 | + ) |
| 80 | + |
| 81 | + # Write some data |
| 82 | + data = np.arange(60 * 80, dtype="u1").reshape(60, 80) |
| 83 | + await arr1.setitem(slice(None), data) |
| 84 | + |
| 85 | + # Re-open the array |
| 86 | + arr2 = await zarr.api.asynchronous.open_array(store=store, path="test_array") |
| 87 | + |
| 88 | + # Verify chunk_grid is preserved |
| 89 | + assert isinstance(arr2.metadata.chunk_grid, RectilinearChunkGrid) |
| 90 | + assert arr2.metadata.chunk_grid.chunk_shapes == ((10, 20, 30), (20, 20, 20, 20)) |
| 91 | + |
| 92 | + # Verify data is preserved |
| 93 | + result = await arr2.getitem(slice(None)) |
| 94 | + np.testing.assert_array_equal(result, data) |
| 95 | + |
| 96 | + |
| 97 | +async def test_from_array_rejects_nested_chunks() -> None: |
| 98 | + """ |
| 99 | + Test that from_array rejects nested chunks (RectilinearChunkGrid) with has_data=True. |
| 100 | + """ |
| 101 | + store = MemoryStore() |
| 102 | + data = np.arange(30 * 40, dtype="i4").reshape(30, 40) |
| 103 | + |
| 104 | + # Should raise error because RectilinearChunkGrid is not compatible with has_data=True |
| 105 | + with pytest.raises( |
| 106 | + ValueError, |
| 107 | + match="Cannot use RectilinearChunkGrid.*when creating array from data", |
| 108 | + ): |
| 109 | + await zarr.api.asynchronous.from_array( |
| 110 | + store=store, |
| 111 | + data=data, |
| 112 | + chunks=[[10, 10, 10], [10, 10, 10, 10]], # type: ignore[arg-type] |
| 113 | + zarr_format=3, |
| 114 | + ) |
| 115 | + |
| 116 | + |
| 117 | +async def test_nested_chunks_with_different_sizes() -> None: |
| 118 | + """ |
| 119 | + Test RectilinearChunkGrid with highly irregular chunk sizes. |
| 120 | + """ |
| 121 | + store = MemoryStore() |
| 122 | + arr = await zarr.api.asynchronous.create_array( |
| 123 | + store=store, |
| 124 | + shape=(100, 100), |
| 125 | + chunks=[[5, 10, 15, 20, 50], [100]], # Very irregular first dim, uniform second |
| 126 | + dtype="i2", |
| 127 | + zarr_format=3, |
| 128 | + ) |
| 129 | + |
| 130 | + assert isinstance(arr.metadata.chunk_grid, RectilinearChunkGrid) |
| 131 | + assert arr.metadata.chunk_grid.chunk_shapes == ((5, 10, 15, 20, 50), (100,)) |
| 132 | + |
| 133 | + # Verify writes work correctly |
| 134 | + data = np.arange(100 * 100, dtype="i2").reshape(100, 100) |
| 135 | + await arr.setitem(slice(None), data) |
| 136 | + result = await arr.getitem(slice(None)) |
| 137 | + np.testing.assert_array_equal(result, data) |
| 138 | + |
| 139 | + |
| 140 | +async def test_rectilinear_chunk_grid_nchunks_not_supported() -> None: |
| 141 | + """ |
| 142 | + Test that nchunks property raises NotImplementedError for RectilinearChunkGrid. |
| 143 | +
|
| 144 | + Note: The chunks property (and thus nchunks) is only defined for RegularChunkGrid. |
| 145 | + For RectilinearChunkGrid, use chunk_grid.get_nchunks() instead. |
| 146 | + """ |
| 147 | + store = MemoryStore() |
| 148 | + arr = await zarr.api.asynchronous.create_array( |
| 149 | + store=store, |
| 150 | + shape=(60, 100), |
| 151 | + chunks=[[10, 20, 30], [25, 25, 25, 25]], |
| 152 | + dtype="u1", |
| 153 | + zarr_format=3, |
| 154 | + ) |
| 155 | + |
| 156 | + # The chunks property is not defined for RectilinearChunkGrid |
| 157 | + with pytest.raises( |
| 158 | + NotImplementedError, match="only defined for arrays using.*RegularChunkGrid" |
| 159 | + ): |
| 160 | + _ = arr.nchunks |
| 161 | + |
| 162 | + # But we can get nchunks from the chunk_grid directly |
| 163 | + assert arr.metadata.chunk_grid.get_nchunks((60, 100)) == 12 |
0 commit comments