gonum.org/v1/gonum@v0.14.0/lapack/testlapack/dsterf.go (about) 1 // Copyright ©2016 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 testlapack 6 7 import ( 8 "fmt" 9 "math" 10 "sort" 11 "testing" 12 13 "golang.org/x/exp/rand" 14 15 "gonum.org/v1/gonum/blas" 16 "gonum.org/v1/gonum/blas/blas64" 17 "gonum.org/v1/gonum/floats" 18 "gonum.org/v1/gonum/lapack" 19 ) 20 21 type Dsterfer interface { 22 Dsteqrer 23 Dlansyer 24 Dsterf(n int, d, e []float64) (ok bool) 25 } 26 27 func DsterfTest(t *testing.T, impl Dsterfer) { 28 const tol = 1e-14 29 30 // Tests with precomputed eigenvalues. 31 for cas, test := range []struct { 32 d []float64 33 e []float64 34 n int 35 36 want []float64 37 }{ 38 { 39 d: []float64{1, 3, 4, 6}, 40 e: []float64{2, 4, 5}, 41 n: 4, 42 // Computed from original Fortran code. 43 want: []float64{11.046227528488854, 4.795922173417400, -2.546379458290125, 0.704229756383872}, 44 }, 45 } { 46 n := test.n 47 got := make([]float64, len(test.d)) 48 copy(got, test.d) 49 e := make([]float64, len(test.e)) 50 copy(e, test.e) 51 ok := impl.Dsterf(n, got, e) 52 if !ok { 53 t.Errorf("Case %d, n=%v: Dsterf failed", cas, n) 54 continue 55 } 56 want := make([]float64, len(test.want)) 57 copy(want, test.want) 58 sort.Float64s(want) 59 diff := floats.Distance(got, want, math.Inf(1)) 60 if diff > tol { 61 t.Errorf("Case %d, n=%v: unexpected result, |dGot-dWant|=%v", cas, n, diff) 62 } 63 } 64 65 rnd := rand.New(rand.NewSource(1)) 66 // Probabilistic tests. 67 for _, n := range []int{0, 1, 2, 3, 4, 5, 6, 10, 50} { 68 for typ := 0; typ <= 8; typ++ { 69 d := make([]float64, n) 70 var e []float64 71 if n > 1 { 72 e = make([]float64, n-1) 73 } 74 // Generate a tridiagonal matrix A. 75 switch typ { 76 case 0: 77 // The zero matrix. 78 case 1: 79 // The identity matrix. 80 for i := range d { 81 d[i] = 1 82 } 83 case 2: 84 // A diagonal matrix with evenly spaced entries 85 // 1, ..., eps and random signs. 86 for i := 0; i < n; i++ { 87 if i == 0 { 88 d[i] = 1 89 } else { 90 d[i] = 1 - (1-dlamchE)*float64(i)/float64(n-1) 91 } 92 if rnd.Float64() < 0.5 { 93 d[i] *= -1 94 } 95 } 96 case 3, 4, 5: 97 // A diagonal matrix with geometrically spaced entries 98 // 1, ..., eps and random signs. 99 for i := 0; i < n; i++ { 100 if i == 0 { 101 d[i] = 1 102 } else { 103 d[i] = math.Pow(dlamchE, float64(i)/float64(n-1)) 104 } 105 if rnd.Float64() < 0.5 { 106 d[i] *= -1 107 } 108 } 109 switch typ { 110 case 4: 111 // Multiply by SQRT(overflow threshold). 112 floats.Scale(math.Sqrt(1/dlamchS), d) 113 case 5: 114 // Multiply by SQRT(underflow threshold). 115 floats.Scale(math.Sqrt(dlamchS), d) 116 } 117 case 6: 118 // A diagonal matrix with "clustered" entries 1, eps, ..., eps 119 // and random signs. 120 for i := range d { 121 if i == 0 { 122 d[i] = 1 123 } else { 124 d[i] = dlamchE 125 } 126 } 127 for i := range d { 128 if rnd.Float64() < 0.5 { 129 d[i] *= -1 130 } 131 } 132 case 7: 133 // Diagonal matrix with random entries. 134 for i := range d { 135 d[i] = rnd.NormFloat64() 136 } 137 case 8: 138 // Random symmetric tridiagonal matrix. 139 for i := range d { 140 d[i] = rnd.NormFloat64() 141 } 142 for i := range e { 143 e[i] = rnd.NormFloat64() 144 } 145 } 146 eCopy := make([]float64, len(e)) 147 copy(eCopy, e) 148 149 name := fmt.Sprintf("n=%d,type=%d", n, typ) 150 151 // Compute the eigenvalues of A using Dsterf. 152 dGot := make([]float64, len(d)) 153 copy(dGot, d) 154 ok := impl.Dsterf(n, dGot, e) 155 if !ok { 156 t.Errorf("%v: Dsterf failed", name) 157 continue 158 } 159 160 if n == 0 { 161 continue 162 } 163 164 // Test that the eigenvalues are sorted. 165 if !sort.Float64sAreSorted(dGot) { 166 t.Errorf("%v: eigenvalues are not sorted", name) 167 continue 168 } 169 170 // Compute the expected eigenvalues of A using Dsteqr. 171 dWant := make([]float64, len(d)) 172 copy(dWant, d) 173 copy(e, eCopy) 174 z := nanGeneral(n, n, n) 175 ok = impl.Dsteqr(lapack.EVTridiag, n, dWant, e, z.Data, z.Stride, make([]float64, 2*n)) 176 if !ok { 177 t.Errorf("%v: computing reference solution using Dsteqr failed", name) 178 continue 179 } 180 181 if resid := residualOrthogonal(z, false); resid > tol*float64(n) { 182 t.Errorf("%v: Z is not orthogonal; resid=%v, want<=%v", name, resid, tol*float64(n)) 183 } 184 185 // Check whether eigenvalues from Dsteqr and Dsterf (which use 186 // different algorithms) are equal. 187 var diff, dMax float64 188 for i, di := range dGot { 189 diffAbs := math.Abs(di - dWant[i]) 190 diff = math.Max(diff, diffAbs) 191 dAbs := math.Max(math.Abs(di), math.Abs(dWant[i])) 192 dMax = math.Max(dMax, dAbs) 193 } 194 dMax = math.Max(dlamchS, dMax) 195 if diff > tol*dMax { 196 t.Errorf("%v: unexpected result; |dGot-dWant|=%v", name, diff) 197 } 198 199 // Construct A as a symmetric dense matrix and compute its 1-norm. 200 copy(e, eCopy) 201 lda := n 202 a := make([]float64, n*lda) 203 var anorm, tmp float64 204 for i := 0; i < n-1; i++ { 205 a[i*lda+i] = d[i] 206 a[i*lda+i+1] = e[i] 207 tmp2 := math.Abs(e[i]) 208 anorm = math.Max(anorm, math.Abs(d[i])+tmp+tmp2) 209 tmp = tmp2 210 } 211 a[(n-1)*lda+n-1] = d[n-1] 212 anorm = math.Max(anorm, math.Abs(d[n-1])+tmp) 213 214 // Compute A - Z D Zᵀ. The result should be the zero matrix. 215 bi := blas64.Implementation() 216 for i := 0; i < n; i++ { 217 bi.Dsyr(blas.Upper, n, -dGot[i], z.Data[i:], z.Stride, a, lda) 218 } 219 220 // Compute |A - Z D Zᵀ|. 221 wnorm := impl.Dlansy(lapack.MaxColumnSum, blas.Upper, n, a, lda, make([]float64, n)) 222 223 // Compute diff := |A - Z D Zᵀ| / (|A| N). 224 if anorm > wnorm { 225 diff = wnorm / anorm / float64(n) 226 } else { 227 if anorm < 1 { 228 diff = math.Min(wnorm, float64(n)*anorm) / anorm / float64(n) 229 } else { 230 diff = math.Min(wnorm/anorm, float64(n)) / float64(n) 231 } 232 } 233 234 // Check whether diff is small. 235 if diff > tol { 236 t.Errorf("%v: unexpected result; |A - Z D Zᵀ|/(|A| n)=%v", name, diff) 237 } 238 } 239 } 240 }