Skip to content

Commit 65f741f

Browse files
committed
Reimplemented numpy.random.
This reimplementation mimics entirely how the python version works. Including random state persistance. - added rand(params int[] size) - added rand(Shape shape) - added uniform (not tested yet) - added np.random.get_state() - added np.random.set_state() - added np.random.seed()
1 parent ca76c20 commit 65f741f

10 files changed

Lines changed: 275 additions & 14 deletions

File tree

src/NumSharp.Core/APIs/np.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static partial class np
2222
public static Type float32 => typeof(float); // Single
2323
public static Type float64 => typeof(double);
2424
public static Type chars => typeof(string);
25-
public static NumPyRandom random => new NumPyRandom();
25+
public static NumPyRandom random { get; } = new NumPyRandom();
2626

2727
// np.nan
2828
public static double nan => double.NaN;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.IO;
3+
using System.Runtime.Serialization.Formatters.Binary;
4+
5+
namespace NumSharp
6+
{
7+
internal static class RandomStateExtensions
8+
{
9+
public static NativeRandomState Save(this System.Random random)
10+
{
11+
var binaryFormatter = new BinaryFormatter();
12+
using (var temp = new MemoryStream())
13+
{
14+
binaryFormatter.Serialize(temp, random);
15+
return new NativeRandomState(temp.ToArray());
16+
}
17+
}
18+
19+
public static System.Random Restore(this NativeRandomState state)
20+
{
21+
var binaryFormatter = new BinaryFormatter();
22+
using (var temp = new MemoryStream(state.State))
23+
{
24+
return (System.Random)binaryFormatter.Deserialize(temp);
25+
}
26+
}
27+
}
28+
29+
/// <summary>
30+
/// Represents the stored state of <see cref="Random"/>.
31+
/// </summary>
32+
[Serializable]
33+
public struct NativeRandomState
34+
{
35+
public readonly byte[] State;
36+
37+
public NativeRandomState(byte[] state)
38+
{
39+
State = state;
40+
}
41+
}
42+
}

src/NumSharp.Core/Random/np.random.cs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,88 @@
55

66
namespace NumSharp
77
{
8+
/// <summary>
9+
/// A class that serves as numpy.random.RandomState in python.
10+
/// </summary>
11+
/// <remarks>https://docs.scipy.org/doc/numpy-1.16.1/reference/routines.random.html</remarks>
812
public partial class NumPyRandom
913
{
10-
public static int Seed { get; set; }
14+
internal Random randomizer;
1115

12-
16+
public int Seed { get; set; }
17+
18+
#region Constructors
19+
20+
internal NumPyRandom(Random randomizer)
21+
{
22+
this.randomizer = randomizer;
23+
}
24+
25+
internal NumPyRandom(NativeRandomState nativeRandomState)
26+
{
27+
set_state(nativeRandomState);
28+
}
29+
30+
internal NumPyRandom(int seed) : this(new Random(seed)) { }
31+
32+
internal NumPyRandom() : this(new Random()) { }
33+
34+
#endregion
35+
36+
#region RandomState
37+
38+
/// <summary>
39+
/// Returns a new instance of <see cref="NumPyRandom"/>.
40+
/// </summary>
41+
public NumPyRandom RandomState()
42+
{
43+
return new NumPyRandom();
44+
}
45+
46+
/// <summary>
47+
/// Returns a new instance of <see cref="NumPyRandom"/>.
48+
/// </summary>
49+
public NumPyRandom RandomState(int seed)
50+
{
51+
return new NumPyRandom(seed);
52+
}
53+
54+
/// <summary>
55+
/// Returns a new instance of <see cref="NumPyRandom"/>.
56+
/// </summary>
57+
public NumPyRandom RandomState(NativeRandomState state)
58+
{
59+
return new NumPyRandom(state);
60+
}
61+
62+
#endregion
63+
64+
/// <summary>
65+
/// Seeds the generator.
66+
/// It can be called again to re-seed the generator.
67+
/// </summary>
68+
public void seed(int seed)
69+
{
70+
randomizer = new System.Random(seed);
71+
}
72+
73+
/// <summary>
74+
/// Set the internal state of the generator from a <see cref="NumPyRandom"/>.
75+
/// for use if one has reason to manually (re-)set the internal state of the pseudo-random number generating algorithm.
76+
/// </summary>
77+
/// <param name="nativeRandomState">The state to restore onto this <see cref="NumPyRandom"/></param>
78+
public void set_state(NativeRandomState nativeRandomState)
79+
{
80+
randomizer = nativeRandomState.Restore();
81+
}
82+
83+
/// <summary>
84+
/// Return a <see cref="NumPyRandom"/> representing the internal state of the generator.
85+
/// </summary>
86+
/// <returns></returns>
87+
public NativeRandomState get_state()
88+
{
89+
return randomizer.Save();
90+
}
1391
}
1492
}

