gonum.org/v1/gonum@v0.14.0/stat/distuv/beta_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  	"gonum.org/v1/gonum/floats/scalar"
    15  )
    16  
    17  func TestBetaProb(t *testing.T) {
    18  	t.Parallel()
    19  	// Values a comparison with scipy
    20  	for _, test := range []struct {
    21  		x, alpha, beta, want float64
    22  	}{
    23  		{0.1, 2, 0.5, 0.079056941504209499},
    24  		{0.5, 1, 5.1, 0.29740426605235754},
    25  		{0.1, 0.5, 0.5, 1.0610329539459691},
    26  		{1, 0.5, 0.5, math.Inf(1)},
    27  		{-1, 0.5, 0.5, 0},
    28  	} {
    29  		pdf := Beta{Alpha: test.alpha, Beta: test.beta}.Prob(test.x)
    30  		if !scalar.EqualWithinAbsOrRel(pdf, test.want, 1e-10, 1e-10) {
    31  			t.Errorf("Pdf mismatch. Got %v, want %v", pdf, test.want)
    32  		}
    33  	}
    34  }
    35  
    36  func TestBetaRand(t *testing.T) {
    37  	t.Parallel()
    38  	src := rand.New(rand.NewSource(1))
    39  	for i, b := range []Beta{
    40  		{Alpha: 0.5, Beta: 0.5, Src: src},
    41  		{Alpha: 5, Beta: 1, Src: src},
    42  		{Alpha: 2, Beta: 2, Src: src},
    43  		{Alpha: 2, Beta: 5, Src: src},
    44  	} {
    45  		testBeta(t, b, i)
    46  	}
    47  }
    48  
    49  func testBeta(t *testing.T, b Beta, i int) {
    50  	const (
    51  		tol  = 1e-2
    52  		n    = 1e5
    53  		bins = 10
    54  	)
    55  	x := make([]float64, n)
    56  	generateSamples(x, b)
    57  	sort.Float64s(x)
    58  
    59  	testRandLogProbContinuous(t, i, 0, x, b, tol, bins)
    60  	checkMean(t, i, x, b, tol)
    61  	checkVarAndStd(t, i, x, b, tol)
    62  	checkExKurtosis(t, i, x, b, 5e-2)
    63  	checkEntropy(t, i, x, b, 5e-3)
    64  	checkProbContinuous(t, i, x, 0, 1, b, 1e-6)
    65  	checkQuantileCDFSurvival(t, i, x, b, tol)
    66  	checkProbQuantContinuous(t, i, x, b, tol)
    67  
    68  	if b.NumParameters() != 2 {
    69  		t.Errorf("Wrong number of parameters")
    70  	}
    71  
    72  	if b.CDF(-0.01) != 0 {
    73  		t.Errorf("CDF below 0 is not 0")
    74  	}
    75  	if b.CDF(0) != 0 {
    76  		t.Errorf("CDF at 0 is not 0")
    77  	}
    78  	if b.CDF(1) != 1 {
    79  		t.Errorf("CDF at 1 is not 1")
    80  	}
    81  	if b.CDF(1.01) != 1 {
    82  		t.Errorf("CDF above 1 is not 1")
    83  	}
    84  
    85  	if b.Survival(-0.01) != 1 {
    86  		t.Errorf("Survival below 0 is not 1")
    87  	}
    88  	if b.Survival(0) != 1 {
    89  		t.Errorf("Survival at 0 is not 1")
    90  	}
    91  	if b.Survival(1) != 0 {
    92  		t.Errorf("Survival at 1 is not 0")
    93  	}
    94  	if b.Survival(1.01) != 0 {
    95  		t.Errorf("Survival above 1 is not 0")
    96  	}
    97  }
    98  
    99  func TestBetaBadParams(t *testing.T) {
   100  	t.Parallel()
   101  	src := rand.New(rand.NewSource(1))
   102  	for _, alpha := range []float64{0, -0.1} {
   103  		testBetaBadParams(t, alpha, 1, src)
   104  		testBetaBadParams(t, 1, alpha, src)
   105  		for _, beta := range []float64{0, -0.1} {
   106  			testBetaBadParams(t, alpha, beta, src)
   107  		}
   108  	}
   109  }
   110  
   111  func testBetaBadParams(t *testing.T, alpha float64, beta float64, src rand.Source) {
   112  	b := Beta{alpha, beta, src}
   113  	if !panics(func() { b.Entropy() }) {
   114  		t.Errorf("Entropy did not panic for Beta(%g, %g)", alpha, beta)
   115  	}
   116  	if !panics(func() { b.LogProb(0.5) }) {
   117  		t.Errorf("LogProb did not panic for Beta(%g, %g)", alpha, beta)
   118  	}
   119  }
   120  
   121  func TestBetaMode(t *testing.T) {
   122  	t.Parallel()
   123  	for _, test := range []struct {
   124  		alpha, beta, want float64
   125  	}{
   126  		{1, 2, 0},
   127  		{0.5, 2, 0},
   128  		{2, 1, 1},
   129  		{2, 0.5, 1},
   130  		{4, 5, 3. / 7.},
   131  	} {
   132  		mode := Beta{Alpha: test.alpha, Beta: test.beta}.Mode()
   133  		if !scalar.EqualWithinAbsOrRel(mode, test.want, 1e-10, 1e-10) {
   134  			t.Errorf("Mode mismatch for Beta(%g, %g). Got %v, want %g", test.alpha, test.beta, mode, test.want)
   135  		}
   136  	}
   137  	for _, test := range []struct {
   138  		alpha, beta float64
   139  	}{
   140  		{1, 1},
   141  		{0.5, 0.5},
   142  		{1, 0.5},
   143  		{0.5, 1},
   144  	} {
   145  		mode := Beta{Alpha: test.alpha, Beta: test.beta}.Mode()
   146  		if !math.IsNaN(mode) {
   147  			t.Errorf("Mode is not NaN for Beta(%g, %g). Got: %v", test.alpha, test.beta, mode)
   148  		}
   149  	}
   150  }
   151  
   152  // See https://github.com/gonum/gonum/issues/1377 for details.
   153  func TestBetaIssue1377(t *testing.T) {
   154  	t.Parallel()
   155  	b := Beta{Alpha: 1, Beta: 1}
   156  	p0 := b.Prob(0)
   157  	if p0 != 1 {
   158  		t.Errorf("Mismatch in PDF value at x == 0 for Alpha == 1 and Beta == 1: got %v, want 1", p0)
   159  	}
   160  	p1 := b.Prob(1)
   161  	if p1 != 1 {
   162  		t.Errorf("Mismatch in PDF value at x == 1 for Alpha == 1 and Beta == 1: got %v, want 1", p1)
   163  	}
   164  	b = Beta{Alpha: 1, Beta: 10}
   165  	p0 = b.Prob(0)
   166  	if math.IsNaN(p0) {
   167  		t.Errorf("NaN PDF at x == 0 for Alpha == 1 and Beta > 10")
   168  	}
   169  	b = Beta{Alpha: 10, Beta: 1}
   170  	p1 = b.Prob(1)
   171  	if math.IsNaN(p1) {
   172  		t.Errorf("NaN PDF at x == 1 for Alpha > 1 and Beta == 1")
   173  	}
   174  }