gonum.org/v1/gonum@v0.14.0/stat/distuv/bernoulli_test.go (about)

     1  // Copyright ©2016 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 distuv
     6  
     7  import (
     8  	"math"
     9  	"sort"
    10  	"testing"
    11  
    12  	"golang.org/x/exp/rand"
    13  )
    14  
    15  func TestBernoulli(t *testing.T) {
    16  	t.Parallel()
    17  	src := rand.New(rand.NewSource(1))
    18  	for i, dist := range []Bernoulli{
    19  		{P: 0.5, Src: src},
    20  		{P: 0.9, Src: src},
    21  		{P: 0.2, Src: src},
    22  		{P: 0.0, Src: src},
    23  		{P: 1.0, Src: src},
    24  	} {
    25  		testBernoulli(t, dist, i)
    26  		testBernoulliCDF(t, dist)
    27  		testBernoulliSurvival(t, dist)
    28  		testBernoulliQuantile(t, dist)
    29  		if dist.P == 0 || dist.P == 1 {
    30  			entropy := dist.Entropy()
    31  			if entropy != 0 {
    32  				t.Errorf("Entropy of a Bernoulli distribution with P = %g is not zero, got: %g", dist.P, entropy)
    33  			}
    34  		}
    35  		if dist.NumParameters() != 1 {
    36  			t.Errorf("Wrong number of parameters")
    37  		}
    38  		for _, x := range []float64{-0.2, 0.5, 1.1} {
    39  			logP := dist.LogProb(x)
    40  			p := dist.Prob(x)
    41  			if !math.IsInf(logP, -1) {
    42  				t.Errorf("Log-probability for x = %g is not -Inf, got: %g", x, logP)
    43  			}
    44  			if p != 0 {
    45  				t.Errorf("Probability for x = %g is not 0, got: %g", x, p)
    46  			}
    47  		}
    48  	}
    49  }
    50  
    51  func testBernoulli(t *testing.T, dist Bernoulli, i int) {
    52  	const (
    53  		tol  = 1e-2
    54  		n    = 3e6
    55  		bins = 50
    56  	)
    57  	x := make([]float64, n)
    58  	generateSamples(x, dist)
    59  	sort.Float64s(x)
    60  
    61  	checkMean(t, i, x, dist, tol)
    62  	checkVarAndStd(t, i, x, dist, tol)
    63  	checkEntropy(t, i, x, dist, tol)
    64  	checkProbDiscrete(t, i, x, dist, tol)
    65  	if dist.P != 0 && dist.P != 1 {
    66  		// Sample kurtosis and skewness are going to be NaN for P = 0 or 1.
    67  		checkExKurtosis(t, i, x, dist, tol)
    68  		checkSkewness(t, i, x, dist, tol)
    69  	} else {
    70  		if !math.IsInf(dist.ExKurtosis(), 1) {
    71  			t.Errorf("Excess kurtosis for P == 0 or 1 is not +Inf")
    72  		}
    73  		skewness := dist.Skewness()
    74  		if dist.P == 0 {
    75  			if !math.IsInf(skewness, 1) {
    76  				t.Errorf("Skewness for P == 0 is not +Inf")
    77  			}
    78  		} else {
    79  			if !math.IsInf(skewness, -1) {
    80  				t.Errorf("Skewness for P == 1 is not -Inf")
    81  			}
    82  		}
    83  	}
    84  	if dist.P != 0.5 {
    85  		checkMedian(t, i, x, dist, tol)
    86  	} else if dist.Median() != 0.5 {
    87  		t.Errorf("Median for P == 0.5 is not 0.5")
    88  	}
    89  }
    90  
    91  func testBernoulliCDF(t *testing.T, dist Bernoulli) {
    92  	if dist.CDF(-0.000001) != 0 {
    93  		t.Errorf("Bernoulli CDF below zero is not zero")
    94  	}
    95  	if dist.CDF(0) != 1-dist.P {
    96  		t.Errorf("Bernoulli CDF at zero is not 1 - P(1)")
    97  	}
    98  	if dist.CDF(0.0001) != 1-dist.P {
    99  		t.Errorf("Bernoulli CDF between zero and one is not 1 - P(1)")
   100  	}
   101  	if dist.CDF(0.9999) != 1-dist.P {
   102  		t.Errorf("Bernoulli CDF between zero and one is not 1 - P(1)")
   103  	}
   104  	if dist.CDF(1) != 1 {
   105  		t.Errorf("Bernoulli CDF at one is not one")
   106  	}
   107  	if dist.CDF(1.00001) != 1 {
   108  		t.Errorf("Bernoulli CDF above one is not one")
   109  	}
   110  }
   111  
   112  func testBernoulliSurvival(t *testing.T, dist Bernoulli) {
   113  	if dist.Survival(-0.000001) != 1 {
   114  		t.Errorf("Bernoulli Survival below zero is not one")
   115  	}
   116  	if dist.Survival(0) != dist.P {
   117  		t.Errorf("Bernoulli Survival at zero is not P(1)")
   118  	}
   119  	if dist.Survival(0.0001) != dist.P {
   120  		t.Errorf("Bernoulli Survival between zero and one is not P(1)")
   121  	}
   122  	if dist.Survival(1) != 0 {
   123  		t.Errorf("Bernoulli Survival at one is not zero")
   124  	}
   125  	if dist.Survival(1.00001) != 0 {
   126  		t.Errorf("Bernoulli Survival above one is not zero")
   127  	}
   128  }
   129  
   130  func testBernoulliQuantile(t *testing.T, dist Bernoulli) {
   131  	if !panics(func() { dist.Quantile(-0.0001) }) {
   132  		t.Errorf("Expected panic with negative argument")
   133  	}
   134  	if !panics(func() { dist.Quantile(1.0001) }) {
   135  		t.Errorf("Expected panic with argument above 1")
   136  	}
   137  	for _, x := range []float64{0., 1.} {
   138  		want := x
   139  		if dist.P == 0 {
   140  			want = 0
   141  		}
   142  		if dist.Quantile(dist.CDF(x)) != want {
   143  			t.Errorf("Quantile(CDF(x)) not equal to %g for x = %g for P = %g", want, x, dist.P)
   144  		}
   145  	}
   146  	expectedQuantile1 := 1.
   147  	if dist.P == 0 {
   148  		expectedQuantile1 = 0.
   149  	}
   150  	if dist.Quantile(1) != expectedQuantile1 {
   151  		t.Errorf("Quantile at 1 not equal to 1 for P = %g", dist.P)
   152  	}
   153  	eps := 1e-12
   154  	if dist.P > eps && dist.P < 1-eps {
   155  		if dist.Quantile(1-dist.P-eps) != 0 {
   156  			t.Errorf("Quantile slightly below 0 < 1-P < 1 is not zero")
   157  		}
   158  		if dist.Quantile(1-dist.P+eps) != 1 {
   159  			t.Errorf("Quantile slightly above 0 < 1-P < 1 is not one")
   160  		}
   161  		if dist.Quantile(1-dist.P) != 0 {
   162  			t.Errorf("Quantile at 0 < 1-P < 1 is not zero")
   163  		}
   164  	}
   165  }