| // Copyright ©2018 The Gonum Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package mat |
| |
| import ( |
| "math" |
| "reflect" |
| "testing" |
| |
| "golang.org/x/exp/rand" |
| |
| "gonum.org/v1/gonum/blas/blas64" |
| ) |
| |
| func TestNewDiagDense(t *testing.T) { |
| t.Parallel() |
| for i, test := range []struct { |
| data []float64 |
| n int |
| mat *DiagDense |
| dense *Dense |
| }{ |
| { |
| data: []float64{1, 2, 3, 4, 5, 6}, |
| n: 6, |
| mat: &DiagDense{ |
| mat: blas64.Vector{N: 6, Inc: 1, Data: []float64{1, 2, 3, 4, 5, 6}}, |
| }, |
| dense: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| } { |
| band := NewDiagDense(test.n, test.data) |
| rows, cols := band.Dims() |
| |
| if rows != test.n { |
| t.Errorf("unexpected number of rows for test %d: got: %d want: %d", i, rows, test.n) |
| } |
| if cols != test.n { |
| t.Errorf("unexpected number of cols for test %d: got: %d want: %d", i, cols, test.n) |
| } |
| if !reflect.DeepEqual(band, test.mat) { |
| t.Errorf("unexpected value via reflect for test %d: got: %v want: %v", i, band, test.mat) |
| } |
| if !Equal(band, test.mat) { |
| t.Errorf("unexpected value via mat.Equal for test %d: got: %v want: %v", i, band, test.mat) |
| } |
| if !Equal(band, test.dense) { |
| t.Errorf("unexpected value via mat.Equal(band, dense) for test %d:\ngot:\n% v\nwant:\n% v", i, Formatted(band), Formatted(test.dense)) |
| } |
| } |
| } |
| |
| func TestDiagDenseZero(t *testing.T) { |
| t.Parallel() |
| // Elements that equal 1 should be set to zero, elements that equal -1 |
| // should remain unchanged. |
| for _, test := range []*DiagDense{ |
| { |
| mat: blas64.Vector{ |
| N: 5, |
| Inc: 2, |
| Data: []float64{ |
| 1, -1, |
| 1, -1, |
| 1, -1, |
| 1, -1, |
| 1, |
| }, |
| }, |
| }, |
| } { |
| dataCopy := make([]float64, len(test.mat.Data)) |
| copy(dataCopy, test.mat.Data) |
| test.Zero() |
| for i, v := range test.mat.Data { |
| if dataCopy[i] != -1 && v != 0 { |
| t.Errorf("Matrix not zeroed in bounds") |
| } |
| if dataCopy[i] == -1 && v != -1 { |
| t.Errorf("Matrix zeroed out of bounds") |
| } |
| } |
| } |
| } |
| |
| func TestDiagonalStride(t *testing.T) { |
| t.Parallel() |
| for _, test := range []struct { |
| diag *DiagDense |
| dense *Dense |
| }{ |
| { |
| diag: &DiagDense{ |
| mat: blas64.Vector{N: 6, Inc: 1, Data: []float64{1, 2, 3, 4, 5, 6}}, |
| }, |
| dense: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| diag: &DiagDense{ |
| mat: blas64.Vector{N: 6, Inc: 2, Data: []float64{ |
| 1, 0, |
| 2, 0, |
| 3, 0, |
| 4, 0, |
| 5, 0, |
| 6, |
| }}, |
| }, |
| dense: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| diag: &DiagDense{ |
| mat: blas64.Vector{N: 6, Inc: 5, Data: []float64{ |
| 1, 0, 0, 0, 0, |
| 2, 0, 0, 0, 0, |
| 3, 0, 0, 0, 0, |
| 4, 0, 0, 0, 0, |
| 5, 0, 0, 0, 0, |
| 6, |
| }}, |
| }, |
| dense: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| } { |
| if !Equal(test.diag, test.dense) { |
| t.Errorf("unexpected value via mat.Equal for stride %d: got: %v want: %v", |
| test.diag.mat.Inc, test.diag, test.dense) |
| } |
| } |
| } |
| |
| func TestDiagFrom(t *testing.T) { |
| t.Parallel() |
| for i, test := range []struct { |
| mat Matrix |
| want *Dense |
| }{ |
| { |
| mat: NewDiagDense(6, []float64{1, 2, 3, 4, 5, 6}), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewBandDense(6, 6, 1, 1, []float64{ |
| math.NaN(), 1, math.NaN(), |
| math.NaN(), 2, math.NaN(), |
| math.NaN(), 3, math.NaN(), |
| math.NaN(), 4, math.NaN(), |
| math.NaN(), 5, math.NaN(), |
| math.NaN(), 6, math.NaN(), |
| }), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewDense(6, 6, []float64{ |
| 1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6, |
| }), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewDense(6, 4, []float64{ |
| 1, math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), 2, math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), 3, math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), 4, |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| }), |
| want: NewDense(4, 4, []float64{ |
| 1, 0, 0, 0, |
| 0, 2, 0, 0, |
| 0, 0, 3, 0, |
| 0, 0, 0, 4, |
| }), |
| }, |
| { |
| mat: NewDense(4, 6, []float64{ |
| 1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(), |
| }), |
| want: NewDense(4, 4, []float64{ |
| 1, 0, 0, 0, |
| 0, 2, 0, 0, |
| 0, 0, 3, 0, |
| 0, 0, 0, 4, |
| }), |
| }, |
| { |
| mat: NewSymBandDense(6, 1, []float64{ |
| 1, math.NaN(), |
| 2, math.NaN(), |
| 3, math.NaN(), |
| 4, math.NaN(), |
| 5, math.NaN(), |
| 6, math.NaN(), |
| }), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewSymDense(6, []float64{ |
| 1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6, |
| }), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewTriBandDense(6, 2, Upper, []float64{ |
| 1, math.NaN(), math.NaN(), |
| 2, math.NaN(), math.NaN(), |
| 3, math.NaN(), math.NaN(), |
| 4, math.NaN(), math.NaN(), |
| 5, math.NaN(), math.NaN(), |
| 6, math.NaN(), math.NaN(), |
| }), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewTriBandDense(6, 2, Lower, []float64{ |
| math.NaN(), math.NaN(), 1, |
| math.NaN(), math.NaN(), 2, |
| math.NaN(), math.NaN(), 3, |
| math.NaN(), math.NaN(), 4, |
| math.NaN(), math.NaN(), 5, |
| math.NaN(), math.NaN(), 6, |
| }), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewTriDense(6, Upper, []float64{ |
| 1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6, |
| }), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewTriDense(6, Lower, []float64{ |
| 1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6, |
| }), |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| { |
| mat: NewVecDense(6, []float64{1, 2, 3, 4, 5, 6}), |
| want: NewDense(1, 1, []float64{1}), |
| }, |
| { |
| mat: &basicMatrix{ |
| mat: blas64.General{ |
| Rows: 6, |
| Cols: 6, |
| Stride: 6, |
| Data: []float64{ |
| 1, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), 2, math.NaN(), math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), 3, math.NaN(), math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), 4, math.NaN(), math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), 5, math.NaN(), |
| math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6, |
| }, |
| }, |
| capRows: 6, |
| capCols: 6, |
| }, |
| want: NewDense(6, 6, []float64{ |
| 1, 0, 0, 0, 0, 0, |
| 0, 2, 0, 0, 0, 0, |
| 0, 0, 3, 0, 0, 0, |
| 0, 0, 0, 4, 0, 0, |
| 0, 0, 0, 0, 5, 0, |
| 0, 0, 0, 0, 0, 6, |
| }), |
| }, |
| } { |
| var got DiagDense |
| got.DiagFrom(test.mat) |
| if !Equal(&got, test.want) { |
| r, c := test.mat.Dims() |
| t.Errorf("unexpected value via mat.Equal for %d×%d %T test %d:\ngot:\n% v\nwant:\n% v", |
| r, c, test.mat, i, Formatted(&got), Formatted(test.want)) |
| } |
| } |
| } |
| |
| // diagDenseViewer takes the view of the Diagonal with the underlying Diagonal |
| // as the DiagDense type. |
| type diagDenseViewer interface { |
| Matrix |
| DiagView() Diagonal |
| } |
| |
| func testDiagView(t *testing.T, cas int, test diagDenseViewer) { |
| // Check the DiagView matches the Diagonal. |
| r, c := test.Dims() |
| diagView := test.DiagView() |
| for i := 0; i < min(r, c); i++ { |
| if diagView.At(i, i) != test.At(i, i) { |
| t.Errorf("Diag mismatch case %d, element %d", cas, i) |
| } |
| } |
| |
| // Check that changes to the diagonal are reflected. |
| offset := 10.0 |
| diag := diagView.(*DiagDense) |
| for i := 0; i < min(r, c); i++ { |
| v := test.At(i, i) |
| diag.SetDiag(i, v+offset) |
| if test.At(i, i) != v+offset { |
| t.Errorf("Diag set mismatch case %d, element %d", cas, i) |
| } |
| } |
| |
| // Check that DiagView and DiagFrom match. |
| var diag2 DiagDense |
| diag2.DiagFrom(test) |
| if !Equal(diag, &diag2) { |
| t.Errorf("Cas %d: DiagView and DiagFrom mismatch", cas) |
| } |
| } |
| |
| func TestDiagonalAtSet(t *testing.T) { |
| t.Parallel() |
| for _, n := range []int{1, 3, 8} { |
| for _, nilstart := range []bool{true, false} { |
| var diag *DiagDense |
| if nilstart { |
| diag = NewDiagDense(n, nil) |
| } else { |
| data := make([]float64, n) |
| diag = NewDiagDense(n, data) |
| // Test the data is used. |
| for i := range data { |
| data[i] = -float64(i) - 1 |
| v := diag.At(i, i) |
| if v != data[i] { |
| t.Errorf("Diag shadow mismatch. Got %v, want %v", v, data[i]) |
| } |
| } |
| } |
| for i := 0; i < n; i++ { |
| for j := 0; j < n; j++ { |
| if i != j { |
| if diag.At(i, j) != 0 { |
| t.Errorf("Diag returned non-zero off diagonal element at %d, %d", i, j) |
| } |
| } |
| v := float64(i) + 1 |
| diag.SetDiag(i, v) |
| v2 := diag.At(i, i) |
| if v2 != v { |
| t.Errorf("Diag at/set mismatch. Got %v, want %v", v, v2) |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| func randDiagDense(size int, rnd *rand.Rand) *DiagDense { |
| t := NewDiagDense(size, nil) |
| for i := 0; i < size; i++ { |
| t.SetDiag(i, rnd.Float64()) |
| } |
| return t |
| } |