Skip to content

Commit ca0a8d7

Browse files
committed
Merge branch 'stable_qr_fix' of https://github.com/wjmaddox/gpytorch into stable_qr_fix
2 parents 43c8f57 + 5a682ef commit ca0a8d7

File tree

5 files changed

+93
-83
lines changed

5 files changed

+93
-83
lines changed

gpytorch/kernels/kernel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,8 @@ def covar_dist(
331331
return res
332332

333333
def named_sub_kernels(self):
334-
for name, module in self._modules.items():
335-
if isinstance(module, Kernel):
334+
for name, module in self.named_modules():
335+
if module is not self and isinstance(module, Kernel):
336336
yield name, module
337337

338338
def num_outputs_per_input(self, x1, x2):

gpytorch/lazy/lazy_tensor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -780,9 +780,9 @@ def cat_rows(self, cross_mat, new_mat, generate_roots=True, **root_decomp_kwargs
780780
A = self
781781

782782
# form matrix C = [A B; B^T D], where A = self, B = cross_mat, D = new_mat
783-
upper_row = CatLazyTensor(A, B, dim=-2)
784-
lower_row = CatLazyTensor(B.transpose(-1, -2), D, dim=-2)
785-
new_lazy_tensor = CatLazyTensor(upper_row, lower_row, dim=-1)
783+
upper_row = CatLazyTensor(A, B, dim=-2, output_device=A.device)
784+
lower_row = CatLazyTensor(B.transpose(-1, -2), D, dim=-2, output_device=A.device)
785+
new_lazy_tensor = CatLazyTensor(upper_row, lower_row, dim=-1, output_device=A.device)
786786

787787
# if the old lazy tensor does not have either a root decomposition or a root inverse decomposition
788788
# don't create one

gpytorch/lazy/low_rank_root_added_diag_lazy_tensor.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,32 @@ def inv_quad_logdet(self, inv_quad_rhs=None, logdet=False, reduce_inv_quad=True)
124124
logdet_term = self._logdet()
125125

126126
return inv_quad_term, logdet_term
127+
128+
def inv_matmul(self, right_tensor, left_tensor=None):
129+
if not self.is_square:
130+
raise RuntimeError(
131+
"inv_matmul only operates on (batches of) square (positive semi-definite) LazyTensors. "
132+
"Got a {} of size {}.".format(self.__class__.__name__, self.size())
133+
)
134+
135+
if self.dim() == 2 and right_tensor.dim() == 1:
136+
if self.shape[-1] != right_tensor.numel():
137+
raise RuntimeError(
138+
"LazyTensor (size={}) cannot be multiplied with right-hand-side Tensor (size={}).".format(
139+
self.shape, right_tensor.shape
140+
)
141+
)
142+
143+
squeeze_solve = False
144+
if right_tensor.ndimension() == 1:
145+
right_tensor = right_tensor.unsqueeze(-1)
146+
squeeze_solve = True
147+
148+
solve = self._solve(right_tensor)
149+
if squeeze_solve:
150+
solve = solve.squeeze(-1)
151+
152+
if left_tensor is not None:
153+
return left_tensor @ solve
154+
else:
155+
return solve

test/examples/test_sgpr_regression.py

Lines changed: 42 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import random
55
import unittest
6+
from unittest.mock import MagicMock, patch
67
import warnings
78
from math import exp, pi
89

@@ -14,6 +15,7 @@
1415
from gpytorch.means import ConstantMean
1516
from gpytorch.priors import SmoothedBoxPrior
1617
from gpytorch.test.utils import least_used_cuda_device
18+
from gpytorch.utils.cholesky import CHOLESKY_METHOD
1719
from gpytorch.utils.warnings import NumericalWarning
1820
from torch import optim
1921

@@ -65,41 +67,56 @@ def tearDown(self):
6567
if hasattr(self, "rng_state"):
6668
torch.set_rng_state(self.rng_state)
6769

68-
def test_sgpr_mean_abs_error(self):
70+
def test_sgpr_mean_abs_error(self, cuda=False):
6971
# Suppress numerical warnings
7072
warnings.simplefilter("ignore", NumericalWarning)
7173

72-
train_x, train_y, test_x, test_y = make_data()
74+
train_x, train_y, test_x, test_y = make_data(cuda=cuda)
7375
likelihood = GaussianLikelihood()
7476
gp_model = GPRegressionModel(train_x, train_y, likelihood)
7577
mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, gp_model)
7678

77-
# Optimize the model
78-
gp_model.train()
79-
likelihood.train()
79+
if cuda:
80+
gp_model = gp_model.cuda()
81+
likelihood = likelihood.cuda()
8082

81-
optimizer = optim.Adam(gp_model.parameters(), lr=0.1)
82-
for _ in range(30):
83-
optimizer.zero_grad()
84-
output = gp_model(train_x)
85-
loss = -mll(output, train_y)
86-
loss.backward()
87-
optimizer.step()
83+
# Mock cholesky
84+
_wrapped_cholesky = MagicMock(
85+
wraps=torch.linalg.cholesky if CHOLESKY_METHOD == "torch.linalg.cholesky" else torch.linalg.cholesky_ex
86+
)
87+
with patch(CHOLESKY_METHOD, new=_wrapped_cholesky) as cholesky_mock:
8888

89-
# Check that we have the right LazyTensor type
90-
kernel = likelihood(gp_model(train_x)).lazy_covariance_matrix.evaluate_kernel()
91-
self.assertIsInstance(kernel, gpytorch.lazy.LowRankRootAddedDiagLazyTensor)
89+
# Optimize the model
90+
gp_model.train()
91+
likelihood.train()
9292

93-
for param in gp_model.parameters():
94-
self.assertTrue(param.grad is not None)
95-
self.assertGreater(param.grad.norm().item(), 0)
93+
optimizer = optim.Adam(gp_model.parameters(), lr=0.1)
94+
for _ in range(30):
95+
optimizer.zero_grad()
96+
output = gp_model(train_x)
97+
loss = -mll(output, train_y)
98+
loss.backward()
99+
optimizer.step()
96100

97-
# Test the model
98-
gp_model.eval()
99-
likelihood.eval()
101+
# Check that we have the right LazyTensor type
102+
kernel = likelihood(gp_model(train_x)).lazy_covariance_matrix.evaluate_kernel()
103+
self.assertIsInstance(kernel, gpytorch.lazy.LowRankRootAddedDiagLazyTensor)
100104

101-
test_preds = likelihood(gp_model(test_x)).mean
102-
mean_abs_error = torch.mean(torch.abs(test_y - test_preds))
105+
for param in gp_model.parameters():
106+
self.assertTrue(param.grad is not None)
107+
self.assertGreater(param.grad.norm().item(), 0)
108+
109+
# Test the model
110+
gp_model.eval()
111+
likelihood.eval()
112+
113+
test_preds = likelihood(gp_model(test_x)).mean
114+
mean_abs_error = torch.mean(torch.abs(test_y - test_preds))
115+
cholesky_mock.assert_called() # We SHOULD call Cholesky...
116+
for chol_arg in cholesky_mock.call_args_list:
117+
first_arg = chol_arg[0][0]
118+
self.assertTrue(torch.is_tensor(first_arg))
119+
self.assertTrue(first_arg.size(-1) == gp_model.covar_module.inducing_points.size(-2))
103120

104121
self.assertLess(mean_abs_error.squeeze().item(), 0.1)
105122

@@ -123,62 +140,9 @@ def test_sgpr_mean_abs_error_cuda(self):
123140

124141
if not torch.cuda.is_available():
125142
return
126-
with least_used_cuda_device():
127-
train_x, train_y, test_x, test_y = make_data(cuda=True)
128-
likelihood = GaussianLikelihood().cuda()
129-
gp_model = GPRegressionModel(train_x, train_y, likelihood).cuda()
130-
mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, gp_model)
131-
132-
# Test the model before optimization
133-
gp_model.eval()
134-
likelihood.eval()
135-
test_preds = likelihood(gp_model(test_x)).mean
136-
mean_abs_error = torch.mean(torch.abs(test_y - test_preds))
137-
self.assertLess(mean_abs_error.squeeze().item(), 0.02)
138-
139-
# Test variances before optimization
140-
test_vars = likelihood(gp_model(test_x)).variance
141-
self.assertAllClose(test_vars, likelihood(gp_model(test_x)).covariance_matrix.diagonal(dim1=-1, dim2=-2))
142-
self.assertGreater(test_vars.min().item() + 0.1, likelihood.noise.item())
143-
self.assertLess(
144-
test_vars.max().item() - 0.05,
145-
likelihood.noise.item() + gp_model.covar_module.base_kernel.outputscale.item()
146-
)
147-
148-
# Optimize the model
149-
gp_model.train()
150-
likelihood.train()
151-
152-
optimizer = optim.Adam(gp_model.parameters(), lr=0.1)
153-
optimizer.n_iter = 0
154-
for _ in range(25):
155-
optimizer.zero_grad()
156-
output = gp_model(train_x)
157-
loss = -mll(output, train_y)
158-
loss.backward()
159-
optimizer.n_iter += 1
160-
optimizer.step()
161143

