Skip to content

Commit 2566f97

Browse files
committed
Streamline inheritance from Operand
1 parent 0f36c81 commit 2566f97

File tree

4 files changed

+122
-206
lines changed

4 files changed

+122
-206
lines changed

src/blosc2/lazyexpr.py

Lines changed: 30 additions & 203 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,10 @@
4141
from blosc2 import compute_chunks_blocks
4242
from blosc2.info import InfoReporter
4343
from blosc2.ndarray import (
44-
_check_allowed_dtypes,
4544
get_chunks_idx,
4645
get_intersecting_chunks,
4746
is_inside_new_expr,
48-
local_ufunc_map,
4947
process_key,
50-
ufunc_map,
51-
ufunc_map_1param,
5248
)
5349

5450
if not blosc2.IS_WASM:
@@ -256,7 +252,7 @@ class LazyArrayEnum(Enum):
256252
UDF = 1
257253

258254

259-
class LazyArray(ABC):
255+
class LazyArray(ABC, blosc2.Operand):
260256
@abstractmethod
261257
def indices(self, order: str | list[str] | None = None) -> blosc2.LazyArray:
262258
"""
@@ -436,68 +432,6 @@ def save(self, **kwargs: Any) -> None:
436432
"""
437433
pass
438434

439-
@property
440-
@abstractmethod
441-
def dtype(self) -> np.dtype:
442-
"""
443-
Get the data type of the :ref:`LazyArray`.
444-
445-
Returns
446-
-------
447-
out: np.dtype
448-
The data type of the :ref:`LazyArray`.
449-
"""
450-
pass
451-
452-
@property
453-
@abstractmethod
454-
def shape(self) -> tuple[int]:
455-
"""
456-
Get the shape of the :ref:`LazyArray`.
457-
458-
Returns
459-
-------
460-
out: tuple
461-
The shape of the :ref:`LazyArray`.
462-
"""
463-
pass
464-
465-
@property
466-
@abstractmethod
467-
def ndim(self) -> int:
468-
"""
469-
Get the number of dimensions of the :ref:`LazyArray`.
470-
471-
Returns
472-
-------
473-
out: int
474-
The number of dimensions of the :ref:`LazyArray`.
475-
"""
476-
pass
477-
478-
@property
479-
@abstractmethod
480-
def info(self) -> InfoReporter:
481-
"""
482-
Get information about the :ref:`LazyArray`.
483-
484-
Returns
485-
-------
486-
out: InfoReporter
487-
A printable class with information about the :ref:`LazyArray`.
488-
"""
489-
pass
490-
491-
# Provide minimal __array_interface__ to allow NumPy to work with this object
492-
@property
493-
def __array_interface__(self):
494-
return {
495-
"shape": self.shape,
496-
"typestr": self.dtype.str,
497-
"data": self[()],
498-
"version": 3,
499-
}
500-
501435
# Provide a way to serialize the LazyArray
502436
def to_cframe(self) -> bytes:
503437
"""
@@ -510,11 +444,6 @@ def to_cframe(self) -> bytes:
510444
"""
511445
return self.compute().to_cframe()
512446

513-
def __bool__(self) -> bool:
514-
if math.prod(self.shape) != 1:
515-
raise ValueError(f"The truth value of a LazyArray of shape {self.shape} is ambiguous.")
516-
return self[()].__bool__()
517-
518447

