ennbo 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
- enn/enn/enn.py +71 -31
- enn/enn/enn_fit.py +26 -24
- enn/enn/enn_normal.py +3 -2
- enn/enn/enn_params.py +13 -0
- enn/enn/enn_util.py +40 -12
- enn/turbo/base_turbo_impl.py +53 -7
- enn/turbo/lhd_only_impl.py +7 -0
- enn/turbo/morbo_trust_region.py +189 -0
- enn/turbo/no_trust_region.py +65 -0
- enn/turbo/proposal.py +11 -2
- enn/turbo/turbo_config.py +48 -4
- enn/turbo/turbo_enn_impl.py +46 -21
- enn/turbo/turbo_gp.py +9 -1
- enn/turbo/turbo_mode_impl.py +11 -2
- enn/turbo/turbo_one_impl.py +163 -24
- enn/turbo/turbo_optimizer.py +246 -58
- enn/turbo/turbo_trust_region.py +8 -10
- enn/turbo/turbo_utils.py +116 -26
- enn/turbo/turbo_zero_impl.py +5 -0
- {ennbo-0.1.0.dist-info → ennbo-0.1.2.dist-info}/METADATA +5 -4
- ennbo-0.1.2.dist-info/RECORD +29 -0
- ennbo-0.1.0.dist-info/RECORD +0 -27
- {ennbo-0.1.0.dist-info → ennbo-0.1.2.dist-info}/WHEEL +0 -0
- {ennbo-0.1.0.dist-info → ennbo-0.1.2.dist-info}/licenses/LICENSE +0 -0
enn/turbo/turbo_optimizer.py
CHANGED
|
@@ -4,7 +4,13 @@ from dataclasses import dataclass
|
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Callable
|
|
5
5
|
|
|
6
6
|
from .proposal import select_uniform
|
|
7
|
-
from .turbo_config import
|
|
7
|
+
from .turbo_config import (
|
|
8
|
+
LHDOnlyConfig,
|
|
9
|
+
TurboConfig,
|
|
10
|
+
TurboENNConfig,
|
|
11
|
+
TurboOneConfig,
|
|
12
|
+
TurboZeroConfig,
|
|
13
|
+
)
|
|
8
14
|
from .turbo_utils import from_unit, latin_hypercube, to_unit
|
|
9
15
|
|
|
10
16
|
|
|
@@ -32,17 +38,51 @@ class TurboOptimizer:
|
|
|
32
38
|
config: TurboConfig | None = None,
|
|
33
39
|
) -> None:
|
|
34
40
|
import numpy as np
|
|
35
|
-
from scipy.stats import qmc
|
|
36
41
|
|
|
37
42
|
from .turbo_mode import TurboMode
|
|
38
43
|
|
|
39
44
|
if config is None:
|
|
40
|
-
|
|
45
|
+
match mode:
|
|
46
|
+
case TurboMode.TURBO_ONE:
|
|
47
|
+
config = TurboOneConfig()
|
|
48
|
+
case TurboMode.TURBO_ZERO:
|
|
49
|
+
config = TurboZeroConfig()
|
|
50
|
+
case TurboMode.TURBO_ENN:
|
|
51
|
+
config = TurboENNConfig()
|
|
52
|
+
case TurboMode.LHD_ONLY:
|
|
53
|
+
config = LHDOnlyConfig()
|
|
54
|
+
case _:
|
|
55
|
+
raise ValueError(f"Unknown mode: {mode}")
|
|
56
|
+
else:
|
|
57
|
+
match mode:
|
|
58
|
+
case TurboMode.TURBO_ONE:
|
|
59
|
+
if not isinstance(config, TurboOneConfig):
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"mode={mode} requires TurboOneConfig, got {type(config).__name__}"
|
|
62
|
+
)
|
|
63
|
+
case TurboMode.TURBO_ZERO:
|
|
64
|
+
if not isinstance(config, TurboZeroConfig):
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"mode={mode} requires TurboZeroConfig, got {type(config).__name__}"
|
|
67
|
+
)
|
|
68
|
+
case TurboMode.TURBO_ENN:
|
|
69
|
+
if not isinstance(config, TurboENNConfig):
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"mode={mode} requires TurboENNConfig, got {type(config).__name__}"
|
|
72
|
+
)
|
|
73
|
+
case TurboMode.LHD_ONLY:
|
|
74
|
+
if not isinstance(config, LHDOnlyConfig):
|
|
75
|
+
raise ValueError(
|
|
76
|
+
f"mode={mode} requires LHDOnlyConfig, got {type(config).__name__}"
|
|
77
|
+
)
|
|
78
|
+
case _:
|
|
79
|
+
raise ValueError(f"Unknown mode: {mode}")
|
|
41
80
|
self._config = config
|
|
42
81
|
|
|
82
|
+
bounds = np.asarray(bounds, dtype=float)
|
|
43
83
|
if bounds.ndim != 2 or bounds.shape[1] != 2:
|
|
44
84
|
raise ValueError(bounds.shape)
|
|
45
|
-
self._bounds =
|
|
85
|
+
self._bounds = bounds
|
|
46
86
|
self._num_dim = self._bounds.shape[0]
|
|
47
87
|
self._mode = mode
|
|
48
88
|
num_candidates = config.num_candidates
|
|
@@ -53,11 +93,12 @@ class TurboOptimizer:
|
|
|
53
93
|
if self._num_candidates <= 0:
|
|
54
94
|
raise ValueError(self._num_candidates)
|
|
55
95
|
self._rng = rng
|
|
56
|
-
|
|
57
|
-
self.
|
|
58
|
-
self.
|
|
59
|
-
self.
|
|
60
|
-
self._yvar_obs_list: list = []
|
|
96
|
+
self._sobol_seed_base = int(self._rng.integers(2**31 - 1))
|
|
97
|
+
self._x_obs_list: list[list[float]] = []
|
|
98
|
+
self._y_obs_list: list[float] | list[list[float]] = []
|
|
99
|
+
self._y_tr_list: list[float] = []
|
|
100
|
+
self._yvar_obs_list: list[float] | list[list[float]] = []
|
|
101
|
+
self._expects_yvar: bool | None = None
|
|
61
102
|
match mode:
|
|
62
103
|
case TurboMode.TURBO_ONE:
|
|
63
104
|
from .turbo_one_impl import TurboOneImpl
|
|
@@ -100,36 +141,37 @@ class TurboOptimizer:
|
|
|
100
141
|
if num_init_val <= 0:
|
|
101
142
|
raise ValueError(f"num_init must be > 0, got {num_init_val}")
|
|
102
143
|
self._num_init = num_init_val
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
self.
|
|
106
|
-
|
|
107
|
-
else:
|
|
108
|
-
self._init_lhd = from_unit(
|
|
109
|
-
latin_hypercube(self._num_init, self._num_dim, rng=self._rng),
|
|
110
|
-
self._bounds,
|
|
111
|
-
)
|
|
144
|
+
self._init_lhd = from_unit(
|
|
145
|
+
latin_hypercube(self._num_init, self._num_dim, rng=self._rng),
|
|
146
|
+
self._bounds,
|
|
147
|
+
)
|
|
112
148
|
self._init_idx = 0
|
|
113
149
|
self._dt_fit: float = 0.0
|
|
114
150
|
self._dt_sel: float = 0.0
|
|
115
|
-
|
|
151
|
+
|
|
152
|
+
def _sobol_seed_for_state(self, *, n_obs: int, num_arms: int) -> int:
|
|
153
|
+
mask64 = (1 << 64) - 1
|
|
154
|
+
|
|
155
|
+
x = int(self._sobol_seed_base) & mask64
|
|
156
|
+
x ^= (int(n_obs) + 1) * 0x9E3779B97F4A7C15 & mask64
|
|
157
|
+
x ^= (int(num_arms) + 1) * 0xBF58476D1CE4E5B9 & mask64
|
|
158
|
+
x = (x + 0x9E3779B97F4A7C15) & mask64
|
|
159
|
+
z = x
|
|
160
|
+
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9 & mask64
|
|
161
|
+
z = (z ^ (z >> 27)) * 0x94D049BB133111EB & mask64
|
|
162
|
+
z = z ^ (z >> 31)
|
|
163
|
+
return int(z & 0xFFFFFFFF)
|
|
116
164
|
|
|
117
165
|
@property
|
|
118
166
|
def tr_obs_count(self) -> int:
|
|
119
167
|
return len(self._y_obs_list)
|
|
120
168
|
|
|
121
|
-
@property
|
|
122
|
-
def best_tr_value(self) -> float | None:
|
|
123
|
-
import numpy as np
|
|
124
|
-
|
|
125
|
-
if len(self._y_obs_list) == 0:
|
|
126
|
-
return None
|
|
127
|
-
return float(np.max(self._y_obs_list))
|
|
128
|
-
|
|
129
169
|
@property
|
|
130
170
|
def tr_length(self) -> float | None:
|
|
131
171
|
if self._tr_state is None:
|
|
132
172
|
return None
|
|
173
|
+
if not hasattr(self._tr_state, "length"):
|
|
174
|
+
return None
|
|
133
175
|
return float(self._tr_state.length)
|
|
134
176
|
|
|
135
177
|
def telemetry(self) -> Telemetry:
|
|
@@ -139,14 +181,14 @@ class TurboOptimizer:
|
|
|
139
181
|
num_arms = int(num_arms)
|
|
140
182
|
if num_arms <= 0:
|
|
141
183
|
raise ValueError(num_arms)
|
|
142
|
-
|
|
184
|
+
# For morbo, defer TR creation until tell() when we can infer num_metrics
|
|
185
|
+
is_morbo = self._config.tr_type == "morbo"
|
|
186
|
+
if self._tr_state is None and not is_morbo:
|
|
143
187
|
self._tr_state = self._mode_impl.create_trust_region(
|
|
144
|
-
self._num_dim, num_arms
|
|
188
|
+
self._num_dim, num_arms, self._rng
|
|
145
189
|
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
self._tr_state.length = min(self._tr_state.length, 0.1)
|
|
149
|
-
self._tr_state.length_init = min(self._tr_state.length_init, 0.1)
|
|
190
|
+
if self._tr_state is not None:
|
|
191
|
+
self._tr_state.validate_request(num_arms)
|
|
150
192
|
early_result = self._mode_impl.try_early_ask(
|
|
151
193
|
num_arms,
|
|
152
194
|
self._x_obs_list,
|
|
@@ -176,6 +218,11 @@ class TurboOptimizer:
|
|
|
176
218
|
|
|
177
219
|
def _ask_normal(self, num_arms: int, *, is_fallback: bool = False) -> np.ndarray:
|
|
178
220
|
import numpy as np
|
|
221
|
+
from scipy.stats import qmc
|
|
222
|
+
|
|
223
|
+
# For morbo, TR is created in tell() - if still None, return LHD
|
|
224
|
+
if self._tr_state is None:
|
|
225
|
+
return self._draw_initial(num_arms)
|
|
179
226
|
|
|
180
227
|
if self._tr_state.needs_restart():
|
|
181
228
|
self._tr_state.restart()
|
|
@@ -187,6 +234,7 @@ class TurboOptimizer:
|
|
|
187
234
|
self._num_init,
|
|
188
235
|
)
|
|
189
236
|
if should_reset_init:
|
|
237
|
+
self._y_tr_list = []
|
|
190
238
|
self._init_idx = new_init_idx
|
|
191
239
|
self._init_lhd = from_unit(
|
|
192
240
|
latin_hypercube(self._num_init, self._num_dim, rng=self._rng),
|
|
@@ -203,7 +251,7 @@ class TurboOptimizer:
|
|
|
203
251
|
import time
|
|
204
252
|
|
|
205
253
|
t0_fit = time.perf_counter()
|
|
206
|
-
_gp_model, _gp_y_mean_fitted, _gp_y_std_fitted,
|
|
254
|
+
_gp_model, _gp_y_mean_fitted, _gp_y_std_fitted, lengthscales = (
|
|
207
255
|
self._mode_impl.prepare_ask(
|
|
208
256
|
self._x_obs_list,
|
|
209
257
|
self._y_obs_list,
|
|
@@ -216,19 +264,27 @@ class TurboOptimizer:
|
|
|
216
264
|
self._dt_fit = time.perf_counter() - t0_fit
|
|
217
265
|
|
|
218
266
|
x_center = self._mode_impl.get_x_center(
|
|
219
|
-
self._x_obs_list,
|
|
267
|
+
self._x_obs_list,
|
|
268
|
+
self._y_obs_list,
|
|
269
|
+
self._rng,
|
|
270
|
+
self._tr_state,
|
|
220
271
|
)
|
|
221
272
|
if x_center is None:
|
|
222
273
|
if len(self._y_obs_list) == 0:
|
|
223
274
|
raise RuntimeError("no observations")
|
|
224
275
|
x_center = np.full(self._num_dim, 0.5)
|
|
225
276
|
|
|
277
|
+
sobol_seed = self._sobol_seed_for_state(
|
|
278
|
+
n_obs=len(self._x_obs_list),
|
|
279
|
+
num_arms=num_arms,
|
|
280
|
+
)
|
|
281
|
+
sobol_engine = qmc.Sobol(d=self._num_dim, scramble=True, seed=sobol_seed)
|
|
226
282
|
x_cand = self._tr_state.generate_candidates(
|
|
227
283
|
x_center,
|
|
228
|
-
|
|
284
|
+
lengthscales,
|
|
229
285
|
self._num_candidates,
|
|
230
286
|
self._rng,
|
|
231
|
-
|
|
287
|
+
sobol_engine,
|
|
232
288
|
)
|
|
233
289
|
|
|
234
290
|
def fallback_fn(x, n):
|
|
@@ -244,12 +300,19 @@ class TurboOptimizer:
|
|
|
244
300
|
self._rng,
|
|
245
301
|
fallback_fn,
|
|
246
302
|
from_unit_fn,
|
|
303
|
+
tr_state=self._tr_state,
|
|
247
304
|
)
|
|
248
305
|
self._dt_sel = time.perf_counter() - t0_sel
|
|
249
306
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
307
|
+
# For morbo, TR is updated in tell() with raw multi-objective y
|
|
308
|
+
if self._config.tr_type != "morbo":
|
|
309
|
+
self._mode_impl.update_trust_region(
|
|
310
|
+
self._tr_state,
|
|
311
|
+
self._x_obs_list,
|
|
312
|
+
self._y_tr_list,
|
|
313
|
+
x_center=x_center,
|
|
314
|
+
k=self._k,
|
|
315
|
+
)
|
|
253
316
|
return selected
|
|
254
317
|
|
|
255
318
|
def _trim_trailing_obs(self) -> None:
|
|
@@ -259,8 +322,8 @@ class TurboOptimizer:
|
|
|
259
322
|
|
|
260
323
|
if len(self._x_obs_list) <= self._trailing_obs:
|
|
261
324
|
return
|
|
262
|
-
|
|
263
|
-
incumbent_idx = argmax_random_tie(
|
|
325
|
+
y_tr_array = np.asarray(self._y_tr_list, dtype=float)
|
|
326
|
+
incumbent_idx = argmax_random_tie(y_tr_array, rng=self._rng)
|
|
264
327
|
num_total = len(self._x_obs_list)
|
|
265
328
|
start_idx = max(0, num_total - self._trailing_obs)
|
|
266
329
|
if incumbent_idx < start_idx:
|
|
@@ -274,21 +337,23 @@ class TurboOptimizer:
|
|
|
274
337
|
if incumbent_idx not in indices:
|
|
275
338
|
raise RuntimeError("Incumbent must be included in trimmed list")
|
|
276
339
|
x_array = np.asarray(self._x_obs_list, dtype=float)
|
|
277
|
-
incumbent_value =
|
|
340
|
+
incumbent_value = y_tr_array[incumbent_idx]
|
|
278
341
|
self._x_obs_list = x_array[indices].tolist()
|
|
279
|
-
self._y_obs_list =
|
|
280
|
-
|
|
342
|
+
y_obs_array = np.asarray(self._y_obs_list, dtype=float)
|
|
343
|
+
self._y_obs_list = y_obs_array[indices].tolist()
|
|
344
|
+
self._y_tr_list = y_tr_array[indices].tolist()
|
|
345
|
+
if len(self._yvar_obs_list) == len(y_obs_array):
|
|
281
346
|
yvar_array = np.asarray(self._yvar_obs_list, dtype=float)
|
|
282
347
|
self._yvar_obs_list = yvar_array[indices].tolist()
|
|
283
|
-
y_trimmed = np.asarray(self.
|
|
348
|
+
y_trimmed = np.asarray(self._y_tr_list, dtype=float)
|
|
284
349
|
if not np.any(np.abs(y_trimmed - incumbent_value) < 1e-10):
|
|
285
350
|
raise RuntimeError("Incumbent value must be preserved in trimmed list")
|
|
286
351
|
|
|
287
352
|
def tell(
|
|
288
353
|
self,
|
|
289
|
-
x: np.ndarray
|
|
290
|
-
y: np.ndarray
|
|
291
|
-
y_var: np.ndarray |
|
|
354
|
+
x: np.ndarray,
|
|
355
|
+
y: np.ndarray,
|
|
356
|
+
y_var: np.ndarray | None = None,
|
|
292
357
|
) -> np.ndarray:
|
|
293
358
|
import numpy as np
|
|
294
359
|
|
|
@@ -296,8 +361,37 @@ class TurboOptimizer:
|
|
|
296
361
|
y = np.asarray(y, dtype=float)
|
|
297
362
|
if x.ndim != 2 or x.shape[1] != self._num_dim:
|
|
298
363
|
raise ValueError(x.shape)
|
|
299
|
-
|
|
300
|
-
|
|
364
|
+
|
|
365
|
+
# morbo accepts 2D y with shape (n, num_metrics)
|
|
366
|
+
is_morbo = self._config.tr_type == "morbo"
|
|
367
|
+
if is_morbo:
|
|
368
|
+
if y.ndim == 1:
|
|
369
|
+
y = y.reshape(-1, 1)
|
|
370
|
+
if y.ndim != 2 or y.shape[0] != x.shape[0]:
|
|
371
|
+
raise ValueError((x.shape, y.shape))
|
|
372
|
+
num_metrics = y.shape[1]
|
|
373
|
+
# Create TR lazily for morbo, inferring num_metrics from y
|
|
374
|
+
if self._tr_state is None:
|
|
375
|
+
self._tr_state = self._mode_impl.create_trust_region(
|
|
376
|
+
self._num_dim, x.shape[0], self._rng, num_metrics=num_metrics
|
|
377
|
+
)
|
|
378
|
+
cfg_num_metrics = self._config.num_metrics
|
|
379
|
+
if cfg_num_metrics is not None and num_metrics != cfg_num_metrics:
|
|
380
|
+
raise ValueError(
|
|
381
|
+
f"y has {num_metrics} metrics but expected {cfg_num_metrics}"
|
|
382
|
+
)
|
|
383
|
+
else:
|
|
384
|
+
if self._tr_state is None:
|
|
385
|
+
raise ValueError("tell() called before ask()")
|
|
386
|
+
if y.ndim != 1 or y.shape[0] != x.shape[0]:
|
|
387
|
+
raise ValueError((x.shape, y.shape))
|
|
388
|
+
|
|
389
|
+
if self._expects_yvar is None:
|
|
390
|
+
self._expects_yvar = y_var is not None
|
|
391
|
+
if (y_var is not None) != bool(self._expects_yvar):
|
|
392
|
+
raise ValueError(
|
|
393
|
+
f"y_var must be {'provided' if self._expects_yvar else 'omitted'} on every tell() call"
|
|
394
|
+
)
|
|
301
395
|
if y_var is not None:
|
|
302
396
|
y_var = np.asarray(y_var, dtype=float)
|
|
303
397
|
if y_var.shape != y.shape:
|
|
@@ -305,14 +399,108 @@ class TurboOptimizer:
|
|
|
305
399
|
if x.shape[0] == 0:
|
|
306
400
|
return np.array([], dtype=float)
|
|
307
401
|
x_unit = to_unit(x, self._bounds)
|
|
308
|
-
y_estimate = self._mode_impl.estimate_y(x_unit, y)
|
|
309
402
|
self._x_obs_list.extend(x_unit.tolist())
|
|
310
|
-
|
|
311
|
-
if
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
403
|
+
|
|
404
|
+
if is_morbo:
|
|
405
|
+
y_estimate = y
|
|
406
|
+
self._y_obs_list.extend(y.tolist())
|
|
407
|
+
if y_var is not None:
|
|
408
|
+
self._yvar_obs_list.extend(y_var.tolist())
|
|
409
|
+
y_all = np.asarray(self._y_obs_list, dtype=float)
|
|
410
|
+
if y_all.ndim == 1:
|
|
411
|
+
y_all = y_all.reshape(-1, num_metrics)
|
|
412
|
+
x_all = np.asarray(self._x_obs_list, dtype=float)
|
|
413
|
+
self._tr_state.update_xy(x_all, y_all, k=self._k)
|
|
414
|
+
else:
|
|
415
|
+
from .turbo_mode import TurboMode
|
|
416
|
+
|
|
417
|
+
self._y_obs_list.extend(y.tolist())
|
|
418
|
+
if y_var is not None:
|
|
419
|
+
self._yvar_obs_list.extend(y_var.tolist())
|
|
420
|
+
|
|
421
|
+
if self._mode in (TurboMode.TURBO_ONE, TurboMode.TURBO_ENN):
|
|
422
|
+
self._mode_impl.prepare_ask(
|
|
423
|
+
self._x_obs_list,
|
|
424
|
+
self._y_obs_list,
|
|
425
|
+
self._yvar_obs_list,
|
|
426
|
+
self._num_dim,
|
|
427
|
+
0,
|
|
428
|
+
rng=self._rng,
|
|
429
|
+
)
|
|
430
|
+
x_all = np.asarray(self._x_obs_list, dtype=float)
|
|
431
|
+
y_all = np.asarray(self._y_obs_list, dtype=float)
|
|
432
|
+
if self._mode == TurboMode.TURBO_ONE:
|
|
433
|
+
# We intentionally evaluate the GP posterior at the training inputs
|
|
434
|
+
# (the observed points) right after conditioning the model. GPyTorch
|
|
435
|
+
# warns about this in debug mode, but it's expected for our TR logic.
|
|
436
|
+
import warnings
|
|
437
|
+
|
|
438
|
+
try:
|
|
439
|
+
from gpytorch.utils.warnings import GPInputWarning
|
|
440
|
+
except Exception: # pragma: no cover
|
|
441
|
+
GPInputWarning = None
|
|
442
|
+
|
|
443
|
+
if GPInputWarning is None:
|
|
444
|
+
mu_all = np.asarray(
|
|
445
|
+
self._mode_impl.estimate_y(x_all, y_all), dtype=float
|
|
446
|
+
).reshape(-1)
|
|
447
|
+
else:
|
|
448
|
+
with warnings.catch_warnings():
|
|
449
|
+
warnings.filterwarnings(
|
|
450
|
+
"ignore",
|
|
451
|
+
message=r"The input matches the stored training data\..*",
|
|
452
|
+
category=GPInputWarning,
|
|
453
|
+
)
|
|
454
|
+
mu_all = np.asarray(
|
|
455
|
+
self._mode_impl.estimate_y(x_all, y_all), dtype=float
|
|
456
|
+
).reshape(-1)
|
|
457
|
+
else:
|
|
458
|
+
mu_all = np.asarray(
|
|
459
|
+
self._mode_impl.estimate_y(x_all, y_all), dtype=float
|
|
460
|
+
).reshape(-1)
|
|
461
|
+
self._y_tr_list = mu_all.tolist()
|
|
462
|
+
if self._mode == TurboMode.TURBO_ONE:
|
|
463
|
+
import warnings
|
|
464
|
+
|
|
465
|
+
try:
|
|
466
|
+
from gpytorch.utils.warnings import GPInputWarning
|
|
467
|
+
except Exception: # pragma: no cover
|
|
468
|
+
GPInputWarning = None
|
|
469
|
+
|
|
470
|
+
if GPInputWarning is None:
|
|
471
|
+
y_estimate = np.asarray(
|
|
472
|
+
self._mode_impl.estimate_y(x_unit, y), dtype=float
|
|
473
|
+
)
|
|
474
|
+
else:
|
|
475
|
+
with warnings.catch_warnings():
|
|
476
|
+
warnings.filterwarnings(
|
|
477
|
+
"ignore",
|
|
478
|
+
message=r"The input matches the stored training data\..*",
|
|
479
|
+
category=GPInputWarning,
|
|
480
|
+
)
|
|
481
|
+
y_estimate = np.asarray(
|
|
482
|
+
self._mode_impl.estimate_y(x_unit, y), dtype=float
|
|
483
|
+
)
|
|
484
|
+
else:
|
|
485
|
+
y_estimate = np.asarray(
|
|
486
|
+
self._mode_impl.estimate_y(x_unit, y), dtype=float
|
|
487
|
+
)
|
|
488
|
+
else:
|
|
489
|
+
y_estimate = self._mode_impl.estimate_y(x_unit, y)
|
|
490
|
+
self._y_tr_list.extend(np.asarray(y_estimate, dtype=float).tolist())
|
|
491
|
+
|
|
492
|
+
if self._trailing_obs is not None:
|
|
493
|
+
self._trim_trailing_obs()
|
|
494
|
+
prev_n = int(getattr(self._tr_state, "prev_num_obs", 0))
|
|
495
|
+
if prev_n > 0 and prev_n <= len(self._y_tr_list):
|
|
496
|
+
if hasattr(self._tr_state, "best_value"):
|
|
497
|
+
self._tr_state.best_value = float(
|
|
498
|
+
np.max(np.asarray(self._y_tr_list, dtype=float)[:prev_n])
|
|
499
|
+
)
|
|
500
|
+
self._mode_impl.update_trust_region(
|
|
501
|
+
self._tr_state, self._x_obs_list, self._y_tr_list, k=self._k
|
|
502
|
+
)
|
|
503
|
+
|
|
316
504
|
return y_estimate
|
|
317
505
|
|
|
318
506
|
def _draw_initial(self, num_arms: int) -> np.ndarray:
|
enn/turbo/turbo_trust_region.py
CHANGED
|
@@ -89,14 +89,14 @@ class TurboTrustRegion:
|
|
|
89
89
|
)
|
|
90
90
|
|
|
91
91
|
def compute_bounds_1d(
|
|
92
|
-
self, x_center: np.ndarray | Any,
|
|
92
|
+
self, x_center: np.ndarray | Any, lengthscales: np.ndarray | None = None
|
|
93
93
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
94
94
|
import numpy as np
|
|
95
95
|
|
|
96
|
-
if
|
|
96
|
+
if lengthscales is None:
|
|
97
97
|
half_length = 0.5 * self.length
|
|
98
98
|
else:
|
|
99
|
-
half_length =
|
|
99
|
+
half_length = lengthscales * self.length / 2.0
|
|
100
100
|
lb = np.clip(x_center - half_length, 0.0, 1.0)
|
|
101
101
|
ub = np.clip(x_center + half_length, 0.0, 1.0)
|
|
102
102
|
return lb, ub
|
|
@@ -104,20 +104,18 @@ class TurboTrustRegion:
|
|
|
104
104
|
def generate_candidates(
|
|
105
105
|
self,
|
|
106
106
|
x_center: np.ndarray,
|
|
107
|
-
|
|
107
|
+
lengthscales: np.ndarray | None,
|
|
108
108
|
num_candidates: int,
|
|
109
109
|
rng: Generator,
|
|
110
110
|
sobol_engine: QMCEngine,
|
|
111
111
|
) -> np.ndarray:
|
|
112
|
-
from .turbo_utils import
|
|
112
|
+
from .turbo_utils import generate_trust_region_candidates
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
return raasp(
|
|
114
|
+
return generate_trust_region_candidates(
|
|
116
115
|
x_center,
|
|
117
|
-
|
|
118
|
-
ub,
|
|
116
|
+
lengthscales,
|
|
119
117
|
num_candidates,
|
|
120
|
-
|
|
118
|
+
compute_bounds_1d=self.compute_bounds_1d,
|
|
121
119
|
rng=rng,
|
|
122
120
|
sobol_engine=sobol_engine,
|
|
123
121
|
)
|