gonum.org/v1/gonum@v0.14.0/mat/band_test.go (about) 1 // Copyright ©2017 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 "reflect" 9 "testing" 10 11 "gonum.org/v1/gonum/blas/blas64" 12 ) 13 14 func TestNewBand(t *testing.T) { 15 t.Parallel() 16 for i, test := range []struct { 17 data []float64 18 r, c int 19 kl, ku int 20 mat *BandDense 21 dense *Dense 22 }{ 23 { 24 data: []float64{ 25 -1, 1, 2, 3, 26 4, 5, 6, 7, 27 8, 9, 10, 11, 28 12, 13, 14, 15, 29 16, 17, 18, -1, 30 19, 20, -1, -1, 31 }, 32 r: 6, c: 6, 33 kl: 1, ku: 2, 34 mat: &BandDense{ 35 mat: blas64.Band{ 36 Rows: 6, 37 Cols: 6, 38 KL: 1, 39 KU: 2, 40 Stride: 4, 41 Data: []float64{ 42 -1, 1, 2, 3, 43 4, 5, 6, 7, 44 8, 9, 10, 11, 45 12, 13, 14, 15, 46 16, 17, 18, -1, 47 19, 20, -1, -1, 48 }, 49 }, 50 }, 51 dense: NewDense(6, 6, []float64{ 52 1, 2, 3, 0, 0, 0, 53 4, 5, 6, 7, 0, 0, 54 0, 8, 9, 10, 11, 0, 55 0, 0, 12, 13, 14, 15, 56 0, 0, 0, 16, 17, 18, 57 0, 0, 0, 0, 19, 20, 58 }), 59 }, 60 { 61 data: []float64{ 62 -1, 1, 2, 3, 63 4, 5, 6, 7, 64 8, 9, 10, 11, 65 12, 13, 14, 15, 66 16, 17, 18, -1, 67 19, 20, -1, -1, 68 21, -1, -1, -1, 69 }, 70 r: 10, c: 6, 71 kl: 1, ku: 2, 72 mat: &BandDense{ 73 mat: blas64.Band{ 74 Rows: 10, 75 Cols: 6, 76 KL: 1, 77 KU: 2, 78 Stride: 4, 79 Data: []float64{ 80 -1, 1, 2, 3, 81 4, 5, 6, 7, 82 8, 9, 10, 11, 83 12, 13, 14, 15, 84 16, 17, 18, -1, 85 19, 20, -1, -1, 86 21, -1, -1, -1, 87 }, 88 }, 89 }, 90 dense: NewDense(10, 6, []float64{ 91 1, 2, 3, 0, 0, 0, 92 4, 5, 6, 7, 0, 0, 93 0, 8, 9, 10, 11, 0, 94 0, 0, 12, 13, 14, 15, 95 0, 0, 0, 16, 17, 18, 96 0, 0, 0, 0, 19, 20, 97 0, 0, 0, 0, 0, 21, 98 0, 0, 0, 0, 0, 0, 99 0, 0, 0, 0, 0, 0, 100 0, 0, 0, 0, 0, 0, 101 }), 102 }, 103 { 104 data: []float64{ 105 -1, 1, 2, 3, 106 4, 5, 6, 7, 107 8, 9, 10, 11, 108 12, 13, 14, 15, 109 16, 17, 18, 19, 110 20, 21, 22, 23, 111 }, 112 r: 6, c: 10, 113 kl: 1, ku: 2, 114 mat: &BandDense{ 115 mat: blas64.Band{ 116 Rows: 6, 117 Cols: 10, 118 KL: 1, 119 KU: 2, 120 Stride: 4, 121 Data: []float64{ 122 -1, 1, 2, 3, 123 4, 5, 6, 7, 124 8, 9, 10, 11, 125 12, 13, 14, 15, 126 16, 17, 18, 19, 127 20, 21, 22, 23, 128 }, 129 }, 130 }, 131 dense: NewDense(6, 10, []float64{ 132 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 133 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 134 0, 8, 9, 10, 11, 0, 0, 0, 0, 0, 135 0, 0, 12, 13, 14, 15, 0, 0, 0, 0, 136 0, 0, 0, 16, 17, 18, 19, 0, 0, 0, 137 0, 0, 0, 0, 20, 21, 22, 23, 0, 0, 138 }), 139 }, 140 } { 141 band := NewBandDense(test.r, test.c, test.kl, test.ku, test.data) 142 rows, cols := band.Dims() 143 144 if rows != test.r { 145 t.Errorf("unexpected number of rows for test %d: got: %d want: %d", i, rows, test.r) 146 } 147 if cols != test.c { 148 t.Errorf("unexpected number of cols for test %d: got: %d want: %d", i, cols, test.c) 149 } 150 if !reflect.DeepEqual(band, test.mat) { 151 t.Errorf("unexpected value via reflect for test %d: got: %v want: %v", i, band, test.mat) 152 } 153 if !Equal(band, test.mat) { 154 t.Errorf("unexpected value via mat.Equal for test %d: got: %v want: %v", i, band, test.mat) 155 } 156 if !Equal(band, test.dense) { 157 t.Errorf("unexpected value via mat.Equal(band, dense) for test %d:\ngot:\n% v\nwant:\n% v", i, Formatted(band), Formatted(test.dense)) 158 } 159 } 160 } 161 162 func TestNewDiagonalRect(t *testing.T) { 163 t.Parallel() 164 for i, test := range []struct { 165 data []float64 166 r, c int 167 mat *BandDense 168 dense *Dense 169 }{ 170 { 171 data: []float64{1, 2, 3, 4, 5, 6}, 172 r: 6, c: 6, 173 mat: &BandDense{ 174 mat: blas64.Band{ 175 Rows: 6, 176 Cols: 6, 177 Stride: 1, 178 Data: []float64{1, 2, 3, 4, 5, 6}, 179 }, 180 }, 181 dense: NewDense(6, 6, []float64{ 182 1, 0, 0, 0, 0, 0, 183 0, 2, 0, 0, 0, 0, 184 0, 0, 3, 0, 0, 0, 185 0, 0, 0, 4, 0, 0, 186 0, 0, 0, 0, 5, 0, 187 0, 0, 0, 0, 0, 6, 188 }), 189 }, 190 { 191 data: []float64{1, 2, 3, 4, 5, 6}, 192 r: 7, c: 6, 193 mat: &BandDense{ 194 mat: blas64.Band{ 195 Rows: 7, 196 Cols: 6, 197 Stride: 1, 198 Data: []float64{1, 2, 3, 4, 5, 6}, 199 }, 200 }, 201 dense: NewDense(7, 6, []float64{ 202 1, 0, 0, 0, 0, 0, 203 0, 2, 0, 0, 0, 0, 204 0, 0, 3, 0, 0, 0, 205 0, 0, 0, 4, 0, 0, 206 0, 0, 0, 0, 5, 0, 207 0, 0, 0, 0, 0, 6, 208 0, 0, 0, 0, 0, 0, 209 }), 210 }, 211 { 212 data: []float64{1, 2, 3, 4, 5, 6}, 213 r: 6, c: 7, 214 mat: &BandDense{ 215 mat: blas64.Band{ 216 Rows: 6, 217 Cols: 7, 218 Stride: 1, 219 Data: []float64{1, 2, 3, 4, 5, 6}, 220 }, 221 }, 222 dense: NewDense(6, 7, []float64{ 223 1, 0, 0, 0, 0, 0, 0, 224 0, 2, 0, 0, 0, 0, 0, 225 0, 0, 3, 0, 0, 0, 0, 226 0, 0, 0, 4, 0, 0, 0, 227 0, 0, 0, 0, 5, 0, 0, 228 0, 0, 0, 0, 0, 6, 0, 229 }), 230 }, 231 } { 232 band := NewDiagonalRect(test.r, test.c, test.data) 233 rows, cols := band.Dims() 234 235 if rows != test.r { 236 t.Errorf("unexpected number of rows for test %d: got: %d want: %d", i, rows, test.r) 237 } 238 if cols != test.c { 239 t.Errorf("unexpected number of cols for test %d: got: %d want: %d", i, cols, test.c) 240 } 241 if !reflect.DeepEqual(band, test.mat) { 242 t.Errorf("unexpected value via reflect for test %d: got: %v want: %v", i, band, test.mat) 243 } 244 if !Equal(band, test.mat) { 245 t.Errorf("unexpected value via mat.Equal for test %d: got: %v want: %v", i, band, test.mat) 246 } 247 if !Equal(band, test.dense) { 248 t.Errorf("unexpected value via mat.Equal(band, dense) for test %d:\ngot:\n% v\nwant:\n% v", i, Formatted(band), Formatted(test.dense)) 249 } 250 } 251 } 252 253 func TestBandDenseZero(t *testing.T) { 254 t.Parallel() 255 // Elements that equal 1 should be set to zero, elements that equal -1 256 // should remain unchanged. 257 for _, test := range []*BandDense{ 258 { 259 mat: blas64.Band{ 260 Rows: 6, 261 Cols: 7, 262 Stride: 8, 263 KL: 1, 264 KU: 2, 265 Data: []float64{ 266 -1, 1, 1, 1, -1, -1, -1, -1, 267 1, 1, 1, 1, -1, -1, -1, -1, 268 1, 1, 1, 1, -1, -1, -1, -1, 269 1, 1, 1, 1, -1, -1, -1, -1, 270 1, 1, 1, -1, -1, -1, -1, -1, 271 1, 1, -1, -1, -1, -1, -1, -1, 272 }, 273 }, 274 }, 275 { 276 mat: blas64.Band{ 277 Rows: 6, 278 Cols: 7, 279 Stride: 8, 280 KL: 2, 281 KU: 1, 282 Data: []float64{ 283 -1, -1, 1, 1, -1, -1, -1, -1, 284 -1, 1, 1, 1, -1, -1, -1, -1, 285 1, 1, 1, 1, -1, -1, -1, -1, 286 1, 1, 1, 1, -1, -1, -1, -1, 287 1, 1, 1, 1, -1, -1, -1, -1, 288 1, 1, 1, -1, -1, -1, -1, -1, 289 }, 290 }, 291 }, 292 } { 293 dataCopy := make([]float64, len(test.mat.Data)) 294 copy(dataCopy, test.mat.Data) 295 test.Zero() 296 for i, v := range test.mat.Data { 297 if dataCopy[i] != -1 && v != 0 { 298 t.Errorf("Matrix not zeroed in bounds") 299 } 300 if dataCopy[i] == -1 && v != -1 { 301 t.Errorf("Matrix zeroed out of bounds") 302 } 303 } 304 } 305 } 306 307 func TestBandDiagView(t *testing.T) { 308 t.Parallel() 309 for cas, test := range []*BandDense{ 310 NewBandDense(1, 1, 0, 0, []float64{1}), 311 NewBandDense(6, 6, 1, 2, []float64{ 312 -1, 2, 3, 4, 313 5, 6, 7, 8, 314 9, 10, 11, 12, 315 13, 14, 15, 16, 316 17, 18, 19, -1, 317 21, 22, -1, -1, 318 }), 319 NewBandDense(6, 6, 2, 1, []float64{ 320 -1, -1, 1, 2, 321 -1, 3, 4, 5, 322 6, 7, 8, 9, 323 10, 11, 12, 13, 324 14, 15, 16, 17, 325 18, 19, 20, -1, 326 }), 327 } { 328 testDiagView(t, cas, test) 329 } 330 } 331 332 func TestBandAtSet(t *testing.T) { 333 t.Parallel() 334 // 2 3 4 0 0 0 335 // 5 6 7 8 0 0 336 // 0 9 10 11 12 0 337 // 0 0 13 14 15 16 338 // 0 0 0 17 18 19 339 // 0 0 0 0 21 22 340 band := NewBandDense(6, 6, 1, 2, []float64{ 341 -1, 2, 3, 4, 342 5, 6, 7, 8, 343 9, 10, 11, 12, 344 13, 14, 15, 16, 345 17, 18, 19, -1, 346 21, 22, -1, -1, 347 }) 348 349 rows, cols := band.Dims() 350 kl, ku := band.Bandwidth() 351 352 // Explicitly test all indexes. 353 want := bandImplicit{rows, cols, kl, ku, func(i, j int) float64 { 354 return float64(i*(kl+ku) + j + kl + 1) 355 }} 356 for i := 0; i < 6; i++ { 357 for j := 0; j < 6; j++ { 358 if band.At(i, j) != want.At(i, j) { 359 t.Errorf("unexpected value for band.At(%d, %d): got:%v want:%v", i, j, band.At(i, j), want.At(i, j)) 360 } 361 } 362 } 363 // Do that same thing via a call to Equal. 364 if !Equal(band, want) { 365 t.Errorf("unexpected value via mat.Equal:\ngot:\n% v\nwant:\n% v", Formatted(band), Formatted(want)) 366 } 367 368 // Check At out of bounds 369 for _, row := range []int{-1, rows, rows + 1} { 370 panicked, message := panics(func() { band.At(row, 0) }) 371 if !panicked || message != ErrRowAccess.Error() { 372 t.Errorf("expected panic for invalid row access N=%d r=%d", rows, row) 373 } 374 } 375 for _, col := range []int{-1, cols, cols + 1} { 376 panicked, message := panics(func() { band.At(0, col) }) 377 if !panicked || message != ErrColAccess.Error() { 378 t.Errorf("expected panic for invalid column access N=%d c=%d", cols, col) 379 } 380 } 381 382 // Check Set out of bounds 383 for _, row := range []int{-1, rows, rows + 1} { 384 panicked, message := panics(func() { band.SetBand(row, 0, 1.2) }) 385 if !panicked || message != ErrRowAccess.Error() { 386 t.Errorf("expected panic for invalid row access N=%d r=%d", rows, row) 387 } 388 } 389 for _, col := range []int{-1, cols, cols + 1} { 390 panicked, message := panics(func() { band.SetBand(0, col, 1.2) }) 391 if !panicked || message != ErrColAccess.Error() { 392 t.Errorf("expected panic for invalid column access N=%d c=%d", cols, col) 393 } 394 } 395 396 for _, st := range []struct { 397 row, col int 398 }{ 399 {row: 0, col: 3}, 400 {row: 0, col: 4}, 401 {row: 0, col: 5}, 402 {row: 1, col: 4}, 403 {row: 1, col: 5}, 404 {row: 2, col: 5}, 405 {row: 2, col: 0}, 406 {row: 3, col: 1}, 407 {row: 4, col: 2}, 408 {row: 5, col: 3}, 409 } { 410 panicked, message := panics(func() { band.SetBand(st.row, st.col, 1.2) }) 411 if !panicked || message != ErrBandSet.Error() { 412 t.Errorf("expected panic for %+v %s", st, message) 413 } 414 } 415 416 for _, st := range []struct { 417 row, col int 418 orig, new float64 419 }{ 420 {row: 1, col: 2, orig: 7, new: 15}, 421 {row: 2, col: 3, orig: 11, new: 15}, 422 } { 423 if e := band.At(st.row, st.col); e != st.orig { 424 t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", st.row, st.col, e, st.orig) 425 } 426 band.SetBand(st.row, st.col, st.new) 427 if e := band.At(st.row, st.col); e != st.new { 428 t.Errorf("unexpected value for At(%d, %d) after SetBand(%[1]d, %d, %v): got: %v want: %[3]v", st.row, st.col, st.new, e) 429 } 430 } 431 } 432 433 // bandImplicit is an implicit band matrix returning val(i, j) 434 // for the value at (i, j). 435 type bandImplicit struct { 436 r, c, kl, ku int 437 val func(i, j int) float64 438 } 439 440 func (b bandImplicit) Dims() (r, c int) { 441 return b.r, b.c 442 } 443 444 func (b bandImplicit) T() Matrix { 445 return Transpose{b} 446 } 447 448 func (b bandImplicit) At(i, j int) float64 { 449 if i < 0 || b.r <= i { 450 panic("row") 451 } 452 if j < 0 || b.c <= j { 453 panic("col") 454 } 455 if j < i-b.kl || i+b.ku < j { 456 return 0 457 } 458 return b.val(i, j) 459 }