|
21 | 21 | import re |
22 | 22 | import sys |
23 | 23 | import threading |
24 | | -from abc import ABC, abstractmethod |
| 24 | +from abc import ABC, abstractmethod, abstractproperty |
25 | 25 | from dataclasses import asdict |
26 | 26 | from enum import Enum |
27 | 27 | from pathlib import Path |
@@ -437,6 +437,41 @@ def to_cframe(self) -> bytes: |
437 | 437 | """ |
438 | 438 | return self.compute().to_cframe() |
439 | 439 |
|
| 440 | + @abstractproperty |
| 441 | + def chunks(self) -> tuple[int]: |
| 442 | + """ |
| 443 | + Return :ref:`LazyArray` chunks. |
| 444 | + """ |
| 445 | + pass |
| 446 | + |
| 447 | + @abstractproperty |
| 448 | + def blocks(self) -> tuple[int]: |
| 449 | + """ |
| 450 | + Return :ref:`LazyArray` blocks. |
| 451 | + """ |
| 452 | + pass |
| 453 | + |
| 454 | + def get_chunk(self, nchunk): |
| 455 | + """Get the `nchunk` of the expression, evaluating only that one.""" |
| 456 | + # Create an empty array with the chunkshape and dtype; this is fast |
| 457 | + shape = self.shape |
| 458 | + chunks = self.chunks |
| 459 | + # Calculate the shape of the (chunk) slice_ (especially at the end of the array) |
| 460 | + chunks_idx, _ = get_chunks_idx(shape, chunks) |
| 461 | + coords = tuple(np.unravel_index(nchunk, chunks_idx)) |
| 462 | + slice_ = tuple( |
| 463 | + slice(c * s, min((c + 1) * s, shape[i])) |
| 464 | + for i, (c, s) in enumerate(zip(coords, chunks, strict=True)) |
| 465 | + ) |
| 466 | + loc_chunks = tuple(s.stop - s.start for s in slice_) |
| 467 | + out = blosc2.empty(shape=self.chunks, dtype=self.dtype, chunks=self.chunks, blocks=self.blocks) |
| 468 | + if loc_chunks == self.chunks: |
| 469 | + self.compute(item=slice_, out=out) |
| 470 | + else: |
| 471 | + _slice_ = tuple(slice(0, s) for s in loc_chunks) |
| 472 | + out[_slice_] = self.compute(item=slice_) |
| 473 | + return out.schunk.get_chunk(0) |
| 474 | + |
440 | 475 |
|
441 | 476 | def convert_inputs(inputs): |
442 | 477 | if not inputs or len(inputs) == 0: |
@@ -2421,27 +2456,6 @@ def __init__(self, new_op): # noqa: C901 |
2421 | 2456 | self.operands = {"o0": value1, "o1": value2} |
2422 | 2457 | self.expression = f"(o0 {op} o1)" |
2423 | 2458 |
|
2424 | | - def get_chunk(self, nchunk): |
2425 | | - """Get the `nchunk` of the expression, evaluating only that one.""" |
2426 | | - # Create an empty array with the chunkshape and dtype; this is fast |
2427 | | - shape = self.shape |
2428 | | - chunks = self.chunks |
2429 | | - # Calculate the shape of the (chunk) slice_ (especially at the end of the array) |
2430 | | - chunks_idx, _ = get_chunks_idx(shape, chunks) |
2431 | | - coords = tuple(np.unravel_index(nchunk, chunks_idx)) |
2432 | | - slice_ = tuple( |
2433 | | - slice(c * s, min((c + 1) * s, shape[i])) |
2434 | | - for i, (c, s) in enumerate(zip(coords, chunks, strict=True)) |
2435 | | - ) |
2436 | | - loc_chunks = tuple(s.stop - s.start for s in slice_) |
2437 | | - out = blosc2.empty(shape=self.chunks, dtype=self.dtype, chunks=self.chunks, blocks=self.blocks) |
2438 | | - if loc_chunks == self.chunks: |
2439 | | - self.compute(item=slice_, out=out) |
2440 | | - else: |
2441 | | - _slice_ = tuple(slice(0, s) for s in loc_chunks) |
2442 | | - out[_slice_] = self.compute(item=slice_) |
2443 | | - return out.schunk.get_chunk(0) |
2444 | | - |
2445 | 2459 | def update_expr(self, new_op): # noqa: C901 |
2446 | 2460 | prev_flag = blosc2._disable_overloaded_equal |
2447 | 2461 | # We use a lot of the original NDArray.__eq__ as 'is', so deactivate the overloaded one |
@@ -3218,6 +3232,38 @@ def info_items(self): |
3218 | 3232 | ("dtype", self.dtype), |
3219 | 3233 | ] |
3220 | 3234 |
|
| 3235 | + @property |
| 3236 | + def chunks(self): |
| 3237 | + if hasattr(self, "_chunks"): |
| 3238 | + return self._chunks |
| 3239 | + shape, self._chunks, self._blocks, fast_path = validate_inputs( |
| 3240 | + self.inputs_dict, getattr(self, "_out", None) |
| 3241 | + ) |
| 3242 | + if not hasattr(self, "_shape"): |
| 3243 | + self._shape = shape |
| 3244 | + if self._shape != shape: # validate inputs only works for elementwise funcs so returned shape might |
| 3245 | + fast_path = False # be incompatible with true output shape |
| 3246 | + if not fast_path: |
| 3247 | + # Not using the fast path, so we need to compute the chunks/blocks automatically |
| 3248 | + self._chunks, self._blocks = compute_chunks_blocks(self.shape, None, None, dtype=self.dtype) |
| 3249 | + return self._chunks |
| 3250 | + |
| 3251 | + @property |
| 3252 | + def blocks(self): |
| 3253 | + if hasattr(self, "_blocks"): |
| 3254 | + return self._blocks |
| 3255 | + shape, self._chunks, self._blocks, fast_path = validate_inputs( |
| 3256 | + self.inputs_dict, getattr(self, "_out", None) |
| 3257 | + ) |
| 3258 | + if not hasattr(self, "_shape"): |
| 3259 | + self._shape = shape |
| 3260 | + if self._shape != shape: # validate inputs only works for elementwise funcs so returned shape might |
| 3261 | + fast_path = False # be incompatible with true output shape |
| 3262 | + if not fast_path: |
| 3263 | + # Not using the fast path, so we need to compute the chunks/blocks automatically |
| 3264 | + self._chunks, self._blocks = compute_chunks_blocks(self.shape, None, None, dtype=self.dtype) |
| 3265 | + return self._blocks |
| 3266 | + |
3221 | 3267 | # TODO: indices and sort are repeated in LazyExpr; refactor |
3222 | 3268 | def indices(self, order: str | list[str] | None = None) -> blosc2.LazyArray: |
3223 | 3269 | if self.dtype.fields is None: |
|
0 commit comments