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