| // 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" |
| |
| "golang.org/x/exp/rand" |
| |
| "gonum.org/v1/gonum/blas/blas64" |
| "gonum.org/v1/gonum/blas/testblas" |
| ) |
| |
| func TestInner(t *testing.T) { |
| t.Parallel() |
| 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) { |
| t.Parallel() |
| 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{ |
| mat: blas64.Vector{ |
| N: len(f), |
| 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) { |
| src := rand.NewSource(1) |
| x := NewVecDense(m, nil) |
| randomSlice(x.mat.Data, src) |
| y := NewVecDense(n, nil) |
| randomSlice(y.mat.Data, src) |
| data := make([]float64, m*n) |
| randomSlice(data, src) |
| 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) |
| } |