Skip to content

Commit 3264583

Browse files
committed
test: use isolated RandomState in distribution tests for parallel execution
Convert remaining np.random.* tests to use isolated RandomState instances instead of global np.random.seed() to avoid test collisions during parallel execution. Files converted: - np.random.logseries.Test.cs - np.random.multivariate_normal.Test.cs - np.random.noncentral_chisquare.Test.cs - np.random.noncentral_f.Test.cs - np.random.standard_gamma.Test.cs - np.random.standard_t.Test.cs - np.random.triangular.Tests.cs - np.random.vonmises.Test.cs - np.random.wald.Test.cs Changes made: - Replace np.random.seed(n) with var rng = np.random.RandomState(n) - Replace np.random.distribution() calls with rng.distribution() - Exception tests continue to use np.random.distribution() directly (no seeding needed for argument validation tests) - Reproducibility tests use separate rng1/rng2 instances test: convert standard_exponential tests to isolated RandomState Fully converted np.random.standard_exponential.Test.cs to use isolated RandomState instances instead of global np.random.seed(). test: convert random tests to use isolated RandomState (partial) Convert tests from using global np.random.seed() to isolated np.random.RandomState() instances for better test isolation: - Remove [NotInParallel] attribute from RandomSampling test classes - Replace np.random.seed(X) + np.random.foo() with var rng = np.random.RandomState(X) + rng.foo() - Use separate RandomState instances for reproducibility tests that compare same-seed vs different-seed scenarios Files fully converted: - OpenBugs.Random.cs - NumpyAlignmentBugTests.cs (choice tests) - np.random.f.Test.cs - np.random.dirichlet.Test.cs - np.random.gumbel.Test.cs - np.random.hypergeometric.Test.cs - np.random.laplace.Test.cs - np.random.logistic.Test.cs - np.random.multinomial.Test.cs - np.random.negative_binomial.Test.cs - np.random.rayleigh.Test.cs - np.random.shuffle.NumPyAligned.Test.cs - np.random.standard_cauchy.Test.cs - np.random.standard_normal.Test.cs - np.random.weibull.Test.cs Files partially converted (NotInParallel removed): - np.random.logseries.Test.cs - np.random.multivariate_normal.Test.cs - np.random.noncentral_chisquare.Test.cs - np.random.noncentral_f.Test.cs - np.random.pareto.Test.cs - np.random.power.Test.cs - np.random.standard_exponential.Test.cs - np.random.standard_gamma.Test.cs - np.random.standard_t.Test.cs - np.random.triangular.Tests.cs - np.random.vonmises.Test.cs - np.random.wald.Test.cs - np.random.zipf.Test.cs These changes enable random sampling tests to run in parallel without shared global state interference. test: continue RandomState conversion for pareto and power tests Continue converting random sampling tests to use isolated RandomState: - np.random.pareto.Test.cs: Fully converted - np.random.power.Test.cs: Fully converted Both files now use var rng = np.random.RandomState(seed) instead of np.random.seed() + np.random.foo() for test isolation. test: convert zipf tests to isolated RandomState Fully converted np.random.zipf.Test.cs to use isolated RandomState instances instead of global np.random.seed().
1 parent a38a715 commit 3264583

27 files changed

Lines changed: 600 additions & 614 deletions

test/NumSharp.UnitTest/Backends/Kernels/NumpyAlignmentBugTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -948,8 +948,8 @@ public void Bug32_Choice_ReplaceFalse_NoDuplicates()
948948
//
949949
// NumSharp BUG: replace parameter is declared but never used.
950950
// Code always uses randint which samples WITH replacement.
951-
np.random.seed(42);
952-
var result = np.random.choice(10, new Shape(5), replace: false);
951+
var rng = np.random.RandomState(42);
952+
var result = rng.choice(10, new Shape(5), replace: false);
953953

954954
// With replace=False, all values must be unique
955955
var values = result.ToArray<int>();
@@ -969,11 +969,11 @@ public void Bug32_Choice_ReplaceFalse_SizeExceedsPopulation_ShouldThrow()
969969
// ValueError: Cannot take a larger sample than population when 'replace=False'
970970
//
971971
// NumSharp BUG: No validation, will produce duplicates instead
972-
np.random.seed(42);
972+
var rng = np.random.RandomState(42);
973973

