github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/native/dggsvp3.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 native
     6  
     7  import (
     8  	"math"
     9  
    10  	"github.com/gonum/blas"
    11  	"github.com/gonum/lapack"
    12  )
    13  
    14  // Dggsvp3 computes orthogonal matrices U, V and Q such that
    15  //
    16  //                  n-k-l  k    l
    17  //  U^T*A*Q =    k [ 0    A12  A13 ] if m-k-l >= 0;
    18  //               l [ 0     0   A23 ]
    19  //           m-k-l [ 0     0    0  ]
    20  //
    21  //                  n-k-l  k    l
    22  //  U^T*A*Q =    k [ 0    A12  A13 ] if m-k-l < 0;
    23  //             m-k [ 0     0   A23 ]
    24  //
    25  //                  n-k-l  k    l
    26  //  V^T*B*Q =    l [ 0     0   B13 ]
    27  //             p-l [ 0     0    0  ]
    28  //
    29  // where the k×k matrix A12 and l×l matrix B13 are non-singular
    30  // upper triangular. A23 is l×l upper triangular if m-k-l >= 0,
    31  // otherwise A23 is (m-k)×l upper trapezoidal.
    32  //
    33  // Dggsvp3 returns k and l, the dimensions of the sub-blocks. k+l
    34  // is the effective numerical rank of the (m+p)×n matrix [ A^T B^T ]^T.
    35  //
    36  // jobU, jobV and jobQ are options for computing the orthogonal matrices. The behavior
    37  // is as follows
    38  //  jobU == lapack.GSVDU        Compute orthogonal matrix U
    39  //  jobU == lapack.GSVDNone     Do not compute orthogonal matrix.
    40  // The behavior is the same for jobV and jobQ with the exception that instead of
    41  // lapack.GSVDU these accept lapack.GSVDV and lapack.GSVDQ respectively.
    42  // The matrices U, V and Q must be m×m, p×p and n×n respectively unless the
    43  // relevant job parameter is lapack.GSVDNone.
    44  //
    45  // tola and tolb are the convergence criteria for the Jacobi-Kogbetliantz
    46  // iteration procedure. Generally, they are the same as used in the preprocessing
    47  // step, for example,
    48  //  tola = max(m, n)*norm(A)*eps,
    49  //  tolb = max(p, n)*norm(B)*eps.
    50  // Where eps is the machine epsilon.
    51  //
    52  // iwork must have length n, work must have length at least max(1, lwork), and
    53  // lwork must be -1 or greater than zero, otherwise Dggsvp3 will panic.
    54  //
    55  // Dggsvp3 is an internal routine. It is exported for testing purposes.
    56  func (impl Implementation) Dggsvp3(jobU, jobV, jobQ lapack.GSVDJob, m, p, n int, a []float64, lda int, b []float64, ldb int, tola, tolb float64, u []float64, ldu int, v []float64, ldv int, q []float64, ldq int, iwork []int, tau, work []float64, lwork int) (k, l int) {
    57  	const forward = true
    58  
    59  	checkMatrix(m, n, a, lda)
    60  	checkMatrix(p, n, b, ldb)
    61  
    62  	wantu := jobU == lapack.GSVDU
    63  	if !wantu && jobU != lapack.GSVDNone {
    64  		panic(badGSVDJob + "U")
    65  	}
    66  	if jobU != lapack.GSVDNone {
    67  		checkMatrix(m, m, u, ldu)
    68  	}
    69  
    70  	wantv := jobV == lapack.GSVDV
    71  	if !wantv && jobV != lapack.GSVDNone {
    72  		panic(badGSVDJob + "V")
    73  	}
    74  	if jobV != lapack.GSVDNone {
    75  		checkMatrix(p, p, v, ldv)
    76  	}
    77  
    78  	wantq := jobQ == lapack.GSVDQ
    79  	if !wantq && jobQ != lapack.GSVDNone {
    80  		panic(badGSVDJob + "Q")
    81  	}
    82  	if jobQ != lapack.GSVDNone {
    83  		checkMatrix(n, n, q, ldq)
    84  	}
    85  
    86  	if len(iwork) != n {
    87  		panic(badWork)
    88  	}
    89  	if lwork != -1 && lwork < 1 {
    90  		panic(badWork)
    91  	}
    92  	if len(work) < max(1, lwork) {
    93  		panic(badWork)
    94  	}
    95  
    96  	var lwkopt int
    97  	impl.Dgeqp3(p, n, b, ldb, iwork, tau, work, -1)
    98  	lwkopt = int(work[0])
    99  	if wantv {
   100  		lwkopt = max(lwkopt, p)
   101  	}
   102  	lwkopt = max(lwkopt, min(n, p))
   103  	lwkopt = max(lwkopt, m)
   104  	if wantq {
   105  		lwkopt = max(lwkopt, n)
   106  	}
   107  	impl.Dgeqp3(m, n, a, lda, iwork, tau, work, -1)
   108  	lwkopt = max(lwkopt, int(work[0]))
   109  	lwkopt = max(1, lwkopt)
   110  	if lwork == -1 {
   111  		work[0] = float64(lwkopt)
   112  		return 0, 0
   113  	}
   114  
   115  	// tau check must come after lwkopt query since
   116  	// the Dggsvd3 call for lwkopt query may have
   117  	// lwork == -1, and tau is provided by work.
   118  	if len(tau) < n {
   119  		panic(badTau)
   120  	}
   121  
   122  	// QR with column pivoting of B: B*P = V*[ S11 S12 ].
   123  	//                                       [  0   0  ]
   124  	for i := range iwork[:n] {
   125  		iwork[i] = 0
   126  	}
   127  	impl.Dgeqp3(p, n, b, ldb, iwork, tau, work, lwork)
   128  
   129  	// Update A := A*P.
   130  	impl.Dlapmt(forward, m, n, a, lda, iwork)
   131  
   132  	// Determine the effective rank of matrix B.
   133  	for i := 0; i < min(p, n); i++ {
   134  		if math.Abs(b[i*ldb+i]) > tolb {
   135  			l++
   136  		}
   137  	}
   138  
   139  	if wantv {
   140  		// Copy the details of V, and form V.
   141  		impl.Dlaset(blas.All, p, p, 0, 0, v, ldv)
   142  		if p > 1 {
   143  			impl.Dlacpy(blas.Lower, p-1, min(p, n), b[ldb:], ldb, v[ldv:], ldv)
   144  		}
   145  		impl.Dorg2r(p, p, min(p, n), v, ldv, tau, work)
   146  	}
   147  
   148  	// Clean up B.
   149  	for i := 1; i < l; i++ {
   150  		r := b[i*ldb : i*ldb+i]
   151  		for j := range r {
   152  			r[j] = 0
   153  		}
   154  	}
   155  	if p > l {
   156  		impl.Dlaset(blas.All, p-l, n, 0, 0, b[l*ldb:], ldb)
   157  	}
   158  
   159  	if wantq {
   160  		// Set Q = I and update Q := Q*P.
   161  		impl.Dlaset(blas.All, n, n, 0, 1, q, ldq)
   162  		impl.Dlapmt(forward, n, n, q, ldq, iwork)
   163  	}
   164  
   165  	if p >= l && n != l {
   166  		// RQ factorization of [ S11 S12 ]: [ S11 S12 ] = [ 0 S12 ]*Z.
   167  		impl.Dgerq2(l, n, b, ldb, tau, work)
   168  
   169  		// Update A := A*Z^T.
   170  		impl.Dormr2(blas.Right, blas.Trans, m, n, l, b, ldb, tau, a, lda, work)
   171  
   172  		if wantq {
   173  			// Update Q := Q*Z^T.
   174  			impl.Dormr2(blas.Right, blas.Trans, n, n, l, b, ldb, tau, q, ldq, work)
   175  		}
   176  
   177  		// Clean up B.
   178  		impl.Dlaset(blas.All, l, n-l, 0, 0, b, ldb)
   179  		for i := 1; i < l; i++ {
   180  			r := b[i*ldb+n-l : i*ldb+i+n-l]
   181  			for j := range r {
   182  				r[j] = 0
   183  			}
   184  		}
   185  	}
   186  
   187  	// Let              N-L     L
   188  	//            A = [ A11    A12 ] M,
   189  	//
   190  	// then the following does the complete QR decomposition of A11:
   191  	//
   192  	//          A11 = U*[  0  T12 ]*P1^T.
   193  	//                  [  0   0  ]
   194  	for i := range iwork[:n-l] {
   195  		iwork[i] = 0
   196  	}
   197  	impl.Dgeqp3(m, n-l, a, lda, iwork[:n-l], tau, work, lwork)
   198  
   199  	// Determine the effective rank of A11.
   200  	for i := 0; i < min(m, n-l); i++ {
   201  		if math.Abs(a[i*lda+i]) > tola {
   202  			k++
   203  		}
   204  	}
   205  
   206  	// Update A12 := U^T*A12, where A12 = A[0:m, n-l:n].
   207  	impl.Dorm2r(blas.Left, blas.Trans, m, l, min(m, n-l), a, lda, tau, a[n-l:], lda, work)
   208  
   209  	if wantu {
   210  		// Copy the details of U, and form U.
   211  		impl.Dlaset(blas.All, m, m, 0, 0, u, ldu)
   212  		if m > 1 {
   213  			impl.Dlacpy(blas.Lower, m-1, min(m, n-l), a[lda:], lda, u[ldu:], ldu)
   214  		}
   215  		impl.Dorg2r(m, m, min(m, n-l), u, ldu, tau, work)
   216  	}
   217  
   218  	if wantq {
   219  		// Update Q[0:n, 0:n-l] := Q[0:n, 0:n-l]*P1.
   220  		impl.Dlapmt(forward, n, n-l, q, ldq, iwork[:n-l])
   221  	}
   222  
   223  	// Clean up A: set the strictly lower triangular part of
   224  	// A[0:k, 0:k] = 0, and A[k:m, 0:n-l] = 0.
   225  	for i := 1; i < k; i++ {
   226  		r := a[i*lda : i*lda+i]
   227  		for j := range r {
   228  			r[j] = 0
   229  		}
   230  	}
   231  	if m > k {
   232  		impl.Dlaset(blas.All, m-k, n-l, 0, 0, a[k*lda:], lda)
   233  	}
   234  
   235  	if n-l > k {
   236  		// RQ factorization of [ T11 T12 ] = [ 0 T12 ]*Z1.
   237  		impl.Dgerq2(k, n-l, a, lda, tau, work)
   238  
   239  		if wantq {
   240  			// Update Q[0:n, 0:n-l] := Q[0:n, 0:n-l]*Z1^T.
   241  			impl.Dorm2r(blas.Right, blas.Trans, n, n-l, k, a, lda, tau, q, ldq, work)
   242  		}
   243  
   244  		// Clean up A.
   245  		impl.Dlaset(blas.All, k, n-l-k, 0, 0, a, lda)
   246  		for i := 1; i < k; i++ {
   247  			r := a[i*lda+n-k-l : i*lda+i+n-k-l]
   248  			for j := range r {
   249  				a[j] = 0
   250  			}
   251  		}
   252  	}
   253  
   254  	if m > k {
   255  		// QR factorization of A[k:m, n-l:n].
   256  		impl.Dgeqr2(m-k, l, a[k*lda+n-l:], lda, tau, work)
   257  		if wantu {
   258  			// Update U[:, k:m) := U[:, k:m]*U1.
   259  			impl.Dorm2r(blas.Right, blas.NoTrans, m, m-k, min(m-k, l), a[k*lda+n-l:], lda, tau, u[k:], ldu, work)
   260  		}
   261  
   262  		// Clean up A.
   263  		for i := k + 1; i < m; i++ {
   264  			r := a[i*lda+n-l : i*lda+min(n-l+i-k, n)]
   265  			for j := range r {
   266  				r[j] = 0
   267  			}
   268  		}
   269  	}
   270  
   271  	work[0] = float64(lwkopt)
   272  	return k, l
   273  }