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