OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / gonum.org / v1 / gonum / mat / matrix_test.go
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.
4
5 package mat
6
7 import (
8         "fmt"
9         "math"
10         "reflect"
11         "testing"
12
13         "gonum.org/v1/gonum/blas"
14         "gonum.org/v1/gonum/blas/blas64"
15         "gonum.org/v1/gonum/floats"
16 )
17
18 func panics(fn func()) (panicked bool, message string) {
19         defer func() {
20                 r := recover()
21                 panicked = r != nil
22                 message = fmt.Sprint(r)
23         }()
24         fn()
25         return
26 }
27
28 func flatten(f [][]float64) (r, c int, d []float64) {
29         r = len(f)
30         if r == 0 {
31                 panic("bad test: no row")
32         }
33         c = len(f[0])
34         d = make([]float64, 0, r*c)
35         for _, row := range f {
36                 if len(row) != c {
37                         panic("bad test: ragged input")
38                 }
39                 d = append(d, row...)
40         }
41         return r, c, d
42 }
43
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]
48         }
49         return m
50 }
51
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 {
56                 d[i] = 1
57         }
58         return NewDense(n, n, d)
59 }
60
61 func TestCol(t *testing.T) {
62         for id, af := range [][][]float64{
63                 {
64                         {1, 2, 3},
65                         {4, 5, 6},
66                         {7, 8, 9},
67                 },
68                 {
69                         {1, 2, 3},
70                         {4, 5, 6},
71                         {7, 8, 9},
72                         {10, 11, 12},
73                 },
74                 {
75                         {1, 2, 3, 4},
76                         {5, 6, 7, 8},
77                         {9, 10, 11, 12},
78                 },
79         } {
80                 a := NewDense(flatten(af))
81                 col := make([]float64, a.mat.Rows)
82                 for j := range af[0] {
83                         for i := range col {
84                                 col[i] = float64(i*a.mat.Cols + j + 1)
85                         }
86
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",
89                                         id, j, got, col)
90                         }
91
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",
95                                         id, j, got, col)
96                         }
97                 }
98         }
99
100         denseComparison := func(a *Dense) interface{} {
101                 r, c := a.Dims()
102                 ans := make([][]float64, c)
103                 for j := range ans {
104                         ans[j] = make([]float64, r)
105                         for i := range ans[j] {
106                                 ans[j][i] = a.At(i, j)
107                         }
108                 }
109                 return ans
110         }
111
112         f := func(a Matrix) interface{} {
113                 _, c := a.Dims()
114                 ans := make([][]float64, c)
115                 for j := range ans {
116                         ans[j] = Col(nil, j, a)
117                 }
118                 return ans
119         }
120         testOneInputFunc(t, "Col", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
121
122         f = func(a Matrix) interface{} {
123                 r, c := a.Dims()
124                 ans := make([][]float64, c)
125                 for j := range ans {
126                         ans[j] = make([]float64, r)
127                         Col(ans[j], j, a)
128                 }
129                 return ans
130         }
131         testOneInputFunc(t, "Col", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
132 }
133
134 func TestRow(t *testing.T) {
135         for id, af := range [][][]float64{
136                 {
137                         {1, 2, 3},
138                         {4, 5, 6},
139                         {7, 8, 9},
140                 },
141                 {
142                         {1, 2, 3},
143                         {4, 5, 6},
144                         {7, 8, 9},
145                         {10, 11, 12},
146                 },
147                 {
148                         {1, 2, 3, 4},
149                         {5, 6, 7, 8},
150                         {9, 10, 11, 12},
151                 },
152         } {
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",
157                                         id, i, got, row)
158                         }
159
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",
163                                         id, i, got, row)
164                         }
165                 }
166         }
167
168         denseComparison := func(a *Dense) interface{} {
169                 r, c := a.Dims()
170                 ans := make([][]float64, r)
171                 for i := range ans {
172                         ans[i] = make([]float64, c)
173                         for j := range ans[i] {
174                                 ans[i][j] = a.At(i, j)
175                         }
176                 }
177                 return ans
178         }
179
180         f := func(a Matrix) interface{} {
181                 r, _ := a.Dims()
182                 ans := make([][]float64, r)
183                 for i := range ans {
184                         ans[i] = Row(nil, i, a)
185                 }
186                 return ans
187         }
188         testOneInputFunc(t, "Row", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
189
190         f = func(a Matrix) interface{} {
191                 r, c := a.Dims()
192                 ans := make([][]float64, r)
193                 for i := range ans {
194                         ans[i] = make([]float64, c)
195                         Row(ans[i], i, a)
196                 }
197                 return ans
198         }
199         testOneInputFunc(t, "Row", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
200 }
201
202 func TestCond(t *testing.T) {
203         for i, test := range []struct {
204                 a       *Dense
205                 condOne float64
206                 condTwo float64
207                 condInf float64
208         }{
209                 {
210                         a: NewDense(3, 3, []float64{
211                                 8, 1, 6,
212                                 3, 5, 7,
213                                 4, 9, 2,
214                         }),
215                         condOne: 16.0 / 3.0,
216                         condTwo: 4.330127018922192,
217                         condInf: 16.0 / 3.0,
218                 },
219                 {
220                         a: NewDense(4, 4, []float64{
221                                 2, 9, 3, 2,
222                                 10, 9, 9, 3,
223                                 1, 1, 5, 2,
224                                 8, 4, 10, 2,
225                         }),
226                         condOne: 1 / 0.024740155174938,
227                         condTwo: 34.521576567075087,
228                         condInf: 1 / 0.012034465570035,
229                 },
230                 {
231                         a: NewDense(3, 3, []float64{
232                                 5, 6, 7,
233                                 8, -2, 1,
234                                 7, 7, 7}),
235                         condOne: 30.769230769230749,
236                         condTwo: 21.662689498448440,
237                         condInf: 31.153846153846136,
238                 },
239         } {
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)
244                 }
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)
247                 }
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)
251                 }
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)
254                 }
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)
258                 }
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)
261                 }
262         }
263
264         for _, test := range []struct {
265                 name string
266                 norm float64
267         }{
268                 {
269                         name: "CondOne",
270                         norm: 1,
271                 },
272                 {
273                         name: "CondTwo",
274                         norm: 2,
275                 },
276                 {
277                         name: "CondInf",
278                         norm: math.Inf(1),
279                 },
280         } {
281                 f := func(a Matrix) interface{} {
282                         return Cond(a, test.norm)
283                 }
284                 denseComparison := func(a *Dense) interface{} {
285                         return Cond(a, test.norm)
286                 }
287                 testOneInputFunc(t, test.name, f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
288         }
289 }
290
291 func TestDet(t *testing.T) {
292         for c, test := range []struct {
293                 a   *Dense
294                 ans float64
295         }{
296                 {
297                         a:   NewDense(2, 2, []float64{1, 0, 0, 1}),
298                         ans: 1,
299                 },
300                 {
301                         a:   NewDense(2, 2, []float64{1, 0, 0, -1}),
302                         ans: -1,
303                 },
304                 {
305                         a: NewDense(3, 3, []float64{
306                                 1, 2, 0,
307                                 0, 1, 2,
308                                 0, 2, 1,
309                         }),
310                         ans: -3,
311                 },
312                 {
313                         a: NewDense(3, 3, []float64{
314                                 1, 2, 3,
315                                 5, 7, 9,
316                                 6, 9, 12,
317                         }),
318                         ans: 0,
319                 },
320         } {
321                 a := DenseCopyOf(test.a)
322                 det := Det(a)
323                 if !Equal(a, test.a) {
324                         t.Errorf("Input matrix changed during Det. Case %d.", c)
325                 }
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)
328                 }
329         }
330         // Perform the normal list test to ensure it works for all types.
331         f := func(a Matrix) interface{} {
332                 return Det(a)
333         }
334         denseComparison := func(a *Dense) interface{} {
335                 return Det(a)
336         }
337         testOneInputFunc(t, "Det", f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isSquare)
338
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 {
342                 return ar <= ac
343         }
344         f = func(a Matrix) interface{} {
345                 ar, ac := a.Dims()
346                 if !isWide(ar, ac) {
347                         panic(ErrShape)
348                 }
349                 var tmp Dense
350                 tmp.Mul(a, a.T())
351                 return Det(&tmp)
352         }
353         denseComparison = func(a *Dense) interface{} {
354                 ar, ac := a.Dims()
355                 if !isWide(ar, ac) {
356                         panic(ErrShape)
357                 }
358                 var tmp SymDense
359                 tmp.SymOuterK(1, a)
360                 var chol Cholesky
361                 ok := chol.Factorize(&tmp)
362                 if !ok {
363                         panic("bad chol test")
364                 }
365                 return chol.Det()
366         }
367         testOneInputFunc(t, "DetVsChol", f, denseComparison, sameAnswerFloatApproxTol(1e-10), isAnyType, isWide)
368 }
369
370 type basicVector struct {
371         m []float64
372 }
373
374 func (v *basicVector) AtVec(i int) float64 {
375         if i < 0 || i >= v.Len() {
376                 panic(ErrRowAccess)
377         }
378         return v.m[i]
379 }
380
381 func (v *basicVector) At(r, c int) float64 {
382         if c != 0 {
383                 panic(ErrColAccess)
384         }
385         return v.AtVec(r)
386 }
387
388 func (v *basicVector) Dims() (r, c int) {
389         return v.Len(), 1
390 }
391
392 func (v *basicVector) T() Matrix {
393         return Transpose{v}
394 }
395
396 func (v *basicVector) Len() int {
397         return len(v.m)
398 }
399
400 func TestDot(t *testing.T) {
401         f := func(a, b Matrix) interface{} {
402                 return Dot(a.(Vector), b.(Vector))
403         }
404         denseComparison := func(a, b *Dense) interface{} {
405                 ra, ca := a.Dims()
406                 rb, cb := b.Dims()
407                 if ra != rb || ca != cb {
408                         panic(ErrShape)
409                 }
410                 var sum float64
411                 for i := 0; i < ra; i++ {
412                         for j := 0; j < ca; j++ {
413                                 sum += a.At(i, j) * b.At(i, j)
414                         }
415                 }
416                 return sum
417         }
418         testTwoInputFunc(t, "Dot", f, denseComparison, sameAnswerFloatApproxTol(1e-12), legalTypesVectorVector, legalSizeSameVec)
419 }
420
421 func TestEqual(t *testing.T) {
422         f := func(a, b Matrix) interface{} {
423                 return Equal(a, b)
424         }
425         denseComparison := func(a, b *Dense) interface{} {
426                 return Equal(a, b)
427         }
428         testTwoInputFunc(t, "Equal", f, denseComparison, sameAnswerBool, legalTypesAll, isAnySize2)
429 }
430
431 func TestMax(t *testing.T) {
432         // A direct test of Max with *Dense arguments is in TestNewDense.
433         f := func(a Matrix) interface{} {
434                 return Max(a)
435         }
436         denseComparison := func(a *Dense) interface{} {
437                 return Max(a)
438         }
439         testOneInputFunc(t, "Max", f, denseComparison, sameAnswerFloat, isAnyType, isAnySize)
440 }
441
442 func TestMin(t *testing.T) {
443         // A direct test of Min with *Dense arguments is in TestNewDense.
444         f := func(a Matrix) interface{} {
445                 return Min(a)
446         }
447         denseComparison := func(a *Dense) interface{} {
448                 return Min(a)
449         }
450         testOneInputFunc(t, "Min", f, denseComparison, sameAnswerFloat, isAnyType, isAnySize)
451 }
452
453 func TestNorm(t *testing.T) {
454         for i, test := range []struct {
455                 a    [][]float64
456                 ord  float64
457                 norm float64
458         }{
459                 {
460                         a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
461                         ord:  1,
462                         norm: 30,
463                 },
464                 {
465                         a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
466                         ord:  2,
467                         norm: 25.495097567963924,
468                 },
469                 {
470                         a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
471                         ord:  math.Inf(1),
472                         norm: 33,
473                 },
474                 {
475                         a:    [][]float64{{1, -2, -2}, {-4, 5, 6}},
476                         ord:  1,
477                         norm: 8,
478                 },
479                 {
480                         a:    [][]float64{{1, -2, -2}, {-4, 5, 6}},
481                         ord:  math.Inf(1),
482                         norm: 15,
483                 },
484         } {
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)
488                 }
489         }
490
491         for _, test := range []struct {
492                 name string
493                 norm float64
494         }{
495                 {"NormOne", 1},
496                 {"NormTwo", 2},
497                 {"NormInf", math.Inf(1)},
498         } {
499                 f := func(a Matrix) interface{} {
500                         return Norm(a, test.norm)
501                 }
502                 denseComparison := func(a *Dense) interface{} {
503                         return Norm(a, test.norm)
504                 }
505                 testOneInputFunc(t, test.name, f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
506         }
507 }
508
509 func TestNormZero(t *testing.T) {
510         for _, a := range []Matrix{
511                 &Dense{},
512                 &SymDense{},
513                 &SymDense{mat: blas64.Symmetric{Uplo: blas.Upper}},
514                 &TriDense{},
515                 &TriDense{mat: blas64.Triangular{Uplo: blas.Upper, Diag: blas.NonUnit}},
516                 &VecDense{},
517         } {
518                 for _, norm := range []float64{1, 2, math.Inf(1)} {
519                         panicked, message := panics(func() { Norm(a, norm) })
520                         if !panicked {
521                                 t.Errorf("expected panic for Norm(&%T{}, %v)", a, norm)
522                         }
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())
526                         }
527                 }
528         }
529 }
530
531 func TestSum(t *testing.T) {
532         f := func(a Matrix) interface{} {
533                 return Sum(a)
534         }
535         denseComparison := func(a *Dense) interface{} {
536                 return Sum(a)
537         }
538         testOneInputFunc(t, "Sum", f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
539 }
540
541 func TestTrace(t *testing.T) {
542         for _, test := range []struct {
543                 a     *Dense
544                 trace float64
545         }{
546                 {
547                         a:     NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}),
548                         trace: 15,
549                 },
550         } {
551                 trace := Trace(test.a)
552                 if trace != test.trace {
553                         t.Errorf("Trace mismatch. Want %v, got %v", test.trace, trace)
554                 }
555         }
556         f := func(a Matrix) interface{} {
557                 return Trace(a)
558         }
559         denseComparison := func(a *Dense) interface{} {
560                 return Trace(a)
561         }
562         testOneInputFunc(t, "Trace", f, denseComparison, sameAnswerFloat, isAnyType, isSquare)
563 }
564
565 func TestDoer(t *testing.T) {
566         type MatrixDoer interface {
567                 Matrix
568                 NonZeroDoer
569                 RowNonZeroDoer
570                 ColNonZeroDoer
571         }
572         ones := func(n int) []float64 {
573                 data := make([]float64, n)
574                 for i := range data {
575                         data[i] = 1
576                 }
577                 return data
578         }
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))),
589         } {
590                 r, c := m.Dims()
591
592                 want := Sum(m)
593
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
599                 // are operated on.
600                 var got float64
601                 fn := func(i, j int, v float64) {
602                         got += v
603                         switch m := m.(type) {
604                         case MutableTriangular:
605                                 m.SetTri(i, j, v)
606                         case MutableBanded:
607                                 m.SetBand(i, j, v)
608                         case MutableSymBanded:
609                                 m.SetSymBand(i, j, v)
610                         default:
611                                 panic("bad test: need mutable type")
612                         }
613                 }
614
615                 panicked, message := panics(func() { m.DoNonZero(fn) })
616                 if panicked {
617                         t.Errorf("unexpected panic for Doer test %d: %q", i, message)
618                         continue
619                 }
620                 if got != want {
621                         t.Errorf("unexpected Doer sum: got:%f want:%f", got, want)
622                 }
623
624                 // Reset got for testing with DoRowNonZero.
625                 got = 0
626                 panicked, message = panics(func() {
627                         for i := 0; i < r; i++ {
628                                 m.DoRowNonZero(i, fn)
629                         }
630                 })
631                 if panicked {
632                         t.Errorf("unexpected panic for RowDoer test %d: %q", i, message)
633                         continue
634                 }
635                 if got != want {
636                         t.Errorf("unexpected RowDoer sum: got:%f want:%f", got, want)
637                 }
638
639                 // Reset got for testing with DoColNonZero.
640                 got = 0
641                 panicked, message = panics(func() {
642                         for j := 0; j < c; j++ {
643                                 m.DoColNonZero(j, fn)
644                         }
645                 })
646                 if panicked {
647                         t.Errorf("unexpected panic for ColDoer test %d: %q", i, message)
648                         continue
649                 }
650                 if got != want {
651                         t.Errorf("unexpected ColDoer sum: got:%f want:%f", got, want)
652                 }
653         }
654 }