gonum.org/v1/gonum@v0.14.0/mat/qr_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 "math" 9 "testing" 10 11 "golang.org/x/exp/rand" 12 13 "gonum.org/v1/gonum/blas/blas64" 14 ) 15 16 func TestQR(t *testing.T) { 17 t.Parallel() 18 rnd := rand.New(rand.NewSource(1)) 19 for _, test := range []struct { 20 m, n int 21 }{ 22 {5, 5}, 23 {10, 5}, 24 } { 25 m := test.m 26 n := test.n 27 a := NewDense(m, n, nil) 28 for i := 0; i < m; i++ { 29 for j := 0; j < n; j++ { 30 a.Set(i, j, rnd.NormFloat64()) 31 } 32 } 33 var want Dense 34 want.CloneFrom(a) 35 36 var qr QR 37 qr.Factorize(a) 38 var q, r Dense 39 qr.QTo(&q) 40 41 if !isOrthonormal(&q, 1e-10) { 42 t.Errorf("Q is not orthonormal: m = %v, n = %v", m, n) 43 } 44 45 qr.RTo(&r) 46 47 var got Dense 48 got.Mul(&q, &r) 49 if !EqualApprox(&got, &want, 1e-12) { 50 t.Errorf("QR does not equal original matrix. \nWant: %v\nGot: %v", want, got) 51 } 52 } 53 } 54 55 func isOrthonormal(q *Dense, tol float64) bool { 56 m, n := q.Dims() 57 if m != n { 58 return false 59 } 60 for i := 0; i < m; i++ { 61 for j := i; j < m; j++ { 62 dot := blas64.Dot(blas64.Vector{N: m, Inc: 1, Data: q.mat.Data[i*q.mat.Stride:]}, 63 blas64.Vector{N: m, Inc: 1, Data: q.mat.Data[j*q.mat.Stride:]}) 64 // Dot product should be 1 if i == j and 0 otherwise. 65 if i == j && math.Abs(dot-1) > tol { 66 return false 67 } 68 if i != j && math.Abs(dot) > tol { 69 return false 70 } 71 } 72 } 73 return true 74 } 75 76 func TestQRSolveTo(t *testing.T) { 77 t.Parallel() 78 rnd := rand.New(rand.NewSource(1)) 79 for _, trans := range []bool{false, true} { 80 for _, test := range []struct { 81 m, n, bc int 82 }{ 83 {5, 5, 1}, 84 {10, 5, 1}, 85 {5, 5, 3}, 86 {10, 5, 3}, 87 } { 88 m := test.m 89 n := test.n 90 bc := test.bc 91 a := NewDense(m, n, nil) 92 for i := 0; i < m; i++ { 93 for j := 0; j < n; j++ { 94 a.Set(i, j, rnd.Float64()) 95 } 96 } 97 br := m 98 if trans { 99 br = n 100 } 101 b := NewDense(br, bc, nil) 102 for i := 0; i < br; i++ { 103 for j := 0; j < bc; j++ { 104 b.Set(i, j, rnd.Float64()) 105 } 106 } 107 var x Dense 108 var qr QR 109 qr.Factorize(a) 110 err := qr.SolveTo(&x, trans, b) 111 if err != nil { 112 t.Errorf("unexpected error from QR solve: %v", err) 113 } 114 115 // Test that the normal equations hold. 116 // Aᵀ * A * x = Aᵀ * b if !trans 117 // A * Aᵀ * x = A * b if trans 118 var lhs Dense 119 var rhs Dense 120 if trans { 121 var tmp Dense 122 tmp.Mul(a, a.T()) 123 lhs.Mul(&tmp, &x) 124 rhs.Mul(a, b) 125 } else { 126 var tmp Dense 127 tmp.Mul(a.T(), a) 128 lhs.Mul(&tmp, &x) 129 rhs.Mul(a.T(), b) 130 } 131 if !EqualApprox(&lhs, &rhs, 1e-10) { 132 t.Errorf("Normal equations do not hold.\nLHS: %v\n, RHS: %v\n", lhs, rhs) 133 } 134 } 135 } 136 // TODO(btracey): Add in testOneInput when it exists. 137 } 138 139 func TestQRSolveVecTo(t *testing.T) { 140 t.Parallel() 141 rnd := rand.New(rand.NewSource(1)) 142 for _, trans := range []bool{false, true} { 143 for _, test := range []struct { 144 m, n int 145 }{ 146 {5, 5}, 147 {10, 5}, 148 } { 149 m := test.m 150 n := test.n 151 a := NewDense(m, n, nil) 152 for i := 0; i < m; i++ { 153 for j := 0; j < n; j++ { 154 a.Set(i, j, rnd.Float64()) 155 } 156 } 157 br := m 158 if trans { 159 br = n 160 } 161 b := NewVecDense(br, nil) 162 for i := 0; i < br; i++ { 163 b.SetVec(i, rnd.Float64()) 164 } 165 var x VecDense 166 var qr QR 167 qr.Factorize(a) 168 err := qr.SolveVecTo(&x, trans, b) 169 if err != nil { 170 t.Errorf("unexpected error from QR solve: %v", err) 171 } 172 173 // Test that the normal equations hold. 174 // Aᵀ * A * x = Aᵀ * b if !trans 175 // A * Aᵀ * x = A * b if trans 176 var lhs Dense 177 var rhs Dense 178 if trans { 179 var tmp Dense 180 tmp.Mul(a, a.T()) 181 lhs.Mul(&tmp, &x) 182 rhs.Mul(a, b) 183 } else { 184 var tmp Dense 185 tmp.Mul(a.T(), a) 186 lhs.Mul(&tmp, &x) 187 rhs.Mul(a.T(), b) 188 } 189 if !EqualApprox(&lhs, &rhs, 1e-10) { 190 t.Errorf("Normal equations do not hold.\nLHS: %v\n, RHS: %v\n", lhs, rhs) 191 } 192 } 193 } 194 // TODO(btracey): Add in testOneInput when it exists. 195 } 196 197 func TestQRSolveCondTo(t *testing.T) { 198 t.Parallel() 199 for _, test := range []*Dense{ 200 NewDense(2, 2, []float64{1, 0, 0, 1e-20}), 201 NewDense(3, 2, []float64{1, 0, 0, 1e-20, 0, 0}), 202 } { 203 m, _ := test.Dims() 204 var qr QR 205 qr.Factorize(test) 206 b := NewDense(m, 2, nil) 207 var x Dense 208 if err := qr.SolveTo(&x, false, b); err == nil { 209 t.Error("No error for near-singular matrix in matrix solve.") 210 } 211 212 bvec := NewVecDense(m, nil) 213 var xvec VecDense 214 if err := qr.SolveVecTo(&xvec, false, bvec); err == nil { 215 t.Error("No error for near-singular matrix in matrix solve.") 216 } 217 } 218 }