stat/distuv: improve test coverage for Categorical distribution
MODIFY
stat/distuv/categorical_test.go
stat/distuv/categorical_test.go
@@ -13,6 +13,14 @@ import (
"gonum.org/v1/gonum/floats"
)
+const (
+ Tiny = 2
+ Small = 5
+ Medium = 10
+ Large = 100
+ Huge = 1000
+)
+
func TestCategoricalProb(t *testing.T) {
t.Parallel()
for _, test := range [][]float64{
@@ -26,19 +34,35 @@ func TestCategoricalProb(t *testing.T) {
if math.Abs(p-v) > 1e-14 {
t.Errorf("Probability mismatch element %d", i)
}
+ logP := dist.LogProb(float64(i))
+ if math.Abs(logP-math.Log(v)) > 1e-14 {
+ t.Errorf("Log-probability mismatch element %d", i)
+ }
p = dist.Prob(float64(i) + 0.5)
if p != 0 {
t.Errorf("Non-zero probability for non-integer x")
}
+ logP = dist.LogProb(float64(i) + 0.5)
+ if !math.IsInf(logP, -1) {
+ t.Errorf("Log-probability for non-integer x is not -Inf")
+ }
}
p := dist.Prob(-1)
if p != 0 {
t.Errorf("Non-zero probability for -1")
}
+ logP := dist.LogProb(-1)
+ if !math.IsInf(logP, -1) {
+ t.Errorf("Log-probability for -1 is not -Inf")
+ }
p = dist.Prob(float64(len(test)))
if p != 0 {
t.Errorf("Non-zero probability for len(test)")
}
+ logP = dist.LogProb(float64(len(test)))
+ if !math.IsInf(logP, -1) {
+ t.Errorf("Log-probability for len(test) is not -Inf")
+ }
}
}
@@ -89,6 +113,33 @@ func TestCategoricalRand(t *testing.T) {
}
}
+func TestCategoricalReweight(t *testing.T) {
+ dist := NewCategorical([]float64{1, 1}, nil)
+ if !panics(func() { dist.Reweight(0, -1) }) {
+ t.Errorf("Reweight did not panic for negative weight")
+ }
+ dist.Reweight(0, 0)
+ if !panics(func() { dist.Reweight(1, 0) }) {
+ t.Errorf("Reweight did not panic when trying to set the last positive weight to zero")
+ }
+}
+
+func TestCategoricalReweightAll(t *testing.T) {
+ w := []float64{0, 1, 2, 1}
+ dist := NewCategorical(w, nil)
+ if !panics(func() { dist.ReweightAll([]float64{1, 1}) }) {
+ t.Errorf("ReweightAll did not panic for different number of weights")
+ }
+ w[0] = -1
+ if !panics(func() { dist.ReweightAll(w) }) {
+ t.Errorf("ReweightAll did not panic for a negative weight")
+ }
+ w = []float64{0, 0, 0, 0}
+ if !panics(func() { dist.ReweightAll(w) }) {
+ t.Errorf("ReweightAll did not panic for weights which are all zero")
+ }
+}
+
func sampleCategorical(t *testing.T, dist Categorical, nSamples int) []float64 {
counts := make([]float64, dist.Len())
for i := 0; i < nSamples; i++ {
@@ -200,3 +251,22 @@ func TestCategoricalMean(t *testing.T) {
}
}
}
+
+func BenchmarkCategoricalRandTiny(b *testing.B) { benchmarkCategoricalRand(b, Tiny) }
+func BenchmarkCategoricalRandSmall(b *testing.B) { benchmarkCategoricalRand(b, Small) }
+func BenchmarkCategoricalRandMedium(b *testing.B) { benchmarkCategoricalRand(b, Medium) }
+func BenchmarkCategoricalRandLarge(b *testing.B) { benchmarkCategoricalRand(b, Large) }
+func BenchmarkCategoricalRandHuge(b *testing.B) { benchmarkCategoricalRand(b, Huge) }
+
+func benchmarkCategoricalRand(b *testing.B, size int) {
+ src := rand.NewSource(1)
+ rng := rand.New(src)
+ weights := make([]float64, size)
+ for i := 0; i < size; i++ {
+ weights[i] = rng.Float64() + 0.001
+ }
+ dist := NewCategorical(weights, src)
+ for i := 0; i < b.N; i++ {
+ dist.Rand()
+ }
+}

stat/distuv: improve test coverage for Categorical distribution