3
3
4
4
import numpy as np
5
5
6
- class HybridDELocalRefinement :
6
+ from kernel_tuner .strategies .wrapper import OptAlg
7
+
8
+ class HybridDELocalRefinement (OptAlg ):
7
9
"""
8
10
A two-phase differential evolution with local refinement, intended for BBOB-type
9
11
black box optimization problems in [-5,5]^dim.
@@ -12,21 +14,14 @@ class HybridDELocalRefinement:
12
14
exploration and local exploitation under a strict function evaluation budget.
13
15
"""
14
16
15
- def __init__ (self , budget , dim ):
16
- """
17
- Initialize the optimizer with:
18
- - budget: total number of function evaluations allowed.
19
- - dim: dimensionality of the search space.
20
- """
21
- self .budget = budget
22
- self .dim = dim
17
+ def __init__ (self ):
18
+ super ().__init__ ()
23
19
# You can adjust these hyperparameters based on experimentation/tuning:
24
- self .population_size = min (50 , 10 * dim ) # Caps for extremely large dim
25
20
self .F = 0.8 # Differential weight
26
21
self .CR = 0.9 # Crossover probability
27
22
self .local_search_freq = 10 # Local refinement frequency in generations
28
23
29
- def __call__ (self , func ):
24
+ def __call__ (self , func , searchspace ):
30
25
"""
31
26
Optimize the black box function `func` in [-5,5]^dim, using
32
27
at most self.budget function evaluations.
@@ -35,9 +30,8 @@ def __call__(self, func):
35
30
best_params: np.ndarray representing the best parameters found
36
31
best_value: float representing the best objective value found
37
32
"""
38
- # Check if we have a non-positive budget
39
- if self .budget <= 0 :
40
- raise ValueError ("Budget must be a positive integer." )
33
+ self .dim = searchspace .num_params
34
+ self .population_size = round (min (min (50 , 10 * self .dim ), np .ceil (searchspace .size / 3 ))) # Caps for extremely large dim
41
35
42
36
# 1. Initialize population
43
37
lower_bound , upper_bound = - 5.0 , 5.0
@@ -49,8 +43,6 @@ def __call__(self, func):
49
43
for i in range (self .population_size ):
50
44
fitness [i ] = func (pop [i ])
51
45
evaluations += 1
52
- if evaluations >= self .budget :
53
- break
54
46
55
47
# Track best solution
56
48
best_idx = np .argmin (fitness )
@@ -59,7 +51,7 @@ def __call__(self, func):
59
51
60
52
# 2. Main evolutionary loop
61
53
gen = 0
62
- while evaluations < self . budget :
54
+ while func . budget_spent_fraction < 1.0 and evaluations < searchspace . size :
63
55
gen += 1
64
56
for i in range (self .population_size ):
65
57
# DE mutation: pick three distinct indices
@@ -78,7 +70,7 @@ def __call__(self, func):
78
70
# Evaluate trial
79
71
trial_fitness = func (trial )
80
72
evaluations += 1
81
- if evaluations >= self . budget :
73
+ if func . budget_spent_fraction > 1.0 :
82
74
# If out of budget, wrap up
83
75
if trial_fitness < fitness [i ]:
84
76
pop [i ] = trial
@@ -99,14 +91,11 @@ def __call__(self, func):
99
91
best_params = trial .copy ()
100
92
101
93
# Periodically refine best solution with a small local neighborhood search
102
- if gen % self .local_search_freq == 0 and evaluations < self . budget :
94
+ if gen % self .local_search_freq == 0 and func . budget_spent_fraction < 1.0 :
103
95
best_params , best_value , evaluations = self ._local_refinement (
104
96
func , best_params , best_value , evaluations , lower_bound , upper_bound
105
97
)
106
98
107
- if evaluations >= self .budget :
108
- break
109
-
110
99
return best_params , best_value
111
100
112
101
def _local_refinement (self , func , best_params , best_value , evaluations , lb , ub ):
@@ -115,11 +104,10 @@ def _local_refinement(self, func, best_params, best_value, evaluations, lb, ub):
115
104
Uses a quick 'perturb-and-accept' approach in a shrinking neighborhood.
116
105
"""
117
106
# Neighborhood size shrinks as the budget is consumed
118
- frac_budget_used = evaluations / self .budget
119
- step_size = 0.2 * (1.0 - frac_budget_used )
107
+ step_size = 0.2 * (1.0 - func .budget_spent_fraction )
120
108
121
109
for _ in range (5 ): # 5 refinements each time
122
- if evaluations >= self . budget :
110
+ if func . budget_spent_fraction >= 1.0 :
123
111
break
124
112
candidate = best_params + np .random .uniform (- step_size , step_size , self .dim )
125
113
candidate = np .clip (candidate , lb , ub )
@@ -138,26 +126,23 @@ def _local_refinement(self, func, best_params, best_value, evaluations, lb, ub):
138
126
import os
139
127
from kernel_tuner import tune_kernel
140
128
from kernel_tuner .strategies .wrapper import OptAlgWrapper
141
- cache_filename = os .path .dirname (
142
-
143
- os .path .realpath (__file__ )) + "/test_cache_file.json"
144
129
145
130
from .test_runners import env
146
131
132
+ cache_filename = os .path .dirname (os .path .realpath (__file__ )) + "/test_cache_file.json"
147
133
148
134
def test_OptAlgWrapper (env ):
149
135
kernel_name , kernel_string , size , args , tune_params = env
150
136
151
137
# Instantiate LLaMAE optimization algorithm
152
- budget = int (15 )
153
- dim = len (tune_params )
154
- optimizer = HybridDELocalRefinement (budget , dim )
138
+ optimizer = HybridDELocalRefinement ()
155
139
156
140
# Wrap the algorithm class in the OptAlgWrapper
157
141
# for use in Kernel Tuner
158
142
strategy = OptAlgWrapper (optimizer )
143
+ strategy_options = { 'max_fevals' : 15 }
159
144
160
145
# Call the tuner
161
146
tune_kernel (kernel_name , kernel_string , size , args , tune_params ,
162
- strategy = strategy , cache = cache_filename ,
147
+ strategy = strategy , strategy_options = strategy_options , cache = cache_filename ,
163
148
simulation_mode = True , verbose = True )
0 commit comments