From 107188f01e8889746a209c2bb06950f608632082 Mon Sep 17 00:00:00 2001 From: k-onoue Date: Sun, 18 Aug 2024 15:38:52 +0900 Subject: [PATCH 01/11] fix filename --- .../grey_wolf_optimization/{example_.ipynb => example.ipynb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename package/samplers/grey_wolf_optimization/{example_.ipynb => example.ipynb} (100%) diff --git a/package/samplers/grey_wolf_optimization/example_.ipynb b/package/samplers/grey_wolf_optimization/example.ipynb similarity index 100% rename from package/samplers/grey_wolf_optimization/example_.ipynb rename to package/samplers/grey_wolf_optimization/example.ipynb From 7523c7b724429c7799ab83a64f0064dcd48fbe8f Mon Sep 17 00:00:00 2001 From: Keisuke Onoue Date: Tue, 20 Aug 2024 15:41:31 +0900 Subject: [PATCH 02/11] Update README.md --- package/samplers/grey_wolf_optimization/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/samplers/grey_wolf_optimization/README.md b/package/samplers/grey_wolf_optimization/README.md index 06a33aa4..7f7503b2 100644 --- a/package/samplers/grey_wolf_optimization/README.md +++ b/package/samplers/grey_wolf_optimization/README.md @@ -1,6 +1,6 @@ --- author: k-onoue -title: Sampler based on Grey Wolf Optimization (GWO) +title: Grey Wolf Optimization (GWO) Sampler description: Swarm algorithm inspired by the leadership and hunting behavior of grey wolves tags: [sampler] optuna_versions: [3.6.1] From f5ab5bb1d3cb47f6e7149bad6d88894bd9ed669d Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 14:09:04 +0900 Subject: [PATCH 03/11] Clip wolves' positions to stay within bounds after updates --- .../grey_wolf_optimization/grey_wolf_optimization.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py index 495b847e..b44fe61c 100644 --- a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py +++ b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py @@ -86,8 +86,11 @@ def sample_relative( D = np.abs(C * self.leaders - self.wolves[:, np.newaxis, :]) X = self.leaders - A * D - # Update wolves' positions and store them in the queue + # Update wolves' positions and clip to fit into the search space self.wolves = np.mean(X, axis=1) + self.wolves = np.clip(self.wolves, self.lower_bound, self.upper_bound) + + # Store the wolves in the queue self.queue.extend( [{k: v for k, v in zip(search_space.keys(), pos)} for pos in self.wolves] ) From 2617a78e6f460781d9bfd5fc8fe8dee08b1969f3 Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 14:29:48 +0900 Subject: [PATCH 04/11] Call SimpleBaseSampler.__init__ to properly initialize the base class --- .../samplers/grey_wolf_optimization/grey_wolf_optimization.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py index b44fe61c..eb4a7c32 100644 --- a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py +++ b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py @@ -18,7 +18,9 @@ def __init__( num_leaders: int = 3, seed: int = 0, ) -> None: - self.search_space = search_space + # Initialize the base class + super().__init__(search_space, seed) + self.population_size = population_size self.max_iter = max_iter self.num_leaders = min(max(1, num_leaders), self.population_size // 2) From 2a87eee10ca042bd2d8cff55e246db1185678514 Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 14:52:45 +0900 Subject: [PATCH 05/11] fix --- .../grey_wolf_optimization/grey_wolf_optimization.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py index eb4a7c32..b5508ab6 100644 --- a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py +++ b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py @@ -33,6 +33,15 @@ def __init__( self.queue: list[dict[str, Any]] = [] # Queue to hold candidate positions def _lazy_init(self, search_space: dict[str, BaseDistribution]) -> None: + # Workaround for the limitation of the type of distributions + if any( + isinstance(dist, optuna.distributions.CategoricalDistribution) + for dist in search_space.values() + ): + raise NotImplementedError( + "CategoricalDistribution is not supported in GreyWolfOptimizationSampler." + ) + self.dim = len(search_space) self.lower_bound = np.array([dist.low for dist in search_space.values()]) self.upper_bound = np.array([dist.high for dist in search_space.values()]) From a2ce47a72d1d70fb57d678643052d50caacf3262 Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 15:26:20 +0900 Subject: [PATCH 06/11] Added support for optimization direction specification --- .../grey_wolf_optimization/grey_wolf_optimization.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py index b5508ab6..81cc5236 100644 --- a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py +++ b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py @@ -6,6 +6,7 @@ import optuna from optuna.distributions import BaseDistribution from optuna.samplers import RandomSampler +from optuna.study._study_direction import StudyDirection import optunahub @@ -81,13 +82,21 @@ def sample_relative( self.fitnesses = np.array( [trial.value for trial in completed_trials[-self.population_size :]] ) + self.fitnesses = np.array([ + trial.value if study.direction == StudyDirection.MINIMIZE else -trial.value + for trial in completed_trials[-self.population_size :] + ]) # Update leaders (alpha, beta, gamma, ...) sorted_indices = np.argsort(self.fitnesses) self.leaders = self.wolves[sorted_indices[: self.num_leaders]] + # Track the current iteration + current_iter = len(study.trials) // self.population_size + max_iter = self.max_iter + # Linearly decrease from 2 to 0 - a = 2 * (1 - len(study.trials) / (self.max_iter * self.population_size)) + a = max(0, 2 * (1 - current_iter / max_iter)) # Calculate A, C, D, X values for position update r1 = self._rng.rand(self.population_size, self.num_leaders, self.dim) From 6922414bafaaa39128abf20029b491c87295838a Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 15:30:52 +0900 Subject: [PATCH 07/11] run pre-commit --- .../grey_wolf_optimization/grey_wolf_optimization.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py index 81cc5236..3016f452 100644 --- a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py +++ b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py @@ -36,7 +36,7 @@ def __init__( def _lazy_init(self, search_space: dict[str, BaseDistribution]) -> None: # Workaround for the limitation of the type of distributions if any( - isinstance(dist, optuna.distributions.CategoricalDistribution) + isinstance(dist, optuna.distributions.CategoricalDistribution) for dist in search_space.values() ): raise NotImplementedError( @@ -82,10 +82,12 @@ def sample_relative( self.fitnesses = np.array( [trial.value for trial in completed_trials[-self.population_size :]] ) - self.fitnesses = np.array([ - trial.value if study.direction == StudyDirection.MINIMIZE else -trial.value - for trial in completed_trials[-self.population_size :] - ]) + self.fitnesses = np.array( + [ + (trial.value if study.direction == StudyDirection.MINIMIZE else -trial.value) + for trial in completed_trials[-self.population_size :] + ] + ) # Update leaders (alpha, beta, gamma, ...) sorted_indices = np.argsort(self.fitnesses) From e9384f25afd8cdf6ab4d924916e1dfe888cb9a94 Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 16:34:07 +0900 Subject: [PATCH 08/11] Restored the previous implementation of 's update --- .../grey_wolf_optimization/grey_wolf_optimization.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py index 3016f452..c77ac8bd 100644 --- a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py +++ b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py @@ -93,12 +93,8 @@ def sample_relative( sorted_indices = np.argsort(self.fitnesses) self.leaders = self.wolves[sorted_indices[: self.num_leaders]] - # Track the current iteration - current_iter = len(study.trials) // self.population_size - max_iter = self.max_iter - # Linearly decrease from 2 to 0 - a = max(0, 2 * (1 - current_iter / max_iter)) + a = 2 * (1 - len(study.trials) / (self.max_iter * self.population_size)) # Calculate A, C, D, X values for position update r1 = self._rng.rand(self.population_size, self.num_leaders, self.dim) From 283cee1c36adb6022d4faa35e18f2c500f5a5093 Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 17:39:06 +0900 Subject: [PATCH 09/11] fix --- .../samplers/grey_wolf_optimization/example.py | 7 +++++-- .../grey_wolf_optimization.py | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/package/samplers/grey_wolf_optimization/example.py b/package/samplers/grey_wolf_optimization/example.py index b2950826..2eb5a621 100644 --- a/package/samplers/grey_wolf_optimization/example.py +++ b/package/samplers/grey_wolf_optimization/example.py @@ -17,13 +17,16 @@ def objective(trial: optuna.trial.Trial) -> float: y = trial.suggest_float("y", -10, 10) return x**2 + y**2 + n_trials = 100 + sampler = GreyWolfOptimizationSampler( { "x": optuna.distributions.FloatDistribution(-10, 10), "y": optuna.distributions.FloatDistribution(-10, 10), - } + }, + max_iter=n_trials, # This should be equal to `n_trials` in `study.optimize`. ) study = optuna.create_study(sampler=sampler) - study.optimize(objective, n_trials=100) + study.optimize(objective, n_trials=n_trials) optuna.visualization.matplotlib.plot_optimization_history(study) plt.show() diff --git a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py index c77ac8bd..f4dea461 100644 --- a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py +++ b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py @@ -19,6 +19,15 @@ def __init__( num_leaders: int = 3, seed: int = 0, ) -> None: + """ + max_iter : int, optional + The maximum number of iterations (should correspond to `n_trials` in `study.optimize`). + population_size : int, optional + The number of wolves in the population. This should be larger than the number of leaders. + num_leaders : int, optional + The number of leaders in the population. This should be smaller than the population size. + """ + # Initialize the base class super().__init__(search_space, seed) @@ -93,8 +102,10 @@ def sample_relative( sorted_indices = np.argsort(self.fitnesses) self.leaders = self.wolves[sorted_indices[: self.num_leaders]] - # Linearly decrease from 2 to 0 - a = 2 * (1 - len(study.trials) / (self.max_iter * self.population_size)) + # Linearly decrease from 2 to 0, ensuring a is clipped between 0 and 2 + current_iter = len(study.trials) + a = 2 * (1 - current_iter / self.max_iter) + a = np.clip(a, 0, 2) # Calculate A, C, D, X values for position update r1 = self._rng.rand(self.population_size, self.num_leaders, self.dim) From 62bc1d0f5197419aecb8d0db036a7bd6ff89f083 Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 17:41:39 +0900 Subject: [PATCH 10/11] run pre-commit --- package/samplers/grey_wolf_optimization/example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/samplers/grey_wolf_optimization/example.py b/package/samplers/grey_wolf_optimization/example.py index 2eb5a621..34b871c7 100644 --- a/package/samplers/grey_wolf_optimization/example.py +++ b/package/samplers/grey_wolf_optimization/example.py @@ -24,7 +24,7 @@ def objective(trial: optuna.trial.Trial) -> float: "x": optuna.distributions.FloatDistribution(-10, 10), "y": optuna.distributions.FloatDistribution(-10, 10), }, - max_iter=n_trials, # This should be equal to `n_trials` in `study.optimize`. + max_iter=n_trials, # This should be equal to `n_trials` in `study.optimize`. ) study = optuna.create_study(sampler=sampler) study.optimize(objective, n_trials=n_trials) From 6f078cc1b1e5b62eda1dfd9c4a655bb9fab1151b Mon Sep 17 00:00:00 2001 From: k-onoue Date: Fri, 27 Sep 2024 17:56:28 +0900 Subject: [PATCH 11/11] fix --- package/samplers/grey_wolf_optimization/README.md | 5 +++-- package/samplers/grey_wolf_optimization/example.ipynb | 6 ++++-- package/samplers/grey_wolf_optimization/example.py | 4 ++-- .../grey_wolf_optimization/grey_wolf_optimization.py | 10 +++++----- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/package/samplers/grey_wolf_optimization/README.md b/package/samplers/grey_wolf_optimization/README.md index 7f7503b2..72195445 100644 --- a/package/samplers/grey_wolf_optimization/README.md +++ b/package/samplers/grey_wolf_optimization/README.md @@ -34,9 +34,10 @@ if __name__ == "__main__": y = trial.suggest_float("y", -10, 10) return x**2 + y**2 - sampler = GreyWolfOptimizationSampler() + # Note: `n_trials` should match the `n_trials` passed to `study.optimize`. + sampler = GreyWolfOptimizationSampler(n_trials=100) study = optuna.create_study(sampler=sampler) - study.optimize(objective, n_trials=100) + study.optimize(objective, n_trials=sampler.n_trials) optuna.visualization.matplotlib.plot_optimization_history(study) plt.show() ``` diff --git a/package/samplers/grey_wolf_optimization/example.ipynb b/package/samplers/grey_wolf_optimization/example.ipynb index f3262a81..65f18906 100644 --- a/package/samplers/grey_wolf_optimization/example.ipynb +++ b/package/samplers/grey_wolf_optimization/example.ipynb @@ -88,7 +88,9 @@ "outputs": [], "source": [ "mod = optunahub.load_module(\"samplers/grey_wolf_optimization\")\n", - "sampler = mod.GreyWolfOptimizationSampler()" + "sampler = mod.GreyWolfOptimizationSampler(n_trials=100)\n", + "\n", + "# Note: `n_trials` should match the `n_trials` passed to `study.optimize`." ] }, { @@ -111,7 +113,7 @@ "outputs": [], "source": [ "study = optuna.create_study(sampler=sampler)\n", - "study.optimize(objective, n_trials=100)" + "study.optimize(objective, n_trials=sampler.n_trials)" ] }, { diff --git a/package/samplers/grey_wolf_optimization/example.py b/package/samplers/grey_wolf_optimization/example.py index 34b871c7..f0b9777d 100644 --- a/package/samplers/grey_wolf_optimization/example.py +++ b/package/samplers/grey_wolf_optimization/example.py @@ -24,9 +24,9 @@ def objective(trial: optuna.trial.Trial) -> float: "x": optuna.distributions.FloatDistribution(-10, 10), "y": optuna.distributions.FloatDistribution(-10, 10), }, - max_iter=n_trials, # This should be equal to `n_trials` in `study.optimize`. + n_trials=n_trials, # This should be equal to `n_trials` in `study.optimize`. ) study = optuna.create_study(sampler=sampler) - study.optimize(objective, n_trials=n_trials) + study.optimize(objective, n_trials=sampler.n_trials) optuna.visualization.matplotlib.plot_optimization_history(study) plt.show() diff --git a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py index f4dea461..edf65987 100644 --- a/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py +++ b/package/samplers/grey_wolf_optimization/grey_wolf_optimization.py @@ -15,13 +15,13 @@ def __init__( self, search_space: dict[str, BaseDistribution] | None = None, population_size: int = 10, - max_iter: int = 40, + n_trials: int = 40, num_leaders: int = 3, seed: int = 0, ) -> None: """ - max_iter : int, optional - The maximum number of iterations (should correspond to `n_trials` in `study.optimize`). + n_trials : int, optional + The number of trials (should correspond to `n_trials` in `study.optimize`). population_size : int, optional The number of wolves in the population. This should be larger than the number of leaders. num_leaders : int, optional @@ -32,7 +32,7 @@ def __init__( super().__init__(search_space, seed) self.population_size = population_size - self.max_iter = max_iter + self.n_trials = n_trials self.num_leaders = min(max(1, num_leaders), self.population_size // 2) self._rng = np.random.RandomState(seed) self.dim = 0 @@ -104,7 +104,7 @@ def sample_relative( # Linearly decrease from 2 to 0, ensuring a is clipped between 0 and 2 current_iter = len(study.trials) - a = 2 * (1 - current_iter / self.max_iter) + a = 2 * (1 - current_iter / self.n_trials) a = np.clip(a, 0, 2) # Calculate A, C, D, X values for position update