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

     1  // Copyright ©2018 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  	"math"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"golang.org/x/exp/rand"
    13  
    14  	"gonum.org/v1/gonum/blas/blas64"
    15  )
    16  
    17  func TestNewDiagDense(t *testing.T) {
    18  	t.Parallel()
    19  	for i, test := range []struct {
    20  		data  []float64
    21  		n     int
    22  		mat   *DiagDense
    23  		dense *Dense
    24  	}{
    25  		{
    26  			data: []float64{1, 2, 3, 4, 5, 6},
    27  			n:    6,
    28  			mat: &DiagDense{
    29  				mat: blas64.Vector{N: 6, Inc: 1, Data: []float64{1, 2, 3, 4, 5, 6}},
    30  			},
    31  			dense: NewDense(6, 6, []float64{
    32  				1, 0, 0, 0, 0, 0,
    33  				0, 2, 0, 0, 0, 0,
    34  				0, 0, 3, 0, 0, 0,
    35  				0, 0, 0, 4, 0, 0,
    36  				0, 0, 0, 0, 5, 0,
    37  				0, 0, 0, 0, 0, 6,
    38  			}),
    39  		},
    40  	} {
    41  		band := NewDiagDense(test.n, test.data)
    42  		rows, cols := band.Dims()
    43  
    44  		if rows != test.n {
    45  			t.Errorf("unexpected number of rows for test %d: got: %d want: %d", i, rows, test.n)
    46  		}
    47  		if cols != test.n {
    48  			t.Errorf("unexpected number of cols for test %d: got: %d want: %d", i, cols, test.n)
    49  		}
    50  		if !reflect.DeepEqual(band, test.mat) {
    51  			t.Errorf("unexpected value via reflect for test %d: got: %v want: %v", i, band, test.mat)
    52  		}
    53  		if !Equal(band, test.mat) {
    54  			t.Errorf("unexpected value via mat.Equal for test %d: got: %v want: %v", i, band, test.mat)
    55  		}
    56  		if !Equal(band, test.dense) {
    57  			t.Errorf("unexpected value via mat.Equal(band, dense) for test %d:\ngot:\n% v\nwant:\n% v", i, Formatted(band), Formatted(test.dense))
    58  		}
    59  	}
    60  }
    61  
    62  func TestDiagDenseZero(t *testing.T) {
    63  	t.Parallel()
    64  	// Elements that equal 1 should be set to zero, elements that equal -1
    65  	// should remain unchanged.
    66  	for _, test := range []*DiagDense{
    67  		{
    68  			mat: blas64.Vector{
    69  				N:   5,
    70  				Inc: 2,
    71  				Data: []float64{
    72  					1, -1,
    73  					1, -1,
    74  					1, -1,
    75  					1, -1,
    76  					1,
    77  				},
    78  			},
    79  		},
    80  	} {
    81  		dataCopy := make([]float64, len(test.mat.Data))
    82  		copy(dataCopy, test.mat.Data)
    83  		test.Zero()
    84  		for i, v := range test.mat.Data {
    85  			if dataCopy[i] != -1 && v != 0 {
    86  				t.Errorf("Matrix not zeroed in bounds")
    87  			}
    88  			if dataCopy[i] == -1 && v != -1 {
    89  				t.Errorf("Matrix zeroed out of bounds")
    90  			}
    91  		}
    92  	}
    93  }
    94  
    95  func TestDiagonalStride(t *testing.T) {
    96  	t.Parallel()
    97  	for _, test := range []struct {
    98  		diag  *DiagDense
    99  		dense *Dense
   100  	}{
   101  		{
   102  			diag: &DiagDense{
   103  				mat: blas64.Vector{N: 6, Inc: 1, Data: []float64{1, 2, 3, 4, 5, 6}},
   104  			},
   105  			dense: NewDense(6, 6, []float64{
   106  				1, 0, 0, 0, 0, 0,
   107  				0, 2, 0, 0, 0, 0,
   108  				0, 0, 3, 0, 0, 0,
   109  				0, 0, 0, 4, 0, 0,
   110  				0, 0, 0, 0, 5, 0,
   111  				0, 0, 0, 0, 0, 6,
   112  			}),
   113  		},
   114  		{
   115  			diag: &DiagDense{
   116  				mat: blas64.Vector{N: 6, Inc: 2, Data: []float64{
   117  					1, 0,
   118  					2, 0,
   119  					3, 0,
   120  					4, 0,
   121  					5, 0,
   122  					6,
   123  				}},
   124  			},
   125  			dense: NewDense(6, 6, []float64{
   126  				1, 0, 0, 0, 0, 0,
   127  				0, 2, 0, 0, 0, 0,
   128  				0, 0, 3, 0, 0, 0,
   129  				0, 0, 0, 4, 0, 0,
   130  				0, 0, 0, 0, 5, 0,
   131  				0, 0, 0, 0, 0, 6,
   132  			}),
   133  		},
   134  		{
   135  			diag: &DiagDense{
   136  				mat: blas64.Vector{N: 6, Inc: 5, Data: []float64{
   137  					1, 0, 0, 0, 0,
   138  					2, 0, 0, 0, 0,
   139  					3, 0, 0, 0, 0,
   140  					4, 0, 0, 0, 0,
   141  					5, 0, 0, 0, 0,
   142  					6,
   143  				}},
   144  			},
   145  			dense: NewDense(6, 6, []float64{
   146  				1, 0, 0, 0, 0, 0,
   147  				0, 2, 0, 0, 0, 0,
   148  				0, 0, 3, 0, 0, 0,
   149  				0, 0, 0, 4, 0, 0,
   150  				0, 0, 0, 0, 5, 0,
   151  				0, 0, 0, 0, 0, 6,
   152  			}),
   153  		},
   154  	} {
   155  		if !Equal(test.diag, test.dense) {
   156  			t.Errorf("unexpected value via mat.Equal for stride %d: got: %v want: %v",
   157  				test.diag.mat.Inc, test.diag, test.dense)
   158  		}
   159  	}
   160  }
   161  
   162  func TestDiagFrom(t *testing.T) {
   163  	t.Parallel()
   164  	for i, test := range []struct {
   165  		mat  Matrix
   166  		want *Dense
   167  	}{
   168  		{
   169  			mat: NewDiagDense(6, []float64{1, 2, 3, 4, 5, 6}),
   170  			want: NewDense(6, 6, []float64{
   171  				1, 0, 0, 0, 0, 0,
   172  				0, 2, 0, 0, 0, 0,
   173  				0, 0, 3, 0, 0, 0,
   174  				0, 0, 0, 4, 0, 0,
   175  				0, 0, 0, 0, 5, 0,
   176  				0, 0, 0, 0, 0, 6,
   177  			}),
   178  		},
   179  		{
   180  			mat: NewBandDense(6, 6, 1, 1, []float64{
   181  				math.NaN(), 1, math.NaN(),
   182  				math.NaN(), 2, math.NaN(),
   183  				math.NaN(), 3, math.NaN(),
   184  				math.NaN(), 4, math.NaN(),
   185  				math.NaN(), 5, math.NaN(),
   186  				math.NaN(), 6, math.NaN(),
   187  			}),
   188  			want: NewDense(6, 6, []float64{
   189  				1, 0, 0, 0, 0, 0,
   190  				0, 2, 0, 0, 0, 0,
   191  				0, 0, 3, 0, 0, 0,
   192  				0, 0, 0, 4, 0, 0,
   193  				0, 0, 0, 0, 5, 0,
   194  				0, 0, 0, 0, 0, 6,
   195  			}),
   196  		},
   197  		{
   198  			mat: NewDense(6, 6, []float64{
   199  				1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   200  				math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   201  				math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(),
   202  				math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(),
   203  				math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(),
   204  				math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6,
   205  			}),
   206  			want: NewDense(6, 6, []float64{
   207  				1, 0, 0, 0, 0, 0,
   208  				0, 2, 0, 0, 0, 0,
   209  				0, 0, 3, 0, 0, 0,
   210  				0, 0, 0, 4, 0, 0,
   211  				0, 0, 0, 0, 5, 0,
   212  				0, 0, 0, 0, 0, 6,
   213  			}),
   214  		},
   215  		{
   216  			mat: NewDense(6, 4, []float64{
   217  				1, math.NaN(), math.NaN(), math.NaN(),
   218  				math.NaN(), 2, math.NaN(), math.NaN(),
   219  				math.NaN(), math.NaN(), 3, math.NaN(),
   220  				math.NaN(), math.NaN(), math.NaN(), 4,
   221  				math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   222  				math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   223  			}),
   224  			want: NewDense(4, 4, []float64{
   225  				1, 0, 0, 0,
   226  				0, 2, 0, 0,
   227  				0, 0, 3, 0,
   228  				0, 0, 0, 4,
   229  			}),
   230  		},
   231  		{
   232  			mat: NewDense(4, 6, []float64{
   233  				1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   234  				math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   235  				math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(),
   236  				math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(),
   237  			}),
   238  			want: NewDense(4, 4, []float64{
   239  				1, 0, 0, 0,
   240  				0, 2, 0, 0,
   241  				0, 0, 3, 0,
   242  				0, 0, 0, 4,
   243  			}),
   244  		},
   245  		{
   246  			mat: NewSymBandDense(6, 1, []float64{
   247  				1, math.NaN(),
   248  				2, math.NaN(),
   249  				3, math.NaN(),
   250  				4, math.NaN(),
   251  				5, math.NaN(),
   252  				6, math.NaN(),
   253  			}),
   254  			want: NewDense(6, 6, []float64{
   255  				1, 0, 0, 0, 0, 0,
   256  				0, 2, 0, 0, 0, 0,
   257  				0, 0, 3, 0, 0, 0,
   258  				0, 0, 0, 4, 0, 0,
   259  				0, 0, 0, 0, 5, 0,
   260  				0, 0, 0, 0, 0, 6,
   261  			}),
   262  		},
   263  		{
   264  			mat: NewSymDense(6, []float64{
   265  				1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   266  				math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   267  				math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(),
   268  				math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(),
   269  				math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(),
   270  				math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6,
   271  			}),
   272  			want: NewDense(6, 6, []float64{
   273  				1, 0, 0, 0, 0, 0,
   274  				0, 2, 0, 0, 0, 0,
   275  				0, 0, 3, 0, 0, 0,
   276  				0, 0, 0, 4, 0, 0,
   277  				0, 0, 0, 0, 5, 0,
   278  				0, 0, 0, 0, 0, 6,
   279  			}),
   280  		},
   281  		{
   282  			mat: NewTriBandDense(6, 2, Upper, []float64{
   283  				1, math.NaN(), math.NaN(),
   284  				2, math.NaN(), math.NaN(),
   285  				3, math.NaN(), math.NaN(),
   286  				4, math.NaN(), math.NaN(),
   287  				5, math.NaN(), math.NaN(),
   288  				6, math.NaN(), math.NaN(),
   289  			}),
   290  			want: NewDense(6, 6, []float64{
   291  				1, 0, 0, 0, 0, 0,
   292  				0, 2, 0, 0, 0, 0,
   293  				0, 0, 3, 0, 0, 0,
   294  				0, 0, 0, 4, 0, 0,
   295  				0, 0, 0, 0, 5, 0,
   296  				0, 0, 0, 0, 0, 6,
   297  			}),
   298  		},
   299  		{
   300  			mat: NewTriBandDense(6, 2, Lower, []float64{
   301  				math.NaN(), math.NaN(), 1,
   302  				math.NaN(), math.NaN(), 2,
   303  				math.NaN(), math.NaN(), 3,
   304  				math.NaN(), math.NaN(), 4,
   305  				math.NaN(), math.NaN(), 5,
   306  				math.NaN(), math.NaN(), 6,
   307  			}),
   308  			want: NewDense(6, 6, []float64{
   309  				1, 0, 0, 0, 0, 0,
   310  				0, 2, 0, 0, 0, 0,
   311  				0, 0, 3, 0, 0, 0,
   312  				0, 0, 0, 4, 0, 0,
   313  				0, 0, 0, 0, 5, 0,
   314  				0, 0, 0, 0, 0, 6,
   315  			}),
   316  		},
   317  		{
   318  			mat: NewTriDense(6, Upper, []float64{
   319  				1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   320  				math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   321  				math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(),
   322  				math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(),
   323  				math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(),
   324  				math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6,
   325  			}),
   326  			want: NewDense(6, 6, []float64{
   327  				1, 0, 0, 0, 0, 0,
   328  				0, 2, 0, 0, 0, 0,
   329  				0, 0, 3, 0, 0, 0,
   330  				0, 0, 0, 4, 0, 0,
   331  				0, 0, 0, 0, 5, 0,
   332  				0, 0, 0, 0, 0, 6,
   333  			}),
   334  		},
   335  		{
   336  			mat: NewTriDense(6, Lower, []float64{
   337  				1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   338  				math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   339  				math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(),
   340  				math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(),
   341  				math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(),
   342  				math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6,
   343  			}),
   344  			want: NewDense(6, 6, []float64{
   345  				1, 0, 0, 0, 0, 0,
   346  				0, 2, 0, 0, 0, 0,
   347  				0, 0, 3, 0, 0, 0,
   348  				0, 0, 0, 4, 0, 0,
   349  				0, 0, 0, 0, 5, 0,
   350  				0, 0, 0, 0, 0, 6,
   351  			}),
   352  		},
   353  		{
   354  			mat:  NewVecDense(6, []float64{1, 2, 3, 4, 5, 6}),
   355  			want: NewDense(1, 1, []float64{1}),
   356  		},
   357  		{
   358  			mat: &basicMatrix{
   359  				mat: blas64.General{
   360  					Rows:   6,
   361  					Cols:   6,
   362  					Stride: 6,
   363  					Data: []float64{
   364  						1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   365  						math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(),
   366  						math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(),
   367  						math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(),
   368  						math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(),
   369  						math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6,
   370  					},
   371  				},
   372  				capRows: 6,
   373  				capCols: 6,
   374  			},
   375  			want: NewDense(6, 6, []float64{
   376  				1, 0, 0, 0, 0, 0,
   377  				0, 2, 0, 0, 0, 0,
   378  				0, 0, 3, 0, 0, 0,
   379  				0, 0, 0, 4, 0, 0,
   380  				0, 0, 0, 0, 5, 0,
   381  				0, 0, 0, 0, 0, 6,
   382  			}),
   383  		},
   384  	} {
   385  		var got DiagDense
   386  		got.DiagFrom(test.mat)
   387  		if !Equal(&got, test.want) {
   388  			r, c := test.mat.Dims()
   389  			t.Errorf("unexpected value via mat.Equal for %d×%d %T test %d:\ngot:\n% v\nwant:\n% v",
   390  				r, c, test.mat, i, Formatted(&got), Formatted(test.want))
   391  		}
   392  	}
   393  }
   394  
   395  // diagDenseViewer takes the view of the Diagonal with the underlying Diagonal
   396  // as the DiagDense type.
   397  type diagDenseViewer interface {
   398  	Matrix
   399  	DiagView() Diagonal
   400  }
   401  
   402  func testDiagView(t *testing.T, cas int, test diagDenseViewer) {
   403  	// Check the DiagView matches the Diagonal.
   404  	r, c := test.Dims()
   405  	diagView := test.DiagView()
   406  	for i := 0; i < min(r, c); i++ {
   407  		if diagView.At(i, i) != test.At(i, i) {
   408  			t.Errorf("Diag mismatch case %d, element %d", cas, i)
   409  		}
   410  	}
   411  
   412  	// Check that changes to the diagonal are reflected.
   413  	offset := 10.0
   414  	diag := diagView.(*DiagDense)
   415  	for i := 0; i < min(r, c); i++ {
   416  		v := test.At(i, i)
   417  		diag.SetDiag(i, v+offset)
   418  		if test.At(i, i) != v+offset {
   419  			t.Errorf("Diag set mismatch case %d, element %d", cas, i)
   420  		}
   421  	}
   422  
   423  	// Check that DiagView and DiagFrom match.
   424  	var diag2 DiagDense
   425  	diag2.DiagFrom(test)
   426  	if !Equal(diag, &diag2) {
   427  		t.Errorf("Cas %d: DiagView and DiagFrom mismatch", cas)
   428  	}
   429  }
   430  
   431  func TestDiagonalAtSet(t *testing.T) {
   432  	t.Parallel()
   433  	for _, n := range []int{1, 3, 8} {
   434  		for _, nilstart := range []bool{true, false} {
   435  			var diag *DiagDense
   436  			if nilstart {
   437  				diag = NewDiagDense(n, nil)
   438  			} else {
   439  				data := make([]float64, n)
   440  				diag = NewDiagDense(n, data)
   441  				// Test the data is used.
   442  				for i := range data {
   443  					data[i] = -float64(i) - 1
   444  					v := diag.At(i, i)
   445  					if v != data[i] {
   446  						t.Errorf("Diag shadow mismatch. Got %v, want %v", v, data[i])
   447  					}
   448  				}
   449  			}
   450  			for i := 0; i < n; i++ {
   451  				for j := 0; j < n; j++ {
   452  					if i != j {
   453  						if diag.At(i, j) != 0 {
   454  							t.Errorf("Diag returned non-zero off diagonal element at %d, %d", i, j)
   455  						}
   456  					}
   457  					v := float64(i) + 1
   458  					diag.SetDiag(i, v)
   459  					v2 := diag.At(i, i)
   460  					if v2 != v {
   461  						t.Errorf("Diag at/set mismatch. Got %v, want %v", v, v2)
   462  					}
   463  				}
   464  			}
   465  		}
   466  	}
   467  }
   468  
   469  func randDiagDense(size int, rnd *rand.Rand) *DiagDense {
   470  	t := NewDiagDense(size, nil)
   471  	for i := 0; i < size; i++ {
   472  		t.SetDiag(i, rnd.Float64())
   473  	}
   474  	return t
   475  }