Skip to content
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ to learn how to make the package more flexible, how to deal with categorical par
- Check out this
[notebook](https://github.com/fmfn/BayesianOptimization/blob/master/examples/visualization.ipynb)
with a step by step visualization of how this method works.
- To understand how to use bayesian optimization when additional constraints are present, see the
[constrained optimization notebook](https://github.com/fmfn/BayesianOptimization/blob/master/examples/constraints.ipynb).
- Explore this [notebook](https://github.com/fmfn/BayesianOptimization/blob/master/examples/exploitation_vs_exploration.ipynb)
exemplifying the balance between exploration and exploitation and how to
control it.
Expand Down
16 changes: 8 additions & 8 deletions bayes_opt/bayesian_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from bayes_opt.constraint import ConstraintModel

from .target_space import TargetSpace, ConstrainedTargetSpace
from .target_space import TargetSpace
from .event import Events, DEFAULT_EVENTS
from .logger import _get_default_logger
from .util import UtilityFunction, acq_max, ensure_rng
Expand Down Expand Up @@ -129,7 +129,7 @@ def __init__(self,
# Data structure containing the function to be optimized, the
# bounds of its domain, and a record of the evaluations we have
# done so far
self._space = TargetSpace(f, pbounds, random_state)
self._space = TargetSpace(f, pbounds, random_state=random_state)
self.is_constrained = False
else:
constraint_ = ConstraintModel(
Expand All @@ -138,11 +138,11 @@ def __init__(self,
constraint.ub,
random_state=random_state
)
self._space = ConstrainedTargetSpace(
self._space = TargetSpace(
f,
constraint_,
pbounds,
random_state
constraint=constraint_,
random_state=random_state
)
self.is_constrained = True

Expand Down Expand Up @@ -175,9 +175,9 @@ def max(self):
def res(self):
return self._space.res()

def register(self, params, target):
def register(self, params, target, constraint_value=None):
"""Expect observation with known target"""
self._space.register(params, target)
self._space.register(params, target, constraint_value)
self.dispatch(Events.OPTIMIZATION_STEP)

def probe(self, params, lazy=True):
Expand Down Expand Up @@ -234,7 +234,7 @@ def _prime_queue(self, init_points):

def _prime_subscriptions(self):
if not any([len(subs) for subs in self._events.values()]):
_logger = _get_default_logger(self._verbose)
_logger = _get_default_logger(self._verbose, self.is_constrained)
self.subscribe(Events.OPTIMIZATION_START, _logger)
self.subscribe(Events.OPTIMIZATION_STEP, _logger)
self.subscribe(Events.OPTIMIZATION_END, _logger)
Expand Down
18 changes: 7 additions & 11 deletions bayes_opt/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,13 @@ class ConstraintModel():
def __init__(self, fun, lb, ub, random_state=None):
self.fun = fun

if isinstance(lb, float):
self._lb = np.array([lb])
else:
self._lb = lb

if isinstance(ub, float):
self._ub = np.array([ub])
else:
self._ub = ub


self._lb = np.atleast_1d(lb)
self._ub = np.atleast_1d(ub)

if np.any(self._lb >= self._ub):
msg = "Lower bounds must be less than upper bounds."
raise ValueError(msg)

basis = lambda: GaussianProcessRegressor(
kernel=Matern(nu=2.5),
alpha=1e-6,
Expand Down
32 changes: 29 additions & 3 deletions bayes_opt/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
from .event import Events
from .util import Colours

def _get_default_logger(verbose):
return ScreenLogger(verbose=verbose)
def _get_default_logger(verbose, is_constrained):
return ScreenLogger(verbose=verbose, is_constrained=is_constrained)


class ScreenLogger(_Tracker):
_default_cell_size = 9
_default_precision = 4

def __init__(self, verbose=2):
def __init__(self, verbose=2, is_constrained=False):
self._verbose = verbose
self._is_constrained = is_constrained
self._header_length = None
super(ScreenLogger, self).__init__()

Expand All @@ -27,6 +28,10 @@ def verbose(self):
def verbose(self, v):
self._verbose = v

@property
def is_constrained(self):
return self._is_constrained

def _format_number(self, x):
if isinstance(x, int):
s = "{x:<{s}}".format(
Expand All @@ -47,6 +52,20 @@ def _format_number(self, x):
return s[:self._default_cell_size - 3] + "..."
return s

def _format_bool(self, x):
if 5 > self._default_cell_size:
if x == True:
x_ = 'T'
elif x == False:
x_ = 'F'
else:
x_ = str(x)
s = "{x:<{s}}".format(
x=x_,
s=self._default_cell_size,
)
return s

def _format_key(self, key):
s = "{key:^{s}}".format(
key=key,
Expand All @@ -62,6 +81,9 @@ def _step(self, instance, colour=Colours.black):

cells.append(self._format_number(self._iterations + 1))
cells.append(self._format_number(res["target"]))
if self._is_constrained:
cells.append(self._format_bool(res["allowed"]))


for key in instance.space.keys:
cells.append(self._format_number(res["params"][key]))
Expand All @@ -72,6 +94,10 @@ def _header(self, instance):
cells = []
cells.append(self._format_key("iter"))
cells.append(self._format_key("target"))

if self._is_constrained:
cells.append(self._format_key("allowed"))

for key in instance.space.keys:
cells.append(self._format_key(key))

Expand Down
Loading