fmfn / BayesianOptimization

@@ -12,12 +12,16 @@
Loading
12 12
13 13
    Parameters
14 14
    ----------
15 -
    func: function
15 +
    fun: function
16 16
        Constraint function. If multiple constraints are handled, this should
17 17
        return a numpy.ndarray of appropriate size.
18 18
19 -
    limits: numeric or numpy.ndarray
20 -
        Upper limit(s) for the constraints. The return value of `func` should
19 +
    lb: numeric or numpy.ndarray
20 +
        Upper limit(s) for the constraints. The return value of `fun` should
21 +
        have exactly this shape.
22 +
23 +
    ub: numeric or numpy.ndarray
24 +
        Upper limit(s) for the constraints. The return value of `fun` should
21 25
        have exactly this shape.
22 26
23 27
    random_state: int or numpy.random.RandomState, optional(default=None)
@@ -33,14 +37,20 @@
Loading
33 37
    is a simply the product of the individual probabilities.
34 38
    """
35 39
36 -
    def __init__(self, func, limits, random_state=None):
37 -
        self.func = func
40 +
    def __init__(self, fun, lb, ub, random_state=None):
41 +
        self.fun = fun
38 42
39 -
        if isinstance(limits, float):
40 -
            self._limits = np.array([limits])
43 +
        if isinstance(lb, float):
44 +
            self._lb = np.array([lb])
41 45
        else:
42 -
            self._limits = limits
43 -
46 +
            self._lb = lb
47 +
        
48 +
        if isinstance(ub, float):
49 +
            self._ub = np.array([ub])
50 +
        else:
51 +
            self._ub = ub
52 +
        
53 +
        
44 54
        basis = lambda: GaussianProcessRegressor(
45 55
            kernel=Matern(nu=2.5),
46 56
            alpha=1e-6,
@@ -48,18 +58,26 @@
Loading
48 58
            n_restarts_optimizer=5,
49 59
            random_state=random_state,
50 60
        )
51 -
        self._model = [basis() for _ in range(len(self._limits))]
61 +
        self._model = [basis() for _ in range(len(self._lb))]
52 62
53 63
    @property
54 -
    def limits(self):
55 -
        return self._limits
64 +
    def lb(self):
65 +
        return self._lb
66 +
67 +
    @property
68 +
    def ub(self):
69 +
        return self._ub
70 +
    
71 +
    @property
72 +
    def model(self):
73 +
        return self._model
56 74
57 75
    def eval(self, **kwargs):
58 76
        """
59 77
        Evaluates the constraint function.
60 78
        """
61 79
        try:
62 -
            return self.func(**kwargs)
80 +
            return self.fun(**kwargs)
63 81
        except TypeError as e:
64 82
            msg = (
65 83
                "Encountered TypeError when evaluating constraint " +
@@ -67,7 +85,8 @@
Loading
67 85
                "doesn't use the same keyword arguments as the target " +
68 86
                f"function. Original error message:\n\n{e}"
69 87
                )
70 -
            raise TypeError(msg)
88 +
            e.args = (msg,)
89 +
            raise
71 90
72 91
    def fit(self, X, Y):
73 92
        """
@@ -92,14 +111,22 @@
Loading
92 111
        X = X.reshape((-1, self._model[0].n_features_in_))
93 112
        if len(self._model) == 1:
94 113
            y_mean, y_std = self._model[0].predict(X, return_std=True)
95 -
            result = norm(loc=y_mean, scale=y_std).cdf(self._limits[0])
114 +
115 +
            p_lower = (norm(loc=y_mean, scale=y_std).cdf(self._lb[0])
116 +
                            if self._lb[0] != -np.inf else np.array([0]))
117 +
            p_upper = (norm(loc=y_mean, scale=y_std).cdf(self._ub[0])
118 +
                            if self._lb[0] != np.inf else np.array([1]))
119 +
            result = p_upper - p_lower
96 120
            return result.reshape(X_shape[:-1])
97 121
        else:
98 122
            result = np.ones(X.shape[0])
99 123
            for j, gp in enumerate(self._model):
100 124
                y_mean, y_std = gp.predict(X, return_std=True)
101 -
                result = result * norm(loc=y_mean, scale=y_std).cdf(
102 -
                    self._limits[j])
125 +
                p_lower = (norm(loc=y_mean, scale=y_std).cdf(self._lb[j])
126 +
                           if self._lb[j] != -np.inf else np.array([0]))
