Skip to content

Commit 81937f5

Browse files
Merge 8fdcaaa into dfd2a9b
2 parents dfd2a9b + 8fdcaaa commit 81937f5

File tree

8 files changed

+904
-128
lines changed

8 files changed

+904
-128
lines changed

botorch/acquisition/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from botorch.acquisition.logei import (
3838
LogImprovementMCAcquisitionFunction,
3939
qLogExpectedImprovement,
40+
qLogNoisyExpectedImprovement,
4041
)
4142
from botorch.acquisition.max_value_entropy_search import (
4243
MaxValueBase,
@@ -96,6 +97,7 @@
9697
"qExpectedImprovement",
9798
"LogImprovementMCAcquisitionFunction",
9899
"qLogExpectedImprovement",
100+
"qLogNoisyExpectedImprovement",
99101
"qKnowledgeGradient",
100102
"MaxValueBase",
101103
"qMultiFidelityKnowledgeGradient",

botorch/acquisition/input_constructors.py

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@
4747
qKnowledgeGradient,
4848
qMultiFidelityKnowledgeGradient,
4949
)
50-
from botorch.acquisition.logei import qLogExpectedImprovement
50+
from botorch.acquisition.logei import (
51+
qLogExpectedImprovement,
52+
qLogNoisyExpectedImprovement,
53+
TAU_MAX,
54+
TAU_RELU,
55+
)
5156
from botorch.acquisition.max_value_entropy_search import (
5257
qMaxValueEntropy,
5358
qMultiFidelityMaxValueEntropy,
@@ -450,7 +455,7 @@ def construct_inputs_qSimpleRegret(
450455
)
451456

452457

453-
@acqf_input_constructor(qExpectedImprovement, qLogExpectedImprovement)
458+
@acqf_input_constructor(qExpectedImprovement)
454459
def construct_inputs_qEI(
455460
model: Model,
456461
training_data: MaybeDict[SupervisedDataset],
@@ -508,6 +513,72 @@ def construct_inputs_qEI(
508513
return {**base_inputs, "best_f": best_f, "constraints": constraints, "eta": eta}
509514

510515

516+
@acqf_input_constructor(qLogExpectedImprovement)
517+
def construct_inputs_qLogEI(
518+
model: Model,
519+
training_data: MaybeDict[SupervisedDataset],
520+
objective: Optional[MCAcquisitionObjective] = None,
521+
posterior_transform: Optional[PosteriorTransform] = None,
522+
X_pending: Optional[Tensor] = None,
523+
sampler: Optional[MCSampler] = None,
524+
best_f: Optional[Union[float, Tensor]] = None,
525+
constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
526+
eta: Union[Tensor, float] = 1e-3,
527+
fat: bool = True,
528+
tau_max: float = TAU_MAX,
529+
tau_relu: float = TAU_RELU,
530+
**ignored: Any,
531+
) -> Dict[str, Any]:
532+
r"""Construct kwargs for the `qExpectedImprovement` constructor.
533+
534+
Args:
535+
model: The model to be used in the acquisition function.
536+
training_data: Dataset(s) used to train the model.
537+
objective: The objective to be used in the acquisition function.
538+
posterior_transform: The posterior transform to be used in the
539+
acquisition function.
540+
X_pending: A `m x d`-dim Tensor of `m` design points that have been
541+
submitted for function evaluation but have not yet been evaluated.
542+
Concatenated into X upon forward call.
543+
sampler: The sampler used to draw base samples. If omitted, uses
544+
the acquisition functions's default sampler.
545+
best_f: Threshold above (or below) which improvement is defined.
546+
constraints: A list of constraint callables which map a Tensor of posterior
547+
samples of dimension `sample_shape x batch-shape x q x m`-dim to a
548+
`sample_shape x batch-shape x q`-dim Tensor. The associated constraints
549+
are considered satisfied if the output is less than zero.
550+
eta: Temperature parameter(s) governing the smoothness of the sigmoid
551+
approximation to the constraint indicators. For more details, on this
552+
parameter, see the docs of `compute_smoothed_feasibility_indicator`.
553+
fat: Toggles the logarithmic / linear asymptotic behavior of the smooth
554+
approximation to the ReLU.
555+
tau_max: Temperature parameter controlling the sharpness of the smooth
556+
approximations to max.
557+
tau_relu: Temperature parameter controlling the sharpness of the smooth
558+
approximations to ReLU.
559+
ignored: Not used.
560+
561+
Returns:
562+
A dict mapping kwarg names of the constructor to values.
563+
"""
564+
return {
565+
**construct_inputs_qEI(
566+
model=model,
567+
training_data=training_data,
568+
objective=objective,
569+
posterior_transform=posterior_transform,
570+
X_pending=X_pending,
571+
sampler=sampler,
572+
best_f=best_f,
573+
constraints=constraints,
574+
eta=eta,
575+
),
576+
"fat": fat,
577+
"tau_max": tau_max,
578+
"tau_relu": tau_relu,
579+
}
580+
581+
511582
@acqf_input_constructor(qNoisyExpectedImprovement)
512583
def construct_inputs_qNEI(
513584
model: Model,
@@ -570,7 +641,6 @@ def construct_inputs_qNEI(
570641
assert_shared=True,
571642
first_only=True,
572643
)
573-
574644
return {
575645
**base_inputs,
576646
"X_baseline": X_baseline,
@@ -581,6 +651,82 @@ def construct_inputs_qNEI(
581651
}
582652

583653

654+
@acqf_input_constructor(qLogNoisyExpectedImprovement)
655+
def construct_inputs_qLogNEI(
656+
model: Model,
657+
training_data: MaybeDict[SupervisedDataset],
658+
objective: Optional[MCAcquisitionObjective] = None,
659+
posterior_transform: Optional[PosteriorTransform] = None,
660+
X_pending: Optional[Tensor] = None,
661+
sampler: Optional[MCSampler] = None,
662+
X_baseline: Optional[Tensor] = None,
663+
prune_baseline: Optional[bool] = True,
664+
cache_root: Optional[bool] = True,
665+
constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
666+
eta: Union[Tensor, float] = 1e-3,
667+
fat: bool = True,
668+
tau_max: float = TAU_MAX,
669+
tau_relu: float = TAU_RELU,
670+
**ignored: Any,
671+
):
672+
r"""Construct kwargs for the `qNoisyExpectedImprovement` constructor.
673+
674+
Args:
675+
model: The model to be used in the acquisition function.
676+
training_data: Dataset(s) used to train the model.
677+
objective: The objective to be used in the acquisition function.
678+
posterior_transform: The posterior transform to be used in the
679+
acquisition function.
680+
X_pending: A `m x d`-dim Tensor of `m` design points that have been
681+
submitted for function evaluation but have not yet been evaluated.
682+
Concatenated into X upon forward call.
683+
sampler: The sampler used to draw base samples. If omitted, uses
684+
the acquisition functions's default sampler.
685+
X_baseline: A `batch_shape x r x d`-dim Tensor of `r` design points
686+
that have already been observed. These points are considered as
687+
the potential best design point. If omitted, checks that all
688+
training_data have the same input features and take the first `X`.
689+
prune_baseline: If True, remove points in `X_baseline` that are
690+
highly unlikely to be the best point. This can significantly
691+
improve performance and is generally recommended.
692+
constraints: A list of constraint callables which map a Tensor of posterior
693+
samples of dimension `sample_shape x batch-shape x q x m`-dim to a
694+
`sample_shape x batch-shape x q`-dim Tensor. The associated constraints
695+
are considered satisfied if the output is less than zero.
696+
eta: Temperature parameter(s) governing the smoothness of the sigmoid
697+
approximation to the constraint indicators. For more details, on this
698+
parameter, see the docs of `compute_smoothed_feasibility_indicator`.
699+
fat: Toggles the logarithmic / linear asymptotic behavior of the smooth
700+
approximation to the ReLU.
701+
tau_max: Temperature parameter controlling the sharpness of the smooth
702+
approximations to max.
703+
tau_relu: Temperature parameter controlling the sharpness of the smooth
704+
approximations to ReLU.
705+
ignored: Not used.
706+
707+
Returns:
708+
A dict mapping kwarg names of the constructor to values.
709+
"""
710+
return {
711+
**construct_inputs_qNEI(
712+
model=model,
713+
training_data=training_data,
714+
objective=objective,
715+
posterior_transform=posterior_transform,
716+
X_pending=X_pending,
717+
sampler=sampler,
718+
X_baseline=X_baseline,
719+
prune_baseline=prune_baseline,
720+
cache_root=cache_root,
721+
constraint=constraints,
722+
eta=eta,
723+
),
724+
"fat": fat,
725+
"tau_max": tau_max,
726+
"tau_relu": tau_relu,
727+
}
728+
729+
584730
@acqf_input_constructor(qProbabilityOfImprovement)
585731
def construct_inputs_qPI(
586732
model: Model,

botorch/acquisition/logei.py

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@
77
Batch implementations of the LogEI family of improvements-based acquisition functions.
88
"""
99

10-
1110
from __future__ import annotations
1211

1312
from functools import partial
1413

15-
from typing import Callable, List, Optional, TypeVar, Union
14+
from typing import Any, Callable, List, Optional, Tuple, TypeVar, Union
1615

1716
import torch
18-
from botorch.acquisition.monte_carlo import SampleReducingMCAcquisitionFunction
17+
from botorch.acquisition.monte_carlo import (
18+
NoisyExpectedImprovementMixin,
19+
SampleReducingMCAcquisitionFunction,
20+
)
1921
from botorch.acquisition.objective import (
2022
ConstrainedMCObjective,
2123
MCAcquisitionObjective,
@@ -219,6 +221,135 @@ def _sample_forward(self, obj: Tensor) -> Tensor:
219221
return li
220222

221223

224+
class qLogNoisyExpectedImprovement(
225+
LogImprovementMCAcquisitionFunction, NoisyExpectedImprovementMixin
226+
):
227+
r"""MC-based batch Log Noisy Expected Improvement.
228+
229+
This function does not assume a `best_f` is known (which would require
230+
noiseless observations). Instead, it uses samples from the joint posterior
231+
over the `q` test points and previously observed points. A smooth approximation
232+
to the canonical improvement over previously observed points is computed
233+
for each sample and the logarithm of the average is returned.
234+
235+
`qLogNEI(X) ~ log(qNEI(X)) = Log E(max(max Y - max Y_baseline, 0))`, where
236+
`(Y, Y_baseline) ~ f((X, X_baseline)), X = (x_1,...,x_q)`
237+
238+
Example:
239+
>>> model = SingleTaskGP(train_X, train_Y)
240+
>>> sampler = SobolQMCNormalSampler(1024)
241+
>>> qLogNEI = qLogNoisyExpectedImprovement(model, train_X, sampler)
242+
>>> acqval = qLogNEI(test_X)
243+
"""
244+
245+
def __init__(
246+
self,
247+
model: Model,
248+
X_baseline: Tensor,
249+
sampler: Optional[MCSampler] = None,
250+
objective: Optional[MCAcquisitionObjective] = None,
251+
posterior_transform: Optional[PosteriorTransform] = None,
252+
X_pending: Optional[Tensor] = None,
253+
constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
254+
eta: Union[Tensor, float] = 1e-3,
255+
fat: bool = True,
256+
prune_baseline: bool = False,
257+
cache_root: bool = True,
258+
tau_max: float = TAU_MAX,
259+
tau_relu: float = TAU_RELU,
260+
**kwargs: Any,
261+
) -> None:
262+
r"""q-Noisy Expected Improvement.
263+
264+
Args:
265+
model: A fitted model.
266+
X_baseline: A `batch_shape x r x d`-dim Tensor of `r` design points
267+
that have already been observed. These points are considered as
268+
the potential best design point.
269+
sampler: The sampler used to draw base samples. See `MCAcquisitionFunction`
270+
more details.
271+
objective: The MCAcquisitionObjective under which the samples are
272+
evaluated. Defaults to `IdentityMCObjective()`.
273+
posterior_transform: A PosteriorTransform (optional).
274+
X_pending: A `batch_shape x m x d`-dim Tensor of `m` design points
275+
that have points that have been submitted for function evaluation
276+
but have not yet been evaluated. Concatenated into `X` upon
277+
forward call. Copied and set to have no gradient.
278+
constraints: A list of constraint callables which map a Tensor of posterior
279+
samples of dimension `sample_shape x batch-shape x q x m`-dim to a
280+
`sample_shape x batch-shape x q`-dim Tensor. The associated constraints
281+
are satisfied if `constraint(samples) < 0`.
282+
eta: Temperature parameter(s) governing the smoothness of the sigmoid
283+
approximation to the constraint indicators. See the docs of
284+
`compute_(log_)smoothed_constraint_indicator` for details.
285+
fat: Toggles the logarithmic / linear asymptotic behavior of the smooth
286+
approximation to the ReLU.
287+
prune_baseline: If True, remove points in `X_baseline` that are
288+
highly unlikely to be the best point. This can significantly
289+
improve performance and is generally recommended. In order to
290+
customize pruning parameters, instead manually call
291+
`botorch.acquisition.utils.prune_inferior_points` on `X_baseline`
292+
before instantiating the acquisition function.
293+
cache_root: A boolean indicating whether to cache the root
294+
decomposition over `X_baseline` and use low-rank updates.
295+
tau_max: Temperature parameter controlling the sharpness of the smooth
296+
approximations to max.
297+
tau_relu: Temperature parameter controlling the sharpness of the smooth
298+
approximations to ReLU.
299+
kwargs: Here for qNEI for compatibility.
300+
301+
TODO: similar to qNEHVI, when we are using sequential greedy candidate
302+
selection, we could incorporate pending points X_baseline and compute
303+
the incremental q(Log)NEI from the new point. This would greatly increase
304+
efficiency for large batches.
305+
"""
306+
LogImprovementMCAcquisitionFunction.__init__(
307+
self,
308+
model=model,
309+
sampler=sampler,
310+
objective=objective,
311+
posterior_transform=posterior_transform,
312+
X_pending=X_pending,
313+
constraints=constraints,
314+
eta=eta,
315+
fat=fat,
316+
tau_max=tau_max,
317+
)
318+
self.tau_relu = tau_relu
319+
NoisyExpectedImprovementMixin.__init__(
320+
self,
321+
model=model,
322+
X_baseline=X_baseline,
323+
sampler=sampler,
324+
objective=objective,
325+
posterior_transform=posterior_transform,
326+
prune_baseline=prune_baseline,
327+
cache_root=cache_root,
328+
**kwargs,
329+
)
330+
331+
def _sample_forward(self, obj: Tensor) -> Tensor:
332+
r"""Evaluate qLogNoisyExpectedImprovement per sample on the candidate set `X`.
333+
334+
Args:
335+
obj: `mc_shape x batch_shape x q`-dim Tensor of MC objective values.
336+
337+
Returns:
338+
A `sample_shape x batch_shape x q`-dim Tensor of log noisy expected smoothed
339+
improvement values.
340+
"""
341+
return _log_improvement(
342+
Y=obj,
343+
best_f=self.compute_best_f(obj),
344+
tau=self.tau_relu,
345+
fat=self._fat,
346+
)
347+
348+
def _get_samples_and_objectives(self, X: Tensor) -> Tuple[Tensor, Tensor]:
349+
# Explicit, as both parent classes have this method, so no MRO magic required.
350+
return NoisyExpectedImprovementMixin._get_samples_and_objectives(self, X)
351+
352+
222353
"""
223354
###################################### utils ##########################################
224355
"""

0 commit comments

Comments
 (0)