gonum.org/v1/gonum@v0.14.0/mat/triangular_test.go (about)

     1  // Copyright ©2015 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  
    15  	"gonum.org/v1/gonum/blas"
    16  	"gonum.org/v1/gonum/blas/blas64"
    17  )
    18  
    19  func TestNewTriangular(t *testing.T) {
    20  	t.Parallel()
    21  	for i, test := range []struct {
    22  		data []float64
    23  		n    int
    24  		kind TriKind
    25  		mat  *TriDense
    26  	}{
    27  		{
    28  			data: []float64{
    29  				1, 2, 3,
    30  				4, 5, 6,
    31  				7, 8, 9,
    32  			},
    33  			n:    3,
    34  			kind: Upper,
    35  			mat: &TriDense{
    36  				mat: blas64.Triangular{
    37  					N:      3,
    38  					Stride: 3,
    39  					Uplo:   blas.Upper,
    40  					Data:   []float64{1, 2, 3, 4, 5, 6, 7, 8, 9},
    41  					Diag:   blas.NonUnit,
    42  				},
    43  				cap: 3,
    44  			},
    45  		},
    46  	} {
    47  		tri := NewTriDense(test.n, test.kind, test.data)
    48  		rows, cols := tri.Dims()
    49  
    50  		if rows != test.n {
    51  			t.Errorf("unexpected number of rows for test %d: got: %d want: %d", i, rows, test.n)
    52  		}
    53  		if cols != test.n {
    54  			t.Errorf("unexpected number of cols for test %d: got: %d want: %d", i, cols, test.n)
    55  		}
    56  		if !reflect.DeepEqual(tri, test.mat) {
    57  			t.Errorf("unexpected data slice for test %d: got: %v want: %v", i, tri, test.mat)
    58  		}
    59  	}
    60  
    61  	for _, kind := range []TriKind{Lower, Upper} {
    62  		panicked, message := panics(func() { NewTriDense(3, kind, []float64{1, 2}) })
    63  		if !panicked || message != ErrShape.Error() {
    64  			t.Errorf("expected panic for invalid data slice length for upper=%t", kind)
    65  		}
    66  	}
    67  }
    68  
    69  func TestTriAtSet(t *testing.T) {
    70  	t.Parallel()
    71  	tri := &TriDense{
    72  		mat: blas64.Triangular{
    73  			N:      3,
    74  			Stride: 3,
    75  			Uplo:   blas.Upper,
    76  			Data:   []float64{1, 2, 3, 4, 5, 6, 7, 8, 9},
    77  			Diag:   blas.NonUnit,
    78  		},
    79  		cap: 3,
    80  	}
    81  
    82  	rows, cols := tri.Dims()
    83  
    84  	// Check At out of bounds
    85  	for _, row := range []int{-1, rows, rows + 1} {
    86  		panicked, message := panics(func() { tri.At(row, 0) })
    87  		if !panicked || message != ErrRowAccess.Error() {
    88  			t.Errorf("expected panic for invalid row access N=%d r=%d", rows, row)
    89  		}
    90  	}
    91  	for _, col := range []int{-1, cols, cols + 1} {
    92  		panicked, message := panics(func() { tri.At(0, col) })
    93  		if !panicked || message != ErrColAccess.Error() {
    94  			t.Errorf("expected panic for invalid column access N=%d c=%d", cols, col)
    95  		}
    96  	}
    97  
    98  	// Check Set out of bounds
    99  	for _, row := range []int{-1, rows, rows + 1} {
   100  		panicked, message := panics(func() { tri.SetTri(row, 0, 1.2) })
   101  		if !panicked || message != ErrRowAccess.Error() {
   102  			t.Errorf("expected panic for invalid row access N=%d r=%d", rows, row)
   103  		}
   104  	}
   105  	for _, col := range []int{-1, cols, cols + 1} {
   106  		panicked, message := panics(func() { tri.SetTri(0, col, 1.2) })
   107  		if !panicked || message != ErrColAccess.Error() {
   108  			t.Errorf("expected panic for invalid column access N=%d c=%d", cols, col)
   109  		}
   110  	}
   111  
   112  	for _, st := range []struct {
   113  		row, col int
   114  		uplo     blas.Uplo
   115  	}{
   116  		{row: 2, col: 1, uplo: blas.Upper},
   117  		{row: 1, col: 2, uplo: blas.Lower},
   118  	} {
   119  		tri.mat.Uplo = st.uplo
   120  		panicked, message := panics(func() { tri.SetTri(st.row, st.col, 1.2) })
   121  		if !panicked || message != ErrTriangleSet.Error() {
   122  			t.Errorf("expected panic for %+v", st)
   123  		}
   124  	}
   125  
   126  	for _, st := range []struct {
   127  		row, col  int
   128  		uplo      blas.Uplo
   129  		orig, new float64
   130  	}{
   131  		{row: 2, col: 1, uplo: blas.Lower, orig: 8, new: 15},
   132  		{row: 1, col: 2, uplo: blas.Upper, orig: 6, new: 15},
   133  	} {
   134  		tri.mat.Uplo = st.uplo
   135  		if e := tri.At(st.row, st.col); e != st.orig {
   136  			t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", st.row, st.col, e, st.orig)
   137  		}
   138  		tri.SetTri(st.row, st.col, st.new)
   139  		if e := tri.At(st.row, st.col); e != st.new {
   140  			t.Errorf("unexpected value for At(%d, %d) after SetTri(%[1]d, %d, %v): got: %v want: %[3]v", st.row, st.col, st.new, e)
   141  		}
   142  	}
   143  }
   144  
   145  func TestTriDenseZero(t *testing.T) {
   146  	t.Parallel()
   147  	// Elements that equal 1 should be set to zero, elements that equal -1
   148  	// should remain unchanged.
   149  	for _, test := range []*TriDense{
   150  		{
   151  			mat: blas64.Triangular{
   152  				Uplo:   blas.Upper,
   153  				N:      4,
   154  				Stride: 5,
   155  				Data: []float64{
   156  					1, 1, 1, 1, -1,
   157  					-1, 1, 1, 1, -1,
   158  					-1, -1, 1, 1, -1,
   159  					-1, -1, -1, 1, -1,
   160  				},
   161  			},
   162  		},
   163  		{
   164  			mat: blas64.Triangular{
   165  				Uplo:   blas.Lower,
   166  				N:      4,
   167  				Stride: 5,
   168  				Data: []float64{
   169  					1, -1, -1, -1, -1,
   170  					1, 1, -1, -1, -1,
   171  					1, 1, 1, -1, -1,
   172  					1, 1, 1, 1, -1,
   173  				},
   174  			},
   175  		},
   176  	} {
   177  		dataCopy := make([]float64, len(test.mat.Data))
   178  		copy(dataCopy, test.mat.Data)
   179  		test.Zero()
   180  		for i, v := range test.mat.Data {
   181  			if dataCopy[i] != -1 && v != 0 {
   182  				t.Errorf("Matrix not zeroed in bounds")
   183  			}
   184  			if dataCopy[i] == -1 && v != -1 {
   185  				t.Errorf("Matrix zeroed out of bounds")
   186  			}
   187  		}
   188  	}
   189  }
   190  
   191  func TestTriDiagView(t *testing.T) {
   192  	t.Parallel()
   193  	for cas, test := range []*TriDense{
   194  		NewTriDense(1, Upper, []float64{1}),
   195  		NewTriDense(2, Upper, []float64{1, 2, 0, 3}),
   196  		NewTriDense(3, Upper, []float64{1, 2, 3, 0, 4, 5, 0, 0, 6}),
   197  		NewTriDense(1, Lower, []float64{1}),
   198  		NewTriDense(2, Lower, []float64{1, 2, 2, 3}),
   199  		NewTriDense(3, Lower, []float64{1, 0, 0, 2, 3, 0, 4, 5, 6}),
   200  	} {
   201  		testDiagView(t, cas, test)
   202  	}
   203  }
   204  
   205  func TestTriDenseCopy(t *testing.T) {
   206  	t.Parallel()
   207  	src := rand.NewSource(1)
   208  	rnd := rand.New(src)
   209  	for i := 0; i < 100; i++ {
   210  		size := rnd.Intn(100)
   211  		r, err := randDense(size, 0.9, src)
   212  		if size == 0 {
   213  			if err != ErrZeroLength {
   214  				t.Fatalf("expected error %v: got: %v", ErrZeroLength, err)
   215  			}
   216  			continue
   217  		}
   218  		if err != nil {
   219  			t.Fatalf("unexpected error: %v", err)
   220  		}
   221  
   222  		u := NewTriDense(size, true, nil)
   223  		l := NewTriDense(size, false, nil)
   224  
   225  		for _, typ := range []Matrix{r, (*basicMatrix)(r)} {
   226  			for j := range u.mat.Data {
   227  				u.mat.Data[j] = math.NaN()
   228  				l.mat.Data[j] = math.NaN()
   229  			}
   230  			u.Copy(typ)
   231  			l.Copy(typ)
   232  			for m := 0; m < size; m++ {
   233  				for n := 0; n < size; n++ {
   234  					want := typ.At(m, n)
   235  					switch {
   236  					case m < n: // Upper triangular matrix.
   237  						if got := u.At(m, n); got != want {
   238  							t.Errorf("unexpected upper value for At(%d, %d) for test %d: got: %v want: %v", m, n, i, got, want)
   239  						}
   240  					case m == n: // Diagonal matrix.
   241  						if got := u.At(m, n); got != want {
   242  							t.Errorf("unexpected upper value for At(%d, %d) for test %d: got: %v want: %v", m, n, i, got, want)
   243  						}
   244  						if got := l.At(m, n); got != want {
   245  							t.Errorf("unexpected diagonal value for At(%d, %d) for test %d: got: %v want: %v", m, n, i, got, want)
   246  						}
   247  					case m > n: // Lower triangular matrix.
   248  						if got := l.At(m, n); got != want {
   249  							t.Errorf("unexpected lower value for At(%d, %d) for test %d: got: %v want: %v", m, n, i, got, want)
   250  						}
   251  					}
   252  				}
   253  			}
   254  		}
   255  	}
   256  }
   257  
   258  func TestTriTriDenseCopy(t *testing.T) {
   259  	t.Parallel()
   260  	src := rand.NewSource(1)
   261  	rnd := rand.New(src)
   262  	for i := 0; i < 100; i++ {
   263  		size := rnd.Intn(100)
   264  		r, err := randDense(size, 1, src)
   265  		if size == 0 {
   266  			if err != ErrZeroLength {
   267  				t.Fatalf("expected error %v: got: %v", ErrZeroLength, err)
   268  			}
   269  			continue
   270  		}
   271  		if err != nil {
   272  			t.Fatalf("unexpected error: %v", err)
   273  		}
   274  
   275  		ur := NewTriDense(size, true, nil)
   276  		lr := NewTriDense(size, false, nil)
   277  
   278  		ur.Copy(r)
   279  		lr.Copy(r)
   280  
   281  		u := NewTriDense(size, true, nil)
   282  		u.Copy(ur)
   283  		if !equal(u, ur) {
   284  			t.Fatal("unexpected result for U triangle copy of U triangle: not equal")
   285  		}
   286  
   287  		l := NewTriDense(size, false, nil)
   288  		l.Copy(lr)
   289  		if !equal(l, lr) {
   290  			t.Fatal("unexpected result for L triangle copy of L triangle: not equal")
   291  		}
   292  
   293  		zero(u.mat.Data)
   294  		u.Copy(lr)
   295  		if !isDiagonal(u) {
   296  			t.Fatal("unexpected result for U triangle copy of L triangle: off diagonal non-zero element")
   297  		}
   298  		if !equalDiagonal(u, lr) {
   299  			t.Fatal("unexpected result for U triangle copy of L triangle: diagonal not equal")
   300  		}
   301  
   302  		zero(l.mat.Data)
   303  		l.Copy(ur)
   304  		if !isDiagonal(l) {
   305  			t.Fatal("unexpected result for L triangle copy of U triangle: off diagonal non-zero element")
   306  		}
   307  		if !equalDiagonal(l, ur) {
   308  			t.Fatal("unexpected result for L triangle copy of U triangle: diagonal not equal")
   309  		}
   310  	}
   311  }
   312  
   313  func TestTriInverse(t *testing.T) {
   314  	t.Parallel()
   315  	rnd := rand.New(rand.NewSource(1))
   316  	for _, kind := range []TriKind{Upper, Lower} {
   317  		for _, n := range []int{1, 3, 5, 9} {
   318  			data := make([]float64, n*n)
   319  			for i := range data {
   320  				data[i] = rnd.NormFloat64()
   321  			}
   322  			a := NewTriDense(n, kind, data)
   323  			var tr TriDense
   324  			err := tr.InverseTri(a)
   325  			if err != nil {
   326  				t.Errorf("Bad test: %s", err)
   327  			}
   328  			var d Dense
   329  			d.Mul(a, &tr)
   330  			if !equalApprox(eye(n), &d, 1e-8, false) {
   331  				var diff Dense
   332  				diff.Sub(eye(n), &d)
   333  				t.Errorf("Tri times inverse is not identity. Norm of difference: %v", Norm(&diff, 2))
   334  			}
   335  		}
   336  	}
   337  }
   338  
   339  func TestTriMul(t *testing.T) {
   340  	t.Parallel()
   341  	method := func(receiver, a, b Matrix) {
   342  		type MulTrier interface {
   343  			MulTri(a, b Triangular)
   344  		}
   345  		receiver.(MulTrier).MulTri(a.(Triangular), b.(Triangular))
   346  	}
   347  	denseComparison := func(receiver, a, b *Dense) {
   348  		receiver.Mul(a, b)
   349  	}
   350  	legalSizeTriMul := func(ar, ac, br, bc int) bool {
   351  		// Need both to be square and the sizes to be the same
   352  		return ar == ac && br == bc && ar == br
   353  	}
   354  
   355  	// The legal types are triangles with the same TriKind.
   356  	// legalTypesTri returns whether both input arguments are Triangular.
   357  	legalTypes := func(a, b Matrix) bool {
   358  		at, ok := a.(Triangular)
   359  		if !ok {
   360  			return false
   361  		}
   362  		bt, ok := b.(Triangular)
   363  		if !ok {
   364  			return false
   365  		}
   366  		_, ak := at.Triangle()
   367  		_, bk := bt.Triangle()
   368  		return ak == bk
   369  	}
   370  	legalTypesLower := func(a, b Matrix) bool {
   371  		legal := legalTypes(a, b)
   372  		if !legal {
   373  			return false
   374  		}
   375  		_, kind := a.(Triangular).Triangle()
   376  		r := kind == Lower
   377  		return r
   378  	}
   379  	receiver := NewTriDense(3, Lower, nil)
   380  	testTwoInput(t, "TriMul", receiver, method, denseComparison, legalTypesLower, legalSizeTriMul, 1e-14)
   381  
   382  	legalTypesUpper := func(a, b Matrix) bool {
   383  		legal := legalTypes(a, b)
   384  		if !legal {
   385  			return false
   386  		}
   387  		_, kind := a.(Triangular).Triangle()
   388  		r := kind == Upper
   389  		return r
   390  	}
   391  	receiver = NewTriDense(3, Upper, nil)
   392  	testTwoInput(t, "TriMul", receiver, method, denseComparison, legalTypesUpper, legalSizeTriMul, 1e-14)
   393  }
   394  
   395  func TestScaleTri(t *testing.T) {
   396  	t.Parallel()
   397  	for _, f := range []float64{0.5, 1, 3} {
   398  		method := func(receiver, a Matrix) {
   399  			type ScaleTrier interface {
   400  				ScaleTri(f float64, a Triangular)
   401  			}
   402  			rd := receiver.(ScaleTrier)
   403  			rd.ScaleTri(f, a.(Triangular))
   404  		}
   405  		denseComparison := func(receiver, a *Dense) {
   406  			receiver.Scale(f, a)
   407  		}
   408  		testOneInput(t, "ScaleTriUpper", NewTriDense(3, Upper, nil), method, denseComparison, legalTypeTriUpper, isSquare, 1e-14)
   409  		testOneInput(t, "ScaleTriLower", NewTriDense(3, Lower, nil), method, denseComparison, legalTypeTriLower, isSquare, 1e-14)
   410  	}
   411  }
   412  
   413  func TestCopySymIntoTriangle(t *testing.T) {
   414  	t.Parallel()
   415  	nan := math.NaN()
   416  	for tc, test := range []struct {
   417  		n     int
   418  		sUplo blas.Uplo
   419  		s     []float64
   420  
   421  		tUplo TriKind
   422  		want  []float64
   423  	}{
   424  		{
   425  			n:     3,
   426  			sUplo: blas.Upper,
   427  			s: []float64{
   428  				1, 2, 3,
   429  				nan, 4, 5,
   430  				nan, nan, 6,
   431  			},
   432  			tUplo: Upper,
   433  			want: []float64{
   434  				1, 2, 3,
   435  				0, 4, 5,
   436  				0, 0, 6,
   437  			},
   438  		},
   439  		{
   440  			n:     3,
   441  			sUplo: blas.Lower,
   442  			s: []float64{
   443  				1, nan, nan,
   444  				2, 3, nan,
   445  				4, 5, 6,
   446  			},
   447  			tUplo: Upper,
   448  			want: []float64{
   449  				1, 2, 4,
   450  				0, 3, 5,
   451  				0, 0, 6,
   452  			},
   453  		},
   454  		{
   455  			n:     3,
   456  			sUplo: blas.Upper,
   457  			s: []float64{
   458  				1, 2, 3,
   459  				nan, 4, 5,
   460  				nan, nan, 6,
   461  			},
   462  			tUplo: Lower,
   463  			want: []float64{
   464  				1, 0, 0,
   465  				2, 4, 0,
   466  				3, 5, 6,
   467  			},
   468  		},
   469  		{
   470  			n:     3,
   471  			sUplo: blas.Lower,
   472  			s: []float64{
   473  				1, nan, nan,
   474  				2, 3, nan,
   475  				4, 5, 6,
   476  			},
   477  			tUplo: Lower,
   478  			want: []float64{
   479  				1, 0, 0,
   480  				2, 3, 0,
   481  				4, 5, 6,
   482  			},
   483  		},
   484  	} {
   485  		n := test.n
   486  		s := NewSymDense(n, test.s)
   487  		// For the purpose of the test, break the assumption that
   488  		// symmetric is stored in the upper triangle (only when S is
   489  		// RawSymmetricer).
   490  		s.mat.Uplo = test.sUplo
   491  
   492  		t1 := NewTriDense(n, test.tUplo, nil)
   493  		copySymIntoTriangle(t1, s)
   494  
   495  		equal := true
   496  	loop1:
   497  		for i := 0; i < n; i++ {
   498  			for j := 0; j < n; j++ {
   499  				if t1.At(i, j) != test.want[i*n+j] {
   500  					equal = false
   501  					break loop1
   502  				}
   503  			}
   504  		}
   505  		if !equal {
   506  			t.Errorf("Case %v: unexpected T when S is RawSymmetricer", tc)
   507  		}
   508  
   509  		if test.sUplo == blas.Lower {
   510  			continue
   511  		}
   512  
   513  		sb := (basicSymmetric)(*s)
   514  		t2 := NewTriDense(n, test.tUplo, nil)
   515  		copySymIntoTriangle(t2, &sb)
   516  		equal = true
   517  	loop2:
   518  		for i := 0; i < n; i++ {
   519  			for j := 0; j < n; j++ {
   520  				if t1.At(i, j) != test.want[i*n+j] {
   521  					equal = false
   522  					break loop2
   523  				}
   524  			}
   525  		}
   526  		if !equal {
   527  			t.Errorf("Case %v: unexpected T when S is not RawSymmetricer", tc)
   528  		}
   529  	}
   530  }
   531  
   532  func TestTriSliceTri(t *testing.T) {
   533  	t.Parallel()
   534  	rnd := rand.New(rand.NewSource(1))
   535  	for cas, test := range []struct {
   536  		n, start1, span1, start2, span2 int
   537  	}{
   538  		{10, 0, 10, 0, 10},
   539  		{10, 0, 8, 0, 8},
   540  		{10, 2, 8, 0, 6},
   541  		{10, 2, 7, 4, 2},
   542  		{10, 2, 6, 0, 5},
   543  		{10, 2, 3, 1, 7},
   544  	} {
   545  		n := test.n
   546  		for _, kind := range []TriKind{Upper, Lower} {
   547  			tri := NewTriDense(n, kind, nil)
   548  			if kind == Upper {
   549  				for i := 0; i < n; i++ {
   550  					for j := i; j < n; j++ {
   551  						tri.SetTri(i, j, rnd.Float64())
   552  					}
   553  				}
   554  			} else {
   555  				for i := 0; i < n; i++ {
   556  					for j := 0; j <= i; j++ {
   557  						tri.SetTri(i, j, rnd.Float64())
   558  					}
   559  				}
   560  			}
   561  
   562  			start1 := test.start1
   563  			span1 := test.span1
   564  			v1 := tri.SliceTri(start1, start1+span1).(*TriDense)
   565  			if kind == Upper {
   566  				for i := 0; i < span1; i++ {
   567  					for j := i; j < span1; j++ {
   568  						if v1.At(i, j) != tri.At(start1+i, start1+j) {
   569  							t.Errorf("Case %d,upper: view mismatch at %v,%v", cas, i, j)
   570  						}
   571  					}
   572  				}
   573  			} else {
   574  				for i := 0; i < span1; i++ {
   575  					for j := 0; j <= i; j++ {
   576  						if v1.At(i, j) != tri.At(start1+i, start1+j) {
   577  							t.Errorf("Case %d,lower: view mismatch at %v,%v", cas, i, j)
   578  						}
   579  					}
   580  				}
   581  			}
   582  
   583  			start2 := test.start2
   584  			span2 := test.span2
   585  			v2 := v1.SliceTri(start2, start2+span2).(*TriDense)
   586  			if kind == Upper {
   587  				for i := 0; i < span2; i++ {
   588  					for j := i; j < span2; j++ {
   589  						if v2.At(i, j) != tri.At(start1+start2+i, start1+start2+j) {
   590  							t.Errorf("Case %d,upper: second view mismatch at %v,%v", cas, i, j)
   591  						}
   592  					}
   593  				}
   594  			} else {
   595  				for i := 0; i < span1; i++ {
   596  					for j := 0; j <= i; j++ {
   597  						if v1.At(i, j) != tri.At(start1+i, start1+j) {
   598  							t.Errorf("Case %d,lower: second view mismatch at %v,%v", cas, i, j)
   599  						}
   600  					}
   601  				}
   602  			}
   603  
   604  			v2.SetTri(span2-1, span2-1, -123.45)
   605  			if tri.At(start1+start2+span2-1, start1+start2+span2-1) != -123.45 {
   606  				t.Errorf("Case %d: write to view not reflected in original", cas)
   607  			}
   608  		}
   609  	}
   610  }
   611  
   612  var triSumForBench float64
   613  
   614  func BenchmarkTriSum(b *testing.B) {
   615  	rnd := rand.New(rand.NewSource(1))
   616  	for n := 100; n <= 1600; n *= 2 {
   617  		a := randTriDense(n, rnd)
   618  		b.Run(fmt.Sprintf("BenchmarkTriSum%d", n), func(b *testing.B) {
   619  			for i := 0; i < b.N; i++ {
   620  				triSumForBench = Sum(a)
   621  			}
   622  		})
   623  	}
   624  }
   625  
   626  var triProductForBench *TriDense
   627  
   628  func BenchmarkTriMul(b *testing.B) {
   629  	rnd := rand.New(rand.NewSource(1))
   630  	for n := 100; n <= 1600; n *= 2 {
   631  		triProductForBench = NewTriDense(n, Upper, nil)
   632  		a := randTriDense(n, rnd)
   633  		c := randTriDense(n, rnd)
   634  		b.Run(fmt.Sprintf("BenchmarkTriMul%d", n), func(b *testing.B) {
   635  			for i := 0; i < b.N; i++ {
   636  				triProductForBench.MulTri(a, c)
   637  			}
   638  		})
   639  	}
   640  }
   641  
   642  func BenchmarkTriMulDiag(b *testing.B) {
   643  	rnd := rand.New(rand.NewSource(1))
   644  	for n := 100; n <= 1600; n *= 2 {
   645  		triProductForBench = NewTriDense(n, Upper, nil)
   646  		a := randTriDense(n, rnd)
   647  		c := randDiagDense(n, rnd)
   648  		b.Run(fmt.Sprintf("BenchmarkTriMulDiag%d", n), func(b *testing.B) {
   649  			for i := 0; i < b.N; i++ {
   650  				triProductForBench.MulTri(a, c)
   651  			}
   652  		})
   653  	}
   654  }
   655  
   656  func BenchmarkTriMul2Diag(b *testing.B) {
   657  	rnd := rand.New(rand.NewSource(1))
   658  	for n := 100; n <= 1600; n *= 2 {
   659  		triProductForBench = NewTriDense(n, Upper, nil)
   660  		a := randDiagDense(n, rnd)
   661  		c := randDiagDense(n, rnd)
   662  		b.Run(fmt.Sprintf("BenchmarkTriMul2Diag%d", n), func(b *testing.B) {
   663  			for i := 0; i < b.N; i++ {
   664  				triProductForBench.MulTri(a, c)
   665  			}
   666  		})
   667  	}
   668  }
   669  
   670  func randTriDense(size int, rnd *rand.Rand) *TriDense {
   671  	t := NewTriDense(size, Upper, nil)
   672  	for i := 0; i < size; i++ {
   673  		for j := i; j < size; j++ {
   674  			t.SetTri(i, j, rnd.Float64())
   675  		}
   676  	}
   677  	return t
   678  }