OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / gonum.org / v1 / gonum / mat / matrix_test.go
diff --git a/vendor/gonum.org/v1/gonum/mat/matrix_test.go b/vendor/gonum.org/v1/gonum/mat/matrix_test.go
new file mode 100644 (file)
index 0000000..35fe6a0
--- /dev/null
@@ -0,0 +1,654 @@
+// Copyright ©2013 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 (
+       "fmt"
+       "math"
+       "reflect"
+       "testing"
+
+       "gonum.org/v1/gonum/blas"
+       "gonum.org/v1/gonum/blas/blas64"
+       "gonum.org/v1/gonum/floats"
+)
+
+func panics(fn func()) (panicked bool, message string) {
+       defer func() {
+               r := recover()
+               panicked = r != nil
+               message = fmt.Sprint(r)
+       }()
+       fn()
+       return
+}
+
+func flatten(f [][]float64) (r, c int, d []float64) {
+       r = len(f)
+       if r == 0 {
+               panic("bad test: no row")
+       }
+       c = len(f[0])
+       d = make([]float64, 0, r*c)
+       for _, row := range f {
+               if len(row) != c {
+                       panic("bad test: ragged input")
+               }
+               d = append(d, row...)
+       }
+       return r, c, d
+}
+
+func unflatten(r, c int, d []float64) [][]float64 {
+       m := make([][]float64, r)
+       for i := 0; i < r; i++ {
+               m[i] = d[i*c : (i+1)*c]
+       }
+       return m
+}
+
+// eye returns a new identity matrix of size n×n.
+func eye(n int) *Dense {
+       d := make([]float64, n*n)
+       for i := 0; i < n*n; i += n + 1 {
+               d[i] = 1
+       }
+       return NewDense(n, n, d)
+}
+
+func TestCol(t *testing.T) {
+       for id, af := range [][][]float64{
+               {
+                       {1, 2, 3},
+                       {4, 5, 6},
+                       {7, 8, 9},
+               },
+               {
+                       {1, 2, 3},
+                       {4, 5, 6},
+                       {7, 8, 9},
+                       {10, 11, 12},
+               },
+               {
+                       {1, 2, 3, 4},
+                       {5, 6, 7, 8},
+                       {9, 10, 11, 12},
+               },
+       } {
+               a := NewDense(flatten(af))
+               col := make([]float64, a.mat.Rows)
+               for j := range af[0] {
+                       for i := range col {
+                               col[i] = float64(i*a.mat.Cols + j + 1)
+                       }
+
+                       if got := Col(nil, j, a); !reflect.DeepEqual(got, col) {
+                               t.Errorf("test %d: unexpected values returned for dense col %d: got: %v want: %v",
+                                       id, j, got, col)
+                       }
+
+                       got := make([]float64, a.mat.Rows)
+                       if Col(got, j, a); !reflect.DeepEqual(got, col) {
+                               t.Errorf("test %d: unexpected values filled for dense col %d: got: %v want: %v",
+                                       id, j, got, col)
+                       }
+               }
+       }
+
+       denseComparison := func(a *Dense) interface{} {
+               r, c := a.Dims()
+               ans := make([][]float64, c)
+               for j := range ans {
+                       ans[j] = make([]float64, r)
+                       for i := range ans[j] {
+                               ans[j][i] = a.At(i, j)
+                       }
+               }
+               return ans
+       }
+
+       f := func(a Matrix) interface{} {
+               _, c := a.Dims()
+               ans := make([][]float64, c)
+               for j := range ans {
+                       ans[j] = Col(nil, j, a)
+               }
+               return ans
+       }
+       testOneInputFunc(t, "Col", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
+
+       f = func(a Matrix) interface{} {
+               r, c := a.Dims()
+               ans := make([][]float64, c)
+               for j := range ans {
+                       ans[j] = make([]float64, r)
+                       Col(ans[j], j, a)
+               }
+               return ans
+       }
+       testOneInputFunc(t, "Col", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
+}
+
+func TestRow(t *testing.T) {
+       for id, af := range [][][]float64{
+               {
+                       {1, 2, 3},
+                       {4, 5, 6},
+                       {7, 8, 9},
+               },
+               {
+                       {1, 2, 3},
+                       {4, 5, 6},
+                       {7, 8, 9},
+                       {10, 11, 12},
+               },
+               {
+                       {1, 2, 3, 4},
+                       {5, 6, 7, 8},
+                       {9, 10, 11, 12},
+               },
+       } {
+               a := NewDense(flatten(af))
+               for i, row := range af {
+                       if got := Row(nil, i, a); !reflect.DeepEqual(got, row) {
+                               t.Errorf("test %d: unexpected values returned for dense row %d: got: %v want: %v",
+                                       id, i, got, row)
+                       }
+
+                       got := make([]float64, len(row))
+                       if Row(got, i, a); !reflect.DeepEqual(got, row) {
+                               t.Errorf("test %d: unexpected values filled for dense row %d: got: %v want: %v",
+                                       id, i, got, row)
+                       }
+               }
+       }
+
+       denseComparison := func(a *Dense) interface{} {
+               r, c := a.Dims()
+               ans := make([][]float64, r)
+               for i := range ans {
+                       ans[i] = make([]float64, c)
+                       for j := range ans[i] {
+                               ans[i][j] = a.At(i, j)
+                       }
+               }
+               return ans
+       }
+
+       f := func(a Matrix) interface{} {
+               r, _ := a.Dims()
+               ans := make([][]float64, r)
+               for i := range ans {
+                       ans[i] = Row(nil, i, a)
+               }
+               return ans
+       }
+       testOneInputFunc(t, "Row", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
+
+       f = func(a Matrix) interface{} {
+               r, c := a.Dims()
+               ans := make([][]float64, r)
+               for i := range ans {
+                       ans[i] = make([]float64, c)
+                       Row(ans[i], i, a)
+               }
+               return ans
+       }
+       testOneInputFunc(t, "Row", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
+}
+
+func TestCond(t *testing.T) {
+       for i, test := range []struct {
+               a       *Dense
+               condOne float64
+               condTwo float64
+               condInf float64
+       }{
+               {
+                       a: NewDense(3, 3, []float64{
+                               8, 1, 6,
+                               3, 5, 7,
+                               4, 9, 2,
+                       }),
+                       condOne: 16.0 / 3.0,
+                       condTwo: 4.330127018922192,
+                       condInf: 16.0 / 3.0,
+               },
+               {
+                       a: NewDense(4, 4, []float64{
+                               2, 9, 3, 2,
+                               10, 9, 9, 3,
+                               1, 1, 5, 2,
+                               8, 4, 10, 2,
+                       }),
+                       condOne: 1 / 0.024740155174938,
+                       condTwo: 34.521576567075087,
+                       condInf: 1 / 0.012034465570035,
+               },
+               {
+                       a: NewDense(3, 3, []float64{
+                               5, 6, 7,
+                               8, -2, 1,
+                               7, 7, 7}),
+                       condOne: 30.769230769230749,
+                       condTwo: 21.662689498448440,
+                       condInf: 31.153846153846136,
+               },
+       } {
+               orig := DenseCopyOf(test.a)
+               condOne := Cond(test.a, 1)
+               if !floats.EqualWithinAbsOrRel(test.condOne, condOne, 1e-13, 1e-13) {
+                       t.Errorf("Case %d: one norm mismatch. Want %v, got %v", i, test.condOne, condOne)
+               }
+               if !Equal(test.a, orig) {
+                       t.Errorf("Case %d: unexpected mutation of input matrix for one norm. Want %v, got %v", i, orig, test.a)
+               }
+               condTwo := Cond(test.a, 2)
+               if !floats.EqualWithinAbsOrRel(test.condTwo, condTwo, 1e-13, 1e-13) {
+                       t.Errorf("Case %d: two norm mismatch. Want %v, got %v", i, test.condTwo, condTwo)
+               }
+               if !Equal(test.a, orig) {
+                       t.Errorf("Case %d: unexpected mutation of input matrix for two norm. Want %v, got %v", i, orig, test.a)
+               }
+               condInf := Cond(test.a, math.Inf(1))
+               if !floats.EqualWithinAbsOrRel(test.condInf, condInf, 1e-13, 1e-13) {
+                       t.Errorf("Case %d: inf norm mismatch. Want %v, got %v", i, test.condInf, condInf)
+               }
+               if !Equal(test.a, orig) {
+                       t.Errorf("Case %d: unexpected mutation of input matrix for inf norm. Want %v, got %v", i, orig, test.a)
+               }
+       }
+
+       for _, test := range []struct {
+               name string
+               norm float64
+       }{
+               {
+                       name: "CondOne",
+                       norm: 1,
+               },
+               {
+                       name: "CondTwo",
+                       norm: 2,
+               },
+               {
+                       name: "CondInf",
+                       norm: math.Inf(1),
+               },
+       } {
+               f := func(a Matrix) interface{} {
+                       return Cond(a, test.norm)
+               }
+               denseComparison := func(a *Dense) interface{} {
+                       return Cond(a, test.norm)
+               }
+               testOneInputFunc(t, test.name, f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
+       }
+}
+
+func TestDet(t *testing.T) {
+       for c, test := range []struct {
+               a   *Dense
+               ans float64
+       }{
+               {
+                       a:   NewDense(2, 2, []float64{1, 0, 0, 1}),
+                       ans: 1,
+               },
+               {
+                       a:   NewDense(2, 2, []float64{1, 0, 0, -1}),
+                       ans: -1,
+               },
+               {
+                       a: NewDense(3, 3, []float64{
+                               1, 2, 0,
+                               0, 1, 2,
+                               0, 2, 1,
+                       }),
+                       ans: -3,
+               },
+               {
+                       a: NewDense(3, 3, []float64{
+                               1, 2, 3,
+                               5, 7, 9,
+                               6, 9, 12,
+                       }),
+                       ans: 0,
+               },
+       } {
+               a := DenseCopyOf(test.a)
+               det := Det(a)
+               if !Equal(a, test.a) {
+                       t.Errorf("Input matrix changed during Det. Case %d.", c)
+               }
+               if !floats.EqualWithinAbsOrRel(det, test.ans, 1e-14, 1e-14) {
+                       t.Errorf("Det mismatch case %d. Got %v, want %v", c, det, test.ans)
+               }
+       }
+       // Perform the normal list test to ensure it works for all types.
+       f := func(a Matrix) interface{} {
+               return Det(a)
+       }
+       denseComparison := func(a *Dense) interface{} {
+               return Det(a)
+       }
+       testOneInputFunc(t, "Det", f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isSquare)
+
+       // Check that it gives approximately the same answer as Cholesky
+       // Ensure the input matrices are wider than tall so they are full rank
+       isWide := func(ar, ac int) bool {
+               return ar <= ac
+       }
+       f = func(a Matrix) interface{} {
+               ar, ac := a.Dims()
+               if !isWide(ar, ac) {
+                       panic(ErrShape)
+               }
+               var tmp Dense
+               tmp.Mul(a, a.T())
+               return Det(&tmp)
+       }
+       denseComparison = func(a *Dense) interface{} {
+               ar, ac := a.Dims()
+               if !isWide(ar, ac) {
+                       panic(ErrShape)
+               }
+               var tmp SymDense
+               tmp.SymOuterK(1, a)
+               var chol Cholesky
+               ok := chol.Factorize(&tmp)
+               if !ok {
+                       panic("bad chol test")
+               }
+               return chol.Det()
+       }
+       testOneInputFunc(t, "DetVsChol", f, denseComparison, sameAnswerFloatApproxTol(1e-10), isAnyType, isWide)
+}
+
+type basicVector struct {
+       m []float64
+}
+
+func (v *basicVector) AtVec(i int) float64 {
+       if i < 0 || i >= v.Len() {
+               panic(ErrRowAccess)
+       }
+       return v.m[i]
+}
+
+func (v *basicVector) At(r, c int) float64 {
+       if c != 0 {
+               panic(ErrColAccess)
+       }
+       return v.AtVec(r)
+}
+
+func (v *basicVector) Dims() (r, c int) {
+       return v.Len(), 1
+}
+
+func (v *basicVector) T() Matrix {
+       return Transpose{v}
+}
+
+func (v *basicVector) Len() int {
+       return len(v.m)
+}
+
+func TestDot(t *testing.T) {
+       f := func(a, b Matrix) interface{} {
+               return Dot(a.(Vector), b.(Vector))
+       }
+       denseComparison := func(a, b *Dense) interface{} {
+               ra, ca := a.Dims()
+               rb, cb := b.Dims()
+               if ra != rb || ca != cb {
+                       panic(ErrShape)
+               }
+               var sum float64
+               for i := 0; i < ra; i++ {
+                       for j := 0; j < ca; j++ {
+                               sum += a.At(i, j) * b.At(i, j)
+                       }
+               }
+               return sum
+       }
+       testTwoInputFunc(t, "Dot", f, denseComparison, sameAnswerFloatApproxTol(1e-12), legalTypesVectorVector, legalSizeSameVec)
+}
+
+func TestEqual(t *testing.T) {
+       f := func(a, b Matrix) interface{} {
+               return Equal(a, b)
+       }
+       denseComparison := func(a, b *Dense) interface{} {
+               return Equal(a, b)
+       }
+       testTwoInputFunc(t, "Equal", f, denseComparison, sameAnswerBool, legalTypesAll, isAnySize2)
+}
+
+func TestMax(t *testing.T) {
+       // A direct test of Max with *Dense arguments is in TestNewDense.
+       f := func(a Matrix) interface{} {
+               return Max(a)
+       }
+       denseComparison := func(a *Dense) interface{} {
+               return Max(a)
+       }
+       testOneInputFunc(t, "Max", f, denseComparison, sameAnswerFloat, isAnyType, isAnySize)
+}
+
+func TestMin(t *testing.T) {
+       // A direct test of Min with *Dense arguments is in TestNewDense.
+       f := func(a Matrix) interface{} {
+               return Min(a)
+       }
+       denseComparison := func(a *Dense) interface{} {
+               return Min(a)
+       }
+       testOneInputFunc(t, "Min", f, denseComparison, sameAnswerFloat, isAnyType, isAnySize)
+}
+
+func TestNorm(t *testing.T) {
+       for i, test := range []struct {
+               a    [][]float64
+               ord  float64
+               norm float64
+       }{
+               {
+                       a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
+                       ord:  1,
+                       norm: 30,
+               },
+               {
+                       a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
+                       ord:  2,
+                       norm: 25.495097567963924,
+               },
+               {
+                       a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
+                       ord:  math.Inf(1),
+                       norm: 33,
+               },
+               {
+                       a:    [][]float64{{1, -2, -2}, {-4, 5, 6}},
+                       ord:  1,
+                       norm: 8,
+               },
+               {
+                       a:    [][]float64{{1, -2, -2}, {-4, 5, 6}},
+                       ord:  math.Inf(1),
+                       norm: 15,
+               },
+       } {
+               a := NewDense(flatten(test.a))
+               if math.Abs(Norm(a, test.ord)-test.norm) > 1e-14 {
+                       t.Errorf("Mismatch test %d: %v norm = %f", i, test.a, test.norm)
+               }
+       }
+
+       for _, test := range []struct {
+               name string
+               norm float64
+       }{
+               {"NormOne", 1},
+               {"NormTwo", 2},
+               {"NormInf", math.Inf(1)},
+       } {
+               f := func(a Matrix) interface{} {
+                       return Norm(a, test.norm)
+               }
+               denseComparison := func(a *Dense) interface{} {
+                       return Norm(a, test.norm)
+               }
+               testOneInputFunc(t, test.name, f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
+       }
+}
+
+func TestNormZero(t *testing.T) {
+       for _, a := range []Matrix{
+               &Dense{},
+               &SymDense{},
+               &SymDense{mat: blas64.Symmetric{Uplo: blas.Upper}},
+               &TriDense{},
+               &TriDense{mat: blas64.Triangular{Uplo: blas.Upper, Diag: blas.NonUnit}},
+               &VecDense{},
+       } {
+               for _, norm := range []float64{1, 2, math.Inf(1)} {
+                       panicked, message := panics(func() { Norm(a, norm) })
+                       if !panicked {
+                               t.Errorf("expected panic for Norm(&%T{}, %v)", a, norm)
+                       }
+                       if message != ErrShape.Error() {
+                               t.Errorf("unexpected panic string for Norm(&%T{}, %v): got:%s want:%s",
+                                       a, norm, message, ErrShape.Error())
+                       }
+               }
+       }
+}
+
+func TestSum(t *testing.T) {
+       f := func(a Matrix) interface{} {
+               return Sum(a)
+       }
+       denseComparison := func(a *Dense) interface{} {
+               return Sum(a)
+       }
+       testOneInputFunc(t, "Sum", f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
+}
+
+func TestTrace(t *testing.T) {
+       for _, test := range []struct {
+               a     *Dense
+               trace float64
+       }{
+               {
+                       a:     NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}),
+                       trace: 15,
+               },
+       } {
+               trace := Trace(test.a)
+               if trace != test.trace {
+                       t.Errorf("Trace mismatch. Want %v, got %v", test.trace, trace)
+               }
+       }
+       f := func(a Matrix) interface{} {
+               return Trace(a)
+       }
+       denseComparison := func(a *Dense) interface{} {
+               return Trace(a)
+       }
+       testOneInputFunc(t, "Trace", f, denseComparison, sameAnswerFloat, isAnyType, isSquare)
+}
+
+func TestDoer(t *testing.T) {
+       type MatrixDoer interface {
+               Matrix
+               NonZeroDoer
+               RowNonZeroDoer
+               ColNonZeroDoer
+       }
+       ones := func(n int) []float64 {
+               data := make([]float64, n)
+               for i := range data {
+                       data[i] = 1
+               }
+               return data
+       }
+       for i, m := range []MatrixDoer{
+               NewTriDense(3, Lower, ones(3*3)),
+               NewTriDense(3, Upper, ones(3*3)),
+               NewBandDense(6, 6, 1, 1, ones(3*6)),
+               NewBandDense(6, 10, 1, 1, ones(3*6)),
+               NewBandDense(10, 6, 1, 1, ones(7*3)),
+               NewSymBandDense(3, 0, ones(3)),
+               NewSymBandDense(3, 1, ones(3*(1+1))),
+               NewSymBandDense(6, 1, ones(6*(1+1))),
+               NewSymBandDense(6, 2, ones(6*(2+1))),
+       } {
+               r, c := m.Dims()
+
+               want := Sum(m)
+
+               // got and fn sum the accessed elements in
+               // the Doer that is being operated on.
+               // fn also tests that the accessed elements
+               // are within the writable areas of the
+               // matrix to check that only valid elements
+               // are operated on.
+               var got float64
+               fn := func(i, j int, v float64) {
+                       got += v
+                       switch m := m.(type) {
+                       case MutableTriangular:
+                               m.SetTri(i, j, v)
+                       case MutableBanded:
+                               m.SetBand(i, j, v)
+                       case MutableSymBanded:
+                               m.SetSymBand(i, j, v)
+                       default:
+                               panic("bad test: need mutable type")
+                       }
+               }
+
+               panicked, message := panics(func() { m.DoNonZero(fn) })
+               if panicked {
+                       t.Errorf("unexpected panic for Doer test %d: %q", i, message)
+                       continue
+               }
+               if got != want {
+                       t.Errorf("unexpected Doer sum: got:%f want:%f", got, want)
+               }
+
+               // Reset got for testing with DoRowNonZero.
+               got = 0
+               panicked, message = panics(func() {
+                       for i := 0; i < r; i++ {
+                               m.DoRowNonZero(i, fn)
+                       }
+               })
+               if panicked {
+                       t.Errorf("unexpected panic for RowDoer test %d: %q", i, message)
+                       continue
+               }
+               if got != want {
+                       t.Errorf("unexpected RowDoer sum: got:%f want:%f", got, want)
+               }
+
+               // Reset got for testing with DoColNonZero.
+               got = 0
+               panicked, message = panics(func() {
+                       for j := 0; j < c; j++ {
+                               m.DoColNonZero(j, fn)
+                       }
+               })
+               if panicked {
+                       t.Errorf("unexpected panic for ColDoer test %d: %q", i, message)
+                       continue
+               }
+               if got != want {
+                       t.Errorf("unexpected ColDoer sum: got:%f want:%f", got, want)
+               }
+       }
+}