func (lq *LQ) updateCond() { // A = LQ, where Q is orthonormal. Orthonormal multiplications do not change // the condition number. Thus, ||A|| = ||L|| ||Q|| = ||Q||. m := lq.lq.mat.Rows work := make([]float64, 3*m) iwork := make([]int, m) l := lq.lq.asTriDense(m, blas.NonUnit, blas.Lower) v := lapack64.Trcon(matrix.CondNorm, l.mat, work, iwork) lq.cond = 1 / v }
func (qr *QR) updateCond() { // A = QR, where Q is orthonormal. Orthonormal multiplications do not change // the condition number. Thus, ||A|| = ||Q|| ||R|| = ||R||. n := qr.qr.mat.Cols work := make([]float64, 3*n) iwork := make([]int, n) r := qr.qr.asTriDense(n, blas.NonUnit, blas.Upper) v := lapack64.Trcon(matrix.CondNorm, r.mat, work, iwork) qr.cond = 1 / v }
// InverseTri computes the inverse of the triangular matrix a, storing the result // into the receiver. If a is ill-conditioned, a Condition error will be returned. // Note that matrix inversion is numerically unstable, and should generally be // avoided where possible, for example by using the Solve routines. func (t *TriDense) InverseTri(a Triangular) error { if rt, ok := a.(RawTriangular); ok { t.checkOverlap(rt.RawTriangular()) } n, _ := a.Triangle() t.reuseAs(a.Triangle()) t.Copy(a) work := make([]float64, 3*n) iwork := make([]int, n) cond := lapack64.Trcon(matrix.CondNorm, t.mat, work, iwork) if math.IsInf(cond, 1) { return matrix.Condition(cond) } ok := lapack64.Trtri(t.mat) if !ok { return matrix.Condition(math.Inf(1)) } if cond > matrix.ConditionTolerance { return matrix.Condition(cond) } return nil }
// Cond returns the condition number of the given matrix under the given norm. // The condition number must be based on the 1-norm, 2-norm or ∞-norm. // Cond will panic with matrix.ErrShape if the matrix has zero size. // // BUG(btracey): The computation of the 1-norm and ∞-norm for non-square matrices // is innacurate, although is typically the right order of magnitude. See // https://github.com/xianyi/OpenBLAS/issues/636. While the value returned will // change with the resolution of this bug, the result from Cond will match the // condition number used internally. func Cond(a Matrix, norm float64) float64 { m, n := a.Dims() if m == 0 || n == 0 { panic(matrix.ErrShape) } var lnorm lapack.MatrixNorm switch norm { default: panic("mat64: bad norm value") case 1: lnorm = lapack.MaxColumnSum case 2: var svd SVD ok := svd.Factorize(a, matrix.SVDNone) if !ok { return math.Inf(1) } return svd.Cond() case math.Inf(1): lnorm = lapack.MaxRowSum } if m == n { // Use the LU decomposition to compute the condition number. tmp := getWorkspace(m, n, false) tmp.Copy(a) work := make([]float64, 4*n) aNorm := lapack64.Lange(lnorm, tmp.mat, work) pivot := make([]int, m) lapack64.Getrf(tmp.mat, pivot) iwork := make([]int, n) v := lapack64.Gecon(lnorm, tmp.mat, aNorm, work, iwork) putWorkspace(tmp) return 1 / v } if m > n { // Use the QR factorization to compute the condition number. tmp := getWorkspace(m, n, false) tmp.Copy(a) work := make([]float64, 3*n) tau := make([]float64, min(m, n)) lapack64.Geqrf(tmp.mat, tau, work, -1) if int(work[0]) > len(work) { work = make([]float64, int(work[0])) } lapack64.Geqrf(tmp.mat, tau, work, len(work)) iwork := make([]int, n) r := tmp.asTriDense(n, blas.NonUnit, blas.Upper) v := lapack64.Trcon(lnorm, r.mat, work, iwork) putWorkspace(tmp) return 1 / v } // Use the LQ factorization to compute the condition number. tmp := getWorkspace(m, n, false) tmp.Copy(a) work := make([]float64, 3*m) tau := make([]float64, min(m, n)) lapack64.Gelqf(tmp.mat, tau, work, -1) if int(work[0]) > len(work) { work = make([]float64, int(work[0])) } lapack64.Gelqf(tmp.mat, tau, work, len(work)) iwork := make([]int, m) l := tmp.asTriDense(m, blas.NonUnit, blas.Lower) v := lapack64.Trcon(lnorm, l.mat, work, iwork) putWorkspace(tmp) return 1 / v }
// Cond returns the condition number of the given matrix under the given norm. // The condition number must be based on the 1-norm, 2-norm or ∞-norm. // Cond will panic with matrix.ErrShape if the matrix has zero size. // // BUG(btracey): The computation of the 1-norm and ∞-norm for non-square matrices // is innacurate, although is typically the right order of magnitude. See // https://github.com/xianyi/OpenBLAS/issues/636. While the value returned will // change with the resolution of this bug, the result from Cond will match the // condition number used internally. func Cond(a Matrix, norm float64) float64 { m, n := a.Dims() if m == 0 || n == 0 { panic(matrix.ErrShape) } var lnorm lapack.MatrixNorm switch norm { default: panic("mat64: bad norm value") case 1: lnorm = lapack.MaxColumnSum case 2: // Use SVD to compute the condition number. // TODO(btracey): Replace this with temporary workspace when SVD is fixed. tmp := NewDense(m, n, nil) tmp.Copy(a) svd := SVD(tmp, math.Pow(2, -52.0), math.Pow(2, -966.0), false, false) return svd.Cond() case math.Inf(1): lnorm = lapack.MaxRowSum } if m == n { // Use the LU decomposition to compute the condition number. tmp := getWorkspace(m, n, false) tmp.Copy(a) work := make([]float64, 4*n) aNorm := lapack64.Lange(lnorm, tmp.mat, work) pivot := make([]int, m) lapack64.Getrf(tmp.mat, pivot) iwork := make([]int, n) v := lapack64.Gecon(lnorm, tmp.mat, aNorm, work, iwork) putWorkspace(tmp) return 1 / v } if m > n { // Use the QR factorization to compute the condition number. tmp := getWorkspace(m, n, false) tmp.Copy(a) work := make([]float64, 3*n) tau := make([]float64, min(m, n)) lapack64.Geqrf(tmp.mat, tau, work, -1) if int(work[0]) > len(work) { work = make([]float64, int(work[0])) } lapack64.Geqrf(tmp.mat, tau, work, len(work)) iwork := make([]int, n) r := tmp.asTriDense(n, blas.NonUnit, blas.Upper) v := lapack64.Trcon(lnorm, r.mat, work, iwork) putWorkspace(tmp) return 1 / v } // Use the LQ factorization to compute the condition number. tmp := getWorkspace(m, n, false) tmp.Copy(a) work := make([]float64, 3*m) tau := make([]float64, min(m, n)) lapack64.Gelqf(tmp.mat, tau, work, -1) if int(work[0]) > len(work) { work = make([]float64, int(work[0])) } lapack64.Gelqf(tmp.mat, tau, work, len(work)) iwork := make([]int, m) l := tmp.asTriDense(m, blas.NonUnit, blas.Lower) v := lapack64.Trcon(lnorm, l.mat, work, iwork) putWorkspace(tmp) return 1 / v }
// Solve solves a minimum-norm solution to a system of linear equations defined // by the matrices a and b. If a is singular or near-singular a Condition error // is returned. Please see the documentation for Condition for more information. // // The minimization problem solved depends on the input parameters. // 1. If m >= n and trans == false, find X such that ||a*X - b||_2 is minimized. // 2. If m < n and trans == false, find the minimum norm solution of a * X = b. // 3. If m >= n and trans == true, find the minimum norm solution of a^T * X = b. // 4. If m < n and trans == true, find X such that ||a*X - b||_2 is minimized. // The solution matrix, X, is stored in place into the receiver. func (m *Dense) Solve(a, b Matrix) error { ar, ac := a.Dims() br, bc := b.Dims() if ar != br { panic(ErrShape) } m.reuseAs(ac, bc) // TODO(btracey): Add special cases for SymDense, etc. aMat, aTrans := untranspose(a) bMat, bTrans := untranspose(b) switch rma := aMat.(type) { case RawTriangular: side := blas.Left tA := blas.NoTrans if aTrans { tA = blas.Trans } if m != bMat { m.Copy(b) } else if bTrans { // m and b share data so Copy cannot be used directly. tmp := getWorkspace(br, bc, false) tmp.Copy(b) m.Copy(tmp) putWorkspace(tmp) } rm := rma.RawTriangular() blas64.Trsm(side, tA, 1, rm, m.mat) work := make([]float64, 3*rm.N) iwork := make([]int, rm.N) cond := lapack64.Trcon(condNorm, rm, work, iwork) if cond > condTol { return Condition(cond) } return nil } switch { case ar == ac: if a == b { // x = I. if ar == 1 { m.mat.Data[0] = 1 return nil } for i := 0; i < ar; i++ { v := m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+ac] zero(v) v[i] = 1 } return nil } var lu LU lu.Factorize(a) return m.SolveLU(&lu, false, b) case ar > ac: var qr QR qr.Factorize(a) return m.SolveQR(&qr, false, b) default: var lq LQ lq.Factorize(a) return m.SolveLQ(&lq, false, b) } }