Skip to content

Commit 515d597

Browse files
committed
Added vecdot function
1 parent 7cd11d6 commit 515d597

3 files changed

Lines changed: 135 additions & 5 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ else()
5050
include(FetchContent)
5151
FetchContent_Declare(blosc2
5252
GIT_REPOSITORY https://github.com/Blosc/c-blosc2
53-
GIT_TAG 82a85f9d285d7ae6d141c73e84c7574bfc24a134 # v2.21.2
53+
GIT_TAG 5b9a8c00d295c1b4ee3a4a68773262b79812ba07 # v2.21.2
5454
)
5555
FetchContent_MakeAvailable(blosc2)
5656
include_directories("${blosc2_SOURCE_DIR}/include")

src/blosc2/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ def _raise(exc):
357357
save,
358358
matmul,
359359
tensordot,
360+
vecdot,
360361
permute_dims,
361362
transpose,
362363
matrix_transpose,
@@ -447,6 +448,7 @@ def _raise(exc):
447448
max,
448449
mean,
449450
min,
451+
multiply,
450452
prod,
451453
real,
452454
sin,
@@ -577,6 +579,7 @@ def _raise(exc):
577579
"mean",
578580
"meshgrid",
579581
"min",
582+
"multiply",
580583
"nans",
581584
"ndarray_from_cframe",
582585
"ones",
@@ -619,6 +622,7 @@ def _raise(exc):
619622
"unpack_tensor",
620623
"validate_expr",
621624
"var",
625+
"vecdot",
622626
"where",
623627
"zeros",
624628
"zeros_like",

src/blosc2/ndarray.py

Lines changed: 130 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

33543382
def where(
@@ -4651,7 +4679,6 @@ def matmul(x1: NDArray | np.ndarray, x2: NDArray, **kwargs: Any) -> NDArray | np
46514679
return result
46524680

46534681

4654-
# @profile
46554682
def 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+
48224948
def permute_dims(
48234949
arr: NDArray | np.ndarray, axes: tuple[int] | list[int] | None = None, **kwargs: Any
48244950
) -> NDArray:

0 commit comments

Comments
 (0)