integrate: rework tests to use testquad integrals
diff --git a/integrate/romberg_test.go b/integrate/romberg_test.go
index 58ac4c3..00c34ad 100644
--- a/integrate/romberg_test.go
+++ b/integrate/romberg_test.go
@@ -9,78 +9,57 @@
"testing"
"gonum.org/v1/gonum/floats"
+ "gonum.org/v1/gonum/integrate/testquad"
)
func TestRomberg(t *testing.T) {
- const n = 1<<8 + 1
- x := floats.Span(make([]float64, n), 0, 1)
-
for i, test := range []struct {
- x []float64
- f func(x float64) float64
- want float64
- tol float64
+ integral testquad.Integral
+ n int
+ tol float64
}{
- {
- x: x,
- f: func(x float64) float64 { return x },
- want: 0.5,
- tol: 1e-12,
- },
- {
- x: x,
- f: func(x float64) float64 { return x * x },
- want: 1.0 / 3.0,
- tol: 1e-12,
- },
- {
- x: x,
- f: func(x float64) float64 { return x * x * x },
- want: 1.0 / 4.0,
- tol: 1e-12,
- },
- {
- x: x,
- f: func(x float64) float64 { return math.Sqrt(x) },
- want: 2.0 / 3.0,
- tol: 1e-4,
- },
- {
- x: x,
- f: func(x float64) float64 { return math.Sin(math.Pi * x) },
- want: 2.0 / math.Pi,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 3), 0, 1),
- f: func(x float64) float64 { return x * x },
- want: 1.0 / 3.0,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 3), 0, 1),
- f: func(x float64) float64 { return x * x * x },
- want: 1.0 / 4.0,
- tol: 1e-12,
- },
- {
- x: x,
- f: func(x float64) float64 { return x * math.Exp(-x) },
- want: (math.Exp(1) - 2) / math.Exp(1),
- tol: 1e-12,
- },
+ {integral: testquad.Constant(0), n: 3, tol: 0},
+ {integral: testquad.Constant(0), n: 1<<5 + 1, tol: 0},
+ {integral: testquad.Poly(0), n: 3, tol: 1e-14},
+ {integral: testquad.Poly(0), n: 1<<5 + 1, tol: 1e-14},
+ {integral: testquad.Poly(1), n: 3, tol: 1e-14},
+ {integral: testquad.Poly(1), n: 1<<5 + 1, tol: 1e-14},
+ {integral: testquad.Poly(2), n: 3, tol: 1e-14},
+ {integral: testquad.Poly(2), n: 1<<5 + 1, tol: 1e-14},
+ {integral: testquad.Poly(3), n: 3, tol: 1e-14},
+ {integral: testquad.Poly(3), n: 1<<5 + 1, tol: 1e-14},
+ {integral: testquad.Poly(4), n: 5, tol: 1e-14},
+ {integral: testquad.Poly(4), n: 1<<5 + 1, tol: 1e-14},
+ {integral: testquad.Poly(5), n: 5, tol: 1e-14},
+ {integral: testquad.Poly(5), n: 1<<5 + 1, tol: 1e-14},
+ {integral: testquad.Sin(), n: 1<<3 + 1, tol: 1e-10},
+ {integral: testquad.Sin(), n: 1<<5 + 1, tol: 1e-14},
+ {integral: testquad.XExpMinusX(), n: 1<<3 + 1, tol: 1e-9},
+ {integral: testquad.XExpMinusX(), n: 1<<5 + 1, tol: 1e-14},
+ {integral: testquad.Sqrt(), n: 1<<10 + 1, tol: 1e-5},
+ {integral: testquad.ExpOverX2Plus1(), n: 1<<4 + 1, tol: 1e-7},
+ {integral: testquad.ExpOverX2Plus1(), n: 1<<6 + 1, tol: 1e-14},
} {
- n := len(test.x)
+ n := test.n
+ a := test.integral.A
+ b := test.integral.B
+
+ x := make([]float64, n)
+ floats.Span(x, a, b)
+
y := make([]float64, n)
- for i, v := range test.x {
- y[i] = test.f(v)
+ for i, xi := range x {
+ y[i] = test.integral.F(xi)
}
- dx := (test.x[n-1] - test.x[0]) / float64(n-1)
- v := Romberg(y, dx)
- diff := math.Abs(v - test.want)
+ dx := (b - a) / float64(n-1)
+ got := Romberg(y, dx)
+
+ want := test.integral.Value
+ diff := math.Abs(got - want)
if diff > test.tol {
- t.Errorf("test #%d: got=%v want=%v diff=%v\n", i, v, test.want, diff)
+ t.Errorf("Test #%d: %v, n=%v: unexpected result; got=%v want=%v diff=%v",
+ i, test.integral.Name, n, got, want, diff)
}
}
}
diff --git a/integrate/simpsons_test.go b/integrate/simpsons_test.go
index cdaff0c..cb6dc54 100644
--- a/integrate/simpsons_test.go
+++ b/integrate/simpsons_test.go
@@ -8,165 +8,51 @@
"math"
"testing"
- "gonum.org/v1/gonum/floats"
+ "golang.org/x/exp/rand"
+
+ "gonum.org/v1/gonum/integrate/testquad"
)
func TestSimpsons(t *testing.T) {
- x := floats.Span(make([]float64, 1e6), 0, 1)
+ rnd := rand.New(rand.NewSource(1))
for i, test := range []struct {
- x []float64
- f func(x float64) float64
- want float64
- tol float64
+ integral testquad.Integral
+ n int
+ tol float64
}{
- {
- x: floats.Span(make([]float64, 1e6), 0, 1),
- f: func(x float64) float64 { return math.Pi },
- want: math.Pi,
- tol: 1e-10,
- },
- {
- x: floats.Span(make([]float64, 1e6), 0, 1),
- f: func(x float64) float64 { return 1.0 },
- want: 1,
- tol: 1e-10,
- },
- {
- x: floats.Span(make([]float64, 3), 0, 2),
- f: func(x float64) float64 { return 2*x + 0.5 },
- want: 5,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 10), 0, 2),
- f: func(x float64) float64 { return 2*x + 0.5 },
- want: 5,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 1e6), 0, 2),
- f: func(x float64) float64 { return 2*x + 0.5 },
- want: 5,
- tol: 1e-12,
- },
- {
- x: x,
- f: func(x float64) float64 { return x },
- want: 0.5,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 1e6), -1, 1),
- f: func(x float64) float64 { return x },
- want: 0,
- tol: 1e-12,
- },
- {
- x: x,
- f: func(x float64) float64 { return x + 10 },
- want: 10.5,
- tol: 1e-12,
- },
- {
- x: x,
- f: func(x float64) float64 { return 3*x*x + 10 },
- want: 11,
- tol: 1e-12,
- },
- {
- x: x,
- f: func(x float64) float64 { return math.Exp(x) },
- want: 1.7182818284591876,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 1e6), 0, math.Pi),
- f: func(x float64) float64 { return math.Cos(x) },
- want: 0,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 1e6), 0, 2*math.Pi),
- f: func(x float64) float64 { return math.Cos(x) },
- want: 0,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 1e6*10), 0, math.Pi),
- f: func(x float64) float64 { return math.Sin(x) },
- want: 2,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 1e6*10), 0, 0.5*math.Pi),
- f: func(x float64) float64 { return math.Sin(x) },
- want: 1,
- tol: 1e-12,
- },
- {
- x: floats.Span(make([]float64, 1e6), 0, 2*math.Pi),
- f: func(x float64) float64 { return math.Sin(x) },
- want: 0,
- tol: 1e-12,
- },
- {
- x: join(floats.Span(make([]float64, 3), 0, math.Pi/3),
- []float64{4 * math.Pi / 10},
- floats.Span(make([]float64, 4), 3*math.Pi/4, math.Pi)),
- f: func(x float64) float64 { return math.Sin(x) },
- want: 2,
- tol: 1e-2,
- },
- {
- x: join(floats.Span(make([]float64, 30), 0, 5*math.Pi/16),
- floats.Span(make([]float64, 100), 3*math.Pi/8, math.Pi)),
- f: func(x float64) float64 { return math.Sin(x) },
- want: 2,
- tol: 1e-4,
- },
- {
- x: join(floats.Span(make([]float64, 1e5), 0, 15),
- floats.Span(make([]float64, 1e5), 23, 40),
- floats.Span(make([]float64, 1e5), 50, 80),
- floats.Span(make([]float64, 1e5), 90, 100)),
- f: func(x float64) float64 { return 2 * x },
- want: 10000,
- tol: 1e-9,
- },
- {
- x: []float64{0, 1, 2},
- f: func(x float64) float64 { return 2 * x },
- want: 4,
- tol: 1e-12,
- },
- {
- x: []float64{0, 0.2, 2},
- f: func(x float64) float64 { return 2 * x },
- want: 4,
- tol: 1e-12,
- },
- {
- x: []float64{0, 1.89, 2},
- f: func(x float64) float64 { return 2 * x },
- want: 4,
- tol: 1e-12,
- },
+ {integral: testquad.Constant(0), n: 3, tol: 0},
+ {integral: testquad.Constant(0), n: 10, tol: 0},
+ {integral: testquad.Poly(0), n: 3, tol: 1e-14},
+ {integral: testquad.Poly(0), n: 10, tol: 1e-14},
+ {integral: testquad.Poly(1), n: 3, tol: 1e-14},
+ {integral: testquad.Poly(1), n: 10, tol: 1e-14},
+ {integral: testquad.Poly(2), n: 3, tol: 1e-14},
+ {integral: testquad.Poly(2), n: 10, tol: 1e-14},
+ {integral: testquad.Poly(3), n: 1e3, tol: 1e-8},
+ {integral: testquad.Poly(4), n: 1e3, tol: 1e-8},
+ {integral: testquad.Poly(5), n: 1e3, tol: 1e-7},
+ {integral: testquad.Sin(), n: 1e2, tol: 1e-8},
+ {integral: testquad.XExpMinusX(), n: 1e2, tol: 1e-8},
+ {integral: testquad.Sqrt(), n: 1e4, tol: 1e-6},
+ {integral: testquad.ExpOverX2Plus1(), n: 1e2, tol: 1e-7},
} {
- y := make([]float64, len(test.x))
- for i, v := range test.x {
- y[i] = test.f(v)
- }
- v := Simpsons(test.x, y)
- if !floats.EqualWithinAbs(v, test.want, test.tol) {
- t.Errorf("test #%d: got=%v want=%f\n", i, v, float64(test.want))
- }
- }
-}
+ n := test.n
+ a := test.integral.A
+ b := test.integral.B
-func join(slices ...[]float64) []float64 {
- var c []float64
- for _, s := range slices {
- c = append(c, s...)
+ x := jitterSpan(n, a, b, rnd)
+ y := make([]float64, n)
+ for i, xi := range x {
+ y[i] = test.integral.F(xi)
+ }
+
+ got := Simpsons(x, y)
+
+ want := test.integral.Value
+ diff := math.Abs(got - want)
+ if diff > test.tol {
+ t.Errorf("Test #%d: %v, n=%v: unexpected result; got=%v want=%v diff=%v",
+ i, test.integral.Name, n, got, want, diff)
+ }
}
- return c
}
diff --git a/integrate/trapezoidal_test.go b/integrate/trapezoidal_test.go
index 1a4451e..68b7379 100644
--- a/integrate/trapezoidal_test.go
+++ b/integrate/trapezoidal_test.go
@@ -8,75 +8,66 @@
"math"
"testing"
- "gonum.org/v1/gonum/floats"
+ "golang.org/x/exp/rand"
+
+ "gonum.org/v1/gonum/integrate/testquad"
)
func TestTrapezoidal(t *testing.T) {
- const N = 1e6
- x := floats.Span(make([]float64, N), 0, 1)
+ rnd := rand.New(rand.NewSource(1))
for i, test := range []struct {
- x []float64
- f func(x float64) float64
- want float64
+ integral testquad.Integral
+ n int
+ tol float64
}{
- {
- x: x,
- f: func(x float64) float64 { return x },
- want: 0.5,
- },
- {
- x: floats.Span(make([]float64, N), -1, 1),
- f: func(x float64) float64 { return x },
- want: 0,
- },
- {
- x: x,
- f: func(x float64) float64 { return x + 10 },
- want: 10.5,
- },
- {
- x: x,
- f: func(x float64) float64 { return 3*x*x + 10 },
- want: 11,
- },
- {
- x: x,
- f: func(x float64) float64 { return math.Exp(x) },
- want: 1.7182818284591876,
- },
- {
- x: floats.Span(make([]float64, N), 0, math.Pi),
- f: func(x float64) float64 { return math.Cos(x) },
- want: 0,
- },
- {
- x: floats.Span(make([]float64, N), 0, 2*math.Pi),
- f: func(x float64) float64 { return math.Cos(x) },
- want: 0,
- },
- {
- x: floats.Span(make([]float64, N*10), 0, math.Pi),
- f: func(x float64) float64 { return math.Sin(x) },
- want: 2,
- },
- {
- x: floats.Span(make([]float64, N*10), 0, 0.5*math.Pi),
- f: func(x float64) float64 { return math.Sin(x) },
- want: 1,
- },
- {
- x: floats.Span(make([]float64, N), 0, 2*math.Pi),
- f: func(x float64) float64 { return math.Sin(x) },
- want: 0,
- },
+ {integral: testquad.Constant(0), n: 2, tol: 0},
+ {integral: testquad.Constant(0), n: 10, tol: 0},
+ {integral: testquad.Poly(0), n: 2, tol: 1e-14},
+ {integral: testquad.Poly(0), n: 10, tol: 1e-14},
+ {integral: testquad.Poly(1), n: 2, tol: 1e-14},
+ {integral: testquad.Poly(1), n: 10, tol: 1e-14},
+ {integral: testquad.Poly(2), n: 1e5, tol: 1e-8},
+ {integral: testquad.Poly(3), n: 1e5, tol: 1e-8},
+ {integral: testquad.Poly(4), n: 1e5, tol: 1e-7},
+ {integral: testquad.Poly(5), n: 1e5, tol: 1e-7},
+ {integral: testquad.Sin(), n: 1e5, tol: 1e-11},
+ {integral: testquad.XExpMinusX(), n: 1e5, tol: 1e-10},
+ {integral: testquad.Sqrt(), n: 1e5, tol: 1e-8},
+ {integral: testquad.ExpOverX2Plus1(), n: 1e5, tol: 1e-10},
} {
- y := make([]float64, len(test.x))
- for i, v := range test.x {
- y[i] = test.f(v)
+ n := test.n
+ a := test.integral.A
+ b := test.integral.B
+
+ x := jitterSpan(n, a, b, rnd)
+ y := make([]float64, n)
+ for i, xi := range x {
+ y[i] = test.integral.F(xi)
}
- v := Trapezoidal(test.x, y)
- if !floats.EqualWithinAbs(v, test.want, 1e-12) {
- t.Errorf("test #%d: got=%v want=%v\n", i, v, test.want)
+
+ got := Trapezoidal(x, y)
+
+ want := test.integral.Value
+ diff := math.Abs(got - want)
+ if diff > test.tol {
+ t.Errorf("Test #%d: %v, n=%v: unexpected result; got=%v want=%v diff=%v",
+ i, test.integral.Name, n, got, want, diff)
}
}
}
+
+func jitterSpan(n int, a, b float64, rnd *rand.Rand) []float64 {
+ dx := (b - a) / float64(n-1)
+ x := make([]float64, n)
+ x[0] = a
+ for i := 1; i < n-1; i++ {
+ // Set x[i] to its regular location.
+ x[i] = a + float64(i)*dx
+ // Generate a random number in [-1,1).
+ jitter := 2*rnd.Float64() - 1
+ // Jitter x[i] without crossing over its neighbors.
+ x[i] += 0.4 * jitter * dx
+ }
+ x[n-1] = b
+ return x
+}