gonum.org/v1/gonum@v0.14.0/mat/eigen_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 "sort" 9 "testing" 10 11 "golang.org/x/exp/rand" 12 13 "gonum.org/v1/gonum/floats" 14 ) 15 16 func TestEigen(t *testing.T) { 17 t.Parallel() 18 for i, test := range []struct { 19 a *Dense 20 21 values []complex128 22 left *CDense 23 right *CDense 24 }{ 25 { 26 a: NewDense(3, 3, []float64{ 27 1, 0, 0, 28 0, 1, 0, 29 0, 0, 1, 30 }), 31 values: []complex128{1, 1, 1}, 32 left: NewCDense(3, 3, []complex128{ 33 1, 0, 0, 34 0, 1, 0, 35 0, 0, 1, 36 }), 37 right: NewCDense(3, 3, []complex128{ 38 1, 0, 0, 39 0, 1, 0, 40 0, 0, 1, 41 }), 42 }, 43 { 44 // Values compared with numpy. 45 a: NewDense(4, 4, []float64{ 46 0.9025, 0.025, 0.475, 0.0475, 47 0.0475, 0.475, 0.475, 0.0025, 48 0.0475, 0.025, 0.025, 0.9025, 49 0.0025, 0.475, 0.025, 0.0475, 50 }), 51 values: []complex128{1, 0.7300317046114154, -0.1400158523057075 + 0.452854925738716i, -0.1400158523057075 - 0.452854925738716i}, 52 left: NewCDense(4, 4, []complex128{ 53 0.5, -0.3135167160788313, -0.02058121780136903 + 0.004580939300127051i, -0.02058121780136903 - 0.004580939300127051i, 54 0.5, 0.7842199280224781, 0.37551026954193356 - 0.2924634904103879i, 0.37551026954193356 + 0.2924634904103879i, 55 0.5, 0.33202200780783525, 0.16052616322784943 + 0.3881393645202527i, 0.16052616322784943 - 0.3881393645202527i, 56 0.5, 0.42008065840123954, -0.7723935249234155, -0.7723935249234155, 57 }), 58 right: NewCDense(4, 4, []complex128{ 59 0.9476399565969628, -0.8637347682162745, -0.2688989440320280 - 0.1282234938321029i, -0.2688989440320280 + 0.1282234938321029i, 60 0.2394935907064427, 0.3457075153704627, -0.3621360383713332 - 0.2583198964498771i, -0.3621360383713332 + 0.2583198964498771i, 61 0.1692743801716332, 0.2706851011641580, 0.7426369401030960, 0.7426369401030960, 62 0.1263626404003607, 0.2473421516816520, -0.1116019576997347 + 0.3865433902819795i, -0.1116019576997347 - 0.3865433902819795i, 63 }), 64 }, 65 } { 66 var e1, e2, e3, e4 Eigen 67 ok := e1.Factorize(test.a, EigenBoth) 68 if !ok { 69 panic("bad factorization") 70 } 71 e2.Factorize(test.a, EigenRight) 72 e3.Factorize(test.a, EigenLeft) 73 e4.Factorize(test.a, EigenNone) 74 75 v1 := e1.Values(nil) 76 if !cmplxEqualTol(v1, test.values, 1e-14) { 77 t.Errorf("eigenvalue mismatch. Case %v", i) 78 } 79 var left CDense 80 e1.LeftVectorsTo(&left) 81 if !CEqualApprox(&left, test.left, 1e-14) { 82 t.Errorf("left eigenvector mismatch. Case %v", i) 83 } 84 var right CDense 85 e1.VectorsTo(&right) 86 if !CEqualApprox(&right, test.right, 1e-14) { 87 t.Errorf("right eigenvector mismatch. Case %v", i) 88 } 89 90 // Check that the eigenvectors and values are the same in all combinations. 91 if !cmplxEqual(v1, e2.Values(nil)) { 92 t.Errorf("eigenvector mismatch. Case %v", i) 93 } 94 if !cmplxEqual(v1, e3.Values(nil)) { 95 t.Errorf("eigenvector mismatch. Case %v", i) 96 } 97 if !cmplxEqual(v1, e4.Values(nil)) { 98 t.Errorf("eigenvector mismatch. Case %v", i) 99 } 100 var right2 CDense 101 e2.VectorsTo(&right2) 102 if !CEqual(&right, &right2) { 103 t.Errorf("right eigenvector mismatch. Case %v", i) 104 } 105 var left3 CDense 106 e3.LeftVectorsTo(&left3) 107 if !CEqual(&left, &left3) { 108 t.Errorf("left eigenvector mismatch. Case %v", i) 109 } 110 111 // TODO(btracey): Also add in a test for correctness when #308 is 112 // resolved and we have a CMat.Mul(). 113 } 114 } 115 116 func cmplxEqual(v1, v2 []complex128) bool { 117 for i, v := range v1 { 118 if v != v2[i] { 119 return false 120 } 121 } 122 return true 123 } 124 125 func cmplxEqualTol(v1, v2 []complex128, tol float64) bool { 126 for i, v := range v1 { 127 if !cEqualWithinAbsOrRel(v, v2[i], tol, tol) { 128 return false 129 } 130 } 131 return true 132 } 133 134 func TestSymEigen(t *testing.T) { 135 t.Parallel() 136 // Hand coded tests with results from lapack. 137 for _, test := range []struct { 138 mat *SymDense 139 140 values []float64 141 vectors *Dense 142 }{ 143 { 144 mat: NewSymDense(3, []float64{8, 2, 4, 2, 6, 10, 4, 10, 5}), 145 values: []float64{-4.707679201365891, 6.294580208480216, 17.413098992885672}, 146 vectors: NewDense(3, 3, []float64{ 147 -0.127343483135656, -0.902414161226903, -0.411621572466779, 148 -0.664177720955769, 0.385801900032553, -0.640331827193739, 149 0.736648893495999, 0.191847792659746, -0.648492738712395, 150 }), 151 }, 152 } { 153 var es EigenSym 154 ok := es.Factorize(test.mat, true) 155 if !ok { 156 t.Errorf("bad factorization") 157 } 158 if !floats.EqualApprox(test.values, es.values, 1e-14) { 159 t.Errorf("Eigenvalue mismatch") 160 } 161 if !EqualApprox(test.vectors, es.vectors, 1e-14) { 162 t.Errorf("Eigenvector mismatch") 163 } 164 165 var es2 EigenSym 166 es2.Factorize(test.mat, false) 167 if !floats.EqualApprox(es2.values, es.values, 1e-14) { 168 t.Errorf("Eigenvalue mismatch when no vectors computed") 169 } 170 } 171 172 // Randomized tests 173 rnd := rand.New(rand.NewSource(1)) 174 for _, n := range []int{3, 5, 10, 70} { 175 for cas := 0; cas < 10; cas++ { 176 a := make([]float64, n*n) 177 for i := range a { 178 a[i] = rnd.NormFloat64() 179 } 180 s := NewSymDense(n, a) 181 var es EigenSym 182 ok := es.Factorize(s, true) 183 if !ok { 184 t.Errorf("Bad test") 185 } 186 187 // Check that the eigenvectors are orthonormal. 188 if !isOrthonormal(es.vectors, 1e-8) { 189 t.Errorf("Eigenvectors not orthonormal") 190 } 191 192 // Check that the eigenvalues are actually eigenvalues. 193 for i := 0; i < n; i++ { 194 v := NewVecDense(n, Col(nil, i, es.vectors)) 195 var m VecDense 196 m.MulVec(s, v) 197 198 var scal VecDense 199 scal.ScaleVec(es.values[i], v) 200 201 if !EqualApprox(&m, &scal, 1e-8) { 202 t.Errorf("Eigenvalue does not match") 203 } 204 } 205 206 // Check that the eigenvalues are in ascending order. 207 if !sort.Float64sAreSorted(es.values) { 208 t.Errorf("Eigenvalues not ascending") 209 } 210 } 211 } 212 }