// Copyright ©2016 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package testlapack import ( "math" "sort" "testing" "golang.org/x/exp/rand" "gonum.org/v1/gonum/floats" ) type Dsterfer interface { Dgetrfer Dsterf(n int, d, e []float64) (ok bool) } func DsterfTest(t *testing.T, impl Dsterfer) { // Hand coded tests. for cas, test := range []struct { d []float64 e []float64 n int ans []float64 }{ // Computed from Fortran code. { d: []float64{1, 3, 4, 6}, e: []float64{2, 4, 5}, n: 4, ans: []float64{11.046227528488854, 4.795922173417400, -2.546379458290125, 0.704229756383872}, }, } { n := test.n d := make([]float64, len(test.d)) copy(d, test.d) e := make([]float64, len(test.e)) copy(e, test.e) ok := impl.Dsterf(n, d, e) if !ok { t.Errorf("Case %d, Eigenvalue decomposition failed", cas) continue } ans := make([]float64, len(test.ans)) copy(ans, test.ans) sort.Float64s(ans) if !floats.EqualApprox(ans, d, 1e-10) { t.Errorf("eigenvalue mismatch") } } rnd := rand.New(rand.NewSource(1)) // Probabilistic tests. for _, n := range []int{4, 6, 10} { for cas := 0; cas < 10; cas++ { d := make([]float64, n) for i := range d { d[i] = rnd.NormFloat64() } dCopy := make([]float64, len(d)) copy(dCopy, d) e := make([]float64, n-1) for i := range e { e[i] = rnd.NormFloat64() } eCopy := make([]float64, len(e)) copy(eCopy, e) ok := impl.Dsterf(n, d, e) if !ok { t.Errorf("Eigenvalue decomposition failed") continue } // Test that the eigenvalues are sorted. if !sort.Float64sAreSorted(d) { t.Errorf("Values are not sorted") } // Construct original tridagional matrix. lda := n a := make([]float64, n*lda) for i := 0; i < n; i++ { a[i*lda+i] = dCopy[i] if i != n-1 { a[i*lda+i+1] = eCopy[i] a[(i+1)*lda+i] = eCopy[i] } } asub := make([]float64, len(a)) ipiv := make([]int, n) // Test that they are actually eigenvalues by computing the // determinant of A - λI. // TODO(btracey): Replace this test with a more numerically stable // test. for _, lambda := range d { copy(asub, a) for i := 0; i < n; i++ { asub[i*lda+i] -= lambda } // Compute LU. ok := impl.Dgetrf(n, n, asub, lda, ipiv) if !ok { // Definitely singular. continue } // Compute determinant. var logdet float64 for i := 0; i < n; i++ { v := asub[i*lda+i] logdet += math.Log(math.Abs(v)) } if math.Exp(logdet) > 2 { t.Errorf("Incorrect singular value. n = %d, cas = %d, det = %v", n, cas, math.Exp(logdet)) } } } } }