4242from blosc2 .info import InfoReporter
4343from blosc2 .ndarray import (
4444 NUMPY_GE_2_0 ,
45- _check_allowed_dtypes ,
4645 get_chunks_idx ,
4746 get_intersecting_chunks ,
48- local_ufunc_map ,
4947 process_key ,
50- ufunc_map ,
51- ufunc_map_1param ,
5248)
5349
5450from .shape_utils import constructors , elementwise_funcs , infer_shape , linalg_attrs , linalg_funcs , reducers
@@ -116,9 +112,9 @@ def ne_evaluate(expression, local_dict=None, **kwargs):
116112 res = eval (expression , safe_blosc2_globals , local_dict )
117113 if "out" in kwargs :
118114 out = kwargs .pop ("out" )
119- out [:] = res [()] if isinstance ( res , blosc2 . LazyArray ) else res
115+ out [:] = res # will handle calc/decomp if res is lazyarray
120116 return out
121- return res [()] if isinstance (res , blosc2 .LazyArray ) else res
117+ return res [()] if isinstance (res , blosc2 .Operand ) else res
122118
123119
124120# Define empty ndindex tuple for function defaults
@@ -228,7 +224,7 @@ class LazyArrayEnum(Enum):
228224 UDF = 1
229225
230226
231- class LazyArray (ABC ):
227+ class LazyArray (ABC , blosc2 . Operand ):
232228 @abstractmethod
233229 def indices (self , order : str | list [str ] | None = None ) -> blosc2 .LazyArray :
234230 """
@@ -408,68 +404,6 @@ def save(self, **kwargs: Any) -> None:
408404 """
409405 pass
410406
411- @property
412- @abstractmethod
413- def dtype (self ) -> np .dtype :
414- """
415- Get the data type of the :ref:`LazyArray`.
416-
417- Returns
418- -------
419- out: np.dtype
420- The data type of the :ref:`LazyArray`.
421- """
422- pass
423-
424- @property
425- @abstractmethod
426- def shape (self ) -> tuple [int ]:
427- """
428- Get the shape of the :ref:`LazyArray`.
429-
430- Returns
431- -------
432- out: tuple
433- The shape of the :ref:`LazyArray`.
434- """
435- pass
436-
437- @property
438- @abstractmethod
439- def ndim (self ) -> int :
440- """
441- Get the number of dimensions of the :ref:`LazyArray`.
442-
443- Returns
444- -------
445- out: int
446- The number of dimensions of the :ref:`LazyArray`.
447- """
448- pass
449-
450- @property
451- @abstractmethod
452- def info (self ) -> InfoReporter :
453- """
454- Get information about the :ref:`LazyArray`.
455-
456- Returns
457- -------
458- out: InfoReporter
459- A printable class with information about the :ref:`LazyArray`.
460- """
461- pass
462-
463- # Provide minimal __array_interface__ to allow NumPy to work with this object
464- @property
465- def __array_interface__ (self ):
466- return {
467- "shape" : self .shape ,
468- "typestr" : self .dtype .str ,
469- "data" : self [()],
470- "version" : 3 ,
471- }
472-
473407 # Provide a way to serialize the LazyArray
474408 def to_cframe (self ) -> bytes :
475409 """
@@ -482,11 +416,6 @@ def to_cframe(self) -> bytes:
482416 """
483417 return self .compute ().to_cframe ()
484418
485- def __bool__ (self ) -> bool :
486- if math .prod (self .shape ) != 1 :
487- raise ValueError (f"The truth value of a LazyArray of shape { self .shape } is ambiguous." )
488- return self [()].__bool__ ()
489-
490419
491420def convert_inputs (inputs ):
492421 if not inputs or len (inputs ) == 0 :
@@ -2328,7 +2257,7 @@ def __init__(self, new_op): # noqa: C901
23282257 "minimum" ,
23292258 ):
23302259 if np .isscalar (value1 ) and np .isscalar (value2 ):
2331- self .expression = f"{ op } (o0, o1 )"
2260+ self .expression = f"{ op } ({ value1 } , { value2 } )"
23322261 elif np .isscalar (value2 ):
23332262 self .operands = {"o0" : value1 }
23342263 self .expression = f"{ op } (o0, { value2 } )"
@@ -2339,6 +2268,15 @@ def __init__(self, new_op): # noqa: C901
23392268 self .operands = {"o0" : value1 , "o1" : value2 }
23402269 self .expression = f"{ op } (o0, o1)"
23412270 return
2271+ elif isinstance (value1 , LazyExpr ) or isinstance (value2 , LazyExpr ):
2272+ if isinstance (value1 , LazyExpr ):
2273+ newexpr = value1 .update_expr (new_op )
2274+ else :
2275+ newexpr = value2 .update_expr (new_op )
2276+ self .expression = newexpr .expression
2277+ self .operands = newexpr .operands
2278+ self ._dtype = newexpr .dtype
2279+ return
23422280
23432281 self ._dtype = dtype_
23442282 if np .isscalar (value1 ) and np .isscalar (value2 ):
@@ -2359,16 +2297,6 @@ def __init__(self, new_op): # noqa: C901
23592297 if value1 is value2 :
23602298 self .operands = {"o0" : value1 }
23612299 self .expression = f"(o0 { op } o0)"
2362- elif isinstance (value1 , LazyExpr ) or isinstance (value2 , LazyExpr ):
2363- if isinstance (value1 , LazyExpr ):
2364- self .expression = value1 .expression
2365- self .operands = {"o0" : value2 }
2366- else :
2367- self .expression = value2 .expression
2368- self .operands = {"o0" : value1 }
2369- newexpr = self .update_expr (new_op )
2370- self .expression = newexpr .expression
2371- self .operands = newexpr .operands
23722300 else :
23732301 # This is the very first time that a LazyExpr is formed from two operands
23742302 # that are not LazyExpr themselves
@@ -2404,6 +2332,7 @@ def update_expr(self, new_op): # noqa: C901
24042332 # One of the two operands are LazyExpr instances
24052333 try :
24062334 value1 , op , value2 = new_op
2335+ dtype_ = check_dtype (op , value1 , value2 ) # conserve dtype
24072336 # The new expression and operands
24082337 expression = None
24092338 new_operands = {}
@@ -2430,28 +2359,29 @@ def update_expr(self, new_op): # noqa: C901
24302359 new_operands , dup_op = fuse_operands (value1 .operands , value2 .operands )
24312360 # Take expression 2 and rebase the operands while removing duplicates
24322361 new_expr = fuse_expressions (value2 .expression , len (value1 .operands ), dup_op )
2433- expression = f"({ self .expression } { op } { new_expr } )"
2362+ expression = f"({ value1 .expression } { op } { new_expr } )"
2363+ self .operands = value1 .operands
24342364 elif isinstance (value1 , LazyExpr ):
24352365 if op == "~" :
2436- expression = f"({ op } { self .expression } )"
2366+ expression = f"({ op } { value1 .expression } )"
24372367 elif np .isscalar (value2 ):
2438- expression = f"({ self .expression } { op } { value2 } )"
2368+ expression = f"({ value1 .expression } { op } { value2 } )"
24392369 elif hasattr (value2 , "shape" ) and value2 .shape == ():
2440- expression = f"({ self .expression } { op } { value2 [()]} )"
2370+ expression = f"({ value1 .expression } { op } { value2 [()]} )"
24412371 else :
24422372 operand_to_key = {id (v ): k for k , v in value1 .operands .items ()}
24432373 try :
24442374 op_name = operand_to_key [id (value2 )]
24452375 except KeyError :
2446- op_name = f"o{ len (self .operands )} "
2376+ op_name = f"o{ len (value1 .operands )} "
24472377 new_operands = {op_name : value2 }
2448- expression = f"({ self .expression } { op } { op_name } )"
2378+ expression = f"({ value1 .expression } { op } { op_name } )"
24492379 self .operands = value1 .operands
24502380 else :
24512381 if np .isscalar (value1 ):
2452- expression = f"({ value1 } { op } { self .expression } )"
2382+ expression = f"({ value1 } { op } { value2 .expression } )"
24532383 elif hasattr (value1 , "shape" ) and value1 .shape == ():
2454- expression = f"({ value1 [()]} { op } { self .expression } )"
2384+ expression = f"({ value1 [()]} { op } { value2 .expression } )"
24552385 else :
24562386 operand_to_key = {id (v ): k for k , v in value2 .operands .items ()}
24572387 try :
@@ -2460,13 +2390,15 @@ def update_expr(self, new_op): # noqa: C901
24602390 op_name = f"o{ len (value2 .operands )} "
24612391 new_operands = {op_name : value1 }
24622392 if op == "[]" : # syntactic sugar for slicing
2463- expression = f"({ op_name } [{ self .expression } ])"
2393+ expression = f"({ op_name } [{ value2 .expression } ])"
24642394 else :
2465- expression = f"({ op_name } { op } { self .expression } )"
2395+ expression = f"({ op_name } { op } { value2 .expression } )"
24662396 self .operands = value2 .operands
24672397 # Return a new expression
24682398 operands = self .operands | new_operands
2469- return self ._new_expr (expression , operands , guess = False , out = None , where = None )
2399+ expr = self ._new_expr (expression , operands , guess = False , out = None , where = None )
2400+ expr ._dtype = dtype_ # override dtype with preserved dtype
2401+ return expr
24702402 finally :
24712403 blosc2 ._disable_overloaded_equal = prev_flag
24722404
@@ -2556,107 +2488,6 @@ def blocks(self):
25562488 self ._chunks , self ._blocks = compute_chunks_blocks (self .shape , None , None , dtype = self .dtype )
25572489 return self ._blocks
25582490
2559- def __array_ufunc__ (self , ufunc , method , * inputs , ** kwargs ):
2560- # Handle operations at the array level
2561- if method != "__call__" :
2562- return NotImplemented
2563-
2564- if ufunc in local_ufunc_map :
2565- return local_ufunc_map [ufunc ](* inputs )
2566-
2567- if ufunc in ufunc_map :
2568- value = inputs [0 ] if inputs [1 ] is self else inputs [1 ]
2569- _check_allowed_dtypes (value )
2570- return blosc2 .LazyExpr (new_op = (value , ufunc_map [ufunc ], self ))
2571-
2572- if ufunc in ufunc_map_1param :
2573- value = inputs [0 ]
2574- _check_allowed_dtypes (value )
2575- return blosc2 .LazyExpr (new_op = (value , ufunc_map_1param [ufunc ], None ))
2576-
2577- return NotImplemented
2578-
2579- def __neg__ (self ):
2580- return self .update_expr (new_op = (0 , "-" , self ))
2581-
2582- def __add__ (self , value ):
2583- return self .update_expr (new_op = (self , "+" , value ))
2584-
2585- def __iadd__ (self , other ):
2586- return self .update_expr (new_op = (self , "+" , other ))
2587-
2588- def __radd__ (self , value ):
2589- return self .update_expr (new_op = (value , "+" , self ))
2590-
2591- def __sub__ (self , value ):
2592- return self .update_expr (new_op = (self , "-" , value ))
2593-
2594- def __isub__ (self , value ):
2595- return self .update_expr (new_op = (self , "-" , value ))
2596-
2597- def __rsub__ (self , value ):
2598- return self .update_expr (new_op = (value , "-" , self ))
2599-
2600- def __mul__ (self , value ):
2601- return self .update_expr (new_op = (self , "*" , value ))
2602-
2603- def __imul__ (self , value ):
2604- return self .update_expr (new_op = (self , "*" , value ))
2605-
2606- def __rmul__ (self , value ):
2607- return self .update_expr (new_op = (value , "*" , self ))
2608-
2609- def __truediv__ (self , value ):
2610- return self .update_expr (new_op = (self , "/" , value ))
2611-
2612- def __itruediv__ (self , value ):
2613- return self .update_expr (new_op = (self , "/" , value ))
2614-
2615- def __rtruediv__ (self , value ):
2616- return self .update_expr (new_op = (value , "/" , self ))
2617-
2618- def __and__ (self , value ):
2619- return self .update_expr (new_op = (self , "&" , value ))
2620-
2621- def __rand__ (self , value ):
2622- return self .update_expr (new_op = (value , "&" , self ))
2623-
2624- def __or__ (self , value ):
2625- return self .update_expr (new_op = (self , "|" , value ))
2626-
2627- def __ror__ (self , value ):
2628- return self .update_expr (new_op = (value , "|" , self ))
2629-
2630- def __invert__ (self ):
2631- return self .update_expr (new_op = (self , "~" , None ))
2632-
2633- def __pow__ (self , value ):
2634- return self .update_expr (new_op = (self , "**" , value ))
2635-
2636- def __rpow__ (self , value ):
2637- return self .update_expr (new_op = (value , "**" , self ))
2638-
2639- def __ipow__ (self , value ):
2640- return self .update_expr (new_op = (self , "**" , value ))
2641-
2642- def __lt__ (self , value ):
2643- return self .update_expr (new_op = (self , "<" , value ))
2644-
2645- def __le__ (self , value ):
2646- return self .update_expr (new_op = (self , "<=" , value ))
2647-
2648- def __eq__ (self , value ):
2649- return self .update_expr (new_op = (self , "==" , value ))
2650-
2651- def __ne__ (self , value ):
2652- return self .update_expr (new_op = (self , "!=" , value ))
2653-
2654- def __gt__ (self , value ):
2655- return self .update_expr (new_op = (self , ">" , value ))
2656-
2657- def __ge__ (self , value ):
2658- return self .update_expr (new_op = (self , ">=" , value ))
2659-
26602491 def where (self , value1 = None , value2 = None ):
26612492 """
26622493 Select value1 or value2 values based on the condition of the current expression.
0 commit comments