github.com/gopherd/gonum@v0.0.4/internal/asm/f64/l2norm_test.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 f64_test 6 7 import ( 8 "fmt" 9 "math" 10 "testing" 11 12 . "github.com/gopherd/gonum/internal/asm/f64" 13 ) 14 15 // nanwith copied from floats package 16 func nanwith(payload uint64) float64 { 17 const ( 18 nanBits = 0x7ff8000000000000 19 nanMask = 0xfff8000000000000 20 ) 21 return math.Float64frombits(nanBits | (payload &^ nanMask)) 22 } 23 24 func TestL2NormUnitary(t *testing.T) { 25 const tol = 1e-15 26 27 var src_gd float64 = 1 28 for j, v := range []struct { 29 want float64 30 x []float64 31 }{ 32 {want: 0, x: []float64{}}, 33 {want: 2, x: []float64{2}}, 34 {want: 3.7416573867739413, x: []float64{1, 2, 3}}, 35 {want: 3.7416573867739413, x: []float64{-1, -2, -3}}, 36 {want: nan, x: []float64{nan}}, 37 {want: nan, x: []float64{1, inf, 3, nanwith(25), 5}}, 38 {want: 17.88854381999832, x: []float64{8, -8, 8, -8, 8}}, 39 {want: 2.23606797749979, x: []float64{0, 1, 0, -1, 0, 1, 0, -1, 0, 1}}, 40 } { 41 g_ln := 4 + j%2 42 v.x = guardVector(v.x, src_gd, g_ln) 43 src := v.x[g_ln : len(v.x)-g_ln] 44 ret := L2NormUnitary(src) 45 if !sameApprox(ret, v.want, tol) { 46 t.Errorf("Test %d L2Norm error Got: %f Expected: %f", j, ret, v.want) 47 } 48 if !isValidGuard(v.x, src_gd, g_ln) { 49 t.Errorf("Test %d Guard violated in src vector %v %v", j, v.x[:g_ln], v.x[len(v.x)-g_ln:]) 50 } 51 } 52 } 53 54 func TestL2NormInc(t *testing.T) { 55 const tol = 1e-15 56 57 var src_gd float64 = 1 58 for j, v := range []struct { 59 inc int 60 want float64 61 x []float64 62 }{ 63 {inc: 2, want: 0, x: []float64{}}, 64 {inc: 3, want: 2, x: []float64{2}}, 65 {inc: 10, want: 3.7416573867739413, x: []float64{1, 2, 3}}, 66 {inc: 5, want: 3.7416573867739413, x: []float64{-1, -2, -3}}, 67 {inc: 3, want: nan, x: []float64{nan}}, 68 {inc: 15, want: 17.88854381999832, x: []float64{8, -8, 8, -8, 8}}, 69 {inc: 1, want: 2.23606797749979, x: []float64{0, 1, 0, -1, 0, 1, 0, -1, 0, 1}}, 70 } { 71 g_ln, ln := 4+j%2, len(v.x) 72 v.x = guardIncVector(v.x, src_gd, v.inc, g_ln) 73 src := v.x[g_ln : len(v.x)-g_ln] 74 ret := L2NormInc(src, uintptr(ln), uintptr(v.inc)) 75 if !sameApprox(ret, v.want, tol) { 76 t.Errorf("Test %d L2NormInc error Got: %f Expected: %f", j, ret, v.want) 77 } 78 checkValidIncGuard(t, v.x, src_gd, v.inc, g_ln) 79 } 80 } 81 82 func TestL2DistanceUnitary(t *testing.T) { 83 const tol = 1e-15 84 85 var src_gd float64 = 1 86 for j, v := range []struct { 87 want float64 88 x, y []float64 89 }{ 90 {want: 0, x: []float64{}, y: []float64{}}, 91 {want: 2, x: []float64{3}, y: []float64{1}}, 92 {want: 3.7416573867739413, x: []float64{2, 4, 6}, y: []float64{1, 2, 3}}, 93 {want: 3.7416573867739413, x: []float64{1, 2, 3}, y: []float64{2, 4, 6}}, 94 {want: nan, x: []float64{nan}, y: []float64{0}}, 95 {want: 17.88854381999832, x: []float64{9, -9, 9, -9, 9}, y: []float64{1, -1, 1, -1, 1}}, 96 {want: 2.23606797749979, x: []float64{0, 1, 0, -1, 0, 1, 0, -1, 0, 1}, y: []float64{0, 2, 0, -2, 0, 2, 0, -2, 0, 2}}, 97 } { 98 g_ln := 4 + j%2 99 v.x = guardVector(v.x, src_gd, g_ln) 100 v.y = guardVector(v.y, src_gd, g_ln) 101 srcX := v.x[g_ln : len(v.x)-g_ln] 102 srcY := v.y[g_ln : len(v.y)-g_ln] 103 ret := L2DistanceUnitary(srcX, srcY) 104 if !sameApprox(ret, v.want, tol) { 105 t.Errorf("Test %d L2Distance error Got: %f Expected: %f", j, ret, v.want) 106 } 107 if !isValidGuard(v.x, src_gd, g_ln) { 108 t.Errorf("Test %d Guard violated in src vector %v %v", j, v.x[:g_ln], v.x[len(v.x)-g_ln:]) 109 } 110 } 111 } 112 113 func BenchmarkL2NormNetlib(b *testing.B) { 114 netlib := func(x []float64) (sum float64) { 115 var scale float64 116 sumSquares := 1.0 117 for _, v := range x { 118 if v == 0 { 119 continue 120 } 121 absxi := math.Abs(v) 122 if math.IsNaN(absxi) { 123 return math.NaN() 124 } 125 if scale < absxi { 126 s := scale / absxi 127 sumSquares = 1 + sumSquares*s*s 128 scale = absxi 129 } else { 130 s := absxi / scale 131 sumSquares += s * s 132 } 133 } 134 if math.IsInf(scale, 1) { 135 return math.Inf(1) 136 } 137 return scale * math.Sqrt(sumSquares) 138 } 139 140 tests := []struct { 141 name string 142 f func(x []float64) float64 143 }{ 144 {"L2NormUnitaryNetlib", netlib}, 145 {"L2NormUnitary", L2NormUnitary}, 146 } 147 x[0] = randomSlice(1, 1)[0] // replace the leading zero (edge case) 148 for _, test := range tests { 149 for _, ln := range []uintptr{1, 3, 10, 30, 1e2, 3e2, 1e3, 3e3, 1e4, 3e4, 1e5} { 150 b.Run(fmt.Sprintf("%s-%d", test.name, ln), func(b *testing.B) { 151 b.SetBytes(int64(64 * ln)) 152 x := x[:ln] 153 b.ResetTimer() 154 for i := 0; i < b.N; i++ { 155 test.f(x) 156 } 157 }) 158 } 159 } 160 }