github.com/gopherd/gonum@v0.0.4/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  	"math/rand"
    11  	"github.com/gopherd/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  }