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.
10 "gonum.org/v1/gonum/blas"
11 "gonum.org/v1/gonum/blas/blas64"
12 "gonum.org/v1/gonum/lapack"
13 "gonum.org/v1/gonum/lapack/lapack64"
16 // LQ is a type for creating and using the LQ factorization of a matrix.
23 func (lq *LQ) updateCond(norm lapack.MatrixNorm) {
24 // Since A = L*Q, and Q is orthogonal, we get for the condition number κ
25 // κ(A) := |A| |A^-1| = |L*Q| |(L*Q)^-1| = |L| |Q^T * L^-1|
26 // = |L| |L^-1| = κ(L),
27 // where we used that fact that Q^-1 = Q^T. However, this assumes that
28 // the matrix norm is invariant under orthogonal transformations which
29 // is not the case for CondNorm. Hopefully the error is negligible: κ
30 // is only a qualitative measure anyway.
32 work := getFloats(3*m, false)
33 iwork := getInts(m, false)
34 l := lq.lq.asTriDense(m, blas.NonUnit, blas.Lower)
35 v := lapack64.Trcon(norm, l.mat, work, iwork)
41 // Factorize computes the LQ factorization of an m×n matrix a where n <= m. The LQ
42 // factorization always exists even if A is singular.
44 // The LQ decomposition is a factorization of the matrix A such that A = L * Q.
45 // The matrix Q is an orthonormal n×n matrix, and L is an m×n upper triangular matrix.
46 // L and Q can be extracted from the LTo and QTo methods.
47 func (lq *LQ) Factorize(a Matrix) {
48 lq.factorize(a, CondNorm)
51 func (lq *LQ) factorize(a Matrix, norm lapack.MatrixNorm) {
62 lq.tau = make([]float64, k)
63 lapack64.Gelqf(lq.lq.mat, lq.tau, work, -1)
64 work = getFloats(int(work[0]), false)
65 lapack64.Gelqf(lq.lq.mat, lq.tau, work, len(work))
70 // Cond returns the condition number for the factorized matrix.
71 // Cond will panic if the receiver does not contain a successful factorization.
72 func (lq *LQ) Cond() float64 {
73 if lq.lq == nil || lq.lq.IsZero() {
74 panic("lq: no decomposition computed")
79 // TODO(btracey): Add in the "Reduced" forms for extracting the m×m orthogonal
80 // and upper triangular matrices.
82 // LTo extracts the m×n lower trapezoidal matrix from a LQ decomposition.
83 // If dst is nil, a new matrix is allocated. The resulting L matrix is returned.
84 func (lq *LQ) LTo(dst *Dense) *Dense {
87 dst = NewDense(r, c, nil)
92 // Disguise the LQ as a lower triangular.
94 mat: blas64.Triangular{
96 Stride: lq.lq.mat.Stride,
108 // Zero right of the triangular.
109 for i := 0; i < r; i++ {
110 zero(dst.mat.Data[i*dst.mat.Stride+r : i*dst.mat.Stride+c])
116 // QTo extracts the n×n orthonormal matrix Q from an LQ decomposition.
117 // If dst is nil, a new matrix is allocated. The resulting Q matrix is returned.
118 func (lq *LQ) QTo(dst *Dense) *Dense {
121 dst = NewDense(c, c, nil)
123 dst.reuseAsZeroed(c, c)
129 for i := 0; i < c; i++ {
133 // Construct Q from the elementary reflectors.
135 lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, q, work, -1)
136 work = getFloats(int(work[0]), false)
137 lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, q, work, len(work))
143 // Solve finds a minimum-norm solution to a system of linear equations defined
144 // by the matrices A and b, where A is an m×n matrix represented in its LQ factorized
145 // form. If A is singular or near-singular a Condition error is returned.
146 // See the documentation for Condition for more information.
148 // The minimization problem solved depends on the input parameters.
149 // If trans == false, find the minimum norm solution of A * X = b.
150 // If trans == true, find X such that ||A*X - b||_2 is minimized.
151 // The solution matrix, X, is stored in place into m.
152 func (lq *LQ) Solve(m *Dense, trans bool, b Matrix) error {
156 // The LQ solve algorithm stores the result in-place into the right hand side.
157 // The storage for the answer must be large enough to hold both b and x.
158 // However, this method's receiver must be the size of x. Copy b, and then
159 // copy the result into m at the end.
171 // Do not need to worry about overlap between m and b because x has its own
172 // independent storage.
173 x := getWorkspace(max(r, c), bc, false)
175 t := lq.lq.asTriDense(lq.lq.mat.Rows, blas.NonUnit, blas.Lower).mat
178 lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, x.mat, work, -1)
179 work = getFloats(int(work[0]), false)
180 lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, x.mat, work, len(work))
183 ok := lapack64.Trtrs(blas.Trans, t, x.mat)
185 return Condition(math.Inf(1))
188 ok := lapack64.Trtrs(blas.NoTrans, t, x.mat)
190 return Condition(math.Inf(1))
192 for i := r; i < c; i++ {
193 zero(x.mat.Data[i*x.mat.Stride : i*x.mat.Stride+bc])
196 lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, x.mat, work, -1)
197 work = getFloats(int(work[0]), false)
198 lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, x.mat, work, len(work))
201 // M was set above to be the correct size for the result.
204 if lq.cond > ConditionTolerance {
205 return Condition(lq.cond)
210 // SolveVec finds a minimum-norm solution to a system of linear equations.
211 // See LQ.Solve for the full documentation.
212 func (lq *LQ) SolveVec(v *VecDense, trans bool, b Vector) error {
214 if _, bc := b.Dims(); bc != 1 {
218 // The Solve implementation is non-trivial, so rather than duplicate the code,
219 // instead recast the VecDenses as Dense and call the matrix code.
221 if rv, ok := b.(RawVectorer); ok {
222 bmat := rv.RawVector()
226 b := VecDense{mat: bmat, n: b.Len()}
234 return lq.Solve(v.asDense(), trans, bm)