mat: use lapack64.Potri in Cholesky.InverseTo

Also,
 - make tricky shadowing conversion from a SymDense to a TriDense more
 explicit in Cholesky.ToSym,
 - add a benchmark for InverseTo:

name                        old time/op  new time/op  delta
CholeskyInverseTo/n=10-4    10.8µs ± 2%   3.2µs ± 1%  -70.16%  (p=0.008 n=5+5)
CholeskyInverseTo/n=100-4   1.07ms ± 2%  0.51ms ± 2%  -52.06%  (p=0.008 n=5+5)
CholeskyInverseTo/n=1000-4   713ms ± 1%   315ms ± 1%  -55.83%  (p=0.008 n=5+5)
This commit is contained in:
Vladimir Chalupecky
2019-01-04 14:33:43 +01:00
committed by Vladimír Chalupecký
parent ce5163176b
commit 9d4132a30c
2 changed files with 64 additions and 15 deletions

View File

@@ -291,10 +291,19 @@ func (c *Cholesky) ToSym(dst *SymDense) *SymDense {
} else { } else {
dst.reuseAs(n) dst.reuseAs(n)
} }
// Copy c.chol into a TriDense U with dst's backing slice. // Create a TriDense representing the Cholesky factor U with dst's
// Operations on u are reflected in dst. // backing slice.
u := *c.chol // Operations on u are reflected in s.
u.mat.Data = dst.mat.Data u := &TriDense{
mat: blas64.Triangular{
Uplo: blas.Upper,
Diag: blas.NonUnit,
N: n,
Data: dst.mat.Data,
Stride: dst.mat.Stride,
},
cap: n,
}
u.Copy(c.chol) u.Copy(c.chol)
// Compute the product U^T*U using the algorithm from LAPACK/TESTING/LIN/dpot01.f // Compute the product U^T*U using the algorithm from LAPACK/TESTING/LIN/dpot01.f
a := u.mat.Data a := u.mat.Data
@@ -318,18 +327,30 @@ func (c *Cholesky) InverseTo(s *SymDense) error {
if !c.valid() { if !c.valid() {
panic(badCholesky) panic(badCholesky)
} }
// TODO(btracey): Replace this code with a direct call to Dpotri when it
// is available.
s.reuseAs(c.chol.mat.N) s.reuseAs(c.chol.mat.N)
// If: // Create a TriDense representing the Cholesky factor U with the backing
// chol(A) = U^T * U // slice from s.
// Then: // Operations on u are reflected in s.
// chol(A^-1) = S * S^T u := &TriDense{
// where S = U^-1 mat: blas64.Triangular{
var t TriDense Uplo: blas.Upper,
err := t.InverseTri(c.chol) Diag: blas.NonUnit,
s.SymOuterK(1, &t) N: s.mat.N,
return err Data: s.mat.Data,
Stride: s.mat.Stride,
},
cap: s.mat.N,
}
u.Copy(c.chol)
_, ok := lapack64.Potri(u.mat)
if !ok {
return Condition(math.Inf(1))
}
if c.cond > ConditionTolerance {
return Condition(c.cond)
}
return nil
} }
// Scale multiplies the original matrix A by a positive constant using // Scale multiplies the original matrix A by a positive constant using

View File

@@ -616,3 +616,31 @@ func BenchmarkCholeskyToSym(b *testing.B) {
}) })
} }
} }
func BenchmarkCholeskyInverseTo(b *testing.B) {
for _, n := range []int{10, 100, 1000} {
b.Run("n="+strconv.Itoa(n), func(b *testing.B) {
rnd := rand.New(rand.NewSource(1))
data := make([]float64, n*n)
for i := range data {
data[i] = rnd.NormFloat64()
}
var a SymDense
a.SymOuterK(1, NewDense(n, n, data))
var chol Cholesky
ok := chol.Factorize(&a)
if !ok {
panic("not positive definite")
}
dst := NewSymDense(n, nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
chol.InverseTo(dst)
}
})
}
}