Skip to content

Commit 7b5e35b

Browse files
TobyBoynemeta-codesync[bot]
authored andcommitted
Add cont_kernel_factory argument to SingleTaskMultifidelityGP (meta-pytorch#3167)
Summary: ## Motivation See meta-pytorch#3158 ### Have you read the [Contributing Guidelines on pull requests](https://github.com/meta-pytorch/botorch/blob/main/CONTRIBUTING.md#pull-requests)? Yes Pull Request resolved: meta-pytorch#3167 Test Plan: Written tests in `test_gp_regression_fidelity` (specifically `test_cont_kernel_factory`) to verify behaviour ## Related PRs N/A Reviewed By: saitcakmak Differential Revision: D92831698 Pulled By: hvarfner fbshipit-source-id: e7284931f9fe235de69f7db206354b80632db61b
1 parent d84f5f2 commit 7b5e35b

2 files changed

Lines changed: 113 additions & 17 deletions

File tree

botorch/models/gp_regression_fidelity.py

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from gpytorch.kernels.kernel import ProductKernel
4545
from gpytorch.kernels.scale_kernel import ScaleKernel
4646
from gpytorch.likelihoods.likelihood import Likelihood
47+
from gpytorch.module import Module
4748
from gpytorch.priors.torch_priors import GammaPrior
4849
from torch import Tensor
4950

@@ -72,6 +73,7 @@ def __init__(
7273
data_fidelities: Sequence[int] | None = None,
7374
linear_truncated: bool = True,
7475
nu: float = 2.5,
76+
covar_module: Module | None = None,
7577
likelihood: Likelihood | None = None,
7678
outcome_transform: OutcomeTransform | _DefaultType | None = DEFAULT,
7779
input_transform: InputTransform | None = None,
@@ -93,6 +95,8 @@ def __init__(
9395
of the default kernel.
9496
nu: The smoothness parameter for the Matern kernel: either 1/2, 3/2, or
9597
5/2. Only used when ``linear_truncated=True``.
98+
covar_module: The module for computing the covariance matrix between
99+
the non-fidelity features. Defaults to ``RBFKernel``.
96100
likelihood: A likelihood. If omitted, use a standard GaussianLikelihood
97101
with inferred noise level.
98102
outcome_transform: An outcome transform that is applied to the
@@ -130,6 +134,7 @@ def __init__(
130134
data_fidelities=data_fidelities,
131135
linear_truncated=linear_truncated,
132136
nu=nu,
137+
data_covar_module=covar_module,
133138
)
134139
super().__init__(
135140
train_X=train_X,
@@ -143,9 +148,10 @@ def __init__(
143148
# Used for subsetting along the output dimension. See Model.subset_output.
144149
self._subset_batch_dict = {
145150
"mean_module.raw_constant": -1,
146-
"covar_module.raw_outputscale": -1,
147151
**subset_batch_dict,
148152
}
153+
if linear_truncated:
154+
self._subset_batch_dict["covar_module.raw_outputscale"] = -1
149155
if train_Yvar is None:
150156
self._subset_batch_dict["likelihood.noise_covar.raw_noise"] = -2
151157
self.to(train_X)
@@ -174,7 +180,8 @@ def _setup_multifidelity_covar_module(
174180
data_fidelities: Sequence[int] | None,
175181
linear_truncated: bool,
176182
nu: float,
177-
) -> tuple[ScaleKernel, dict]:
183+
data_covar_module: Module | None = None,
184+
) -> tuple[ScaleKernel | ProductKernel, dict[str, int]]:
178185
"""Helper function to get the covariance module and associated subset_batch_dict
179186
for the multifidelity setting.
180187
@@ -190,7 +197,8 @@ def _setup_multifidelity_covar_module(
190197
of the default kernel.
191198
nu: The smoothness parameter for the Matern kernel: either 1/2, 3/2, or
192199
5/2. Only used when ``linear_truncated=True``.
193-
200+
data_covar_module: The module for computing the covariance matrix between
201+
the non-fidelity features. Defaults to ``RBFKernel``.
194202
Returns:
195203
The covariance module and subset_batch_dict.
196204
"""
@@ -205,6 +213,12 @@ def _setup_multifidelity_covar_module(
205213

206214
kernels = []
207215

216+
if linear_truncated and data_covar_module is not None:
217+
raise ValueError(
218+
"Non-fidelity covariance module cannot be specified when using a linear "
219+
"truncated kernel."
220+
)
221+
208222
if linear_truncated:
209223
leading_dims = [iteration_fidelity] if iteration_fidelity is not None else []
210224
trailing_dims = (
@@ -225,13 +239,18 @@ def _setup_multifidelity_covar_module(
225239
if iteration_fidelity is not None:
226240
non_active_dims.add(iteration_fidelity)
227241
active_dimsX = sorted(set(range(dim)) - non_active_dims)
228-
kernels.append(
229-
get_covar_module_with_dim_scaled_prior(
230-
ard_num_dims=len(active_dimsX),
231-
batch_shape=aug_batch_shape,
232-
active_dims=active_dimsX,
242+
243+
if data_covar_module is None:
244+
kernels.append(
245+
get_covar_module_with_dim_scaled_prior(
246+
ard_num_dims=len(active_dimsX),
247+
batch_shape=aug_batch_shape,
248+
active_dims=active_dimsX,
249+
)
233250
)
234-
)
251+
else:
252+
kernels.append(data_covar_module)
253+
235254
if iteration_fidelity is not None:
236255
kernels.append(
237256
ExponentialDecayKernel(
@@ -255,11 +274,15 @@ def _setup_multifidelity_covar_module(
255274

256275
kernel = ProductKernel(*kernels)
257276

258-
covar_module = ScaleKernel(
259-
kernel, batch_shape=aug_batch_shape, outputscale_prior=GammaPrior(2.0, 0.15)
260-
)
277+
if linear_truncated:
278+
covar_module = ScaleKernel(
279+
kernel, batch_shape=aug_batch_shape, outputscale_prior=GammaPrior(2.0, 0.15)
280+
)
281+
key_prefix = "covar_module.base_kernel.kernels"
282+
else:
283+
covar_module = kernel
284+
key_prefix = "covar_module.kernels"
261285

262-
key_prefix = "covar_module.base_kernel.kernels"
263286
if linear_truncated:
264287
subset_batch_dict = {}
265288
for i in range(len(kernels)):
@@ -271,9 +294,15 @@ def _setup_multifidelity_covar_module(
271294
}
272295
)
273296
else:
274-
subset_batch_dict = {
275-
f"{key_prefix}.0.raw_lengthscale": -3,
276-
}
297+
subset_batch_dict = {}
298+
299+
# Only set the subset_batch_dict if using the default kernel. See SingleTaskGP.
300+
if data_covar_module is None:
301+
subset_batch_dict.update(
302+
{
303+
f"{key_prefix}.0.raw_lengthscale": -3,
304+
}
305+
)
277306

278307
if iteration_fidelity is not None:
279308
subset_batch_dict.update(

test/models/test_gp_regression_fidelity.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
from botorch.sampling import SobolQMCNormalSampler
1818
from botorch.utils.datasets import SupervisedDataset
1919
from botorch.utils.testing import BotorchTestCase, get_random_data
20+
from gpytorch.kernels.kernel import ProductKernel
21+
from gpytorch.kernels.linear_kernel import LinearKernel
22+
from gpytorch.kernels.rbf_kernel import RBFKernel
2023
from gpytorch.kernels.scale_kernel import ScaleKernel
2124
from gpytorch.likelihoods import FixedNoiseGaussianLikelihood
2225
from gpytorch.means import ConstantMean
@@ -147,7 +150,9 @@ def test_gp(self) -> None:
147150

148151
# test init
149152
self.assertIsInstance(model.mean_module, ConstantMean)
150-
self.assertIsInstance(model.covar_module, ScaleKernel)
153+
self.assertIsInstance(
154+
model.covar_module, ScaleKernel if lin_trunc else ProductKernel
155+
)
151156
if use_octf:
152157
self.assertIsInstance(model.outcome_transform, Standardize)
153158
if use_intf:
@@ -427,6 +432,68 @@ def test_construct_inputs(self):
427432
self.assertTrue(kwargs["train_X"].equal(data_dict["train_X"]))
428433
self.assertTrue(kwargs["train_Y"].equal(data_dict["train_Y"]))
429434

435+
def test_custom_data_covar_module(self):
436+
iteration_fidelity, data_fidelities = self.FIDELITY_TEST_PAIRS[0]
437+
d = 1
438+
m = 2
439+
batch_shape = torch.Size([2, 3])
440+
aug_batch_shape = batch_shape + torch.Size([m])
441+
tkwargs = {"device": self.device, "dtype": torch.double}
442+
443+
_, model_kwargs = self._get_model_and_data(
444+
iteration_fidelity=iteration_fidelity,
445+
data_fidelities=data_fidelities,
446+
batch_shape=batch_shape,
447+
m=m,
448+
lin_truncated=False,
449+
**tkwargs,
450+
)
451+
452+
active_dimsX = [0]
453+
linear_kernel = LinearKernel(
454+
ard_num_dims=len(active_dimsX),
455+
batch_shape=aug_batch_shape,
456+
active_dims=active_dimsX,
457+
)
458+
459+
expected_kernels = (RBFKernel, LinearKernel)
460+
expected_subset_batch_dict = {
461+
"mean_module.raw_constant": -1,
462+
"covar_module.kernels.1.raw_power": -2,
463+
"covar_module.kernels.1.raw_offset": -2,
464+
}
465+
if model_kwargs.get("train_Yvar", None) is None:
466+
expected_subset_batch_dict["likelihood.noise_covar.raw_noise"] = -2
467+
468+
for covar_module, expected_kernel in zip(
469+
(None, linear_kernel), expected_kernels
470+
):
471+
model = SingleTaskMultiFidelityGP(**model_kwargs, covar_module=covar_module)
472+
non_fid_kernel = model.covar_module.kernels[0]
473+
self.assertIsInstance(non_fid_kernel, expected_kernel)
474+
self.assertEqual(non_fid_kernel.ard_num_dims, d)
475+
self.assertEqual(non_fid_kernel.batch_shape, aug_batch_shape)
476+
self.assertEqual(
477+
non_fid_kernel.active_dims,
478+
torch.as_tensor(list(range(d)), device=self.device),
479+
)
480+
481+
extra_subset_batch_dict = (
482+
{
483+
"covar_module.kernels.0.raw_lengthscale": -3,
484+
}
485+
if covar_module is None
486+
else {}
487+
)
488+
self.assertEqual(
489+
model._subset_batch_dict,
490+
{**expected_subset_batch_dict, **extra_subset_batch_dict},
491+
)
492+
493+
model_kwargs.update({"linear_truncated": True})
494+
with self.assertRaises(ValueError):
495+
model = SingleTaskMultiFidelityGP(**model_kwargs, covar_module=covar_module)
496+
430497

431498
class TestFixedNoiseSingleTaskMultiFidelityGP(TestSingleTaskMultiFidelityGP):
432499
def _get_model_and_data(

0 commit comments

Comments
 (0)