Kronecker.jl icon indicating copy to clipboard operation
Kronecker.jl copied to clipboard

Khatri-Rao (columnwise Kronecker) products

Open PythonNut opened this issue 4 years ago • 2 comments

The Khatri-Rao product is a matrix product that is closely related to the Kronecker product. Although the definition of Khatri-Rao product applies to partitioned matrices, it is common to use the trivial partition (where every element is in its own partition). In this case, the Khatri-Rao product becomes the column-wise Kronecker product. One (probably pretty inefficient) way to implement the Khatri-Rao product in this case would be:

khatri_rao(A, B) = mapreduce(kron, hcat, eachcol(A), eachcol(B))

This means that a lazy Khatri-Rao product could be created using existing tools, but this would require a lot of reshaping and a lot of lazy hcats which are slow, and of course the real benefits come when we encode algebraic optimizations using multiple dispatch. There is also an analogous row-wise product called the transposed Khatri-Rao product (or the "face-splitting" product).

References:

PythonNut avatar Mar 18 '21 23:03 PythonNut

Hmmm, I don't really know this product but would it not possible to directly reproduce it using block matrices and the dot syntax, for example:

A = [rand(2,2) for i in 1:2, j in 1:2]
B = [randn(2,2) for i in 1:2, j in 1:2]
kronecker.(A, B)

Which would mean that we can implement this as:

⋆(A::AbstractMatrix{<:AbstractMatrix}, B::AbstractMatrix{<:AbstractMatrix}) = A ⊗. B

?

MichielStock avatar Mar 31 '21 14:03 MichielStock

You can implement it with LinearMaps.jl as follows:

using LinearMaps
khatri_rao(A::AbstractMatrix, B::AbstractMatrix) = hcat(map(⊗, eachcol(A), eachcol(B))...)
# alternatively
using Kronecker
khatri_rao(A::AbstractMatrix, B::AbstractMatrix) = hcat(map(LinearMap∘kronecker, eachcol(A), eachcol(B))...)

I strongly recommend benchmarking for your use case, which Kronecker product implementation works best for you.

dkarrasch avatar Sep 09 '22 12:09 dkarrasch