gonum.org/v1/gonum@v0.14.0/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  	"gonum.org/v1/gonum/blas"
    15  	"gonum.org/v1/gonum/blas/blas64"
    16  	"gonum.org/v1/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 != ErrZeroLength.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, sameAnswerFloatApproxTol(1e-15), 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  		NewTriBandDense(3, 0, Upper, ones(3)),
   616  		NewTriBandDense(3, 1, Upper, ones(3*(1+1))),
   617  		NewTriBandDense(6, 1, Upper, ones(6*(1+1))),
   618  		NewTriBandDense(6, 2, Upper, ones(6*(2+1))),
   619  		NewTriBandDense(3, 0, Lower, ones(3)),
   620  		NewTriBandDense(3, 1, Lower, ones(3*(1+1))),
   621  		NewTriBandDense(6, 1, Lower, ones(6*(1+1))),
   622  		NewTriBandDense(6, 2, Lower, ones(6*(2+1))),
   623  		NewTridiag(1, nil, ones(1), nil),
   624  		NewTridiag(2, ones(1), ones(2), ones(1)),
   625  		NewTridiag(3, ones(2), ones(3), ones(2)),
   626  		NewTridiag(4, ones(3), ones(4), ones(3)),
   627  		NewTridiag(7, ones(6), ones(7), ones(6)),
   628  		NewTridiag(10, ones(9), ones(10), ones(9)),
   629  	} {
   630  		r, c := m.Dims()
   631  
   632  		want := Sum(m)
   633  
   634  		// got and fn sum the accessed elements in
   635  		// the Doer that is being operated on.
   636  		// fn also tests that the accessed elements
   637  		// are within the writable areas of the
   638  		// matrix to check that only valid elements
   639  		// are operated on.
   640  		var got float64
   641  		fn := func(i, j int, v float64) {
   642  			got += v
   643  			switch m := m.(type) {
   644  			case MutableTriangular:
   645  				m.SetTri(i, j, v)
   646  			case MutableBanded:
   647  				m.SetBand(i, j, v)
   648  			case MutableSymBanded:
   649  				m.SetSymBand(i, j, v)
   650  			case MutableTriBanded:
   651  				m.SetTriBand(i, j, v)
   652  			default:
   653  				panic("bad test: need mutable type")
   654  			}
   655  		}
   656  
   657  		panicked, message := panics(func() { m.DoNonZero(fn) })
   658  		if panicked {
   659  			t.Errorf("unexpected panic for Doer test %d: %q", i, message)
   660  			continue
   661  		}
   662  		if got != want {
   663  			t.Errorf("unexpected Doer sum: got:%f want:%f", got, want)
   664  		}
   665  
   666  		// Reset got for testing with DoRowNonZero.
   667  		got = 0
   668  		panicked, message = panics(func() {
   669  			for i := 0; i < r; i++ {
   670  				m.DoRowNonZero(i, fn)
   671  			}
   672  		})
   673  		if panicked {
   674  			t.Errorf("unexpected panic for RowDoer test %d: %q", i, message)
   675  			continue
   676  		}
   677  		if got != want {
   678  			t.Errorf("unexpected RowDoer sum: got:%f want:%f", got, want)
   679  		}
   680  
   681  		// Reset got for testing with DoColNonZero.
   682  		got = 0
   683  		panicked, message = panics(func() {
   684  			for j := 0; j < c; j++ {
   685  				m.DoColNonZero(j, fn)
   686  			}
   687  		})
   688  		if panicked {
   689  			t.Errorf("unexpected panic for ColDoer test %d: %q", i, message)
   690  			continue
   691  		}
   692  		if got != want {
   693  			t.Errorf("unexpected ColDoer sum: got:%f want:%f", got, want)
   694  		}
   695  	}
   696  }
   697  
   698  func TestMulVecToer(t *testing.T) {
   699  	t.Parallel()
   700  	const tol = 1e-14
   701  
   702  	rnd := rand.New(rand.NewSource(1))
   703  	random := func(n int) []float64 {
   704  		d := make([]float64, n)
   705  		for i := range d {
   706  			d[i] = rnd.NormFloat64()
   707  		}
   708  		return d
   709  	}
   710  
   711  	type mulVecToer interface {
   712  		Matrix
   713  		MulVecTo(*VecDense, bool, Vector)
   714  	}
   715  	for _, a := range []mulVecToer{
   716  		NewBandDense(1, 1, 0, 0, random(1)),
   717  		NewBandDense(3, 1, 0, 0, random(1)),
   718  		NewBandDense(3, 1, 1, 0, random(4)),
   719  		NewBandDense(1, 3, 0, 0, random(1)),
   720  		NewBandDense(1, 3, 0, 1, random(2)),
   721  		NewBandDense(7, 10, 0, 0, random(7)),
   722  		NewBandDense(7, 10, 2, 3, random(42)),
   723  		NewBandDense(10, 7, 0, 0, random(7)),
   724  		NewBandDense(10, 7, 2, 3, random(54)),
   725  		NewBandDense(10, 10, 0, 0, random(10)),
   726  		NewBandDense(10, 10, 2, 3, random(60)),
   727  		NewSymBandDense(1, 0, random(1)),
   728  		NewSymBandDense(3, 0, random(3)),
   729  		NewSymBandDense(3, 1, random(6)),
   730  		NewSymBandDense(10, 0, random(10)),
   731  		NewSymBandDense(10, 1, random(20)),
   732  		NewSymBandDense(10, 4, random(50)),
   733  		NewTridiag(1, nil, random(1), nil),
   734  		NewTridiag(2, random(1), random(2), random(1)),
   735  		NewTridiag(3, random(2), random(3), random(2)),
   736  		NewTridiag(4, random(3), random(4), random(3)),
   737  		NewTridiag(7, random(6), random(7), random(6)),
   738  		NewTridiag(10, random(9), random(10), random(9)),
   739  	} {
   740  		// Dense copy of A used for computing the expected result.
   741  		var aDense Dense
   742  		aDense.CloneFrom(a)
   743  
   744  		r, c := a.Dims()
   745  		for _, trans := range []bool{false, true} {
   746  			m, n := r, c
   747  			if trans {
   748  				m, n = c, r
   749  			}
   750  			for _, dst := range []*VecDense{
   751  				new(VecDense),
   752  				NewVecDense(m, random(m)),
   753  			} {
   754  				for xType := 0; xType <= 3; xType++ {
   755  					var x Vector
   756  					switch xType {
   757  					case 0:
   758  						x = NewVecDense(n, random(n))
   759  					case 1:
   760  						if m != n {
   761  							continue
   762  						}
   763  						x = dst
   764  					case 2:
   765  						x = &rawVector{asBasicVector(NewVecDense(n, random(n)))}
   766  					case 3:
   767  						x = asBasicVector(NewVecDense(n, random(n)))
   768  					default:
   769  						panic("bad xType")
   770  					}
   771  
   772  					var want VecDense
   773  					if !trans {
   774  						want.MulVec(&aDense, x)
   775  					} else {
   776  						want.MulVec(aDense.T(), x)
   777  					}
   778  
   779  					a.MulVecTo(dst, trans, x)
   780  
   781  					var diff VecDense
   782  					diff.SubVec(dst, &want)
   783  					if resid := Norm(&diff, 1); resid > tol*float64(m) {
   784  						t.Errorf("r=%d,c=%d,trans=%t,xType=%d: unexpected result; resid=%v, want<=%v",
   785  							r, c, trans, xType, resid, tol*float64(m))
   786  					}
   787  				}
   788  			}
   789  		}
   790  	}
   791  }