mat: add Kronecker product function
MODIFY
mat/dense_arithmetic.go
mat/dense_arithmetic.go
@@ -673,6 +673,20 @@ func (m *Dense) Pow(a Matrix, n int) {
putWorkspace(x)
}
+// Kronecker calculates the Kronecker product of a and b, placing the result in
+// the receiver.
+func (m *Dense) Kronecker(a, b Matrix) {
+ ra, ca := a.Dims()
+ rb, cb := b.Dims()
+
+ m.reuseAsNonZeroed(ra*rb, ca*cb)
+ for i := 0; i < ra; i++ {
+ for j := 0; j < ca; j++ {
+ m.Slice(i*rb, (i+1)*rb, j*cb, (j+1)*cb).(*Dense).Scale(a.At(i, j), b)
+ }
+ }
+}
+
// Scale multiplies the elements of a by f, placing the result in the receiver.
//
// See the Scaler interface for more information.

MODIFY
mat/dense_test.go
mat/dense_test.go
@@ -1087,6 +1087,106 @@ func TestDensePow(t *testing.T) {
}
}
+func TestDenseKronecker(t *testing.T) {
+ for i, test := range []struct {
+ a, b Matrix
+ want *Dense
+ }{
+ {
+ a: NewDense(1, 3, []float64{1, 2, 3}),
+ b: NewDense(3, 1, []float64{1, 2, 3}),
+ want: NewDense(3, 3, []float64{
+ 1, 2, 3,
+ 2, 4, 6,
+ 3, 6, 9,
+ }),
+ },
+ {
+ a: NewDense(3, 1, []float64{1, 2, 3}),
+ b: NewDense(1, 3, []float64{1, 2, 3}),
+ want: NewDense(3, 3, []float64{
+ 1, 2, 3,
+ 2, 4, 6,
+ 3, 6, 9,
+ }),
+ },
+ {
+ a: NewDense(1, 3, []float64{1, 2, 3}),
+ b: NewDense(2, 1, []float64{1, 2}),
+ want: NewDense(2, 3, []float64{
+ 1, 2, 3,
+ 2, 4, 6,
+ }),
+ },
+ {
+ a: NewDense(2, 1, []float64{1, 2}),
+ b: NewDense(1, 3, []float64{1, 2, 3}),
+ want: NewDense(2, 3, []float64{
+ 1, 2, 3,
+ 2, 4, 6,
+ }),
+ },
+ {
+ a: NewDense(1, 2, []float64{1, 2}),
+ b: NewDense(3, 1, []float64{1, 2, 3}),
+ want: NewDense(3, 2, []float64{
+ 1, 2,
+ 2, 4,
+ 3, 6,
+ }),
+ },
+ {
+ a: NewDense(3, 1, []float64{1, 2, 3}),
+ b: NewDense(1, 2, []float64{1, 2}),
+ want: NewDense(3, 2, []float64{
+ 1, 2,
+ 2, 4,
+ 3, 6,
+ }),
+ },
+
+ // Examples from https://en.wikipedia.org/wiki/Kronecker_product.
+ {
+ a: NewDense(2, 2, []float64{1, 2, 3, 4}),
+ b: NewDense(2, 2, []float64{0, 5, 6, 7}),
+ want: NewDense(4, 4, []float64{
+ 0, 5, 0, 10,
+ 6, 7, 12, 14,
+ 0, 15, 0, 20,
+ 18, 21, 24, 28,
+ }),
+ },
+ {
+ a: NewDense(2, 3, []float64{
+ 1, -4, 7,
+ -2, 3, 3,
+ }),
+ b: NewDense(4, 4, []float64{
+ 8, -9, -6, 5,
+ 1, -3, -4, 7,
+ 2, 8, -8, -3,
+ 1, 2, -5, -1,
+ }),
+ want: NewDense(8, 12, []float64{
+ 8, -9, -6, 5, -32, 36, 24, -20, 56, -63, -42, 35,
+ 1, -3, -4, 7, -4, 12, 16, -28, 7, -21, -28, 49,
+ 2, 8, -8, -3, -8, -32, 32, 12, 14, 56, -56, -21,
+ 1, 2, -5, -1, -4, -8, 20, 4, 7, 14, -35, -7,
+ -16, 18, 12, -10, 24, -27, -18, 15, 24, -27, -18, 15,
+ -2, 6, 8, -14, 3, -9, -12, 21, 3, -9, -12, 21,
+ -4, -16, 16, 6, 6, 24, -24, -9, 6, 24, -24, -9,
+ -2, -4, 10, 2, 3, 6, -15, -3, 3, 6, -15, -3,
+ }),
+ },
+ } {
+ var got Dense
+ got.Kronecker(test.a, test.b)
+ if !Equal(&got, test.want) {
+ t.Errorf("unexpected result for test %d\ngot:%#v want:%#v", i, &got, test.want)
+ }
+ }
+}
+
func TestDenseScale(t *testing.T) {
for _, f := range []float64{0.5, 1, 3} {
method := func(receiver, a Matrix) {

mat: add Kronecker product function