gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/lapack/testlapack/general.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 package testlapack 6 7 import ( 8 "fmt" 9 "math" 10 "math/cmplx" 11 "testing" 12 13 "golang.org/x/exp/rand" 14 15 "gonum.org/v1/gonum/blas" 16 "gonum.org/v1/gonum/blas/blas64" 17 "gonum.org/v1/gonum/lapack" 18 ) 19 20 const ( 21 // dlamchE is the machine epsilon. For IEEE this is 2^{-53}. 22 dlamchE = 0x1p-53 23 dlamchB = 2 24 dlamchP = dlamchB * dlamchE 25 // dlamchS is the smallest normal number. For IEEE this is 2^{-1022}. 26 dlamchS = 0x1p-1022 27 28 safmin = dlamchS 29 safmax = 1 / safmin 30 ulp = dlamchP 31 smlnum = safmin / ulp 32 bignum = safmax * ulp 33 ) 34 35 // worklen describes how much workspace a test should use. 36 type worklen int 37 38 const ( 39 minimumWork worklen = iota 40 mediumWork 41 optimumWork 42 ) 43 44 func (wl worklen) String() string { 45 switch wl { 46 case minimumWork: 47 return "minimum" 48 case mediumWork: 49 return "medium" 50 case optimumWork: 51 return "optimum" 52 } 53 return "" 54 } 55 56 func normToString(norm lapack.MatrixNorm) string { 57 switch norm { 58 case lapack.MaxAbs: 59 return "MaxAbs" 60 case lapack.MaxRowSum: 61 return "MaxRowSum" 62 case lapack.MaxColumnSum: 63 return "MaxColSum" 64 case lapack.Frobenius: 65 return "Frobenius" 66 default: 67 panic("invalid norm") 68 } 69 } 70 71 func uploToString(uplo blas.Uplo) string { 72 switch uplo { 73 case blas.Lower: 74 return "Lower" 75 case blas.Upper: 76 return "Upper" 77 default: 78 panic("invalid uplo") 79 } 80 } 81 82 func diagToString(diag blas.Diag) string { 83 switch diag { 84 case blas.NonUnit: 85 return "NonUnit" 86 case blas.Unit: 87 return "Unit" 88 default: 89 panic("invalid diag") 90 } 91 } 92 93 func sideToString(side blas.Side) string { 94 switch side { 95 case blas.Left: 96 return "Left" 97 case blas.Right: 98 return "Right" 99 default: 100 panic("invalid side") 101 } 102 } 103 104 func transToString(trans blas.Transpose) string { 105 switch trans { 106 case blas.NoTrans: 107 return "NoTrans" 108 case blas.Trans: 109 return "Trans" 110 case blas.ConjTrans: 111 return "ConjTrans" 112 default: 113 panic("invalid trans") 114 } 115 } 116 117 // nanSlice allocates a new slice of length n filled with NaN. 118 func nanSlice(n int) []float64 { 119 s := make([]float64, n) 120 for i := range s { 121 s[i] = math.NaN() 122 } 123 return s 124 } 125 126 // randomSlice allocates a new slice of length n filled with random values. 127 func randomSlice(n int, rnd *rand.Rand) []float64 { 128 s := make([]float64, n) 129 for i := range s { 130 s[i] = rnd.NormFloat64() 131 } 132 return s 133 } 134 135 // nanGeneral allocates a new r×c general matrix filled with NaN values. 136 func nanGeneral(r, c, stride int) blas64.General { 137 if r < 0 || c < 0 { 138 panic("bad matrix size") 139 } 140 if r == 0 || c == 0 { 141 return blas64.General{Stride: max(1, stride)} 142 } 143 if stride < c { 144 panic("bad stride") 145 } 146 return blas64.General{ 147 Rows: r, 148 Cols: c, 149 Stride: stride, 150 Data: nanSlice((r-1)*stride + c), 151 } 152 } 153 154 // randomGeneral allocates a new r×c general matrix filled with random 155 // numbers. Out-of-range elements are filled with NaN values. 156 func randomGeneral(r, c, stride int, rnd *rand.Rand) blas64.General { 157 ans := nanGeneral(r, c, stride) 158 for i := 0; i < r; i++ { 159 for j := 0; j < c; j++ { 160 ans.Data[i*ans.Stride+j] = rnd.NormFloat64() 161 } 162 } 163 return ans 164 } 165 166 // randomHessenberg allocates a new n×n Hessenberg matrix filled with zeros 167 // under the first subdiagonal and with random numbers elsewhere. Out-of-range 168 // elements are filled with NaN values. 169 func randomHessenberg(n, stride int, rnd *rand.Rand) blas64.General { 170 ans := nanGeneral(n, n, stride) 171 for i := 0; i < n; i++ { 172 for j := 0; j < i-1; j++ { 173 ans.Data[i*ans.Stride+j] = 0 174 } 175 for j := max(0, i-1); j < n; j++ { 176 ans.Data[i*ans.Stride+j] = rnd.NormFloat64() 177 } 178 } 179 return ans 180 } 181 182 // randomSchurCanonical returns a random, general matrix in Schur canonical 183 // form, that is, block upper triangular with 1×1 and 2×2 diagonal blocks where 184 // each 2×2 diagonal block has its diagonal elements equal and its off-diagonal 185 // elements of opposite sign. bad controls whether the returned matrix will have 186 // zero or tiny eigenvalues. 187 func randomSchurCanonical(n, stride int, bad bool, rnd *rand.Rand) (t blas64.General, wr, wi []float64) { 188 t = randomGeneral(n, n, stride, rnd) 189 // Zero out the lower triangle including the diagonal which will be set later. 190 for i := 0; i < t.Rows; i++ { 191 for j := 0; j <= i; j++ { 192 t.Data[i*t.Stride+j] = 0 193 } 194 } 195 // Randomly create 2×2 diagonal blocks. 196 for i := 0; i < t.Rows; { 197 a := rnd.NormFloat64() 198 if bad && rnd.Float64() < 0.5 { 199 if rnd.Float64() < 0.5 { 200 // A quarter of real parts of eigenvalues will be tiny. 201 a = dlamchS 202 } else { 203 // A quarter of them will be zero. 204 a = 0 205 } 206 } 207 208 // A half of eigenvalues will be real. 209 if rnd.Float64() < 0.5 || i == t.Rows-1 { 210 // Store 1×1 block at the diagonal of T. 211 t.Data[i*t.Stride+i] = a 212 wr = append(wr, a) 213 wi = append(wi, 0) 214 i++ 215 continue 216 } 217 218 // Diagonal elements are equal. 219 d := a 220 // Element under the diagonal is "normal". 221 c := rnd.NormFloat64() 222 // Element above the diagonal cannot be zero. 223 var b float64 224 if bad && rnd.Float64() < 0.5 { 225 b = dlamchS 226 } else { 227 b = rnd.NormFloat64() 228 } 229 // Make sure off-diagonal elements are of opposite sign. 230 if math.Signbit(b) == math.Signbit(c) { 231 c *= -1 232 } 233 234 // Store 2×2 block at the diagonal of T. 235 t.Data[i*t.Stride+i], t.Data[i*t.Stride+i+1] = a, b 236 t.Data[(i+1)*t.Stride+i], t.Data[(i+1)*t.Stride+i+1] = c, d 237 238 wr = append(wr, a, a) 239 im := math.Sqrt(math.Abs(b)) * math.Sqrt(math.Abs(c)) 240 wi = append(wi, im, -im) 241 i += 2 242 } 243 return t, wr, wi 244 } 245 246 // blockedUpperTriGeneral returns a normal random, general matrix in the form 247 // 248 // c-k-l k l 249 // A = k [ 0 A12 A13 ] if r-k-l >= 0; 250 // l [ 0 0 A23 ] 251 // r-k-l [ 0 0 0 ] 252 // 253 // c-k-l k l 254 // A = k [ 0 A12 A13 ] if r-k-l < 0; 255 // r-k [ 0 0 A23 ] 256 // 257 // where the k×k matrix A12 and l×l matrix is non-singular 258 // upper triangular. A23 is l×l upper triangular if r-k-l >= 0, 259 // otherwise A23 is (r-k)×l upper trapezoidal. 260 func blockedUpperTriGeneral(r, c, k, l, stride int, kblock bool, rnd *rand.Rand) blas64.General { 261 t := l 262 if kblock { 263 t += k 264 } 265 ans := zeros(r, c, stride) 266 for i := 0; i < min(r, t); i++ { 267 var v float64 268 for v == 0 { 269 v = rnd.NormFloat64() 270 } 271 ans.Data[i*ans.Stride+i+(c-t)] = v 272 } 273 for i := 0; i < min(r, t); i++ { 274 for j := i + (c - t) + 1; j < c; j++ { 275 ans.Data[i*ans.Stride+j] = rnd.NormFloat64() 276 } 277 } 278 return ans 279 } 280 281 // nanTriangular allocates a new r×c triangular matrix filled with NaN values. 282 func nanTriangular(uplo blas.Uplo, n, stride int) blas64.Triangular { 283 if n < 0 { 284 panic("bad matrix size") 285 } 286 if n == 0 { 287 return blas64.Triangular{ 288 Stride: max(1, stride), 289 Uplo: uplo, 290 Diag: blas.NonUnit, 291 } 292 } 293 if stride < n { 294 panic("bad stride") 295 } 296 return blas64.Triangular{ 297 N: n, 298 Stride: stride, 299 Data: nanSlice((n-1)*stride + n), 300 Uplo: uplo, 301 Diag: blas.NonUnit, 302 } 303 } 304 305 // generalOutsideAllNaN returns whether all out-of-range elements have NaN 306 // values. 307 func generalOutsideAllNaN(a blas64.General) bool { 308 // Check after last column. 309 for i := 0; i < a.Rows-1; i++ { 310 for _, v := range a.Data[i*a.Stride+a.Cols : i*a.Stride+a.Stride] { 311 if !math.IsNaN(v) { 312 return false 313 } 314 } 315 } 316 // Check after last element. 317 last := (a.Rows-1)*a.Stride + a.Cols 318 if a.Rows == 0 || a.Cols == 0 { 319 last = 0 320 } 321 for _, v := range a.Data[last:] { 322 if !math.IsNaN(v) { 323 return false 324 } 325 } 326 return true 327 } 328 329 // triangularOutsideAllNaN returns whether all out-of-triangle elements have NaN 330 // values. 331 func triangularOutsideAllNaN(a blas64.Triangular) bool { 332 if a.Uplo == blas.Upper { 333 // Check below diagonal. 334 for i := 0; i < a.N; i++ { 335 for _, v := range a.Data[i*a.Stride : i*a.Stride+i] { 336 if !math.IsNaN(v) { 337 return false 338 } 339 } 340 } 341 // Check after last column. 342 for i := 0; i < a.N-1; i++ { 343 for _, v := range a.Data[i*a.Stride+a.N : i*a.Stride+a.Stride] { 344 if !math.IsNaN(v) { 345 return false 346 } 347 } 348 } 349 } else { 350 // Check above diagonal. 351 for i := 0; i < a.N-1; i++ { 352 for _, v := range a.Data[i*a.Stride+i+1 : i*a.Stride+a.Stride] { 353 if !math.IsNaN(v) { 354 return false 355 } 356 } 357 } 358 } 359 // Check after last element. 360 for _, v := range a.Data[max(0, a.N-1)*a.Stride+a.N:] { 361 if !math.IsNaN(v) { 362 return false 363 } 364 } 365 return true 366 } 367 368 // transposeGeneral returns a new general matrix that is the transpose of the 369 // input. Nothing is done with data outside the {rows, cols} limit of the general. 370 func transposeGeneral(a blas64.General) blas64.General { 371 ans := blas64.General{ 372 Rows: a.Cols, 373 Cols: a.Rows, 374 Stride: a.Rows, 375 Data: make([]float64, a.Cols*a.Rows), 376 } 377 for i := 0; i < a.Rows; i++ { 378 for j := 0; j < a.Cols; j++ { 379 ans.Data[j*ans.Stride+i] = a.Data[i*a.Stride+j] 380 } 381 } 382 return ans 383 } 384 385 // columnNorms returns the column norms of a. 386 func columnNorms(m, n int, a []float64, lda int) []float64 { 387 bi := blas64.Implementation() 388 norms := make([]float64, n) 389 for j := 0; j < n; j++ { 390 norms[j] = bi.Dnrm2(m, a[j:], lda) 391 } 392 return norms 393 } 394 395 // extractVMat collects the single reflectors from a into a matrix. 396 func extractVMat(m, n int, a []float64, lda int, direct lapack.Direct, store lapack.StoreV) blas64.General { 397 k := min(m, n) 398 switch { 399 default: 400 panic("not implemented") 401 case direct == lapack.Forward && store == lapack.ColumnWise: 402 v := blas64.General{ 403 Rows: m, 404 Cols: k, 405 Stride: k, 406 Data: make([]float64, m*k), 407 } 408 for i := 0; i < k; i++ { 409 for j := 0; j < i; j++ { 410 v.Data[j*v.Stride+i] = 0 411 } 412 v.Data[i*v.Stride+i] = 1 413 for j := i + 1; j < m; j++ { 414 v.Data[j*v.Stride+i] = a[j*lda+i] 415 } 416 } 417 return v 418 case direct == lapack.Forward && store == lapack.RowWise: 419 v := blas64.General{ 420 Rows: k, 421 Cols: n, 422 Stride: n, 423 Data: make([]float64, k*n), 424 } 425 for i := 0; i < k; i++ { 426 for j := 0; j < i; j++ { 427 v.Data[i*v.Stride+j] = 0 428 } 429 v.Data[i*v.Stride+i] = 1 430 for j := i + 1; j < n; j++ { 431 v.Data[i*v.Stride+j] = a[i*lda+j] 432 } 433 } 434 return v 435 } 436 } 437 438 // constructBidiagonal constructs a bidiagonal matrix with the given diagonal 439 // and off-diagonal elements. 440 func constructBidiagonal(uplo blas.Uplo, n int, d, e []float64) blas64.General { 441 bMat := blas64.General{ 442 Rows: n, 443 Cols: n, 444 Stride: n, 445 Data: make([]float64, n*n), 446 } 447 448 for i := 0; i < n-1; i++ { 449 bMat.Data[i*bMat.Stride+i] = d[i] 450 if uplo == blas.Upper { 451 bMat.Data[i*bMat.Stride+i+1] = e[i] 452 } else { 453 bMat.Data[(i+1)*bMat.Stride+i] = e[i] 454 } 455 } 456 bMat.Data[(n-1)*bMat.Stride+n-1] = d[n-1] 457 return bMat 458 } 459 460 // constructVMat transforms the v matrix based on the storage. 461 func constructVMat(vMat blas64.General, store lapack.StoreV, direct lapack.Direct) blas64.General { 462 m := vMat.Rows 463 k := vMat.Cols 464 switch { 465 default: 466 panic("not implemented") 467 case store == lapack.ColumnWise && direct == lapack.Forward: 468 ldv := k 469 v := make([]float64, m*k) 470 for i := 0; i < m; i++ { 471 for j := 0; j < k; j++ { 472 if j > i { 473 v[i*ldv+j] = 0 474 } else if j == i { 475 v[i*ldv+i] = 1 476 } else { 477 v[i*ldv+j] = vMat.Data[i*vMat.Stride+j] 478 } 479 } 480 } 481 return blas64.General{ 482 Rows: m, 483 Cols: k, 484 Stride: k, 485 Data: v, 486 } 487 case store == lapack.RowWise && direct == lapack.Forward: 488 ldv := m 489 v := make([]float64, m*k) 490 for i := 0; i < m; i++ { 491 for j := 0; j < k; j++ { 492 if j > i { 493 v[j*ldv+i] = 0 494 } else if j == i { 495 v[j*ldv+i] = 1 496 } else { 497 v[j*ldv+i] = vMat.Data[i*vMat.Stride+j] 498 } 499 } 500 } 501 return blas64.General{ 502 Rows: k, 503 Cols: m, 504 Stride: m, 505 Data: v, 506 } 507 case store == lapack.ColumnWise && direct == lapack.Backward: 508 rowsv := m 509 ldv := k 510 v := make([]float64, m*k) 511 for i := 0; i < m; i++ { 512 for j := 0; j < k; j++ { 513 vrow := rowsv - i - 1 514 vcol := k - j - 1 515 if j > i { 516 v[vrow*ldv+vcol] = 0 517 } else if j == i { 518 v[vrow*ldv+vcol] = 1 519 } else { 520 v[vrow*ldv+vcol] = vMat.Data[i*vMat.Stride+j] 521 } 522 } 523 } 524 return blas64.General{ 525 Rows: rowsv, 526 Cols: ldv, 527 Stride: ldv, 528 Data: v, 529 } 530 case store == lapack.RowWise && direct == lapack.Backward: 531 rowsv := k 532 ldv := m 533 v := make([]float64, m*k) 534 for i := 0; i < m; i++ { 535 for j := 0; j < k; j++ { 536 vcol := ldv - i - 1 537 vrow := k - j - 1 538 if j > i { 539 v[vrow*ldv+vcol] = 0 540 } else if j == i { 541 v[vrow*ldv+vcol] = 1 542 } else { 543 v[vrow*ldv+vcol] = vMat.Data[i*vMat.Stride+j] 544 } 545 } 546 } 547 return blas64.General{ 548 Rows: rowsv, 549 Cols: ldv, 550 Stride: ldv, 551 Data: v, 552 } 553 } 554 } 555 556 func constructH(tau []float64, v blas64.General, store lapack.StoreV, direct lapack.Direct) blas64.General { 557 m := v.Rows 558 k := v.Cols 559 if store == lapack.RowWise { 560 m, k = k, m 561 } 562 h := blas64.General{ 563 Rows: m, 564 Cols: m, 565 Stride: m, 566 Data: make([]float64, m*m), 567 } 568 for i := 0; i < m; i++ { 569 h.Data[i*m+i] = 1 570 } 571 for i := 0; i < k; i++ { 572 vecData := make([]float64, m) 573 if store == lapack.ColumnWise { 574 for j := 0; j < m; j++ { 575 vecData[j] = v.Data[j*v.Cols+i] 576 } 577 } else { 578 for j := 0; j < m; j++ { 579 vecData[j] = v.Data[i*v.Cols+j] 580 } 581 } 582 vec := blas64.Vector{ 583 Inc: 1, 584 Data: vecData, 585 } 586 587 hi := blas64.General{ 588 Rows: m, 589 Cols: m, 590 Stride: m, 591 Data: make([]float64, m*m), 592 } 593 for i := 0; i < m; i++ { 594 hi.Data[i*m+i] = 1 595 } 596 // hi = I - tau * v * vᵀ 597 blas64.Ger(-tau[i], vec, vec, hi) 598 599 hcopy := blas64.General{ 600 Rows: m, 601 Cols: m, 602 Stride: m, 603 Data: make([]float64, m*m), 604 } 605 copy(hcopy.Data, h.Data) 606 if direct == lapack.Forward { 607 // H = H * H_I in forward mode 608 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hcopy, hi, 0, h) 609 } else { 610 // H = H_I * H in backward mode 611 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hi, hcopy, 0, h) 612 } 613 } 614 return h 615 } 616 617 // constructQ constructs the Q matrix from the result of dgeqrf and dgeqr2. 618 func constructQ(kind string, m, n int, a []float64, lda int, tau []float64) blas64.General { 619 k := min(m, n) 620 return constructQK(kind, m, n, k, a, lda, tau) 621 } 622 623 // constructQK constructs the Q matrix from the result of dgeqrf and dgeqr2 using 624 // the first k reflectors. 625 func constructQK(kind string, m, n, k int, a []float64, lda int, tau []float64) blas64.General { 626 var sz int 627 switch kind { 628 case "QR": 629 sz = m 630 case "LQ", "RQ": 631 sz = n 632 } 633 634 q := blas64.General{ 635 Rows: sz, 636 Cols: sz, 637 Stride: max(1, sz), 638 Data: make([]float64, sz*sz), 639 } 640 for i := 0; i < sz; i++ { 641 q.Data[i*sz+i] = 1 642 } 643 qCopy := blas64.General{ 644 Rows: q.Rows, 645 Cols: q.Cols, 646 Stride: q.Stride, 647 Data: make([]float64, len(q.Data)), 648 } 649 for i := 0; i < k; i++ { 650 h := blas64.General{ 651 Rows: sz, 652 Cols: sz, 653 Stride: max(1, sz), 654 Data: make([]float64, sz*sz), 655 } 656 for j := 0; j < sz; j++ { 657 h.Data[j*sz+j] = 1 658 } 659 vVec := blas64.Vector{ 660 Inc: 1, 661 Data: make([]float64, sz), 662 } 663 switch kind { 664 case "QR": 665 vVec.Data[i] = 1 666 for j := i + 1; j < sz; j++ { 667 vVec.Data[j] = a[lda*j+i] 668 } 669 case "LQ": 670 vVec.Data[i] = 1 671 for j := i + 1; j < sz; j++ { 672 vVec.Data[j] = a[i*lda+j] 673 } 674 case "RQ": 675 for j := 0; j < n-k+i; j++ { 676 vVec.Data[j] = a[(m-k+i)*lda+j] 677 } 678 vVec.Data[n-k+i] = 1 679 } 680 blas64.Ger(-tau[i], vVec, vVec, h) 681 copy(qCopy.Data, q.Data) 682 // Multiply q by the new h. 683 switch kind { 684 case "QR", "RQ": 685 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, h, 0, q) 686 case "LQ": 687 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, h, qCopy, 0, q) 688 } 689 } 690 return q 691 } 692 693 // checkBidiagonal checks the bidiagonal decomposition from dlabrd and dgebd2. 694 // The input to this function is the answer returned from the routines, stored 695 // in a, d, e, tauP, and tauQ. The data of original A matrix (before 696 // decomposition) is input in aCopy. 697 // 698 // checkBidiagonal constructs the V and U matrices, and from them constructs Q 699 // and P. Using these constructions, it checks that Qᵀ * A * P and checks that 700 // the result is bidiagonal. 701 func checkBidiagonal(t *testing.T, m, n, nb int, a []float64, lda int, d, e, tauP, tauQ, aCopy []float64) { 702 // Check the answer. 703 // Construct V and U. 704 qMat := constructQPBidiagonal(lapack.ApplyQ, m, n, nb, a, lda, tauQ) 705 pMat := constructQPBidiagonal(lapack.ApplyP, m, n, nb, a, lda, tauP) 706 707 // Compute Qᵀ * A * P. 708 aMat := blas64.General{ 709 Rows: m, 710 Cols: n, 711 Stride: lda, 712 Data: make([]float64, len(aCopy)), 713 } 714 copy(aMat.Data, aCopy) 715 716 tmp1 := blas64.General{ 717 Rows: m, 718 Cols: n, 719 Stride: n, 720 Data: make([]float64, m*n), 721 } 722 blas64.Gemm(blas.Trans, blas.NoTrans, 1, qMat, aMat, 0, tmp1) 723 tmp2 := blas64.General{ 724 Rows: m, 725 Cols: n, 726 Stride: n, 727 Data: make([]float64, m*n), 728 } 729 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, tmp1, pMat, 0, tmp2) 730 731 // Check that the first nb rows and cols of tm2 are upper bidiagonal 732 // if m >= n, and lower bidiagonal otherwise. 733 correctDiag := true 734 matchD := true 735 matchE := true 736 for i := 0; i < m; i++ { 737 for j := 0; j < n; j++ { 738 if i >= nb && j >= nb { 739 continue 740 } 741 v := tmp2.Data[i*tmp2.Stride+j] 742 if i == j { 743 if math.Abs(d[i]-v) > 1e-12 { 744 matchD = false 745 } 746 continue 747 } 748 if m >= n && i == j-1 { 749 if math.Abs(e[j-1]-v) > 1e-12 { 750 matchE = false 751 } 752 continue 753 } 754 if m < n && i-1 == j { 755 if math.Abs(e[i-1]-v) > 1e-12 { 756 matchE = false 757 } 758 continue 759 } 760 if math.Abs(v) > 1e-12 { 761 correctDiag = false 762 } 763 } 764 } 765 if !correctDiag { 766 t.Errorf("Updated A not bi-diagonal") 767 } 768 if !matchD { 769 fmt.Println("d = ", d) 770 t.Errorf("D Mismatch") 771 } 772 if !matchE { 773 t.Errorf("E mismatch") 774 } 775 } 776 777 // constructQPBidiagonal constructs Q or P from the Bidiagonal decomposition 778 // computed by dlabrd and bgebd2. 779 func constructQPBidiagonal(vect lapack.ApplyOrtho, m, n, nb int, a []float64, lda int, tau []float64) blas64.General { 780 sz := n 781 if vect == lapack.ApplyQ { 782 sz = m 783 } 784 785 var ldv int 786 var v blas64.General 787 if vect == lapack.ApplyQ { 788 ldv = nb 789 v = blas64.General{ 790 Rows: m, 791 Cols: nb, 792 Stride: ldv, 793 Data: make([]float64, m*ldv), 794 } 795 } else { 796 ldv = n 797 v = blas64.General{ 798 Rows: nb, 799 Cols: n, 800 Stride: ldv, 801 Data: make([]float64, m*ldv), 802 } 803 } 804 805 if vect == lapack.ApplyQ { 806 if m >= n { 807 for i := 0; i < m; i++ { 808 for j := 0; j <= min(nb-1, i); j++ { 809 if i == j { 810 v.Data[i*ldv+j] = 1 811 continue 812 } 813 v.Data[i*ldv+j] = a[i*lda+j] 814 } 815 } 816 } else { 817 for i := 1; i < m; i++ { 818 for j := 0; j <= min(nb-1, i-1); j++ { 819 if i-1 == j { 820 v.Data[i*ldv+j] = 1 821 continue 822 } 823 v.Data[i*ldv+j] = a[i*lda+j] 824 } 825 } 826 } 827 } else { 828 if m < n { 829 for i := 0; i < nb; i++ { 830 for j := i; j < n; j++ { 831 if i == j { 832 v.Data[i*ldv+j] = 1 833 continue 834 } 835 v.Data[i*ldv+j] = a[i*lda+j] 836 } 837 } 838 } else { 839 for i := 0; i < nb; i++ { 840 for j := i + 1; j < n; j++ { 841 if j-1 == i { 842 v.Data[i*ldv+j] = 1 843 continue 844 } 845 v.Data[i*ldv+j] = a[i*lda+j] 846 } 847 } 848 } 849 } 850 851 // The variable name is a computation of Q, but the algorithm is mostly the 852 // same for computing P (just with different data). 853 qMat := blas64.General{ 854 Rows: sz, 855 Cols: sz, 856 Stride: sz, 857 Data: make([]float64, sz*sz), 858 } 859 hMat := blas64.General{ 860 Rows: sz, 861 Cols: sz, 862 Stride: sz, 863 Data: make([]float64, sz*sz), 864 } 865 // set Q to I 866 for i := 0; i < sz; i++ { 867 qMat.Data[i*qMat.Stride+i] = 1 868 } 869 for i := 0; i < nb; i++ { 870 qCopy := blas64.General{Rows: qMat.Rows, Cols: qMat.Cols, Stride: qMat.Stride, Data: make([]float64, len(qMat.Data))} 871 copy(qCopy.Data, qMat.Data) 872 873 // Set g and h to I 874 for i := 0; i < sz; i++ { 875 for j := 0; j < sz; j++ { 876 if i == j { 877 hMat.Data[i*sz+j] = 1 878 } else { 879 hMat.Data[i*sz+j] = 0 880 } 881 } 882 } 883 var vi blas64.Vector 884 // H -= tauQ[i] * v[i] * v[i]^t 885 if vect == lapack.ApplyQ { 886 vi = blas64.Vector{ 887 Inc: v.Stride, 888 Data: v.Data[i:], 889 } 890 } else { 891 vi = blas64.Vector{ 892 Inc: 1, 893 Data: v.Data[i*v.Stride:], 894 } 895 } 896 blas64.Ger(-tau[i], vi, vi, hMat) 897 // Q = Q * G[1] 898 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, hMat, 0, qMat) 899 } 900 return qMat 901 } 902 903 // printRowise prints the matrix with one row per line. This is useful for debugging. 904 // If beyond is true, it prints beyond the final column to lda. If false, only 905 // the columns are printed. 906 // 907 //lint:ignore U1000 This is useful for debugging. 908 func printRowise(a []float64, m, n, lda int, beyond bool) { 909 for i := 0; i < m; i++ { 910 end := n 911 if beyond { 912 end = lda 913 } 914 fmt.Println(a[i*lda : i*lda+end]) 915 } 916 } 917 918 func copyGeneral(dst, src blas64.General) { 919 r := min(dst.Rows, src.Rows) 920 c := min(dst.Cols, src.Cols) 921 for i := 0; i < r; i++ { 922 copy(dst.Data[i*dst.Stride:i*dst.Stride+c], src.Data[i*src.Stride:i*src.Stride+c]) 923 } 924 } 925 926 // cloneGeneral allocates and returns an exact copy of the given general matrix. 927 func cloneGeneral(a blas64.General) blas64.General { 928 c := a 929 c.Data = make([]float64, len(a.Data)) 930 copy(c.Data, a.Data) 931 return c 932 } 933 934 // equalGeneral returns whether the general matrices a and b are equal. 935 func equalGeneral(a, b blas64.General) bool { 936 if a.Rows != b.Rows || a.Cols != b.Cols { 937 panic("bad input") 938 } 939 for i := 0; i < a.Rows; i++ { 940 for j := 0; j < a.Cols; j++ { 941 if a.Data[i*a.Stride+j] != b.Data[i*b.Stride+j] { 942 return false 943 } 944 } 945 } 946 return true 947 } 948 949 // equalApproxGeneral returns whether the general matrices a and b are 950 // approximately equal within given tolerance. 951 func equalApproxGeneral(a, b blas64.General, tol float64) bool { 952 if a.Rows != b.Rows || a.Cols != b.Cols { 953 panic("bad input") 954 } 955 for i := 0; i < a.Rows; i++ { 956 for j := 0; j < a.Cols; j++ { 957 diff := a.Data[i*a.Stride+j] - b.Data[i*b.Stride+j] 958 if math.IsNaN(diff) || math.Abs(diff) > tol { 959 return false 960 } 961 } 962 } 963 return true 964 } 965 966 func intsEqual(a, b []int) bool { 967 if len(a) != len(b) { 968 return false 969 } 970 for i, ai := range a { 971 if b[i] != ai { 972 return false 973 } 974 } 975 return true 976 } 977 978 // randSymBand returns an n×n random symmetric positive definite band matrix 979 // with kd diagonals. 980 func randSymBand(uplo blas.Uplo, n, kd, ldab int, rnd *rand.Rand) []float64 { 981 // Allocate a triangular band matrix U or L and fill it with random numbers. 982 var ab []float64 983 if n > 0 { 984 ab = make([]float64, (n-1)*ldab+kd+1) 985 } 986 for i := range ab { 987 ab[i] = rnd.NormFloat64() 988 } 989 // Make sure that the matrix U or L has a sufficiently positive diagonal. 990 switch uplo { 991 case blas.Upper: 992 for i := 0; i < n; i++ { 993 ab[i*ldab] = float64(n) + rnd.Float64() 994 } 995 case blas.Lower: 996 for i := 0; i < n; i++ { 997 ab[i*ldab+kd] = float64(n) + rnd.Float64() 998 } 999 } 1000 // Compute Uᵀ*U or L*Lᵀ. The resulting (symmetric) matrix A will be 1001 // positive definite and well-conditioned. 1002 dsbmm(uplo, n, kd, ab, ldab) 1003 return ab 1004 } 1005 1006 // distSymBand returns the max-norm distance between the symmetric band matrices 1007 // A and B. 1008 func distSymBand(uplo blas.Uplo, n, kd int, a []float64, lda int, b []float64, ldb int) float64 { 1009 var dist float64 1010 switch uplo { 1011 case blas.Upper: 1012 for i := 0; i < n; i++ { 1013 for j := 0; j < min(kd+1, n-i); j++ { 1014 dist = math.Max(dist, math.Abs(a[i*lda+j]-b[i*ldb+j])) 1015 } 1016 } 1017 case blas.Lower: 1018 for i := 0; i < n; i++ { 1019 for j := max(0, kd-i); j < kd+1; j++ { 1020 dist = math.Max(dist, math.Abs(a[i*lda+j]-b[i*ldb+j])) 1021 } 1022 } 1023 } 1024 return dist 1025 } 1026 1027 // eye returns an identity matrix of given order and stride. 1028 func eye(n, stride int) blas64.General { 1029 ans := nanGeneral(n, n, stride) 1030 for i := 0; i < n; i++ { 1031 for j := 0; j < n; j++ { 1032 ans.Data[i*ans.Stride+j] = 0 1033 } 1034 ans.Data[i*ans.Stride+i] = 1 1035 } 1036 return ans 1037 } 1038 1039 // zeros returns an m×n matrix with given stride filled with zeros. 1040 func zeros(m, n, stride int) blas64.General { 1041 a := nanGeneral(m, n, stride) 1042 for i := 0; i < m; i++ { 1043 for j := 0; j < n; j++ { 1044 a.Data[i*a.Stride+j] = 0 1045 } 1046 } 1047 return a 1048 } 1049 1050 // extract2x2Block returns the elements of T at [0,0], [0,1], [1,0], and [1,1]. 1051 func extract2x2Block(t []float64, ldt int) (a, b, c, d float64) { 1052 return t[0], t[1], t[ldt], t[ldt+1] 1053 } 1054 1055 // isSchurCanonical returns whether the 2×2 matrix [a b; c d] is in Schur 1056 // canonical form. 1057 func isSchurCanonical(a, b, c, d float64) bool { 1058 return c == 0 || (b != 0 && a == d && math.Signbit(b) != math.Signbit(c)) 1059 } 1060 1061 // isSchurCanonicalGeneral returns whether T is block upper triangular with 1×1 1062 // and 2×2 diagonal blocks, each 2×2 block in Schur canonical form. The function 1063 // checks only along the diagonal and the first subdiagonal, otherwise the lower 1064 // triangle is not accessed. 1065 func isSchurCanonicalGeneral(t blas64.General) bool { 1066 n := t.Cols 1067 if t.Rows != n { 1068 panic("invalid matrix") 1069 } 1070 for j := 0; j < n-1; { 1071 if t.Data[(j+1)*t.Stride+j] == 0 { 1072 // 1×1 block. 1073 for i := j + 1; i < n; i++ { 1074 if t.Data[i*t.Stride+j] != 0 { 1075 return false 1076 } 1077 } 1078 j++ 1079 continue 1080 } 1081 // 2×2 block. 1082 a, b, c, d := extract2x2Block(t.Data[j*t.Stride+j:], t.Stride) 1083 if !isSchurCanonical(a, b, c, d) { 1084 return false 1085 } 1086 for i := j + 2; i < n; i++ { 1087 if t.Data[i*t.Stride+j] != 0 { 1088 return false 1089 } 1090 } 1091 for i := j + 2; i < n; i++ { 1092 if t.Data[i*t.Stride+j+1] != 0 { 1093 return false 1094 } 1095 } 1096 j += 2 1097 } 1098 return true 1099 } 1100 1101 // schurBlockEigenvalues returns the two eigenvalues of the 2×2 matrix [a b; c d] 1102 // that must be in Schur canonical form. 1103 // 1104 //lint:ignore U1000 This is useful for debugging. 1105 func schurBlockEigenvalues(a, b, c, d float64) (ev1, ev2 complex128) { 1106 if !isSchurCanonical(a, b, c, d) { 1107 panic("block not in Schur canonical form") 1108 } 1109 if c == 0 { 1110 return complex(a, 0), complex(d, 0) 1111 } 1112 im := math.Sqrt(math.Abs(b)) * math.Sqrt(math.Abs(c)) 1113 return complex(a, im), complex(a, -im) 1114 } 1115 1116 // schurBlockSize returns the size of the diagonal block at i-th row in the 1117 // upper quasi-triangular matrix t in Schur canonical form, and whether i points 1118 // to the first row of the block. For zero-sized matrices the function returns 0 1119 // and true. 1120 func schurBlockSize(t blas64.General, i int) (size int, first bool) { 1121 if t.Rows != t.Cols { 1122 panic("matrix not square") 1123 } 1124 if t.Rows == 0 { 1125 return 0, true 1126 } 1127 if i < 0 || t.Rows <= i { 1128 panic("index out of range") 1129 } 1130 1131 first = true 1132 if i > 0 && t.Data[i*t.Stride+i-1] != 0 { 1133 // There is a non-zero element to the left, therefore i must 1134 // point to the second row in a 2×2 diagonal block. 1135 first = false 1136 i-- 1137 } 1138 size = 1 1139 if i+1 < t.Rows && t.Data[(i+1)*t.Stride+i] != 0 { 1140 // There is a non-zero element below, this must be a 2×2 1141 // diagonal block. 1142 size = 2 1143 } 1144 return size, first 1145 } 1146 1147 // containsComplex returns whether z is approximately equal to one of the complex 1148 // numbers in v. If z is found, its index in v will be also returned. 1149 func containsComplex(v []complex128, z complex128, tol float64) (found bool, index int) { 1150 for i := range v { 1151 if cmplx.Abs(v[i]-z) < tol { 1152 return true, i 1153 } 1154 } 1155 return false, -1 1156 } 1157 1158 // isAllNaN returns whether x contains only NaN values. 1159 func isAllNaN(x []float64) bool { 1160 for _, v := range x { 1161 if !math.IsNaN(v) { 1162 return false 1163 } 1164 } 1165 return true 1166 } 1167 1168 // isUpperHessenberg returns whether h contains only zeros below the 1169 // subdiagonal. 1170 func isUpperHessenberg(h blas64.General) bool { 1171 if h.Rows != h.Cols { 1172 panic("matrix not square") 1173 } 1174 n := h.Rows 1175 for i := 0; i < n; i++ { 1176 for j := 0; j < n; j++ { 1177 if i > j+1 && h.Data[i*h.Stride+j] != 0 { 1178 return false 1179 } 1180 } 1181 } 1182 return true 1183 } 1184 1185 // isUpperTriangular returns whether a contains only zeros below the diagonal. 1186 func isUpperTriangular(a blas64.General) bool { 1187 if a.Rows != a.Cols { 1188 panic("matrix not square") 1189 } 1190 n := a.Rows 1191 for i := 1; i < n; i++ { 1192 for j := 0; j < i; j++ { 1193 if a.Data[i*a.Stride+j] != 0 { 1194 return false 1195 } 1196 } 1197 } 1198 return true 1199 } 1200 1201 // unbalancedSparseGeneral returns an m×n dense matrix with a random sparse 1202 // structure consisting of nz nonzero elements. The matrix will be unbalanced by 1203 // multiplying each element randomly by its row or column index. 1204 func unbalancedSparseGeneral(m, n, stride int, nonzeros int, rnd *rand.Rand) blas64.General { 1205 a := zeros(m, n, stride) 1206 for k := 0; k < nonzeros; k++ { 1207 i := rnd.Intn(n) 1208 j := rnd.Intn(n) 1209 if rnd.Float64() < 0.5 { 1210 a.Data[i*stride+j] = float64(i+1) * rnd.NormFloat64() 1211 } else { 1212 a.Data[i*stride+j] = float64(j+1) * rnd.NormFloat64() 1213 } 1214 } 1215 return a 1216 } 1217 1218 // rootsOfUnity returns the n complex numbers whose n-th power is equal to 1. 1219 func rootsOfUnity(n int) []complex128 { 1220 w := make([]complex128, n) 1221 for i := 0; i < n; i++ { 1222 angle := math.Pi * float64(2*i) / float64(n) 1223 w[i] = complex(math.Cos(angle), math.Sin(angle)) 1224 } 1225 return w 1226 } 1227 1228 // constructGSVDresults returns the matrices [ 0 R ], D1 and D2 described 1229 // in the documentation of Dtgsja and Dggsvd3, and the result matrix in 1230 // the documentation for Dggsvp3. 1231 func constructGSVDresults(n, p, m, k, l int, a, b blas64.General, alpha, beta []float64) (zeroR, d1, d2 blas64.General) { 1232 // [ 0 R ] 1233 zeroR = zeros(k+l, n, n) 1234 dst := zeroR 1235 dst.Rows = min(m, k+l) 1236 dst.Cols = k + l 1237 dst.Data = zeroR.Data[n-k-l:] 1238 src := a 1239 src.Rows = min(m, k+l) 1240 src.Cols = k + l 1241 src.Data = a.Data[n-k-l:] 1242 copyGeneral(dst, src) 1243 if m < k+l { 1244 // [ 0 R ] 1245 dst.Rows = k + l - m 1246 dst.Cols = k + l - m 1247 dst.Data = zeroR.Data[m*zeroR.Stride+n-(k+l-m):] 1248 src = b 1249 src.Rows = k + l - m 1250 src.Cols = k + l - m 1251 src.Data = b.Data[(m-k)*b.Stride+n+m-k-l:] 1252 copyGeneral(dst, src) 1253 } 1254 1255 // D1 1256 d1 = zeros(m, k+l, k+l) 1257 for i := 0; i < k; i++ { 1258 d1.Data[i*d1.Stride+i] = 1 1259 } 1260 for i := k; i < min(m, k+l); i++ { 1261 d1.Data[i*d1.Stride+i] = alpha[i] 1262 } 1263 1264 // D2 1265 d2 = zeros(p, k+l, k+l) 1266 for i := 0; i < min(l, m-k); i++ { 1267 d2.Data[i*d2.Stride+i+k] = beta[k+i] 1268 } 1269 for i := m - k; i < l; i++ { 1270 d2.Data[i*d2.Stride+i+k] = 1 1271 } 1272 1273 return zeroR, d1, d2 1274 } 1275 1276 func constructGSVPresults(n, p, m, k, l int, a, b blas64.General) (zeroA, zeroB blas64.General) { 1277 zeroA = zeros(m, n, n) 1278 dst := zeroA 1279 dst.Rows = min(m, k+l) 1280 dst.Cols = k + l 1281 dst.Data = zeroA.Data[n-k-l:] 1282 src := a 1283 dst.Rows = min(m, k+l) 1284 src.Cols = k + l 1285 src.Data = a.Data[n-k-l:] 1286 copyGeneral(dst, src) 1287 1288 zeroB = zeros(p, n, n) 1289 dst = zeroB 1290 dst.Rows = l 1291 dst.Cols = l 1292 dst.Data = zeroB.Data[n-l:] 1293 src = b 1294 dst.Rows = l 1295 src.Cols = l 1296 src.Data = b.Data[n-l:] 1297 copyGeneral(dst, src) 1298 1299 return zeroA, zeroB 1300 } 1301 1302 // distFromIdentity returns the L-infinity distance of an n×n matrix A from the 1303 // identity. If A contains NaN elements, distFromIdentity will return +inf. 1304 func distFromIdentity(n int, a []float64, lda int) float64 { 1305 var dist float64 1306 for i := 0; i < n; i++ { 1307 for j := 0; j < n; j++ { 1308 aij := a[i*lda+j] 1309 if math.IsNaN(aij) { 1310 return math.Inf(1) 1311 } 1312 if i == j { 1313 dist = math.Max(dist, math.Abs(aij-1)) 1314 } else { 1315 dist = math.Max(dist, math.Abs(aij)) 1316 } 1317 } 1318 } 1319 return dist 1320 } 1321 1322 func sameFloat64(a, b float64) bool { 1323 return a == b || math.IsNaN(a) && math.IsNaN(b) 1324 } 1325 1326 // sameLowerTri returns whether n×n matrices A and B are same under the diagonal. 1327 func sameLowerTri(n int, a []float64, lda int, b []float64, ldb int) bool { 1328 for i := 1; i < n; i++ { 1329 for j := 0; j < i; j++ { 1330 aij := a[i*lda+j] 1331 bij := b[i*ldb+j] 1332 if !sameFloat64(aij, bij) { 1333 return false 1334 } 1335 } 1336 } 1337 return true 1338 } 1339 1340 // sameUpperTri returns whether n×n matrices A and B are same above the diagonal. 1341 func sameUpperTri(n int, a []float64, lda int, b []float64, ldb int) bool { 1342 for i := 0; i < n-1; i++ { 1343 for j := i + 1; j < n; j++ { 1344 aij := a[i*lda+j] 1345 bij := b[i*ldb+j] 1346 if !sameFloat64(aij, bij) { 1347 return false 1348 } 1349 } 1350 } 1351 return true 1352 } 1353 1354 // svdJobString returns a string representation of job. 1355 func svdJobString(job lapack.SVDJob) string { 1356 switch job { 1357 case lapack.SVDAll: 1358 return "All" 1359 case lapack.SVDStore: 1360 return "Store" 1361 case lapack.SVDOverwrite: 1362 return "Overwrite" 1363 case lapack.SVDNone: 1364 return "None" 1365 } 1366 return "unknown SVD job" 1367 } 1368 1369 // residualOrthogonal returns the residual 1370 // 1371 // |I - Q * Qᵀ| if m < n or (m == n and rowwise == true), 1372 // |I - Qᵀ * Q| otherwise. 1373 // 1374 // It can be used to check that the matrix Q is orthogonal. 1375 func residualOrthogonal(q blas64.General, rowwise bool) float64 { 1376 m, n := q.Rows, q.Cols 1377 if m == 0 || n == 0 { 1378 return 0 1379 } 1380 var transq blas.Transpose 1381 if m < n || (m == n && rowwise) { 1382 transq = blas.NoTrans 1383 } else { 1384 transq = blas.Trans 1385 } 1386 minmn := min(m, n) 1387 1388 // Set work = I. 1389 work := blas64.Symmetric{ 1390 Uplo: blas.Upper, 1391 N: minmn, 1392 Data: make([]float64, minmn*minmn), 1393 Stride: minmn, 1394 } 1395 for i := 0; i < minmn; i++ { 1396 work.Data[i*work.Stride+i] = 1 1397 } 1398 1399 // Compute 1400 // work = work - Q * Qᵀ = I - Q * Qᵀ 1401 // or 1402 // work = work - Qᵀ * Q = I - Qᵀ * Q 1403 blas64.Syrk(transq, -1, q, 1, work) 1404 return dlansy(lapack.MaxColumnSum, blas.Upper, work.N, work.Data, work.Stride) 1405 }