github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/mat/matrix_test.go (about)

     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  	"golang.org/x/exp/rand"
    14  	"github.com/jingcheng-WU/gonum/blas"
    15  	"github.com/jingcheng-WU/gonum/blas/blas64"
    16  	"github.com/jingcheng-WU/gonum/floats/scalar"
    17  )
    18  
    19  func panics(fn func()) (panicked bool, message string) {
    20  	defer func() {
    21  		r := recover()
    22  		panicked = r != nil
    23  		message = fmt.Sprint(r)
    24  	}()
    25  	fn()
    26  	return
    27  }
    28  
    29  func flatten(f [][]float64) (r, c int, d []float64) {
    30  	r = len(f)
    31  	if r == 0 {
    32  		panic("bad test: no row")
    33  	}
    34  	c = len(f[0])
    35  	d = make([]float64, 0, r*c)
    36  	for _, row := range f {
    37  		if len(row) != c {
    38  			panic("bad test: ragged input")
    39  		}
    40  		d = append(d, row...)
    41  	}
    42  	return r, c, d
    43  }
    44  
    45  func unflatten(r, c int, d []float64) [][]float64 {
    46  	m := make([][]float64, r)
    47  	for i := 0; i < r; i++ {
    48  		m[i] = d[i*c : (i+1)*c]
    49  	}
    50  	return m
    51  }
    52  
    53  // eye returns a new identity matrix of size n×n.
    54  func eye(n int) *Dense {
    55  	d := make([]float64, n*n)
    56  	for i := 0; i < n*n; i += n + 1 {
    57  		d[i] = 1
    58  	}
    59  	return NewDense(n, n, d)
    60  }
    61  
    62  func TestCol(t *testing.T) {
    63  	t.Parallel()
    64  	for id, af := range [][][]float64{
    65  		{
    66  			{1, 2, 3},
    67  			{4, 5, 6},
    68  			{7, 8, 9},
    69  		},
    70  		{
    71  			{1, 2, 3},
    72  			{4, 5, 6},
    73  			{7, 8, 9},
    74  			{10, 11, 12},
    75  		},
    76  		{
    77  			{1, 2, 3, 4},
    78  			{5, 6, 7, 8},
    79  			{9, 10, 11, 12},
    80  		},
    81  	} {
    82  		a := NewDense(flatten(af))
    83  		col := make([]float64, a.mat.Rows)
    84  		for j := range af[0] {
    85  			for i := range col {
    86  				col[i] = float64(i*a.mat.Cols + j + 1)
    87  			}
    88  
    89  			if got := Col(nil, j, a); !reflect.DeepEqual(got, col) {
    90  				t.Errorf("test %d: unexpected values returned for dense col %d: got: %v want: %v",
    91  					id, j, got, col)
    92  			}
    93  
    94  			got := make([]float64, a.mat.Rows)
    95  			if Col(got, j, a); !reflect.DeepEqual(got, col) {
    96  				t.Errorf("test %d: unexpected values filled for dense col %d: got: %v want: %v",
    97  					id, j, got, col)
    98  			}
    99  		}
   100  	}
   101  
   102  	denseComparison := func(a *Dense) interface{} {
   103  		r, c := a.Dims()
   104  		ans := make([][]float64, c)
   105  		for j := range ans {
   106  			ans[j] = make([]float64, r)
   107  			for i := range ans[j] {
   108  				ans[j][i] = a.At(i, j)
   109  			}
   110  		}
   111  		return ans
   112  	}
   113  
   114  	f := func(a Matrix) interface{} {
   115  		_, c := a.Dims()
   116  		ans := make([][]float64, c)
   117  		for j := range ans {
   118  			ans[j] = Col(nil, j, a)
   119  		}
   120  		return ans
   121  	}
   122  	testOneInputFunc(t, "Col", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
   123  
   124  	f = func(a Matrix) interface{} {
   125  		r, c := a.Dims()
   126  		ans := make([][]float64, c)
   127  		for j := range ans {
   128  			ans[j] = make([]float64, r)
   129  			Col(ans[j], j, a)
   130  		}
   131  		return ans
   132  	}
   133  	testOneInputFunc(t, "Col", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
   134  }
   135  
   136  func TestRow(t *testing.T) {
   137  	t.Parallel()
   138  	for id, af := range [][][]float64{
   139  		{
   140  			{1, 2, 3},
   141  			{4, 5, 6},
   142  			{7, 8, 9},
   143  		},
   144  		{
   145  			{1, 2, 3},
   146  			{4, 5, 6},
   147  			{7, 8, 9},
   148  			{10, 11, 12},
   149  		},
   150  		{
   151  			{1, 2, 3, 4},
   152  			{5, 6, 7, 8},
   153  			{9, 10, 11, 12},
   154  		},
   155  	} {
   156  		a := NewDense(flatten(af))
   157  		for i, row := range af {
   158  			if got := Row(nil, i, a); !reflect.DeepEqual(got, row) {
   159  				t.Errorf("test %d: unexpected values returned for dense row %d: got: %v want: %v",
   160  					id, i, got, row)
   161  			}
   162  
   163  			got := make([]float64, len(row))
   164  			if Row(got, i, a); !reflect.DeepEqual(got, row) {
   165  				t.Errorf("test %d: unexpected values filled for dense row %d: got: %v want: %v",
   166  					id, i, got, row)
   167  			}
   168  		}
   169  	}
   170  
   171  	denseComparison := func(a *Dense) interface{} {
   172  		r, c := a.Dims()
   173  		ans := make([][]float64, r)
   174  		for i := range ans {
   175  			ans[i] = make([]float64, c)
   176  			for j := range ans[i] {
   177  				ans[i][j] = a.At(i, j)
   178  			}
   179  		}
   180  		return ans
   181  	}
   182  
   183  	f := func(a Matrix) interface{} {
   184  		r, _ := a.Dims()
   185  		ans := make([][]float64, r)
   186  		for i := range ans {
   187  			ans[i] = Row(nil, i, a)
   188  		}
   189  		return ans
   190  	}
   191  	testOneInputFunc(t, "Row", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
   192  
   193  	f = func(a Matrix) interface{} {
   194  		r, c := a.Dims()
   195  		ans := make([][]float64, r)
   196  		for i := range ans {
   197  			ans[i] = make([]float64, c)
   198  			Row(ans[i], i, a)
   199  		}
   200  		return ans
   201  	}
   202  	testOneInputFunc(t, "Row", f, denseComparison, sameAnswerF64SliceOfSlice, isAnyType, isAnySize)
   203  }
   204  
   205  func TestCond(t *testing.T) {
   206  	t.Parallel()
   207  	for i, test := range []struct {
   208  		a       *Dense
   209  		condOne float64
   210  		condTwo float64
   211  		condInf float64
   212  	}{
   213  		{
   214  			a: NewDense(3, 3, []float64{
   215  				8, 1, 6,
   216  				3, 5, 7,
   217  				4, 9, 2,
   218  			}),
   219  			condOne: 16.0 / 3.0,
   220  			condTwo: 4.330127018922192,
   221  			condInf: 16.0 / 3.0,
   222  		},
   223  		{
   224  			a: NewDense(4, 4, []float64{
   225  				2, 9, 3, 2,
   226  				10, 9, 9, 3,
   227  				1, 1, 5, 2,
   228  				8, 4, 10, 2,
   229  			}),
   230  			condOne: 1 / 0.024740155174938,
   231  			condTwo: 34.521576567075087,
   232  			condInf: 1 / 0.012034465570035,
   233  		},
   234  		{
   235  			a: NewDense(3, 3, []float64{
   236  				5, 6, 7,
   237  				8, -2, 1,
   238  				7, 7, 7}),
   239  			condOne: 30.769230769230749,
   240  			condTwo: 21.662689498448440,
   241  			condInf: 31.153846153846136,
   242  		},
   243  	} {
   244  		orig := DenseCopyOf(test.a)
   245  		condOne := Cond(test.a, 1)
   246  		if !scalar.EqualWithinAbsOrRel(test.condOne, condOne, 1e-13, 1e-13) {
   247  			t.Errorf("Case %d: one norm mismatch. Want %v, got %v", i, test.condOne, condOne)
   248  		}
   249  		if !Equal(test.a, orig) {
   250  			t.Errorf("Case %d: unexpected mutation of input matrix for one norm. Want %v, got %v", i, orig, test.a)
   251  		}
   252  		condTwo := Cond(test.a, 2)
   253  		if !scalar.EqualWithinAbsOrRel(test.condTwo, condTwo, 1e-13, 1e-13) {
   254  			t.Errorf("Case %d: two norm mismatch. Want %v, got %v", i, test.condTwo, condTwo)
   255  		}
   256  		if !Equal(test.a, orig) {
   257  			t.Errorf("Case %d: unexpected mutation of input matrix for two norm. Want %v, got %v", i, orig, test.a)
   258  		}
   259  		condInf := Cond(test.a, math.Inf(1))
   260  		if !scalar.EqualWithinAbsOrRel(test.condInf, condInf, 1e-13, 1e-13) {
   261  			t.Errorf("Case %d: inf norm mismatch. Want %v, got %v", i, test.condInf, condInf)
   262  		}
   263  		if !Equal(test.a, orig) {
   264  			t.Errorf("Case %d: unexpected mutation of input matrix for inf norm. Want %v, got %v", i, orig, test.a)
   265  		}
   266  	}
   267  
   268  	for _, test := range []struct {
   269  		name string
   270  		norm float64
   271  	}{
   272  		{
   273  			name: "CondOne",
   274  			norm: 1,
   275  		},
   276  		{
   277  			name: "CondTwo",
   278  			norm: 2,
   279  		},
   280  		{
   281  			name: "CondInf",
   282  			norm: math.Inf(1),
   283  		},
   284  	} {
   285  		f := func(a Matrix) interface{} {
   286  			return Cond(a, test.norm)
   287  		}
   288  		denseComparison := func(a *Dense) interface{} {
   289  			return Cond(a, test.norm)
   290  		}
   291  		testOneInputFunc(t, test.name, f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
   292  	}
   293  }
   294  
   295  func TestDet(t *testing.T) {
   296  	t.Parallel()
   297  	for c, test := range []struct {
   298  		a   *Dense
   299  		ans float64
   300  	}{
   301  		{
   302  			a:   NewDense(2, 2, []float64{1, 0, 0, 1}),
   303  			ans: 1,
   304  		},
   305  		{
   306  			a:   NewDense(2, 2, []float64{1, 0, 0, -1}),
   307  			ans: -1,
   308  		},
   309  		{
   310  			a: NewDense(3, 3, []float64{
   311  				1, 2, 0,
   312  				0, 1, 2,
   313  				0, 2, 1,
   314  			}),
   315  			ans: -3,
   316  		},
   317  		{
   318  			a: NewDense(3, 3, []float64{
   319  				1, 2, 3,
   320  				5, 7, 9,
   321  				6, 9, 12,
   322  			}),
   323  			ans: 0,
   324  		},
   325  	} {
   326  		a := DenseCopyOf(test.a)
   327  		det := Det(a)
   328  		if !Equal(a, test.a) {
   329  			t.Errorf("Input matrix changed during Det. Case %d.", c)
   330  		}
   331  		if !scalar.EqualWithinAbsOrRel(det, test.ans, 1e-14, 1e-14) {
   332  			t.Errorf("Det mismatch case %d. Got %v, want %v", c, det, test.ans)
   333  		}
   334  	}
   335  	// Perform the normal list test to ensure it works for all types.
   336  	f := func(a Matrix) interface{} {
   337  		return Det(a)
   338  	}
   339  	denseComparison := func(a *Dense) interface{} {
   340  		return Det(a)
   341  	}
   342  	testOneInputFunc(t, "Det", f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isSquare)
   343  
   344  	// Check that it gives approximately the same answer as Cholesky
   345  	// Ensure the input matrices are wider than tall so they are full rank
   346  	isWide := func(ar, ac int) bool {
   347  		return ar <= ac
   348  	}
   349  	f = func(a Matrix) interface{} {
   350  		ar, ac := a.Dims()
   351  		if !isWide(ar, ac) {
   352  			panic(ErrShape)
   353  		}
   354  		var tmp Dense
   355  		tmp.Mul(a, a.T())
   356  		return Det(&tmp)
   357  	}
   358  	denseComparison = func(a *Dense) interface{} {
   359  		ar, ac := a.Dims()
   360  		if !isWide(ar, ac) {
   361  			panic(ErrShape)
   362  		}
   363  		var tmp SymDense
   364  		tmp.SymOuterK(1, a)
   365  		var chol Cholesky
   366  		ok := chol.Factorize(&tmp)
   367  		if !ok {
   368  			panic("bad chol test")
   369  		}
   370  		return chol.Det()
   371  	}
   372  	testOneInputFunc(t, "DetVsChol", f, denseComparison, sameAnswerFloatApproxTol(1e-10), isAnyType, isWide)
   373  }
   374  
   375  func TestDot(t *testing.T) {
   376  	t.Parallel()
   377  	f := func(a, b Matrix) interface{} {
   378  		return Dot(a.(Vector), b.(Vector))
   379  	}
   380  	denseComparison := func(a, b *Dense) interface{} {
   381  		ra, ca := a.Dims()
   382  		rb, cb := b.Dims()
   383  		if ra != rb || ca != cb {
   384  			panic(ErrShape)
   385  		}
   386  		var sum float64
   387  		for i := 0; i < ra; i++ {
   388  			for j := 0; j < ca; j++ {
   389  				sum += a.At(i, j) * b.At(i, j)
   390  			}
   391  		}
   392  		return sum
   393  	}
   394  	testTwoInputFunc(t, "Dot", f, denseComparison, sameAnswerFloatApproxTol(1e-12), legalTypesVectorVector, legalSizeSameVec)
   395  }
   396  
   397  func TestEqual(t *testing.T) {
   398  	t.Parallel()
   399  	f := func(a, b Matrix) interface{} {
   400  		return Equal(a, b)
   401  	}
   402  	denseComparison := func(a, b *Dense) interface{} {
   403  		return Equal(a, b)
   404  	}
   405  	testTwoInputFunc(t, "Equal", f, denseComparison, sameAnswerBool, legalTypesAll, isAnySize2)
   406  }
   407  
   408  func TestMax(t *testing.T) {
   409  	t.Parallel()
   410  	// A direct test of Max with *Dense arguments is in TestNewDense.
   411  	f := func(a Matrix) interface{} {
   412  		return Max(a)
   413  	}
   414  	denseComparison := func(a *Dense) interface{} {
   415  		return Max(a)
   416  	}
   417  	testOneInputFunc(t, "Max", f, denseComparison, sameAnswerFloat, isAnyType, isAnySize)
   418  }
   419  
   420  func TestMin(t *testing.T) {
   421  	t.Parallel()
   422  	// A direct test of Min with *Dense arguments is in TestNewDense.
   423  	f := func(a Matrix) interface{} {
   424  		return Min(a)
   425  	}
   426  	denseComparison := func(a *Dense) interface{} {
   427  		return Min(a)
   428  	}
   429  	testOneInputFunc(t, "Min", f, denseComparison, sameAnswerFloat, isAnyType, isAnySize)
   430  }
   431  
   432  func TestNorm(t *testing.T) {
   433  	t.Parallel()
   434  	for i, test := range []struct {
   435  		a    [][]float64
   436  		ord  float64
   437  		norm float64
   438  	}{
   439  		{
   440  			a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
   441  			ord:  1,
   442  			norm: 30,
   443  		},
   444  		{
   445  			a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
   446  			ord:  2,
   447  			norm: 25.495097567963924,
   448  		},
   449  		{
   450  			a:    [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}},
   451  			ord:  math.Inf(1),
   452  			norm: 33,
   453  		},
   454  		{
   455  			a:    [][]float64{{1, -2, -2}, {-4, 5, 6}},
   456  			ord:  1,
   457  			norm: 8,
   458  		},
   459  		{
   460  			a:    [][]float64{{1, -2, -2}, {-4, 5, 6}},
   461  			ord:  math.Inf(1),
   462  			norm: 15,
   463  		},
   464  	} {
   465  		a := NewDense(flatten(test.a))
   466  		if math.Abs(Norm(a, test.ord)-test.norm) > 1e-14 {
   467  			t.Errorf("Mismatch test %d: %v norm = %f", i, test.a, test.norm)
   468  		}
   469  	}
   470  
   471  	for _, test := range []struct {
   472  		name string
   473  		norm float64
   474  	}{
   475  		{"NormOne", 1},
   476  		{"NormTwo", 2},
   477  		{"NormInf", math.Inf(1)},
   478  	} {
   479  		f := func(a Matrix) interface{} {
   480  			return Norm(a, test.norm)
   481  		}
   482  		denseComparison := func(a *Dense) interface{} {
   483  			return Norm(a, test.norm)
   484  		}
   485  		testOneInputFunc(t, test.name, f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
   486  	}
   487  }
   488  
   489  func TestNormZero(t *testing.T) {
   490  	t.Parallel()
   491  	for _, a := range []Matrix{
   492  		&Dense{},
   493  		&SymDense{},
   494  		&SymDense{mat: blas64.Symmetric{Uplo: blas.Upper}},
   495  		&TriDense{},
   496  		&TriDense{mat: blas64.Triangular{Uplo: blas.Upper, Diag: blas.NonUnit}},
   497  		&VecDense{},
   498  	} {
   499  		for _, norm := range []float64{1, 2, math.Inf(1)} {
   500  			panicked, message := panics(func() { Norm(a, norm) })
   501  			if !panicked {
   502  				t.Errorf("expected panic for Norm(&%T{}, %v)", a, norm)
   503  			}
   504  			if message != ErrShape.Error() {
   505  				t.Errorf("unexpected panic string for Norm(&%T{}, %v): got:%s want:%s",
   506  					a, norm, message, ErrShape.Error())
   507  			}
   508  		}
   509  	}
   510  }
   511  
   512  func TestSum(t *testing.T) {
   513  	t.Parallel()
   514  	f := func(a Matrix) interface{} {
   515  		return Sum(a)
   516  	}
   517  	denseComparison := func(a *Dense) interface{} {
   518  		return Sum(a)
   519  	}
   520  	testOneInputFunc(t, "Sum", f, denseComparison, sameAnswerFloatApproxTol(1e-12), isAnyType, isAnySize)
   521  }
   522  
   523  func TestTrace(t *testing.T) {
   524  	t.Parallel()
   525  	for _, test := range []struct {
   526  		a     *Dense
   527  		trace float64
   528  	}{
   529  		{
   530  			a:     NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}),
   531  			trace: 15,
   532  		},
   533  	} {
   534  		trace := Trace(test.a)
   535  		if trace != test.trace {
   536  			t.Errorf("Trace mismatch. Want %v, got %v", test.trace, trace)
   537  		}
   538  	}
   539  	f := func(a Matrix) interface{} {
   540  		return Trace(a)
   541  	}
   542  	denseComparison := func(a *Dense) interface{} {
   543  		return Trace(a)
   544  	}
   545  	testOneInputFunc(t, "Trace", f, denseComparison, sameAnswerFloat, isAnyType, isSquare)
   546  }
   547  
   548  func TestTracer(t *testing.T) {
   549  	t.Parallel()
   550  	for _, test := range []struct {
   551  		a    Tracer
   552  		want float64
   553  	}{
   554  		{
   555  			a:    NewDense(3, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}),
   556  			want: 15,
   557  		},
   558  		{
   559  			a:    NewSymDense(4, []float64{1, 2, 3, 4, 0, 5, 6, 7, 0, 0, 8, 9, 0, 0, 0, 10}),
   560  			want: 24,
   561  		},
   562  		{
   563  			a:    NewBandDense(6, 6, 1, 2, []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0, 19, 20, 0, 0}),
   564  			want: 65,
   565  		},
   566  		{
   567  			a:    NewDiagDense(6, []float64{1, 2, 3, 4, 5, 6}),
   568  			want: 21,
   569  		},
   570  		{
   571  			a:    NewSymBandDense(6, 2, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 15, 0, 0}),
   572  			want: 50,
   573  		},
   574  		{
   575  			a:    NewTriBandDense(6, 2, Upper, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 15, 0, 0}),
   576  			want: 50,
   577  		},
   578  		{
   579  			a:    NewTriBandDense(6, 2, Lower, []float64{0, 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
   580  			want: 46,
   581  		},
   582  	} {
   583  		got := test.a.Trace()
   584  		if got != test.want {
   585  			t.Errorf("Trace mismatch. Want %v, got %v", test.want, got)
   586  		}
   587  	}
   588  }
   589  
   590  func TestDoer(t *testing.T) {
   591  	t.Parallel()
   592  	type MatrixDoer interface {
   593  		Matrix
   594  		NonZeroDoer
   595  		RowNonZeroDoer
   596  		ColNonZeroDoer
   597  	}
   598  	ones := func(n int) []float64 {
   599  		data := make([]float64, n)
   600  		for i := range data {
   601  			data[i] = 1
   602  		}
   603  		return data
   604  	}
   605  	for i, m := range []MatrixDoer{
   606  		NewTriDense(3, Lower, ones(3*3)),
   607  		NewTriDense(3, Upper, ones(3*3)),
   608  		NewBandDense(6, 6, 1, 1, ones(3*6)),
   609  		NewBandDense(6, 10, 1, 1, ones(3*6)),
   610  		NewBandDense(10, 6, 1, 1, ones(7*3)),
   611  		NewSymBandDense(3, 0, ones(3)),
   612  		NewSymBandDense(3, 1, ones(3*(1+1))),
   613  		NewSymBandDense(6, 1, ones(6*(1+1))),
   614  		NewSymBandDense(6, 2, ones(6*(2+1))),
   615  	} {
   616  		r, c := m.Dims()
   617  
   618  		want := Sum(m)
   619  
   620  		// got and fn sum the accessed elements in
   621  		// the Doer that is being operated on.
   622  		// fn also tests that the accessed elements
   623  		// are within the writable areas of the
   624  		// matrix to check that only valid elements
   625  		// are operated on.
   626  		var got float64
   627  		fn := func(i, j int, v float64) {
   628  			got += v
   629  			switch m := m.(type) {
   630  			case MutableTriangular:
   631  				m.SetTri(i, j, v)
   632  			case MutableBanded:
   633  				m.SetBand(i, j, v)
   634  			case MutableSymBanded:
   635  				m.SetSymBand(i, j, v)
   636  			default:
   637  				panic("bad test: need mutable type")
   638  			}
   639  		}
   640  
   641  		panicked, message := panics(func() { m.DoNonZero(fn) })
   642  		if panicked {
   643  			t.Errorf("unexpected panic for Doer test %d: %q", i, message)
   644  			continue
   645  		}
   646  		if got != want {
   647  			t.Errorf("unexpected Doer sum: got:%f want:%f", got, want)
   648  		}
   649  
   650  		// Reset got for testing with DoRowNonZero.
   651  		got = 0
   652  		panicked, message = panics(func() {
   653  			for i := 0; i < r; i++ {
   654  				m.DoRowNonZero(i, fn)
   655  			}
   656  		})
   657  		if panicked {
   658  			t.Errorf("unexpected panic for RowDoer test %d: %q", i, message)
   659  			continue
   660  		}
   661  		if got != want {
   662  			t.Errorf("unexpected RowDoer sum: got:%f want:%f", got, want)
   663  		}
   664  
   665  		// Reset got for testing with DoColNonZero.
   666  		got = 0
   667  		panicked, message = panics(func() {
   668  			for j := 0; j < c; j++ {
   669  				m.DoColNonZero(j, fn)
   670  			}
   671  		})
   672  		if panicked {
   673  			t.Errorf("unexpected panic for ColDoer test %d: %q", i, message)
   674  			continue
   675  		}
   676  		if got != want {
   677  			t.Errorf("unexpected ColDoer sum: got:%f want:%f", got, want)
   678  		}
   679  	}
   680  }
   681  
   682  func TestMulVecToer(t *testing.T) {
   683  	t.Parallel()
   684  	const tol = 1e-14
   685  
   686  	rnd := rand.New(rand.NewSource(1))
   687  	random := func(n int) []float64 {
   688  		d := make([]float64, n)
   689  		for i := range d {
   690  			d[i] = rnd.NormFloat64()
   691  		}
   692  		return d
   693  	}
   694  
   695  	type mulVecToer interface {
   696  		Matrix
   697  		MulVecTo(*VecDense, bool, Vector)
   698  	}
   699  	for _, a := range []mulVecToer{
   700  		NewBandDense(1, 1, 0, 0, random(1)),
   701  		NewBandDense(3, 1, 0, 0, random(1)),
   702  		NewBandDense(3, 1, 1, 0, random(4)),
   703  		NewBandDense(1, 3, 0, 0, random(1)),
   704  		NewBandDense(1, 3, 0, 1, random(2)),
   705  		NewBandDense(7, 10, 0, 0, random(7)),
   706  		NewBandDense(7, 10, 2, 3, random(42)),
   707  		NewBandDense(10, 7, 0, 0, random(7)),
   708  		NewBandDense(10, 7, 2, 3, random(54)),
   709  		NewBandDense(10, 10, 0, 0, random(10)),
   710  		NewBandDense(10, 10, 2, 3, random(60)),
   711  		NewSymBandDense(1, 0, random(1)),
   712  		NewSymBandDense(3, 0, random(3)),
   713  		NewSymBandDense(3, 1, random(6)),
   714  		NewSymBandDense(10, 0, random(10)),
   715  		NewSymBandDense(10, 1, random(20)),
   716  		NewSymBandDense(10, 4, random(50)),
   717  	} {
   718  		// Dense copy of A used for computing the expected result.
   719  		var aDense Dense
   720  		aDense.CloneFrom(a)
   721  
   722  		r, c := a.Dims()
   723  		for _, trans := range []bool{false, true} {
   724  			m, n := r, c
   725  			if trans {
   726  				m, n = c, r
   727  			}
   728  			for _, dst := range []*VecDense{
   729  				new(VecDense),
   730  				NewVecDense(m, random(m)),
   731  			} {
   732  				for xType := 0; xType <= 3; xType++ {
   733  					var x Vector
   734  					switch xType {
   735  					case 0:
   736  						x = NewVecDense(n, random(n))
   737  					case 1:
   738  						if m != n {
   739  							continue
   740  						}
   741  						x = dst
   742  					case 2:
   743  						x = &rawVector{asBasicVector(NewVecDense(n, random(n)))}
   744  					case 3:
   745  						x = asBasicVector(NewVecDense(n, random(n)))
   746  					default:
   747  						panic("bad xType")
   748  					}
   749  
   750  					var want VecDense
   751  					if !trans {
   752  						want.MulVec(&aDense, x)
   753  					} else {
   754  						want.MulVec(aDense.T(), x)
   755  					}
   756  
   757  					a.MulVecTo(dst, trans, x)
   758  
   759  					var diff VecDense
   760  					diff.SubVec(dst, &want)
   761  					if resid := Norm(&diff, 1); resid > tol*float64(m) {
   762  						t.Errorf("r=%d,c=%d,trans=%t,xType=%d: unexpected result; resid=%v, want<=%v",
   763  							r, c, trans, xType, resid, tol*float64(m))
   764  					}
   765  				}
   766  			}
   767  		}
   768  	}
   769  }