gonum.org/v1/gonum@v0.14.0/stat/distmv/uniform.go (about) 1 // Copyright ©2015 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 distmv 6 7 import ( 8 "math" 9 10 "golang.org/x/exp/rand" 11 "gonum.org/v1/gonum/spatial/r1" 12 ) 13 14 // Uniform represents a multivariate uniform distribution. 15 type Uniform struct { 16 bounds []r1.Interval 17 dim int 18 rnd *rand.Rand 19 } 20 21 // NewUniform creates a new uniform distribution with the given bounds. 22 func NewUniform(bnds []r1.Interval, src rand.Source) *Uniform { 23 dim := len(bnds) 24 if dim == 0 { 25 panic(badZeroDimension) 26 } 27 for _, b := range bnds { 28 if b.Max < b.Min { 29 panic("uniform: maximum less than minimum") 30 } 31 } 32 u := &Uniform{ 33 bounds: make([]r1.Interval, dim), 34 dim: dim, 35 } 36 if src != nil { 37 u.rnd = rand.New(src) 38 } 39 for i, b := range bnds { 40 u.bounds[i].Min = b.Min 41 u.bounds[i].Max = b.Max 42 } 43 return u 44 } 45 46 // NewUnitUniform creates a new Uniform distribution over the dim-dimensional 47 // unit hypercube. That is, a uniform distribution where each dimension has 48 // Min = 0 and Max = 1. 49 func NewUnitUniform(dim int, src rand.Source) *Uniform { 50 if dim <= 0 { 51 panic(nonPosDimension) 52 } 53 bounds := make([]r1.Interval, dim) 54 for i := range bounds { 55 bounds[i].Min = 0 56 bounds[i].Max = 1 57 } 58 u := Uniform{ 59 bounds: bounds, 60 dim: dim, 61 } 62 if src != nil { 63 u.rnd = rand.New(src) 64 } 65 return &u 66 } 67 68 // Bounds returns the bounds on the variables of the distribution. If the input 69 // is nil, a new slice is allocated and returned. If the input is non-nil, then 70 // the bounds are stored in-place into the input argument, and Bounds will panic 71 // if len(bounds) != u.Dim(). 72 func (u *Uniform) Bounds(bounds []r1.Interval) []r1.Interval { 73 if bounds == nil { 74 bounds = make([]r1.Interval, u.Dim()) 75 } 76 if len(bounds) != u.Dim() { 77 panic(badInputLength) 78 } 79 copy(bounds, u.bounds) 80 return bounds 81 } 82 83 // CDF returns the multidimensional cumulative distribution function of the 84 // probability distribution at the point x. If p is non-nil, the CDF is stored 85 // in-place into the first argument, otherwise a new slice is allocated and 86 // returned. 87 // 88 // CDF will panic if len(x) is not equal to the dimension of the distribution, 89 // or if p is non-nil and len(p) is not equal to the dimension of the distribution. 90 func (u *Uniform) CDF(p, x []float64) []float64 { 91 if len(x) != u.dim { 92 panic(badSizeMismatch) 93 } 94 if p == nil { 95 p = make([]float64, u.dim) 96 } 97 if len(p) != u.dim { 98 panic(badSizeMismatch) 99 } 100 for i, v := range x { 101 if v < u.bounds[i].Min { 102 p[i] = 0 103 } else if v > u.bounds[i].Max { 104 p[i] = 1 105 } else { 106 p[i] = (v - u.bounds[i].Min) / (u.bounds[i].Max - u.bounds[i].Min) 107 } 108 } 109 return p 110 } 111 112 // Dim returns the dimension of the distribution. 113 func (u *Uniform) Dim() int { 114 return u.dim 115 } 116 117 // Entropy returns the differential entropy of the distribution. 118 func (u *Uniform) Entropy() float64 { 119 // Entropy is log of the volume. 120 var logVol float64 121 for _, b := range u.bounds { 122 logVol += math.Log(b.Max - b.Min) 123 } 124 return logVol 125 } 126 127 // LogProb computes the log of the pdf of the point x. 128 func (u *Uniform) LogProb(x []float64) float64 { 129 dim := u.dim 130 if len(x) != dim { 131 panic(badSizeMismatch) 132 } 133 var logprob float64 134 for i, b := range u.bounds { 135 if x[i] < b.Min || x[i] > b.Max { 136 return math.Inf(-1) 137 } 138 logprob -= math.Log(b.Max - b.Min) 139 } 140 return logprob 141 } 142 143 // Mean returns the mean of the probability distribution at x. If the 144 // input argument is nil, a new slice will be allocated, otherwise the result 145 // will be put in-place into the receiver. 146 func (u *Uniform) Mean(x []float64) []float64 { 147 x = reuseAs(x, u.dim) 148 for i, b := range u.bounds { 149 x[i] = (b.Max + b.Min) / 2 150 } 151 return x 152 } 153 154 // Prob computes the value of the probability density function at x. 155 func (u *Uniform) Prob(x []float64) float64 { 156 return math.Exp(u.LogProb(x)) 157 } 158 159 // Rand generates a random number according to the distributon. 160 // If the input slice is nil, new memory is allocated, otherwise the result is stored 161 // in place. 162 func (u *Uniform) Rand(x []float64) []float64 { 163 x = reuseAs(x, u.dim) 164 if u.rnd == nil { 165 for i, b := range u.bounds { 166 x[i] = rand.Float64()*(b.Max-b.Min) + b.Min 167 } 168 return x 169 } 170 for i, b := range u.bounds { 171 x[i] = u.rnd.Float64()*(b.Max-b.Min) + b.Min 172 } 173 return x 174 } 175 176 // Quantile returns the multi-dimensional inverse cumulative distribution function. 177 // len(x) must equal len(p), and if x is non-nil, len(x) must also equal len(p). 178 // If x is nil, a new slice will be allocated and returned, otherwise the quantile 179 // will be stored in-place into x. All of the values of p must be between 0 and 1, 180 // or Quantile will panic. 181 func (u *Uniform) Quantile(x, p []float64) []float64 { 182 if len(p) != u.dim { 183 panic(badSizeMismatch) 184 } 185 if x == nil { 186 x = make([]float64, u.dim) 187 } 188 if len(x) != u.dim { 189 panic(badSizeMismatch) 190 } 191 for i, v := range p { 192 if v < 0 || v > 1 { 193 panic(badQuantile) 194 } 195 x[i] = v*(u.bounds[i].Max-u.bounds[i].Min) + u.bounds[i].Min 196 } 197 return x 198 }