// Copyright ©2020 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package distuv import ( "math" "golang.org/x/exp/rand" ) // AlphaStable represents an α-stable distribution with four parameters. // See https://en.wikipedia.org/wiki/Stable_distribution for more information. type AlphaStable struct { // Alpha is the stability parameter. // It is valid within the range 0 < α ≤ 2. Alpha float64 // Beta is the skewness parameter. // It is valid within the range -1 ≤ β ≤ 1. Beta float64 // C is the scale parameter. // It is valid when positive. C float64 // Mu is the location parameter. Mu float64 Src rand.Source } // ExKurtosis returns the excess kurtosis of the distribution. // ExKurtosis returns NaN when Alpha != 2. func (a AlphaStable) ExKurtosis() float64 { if a.Alpha == 2 { return 0 } return math.NaN() } // Mean returns the mean of the probability distribution. // Mean returns NaN when Alpha <= 1. func (a AlphaStable) Mean() float64 { if a.Alpha > 1 { return a.Mu } return math.NaN() } // Median returns the median of the distribution. // Median panics when Beta != 0, because then the mode is not analytically // expressible. func (a AlphaStable) Median() float64 { if a.Beta == 0 { return a.Mu } panic("distuv: cannot compute Median for Beta != 0") } // Mode returns the mode of the distribution. // Mode panics when Beta != 0, because then the mode is not analytically // expressible. func (a AlphaStable) Mode() float64 { if a.Beta == 0 { return a.Mu } panic("distuv: cannot compute Mode for Beta != 0") } // NumParameters returns the number of parameters in the distribution. func (a AlphaStable) NumParameters() int { return 4 } // Rand returns a random sample drawn from the distribution. func (a AlphaStable) Rand() float64 { // From https://en.wikipedia.org/wiki/Stable_distribution#Simulation_of_stable_variables const halfPi = math.Pi / 2 u := Uniform{-halfPi, halfPi, a.Src}.Rand() w := Exponential{1, a.Src}.Rand() if a.Alpha == 1 { f := halfPi + a.Beta*u x := (f*math.Tan(u) - a.Beta*math.Log(halfPi*w*math.Cos(u)/f)) / halfPi return a.C*(x+a.Beta*math.Log(a.C)/halfPi) + a.Mu } zeta := -a.Beta * math.Tan(halfPi*a.Alpha) xi := math.Atan(-zeta) / a.Alpha f := a.Alpha * (u + xi) g := math.Sqrt(1+zeta*zeta) * math.Pow(math.Cos(u-f)/w, 1-a.Alpha) / math.Cos(u) x := math.Pow(g, 1/a.Alpha) * math.Sin(f) return a.C*x + a.Mu } // Skewness returns the skewness of the distribution. // Skewness returns NaN when Alpha != 2. func (a AlphaStable) Skewness() float64 { if a.Alpha == 2 { return 0 } return math.NaN() } // StdDev returns the standard deviation of the probability distribution. func (a AlphaStable) StdDev() float64 { return math.Sqrt(a.Variance()) } // Variance returns the variance of the probability distribution. // Variance returns +Inf when Alpha != 2. func (a AlphaStable) Variance() float64 { if a.Alpha == 2 { return 2 * a.C * a.C } return math.Inf(1) }