Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations

import numpy as np

from pyrecest.backend_support._pytorch_assignment_index_contract import (
patch_pytorch_assignment_index_contract as _patch_pytorch_assignment_index_contract,
)
Expand Down
6 changes: 3 additions & 3 deletions src/pyrecest/calibration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ def _aggregate_summary_metric(
if scale == 0.0:
return 0.0
scaled_weights = valid_counts / np.max(valid_counts)
normalized_mse = np.sum(
(valid_values / scale) ** 2 * scaled_weights
) / np.sum(scaled_weights)
normalized_mse = np.sum((valid_values / scale) ** 2 * scaled_weights) / np.sum(
scaled_weights
)
return float(scale * np.sqrt(normalized_mse))
if key == "max":
return float(np.max(valid_values))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,9 @@ def sample(self, n: int):
return column_stack([samples_bounded, samples_linear])

@classmethod
def from_distribution(cls, distribution, no_of_grid_points, grid_type="cartesian_prod"):
def from_distribution(
cls, distribution, no_of_grid_points, grid_type="cartesian_prod"
):
"""
Create a HypercylindricalStateSpaceSubdivisionDistribution from an
AbstractHypercylindricalDistribution.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ def mode(self):
mode_scores = []
for grid_idx, gaussian in enumerate(self.gaussians):
if bool(weights[grid_idx] > 0.0):
conditional_log_peak = array(
gaussian.ln_pdf(gaussian.mode())
).reshape(-1)[0]
conditional_log_peak = array(gaussian.ln_pdf(gaussian.mode())).reshape(
-1
)[0]
mode_scores.append(log(weights[grid_idx]) + conditional_log_peak)
else:
mode_scores.append(-float("inf"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ def from_distribution(
if n_particles is None:
raise ValueError("n_particles is required for sampling-based conversion.")
n_particles = cls._validate_particle_count(n_particles)
return cls(
distribution.sample(n_particles), ones(n_particles) / n_particles
)
return cls(distribution.sample(n_particles), ones(n_particles) / n_particles)

def plot_interpolated(self, _=None):
"""Raise because interpolation is unavailable for Dirac distributions."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ def pdf(self, xs, use_sinc=False, sinc_repetitions=5):
return self._pdf_via_fourier(xs)

@classmethod
def from_distribution(cls, distribution, no_of_gridpoints, enforce_pdf_nonnegative=True):
def from_distribution(
cls, distribution, no_of_gridpoints, enforce_pdf_nonnegative=True
):
return cls.from_function(
distribution.pdf,
no_of_gridpoints,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
floor,
isfinite,
log,
)
from pyrecest.backend import max as backend_max
from pyrecest.backend import (
mean,
mod,
pi,
Expand All @@ -18,7 +21,6 @@
sum,
zeros,
)
from pyrecest.backend import max as backend_max

from .abstract_circular_distribution import AbstractCircularDistribution

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,7 @@ def apply_function(self, f: Callable, function_is_vectorized: bool = True):
)

if transformed_dim != self.dim:
return HypertoroidalDiracDistribution(
dist.d, dist.w, dim=transformed_dim
)
return HypertoroidalDiracDistribution(dist.d, dist.w, dim=transformed_dim)
return dist

