github.com/gopherd/gonum@v0.0.4/interp/interp_test.go (about)

     1  // Copyright ©2020 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package interp
     6  
     7  import (
     8  	"math"
     9  	"testing"
    10  
    11  	"github.com/gopherd/gonum/floats"
    12  )
    13  
    14  func TestConstant(t *testing.T) {
    15  	t.Parallel()
    16  	const value = 42.0
    17  	c := Constant(value)
    18  	xs := []float64{math.Inf(-1), -11, 0.4, 1e9, math.Inf(1)}
    19  	for _, x := range xs {
    20  		y := c.Predict(x)
    21  		if y != value {
    22  			t.Errorf("unexpected Predict(%g) value: got: %g want: %g", x, y, value)
    23  		}
    24  	}
    25  }
    26  
    27  func TestFunction(t *testing.T) {
    28  	fn := func(x float64) float64 { return math.Exp(x) }
    29  	predictor := Function(fn)
    30  	xs := []float64{-100, -1, 0, 0.5, 15}
    31  	for _, x := range xs {
    32  		want := fn(x)
    33  		got := predictor.Predict(x)
    34  		if got != want {
    35  			t.Errorf("unexpected Predict(%g) value: got: %g want: %g", x, got, want)
    36  		}
    37  	}
    38  }
    39  
    40  func TestFindSegment(t *testing.T) {
    41  	t.Parallel()
    42  	xs := []float64{0, 1, 2}
    43  	testXs := []float64{-0.6, 0, 0.3, 1, 1.5, 2, 2.8}
    44  	expectedIs := []int{-1, 0, 0, 1, 1, 2, 2}
    45  	for k, x := range testXs {
    46  		i := findSegment(xs, x)
    47  		if i != expectedIs[k] {
    48  			t.Errorf("unexpected value of findSegment(xs, %g): got %d want: %d", x, i, expectedIs[k])
    49  		}
    50  	}
    51  }
    52  
    53  func BenchmarkFindSegment(b *testing.B) {
    54  	xs := []float64{0, 1.5, 3, 4.5, 6, 7.5, 9, 12, 13.5, 16.5}
    55  	for i := 0; i < b.N; i++ {
    56  		findSegment(xs, 0)
    57  		findSegment(xs, 16.5)
    58  		findSegment(xs, -1)
    59  		findSegment(xs, 8.25)
    60  		findSegment(xs, 4.125)
    61  		findSegment(xs, 13.6)
    62  		findSegment(xs, 23.6)
    63  		findSegment(xs, 13.5)
    64  		findSegment(xs, 6)
    65  		findSegment(xs, 4.5)
    66  	}
    67  }
    68  
    69  // testPiecewiseInterpolatorCreation tests common functionality in creating piecewise  interpolators.
    70  func testPiecewiseInterpolatorCreation(t *testing.T, fp FittablePredictor) {
    71  	type errorParams struct {
    72  		xs []float64
    73  		ys []float64
    74  	}
    75  	errorParamSets := []errorParams{
    76  		{[]float64{0, 1, 2}, []float64{-0.5, 1.5}},
    77  		{[]float64{0.3}, []float64{0}},
    78  		{[]float64{0.3, 0.3}, []float64{0, 0}},
    79  		{[]float64{0.3, -0.3}, []float64{0, 0}},
    80  	}
    81  	for _, params := range errorParamSets {
    82  		if !panics(func() { _ = fp.Fit(params.xs, params.ys) }) {
    83  			t.Errorf("expected panic for xs: %v and ys: %v", params.xs, params.ys)
    84  		}
    85  	}
    86  }
    87  
    88  func TestPiecewiseLinearFit(t *testing.T) {
    89  	t.Parallel()
    90  	testPiecewiseInterpolatorCreation(t, &PiecewiseLinear{})
    91  }
    92  
    93  // testInterpolatorPredict tests evaluation of a  interpolator.
    94  func testInterpolatorPredict(t *testing.T, p Predictor, xs []float64, expectedYs []float64, tol float64) {
    95  	for i, x := range xs {
    96  		y := p.Predict(x)
    97  		yErr := math.Abs(y - expectedYs[i])
    98  		if yErr > tol {
    99  			if tol == 0 {
   100  				t.Errorf("unexpected Predict(%g) value: got: %g want: %g", x, y, expectedYs[i])
   101  			} else {
   102  				t.Errorf("unexpected Predict(%g) value: got: %g want: %g with tolerance: %g", x, y, expectedYs[i], tol)
   103  			}
   104  		}
   105  	}
   106  }
   107  
   108  func TestPiecewiseLinearPredict(t *testing.T) {
   109  	t.Parallel()
   110  	xs := []float64{0, 1, 2}
   111  	ys := []float64{-0.5, 1.5, 1}
   112  	var pl PiecewiseLinear
   113  	err := pl.Fit(xs, ys)
   114  	if err != nil {
   115  		t.Errorf("Fit error: %s", err.Error())
   116  	}
   117  	testInterpolatorPredict(t, pl, xs, ys, 0)
   118  	testInterpolatorPredict(t, pl, []float64{-0.4, 2.6}, []float64{-0.5, 1}, 0)
   119  	testInterpolatorPredict(t, pl, []float64{0.1, 0.5, 0.8, 1.2}, []float64{-0.3, 0.5, 1.1, 1.4}, 1e-15)
   120  }
   121  
   122  func BenchmarkNewPiecewiseLinear(b *testing.B) {
   123  	xs := []float64{0, 1.5, 3, 4.5, 6, 7.5, 9, 12, 13.5, 16.5}
   124  	ys := []float64{0, 1, 2, 2.5, 2, 1.5, 4, 10, -2, 2}
   125  	var pl PiecewiseLinear
   126  	for i := 0; i < b.N; i++ {
   127  		_ = pl.Fit(xs, ys)
   128  	}
   129  }
   130  
   131  func BenchmarkPiecewiseLinearPredict(b *testing.B) {
   132  	xs := []float64{0, 1.5, 3, 4.5, 6, 7.5, 9, 12, 13.5, 16.5}
   133  	ys := []float64{0, 1, 2, 2.5, 2, 1.5, 4, 10, -2, 2}
   134  	var pl PiecewiseLinear
   135  	_ = pl.Fit(xs, ys)
   136  	for i := 0; i < b.N; i++ {
   137  		pl.Predict(0)
   138  		pl.Predict(16.5)
   139  		pl.Predict(-2)
   140  		pl.Predict(4)
   141  		pl.Predict(7.32)
   142  		pl.Predict(9.0001)
   143  		pl.Predict(1.4)
   144  		pl.Predict(1.6)
   145  		pl.Predict(30)
   146  		pl.Predict(13.5)
   147  		pl.Predict(4.5)
   148  	}
   149  }
   150  
   151  func TestNewPiecewiseConstant(t *testing.T) {
   152  	var pc PiecewiseConstant
   153  	testPiecewiseInterpolatorCreation(t, &pc)
   154  }
   155  
   156  func benchmarkPiecewiseConstantPredict(b *testing.B) {
   157  	xs := []float64{0, 1.5, 3, 4.5, 6, 7.5, 9, 12, 13.5, 16.5}
   158  	ys := []float64{0, 1, 2, 2.5, 2, 1.5, 4, 10, -2, 2}
   159  	var pc PiecewiseConstant
   160  	_ = pc.Fit(xs, ys)
   161  	for i := 0; i < b.N; i++ {
   162  		pc.Predict(0)
   163  		pc.Predict(16.5)
   164  		pc.Predict(4)
   165  		pc.Predict(7.32)
   166  		pc.Predict(9.0001)
   167  		pc.Predict(1.4)
   168  		pc.Predict(1.6)
   169  		pc.Predict(13.5)
   170  		pc.Predict(4.5)
   171  	}
   172  }
   173  
   174  func BenchmarkPiecewiseConstantPredict(b *testing.B) {
   175  	benchmarkPiecewiseConstantPredict(b)
   176  }
   177  
   178  func TestPiecewiseConstantPredict(t *testing.T) {
   179  	t.Parallel()
   180  	xs := []float64{0, 1, 2}
   181  	ys := []float64{-0.5, 1.5, 1}
   182  	var pc PiecewiseConstant
   183  	err := pc.Fit(xs, ys)
   184  	if err != nil {
   185  		t.Errorf("Fit error: %s", err.Error())
   186  	}
   187  	testInterpolatorPredict(t, pc, xs, ys, 0)
   188  	testXs := []float64{-0.9, 0.1, 0.5, 0.8, 1.2, 3.1}
   189  	leftYs := []float64{-0.5, 1.5, 1.5, 1.5, 1, 1}
   190  	testInterpolatorPredict(t, pc, testXs, leftYs, 0)
   191  }
   192  
   193  func TestCalculateSlopesErrors(t *testing.T) {
   194  	t.Parallel()
   195  	for _, test := range []struct {
   196  		xs, ys []float64
   197  	}{
   198  		{
   199  			xs: []float64{0},
   200  			ys: []float64{0},
   201  		},
   202  		{
   203  			xs: []float64{0, 1, 2},
   204  			ys: []float64{0, 1}},
   205  		{
   206  			xs: []float64{0, 0, 1},
   207  			ys: []float64{0, 0, 0},
   208  		},
   209  		{
   210  			xs: []float64{0, 1, 0},
   211  			ys: []float64{0, 0, 0},
   212  		},
   213  	} {
   214  		if !panics(func() { calculateSlopes(test.xs, test.ys) }) {
   215  			t.Errorf("expected panic for xs: %v and ys: %v", test.xs, test.ys)
   216  		}
   217  	}
   218  }
   219  
   220  func TestCalculateSlopes(t *testing.T) {
   221  	t.Parallel()
   222  	for i, test := range []struct {
   223  		xs, ys, want []float64
   224  	}{
   225  		{
   226  			xs:   []float64{0, 2, 3, 5},
   227  			ys:   []float64{0, 1, 1, -1},
   228  			want: []float64{0.5, 0, -1},
   229  		},
   230  		{
   231  			xs:   []float64{10, 20},
   232  			ys:   []float64{50, 100},
   233  			want: []float64{5},
   234  		},
   235  	} {
   236  		got := calculateSlopes(test.xs, test.ys)
   237  		if !floats.EqualApprox(got, test.want, 1e-14) {
   238  			t.Errorf("Mismatch in calculated slopes in case %d: got %v, want %v", i, got, test.want)
   239  		}
   240  	}
   241  }
   242  
   243  func applyFunc(xs []float64, f func(x float64) float64) []float64 {
   244  	ys := make([]float64, len(xs))
   245  	for i, x := range xs {
   246  		ys[i] = f(x)
   247  	}
   248  	return ys
   249  }
   250  
   251  func panics(fun func()) (b bool) {
   252  	defer func() {
   253  		err := recover()
   254  		if err != nil {
   255  			b = true
   256  		}
   257  	}()
   258  	fun()
   259  	return
   260  }
   261  
   262  func discrDerivPredict(p Predictor, x0, x1, x, h float64) float64 {
   263  	if x <= x0+h {
   264  		return (p.Predict(x+h) - p.Predict(x)) / h
   265  	} else if x >= x1-h {
   266  		return (p.Predict(x) - p.Predict(x-h)) / h
   267  	} else {
   268  		return (p.Predict(x+h) - p.Predict(x-h)) / (2 * h)
   269  	}
   270  }