diff --git a/AUTHORS.md b/AUTHORS.md index 3bfcd4e79..217aef5ff 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -15,6 +15,7 @@ SMT has been developed thanks to contributions from: * Andres Lopez Lopera * Antoine Averland * Emile Roux +* Enrico Stragiotti * Ewout ter Hoeven * Florent Vergnes * Frederick Zahle diff --git a/doc/_src_docs/dev_docs.rst b/doc/_src_docs/dev_docs.rst index 4514f7812..0a20294a8 100644 --- a/doc/_src_docs/dev_docs.rst +++ b/doc/_src_docs/dev_docs.rst @@ -43,7 +43,8 @@ Users can read the docs online at `smt.readthedocs.io 2.0.0, or use the legacy numpy.random.RandomState " + "class in the future.", + FutureWarning, + ) + elif isinstance(self.options["random_state"], int): + self.random_state = np.random.RandomState(self.options["random_state"]) + else: + self.random_state = np.random.RandomState() + else: + # Construct a new Generator with the default BitGenerator (PCG64). + # If passed a Generator, it will be returned unaltered. When passed a legacy + # RandomState instance it will be coerced to a Generator. + self.random_state = np.random.default_rng(seed=self.options["random_state"]) + def _compute(self, nt): """ Implemented by sampling methods to compute the requested number of sampling points. @@ -30,4 +71,9 @@ def _compute(self, nt): """ xlimits = self.options["xlimits"] nx = xlimits.shape[0] - return np.random.rand(nt, nx) + if numpy_version < (2, 0): # Version is below 2.0.0 + return self.random_state.rand(nt, nx) + else: + # Create a Generator object with a specified seed (numpy.random_state.rand(nt, nx) + # is being deprecated) + return self.random_state.random((nt, nx)) diff --git a/smt/sampling_methods/tests/test_random.py b/smt/sampling_methods/tests/test_random.py new file mode 100644 index 000000000..9e00bc7f5 --- /dev/null +++ b/smt/sampling_methods/tests/test_random.py @@ -0,0 +1,73 @@ +import unittest +from unittest.mock import patch + +import numpy as np +import numpy.testing as npt + +from smt.sampling_methods import Random + + +class TestRandomSamplingMethod(unittest.TestCase): + def setUp(self): + self.xlimits = np.array([[0.0, 1.0], [0.0, 1.0]]) # 2D unit hypercube + + def test_random_state_initialization_legacy(self): + # Test random state initialization for numpy < 2.0.0 + with patch("smt.sampling_methods.random.numpy_version", new=(1, 21)): + sampler = Random(xlimits=self.xlimits, random_state=12) + self.assertIsInstance(sampler.random_state, np.random.RandomState) + + def test_random_state_initialization_new(self): + # Test random state initialization for numpy >= 2.0.0 + with patch("smt.sampling_methods.random.numpy_version", new=(2, 0)): + sampler = Random(xlimits=self.xlimits, random_state=12) + self.assertIsInstance(sampler.random_state, np.random.Generator) + + def test_random_state_warning_for_generator_legacy(self): + # Test that a warning is issued when using Generator with numpy < 2.0.0 + with ( + patch("smt.sampling_methods.random.numpy_version", new=(1, 21)), + self.assertWarns(FutureWarning), + ): + sampler = Random(xlimits=self.xlimits, random_state=np.random.default_rng()) + self.assertIsInstance(sampler.random_state, np.random.RandomState) + + def test_compute_legacy(self): + # Test _compute method for numpy < 2.0.0 + with patch("smt.sampling_methods.random.numpy_version", new=(1, 26)): + sampler = Random(xlimits=self.xlimits, random_state=12) + points = sampler(4) + self.assertEqual(points.shape, (4, 2)) + self.assertTrue(np.all(points >= 0) and np.all(points <= 1)) + # Check almost equality with known seed-generated data (example) + expected_points = np.array( + [ + [0.154163, 0.74005], + [0.263315, 0.533739], + [0.014575, 0.918747], + [0.900715, 0.033421], + ] + ) + npt.assert_allclose(points, expected_points, rtol=1e-4) + + def test_compute_new(self): + # Test _compute method for numpy >= 2.0.0 + with patch("smt.sampling_methods.random.numpy_version", new=(2, 2)): + sampler = Random(xlimits=self.xlimits, random_state=12) + points = sampler(4) + self.assertEqual(points.shape, (4, 2)) + self.assertTrue(np.all(points >= 0) and np.all(points <= 1)) + # Check almost equality with known seed-generated data (example) + expected_points = np.array( + [ + [0.250824, 0.946753], + [0.18932, 0.179291], + [0.349889, 0.230541], + [0.670446, 0.115079], + ] + ) + npt.assert_allclose(points, expected_points, rtol=1e-4) + + +if __name__ == "__main__": + unittest.main() diff --git a/smt/sampling_methods/tests/test_sampling_method_examples.py b/smt/sampling_methods/tests/test_sampling_method_examples.py index 2e1da1d8e..111b6c7fa 100644 --- a/smt/sampling_methods/tests/test_sampling_method_examples.py +++ b/smt/sampling_methods/tests/test_sampling_method_examples.py @@ -18,7 +18,7 @@ def run_random(): from smt.sampling_methods import Random xlimits = np.array([[0.0, 4.0], [0.0, 3.0]]) - sampling = Random(xlimits=xlimits) + sampling = Random(xlimits=xlimits, random_state=12) num = 50 x = sampling(num)