internal/asm,blas,floats: move level 2 norm to asm
This allows sharing of the blas implementation with floats and opens the
possibility of an assembly implementation of this function.
MODIFY
blas/gonum/level1float32.go
blas/gonum/level1float32.go
@@ -39,52 +39,10 @@ func (Implementation) Snrm2(n int, x []float32, incX int) float32 {
}
panic(nLT0)
}
- var (
- scale float32 = 0
- sumSquares float32 = 1
- )
if incX == 1 {
- x = x[:n]
- for _, v := range x {
- if v == 0 {
- continue
- }
- absxi := math.Abs(v)
- if math.IsNaN(absxi) {
- return math.NaN()
- }
- if scale < absxi {
- sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
- scale = absxi
- } else {
- sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
- }
- }
- if math.IsInf(scale, 1) {
- return math.Inf(1)
- }
- return scale * math.Sqrt(sumSquares)
- }
- for ix := 0; ix < n*incX; ix += incX {
- val := x[ix]
- if val == 0 {
- continue
- }
- absxi := math.Abs(val)
- if math.IsNaN(absxi) {
- return math.NaN()
- }
- if scale < absxi {
- sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
- scale = absxi
- } else {
- sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
- }
- }
- if math.IsInf(scale, 1) {
- return math.Inf(1)
+ return f32.L2NormUnitary(x[:n])
}
- return scale * math.Sqrt(sumSquares)
+ return f32.L2NormInc(x, uintptr(n), uintptr(incX))
}
// Sasum computes the sum of the absolute values of the elements of x.

MODIFY
blas/gonum/level1float64.go
blas/gonum/level1float64.go
@@ -35,52 +35,10 @@ func (Implementation) Dnrm2(n int, x []float64, incX int) float64 {
}
panic(nLT0)
}
- var (
- scale float64 = 0
- sumSquares float64 = 1
- )
if incX == 1 {
- x = x[:n]
- for _, v := range x {
- if v == 0 {
- continue
- }
- absxi := math.Abs(v)
- if math.IsNaN(absxi) {
- return math.NaN()
- }
- if scale < absxi {
- sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
- scale = absxi
- } else {
- sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
- }
- }
- if math.IsInf(scale, 1) {
- return math.Inf(1)
- }
- return scale * math.Sqrt(sumSquares)
- }
- for ix := 0; ix < n*incX; ix += incX {
- val := x[ix]
- if val == 0 {
- continue
- }
- absxi := math.Abs(val)
- if math.IsNaN(absxi) {
- return math.NaN()
- }
- if scale < absxi {
- sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
- scale = absxi
- } else {
- sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
- }
- }
- if math.IsInf(scale, 1) {
- return math.Inf(1)
+ return f64.L2NormUnitary(x[:n])
}
- return scale * math.Sqrt(sumSquares)
+ return f64.L2NormInc(x, uintptr(n), uintptr(incX))
}
// Dasum computes the sum of the absolute values of the elements of x.

MODIFY
blas/gonum/single_precision.bash
blas/gonum/single_precision.bash
@@ -24,6 +24,8 @@ cat level1float64.go \
| gofmt -r 'f64.AxpyInc -> f32.AxpyInc' \
| gofmt -r 'f64.AxpyUnitary -> f32.AxpyUnitary' \
| gofmt -r 'f64.DotUnitary -> f32.DotUnitary' \
+| gofmt -r 'f64.L2NormInc -> f32.L2NormInc' \
+| gofmt -r 'f64.L2NormUnitary -> f32.L2NormUnitary' \
| gofmt -r 'f64.ScalInc -> f32.ScalInc' \
| gofmt -r 'f64.ScalUnitary -> f32.ScalUnitary' \
\

MODIFY
floats/floats.go
floats/floats.go
@@ -648,11 +648,7 @@ func Norm(s []float64, L float64) float64 {
return 0
}
if L == 2 {
- twoNorm := math.Abs(s[0])
- for i := 1; i < len(s); i++ {
- twoNorm = math.Hypot(twoNorm, s[i])
- }
- return twoNorm
+ return f64.L2NormUnitary(s)
}
var norm float64
if L == 1 {

MODIFY
floats/floats_test.go
floats/floats_test.go
@@ -261,7 +261,7 @@ func TestDistance(t *testing.T) {
copy(tmp, test.s)
Sub(tmp, test.t)
norm := Norm(tmp, L)
- if dist != norm { // Use equality because they should be identical
+ if !EqualWithinAbsOrRel(dist, norm, 1e-15, 1e-15) {
t.Errorf("Distance does not match norm for case %v, %v. Expected %v, Found %v.", i, j, norm, dist)
}
}
@@ -1753,3 +1753,15 @@ func BenchmarkScaleSmall(b *testing.B) { benchmarkScale(b, Small) }
func BenchmarkScaleMedium(b *testing.B) { benchmarkScale(b, Medium) }
func BenchmarkScaleLarge(b *testing.B) { benchmarkScale(b, Large) }
func BenchmarkScaleHuge(b *testing.B) { benchmarkScale(b, Huge) }
+
+func benchmarkNorm2(b *testing.B, size int) {
+ s := randomSlice(size)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Norm(s, 2)
+ }
+}
+func BenchmarkNorm2Small(b *testing.B) { benchmarkNorm2(b, Small) }
+func BenchmarkNorm2Medium(b *testing.B) { benchmarkNorm2(b, Medium) }
+func BenchmarkNorm2Large(b *testing.B) { benchmarkNorm2(b, Large) }
+func BenchmarkNorm2Huge(b *testing.B) { benchmarkNorm2(b, Huge) }

internal/asm,blas,floats: move level 2 norm to asm

This allows sharing of the blas implementation with floats and opens the
possibility of an assembly implementation of this function.