Multiplication
Kronecker.jl
allows for efficient multiplication of large Kronecker systems by overloading the multiplication function *
. We distinguish three cases:
- Kronecker-Kronecker multiplications yield again a type of
AbstractKroneckerProduct
; - Kronecker-vector multiplications use the 'vec trick' and yield a vector;
- sampled Kronecker-vector multiplications use the sampled-vec trick to yield a vector.
Kronecker-kronecker multiplications
Multiplying two conformable Kronecker products of the same order yield a new Kronecker product, based on the mixed-product property:
\[(A \otimes B)(C \otimes D) = (AC) \otimes (BD),\]
A, B, C, D = randn(5, 5), randn(4, 4), randn(5, 4), randn(4, 4);
(A ⊗ B) * (C ⊗ D)
20×16 Kronecker.KroneckerProduct{Float64,Array{Float64,2},Array{Float64,2}}:
-2.53001 1.32594 1.31749 … -0.182948 0.547951
6.14912 7.16137 2.66271 -0.369747 -0.919557
1.19679 0.886609 0.293427 -0.0407456 -0.405255
-2.4154 -0.0394593 -7.63511 1.06022 1.92124
1.26891 -0.66502 -0.660778 -0.0472649 0.141564
-3.08406 -3.59175 -1.33547 … -0.0955249 -0.237569
-0.600245 -0.444674 -0.147167 -0.0105267 -0.104698
1.21143 0.0197906 3.82935 0.27391 0.496356
-0.0692255 0.0362801 0.0360487 -0.0210722 0.0631141
0.168251 0.195947 0.0728564 -0.0425882 -0.105916
0.0327463 0.0242591 0.00802866 … -0.00469315 -0.046678
-0.0660895 -0.00107968 -0.20891 0.122118 0.221292
1.64099 -0.860019 -0.854533 -0.212799 0.637361
-3.98838 -4.64493 -1.72706 -0.430079 -1.0696
-0.77625 -0.575063 -0.190319 -0.0473941 -0.471381
1.56665 0.0255937 4.9522 … 1.23322 2.23473
-1.31762 0.690545 0.686141 -1.43423 4.29569
3.20243 3.72961 1.38673 -2.89865 -7.20892
0.623284 0.461742 0.152815 -0.319427 -3.17701
-1.25793 -0.0205503 -3.97633 8.31165 15.0617
The Vec trick
Reshaping allows computing a product between a Kronecker product and vector as two matrix multiplications. This is the so-called vec trick which holds for any set of conformable matrices:
\[(A \otimes B) \text{vec}(X) = \text{vec}(B^\intercal X A).\]
Here, $\text{vec}(\cdot)$ is the vectorization operator, which stacks all columns of a matrix into a vector.
A, B = rand(10, 10), rand(5, 6);
x = randn(60);
(A ⊗ B) * x
50-element Array{Float64,1}:
-6.755234772252771
-1.5339020476853158
0.28718084992929105
-4.461604506324915
-1.683274371359666
-7.108126720386315
-2.963076723433096
0.5198640466570326
-5.503484373407693
-0.8754558089887536
⋮
-1.7003184119406807
0.4328280494699081
-5.023359964824601
-1.0650826889880298
-7.945219678754793
-2.4329946184020574
-0.20306351171838877
-6.644509080895488
-1.4967462019517703
Note that this trick is extended to also work with matrices:
A, B = rand(10, 10), rand(5, 6);
x = randn(60, 2);
(A ⊗ B) * x
50×2 Array{Float64,2}:
3.6005 -2.62589
4.18739 -0.533891
2.57193 -1.84092
4.49801 0.55351
5.46304 -3.07077
0.147221 -2.64655
1.84116 -1.82511
-0.593763 -2.94895
1.32617 -2.69926
1.45043 -3.32702
⋮
2.07592 0.227418
0.418109 -1.04095
2.00125 1.076
2.47961 -1.98871
2.30232 -2.0384
4.41552 0.426986
2.32245 -1.57576
5.42984 1.64215
3.68175 -1.81372
The vec trick works with higher-order Kronecker products. However, at the moment this has a substantial overhead and likely be relatively slow.
Docstrings
Missing docstring for mul!
. Check Documenter's build log for details.
LinearAlgebra.lmul!
— Functionlmul!(a::Number, K::AbstractKroneckerProduct)
Scale an AbstractKroneckerProduct
K
inplace by a factor a
by rescaling the left matrix.
lmul!(a::Number, K::KroneckerPower)
Scale an KroneckerPower
K
inplace by a factor a
by rescaling the matrix the base matrix with a factor a^(1/N)
.
It is recommended to rewrite your Kronecker product rather as copy(A) ⊗ (A ⊗ n - 1)
(note the copy) for numerical stability. This will only modify the first matrix, leaving the chain of Kronecker products alone.
LinearAlgebra.rmul!
— Functionrmul!(K::AbstractKroneckerProduct, a::Number)
Scale an AbstractKroneckerProduct
K
inplace by a factor a
by rescaling the right matrix.
Sampled Kronecker-vector multiplications
See Indexed Kronecker products for the specifics.