Skip to content

Commit 0c7a8d0

Browse files
authored
Fix hypertoroidal MH defaults and infinite-angle handling (#5462)
* Fix hypertoroidal MH defaults and non-finite angle validation * Add hypertoroidal sampling and angle regressions
1 parent d5ee7c8 commit 0c7a8d0

2 files changed

Lines changed: 39 additions & 4 deletions

File tree

src/pyrecest/distributions/hypertorus/abstract_hypertoroidal_distribution.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
cos,
2222
int32,
2323
int64,
24-
isnan,
24+
isfinite,
2525
linspace,
2626
log,
2727
meshgrid,
@@ -171,8 +171,10 @@ def angular_error(alpha, beta):
171171
Returns:
172172
float or numpy array: The angular error(s) in radians.
173173
"""
174-
if bool(isnan(alpha).any()) or bool(isnan(beta).any()):
175-
raise ValueError("Angles must not contain NaN values.")
174+
if bool(backend_any(~isfinite(alpha))) or bool(backend_any(~isfinite(beta))):
175+
raise ValueError(
176+
"Angles must contain only finite values; NaN and infinite values are invalid."
177+
)
176178
# Ensure the angles are between 0 and 2*pi
177179
alpha = mod(alpha, 2.0 * pi)
178180
beta = mod(beta, 2.0 * pi)
@@ -312,7 +314,12 @@ def proposal_np(x):
312314
proposal = proposal_np
313315

314316
if start_point is None:
315-
start_point = self.mean_direction()
317+
try:
318+
start_point = self.mean_direction()
319+
except ValueError:
320+
# Uniform and other symmetric hypertoroidal distributions can have
321+
# no unique mean direction even though MH sampling is well-defined.
322+
start_point = zeros(self.dim)
316323

317324
# pylint: disable=duplicate-code
318325
s = AbstractManifoldSpecificDistribution.sample_metropolis_hastings(
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import pytest
2+
from pyrecest.backend import array
3+
from pyrecest.distributions import AbstractHypertoroidalDistribution
4+
from pyrecest.distributions.hypertorus.hypertoroidal_uniform_distribution import (
5+
HypertoroidalUniformDistribution,
6+
)
7+
8+
9+
def test_uniform_metropolis_hastings_does_not_require_unique_mean():
10+
dist = HypertoroidalUniformDistribution(2)
11+
12+
samples = dist.sample_metropolis_hastings(4, burn_in=0, skipping=1)
13+
14+
assert samples.shape == (4, 2)
15+
16+
17+
@pytest.mark.parametrize(
18+
("alpha", "beta"),
19+
[
20+
(float("inf"), 0.0),
21+
(float("-inf"), 0.0),
22+
(0.0, float("inf")),
23+
(0.0, float("-inf")),
24+
],
25+
)
26+
def test_angular_error_rejects_infinite_inputs(alpha, beta):
27+
with pytest.raises(ValueError, match="finite"):
28+
AbstractHypertoroidalDistribution.angular_error(array(alpha), array(beta))

0 commit comments

Comments
 (0)