gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/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.
    69  //
    70  // If dst is not nil, the bounds will be stored in-place into dst and returned,
    71  // otherwise a new slice will be allocated first. If dst is not nil, it must
    72  // have length equal to the dimension of the distribution.
    73  func (u *Uniform) Bounds(bounds []r1.Interval) []r1.Interval {
    74  	if bounds == nil {
    75  		bounds = make([]r1.Interval, u.Dim())
    76  	}
    77  	if len(bounds) != u.Dim() {
    78  		panic(badInputLength)
    79  	}
    80  	copy(bounds, u.bounds)
    81  	return bounds
    82  }
    83  
    84  // CDF returns the value of the multidimensional cumulative distribution
    85  // function of the probability distribution at the point x.
    86  //
    87  // If dst is not nil, the value will be stored in-place into dst and returned,
    88  // otherwise a new slice will be allocated first. If dst is not nil, it must
    89  // have length equal to the dimension of the distribution. CDF will also panic
    90  // if the length of x is not equal to the dimension of the distribution.
    91  func (u *Uniform) CDF(dst, x []float64) []float64 {
    92  	if len(x) != u.dim {
    93  		panic(badSizeMismatch)
    94  	}
    95  	dst = reuseAs(dst, u.dim)
    96  
    97  	for i, v := range x {
    98  		if v < u.bounds[i].Min {
    99  			dst[i] = 0
   100  		} else if v > u.bounds[i].Max {
   101  			dst[i] = 1
   102  		} else {
   103  			dst[i] = (v - u.bounds[i].Min) / (u.bounds[i].Max - u.bounds[i].Min)
   104  		}
   105  	}
   106  	return dst
   107  }
   108  
   109  // Dim returns the dimension of the distribution.
   110  func (u *Uniform) Dim() int {
   111  	return u.dim
   112  }
   113  
   114  // Entropy returns the differential entropy of the distribution.
   115  func (u *Uniform) Entropy() float64 {
   116  	// Entropy is log of the volume.
   117  	var logVol float64
   118  	for _, b := range u.bounds {
   119  		logVol += math.Log(b.Max - b.Min)
   120  	}
   121  	return logVol
   122  }
   123  
   124  // LogProb computes the log of the pdf of the point x.
   125  func (u *Uniform) LogProb(x []float64) float64 {
   126  	dim := u.dim
   127  	if len(x) != dim {
   128  		panic(badSizeMismatch)
   129  	}
   130  	var logprob float64
   131  	for i, b := range u.bounds {
   132  		if x[i] < b.Min || x[i] > b.Max {
   133  			return math.Inf(-1)
   134  		}
   135  		logprob -= math.Log(b.Max - b.Min)
   136  	}
   137  	return logprob
   138  }
   139  
   140  // Mean returns the mean of the probability distribution.
   141  //
   142  // If dst is not nil, the mean will be stored in-place into dst and returned,
   143  // otherwise a new slice will be allocated first. If dst is not nil, it must
   144  // have length equal to the dimension of the distribution.
   145  func (u *Uniform) Mean(dst []float64) []float64 {
   146  	dst = reuseAs(dst, u.dim)
   147  	for i, b := range u.bounds {
   148  		dst[i] = (b.Max + b.Min) / 2
   149  	}
   150  	return dst
   151  }
   152  
   153  // Prob computes the value of the probability density function at x.
   154  func (u *Uniform) Prob(x []float64) float64 {
   155  	return math.Exp(u.LogProb(x))
   156  }
   157  
   158  // Rand generates a random sample according to the distributon.
   159  //
   160  // If dst is not nil, the sample will be stored in-place into dst and returned,
   161  // otherwise a new slice will be allocated first. If dst is not nil, it must
   162  // have length equal to the dimension of the distribution.
   163  func (u *Uniform) Rand(dst []float64) []float64 {
   164  	dst = reuseAs(dst, u.dim)
   165  	if u.rnd == nil {
   166  		for i, b := range u.bounds {
   167  			dst[i] = rand.Float64()*(b.Max-b.Min) + b.Min
   168  		}
   169  		return dst
   170  	}
   171  	for i, b := range u.bounds {
   172  		dst[i] = u.rnd.Float64()*(b.Max-b.Min) + b.Min
   173  	}
   174  	return dst
   175  }
   176  
   177  // Quantile returns the value of the multi-dimensional inverse cumulative
   178  // distribution function at p.
   179  //
   180  // If dst is not nil, the quantile will be stored in-place into dst and
   181  // returned, otherwise a new slice will be allocated first. If dst is not nil,
   182  // it must have length equal to the dimension of the distribution. Quantile will
   183  // also panic if the length of p is not equal to the dimension of the
   184  // distribution.
   185  //
   186  // All of the values of p must be between 0 and 1, inclusive, or Quantile will
   187  // panic.
   188  func (u *Uniform) Quantile(dst, p []float64) []float64 {
   189  	if len(p) != u.dim {
   190  		panic(badSizeMismatch)
   191  	}
   192  	dst = reuseAs(dst, u.dim)
   193  	for i, v := range p {
   194  		if v < 0 || v > 1 {
   195  			panic(badQuantile)
   196  		}
   197  		dst[i] = v*(u.bounds[i].Max-u.bounds[i].Min) + u.bounds[i].Min
   198  	}
   199  	return dst
   200  }