// Copyright ©2014 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package mat import ( "math" "testing" "gonum.org/v1/gonum/blas/blas64" "gonum.org/v1/gonum/blas/testblas" ) func TestInner(t *testing.T) { for i, test := range []struct { x []float64 y []float64 m [][]float64 }{ { x: []float64{5}, y: []float64{10}, m: [][]float64{{2}}, }, { x: []float64{5, 6, 1}, y: []float64{10}, m: [][]float64{{2}, {-3}, {5}}, }, { x: []float64{5}, y: []float64{10, 15}, m: [][]float64{{2, -3}}, }, { x: []float64{1, 5}, y: []float64{10, 15}, m: [][]float64{ {2, -3}, {4, -1}, }, }, { x: []float64{2, 3, 9}, y: []float64{8, 9}, m: [][]float64{ {2, 3}, {4, 5}, {6, 7}, }, }, { x: []float64{2, 3}, y: []float64{8, 9, 9}, m: [][]float64{ {2, 3, 6}, {4, 5, 7}, }, }, } { for _, inc := range []struct{ x, y int }{ {1, 1}, {1, 2}, {2, 1}, {2, 2}, } { x := NewDense(1, len(test.x), test.x) m := NewDense(flatten(test.m)) mWant := NewDense(flatten(test.m)) y := NewDense(len(test.y), 1, test.y) var tmp, cell Dense tmp.Mul(mWant, y) cell.Mul(x, &tmp) rm, cm := cell.Dims() if rm != 1 { t.Errorf("Test %d result doesn't have 1 row", i) } if cm != 1 { t.Errorf("Test %d result doesn't have 1 column", i) } want := cell.At(0, 0) got := Inner(makeVecDenseInc(inc.x, test.x), m, makeVecDenseInc(inc.y, test.y)) if got != want { t.Errorf("Test %v: want %v, got %v", i, want, got) } } } } func TestInnerSym(t *testing.T) { for _, inc := range []struct{ x, y int }{ {1, 1}, {1, 2}, {2, 1}, {2, 2}, } { n := 10 xData := make([]float64, n) yData := make([]float64, n) data := make([]float64, n*n) for i := 0; i < n; i++ { xData[i] = float64(i) yData[i] = float64(i) for j := i; j < n; j++ { data[i*n+j] = float64(i*n + j) data[j*n+i] = data[i*n+j] } } x := makeVecDenseInc(inc.x, xData) y := makeVecDenseInc(inc.y, yData) m := NewDense(n, n, data) ans := Inner(x, m, y) sym := NewSymDense(n, data) // Poison the lower half of data to ensure it is not used. for i := 1; i < n; i++ { for j := 0; j < i; j++ { data[i*n+j] = math.NaN() } } if math.Abs(Inner(x, sym, y)-ans) > 1e-14 { t.Error("inner different symmetric and dense") } } } func makeVecDenseInc(inc int, f []float64) *VecDense { v := &VecDense{ n: len(f), mat: blas64.Vector{ Inc: inc, Data: make([]float64, (len(f)-1)*inc+1), }, } // Contaminate backing data in all positions... const base = 100 for i := range v.mat.Data { v.mat.Data[i] = float64(i + base) } // then write real elements. for i := range f { v.mat.Data[i*inc] = f[i] } return v } func benchmarkInner(b *testing.B, m, n int) { x := NewVecDense(m, nil) randomSlice(x.mat.Data) y := NewVecDense(n, nil) randomSlice(y.mat.Data) data := make([]float64, m*n) randomSlice(data) mat := &Dense{mat: blas64.General{Rows: m, Cols: n, Stride: n, Data: data}, capRows: m, capCols: n} b.ResetTimer() for i := 0; i < b.N; i++ { Inner(x, mat, y) } } func BenchmarkInnerSmSm(b *testing.B) { benchmarkInner(b, testblas.SmallMat, testblas.SmallMat) } func BenchmarkInnerMedMed(b *testing.B) { benchmarkInner(b, testblas.MediumMat, testblas.MediumMat) } func BenchmarkInnerLgLg(b *testing.B) { benchmarkInner(b, testblas.LargeMat, testblas.LargeMat) } func BenchmarkInnerLgSm(b *testing.B) { benchmarkInner(b, testblas.LargeMat, testblas.SmallMat) }