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 }