blob: 2ae13ea3890d9b5cdee1de68dcf3988a642a3132 [file] [log] [blame]
// 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.
package mat
import (
"testing"
"golang.org/x/exp/rand"
)
func TestLU(t *testing.T) {
t.Parallel()
const tol = 1e-16
rnd := rand.New(rand.NewSource(1))
for _, n := range []int{1, 2, 3, 4, 5, 10, 11, 50} {
// Construct a random matrix A.
a := NewDense(n, n, nil)
a.Apply(func(_, _ int, _ float64) float64 { return rnd.NormFloat64() }, a)
// Compute the LU factorization of A.
var lu LU
lu.Factorize(a)
// Compare A and LU using At.
if !EqualApprox(a, &lu, tol*float64(n)) {
var diff Dense
diff.Sub(a, &lu)
t.Errorf("n=%d: A and LU not equal\ndiff=%v", n, Formatted(&diff, Prefix(" ")))
}
// Recover A using RowPivots, LTo and UTo.
var l, u TriDense
lu.LTo(&l)
lu.UTo(&u)
var got Dense
got.Mul(&l, &u)
got.PermuteRows(lu.RowPivots(nil), false)
if !EqualApprox(&got, a, tol*float64(n)) {
var diff Dense
diff.Sub(&got, a)
t.Errorf("n=%d: A and P*L*U not equal\ndiff=%v", n, Formatted(&diff, Prefix(" ")))
}
}
}
func TestLURankOne(t *testing.T) {
t.Parallel()
const tol = 1e-14
rnd := rand.New(rand.NewSource(1))
for _, n := range []int{1, 2, 3, 4, 5, 10, 50} {
// Construct a random matrix A.
a := NewDense(n, n, nil)
a.Apply(func(_, _ int, _ float64) float64 { return rnd.NormFloat64() }, a)
// Compute the LU factorization of A.
var lu LU
lu.Factorize(a)
// Apply a rank one update to A. Ensure the update magnitude is larger than
// the equal tolerance.
alpha := rnd.Float64() + 1
x := NewVecDense(n, nil)
y := NewVecDense(n, nil)
for i := 0; i < n; i++ {
x.setVec(i, rnd.Float64()+1)
y.setVec(i, rnd.Float64()+1)
}
a.RankOne(a, alpha, x, y)
// Apply the same rank one update to the LU factorization of A.
var luNew LU
luNew.RankOne(&lu, alpha, x, y)
lu.RankOne(&lu, alpha, x, y)
if !EqualApprox(&lu, a, tol*float64(n)) {
var diff Dense
diff.Sub(&lu, a)
t.Errorf("n=%d: rank one mismatch\ndiff=%v", n, Formatted(&diff, Prefix(" ")))
}
if !Equal(&lu, &luNew) {
var diff Dense
diff.Sub(&lu, &luNew)
t.Errorf("n=%d: rank one mismatch with new receiver\ndiff=%v", n, Formatted(&diff, Prefix(" ")))
}
}
}
func TestLUSolveTo(t *testing.T) {
t.Parallel()
rnd := rand.New(rand.NewSource(1))
for _, test := range []struct {
n, bc int
}{
{5, 5},
{5, 10},
{10, 5},
} {
n := test.n
bc := test.bc
a := NewDense(n, n, nil)
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
a.Set(i, j, rnd.NormFloat64())
}
}
b := NewDense(n, bc, nil)
for i := 0; i < n; i++ {
for j := 0; j < bc; j++ {
b.Set(i, j, rnd.NormFloat64())
}
}
var lu LU
lu.Factorize(a)
var x Dense
if err := lu.SolveTo(&x, false, b); err != nil {
continue
}
var got Dense
got.Mul(a, &x)
if !EqualApprox(&got, b, 1e-12) {
t.Errorf("SolveTo mismatch for non-singular matrix. n = %v, bc = %v.\nWant: %v\nGot: %v", n, bc, b, got)
}
}
// TODO(btracey): Add testOneInput test when such a function exists.
}
func TestLUSolveToCond(t *testing.T) {
t.Parallel()
for _, test := range []*Dense{
NewDense(2, 2, []float64{1, 0, 0, 1e-20}),
} {
m, _ := test.Dims()
var lu LU
lu.Factorize(test)
b := NewDense(m, 2, nil)
var x Dense
if err := lu.SolveTo(&x, false, b); err == nil {
t.Error("No error for near-singular matrix in matrix solve.")
}
bvec := NewVecDense(m, nil)
var xvec VecDense
if err := lu.SolveVecTo(&xvec, false, bvec); err == nil {
t.Error("No error for near-singular matrix in matrix solve.")
}
}
}
func TestLUSolveVecTo(t *testing.T) {
t.Parallel()
rnd := rand.New(rand.NewSource(1))
for _, n := range []int{5, 10} {
a := NewDense(n, n, nil)
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
a.Set(i, j, rnd.NormFloat64())
}
}
b := NewVecDense(n, nil)
for i := 0; i < n; i++ {
b.SetVec(i, rnd.NormFloat64())
}
var lu LU
lu.Factorize(a)
var x VecDense
if err := lu.SolveVecTo(&x, false, b); err != nil {
continue
}
var got VecDense
got.MulVec(a, &x)
if !EqualApprox(&got, b, 1e-12) {
t.Errorf("SolveTo mismatch n = %v.\nWant: %v\nGot: %v", n, b, got)
}
}
// TODO(btracey): Add testOneInput test when such a function exists.
}