| // Copyright ©2015 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 ( |
| "fmt" |
| "math" |
| "sort" |
| "testing" |
| |
| "golang.org/x/exp/rand" |
| |
| "gonum.org/v1/gonum/floats" |
| ) |
| |
| type Dlasq1er interface { |
| Dlasq1(n int, d, e, work []float64) int |
| |
| Dgebrd(m, n int, a []float64, lda int, d, e, tauQ, tauP, work []float64, lwork int) |
| } |
| |
| func Dlasq1Test(t *testing.T, impl Dlasq1er) { |
| const tol = 1e-14 |
| rnd := rand.New(rand.NewSource(1)) |
| for _, n := range []int{0, 1, 2, 3, 4, 5, 8, 10, 30, 50} { |
| for typ := 0; typ <= 7; typ++ { |
| name := fmt.Sprintf("n=%v,typ=%v", n, typ) |
| |
| // Generate a diagonal matrix D with positive entries. |
| d := make([]float64, n) |
| switch typ { |
| case 0: |
| // The zero matrix. |
| case 1: |
| // The identity matrix. |
| for i := range d { |
| d[i] = 1 |
| } |
| case 2: |
| // A diagonal matrix with evenly spaced entries 1, ..., eps. |
| for i := 0; i < n; i++ { |
| if i == 0 { |
| d[0] = 1 |
| } else { |
| d[i] = 1 - (1-dlamchE)*float64(i)/float64(n-1) |
| } |
| } |
| case 3, 4, 5: |
| // A diagonal matrix with geometrically spaced entries 1, ..., eps. |
| for i := 0; i < n; i++ { |
| if i == 0 { |
| d[0] = 1 |
| } else { |
| d[i] = math.Pow(dlamchE, float64(i)/float64(n-1)) |
| } |
| } |
| switch typ { |
| case 4: |
| // Multiply by SQRT(overflow threshold). |
| floats.Scale(math.Sqrt(1/dlamchS), d) |
| case 5: |
| // Multiply by SQRT(underflow threshold). |
| floats.Scale(math.Sqrt(dlamchS), d) |
| } |
| case 6: |
| // A diagonal matrix with "clustered" entries 1, eps, ..., eps. |
| for i := range d { |
| if i == 0 { |
| d[i] = 1 |
| } else { |
| d[i] = dlamchE |
| } |
| } |
| case 7: |
| // Diagonal matrix with random entries. |
| for i := range d { |
| d[i] = math.Abs(rnd.NormFloat64()) |
| } |
| } |
| |
| dWant := make([]float64, n) |
| copy(dWant, d) |
| sort.Sort(sort.Reverse(sort.Float64Slice(dWant))) |
| |
| // Allocate work slice to the maximum length needed below. |
| work := make([]float64, max(1, 4*n)) |
| |
| // Generate an n×n matrix A by pre- and post-multiplying D with |
| // random orthogonal matrices: |
| // A = U*D*V. |
| lda := max(1, n) |
| a := make([]float64, n*lda) |
| Dlagge(n, n, 0, 0, d, a, lda, rnd, work) |
| |
| // Reduce A to bidiagonal form B represented by the diagonal d and |
| // off-diagonal e. |
| tauQ := make([]float64, n) |
| tauP := make([]float64, n) |
| e := make([]float64, max(0, n-1)) |
| impl.Dgebrd(n, n, a, lda, d, e, tauQ, tauP, work, len(work)) |
| |
| // Compute the singular values of B. |
| for i := range work { |
| work[i] = math.NaN() |
| } |
| info := impl.Dlasq1(n, d, e, work) |
| if info != 0 { |
| t.Fatalf("%v: Dlasq1 returned non-zero info=%v", name, info) |
| } |
| |
| if n == 0 { |
| continue |
| } |
| |
| if !sort.IsSorted(sort.Reverse(sort.Float64Slice(d))) { |
| t.Errorf("%v: singular values not sorted", name) |
| } |
| |
| diff := floats.Distance(d, dWant, math.Inf(1)) |
| if diff > tol*floats.Max(dWant) { |
| t.Errorf("%v: unexpected result; diff=%v", name, diff) |
| } |
| } |
| } |
| } |