https://github.com/tensorly/tensorly
generalised_inner_product.py
from ... import backend as T
import numpy as np
# Author: Jean Kossaifi
# License: BSD 3 clause
def inner(tensor1, tensor2, n_modes=None):
"""Generalised inner products between tensors
Takes the inner product between the last (respectively first)
`n_modes` of `tensor1` (respectively `tensor2`)
Parameters
----------
tensor1, tensor2 : tensor
n_modes : int, default is None
* if None, the traditional inner product is returned (i.e. a float)
* otherwise, the product between the `n_modes` last modes of `tensor1`
and the `n_modes` first modes of `tensor2` is returned.
The resulting tensor's order is `ndim(tensor1) - n_modes`.
Returns
-------
inner_product : float if n_modes is None, tensor otherwise
"""
# Traditional inner product
if n_modes is None:
if tensor1.shape != tensor2.shape:
raise ValueError(
"Taking a generalised product between two tensors without specifying common modes"
" is equivalent to taking inner product."
"This requires tensor1.shape == tensor2.shape."
f"However, got shapes {tensor1.shape} and {tensor2.shape}"
)
return T.sum(tensor1 * tensor2)
# Inner product along `n_modes` common modes
shape_t1 = list(T.shape(tensor1))
shape_t2 = list(T.shape(tensor2))
common_modes = shape_t1[len(shape_t1) - n_modes :]
common_size = int(np.prod(common_modes))
output_shape = shape_t1[:-n_modes] + shape_t2[n_modes:]
if common_modes != shape_t2[:n_modes]:
raise ValueError(
f"Incorrect shapes for inner product along {n_modes} common modes."
f"Shapes {tensor1.shape} and {tensor2.shape}"
)
inner_product = T.dot(
T.reshape(tensor1, (-1, common_size)), T.reshape(tensor2, (common_size, -1))
)
return T.reshape(inner_product, output_shape)