Files
tensor/oracle_test.go
petrbalvin af4ee19703
Release / gates (push) Successful in 4m38s
Test / test (push) Successful in 5m16s
Release / release (push) Successful in 35s
feat: initial release
Assisted-by: GLM 5.3 Flash
2026-09-03 10:00:00 +02:00

2853 lines
90 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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)
}
}
}