127 +
                p_upper = (norm(loc=y_mean, scale=y_std).cdf(self._ub[j])
128 +
                           if self._lb[j] != np.inf else np.array([1]))
129 +
                result = result * (p_upper - p_lower)
103 130
            return result.reshape(X_shape[:-1])
104 131
105 132
    def approx(self, X):
@@ -113,13 +140,15 @@
Loading
113 140
            return self._model[0].predict(X).reshape(X_shape[:-1])
114 141
        else:
115 142
            result = np.column_stack([gp.predict(X) for gp in self._model])
116 -
            return result.reshape(X_shape[:-1] + (len(self._limits), ))
143 +
            return result.reshape(X_shape[:-1] + (len(self._lb), ))
117 144
118 145
    def allowed(self, constraint_values):
119 146
        """
120 147
        Checks whether `constraint_values` are below the specified limits.
121 148
        """
122 -
        if self._limits.size == 1:
123 -
            return np.less_equal(constraint_values, self._limits)
149 +
        if self._lb.size == 1:
150 +
            return (np.less_equal(self._lb, constraint_values)
151 +
                    & np.less_equal(constraint_values, self._ub))
124 152
125 -
        return np.all(constraint_values <= self._limits, axis=-1)
153 +
        return (np.all(constraint_values <= self._ub, axis=-1)
154 +
                    & np.all(constraint_values >= self._lb, axis=-1))

@@ -1,6 +1,8 @@
Loading
1 1
import warnings
2 2
from queue import Queue, Empty
3 3
4 +
from bayes_opt.constraint import ConstraintModel
5 +
4 6
from .target_space import TargetSpace, ConstrainedTargetSpace
5 7
from .event import Events, DEFAULT_EVENTS
6 8
from .logger import _get_default_logger
@@ -106,10 +108,21 @@
Loading
106 108
            # bounds of its domain, and a record of the evaluations we have
107 109
            # done so far
108 110
            self._space = TargetSpace(f, pbounds, random_state)
111 +
            self.is_constrained = False
109 112
        else:
110 -
            self._space = ConstrainedTargetSpace(f, constraint, pbounds,
111 -
                                                 random_state)
112 -
        self.constraint = constraint
113 +
            constraint_ = ConstraintModel(
114 +
                constraint.fun,
115 +
                constraint.lb,
116 +
                constraint.ub,
117 +
                random_state=random_state
118 +
            )
119 +
            self._space = ConstrainedTargetSpace(
120 +
                f,
121 +
                constraint_,
122 +
                pbounds,
123 +
                random_state
124 +
            )
125 +
            self.is_constrained = True
113 126
114 127
        self._verbose = verbose
115 128
        self._bounds_transformer = bounds_transformer
@@ -126,6 +139,12 @@
Loading
126 139
    def space(self):
127 140
        return self._space
128 141
142 +
    @property
143 +
    def constraint(self):
144 +
        if self.is_constrained:
145 +
            return self._space.constraint
146 +
        return None
147 +
129 148
    @property
130 149
    def max(self):
131 150
        return self._space.max()
@@ -169,7 +188,7 @@
Loading
169 188
        with warnings.catch_warnings():
170 189
            warnings.simplefilter("ignore")
171 190
            self._gp.fit(self._space.params, self._space.target)
172 -
            if self.constraint is not None:
191 +
            if self.is_constrained:
173 192
                self.constraint.fit(self._space.params,
174 193
                                    self._space._constraint_values)
175 194

@@ -267,10 +267,14 @@
Loading
267 267
        self._constraint = constraint
268 268
269 269
        # preallocated memory for constraint fulfillement
270 -
        if constraint.limits.size == 1:
270 +
        if constraint.lb.size == 1:
271 271
            self._constraint_values = np.empty(shape=(0), dtype=float)
272 272
        else:
273 -
            self._constraint_values = np.empty(shape=(0, constraint.limits.size), dtype=float)
273 +
            self._constraint_values = np.empty(shape=(0, constraint.lb.size), dtype=float)
274 +
275 +
    @property
276 +
    def constraint(self):
277 +
        return self._constraint
274 278
275 279
    @property
276 280
    def constraint_values(self):
Files Coverage
bayes_opt 99.25%
Project Totals (8 files) 99.25%
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading