Files

2853 lines
90 KiB
Go
Raw Permalink Normal View History

2026-09-03 10:00:00 +02:00
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
package tensor
import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"fmt"
"hash"
"maps"
"math"
"os"
"path/filepath"
"runtime"
"runtime/debug"
"slices"
"testing"
"sourcedock.dev/petrbalvin/tensor/internal/core"
"sourcedock.dev/petrbalvin/tensor/spmd"
)
// The oracle harness. Every domain runs one fixed workload through the
// re-exported facade only, the raw bits of every output are hashed, and
// the per-case digest is pinned in the table below. A silent behaviour
// change, a swapped reduction order, a regression in a kernel: any of
// them moves the digest and fails the test. A deliberate change to an
// algorithm re-records the table in the same commit, so the diff shows
// exactly which case moved.
//
// Inputs are fixed literals or the seeded generator, which is
// bit-stable across Go releases by construction, and the parallel
// kernels promise a fixed reduction order, so the digest is
// deterministic on a given architecture. Run with
// TENSOR_ORACLE_RECORD=1 to print the current digests instead of
// comparing them.
// oracleDigest accumulates the raw bits of the given values. Supported
// carriers: *Array, Scalar, float64, []float64, string, bool and int;
// anything else is a harness bug and panics.
type oracleDigest struct {
h hash.Hash
}
func newOracleDigest() *oracleDigest { return &oracleDigest{h: sha256.New()} }
func (d *oracleDigest) array(t *testing.T, a *core.Array) {
d.h.Write([]byte{byte(a.Dtype())})
for _, s := range a.Shape() {
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], uint64(s))
d.h.Write(buf[:])
}
switch a.Dtype() {
case core.Int:
for _, v := range a.RawInts()[:a.Len()] {
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], uint64(v))
d.h.Write(buf[:])
}
case core.Float32:
for _, v := range a.RawFloat32s()[:a.Len()] {
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], math.Float32bits(v))
d.h.Write(buf[:])
}
case core.Float16:
for _, v := range a.RawHalves()[:a.Len()] {
var buf [2]byte
binary.LittleEndian.PutUint16(buf[:], v)
d.h.Write(buf[:])
}
case core.Float:
for _, v := range a.RawFloats()[:a.Len()] {
d.f64(v)
}
case core.Complex:
for _, v := range a.RawComplexes()[:a.Len()] {
d.f64(real(v))
d.f64(imag(v))
}
case core.Bool:
// The boolean payload hashes as its own bytes, one 0 or 1 per
// element, the layout every mask round-trip is read back in.
for _, v := range a.RawBools()[:a.Len()] {
if v {
d.h.Write([]byte{1})
} else {
d.h.Write([]byte{0})
}
}
case core.Int8:
for _, v := range a.RawInt8s()[:a.Len()] {
d.h.Write([]byte{byte(v)})
}
case core.Uint8:
for _, v := range a.RawUint8s()[:a.Len()] {
d.h.Write([]byte{byte(v)})
}
case core.Int16:
for _, v := range a.RawInt16s()[:a.Len()] {
var buf [2]byte
binary.LittleEndian.PutUint16(buf[:], uint16(v))
d.h.Write(buf[:])
}
case core.Uint16:
for _, v := range a.RawUint16s()[:a.Len()] {
var buf [2]byte
binary.LittleEndian.PutUint16(buf[:], uint16(v))
d.h.Write(buf[:])
}
case core.Int32:
for _, v := range a.RawInt32s()[:a.Len()] {
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], uint32(v))
d.h.Write(buf[:])
}
case core.Uint32:
for _, v := range a.RawUint32s()[:a.Len()] {
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], uint32(v))
d.h.Write(buf[:])
}
default:
// An unhashed payload would pin nothing: fail loudly in the
// test rather than panicking or hashing the wrong bytes.
t.Fatalf("oracle harness: array payload of dtype %s (%d) has no hash rule", a.Dtype(), a.Dtype())
}
}
func (d *oracleDigest) f64(v float64) {
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], math.Float64bits(v))
d.h.Write(buf[:])
}
func (d *oracleDigest) floats(vals []float64) {
var n [8]byte
binary.LittleEndian.PutUint64(n[:], uint64(len(vals)))
d.h.Write(n[:])
for _, v := range vals {
d.f64(v)
}
}
func (d *oracleDigest) int(v int) {
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], uint64(v))
d.h.Write(buf[:])
}
// result carries one output of an oracle case into the digest.
func (d *oracleDigest) result(t *testing.T, v any) {
switch x := v.(type) {
case *Array:
d.array(t, x)
case Scalar:
d.f64(x.Float())
case float64:
d.f64(x)
case []float64:
d.floats(x)
case string:
d.h.Write([]byte(x))
case bool:
// Error paths carry the fact of the refusal, not its wording:
// the text may be reworded, the refusal must not disappear.
if x {
d.h.Write([]byte{1})
} else {
d.h.Write([]byte{0})
}
case int:
d.int(x)
default:
panic("oracle harness: unsupported carrier type")
}
}
func (d *oracleDigest) sum() string { return hex.EncodeToString(d.h.Sum(nil)) }
// mustA builds an array from fixed values.
func mustA(t *testing.T, vals []float64, shape ...int) *Array {
t.Helper()
a, err := FromFloats(vals, shape...)
if err != nil {
t.Fatal(err)
}
return a
}
// randA draws n deterministic samples through the seeded generator.
func randA(t *testing.T, seed int64, n int) *Array {
t.Helper()
g := NewGenerator(seed)
a, err := Floats(g, n)
if err != nil {
t.Fatal(err)
}
return a
}
// wobble is the bounded deterministic wiggle the formula-driven
// workloads use in place of a generator: a fixed function of the
// index, so a case's inputs are its own, not the generator state's.
func wobble(i int) float64 {
return 0.3*math.Sin(7.3*float64(i)+1.1)*math.Cos(2.1*float64(i)) +
0.1*math.Sin(0.7*float64(i))
}
// oracleCases is the harness workload: one entry per domain feature,
// every output it produces fed into the digest.
var oracleCases = []struct {
name string
run func(t *testing.T) []any
}{
{"spmd-shards", func(t *testing.T) []any {
// The distributed reduction answers the single-array
// reduction's exact bits at any world size: the digest pins
// the shards' answers beside the single-array ones they must
// equal, over a length that cuts the fold's partition into
// several blocks.
const gn = 200001
vals := make([]float64, gn)
for i := range vals {
vals[i] = float64((i*6559)%2001-1000) / 7.0
if i%401 == 3 {
vals[i] = math.NaN()
}
}
yVals := make([]float64, gn)
for i := range yVals {
yVals[i] = float64((i*7919)%901-450) / 5.0
}
whole, err := FromFloats(vals, gn)
if err != nil {
t.Fatal(err)
}
wantSum := Sum(whole)
wantMax, err := Max(whole)
if err != nil {
t.Fatal(err)
}
out := []any{wantSum, wantMax}
for _, size := range []int{1, 3, 5} {
// Each rank writes its own slots; the digest takes them in
// rank order after the world has joined up again.
answers := make([]core.Scalar, size*5)
err := spmd.Launch(size, func(w *spmd.World) error {
span, err := spmd.Partition(gn, size, w.Rank())
if err != nil {
return err
}
local, err := Slice(whole, 0, span.Lo, span.Hi)
if err != nil {
return err
}
gotSum, err := w.AllReduceShards(local, span, spmd.Sum)
if err != nil {
return err
}
gotMax, err := w.AllReduceShards(local, span, spmd.Max)
if err != nil {
return err
}
gotProd, err := w.AllReduceShards(local, span, spmd.Prod)
if err != nil {
return err
}
gotNorm, err := w.AllReduceNormShards(local, span, 2)
if err != nil {
return err
}
second, err := FromFloats(yVals, gn)
if err != nil {
return err
}
secondLocal, err := Slice(second, 0, span.Lo, span.Hi)
if err != nil {
return err
}
gotDot, err := w.AllReduceDotShards(local, secondLocal, span)
if err != nil {
return err
}
wantProd, err := Prod(whole, 0, false)
if err != nil {
return err
}
wantNorm, err := Norm(whole, 2, 0, false)
if err != nil {
return err
}
wantDot, err := Dot(whole, second)
if err != nil {
return err
}
if math.Float64bits(gotSum.Float()) != math.Float64bits(wantSum.Float()) ||
math.Float64bits(gotMax.Float()) != math.Float64bits(wantMax.Float()) ||
math.Float64bits(gotProd.Float()) != math.Float64bits(wantProd.FloatAt(0)) ||
math.Float64bits(gotNorm.Float()) != math.Float64bits(wantNorm.FloatAt(0)) ||
math.Float64bits(gotDot.Float()) != math.Float64bits(wantDot.Float()) {
t.Fatalf("size %d: the sharded answers moved against the single-array reduction", size)
}
answers[w.Rank()*5] = gotSum
answers[w.Rank()*5+1] = gotMax
answers[w.Rank()*5+2] = gotProd
answers[w.Rank()*5+3] = gotNorm
answers[w.Rank()*5+4] = gotDot
return nil
})
if err != nil {
t.Fatal(err)
}
for _, s := range answers {
out = append(out, s)
}
out = append(out, fmt.Sprintf("size %d agreed", size))
}
return out
}},
{"core-elementwise", func(t *testing.T) []any {
a := randA(t, 1, 64)
b := randA(t, 2, 64)
sum, _ := Add(a, b)
prod, _ := Mul(a, b)
return []any{sum, prod, MulF(a, 1.5)}
}},
{"core-matmul-einsum", func(t *testing.T) []any {
a := randA(t, 3, 64)
b := randA(t, 4, 64)
mm, err := MatMul2D(mustA(t, a.RawFloats(), 8, 8), mustA(t, b.RawFloats(), 8, 8))
if err != nil {
t.Fatal(err)
}
e, err := Einsum("ij,jk->ik", mustA(t, a.RawFloats(), 8, 8), mustA(t, b.RawFloats(), 8, 8))
if err != nil {
t.Fatal(err)
}
return []any{mm, e}
}},
{"core-sort-argsort", func(t *testing.T) []any {
vals := randA(t, 5, 200).RawFloats()
vals[3] = math.NaN()
vals[17] = math.Copysign(0, -1)
vals[42] = math.Inf(1)
vals[43] = math.Inf(-1)
vals[7] = vals[8] // a tie
src := mustA(t, vals, len(vals))
s, err := Sort(src)
if err != nil {
t.Fatal(err)
}
idx, err := ArgSort(src)
if err != nil {
t.Fatal(err)
}
return []any{s, idx}
}},
{"core-reductions", func(t *testing.T) []any {
a := randA(t, 6, 1000)
b := randA(t, 7, 1000)
dot, err := Dot(a, b)
if err != nil {
t.Fatal(err)
}
mean, err := Mean(a)
if err != nil {
t.Fatal(err)
}
mn, err := Min(a)
if err != nil {
t.Fatal(err)
}
mx, err := Max(a)
if err != nil {
t.Fatal(err)
}
cs, err := CumSum(a, 0)
if err != nil {
t.Fatal(err)
}
return []any{Sum(a), mean, mn, mx, dot, cs}
}},
{"core-shape", func(t *testing.T) []any {
a := randA(t, 8, 24)
m := mustA(t, a.RawFloats(), 4, 6)
tr, err := TransposeAxes(m, 1, 0)
if err != nil {
t.Fatal(err)
}
r, err := Reshape(m, 3, 8)
if err != nil {
t.Fatal(err)
}
sl, err := Slice(m, 0, 1, 3)
if err != nil {
t.Fatal(err)
}
return []any{tr, r, sl}
}},
{"core-fft", func(t *testing.T) []any {
a := randA(t, 9, 256)
spec, err := FFT(a)
if err != nil {
t.Fatal(err)
}
back, err := IFFT(spec)
if err != nil {
t.Fatal(err)
}
return []any{spec, back}
}},
{"core-quasirandom", func(t *testing.T) []any {
sob, err := SobolPoints(64, 4, 0)
if err != nil {
t.Fatal(err)
}
sob2, err := SobolPoints(32, 4, 1000)
if err != nil {
t.Fatal(err)
}
hal, err := HaltonPoints(64, 4, 0)
if err != nil {
t.Fatal(err)
}
return []any{sob, sob2, hal}
}},
{"linalg-solve-inv-det", func(t *testing.T) []any {
a := randA(t, 10, 25)
// Diagonal dominance keeps the solve well conditioned.
m := mustA(t, a.RawFloats(), 5, 5)
md := m.RawFloats()
for i := range 5 {
md[i*5+i] += 10
}
b := randA(t, 11, 5)
x, err := Solve(m, b)
if err != nil {
t.Fatal(err)
}
iv, err := Inv(m)
if err != nil {
t.Fatal(err)
}
det, err := Det(m)
if err != nil {
t.Fatal(err)
}
return []any{x, iv, det}
}},
{"linalg-factorisations", func(t *testing.T) []any {
a := randA(t, 12, 36)
m := mustA(t, a.RawFloats(), 6, 6)
// Symmetrise, then shift to positive definite.
sym := make([]float64, 36)
mm := m.RawFloats()
for i := range 6 {
for j := range 6 {
sym[i*6+j] = (mm[i*6+j] + mm[j*6+i]) / 2
}
sym[i*6+i] += 8
}
pd := mustA(t, sym, 6, 6)
q, r, err := QR(pd)
if err != nil {
t.Fatal(err)
}
chol, err := Cholesky(pd)
if err != nil {
t.Fatal(err)
}
u, sigma, vt, err := SVD(pd)
if err != nil {
t.Fatal(err)
}
vals, vecs, err := Eigen(pd)
if err != nil {
t.Fatal(err)
}
return []any{q, r, chol, u, sigma, vt, vals, vecs}
}},
{"linalg-sparse-complex", func(t *testing.T) []any {
const n = 32
// Hermitian tridiagonal: real diagonal, imaginary couplings
// conjugated across the diagonal.
var hIdx []int64
var hVal []complex128
for i := range n {
hIdx = append(hIdx, int64(i), int64(i))
hVal = append(hVal, 2+0i)
if i+1 < n {
hIdx = append(hIdx, int64(i), int64(i+1))
hVal = append(hVal, 0.5i)
hIdx = append(hIdx, int64(i+1), int64(i))
hVal = append(hVal, -0.5i)
}
}
hIndices, err := FromInts(hIdx, len(hVal), 2)
if err != nil {
t.Fatal(err)
}
hValues, err := FromComplexes(hVal, len(hVal))
if err != nil {
t.Fatal(err)
}
h, err := NewSparseCOO(hIndices, hValues, []int{n, n})
if err != nil {
t.Fatal(err)
}
ones := make([]complex128, n)
for i := range ones {
ones[i] = 1
}
rhs, err := FromComplexes(ones, n)
if err != nil {
t.Fatal(err)
}
xCG, err := SpSolveComplexCG(h, rhs, 1e-12, 200)
if err != nil {
t.Fatal(err)
}
// Genuinely non-Hermitian: different off-diagonal values.
var gIdx []int64
var gVal []complex128
for i := range n {
gIdx = append(gIdx, int64(i), int64(i))
gVal = append(gVal, 2+1i)
if i+1 < n {
gIdx = append(gIdx, int64(i), int64(i+1))
gVal = append(gVal, 1)
gIdx = append(gIdx, int64(i+1), int64(i))
gVal = append(gVal, 0.25i)
}
}
gIndices, err := FromInts(gIdx, len(gVal), 2)
if err != nil {
t.Fatal(err)
}
gValues, err := FromComplexes(gVal, len(gVal))
if err != nil {
t.Fatal(err)
}
g, err := NewSparseCOO(gIndices, gValues, []int{n, n})
if err != nil {
t.Fatal(err)
}
xBiCG, err := SpSolveComplexBiCGSTAB(g, rhs, 1e-12, 400)
if err != nil {
t.Fatal(err)
}
vals, vecs, err := SpEigenComplex(h, 4, NewGenerator(11))
if err != nil {
t.Fatal(err)
}
return []any{xCG, xBiCG, vals, vecs}
}},
{"linalg-sparse-cholesky", func(t *testing.T) []any {
// A shuffled 5-point Laplacian: the ordering does the work,
// the solve must answer A⁻¹·(A·v) = v for both orderings.
const n = 100
g := NewGenerator(31)
perm := make([]int, n)
for i := range perm {
perm[i] = i
}
for i := n - 1; i > 0; i-- {
j := int(g.Next() % uint64(i+1))
perm[i], perm[j] = perm[j], perm[i]
}
label := func(x, y, w int) int { return perm[y*w+x] }
var idx []int64
var vals []float64
add := func(r, c int, v float64) {
idx = append(idx, int64(r), int64(c))
vals = append(vals, v)
}
const w, h = 10, 10
for y := range h {
for x := range w {
add(label(x, y, w), label(x, y, w), 4)
if x+1 < w {
add(label(x, y, w), label(x+1, y, w), -1)
add(label(x+1, y, w), label(x, y, w), -1)
}
if y+1 < h {
add(label(x, y, w), label(x, y+1, w), -1)
add(label(x, y+1, w), label(x, y, w), -1)
}
}
}
indices, err := FromInts(idx, len(vals), 2)
if err != nil {
t.Fatal(err)
}
valArray, err := FloatsFromArray(vals, len(vals))
if err != nil {
t.Fatal(err)
}
coo, err := NewSparseCOO(indices, valArray, []int{n, n})
if err != nil {
t.Fatal(err)
}
csr, err := CSRFromCOO(coo)
if err != nil {
t.Fatal(err)
}
v := make([]float64, n)
for i := range v {
v[i] = float64(i%13) - 6 + 0.5*float64(i%7)
}
xTrue := New(Float, n)
for i := range v {
xTrue.RawFloats()[i] = v[i]
}
b, err := csr.MatVec(xTrue)
if err != nil {
t.Fatal(err)
}
nat, err := NewSparseCholesky(coo, SparseOrderingNatural)
if err != nil {
t.Fatal(err)
}
rcm, err := NewSparseCholesky(coo, SparseOrderingReverseCuthillMcKee)
if err != nil {
t.Fatal(err)
}
xn, err := nat.Solve(b)
if err != nil {
t.Fatal(err)
}
xr, err := rcm.Solve(b)
if err != nil {
t.Fatal(err)
}
pn := nat.Permutation()
pr := rcm.Permutation()
pnf := make([]float64, len(pn))
prf := make([]float64, len(pr))
for i := range pn {
pnf[i] = float64(pn[i])
prf[i] = float64(pr[i])
}
return []any{pnf, prf, float64(nat.NNZ()), float64(rcm.NNZ()), xn, xr}
}},
{"linalg-sparse-lu", func(t *testing.T) []any {
// A nonsymmetric banded system with real fill: the factor must
// invert the map the CSR MatVec builds, pivoting included.
const n = 40
g := NewGenerator(41)
var idx []int64
var vals []float64
add := func(r, c int, v float64) {
idx = append(idx, int64(r), int64(c))
vals = append(vals, v)
}
for i := range n {
add(i, i, 5+2*g.Unit())
if i+1 < n {
add(i, i+1, -1-g.Unit())
add(i+1, i, -1-g.Unit())
}
if i+3 < n {
add(i, i+3, -0.6*g.Unit())
}
if i+4 < n {
add(i+4, i, -0.4*g.Unit())
}
}
indices, err := FromInts(idx, len(vals), 2)
if err != nil {
t.Fatal(err)
}
valArray, err := FloatsFromArray(vals, len(vals))
if err != nil {
t.Fatal(err)
}
coo, err := NewSparseCOO(indices, valArray, []int{n, n})
if err != nil {
t.Fatal(err)
}
csr, err := CSRFromCOO(coo)
if err != nil {
t.Fatal(err)
}
v := make([]float64, n)
for i := range v {
v[i] = float64(i%9) - 4 + 0.25*float64(i%5)
}
xTrue := New(Float, n)
for i := range v {
xTrue.RawFloats()[i] = v[i]
}
b, err := csr.MatVec(xTrue)
if err != nil {
t.Fatal(err)
}
f, err := NewSparseLU(coo)
if err != nil {
t.Fatal(err)
}
x, err := f.Solve(b)
if err != nil {
t.Fatal(err)
}
p := f.Permutation()
pf := make([]float64, len(p))
for i := range p {
pf[i] = float64(p[i])
}
return []any{pf, float64(f.NNZ()), x}
}},
{"integrate-fem-poisson", func(t *testing.T) []any {
// The manufactured solution u = sin(πx)·sin(πy) on a 10×10
// structured mesh of the unit square: assemble, lift the
// boundary, solve through the sparse Cholesky.
const m = 10
n := (m + 1) * (m + 1)
vertices := make([]float64, 2*n)
for j := range m + 1 {
for i := range m + 1 {
vertices[2*(j*(m+1)+i)] = float64(i) / float64(m)
vertices[2*(j*(m+1)+i)+1] = float64(j) / float64(m)
}
}
at := func(i, j int) int64 { return int64(j*(m+1) + i) }
idx := make([]int64, 0, 6*m*m)
for j := range m {
for i := range m {
idx = append(idx, at(i, j), at(i+1, j), at(i+1, j+1), at(i, j), at(i+1, j+1), at(i, j+1))
}
}
vArr, err := FromFloats(vertices, n, 2)
if err != nil {
t.Fatal(err)
}
tArr, err := FromInts(idx, m*m*2, 3)
if err != nil {
t.Fatal(err)
}
mesh, err := NewTriangleMesh2D(vArr, tArr)
if err != nil {
t.Fatal(err)
}
sol := func(x, y float64) float64 { return math.Sin(math.Pi*x) * math.Sin(math.Pi*y) }
src := func(x, y float64) float64 { return 2 * math.Pi * math.Pi * sol(x, y) }
var bound []int
var vals []float64
for j := range m + 1 {
for i := range m + 1 {
if i == 0 || i == m || j == 0 || j == m {
bound = append(bound, j*(m+1)+i)
vals = append(vals, sol(float64(i)/float64(m), float64(j)/float64(m)))
}
}
}
u, err := SolvePoissonFEM2D(mesh, src, FEMPoissonOptions{Kappa: 1, DirichletNodes: bound, DirichletValues: vals, Ordering: SparseOrderingReverseCuthillMcKee})
if err != nil {
t.Fatal(err)
}
first := make([]float64, 0, 12)
for i := range 12 {
first = append(first, u.FloatAt(i))
}
return []any{first, u.FloatAt(n / 2), u.FloatAt(n - 1)}
}},
{"signal-welch-savgol", func(t *testing.T) []any {
x := randA(t, 13, 512)
freqs, psd, err := WelchPSD(x, 1000, 128, 64, "hann")
if err != nil {
t.Fatal(err)
}
sm, err := SavitzkyGolay(x, 11, 3)
if err != nil {
t.Fatal(err)
}
return []any{freqs, psd, sm}
}},
{"signal-wavelets", func(t *testing.T) []any {
x := randA(t, 14, 128)
coef, err := DWT(x, 3)
if err != nil {
t.Fatal(err)
}
back, err := IDWT(coef, 3)
if err != nil {
t.Fatal(err)
}
cw, err := CWT(x, Morlet, []float64{1, 2, 4, 8}, 1)
if err != nil {
t.Fatal(err)
}
return []any{coef, back, cw}
}},
{"signal-lombscargle-conv", func(t *testing.T) []any {
g := NewGenerator(15)
times, err := Floats(g, 100)
if err != nil {
t.Fatal(err)
}
acc := 0.0
for i, v := range times.RawFloats() {
acc += v + 0.1
times.RawFloats()[i] = acc // irregular but increasing
}
vals := randA(t, 16, 100)
freqs, power, err := LombScargle(times, vals, 0.1, 5, 32)
if err != nil {
t.Fatal(err)
}
input := mustA(t, randA(t, 17, 64).RawFloats(), 1, 1, 64)
kernel := mustA(t, randA(t, 18, 5).RawFloats(), 1, 1, 5)
conv, err := Conv1D(input, kernel, nil, 1, 0, 1)
if err != nil {
t.Fatal(err)
}
return []any{freqs, power, conv}
}},
{"signal-filters-stencils", func(t *testing.T) []any {
x := randA(t, 19, 256)
b, a, err := ButterworthLowPass(4, 1000, 100)
if err != nil {
t.Fatal(err)
}
filt, err := FilterApply(b, a, x)
if err != nil {
t.Fatal(err)
}
grad, err := Gradient1D(x, 0.5)
if err != nil {
t.Fatal(err)
}
m := mustA(t, randA(t, 20, 64).RawFloats(), 8, 8)
lap, err := Laplacian(m, 1.0, 1.0)
if err != nil {
t.Fatal(err)
}
return []any{filt, grad, lap}
}},
{"stats-moments-quantiles", func(t *testing.T) []any {
a := randA(t, 21, 1000)
v, err := Var(a)
if err != nil {
t.Fatal(err)
}
s, err := Std(a)
if err != nil {
t.Fatal(err)
}
med, err := Median(a)
if err != nil {
t.Fatal(err)
}
q, err := Quantile(a, []float64{0.05, 0.25, 0.5, 0.75, 0.95})
if err != nil {
t.Fatal(err)
}
mean, err := Mean(a)
if err != nil {
t.Fatal(err)
}
return []any{mean, v, s, med, q}
}},
{"stats-correlation-regression", func(t *testing.T) []any {
x := randA(t, 22, 200)
ac, err := Autocorrelate(x, 16)
if err != nil {
t.Fatal(err)
}
pac, err := PartialAutocorrelate(x, 8)
if err != nil {
t.Fatal(err)
}
// The design carries its own intercept column.
xd := x.RawFloats()
design := make([]float64, 400)
for i := range 200 {
design[i*2] = 1
design[i*2+1] = xd[i]
}
lr, err := LinearRegression(mustA(t, design, 200, 2), randA(t, 23, 200))
if err != nil {
t.Fatal(err)
}
return []any{ac, pac, lr.Coefficients, lr.StandardErrors, lr.PValues,
lr.ResidualVariance, lr.RSquared}
}},
{"stats-poisson-regression", func(t *testing.T) []any {
// Counts from the seeded generator: one draw per row at the
// mean the true coefficients produce.
g := NewGenerator(24)
xv := randA(t, 23, 120)
design := make([]float64, 240)
y := make([]float64, 120)
for i := range 120 {
design[i*2] = 1
design[i*2+1] = xv.FloatAt(i)
counts, err := PoissonDraws(g, 1, math.Exp(0.3+0.7*xv.FloatAt(i)))
if err != nil {
t.Fatal(err)
}
y[i] = counts.FloatAt(0)
}
res, err := PoissonRegression(mustA(t, design, 120, 2), mustA(t, y, 120))
if err != nil {
t.Fatal(err)
}
return []any{res.Coefficients, res.StandardErrors, res.PValues,
res.Fitted, res.LogLikelihood, res.Iterations}
}},
{"stats-distributions", func(t *testing.T) []any {
tt, df, p, err := WelchTTest(randA(t, 24, 50), randA(t, 25, 60))
if err != nil {
t.Fatal(err)
}
gc, err := GammaCDF(2.5, 3, 1.5)
if err != nil {
t.Fatal(err)
}
ec, err := ExponentialCDF(1.25, 2)
if err != nil {
t.Fatal(err)
}
sc, err := StudentTCDF(1.5, 7)
if err != nil {
t.Fatal(err)
}
pc, err := PoissonCDF(3, 2.5)
if err != nil {
t.Fatal(err)
}
bc, err := BinomialCDF(4, 10, 0.35)
if err != nil {
t.Fatal(err)
}
nq, err := NormalQuantile(0.975)
if err != nil {
t.Fatal(err)
}
return []any{NormalCDF(1.96), tt, df, p, gc, ec, sc, pc, bc, nq}
}},
{"integrate-quadrature-ode", func(t *testing.T) []any {
q, qerr, err := IntegrateFunction(func(x float64) (float64, error) {
return math.Exp(-x * x), nil
}, 0, 1, QuadratureOptions{})
if err != nil {
t.Fatal(err)
}
decay := func(t float64, y *Array) (*Array, error) { return MulF(y, -1), nil }
y0 := mustA(t, []float64{1}, 1)
end, err := IntegrateODE(decay, 0, 2, y0, ODEOptions{MaxSteps: 10000})
if err != nil {
t.Fatal(err)
}
nd, err := IntegrateND(func(x []float64) float64 {
return math.Exp(-x[0] - x[1]*x[1])
}, []float64{0, 0}, []float64{1, 1}, CubatureOptions{})
if err != nil {
t.Fatal(err)
}
heat, err := IntegrateHeat1D(mustA(t, []float64{1, 2, 3, 4, 5, 6, 7, 8}, 8),
0.1, 0.1, 0.05, 0.005, 3, 0, 0)
if err != nil {
t.Fatal(err)
}
return []any{q, qerr, end, nd, heat}
}},
{"optim-roots-minima", func(t *testing.T) []any {
root, err := FindRoot(func(x float64) float64 { return math.Cos(x) - x }, 0, 1, 1e-12)
if err != nil {
t.Fatal(err)
}
rosen := func(p *Array) (float64, error) {
x := p.FloatAt(0)
y := p.FloatAt(1)
return (1-x)*(1-x) + 100*(y-x*x)*(y-x*x), nil
}
pt, val, err := Minimise(rosen, mustA(t, []float64{-1.2, 1}, 2),
MinimiseOptions{MaxIterations: 500, Tolerance: 1e-12})
if err != nil {
t.Fatal(err)
}
return []any{root, pt, val}
}},
{"optim-bounded-minima", func(t *testing.T) []any {
// The bounded solver on the wall case: with x forced past 1.5
// the Rosenbrock neck at (1, 1) is infeasible and the minimum
// sits at (1.5, 2.25) with value 0.25, the first coordinate
// pinned by the projection and released never.
rosen := func(p *Array) (float64, error) {
x := p.FloatAt(0)
y := p.FloatAt(1)
return (1-x)*(1-x) + 100*(y-x*x)*(y-x*x), nil
}
pt, val, err := MinimiseLBFGS(rosen, nil, mustA(t, []float64{-1.2, 1}, 2),
LBFGSOptions{Tolerance: 1e-10, Lower: []float64{1.5, math.Inf(-1)}})
if err != nil {
t.Fatal(err)
}
refused := false
_, _, err = MinimiseLBFGS(rosen, nil, mustA(t, []float64{0, 0}, 2),
LBFGSOptions{Lower: []float64{2}, Upper: []float64{1}})
if err != nil {
refused = true
}
return []any{pt, val, refused}
}},
{"optim-constrained-minima", func(t *testing.T) []any {
// The augmented Lagrangian on an active inequality: the bowl
// around (2, -1) cut by x + y ≤ 0 bottoms out on the wall at
// (1.5, -1.5) with value 0.5.
bowl := func(p *Array) (float64, error) {
dx := p.FloatAt(0) - 2
dy := p.FloatAt(1) + 1
return dx*dx + dy*dy, nil
}
A := mustA(t, []float64{1, 1}, 1, 2)
pt, val, err := MinimiseConstrained(bowl, nil, mustA(t, []float64{4, 4}, 2),
LinearConstraints{A: A, Lower: []float64{math.Inf(-1)}, Upper: []float64{0}},
LBFGSOptions{Tolerance: 1e-10})
if err != nil {
t.Fatal(err)
}
refused := false
_, _, err = MinimiseConstrained(bowl, nil, mustA(t, []float64{0, 0}, 2),
LinearConstraints{Lower: []float64{0}, Upper: []float64{1}}, LBFGSOptions{})
if err != nil {
refused = true
}
return []any{pt, val, refused}
}},
{"grad-backward", func(t *testing.T) []any {
x := FromArray(mustA(t, []float64{1.5, -2, 3}, 3), true)
loss, err := x.Mul(x)
if err != nil {
t.Fatal(err)
}
l, err := loss.Sum()
if err != nil {
t.Fatal(err)
}
if err := l.Backward(); err != nil {
t.Fatal(err)
}
return []any{x.Grad()}
}},
{"io-roundtrips", func(t *testing.T) []any {
dir := t.TempDir()
a := randA(t, 26, 24)
m := mustA(t, a.RawFloats(), 4, 6)
fitsPath := filepath.Join(dir, "o.fits")
if err := SaveFITS(fitsPath, m, map[string]string{"OBJECT": "oracle"}); err != nil {
t.Fatal(err)
}
backFits, hdr, err := LoadFITS(fitsPath)
if err != nil {
t.Fatal(err)
}
ncPath := filepath.Join(dir, "o.nc")
dims := []NetCDFDim{{Name: "row", Length: 4}, {Name: "col", Length: 6}}
vars := []NetCDFVar{{Name: "field", Dims: []string{"row", "col"}, Values: m}}
if err := SaveNetCDF(ncPath, dims, vars, map[string]string{"title": "oracle"}); err != nil {
t.Fatal(err)
}
_, backNc, ncAttrs, err := LoadNetCDF(ncPath)
if err != nil {
t.Fatal(err)
}
return []any{backFits, hdr["OBJECT"], backNc[0].Values, ncAttrs["title"]}
}},
{"core-views-and-int-precision", func(t *testing.T) []any {
// Views, the integer range above 2^53 and a widened query
// array: the 2026-09 review found defects in all three.
x := randA(t, 41, 12)
view, err := Slice(x, 0, 3, 9)
if err != nil {
t.Fatal(err)
}
pow, err := PowI(view, 3)
if err != nil {
t.Fatal(err)
}
big, err := FromInts([]int64{1 << 53, 1<<53 + 1, -(1 << 53)}, 3)
if err != nil {
t.Fatal(err)
}
hi, err := ArgMax(big)
if err != nil {
t.Fatal(err)
}
lo, err := ArgMin(big)
if err != nil {
t.Fatal(err)
}
// A float32 view whose payload is longer than its extent: the
// widening inside Interpolate2D used to walk the payload.
xs, err := FromFloat32s(make([]float32, x.Len()), x.Len())
if err != nil {
t.Fatal(err)
}
for i := range xs.Len() {
// Stay inside the (3, 4) grid's closed domain.
xs.SetFloatAt(i, float64(i%4))
}
viewXs, err := Slice(xs, 0, 0, view.Len())
if err != nil {
t.Fatal(err)
}
grid, err := FromFloats(make([]float64, 12), 3, 4)
if err != nil {
t.Fatal(err)
}
// The y queries need their own range: the grid's y domain is
// [0, rows−1], narrower than x's.
ys, err := FromFloat32s(make([]float32, viewXs.Len()), viewXs.Len())
if err != nil {
t.Fatal(err)
}
for i := range ys.Len() {
ys.SetFloatAt(i, float64(i%3))
}
interp, err := Interpolate2D(grid, viewXs, ys, 0, 0, 1, 1)
if err != nil {
t.Fatal(err)
}
return []any{pow, hi, lo, interp}
}},
{"io-hdf5-fixture", func(t *testing.T) []any {
// The HDF5 reference-library fixture: contiguous ints, a chunked
// deflate+shuffle float vector, a float32 matrix in a group,
// and attributes inherited from the groups.
sets, err := LoadHDF5(filepath.Join("io", "testdata", "h5", "fixture.h5"))
if err != nil {
t.Fatal(err)
}
out := []any{}
for _, d := range sets {
out = append(out, d.Path, d.Values)
for _, k := range slices.Sorted(maps.Keys(d.Attrs)) {
out = append(out, k, d.Attrs[k])
}
}
return out
}},
{"io-hostile-inputs", func(t *testing.T) []any {
// A header that lies about its sizes must be refused, never
// allocated and never panicked over: both files here are a few
// dozen bytes and claim data no file could hold.
dir := t.TempDir()
ncPath := filepath.Join(dir, "hostile.nc")
if err := os.WriteFile(ncPath, hostileOracleHeader(), 0o644); err != nil {
t.Fatal(err)
}
_, _, _, ncErr := LoadNetCDF(ncPath)
fitsPath := filepath.Join(dir, "hostile.fits")
if err := os.WriteFile(fitsPath, hostileOracleTable(), 0o644); err != nil {
t.Fatal(err)
}
_, fitsErr := LoadFITSTable(fitsPath)
return []any{ncErr != nil, fitsErr != nil}
}},
{"core-float16", func(t *testing.T) []any {
// The half dtype end to end: narrowing of values that straddle
// the format's corners, arithmetic through the promotion
// ladder, ordering that must widen before it compares.
vals := []float64{1, -2.5, 0.25, 65504, -65504, 1.0 / 16384, 1.0 / 16777216, 0.1, 2048.5}
h, err := FromFloat16s(vals, len(vals))
if err != nil {
t.Fatal(err)
}
s := Sum(h)
sorted, err := Sort(h)
if err != nil {
t.Fatal(err)
}
prod := MulF(h, 2)
widened, err := Add(h, mustA(t, make([]float64, len(vals)), len(vals)))
if err != nil {
t.Fatal(err)
}
ints, err := Astype(h, Int)
if err != nil {
t.Fatal(err)
}
return []any{h, s, sorted, prod, widened, ints,
HalfToFloat64(HalfFromFloat64(0.1)), HalfToFloat64(0x7BFF),
HalfToFloat64(0x0001), HalfFromFloat64(65520) == 0x7C00,
HalfFromFloat64(-1.0/33554432.0) == 0x8000}
}},
{"signal-kalman-arma", func(t *testing.T) []any {
// The linear filter on a scalar random walk with noisy
// measurements from the seeded generator, the unscented filter
// over the same exactly linear model, and the AR fit with its
// theoretical spectrum.
g := NewGenerator(50)
z, err := Floats(g, 40)
if err != nil {
t.Fatal(err)
}
one := mustA(t, []float64{1}, 1, 1)
qv := mustA(t, []float64{0.01}, 1, 1)
rv := mustA(t, []float64{1}, 1, 1)
zero := mustA(t, []float64{0}, 1)
pv := mustA(t, []float64{1}, 1, 1)
res, err := KalmanFilter(z, one, one, KalmanOptions{
InitialState: zero, InitialCovariance: pv,
ProcessNoise: qv, MeasurementNoise: rv})
if err != nil {
t.Fatal(err)
}
uk, err := UnscentedKalmanFilter(z,
func(x *Array) (*Array, error) { return MulF(x, 1), nil },
func(x *Array) (*Array, error) { return MulF(x, 1), nil },
KalmanOptions{
InitialState: zero, InitialCovariance: pv,
ProcessNoise: qv, MeasurementNoise: rv})
if err != nil {
t.Fatal(err)
}
x, err := Floats(NewGenerator(51), 300)
if err != nil {
t.Fatal(err)
}
ar, err := EstimateAR(x, 2)
if err != nil {
t.Fatal(err)
}
freqs, psd, err := ARMASpectrum(ar, 32)
if err != nil {
t.Fatal(err)
}
return []any{res.States, res.Covariances, res.Innovations, res.LogLikelihood,
uk.LogLikelihood, uk.States, ar.AR, ar.InnovationVariance, ar.AIC, freqs, psd}
}},
{"signal-windows-filtfilt-dwt", func(t *testing.T) []any {
// The window catalogue, the zero-phase filter, the 2-D median
// and the Daubechies transform over seeded inputs.
hann, err := WindowHann(16, false)
if err != nil {
t.Fatal(err)
}
kaiser, err := WindowKaiser(16, 4.5, true)
if err != nil {
t.Fatal(err)
}
x := randA(t, 52, 128)
b, a, err := ButterworthLowPass(3, 100, 20)
if err != nil {
t.Fatal(err)
}
ff, err := Filtfilt(b, a, x)
if err != nil {
t.Fatal(err)
}
img := mustA(t, randA(t, 53, 64).RawFloats(), 8, 8)
med, err := MedianFilter2D(img, 3)
if err != nil {
t.Fatal(err)
}
coef, err := DaubechiesDWT(x, DB4, 2, DWTPeriodic)
if err != nil {
t.Fatal(err)
}
back, err := DaubechiesIDWT(coef, DB4, 2, DWTPeriodic)
if err != nil {
t.Fatal(err)
}
return []any{hann, kaiser, ff, med, coef, back}
}},
{"stats-distributions2-multipletest", func(t *testing.T) []any {
// The second-league distributions and the corrections, on
// fixed parameters and the classic Benjamini-Hochberg vector.
wc, err := WeibullCDF(1.5, 2, 3)
if err != nil {
t.Fatal(err)
}
wq, err := WeibullQuantile(0.4, 2, 3)
if err != nil {
t.Fatal(err)
}
lc, err := LognormalCDF(1, 0, 0.5)
if err != nil {
t.Fatal(err)
}
pq, err := ParetoQuantile(0.5, 1, 3)
if err != nil {
t.Fatal(err)
}
nb, err := NegativeBinomialCDF(5, 3, 0.4)
if err != nil {
t.Fatal(err)
}
ncx, err := NoncentralChiSquareCDF(9, 4, 2.5)
if err != nil {
t.Fatal(err)
}
nct, err := NoncentralTCDF(2, 8, 3)
if err != nil {
t.Fatal(err)
}
ncf, err := NoncentralFQuantile(0.5, 4, 10, 2)
if err != nil {
t.Fatal(err)
}
draws, err := DirichletDraws(NewGenerator(54), 12, []float64{2, 3, 4})
if err != nil {
t.Fatal(err)
}
px := randA(t, 55, 100)
py := randA(t, 56, 100)
rho, err := SpearmanRho(px, py)
if err != nil {
t.Fatal(err)
}
tau, err := KendallTau(px, py)
if err != nil {
t.Fatal(err)
}
p := []float64{0.001, 0.008, 0.039, 0.041, 0.042, 0.06, 0.074, 0.205,
0.212, 0.216, 0.222, 0.251, 0.269, 0.275, 0.34, 0.341, 0.384,
0.456, 0.657, 0.876}
bonf, err := Bonferroni(p)
if err != nil {
t.Fatal(err)
}
holm, err := Holm(p)
if err != nil {
t.Fatal(err)
}
bh, err := BenjaminiHochberg(p)
if err != nil {
t.Fatal(err)
}
return []any{wc, wq, lc, pq, nb, ncx, nct, ncf, draws, rho, tau, bonf, holm, bh}
}},
{"stats-regression2", func(t *testing.T) []any {
// The regularised, robust and quantile fits on one seeded
// design, with one gross outlier the robust fit must survive.
g := NewGenerator(57)
xv, err := Floats(g, 60)
if err != nil {
t.Fatal(err)
}
n := xv.Len()
design := make([]float64, 0, 2*n)
y := make([]float64, n)
for i := range n {
design = append(design, 1, xv.FloatAt(i))
y[i] = 1 + 2*xv.FloatAt(i) + 0.3*(float64(i%7)-3)
}
y[7] += 100
dArr := mustA(t, design, n, 2)
yArr := mustA(t, y, n)
xOnly := make([]float64, n)
for i := range n {
xOnly[i] = xv.FloatAt(i)
}
xArr := mustA(t, xOnly, n, 1)
lasso, err := Lasso(xArr, yArr, 0.05)
if err != nil {
t.Fatal(err)
}
en, err := ElasticNet(xArr, yArr, 0.05, 0.5)
if err != nil {
t.Fatal(err)
}
huber, err := HuberRegression(dArr, yArr)
if err != nil {
t.Fatal(err)
}
si, sl, err := TheilSenRegression(xv, yArr)
if err != nil {
t.Fatal(err)
}
qr, err := QuantileRegression(dArr, yArr, 0.75)
if err != nil {
t.Fatal(err)
}
return []any{lasso.Intercept, lasso.Coefficients, lasso.Iterations,
en.Coefficients, huber.Coefficients, huber.Scale, huber.Iterations,
si, sl, qr.Coefficients, qr.Objective}
}},
{"stats-unsupervised", func(t *testing.T) []any {
// PCA over a seeded cloud, k-means over two blobs, the mixture
// over a univariate draw and the Gaussian-process posterior.
g := NewGenerator(58)
a1, err := Normal(g, 48, 0, 1)
if err != nil {
t.Fatal(err)
}
a2, err := Normal(g, 48, 0, 1)
if err != nil {
t.Fatal(err)
}
a3, err := Normal(g, 48, 0, 1)
if err != nil {
t.Fatal(err)
}
cloud := make([]float64, 144)
for i := range 48 {
cloud[3*i] = 3 * a1.FloatAt(i)
cloud[3*i+1] = a2.FloatAt(i)
cloud[3*i+2] = 0.1 * a3.FloatAt(i)
}
pca, err := PCA(mustA(t, cloud, 48, 3))
if err != nil {
t.Fatal(err)
}
bg := NewGenerator(59)
b1, err := Normal(bg, 40, 0, 1)
if err != nil {
t.Fatal(err)
}
b2, err := Normal(bg, 40, 0, 1)
if err != nil {
t.Fatal(err)
}
b3, err := Normal(bg, 40, 8, 1)
if err != nil {
t.Fatal(err)
}
b4, err := Normal(bg, 40, 8, 1)
if err != nil {
t.Fatal(err)
}
blobs := make([]float64, 160)
for i := range 40 {
blobs[2*i] = b1.FloatAt(i)
blobs[2*i+1] = b2.FloatAt(i)
blobs[40+2*i] = b3.FloatAt(i)
blobs[40+2*i+1] = b4.FloatAt(i)
}
km, err := KMeans(NewGenerator(60), mustA(t, blobs, 80, 2), 2)
if err != nil {
t.Fatal(err)
}
centres := make([]float64, 0, 4)
for _, c := range km.Centres {
centres = append(centres, c...)
}
mg := NewGenerator(61)
hi, err := Normal(mg, 40, 6, 1)
if err != nil {
t.Fatal(err)
}
lo, err := Normal(mg, 40, 0, 1)
if err != nil {
t.Fatal(err)
}
mix := make([]float64, 80)
for i := range 40 {
mix[2*i] = hi.FloatAt(i)
mix[2*i+1] = lo.FloatAt(i)
}
gmm, err := GaussianMixture(NewGenerator(62), mustA(t, mix, 80, 1), 2)
if err != nil {
t.Fatal(err)
}
means := make([]float64, 0, len(gmm.Means))
for _, m := range gmm.Means {
means = append(means, m...)
}
trainX := mustA(t, randA(t, 63, 16).RawFloats(), 16, 1)
sinVals, err := Sin(trainX)
if err != nil {
t.Fatal(err)
}
trainY, err := Reshape(sinVals, 16)
if err != nil {
t.Fatal(err)
}
testX := mustA(t, randA(t, 64, 8).RawFloats(), 8, 1)
kern, err := SquaredExponentialKernel(1)
if err != nil {
t.Fatal(err)
}
gp, err := GaussianProcessRegression(kern, trainX, trainY, 0.05, testX)
if err != nil {
t.Fatal(err)
}
mll, err := MarginalLogLikelihood(kern, trainX, trainY, 0.05)
if err != nil {
t.Fatal(err)
}
return []any{pca.Loadings, pca.ExplainedVarianceRatio, float64(km.Iterations),
centres, km.Inertia, gmm.Weights, means, gmm.BIC, gp.Mean, gp.Variance, mll}
}},
{"stats-contingency", func(t *testing.T) []any {
// The exact and the asymptotic table tests over fixed tables,
// every alternative, and the effect sizes beside them.
out := []any{}
tables := [][]float64{
{12, 5, 7, 10},
{8, 2, 1, 5},
{17, 0, 0, 23},
{21, 8, 3, 9, 15, 11, 4, 12, 30},
}
for ti, tab := range tables[:3] {
a, _, err := FisherExactTest(mustA(t, tab, 2, 2), TwoSided)
if err != nil {
t.Fatal(err)
}
less, _, err := FisherExactTest(mustA(t, tab, 2, 2), Less)
if err != nil {
t.Fatal(err)
}
greater, or, err := FisherExactTest(mustA(t, tab, 2, 2), Greater)
if err != nil {
t.Fatal(err)
}
out = append(out, a, less, greater, or)
p, err := McNemarTest(mustA(t, tab, 2, 2))
if err != nil {
t.Fatal(err)
}
out = append(out, p)
if ti < 2 {
v, err := CramersV(mustA(t, tab, 2, 2))
if err != nil {
t.Fatal(err)
}
out = append(out, v)
}
}
chi2, df, p, err := ChiSquareIndependence(mustA(t, tables[3], 3, 3))
if err != nil {
t.Fatal(err)
}
v, err := CramersV(mustA(t, tables[3], 3, 3))
if err != nil {
t.Fatal(err)
}
out = append(out, chi2, df, p, v)
return out
}},
{"stats-mixedmodel", func(t *testing.T) []any {
// A balanced random-intercept fit and a random-slope fit, over
// formula-driven data no generator touches.
const groups8 = 8
const per = 6
y := make([]float64, 0, groups8*per)
labels := make([]int, 0, groups8*per)
for g := range groups8 {
effect := 2 * math.Sin(1.7*float64(g)+0.4)
for i := range per {
y = append(y, 5+effect+wobble(g*per+i))
labels = append(labels, g)
}
}
ones := make([]float64, len(y))
for i := range ones {
ones[i] = 1
}
res, err := LinearMixedModel(mustA(t, y, len(y)), mustA(t, ones, len(y), 1),
mustA(t, ones, len(y), 1), labels)
if err != nil {
t.Fatal(err)
}
out := []any{res.Coefficients, res.StandardErrors,
res.RandomEffects[0], res.RandomEffects[7], res.RandomCovariance,
res.ResidualVariance, res.LogLikelihood, res.Converged,
fmt.Sprintf("%v", res.GroupLabels)}
// The slope fit: the random design carries the covariate
// alone, the identified configuration.
slopes := []float64{0.9, -1.1, 1.9, -0.3, -1.8, 0.4}
sy := make([]float64, 0, len(slopes)*per)
sx := make([]float64, 0, 2*len(slopes)*per)
sz := make([]float64, 0, len(slopes)*per)
slabels := make([]int, 0, len(slopes)*per)
jitter := make([]float64, 0, len(slopes)*per)
for g := range slopes {
for _, x := range []float64{-1, -0.6, -0.2, 0.2, 0.6, 1} {
jitter = append(jitter, wobble(len(jitter)+13)*0.1)
slabels = append(slabels, g)
sz = append(sz, x)
}
}
mean := 0.0
for _, j := range jitter {
mean += j
}
mean /= float64(len(jitter))
for g, s := range slopes {
for k := range per {
i := g*per + k
x := sz[i]
sy = append(sy, 1+2*x+0.5*s*x+0.1*(jitter[i]-mean))
sx = append(sx, 1, x)
}
}
slope, err := LinearMixedModel(mustA(t, sy, len(sy)), mustA(t, sx, len(sy), 2),
mustA(t, sz, len(sz), 1), slabels)
if err != nil {
t.Fatal(err)
}
return append(out, slope.Coefficients, slope.RandomCovariance,
slope.ResidualVariance, slope.Converged)
}},
{"stats-hmm", func(t *testing.T) []any {
// The scaled recursions, the decode and the Baum-Welch fit on
// a fixed sequence under a fixed model.
model, err := NewHiddenMarkovModel([]float64{0.6, 0.4},
[]float64{0.7, 0.3, 0.2, 0.8}, []float64{0.9, 0.1, 0.25, 0.75})
if err != nil {
t.Fatal(err)
}
observations := []int{0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0}
filtered, ll, err := model.Forward(observations)
if err != nil {
t.Fatal(err)
}
smoothed, sll, err := model.Smooth(observations)
if err != nil {
t.Fatal(err)
}
path, pathProb, err := model.Viterbi(observations)
if err != nil {
t.Fatal(err)
}
out := []any{filtered[len(observations)-1], smoothed[0], ll, sll,
fmt.Sprintf("%v", path), pathProb}
seq := make([]int, 240)
for i := range seq {
// The wiggle stays inside ±0.4, so the shifted scale is
// positive and the modulo lands inside the symbol set.
seq[i] = int(wobble(i)*100+200) % 3
}
fit, err := FitHiddenMarkovModel(NewGenerator(77), seq, 2, 3)
if err != nil {
t.Fatal(err)
}
return append(out, fit.Model.Initial, fit.Model.Transition, fit.Model.Emission,
fit.LogLikelihood, fit.Converged)
}},
{"stats-hierarchy", func(t *testing.T) []any {
// The five linkages over one fixed two-column sample, with the
// cuts the dendrogram answers.
sample := make([]float64, 24)
for i := range 12 {
sample[2*i] = wobble(2*i+5)*3 + float64(i%4)
sample[2*i+1] = wobble(2*i+31) * 2
}
out := []any{}
for _, method := range []Linkage{SingleLinkage, CompleteLinkage, AverageLinkage, CentroidLinkage, WardLinkage} {
d, err := HierarchicalClustering(mustA(t, sample, 12, 2), method)
if err != nil {
t.Fatal(err)
}
labels, err := d.Cut(3)
if err != nil {
t.Fatal(err)
}
out = append(out, d.Heights, fmt.Sprintf("%v", labels), d.Sizes[10])
}
first, err := HierarchicalClustering(mustA(t, sample, 12, 2), WardLinkage)
if err != nil {
t.Fatal(err)
}
byHeight, err := first.CutHeight(first.Heights[8])
if err != nil {
t.Fatal(err)
}
return append(out, fmt.Sprintf("%v", byHeight))
}},
{"integrate-stiff-solvers", func(t *testing.T) []any {
// The stiff batch on one decay problem, the index-1 DAE on its
// circuit, the collocation on a linear two-point problem, the
// symplectic pair and the advection pair.
decay := func(t float64, y *Array) (*Array, error) { return MulF(y, -1), nil }
y0 := mustA(t, []float64{1}, 1)
stats := &BDFVarStats{}
bdf, err := IntegrateBDFVar(decay, 0, 2, y0, BDFVarOptions{MaxSteps: 10000, Stats: stats})
if err != nil {
t.Fatal(err)
}
ros, err := IntegrateROS4(decay, 0, 2, y0, ODEOptions{MaxSteps: 10000})
if err != nil {
t.Fatal(err)
}
daeRHS := func(t float64, y *Array) (*Array, error) {
out, err := Zeros(Float, 2)
if err != nil {
return nil, err
}
out.SetFloatAt(0, 1-y.FloatAt(0))
out.SetFloatAt(1, y.FloatAt(1)-y.FloatAt(0))
return out, nil
}
mass, err := FromFloats([]float64{1, 0, 0, 0}, 2, 2)
if err != nil {
t.Fatal(err)
}
dae, err := IntegrateDAE(daeRHS, mass, 0, 1, mustA(t, []float64{0, 0}, 2), 50, DAEOptions{})
if err != nil {
t.Fatal(err)
}
colRHS := func(t float64, y *Array) (*Array, error) {
out, err := Zeros(Float, 2)
if err != nil {
return nil, err
}
out.SetFloatAt(0, y.FloatAt(1))
return out, nil
}
col, err := SolveBoundaryCollocation(colRHS, 0, 1, mustA(t, []float64{0, 0}, 2),
BoundaryConditions{Start: []int{0}, End: []int{0}, EndValues: []float64{1}},
CollocationOptions{})
if err != nil {
t.Fatal(err)
}
accel := func(q *Array) (*Array, error) { return MulF(q, -1), nil }
positions, momenta, err := IntegrateYoshida4(accel, 0, 6.283185307179586,
mustA(t, []float64{1}, 1), mustA(t, []float64{0}, 1), 200)
if err != nil {
t.Fatal(err)
}
gradH := func(z *Array) (*Array, error) {
out, err := Zeros(Float, 2)
if err != nil {
return nil, err
}
out.SetFloatAt(0, z.FloatAt(1))
out.SetFloatAt(1, z.FloatAt(0))
return out, nil
}
mids, _, err := IntegrateMidpoint(gradH, 0, 1, mustA(t, []float64{1}, 1),
mustA(t, []float64{1}, 1), 32, MidpointOptions{})
if err != nil {
t.Fatal(err)
}
n := 64
dx := 1.0 / 65
pulse := make([]float64, n)
for i := range n {
c := (float64(i) + 0.5) * dx
if c >= 0.3 && c <= 0.6 {
pulse[i] = 1
}
}
upwind, err := IntegrateUpwindAdvection1D(mustA(t, pulse, n), 1, dx, 0.2, 0.9*dx, 2, 0, 0)
if err != nil {
t.Fatal(err)
}
koren, err := IntegrateAdvection1D(mustA(t, pulse, n), 1, dx, 0.2, 0.9*dx, 2, 0, 0)
if err != nil {
t.Fatal(err)
}
return []any{bdf, float64(stats.MaxOrder), ros, dae,
col.Values[len(col.Values)-1], float64(len(col.Mesh)),
positions[len(positions)-1], momenta[len(momenta)-1], mids[len(mids)-1],
upwind, koren}
}},
{"integrate-fem3d", func(t *testing.T) []any {
// The tetrahedral Poisson assembly on the unit box with the
// manufactured sin(πx)·sin(πy)·sin(πz) solution.
mesh, err := BoxTetraMesh3D(0, 0, 0, 1, 1, 1, 4, 4, 4)
if err != nil {
t.Fatal(err)
}
src := func(x, y, z float64) float64 {
return 3 * math.Pi * math.Pi * math.Sin(math.Pi*x) * math.Sin(math.Pi*y) * math.Sin(math.Pi*z)
}
var bound []int
var vals []float64
for i := range mesh.Vertices3() {
x, y, z := mesh.Vertices[3*i], mesh.Vertices[3*i+1], mesh.Vertices[3*i+2]
if x == 0 || x == 1 || y == 0 || y == 1 || z == 0 || z == 1 {
bound = append(bound, i)
vals = append(vals, 0)
}
}
u, err := SolvePoissonFEM3D(mesh, src, FEMPoisson3DOptions{
Kappa: 1, DirichletNodes: bound, DirichletValues: vals,
Ordering: SparseOrderingReverseCuthillMcKee})
if err != nil {
t.Fatal(err)
}
return []any{u.FloatAt(u.Len() / 2), u.FloatAt(u.Len() - 1), u.FloatAt(7)}
}},
{"linalg-sparse-lsqr-rrqr-update", func(t *testing.T) []any {
// Sparse least squares against a consistent right-hand side,
// the pivoted QR on a seeded matrix and the sparse rank-one
// round trip on a banded factor.
g := NewGenerator(65)
const m, n = 12, 8
var idx []int64
var vals []float64
for i := range m {
idx = append(idx, int64(i), int64(i%(n)))
vals = append(vals, 2+g.Unit())
if i+1 < m && i%(n)+1 < n {
idx = append(idx, int64(i), int64(i%n+1))
vals = append(vals, -0.5-0.5*g.Unit())
}
}
indices, err := FromInts(idx, len(vals), 2)
if err != nil {
t.Fatal(err)
}
valArr, err := FloatsFromArray(vals, len(vals))
if err != nil {
t.Fatal(err)
}
coo, err := NewSparseCOO(indices, valArr, []int{m, n})
if err != nil {
t.Fatal(err)
}
csr, err := CSRFromCOO(coo)
if err != nil {
t.Fatal(err)
}
xTrue := New(Float, n)
for i := range n {
xTrue.RawFloats()[i] = float64(i%5) - 2 + 0.25*float64(i%3)
}
b, err := csr.MatVec(xTrue)
if err != nil {
t.Fatal(err)
}
xl, infoL, err := SpLSQR(coo, b, 1e-12, 300, 0)
if err != nil {
t.Fatal(err)
}
xm, infoM, err := SpLSMR(coo, b, 1e-12, 300, 0)
if err != nil {
t.Fatal(err)
}
dm := mustA(t, randA(t, 66, 60).RawFloats(), 10, 6)
q, r, perm, rank, err := RRQR(dm)
if err != nil {
t.Fatal(err)
}
permF := make([]float64, len(perm))
for i, p := range perm {
permF[i] = float64(p)
}
b6 := randA(t, 67, 10)
xr, err := SolveRRQR(dm, b6)
if err != nil {
t.Fatal(err)
}
const bn = 30
var bIdx []int64
var bVals []float64
for i := range bn {
bIdx = append(bIdx, int64(i), int64(i))
bVals = append(bVals, 4)
if i+1 < bn {
bIdx = append(bIdx, int64(i), int64(i+1))
bVals = append(bVals, -1)
bIdx = append(bIdx, int64(i+1), int64(i))
bVals = append(bVals, -1)
}
}
bIndices, err := FromInts(bIdx, len(bVals), 2)
if err != nil {
t.Fatal(err)
}
bValArr, err := FloatsFromArray(bVals, len(bVals))
if err != nil {
t.Fatal(err)
}
bCoo, err := NewSparseCOO(bIndices, bValArr, []int{bn, bn})
if err != nil {
t.Fatal(err)
}
factor, err := NewSparseCholesky(bCoo, SparseOrderingNatural)
if err != nil {
t.Fatal(err)
}
bv := New(Float, bn)
for i := range bn {
bv.RawFloats()[i] = float64(i%7) - 3
}
before, err := factor.Solve(bv)
if err != nil {
t.Fatal(err)
}
upd := New(Float, bn)
upd.RawFloats()[7] = 2
if err := factor.Update(upd); err != nil {
t.Fatal(err)
}
after, err := factor.Solve(bv)
if err != nil {
t.Fatal(err)
}
if err := factor.Downdate(upd); err != nil {
t.Fatal(err)
}
restored, err := factor.Solve(bv)
if err != nil {
t.Fatal(err)
}
return []any{xl, infoL.Criterion, infoL.ResidualNorm, float64(infoL.Iterations),
xm, infoM.Criterion, q, r, permF, float64(rank), xr,
before, after, restored}
}},
{"optim-lp-qp-global2", func(t *testing.T) []any {
// The simplex through the two-sided rows, the active-set QP on
// an active wall, the seeded global pair on one rotated bowl,
// the nonlinear equality and the scalar bracket.
a := mustA(t, []float64{1, 1, 1, 0}, 2, 2)
xLP, vLP, err := MinimiseLinearRows(mustA(t, []float64{1, 1}, 2),
LinearConstraints{A: a, Lower: []float64{2, math.Inf(-1)}, Upper: []float64{math.Inf(1), 3}},
LinearProgramOptions{})
if err != nil {
t.Fatal(err)
}
h := mustA(t, []float64{2, 0, 0, 2}, 2, 2)
xQP, vQP, multQP, err := MinimiseQP(h, mustA(t, []float64{-2, -4}, 2),
LinearConstraints{A: mustA(t, []float64{1, 0}, 1, 2),
Lower: []float64{math.Inf(-1)}, Upper: []float64{1}},
nil, QPOptions{})
if err != nil {
t.Fatal(err)
}
cmaF := func(p *Array) (float64, error) {
var s float64
w := []float64{4, 3, 2, 1}
for i := range 4 {
di := p.FloatAt(i) - float64(i+1)
s += w[i] * di * di
if i+1 < 4 {
s += 0.5 * di * (p.FloatAt(i+1) - float64(i+2))
}
}
return s, nil
}
xCMA, vCMA, err := MinimiseCMAES(cmaF, mustA(t, []float64{2, -2, 1, -1}, 4),
CMAESOptions{Sigma0: 0.5, Generations: 300, Tolerance: 1e-10, Seed: 7})
if err != nil {
t.Fatal(err)
}
xSA, vSA, err := MinimiseSimulatedAnnealing(cmaF, mustA(t, []float64{2, -2, 1, -1}, 4),
SimulatedAnnealingOptions{Steps: 20000, Tolerance: 1e-6, Seed: 42, AllowBudgetExit: true})
if err != nil {
t.Fatal(err)
}
xNL, vNL, multNL, err := MinimiseNonlinearConstrained(
func(p *Array) (float64, error) { return p.FloatAt(0), nil },
nil, mustA(t, []float64{0.5, 0.5}, 2),
NonlinearConstraints{Equalities: []func(*Array) (float64, error){
func(p *Array) (float64, error) {
x, y := p.FloatAt(0), p.FloatAt(1)
return x*x + y*y - 1, nil
},
}}, LBFGSOptions{})
if err != nil {
t.Fatal(err)
}
root, err := FindRootBrent(func(x float64) float64 { return math.Cos(x) - x }, -10, 10, BrentOptions{})
if err != nil {
t.Fatal(err)
}
broydenRHS := func(x *Array) (*Array, error) {
out, err := Zeros(Float, 2)
if err != nil {
return nil, err
}
xf, yf := x.FloatAt(0), x.FloatAt(1)
out.SetFloatAt(0, xf*xf+yf*yf-1)
out.SetFloatAt(1, xf-yf)
return out, nil
}
xBr, resBr, err := FindRootSystem(broydenRHS, mustA(t, []float64{2, 0.5}, 2),
RootSystemOptions{UseBroyden: true})
if err != nil {
t.Fatal(err)
}
return []any{xLP, vLP, xQP, vQP, multQP, xCMA, vCMA, xSA, vSA,
xNL, vNL, multNL, root, xBr, resBr}
}},
{"io-hdf5-write", func(t *testing.T) []any {
// The writer round trip: chunked, shuffled, deflated floats, a
// float32 matrix, wide integers, a nested group and attributes,
// read back through the unchanged reader in both superblocks.
dir := t.TempDir()
f64 := randA(t, 68, 32)
f32, err := FromFloat32s([]float32{1.5, -2.5, 3.25, 4, 5, 6}, 2, 3)
if err != nil {
t.Fatal(err)
}
wide, err := FromInts([]int64{5, -3, 1 << 40}, 3)
if err != nil {
t.Fatal(err)
}
nested, err := FromFloats([]float64{1, 2, 3, 4}, 2, 2)
if err != nil {
t.Fatal(err)
}
sets := []HDF5Dataset{
{Path: "/signals/main", Shape: f64.Shape(), Values: f64, Attrs: map[string]string{"units": "m"}},
{Path: "/image", Shape: f32.Shape(), Values: f32},
{Path: "/wide", Shape: wide.Shape(), Values: wide},
{Path: "/g/sub/deep", Shape: nested.Shape(), Values: nested},
}
attrs := map[string]map[string]string{"/": {"title": "oracle"}, "/g": {"note": "middle"}}
classic := filepath.Join(dir, "classic.h5")
if err := SaveHDF5(classic, sets, attrs, HDF5WriteOptions{Gzip: 6, Shuffle: true}); err != nil {
t.Fatal(err)
}
back, err := LoadHDF5(classic)
if err != nil {
t.Fatal(err)
}
out := []any{}
for _, d := range back {
out = append(out, d.Path, d.Values)
for _, k := range slices.Sorted(maps.Keys(d.Attrs)) {
out = append(out, k, d.Attrs[k])
}
}
latest := filepath.Join(dir, "latest.h5")
if err := SaveHDF5(latest, sets, attrs, HDF5WriteOptions{Latest: true}); err != nil {
t.Fatal(err)
}
backLatest, err := LoadHDF5(latest)
if err != nil {
t.Fatal(err)
}
out = append(out, len(backLatest) == len(back))
for _, d := range backLatest {
if d.Path == "/signals/main" {
out = append(out, d.Values)
}
}
return out
}},
{"io-hdf5-narrow-write-roundtrip", func(t *testing.T) []any {
// The seven narrow dtypes through the writer and back, the
// values at each dtype's extremes: bool through the boolean
// enumeration convention, every integer at its stored width and
// signedness. The same sets are written contiguous, in the
// latest layout and chunked with shuffle and deflate, and every
// read-back must land the written dtype and values.
dir := t.TempDir()
sets := []HDF5Dataset{}
must := func(values *Array, err error) *Array {
if err != nil {
t.Fatal(err)
}
return values
}
put := func(path string, values *Array) {
sets = append(sets, HDF5Dataset{Path: path, Shape: values.Shape(), Values: values})
}
put("/bool", must(FromBools([]bool{false, true, true, false}, 2, 2)))
put("/i8", must(FromInt8s([]int8{-128, 127, 0, -1}, 2, 2)))
put("/u8", must(FromUint8s([]uint8{0, 255, 7, 128}, 2, 2)))
put("/i16", must(FromInt16s([]int16{-32768, 32767, 0, -2}, 2, 2)))
put("/u16", must(FromUint16s([]uint16{0, 65535, 5, 32768}, 2, 2)))
put("/i32", must(FromInt32s([]int32{-2147483648, 2147483647, 0, -3}, 2, 2)))
put("/u32", must(FromUint32s([]uint32{0, 4294967295, 9, 2147483648}, 2, 2)))
out := []any{}
for _, c := range []struct {
file string
opts HDF5WriteOptions
}{
{"contiguous.h5", HDF5WriteOptions{}},
{"latest.h5", HDF5WriteOptions{Latest: true}},
{"chunked.h5", HDF5WriteOptions{Gzip: 1, Shuffle: true, ChunkBytes: 8}},
} {
path := filepath.Join(dir, c.file)
if err := SaveHDF5(path, sets, nil, c.opts); err != nil {
t.Fatal(err)
}
back, err := LoadHDF5(path)
if err != nil {
t.Fatal(err)
}
out = append(out, c.file)
for _, d := range back {
out = append(out, d.Path, d.Values)
}
}
return out
}},
{"io-hdf5-uint8-roundtrip", func(t *testing.T) []any {
// A uint8 dataset through the HDF5 read path: the fixture pins
// the reader's decode directly, hand-built in the classic
// layout, and must land uint8 on the values below. The writer
// path is covered by io-hdf5-narrow-write-roundtrip above,
// which writes every narrow dtype and reads it back.
path := filepath.Join(t.TempDir(), "u8.h5")
file := oracleHDF5Narrow(oracleHDF5Fixed(1, false), []uint64{5},
[]byte{0, 1, 200, 255, 42})
if err := os.WriteFile(path, file, 0o644); err != nil {
t.Fatal(err)
}
sets, err := LoadHDF5(path)
if err != nil {
t.Fatal(err)
}
return []any{len(sets), sets[0].Path, sets[0].Values}
}},
{"io-hdf5-int16-roundtrip", func(t *testing.T) []any {
// An int16 dataset through the same hand-built fixture path:
// the read-back must land int16 on the extremes below.
path := filepath.Join(t.TempDir(), "i16.h5")
file := oracleHDF5Narrow(oracleHDF5Fixed(2, true), []uint64{4},
[]byte{0xfd, 0xff, 0x00, 0x80, 0xff, 0x7f, 0x07, 0x00})
if err := os.WriteFile(path, file, 0o644); err != nil {
t.Fatal(err)
}
sets, err := LoadHDF5(path)
if err != nil {
t.Fatal(err)
}
return []any{len(sets), sets[0].Path, sets[0].Values}
}},
{"io-hdf5-bool-roundtrip", func(t *testing.T) []any {
// A boolean enumeration dataset, the convention HDF5 writers
// carry booleans in: the read-back must land core.Bool.
path := filepath.Join(t.TempDir(), "bool.h5")
file := oracleHDF5Narrow(oracleHDF5EnumBool(), []uint64{3}, []byte{1, 0, 1})
if err := os.WriteFile(path, file, 0o644); err != nil {
t.Fatal(err)
}
sets, err := LoadHDF5(path)
if err != nil {
t.Fatal(err)
}
return []any{len(sets), sets[0].Path, sets[0].Values}
}},
{"io-netcdf-classic-natives", func(t *testing.T) []any {
// A NetCDF classic file carrying byte, short, int, char, float
// and double values, read natively. The NetCDF writer stores
// float64, float32 and int64 only, so the fixture is written
// here as classic-format bytes and the oracle pins the landings.
path := filepath.Join(t.TempDir(), "natives.nc")
if err := os.WriteFile(path, oracleNetCDFNatives(), 0o644); err != nil {
t.Fatal(err)
}
_, vars, _, err := LoadNetCDF(path)
if err != nil {
t.Fatal(err)
}
out := []any{len(vars)}
for _, v := range vars {
out = append(out, v.Name, v.Values)
}
return out
}},
{"optim-lm-fit", func(t *testing.T) []any {
// The Levenberg-Marquardt fit through the Fit surface: an
// exact exponential decay with a requested covariance, then a
// diagonal-Sigma run whose first observation carries a quarter
// of the variance, so the whitening moves the answer. The
// digest pins the parameters, both chi2 values, the statuses
// and the covariance.
xs := []float64{0, 1, 2, 3, 4, 5, 6, 7}
ys := make([]float64, len(xs))
for i, x := range xs {
ys[i] = 2.5*math.Exp(-0.7*float64(x)) + 0.3
}
residual := func(p *Array) (*Array, error) {
out := New(Float, len(xs))
for i := range len(xs) {
out.RawFloats()[i] = ys[i] - (p.FloatAt(0)*math.Exp(-p.FloatAt(1)*float64(xs[i])) + p.FloatAt(2))
}
return out, nil
}
p0, err := FromFloats([]float64{2, 0.5, 0.1}, 3)
if err != nil {
t.Fatal(err)
}
res, err := LevenbergMarquardtFit(residual, p0, LMOptions{RequestCovariance: true})
if err != nil {
t.Fatal(err)
}
if res.Status != FitConverged {
t.Fatalf("status %d, want FitConverged", res.Status)
}
out := []any{res.Parameters, res.Chi2, int(res.Status), res.Covariance}
sigma, err := FromFloats([]float64{0.25, 1, 1, 1, 1, 1, 1, 1}, 8)
if err != nil {
t.Fatal(err)
}
wres, err := LevenbergMarquardtFit(residual, p0, LMOptions{Sigma: sigma})
if err != nil {
t.Fatal(err)
}
if wres.Status != FitConverged {
t.Fatalf("weighted status %d, want FitConverged", wres.Status)
}
return append(out, wres.Parameters, wres.Chi2, int(wres.Status))
}},
{"core-cosm1", func(t *testing.T) []any {
// The small-argument cosine deficit across its regimes: the
// digest pins the factored series from the underflowed end to
// the crossover and the direct subtraction above it.
a, err := FromFloats([]float64{
1e-300, 1e-12, 1e-9, 1e-8, 1e-4, 0.01, 0.5,
math.Pi / 4, 0.79, 1, 2, -0.3, -2.5, 1e6,
}, 14)
if err != nil {
t.Fatal(err)
}
v, err := Cosm1(a)
if err != nil {
t.Fatal(err)
}
return []any{v}
}},
{"core-substream", func(t *testing.T) []any {
// The substream family and the scalar splitmix64 chain: the
// digest pins the first draws of three members of one seed's
// family beside the chain of states the scalar surface hands
// out, so a change to the seeding moves loudly.
out := []any{}
for _, i := range []int{0, 1, 7} {
g, err := Substream(42, i)
if err != nil {
t.Fatal(err)
}
a, err := Floats(g, 8)
if err != nil {
t.Fatal(err)
}
out = append(out, a)
}
state, v := Splitmix64(0)
for range 3 {
out = append(out, fmt.Sprintf("%#016x", state), fmt.Sprintf("%#016x", v))
state, v = Splitmix64(state)
}
return out
}},
{"signal-chirp", func(t *testing.T) []any {
// The chirp synthesiser: the digest pins a rising sweep, a
// falling one and the constant tone that must be the plain
// sine.
rise, err := Chirp(64, 5, 60, 128)
if err != nil {
t.Fatal(err)
}
fall, err := Chirp(16, 900, 100, 8000)
if err != nil {
t.Fatal(err)
}
tone, err := Chirp(32, 11, 11, 256)
if err != nil {
t.Fatal(err)
}
return []any{rise, fall, tone}
}},
{"core-besseljreal", func(t *testing.T) []any {
// The real-order Bessel J across its regimes: the series at a
// small argument, the climb seeded from the expansion, the
// fractional Miller walk past the argument, and the integer
// delegation, pinned beside the points themselves.
type point struct {
nu, x float64
}
pts := []point{
{0.5, 2}, {2.5, 0.05}, {1.5, 12.1}, {2.3, 15},
{7.7, 40}, {20.5, 15}, {1.7, 1000}, {3, 20},
}
out := []any{len(pts)}
for _, p := range pts {
v, err := BesselJRealOrder(p.nu, p.x)
if err != nil {
t.Fatal(err)
}
out = append(out, v)
}
return out
}},
{"integrate-filon", func(t *testing.T) []any {
// The Filon quadrature across its regimes: polynomial
// amplitudes, where the construction is exact whatever the
// carrier does, an exponential amplitude under a carrier of
// five hundred, and the zero-frequency degeneration beside
// them.
poly := func(x float64) (float64, error) { return x, nil }
c1, s1, err := IntegrateFilon(poly, 2, 7, 500, FilonOptions{})
if err != nil {
t.Fatal(err)
}
exp := func(x float64) (float64, error) { return math.Exp(0.5 * x), nil }
c2, s2, err := IntegrateFilon(exp, 2, 7, 500, FilonOptions{})
if err != nil {
t.Fatal(err)
}
c3, s3, err := IntegrateFilon(exp, 0, 3, 0, FilonOptions{})
if err != nil {
t.Fatal(err)
}
return []any{c1, s1, c2, s2, c3, s3}
}},
{"core-narrow", func(t *testing.T) []any {
// The narrow integer dtypes end to end: every width over the
// corners of its own range, promotion across widths and across
// the class boundary, the exact int64 folds, the bool masks the
// comparisons answer and the selection and logic they feed.
i8, err := Int8sFromArray([]int8{-128, -1, 0, 1, 127, -128, 127, 0, 3, -7}, 10)
if err != nil {
t.Fatal(err)
}
u8, err := Uint8sFromArray([]uint8{0, 1, 127, 128, 255, 254, 3, 0, 9, 200}, 10)
if err != nil {
t.Fatal(err)
}
i16, err := Int16sFromArray([]int16{-32768, -1, 0, 1, 32767, -32768, 32767, 0, 500, -900}, 10)
if err != nil {
t.Fatal(err)
}
u16, err := Uint16sFromArray([]uint16{0, 1, 32767, 32768, 65535, 65534, 7, 0, 40, 60000}, 10)
if err != nil {
t.Fatal(err)
}
i32, err := Int32sFromArray([]int32{-2147483648, -1, 0, 1, 2147483647, -2147483648, 2147483647, 0, 70000, -300}, 10)
if err != nil {
t.Fatal(err)
}
u32, err := Uint32sFromArray([]uint32{0, 1, 2147483647, 2147483648, 4294967295, 4294967294, 11, 0, 5, 4000000000}, 10)
if err != nil {
t.Fatal(err)
}
cross1, err := Add(i8, u16)
if err != nil {
t.Fatal(err)
}
cross2, err := Sub(i32, u8)
if err != nil {
t.Fatal(err)
}
cross3, err := Mul(i16, u8)
if err != nil {
t.Fatal(err)
}
cross4, err := Add(u32, i16)
if err != nil {
t.Fatal(err)
}
ltMask, err := Lt(i8, u16)
if err != nil {
t.Fatal(err)
}
eqMask, err := EqI(u8, 1)
if err != nil {
t.Fatal(err)
}
leMask, err := LeI(i32, -1)
if err != nil {
t.Fatal(err)
}
geMask, err := GeF(i8, 0.5)
if err != nil {
t.Fatal(err)
}
ltfMask, err := LtF(i16, -0.5)
if err != nil {
t.Fatal(err)
}
joined, err := And(ltMask, eqMask)
if err != nil {
t.Fatal(err)
}
flipped, err := Not(eqMask)
if err != nil {
t.Fatal(err)
}
picked, err := Select(i8, joined)
if err != nil {
t.Fatal(err)
}
chosen, err := Where(eqMask, u8, i8)
if err != nil {
t.Fatal(err)
}
widened, err := Astype(u32, Float)
if err != nil {
t.Fatal(err)
}
wide, err := FromInts([]int64{2147483648, -2147483649, -1}, 3)
if err != nil {
t.Fatal(err)
}
_, narrowErr := Astype(wide, Int32)
if narrowErr == nil {
t.Fatal("Astype narrowed 2^31 into int32")
}
inRange, err := FromInts([]int64{2147483647, -2147483648, -1}, 3)
if err != nil {
t.Fatal(err)
}
narrowed, err := Astype(inRange, Int32)
if err != nil {
t.Fatal(err)
}
count, err := CountNonzero(leMask)
if err != nil {
t.Fatal(err)
}
minU8, err := Min(u8)
if err != nil {
t.Fatal(err)
}
maxI32, err := Max(i32)
if err != nil {
t.Fatal(err)
}
return []any{i8, u8, i16, u16, i32, u32,
cross1, cross2, cross3, cross4,
Sum(i8), Sum(i16), Sum(u32), minU8, maxI32,
ltMask, eqMask, leMask, geMask, ltfMask, joined, flipped,
picked, chosen, widened, narrowed, narrowErr != nil, count}
}},
}
// hostileNetCDF builds a CDF-1 file whose single variable spans three
// dimensions of 2^21, 2^20 and 2^20 elements: their product times the
// 8-byte element width wraps to zero in 64-bit arithmetic, so a reader
// that multiplies before it bounds would allocate what the header
// claims. The file is 112 bytes.
func hostileOracleHeader() []byte {
u32 := func(v uint32) []byte {
var b [4]byte
binary.BigEndian.PutUint32(b[:], v)
return b[:]
}
name := func(s string) []byte {
b := u32(uint32(len(s)))
b = append(b, s...)
for len(s)%4 != 0 {
b = append(b, 0)
s += " "
}
return b
}
var b []byte
b = append(b, 'C', 'D', 'F', 1)
b = append(b, u32(0)...) // numrecs
b = append(b, u32(10)...) // NC_DIMENSION
b = append(b, u32(3)...)
for i, l := range []uint32{1 << 21, 1 << 20, 1 << 20} {
b = append(b, name(string(rune('a'+i)))...)
b = append(b, u32(l)...)
}
b = append(b, u32(0)...) // absent attribute list
b = append(b, u32(0)...)
b = append(b, u32(11)...) // NC_VARIABLE
b = append(b, u32(1)...)
b = append(b, name("v")...)
b = append(b, u32(3)...)
b = append(b, u32(0)...)
b = append(b, u32(1)...)
b = append(b, u32(2)...)
b = append(b, u32(0)...) // absent variable attributes
b = append(b, u32(0)...)
b = append(b, u32(6)...) // NC_DOUBLE
b = append(b, u32(0)...) // vsize
b = append(b, u32(0)...) // begin
return b
}
// hostileFITSTable builds a BINTABLE header with a zero-width TFORM and
// no data block: the row arithmetic must not be able to divide by the
// row size, and the truncated file must be refused.
func hostileOracleTable() []byte {
var b []byte
for _, body := range []string{
"XTENSION= 'BINTABLE'", "BITPIX = 8", "NAXIS = 2",
"NAXIS1 = 4", "NAXIS2 = 2", "TFIELDS = 1",
"TFORM1 = 'X '", "END",
} {
b = append(b, []byte(body)...)
for len(b)%80 != 0 {
b = append(b, ' ')
}
}
return b
}
// oracleHDF5Fixed renders a version 1 fixed-point datatype message of
// the given element width and signedness, with the bit offset and bit
// precision the HDF5 file format specification's fixed-point property
// table defines behind the header.
func oracleHDF5Fixed(size uint32, signed bool) []byte {
m := []byte{0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // version 1, class 0
if signed {
m[1] = 0x08 // class bit field: bit 3 marks two's complement
}
binary.LittleEndian.PutUint32(m[4:], size)
binary.LittleEndian.PutUint16(m[10:], uint16(8*size)) // bit precision
return m
}
// oracleHDF5EnumBool renders the boolean enumeration datatype message:
// class 8 over a one-byte unsigned base, the member names padded in
// their own fields to multiples of eight bytes, the member values
// packed behind them, exactly as the HDF5 file format specification's
// enumeration class defines.
func oracleHDF5EnumBool() []byte {
m := []byte{0x18, 2, 0, 0, 1, 0, 0, 0} // class 8, two members, size 1
m = append(m, oracleHDF5Fixed(1, false)...) // the base type
for _, name := range []string{"TRUE", "FALSE"} {
start := len(m)
m = append(m, name...)
m = append(m, 0)
for (len(m)-start)%8 != 0 {
m = append(m, 0)
}
}
return append(m, 1, 0) // TRUE = 1, FALSE = 0
}
// oracleHDF5Narrow builds a minimal classic-layout HDF5 file carrying
// one dataset: a version 0 superblock of eight-byte addresses and
// lengths, a version 1 object header with the given datatype message,
// contiguous storage and the payload bytes. The io tests build their
// hostile fixtures the same way; these bytes are fixed, so the digest
// of what the reader lands is stable across runs.
func oracleHDF5Narrow(dtypeMsg []byte, dims []uint64, payload []byte) []byte {
const headerAt, dataAt = 96, 448
align8 := func(n int) int { return (n + 7) / 8 * 8 }
n := dataAt + len(payload)
f := make([]byte, n)
copy(f, []byte{0x89, 'H', 'D', 'F', '\r', '\n', 0x1a, '\n'})
f[8] = 0 // superblock version 0, the classic layout
f[13], f[14] = 8, 8 // address and length sizes
put := func(at int, v uint64) { binary.LittleEndian.PutUint64(f[at:], v) }
put(32, math.MaxUint64) // free space undefined
put(40, uint64(n)) // end of file
put(48, math.MaxUint64) // driver information undefined
put(64, headerAt) // the root object header
space := make([]byte, 8+8*len(dims))
space[0] = 1 // dataspace version 1
space[1] = byte(len(dims))
for i, d := range dims {
binary.LittleEndian.PutUint64(space[8+8*i:], d)
}
lay := make([]byte, 18)
lay[0], lay[1] = 3, 1 // layout version 3, contiguous
binary.LittleEndian.PutUint64(lay[2:], dataAt)
binary.LittleEndian.PutUint64(lay[10:], uint64(len(payload)))
msgs := []struct {
typ uint16
body []byte
}{
{1, space}, // dataspace
{3, dtypeMsg},
{8, lay}, // data layout
}
off := headerAt
f[off] = 1 // object header version 1
binary.LittleEndian.PutUint16(f[off+2:], uint16(len(msgs)))
binary.LittleEndian.PutUint32(f[off+4:], 1) // reference count
region := off + 16
size := 0
for _, m := range msgs {
size = align8(size + 8 + len(m.body))
}
binary.LittleEndian.PutUint32(f[off+8:], uint32(size))
for _, m := range msgs {
binary.LittleEndian.PutUint16(f[region:], m.typ)
binary.LittleEndian.PutUint16(f[region+2:], uint16(len(m.body)))
copy(f[region+8:], m.body)
region += align8(8 + len(m.body))
}
copy(f[dataAt:], payload)
return f
}
// oracleNetCDFNatives builds a CDF-1 file by hand carrying one
// variable per classic type code: NC_BYTE, NC_SHORT, NC_INT, NC_CHAR,
// NC_FLOAT and NC_DOUBLE over one shared four-element dimension, the
// integers at the extremes of every width and the floats at
// recognisable values. The NetCDF writer stores float64, float32 and
// int64 only, so the fixture bytes live here exactly as the foreign
// fixtures the io package carries.
func oracleNetCDFNatives() []byte {
u32 := func(v uint32) []byte {
var b [4]byte
binary.BigEndian.PutUint32(b[:], v)
return b[:]
}
name := func(s string) []byte {
b := u32(uint32(len(s)))
b = append(b, s...)
for len(b)%4 != 0 {
b = append(b, 0)
}
return b
}
type classicVar struct {
name string
code uint32
data []byte
}
vars := []classicVar{
{"b", 1, []byte{0x80, 0xff, 0x00, 0x7f}}, // NC_BYTE: -128, -1, 0, 127
{"s", 3, []byte{0x80, 0x00, 0xff, 0xff, 0x00, 0x01, 0x7f, 0xff}}, // NC_SHORT extremes
{"i", 4, []byte{0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 1, 0x7f, 0xff, 0xff, 0xff}}, // NC_INT
{"c", 2, []byte{0x00, 0x41, 0xc8, 0xff}}, // NC_CHAR: raw bytes
{"f", 5, []byte{ // NC_FLOAT: 1.5, -2.5, 0, 3.25
0x3f, 0xc0, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x50, 0x00, 0x00}},
{"d", 6, []byte{ // NC_DOUBLE: 1.5, -2.5, 0, 4
0x3f, 0xf8, 0, 0, 0, 0, 0, 0, 0xc0, 0x04, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x10, 0, 0, 0, 0, 0, 0}},
}
var b []byte
b = append(b, 'C', 'D', 'F', 1)
b = append(b, u32(0)...) // numrecs
b = append(b, u32(10)...) // NC_DIMENSION
b = append(b, u32(1)...) // one dimension
b = append(b, name("n")...)
b = append(b, u32(4)...) // length 4
b = append(b, u32(0)...) // gatt_list ABSENT
b = append(b, u32(0)...) // gatt_list ABSENT
b = append(b, u32(11)...) // NC_VARIABLE
b = append(b, u32(uint32(len(vars)))...) // six variables
// The header ends where the first payload begins: one 36-byte
// variable record per variable behind the fixed prefix.
begin := len(b) + len(vars)*36
off := begin
for _, v := range vars {
b = append(b, name(v.name)...)
b = append(b, u32(1)...) // rank 1
b = append(b, u32(0)...) // dimid 0
b = append(b, u32(0)...) // variable attributes ABSENT
b = append(b, u32(0)...) // variable attributes ABSENT
b = append(b, u32(v.code)...)
b = append(b, u32(uint32(len(v.data)))...) // vsize
b = append(b, u32(uint32(off))...)
off += len(v.data)
}
for _, v := range vars {
b = append(b, v.data...)
}
return b
}
// pinnedOracleDigests is the recorded behaviour of the harness, keyed
// by runtime.GOOS/GOARCH plus the code generation the binary was built
// with: floating-point kernels may differ between architectures (FMA
// availability, libm corners), and GOAMD64 decides whether the compiler
// may contract a multiply and an add into one fused operation, which
// changes the last bits without changing the algorithm. A deliberate
// algorithm change re-records the affected entries on the configuration
// it was made on; an accidental one fails the test there. A build whose
// combination has no block skips loudly until it is recorded, so
// determinism is measured per configuration, never claimed in general.
//
// The portable build is the product and the only build, and its
// digests are recorded at the toolchain default level (v1): the
// compiler has no auto-vectoriser, so a pinned higher level would buy
// only scalar FMA contraction, which the bit-pinned kernels already
// suppress by spelling. A build pinned to another GOAMD64 level has no
// block and skips with the recording instructions rather than failing.
//
// A re-recorded digest is sometimes the point itself: an old digest
// can have pinned a bug, and three entries exist because their
// predecessors did. signal-welch-savgol's magnitude goes through
// math.Hypot, so it no longer rounds through a squared sum.
// optim-roots-minima and optim-constrained-minima carry the L-BFGS
// line search that no longer returns its start point when a stiff
// objective rejects every trial step, the Nelder-Mead that no longer
// stops on a simplex lying on one level set, and MinimiseConstrained
// returning f at the answer instead of the augmented Lagrangian value.
var pinnedOracleDigests = map[string]map[string]string{
"linux/amd64+v1": {
"spmd-shards": "0bec5217a9a79b182241ac0937f32e9d20c89a5b392fc797ab3787a03fc8c9ed",
"io-hdf5-narrow-write-roundtrip": "f2e87eaadcbab846e4acf5473b3023faca3015cd3b3f0919c4c3792114589ee9",
"io-hdf5-uint8-roundtrip": "54550518525945c92d970a38d2d81a5005fb8a899b597819c6060610ae77dd54",
"io-hdf5-int16-roundtrip": "b4163c6e9ed7a04ddb4a2328d394afe074237533738d19aa5ecaa68bff9e2685",
"io-hdf5-bool-roundtrip": "fa56fd9a421d2e78f17711e717f851c4b19dc1f6946d5c4e1dddad85cc42d347",
"io-netcdf-classic-natives": "c0219b60aa963176ce509f5633c22b96bc070529db1035d53a643b67d614d01b",
"core-elementwise": "0812ac01c62b084326d0e590941a46327136e9022a101fad68998781b68f7014",
"core-matmul-einsum": "a62b07d724f06603fe4c5ebf3d448b61d7638b93d1b58df605d94096d30ba4b4",
"core-sort-argsort": "24fc532b4140bc6633b5fabb8a19c708c1919932c75ae8fce048c413c94aff0f",
"core-reductions": "764eac75a3bcc1a38f7975ed4de1e5df39d70889dfb845507ef1cfe1ff456eac",
"core-shape": "c84940b2aff4e4a25e7a4292fa6d76c2a0306f328ba334e3a6085843d94fcc3b",
"core-fft": "1848e23763a0a72f282eff45c1cc54a3d2486f4ff53b12b4a8f462bcdc773061",
"core-quasirandom": "000620721b8ce6fff07287da6301b89dbcdaa7293d68a0783aae60fa4370aa6d",
"linalg-solve-inv-det": "5b73c4a57a6221f2b5abd3b6769d6aa13723db0ada0373e870d551f795c61bf3",
"linalg-factorisations": "f70c03b3759703a6fcd1ff1ef3cbab024acd000781e2065331c18f95c45b4d15",
"linalg-sparse-cholesky": "da49d6554341ffbf40500a99f241e0133528c2e6fadfc385cfa6d7d59685da28",
"linalg-sparse-lu": "3ccf6e7064b410c3c6df0a6b2697b4c66f3722aec99491ed9298db6212d65fbb",
"integrate-fem-poisson": "065cb65bb7fba9838ed6f84b703d7ffb69d877564bfbe006905e81fdea6b9175",
"linalg-sparse-complex": "d81a8dd242a23ce7026ded53c44a4fea3a58d9b20c8be4ed2c03147c740e2130",
"signal-welch-savgol": "36d6581ac2b06191b2d6eb743e042283d8cd0cbc187c54b6d40f490eef5984a4",
"signal-wavelets": "f324710c1f67c8e5bb099731c8647c944744a62e9219ef8fdbb070412df434ab",
"signal-lombscargle-conv": "4c3f3eb0f292b978d701dcea384ccbfc26be06acff348756ec0bda19c4cca20e",
"signal-filters-stencils": "00bccd2f3b5d61f07c3383280dc8111897e1eaf57a6e16a0d70081a703a8b158",
"stats-moments-quantiles": "61d196193641395bbb855685f2727cfe29521848bba111d89de9f480ef7264cc",
"stats-correlation-regression": "f398fea69ae522ffaafdf0d307edd02d657aaf7a452c6c75d174307becbf7218",
"stats-poisson-regression": "e385d9fda18acef4669ac1d1f1e8b12a5abeb682eff37a06f6ec140dd7b9f91a",
"stats-distributions": "8986f622708f3f1f28c1c00fed9225014c17a90a8e0714e2ce6e2f181ee03321",
"integrate-quadrature-ode": "66dd47bbc30de692902937078808da5322c76e6de1facaf5099fd72344f248a5",
"optim-roots-minima": "9af044a7e71416f0ab1f8dcbd4073f199163791ad28779b59c7faabdf982e6f4",
"optim-bounded-minima": "368dbb82d31c5e53c7dad9f944921e741ccccedafa7fda8d8b180f9daa58628e",
"optim-constrained-minima": "884020c56542ac84e5460f0cbd171f64904f86254ebbae6479e3bd6662cc6662",
"grad-backward": "0f3a1be94160019b796b457aa42531817685b7bc478ed041cc6c73ec5810b744",
"io-roundtrips": "de1a36e2b2e9235a40af37b4ee5823e526f6a46583c34af6245dbbd322e3f1e3",
"core-views-and-int-precision": "ca3c789ead2894ed2c53416f128b8aa665aed9dc3d4d67e38ae9d540a1ac5ccd",
"io-hdf5-fixture": "01e96137069f7d9dd7d4cd2e236de8f5fbcf4ba6121a1f121f21c041197cf117",
"io-hostile-inputs": "9dcf97a184f32623d11a73124ceb99a5709b083721e878a16d78f596718ba7b2",
"core-float16": "6af37847cc6187b64f2bb20b7fefdd3b28a039e42988708d942c41bf62576949",
"signal-kalman-arma": "874b9c7bfd3dae3111cb49576a51913972ad572963a2ab7ad1eaa3187f15196b",
"signal-windows-filtfilt-dwt": "b435d694350e7a7f06303ded85c2b63e0e562795cf69686f2ae854f2c6dc81c5",
"stats-distributions2-multipletest": "6d2f2661fa3a2bdaefbdf16f3d26b41ef69b29c93347e16e6df8542f30b14078",
"stats-regression2": "cabb52d66ccb5d92b8d0885f5a5a166c1814d364dc75a4e8cac89819820edba3",
"stats-unsupervised": "c8a17031b7133d58a6b6b6ec21a5624281d8f291717749869e41251b3e8ef68f",
"integrate-stiff-solvers": "48276e65576eeefa8d7c3da8da42ee782441462ce34cf6bb814e494c0e00668f",
"integrate-fem3d": "8323b5c89ac9187441cfac694193ef6394aff0508e71b53052d1b76fd59a535b",
"linalg-sparse-lsqr-rrqr-update": "806455d7e53765a272afca8cfef90aa233ab78e62e4ad682c085ff7e117231fc",
"optim-lp-qp-global2": "510ce65940755a164784ed4577502db2b60604aa9cbe184f22b2ab2a4c03bd57",
"io-hdf5-write": "21482614860f885f277b05666924ab3b63a2c55b8b24734cbadc4577f2c7ac90",
"optim-lm-fit": "cdbf15c3a4c5615e1a364a9fe4e5ec431d0cb65d9744cab52cbca66af51c58cf",
"core-cosm1": "f995296d5c1f285ec6f2e719ee62f8eaf7c9a8e751cea8702cbe16b3df66ddb2",
"core-substream": "ce49fae36f5e15b37aacc25d62b97c9f800d51aa08dee553cee1a5b6e1c01798",
"signal-chirp": "ff9af5783d32eed88bf2658bde5660c247a0bd30a699c403f1755da290830a4b",
"core-besseljreal": "49698a21228f091079cd65ae94cda134b16c3b0bec58a47642f6ad3fec7461d7",
"integrate-filon": "4d5242520a5c144e4bdf111df83c2f2faf4fadce0859f87738acab91e3ed93e0",
"core-narrow": "80913507636c4aabcdc57e5f90a75cd18499c86e244d84006650de3e1273da92",
"stats-contingency": "9ac61a7a29227ddae02861076c85a669fd467f466a8c1118c52965ad4ac20a1b",
"stats-mixedmodel": "6db27ea7908863a1f2d0725325d8a72a93a5ec83ad3882bca28a5d3d2b614227",
"stats-hmm": "779e2c9cb8ee1e1871b17a04a42b2d574ff3de6132430bae8c58be502d450b75",
"stats-hierarchy": "c4171ae9c61b76c676d016356ae319c5e3c15c0e32f7d0c26f0b1b38700cb96e",
},
"linux/amd64+v1+race": {
"spmd-shards": "0bec5217a9a79b182241ac0937f32e9d20c89a5b392fc797ab3787a03fc8c9ed",
"io-hdf5-narrow-write-roundtrip": "f2e87eaadcbab846e4acf5473b3023faca3015cd3b3f0919c4c3792114589ee9",
"io-hdf5-uint8-roundtrip": "54550518525945c92d970a38d2d81a5005fb8a899b597819c6060610ae77dd54",
"io-hdf5-int16-roundtrip": "b4163c6e9ed7a04ddb4a2328d394afe074237533738d19aa5ecaa68bff9e2685",
"io-hdf5-bool-roundtrip": "fa56fd9a421d2e78f17711e717f851c4b19dc1f6946d5c4e1dddad85cc42d347",
"io-netcdf-classic-natives": "c0219b60aa963176ce509f5633c22b96bc070529db1035d53a643b67d614d01b",
"core-elementwise": "0812ac01c62b084326d0e590941a46327136e9022a101fad68998781b68f7014",
"core-matmul-einsum": "a62b07d724f06603fe4c5ebf3d448b61d7638b93d1b58df605d94096d30ba4b4",
"core-sort-argsort": "24fc532b4140bc6633b5fabb8a19c708c1919932c75ae8fce048c413c94aff0f",
"core-reductions": "764eac75a3bcc1a38f7975ed4de1e5df39d70889dfb845507ef1cfe1ff456eac",
"core-shape": "c84940b2aff4e4a25e7a4292fa6d76c2a0306f328ba334e3a6085843d94fcc3b",
"core-fft": "1848e23763a0a72f282eff45c1cc54a3d2486f4ff53b12b4a8f462bcdc773061",
"core-quasirandom": "000620721b8ce6fff07287da6301b89dbcdaa7293d68a0783aae60fa4370aa6d",
"linalg-solve-inv-det": "5b73c4a57a6221f2b5abd3b6769d6aa13723db0ada0373e870d551f795c61bf3",
"linalg-factorisations": "f70c03b3759703a6fcd1ff1ef3cbab024acd000781e2065331c18f95c45b4d15",
"linalg-sparse-cholesky": "da49d6554341ffbf40500a99f241e0133528c2e6fadfc385cfa6d7d59685da28",
"linalg-sparse-lu": "3ccf6e7064b410c3c6df0a6b2697b4c66f3722aec99491ed9298db6212d65fbb",
"integrate-fem-poisson": "065cb65bb7fba9838ed6f84b703d7ffb69d877564bfbe006905e81fdea6b9175",
"linalg-sparse-complex": "d81a8dd242a23ce7026ded53c44a4fea3a58d9b20c8be4ed2c03147c740e2130",
"signal-welch-savgol": "36d6581ac2b06191b2d6eb743e042283d8cd0cbc187c54b6d40f490eef5984a4",
"signal-wavelets": "f324710c1f67c8e5bb099731c8647c944744a62e9219ef8fdbb070412df434ab",
"signal-lombscargle-conv": "4c3f3eb0f292b978d701dcea384ccbfc26be06acff348756ec0bda19c4cca20e",
"signal-filters-stencils": "00bccd2f3b5d61f07c3383280dc8111897e1eaf57a6e16a0d70081a703a8b158",
"stats-moments-quantiles": "61d196193641395bbb855685f2727cfe29521848bba111d89de9f480ef7264cc",
"stats-correlation-regression": "f398fea69ae522ffaafdf0d307edd02d657aaf7a452c6c75d174307becbf7218",
"stats-poisson-regression": "e385d9fda18acef4669ac1d1f1e8b12a5abeb682eff37a06f6ec140dd7b9f91a",
"stats-distributions": "8986f622708f3f1f28c1c00fed9225014c17a90a8e0714e2ce6e2f181ee03321",
"integrate-quadrature-ode": "66dd47bbc30de692902937078808da5322c76e6de1facaf5099fd72344f248a5",
"optim-roots-minima": "9af044a7e71416f0ab1f8dcbd4073f199163791ad28779b59c7faabdf982e6f4",
"optim-bounded-minima": "368dbb82d31c5e53c7dad9f944921e741ccccedafa7fda8d8b180f9daa58628e",
"optim-constrained-minima": "884020c56542ac84e5460f0cbd171f64904f86254ebbae6479e3bd6662cc6662",
"grad-backward": "0f3a1be94160019b796b457aa42531817685b7bc478ed041cc6c73ec5810b744",
"io-roundtrips": "de1a36e2b2e9235a40af37b4ee5823e526f6a46583c34af6245dbbd322e3f1e3",
"core-views-and-int-precision": "ca3c789ead2894ed2c53416f128b8aa665aed9dc3d4d67e38ae9d540a1ac5ccd",
"io-hdf5-fixture": "01e96137069f7d9dd7d4cd2e236de8f5fbcf4ba6121a1f121f21c041197cf117",
"io-hostile-inputs": "9dcf97a184f32623d11a73124ceb99a5709b083721e878a16d78f596718ba7b2",
"core-float16": "6af37847cc6187b64f2bb20b7fefdd3b28a039e42988708d942c41bf62576949",
"signal-kalman-arma": "874b9c7bfd3dae3111cb49576a51913972ad572963a2ab7ad1eaa3187f15196b",
"signal-windows-filtfilt-dwt": "b435d694350e7a7f06303ded85c2b63e0e562795cf69686f2ae854f2c6dc81c5",
"stats-distributions2-multipletest": "6d2f2661fa3a2bdaefbdf16f3d26b41ef69b29c93347e16e6df8542f30b14078",
"stats-regression2": "cabb52d66ccb5d92b8d0885f5a5a166c1814d364dc75a4e8cac89819820edba3",
"stats-unsupervised": "c8a17031b7133d58a6b6b6ec21a5624281d8f291717749869e41251b3e8ef68f",
"integrate-stiff-solvers": "48276e65576eeefa8d7c3da8da42ee782441462ce34cf6bb814e494c0e00668f",
"integrate-fem3d": "8323b5c89ac9187441cfac694193ef6394aff0508e71b53052d1b76fd59a535b",
"linalg-sparse-lsqr-rrqr-update": "806455d7e53765a272afca8cfef90aa233ab78e62e4ad682c085ff7e117231fc",
"optim-lp-qp-global2": "510ce65940755a164784ed4577502db2b60604aa9cbe184f22b2ab2a4c03bd57",
"io-hdf5-write": "21482614860f885f277b05666924ab3b63a2c55b8b24734cbadc4577f2c7ac90",
"optim-lm-fit": "cdbf15c3a4c5615e1a364a9fe4e5ec431d0cb65d9744cab52cbca66af51c58cf",
"core-cosm1": "f995296d5c1f285ec6f2e719ee62f8eaf7c9a8e751cea8702cbe16b3df66ddb2",
"core-substream": "ce49fae36f5e15b37aacc25d62b97c9f800d51aa08dee553cee1a5b6e1c01798",
"signal-chirp": "ff9af5783d32eed88bf2658bde5660c247a0bd30a699c403f1755da290830a4b",
"core-besseljreal": "49698a21228f091079cd65ae94cda134b16c3b0bec58a47642f6ad3fec7461d7",
"integrate-filon": "4d5242520a5c144e4bdf111df83c2f2faf4fadce0859f87738acab91e3ed93e0",
"core-narrow": "80913507636c4aabcdc57e5f90a75cd18499c86e244d84006650de3e1273da92",
"stats-contingency": "9ac61a7a29227ddae02861076c85a669fd467f466a8c1118c52965ad4ac20a1b",
"stats-mixedmodel": "6db27ea7908863a1f2d0725325d8a72a93a5ec83ad3882bca28a5d3d2b614227",
"stats-hmm": "779e2c9cb8ee1e1871b17a04a42b2d574ff3de6132430bae8c58be502d450b75",
"stats-hierarchy": "c4171ae9c61b76c676d016356ae319c5e3c15c0e32f7d0c26f0b1b38700cb96e",
},
}
// oracleBuild names the code generation this binary was built with: the
// GOAMD64 level decides whether the compiler may contract a multiply and
// an add into one fused operation, and the race detector's
// instrumentation changes which loops the compiler still contracts.
// Each changes the last bits of ordinary arithmetic without changing
// the algorithm, so the digests are keyed by both alongside the
// architecture: a build whose combination has no recorded block skips
// loudly instead of failing, exactly as an unseen architecture does.
func oracleBuild() string {
level := "v1"
if bi, ok := debug.ReadBuildInfo(); ok {
for _, s := range bi.Settings {
if s.Key == "GOAMD64" && s.Value != "" {
level = s.Value
}
}
}
return "+" + level + oracleRaceSuffix
}
// oracleKey names this architecture and code-generation block.
func oracleKey() string {
return runtime.GOOS + "/" + runtime.GOARCH + oracleBuild()
}
func TestOracle(t *testing.T) {
record := os.Getenv("TENSOR_ORACLE_RECORD") == "1"
current := make(map[string]string, len(oracleCases))
for _, c := range oracleCases {
d := newOracleDigest()
for _, v := range c.run(t) {
d.result(t, v)
}
current[c.name] = d.sum()
}
if record {
for _, c := range oracleCases {
t.Logf("oracle %s %s", c.name, current[c.name])
}
t.Logf("record these into pinnedOracleDigests[%q] and commit", oracleKey())
return
}
pinned, known := pinnedOracleDigests[oracleKey()]
if !known {
t.Skipf("no oracle digests recorded for %s; run TENSOR_ORACLE_RECORD=1 go test -run TestOracle -v . on it, "+
"then commit the pinnedOracleDigests[%q] block", oracleKey(), oracleKey())
}
if len(pinned) != len(oracleCases) {
t.Fatalf("pinnedOracleDigests[%q] holds %d entries, the harness runs %d; record with TENSOR_ORACLE_RECORD=1",
oracleKey(), len(pinned), len(oracleCases))
}
for _, c := range oracleCases {
want, ok := pinned[c.name]
if !ok {
t.Errorf("oracle case %q is not pinned", c.name)
continue
}
if current[c.name] != want {
t.Errorf("oracle case %q moved:\n got %s\nwant %s\n"+
"If the change is deliberate, re-record with TENSOR_ORACLE_RECORD=1 in the same commit.",
c.name, current[c.name], want)
}
}
}