Skip to content

Expose vecdot, vecmat and matvec helpers #1237

Closed
@ricardoV94

Description

@ricardoV94

Description

Newer numpy versions introduced the vecmat and matvec (and they had vecdot for a while), which we have internally as different Blockwise versions of Dot, just like matmul. We should expose those as we do with matmul:

# Predefine all batched variations of Dot
_inner_prod = Blockwise(
_dot,
signature="(n),(n)->()",
)
_matrix_vec_prod = Blockwise(
_dot,
signature="(m,k),(k)->(m)",
)
_vec_matrix_prod = Blockwise(
_dot,
signature="(k),(k,n)->(n)",
)
_matrix_matrix_matmul = Blockwise(
_dot,
signature="(m,k),(k,n)->(m,n)",
gufunc_spec=("numpy.matmul", 2, 1),
)
def matmul(x1: "ArrayLike", x2: "ArrayLike", dtype: Optional["DTypeLike"] = None):
"""Compute the matrix product of two tensor variables.
Parameters
----------
x1, x2
Input arrays, scalars not allowed.
dtype
The desired data-type for the array. If not given, then the type will
be determined as the minimum type required to hold the objects in the
sequence.
Returns
-------
out : ndarray
The matrix product of the inputs. This is a scalar only when both
`x1`, `x2` are 1-d vectors.
Raises
------
ValueError
If the last dimension of `x1` is not the same size as the second-to-last
dimension of `x2`. If a scalar value is passed in.
Notes
-----
The behavior depends on the arguments in the following way.
- If both arguments are 2-D they are multiplied like conventional matrices.
- If either argument is N-D, N > 2, it is treated as a stack of matrices
residing in the last two indexes and broadcast accordingly.
- If the first argument is 1-D, it is promoted to a matrix by prepending a
1 to its dimensions. After matrix multiplication the prepended 1 is removed.
- If the second argument is 1-D, it is promoted to a matrix by appending a
1 to its dimensions. After matrix multiplication the appended 1 is removed.
`matmul` differs from `dot` in two important ways:
- Multiplication by scalars is not allowed, use `mul` instead.
- Stacks of matrices are broadcast together as if the matrices were elements,
respecting the signature ``(n, k), (k, m) -> (n, m)``:
"""
x1 = as_tensor_variable(x1)
x2 = as_tensor_variable(x2)
if x1.type.ndim == 0 or x2.type.ndim == 0:
raise ValueError("matmul operand cannot be scalar")
if x1.type.ndim == 1 and x2.type.ndim == 1:
out = _dot(x1, x2)
elif x1.type.ndim == 1:
out = _matrix_matrix_matmul(x1[None], x2).squeeze(-2)
elif x2.type.ndim == 1:
out = _matrix_matrix_matmul(x1, x2[:, None]).squeeze(-1)
else:
out = _matrix_matrix_matmul(x1, x2)
if dtype is not None:
out = out.astype(dtype)
return out

https://numpy.org/doc/stable/reference/generated/numpy.matvec.html#numpy.matvec
https://numpy.org/doc/stable/reference/generated/numpy.vecmat.html
https://numpy.org/doc/stable/reference/generated/numpy.vecdot.html#numpy.vecdot

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions