@@ -385,7 +385,7 @@ def _check_allowed_dtypes(
385385 ):
386386 raise RuntimeError (
387387 "Expected LazyExpr, NDArray, NDField, C2Array, Proxy, np.ndarray or scalar instances"
388- + f" and you provided a '{ type (value )} ' instance"
388+ f" and you provided a '{ type (value )} ' instance"
389389 )
390390
391391
@@ -3348,7 +3348,35 @@ def equal(
33483348 ----------
33493349 `np.equal <https://numpy.org/doc/stable/reference/generated/numpy.equal.html#numpy.equal>`_
33503350 """
3351- return blosc2 .LazyExpr (new_op = (x1 , "==" , x2 ))
3351+ return x1 == x2
3352+
3353+
3354+ def multiply (
3355+ x1 : NDArray | NDField | blosc2 .C2Array | blosc2 .LazyExpr ,
3356+ x2 : NDArray | NDField | blosc2 .C2Array | blosc2 .LazyExpr ,
3357+ ) -> blosc2 .LazyExpr :
3358+ """
3359+ Computes the value of x1_i * x2_i for each element x1_i of the input array x1
3360+ with the respective element x2_i of the input array x2.
3361+
3362+ Parameters
3363+ -----------
3364+ x1: NDArray | NDField | blosc2.C2Array | blosc2.LazyExpr
3365+ First input array. May have any data type.
3366+
3367+ x2:NDArray | NDField | blosc2.C2Array | blosc2.LazyExpr
3368+ Second input array. Must be compatible with x1. May have any data type.
3369+
3370+ Returns
3371+ -------
3372+ out LazyExpr
3373+ A LazyArray containing the element-wise results.
3374+
3375+ References
3376+ ----------
3377+ `np.multiply <https://numpy.org/doc/stable/reference/generated/numpy.multiply.html#numpy.multiply>`_
3378+ """
3379+ return x1 * x2
33523380
33533381
33543382def where (
@@ -4651,7 +4679,6 @@ def matmul(x1: NDArray | np.ndarray, x2: NDArray, **kwargs: Any) -> NDArray | np
46514679 return result
46524680
46534681
4654- # @profile
46554682def tensordot (
46564683 x1 : NDArray , x2 : NDArray , axes : int | tuple [Sequence [int ], Sequence [int ]] = 2 , ** kwargs : Any
46574684) -> NDArray :
@@ -4666,7 +4693,7 @@ def tensordot(
46664693 x2: NDArray
46674694 Second input array. Should have a numeric data type. Corresponding contracted axes of x1 and x2 must be equal.
46684695
4669- axes: (Union[ int, Tuple [Sequence[int], Sequence[int]]])
4696+ axes: int | tuple [Sequence[int], Sequence[int]]
46704697 Number of axes (dimensions) to contract or explicit sequences of axis (dimension) indices for x1 and x2, respectively.
46714698 * If axes is an int equal to N, then contraction is performed over the last N axes of x1 and the first N axes of x2 in order.
46724699 The size of each corresponding axis (dimension) must match. Must be nonnegative.
@@ -4819,6 +4846,105 @@ def tensordot(
48194846 return result
48204847
48214848
4849+ def vecdot (x1 : NDArray , x2 : NDArray , axis : int = - 1 , ** kwargs ) -> NDArray :
4850+ """
4851+ Computes the (vector) dot product of two arrays. Complex conjugates x1.
4852+
4853+ Parameters
4854+ ----------
4855+ x1: NDArray
4856+ First input array. Must have floating-point data type.
4857+
4858+ x2: NDArray
4859+ Second input array. Must be compatible with x1 for all non-contracted axes (via broadcasting).
4860+ The size of the axis over which to compute the dot product must be the same size as the respective axis in x1.
4861+ Must have a floating-point data type.
4862+
4863+ axis: int
4864+ The axis (dimension) of x1 and x2 containing the vectors for which to compute the dot product.
4865+ Should be an integer on the interval [-N, -1], where N is min(x1.ndim, x2.ndim). Default: -1.
4866+
4867+ Returns
4868+ -------
4869+ out: NDArray
4870+ If x1 and x2 are both one-dimensional arrays, a zero-dimensional containing the dot product;
4871+ otherwise, a non-zero-dimensional array containing the dot products and having rank N-1,
4872+ where N is the rank (number of dimensions) of the shape determined according to broadcasting
4873+ along the non-contracted axes.
4874+ """
4875+ fast_path = kwargs .pop ("fast_path" , None ) # for testing purposes
4876+ # Added this to pass array-api tests (which use internal getitem to check results)
4877+ if isinstance (x1 , np .ndarray ) and isinstance (x2 , np .ndarray ):
4878+ return np .vecdot (x1 , x2 , axis = axis )
4879+
4880+ x1 , x2 = blosc2 .asarray (x1 ), blosc2 .asarray (x2 )
4881+
4882+ N = builtins .min (x1 .ndim , x2 .ndim )
4883+ if axis < - N or axis > - 1 :
4884+ raise ValueError ("axis must be on interval [-N,-1]." )
4885+ a_axes = axis + x1 .ndim
4886+ b_axes = axis + x2 .ndim
4887+ a_keep = [True ] * x1 .ndim
4888+ a_keep [a_axes ] = False
4889+ b_keep = [True ] * x2 .ndim
4890+ b_keep [b_axes ] = False
4891+ if np .issubdtype (x1 .dtype , complex ):
4892+ x1 = blosc2 .conj (x1 )
4893+ x1shape = np .array (x1 .shape )
4894+ x2shape = np .array (x2 .shape )
4895+ result_shape = np .broadcast_shapes (x1shape [a_keep ], x2shape [b_keep ])
4896+ result = blosc2 .zeros (result_shape , dtype = np .result_type (x1 , x2 ), ** kwargs )
4897+
4898+ x1shape = np .array (x1 .shape )
4899+ x2shape = np .array (x2 .shape )
4900+ a_chunks_red = x1 .chunks [a_axes ]
4901+ a_shape_red = x1 .shape [a_axes ]
4902+
4903+ if np .any (x1shape [a_axes ] != x2shape [b_axes ]):
4904+ raise ValueError ("x1 and x2 must have same shapes along reduction dimensions" )
4905+
4906+ result_shape = np .broadcast_shapes (x1shape [a_keep ], x2shape [b_keep ])
4907+ result = blosc2 .zeros (result_shape , dtype = np .result_type (x1 , x2 ), ** kwargs )
4908+
4909+ res_chunks = [
4910+ slice_to_chunktuple (s , c )
4911+ for s , c in zip ([slice (0 , r , 1 ) for r in result .shape ], result .chunks , strict = True )
4912+ ]
4913+ a_selection = (slice (None , None , 1 ),) * x1 .ndim
4914+ b_selection = (slice (None , None , 1 ),) * x2 .ndim
4915+
4916+ chunk_memory = np .prod (result .chunks ) * (
4917+ x1shape [a_axes ] * x1 .dtype .itemsize + x2shape [b_axes ] * x2 .dtype .itemsize
4918+ )
4919+ if chunk_memory < blosc2 .MAX_FAST_PATH_SIZE :
4920+ fast_path = True if fast_path is None else fast_path
4921+ fast_path = False if fast_path is None else fast_path # fast_path set via kwargs for testing
4922+
4923+ for rchunk in product (* res_chunks ):
4924+ res_chunk = tuple (
4925+ slice (rc * rcs , builtins .min ((rc + 1 ) * rcs , rshape ), 1 )
4926+ for rc , rcs , rshape in zip (rchunk , result .chunks , result .shape , strict = True )
4927+ )
4928+ rchunk_iter = iter (res_chunk )
4929+ a_selection = tuple (next (rchunk_iter ) if a else slice (None , None , 1 ) for a in a_keep )
4930+ rchunk_iter = iter (res_chunk )
4931+ b_selection = tuple (next (rchunk_iter ) if b else slice (None , None , 1 ) for b in b_keep )
4932+
4933+ if fast_path : # just load everything
4934+ bx1 = x1 [a_selection ]
4935+ bx2 = x2 [b_selection ]
4936+ result [res_chunk ] += np .vecdot (bx1 , bx2 , axis = axis )
4937+ else : # operands too big, have to go chunk-by-chunk
4938+ for ochunk in range (0 , a_shape_red , a_chunks_red ):
4939+ op_chunk = slice (ochunk , builtins .min (ochunk + a_chunks_red , x1 .shape [a_axes ]), 1 )
4940+ a_selection [a_axes ] = op_chunk
4941+ b_selection [b_axes ] = op_chunk
4942+ bx1 = x1 [a_selection ]
4943+ bx2 = x2 [b_selection ]
4944+ result [res_chunk ] += np .vecdot (bx1 , bx2 , axis = axis )
4945+ return result
4946+
4947+
48224948def permute_dims (
48234949 arr : NDArray | np .ndarray , axes : tuple [int ] | list [int ] | None = None , ** kwargs : Any
48244950) -> NDArray :
0 commit comments