Skip to content

Commit

Permalink
Merge 03f7a30 into a1b38fc
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianAment authored Jul 13, 2023
2 parents a1b38fc + 03f7a30 commit 36dc426
Show file tree
Hide file tree
Showing 7 changed files with 520 additions and 64 deletions.
92 changes: 78 additions & 14 deletions botorch/acquisition/input_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
from botorch.acquisition.preference import AnalyticExpectedUtilityOfBestOption
from botorch.acquisition.risk_measures import RiskMeasureMCObjective
from botorch.acquisition.utils import (
compute_best_feasible_objective,
expand_trace_observations,
get_optimal_samples,
project_to_target_fidelity,
Expand Down Expand Up @@ -457,7 +458,9 @@ def construct_inputs_qEI(
X_pending: Optional[Tensor] = None,
sampler: Optional[MCSampler] = None,
best_f: Optional[Union[float, Tensor]] = None,
**kwargs: Any,
constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
eta: Union[Tensor, float] = 1e-3,
**ignored: Any,
) -> Dict[str, Any]:
r"""Construct kwargs for the `qExpectedImprovement` constructor.
Expand All @@ -473,7 +476,15 @@ def construct_inputs_qEI(
sampler: The sampler used to draw base samples. If omitted, uses
the acquisition functions's default sampler.
best_f: Threshold above (or below) which improvement is defined.
kwargs: Not used.
constraints: A list of constraint callables which map a Tensor of posterior
samples of dimension `sample_shape x batch-shape x q x m`-dim to a
`sample_shape x batch-shape x q`-dim Tensor. The associated constraints
are considered satisfied if the output is less than zero.
eta: Temperature parameter(s) governing the smoothness of the sigmoid
approximation to the constraint indicators. For more details, on this
parameter, see the docs of `compute_smoothed_constraint_indicator`.
ignored: Not used.
Returns:
A dict mapping kwarg names of the constructor to values.
"""
Expand All @@ -489,9 +500,11 @@ def construct_inputs_qEI(
training_data=training_data,
objective=objective,
posterior_transform=posterior_transform,
constraints=constraints,
model=model,
)

return {**base_inputs, "best_f": best_f}
return {**base_inputs, "best_f": best_f, "constraints": constraints, "eta": eta}


@acqf_input_constructor(qNoisyExpectedImprovement)
Expand All @@ -505,7 +518,9 @@ def construct_inputs_qNEI(
X_baseline: Optional[Tensor] = None,
prune_baseline: Optional[bool] = True,
cache_root: Optional[bool] = True,
**kwargs: Any,
constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
eta: Union[Tensor, float] = 1e-3,
**ignored: Any,
) -> Dict[str, Any]:
r"""Construct kwargs for the `qNoisyExpectedImprovement` constructor.
Expand All @@ -527,7 +542,14 @@ def construct_inputs_qNEI(
prune_baseline: If True, remove points in `X_baseline` that are
highly unlikely to be the best point. This can significantly
improve performance and is generally recommended.
kwargs: Not used.
constraints: A list of constraint callables which map a Tensor of posterior
samples of dimension `sample_shape x batch-shape x q x m`-dim to a
`sample_shape x batch-shape x q`-dim Tensor. The associated constraints
are considered satisfied if the output is less than zero.
eta: Temperature parameter(s) governing the smoothness of the sigmoid
approximation to the constraint indicators. For more details, on this
parameter, see the docs of `compute_smoothed_constraint_indicator`.
ignored: Not used.
Returns:
A dict mapping kwarg names of the constructor to values.
Expand All @@ -553,6 +575,8 @@ def construct_inputs_qNEI(
"X_baseline": X_baseline,
"prune_baseline": prune_baseline,
"cache_root": cache_root,
"constraints": constraints,
"eta": eta,
}


Expand All @@ -566,7 +590,9 @@ def construct_inputs_qPI(
sampler: Optional[MCSampler] = None,
tau: float = 1e-3,
best_f: Optional[Union[float, Tensor]] = None,
**kwargs: Any,
constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
eta: Union[Tensor, float] = 1e-3,
**ignored: Any,
) -> Dict[str, Any]:
r"""Construct kwargs for the `qProbabilityOfImprovement` constructor.
Expand All @@ -588,13 +614,26 @@ def construct_inputs_qPI(
best_f: The best objective value observed so far (assumed noiseless). Can
be a `batch_shape`-shaped tensor, which in case of a batched model
specifies potentially different values for each element of the batch.
kwargs: Not used.
constraints: A list of constraint callables which map a Tensor of posterior
samples of dimension `sample_shape x batch-shape x q x m`-dim to a
`sample_shape x batch-shape x q`-dim Tensor. The associated constraints
are considered satisfied if the output is less than zero.
eta: Temperature parameter(s) governing the smoothness of the sigmoid
approximation to the constraint indicators. For more details, on this
parameter, see the docs of `compute_smoothed_constraint_indicator`.
ignored: Not used.
Returns:
A dict mapping kwarg names of the constructor to values.
"""
if best_f is None:
best_f = get_best_f_mc(training_data=training_data, objective=objective)

best_f = get_best_f_mc(
training_data=training_data,
objective=objective,
posterior_transform=posterior_transform,
constraints=constraints,
model=model,
)
base_inputs = _construct_inputs_mc_base(
model=model,
objective=objective,
Expand All @@ -603,7 +642,13 @@ def construct_inputs_qPI(
X_pending=X_pending,
)

return {**base_inputs, "tau": tau, "best_f": best_f}
return {
**base_inputs,
"tau": tau,
"best_f": best_f,
"constraints": constraints,
"eta": eta,
}


@acqf_input_constructor(qUpperConfidenceBound)
Expand All @@ -615,7 +660,7 @@ def construct_inputs_qUCB(
X_pending: Optional[Tensor] = None,
sampler: Optional[MCSampler] = None,
beta: float = 0.2,
**kwargs: Any,
**ignored: Any,
) -> Dict[str, Any]:
r"""Construct kwargs for the `qUpperConfidenceBound` constructor.
Expand All @@ -631,7 +676,7 @@ def construct_inputs_qUCB(
sampler: The sampler used to draw base samples. If omitted, uses
the acquisition functions's default sampler.
beta: Controls tradeoff between mean and standard deviation in UCB.
kwargs: Not used.
ignored: Not used.
Returns:
A dict mapping kwarg names of the constructor to values.
Expand Down Expand Up @@ -1083,18 +1128,28 @@ def get_best_f_mc(
training_data: MaybeDict[SupervisedDataset],
objective: Optional[MCAcquisitionObjective] = None,
posterior_transform: Optional[PosteriorTransform] = None,
constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
model: Optional[Model] = None,
) -> Tensor:
if isinstance(training_data, dict) and not _field_is_shared(
training_data, fieldname="X"
):
raise NotImplementedError("Currently only block designs are supported.")

X_baseline = _get_dataset_field(
training_data,
fieldname="X",
transform=lambda field: field(),
assert_shared=True,
first_only=True,
)

Y = _get_dataset_field(
training_data,
fieldname="Y",
transform=lambda field: field(),
join_rule=lambda field_tensors: torch.cat(field_tensors, dim=-1),
)
) # batch_shape x n x d

if posterior_transform is not None:
# retain the original tensor dimension since objective expects explicit
Expand All @@ -1111,7 +1166,16 @@ def get_best_f_mc(
"acquisition functions)."
)
objective = IdentityMCObjective()
return objective(Y).max(-1).values
obj = objective(Y, X=X_baseline) # batch_shape x n
return compute_best_feasible_objective(
samples=Y,
obj=obj,
constraints=constraints,
model=model,
objective=objective,
posterior_transform=posterior_transform,
X_baseline=X_baseline,
)


def optimize_objective(
Expand Down
50 changes: 15 additions & 35 deletions botorch/acquisition/monte_carlo.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from typing import Any, Callable, List, Optional, Protocol, Tuple, Union

import torch
from botorch import acquisition
from botorch.acquisition.acquisition import AcquisitionFunction, MCSamplerMixin
from botorch.acquisition.cached_cholesky import CachedCholeskyMCAcquisitionFunction
from botorch.acquisition.objective import (
Expand All @@ -36,7 +35,10 @@
MCAcquisitionObjective,
PosteriorTransform,
)
from botorch.acquisition.utils import prune_inferior_points
from botorch.acquisition.utils import (
compute_best_feasible_objective,
prune_inferior_points,
)
from botorch.exceptions.errors import UnsupportedError
from botorch.models.model import Model
from botorch.sampling.base import MCSampler
Expand Down Expand Up @@ -591,46 +593,24 @@ def _get_samples_and_objectives(self, X: Tensor) -> Tuple[Tensor, Tensor]:
return samples, obj

def _compute_best_feasible_objective(self, samples: Tensor, obj: Tensor) -> Tensor:
"""
r"""Computes best feasible objective value from samples.
Args:
samples: `sample_shape x batch_shape x q x m`-dim posterior samples.
obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values.
Returns:
A `sample_shape x batch_shape x 1`-dim Tensor of best feasible objectives.
"""
if self._constraints is not None:
# is_feasible is sample_shape x batch_shape x q
is_feasible = compute_smoothed_constraint_indicator(
constraints=self._constraints, samples=samples, eta=self._eta
)
is_feasible = is_feasible > 0.5 # due to smooth approximation
if is_feasible.any():
obj = torch.where(is_feasible, obj, -torch.inf)
else: # if there are no feasible observations, estimate a lower
# bound on the objective by sampling convex combinations of X_baseline.
convex_weights = torch.rand(
32,
self.X_baseline.shape[-2],
dtype=self.X_baseline.dtype,
device=self.X_baseline.device,
)
weights_sum = convex_weights.sum(dim=0, keepdim=True)
convex_weights = convex_weights / weights_sum
# infeasible cost M is such that -M < min_x f(x), thus
# 0 < min_x f(x) - (-M), so we should take -M as a lower
# bound on the best feasible objective
return -acquisition.utils.get_infeasible_cost(
X=convex_weights @ self.X_baseline,
model=self.model,
objective=self.objective,
posterior_transform=self.posterior_transform,
).expand(*obj.shape[:-1], 1)

# we don't need to differentiate through X_baseline for now, so taking
# the regular max over the n points to get best_f is fine
with torch.no_grad():
return obj.amax(dim=-1, keepdim=True)
return compute_best_feasible_objective(
samples=samples,
obj=obj,
constraints=self._constraints,
model=self.model,
objective=self.objective,
posterior_transform=self.posterior_transform,
X_baseline=self.X_baseline,
)


class qProbabilityOfImprovement(SampleReducingMCAcquisitionFunction):
Expand Down
Loading

0 comments on commit 36dc426

Please sign in to comment.