mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 15:16:59 +08:00
476 lines
12 KiB
Go
476 lines
12 KiB
Go
// 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
|
||
}
|