// SolveCholesky finds the matrix m that solves A * m = b where A = L * L^T or // A = U^T * U, and U or L are represented by t, placing the result in the // receiver. func (m *Dense) SolveCholesky(t Triangular, b Matrix) { _, n := t.Dims() bm, bn := b.Dims() if n != bm { panic(ErrShape) } m.reuseAs(bm, bn) if b != m { m.Copy(b) } // TODO(btracey): Implement an algorithm that doesn't require a copy into // a blas64.Triangular. ta := getBlasTriangular(t) switch ta.Uplo { case blas.Upper: blas64.Trsm(blas.Left, blas.Trans, 1, ta, m.mat) blas64.Trsm(blas.Left, blas.NoTrans, 1, ta, m.mat) case blas.Lower: blas64.Trsm(blas.Left, blas.NoTrans, 1, ta, m.mat) blas64.Trsm(blas.Left, blas.Trans, 1, ta, m.mat) default: panic(badTriangle) } }
// SolveTri finds the matrix x that solves op(A) * X = B where A is a triangular // matrix and op is specified by trans. func (m *Dense) SolveTri(a Triangular, trans bool, b Matrix) { n, _ := a.Triangle() bm, bn := b.Dims() if n != bm { panic(ErrShape) } m.reuseAs(bm, bn) if b != m { m.Copy(b) } // TODO(btracey): Implement an algorithm that doesn't require a copy into // a blas64.Triangular. ta := getBlasTriangular(a) t := blas.NoTrans if trans { t = blas.Trans } switch ta.Uplo { case blas.Upper, blas.Lower: blas64.Trsm(blas.Left, t, 1, ta, m.mat) default: panic(badTriangle) } }
// 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 }
// SolveCholesky finds the matrix x that solves A * X = B where A = L * L^T or // A = U^T * U, and U or L are represented by t. The matrix A must be symmetric // and positive definite. func (m *Dense) SolveCholesky(t *Triangular, b Matrix) { _, n := t.Dims() bm, bn := b.Dims() if n != bm { panic(ErrShape) } m.reuseAs(bm, bn) if b != m { m.Copy(b) } switch t.mat.Uplo { case blas.Upper: blas64.Trsm(blas.Left, blas.Trans, 1, t.mat, m.mat) blas64.Trsm(blas.Left, blas.NoTrans, 1, t.mat, m.mat) case blas.Lower: blas64.Trsm(blas.Left, blas.NoTrans, 1, t.mat, m.mat) blas64.Trsm(blas.Left, blas.Trans, 1, t.mat, m.mat) default: panic(badTriangle) } }
// Solve computes minimum norm least squares solution of a.x = b where b has as many rows as a. // A matrix x is returned that minimizes the two norm of Q*R*X-B. Solve will panic // if a is not full rank. func (f LQFactor) Solve(b *Dense) (x *Dense) { lq := f.LQ lDiag := f.lDiag m, n := lq.Dims() bm, bn := b.Dims() if bm != m { panic(ErrShape) } if !f.IsFullRank() { panic(ErrSingular) } x = NewDense(n, bn, nil) x.Copy(b) tau := make([]float64, m) for i := range tau { tau[i] = lq.at(i, i) lq.set(i, i, lDiag[i]) } lqT := blas64.Triangular{ // N omitted since it is not used by Trsm. Stride: lq.Mat.Stride, Data: lq.Mat.Data, Uplo: blas.Lower, Diag: blas.NonUnit, } x.Mat.Rows = bm blas64.Trsm(blas.Left, blas.NoTrans, 1, lqT, x.Mat) x.Mat.Rows = n for i := range tau { lq.set(i, i, tau[i]) } f.applyQTo(x, true) return x }
// SolveTri finds the matrix x that solves op(A) * X = B where A is a triangular // matrix and op is specified by trans. func (m *Dense) SolveTri(a *Triangular, trans bool, b Matrix) { _, n := a.Dims() bm, bn := b.Dims() if n != bm { panic(ErrShape) } m.reuseAs(bm, bn) if b != m { m.Copy(b) } t := blas.NoTrans if trans { t = blas.Trans } switch a.mat.Uplo { case blas.Upper, blas.Lower: blas64.Trsm(blas.Left, t, 1, a.mat, m.mat) default: panic(badTriangle) } }
// 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) } }