github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/testlapack/dlatrs.go (about) 1 // Copyright ©2017 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 "math/rand" 11 "testing" 12 13 "github.com/gonum/blas" 14 "github.com/gonum/blas/blas64" 15 ) 16 17 type Dlatrser interface { 18 Dlatrs(uplo blas.Uplo, trans blas.Transpose, diag blas.Diag, normin bool, n int, a []float64, lda int, x []float64, cnorm []float64) (scale float64) 19 } 20 21 func DlatrsTest(t *testing.T, impl Dlatrser) { 22 rnd := rand.New(rand.NewSource(1)) 23 for _, uplo := range []blas.Uplo{blas.Upper, blas.Lower} { 24 for _, trans := range []blas.Transpose{blas.Trans, blas.NoTrans} { 25 for _, n := range []int{0, 1, 2, 3, 4, 5, 6, 7, 10, 20, 50, 100} { 26 for _, lda := range []int{n, 2*n + 1} { 27 lda = max(1, lda) 28 imats := []int{7, 11, 12, 13, 14, 15, 16, 17, 18} 29 if n < 6 { 30 imats = append(imats, 19) 31 } 32 for _, imat := range imats { 33 testDlatrs(t, impl, imat, uplo, trans, n, lda, rnd) 34 } 35 } 36 } 37 } 38 } 39 } 40 41 func testDlatrs(t *testing.T, impl Dlatrser, imat int, uplo blas.Uplo, trans blas.Transpose, n, lda int, rnd *rand.Rand) { 42 const tol = 1e-14 43 44 a := nanSlice(n * lda) 45 b := nanSlice(n) 46 work := make([]float64, 3*n) 47 48 // Generate triangular test matrix and right hand side. 49 diag := dlattr(imat, uplo, trans, n, a, lda, b, work, rnd) 50 if imat <= 10 { 51 // b has not been generated. 52 dlarnv(b, 3, rnd) 53 } 54 55 cnorm := nanSlice(n) 56 x := make([]float64, n) 57 58 // Call Dlatrs with normin=false. 59 copy(x, b) 60 scale := impl.Dlatrs(uplo, trans, diag, false, n, a, lda, x, cnorm) 61 prefix := fmt.Sprintf("Case imat=%v (n=%v,lda=%v,trans=%v,uplo=%v,diag=%v", imat, n, lda, trans, uplo, diag) 62 for i, v := range cnorm { 63 if math.IsNaN(v) { 64 t.Errorf("%v: cnorm[%v] not computed (scale=%v,normin=false)", prefix, i, scale) 65 } 66 } 67 resid, hasNaN := dlatrsResidual(uplo, trans, diag, n, a, lda, scale, cnorm, x, b, work[:n]) 68 if hasNaN { 69 t.Errorf("%v: unexpected NaN (scale=%v,normin=false)", prefix, scale) 70 } else if resid > tol { 71 t.Errorf("%v: residual %v too large (scale=%v,normin=false)", prefix, scale) 72 } 73 74 // Call Dlatrs with normin=true because cnorm has been filled. 75 copy(x, b) 76 scale = impl.Dlatrs(uplo, trans, diag, true, n, a, lda, x, cnorm) 77 resid, hasNaN = dlatrsResidual(uplo, trans, diag, n, a, lda, scale, cnorm, x, b, work[:n]) 78 if hasNaN { 79 t.Errorf("%v: unexpected NaN (scale=%v,normin=true)", prefix, scale) 80 } else if resid > tol { 81 t.Errorf("%v: residual %v too large (scale=%v,normin=true)", prefix, scale) 82 } 83 } 84 85 // dlatrsResidual returns norm(trans(A)*x-scale*b) / (norm(trans(A))*norm(x)*eps) 86 // and whether NaN has been encountered in the process. 87 func dlatrsResidual(uplo blas.Uplo, trans blas.Transpose, diag blas.Diag, n int, a []float64, lda int, scale float64, cnorm []float64, x, b, work []float64) (resid float64, hasNaN bool) { 88 if n == 0 { 89 return 0, false 90 } 91 92 // Compute the norm of the triangular matrix A using the column norms 93 // already computed by Dlatrs. 94 var tnorm float64 95 if diag == blas.NonUnit { 96 for j := 0; j < n; j++ { 97 tnorm = math.Max(tnorm, math.Abs(a[j*lda+j])+cnorm[j]) 98 } 99 } else { 100 for j := 0; j < n; j++ { 101 tnorm = math.Max(tnorm, 1+cnorm[j]) 102 } 103 } 104 105 eps := dlamchE 106 smlnum := dlamchS 107 bi := blas64.Implementation() 108 109 // Compute norm(trans(A)*x-scale*b) / (norm(trans(A))*norm(x)*eps) 110 copy(work, x) 111 ix := bi.Idamax(n, work, 1) 112 xnorm := math.Max(1, math.Abs(work[ix])) 113 xscal := 1 / xnorm / float64(n) 114 bi.Dscal(n, xscal, work, 1) 115 bi.Dtrmv(uplo, trans, diag, n, a, lda, work, 1) 116 bi.Daxpy(n, -scale*xscal, b, 1, work, 1) 117 for _, v := range work { 118 if math.IsNaN(v) { 119 return 1 / eps, true 120 } 121 } 122 ix = bi.Idamax(n, work, 1) 123 resid = math.Abs(work[ix]) 124 ix = bi.Idamax(n, x, 1) 125 xnorm = math.Abs(x[ix]) 126 if resid*smlnum <= xnorm { 127 if xnorm > 0 { 128 resid /= xnorm 129 } 130 } else if resid > 0 { 131 resid = 1 / eps 132 } 133 if resid*smlnum <= tnorm { 134 if tnorm > 0 { 135 resid /= tnorm 136 } 137 } else if resid > 0 { 138 resid = 1 / eps 139 } 140 return resid, false 141 }