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 }