gonum.org/v1/gonum@v0.14.0/stat/distuv/gamma_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 "fmt" 9 "math" 10 "sort" 11 "testing" 12 13 "golang.org/x/exp/rand" 14 15 "gonum.org/v1/gonum/floats/scalar" 16 ) 17 18 func TestGamma(t *testing.T) { 19 t.Parallel() 20 // Values a comparison with scipy 21 for _, test := range []struct { 22 x, alpha, want float64 23 }{ 24 {0.9, 0.1, 0.046986817861555757}, 25 {0.9, 0.01, 0.0045384353289090401}, 26 {0.45, 0.01, 0.014137035997241795}, 27 } { 28 pdf := Gamma{Alpha: test.alpha, Beta: 1}.Prob(test.x) 29 if !scalar.EqualWithinAbsOrRel(pdf, test.want, 1e-10, 1e-10) { 30 t.Errorf("Pdf mismatch. Got %v, want %v", pdf, test.want) 31 } 32 } 33 src := rand.New(rand.NewSource(1)) 34 for i, g := range []Gamma{ 35 {Alpha: 0.1, Beta: 0.8, Src: src}, 36 {Alpha: 0.3, Beta: 0.8, Src: src}, 37 {Alpha: 0.5, Beta: 0.8, Src: src}, 38 {Alpha: 0.9, Beta: 6, Src: src}, 39 {Alpha: 0.9, Beta: 500, Src: src}, 40 {Alpha: 1, Beta: 1, Src: src}, 41 {Alpha: 1.6, Beta: 0.4, Src: src}, 42 {Alpha: 2.6, Beta: 1.5, Src: src}, 43 {Alpha: 5.6, Beta: 0.5, Src: src}, 44 {Alpha: 30, Beta: 1.7, Src: src}, 45 {Alpha: 30.2, Beta: 1.7, Src: src}, 46 } { 47 testGamma(t, g, i) 48 } 49 } 50 51 func testGamma(t *testing.T, f Gamma, i int) { 52 // TODO(btracey): Replace this when Gamma implements FullDist. 53 const ( 54 tol = 1e-2 55 n = 1e5 56 bins = 50 57 ) 58 x := make([]float64, n) 59 generateSamples(x, f) 60 sort.Float64s(x) 61 62 var quadTol float64 63 64 if f.Alpha < 0.3 { 65 // Gamma PDF has a singularity at 0 for alpha < 1, 66 // which gets sharper as alpha -> 0. 67 quadTol = 0.2 68 } else { 69 quadTol = tol 70 } 71 testRandLogProbContinuous(t, i, 0, x, f, quadTol, bins) 72 checkMean(t, i, x, f, tol) 73 checkVarAndStd(t, i, x, f, 2e-2) 74 checkExKurtosis(t, i, x, f, 2e-1) 75 switch { 76 case f.Alpha < 0.3: 77 quadTol = 0.1 78 case f.Alpha < 1: 79 quadTol = 1e-3 80 default: 81 quadTol = 1e-10 82 } 83 checkProbContinuous(t, i, x, 0, math.Inf(1), f, quadTol) 84 checkQuantileCDFSurvival(t, i, x, f, 5e-2) 85 if f.Alpha < 1 { 86 if !math.IsNaN(f.Mode()) { 87 t.Errorf("Expected NaN mode for alpha < 1, got %v", f.Mode()) 88 } 89 } else { 90 checkMode(t, i, x, f, 2e-1, 1) 91 } 92 cdfNegX := f.CDF(-0.0001) 93 if cdfNegX != 0 { 94 t.Errorf("Expected zero CDF for negative argument, got %v", cdfNegX) 95 } 96 survNegX := f.Survival(-0.0001) 97 if survNegX != 1 { 98 t.Errorf("Expected survival function of 1 for negative argument, got %v", survNegX) 99 } 100 if f.NumParameters() != 2 { 101 t.Errorf("Mismatch in NumParameters: got %v, want 2", f.NumParameters()) 102 } 103 lPdf := f.LogProb(-0.0001) 104 if !math.IsInf(lPdf, -1) { 105 t.Errorf("Expected log(CDF) to be -Infinity for negative argument, got %v", lPdf) 106 } 107 } 108 109 func TestGammaPanics(t *testing.T) { 110 t.Parallel() 111 g := Gamma{1, 0, nil} 112 if !panics(func() { g.Rand() }) { 113 t.Errorf("Expected Rand panic for Beta <= 0") 114 } 115 g = Gamma{0, 1, nil} 116 if !panics(func() { g.Rand() }) { 117 t.Errorf("Expected Rand panic for Alpha <= 0") 118 } 119 } 120 121 func BenchmarkGammaRand(b *testing.B) { 122 src := rand.New(rand.NewSource(1)) 123 for i, g := range []Gamma{ 124 {Alpha: 0.1, Beta: 0.8, Src: src}, 125 {Alpha: 0.5, Beta: 0.8, Src: src}, 126 {Alpha: 1, Beta: 1, Src: src}, 127 {Alpha: 1.6, Beta: 0.4, Src: src}, 128 {Alpha: 30, Beta: 1.7, Src: src}, 129 } { 130 b.Run(fmt.Sprintf("case %d", i), func(b *testing.B) { 131 for i := 0; i < b.N; i++ { 132 g.Rand() 133 } 134 }) 135 } 136 }