src/NumSharp.Core/Random/np.random.permutation.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@ public partial class NumPyRandom
88
{
99
public NDArray permutation(int max)
1010
{
11-
var random = new Random();
1211
int[] orders = new int[max];
1312

1413
var nd = np.arange(max);
1514

1615
for (int i = 0; i < max; i++)
1716
{
18-
var pos = random.Next(0, max);
17+
var pos = randomizer.Next(0, max);
1918
var zero = nd.Data<int>(0);
2019
nd[0] = nd.Data<int>(pos);
2120
nd[pos] = zero;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Text;
6+
using NumSharp.Generic;
7+
8+
namespace NumSharp
9+
{
10+
public partial class NumPyRandom
11+
{
12+
/// <summary>
13+
/// Random values in a given shape.
14+
/// Create an array of the given shape and populate it with random samples from a uniform distribution over [0, 1).
15+
/// </summary>
16+
/// <remarks>This is a convenience function. If you want an interface that takes a shape-tuple as the first argument, refer to np.random.random_sample.</remarks>
17+
public NDArray rand(params int[] size) {
18+
return rand(new Shape(size));
19+
}
20+
21+
/// <summary>
22+
/// Return random floats in the half-open interval [0.0, 1.0).
23+
/// Results are from the “continuous uniform” distribution over the stated interval. To sample Unif[a, b), b > a multiply the output of random_sample by (b-a) and add a:
24+
/// </summary>
25+
/// <param name="size">The samples</param>
26+
/// <returns></returns>
27+
public NDArray random_sample(params int[] size)
28+
{
29+
return random_sample(new Shape(size));
30+
}
31+
32+
/// <summary>
33+
/// Return random floats in the half-open interval [0.0, 1.0).
34+
/// Results are from the “continuous uniform” distribution over the stated interval. To sample Unif[a, b), b > a multiply the output of random_sample by (b-a) and add a:
35+
/// </summary>
36+
/// <param name="shape">The shape to randomize</param>
37+
/// <returns></returns>
38+
public NDArray random_sample(Shape shape)
39+
{
40+
return null; //todo https://www.youtube.com/watch?v=-qt8CPIadWQ
41+
}
42+
43+
//todo get proper documentation form pyhon docs.
44+
45+
/// <summary>
46+
/// Creates random array of given shape
47+
/// </summary>
48+
public NDArray rand(Shape shape) {
49+
NDArray ndArray = new NDArray(typeof(double), shape);
50+
double[] numArray = ndArray.Data<double>();
51+
for (int index = 0; index < ndArray.size; ++index) {
52+
numArray[index] = randomizer.NextDouble();
53+
}
54+
55+
ndArray.SetData<double[]>(numArray);
56+
return ndArray;
57+
}
58+
59+
}
60+
}

src/NumSharp.Core/Random/np.random.randint.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ public partial class NumPyRandom
1010
{
1111
public NDArray randint(int low, int size = 1)
1212
{
13-
var rng = new Random();
1413
var data = new int[size];
1514
for (int i = 0; i < data.Length; i++)
1615
{
17-
data[i] = rng.Next(low, int.MaxValue);
16+
data[i] = randomizer.Next(low, int.MaxValue);
1817
}
1918

2019
var np = new NDArray(typeof(int), size);
@@ -25,7 +24,6 @@ public NDArray randint(int low, int size = 1)
2524

2625
public NDArray randint(int low, int? high = null, Shape shape = null)
2726
{
28-
var rng = new Random();
2927
if(high == null)
3028
{
3129
high = int.MaxValue;
@@ -37,7 +35,7 @@ public NDArray randint(int low, int? high = null, Shape shape = null)
3735
var data = new int[shape.Size];
3836
for(int i = 0; i < data.Length; i++)
3937
{
40-
data[i] = rng.Next(low, high.Value);
38+
data[i] = randomizer.Next(low, high.Value);
4139
}
4240

4341
var np = new NDArray(typeof(int), shape.Dimensions.ToArray());

src/NumSharp.Core/Random/np.random.randn.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
35
using System.Text;
6+
using NumSharp.Generic;
47

58
namespace NumSharp
69
{
@@ -43,16 +46,15 @@ public T randn<T>()
4346
public NDArray normal(double loc, double scale, params int[] dims)
4447
{
4548
var array = new NDArray(typeof(double), new Shape(dims));
46-
Random rand = new Random(); //reuse this if you are generating many
4749

4850
double[] arr = array.Data<double>();
4951

5052
for (int i = 0; i < array.size; i++)
5153
{
52-
double u1 = 1.0 - rand.NextDouble(); //uniform(0,1] random doubles
53-
double u2 = 1.0 - rand.NextDouble();
54+
double u1 = 1.0 - randomizer.NextDouble(); //uniform(0,1] random doubles
55+
double u2 = 1.0 - randomizer.NextDouble();
5456
double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) *
55-
Math.Sin(2.0 * Math.PI * u2); //random normal(0,1)
57+
Math.Sin(2.0 * Math.PI * u2); //random normal(0,1)
5658
double randNormal = loc + scale * randStdNormal; //random normal(mean,stdDev^2)
5759
arr[i] = randNormal;
5860
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using NumSharp.Generic;
8+
9+
namespace NumSharp
10+
{
11+
public partial class NumPyRandom
12+
{
13+
/// <summary>
14+
/// Draw samples from a uniform distribution.
15+
/// Samples are uniformly distributed over the half-open interval [low, high) (includes low, but excludes high). In other words, any value within the given interval is equally likely to be drawn by uniform.
16+
/// </summary>
17+
/// <param name="low">Lower boundary of the output interval. All values generated will be greater than or equal to low. The default value is 0.</param>
18+
/// <param name="high">Upper boundary of the output interval. All values generated will be less than high. The default value is 1.0.</param>
19+
/// <param name="size">Output shape. If the given shape is, e.g., m, n, k, then m * n * k samples are drawn. If size is None (default), a single value is returned if low and high are both scalars. </param>
20+
/// <returns>NDArray with values of type <see cref="double"/></returns>
21+
public NDArray uniform(double low, double high, params int[] size)
22+
{
23+
if (size == null || size.Length == 0) //return scalar
24+
{
25+
var ret = new NDArray<double>(new Shape(1));
26+
var data = new double[] {low + randomizer.NextDouble() * (high - low)};
27+
ret.SetData(data);
28+
return ret;
29+
}
30+
31+
var result = new NDArray<double>(size);
32+
double[] resultArray = result.Data<double>();
33+
34+
//parallelism is prohibited to make sure the result come out presistant
35+
double diff = high - low;
36+
for (int i = 0; i < result.size; ++i)
37+
resultArray[i] = low + randomizer.NextDouble() * diff;
38+
39+
result.SetData(resultArray); //incase of a view
40+
return result;
41+
}
42+
43+
/// <summary>
44+
/// Draw samples from a uniform distribution.
45+
/// Samples are uniformly distributed over the half-open interval [low, high) (includes low, but excludes high). In other words, any value within the given interval is equally likely to be drawn by uniform.
46+
/// </summary>
47+
/// <param name="low">Lower boundary of the output interval. All values generated will be greater than or equal to low. The default value is 0.</param>
48+
/// <param name="high">Upper boundary of the output interval. All values generated will be less than high. The default value is 1.0.</param>
49+
/// <param name="dType">The type of the output <see cref="NDArray"/></param>
50+
/// <returns></returns>
51+
public NDArray uniform(NDArray low, NDArray high, Type dType = null)
52+
{
53+
if (!low.shape.SequenceEqual(high.shape))
54+
throw new IncorrectShapeException();
55+
dType = dType ?? (low.dtype == high.dtype ? low.dtype : throw new IncorrectTypeException());
56+
57+
var ret = low + rand(low.shape).astype(dType) * (high - low);
58+
return dType != null ? ret.astype(dType) : ret;
59+
}
60+
}
61+
}

test/NumSharp.UnitTest/Creation/np.random.randint.Test.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class NdArrayRandomRandIntTest
1414
[TestMethod]
1515
public void randint()
1616
{
17-
var a = new NumPyRandom().randint(low: 0, high: 10, shape: new Shape(5, 5));
17+
var a = np.random.RandomState().randint(low: 0, high: 10, shape: new Shape(5, 5));
1818
Assert.IsTrue(a.Data<int>().Count(x => x < 10) == 25);
1919
}
2020
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2+
using System;
3+
using System.Numerics;
4+
using System.Collections.Generic;
5+
using System.Text;
6+
using System.Linq;
7+
8+
namespace NumSharp.UnitTest
9+
{
10+
[TestClass]
11+
public class NpRandomUniformTests : TestClass
12+
{
13+
[TestMethod]
14+
public void Basic()
15+
{
16+
var low = np.array(1d, 2d, 3d, 4d, 5d);
17+
var high = low + 1;
18+
var uniformed = np.random.uniform(low, high);
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)