gonum.org/v1/gonum@v0.14.0/lapack/testlapack/dlassq.go (about) 1 // Copyright ©2019 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 "testing" 11 12 "golang.org/x/exp/rand" 13 14 "gonum.org/v1/gonum/floats" 15 ) 16 17 type Dlassqer interface { 18 Dlassq(n int, x []float64, incx int, scale, ssq float64) (float64, float64) 19 } 20 21 func DlassqTest(t *testing.T, impl Dlassqer) { 22 values := []float64{ 23 0, 24 2 * safmin, 25 smlnum, 26 ulp, 27 1, 28 1 / ulp, 29 bignum, 30 safmax, 31 math.Inf(1), 32 math.NaN(), 33 } 34 35 rnd := rand.New(rand.NewSource(1)) 36 for _, n := range []int{0, 1, 2, 3, 4, 5, 10, 20, 30, 40} { 37 for _, incx := range []int{1, 3} { 38 for cas := 0; cas < 3; cas++ { 39 for _, v0 := range values { 40 if v0 > 1 { 41 v0 *= 0.5 42 } 43 for _, v1 := range values { 44 if v1 > 1 { 45 v1 = 0.5 * v1 / math.Sqrt(float64(n+1)) 46 } 47 dlassqTest(t, impl, rnd, n, incx, cas, v0, v1) 48 } 49 } 50 } 51 } 52 } 53 } 54 55 func dlassqTest(t *testing.T, impl Dlassqer, rnd *rand.Rand, n, incx, cas int, v0, v1 float64) { 56 const ( 57 rogue = 1234.5678 58 tol = 1e-14 59 ) 60 61 name := fmt.Sprintf("n=%v,incx=%v,cas=%v,v0=%v,v1=%v", n, incx, cas, v0, v1) 62 63 // Generate n random values in (-1,1]. 64 work := make([]float64, n) 65 for i := range work { 66 work[i] = 1 - 2*rnd.Float64() 67 } 68 69 // Compute the sum of squares by an unscaled algorithm. 70 var workssq float64 71 for _, wi := range work { 72 workssq += wi * wi 73 } 74 75 // Construct a test (n+1)-vector z as 76 // z[0] = known value 77 // z[1:n+1] = scaling factor * work[:] 78 z := make([]float64, n+1) 79 z[0] = math.Sqrt(v0) 80 for i, wi := range work { 81 z[i+1] = v1 * wi 82 } 83 // Set initial scale and ssq corresponding to z[0]. 84 var scale, ssq float64 85 switch cas { 86 case 0: 87 scale = 1 88 ssq = v0 89 case 1: 90 scale = math.Sqrt(v0) 91 ssq = 1 92 case 2: 93 if v0 < 1 { 94 scale = 1.0 / 3 95 ssq = 9 * v0 96 } else { 97 scale = 3 98 ssq = v0 / 9 99 } 100 default: 101 panic("bad cas") 102 } 103 104 const ( 105 dtsml = 0x1p-511 106 dtbig = 0x1p486 107 dssml = 0x1p537 108 dsbig = 0x1p-538 109 ) 110 if scale*math.Sqrt(ssq) > dtbig && scale < math.Sqrt(math.SmallestNonzeroFloat64/ulp)/dsbig { 111 // The scaled sum is big but the scale itself is small. 112 return 113 } 114 if scale*math.Sqrt(ssq) < dtsml && scale > math.Sqrt(math.MaxFloat64)/dssml { 115 // The scaled sum is small but the scale itself is big. 116 return 117 } 118 119 // Compute the expected value of the sum of squares using the (n+1)-vector z. 120 z0 := z[0] 121 var z1n float64 122 if n >= 1 { 123 z1n = v1 * math.Sqrt(workssq) 124 } 125 zmin := math.Min(z0, z1n) 126 zmax := math.Max(z0, z1n) 127 var nrmWant float64 128 switch { 129 case math.IsNaN(z0) || math.IsNaN(z1n): 130 nrmWant = math.NaN() 131 case zmin == zmax: 132 nrmWant = math.Sqrt2 * zmax 133 case zmax == 0: 134 nrmWant = 0 135 default: 136 nrmWant = zmax * math.Sqrt(1+(zmin/zmax)*(zmin/zmax)) 137 } 138 139 // Allocate input slice for Dlassq and fill it with z[1:]. 140 x := make([]float64, max(0, 1+(n-1)*incx)) 141 for i := range x { 142 x[i] = rogue 143 } 144 for i, zi := range z[1:] { 145 x[i*incx] = zi 146 } 147 xCopy := make([]float64, len(x)) 148 copy(xCopy, x) 149 150 scaleGot, ssqGot := impl.Dlassq(n, x, incx, scale, ssq) 151 nrmGot := scaleGot * math.Sqrt(ssqGot) 152 153 if !floats.Same(x, xCopy) { 154 t.Fatalf("%v: unexpected modification of x", name) 155 } 156 157 // Check the result. 158 switch { 159 case math.IsNaN(nrmGot) || math.IsNaN(nrmWant): 160 if !math.IsNaN(nrmGot) { 161 t.Errorf("%v: expected NaN; got %v", name, nrmGot) 162 } 163 if !math.IsNaN(nrmWant) { 164 t.Errorf("%v: unexpected NaN; want %v", name, nrmWant) 165 } 166 case nrmGot == nrmWant: 167 case nrmWant == 0: 168 if nrmGot > tol { 169 t.Errorf("%v: unexpected result; got %v, want 0", name, nrmGot) 170 } 171 default: 172 diff := math.Abs(nrmGot-nrmWant) / nrmWant / math.Max(1, float64(n)) 173 if math.IsNaN(diff) || diff > tol { 174 t.Errorf("%v: unexpected result; got %v, want %v", name, nrmGot, nrmWant) 175 } 176 } 177 }