519448
def convert_inputs(inputs):
520449
if not inputs or len(inputs) == 0:
@@ -2263,15 +2192,7 @@ def __init__(self, new_op): # noqa: C901
22632192
return
22642193
value1, op, value2 = new_op
22652194
dtype_ = infer_dtype(op, value1, value2) # perform some checks
2266-
if value2 is None:
2267-
if isinstance(value1, LazyExpr):
2268-
self.expression = f"{op}({value1.expression})"
2269-
self.operands = value1.operands
2270-
else:
2271-
self.operands = {"o0": value1}
2272-
self.expression = "o0" if op is None else f"{op}(o0)"
2273-
return
2274-
elif op in (
2195+
if op in (
22752196
"arctan2",
22762197
"contains",
22772198
"pow",
@@ -2283,7 +2204,7 @@ def __init__(self, new_op): # noqa: C901
22832204
"minimum",
22842205
):
22852206
if np.isscalar(value1) and np.isscalar(value2):
2286-
self.expression = f"{op}(o0, o1)"
2207+
self.expression = f"{op}({value1}, {value2})"
22872208
elif np.isscalar(value2):
22882209
self.operands = {"o0": value1}
22892210
self.expression = f"{op}(o0, {value2})"
@@ -2294,6 +2215,22 @@ def __init__(self, new_op): # noqa: C901
22942215
self.operands = {"o0": value1, "o1": value2}
22952216
self.expression = f"{op}(o0, o1)"
22962217
return
2218+
elif value2 is None:
2219+
if isinstance(value1, LazyExpr):
2220+
self.expression = f"{op}({value1.expression})"
2221+
self.operands = value1.operands
2222+
else:
2223+
self.operands = {"o0": value1}
2224+
self.expression = "o0" if op is None else f"{op}(o0)"
2225+
return
2226+
elif isinstance(value1, LazyExpr) or isinstance(value2, LazyExpr):
2227+
if isinstance(value1, LazyExpr):
2228+
newexpr = value1.update_expr(new_op)
2229+
else:
2230+
newexpr = value2.update_expr(new_op)
2231+
self.expression = newexpr.expression
2232+
self.operands = newexpr.operands
2233+
return
22972234

22982235
self._dtype = dtype_
22992236
if np.isscalar(value1) and np.isscalar(value2):
@@ -2314,16 +2251,6 @@ def __init__(self, new_op): # noqa: C901
23142251
if value1 is value2:
23152252
self.operands = {"o0": value1}
23162253
self.expression = f"(o0 {op} o0)"
2317-
elif isinstance(value1, LazyExpr) or isinstance(value2, LazyExpr):
2318-
if isinstance(value1, LazyExpr):
2319-
self.expression = value1.expression
2320-
self.operands = {"o0": value2}
2321-
else:
2322-
self.expression = value2.expression
2323-
self.operands = {"o0": value1}
2324-
newexpr = self.update_expr(new_op)
2325-
self.expression = newexpr.expression
2326-
self.operands = newexpr.operands
23272254
else:
23282255
# This is the very first time that a LazyExpr is formed from two operands
23292256
# that are not LazyExpr themselves
@@ -2385,28 +2312,29 @@ def update_expr(self, new_op): # noqa: C901
23852312
new_operands, dup_op = fuse_operands(value1.operands, value2.operands)
23862313
# Take expression 2 and rebase the operands while removing duplicates
23872314
new_expr = fuse_expressions(value2.expression, len(value1.operands), dup_op)
2388-
expression = f"({self.expression} {op} {new_expr})"
2315+
expression = f"({value1.expression} {op} {new_expr})"
2316+
self.operands = value1.operands
23892317
elif isinstance(value1, LazyExpr):
23902318
if op == "~":
2391-
expression = f"({op}{self.expression})"
2319+
expression = f"({op}{value1.expression})"
23922320
elif np.isscalar(value2):
2393-
expression = f"({self.expression} {op} {value2})"
2321+
expression = f"({value1.expression} {op} {value2})"
23942322
elif hasattr(value2, "shape") and value2.shape == ():
2395-
expression = f"({self.expression} {op} {value2[()]})"
2323+
expression = f"({value1.expression} {op} {value2[()]})"
23962324
else:
23972325
operand_to_key = {id(v): k for k, v in value1.operands.items()}
23982326
try:
23992327
op_name = operand_to_key[id(value2)]
24002328
except KeyError:
2401-
op_name = f"o{len(self.operands)}"
2329+
op_name = f"o{len(value1.operands)}"
24022330
new_operands = {op_name: value2}
2403-
expression = f"({self.expression} {op} {op_name})"
2331+
expression = f"({value1.expression} {op} {op_name})"
24042332
self.operands = value1.operands
24052333
else:
24062334
if np.isscalar(value1):
2407-
expression = f"({value1} {op} {self.expression})"
2335+
expression = f"({value1} {op} {value2.expression})"
24082336
elif hasattr(value1, "shape") and value1.shape == ():
2409-
expression = f"({value1[()]} {op} {self.expression})"
2337+
expression = f"({value1[()]} {op} {value2.expression})"
24102338
else:
24112339
operand_to_key = {id(v): k for k, v in value2.operands.items()}
24122340
try:
@@ -2415,9 +2343,9 @@ def update_expr(self, new_op): # noqa: C901
24152343
op_name = f"o{len(value2.operands)}"
24162344
new_operands = {op_name: value1}
24172345
if op == "[]": # syntactic sugar for slicing
2418-
expression = f"({op_name}[{self.expression}])"
2346+
expression = f"({op_name}[{value2.expression}])"
24192347
else:
2420-
expression = f"({op_name} {op} {self.expression})"
2348+
expression = f"({op_name} {op} {value2.expression})"
24212349
self.operands = value2.operands
24222350
# Return a new expression
24232351
operands = self.operands | new_operands
@@ -2521,107 +2449,6 @@ def blocks(self):
25212449
self._chunks, self._blocks = compute_chunks_blocks(self.shape, None, None, dtype=self.dtype)
25222450
return self._blocks
25232451

2524-
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
2525-
# Handle operations at the array level
2526-
if method != "__call__":
2527-
return NotImplemented
2528-
2529-
if ufunc in local_ufunc_map:
2530-
return local_ufunc_map[ufunc](*inputs)
2531-
2532-
if ufunc in ufunc_map:
2533-
value = inputs[0] if inputs[1] is self else inputs[1]
2534-
_check_allowed_dtypes(value)
2535-
return blosc2.LazyExpr(new_op=(value, ufunc_map[ufunc], self))
2536-
2537-
if ufunc in ufunc_map_1param:
2538-
value = inputs[0]
2539-
_check_allowed_dtypes(value)
2540-
return blosc2.LazyExpr(new_op=(value, ufunc_map_1param[ufunc], None))
2541-
2542-
return NotImplemented
2543-
2544-
def __neg__(self):
2545-
return self.update_expr(new_op=(0, "-", self))
2546-
2547-
def __add__(self, value):
2548-
return self.update_expr(new_op=(self, "+", value))
2549-
2550-
def __iadd__(self, other):
2551-
return self.update_expr(new_op=(self, "+", other))
2552-
2553-
def __radd__(self, value):
2554-
return self.update_expr(new_op=(value, "+", self))
2555-
2556-
def __sub__(self, value):
2557-
return self.update_expr(new_op=(self, "-", value))
2558-
2559-
def __isub__(self, value):
2560-
return self.update_expr(new_op=(self, "-", value))
2561-
2562-
def __rsub__(self, value):
2563-
return self.update_expr(new_op=(value, "-", self))
2564-
2565-
def __mul__(self, value):
2566-
return self.update_expr(new_op=(self, "*", value))
2567-
2568-
def __imul__(self, value):
2569-
return self.update_expr(new_op=(self, "*", value))
2570-
2571-
def __rmul__(self, value):
2572-
return self.update_expr(new_op=(value, "*", self))
2573-
2574-
def __truediv__(self, value):
2575-
return self.update_expr(new_op=(self, "/", value))
2576-
2577-
def __itruediv__(self, value):
2578-
return self.update_expr(new_op=(self, "/", value))
2579-
2580-
def __rtruediv__(self, value):
2581-
return self.update_expr(new_op=(value, "/", self))
2582-
2583-
def __and__(self, value):
2584-
return self.update_expr(new_op=(self, "&", value))
2585-
2586-
def __rand__(self, value):
2587-
return self.update_expr(new_op=(value, "&", self))
2588-
2589-
def __or__(self, value):
2590-
return self.update_expr(new_op=(self, "|", value))
2591-
2592-
def __ror__(self, value):
2593-
return self.update_expr(new_op=(value, "|", self))
2594-
2595-
def __invert__(self):
2596-
return self.update_expr(new_op=(self, "~", None))
2597-
2598-
def __pow__(self, value):
2599-
return self.update_expr(new_op=(self, "**", value))
2600-
2601-
def __rpow__(self, value):
2602-
return self.update_expr(new_op=(value, "**", self))
2603-
2604-
def __ipow__(self, value):
2605-
return self.update_expr(new_op=(self, "**", value))
2606-
2607-
def __lt__(self, value):
2608-
return self.update_expr(new_op=(self, "<", value))
2609-
2610-
def __le__(self, value):
2611-
return self.update_expr(new_op=(self, "<=", value))
2612-
2613-
def __eq__(self, value):
2614-
return self.update_expr(new_op=(self, "==", value))
2615-
2616-
def __ne__(self, value):
2617-
return self.update_expr(new_op=(self, "!=", value))
2618-
2619-
def __gt__(self, value):
2620-
return self.update_expr(new_op=(self, ">", value))
2621-
2622-
def __ge__(self, value):
2623-
return self.update_expr(new_op=(self, ">=", value))
2624-
26252452
def where(self, value1=None, value2=None):
26262453
"""
26272454
Select value1 or value2 values based on the condition of the current expression.

0 commit comments

Comments
 (0)