-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Continuous inter-point constraints #345
base: main
Are you sure you want to change the base?
Conversation
Note: This PR is currently on hold for two reasons:
|
a5ed090
to
7cbb490
Compare
7cbb490
to
d5758c5
Compare
d5758c5
to
ecb050e
Compare
as mentioned in our call but posting here for documentation / reference: I think its worth exploring whether a design not via deriving from |
e82a4cc
to
1e2cd40
Compare
1e2cd40
to
004ec49
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not yet completely done with the review, but because time is running, here a first batch of comments
@@ -234,6 +234,8 @@ def _recommend_hybrid( | |||
Returns: | |||
The recommended points. | |||
""" | |||
# TODO Interpoint constraints are not yet enabled in hybrid search spaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you raise an actual NotImplementedError
here? Otherwise, these constraints would just silently be ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General comment: through the PR, the writing is not consistent. Sometimes you write interpoint
and sometimes inter-point
. Let's settle for one and be consistent.
@@ -82,6 +82,10 @@ def __str__(self) -> str: | |||
nonlin_constraints_list = [ | |||
constr.summary() for constr in self.constraints_nonlin | |||
] | |||
nonlin_constraints_list = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplication
@property | ||
def is_constrained(self) -> bool: | ||
"""Return whether the subspace is constrained in any way.""" | ||
return any( | ||
( | ||
self.constraints_lin_eq, | ||
self.constraints_lin_ineq, | ||
self.constraints_nonlin, | ||
) | ||
) | ||
|
||
@property | ||
def has_interpoint_constraints(self) -> bool: | ||
"""Return whether or not the space has any interpoint constraints.""" | ||
return any( | ||
c.is_interpoint for c in self.constraints_lin_eq + self.constraints_lin_ineq | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docstrings of properties should read as if they were an attribute, i.e. no verb like return ...
. Instead, something like Boolean flag indicating ...
@@ -359,7 +381,10 @@ def samples_random(self, n_points: int = 1) -> pd.DataFrame: | |||
) | |||
return self.sample_uniform(n_points) | |||
|
|||
def sample_uniform(self, batch_size: int = 1) -> pd.DataFrame: | |||
def sample_uniform( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's this reformat about? By mistake?
@@ -45,6 +46,15 @@ class ContinuousLinearConstraint(ContinuousConstraint): | |||
rhs: float = field(default=0.0, converter=float, validator=finite_float) | |||
"""Right-hand side value of the in-/equality.""" | |||
|
|||
is_interpoint: bool = field(default=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is_interpoint: bool = field(default=False) | |
is_interpoint: bool = field(default=False, validator=instance_of(bool)) |
@@ -45,6 +46,15 @@ class ContinuousLinearConstraint(ContinuousConstraint): | |||
rhs: float = field(default=0.0, converter=float, validator=finite_float) | |||
"""Right-hand side value of the in-/equality.""" | |||
|
|||
is_interpoint: bool = field(default=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what is more in line with our convention, is_interpoint
or interpoint
🤔 The former is what we typically use for properties, while the latter makes a bit more sense from a constructor perspective. @Scienfitz: opinions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, would use the latter for arguments
An inter-point constraint is a constraint that is defined over full batches. That | ||
is, and inter-point constraint of the form ``param_1 + 2*param_2 <=2`` means that | ||
the sum of ``param2`` plus two times the sum of ``param_2`` across the full batch | ||
must not exceed 2. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An inter-point constraint is a constraint that is defined over full batches. That | |
is, and inter-point constraint of the form ``param_1 + 2*param_2 <=2`` means that | |
the sum of ``param2`` plus two times the sum of ``param_2`` across the full batch | |
must not exceed 2. | |
While intra-point constraints impose conditions on each individual point of a batch, | |
inter-point constraints do so **across** the points of the batch. That is, an | |
inter-point constraint of the form ``x_1 + x_2 <= 1`` enforces that the sum of all | |
``x_1`` values plus the sum of all ``x_2`` values in the batch must not exceed 1. |
@@ -45,6 +46,15 @@ class ContinuousLinearConstraint(ContinuousConstraint): | |||
rhs: float = field(default=0.0, converter=float, validator=finite_float) | |||
"""Right-hand side value of the in-/equality.""" | |||
|
|||
is_interpoint: bool = field(default=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, would use the latter for arguments
self, | ||
parameters: Sequence[NumericalContinuousParameter], | ||
idx_offset: int = 0, | ||
batch_size: int = 1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i wouldnt give this an int default ('.recommend' also does not have a default for batch size) but would make the default 'None'
@@ -18,7 +18,9 @@ | |||
from botorch.test_functions import Rastrigin | |||
|
|||
from baybe import Campaign | |||
from baybe.constraints import ContinuousLinearConstraint | |||
from baybe.constraints import ( | |||
ContinuousLinearConstraint, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strange reformat
@@ -140,3 +142,59 @@ | |||
"2.0*x_2 + 3.0*x_4 <= 1.0 satisfied in all recommendations? ", | |||
(2.0 * measurements["x_2"] + 3.0 * measurements["x_4"]).le(1.0 + TOLERANCE).all(), | |||
) | |||
|
|||
|
|||
### Using inter-point constraints |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interpoint should be its own example file feels just 'drangepappt' for this example
# Since these constraints require information about the batch size, they are not used | ||
# during the creation of the search space but handed over to the `recommend` call. | ||
# This example models the following inter-point constraints and combines them also | ||
# with regular constraints. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that does not appear to be true
rec["Target"] = target_values | ||
inter_campaign.add_measurements(rec) | ||
# Check inter-point constraints | ||
assert rec["x_1"].sum() >= 2.5 - TOLERANCE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prints are more important than asserts in examples (nbut nothing speaks against having both)
for c in [*self.constraints_lin_eq, *self.constraints_lin_ineq]: | ||
if not c.is_interpoint: | ||
param_indices, coefficients, rhs = c.to_botorch( | ||
self.parameters, batch_size=batch_size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.parameters, batch_size=batch_size | |
self.parameters |
return self._sample_from_bounds(batch_size, self.comp_rep_bounds.values) | ||
|
||
if self.has_interpoint_constraints: | ||
return self._sample_from_polytope_with_interpoint_constraints( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it feasible to keep splittitng the sample function up like that? eg what happens if we have interpoint+cardinality constraints
return self._sample_from_bounds(batch_size, self.comp_rep_bounds.values) | ||
|
||
if self.has_interpoint_constraints: | ||
return self._sample_from_polytope_with_interpoint_constraints( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isnt 'sample_from_polytope' a special case of the new function? If so I think it could be better design if one was contained int he other or one calls the other
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the new intperpoint flag should be mentioned in the userguide
This PR introduces a first variant of inter-point constraints by using the
botorch
-provided interface.Here, an inter-point constraint is a constraint that acts on a full batch instead of a single recommendation. If we think of a batch recommendation of a matrix with shape
batches x features
, then our previous constraints would be row-wise, while these constraints allow mixed constraints across both dimensions.This PR introduces new classes, and these classes were modeled similar to the already existing classes for continuous constraints. Some things had to be changed though resp. we need to aware:
get_polytope_samples
is not made for including inter-point constraints, and hence a workaround was implemented. The workaround basically transforms the space in a one-dimensional space withbatches * features
many features, and then defines both normal and inter-point constraints over this space (see [Bug]get_polytope_samples
fails for inequalities overq
-batches and the actual dimension pytorch/botorch#2468). Note that this might interfere with Botorch with cardinality constraint via sampling #301 and that some alignment might be necessary.