gonum.org/v1/gonum@v0.14.0/lapack/gonum/dtgsja.go (about)

     1  // Copyright ©2017 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gonum
     6  
     7  import (
     8  	"math"
     9  
    10  	"gonum.org/v1/gonum/blas"
    11  	"gonum.org/v1/gonum/blas/blas64"
    12  	"gonum.org/v1/gonum/lapack"
    13  )
    14  
    15  // Dtgsja computes the generalized singular value decomposition (GSVD)
    16  // of two real upper triangular or trapezoidal matrices A and B.
    17  //
    18  // A and B have the following forms, which may be obtained by the
    19  // preprocessing subroutine Dggsvp from a general m×n matrix A and p×n
    20  // matrix B:
    21  //
    22  //	          n-k-l  k    l
    23  //	A =    k [  0   A12  A13 ] if m-k-l >= 0;
    24  //	       l [  0    0   A23 ]
    25  //	   m-k-l [  0    0    0  ]
    26  //
    27  //	          n-k-l  k    l
    28  //	A =    k [  0   A12  A13 ] if m-k-l < 0;
    29  //	     m-k [  0    0   A23 ]
    30  //
    31  //	          n-k-l  k    l
    32  //	B =    l [  0    0   B13 ]
    33  //	     p-l [  0    0    0  ]
    34  //
    35  // where the k×k matrix A12 and l×l matrix B13 are non-singular
    36  // upper triangular. A23 is l×l upper triangular if m-k-l >= 0,
    37  // otherwise A23 is (m-k)×l upper trapezoidal.
    38  //
    39  // On exit,
    40  //
    41  //	Uᵀ*A*Q = D1*[ 0 R ], Vᵀ*B*Q = D2*[ 0 R ],
    42  //
    43  // where U, V and Q are orthogonal matrices.
    44  // R is a non-singular upper triangular matrix, and D1 and D2 are
    45  // diagonal matrices, which are of the following structures:
    46  //
    47  // If m-k-l >= 0,
    48  //
    49  //	                  k  l
    50  //	     D1 =     k [ I  0 ]
    51  //	              l [ 0  C ]
    52  //	          m-k-l [ 0  0 ]
    53  //
    54  //	                k  l
    55  //	     D2 = l   [ 0  S ]
    56  //	          p-l [ 0  0 ]
    57  //
    58  //	             n-k-l  k    l
    59  //	[ 0 R ] = k [  0   R11  R12 ] k
    60  //	          l [  0    0   R22 ] l
    61  //
    62  // where
    63  //
    64  //	C = diag( alpha_k, ... , alpha_{k+l} ),
    65  //	S = diag( beta_k,  ... , beta_{k+l} ),
    66  //	C^2 + S^2 = I.
    67  //
    68  // R is stored in
    69  //
    70  //	A[0:k+l, n-k-l:n]
    71  //
    72  // on exit.
    73  //
    74  // If m-k-l < 0,
    75  //
    76  //	               k m-k k+l-m
    77  //	    D1 =   k [ I  0    0  ]
    78  //	         m-k [ 0  C    0  ]
    79  //
    80  //	                 k m-k k+l-m
    81  //	    D2 =   m-k [ 0  S    0  ]
    82  //	         k+l-m [ 0  0    I  ]
    83  //	           p-l [ 0  0    0  ]
    84  //
    85  //	               n-k-l  k   m-k  k+l-m
    86  //	[ 0 R ] =    k [ 0    R11  R12  R13 ]
    87  //	           m-k [ 0     0   R22  R23 ]
    88  //	         k+l-m [ 0     0    0   R33 ]
    89  //
    90  // where
    91  //
    92  //	C = diag( alpha_k, ... , alpha_m ),
    93  //	S = diag( beta_k,  ... , beta_m ),
    94  //	C^2 + S^2 = I.
    95  //
    96  //	R = [ R11 R12 R13 ] is stored in A[0:m, n-k-l:n]
    97  //	    [  0  R22 R23 ]
    98  //
    99  // and R33 is stored in
   100  //
   101  //	B[m-k:l, n+m-k-l:n] on exit.
   102  //
   103  // The computation of the orthogonal transformation matrices U, V or Q
   104  // is optional. These matrices may either be formed explicitly, or they
   105  // may be post-multiplied into input matrices U1, V1, or Q1.
   106  //
   107  // Dtgsja essentially uses a variant of Kogbetliantz algorithm to reduce
   108  // min(l,m-k)×l triangular or trapezoidal matrix A23 and l×l
   109  // matrix B13 to the form:
   110  //
   111  //	U1ᵀ*A13*Q1 = C1*R1; V1ᵀ*B13*Q1 = S1*R1,
   112  //
   113  // where U1, V1 and Q1 are orthogonal matrices. C1 and S1 are diagonal
   114  // matrices satisfying
   115  //
   116  //	C1^2 + S1^2 = I,
   117  //
   118  // and R1 is an l×l non-singular upper triangular matrix.
   119  //
   120  // jobU, jobV and jobQ are options for computing the orthogonal matrices. The behavior
   121  // is as follows
   122  //
   123  //	jobU == lapack.GSVDU        Compute orthogonal matrix U
   124  //	jobU == lapack.GSVDUnit     Use unit-initialized matrix
   125  //	jobU == lapack.GSVDNone     Do not compute orthogonal matrix.
   126  //
   127  // The behavior is the same for jobV and jobQ with the exception that instead of
   128  // lapack.GSVDU these accept lapack.GSVDV and lapack.GSVDQ respectively.
   129  // The matrices U, V and Q must be m×m, p×p and n×n respectively unless the
   130  // relevant job parameter is lapack.GSVDNone.
   131  //
   132  // k and l specify the sub-blocks in the input matrices A and B:
   133  //
   134  //	A23 = A[k:min(k+l,m), n-l:n) and B13 = B[0:l, n-l:n]
   135  //
   136  // of A and B, whose GSVD is going to be computed by Dtgsja.
   137  //
   138  // tola and tolb are the convergence criteria for the Jacobi-Kogbetliantz
   139  // iteration procedure. Generally, they are the same as used in the preprocessing
   140  // step, for example,
   141  //
   142  //	tola = max(m, n)*norm(A)*eps,
   143  //	tolb = max(p, n)*norm(B)*eps,
   144  //
   145  // where eps is the machine epsilon.
   146  //
   147  // work must have length at least 2*n, otherwise Dtgsja will panic.
   148  //
   149  // alpha and beta must have length n or Dtgsja will panic. On exit, alpha and
   150  // beta contain the generalized singular value pairs of A and B
   151  //
   152  //	alpha[0:k] = 1,
   153  //	beta[0:k]  = 0,
   154  //
   155  // if m-k-l >= 0,
   156  //
   157  //	alpha[k:k+l] = diag(C),
   158  //	beta[k:k+l]  = diag(S),
   159  //
   160  // if m-k-l < 0,
   161  //
   162  //	alpha[k:m]= C, alpha[m:k+l]= 0
   163  //	beta[k:m] = S, beta[m:k+l] = 1.
   164  //
   165  // if k+l < n,
   166  //
   167  //	alpha[k+l:n] = 0 and
   168  //	beta[k+l:n]  = 0.
   169  //
   170  // On exit, A[n-k:n, 0:min(k+l,m)] contains the triangular matrix R or part of R
   171  // and if necessary, B[m-k:l, n+m-k-l:n] contains a part of R.
   172  //
   173  // Dtgsja returns whether the routine converged and the number of iteration cycles
   174  // that were run.
   175  //
   176  // Dtgsja is an internal routine. It is exported for testing purposes.
   177  func (impl Implementation) Dtgsja(jobU, jobV, jobQ lapack.GSVDJob, m, p, n, k, l int, a []float64, lda int, b []float64, ldb int, tola, tolb float64, alpha, beta, u []float64, ldu int, v []float64, ldv int, q []float64, ldq int, work []float64) (cycles int, ok bool) {
   178  	const maxit = 40
   179  
   180  	initu := jobU == lapack.GSVDUnit
   181  	wantu := initu || jobU == lapack.GSVDU
   182  
   183  	initv := jobV == lapack.GSVDUnit
   184  	wantv := initv || jobV == lapack.GSVDV
   185  
   186  	initq := jobQ == lapack.GSVDUnit
   187  	wantq := initq || jobQ == lapack.GSVDQ
   188  
   189  	switch {
   190  	case !initu && !wantu && jobU != lapack.GSVDNone:
   191  		panic(badGSVDJob + "U")
   192  	case !initv && !wantv && jobV != lapack.GSVDNone:
   193  		panic(badGSVDJob + "V")
   194  	case !initq && !wantq && jobQ != lapack.GSVDNone:
   195  		panic(badGSVDJob + "Q")
   196  	case m < 0:
   197  		panic(mLT0)
   198  	case p < 0:
   199  		panic(pLT0)
   200  	case n < 0:
   201  		panic(nLT0)
   202  
   203  	case lda < max(1, n):
   204  		panic(badLdA)
   205  	case len(a) < (m-1)*lda+n:
   206  		panic(shortA)
   207  
   208  	case ldb < max(1, n):
   209  		panic(badLdB)
   210  	case len(b) < (p-1)*ldb+n:
   211  		panic(shortB)
   212  
   213  	case len(alpha) != n:
   214  		panic(badLenAlpha)
   215  	case len(beta) != n:
   216  		panic(badLenBeta)
   217  
   218  	case ldu < 1, wantu && ldu < m:
   219  		panic(badLdU)
   220  	case wantu && len(u) < (m-1)*ldu+m:
   221  		panic(shortU)
   222  
   223  	case ldv < 1, wantv && ldv < p:
   224  		panic(badLdV)
   225  	case wantv && len(v) < (p-1)*ldv+p:
   226  		panic(shortV)
   227  
   228  	case ldq < 1, wantq && ldq < n:
   229  		panic(badLdQ)
   230  	case wantq && len(q) < (n-1)*ldq+n:
   231  		panic(shortQ)
   232  
   233  	case len(work) < 2*n:
   234  		panic(shortWork)
   235  	}
   236  
   237  	// Initialize U, V and Q, if necessary
   238  	if initu {
   239  		impl.Dlaset(blas.All, m, m, 0, 1, u, ldu)
   240  	}
   241  	if initv {
   242  		impl.Dlaset(blas.All, p, p, 0, 1, v, ldv)
   243  	}
   244  	if initq {
   245  		impl.Dlaset(blas.All, n, n, 0, 1, q, ldq)
   246  	}
   247  
   248  	bi := blas64.Implementation()
   249  	minTol := math.Min(tola, tolb)
   250  
   251  	// Loop until convergence.
   252  	upper := false
   253  	for cycles = 1; cycles <= maxit; cycles++ {
   254  		upper = !upper
   255  
   256  		for i := 0; i < l-1; i++ {
   257  			for j := i + 1; j < l; j++ {
   258  				var a1, a2, a3 float64
   259  				if k+i < m {
   260  					a1 = a[(k+i)*lda+n-l+i]
   261  				}
   262  				if k+j < m {
   263  					a3 = a[(k+j)*lda+n-l+j]
   264  				}
   265  
   266  				b1 := b[i*ldb+n-l+i]
   267  				b3 := b[j*ldb+n-l+j]
   268  
   269  				var b2 float64
   270  				if upper {
   271  					if k+i < m {
   272  						a2 = a[(k+i)*lda+n-l+j]
   273  					}
   274  					b2 = b[i*ldb+n-l+j]
   275  				} else {
   276  					if k+j < m {
   277  						a2 = a[(k+j)*lda+n-l+i]
   278  					}
   279  					b2 = b[j*ldb+n-l+i]
   280  				}
   281  
   282  				csu, snu, csv, snv, csq, snq := impl.Dlags2(upper, a1, a2, a3, b1, b2, b3)
   283  
   284  				// Update (k+i)-th and (k+j)-th rows of matrix A: Uᵀ*A.
   285  				if k+j < m {
   286  					bi.Drot(l, a[(k+j)*lda+n-l:], 1, a[(k+i)*lda+n-l:], 1, csu, snu)
   287  				}
   288  
   289  				// Update i-th and j-th rows of matrix B: Vᵀ*B.
   290  				bi.Drot(l, b[j*ldb+n-l:], 1, b[i*ldb+n-l:], 1, csv, snv)
   291  
   292  				// Update (n-l+i)-th and (n-l+j)-th columns of matrices
   293  				// A and B: A*Q and B*Q.
   294  				bi.Drot(min(k+l, m), a[n-l+j:], lda, a[n-l+i:], lda, csq, snq)
   295  				bi.Drot(l, b[n-l+j:], ldb, b[n-l+i:], ldb, csq, snq)
   296  
   297  				if upper {
   298  					if k+i < m {
   299  						a[(k+i)*lda+n-l+j] = 0
   300  					}
   301  					b[i*ldb+n-l+j] = 0
   302  				} else {
   303  					if k+j < m {
   304  						a[(k+j)*lda+n-l+i] = 0
   305  					}
   306  					b[j*ldb+n-l+i] = 0
   307  				}
   308  
   309  				// Update orthogonal matrices U, V, Q, if desired.
   310  				if wantu && k+j < m {
   311  					bi.Drot(m, u[k+j:], ldu, u[k+i:], ldu, csu, snu)
   312  				}
   313  				if wantv {
   314  					bi.Drot(p, v[j:], ldv, v[i:], ldv, csv, snv)
   315  				}
   316  				if wantq {
   317  					bi.Drot(n, q[n-l+j:], ldq, q[n-l+i:], ldq, csq, snq)
   318  				}
   319  			}
   320  		}
   321  
   322  		if !upper {
   323  			// The matrices A13 and B13 were lower triangular at the start
   324  			// of the cycle, and are now upper triangular.
   325  			//
   326  			// Convergence test: test the parallelism of the corresponding
   327  			// rows of A and B.
   328  			var error float64
   329  			for i := 0; i < min(l, m-k); i++ {
   330  				bi.Dcopy(l-i, a[(k+i)*lda+n-l+i:], 1, work, 1)
   331  				bi.Dcopy(l-i, b[i*ldb+n-l+i:], 1, work[l:], 1)
   332  				ssmin := impl.Dlapll(l-i, work, 1, work[l:], 1)
   333  				error = math.Max(error, ssmin)
   334  			}
   335  			if math.Abs(error) <= minTol {
   336  				// The algorithm has converged.
   337  				// Compute the generalized singular value pairs (alpha, beta)
   338  				// and set the triangular matrix R to array A.
   339  				for i := 0; i < k; i++ {
   340  					alpha[i] = 1
   341  					beta[i] = 0
   342  				}
   343  
   344  				for i := 0; i < min(l, m-k); i++ {
   345  					a1 := a[(k+i)*lda+n-l+i]
   346  					b1 := b[i*ldb+n-l+i]
   347  					gamma := b1 / a1
   348  					if !math.IsInf(gamma, 0) {
   349  						// Change sign if necessary.
   350  						if gamma < 0 {
   351  							bi.Dscal(l-i, -1, b[i*ldb+n-l+i:], 1)
   352  							if wantv {
   353  								bi.Dscal(p, -1, v[i:], ldv)
   354  							}
   355  						}
   356  						beta[k+i], alpha[k+i], _ = impl.Dlartg(math.Abs(gamma), 1)
   357  
   358  						if alpha[k+i] >= beta[k+i] {
   359  							bi.Dscal(l-i, 1/alpha[k+i], a[(k+i)*lda+n-l+i:], 1)
   360  						} else {
   361  							bi.Dscal(l-i, 1/beta[k+i], b[i*ldb+n-l+i:], 1)
   362  							bi.Dcopy(l-i, b[i*ldb+n-l+i:], 1, a[(k+i)*lda+n-l+i:], 1)
   363  						}
   364  					} else {
   365  						alpha[k+i] = 0
   366  						beta[k+i] = 1
   367  						bi.Dcopy(l-i, b[i*ldb+n-l+i:], 1, a[(k+i)*lda+n-l+i:], 1)
   368  					}
   369  				}
   370  
   371  				for i := m; i < k+l; i++ {
   372  					alpha[i] = 0
   373  					beta[i] = 1
   374  				}
   375  				if k+l < n {
   376  					for i := k + l; i < n; i++ {
   377  						alpha[i] = 0
   378  						beta[i] = 0
   379  					}
   380  				}
   381  
   382  				return cycles, true
   383  			}
   384  		}
   385  	}
   386  
   387  	// The algorithm has not converged after maxit cycles.
   388  	return cycles, false
   389  }