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.
13 "golang.org/x/exp/rand"
15 "gonum.org/v1/gonum/blas"
16 "gonum.org/v1/gonum/blas/blas64"
17 "gonum.org/v1/gonum/floats"
18 "gonum.org/v1/gonum/lapack"
22 // dlamchE is the machine epsilon. For IEEE this is 2^{-53}.
23 dlamchE = 1.0 / (1 << 53)
25 dlamchP = dlamchB * dlamchE
26 // dlamchS is the smallest normal number. For IEEE this is 2^{-1022}.
27 dlamchS = 1.0 / (1 << 256) / (1 << 256) / (1 << 256) / (1 << 254)
30 func max(a, b int) int {
37 func min(a, b int) int {
44 // worklen describes how much workspace a test should use.
48 minimumWork worklen = iota
53 // nanSlice allocates a new slice of length n filled with NaN.
54 func nanSlice(n int) []float64 {
55 s := make([]float64, n)
62 // randomSlice allocates a new slice of length n filled with random values.
63 func randomSlice(n int, rnd *rand.Rand) []float64 {
64 s := make([]float64, n)
66 s[i] = rnd.NormFloat64()
71 // nanGeneral allocates a new r×c general matrix filled with NaN values.
72 func nanGeneral(r, c, stride int) blas64.General {
74 panic("bad matrix size")
77 return blas64.General{Stride: max(1, stride)}
82 return blas64.General{
86 Data: nanSlice((r-1)*stride + c),
90 // randomGeneral allocates a new r×c general matrix filled with random
91 // numbers. Out-of-range elements are filled with NaN values.
92 func randomGeneral(r, c, stride int, rnd *rand.Rand) blas64.General {
93 ans := nanGeneral(r, c, stride)
94 for i := 0; i < r; i++ {
95 for j := 0; j < c; j++ {
96 ans.Data[i*ans.Stride+j] = rnd.NormFloat64()
102 // randomHessenberg allocates a new n×n Hessenberg matrix filled with zeros
103 // under the first subdiagonal and with random numbers elsewhere. Out-of-range
104 // elements are filled with NaN values.
105 func randomHessenberg(n, stride int, rnd *rand.Rand) blas64.General {
106 ans := nanGeneral(n, n, stride)
107 for i := 0; i < n; i++ {
108 for j := 0; j < i-1; j++ {
109 ans.Data[i*ans.Stride+j] = 0
111 for j := max(0, i-1); j < n; j++ {
112 ans.Data[i*ans.Stride+j] = rnd.NormFloat64()
118 // randomSchurCanonical returns a random, general matrix in Schur canonical
119 // form, that is, block upper triangular with 1×1 and 2×2 diagonal blocks where
120 // each 2×2 diagonal block has its diagonal elements equal and its off-diagonal
121 // elements of opposite sign.
122 func randomSchurCanonical(n, stride int, rnd *rand.Rand) blas64.General {
123 t := randomGeneral(n, n, stride, rnd)
124 // Zero out the lower triangle.
125 for i := 0; i < t.Rows; i++ {
126 for j := 0; j < i; j++ {
127 t.Data[i*t.Stride+j] = 0
130 // Randomly create 2×2 diagonal blocks.
131 for i := 0; i < t.Rows; {
132 if i == t.Rows-1 || rnd.Float64() < 0.5 {
138 // Diagonal elements equal.
139 t.Data[(i+1)*t.Stride+i+1] = t.Data[i*t.Stride+i]
140 // Off-diagonal elements of opposite sign.
141 c := rnd.NormFloat64()
142 if math.Signbit(c) == math.Signbit(t.Data[i*t.Stride+i+1]) {
145 t.Data[(i+1)*t.Stride+i] = c
151 // blockedUpperTriGeneral returns a normal random, general matrix in the form
154 // A = k [ 0 A12 A13 ] if r-k-l >= 0;
159 // A = k [ 0 A12 A13 ] if r-k-l < 0;
162 // where the k×k matrix A12 and l×l matrix is non-singular
163 // upper triangular. A23 is l×l upper triangular if r-k-l >= 0,
164 // otherwise A23 is (r-k)×l upper trapezoidal.
165 func blockedUpperTriGeneral(r, c, k, l, stride int, kblock bool, rnd *rand.Rand) blas64.General {
170 ans := zeros(r, c, stride)
171 for i := 0; i < min(r, t); i++ {
174 v = rnd.NormFloat64()
176 ans.Data[i*ans.Stride+i+(c-t)] = v
178 for i := 0; i < min(r, t); i++ {
179 for j := i + (c - t) + 1; j < c; j++ {
180 ans.Data[i*ans.Stride+j] = rnd.NormFloat64()
186 // nanTriangular allocates a new r×c triangular matrix filled with NaN values.
187 func nanTriangular(uplo blas.Uplo, n, stride int) blas64.Triangular {
189 panic("bad matrix size")
192 return blas64.Triangular{
193 Stride: max(1, stride),
201 return blas64.Triangular{
204 Data: nanSlice((n-1)*stride + n),
210 // generalOutsideAllNaN returns whether all out-of-range elements have NaN
212 func generalOutsideAllNaN(a blas64.General) bool {
213 // Check after last column.
214 for i := 0; i < a.Rows-1; i++ {
215 for _, v := range a.Data[i*a.Stride+a.Cols : i*a.Stride+a.Stride] {
221 // Check after last element.
222 last := (a.Rows-1)*a.Stride + a.Cols
223 if a.Rows == 0 || a.Cols == 0 {
226 for _, v := range a.Data[last:] {
234 // triangularOutsideAllNaN returns whether all out-of-triangle elements have NaN
236 func triangularOutsideAllNaN(a blas64.Triangular) bool {
237 if a.Uplo == blas.Upper {
238 // Check below diagonal.
239 for i := 0; i < a.N; i++ {
240 for _, v := range a.Data[i*a.Stride : i*a.Stride+i] {
246 // Check after last column.
247 for i := 0; i < a.N-1; i++ {
248 for _, v := range a.Data[i*a.Stride+a.N : i*a.Stride+a.Stride] {
255 // Check above diagonal.
256 for i := 0; i < a.N-1; i++ {
257 for _, v := range a.Data[i*a.Stride+i+1 : i*a.Stride+a.Stride] {
264 // Check after last element.
265 for _, v := range a.Data[max(0, a.N-1)*a.Stride+a.N:] {
273 // transposeGeneral returns a new general matrix that is the transpose of the
274 // input. Nothing is done with data outside the {rows, cols} limit of the general.
275 func transposeGeneral(a blas64.General) blas64.General {
276 ans := blas64.General{
280 Data: make([]float64, a.Cols*a.Rows),
282 for i := 0; i < a.Rows; i++ {
283 for j := 0; j < a.Cols; j++ {
284 ans.Data[j*ans.Stride+i] = a.Data[i*a.Stride+j]
290 // columnNorms returns the column norms of a.
291 func columnNorms(m, n int, a []float64, lda int) []float64 {
292 bi := blas64.Implementation()
293 norms := make([]float64, n)
294 for j := 0; j < n; j++ {
295 norms[j] = bi.Dnrm2(m, a[j:], lda)
300 // extractVMat collects the single reflectors from a into a matrix.
301 func extractVMat(m, n int, a []float64, lda int, direct lapack.Direct, store lapack.StoreV) blas64.General {
305 panic("not implemented")
306 case direct == lapack.Forward && store == lapack.ColumnWise:
311 Data: make([]float64, m*k),
313 for i := 0; i < k; i++ {
314 for j := 0; j < i; j++ {
315 v.Data[j*v.Stride+i] = 0
317 v.Data[i*v.Stride+i] = 1
318 for j := i + 1; j < m; j++ {
319 v.Data[j*v.Stride+i] = a[j*lda+i]
323 case direct == lapack.Forward && store == lapack.RowWise:
328 Data: make([]float64, k*n),
330 for i := 0; i < k; i++ {
331 for j := 0; j < i; j++ {
332 v.Data[i*v.Stride+j] = 0
334 v.Data[i*v.Stride+i] = 1
335 for j := i + 1; j < n; j++ {
336 v.Data[i*v.Stride+j] = a[i*lda+j]
343 // constructBidiagonal constructs a bidiagonal matrix with the given diagonal
344 // and off-diagonal elements.
345 func constructBidiagonal(uplo blas.Uplo, n int, d, e []float64) blas64.General {
346 bMat := blas64.General{
350 Data: make([]float64, n*n),
353 for i := 0; i < n-1; i++ {
354 bMat.Data[i*bMat.Stride+i] = d[i]
355 if uplo == blas.Upper {
356 bMat.Data[i*bMat.Stride+i+1] = e[i]
358 bMat.Data[(i+1)*bMat.Stride+i] = e[i]
361 bMat.Data[(n-1)*bMat.Stride+n-1] = d[n-1]
365 // constructVMat transforms the v matrix based on the storage.
366 func constructVMat(vMat blas64.General, store lapack.StoreV, direct lapack.Direct) blas64.General {
371 panic("not implemented")
372 case store == lapack.ColumnWise && direct == lapack.Forward:
374 v := make([]float64, m*k)
375 for i := 0; i < m; i++ {
376 for j := 0; j < k; j++ {
382 v[i*ldv+j] = vMat.Data[i*vMat.Stride+j]
386 return blas64.General{
392 case store == lapack.RowWise && direct == lapack.Forward:
394 v := make([]float64, m*k)
395 for i := 0; i < m; i++ {
396 for j := 0; j < k; j++ {
402 v[j*ldv+i] = vMat.Data[i*vMat.Stride+j]
406 return blas64.General{
412 case store == lapack.ColumnWise && direct == lapack.Backward:
415 v := make([]float64, m*k)
416 for i := 0; i < m; i++ {
417 for j := 0; j < k; j++ {
418 vrow := rowsv - i - 1
425 v[vrow*ldv+vcol] = vMat.Data[i*vMat.Stride+j]
429 return blas64.General{
435 case store == lapack.RowWise && direct == lapack.Backward:
438 v := make([]float64, m*k)
439 for i := 0; i < m; i++ {
440 for j := 0; j < k; j++ {
448 v[vrow*ldv+vcol] = vMat.Data[i*vMat.Stride+j]
452 return blas64.General{
461 func constructH(tau []float64, v blas64.General, store lapack.StoreV, direct lapack.Direct) blas64.General {
464 if store == lapack.RowWise {
471 Data: make([]float64, m*m),
473 for i := 0; i < m; i++ {
476 for i := 0; i < k; i++ {
477 vecData := make([]float64, m)
478 if store == lapack.ColumnWise {
479 for j := 0; j < m; j++ {
480 vecData[j] = v.Data[j*v.Cols+i]
483 for j := 0; j < m; j++ {
484 vecData[j] = v.Data[i*v.Cols+j]
487 vec := blas64.Vector{
492 hi := blas64.General{
496 Data: make([]float64, m*m),
498 for i := 0; i < m; i++ {
501 // hi = I - tau * v * v^T
502 blas64.Ger(-tau[i], vec, vec, hi)
504 hcopy := blas64.General{
508 Data: make([]float64, m*m),
510 copy(hcopy.Data, h.Data)
511 if direct == lapack.Forward {
512 // H = H * H_I in forward mode
513 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hcopy, hi, 0, h)
515 // H = H_I * H in backward mode
516 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hi, hcopy, 0, h)
522 // constructQ constructs the Q matrix from the result of dgeqrf and dgeqr2.
523 func constructQ(kind string, m, n int, a []float64, lda int, tau []float64) blas64.General {
525 return constructQK(kind, m, n, k, a, lda, tau)
528 // constructQK constructs the Q matrix from the result of dgeqrf and dgeqr2 using
529 // the first k reflectors.
530 func constructQK(kind string, m, n, k int, a []float64, lda int, tau []float64) blas64.General {
543 Data: make([]float64, sz*sz),
545 for i := 0; i < sz; i++ {
548 qCopy := blas64.General{
552 Data: make([]float64, len(q.Data)),
554 for i := 0; i < k; i++ {
559 Data: make([]float64, sz*sz),
561 for j := 0; j < sz; j++ {
564 vVec := blas64.Vector{
566 Data: make([]float64, sz),
571 for j := i + 1; j < sz; j++ {
572 vVec.Data[j] = a[lda*j+i]
576 for j := i + 1; j < sz; j++ {
577 vVec.Data[j] = a[i*lda+j]
580 for j := 0; j < n-k+i; j++ {
581 vVec.Data[j] = a[(m-k+i)*lda+j]
585 blas64.Ger(-tau[i], vVec, vVec, h)
586 copy(qCopy.Data, q.Data)
587 // Multiply q by the new h.
590 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, h, 0, q)
592 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, h, qCopy, 0, q)
598 // checkBidiagonal checks the bidiagonal decomposition from dlabrd and dgebd2.
599 // The input to this function is the answer returned from the routines, stored
600 // in a, d, e, tauP, and tauQ. The data of original A matrix (before
601 // decomposition) is input in aCopy.
603 // checkBidiagonal constructs the V and U matrices, and from them constructs Q
604 // and P. Using these constructions, it checks that Q^T * A * P and checks that
605 // the result is bidiagonal.
606 func checkBidiagonal(t *testing.T, m, n, nb int, a []float64, lda int, d, e, tauP, tauQ, aCopy []float64) {
608 // Construct V and U.
609 qMat := constructQPBidiagonal(lapack.ApplyQ, m, n, nb, a, lda, tauQ)
610 pMat := constructQPBidiagonal(lapack.ApplyP, m, n, nb, a, lda, tauP)
612 // Compute Q^T * A * P.
613 aMat := blas64.General{
617 Data: make([]float64, len(aCopy)),
619 copy(aMat.Data, aCopy)
621 tmp1 := blas64.General{
625 Data: make([]float64, m*n),
627 blas64.Gemm(blas.Trans, blas.NoTrans, 1, qMat, aMat, 0, tmp1)
628 tmp2 := blas64.General{
632 Data: make([]float64, m*n),
634 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, tmp1, pMat, 0, tmp2)
636 // Check that the first nb rows and cols of tm2 are upper bidiagonal
637 // if m >= n, and lower bidiagonal otherwise.
641 for i := 0; i < m; i++ {
642 for j := 0; j < n; j++ {
643 if i >= nb && j >= nb {
646 v := tmp2.Data[i*tmp2.Stride+j]
648 if math.Abs(d[i]-v) > 1e-12 {
653 if m >= n && i == j-1 {
654 if math.Abs(e[j-1]-v) > 1e-12 {
659 if m < n && i-1 == j {
660 if math.Abs(e[i-1]-v) > 1e-12 {
665 if math.Abs(v) > 1e-12 {
671 t.Errorf("Updated A not bi-diagonal")
674 fmt.Println("d = ", d)
675 t.Errorf("D Mismatch")
678 t.Errorf("E mismatch")
682 // constructQPBidiagonal constructs Q or P from the Bidiagonal decomposition
683 // computed by dlabrd and bgebd2.
684 func constructQPBidiagonal(vect lapack.DecompUpdate, m, n, nb int, a []float64, lda int, tau []float64) blas64.General {
686 if vect == lapack.ApplyQ {
692 if vect == lapack.ApplyQ {
698 Data: make([]float64, m*ldv),
706 Data: make([]float64, m*ldv),
710 if vect == lapack.ApplyQ {
712 for i := 0; i < m; i++ {
713 for j := 0; j <= min(nb-1, i); j++ {
718 v.Data[i*ldv+j] = a[i*lda+j]
722 for i := 1; i < m; i++ {
723 for j := 0; j <= min(nb-1, i-1); j++ {
728 v.Data[i*ldv+j] = a[i*lda+j]
734 for i := 0; i < nb; i++ {
735 for j := i; j < n; j++ {
740 v.Data[i*ldv+j] = a[i*lda+j]
744 for i := 0; i < nb; i++ {
745 for j := i + 1; j < n; j++ {
750 v.Data[i*ldv+j] = a[i*lda+j]
756 // The variable name is a computation of Q, but the algorithm is mostly the
757 // same for computing P (just with different data).
758 qMat := blas64.General{
762 Data: make([]float64, sz*sz),
764 hMat := blas64.General{
768 Data: make([]float64, sz*sz),
771 for i := 0; i < sz; i++ {
772 qMat.Data[i*qMat.Stride+i] = 1
774 for i := 0; i < nb; i++ {
775 qCopy := blas64.General{Rows: qMat.Rows, Cols: qMat.Cols, Stride: qMat.Stride, Data: make([]float64, len(qMat.Data))}
776 copy(qCopy.Data, qMat.Data)
779 for i := 0; i < sz; i++ {
780 for j := 0; j < sz; j++ {
782 hMat.Data[i*sz+j] = 1
784 hMat.Data[i*sz+j] = 0
789 // H -= tauQ[i] * v[i] * v[i]^t
790 if vect == lapack.ApplyQ {
798 Data: v.Data[i*v.Stride:],
801 blas64.Ger(-tau[i], vi, vi, hMat)
803 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, hMat, 0, qMat)
808 // printRowise prints the matrix with one row per line. This is useful for debugging.
809 // If beyond is true, it prints beyond the final column to lda. If false, only
810 // the columns are printed.
811 func printRowise(a []float64, m, n, lda int, beyond bool) {
812 for i := 0; i < m; i++ {
817 fmt.Println(a[i*lda : i*lda+end])
821 // isOrthonormal checks that a general matrix is orthonormal.
822 func isOrthonormal(q blas64.General) bool {
824 for i := 0; i < n; i++ {
825 for j := i; j < n; j++ {
827 blas64.Vector{Inc: 1, Data: q.Data[i*q.Stride:]},
828 blas64.Vector{Inc: 1, Data: q.Data[j*q.Stride:]},
834 if math.Abs(dot-1) > 1e-10 {
838 if math.Abs(dot) > 1e-10 {
847 // copyMatrix copies an m×n matrix src of stride n into an m×n matrix dst of stride ld.
848 func copyMatrix(m, n int, dst []float64, ld int, src []float64) {
849 for i := 0; i < m; i++ {
850 copy(dst[i*ld:i*ld+n], src[i*n:i*n+n])
854 func copyGeneral(dst, src blas64.General) {
855 r := min(dst.Rows, src.Rows)
856 c := min(dst.Cols, src.Cols)
857 for i := 0; i < r; i++ {
858 copy(dst.Data[i*dst.Stride:i*dst.Stride+c], src.Data[i*src.Stride:i*src.Stride+c])
862 // cloneGeneral allocates and returns an exact copy of the given general matrix.
863 func cloneGeneral(a blas64.General) blas64.General {
865 c.Data = make([]float64, len(a.Data))
870 // equalApprox returns whether the matrices A and B of order n are approximately
871 // equal within given tolerance.
872 func equalApprox(m, n int, a []float64, lda int, b []float64, tol float64) bool {
873 for i := 0; i < m; i++ {
874 for j := 0; j < n; j++ {
875 diff := a[i*lda+j] - b[i*n+j]
876 if math.IsNaN(diff) || math.Abs(diff) > tol {
884 // equalApproxGeneral returns whether the general matrices a and b are
885 // approximately equal within given tolerance.
886 func equalApproxGeneral(a, b blas64.General, tol float64) bool {
887 if a.Rows != b.Rows || a.Cols != b.Cols {
890 for i := 0; i < a.Rows; i++ {
891 for j := 0; j < a.Cols; j++ {
892 diff := a.Data[i*a.Stride+j] - b.Data[i*b.Stride+j]
893 if math.IsNaN(diff) || math.Abs(diff) > tol {
901 // equalApproxTriangular returns whether the triangular matrices A and B of
902 // order n are approximately equal within given tolerance.
903 func equalApproxTriangular(upper bool, n int, a []float64, lda int, b []float64, tol float64) bool {
905 for i := 0; i < n; i++ {
906 for j := i; j < n; j++ {
907 diff := a[i*lda+j] - b[i*n+j]
908 if math.IsNaN(diff) || math.Abs(diff) > tol {
915 for i := 0; i < n; i++ {
916 for j := 0; j <= i; j++ {
917 diff := a[i*lda+j] - b[i*n+j]
918 if math.IsNaN(diff) || math.Abs(diff) > tol {
926 func equalApproxSymmetric(a, b blas64.Symmetric, tol float64) bool {
927 if a.Uplo != b.Uplo {
933 if a.Uplo == blas.Upper {
934 for i := 0; i < a.N; i++ {
935 for j := i; j < a.N; j++ {
936 if !floats.EqualWithinAbsOrRel(a.Data[i*a.Stride+j], b.Data[i*b.Stride+j], tol, tol) {
943 for i := 0; i < a.N; i++ {
944 for j := 0; j <= i; j++ {
945 if !floats.EqualWithinAbsOrRel(a.Data[i*a.Stride+j], b.Data[i*b.Stride+j], tol, tol) {
953 // randSymBand creates a random symmetric banded matrix, and returns both the
954 // random matrix and the equivalent Symmetric matrix for testing. rnder
955 // specifies the random number
956 func randSymBand(ul blas.Uplo, n, ldab, kb int, rnd *rand.Rand) (blas64.Symmetric, blas64.SymmetricBand) {
957 // A matrix is positive definite if and only if it has a Cholesky
958 // decomposition. Generate a random banded lower triangular matrix
959 // to construct the random symmetric matrix.
960 a := make([]float64, n*n)
961 for i := 0; i < n; i++ {
962 for j := max(0, i-kb); j <= i; j++ {
963 a[i*n+j] = rnd.NormFloat64()
965 a[i*n+i] = math.Abs(a[i*n+i])
966 // Add an extra amound to the diagonal in order to improve the condition number.
967 a[i*n+i] += 1.5 * rnd.Float64()
969 agen := blas64.General{
976 // Construct the SymDense from a*a^T
977 c := make([]float64, n*n)
978 cgen := blas64.General{
984 blas64.Gemm(blas.NoTrans, blas.Trans, 1, agen, agen, 0, cgen)
985 sym := blas64.Symmetric{
992 b := symToSymBand(ul, c, n, n, kb, ldab)
993 band := blas64.SymmetricBand{
1004 // symToSymBand takes the data in a Symmetric matrix and returns a
1005 // SymmetricBanded matrix.
1006 func symToSymBand(ul blas.Uplo, a []float64, n, lda, kb, ldab int) []float64 {
1007 if ul == blas.Upper {
1008 band := make([]float64, (n-1)*ldab+kb+1)
1009 for i := 0; i < n; i++ {
1010 for j := i; j < min(i+kb+1, n); j++ {
1011 band[i*ldab+j-i] = a[i*lda+j]
1016 band := make([]float64, (n-1)*ldab+kb+1)
1017 for i := 0; i < n; i++ {
1018 for j := max(0, i-kb); j <= i; j++ {
1019 band[i*ldab+j-i+kb] = a[i*lda+j]
1025 // symBandToSym takes a banded symmetric matrix and returns the same data as
1026 // a Symmetric matrix.
1027 func symBandToSym(ul blas.Uplo, band []float64, n, kb, ldab int) blas64.Symmetric {
1028 sym := make([]float64, n*n)
1029 if ul == blas.Upper {
1030 for i := 0; i < n; i++ {
1031 for j := 0; j < min(kb+1+i, n)-i; j++ {
1032 sym[i*n+i+j] = band[i*ldab+j]
1036 for i := 0; i < n; i++ {
1037 for j := kb - min(i, kb); j < kb+1; j++ {
1038 sym[i*n+i-kb+j] = band[i*ldab+j]
1042 return blas64.Symmetric{
1050 // eye returns an identity matrix of given order and stride.
1051 func eye(n, stride int) blas64.General {
1052 ans := nanGeneral(n, n, stride)
1053 for i := 0; i < n; i++ {
1054 for j := 0; j < n; j++ {
1055 ans.Data[i*ans.Stride+j] = 0
1057 ans.Data[i*ans.Stride+i] = 1
1062 // zeros returns an m×n matrix with given stride filled with zeros.
1063 func zeros(m, n, stride int) blas64.General {
1064 a := nanGeneral(m, n, stride)
1065 for i := 0; i < m; i++ {
1066 for j := 0; j < n; j++ {
1067 a.Data[i*a.Stride+j] = 0
1073 // extract2x2Block returns the elements of T at [0,0], [0,1], [1,0], and [1,1].
1074 func extract2x2Block(t []float64, ldt int) (a, b, c, d float64) {
1075 return t[0], t[1], t[ldt], t[ldt+1]
1078 // isSchurCanonical returns whether the 2×2 matrix [a b; c d] is in Schur
1080 func isSchurCanonical(a, b, c, d float64) bool {
1081 return c == 0 || (a == d && math.Signbit(b) != math.Signbit(c))
1084 // isSchurCanonicalGeneral returns whether T is block upper triangular with 1×1
1085 // and 2×2 diagonal blocks, each 2×2 block in Schur canonical form. The function
1086 // checks only along the diagonal and the first subdiagonal, otherwise the lower
1087 // triangle is not accessed.
1088 func isSchurCanonicalGeneral(t blas64.General) bool {
1089 if t.Rows != t.Cols {
1090 panic("invalid matrix")
1092 for i := 0; i < t.Rows-1; {
1093 if t.Data[(i+1)*t.Stride+i] == 0 {
1099 a, b, c, d := extract2x2Block(t.Data[i*t.Stride+i:], t.Stride)
1100 if !isSchurCanonical(a, b, c, d) {
1108 // schurBlockEigenvalues returns the two eigenvalues of the 2×2 matrix [a b; c d]
1109 // that must be in Schur canonical form.
1110 func schurBlockEigenvalues(a, b, c, d float64) (ev1, ev2 complex128) {
1111 if !isSchurCanonical(a, b, c, d) {
1112 panic("block not in Schur canonical form")
1115 return complex(a, 0), complex(d, 0)
1117 im := math.Sqrt(-b * c)
1118 return complex(a, im), complex(a, -im)
1121 // schurBlockSize returns the size of the diagonal block at i-th row in the
1122 // upper quasi-triangular matrix t in Schur canonical form, and whether i points
1123 // to the first row of the block. For zero-sized matrices the function returns 0
1125 func schurBlockSize(t blas64.General, i int) (size int, first bool) {
1126 if t.Rows != t.Cols {
1127 panic("matrix not square")
1132 if i < 0 || t.Rows <= i {
1133 panic("index out of range")
1137 if i > 0 && t.Data[i*t.Stride+i-1] != 0 {
1138 // There is a non-zero element to the left, therefore i must
1139 // point to the second row in a 2×2 diagonal block.
1144 if i+1 < t.Rows && t.Data[(i+1)*t.Stride+i] != 0 {
1145 // There is a non-zero element below, this must be a 2×2
1152 // containsComplex returns whether z is approximately equal to one of the complex
1153 // numbers in v. If z is found, its index in v will be also returned.
1154 func containsComplex(v []complex128, z complex128, tol float64) (found bool, index int) {
1156 if cmplx.Abs(v[i]-z) < tol {
1163 // isAllNaN returns whether x contains only NaN values.
1164 func isAllNaN(x []float64) bool {
1165 for _, v := range x {
1173 // isUpperHessenberg returns whether h contains only zeros below the
1175 func isUpperHessenberg(h blas64.General) bool {
1176 if h.Rows != h.Cols {
1177 panic("matrix not square")
1180 for i := 0; i < n; i++ {
1181 for j := 0; j < n; j++ {
1182 if i > j+1 && h.Data[i*h.Stride+j] != 0 {
1190 // isUpperTriangular returns whether a contains only zeros below the diagonal.
1191 func isUpperTriangular(a blas64.General) bool {
1193 for i := 1; i < n; i++ {
1194 for j := 0; j < i; j++ {
1195 if a.Data[i*a.Stride+j] != 0 {
1203 // unbalancedSparseGeneral returns an m×n dense matrix with a random sparse
1204 // structure consisting of nz nonzero elements. The matrix will be unbalanced by
1205 // multiplying each element randomly by its row or column index.
1206 func unbalancedSparseGeneral(m, n, stride int, nonzeros int, rnd *rand.Rand) blas64.General {
1207 a := zeros(m, n, stride)
1208 for k := 0; k < nonzeros; k++ {
1211 if rnd.Float64() < 0.5 {
1212 a.Data[i*stride+j] = float64(i+1) * rnd.NormFloat64()
1214 a.Data[i*stride+j] = float64(j+1) * rnd.NormFloat64()
1220 // columnOf returns a copy of the j-th column of a.
1221 func columnOf(a blas64.General, j int) []float64 {
1222 if j < 0 || a.Cols <= j {
1223 panic("bad column index")
1225 col := make([]float64, a.Rows)
1226 for i := range col {
1227 col[i] = a.Data[i*a.Stride+j]
1232 // isRightEigenvectorOf returns whether the vector xRe+i*xIm, where i is the
1233 // imaginary unit, is the right eigenvector of A corresponding to the eigenvalue
1236 // A right eigenvector corresponding to a complex eigenvalue λ is a complex
1237 // non-zero vector x such that
1239 func isRightEigenvectorOf(a blas64.General, xRe, xIm []float64, lambda complex128, tol float64) bool {
1240 if a.Rows != a.Cols {
1241 panic("matrix not square")
1244 if imag(lambda) != 0 && xIm == nil {
1245 // Complex eigenvalue of a real matrix cannot have a real
1252 // Compute A real(x) and store the result into xReAns.
1253 xReAns := make([]float64, n)
1254 blas64.Gemv(blas.NoTrans, 1, a, blas64.Vector{1, xRe}, 0, blas64.Vector{1, xReAns})
1256 if imag(lambda) == 0 && xIm == nil {
1257 // Real eigenvalue and eigenvector.
1259 // Compute λx and store the result into lambdax.
1260 lambdax := make([]float64, n)
1261 floats.AddScaled(lambdax, real(lambda), xRe)
1263 // This is expressed as the inverse to catch the case
1264 // xReAns_i = Inf and lambdax_i = Inf of the same sign.
1265 return !(floats.Distance(xReAns, lambdax, math.Inf(1)) > tol)
1268 // Complex eigenvector, and real or complex eigenvalue.
1270 // Compute A imag(x) and store the result into xImAns.
1271 xImAns := make([]float64, n)
1272 blas64.Gemv(blas.NoTrans, 1, a, blas64.Vector{1, xIm}, 0, blas64.Vector{1, xImAns})
1274 // Compute λx and store the result into lambdax.
1275 lambdax := make([]complex128, n)
1276 for i := range lambdax {
1277 lambdax[i] = lambda * complex(xRe[i], xIm[i])
1280 for i, v := range lambdax {
1281 ax := complex(xReAns[i], xImAns[i])
1282 if cmplx.Abs(v-ax) > tol {
1289 // isLeftEigenvectorOf returns whether the vector yRe+i*yIm, where i is the
1290 // imaginary unit, is the left eigenvector of A corresponding to the eigenvalue
1293 // A left eigenvector corresponding to a complex eigenvalue λ is a complex
1294 // non-zero vector y such that
1296 // which is equivalent for real A to
1297 // A^T y = conj(λ) y,
1298 func isLeftEigenvectorOf(a blas64.General, yRe, yIm []float64, lambda complex128, tol float64) bool {
1299 if a.Rows != a.Cols {
1300 panic("matrix not square")
1303 if imag(lambda) != 0 && yIm == nil {
1304 // Complex eigenvalue of a real matrix cannot have a real
1311 // Compute A^T real(y) and store the result into yReAns.
1312 yReAns := make([]float64, n)
1313 blas64.Gemv(blas.Trans, 1, a, blas64.Vector{1, yRe}, 0, blas64.Vector{1, yReAns})
1315 if imag(lambda) == 0 && yIm == nil {
1316 // Real eigenvalue and eigenvector.
1318 // Compute λy and store the result into lambday.
1319 lambday := make([]float64, n)
1320 floats.AddScaled(lambday, real(lambda), yRe)
1322 // This is expressed as the inverse to catch the case
1323 // yReAns_i = Inf and lambday_i = Inf of the same sign.
1324 return !(floats.Distance(yReAns, lambday, math.Inf(1)) > tol)
1327 // Complex eigenvector, and real or complex eigenvalue.
1329 // Compute A^T imag(y) and store the result into yImAns.
1330 yImAns := make([]float64, n)
1331 blas64.Gemv(blas.Trans, 1, a, blas64.Vector{1, yIm}, 0, blas64.Vector{1, yImAns})
1333 // Compute conj(λ)y and store the result into lambday.
1334 lambda = cmplx.Conj(lambda)
1335 lambday := make([]complex128, n)
1336 for i := range lambday {
1337 lambday[i] = lambda * complex(yRe[i], yIm[i])
1340 for i, v := range lambday {
1341 ay := complex(yReAns[i], yImAns[i])
1342 if cmplx.Abs(v-ay) > tol {
1349 // rootsOfUnity returns the n complex numbers whose n-th power is equal to 1.
1350 func rootsOfUnity(n int) []complex128 {
1351 w := make([]complex128, n)
1352 for i := 0; i < n; i++ {
1353 angle := math.Pi * float64(2*i) / float64(n)
1354 w[i] = complex(math.Cos(angle), math.Sin(angle))
1359 // randomOrthogonal returns an n×n random orthogonal matrix.
1360 func randomOrthogonal(n int, rnd *rand.Rand) blas64.General {
1362 x := make([]float64, n)
1363 v := make([]float64, n)
1364 for j := 0; j < n-1; j++ {
1365 // x represents the j-th column of a random matrix.
1366 for i := 0; i < j; i++ {
1369 for i := j; i < n; i++ {
1370 x[i] = rnd.NormFloat64()
1372 // Compute v that represents the elementary reflector that
1373 // annihilates the subdiagonal elements of x.
1375 // Compute Q * H_j and store the result into Q.
1376 applyReflector(q, q, v)
1378 if !isOrthonormal(q) {
1379 panic("Q not orthogonal")
1384 // reflector generates a Householder reflector v that zeros out subdiagonal
1385 // entries in the j-th column of a matrix.
1386 func reflector(v, col []float64, j int) {
1389 panic("slice length mismatch")
1391 if j < 0 || n <= j {
1392 panic("invalid column index")
1401 s := floats.Norm(col[j:], 2)
1405 v[j] = col[j] + math.Copysign(s, col[j])
1406 copy(v[j+1:], col[j+1:])
1407 s = floats.Norm(v[j:], 2)
1408 floats.Scale(1/s, v[j:])
1411 // applyReflector computes Q*H where H is a Householder matrix represented by
1412 // the Householder reflector v.
1413 func applyReflector(qh blas64.General, q blas64.General, v []float64) {
1415 if qh.Rows != n || qh.Cols != n {
1416 panic("bad size of qh")
1418 if q.Rows != n || q.Cols != n {
1419 panic("bad size of q")
1421 qv := make([]float64, n)
1422 blas64.Gemv(blas.NoTrans, 1, q, blas64.Vector{1, v}, 0, blas64.Vector{1, qv})
1423 for i := 0; i < n; i++ {
1424 for j := 0; j < n; j++ {
1425 qh.Data[i*qh.Stride+j] = q.Data[i*q.Stride+j]
1428 for i := 0; i < n; i++ {
1429 for j := 0; j < n; j++ {
1430 qh.Data[i*qh.Stride+j] -= 2 * qv[i] * v[j]
1434 for _, vi := range v {
1437 norm2inv := 1 / norm2
1438 for i := 0; i < n; i++ {
1439 for j := 0; j < n; j++ {
1440 qh.Data[i*qh.Stride+j] *= norm2inv
1445 // constructGSVDresults returns the matrices [ 0 R ], D1 and D2 described
1446 // in the documentation of Dtgsja and Dggsvd3, and the result matrix in
1447 // the documentation for Dggsvp3.
1448 func constructGSVDresults(n, p, m, k, l int, a, b blas64.General, alpha, beta []float64) (zeroR, d1, d2 blas64.General) {
1450 zeroR = zeros(k+l, n, n)
1452 dst.Rows = min(m, k+l)
1454 dst.Data = zeroR.Data[n-k-l:]
1456 src.Rows = min(m, k+l)
1458 src.Data = a.Data[n-k-l:]
1459 copyGeneral(dst, src)
1462 dst.Rows = k + l - m
1463 dst.Cols = k + l - m
1464 dst.Data = zeroR.Data[m*zeroR.Stride+n-(k+l-m):]
1466 src.Rows = k + l - m
1467 src.Cols = k + l - m
1468 src.Data = b.Data[(m-k)*b.Stride+n+m-k-l:]
1469 copyGeneral(dst, src)
1473 d1 = zeros(m, k+l, k+l)
1474 for i := 0; i < k; i++ {
1475 d1.Data[i*d1.Stride+i] = 1
1477 for i := k; i < min(m, k+l); i++ {
1478 d1.Data[i*d1.Stride+i] = alpha[i]
1482 d2 = zeros(p, k+l, k+l)
1483 for i := 0; i < min(l, m-k); i++ {
1484 d2.Data[i*d2.Stride+i+k] = beta[k+i]
1486 for i := m - k; i < l; i++ {
1487 d2.Data[i*d2.Stride+i+k] = 1
1490 return zeroR, d1, d2
1493 func constructGSVPresults(n, p, m, k, l int, a, b blas64.General) (zeroA, zeroB blas64.General) {
1494 zeroA = zeros(m, n, n)
1496 dst.Rows = min(m, k+l)
1498 dst.Data = zeroA.Data[n-k-l:]
1500 dst.Rows = min(m, k+l)
1502 src.Data = a.Data[n-k-l:]
1503 copyGeneral(dst, src)
1505 zeroB = zeros(p, n, n)
1509 dst.Data = zeroB.Data[n-l:]
1513 src.Data = b.Data[n-l:]
1514 copyGeneral(dst, src)