github.com/gonum/matrix@v0.0.0-20181209220409-c518dec07be9/mat64/lq.go (about) 1 // Copyright ©2013 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 mat64 6 7 import ( 8 "math" 9 10 "github.com/gonum/blas" 11 "github.com/gonum/blas/blas64" 12 "github.com/gonum/lapack/lapack64" 13 "github.com/gonum/matrix" 14 ) 15 16 // LQ is a type for creating and using the LQ factorization of a matrix. 17 type LQ struct { 18 lq *Dense 19 tau []float64 20 cond float64 21 } 22 23 func (lq *LQ) updateCond() { 24 // A = LQ, where Q is orthonormal. Orthonormal multiplications do not change 25 // the condition number. Thus, ||A|| = ||L|| ||Q|| = ||Q||. 26 m := lq.lq.mat.Rows 27 work := make([]float64, 3*m) 28 iwork := make([]int, m) 29 l := lq.lq.asTriDense(m, blas.NonUnit, blas.Lower) 30 v := lapack64.Trcon(matrix.CondNorm, l.mat, work, iwork) 31 lq.cond = 1 / v 32 } 33 34 // Factorize computes the LQ factorization of an m×n matrix a where n <= m. The LQ 35 // factorization always exists even if A is singular. 36 // 37 // The LQ decomposition is a factorization of the matrix A such that A = L * Q. 38 // The matrix Q is an orthonormal n×n matrix, and L is an m×n upper triangular matrix. 39 // L and Q can be extracted from the LFromLQ and QFromLQ methods on Dense. 40 func (lq *LQ) Factorize(a Matrix) { 41 m, n := a.Dims() 42 if m > n { 43 panic(matrix.ErrShape) 44 } 45 k := min(m, n) 46 if lq.lq == nil { 47 lq.lq = &Dense{} 48 } 49 lq.lq.Clone(a) 50 work := make([]float64, 1) 51 lq.tau = make([]float64, k) 52 lapack64.Gelqf(lq.lq.mat, lq.tau, work, -1) 53 work = make([]float64, int(work[0])) 54 lapack64.Gelqf(lq.lq.mat, lq.tau, work, len(work)) 55 lq.updateCond() 56 } 57 58 // TODO(btracey): Add in the "Reduced" forms for extracting the m×m orthogonal 59 // and upper triangular matrices. 60 61 // LFromLQ extracts the m×n lower trapezoidal matrix from a LQ decomposition. 62 func (m *Dense) LFromLQ(lq *LQ) { 63 r, c := lq.lq.Dims() 64 m.reuseAs(r, c) 65 66 // Disguise the LQ as a lower triangular 67 t := &TriDense{ 68 mat: blas64.Triangular{ 69 N: r, 70 Stride: lq.lq.mat.Stride, 71 Data: lq.lq.mat.Data, 72 Uplo: blas.Lower, 73 Diag: blas.NonUnit, 74 }, 75 cap: lq.lq.capCols, 76 } 77 m.Copy(t) 78 79 if r == c { 80 return 81 } 82 // Zero right of the triangular. 83 for i := 0; i < r; i++ { 84 zero(m.mat.Data[i*m.mat.Stride+r : i*m.mat.Stride+c]) 85 } 86 } 87 88 // QFromLQ extracts the n×n orthonormal matrix Q from an LQ decomposition. 89 func (m *Dense) QFromLQ(lq *LQ) { 90 r, c := lq.lq.Dims() 91 m.reuseAs(c, c) 92 93 // Set Q = I. 94 for i := 0; i < c; i++ { 95 v := m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c] 96 zero(v) 97 v[i] = 1 98 } 99 100 // Construct Q from the elementary reflectors. 101 h := blas64.General{ 102 Rows: c, 103 Cols: c, 104 Stride: c, 105 Data: make([]float64, c*c), 106 } 107 qCopy := getWorkspace(c, c, false) 108 v := blas64.Vector{ 109 Inc: 1, 110 Data: make([]float64, c), 111 } 112 for i := 0; i < r; i++ { 113 // Set h = I. 114 zero(h.Data) 115 for j := 0; j < len(h.Data); j += c + 1 { 116 h.Data[j] = 1 117 } 118 119 // Set the vector data as the elementary reflector. 120 for j := 0; j < i; j++ { 121 v.Data[j] = 0 122 } 123 v.Data[i] = 1 124 for j := i + 1; j < c; j++ { 125 v.Data[j] = lq.lq.mat.Data[i*lq.lq.mat.Stride+j] 126 } 127 128 // Compute the multiplication matrix. 129 blas64.Ger(-lq.tau[i], v, v, h) 130 qCopy.Copy(m) 131 blas64.Gemm(blas.NoTrans, blas.NoTrans, 132 1, h, qCopy.mat, 133 0, m.mat) 134 } 135 } 136 137 // SolveLQ finds a minimum-norm solution to a system of linear equations defined 138 // by the matrices A and b, where A is an m×n matrix represented in its LQ factorized 139 // form. If A is singular or near-singular a Condition error is returned. Please 140 // see the documentation for Condition for more information. 141 // 142 // The minimization problem solved depends on the input parameters. 143 // If trans == false, find the minimum norm solution of A * X = b. 144 // If trans == true, find X such that ||A*X - b||_2 is minimized. 145 // The solution matrix, X, is stored in place into the receiver. 146 func (m *Dense) SolveLQ(lq *LQ, trans bool, b Matrix) error { 147 r, c := lq.lq.Dims() 148 br, bc := b.Dims() 149 150 // The LQ solve algorithm stores the result in-place into the right hand side. 151 // The storage for the answer must be large enough to hold both b and x. 152 // However, this method's receiver must be the size of x. Copy b, and then 153 // copy the result into m at the end. 154 if trans { 155 if c != br { 156 panic(matrix.ErrShape) 157 } 158 m.reuseAs(r, bc) 159 } else { 160 if r != br { 161 panic(matrix.ErrShape) 162 } 163 m.reuseAs(c, bc) 164 } 165 // Do not need to worry about overlap between m and b because x has its own 166 // independent storage. 167 x := getWorkspace(max(r, c), bc, false) 168 x.Copy(b) 169 t := lq.lq.asTriDense(lq.lq.mat.Rows, blas.NonUnit, blas.Lower).mat 170 if trans { 171 work := make([]float64, 1) 172 lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, x.mat, work, -1) 173 work = make([]float64, int(work[0])) 174 lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, x.mat, work, len(work)) 175 176 ok := lapack64.Trtrs(blas.Trans, t, x.mat) 177 if !ok { 178 return matrix.Condition(math.Inf(1)) 179 } 180 } else { 181 ok := lapack64.Trtrs(blas.NoTrans, t, x.mat) 182 if !ok { 183 return matrix.Condition(math.Inf(1)) 184 } 185 for i := r; i < c; i++ { 186 zero(x.mat.Data[i*x.mat.Stride : i*x.mat.Stride+bc]) 187 } 188 work := make([]float64, 1) 189 lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, x.mat, work, -1) 190 work = make([]float64, int(work[0])) 191 lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, x.mat, work, len(work)) 192 } 193 // M was set above to be the correct size for the result. 194 m.Copy(x) 195 putWorkspace(x) 196 if lq.cond > matrix.ConditionTolerance { 197 return matrix.Condition(lq.cond) 198 } 199 return nil 200 } 201 202 // SolveLQVec finds a minimum-norm solution to a system of linear equations. 203 // Please see Dense.SolveLQ for the full documentation. 204 func (v *Vector) SolveLQVec(lq *LQ, trans bool, b *Vector) error { 205 if v != b { 206 v.checkOverlap(b.mat) 207 } 208 r, c := lq.lq.Dims() 209 // The Solve implementation is non-trivial, so rather than duplicate the code, 210 // instead recast the Vectors as Dense and call the matrix code. 211 if trans { 212 v.reuseAs(r) 213 } else { 214 v.reuseAs(c) 215 } 216 return v.asDense().SolveLQ(lq, trans, b.asDense()) 217 }