From 7d7c4936363b7046d1d36fb85cc715395ca4080a Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:16:11 +0200 Subject: [PATCH 1/2] Center wrapped-normal truncation on residuals --- .../hypertoroidal_wrapped_normal_distribution.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pyrecest/distributions/hypertorus/hypertoroidal_wrapped_normal_distribution.py b/src/pyrecest/distributions/hypertorus/hypertoroidal_wrapped_normal_distribution.py index d76d0e4e1a..7ff5c49f54 100644 --- a/src/pyrecest/distributions/hypertorus/hypertoroidal_wrapped_normal_distribution.py +++ b/src/pyrecest/distributions/hypertorus/hypertoroidal_wrapped_normal_distribution.py @@ -143,7 +143,7 @@ def pdf(self, xs, m: Union[int, int32, int64] = 3): """ m = _validate_series_order(m) xs = as_hypertoroidal_points(xs, self.dim) - xs = (xs + pi) % (2 * pi) - pi + residuals = mod(xs - self.mu + pi, 2.0 * pi) - pi # Generate all combinations of offsets for each dimension offsets = [arange(-m, m + 1) * 2.0 * pi for _ in range(self.dim)] @@ -151,12 +151,14 @@ def pdf(self, xs, m: Union[int, int32, int64] = 3): -1, self.dim ) - # Calculate the PDF values by considering all combinations of offsets + # Center the finite image expansion on the nearest wrapped residual. pdf_values = zeros(xs.shape[0]) for offset in offset_combinations: - shifted_xa = xs + offset[None, :] + shifted_residuals = residuals + offset[None, :] pdf_values += multivariate_normal.pdf( - shifted_xa, mean=self.mu.flatten(), cov=self.C + shifted_residuals, + mean=zeros(self.dim), + cov=self.C, ) return pdf_values From 78f288678f1589c3279988fe45bec1732f630617 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:16:27 +0200 Subject: [PATCH 2/2] Add wrapped-normal truncation regression --- ...hypertoroidal_wrapped_normal_truncation.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/distributions/test_hypertoroidal_wrapped_normal_truncation.py diff --git a/tests/distributions/test_hypertoroidal_wrapped_normal_truncation.py b/tests/distributions/test_hypertoroidal_wrapped_normal_truncation.py new file mode 100644 index 0000000000..2eeffbd5ab --- /dev/null +++ b/tests/distributions/test_hypertoroidal_wrapped_normal_truncation.py @@ -0,0 +1,29 @@ +import numpy as np +import numpy.testing as npt + +from pyrecest.backend import array, to_numpy +from pyrecest.distributions.hypertorus.hypertoroidal_wrapped_normal_distribution import ( + HypertoroidalWrappedNormalDistribution, +) + + +def test_zero_order_pdf_centers_truncation_on_wrapped_residual(): + sigma = 0.2 + mean = 5.5 + distribution = HypertoroidalWrappedNormalDistribution( + array([mean]), + array([[sigma**2]]), + ) + + values = distribution.pdf( + array([[mean], [mean - 2.0 * np.pi]]), + m=0, + ) + + expected_peak = 1.0 / (np.sqrt(2.0 * np.pi) * sigma) + npt.assert_allclose( + to_numpy(values), + [expected_peak, expected_peak], + rtol=1.0e-6, + atol=1.0e-6, + )