using System; namespace MLAgents.InferenceBrain.Utils { /// /// RandomNormal - A random number generator that produces normally distributed random numbers using the Marsaglia /// polar method (https://en.wikipedia.org/wiki/Marsaglia_polar_method) /// TODO: worth overriding System.Random instead of aggregating? /// public class RandomNormal { private readonly double m_mean; private readonly double m_stddev; private readonly System.Random m_random; public RandomNormal(int seed, float mean = 0.0f, float stddev = 1.0f) { m_mean = mean; m_stddev = stddev; m_random = new System.Random(seed); } // Each iteration produces two numbers. Hold one here for next call private bool m_hasSpare = false; private double m_spare = 0.0f; /// /// Return the next random double number /// /// Next random double number public double NextDouble() { if (m_hasSpare) { m_hasSpare = false; return m_spare * m_stddev + m_mean; } double u, v, s; do { u = m_random.NextDouble() * 2.0 - 1.0; v = m_random.NextDouble() * 2.0 - 1.0; s = u * u + v * v; } while (s >= 1.0 || s == 0.0); s = Math.Sqrt(-2.0 * Math.Log(s) / 2); m_spare = u * s; m_hasSpare = true; return v * s * m_stddev + m_mean; } private void IncreaseNextDim(Array arr, long[] indices) { for (int i = 1; i < arr.Rank; ++i) { ++indices[i]; if (i == arr.Rank - 1 || indices[i] < arr.GetLength(i)) { break; } else { indices[i] = 0; } } } /// /// Fill a pre-allocated Tensor with random numbers /// /// The pre-allocated Tensor to fill /// Throws when trying to fill a Tensor of type other than float /// Throws when the Tensor is not allocated public void FillTensor(Tensor t) { if (t.DataType != typeof(float)) { throw new NotImplementedException("Random Normal does not support integer tensors yet!"); } if (t.Data == null) { throw new ArgumentNullException(); } long[] indices = new long[t.Data.Rank]; // Since IEnumerable is const, and we don't know the dimentions of the Array // we need to traverse all the dimentions // TODO: this seems like a nice general operation for the Tensor, consider moving it there do { t.Data.SetValue((float) NextDouble(), indices); ++indices[0]; if (indices[0] == t.Data.GetLength(0)) { indices[0] = 0; IncreaseNextDim(t.Data, indices); } } while (indices[t.Data.Rank - 1] < t.Data.GetLength(t.Data.Rank - 1)); } } }