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