// SolveLUVec solves a system of linear equations using the LU decomposition of a matrix. // It computes // A * x = b if trans == false // A^T * x = b if trans == true // In both cases, A is represeneted in LU factorized form, and the matrix x is // stored into the receiver. // // If A is singular or near-singular a Condition error is returned. Please see // the documentation for Condition for more information. func (v *Vector) SolveLUVec(lu *LU, trans bool, b *Vector) error { _, n := lu.lu.Dims() bn := b.Len() if bn != n { panic(matrix.ErrShape) } // TODO(btracey): Should test the condition number instead of testing that // the determinant is exactly zero. if lu.Det() == 0 { return matrix.Condition(math.Inf(1)) } v.reuseAs(n) var restore func() if v == b { v, restore = v.isolatedWorkspace(b) defer restore() } v.CopyVec(b) vMat := blas64.General{ Rows: n, Cols: 1, Stride: v.mat.Inc, Data: v.mat.Data, } t := blas.NoTrans if trans { t = blas.Trans } lapack64.Getrs(t, lu.lu.mat, vMat, lu.pivot) if lu.cond > matrix.ConditionTolerance { return matrix.Condition(lu.cond) } return nil }
// SolveLU solves a system of linear equations using the LU decomposition of a matrix. // It computes // A * x = b if trans == false // A^T * x = b if trans == true // In both cases, A is represented in LU factorized form, and the matrix x is // stored into the receiver. // // If A is singular or near-singular a Condition error is returned. Please see // the documentation for Condition for more information. func (m *Dense) SolveLU(lu *LU, trans bool, b Matrix) error { _, n := lu.lu.Dims() br, bc := b.Dims() if br != n { panic(matrix.ErrShape) } // TODO(btracey): Should test the condition number instead of testing that // the determinant is exactly zero. if lu.Det() == 0 { return matrix.Condition(math.Inf(1)) } m.reuseAs(n, bc) bU, _ := untranspose(b) var restore func() if m == bU { m, restore = m.isolatedWorkspace(bU) defer restore() } else if rm, ok := bU.(RawMatrixer); ok { m.checkOverlap(rm.RawMatrix()) } m.Copy(b) t := blas.NoTrans if trans { t = blas.Trans } lapack64.Getrs(t, lu.lu.mat, m.mat, lu.pivot) if lu.cond > matrix.ConditionTolerance { return matrix.Condition(lu.cond) } return nil }
// SolveLQ finds a minimum-norm solution to a system of linear equations defined // by the matrices A and b, where A is an m×n matrix represented in its LQ factorized // form. 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. // If trans == false, find the minimum norm solution of A * X = b. // If 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) SolveLQ(lq *LQ, trans bool, b Matrix) error { r, c := lq.lq.Dims() br, bc := b.Dims() // The LQ solve algorithm stores the result in-place into the right hand side. // The storage for the answer must be large enough to hold both b and x. // However, this method's receiver must be the size of x. Copy b, and then // copy the result into m at the end. if trans { if c != br { panic(matrix.ErrShape) } m.reuseAs(r, bc) } else { if r != br { panic(matrix.ErrShape) } m.reuseAs(c, bc) } // Do not need to worry about overlap between m and b because x has its own // independent storage. x := getWorkspace(max(r, c), bc, false) x.Copy(b) t := lq.lq.asTriDense(lq.lq.mat.Rows, blas.NonUnit, blas.Lower).mat if trans { work := make([]float64, 1) lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, x.mat, work, -1) work = make([]float64, int(work[0])) lapack64.Ormlq(blas.Left, blas.NoTrans, lq.lq.mat, lq.tau, x.mat, work, len(work)) ok := lapack64.Trtrs(blas.Trans, t, x.mat) if !ok { return matrix.Condition(math.Inf(1)) } } else { ok := lapack64.Trtrs(blas.NoTrans, t, x.mat) if !ok { return matrix.Condition(math.Inf(1)) } for i := r; i < c; i++ { zero(x.mat.Data[i*x.mat.Stride : i*x.mat.Stride+bc]) } work := make([]float64, 1) lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, x.mat, work, -1) work = make([]float64, int(work[0])) lapack64.Ormlq(blas.Left, blas.Trans, lq.lq.mat, lq.tau, x.mat, work, len(work)) } // M was set above to be the correct size for the result. m.Copy(x) putWorkspace(x) if lq.cond > matrix.ConditionTolerance { return matrix.Condition(lq.cond) } return nil }
// 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 }
// Inverse computes the inverse of the 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 (m *Dense) Inverse(a Matrix) error { // TODO(btracey): Special case for RawTriangular, etc. r, c := a.Dims() if r != c { panic(matrix.ErrSquare) } m.reuseAs(a.Dims()) aU, aTrans := untranspose(a) switch rm := aU.(type) { case RawMatrixer: if m != aU || aTrans { if m == aU || m.checkOverlap(rm.RawMatrix()) { tmp := getWorkspace(r, c, false) tmp.Copy(a) m.Copy(tmp) putWorkspace(tmp) break } m.Copy(a) } default: if m != aU { m.Copy(a) } else if aTrans { // m and a share data so Copy cannot be used directly. tmp := getWorkspace(r, c, false) tmp.Copy(a) m.Copy(tmp) putWorkspace(tmp) } } ipiv := make([]int, r) lapack64.Getrf(m.mat, ipiv) work := make([]float64, 1, 4*r) // must be at least 4*r for cond. lapack64.Getri(m.mat, ipiv, work, -1) if int(work[0]) > 4*r { work = make([]float64, int(work[0])) } else { work = work[:4*r] } lapack64.Getri(m.mat, ipiv, work, len(work)) norm := lapack64.Lange(matrix.CondNorm, m.mat, work) cond := lapack64.Gecon(matrix.CondNorm, m.mat, norm, work, ipiv) // reuse ipiv if cond > matrix.ConditionTolerance { return matrix.Condition(cond) } return nil }
// SolveCholesky finds the matrix m that solves A * m = b where A is represented // by the cholesky decomposition, placing the result in the receiver. func (m *Dense) SolveCholesky(chol *Cholesky, b Matrix) error { n := chol.chol.mat.N bm, bn := b.Dims() if n != bm { panic(matrix.ErrShape) } m.reuseAs(bm, bn) if b != m { m.Copy(b) } blas64.Trsm(blas.Left, blas.Trans, 1, chol.chol.mat, m.mat) blas64.Trsm(blas.Left, blas.NoTrans, 1, chol.chol.mat, m.mat) if chol.cond > matrix.ConditionTolerance { return matrix.Condition(chol.cond) } return nil }
// SolveCholeskyVec finds the vector v that solves A * v = b where A is represented // by the Cholesky decomposition, placing the result in the receiver. func (v *Vector) SolveCholeskyVec(chol *Cholesky, b *Vector) error { n := chol.chol.mat.N vn := b.Len() if vn != n { panic(matrix.ErrShape) } v.reuseAs(n) if v != b { v.CopyVec(b) } blas64.Trsv(blas.Trans, chol.chol.mat, v.mat) blas64.Trsv(blas.NoTrans, chol.chol.mat, v.mat) if chol.cond > matrix.ConditionTolerance { return matrix.Condition(chol.cond) } return nil }
// Solve finds 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: // - if m >= n, find X such that ||A*X - B||_2 is minimized, // - if m < n, find the minimum norm solution of A * X = B. // 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(matrix.ErrShape) } m.reuseAs(ac, bc) // TODO(btracey): Add special cases for SymDense, etc. aU, aTrans := untranspose(a) bU, bTrans := untranspose(b) switch rma := aU.(type) { case RawTriangular: side := blas.Left tA := blas.NoTrans if aTrans { tA = blas.Trans } switch rm := bU.(type) { case RawMatrixer: if m != bU || bTrans { if m == bU || m.checkOverlap(rm.RawMatrix()) { tmp := getWorkspace(br, bc, false) tmp.Copy(b) m.Copy(tmp) putWorkspace(tmp) break } m.Copy(b) } default: if m != bU { 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(matrix.CondNorm, rm, work, iwork) if cond > matrix.ConditionTolerance { return matrix.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) } }