Skip to content

Commit 10b2e89

Browse files
authored
Adds bounds to CMA-ES and TuRBO (#33)
* Adds bounds to CMA-ES * Brings Simon's changes to TuRBO * Adds default bounds to TuRBO * Updates the TuRBO to be a step-by-step solver
1 parent 0d1b030 commit 10b2e89

2 files changed

Lines changed: 50 additions & 11 deletions

File tree

src/poli_baselines/solvers/bayesian_optimization/turbo/turbo_wrapper.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Code taken from https://botorch.org/tutorials/turbo_1
2+
from __future__ import annotations
3+
24
import numpy as np
35
from poli.core.abstract_black_box import AbstractBlackBox
6+
from typing import List
47

58
from poli_baselines.core.step_by_step_solver import StepByStepSolver
69
import math
@@ -29,9 +32,42 @@
2932

3033

3134
class TurboWrapper(StepByStepSolver):
32-
def __init__(self, black_box: AbstractBlackBox, x0: np.ndarray, y0: np.ndarray):
35+
def __init__(
36+
self,
37+
black_box: AbstractBlackBox,
38+
x0: np.ndarray,
39+
y0: np.ndarray,
40+
bounds: np.ndarray | None = None,
41+
):
42+
"""
43+
44+
Parameters
45+
----------
46+
black_box
47+
x0
48+
y0
49+
bounds:
50+
array of shape Dx2 where D is the dimensionality
51+
The first row contains the lower bounds on x, the last row contains the upper bounds.
52+
"""
3353
super().__init__(black_box, x0, y0)
34-
self.X_turbo = torch.tensor(x0)
54+
assert x0.shape[0] > 1
55+
56+
if bounds is None:
57+
bounds = np.array([[x0.min() - 1.0, x0.max() + 1.0]] * x0.shape[1])
58+
59+
assert bounds.shape[1] == 2
60+
assert bounds.shape[0] == x0.shape[1]
61+
assert np.all(bounds[:, 1] >= bounds[:, 0])
62+
bounds[:, 1] -= bounds[:, 0]
63+
64+
def make_transforms():
65+
to_turbo = lambda X: (X - bounds[:, 0]) / bounds[:, 1]
66+
from_turbo = lambda X: X * bounds[:, 1] + bounds[:, 0]
67+
return to_turbo, from_turbo
68+
69+
self.to_turbo, self.from_turbo = make_transforms()
70+
self.X_turbo = torch.tensor(self.to_turbo(x0))
3571
self.Y_turbo = torch.tensor(y0)
3672
self.batch_size = 1
3773
dim = x0.shape[1]
@@ -70,14 +106,14 @@ def next_candidate(self) -> np.ndarray:
70106
raw_samples=RAW_SAMPLES,
71107
acqf="ts",
72108
)
73-
return X_next
109+
return self.from_turbo(X_next.numpy())
74110

75111
def post_update(self, x: np.ndarray, y: np.ndarray) -> None:
76112
"""
77113
This method is called after the history is updated.
78114
"""
79115
Y_next = torch.tensor(y)
80-
X_next = torch.tensor(x)
116+
X_next = torch.tensor(self.to_turbo(x))
81117

82118
# Update state
83119
self.state = update_state(state=self.state, Y_next=Y_next)

src/poli_baselines/solvers/evolutionary_strategies/cma_es.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1818
"""
1919

20+
from __future__ import annotations
2021
from typing import Tuple
2122
import numpy as np
2223
from poli.core.abstract_black_box import AbstractBlackBox
@@ -71,6 +72,7 @@ def __init__(
7172
initial_mean: np.ndarray,
7273
initial_sigma: float = 1.0,
7374
population_size: int = 10,
75+
bounds: tuple[float, float] | None = None,
7476
):
7577
"""
7678
Initialize the CMA-ES solver.
@@ -93,13 +95,14 @@ def __init__(
9395
self.initial_sigma = initial_sigma
9496
self.population_size = population_size
9597

96-
self._internal_cma = cma.CMAEvolutionStrategy(
97-
initial_mean,
98-
initial_sigma,
99-
{
100-
"popsize": population_size,
101-
},
102-
)
98+
opts = {
99+
"popsize": population_size,
100+
}
101+
102+
if bounds is not None:
103+
opts["bounds"] = bounds
104+
105+
self._internal_cma = cma.CMAEvolutionStrategy(initial_mean, initial_sigma, opts)
103106

104107
# Include the x0 and y0
105108
_ = self._internal_cma.ask()

0 commit comments

Comments
 (0)