github.com/gonum/matrix@v0.0.0-20181209220409-c518dec07be9/mat64/lq_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 mat64 6 7 import ( 8 "math/rand" 9 "testing" 10 ) 11 12 func TestLQ(t *testing.T) { 13 for _, test := range []struct { 14 m, n int 15 }{ 16 {5, 5}, 17 {5, 10}, 18 } { 19 m := test.m 20 n := test.n 21 a := NewDense(m, n, nil) 22 for i := 0; i < m; i++ { 23 for j := 0; j < n; j++ { 24 a.Set(i, j, rand.NormFloat64()) 25 } 26 } 27 var want Dense 28 want.Clone(a) 29 30 lq := &LQ{} 31 lq.Factorize(a) 32 var l, q Dense 33 q.QFromLQ(lq) 34 35 if !isOrthonormal(&q, 1e-10) { 36 t.Errorf("Q is not orthonormal: m = %v, n = %v", m, n) 37 } 38 39 l.LFromLQ(lq) 40 41 var got Dense 42 got.Mul(&l, &q) 43 if !EqualApprox(&got, &want, 1e-12) { 44 t.Errorf("LQ does not equal original matrix. \nWant: %v\nGot: %v", want, got) 45 } 46 } 47 } 48 49 func TestSolveLQ(t *testing.T) { 50 for _, trans := range []bool{false, true} { 51 for _, test := range []struct { 52 m, n, bc int 53 }{ 54 {5, 5, 1}, 55 {5, 10, 1}, 56 {5, 5, 3}, 57 {5, 10, 3}, 58 } { 59 m := test.m 60 n := test.n 61 bc := test.bc 62 a := NewDense(m, n, nil) 63 for i := 0; i < m; i++ { 64 for j := 0; j < n; j++ { 65 a.Set(i, j, rand.Float64()) 66 } 67 } 68 br := m 69 if trans { 70 br = n 71 } 72 b := NewDense(br, bc, nil) 73 for i := 0; i < br; i++ { 74 for j := 0; j < bc; j++ { 75 b.Set(i, j, rand.Float64()) 76 } 77 } 78 var x Dense 79 lq := &LQ{} 80 lq.Factorize(a) 81 x.SolveLQ(lq, trans, b) 82 83 // Test that the normal equations hold. 84 // A^T * A * x = A^T * b if !trans 85 // A * A^T * x = A * b if trans 86 var lhs Dense 87 var rhs Dense 88 if trans { 89 var tmp Dense 90 tmp.Mul(a, a.T()) 91 lhs.Mul(&tmp, &x) 92 rhs.Mul(a, b) 93 } else { 94 var tmp Dense 95 tmp.Mul(a.T(), a) 96 lhs.Mul(&tmp, &x) 97 rhs.Mul(a.T(), b) 98 } 99 if !EqualApprox(&lhs, &rhs, 1e-10) { 100 t.Errorf("Normal equations do not hold.\nLHS: %v\n, RHS: %v\n", lhs, rhs) 101 } 102 } 103 } 104 // TODO(btracey): Add in testOneInput when it exists. 105 } 106 107 func TestSolveLQVec(t *testing.T) { 108 for _, trans := range []bool{false, true} { 109 for _, test := range []struct { 110 m, n int 111 }{ 112 {5, 5}, 113 {5, 10}, 114 } { 115 m := test.m 116 n := test.n 117 a := NewDense(m, n, nil) 118 for i := 0; i < m; i++ { 119 for j := 0; j < n; j++ { 120 a.Set(i, j, rand.Float64()) 121 } 122 } 123 br := m 124 if trans { 125 br = n 126 } 127 b := NewVector(br, nil) 128 for i := 0; i < br; i++ { 129 b.SetVec(i, rand.Float64()) 130 } 131 var x Vector 132 lq := &LQ{} 133 lq.Factorize(a) 134 x.SolveLQVec(lq, trans, b) 135 136 // Test that the normal equations hold. 137 // A^T * A * x = A^T * b if !trans 138 // A * A^T * x = A * b if trans 139 var lhs Dense 140 var rhs Dense 141 if trans { 142 var tmp Dense 143 tmp.Mul(a, a.T()) 144 lhs.Mul(&tmp, &x) 145 rhs.Mul(a, b) 146 } else { 147 var tmp Dense 148 tmp.Mul(a.T(), a) 149 lhs.Mul(&tmp, &x) 150 rhs.Mul(a.T(), b) 151 } 152 if !EqualApprox(&lhs, &rhs, 1e-10) { 153 t.Errorf("Normal equations do not hold.\nLHS: %v\n, RHS: %v\n", lhs, rhs) 154 } 155 } 156 } 157 // TODO(btracey): Add in testOneInput when it exists. 158 } 159 160 func TestSolveLQCond(t *testing.T) { 161 for _, test := range []*Dense{ 162 NewDense(2, 2, []float64{1, 0, 0, 1e-20}), 163 NewDense(2, 3, []float64{1, 0, 0, 0, 1e-20, 0}), 164 } { 165 m, _ := test.Dims() 166 var lq LQ 167 lq.Factorize(test) 168 b := NewDense(m, 2, nil) 169 var x Dense 170 if err := x.SolveLQ(&lq, false, b); err == nil { 171 t.Error("No error for near-singular matrix in matrix solve.") 172 } 173 174 bvec := NewVector(m, nil) 175 var xvec Vector 176 if err := xvec.SolveLQVec(&lq, false, bvec); err == nil { 177 t.Error("No error for near-singular matrix in matrix solve.") 178 } 179 } 180 }