Skip to content

Commit ec45123

Browse files
authored
Broadcast scalar constraint bounds before constructing surrogate models (#628)
1 parent 2e1701a commit ec45123

2 files changed

Lines changed: 33 additions & 2 deletions

File tree

‎bayes_opt/constraint.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ class ConstraintModel:
3737
lb : float or np.ndarray
3838
The lower bound on the constraints. Should have the same
3939
dimensionality as the return value of the constraint function.
40+
A scalar is broadcast to match the upper bound.
4041
4142
ub : float or np.ndarray
4243
The upper bound on the constraints. Should have the same
4344
dimensionality as the return value of the constraint function.
45+
A scalar is broadcast to match the lower bound.
4446
4547
random_state : np.random.RandomState or int or None, default=None
4648
Random state to use.
@@ -62,8 +64,7 @@ def __init__(
6264
) -> None:
6365
self.fun = fun
6466

65-
self._lb = np.atleast_1d(lb)
66-
self._ub = np.atleast_1d(ub)
67+
self._lb, self._ub = np.broadcast_arrays(np.atleast_1d(lb), np.atleast_1d(ub))
6768

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

‎tests/test_constraint.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,36 @@ def constraint_function_2_dim(x, y):
125125
)
126126

127127

128+
@pytest.mark.parametrize(
129+
("lb", "ub"),
130+
[
131+
(-np.inf, np.array([0.5, 0.25])),
132+
(np.array([-0.5, -0.25]), np.inf),
133+
(-0.5, np.array([0.5, 0.25])),
134+
(np.array([-0.5, -0.25]), 0.5),
135+
(np.array([-np.inf, -np.inf]), np.array([0.5, 0.25])),
136+
(np.array([-0.5, -0.25]), np.array([np.inf, np.inf])),
137+
],
138+
)
139+
def test_multiple_constraints_broadcast_bounds(lb, ub):
140+
constraint = NonlinearConstraint(lambda x: np.array([x, -x]), lb, ub)
141+
optimizer = BayesianOptimization(
142+
f=lambda x: -(x**2), constraint=constraint, pbounds={"x": (-1, 1)}, verbose=0, random_state=1
143+
)
144+
145+
for x in [-0.75, 0.0, 0.75]:
146+
optimizer.probe({"x": x}, lazy=False)
147+
148+
np.testing.assert_array_equal(
149+
optimizer.space.constraint_values, [[-0.75, 0.75], [0.0, 0.0], [0.75, -0.75]]
150+
)
151+
assert [result["allowed"] for result in optimizer.res] == [False, True, False]
152+
assert optimizer.max["params"] == {"x": 0.0}
153+
assert -1 <= optimizer.suggest()["x"] <= 1
154+
probabilities = optimizer.constraint.predict(np.array([[-0.75], [0.0], [0.75]]))
155+
np.testing.assert_allclose(probabilities, [0.0, 1.0, 0.0], atol=1e-6)
156+
157+
128158
def test_kwargs_not_the_same(target_function):
129159
def target_function(x, y):
130160
return np.cos(2 * x) * np.cos(y) + np.sin(x)

0 commit comments

Comments
 (0)