gonum.org/v1/gonum@v0.14.0/lapack/gonum/dormbr.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  package gonum
     6  
     7  import (
     8  	"gonum.org/v1/gonum/blas"
     9  	"gonum.org/v1/gonum/lapack"
    10  )
    11  
    12  // Dormbr applies a multiplicative update to the matrix C based on a
    13  // decomposition computed by Dgebrd.
    14  //
    15  // Dormbr overwrites the m×n matrix C with
    16  //
    17  //	Q * C   if vect == lapack.ApplyQ, side == blas.Left, and trans == blas.NoTrans
    18  //	C * Q   if vect == lapack.ApplyQ, side == blas.Right, and trans == blas.NoTrans
    19  //	Qᵀ * C  if vect == lapack.ApplyQ, side == blas.Left, and trans == blas.Trans
    20  //	C * Qᵀ  if vect == lapack.ApplyQ, side == blas.Right, and trans == blas.Trans
    21  //
    22  //	P * C   if vect == lapack.ApplyP, side == blas.Left, and trans == blas.NoTrans
    23  //	C * P   if vect == lapack.ApplyP, side == blas.Right, and trans == blas.NoTrans
    24  //	Pᵀ * C  if vect == lapack.ApplyP, side == blas.Left, and trans == blas.Trans
    25  //	C * Pᵀ  if vect == lapack.ApplyP, side == blas.Right, and trans == blas.Trans
    26  //
    27  // where P and Q are the orthogonal matrices determined by Dgebrd when reducing
    28  // a matrix A to bidiagonal form: A = Q * B * Pᵀ. See Dgebrd for the
    29  // definitions of Q and P.
    30  //
    31  // If vect == lapack.ApplyQ, A is assumed to have been an nq×k matrix, while if
    32  // vect == lapack.ApplyP, A is assumed to have been a k×nq matrix. nq = m if
    33  // side == blas.Left, while nq = n if side == blas.Right.
    34  //
    35  // tau must have length min(nq,k), and Dormbr will panic otherwise. tau contains
    36  // the elementary reflectors to construct Q or P depending on the value of
    37  // vect.
    38  //
    39  // work must have length at least max(1,lwork), and lwork must be either -1 or
    40  // at least max(1,n) if side == blas.Left, and at least max(1,m) if side ==
    41  // blas.Right. For optimum performance lwork should be at least n*nb if side ==
    42  // blas.Left, and at least m*nb if side == blas.Right, where nb is the optimal
    43  // block size. On return, work[0] will contain the optimal value of lwork.
    44  //
    45  // If lwork == -1, the function only calculates the optimal value of lwork and
    46  // returns it in work[0].
    47  //
    48  // Dormbr is an internal routine. It is exported for testing purposes.
    49  func (impl Implementation) Dormbr(vect lapack.ApplyOrtho, side blas.Side, trans blas.Transpose, m, n, k int, a []float64, lda int, tau, c []float64, ldc int, work []float64, lwork int) {
    50  	nq := n
    51  	nw := m
    52  	if side == blas.Left {
    53  		nq = m
    54  		nw = n
    55  	}
    56  	applyQ := vect == lapack.ApplyQ
    57  	switch {
    58  	case !applyQ && vect != lapack.ApplyP:
    59  		panic(badApplyOrtho)
    60  	case side != blas.Left && side != blas.Right:
    61  		panic(badSide)
    62  	case trans != blas.NoTrans && trans != blas.Trans:
    63  		panic(badTrans)
    64  	case m < 0:
    65  		panic(mLT0)
    66  	case n < 0:
    67  		panic(nLT0)
    68  	case k < 0:
    69  		panic(kLT0)
    70  	case applyQ && lda < max(1, min(nq, k)):
    71  		panic(badLdA)
    72  	case !applyQ && lda < max(1, nq):
    73  		panic(badLdA)
    74  	case ldc < max(1, n):
    75  		panic(badLdC)
    76  	case lwork < max(1, nw) && lwork != -1:
    77  		panic(badLWork)
    78  	case len(work) < max(1, lwork):
    79  		panic(shortWork)
    80  	}
    81  
    82  	// Quick return if possible.
    83  	if m == 0 || n == 0 {
    84  		work[0] = 1
    85  		return
    86  	}
    87  
    88  	// The current implementation does not use opts, but a future change may
    89  	// use these options so construct them.
    90  	var opts string
    91  	if side == blas.Left {
    92  		opts = "L"
    93  	} else {
    94  		opts = "R"
    95  	}
    96  	if trans == blas.Trans {
    97  		opts += "T"
    98  	} else {
    99  		opts += "N"
   100  	}
   101  	var nb int
   102  	if applyQ {
   103  		if side == blas.Left {
   104  			nb = impl.Ilaenv(1, "DORMQR", opts, m-1, n, m-1, -1)
   105  		} else {
   106  			nb = impl.Ilaenv(1, "DORMQR", opts, m, n-1, n-1, -1)
   107  		}
   108  	} else {
   109  		if side == blas.Left {
   110  			nb = impl.Ilaenv(1, "DORMLQ", opts, m-1, n, m-1, -1)
   111  		} else {
   112  			nb = impl.Ilaenv(1, "DORMLQ", opts, m, n-1, n-1, -1)
   113  		}
   114  	}
   115  	lworkopt := max(1, nw) * nb
   116  	if lwork == -1 {
   117  		work[0] = float64(lworkopt)
   118  		return
   119  	}
   120  
   121  	minnqk := min(nq, k)
   122  	switch {
   123  	case applyQ && len(a) < (nq-1)*lda+minnqk:
   124  		panic(shortA)
   125  	case !applyQ && len(a) < (minnqk-1)*lda+nq:
   126  		panic(shortA)
   127  	case len(tau) < minnqk:
   128  		panic(shortTau)
   129  	case len(c) < (m-1)*ldc+n:
   130  		panic(shortC)
   131  	}
   132  
   133  	if applyQ {
   134  		// Change the operation to get Q depending on the size of the initial
   135  		// matrix to Dgebrd. The size matters due to the storage location of
   136  		// the off-diagonal elements.
   137  		if nq >= k {
   138  			impl.Dormqr(side, trans, m, n, k, a, lda, tau[:k], c, ldc, work, lwork)
   139  		} else if nq > 1 {
   140  			mi := m
   141  			ni := n - 1
   142  			i1 := 0
   143  			i2 := 1
   144  			if side == blas.Left {
   145  				mi = m - 1
   146  				ni = n
   147  				i1 = 1
   148  				i2 = 0
   149  			}
   150  			impl.Dormqr(side, trans, mi, ni, nq-1, a[lda:], lda, tau[:nq-1], c[i1*ldc+i2:], ldc, work, lwork)
   151  		}
   152  		work[0] = float64(lworkopt)
   153  		return
   154  	}
   155  
   156  	transt := blas.Trans
   157  	if trans == blas.Trans {
   158  		transt = blas.NoTrans
   159  	}
   160  
   161  	// Change the operation to get P depending on the size of the initial
   162  	// matrix to Dgebrd. The size matters due to the storage location of
   163  	// the off-diagonal elements.
   164  	if nq > k {
   165  		impl.Dormlq(side, transt, m, n, k, a, lda, tau, c, ldc, work, lwork)
   166  	} else if nq > 1 {
   167  		mi := m
   168  		ni := n - 1
   169  		i1 := 0
   170  		i2 := 1
   171  		if side == blas.Left {
   172  			mi = m - 1
   173  			ni = n
   174  			i1 = 1
   175  			i2 = 0
   176  		}
   177  		impl.Dormlq(side, transt, mi, ni, nq-1, a[1:], lda, tau, c[i1*ldc+i2:], ldc, work, lwork)
   178  	}
   179  	work[0] = float64(lworkopt)
   180  }