Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
internal/asm/{c64,c128}: add stubs for kernels needed by cmplxs package
- Loading branch information
Showing
8 changed files
with
1,931 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// 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 c128_test | ||
|
||
import ( | ||
"math" | ||
"math/cmplx" | ||
"testing" | ||
|
||
"golang.org/x/exp/rand" | ||
|
||
"gonum.org/v1/gonum/floats" | ||
) | ||
|
||
const ( | ||
msgVal = "%v: unexpected value at %v Got: %v Expected: %v" | ||
msgGuard = "%v: Guard violated in %s vector %v %v" | ||
) | ||
|
||
var ( | ||
nan = math.NaN() | ||
|
||
cnan = cmplx.NaN() | ||
cinf = cmplx.Inf() | ||
) | ||
|
||
// TODO(kortschak): Harmonise the situation in asm/{f32,f64} and their sinks. | ||
const testLen = 1e5 | ||
|
||
var x = make([]complex128, testLen) | ||
|
||
// guardVector copies the source vector (vec) into a new slice with guards. | ||
// Guards guarded[:gdLn] and guarded[len-gdLn:] will be filled with sigil value gdVal. | ||
func guardVector(vec []complex128, gdVal complex128, gdLn int) (guarded []complex128) { | ||
guarded = make([]complex128, len(vec)+gdLn*2) | ||
copy(guarded[gdLn:], vec) | ||
for i := 0; i < gdLn; i++ { | ||
guarded[i] = gdVal | ||
guarded[len(guarded)-1-i] = gdVal | ||
} | ||
return guarded | ||
} | ||
|
||
// isValidGuard will test for violated guards, generated by guardVector. | ||
func isValidGuard(vec []complex128, gdVal complex128, gdLn int) bool { | ||
for i := 0; i < gdLn; i++ { | ||
if !sameCmplx(vec[i], gdVal) || !sameCmplx(vec[len(vec)-1-i], gdVal) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// guardIncVector copies the source vector (vec) into a new incremented slice with guards. | ||
// End guards will be length gdLen. | ||
// Internal and end guards will be filled with sigil value gdVal. | ||
func guardIncVector(vec []complex128, gdVal complex128, inc, gdLen int) (guarded []complex128) { | ||
if inc < 0 { | ||
inc = -inc | ||
} | ||
inrLen := len(vec) * inc | ||
guarded = make([]complex128, inrLen+gdLen*2) | ||
for i := range guarded { | ||
guarded[i] = gdVal | ||
} | ||
for i, v := range vec { | ||
guarded[gdLen+i*inc] = v | ||
} | ||
return guarded | ||
} | ||
|
||
// checkValidIncGuard will test for violated guards, generated by guardIncVector | ||
func checkValidIncGuard(t *testing.T, vec []complex128, gdVal complex128, inc, gdLen int) { | ||
srcLn := len(vec) - 2*gdLen | ||
for i := range vec { | ||
switch { | ||
case sameCmplx(vec[i], gdVal): | ||
// Correct value | ||
case (i-gdLen)%inc == 0 && (i-gdLen)/inc < len(vec): | ||
// Ignore input values | ||
case i < gdLen: | ||
t.Errorf("Front guard violated at %d %v", i, vec[:gdLen]) | ||
case i > gdLen+srcLn: | ||
t.Errorf("Back guard violated at %d %v", i-gdLen-srcLn, vec[gdLen+srcLn:]) | ||
default: | ||
t.Errorf("Internal guard violated at %d %v", i-gdLen, vec[gdLen:gdLen+srcLn]) | ||
} | ||
} | ||
} | ||
|
||
// same tests for nan-aware equality. | ||
func same(a, b float64) bool { | ||
return a == b || (math.IsNaN(a) && math.IsNaN(b)) | ||
} | ||
|
||
// sameApprox tests for nan-aware equality within tolerance. | ||
func sameApprox(a, b, tol float64) bool { | ||
return same(a, b) || floats.EqualWithinAbsOrRel(a, b, tol, tol) | ||
} | ||
|
||
// sameCmplx tests for nan-aware equality. | ||
func sameCmplx(a, b complex128) bool { | ||
return a == b || (cmplx.IsNaN(a) && cmplx.IsNaN(b)) | ||
} | ||
|
||
var ( // Offset sets for testing alignment handling in Unitary assembly functions. | ||
align1 = []int{0, 1} | ||
) | ||
|
||
func randomSlice(n, inc int) []complex128 { | ||
if inc < 0 { | ||
inc = -inc | ||
} | ||
x := make([]complex128, (n-1)*inc+1) | ||
for i := range x { | ||
x[i] = complex(rand.Float64(), rand.Float64()) | ||
} | ||
return x | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright ©2019 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 c128_test | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"math/cmplx" | ||
"testing" | ||
|
||
. "gonum.org/v1/gonum/internal/asm/c128" | ||
) | ||
|
||
// nanwith copied from floats package | ||
func nanwith(payload uint64) complex128 { | ||
const ( | ||
nanBits = 0x7ff8000000000000 | ||
nanMask = 0xfff8000000000000 | ||
) | ||
nan := math.Float64frombits(nanBits | (payload &^ nanMask)) | ||
return complex(nan, nan) | ||
} | ||
|
||
func TestL2NormUnitary(t *testing.T) { | ||
const tol = 1e-15 | ||
|
||
var src_gd complex128 = 1 | ||
for j, v := range []struct { | ||
want float64 | ||
x []complex128 | ||
}{ | ||
{want: 0, x: []complex128{}}, | ||
{want: 2, x: []complex128{2}}, | ||
{want: 2, x: []complex128{2i}}, | ||
{want: math.Sqrt(8), x: []complex128{2 + 2i}}, | ||
{want: 3.7416573867739413, x: []complex128{1, 2, 3}}, | ||
{want: 3.7416573867739413, x: []complex128{-1, -2, -3}}, | ||
{want: 3.7416573867739413, x: []complex128{1i, 2i, 3i}}, | ||
{want: 3.7416573867739413, x: []complex128{-1i, -2i, -3i}}, | ||
{want: math.Sqrt(28), x: []complex128{1 + 1i, 2 + 2i, 3 + 3i}}, | ||
{want: math.Sqrt(28), x: []complex128{-1 - 1i, -2 - 2i, -3 - 3i}}, | ||
{want: nan, x: []complex128{cnan}}, | ||
{want: nan, x: []complex128{1, cinf, 3, nanwith(25), 5}}, | ||
{want: 17.88854381999832, x: []complex128{8, -8, 8, -8, 8}}, | ||
{want: 2.23606797749979, x: []complex128{0, 1, 0, -1, 0, 1, 0, -1, 0, 1}}, | ||
{want: 17.88854381999832, x: []complex128{8i, -8i, 8i, -8i, 8i}}, | ||
{want: 2.23606797749979, x: []complex128{0, 1i, 0, -1i, 0, 1i, 0, -1i, 0, 1i}}, | ||
{want: math.Sqrt(640), x: []complex128{8 + 8i, -8 - 8i, 8 + 8i, -8 - 8i, 8 + 8i}}, | ||
{want: math.Sqrt(10), x: []complex128{0, 1 + 1i, 0, -1 - 1i, 0, 1 + 1i, 0, -1 - 1i, 0, 1 + 1i}}, | ||
} { | ||
g_ln := 4 + j%2 | ||
v.x = guardVector(v.x, src_gd, g_ln) | ||
src := v.x[g_ln : len(v.x)-g_ln] | ||
ret := L2NormUnitary(src) | ||
if !sameApprox(ret, v.want, tol) { | ||
t.Errorf("Test %d L2Norm error Got: %f Expected: %f", j, ret, v.want) | ||
} | ||
if !isValidGuard(v.x, src_gd, g_ln) { | ||
t.Errorf("Test %d Guard violated in src vector %v %v", j, v.x[:g_ln], v.x[len(v.x)-g_ln:]) | ||
} | ||
} | ||
} | ||
|
||
func TestL2DistanceUnitary(t *testing.T) { | ||
const tol = 1e-15 | ||
|
||
var src_gd complex128 = 1 | ||
for j, v := range []struct { | ||
want float64 | ||
x, y []complex128 | ||
}{ | ||
{want: 0, x: []complex128{}, y: []complex128{}}, | ||
{want: 2, x: []complex128{3}, y: []complex128{1}}, | ||
{want: 2, x: []complex128{3i}, y: []complex128{1i}}, | ||
{want: 3.7416573867739413, x: []complex128{2, 4, 6}, y: []complex128{1, 2, 3}}, | ||
{want: 3.7416573867739413, x: []complex128{1, 2, 3}, y: []complex128{2, 4, 6}}, | ||
{want: 3.7416573867739413, x: []complex128{2i, 4i, 6i}, y: []complex128{1i, 2i, 3i}}, | ||
{want: 3.7416573867739413, x: []complex128{1i, 2i, 3i}, y: []complex128{2i, 4i, 6i}}, | ||
{want: math.Sqrt(28), x: []complex128{2 + 2i, 4 + 4i, 6 + 6i}, y: []complex128{1 + 1i, 2 + 2i, 3 + 3i}}, | ||
{want: math.Sqrt(28), x: []complex128{1 + 1i, 2 + 2i, 3 + 3i}, y: []complex128{2 + 2i, 4 + 4i, 6 + 6i}}, | ||
{want: nan, x: []complex128{cnan}, y: []complex128{0}}, | ||
{want: 17.88854381999832, x: []complex128{9, -9, 9, -9, 9}, y: []complex128{1, -1, 1, -1, 1}}, | ||
{want: 2.23606797749979, x: []complex128{0, 1, 0, -1, 0, 1, 0, -1, 0, 1}, y: []complex128{0, 2, 0, -2, 0, 2, 0, -2, 0, 2}}, | ||
{want: 17.88854381999832, x: []complex128{9i, -9i, 9i, -9i, 9i}, y: []complex128{1i, -1i, 1i, -1i, 1i}}, | ||
{want: 2.23606797749979, x: []complex128{0, 1i, 0, -1i, 0, 1i, 0, -1i, 0, 1i}, y: []complex128{0, 2i, 0, -2i, 0, 2i, 0, -2i, 0, 2i}}, | ||
{want: math.Sqrt(640), x: []complex128{9 + 9i, -9 - 9i, 9 + 9i, -9 - 9i, 9 + 9i}, y: []complex128{1 + 1i, -1 - 1i, 1 + 1i, -1 - 1i, 1 + 1i}}, | ||
{want: math.Sqrt(10), x: []complex128{0, 1 + 1i, 0, -1 - 1i, 0, 1 + 1i, 0, -1 - 1i, 0, 1 + 1i}, y: []complex128{0, 2 + 2i, 0, -2 - 2i, 0, 2 + 2i, 0, -2 - 2i, 0, 2 + 2i}}, | ||
} { | ||
g_ln := 4 + j%2 | ||
v.x = guardVector(v.x, src_gd, g_ln) | ||
v.y = guardVector(v.y, src_gd, g_ln) | ||
srcX := v.x[g_ln : len(v.x)-g_ln] | ||
srcY := v.y[g_ln : len(v.y)-g_ln] | ||
ret := L2DistanceUnitary(srcX, srcY) | ||
if !sameApprox(ret, v.want, tol) { | ||
t.Errorf("Test %d L2Distance error Got: %f Expected: %f", j, ret, v.want) | ||
} | ||
if !isValidGuard(v.x, src_gd, g_ln) { | ||
t.Errorf("Test %d Guard violated in src vector %v %v", j, v.x[:g_ln], v.x[len(v.x)-g_ln:]) | ||
} | ||
} | ||
} | ||
|
||
func BenchmarkL2NormNetlib(b *testing.B) { | ||
netlib := func(x []complex128) (sum float64) { | ||
var scale float64 | ||
sumSquares := 1.0 | ||
for _, v := range x { | ||
if v == 0 { | ||
continue | ||
} | ||
absxi := cmplx.Abs(v) | ||
if math.IsNaN(absxi) { | ||
return math.NaN() | ||
} | ||
if scale < absxi { | ||
s := scale / absxi | ||
sumSquares = 1 + sumSquares*s*s | ||
scale = absxi | ||
} else { | ||
s := absxi / scale | ||
sumSquares += s * s | ||
} | ||
} | ||
if math.IsInf(scale, 1) { | ||
return math.Inf(1) | ||
} | ||
return scale * math.Sqrt(sumSquares) | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
f func(x []complex128) float64 | ||
}{ | ||
{"L2NormUnitaryNetlib", netlib}, | ||
{"L2NormUnitary", L2NormUnitary}, | ||
} | ||
x[0] = randomSlice(1, 1)[0] // replace the leading zero (edge case) | ||
for _, test := range tests { | ||
for _, ln := range []uintptr{1, 3, 10, 30, 1e2, 3e2, 1e3, 3e3, 1e4, 3e4, 1e5} { | ||
b.Run(fmt.Sprintf("%s-%d", test.name, ln), func(b *testing.B) { | ||
b.SetBytes(int64(64 * ln)) | ||
x := x[:ln] | ||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
test.f(x) | ||
} | ||
}) | ||
} | ||
} | ||
} |
Oops, something went wrong.