diff --git a/pyDOE3/doe_lhs.py b/pyDOE3/doe_lhs.py index 04d47dc..036a8da 100644 --- a/pyDOE3/doe_lhs.py +++ b/pyDOE3/doe_lhs.py @@ -19,6 +19,8 @@ from scipy import linalg from numpy import ma +from warnings import warn + __all__ = ["lhs"] @@ -29,6 +31,7 @@ def lhs( iterations=None, random_state=None, correlation_matrix=None, + seed=None, ): """ Generate a latin-hypercube design @@ -50,9 +53,12 @@ def lhs( The number of iterations in the maximin and correlations algorithms (Default: 5). randomstate : np.random.RandomState, int + DEPRECATED! It will be removed in a future release. Use seed parameter instead. Random state (or seed-number) which controls the seed and random draws correlation_matrix : ndarray Enforce correlation between factors (only used in lhsmu) + seed : int or np.random.Generator + Seed or np.random.Generator which controls random draws Returns ------- @@ -111,10 +117,31 @@ def lhs( H = None if random_state is None: - random_state = np.random.RandomState() + random_state = np.random.default_rng() + elif isinstance(random_state, np.random.RandomState): + warn( + "Using random_state is deprecated " + "and will raise an error in a future version. Please " + "use seed parameter and pass a np.random.Generator", + DeprecationWarning, + stacklevel=2, + ) elif not isinstance(random_state, np.random.RandomState): + warn( + "Passing a seed or integer to random_state is deprecated " + "and will raise an error in a future version. Please " + "use seed parameter", + DeprecationWarning, + stacklevel=2, + ) random_state = np.random.RandomState(random_state) + if seed is not None: + if isinstance(seed, np.random.Generator): + random_state = seed + else: + random_state = np.random.default_rng(seed) + if samples is None: samples = n @@ -164,7 +191,11 @@ def _lhsclassic(n, samples, randomstate): cut = np.linspace(0, 1, samples + 1) # Fill points uniformly in each interval - u = randomstate.rand(samples, n) + u = ( + randomstate.rand(samples, n) + if isinstance(randomstate, np.random.RandomState) + else randomstate.random((samples, n)) + ) a = cut[:samples] b = cut[1 : samples + 1] rdpoints = np.zeros_like(u) @@ -188,7 +219,11 @@ def _lhscentered(n, samples, randomstate): cut = np.linspace(0, 1, samples + 1) # Fill points uniformly in each interval - u = randomstate.rand(samples, n) + u = ( + randomstate.rand(samples, n) + if isinstance(randomstate, np.random.RandomState) + else randomstate.random((samples, n)) + ) a = cut[:samples] b = cut[1 : samples + 1] _center = (a + b) / 2 diff --git a/tests/test_lhs.py b/tests/test_lhs.py index f7f9714..bb1fd71 100644 --- a/tests/test_lhs.py +++ b/tests/test_lhs.py @@ -4,16 +4,17 @@ class TestLhs(unittest.TestCase): - def test_lhs1(self): + def test_lhs1_deprecated(self): expected = [ [0.12484671, 0.95539205, 0.24399798], [0.53288616, 0.38533955, 0.86703834], [0.68602787, 0.31690477, 0.38533151], ] - actual = lhs(3, random_state=42) - np.testing.assert_allclose(actual, expected) + with self.assertWarns(DeprecationWarning): + actual = lhs(3, random_state=42) + np.testing.assert_allclose(actual, expected) - def test_lhs2(self): + def test_lhs2_deprecated(self): expected = [ [0.06242335, 0.19266575, 0.88202411, 0.89439364], [0.19266977, 0.53538985, 0.53030416, 0.49498498], @@ -22,25 +23,28 @@ def test_lhs2(self): [0.43351917, 0.45134543, 0.12199899, 0.53056742], [0.93530882, 0.15845238, 0.7386575, 0.09977641], ] - actual = lhs(4, samples=6, random_state=42) - np.testing.assert_allclose(actual, expected) + with self.assertWarns(DeprecationWarning): + actual = lhs(4, samples=6, random_state=42) + np.testing.assert_allclose(actual, expected) - def test_lhs3(self): + def test_lhs3_deprecated(self): expected = [[0.1, 0.9], [0.5, 0.5], [0.7, 0.1], [0.3, 0.7], [0.9, 0.3]] - actual = lhs(2, samples=5, criterion="center", random_state=42) - np.testing.assert_allclose(actual, expected) + with self.assertWarns(DeprecationWarning): + actual = lhs(2, samples=5, criterion="center", random_state=42) + np.testing.assert_allclose(actual, expected) - def test_lhs4(self): + def test_lhs4_deprecated(self): expected = [ [0.69754389, 0.2997106, 0.96250964], [0.10585037, 0.09872038, 0.73157522], [0.25351996, 0.65148999, 0.07337204], [0.91276926, 0.97873992, 0.42783549], ] - actual = lhs(3, samples=4, criterion="maximin", random_state=42) - np.testing.assert_allclose(actual, expected) + with self.assertWarns(DeprecationWarning): + actual = lhs(3, samples=4, criterion="maximin", random_state=42) + np.testing.assert_allclose(actual, expected) - def test_lhs5(self): + def test_lhs5_deprecated(self): expected = [ [0.4846803, 0.74226839, 0.23305339, 0.97000772], [0.98526018, 0.11735023, 0.75803511, 0.20312728], @@ -48,7 +52,57 @@ def test_lhs5(self): [0.25519984, 0.4789763, 0.19305106, 0.12140685], [0.63976848, 0.93021541, 0.45869763, 0.40281596], ] - actual = lhs( - 4, samples=5, criterion="correlation", iterations=10, random_state=42 - ) + with self.assertWarns(DeprecationWarning): + actual = lhs( + 4, samples=5, criterion="correlation", iterations=10, random_state=42 + ) + np.testing.assert_allclose(actual, expected) + + def test_lhs1(self): + expected = [ + [0.25798535, 0.14629281, 0.65854078], + [0.56578934, 0.9286881, 0.28619931], + [0.9203799, 0.36472578, 0.70937121], + ] + actual = lhs(3, seed=42) + np.testing.assert_allclose(actual, expected, atol=1e-6) + + def test_lhs2(self): + expected = [ + [0.60731085, 0.32927039, 0.8046052, 0.98218685], + [0.35468561, 0.67730288, 0.14309965, 0.77194407], + [0.18236289, 0.89242099, 0.29352328, 0.53787312], + [0.75909746, 0.63712694, 0.395133, 0.48779416], + [0.95968129, 0.40839766, 0.99511634, 0.116228], + [0.12899267, 0.07314641, 0.57390237, 0.29767738], + ] + actual = lhs(4, samples=6, seed=42) + np.testing.assert_allclose(actual, expected, atol=1e-6) + + def test_lhs3(self): + expected = [[0.9, 0.1], [0.1, 0.7], [0.5, 0.9], [0.7, 0.5], [0.3, 0.3]] + actual = lhs(2, samples=5, criterion="center", seed=42) + print(actual) + np.testing.assert_allclose(actual, expected) + + def test_lhs4(self): + expected = [ + [0.91547913, 0.46335077, 0.05364617], + [0.51457569, 0.10917935, 0.94597455], + [0.35213216, 0.57034597, 0.30848487], + [0.00770446, 0.88925804, 0.57339844], + ] + actual = lhs(3, samples=4, criterion="maximin", seed=42) + print(actual) + np.testing.assert_allclose(actual, expected, atol=1e-6) + + def test_lhs5(self): + expected = [ + [0.15479121, 0.49007719, 0.68868284, 0.35721286], + [0.21883547, 0.39512447, 0.4741596, 0.64544774], + [0.42562273, 0.08777569, 0.35222794, 0.92633288], + [0.91091696, 0.76455232, 0.96552623, 0.585353], + [0.72877302, 0.81276345, 0.17171958, 0.13947361], + ] + actual = lhs(4, samples=5, criterion="correlation", iterations=10, seed=42) np.testing.assert_allclose(actual, expected)