mirror of
https://github.com/gonum/gonum.git
synced 2025-10-10 01:20:14 +08:00

This is an API breaking change. NewDense now panics if len(mat) != r*c, unless mat == nil. When mat is nil a new, correctly sized slice is allocated.
128 lines
2.8 KiB
Go
128 lines
2.8 KiB
Go
// Copyright ©2013 The gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
// Based on the CholeskyDecomposition class from Jama 1.0.3.
|
|
|
|
package mat64
|
|
|
|
import (
|
|
"math"
|
|
)
|
|
|
|
type CholeskyFactor struct {
|
|
L *Dense
|
|
SPD bool
|
|
}
|
|
|
|
// CholeskyL returns the left Cholesky decomposition of the matrix a and whether
|
|
// the matrix is symmetric or positive definite, the returned matrix l is a lower
|
|
// triangular matrix such that a = l.l'.
|
|
func Cholesky(a *Dense) CholeskyFactor {
|
|
// Initialize.
|
|
m, n := a.Dims()
|
|
spd := m == n
|
|
l := NewDense(n, n, nil)
|
|
|
|
// Main loop.
|
|
lRowj := make([]float64, n)
|
|
lRowk := make([]float64, n)
|
|
for j := 0; j < n; j++ {
|
|
var d float64
|
|
l.Row(lRowj, j)
|
|
for k := 0; k < j; k++ {
|
|
var s float64
|
|
l.Row(lRowk, k)
|
|
for i := 0; i < k; i++ {
|
|
s += lRowk[i] * lRowj[i]
|
|
}
|
|
s = (a.At(j, k) - s) / l.At(k, k)
|
|
lRowj[k] = s
|
|
d += s * s
|
|
spd = spd && a.At(k, j) == a.At(j, k)
|
|
}
|
|
l.SetRow(j, lRowj)
|
|
d = a.At(j, j) - d
|
|
spd = spd && d > 0
|
|
l.Set(j, j, math.Sqrt(math.Max(d, 0)))
|
|
for k := j + 1; k < n; k++ {
|
|
l.Set(j, k, 0)
|
|
}
|
|
}
|
|
|
|
return CholeskyFactor{L: l, SPD: spd}
|
|
}
|
|
|
|
// CholeskyR returns the right Cholesky decomposition of the matrix a and whether
|
|
// the matrix is symmetric or positive definite, the returned matrix r is an upper
|
|
// triangular matrix such that a = r'.r.
|
|
func CholeskyR(a *Dense) (r *Dense, spd bool) {
|
|
// Initialize.
|
|
m, n := a.Dims()
|
|
spd = m == n
|
|
r = NewDense(n, n, nil)
|
|
|
|
// Main loop.
|
|
for j := 0; j < n; j++ {
|
|
var d float64
|
|
for k := 0; k < j; k++ {
|
|
s := a.At(k, j)
|
|
for i := 0; i < k; i++ {
|
|
s -= r.At(i, k) * r.At(i, j)
|
|
}
|
|
s /= r.At(k, k)
|
|
r.Set(k, j, s)
|
|
d += s * s
|
|
spd = spd && a.At(k, j) == a.At(j, k)
|
|
}
|
|
d = a.At(j, j) - d
|
|
spd = spd && d > 0
|
|
r.Set(j, j, math.Sqrt(math.Max(d, 0)))
|
|
for k := j + 1; k < n; k++ {
|
|
r.Set(k, j, 0)
|
|
}
|
|
}
|
|
|
|
return r, spd
|
|
}
|
|
|
|
// CholeskySolve returns a matrix x that solves a.x = b where a = l.l'. The matrix b must
|
|
// have the same number of rows as a, and a must be symmetric and positive definite. The
|
|
// matrix b is overwritten by the operation.
|
|
func (f CholeskyFactor) Solve(b *Dense) (x *Dense) {
|
|
if !f.SPD {
|
|
panic("mat64: matrix not symmetric positive definite")
|
|
}
|
|
l := f.L
|
|
|
|
_, n := l.Dims()
|
|
_, bn := b.Dims()
|
|
if n != bn {
|
|
panic(ErrShape)
|
|
}
|
|
|
|
nx := bn
|
|
x = b
|
|
|
|
// Solve L*Y = B;
|
|
for k := 0; k < n; k++ {
|
|
for j := 0; j < nx; j++ {
|
|
for i := 0; i < k; i++ {
|
|
x.Set(k, j, x.At(k, j)-x.At(i, j)*l.At(k, i))
|
|
}
|
|
x.Set(k, j, x.At(k, j)/l.At(k, k))
|
|
}
|
|
}
|
|
|
|
// Solve L'*X = Y;
|
|
for k := n - 1; k >= 0; k-- {
|
|
for j := 0; j < nx; j++ {
|
|
for i := k + 1; i < n; i++ {
|
|
x.Set(k, j, x.At(k, j)-x.At(i, j)*l.At(i, k))
|
|
}
|
|
x.Set(k, j, x.At(k, j)/l.At(k, k))
|
|
}
|
|
}
|
|
|
|
return x
|
|
}
|