Passed
Push — master ( d39371...69bf6f )
by Simon
03:38
created

BayesianOptimizer.iterate()   A

Complexity

Conditions 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 11
nop 1
dl 0
loc 17
rs 9.85
c 0
b 0
f 0
1
# Author: Simon Blanke
2
# Email: [email protected]
3
# License: MIT License
4
5
6
import numpy as np
7
from scipy.stats import norm
8
from scipy.spatial.distance import cdist
9
10
from .sbom import SBOM
11
12
from .surrogate_models import (
13
    GPR_linear,
14
    GPR,
15
)
16
17
gaussian_process = {"gp_nonlinear": GPR(), "gp_linear": GPR_linear()}
18
19
20
class BayesianOptimizer(SBOM):
21
    def __init__(
22
        self, search_space, xi=0.01, gpr=gaussian_process["gp_nonlinear"], **kwargs,
23
    ):
24
        super().__init__(search_space, **kwargs)
25
        self.xi = xi
26
        self.regr = gpr
27
        self.new_positions = []
28
29
    def _expected_improvement(self):
30
        all_pos_comb_sampled = self.get_random_sample()
31
32
        mu, sigma = self.regr.predict(all_pos_comb_sampled, return_std=True)
33
        mu_sample = self.regr.predict(self.X_sample)
34
35
        mu = mu.reshape(-1, 1)
36
        sigma = sigma.reshape(-1, 1)
37
        mu_sample = mu_sample.reshape(-1, 1)
38
39
        mu_sample_opt = np.max(mu_sample)
40
        imp = mu - mu_sample_opt - self.xi
41
42
        Z = np.divide(imp, sigma, out=np.zeros_like(sigma), where=sigma != 0)
43
        exp_imp = imp * norm.cdf(Z) + sigma * norm.pdf(Z)
44
        exp_imp[sigma == 0.0] = 0.0
45
46
        return exp_imp
47
48
    def _propose_location(self):
49
        self.regr.fit(self.X_sample, self.Y_sample)
50
51
        exp_imp = self._expected_improvement()
52
        exp_imp = exp_imp[:, 0]
53
54
        index_best = list(exp_imp.argsort()[::-1])
55
        all_pos_comb_sorted = self.all_pos_comb[index_best]
56
57
        pos_best = [all_pos_comb_sorted[0]]
58
59
        while len(pos_best) < self.skip_retrain(len(self.pos_new)):
60
            if all_pos_comb_sorted.shape[0] == 0:
61
                break
62
63
            dists = cdist(all_pos_comb_sorted, [pos_best[-1]], metric="cityblock")
64
            dists_norm = dists / dists.max()
65
            bool = np.squeeze(dists_norm > 0.25)
66
            all_pos_comb_sorted = all_pos_comb_sorted[bool]
67
68
            if len(all_pos_comb_sorted) > 0:
69
                pos_best.append(all_pos_comb_sorted[0])
70
71
        return pos_best
72
73
    def iterate(self):
74
        if len(self.pos_new) < self.start_up_evals:
75
            pos = self.move_random()
76
        else:
77
            if len(self.new_positions) == 0:
78
                self.new_positions = self._propose_location()
79
80
            pos = self.new_positions[0]
81
            self.pos_new = pos
82
83
            self.new_positions.pop(0)
84
85
        self.X_sample.append(pos)
86
87
        self.pos = pos
88
89
        return pos
90
91
    def evaluate(self, score_new):
92
        self.score_new = score_new
93
94
        self._evaluate_new2current(score_new)
95
        self._evaluate_current2best()
96
97
        self.Y_sample.append(score_new)
98