1 // Copyright ©2013 The Gonum Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
13 "gonum.org/v1/gonum/blas"
14 "gonum.org/v1/gonum/blas/blas64"
15 "gonum.org/v1/gonum/floats"
18 func panics(fn func()) (panicked bool, message string) {
22 message = fmt.Sprint(r)
28 func flatten(f [][]float64) (r, c int, d []float64) {
31 panic("bad test: no row")
34 d = make([]float64, 0, r*c)
35 for _, row := range f {
37 panic("bad test: ragged input")
44 func unflatten(r, c int, d []float64) [][]float64 {
45 m := make([][]float64, r)
46 for i := 0; i < r; i++ {
47 m[i] = d[i*c : (i+1)*c]
52 // eye returns a new identity matrix of size n×n.
53 func eye(n int) *Dense {
54 d := make([]float64, n*n)
55 for i := 0; i < n*n; i += n + 1 {
58 return NewDense(n, n, d)
61 func TestCol(t *testing.T) {
62 for id, af := range [][][]float64{
80 a := NewDense(flatten(af))
81 col := make([]float64, a.mat.Rows)
82 for j := range af[0] {
84 col[i] = float64(i*a.mat.Cols + j + 1)
87 if got := Col(nil, j, a); !reflect.DeepEqual(got, col) {
88 t.Errorf("test %d: unexpected values returned for dense col %d: got: %v want: %v",
92 got := make([]float64, a.mat.Rows)
93 if Col(got, j, a); !reflect.DeepEqual(got, col) {
94 t.Errorf("test %d: unexpected values filled for dense col %d: got: %v want: %v",
100 denseComparison := func(a *Dense) interface{} {
102 ans := make([][]float64, c)
104 ans[j] = make([]float64, r)
105 for i := range ans[j] {
106 ans[j][i] = a.At(i, j)
112 f := func(a Matrix) interface{} {
114 ans := make([][]float64, c)
116 ans[j] = Col(nil, j, a)
120 testOneInputFunc(t, "Col", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
122 f = func(a Matrix) interface{} {
124 ans := make([][]float64, c)
126 ans[j] = make([]float64, r)
131 testOneInputFunc(t, "Col", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
134 func TestRow(t *testing.T) {
135 for id, af := range [][][]float64{
153 a := NewDense(flatten(af))
154 for i, row := range af {
155 if got := Row(nil, i, a); !reflect.DeepEqual(got, row) {
156 t.Errorf("test %d: unexpected values returned for dense row %d: got: %v want: %v",
160 got := make([]float64, len(row))
161 if Row(got, i, a); !reflect.DeepEqual(got, row) {
162 t.Errorf("test %d: unexpected values filled for dense row %d: got: %v want: %v",
168 denseComparison := func(a *Dense) interface{} {
170 ans := make([][]float64, r)
172 ans[i] = make([]float64, c)
173 for j := range ans[i] {
174 ans[i][j] = a.At(i, j)
180 f := func(a Matrix) interface{} {
182 ans := make([][]float64, r)
184 ans[i] = Row(nil, i, a)
188 testOneInputFunc(t, "Row", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
190 f = func(a Matrix) interface{} {
192 ans := make([][]float64, r)
194 ans[i] = make([]float64, c)
199 testOneInputFunc(t, "Row", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
202 func TestCond(t *testing.T) {
203 for i, test := range []struct {
210 a: NewDense(3, 3, []float64{
216 condTwo: 4.330127018922192,
220 a: NewDense(4, 4, []float64{
226 condOne: 1 / 0.024740155174938,
227 condTwo: 34.521576567075087,
228 condInf: 1 / 0.012034465570035,
231 a: NewDense(3, 3, []float64{
235 condOne: 30.769230769230749,
236 condTwo: 21.662689498448440,
237 condInf: 31.153846153846136,
240 orig := DenseCopyOf(test.a)
241 condOne := Cond(test.a, 1)
242 if !floats.EqualWithinAbsOrRel(test.condOne, condOne, 1e-13, 1e-13) {
243 t.Errorf("Case %d: one norm mismatch. Want %v, got %v", i, test.condOne, condOne)
245 if !Equal(test.a, orig) {
246 t.Errorf("Case %d: unexpected mutation of input matrix for one norm. Want %v, got %v", i, orig, test.a)
248 condTwo := Cond(test.a, 2)
249 if !floats.EqualWithinAbsOrRel(test.condTwo, condTwo, 1e-13, 1e-13) {
250 t.Errorf("Case %d: two norm mismatch. Want %v, got %v", i, test.condTwo, condTwo)
252 if !Equal(test.a, orig) {
253 t.Errorf("Case %d: unexpected mutation of input matrix for two norm. Want %v, got %v", i, orig, test.a)
255 condInf := Cond(test.a, math.Inf(1))
256 if !floats.EqualWithinAbsOrRel(test.condInf, condInf, 1e-13, 1e-13) {
257 t.Errorf("Case %d: inf norm mismatch. Want %v, got %v", i, test.condInf, condInf)
259 if !Equal(test.a, orig) {
260 t.Errorf("Case %d: unexpected mutation of input matrix for inf norm. Want %v, got %v", i, orig, test.a)
264 for _, test := range []struct {
281 f := func(a Matrix) interface{} {
282 return Cond(a, test.norm)
284 denseComparison := func(a *Dense) interface{} {
285 return Cond(a, test.norm)
287 testOneInputFunc(t, test.name, f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
291 func TestDet(t *testing.T) {
292 for c, test := range []struct {
297 a: NewDense(2, 2, []float64{1, 0, 0, 1}),
301 a: NewDense(2, 2, []float64{1, 0, 0, -1}),
305 a: NewDense(3, 3, []float64{
313 a: NewDense(3, 3, []float64{
321 a := DenseCopyOf(test.a)
323 if !Equal(a, test.a) {
324 t.Errorf("Input matrix changed during Det. Case %d.", c)
326 if !floats.EqualWithinAbsOrRel(det, test.ans, 1e-14, 1e-14) {
327 t.Errorf("Det mismatch case %d. Got %v, want %v", c, det, test.ans)
330 // Perform the normal list test to ensure it works for all types.
331 f := func(a Matrix) interface{} {
334 denseComparison := func(a *Dense) interface{} {
337 testOneInputFunc(t, "Det", f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isSquare)
339 // Check that it gives approximately the same answer as Cholesky
340 // Ensure the input matrices are wider than tall so they are full rank
341 isWide := func(ar, ac int) bool {
344 f = func(a Matrix) interface{} {
353 denseComparison = func(a *Dense) interface{} {
361 ok := chol.Factorize(&tmp)
363 panic("bad chol test")
367 testOneInputFunc(t, "DetVsChol", f, denseComparison, sameAnswerFloatApproxTol(1e-10), isAnyType, isWide)
370 type basicVector struct {
374 func (v *basicVector) AtVec(i int) float64 {
375 if i < 0 || i >= v.Len() {
381 func (v *basicVector) At(r, c int) float64 {
388 func (v *basicVector) Dims() (r, c int) {
392 func (v *basicVector) T() Matrix {
396 func (v *basicVector) Len() int {
400 func TestDot(t *testing.T) {
401 f := func(a, b Matrix) interface{} {
402 return Dot(a.(Vector), b.(Vector))
404 denseComparison := func(a, b *Dense) interface{} {
407 if ra != rb || ca != cb {
411 for i := 0; i < ra; i++ {
412 for j := 0; j < ca; j++ {
413 sum += a.At(i, j) * b.At(i, j)
418 testTwoInputFunc(t, "Dot", f, denseComparison, sameAnswerFloatApproxTol(1e-12), legalTypesVectorVector, legalSizeSameVec)
421 func TestEqual(t *testing.T) {
422 f := func(a, b Matrix) interface{} {
425 denseComparison := func(a, b *Dense) interface{} {
428 testTwoInputFunc(t, "Equal", f, denseComparison, sameAnswerBool, legalTypesAll, isAnySize2)
431 func TestMax(t *testing.T) {
432 // A direct test of Max with *Dense arguments is in TestNewDense.
433 f := func(a Matrix) interface{} {
436 denseComparison := func(a *Dense) interface{} {
439 testOneInputFunc(t, "Max", f, denseComparison, sameAnswerFloat, isAnyType, isAnySize)
442 func TestMin(t *testing.T) {
443 // A direct test of Min with *Dense arguments is in TestNewDense.
444 f := func(a Matrix) interface{} {
447 denseComparison := func(a *Dense) interface{} {
450 testOneInputFunc(t, "Min", f, denseComparison, sameAnswerFloat, isAnyType, isAnySize)
453 func TestNorm(t *testing.T) {
454 for i, test := range []struct {
460 a: [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
465 a: [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
467 norm: 25.495097567963924,
470 a: [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
475 a: [][]float64{{1, -2, -2}, {-4, 5, 6}},
480 a: [][]float64{{1, -2, -2}, {-4, 5, 6}},
485 a := NewDense(flatten(test.a))
486 if math.Abs(Norm(a, test.ord)-test.norm) > 1e-14 {
487 t.Errorf("Mismatch test %d: %v norm = %f", i, test.a, test.norm)
491 for _, test := range []struct {
497 {"NormInf", math.Inf(1)},
499 f := func(a Matrix) interface{} {
500 return Norm(a, test.norm)
502 denseComparison := func(a *Dense) interface{} {
503 return Norm(a, test.norm)
505 testOneInputFunc(t, test.name, f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
509 func TestNormZero(t *testing.T) {
510 for _, a := range []Matrix{
513 &SymDense{mat: blas64.Symmetric{Uplo: blas.Upper}},
515 &TriDense{mat: blas64.Triangular{Uplo: blas.Upper, Diag: blas.NonUnit}},
518 for _, norm := range []float64{1, 2, math.Inf(1)} {
519 panicked, message := panics(func() { Norm(a, norm) })
521 t.Errorf("expected panic for Norm(&%T{}, %v)", a, norm)
523 if message != ErrShape.Error() {
524 t.Errorf("unexpected panic string for Norm(&%T{}, %v): got:%s want:%s",
525 a, norm, message, ErrShape.Error())
531 func TestSum(t *testing.T) {
532 f := func(a Matrix) interface{} {
535 denseComparison := func(a *Dense) interface{} {
538 testOneInputFunc(t, "Sum", f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
541 func TestTrace(t *testing.T) {
542 for _, test := range []struct {
547 a: NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}),
551 trace := Trace(test.a)
552 if trace != test.trace {
553 t.Errorf("Trace mismatch. Want %v, got %v", test.trace, trace)
556 f := func(a Matrix) interface{} {
559 denseComparison := func(a *Dense) interface{} {
562 testOneInputFunc(t, "Trace", f, denseComparison, sameAnswerFloat, isAnyType, isSquare)
565 func TestDoer(t *testing.T) {
566 type MatrixDoer interface {
572 ones := func(n int) []float64 {
573 data := make([]float64, n)
574 for i := range data {
579 for i, m := range []MatrixDoer{
580 NewTriDense(3, Lower, ones(3*3)),
581 NewTriDense(3, Upper, ones(3*3)),
582 NewBandDense(6, 6, 1, 1, ones(3*6)),
583 NewBandDense(6, 10, 1, 1, ones(3*6)),
584 NewBandDense(10, 6, 1, 1, ones(7*3)),
585 NewSymBandDense(3, 0, ones(3)),
586 NewSymBandDense(3, 1, ones(3*(1+1))),
587 NewSymBandDense(6, 1, ones(6*(1+1))),
588 NewSymBandDense(6, 2, ones(6*(2+1))),
594 // got and fn sum the accessed elements in
595 // the Doer that is being operated on.
596 // fn also tests that the accessed elements
597 // are within the writable areas of the
598 // matrix to check that only valid elements
601 fn := func(i, j int, v float64) {
603 switch m := m.(type) {
604 case MutableTriangular:
608 case MutableSymBanded:
609 m.SetSymBand(i, j, v)
611 panic("bad test: need mutable type")
615 panicked, message := panics(func() { m.DoNonZero(fn) })
617 t.Errorf("unexpected panic for Doer test %d: %q", i, message)
621 t.Errorf("unexpected Doer sum: got:%f want:%f", got, want)
624 // Reset got for testing with DoRowNonZero.
626 panicked, message = panics(func() {
627 for i := 0; i < r; i++ {
628 m.DoRowNonZero(i, fn)
632 t.Errorf("unexpected panic for RowDoer test %d: %q", i, message)
636 t.Errorf("unexpected RowDoer sum: got:%f want:%f", got, want)
639 // Reset got for testing with DoColNonZero.
641 panicked, message = panics(func() {
642 for j := 0; j < c; j++ {
643 m.DoColNonZero(j, fn)
647 t.Errorf("unexpected panic for ColDoer test %d: %q", i, message)
651 t.Errorf("unexpected ColDoer sum: got:%f want:%f", got, want)