github.com/gopherd/gonum@v0.0.4/mat/list_test.go (about) 1 // Copyright ©2015 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 //nolint:deadcode,unused 6 package mat 7 8 import ( 9 "fmt" 10 "math" 11 "reflect" 12 "testing" 13 14 "math/rand" 15 16 "github.com/gopherd/gonum/blas" 17 "github.com/gopherd/gonum/blas/blas64" 18 "github.com/gopherd/gonum/floats" 19 "github.com/gopherd/gonum/floats/scalar" 20 ) 21 22 // legalSizeSameRectangular returns whether the two matrices have the same rectangular shape. 23 func legalSizeSameRectangular(ar, ac, br, bc int) bool { 24 if ar != br { 25 return false 26 } 27 if ac != bc { 28 return false 29 } 30 return true 31 } 32 33 // legalSizeSameSquare returns whether the two matrices have the same square shape. 34 func legalSizeSameSquare(ar, ac, br, bc int) bool { 35 if ar != br { 36 return false 37 } 38 if ac != bc { 39 return false 40 } 41 if ar != ac { 42 return false 43 } 44 return true 45 } 46 47 // legalSizeSameHeight returns whether the two matrices have the same number of rows. 48 func legalSizeSameHeight(ar, _, br, _ int) bool { 49 return ar == br 50 } 51 52 // legalSizeSameWidth returns whether the two matrices have the same number of columns. 53 func legalSizeSameWidth(_, ac, _, bc int) bool { 54 return ac == bc 55 } 56 57 // legalSizeSolve returns whether the two matrices can be used in a linear solve. 58 func legalSizeSolve(ar, ac, br, bc int) bool { 59 return ar == br 60 } 61 62 // legalSizeSameVec returns whether the two matrices are column vectors. 63 func legalSizeVector(_, ac, _, bc int) bool { 64 return ac == 1 && bc == 1 65 } 66 67 // legalSizeSameVec returns whether the two matrices are column vectors of the 68 // same dimension. 69 func legalSizeSameVec(ar, ac, br, bc int) bool { 70 return ac == 1 && bc == 1 && ar == br 71 } 72 73 // isAnySize returns true for all matrix sizes. 74 func isAnySize(ar, ac int) bool { 75 return true 76 } 77 78 // isAnySize2 returns true for all matrix sizes. 79 func isAnySize2(ar, ac, br, bc int) bool { 80 return true 81 } 82 83 // isAnyColumnVector returns true for any column vector sizes. 84 func isAnyColumnVector(ar, ac int) bool { 85 return ac == 1 86 } 87 88 // isSquare returns whether the input matrix is square. 89 func isSquare(r, c int) bool { 90 return r == c 91 } 92 93 // sameAnswerFloat returns whether the two inputs are both NaN or are equal. 94 func sameAnswerFloat(a, b interface{}) bool { 95 if math.IsNaN(a.(float64)) { 96 return math.IsNaN(b.(float64)) 97 } 98 return a.(float64) == b.(float64) 99 } 100 101 // sameAnswerFloatApproxTol returns a function that determines whether its two 102 // inputs are both NaN or within tol of each other. 103 func sameAnswerFloatApproxTol(tol float64) func(a, b interface{}) bool { 104 return func(a, b interface{}) bool { 105 if math.IsNaN(a.(float64)) { 106 return math.IsNaN(b.(float64)) 107 } 108 return scalar.EqualWithinAbsOrRel(a.(float64), b.(float64), tol, tol) 109 } 110 } 111 112 func sameAnswerF64SliceOfSlice(a, b interface{}) bool { 113 for i, v := range a.([][]float64) { 114 if same := floats.Same(v, b.([][]float64)[i]); !same { 115 return false 116 } 117 } 118 return true 119 } 120 121 // sameAnswerBool returns whether the two inputs have the same value. 122 func sameAnswerBool(a, b interface{}) bool { 123 return a.(bool) == b.(bool) 124 } 125 126 // isAnyType returns true for all Matrix types. 127 func isAnyType(Matrix) bool { 128 return true 129 } 130 131 // legalTypesAll returns true for all Matrix types. 132 func legalTypesAll(a, b Matrix) bool { 133 return true 134 } 135 136 // legalTypeSym returns whether a is a Symmetric. 137 func legalTypeSym(a Matrix) bool { 138 _, ok := a.(Symmetric) 139 return ok 140 } 141 142 // legalTypeTri returns whether a is a Triangular. 143 func legalTypeTri(a Matrix) bool { 144 _, ok := a.(Triangular) 145 return ok 146 } 147 148 // legalTypeTriLower returns whether a is a Triangular with kind == Lower. 149 func legalTypeTriLower(a Matrix) bool { 150 t, ok := a.(Triangular) 151 if !ok { 152 return false 153 } 154 _, kind := t.Triangle() 155 return kind == Lower 156 } 157 158 // legalTypeTriUpper returns whether a is a Triangular with kind == Upper. 159 func legalTypeTriUpper(a Matrix) bool { 160 t, ok := a.(Triangular) 161 if !ok { 162 return false 163 } 164 _, kind := t.Triangle() 165 return kind == Upper 166 } 167 168 // legalTypesSym returns whether both input arguments are Symmetric. 169 func legalTypesSym(a, b Matrix) bool { 170 if _, ok := a.(Symmetric); !ok { 171 return false 172 } 173 if _, ok := b.(Symmetric); !ok { 174 return false 175 } 176 return true 177 } 178 179 // legalTypeVector returns whether v is a Vector. 180 func legalTypeVector(v Matrix) bool { 181 _, ok := v.(Vector) 182 return ok 183 } 184 185 // legalTypeVec returns whether v is a *VecDense. 186 func legalTypeVecDense(v Matrix) bool { 187 _, ok := v.(*VecDense) 188 return ok 189 } 190 191 // legalTypesVectorVector returns whether both inputs are Vector 192 func legalTypesVectorVector(a, b Matrix) bool { 193 if _, ok := a.(Vector); !ok { 194 return false 195 } 196 if _, ok := b.(Vector); !ok { 197 return false 198 } 199 return true 200 } 201 202 // legalTypesVecDenseVecDense returns whether both inputs are *VecDense. 203 func legalTypesVecDenseVecDense(a, b Matrix) bool { 204 if _, ok := a.(*VecDense); !ok { 205 return false 206 } 207 if _, ok := b.(*VecDense); !ok { 208 return false 209 } 210 return true 211 } 212 213 // legalTypesMatrixVector returns whether the first input is an arbitrary Matrix 214 // and the second input is a Vector. 215 func legalTypesMatrixVector(a, b Matrix) bool { 216 _, ok := b.(Vector) 217 return ok 218 } 219 220 // legalTypesMatrixVecDense returns whether the first input is an arbitrary Matrix 221 // and the second input is a *VecDense. 222 func legalTypesMatrixVecDense(a, b Matrix) bool { 223 _, ok := b.(*VecDense) 224 return ok 225 } 226 227 // legalDims returns whether {m,n} is a valid dimension of the given matrix type. 228 func legalDims(a Matrix, m, n int) bool { 229 switch t := a.(type) { 230 default: 231 panic("legal dims type not coded") 232 case Untransposer: 233 return legalDims(t.Untranspose(), n, m) 234 case *Dense, *basicMatrix, *BandDense, *basicBanded: 235 if m < 0 || n < 0 { 236 return false 237 } 238 return true 239 case *SymDense, *TriDense, *basicSymmetric, *basicTriangular, 240 *SymBandDense, *basicSymBanded, *TriBandDense, *basicTriBanded, 241 *basicDiagonal, *DiagDense, *Tridiag: 242 if m < 0 || n < 0 || m != n { 243 return false 244 } 245 return true 246 case *VecDense, *basicVector: 247 if m < 0 || n < 0 { 248 return false 249 } 250 return n == 1 251 } 252 } 253 254 // returnAs returns the matrix a with the type of t. Used for making a concrete 255 // type and changing to the basic form. 256 func returnAs(a, t Matrix) Matrix { 257 switch mat := a.(type) { 258 default: 259 panic("unknown type for a") 260 case *Dense: 261 switch t.(type) { 262 default: 263 panic("bad type") 264 case *Dense: 265 return mat 266 case *basicMatrix: 267 return asBasicMatrix(mat) 268 } 269 case *SymDense: 270 switch t.(type) { 271 default: 272 panic("bad type") 273 case *SymDense: 274 return mat 275 case *basicSymmetric: 276 return asBasicSymmetric(mat) 277 } 278 case *TriDense: 279 switch t.(type) { 280 default: 281 panic("bad type") 282 case *TriDense: 283 return mat 284 case *basicTriangular: 285 return asBasicTriangular(mat) 286 } 287 case *BandDense: 288 switch t.(type) { 289 default: 290 panic("bad type") 291 case *BandDense: 292 return mat 293 case *basicBanded: 294 return asBasicBanded(mat) 295 } 296 case *SymBandDense: 297 switch t.(type) { 298 default: 299 panic("bad type") 300 case *SymBandDense: 301 return mat 302 case *basicSymBanded: 303 return asBasicSymBanded(mat) 304 } 305 case *TriBandDense: 306 switch t.(type) { 307 default: 308 panic("bad type") 309 case *TriBandDense: 310 return mat 311 case *basicTriBanded: 312 return asBasicTriBanded(mat) 313 } 314 case *DiagDense: 315 switch t.(type) { 316 default: 317 panic("bad type") 318 case *DiagDense: 319 return mat 320 case *basicDiagonal: 321 return asBasicDiagonal(mat) 322 } 323 case *Tridiag: 324 switch t.(type) { 325 default: 326 panic("bad type") 327 case *Tridiag: 328 return mat 329 } 330 } 331 } 332 333 // retranspose returns the matrix m inside an Untransposer of the type 334 // of a. 335 func retranspose(a, m Matrix) Matrix { 336 switch a.(type) { 337 case TransposeTriBand: 338 return TransposeTriBand{m.(TriBanded)} 339 case TransposeBand: 340 return TransposeBand{m.(Banded)} 341 case TransposeTri: 342 return TransposeTri{m.(Triangular)} 343 case Transpose: 344 return Transpose{m} 345 case Untransposer: 346 panic("unknown transposer type") 347 default: 348 panic("a is not an untransposer") 349 } 350 } 351 352 // makeRandOf returns a new randomly filled m×n matrix of the underlying matrix type. 353 func makeRandOf(a Matrix, m, n int, src rand.Source) Matrix { 354 rnd := rand.New(src) 355 var rMatrix Matrix 356 switch t := a.(type) { 357 default: 358 panic("unknown type for make rand of") 359 case Untransposer: 360 rMatrix = retranspose(a, makeRandOf(t.Untranspose(), n, m, src)) 361 case *Dense, *basicMatrix: 362 var mat = &Dense{} 363 if m != 0 && n != 0 { 364 mat = NewDense(m, n, nil) 365 } 366 for i := 0; i < m; i++ { 367 for j := 0; j < n; j++ { 368 mat.Set(i, j, rnd.NormFloat64()) 369 } 370 } 371 rMatrix = returnAs(mat, t) 372 case *VecDense: 373 if m == 0 && n == 0 { 374 return &VecDense{} 375 } 376 if n != 1 { 377 panic(fmt.Sprintf("bad vector size: m = %v, n = %v", m, n)) 378 } 379 length := m 380 inc := 1 381 if t.mat.Inc != 0 { 382 inc = t.mat.Inc 383 } 384 mat := &VecDense{ 385 mat: blas64.Vector{ 386 N: length, 387 Inc: inc, 388 Data: make([]float64, inc*(length-1)+1), 389 }, 390 } 391 for i := 0; i < length; i++ { 392 mat.SetVec(i, rnd.NormFloat64()) 393 } 394 return mat 395 case *basicVector: 396 if n != 1 { 397 panic(fmt.Sprintf("bad vector size: m = %v, n = %v", m, n)) 398 } 399 if m == 0 { 400 return &basicVector{} 401 } 402 mat := NewVecDense(m, nil) 403 for i := 0; i < m; i++ { 404 mat.SetVec(i, rnd.NormFloat64()) 405 } 406 return asBasicVector(mat) 407 case *SymDense, *basicSymmetric: 408 if m != n { 409 panic("bad size") 410 } 411 mat := &SymDense{} 412 if n != 0 { 413 mat = NewSymDense(n, nil) 414 } 415 for i := 0; i < m; i++ { 416 for j := i; j < n; j++ { 417 mat.SetSym(i, j, rnd.NormFloat64()) 418 } 419 } 420 rMatrix = returnAs(mat, t) 421 case *TriDense, *basicTriangular: 422 if m != n { 423 panic("bad size") 424 } 425 426 // This is necessary because we are making 427 // a triangle from the zero value, which 428 // always returns upper as true. 429 var triKind TriKind 430 switch t := t.(type) { 431 case *TriDense: 432 triKind = t.triKind() 433 case *basicTriangular: 434 triKind = (*TriDense)(t).triKind() 435 } 436 437 if n == 0 { 438 uplo := blas.Upper 439 if triKind == Lower { 440 uplo = blas.Lower 441 } 442 return returnAs(&TriDense{mat: blas64.Triangular{Uplo: uplo}}, t) 443 } 444 445 mat := NewTriDense(n, triKind, nil) 446 if triKind == Upper { 447 for i := 0; i < m; i++ { 448 for j := i; j < n; j++ { 449 mat.SetTri(i, j, rnd.NormFloat64()) 450 } 451 } 452 } else { 453 for i := 0; i < m; i++ { 454 for j := 0; j <= i; j++ { 455 mat.SetTri(i, j, rnd.NormFloat64()) 456 } 457 } 458 } 459 rMatrix = returnAs(mat, t) 460 case *BandDense, *basicBanded: 461 var kl, ku int 462 switch t := t.(type) { 463 case *BandDense: 464 kl = t.mat.KL 465 ku = t.mat.KU 466 case *basicBanded: 467 ku = (*BandDense)(t).mat.KU 468 kl = (*BandDense)(t).mat.KL 469 } 470 ku = min(ku, n-1) 471 kl = min(kl, m-1) 472 data := make([]float64, min(m, n+kl)*(kl+ku+1)) 473 for i := range data { 474 data[i] = rnd.NormFloat64() 475 } 476 mat := NewBandDense(m, n, kl, ku, data) 477 rMatrix = returnAs(mat, t) 478 case *SymBandDense, *basicSymBanded: 479 if m != n { 480 panic("bad size") 481 } 482 var k int 483 switch t := t.(type) { 484 case *SymBandDense: 485 k = t.mat.K 486 case *basicSymBanded: 487 k = (*SymBandDense)(t).mat.K 488 } 489 k = min(k, m-1) // Special case for small sizes. 490 data := make([]float64, m*(k+1)) 491 for i := range data { 492 data[i] = rnd.NormFloat64() 493 } 494 mat := NewSymBandDense(n, k, data) 495 rMatrix = returnAs(mat, t) 496 case *TriBandDense, *basicTriBanded: 497 if m != n { 498 panic("bad size") 499 } 500 var k int 501 var triKind TriKind 502 switch t := t.(type) { 503 case *TriBandDense: 504 k = t.mat.K 505 triKind = t.triKind() 506 case *basicTriBanded: 507 k = (*TriBandDense)(t).mat.K 508 triKind = (*TriBandDense)(t).triKind() 509 } 510 k = min(k, m-1) // Special case for small sizes. 511 data := make([]float64, m*(k+1)) 512 for i := range data { 513 data[i] = rnd.NormFloat64() 514 } 515 mat := NewTriBandDense(n, k, triKind, data) 516 rMatrix = returnAs(mat, t) 517 case *DiagDense, *basicDiagonal: 518 if m != n { 519 panic("bad size") 520 } 521 var inc int 522 switch t := t.(type) { 523 case *DiagDense: 524 inc = t.mat.Inc 525 case *basicDiagonal: 526 inc = (*DiagDense)(t).mat.Inc 527 } 528 if inc == 0 { 529 inc = 1 530 } 531 mat := &DiagDense{ 532 mat: blas64.Vector{ 533 N: n, 534 Inc: inc, 535 Data: make([]float64, inc*(n-1)+1), 536 }, 537 } 538 for i := 0; i < n; i++ { 539 mat.SetDiag(i, rnd.Float64()) 540 } 541 rMatrix = returnAs(mat, t) 542 case *Tridiag: 543 if m != n { 544 panic("bad size") 545 } 546 mat := NewTridiag(n, nil, nil, nil) 547 for i := 0; i < n; i++ { 548 for j := max(0, i-1); j <= min(i+1, n-1); j++ { 549 mat.SetBand(i, j, rnd.NormFloat64()) 550 } 551 } 552 rMatrix = returnAs(mat, t) 553 } 554 if mr, mc := rMatrix.Dims(); mr != m || mc != n { 555 panic(fmt.Sprintf("makeRandOf for %T returns wrong size: %d×%d != %d×%d", a, m, n, mr, mc)) 556 } 557 return rMatrix 558 } 559 560 // makeNaNOf returns a new m×n matrix of the underlying matrix type filled with NaN values. 561 func makeNaNOf(a Matrix, m, n int) Matrix { 562 var rMatrix Matrix 563 switch t := a.(type) { 564 default: 565 panic("unknown type for makeNaNOf") 566 case Untransposer: 567 rMatrix = retranspose(a, makeNaNOf(t.Untranspose(), n, m)) 568 case *Dense, *basicMatrix: 569 var mat = &Dense{} 570 if m != 0 && n != 0 { 571 mat = NewDense(m, n, nil) 572 } 573 for i := 0; i < m; i++ { 574 for j := 0; j < n; j++ { 575 mat.Set(i, j, math.NaN()) 576 } 577 } 578 rMatrix = returnAs(mat, t) 579 case *VecDense: 580 if m == 0 && n == 0 { 581 return &VecDense{} 582 } 583 if n != 1 { 584 panic(fmt.Sprintf("bad vector size: m = %v, n = %v", m, n)) 585 } 586 length := m 587 inc := 1 588 if t.mat.Inc != 0 { 589 inc = t.mat.Inc 590 } 591 mat := &VecDense{ 592 mat: blas64.Vector{ 593 N: length, 594 Inc: inc, 595 Data: make([]float64, inc*(length-1)+1), 596 }, 597 } 598 for i := 0; i < length; i++ { 599 mat.SetVec(i, math.NaN()) 600 } 601 return mat 602 case *basicVector: 603 if n != 1 { 604 panic(fmt.Sprintf("bad vector size: m = %v, n = %v", m, n)) 605 } 606 if m == 0 { 607 return &basicVector{} 608 } 609 mat := NewVecDense(m, nil) 610 for i := 0; i < m; i++ { 611 mat.SetVec(i, math.NaN()) 612 } 613 return asBasicVector(mat) 614 case *SymDense, *basicSymmetric: 615 if m != n { 616 panic("bad size") 617 } 618 mat := &SymDense{} 619 if n != 0 { 620 mat = NewSymDense(n, nil) 621 } 622 for i := 0; i < m; i++ { 623 for j := i; j < n; j++ { 624 mat.SetSym(i, j, math.NaN()) 625 } 626 } 627 rMatrix = returnAs(mat, t) 628 case *TriDense, *basicTriangular: 629 if m != n { 630 panic("bad size") 631 } 632 633 // This is necessary because we are making 634 // a triangle from the zero value, which 635 // always returns upper as true. 636 var triKind TriKind 637 switch t := t.(type) { 638 case *TriDense: 639 triKind = t.triKind() 640 case *basicTriangular: 641 triKind = (*TriDense)(t).triKind() 642 } 643 644 if n == 0 { 645 uplo := blas.Upper 646 if triKind == Lower { 647 uplo = blas.Lower 648 } 649 return returnAs(&TriDense{mat: blas64.Triangular{Uplo: uplo}}, t) 650 } 651 652 mat := NewTriDense(n, triKind, nil) 653 if triKind == Upper { 654 for i := 0; i < m; i++ { 655 for j := i; j < n; j++ { 656 mat.SetTri(i, j, math.NaN()) 657 } 658 } 659 } else { 660 for i := 0; i < m; i++ { 661 for j := 0; j <= i; j++ { 662 mat.SetTri(i, j, math.NaN()) 663 } 664 } 665 } 666 rMatrix = returnAs(mat, t) 667 case *BandDense, *basicBanded: 668 var kl, ku int 669 switch t := t.(type) { 670 case *BandDense: 671 kl = t.mat.KL 672 ku = t.mat.KU 673 case *basicBanded: 674 ku = (*BandDense)(t).mat.KU 675 kl = (*BandDense)(t).mat.KL 676 } 677 ku = min(ku, n-1) 678 kl = min(kl, m-1) 679 data := make([]float64, min(m, n+kl)*(kl+ku+1)) 680 for i := range data { 681 data[i] = math.NaN() 682 } 683 mat := NewBandDense(m, n, kl, ku, data) 684 rMatrix = returnAs(mat, t) 685 case *SymBandDense, *basicSymBanded: 686 if m != n { 687 panic("bad size") 688 } 689 var k int 690 switch t := t.(type) { 691 case *SymBandDense: 692 k = t.mat.K 693 case *basicSymBanded: 694 k = (*SymBandDense)(t).mat.K 695 } 696 k = min(k, m-1) // Special case for small sizes. 697 data := make([]float64, m*(k+1)) 698 for i := range data { 699 data[i] = math.NaN() 700 } 701 mat := NewSymBandDense(n, k, data) 702 rMatrix = returnAs(mat, t) 703 case *TriBandDense, *basicTriBanded: 704 if m != n { 705 panic("bad size") 706 } 707 var k int 708 var triKind TriKind 709 switch t := t.(type) { 710 case *TriBandDense: 711 k = t.mat.K 712 triKind = t.triKind() 713 case *basicTriBanded: 714 k = (*TriBandDense)(t).mat.K 715 triKind = (*TriBandDense)(t).triKind() 716 } 717 k = min(k, m-1) // Special case for small sizes. 718 data := make([]float64, m*(k+1)) 719 for i := range data { 720 data[i] = math.NaN() 721 } 722 mat := NewTriBandDense(n, k, triKind, data) 723 rMatrix = returnAs(mat, t) 724 case *DiagDense, *basicDiagonal: 725 if m != n { 726 panic("bad size") 727 } 728 var inc int 729 switch t := t.(type) { 730 case *DiagDense: 731 inc = t.mat.Inc 732 case *basicDiagonal: 733 inc = (*DiagDense)(t).mat.Inc 734 } 735 if inc == 0 { 736 inc = 1 737 } 738 mat := &DiagDense{ 739 mat: blas64.Vector{ 740 N: n, 741 Inc: inc, 742 Data: make([]float64, inc*(n-1)+1), 743 }, 744 } 745 for i := 0; i < n; i++ { 746 mat.SetDiag(i, math.NaN()) 747 } 748 rMatrix = returnAs(mat, t) 749 } 750 if mr, mc := rMatrix.Dims(); mr != m || mc != n { 751 panic(fmt.Sprintf("makeNaNOf for %T returns wrong size: %d×%d != %d×%d", a, m, n, mr, mc)) 752 } 753 return rMatrix 754 } 755 756 // makeCopyOf returns a copy of the matrix. 757 func makeCopyOf(a Matrix) Matrix { 758 switch t := a.(type) { 759 default: 760 panic("unknown type in makeCopyOf") 761 case Untransposer: 762 return retranspose(a, makeCopyOf(t.Untranspose())) 763 case *Dense, *basicMatrix: 764 var m Dense 765 m.CloneFrom(a) 766 return returnAs(&m, t) 767 case *SymDense, *basicSymmetric: 768 n := t.(Symmetric).SymmetricDim() 769 m := NewSymDense(n, nil) 770 m.CopySym(t.(Symmetric)) 771 return returnAs(m, t) 772 case *TriDense, *basicTriangular: 773 n, upper := t.(Triangular).Triangle() 774 m := NewTriDense(n, upper, nil) 775 if upper { 776 for i := 0; i < n; i++ { 777 for j := i; j < n; j++ { 778 m.SetTri(i, j, t.At(i, j)) 779 } 780 } 781 } else { 782 for i := 0; i < n; i++ { 783 for j := 0; j <= i; j++ { 784 m.SetTri(i, j, t.At(i, j)) 785 } 786 } 787 } 788 return returnAs(m, t) 789 case *BandDense, *basicBanded: 790 var band *BandDense 791 switch s := t.(type) { 792 case *BandDense: 793 band = s 794 case *basicBanded: 795 band = (*BandDense)(s) 796 } 797 m := &BandDense{ 798 mat: blas64.Band{ 799 Rows: band.mat.Rows, 800 Cols: band.mat.Cols, 801 KL: band.mat.KL, 802 KU: band.mat.KU, 803 Data: make([]float64, len(band.mat.Data)), 804 Stride: band.mat.Stride, 805 }, 806 } 807 copy(m.mat.Data, band.mat.Data) 808 return returnAs(m, t) 809 case *SymBandDense, *basicSymBanded: 810 var sym *SymBandDense 811 switch s := t.(type) { 812 case *SymBandDense: 813 sym = s 814 case *basicSymBanded: 815 sym = (*SymBandDense)(s) 816 } 817 m := &SymBandDense{ 818 mat: blas64.SymmetricBand{ 819 Uplo: blas.Upper, 820 N: sym.mat.N, 821 K: sym.mat.K, 822 Data: make([]float64, len(sym.mat.Data)), 823 Stride: sym.mat.Stride, 824 }, 825 } 826 copy(m.mat.Data, sym.mat.Data) 827 return returnAs(m, t) 828 case *TriBandDense, *basicTriBanded: 829 var tri *TriBandDense 830 switch s := t.(type) { 831 case *TriBandDense: 832 tri = s 833 case *basicTriBanded: 834 tri = (*TriBandDense)(s) 835 } 836 m := &TriBandDense{ 837 mat: blas64.TriangularBand{ 838 Uplo: tri.mat.Uplo, 839 Diag: tri.mat.Diag, 840 N: tri.mat.N, 841 K: tri.mat.K, 842 Data: make([]float64, len(tri.mat.Data)), 843 Stride: tri.mat.Stride, 844 }, 845 } 846 copy(m.mat.Data, tri.mat.Data) 847 return returnAs(m, t) 848 case *VecDense: 849 var m VecDense 850 m.CloneFromVec(t) 851 return &m 852 case *basicVector: 853 var m VecDense 854 m.CloneFromVec(t) 855 return asBasicVector(&m) 856 case *DiagDense, *basicDiagonal: 857 var diag *DiagDense 858 switch s := t.(type) { 859 case *DiagDense: 860 diag = s 861 case *basicDiagonal: 862 diag = (*DiagDense)(s) 863 } 864 d := &DiagDense{ 865 mat: blas64.Vector{N: diag.mat.N, Inc: diag.mat.Inc, Data: make([]float64, len(diag.mat.Data))}, 866 } 867 copy(d.mat.Data, diag.mat.Data) 868 return returnAs(d, t) 869 case *Tridiag: 870 var m Tridiag 871 m.CloneFromTridiag(a.(*Tridiag)) 872 return returnAs(&m, t) 873 } 874 } 875 876 // sameType returns true if a and b have the same underlying type. 877 func sameType(a, b Matrix) bool { 878 return reflect.ValueOf(a).Type() == reflect.ValueOf(b).Type() 879 } 880 881 // maybeSame returns true if the two matrices could be represented by the same 882 // pointer. 883 func maybeSame(receiver, a Matrix) bool { 884 rr, rc := receiver.Dims() 885 u, trans := a.(Untransposer) 886 if trans { 887 a = u.Untranspose() 888 } 889 if !sameType(receiver, a) { 890 return false 891 } 892 ar, ac := a.Dims() 893 if rr != ar || rc != ac { 894 return false 895 } 896 if _, ok := a.(Triangular); ok { 897 // They are both triangular types. The TriType needs to match 898 _, aKind := a.(Triangular).Triangle() 899 _, rKind := receiver.(Triangular).Triangle() 900 if aKind != rKind { 901 return false 902 } 903 } 904 return true 905 } 906 907 // equalApprox returns whether the elements of a and b are the same to within 908 // the tolerance. If ignoreNaN is true the test is relaxed such that NaN == NaN. 909 func equalApprox(a, b Matrix, tol float64, ignoreNaN bool) bool { 910 ar, ac := a.Dims() 911 br, bc := b.Dims() 912 if ar != br { 913 return false 914 } 915 if ac != bc { 916 return false 917 } 918 for i := 0; i < ar; i++ { 919 for j := 0; j < ac; j++ { 920 if !scalar.EqualWithinAbsOrRel(a.At(i, j), b.At(i, j), tol, tol) { 921 if ignoreNaN && math.IsNaN(a.At(i, j)) && math.IsNaN(b.At(i, j)) { 922 continue 923 } 924 return false 925 } 926 } 927 } 928 return true 929 } 930 931 // equal returns true if the matrices have equal entries. 932 func equal(a, b Matrix) bool { 933 ar, ac := a.Dims() 934 br, bc := b.Dims() 935 if ar != br { 936 return false 937 } 938 if ac != bc { 939 return false 940 } 941 for i := 0; i < ar; i++ { 942 for j := 0; j < ac; j++ { 943 if a.At(i, j) != b.At(i, j) { 944 return false 945 } 946 } 947 } 948 return true 949 } 950 951 // isDiagonal returns whether a is a diagonal matrix. 952 func isDiagonal(a Matrix) bool { 953 r, c := a.Dims() 954 for i := 0; i < r; i++ { 955 for j := 0; j < c; j++ { 956 if a.At(i, j) != 0 && i != j { 957 return false 958 } 959 } 960 } 961 return true 962 } 963 964 // equalDiagonal returns whether a and b are equal on the diagonal. 965 func equalDiagonal(a, b Matrix) bool { 966 ar, ac := a.Dims() 967 br, bc := a.Dims() 968 if min(ar, ac) != min(br, bc) { 969 return false 970 } 971 for i := 0; i < min(ar, ac); i++ { 972 if a.At(i, i) != b.At(i, i) { 973 return false 974 } 975 } 976 return true 977 } 978 979 // underlyingData extracts the underlying data of the matrix a. 980 func underlyingData(a Matrix) []float64 { 981 switch t := a.(type) { 982 default: 983 panic("matrix type not implemented for extracting underlying data") 984 case Untransposer: 985 return underlyingData(t.Untranspose()) 986 case *Dense: 987 return t.mat.Data 988 case *SymDense: 989 return t.mat.Data 990 case *TriDense: 991 return t.mat.Data 992 case *VecDense: 993 return t.mat.Data 994 } 995 } 996 997 // testMatrices is a list of matrix types to test. 998 // This test relies on the fact that the implementations of Triangle do not 999 // corrupt the value of Uplo when they are empty. This test will fail 1000 // if that changes (and some mechanism will need to be used to force the 1001 // correct TriKind to be read). 1002 var testMatrices = []Matrix{ 1003 &Dense{}, 1004 &basicMatrix{}, 1005 Transpose{&Dense{}}, 1006 1007 &VecDense{mat: blas64.Vector{Inc: 1}}, 1008 &VecDense{mat: blas64.Vector{Inc: 10}}, 1009 &basicVector{}, 1010 Transpose{&VecDense{mat: blas64.Vector{Inc: 1}}}, 1011 Transpose{&VecDense{mat: blas64.Vector{Inc: 10}}}, 1012 Transpose{&basicVector{}}, 1013 1014 &BandDense{mat: blas64.Band{KL: 2, KU: 1}}, 1015 &BandDense{mat: blas64.Band{KL: 1, KU: 2}}, 1016 Transpose{&BandDense{mat: blas64.Band{KL: 2, KU: 1}}}, 1017 Transpose{&BandDense{mat: blas64.Band{KL: 1, KU: 2}}}, 1018 TransposeBand{&BandDense{mat: blas64.Band{KL: 2, KU: 1}}}, 1019 TransposeBand{&BandDense{mat: blas64.Band{KL: 1, KU: 2}}}, 1020 1021 &SymDense{}, 1022 &basicSymmetric{}, 1023 Transpose{&basicSymmetric{}}, 1024 1025 &TriDense{mat: blas64.Triangular{Uplo: blas.Upper}}, 1026 &TriDense{mat: blas64.Triangular{Uplo: blas.Lower}}, 1027 &basicTriangular{mat: blas64.Triangular{Uplo: blas.Upper}}, 1028 &basicTriangular{mat: blas64.Triangular{Uplo: blas.Lower}}, 1029 Transpose{&TriDense{mat: blas64.Triangular{Uplo: blas.Upper}}}, 1030 Transpose{&TriDense{mat: blas64.Triangular{Uplo: blas.Lower}}}, 1031 TransposeTri{&TriDense{mat: blas64.Triangular{Uplo: blas.Upper}}}, 1032 TransposeTri{&TriDense{mat: blas64.Triangular{Uplo: blas.Lower}}}, 1033 Transpose{&basicTriangular{mat: blas64.Triangular{Uplo: blas.Upper}}}, 1034 Transpose{&basicTriangular{mat: blas64.Triangular{Uplo: blas.Lower}}}, 1035 TransposeTri{&basicTriangular{mat: blas64.Triangular{Uplo: blas.Upper}}}, 1036 TransposeTri{&basicTriangular{mat: blas64.Triangular{Uplo: blas.Lower}}}, 1037 1038 &SymBandDense{}, 1039 &basicSymBanded{}, 1040 Transpose{&basicSymBanded{}}, 1041 1042 &SymBandDense{mat: blas64.SymmetricBand{K: 2}}, 1043 &basicSymBanded{mat: blas64.SymmetricBand{K: 2}}, 1044 Transpose{&basicSymBanded{mat: blas64.SymmetricBand{K: 2}}}, 1045 TransposeBand{&basicSymBanded{mat: blas64.SymmetricBand{K: 2}}}, 1046 1047 &TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}, 1048 &TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}, 1049 &basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}, 1050 &basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}, 1051 Transpose{&TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}}, 1052 Transpose{&TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}}, 1053 Transpose{&basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}}, 1054 Transpose{&basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}}, 1055 TransposeTri{&TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}}, 1056 TransposeTri{&TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}}, 1057 TransposeTri{&basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}}, 1058 TransposeTri{&basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}}, 1059 TransposeBand{&TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}}, 1060 TransposeBand{&TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}}, 1061 TransposeBand{&basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}}, 1062 TransposeBand{&basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}}, 1063 TransposeTriBand{&TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}}, 1064 TransposeTriBand{&TriBandDense{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}}, 1065 TransposeTriBand{&basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Upper}}}, 1066 TransposeTriBand{&basicTriBanded{mat: blas64.TriangularBand{K: 2, Uplo: blas.Lower}}}, 1067 1068 &DiagDense{}, 1069 &DiagDense{mat: blas64.Vector{Inc: 10}}, 1070 Transpose{&DiagDense{}}, 1071 Transpose{&DiagDense{mat: blas64.Vector{Inc: 10}}}, 1072 TransposeTri{&DiagDense{}}, 1073 TransposeTri{&DiagDense{mat: blas64.Vector{Inc: 10}}}, 1074 TransposeBand{&DiagDense{}}, 1075 TransposeBand{&DiagDense{mat: blas64.Vector{Inc: 10}}}, 1076 TransposeTriBand{&DiagDense{}}, 1077 TransposeTriBand{&DiagDense{mat: blas64.Vector{Inc: 10}}}, 1078 &basicDiagonal{}, 1079 Transpose{&basicDiagonal{}}, 1080 TransposeTri{&basicDiagonal{}}, 1081 TransposeBand{&basicDiagonal{}}, 1082 TransposeTriBand{&basicDiagonal{}}, 1083 1084 &Tridiag{}, 1085 Transpose{&Tridiag{}}, 1086 TransposeBand{&Tridiag{}}, 1087 } 1088 1089 var sizes = []struct { 1090 ar, ac int 1091 }{ 1092 {1, 1}, 1093 {1, 3}, 1094 {3, 1}, 1095 1096 {6, 6}, 1097 {6, 11}, 1098 {11, 6}, 1099 } 1100 1101 func testOneInputFunc(t *testing.T, 1102 // name is the name of the function being tested. 1103 name string, 1104 1105 // f is the function being tested. 1106 f func(a Matrix) interface{}, 1107 1108 // denseComparison performs the same operation, but using Dense matrices for 1109 // comparison. 1110 denseComparison func(a *Dense) interface{}, 1111 1112 // sameAnswer compares the result from two different evaluations of the function 1113 // and returns true if they are the same. The specific function being tested 1114 // determines the definition of "same". It may mean identical or it may mean 1115 // approximately equal. 1116 sameAnswer func(a, b interface{}) bool, 1117 1118 // legalType returns true if the type of the input is a legal type for the 1119 // input of the function. 1120 legalType func(a Matrix) bool, 1121 1122 // legalSize returns true if the size is valid for the function. 1123 legalSize func(r, c int) bool, 1124 ) { 1125 src := rand.NewSource(1) 1126 for _, aMat := range testMatrices { 1127 for _, test := range sizes { 1128 // Skip the test if the argument would not be assignable to the 1129 // method's corresponding input parameter or it is not possible 1130 // to construct an argument of the requested size. 1131 if !legalType(aMat) { 1132 continue 1133 } 1134 if !legalDims(aMat, test.ar, test.ac) { 1135 continue 1136 } 1137 a := makeRandOf(aMat, test.ar, test.ac, src) 1138 1139 // Compute the true answer if the sizes are legal. 1140 dimsOK := legalSize(test.ar, test.ac) 1141 var want interface{} 1142 if dimsOK { 1143 var aDense Dense 1144 aDense.CloneFrom(a) 1145 want = denseComparison(&aDense) 1146 } 1147 aCopy := makeCopyOf(a) 1148 // Test the method for a zero-value of the receiver. 1149 aType, aTrans := untranspose(a) 1150 errStr := fmt.Sprintf("%v(%T), size: %#v, atrans %t", name, aType, test, aTrans) 1151 var got interface{} 1152 panicked, err := panics(func() { got = f(a) }) 1153 if !dimsOK && !panicked { 1154 t.Errorf("Did not panic with illegal size: %s", errStr) 1155 continue 1156 } 1157 if dimsOK && panicked { 1158 t.Errorf("Panicked with legal size: %s: %v", errStr, err) 1159 continue 1160 } 1161 if !equal(a, aCopy) { 1162 t.Errorf("First input argument changed in call: %s", errStr) 1163 } 1164 if !dimsOK { 1165 continue 1166 } 1167 if !sameAnswer(want, got) { 1168 t.Errorf("Answer mismatch: %s; got %v, want %v", errStr, got, want) 1169 } 1170 } 1171 } 1172 } 1173 1174 var sizePairs = []struct { 1175 ar, ac, br, bc int 1176 }{ 1177 {1, 1, 1, 1}, 1178 {6, 6, 6, 6}, 1179 {7, 7, 7, 7}, 1180 1181 {1, 1, 1, 5}, 1182 {1, 1, 5, 1}, 1183 {1, 5, 1, 1}, 1184 {5, 1, 1, 1}, 1185 1186 {5, 5, 5, 1}, 1187 {5, 5, 1, 5}, 1188 {5, 1, 5, 5}, 1189 {1, 5, 5, 5}, 1190 1191 {6, 6, 6, 11}, 1192 {6, 6, 11, 6}, 1193 {6, 11, 6, 6}, 1194 {11, 6, 6, 6}, 1195 {11, 11, 11, 6}, 1196 {11, 11, 6, 11}, 1197 {11, 6, 11, 11}, 1198 {6, 11, 11, 11}, 1199 1200 {1, 1, 5, 5}, 1201 {1, 5, 1, 5}, 1202 {1, 5, 5, 1}, 1203 {5, 1, 1, 5}, 1204 {5, 1, 5, 1}, 1205 {5, 5, 1, 1}, 1206 {6, 6, 11, 11}, 1207 {6, 11, 6, 11}, 1208 {6, 11, 11, 6}, 1209 {11, 6, 6, 11}, 1210 {11, 6, 11, 6}, 1211 {11, 11, 6, 6}, 1212 1213 {1, 1, 17, 11}, 1214 {1, 1, 11, 17}, 1215 {1, 11, 1, 17}, 1216 {1, 17, 1, 11}, 1217 {1, 11, 17, 1}, 1218 {1, 17, 11, 1}, 1219 {11, 1, 1, 17}, 1220 {17, 1, 1, 11}, 1221 {11, 1, 17, 1}, 1222 {17, 1, 11, 1}, 1223 {11, 17, 1, 1}, 1224 {17, 11, 1, 1}, 1225 1226 {6, 6, 1, 11}, 1227 {6, 6, 11, 1}, 1228 {6, 11, 6, 1}, 1229 {6, 1, 6, 11}, 1230 {6, 11, 1, 6}, 1231 {6, 1, 11, 6}, 1232 {11, 6, 6, 1}, 1233 {1, 6, 6, 11}, 1234 {11, 6, 1, 6}, 1235 {1, 6, 11, 6}, 1236 {11, 1, 6, 6}, 1237 {1, 11, 6, 6}, 1238 1239 {6, 6, 17, 1}, 1240 {6, 6, 1, 17}, 1241 {6, 1, 6, 17}, 1242 {6, 17, 6, 1}, 1243 {6, 1, 17, 6}, 1244 {6, 17, 1, 6}, 1245 {1, 6, 6, 17}, 1246 {17, 6, 6, 1}, 1247 {1, 6, 17, 6}, 1248 {17, 6, 1, 6}, 1249 {1, 17, 6, 6}, 1250 {17, 1, 6, 6}, 1251 1252 {6, 6, 17, 11}, 1253 {6, 6, 11, 17}, 1254 {6, 11, 6, 17}, 1255 {6, 17, 6, 11}, 1256 {6, 11, 17, 6}, 1257 {6, 17, 11, 6}, 1258 {11, 6, 6, 17}, 1259 {17, 6, 6, 11}, 1260 {11, 6, 17, 6}, 1261 {17, 6, 11, 6}, 1262 {11, 17, 6, 6}, 1263 {17, 11, 6, 6}, 1264 } 1265 1266 func testTwoInputFunc(t *testing.T, 1267 // name is the name of the function being tested. 1268 name string, 1269 1270 // f is the function being tested. 1271 f func(a, b Matrix) interface{}, 1272 1273 // denseComparison performs the same operation, but using Dense matrices for 1274 // comparison. 1275 denseComparison func(a, b *Dense) interface{}, 1276 1277 // sameAnswer compares the result from two different evaluations of the function 1278 // and returns true if they are the same. The specific function being tested 1279 // determines the definition of "same". It may mean identical or it may mean 1280 // approximately equal. 1281 sameAnswer func(a, b interface{}) bool, 1282 1283 // legalType returns true if the types of the inputs are legal for the 1284 // input of the function. 1285 legalType func(a, b Matrix) bool, 1286 1287 // legalSize returns true if the sizes are valid for the function. 1288 legalSize func(ar, ac, br, bc int) bool, 1289 ) { 1290 src := rand.NewSource(1) 1291 for _, aMat := range testMatrices { 1292 for _, bMat := range testMatrices { 1293 // Loop over all of the size combinations (bigger, smaller, etc.). 1294 for _, test := range sizePairs { 1295 // Skip the test if the argument would not be assignable to the 1296 // method's corresponding input parameter or it is not possible 1297 // to construct an argument of the requested size. 1298 if !legalType(aMat, bMat) { 1299 continue 1300 } 1301 if !legalDims(aMat, test.ar, test.ac) { 1302 continue 1303 } 1304 if !legalDims(bMat, test.br, test.bc) { 1305 continue 1306 } 1307 a := makeRandOf(aMat, test.ar, test.ac, src) 1308 b := makeRandOf(bMat, test.br, test.bc, src) 1309 1310 // Compute the true answer if the sizes are legal. 1311 dimsOK := legalSize(test.ar, test.ac, test.br, test.bc) 1312 var want interface{} 1313 if dimsOK { 1314 var aDense, bDense Dense 1315 aDense.CloneFrom(a) 1316 bDense.CloneFrom(b) 1317 want = denseComparison(&aDense, &bDense) 1318 } 1319 aCopy := makeCopyOf(a) 1320 bCopy := makeCopyOf(b) 1321 // Test the method for a zero-value of the receiver. 1322 aType, aTrans := untranspose(a) 1323 bType, bTrans := untranspose(b) 1324 errStr := fmt.Sprintf("%v(%T, %T), size: %#v, atrans %t, btrans %t", name, aType, bType, test, aTrans, bTrans) 1325 var got interface{} 1326 panicked, err := panics(func() { got = f(a, b) }) 1327 if !dimsOK && !panicked { 1328 t.Errorf("Did not panic with illegal size: %s", errStr) 1329 continue 1330 } 1331 if dimsOK && panicked { 1332 t.Errorf("Panicked with legal size: %s: %v", errStr, err) 1333 continue 1334 } 1335 if !equal(a, aCopy) { 1336 t.Errorf("First input argument changed in call: %s", errStr) 1337 } 1338 if !equal(b, bCopy) { 1339 t.Errorf("First input argument changed in call: %s", errStr) 1340 } 1341 if !dimsOK { 1342 continue 1343 } 1344 if !sameAnswer(want, got) { 1345 t.Errorf("Answer mismatch: %s", errStr) 1346 } 1347 } 1348 } 1349 } 1350 } 1351 1352 // testOneInput tests a method that has one matrix input argument 1353 func testOneInput(t *testing.T, 1354 // name is the name of the method being tested. 1355 name string, 1356 1357 // receiver is a value of the receiver type. 1358 receiver Matrix, 1359 1360 // method is the generalized receiver.Method(a). 1361 method func(receiver, a Matrix), 1362 1363 // denseComparison performs the same operation as method, but with dense 1364 // matrices for comparison with the result. 1365 denseComparison func(receiver, a *Dense), 1366 1367 // legalTypes returns whether the concrete types in Matrix are valid for 1368 // the method. 1369 legalType func(a Matrix) bool, 1370 1371 // legalSize returns whether the matrix sizes are valid for the method. 1372 legalSize func(ar, ac int) bool, 1373 1374 // tol is the tolerance for equality when comparing method results. 1375 tol float64, 1376 ) { 1377 src := rand.NewSource(1) 1378 for _, aMat := range testMatrices { 1379 for _, test := range sizes { 1380 // Skip the test if the argument would not be assignable to the 1381 // method's corresponding input parameter or it is not possible 1382 // to construct an argument of the requested size. 1383 if !legalType(aMat) { 1384 continue 1385 } 1386 if !legalDims(aMat, test.ar, test.ac) { 1387 continue 1388 } 1389 a := makeRandOf(aMat, test.ar, test.ac, src) 1390 1391 // Compute the true answer if the sizes are legal. 1392 dimsOK := legalSize(test.ar, test.ac) 1393 var want Dense 1394 if dimsOK { 1395 var aDense Dense 1396 aDense.CloneFrom(a) 1397 denseComparison(&want, &aDense) 1398 } 1399 aCopy := makeCopyOf(a) 1400 1401 // Test the method for a zero-value of the receiver. 1402 aType, aTrans := untranspose(a) 1403 errStr := fmt.Sprintf("%T.%s(%T), size: %#v, atrans %v", receiver, name, aType, test, aTrans) 1404 empty := makeRandOf(receiver, 0, 0, src) 1405 panicked, err := panics(func() { method(empty, a) }) 1406 if !dimsOK && !panicked { 1407 t.Errorf("Did not panic with illegal size: %s", errStr) 1408 continue 1409 } 1410 if dimsOK && panicked { 1411 t.Errorf("Panicked with legal size: %s: %v", errStr, err) 1412 continue 1413 } 1414 if !equal(a, aCopy) { 1415 t.Errorf("First input argument changed in call: %s", errStr) 1416 } 1417 if !dimsOK { 1418 continue 1419 } 1420 if !equalApprox(empty, &want, tol, false) { 1421 t.Errorf("Answer mismatch with empty receiver: %s.\nGot:\n% v\nWant:\n% v\n", errStr, Formatted(empty), Formatted(&want)) 1422 continue 1423 } 1424 1425 // Test the method with a non-empty-value of the receiver. 1426 // The receiver has been overwritten in place so use its size 1427 // to construct a new random matrix. 1428 rr, rc := empty.Dims() 1429 neverEmpty := makeRandOf(receiver, rr, rc, src) 1430 panicked, message := panics(func() { method(neverEmpty, a) }) 1431 if panicked { 1432 t.Errorf("Panicked with non-empty receiver: %s: %s", errStr, message) 1433 } 1434 if !equalApprox(neverEmpty, &want, tol, false) { 1435 t.Errorf("Answer mismatch non-empty receiver: %s", errStr) 1436 } 1437 1438 // Test the method with a NaN-filled-value of the receiver. 1439 // The receiver has been overwritten in place so use its size 1440 // to construct a new NaN matrix. 1441 nanMatrix := makeNaNOf(receiver, rr, rc) 1442 panicked, message = panics(func() { method(nanMatrix, a) }) 1443 if panicked { 1444 t.Errorf("Panicked with NaN-filled receiver: %s: %s", errStr, message) 1445 } 1446 if !equalApprox(nanMatrix, &want, tol, false) { 1447 t.Errorf("Answer mismatch NaN-filled receiver: %s", errStr) 1448 } 1449 1450 // Test with an incorrectly sized matrix. 1451 switch receiver.(type) { 1452 default: 1453 panic("matrix type not coded for incorrect receiver size") 1454 case *Dense: 1455 wrongSize := makeRandOf(receiver, rr+1, rc, src) 1456 panicked, _ = panics(func() { method(wrongSize, a) }) 1457 if !panicked { 1458 t.Errorf("Did not panic with wrong number of rows: %s", errStr) 1459 } 1460 wrongSize = makeRandOf(receiver, rr, rc+1, src) 1461 panicked, _ = panics(func() { method(wrongSize, a) }) 1462 if !panicked { 1463 t.Errorf("Did not panic with wrong number of columns: %s", errStr) 1464 } 1465 case *TriDense, *SymDense: 1466 // Add to the square size. 1467 wrongSize := makeRandOf(receiver, rr+1, rc+1, src) 1468 panicked, _ = panics(func() { method(wrongSize, a) }) 1469 if !panicked { 1470 t.Errorf("Did not panic with wrong size: %s", errStr) 1471 } 1472 case *VecDense: 1473 // Add to the column length. 1474 wrongSize := makeRandOf(receiver, rr+1, rc, src) 1475 panicked, _ = panics(func() { method(wrongSize, a) }) 1476 if !panicked { 1477 t.Errorf("Did not panic with wrong number of rows: %s", errStr) 1478 } 1479 } 1480 1481 // The receiver and the input may share a matrix pointer 1482 // if the type and size of the receiver and one of the 1483 // arguments match. Test the method works properly 1484 // when this is the case. 1485 aMaybeSame := maybeSame(neverEmpty, a) 1486 if aMaybeSame { 1487 aSame := makeCopyOf(a) 1488 receiver = aSame 1489 u, ok := aSame.(Untransposer) 1490 if ok { 1491 receiver = u.Untranspose() 1492 } 1493 preData := underlyingData(receiver) 1494 panicked, err = panics(func() { method(receiver, aSame) }) 1495 if panicked { 1496 t.Errorf("Panics when a maybeSame: %s: %v", errStr, err) 1497 } else { 1498 if !equalApprox(receiver, &want, tol, false) { 1499 t.Errorf("Wrong answer when a maybeSame: %s", errStr) 1500 } 1501 postData := underlyingData(receiver) 1502 if !floats.Equal(preData, postData) { 1503 t.Errorf("Original data slice not modified when a maybeSame: %s", errStr) 1504 } 1505 } 1506 } 1507 } 1508 } 1509 } 1510 1511 // testTwoInput tests a method that has two input arguments. 1512 func testTwoInput(t *testing.T, 1513 // name is the name of the method being tested. 1514 name string, 1515 1516 // receiver is a value of the receiver type. 1517 receiver Matrix, 1518 1519 // method is the generalized receiver.Method(a, b). 1520 method func(receiver, a, b Matrix), 1521 1522 // denseComparison performs the same operation as method, but with dense 1523 // matrices for comparison with the result. 1524 denseComparison func(receiver, a, b *Dense), 1525 1526 // legalTypes returns whether the concrete types in Matrix are valid for 1527 // the method. 1528 legalTypes func(a, b Matrix) bool, 1529 1530 // legalSize returns whether the matrix sizes are valid for the method. 1531 legalSize func(ar, ac, br, bc int) bool, 1532 1533 // tol is the tolerance for equality when comparing method results. 1534 tol float64, 1535 ) { 1536 src := rand.NewSource(1) 1537 for _, aMat := range testMatrices { 1538 for _, bMat := range testMatrices { 1539 // Loop over all of the size combinations (bigger, smaller, etc.). 1540 for _, test := range sizePairs { 1541 // Skip the test if any argument would not be assignable to the 1542 // method's corresponding input parameter or it is not possible 1543 // to construct an argument of the requested size. 1544 if !legalTypes(aMat, bMat) { 1545 continue 1546 } 1547 if !legalDims(aMat, test.ar, test.ac) { 1548 continue 1549 } 1550 if !legalDims(bMat, test.br, test.bc) { 1551 continue 1552 } 1553 a := makeRandOf(aMat, test.ar, test.ac, src) 1554 b := makeRandOf(bMat, test.br, test.bc, src) 1555 1556 // Compute the true answer if the sizes are legal. 1557 dimsOK := legalSize(test.ar, test.ac, test.br, test.bc) 1558 var want Dense 1559 if dimsOK { 1560 var aDense, bDense Dense 1561 aDense.CloneFrom(a) 1562 bDense.CloneFrom(b) 1563 denseComparison(&want, &aDense, &bDense) 1564 } 1565 aCopy := makeCopyOf(a) 1566 bCopy := makeCopyOf(b) 1567 1568 // Test the method for a empty-value of the receiver. 1569 aType, aTrans := untranspose(a) 1570 bType, bTrans := untranspose(b) 1571 errStr := fmt.Sprintf("%T.%s(%T, %T), sizes: %#v, atrans %v, btrans %v", receiver, name, aType, bType, test, aTrans, bTrans) 1572 empty := makeRandOf(receiver, 0, 0, src) 1573 panicked, err := panics(func() { method(empty, a, b) }) 1574 if !dimsOK && !panicked { 1575 t.Errorf("Did not panic with illegal size: %s", errStr) 1576 continue 1577 } 1578 if dimsOK && panicked { 1579 t.Errorf("Panicked with legal size: %s: %v", errStr, err) 1580 continue 1581 } 1582 if !equal(a, aCopy) { 1583 t.Errorf("First input argument changed in call: %s", errStr) 1584 } 1585 if !equal(b, bCopy) { 1586 t.Errorf("Second input argument changed in call: %s", errStr) 1587 } 1588 if !dimsOK { 1589 continue 1590 } 1591 wasEmpty, empty := empty, nil // Nil-out empty so we detect illegal use. 1592 // NaN equality is allowed because of 0/0 in DivElem test. 1593 if !equalApprox(wasEmpty, &want, tol, true) { 1594 t.Errorf("Answer mismatch with empty receiver: %s", errStr) 1595 continue 1596 } 1597 1598 // Test the method with a non-empty-value of the receiver. 1599 // The receiver has been overwritten in place so use its size 1600 // to construct a new random matrix. 1601 rr, rc := wasEmpty.Dims() 1602 neverEmpty := makeRandOf(receiver, rr, rc, src) 1603 panicked, message := panics(func() { method(neverEmpty, a, b) }) 1604 if panicked { 1605 t.Errorf("Panicked with non-empty receiver: %s: %s", errStr, message) 1606 } 1607 // NaN equality is allowed because of 0/0 in DivElem test. 1608 if !equalApprox(neverEmpty, &want, tol, true) { 1609 t.Errorf("Answer mismatch non-empty receiver: %s", errStr) 1610 } 1611 1612 // Test the method with a NaN-filled value of the receiver. 1613 // The receiver has been overwritten in place so use its size 1614 // to construct a new NaN matrix. 1615 nanMatrix := makeNaNOf(receiver, rr, rc) 1616 panicked, message = panics(func() { method(nanMatrix, a, b) }) 1617 if panicked { 1618 t.Errorf("Panicked with NaN-filled receiver: %s: %s", errStr, message) 1619 } 1620 // NaN equality is allowed because of 0/0 in DivElem test. 1621 if !equalApprox(nanMatrix, &want, tol, true) { 1622 t.Errorf("Answer mismatch NaN-filled receiver: %s", errStr) 1623 } 1624 1625 // Test with an incorrectly sized matrix. 1626 switch receiver.(type) { 1627 default: 1628 panic("matrix type not coded for incorrect receiver size") 1629 case *Dense: 1630 wrongSize := makeRandOf(receiver, rr+1, rc, src) 1631 panicked, _ = panics(func() { method(wrongSize, a, b) }) 1632 if !panicked { 1633 t.Errorf("Did not panic with wrong number of rows: %s", errStr) 1634 } 1635 wrongSize = makeRandOf(receiver, rr, rc+1, src) 1636 panicked, _ = panics(func() { method(wrongSize, a, b) }) 1637 if !panicked { 1638 t.Errorf("Did not panic with wrong number of columns: %s", errStr) 1639 } 1640 case *TriDense, *SymDense: 1641 // Add to the square size. 1642 wrongSize := makeRandOf(receiver, rr+1, rc+1, src) 1643 panicked, _ = panics(func() { method(wrongSize, a, b) }) 1644 if !panicked { 1645 t.Errorf("Did not panic with wrong size: %s", errStr) 1646 } 1647 case *VecDense: 1648 // Add to the column length. 1649 wrongSize := makeRandOf(receiver, rr+1, rc, src) 1650 panicked, _ = panics(func() { method(wrongSize, a, b) }) 1651 if !panicked { 1652 t.Errorf("Did not panic with wrong number of rows: %s", errStr) 1653 } 1654 } 1655 1656 // The receiver and an input may share a matrix pointer 1657 // if the type and size of the receiver and one of the 1658 // arguments match. Test the method works properly 1659 // when this is the case. 1660 aMaybeSame := maybeSame(neverEmpty, a) 1661 bMaybeSame := maybeSame(neverEmpty, b) 1662 if aMaybeSame { 1663 aSame := makeCopyOf(a) 1664 receiver = aSame 1665 u, ok := aSame.(Untransposer) 1666 if ok { 1667 receiver = u.Untranspose() 1668 } 1669 preData := underlyingData(receiver) 1670 panicked, err = panics(func() { method(receiver, aSame, b) }) 1671 if panicked { 1672 t.Errorf("Panics when a maybeSame: %s: %v", errStr, err) 1673 } else { 1674 if !equalApprox(receiver, &want, tol, false) { 1675 t.Errorf("Wrong answer when a maybeSame: %s", errStr) 1676 } 1677 postData := underlyingData(receiver) 1678 if !floats.Equal(preData, postData) { 1679 t.Errorf("Original data slice not modified when a maybeSame: %s", errStr) 1680 } 1681 } 1682 } 1683 if bMaybeSame { 1684 bSame := makeCopyOf(b) 1685 receiver = bSame 1686 u, ok := bSame.(Untransposer) 1687 if ok { 1688 receiver = u.Untranspose() 1689 } 1690 preData := underlyingData(receiver) 1691 panicked, err = panics(func() { method(receiver, a, bSame) }) 1692 if panicked { 1693 t.Errorf("Panics when b maybeSame: %s: %v", errStr, err) 1694 } else { 1695 if !equalApprox(receiver, &want, tol, false) { 1696 t.Errorf("Wrong answer when b maybeSame: %s", errStr) 1697 } 1698 postData := underlyingData(receiver) 1699 if !floats.Equal(preData, postData) { 1700 t.Errorf("Original data slice not modified when b maybeSame: %s", errStr) 1701 } 1702 } 1703 } 1704 if aMaybeSame && bMaybeSame { 1705 aSame := makeCopyOf(a) 1706 receiver = aSame 1707 u, ok := aSame.(Untransposer) 1708 if ok { 1709 receiver = u.Untranspose() 1710 } 1711 // Ensure that b is the correct transpose type if applicable. 1712 // The receiver is always a concrete type so use it. 1713 bSame := receiver 1714 _, ok = b.(Untransposer) 1715 if ok { 1716 bSame = retranspose(b, receiver) 1717 } 1718 // Compute the real answer for this case. It is different 1719 // from the initial answer since now a and b have the 1720 // same data. 1721 empty = makeRandOf(wasEmpty, 0, 0, src) 1722 method(empty, aSame, bSame) 1723 wasEmpty, empty = empty, nil // Nil-out empty so we detect illegal use. 1724 preData := underlyingData(receiver) 1725 panicked, err = panics(func() { method(receiver, aSame, bSame) }) 1726 if panicked { 1727 t.Errorf("Panics when both maybeSame: %s: %v", errStr, err) 1728 } else { 1729 if !equalApprox(receiver, wasEmpty, tol, false) { 1730 t.Errorf("Wrong answer when both maybeSame: %s", errStr) 1731 } 1732 postData := underlyingData(receiver) 1733 if !floats.Equal(preData, postData) { 1734 t.Errorf("Original data slice not modified when both maybeSame: %s", errStr) 1735 } 1736 } 1737 } 1738 } 1739 } 1740 } 1741 }