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