blob: 269b2ea30406cf0f8c7a95129d676869e3c1defb [file] [log] [blame]
// 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)
}