bayesian-optimization 2.0.3__tar.gz → 3.0.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/PKG-INFO +14 -1
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/README.md +13 -0
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/acquisition.py +56 -34
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/bayesian_optimization.py +34 -35
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/constraint.py +4 -1
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/domain_reduction.py +14 -7
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/logger.py +11 -8
- bayesian_optimization-3.0.0b1/bayes_opt/parameter.py +506 -0
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/target_space.py +235 -65
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/pyproject.toml +1 -1
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/LICENSE +0 -0
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/__init__.py +0 -0
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/event.py +0 -0
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/exception.py +0 -0
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/observer.py +0 -0
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/py.typed +0 -0
- {bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bayesian-optimization
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0b1
|
|
4
4
|
Summary: Bayesian Optimization package
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Fernando Nogueira
|
|
@@ -207,3 +207,16 @@ For constrained optimization:
|
|
|
207
207
|
}
|
|
208
208
|
```
|
|
209
209
|
|
|
210
|
+
For optimization over non-float parameters:
|
|
211
|
+
```
|
|
212
|
+
@article{garrido2020dealing,
|
|
213
|
+
title={Dealing with categorical and integer-valued variables in bayesian optimization with gaussian processes},
|
|
214
|
+
author={Garrido-Merch{\'a}n, Eduardo C and Hern{\'a}ndez-Lobato, Daniel},
|
|
215
|
+
journal={Neurocomputing},
|
|
216
|
+
volume={380},
|
|
217
|
+
pages={20--35},
|
|
218
|
+
year={2020},
|
|
219
|
+
publisher={Elsevier}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
@@ -185,3 +185,16 @@ For constrained optimization:
|
|
|
185
185
|
year={2014}
|
|
186
186
|
}
|
|
187
187
|
```
|
|
188
|
+
|
|
189
|
+
For optimization over non-float parameters:
|
|
190
|
+
```
|
|
191
|
+
@article{garrido2020dealing,
|
|
192
|
+
title={Dealing with categorical and integer-valued variables in bayesian optimization with gaussian processes},
|
|
193
|
+
author={Garrido-Merch{\'a}n, Eduardo C and Hern{\'a}ndez-Lobato, Daniel},
|
|
194
|
+
journal={Neurocomputing},
|
|
195
|
+
volume={380},
|
|
196
|
+
pages={20--35},
|
|
197
|
+
year={2020},
|
|
198
|
+
publisher={Elsevier}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
@@ -127,7 +127,7 @@ class AcquisitionFunction(abc.ABC):
|
|
|
127
127
|
self._fit_gp(gp=gp, target_space=target_space)
|
|
128
128
|
|
|
129
129
|
acq = self._get_acq(gp=gp, constraint=target_space.constraint)
|
|
130
|
-
return self._acq_min(acq, target_space
|
|
130
|
+
return self._acq_min(acq, target_space, n_random=n_random, n_l_bfgs_b=n_l_bfgs_b)
|
|
131
131
|
|
|
132
132
|
def _get_acq(
|
|
133
133
|
self, gp: GaussianProcessRegressor, constraint: ConstraintModel | None = None
|
|
@@ -182,7 +182,7 @@ class AcquisitionFunction(abc.ABC):
|
|
|
182
182
|
def _acq_min(
|
|
183
183
|
self,
|
|
184
184
|
acq: Callable[[NDArray[Float]], NDArray[Float]],
|
|
185
|
-
|
|
185
|
+
space: TargetSpace,
|
|
186
186
|
n_random: int = 10_000,
|
|
187
187
|
n_l_bfgs_b: int = 10,
|
|
188
188
|
) -> NDArray[Float]:
|
|
@@ -197,10 +197,8 @@ class AcquisitionFunction(abc.ABC):
|
|
|
197
197
|
acq : Callable
|
|
198
198
|
Acquisition function to use. Should accept an array of parameters `x`.
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
`(N, 2)` with `[i, 0]` the lower bound of parameter `i` and
|
|
203
|
-
`[i, 1]` the upper bound.
|
|
200
|
+
space : TargetSpace
|
|
201
|
+
The target space over which to optimize.
|
|
204
202
|
|
|
205
203
|
n_random : int
|
|
206
204
|
Number of random samples to use.
|
|
@@ -217,15 +215,22 @@ class AcquisitionFunction(abc.ABC):
|
|
|
217
215
|
if n_random == 0 and n_l_bfgs_b == 0:
|
|
218
216
|
error_msg = "Either n_random or n_l_bfgs_b needs to be greater than 0."
|
|
219
217
|
raise ValueError(error_msg)
|
|
220
|
-
x_min_r, min_acq_r = self._random_sample_minimize(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if
|
|
224
|
-
|
|
225
|
-
|
|
218
|
+
x_min_r, min_acq_r, x_seeds = self._random_sample_minimize(
|
|
219
|
+
acq, space, n_random=max(n_random, n_l_bfgs_b), n_x_seeds=n_l_bfgs_b
|
|
220
|
+
)
|
|
221
|
+
if n_l_bfgs_b:
|
|
222
|
+
x_min_l, min_acq_l = self._l_bfgs_b_minimize(acq, space, x_seeds=x_seeds)
|
|
223
|
+
# Either n_random or n_l_bfgs_b is not 0 => at least one of x_min_r and x_min_l is not None
|
|
224
|
+
if min_acq_r > min_acq_l:
|
|
225
|
+
return x_min_l
|
|
226
|
+
return x_min_r
|
|
226
227
|
|
|
227
228
|
def _random_sample_minimize(
|
|
228
|
-
self,
|
|
229
|
+
self,
|
|
230
|
+
acq: Callable[[NDArray[Float]], NDArray[Float]],
|
|
231
|
+
space: TargetSpace,
|
|
232
|
+
n_random: int,
|
|
233
|
+
n_x_seeds: int = 0,
|
|
229
234
|
) -> tuple[NDArray[Float] | None, float]:
|
|
230
235
|
"""Random search to find the minimum of `acq` function.
|
|
231
236
|
|
|
@@ -234,14 +239,14 @@ class AcquisitionFunction(abc.ABC):
|
|
|
234
239
|
acq : Callable
|
|
235
240
|
Acquisition function to use. Should accept an array of parameters `x`.
|
|
236
241
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
`(N, 2)` with `[i, 0]` the lower bound of parameter `i` and
|
|
240
|
-
`[i, 1]` the upper bound.
|
|
242
|
+
space : TargetSpace
|
|
243
|
+
The target space over which to optimize.
|
|
241
244
|
|
|
242
245
|
n_random : int
|
|
243
246
|
Number of random samples to use.
|
|
244
247
|
|
|
248
|
+
n_x_seeds : int
|
|
249
|
+
Number of top points to return, for use as starting points for L-BFGS-B.
|
|
245
250
|
Returns
|
|
246
251
|
-------
|
|
247
252
|
x_min : np.ndarray
|
|
@@ -252,14 +257,22 @@ class AcquisitionFunction(abc.ABC):
|
|
|
252
257
|
"""
|
|
253
258
|
if n_random == 0:
|
|
254
259
|
return None, np.inf
|
|
255
|
-
x_tries =
|
|
260
|
+
x_tries = space.random_sample(n_random, random_state=self.random_state)
|
|
256
261
|
ys = acq(x_tries)
|
|
257
262
|
x_min = x_tries[ys.argmin()]
|
|
258
263
|
min_acq = ys.min()
|
|
259
|
-
|
|
264
|
+
if n_x_seeds != 0:
|
|
265
|
+
idxs = np.argsort(ys)[-n_x_seeds:]
|
|
266
|
+
x_seeds = x_tries[idxs]
|
|
267
|
+
else:
|
|
268
|
+
x_seeds = []
|
|
269
|
+
return x_min, min_acq, x_seeds
|
|
260
270
|
|
|
261
271
|
def _l_bfgs_b_minimize(
|
|
262
|
-
self,
|
|
272
|
+
self,
|
|
273
|
+
acq: Callable[[NDArray[Float]], NDArray[Float]],
|
|
274
|
+
space: TargetSpace,
|
|
275
|
+
x_seeds: NDArray[Float] | None = None,
|
|
263
276
|
) -> tuple[NDArray[Float] | None, float]:
|
|
264
277
|
"""Random search to find the minimum of `acq` function.
|
|
265
278
|
|
|
@@ -268,13 +281,11 @@ class AcquisitionFunction(abc.ABC):
|
|
|
268
281
|
acq : Callable
|
|
269
282
|
Acquisition function to use. Should accept an array of parameters `x`.
|
|
270
283
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
`(N, 2)` with `[i, 0]` the lower bound of parameter `i` and
|
|
274
|
-
`[i, 1]` the upper bound.
|
|
284
|
+
space : TargetSpace
|
|
285
|
+
The target space over which to optimize.
|
|
275
286
|
|
|
276
|
-
|
|
277
|
-
|
|
287
|
+
x_seeds : int
|
|
288
|
+
Starting points for the L-BFGS-B optimizer.
|
|
278
289
|
|
|
279
290
|
Returns
|
|
280
291
|
-------
|
|
@@ -284,33 +295,44 @@ class AcquisitionFunction(abc.ABC):
|
|
|
284
295
|
min_acq : float
|
|
285
296
|
Acquisition function value at `x_min`
|
|
286
297
|
"""
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
298
|
+
continuous_dimensions = space.continuous_dimensions
|
|
299
|
+
continuous_bounds = space.bounds[continuous_dimensions]
|
|
300
|
+
|
|
301
|
+
if not continuous_dimensions.any():
|
|
302
|
+
min_acq = np.inf
|
|
303
|
+
x_min = np.array([np.nan] * space.bounds.shape[0])
|
|
304
|
+
return x_min, min_acq
|
|
290
305
|
|
|
291
306
|
min_acq: float | None = None
|
|
292
307
|
x_try: NDArray[Float]
|
|
293
308
|
x_min: NDArray[Float]
|
|
294
309
|
for x_try in x_seeds:
|
|
295
|
-
# Find the minimum of minus the acquisition function
|
|
296
|
-
res: OptimizeResult = minimize(acq, x_try, bounds=bounds, method="L-BFGS-B")
|
|
297
310
|
|
|
311
|
+
def continuous_acq(x: NDArray[Float], x_try=x_try) -> NDArray[Float]:
|
|
312
|
+
x_try[continuous_dimensions] = x
|
|
313
|
+
return acq(x_try)
|
|
314
|
+
|
|
315
|
+
# Find the minimum of minus the acquisition function
|
|
316
|
+
res: OptimizeResult = minimize(
|
|
317
|
+
continuous_acq, x_try[continuous_dimensions], bounds=continuous_bounds, method="L-BFGS-B"
|
|
318
|
+
)
|
|
298
319
|
# See if success
|
|
299
320
|
if not res.success:
|
|
300
321
|
continue
|
|
301
322
|
|
|
302
323
|
# Store it if better than previous minimum(maximum).
|
|
303
324
|
if min_acq is None or np.squeeze(res.fun) >= min_acq:
|
|
304
|
-
|
|
325
|
+
x_try[continuous_dimensions] = res.x
|
|
326
|
+
x_min = x_try
|
|
305
327
|
min_acq = np.squeeze(res.fun)
|
|
306
328
|
|
|
307
329
|
if min_acq is None:
|
|
308
330
|
min_acq = np.inf
|
|
309
|
-
x_min = np.array([np.nan] * bounds.shape[0])
|
|
331
|
+
x_min = np.array([np.nan] * space.bounds.shape[0])
|
|
310
332
|
|
|
311
333
|
# Clip output to make sure it lies within the bounds. Due to floating
|
|
312
334
|
# point technicalities this is not always the case.
|
|
313
|
-
return np.clip(x_min, bounds[:, 0], bounds[:, 1]), min_acq
|
|
335
|
+
return np.clip(x_min, space.bounds[:, 0], space.bounds[:, 1]), min_acq
|
|
314
336
|
|
|
315
337
|
|
|
316
338
|
class UpperConfidenceBound(AcquisitionFunction):
|
{bayesian_optimization-2.0.3 → bayesian_optimization-3.0.0b1}/bayes_opt/bayesian_optimization.py
RENAMED
|
@@ -16,13 +16,15 @@ from sklearn.gaussian_process.kernels import Matern
|
|
|
16
16
|
|
|
17
17
|
from bayes_opt import acquisition
|
|
18
18
|
from bayes_opt.constraint import ConstraintModel
|
|
19
|
+
from bayes_opt.domain_reduction import DomainTransformer
|
|
19
20
|
from bayes_opt.event import DEFAULT_EVENTS, Events
|
|
20
21
|
from bayes_opt.logger import _get_default_logger
|
|
22
|
+
from bayes_opt.parameter import wrap_kernel
|
|
21
23
|
from bayes_opt.target_space import TargetSpace
|
|
22
24
|
from bayes_opt.util import ensure_rng
|
|
23
25
|
|
|
24
26
|
if TYPE_CHECKING:
|
|
25
|
-
from collections.abc import Callable, Iterable, Mapping
|
|
27
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
26
28
|
|
|
27
29
|
from numpy.random import RandomState
|
|
28
30
|
from numpy.typing import NDArray
|
|
@@ -31,6 +33,7 @@ if TYPE_CHECKING:
|
|
|
31
33
|
from bayes_opt.acquisition import AcquisitionFunction
|
|
32
34
|
from bayes_opt.constraint import ConstraintModel
|
|
33
35
|
from bayes_opt.domain_reduction import DomainTransformer
|
|
36
|
+
from bayes_opt.parameter import BoundsMapping, ParamsType
|
|
34
37
|
|
|
35
38
|
Float = np.floating[Any]
|
|
36
39
|
|
|
@@ -114,7 +117,7 @@ class BayesianOptimization(Observable):
|
|
|
114
117
|
):
|
|
115
118
|
self._random_state = ensure_rng(random_state)
|
|
116
119
|
self._allow_duplicate_points = allow_duplicate_points
|
|
117
|
-
self._queue: deque[
|
|
120
|
+
self._queue: deque[ParamsType] = deque()
|
|
118
121
|
|
|
119
122
|
if acquisition_function is None:
|
|
120
123
|
if constraint is None:
|
|
@@ -128,15 +131,6 @@ class BayesianOptimization(Observable):
|
|
|
128
131
|
else:
|
|
129
132
|
self._acquisition_function = acquisition_function
|
|
130
133
|
|
|
131
|
-
# Internal GP regressor
|
|
132
|
-
self._gp = GaussianProcessRegressor(
|
|
133
|
-
kernel=Matern(nu=2.5),
|
|
134
|
-
alpha=1e-6,
|
|
135
|
-
normalize_y=True,
|
|
136
|
-
n_restarts_optimizer=5,
|
|
137
|
-
random_state=self._random_state,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
134
|
if constraint is None:
|
|
141
135
|
# Data structure containing the function to be optimized, the
|
|
142
136
|
# bounds of its domain, and a record of the evaluations we have
|
|
@@ -158,14 +152,22 @@ class BayesianOptimization(Observable):
|
|
|
158
152
|
)
|
|
159
153
|
self.is_constrained = True
|
|
160
154
|
|
|
155
|
+
# Internal GP regressor
|
|
156
|
+
self._gp = GaussianProcessRegressor(
|
|
157
|
+
kernel=wrap_kernel(Matern(nu=2.5), transform=self._space.kernel_transform),
|
|
158
|
+
alpha=1e-6,
|
|
159
|
+
normalize_y=True,
|
|
160
|
+
n_restarts_optimizer=5,
|
|
161
|
+
random_state=self._random_state,
|
|
162
|
+
)
|
|
163
|
+
|
|
161
164
|
self._verbose = verbose
|
|
162
165
|
self._bounds_transformer = bounds_transformer
|
|
163
166
|
if self._bounds_transformer:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
raise TypeError(error_msg) from exc
|
|
167
|
+
if not isinstance(self._bounds_transformer, DomainTransformer):
|
|
168
|
+
msg = "The transformer must be an instance of DomainTransformer"
|
|
169
|
+
raise TypeError(msg)
|
|
170
|
+
self._bounds_transformer.initialize(self._space)
|
|
169
171
|
|
|
170
172
|
self._sorting_warning_already_shown = False # TODO: remove in future version
|
|
171
173
|
super().__init__(events=DEFAULT_EVENTS)
|
|
@@ -204,10 +206,7 @@ class BayesianOptimization(Observable):
|
|
|
204
206
|
return self._space.res()
|
|
205
207
|
|
|
206
208
|
def register(
|
|
207
|
-
self,
|
|
208
|
-
params: Mapping[str, float] | Sequence[float] | NDArray[Float],
|
|
209
|
-
target: float,
|
|
210
|
-
constraint_value: float | NDArray[Float] | None = None,
|
|
209
|
+
self, params: ParamsType, target: float, constraint_value: float | NDArray[Float] | None = None
|
|
211
210
|
) -> None:
|
|
212
211
|
"""Register an observation with known target.
|
|
213
212
|
|
|
@@ -225,10 +224,10 @@ class BayesianOptimization(Observable):
|
|
|
225
224
|
# TODO: remove in future version
|
|
226
225
|
if isinstance(params, np.ndarray) and not self._sorting_warning_already_shown:
|
|
227
226
|
msg = (
|
|
228
|
-
"You're attempting to register an np.ndarray.
|
|
229
|
-
" parameters by key and
|
|
230
|
-
"
|
|
231
|
-
"
|
|
227
|
+
"You're attempting to register an np.ndarray. In previous versions, the optimizer internally"
|
|
228
|
+
" sorted parameters by key and expected any registered array to respect this order."
|
|
229
|
+
" In the current and any future version the order as given by the pbounds dictionary will be"
|
|
230
|
+
" used. If you wish to retain sorted parameters, please manually sort your pbounds"
|
|
232
231
|
" dictionary before constructing the optimizer."
|
|
233
232
|
)
|
|
234
233
|
warn(msg, stacklevel=1)
|
|
@@ -236,9 +235,7 @@ class BayesianOptimization(Observable):
|
|
|
236
235
|
self._space.register(params, target, constraint_value)
|
|
237
236
|
self.dispatch(Events.OPTIMIZATION_STEP)
|
|
238
237
|
|
|
239
|
-
def probe(
|
|
240
|
-
self, params: Mapping[str, float] | Sequence[float] | NDArray[Float], lazy: bool = True
|
|
241
|
-
) -> None:
|
|
238
|
+
def probe(self, params: ParamsType, lazy: bool = True) -> None:
|
|
242
239
|
"""Evaluate the function at the given points.
|
|
243
240
|
|
|
244
241
|
Useful to guide the optimizer.
|
|
@@ -255,10 +252,10 @@ class BayesianOptimization(Observable):
|
|
|
255
252
|
# TODO: remove in future version
|
|
256
253
|
if isinstance(params, np.ndarray) and not self._sorting_warning_already_shown:
|
|
257
254
|
msg = (
|
|
258
|
-
"You're attempting to register an np.ndarray.
|
|
259
|
-
" parameters by key and
|
|
260
|
-
"
|
|
261
|
-
"
|
|
255
|
+
"You're attempting to register an np.ndarray. In previous versions, the optimizer internally"
|
|
256
|
+
" sorted parameters by key and expected any registered array to respect this order."
|
|
257
|
+
" In the current and any future version the order as given by the pbounds dictionary will be"
|
|
258
|
+
" used. If you wish to retain sorted parameters, please manually sort your pbounds"
|
|
262
259
|
" dictionary before constructing the optimizer."
|
|
263
260
|
)
|
|
264
261
|
warn(msg, stacklevel=1)
|
|
@@ -270,10 +267,10 @@ class BayesianOptimization(Observable):
|
|
|
270
267
|
self._space.probe(params)
|
|
271
268
|
self.dispatch(Events.OPTIMIZATION_STEP)
|
|
272
269
|
|
|
273
|
-
def suggest(self) -> dict[str, float]:
|
|
270
|
+
def suggest(self) -> dict[str, float | NDArray[Float]]:
|
|
274
271
|
"""Suggest a promising point to probe next."""
|
|
275
272
|
if len(self._space) == 0:
|
|
276
|
-
return self._space.array_to_params(self._space.random_sample())
|
|
273
|
+
return self._space.array_to_params(self._space.random_sample(random_state=self._random_state))
|
|
277
274
|
|
|
278
275
|
# Finding argmax of the acquisition function.
|
|
279
276
|
suggestion = self._acquisition_function.suggest(gp=self._gp, target_space=self._space, fit_gp=True)
|
|
@@ -292,7 +289,7 @@ class BayesianOptimization(Observable):
|
|
|
292
289
|
init_points = max(init_points, 1)
|
|
293
290
|
|
|
294
291
|
for _ in range(init_points):
|
|
295
|
-
sample = self._space.random_sample()
|
|
292
|
+
sample = self._space.random_sample(random_state=self._random_state)
|
|
296
293
|
self._queue.append(self._space.array_to_params(sample))
|
|
297
294
|
|
|
298
295
|
def _prime_subscriptions(self) -> None:
|
|
@@ -344,7 +341,7 @@ class BayesianOptimization(Observable):
|
|
|
344
341
|
|
|
345
342
|
self.dispatch(Events.OPTIMIZATION_END)
|
|
346
343
|
|
|
347
|
-
def set_bounds(self, new_bounds:
|
|
344
|
+
def set_bounds(self, new_bounds: BoundsMapping) -> None:
|
|
348
345
|
"""Modify the bounds of the search space.
|
|
349
346
|
|
|
350
347
|
Parameters
|
|
@@ -356,4 +353,6 @@ class BayesianOptimization(Observable):
|
|
|
356
353
|
|
|
357
354
|
def set_gp_params(self, **params: Any) -> None:
|
|
358
355
|
"""Set parameters of the internal Gaussian Process Regressor."""
|
|
356
|
+
if "kernel" in params:
|
|
357
|
+
params["kernel"] = wrap_kernel(kernel=params["kernel"], transform=self._space.kernel_transform)
|
|
359
358
|
self._gp.set_params(**params)
|
|
@@ -9,6 +9,8 @@ from scipy.stats import norm
|
|
|
9
9
|
from sklearn.gaussian_process import GaussianProcessRegressor
|
|
10
10
|
from sklearn.gaussian_process.kernels import Matern
|
|
11
11
|
|
|
12
|
+
from bayes_opt.parameter import wrap_kernel
|
|
13
|
+
|
|
12
14
|
if TYPE_CHECKING:
|
|
13
15
|
from collections.abc import Callable
|
|
14
16
|
|
|
@@ -55,6 +57,7 @@ class ConstraintModel:
|
|
|
55
57
|
fun: Callable[..., float] | Callable[..., NDArray[Float]] | None,
|
|
56
58
|
lb: float | NDArray[Float],
|
|
57
59
|
ub: float | NDArray[Float],
|
|
60
|
+
transform: Callable[[Any], Any] | None = None,
|
|
58
61
|
random_state: int | RandomState | None = None,
|
|
59
62
|
) -> None:
|
|
60
63
|
self.fun = fun
|
|
@@ -68,7 +71,7 @@ class ConstraintModel:
|
|
|
68
71
|
|
|
69
72
|
self._model = [
|
|
70
73
|
GaussianProcessRegressor(
|
|
71
|
-
kernel=Matern(nu=2.5),
|
|
74
|
+
kernel=wrap_kernel(Matern(nu=2.5), transform) if transform is not None else Matern(nu=2.5),
|
|
72
75
|
alpha=1e-6,
|
|
73
76
|
normalize_y=True,
|
|
74
77
|
n_restarts_optimizer=5,
|
|
@@ -14,6 +14,7 @@ from warnings import warn
|
|
|
14
14
|
|
|
15
15
|
import numpy as np
|
|
16
16
|
|
|
17
|
+
from bayes_opt.parameter import FloatParameter
|
|
17
18
|
from bayes_opt.target_space import TargetSpace
|
|
18
19
|
|
|
19
20
|
if TYPE_CHECKING:
|
|
@@ -62,22 +63,19 @@ class SequentialDomainReductionTransformer(DomainTransformer):
|
|
|
62
63
|
|
|
63
64
|
def __init__(
|
|
64
65
|
self,
|
|
66
|
+
parameters: Iterable[str] | None = None,
|
|
65
67
|
gamma_osc: float = 0.7,
|
|
66
68
|
gamma_pan: float = 1.0,
|
|
67
69
|
eta: float = 0.9,
|
|
68
70
|
minimum_window: NDArray[Float] | Sequence[float] | Mapping[str, float] | float = 0.0,
|
|
69
71
|
) -> None:
|
|
72
|
+
# TODO: Ensure that this is only applied to continuous parameters
|
|
73
|
+
self.parameters = parameters
|
|
70
74
|
self.gamma_osc = gamma_osc
|
|
71
75
|
self.gamma_pan = gamma_pan
|
|
72
76
|
self.eta = eta
|
|
73
77
|
|
|
74
|
-
self.minimum_window_value
|
|
75
|
-
if isinstance(minimum_window, Mapping):
|
|
76
|
-
self.minimum_window_value = [
|
|
77
|
-
item[1] for item in sorted(minimum_window.items(), key=lambda x: x[0])
|
|
78
|
-
]
|
|
79
|
-
else:
|
|
80
|
-
self.minimum_window_value = minimum_window
|
|
78
|
+
self.minimum_window_value = minimum_window
|
|
81
79
|
|
|
82
80
|
def initialize(self, target_space: TargetSpace) -> None:
|
|
83
81
|
"""Initialize all of the parameters.
|
|
@@ -87,6 +85,15 @@ class SequentialDomainReductionTransformer(DomainTransformer):
|
|
|
87
85
|
target_space : TargetSpace
|
|
88
86
|
TargetSpace this DomainTransformer operates on.
|
|
89
87
|
"""
|
|
88
|
+
if isinstance(self.minimum_window_value, Mapping):
|
|
89
|
+
self.minimum_window_value = [self.minimum_window_value[key] for key in target_space.keys]
|
|
90
|
+
else:
|
|
91
|
+
self.minimum_window_value = self.minimum_window_value
|
|
92
|
+
|
|
93
|
+
any_not_float = any([not isinstance(p, FloatParameter) for p in target_space._params_config.values()])
|
|
94
|
+
if any_not_float:
|
|
95
|
+
msg = "Domain reduction is only supported for all-FloatParameter optimization."
|
|
96
|
+
raise ValueError(msg)
|
|
90
97
|
# Set the original bounds
|
|
91
98
|
self.original_bounds = np.copy(target_space.bounds)
|
|
92
99
|
self.bounds = [self.original_bounds]
|
|
@@ -127,19 +127,19 @@ class ScreenLogger(_Tracker):
|
|
|
127
127
|
x_ = ("T" if x else "F") if self._default_cell_size < 5 else str(x)
|
|
128
128
|
return f"{x_:<{self._default_cell_size}}"
|
|
129
129
|
|
|
130
|
-
def
|
|
131
|
-
"""Format a
|
|
130
|
+
def _format_str(self, str_: str) -> str:
|
|
131
|
+
"""Format a str.
|
|
132
132
|
|
|
133
133
|
Parameters
|
|
134
134
|
----------
|
|
135
|
-
|
|
135
|
+
str_ : str
|
|
136
136
|
Value to format.
|
|
137
137
|
|
|
138
138
|
Returns
|
|
139
139
|
-------
|
|
140
140
|
A stringified, formatted version of `x`.
|
|
141
141
|
"""
|
|
142
|
-
s = f"{
|
|
142
|
+
s = f"{str_:^{self._default_cell_size}}"
|
|
143
143
|
if len(s) > self._default_cell_size:
|
|
144
144
|
return s[: self._default_cell_size - 3] + "..."
|
|
145
145
|
return s
|
|
@@ -168,7 +168,10 @@ class ScreenLogger(_Tracker):
|
|
|
168
168
|
if self._is_constrained:
|
|
169
169
|
cells[2] = self._format_bool(res["allowed"])
|
|
170
170
|
params = res.get("params", {})
|
|
171
|
-
cells[3:] = [
|
|
171
|
+
cells[3:] = [
|
|
172
|
+
instance.space._params_config[key].to_string(val, self._default_cell_size)
|
|
173
|
+
for key, val in params.items()
|
|
174
|
+
]
|
|
172
175
|
|
|
173
176
|
return "| " + " | ".join(colour + x + self._colour_reset for x in cells if x is not None) + " |"
|
|
174
177
|
|
|
@@ -188,10 +191,10 @@ class ScreenLogger(_Tracker):
|
|
|
188
191
|
# iter, target, allowed [, *params]
|
|
189
192
|
cells: list[str | None] = [None] * (3 + len(keys))
|
|
190
193
|
|
|
191
|
-
cells[:2] = self.
|
|
194
|
+
cells[:2] = self._format_str("iter"), self._format_str("target")
|
|
192
195
|
if self._is_constrained:
|
|
193
|
-
cells[2] = self.
|
|
194
|
-
cells[3:] = [self.
|
|
196
|
+
cells[2] = self._format_str("allowed")
|
|
197
|
+
cells[3:] = [self._format_str(key) for key in keys]
|
|
195
198
|
|
|
196
199
|
line = "| " + " | ".join(x for x in cells if x is not None) + " |"
|
|
197
200
|
self._header_length = len(line)
|