gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/mat/lu_test.go (about) 1 // Copyright ©2013 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package mat 6 7 import ( 8 "testing" 9 10 "golang.org/x/exp/rand" 11 ) 12 13 func TestLU(t *testing.T) { 14 t.Parallel() 15 const tol = 1e-16 16 rnd := rand.New(rand.NewSource(1)) 17 for _, n := range []int{1, 2, 3, 4, 5, 10, 11, 50} { 18 // Construct a random matrix A. 19 a := NewDense(n, n, nil) 20 a.Apply(func(_, _ int, _ float64) float64 { return rnd.NormFloat64() }, a) 21 22 // Compute the LU factorization of A. 23 var lu LU 24 lu.Factorize(a) 25 26 // Compare A and LU using At. 27 if !EqualApprox(a, &lu, tol*float64(n)) { 28 var diff Dense 29 diff.Sub(a, &lu) 30 t.Errorf("n=%d: A and LU not equal\ndiff=%v", n, Formatted(&diff, Prefix(" "))) 31 } 32 33 // Recover A using RowPivots, LTo and UTo. 34 var l, u TriDense 35 lu.LTo(&l) 36 lu.UTo(&u) 37 var got Dense 38 got.Mul(&l, &u) 39 got.PermuteRows(lu.RowPivots(nil), false) 40 if !EqualApprox(&got, a, tol*float64(n)) { 41 var diff Dense 42 diff.Sub(&got, a) 43 t.Errorf("n=%d: A and P*L*U not equal\ndiff=%v", n, Formatted(&diff, Prefix(" "))) 44 } 45 } 46 } 47 48 func TestLURankOne(t *testing.T) { 49 t.Parallel() 50 const tol = 1e-14 51 rnd := rand.New(rand.NewSource(1)) 52 for _, n := range []int{1, 2, 3, 4, 5, 10, 50} { 53 // Construct a random matrix A. 54 a := NewDense(n, n, nil) 55 a.Apply(func(_, _ int, _ float64) float64 { return rnd.NormFloat64() }, a) 56 57 // Compute the LU factorization of A. 58 var lu LU 59 lu.Factorize(a) 60 61 // Apply a rank one update to A. Ensure the update magnitude is larger than 62 // the equal tolerance. 63 alpha := rnd.Float64() + 1 64 x := NewVecDense(n, nil) 65 y := NewVecDense(n, nil) 66 for i := 0; i < n; i++ { 67 x.setVec(i, rnd.Float64()+1) 68 y.setVec(i, rnd.Float64()+1) 69 } 70 a.RankOne(a, alpha, x, y) 71 72 // Apply the same rank one update to the LU factorization of A. 73 var luNew LU 74 luNew.RankOne(&lu, alpha, x, y) 75 lu.RankOne(&lu, alpha, x, y) 76 77 if !EqualApprox(&lu, a, tol*float64(n)) { 78 var diff Dense 79 diff.Sub(&lu, a) 80 t.Errorf("n=%d: rank one mismatch\ndiff=%v", n, Formatted(&diff, Prefix(" "))) 81 } 82 83 if !Equal(&lu, &luNew) { 84 var diff Dense 85 diff.Sub(&lu, &luNew) 86 t.Errorf("n=%d: rank one mismatch with new receiver\ndiff=%v", n, Formatted(&diff, Prefix(" "))) 87 } 88 } 89 } 90 91 func TestLUSolveTo(t *testing.T) { 92 t.Parallel() 93 rnd := rand.New(rand.NewSource(1)) 94 for _, test := range []struct { 95 n, bc int 96 }{ 97 {5, 5}, 98 {5, 10}, 99 {10, 5}, 100 } { 101 n := test.n 102 bc := test.bc 103 a := NewDense(n, n, nil) 104 for i := 0; i < n; i++ { 105 for j := 0; j < n; j++ { 106 a.Set(i, j, rnd.NormFloat64()) 107 } 108 } 109 b := NewDense(n, bc, nil) 110 for i := 0; i < n; i++ { 111 for j := 0; j < bc; j++ { 112 b.Set(i, j, rnd.NormFloat64()) 113 } 114 } 115 var lu LU 116 lu.Factorize(a) 117 var x Dense 118 if err := lu.SolveTo(&x, false, b); err != nil { 119 continue 120 } 121 var got Dense 122 got.Mul(a, &x) 123 if !EqualApprox(&got, b, 1e-12) { 124 t.Errorf("SolveTo mismatch for non-singular matrix. n = %v, bc = %v.\nWant: %v\nGot: %v", n, bc, b, got) 125 } 126 } 127 // TODO(btracey): Add testOneInput test when such a function exists. 128 } 129 130 func TestLUSolveToCond(t *testing.T) { 131 t.Parallel() 132 for _, test := range []*Dense{ 133 NewDense(2, 2, []float64{1, 0, 0, 1e-20}), 134 } { 135 m, _ := test.Dims() 136 var lu LU 137 lu.Factorize(test) 138 b := NewDense(m, 2, nil) 139 var x Dense 140 if err := lu.SolveTo(&x, false, b); err == nil { 141 t.Error("No error for near-singular matrix in matrix solve.") 142 } 143 144 bvec := NewVecDense(m, nil) 145 var xvec VecDense 146 if err := lu.SolveVecTo(&xvec, false, bvec); err == nil { 147 t.Error("No error for near-singular matrix in matrix solve.") 148 } 149 } 150 } 151 152 func TestLUSolveVecTo(t *testing.T) { 153 t.Parallel() 154 rnd := rand.New(rand.NewSource(1)) 155 for _, n := range []int{5, 10} { 156 a := NewDense(n, n, nil) 157 for i := 0; i < n; i++ { 158 for j := 0; j < n; j++ { 159 a.Set(i, j, rnd.NormFloat64()) 160 } 161 } 162 b := NewVecDense(n, nil) 163 for i := 0; i < n; i++ { 164 b.SetVec(i, rnd.NormFloat64()) 165 } 166 var lu LU 167 lu.Factorize(a) 168 var x VecDense 169 if err := lu.SolveVecTo(&x, false, b); err != nil { 170 continue 171 } 172 var got VecDense 173 got.MulVec(a, &x) 174 if !EqualApprox(&got, b, 1e-12) { 175 t.Errorf("SolveTo mismatch n = %v.\nWant: %v\nGot: %v", n, b, got) 176 } 177 } 178 // TODO(btracey): Add testOneInput test when such a function exists. 179 }