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

     1  // Copyright ©2021 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  	"testing"
    10  
    11  	"gonum.org/v1/gonum/floats/scalar"
    12  )
    13  
    14  func TestLogisticParameters(t *testing.T) {
    15  	t.Parallel()
    16  
    17  	var want float64
    18  
    19  	l := Logistic{Mu: 1, S: 0}
    20  
    21  	want = 2
    22  	if result := l.NumParameters(); result != int(want) {
    23  		t.Errorf("Wrong number of parameters: %d != %.0f", result, want)
    24  	}
    25  
    26  	want = 6.0 / 5.0
    27  	if result := l.ExKurtosis(); result != want {
    28  		t.Errorf("Wrong excess kurtosis: %f != %f", result, want)
    29  	}
    30  
    31  	want = 0.0
    32  	if result := l.Skewness(); result != want {
    33  		t.Errorf("Wrong skewness: %f != %f", result, want)
    34  	}
    35  
    36  	want = l.Mu
    37  	if result := l.Mean(); result != want {
    38  		t.Errorf("Wrong mean value: %f != %f", result, want)
    39  	}
    40  
    41  	want = l.Mu
    42  	if result := l.Median(); result != want {
    43  		t.Errorf("Wrong median value: %f != %f", result, want)
    44  	}
    45  
    46  	want = l.Mu
    47  	if result := l.Mode(); result != want {
    48  		t.Errorf("Wrong mode value: %f != %f", result, want)
    49  	}
    50  }
    51  
    52  func TestLogisticStdDev(t *testing.T) {
    53  	t.Parallel()
    54  
    55  	l := Logistic{Mu: 0, S: sqrt3 / math.Pi}
    56  
    57  	want := 1.0
    58  	if result := l.StdDev(); !scalar.EqualWithinAbs(result, want, 1e-10) {
    59  		t.Errorf("Wrong StdDev with Mu=%f, S=%f: %f != %f", l.Mu, l.S, result, want)
    60  	}
    61  
    62  	want = 1.0
    63  	if result := l.Variance(); !scalar.EqualWithinAbs(result, want, 1e-10) {
    64  		t.Errorf("Wrong Variance with Mu=%f, S=%f: %f != %f", l.Mu, l.S, result, want)
    65  	}
    66  }
    67  
    68  func TestLogisticCDF(t *testing.T) {
    69  	t.Parallel()
    70  
    71  	// Values for "want" are taken from WolframAlpha: CDF[LogisticDistribution[mu,s], input] to 10 digits.
    72  	for _, v := range []struct {
    73  		mu, s, input, want float64
    74  	}{
    75  		{0.0, 0.0, 1.0, 1.0},
    76  		{0.0, 1.0, 0.0, 0.5},
    77  		{-0.5, 1.0, 0.0, 0.6224593312},
    78  		{69.0, 420.0, 42.0, 0.4839341039},
    79  	} {
    80  		l := Logistic{Mu: v.mu, S: v.s}
    81  		if result := l.CDF(v.input); !scalar.EqualWithinAbs(result, v.want, 1e-10) {
    82  			t.Errorf("Wrong CDF(%f) with Mu=%f, S=%f: %f != %f", v.input, l.Mu, l.S, result, v.want)
    83  		}
    84  	}
    85  
    86  	// Edge case of zero in denominator.
    87  	l := Logistic{Mu: 0, S: 0}
    88  
    89  	input := 0.0
    90  	if result := l.CDF(input); !math.IsNaN(result) {
    91  		t.Errorf("Wrong CDF(%f) with Mu=%f, S=%f: %f is not NaN", input, l.Mu, l.S, result)
    92  	}
    93  }
    94  
    95  // TestLogisticSurvival doesn't need excessive testing since it's just 1-CDF.
    96  func TestLogisticSurvival(t *testing.T) {
    97  	t.Parallel()
    98  
    99  	l := Logistic{Mu: 0, S: 1}
   100  
   101  	input, want := 0.0, 0.5
   102  	if result := l.Survival(input); result != want {
   103  		t.Errorf("Wrong Survival(%f) with Mu=%f, S=%f: %f != %f", input, l.Mu, l.S, result, want)
   104  	}
   105  }
   106  
   107  func TestLogisticProb(t *testing.T) {
   108  	t.Parallel()
   109  
   110  	// Values for "want" are taken from WolframAlpha: PDF[LogisticDistribution[mu,s], input] to 10 digits.
   111  	for _, v := range []struct {
   112  		mu, s, input, want float64
   113  	}{
   114  		{0.0, 1.0, 0.0, 0.25},
   115  		{-0.5, 1.0, 0.0, 0.2350037122},
   116  		{69.0, 420.0, 42.0, 0.0005946235404},
   117  	} {
   118  		l := Logistic{Mu: v.mu, S: v.s}
   119  		if result := l.Prob(v.input); !scalar.EqualWithinAbs(result, v.want, 1e-10) {
   120  			t.Errorf("Wrong Prob(%f) with Mu=%f, S=%f: %.09f != %.09f", v.input, l.Mu, l.S, result, v.want)
   121  		}
   122  	}
   123  
   124  	// Edge case of zero in denominator.
   125  	l := Logistic{Mu: 0, S: 0}
   126  
   127  	input := 0.0
   128  	if result := l.Prob(input); !math.IsNaN(result) {
   129  		t.Errorf("Wrong Prob(%f) with Mu=%f, S=%f: %f is not NaN", input, l.Mu, l.S, result)
   130  	}
   131  
   132  	input = 1.0
   133  	if result := l.Prob(input); !math.IsNaN(result) {
   134  		t.Errorf("Wrong Prob(%f) with Mu=%f, S=%f: %f is not NaN", input, l.Mu, l.S, result)
   135  	}
   136  }
   137  
   138  func TestLogisticLogProb(t *testing.T) {
   139  	t.Parallel()
   140  
   141  	l := Logistic{Mu: 0, S: 1}
   142  
   143  	input, want := 0.0, -math.Log(4)
   144  	if result := l.LogProb(input); result != want {
   145  		t.Errorf("Wrong LogProb(%f) with Mu=%f, S=%f: %f != %f", input, l.Mu, l.S, result, want)
   146  	}
   147  }
   148  
   149  func TestQuantile(t *testing.T) {
   150  	t.Parallel()
   151  
   152  	for _, v := range []struct {
   153  		mu, s, input, want float64
   154  	}{
   155  		{0.0, 1.0, 0.5, 0.0},
   156  		{0.0, 1.0, 0.0, math.Inf(-1)},
   157  		{0.0, 1.0, 1.0, math.Inf(+1)},
   158  	} {
   159  		l := Logistic{Mu: v.mu, S: v.s}
   160  		if result := l.Quantile(v.input); result != v.want {
   161  			t.Errorf("Wrong Quantile(%f) with Mu=%f, S=%f: %f != %f", v.input, l.Mu, l.S, result, v.want)
   162  		}
   163  	}
   164  
   165  	// Edge case with NaN.
   166  	l := Logistic{Mu: 0, S: 0}
   167  
   168  	input := 0.0
   169  	if result := l.Quantile(input); !math.IsNaN(result) {
   170  		t.Errorf("Wrong Quantile(%f) with Mu=%f, S=%f: %f is not NaN", input, l.Mu, l.S, result)
   171  	}
   172  }