Skip to content
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

Support multiple data fidelity dimensions in MultiFidelityGP models #1956

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
41b08e8
generalises SingleTaskMultiFidelityGP and FixedNoiseMultiFidelityGP t…
Jul 30, 2023
c3949d1
Merge remote-tracking branch 'upstream/main'
Aug 7, 2023
0b3cb6d
addresses PR comments
Aug 7, 2023
633d9c0
BOPE tutorial text edits (#1972)
esantorella Aug 9, 2023
3506538
Merge SupervisedDataset & FixedNoiseDataset (#1945)
saitcakmak Aug 9, 2023
52529e1
fix constraint handling in single objective MBM (#1973)
sdaulton Aug 10, 2023
0a1cfbe
update changelog for 0.9.2
sdaulton Aug 10, 2023
b7258c8
Type signature fix for private methods of `qMultiStepLookahead`
SebastianAment Aug 10, 2023
fe122b0
bump py version in deploy on release
sdaulton Aug 11, 2023
b6e74ac
Don't allow unused **kwargs in input_constructors except for a define…
esantorella Aug 17, 2023
814f522
Even more fixes to unused kwargs (#1985)
esantorella Aug 18, 2023
36549fd
generalises SingleTaskMultiFidelityGP and FixedNoiseMultiFidelityGP t…
Jul 30, 2023
2b4c5e2
addresses PR comments
Aug 7, 2023
2d41c94
restores coverage to 100%
Aug 18, 2023
129251b
updates documentation for multi-fidelity GPs in tutorials
Aug 18, 2023
9a1a41f
merge changes from upstream/main
Aug 18, 2023
946f0b4
Dont use a Sphinx version later than 7.1.2 (#1990)
Aug 18, 2023
d245dd4
generalises SingleTaskMultiFidelityGP and FixedNoiseMultiFidelityGP t…
Jul 30, 2023
9bc46ca
addresses PR comments
Aug 7, 2023
1666b1d
restores coverage to 100%
Aug 18, 2023
db9b864
updates documentation for multi-fidelity GPs in tutorials
Aug 18, 2023
c56eb14
Merge remote-tracking branch 'refs/remotes/origin/main'
Aug 20, 2023
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
189 changes: 114 additions & 75 deletions botorch/models/gp_regression_fidelity.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def __init__(
train_X: Tensor,
train_Y: Tensor,
iteration_fidelity: Optional[int] = None,
data_fidelity: Optional[int] = None,
data_fidelity: Optional[int | List[int]] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I like this, the | operator only works in py3.10+, and we still need to be compatible with py3.9

Suggested change
data_fidelity: Optional[int | List[int]] = None,
data_fidelity: Optional[Union[int, List[int]]] = None,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also make sense to have a data_fidelities argument that only accepts lists (or tuples) and require people to use that even for the single-fidelity case, that way we wouldn't have to overload the arg types. This could be done nicely by keeping the data_fidelity arg around but marking it as deprecated (and raise a deprecation warning if used in the constructor). Wdyt?

cc @saitcakmak, @esantorella for thoughts.

linear_truncated: bool = True,
nu: float = 2.5,
likelihood: Optional[Likelihood] = None,
Expand All @@ -81,8 +81,9 @@ def __init__(
train_Y: A `batch_shape x n x m` tensor of training observations.
iteration_fidelity: The column index for the training iteration fidelity
parameter (optional).
data_fidelity: The column index for the downsampling fidelity parameter
(optional).
data_fidelity: The column index for the downsampling fidelity parameter.
If multiple indices are provided, a kernel will be constructed for
Balandat marked this conversation as resolved.
Show resolved Hide resolved
each index (optional).
linear_truncated: If True, use a `LinearTruncatedFidelityKernel` instead
of the default kernel.
nu: The smoothness parameter for the Matern kernel: either 1/2, 3/2, or
Expand Down Expand Up @@ -117,7 +118,7 @@ def __init__(
dim=transformed_X.size(-1),
aug_batch_shape=self._aug_batch_shape,
iteration_fidelity=iteration_fidelity,
data_fidelity=data_fidelity,
data_fidelities=data_fidelity,
linear_truncated=linear_truncated,
nu=nu,
)
Expand Down Expand Up @@ -150,11 +151,8 @@ def construct_inputs(
training_data: Dictionary of `SupervisedDataset`.
fidelity_features: Index of fidelity parameter as input columns.
"""
if len(fidelity_features) != 1:
raise UnsupportedError("Multiple fidelity features not supported.")
Balandat marked this conversation as resolved.
Show resolved Hide resolved

inputs = super().construct_inputs(training_data=training_data, **kwargs)
inputs["data_fidelity"] = fidelity_features[0]
inputs["data_fidelity"] = fidelity_features
return inputs


Expand Down Expand Up @@ -185,7 +183,7 @@ def __init__(
train_Y: Tensor,
train_Yvar: Tensor,
iteration_fidelity: Optional[int] = None,
data_fidelity: Optional[int] = None,
data_fidelity: Optional[int | List[int]] = None,
linear_truncated: bool = True,
nu: float = 2.5,
outcome_transform: Optional[OutcomeTransform] = None,
Expand All @@ -200,8 +198,9 @@ def __init__(
train_Yvar: A `batch_shape x n x m` tensor of observed measurement noise.
iteration_fidelity: The column index for the training iteration fidelity
parameter (optional).
data_fidelity: The column index for the downsampling fidelity parameter
(optional).
data_fidelity: The column index for the downsampling fidelity parameter.
If multiple indices are provided, a kernel will be constructed for
Balandat marked this conversation as resolved.
Show resolved Hide resolved
each index (optional).
linear_truncated: If True, use a `LinearTruncatedFidelityKernel` instead
of the default kernel.
nu: The smoothness parameter for the Matern kernel: either 1/2, 3/2, or
Expand All @@ -213,6 +212,13 @@ def __init__(
input_transform: An input transform that is applied in the model's
forward pass.
"""
self._init_args = {
"iteration_fidelity": iteration_fidelity,
"data_fidelity": data_fidelity,
"linear_truncated": linear_truncated,
"nu": nu,
"outcome_transform": outcome_transform,
}
if iteration_fidelity is None and data_fidelity is None:
raise UnsupportedError(
"FixedNoiseMultiFidelityGP requires at least one fidelity parameter."
Expand All @@ -226,7 +232,7 @@ def __init__(
dim=transformed_X.size(-1),
aug_batch_shape=self._aug_batch_shape,
iteration_fidelity=iteration_fidelity,
data_fidelity=data_fidelity,
data_fidelities=data_fidelity,
linear_truncated=linear_truncated,
nu=nu,
)
Expand Down Expand Up @@ -259,19 +265,16 @@ def construct_inputs(
training_data: Dictionary of `SupervisedDataset`.
fidelity_features: Column indices of fidelity features.
"""
if len(fidelity_features) != 1:
raise UnsupportedError("Multiple fidelity features not supported.")

inputs = super().construct_inputs(training_data=training_data, **kwargs)
inputs["data_fidelity"] = fidelity_features[0]
inputs["data_fidelity"] = fidelity_features
return inputs


def _setup_multifidelity_covar_module(
dim: int,
aug_batch_shape: torch.Size,
iteration_fidelity: Optional[int],
data_fidelity: Optional[int],
data_fidelities: Optional[int | List[int]],
linear_truncated: bool,
nu: float,
) -> Tuple[ScaleKernel, Dict]:
Expand All @@ -284,8 +287,8 @@ def _setup_multifidelity_covar_module(
`BatchedMultiOutputGPyTorchModel`.
iteration_fidelity: The column index for the training iteration fidelity
parameter (optional).
data_fidelity: The column index for the downsampling fidelity parameter
(optional).
data_fidelities: The column indices for the downsampling fidelity parameters
(optional). A single index can be provided as an int.
Balandat marked this conversation as resolved.
Show resolved Hide resolved
linear_truncated: If True, use a `LinearTruncatedFidelityKernel` instead
of the default kernel.
nu: The smoothness parameter for the Matern kernel: either 1/2, 3/2, or
Expand All @@ -295,78 +298,114 @@ def _setup_multifidelity_covar_module(
The covariance module and subset_batch_dict.
"""

if iteration_fidelity is not None and iteration_fidelity < 0:
iteration_fidelity = dim + iteration_fidelity
if data_fidelity is not None and data_fidelity < 0:
data_fidelity = dim + data_fidelity
if iteration_fidelity is not None:
if iteration_fidelity < 0:
iteration_fidelity = dim + iteration_fidelity
Balandat marked this conversation as resolved.
Show resolved Hide resolved
if data_fidelities is not None:
if isinstance(data_fidelities, int):
data_fidelities = [data_fidelities]
for i in range(len(data_fidelities)):
if data_fidelities[i] < 0:
data_fidelities[i] = dim + data_fidelities[i]
Balandat marked this conversation as resolved.
Show resolved Hide resolved

kernels = []

if linear_truncated:
fidelity_dims = [
i for i in (iteration_fidelity, data_fidelity) if i is not None
]
kernel = LinearTruncatedFidelityKernel(
fidelity_dims=fidelity_dims,
dimension=dim,
nu=nu,
batch_shape=aug_batch_shape,
power_prior=GammaPrior(3.0, 3.0),
)
if data_fidelities is not None:
for data_fidelity in data_fidelities:
if iteration_fidelity is not None:
fidelity_dims = [iteration_fidelity, data_fidelity]
else:
fidelity_dims = [data_fidelity]
kernels.append(
LinearTruncatedFidelityKernel(
fidelity_dims=fidelity_dims,
dimension=dim,
nu=nu,
batch_shape=aug_batch_shape,
power_prior=GammaPrior(3.0, 3.0),
)
)
else:
kernels.append(
LinearTruncatedFidelityKernel(
fidelity_dims=[iteration_fidelity],
dimension=dim,
nu=nu,
batch_shape=aug_batch_shape,
power_prior=GammaPrior(3.0, 3.0),
)
)
Balandat marked this conversation as resolved.
Show resolved Hide resolved
else:
active_dimsX = [
i for i in range(dim) if i not in {iteration_fidelity, data_fidelity}
]
kernel = RBFKernel(
ard_num_dims=len(active_dimsX),
batch_shape=aug_batch_shape,
lengthscale_prior=GammaPrior(3.0, 6.0),
active_dims=active_dimsX,
)
additional_kernels = []
if iteration_fidelity is not None:
exp_kernel = ExponentialDecayKernel(
if iteration_fidelity is not None and data_fidelities is not None:
active_dimsX = [
i
for i in range(dim)
if (i != iteration_fidelity and i not in data_fidelities)
]
elif iteration_fidelity is not None:
active_dimsX = [i for i in range(dim) if i != iteration_fidelity]
elif data_fidelities is not None:
active_dimsX = [i for i in range(dim) if i not in data_fidelities]
Balandat marked this conversation as resolved.
Show resolved Hide resolved
kernels.append(
RBFKernel(
ard_num_dims=len(active_dimsX),
batch_shape=aug_batch_shape,
lengthscale_prior=GammaPrior(3.0, 6.0),
offset_prior=GammaPrior(3.0, 6.0),
power_prior=GammaPrior(3.0, 6.0),
active_dims=[iteration_fidelity],
active_dims=active_dimsX,
)
additional_kernels.append(exp_kernel)
if data_fidelity is not None:
ds_kernel = DownsamplingKernel(
batch_shape=aug_batch_shape,
offset_prior=GammaPrior(3.0, 6.0),
power_prior=GammaPrior(3.0, 6.0),
active_dims=[data_fidelity],
)
if iteration_fidelity is not None:
kernels.append(
ExponentialDecayKernel(
batch_shape=aug_batch_shape,
lengthscale_prior=GammaPrior(3.0, 6.0),
offset_prior=GammaPrior(3.0, 6.0),
power_prior=GammaPrior(3.0, 6.0),
active_dims=[iteration_fidelity],
)
)
additional_kernels.append(ds_kernel)
kernel = ProductKernel(kernel, *additional_kernels)
if data_fidelities is not None:
for data_fidelity in data_fidelities:
kernels.append(
DownsamplingKernel(
batch_shape=aug_batch_shape,
offset_prior=GammaPrior(3.0, 6.0),
power_prior=GammaPrior(3.0, 6.0),
active_dims=[data_fidelity],
)
)

kernel = ProductKernel(*kernels)

covar_module = ScaleKernel(
kernel, batch_shape=aug_batch_shape, outputscale_prior=GammaPrior(2.0, 0.15)
)

key_prefix = "covar_module.base_kernel.kernels"
if linear_truncated:
subset_batch_dict = {
"covar_module.base_kernel.raw_power": -2,
"covar_module.base_kernel.covar_module_unbiased.raw_lengthscale": -3,
"covar_module.base_kernel.covar_module_biased.raw_lengthscale": -3,
}
subset_batch_dict = {}
for i in range(len(kernels)):
subset_batch_dict[f"{key_prefix}.{i}.raw_power"] = -2
subset_batch_dict[
f"{key_prefix}.{i}.covar_module_unbiased.raw_lengthscale"
] = -3
subset_batch_dict[
f"{key_prefix}.{i}.covar_module_biased.raw_lengthscale"
] = -3
Balandat marked this conversation as resolved.
Show resolved Hide resolved
else:
subset_batch_dict = {
"covar_module.base_kernel.kernels.0.raw_lengthscale": -3,
"covar_module.base_kernel.kernels.1.raw_power": -2,
"covar_module.base_kernel.kernels.1.raw_offset": -2,
f"{key_prefix}.0.raw_lengthscale": -3,
}

if iteration_fidelity is not None:
subset_batch_dict = {
"covar_module.base_kernel.kernels.1.raw_lengthscale": -3,
**subset_batch_dict,
}
if data_fidelity is not None:
subset_batch_dict = {
"covar_module.base_kernel.kernels.2.raw_power": -2,
"covar_module.base_kernel.kernels.2.raw_offset": -2,
**subset_batch_dict,
}
subset_batch_dict[f"{key_prefix}.1.raw_power"] = -2
subset_batch_dict[f"{key_prefix}.1.raw_offset"] = -2
subset_batch_dict[f"{key_prefix}.1.raw_lengthscale"] = -3
if data_fidelities is not None:
start_idx = 2 if iteration_fidelity is not None else 1
for i in range(start_idx, len(data_fidelities) + start_idx):
subset_batch_dict[f"{key_prefix}.{i}.raw_power"] = -2
subset_batch_dict[f"{key_prefix}.{i}.raw_offset"] = -2

return covar_module, subset_batch_dict
Loading
Loading