974974
try
975975
{
976-
var result = np.random.choice(5, new Shape(10), replace: false);
976+
var result = rng.choice(5, new Shape(10), replace: false);
977977
Assert.Fail(
978978
"np.random.choice(5, 10, replace=False) should throw ValueError, " +
979979
"but returned: " + string.Join(", ", result.ToArray<int>()));
@@ -995,8 +995,8 @@ public void Bug32_Choice_ReplaceTrue_AllowsDuplicates()
995995
{
996996
// NUMPY: replace=True (default) allows duplicates
997997
// This test verifies the default behavior still works
998-
np.random.seed(42);
999-
var result = np.random.choice(3, new Shape(100), replace: true);
998+
var rng = np.random.RandomState(42);
999+
var result = rng.choice(3, new Shape(100), replace: true);
10001000

10011001
// With only 3 choices and 100 samples, duplicates are guaranteed
10021002
var values = result.ToArray<int>();
@@ -1012,9 +1012,9 @@ public void Bug32_Choice_NDArray_ReplaceFalse()
10121012
{
10131013
// NUMPY: np.random.choice(['a','b','c','d','e'], 3, replace=False)
10141014
// Should return 3 unique elements from the array
1015-
np.random.seed(42);
1015+
var rng = np.random.RandomState(42);
10161016
var arr = np.array(new[] { 10, 20, 30, 40, 50 });
1017-
var result = np.random.choice(arr, new Shape(3), replace: false);
1017+
var result = rng.choice(arr, new Shape(3), replace: false);
10181018

10191019
var values = result.ToArray<int>();
10201020
var uniqueCount = values.Distinct().Count();

test/NumSharp.UnitTest/RandomSampling/np.random.dirichlet.Test.cs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ namespace NumSharp.UnitTest.RandomSampling
66
/// <summary>
77
/// Tests for np.random.dirichlet (Dirichlet distribution)
88
/// </summary>
9-
[NotInParallel]
109
public class NpRandomDirichletTests : TestClass
1110
{
1211
[Test]
@@ -53,9 +52,9 @@ public void Dirichlet_ReturnsFloat64()
5352
[Test]
5453
public void Dirichlet_EachRowSumsToOne()
5554
{
56-
np.random.seed(42);
55+
var rng = np.random.RandomState(42);
5756
var alpha = new double[] { 1, 2, 3 };
58-
var samples = np.random.dirichlet(alpha, 100);
57+
var samples = rng.dirichlet(alpha, 100);
5958

6059
for (int i = 0; i < 100; i++)
6160
{
@@ -71,9 +70,9 @@ public void Dirichlet_EachRowSumsToOne()
7170
[Test]
7271
public void Dirichlet_AllValuesInZeroOne()
7372
{
74-
np.random.seed(42);
73+
var rng = np.random.RandomState(42);
7574
var alpha = new double[] { 1, 2, 3 };
76-
var samples = np.random.dirichlet(alpha, 1000);
75+
var samples = rng.dirichlet(alpha, 1000);
7776

7877
foreach (var val in samples.AsIterator<double>())
7978
{
@@ -86,9 +85,9 @@ public void Dirichlet_HasCorrectMean()
8685
{
8786
// Mean of component i = alpha[i] / sum(alpha)
8887
// For alpha = [1, 2, 3], sum = 6, means = [1/6, 2/6, 3/6] = [0.167, 0.333, 0.5]
89-
np.random.seed(42);
88+
var rng = np.random.RandomState(42);
9089
var alpha = new double[] { 1, 2, 3 };
91-
var samples = np.random.dirichlet(alpha, 100000);
90+
var samples = rng.dirichlet(alpha, 100000);
9291

9392
double[] means = new double[3];
9493
for (int i = 0; i < 100000; i++)
@@ -113,9 +112,9 @@ public void Dirichlet_HasCorrectMean()
113112
public void Dirichlet_UniformAlpha_HasEqualMeans()
114113
{
115114
// For alpha = [1, 1, 1], all means should be 1/3
116-
np.random.seed(42);
115+
var rng = np.random.RandomState(42);
117116
var alpha = new double[] { 1, 1, 1 };
118-
var samples = np.random.dirichlet(alpha, 10000);
117+
var samples = rng.dirichlet(alpha, 10000);
119118

120119
double[] means = new double[3];
121120
for (int i = 0; i < 10000; i++)
@@ -158,11 +157,11 @@ public void Dirichlet_SameSeed_ProducesSameResults()
158157
{
159158
var alpha = new double[] { 1, 2, 3 };
160159

161-
np.random.seed(42);
162-
var samples1 = np.random.dirichlet(alpha, 10);
160+
var rng1 = np.random.RandomState(42);
161+
var samples1 = rng1.dirichlet(alpha, 10);
163162

164-
np.random.seed(42);
165-
var samples2 = np.random.dirichlet(alpha, 10);
163+
var rng2 = np.random.RandomState(42);
164+
var samples2 = rng2.dirichlet(alpha, 10);
166165

167166
for (int i = 0; i < samples1.size; i++)
168167
{
@@ -223,9 +222,9 @@ public void Dirichlet_NumPy_SmokeTest()
223222
[Test]
224223
public void Dirichlet_NumPy_SamplesSumToOne()
225224
{
226-
np.random.seed(12345);
225+
var rng = np.random.RandomState(12345);
227226
var alpha = new double[] { 0.5, 0.5, 0.5, 0.5 };
228-
var samples = np.random.dirichlet(alpha, 100);
227+
var samples = rng.dirichlet(alpha, 100);
229228

230229
for (int i = 0; i < 100; i++)
231230
{

test/NumSharp.UnitTest/RandomSampling/np.random.f.Test.cs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44

55
namespace NumSharp.UnitTest.RandomSampling
66
{
7-
[NotInParallel]
87
public class NpRandomFTest : TestClass
98
{
109
[Test]
1110
public void F_ScalarReturn()
1211
{
13-
np.random.seed(42);
14-
var result = np.random.f(5, 10);
12+
var rng = np.random.RandomState(42);
13+
var result = rng.f(5, 10);
1514

1615
Assert.AreEqual(0, result.ndim);
1716
Assert.AreEqual(1, result.size);
@@ -75,8 +74,8 @@ public void F_DfdenNegative_ThrowsArgumentException()
7574
public void F_MeanMatchesTheory()
7675
{
7776
// For F(dfnum, dfden), mean = dfden / (dfden - 2) when dfden > 2
78-
np.random.seed(42);
79-
var samples = np.random.f(5, 10, 100000);
77+
var rng = np.random.RandomState(42);
78+
var samples = rng.f(5, 10, 100000);
8079
double expectedMean = 10.0 / (10.0 - 2.0); // 1.25
8180
double actualMean = (double)np.mean(samples);
8281

@@ -87,8 +86,8 @@ public void F_MeanMatchesTheory()
8786
[Test]
8887
public void F_AllValuesPositive()
8988
{
90-
np.random.seed(42);
91-
var samples = np.random.f(5, 10, 10000);
89+
var rng = np.random.RandomState(42);
90+
var samples = rng.f(5, 10, 10000);
9291

9392
foreach (var val in samples.AsIterator<double>())
9493
Assert.IsTrue(val > 0, $"Value {val} should be positive");
@@ -98,8 +97,8 @@ public void F_AllValuesPositive()
9897
public void F_DifferentDf_MeanMatchesTheory()
9998
{
10099
// Test with different df values
101-
np.random.seed(42);
102-
var samples = np.random.f(10, 20, 100000);
100+
var rng = np.random.RandomState(42);
101+
var samples = rng.f(10, 20, 100000);
103102
double expectedMean = 20.0 / (20.0 - 2.0); // 1.111
104103
double actualMean = (double)np.mean(samples);
105104

@@ -118,11 +117,11 @@ public void F_ReturnsFloat64()
118117
[Test]
119118
public void F_Reproducible()
120119
{
121-
np.random.seed(123);
122-
var a = np.random.f(5, 10, 5);
120+
var rng1 = np.random.RandomState(123);
121+
var a = rng1.f(5, 10, 5);
123122

124-
np.random.seed(123);
125-
var b = np.random.f(5, 10, 5);
123+
var rng2 = np.random.RandomState(123);
124+
var b = rng2.f(5, 10, 5);
126125

127126
var aData = a.Data<double>();
128127
var bData = b.Data<double>();

test/NumSharp.UnitTest/RandomSampling/np.random.gumbel.Test.cs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ namespace NumSharp.UnitTest.RandomSampling
66
/// <summary>
77
/// Tests for np.random.gumbel (Gumbel/extreme value type I distribution)
88
/// </summary>
9-
[NotInParallel]
109
public class NpRandomGumbelTests : TestClass
1110
{
1211
// Euler-Mascheroni constant
@@ -41,8 +40,8 @@ public void Gumbel_DefaultParameters_HasCorrectStatistics()
4140
{
4241
// Gumbel(0, 1) has mean = γ ≈ 0.5772 (Euler-Mascheroni constant)
4342
// and std = π / sqrt(6) ≈ 1.283
44-
np.random.seed(42);
45-
var samples = np.random.gumbel(0, 1, 100000);
43+
var rng = np.random.RandomState(42);
44+
var samples = rng.gumbel(0, 1, 100000);
4645

4746
var mean = (double)np.mean(samples);
4847
var std = (double)np.std(samples);
@@ -57,10 +56,10 @@ public void Gumbel_DefaultParameters_HasCorrectStatistics()
5756
public void Gumbel_WithLocScale_TransformsCorrectly()
5857
{
5958
// Gumbel(loc, scale) has mean = loc + scale * γ
60-
np.random.seed(42);
59+
var rng = np.random.RandomState(42);
6160
double loc = 2.0;
6261
double scale = 3.0;
63-
var samples = np.random.gumbel(loc, scale, 100000);
62+
var samples = rng.gumbel(loc, scale, 100000);
6463

6564
var mean = (double)np.mean(samples);
6665
double expectedMean = loc + scale * EulerGamma;
@@ -89,8 +88,8 @@ public void Gumbel_ScaleZero_ReturnsConstantAtLoc()
8988
[Test]
9089
public void Gumbel_Scalar_ReturnsScalar()
9190
{
92-
np.random.seed(42);
93-
var result = np.random.gumbel();
91+
var rng = np.random.RandomState(42);
92+
var result = rng.gumbel();
9493
// NumPy returns a scalar (0-dimensional) when no size is given
9594
Assert.AreEqual(0, result.ndim);
9695
Assert.AreEqual(1, result.size);
@@ -106,11 +105,11 @@ public void Gumbel_ReturnsFloat64()
106105
[Test]
107106
public void Gumbel_SameSeed_ProducesSameResults()
108107
{
109-
np.random.seed(42);
110-
var samples1 = np.random.gumbel(0, 1, 10);
108+
var rng1 = np.random.RandomState(42);
109+
var samples1 = rng1.gumbel(0, 1, 10);
111110

112-
np.random.seed(42);
113-
var samples2 = np.random.gumbel(0, 1, 10);
111+
var rng2 = np.random.RandomState(42);
112+
var samples2 = rng2.gumbel(0, 1, 10);
114113

115114
for (int i = 0; i < 10; i++)
116115
{
@@ -121,11 +120,11 @@ public void Gumbel_SameSeed_ProducesSameResults()
121120
[Test]
122121
public void Gumbel_DifferentSeeds_ProduceDifferentResults()
123122
{
124-
np.random.seed(42);
125-
var samples1 = np.random.gumbel(0, 1, 10);
123+
var rng1 = np.random.RandomState(42);
124+
var samples1 = rng1.gumbel(0, 1, 10);
126125

127-
np.random.seed(123);
128-
var samples2 = np.random.gumbel(0, 1, 10);
126+
var rng2 = np.random.RandomState(123);
127+
var samples2 = rng2.gumbel(0, 1, 10);
129128

130129
bool anyDifferent = false;
131130
for (int i = 0; i < 10; i++)
@@ -143,8 +142,8 @@ public void Gumbel_DifferentSeeds_ProduceDifferentResults()
143142
public void Gumbel_CanProduceNegativeValues()
144143
{
145144
// Gumbel distribution can produce negative values (unlike Rayleigh)
146-
np.random.seed(42);
147-
var samples = np.random.gumbel(0, 1, 10000);
145+
var rng = np.random.RandomState(42);
146+
var samples = rng.gumbel(0, 1, 10000);
148147

149148
bool hasNegative = false;
150149
foreach (var val in samples.AsIterator<double>())

0 commit comments

Comments
 (0)