Neodroid  0.2.0
Machine Learning Environment Prototyping Tool
Distributions.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using UnityEngine;
4 using Random = UnityEngine.Random;
5 
6 namespace droid.Runtime.Utilities.Sampling {
7  [Serializable]
8  public enum DistributionEnum {
12  Uniform_,
13 
17  Normal_,
18 
22  Sloped_,
23 
28 
32  Linear_
33  }
34 
37  [Serializable]
38  public struct DistributionSampler {
39  Distributions.ConfidenceLevel _conf_level;
40  public Distributions.DirectionE _Direction;
41 
42  public float _factor;
43 
44  [SerializeField] DistributionEnum _de;
45 
46  public DistributionSampler(DistributionEnum distribution_enum = DistributionEnum.Uniform_,
47  Distributions.DirectionE d = Distributions.DirectionE.Left_) {
48  this._de = distribution_enum;
49  _conf_level = Distributions.ConfidenceLevel._95;
50  _Direction = d;
51  _factor = 1.267291f;
52  }
53 
61  public float Range(float min, float max) {
62  switch (this._de) {
63  case DistributionEnum.Uniform_:
64  return Random.Range(min, max);
65  case DistributionEnum.Normal_:
66  return Distributions.RandomRangeNormalDistribution(min, max, this._conf_level);
67  case DistributionEnum.Sloped_:
68  return Distributions.RandomRangeSlope(min, max, this._factor, this._Direction);
69  case DistributionEnum.Exponential_:
70  return Distributions.RandomRangeExponential(min, max, this._factor, this._Direction);
71  case DistributionEnum.Linear_:
72  return Distributions.RandomLinear(_factor);
73  default:
74  return Random.Range(min, max);
75  }
76 
77  //
78  }
79  }
80 
81  public static class Distributions {
82  //--------------------------------------------------------------------------------------------
83  // Normal Distribution
84  //--------------------------------------------------------------------------------------------
85 
86  public enum ConfidenceLevel {
87  _60 = 0,
88  _80,
89  _90,
90  _95,
91  _98,
92  _99,
93  _998,
94  _999
95  }
96 
97  //--------------------------------------------------------------------------------------------
98  // Sloped Distribution (sec^2 distribution)
99  //--------------------------------------------------------------------------------------------
100 
103  public enum DirectionE {
104  Right_,
105  Left_
106  }
107 
108  static float[] _confidence_to_z_score = {
109  0.84162123f,
110  1.28155156f,
111  1.64485363f,
112  1.95996399f,
113  2.32634787f,
114  2.57582931f,
115  3.0902323f,
116  3.29052673f
117  };
118 
141  public static float RandomRangeNormalDistribution(float min,
142  float max,
143  ConfidenceLevel
144  confidence_level_cutoff /*, float confidence_level_cutoff*/) {
145  var mean = 0.5f * (min + max);
146 
147  // TODO formula for this?
148  var z_score_cutoff = _confidence_to_z_score[(int)confidence_level_cutoff];
149 
150  var new_width = (max - min) / 2.0f;
151  var sigma = new_width / z_score_cutoff;
152 
153  // Get random normal from Normal Distribution that's within the confidence level cutoff requested
154  float random_normal_num;
155  do {
156  random_normal_num = RandomNormalDistribution(mean, sigma);
157  } while (random_normal_num > max || random_normal_num < min);
158 
159  // now you have a number selected from a bell curve stretching from min to max!
160  return random_normal_num;
161  }
162 
176  public static float RandomNormalDistribution(float mean, float std_dev) {
177  // Get random normal from Standard Normal Distribution
178  var random_normal_num = RandomFromStandardNormalDistribution();
179 
180  // Stretch distribution to the requested sigma variance
181  random_normal_num *= std_dev;
182 
183  // Shift mean to requested mean:
184  random_normal_num += mean;
185 
186  // now you have a number selected from a normal distribution with requested mean and sigma!
187  return random_normal_num;
188  }
189 
197  public static float RandomFromStandardNormalDistribution() {
198  // This code follows the polar form of the muller transform:
199  // https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform#Polar_form
200  // also known as Marsaglia polar method
201  // https://en.wikipedia.org/wiki/Marsaglia_polar_method
202 
203  // calculate points on a circle
204  float u, v;
205 
206  float s; // this is the hypotenuse squared.
207  do {
208  u = Random.Range(-1f, 1f);
209  v = Random.Range(-1f, 1f);
210  s = u * u + v * v;
211  } while (!(s != 0 && s < 1)); // keep going until s is nonzero and less than one
212 
213  // TODO allow a user to specify how many random numbers they want!
214  // choose between u and v for seed (z0 vs z1)
215  float seed;
216  if (Random.Range(0, 2) == 0) {
217  seed = u;
218  } else {
219  seed = v;
220  }
221 
222  // create normally distributed number.
223  var z = seed * Mathf.Sqrt(-2.0f * Mathf.Log(s) / s);
224 
225  return z;
226  }
227 
233  public static float RandomRangeSlope(float min, float max, float skew, DirectionE direction) {
234  return min + RandomFromSlopedDistribution(skew, direction) * (max - min);
235  }
236 
242  public static float RandomFromSlopedDistribution(float skew, DirectionE direction) {
243  // the difference in scale is just the same as the max y-value..
244  var max_y = skew;
245 
246  // our curve will go from 0 to max_x.
247  var max_x = Inverse_Sec_Sqrd(max_y);
248 
249  var max_cdf = Sec_Sqrd_CumulativeDistributionFunction(max_x);
250 
251  var u = Random.Range(0.0f, max_cdf);
252  var x_val = Sec_Sqrd_InverseCumulativeDistributionFunction(u);
253 
254  // scale to [0,1]
255  var value = x_val / max_x;
256 
257  if (direction == DirectionE.Left_) {
258  value = 1.0f - value;
259  }
260 
261  return value;
262  }
263 
270  static float Inverse_Sec_Sqrd(float y) {
271  // Note: arcsec(x) = arccos(1/x)
272 
273  // return arcsec(sqrt(y))
274  return Mathf.Acos(1.0f / Mathf.Sqrt(y));
275  }
276 
277  // The integral of sec^2
278  static float Sec_Sqrd_CumulativeDistributionFunction(float x) {
279  // The cumulative distribution function for sec^2 is just the definite integral of sec^2(x) = tan(x) - tan(0) = tan(x)
280 
281  return Mathf.Tan(x);
282  }
283 
284  // The inverse of the integral of sec^2
285  static float Sec_Sqrd_InverseCumulativeDistributionFunction(float x) {
286  // The cumulative distribution function for sec^2 is just the definite integral of sec^2(x) = tan(x) - tan(0) = tan(x)
287  // Then the Inverse cumulative distribution function is just atan(x)
288 
289  return Mathf.Atan(x);
290  }
291 
292  //--------------------------------------------------------------------------------------------
293  // Linear Distribution
294  //--------------------------------------------------------------------------------------------
295 
296  // Returns random in range [min, max] with linear distribution of given slope.
297  public static float RandomRangeLinear(float min, float max, float slope) {
298  if (slope == 0) {
299  return Random.Range(min, max);
300  }
301 
302  var val = RandomLinear(slope);
303 
304  return min + (max - min) * val;
305  }
306 
307  // Returns random in range [0,1] with linear distribution of given slope.
308  public static float RandomLinear(float slope) {
309  var abs_value = RandomFromLinearWithPositiveSlope(Mathf.Abs(slope));
310  if (slope < 0) {
311  return 1 - abs_value;
312  }
313 
314  return abs_value;
315  }
316 
317  // Returns random in range [0,1] with linear distribution of given slope.
318  static float RandomFromLinearWithPositiveSlope(float slope) {
319  if (slope == 0) {
320  return Random.Range(0.0f, 1.0f);
321  }
322 
323  float x, y;
324  do {
325  x = Random.Range(0.0f, 1.0f);
326  y = Random.Range(0.0f, 1.0f);
327  if (slope < 1) {
328  y -= (1 - slope) / 2.0f;
329  }
330  } while (y > x * slope);
331 
332  return x;
333  }
334 
335  //--------------------------------------------------------------------------------------------
336  // Exponential Distribution
337  //--------------------------------------------------------------------------------------------
338 
350  public static float RandomRangeExponential(float min, float max, float exponent, DirectionE direction) {
351  return min + RandomFromExponentialDistribution(exponent, direction) * (max - min);
352  }
353 
363  public static float RandomFromExponentialDistribution(float exponent, DirectionE direction) {
364  // our curve will go from 0 to 1.
365  var max_cdf = ExponentialRightCdf(1.0f, exponent);
366 
367  var u = Random.Range(0.0f, max_cdf);
368  var x_val = EponentialRightInverseCdf(u, exponent);
369 
370  if (direction == DirectionE.Left_) {
371  x_val = 1.0f - x_val;
372  }
373 
374  return x_val;
375  }
376 
377  // The inverse of the curve.
378  static float ExponentialRightInverse(float y, float exponent) { return Mathf.Pow(y, 1.0f / exponent); }
379 
380  // The integral of the exponent curve.
381  static float ExponentialRightCdf(float x, float exponent) {
382  var integral_exp = exponent + 1.0f;
383  return Mathf.Pow(x, integral_exp) / integral_exp;
384  }
385 
386  // The inverse of the integral of the exponent curve.
387  static float EponentialRightInverseCdf(float x, float exponent) {
388  var integral_exp = exponent + 1.0f;
389  return Mathf.Pow(integral_exp * x, 1.0f / integral_exp);
390  }
391 
392  //--------------------------------------------------------------------------------------------
393  // User-Defined Probability Distribution Function
394  //--------------------------------------------------------------------------------------------
395 
405  public static int RandomChoiceFollowingDistribution(List<float> probabilities) {
406  // Sum to create CDF:
407  var cdf = new float[probabilities.Count];
408  float sum = 0;
409  for (var i = 0; i < probabilities.Count; ++i) {
410  cdf[i] = sum + probabilities[i];
411  sum = cdf[i];
412  }
413 
414  // Choose from CDF:
415  var cdf_value = Random.Range(0.0f, cdf[probabilities.Count - 1]);
416  var index = Array.BinarySearch(cdf, cdf_value);
417 
418  if (index < 0) {
419  index = ~index; // if not found (probably won't be) BinarySearch returns bitwise complement of next-highest index.
420  }
421 
422  return index;
423  }
424  }
425 }
DistributionSampler(DistributionEnum distribution_enum=DistributionEnum.Uniform_, Distributions.DirectionE d=Distributions.DirectionE.Left_)