From 3199e15ced6ccfadf220ccc6c463bc27c121d6d8 Mon Sep 17 00:00:00 2001 From: Geoffrey Pruvost Date: Tue, 23 Mar 2021 11:56:23 +0100 Subject: [PATCH 1/3] add the set_seed() function --- docs/tuto.rst | 11 ++++------- moead_framework/tool/result.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/tuto.rst b/docs/tuto.rst index 097d965..85f21b9 100644 --- a/docs/tuto.rst +++ b/docs/tuto.rst @@ -98,18 +98,15 @@ Reproducibility of results is a major principle for scientific research. The feature used here is not specific to the framework but can be used for every python project that uses the random and numpy modules. -Because the framework uses the random and numpy modules, you can be sure -to have the same results by running the same script several times if you -add the following instructions before initializing problems or algorithms: +You can be sure to have the same results by running the same script several times if you +call the ``set_seed(seed)`` method before initializing problems and algorithms: .. code-block:: python - import random - import numpy + from moead_framework.tool.result import set_seed seed = 0 - random.seed(seed) - np.random.seed(seed) + set_seed(seed) You can find more information with the following links: diff --git a/moead_framework/tool/result.py b/moead_framework/tool/result.py index 3961b46..0411862 100644 --- a/moead_framework/tool/result.py +++ b/moead_framework/tool/result.py @@ -1,3 +1,4 @@ +import random import numpy as np @@ -43,6 +44,17 @@ def save_population_full(file_name, population): file.close() +def set_seed(seed): + """ + Set the seed for numpy and random packages + + :param seed: integer + :return: + """ + random.seed(seed) + np.random.seed(seed) + + def compute_hypervolume(solutions, ref_point): hypervolume_class = HyperVolume(reference_point=ref_point) From 2c322fa53c5322b82bbb0a11f8033397a1562a1f Mon Sep 17 00:00:00 2001 From: Geoffrey Pruvost Date: Wed, 31 Mar 2021 11:17:50 +0200 Subject: [PATCH 2/3] use set_seed() in unit test --- moead_framework/test/test_combinatorial_algorithm.py | 7 ++----- moead_framework/test/test_genetic_operator.py | 6 ++---- moead_framework/test/test_numerical_algorithm.py | 7 ++----- moead_framework/test/test_parent_selector.py | 6 ++---- moead_framework/test/test_solution.py | 6 ++---- moead_framework/test/test_tool.py | 7 ++----- 6 files changed, 12 insertions(+), 27 deletions(-) diff --git a/moead_framework/test/test_combinatorial_algorithm.py b/moead_framework/test/test_combinatorial_algorithm.py index 4f44a2b..bac072a 100644 --- a/moead_framework/test/test_combinatorial_algorithm.py +++ b/moead_framework/test/test_combinatorial_algorithm.py @@ -1,11 +1,9 @@ import unittest -import random import os -import numpy as np from moead_framework.aggregation.tchebycheff import Tchebycheff from moead_framework.algorithm.combinatorial import Moead, MoeadDeltaNr, MoeadSPSRandom, MoeadDRA from moead_framework.problem.combinatorial import Rmnk -from moead_framework.tool.result import compute_hypervolume +from moead_framework.tool.result import compute_hypervolume, set_seed class AlgorithmsTest(unittest.TestCase): @@ -13,8 +11,7 @@ class AlgorithmsTest(unittest.TestCase): def setUp(self): """Init""" - random.seed(1) - np.random.seed(1) + set_seed(1) self.number_of_evaluations = 100 project_path = os.path.dirname(os.path.abspath(__file__)) diff --git a/moead_framework/test/test_genetic_operator.py b/moead_framework/test/test_genetic_operator.py index 15c9456..3d5b415 100644 --- a/moead_framework/test/test_genetic_operator.py +++ b/moead_framework/test/test_genetic_operator.py @@ -1,11 +1,10 @@ import unittest -import random -import numpy as np from moead_framework.core.genetic_operator.combinatorial import Crossover, BinaryMutation from moead_framework.core.genetic_operator.numerical import DifferentialEvolutionCrossover, PolynomialMutation, \ MoeadDeOperators from moead_framework.problem.numerical import Zdt1 +from moead_framework.tool.result import set_seed class GeneticOperatorsTest(unittest.TestCase): @@ -13,8 +12,7 @@ class GeneticOperatorsTest(unittest.TestCase): def setUp(self): """Init""" - random.seed(1) - np.random.seed(1) + set_seed(1) def test_combinatorial_binary_mutation(self): """Test combinatorial binary mutation""" diff --git a/moead_framework/test/test_numerical_algorithm.py b/moead_framework/test/test_numerical_algorithm.py index ef41540..4baec56 100644 --- a/moead_framework/test/test_numerical_algorithm.py +++ b/moead_framework/test/test_numerical_algorithm.py @@ -1,11 +1,9 @@ import unittest -import random import os -import numpy as np from moead_framework.aggregation import Tchebycheff from moead_framework.algorithm.numerical import Moead from moead_framework.problem.numerical import Zdt1 -from moead_framework.tool.result import compute_hypervolume +from moead_framework.tool.result import compute_hypervolume, set_seed class AlgorithmsNumericalTest(unittest.TestCase): @@ -13,8 +11,7 @@ class AlgorithmsNumericalTest(unittest.TestCase): def setUp(self): """Init""" - random.seed(1) - np.random.seed(1) + set_seed(1) self.number_of_evaluations = 100 project_path = os.path.dirname(os.path.abspath(__file__)) diff --git a/moead_framework/test/test_parent_selector.py b/moead_framework/test/test_parent_selector.py index 5a55be0..5064601 100644 --- a/moead_framework/test/test_parent_selector.py +++ b/moead_framework/test/test_parent_selector.py @@ -1,14 +1,13 @@ import os import unittest -import random -import numpy as np from moead_framework.aggregation import Tchebycheff from moead_framework.algorithm.combinatorial import Moead from moead_framework.core.parent_selector import TwoRandomParentSelector, OneRandomAndCurrentParentSelector, \ TwoRandomAndCurrentParentSelector from moead_framework.problem.combinatorial import Rmnk +from moead_framework.tool.result import set_seed class ParentSelectorTest(unittest.TestCase): @@ -16,8 +15,7 @@ class ParentSelectorTest(unittest.TestCase): def setUp(self): """Init""" - random.seed(1) - np.random.seed(1) + set_seed(1) self.number_of_evaluations = 100 diff --git a/moead_framework/test/test_solution.py b/moead_framework/test/test_solution.py index 5d24d4e..1d5f911 100644 --- a/moead_framework/test/test_solution.py +++ b/moead_framework/test/test_solution.py @@ -1,11 +1,10 @@ from copy import copy -import numpy as np -import random import os import unittest from moead_framework.problem.combinatorial import Rmnk +from moead_framework.tool.result import set_seed class RmnkTest(unittest.TestCase): @@ -13,8 +12,7 @@ class RmnkTest(unittest.TestCase): def setUp(self): """Init""" - random.seed(1) - np.random.seed(1) + set_seed(1) project_path = os.path.dirname(os.path.abspath(__file__)) self.problem = Rmnk(project_path + '/data/instances/rmnk_0_2_100_1_0.dat') self.solution = self.problem.generate_random_solution() diff --git a/moead_framework/test/test_tool.py b/moead_framework/test/test_tool.py index b7434b1..1235888 100644 --- a/moead_framework/test/test_tool.py +++ b/moead_framework/test/test_tool.py @@ -1,10 +1,8 @@ import unittest -import random import os -import numpy as np from moead_framework.problem.combinatorial import Rmnk from moead_framework.tool.mop import get_non_dominated, population_size_without_duplicate, compute_crowding_distance -from moead_framework.tool.result import save_population, save_population_full, compute_hypervolume +from moead_framework.tool.result import save_population, save_population_full, compute_hypervolume, set_seed class ToolsTest(unittest.TestCase): @@ -12,8 +10,7 @@ class ToolsTest(unittest.TestCase): def setUp(self): """Init""" - random.seed(1) - np.random.seed(1) + set_seed(1) project_path = os.path.dirname(os.path.abspath(__file__)) self.rmnk = Rmnk(instance_file=project_path + '/data/instances/rmnk_0_2_100_1_0.dat') self.rmnk3D = Rmnk(instance_file=project_path + '/data/instances/rmnk_0_3_100_1_0.dat') From 2b313c197ec40984bee253b6c5f791615a8e1060 Mon Sep 17 00:00:00 2001 From: Geoffrey Pruvost Date: Wed, 31 Mar 2021 13:00:52 +0200 Subject: [PATCH 3/3] notice than set_seed is not safe in threaded environments --- docs/tuto.rst | 4 ++++ moead_framework/tool/result.py | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/tuto.rst b/docs/tuto.rst index 7d91664..3b44989 100644 --- a/docs/tuto.rst +++ b/docs/tuto.rst @@ -110,6 +110,10 @@ add the following instructions before initializing problems or algorithms: seed = 0 set_seed(seed) +.. warning:: + This approach is not safe in threaded environments. + By re-using a seed value, the same sequence should be reproducible from run to run as long as + multiple threads are not running (https://docs.python.org/3/library/random.html#notes-on-reproducibility) You can find more information at the following links: diff --git a/moead_framework/tool/result.py b/moead_framework/tool/result.py index 0411862..f5330de 100644 --- a/moead_framework/tool/result.py +++ b/moead_framework/tool/result.py @@ -46,7 +46,10 @@ def save_population_full(file_name, population): def set_seed(seed): """ - Set the seed for numpy and random packages + Set the seed for numpy and random packages. + Warning, this approach is not safe in threaded environments. + By re-using a seed value, the same sequence should be reproducible from run to run as long as + multiple threads are not running (https://docs.python.org/3/library/random.html#notes-on-reproducibility) :param seed: integer :return: