Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
3ce4ab0
Multi fidelity searchspaces and surrogate modelling
jpenn2023 Jan 7, 2026
5525bb0
Typing fixes
jpenn2023 Jan 13, 2026
da08684
More typing fixes
jpenn2023 Jan 14, 2026
c0ee704
More typing fixes with some unresolved
jpenn2023 Jan 16, 2026
455466c
Typo fix
jpenn2023 Feb 5, 2026
163574b
Integrating multi fidelity surrogate models with multitask refactor
jpenn2023 Feb 27, 2026
8fdff67
Integrate typing
jpenn2023 Feb 27, 2026
55d12b0
Integrating kernel factories with multi fidelity
jpenn2023 Mar 6, 2026
dee1768
Move _ModelContext
jpenn2023 Mar 20, 2026
98b6396
Remove deprecated/unused from_preset from STMF GP.
jpenn2023 Mar 20, 2026
bc6bb18
Mainly docstring fixes
jpenn2023 Mar 20, 2026
ac1042f
Separating task and fidelity types in search space.
jpenn2023 Mar 27, 2026
5e1ea96
Tidying supports_ inheritance
jpenn2023 Mar 31, 2026
7ae620d
Remove unused imports after ModelContext move
AVHopp Apr 24, 2026
14fc575
Minor fixes in `gaussian_process/utils.py`
AVHopp May 22, 2026
013b444
Add assertion to verify that only one task resp. fidelity parameter i…
AVHopp May 22, 2026
703232e
Fix bugs and improve code for `task_type` and `fidelity_type`
AVHopp May 22, 2026
46f96e7
Remove unused import that were overlooked during rebase
AVHopp Jun 11, 2026
55dab58
Fix incorrect formatting
AVHopp Jun 11, 2026
6cf2fd2
Re-introduce `objective` and `measurements` fields to `_ModelContext`
AVHopp Jun 15, 2026
70de6a7
Add support for `_ParameterKind.FIDELITY` to `from_parameters``
AVHopp Jun 15, 2026
377f18e
Add validation for multiple fidelity/task parameter
AVHopp Jun 15, 2026
58c30d9
Use likelihood and fit criterion factories in STMF-GP
AVHopp Jun 15, 2026
81d1b16
Add fidelity type validation in `_fit`
AVHopp Jun 15, 2026
0b375c7
Add construction tests for multi-fidelity surrogates
AVHopp Jun 15, 2026
88046e4
Generalize `_enable_transfer_learning` to `_enable_index_kernel`
AVHopp Jun 15, 2026
9cf8f6a
Add additional kernel rejection tests
AVHopp Jun 15, 2026
c97b68f
Generalize `__call__` of ICM Kernel to also handle fidelities
AVHopp Jun 15, 2026
9403132
Generalize index kernel factory to support categorical fidelity param…
AVHopp Jun 15, 2026
3d9525a
Rename `_BayBETaskKernelFactory` to `_BayBEIndexKernelFactory`
AVHopp Jun 15, 2026
778d54c
Add test for fitting GaussianProcessSurrogate on categorical fidelity…
AVHopp Jun 15, 2026
093336c
Auto-dispatch correct surrogate model based on the search space
AVHopp Jun 15, 2026
8549b97
Use LOO for any search space that uses the IndexKernel structure
AVHopp Jun 15, 2026
7597cf1
Improve docstrings
AVHopp Jun 15, 2026
cf60531
Adjust incorrect comments
AVHopp Jun 15, 2026
74b2b07
Minor fixes to type hint and str method
AVHopp Jun 15, 2026
e407771
Validate surrogate fit context before caching
AVHopp Jun 19, 2026
0c85748
Re-introduce `to_numpy(copy=True)` conversion
AVHopp Jun 19, 2026
7673471
Re-dispatch default surrogate for changed fidelity type
AVHopp Jun 19, 2026
495e466
Expose MultiFidelty model for higher level imports
AVHopp Jun 19, 2026
b55fcf9
Add support for fidelity parameters in Hvarfner preset
AVHopp Jun 19, 2026
6d0baec
Remove unnecessary type: ignore comments
AVHopp Jun 19, 2026
55a6851
Tighten ICML Kernel fidelity guard
AVHopp Jun 19, 2026
088197b
Reject index-only search spaces
AVHopp Jun 19, 2026
cd7a5cb
Improve in-line comments
AVHopp Jun 19, 2026
4bdba0c
Model-class dispatch inside `_fit`
AVHopp Jun 26, 2026
bdc0a3c
Revert "Model-class dispatch inside `_fit`"
AVHopp Jun 26, 2026
4e50c7f
Re-create `botorch` model using our factories
AVHopp Jun 26, 2026
dbe5a3d
Adjust kernel mechanism to follow new botorch settings
AVHopp Jun 26, 2026
d57b7b4
Add acquisition functions
jpenn2023 Feb 27, 2026
30df5bd
Add acquisition functions
jpenn2023 Feb 27, 2026
a43681f
Moving generic dict comparison validator
jpenn2023 Mar 5, 2026
e8d1cfe
Adding qMFKG botorch attributes and including qKG current_value attri…
jpenn2023 Mar 5, 2026
d91fdcc
Add SearchSpaceTaskType to searchspace init file
jpenn2023 Mar 5, 2026
fa0caee
Add multi fidelity acqfs to acquistion init files
jpenn2023 Mar 5, 2026
93daff5
Add multi-fidelity acquisiton arguments
jpenn2023 Mar 5, 2026
c876c3f
Add recommender logic.
jpenn2023 Mar 5, 2026
955669f
Checkpoint files should not be in the merge.
jpenn2023 Mar 9, 2026
37d81eb
Attrs usage for custom acqf, minor bug fixes and docstring updates
jpenn2023 Mar 9, 2026
f53a5dd
Validation typing fix
jpenn2023 Mar 9, 2026
1abc8ae
Typing fixes: working around broad Botorch typing
jpenn2023 Mar 13, 2026
a298ed9
searchspace dependent acquisition function choice
jpenn2023 Mar 13, 2026
d05ed67
MFUCB fidelities costs and values set at acqf build time
jpenn2023 Mar 13, 2026
8c99f4e
Comp rep fidelity dictionary for MFUCB
jpenn2023 Mar 13, 2026
bc3c3b3
Restricted searchspace for MFUCB stage one
jpenn2023 Mar 13, 2026
d699ed9
Fixed custom acqf imports
jpenn2023 Mar 13, 2026
e294393
Fix typing in acqf builder
jpenn2023 Mar 13, 2026
992ad7a
Docstring and typing fixes.
jpenn2023 Mar 27, 2026
832e173
Minor fixes
jpenn2023 Mar 27, 2026
7aa47eb
Docstrings, error messages, variable names, file structure.
jpenn2023 Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions baybe/acquisition/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from baybe.acquisition.acqfs import (
ExpectedImprovement,
LogExpectedImprovement,
MultiFidelityUpperConfidenceBound,
PosteriorMean,
PosteriorStandardDeviation,
ProbabilityOfImprovement,
Expand All @@ -13,6 +14,7 @@
qLogNoisyExpectedHypervolumeImprovement,
qLogNoisyExpectedImprovement,
qLogNParEGO,
qMultiFidelityKnowledgeGradient,
qNegIntegratedPosteriorVariance,
qNoisyExpectedHypervolumeImprovement,
qNoisyExpectedImprovement,
Expand All @@ -30,6 +32,7 @@
EI = ExpectedImprovement
qEI = qExpectedImprovement
qKG = qKnowledgeGradient
qMFKG = qMultiFidelityKnowledgeGradient
LogEI = LogExpectedImprovement
qLogEI = qLogExpectedImprovement
qNEI = qNoisyExpectedImprovement
Expand All @@ -38,6 +41,7 @@
PI = ProbabilityOfImprovement
qPI = qProbabilityOfImprovement
UCB = UpperConfidenceBound
MFUCB = MultiFidelityUpperConfidenceBound
qUCB = qUpperConfidenceBound
qTS = qThompsonSampling
qNEHVI = qNoisyExpectedHypervolumeImprovement
Expand All @@ -47,6 +51,7 @@
######################### Acquisition functions
# Knowledge Gradient
"qKnowledgeGradient",
"qMultiFidelityKnowledgeGradient",
# Posterior Statistics
"PosteriorMean",
"PosteriorStandardDeviation",
Expand All @@ -67,6 +72,7 @@
# Upper Confidence Bound
"UpperConfidenceBound",
"qUpperConfidenceBound",
"MultiFidelityUpperConfidenceBound",
# Thompson Sampling
"qThompsonSampling",
# Hypervolume Improvement
Expand All @@ -77,6 +83,7 @@
######################### Abbreviations
# Knowledge Gradient
"qKG",
"qMFKG",
# Posterior Statistics
"PM",
"PSTD",
Expand All @@ -97,6 +104,7 @@
# Upper Confidence Bound
"UCB",
"qUCB",
"MFUCB",
# Thompson Sampling
"qTS",
# Hypervolume Improvement
Expand Down
87 changes: 86 additions & 1 deletion baybe/acquisition/_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
_ExpectedHypervolumeImprovement,
qExpectedHypervolumeImprovement,
qLogExpectedHypervolumeImprovement,
qMultiFidelityKnowledgeGradient,
qNegIntegratedPosteriorVariance,
qThompsonSampling,
)
from baybe.acquisition.base import AcquisitionFunction, _get_botorch_acqf_class
from baybe.acquisition.utils import make_partitioning
from baybe.acquisition.custom_acqfs import MultiFidelityUpperConfidenceBound
from baybe.acquisition.utils import make_MFUCB_dicts, make_partitioning
from baybe.exceptions import (
IncompatibilityError,
IncompleteMeasurementsError,
Expand Down Expand Up @@ -75,16 +77,21 @@ class BotorchAcquisitionArgs:
# Optional, depending on the specific acquisition function being used
best_f: float | None = _OPT_FIELD
beta: float | None = _OPT_FIELD
costs_dict: dict[Any, tuple[float, ...]] = _OPT_FIELD
current_value: Tensor | None = _OPT_FIELD
fidelities_dict: dict[Any, tuple[Any, ...]] = _OPT_FIELD
maximize: bool | None = _OPT_FIELD
mc_points: Tensor | None = _OPT_FIELD
num_fantasies: int | None = _OPT_FIELD
objective: MCAcquisitionObjective | None = _OPT_FIELD
partitioning: BoxDecomposition | None = _OPT_FIELD
posterior_transform: PosteriorTransform | None = _OPT_FIELD
project: Callable[[Tensor], Tensor] | None = _OPT_FIELD
prune_baseline: bool | None = _OPT_FIELD
ref_point: Tensor | None = _OPT_FIELD
X_baseline: Tensor | None = _OPT_FIELD
X_pending: Tensor | None = _OPT_FIELD
zetas_dict: dict[Any, tuple[float, ...]] = _OPT_FIELD

def collect(self) -> dict[str, Any]:
"""Collect the assigned arguments into a dictionary."""
Expand Down Expand Up @@ -202,6 +209,9 @@ def build(self) -> BoAcquisitionFunction:
self._set_mc_points()
self._set_ref_point()
self._set_partitioning()
self._set_current_value()
self._set_projection()
self._set_MFUCB_dicts()

botorch_acqf = self._botorch_acqf_cls(**self._args.collect())
self.set_default_sample_shape(botorch_acqf)
Expand Down Expand Up @@ -264,6 +274,81 @@ def _set_best_f(self) -> None:
case _:
raise NotImplementedError("This line should be impossible to reach.")

def _set_current_value(self) -> None:
"""Set current value maximising posterior mean in qMFKG."""
if not isinstance(self.acqf, qMultiFidelityKnowledgeGradient):
return

from botorch.optim import optimize_acqf_mixed

if isinstance(self.acqf, qMultiFidelityKnowledgeGradient):
from botorch.acquisition import PosteriorMean
from botorch.acquisition.fixed_feature import (
FixedFeatureAcquisitionFunction,
)

curr_val_acqf = FixedFeatureAcquisitionFunction(
acq_function=PosteriorMean(self._botorch_surrogate),
d=len(self.searchspace.parameters),
columns=[
self.searchspace.fidelity_idx,
],
values=[
1.0,
],
)

# Jordan MHS NOTE: This is fast-and-loose use of mixed space optimization.
# Changes will be made with the next PR which uses a notion of wrapped acqfs
# for setting a current value but also for defining cost aware wrappers.

candidates_comp = self.searchspace.discrete.comp_rep
num_comp_columns = len(candidates_comp.columns)
candidates_comp.columns = list(range(num_comp_columns)) # type: ignore
candidates_comp_dict = candidates_comp.to_dict("records")

# Possible TODO. Align num_restarts and raw_samples with that defined by the
# user for the main acquisition function.
_, current_value = optimize_acqf_mixed(
acq_function=curr_val_acqf,
bounds=torch.from_numpy(self.searchspace.comp_rep_bounds.values),
fixed_features_list=candidates_comp_dict, # type: ignore[arg-type]
q=1,
num_restarts=10,
raw_samples=64,
)

self._args.current_value = current_value

def _set_projection(self) -> None:
"""Set projection to the target fidelity for qMFKG."""
if not isinstance(self.acqf, (qMultiFidelityKnowledgeGradient)):
return

assert self.searchspace.fidelity_idx is not None # for mypy

target_fidelities = {self.searchspace.fidelity_idx: 1.0}

num_dims = len(self.searchspace.parameters)

def target_fidelity_projection(X: Tensor) -> Tensor:
from botorch.acquisition.utils import project_to_target_fidelity

return project_to_target_fidelity(X, target_fidelities, num_dims)

self._args.project = target_fidelity_projection

def _set_MFUCB_dicts(self) -> None:
Comment thread
jpenn2023 marked this conversation as resolved.
"""Set value, fidelities and cost dictionaries for MFUCB."""
if not isinstance(self.acqf, MultiFidelityUpperConfidenceBound):
return

fidelities_dict, costs_dict, zetas_dict = make_MFUCB_dicts(self.searchspace)

self._args.fidelities_dict = fidelities_dict
self._args.costs_dict = costs_dict
self._args.zetas_dict = zetas_dict

def set_default_sample_shape(self, acqf: BoAcquisitionFunction, /):
"""Apply temporary workaround for Thompson sampling."""
# TODO: Needs redesign once bandits are supported more generally
Expand Down
43 changes: 42 additions & 1 deletion baybe/acquisition/acqfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from attr.converters import optional as optional_c
from attr.validators import optional as optional_v
from attrs import AttrsInstance, define, field, fields
from attrs.validators import gt, instance_of, le
from attrs.validators import ge, gt, instance_of, le
from typing_extensions import override

from baybe.acquisition.base import AcquisitionFunction
Expand Down Expand Up @@ -156,6 +156,22 @@ class qKnowledgeGradient(AcquisitionFunction):
memory footprint and wall time."""


@define(frozen=True)
class qMultiFidelityKnowledgeGradient(AcquisitionFunction):
"""Monte Carlo based knowledge gradient.

This acquisition function currently only supports purely continuous spaces.
"""

abbreviation: ClassVar[str] = "qMFKG"

num_fantasies: int = field(validator=[instance_of(int), gt(0)], default=128)
"""Number of fantasies to draw for approximating the knowledge gradient.

More samples result in a better approximation, at the expense of both increased
memory footprint and wall time."""


########################################################################################
### Posterior Statistics
@define(frozen=True)
Expand Down Expand Up @@ -289,6 +305,31 @@ class qUpperConfidenceBound(AcquisitionFunction):
"""See :paramref:`UpperConfidenceBound.beta`."""


@define(frozen=True)
class MultiFidelityUpperConfidenceBound(AcquisitionFunction):
"""Two stage acquisition function of Kandasamy et al (2016).

Stage 1: Choose design features based on argmax_x (softmin_m (UCB_m(x) + zeta_m)).

Stage 2: Choose cheapest fidelity satisfying a cost-aware informativeness threshold.
"""

abbreviation: ClassVar[str] = "MFUCB"

softmin_temperature: float = field(
converter=float, validator=[finite_float, ge(0.0)], default=1e-2
)
"""Softmin smoothing parameter."""

beta: float = field(converter=float, validator=finite_float, default=0.2)
"""See :paramref:`UpperConfidenceBound.beta`."""

@override
@classproperty
def supports_batching(cls) -> bool:
return False


########################################################################################
### ThompsonSampling
@define(frozen=True)
Expand Down
3 changes: 3 additions & 0 deletions baybe/acquisition/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,14 @@ def _get_botorch_acqf_class(
"""Extract the BoTorch acquisition class for the given BayBE acquisition class."""
import botorch

from baybe.acquisition import custom_acqfs

for cls in baybe_acqf_cls.mro():
if (
acqf_cls := getattr(botorch.acquisition, cls.__name__, False)
or getattr(botorch.acquisition.multi_objective, cls.__name__, False)
or getattr(botorch.acquisition.multi_objective.parego, cls.__name__, False)
or getattr(custom_acqfs, cls.__name__, False)
):
if is_abstract(acqf_cls):
continue
Expand Down
10 changes: 10 additions & 0 deletions baybe/acquisition/custom_acqfs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Custom acquisition functions."""

from baybe.acquisition.custom_acqfs.two_stage import (
MultiFidelityUpperConfidenceBound,
)

__all__ = [
# Multi fidelity acquisition functions
"MultiFidelityUpperConfidenceBound",
]
Loading
Loading