-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathsolver.py
More file actions
120 lines (101 loc) · 4.22 KB
/
solver.py
File metadata and controls
120 lines (101 loc) · 4.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""
This has to be run inside the poli__boss environment.
"""
from __future__ import annotations
from pathlib import Path
from typing import Literal
import numpy as np
import torch
try:
from boss.code.emukit_models.emukit_ssk_model import SSK_model
from boss.code.parameters.candidate_parameter import CandidateStringParameter
from emukit.core import ParameterSpace
from emukit.core.loop import FixedIterationsStoppingCondition
from emukit.core.optimization import RandomSearchAcquisitionOptimizer
from emukit.bayesian_optimization.loops import BayesianOptimizationLoop
from emukit.bayesian_optimization.acquisitions import ExpectedImprovement
from emukit.core.initial_designs import RandomDesign
except ImportError as e:
raise ImportError(
"You are trying to use the BOSS solver. Install "
"the relevant optional dependencies with [boss]. \n"
"You can do this by running: \n"
"pip install 'poli-baselines[boss] @ git+https://github.com/MachineLearningLifeScience/poli-baselines.git'"
) from e
from poli.core.abstract_black_box import AbstractBlackBox
from poli.core.util.seeding import seed_python_numpy_and_torch
from poli_baselines.core.abstract_solver import AbstractSolver
ROOT_DIR = Path(__file__).parent.parent.parent.parent.parent.parent.resolve()
class BossSolver(AbstractSolver):
def __init__(
self,
black_box: AbstractBlackBox,
x0: np.ndarray = None,
y0: np.ndarray = None,
device: str | None = None,
dtype: Literal["float32", "float64"] = "float32",
batch_size: int = 1,
n_initial_points: int | None = None,
number_new_bins_on_split: int = 2,
results_dir: Path | None = None,
):
super().__init__(black_box, None, None)
if device is None:
device = "cuda" if torch.cuda.is_available() else "cpu"
self.x0 = x0
self.y0 = y0
self.device = device
self.dtype = dtype
self.batch_size = batch_size
self.number_new_bins_on_split = number_new_bins_on_split
self.n_initial_points = n_initial_points
self.objective = lambda x: -self.black_box(x) # BOSS minimizes
# see SMILES examples
token_space = np.array([" ".join(list(ss)) for ss in self.x0]).reshape(-1, 1)
self.search_space = ParameterSpace(
[CandidateStringParameter("string", token_space)]
) # x0 goes here with correct wrapper
self.model = SSK_model(
self.search_space, self.x0, self.y0, max_subsequence_length=5, n_restarts=1
)
self.acquisition = ExpectedImprovement(self.model)
self.optimizer = RandomSearchAcquisitionOptimizer(self.search_space, 100)
self.bo_loop_ssk = BayesianOptimizationLoop(
model=self.model,
space=self.search_space,
acquisition=self.acquisition,
acquisition_optimizer=self.optimizer,
)
# Creating the results dir for boss
if results_dir is None:
results_dir = ROOT_DIR / "boss_results"
Path(results_dir).mkdir(parents=True, exist_ok=True)
# Creating a gitignore file inside that dir
with open(results_dir / ".gitignore", "w") as fp:
fp.write("*\n!.gitignore")
def solve(
self,
max_iter: int = 100,
n_initial_points: int | None = None,
seed: int | None = None,
) -> None:
if seed is not None:
seed_python_numpy_and_torch(seed)
if n_initial_points is None:
if self.n_initial_points is None:
raise ValueError(
"n_initial_points must be set, either in init or in solve"
)
n_initial_points = self.n_initial_points
stopping_condition = FixedIterationsStoppingCondition(i_max=max_iter)
self.boss = BossSolver(
black_box=self.black_box,
x0=self.x0,
y0=self.y0,
n_initial_points=n_initial_points,
batch_size=self.batch_size,
results_dir=ROOT_DIR / "data" / "boss_results",
device=self.device,
dtype=self.dtype,
)
self.boss.bo_loop_ssk.run_loop(self.objective, stopping_condition)