Skip to content

Commit 5e30554

Browse files
ferrineAlexAndorra
andauthored
Circular kernel (#4082)
* Adding Cirdular kernel * adding an example notebook * ad docs, append to release notes * update circular example * Update RELEASE-NOTES.md Co-authored-by: Alexandre ANDORRA <[email protected]> * review nb * fix typos * circular update only * update kernels and covs nb * fix typos * bound->period for consistency with Periodic * update plots, notebooks * fix typo in doc * fix even more typos * follow pre-commit advice * typo fix * update notebooks * nitpics * fix typo Co-authored-by: Alexandre ANDORRA <[email protected]>
1 parent 2482a35 commit 5e30554

File tree

5 files changed

+916
-52
lines changed

5 files changed

+916
-52
lines changed

RELEASE-NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
### New features
1515
- `sample_posterior_predictive_w` can now feed on `xarray.Dataset` - e.g. from `InferenceData.posterior`. (see [#4042](https://github.com/pymc-devs/pymc3/pull/4042))
16+
- Added `pymc3.gp.cov.Circular` kernel for Gaussian Processes on circular domains, e.g. the unit circle (see [#4082](https://github.com/pymc-devs/pymc3/pull/4082)).
1617
- Add MLDA, a new stepper for multilevel sampling. MLDA can be used when a hierarchy of approximate posteriors of varying accuracy is available, offering improved sampling efficiency especially in high-dimensional problems and/or where gradients are not available (see [#3926](https://github.com/pymc-devs/pymc3/pull/3926))
1718
- Change SMC metropolis kernel to independent metropolis kernel [#4115](https://github.com/pymc-devs/pymc3/pull/3926))
1819
- Add alternative parametrization to NegativeBinomial distribution in terms of n and p (see [#4126](https://github.com/pymc-devs/pymc3/issues/4126))

docs/source/notebooks/GP-Circular.ipynb

Lines changed: 650 additions & 0 deletions
Large diffs are not rendered by default.

docs/source/notebooks/GP-MeansAndCovs.ipynb

Lines changed: 182 additions & 52 deletions
Large diffs are not rendered by default.

pymc3/gp/cov.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,61 @@ def full(self, X, Xs=None):
294294
return tt.alloc(0.0, X.shape[0], Xs.shape[0])
295295

296296

297+
class Circular(Covariance):
298+
R"""
299+
Circular Kernel.
300+
301+
.. math::
302+
303+
k_g(x, y) = W_\pi(\operatorname{dist}_{\mathit{geo}}(x, y)),
304+
305+
with
306+
307+
.. math::
308+
309+
W_c(t) = \left(1 + \tau \frac{t}{c}\right)\left(1-\frac{t}{c}\right)^\tau_+
310+
311+
where :math:`c` is maximum value for :math:`t` and :math:`\tau\ge 4`.
312+
:math:`\tau` controls for correlation strength, larger :math:`\tau` leads to less smooth functions
313+
See [1]_ for more explanations and use cases.
314+
315+
Parameters
316+
----------
317+
period : scalar
318+
defines the circular interval :math:`[0, \mathit{bound})`
319+
tau : scalar
320+
:math:`\tau\ge 4` defines correlation strength, the larger,
321+
the smaller correlation is. Minimum value is :math:`4`
322+
323+
References
324+
----------
325+
.. [1] Espéran Padonou, O Roustant, "Polar Gaussian Processes for Predicting on Circular Domains"
326+
https://hal.archives-ouvertes.fr/hal-01119942v1/document
327+
"""
328+
329+
def __init__(self, input_dim, period, tau=4, active_dims=None):
330+
super().__init__(input_dim, active_dims)
331+
self.c = tt.as_tensor_variable(period / 2)
332+
self.tau = tau
333+
334+
def dist(self, X, Xs):
335+
if Xs is None:
336+
Xs = tt.transpose(X)
337+
else:
338+
Xs = tt.transpose(Xs)
339+
return tt.abs_((X - Xs + self.c) % (self.c * 2) - self.c)
340+
341+
def weinland(self, t):
342+
return (1 + self.tau * t / self.c) * tt.clip(1 - t / self.c, 0, np.inf) ** self.tau
343+
344+
def full(self, X, Xs=None):
345+
X, Xs = self._slice(X, Xs)
346+
return self.weinland(self.dist(X, Xs))
347+
348+
def diag(self, X):
349+
return tt.alloc(1.0, X.shape[0])
350+
351+
297352
class Stationary(Covariance):
298353
r"""
299354
Base class for stationary kernels/covariance functions.

pymc3/tests/test_gp.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,3 +1189,31 @@ def test_plot_gp_dist_warn_nan(self):
11891189
pm.gp.util.plot_gp_dist(ax, x=np.linspace(0, 50, X), samples=samples)
11901190
plt.close()
11911191
pass
1192+
1193+
1194+
class TestCircular:
1195+
def test_1d_tau1(self):
1196+
X = np.linspace(0, 1, 10)[:, None]
1197+
etalon = 0.600881
1198+
with pm.Model():
1199+
cov = pm.gp.cov.Circular(1, 1, tau=5)
1200+
K = theano.function([], cov(X))()
1201+
npt.assert_allclose(K[0, 1], etalon, atol=1e-3)
1202+
K = theano.function([], cov(X, X))()
1203+
npt.assert_allclose(K[0, 1], etalon, atol=1e-3)
1204+
# check diagonal
1205+
Kd = theano.function([], cov(X, diag=True))()
1206+
npt.assert_allclose(np.diag(K), Kd, atol=1e-5)
1207+
1208+
def test_1d_tau2(self):
1209+
X = np.linspace(0, 1, 10)[:, None]
1210+
etalon = 0.691239
1211+
with pm.Model():
1212+
cov = pm.gp.cov.Circular(1, 1, tau=4)
1213+
K = theano.function([], cov(X))()
1214+
npt.assert_allclose(K[0, 1], etalon, atol=1e-3)
1215+
K = theano.function([], cov(X, X))()
1216+
npt.assert_allclose(K[0, 1], etalon, atol=1e-3)
1217+
# check diagonal
1218+
Kd = theano.function([], cov(X, diag=True))()
1219+
npt.assert_allclose(np.diag(K), Kd, atol=1e-5)

0 commit comments

Comments
 (0)