Skip to content

Commit e99e33d

Browse files
fix(kernels): correct q=2 coefficient in PiecewisePolynomialKernel (#2756)
The q=2 branch of _get_cov used (j + 4*j + 3) instead of (j**2 + 4*j + 3), yielding 5j+3 in place of j^2+4j+3 and producing materially wrong covariance values for the default smoothness setting. Fix the coefficient to match Rasmussen & Williams Eq. 4.21 (and the kernel's own docstring and test reference). Add a q=2 numeric test with closely-spaced inputs so the polynomial term is actually exercised (existing tests only used q=0 with out-of-support distances, masking the bug). Co-authored-by: Geoff Pleiss <gpleiss@gmail.com>
1 parent 93222a0 commit e99e33d

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

gpytorch/kernels/piecewise_polynomial_kernel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def _get_cov(r: Tensor, j: int, q: int) -> Tensor:
1818
if q == 1:
1919
return (j + 1) * r + 1
2020
if q == 2:
21-
return 1 + (j + 2) * r + ((j + 4 * j + 3) / 3.0) * (r**2)
21+
return 1 + (j + 2) * r + ((j**2 + 4 * j + 3) / 3.0) * (r**2)
2222
if q == 3:
2323
return (
2424
1

test/kernels/test_piecewise_polynomial_kernel.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python3
22

3+
import math
34
import unittest
45

56
import torch
@@ -64,6 +65,28 @@ def test_fmax(r, j, q):
6465
actual = torch.cat([actual[i].diagonal(dim1=-1, dim2=-2).unsqueeze(0) for i in range(actual.size(0))])
6566
self.assertLess(torch.norm(res - actual), 1e-5)
6667

68+
def test_computes_piecewise_polynomial_kernel_q2(self):
69+
# Points must be close enough that r < 1 (i.e. inside the kernel's
70+
# compact support), otherwise (1 - r)_+ zeros out the polynomial term
71+
# and the q=2 coefficient is never exercised.
72+
a = torch.tensor([[0.0, 0.0], [0.1, 0.2], [0.3, 0.1]], dtype=torch.float)
73+
b = torch.tensor([[0.05, 0.05], [0.2, 0.1], [0.15, 0.25]], dtype=torch.float)
74+
D = a.shape[-1]
75+
kernel = PiecewisePolynomialKernel(q=2)
76+
kernel.eval()
77+
78+
# Reconstruct the reference value using the kernel's own distance (so the
79+
# only thing under test is the q=2 covariance polynomial coefficient).
80+
r = kernel.covar_dist(a.div(kernel.lengthscale), b.div(kernel.lengthscale))
81+
self.assertTrue((r < 1).any()) # ensure the polynomial term is actually exercised
82+
j = math.floor(D / 2.0) + kernel.q + 1
83+
fmax = torch.clamp(1 - r, min=0.0).pow(j + kernel.q)
84+
# Closed form from Rasmussen & Williams Eq. 4.21
85+
cov = 1 + (j + 2) * r + ((j**2 + 4 * j + 3) / 3.0) * r**2
86+
actual = fmax * cov
87+
res = kernel(a, b).to_dense()
88+
self.assertLess(torch.norm(res - actual), 1e-5)
89+
6790
def test_piecewise_polynomial_kernel_batch(self):
6891
a = torch.tensor([[4, 2, 8], [1, 2, 3]], dtype=torch.float).view(2, 3, 1)
6992
b = torch.tensor([[0, 2, 1], [-1, 2, 0]], dtype=torch.float).view(2, 3, 1)

0 commit comments

Comments
 (0)