diff --git a/src/pyrecest/distributions/circle/von_mises_distribution.py b/src/pyrecest/distributions/circle/von_mises_distribution.py index d858b03e4f..3e213ea848 100644 --- a/src/pyrecest/distributions/circle/von_mises_distribution.py +++ b/src/pyrecest/distributions/circle/von_mises_distribution.py @@ -78,7 +78,13 @@ def sample(self, n): raise ValueError("n must be a positive integer.") n = int(n) return mod( - array(vonmises.rvs(kappa=float(self.kappa), loc=float(self.mu), size=n)), + array( + vonmises.rvs( + kappa=self._as_float_scalar(self.kappa, "kappa"), + loc=self._as_float_scalar(self.mu, "mu"), + size=n, + ) + ), 2.0 * pi, ) @@ -155,8 +161,12 @@ def to_minus_pi_to_pi_range(angle): @staticmethod def _as_float_scalar(value, name: str) -> float: + value_array = array(value) + if value_array.shape not in ((), (1,)): + raise ValueError(f"{name} must be a scalar.") + try: - scalar = float(value) + scalar = float(value_array.reshape(())) except (TypeError, ValueError) as exc: raise ValueError(f"{name} must be a scalar.") from exc diff --git a/src/pyrecest/filters/bingham_filter.py b/src/pyrecest/filters/bingham_filter.py index 5dff8c18e9..1cebbf779d 100644 --- a/src/pyrecest/filters/bingham_filter.py +++ b/src/pyrecest/filters/bingham_filter.py @@ -164,7 +164,6 @@ def update_identity(self, bv, z): for i in range(n): m_conj = self._conjugate(bv.M[:, i]) bv.M[:, i] = self._compose(z, m_conj) - self.filter_state = self.filter_state.multiply(bv) def get_point_estimate(self): @@ -178,7 +177,7 @@ def _conjugate(q): For q = [w, x, y, z], conjugate = [w, -x, -y, -z]. For q = [a, b], conjugate = [a, -b]. """ - result = copy.copy(q) + result = pyrecest.backend.copy(q) result[1:] = -result[1:] return result diff --git a/tests/distributions/test_von_mises_length_one_scalars.py b/tests/distributions/test_von_mises_length_one_scalars.py new file mode 100644 index 0000000000..2bc571dc8f --- /dev/null +++ b/tests/distributions/test_von_mises_length_one_scalars.py @@ -0,0 +1,22 @@ +import numpy as np + +from pyrecest.backend import array, to_numpy +from pyrecest.distributions import VonMisesDistribution + + +def test_set_mean_accepts_length_one_backend_array(): + dist = VonMisesDistribution(array(0.0), array(2.0)) + + shifted = dist.set_mean(array([1.0])) + density_at_mode = np.asarray(to_numpy(shifted.pdf(array([1.0])))) + + assert np.all(np.isfinite(density_at_mode)) + assert np.all(density_at_mode > 0.0) + + +def test_sample_accepts_length_one_parameter_arrays(): + dist = VonMisesDistribution(array([0.3]), array([2.0])) + + samples = dist.sample(3) + + assert samples.shape == (3,) diff --git a/tests/filters/test_bingham_filter_conjugate_independence.py b/tests/filters/test_bingham_filter_conjugate_independence.py new file mode 100644 index 0000000000..8d7f62692e --- /dev/null +++ b/tests/filters/test_bingham_filter_conjugate_independence.py @@ -0,0 +1,25 @@ +import unittest + +import numpy.testing as npt + +import pyrecest.backend +from pyrecest.backend import array, to_numpy +from pyrecest.filters.bingham_filter import BinghamFilter + + +class TestBinghamFilterConjugateIndependence(unittest.TestCase): + @unittest.skipIf( + pyrecest.backend.__backend_name__ == "jax", + reason="BinghamFilter is not supported on the JAX backend", + ) + def test_conjugate_does_not_mutate_input(self): + quaternion = array([1.0, 2.0, 3.0, 4.0]) + + conjugated = BinghamFilter._conjugate(quaternion) + + npt.assert_allclose(to_numpy(quaternion), [1.0, 2.0, 3.0, 4.0]) + npt.assert_allclose(to_numpy(conjugated), [1.0, -2.0, -3.0, -4.0]) + + +if __name__ == "__main__": + unittest.main()