Repository: scipy/scipy

Issue:
Khatri-Rao product
https://github.com/numpy/numpy/issues/15298
[reference](https://math.stackexchange.com/a/1916083/1896)
I would like to work on this issue


Discussion:

Resolving patch:
diff --git a/scipy/linalg/__init__.py b/scipy/linalg/__init__.py
--- a/scipy/linalg/__init__.py
+++ b/scipy/linalg/__init__.py
@@ -38,6 +38,7 @@
    pinv2 - Pseudo-inverse using svd
    pinvh - Pseudo-inverse of hermitian matrix
    kron - Kronecker product of two arrays
+   khatri_rao - Khatri-Rao product of two arrays
    tril - Construct a lower-triangular matrix from a given matrix
    triu - Construct an upper-triangular matrix from a given matrix
    orthogonal_procrustes - Solve an orthogonal Procrustes problem
diff --git a/scipy/linalg/matfuncs.py b/scipy/linalg/matfuncs.py
--- a/scipy/linalg/matfuncs.py
+++ b/scipy/linalg/matfuncs.py
@@ -6,7 +6,8 @@
 
 __all__ = ['expm','cosm','sinm','tanm','coshm','sinhm',
            'tanhm','logm','funm','signm','sqrtm',
-           'expm_frechet', 'expm_cond', 'fractional_matrix_power']
+           'expm_frechet', 'expm_cond', 'fractional_matrix_power',
+           'khatri_rao']
 
 from numpy import (Inf, dot, diag, prod, logical_not, ravel,
         transpose, conjugate, absolute, amax, sign, isfinite, single)
@@ -668,3 +669,66 @@ def rounded_sign(x):
         return S0
     else:
         return S0, errest
+
+
+def khatri_rao(a, b):
+    r"""
+    Khatri-rao product
+
+    A column-wise Kronecker product of two matrices
+
+    Parameters
+    ----------
+    a:  (n, k) array_like
+        Input array
+    b:  (m, k) array_like
+        Input array
+
+    Returns
+    -------
+    c:  (n*m, k) ndarray
+        Khatri-rao product of `a` and `b`.
+
+    Notes
+    -----
+    The mathematical definition of the Khatri-Rao product is:
+
+    .. math::
+
+        (A_{ij}  \bigotimes B_{ij})_{ij}
+
+    which is the Kronecker product of every column of A and B, e.g.::
+
+        c = np.vstack([np.kron(a[:, k], b[:, k]) for k in range(b.shape[1])]).T
+
+    See Also
+    --------
+    kron : Kronecker product
+
+    Examples
+    --------
+    >>> from scipy import linalg
+    >>> a = np.array([[1, 2, 3], [4, 5, 6]])
+    >>> b = np.array([[3, 4, 5], [6, 7, 8], [2, 3, 9]])
+    >>> linalg.khatri_rao(a, b)
+    array([[ 3,  8, 15],
+           [ 6, 14, 24],
+           [ 2,  6, 27],
+           [12, 20, 30],
+           [24, 35, 48],
+           [ 8, 15, 54]])
+
+    """
+    a = np.asarray(a)
+    b = np.asarray(b)
+
+    if not(a.ndim == 2 and b.ndim == 2):
+        raise ValueError("The both arrays should be 2-dimensional.")
+
+    if not a.shape[1] == b.shape[1]:
+        raise ValueError("The number of columns for both arrays "
+                         "should be equal.")
+
+    # c = np.vstack([np.kron(a[:, k], b[:, k]) for k in range(b.shape[1])]).T
+    c = a[..., :, np.newaxis, :] * b[..., np.newaxis, :, :]
+    return c.reshape((-1,) + c.shape[2:])


Test patch:
diff --git a/scipy/linalg/tests/test_matfuncs.py b/scipy/linalg/tests/test_matfuncs.py
--- a/scipy/linalg/tests/test_matfuncs.py
+++ b/scipy/linalg/tests/test_matfuncs.py
@@ -19,7 +19,7 @@
 
 import scipy.linalg
 from scipy.linalg import (funm, signm, logm, sqrtm, fractional_matrix_power,
-                          expm, expm_frechet, expm_cond, norm)
+                          expm, expm_frechet, expm_cond, norm, khatri_rao)
 from scipy.linalg import _matfuncs_inv_ssq
 import scipy.linalg._expm_frechet
 
@@ -838,3 +838,61 @@ def test_expm_cond_fuzz(self):
             # eps times the condition number kappa.
             # In the limit as eps approaches zero it should never be greater.
             assert_array_less(p_best_relerr, (1 + 2*eps) * eps * kappa)
+
+
+class TestKhatriRao(object):
+
+    def test_basic(self):
+        a = khatri_rao(array([[1, 2], [3, 4]]),
+                       array([[5, 6], [7, 8]]))
+
+        assert_array_equal(a, array([[5, 12],
+                                     [7, 16],
+                                     [15, 24],
+                                     [21, 32]]))
+
+        b = khatri_rao(np.empty([2, 2]), np.empty([2, 2]))
+        assert_array_equal(b.shape, (4, 2))
+
+    def test_number_of_columns_equality(self):
+        with pytest.raises(ValueError):
+            a = array([[1, 2, 3],
+                       [4, 5, 6]])
+            b = array([[1, 2],
+                       [3, 4]])
+            khatri_rao(a, b)
+
+    def test_to_assure_2d_array(self):
+        with pytest.raises(ValueError):
+            # both arrays are 1-D
+            a = array([1, 2, 3])
+            b = array([4, 5, 6])
+            khatri_rao(a, b)
+
+        with pytest.raises(ValueError):
+            # first array is 1-D
+            a = array([1, 2, 3])
+            b = array([
+                [1, 2, 3],
+                [4, 5, 6]
+            ])
+            khatri_rao(a, b)
+
+        with pytest.raises(ValueError):
+            # second array is 1-D
+            a = array([
+                [1, 2, 3],
+                [7, 8, 9]
+            ])
+            b = array([4, 5, 6])
+            khatri_rao(a, b)
+
+    def test_equality_of_two_equations(self):
+        a = array([[1, 2], [3, 4]])
+        b = array([[5, 6], [7, 8]])
+
+        res1 = khatri_rao(a, b)
+        res2 = np.vstack([np.kron(a[:, k], b[:, k])
+                          for k in range(b.shape[1])]).T
+
+        assert_array_equal(res1, res2)