162-
for param in gp_model.parameters():
163-
self.assertTrue(param.grad is not None)
164-
self.assertGreater(param.grad.norm().item(), 0)
165-
166-
# Test the model
167-
gp_model.eval()
168-
likelihood.eval()
169-
test_preds = likelihood(gp_model(test_x)).mean
170-
mean_abs_error = torch.mean(torch.abs(test_y - test_preds))
171-
172-
self.assertLess(mean_abs_error.squeeze().item(), 0.02)
173-
174-
# Test variances
175-
test_vars = likelihood(gp_model(test_x)).variance
176-
self.assertAllClose(test_vars, likelihood(gp_model(test_x)).covariance_matrix.diagonal(dim1=-1, dim2=-2))
177-
self.assertGreater(test_vars.min().item() + 0.1, likelihood.noise.item())
178-
self.assertLess(
179-
test_vars.max().item() - 0.05,
180-
likelihood.noise.item() + gp_model.covar_module.base_kernel.outputscale.item()
181-
)
144+
with least_used_cuda_device():
145+
self.test_sgpr_mean_abs_error(cuda=True)
182146

183147

184148
if __name__ == "__main__":

test/kernels/test_additive_and_product_kernels.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,23 @@ def test_computes_product_of_radial_basis_function(self):
5656
res = kernel(a, b).evaluate()
5757
self.assertLess(torch.norm(res - actual), 2e-5)
5858

59+
def test_computes_product_of_radial_basis_function_batch(self):
60+
a = torch.tensor([4, 2, 8], dtype=torch.float).view(3, 1)
61+
b = torch.tensor([0, 2], dtype=torch.float).view(2, 1)
62+
lengthscale = 2
63+
64+
kernel_1 = RBFKernel(batch_shape=torch.Size([4])).initialize(lengthscale=lengthscale)
65+
kernel_2 = RBFKernel().initialize(lengthscale=lengthscale)
66+
kernel = kernel_1 * kernel_2
67+
68+
actual = torch.tensor([[16, 4], [4, 0], [64, 36]], dtype=torch.float)
69+
actual = actual.mul_(-0.5).div_(lengthscale ** 2).exp() ** 2
70+
actual = actual.repeat(4, 1, 1)
71+
72+
kernel.eval()
73+
res = kernel(a, b).evaluate()
74+
self.assertLess(torch.norm(res - actual), 2e-5)
75+
5976
def test_computes_sum_of_radial_basis_function(self):
6077
a = torch.tensor([4, 2, 8], dtype=torch.float).view(3, 1)
6178
b = torch.tensor([0, 2], dtype=torch.float).view(2, 1)

0 commit comments

Comments
 (0)