gonum.org/v1/gonum@v0.14.0/lapack/testlapack/dlasq1.go (about) 1 // Copyright ©2015 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/floats" 16 ) 17 18 type Dlasq1er interface { 19 Dlasq1(n int, d, e, work []float64) int 20 21 Dgebrd(m, n int, a []float64, lda int, d, e, tauQ, tauP, work []float64, lwork int) 22 } 23 24 func Dlasq1Test(t *testing.T, impl Dlasq1er) { 25 const tol = 1e-14 26 rnd := rand.New(rand.NewSource(1)) 27 for _, n := range []int{0, 1, 2, 3, 4, 5, 8, 10, 30, 50} { 28 for typ := 0; typ <= 7; typ++ { 29 name := fmt.Sprintf("n=%v,typ=%v", n, typ) 30 31 // Generate a diagonal matrix D with positive entries. 32 d := make([]float64, n) 33 switch typ { 34 case 0: 35 // The zero matrix. 36 case 1: 37 // The identity matrix. 38 for i := range d { 39 d[i] = 1 40 } 41 case 2: 42 // A diagonal matrix with evenly spaced entries 1, ..., eps. 43 for i := 0; i < n; i++ { 44 if i == 0 { 45 d[0] = 1 46 } else { 47 d[i] = 1 - (1-dlamchE)*float64(i)/float64(n-1) 48 } 49 } 50 case 3, 4, 5: 51 // A diagonal matrix with geometrically spaced entries 1, ..., eps. 52 for i := 0; i < n; i++ { 53 if i == 0 { 54 d[0] = 1 55 } else { 56 d[i] = math.Pow(dlamchE, float64(i)/float64(n-1)) 57 } 58 } 59 switch typ { 60 case 4: 61 // Multiply by SQRT(overflow threshold). 62 floats.Scale(math.Sqrt(1/dlamchS), d) 63 case 5: 64 // Multiply by SQRT(underflow threshold). 65 floats.Scale(math.Sqrt(dlamchS), d) 66 } 67 case 6: 68 // A diagonal matrix with "clustered" entries 1, eps, ..., eps. 69 for i := range d { 70 if i == 0 { 71 d[i] = 1 72 } else { 73 d[i] = dlamchE 74 } 75 } 76 case 7: 77 // Diagonal matrix with random entries. 78 for i := range d { 79 d[i] = math.Abs(rnd.NormFloat64()) 80 } 81 } 82 83 dWant := make([]float64, n) 84 copy(dWant, d) 85 sort.Sort(sort.Reverse(sort.Float64Slice(dWant))) 86 87 // Allocate work slice to the maximum length needed below. 88 work := make([]float64, max(1, 4*n)) 89 90 // Generate an n×n matrix A by pre- and post-multiplying D with 91 // random orthogonal matrices: 92 // A = U*D*V. 93 lda := max(1, n) 94 a := make([]float64, n*lda) 95 Dlagge(n, n, 0, 0, d, a, lda, rnd, work) 96 97 // Reduce A to bidiagonal form B represented by the diagonal d and 98 // off-diagonal e. 99 tauQ := make([]float64, n) 100 tauP := make([]float64, n) 101 e := make([]float64, max(0, n-1)) 102 impl.Dgebrd(n, n, a, lda, d, e, tauQ, tauP, work, len(work)) 103 104 // Compute the singular values of B. 105 for i := range work { 106 work[i] = math.NaN() 107 } 108 info := impl.Dlasq1(n, d, e, work) 109 if info != 0 { 110 t.Fatalf("%v: Dlasq1 returned non-zero info=%v", name, info) 111 } 112 113 if n == 0 { 114 continue 115 } 116 117 if !sort.IsSorted(sort.Reverse(sort.Float64Slice(d))) { 118 t.Errorf("%v: singular values not sorted", name) 119 } 120 121 diff := floats.Distance(d, dWant, math.Inf(1)) 122 if diff > tol*floats.Max(dWant) { 123 t.Errorf("%v: unexpected result; diff=%v", name, diff) 124 } 125 } 126 } 127 }