def to_toroidal_wd(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ def set_mean(self, new_mean):
new_lower = 2.0 * (0.5 * self.lower + half_offset)
new_upper = 2.0 * (0.5 * self.upper + half_offset)
if not bool(all(isfinite(new_lower))) or not bool(all(isfinite(new_upper))):
raise ValueError("new_mean would move box supports outside the finite range")
raise ValueError(
"new_mean would move box supports outside the finite range"
)

dist = copy.deepcopy(self)
dist.lower = new_lower
Expand Down
11 changes: 4 additions & 7 deletions src/pyrecest/filters/circular_fourier_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,10 @@ def predict_identity(self, d_sys):
# convolution. The inverse FFT returns the discrete circular sum;
# multiplying by the grid spacing approximates the convolution
# integral over [0, 2*pi).
predicted_values = (
fft.irfft(
fft.rfft(density_values) * fft.rfft(d_sys),
n=no_coefficients,
)
* (2.0 * pi / no_coefficients)
)
predicted_values = fft.irfft(
fft.rfft(density_values) * fft.rfft(d_sys),
n=no_coefficients,
) * (2.0 * pi / no_coefficients)
# Round-off in the FFT can produce tiny negative values although a
# convolution of nonnegative densities is nonnegative.
predicted_values = maximum(predicted_values, 0.0)
Expand Down
2 changes: 1 addition & 1 deletion src/pyrecest/filters/global_nearest_neighbor.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,4 +433,4 @@ def update_linear(
)

if self.log_posterior_estimates:
self.store_posterior_estimates()
self.store_posterior_estimates()
4 changes: 1 addition & 3 deletions src/pyrecest/filters/hyperhemispherical_particle_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ def __init__(self, n_particles: int, dim: int) -> None:
"""
n_particles = _validate_positive_integer(n_particles, "n_particles")
dim = _validate_positive_integer(dim, "dim")
initial_particles = hstack(
(zeros((n_particles, dim)), ones((n_particles, 1)))
)
initial_particles = hstack((zeros((n_particles, dim)), ones((n_particles, 1))))
initial_filter_state = HyperhemisphericalDiracDistribution(initial_particles)
HyperhemisphericalFilterMixin.__init__(self)
AbstractParticleFilter.__init__(self, initial_filter_state=initial_filter_state)
Expand Down
8 changes: 2 additions & 6 deletions src/pyrecest/filters/interacting_multiple_model_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,9 +496,7 @@ def update_mode_probabilities(self, likelihoods=None, log_likelihoods=None):
raise ValueError("likelihoods must be nonnegative.")
positive = likelihoods > 0.0
safe_likelihoods = where(positive, likelihoods, 1.0)
log_likelihoods = where(
positive, log(safe_likelihoods), -float("inf")
)
log_likelihoods = where(positive, log(safe_likelihoods), -float("inf"))
self.latest_model_likelihoods = likelihoods
else:
_reject_complex_values(log_likelihoods, "log_likelihoods")
Expand All @@ -518,9 +516,7 @@ def update_mode_probabilities(self, likelihoods=None, log_likelihoods=None):
prior_probabilities = asarray(self.mode_probabilities, dtype=float).reshape(-1)
positive = prior_probabilities > 0.0
safe_prior_probabilities = where(positive, prior_probabilities, 1.0)
log_prior = where(
positive, log(safe_prior_probabilities), -float("inf")
)
log_prior = where(positive, log(safe_prior_probabilities), -float("inf"))

log_posterior_unnormalized = log_prior + log_likelihoods
if not isfinite(log_posterior_unnormalized).any():
Expand Down
4 changes: 1 addition & 3 deletions src/pyrecest/filters/kalman_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ def _validate_kalman_state_covariance(covariance, dim):
check_symmetric=True,
)
eigenvalues = linalg.eigvalsh(covariance)
if not bool(
backend_all(eigenvalues >= -_STATE_COVARIANCE_EIGENVALUE_ATOL)
):
if not bool(backend_all(eigenvalues >= -_STATE_COVARIANCE_EIGENVALUE_ATOL)):
raise ValueError("state.covariance must be positive semidefinite.")
return covariance

Expand Down
4 changes: 1 addition & 3 deletions src/pyrecest/filters/state_space_subdivision_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,7 @@ def _validated_input_argument(values):
)
return values

system_matrices = _validated_matrix_argument(
system_matrices, "system_matrices"
)
system_matrices = _validated_matrix_argument(system_matrices, "system_matrices")
covariance_matrices = _validated_matrix_argument(
covariance_matrices, "covariance_matrices"
)
Expand Down
3 changes: 2 additions & 1 deletion src/pyrecest/filters/unscented_kalman_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from typing import Callable

import pyrecest.backend
from pyrecest.backend import atleast_1d, zeros
from pyrecest.backend import atleast_1d
from pyrecest.backend import copy as backend_copy
from pyrecest.backend import zeros
from pyrecest.distributions import GaussianDistribution
from pyrecest.models import AdditiveNoiseMeasurementModel, AdditiveNoiseTransitionModel
from pyrecest.sampling.sigma_points import MerweScaledSigmaPoints
Expand Down
12 changes: 3 additions & 9 deletions src/pyrecest/models/_validated_motion_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,7 @@ def coordinated_turn_transition(
)


def coordinated_turn_model(
dt: float = 1.0, noise_covariance: Any | None = None
) -> Any:
def coordinated_turn_model(dt: float = 1.0, noise_covariance: Any | None = None) -> Any:
"""Return a coordinated-turn model with validated process-noise covariance."""
dt = _motion_models._as_scalar_float( # pylint: disable=protected-access
dt,
Expand Down Expand Up @@ -180,9 +178,7 @@ def se2_unicycle_transition(
)


def se2_unicycle_model(
dt: float = 1.0, noise_covariance: Any | None = None
) -> Any:
def se2_unicycle_model(dt: float = 1.0, noise_covariance: Any | None = None) -> Any:
"""Return an SE(2) unicycle model with validated process noise."""
dt = _motion_models._as_scalar_float( # pylint: disable=protected-access
dt,
Expand All @@ -193,9 +189,7 @@ def se2_unicycle_model(
return _se2_unicycle_model_impl(dt=dt, noise_covariance=noise_covariance)


def se3_pose_twist_model(
dt: float = 1.0, noise_covariance: Any | None = None
) -> Any:
def se3_pose_twist_model(dt: float = 1.0, noise_covariance: Any | None = None) -> Any:
"""Return an SE(3) pose/twist model with validated process noise."""
dt = _motion_models._as_scalar_float( # pylint: disable=protected-access
dt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import importlib.util

import pytest

from tests.support.backend_runner import run_backend_code

pytestmark = pytest.mark.backend_portable
Expand Down
16 changes: 4 additions & 12 deletions tests/distributions/test_dirac_factory_subclass_preservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,14 @@ class DiracFactorySubclassPreservationTest(unittest.TestCase):
def test_linear_conversion_factory_preserves_requested_subclass(self):
source = LinearDiracDistribution(array([0.0, 1.0]))

converted = convert_distribution(
source, _LinearDiracSubclass, n_particles=2
)
converted = convert_distribution(source, _LinearDiracSubclass, n_particles=2)

self.assertIsInstance(converted, _LinearDiracSubclass)

def test_circular_conversion_factory_preserves_requested_subclass(self):
source = CircularDiracDistribution(array([0.0, 1.0]))

converted = convert_distribution(
source, _CircularDiracSubclass, n_particles=2
)
converted = convert_distribution(source, _CircularDiracSubclass, n_particles=2)

self.assertIsInstance(converted, _CircularDiracSubclass)

Expand All @@ -83,9 +79,7 @@ def test_circular_grid_conversion_factory_preserves_requested_subclass(self):
self.assertIsInstance(converted, _CircularGridSubclass)

def test_hypertoroidal_conversion_factory_preserves_requested_subclass(self):
source = HypertoroidalDiracDistribution(
array([[0.0, 0.5], [1.0, 1.5]])
)
source = HypertoroidalDiracDistribution(array([[0.0, 0.5], [1.0, 1.5]]))

converted = convert_distribution(
source, _HypertoroidalDiracSubclass, n_particles=2
Expand All @@ -110,9 +104,7 @@ def test_se2_conversion_factory_preserves_requested_subclass(self):
self.assertIsInstance(converted, _SE2DiracSubclass)

def test_se3_conversion_factory_preserves_requested_subclass(self):
source = SE3DiracDistribution(
array([[1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0]])
)
source = SE3DiracDistribution(array([[1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0]]))

converted = convert_distribution(source, _SE3DiracSubclass, n_particles=2)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
from pyrecest.distributions.circle.circular_dirac_distribution import (
CircularDiracDistribution,
)
from pyrecest.distributions.nonperiodic.gaussian_distribution import GaussianDistribution
from pyrecest.distributions.nonperiodic.gaussian_distribution import (
GaussianDistribution,
)


class TestHypercylindricalStateSpaceSubdivisionGaussianDistribution(unittest.TestCase):
def test_mode_uses_joint_peak_not_periodic_marginal_weight(self):
periodic = CircularDiracDistribution(
array([0.25, 1.25]), array([0.6, 0.4])
)
periodic = CircularDiracDistribution(array([0.25, 1.25]), array([0.6, 0.4]))
broad = GaussianDistribution(array([0.0]), array([[100.0]]))
narrow = GaussianDistribution(array([5.0]), array([[0.01]]))
distribution = HypercylindricalStateSpaceSubdivisionGaussianDistribution(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ def test_set_mean_avoids_overflow_when_finite_shifted_support_exists():
lower_value = 0.75 * max_finite
upper_value = max_finite
target_mean = -0.5 * max_finite
dist = LinearBoxParticleDistribution(
array([[lower_value]]), array([[upper_value]])
)
dist = LinearBoxParticleDistribution(array([[lower_value]]), array([[upper_value]]))

with np.errstate(over="raise", invalid="raise"):
shifted = dist.set_mean(array([target_mean]))
Expand Down
1 change: 0 additions & 1 deletion tests/distributions/test_se3_plot_axis_bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import numpy as np
import numpy.testing as npt

from pyrecest.backend import array
from pyrecest.distributions.abstract_se3_distribution import AbstractSE3Distribution

Expand Down
1 change: 0 additions & 1 deletion tests/filters/test_abstract_grid_filter_state_ownership.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import numpy.testing as npt

from pyrecest.backend import array
from pyrecest.distributions.circle.circular_grid_distribution import (
CircularGridDistribution,
Expand Down
2 changes: 1 addition & 1 deletion tests/filters/test_fejer_filter_control_validation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np
import pytest
import pyrecest.backend
import pytest
from pyrecest.filters import FejerIdentityFilter


Expand Down
7 changes: 2 additions & 5 deletions tests/filters/test_global_nearest_neighbor_gating.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import unittest

from scipy.stats import chi2

from pyrecest.filters import GlobalNearestNeighbor
from scipy.stats import chi2


class GlobalNearestNeighborGatingTest(unittest.TestCase):
def test_default_gate_matches_distance_representation(self):
squared_tracker = GlobalNearestNeighbor(
association_param={"square_dist": True}
)
squared_tracker = GlobalNearestNeighbor(association_param={"square_dist": True})
unsquared_tracker = GlobalNearestNeighbor(
association_param={"square_dist": False}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import numpy.testing as npt
import pyrecest.backend

from pyrecest.filters.hyperhemispherical_grid_filter import (
HyperhemisphericalGridFilter,
)
Expand Down
2 changes: 0 additions & 2 deletions tests/filters/test_hyperspherical_ukf_dimension_validation.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import numpy as np
import pyrecest.backend
import pytest

from pyrecest.filters.hyperspherical_ukf import HypersphericalUKF


pytestmark = pytest.mark.skipif(
pyrecest.backend.__backend_name__ == "jax",
reason="HypersphericalUKF is unsupported on JAX.",
Expand Down
2 changes: 0 additions & 2 deletions tests/filters/test_hyperspherical_ukf_state_ownership.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import numpy.testing as npt
import pyrecest.backend
import pytest

from pyrecest.backend import array, eye, to_numpy
from pyrecest.distributions import GaussianDistribution
from pyrecest.filters.hyperspherical_ukf import HypersphericalUKF


pytestmark = pytest.mark.skipif(
pyrecest.backend.__backend_name__ == "jax",
reason="HypersphericalUKF is unsupported on JAX.",
Expand Down
4 changes: 1 addition & 3 deletions tests/filters/test_imm_shared_python_matrices.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ def predict_linear(self, system_matrix, sys_noise_cov, sys_input=None):
covariance = (
system_matrix @ self.filter_state.C @ system_matrix.T + sys_noise_cov
)
self.filter_state = GaussianDistribution(
mean, covariance, check_validity=False
)
self.filter_state = GaussianDistribution(mean, covariance, check_validity=False)


@unittest.skipIf(
Expand Down
Loading