From 9e89632d5dd53a4cc320823f4c2a3c87c6c5dfd2 Mon Sep 17 00:00:00 2001 From: Paul-Saves Date: Fri, 2 Feb 2024 15:59:40 +0100 Subject: [PATCH] fix seeding --- smt/applications/ego.py | 15 ++++++--- smt/applications/mixed_integer.py | 8 ++--- smt/applications/tests/test_ego.py | 50 ++++++++++++++--------------- smt/utils/design_space.py | 30 ++++++++++++++--- smt/utils/test/test_design_space.py | 10 +++--- 5 files changed, 71 insertions(+), 42 deletions(-) diff --git a/smt/applications/ego.py b/smt/applications/ego.py index 59ce004ff..83e00281e 100644 --- a/smt/applications/ego.py +++ b/smt/applications/ego.py @@ -14,7 +14,10 @@ from smt.surrogate_models import KPLS, KRG, KPLSK, MGP, GEKPLS from smt.applications.application import SurrogateBasedApplication -from smt.applications.mixed_integer import MixedIntegerContext +from smt.applications.mixed_integer import ( + MixedIntegerContext, + MixedIntegerSamplingMethod, +) from smt.utils.design_space import ( BaseDesignSpace, DesignSpace, @@ -271,10 +274,14 @@ def _setup_optimizer(self, fun): else: self.mixint = None - self._sampling = lambda n: self.design_space.sample_valid_x( - n, + sampling = MixedIntegerSamplingMethod( + LHS, + self.design_space, + criterion="ese", random_state=self.options["random_state"], - )[0] + ) + self._sampling = lambda n: sampling(n) + self.categorical_kernel = None # Build DOE diff --git a/smt/applications/mixed_integer.py b/smt/applications/mixed_integer.py index 6f89451c3..29fdeb831 100644 --- a/smt/applications/mixed_integer.py +++ b/smt/applications/mixed_integer.py @@ -44,9 +44,9 @@ def __init__(self, sampling_method_class, design_space, **kwargs): ) self._design_space = design_space if "random_state" in kwargs: - self._design_space.seed = kwargs["random_state"] - elif self._design_space.seed is None: - self._design_space.seed = 42 + self._design_space.random_state = kwargs["random_state"] + elif self._design_space.random_state is None: + self._design_space.random_state = 42 self._unfolded_xlimits = design_space.get_unfolded_num_bounds() self._output_in_folded_space = kwargs.get("output_in_folded_space", True) kwargs.pop("output_in_folded_space", None) @@ -60,7 +60,7 @@ def _compute(self, nt, return_is_acting=False): x_doe, is_acting = self._design_space.sample_valid_x( nt, unfolded=not self._output_in_folded_space, - random_state=self._design_space.seed, + random_state=self._design_space.random_state, ) if return_is_acting: return x_doe, is_acting diff --git a/smt/applications/tests/test_ego.py b/smt/applications/tests/test_ego.py index 5f3bb6626..0b1735104 100644 --- a/smt/applications/tests/test_ego.py +++ b/smt/applications/tests/test_ego.py @@ -121,7 +121,7 @@ def test_rosenbrock_2D(self): xlimits = fun.xlimits criterion = "LCB" #'EI' or 'SBO' or 'LCB' random_state = 42 - design_space = DesignSpace(xlimits, seed=random_state) + design_space = DesignSpace(xlimits, random_state=random_state) xdoe = FullFactorial(xlimits=xlimits)(10) ego = EGO( n_start=30, @@ -164,7 +164,7 @@ def test_rosenbrock_2D_parallel(self): xlimits = fun.xlimits criterion = "LCB" #'EI' or 'SBO' or 'LCB' random_state = 42 - design_space = DesignSpace(xlimits, seed=random_state) + design_space = DesignSpace(xlimits, random_state=random_state) xdoe = FullFactorial(xlimits=xlimits)(10) qEI = "KB" @@ -250,7 +250,7 @@ def test_branin_2D_mixed_parallel(self): IntegerVariable(*xlimits[0]), FloatVariable(*xlimits[1]), ], - seed=random_state, + random_state=random_state, ) sm = KRG(design_space=design_space, print_global=False, n_start=25) mixint = MixedIntegerContext(design_space) @@ -289,7 +289,7 @@ def test_branin_2D_mixed(self): IntegerVariable(*xlimits[0]), FloatVariable(*xlimits[1]), ], - seed=random_state, + random_state=random_state, ) criterion = "EI" #'EI' or 'SBO' or 'LCB' @@ -326,7 +326,7 @@ def test_branin_2D_mixed_tunnel(self): IntegerVariable(*xlimits[0]), FloatVariable(*xlimits[1]), ], - seed=random_state, + random_state=random_state, ) criterion = "EI" #'EI' or 'SBO' or 'LCB' @@ -388,10 +388,10 @@ def test_ego_mixed_integer(self): CategoricalVariable(["large", "small"]), OrdinalVariable([0, 2, 3]), ], - seed=42, + random_state=42, ) samp = MixedIntegerSamplingMethod( - LHS, design_space, criterion="ese", random_state=design_space.seed + LHS, design_space, criterion="ese", random_state=design_space.random_state ) xdoe = samp(n_doe) @@ -402,7 +402,7 @@ def test_ego_mixed_integer(self): xdoe=xdoe, surrogate=KRG(design_space=design_space, print_global=False), enable_tunneling=False, - random_state=design_space.seed, + random_state=design_space.random_state, ) _, y_opt, _, _, _ = ego.optimize(fun=TestEGO.function_test_mixed_integer) @@ -420,10 +420,10 @@ def test_ego_mixed_integer_gower_distance(self): CategoricalVariable(["large", "small"]), IntegerVariable(0, 2), ], - seed=random_state, + random_state=random_state, ) samp = MixedIntegerSamplingMethod( - LHS, design_space, criterion="ese", random_state=design_space.seed + LHS, design_space, criterion="ese", random_state=design_space.random_state ) xdoe = samp(n_doe) @@ -440,7 +440,7 @@ def test_ego_mixed_integer_gower_distance(self): print_global=False, ), enable_tunneling=False, - random_state=design_space.seed, + random_state=design_space.random_state, ) _, y_opt, _, _, _ = ego.optimize(fun=TestEGO.function_test_mixed_integer) @@ -495,7 +495,7 @@ def f_hv(X): IntegerVariable(0, 5), # x6 IntegerVariable(0, 5), # x7 ], - seed=random_state, + random_state=random_state, ) # x6 is active when x0 >= 2 @@ -686,7 +686,7 @@ def f_hv(X): IntegerVariable(0, 2), IntegerVariable(0, 2), ], - seed=random_state, + random_state=random_state, ) # x4 is acting if meta == 1, 3 @@ -700,7 +700,7 @@ def f_hv(X): n_doe = 25 samp = MixedIntegerSamplingMethod( - LHS, ds, criterion="ese", random_state=ds.seed + LHS, ds, criterion="ese", random_state=ds.random_state ) Xt, x_is_active = samp(n_doe, return_is_acting=True) @@ -742,7 +742,7 @@ def test_ego_mixed_integer_homo_gaussian(self): CategoricalVariable(["large", "small"]), IntegerVariable(0, 2), ], - seed=random_state, + random_state=random_state, ) n_doe = 5 sampling = MixedIntegerSamplingMethod( @@ -782,7 +782,7 @@ def test_ego_mixed_integer_homo_gaussian_pls(self): CategoricalVariable(["large", "small"]), IntegerVariable(0, 2), ], - seed=random_state, + random_state=random_state, ) sampling = MixedIntegerSamplingMethod( LHS, @@ -819,7 +819,7 @@ def test_ydoe_option(self): xlimits = fun.xlimits criterion = "LCB" #'EI' or 'SBO' or 'LCB' random_state = 42 - design_space = DesignSpace(xlimits, seed=random_state) + design_space = DesignSpace(xlimits, random_state=random_state) xdoe = FullFactorial(xlimits=xlimits)(10) ydoe = fun(xdoe) ego = EGO( @@ -838,7 +838,7 @@ def test_find_best_point(self): fun = TestEGO.function_test_1d xlimits = np.array([[0.0, 25.0]]) random_state = 42 - design_space = DesignSpace(xlimits, seed=random_state) + design_space = DesignSpace(xlimits, random_state=random_state) xdoe = FullFactorial(xlimits=xlimits)(3) ydoe = fun(xdoe) ego = EGO( @@ -874,7 +874,7 @@ def _evaluate(self, x, kx): fun = TensorProductIndirect(ndim=2, func=func) random_state = 42 - design_space = DesignSpace(fun.xlimits, seed=42) + design_space = DesignSpace(fun.xlimits, random_state=42) # Construction of the DOE sampling = LHS(xlimits=fun.xlimits, criterion="m", random_state=random_state) @@ -906,7 +906,7 @@ def _evaluate(self, x, kx): return ego, fun - def test_ego_seeding(self): + def test_ego_random_stateing(self): def f_obj(X): """ s01 objective @@ -983,7 +983,7 @@ def f_obj(X): ) # To define the initial DOE - random_state = 42 # seed value for the sampling + random_state = 42 # random_state value for the sampling n_doe = 5 # initial doe size sampling = MixedIntegerSamplingMethod( LHS, design_space, criterion="ese", random_state=random_state @@ -1039,7 +1039,7 @@ def test_qei_criterion_default(self): fun = TestEGO.function_test_1d xlimits = np.array([[0.0, 25.0]]) random_state = 42 - design_space = DesignSpace(xlimits, seed=random_state) + design_space = DesignSpace(xlimits, random_state=random_state) xdoe = FullFactorial(xlimits=xlimits)(3) ydoe = fun(xdoe) ego = EGO( @@ -1094,7 +1094,7 @@ def function_test_1d(x): xlimits = np.array([[0.0, 25.0]]) random_state = 42 # for reproducibility - design_space = DesignSpace(xlimits, seed=random_state) + design_space = DesignSpace(xlimits, random_state=random_state) xdoe = np.atleast_2d([0, 7, 25]).T n_doe = xdoe.size @@ -1213,7 +1213,7 @@ def function_test_mixed_integer(X): CategoricalVariable(["square", "circle"]), IntegerVariable(0, 2), ], - seed=random_state, + random_state=random_state, ) criterion = "EI" #'EI' or 'SBO' or 'LCB' @@ -1284,7 +1284,7 @@ def function_test_1d(x): xlimits = np.array([[0.0, 25.0]]) random_state = 42 - design_space = DesignSpace(xlimits, seed=random_state) + design_space = DesignSpace(xlimits, random_state=random_state) xdoe = np.atleast_2d([0, 7, 25]).T n_doe = xdoe.size diff --git a/smt/utils/design_space.py b/smt/utils/design_space.py index 8ad1112c3..26ba153e1 100644 --- a/smt/utils/design_space.py +++ b/smt/utils/design_space.py @@ -696,7 +696,9 @@ class DesignSpace(BaseDesignSpace): """ def __init__( - self, design_variables: Union[List[DesignVariable], list, np.ndarray], seed=None + self, + design_variables: Union[List[DesignVariable], list, np.ndarray], + random_state=None, ): self.sampler = None @@ -721,7 +723,7 @@ def _is_num(val): converted_dvs.append(FloatVariable(bounds[0], bounds[1])) design_variables = converted_dvs - self.seed = seed # For testing + self.random_state = random_state # For testing self._cs = None if HAS_CONFIG_SPACE: @@ -742,6 +744,11 @@ def _is_num(val): cs_vars[name] = CategoricalHyperparameter(name, choices=dv.values) else: raise ValueError(f"Unknown variable type: {dv!r}") + seed = None + if isinstance(random_state, int): + seed = random_state + elif isinstance(random_state, np.random.RandomState): + seed = random_state.get_state()[1][0] self._cs = NoDefaultConfigurationSpace(space=cs_vars, seed=seed) @@ -927,11 +934,18 @@ def _sample_valid_x( """Sample design vectors""" # Simplified implementation: sample design vectors in unfolded space x_limits_unfolded = self.get_unfolded_num_bounds() - if self.seed is None: - self.seed = random_state + if self.random_state is None: + self.random_state = random_state if self._cs is not None: # Sample Configuration objects + if not (hasattr(self, "seed")): + seed = None + if isinstance(random_state, int): + seed = random_state + elif isinstance(random_state, np.random.RandomState): + seed = random_state.get_state()[1][0] + self.seed = seed self._cs.seed(self.seed) if self.seed is not None: self.seed += 1 @@ -984,6 +998,14 @@ def _get_correct_config(self, vector: np.ndarray) -> Configuration: # At this point, the parameter active statuses are set correctly, so we only need to correct the # configuration to one that does not violate the forbidden clauses elif isinstance(e, ForbiddenValueError): + if not (hasattr(self, "seed")): + seed = None + if isinstance(self.random_state, int): + seed = self.random_state + elif isinstance(random_state, np.random.RandomState): + seed = self.random_state.get_state()[1][0] + self.seed = seed + return get_random_neighbor(config, seed=self.seed) else: diff --git a/smt/utils/test/test_design_space.py b/smt/utils/test/test_design_space.py index 13e439ffe..4f624681a 100644 --- a/smt/utils/test/test_design_space.py +++ b/smt/utils/test/test_design_space.py @@ -198,7 +198,7 @@ def test_design_space(self): IntegerVariable(-1, 2), FloatVariable(0.5, 1.5), ], - seed=42, + random_state=42, ) self.assertEqual(len(ds.design_variables), 4) if HAS_CONFIG_SPACE: @@ -340,7 +340,7 @@ def test_design_space_hierarchical(self): IntegerVariable(0, 1), # x2 FloatVariable(0, 1), # x3 ], - seed=42, + random_state=42, ) ds.declare_decreed_var( decreed_var=3, meta_var=0, meta_value="A" @@ -434,7 +434,7 @@ def test_design_space_hierarchical_config_space(self): IntegerVariable(0, 1), # x2 FloatVariable(0, 1), # x3 ], - seed=42, + random_state=42, ) ds.declare_decreed_var( decreed_var=3, meta_var=0, meta_value="A" @@ -530,7 +530,7 @@ def _is_conditionally_acting(self) -> np.ndarray: IntegerVariable(0, 1), # x2 FloatVariable(0, 1), # x3 ], - seed=42, + random_state=42, ) ds.declare_decreed_var( decreed_var=3, meta_var=0, meta_value="A" @@ -550,7 +550,7 @@ def test_check_conditionally_acting_2(self): IntegerVariable(0, 1), # x2 FloatVariable(0, 1), # x3 ], - seed=42, + random_state=42, ) ds.declare_decreed_var( decreed_var=0, meta_var=1, meta_value="E"